X-Git-Url: http://git.eversberg.eu/gitweb.cgi?a=blobdiff_plain;f=chan_lcr.c;h=00c27f862519d9a51a285ff9e172b7585a6775c7;hb=20a671d76854520ad9d5ea9d481e008240465e62;hp=53a7fb61881bd1f6a51fa3ccfb328ae6e4267126;hpb=bf61575a94ec0e4e02824b7bfe8b1063ddf3daa5;p=lcr.git diff --git a/chan_lcr.c b/chan_lcr.c index 53a7fb6..00c27f8 100644 --- a/chan_lcr.c +++ b/chan_lcr.c @@ -105,6 +105,11 @@ it is called from ast_channel process which has already locked ast_channel. */ + +/* Choose if you want to have chan_lcr for Asterisk 1.4.x or CallWeaver 1.2.x */ +#define LCR_FOR_ASTERISK +/* #define LCR_FOR_CALLWEAVER */ + #include #include #include @@ -124,9 +129,13 @@ it is called from ast_channel process which has already locked ast_channel. #define HAVE_ATTRIBUTE_always_inline 1 #define HAVE_ARPA_INET_H 1 #define HAVE_TIMERSUB 1 +#define HAVE_STRTOQ 1 +#define HAVE_INET_ATON 1 #include +#ifdef LCR_FOR_ASTERISK #include +#endif #include #include #include @@ -141,7 +150,13 @@ it is called from ast_channel process which has already locked ast_channel. #include #include #include +#ifdef LCR_FOR_ASTERISK #include +#endif +#ifdef LCR_FOR_CALLWEAVER +#include +#endif + #include #include #include @@ -159,13 +174,27 @@ it is called from ast_channel process which has already locked ast_channel. CHAN_LCR_STATE // state description structure MESSAGES // message text +#ifdef LCR_FOR_CALLWEAVER +AST_MUTEX_DEFINE_STATIC(rand_lock); +#endif + unsigned char flip_bits[256]; +#ifdef LCR_FOR_CALLWEAVER +static struct ast_frame nullframe = { AST_FRAME_NULL, }; +#endif + int lcr_debug=1; int mISDN_created=1; char lcr_type[]="lcr"; +#ifdef LCR_FOR_CALLWEAVER +static ast_mutex_t usecnt_lock; +static int usecnt=0; +static char *desc = "Channel driver for mISDN/LCR Support (Bri/Pri)"; +#endif + pthread_t chan_tid; ast_mutex_t chan_lock; /* global lock */ ast_mutex_t log_lock; /* logging log */ @@ -259,6 +288,10 @@ void free_call(struct chan_call *call) CERROR(call, NULL, "Linked call structure has no link to us.\n"); call->bridge_call->bridge_call = NULL; } + if (call->trans) + ast_translator_free_path(call->trans); + if (call->dsp) + ast_dsp_free(call->dsp); CDEBUG(call, NULL, "Call instance freed.\n"); free(call); return; @@ -349,7 +382,7 @@ void apply_opt(struct chan_call *call, char *data) { union parameter newparam; char string[1024], *p = string, *opt, *key; - int gain, i, newmode = 0; + int gain, i; if (!data[0]) return; // no opts @@ -437,10 +470,8 @@ void apply_opt(struct chan_call *call, char *data) break; } CDEBUG(call, call->ast, "Option 'h' (HDLC).\n"); - if (!call->hdlc) { + if (!call->hdlc) call->hdlc = 1; - newmode = 1; - } break; case 't': if (opt[1] != '\0') { @@ -448,10 +479,8 @@ void apply_opt(struct chan_call *call, char *data) break; } CDEBUG(call, call->ast, "Option 't' (no dsp).\n"); - if (!call->nodsp) { + if (!call->nodsp) call->nodsp = 1; - newmode = 1; - } break; case 'e': if (opt[1] == '\0') { @@ -463,6 +492,36 @@ void apply_opt(struct chan_call *call, char *data) if (call->bchannel) bchannel_pipeline(call->bchannel, call->pipeline); break; + case 'f': + if (opt[1] == '\0') { + CERROR(call, call->ast, "Option 'f' (faxdetect) expects parameter.\n", opt); + break; + } + call->faxdetect=atoi(opt+1); + if (!call->dsp) + call->dsp=ast_dsp_new(); + if (call->dsp) { + #ifdef LCR_FOR_CALLWEAVER + ast_dsp_set_features(call->dsp, DSP_FEATURE_DTMF_DETECT| DSP_FEATURE_FAX_CNG_DETECT); + #endif + #ifdef LCR_FOR_ASTERISK + #ifdef DSP_FEATURE_DTMF_DETECT + ast_dsp_set_features(call->dsp, DSP_FEATURE_DTMF_DETECT| DSP_FEATURE_FAX_DETECT); + #else + ast_dsp_set_features(call->dsp, DSP_FEATURE_DIGIT_DETECT| DSP_FEATURE_FAX_DETECT); + #endif + + #endif + if (!call->trans) + #ifdef LCR_FOR_CALLWEAVER + call->trans=ast_translator_build_path(AST_FORMAT_SLINEAR, 8000, (options.law=='a')?AST_FORMAT_ALAW:AST_FORMAT_ULAW, 8000); + #endif + #ifdef LCR_FOR_ASTERISK + call->trans=ast_translator_build_path(AST_FORMAT_SLINEAR, (options.law=='a')?AST_FORMAT_ALAW:AST_FORMAT_ULAW); + #endif + } + CDEBUG(call, call->ast, "Option 'f' (faxdetect) with config '%s'.\n", call->faxdetect); + break; case 'r': if (opt[1] != '\0') { CERROR(call, call->ast, "Option 'r' (re-buffer 160 bytes) expects no parameter.\n", opt); @@ -470,6 +529,7 @@ void apply_opt(struct chan_call *call, char *data) } CDEBUG(call, call->ast, "Option 'r' (re-buffer 160 bytes)"); call->rebuffer = 1; + call->framepos = 0; break; case 's': if (opt[1] != '\0') { @@ -508,7 +568,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)?1:0) + ((call->hdlc)?2:0))) + if (bchannel_create(call->bchannel, ((call->nodsp || call->faxdetect > 0)?1:0) + ((call->hdlc)?2:0))) bchannel_activate(call->bchannel, 1); } } @@ -730,6 +790,12 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c start: /* send setup to asterisk */ CDEBUG(call, ast, "Starting call to Asterisk due to matching extension.\n"); + + #ifdef LCR_FOR_CALLWEAVER + ast->type = "LCR"; + snprintf(ast->name, sizeof(ast->name), "LCR/%s-%04x",ast->cid.cid_num, ast_random() & 0xffff); + #endif + ret = ast_pbx_start(ast); if (ret < 0) { @@ -750,7 +816,15 @@ static void lcr_in_setup(struct chan_call *call, int message_type, union paramet CDEBUG(call, NULL, "Incomming setup from LCR. (callerid %s, dialing %s)\n", param->setup.callerinfo.id, param->setup.dialinginfo.id); /* create asterisk channel instrance */ + + #ifdef LCR_FOR_CALLWEAVER + ast = ast_channel_alloc(1); + #endif + + #ifdef LCR_FOR_ASTERISK ast = ast_channel_alloc(1, AST_STATE_RESERVED, NULL, NULL, "", NULL, "", 0, "%s/%d", lcr_type, ++glob_channel); + #endif + if (!ast) { /* release */ @@ -890,7 +964,7 @@ static void lcr_in_connect(struct chan_call *call, int message_type, union param memcpy(&call->connectinfo, ¶m->connectinfo, sizeof(struct connect_info)); /* queue event to asterisk */ if (call->ast && call->pbx_started) - strncat(call->queue_string, "A", sizeof(call->queue_string)-1); + strncat(call->queue_string, "N", sizeof(call->queue_string)-1); } /* @@ -1044,6 +1118,34 @@ static void lcr_in_facility(struct chan_call *call, int message_type, union para } /* + * incoming pattern from LCR + */ +static void lcr_in_pattern(struct chan_call *call, int message_type, union parameter *param) +{ + union parameter newparam; + + CDEBUG(call, call->ast, "Incomming pattern indication from LCR.\n"); + + if (!call->ast) return; + + /* pattern are indicated only once */ + if (call->has_pattern) + return; + call->has_pattern = 1; + + /* request bchannel */ + if (!call->bchannel) { + CDEBUG(call, call->ast, "Requesting B-channel.\n"); + memset(&newparam, 0, sizeof(union parameter)); + newparam.bchannel.type = BCHANNEL_REQUEST; + send_message(MESSAGE_BCHANNEL, call->ref, &newparam); + } + /* queue PROGRESS, because tones are available */ + if (call->ast && call->pbx_started) + strncat(call->queue_string, "T", sizeof(call->queue_string)-1); +} + +/* * got dtmf from bchannel (locked state) */ void lcr_in_dtmf(struct chan_call *call, int val) @@ -1129,7 +1231,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)?1:0) + ((call->hdlc)?2:0))) + if (bchannel_create(bchannel, ((call->nodsp || call->faxdetect > 0)?1:0) + ((call->hdlc)?2:0))) bchannel_activate(bchannel, 1); } /* acknowledge */ @@ -1280,6 +1382,8 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) break; case MESSAGE_PATTERN: // audio available from LCR + if (!call->has_pattern) + lcr_in_pattern(call, message_type, param); break; case MESSAGE_NOPATTERN: // audio not available from LCR @@ -1498,6 +1602,10 @@ static int queue_send(void) 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); @@ -1507,7 +1615,7 @@ static int queue_send(void) ast_queue_control(ast, AST_CONTROL_RINGING); ast_setstate(ast, AST_STATE_RINGING); break; - case 'A': + case 'N': CDEBUG(call, ast, "Sending queued ANSWER to Asterisk.\n"); ast_queue_control(ast, AST_CONTROL_ANSWER); break; @@ -1515,22 +1623,34 @@ static int queue_send(void) 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': + 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%02d.\n", *p); + CDEBUG(call, ast, "Ignoring queued digit 0x%02x.\n", *p); } p++; } @@ -1608,7 +1728,15 @@ static void *chan_thread(void *arg) /* 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); } } @@ -1655,7 +1783,15 @@ struct ast_channel *lcr_request(const char *type, int format, void *data, int *c } /* create asterisk channel instrance */ + + #ifdef LCR_FOR_ASTERISK ast = ast_channel_alloc(1, AST_STATE_RESERVED, NULL, NULL, "", NULL, "", 0, "%s/%d", lcr_type, ++glob_channel); + #endif + + #ifdef LCR_FOR_CALLWEAVER + ast = ast_channel_alloc(1); + #endif + if (!ast) { CERROR(NULL, NULL, "Failed to create Asterisk channel.\n"); @@ -1720,6 +1856,12 @@ static int lcr_call(struct ast_channel *ast, char *dest, int timeout) ast_mutex_lock(&chan_lock); call = ast->tech_pvt; + + #ifdef LCR_FOR_CALLWEAVER + ast->type = "LCR"; + snprintf(ast->name, sizeof(ast->name), "LCR/%s-%04x",call->dialstring, ast_random() & 0xffff); + #endif + if (!call) { CERROR(NULL, ast, "Received call from Asterisk, but call instance does not exist.\n"); ast_mutex_unlock(&chan_lock); @@ -1801,13 +1943,21 @@ static void send_digit_to_chan(struct ast_channel * ast, char digit ) } } - +#ifdef LCR_FOR_ASTERISK static int lcr_digit_begin(struct ast_channel *ast, char digit) +#endif +#ifdef LCR_FOR_CALLWEAVER +static int lcr_digit(struct ast_channel *ast, char digit) +#endif { struct chan_call *call; union parameter newparam; char buf[]="x"; +#ifdef LCR_FOR_CALLWEAVER + int inband_dtmf = 0; +#endif + /* only pass IA5 number space */ if (digit > 126 || digit < 32) return 0; @@ -1840,6 +1990,8 @@ static int lcr_digit_begin(struct ast_channel *ast, char digit) } ast_mutex_unlock(&chan_lock); + +#ifdef LCR_FOR_ASTERISK return(0); } @@ -1847,6 +1999,7 @@ static int lcr_digit_end(struct ast_channel *ast, char digit, unsigned int durat { int inband_dtmf = 0; struct chan_call *call; +#endif ast_mutex_lock(&chan_lock); @@ -1995,7 +2148,7 @@ static int lcr_write(struct ast_channel *ast, struct ast_frame *f) return -1; } if (call->bchannel && f->samples) - bchannel_transmit(call->bchannel, f->data, f->samples); + bchannel_transmit(call->bchannel, *((unsigned char **)&(f->data)), f->samples); ast_mutex_unlock(&chan_lock); return 0; } @@ -2014,28 +2167,50 @@ static struct ast_frame *lcr_read(struct ast_channel *ast) } if (call->pipe[0] > -1) { if (call->rebuffer && !call->hdlc) { - len = read(call->pipe[0], call->read_buff, 160); + /* Make sure we have a complete 20ms (160byte) frame */ + len=read(call->pipe[0],call->read_buff + call->framepos, 160 - call->framepos); + if (len > 0) { + call->framepos += len; + } } else { len = read(call->pipe[0], call->read_buff, sizeof(call->read_buff)); } if (len < 0 && errno == EAGAIN) { ast_mutex_unlock(&chan_lock); + + #ifdef LCR_FOR_ASTERISK return &ast_null_frame; + #endif + + #ifdef LCR_FOR_CALLWEAVER + return &nullframe; + #endif + } if (len <= 0) { close(call->pipe[0]); call->pipe[0] = -1; ast_mutex_unlock(&chan_lock); return NULL; + } else if (call->rebuffer && call->framepos < 160) { + /* Not a complete frame, so we send a null-frame */ + ast_mutex_unlock(&chan_lock); + return &ast_null_frame; } } call->read_fr.frametype = AST_FRAME_VOICE; call->read_fr.subclass = ast->nativeformats; - call->read_fr.datalen = len; - call->read_fr.samples = len; + if (call->rebuffer) { + call->read_fr.datalen = call->framepos; + call->read_fr.samples = call->framepos; + call->framepos = 0; + } else { + call->read_fr.datalen = len; + call->read_fr.samples = len; + } call->read_fr.delivery = ast_tv(0,0); - call->read_fr.data = call->read_buff; + *((unsigned char **)&(call->read_fr.data)) = call->read_buff; ast_mutex_unlock(&chan_lock); return &call->read_fr; @@ -2105,6 +2280,16 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz call->state = CHAN_LCR_STATE_IN_ALERTING; } break; + case AST_CONTROL_PROGRESS: + CDEBUG(call, ast, "Received indicate AST_CONTROL_PROGRESS from Asterisk.\n"); + /* request bchannel */ + if (!call->bchannel) { + CDEBUG(call, ast, "Requesting B-channel.\n"); + memset(&newparam, 0, sizeof(union parameter)); + newparam.bchannel.type = BCHANNEL_REQUEST; + send_message(MESSAGE_BCHANNEL, call->ref, &newparam); + } + break; case -1: CDEBUG(call, ast, "Received indicate -1.\n"); res = -1; @@ -2122,7 +2307,14 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz send_message(MESSAGE_NOTIFY, call->ref, &newparam); /*start music onhold*/ + #ifdef LCR_FOR_ASTERISK ast_moh_start(ast,data,ast->musicclass); + #endif + + #ifdef LCR_FOR_CALLWEAVER + ast_moh_start(ast, NULL); + #endif + call->on_hold = 1; break; case AST_CONTROL_UNHOLD: @@ -2231,7 +2423,9 @@ enum ast_bridge_result lcr_bridge(struct ast_channel *ast1, return AST_BRIDGE_COMPLETE; } - /* join, if both call instances uses dsp */ + /* join, if both call instances uses dsp + ignore the case of fax detection here it may be benificial for ISDN fax machines or pass through. + */ if (!call1->nodsp && !call2->nodsp) { CDEBUG(NULL, NULL, "Both calls use DSP, bridging via DSP.\n"); @@ -2362,20 +2556,29 @@ enum ast_bridge_result lcr_bridge(struct ast_channel *ast1, } static struct ast_channel_tech lcr_tech = { .type="LCR", - .description="Channel driver for connecting to Linux-Call-Router", - .requester=lcr_request, - .send_digit_begin=lcr_digit_begin, - .send_digit_end=lcr_digit_end, - .call=lcr_call, - .bridge=lcr_bridge, - .hangup=lcr_hangup, - .answer=lcr_answer, - .read=lcr_read, - .write=lcr_write, - .indicate=lcr_indicate, - .fixup=lcr_fixup, - .send_text=lcr_send_text, - .properties=0 + .description = "Channel driver for connecting to Linux-Call-Router", + .capabilities = AST_FORMAT_ALAW, + .requester = lcr_request, + + #ifdef LCR_FOR_ASTERISK + .send_digit_begin = lcr_digit_begin, + .send_digit_end = lcr_digit_end, + #endif + + #ifdef LCR_FOR_CALLWEAVER + .send_digit = lcr_digit, + #endif + + .call = lcr_call, + .bridge = lcr_bridge, + .hangup = lcr_hangup, + .answer = lcr_answer, + .read = lcr_read, + .write = lcr_write, + .indicate = lcr_indicate, + .fixup = lcr_fixup, + .send_text = lcr_send_text, + .properties = 0 }; @@ -2469,13 +2672,26 @@ static struct ast_cli_entry cli_port_unload = #endif - +#ifdef LCR_FOR_ASTERISK static int lcr_config_exec(struct ast_channel *ast, void *data) +#endif + +#ifdef LCR_FOR_CALLWEAVER +static int lcr_config_exec(struct ast_channel *ast, void *data, char **argv) +#endif { struct chan_call *call; ast_mutex_lock(&chan_lock); + + #ifdef LCR_FOR_ASTERISK CDEBUG(NULL, ast, "Received lcr_config (data=%s)\n", (char *)data); + #endif + + #ifdef LCR_FOR_CALLWEAVER + CDEBUG(NULL, ast, "Received lcr_config (data=%s)\n", argv[0]); + #endif + /* find channel */ call = call_first; while(call) { @@ -2484,7 +2700,15 @@ static int lcr_config_exec(struct ast_channel *ast, void *data) call = call->next; } if (call) + + #ifdef LCR_FOR_ASTERISK apply_opt(call, (char *)data); + #endif + + #ifdef LCR_FOR_CALLWEAVER + apply_opt(call, (char *)argv[0]); + #endif + else CERROR(NULL, ast, "lcr_config app not called by chan_lcr channel.\n"); @@ -2506,7 +2730,15 @@ int load_module(void) if (read_options() == 0) { CERROR(NULL, NULL, "%s", options_error); + + #ifdef LCR_FOR_ASTERISK return AST_MODULE_LOAD_DECLINE; + #endif + + #ifdef LCR_FOR_CALLWEAVER + return 0; + #endif + } ast_mutex_init(&chan_lock); @@ -2519,7 +2751,14 @@ int load_module(void) if (bchannel_initialize()) { CERROR(NULL, NULL, "Unable to open mISDN device\n"); close_socket(); + + #ifdef LCR_FOR_ASTERISK return AST_MODULE_LOAD_DECLINE; + #endif + + #ifdef LCR_FOR_CALLWEAVER + return 0; + #endif } mISDN_created = 1; @@ -2528,11 +2767,26 @@ int load_module(void) CERROR(NULL, NULL, "Unable to register channel class\n"); bchannel_deinitialize(); close_socket(); + + #ifdef LCR_FOR_ASTERISK return AST_MODULE_LOAD_DECLINE; + #endif + + #ifdef LCR_FOR_CALLWEAVER + return 0; + #endif } ast_register_application("lcr_config", lcr_config_exec, "lcr_config", + + #ifdef LCR_FOR_ASTERISK "lcr_config(::...)\n" + #endif + + #ifdef LCR_FOR_CALLWEAVER + "lcr_config(::...)\n", + #endif + "Sets LCR opts. and optargs\n" "\n" "The available options are:\n" @@ -2540,10 +2794,13 @@ 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" + " 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" " e - Perform echo cancelation on this channel.\n" " Takes mISDN pipeline option as optarg.\n" " s - Send Non Inband DTMF as inband.\n" + " r - re-buffer packets (160 bytes). Required for some SIP-phones and fax applications.\n" " vr - rxgain control\n" " vt - txgain control\n" " Volume changes at factor 2 ^ optarg.\n" @@ -2567,7 +2824,15 @@ int load_module(void) bchannel_deinitialize(); close_socket(); ast_channel_unregister(&lcr_tech); + + #ifdef LCR_FOR_ASTERISK return AST_MODULE_LOAD_DECLINE; + #endif + + #ifdef LCR_FOR_CALLWEAVER + return 0; + #endif + } return 0; } @@ -2604,12 +2869,32 @@ int reload_module(void) return 0; } - +#ifdef LCR_FOR_ASTERISK #define AST_MODULE "chan_lcr" +#endif +#ifdef LCR_FOR_CALLWEAVER +int usecount(void) +{ + int res; + ast_mutex_lock(&usecnt_lock); + res = usecnt; + ast_mutex_unlock(&usecnt_lock); + return res; +} +#endif + +#ifdef LCR_FOR_ASTERISK AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Channel driver for Linux-Call-Router Support (ISDN BRI/PRI)", .load = load_module, .unload = unload_module, .reload = reload_module, ); +#endif +#ifdef LCR_FOR_CALLWEAVER +char *description(void) +{ + return desc; +} +#endif