From dd175a63b5056ec76a0f82d22bf42802a94ae1e0 Mon Sep 17 00:00:00 2001 From: Grzegorz Sluja <grzegorz.sluja@iopsys.eu> Date: Fri, 3 Jun 2022 09:16:13 +0000 Subject: [PATCH] The commit includes lot of fixes for call waiting and 3 way call - Call waiting is enabled/disabled now per feature_set. Each line has the feature_set defined and each provider (pjsip endpoint) has line selected. From now on call waiting status can be defined in uci config and changed by feature code, as a result corresponding feature set or endpoint cw status will be changed - Rename some functions and variables which had misleading names - Add 5s beep timer indicating incoming call waiting - Fix 20s timeout when there is already another call in progress - Support call waiting/3 way call for DECT - Implement "exceed call count" checking for line/extension/all --- channels/chan_brcm.c | 408 ++++++++++++++++++++-------- channels/chan_brcm.h | 33 ++- include/asterisk/res_pjsip.h | 2 + res/res_pjsip.c | 6 + res/res_pjsip/pjsip_configuration.c | 51 ++++ res/res_pjsip_session.c | 33 +++ schemas/uci/asterisk.json | 7 - 7 files changed, 409 insertions(+), 131 deletions(-) diff --git a/channels/chan_brcm.c b/channels/chan_brcm.c index e22d616301..cb484fb6c9 100644 --- a/channels/chan_brcm.c +++ b/channels/chan_brcm.c @@ -75,6 +75,7 @@ static void brcm_dialtone_set(struct brcm_pvt *p, dialtone_state state); static int brcm_signal_dialtone(struct brcm_pvt *p); static int brcm_in_conference(const struct brcm_pvt *p); static int cwtimeout_cb(const void *data); +static int cwbeep_cb(const void *data); static int r4hanguptimeout_cb(const void *data); static void brcm_generate_rtp_packet(struct brcm_subchannel *p, uint8_t *packet_buf, int type, int marker, int dtmf_timestamp); static void brcm_interpret_rtcp_packet(struct brcm_subchannel *p, uint8_t *rtcp_frame, uint32_t rtcp_size); @@ -126,12 +127,14 @@ static struct ast_channel *brcm_new(struct brcm_subchannel *subchan, int state, struct ast_format_cap *format); static int handle_dialtone_timeout(const void *data); static int endpt_get_rtp_stats(int line); +static int is_call_waiting_enabled(const char *sip_account); +static int has_call_in_sip_client(const char *sip_account); /* Global brcm channel parameters */ static const char tdesc[] = "Broadcom SLIC Driver"; static const char config[] = "chan_telephony.conf"; static const char broadcast_path[] = "voice.line"; -static line_settings line_config[MAX_NUM_LINEID]; +static channel_settings channel_config[MAX_NUM_LINEID]; static int current_connection_id = 0; static int num_fxs_endpoints = -1; static int num_fxo_endpoints = -1; @@ -143,6 +146,7 @@ static struct ast_sched_context *sched = NULL; /* Call waiting */ static int cwtimeout = DEFAULT_CALL_WAITING_TIMEOUT; +static int cwBeep = DEFAULT_CALL_WAITING_BEEP_FREQ; /* R4 transfer */ static int r4hanguptimeout = DEFAULT_R4_HANGUP_TIMEOUT; @@ -150,8 +154,9 @@ static int r4hanguptimeout = DEFAULT_R4_HANGUP_TIMEOUT; /* Automatic call on hold hangup */ static int onholdhanguptimeout = DEFAULT_ONHOLD_HANGUP_TIMEOUT; -/* Max call count per line */ -static int max_sessions_per_line; +/* Max call count per extension */ +#define DEFAULT_MAX_SESSION_PER_EXTENSION 2 +static int max_sessions_per_extension = DEFAULT_MAX_SESSION_PER_EXTENSION; /* Boolean, controls whether the transferor puts the transfer target on-hold before sending * REFER to the transferee */ @@ -266,6 +271,10 @@ static struct endpt_event event_map[] = { { .name = "EARLY_ONHOOK", .event = EVENT_EARLY_ONHOOK }, { .name = "FLASH", .event = EVENT_FLASH }, { .name = "CALL_REJECT", .event = EVENT_CALL_REJECT }, + { .name = "MEDIA", .event = EVENT_MEDIA }, + { .name = "SWITCH", .event = EVENT_SWITCH }, + { .name = "JOIN", .event = EVENT_JOIN }, + { .name = "RELEASE", .event = EVENT_RELEASE }, { .name = "", .event = EVENT_LAST }, }; @@ -322,7 +331,7 @@ AST_MUTEX_DEFINE_STATIC(monlock); static struct ast_format_cap *default_cap; static int load_common_settings(struct ast_config **cfg); -static void load_lines_settings(struct ast_config *cfg); +static void load_settings(struct ast_config *cfg); static char *state2str(enum brcm_channel_state state); /* exported capabilities */ @@ -507,7 +516,7 @@ static void endpt_connection(int line, int id, char *action) { blob_buf_init(&bb, 0); blobmsg_add_u32(&bb, "line", line); - blobmsg_add_u32(&bb, "id", line); + blobmsg_add_u32(&bb, "id", id); blobmsg_add_string(&bb, "action", action); req = calloc(1, sizeof(struct ubus_request)); @@ -568,6 +577,10 @@ static int brcm_indicate(struct ast_channel *ast, int condition, const void *dat case AST_CONTROL_RINGING: sub->channel_state = RINGBACK; endpt_signal(sub->parent->line_id, "ringback", "on", NULL); + if (sub->owner) { + sub->call_id = ast_channel_callid(sub->owner); + endpt_connection(sub->parent->line_id, sub->call_id, "update"); + } res = 0; break; case AST_CONTROL_TRANSFER: @@ -595,8 +608,29 @@ static int brcm_indicate(struct ast_channel *ast, int condition, const void *dat case AST_CONTROL_CONNECTED_LINE: ast_debug(4, "Got CONNECTED LINE UPDATE on %s\n", ast_channel_name(ast)); /* Update caller IDs on display - dect ? */ - //TODO Ronny: perhaps reverse loop current here? - res = -1; + struct brcm_subchannel *sub_peer = brcm_subchannel_get_peer(sub); + if ((sub->connection_id != -1) && (sub_peer->connection_id != -1)) { + struct ast_bridge *bridge = ast_channel_internal_bridge(sub->owner); + struct ast_bridge_channel *bridge_channel; + if(bridge){ + // Find out which end party of call conference hangs up + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + if (strstr(ast_channel_name(bridge_channel->chan), "PJSIP") != NULL) { + if (sub->call_id == ast_channel_callid(bridge_channel->chan)) { + endpt_connection(sub_peer->parent->line_id, sub_peer->call_id, "release"); + sub_peer->channel_state = ONHOOK; + } + else if(sub_peer->call_id == ast_channel_callid(bridge_channel->chan)) { + endpt_connection(sub->parent->line_id, sub->call_id, "release"); + sub->channel_state = ONHOOK; + } + } + } + } + res = 0; + } else { + res = -1; + } break; case AST_CONTROL_BUSY: @@ -914,28 +948,29 @@ static int brcm_call(struct ast_channel *chan, const char *dest, int timeout) p = sub->parent; sub_peer = brcm_subchannel_get_peer(sub); - if (!line_config[p->line_id].enabled) { + if (!channel_config[p->line_id].enabled) { ast_debug(1, "tel_line %d disabled!\n", p->line_id); ast_channel_hangupcause_set(chan, AST_CAUSE_CALL_REJECTED); ast_queue_control(chan, AST_CONTROL_BUSY); brcm_send_ubus_event("CALL_REJECTED",p->line_id); } - else if (line_config[p->line_id].do_not_disturb) { + else if (channel_config[p->line_id].do_not_disturb) { ast_debug(1, "Do not disturbed\n"); ast_channel_hangupcause_set(chan, AST_CAUSE_USER_BUSY); brcm_send_ubus_event("USER_BUSY",p->line_id); ast_queue_control(chan, AST_CONTROL_BUSY); } - else if (brcm_in_call(p) && // a call is established - line_config[p->line_id].callwaiting && // call waiting active - !sub_peer->cw_rejected) { // a previous call has not been rejected using R0 + else if (brcm_in_call(p) && // a call is established + is_call_waiting_enabled(p->context)) { // call waiting enabled ast_debug(1, "Call waiting\n"); sub->channel_state = CALLWAITING; brcm_signal_callwaiting(p); int cwtimeout_ms = cwtimeout * 1000; + int cwBeep_ms = cwBeep * 1000; sub->cw_timer_id = ast_sched_add(sched, cwtimeout_ms, cwtimeout_cb, sub); - ast_setstate(chan, AST_STATE_RINGING_CW); - ast_queue_control(chan, AST_CONTROL_RINGING_CW); + sub->cwBeep_timer_id = ast_sched_add(sched, cwBeep_ms, cwbeep_cb, sub); + ast_setstate(chan, AST_STATE_RINGING_CW); + ast_queue_control(chan, AST_CONTROL_RINGING_CW); } else if (!brcm_subchannel_is_idle(sub_peer)) { ast_debug(1, "Line is busy\n"); @@ -967,14 +1002,20 @@ static int brcm_call(struct ast_channel *chan, const char *dest, int timeout) /*We can move status to Alerting ,since we are ringing now */ strncpy(p->extensionCallStatus, "Alerting", CALL_STATUS_MAX_LEN); sub->channel_state = RINGING; - if (!line_config[p->line_id].calleridenable) { + if (!channel_config[p->line_id].calleridenable) { p->tech->signal_ringing(p); } else { p->tech->signal_ringing_callerid_pending(p); p->tech->signal_callerid(chan, sub); } - ast_setstate(chan, AST_STATE_RINGING); - ast_queue_control(chan, AST_CONTROL_RINGING); + if (has_call_in_sip_client(p->context)) { + sub->cw_timer_id = ast_sched_add(sched, cwtimeout*1000, cwtimeout_cb, sub); + ast_setstate(chan, AST_STATE_RINGING_CW); + ast_queue_control(chan, AST_CONTROL_RINGING_CW); + } else { + ast_setstate(chan, AST_STATE_RINGING); + ast_queue_control(chan, AST_CONTROL_RINGING); + } brcm_send_ubus_event("RINGING",p->line_id); } //ast_mutex_unlock(&sub->parent->lock); @@ -1002,7 +1043,6 @@ static int brcm_hangup(struct ast_channel *ast) ast_debug(1, "ast=%s line_id=%d connection_id=%d dialtone=%s channel_state=%s peer_state=%s\n", ast_channel_name(ast), p->line_id, sub->connection_id, dialtone_map[p->dialtone].str, state2str(sub->channel_state), state2str(sub_peer->channel_state)); - /* If call is not connected (INCALL) or is dialing but Subchannel is busy, move to Disconnected state */ if ((sub->channel_state!= ONHOOK && sub_peer->channel_state != OFFHOOK)) { @@ -1041,7 +1081,7 @@ static int brcm_hangup(struct ast_channel *ast) endpt_signal(sub->parent->line_id, "ringback", "off", NULL); } else if (sub->channel_state == RINGING || sub->onhold_hangup_timer_id != -1) { //Stop ringing if other end hungup before we answered - line_settings *s = &line_config[p->line_id]; + channel_settings *s = &channel_config[p->line_id]; if (!s->calleridenable) { p->tech->stop_ringing(p); } else { @@ -1060,6 +1100,13 @@ static int brcm_hangup(struct ast_channel *ast) sub->cw_timer_id = -1; } + if (sub->cwBeep_timer_id != -1) { + if (ast_sched_del(sched, sub->cwBeep_timer_id)) { + ast_log(LOG_WARNING, "Failed to remove scheduled call waiting beep timer\n"); + } + sub->cwBeep_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"); @@ -1118,7 +1165,6 @@ static int brcm_hangup(struct ast_channel *ast) sub->conference_initiator = 0; free(sub->conference_id); sub->conference_id = NULL; - sub->cw_rejected = 0; ast_module_unref(ast_module_info->self); ast_verb(3, "Hungup '%s'\n", ast_channel_name(ast)); ast_channel_tech_pvt_set(ast, NULL); @@ -1131,6 +1177,13 @@ static int brcm_hangup(struct ast_channel *ast) handle_dialtone_timeout(p); } + if (sub_peer->cwBeep_timer_id != -1) { + if (ast_sched_del(sched, sub_peer->cwBeep_timer_id)) { + ast_log(LOG_WARNING, "Failed to remove scheduled call waiting beep timer\n"); + } + sub_peer->cwBeep_timer_id = -1; + } + pvt_unlock(p); return 0; } @@ -1485,7 +1538,7 @@ static struct ast_channel *brcm_new(struct brcm_subchannel *subchan, int state, ao2_ref(caps, -1); fmt = ast_format_cap_get_format(ast_channel_nativeformats(chan), 0); - line_settings *s = &line_config[subchan->parent->line_id]; + channel_settings *s = &channel_config[subchan->parent->line_id]; /* set codecs */ ast_channel_set_writeformat(chan, fmt); @@ -1646,17 +1699,27 @@ static int cwtimeout_cb(const void *data) struct brcm_subchannel *sub; struct ast_channel *owner = NULL; - ast_debug(2, "No response to call waiting, hanging up\n"); sub = (struct brcm_subchannel *) data; + ast_debug(2, "No response to call waiting, hanging up, sub->channel_state: %d\n", sub->channel_state); //ast_mutex_lock(&sub->parent->lock); pvt_lock(sub->parent, "Cwtimeout callback"); + if (sub->cwBeep_timer_id != -1) { + if (ast_sched_del(sched, sub->cwBeep_timer_id)) { + ast_log(LOG_WARNING, "Failed to remove scheduled call waiting beep timer\n"); + } + sub->cwBeep_timer_id = -1; + } sub->cw_timer_id = -1; if (sub->owner) { ast_channel_ref(sub->owner); owner = sub->owner; } //ast_mutex_unlock(&sub->parent->lock); + if (sub->channel_state == CALLWAITING) + brcm_stop_callwaiting(sub->parent); + else if (sub->channel_state == RINGING) + brcm_stop_ringing(sub->parent); pvt_unlock(sub->parent); if (owner) { @@ -1670,6 +1733,23 @@ static int cwtimeout_cb(const void *data) return 0; } +/* Play beep during call waiting */ +static int cwbeep_cb(const void *data) +{ + struct brcm_subchannel *sub = (struct brcm_subchannel *) data; + int cwBeep_ms = cwBeep * 1000; + + pvt_lock(sub->parent, "cwbeep_cb callback"); + if (sub->parent) { + brcm_signal_callwaiting(sub->parent); + } + pvt_unlock(sub->parent); + + sub->cwBeep_timer_id = ast_sched_add(sched, cwBeep_ms, cwbeep_cb, sub); + + return 0; +} + /* Hangup calls if not done by remote after R4 transfer */ static int r4hanguptimeout_cb(const void *data) { @@ -1787,7 +1867,7 @@ static int handle_interdigit_timeout(const void *data) pvt_lock(p, "interdigit callback"); p->interdigit_timer_id = -1; struct brcm_subchannel *sub = brcm_get_active_subchannel(p); - if (line_config[p->line_id].minimumnumberdigits && strlen(p->dtmfbuf) >= line_config[p->line_id].minimumnumberdigits){ + if (channel_config[p->line_id].minimumnumberdigits && strlen(p->dtmfbuf) >= channel_config[p->line_id].minimumnumberdigits){ if (ast_exists_extension(NULL, p->context, p->dtmfbuf, 1, p->cid_num)) { //We have at least one matching extension in "normal" context, @@ -1798,7 +1878,7 @@ static int handle_interdigit_timeout(const void *data) } } //if no limit on minimum digits check extension exits - if(!line_config[p->line_id].minimumnumberdigits){ + if(!channel_config[p->line_id].minimumnumberdigits){ if (ast_exists_extension(NULL, p->context, p->dtmfbuf, 1, p->cid_num)) { //We have at least one matching extension in "normal" context, @@ -1825,7 +1905,7 @@ static int handle_autodial_timeout(const void *data) //ast_mutex_lock(&p->lock); p->autodial_timer_id = -1; struct brcm_subchannel *sub = brcm_get_active_subchannel(p); - line_settings *s = &line_config[p->line_id]; + channel_settings *s = &channel_config[p->line_id]; if (ast_exists_extension(NULL, p->context, s->autodial_ext, 1, p->cid_num)) { @@ -1857,7 +1937,7 @@ static int handle_dialtone_timeout(const void *data) if (sub && (sub->channel_state == OFFHOOK || sub->channel_state == AWAITONHOOK || sub->channel_state == CALLENDED)) { /* Enter state where nothing else other than ONHOOK is accepted and play series of tones */ sub->channel_state = AWAITONHOOK; - line_settings *s = &line_config[p->line_id]; + channel_settings *s = &channel_config[p->line_id]; switch (p->dialtone) { case DIALTONE_ON: @@ -1897,7 +1977,7 @@ void handle_dtmf_calling(struct brcm_subchannel *sub) char dtmf_last_char = p->dtmfbuf[(dtmfbuf_len - 1)]; char dtmf_one_before_last_char = p->dtmfbuf[(dtmfbuf_len > 1 ? dtmfbuf_len - 2 : 0)]; - termination_digit = line_config[p->line_id].terminationdigit?line_config[p->line_id].terminationdigit:0x23; + termination_digit = channel_config[p->line_id].terminationdigit?channel_config[p->line_id].terminationdigit:0x23; if (ast_exists_extension(NULL, p->context_direct, p->dtmfbuf, 1, p->cid_num) && !ast_matchmore_extension(NULL, p->context_direct, p->dtmfbuf, 1, p->cid_num)) { @@ -1909,13 +1989,13 @@ void handle_dtmf_calling(struct brcm_subchannel *sub) else if (ast_exists_extension(NULL, p->context, p->dtmfbuf, 1, p->cid_num) && dtmf_last_char == termination_digit && dtmf_one_before_last_char != 0x2A && feature_access_code_match(p->dtmfbuf) != 1) { - if(line_config[p->line_id].minimumnumberdigits) + if(channel_config[p->line_id].minimumnumberdigits) { //Minimum dtmf limit is checked before starting a call.If limit is not reached,ignore //We have a match in the "normal" context, and user ended the dialling sequence with a # or temination digit set in dialplan, //so have asterisk place a call immediately if sequence is not partially matching a feature access code ast_log(LOG_NOTICE, "Termination key %c pressed during dialling, extension %s found\n",termination_digit, p->dtmfbuf); - if(strlen(p->dtmfbuf) >= line_config[p->line_id].minimumnumberdigits) + if(strlen(p->dtmfbuf) >= channel_config[p->line_id].minimumnumberdigits) brcm_start_calling(p, sub, p->context); else ast_log(LOG_ERROR, "Not reached the minimum digits to start a call!! \n"); @@ -1946,25 +2026,25 @@ void handle_dtmf_calling(struct brcm_subchannel *sub) ast_debug(9, "number not matching any extensions, awaiting on hook\n"); p->dialtone = DIALTONE_UNOBTAINABLE; sub->channel_state = AWAITONHOOK; - p->dialtone_timeout_timer_id = ast_sched_add(sched, line_config[p->line_id].offhook_nu_timeoutmsec, handle_dialtone_timeout, p); + p->dialtone_timeout_timer_id = ast_sched_add(sched, channel_config[p->line_id].offhook_nu_timeoutmsec, handle_dialtone_timeout, p); brcm_signal_dialtone(p); } else { - if (line_config[p->line_id].minimumnumberdigits && strlen(p->dtmfbuf) >= line_config[p->line_id].minimumnumberdigits ){ - int interdigitopenmsec = line_config[p->line_id].interdigitopenmsec; + if (channel_config[p->line_id].minimumnumberdigits && strlen(p->dtmfbuf) >= channel_config[p->line_id].minimumnumberdigits ){ + int interdigitopenmsec = channel_config[p->line_id].interdigitopenmsec; if (interdigitopenmsec){ ast_debug(9,"Scheduling interdigitopenmsec timeout of %d msec\n", interdigitopenmsec); p->interdigit_timer_id = ast_sched_add(sched, interdigitopenmsec, handle_interdigit_timeout, p); } else{ - int timeoutmsec = line_config[p->line_id].timeoutmsec; + int timeoutmsec = channel_config[p->line_id].timeoutmsec; ast_debug(9,"Interdigitopenmsec not found !!Using timeoutsec timeout of %d msec\n", timeoutmsec); p->interdigit_timer_id = ast_sched_add(sched, timeoutmsec, handle_interdigit_timeout, p); } } else { //No matches. We schedule a (new) interdigit timeout to occur - int timeoutmsec = line_config[p->line_id].timeoutmsec; + int timeoutmsec = channel_config[p->line_id].timeoutmsec; ast_debug(9, "Scheduling interdigit timeout of %d msec\n", timeoutmsec); p->interdigit_timer_id = ast_sched_add(sched, timeoutmsec, handle_interdigit_timeout, p); } @@ -1998,8 +2078,8 @@ static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel } if (sub->channel_state == INCALL){ - if (bridge && bridge->num_active > max_sessions_per_line) { - ast_log(LOG_WARNING, "Max call limit exceeded\n"); + if (bridge && bridge->num_active > max_sessions_per_extension) { + ast_log(LOG_WARNING, "Max call per extension limit exceeded [%d/%d]\n", bridge->num_active, max_sessions_per_extension); p->hf_detected = 0; return; } @@ -2033,6 +2113,7 @@ static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel p->dialtone = DIALTONE_ON; brcm_signal_dialtone(p); sub_peer->channel_state = OFFHOOK; + sub_peer->call_direction = OUTGOING_CALL; /* If offhook/dialing/calling and peer subchannel is on hold, switch call */ } else if ((sub->channel_state == DIALING || sub->channel_state == OFFHOOK || @@ -2100,13 +2181,15 @@ static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel ast_queue_hold(owner, NULL); } ast_log(LOG_NOTICE, " R in Call waiting\n"); - /* Stop call waiting tone on current call */ - brcm_stop_callwaiting(p); - /* Cancel timer */ + /* Cancel timers */ if (ast_sched_del(sched, sub_peer->cw_timer_id)) { ast_log(LOG_WARNING, "Failed to remove scheduled call waiting timer\n"); } sub_peer->cw_timer_id = -1; + if (ast_sched_del(sched, sub_peer->cwBeep_timer_id)) { + ast_log(LOG_WARNING, "Failed to remove scheduled call waiting beep timer\n"); + } + sub_peer->cwBeep_timer_id = -1; /* Pick up call waiting */ if (!sub_peer->connection_init) { ast_debug(9, "create_connection()\n"); @@ -2115,12 +2198,24 @@ 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_peer->call_direction = INCOMING_CALL; sub->channel_state = ONHOLD; } } else if (sub_peer->channel_state == ONHOLD) { - ast_log(LOG_NOTICE, "R on hold \n"); - sub->channel_state = INCALL; - sub->conf_timer_id = ast_sched_add(sched, dtmf_wait_timer, setup_conference_call_cb, p); + ast_log(LOG_NOTICE, "R on hold, line: %d direction of 2nd call is %s\n", + p->line_id, sub->call_direction == INCOMING_CALL ? "incoming" : "outgoing"); + if (sub->call_direction == OUTGOING_CALL) { + sub->channel_state = INCALL; + sub->conf_timer_id = ast_sched_add(sched, dtmf_wait_timer, setup_conference_call_cb, p); + } else if (sub->call_direction == INCOMING_CALL){ + brcm_unmute_connection(sub_peer); + ast_queue_unhold(peer_owner); + sub_peer->channel_state = INCALL; + + brcm_mute_connection(sub); + ast_queue_hold(owner, NULL); + sub->channel_state = ONHOLD; + } } } else if (sub->channel_state == DIALING || sub->channel_state == OFFHOOK || sub->channel_state == RINGBACK) { ast_log(LOG_NOTICE, "R while offhook/dialing\n"); @@ -2198,6 +2293,42 @@ static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel brcm_reset_dtmf_buffer(p); } +static void handle_dect_event(struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer, + struct ast_channel *owner, struct ast_channel *peer_owner, enum LINE_EVENT event) +{ + struct brcm_pvt *p = sub->parent; + + if (event == EVENT_JOIN) { + ast_log(LOG_NOTICE, "Join detected for phone line %d\n", sub->parent->line_id); + + // Start 3way call conference + if (sub->channel_state == INCALL && sub_peer->channel_state == ONHOLD) + sub->conf_timer_id = ast_sched_add(sched, dtmf_wait_timer, setup_conference_call_cb, p); + } + else if (event == EVENT_SWITCH) { + ast_log(LOG_NOTICE, "Switch detected for phone line %d\n", sub->parent->line_id); + // Toggle calls + if (sub->channel_state == INCALL && sub_peer->channel_state == ONHOLD) { + brcm_unmute_connection(sub_peer); + ast_queue_unhold(peer_owner); + sub_peer->channel_state = INCALL; + + brcm_mute_connection(sub); + ast_queue_hold(owner, NULL); + sub->channel_state = ONHOLD; + } + } + else if (event == EVENT_RELEASE) { + ast_log(LOG_NOTICE, "EVENT_RELEASE detected for phone line %d\n", sub->parent->line_id); + ast_log(LOG_NOTICE, "sub->channel_state = %d, sub_peer->channel_state = %d\n", sub->channel_state, sub_peer->channel_state); + if (sub->channel_state == INCALL && sub_peer->channel_state != CALLWAITING) { + ast_log(LOG_ERROR, "Hanging up call\n"); + ast_queue_control(owner, AST_CONTROL_HANGUP); + } + } + brcm_reset_dtmf_buffer(p); +} + static void handle_dtmf(enum LINE_EVENT event, struct brcm_subchannel *sub, struct brcm_subchannel *sub_peer, struct ast_channel *owner, struct ast_channel *peer_owner) @@ -2509,12 +2640,37 @@ static int voicemail_messages_waiting(const char *sip_account) return 0; } +static int check_endpoint_cw_enabled(const char *line, const char *data) +{ + return (strstr(line, "call_waiting_enabled") != NULL && strstr(line, "true") != NULL); + +} + +static int check_endpoint_state_in_use(const char *line, const char *data) +{ + return (strstr(line, "Endpoint:") != NULL && strstr(line, "In use") != NULL); + +} + +static int is_call_waiting_enabled(const char *sip_account) +{ + char cmd[32]; + sprintf(cmd, "pjsip show endpoint %s", sip_account); + return call_cli_command(cmd, &check_endpoint_cw_enabled, NULL); +} +static int has_call_in_sip_client(const char *sip_account) +{ + char cmd[32]; + sprintf(cmd, "pjsip show endpoint %s", sip_account); + return call_cli_command(cmd, &check_endpoint_state_in_use, NULL); +} + static int check_is_sip_account_registered(const char *line, const char *sip_account) { /* The example output of the command is shown below. * Some extra spaces are removed since the line is too long. * sip0:5060 N 1313@10.0.2. 285 Registered Mon, 17 Jun 2019 17:12:58 */ - return ((strstr(line, sip_account) != NULL) && (strstr(line, "Registered") != NULL));; + return ((strstr(line, sip_account) != NULL) && (strstr(line, "Registered") != NULL)); } static int is_sip_account_registered(const char *sip_account) @@ -2567,7 +2723,7 @@ static void brcm_process_event(struct endpt_event *ev) ast_debug(3, "Received event %s from line %d\n", ev->name, ev->line); // Ignore events when the telline is not enabled - if (!line_config[ev->line].enabled) { + if (!channel_config[ev->line].enabled) { ast_debug(3, "Line %d disabled, ignore event %s!\n", ev->line, ev->name); return; } @@ -2644,6 +2800,13 @@ static void brcm_process_event(struct endpt_event *ev) sub->cw_timer_id = -1; } + if (sub->cwBeep_timer_id > -1) { + /* Picking up during reminder ringing for call waiting */ + if (ast_sched_del(sched, sub->cwBeep_timer_id) < 0) + ast_debug(3, "Error deleting timer\n"); + sub->cwBeep_timer_id = -1; + } + sub->channel_state = INCALL; ast_queue_control(owner, AST_CONTROL_ANSWER); brcm_send_ubus_event("ANSWERED_CALL",p->line_id); @@ -2657,7 +2820,7 @@ static void brcm_process_event(struct endpt_event *ev) sub_peer->channel_state = INCALL; ast_queue_control(peer_owner, AST_CONTROL_ANSWER); brcm_unmute_connection(sub_peer); - endpt_connection(sub_peer->parent->line_id, sub_peer->connection_id, "create"); + endpt_connection(sub_peer->parent->line_id, sub_peer->call_id, "create"); ast_queue_unhold(peer_owner); brcm_send_ubus_event("CALL_UNHOLD", p->line_id); } else if (sub->channel_state == OFFHOOK) { @@ -2667,7 +2830,7 @@ static void brcm_process_event(struct endpt_event *ev) p->dialtone = voicemail_messages_waiting(p->context) ? mwi_dialtone_state : DIALTONE_ON; brcm_signal_dialtone(p); brcm_send_ubus_event("DIALTONE_ON", p->line_id); - line_settings *s = &line_config[p->line_id]; + channel_settings *s = &channel_config[p->line_id]; if (strlen(s->autodial_ext)) { /* Schedule autodial timeout if autodial extension is set */ p->autodial_timer_id = ast_sched_add(sched, s->autodial_timeoutmsec, handle_autodial_timeout, p); @@ -2718,7 +2881,8 @@ static void brcm_process_event(struct endpt_event *ev) /* Remind user of call on hold */ sub_peer->onhold_hangup_timer_id = ast_sched_add(sched, onholdhanguptimeout * 1000, onholdhanguptimeout_cb, sub_peer); ast_debug(2, "Forgotten call on hold for %s!\n", ast_channel_name(peer_owner)); - endpt_connection(sub_peer->parent->line_id, sub_peer->connection_id, "destroy"); + endpt_connection(sub_peer->parent->line_id, sub_peer->call_id, "destroy"); + usleep(500000); p->tech->signal_ringing_callerid_pending(p); p->tech->signal_callerid(sub_peer->owner, sub_peer); sub->channel_state = RINGING; @@ -2771,11 +2935,15 @@ static void brcm_process_event(struct endpt_event *ev) break; case EVENT_CALL_REJECT: - if (sub->channel_state == RINGING) { + if (sub->channel_state == RINGING || (sub->channel_state == INCALL && sub_peer->channel_state == CALLWAITING)) { const char *linkedid = ast_channel_linkedid(owner); + const char *peer_linkedid; struct brcm_pvt *pvt = iflist; int i; + if (peer_owner) + peer_linkedid = ast_channel_linkedid(peer_owner); + /* Release all requested channels for the same incoming call when one channel rejects, e.g. from DECT handset */ while (pvt) { pvt_lock(pvt, "lock pvt for call reject"); @@ -2784,10 +2952,17 @@ static void brcm_process_event(struct endpt_event *ev) struct brcm_subchannel *sc = pvt->sub[i]; if ((sc->channel_state == RINGING || sc->channel_state == CALLWAITING) && sc->owner) { const char *link = ast_channel_linkedid(sc->owner); - if (link == linkedid || strcmp(link, linkedid) == 0) { + + if (sub->channel_state == RINGING && (link == linkedid || strcmp(link, linkedid) == 0)) { ast_debug(2, "Hang up the unanswered call on channel %s since %s has rejected\n", ast_channel_name(sc->owner), ast_channel_name(owner)); + ast_channel_hangupcause_set(sc->owner, AST_CAUSE_CALL_REJECTED); + ast_queue_control(sc->owner, AST_CONTROL_HANGUP); + } else if (sub->channel_state == INCALL && (link == peer_linkedid || strcmp(link, peer_linkedid) == 0)) { + ast_debug(2, "Hang up the unanswered call on channel %s since %s has rejected\n", + ast_channel_name(sc->owner), ast_channel_name(peer_owner)); + ast_channel_hangupcause_set(sc->owner, AST_CAUSE_CALL_REJECTED); ast_queue_control(sc->owner, AST_CONTROL_HANGUP); } @@ -2801,7 +2976,12 @@ static void brcm_process_event(struct endpt_event *ev) brcm_send_ubus_event("CALL_REJECTED", ev->line); } break; - + case EVENT_SWITCH: + case EVENT_JOIN: + case EVENT_RELEASE: + ast_debug(1, "EVENT %d from dect received\n", ev->event); + handle_dect_event(sub, sub_peer, owner, peer_owner, ev->event); + break; default: ast_debug(1, "Ignore event %s\n", ev->name); break; @@ -2822,7 +3002,7 @@ static void brcm_process_event(struct endpt_event *ev) /* Load settings for each line */ static void brcm_initialize_pvt(struct brcm_pvt *p) { - line_settings *s = &line_config[p->line_id]; + channel_settings *s = &channel_config[p->line_id]; ast_copy_string(p->language, s->language, sizeof(p->language)); ast_copy_string(p->context, s->context, sizeof(p->context)); @@ -2845,6 +3025,7 @@ static struct brcm_pvt *brcm_allocate_pvt(void) sub = ast_calloc(1, sizeof(*sub)); if (sub) { sub->id = i; + sub->call_id = 0; sub->owner = NULL; sub->connection_id = -1; sub->connection_init = 0; @@ -2855,6 +3036,7 @@ static struct brcm_pvt *brcm_allocate_pvt(void) sub->codec = -1; sub->parent = tmp; sub->cw_timer_id = -1; + sub->cwBeep_timer_id = -1; sub->conf_timer_id = -1; sub->r4_hangup_timer_id = -1; sub->onhold_hangup_timer_id = -1; @@ -3115,7 +3297,7 @@ static void brcm_show_subchannels(struct ast_cli_args *a, struct brcm_pvt *p) ast_cli(a->fd, " RTP SSRC : %d\n", sub->ssrc); ast_cli(a->fd, " RTP timestamp : %d\n", sub->time_stamp); ast_cli(a->fd, " CW Timer id : %d\n", sub->cw_timer_id); - ast_cli(a->fd, " CW Rejected : %d\n", sub->cw_rejected); + ast_cli(a->fd, " CW Beep Timer id : %d\n", sub->cwBeep_timer_id); ast_cli(a->fd, " R4 Hangup Timer id : %d\n", sub->r4_hangup_timer_id); ast_cli(a->fd, " Conference initiator: %d\n", sub->conference_initiator); ast_cli(a->fd, " Onhold Hangup Timer id: %d\n", sub->onhold_hangup_timer_id); @@ -3136,7 +3318,7 @@ static void brcm_show_pvts(struct ast_cli_args *a) ast_cli(a->fd, "DTMF buffer : %s\n", p->dtmfbuf); ast_cli(a->fd, "Default context : %s\n", p->context); ast_cli(a->fd, "Direct context : %s\n", p->context_direct); - line_settings* s = &line_config[p->line_id]; + channel_settings* s = &channel_config[p->line_id]; ast_cli(a->fd, "Ringsignal : %s\n", s->ringsignal ? "on" : "off"); ast_cli(a->fd, "Dialout msecs : %d\n", s->timeoutmsec); @@ -3146,7 +3328,6 @@ static void brcm_show_pvts(struct ast_cli_args *a) ast_cli(a->fd, "Ast JitterBuf impl : %s\n", global_jbconf.impl); ast_cli(a->fd, "Ast JitterBuf max : %ld\n", global_jbconf.max_size); - ast_cli(a->fd, "Call waiting : %s\n", s->callwaiting ? "on" : "off"); ast_cli(a->fd, "Do not disturb : %s\n", s->do_not_disturb ? "on" : "off"); ast_cli(a->fd, "CLIR : %s\n", s->anonymouscallenable ? "on" : "off"); @@ -3266,7 +3447,7 @@ static char *brcm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args * return CLI_FAILURE; } - load_lines_settings(cfg); + load_settings(cfg); struct brcm_pvt *p = iflist; while(p) { brcm_initialize_pvt(p); @@ -3305,7 +3486,7 @@ static char *brcm_set_parameters_on_off(struct ast_cli_entry *e, int cmd, struct if (pvt_id >= num_endpoints || pvt_id < 0) { return CLI_SHOWUSAGE; } - line_settings *s = &line_config[pvt_id]; + channel_settings *s = &channel_config[pvt_id]; if (!strcasecmp(a->argv[3], "on")) { on_off = 1; @@ -3338,7 +3519,7 @@ static char *brcm_set_parameters_value(struct ast_cli_entry *e, int cmd, struct if (pvt_id >= num_endpoints || pvt_id < 0) { return CLI_SHOWUSAGE; } - line_settings *s = &line_config[pvt_id]; + channel_settings *s = &channel_config[pvt_id]; s->timeoutmsec = atoi(a->argv[3]); @@ -3367,7 +3548,7 @@ static char *brcm_set_autodial_extension(struct ast_cli_entry *e, int cmd, struc p = iflist; while(p) { if (p->line_id == (a->argv[3][0]-'0')) { - line_settings *s = &line_config[p->line_id]; + channel_settings *s = &channel_config[p->line_id]; ast_copy_string(s->autodial_ext, a->argv[4], sizeof(s->autodial_ext)); break; } @@ -3469,11 +3650,11 @@ static int unload_module(void) } /* - * Create a line_settings struct with default values. + * Create a channel_settings struct with default values. */ -static line_settings line_settings_create(void) +static channel_settings channel_settings_create(void) { - line_settings line_conf = (line_settings){ + channel_settings line_conf = (channel_settings){ .enabled = 1, .language = "", .cid_num = "", @@ -3489,7 +3670,6 @@ static line_settings line_settings_create(void) .dialtone_timeoutmsec = 20000, .offhook_nu_timeoutmsec = 60000, .offhook_silence_timeoutmsec = 180000, - .callwaiting = 1, .do_not_disturb = 0, .calleridenable = 0, //clip .calleridnameenable = 0, //cnip @@ -3500,60 +3680,57 @@ static line_settings line_settings_create(void) } /* - * Load config file settings into the specified line_settings struct. + * Load config file settings into the specified channel_settings struct. * Can be called multiple times in order to load from multiple ast_variables. */ -static void line_settings_load(line_settings *line_config, struct ast_variable *v) +static void channel_settings_load(channel_settings *channel_config, struct ast_variable *v) { while(v) { if (!strcasecmp(v->name, "enabled")) { - line_config->enabled = ast_true(v->value)?1:0; + channel_config->enabled = ast_true(v->value)?1:0; } else if (!strcasecmp(v->name, "language")) { - ast_copy_string(line_config->language, v->value, sizeof(line_config->language)); + ast_copy_string(channel_config->language, v->value, sizeof(channel_config->language)); } else if (!strcasecmp(v->name, "callerid")) { - ast_callerid_split(v->value, line_config->cid_name, sizeof(line_config->cid_name), line_config->cid_num, sizeof(line_config->cid_num)); + ast_callerid_split(v->value, channel_config->cid_name, sizeof(channel_config->cid_name), channel_config->cid_num, sizeof(channel_config->cid_num)); } else if (!strcasecmp(v->name, "context")) { - ast_copy_string(line_config->context, v->value, sizeof(line_config->context)); + ast_copy_string(channel_config->context, v->value, sizeof(channel_config->context)); } else if (!strcasecmp(v->name, "context_direct")) { - ast_copy_string(line_config->context_direct, v->value, sizeof(line_config->context_direct)); + ast_copy_string(channel_config->context_direct, v->value, sizeof(channel_config->context_direct)); } else if (!strcasecmp(v->name, "autodial")) { - ast_copy_string(line_config->autodial_ext, v->value, sizeof(line_config->autodial_ext)); + ast_copy_string(channel_config->autodial_ext, v->value, sizeof(channel_config->autodial_ext)); } else if (!strcasecmp(v->name, "ringsignal")) { - line_config->ringsignal = ast_true(v->value)?1:0; + channel_config->ringsignal = ast_true(v->value)?1:0; } else if (!strcasecmp(v->name, "dialoutmsec")) { - line_config->timeoutmsec = atoi(v->value); + channel_config->timeoutmsec = atoi(v->value); } else if (!strcasecmp(v->name, "interdigitopenmsec")) { - line_config->interdigitopenmsec = atoi(v->value); + channel_config->interdigitopenmsec = atoi(v->value); } else if (!strcasecmp(v->name, "minimumnumberdigits")) { - line_config->minimumnumberdigits = atoi(v->value); + channel_config->minimumnumberdigits = atoi(v->value); } else if (!strcasecmp(v->name, "terminationdigit")) { - line_config->terminationdigit =v->value[0]; + channel_config->terminationdigit =v->value[0]; } else if (!strcasecmp(v->name, "autodial_timeoutmsec")) { - line_config->autodial_timeoutmsec = atoi(v->value); + channel_config->autodial_timeoutmsec = atoi(v->value); } else if (!strcasecmp(v->name, "dialtone_timeoutmsec")) { - line_config->dialtone_timeoutmsec = atoi(v->value); + channel_config->dialtone_timeoutmsec = atoi(v->value); } else if (!strcasecmp(v->name, "offhook_nu_timeoutmsec")) { - line_config->offhook_nu_timeoutmsec = atoi(v->value); + channel_config->offhook_nu_timeoutmsec = atoi(v->value); } else if (!strcasecmp(v->name, "offhook_silence_timeoutmsec")) { - line_config->offhook_silence_timeoutmsec = atoi(v->value); + channel_config->offhook_silence_timeoutmsec = atoi(v->value); } else if (!strcasecmp(v->name, "hangup_xfer")) { - line_config->hangup_xfer = ast_true(v->value)?1:0; - } - else if (!strcasecmp(v->name, "callwaiting")) { - line_config->callwaiting = ast_true(v->value)?1:0; + channel_config->hangup_xfer = ast_true(v->value)?1:0; } else if (!strcasecmp(v->name, "do_not_disturb")) { - line_config->do_not_disturb = ast_true(v->value)?1:0; + channel_config->do_not_disturb = ast_true(v->value)?1:0; } else if (!strcasecmp(v->name, "calleridenable")) { - line_config->calleridenable = ast_true(v->value)?1:0; + channel_config->calleridenable = ast_true(v->value)?1:0; } else if (!strcasecmp(v->name, "calleridnameenable")) { - line_config->calleridnameenable = ast_true(v->value)?1:0; + channel_config->calleridnameenable = ast_true(v->value)?1:0; } else if (!strcasecmp(v->name, "anonymouscallenable")) { - line_config->anonymouscallenable = ast_true(v->value)?1:0; + channel_config->anonymouscallenable = ast_true(v->value)?1:0; } v = v->next; } @@ -3565,7 +3742,6 @@ static int load_common_settings(struct ast_config **cfg) /* Set default values */ hold_target_before_refer = 1; - max_sessions_per_line = 2; if ((*cfg = ast_config_load(config, config_flags)) == CONFIG_STATUS_FILEINVALID) { ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", config); @@ -3634,13 +3810,6 @@ static int load_common_settings(struct ast_config **cfg) tok = strtok(NULL, ","); } } - } else if (!strcasecmp(v->name, "maxsessionsperline")) { - if (ast_strlen_zero(v->value) || atoi(v->value) > 20 || atoi(v->value) < 1) { - ast_log(LOG_WARNING, "Incorrect session limit per line '%s', defaulting to '%d'\n", - v->value, max_sessions_per_line); - } else { - max_sessions_per_line = atoi(v->value); - } } v = v->next; @@ -3649,18 +3818,17 @@ static int load_common_settings(struct ast_config **cfg) return 0; } -// Load settings fore all lines -static void load_lines_settings(struct ast_config *cfg) +static void load_settings(struct ast_config *cfg) { struct ast_variable *v; - int i; - for (i = 0; i < num_endpoints; i++) { + // Load settings fore all channels / endpoints + for (int i = 0; i < num_endpoints; i++) { // Create and init a new settings struct - line_config[i] = line_settings_create(); + channel_config[i] = channel_settings_create(); // Load default settings v = ast_variable_browse(cfg, "default"); - line_settings_load(&line_config[i], v); + channel_settings_load(&channel_config[i], v); // Load per line specific settings char config_section[64]; snprintf(config_section, 64, "extension%d", i); @@ -3668,9 +3836,8 @@ static void load_lines_settings(struct ast_config *cfg) if (!v) { ast_log(LOG_WARNING, "Unable to load endpoint specific config (missing config section?): %s\n", config_section); } - line_settings_load(&line_config[i], v); + channel_settings_load(&channel_config[i], v); } - } enum { @@ -4212,7 +4379,7 @@ static int load_module(void) goto err; } - load_lines_settings(cfg); + load_settings(cfg); brcm_create_pvts(iflist, 0); brcm_assign_line_id(iflist); @@ -4261,7 +4428,7 @@ static int brcm_stop_callwaiting(const struct brcm_pvt *p) static int brcm_signal_ringing(struct brcm_pvt *p) { - if (line_config[p->line_id].ringsignal) { + if (channel_config[p->line_id].ringsignal) { endpt_signal(p->line_id, "ringing", "on", NULL); } return 0; @@ -4270,7 +4437,8 @@ static int brcm_signal_ringing(struct brcm_pvt *p) static int brcm_stop_ringing(struct brcm_pvt *p) { - if (line_config[p->line_id].ringsignal) { + ast_log(LOG_ERROR, "brcm_stop_ringing()\n"); + if (channel_config[p->line_id].ringsignal) { endpt_signal(p->line_id, "ringing", "off", NULL); } @@ -4280,7 +4448,7 @@ static int brcm_stop_ringing(struct brcm_pvt *p) /* Prepare endpoint for ringing. Caller ID signal pending. */ static int brcm_signal_ringing_callerid_pending(struct brcm_pvt *p) { - if (line_config[p->line_id].ringsignal) { + if (channel_config[p->line_id].ringsignal) { endpt_signal(p->line_id, "callid_ringing", "on", NULL); } @@ -4289,7 +4457,7 @@ static int brcm_signal_ringing_callerid_pending(struct brcm_pvt *p) static int brcm_stop_ringing_callerid_pending(struct brcm_pvt *p) { - if (line_config[p->line_id].ringsignal) { + if (channel_config[p->line_id].ringsignal) { endpt_signal(p->line_id, "callid_ringing", "off", NULL); } @@ -4304,7 +4472,7 @@ static int brcm_stop_ringing_callerid_pending(struct brcm_pvt *p) */ static int brcm_signal_callerid(struct ast_channel *chan, struct brcm_subchannel *sub) { - if (line_config[sub->parent->line_id].ringsignal) { + if (channel_config[sub->parent->line_id].ringsignal) { CLID_STRING clid_string; struct timeval utc_time; struct ast_tm local_time; @@ -4385,9 +4553,19 @@ static int brcm_create_connection(struct brcm_subchannel *sub) { sub->connection_init = 1; sub->jitter_count = 0; sub->farEndInterrivalJitter = 0; + + if (sub->owner) { + sub->call_id = ast_channel_callid(sub->owner); + } else { + sub->call_id = 0; + } + if(!brcm_in_onhold(sub->parent) && !brcm_in_call(sub->parent)) { // Is there another connection already? - ast_debug(1, "Creating real endpoint connection for pvt line_id=%i\n", sub->parent->line_id); - endpt_connection(sub->parent->line_id, sub->connection_id, "create"); + ast_debug(1, "Creating real endpoint connection for pvt line_id=%i, connection_id: %d, call_id: %d\n", sub->parent->line_id, sub->connection_id, sub->call_id); + endpt_connection(sub->parent->line_id, sub->call_id, "create"); + } else { + ast_debug(1, "Updating real endpoint connection for pvt line_id=%i, connection_id: %d, call_id: %d\n", sub->parent->line_id, sub->connection_id, sub->call_id); + endpt_connection(sub->parent->line_id, sub->call_id, "update"); } } @@ -4501,8 +4679,12 @@ static int brcm_close_connection(struct brcm_subchannel *sub) if (sub->connection_init) { if (!brcm_in_onhold(p) && !brcm_in_call(p) && !brcm_in_dialing(p) && !brcm_in_ringback(p) && !brcm_in_callwaiting(p) && !brcm_in_transferring(p)) { // Does the line have another call? - ast_debug(1, "Closing real endpoint connection %d\n", p->line_id); - endpt_connection(p->line_id, sub->connection_id, "destroy"); + ast_debug(1, "Closing real endpoint connection line_id: %d, connection_id=%d, call_id: %d\n", p->line_id, sub->connection_id, sub->call_id); + endpt_connection(p->line_id, sub->call_id, "destroy"); + } else { + ast_debug(1, "Releasing connection for pvt line_id=%i connection_id=%d, call_id: %d\n", + sub->parent->line_id, sub->connection_id, sub->call_id); + endpt_connection(p->line_id, sub->call_id, "release"); } sub->connection_init = 0; ast_debug(1, "Virtual Asterisk connection %d/%d destroyed\n", p->line_id, sub->connection_id); diff --git a/channels/chan_brcm.h b/channels/chan_brcm.h index 3b91eca753..79e3aea545 100644 --- a/channels/chan_brcm.h +++ b/channels/chan_brcm.h @@ -40,12 +40,12 @@ #define END 2 enum brcm_channel_state { - ONHOOK, - OFFHOOK, - DIALING, - CALLING, - INCALL, - ANSWER, + ONHOOK, + OFFHOOK, + DIALING, + CALLING, + INCALL, + ANSWER, CALLENDED, RINGING, CALLWAITING, @@ -79,6 +79,10 @@ enum LINE_EVENT { // Events from low level line (endpoint etc.) EVENT_EARLY_ONHOOK, EVENT_FLASH, EVENT_CALL_REJECT, + EVENT_MEDIA, + EVENT_SWITCH, + EVENT_JOIN, + EVENT_RELEASE, EVENT_LAST, }; @@ -88,6 +92,11 @@ enum endpoint_type { DECT, }; +enum CALL_DIRECTION { + INCOMING_CALL, + OUTGOING_CALL, +}; + typedef enum dialtone_state { DIALTONE_OFF = 0, DIALTONE_ON, @@ -101,9 +110,11 @@ typedef enum dialtone_state { struct brcm_subchannel { int id; - struct ast_channel *owner; /* Channel we belong to, possibly NULL */ + unsigned int call_id; /* The call_id of connection assigned by pjsip */ + struct ast_channel *owner; /* Channel we belong to, possibly NULL */ int connection_id; /* Current connection id, may be -1 */ enum brcm_channel_state channel_state; /* Channel states */ + enum CALL_DIRECTION call_direction; // Direction of call for the subchannel : 0 = incoming, 1 = outgoing unsigned int connection_init; /* State for endpoint id connection initialization */ struct ast_frame fr; /* Frame */ unsigned int sequence_number; /* Endpoint RTP sequence number state */ @@ -112,8 +123,8 @@ struct brcm_subchannel { unsigned int ssrc; /* Endpoint RTP synchronization source */ int codec; /* Used codec */ struct brcm_pvt *parent; /* brcm_line owning this subchannel */ - int cw_timer_id; /* Current call waiting timer id, -1 if no active timer */ - int cw_rejected; /* True if a previous waiting call has been rejected during current call */ + int cw_timer_id; /* Current call waiting timer id, -1 if no active timer */ + int cwBeep_timer_id; /* Current call waiting beep timer id, -1 if no active timer */ int r4_hangup_timer_id; /* Current R4 hangup timer id, -1 if no active timer */ 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 */ @@ -226,12 +237,11 @@ typedef struct { int dialtone_timeoutmsec; int offhook_nu_timeoutmsec; int offhook_silence_timeoutmsec; - int callwaiting; int do_not_disturb; int anonymouscallenable; int calleridenable; int calleridnameenable; -} line_settings; +} channel_settings; /* Caller ID */ @@ -257,6 +267,7 @@ static struct ast_jb_conf default_jbconf = #define DEFAULT_CALL_WAITING_TIMEOUT 20 // In seconds +#define DEFAULT_CALL_WAITING_BEEP_FREQ 5 // In seconds #define DEFAULT_R4_HANGUP_TIMEOUT 5000 // In milliseconds #define DEFAULT_ONHOLD_HANGUP_TIMEOUT 20 // In seconds diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 8f6cd78adc..a313bb7ffe 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -929,6 +929,8 @@ struct ast_sip_endpoint { unsigned int allow_unauthenticated_options; /*! Support mediasec on endpoint */ unsigned int mediasec; + /*! Call waiting enabled flag */ + unsigned int call_waiting_enabled; /*! Security mechanisms supported by peer */ AST_LIST_HEAD_NOLOCK(, security_mechanism) security_mechanisms; }; diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 0a2e79c67c..83ff05997f 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -1485,6 +1485,12 @@ When this option is enabled, the Mediasec Headers are added and enforced. </para></description> </configOption> + <configOption name="call_waiting_enabled"> + <synopsis>Enables call waiting feature for the endpoint </synopsis> + <description><para> + When this option is enabled, call waiting is enabled, otherwise it is disabled. + </para></description> + </configOption> <configOption name="stir_shaken" default="no"> <synopsis>Enable STIR/SHAKEN support on this endpoint</synopsis> <description><para> diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 16f0a89c49..833a79b62a 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -1932,6 +1932,55 @@ static int cli_endpoint_print_body(void *obj, void *arg, int flags) return 0; } +static char *cli_pjsip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); + struct ast_config *cfg = NULL; + struct ast_flags config_flags = { 0 }; + static const char config[] = "pjsip_endpoints.conf"; + + switch (cmd) { + case CLI_INIT: + e->command = "pjsip reload"; + e->usage = + "Usage: pjsip reload\n" + " Reload pjsip configuration.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if ((cfg = ast_config_load(config, config_flags)) == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", config); + return CLI_FAILURE; + } + if (!(cfg)) { + ast_log(LOG_ERROR, "Unable to load config %s\n", config); + return CLI_FAILURE; + } + + // reload endpoint's call_waiting_enabled value + char *cat = NULL; + struct ast_variable *v; + while ((cat = ast_category_browse(cfg, cat))) { + if (!(endpoint = ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), "endpoint", cat))) { + ast_log(LOG_ERROR, "Unable to retrieve endpoint %s\n", + cat); + return CLI_FAILURE; + } + v = ast_variable_browse(cfg, cat); + while (v) { + if (!strcasecmp(v->name, "call_waiting_enabled")) { + endpoint->call_waiting_enabled = strcasecmp(v->value, "yes") == 0 ? 1 : 0; + break; + } + v = v->next; + } + } + ast_verbose("PJSIP reload done\n"); + return CLI_SUCCESS; +} + static struct ast_cli_entry cli_commands[] = { AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Endpoints", .command = "pjsip list endpoints", @@ -1947,6 +1996,7 @@ static struct ast_cli_entry cli_commands[] = { .command = "pjsip show endpoint", .usage = "Usage: pjsip show endpoint <id>\n" " Show the configured PJSIP endpoint\n"), + AST_CLI_DEFINE(cli_pjsip_reload, "Reload PJSIP configuration"), }; struct ast_sip_cli_formatter_entry *channel_formatter; @@ -2157,6 +2207,7 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register(sip_sorcery, "endpoint", "stir_shaken", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, stir_shaken)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_unauthenticated_options", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allow_unauthenticated_options)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mediasec", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, mediasec)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "call_waiting_enabled", "false", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, call_waiting_enabled)); if (ast_sip_initialize_sorcery_transport()) { ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n"); diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index a9aaa1626f..802a351ba4 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -60,6 +60,12 @@ /* Most common case is one audio and one video stream */ #define DEFAULT_NUM_SESSION_MEDIA 2 +/* Max call count per line and Max call count generally*/ +#define DEFAULT_MAX_SESSION_PER_LINE 2 +#define DEFAULT_MAX_SESSION 4 +static int max_sessions_per_line = DEFAULT_MAX_SESSION_PER_LINE; +static int max_sessions = DEFAULT_MAX_SESSION; + /* Some forward declarations */ static void handle_session_begin(struct ast_sip_session *session); static void handle_session_end(struct ast_sip_session *session); @@ -3377,6 +3383,19 @@ struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint uri = contact->uri; } + RAII_VAR(struct ast_endpoint_snapshot *, endpoint_snapshot, ast_sip_get_endpoint_snapshot(endpoint), ao2_cleanup); + unsigned int num_channels = ast_active_channels(); + ast_assert(endpoint_snapshot != NULL); + if (endpoint_snapshot && endpoint_snapshot->num_channels >= max_sessions_per_line) { + ast_log(LOG_WARNING, "Max call per line limit exceeded [%d/%d]\n", endpoint_snapshot->num_channels, max_sessions_per_line); + SCOPE_EXIT_RTN_VALUE(NULL, "Max call per line limit exceeded\n"); + } + + if (num_channels >= (max_sessions * 2)) { // ast_active_channels() return 2 for each call, hence *2 here + ast_log(LOG_WARNING, "Max call limit exceeded [%d/%d]\n", num_channels, max_sessions*2); + SCOPE_EXIT_RTN_VALUE(NULL, "Max call limit exceeded\n"); + } + /* If we still have no URI to dial fail to create the session */ if (ast_strlen_zero(uri)) { ast_log(LOG_ERROR, "Endpoint '%s': No URI available. Is endpoint registered?\n", @@ -4104,6 +4123,20 @@ static void handle_new_invite_request(pjsip_rx_data *rdata) SCOPE_EXIT_RTN("Failure in pre session setup\n"); } + RAII_VAR(struct ast_endpoint_snapshot *, endpoint_snapshot, ast_sip_get_endpoint_snapshot(endpoint), ao2_cleanup); + unsigned int num_channels = ast_active_channels(); + ast_assert(endpoint_snapshot != NULL); + + if (endpoint_snapshot && endpoint_snapshot->num_channels >= max_sessions_per_line) { + ast_log(LOG_WARNING, "Max call per line limit exceeded [%d/%d]\n", endpoint_snapshot->num_channels, max_sessions_per_line); + SCOPE_EXIT_RTN_VALUE(NULL, "Max call per line limit exceeded\n"); + } + + if (num_channels >= (max_sessions * 2)) { // ast_active_channels() return 2 for each call, hence *2 here + ast_log(LOG_WARNING, "Max call limit exceeded [%d/%d]\n", num_channels, max_sessions*2); + SCOPE_EXIT_RTN_VALUE(NULL, "Max call limit exceeded\n"); + } + /* * Upon a successful pre_session_setup the associated dialog is returned locked * and with an added reference. Well actually two references. One added when the diff --git a/schemas/uci/asterisk.json b/schemas/uci/asterisk.json index ccfbfea191..fe5585fae0 100644 --- a/schemas/uci/asterisk.json +++ b/schemas/uci/asterisk.json @@ -404,13 +404,6 @@ "default": "1", "description": "Cancels echo arriving over voip channel if enabled" }, - { - "name": "maxsessionsperline", - "type": "integer", - "required": "yes", - "default": "2", - "description": "Describes maximum session in each line " - }, { "name": "fac", "type": "string", -- GitLab