asterisk-chan-dongle/cpvt.c

152 lines
3.2 KiB
C

/*
Copyright (C) 2010,2011 bg <bg_one@mail.ru>
*/
#include "ast_config.h"
#include <unistd.h>
#include <fcntl.h>
#include <asterisk/utils.h>
#include "cpvt.h"
#include "chan_dongle.h" /* struct pvt */
#include "at_queue.h" /* struct at_queue_task */
#include "mutils.h" /* ITEMS_OF() */
#/* return 0 on success */
// TODO: move to activation time, save resouces
static int init_pipe(int filedes[2])
{
int x;
int rv;
int flags;
rv = pipe(filedes);
if(rv == 0) {
for(x = 0; x < 2; ++x) {
rv = fcntl(filedes[x], F_GETFL);
flags = fcntl(filedes[x], F_GETFD);
if(rv == -1 || flags == -1 || (rv = fcntl(filedes[x], F_SETFL, O_NONBLOCK | rv)) == -1 || (rv = fcntl(filedes[x], F_SETFD, flags | FD_CLOEXEC)) == -1)
goto bad;
}
return 0;
bad:
close(filedes[0]);
close(filedes[1]);
}
return rv;
}
#/* */
EXPORT_DEF struct cpvt * cpvt_alloc(struct pvt * pvt, int call_idx, unsigned dir, call_state_t state)
{
int filedes[2];
struct cpvt * cpvt = NULL;
if(init_pipe(filedes) == 0)
{
cpvt = ast_calloc (1, sizeof (*cpvt));
if(cpvt)
{
cpvt->pvt = pvt;
cpvt->call_idx = call_idx;
cpvt->state = state;
cpvt->dir = dir;
cpvt->rd_pipe[0] = filedes[0];
cpvt->rd_pipe[1] = filedes[1];
// rb_init (&cpvt->a_write_rb, cpvt->a_write_buf, sizeof (cpvt->a_write_buf));
AST_LIST_INSERT_TAIL(&pvt->chans, cpvt, entry);
if(PVT_NO_CHANS(pvt))
pvt_on_create_1st_channel(pvt);
PVT_STATE(pvt, chansno)++;
PVT_STATE(pvt, chan_count[cpvt->state])++;
ast_debug (3, "[%s] create cpvt for call_idx %d dir %d state '%s'\n", PVT_ID(pvt), call_idx, dir, call_state2str(state));
return cpvt;
}
close(filedes[0]);
close(filedes[1]);
}
return cpvt;
}
#/* */
EXPORT_DEF void cpvt_free(struct cpvt* cpvt)
{
pvt_t * pvt = cpvt->pvt;
struct cpvt * found;
struct at_queue_task * task;
close(cpvt->rd_pipe[1]);
close(cpvt->rd_pipe[0]);
ast_debug (3, "[%s] destroy cpvt for call_idx %d dir %d state '%s' flags %d has%s channel\n", PVT_ID(pvt), cpvt->call_idx, cpvt->dir, call_state2str(cpvt->state), cpvt->flags, cpvt->channel ? "" : "'t");
AST_LIST_TRAVERSE_SAFE_BEGIN(&pvt->chans, found, entry) {
if(found == cpvt)
{
AST_LIST_REMOVE_CURRENT(entry);
PVT_STATE(pvt, chan_count[cpvt->state])--;
PVT_STATE(pvt, chansno) --;
break;
}
}
AST_LIST_TRAVERSE_SAFE_END;
/* relink task to sys_chan */
AST_LIST_TRAVERSE(&pvt->at_queue, task, entry) {
if(task->cpvt == cpvt)
{
task->cpvt = &pvt->sys_chan;
}
}
/* drop last_dialed_cpvt if need */
if(pvt->last_dialed_cpvt == cpvt)
pvt->last_dialed_cpvt = NULL;
if(PVT_NO_CHANS(pvt)) {
pvt_on_remove_last_channel(pvt);
pvt_try_restate(pvt);
}
ast_free(cpvt);
}
#/* */
EXPORT_DEF struct cpvt * pvt_find_cpvt(struct pvt * pvt, int call_idx)
{
struct cpvt * cpvt;
AST_LIST_TRAVERSE(&pvt->chans, cpvt, entry) {
if(call_idx == cpvt->call_idx)
return cpvt;
}
return 0;
}
#/* */
EXPORT_DEF const char * pvt_call_dir(const struct pvt * pvt)
{
static const char * dirs[] = {
"Active",
"Outgoing",
"Incoming",
"Both"
};
int index = 0;
struct cpvt * cpvt;
AST_LIST_TRAVERSE(&pvt->chans, cpvt, entry) {
if(cpvt->dir == CALL_DIR_OUTGOING)
index |= 0x1;
else
index |= 0x2;
}
return dirs[index];
}