diff --git a/pri_internal.h b/pri_internal.h index ae71ae9..7e820e5 100644 --- a/pri_internal.h +++ b/pri_internal.h @@ -532,9 +532,9 @@ struct q931_call { /*! \brief Incoming call transfer state. */ enum INCOMING_CT_STATE incoming_ct_state; - /*! Call hold supplementary state. */ + /*! Call hold supplementary state. Valid on master call record only. */ enum Q931_HOLD_STATE hold_state; - /*! Call hold event timer */ + /*! Call hold event timer. Valid on master call record only. */ int hold_timer; int deflection_in_progress; /*!< CallDeflection for NT PTMP in progress. */ diff --git a/q931.c b/q931.c index 086dc46..405f2ee 100644 --- a/q931.c +++ b/q931.c @@ -5572,6 +5572,7 @@ int q931_connect_acknowledge(struct pri *ctrl, q931_call *call, int channel) static void q931_hold_timeout(void *data) { struct q931_call *call = data; + struct q931_call *master = call->master_call; struct pri *ctrl = call->pri; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { @@ -5579,16 +5580,23 @@ static void q931_hold_timeout(void *data) } /* Ensure that the timer is deleted. */ - pri_schedule_del(ctrl, call->hold_timer); - call->hold_timer = 0; + pri_schedule_del(ctrl, master->hold_timer); + master->hold_timer = 0; - UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_IDLE); + /* Don't change the hold state if there was HOLD a collision. */ + switch (master->hold_state) { + case Q931_HOLD_STATE_HOLD_REQ: + UPDATE_HOLD_STATE(ctrl, master, Q931_HOLD_STATE_IDLE); + break; + default: + break; + } q931_clr_subcommands(ctrl); ctrl->schedev = 1; ctrl->ev.e = PRI_EVENT_HOLD_REJ; ctrl->ev.hold_rej.channel = q931_encode_channel(call); - ctrl->ev.hold_rej.call = call; + ctrl->ev.hold_rej.call = master; ctrl->ev.hold_rej.cause = PRI_CAUSE_MESSAGE_TYPE_NONEXIST; ctrl->ev.hold_rej.subcmds = &ctrl->subcmds; } @@ -5662,8 +5670,8 @@ int q931_send_hold(struct pri *ctrl, struct q931_call *call) } pri_schedule_del(ctrl, call->hold_timer); call->hold_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T_HOLD], - q931_hold_timeout, call); - if (send_message(ctrl, winner, Q931_HOLD, hold_ies)) { + q931_hold_timeout, winner); + if (!call->hold_timer || send_message(ctrl, winner, Q931_HOLD, hold_ies)) { pri_schedule_del(ctrl, call->hold_timer); call->hold_timer = 0; return -1; @@ -5764,33 +5772,38 @@ int q931_send_hold_rej(struct pri *ctrl, struct q931_call *call, int cause) static void q931_retrieve_timeout(void *data) { struct q931_call *call = data; + struct q931_call *master = call->master_call; struct pri *ctrl = call->pri; - struct q931_call *winner; if (ctrl->debug & PRI_DEBUG_Q931_STATE) { pri_message(ctrl, "Time-out waiting for RETRIEVE response\n"); } /* Ensure that the timer is deleted. */ - pri_schedule_del(ctrl, call->hold_timer); - call->hold_timer = 0; + pri_schedule_del(ctrl, master->hold_timer); + master->hold_timer = 0; - UPDATE_HOLD_STATE(ctrl, call, Q931_HOLD_STATE_CALL_HELD); + /* Don't change the hold state if there was RETRIEVE a collision. */ + switch (master->hold_state) { + case Q931_HOLD_STATE_CALL_HELD: + case Q931_HOLD_STATE_RETRIEVE_REQ: + UPDATE_HOLD_STATE(ctrl, master, Q931_HOLD_STATE_CALL_HELD); - winner = q931_find_winning_call(call); - if (winner) { /* Call is still on hold so forget the channel. */ - winner->channelno = 0;/* No channel */ - winner->ds1no = 0; - winner->ds1explicit = 0; - winner->chanflags = 0; + call->channelno = 0;/* No channel */ + call->ds1no = 0; + call->ds1explicit = 0; + call->chanflags = 0; + break; + default: + break; } q931_clr_subcommands(ctrl); ctrl->schedev = 1; ctrl->ev.e = PRI_EVENT_RETRIEVE_REJ; ctrl->ev.retrieve_rej.channel = q931_encode_channel(call); - ctrl->ev.retrieve_rej.call = call; + ctrl->ev.retrieve_rej.call = master; ctrl->ev.retrieve_rej.cause = PRI_CAUSE_MESSAGE_TYPE_NONEXIST; ctrl->ev.retrieve_rej.subcmds = &ctrl->subcmds; } @@ -5869,7 +5882,7 @@ int q931_send_retrieve(struct pri *ctrl, struct q931_call *call, int channel) winner->ds1no = (channel & 0xff00) >> 8; winner->ds1explicit = (channel & 0x10000) >> 16; winner->channelno = channel & 0xff; - if (ctrl->localtype == PRI_NETWORK) { + if (ctrl->localtype == PRI_NETWORK && winner->channelno != 0xFF) { winner->chanflags = FLAG_EXCLUSIVE; } else { winner->chanflags = FLAG_PREFERRED; @@ -5881,8 +5894,8 @@ int q931_send_retrieve(struct pri *ctrl, struct q931_call *call, int channel) pri_schedule_del(ctrl, call->hold_timer); call->hold_timer = pri_schedule_event(ctrl, ctrl->timers[PRI_TIMER_T_RETRIEVE], - q931_retrieve_timeout, call); - if (send_message(ctrl, winner, Q931_RETRIEVE, retrieve_ies)) { + q931_retrieve_timeout, winner); + if (!call->hold_timer || send_message(ctrl, winner, Q931_RETRIEVE, retrieve_ies)) { pri_schedule_del(ctrl, call->hold_timer); call->hold_timer = 0; @@ -8211,6 +8224,9 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct master_call = c->master_call; switch (master_call->hold_state) { case Q931_HOLD_STATE_HOLD_REQ: + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "HOLD collision\n"); + } if (ctrl->localtype == PRI_NETWORK) { /* The network ignores HOLD request on a hold collision. */ break; @@ -8224,10 +8240,6 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct res = Q931_RES_HAVEEVENT; UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_HOLD_IND); - - /* Stop any T-HOLD timer from possible hold collision. */ - pri_schedule_del(ctrl, master_call->hold_timer); - master_call->hold_timer = 0; break; default: q931_send_hold_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); @@ -8324,6 +8336,9 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct master_call = c->master_call; switch (master_call->hold_state) { case Q931_HOLD_STATE_RETRIEVE_REQ: + if (ctrl->debug & PRI_DEBUG_Q931_STATE) { + pri_message(ctrl, "RETRIEVE collision\n"); + } if (ctrl->localtype == PRI_NETWORK) { /* The network ignores RETRIEVE request on a retrieve collision. */ break; @@ -8338,10 +8353,6 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct res = Q931_RES_HAVEEVENT; UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_RETRIEVE_IND); - - /* Stop any T-RETRIEVE timer from possible retrieve collision. */ - pri_schedule_del(ctrl, master_call->hold_timer); - master_call->hold_timer = 0; break; default: q931_send_retrieve_rej_msg(ctrl, c, PRI_CAUSE_WRONG_CALL_STATE); @@ -8383,6 +8394,8 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct res = 0; master_call = c->master_call; switch (master_call->hold_state) { + case Q931_HOLD_STATE_CALL_HELD: + /* In this state likely because of a RETRIEVE collision. */ case Q931_HOLD_STATE_RETRIEVE_REQ: UPDATE_HOLD_STATE(ctrl, master_call, Q931_HOLD_STATE_CALL_HELD);