Skip to content
Snippets Groups Projects
line.c 23.9 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"
    
    
    static char *pcmState2Str(callid_state state)
    {
    	if (state <= CALLID_INVALID)
    		return "Invalid";
    	else if (state == CALLID_OBTAINING)
    		return "Obtaining";
    	else
    		return "Established";
    }
    
    void pcm_states_dump(const char *func, int line)
    {
    
    	ENDPT_DBG("%s: line=%d, pcm_callid[%d]=\'%s\', pcm_callid[%d]=\'%s\'\n", func, line,
    		PCM_0, pcmState2Str(lines[line].pcm_callid[PCM_0]),
    
    		PCM_1, pcmState2Str(lines[line].pcm_callid[PCM_1]));
    }
    
    // Get call_id state (-1, 0, 1)
    callid_state get_callid_state(int call_id) {
    	if (call_id <= CALLID_INVALID)
    		return CALLID_INVALID;
    	else if (call_id == CALLID_OBTAINING)
    		return CALLID_OBTAINING;
    	else
    		return CALLID_ESTABLISHED;
    }
    
    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 Asterisk 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++)
    		continue;
    
    	msg = malloc(sizeof(struct line_event_t));
    
    	if (msg) {
    		msg->name = ev->name;
    		msg->data = 0;
    		msg->line = line;
    		send_event_main(msg, EVENT_MAIN_LINE);
    	} else {
    
    		ENDPT_ERR("%s: out of memory\n", __func__);
    
    //----------------------------------------------------------------------------------
    // DECT handset tone trigger
    static int dect_tone_play(int line, int pcm, enum VOICE_SIGNAL signal, const char *data, struct voice_ubus_req_t *ubus_req)
    {
    	struct line_req_t *line_req = NULL;
    
    	ENDPT_DBG("line=%d, pcm=%d, data=%s\n", line, pcm, data);
    
    	pcm_states_dump(__func__, line);
    
    	assert(ubus_req);
    	line_req = calloc(1, sizeof(struct line_req_t));
    	if(!line_req) {
    
    		ENDPT_ERR("%s: out of memory\n", __func__);
    
    		return -1;
    	}
    	line_req->line = line;
    	line_req->connection_id = -1;
    	line_req->pcm_id = pcm;
    	switch(signal) {
    		case VOICE_SIG_UNOBTAINABLE:
    		case VOICE_SIG_BUSY:
    		case VOICE_SIG_NETBUSY:
    			line_req->action = ACTION_SIG_BUSY_TONE; //only busy tone available
    			break;
    		default:
    			free(line_req);
    			return -1;
    	}
    
    
    	ENDPT_DBG("line %d set pcm to %d\n", line, line_req->pcm_id);
    
    	memcpy(&line_req->ubus, ubus_req, sizeof(struct voice_ubus_req_t));
    
    	assert(lines[line].type == VOICE_LINE_DECT);
    	assert(line_req);
    	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;
    
    	return 0;
    }
    
    static void setCallerName(const char *data, char *callerName)
    {
    	char *start;
    
    	if ( (start = strchr(data,'\"')) )
    	{
    		char *end;
    
    		start += 1;
    		if ( (end = strchr(start,'\"')) )
    		{
    			int len = ((end - start) < MAX_CALLER_NAME ? (end - start) : MAX_CALLER_NAME);
    			memcpy(callerName, start, len);
    			callerName[len] = '\0';
    		}
    	}
    }
    
    
    //----------------------------------------------------------------------------------
    // Asterisk wants to make a FXS phone or DECT handset ring probably with a caller ID
    
    static int line_signal_ring(int line, int pcm, enum VOICE_SIGNAL signal, const char *data, struct voice_ubus_req_t *ubus_req)
    {
    
    	struct line_req_t *line_req = NULL;
    	int start_ring = data && strcmp(data, "0") != 0;
    
    	ENDPT_DBG("line=%d, pcm=%d, data=%s\n", line, pcm, data);
    
    	pcm_states_dump(__func__, line);
    
    
    	// 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_ERR("%s: out of memory\n", __func__);
    
    		}
    		line_req->line = line;
    		line_req->connection_id = -1;
    		line_req->pcm_id = pcm;
    
    		ENDPT_DBG("line %d set pcm to %d\n", 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) >= MIN_CALLER_ID_LEN && strlen(data) <= MAX_CALLER_ID_LEN &&
    
    			data[CLID_TIME_DELIM] == ',' && data[CLID_NUMB_REC + 1] != ',') {
    
    			char cid_tmp[strlen(data) + 1], *end;
    
    			strcpy(cid_tmp, data);
    			if ((end = strchr(cid_tmp + CLID_NUMB_REC, ',')) != NULL)
    				*end = '\0'; // Find comma after digits and null it
    			strcpy(line_req->caller_id, cid_tmp + CLID_NUMB_REC); // Extract the number
    
    			setCallerName(data, line_req->caller_name);
    
    				line_req->action = start_ring ? ACTION_SIG_RING : ACTION_RINGING_STOP;
    
    				return voice_line_signal(line, -1, signal, start_ring, (void *)data);
    
    		case VOICE_SIG_RINGING:
    		case VOICE_SIG_CALLID_RINGING:
    
    						// Dect ignore this enable signal
    
    					line_req->action = ACTION_RINGING_STOP;
    
    				line_req->caller_id[0] = 0; // Discard enable/disable char or it will become the caller ID
    			} else {
    
    				return voice_line_signal(line, -1, signal, start_ring, NULL);
    
    		case VOICE_SIG_CALL_WAITING: // only used for stopping call waiting indication
    			if(line_req) {
    				if(!start_ring) {
    
    					line_req->action = ACTION_RINGING_STOP;
    
    				}
    				line_req->caller_id[0] = 0; // Discard enable/disable char or it will become the caller ID
    				break;
    			} else {
    				return -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;
    
    //-------------------------------------------------------------
    // Reception of a answer connection request from Asterisk. If
    // line type is Dect we need to relay the requets to the Dectmngr.
    
    static int line_signal_answer(int line, int pcm, const char *data, struct voice_ubus_req_t *ubus_req)
    {
    
    	struct line_req_t *line_req = NULL;
    
    
    	ENDPT_DBG("line %d pcm: %d data: %s\n", line, pcm, data);
    
    	pcm_states_dump(__func__, line);
    
    	assert(ubus_req);
    	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->pcm_id = pcm;
    	line_req->action = ACTION_SIG_ANSWERED;
    	memcpy(&line_req->ubus, ubus_req, sizeof(struct voice_ubus_req_t));
    
    	assert(line_req);
    
    	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;
    
    	return 0;
    }
    
    
    //-------------------------------------------------------------
    // 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)
    {
    
    	ENDPT_DBG("line=%d(type:%d), signame=%s, data=%s\n", line, lines[line].type, signame, data);
    
    	for(sig = signal_map; sig->signal != VOICE_SIG_LAST && strcmp(signame, sig->name) != 0; sig++)
    
    		ENDPT_ERR("%s: signal %s is not supported\n", __func__, signame);
    
    		return -1;
    	}
    
    	// Is line disabled?
    
    	if(perhaps_simulate_busy(line, ubus_req))
    
    	switch(sig->signal) {
    		case VOICE_SIG_CALLID_RINGING:
    		case VOICE_SIG_CALLID:
    		case VOICE_SIG_RINGING:
    
    			if(atoi(data) == 0) {
    
    				res = line_signal_ring(line, get_callid_state(lines[line].pcm_callid[PCM_0]) == CALLID_OBTAINING ?
    						PCM_0 : PCM_1, sig->signal, data, ubus_req);
    
    				lines[line].signaled_call_waiting = 0;
    
    			} else {
    
    				res = line_signal_ring(line, get_callid_state(lines[line].pcm_callid[PCM_0]) == CALLID_INVALID ?
    						PCM_0 : PCM_1, sig->signal, data, ubus_req);
    
    		case VOICE_SIG_INGRESS_DTMF: // Simulate phone keypad button presses
    
    			res = voice_line_signal(line, -1, sig->signal, 1, (void *)data);
    
    			if (lines[line].type == VOICE_LINE_DECT) {
    
    				ENDPT_DBG("lines[%d].signaled_call_waiting: %d\n", line, lines[line].signaled_call_waiting);
    
    				if(data && strlen(data) >= MIN_CALLER_ID_LEN &&
    					strlen(data) <= MAX_CALLER_ID_LEN &&
    					data[CLID_TIME_DELIM] == ',' &&
    					data[CLID_NUMB_REC + 1] != ',' &&
    					lines[line].signaled_call_waiting == 0) {
    					pcm_states_dump(__func__, line);
    
    					// start call waiting
    
    					res = line_signal_ring(line, get_callid_state(lines[line].pcm_callid[PCM_1]) == CALLID_INVALID ?
    
    							PCM_1 : PCM_0, VOICE_SIG_CALLID, data, ubus_req);
    
    					lines[line].signaled_call_waiting = 1;
    
    				} else if(data && (data[0] == '0')) {
    
    					// stop call waiting when accepted but not close the connection on the accepted line
    
    					res = line_signal_ring(line, get_callid_state(lines[line].pcm_callid[PCM_1]) == CALLID_OBTAINING ?
    
    							PCM_1 : PCM_0, VOICE_SIG_CALL_WAITING, data, ubus_req);
    
    					lines[line].signaled_call_waiting = 0;
    
    				}
    			} else {
    
    				res = voice_line_signal(line, -1, sig->signal, atoi(data), NULL);
    
    			if (lines[line].type == VOICE_LINE_DECT) {
    
    				pcm_states_dump(__func__, line);
    
    				send_dect_event_to_asterisk(line, dect_event_map[DECT_EVT_RELEASE]);
    
    			} else {
    
    				res = voice_line_signal(line, -1, sig->signal, atoi(data), NULL);
    
    		case VOICE_SIG_BUSY:
    
    			if (lines[line].type == VOICE_LINE_DECT) {
    
    				pcm_states_dump(__func__, line);
    				if (get_callid_state(lines[line].pcm_callid[PCM_0])==CALLID_ESTABLISHED && get_callid_state(lines[line].pcm_callid[PCM_1])==CALLID_ESTABLISHED){
    					send_dect_event_to_asterisk(line, dect_event_map[DECT_EVT_RELEASE]);
    				} else if (get_callid_state(lines[line].pcm_callid[PCM_0]) == CALLID_ESTABLISHED || get_callid_state(lines[line].pcm_callid[PCM_1]) == CALLID_ESTABLISHED){
    				// play tone on DECT
    					res = dect_tone_play(line, get_callid_state(lines[line].pcm_callid[PCM_0]) == CALLID_ESTABLISHED ? PCM_0 : PCM_1, sig->signal, data, ubus_req);
    				}
    
    				res = voice_line_signal(line, -1, sig->signal, atoi(data), NULL);
    
    		case VOICE_SIG_ANSWER:
    			if ((lines[line].type == VOICE_LINE_DECT) && (data[0] == '1')) {
    				res = line_signal_answer(line, get_callid_state(lines[line].pcm_callid[PCM_1]) >= CALLID_OBTAINING ?
    						PCM_1 : PCM_0, data, ubus_req);
    			} else {
    				res = 0;
    			}
    			break;
    
    
    Yalu Zhang's avatar
    Yalu Zhang committed
    		case VOICE_SIG_K_BREAK:
    			if (lines[line].type == VOICE_LINE_FXS) {
    				res = voice_line_signal(line, -1, sig->signal, atoi(data), NULL);
    			}
    			break;
    
    
    		default:
    
    			res = voice_line_signal(line, -1, sig->signal, atoi(data), NULL);
    
    	if (res != 0) {
    		ENDPT_ERR("%s: failed to generate signal %s(%s) on line %d\n", __func__, sig->name, data, line);
    
    //---------------------------------------------------------------------------------------------------
    // Reception of a create connection request from Asterisk. If line type is DECT, we need to relay the
    // request to the dectmngr.
    
    int line_new_connection_by_asterisk(int line, int connection, struct voice_ubus_req_t *ubus_req)
    {
    
    	if (!voice_line_is_ready(line))
    		return -1;
    
    	ENDPT_DBG("%s line: %d, connection: %d\n", __func__, line, connection);
    
    	pcm_states_dump(__func__, line);
    
    	if (get_callid_state(lines[line].pcm_callid[PCM_0]) == CALLID_OBTAINING)
    		lines[line].pcm_callid[PCM_0] = connection;
    	else if (get_callid_state(lines[line].pcm_callid[PCM_1]) == CALLID_OBTAINING)
    		lines[line].pcm_callid[PCM_1] = connection;
    
    	if (lines[line].type == VOICE_LINE_DECT) {
    		if (voice_line_is_offhook(line) || voice_line_get_connection_count(line) > 0) {
    			/* dectmngr already has audio streaming, now libvoice 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
    			 * chance to relay it to dectmngr. */
    
    			line_req = calloc(1, sizeof(struct line_req_t));
    
    				ENDPT_ERR("%s: out of memory\n", __func__);
    
    			line_req->line = line;
    			line_req->connection_id = line;
    
    			line_req->pcm_id = lines[line].pcm_callid[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
    
    		res = voice_connection_create(line, line);
    	}
    
    	return res;
    }
    
    
    //---------------------------------------------------------------------------------------------------
    
    // Reception of a connection parameter update request from Asterisk.
    int line_connection_parm_update_by_asterisk(int line, int connection, struct config_update_struct *data) {
    
    	int res = 0;
    
    	if (!voice_line_is_ready(line))
    		return -1;
    
    	ENDPT_DBG("parm_update request for line: %d, connection: %d, mask: %d, codec: %d, ptime: %d, dtmf_mode: %d \n", line, connection, data->mask, data->codec, data->ptime, data->dtmf_mode);
    
    	pcm_states_dump(__func__, line);
    
    
    	res = voice_connection_parm_update(line, line, data);
    
    //------------------------------------------------------------------------------------------------------------
    // Reception of a close connection request from Asterisk. If line type is DECT we need to relay the request to
    // dectmngr.
    
    int line_close_connection_by_asterisk(int line, int connection, struct voice_ubus_req_t *ubus_req)
    {
    
    	if (!voice_line_is_ready(line))
    		return -1;
    
    	ENDPT_DBG("%s line: %d, connection: %d\n", __func__, line, connection);
    
    	pcm_states_dump(__func__, line);
    
    	if (get_callid_state(lines[line].pcm_callid[PCM_0]) == CALLID_INVALID &&
    		get_callid_state(lines[line].pcm_callid[PCM_1]) == CALLID_INVALID) {
    
    		return voice_connection_close(line, line);
    	}
    
    
    	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 (get_callid_state(lines[line].pcm_callid[PCM_0]) == CALLID_ESTABLISHED &&
    				get_callid_state(lines[line].pcm_callid[PCM_1]) == CALLID_INVALID) {
    
    				lines[line].pcm_callid[PCM_0] = CALLID_INVALID;
    			} else if (get_callid_state(lines[line].pcm_callid[PCM_1]) == CALLID_ESTABLISHED &&
    				get_callid_state(lines[line].pcm_callid[PCM_0]) == CALLID_INVALID) {
    
    				lines[line].pcm_callid[PCM_1] = CALLID_INVALID;
    
    			if (connection == -1) {
    				ENDPT_DBG("Set line_req->pcm_id to -1 for releasing all calls on the extension %d\n", line);
    				line_req->pcm_id = connection;
    			}
    
    			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 (get_callid_state(lines[line].pcm_callid[PCM_0]) == CALLID_ESTABLISHED)
    				lines[line].pcm_callid[PCM_0] = CALLID_INVALID;
    			else if (get_callid_state(lines[line].pcm_callid[PCM_1]) == CALLID_ESTABLISHED)
    				lines[line].pcm_callid[PCM_1] = CALLID_INVALID;
    
    
    			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 (!voice_line_is_ready(line))
    		return -1;
    
    	ENDPT_DBG("%s line: %d connection: %d\n", __func__, line, connection);
    
    	pcm_states_dump(__func__, line);
    
    
    	// 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;
    
    	if (get_callid_state(lines[line].pcm_callid[PCM_0]) < CALLID_ESTABLISHED &&
    		get_callid_state(lines[line].pcm_callid[PCM_1]) < CALLID_ESTABLISHED) {
    
    	if (lines[line].pcm_callid[PCM_0] != connection && lines[line].pcm_callid[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_callid[PCM_0] == connection) {
    
    				lines[line].pcm_callid[PCM_0] = CALLID_INVALID;
    			} else if (lines[line].pcm_callid[PCM_1] == connection) {
    
    				lines[line].pcm_callid[PCM_1] = CALLID_INVALID;
    
    			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_callid[PCM_0] == connection)
    				lines[line].pcm_callid[PCM_0] = CALLID_INVALID;
    			else if (lines[line].pcm_callid[PCM_1] == connection)
    				lines[line].pcm_callid[PCM_1] = CALLID_INVALID;
    
    
    			return voice_connection_close(line, line);
    	}
    
    	return 0;
    }
    
    
    int line_update_connection_by_pbx(int line, int pcm_callid)
    {
    
    	if (!voice_line_is_ready(line))
    		return -1;
    
    	ENDPT_DBG("Received update connection for line: %d with pcm_callid: %d\n", line, pcm_callid);
    
    	pcm_states_dump(__func__, line);
    
    	if(lines[line].type != VOICE_LINE_DECT)
    		return 0;
    
    	if (get_callid_state(lines[line].pcm_callid[PCM_0]) == CALLID_OBTAINING)
    		lines[line].pcm_callid[PCM_0] = pcm_callid;
    	else if (get_callid_state(lines[line].pcm_callid[PCM_1]) == CALLID_OBTAINING)
    		lines[line].pcm_callid[PCM_1] = pcm_callid;
    
    
    	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)
    
    	if (!voice_line_is_ready(line) || lines[line].type != VOICE_LINE_DECT)
    
    	ENDPT_DBG("line=%d, pcm=%d, cid=%s\n", line, pcm, cid);
    
    	pcm_states_dump(__func__, line);
    
    	if (pcm == PCM_0 || pcm == PCM_1) {
    
    		lines[line].pcm_callid[pcm] = CALLID_OBTAINING;
    
    		lines[line].signaled_call_waiting = 0;
    
    	if (!voice_line_is_offhook(line)) {
    		if (voice_line_simulate_hook(line, VOICE_EVT_OFFHOOK))
    			return -1;
    	} else {
    
    		switch(pcm) {
    
    			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));
    
    	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));
    
    		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)
    {
    
    	if (!voice_line_is_ready(line))
    
    		return -1;
    
    
    	if(lines[line].type != VOICE_LINE_DECT) {
    
    		ENDPT_ERR("%s: lines[%d].type != VOICE_LINE_DECT\n", __func__, line);
    
    		return -1;
    	}
    
    	ENDPT_DBG("line %d: pcm = %d\n", line, pcm);
    
    	pcm_states_dump(__func__, line);
    
    	if (pcm == CALL_DECT_UNAVAILABLE) {
    
    		struct line_event_t *msg;
    
    
    		ENDPT_DBG("There is no available DECT handsets for the call. "
    			"Send DECT_UNAVAILABLE event to Asterisk\n");
    
    
    		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 ((pcm & 0xFFFF) == CALL_REJECT && ((pcm >> 16) == CALL_DEFAULT0 || (pcm >> 16) == CALL_DEFAULT1)) {
    		if (get_callid_state(lines[line].pcm_callid[(pcm >> 16)]) == CALLID_OBTAINING) {
    			struct line_event_t *msg;
    
    			ENDPT_DBG("DECT handsets may have not answered the call yet. "
    				"Send CALL_REJECT event to Asterisk\n");
    
    			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);
    		} else {
    			pcm = pcm >> 16;
    		}
    
    	if(get_callid_state(lines[line].pcm_callid[PCM_0]) <= CALLID_OBTAINING ||
    		get_callid_state(lines[line].pcm_callid[PCM_1]) <= CALLID_OBTAINING) {
    		if(pcm == CALL_DEFAULT0 || pcm == CALL_DEFAULT1) {
    			lines[line].pcm_callid[pcm] = CALLID_INVALID;
    			if (get_callid_state(lines[line].pcm_callid[1-pcm]) == CALLID_OBTAINING) {
    				lines[line].pcm_callid[1-pcm] = CALLID_INVALID;
    			}
    		}
    
    		voice_line_simulate_hook(line, VOICE_EVT_ONHOOK);
    
    		return send_reply_dectmngr(ubus_req, line + 1, // +1 Dectmngr lines starting from 1
    			pcm, UBUS_STATUS_OK);
    
    	if(pcm == CALL_DEFAULT0 || pcm == CALL_DEFAULT1)
    		lines[line].pcm_callid[pcm] = CALLID_INVALID;
    
    
    	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;