aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--channels/chan_dahdi.c298
1 files changed, 222 insertions, 76 deletions
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index bad9e2c81..2ac06305d 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -1519,23 +1519,47 @@ static int dahdi_get_index(struct ast_channel *ast, struct dahdi_pvt *p, int nul
return res;
}
+/*!
+ * \internal
+ * \brief Obtain the specified subchannel owner lock if the owner exists.
+ *
+ * \param pvt Channel private struct.
+ * \param sub_idx Subchannel owner to lock.
+ *
+ * \note Assumes the pvt->lock 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 dahdi_lock_sub_owner(struct dahdi_pvt *pvt, int 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 */
+ DEADLOCK_AVOIDANCE(&pvt->lock);
+ }
+}
+
static void wakeup_sub(struct dahdi_pvt *p, int a, struct dahdi_pri *pri)
{
#ifdef HAVE_PRI
if (pri)
ast_mutex_unlock(&pri->lock);
#endif
- for (;;) {
- if (p->subs[a].owner) {
- if (ast_channel_trylock(p->subs[a].owner)) {
- DEADLOCK_AVOIDANCE(&p->lock);
- } else {
- ast_queue_frame(p->subs[a].owner, &ast_null_frame);
- ast_channel_unlock(p->subs[a].owner);
- break;
- }
- } else
- break;
+ dahdi_lock_sub_owner(p, a);
+ if (p->subs[a].owner) {
+ ast_queue_frame(p->subs[a].owner, &ast_null_frame);
+ ast_channel_unlock(p->subs[a].owner);
}
#ifdef HAVE_PRI
if (pri)
@@ -4391,7 +4415,8 @@ static int dahdi_hangup(struct ast_channel *ast)
p->subs[idx].needcallerid = 0;
p->polarity = POLARITY_IDLE;
dahdi_setlinear(p->subs[idx].dfd, 0);
- if (idx == SUB_REAL) {
+ switch (idx) {
+ case SUB_REAL:
if ((p->subs[SUB_CALLWAIT].dfd > -1) && (p->subs[SUB_THREEWAY].dfd > -1)) {
ast_debug(1, "Normal call hung up with both three way call and a call waiting call in place?\n");
if (p->subs[SUB_CALLWAIT].inthreeway) {
@@ -4410,15 +4435,23 @@ static int dahdi_hangup(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[SUB_REAL].inthreeway = 0;
p->owner = p->subs[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[SUB_REAL].inthreeway = 0;
}
} else if (p->subs[SUB_CALLWAIT].dfd > -1) {
+ /* Need to hold the lock for real-call, private, and call-waiting call */
+ dahdi_lock_sub_owner(p, SUB_CALLWAIT);
+ if (!p->subs[SUB_CALLWAIT].owner) {
+ /* The call waiting call dissappeared. */
+ p->owner = NULL;
+ break;
+ }
+
/* Move to the call-wait and switch back to them. */
swap_subs(p, SUB_CALLWAIT, SUB_REAL);
unalloc_sub(p, SUB_CALLWAIT);
@@ -4427,6 +4460,8 @@ static int dahdi_hangup(struct ast_channel *ast)
p->subs[SUB_REAL].needanswer = 1;
if (ast_bridged_channel(p->subs[SUB_REAL].owner))
ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD);
+ /* Unlock the call-waiting call that we swapped to real-call. */
+ ast_channel_unlock(p->subs[SUB_REAL].owner);
} else if (p->subs[SUB_THREEWAY].dfd > -1) {
swap_subs(p, SUB_THREEWAY, SUB_REAL);
unalloc_sub(p, SUB_THREEWAY);
@@ -4434,17 +4469,21 @@ static int dahdi_hangup(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[SUB_REAL].inthreeway = 0;
p->owner = p->subs[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[SUB_REAL].inthreeway = 0;
}
- } else if (idx == SUB_CALLWAIT) {
+ break;
+ case SUB_CALLWAIT:
/* Ditch the holding callwait call, and immediately make it availabe */
if (p->subs[SUB_CALLWAIT].inthreeway) {
+ /* Need to hold the lock for call-waiting call, private, and 3-way call */
+ dahdi_lock_sub_owner(p, 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[SUB_THREEWAY].owner && ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
@@ -4456,26 +4495,41 @@ static int dahdi_hangup(struct ast_channel *ast)
/* Make it the call wait now */
swap_subs(p, SUB_CALLWAIT, SUB_THREEWAY);
unalloc_sub(p, SUB_THREEWAY);
+ if (p->subs[SUB_CALLWAIT].owner) {
+ /* Unlock the 3-way call that we swapped to call-waiting call. */
+ ast_channel_unlock(p->subs[SUB_CALLWAIT].owner);
+ }
} else
unalloc_sub(p, SUB_CALLWAIT);
- } else if (idx == SUB_THREEWAY) {
+ break;
+ case SUB_THREEWAY:
+ /* Need to hold the lock for 3-way call, private, and call-waiting call */
+ dahdi_lock_sub_owner(p, SUB_CALLWAIT);
if (p->subs[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[SUB_CALLWAIT].inthreeway = 0;
if (p->subs[SUB_CALLWAIT].owner && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) {
ast_queue_control_data(p->subs[SUB_CALLWAIT].owner, AST_CONTROL_HOLD,
S_OR(p->mohsuggest, NULL),
!ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
}
- p->subs[SUB_CALLWAIT].inthreeway = 0;
+ }
+ if (p->subs[SUB_CALLWAIT].owner) {
+ ast_channel_unlock(p->subs[SUB_CALLWAIT].owner);
}
p->subs[SUB_REAL].inthreeway = 0;
/* If this was part of a three way call index, let us make
another three way call */
unalloc_sub(p, 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;
}
}
@@ -5584,6 +5638,20 @@ static int dahdi_ring_phone(struct dahdi_pvt *p)
static void *ss_thread(void *data);
+/*!
+ * \internal
+ * \brief Attempt to transfer 3-way call.
+ *
+ * \param p 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 attempt_transfer(struct dahdi_pvt *p)
{
/* In order to transfer, we need at least one of the channels to
@@ -5591,10 +5659,17 @@ static int attempt_transfer(struct dahdi_pvt *p)
together (but then, why would we want to?) */
if (ast_bridged_channel(p->subs[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[SUB_THREEWAY].owner))
ast_queue_control(p->subs[SUB_THREEWAY].owner, AST_CONTROL_UNHOLD);
if (p->subs[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[SUB_REAL].owner), AST_CONTROL_RINGING);
}
if (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_RING) {
@@ -5611,6 +5686,13 @@ static int attempt_transfer(struct dahdi_pvt *p)
} else if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD);
if (p->subs[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[SUB_THREEWAY].owner), AST_CONTROL_RINGING);
}
if (p->subs[SUB_REAL].owner->_state == AST_STATE_RING) {
@@ -5630,7 +5712,6 @@ static int attempt_transfer(struct dahdi_pvt *p)
} else {
ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n",
p->subs[SUB_REAL].owner->name, p->subs[SUB_THREEWAY].owner->name);
- p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
return -1;
}
return 0;
@@ -5792,9 +5873,17 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
struct ast_frame *f;
idx = dahdi_get_index(ast, p, 0);
+ if (idx < 0) {
+ return &ast_null_frame;
+ }
+ if (idx != 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[idx].f.frametype = AST_FRAME_NULL;
p->subs[idx].f.subclass = 0;
p->subs[idx].f.datalen = 0;
@@ -5805,8 +5894,6 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
p->subs[idx].f.data.ptr = NULL;
f = &p->subs[idx].f;
- if (idx < 0)
- return &p->subs[idx].f;
if (p->fake_event) {
res = p->fake_event;
p->fake_event = 0;
@@ -5999,6 +6086,17 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
if (idx == SUB_REAL) {
/* The normal line was hung up */
if (p->subs[SUB_CALLWAIT].owner) {
+ /* Need to hold the lock for real-call, private, and call-waiting call */
+ dahdi_lock_sub_owner(p, SUB_CALLWAIT);
+ if (!p->subs[SUB_CALLWAIT].owner) {
+ /*
+ * The call waiting call dissappeared.
+ * This is now a normal hangup.
+ */
+ dahdi_disable_ec(p);
+ return NULL;
+ }
+
/* There's a call waiting call, so ring the phone, but make it unowned in the mean time */
swap_subs(p, SUB_CALLWAIT, SUB_REAL);
ast_verb(3, "Channel %d still has (callwait) call, ringing phone\n", p->channel);
@@ -6013,37 +6111,34 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
/* Don't start streaming audio yet if the incoming call isn't up yet */
if (p->subs[SUB_REAL].owner->_state != AST_STATE_UP)
p->dialing = 1;
+ /* Unlock the call-waiting call that we swapped to real-call. */
+ ast_channel_unlock(p->subs[SUB_REAL].owner);
dahdi_ring_phone(p);
} else if (p->subs[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[SUB_THREEWAY].owner && ast_channel_trylock(p->subs[SUB_THREEWAY].owner)) {
- /* Yuck, didn't get the lock on the 3-way, gotta release everything and re-grab! */
- DLA_UNLOCK(&p->lock);
- 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! */
- DLA_LOCK(&p->lock);
- 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 */
+ dahdi_lock_sub_owner(p, SUB_THREEWAY);
if (!p->subs[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[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[SUB_THREEWAY].owner)
- ast_queue_hangup_with_cause(p->subs[SUB_THREEWAY].owner, AST_CAUSE_NO_ANSWER);
- p->subs[SUB_THREEWAY].owner->_softhangup |= 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[SUB_THREEWAY].owner, AST_CAUSE_NO_ANSWER);
+ p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
} else if ((ast->pbx) || (ast->_state == AST_STATE_UP)) {
if (p->transfer) {
@@ -6052,33 +6147,33 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
p->subs[SUB_THREEWAY].inthreeway = 0;
/* 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[SUB_THREEWAY].owner);
/* Swap subs and dis-own channel */
swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ /* Unlock the 3-way call that we swapped to real-call. */
+ ast_channel_unlock(p->subs[SUB_REAL].owner);
p->owner = NULL;
/* Ring the phone */
dahdi_ring_phone(p);
} else {
- if ((res = attempt_transfer(p)) < 0) {
+ res = attempt_transfer(p);
+ if (res < 0) {
+ /* Transfer attempt failed. */
p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
- if (p->subs[SUB_THREEWAY].owner)
- ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+ ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
} else if (res) {
/* Don't actually hang up at this point */
- if (p->subs[SUB_THREEWAY].owner)
- ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
break;
}
}
} else {
p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
- if (p->subs[SUB_THREEWAY].owner)
- ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
+ ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
}
} else {
- ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
/* Swap subs and dis-own channel */
swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ /* Unlock the 3-way call that we swapped to real-call. */
+ ast_channel_unlock(p->subs[SUB_REAL].owner);
p->owner = NULL;
/* Ring the phone */
dahdi_ring_phone(p);
@@ -6339,6 +6434,17 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
}
if (p->subs[SUB_CALLWAIT].owner) {
+ /* Need to hold the lock for real-call, private, and call-waiting call */
+ dahdi_lock_sub_owner(p, SUB_CALLWAIT);
+ if (!p->subs[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 */
swap_subs(p, SUB_REAL, SUB_CALLWAIT);
tone_zone_play_tone(p->subs[SUB_REAL].dfd, -1);
@@ -6350,6 +6456,7 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
}
p->callwaitingrepeat = 0;
p->cidcwexpire = 0;
+
/* Start music on hold if appropriate */
if (!p->subs[SUB_CALLWAIT].inthreeway && ast_bridged_channel(p->subs[SUB_CALLWAIT].owner)) {
ast_queue_control_data(p->subs[SUB_CALLWAIT].owner, AST_CONTROL_HOLD,
@@ -6363,6 +6470,9 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
!ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
}
p->subs[SUB_REAL].needunhold = 1;
+
+ /* Unlock the call-waiting call that we swapped to real-call. */
+ ast_channel_unlock(p->subs[SUB_REAL].owner);
} else if (!p->subs[SUB_THREEWAY].owner) {
if (!p->threewaycalling) {
/* Just send a flash if no 3-way calling */
@@ -6394,6 +6504,13 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
}
/* Make new channel */
chan = dahdi_new(p, AST_STATE_RESERVED, 0, SUB_THREEWAY, 0, 0);
+ if (!chan) {
+ ast_log(LOG_WARNING,
+ "Cannot allocate new call structure on channel %d\n",
+ p->channel);
+ unalloc_sub(p, SUB_THREEWAY);
+ goto winkflashdone;
+ }
if (p->dahditrcallerid) {
if (!p->origcid_num)
p->origcid_num = ast_strdup(p->cid_num);
@@ -6410,9 +6527,7 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
if (res)
ast_log(LOG_WARNING, "Unable to start dial recall tone on channel %d\n", p->channel);
p->owner = chan;
- if (!chan) {
- ast_log(LOG_WARNING, "Cannot allocate new structure on channel %d\n", p->channel);
- } else if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) {
+ if (ast_pthread_create_detached(&threadid, NULL, ss_thread, chan)) {
ast_log(LOG_WARNING, "Unable to start simple switch on channel %d\n", p->channel);
res = tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_CONGESTION);
dahdi_enable_ec(p);
@@ -6442,6 +6557,20 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
}
} else {
/* Already have a 3 way call */
+ int orig_3way_sub;
+
+ /* Need to hold the lock for real-call, private, and 3-way call */
+ dahdi_lock_sub_owner(p, SUB_THREEWAY);
+ if (!p->subs[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 = SUB_THREEWAY;
+
if (p->subs[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);
@@ -6449,6 +6578,7 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
if ((p->subs[SUB_REAL].owner->_state != AST_STATE_UP) && (p->subs[SUB_THREEWAY].owner->_state == AST_STATE_UP)) {
/* Swap back -- we're dropping the real 3-way that isn't finished yet*/
swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ orig_3way_sub = SUB_REAL;
p->owner = p->subs[SUB_REAL].owner;
}
/* Drop the last call and stop the conference */
@@ -6460,7 +6590,6 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *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 = SUB_THREEWAY;
struct ast_channel *other = ast_bridged_channel(p->subs[SUB_THREEWAY].owner);
int way3bridge = 0, cdr3way = 0;
@@ -6478,11 +6607,12 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
p->subs[SUB_REAL].inthreeway = 1;
if (ast->_state == AST_STATE_UP) {
swap_subs(p, SUB_THREEWAY, SUB_REAL);
- otherindex = SUB_REAL;
+ orig_3way_sub = SUB_REAL;
}
- if (p->subs[otherindex].owner && ast_bridged_channel(p->subs[otherindex].owner))
- ast_queue_control(p->subs[otherindex].owner, AST_CONTROL_UNHOLD);
- p->subs[otherindex].needunhold = 1;
+ if (ast_bridged_channel(p->subs[orig_3way_sub].owner)) {
+ ast_queue_control(p->subs[orig_3way_sub].owner, AST_CONTROL_UNHOLD);
+ }
+ p->subs[orig_3way_sub].needunhold = 1;
p->owner = p->subs[SUB_REAL].owner;
if (ast->_state == AST_STATE_RINGING) {
ast_debug(1, "Enabling ringtone on real and threeway\n");
@@ -6490,16 +6620,19 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
res = tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, DAHDI_TONE_RINGTONE);
}
} else {
- ast_verb(3, "Dumping incomplete call on on %s\n", p->subs[SUB_THREEWAY].owner->name);
+ ast_verb(3, "Dumping incomplete call on %s\n", p->subs[SUB_THREEWAY].owner->name);
swap_subs(p, SUB_THREEWAY, SUB_REAL);
+ orig_3way_sub = SUB_REAL;
p->subs[SUB_THREEWAY].owner->_softhangup |= AST_SOFTHANGUP_DEV;
p->owner = p->subs[SUB_REAL].owner;
- if (p->subs[SUB_REAL].owner && ast_bridged_channel(p->subs[SUB_REAL].owner))
+ if (ast_bridged_channel(p->subs[SUB_REAL].owner)) {
ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_UNHOLD);
+ }
p->subs[SUB_REAL].needunhold = 1;
dahdi_enable_ec(p);
}
}
+ ast_channel_unlock(p->subs[orig_3way_sub].owner);
}
winkflashdone:
update_conf(p);
@@ -6688,12 +6821,14 @@ static struct ast_frame *__dahdi_exception(struct ast_channel *ast)
{
struct dahdi_pvt *p = ast->tech_pvt;
int res;
- int usedindex=-1;
int idx;
struct ast_frame *f;
idx = dahdi_get_index(ast, p, 1);
+ if (idx < 0) {
+ idx = SUB_REAL;
+ }
p->subs[idx].f.frametype = AST_FRAME_NULL;
p->subs[idx].f.datalen = 0;
@@ -6705,7 +6840,6 @@ static struct ast_frame *__dahdi_exception(struct ast_channel *ast)
p->subs[idx].f.src = "dahdi_exception";
p->subs[idx].f.data.ptr = NULL;
-
if ((!p->owner) && (!(p->radio || (p->oprmode < 0)))) {
/* If nobody owns us, absorb the event appropriately, otherwise
we loop indefinitely. This occurs when, during call waiting, the
@@ -6722,6 +6856,14 @@ static struct ast_frame *__dahdi_exception(struct ast_channel *ast)
(res != DAHDI_EVENT_HOOKCOMPLETE)) {
ast_debug(1, "Restoring owner of channel %d on event %d\n", p->channel, res);
p->owner = p->subs[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",
+ event2str(res), ast->name, p->owner->name);
+ }
if (p->owner && ast_bridged_channel(p->owner))
ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
p->subs[SUB_REAL].needunhold = 1;
@@ -6734,8 +6876,10 @@ static struct ast_frame *__dahdi_exception(struct ast_channel *ast)
dahdi_ring_phone(p);
p->callwaitingrepeat = 0;
p->cidcwexpire = 0;
- } else
- ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+ } else {
+ ast_log(LOG_WARNING, "Absorbed %s, but nobody is left!?!?\n",
+ event2str(res));
+ }
update_conf(p);
break;
case DAHDI_EVENT_RINGOFFHOOK:
@@ -6757,10 +6901,7 @@ static struct ast_frame *__dahdi_exception(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 = dahdi_get_index(p->owner, p, 0);
- if (usedindex > -1) {
- p->subs[usedindex].needanswer = 1;
- }
+ p->subs[SUB_REAL].needanswer = 1;
ast_setstate(p->owner, AST_STATE_UP);
}
p->callwaitingrepeat = 0;
@@ -6768,12 +6909,15 @@ static struct ast_frame *__dahdi_exception(struct ast_channel *ast)
if (ast_bridged_channel(p->owner))
ast_queue_control(p->owner, AST_CONTROL_UNHOLD);
p->subs[SUB_REAL].needunhold = 1;
- } else
- ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+ } else {
+ ast_log(LOG_WARNING, "Absorbed %s, but nobody is left!?!?\n",
+ event2str(res));
+ }
update_conf(p);
break;
default:
ast_log(LOG_WARNING, "Don't know how to absorb event %s\n", event2str(res));
+ break;
}
f = &p->subs[idx].f;
return f;
@@ -7797,9 +7941,8 @@ static void *ss_thread(void *data)
goto quit;
}
ast_verb(3, "Starting simple switch on '%s'\n", chan->name);
- idx = dahdi_get_index(chan, p, 1);
+ idx = dahdi_get_index(chan, p, 0);
if (idx < 0) {
- ast_log(LOG_WARNING, "Huh?\n");
ast_hangup(chan);
goto quit;
}
@@ -9708,9 +9851,12 @@ static void *do_monitor(void *data)
last = i;
if (last) {
/* Only allow MWI to be initiated on a quiescent fxs port */
- if (!last->mwisendactive && last->sig & __DAHDI_SIG_FXO &&
- !last->fxsoffhookstate && !last->owner &&
- !ast_strlen_zero(last->mailbox) && (thispass - last->onhooktime > 3)) {
+ if (!last->mwisendactive
+ && (last->sig & __DAHDI_SIG_FXO)
+ && !last->fxsoffhookstate
+ && !last->owner
+ && !ast_strlen_zero(last->mailbox)
+ && (thispass - last->onhooktime > 3)) {
res = has_voicemail(last);
if (last->msgstate != res) {
/* Set driver resources for signalling VMWI */