X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=chan_lcr.c;h=7eabe0e6cdf84f86417526983d78ad3cb208bdd1;hp=ab5886c64a2cad81f256f2334fd7fcd406236b89;hb=ab4a1270e9c99ab7a21a957759de2a885100afb1;hpb=badc91ad966b4b7598077a96121931cd4d90754b diff --git a/chan_lcr.c b/chan_lcr.c index ab5886c..7eabe0e 100644 --- a/chan_lcr.c +++ b/chan_lcr.c @@ -78,6 +78,27 @@ LCR and the chan_call instance is destroyed. 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 @@ -454,6 +475,14 @@ void apply_opt(struct chan_call *call, char *data) 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') { @@ -661,6 +690,7 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c /* change state */ call->state = CHAN_LCR_STATE_IN_PROCEEDING; + ast_setstate(ast, AST_STATE_OFFHOOK); goto start; } @@ -675,6 +705,7 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c /* change state */ call->state = CHAN_LCR_STATE_IN_DIALING; + ast_setstate(ast, AST_STATE_OFFHOOK); /* if match, start pbx */ if (ast_exists_extension(ast, ast->context, ast->exten, 1, call->oad)) { @@ -796,7 +827,8 @@ static void lcr_in_setup(struct chan_call *call, int message_type, union paramet /* 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); } /* @@ -824,9 +856,9 @@ static void lcr_in_proceeding(struct chan_call *call, int message_type, union pa /* 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); } /* @@ -838,9 +870,9 @@ static void lcr_in_alerting(struct chan_call *call, int message_type, union para /* 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); } /* @@ -863,9 +895,9 @@ static void lcr_in_connect(struct chan_call *call, int message_type, union param } /* 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); } /* @@ -897,12 +929,12 @@ static void lcr_in_disconnect(struct chan_call *call, int message_type, union pa 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 } @@ -928,12 +960,12 @@ static void lcr_in_release(struct chan_call *call, int message_type, union param 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 } @@ -950,8 +982,6 @@ static void lcr_in_release(struct chan_call *call, int message_type, union param 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); @@ -966,22 +996,10 @@ static void lcr_in_information(struct chan_call *call, int message_type, union p 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++; - } - } + /* 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"); @@ -1031,21 +1049,17 @@ static void lcr_in_facility(struct chan_call *call, int message_type, union para 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); } /* @@ -1065,7 +1079,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) 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); @@ -1310,7 +1324,7 @@ again: 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; } @@ -1464,10 +1478,76 @@ void close_socket(void) 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; + fr.subclass = *p; + fr.delivery = ast_tv(0, 0); + fr.len = 100; + 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; @@ -1516,7 +1596,13 @@ static void *chan_thread(void *arg) 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); @@ -1552,6 +1638,7 @@ struct ast_channel *lcr_request(const char *type, int format, void *data, int *c if (lcr_sock < 0) { CERROR(NULL, NULL, "Rejecting call from Asterisk, because LCR not running.\n"); + ast_mutex_unlock(&chan_lock); return NULL; } @@ -1560,6 +1647,7 @@ struct ast_channel *lcr_request(const char *type, int format, void *data, int *c if (!call) { /* failed to create instance */ + ast_mutex_unlock(&chan_lock); return NULL; } @@ -1570,6 +1658,7 @@ struct ast_channel *lcr_request(const char *type, int format, void *data, int *c 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; @@ -1676,12 +1765,6 @@ static int lcr_call(struct ast_channel *ast, char *dest, int timeout) static int lcr_digit_begin(struct ast_channel *ast, char digit) { - printf("DIGT BEGIN %c\n", digit); - return (0); -} - -static int lcr_digit_end(struct ast_channel *ast, char digit, unsigned int duration) -{ struct chan_call *call; union parameter newparam; char buf[]="x"; @@ -1721,6 +1804,12 @@ static int lcr_digit_end(struct ast_channel *ast, char digit, unsigned int durat 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; @@ -1856,7 +1945,11 @@ static struct ast_frame *lcr_read(struct ast_channel *ast) return NULL; } if (call->pipe[0] > -1) { - len = read(call->pipe[0], call->read_buff, sizeof(call->read_buff)); + if (call->rebuffer) { + 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;