@ -508,19 +508,59 @@ static void analog_all_subchannels_hungup(struct analog_pvt *p)
}
}
#if 0
static void analog_unlock_private ( struct analog_pvt * p )
{
if ( p - > calls - > unlock_private ) {
p - > calls - > unlock_private ( p - > chan_pvt ) ;
}
}
# endif
#if 0
static void analog_lock_private ( struct analog_pvt * p )
{
if ( p - > calls - > lock_private ) {
p - > calls - > lock_private ( p - > chan_pvt ) ;
}
}
# endif
/*!
* \ internal
* \ brief Obtain the specified subchannel owner lock if the owner exists .
*
* \ param pvt Analog private struct .
* \ param sub_idx Subchannel owner to lock .
*
* \ note Assumes the analog_lock_private ( pvt - > chan_pvt ) is already obtained .
*
* \ note
* Because deadlock avoidance may have been necessary , you need to confirm
* the state of things before continuing .
*
* \ return Nothing
*/
static void analog_lock_sub_owner ( struct analog_pvt * pvt , enum analog_sub sub_idx )
{
for ( ; ; ) {
if ( ! pvt - > subs [ sub_idx ] . owner ) {
/* No subchannel owner pointer */
break ;
}
if ( ! ast_channel_trylock ( pvt - > subs [ sub_idx ] . owner ) ) {
/* Got subchannel owner lock */
break ;
}
/* We must unlock the private to avoid the possibility of a deadlock */
if ( pvt - > calls - > deadlock_avoidance_private ) {
pvt - > calls - > deadlock_avoidance_private ( pvt - > chan_pvt ) ;
} else {
/* Don't use 100% CPU if required callback not present. */
usleep ( 1 ) ;
}
}
}
static int analog_off_hook ( struct analog_pvt * p )
{
@ -605,6 +645,20 @@ static int analog_is_dialing(struct analog_pvt *p, enum analog_sub index)
return - 1 ;
}
/*!
* \ internal
* \ brief Attempt to transfer 3 - way call .
*
* \ param p Analog private structure .
*
* \ note
* On entry these locks are held : real - call , private , 3 - way call .
*
* \ retval 1 Transfer successful . 3 - way call is unlocked and subchannel is unalloced .
* Swapped real and 3 - way subchannel .
* \ retval 0 Transfer successful . 3 - way call is unlocked and subchannel is unalloced .
* \ retval - 1 on error . Caller must unlock 3 - way call .
*/
static int analog_attempt_transfer ( struct analog_pvt * p )
{
/* In order to transfer, we need at least one of the channels to
@ -612,11 +666,18 @@ static int analog_attempt_transfer(struct analog_pvt *p)
together ( but then , why would we want to ? ) */
if ( ast_bridged_channel ( p - > subs [ ANALOG_SUB_REAL ] . owner ) ) {
/* The three-way person we're about to transfer to could still be in MOH, so
stop if now if appropriate */
stop it now if appropriate */
if ( ast_bridged_channel ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ) {
ast_queue_control ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner , AST_CONTROL_UNHOLD ) ;
}
if ( p - > subs [ ANALOG_SUB_REAL ] . owner - > _state = = AST_STATE_RINGING ) {
/*
* This may not be safe .
* We currently hold the locks on the real - call , private , and 3 - way call .
* We could possibly avoid this here by using an ast_queue_control ( ) instead .
* However , the following ast_channel_masquerade ( ) is going to be locking
* the bridged channel again anyway .
*/
ast_indicate ( ast_bridged_channel ( p - > subs [ ANALOG_SUB_REAL ] . owner ) , AST_CONTROL_RINGING ) ;
}
if ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner - > _state = = AST_STATE_RING ) {
@ -636,6 +697,13 @@ static int analog_attempt_transfer(struct analog_pvt *p)
} else if ( ast_bridged_channel ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ) {
ast_queue_control ( p - > subs [ ANALOG_SUB_REAL ] . owner , AST_CONTROL_UNHOLD ) ;
if ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner - > _state = = AST_STATE_RINGING ) {
/*
* This may not be safe .
* We currently hold the locks on the real - call , private , and 3 - way call .
* We could possibly avoid this here by using an ast_queue_control ( ) instead .
* However , the following ast_channel_masquerade ( ) is going to be locking
* the bridged channel again anyway .
*/
ast_indicate ( ast_bridged_channel ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) , AST_CONTROL_RINGING ) ;
}
if ( p - > subs [ ANALOG_SUB_REAL ] . owner - > _state = = AST_STATE_RING ) {
@ -656,7 +724,6 @@ static int analog_attempt_transfer(struct analog_pvt *p)
} else {
ast_debug ( 1 , " Neither %s nor %s are in a bridge, nothing to transfer \n " ,
p - > subs [ ANALOG_SUB_REAL ] . owner - > name , p - > subs [ ANALOG_SUB_THREEWAY ] . owner - > name ) ;
ast_softhangup_nolock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner , AST_SOFTHANGUP_DEV ) ;
return - 1 ;
}
return 0 ;
@ -1196,7 +1263,8 @@ int analog_hangup(struct analog_pvt *p, struct ast_channel *ast)
p - > subs [ index ] . owner = NULL ;
p - > polarity = POLARITY_IDLE ;
analog_set_linear_mode ( p , index , 0 ) ;
if ( index = = ANALOG_SUB_REAL ) {
switch ( index ) {
case ANALOG_SUB_REAL :
if ( p - > subs [ ANALOG_SUB_CALLWAIT ] . allocd & & p - > subs [ ANALOG_SUB_THREEWAY ] . allocd ) {
ast_debug ( 1 , " Normal call hung up with both three way call and a call waiting call in place? \n " ) ;
if ( p - > subs [ ANALOG_SUB_CALLWAIT ] . inthreeway ) {
@ -1215,15 +1283,23 @@ int analog_hangup(struct analog_pvt *p, struct ast_channel *ast)
/* This was part of a three way call. Immediately make way for
another call */
ast_debug ( 1 , " Call was complete, setting owner to former third call \n " ) ;
p - > subs [ ANALOG_SUB_REAL ] . inthreeway = 0 ;
p - > owner = p - > subs [ ANALOG_SUB_REAL ] . owner ;
} else {
/* This call hasn't been completed yet... Set owner to NULL */
ast_debug ( 1 , " Call was incomplete, setting owner to NULL \n " ) ;
p - > owner = NULL ;
}
p - > subs [ ANALOG_SUB_REAL ] . inthreeway = 0 ;
}
} else if ( p - > subs [ ANALOG_SUB_CALLWAIT ] . allocd ) {
/* Need to hold the lock for real-call, private, and call-waiting call */
analog_lock_sub_owner ( p , ANALOG_SUB_CALLWAIT ) ;
if ( ! p - > subs [ ANALOG_SUB_CALLWAIT ] . owner ) {
/* The call waiting call dissappeared. */
p - > owner = NULL ;
break ;
}
/* Move to the call-wait and switch back to them. */
analog_swap_subs ( p , ANALOG_SUB_CALLWAIT , ANALOG_SUB_REAL ) ;
analog_unalloc_sub ( p , ANALOG_SUB_CALLWAIT ) ;
@ -1234,6 +1310,8 @@ int analog_hangup(struct analog_pvt *p, struct ast_channel *ast)
if ( ast_bridged_channel ( p - > subs [ ANALOG_SUB_REAL ] . owner ) ) {
ast_queue_control ( p - > subs [ ANALOG_SUB_REAL ] . owner , AST_CONTROL_UNHOLD ) ;
}
/* Unlock the call-waiting call that we swapped to real-call. */
ast_channel_unlock ( p - > subs [ ANALOG_SUB_REAL ] . owner ) ;
} else if ( p - > subs [ ANALOG_SUB_THREEWAY ] . allocd ) {
analog_swap_subs ( p , ANALOG_SUB_THREEWAY , ANALOG_SUB_REAL ) ;
analog_unalloc_sub ( p , ANALOG_SUB_THREEWAY ) ;
@ -1241,17 +1319,21 @@ int analog_hangup(struct analog_pvt *p, struct ast_channel *ast)
/* This was part of a three way call. Immediately make way for
another call */
ast_debug ( 1 , " Call was complete, setting owner to former third call \n " ) ;
p - > subs [ ANALOG_SUB_REAL ] . inthreeway = 0 ;
p - > owner = p - > subs [ ANALOG_SUB_REAL ] . owner ;
} else {
/* This call hasn't been completed yet... Set owner to NULL */
ast_debug ( 1 , " Call was incomplete, setting owner to NULL \n " ) ;
p - > owner = NULL ;
}
p - > subs [ ANALOG_SUB_REAL ] . inthreeway = 0 ;
}
} else if ( index = = ANALOG_SUB_CALLWAIT ) {
/* Ditch the holding callwait call, and immediately make it availabe */
break ;
case ANALOG_SUB_CALLWAIT :
/* Ditch the holding callwait call, and immediately make it available */
if ( p - > subs [ ANALOG_SUB_CALLWAIT ] . inthreeway ) {
/* Need to hold the lock for call-waiting call, private, and 3-way call */
analog_lock_sub_owner ( p , ANALOG_SUB_THREEWAY ) ;
/* This is actually part of a three way, placed on hold. Place the third part
on music on hold now */
if ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner & & ast_bridged_channel ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ) {
@ -1263,27 +1345,42 @@ int analog_hangup(struct analog_pvt *p, struct ast_channel *ast)
/* Make it the call wait now */
analog_swap_subs ( p , ANALOG_SUB_CALLWAIT , ANALOG_SUB_THREEWAY ) ;
analog_unalloc_sub ( p , ANALOG_SUB_THREEWAY ) ;
if ( p - > subs [ ANALOG_SUB_CALLWAIT ] . owner ) {
/* Unlock the 3-way call that we swapped to call-waiting call. */
ast_channel_unlock ( p - > subs [ ANALOG_SUB_CALLWAIT ] . owner ) ;
}
} else {
analog_unalloc_sub ( p , ANALOG_SUB_CALLWAIT ) ;
}
} else if ( index = = ANALOG_SUB_THREEWAY ) {
break ;
case ANALOG_SUB_THREEWAY :
/* Need to hold the lock for 3-way call, private, and call-waiting call */
analog_lock_sub_owner ( p , ANALOG_SUB_CALLWAIT ) ;
if ( p - > subs [ ANALOG_SUB_CALLWAIT ] . inthreeway ) {
/* The other party of the three way call is currently in a call-wait state.
Start music on hold for them , and take the main guy out of the third call */
p - > subs [ ANALOG_SUB_CALLWAIT ] . inthreeway = 0 ;
if ( p - > subs [ ANALOG_SUB_CALLWAIT ] . owner & & ast_bridged_channel ( p - > subs [ ANALOG_SUB_CALLWAIT ] . owner ) ) {
ast_queue_control_data ( p - > subs [ ANALOG_SUB_CALLWAIT ] . owner , AST_CONTROL_HOLD ,
S_OR ( p - > mohsuggest , NULL ) ,
! ast_strlen_zero ( p - > mohsuggest ) ? strlen ( p - > mohsuggest ) + 1 : 0 ) ;
}
p - > subs [ ANALOG_SUB_CALLWAIT ] . inthreeway = 0 ;
}
if ( p - > subs [ ANALOG_SUB_CALLWAIT ] . owner ) {
ast_channel_unlock ( p - > subs [ ANALOG_SUB_CALLWAIT ] . owner ) ;
}
p - > subs [ ANALOG_SUB_REAL ] . inthreeway = 0 ;
/* If this was part of a three way call index, let us make
another three way call */
analog_unalloc_sub ( p , ANALOG_SUB_THREEWAY ) ;
} else {
/* This wasn't any sort of call, but how are we an index? */
ast_log ( LOG_WARNING , " Index found but not any type of call? \n " ) ;
break ;
default :
/*
* Should never happen .
* This wasn ' t any sort of call , so how are we an index ?
*/
ast_log ( LOG_ERROR , " Index found but not any type of call? \n " ) ;
break ;
}
}
@ -1599,9 +1696,8 @@ static void *__analog_ss_thread(void *data)
}
ast_verb ( 3 , " Starting simple switch on '%s' \n " , chan - > name ) ;
index = analog_get_index ( chan , p , 1 ) ;
index = analog_get_index ( chan , p , 0 ) ;
if ( index < 0 ) {
ast_log ( LOG_WARNING , " Huh? \n " ) ;
ast_hangup ( chan ) ;
goto quit ;
}
@ -2453,10 +2549,18 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
ast_log ( LOG_DEBUG , " %s %d \n " , __FUNCTION__ , p - > channel ) ;
index = analog_get_index ( ast , p , 0 ) ;
if ( index < 0 ) {
return & ast_null_frame ;
}
if ( index ! = ANALOG_SUB_REAL ) {
ast_log ( LOG_ERROR , " We got an event on a non real sub. Fix it! \n " ) ;
}
mysig = p - > sig ;
if ( p - > outsigmod > - 1 ) {
mysig = p - > outsigmod ;
}
p - > subs [ index ] . f . frametype = AST_FRAME_NULL ;
p - > subs [ index ] . f . subclass . integer = 0 ;
p - > subs [ index ] . f . datalen = 0 ;
@ -2467,14 +2571,6 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
p - > subs [ index ] . f . data . ptr = NULL ;
f = & p - > subs [ index ] . f ;
if ( index < 0 ) {
return & p - > subs [ index ] . f ;
}
if ( index ! = ANALOG_SUB_REAL ) {
ast_log ( LOG_ERROR , " We got an event on a non real sub. Fix it! \n " ) ;
}
res = analog_get_event ( p ) ;
ast_debug ( 1 , " Got event %s(%d) on channel %d (index %d) \n " , analog_event2str ( res ) , res , p - > channel , index ) ;
@ -2583,6 +2679,17 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
if ( index = = ANALOG_SUB_REAL ) {
/* The normal line was hung up */
if ( p - > subs [ ANALOG_SUB_CALLWAIT ] . owner ) {
/* Need to hold the lock for real-call, private, and call-waiting call */
analog_lock_sub_owner ( p , ANALOG_SUB_CALLWAIT ) ;
if ( ! p - > subs [ ANALOG_SUB_CALLWAIT ] . owner ) {
/*
* The call waiting call dissappeared .
* This is now a normal hangup .
*/
analog_set_echocanceller ( p , 0 ) ;
return NULL ;
}
/* There's a call waiting call, so ring the phone, but make it unowned in the mean time */
analog_swap_subs ( p , ANALOG_SUB_CALLWAIT , ANALOG_SUB_REAL ) ;
ast_verb ( 3 , " Channel %d still has (callwait) call, ringing phone \n " , p - > channel ) ;
@ -2593,64 +2700,58 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
if ( p - > subs [ ANALOG_SUB_REAL ] . owner - > _state ! = AST_STATE_UP ) {
analog_set_dialing ( p , 1 ) ;
}
/* Unlock the call-waiting call that we swapped to real-call. */
ast_channel_unlock ( p - > subs [ ANALOG_SUB_REAL ] . owner ) ;
analog_ring ( p ) ;
} else if ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) {
unsigned int mssinceflash ;
/* Here we have to retain the lock on both the main channel, the 3-way channel, and
the private structure - - not especially easy or clean */
while ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner & & ast_channel_trylock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ) {
/* Yuck, didn't get the lock on the 3-way, gotta release everything and re-grab! */
analog_unlock_private ( p ) ;
CHANNEL_DEADLOCK_AVOIDANCE ( ast ) ;
/* We can grab ast and p in that order, without worry. We should make sure
nothing seriously bad has happened though like some sort of bizarre double
masquerade ! */
analog_lock_private ( p ) ;
if ( p - > owner ! = ast ) {
ast_log ( LOG_WARNING , " This isn't good... \n " ) ;
return NULL ;
}
}
/* Need to hold the lock for real-call, private, and 3-way call */
analog_lock_sub_owner ( p , ANALOG_SUB_THREEWAY ) ;
if ( ! p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) {
ast_log ( LOG_NOTICE , " Whoa, threeway disappeared kinda randomly. \n " ) ;
/* Just hangup */
return NULL ;
}
if ( p - > owner ! = ast ) {
ast_channel_unlock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ;
ast_log ( LOG_WARNING , " This isn't good... \n " ) ;
/* Just hangup */
return NULL ;
}
mssinceflash = ast_tvdiff_ms ( ast_tvnow ( ) , p - > flashtime ) ;
ast_debug ( 1 , " Last flash was %d ms ago \n " , mssinceflash ) ;
if ( mssinceflash < MIN_MS_SINCE_FLASH ) {
/* It hasn't been long enough since the last flashook. This is probably a bounce on
hanging up . Hangup both channels now */
if ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) {
ast_queue_hangup_with_cause ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner , AST_CAUSE_NO_ANSWER ) ;
}
ast_softhangup_nolock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner , AST_SOFTHANGUP_DEV ) ;
ast_debug ( 1 , " Looks like a bounced flash, hanging up both calls on %d \n " , p - > channel ) ;
ast_queue_hangup_with_cause ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner , AST_CAUSE_NO_ANSWER ) ;
ast_softhangup_nolock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner , AST_SOFTHANGUP_DEV ) ;
ast_channel_unlock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ;
} else if ( ( ast - > pbx ) | | ( ast - > _state = = AST_STATE_UP ) ) {
if ( p - > transfer ) {
/* Only attempt transfer if the phone is ringing; why transfer to busy tone eh? */
if ( ! p - > transfertobusy & & ast - > _state = = AST_STATE_BUSY ) {
ast_channel_unlock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ;
/* Swap subs and dis-own channel */
analog_swap_subs ( p , ANALOG_SUB_THREEWAY , ANALOG_SUB_REAL ) ;
/* Unlock the 3-way call that we swapped to real-call. */
ast_channel_unlock ( p - > subs [ ANALOG_SUB_REAL ] . owner ) ;
p - > owner = NULL ;
/* Ring the phone */
analog_ring ( p ) ;
} else {
if ( ( res = analog_attempt_transfer ( p ) ) < 0 ) {
res = analog_attempt_transfer ( p ) ;
if ( res < 0 ) {
/* Transfer attempt failed. */
ast_softhangup_nolock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner , AST_SOFTHANGUP_DEV ) ;
if ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) {
ast_channel_unlock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ;
}
ast_channel_unlock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ;
} else if ( res ) {
/* this isn't a threeway call anymore */
p - > subs [ ANALOG_SUB_REAL ] . inthreeway = 0 ;
p - > subs [ ANALOG_SUB_THREEWAY ] . inthreeway = 0 ;
/* Don't actually hang up at this point */
if ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) {
ast_channel_unlock ( & p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ;
}
break ;
}
}
@ -2659,15 +2760,14 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
p - > subs [ ANALOG_SUB_THREEWAY ] . inthreeway = 0 ;
} else {
ast_softhangup_nolock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner , AST_SOFTHANGUP_DEV ) ;
if ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) {
ast_channel_unlock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ;
}
ast_channel_unlock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ;
}
} else {
ast_cel_report_event ( ast , AST_CEL_BLINDTRANSFER , NULL , ast - > linkedid , NULL ) ;
ast_channel_unlock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ;
/* Swap subs and dis-own channel */
analog_swap_subs ( p , ANALOG_SUB_THREEWAY , ANALOG_SUB_REAL ) ;
/* Unlock the 3-way call that we swapped to real-call. */
ast_channel_unlock ( p - > subs [ ANALOG_SUB_REAL ] . owner ) ;
p - > owner = NULL ;
/* Ring the phone */
analog_ring ( p ) ;
@ -2905,32 +3005,43 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
}
if ( p - > subs [ ANALOG_SUB_CALLWAIT ] . owner ) {
/* Swap to call-wait */
int previous_state = p - > subs [ ANALOG_SUB_CALLWAIT ] . owner - > _state ;
if ( p - > subs [ ANALOG_SUB_CALLWAIT ] . owner - > _state = = AST_STATE_RINGING ) {
ast_setstate ( p - > subs [ ANALOG_SUB_CALLWAIT ] . owner , AST_STATE_UP ) ;
/* Need to hold the lock for real-call, private, and call-waiting call */
analog_lock_sub_owner ( p , ANALOG_SUB_CALLWAIT ) ;
if ( ! p - > subs [ ANALOG_SUB_CALLWAIT ] . owner ) {
/*
* The call waiting call dissappeared .
* Let ' s just ignore this flash - hook .
*/
ast_log ( LOG_NOTICE , " Whoa, the call-waiting call disappeared. \n " ) ;
goto winkflashdone ;
}
/* Swap to call-wait */
analog_swap_subs ( p , ANALOG_SUB_REAL , ANALOG_SUB_CALLWAIT ) ;
analog_play_tone ( p , ANALOG_SUB_REAL , - 1 ) ;
p - > owner = p - > subs [ ANALOG_SUB_REAL ] . owner ;
ast_debug ( 1 , " Making %s the new owner \n " , p - > owner - > name ) ;
if ( previous_state = = AST_STATE_RINGING ) {
if ( p - > subs [ ANALOG_SUB_REAL ] . owner - > _state = = AST_STATE_RINGING ) {
ast_setstate ( p - > subs [ ANALOG_SUB_REAL ] . owner , AST_STATE_UP ) ;
ast_queue_control ( p - > subs [ ANALOG_SUB_REAL ] . owner , AST_CONTROL_ANSWER ) ;
}
analog_stop_callwait ( p ) ;
/* Start music on hold if appropriate */
if ( ! p - > subs [ ANALOG_SUB_CALLWAIT ] . inthreeway & & ast_bridged_channel ( p - > subs [ ANALOG_SUB_CALLWAIT ] . owner ) ) {
ast_queue_control_data ( p - > subs [ ANALOG_SUB_CALLWAIT ] . owner , AST_CONTROL_HOLD ,
S_OR ( p - > mohsuggest , NULL ) ,
! ast_strlen_zero ( p - > mohsuggest ) ? strlen ( p - > mohsuggest ) + 1 : 0 ) ;
}
ast_queue_control ( p - > subs [ ANALOG_SUB_REAL ] . owner , AST_CONTROL_ANSWER ) ;
if ( ast_bridged_channel ( p - > subs [ ANALOG_SUB_REAL ] . owner ) ) {
ast_queue_control_data ( p - > subs [ ANALOG_SUB_REAL ] . owner , AST_CONTROL_HOLD ,
S_OR ( p - > mohsuggest , NULL ) ,
! ast_strlen_zero ( p - > mohsuggest ) ? strlen ( p - > mohsuggest ) + 1 : 0 ) ;
}
ast_queue_control ( p - > subs [ ANALOG_SUB_REAL ] . owner , AST_CONTROL_UNHOLD ) ;
/* Unlock the call-waiting call that we swapped to real-call. */
ast_channel_unlock ( p - > subs [ ANALOG_SUB_REAL ] . owner ) ;
} else if ( ! p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) {
if ( ! p - > threewaycalling ) {
/* Just send a flash if no 3-way calling */
@ -2957,10 +3068,10 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
/* XXX This section needs much more error checking!!! XXX */
/* Start a 3-way call if feasible */
if ( ! ( ( ast - > pbx ) | |
( ast - > _state = = AST_STATE_UP ) | |
( ast - > _state = = AST_STATE_RING ) ) ) {
( ast - > _state = = AST_STATE_UP ) | |
( ast - > _state = = AST_STATE_RING ) ) ) {
ast_debug ( 1 , " Flash when call not up or ringing \n " ) ;
goto winkflashdone ;
goto winkflashdone ;
}
if ( analog_alloc_sub ( p , ANALOG_SUB_THREEWAY ) ) {
ast_log ( LOG_WARNING , " Unable to allocate three-way subchannel \n " ) ;
@ -2968,6 +3079,13 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
}
/* Make new channel */
chan = analog_new_ast_channel ( p , AST_STATE_RESERVED , 0 , ANALOG_SUB_THREEWAY , NULL ) ;
if ( ! chan ) {
ast_log ( LOG_WARNING ,
" Cannot allocate new call structure on channel %d \n " ,
p - > channel ) ;
analog_unalloc_sub ( p , ANALOG_SUB_THREEWAY ) ;
goto winkflashdone ;
}
if ( p - > dahditrcallerid ) {
if ( ! p - > origcid_num ) {
p - > origcid_num = ast_strdup ( p - > cid_num ) ;
@ -2989,9 +3107,7 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
p - > ss_astchan = p - > owner = chan ;
pthread_attr_init ( & attr ) ;
pthread_attr_setdetachstate ( & attr , PTHREAD_CREATE_DETACHED ) ;
if ( ! chan ) {
ast_log ( LOG_WARNING , " Cannot allocate new structure on channel %d \n " , p - > channel ) ;
} else if ( ast_pthread_create ( & threadid , & attr , __analog_ss_thread , p ) ) {
if ( ast_pthread_create ( & threadid , & attr , __analog_ss_thread , p ) ) {
ast_log ( LOG_WARNING , " Unable to start simple switch on channel %d \n " , p - > channel ) ;
res = analog_play_tone ( p , ANALOG_SUB_REAL , ANALOG_TONE_CONGESTION ) ;
analog_set_echocanceller ( p , 1 ) ;
@ -3022,6 +3138,20 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
}
} else {
/* Already have a 3 way call */
enum analog_sub orig_3way_sub ;
/* Need to hold the lock for real-call, private, and 3-way call */
analog_lock_sub_owner ( p , ANALOG_SUB_THREEWAY ) ;
if ( ! p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) {
/*
* The 3 - way call dissappeared .
* Let ' s just ignore this flash - hook .
*/
ast_log ( LOG_NOTICE , " Whoa, the 3-way call disappeared. \n " ) ;
goto winkflashdone ;
}
orig_3way_sub = ANALOG_SUB_THREEWAY ;
if ( p - > subs [ ANALOG_SUB_THREEWAY ] . inthreeway ) {
/* Call is already up, drop the last person */
ast_debug ( 1 , " Got flash with three way call up, dropping last call on %d \n " , p - > channel ) ;
@ -3030,6 +3160,7 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
( p - > subs [ ANALOG_SUB_THREEWAY ] . owner - > _state = = AST_STATE_UP ) ) {
/* Swap back -- we're dropping the real 3-way that isn't finished yet*/
analog_swap_subs ( p , ANALOG_SUB_THREEWAY , ANALOG_SUB_REAL ) ;
orig_3way_sub = ANALOG_SUB_REAL ;
p - > owner = p - > subs [ ANALOG_SUB_REAL ] . owner ;
}
/* Drop the last call and stop the conference */
@ -3041,7 +3172,6 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
/* Lets see what we're up to */
if ( ( ( ast - > pbx ) | | ( ast - > _state = = AST_STATE_UP ) ) & &
( p - > transfertobusy | | ( ast - > _state ! = AST_STATE_BUSY ) ) ) {
int otherindex = ANALOG_SUB_THREEWAY ;
struct ast_channel * other = ast_bridged_channel ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner ) ;
int way3bridge = 0 , cdr3way = 0 ;
@ -3061,10 +3191,10 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
p - > subs [ ANALOG_SUB_REAL ] . inthreeway = 1 ;
if ( ast - > _state = = AST_STATE_UP ) {
analog_swap_subs ( p , ANALOG_SUB_THREEWAY , ANALOG_SUB_REAL ) ;
o therindex = ANALOG_SUB_REAL ;
o rig_3way_sub = ANALOG_SUB_REAL ;
}
if ( p - > subs [ otherindex ] . owner & & ast_bridged_channel ( p - > subs [ o therindex ] . owner ) ) {
ast_queue_control ( p - > subs [ o therindex ] . owner , AST_CONTROL_UNHOLD ) ;
if ( ast_bridged_channel ( p - > subs [ o rig_3way_sub ] . owner ) ) {
ast_queue_control ( p - > subs [ o rig_3way_sub ] . owner , AST_CONTROL_UNHOLD ) ;
}
p - > owner = p - > subs [ ANALOG_SUB_REAL ] . owner ;
if ( ast - > _state = = AST_STATE_RINGING ) {
@ -3073,18 +3203,20 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
analog_play_tone ( p , ANALOG_SUB_THREEWAY , ANALOG_TONE_RINGTONE ) ;
}
} else {
ast_verb ( 3 , " Dumping incomplete call on on %s\n " , p - > subs [ ANALOG_SUB_THREEWAY ] . owner - > name ) ;
ast_verb ( 3 , " Dumping incomplete call on %s\n " , p - > subs [ ANALOG_SUB_THREEWAY ] . owner - > name ) ;
analog_swap_subs ( p , ANALOG_SUB_THREEWAY , ANALOG_SUB_REAL ) ;
orig_3way_sub = ANALOG_SUB_REAL ;
ast_softhangup_nolock ( p - > subs [ ANALOG_SUB_THREEWAY ] . owner , AST_SOFTHANGUP_DEV ) ;
p - > owner = p - > subs [ ANALOG_SUB_REAL ] . owner ;
if ( p - > subs [ ANALOG_SUB_REAL ] . owner & & ast_bridged_channel ( p - > subs [ ANALOG_SUB_REAL ] . owner ) ) {
if ( ast_bridged_channel ( p - > subs [ ANALOG_SUB_REAL ] . owner ) ) {
ast_queue_control ( p - > subs [ ANALOG_SUB_REAL ] . owner , AST_CONTROL_UNHOLD ) ;
}
analog_set_echocanceller ( p , 1 ) ;
}
}
ast_channel_unlock ( p - > subs [ orig_3way_sub ] . owner ) ;
}
winkflashdone :
winkflashdone :
analog_update_conf ( p ) ;
break ;
case ANALOG_SIG_EM :
@ -3275,13 +3407,15 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
struct ast_frame * analog_exception ( struct analog_pvt * p , struct ast_channel * ast )
{
int res ;
int usedindex = - 1 ;
int index ;
struct ast_frame * f ;
ast_log ( LOG_DEBUG , " %s %d \n " , __FUNCTION__ , p - > channel ) ;
index = analog_get_index ( ast , p , 1 ) ;
if ( index < 0 ) {
index = ANALOG_SUB_REAL ;
}
p - > subs [ index ] . f . frametype = AST_FRAME_NULL ;
p - > subs [ index ] . f . datalen = 0 ;
@ -3293,7 +3427,6 @@ struct ast_frame *analog_exception(struct analog_pvt *p, struct ast_channel *ast
p - > subs [ index ] . f . src = " dahdi_exception " ;
p - > subs [ index ] . f . data . ptr = NULL ;
if ( ! p - > owner ) {
/* If nobody owns us, absorb the event appropriately, otherwise
we loop indefinitely . This occurs when , during call waiting , the
@ -3307,6 +3440,14 @@ struct ast_frame *analog_exception(struct analog_pvt *p, struct ast_channel *ast
( res ! = ANALOG_EVENT_HOOKCOMPLETE ) ) {
ast_debug ( 1 , " Restoring owner of channel %d on event %d \n " , p - > channel , res ) ;
p - > owner = p - > subs [ ANALOG_SUB_REAL ] . owner ;
if ( p - > owner & & ast ! = p - > owner ) {
/*
* Could this even happen ?
* Possible deadlock because we do not have the real - call lock .
*/
ast_log ( LOG_WARNING , " Event %s on %s is not restored owner %s \n " ,
analog_event2str ( res ) , ast - > name , p - > owner - > name ) ;
}
if ( p - > owner & & ast_bridged_channel ( p - > owner ) ) {
ast_queue_control ( p - > owner , AST_CONTROL_UNHOLD ) ;
}
@ -3319,7 +3460,8 @@ struct ast_frame *analog_exception(struct analog_pvt *p, struct ast_channel *ast
analog_ring ( p ) ;
analog_stop_callwait ( p ) ;
} else {
ast_log ( LOG_WARNING , " Absorbed on hook, but nobody is left!?!? \n " ) ;
ast_log ( LOG_WARNING , " Absorbed %s, but nobody is left!?!? \n " ,
analog_event2str ( res ) ) ;
}
analog_update_conf ( p ) ;
break ;
@ -3327,7 +3469,7 @@ struct ast_frame *analog_exception(struct analog_pvt *p, struct ast_channel *ast
analog_set_echocanceller ( p , 1 ) ;
analog_off_hook ( p ) ;
if ( p - > owner & & ( p - > owner - > _state = = AST_STATE_RINGING ) ) {
ast_queue_control ( p - > subs[ ANALOG_SUB_REAL ] . owner, AST_CONTROL_ANSWER ) ;
ast_queue_control ( p - > owner, AST_CONTROL_ANSWER ) ;
analog_set_dialing ( p , 0 ) ;
}
break ;
@ -3342,10 +3484,7 @@ struct ast_frame *analog_exception(struct analog_pvt *p, struct ast_channel *ast
ast_verb ( 3 , " Channel %d flashed to other channel %s \n " , p - > channel , p - > owner - > name ) ;
if ( p - > owner - > _state ! = AST_STATE_UP ) {
/* Answer if necessary */
usedindex = analog_get_index ( p - > owner , p , 0 ) ;
if ( usedindex > - 1 ) {
ast_queue_control ( p - > subs [ usedindex ] . owner , AST_CONTROL_ANSWER ) ;
}
ast_queue_control ( p - > owner , AST_CONTROL_ANSWER ) ;
ast_setstate ( p - > owner , AST_STATE_UP ) ;
}
analog_stop_callwait ( p ) ;
@ -3353,12 +3492,14 @@ struct ast_frame *analog_exception(struct analog_pvt *p, struct ast_channel *ast
ast_queue_control ( p - > owner , AST_CONTROL_UNHOLD ) ;
}
} else {
ast_log ( LOG_WARNING , " Absorbed on hook, but nobody is left!?!? \n " ) ;
ast_log ( LOG_WARNING , " Absorbed %s, but nobody is left!?!? \n " ,
analog_event2str ( res ) ) ;
}
analog_update_conf ( p ) ;
break ;
default :
ast_log ( LOG_WARNING , " Don't know how to absorb event %s \n " , analog_event2str ( res ) ) ;
break ;
}
f = & p - > subs [ index ] . f ;
return f ;