X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=chan_lcr.c;h=25515d03b6109042f6bf910452b45ec7189beac7;hp=e50847c53e4b99fdad1821896b00136841ed3c9b;hb=3342fc8abedd0df527776d6df3e14e90133f2951;hpb=03cb33b51da2596a2ef4373debe52ccde8e5cc7b diff --git a/chan_lcr.c b/chan_lcr.c index e50847c..25515d0 100644 --- a/chan_lcr.c +++ b/chan_lcr.c @@ -167,6 +167,7 @@ it is called from ast_channel process which has already locked ast_channel. #include "callerid.h" #include "lcrsocket.h" #include "cause.h" +#include "select.h" #include "bchannel.h" #include "options.h" #include "chan_lcr.h" @@ -198,11 +199,22 @@ static char *desc = "Channel driver for mISDN/LCR Support (Bri/Pri)"; pthread_t chan_tid; ast_mutex_t chan_lock; /* global lock */ ast_mutex_t log_lock; /* logging log */ +/* global_change: + * used to indicate change in file descriptors, so select function's result may + * be obsolete. + */ +int global_change = 0; +int wake_global = 0; +int wake_pipe[2]; +struct lcr_fd wake_fd; + int quit; int glob_channel = 0; int lcr_sock = -1; +struct lcr_fd socket_fd; +struct lcr_timer socket_retry; struct admin_list { struct admin_list *next; @@ -233,8 +245,9 @@ void chan_lcr_log(int type, const char *file, int line, const char *function, st if (ast) strncpy(ast_text, ast->name, sizeof(ast_text)-1); ast_text[sizeof(ast_text)-1] = '\0'; - - ast_log(type, file, line, function, "[call=%s ast=%s] %s", call_text, ast_text, buffer); + +// ast_log(type, file, line, function, "[call=%s ast=%s] %s", call_text, ast_text, buffer); + printf("[call=%s ast=%s] %s", call_text, ast_text, buffer); ast_mutex_unlock(&log_lock); } @@ -259,7 +272,7 @@ struct chan_call *find_call_ref(unsigned int ref) break; call = call->next; } - return(call); + return call; } void free_call(struct chan_call *call) @@ -289,6 +302,7 @@ void free_call(struct chan_call *call) ast_dsp_free(call->dsp); CDEBUG(call, NULL, "Call instance freed.\n"); free(call); + global_change = 1; return; } temp = &((*temp)->next); @@ -309,11 +323,11 @@ struct chan_call *alloc_call(void) if (pipe((*callp)->pipe) < 0) { CERROR(*callp, NULL, "Failed to create pipe.\n"); free_call(*callp); - return(NULL); + return NULL; } fcntl((*callp)->pipe[0], F_SETFL, O_NONBLOCK); CDEBUG(*callp, NULL, "Call instance allocated.\n"); - return(*callp); + return *callp; } unsigned short new_bridge_id(void) @@ -334,7 +348,7 @@ unsigned short new_bridge_id(void) id++; } CDEBUG(NULL, NULL, "New bridge ID %d.\n", id); - return(id); + return id; } /* @@ -364,8 +378,14 @@ int send_message(int message_type, unsigned int ref, union parameter *param) admin->msg.u.msg.type = message_type; admin->msg.u.msg.ref = ref; memcpy(&admin->msg.u.msg.param, param, sizeof(union parameter)); + socket_fd.when |= LCR_FD_WRITE; + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } - return(0); + return 0; } /* @@ -477,6 +497,14 @@ void apply_opt(struct chan_call *call, char *data) if (!call->nodsp) call->nodsp = 1; break; + case 'q': + if (opt[1] == '\0') { + CERROR(call, call->ast, "Option 'q' (queue) expects parameter.\n", opt); + break; + } + CDEBUG(call, call->ast, "Option 'q' (queue).\n"); + call->nodsp_queue = atoi(opt+1); + break; case 'e': if (opt[1] == '\0') { CERROR(call, call->ast, "Option 'e' (echo cancel) expects parameter.\n", opt); @@ -572,7 +600,7 @@ void apply_opt(struct chan_call *call, char *data) /* re-open, if bchannel is created */ if (call->bchannel && call->bchannel->b_sock > -1) { bchannel_destroy(call->bchannel); - if (bchannel_create(call->bchannel, ((call->nodsp || call->faxdetect > 0)?1:0) + ((call->hdlc)?2:0))) + if (bchannel_create(call->bchannel, ((call->nodsp || call->faxdetect > 0)?1:0) + ((call->hdlc)?2:0), call->nodsp_queue)) bchannel_activate(call->bchannel, 1); } } @@ -585,6 +613,7 @@ static void send_setup_to_lcr(struct chan_call *call) { union parameter newparam; struct ast_channel *ast = call->ast; +// const char *tmp; if (!call->ast || !call->ref) return; @@ -637,6 +666,10 @@ static void send_setup_to_lcr(struct chan_call *call) default: newparam.setup.callerinfo.ntype = INFO_NTYPE_UNKNOWN; } +#warning DISABLED DUE TO DOUBLE LOCKING PROBLEM +// tmp = pbx_builtin_getvar_helper(ast, "LCR_TRANSFERCAPABILITY"); +// if (tmp && *tmp) +// ast->transfercapability = atoi(tmp); newparam.setup.capainfo.bearer_capa = ast->transfercapability; newparam.setup.capainfo.bearer_mode = INFO_BMODE_CIRCUIT; if (call->hdlc) @@ -848,12 +881,15 @@ static void lcr_in_setup(struct chan_call *call, int message_type, union paramet strncpy(ast->context, param->setup.context, AST_MAX_CONTEXT-1); else strncpy(ast->context, param->setup.callerinfo.interface, AST_MAX_CONTEXT-1); + memset(&ast->cid, 0, sizeof(ast->cid)); if (param->setup.callerinfo.id[0]) ast->cid.cid_num = strdup(param->setup.callerinfo.id); + if (param->setup.callerinfo.id2[0]) + ast->cid.cid_ani = strdup(param->setup.callerinfo.id2); if (param->setup.callerinfo.name[0]) ast->cid.cid_name = strdup(param->setup.callerinfo.name); if (param->setup.redirinfo.id[0]) - ast->cid.cid_name = strdup(numberrize_callerinfo(param->setup.callerinfo.id, param->setup.callerinfo.ntype, options.national, options.international)); + ast->cid.cid_rdnis = strdup(numberrize_callerinfo(param->setup.redirinfo.id, param->setup.redirinfo.ntype, options.national, options.international)); switch (param->setup.callerinfo.present) { case INFO_PRESENT_ALLOWED: ast->cid.cid_pres = AST_PRES_ALLOWED; @@ -923,8 +959,15 @@ static void lcr_in_proceeding(struct chan_call *call, int message_type, union pa /* change state */ call->state = CHAN_LCR_STATE_OUT_PROCEEDING; /* queue event for asterisk */ - if (call->ast && call->pbx_started) + if (call->ast && call->pbx_started) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strncat(call->queue_string, "P", sizeof(call->queue_string)-1); + } + } /* @@ -937,8 +980,14 @@ static void lcr_in_alerting(struct chan_call *call, int message_type, union para /* change state */ call->state = CHAN_LCR_STATE_OUT_ALERTING; /* queue event to asterisk */ - if (call->ast && call->pbx_started) + if (call->ast && call->pbx_started) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strncat(call->queue_string, "R", sizeof(call->queue_string)-1); + } } /* @@ -962,8 +1011,14 @@ 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)); /* queue event to asterisk */ - if (call->ast && call->pbx_started) + if (call->ast && call->pbx_started) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strncat(call->queue_string, "N", sizeof(call->queue_string)-1); + } } /* @@ -997,9 +1052,14 @@ static void lcr_in_disconnect(struct chan_call *call, int message_type, union pa /* queue release asterisk */ if (ast) { ast->hangupcause = call->cause; - if (call->pbx_started) + if (call->pbx_started) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strcpy(call->queue_string, "H"); // overwrite other indications - else { + } else { ast_hangup(ast); // call will be destroyed here } } @@ -1026,9 +1086,14 @@ static void lcr_in_release(struct chan_call *call, int message_type, union param /* if we have an asterisk instance, queue hangup, else we are done */ if (ast) { ast->hangupcause = call->cause; - if (call->pbx_started) + if (call->pbx_started) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strcpy(call->queue_string, "H"); - else { + } else { ast_hangup(ast); // call will be destroyed here } } else { @@ -1064,8 +1129,14 @@ static void lcr_in_information(struct chan_call *call, int message_type, union p } /* queue digits */ - if (call->state == CHAN_LCR_STATE_IN_DIALING && param->information.id[0]) + if (call->state == CHAN_LCR_STATE_IN_DIALING && param->information.id[0]) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } 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) { @@ -1134,8 +1205,14 @@ static void lcr_in_pattern(struct chan_call *call, int message_type, union param send_message(MESSAGE_BCHANNEL, call->ref, &newparam); } /* queue PROGRESS, because tones are available */ - if (call->ast && call->pbx_started) + if (call->ast && call->pbx_started) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strncat(call->queue_string, "T", sizeof(call->queue_string)-1); + } } /* @@ -1159,6 +1236,11 @@ void lcr_in_dtmf(struct chan_call *call, int val) CDEBUG(call, call->ast, "Recognised DTMF digit '%c'.\n", val); digit[0] = val; digit[1] = '\0'; + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strncat(call->queue_string, digit, sizeof(call->queue_string)-1); } @@ -1180,13 +1262,13 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) 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); - return(-1); + return -1; } /* create bchannel */ bchannel = alloc_bchannel(param->bchannel.handle); if (!bchannel) { CERROR(NULL, NULL, "alloc bchannel handle %x failed.\n", (int)param->bchannel.handle); - return(-1); + return -1; } /* configure channel */ @@ -1222,7 +1304,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) bchannel_join(bchannel, call->bridge_id); } /* create only, if call exists, othewhise it bchannel is freed below... */ - if (bchannel_create(bchannel, ((call->nodsp || call->faxdetect > 0)?1:0) + ((call->hdlc)?2:0))) + if (bchannel_create(bchannel, ((call->nodsp || call->faxdetect > 0)?1:0) + ((call->hdlc)?2:0), call->nodsp_queue)) bchannel_activate(bchannel, 1); } /* acknowledge */ @@ -1242,7 +1324,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) CDEBUG(NULL, NULL, "Received BCHANNEL_REMOVE message. (handle=%08lx)\n", param->bchannel.handle); if (!(bchannel = find_bchannel_handle(param->bchannel.handle))) { CERROR(NULL, NULL, "Bchannel handle %x not assigned.\n", (int)param->bchannel.handle); - return(-1); + return -1; } /* unklink from call and destroy bchannel */ free_bchannel(bchannel); @@ -1257,7 +1339,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) default: CDEBUG(NULL, NULL, "Received unknown bchannel message %d.\n", param->bchannel.type); } - return(0); + return 0; } /* handle new ref */ @@ -1267,7 +1349,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) CDEBUG(NULL, NULL, "Received new ref by LCR, due to incomming call. (ref=%ld)\n", ref); if (!ref || find_call_ref(ref)) { CERROR(NULL, NULL, "Illegal new ref %ld received.\n", ref); - return(-1); + return -1; } /* allocate new call instance */ call = alloc_call(); @@ -1287,11 +1369,13 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) /* send release, if ref does not exist */ CDEBUG(NULL, NULL, "No call found, that requests a ref.\n"); send_release_and_import(call, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL); - return(0); + return 0; } /* store new ref */ call->ref = ref; call->ref_was_assigned = 1; + /* set dtmf (default, use option 'n' to disable */ + call->dsp_dtmf = 1; /* send pending setup info */ if (call->state == CHAN_LCR_STATE_OUT_PREPARE) send_setup_to_lcr(call); @@ -1304,22 +1388,22 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) send_release_and_import(call, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL); /* free call */ free_call(call); - return(0); + return 0; } } - return(0); + return 0; } /* check ref */ if (!ref) { CERROR(NULL, NULL, "Received message %d without ref.\n", message_type); - return(-1); + return -1; } call = find_call_ref(ref); if (!call) { /* ignore ref that is not used (anymore) */ CDEBUG(NULL, NULL, "Message %d from LCR ignored, because no call instance found.\n", message_type); - return(0); + return 0; } /* handle messages */ @@ -1380,7 +1464,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) CDEBUG(call, call->ast, "Message %d from LCR unhandled.\n", message_type); break; } - return(0); + return 0; } /* @@ -1413,6 +1497,11 @@ again: goto again; } CDEBUG(call, call->ast, "Queue call release, because Asterisk channel is running.\n"); + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strcpy(call->queue_string, "H"); call = call->next; } @@ -1422,69 +1511,74 @@ again: free_bchannel(bchannel_first); } +void close_socket(void); /* asterisk handler * warning! not thread safe * returns -1 for socket error, 0 for no work, 1 for work */ -int handle_socket(void) +static int handle_socket(struct lcr_fd *fd, unsigned int what, void *instance, int index) { - int work = 0; int len; struct admin_list *admin; struct admin_message msg; - /* read from socket */ - len = read(lcr_sock, &msg, sizeof(msg)); - if (len == 0) { - CERROR(NULL, NULL, "Socket closed.\n"); - return(-1); // socket closed - } - if (len > 0) { - if (len != sizeof(msg)) { - CERROR(NULL, NULL, "Socket short read. (len %d)\n", len); - return(-1); // socket error + if ((what & LCR_FD_READ)) { + /* read from socket */ + len = read(lcr_sock, &msg, sizeof(msg)); + if (len == 0) { + CERROR(NULL, NULL, "Socket closed.\n"); + error: + CERROR(NULL, NULL, "Handling of socket failed - closing for some seconds.\n"); + close_socket(); + release_all_calls(); + schedule_timer(&socket_retry, SOCKET_RETRY_TIMER, 0); + return 0; } - if (msg.message != ADMIN_MESSAGE) { - CERROR(NULL, NULL, "Socket received illegal message %d.\n", msg.message); - return(-1); - } - receive_message(msg.u.msg.type, msg.u.msg.ref, &msg.u.msg.param); - work = 1; - } else { - if (errno != EWOULDBLOCK) { + if (len > 0) { + if (len != sizeof(msg)) { + CERROR(NULL, NULL, "Socket short read. (len %d)\n", len); + goto error; + } + if (msg.message != ADMIN_MESSAGE) { + CERROR(NULL, NULL, "Socket received illegal message %d.\n", msg.message); + goto error; + } + receive_message(msg.u.msg.type, msg.u.msg.ref, &msg.u.msg.param); + } else { CERROR(NULL, NULL, "Socket failed (errno %d).\n", errno); - return(-1); + goto error; } } - /* write to socket */ - if (!admin_first) - return(work); - admin = admin_first; - len = write(lcr_sock, &admin->msg, sizeof(msg)); - if (len == 0) { - CERROR(NULL, NULL, "Socket closed.\n"); - return(-1); // socket closed - } - if (len > 0) { - if (len != sizeof(msg)) { - CERROR(NULL, NULL, "Socket short write. (len %d)\n", len); - return(-1); // socket error + if ((what & LCR_FD_WRITE)) { + /* write to socket */ + if (!admin_first) { + socket_fd.when &= ~LCR_FD_WRITE; + return 0; } - /* free head */ - admin_first = admin->next; - free(admin); - - work = 1; - } else { - if (errno != EWOULDBLOCK) { + admin = admin_first; + len = write(lcr_sock, &admin->msg, sizeof(msg)); + if (len == 0) { + CERROR(NULL, NULL, "Socket closed.\n"); + goto error; + } + if (len > 0) { + if (len != sizeof(msg)) { + CERROR(NULL, NULL, "Socket short write. (len %d)\n", len); + goto error; + } + /* free head */ + admin_first = admin->next; + free(admin); + global_change = 1; + } else { CERROR(NULL, NULL, "Socket failed (errno %d).\n", errno); - return(-1); + goto error; } } - return(work); + return 0; } /* @@ -1492,16 +1586,14 @@ int handle_socket(void) */ int open_socket(void) { - int ret; int conn; struct sockaddr_un sock_address; - unsigned int on = 1; union parameter param; /* open socket */ if ((lcr_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { CERROR(NULL, NULL, "Failed to create socket.\n"); - return(lcr_sock); + return lcr_sock; } /* set socket address and name */ @@ -1514,29 +1606,28 @@ int open_socket(void) close(lcr_sock); lcr_sock = -1; CDEBUG(NULL, NULL, "Failed to connect to socket '%s'. Is LCR running?\n", sock_address.sun_path); - return(conn); + return conn; } - /* set non-blocking io */ - if ((ret = ioctl(lcr_sock, FIONBIO, (unsigned char *)(&on))) < 0) { - close(lcr_sock); - lcr_sock = -1; - CERROR(NULL, NULL, "Failed to set socket into non-blocking IO.\n"); - return(ret); - } + /* register socket fd */ + memset(&socket_fd, 0, sizeof(socket_fd)); + socket_fd.fd = lcr_sock; + register_fd(&socket_fd, LCR_FD_READ | LCR_FD_EXCEPT, handle_socket, NULL, 0); /* enque hello message */ memset(¶m, 0, sizeof(param)); strcpy(param.hello.application, "asterisk"); send_message(MESSAGE_HELLO, 0, ¶m); - return(lcr_sock); + return lcr_sock; } void close_socket(void) { struct admin_list *admin, *temp; - + + unregister_fd(&socket_fd); + /* flush pending messages */ admin = admin_first; while(admin) { @@ -1550,173 +1641,158 @@ void close_socket(void) if (lcr_sock >= 0) close(lcr_sock); lcr_sock = -1; + global_change = 1; } /* sending queue to asterisk */ -static int queue_send(void) +static int wake_event(struct lcr_fd *fd, unsigned int what, void *instance, int index) +{ + char byte; + + read(wake_pipe[0], &byte, 1); + + wake_global = 0; + + return 0; +} + +static void handle_queue() { - int work = 0; struct chan_call *call; struct ast_channel *ast; struct ast_frame fr; char *p; +again: 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 'T': - CDEBUG(call, ast, "Sending queued PROGRESS to Asterisk.\n"); - ast_queue_control(ast, AST_CONTROL_PROGRESS); - break; - 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); - ast_setstate(ast, AST_STATE_RINGING); - break; - case 'N': - 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)); - - #ifdef LCR_FOR_ASTERISK - fr.frametype = AST_FRAME_DTMF_BEGIN; - #endif - - #ifdef LCR_FOR_CALLWEAVER - fr.frametype = AST_FRAME_DTMF; - #endif - - fr.subclass = *p; - fr.delivery = ast_tv(0, 0); - ast_queue_frame(ast, &fr); - - #ifdef LCR_FOR_ASTERISK - fr.frametype = AST_FRAME_DTMF_END; - ast_queue_frame(ast, &fr); - #endif - - break; - default: - CDEBUG(call, ast, "Ignoring queued digit 0x%02x.\n", *p); - } - p++; + if (ast_channel_trylock(ast)) { + ast_mutex_unlock(&chan_lock); + usleep(1); + ast_mutex_lock(&chan_lock); + goto again; + } + while(*p) { + switch (*p) { + case 'T': + CDEBUG(call, ast, "Sending queued PROGRESS to Asterisk.\n"); + ast_queue_control(ast, AST_CONTROL_PROGRESS); + break; + 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); + ast_setstate(ast, AST_STATE_RINGING); + break; + case 'N': + 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)); + + #ifdef LCR_FOR_ASTERISK + fr.frametype = AST_FRAME_DTMF_BEGIN; + #endif + + #ifdef LCR_FOR_CALLWEAVER + fr.frametype = AST_FRAME_DTMF; + #endif + + fr.subclass = *p; + fr.delivery = ast_tv(0, 0); + ast_queue_frame(ast, &fr); + + #ifdef LCR_FOR_ASTERISK + fr.frametype = AST_FRAME_DTMF_END; + ast_queue_frame(ast, &fr); + #endif + + break; + default: + CDEBUG(call, ast, "Ignoring queued digit 0x%02x.\n", *p); } - call->queue_string[0] = '\0'; - ast_channel_unlock(ast); - work = 1; + p++; } + call->queue_string[0] = '\0'; + ast_channel_unlock(ast); } call = call->next; } +} - return work; +static int handle_retry(struct lcr_timer *timer, void *instance, int index) +{ + CDEBUG(NULL, NULL, "Retry to open socket.\n"); + if (open_socket() < 0) + schedule_timer(&socket_retry, SOCKET_RETRY_TIMER, 0); + + return 0; } -/* signal handler */ -void sighandler(int sigset) +void lock_chan(void) { + ast_mutex_lock(&chan_lock); +} + +void unlock_chan(void) +{ + ast_mutex_unlock(&chan_lock); } /* chan_lcr thread */ static void *chan_thread(void *arg) { - int work; - int ret; - union parameter param; - time_t retry = 0, now; + if (pipe(wake_pipe) < 0) { + CERROR(NULL, NULL, "Failed to open pipe.\n"); + return NULL; + } + memset(&wake_fd, 0, sizeof(wake_fd)); + wake_fd.fd = wake_pipe[0]; + register_fd(&wake_fd, LCR_FD_READ, wake_event, NULL, 0); + + memset(&socket_retry, 0, sizeof(socket_retry)); + add_timer(&socket_retry, handle_retry, NULL, 0); bchannel_pid = getpid(); -// signal(SIGPIPE, sighandler); - - memset(¶m, 0, sizeof(union parameter)); - if (lcr_sock < 0) - time(&retry); + /* open socket the first time */ + handle_retry(NULL, NULL, 0); ast_mutex_lock(&chan_lock); while(!quit) { - work = 0; - - if (lcr_sock > 0) { - /* handle socket */ - ret = handle_socket(); - if (ret < 0) { - CERROR(NULL, NULL, "Handling of socket failed - closing for some seconds.\n"); - close_socket(); - release_all_calls(); - time(&retry); - } - if (ret) - work = 1; - } else { - time(&now); - if (retry && now-retry > 5) { - CDEBUG(NULL, NULL, "Retry to open socket.\n"); - retry = 0; - if (open_socket() < 0) { - time(&retry); - } - work = 1; - } - - } - - /* handle mISDN */ - 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); - - #ifdef LCR_FOR_ASTERISK - usleep(30000); - #endif - - #ifdef LCR_FOR_CALLWEAVER - usleep(20000); - #endif - - ast_mutex_lock(&chan_lock); - } + handle_queue(); + select_main(0, &global_change, lock_chan, unlock_chan); } close_socket(); + del_timer(&socket_retry); + + unregister_fd(&wake_fd); + close(wake_pipe[0]); + close(wake_pipe[1]); + CERROR(NULL, NULL, "Thread exit.\n"); - - ast_mutex_unlock(&chan_lock); -// signal(SIGPIPE, SIG_DFL); + ast_mutex_unlock(&chan_lock); return NULL; } @@ -1904,7 +1980,7 @@ static void send_digit_to_chan(struct ast_channel * ast, char digit ) ast_playtones_start(ast,0,dtmf_tones[15], 0); else { /* not handled */ - ast_log(LOG_DEBUG, "Unable to handle DTMF tone " + CDEBUG(NULL, ast, "Unable to handle DTMF tone " "'%c' for '%s'\n", digit, ast->name); } } @@ -1961,7 +2037,7 @@ static int lcr_digit(struct ast_channel *ast, char digit) ast_mutex_unlock(&chan_lock); #ifdef LCR_FOR_ASTERISK - return(0); + return 0; } static int lcr_digit_end(struct ast_channel *ast, char digit, unsigned int duration) @@ -1995,7 +2071,7 @@ static int lcr_digit_end(struct ast_channel *ast, char digit, unsigned int durat send_digit_to_chan(ast, digit); } - return (0); + return 0; } static int lcr_answer(struct ast_channel *ast) @@ -2044,13 +2120,15 @@ static int lcr_hangup(struct ast_channel *ast) struct chan_call *call; pthread_t tid = pthread_self(); - if (!pthread_equal(tid, chan_tid)) + if (!pthread_equal(tid, chan_tid)) { ast_mutex_lock(&chan_lock); + } call = ast->tech_pvt; if (!call) { CERROR(NULL, ast, "Received hangup from Asterisk, but no call instance exists.\n"); - if (!pthread_equal(tid, chan_tid)) + if (!pthread_equal(tid, chan_tid)) { ast_mutex_unlock(&chan_lock); + } return -1; } @@ -2071,8 +2149,9 @@ static int lcr_hangup(struct ast_channel *ast) send_release_and_import(call, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL); /* remove call */ free_call(call); - if (!pthread_equal(tid, chan_tid)) + if (!pthread_equal(tid, chan_tid)) { ast_mutex_unlock(&chan_lock); + } return 0; } else { /* ref is not set, due to prepare setup or release */ @@ -2087,8 +2166,9 @@ static int lcr_hangup(struct ast_channel *ast) call->ast = NULL; } } - if (!pthread_equal(tid, chan_tid)) + if (!pthread_equal(tid, chan_tid)) { ast_mutex_unlock(&chan_lock); + } return 0; } @@ -2117,7 +2197,7 @@ static int lcr_write(struct ast_channel *ast, struct ast_frame *f) static struct ast_frame *lcr_read(struct ast_channel *ast) { struct chan_call *call; - int len; + int len = 0; ast_mutex_lock(&chan_lock); call = ast->tech_pvt; @@ -2150,6 +2230,7 @@ static struct ast_frame *lcr_read(struct ast_channel *ast) if (len <= 0) { close(call->pipe[0]); call->pipe[0] = -1; + global_change = 1; ast_mutex_unlock(&chan_lock); return NULL; } else if (call->rebuffer && call->framepos < 160) { @@ -2181,6 +2262,7 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz union parameter newparam; int res = 0; struct chan_call *call; + const struct tone_zone_sound *ts = NULL; ast_mutex_lock(&chan_lock); call = ast->tech_pvt; @@ -2202,6 +2284,9 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz send_message(MESSAGE_DISCONNECT, call->ref, &newparam); /* change state */ call->state = CHAN_LCR_STATE_OUT_DISCONNECT; + } else { + CDEBUG(call, ast, "Using Asterisk 'busy' indication\n"); + ts = ast_get_indication_tone(ast->zone, "busy"); } break; case AST_CONTROL_CONGESTION: @@ -2214,6 +2299,9 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz send_message(MESSAGE_DISCONNECT, call->ref, &newparam); /* change state */ call->state = CHAN_LCR_STATE_OUT_DISCONNECT; + } else { + CDEBUG(call, ast, "Using Asterisk 'congestion' indication\n"); + ts = ast_get_indication_tone(ast->zone, "congestion"); } break; case AST_CONTROL_PROCEEDING: @@ -2238,6 +2326,9 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz send_message(MESSAGE_ALERTING, call->ref, &newparam); /* change state */ call->state = CHAN_LCR_STATE_IN_ALERTING; + } else { + CDEBUG(call, ast, "Using Asterisk 'ring' indication\n"); + ts = ast_get_indication_tone(ast->zone, "ring"); } break; case AST_CONTROL_PROGRESS: @@ -2252,6 +2343,7 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz break; case -1: CDEBUG(call, ast, "Received indicate -1.\n"); + ast_playtones_stop(ast); res = -1; break; @@ -2290,15 +2382,21 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz break; #ifdef AST_CONTROL_SRCUPDATE case AST_CONTROL_SRCUPDATE: +#else + case 20: +#endif CDEBUG(call, ast, "Received AST_CONTROL_SRCUPDATE from Asterisk.\n"); break; -#endif default: CERROR(call, ast, "Received indicate from Asterisk with unknown condition %d.\n", cond); res = -1; break; } + if (ts && ts->data[0]) { + ast_playtones_start(ast, 0, ts->data, 1); + } + /* return */ ast_mutex_unlock(&chan_lock); return res; @@ -2349,7 +2447,7 @@ static int lcr_send_text(struct ast_channel *ast, const char *text) memset(&newparam, 0, sizeof(union parameter)); strncpy(newparam.notifyinfo.display, text, sizeof(newparam.notifyinfo.display)-1); send_message(MESSAGE_NOTIFY, call->ref, &newparam); - ast_mutex_lock(&chan_lock); + ast_mutex_unlock(&chan_lock); return 0; } @@ -2680,13 +2778,14 @@ static int lcr_config_exec(struct ast_channel *ast, void *data, char **argv) int load_module(void) { u_short i; + char options_error[256]; for (i = 0; i < 256; i++) { flip_bits[i] = (i>>7) | ((i>>5)&2) | ((i>>3)&4) | ((i>>1)&8) | (i<<7) | ((i&2)<<5) | ((i&4)<<3) | ((i&8)<<1); } - if (read_options() == 0) { + if (read_options(options_error) == 0) { CERROR(NULL, NULL, "%s", options_error); #ifdef LCR_FOR_ASTERISK @@ -2702,10 +2801,6 @@ int load_module(void) ast_mutex_init(&chan_lock); ast_mutex_init(&log_lock); - if (open_socket() < 0) { - /* continue with closed socket */ - } - if (bchannel_initialize()) { CERROR(NULL, NULL, "Unable to open mISDN device\n"); close_socket(); @@ -2752,6 +2847,8 @@ int load_module(void) " n - Don't detect dtmf tones on called channel.\n" " h - Force data call (HDLC).\n" " t - Disable mISDN_dsp features (required for fax application).\n" + " q - Add queue to make fax stream seamless (required for fax app).\n" + " Use queue size in miliseconds for optarg. (try 250)\n" " f - Adding fax detection. It it timeouts, mISDN_dsp is used.\n" " Use time to detect for optarg.\n" " c - Make crypted outgoing call, optarg is keyindex.\n" @@ -2763,6 +2860,12 @@ int load_module(void) " vt - txgain control\n" " Volume changes at factor 2 ^ optarg.\n" " k - use keypad to dial this call.\n" + "\n" + "set LCR_TRANSFERCAPABILITY to the numerical bearer capabilty in order to alter caller's capability\n" + " -> use 16 for fax (3.1k audio)\n" + "\n" + "To send a fax, you need to set LCR_TRANSFERCAPABILITY environment to 16, also you need to set\n" + "options: \"n:t:q250\" for seamless audio transmission.\n" ); @@ -2833,6 +2936,7 @@ int reload_module(void) #ifdef LCR_FOR_CALLWEAVER int usecount(void) +hae { int res; ast_mutex_lock(&usecnt_lock);