diff --git a/channels/chan_brcm.c b/channels/chan_brcm.c index 10da1da92b14c95b362a7ba486ada997029ab315..b263432fb538689f4d2a483dc1f9330db275307e 100644 --- a/channels/chan_brcm.c +++ b/channels/chan_brcm.c @@ -210,6 +210,8 @@ static int audio_rx_fd = 0, audio_tx_fd = 0; static pe_bus_t *audio_rx_bus, *audio_tx_bus; static pe_stream_t *audio_rx_stream; static int mwi_dialtone_state = DIALTONE_ON; +int dtmf_wait_timer = 2000; /*Time to wait for dtmf(for R4 call setup) before setting up a conference call*/ + #define RTP_HEADER_SIZE 12 #define RTP_DTMF_SIZE 4 @@ -536,7 +538,9 @@ static int brcm_indicate(struct ast_channel *ast, int condition, const void *dat ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_TRANSFER. Expected %d, got %d\n", (int) sizeof(enum ast_control_transfer), (int) datalen); } else { enum ast_control_transfer *message = (enum ast_control_transfer *) data; - brcm_finish_transfer(ast, sub, *message); + struct brcm_subchannel* peer_sub; + peer_sub = brcm_subchannel_get_peer(sub); + brcm_finish_transfer(ast, peer_sub, *message); } break; case AST_CONTROL_CONGESTION: @@ -903,6 +907,13 @@ static int brcm_hangup(struct ast_channel *ast) sub->cw_timer_id = -1; } + if (sub->conf_timer_id != -1) { + if (ast_sched_del(sched, sub->conf_timer_id)) { + ast_log(LOG_WARNING, "Failed to remove scheduled conference setup timer\n"); + } + sub->conf_timer_id = -1; + } + if(sub->r4_hangup_timer_id != -1) { if (ast_sched_del(sched, sub->r4_hangup_timer_id)) { ast_log(LOG_WARNING, "Failed to remove scheduled r4 hangup timer\n"); @@ -1407,6 +1418,42 @@ static struct brcm_subchannel *brcm_get_onhold_subchannel(const struct brcm_pvt return NULL; } +/*Set up a Conference call on press of flash hook once timer expires */ +static int setup_conference_call_cb( const void *data) +{ + struct brcm_subchannel *sub; + struct brcm_subchannel *sub_peer; + struct ast_channel *peer_owner; + struct ast_channel *owner = NULL; + + sub = (struct brcm_subchannel *) data; + sub_peer = brcm_subchannel_get_peer(sub); + struct brcm_pvt *p = sub->parent; + if (sub->owner) { + ast_channel_ref(sub->owner); + owner = sub->owner; + } + if (sub_peer->owner) { + ast_channel_ref(sub_peer->owner); + peer_owner = sub_peer->owner; + } + /* Setup conference if no dtmf is pressed after flash ,i.e we are not waiting for dtmf now*/ + // Unhold inactive subchannel // + brcm_create_conference(p); + if (peer_owner) { + brcm_unmute_connection(sub_peer); + ast_queue_unhold(peer_owner); + send_ubus_event("UNHOLD",sub->parent->line_id); + sub_peer->channel_state = INCALL; + } + sub->channel_state = ONHOLD; + if (ast_sched_del(sched, sub->conf_timer_id)) { + ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n"); + } + sub->cw_timer_id = -1; + return 0; +} + /* Hangup incoming call after call waiting times out */ static int cwtimeout_cb(const void *data) { @@ -1744,7 +1791,6 @@ static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel struct brcm_pvt *p = sub->parent; ast_log(LOG_DTMF, "Hook Flash detected for phone line %d\r\n", sub->parent->line_id); - struct ast_bridge *bridge; struct ast_bridge_channel *bridge_channel; @@ -1763,7 +1809,6 @@ static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel p->hf_detected = 0; return; } - if (p->dtmf_first < 0) { /* If current subchannel is in call and peer subchannel is idle, provide dialtone */ if (sub->channel_state == INCALL && (sub_peer->channel_state == ONHOOK || sub_peer->channel_state == CALLENDED)) { @@ -1857,7 +1902,7 @@ static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel ast_queue_hold(owner, NULL); } if (sub_peer->channel_state == CALLWAITING) { - ast_log(LOG_NOTICE, " R2 Call waiting\n"); + ast_log(LOG_NOTICE, " R in Call waiting\n"); /* Stop call waiting tone on current call */ brcm_stop_callwaiting(p); /* Cancel timer */ @@ -1873,25 +1918,71 @@ static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel if (peer_owner) { ast_queue_control(peer_owner, AST_CONTROL_ANSWER); sub_peer->channel_state = INCALL; + sub->channel_state = ONHOLD; } } else if (sub_peer->channel_state == ONHOLD) { - ast_log(LOG_NOTICE, " R2 on hold\n"); - /* Unhold inactive subchannel */ - brcm_create_conference(p); - if (peer_owner) { - brcm_unmute_connection(sub_peer); - ast_queue_unhold(peer_owner); - send_ubus_event("UNHOLD",sub->parent->line_id); - sub_peer->channel_state = INCALL; - } - } - sub->channel_state = ONHOLD; + ast_log(LOG_NOTICE, "R on hold\n"); + sub->conf_timer_id = ast_sched_add(sched, dtmf_wait_timer, setup_conference_call_cb, sub); - } - p->hf_detected = 0; - return; + } + } } - brcm_reset_dtmf_buffer(p); + + if (p->dtmf_first > 0) { + switch (p->dtmf_first) { + case '4': + if (ast_sched_del(sched, sub->conf_timer_id)) { + ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n"); + } + sub_peer->conf_timer_id = -1; + + if (sub->channel_state == INCALL && sub_peer->channel_state == ONHOLD && + owner && peer_owner) { + struct ast_channel *bridged_chan_inactive = ast_channel_bridge_peer(peer_owner); + struct ast_channel *bridged_chan_active = ast_channel_bridge_peer(owner); + char dest[AST_CHANNEL_NAME * 2] = { '\0', }; + + if (bridged_chan_inactive && bridged_chan_active) { + // Hold the transfer target by default unless being configured no + if (hold_target_before_refer) + ast_queue_hold(owner, NULL); + + // Start the transfer by sending REFER to the transferee + snprintf(dest, sizeof(dest) - 1, "%s?Replaces=%s", + ast_channel_exten(owner), ast_channel_name(bridged_chan_active)); + ast_debug(1, "Start transfer to [%s] on channel %s\n", dest, ast_channel_name(bridged_chan_active)); + int res = -1; + ast_channel_lock(bridged_chan_inactive); + if (!ast_test_flag(ast_channel_flags(bridged_chan_inactive), AST_FLAG_ZOMBIE) && + !ast_check_hangup(bridged_chan_inactive)) { + if (ast_channel_tech(bridged_chan_inactive)->transfer) { + res = ast_channel_tech(bridged_chan_inactive)->transfer(bridged_chan_inactive, dest); + if (res == 0) + res = 1; + } else + res = 0; + } + ast_channel_unlock(bridged_chan_inactive); + if (res < 0) { + ast_log(LOG_ERROR, "ast_transfer() failed\n"); + } else if (res == 0) { + ast_log(LOG_ERROR, "ast_transfer() is not supported on the peer channel\n"); + } else { + sub->channel_state = TRANSFERING; + } + } else { + ast_log(LOG_ERROR, "can't get the peer channel\n"); + } + } + break; + + default: + ast_log(LOG_ERROR, "Unhandled DTMF %c\n", p->dtmfbuf[0]); + break; + + } + } + brcm_reset_dtmf_buffer(p); } static void handle_dtmf(enum LINE_EVENT event, @@ -2345,7 +2436,8 @@ static void *brcm_process_event(struct endpt_event *ev) { sub->channel_state = ONHOOK; brcm_cancel_dialing_timeouts(p); - + if (sub_peer->channel_state == CALLENDED) + sub_peer->channel_state = ONHOOK; /* Reset the dtmf buffer */ memset(p->dtmfbuf, 0, sizeof(p->dtmfbuf)); p->dtmf_len = 0; @@ -2485,6 +2577,7 @@ static struct brcm_pvt *brcm_allocate_pvt(void) sub->codec = -1; sub->parent = tmp; sub->cw_timer_id = -1; + sub->conf_timer_id = -1; sub->r4_hangup_timer_id = -1; sub->onhold_hangup_timer_id = -1; sub->period = 20; // 20 ms diff --git a/channels/chan_brcm.h b/channels/chan_brcm.h index b94d28f64b358f792027eaf08c9d194c537ae218..5e1eca20d0733d234b886497c4aaaf48d4b84cc6 100644 --- a/channels/chan_brcm.h +++ b/channels/chan_brcm.h @@ -111,6 +111,7 @@ struct brcm_subchannel { int onhold_hangup_timer_id; /* Current onhold hangup timer id, -1 if no active timer */ int conference_initiator; /* True if this subchannel is the original leg in a 3-way conference */ char *conference_id; /* uuid of the conference initiated by this subchannel */ + int conf_timer_id; /* Current conference call timer id, -1 if no active timer */ };