Newer
Older
// Handles UBUS communication between Asterisk <-> dectmngr
// Takes care of translating Asterisk UBUS request to
// corresponding dectmngr requests.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <assert.h>
#include "line-dect.h"
#include "line.h"
#include "ubus.h"
//-------------------------------------------------------------
static int num_dectmngr_pending_reqs; // Number of of pending UBUS RPC calls both from and to dectmngr
static int current_handled_line; // Current Asterisk line request in investigation
static pe_list_t *ubus_req_list_to_dect; // List of pending UBUS calls to dectmngr.
static pe_list_t *ubus_req_list_to_asterisk; // List of DECT UBUS calls waiting for Asterisk to answer.
//-------------------------------------------------------------
// Add a pending UBUS request destined for dectmngr to a wait queue.
int ubus_queue_req_to_dectmngr(struct line_req_t *req) {
ENDPT_DBG("Adding req %p to list for dectmngr\n", req);
pe_list_add(ubus_req_list_to_dect, req);
return 0;
}
//-------------------------------------------------------------
// Add a pending UBUS request destined for Asterisk to a wait queue.
int ubus_queue_req_to_asterisk(struct line_req_t *req) {
ENDPT_DBG("Adding req %p to list for Asterisk, reqs %d\n", req, num_dectmngr_pending_reqs);
pe_list_add(ubus_req_list_to_asterisk, req);
return 0;
}
//-------------------------------------------------------------
// Pop oldest pending UBUS request from waiting queue if
// we have no outstanding communication with dectmngr.
int ubus_process_queued_reqs_to_dectmngr(void) {
// Only one outstanding request at a time
if(num_dectmngr_pending_reqs > 0)
return 0;
// Fetch oldest waiting request
req = pe_list_get(ubus_req_list_to_dect);
if (!req)
ENDPT_DBG("Poped DECT req %p from list, action: %d pcmId: %d, caller_id: %s, callerName: %s\n",
req, req->action, req->pcm_id, req->caller_id, req->caller_name);
pcm_states_dump(__func__, req->line);
case ACTION_CONN_CREATE:
case ACTION_SIG_RING:
if(ubus_call_dectmngr(voicemngr_line_to_dectmngr_line(req->line), 1, 0, req->caller_id, req->caller_name, req->pcm_id, req)) {
return -1;
}
break;
case ACTION_CONN_CLOSE:
if(ubus_call_dectmngr(voicemngr_line_to_dectmngr_line(req->line), 0, 1, req->caller_id, req->caller_name, req->pcm_id, req)) {
if(ubus_call_dectmngr(voicemngr_line_to_dectmngr_line(req->line), 0, 2, req->caller_id, req->caller_name, req->pcm_id, req)) {
free(req);
return -1;
}
break;
case ACTION_SIG_ANSWERED:
if(ubus_call_dectmngr(voicemngr_line_to_dectmngr_line(req->line), 2, 0, req->caller_id, req->caller_name, req->pcm_id, req)) {
free(req);
return -1;
}
break;
default:
break;
}
num_dectmngr_pending_reqs++;
return 0;
}
//-----------------------------------------------------------------
// If appropriate, simulate keypad digit pressesing. Returned value
// -1 Something gets wrong
// 0 Does nothing
// 1 Keypad events have been handled
int simulate_digits_pressing(int line, const char *pressed_digits) {
if(lines[line].type != VOICE_LINE_DECT)
ENDPT_DBG("line: %d pressed_digits: %s\n", line, (pressed_digits ? pressed_digits : ""));
if(pressed_digits && lines[line].pending_digits) {
digits = strndup(pressed_digits, MAX_KEYPAD_DIGITS);
ENDPT_DBG("Queueing line %d digits %s\n", line, pressed_digits);
pe_list_add(lines[line].pending_digits, digits);
digits = pe_list_get(lines[line].pending_digits);
if(digits) {
int res;
ENDPT_DBG("Dequeued line %d digits %s\n", line, digits);
res = line_signal(line, "keypad", digits, NULL);
free(digits);
return (res == 0 ? 1 : -1);
}
return 0;
}
//-------------------------------------------------------------
// dectmngr has answered an UBUS RPC "call" request. Did it
// accept the line action request? Then we call libvoice to
// start/stop the audio stream and inform Asterisk to proceed
// with phone call setup.
int ubus_cb_dectmngr_replied(struct line_req_t *req, enum ubus_msg_status reply_status) {
if(!req)
return -1;
ENDPT_DBG("got answer req %p from dectmngr, action: %d, line: %d, pcmId: %d, conId: %d\n",
req, req->action, req->line, req->pcm_id, req->connection_id);
pcm_states_dump(__func__, req->line);
if(reply_status == UBUS_STATUS_OK && voice_connection_create(req->line, req->line)) {
reply_status = UBUS_STATUS_UNKNOWN_ERROR;
lines[req->line].pcm_callid[req->pcm_id] = CALLID_INVALID;
if (get_callid_state(lines[req->line].pcm_callid[PCM_0]) == CALLID_ESTABLISHED ||
get_callid_state(lines[req->line].pcm_callid[PCM_1]) == CALLID_ESTABLISHED)
reply_status = voice_connection_close(req->line, req->line) ? // Close regardless of any dectmngr error.
UBUS_STATUS_UNKNOWN_ERROR : UBUS_STATUS_OK;
if ((req->pcm_id == PCM_0 && get_callid_state(lines[req->line].pcm_callid[PCM_1]) == CALLID_OBTAINING) ||
(req->pcm_id == PCM_1 && get_callid_state(lines[req->line].pcm_callid[PCM_0]) == CALLID_OBTAINING))
lines[req->line].pcm_callid[req->pcm_id] = CALLID_OBTAINING;
send_reply_asterisk(&req->ubus, reply_status);
if(num_dectmngr_pending_reqs > 0)
num_dectmngr_pending_reqs--;
return 0;
}
//-------------------------------------------------------------
// libpicoevent let us iterate though a list with dect ubus
// requests waiting for an answer. In this list find the
// UBUS request object of interest and ignore the rest.
static int pe_list_cb_find_req(void *arg) {
struct line_req_t *req = (struct line_req_t*) arg;
return (req->line == current_handled_line);
}
//-------------------------------------------------------------
// Asterisk has answered an UBUS RPC "call" request initiated
// by dectmngr. Now relay the Asterisk reply back to dectmngr.
int ubus_cb_asterisk_replied(struct line_event_t *lineEv, enum ubus_msg_status reply_status) {
if(!lineEv || !voice_line_is_ready(lineEv->line)) {
ENDPT_ERR("Invalid argument\n");
ENDPT_DBG("started\n");
if(lines[lineEv->line].simulated_hook) {
voice_hook_simulation_maintain(lineEv->line);
}
// Find map index to the line event that was sent to Asterisk.
for(epEvntIdx = 0; event_map[epEvntIdx].event != VOICE_EVT_END &&
strcasecmp(lineEv->name, event_map[epEvntIdx].name); epEvntIdx++);
// Check also dect event map
for(dectEvntIdx = 0; dect_event_map[dectEvntIdx].event != DECT_EVT_END &&
strcasecmp(lineEv->name, dect_event_map[dectEvntIdx].name); dectEvntIdx++);
if(event_map[epEvntIdx].event == VOICE_EVT_END && dect_event_map[dectEvntIdx].event == DECT_EVT_END)
// Find the current line request matching the line event.
assert(current_handled_line == -1);
current_handled_line = lineEv->line;
req = pe_list_find(ubus_req_list_to_asterisk, pe_list_cb_find_req);
ENDPT_DBG("poped Asterisk req %p line %d event %s from list, action %d\n",
req, lineEv->line, lineEv->name, req->action);
if(reply_status == UBUS_STATUS_OK) {
case ACTION_CONN_CREATE:
switch(event_map[epEvntIdx].event) {
case VOICE_EVT_DTMF0:
case VOICE_EVT_DTMF1:
case VOICE_EVT_DTMF2:
case VOICE_EVT_DTMF3:
case VOICE_EVT_DTMF4:
case VOICE_EVT_DTMF5:
case VOICE_EVT_DTMF6:
case VOICE_EVT_DTMF7:
case VOICE_EVT_DTMF8:
case VOICE_EVT_DTMF9:
case VOICE_EVT_DTMFS:
case VOICE_EVT_DTMFH:
case VOICE_EVT_DTMFA:
case VOICE_EVT_DTMFB:
case VOICE_EVT_DTMFC:
case VOICE_EVT_DTMFD:
case VOICE_EVT_OFFHOOK:
case VOICE_EVT_FLASH:
/* By now Asterisk knows the terminal is off-hook. If we
* have any queued phone number digits they are sent now. */
res = simulate_digits_pressing(lineEv->line, NULL);
return -1;
}
break;
default: // Ignore other events
if (dect_event_map[dectEvntIdx].event != DECT_EVT_END)
break;
else
return 0;
}
/* Asterisk will create an endpoint signal
* request for dialtone soon. */
break;
case ACTION_CONN_CLOSE:
if(event_map[epEvntIdx].event != VOICE_EVT_ONHOOK && dect_event_map[dectEvntIdx].event != DECT_EVT_RELEASE)
return 0;
break;
default:
break;
}
}
send_reply_dectmngr(&req->ubus, voicemngr_line_to_dectmngr_line(lineEv->line), lineEv->line, reply_status);
pe_list_delete(ubus_req_list_to_asterisk, req);
if(num_dectmngr_pending_reqs > 0)
num_dectmngr_pending_reqs--;
return 0;
}
//-------------------------------------------------------------
// Translate dectmngr line to voicemngr line
int dectmngr_line_to_voicemngr_line(int line) {
return line - 1;
}
//-------------------------------------------------------------
// Translate voicemngr line to dectmngr line
int voicemngr_line_to_dectmngr_line(int line) {
return line + 1;
}
//-------------------------------------------------------------
int line_dect_init(void) {
ubus_req_list_to_dect = pe_list_new();
ubus_req_list_to_asterisk = pe_list_new();
current_handled_line = -1;
return 0;
}
//-------------------------------------------------------------
// Free resources and cleanup
int line_dect_deinit(void) {
pe_list_t *reqLists[2];
int i;
reqLists[0] = ubus_req_list_to_dect;
reqLists[1] = ubus_req_list_to_asterisk;
if(!reqLists[i])
continue;
req = pe_list_get(reqLists[i]);
if(req) {
send_reply_asterisk(&req->ubus, UBUS_STATUS_CONNECTION_FAILED);
free(reqLists[i]);
}
num_dectmngr_pending_reqs = 0;
return 0;
}