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 struct timespec ts_last_req_to_dectmngr = { 0, };
static int num_dectmngr_pending_reqs = 0; // 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 = NULL; // List of pending UBUS calls to dectmngr.
static pe_list_t *ubus_req_list_to_asterisk = NULL; // 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) {
clock_gettime(CLOCK_MONOTONIC, &req->time_stamp);
ENDPT_DBG("Added a new line_req to list for dectmngr and num_dectmngr_pending_reqs is %d\n",
num_dectmngr_pending_reqs);
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) {
clock_gettime(CLOCK_MONOTONIC, &req->time_stamp);
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
num_dectmngr_pending_reqs++;
ENDPT_DBG("Added a new line_req to list for Asterisk and increased num_dectmngr_pending_reqs to %d\n",
num_dectmngr_pending_reqs);
return 0;
}
//-------------------------------------------------------------
// Callback function to find an old line_req_t
static int pe_list_find_old_req(void *arg) {
struct line_req_t *req = (struct line_req_t*)arg;
struct timespec ts_now = { 0, };
clock_gettime(CLOCK_MONOTONIC, &ts_now);
if ((ts_now.tv_sec - req->time_stamp.tv_sec) >= (UBUS_REQUEST_TIMEOUT + 1) && ts_now.tv_nsec >= req->time_stamp.tv_nsec)
return 1;
return 0;
}
//-------------------------------------------------------------
// Remove old line requests
static int rm_old_line_reqs() {
while (num_dectmngr_pending_reqs > 0 && ubus_req_list_to_asterisk->count > 0) {
struct line_req_t *req = pe_list_find(ubus_req_list_to_asterisk, pe_list_find_old_req);
if (!req) {
break;
}
pe_list_delete(ubus_req_list_to_asterisk, req);
ENDPT_DBG("Remove one old line_req_t to Asterisk: line:%d, pcm_id:%d, connection_id:%d, action:%d, "
"caller_id:%s, caller_name:%s\n",
req->line, req->pcm_id, req->connection_id, req->action, req->caller_id, req->caller_name);
free(req);
num_dectmngr_pending_reqs--;
}
if (num_dectmngr_pending_reqs > 0 && ts_last_req_to_dectmngr.tv_sec > 0) {
struct timespec ts_now = { 0, };
clock_gettime(CLOCK_MONOTONIC, &ts_now);
if ((ts_now.tv_sec - ts_last_req_to_dectmngr.tv_sec) >= (UBUS_REQUEST_TIMEOUT + 1) &&
ts_now.tv_nsec >= ts_last_req_to_dectmngr.tv_nsec) {
ENDPT_DBG("The last ubus request to dectmngr may have been timeout\n");
num_dectmngr_pending_reqs--;
}
}
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) {
struct dectmngr_rpc_t dectmngr_rpc = { 0, };
if (rm_old_line_reqs() || num_dectmngr_pending_reqs > 0) {
ENDPT_DBG("Already %d pending request(s)\n", num_dectmngr_pending_reqs);
}
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)) {
case ACTION_SIG_CW_CLOSE:
if (get_callid_state(lines[req->line].pcm_callid[req->pcm_id]) == CALLID_ESTABLISHED){
// do nothing if accept the call waiting
free(req);
return 0;
}
// ACTION_CONN_CLOSE: could not just skip due to compile warning is treated as error.
if(ubus_call_dectmngr(voicemngr_line_to_dectmngr_line(req->line), 0, 1, req->caller_id, req->caller_name, req->pcm_id, req)) {
free(req);
return -1;
}
break;
case ACTION_RINGING_STOP:
dectmngr_rpc.action = req->action;
dectmngr_rpc.extension_id = voicemngr_line_to_dectmngr_line(req->line);
if(ubus_dectmngr_rpc(&dectmngr_rpc, req)) {
free(req);
return -1;
}
break;
dectmngr_rpc.action = req->action;
dectmngr_rpc.extension_id = voicemngr_line_to_dectmngr_line(req->line);
dectmngr_rpc.pcm_id = req->pcm_id;
if(ubus_dectmngr_rpc(&dectmngr_rpc, 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;
ENDPT_DBG("Invalid action: %d\n", req->action);
return -1;
clock_gettime(CLOCK_MONOTONIC, &ts_last_req_to_dectmngr);
ENDPT_DBG("Increased num_dectmngr_pending_reqs to %d\n", num_dectmngr_pending_reqs);
//-----------------------------------------------------------------
// 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;
case ACTION_RINGING_STOP:
if (req->pcm_id == PCM_0 || req->pcm_id == PCM_1) {
lines[req->line].pcm_callid[req->pcm_id] = CALLID_INVALID;
} else if (req->pcm_id == -1) {
// Close all calls
lines[req->line].pcm_callid[PCM_0] = CALLID_INVALID;
lines[req->line].pcm_callid[PCM_1] = 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))
if (req->pcm_id == PCM_0 || req->pcm_id == PCM_1)
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--;
ENDPT_DBG("Decreased num_dectmngr_pending_reqs to %d\n", 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.
void 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");
return;
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)
return;
// 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);
if (!req)
return; // None found, then do nothing
ENDPT_DBG("Poped Asterisk request %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. */
if (simulate_digits_pressing(lineEv->line, NULL) != 0)
return;
break;
default: // Ignore other events
if (dect_event_map[dectEvntIdx].event == DECT_EVT_END)
return;
break;
if(event_map[epEvntIdx].event != VOICE_EVT_ONHOOK && dect_event_map[dectEvntIdx].event != DECT_EVT_RELEASE)
return;
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);
free(req);
if (num_dectmngr_pending_reqs > 0) {
num_dectmngr_pending_reqs--;
ENDPT_DBG("Decreased num_dectmngr_pending_reqs to %d\n", num_dectmngr_pending_reqs);
}
return;
}
//-------------------------------------------------------------
// 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;
}