If the ref is 0 and the state is CHAN_LCR_STATE_RELEASE, see the proceedure
"Call is released by LCR".
+
+Locking issues:
+
+The deadlocking problem:
+
+- chan_lcr locks chan_lock and waits inside ast_queue_xxxx() for ast_channel
+to be unlocked.
+- ast_channel thread locks ast_channel and calls a tech function and waits
+there for chan_lock to be unlocked.
+
+The solution:
+
+Never call ast_queue_xxxx() if ast_channel is not locked and don't wait until
+ast_channel can be locked. All messages to asterisk are queued inside call
+instance and will be handled using a try-lock to get ast_channel lock.
+If it succeeds to lock ast_channel, the ast_queue_xxxx can safely called even
+if the lock is incremented and decremented there.
+
+Exception: Calling ast_queue_frame inside ast->tech->read is safe, because
+it is called from ast_channel process which has already locked ast_channel.
+
*/
#include <stdio.h>
char string[1024], *p = string, *opt, *key;
int gain, i, newmode = 0;
+ if (!data[0])
+ return; // no opts
+
strncpy(string, data, sizeof(string)-1);
string[sizeof(string)-1] = '\0';
break;
case 't':
if (opt[1] != '\0') {
- CERROR(call, call->ast, "Option 't' (transparent) expects no parameter.\n", opt);
+ CERROR(call, call->ast, "Option 't' (no_dsp) expects no parameter.\n", opt);
break;
}
- CDEBUG(call, call->ast, "Option 't' (transparent).\n");
- if (!call->transparent) {
- call->transparent = 1;
+ CDEBUG(call, call->ast, "Option 't' (no dsp).\n");
+ if (!call->nodsp) {
+ call->nodsp = 1;
newmode = 1;
}
break;
if (call->bchannel)
bchannel_pipeline(call->bchannel, call->pipeline);
break;
+ case 'r':
+ if (opt[1] == '\0') {
+ CERROR(call, call->ast, "Option 'r' (re-buffer 160 bytes) expects no parameter.\n", opt);
+ break;
+ }
+ CDEBUG(call, call->ast, "Option 'r' (re-buffer 160 bytes)");
+ call->rebuffer = 1;
+ break;
#if 0
case 's':
if (opt[1] != '\0') {
/* re-open, if bchannel is created */
if (call->bchannel && call->bchannel->b_sock > -1) {
bchannel_destroy(call->bchannel);
- if (bchannel_create(call->bchannel, ((call->transparent)?1:0) + ((call->hdlc)?2:0)))
+ if (bchannel_create(call->bchannel, ((call->nodsp)?1:0) + ((call->hdlc)?2:0)))
bchannel_activate(call->bchannel, 1);
}
}
newparam.setup.callerinfo.ntype = INFO_NTYPE_UNKNOWN;
}
newparam.setup.capainfo.bearer_capa = ast->transfercapability;
- newparam.setup.capainfo.bearer_info1 = (options.law=='a')?3:2;
newparam.setup.capainfo.bearer_mode = INFO_BMODE_CIRCUIT;
+ if (!call->hdlc)
+ newparam.setup.capainfo.bearer_info1 = (options.law=='a')?3:2;
newparam.setup.capainfo.hlc = INFO_HLC_NONE;
newparam.setup.capainfo.exthlc = INFO_HLC_NONE;
send_message(MESSAGE_SETUP, call->ref, &newparam);
goto release;
}
call->pbx_started = 1;
+// if (call->state == CHAN_LCR_STATE_IN_DIALING)
+// ast_setstate(ast, AST_STATE_DIALING);
+// else
+// ast_setstate(ast, AST_STATE_OFFHOOK);
return;
}
/* change state */
call->state = CHAN_LCR_STATE_IN_SETUP;
- lcr_start_pbx(call, ast, param->setup.dialinginfo.sending_complete);
+ if (!call->pbx_started)
+ lcr_start_pbx(call, ast, param->setup.dialinginfo.sending_complete);
}
/*
/* change state */
call->state = CHAN_LCR_STATE_OUT_PROCEEDING;
- /* send event to asterisk */
+ /* queue event for asterisk */
if (call->ast && call->pbx_started)
- ast_queue_control(call->ast, AST_CONTROL_PROCEEDING);
+ strncat(call->queue_string, "P", sizeof(call->queue_string)-1);
}
/*
/* change state */
call->state = CHAN_LCR_STATE_OUT_ALERTING;
- /* send event to asterisk */
+ /* queue event to asterisk */
if (call->ast && call->pbx_started)
- ast_queue_control(call->ast, AST_CONTROL_RINGING);
+ strncat(call->queue_string, "R", sizeof(call->queue_string)-1);
}
/*
}
/* copy connectinfo */
memcpy(&call->connectinfo, ¶m->connectinfo, sizeof(struct connect_info));
- /* send event to asterisk */
+ /* queue event to asterisk */
if (call->ast && call->pbx_started)
- ast_queue_control(call->ast, AST_CONTROL_ANSWER);
+ strncat(call->queue_string, "A", sizeof(call->queue_string)-1);
}
/*
call->ref = 0;
/* change to release state */
call->state = CHAN_LCR_STATE_RELEASE;
- /* release asterisk */
+ /* queue release asterisk */
if (ast)
{
ast->hangupcause = call->cause;
if (call->pbx_started)
- ast_queue_hangup(ast);
+ strcpy(call->queue_string, "H"); // overwrite other indications
else {
ast_hangup(ast); // call will be destroyed here
}
call->cause = param->disconnectinfo.cause;
call->location = param->disconnectinfo.location;
}
- /* if we have an asterisk instance, send hangup, else we are done */
+ /* if we have an asterisk instance, queue hangup, else we are done */
if (ast)
{
ast->hangupcause = call->cause;
if (call->pbx_started)
- ast_queue_hangup(ast);
+ strcpy(call->queue_string, "H");
else {
ast_hangup(ast); // call will be destroyed here
}
static void lcr_in_information(struct chan_call *call, int message_type, union parameter *param)
{
struct ast_channel *ast = call->ast;
- struct ast_frame fr;
- char *p;
CDEBUG(call, call->ast, "Incoming information from LCR. (dialing=%s)\n", param->information.id);
return;
}
- /* copy digits */
- p = param->information.id;
- if (call->state == CHAN_LCR_STATE_IN_DIALING && *p)
- {
- CDEBUG(call, call->ast, "Asterisk is started, sending DTMF frame.\n");
- while (*p)
- {
- /* send digit to asterisk */
- memset(&fr, 0, sizeof(fr));
- fr.frametype = AST_FRAME_DTMF;
- fr.subclass = *p;
- fr.delivery = ast_tv(0, 0);
- ast_queue_frame(call->ast, &fr);
- p++;
- }
+ /* change dailing state after setup */
+ if (call->state == CHAN_LCR_STATE_IN_SETUP) {
+ CDEBUG(call, call->ast, "Changing from SETUP to DIALING state.\n");
+ call->state = CHAN_LCR_STATE_IN_DIALING;
+// ast_setstate(ast, AST_STATE_DIALING);
}
+
+ /* queue digits */
+ if (call->state == CHAN_LCR_STATE_IN_DIALING && param->information.id[0])
+ strncat(call->queue_string, param->information.id, sizeof(call->queue_string)-1);
+
/* use bridge to forware message not supported by asterisk */
if (call->state == CHAN_LCR_STATE_CONNECT) {
CDEBUG(call, call->ast, "Call is connected, briding.\n");
void lcr_in_dtmf(struct chan_call *call, int val)
{
struct ast_channel *ast = call->ast;
- struct ast_frame fr;
+ char digit[2];
if (!ast)
return;
if (!call->pbx_started)
return;
- CDEBUG(call, call->ast, "Forwarding DTMF digit '%c' to Asterisk.\n", val);
-
- /* send digit to asterisk */
- memset(&fr, 0, sizeof(fr));
- fr.frametype = AST_FRAME_DTMF;
- fr.subclass = val;
- fr.delivery = ast_tv(0, 0);
- ast_queue_frame(call->ast, &fr);
+ CDEBUG(call, call->ast, "Recognised DTMF digit '%c'.\n", val);
+ digit[0] = val;
+ digit[1] = '\0';
+ strncat(call->queue_string, digit, sizeof(call->queue_string)-1);
}
/*
switch(param->bchannel.type)
{
case BCHANNEL_ASSIGN:
- CDEBUG(NULL, NULL, "Received BCHANNEL_ASSIGN message. (handle=%08lx)\n", param->bchannel.handle);
+ CDEBUG(NULL, NULL, "Received BCHANNEL_ASSIGN message. (handle=%08lx) for ref %d\n", param->bchannel.handle, ref);
if ((bchannel = find_bchannel_handle(param->bchannel.handle)))
{
CERROR(NULL, NULL, "bchannel handle %x already assigned.\n", (int)param->bchannel.handle);
CDEBUG(call, call->ast, "Join bchannel, because call is already bridged.\n");
bchannel_join(bchannel, call->bridge_id);
}
+ /* create only, if call exists, othewhise it bchannel is freed below... */
+ if (bchannel_create(bchannel, ((call->nodsp)?1:0) + ((call->hdlc)?2:0)))
+ bchannel_activate(bchannel, 1);
}
- if (bchannel_create(bchannel, ((call->transparent)?1:0) + ((call->hdlc)?2:0)))
- bchannel_activate(bchannel, 1);
/* acknowledge */
newparam.bchannel.type = BCHANNEL_ASSIGN_ACK;
newparam.bchannel.handle = param->bchannel.handle;
goto again;
}
CDEBUG(call, call->ast, "Queue call release, because Asterisk channel is running.\n");
- ast_queue_hangup(call->ast);
+ strcpy(call->queue_string, "H");
call = call->next;
}
lcr_sock = -1;
}
+
+/* sending queue to asterisk */
+static int queue_send(void)
+{
+ int work = 0;
+ struct chan_call *call;
+ struct ast_channel *ast;
+ struct ast_frame fr;
+ char *p;
+
+ call = call_first;
+ while(call) {
+ p = call->queue_string;
+ ast = call->ast;
+ if (*p && ast) {
+ /* there is something to queue */
+ if (!ast_channel_trylock(ast)) { /* succeed */
+ while(*p) {
+ switch (*p) {
+ case 'P':
+ CDEBUG(call, ast, "Sending queued PROCEEDING to Asterisk.\n");
+ ast_queue_control(ast, AST_CONTROL_PROCEEDING);
+ break;
+ case 'R':
+ CDEBUG(call, ast, "Sending queued RINGING to Asterisk.\n");
+ ast_queue_control(ast, AST_CONTROL_RINGING);
+ break;
+ case 'A':
+ CDEBUG(call, ast, "Sending queued ANSWER to Asterisk.\n");
+ ast_queue_control(ast, AST_CONTROL_ANSWER);
+ break;
+ case 'H':
+ CDEBUG(call, ast, "Sending queued HANGUP to Asterisk.\n");
+ ast_queue_hangup(ast);
+ break;
+ case '1': case '2': case '3': case 'a':
+ case '4': case '5': case '6': case 'b':
+ case '7': case '8': case '9': case 'c':
+ case '*': case '0': case '#': case 'd':
+ CDEBUG(call, ast, "Sending queued digit '%c' to Asterisk.\n", *p);
+ /* send digit to asterisk */
+ memset(&fr, 0, sizeof(fr));
+ fr.frametype = AST_FRAME_DTMF_BEGIN;
+ fr.subclass = *p;
+ fr.delivery = ast_tv(0, 0);
+ ast_queue_frame(ast, &fr);
+ fr.frametype = AST_FRAME_DTMF_END;
+ ast_queue_frame(ast, &fr);
+ break;
+ default:
+ CDEBUG(call, ast, "Ignoring queued digit 0x%02d.\n", *p);
+ }
+ p++;
+ }
+ call->queue_string[0] = '\0';
+ ast_channel_unlock(ast);
+ work = 1;
+ }
+ }
+ call = call->next;
+ }
+
+ return work;
+}
+
+/* signal handler */
void sighandler(int sigset)
{
}
+/* chan_lcr thread */
static void *chan_thread(void *arg)
{
int work;
ret = bchannel_handle();
if (ret)
work = 1;
-
+
+ /* handle messages to asterisk */
+ ret = queue_send();
+ if (ret)
+ work = 1;
+
+ /* delay if no work done */
if (!work) {
ast_mutex_unlock(&chan_lock);
usleep(30000);
if (lcr_sock < 0)
{
CERROR(NULL, NULL, "Rejecting call from Asterisk, because LCR not running.\n");
+ ast_mutex_unlock(&chan_lock);
return NULL;
}
if (!call)
{
/* failed to create instance */
+ ast_mutex_unlock(&chan_lock);
return NULL;
}
CERROR(NULL, NULL, "Failed to create Asterisk channel.\n");
free_call(call);
/* failed to create instance */
+ ast_mutex_unlock(&chan_lock);
return NULL;
}
ast->tech = &lcr_tech;
return 0;
}
-static int lcr_digit(struct ast_channel *ast, char digit)
+static int lcr_digit_begin(struct ast_channel *ast, char digit)
{
struct chan_call *call;
union parameter newparam;
return(0);
}
+static int lcr_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+ printf("DIGIT END %c\n", digit);
+ return (0);
+}
+
static int lcr_answer(struct ast_channel *ast)
{
union parameter newparam;
return -1;
}
- CDEBUG(call, ast, "Received answer from Asterisk.\n");
+ CDEBUG(call, ast, "Received answer from Asterisk (maybe during lcr_bridge).\n");
/* copy connectinfo, if bridged */
if (call->bridge_call)
memcpy(&call->connectinfo, &call->bridge_call->connectinfo, sizeof(struct connect_info));
/* send connect message to lcr */
- memset(&newparam, 0, sizeof(union parameter));
- memcpy(&newparam.connectinfo, &call->connectinfo, sizeof(struct connect_info));
- send_message(MESSAGE_CONNECT, call->ref, &newparam);
+ if (call->state != CHAN_LCR_STATE_CONNECT) {
+ memset(&newparam, 0, sizeof(union parameter));
+ memcpy(&newparam.connectinfo, &call->connectinfo, sizeof(struct connect_info));
+ send_message(MESSAGE_CONNECT, call->ref, &newparam);
+ call->state = CHAN_LCR_STATE_CONNECT;
+ }
/* change state */
- call->state = CHAN_LCR_STATE_CONNECT;
/* request bchannel */
if (!call->bchannel) {
CDEBUG(call, ast, "Requesting B-channel.\n");
if (ast->hangupcause > 0)
send_release_and_import(call, ast->hangupcause, LOCATION_PRIVATE_LOCAL);
else
- send_release_and_import(call, CAUSE_RESSOURCEUNAVAIL, LOCATION_PRIVATE_LOCAL);
+ send_release_and_import(call, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL);
/* remove call */
free_call(call);
if (!pthread_equal(tid, chan_tid))
static struct ast_frame *lcr_read(struct ast_channel *ast)
{
struct chan_call *call;
- int i, len;
- unsigned char *p;
+ int len;
ast_mutex_lock(&chan_lock);
call = ast->tech_pvt;
return NULL;
}
if (call->pipe[0] > -1) {
- len = read(call->pipe[0], call->read_buff, sizeof(call->read_buff));
+ if (call->rebuffer && !call->hdlc) {
+ len = read(call->pipe[0], call->read_buff, 160);
+ } else {
+ len = read(call->pipe[0], call->read_buff, sizeof(call->read_buff));
+ }
if (len <= 0) {
close(call->pipe[0]);
call->pipe[0] = -1;
}
}
- p = call->read_buff;
- for (i = 0; i < len; i++) {
- *p = flip_bits[*p];
- p++;
- }
-
call->read_fr.frametype = AST_FRAME_VOICE;
call->read_fr.subclass = ast->nativeformats;
call->read_fr.datalen = len;
carr[0] = ast1;
carr[1] = ast2;
-
+
/* join via dsp (if the channels are currently open) */
ast_mutex_lock(&chan_lock);
- bridge_id = new_bridge_id();
call1 = ast1->tech_pvt;
call2 = ast2->tech_pvt;
- if (call1 && call2)
- {
+ if (!call1 || !call2) {
+ CDEBUG(NULL, NULL, "Bridge, but we don't have two call instances, exitting.\n");
+ ast_mutex_unlock(&chan_lock);
+ return AST_BRIDGE_COMPLETE;
+ }
+
+ /* join, if both call instances uses dsp */
+ if (!call1->nodsp && !call2->nodsp) {
+ CDEBUG(NULL, NULL, "Both calls use DSP, briding via DSP.\n");
+
+ /* get bridge id and join */
+ bridge_id = new_bridge_id();
+
call1->bridge_id = bridge_id;
if (call1->bchannel)
bchannel_join(call1->bchannel, bridge_id);
- call1->bridge_call = call2;
- }
- if (call2)
- {
+
call2->bridge_id = bridge_id;
if (call2->bchannel)
bchannel_join(call2->bchannel, bridge_id);
- call2->bridge_call = call1;
+ } else
+ if (call1->nodsp && call2->nodsp)
+ CDEBUG(NULL, NULL, "Both calls use no DSP, briding in channel driver.\n");
+ else
+ CDEBUG(NULL, NULL, "One call uses no DSP, briding in channel driver.\n");
+ call1->bridge_call = call2;
+ call2->bridge_call = call1;
+
+ if (call1->state == CHAN_LCR_STATE_IN_SETUP
+ || call1->state == CHAN_LCR_STATE_IN_DIALING
+ || call1->state == CHAN_LCR_STATE_IN_PROCEEDING
+ || call1->state == CHAN_LCR_STATE_IN_ALERTING) {
+ CDEBUG(call1, ast1, "Bridge established before lcr_answer, so we call it ourself: Calling lcr_answer...\n");
+ lcr_answer(ast1);
+ }
+ if (call2->state == CHAN_LCR_STATE_IN_SETUP
+ || call2->state == CHAN_LCR_STATE_IN_DIALING
+ || call2->state == CHAN_LCR_STATE_IN_PROCEEDING
+ || call2->state == CHAN_LCR_STATE_IN_ALERTING) {
+ CDEBUG(call2, ast2, "Bridge established before lcr_answer, so we call it ourself: Calling lcr_answer...\n");
+ lcr_answer(ast2);
}
+
ast_mutex_unlock(&chan_lock);
while(1) {
ast_mutex_lock(&chan_lock);
call1 = ast1->tech_pvt;
call2 = ast2->tech_pvt;
- if (call1)
+ if (call1 && call1->bridge_id)
{
call1->bridge_id = 0;
if (call1->bchannel)
bchannel_join(call1->bchannel, 0);
if (call1->bridge_call)
call1->bridge_call->bridge_call = NULL;
- call1->bridge_call = NULL;
}
- if (call2)
+ if (call2 && call1->bridge_id)
{
call2->bridge_id = 0;
if (call2->bchannel)
bchannel_join(call2->bchannel, 0);
if (call2->bridge_call)
call2->bridge_call->bridge_call = NULL;
- call2->bridge_call = NULL;
}
+ call1->bridge_call = NULL;
+ call2->bridge_call = NULL;
ast_mutex_unlock(&chan_lock);
return AST_BRIDGE_COMPLETE;
.type="LCR",
.description="Channel driver for connecting to Linux-Call-Router",
.requester=lcr_request,
- .send_digit_begin=lcr_digit,
+ .send_digit_begin=lcr_digit_begin,
+ .send_digit_end=lcr_digit_end,
.call=lcr_call,
.bridge=lcr_bridge,
.hangup=lcr_hangup,
" d - Send display text on called phone, text is the optarg.\n"
" n - Don't detect dtmf tones on called channel.\n"
" h - Force data call (HDLC).\n"
- " t - Disable all audio features (required for fax application).\n"
+ " t - Disable mISDN_dsp features (required for fax application).\n"
" c - Make crypted outgoing call, optarg is keyindex.\n"
" e - Perform echo cancelation on this channel.\n"
" Takes mISDN pipeline option as optarg.\n"