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