Skip to content
Snippets Groups Projects
line.c 24.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include <stdio.h>
    #include <ctype.h>
    #include <unistd.h>
    #include <stdbool.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <libubus.h>
    
    #include <sched.h>         // sched_yield()
    
    #include <assert.h>
    #include <alloca.h>
    
    
    #include <libpicoevent.h>  // for err_exit()
    
    #include "libvoice.h"
    
    #include "line.h"
    #include "line-dect.h"
    #include "main.h"
    #include "ubus.h"
    
    enum {
    	STATE_OFF = 0,
    	STATE_ON,
    	STATE_LAST,
    };
    
    enum {
    	ACTION_CREATE,
    	ACTION_DESTROY,
    	ACTION_LAST,
    };
    
    
    #define VRG_COUNTRY_LAST  9999
    
    
    struct epsig {
    	const char *name;
    	EPSIG epsig;
    };
    
    
    static const struct epsig epsig_map[] = { // For generating a signal to the phone
    
    	{ .name = "dial", .epsig = EPSIG_DIAL },
    	{ .name = "ringback", .epsig = EPSIG_RINGBACK },
    	{ .name = "stutter", .epsig = EPSIG_STUTTER },
    	{ .name = "unobtainable", .epsig = EPSIG_RINGBACK_CUST1 },    // Maps onto ToneUserDefined1 in endpoint_api.h
    	{ .name = "callwt", .epsig = EPSIG_CALLWT },
    	{ .name = "busy", .epsig = EPSIG_BUSY },
    	{ .name = "ringing", .epsig = EPSIG_RINGING },
    	{ .name = "callid_ringing", .epsig = EPSIG_CALLID_RINGING },
    	{ .name = "callid", .epsig = EPSIG_CALLID },
    	{ .name = "congestion", .epsig = EPSIG_NETBUSY },
    	{ .name = "dial2", .epsig = EPSIG_DIAL2 },
    	{ .name = "dtmf0", .epsig = EPSIG_DTMF0 },
    	{ .name = "dtmf1", .epsig = EPSIG_DTMF1 },
    	{ .name = "dtmf2", .epsig = EPSIG_DTMF2 },
    	{ .name = "dtmf3", .epsig = EPSIG_DTMF3 },
    	{ .name = "dtmf4", .epsig = EPSIG_DTMF4 },
    	{ .name = "dtmf5", .epsig = EPSIG_DTMF5 },
    	{ .name = "dtmf6", .epsig = EPSIG_DTMF6 },
    	{ .name = "dtmf7", .epsig = EPSIG_DTMF7 },
    	{ .name = "dtmf8", .epsig = EPSIG_DTMF8 },
    	{ .name = "dtmf9", .epsig = EPSIG_DTMF9 },
    	{ .name = "dtmf#", .epsig = EPSIG_DTMFH },
    	{ .name = "dtmf*", .epsig = EPSIG_DTMFS },
    	{ .name = "dtmfA", .epsig = EPSIG_DTMFA },
    	{ .name = "dtmfB", .epsig = EPSIG_DTMFB },
    	{ .name = "dtmfC", .epsig = EPSIG_DTMFC },
    	{ .name = "dtmfD", .epsig = EPSIG_DTMFD },
    	{ .name = "keypad", .epsig = EPSIG_INGRESS_DTMF },
    	{ .name = "", .epsig = EPSIG_LAST },
    };
    
    
    static int send_dect_event_to_asterisk(int line, struct dect_event_t dectEvnt) {
    
    	struct line_event_t *msg;
    
    	msg = malloc(sizeof(struct line_event_t));
    	msg->name = dectEvnt.name;
    	msg->data = 0;
    	msg->line = line;
    
    	ubus_call_asterisk(msg);
    	ubus_broadcast_event(msg);
    
    	return 0;
    }
    
    //-------------------------------------------------------------
    // If a line has been disabled in run time we simulate
    // it as always being busy by going off-hook forever. This
    // is used by DECT when the HW is unpopulated.
    
    static int perhaps_simulate_busy(int line, struct voice_ubus_req_t *ubus_req) {
    
    	const struct voice_event_t *ev;
    	struct line_event_t *msg;
    
    	if(!lines[line].simulated_busy)
    		return 0;
    
    	// Inform the PBX only once per UBUS session or we get a loop.
    
    	assert(ubus_req && ubus_req->reqIn);
    	if(lines[line].simulated_busy_peer_id == ubus_req->reqIn->peer) {
    
    	lines[line].simulated_busy_peer_id = ubus_req->reqIn->peer;
    
    	for(ev = event_map; ev->event != VOICE_EVT_OFFHOOK; ev++);
    
    	msg = malloc(sizeof(struct line_event_t));
    	msg->name = ev->name;
    	msg->data = 0;
    	msg->line = line;
    	send_event_main(msg, EVENT_MAIN_LINE);
    
    	return 1;
    }
    
    
    //----------------------------------------------------------------------------------
    // Asterisk wants to make a FXS phone or DECT handset ring probably with a caller ID
    static EPSTATUS line_signal_ring(int line, int pcm, EPSIG epsig, const char *data, struct voice_ubus_req_t *ubus_req) {
    
    	const unsigned int CLID_MIN_LEN = 14;                   // Minimum string length to be valid.
    	const unsigned int CLID_TIME_DELIM = 8;                 // String index where time record ends.
    	const unsigned int CLID_NUMB_REC = CLID_TIME_DELIM + 2; // String index where number starts.
    
    	struct line_req_t *line_req = NULL;
    	int start_ring = data && strcmp(data, "0") != 0;
    
    	ENDPT_DBG("%s: line %d pcm: %d data: %s\n", __func__, line, pcm, data);
    	ENDPT_DBG("%s: pcm_state[%d]: %d, pcm_state[%d]: %d\n", __func__, PCM_0, lines[line].pcm_state[PCM_0],
    			PCM_1, lines[line].pcm_state[PCM_1]);
    
    	// Relay the request to dectmngr if the line is DECT
    
    	if(lines[line].type == VOICE_LINE_DECT) {
    
    		assert(ubus_req);
    
    		line_req = calloc(1, sizeof(struct line_req_t));
    		if(!line_req) {
    			ENDPT_DBG("%s: out of memory\n", __func__);
    			return EPSTATUS_ERROR;
    		}
    		line_req->line = line;
    		line_req->connection_id = -1;
    		line_req->pcm_id = pcm;
    		ENDPT_DBG("%s: line %d set pcm to %d\n", __func__, line, line_req->pcm_id);
    		memcpy(&line_req->ubus, ubus_req, sizeof(struct voice_ubus_req_t));
    
    		// Parse the called ID string generated by Asterisk
    		if(data && strlen(data) >= CLID_MIN_LEN && strlen(data) <= MAX_CALLER_ID_LEN &&
    			data[CLID_TIME_DELIM] == ',' && data[CLID_NUMB_REC + 1] != ',') {
    			char callIdTmp[strlen(data) + 1];
    
    
    			strcpy(callIdTmp, data);                               // Copy into a temp buffer.
    			*strchrnul(callIdTmp + CLID_NUMB_REC, ',') = 0;        // Find comma after digits and null it.
    
    			strcpy(line_req->caller_id, callIdTmp + CLID_NUMB_REC); // Extract the number.
    
    		}
    	}
    
    	// What type of ring signal?
    	switch(epsig) {
    		case EPSIG_CALLID:
    
    			if(line_req) {
    				line_req->action = start_ring ? ACTION_SIG_RING : ACTION_CONN_CLOSE;
    			} else {
    				return vrgEndptSignal(&lines[line].epHandle, -1, epsig, (unsigned int)data, -1, -1, -1);
    
    			}
    			break;
    
    		case EPSIG_RINGING:
    		case EPSIG_CALLID_RINGING:
    
    					if(epsig == EPSIG_CALLID_RINGING) {
    						// Dect ignore this enable signal
    
    					line_req->action = ACTION_SIG_RING;
    				} else {
    					line_req->action = ACTION_CONN_CLOSE;
    
    				line_req->caller_id[0] = 0; // Discard enable/disable char or it will become the caller ID
    			} else {
    				return vrgEndptSignal(&lines[line].epHandle, -1, epsig, start_ring, -1, -1, -1);
    
    	// If getting here the request is aimed for DECT
    
    	assert(lines[line].type == VOICE_LINE_DECT);
    
    	assert(line_req);
    	if(ubus_queue_req_to_dectmngr(line_req) || ubus_process_queued_reqs_to_dectmngr())
    
    	ubus_defer_request(line_req->ubus.ctx, line_req->ubus.reqIn, &line_req->ubus.reqOut);
    	line_req->ubus.reqIn = NULL;
    
    
    //-------------------------------------------------------------
    // Generate a signal to the phone, such as DTMF tones or ringing
    
    int line_signal(int line, const char *signame, const char *data, struct voice_ubus_req_t *ubus_req) {
    
    	const struct epsig *sig;
    	EPCMD_PARMS sigArg;
    	EPSTATUS status;
    	int i;
    
    
    	ENDPT_DBG("%s: line: %d, signame: %s, data: %s\n", __func__, line, signame, data);
    
    	for(sig = epsig_map; sig->epsig != EPSIG_LAST && strcmp(signame, sig->name) != 0; sig++)
    		continue;
    	if(sig->epsig == EPSIG_LAST) {
    		ENDPT_DBG("%s: signal %s is not supported\n", __func__, signame);
    
    		return -1;
    	}
    
    	// Is line disabled?
    
    	if(perhaps_simulate_busy(line, ubus_req))
    
    
    	switch(sig->epsig) {
    		case EPSIG_CALLID_RINGING:
    		case EPSIG_CALLID:
    		case EPSIG_RINGING:
    			if(atoi(data) == 0) {
    
    				status = line_signal_ring(line, lines[line].pcm_state[PCM_0] ==
    						LINE_PCM_STATE_RINGING ? PCM_0 : PCM_1, sig->epsig, data, ubus_req);
    
    				lines[line].signaled_call_waiting = 0;
    
    			} else {
    
    				status = line_signal_ring(line, lines[line].pcm_state[PCM_0] ==
    						LINE_PCM_STATE_NOT_USED ? PCM_0 : PCM_1, sig->epsig, data, ubus_req);
    
    		case EPSIG_INGRESS_DTMF: // Simulate phone keypad button presses
    			status = (data ? EPSTATUS_SUCCESS : EPSTATUS_SIGNAL_UNKNOWN);
    
    			/* Split user arg ASCII phone number string into (max 100)
    			 * individual libvoip DTMF generation events. */
    			for(i = 0; i < MAX_KEYPAD_DIGITS && data[i] && status == EPSTATUS_SUCCESS; i++) {
    				if(!isascii(data[i])) {
    					sigArg.arg[0] = HAPI_PTE_DTMFDDGTERROR;
    
    				} else if(isdigit(data[i])) {
    					sigArg.arg[0] = (int)(data[i] - '0') + HAPI_PTE_DTMFDDGT0;
    
    					if(sigArg.arg[0] > HAPI_PTE_DTMFDDGT9) {
    						sigArg.arg[0] = HAPI_PTE_DTMFDDGTERROR;
    					}
    
    				} else if(isxdigit(data[i])) {
    					sigArg.arg[0] = tolower((int) data[i]) - (int) 'a' + HAPI_PTE_DTMFDDGTA;
    					if(sigArg.arg[0] < HAPI_PTE_DTMFDDGTA || sigArg.arg[0] > HAPI_PTE_DTMFDDGTD) {
    
    						sigArg.arg[0] = HAPI_PTE_DTMFDDGTERROR;
    					}
    
    					sigArg.arg[0] = HAPI_PTE_DTMFDDGTSTR;
    
    					sigArg.arg[0] = HAPI_PTE_DTMFDDGTPND;
    
    				} else if(data[i] == 'R') {
    					sigArg.arg[0] = brcm_simulate_hook(line, VOICE_EVT_FLASH) ? HAPI_PTE_DTMFDDGTERROR : 0;
    				} else {
    
    					sigArg.arg[0] = HAPI_PTE_DTMFDDGTERROR;
    				}
    
    				if(sigArg.arg[0] == HAPI_PTE_DTMFDDGTERROR) {
    
    					ENDPT_DBG("Warning, invalid keypad char %d %c\n", (int)data[i], data[i]);
    
    					status = EPSTATUS_SIGNAL_UNKNOWN;
    					break;
    
    				} else if(sigArg.arg[0] || data[i] == '0') { // Accept digit 0 also when it calculates to integer 0.
    					status = vrgEndptConsoleCmd(&lines[line].epHandle, EPCMD_INDTMF_GENERATE, &sigArg);
    
    				}
    			}
    			break;
    
    		case EPSIG_CALLWT:
    
    			if (lines[line].type == VOICE_LINE_DECT) {
    
    				ENDPT_DBG("lines[%d].signaled_call_waiting: %d\n", line, lines[line].signaled_call_waiting);
    				if((data[0] == '1') && (lines[line].signaled_call_waiting == 0)) {
    
    					// start call waiting
    
    					status = line_signal_ring(line, lines[line].pcm_state[PCM_1] ==
    							LINE_PCM_STATE_NOT_USED ? PCM_1 : PCM_0, EPSIG_RINGING, data, ubus_req);
    
    					lines[line].signaled_call_waiting = 1;
    
    				} else if((data[0] == '0')) {
    					// stop call waiting
    
    					status = line_signal_ring(line, lines[line].pcm_state[PCM_1] ==
    							LINE_PCM_STATE_RINGING ? PCM_1 : PCM_0, EPSIG_RINGING, data, ubus_req);
    
    					lines[line].signaled_call_waiting = 0;
    
    				} else {
    					return 0;
    				}
    			} else {
    
    				status = vrgEndptSignal(&lines[line].epHandle, -1, sig->epsig, atoi(data), -1, -1, -1);
    
    		case EPSIG_RINGBACK_CUST1:
    		case EPSIG_BUSY:
    
    			if (lines[line].type == VOICE_LINE_DECT) {
    
    				send_dect_event_to_asterisk(line, dect_event_map[DECT_EVT_RELEASE]);
    
    				status = EPSTATUS_SUCCESS;
    			} else {
    
    				status = vrgEndptSignal(&lines[line].epHandle, -1, sig->epsig, atoi(data), -1, -1, -1);
    
    		case EPSIG_NETBUSY:
    
    			if (lines[line].type == VOICE_LINE_DECT) {
    
    				send_dect_event_to_asterisk(line, dect_event_map[DECT_EVT_RELEASE]);
    
    			}
    			status = EPSTATUS_SUCCESS;
    			break;
    
    		default:
    
    			status = vrgEndptSignal(&lines[line].epHandle, -1, sig->epsig, atoi(data), -1, -1, -1);
    
    			break;
    	}
    
    	if (status == EPSTATUS_SUCCESS) {
    
    		ENDPT_DBG("%s: signal %s(%s) was set for line %d\n", __func__, sig->name, data, line);
    	} else {
    		ENDPT_DBG("%s: error setting signal %s(%s)\n", __func__, sig->name, data);
    
    		return -1;
    	}
    
    	return 0;
    }
    
    //-------------------------------------------------------------
    // Reception of a create connection request from Asterisk. If
    
    // line type is Dect we need to relay the requets to the dectmngr.
    
    int line_new_connection_by_asterisk(int line, int connection, struct voice_ubus_req_t *ubus_req) {
    
    	VRG_ENDPT *epIntern;
    	int res;
    
    
    	if(line < 0 || line >= terminal_info.num_terminals) return -1;
    
    	epIntern = GetEndptState(line);
    	if(!epIntern) return -1;
    
    	ENDPT_DBG("%s() line: %d, connection: %d\n", __func__, line, connection);
    
    	ENDPT_DBG("%s lines[%d].pcm_state[%d]: %d, lines[%d].pcm_state[%d]: %d\n", __func__, line, PCM_0, lines[line].pcm_state[PCM_0],
    			line, PCM_1, lines[line].pcm_state[PCM_1]);
    
    	if (lines[line].pcm_state[PCM_0] == LINE_PCM_STATE_RINGING) {
    
    		lines[line].pcm_state[PCM_0] = connection;
    
    		ENDPT_DBG("%s changing value of lines[%d].pcm_state[PCM_0] to %d\n", __func__, line, lines[line].pcm_state[PCM_0]);
    	}
    	else if (lines[line].pcm_state[PCM_1] == LINE_PCM_STATE_RINGING) {
    
    		lines[line].pcm_state[PCM_1] = connection;
    
    		ENDPT_DBG("%s changing value of lines[%d].pcm_state[PCM_1] to %d\n", __func__, line, lines[line].pcm_state[PCM_1]);
    	}
    	res = 0;
    
    
    	if(lines[line].type == VOICE_LINE_DECT) {
    
    		if(voice_line_is_offhook(line) || epIntern->cnxCnt > 0) {
    
    			/* dectmngr alreay has audio streaming, now
    
    			 * libvoip and Asterisk should do the same. */
    			res = voice_connection_create(line, line);
    		}
    		else {
    
    			/* Relay request to dectmngr by putting the request
    
    			 * in a queue, where it will wait until we get a
    
    			 * chanse to relay it to dectmngr. */
    
    			line_req = calloc(1, sizeof(struct line_req_t));
    			if(!line_req) return -1;
    			line_req->line = line;
    			line_req->connection_id = line;
    			line_req->pcm_id = lines[line].pcm_state[PCM_0] == connection ? PCM_0 : PCM_1;
    			line_req->action = ACTION_CONN_CREATE;
    			memcpy(&line_req->ubus, ubus_req, sizeof(struct voice_ubus_req_t));
    			if(ubus_queue_req_to_dectmngr(line_req) || ubus_process_queued_reqs_to_dectmngr()) return -1;
    			ubus_defer_request(line_req->ubus.ctx, line_req->ubus.reqIn,
    				&line_req->ubus.reqOut);
    			line_req->ubus.reqIn = NULL; // Deny further use of input request
    
    		}
    	}
    	else {
    		res = voice_connection_create(line, line);
    	}
    
    	return res;
    }
    
    //-------------------------------------------------------------
    // Reception of a close connection request from Asterisk. If
    
    // line type is Dect we need to relay the requets to the dectmngr.
    
    int line_close_connection_by_asterisk(int line, int connection, struct voice_ubus_req_t *ubus_req) {
    
    	ENDPT_DBG("%s() line: %d, connection: %d\n", __func__, line, connection);
    
    	ENDPT_DBG("%s lines[%d].pcm_state[%d]: %d, lines[%d].pcm_state[%d]: %d\n", __func__, line, PCM_0, lines[line].pcm_state[PCM_0],
    			line, PCM_1, lines[line].pcm_state[PCM_1]);
    
    	if (lines[line].pcm_state[PCM_0] == LINE_PCM_STATE_NOT_USED && lines[line].pcm_state[PCM_1] == LINE_PCM_STATE_NOT_USED) {
    		return voice_connection_close(line, line);
    	}
    
    
    	if(line < 0 || line >= terminal_info.num_terminals) return -1;
    
    	switch(lines[line].type) {
    		case VOICE_LINE_DECT:
    
    			/* Put the request in a queue, where it will wait
    
    			 * until we get a chanse to relay it to dectmngr. */
    
    			line_req = calloc(1, sizeof(struct line_req_t));
    			if(!line_req) return -1;
    			line_req->line = line;
    			line_req->connection_id = line;
    
    			if (lines[line].pcm_state[PCM_0] >= LINE_PCM_STATE_CONNECTED && lines[line].pcm_state[PCM_1] == LINE_PCM_STATE_NOT_USED) {
    
    				lines[line].pcm_state[PCM_0] = LINE_PCM_STATE_NOT_USED;
    				ENDPT_DBG("%s changing value of lines[%d].pcm_state[PCM_0] to %d\n", __func__, line, lines[line].pcm_state[PCM_0]);
    			} else if (lines[line].pcm_state[PCM_1] >= LINE_PCM_STATE_CONNECTED && lines[line].pcm_state[PCM_0] == LINE_PCM_STATE_NOT_USED) {
    
    				lines[line].pcm_state[PCM_1] = LINE_PCM_STATE_NOT_USED;
    				ENDPT_DBG("%s changing value of lines[%d].pcm_state[PCM_1] to %d\n", __func__, line, lines[line].pcm_state[PCM_1]);
    			}
    
    			line_req->action = ACTION_CONN_CLOSE;
    			memcpy(&line_req->ubus, ubus_req, sizeof(struct voice_ubus_req_t));
    			if(ubus_queue_req_to_dectmngr(line_req) || ubus_process_queued_reqs_to_dectmngr())
    
    				return -1;
    
    			ubus_defer_request(line_req->ubus.ctx, line_req->ubus.reqIn, &line_req->ubus.reqOut);
    			line_req->ubus.reqIn = NULL;	// Deny further use of input request
    
    			break;
    
    		default:
    			if (lines[line].pcm_state[PCM_0] >= LINE_PCM_STATE_CONNECTED) {
    				lines[line].pcm_state[PCM_0] = LINE_PCM_STATE_NOT_USED;
    				ENDPT_DBG("%s changing value of lines[%d].pcm_state[PCM_0] to %d\n", line, lines[line].pcm_state[PCM_0]);
    			} else if (lines[line].pcm_state[PCM_1] >= LINE_PCM_STATE_CONNECTED) {
    				lines[line].pcm_state[PCM_1] = LINE_PCM_STATE_NOT_USED;
    				ENDPT_DBG("%s changing value of lines[%d].pcm_state[PCM_1] to %d\n", line, lines[line].pcm_state[PCM_1]);
    			}
    			return voice_connection_close(line, line);
    	}
    
    	return 0;
    }
    
    
    int line_release_connection_by_asterisk(int line, int connection, struct voice_ubus_req_t *ubus_req) {
    
    	if(line < 0 || line >= terminal_info.num_terminals) return -1;
    
    	ENDPT_DBG("%s() line: %d connection: %d\n", __func__, line, connection);
    
    	ENDPT_DBG("%s lines[%d].pcm_state[%d]: %d, lines[%d].pcm_state[%d]: %d\n", __func__, line, PCM_0, lines[line].pcm_state[PCM_0],
    					line, PCM_1, lines[line].pcm_state[PCM_1]);
    
    	// during start of call conference asterisk removes 1 of connection, we skip
    
    	// it because voicemngr<->dectmngr needs to keep it active
    
    	if (lines[line].conference_started == 1) {
    		lines[line].conference_started = 0;
    
    		return 0;
    	}
    
    	if (lines[line].pcm_state[PCM_0] < LINE_PCM_STATE_CONNECTED && lines[line].pcm_state[PCM_1] < LINE_PCM_STATE_CONNECTED) {
    		return 0;
    	}
    
    
    	if (lines[line].pcm_state[PCM_0] != connection && lines[line].pcm_state[PCM_1] != connection) {
    
    	switch(lines[line].type) {
    		case VOICE_LINE_DECT:
    
    			/* Put the request in a queue, where it will wait
    
    			 * until we get a chanse to relay it to dectmngr. */
    
    			line_req = calloc(1, sizeof(struct line_req_t));
    			if(!line_req) return -1;
    			line_req->line = line;
    			line_req->connection_id = line;
    
    			if (lines[line].pcm_state[PCM_0] == connection) {
    
    				lines[line].pcm_state[PCM_0] = LINE_PCM_STATE_NOT_USED;
    				ENDPT_DBG("%s changing value of lines[%d].pcm_state[PCM_0] to %d\n", __func__, line, lines[line].pcm_state[PCM_0]);
    
    			} else if (lines[line].pcm_state[PCM_1] == connection) {
    
    				lines[line].pcm_state[PCM_1] = LINE_PCM_STATE_NOT_USED;
    				ENDPT_DBG("%s changing value of lines[%d].pcm_state[PCM_1] to %d\n", __func__, line, lines[line].pcm_state[PCM_1]);
    			}
    
    			line_req->action = ACTION_CONN_CLOSE;
    			memcpy(&line_req->ubus, ubus_req, sizeof(struct voice_ubus_req_t));
    			if(ubus_queue_req_to_dectmngr(line_req) || ubus_process_queued_reqs_to_dectmngr())
    
    				return -1;
    
    			ubus_defer_request(line_req->ubus.ctx, line_req->ubus.reqIn, &line_req->ubus.reqOut);
    			line_req->ubus.reqIn = NULL; // Deny further use of input request
    
    			break;
    
    		default:
    
    			if (lines[line].pcm_state[PCM_0] == connection) {
    
    				lines[line].pcm_state[PCM_0] = LINE_PCM_STATE_NOT_USED;
    				ENDPT_DBG("%s changing value of lines[%d].pcm_state[PCM_0] to %d\n", __func__, line, lines[line].pcm_state[PCM_0]);
    
    			} else if (lines[line].pcm_state[PCM_1] == connection) {
    
    				lines[line].pcm_state[PCM_1] = LINE_PCM_STATE_NOT_USED;
    				ENDPT_DBG("%s changing value of lines[%d].pcm_state[PCM_1] to %d\n", __func__, line, lines[line].pcm_state[PCM_1]);
    			}
    			return voice_connection_close(line, line);
    	}
    
    	return 0;
    }
    
    
    int line_update_connection_by_pbx(int line, int pcm_state) {
    
    
    	ENDPT_DBG("%s Received update connection for line: %d with pcm_state: %d\n",
    		__func__, line, pcm_state);
    	ENDPT_DBG("%s lines[%d].pcm_state[%d]: %d, lines[%d].pcm_state[%d]: %d\n", __func__, line, PCM_0, lines[line].pcm_state[PCM_0],
    		line, PCM_1, lines[line].pcm_state[PCM_1]);
    
    
    	if(lines[line].type != VOICE_LINE_DECT)
    		return 0;
    
    
    	if (lines[line].pcm_state[PCM_0] == LINE_PCM_STATE_RINGING) {
    		lines[line].pcm_state[PCM_0] = pcm_state;
    		ENDPT_DBG("%s changing value of lines[%d].pcm_state[PCM_0] to %d\n", __func__, line, lines[line].pcm_state[PCM_0]);
    	} else if(lines[line].pcm_state[PCM_1] == LINE_PCM_STATE_RINGING) {
    		lines[line].pcm_state[PCM_1] = pcm_state;
    		ENDPT_DBG("%s changing value of lines[%d].pcm_state[PCM_1] to %d\n", __func__, line, lines[line].pcm_state[PCM_1]);
    	}
    
    	return 0;
    }
    
    //-------------------------------------------------------------
    
    // Reception of a create connection request from dectmngr.
    // Generate a off-hook event and queue the request
    // until Asterisk acknowledge the offh-ook event.
    
    int line_new_connection_by_dect(int line, const char *cid, int pcm, struct voice_ubus_req_t *ubus_req) {
    
    	VRG_ENDPT *epIntern;
    
    
    	if(line < 0 || line >= terminal_info.num_terminals)
    		return -1;
    	if(lines[line].type != VOICE_LINE_DECT)
    		return -1;
    
    
    	ENDPT_DBG("%s() for line %d pcm %d\n", __func__, line, pcm);
    
    
    	epIntern = GetEndptState(line);
    
    	if(!epIntern)
    		return -1;
    
    	ENDPT_DBG("%s lines[%d].pcm_state[%d]: %d, lines[%d].pcm_state[%d]: %d\n", __func__, line, PCM_0,
    		lines[line].pcm_state[PCM_0], line, PCM_1, lines[line].pcm_state[PCM_1]);
    
    	if(pcm <= PCM_1) {
    		lines[line].pcm_state[pcm] = LINE_PCM_STATE_RINGING;
    
    		lines[line].signaled_call_waiting = 0;
    
    		ENDPT_DBG("%s changing value of lines[%d].pcm_state[%d] to %d\n", __func__, line, pcm, lines[line].pcm_state[pcm]);
    
    	if(!voice_line_is_offhook(line) && brcm_simulate_hook(line, VOICE_EVT_OFFHOOK))
    
    	if(voice_line_is_offhook(line)) {
    
    		switch(pcm) {
    
    			case CALL_DEFAULT0:
    			case CALL_DEFAULT1:
    
    				if (brcm_simulate_hook(line, VOICE_EVT_FLASH))
    					return -1;
    
    				break;
    			case CALL_DIGIT_PRESSED:
    				break;
    			case CALL_TOGGLE:
    
    				if (send_dect_event_to_asterisk(line, dect_event_map[DECT_EVT_SWITCH]))
    
    				break;
    			case CALL_CONFERENCE:
    
    				if (send_dect_event_to_asterisk(line, dect_event_map[DECT_EVT_JOIN]))
    
    				lines[line].conference_started = 1;
    
    				break;
    			default:
    				break;
    		}
    	}
    
    	if (simulate_digits_pressing(line, cid) == -1)
    
    		return -1;
    
    	/* Store the ubus request in a queue. It will be picked up
    	 * later, via the Asterisk ubus answer handler. */
    
    	line_req = calloc(1, sizeof(struct line_req_t));
    	if(!line_req) return -1;
    	line_req->line = line;
    	line_req->connection_id = -1;
    	line_req->action = ACTION_CONN_CREATE;
    	memcpy(&line_req->ubus, ubus_req, sizeof(struct voice_ubus_req_t));
    	if(ubus_queue_req_to_asterisk(line_req)) {
    		free(line_req);
    
    		return -1;
    	}
    
    	ubus_defer_request(line_req->ubus.ctx, line_req->ubus.reqIn, &line_req->ubus.reqOut);
    	line_req->ubus.reqIn = NULL;	// Deny further use of input request
    
    
    	return 0;
    }
    
    //-------------------------------------------------------------
    
    // Reception of a close connection request from dectmngr.
    
    // Generate a libvoip onhook event and queue the request
    // until Asterisk acknowledge the onhook event.
    
    int line_close_connection_by_dect(int line, int pcm, struct voice_ubus_req_t *ubus_req) {
    
    	VRG_ENDPT *epIntern;
    
    
    	if(line < 0 || line >= terminal_info.num_terminals) {
    
    		ENDPT_DBG("%s: wrong parameter\n", __func__);
    		return -1;
    	}
    
    
    	if(lines[line].type != VOICE_LINE_DECT) {
    		ENDPT_DBG("%s: lines[%d].type != VOICE_LINE_DECT\n", __func__, line);
    
    		return -1;
    	}
    
    	ENDPT_DBG("%s() for line %d, pcm: %d\n", __func__, line, pcm);
    
    	ENDPT_DBG("%s lines[%d].pcm_state[%d]: %d, lines[%d].pcm_state[%d]: %d\n", __func__, line, PCM_0, lines[line].pcm_state[PCM_0],
    		line, PCM_1, lines[line].pcm_state[PCM_1]);
    
    	epIntern = GetEndptState(line);
    	if(!epIntern) {
    		ENDPT_DBG("%s: GetEndptState() failed\n", __func__);
    		return -1;
    	}
    
    
    	if (pcm == CALL_DECT_UNAVAILABLE) {
    
    		struct line_event_t *msg;
    
    		ENDPT_DBG("%s: There is no available DECT handsets for the call. "
    			"Send DECT_UNAVAILABLE event to Asterisk\n", __func__);
    
    		msg = malloc(sizeof(struct line_event_t));
    		if (msg) {
    			msg->name = "DECT_UNAVAILABLE";
    			msg->data = 0;
    			msg->line = line;
    			send_event_main(msg, EVENT_MAIN_LINE);
    		}
    
    
    		return send_reply_dectmngr(ubus_req, line + 1, // +1 Dectmngr lines starting from 1
    			pcm, UBUS_STATUS_OK);
    
    	 if (lines[line].pcm_state[pcm] == LINE_PCM_STATE_RINGING) {
    
    		struct line_event_t *msg;
    
    		ENDPT_DBG("%s: DECT handsets may have not answered the call yet. "
    			"Send CALL_REJECT event to Asterisk\n", __func__);
    
    		msg = malloc(sizeof(struct line_event_t));
    		if (msg) {
    			msg->name = "CALL_REJECT";
    			msg->data = 0;
    			msg->line = line;
    			send_event_main(msg, EVENT_MAIN_LINE);
    		}
    
    
    		return send_reply_dectmngr(ubus_req, line + 1, // +1 Dectmngr lines starting from 1
    			pcm, UBUS_STATUS_OK);
    
    	}
    
    	if(lines[line].pcm_state[PCM_0] <= LINE_PCM_STATE_RINGING || lines[line].pcm_state[PCM_1] <= LINE_PCM_STATE_RINGING) {
    
    		brcm_simulate_hook(line, VOICE_EVT_ONHOOK);
    
    	lines[line].pcm_state[pcm] = LINE_PCM_STATE_NOT_USED;
    	ENDPT_DBG("%s changing value of lines[%d].pcm_state[%d] to %d\n", __func__, line, pcm, lines[line].pcm_state[pcm]);
    	if(send_dect_event_to_asterisk(line, dect_event_map[DECT_EVT_RELEASE]))
    		return -1;
    
    
    	/* Store the request in a queue. It will be picked up
    	 * later, via the Asterisk ubus answer handler. */
    
    	line_req = calloc(1, sizeof(struct line_req_t));
    	if(!line_req) return -1;
    	line_req->line = line;
    	line_req->connection_id = -1;
    	line_req->action = ACTION_CONN_CLOSE;
    	memcpy(&line_req->ubus, ubus_req, sizeof(struct voice_ubus_req_t));
    	if(ubus_queue_req_to_asterisk(line_req)) {
    		free(line_req);
    
    		return -1;
    	}
    
    	ubus_defer_request(line_req->ubus.ctx, line_req->ubus.reqIn, &line_req->ubus.reqOut);
    	line_req->ubus.reqIn = NULL;