From 3a8f58ec8946b7f1683208d1cc3b054486f12e6c Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 13 Dec 2010 09:22:49 +0100 Subject: [PATCH] Adding interface support for remote app (chan_lcr). chan_lcr can be handled as an interface. This way it is possible to (e.g.): - make a SIP phone become an LCR extension with all LCR features. - make conference calls. (untested) - perform parallel ringing. (ISDN phone and SIP phones can ring in parallel.) - do voice recoding. It is still also possible to link chan_lcr directly without interface (as before). Documentation/howto for that will follow. --- Makefile.am | 4 +- Makefile.in | 43 +++--- apppbx.cpp | 42 ++++-- chan_lcr.c | 349 +++++++++++++++++++++++++------------------------ default/gsm.conf | 10 +- default/interface.conf | 14 ++ default/options.conf | 10 ++ gsm.cpp | 145 +------------------- gsm.h | 4 - gsm_conf.c | 18 --- interface.c | 48 ++++++- interface.h | 2 + joinremote.cpp | 29 +--- joinremote.h | 2 +- loop.c | 153 ++++++++++++++++++++++ loop.h | 12 ++ mISDN.cpp | 88 ++++++------- mISDN.h | 3 +- main.c | 4 + main.h | 2 + message.h | 9 +- options.c | 20 ++- options.h | 2 + port.h | 7 + remote.cpp | 221 +++++++++++++++++++++++++++++++ remote.h | 20 +++ socket_server.c | 139 ++++++++++++++++---- socket_server.h | 5 +- 28 files changed, 924 insertions(+), 481 deletions(-) create mode 100644 loop.c create mode 100644 loop.h create mode 100644 remote.cpp create mode 100644 remote.h diff --git a/Makefile.am b/Makefile.am index f71d715..ea96ae7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -125,8 +125,8 @@ endif INCLUDES = $(all_includes) $(GSM_INCLUDE) $(SS5_INCLUDE) -Wall $(INSTALLATION_DEFINES) -lcr_SOURCES = $(GSM_SOURCE) $(SS5_SOURCE) select.c action.cpp mISDN.cpp tones.c \ - action_efi.cpp crypt.cpp mail.c trace.c \ +lcr_SOURCES = $(GSM_SOURCE) $(SS5_SOURCE) select.c action.cpp mISDN.cpp \ + tones.c loop.c remote.c action_efi.cpp crypt.cpp mail.c trace.c \ action_vbox.cpp dss1.cpp main.c \ vbox.cpp alawulaw.c endpoint.cpp interface.c message.c \ apppbx.cpp endpointapp.cpp join.cpp options.c \ diff --git a/Makefile.in b/Makefile.in index 067dfe1..3568597 100644 --- a/Makefile.in +++ b/Makefile.in @@ -97,12 +97,12 @@ am__lcr_SOURCES_DIST = gsm_audio.c gsm.cpp gsm_conf.c gsm_bs.cpp \ openbsc/src/vty_interface_layer3.c openbsc/src/bsc_api.c \ openbsc/src/bsc_version.c gsm_ms.cpp \ layer23/src/mobile/app_mobile.c ss5.cpp ss5_encode.c \ - ss5_decode.c select.c action.cpp mISDN.cpp tones.c \ - action_efi.cpp crypt.cpp mail.c trace.c action_vbox.cpp \ - dss1.cpp main.c vbox.cpp alawulaw.c endpoint.cpp interface.c \ - message.c apppbx.cpp endpointapp.cpp join.cpp options.c \ - extension.c joinpbx.cpp port.cpp callerid.c joinremote.cpp \ - route.c cause.c socket_server.c + ss5_decode.c select.c action.cpp mISDN.cpp tones.c loop.c \ + remote.c action_efi.cpp crypt.cpp mail.c trace.c \ + action_vbox.cpp dss1.cpp main.c vbox.cpp alawulaw.c \ + endpoint.cpp interface.c message.c apppbx.cpp endpointapp.cpp \ + join.cpp options.c extension.c joinpbx.cpp port.cpp callerid.c \ + joinremote.cpp route.c cause.c socket_server.c @ENABLE_GSM_TRUE@am__objects_1 = gsm_audio.$(OBJEXT) gsm.$(OBJEXT) \ @ENABLE_GSM_TRUE@ gsm_conf.$(OBJEXT) @ENABLE_GSM_BS_TRUE@am__objects_2 = gsm_bs.$(OBJEXT) \ @@ -116,14 +116,15 @@ am__objects_4 = $(am__objects_1) $(am__objects_2) $(am__objects_3) @ENABLE_SS5_TRUE@ ss5_decode.$(OBJEXT) am_lcr_OBJECTS = $(am__objects_4) $(am__objects_5) select.$(OBJEXT) \ action.$(OBJEXT) mISDN.$(OBJEXT) tones.$(OBJEXT) \ - action_efi.$(OBJEXT) crypt.$(OBJEXT) mail.$(OBJEXT) \ - trace.$(OBJEXT) action_vbox.$(OBJEXT) dss1.$(OBJEXT) \ - main.$(OBJEXT) vbox.$(OBJEXT) alawulaw.$(OBJEXT) \ - endpoint.$(OBJEXT) interface.$(OBJEXT) message.$(OBJEXT) \ - apppbx.$(OBJEXT) endpointapp.$(OBJEXT) join.$(OBJEXT) \ - options.$(OBJEXT) extension.$(OBJEXT) joinpbx.$(OBJEXT) \ - port.$(OBJEXT) callerid.$(OBJEXT) joinremote.$(OBJEXT) \ - route.$(OBJEXT) cause.$(OBJEXT) socket_server.$(OBJEXT) + loop.$(OBJEXT) remote.$(OBJEXT) action_efi.$(OBJEXT) \ + crypt.$(OBJEXT) mail.$(OBJEXT) trace.$(OBJEXT) \ + action_vbox.$(OBJEXT) dss1.$(OBJEXT) main.$(OBJEXT) \ + vbox.$(OBJEXT) alawulaw.$(OBJEXT) endpoint.$(OBJEXT) \ + interface.$(OBJEXT) message.$(OBJEXT) apppbx.$(OBJEXT) \ + endpointapp.$(OBJEXT) join.$(OBJEXT) options.$(OBJEXT) \ + extension.$(OBJEXT) joinpbx.$(OBJEXT) port.$(OBJEXT) \ + callerid.$(OBJEXT) joinremote.$(OBJEXT) route.$(OBJEXT) \ + cause.$(OBJEXT) socket_server.$(OBJEXT) lcr_OBJECTS = $(am_lcr_OBJECTS) am__DEPENDENCIES_1 = @ENABLE_GSM_BS_TRUE@am__DEPENDENCIES_2 = ./openbsc/src/libbsc.a \ @@ -285,8 +286,8 @@ GSM_LIB = $(am__append_3) $(am__append_6) $(am__append_9) @ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@chan_lcr_so_LDFLAGS = -shared @ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@chan_lcr_so_LDADD = chan_lcr.po bchannel.po options.po callerid.po select.po INCLUDES = $(all_includes) $(GSM_INCLUDE) $(SS5_INCLUDE) -Wall $(INSTALLATION_DEFINES) -lcr_SOURCES = $(GSM_SOURCE) $(SS5_SOURCE) select.c action.cpp mISDN.cpp tones.c \ - action_efi.cpp crypt.cpp mail.c trace.c \ +lcr_SOURCES = $(GSM_SOURCE) $(SS5_SOURCE) select.c action.cpp mISDN.cpp \ + tones.c loop.c remote.c action_efi.cpp crypt.cpp mail.c trace.c \ action_vbox.cpp dss1.cpp main.c \ vbox.cpp alawulaw.c endpoint.cpp interface.c message.c \ apppbx.cpp endpointapp.cpp join.cpp options.c \ @@ -318,15 +319,15 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ - echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ - $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu Makefile + $(AUTOMAKE) --foreign Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ @@ -499,12 +500,14 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/joinpbx.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/joinremote.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lcradmin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/loop.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mISDN.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mail.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/message.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/port.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remote.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/route.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/select.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_server.Po@am__quote@ diff --git a/apppbx.cpp b/apppbx.cpp index 87e4f41..aaa02cb 100644 --- a/apppbx.cpp +++ b/apppbx.cpp @@ -851,6 +851,7 @@ void EndpointAppPBX::out_setup(int cfnr) int channel = 0; int earlyb; int mode = B_MODE_TRANSPARENT; + struct admin_list *admin; /* set bchannel mode */ mode = e_capainfo.source_mode; @@ -989,6 +990,21 @@ void EndpointAppPBX::out_setup(int cfnr) port = new Pgsm_ms(PORT_TYPE_GSM_MS_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode); else #endif + if (mISDNport->ifport->remote) { + admin = admin_first; + while(admin) { + if (admin->remote_name[0] && !strcmp(admin->remote_name, mISDNport->ifport->remote_app)) + break; + admin = admin->next; + } + if (!admin) { + trace_header("INTERFACE (remote not connected)", DIRECTION_NONE); + add_trace("application", NULL, "%s", mISDNport->ifport->remote_app); + end_trace(); + continue; + } + port = new Premote(PORT_TYPE_REMOTE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode, admin->sock); + } else port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode); if (!port) FATAL("Failed to create Port instance\n"); @@ -1208,6 +1224,21 @@ void EndpointAppPBX::out_setup(int cfnr) port = new Pgsm_ms(PORT_TYPE_GSM_MS_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode); else #endif + if (mISDNport->ifport->remote) { + admin = admin_first; + while(admin) { + if (admin->remote_name[0] && !strcmp(admin->remote_name, mISDNport->ifport->remote_app)) + break; + admin = admin->next; + } + if (!admin) { + trace_header("INTERFACE (remote not connected)", DIRECTION_NONE); + add_trace("application", NULL, "%s", mISDNport->ifport->remote_app); + end_trace(); + continue; + } + port = new Premote(PORT_TYPE_REMOTE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode, admin->sock); + } else port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode); if (!port) FATAL("No memory for Port instance\n"); @@ -2048,10 +2079,8 @@ void EndpointAppPBX::port_connect(struct port_list *portlist, int message_type, /* other calls with no caller id (or not available for the extension) and force colp */ if ((e_connectinfo.id[0]=='\0' || (e_connectinfo.present==INFO_PRESENT_RESTRICTED && !e_ext.anon_ignore))&& e_ext.colp==COLP_FORCE) { e_connectinfo.ntype = INFO_NTYPE_NOTPRESENT; - if (portlist->port_type==PORT_TYPE_DSS1_TE_OUT - || portlist->port_type==PORT_TYPE_DSS1_NT_OUT - || portlist->port_type==PORT_TYPE_GSM_BS_OUT - || portlist->port_type==PORT_TYPE_GSM_MS_OUT) { /* external extension answered */ + if ((portlist->port_type & PORT_CLASS_DIR_MASK) == PORT_CLASS_DIR_OUT) { + /* external extension answered */ port = find_port_id(portlist->port_id); if (port) { SCPY(e_connectinfo.id, nationalize_callerinfo(port->p_dialinginfo.id, &e_connectinfo.ntype, options.national, options.international)); @@ -3530,10 +3559,7 @@ void EndpointAppPBX::pick_join(char *extensions) break; } } - if ((port->p_type==PORT_TYPE_DSS1_NT_OUT - || port->p_type==PORT_TYPE_DSS1_TE_OUT - || port->p_type==PORT_TYPE_GSM_BS_OUT - || port->p_type==PORT_TYPE_GSM_MS_OUT) + if ((portlist->port_type & PORT_CLASS_DIR_MASK) == PORT_CLASS_DIR_OUT && port->p_state==PORT_STATE_OUT_ALERTING) if (match_list(extensions, eapp->e_ext.number)) { found = eapp; diff --git a/chan_lcr.c b/chan_lcr.c index f4d8378..e9e26fa 100644 --- a/chan_lcr.c +++ b/chan_lcr.c @@ -40,7 +40,7 @@ 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. +is stored in the chan_call structure. The setup information is sent to LCR using MESSAGE_SETUP. The state changes to CHAN_LCR_STATE_OUT_SETUP. @@ -207,7 +207,7 @@ int global_change = 0; int wake_global = 0; int wake_pipe[2]; struct lcr_fd wake_fd; - + int quit; int glob_channel = 0; @@ -266,7 +266,7 @@ struct chan_call *find_call_ref(unsigned int ref) { struct chan_call *call = call_first; int assigned = (ref > 0); - + while(call) { if (call->ref == ref && call->ref_was_assigned == assigned) break; @@ -595,8 +595,8 @@ void apply_opt(struct chan_call *call, char *data) default: CERROR(call, call->ast, "Option '%s' unknown.\n", opt); } - } - + } + /* re-open, if bchannel is created */ if (call->bchannel && call->bchannel->b_sock > -1) { bchannel_destroy(call->bchannel); @@ -622,15 +622,16 @@ static void send_setup_to_lcr(struct chan_call *call) /* send setup message to LCR */ memset(&newparam, 0, sizeof(union parameter)); - newparam.setup.dialinginfo.itype = INFO_ITYPE_ISDN; - newparam.setup.dialinginfo.ntype = INFO_NTYPE_UNKNOWN; + newparam.setup.dialinginfo.itype = INFO_ITYPE_ISDN; + newparam.setup.dialinginfo.ntype = INFO_NTYPE_UNKNOWN; 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; + if (!!strcmp(call->interface, "pbx")) + 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; strncpy(newparam.setup.callerinfo.display, call->display, sizeof(newparam.setup.callerinfo.display)-1); call->display[0] = '\0'; if (call->cid_num[0]) @@ -639,8 +640,8 @@ static void send_setup_to_lcr(struct chan_call *call) strncpy(newparam.setup.callerinfo.name, call->cid_name, sizeof(newparam.setup.callerinfo.name)-1); 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; + 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_RESTRICTED: @@ -697,7 +698,7 @@ static void send_dialque_to_lcr(struct chan_call *call) if (!call->ast || !call->ref || !call->dialque[0]) return; - + CDEBUG(call, call->ast, "Sending dial queue to LCR. (dialing=%s)\n", call->dialque); /* send setup message to LCR */ @@ -758,7 +759,7 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c exten = "s"; CDEBUG(call, ast, "Try to start pbx. (exten=%s context=%s complete=%s)\n", exten, ast->context, complete?"yes":"no"); - + if (complete) { /* if not match */ if (!ast_canmatch_extension(ast, ast->context, exten, 1, call->oad)) { @@ -822,16 +823,16 @@ static void lcr_start_pbx(struct chan_call *call, struct ast_channel *ast, int c call->state = CHAN_LCR_STATE_RELEASE; ast_hangup(ast); // call will be destroyed here return; - + start: /* send setup to asterisk */ CDEBUG(call, ast, "Starting call to Asterisk due to matching extension.\n"); - #ifdef LCR_FOR_CALLWEAVER + #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) { cause = (ret==-2)?34:27; @@ -859,7 +860,7 @@ static void lcr_in_setup(struct chan_call *call, int message_type, union paramet #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 */ CERROR(call, NULL, "Failed to create Asterisk channel - releasing.\n"); @@ -873,7 +874,7 @@ static void lcr_in_setup(struct chan_call *call, int message_type, union paramet ast->tech_pvt = call; ast->tech = &lcr_tech; ast->fds[0] = call->pipe[0]; - + /* fill setup information */ if (param->setup.dialinginfo.id) strncpy(ast->exten, param->setup.dialinginfo.id, AST_MAX_EXTENSION-1); @@ -1080,8 +1081,8 @@ static void lcr_in_release(struct chan_call *call, int message_type, union param call->state = CHAN_LCR_STATE_RELEASE; /* copy release info */ if (!call->cause) { - call->cause = param->disconnectinfo.cause; - call->location = param->disconnectinfo.location; + call->cause = param->disconnectinfo.cause; + call->location = param->disconnectinfo.location; } /* if we have an asterisk instance, queue hangup, else we are done */ if (ast) { @@ -1099,7 +1100,7 @@ static void lcr_in_release(struct chan_call *call, int message_type, union param } else { free_call(call); } - + } /* @@ -1110,7 +1111,7 @@ static void lcr_in_information(struct chan_call *call, int message_type, union p struct ast_channel *ast = call->ast; CDEBUG(call, call->ast, "Incoming information from LCR. (dialing=%s)\n", param->information.id); - + if (!ast) return; /* pbx not started */ @@ -1120,14 +1121,14 @@ static void lcr_in_information(struct chan_call *call, int message_type, union p lcr_start_pbx(call, ast, param->information.sending_complete); return; } - + /* change dailing state after setup */ if (call->state == CHAN_LCR_STATE_IN_SETUP) { CDEBUG(call, call->ast, "Changing from SETUP to DIALING state.\n"); call->state = CHAN_LCR_STATE_IN_DIALING; // ast_setstate(ast, AST_STATE_DIALING); } - + /* queue digits */ if (call->state == CHAN_LCR_STATE_IN_DIALING && param->information.id[0]) { if (!wake_global) { @@ -1303,6 +1304,10 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) CDEBUG(call, call->ast, "Join bchannel, because call is already bridged.\n"); bchannel_join(bchannel, call->bridge_id); } + /* ignore all dsp features, if it is a loopback interface */ + if (param->bchannel.isloopback) + call->nodsp = 1; + /* 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), call->nodsp_queue)) bchannel_activate(bchannel, 1); @@ -1333,7 +1338,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) newparam.bchannel.type = BCHANNEL_REMOVE_ACK; newparam.bchannel.handle = param->bchannel.handle; send_message(MESSAGE_BCHANNEL, 0, &newparam); - + break; default: @@ -1344,7 +1349,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) /* handle new ref */ if (message_type == MESSAGE_NEWREF) { - if (param->direction) { + if (param->newref.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)) { @@ -1527,7 +1532,7 @@ static int handle_socket(struct lcr_fd *fd, unsigned int what, void *instance, i /* read from socket */ len = read(lcr_sock, &msg, sizeof(msg)); if (len == 0) { - CERROR(NULL, NULL, "Socket closed.\n"); + CERROR(NULL, NULL, "Socket closed.(read)\n"); error: CERROR(NULL, NULL, "Handling of socket failed - closing for some seconds.\n"); close_socket(); @@ -1560,7 +1565,7 @@ static int handle_socket(struct lcr_fd *fd, unsigned int what, void *instance, i admin = admin_first; len = write(lcr_sock, &admin->msg, sizeof(msg)); if (len == 0) { - CERROR(NULL, NULL, "Socket closed.\n"); + CERROR(NULL, NULL, "Socket closed.(write)\n"); goto error; } if (len > 0) { @@ -1638,7 +1643,7 @@ void close_socket(void) admin_first = NULL; /* close socket */ - if (lcr_sock >= 0) + if (lcr_sock >= 0) close(lcr_sock); lcr_sock = -1; global_change = 1; @@ -1706,7 +1711,7 @@ again: 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 @@ -1714,16 +1719,16 @@ again: #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); @@ -1805,7 +1810,7 @@ struct ast_channel *lcr_request(const char *type, int format, void *data, int *c { char exten[256], *dial, *interface, *opt; struct ast_channel *ast; - struct chan_call *call; + struct chan_call *call; ast_mutex_lock(&chan_lock); CDEBUG(NULL, NULL, "Received request from Asterisk. (data=%s)\n", (char *)data); @@ -1830,11 +1835,11 @@ struct ast_channel *lcr_request(const char *type, int format, void *data, int *c #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"); free_call(call); @@ -1894,16 +1899,16 @@ struct ast_channel *lcr_request(const char *type, int format, void *data, int *c static int lcr_call(struct ast_channel *ast, char *dest, int timeout) { union parameter newparam; - struct chan_call *call; + struct chan_call *call; 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 - + 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); @@ -1916,7 +1921,9 @@ static int lcr_call(struct ast_channel *ast, char *dest, int timeout) call->pbx_started = 1; /* send MESSAGE_NEWREF */ memset(&newparam, 0, sizeof(union parameter)); - newparam.direction = 0; /* request from app */ + newparam.newref.direction = 0; /* request from app */ + if (!strcmp(call->interface, "pbx")) + newparam.newref.mode = 1; send_message(MESSAGE_NEWREF, 0, &newparam); /* set hdlc if capability requires hdlc */ @@ -1940,49 +1947,49 @@ static int lcr_call(struct ast_channel *ast, char *dest, int timeout) sizeof(call->cid_num)-1); if (ast->cid.cid_name) if (ast->cid.cid_name[0]) - strncpy(call->cid_name, ast->cid.cid_name, + strncpy(call->cid_name, ast->cid.cid_name, sizeof(call->cid_name)-1); if (ast->cid.cid_rdnis) if (ast->cid.cid_rdnis[0]) - strncpy(call->cid_rdnis, ast->cid.cid_rdnis, + strncpy(call->cid_rdnis, ast->cid.cid_rdnis, sizeof(call->cid_rdnis)-1); ast_mutex_unlock(&chan_lock); - return 0; + 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 */ + 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 */ CDEBUG(NULL, ast, "Unable to handle DTMF tone " "'%c' for '%s'\n", digit, ast->name); - } + } } #ifdef LCR_FOR_ASTERISK @@ -1992,7 +1999,7 @@ static int lcr_digit_begin(struct ast_channel *ast, char digit) static int lcr_digit(struct ast_channel *ast, char digit) #endif { - struct chan_call *call; + struct chan_call *call; union parameter newparam; char buf[]="x"; @@ -2005,7 +2012,7 @@ static int lcr_digit(struct ast_channel *ast, char digit) return 0; ast_mutex_lock(&chan_lock); - call = ast->tech_pvt; + call = ast->tech_pvt; if (!call) { CERROR(NULL, ast, "Received digit from Asterisk, but no call instance exists.\n"); ast_mutex_unlock(&chan_lock); @@ -2043,17 +2050,17 @@ static int lcr_digit(struct ast_channel *ast, char digit) static int lcr_digit_end(struct ast_channel *ast, char digit, unsigned int duration) { int inband_dtmf = 0; - struct chan_call *call; + struct chan_call *call; #endif ast_mutex_lock(&chan_lock); - call = ast->tech_pvt; + call = ast->tech_pvt; if (!call) { - CERROR(NULL, ast, - "Received digit from Asterisk, " - "but no call instance exists.\n"); + CERROR(NULL, ast, + "Received digit from Asterisk, " + "but no call instance exists.\n"); ast_mutex_unlock(&chan_lock); return -1; } @@ -2077,18 +2084,18 @@ static int lcr_digit_end(struct ast_channel *ast, char digit, unsigned int durat static int lcr_answer(struct ast_channel *ast) { union parameter newparam; - struct chan_call *call; + struct chan_call *call; ast_mutex_lock(&chan_lock); - call = ast->tech_pvt; + call = ast->tech_pvt; if (!call) { CERROR(NULL, ast, "Received answer from Asterisk, but no call instance exists.\n"); ast_mutex_unlock(&chan_lock); return -1; } - + CDEBUG(call, ast, "Received answer from Asterisk (maybe during lcr_bridge).\n"); - + /* copy connectinfo, if bridged */ if (call->bridge_call) memcpy(&call->connectinfo, &call->bridge_call->connectinfo, sizeof(struct connect_info)); @@ -2110,20 +2117,20 @@ static int lcr_answer(struct ast_channel *ast) /* enable keypad */ // memset(&newparam, 0, sizeof(union parameter)); // send_message(MESSAGE_ENABLEKEYPAD, call->ref, &newparam); - - ast_mutex_unlock(&chan_lock); - return 0; + + ast_mutex_unlock(&chan_lock); + return 0; } static int lcr_hangup(struct ast_channel *ast) { - struct chan_call *call; + struct chan_call *call; pthread_t tid = pthread_self(); if (!pthread_equal(tid, chan_tid)) { ast_mutex_lock(&chan_lock); } - call = ast->tech_pvt; + 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)) { @@ -2165,7 +2172,7 @@ static int lcr_hangup(struct ast_channel *ast) call->state = CHAN_LCR_STATE_RELEASE; call->ast = NULL; } - } + } if (!pthread_equal(tid, chan_tid)) { ast_mutex_unlock(&chan_lock); } @@ -2174,14 +2181,14 @@ static int lcr_hangup(struct ast_channel *ast) static int lcr_write(struct ast_channel *ast, struct ast_frame *fr) { - struct chan_call *call; + struct chan_call *call; struct ast_frame * f = fr; if (!f->subclass) CDEBUG(NULL, ast, "No subclass\n"); if (!(f->subclass & ast->nativeformats)) { CDEBUG(NULL, ast, - "Unexpected format. " + "Unexpected format. " "Activating emergency conversion...\n"); ast_set_write_format(ast, f->subclass); @@ -2190,7 +2197,7 @@ static int lcr_write(struct ast_channel *ast, struct ast_frame *fr) } ast_mutex_lock(&chan_lock); - call = ast->tech_pvt; + call = ast->tech_pvt; if (!call) { ast_mutex_unlock(&chan_lock); if (f != fr) { @@ -2210,11 +2217,11 @@ static int lcr_write(struct ast_channel *ast, struct ast_frame *fr) static struct ast_frame *lcr_read(struct ast_channel *ast) { - struct chan_call *call; + struct chan_call *call; int len = 0; ast_mutex_lock(&chan_lock); - call = ast->tech_pvt; + call = ast->tech_pvt; if (!call) { ast_mutex_unlock(&chan_lock); return NULL; @@ -2235,11 +2242,11 @@ static struct ast_frame *lcr_read(struct ast_channel *ast) #ifdef LCR_FOR_ASTERISK return &ast_null_frame; #endif - + #ifdef LCR_FOR_CALLWEAVER return &nullframe; #endif - + } if (len <= 0) { close(call->pipe[0]); @@ -2274,20 +2281,20 @@ static struct ast_frame *lcr_read(struct ast_channel *ast) static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, size_t datalen) { union parameter newparam; - int res = 0; - struct chan_call *call; + int res = 0; + struct chan_call *call; const struct tone_zone_sound *ts = NULL; ast_mutex_lock(&chan_lock); - call = ast->tech_pvt; + call = ast->tech_pvt; if (!call) { CERROR(NULL, ast, "Received indicate from Asterisk, but no call instance exists.\n"); ast_mutex_unlock(&chan_lock); return -1; } - switch (cond) { - case AST_CONTROL_BUSY: + switch (cond) { + case AST_CONTROL_BUSY: CDEBUG(call, ast, "Received indicate AST_CONTROL_BUSY from Asterisk.\n"); ast_setstate(ast, AST_STATE_BUSY); if (call->state != CHAN_LCR_STATE_OUT_DISCONNECT) { @@ -2303,7 +2310,7 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz ts = ast_get_indication_tone(ast->zone, "busy"); } break; - case AST_CONTROL_CONGESTION: + case AST_CONTROL_CONGESTION: CDEBUG(call, ast, "Received indicate AST_CONTROL_CONGESTION from Asterisk. (cause %d)\n", ast->hangupcause); if (call->state != CHAN_LCR_STATE_OUT_DISCONNECT) { /* send message to lcr */ @@ -2318,7 +2325,7 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz ts = ast_get_indication_tone(ast->zone, "congestion"); } break; - case AST_CONTROL_PROCEEDING: + case AST_CONTROL_PROCEEDING: CDEBUG(call, ast, "Received indicate AST_CONTROL_PROCEEDING from Asterisk.\n"); if (call->state == CHAN_LCR_STATE_IN_SETUP || call->state == CHAN_LCR_STATE_IN_DIALING) { @@ -2329,7 +2336,7 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz call->state = CHAN_LCR_STATE_IN_PROCEEDING; } break; - case AST_CONTROL_RINGING: + case AST_CONTROL_RINGING: CDEBUG(call, ast, "Received indicate AST_CONTROL_RINGING from Asterisk.\n"); ast_setstate(ast, AST_STATE_RING); if (call->state == CHAN_LCR_STATE_IN_SETUP @@ -2355,35 +2362,35 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz send_message(MESSAGE_BCHANNEL, call->ref, &newparam); } break; - case -1: + case -1: CDEBUG(call, ast, "Received indicate -1.\n"); ast_playtones_stop(ast); - res = -1; + res = -1; break; - case AST_CONTROL_VIDUPDATE: + case AST_CONTROL_VIDUPDATE: CDEBUG(call, ast, "Received indicate AST_CONTROL_VIDUPDATE.\n"); - res = -1; - break; - case AST_CONTROL_HOLD: + res = -1; + break; + case AST_CONTROL_HOLD: CDEBUG(call, ast, "Received indicate AST_CONTROL_HOLD from Asterisk.\n"); /* send message to lcr */ memset(&newparam, 0, sizeof(union parameter)); newparam.notifyinfo.notify = INFO_NOTIFY_REMOTE_HOLD; 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: + break; + case AST_CONTROL_UNHOLD: CDEBUG(call, ast, "Received indicate AST_CONTROL_UNHOLD from Asterisk.\n"); /* send message to lcr */ memset(&newparam, 0, sizeof(union parameter)); @@ -2391,21 +2398,21 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz send_message(MESSAGE_NOTIFY, call->ref, &newparam); /*stop moh*/ - ast_moh_stop(ast); + ast_moh_stop(ast); call->on_hold = 0; - break; + break; #ifdef AST_CONTROL_SRCUPDATE - case AST_CONTROL_SRCUPDATE: + case AST_CONTROL_SRCUPDATE: #else - case 20: + case 20: #endif CDEBUG(call, ast, "Received AST_CONTROL_SRCUPDATE from Asterisk.\n"); - break; - default: + break; + default: CERROR(call, ast, "Received indicate from Asterisk with unknown condition %d.\n", cond); - res = -1; + res = -1; break; - } + } if (ts && ts->data[0]) { ast_playtones_start(ast, 0, ts->data, 1); @@ -2413,7 +2420,7 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz /* return */ ast_mutex_unlock(&chan_lock); - return res; + return res; } /* @@ -2421,7 +2428,7 @@ static int lcr_indicate(struct ast_channel *ast, int cond, const void *data, siz */ static int lcr_fixup(struct ast_channel *oldast, struct ast_channel *ast) { - struct chan_call *call; + struct chan_call *call; if (!ast) { return -1; @@ -2446,7 +2453,7 @@ static int lcr_fixup(struct ast_channel *oldast, struct ast_channel *ast) */ static int lcr_send_text(struct ast_channel *ast, const char *text) { - struct chan_call *call; + struct chan_call *call; union parameter newparam; ast_mutex_lock(&chan_lock); @@ -2495,15 +2502,15 @@ 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"); /* get bridge id and join */ bridge_id = new_bridge_id(); - + call1->bridge_id = bridge_id; if (call1->bchannel) bchannel_join(call1->bchannel, bridge_id); @@ -2557,9 +2564,9 @@ enum ast_bridge_result lcr_bridge(struct ast_channel *ast1, call2->on_hold = 0; } - + ast_mutex_unlock(&chan_lock); - + while(1) { to = -1; who = ast_waitfor_n(carr, 2, &to); @@ -2569,7 +2576,7 @@ enum ast_bridge_result lcr_bridge(struct ast_channel *ast1, break; } f = ast_read(who); - + if (!f || f->frametype == AST_FRAME_CONTROL) { if (!f) CDEBUG(NULL, NULL, "Got hangup.\n"); @@ -2580,14 +2587,14 @@ enum ast_bridge_result lcr_bridge(struct ast_channel *ast1, *rc=who; break; } - + if ( f->frametype == AST_FRAME_DTMF ) { CDEBUG(NULL, NULL, "Got DTMF.\n"); *fo=f; *rc=who; break; } - + if (who == ast1) { ast_write(ast2,f); @@ -2595,9 +2602,9 @@ enum ast_bridge_result lcr_bridge(struct ast_channel *ast1, else { ast_write(ast1,f); } - + } - + CDEBUG(NULL, NULL, "Releasing bridge.\n"); /* split channels */ @@ -2640,7 +2647,7 @@ static struct ast_channel_tech lcr_tech = { #endif .call = lcr_call, - .bridge = lcr_bridge, + .bridge = lcr_bridge, .hangup = lcr_hangup, .answer = lcr_answer, .read = lcr_read, @@ -2757,11 +2764,11 @@ static int lcr_config_exec(struct ast_channel *ast, void *data, char **argv) #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) { @@ -2770,12 +2777,12 @@ static int lcr_config_exec(struct ast_channel *ast, void *data, char **argv) call = call->next; } if (call) - + #ifdef LCR_FOR_ASTERISK apply_opt(call, (char *)data); - #endif - - #ifdef LCR_FOR_CALLWEAVER + #endif + + #ifdef LCR_FOR_CALLWEAVER apply_opt(call, (char *)argv[0]); #endif @@ -2804,12 +2811,12 @@ int load_module(void) #ifdef LCR_FOR_ASTERISK return AST_MODULE_LOAD_DECLINE; - #endif - + #endif + #ifdef LCR_FOR_CALLWEAVER return 0; #endif - + } ast_mutex_init(&chan_lock); @@ -2818,11 +2825,11 @@ 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 - + #endif + #ifdef LCR_FOR_CALLWEAVER return 0; #endif @@ -2837,29 +2844,29 @@ int load_module(void) #ifdef LCR_FOR_ASTERISK return AST_MODULE_LOAD_DECLINE; - #endif - + #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", + "lcr_config(::...)\n", #endif - + "Sets LCR opts. and optargs\n" "\n" "The available options are:\n" " d - Send display text on called phone, text is the optarg.\n" " n - Don't detect dtmf tones on called channel.\n" - " h - Force data call (HDLC).\n" + " 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" @@ -2882,8 +2889,8 @@ int load_module(void) "options: \"n:t:q250\" for seamless audio transmission.\n" ); - -#if 0 + +#if 0 ast_cli_register(&cli_show_lcr); ast_cli_register(&cli_show_calls); ast_cli_register(&cli_reload_routing); @@ -2893,7 +2900,7 @@ int load_module(void) ast_cli_register(&cli_port_unload); #endif - quit = 0; + quit = 0; if ((pthread_create(&chan_tid, NULL, chan_thread, NULL)<0)) { /* failed to create thread */ bchannel_deinitialize(); @@ -2902,12 +2909,12 @@ int load_module(void) #ifdef LCR_FOR_ASTERISK return AST_MODULE_LOAD_DECLINE; - #endif - + #endif + #ifdef LCR_FOR_CALLWEAVER return 0; #endif - + } return 0; } @@ -2918,11 +2925,11 @@ int unload_module(void) CDEBUG(NULL, NULL, "-- Unregistering mISDN Channel Driver --\n"); quit = 1; - pthread_join(chan_tid, NULL); - + pthread_join(chan_tid, NULL); + ast_channel_unregister(&lcr_tech); - ast_unregister_application("lcr_config"); + ast_unregister_application("lcr_config"); if (mISDN_created) { diff --git a/default/gsm.conf b/default/gsm.conf index 148513b..713a620 100644 --- a/default/gsm.conf +++ b/default/gsm.conf @@ -1,15 +1,7 @@ # LCR GSM options ################# -# Two Loopback interfaces for audio transfer between OpenBSC and mISDN. -# They are also used for any Osmocom-BB interface, if exists. -# The first interface must provide B-channelis for each call mobile call. -# The seond interface links them to LCR. -# Use 30 B-channels unless you need more due to many TRXs or mobile stations. -# -> Load with: "modprobe mISDN_l1loop pri=1 nchannel=30" -# By default "mISDN_l1loop.1" and "mISDN_l1loop.2" is used. -#interface-bsc mISDN_l1loop.1 -#interface-lcr mISDN_l1loop.2 +# interfaces-bsc and interface-lcr has been moved to options.conf! # Enable debugging of OpenBSC library. # Refer to OpenBSC project for debugging options. diff --git a/default/interface.conf b/default/interface.conf index 6e19538..4c80fed 100644 --- a/default/interface.conf +++ b/default/interface.conf @@ -191,6 +191,20 @@ ##external +# Use chan_lcr (Asterisk PBX interface) as internal interface. +# The interface requires mISDN_l1loop.ko to be loaded: +# modprobe mISDN_l1loop nchannel=8 # use up to 8 b-channels +# The caller ID is used as extension, if "extension" parameter is given. +# Use "screen-in % xxx" to modify any caller id to xxx. +# An internal extension does not receive tones ("earlyb"), but sends them. +#[ast] +#remote asterisk +#extension +##screen-in % 209 +#earlyb no +#tones yes + + # Hint: Enter "lcr interface" for quick help on interface options. diff --git a/default/options.conf b/default/options.conf index e0e93a0..cb48ea7 100644 --- a/default/options.conf +++ b/default/options.conf @@ -120,3 +120,13 @@ # Enable polling in main loop. # This feature is temporarily for test purpose. Don't enable it #polling + +# Two Loopback interfaces for audio transfer between GSM/Asterisk and mISDN. +# The first interface must provide B-channels for each GSM call or channel +# instance, the seond interface links them to LCR. +# Use 30 B-channels unless you need more due to more instances. +# -> Load with: "modprobe mISDN_l1loop pri=1 nchannel=30" +# By default "mISDN_l1loop.1" and "mISDN_l1loop.2" is used. +#loopback-ext mISDN_l1loop.1 +#loopback-lcr mISDN_l1loop.2 + diff --git a/gsm.cpp b/gsm.cpp index 7245632..e7216dd 100644 --- a/gsm.cpp +++ b/gsm.cpp @@ -119,7 +119,7 @@ void Pgsm::bchannel_close(void) static int b_handler(struct lcr_fd *fd, unsigned int what, void *instance, int index); -/* open bsc side bchannel */ +/* open external side bchannel */ int Pgsm::bchannel_open(int index) { int ret; @@ -295,70 +295,10 @@ void gsm_trace_header(struct mISDNport *mISDNport, class PmISDN *port, unsigned msgtext); } -/* select bchannel */ +/* select free bchannel from loopback interface */ int Pgsm::hunt_bchannel(void) { - int channel; - int i; - char map[p_m_mISDNport->b_num]; - struct interface *interface; - struct interface_port *ifport; - - chan_trace_header(p_m_mISDNport, this, "CHANNEL SELECTION (setup)", DIRECTION_NONE); - add_trace("channel", "reserved", "%d", p_m_mISDNport->b_reserved); - if (p_m_mISDNport->b_reserved >= p_m_mISDNport->b_num) { // of out chan.. - add_trace("conclusion", NULL, "all channels are reserved"); - end_trace(); - return(-34); // no channel - } - - /* map all used ports of shared loopback interface */ - memset(map, 0, sizeof(map)); - interface = interface_first; - while(interface) { - ifport = interface->ifport; - while(ifport) { -#if defined WITH_GSM_BS && defined WITH_GSM_MS - if ((ifport->gsm_bs || ifport->gsm_ms) && ifport->mISDNport) { -#else -#ifdef WITH_GSM_BS - if (ifport->gsm_bs && ifport->mISDNport) { -#endif -#ifdef WITH_GSM_MS - if (ifport->gsm_ms && ifport->mISDNport) { -#endif -#endif - i = 0; - while(i < p_m_mISDNport->b_num) { - if (p_m_mISDNport->b_port[i]) - map[i] = 1; - i++; - } - } - ifport = ifport->next; - } - interface = interface->next; - } - - /* find channel */ - i = 0; - channel = 0; - while(i < p_m_mISDNport->b_num) { - if (!map[i]) { - channel = i+1+(i>=15); - break; - } - i++; - } - if (!channel) { - add_trace("conclusion", NULL, "no channel available"); - end_trace(); - return(-6); // channel unacceptable - } - add_trace("conclusion", NULL, "channel available"); - add_trace("connect", "channel", "%d", channel); - end_trace(); - return(channel); + return loop_hunt_bchannel(this, p_m_mISDNport); } /* PROCEEDING INDICATION */ @@ -884,87 +824,10 @@ static int b_handler(struct lcr_fd *fd, unsigned int what, void *instance, int i return 0; } -static void gsm_sock_close(void) -{ - if (gsm->gsm_sock > -1) - close(gsm->gsm_sock); - gsm->gsm_sock = -1; -} - -static int gsm_sock_open(char *portname) -{ - int ret; - int cnt; - unsigned long on = 1; - struct sockaddr_mISDN addr; - struct mISDN_devinfo devinfo; - int pri, bri; - - /* check port counts */ - ret = ioctl(mISDNsocket, IMGETCOUNT, &cnt); - if (ret < 0) { - fprintf(stderr, "Cannot get number of mISDN devices. (ioctl IMGETCOUNT failed ret=%d)\n", ret); - return(ret); - } - - if (cnt <= 0) { - PERROR_RUNTIME("Found no card. Please be sure to load card drivers.\n"); - return -EIO; - } - gsm->gsm_port = mISDN_getportbyname(mISDNsocket, cnt, portname); - if (gsm->gsm_port < 0) { - PERROR_RUNTIME("Port name '%s' not found, did you load loopback interface for GSM?.\n", portname); - return gsm->gsm_port; - } - /* get protocol */ - bri = pri = 0; - devinfo.id = gsm->gsm_port; - ret = ioctl(mISDNsocket, IMGETDEVINFO, &devinfo); - if (ret < 0) { - PERROR_RUNTIME("Cannot get device information for port %d. (ioctl IMGETDEVINFO failed ret=%d)\n", gsm->gsm_port, ret); - return ret; - } - if (devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) { - bri = 1; - } - if (devinfo.Dprotocols & (1 << ISDN_P_TE_E1)) { - pri = 1; - } - if (!pri && !pri) { - PERROR_RUNTIME("GSM port %d does not support TE PRI or TE BRI.\n", gsm->gsm_port); - } - /* open socket */ - if ((gsm->gsm_sock = socket(PF_ISDN, SOCK_DGRAM, (pri)?ISDN_P_TE_E1:ISDN_P_TE_S0)) < 0) { - PERROR_RUNTIME("GSM port %d failed to open socket.\n", gsm->gsm_port); - gsm_sock_close(); - return gsm->gsm_sock; - } - /* set nonblocking io */ - if ((ret = ioctl(gsm->gsm_sock, FIONBIO, &on)) < 0) { - PERROR_RUNTIME("GSM port %d failed to set socket into nonblocking io.\n", gsm->gsm_port); - gsm_sock_close(); - return ret; - } - /* bind socket to dchannel */ - memset(&addr, 0, sizeof(addr)); - addr.family = AF_ISDN; - addr.dev = gsm->gsm_port; - addr.channel = 0; - if ((ret = bind(gsm->gsm_sock, (struct sockaddr *)&addr, sizeof(addr))) < 0) { - PERROR_RUNTIME("GSM port %d failed to bind socket. (name = %s errno=%d)\n", gsm->gsm_port, portname, errno); - gsm_sock_close(); - return (ret); - } - - return 0; -} - int gsm_exit(int rc) { /* free gsm instance */ if (gsm) { - if (gsm->gsm_sock > -1) - gsm_sock_close(); free(gsm); gsm = NULL; } @@ -996,7 +859,7 @@ int gsm_init(void) } /* open gsm loop interface */ - if (gsm_sock_open(gsm->conf.interface_bsc)) { + if (loopback_open()) { return gsm_exit(-1); } diff --git a/gsm.h b/gsm.h index 71ed759..de4c02b 100644 --- a/gsm.h +++ b/gsm.h @@ -2,8 +2,6 @@ extern int new_callref; struct gsm_conf { char debug[128]; /* debug info */ - char interface_bsc[64]; /* loopback interface BSC side */ - char interface_lcr[64]; /* loopback interface LCR side */ char openbsc_cfg[128]; /* openbsc config file */ char short_name[64]; /* short network name */ char long_name[64]; /* long network name */ @@ -17,8 +15,6 @@ struct gsm_conf { struct lcr_gsm { void *network; /* OpenBSC network handle */ struct gsm_conf conf; /* gsm.conf options */ - int gsm_sock; /* loopback interface GSM side */ - int gsm_port; /* loopback interface port number */ }; extern struct lcr_gsm *gsm; diff --git a/gsm_conf.c b/gsm_conf.c index a64f2ff..f25cf85 100644 --- a/gsm_conf.c +++ b/gsm_conf.c @@ -29,8 +29,6 @@ int gsm_conf(struct gsm_conf *gsm_conf, char *conf_error) /* set defaults */ SCPY(gsm_conf->debug, ""); - SCPY(gsm_conf->interface_bsc, "mISDN_l1loop.1"); - SCPY(gsm_conf->interface_lcr, "mISDN_l1loop.2"); SCPY(gsm_conf->hlr, "hlr.sqlite3"); SCPY(gsm_conf->openbsc_cfg, "openbsc.cfg"); gsm_conf->reject_cause = 0; @@ -105,22 +103,6 @@ int gsm_conf(struct gsm_conf *gsm_conf, char *conf_error) SCPY(gsm_conf->debug, params[0]); } else - if (!strcmp(option,"interface-bsc")) { - if (params[0][0]==0) { - UPRINT(conf_error, "Error in %s (line %d): parameter for option %s missing.\n",filename,line, option); - goto error; - } - SCPY(gsm_conf->interface_bsc, params[0]); - - } else - if (!strcmp(option,"interface-lcr")) { - if (params[0][0]==0) { - UPRINT(conf_error, "Error in %s (line %d): parameter for option %s missing.\n",filename,line, option); - goto error; - } - SCPY(gsm_conf->interface_lcr, params[0]); - - } else if (!strcmp(option,"config")) { if (params[0][0]==0) { UPRINT(conf_error, "Error in %s (line %d): parameter for option %s missing.\n",filename,line, option); diff --git a/interface.c b/interface.c index a312e4e..448affc 100644 --- a/interface.c +++ b/interface.c @@ -327,9 +327,7 @@ static int inter_portname(struct interface *interface, char *filename, int line, /* check for port already assigned, but not for shared gsm interface */ searchif = interface_newlist; -#if defined WITH_GSM_BS || defined WITH_GSM_MS - if (!strcmp(value, gsm->conf.interface_lcr)) -#endif + if (!strcmp(value, options.loopback_lcr)) { while(searchif) { ifport = searchif->ifport; @@ -918,7 +916,7 @@ static int inter_gsm_bs(struct interface *interface, char *filename, int line, c } /* set portname */ - if (inter_portname(interface, filename, line, (char *)"portname", gsm->conf.interface_lcr)) + if (inter_portname(interface, filename, line, (char *)"portname", options.loopback_lcr)) return(-1); /* goto end of chain again to set gsmflag */ @@ -947,7 +945,7 @@ static int inter_gsm_ms(struct interface *interface, char *filename, int line, c } /* set portname */ - if (inter_portname(interface, filename, line, (char *)"portname", gsm->conf.interface_lcr)) + if (inter_portname(interface, filename, line, (char *)"portname", options.loopback_lcr)) return(-1); /* goto end of chain again to set gsmflag and socket */ @@ -1054,6 +1052,42 @@ static int inter_ss5(struct interface *interface, char *filename, int line, char return(0); } #endif +static int inter_remote(struct interface *interface, char *filename, int line, char *parameter, char *value) +{ + struct interface_port *ifport; + struct interface *searchif; + + if (!value[0]) { + SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects application name as value.\n", filename, line, parameter); + return(-1); + } + searchif = interface_newlist; + while(searchif) { + ifport = searchif->ifport; + while(ifport) { + if (ifport->remote && !strcmp(ifport->remote_app, value)) { + SPRINT(interface_error, "Error in %s (line %d): port '%s' already uses remote application '%s'.\n", filename, line, ifport->portname, value); + return(-1); + } + ifport = ifport->next; + } + searchif = searchif->next; + } + + /* set portname */ + if (inter_portname(interface, filename, line, (char *)"portname", options.loopback_lcr)) + return(-1); + + /* goto end of chain again to set application name */ + ifport = interface->ifport; + while(ifport->next) + ifport = ifport->next; + ifport->remote = 1; + SCPY(ifport->remote_app, value); + + + return(0); +} /* @@ -1215,6 +1249,10 @@ struct interface_param interface_param[] = { " suppress - Suppress received tones, as they will be recognized."}, #endif + {"remote", &inter_remote, "", + "Sets up an interface that communicates with the remote application.\n" + "Use \"asterisk\" to use chan_lcr as remote application."}, + {NULL, NULL, NULL, NULL} }; diff --git a/interface.h b/interface.h index d1bea04..eea6915 100644 --- a/interface.h +++ b/interface.h @@ -61,6 +61,8 @@ struct interface_port { char gsm_ms_service; /* see GSM_SERVICE_* */ #endif unsigned int ss5; /* set, if SS5 signalling enabled, also holds feature bits */ + int remote; /* interface is a remote app interface */ + char remote_app[32]; /* name of remote application */ int channel_force; /* forces channel by protocol */ int nodtmf; /* disables DTMF */ struct select_channel *out_channel; /* list of channels to select */ diff --git a/joinremote.cpp b/joinremote.cpp index 167bbce..c02de8e 100644 --- a/joinremote.cpp +++ b/joinremote.cpp @@ -14,6 +14,7 @@ //#define __u16 unsigned short //#define __u32 unsigned int +extern unsigned int new_remote; /* * constructor for a new join @@ -27,6 +28,7 @@ JoinRemote::JoinRemote(unsigned int serial, char *remote_name, int remote_id) : SCPY(j_remote_name, remote_name); j_remote_id = remote_id; j_type = JOIN_TYPE_REMOTE; + j_remote_ref = new_remote++; j_epoint_id = serial; /* this is the endpoint, if created by epoint */ if (j_epoint_id) @@ -35,9 +37,8 @@ JoinRemote::JoinRemote(unsigned int serial, char *remote_name, int remote_id) : /* send new ref to remote socket */ memset(¶m, 0, sizeof(union parameter)); if (serial) - param.direction = 1; /* new ref from lcr */ - /* the j_serial is assigned by Join() parent. this is sent as new ref */ - if (admin_message_from_join(j_remote_id, j_serial, MESSAGE_NEWREF, ¶m)<0) + param.newref.direction = 1; /* new ref from lcr */ + if (admin_message_from_lcr(j_remote_id, j_remote_ref, MESSAGE_NEWREF, ¶m)<0) FATAL("No socket with remote application '%s' found, this shall not happen. because we already created one.\n", j_remote_name); } @@ -56,7 +57,7 @@ void JoinRemote::message_epoint(unsigned int epoint_id, int message_type, union return; /* look for Remote's interface */ - if (admin_message_from_join(j_remote_id, j_serial, message_type, param)<0) { + if (admin_message_from_lcr(j_remote_id, j_remote_ref, message_type, param)<0) { PERROR("No socket with remote application '%s' found, this shall not happen. Closing socket shall cause release of all joins.\n", j_remote_name); return; } @@ -100,25 +101,5 @@ void JoinRemote::message_remote(int message_type, union parameter *param) } } -void message_bchannel_to_remote(unsigned int remote_id, unsigned int ref, int type, unsigned int handle, int tx_gain, int rx_gain, char *pipeline, unsigned char *crypt, int crypt_len, int crypt_type) -{ - union parameter param; - - memset(¶m, 0, sizeof(union parameter)); - param.bchannel.type = type; - param.bchannel.handle = handle; - param.bchannel.tx_gain = tx_gain; - param.bchannel.rx_gain = rx_gain; - if (pipeline) - SCPY(param.bchannel.pipeline, pipeline); - if (crypt_len) - memcpy(param.bchannel.crypt, crypt, crypt_len); - param.bchannel.crypt_type = crypt_type; - if (admin_message_from_join(remote_id, ref, MESSAGE_BCHANNEL, ¶m)<0) { - PERROR("No socket with remote id %d found, this happens, if the socket is closed before all bchannels are imported.\n", remote_id); - return; - } -} - diff --git a/joinremote.h b/joinremote.h index 1582133..a933466 100644 --- a/joinremote.h +++ b/joinremote.h @@ -17,10 +17,10 @@ class JoinRemote : public Join void message_epoint(unsigned int epoint_id, int message, union parameter *param); void message_remote(int message_type, union parameter *param); + unsigned int j_remote_ref; int j_remote_id; char j_remote_name[32]; unsigned int j_epoint_id; }; -void message_bchannel_to_remote(unsigned int remote_id, unsigned int ref, int type, unsigned int handle, int tx_gain, int rx_gain, char *pipeline, unsigned char *crypt, int crypt_len, int crypt_type); diff --git a/loop.c b/loop.c new file mode 100644 index 0000000..cbd82e5 --- /dev/null +++ b/loop.c @@ -0,0 +1,153 @@ +/*****************************************************************************\ +** ** +** LCR ** +** ** +**---------------------------------------------------------------------------** +** Copyright: Andreas Eversberg ** +** ** +** loopback interface functions ** +** ** +\*****************************************************************************/ + +#include "main.h" + +struct mISDNloop mISDNloop = { -1, 0 }; + +void mISDNloop_close(void) +{ + if (mISDNloop.sock > -1) + close(mISDNloop.sock); + mISDNloop.sock = -1; +} + +int mISDNloop_open() +{ + int ret; + int cnt; + unsigned long on = 1; + struct sockaddr_mISDN addr; + struct mISDN_devinfo devinfo; + int pri, bri; + + /* already open */ + if (mISDNloop.sock > -1) + return 0; + + PDEBUG(DEBUG_PORT, "Open external interface of loopback.\n"); + + /* check port counts */ + ret = ioctl(mISDNsocket, IMGETCOUNT, &cnt); + if (ret < 0) { + fprintf(stderr, "Cannot get number of mISDN devices. (ioctl IMGETCOUNT failed ret=%d)\n", ret); + return(ret); + } + + if (cnt <= 0) { + PERROR_RUNTIME("Found no card. Please be sure to load card drivers.\n"); + return -EIO; + } + mISDNloop.port = mISDN_getportbyname(mISDNsocket, cnt, options.loopback_ext); + if (mISDNloop.port < 0) { + PERROR_RUNTIME("Port name '%s' not found, did you load loopback interface?.\n", options.loopback_ext); + return mISDNloop.port; + } + /* get protocol */ + bri = pri = 0; + devinfo.id = mISDNloop.port; + ret = ioctl(mISDNsocket, IMGETDEVINFO, &devinfo); + if (ret < 0) { + PERROR_RUNTIME("Cannot get device information for port %d. (ioctl IMGETDEVINFO failed ret=%d)\n", mISDNloop.port, ret); + return ret; + } + if (devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) { + bri = 1; + } + if (devinfo.Dprotocols & (1 << ISDN_P_TE_E1)) { + pri = 1; + } + if (!bri && !pri) { + PERROR_RUNTIME("loop port %d does not support TE PRI or TE BRI.\n", mISDNloop.port); + } + /* open socket */ + if ((mISDNloop.sock = socket(PF_ISDN, SOCK_DGRAM, (pri)?ISDN_P_TE_E1:ISDN_P_TE_S0)) < 0) { + PERROR_RUNTIME("loop port %d failed to open socket.\n", mISDNloop.port); + mISDNloop_close(); + return mISDNloop.sock; + } + /* set nonblocking io */ + if ((ret = ioctl(mISDNloop.sock, FIONBIO, &on)) < 0) { + PERROR_RUNTIME("loop port %d failed to set socket into nonblocking io.\n", mISDNloop.port); + mISDNloop_close(); + return ret; + } + /* bind socket to dchannel */ + memset(&addr, 0, sizeof(addr)); + addr.family = AF_ISDN; + addr.dev = mISDNloop.port; + addr.channel = 0; + if ((ret = bind(mISDNloop.sock, (struct sockaddr *)&addr, sizeof(addr))) < 0) { + PERROR_RUNTIME("loop port %d failed to bind socket. (name = %s errno=%d)\n", mISDNloop.port, options.loopback_ext, errno); + mISDNloop_close(); + return (ret); + } + + return 0; +} + +int loop_hunt_bchannel(class PmISDN *port, struct mISDNport *mISDNport) +{ + int channel; + int i; + char map[mISDNport->b_num]; + struct interface *interface; + struct interface_port *ifport; + + chan_trace_header(mISDNport, port, "CHANNEL SELECTION (setup)", DIRECTION_NONE); + add_trace("channel", "reserved", "%d", mISDNport->b_reserved); + if (mISDNport->b_reserved >= mISDNport->b_num) { // of out chan.. + add_trace("conclusion", NULL, "all channels are reserved"); + end_trace(); + return(-34); // no channel + } + + /* map all used ports of shared loopback interface */ + memset(map, 0, sizeof(map)); + interface = interface_first; + while(interface) { + ifport = interface->ifport; + while(ifport) { + if (!strcmp(ifport->portname, options.loopback_lcr)) { + i = 0; + while(i < mISDNport->b_num) { + if (mISDNport->b_port[i]) + map[i] = 1; + i++; + } + } + ifport = ifport->next; + } + interface = interface->next; + } + + /* find channel */ + i = 0; + channel = 0; + while(i < mISDNport->b_num) { + if (!map[i]) { + channel = i+1+(i>=15); + break; + } + i++; + } + if (!channel) { + add_trace("conclusion", NULL, "no channel available"); + end_trace(); + return(-6); // channel unacceptable + } + add_trace("conclusion", NULL, "channel available"); + add_trace("connect", "channel", "%d", channel); + end_trace(); + return(channel); +} + + diff --git a/loop.h b/loop.h new file mode 100644 index 0000000..cb2ad05 --- /dev/null +++ b/loop.h @@ -0,0 +1,12 @@ + +struct mISDNloop { + int sock; /* loopback interface external side */ + int port; /* port number for external side */ +}; + +extern mISDNloop mISDNloop; + +void mISDNloop_close(void); +int mISDNloop_open(); +int loop_hunt_bchannel(class PmISDN *port, struct mISDNport *mISDNport); + diff --git a/mISDN.cpp b/mISDN.cpp index ee50bf1..087963c 100644 --- a/mISDN.cpp +++ b/mISDN.cpp @@ -33,21 +33,6 @@ int __af_isdn = MISDN_AF_ISDN; #define B_TIMER_ACTIVATING 1 // seconds #define B_TIMER_DEACTIVATING 1 // seconds -/* GSM condition */ -#if defined WITH_GSM_BS && defined WITH_GSM_MS - #define MISDNPORT_GSM (mISDNport->gsm_bs || mISDNport->gsm_ms) -#else - #ifdef WITH_GSM_BS - #define MISDNPORT_GSM (mISDNport->gsm_bs) - #endif - #ifdef WITH_GSM_MS - #define MISDNPORT_GSM (mISDNport->gsm_ms) - #endif -#endif -#ifndef MISDNPORT_GSM - #define MISDNPORT_GSM (0) -#endif - /* list of mISDN ports */ struct mISDNport *mISDNport_first; @@ -671,7 +656,7 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) case B_STATE_IDLE: if (p_m_remote_ref) { /* export bchannel */ - message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type); + message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type, 0); chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE); add_trace("type", NULL, "assign"); add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff); @@ -719,7 +704,7 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) /* in case, the bchannel is exported right after seize_bchannel */ /* export bchannel */ /* p_m_remote_id is set, when this event happens. */ - message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type); + message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type, 0); chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE); add_trace("type", NULL, "assign"); add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff); @@ -774,7 +759,7 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) case B_STATE_REMOTE: /* bchannel is exported, so we re-import */ - message_bchannel_to_remote(mISDNport->b_remote_id[i], 0, BCHANNEL_REMOVE, portid, 0,0,0,0,0,0); + message_bchannel_to_remote(mISDNport->b_remote_id[i], 0, BCHANNEL_REMOVE, portid, 0,0,0,0,0,0, 0); chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE); add_trace("type", NULL, "remove"); add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff); @@ -825,7 +810,7 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) * OR bchannel is not used anymore * OR bchannel has been exported to an obsolete ref, * so reimport, to later export to new remote */ - message_bchannel_to_remote(mISDNport->b_remote_id[i], 0, BCHANNEL_REMOVE, portid, 0,0,0,0,0,0); + message_bchannel_to_remote(mISDNport->b_remote_id[i], 0, BCHANNEL_REMOVE, portid, 0,0,0,0,0,0, 0); chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE); add_trace("type", NULL, "remove"); add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff); @@ -861,7 +846,7 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) case B_STATE_REMOTE: /* bchannel is exported, so we re-import */ - message_bchannel_to_remote(mISDNport->b_remote_id[i], 0, BCHANNEL_REMOVE, portid, 0,0,0,0,0,0); + message_bchannel_to_remote(mISDNport->b_remote_id[i], 0, BCHANNEL_REMOVE, portid, 0,0,0,0,0,0, 0); chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE); add_trace("type", NULL, "remove"); add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff); @@ -892,7 +877,7 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) if (b_port) { /* bchannel is now deactivate, but is requied by Port class, so we reactivate / export */ if (p_m_remote_ref) { - message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type); + message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type, 0); chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE); add_trace("type", NULL, "assign"); add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff); @@ -924,7 +909,7 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) if (b_port) { /* bchannel is now imported, but is requied by Port class, so we reactivate / export */ if (p_m_remote_ref) { - message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type); + message_bchannel_to_remote(p_m_remote_id, p_m_remote_ref, BCHANNEL_ASSIGN, portid, p_m_tx_gain, p_m_rx_gain, p_m_pipeline, p_m_crypt_key, p_m_crypt_key_len, p_m_crypt_key_type, 0); chan_trace_header(mISDNport, b_port, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE); add_trace("type", NULL, "assign"); add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff); @@ -1856,7 +1841,7 @@ static int mISDN_upqueue(struct lcr_fd *fd, unsigned int what, void *instance, i mISDNport = mISDNport_first; while(mISDNport) { /* handle queued up-messages (d-channel) */ - if (!MISDNPORT_GSM) { + if (!mISDNport->isloopback) { while ((mb = mdequeue(&mISDNport->upqueue))) { l3m = &mb->l3; switch(l3m->type) { @@ -1932,7 +1917,7 @@ static int mISDN_upqueue(struct lcr_fd *fd, unsigned int what, void *instance, i if (!mISDNport->ntmode || mISDNport->ptp) mISDNport->l2link = 0; } - if (!MISDNPORT_GSM && (!mISDNport->ntmode || mISDNport->ptp) && l3m->pid < 127) { + if (!mISDNport->isloopback && (!mISDNport->ntmode || mISDNport->ptp) && l3m->pid < 127) { if (!mISDNport->l2establish.active && mISDNport->l2hold) { PDEBUG(DEBUG_ISDN, "set timer and establish.\n"); schedule_timer(&mISDNport->l2establish, 5, 0); @@ -1959,7 +1944,7 @@ static int l2establish_timeout(struct lcr_timer *timer, void *instance, int i) { struct mISDNport *mISDNport = (struct mISDNport *)instance; - if (!MISDNPORT_GSM && mISDNport->l2hold && (mISDNport->ptp || !mISDNport->ntmode)) { + if (!mISDNport->isloopback && mISDNport->l2hold && (mISDNport->ptp || !mISDNport->ntmode)) { // PDEBUG(DEBUG_ISDN, "the L2 establish timer expired, we try to establish the link portnum=%d.\n", mISDNport->portnum); mISDNport->ml3->to_layer3(mISDNport->ml3, MT_L2ESTABLISH, 0, NULL); schedule_timer(&mISDNport->l2establish, 5, 0); /* 5 seconds */ @@ -2119,7 +2104,7 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport) int force_nt = ifport->nt; int l1hold = ifport->l1hold; int l2hold = ifport->l2hold; - int gsm = 0; + int loop = 0; int ss5 = ifport->ss5; int i, cnt; int pri, bri, pots; @@ -2129,15 +2114,23 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport) unsigned int protocol, prop; #if defined WITH_GSM_BS && defined WITH_GSM_MS - gsm = ifport->gsm_ms | ifport->gsm_bs; + loop = ifport->gsm_ms | ifport->gsm_bs; #else #ifdef WITH_GSM_BS - gsm = ifport->gsm_bs; + loop = ifport->gsm_bs; #endif #ifdef WITH_GSM_MS - gsm = ifport->gsm_ms; + loop = ifport->gsm_ms; #endif #endif +//printf("%s == %s\n", ifport->portname, options.loopback_int); + if (!strcmp(ifport->portname, options.loopback_lcr)) + loop = 1; + + if (loop) { + if (mISDNloop_open()) + return NULL; + } /* check port counts */ ret = ioctl(mISDNsocket, IMGETCOUNT, &cnt); @@ -2153,8 +2146,8 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport) if (port < 0) { port = mISDN_getportbyname(mISDNsocket, cnt, ifport->portname); if (port < 0) { - if (gsm) - PERROR_RUNTIME("Port name '%s' not found, did you load loopback interface for GSM?.\n", ifport->portname); + if (loop) + PERROR_RUNTIME("Port name '%s' not found, did you load loopback interface?.\n", ifport->portname); else PERROR_RUNTIME("Port name '%s' not found, use 'misdn_info' tool to list all existing ports.\n", ifport->portname); return(NULL); @@ -2270,8 +2263,8 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport) mISDNportp = &((*mISDNportp)->next); mISDNport = (struct mISDNport *)MALLOC(sizeof(struct mISDNport)); add_timer(&mISDNport->l2establish, l2establish_timeout, mISDNport, 0); - if (gsm | ss5) { - /* gsm/ss5 link is always active */ + if (loop | ss5) { + /* loop/ss5 link is always active */ mISDNport->l1link = 1; mISDNport->l2link = 1; } else { @@ -2284,6 +2277,7 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport) #ifdef WITH_GSM_MS mISDNport->gsm_ms = ifport->gsm_ms; #endif + mISDNport->isloopback = loop; pmemuse++; *mISDNportp = mISDNport; @@ -2325,25 +2319,24 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport) if (l2hold) // supports layer 2 hold prop |= (1 << MISDN_FLG_L2_HOLD); /* open layer 3 and init upqueue */ - if (gsm) { -#if defined WITH_GSM_BS || defined WITH_GSM_MS + if (loop) { unsigned long on = 1; struct sockaddr_mISDN addr; if (devinfo.nrbchan < 8) { - PERROR_RUNTIME("GSM port %d must have at least 8 b-channels.\n", port); - mISDNport_close(mISDNport); - return(NULL); + printf("loop port %d has a low number of bchannels. (only %d) remember that all interfaces that requires a loopback could run out of channels\n", port, devinfo.nrbchan); +// mISDNport_close(mISDNport); +// return(NULL); } - if ((mISDNport->lcr_sock = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_NT_S0)) < 0) { - PERROR_RUNTIME("GSM port %d failed to open socket.\n", port); + if ((mISDNport->lcr_sock = socket(PF_ISDN, SOCK_DGRAM, (bri) ? ISDN_P_TE_S0 : ISDN_P_TE_E1)) < 0) { + PERROR_RUNTIME("loop port %d failed to open socket.\n", port); mISDNport_close(mISDNport); return(NULL); } /* set nonblocking io */ if (ioctl(mISDNport->lcr_sock, FIONBIO, &on) < 0) { - PERROR_RUNTIME("GSM port %d failed to set socket into nonblocking io.\n", port); + PERROR_RUNTIME("loop port %d failed to set socket into nonblocking io.\n", port); mISDNport_close(mISDNport); return(NULL); } @@ -2353,11 +2346,10 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport) addr.dev = port; addr.channel = 0; if (bind(mISDNport->lcr_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - PERROR_RUNTIME("GSM port %d failed to bind socket. (errno %d)\n", port, errno); + PERROR_RUNTIME("loop port %d failed to bind socket. (errno %d)\n", port, errno); mISDNport_close(mISDNport); return(NULL); } -#endif } else { /* queue must be initializes, because l3-thread may send messages during open_layer3() */ mqueue_init(&mISDNport->upqueue); @@ -2398,7 +2390,7 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport) } /* if ptp, pull up the link */ - if (!MISDNPORT_GSM && mISDNport->l2hold && (mISDNport->ptp || !mISDNport->ntmode)) { + if (!mISDNport->isloopback && mISDNport->l2hold && (mISDNport->ptp || !mISDNport->ntmode)) { mISDNport->ml3->to_layer3(mISDNport->ml3, MT_L2ESTABLISH, 0, NULL); l1l2l3_trace_header(mISDNport, NULL, L2_ESTABLISH_REQ, DIRECTION_OUT); add_trace("tei", NULL, "%d", 0); @@ -2511,19 +2503,17 @@ void mISDNport_close(struct mISDNport *mISDNport) del_timer(&mISDNport->l2establish); /* close layer 3, if open */ - if (!MISDNPORT_GSM && mISDNport->ml3) { + if (!mISDNport->isloopback && mISDNport->ml3) { close_layer3(mISDNport->ml3); } -#if defined WITH_GSM_BS || defined WITH_GSM_MS /* close gsm socket, if open */ - if (MISDNPORT_GSM && mISDNport->lcr_sock > -1) { + if (mISDNport->isloopback && mISDNport->lcr_sock > -1) { close(mISDNport->lcr_sock); } -#endif /* purge upqueue */ - if (!MISDNPORT_GSM) + if (!mISDNport->isloopback) mqueue_purge(&mISDNport->upqueue); /* remove from list */ diff --git a/mISDN.h b/mISDN.h index 644658b..de223c8 100644 --- a/mISDN.h +++ b/mISDN.h @@ -71,9 +71,8 @@ struct mISDNport { #ifdef WITH_GSM_MS int gsm_ms; /* this is the an GSM MS interface */ #endif -#if defined WITH_GSM_BS || defined WITH_GSM_MS int lcr_sock; /* socket of loopback on LCR side */ -#endif + int isloopback; /* will be set on open, in case it is a loopback if */ /* ss5 */ unsigned int ss5; /* set, if SS5 signalling enabled, also holds feature bits */ diff --git a/main.c b/main.c index 98be1c0..0cc68a4 100644 --- a/main.c +++ b/main.c @@ -602,6 +602,10 @@ free: #endif } + /* close loopback, if used by GSM or remote */ + if (mISDNloop.sock > -1) + mISDNloop_close(); + /* display memory leak */ #define MEMCHECK(a, b) \ if (b) { \ diff --git a/main.h b/main.h index 8e45581..7aad3c2 100644 --- a/main.h +++ b/main.h @@ -148,6 +148,8 @@ extern "C" { #include "port.h" #include "mISDN.h" #include "dss1.h" +#include "loop.h" +#include "remote.h" #if defined WITH_GSM_BS || defined WITH_GSM_MS #include "gsm.h" #endif diff --git a/message.h b/message.h index 471e2c8..c2076af 100644 --- a/message.h +++ b/message.h @@ -324,6 +324,7 @@ struct param_hello { struct param_bchannel { int type; /* BCHANNEL_* */ unsigned int handle; /* bchannel stack/portid */ + int isloopback; /* in this case the application behaves like an interface, dsp should not be used */ int tx_gain, rx_gain; char pipeline[256]; unsigned char crypt[128]; @@ -331,6 +332,11 @@ struct param_bchannel { int crypt_type; /* 1 = blowfish */ }; +struct param_newref { + int direction; /* who requests a refe? */ + int mode; /* 0 = direct-mode, 1 = PBX mode */ +}; + /* structure of message parameter */ union parameter { struct param_tone tone; /* MESSAGE_TONE */ @@ -355,7 +361,7 @@ union parameter { struct param_crypt crypt; /* MESSAGE_CRYPT */ struct param_hello hello; /* MESSAGE_HELLO */ struct param_bchannel bchannel; /* MESSAGE_BCHANNEL */ - int direction; /* MESSAGE_NEWREF */ + struct param_newref newref; /* MESSAGE_NEWREF */ }; enum { /* message flow */ @@ -428,6 +434,7 @@ enum { /* messages between entities */ "MESSAGE_RELEASE", \ "MESSAGE_TIMEOUT", \ "MESSAGE_NOTIFY", \ + "MESSAGE_PROGRESS", \ "MESSAGE_FACILITY", \ "MESSAGE_SUSPEND", \ "MESSAGE_RESUME", \ diff --git a/options.c b/options.c index 88f6424..7fb9bcb 100644 --- a/options.c +++ b/options.c @@ -36,7 +36,9 @@ struct options options = { -1, /* socket user (-1= no change) */ -1, /* socket group (-1= no change) */ 0, /* enable gsm */ - 1 /* use polling of main loop */ + 1, /* use polling of main loop */ + "mISDN_l1loop.1", /* GSM/Asterisk side */ + "mISDN_l1loop.2", /* LCR side */ }; char options_error[256]; @@ -239,6 +241,22 @@ int read_options(char *options_error) } else if (!strcmp(option,"polling")) { options.polling = 1; + } else + if (!strcmp(option,"loopback-ext")) { + if (param[0]==0) { + UPRINT(options_error, "Error in %s (line %d): parameter for option %s missing.\n",filename,line, option); + goto error; + } + SCPY(options.loopback_ext, param); + + } else + if (!strcmp(option,"loopback-lcr")) { + if (param[0]==0) { + UPRINT(options_error, "Error in %s (line %d): parameter for option %s missing.\n",filename,line, option); + goto error; + } + SCPY(options.loopback_lcr, param); + } else { UPRINT(options_error, "Error in %s (line %d): wrong option keyword %s.\n", filename,line,option); goto error; diff --git a/options.h b/options.h index c13ee2c..c7f9f8f 100644 --- a/options.h +++ b/options.h @@ -31,6 +31,8 @@ struct options { int socketgroup; /* socket chgrp to this group */ int gsm; /* enable gsm support */ int polling; + char loopback_ext[64]; /* loopback interface GSM side */ + char loopback_lcr[64]; /* loopback interface LCR side */ }; extern struct options options; diff --git a/port.h b/port.h index d7c0580..a4da7de 100644 --- a/port.h +++ b/port.h @@ -22,10 +22,14 @@ #define PORT_CLASS_GSM_BS 0x1210 #define PORT_CLASS_GSM_MS 0x1220 #define PORT_CLASS_SS5 0x1300 +#define PORT_CLASS_REMOTE 0x1400 #define PORT_CLASS_MASK 0xf000 #define PORT_CLASS_mISDN_MASK 0xff00 #define PORT_CLASS_DSS1_MASK 0xfff0 #define PORT_CLASS_GSM_MASK 0xfff0 +#define PORT_CLASS_DIR_MASK 0x000f +#define PORT_CLASS_DIR_IN 0x0001 +#define PORT_CLASS_DIR_OUT 0x0002 /* nt-mode */ #define PORT_TYPE_DSS1_NT_IN 0x1111 #define PORT_TYPE_DSS1_NT_OUT 0x1112 @@ -41,6 +45,9 @@ #define PORT_TYPE_SS5_IN 0x1311 #define PORT_TYPE_SS5_OUT 0x1312 #define PORT_TYPE_SS5_IDLE 0x1313 + /* remote */ +#define PORT_TYPE_REMOTE_IN 0x1411 +#define PORT_TYPE_REMOTE_OUT 0x1412 /* answering machine */ #define PORT_TYPE_VBOX_OUT 0x3111 diff --git a/remote.cpp b/remote.cpp new file mode 100644 index 0000000..e14c8e3 --- /dev/null +++ b/remote.cpp @@ -0,0 +1,221 @@ +/*****************************************************************************\ +** ** +** LCR ** +** ** +**---------------------------------------------------------------------------** +** Copyright: Andreas Eversberg ** +** ** +** mISDN remote ** +** ** +\*****************************************************************************/ + +#include "main.h" + +unsigned int new_remote = 0x00000001; + +/* + * constructor + */ +Premote::Premote(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode, int remote_id) : PmISDN(type, mISDNport, portname, settings, channel, exclusive, mode) +{ + union parameter param; + + p_callerinfo.itype = (mISDNport->ifport->interface->extension)?INFO_ITYPE_ISDN_EXTENSION:INFO_ITYPE_ISDN; + p_m_r_ref = new_remote++; + SCPY(p_m_r_remote_app, mISDNport->ifport->remote_app); + p_m_r_handle = 0; + + /* send new ref to remote socket */ + memset(¶m, 0, sizeof(union parameter)); + if (type == PORT_TYPE_REMOTE_OUT) + param.newref.direction = 1; /* new ref from lcr */ + p_m_r_remote_id = remote_id; + if (admin_message_from_lcr(p_m_r_remote_id, p_m_r_ref, MESSAGE_NEWREF, ¶m) < 0) + FATAL("No socket with remote application '%s' found, this shall not happen. because we already created one.\n", mISDNport->ifport->remote_app); + + PDEBUG(DEBUG_GSM, "Created new RemotePort(%s).\n", portname); + +} + +/* + * destructor + */ +Premote::~Premote() +{ + /* need to remote (import) external channel from remote application */ + if (p_m_r_handle) { + message_bchannel_to_remote(p_m_r_remote_id, p_m_r_ref, BCHANNEL_REMOVE, p_m_r_handle, 0, 0, 0, 0, 0, 0, 1); + chan_trace_header(p_m_mISDNport, this, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE); + add_trace("type", NULL, "remove"); + add_trace("channel", NULL, "%d.%d", p_m_r_handle>>8, p_m_r_handle&0xff); + end_trace(); + } + + PDEBUG(DEBUG_GSM, "Destroyed Remote process(%s).\n", p_name); + +} + +/* + * endpoint sends messages to the port + */ +int Premote::message_epoint(unsigned int epoint_id, int message_type, union parameter *param) +{ + struct lcr_msg *message; + int channel; + int ret; + struct epoint_list *epointlist; + + if (PmISDN::message_epoint(epoint_id, message_type, param)) + return 1; + + if (message_type == MESSAGE_SETUP) { + ret = channel = hunt_bchannel(); + if (ret < 0) + goto no_channel; + /* open channel */ + ret = seize_bchannel(channel, 1); + if (ret < 0) { + no_channel: + message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE); + message->param.disconnectinfo.cause = 34; + message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + message_put(message); + new_state(PORT_STATE_RELEASE); + delete this; + return 0; + } + bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE); + + /* attach only if not already */ + epointlist = p_epointlist; + while(epointlist) { + if (epointlist->epoint_id == epoint_id) + break; + epointlist = epointlist->next; + } + if (!epointlist) + epointlist_new(epoint_id); + + /* set context to pbx */ + SCPY(param->setup.context, "pbx"); + } + + /* look for Remote's interface */ + if (admin_message_from_lcr(p_m_r_remote_id, p_m_r_ref, message_type, param)<0) { + PERROR("No socket with remote application '%s' found, this shall not happen. Closing socket shall cause release of all remote ports.\n", p_m_mISDNport->ifport->remote_app); + return 0; + } + + /* enable audio path */ + if (message_type == MESSAGE_SETUP) { + union parameter newparam; + memset(&newparam, 0, sizeof(union parameter)); + admin_message_from_lcr(p_m_r_remote_id, p_m_r_ref, MESSAGE_PATTERN, &newparam); + newparam.audiopath = 1; + admin_message_from_lcr(p_m_r_remote_id, p_m_r_ref, MESSAGE_AUDIOPATH, &newparam); + } + + if (message_type == MESSAGE_RELEASE) { + new_state(PORT_STATE_RELEASE); + delete this; + return 0; + } + + return 0; +} + +void Premote::message_remote(int message_type, union parameter *param) +{ + class Endpoint *epoint; + struct lcr_msg *message; + int channel; + int ret; + + if (message_type == MESSAGE_SETUP) { + /* enable audio path */ + union parameter newparam; + memset(&newparam, 0, sizeof(union parameter)); + admin_message_from_lcr(p_m_r_remote_id, p_m_r_ref, MESSAGE_PATTERN, &newparam); + newparam.audiopath = 1; + admin_message_from_lcr(p_m_r_remote_id, p_m_r_ref, MESSAGE_AUDIOPATH, &newparam); + + /* set source interface */ + param->setup.callerinfo.itype = p_callerinfo.itype; + param->setup.callerinfo.isdn_port = p_m_portnum; + SCPY(param->setup.callerinfo.interface, p_m_mISDNport->ifport->interface->name); + + ret = channel = hunt_bchannel(); + if (ret < 0) + goto no_channel; + + /* open channel */ + ret = seize_bchannel(channel, 1); + if (ret < 0) { + no_channel: + message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE); + message->param.disconnectinfo.cause = 34; + message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + message_put(message); + new_state(PORT_STATE_RELEASE); + delete this; + return; + } + bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE); + + /* create endpoint */ + if (p_epointlist) + FATAL("Incoming call but already got an endpoint.\n"); + if (!(epoint = new Endpoint(p_serial, 0))) + FATAL("No memory for Endpoint instance\n"); + if (!(epoint->ep_app = new DEFAULT_ENDPOINT_APP(epoint, 0))) //incoming + FATAL("No memory for Endpoint Application instance\n"); + + epointlist_new(epoint->ep_serial); + } + + /* set serial on bchannel message + * also ref is given, so we send message with ref */ + if (message_type == MESSAGE_BCHANNEL) { + int i = p_m_b_index; + unsigned int portid = (mISDNloop.port<<8) + i+1+(i>=15); + switch (param->bchannel.type) { + case BCHANNEL_REQUEST: + p_m_r_handle = portid; + message_bchannel_to_remote(p_m_r_remote_id, p_m_r_ref, BCHANNEL_ASSIGN, portid, 0, 0, 0, 0, 0, 0, 1); + chan_trace_header(p_m_mISDNport, this, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE); + add_trace("type", NULL, "assign"); + add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff); + end_trace(); + break; + case BCHANNEL_RELEASE: + p_m_r_handle = 0; + message_bchannel_to_remote(p_m_r_remote_id, p_m_r_ref, BCHANNEL_REMOVE, portid, 0, 0, 0, 0, 0, 0, 1); + chan_trace_header(p_m_mISDNport, this, "MESSAGE_BCHANNEL (to remote application)", DIRECTION_NONE); + add_trace("type", NULL, "remove"); + add_trace("channel", NULL, "%d.%d", portid>>8, portid&0xff); + end_trace(); + break; + } + return; + } + + /* cannot just forward, because param is not of container "struct lcr_msg" */ + message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, message_type); + memcpy(&message->param, param, sizeof(message->param)); + message_put(message); + + if (message_type == MESSAGE_RELEASE) { + new_state(PORT_STATE_RELEASE); + delete this; + return; + } +} + +/* select free bchannel from loopback interface */ +int Premote::hunt_bchannel(void) +{ + return loop_hunt_bchannel(this, p_m_mISDNport); +} + + + diff --git a/remote.h b/remote.h new file mode 100644 index 0000000..f39245b --- /dev/null +++ b/remote.h @@ -0,0 +1,20 @@ + +/* GSM port class */ +class Premote : public PmISDN +{ + public: + Premote(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode, int remote_id); + ~Premote(); + + unsigned int p_m_r_ref; + int p_m_r_remote_id; /* remote instance (socket) */ + char p_m_r_remote_app[32]; + unsigned int p_m_r_handle; /* 0, if no bchannel is exported */ + + int message_epoint(unsigned int epoint_id, int message_id, union parameter *param); + void message_remote(int message_type, union parameter *param); + + int hunt_bchannel(void); +}; + + diff --git a/socket_server.c b/socket_server.c index e84fa5e..9922dc6 100644 --- a/socket_server.c +++ b/socket_server.c @@ -19,6 +19,7 @@ char socket_name[128]; int sock = -1; struct sockaddr_un sock_address; +extern unsigned int new_remote; struct admin_list *admin_first = NULL; static struct lcr_fd admin_fd; @@ -84,6 +85,8 @@ void free_connection(struct admin_list *admin) struct mISDNport *mISDNport; int i, ii; struct admin_list **adminp; + class Port *port, *portnext; + class Premote *remote; /* free remote joins */ if (admin->remote_name[0]) { @@ -126,6 +129,22 @@ void free_connection(struct admin_list *admin) } join = joinnext; } + /* release remote port */ + port = port_first; + while(port) { + portnext = port->next; + if ((port->p_type & PORT_CLASS_MASK) == PORT_CLASS_REMOTE) { + remote = (class Premote *) port; + if (remote->p_m_r_remote_id == admin->sock) { + memset(¶m, 0, sizeof(param)); + param.disconnectinfo.cause = CAUSE_OUTOFORDER; + param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + remote->message_remote(MESSAGE_RELEASE, ¶m); + /* port is now destroyed, so we go to next join */ + } + } + port = portnext; + } } if (admin->sock >= 0) { @@ -586,9 +605,13 @@ void admin_call_response(int adminid, int message, const char *connected, int ca /* * send data to the remote socket join instance */ -int admin_message_to_join(struct admin_msg *msg, struct admin_list *admin) +int admin_message_to_lcr(struct admin_msg *msg, struct admin_list *admin) { + struct mISDNport *mISDNport; class Join *join; + class JoinRemote *joinremote = NULL; /* make GCC happy */ + class Port *port; + class Premote *remote = NULL; /* make GCC happy */ struct admin_list *temp; /* hello message */ @@ -629,13 +652,41 @@ int admin_message_to_join(struct admin_msg *msg, struct admin_list *admin) return(-1); } - /* new join */ + /* new join. the reply (NEWREF assignment) is sent from constructor */ if (msg->type == MESSAGE_NEWREF) { - /* create new join instance */ - join = new JoinRemote(0, admin->remote_name, admin->sock); // must have no serial, because no endpoint is connected - if (!join) { - FATAL("No memory for remote join instance\n"); - return(-1); + if (msg->param.newref.mode) { + char name[32]; + /* find remote port */ + mISDNport = mISDNport_first; + while(mISDNport) { + if (mISDNport->ifport->remote && !strcmp(mISDNport->ifport->remote_app, admin->remote_name)) + break; + mISDNport = mISDNport->next; + } + if (!mISDNport) { + unsigned int remote_ref = new_remote++; + union parameter param; + + memset(¶m, 0, sizeof(union parameter)); + admin_message_from_lcr(mISDNport->ifport->remote, remote_ref, MESSAGE_NEWREF, ¶m); + memset(¶m, 0, sizeof(union parameter)); + param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + param.disconnectinfo.cause = CAUSE_RESSOURCEUNAVAIL; + admin_message_from_lcr(mISDNport->ifport->remote, remote_ref, MESSAGE_RELEASE, ¶m); + return 0; + } + /* creating port object, transparent until setup with hdlc */ + SPRINT(name, "%s-%s-in", mISDNport->ifport->interface->name, mISDNport->ifport->remote_app); + if (!(remote = new Premote(PORT_TYPE_REMOTE_IN, mISDNport, name, NULL, 0, 0, B_MODE_TRANSPARENT, admin->sock))) + + FATAL("Cannot create Port instance.\n"); + } else { + /* create new join instance */ + join = new JoinRemote(0, admin->remote_name, admin->sock); // must have no serial, because no endpoint is connected + if (!join) { + FATAL("No memory for remote join instance\n"); + return(-1); + } } return(0); } @@ -646,6 +697,7 @@ int admin_message_to_join(struct admin_msg *msg, struct admin_list *admin) if (msg->param.bchannel.type == BCHANNEL_ASSIGN_ACK || msg->param.bchannel.type == BCHANNEL_REMOVE_ACK || msg->param.bchannel.type == BCHANNEL_RELEASE) { +#warning TODO: depending on the mode (join / remoteport) forward message /* no ref, but address */ message_bchannel_from_remote(NULL, msg->param.bchannel.type, msg->param.bchannel.handle); return(0); @@ -660,36 +712,56 @@ int admin_message_to_join(struct admin_msg *msg, struct admin_list *admin) /* find join instance */ join = join_first; while(join) { - if (join->j_serial == msg->ref) - break; + if (join->j_type != JOIN_TYPE_REMOTE) { + joinremote = (class JoinRemote *)join; + if (joinremote->j_remote_ref == msg->ref) + break; + } join = join->next; } - if (!join) { - PDEBUG(DEBUG_LOG, "No join found with serial %d. (May have been already released.)\n", msg->ref); + if (join) { + if (admin->sock != joinremote->j_remote_id) { + PERROR("Ref %d belongs to remote application %s, but not to sending application %s.\n", msg->ref, joinremote->j_remote_name, admin->remote_name); + return(-1); + } + /* send message */ + joinremote->message_remote(msg->type, &msg->param); + return(0); } - /* check application */ - if (join->j_type != JOIN_TYPE_REMOTE) { - PERROR("Ref %d does not belong to a remote join instance.\n", msg->ref); - return(-1); - } - if (admin->sock != ((class JoinRemote *)join)->j_remote_id) { - PERROR("Ref %d belongs to remote application %s, but not to sending application %s.\n", msg->ref, ((class JoinRemote *)join)->j_remote_name, admin->remote_name); - return(-1); + /* find port instance */ + port = port_first; + while(port) { + if ((port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_REMOTE) { + remote = (class Premote *) port; + if (remote->p_m_r_ref == msg->ref) + break; + } + port = port->next; } + if (port) { + if (admin->sock != remote->p_m_r_remote_id) { + PERROR("Ref %d belongs to remote application %s, but not to sending application %s.\n", msg->ref, remote->p_m_r_remote_app, admin->remote_name); + return(-1); + } + + /* send message */ + remote->message_remote(msg->type, &msg->param); - /* send message */ - ((class JoinRemote *)join)->message_remote(msg->type, &msg->param); + return(0); + } + PDEBUG(DEBUG_LOG, "No remote instance found with ref %d. (May have been already released.)\n", msg->ref); return(0); + } /* * this function is called for every message to remote socket */ -int admin_message_from_join(int remote_id, unsigned int ref, int message_type, union parameter *param) +int admin_message_from_lcr(int remote_id, unsigned int ref, int message_type, union parameter *param) { struct admin_list *admin; struct admin_queue **responsep; /* response pointer */ @@ -1234,7 +1306,7 @@ int admin_handle_con(struct lcr_fd *fd, unsigned int what, void *instance, int i break; case ADMIN_MESSAGE: - if (admin_message_to_join(&msg.u.msg, admin) < 0) { + if (admin_message_to_lcr(&msg.u.msg, admin) < 0) { PERROR("Failed to deliver message for socket %d.\n", admin->sock); goto response_error; } @@ -1281,3 +1353,24 @@ int admin_handle_con(struct lcr_fd *fd, unsigned int what, void *instance, int i return 0; } +void message_bchannel_to_remote(unsigned int remote_id, unsigned int ref, int type, unsigned int handle, int tx_gain, int rx_gain, char *pipeline, unsigned char *crypt, int crypt_len, int crypt_type, int isloopback) +{ + union parameter param; + + memset(¶m, 0, sizeof(union parameter)); + param.bchannel.isloopback = isloopback; + param.bchannel.type = type; + param.bchannel.handle = handle; + param.bchannel.tx_gain = tx_gain; + param.bchannel.rx_gain = rx_gain; + if (pipeline) + SCPY(param.bchannel.pipeline, pipeline); + if (crypt_len) + memcpy(param.bchannel.crypt, crypt, crypt_len); + param.bchannel.crypt_type = crypt_type; + if (admin_message_from_lcr(remote_id, ref, MESSAGE_BCHANNEL, ¶m)<0) { + PERROR("No socket with remote id %d found, this happens, if the socket is closed before all bchannels are imported.\n", remote_id); + return; + } +} + diff --git a/socket_server.h b/socket_server.h index b3ea89a..9a31b10 100644 --- a/socket_server.h +++ b/socket_server.h @@ -33,8 +33,9 @@ extern struct admin_list *admin_first; int admin_init(void); void admin_cleanup(void); void admin_call_response(int adminid, int message, const char *connected, int cause, int location, int notify); -int admin_message_to_join(struct admin_message *msg, int remote_id); -int admin_message_from_join(int remote_id, unsigned int ref, int message_type, union parameter *param); +int admin_message_to_lcr(struct admin_message *msg, int remote_id); +int admin_message_from_lcr(int remote_id, unsigned int ref, int message_type, union parameter *param); +void message_bchannel_to_remote(unsigned int remote_id, unsigned int ref, int type, unsigned int handle, int tx_gain, int rx_gain, char *pipeline, unsigned char *crypt, int crypt_len, int crypt_type, int isloopback); -- 2.13.6