X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=chan_lcr.c;h=e50847c53e4b99fdad1821896b00136841ed3c9b;hp=e09004c4bbae0ed7abd48af9d85b9b65e2541a00;hb=3ac6881c22bce18091f19b06958ac66016bf9a32;hpb=2ac0ed4fc437dd23589640f2e48f6dc38ee3c172 diff --git a/chan_lcr.c b/chan_lcr.c index e09004c..e50847c 100644 --- a/chan_lcr.c +++ b/chan_lcr.c @@ -21,8 +21,9 @@ Now the channel driver is linked to LCR and can receive and make calls. Call is initiated by LCR: If a call is received from LCR, a MESSAGE_NEWREF is received first. +The ref_was_assigned ist set to 1. A new chan_call instance is created. The call reference (ref) is given by -MESSAGE_NEWREF. The state is CHAN_LCR_STATE_IN_PREPARE. +the received MESSAGE_NEWREF. The state is CHAN_LCR_STATE_IN_PREPARE. After receiving MESSAGE_SETUP from LCR, the ast_channel instance is created using ast_channel_alloc(1). The setup information is given to asterisk. The new Asterisk instance pointer (ast) is stored to chan_call structure. @@ -34,8 +35,9 @@ Call is initiated by Asterisk: If a call is requested from Asterisk, a new chan_call instance is created. The new Asterisk instance pointer (ast) is stored to chan_call structure. The current call ref is set to 0, the state is CHAN_LCR_STATE_OUT_PREPARE. -If the call is received (lcr_call) A MESSASGE_NEWREF is sent to LCR requesting +If the call is received (lcr_call) A MESSAGE_NEWREF is sent to LCR requesting a new call reference (ref). +The ref_was_assigned ist set to 1. Further dialing information is queued. After the new callref is received by special MESSAGE_NEWREF reply, new ref is stored in the chan_call structure. @@ -57,6 +59,7 @@ Call is released by LCR: A MESSAGE_RELEASE is received with the call reference (ref) to be released. The current ref is set to 0, to indicate released reference. +The ref_was_assigned==1 shows that there is no other ref to be assigned. The state changes to CHAN_LCR_STATE_RELEASE. ast_queue_hangup() is called, if asterisk instance (ast) exists, if not, the chan_call instance is destroyed. @@ -73,6 +76,7 @@ lcr_hangup() is called-back by Asterisk. If the call reference (ref) is set, a MESSAGE_RELEASE is sent to LCR and the chan_call instance is destroyed. If the ref is 0 and the state is not CHAN_LCR_STATE_RELEASE, the new state is set to CHAN_LCR_STATE_RELEASE. +The ref_was_assigned==0 shows that a ref is still requested. Later, if the MESSAGE_NEWREF reply is received, a MESSAGE_RELEASE is sent to LCR and the chan_call instance is destroyed. If the ref is 0 and the state is CHAN_LCR_STATE_RELEASE, see the proceedure @@ -101,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 @@ -117,6 +126,16 @@ it is called from ast_channel process which has already locked ast_channel. #include +#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 @@ -131,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 @@ -149,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 */ @@ -205,72 +244,49 @@ void chan_lcr_log(int type, const char *file, int line, const char *function, st */ struct chan_call *call_first; -struct chan_call *find_call_ref(unsigned int ref) -{ - struct chan_call *call = call_first; - - while(call) - { - if (call->ref == ref) - break; - call = call->next; - } - return(call); -} - -#if 0 -struct chan_call *find_call_ast(struct ast_channel *ast) -{ - struct chan_call *call = call_first; - - while(call) - { - if (call->ast == ast) - break; - call = call->next; - } - return(call); -} +/* + * find call by ref + * special case: 0: find new ref, that has not been assigned a ref yet + */ -struct chan_call *find_call_handle(unsigned int handle) +struct chan_call *find_call_ref(unsigned int ref) { struct chan_call *call = call_first; - - while(call) - { - if (call->bchannel_handle == handle) + int assigned = (ref > 0); + + while(call) { + if (call->ref == ref && call->ref_was_assigned == assigned) break; call = call->next; } return(call); } -#endif void free_call(struct chan_call *call) { struct chan_call **temp = &call_first; - while(*temp) - { - if (*temp == call) - { + while(*temp) { + if (*temp == call) { *temp = (*temp)->next; if (call->pipe[0] > -1) close(call->pipe[0]); if (call->pipe[1] > -1) close(call->pipe[1]); - if (call->bchannel) - { + if (call->bchannel) { if (call->bchannel->call != call) CERROR(call, NULL, "Linked bchannel structure has no link to us.\n"); call->bchannel->call = NULL; } - if (call->bridge_call) - { + if (call->bridge_call) { if (call->bridge_call->bridge_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; @@ -295,22 +311,20 @@ struct chan_call *alloc_call(void) free_call(*callp); return(NULL); } + fcntl((*callp)->pipe[0], F_SETFL, O_NONBLOCK); CDEBUG(*callp, NULL, "Call instance allocated.\n"); return(*callp); } - unsigned short new_bridge_id(void) { struct chan_call *call; unsigned short id = 1; /* search for lowest bridge id that is not in use and not 0 */ - while(id) - { + while(id) { call = call_first; - while(call) - { + while(call) { if (call->bridge_id == id) break; call = call->next; @@ -361,7 +375,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 @@ -370,8 +384,7 @@ void apply_opt(struct chan_call *call, char *data) string[sizeof(string)-1] = '\0'; /* parse options */ - while((opt = strsep(&p, ":"))) - { + while((opt = strsep(&p, ":"))) { switch(opt[0]) { case 'd': if (opt[1] == '\0') { @@ -393,7 +406,11 @@ void apply_opt(struct chan_call *call, char *data) break; } CDEBUG(call, call->ast, "Option 'n' (no DTMF).\n"); - call->no_dtmf = 1; + if (call->dsp_dtmf) { + call->dsp_dtmf = 0; + if (call->bchannel) + bchannel_dtmf(call->bchannel, 0); + } break; case 'c': if (opt[1] == '\0') { @@ -412,8 +429,7 @@ void apply_opt(struct chan_call *call, char *data) break; } i = 0; - while(*key) - { + while(*key) { if (*key>='0' && *key<='9') call->bf_key[i] = (*key-'0') << 8; else if (*key>='a' && *key<='f') @@ -449,10 +465,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') { @@ -460,10 +474,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') { @@ -475,15 +487,45 @@ void apply_opt(struct chan_call *call, char *data) if (call->bchannel) bchannel_pipeline(call->bchannel, call->pipeline); break; - case 'r': + 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); break; } CDEBUG(call, call->ast, "Option 'r' (re-buffer 160 bytes)"); call->rebuffer = 1; + call->framepos = 0; break; -#if 0 case 's': if (opt[1] != '\0') { CERROR(call, call->ast, "Option 's' (inband DTMF) expects no parameter.\n", opt); @@ -491,9 +533,7 @@ void apply_opt(struct chan_call *call, char *data) } CDEBUG(call, call->ast, "Option 's' (inband DTMF).\n"); call->inband_dtmf = 1; -todo break; -#endif case 'v': if (opt[1] != 'r' && opt[1] != 't') { CERROR(call, call->ast, "Option 'v' (volume) expects parameter.\n", opt); @@ -515,6 +555,15 @@ todo bchannel_gain(call->bchannel, call->tx_gain, 1); } break; + case 'k': + if (opt[1] != '\0') { + CERROR(call, call->ast, "Option 'k' (keypad) expects no parameter.\n", opt); + break; + } + CDEBUG(call, call->ast, "Option 'k' (keypad).\n"); + if (!call->keypad) + call->keypad = 1; + break; default: CERROR(call, call->ast, "Option '%s' unknown.\n", opt); } @@ -523,7 +572,7 @@ todo /* 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); } } @@ -546,7 +595,10 @@ static void send_setup_to_lcr(struct chan_call *call) memset(&newparam, 0, sizeof(union parameter)); newparam.setup.dialinginfo.itype = INFO_ITYPE_ISDN; newparam.setup.dialinginfo.ntype = INFO_NTYPE_UNKNOWN; - strncpy(newparam.setup.dialinginfo.id, call->dialstring, sizeof(newparam.setup.dialinginfo.id)-1); + if (call->keypad) + strncpy(newparam.setup.dialinginfo.keypad, call->dialstring, sizeof(newparam.setup.dialinginfo.keypad)-1); + else + strncpy(newparam.setup.dialinginfo.id, call->dialstring, sizeof(newparam.setup.dialinginfo.id)-1); strncpy(newparam.setup.dialinginfo.interfaces, call->interface, sizeof(newparam.setup.dialinginfo.interfaces)-1); newparam.setup.callerinfo.itype = INFO_ITYPE_CHAN; newparam.setup.callerinfo.ntype = INFO_NTYPE_UNKNOWN; @@ -556,28 +608,23 @@ static void send_setup_to_lcr(struct chan_call *call) strncpy(newparam.setup.callerinfo.id, call->cid_num, sizeof(newparam.setup.callerinfo.id)-1); if (call->cid_name[0]) strncpy(newparam.setup.callerinfo.name, call->cid_name, sizeof(newparam.setup.callerinfo.name)-1); - if (call->cid_rdnis[0]) - { + if (call->cid_rdnis[0]) { strncpy(newparam.setup.redirinfo.id, call->cid_rdnis, sizeof(newparam.setup.redirinfo.id)-1); newparam.setup.redirinfo.itype = INFO_ITYPE_CHAN; newparam.setup.redirinfo.ntype = INFO_NTYPE_UNKNOWN; } - switch(ast->cid.cid_pres & AST_PRES_RESTRICTION) - { - case AST_PRES_ALLOWED: - newparam.setup.callerinfo.present = INFO_PRESENT_ALLOWED; - break; + switch(ast->cid.cid_pres & AST_PRES_RESTRICTION) { case AST_PRES_RESTRICTED: newparam.setup.callerinfo.present = INFO_PRESENT_RESTRICTED; break; case AST_PRES_UNAVAILABLE: newparam.setup.callerinfo.present = INFO_PRESENT_NOTAVAIL; break; + case AST_PRES_ALLOWED: default: - newparam.setup.callerinfo.present = INFO_PRESENT_NULL; + newparam.setup.callerinfo.present = INFO_PRESENT_ALLOWED; } - switch(ast->cid.cid_ton) - { + switch(ast->cid.cid_ton) { case 4: newparam.setup.callerinfo.ntype = INFO_NTYPE_SUBSCRIBER; break; @@ -622,7 +669,10 @@ static void send_dialque_to_lcr(struct chan_call *call) /* send setup message to LCR */ memset(&newparam, 0, sizeof(union parameter)); - strncpy(newparam.information.id, call->dialque, sizeof(newparam.information.id)-1); + if (call->keypad) + strncpy(newparam.information.keypad, call->dialque, sizeof(newparam.information.keypad)-1); + else + strncpy(newparam.information.id, call->dialque, sizeof(newparam.information.id)-1); call->dialque[0] = '\0'; send_message(MESSAGE_INFORMATION, call->ref, &newparam); } @@ -636,7 +686,7 @@ static void bridge_message_if_bridged(struct chan_call *call, int message_type, /* check bridge */ if (!call) return; if (!call->bridge_call) return; - CDEBUG(call, NULL, "Sending message due briding.\n"); + CDEBUG(call, NULL, "Sending message due bridging.\n"); send_message(message_type, call->bridge_call->ref, param); } @@ -670,21 +720,21 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c { int cause, ret; union parameter newparam; + char *exten = ast->exten; + if (!*exten) + exten = "s"; - CDEBUG(call, ast, "Try to start pbx. (exten=%s context=%s complete=%s)\n", ast->exten, ast->context, complete?"yes":"no"); + CDEBUG(call, ast, "Try to start pbx. (exten=%s context=%s complete=%s)\n", exten, ast->context, complete?"yes":"no"); - if (complete) - { + if (complete) { /* if not match */ - if (!ast_canmatch_extension(ast, ast->context, ast->exten, 1, call->oad)) - { - CDEBUG(call, ast, "Got 'sending complete', but extension '%s' will not match at context '%s' - releasing.\n", ast->exten, ast->context); + if (!ast_canmatch_extension(ast, ast->context, exten, 1, call->oad)) { + CDEBUG(call, ast, "Got 'sending complete', but extension '%s' will not match at context '%s' - releasing.\n", exten, ast->context); cause = 1; goto release; } - if (!ast_exists_extension(ast, ast->context, ast->exten, 1, call->oad)) - { - CDEBUG(call, ast, "Got 'sending complete', but extension '%s' would match at context '%s', if more digits would be dialed - releasing.\n", ast->exten, ast->context); + if (!ast_exists_extension(ast, ast->context, exten, 1, call->oad)) { + CDEBUG(call, ast, "Got 'sending complete', but extension '%s' would match at context '%s', if more digits would be dialed - releasing.\n", exten, ast->context); cause = 28; goto release; } @@ -699,8 +749,7 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c goto start; } - if (ast_canmatch_extension(ast, ast->context, ast->exten, 1, call->oad)) - { + if (ast_canmatch_extension(ast, ast->context, exten, 1, call->oad)) { /* send setup acknowledge to lcr */ if (call->state != CHAN_LCR_STATE_IN_DIALING) { memset(&newparam, 0, sizeof(union parameter)); @@ -711,7 +760,7 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c call->state = CHAN_LCR_STATE_IN_DIALING; /* if match, start pbx */ - if (ast_exists_extension(ast, ast->context, ast->exten, 1, call->oad)) { + if (ast_exists_extension(ast, ast->context, exten, 1, call->oad)) { CDEBUG(call, ast, "Extensions matches.\n"); goto start; } @@ -721,6 +770,12 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c return; } + if (!*ast->exten) { + /* if can match */ + CDEBUG(call, ast, "There is no 's' extension (and we tried to match it implicitly). Extensions may match, if more digits are dialed.\n"); + return; + } + /* if not match */ cause = 1; release: @@ -738,18 +793,19 @@ 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) - { + if (ret < 0) { cause = (ret==-2)?34:27; goto release; } call->pbx_started = 1; -// if (call->state == CHAN_LCR_STATE_IN_DIALING) - ast_setstate(ast, AST_STATE_RINGING); -// else -// ast_setstate(ast, AST_STATE_RINGING); -// return; + ast_setstate(ast, AST_STATE_RING); } /* @@ -762,9 +818,16 @@ 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); - if (!ast) - { + #endif + + if (!ast) { /* release */ CERROR(call, NULL, "Failed to create Asterisk channel - releasing.\n"); send_release_and_import(call, CAUSE_RESSOURCEUNAVAIL, LOCATION_PRIVATE_LOCAL); @@ -791,8 +854,7 @@ static void lcr_in_setup(struct chan_call *call, int message_type, union paramet 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)); - switch (param->setup.callerinfo.present) - { + switch (param->setup.callerinfo.present) { case INFO_PRESENT_ALLOWED: ast->cid.cid_pres = AST_PRES_ALLOWED; break; @@ -802,8 +864,7 @@ static void lcr_in_setup(struct chan_call *call, int message_type, union paramet default: ast->cid.cid_pres = AST_PRES_UNAVAILABLE; } - switch (param->setup.callerinfo.ntype) - { + switch (param->setup.callerinfo.ntype) { case INFO_NTYPE_SUBSCRIBER: ast->cid.cid_ton = 4; break; @@ -902,7 +963,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); } /* @@ -922,8 +983,7 @@ static void lcr_in_disconnect(struct chan_call *call, int message_type, union pa /* if bridge, forward disconnect and return */ #ifdef TODO feature flag - if (call->bridge_call) - { + if (call->bridge_call) { CDEBUG(call, call->ast, "Only signal disconnect via bridge.\n"); bridge_message_if_bridged(call, message_type, param); return; @@ -935,8 +995,7 @@ static void lcr_in_disconnect(struct chan_call *call, int message_type, union pa /* change to release state */ call->state = CHAN_LCR_STATE_RELEASE; /* queue release asterisk */ - if (ast) - { + if (ast) { ast->hangupcause = call->cause; if (call->pbx_started) strcpy(call->queue_string, "H"); // overwrite other indications @@ -947,7 +1006,7 @@ static void lcr_in_disconnect(struct chan_call *call, int message_type, union pa } /* - * incoming setup acknowledge from LCR + * incoming release from LCR */ static void lcr_in_release(struct chan_call *call, int message_type, union parameter *param) { @@ -960,22 +1019,19 @@ static void lcr_in_release(struct chan_call *call, int message_type, union param /* change to release state */ call->state = CHAN_LCR_STATE_RELEASE; /* copy release info */ - if (!call->cause) - { + if (!call->cause) { call->cause = param->disconnectinfo.cause; call->location = param->disconnectinfo.location; } /* if we have an asterisk instance, queue hangup, else we are done */ - if (ast) - { + if (ast) { ast->hangupcause = call->cause; if (call->pbx_started) strcpy(call->queue_string, "H"); else { ast_hangup(ast); // call will be destroyed here } - } else - { + } else { free_call(call); } @@ -993,8 +1049,7 @@ static void lcr_in_information(struct chan_call *call, int message_type, union p if (!ast) return; /* pbx not started */ - if (!call->pbx_started) - { + if (!call->pbx_started) { CDEBUG(call, call->ast, "Asterisk not started, adding digits to number.\n"); strncat(ast->exten, param->information.id, AST_MAX_EXTENSION-1); lcr_start_pbx(call, ast, param->information.sending_complete); @@ -1014,7 +1069,7 @@ static void lcr_in_information(struct chan_call *call, int message_type, union p /* 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"); + CDEBUG(call, call->ast, "Call is connected, bridging.\n"); bridge_message_if_bridged(call, message_type, param); } } @@ -1056,6 +1111,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) @@ -1068,6 +1151,11 @@ void lcr_in_dtmf(struct chan_call *call, int val) if (!call->pbx_started) return; + if (!call->dsp_dtmf) { + CDEBUG(call, call->ast, "Recognised DTMF digit '%c', but ignoring. This is fixed in later mISDN driver.\n", val); + return; + } + CDEBUG(call, call->ast, "Recognised DTMF digit '%c'.\n", val); digit[0] = val; digit[1] = '\0'; @@ -1086,21 +1174,17 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) memset(&newparam, 0, sizeof(union parameter)); /* handle bchannel message*/ - if (message_type == MESSAGE_BCHANNEL) - { - switch(param->bchannel.type) - { + if (message_type == MESSAGE_BCHANNEL) { + switch(param->bchannel.type) { case BCHANNEL_ASSIGN: 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))) - { + if ((bchannel = find_bchannel_handle(param->bchannel.handle))) { CERROR(NULL, NULL, "bchannel handle %x already assigned.\n", (int)param->bchannel.handle); return(-1); } /* create bchannel */ bchannel = alloc_bchannel(param->bchannel.handle); - if (!bchannel) - { + if (!bchannel) { CERROR(NULL, NULL, "alloc bchannel handle %x failed.\n", (int)param->bchannel.handle); return(-1); } @@ -1109,24 +1193,21 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) bchannel->b_tx_gain = param->bchannel.tx_gain; bchannel->b_rx_gain = param->bchannel.rx_gain; strncpy(bchannel->b_pipeline, param->bchannel.pipeline, sizeof(bchannel->b_pipeline)-1); - if (param->bchannel.crypt_len && param->bchannel.crypt_len <= sizeof(bchannel->b_bf_key)) - { + if (param->bchannel.crypt_len && param->bchannel.crypt_len <= sizeof(bchannel->b_bf_key)) { bchannel->b_bf_len = param->bchannel.crypt_len; memcpy(bchannel->b_bf_key, param->bchannel.crypt, param->bchannel.crypt_len); } bchannel->b_txdata = 0; - bchannel->b_dtmf = 1; bchannel->b_tx_dejitter = 1; /* in case, ref is not set, this bchannel instance must * be created until it is removed again by LCR */ /* link to call */ call = find_call_ref(ref); - if (call) - { + if (call) { bchannel->call = call; call->bchannel = bchannel; - if (call->dtmf) + if (call->dsp_dtmf) bchannel_dtmf(bchannel, 1); if (call->bf_len) bchannel_blowfish(bchannel, call->bf_key, call->bf_len); @@ -1141,7 +1222,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 */ @@ -1159,8 +1240,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) case BCHANNEL_REMOVE: CDEBUG(NULL, NULL, "Received BCHANNEL_REMOVE message. (handle=%08lx)\n", param->bchannel.handle); - if (!(bchannel = find_bchannel_handle(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); } @@ -1181,14 +1261,11 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) } /* handle new ref */ - if (message_type == MESSAGE_NEWREF) - { - if (param->direction) - { + if (message_type == MESSAGE_NEWREF) { + if (param->direction) { /* new ref from lcr */ CDEBUG(NULL, NULL, "Received new ref by LCR, due to incomming call. (ref=%ld)\n", ref); - if (!ref || find_call_ref(ref)) - { + if (!ref || find_call_ref(ref)) { CERROR(NULL, NULL, "Illegal new ref %ld received.\n", ref); return(-1); } @@ -1198,14 +1275,15 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) call->state = CHAN_LCR_STATE_IN_PREPARE; /* set ref */ call->ref = ref; + call->ref_was_assigned = 1; + /* set dtmf (default, use option 'n' to disable */ + call->dsp_dtmf = 1; /* wait for setup (or release from asterisk) */ - } else - { + } else { /* new ref, as requested from this remote application */ CDEBUG(NULL, NULL, "Received new ref by LCR, as requested from chan_lcr. (ref=%ld)\n", ref); call = find_call_ref(0); - if (!call) - { + if (!call) { /* 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); @@ -1213,12 +1291,12 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) } /* store new ref */ call->ref = ref; + call->ref_was_assigned = 1; /* send pending setup info */ if (call->state == CHAN_LCR_STATE_OUT_PREPARE) send_setup_to_lcr(call); /* release if asterisk has signed off */ - else if (call->state == CHAN_LCR_STATE_RELEASE) - { + else if (call->state == CHAN_LCR_STATE_RELEASE) { /* send release */ if (call->cause) send_release_and_import(call, call->cause, call->location); @@ -1233,22 +1311,19 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) } /* check ref */ - if (!ref) - { + if (!ref) { CERROR(NULL, NULL, "Received message %d without ref.\n", message_type); return(-1); } call = find_call_ref(ref); - if (!call) - { + 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); } /* handle messages */ - switch(message_type) - { + switch(message_type) { case MESSAGE_SETUP: lcr_in_setup(call, message_type, param); break; @@ -1290,6 +1365,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 @@ -1359,29 +1436,23 @@ int handle_socket(void) /* read from socket */ len = read(lcr_sock, &msg, sizeof(msg)); - if (len == 0) - { + if (len == 0) { CERROR(NULL, NULL, "Socket closed.\n"); return(-1); // socket closed } - if (len > 0) - { - if (len != sizeof(msg)) - { + if (len > 0) { + if (len != sizeof(msg)) { CERROR(NULL, NULL, "Socket short read. (len %d)\n", len); return(-1); // socket error } - if (msg.message != ADMIN_MESSAGE) - { + 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) - { + } else { + if (errno != EWOULDBLOCK) { CERROR(NULL, NULL, "Socket failed (errno %d).\n", errno); return(-1); } @@ -1392,15 +1463,12 @@ int handle_socket(void) return(work); admin = admin_first; len = write(lcr_sock, &admin->msg, sizeof(msg)); - if (len == 0) - { + if (len == 0) { CERROR(NULL, NULL, "Socket closed.\n"); return(-1); // socket closed } - if (len > 0) - { - if (len != sizeof(msg)) - { + if (len > 0) { + if (len != sizeof(msg)) { CERROR(NULL, NULL, "Socket short write. (len %d)\n", len); return(-1); // socket error } @@ -1409,10 +1477,8 @@ int handle_socket(void) free(admin); work = 1; - } else - { - if (errno != EWOULDBLOCK) - { + } else { + if (errno != EWOULDBLOCK) { CERROR(NULL, NULL, "Socket failed (errno %d).\n", errno); return(-1); } @@ -1427,15 +1493,13 @@ int handle_socket(void) int open_socket(void) { int ret; - char *socket_name = SOCKET_NAME; 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) - { + if ((lcr_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { CERROR(NULL, NULL, "Failed to create socket.\n"); return(lcr_sock); } @@ -1443,11 +1507,10 @@ int open_socket(void) /* set socket address and name */ memset(&sock_address, 0, sizeof(sock_address)); sock_address.sun_family = PF_UNIX; - strcpy(sock_address.sun_path, socket_name); + sprintf(sock_address.sun_path, SOCKET_NAME, options.lock); /* connect socket */ - if ((conn = connect(lcr_sock, (struct sockaddr *)&sock_address, SUN_LEN(&sock_address))) < 0) - { + if ((conn = connect(lcr_sock, (struct sockaddr *)&sock_address, SUN_LEN(&sock_address))) < 0) { close(lcr_sock); lcr_sock = -1; CDEBUG(NULL, NULL, "Failed to connect to socket '%s'. Is LCR running?\n", sock_address.sun_path); @@ -1455,8 +1518,7 @@ int open_socket(void) } /* set non-blocking io */ - if ((ret = ioctl(lcr_sock, FIONBIO, (unsigned char *)(&on))) < 0) - { + 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"); @@ -1509,6 +1571,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); @@ -1516,8 +1582,9 @@ static int queue_send(void) 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 'A': + case 'N': CDEBUG(call, ast, "Sending queued ANSWER to Asterisk.\n"); ast_queue_control(ast, AST_CONTROL_ANSWER); break; @@ -1525,22 +1592,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++; } @@ -1618,7 +1697,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); } } @@ -1648,8 +1735,7 @@ struct ast_channel *lcr_request(const char *type, int format, void *data, int *c CDEBUG(NULL, NULL, "Received request from Asterisk. (data=%s)\n", (char *)data); /* if socket is closed */ - if (lcr_sock < 0) - { + if (lcr_sock < 0) { CERROR(NULL, NULL, "Rejecting call from Asterisk, because LCR not running.\n"); ast_mutex_unlock(&chan_lock); return NULL; @@ -1657,17 +1743,23 @@ struct ast_channel *lcr_request(const char *type, int format, void *data, int *c /* create call instance */ call = alloc_call(); - if (!call) - { + if (!call) { /* failed to create instance */ ast_mutex_unlock(&chan_lock); return NULL; } /* 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); - if (!ast) - { + #endif + + #ifdef LCR_FOR_CALLWEAVER + ast = ast_channel_alloc(1); + #endif + + if (!ast) { CERROR(NULL, NULL, "Failed to create Asterisk channel.\n"); free_call(call); /* failed to create instance */ @@ -1730,6 +1822,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); @@ -1776,12 +1874,56 @@ static int lcr_call(struct ast_channel *ast, char *dest, int timeout) return 0; } +static void send_digit_to_chan(struct ast_channel * ast, char digit ) +{ + static const char* dtmf_tones[] = { + "!941+1336/100,!0/100", /* 0 */ + "!697+1209/100,!0/100", /* 1 */ + "!697+1336/100,!0/100", /* 2 */ + "!697+1477/100,!0/100", /* 3 */ + "!770+1209/100,!0/100", /* 4 */ + "!770+1336/100,!0/100", /* 5 */ + "!770+1477/100,!0/100", /* 6 */ + "!852+1209/100,!0/100", /* 7 */ + "!852+1336/100,!0/100", /* 8 */ + "!852+1477/100,!0/100", /* 9 */ + "!697+1633/100,!0/100", /* A */ + "!770+1633/100,!0/100", /* B */ + "!852+1633/100,!0/100", /* C */ + "!941+1633/100,!0/100", /* D */ + "!941+1209/100,!0/100", /* * */ + "!941+1477/100,!0/100" }; /* # */ + + if (digit >= '0' && digit <='9') + ast_playtones_start(ast,0,dtmf_tones[digit-'0'], 0); + else if (digit >= 'A' && digit <= 'D') + ast_playtones_start(ast,0,dtmf_tones[digit-'A'+10], 0); + else if (digit == '*') + ast_playtones_start(ast,0,dtmf_tones[14], 0); + else if (digit == '#') + ast_playtones_start(ast,0,dtmf_tones[15], 0); + else { + /* not handled */ + ast_log(LOG_DEBUG, "Unable to handle DTMF tone " + "'%c' for '%s'\n", digit, ast->name); + } +} + +#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; @@ -1797,29 +1939,62 @@ static int lcr_digit_begin(struct ast_channel *ast, char digit) CDEBUG(call, ast, "Received digit '%c' from Asterisk.\n", digit); /* send information or queue them */ - if (call->ref && call->state == CHAN_LCR_STATE_OUT_DIALING) - { + if (call->ref && call->state == CHAN_LCR_STATE_OUT_DIALING) { CDEBUG(call, ast, "Sending digit to LCR, because we are in dialing state.\n"); memset(&newparam, 0, sizeof(union parameter)); - newparam.information.id[0] = digit; - newparam.information.id[1] = '\0'; + if (call->keypad) { + newparam.information.keypad[0] = digit; + newparam.information.keypad[1] = '\0'; + } else { + newparam.information.id[0] = digit; + newparam.information.id[1] = '\0'; + } send_message(MESSAGE_INFORMATION, call->ref, &newparam); } else if (!call->ref - && (call->state == CHAN_LCR_STATE_OUT_PREPARE || call->state == CHAN_LCR_STATE_OUT_SETUP)) - { + && (call->state == CHAN_LCR_STATE_OUT_PREPARE || call->state == CHAN_LCR_STATE_OUT_SETUP)) { CDEBUG(call, ast, "Queue digits, because we are in setup/dialing state and have no ref yet.\n"); *buf = digit; strncat(call->dialque, buf, strlen(call->dialque)-1); } ast_mutex_unlock(&chan_lock); + +#ifdef LCR_FOR_ASTERISK return(0); } static int lcr_digit_end(struct ast_channel *ast, char digit, unsigned int duration) { - printf("DIGIT END %c\n", digit); + int inband_dtmf = 0; + struct chan_call *call; +#endif + + ast_mutex_lock(&chan_lock); + + call = ast->tech_pvt; + + if (!call) { + CERROR(NULL, ast, + "Received digit from Asterisk, " + "but no call instance exists.\n"); + ast_mutex_unlock(&chan_lock); + return -1; + } + + CDEBUG(call, ast, "DIGIT END '%c' from Asterisk.\n", digit); + + if (call->state == CHAN_LCR_STATE_CONNECT && call->inband_dtmf) { + inband_dtmf = 1; + } + + ast_mutex_unlock(&chan_lock); + + if (inband_dtmf) { + CDEBUG(call, ast, "-> sending '%c' inband.\n", digit); + send_digit_to_chan(ast, digit); + } + return (0); } @@ -1859,11 +2034,6 @@ static int lcr_answer(struct ast_channel *ast) /* enable keypad */ // memset(&newparam, 0, sizeof(union parameter)); // send_message(MESSAGE_ENABLEKEYPAD, call->ref, &newparam); - /* enable dtmf */ - if (call->no_dtmf) - CDEBUG(call, ast, "DTMF is disabled by option.\n"); - else - call->dtmf = 1; ast_mutex_unlock(&chan_lock); return 0; @@ -1892,8 +2062,7 @@ static int lcr_hangup(struct ast_channel *ast) /* disconnect asterisk, maybe not required */ ast->tech_pvt = NULL; ast->fds[0] = -1; - if (call->ref) - { + if (call->ref) { /* release */ CDEBUG(call, ast, "Releasing ref and freeing call instance.\n"); if (ast->hangupcause > 0) @@ -1905,19 +2074,17 @@ static int lcr_hangup(struct ast_channel *ast) if (!pthread_equal(tid, chan_tid)) ast_mutex_unlock(&chan_lock); return 0; - } else - { + } else { /* ref is not set, due to prepare setup or release */ - if (call->state == CHAN_LCR_STATE_RELEASE) - { + if (call->state == CHAN_LCR_STATE_RELEASE) { /* we get the response to our release */ CDEBUG(call, ast, "Freeing call instance, because we have no ref AND we are requesting no ref.\n"); free_call(call); - } else - { + } else { /* during prepare, we change to release state */ CDEBUG(call, ast, "We must wait until we received our ref, until we can free call instance.\n"); call->state = CHAN_LCR_STATE_RELEASE; + call->ast = NULL; } } if (!pthread_equal(tid, chan_tid)) @@ -1941,7 +2108,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; } @@ -1960,23 +2127,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; @@ -2035,7 +2229,7 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz break; case AST_CONTROL_RINGING: CDEBUG(call, ast, "Received indicate AST_CONTROL_RINGING from Asterisk.\n"); - ast_setstate(ast, AST_STATE_RINGING); + ast_setstate(ast, AST_STATE_RING); if (call->state == CHAN_LCR_STATE_IN_SETUP || call->state == CHAN_LCR_STATE_IN_DIALING || call->state == CHAN_LCR_STATE_IN_PROCEEDING) { @@ -2046,6 +2240,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; @@ -2063,7 +2267,15 @@ 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: CDEBUG(call, ast, "Received indicate AST_CONTROL_UNHOLD from Asterisk.\n"); @@ -2074,8 +2286,13 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz /*stop moh*/ ast_moh_stop(ast); + call->on_hold = 0; break; - +#ifdef AST_CONTROL_SRCUPDATE + case AST_CONTROL_SRCUPDATE: + 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; @@ -2090,21 +2307,25 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz /* * fixup asterisk */ -static int lcr_fixup(struct ast_channel *oldast, struct ast_channel *newast) +static int lcr_fixup(struct ast_channel *oldast, struct ast_channel *ast) { struct chan_call *call; + if (!ast) { + return -1; + } + ast_mutex_lock(&chan_lock); - call = oldast->tech_pvt; + call = ast->tech_pvt; if (!call) { - CERROR(NULL, oldast, "Received fixup from Asterisk, but no call instance exists.\n"); + CERROR(NULL, ast, "Received fixup from Asterisk, but no call instance exists.\n"); ast_mutex_unlock(&chan_lock); return -1; } - CDEBUG(call, oldast, "Received fixup from Asterisk.\n"); - call->ast = newast; - ast_mutex_lock(&chan_lock); + CDEBUG(call, ast, "Received fixup from Asterisk.\n"); + call->ast = ast; + ast_mutex_unlock(&chan_lock); return 0; } @@ -2147,7 +2368,7 @@ enum ast_bridge_result lcr_bridge(struct ast_channel *ast1, struct ast_frame *f; int bridge_id; - CDEBUG(NULL, NULL, "Received briding request from Asterisk.\n"); + CDEBUG(NULL, NULL, "Received bridging request from Asterisk.\n"); carr[0] = ast1; carr[1] = ast2; @@ -2162,9 +2383,11 @@ 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, briding via DSP.\n"); + CDEBUG(NULL, NULL, "Both calls use DSP, bridging via DSP.\n"); /* get bridge id and join */ bridge_id = new_bridge_id(); @@ -2178,9 +2401,9 @@ enum ast_bridge_result lcr_bridge(struct ast_channel *ast1, bchannel_join(call2->bchannel, bridge_id); } else if (call1->nodsp && call2->nodsp) - CDEBUG(NULL, NULL, "Both calls use no DSP, briding in channel driver.\n"); + CDEBUG(NULL, NULL, "Both calls use no DSP, bridging in channel driver.\n"); else - CDEBUG(NULL, NULL, "One call uses no DSP, briding in channel driver.\n"); + CDEBUG(NULL, NULL, "One call uses no DSP, bridging in channel driver.\n"); call1->bridge_call = call2; call2->bridge_call = call1; @@ -2198,6 +2421,30 @@ enum ast_bridge_result lcr_bridge(struct ast_channel *ast1, CDEBUG(call2, ast2, "Bridge established before lcr_answer, so we call it ourself: Calling lcr_answer...\n"); lcr_answer(ast2); } + + /* sometimes SIP phones forget to send RETRIEVE before TRANSFER + so let's do it for them. Hmpf. + */ + + if (call1->on_hold) { + union parameter newparam; + + memset(&newparam, 0, sizeof(union parameter)); + newparam.notifyinfo.notify = INFO_NOTIFY_REMOTE_RETRIEVAL; + send_message(MESSAGE_NOTIFY, call1->ref, &newparam); + + call1->on_hold = 0; + } + + if (call2->on_hold) { + union parameter newparam; + + memset(&newparam, 0, sizeof(union parameter)); + newparam.notifyinfo.notify = INFO_NOTIFY_REMOTE_RETRIEVAL; + send_message(MESSAGE_NOTIFY, call2->ref, &newparam); + + call2->on_hold = 0; + } ast_mutex_unlock(&chan_lock); @@ -2239,22 +2486,20 @@ enum ast_bridge_result lcr_bridge(struct ast_channel *ast1, } - CDEBUG(NULL, NULL, "Releasing bride.\n"); + CDEBUG(NULL, NULL, "Releasing bridge.\n"); /* split channels */ ast_mutex_lock(&chan_lock); call1 = ast1->tech_pvt; call2 = ast2->tech_pvt; - if (call1 && call1->bridge_id) - { + 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; } - if (call2 && call1->bridge_id) - { + if (call2 && call1->bridge_id) { call2->bridge_id = 0; if (call2->bchannel) bchannel_join(call2->bchannel, 0); @@ -2269,20 +2514,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 }; @@ -2376,13 +2630,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) { @@ -2391,7 +2658,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"); @@ -2413,7 +2688,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); @@ -2426,7 +2709,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; @@ -2435,11 +2725,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" @@ -2447,13 +2752,17 @@ 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" + " 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" + " k - use keypad to dial this call.\n" ); @@ -2468,13 +2777,20 @@ int load_module(void) #endif quit = 0; - if ((pthread_create(&chan_tid, NULL, chan_thread, NULL)<0)) - { + if ((pthread_create(&chan_tid, NULL, chan_thread, NULL)<0)) { /* failed to create thread */ 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; } @@ -2511,12 +2827,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