From b0bd74e35e935aa976b68c594def4e8d2c22ef95 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 16 Jan 2010 11:20:23 +0100 Subject: [PATCH] Replaced polling loop for LCR and chan_lcr with select based event loop. Now LCR and chan_lcr will not use any CPU until there is work to do. --- Makefile.am | 7 +- Makefile.in | 43 +++-- README | 2 + action.cpp | 56 +++--- action_vbox.cpp | 98 +++++----- apppbx.cpp | 375 +++++++++++++++++++++----------------- apppbx.h | 27 +-- bchannel.c | 124 ++++++------- bchannel.h | 2 +- chan_lcr.c | 473 ++++++++++++++++++++++++++---------------------- chan_lcr.h | 2 + crypt.cpp | 60 ++++--- crypt.h | 2 +- dss1.cpp | 98 +++++----- dss1.h | 2 +- endpoint.cpp | 23 +-- endpoint.h | 2 +- endpointapp.cpp | 5 - endpointapp.h | 1 - gsm.cpp | 147 +++++++++------ gsm.h | 3 +- join.cpp | 9 - join.h | 1 - joinpbx.cpp | 49 ++--- joinpbx.h | 3 +- joinremote.cpp | 11 -- joinremote.h | 1 - lcradmin.c | 1 + mISDN.cpp | 544 +++++++++++++++++++++++++++++++------------------------- mISDN.h | 18 +- macro.h | 4 +- main.c | 257 +++----------------------- main.h | 6 +- message.c | 80 +++++++++ message.h | 10 +- options.c | 10 +- port.cpp | 27 ++- port.h | 3 +- route.c | 35 +++- select.c | 434 ++++++++++++++++++++++++++++++++++++++++++++ select.h | 62 +++++++ socket_server.c | 194 ++++++++------------ socket_server.h | 2 +- ss5.cpp | 97 +++++----- ss5.h | 4 +- trace.c | 9 +- vbox.cpp | 204 +++++++++++---------- vbox.h | 7 +- 48 files changed, 2103 insertions(+), 1531 deletions(-) create mode 100644 select.c create mode 100644 select.h diff --git a/Makefile.am b/Makefile.am index bce48d1..17f9a29 100644 --- a/Makefile.am +++ b/Makefile.am @@ -72,7 +72,7 @@ if ENABLE_ASTERISK_CHANNEL_DRIVER noinst_PROGRAMS = chan_lcr.so chan_lcr_so_SOURCES = chan_lcr_so_LDFLAGS = -shared -chan_lcr_so_LDADD = chan_lcr.po bchannel.po options.po callerid.po +chan_lcr_so_LDADD = chan_lcr.po bchannel.po options.po callerid.po select.po chan_lcr.po: chan_lcr.c chan_lcr.h $(CC) $(INCLUDES) -D_GNU_SOURCE -fPIC -c chan_lcr.c -o chan_lcr.po @@ -86,6 +86,9 @@ callerid.po: callerid.c callerid.h options.po: options.c options.h $(CC) $(INCLUDES) -D_GNU_SOURCE -fPIC -c options.c -o options.po +select.po: select.c select.h + $(CC) $(INCLUDES) -D_GNU_SOURCE -fPIC -c select.c -o select.po + install-exec-hook: mkdir -p $(astmoddir) $(INSTALL) -d $(astmoddir) @@ -94,7 +97,7 @@ endif INCLUDES = $(all_includes) $(GSM_INCLUDE) $(SS5_INCLUDE) -Wall -I/usr/include/mISDNuser $(INSTALLATION_DEFINES) -lcr_SOURCES = $(GSM_SOURCE) $(SS5_SOURCE) action.cpp mISDN.cpp tones.c \ +lcr_SOURCES = $(GSM_SOURCE) $(SS5_SOURCE) 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 \ diff --git a/Makefile.in b/Makefile.in index 1bf2262..1a2bd77 100644 --- a/Makefile.in +++ b/Makefile.in @@ -61,7 +61,8 @@ am_chan_lcr_so_OBJECTS = chan_lcr_so_OBJECTS = $(am_chan_lcr_so_OBJECTS) @ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@chan_lcr_so_DEPENDENCIES = \ @ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@ chan_lcr.po bchannel.po \ -@ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@ options.po callerid.po +@ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@ options.po callerid.po \ +@ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@ select.po am_genextension_OBJECTS = genext.$(OBJEXT) options.$(OBJEXT) \ extension.$(OBJEXT) genextension_OBJECTS = $(am_genextension_OBJECTS) @@ -78,28 +79,28 @@ genwave_LDADD = $(LDADD) am__lcr_SOURCES_DIST = gsm_audio.c gsm.cpp gsm_conf.c \ openbsc/src/bsc_init.c openbsc/src/vty_interface.c \ openbsc/src/vty_interface_layer3.c ss5.cpp ss5_encode.c \ - ss5_decode.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 \ + 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) bsc_init.$(OBJEXT) \ @ENABLE_GSM_TRUE@ vty_interface.$(OBJEXT) \ @ENABLE_GSM_TRUE@ vty_interface_layer3.$(OBJEXT) @ENABLE_SS5_TRUE@am__objects_2 = ss5.$(OBJEXT) ss5_encode.$(OBJEXT) \ @ENABLE_SS5_TRUE@ ss5_decode.$(OBJEXT) -am_lcr_OBJECTS = $(am__objects_1) $(am__objects_2) 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) +am_lcr_OBJECTS = $(am__objects_1) $(am__objects_2) 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) lcr_OBJECTS = $(am_lcr_OBJECTS) am__DEPENDENCIES_1 = @ENABLE_GSM_TRUE@am__DEPENDENCIES_2 = /usr/lib/libgsm.a \ @@ -258,9 +259,9 @@ INSTALLATION_DEFINES = \ @ENABLE_SS5_TRUE@SS5_SOURCE = ss5.cpp ss5_encode.c ss5_decode.c @ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@chan_lcr_so_SOURCES = @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 +@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 -I/usr/include/mISDNuser $(INSTALLATION_DEFINES) -lcr_SOURCES = $(GSM_SOURCE) $(SS5_SOURCE) action.cpp mISDN.cpp tones.c \ +lcr_SOURCES = $(GSM_SOURCE) $(SS5_SOURCE) 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 \ @@ -446,6 +447,7 @@ distclean-compile: @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)/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@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss5.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss5_decode.Po@am__quote@ @@ -819,6 +821,9 @@ uninstall-am: uninstall-binPROGRAMS uninstall-info-am \ @ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@options.po: options.c options.h @ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@ $(CC) $(INCLUDES) -D_GNU_SOURCE -fPIC -c options.c -o options.po +@ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@select.po: select.c select.h +@ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@ $(CC) $(INCLUDES) -D_GNU_SOURCE -fPIC -c select.c -o select.po + @ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@install-exec-hook: @ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@ mkdir -p $(astmoddir) @ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@ $(INSTALL) -d $(astmoddir) diff --git a/README b/README index c495bd7..2515150 100644 --- a/README +++ b/README @@ -532,6 +532,8 @@ New release Version 1.7 Changes after Version 1.7 - Added new option to interface.conf: "nonotify" to disable notify messages. +- Replaced polling main loop by event driven "select()" loop. +- Also replaced polling main loop by event driven "select()" loop on chan_lcr. diff --git a/action.cpp b/action.cpp index d720dbe..bf7c23d 100644 --- a/action.cpp +++ b/action.cpp @@ -644,16 +644,16 @@ void EndpointAppPBX::action_dialing_login(void) e_ruleset = NULL; e_rule = NULL; e_action = &action_password; - e_match_timeout = 0; + unsched_timer(&e_match_timeout); e_match_to_action = NULL; e_dialinginfo.id[0] = '\0'; e_extdialing = strchr(e_dialinginfo.id, '\0'); /* set timeout */ - e_password_timeout = now+20; + schedule_timer(&e_password_timeout, 20, 0); /* do dialing */ - process_dialing(); + process_dialing(0); } else { /* make call state */ new_state(EPOINT_STATE_IN_OVERLAP); @@ -945,7 +945,7 @@ void EndpointAppPBX::_action_redial_reply(int in) SCPY(e_dialinginfo.id, last); e_extdialing = e_dialinginfo.id; e_action = NULL; - process_dialing(); + process_dialing(0); return; } e_extdialing[0] = '\0'; @@ -1039,10 +1039,10 @@ void EndpointAppPBX::action_dialing_powerdial(void) /* do dialing */ SCPY(e_dialinginfo.id, e_ext.last_out[0]); - e_powerdialing = -1; /* indicates the existence of powerdialing but no redial time given */ + e_powerdial_on = 1; /* indicates the existence of powerdialing but no redial time given */ e_powercount = 0; e_action = NULL; - process_dialing(); + process_dialing(0); } @@ -1144,7 +1144,7 @@ void EndpointAppPBX::action_hangup_callback(void) end_trace(); /* set time to callback */ - e_callback = now_d + delay; + schedule_timer(&e_callback_timeout, delay, 0); } @@ -1197,7 +1197,7 @@ void EndpointAppPBX::action_dialing_abbrev(void) SCPY(e_dialinginfo.id, phone); e_extdialing = e_dialinginfo.id; e_action = NULL; - process_dialing(); + process_dialing(0); } @@ -1701,7 +1701,7 @@ void EndpointAppPBX::_action_goto_menu(int mode) /* do dialing with new ruleset */ e_action = NULL; - process_dialing(); + process_dialing(0); } /* process dialing goto @@ -1839,7 +1839,7 @@ void EndpointAppPBX::action_dialing_help(void) e_extdialing = e_dialinginfo.id+strlen(numbering->prefix); PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal %s selected a new menu '%s' dialing: %s\n", ea_endpoint->ep_serial, e_ext.number, numb_actions[numbering->action], e_dialinginfo.id); nesting?: - process_dialing(); + process_dialing(0); return; } @@ -2098,11 +2098,16 @@ void EndpointAppPBX::action_dialing_password_wr(void) * depending on the detected prefix, subfunctions above (action_*) will be * calles. */ -void EndpointAppPBX::process_dialing(void) +void EndpointAppPBX::process_dialing(int timeout) { struct port_list *portlist = ea_endpoint->ep_portlist; struct lcr_msg *message; struct route_param *rparam; + struct timeval current_time; + + /* set if timeout is active, or if timeout value was given due to timeout action */ + if (e_action_timeout.active) + timeout = 1; //#warning Due to HANG-BUG somewhere here, I added some HANG-BUG-DEBUGGING output that cannot be disabled. after bug has been found, this will be removed. //PDEBUG(~0, "HANG-BUG-DEBUGGING: entered porcess_dialing\n"); @@ -2111,8 +2116,8 @@ void EndpointAppPBX::process_dialing(void) if (!portlist) { portlist_error: PDEBUG(DEBUG_EPOINT, "EPOINT(%d): note: dialing call requires exactly one port object to process dialing. this case could happen due to a parked call. we end dialing here.\n", ea_endpoint->ep_serial, e_ext.number); - e_action_timeout = 0; - e_match_timeout = 0; + unsched_timer(&e_action_timeout); + unsched_timer(&e_match_timeout); return; } if (portlist->next) { @@ -2127,15 +2132,15 @@ void EndpointAppPBX::process_dialing(void) new_state(EPOINT_STATE_OUT_DISCONNECT); message_disconnect_port(portlist, CAUSE_UNSPECIFIED, LOCATION_PRIVATE_LOCAL, ""); set_tone(portlist, "cause_3f"); - e_action_timeout = 0; - e_match_timeout = 0; + unsched_timer(&e_action_timeout); + unsched_timer(&e_match_timeout); goto end; } //PDEBUG(~0, "HANG-BUG-DEBUGGING: before action-timeout processing\n"); /* process timeout */ - if (e_action && e_action_timeout) { /* e_action may be NULL, but e_action_timeout may still be set and must be ignored */ - e_action_timeout = 0; + if (e_action && timeout) { /* e_action may be NULL, but e_action_timeout may still be set and must be ignored */ + unsched_timer(&e_action_timeout); if (e_state == EPOINT_STATE_CONNECT) { PDEBUG(DEBUG_ROUTE|DEBUG_EPOINT, "EPOINT(%d): action timed out, but we already have connected, so we stop timer and continue.\n", ea_endpoint->ep_serial); goto end; @@ -2163,7 +2168,7 @@ void EndpointAppPBX::process_dialing(void) if (e_state!=EPOINT_STATE_IN_SETUP && e_state!=EPOINT_STATE_IN_OVERLAP) { PDEBUG(DEBUG_EPOINT, "EPOINT(%d): we are not in incoming setup/overlap state, so we ignore init/dialing process.\n", ea_endpoint->ep_serial, e_rule_nesting); - e_match_timeout = 0; + unsched_timer(&e_match_timeout); goto end; } @@ -2176,8 +2181,8 @@ void EndpointAppPBX::process_dialing(void) e_dialinginfo.id[0] = '\0'; e_action = NUMB_ACTION_MENU; e_menu = 0; - process_dialing(); - e_match_timeout = 0; + process_dialing(0); + unsched_timer(&e_match_timeout); goto end; } /* invalid dialing */ @@ -2194,7 +2199,7 @@ void EndpointAppPBX::process_dialing(void) } new_state(EPOINT_STATE_OUT_DISCONNECT); set_tone(portlist,"cause_1c"); - e_match_timeout = 0; + unsched_timer(&e_match_timeout); goto end; } #endif @@ -2230,10 +2235,11 @@ void EndpointAppPBX::process_dialing(void) goto process_action; } - if (e_match_timeout && now_d>=e_match_timeout) { + gettimeofday(¤t_time, NULL); + if (timeout && TIME_SMALLER(&e_match_timeout.timeout, ¤t_time)) { /* return timeout rule */ PDEBUG(DEBUG_EPOINT, "EPOINT(%d): terminal '%s' dialing: '%s', timeout in ruleset '%s'\n", ea_endpoint->ep_serial, e_ext.number, e_dialinginfo.id, e_ruleset->name); - e_match_timeout = 0; + unsched_timer(&e_match_timeout); e_action = e_match_to_action; e_extdialing = e_match_to_extdialing; trace_header("ROUTING (timeout)", DIRECTION_NONE); @@ -2272,9 +2278,9 @@ void EndpointAppPBX::process_dialing(void) action_timeout: /* set timeout */ - e_action_timeout = 0; + unsched_timer(&e_action_timeout); if (e_action->timeout) { - e_action_timeout = now_d + e_action->timeout; + schedule_timer(&e_action_timeout, e_action->timeout, 0); PDEBUG(DEBUG_ROUTE|DEBUG_EPOINT, "EPOINT(%d): action has a timeout of %d secods.\n", ea_endpoint->ep_serial, e_action->timeout); } diff --git a/action_vbox.cpp b/action_vbox.cpp index e325c0f..120bfa6 100644 --- a/action_vbox.cpp +++ b/action_vbox.cpp @@ -101,7 +101,7 @@ void EndpointAppPBX::action_init_vbox_play(void) e_vbox_state = VBOX_STATE_MENU; SCPY(e_vbox_display, (char *)((language)?"druecke 2 f. wiedergabe":"press 2 to play")); - e_vbox_display_refresh = 1; + schedule_timer(&e_vbox_refresh, 0, 0); set_tone_vbox("menu"); e_vbox_menu = -1; @@ -112,7 +112,7 @@ void EndpointAppPBX::action_init_vbox_play(void) if (e_vbox_index_num == 0) { e_vbox_state = VBOX_STATE_NOTHING; SCPY(e_vbox_display, (char *)((language)?"keine Anrufe":"no calls")); - e_vbox_display_refresh = 1; + schedule_timer(&e_vbox_refresh, 0, 0); set_tone_vbox("nothing"); } } @@ -232,6 +232,8 @@ void EndpointAppPBX::action_dialing_vbox_play(void) int language = e_ext.vbox_language; struct port_list *portlist; class Port *port; + time_t current_time; + struct tm *current_tm; portlist = ea_endpoint->ep_portlist; @@ -242,7 +244,7 @@ void EndpointAppPBX::action_dialing_vbox_play(void) PDEBUG(DEBUG_EPOINT, "EPOINT(%d) dialing digit: %c\n", ea_endpoint->ep_serial, e_extdialing[0]); - e_vbox_display_refresh = 1; + schedule_timer(&e_vbox_refresh, 0, 0); if (e_vbox_state == VBOX_STATE_RECORD_RECORD) { if (e_extdialing[0] == '1' || e_extdialing[0] == '0') { @@ -280,10 +282,10 @@ void EndpointAppPBX::action_dialing_vbox_play(void) PDEBUG(DEBUG_EPOINT, "EPOINT(%d) play recoding.\n", ea_endpoint->ep_serial); /* play announcement */ e_vbox_counter = 0; - e_vbox_counter_last = 0; e_vbox_counter_max = 0; e_vbox_speed = 1; e_vbox_state = VBOX_STATE_RECORD_PLAY; + schedule_timer(&e_vbox_refresh, 0, 0); if (e_ext.vbox_language) SCPY(e_vbox_display, "Wied., 1=stop %s"); else @@ -396,10 +398,12 @@ void EndpointAppPBX::action_dialing_vbox_play(void) e_vbox_state = VBOX_STATE_CALLINFO_INTRO; SPRINT(e_vbox_display, "#%d", e_vbox_play+1); vbox_index_read(e_vbox_play); - if (e_vbox_index_mon!=now_tm->tm_mon || e_vbox_index_year!=now_tm->tm_year) { + time(¤t_time); + current_tm = localtime(¤t_time); + if (e_vbox_index_mon!=current_tm->tm_mon || e_vbox_index_year!=current_tm->tm_year) { UPRINT(strchr(e_vbox_display,'\0'), " %s", (language)?months_german[e_vbox_index_mon]:months_english[e_vbox_index_mon]); } - if (e_vbox_index_mday!=now_tm->tm_mday || e_vbox_index_mon!=now_tm->tm_mon || e_vbox_index_year!=now_tm->tm_year) { + if (e_vbox_index_mday!=current_tm->tm_mday || e_vbox_index_mon!=current_tm->tm_mon || e_vbox_index_year!=current_tm->tm_year) { UPRINT(strchr(e_vbox_display,'\0'), " %d", e_vbox_index_mday); } UPRINT(strchr(e_vbox_display,'\0'), " %02d:%02d", e_vbox_index_hour, e_vbox_index_min); @@ -419,10 +423,10 @@ void EndpointAppPBX::action_dialing_vbox_play(void) PDEBUG(DEBUG_EPOINT, "EPOINT(%d) play call #%d. abborting announcement and starting with playback\n", ea_endpoint->ep_serial, e_vbox_play+1); /* the callinfo is played, so we start with the call */ e_vbox_counter = 0; - e_vbox_counter_last = 0; e_vbox_counter_max = 0; e_vbox_speed = 1; e_vbox_state = VBOX_STATE_PLAY; + schedule_timer(&e_vbox_refresh, 0, 0); SPRINT(e_vbox_display, "#%d %%s", e_vbox_play+1); if (e_ext.vbox_display == VBOX_DISPLAY_DETAILED) UPRINT(strchr(e_vbox_display,'\0'), " (%s)", e_vbox_index_callerid); @@ -558,7 +562,7 @@ void EndpointAppPBX::action_dialing_vbox_play(void) SPRINT(e_dialinginfo.id, "extern:%s", e_vbox_index_callerid); e_extdialing = e_dialinginfo.id; e_action = NULL; - process_dialing(); + process_dialing(0); return; } break; @@ -600,31 +604,35 @@ void EndpointAppPBX::action_dialing_vbox_play(void) /* * this handler is called by Epoint::handler(), whenever the action is NUMB_ACTION_VBOX_PLAY */ -void EndpointAppPBX::vbox_handler(void) +int vbox_refresh(struct lcr_timer *timer, void *instance, int index) { - /* refresh if counter changes */ - if (e_vbox_state==VBOX_STATE_PLAY || e_vbox_state==VBOX_STATE_RECORD_PLAY) - if (e_vbox_counter != e_vbox_counter_last) { - e_vbox_counter_last = e_vbox_counter; - e_vbox_display_refresh = 1; - } + class EndpointAppPBX *ea = (class EndpointAppPBX *)instance; - /* refresh display, if required (include counter) */ - if (e_vbox_display_refresh && e_ext.vbox_display!=VBOX_DISPLAY_OFF) { - char counter[32]; - struct lcr_msg *message; - - SPRINT(counter, "%02d:%02d", e_vbox_counter/60, e_vbox_counter%60); - if (e_vbox_counter_max) - UPRINT(strchr(counter,'\0'), " of %02d:%02d", e_vbox_counter_max/60, e_vbox_counter_max%60); - - e_vbox_display_refresh = 0; - message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY); - SPRINT(message->param.notifyinfo.display, e_vbox_display, counter); - PDEBUG(DEBUG_EPOINT, "EPOINT(%d) terminal %s pending display:%s\n", ea_endpoint->ep_serial, e_ext.number, message->param.notifyinfo.display); - message_put(message); - logmessage(message->type, &message->param, ea_endpoint->ep_portlist->port_id, DIRECTION_OUT); - } + /* no display */ + if (ea->e_ext.vbox_display == VBOX_DISPLAY_OFF) + return 0; + + /* refresh display */ + char counter[32]; + struct lcr_msg *message; + + SPRINT(counter, "%02d:%02d", ea->e_vbox_counter/60, ea->e_vbox_counter%60); + if (ea->e_vbox_counter_max) + UPRINT(strchr(counter,'\0'), " of %02d:%02d", ea->e_vbox_counter_max/60, ea->e_vbox_counter_max%60); + + message = message_create(ea->ea_endpoint->ep_serial, ea->ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY); + SPRINT(message->param.notifyinfo.display, ea->e_vbox_display, counter); + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) terminal %s pending display:%s\n", ea->ea_endpoint->ep_serial, ea->e_ext.number, message->param.notifyinfo.display); + message_put(message); + ea->logmessage(message->type, &message->param, ea->ea_endpoint->ep_portlist->port_id, DIRECTION_OUT); + + /* not playing anymore */ + if (!ea->e_vbox_state==VBOX_STATE_PLAY && !ea->e_vbox_state==VBOX_STATE_RECORD_PLAY) + return 0; + + schedule_timer(&ea->e_vbox_refresh, 1, 0); + + return 0; } @@ -636,6 +644,8 @@ void EndpointAppPBX::vbox_message_eof(void) { char buffer[32]; int language = e_ext.vbox_language; + time_t current_time; + struct tm *current_tm; PDEBUG(DEBUG_EPOINT, "EPOINT(%d) terminal %s end of file during state: %d\n", ea_endpoint->ep_serial, e_ext.number, e_vbox_state); @@ -644,7 +654,7 @@ void EndpointAppPBX::vbox_message_eof(void) case VBOX_STATE_NOTHING: e_vbox_state = VBOX_STATE_MENU; SCPY(e_vbox_display, (char *)((language)?"druecke 2 f. wiedergabe":"press 2 to play")); - e_vbox_display_refresh = 1; + schedule_timer(&e_vbox_refresh, 0, 0); set_tone_vbox("menu"); break; @@ -652,7 +662,7 @@ void EndpointAppPBX::vbox_message_eof(void) if (e_vbox_speed > 0) { e_vbox_state = VBOX_STATE_MENU; SCPY(e_vbox_display, (char *)((language)?"druecke 3 f. Naechste":"press 3 for next")); - e_vbox_display_refresh = 1; + schedule_timer(&e_vbox_refresh, 0, 0); set_tone_vbox("menu"); } else { /* if we have endoffile because we were playing backwards, we continue to play forward */ @@ -664,11 +674,13 @@ void EndpointAppPBX::vbox_message_eof(void) case VBOX_STATE_PAUSE: SCPY(e_vbox_display, (char *)((language)?"druecke 2 f. weiterspielen":"press 2 to continue")); - e_vbox_display_refresh = 1; + schedule_timer(&e_vbox_refresh, 0, 0); break; case VBOX_STATE_CALLINFO_INTRO: - if (e_vbox_index_mday==now_tm->tm_mday && e_vbox_index_mon==now_tm->tm_mon && e_vbox_index_year==now_tm->tm_year) + time(¤t_time); + current_tm = localtime(¤t_time); + if (e_vbox_index_mday==current_tm->tm_mday && e_vbox_index_mon==current_tm->tm_mon && e_vbox_index_year==current_tm->tm_year) goto skip_day_month; e_vbox_state = VBOX_STATE_CALLINFO_MONTH; //german day if (e_ext.vbox_language) @@ -764,37 +776,37 @@ void EndpointAppPBX::vbox_message_eof(void) } else { /* the callinfo is played, so we start with the call */ e_vbox_counter = 0; - e_vbox_counter_last = 0; e_vbox_counter_max = 0; e_vbox_speed = 1; e_vbox_state = VBOX_STATE_PLAY; + schedule_timer(&e_vbox_refresh, 0, 0); SPRINT(e_vbox_display, "#%d %%s", e_vbox_play); if (e_ext.vbox_display == VBOX_DISPLAY_DETAILED) UPRINT(strchr(e_vbox_display,'\0'), " (%s)", e_vbox_index_callerid); - e_vbox_display_refresh = 1; + schedule_timer(&e_vbox_refresh, 0, 0); set_play_vbox(e_vbox_index_file, 0); } break; case VBOX_STATE_RECORD_ASK: set_tone_vbox("record_ask"); - e_vbox_display_refresh = 1; + schedule_timer(&e_vbox_refresh, 0, 0); break; case VBOX_STATE_STORE_ASK: set_tone_vbox("store_ask"); - e_vbox_display_refresh = 1; + schedule_timer(&e_vbox_refresh, 0, 0); break; case VBOX_STATE_DELETE_ASK: set_tone_vbox("delete_ask"); - e_vbox_display_refresh = 1; + schedule_timer(&e_vbox_refresh, 0, 0); break; case VBOX_STATE_RECORD_PLAY: e_vbox_state = VBOX_STATE_RECORD_ASK; SCPY(e_vbox_display, (char *)((language)?"1=Aufn. 2=Wied. 3=nein":"1=record 2=play 3=no")); - e_vbox_display_refresh = 1; + schedule_timer(&e_vbox_refresh, 0, 0); set_tone_vbox("record_ask"); break; @@ -803,12 +815,12 @@ void EndpointAppPBX::vbox_message_eof(void) if (e_vbox_index_num == 0) { /* nothing to play */ e_vbox_state = VBOX_STATE_MENU; SCPY(e_vbox_display, (char *)((language)?"keine Anrufe":"no calls")); - e_vbox_display_refresh = 1; + schedule_timer(&e_vbox_refresh, 0, 0); set_tone_vbox("nothing"); } else { e_vbox_state = VBOX_STATE_MENU; SCPY(e_vbox_display, (char *)((language)?"druecke 2 f. wiedergabe":"press 2 to play")); - e_vbox_display_refresh = 1; + schedule_timer(&e_vbox_refresh, 0, 0); set_tone_vbox("menu"); } break; diff --git a/apppbx.cpp b/apppbx.cpp index 9858207..46b1f62 100644 --- a/apppbx.cpp +++ b/apppbx.cpp @@ -14,6 +14,15 @@ class EndpointAppPBX *apppbx_first = NULL; +int action_timeout(struct lcr_timer *timer, void *instance, int index); +int match_timeout(struct lcr_timer *timer, void *instance, int index); +int redial_timeout(struct lcr_timer *timer, void *instance, int index); +int powerdial_timeout(struct lcr_timer *timer, void *instance, int index); +int cfnr_timeout(struct lcr_timer *timer, void *instance, int index); +int cfnr_call_timeout(struct lcr_timer *timer, void *instance, int index); +int password_timeout(struct lcr_timer *timer, void *instance, int index); +int callback_timeout(struct lcr_timer *timer, void *instance, int index); + /* * EndpointAppPBX constructor */ @@ -21,6 +30,28 @@ EndpointAppPBX::EndpointAppPBX(class Endpoint *epoint, int origin) : EndpointApp { class EndpointAppPBX **apppointer; + memset(&e_crypt_handler, 0, sizeof(e_crypt_handler)); + add_timer(&e_crypt_handler, crypt_handler, this, 0); + memset(&e_vbox_refresh, 0, sizeof(e_vbox_refresh)); + add_timer(&e_vbox_refresh, vbox_refresh, this, 0); + memset(&e_action_timeout, 0, sizeof(e_action_timeout)); + add_timer(&e_action_timeout, action_timeout, this, 0); + memset(&e_match_timeout, 0, sizeof(e_match_timeout)); + add_timer(&e_match_timeout, match_timeout, this, 0); + memset(&e_redial_timeout, 0, sizeof(e_redial_timeout)); + add_timer(&e_redial_timeout, redial_timeout, this, 0); + memset(&e_powerdial_timeout, 0, sizeof(e_powerdial_timeout)); + add_timer(&e_powerdial_timeout, powerdial_timeout, this, 0); + memset(&e_cfnr_timeout, 0, sizeof(e_cfnr_timeout)); + add_timer(&e_cfnr_timeout, cfnr_timeout, this, 0); + memset(&e_cfnr_call_timeout, 0, sizeof(e_cfnr_call_timeout)); + add_timer(&e_cfnr_call_timeout, cfnr_call_timeout, this, 0); + memset(&e_callback_timeout, 0, sizeof(e_callback_timeout)); + add_timer(&e_callback_timeout, callback_timeout, this, 0); + memset(&e_password_timeout, 0, sizeof(e_password_timeout)); + add_timer(&e_password_timeout, password_timeout, this, 0); + e_powerdial_on = 0; + /* add application to chain */ next = NULL; apppointer = &apppbx_first; @@ -48,8 +79,6 @@ EndpointAppPBX::EndpointAppPBX(class Endpoint *epoint, int origin) : EndpointApp e_rule = e_ruleset->rule_first; e_rule_nesting = 0; e_action = NULL; - e_action_timeout = 0; - e_match_timeout = 0; e_match_to_action = NULL; e_select = 0; e_extdialing = e_dialinginfo.id; @@ -58,13 +87,10 @@ EndpointAppPBX::EndpointAppPBX(class Endpoint *epoint, int origin) : EndpointApp e_hold = 0; // e_join_tone[0] = e_hold_tone[0] = '\0'; e_join_pattern /*= e_hold_pattern*/ = 0; - e_redial = 0; e_tone[0] = '\0'; e_adminid = 0; // will be set, if call was initiated via admin socket - e_powerdialing = 0; e_powerdelay = 0; e_powerlimit = 0; - e_callback = 0; e_cbdialing[0] = '\0'; e_cbcaller[0] = '\0'; e_cbto[0] = '\0'; @@ -74,9 +100,6 @@ EndpointAppPBX::EndpointAppPBX(class Endpoint *epoint, int origin) : EndpointApp e_dtmf_time = 0; e_dtmf_last = 0; e_enablekeypad = 0; - e_cfnr_release = 0; - e_cfnr_call = 0; - e_password_timeout = 0; e_multipoint_cause = 0; e_multipoint_location = 0; e_dialing_queue[0] = '\0'; @@ -107,6 +130,17 @@ EndpointAppPBX::~EndpointAppPBX(void) { class EndpointAppPBX *temp, **tempp; + del_timer(&e_crypt_handler); + del_timer(&e_vbox_refresh); + del_timer(&e_action_timeout); + del_timer(&e_match_timeout); + del_timer(&e_redial_timeout); + del_timer(&e_powerdial_timeout); + del_timer(&e_cfnr_timeout); + del_timer(&e_cfnr_call_timeout); + del_timer(&e_callback_timeout); + del_timer(&e_password_timeout); + /* detach */ temp =apppbx_first; tempp = &apppbx_first; @@ -211,7 +245,7 @@ void EndpointAppPBX::release(int release, int joinlocation, int joincause, int p } /* if callback is enabled, call back with the given caller id */ - if (e_callback) { + if (e_callback_timeout.active) { /* reset some stuff */ new_state(EPOINT_STATE_IDLE); memset(&e_connectinfo, 0, sizeof(struct connect_info)); @@ -221,8 +255,10 @@ void EndpointAppPBX::release(int release, int joinlocation, int joincause, int p if (e_ruleset) e_rule = e_ruleset->rule_first; e_action = NULL; - e_action_timeout = 0; - e_match_timeout = 0; + unsched_timer(&e_action_timeout); + unsched_timer(&e_match_timeout); + unsched_timer(&e_cfnr_timeout); + unsched_timer(&e_cfnr_call_timeout); e_match_to_action = NULL; //e_select = 0; e_extdialing = e_dialinginfo.id; @@ -231,8 +267,6 @@ void EndpointAppPBX::release(int release, int joinlocation, int joincause, int p e_dtmf_time = 0; e_dtmf_last = 0; e_enablekeypad = 0; - e_cfnr_release = 0; - e_cfnr_call = 0; e_multipoint_cause = 0; e_multipoint_location = 0; e_dialing_queue[0] = '\0'; @@ -273,7 +307,8 @@ void EndpointAppPBX::release(int release, int joinlocation, int joincause, int p } PDEBUG(DEBUG_EPOINT, "EPOINT(%d) do pending release of epoint itself.\n", ea_endpoint->ep_serial); - ea_endpoint->ep_use--; /* when e_lock is 0, the endpoint will be deleted */ + if (--ea_endpoint->ep_use <= 0) /* when e_lock is 0, the endpoint will be deleted */ + trigger_work(&ea_endpoint->ep_delete); return; } } @@ -901,7 +936,7 @@ void EndpointAppPBX::out_setup(void) p = e_ext.cfnr; if (*p) { /* when cfnr is done, out_setup() will setup the call */ - if (e_cfnr_call) { + if (e_cfnr_call_timeout.active) { /* present to forwarded party */ if (e_ext.anon_ignore && e_callerinfo.id[0]) { e_callerinfo.present = INFO_PRESENT_ALLOWED; @@ -909,8 +944,8 @@ void EndpointAppPBX::out_setup(void) goto cfnr_only; } if (!!strcmp(p, "vbox") || (e_capainfo.bearer_capa==INFO_BC_AUDIO) || (e_capainfo.bearer_capa==INFO_BC_SPEECH)) { - e_cfnr_release = now + e_ext.cfnr_delay; - e_cfnr_call = now + e_ext.cfnr_delay + 1; /* call one second after release */ + schedule_timer(&e_cfnr_timeout, e_ext.cfnr_delay, 0); + schedule_timer(&e_cfnr_call_timeout, e_ext.cfnr_delay + 1, 0); /* call one second after release */ PDEBUG(DEBUG_EPOINT, "EPOINT(%d) setting time for call-forward-busy to %s with delay %ld.\n", ea_endpoint->ep_serial, e_ext.cfnr, e_ext.cfnr_delay); } } @@ -1218,163 +1253,157 @@ void EndpointAppPBX::out_setup(void) } +int action_timeout(struct lcr_timer *timer, void *instance, int index) +{ + class EndpointAppPBX *ea = (class EndpointAppPBX *)instance; -/* handler for endpoint - */ + if (!ea->e_action || ea->e_state == EPOINT_STATE_CONNECT) + return 0; + + unsched_timer(&ea->e_redial_timeout); + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) current action timed out.\n", ea->ea_endpoint->ep_serial); + ea->e_multipoint_cause = 0; + ea->e_multipoint_location = 0; + ea->new_state(EPOINT_STATE_IN_OVERLAP); + ea->e_join_pattern = 0; + ea->process_dialing(1); + /* we must exit, because our endpoint might be gone */ -extern int quit; -int EndpointAppPBX::handler(void) + return 0; +} + +int match_timeout(struct lcr_timer *timer, void *instance, int index) { - if (e_crypt_state!=CM_ST_NULL) { - cryptman_handler(); - } - - /* process answering machine (play) handling */ - if (e_action) { - if (e_action->index == ACTION_VBOX_PLAY) - vbox_handler(); - - /* process action timeout */ - if (e_action_timeout) - if (now_d >= e_action_timeout) { - if (e_state!=EPOINT_STATE_CONNECT) { - e_redial = 0; - PDEBUG(DEBUG_EPOINT, "EPOINT(%d) current action timed out.\n", ea_endpoint->ep_serial); - e_multipoint_cause = 0; - e_multipoint_location = 0; - new_state(EPOINT_STATE_IN_OVERLAP); - e_join_pattern = 0; - process_dialing(); - return(1); /* we must exit, because our endpoint might be gone */ - } else - e_action_timeout = 0; - } - } else { - /* process action timeout */ - if (e_match_timeout) - if (now_d >= e_match_timeout) { - e_redial = 0; - PDEBUG(DEBUG_EPOINT, "EPOINT(%d) we got a match timeout.\n", ea_endpoint->ep_serial); - process_dialing(); - return(1); /* we must exit, because our endpoint might be gone */ - } + class EndpointAppPBX *ea = (class EndpointAppPBX *)instance; + + if (!ea->e_action) { + unsched_timer(&ea->e_redial_timeout); + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) we got a match timeout.\n", ea->ea_endpoint->ep_serial); + ea->process_dialing(0); + /* we must exit, because our endpoint might be gone */ } + return 0; +} - /* process redialing (epoint redials to port) */ - if (e_redial) { - if (now_d >= e_redial) { - e_redial = 0; - PDEBUG(DEBUG_EPOINT, "EPOINT(%d) starting redial.\n", ea_endpoint->ep_serial); +int redial_timeout(struct lcr_timer *timer, void *instance, int index) +{ + class EndpointAppPBX *ea = (class EndpointAppPBX *)instance; - new_state(EPOINT_STATE_OUT_SETUP); - /* call special setup routine */ - out_setup(); + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) starting redial.\n", ea->ea_endpoint->ep_serial); - return(1); - } - } + ea->new_state(EPOINT_STATE_OUT_SETUP); + /* call special setup routine */ + ea->out_setup(); - /* process powerdialing (epoint redials to epoint) */ - if (e_powerdialing > 0) { - if (now_d >= e_powerdialing) { - e_powerdialing = -1; /* leave power dialing on */ - PDEBUG(DEBUG_EPOINT, "EPOINT(%d) starting redial of powerdial.\n", ea_endpoint->ep_serial); + return 0; +} - /* redial */ - e_ruleset = ruleset_main; - if (e_ruleset) - e_rule = e_ruleset->rule_first; - e_action = NULL; - new_state(EPOINT_STATE_IN_OVERLAP); - process_dialing(); - return(1); - } - } +int powerdial_timeout(struct lcr_timer *timer, void *instance, int index) +{ + class EndpointAppPBX *ea = (class EndpointAppPBX *)instance; - /* process call forward no response */ - if (e_cfnr_release) { - struct port_list *portlist; - struct lcr_msg *message; + /* leave power dialing on */ + ea->e_powerdial_on = 1; + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) starting redial of powerdial.\n", ea->ea_endpoint->ep_serial); - if (now >= e_cfnr_release) { - PDEBUG(DEBUG_EPOINT, "EPOINT(%d) call-forward-no-response time has expired, hanging up.\n", ea_endpoint->ep_serial); - e_cfnr_release = 0; + /* redial */ + ea->e_ruleset = ruleset_main; + if (ea->e_ruleset) + ea->e_rule = ea->e_ruleset->rule_first; + ea->e_action = NULL; + ea->new_state(EPOINT_STATE_IN_OVERLAP); + ea->process_dialing(0); - /* release all ports */ - while((portlist = ea_endpoint->ep_portlist)) { - message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_RELEASE); - message->param.disconnectinfo.cause = CAUSE_NORMAL; /* normal clearing */ - message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; - message_put(message); - logmessage(message->type, &message->param, portlist->port_id, DIRECTION_OUT); - ea_endpoint->free_portlist(portlist); - } - /* put on hold */ - message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_AUDIOPATH); - message->param.audiopath = 0; - message_put(message); - /* indicate no patterns */ - message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_NOPATTERN); - message_put(message); - /* set setup state, since we have no response from the new join */ - new_state(EPOINT_STATE_OUT_SETUP); - } - } else - if (e_cfnr_call) { - if (now >= e_cfnr_call) { - PDEBUG(DEBUG_EPOINT, "EPOINT(%d) call-forward-busy time has expired, calling the forwarded number: %s.\n", ea_endpoint->ep_serial, e_ext.cfnr); - out_setup(); - e_cfnr_call = 0; - } + return 0; +} + +int cfnr_timeout(struct lcr_timer *timer, void *instance, int index) +{ + class EndpointAppPBX *ea = (class EndpointAppPBX *)instance; + struct port_list *portlist; + struct lcr_msg *message; + + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) call-forward-no-response time has expired, hanging up.\n", ea->ea_endpoint->ep_serial); + + /* release all ports */ + while((portlist = ea->ea_endpoint->ep_portlist)) { + message = message_create(ea->ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_RELEASE); + message->param.disconnectinfo.cause = CAUSE_NORMAL; /* normal clearing */ + message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + message_put(message); + ea->logmessage(message->type, &message->param, portlist->port_id, DIRECTION_OUT); + ea->ea_endpoint->free_portlist(portlist); } + /* put on hold */ + message = message_create(ea->ea_endpoint->ep_serial, ea->ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_AUDIOPATH); + message->param.audiopath = 0; + message_put(message); + /* indicate no patterns */ + message = message_create(ea->ea_endpoint->ep_serial, ea->ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_NOPATTERN); + message_put(message); + /* set setup state, since we have no response from the new join */ + ea->new_state(EPOINT_STATE_OUT_SETUP); - /* handle connection to user */ - if (e_state == EPOINT_STATE_IDLE) { + return 0; +} + +int cfnr_call_timeout(struct lcr_timer *timer, void *instance, int index) +{ + class EndpointAppPBX *ea = (class EndpointAppPBX *)instance; + + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) call-forward-busy time has expired, calling the forwarded number: %s.\n", ea->ea_endpoint->ep_serial, ea->e_ext.cfnr); + ea->out_setup(); + + return 0; +} + +int callback_timeout(struct lcr_timer *timer, void *instance, int index) +{ + class EndpointAppPBX *ea = (class EndpointAppPBX *)instance; + + if (ea->e_state == EPOINT_STATE_IDLE) { /* epoint is idle, check callback */ - if (e_callback) - if (now_d >= e_callback) { - e_callback = 0; /* done with callback */ - PDEBUG(DEBUG_EPOINT, "EPOINT(%d) starting callback.\n", ea_endpoint->ep_serial); - new_state(EPOINT_STATE_OUT_SETUP); - out_setup(); - return(1); - } + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) starting callback.\n", ea->ea_endpoint->ep_serial); + ea->new_state(EPOINT_STATE_OUT_SETUP); + ea->out_setup(); } - /* check for password timeout */ - if (e_action) - if (e_action->index==ACTION_PASSWORD || e_action->index==ACTION_PASSWORD_WRITE) { + return 0; +} + +int password_timeout(struct lcr_timer *timer, void *instance, int index) +{ + class EndpointAppPBX *ea = (class EndpointAppPBX *)instance; + + if (ea->e_action->index==ACTION_PASSWORD || ea->e_action->index==ACTION_PASSWORD_WRITE) { struct port_list *portlist; - if (now >= e_password_timeout) { - e_ruleset = ruleset_main; - if (e_ruleset) - e_rule = e_ruleset->rule_first; - e_action = NULL; - PDEBUG(DEBUG_EPOINT, "EPOINT(%d) password timeout %s\n", ea_endpoint->ep_serial, e_extdialing); - trace_header("PASSWORD timeout", DIRECTION_NONE); - end_trace(); - e_connectedmode = 0; - e_dtmf = 0; - new_state(EPOINT_STATE_OUT_DISCONNECT); - portlist = ea_endpoint->ep_portlist; - if (portlist) { - message_disconnect_port(portlist, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, ""); - set_tone(portlist, "cause_10"); - } - return(1); + ea->e_ruleset = ruleset_main; + if (ea->e_ruleset) + ea->e_rule = ea->e_ruleset->rule_first; + ea->e_action = NULL; + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) password timeout %s\n", ea->ea_endpoint->ep_serial, ea->e_extdialing); + ea->trace_header("PASSWORD timeout", DIRECTION_NONE); + end_trace(); + ea->e_connectedmode = 0; + ea->e_dtmf = 0; + ea->new_state(EPOINT_STATE_OUT_DISCONNECT); + portlist = ea->ea_endpoint->ep_portlist; + if (portlist) { + ea->message_disconnect_port(portlist, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, ""); + ea->set_tone(portlist, "cause_10"); } } - - return(0); -} + return 0; +} /* doing a hookflash */ void EndpointAppPBX::hookflash(void) { class Port *port; + time_t now; /* be sure that we are active */ notify_active(); @@ -1408,10 +1437,11 @@ void EndpointAppPBX::hookflash(void) e_join_pattern = 0; if (e_dialinginfo.id[0]) { set_tone(ea_endpoint->ep_portlist, "dialing"); - process_dialing(); + process_dialing(0); } else { set_tone(ea_endpoint->ep_portlist, "dialpbx"); } + time(&now); e_dtmf_time = now; e_dtmf_last = '\0'; } @@ -1566,7 +1596,7 @@ void EndpointAppPBX::port_setup(struct port_list *portlist, int message_type, un else set_tone(portlist, "dialtone"); } - process_dialing(); + process_dialing(0); if (e_state == EPOINT_STATE_IN_SETUP) { /* request MORE info, if not already at higher state */ new_state(EPOINT_STATE_IN_OVERLAP); @@ -1599,7 +1629,7 @@ void EndpointAppPBX::port_information(struct port_list *portlist, int message_ty if (e_action->index == ACTION_VBOX_PLAY) { /* concat dialing string */ SCAT(e_dialinginfo.id, param->information.id); - process_dialing(); + process_dialing(0); return; } @@ -1656,12 +1686,16 @@ void EndpointAppPBX::port_information(struct port_list *portlist, int message_ty } /* concat dialing string */ SCAT(e_dialinginfo.id, param->information.id); - process_dialing(); + process_dialing(0); } /* port MESSAGE_DTMF */ void EndpointAppPBX::port_dtmf(struct port_list *portlist, int message_type, union parameter *param) { + time_t now; + + time(&now); + /* only if dtmf detection is enabled */ if (!e_dtmf) { trace_header("DTMF (disabled)", DIRECTION_IN); @@ -1681,7 +1715,7 @@ NOTE: vbox is now handled due to overlap state if (strlen(e_dialinginfo.id)+1 < sizeof(e_dialinginfo.id)) { e_dialinginfo.id[strlen(e_dialinginfo.id)+1] = '\0'; e_dialinginfo.id[strlen(e_dialinginfo.id)] = param->dtmf; - process_dialing(); + process_dialing(0); } /* continue to process *X# sequences */ } @@ -1755,7 +1789,7 @@ NOTE: vbox is now handled due to overlap state if (strlen(e_dialinginfo.id)+1 < sizeof(e_dialinginfo.id)) { e_dialinginfo.id[strlen(e_dialinginfo.id)+1] = '\0'; e_dialinginfo.id[strlen(e_dialinginfo.id)] = param->dtmf; - process_dialing(); + process_dialing(0); } } } @@ -1909,6 +1943,7 @@ void EndpointAppPBX::port_connect(struct port_list *portlist, int message_type, struct port_list *tportlist; class Port *port; struct interface *interface; + time_t now; logmessage(message_type, param, portlist->port_id, DIRECTION_IN); @@ -1932,6 +1967,7 @@ void EndpointAppPBX::port_connect(struct port_list *portlist, int message_type, } PDEBUG(DEBUG_EPOINT, "EPOINT(%d) removing all other ports (end)\n", ea_endpoint->ep_serial); + time(&now); e_start = now; /* screen incoming connected id */ @@ -1979,7 +2015,8 @@ void EndpointAppPBX::port_connect(struct port_list *portlist, int message_type, message_put(message); } - e_cfnr_call = e_cfnr_release = 0; + unsched_timer(&e_cfnr_timeout); + unsched_timer(&e_cfnr_call_timeout); if (e_ext.number[0]) e_dtmf = 1; /* allow dtmf */ @@ -2053,12 +2090,12 @@ void EndpointAppPBX::port_connect(struct port_list *portlist, int message_type, /* make call state to enter password */ new_state(EPOINT_STATE_IN_OVERLAP); e_action = &action_password_write; - e_match_timeout = 0; + unsched_timer(&e_match_timeout); e_match_to_action = NULL; e_dialinginfo.id[0] = '\0'; e_extdialing = strchr(e_dialinginfo.id, '\0'); - e_password_timeout = now+20; - process_dialing(); + schedule_timer(&e_password_timeout, 20, 0); + process_dialing(0); } else { /* incoming call (callback) */ e_ruleset = ruleset_main; @@ -2068,7 +2105,7 @@ void EndpointAppPBX::port_connect(struct port_list *portlist, int message_type, e_extdialing = e_dialinginfo.id; if (e_dialinginfo.id[0]) { set_tone(portlist, "dialing"); - process_dialing(); + process_dialing(0); } else { set_tone(portlist, "dialpbx"); } @@ -2151,7 +2188,8 @@ void EndpointAppPBX::port_disconnect_release(struct port_list *portlist, int mes } } - e_cfnr_call = e_cfnr_release = 0; + unsched_timer(&e_cfnr_timeout); + unsched_timer(&e_cfnr_call_timeout); /* process hangup */ process_hangup(e_join_cause, e_join_location); @@ -2816,6 +2854,7 @@ void EndpointAppPBX::join_alerting(struct port_list *portlist, int message_type, void EndpointAppPBX::join_connect(struct port_list *portlist, int message_type, union parameter *param) { struct lcr_msg *message; + time_t now; new_state(EPOINT_STATE_CONNECT); // UCPY(e_join_tone, ""); @@ -2823,7 +2862,8 @@ void EndpointAppPBX::join_connect(struct port_list *portlist, int message_type, if (e_ext.number[0]) e_dtmf = 1; /* allow dtmf */ - e_powerdialing = 0; + e_powerdial_on = 0; + unsched_timer(&e_powerdial_timeout); memcpy(&e_connectinfo, ¶m->connectinfo, sizeof(e_callerinfo)); if(portlist) { message = message_create(ea_endpoint->ep_serial, portlist->port_id, EPOINT_TO_PORT, MESSAGE_CONNECT); @@ -2860,6 +2900,7 @@ void EndpointAppPBX::join_connect(struct port_list *portlist, int message_type, message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_join_id, EPOINT_TO_JOIN, MESSAGE_AUDIOPATH); message->param.audiopath = 1; message_put(message); + time(&now); e_start = now; } @@ -2869,18 +2910,19 @@ void EndpointAppPBX::join_disconnect_release(int message_type, union parameter * char cause[16]; struct lcr_msg *message; struct port_list *portlist = NULL; + time_t now; /* be sure that we are active */ notify_active(); e_tx_state = NOTIFY_STATE_ACTIVE; - /* we are powerdialing, if e_powerdialing is set and limit is not exceeded if given */ - if (e_powerdialing && ((e_powercount+1)ep_serial, param->setup.dialinginfo.id); memcpy(&e_dialinginfo, ¶m->setup.dialinginfo, sizeof(e_dialinginfo)); return; @@ -3525,7 +3568,7 @@ reject: message_put(message); /* beeing paranoid, we make call update */ - joinpbx->j_updatebridge = 1; + trigger_work(&joinpbx->j_updatebridge); if (options.deb & DEBUG_EPOINT) { class Join *debug_c = join_first; @@ -3703,7 +3746,7 @@ void EndpointAppPBX::join_join(void) PDEBUG(DEBUG_EPOINT, "EPOINT(%d)d-join completely removed!\n"); /* mixer must update */ - our_joinpbx->j_updatebridge = 1; /* update mixer flag */ + trigger_work(&our_joinpbx->j_updatebridge); /* we send a retrieve to that endpoint */ // mixer will update the hold-state of the join and send it to the endpoints is changes diff --git a/apppbx.h b/apppbx.h index 78355b5..b10a119 100644 --- a/apppbx.h +++ b/apppbx.h @@ -49,6 +49,8 @@ static const char *state_name[] = { \ }; \ int state_name_num = sizeof(state_name) / sizeof(char *); +int vbox_refresh(struct lcr_timer *timer, void *instance, int index); + extern class EndpointAppPBX *apppbx_first; /* structure of an EndpointAppPBX */ @@ -59,7 +61,6 @@ class EndpointAppPBX : public EndpointApp ~EndpointAppPBX(); class EndpointAppPBX *next; - int handler(void); int e_hold; /* is this endpoint on hold ? */ char e_tone[256]; /* save tone for resuming ports */ @@ -79,9 +80,7 @@ class EndpointAppPBX : public EndpointApp struct route_ruleset *e_ruleset; /* current ruleset pointer (NULL=no ruleset) */ struct route_rule *e_rule; /* current rule pointer (NULL=no rule) */ struct route_action *e_action; /* current action pointer (NULL=no action) */ - double e_action_timeout; /* when to timeout */ int e_rule_nesting; /* 'goto'/'menu' recrusion counter to prevent infinie loops */ - double e_match_timeout; /* set for the next possible timeout time */ struct route_action *e_match_to_action; /* what todo when timeout */ char *e_match_to_extdialing; /* dialing after matching timeout rule */ int e_select; /* current selection for various selector options */ @@ -97,15 +96,11 @@ class EndpointAppPBX : public EndpointApp /* action */ char e_dialing_queue[32]; /* holds dialing during setup state */ - double e_redial; /* time when redialing 0=off */ - double e_powerdialing; /* on disconnect redial! 0=off, >0=redial time, -1=on */ double e_powerdelay; /* delay when to redial */ int e_powercount; /* power count */ int e_powerlimit; /* power limit */ - double e_callback; /* time when callback (when idle reached) 0=off */ - signed int e_cfnr_release; /* time stamp when to do the release for call forward on no response */ - signed int e_cfnr_call; /* time stamp when to do the call for call forward on no response */ - signed int e_password_timeout; /* time stamp when to do the release for password timeout */ + struct lcr_timer e_action_timeout; + struct lcr_timer e_match_timeout; /* port relation */ int e_multipoint_cause; /* cause value of disconnected multiport calls (highest priority) */ @@ -120,6 +115,13 @@ class EndpointAppPBX : public EndpointApp char e_cbcaller[256]; /* extension for the epoint which calls back */ char e_cbto[32]; /* override callerid to call back to */ struct caller_info e_callbackinfo; /* information about the callback caller */ + struct lcr_timer e_redial_timeout; + int e_powerdial_on; + struct lcr_timer e_powerdial_timeout; + struct lcr_timer e_cfnr_timeout; + struct lcr_timer e_cfnr_call_timeout; + struct lcr_timer e_callback_timeout; + struct lcr_timer e_password_timeout; /* dtmf stuff */ int e_connectedmode; /* if the port should stay connected if the enpoint disconnects or releases (usefull for callback) */ @@ -138,7 +140,7 @@ class EndpointAppPBX : public EndpointApp int e_vbox_state; /* state of vbox during playback */ int e_vbox_menu; /* currently selected menu using '*' and '#' */ char e_vbox_display[128]; /* current display message */ - int e_vbox_display_refresh; /* display must be refreshed du to change */ + struct lcr_timer e_vbox_refresh; /* display must be refreshed du to change */ int e_vbox_counter; /* current playback counter in seconds */ int e_vbox_counter_max; /* size of file in seconds */ int e_vbox_counter_last; /* temp variable to recognise a change in seconds */ @@ -188,6 +190,7 @@ class EndpointAppPBX : public EndpointApp int e_crypt_rsa_iqmp_len; int e_crypt_keyengine_busy; /* current job and busy state */ int e_crypt_keyengine_return; /* return */ + struct lcr_timer e_crypt_handler; /* poll timer for crypt events */ /* messages */ void hookflash(void); @@ -234,7 +237,6 @@ class EndpointAppPBX : public EndpointApp void vbox_init(void); void vbox_index_read(int num); void vbox_index_remove(int num); - void vbox_handler(void); void efi_message_eof(void); void vbox_message_eof(void); void set_tone_vbox(const char *tone); @@ -296,7 +298,7 @@ class EndpointAppPBX : public EndpointApp void action_init_pick(void); void action_dialing_password(void); void action_dialing_password_wr(void); - void process_dialing(void); + void process_dialing(int timeout); void process_hangup(int cause, int location); /* facility function */ @@ -310,7 +312,6 @@ class EndpointAppPBX : public EndpointApp /* crypt */ void cryptman_keyengine(int job); - void cryptman_handler(void); void cr_ident(int message, unsigned char *param, int len); void cr_activate(int message, unsigned char *param, int len); void cr_deactivate(int message, unsigned char *param, int len); diff --git a/bchannel.c b/bchannel.c index fd3e33d..9e878fe 100644 --- a/bchannel.c +++ b/bchannel.c @@ -50,6 +50,7 @@ #include "message.h" #include "lcrsocket.h" #include "cause.h" +#include "select.h" #include "bchannel.h" #include "chan_lcr.h" #include "callerid.h" @@ -73,7 +74,7 @@ int bchannel_initialize(void) { init_af_isdn(); - return(0); + return 0; } void bchannel_deinitialize(void) @@ -123,6 +124,7 @@ static void ph_control_block(int sock, unsigned int c1, void *c2, int c2_len, ch CERROR(NULL, NULL, "Failed to send to socket %d\n", sock); } +static int bchannel_handle(struct lcr_fd *fd, unsigned int what, void *instance, int index); /* * create stack @@ -130,12 +132,11 @@ static void ph_control_block(int sock, unsigned int c1, void *c2, int c2_len, ch int bchannel_create(struct bchannel *bchannel, int mode) { int ret; - unsigned int on = 1; struct sockaddr_mISDN addr; if (bchannel->b_sock > -1) { CERROR(bchannel->call, NULL, "Socket already created for handle 0x%x\n", bchannel->handle); - return(0); + return 0; } /* open socket */ @@ -160,18 +161,14 @@ int bchannel_create(struct bchannel *bchannel, int mode) } if (bchannel->b_sock < 0) { CERROR(bchannel->call, NULL, "Failed to open bchannel-socket for handle 0x%x with mISDN-DSP layer. Did you load mISDN_dsp.ko?\n", bchannel->handle); - return(0); - } - - /* set nonblocking io */ - ret = ioctl(bchannel->b_sock, FIONBIO, &on); - if (ret < 0) { - CERROR(bchannel->call, NULL, "Failed to set bchannel-socket handle 0x%x into nonblocking IO\n", bchannel->handle); - close(bchannel->b_sock); - bchannel->b_sock = -1; - return(0); + return 0; } + /* register fd */ + memset(&bchannel->lcr_fd, 0, sizeof(bchannel->lcr_fd)); + bchannel->lcr_fd.fd = bchannel->b_sock; + register_fd(&bchannel->lcr_fd, LCR_FD_READ | LCR_FD_EXCEPT, bchannel_handle, bchannel, 0); + /* bind socket to bchannel */ addr.family = AF_ISDN; addr.dev = (bchannel->handle>>8); @@ -179,11 +176,12 @@ int bchannel_create(struct bchannel *bchannel, int mode) ret = bind(bchannel->b_sock, (struct sockaddr *)&addr, sizeof(addr)); if (ret < 0) { CERROR(bchannel->call, NULL, "Failed to bind bchannel-socket for handle 0x%x with mISDN-DSP layer. (port %d, channel %d) Did you load mISDN_dsp.ko?\n", bchannel->handle, addr.dev, addr.channel); + unregister_fd(&bchannel->lcr_fd); close(bchannel->b_sock); bchannel->b_sock = -1; - return(0); + return 0; } - return(1); + return 1; } @@ -264,6 +262,7 @@ static void bchannel_activated(struct bchannel *bchannel) void bchannel_destroy(struct bchannel *bchannel) { if (bchannel->b_sock > -1) { + unregister_fd(&bchannel->lcr_fd); close(bchannel->b_sock); bchannel->b_sock = -1; } @@ -517,63 +516,54 @@ void bchannel_gain(struct bchannel *bchannel, int gain, int tx) /* * main loop for processing messages from mISDN */ -int bchannel_handle(void) +static int bchannel_handle(struct lcr_fd *fd, unsigned int what, void *instance, int index) { - int ret, work = 0; - struct bchannel *bchannel; + struct bchannel *bchannel = (struct bchannel *)instance; + int ret; unsigned char buffer[2048+MISDN_HEADER_LEN]; struct mISDNhead *hh = (struct mISDNhead *)buffer; - /* process all bchannels */ - bchannel = bchannel_first; - while(bchannel) { - /* handle message from bchannel */ - if (bchannel->b_sock > -1) { - ret = recv(bchannel->b_sock, buffer, sizeof(buffer), 0); - if (ret >= (int)MISDN_HEADER_LEN) { - work = 1; - switch(hh->prim) { - /* we don't care about confirms, we use rx data to sync tx */ - case PH_DATA_CNF: - break; - - /* we receive audio data, we respond to it AND we send tones */ - case PH_DATA_IND: - case PH_DATA_REQ: - case DL_DATA_IND: - case PH_CONTROL_IND: - bchannel_receive(bchannel, buffer, ret-MISDN_HEADER_LEN); - break; - - case PH_ACTIVATE_IND: - case DL_ESTABLISH_IND: - case PH_ACTIVATE_CNF: - case DL_ESTABLISH_CNF: - CDEBUG(bchannel->call, NULL, "DL_ESTABLISH confirm: bchannel is now activated (socket %d).\n", bchannel->b_sock); - bchannel_activated(bchannel); - break; - - case PH_DEACTIVATE_IND: - case DL_RELEASE_IND: - case PH_DEACTIVATE_CNF: - case DL_RELEASE_CNF: - CDEBUG(bchannel->call, NULL, "DL_RELEASE confirm: bchannel is now de-activated (socket %d).\n", bchannel->b_sock); + ret = recv(bchannel->b_sock, buffer, sizeof(buffer), 0); + if (ret >= (int)MISDN_HEADER_LEN) { + switch(hh->prim) { + /* we don't care about confirms, we use rx data to sync tx */ + case PH_DATA_CNF: + break; + + /* we receive audio data, we respond to it AND we send tones */ + case PH_DATA_IND: + case PH_DATA_REQ: + case DL_DATA_IND: + case PH_CONTROL_IND: + bchannel_receive(bchannel, buffer, ret-MISDN_HEADER_LEN); + break; + + case PH_ACTIVATE_IND: + case DL_ESTABLISH_IND: + case PH_ACTIVATE_CNF: + case DL_ESTABLISH_CNF: + CDEBUG(bchannel->call, NULL, "DL_ESTABLISH confirm: bchannel is now activated (socket %d).\n", bchannel->b_sock); + bchannel_activated(bchannel); + break; + + case PH_DEACTIVATE_IND: + case DL_RELEASE_IND: + case PH_DEACTIVATE_CNF: + case DL_RELEASE_CNF: + CDEBUG(bchannel->call, NULL, "DL_RELEASE confirm: bchannel is now de-activated (socket %d).\n", bchannel->b_sock); // bchannel_deactivated(bchannel); - break; - - default: - CERROR(bchannel->call, NULL, "child message not handled: prim(0x%x) socket(%d) data len(%d)\n", hh->prim, bchannel->b_sock, ret - MISDN_HEADER_LEN); - } - } else { - if (ret < 0 && errno != EWOULDBLOCK) - CERROR(bchannel->call, NULL, "Read from socket %d failed with return code %d\n", bchannel->b_sock, ret); - } + break; + + default: + CERROR(bchannel->call, NULL, "child message not handled: prim(0x%x) socket(%d) data len(%d)\n", hh->prim, bchannel->b_sock, ret - MISDN_HEADER_LEN); } - bchannel = bchannel->next; + } else { +// if (ret < 0 && errno != EWOULDBLOCK) + CERROR(bchannel->call, NULL, "Read from socket %d failed with return code %d\n", bchannel->b_sock, ret); } /* if we received at least one b-frame, we will return 1 */ - return(work); + return 0; } @@ -590,7 +580,7 @@ struct bchannel *find_bchannel_handle(unsigned int handle) break; bchannel = bchannel->next; } - return(bchannel); + return bchannel; } #if 0 @@ -603,7 +593,7 @@ struct bchannel *find_bchannel_ref(unsigned int ref) break; bchannel = bchannel->next; } - return(bchannel); + return bchannel; } #endif @@ -616,12 +606,12 @@ struct bchannel *alloc_bchannel(unsigned int handle) *bchannelp = (struct bchannel *)calloc(1, sizeof(struct bchannel)); if (!*bchannelp) - return(NULL); + return NULL; (*bchannelp)->handle = handle; (*bchannelp)->b_state = BSTATE_IDLE; (*bchannelp)->b_sock = -1; - return(*bchannelp); + return *bchannelp; } void free_bchannel(struct bchannel *bchannel) diff --git a/bchannel.h b/bchannel.h index f7b3860..2b03444 100644 --- a/bchannel.h +++ b/bchannel.h @@ -15,6 +15,7 @@ struct bchannel { struct chan_call *call; /* link to call process */ unsigned int handle; /* handle for stack id */ int b_sock; /* socket for b-channel */ + struct lcr_fd lcr_fd; /* socket register */ int b_mode; /* dsp, raw, dsphdlc */ int b_state; int b_txdata; @@ -47,7 +48,6 @@ void bchannel_dtmf(struct bchannel *channel, int on); void bchannel_blowfish(struct bchannel *bchannel, unsigned char *key, int len); void bchannel_pipeline(struct bchannel *bchannel, char *pipeline); void bchannel_gain(struct bchannel *bchannel, int gain, int tx); -int bchannel_handle(void); struct bchannel *find_bchannel_handle(unsigned int handle); //struct bchannel *find_bchannel_ref(unsigned int ref); struct bchannel *alloc_bchannel(unsigned int handle); diff --git a/chan_lcr.c b/chan_lcr.c index 61905bf..d0993b7 100644 --- a/chan_lcr.c +++ b/chan_lcr.c @@ -167,6 +167,7 @@ it is called from ast_channel process which has already locked ast_channel. #include "callerid.h" #include "lcrsocket.h" #include "cause.h" +#include "select.h" #include "bchannel.h" #include "options.h" #include "chan_lcr.h" @@ -198,11 +199,22 @@ static char *desc = "Channel driver for mISDN/LCR Support (Bri/Pri)"; pthread_t chan_tid; ast_mutex_t chan_lock; /* global lock */ ast_mutex_t log_lock; /* logging log */ +/* global_change: + * used to indicate change in file descriptors, so select function's result may + * be obsolete. + */ +int global_change = 0; +int wake_global = 0; +int wake_pipe[2]; +struct lcr_fd wake_fd; + int quit; int glob_channel = 0; int lcr_sock = -1; +struct lcr_fd socket_fd; +struct lcr_timer socket_retry; struct admin_list { struct admin_list *next; @@ -259,7 +271,7 @@ struct chan_call *find_call_ref(unsigned int ref) break; call = call->next; } - return(call); + return call; } void free_call(struct chan_call *call) @@ -289,6 +301,7 @@ void free_call(struct chan_call *call) ast_dsp_free(call->dsp); CDEBUG(call, NULL, "Call instance freed.\n"); free(call); + global_change = 1; return; } temp = &((*temp)->next); @@ -309,11 +322,11 @@ struct chan_call *alloc_call(void) if (pipe((*callp)->pipe) < 0) { CERROR(*callp, NULL, "Failed to create pipe.\n"); free_call(*callp); - return(NULL); + return NULL; } fcntl((*callp)->pipe[0], F_SETFL, O_NONBLOCK); CDEBUG(*callp, NULL, "Call instance allocated.\n"); - return(*callp); + return *callp; } unsigned short new_bridge_id(void) @@ -334,7 +347,7 @@ unsigned short new_bridge_id(void) id++; } CDEBUG(NULL, NULL, "New bridge ID %d.\n", id); - return(id); + return id; } /* @@ -364,8 +377,14 @@ int send_message(int message_type, unsigned int ref, union parameter *param) admin->msg.u.msg.type = message_type; admin->msg.u.msg.ref = ref; memcpy(&admin->msg.u.msg.param, param, sizeof(union parameter)); + socket_fd.when |= LCR_FD_WRITE; + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } - return(0); + return 0; } /* @@ -923,8 +942,15 @@ static void lcr_in_proceeding(struct chan_call *call, int message_type, union pa /* change state */ call->state = CHAN_LCR_STATE_OUT_PROCEEDING; /* queue event for asterisk */ - if (call->ast && call->pbx_started) + if (call->ast && call->pbx_started) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strncat(call->queue_string, "P", sizeof(call->queue_string)-1); + } + } /* @@ -937,8 +963,14 @@ static void lcr_in_alerting(struct chan_call *call, int message_type, union para /* change state */ call->state = CHAN_LCR_STATE_OUT_ALERTING; /* queue event to asterisk */ - if (call->ast && call->pbx_started) + if (call->ast && call->pbx_started) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strncat(call->queue_string, "R", sizeof(call->queue_string)-1); + } } /* @@ -962,8 +994,14 @@ static void lcr_in_connect(struct chan_call *call, int message_type, union param /* copy connectinfo */ memcpy(&call->connectinfo, ¶m->connectinfo, sizeof(struct connect_info)); /* queue event to asterisk */ - if (call->ast && call->pbx_started) + if (call->ast && call->pbx_started) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strncat(call->queue_string, "N", sizeof(call->queue_string)-1); + } } /* @@ -997,9 +1035,14 @@ static void lcr_in_disconnect(struct chan_call *call, int message_type, union pa /* queue release asterisk */ if (ast) { ast->hangupcause = call->cause; - if (call->pbx_started) + if (call->pbx_started) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strcpy(call->queue_string, "H"); // overwrite other indications - else { + } else { ast_hangup(ast); // call will be destroyed here } } @@ -1026,9 +1069,14 @@ static void lcr_in_release(struct chan_call *call, int message_type, union param /* if we have an asterisk instance, queue hangup, else we are done */ if (ast) { ast->hangupcause = call->cause; - if (call->pbx_started) + if (call->pbx_started) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strcpy(call->queue_string, "H"); - else { + } else { ast_hangup(ast); // call will be destroyed here } } else { @@ -1064,8 +1112,14 @@ static void lcr_in_information(struct chan_call *call, int message_type, union p } /* queue digits */ - if (call->state == CHAN_LCR_STATE_IN_DIALING && param->information.id[0]) + if (call->state == CHAN_LCR_STATE_IN_DIALING && param->information.id[0]) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strncat(call->queue_string, param->information.id, sizeof(call->queue_string)-1); + } /* use bridge to forware message not supported by asterisk */ if (call->state == CHAN_LCR_STATE_CONNECT) { @@ -1134,8 +1188,14 @@ static void lcr_in_pattern(struct chan_call *call, int message_type, union param send_message(MESSAGE_BCHANNEL, call->ref, &newparam); } /* queue PROGRESS, because tones are available */ - if (call->ast && call->pbx_started) + if (call->ast && call->pbx_started) { + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strncat(call->queue_string, "T", sizeof(call->queue_string)-1); + } } /* @@ -1159,6 +1219,11 @@ void lcr_in_dtmf(struct chan_call *call, int val) CDEBUG(call, call->ast, "Recognised DTMF digit '%c'.\n", val); digit[0] = val; digit[1] = '\0'; + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strncat(call->queue_string, digit, sizeof(call->queue_string)-1); } @@ -1180,13 +1245,13 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) CDEBUG(NULL, NULL, "Received BCHANNEL_ASSIGN message. (handle=%08lx) for ref %d\n", param->bchannel.handle, ref); if ((bchannel = find_bchannel_handle(param->bchannel.handle))) { CERROR(NULL, NULL, "bchannel handle %x already assigned.\n", (int)param->bchannel.handle); - return(-1); + return -1; } /* create bchannel */ bchannel = alloc_bchannel(param->bchannel.handle); if (!bchannel) { CERROR(NULL, NULL, "alloc bchannel handle %x failed.\n", (int)param->bchannel.handle); - return(-1); + return -1; } /* configure channel */ @@ -1242,7 +1307,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) CDEBUG(NULL, NULL, "Received BCHANNEL_REMOVE message. (handle=%08lx)\n", param->bchannel.handle); if (!(bchannel = find_bchannel_handle(param->bchannel.handle))) { CERROR(NULL, NULL, "Bchannel handle %x not assigned.\n", (int)param->bchannel.handle); - return(-1); + return -1; } /* unklink from call and destroy bchannel */ free_bchannel(bchannel); @@ -1257,7 +1322,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) default: CDEBUG(NULL, NULL, "Received unknown bchannel message %d.\n", param->bchannel.type); } - return(0); + return 0; } /* handle new ref */ @@ -1267,7 +1332,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) CDEBUG(NULL, NULL, "Received new ref by LCR, due to incomming call. (ref=%ld)\n", ref); if (!ref || find_call_ref(ref)) { CERROR(NULL, NULL, "Illegal new ref %ld received.\n", ref); - return(-1); + return -1; } /* allocate new call instance */ call = alloc_call(); @@ -1287,7 +1352,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) /* send release, if ref does not exist */ CDEBUG(NULL, NULL, "No call found, that requests a ref.\n"); send_release_and_import(call, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL); - return(0); + return 0; } /* store new ref */ call->ref = ref; @@ -1306,22 +1371,22 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) send_release_and_import(call, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL); /* free call */ free_call(call); - return(0); + return 0; } } - return(0); + return 0; } /* check ref */ if (!ref) { CERROR(NULL, NULL, "Received message %d without ref.\n", message_type); - return(-1); + return -1; } call = find_call_ref(ref); if (!call) { /* ignore ref that is not used (anymore) */ CDEBUG(NULL, NULL, "Message %d from LCR ignored, because no call instance found.\n", message_type); - return(0); + return 0; } /* handle messages */ @@ -1382,7 +1447,7 @@ int receive_message(int message_type, unsigned int ref, union parameter *param) CDEBUG(call, call->ast, "Message %d from LCR unhandled.\n", message_type); break; } - return(0); + return 0; } /* @@ -1415,6 +1480,11 @@ again: goto again; } CDEBUG(call, call->ast, "Queue call release, because Asterisk channel is running.\n"); + if (!wake_global) { + wake_global = 1; + char byte = 0; + write(wake_pipe[1], &byte, 1); + } strcpy(call->queue_string, "H"); call = call->next; } @@ -1424,69 +1494,74 @@ again: free_bchannel(bchannel_first); } +void close_socket(void); /* asterisk handler * warning! not thread safe * returns -1 for socket error, 0 for no work, 1 for work */ -int handle_socket(void) +static int handle_socket(struct lcr_fd *fd, unsigned int what, void *instance, int index) { - int work = 0; int len; struct admin_list *admin; struct admin_message msg; - /* read from socket */ - len = read(lcr_sock, &msg, sizeof(msg)); - if (len == 0) { - CERROR(NULL, NULL, "Socket closed.\n"); - return(-1); // socket closed - } - if (len > 0) { - if (len != sizeof(msg)) { - CERROR(NULL, NULL, "Socket short read. (len %d)\n", len); - return(-1); // socket error + if ((what & LCR_FD_READ)) { + /* read from socket */ + len = read(lcr_sock, &msg, sizeof(msg)); + if (len == 0) { + CERROR(NULL, NULL, "Socket closed.\n"); + error: + CERROR(NULL, NULL, "Handling of socket failed - closing for some seconds.\n"); + close_socket(); + release_all_calls(); + schedule_timer(&socket_retry, SOCKET_RETRY_TIMER, 0); + return 0; } - if (msg.message != ADMIN_MESSAGE) { - CERROR(NULL, NULL, "Socket received illegal message %d.\n", msg.message); - return(-1); - } - receive_message(msg.u.msg.type, msg.u.msg.ref, &msg.u.msg.param); - work = 1; - } else { - if (errno != EWOULDBLOCK) { + if (len > 0) { + if (len != sizeof(msg)) { + CERROR(NULL, NULL, "Socket short read. (len %d)\n", len); + goto error; + } + if (msg.message != ADMIN_MESSAGE) { + CERROR(NULL, NULL, "Socket received illegal message %d.\n", msg.message); + goto error; + } + receive_message(msg.u.msg.type, msg.u.msg.ref, &msg.u.msg.param); + } else { CERROR(NULL, NULL, "Socket failed (errno %d).\n", errno); - return(-1); + goto error; } } - /* write to socket */ - if (!admin_first) - return(work); - admin = admin_first; - len = write(lcr_sock, &admin->msg, sizeof(msg)); - if (len == 0) { - CERROR(NULL, NULL, "Socket closed.\n"); - return(-1); // socket closed - } - if (len > 0) { - if (len != sizeof(msg)) { - CERROR(NULL, NULL, "Socket short write. (len %d)\n", len); - return(-1); // socket error + if ((what & LCR_FD_WRITE)) { + /* write to socket */ + if (!admin_first) { + socket_fd.when &= ~LCR_FD_WRITE; + return 0; } - /* free head */ - admin_first = admin->next; - free(admin); - - work = 1; - } else { - if (errno != EWOULDBLOCK) { + admin = admin_first; + len = write(lcr_sock, &admin->msg, sizeof(msg)); + if (len == 0) { + CERROR(NULL, NULL, "Socket closed.\n"); + goto error; + } + if (len > 0) { + if (len != sizeof(msg)) { + CERROR(NULL, NULL, "Socket short write. (len %d)\n", len); + goto error; + } + /* free head */ + admin_first = admin->next; + free(admin); + global_change = 1; + } else { CERROR(NULL, NULL, "Socket failed (errno %d).\n", errno); - return(-1); + goto error; } } - return(work); + return 0; } /* @@ -1494,16 +1569,14 @@ int handle_socket(void) */ int open_socket(void) { - int ret; int conn; struct sockaddr_un sock_address; - unsigned int on = 1; union parameter param; /* open socket */ if ((lcr_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { CERROR(NULL, NULL, "Failed to create socket.\n"); - return(lcr_sock); + return lcr_sock; } /* set socket address and name */ @@ -1516,29 +1589,28 @@ int open_socket(void) close(lcr_sock); lcr_sock = -1; CDEBUG(NULL, NULL, "Failed to connect to socket '%s'. Is LCR running?\n", sock_address.sun_path); - return(conn); + return conn; } - /* set non-blocking io */ - if ((ret = ioctl(lcr_sock, FIONBIO, (unsigned char *)(&on))) < 0) { - close(lcr_sock); - lcr_sock = -1; - CERROR(NULL, NULL, "Failed to set socket into non-blocking IO.\n"); - return(ret); - } + /* register socket fd */ + memset(&socket_fd, 0, sizeof(socket_fd)); + socket_fd.fd = lcr_sock; + register_fd(&socket_fd, LCR_FD_READ | LCR_FD_EXCEPT, handle_socket, NULL, 0); /* enque hello message */ memset(¶m, 0, sizeof(param)); strcpy(param.hello.application, "asterisk"); send_message(MESSAGE_HELLO, 0, ¶m); - return(lcr_sock); + return lcr_sock; } void close_socket(void) { struct admin_list *admin, *temp; - + + unregister_fd(&socket_fd); + /* flush pending messages */ admin = admin_first; while(admin) { @@ -1552,13 +1624,24 @@ void close_socket(void) if (lcr_sock >= 0) close(lcr_sock); lcr_sock = -1; + global_change = 1; } /* sending queue to asterisk */ -static int queue_send(void) +static int wake_event(struct lcr_fd *fd, unsigned int what, void *instance, int index) +{ + char byte; + + read(wake_pipe[0], &byte, 1); + + wake_global = 0; + + return 0; +} + +static void handle_queue() { - int work = 0; struct chan_call *call; struct ast_channel *ast; struct ast_frame fr; @@ -1569,156 +1652,124 @@ static int queue_send(void) p = call->queue_string; ast = call->ast; if (*p && ast) { - /* there is something to queue */ - if (!ast_channel_trylock(ast)) { /* succeed */ - while(*p) { - switch (*p) { - case 'T': - CDEBUG(call, ast, "Sending queued PROGRESS to Asterisk.\n"); - ast_queue_control(ast, AST_CONTROL_PROGRESS); - break; - case 'P': - CDEBUG(call, ast, "Sending queued PROCEEDING to Asterisk.\n"); - ast_queue_control(ast, AST_CONTROL_PROCEEDING); - break; - case 'R': - CDEBUG(call, ast, "Sending queued RINGING to Asterisk.\n"); - ast_queue_control(ast, AST_CONTROL_RINGING); - ast_setstate(ast, AST_STATE_RINGING); - break; - case 'N': - CDEBUG(call, ast, "Sending queued ANSWER to Asterisk.\n"); - ast_queue_control(ast, AST_CONTROL_ANSWER); - break; - case 'H': - CDEBUG(call, ast, "Sending queued HANGUP to Asterisk.\n"); - ast_queue_hangup(ast); - break; - case '1': case '2': case '3': case 'A': - case '4': case '5': case '6': case 'B': - case '7': case '8': case '9': case 'C': - case '*': case '0': case '#': case 'D': - CDEBUG(call, ast, "Sending queued digit '%c' to Asterisk.\n", *p); - /* send digit to asterisk */ - memset(&fr, 0, sizeof(fr)); - - #ifdef LCR_FOR_ASTERISK - fr.frametype = AST_FRAME_DTMF_BEGIN; - #endif - - #ifdef LCR_FOR_CALLWEAVER - fr.frametype = AST_FRAME_DTMF; - #endif - - fr.subclass = *p; - fr.delivery = ast_tv(0, 0); - ast_queue_frame(ast, &fr); - - #ifdef LCR_FOR_ASTERISK - fr.frametype = AST_FRAME_DTMF_END; - ast_queue_frame(ast, &fr); - #endif - - break; - default: - CDEBUG(call, ast, "Ignoring queued digit 0x%02x.\n", *p); - } - p++; + ast_channel_lock(ast); + while(*p) { + switch (*p) { + case 'T': + CDEBUG(call, ast, "Sending queued PROGRESS to Asterisk.\n"); + ast_queue_control(ast, AST_CONTROL_PROGRESS); + break; + case 'P': + CDEBUG(call, ast, "Sending queued PROCEEDING to Asterisk.\n"); + ast_queue_control(ast, AST_CONTROL_PROCEEDING); + break; + case 'R': + CDEBUG(call, ast, "Sending queued RINGING to Asterisk.\n"); + ast_queue_control(ast, AST_CONTROL_RINGING); + ast_setstate(ast, AST_STATE_RINGING); + break; + case 'N': + CDEBUG(call, ast, "Sending queued ANSWER to Asterisk.\n"); + ast_queue_control(ast, AST_CONTROL_ANSWER); + break; + case 'H': + CDEBUG(call, ast, "Sending queued HANGUP to Asterisk.\n"); + ast_queue_hangup(ast); + break; + case '1': case '2': case '3': case 'A': + case '4': case '5': case '6': case 'B': + case '7': case '8': case '9': case 'C': + case '*': case '0': case '#': case 'D': + CDEBUG(call, ast, "Sending queued digit '%c' to Asterisk.\n", *p); + /* send digit to asterisk */ + memset(&fr, 0, sizeof(fr)); + + #ifdef LCR_FOR_ASTERISK + fr.frametype = AST_FRAME_DTMF_BEGIN; + #endif + + #ifdef LCR_FOR_CALLWEAVER + fr.frametype = AST_FRAME_DTMF; + #endif + + fr.subclass = *p; + fr.delivery = ast_tv(0, 0); + ast_queue_frame(ast, &fr); + + #ifdef LCR_FOR_ASTERISK + fr.frametype = AST_FRAME_DTMF_END; + ast_queue_frame(ast, &fr); + #endif + + break; + default: + CDEBUG(call, ast, "Ignoring queued digit 0x%02x.\n", *p); } - call->queue_string[0] = '\0'; - ast_channel_unlock(ast); - work = 1; + p++; } + call->queue_string[0] = '\0'; + ast_channel_unlock(ast); } call = call->next; } +} + +static int handle_retry(struct lcr_timer *timer, void *instance, int index) +{ + CDEBUG(NULL, NULL, "Retry to open socket.\n"); + if (open_socket() < 0) + schedule_timer(&socket_retry, SOCKET_RETRY_TIMER, 0); + + return 0; +} - return work; +void lock_chan(void) +{ + ast_mutex_lock(&chan_lock); } -/* signal handler */ -void sighandler(int sigset) +void unlock_chan(void) { + ast_mutex_unlock(&chan_lock); } /* chan_lcr thread */ static void *chan_thread(void *arg) { - int work; - int ret; - union parameter param; - time_t retry = 0, now; + if (pipe(wake_pipe) < 0) { + CERROR(NULL, NULL, "Failed to open pipe.\n"); + return NULL; + } + memset(&wake_fd, 0, sizeof(wake_fd)); + wake_fd.fd = wake_pipe[0]; + register_fd(&wake_fd, LCR_FD_READ, wake_event, NULL, 0); + + memset(&socket_retry, 0, sizeof(socket_retry)); + add_timer(&socket_retry, handle_retry, NULL, 0); bchannel_pid = getpid(); -// signal(SIGPIPE, sighandler); - - memset(¶m, 0, sizeof(union parameter)); - if (lcr_sock < 0) - time(&retry); + /* open socket the first time */ + handle_retry(NULL, NULL, 0); ast_mutex_lock(&chan_lock); while(!quit) { - work = 0; - - if (lcr_sock > 0) { - /* handle socket */ - ret = handle_socket(); - if (ret < 0) { - CERROR(NULL, NULL, "Handling of socket failed - closing for some seconds.\n"); - close_socket(); - release_all_calls(); - time(&retry); - } - if (ret) - work = 1; - } else { - time(&now); - if (retry && now-retry > 5) { - CDEBUG(NULL, NULL, "Retry to open socket.\n"); - retry = 0; - if (open_socket() < 0) { - time(&retry); - } - work = 1; - } - - } - - /* handle mISDN */ - ret = bchannel_handle(); - if (ret) - work = 1; - - /* handle messages to asterisk */ - ret = queue_send(); - if (ret) - work = 1; - - /* delay if no work done */ - if (!work) { - ast_mutex_unlock(&chan_lock); - - #ifdef LCR_FOR_ASTERISK - usleep(30000); - #endif - - #ifdef LCR_FOR_CALLWEAVER - usleep(20000); - #endif - - ast_mutex_lock(&chan_lock); - } + handle_queue(); + select_main(0, &global_change, lock_chan, unlock_chan); } close_socket(); + del_timer(&socket_retry); + + unregister_fd(&wake_fd); + close(wake_pipe[0]); + close(wake_pipe[1]); + CERROR(NULL, NULL, "Thread exit.\n"); - - ast_mutex_unlock(&chan_lock); -// signal(SIGPIPE, SIG_DFL); + ast_mutex_unlock(&chan_lock); return NULL; } @@ -1963,7 +2014,7 @@ static int lcr_digit(struct ast_channel *ast, char digit) ast_mutex_unlock(&chan_lock); #ifdef LCR_FOR_ASTERISK - return(0); + return 0; } static int lcr_digit_end(struct ast_channel *ast, char digit, unsigned int duration) @@ -1997,7 +2048,7 @@ static int lcr_digit_end(struct ast_channel *ast, char digit, unsigned int durat send_digit_to_chan(ast, digit); } - return (0); + return 0; } static int lcr_answer(struct ast_channel *ast) @@ -2152,6 +2203,7 @@ static struct ast_frame *lcr_read(struct ast_channel *ast) if (len <= 0) { close(call->pipe[0]); call->pipe[0] = -1; + global_change = 1; ast_mutex_unlock(&chan_lock); return NULL; } else if (call->rebuffer && call->framepos < 160) { @@ -2704,10 +2756,6 @@ int load_module(void) ast_mutex_init(&chan_lock); ast_mutex_init(&log_lock); - if (open_socket() < 0) { - /* continue with closed socket */ - } - if (bchannel_initialize()) { CERROR(NULL, NULL, "Unable to open mISDN device\n"); close_socket(); @@ -2835,6 +2883,7 @@ int reload_module(void) #ifdef LCR_FOR_CALLWEAVER int usecount(void) +hae { int res; ast_mutex_lock(&usecnt_lock); diff --git a/chan_lcr.h b/chan_lcr.h index 5905ea1..3a64593 100644 --- a/chan_lcr.h +++ b/chan_lcr.h @@ -132,6 +132,8 @@ enum { }; +#define SOCKET_RETRY_TIMER 5 + #define CERROR(call, ast, arg...) chan_lcr_log(__LOG_ERROR, __FILE__, __LINE__, __FUNCTION__, call, ast, ##arg) #define CDEBUG(call, ast, arg...) chan_lcr_log(__LOG_NOTICE, __FILE__, __LINE__, __FUNCTION__, call, ast, ##arg) void chan_lcr_log(int type, const char *file, int line, const char *function, struct chan_call *call, struct ast_channel *ast, const char *fmt, ...); diff --git a/crypt.cpp b/crypt.cpp index 44e901c..125b516 100644 --- a/crypt.cpp +++ b/crypt.cpp @@ -549,7 +549,8 @@ static void *keyengine_child(void *arg) PDEBUG((DEBUG_EPOINT | DEBUG_CRYPT), "child process done after using libcrypto with return value %d\n", apppbx->e_crypt_keyengine_return); /* exit process */ - apppbx->ea_endpoint->ep_use--; + if (--apppbx->ea_endpoint->ep_use <= 0) + trigger_work(&apppbx->ea_endpoint->ep_delete); FREE(args, sizeof(struct auth_args)); amemuse--; return(NULL); @@ -588,37 +589,47 @@ void EndpointAppPBX::cryptman_keyengine(int job) /* handler for authentication (called by apppbx's handler) */ -void EndpointAppPBX::cryptman_handler(void) +int crypt_handler(struct lcr_timer *timer, void *instance, int index) { - if (e_crypt_keyengine_busy) { - if (e_crypt_keyengine_return < 0) { - e_crypt_keyengine_busy = 0; - cryptman_message(CK_ERROR_IND, NULL, 0); + class EndpointAppPBX *ea = (class EndpointAppPBX *)instance; + struct timeval current_time; + + if (ea->e_crypt_keyengine_busy) { + if (ea->e_crypt_keyengine_return < 0) { + ea->e_crypt_keyengine_busy = 0; + ea->cryptman_message(CK_ERROR_IND, NULL, 0); } else - if (e_crypt_keyengine_return > 0) { - switch(e_crypt_keyengine_busy) { + if (ea->e_crypt_keyengine_return > 0) { + switch(ea->e_crypt_keyengine_busy) { case CK_GENRSA_REQ: - e_crypt_keyengine_busy = 0; - cryptman_message(CK_GENRSA_CONF, NULL, 0); + ea->e_crypt_keyengine_busy = 0; + ea->cryptman_message(CK_GENRSA_CONF, NULL, 0); break; case CK_CPTRSA_REQ: - e_crypt_keyengine_busy = 0; - cryptman_message(CK_CPTRSA_CONF, NULL, 0); + ea->e_crypt_keyengine_busy = 0; + ea->cryptman_message(CK_CPTRSA_CONF, NULL, 0); break; case CK_DECRSA_REQ: - e_crypt_keyengine_busy = 0; - cryptman_message(CK_DECRSA_CONF, NULL, 0); + ea->e_crypt_keyengine_busy = 0; + ea->cryptman_message(CK_DECRSA_CONF, NULL, 0); break; } } } /* check for event, make next event */ - if (e_crypt_timeout_sec) if (e_crypt_timeout_sece_crypt_timeout_sec) if (ea->e_crypt_timeout_sece_crypt_timeout_sec==current_time.tv_sec && ea->e_crypt_timeout_usece_crypt_timeout_sec = 0; + ea->e_crypt_timeout_usec = 0; + ea->cryptman_message(CT_TIMEOUT, NULL, 0); } + + /* trigger until state is 0 */ + if (ea->e_crypt_state != CM_ST_NULL) + schedule_timer(&ea->e_crypt_handler, 0, 100000); + + return 0; } @@ -679,6 +690,7 @@ void EndpointAppPBX::cr_activate(int message, unsigned char *param, int len) unsigned char buf[128] = ""; unsigned char msg; unsigned char bogomips[4], ran[4]; + struct timeval current_time; /* activate listener */ cryptman_msg2crengine(CR_LISTEN_REQ, NULL, 0); @@ -686,7 +698,8 @@ void EndpointAppPBX::cr_activate(int message, unsigned char *param, int len) msg = CMSG_IDENT; CM_ADDINF(CM_INFO_MESSAGE, 1, &msg); /* random number element */ - srandom(now_tv.tv_sec ^ now_tv.tv_usec ^ random()); + gettimeofday(¤t_time, NULL); + srandom(current_time.tv_sec ^ current_time.tv_usec ^ random()); e_crypt_random = random(); ran[0] = e_crypt_random >> 24; ran[1] = e_crypt_random >> 16; @@ -1484,6 +1497,8 @@ void EndpointAppPBX::cryptman_msg2user(int msg, const char *text) void EndpointAppPBX::cryptman_state(int state) { PDEBUG(DEBUG_CRYPT, "Changing state from %s to %s\n", statename(e_crypt_state), statename(state)); + if (state != CM_ST_NULL && e_crypt_state == CM_ST_NULL) + schedule_timer(&e_crypt_handler, 0, 100000); e_crypt_state = state; } @@ -1492,9 +1507,12 @@ void EndpointAppPBX::cryptman_state(int state) */ void EndpointAppPBX::cryptman_timeout(int secs) { + struct timeval current_time; + + gettimeofday(¤t_time, NULL); if (secs) { - e_crypt_timeout_sec = now_tv.tv_sec+secs; - e_crypt_timeout_usec = now_tv.tv_usec; + e_crypt_timeout_sec = current_time.tv_sec+secs; + e_crypt_timeout_usec = current_time.tv_usec; PDEBUG(DEBUG_CRYPT, "Changing timeout to %d seconds\n", secs); } else { e_crypt_timeout_sec = 0; diff --git a/crypt.h b/crypt.h index f5a030c..5659082 100644 --- a/crypt.h +++ b/crypt.h @@ -170,4 +170,4 @@ int cm_msg_num = sizeof(cm_msg_name) / sizeof(char *); void crc_init(void); unsigned int crc32(unsigned char *data, int len); int cryptman_encode_bch(unsigned char *data, int len, unsigned char *buf, int buf_len); - +int crypt_handler(struct lcr_timer *timer, void *instance, int index); diff --git a/dss1.cpp b/dss1.cpp index 75ff448..4a96e28 100644 --- a/dss1.cpp +++ b/dss1.cpp @@ -20,6 +20,8 @@ extern unsigned int mt_assign_pid; #include "ie.cpp" +static int delete_event(struct lcr_work *work, void *instance, int index); + /* * constructor */ @@ -29,6 +31,8 @@ Pdss1::Pdss1(int type, struct mISDNport *mISDNport, char *portname, struct port_ p_m_d_ntmode = mISDNport->ntmode; p_m_d_tespecial = mISDNport->tespecial; p_m_d_l3id = 0; + memset(&p_m_d_delete, 0, sizeof(p_m_d_delete)); + add_work(&p_m_d_delete, delete_event, this, 0); p_m_d_ces = -1; p_m_d_queue[0] = '\0'; p_m_d_notify_pending = NULL; @@ -44,6 +48,8 @@ Pdss1::Pdss1(int type, struct mISDNport *mISDNport, char *portname, struct port_ */ Pdss1::~Pdss1() { + del_work(&p_m_d_delete); + /* remove queued message */ if (p_m_d_notify_pending) message_free(p_m_d_notify_pending); @@ -255,7 +261,7 @@ int Pdss1::received_first_reply_to_setup(unsigned int cmd, int channel, int excl end_trace(); p_m_mISDNport->ml3->to_layer3(p_m_mISDNport->ml3, MT_RELEASE_COMPLETE, p_m_d_l3id, l3m); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return(-34); /* to epoint: no channel available */ } @@ -422,7 +428,7 @@ void Pdss1::setup_ind(unsigned int cmd, unsigned int pid, struct l3_msg *l3m) end_trace(); p_m_mISDNport->ml3->to_layer3(p_m_mISDNport->ml3, MT_RELEASE_COMPLETE, pid, l3m); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return; } p_m_d_l3id = pid; @@ -453,7 +459,7 @@ void Pdss1::setup_ind(unsigned int cmd, unsigned int pid, struct l3_msg *l3m) end_trace(); p_m_mISDNport->ml3->to_layer3(p_m_mISDNport->ml3, MT_RELEASE_COMPLETE, p_m_d_l3id, l3m); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return; } @@ -711,7 +717,7 @@ void Pdss1::setup_ind(unsigned int cmd, unsigned int pid, struct l3_msg *l3m) end_trace(); p_m_mISDNport->ml3->to_layer3(p_m_mISDNport->ml3, MT_RELEASE_COMPLETE, p_m_d_l3id, l3m); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return; } bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE); @@ -803,7 +809,7 @@ void Pdss1::setup_acknowledge_ind(unsigned int cmd, unsigned int pid, struct l3_ message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; message_put(message); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return; } @@ -851,7 +857,7 @@ void Pdss1::proceeding_ind(unsigned int cmd, unsigned int pid, struct l3_msg *l3 message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; message_put(message); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return; } message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_PROCEEDING); @@ -928,7 +934,7 @@ void Pdss1::alerting_ind(unsigned int cmd, unsigned int pid, struct l3_msg *l3m) message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; message_put(message); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return; } message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_ALERTING); @@ -1005,7 +1011,7 @@ void Pdss1::connect_ind(unsigned int cmd, unsigned int pid, struct l3_msg *l3m) message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; message_put(message); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return; } @@ -1117,7 +1123,7 @@ void Pdss1::disconnect_ind(unsigned int cmd, unsigned int pid, struct l3_msg *l3 free_epointlist(p_epointlist); } new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return; } @@ -1197,7 +1203,7 @@ void Pdss1::release_ind(unsigned int cmd, unsigned int pid, struct l3_msg *l3m) } new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); } /* CC_RESTART INDICATION */ @@ -1247,7 +1253,7 @@ void Pdss1::release_complete_ind(unsigned int cmd, unsigned int pid, struct l3_m } new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); } /* T312 timeout */ @@ -1351,8 +1357,8 @@ void Pdss1::hold_ind(unsigned int cmd, unsigned int pid, struct l3_msg *l3m) #if 0 epoint = find_epoint_id(ACTIVE_EPOINT(p_epointlist)); if (epoint && p_m_d_ntmode) { - p_m_timeout = p_settings.tout_hold; - time(&p_m_timer); + if (p_settings.tout_hold) + schedule_timer(&p_m_timeout, p_settings.tout_hold, 0); } #endif @@ -1410,7 +1416,7 @@ void Pdss1::retrieve_ind(unsigned int cmd, unsigned int pid, struct l3_msg *l3m) /* set hold state */ p_m_hold = 0; - p_m_timeout = 0; + unsched_timer(&p_m_timeout); /* acknowledge retrieve */ l3m = create_l3msg(); @@ -1488,7 +1494,7 @@ void Pdss1::suspend_ind(unsigned int cmd, unsigned int pid, struct l3_msg *l3m) p_m_mISDNport->ml3->to_layer3(p_m_mISDNport->ml3, MT_SUSPEND_ACKNOWLEDGE, p_m_d_l3id, l3m); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); } /* CC_RESUME INDICATION */ @@ -1515,7 +1521,7 @@ void Pdss1::resume_ind(unsigned int cmd, unsigned int pid, struct l3_msg *l3m) end_trace(); p_m_mISDNport->ml3->to_layer3(p_m_mISDNport->ml3, MT_RESUME_REJECT, pid, l3m); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return; } p_m_d_l3id = pid; @@ -1558,7 +1564,7 @@ void Pdss1::resume_ind(unsigned int cmd, unsigned int pid, struct l3_msg *l3m) end_trace(); p_m_mISDNport->ml3->to_layer3(p_m_mISDNport->ml3, MT_RESUME_REJECT, p_m_d_l3id, l3m); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return; } bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE); @@ -1778,8 +1784,8 @@ void Pdss1::message_isdn(unsigned int cmd, unsigned int pid, struct l3_msg *l3m) add_trace("callref", NULL, "0x%x", p_m_d_l3id); end_trace(); p_m_d_l3id = 0; + trigger_work(&p_m_d_delete); p_m_d_ces = -1; - p_m_delete = 1; /* sending release to endpoint in case we still have an endpoint * this is because we don't get any response if a release_complete is received (or a release in release state) */ @@ -1814,35 +1820,39 @@ void Pdss1::new_state(int state) /* set timeout */ if (state == PORT_STATE_IN_OVERLAP) { - p_m_timeout = p_m_mISDNport->ifport->tout_dialing; - time(&p_m_timer); + if (p_m_mISDNport->ifport->tout_dialing) + schedule_timer(&p_m_timeout, p_m_mISDNport->ifport->tout_dialing, 0); } if (state != p_state) { + unsched_timer(&p_m_timeout); if (state == PORT_STATE_IN_SETUP || state == PORT_STATE_OUT_SETUP || state == PORT_STATE_IN_OVERLAP || state == PORT_STATE_OUT_OVERLAP) { - p_m_timeout = p_m_mISDNport->ifport->tout_setup; - time(&p_m_timer); + if (p_m_mISDNport->ifport->tout_setup) + schedule_timer(&p_m_timeout, p_m_mISDNport->ifport->tout_setup, 0); } if (state == PORT_STATE_IN_PROCEEDING || state == PORT_STATE_OUT_PROCEEDING) { - p_m_timeout = p_m_mISDNport->ifport->tout_proceeding; - time(&p_m_timer); + if (p_m_mISDNport->ifport->tout_proceeding) + schedule_timer(&p_m_timeout, p_m_mISDNport->ifport->tout_proceeding, 0); } if (state == PORT_STATE_IN_ALERTING || state == PORT_STATE_OUT_ALERTING) { - p_m_timeout = p_m_mISDNport->ifport->tout_alerting; - time(&p_m_timer); + if (p_m_mISDNport->ifport->tout_alerting) + schedule_timer(&p_m_timeout, p_m_mISDNport->ifport->tout_alerting, 0); } +#if 0 if (state == PORT_STATE_CONNECT || state == PORT_STATE_CONNECT_WAITING) { - p_m_timeout = 0; + if (p_m_mISDNport->ifport->tout_connect) + schedule_timer(&p_m_timeout, p_m_mISDNport->ifport->tout_connect, 0); } +#endif if (state == PORT_STATE_IN_DISCONNECT || state == PORT_STATE_OUT_DISCONNECT) { - p_m_timeout = p_m_mISDNport->ifport->tout_disconnect; - time(&p_m_timer); + if (p_m_mISDNport->ifport->tout_disconnect) + schedule_timer(&p_m_timeout, p_m_mISDNport->ifport->tout_disconnect, 0); } } @@ -1850,23 +1860,15 @@ void Pdss1::new_state(int state) } -/* - * handler - */ -int Pdss1::handler(void) +/* deletes only if l3id is release, otherwhise it will be triggered then */ +static int delete_event(struct lcr_work *work, void *instance, int index) { - int ret; + class Pdss1 *isdnport = (class Pdss1 *)instance; - if ((ret = PmISDN::handler())) - return(ret); + if (!isdnport->p_m_d_l3id) + delete isdnport; - /* handle destruction */ - if (p_m_delete && p_m_d_l3id==0) { - delete this; - return(-1); - } - - return(0); + return 0; } @@ -1921,7 +1923,7 @@ void Pdss1::message_setup(unsigned int epoint_id, int message_id, union paramete message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; message_put(message); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return; } @@ -1987,7 +1989,7 @@ void Pdss1::message_setup(unsigned int epoint_id, int message_id, union paramete message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; message_put(message); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return; } p_m_d_l3id = mt_assign_pid; @@ -2419,6 +2421,7 @@ void Pdss1::message_connect(unsigned int epoint_id, int message_id, union parame l3_msg *l3m; int type, plan, present, screen; class Endpoint *epoint; + time_t current_time; /* NT-MODE in setup state we must send PROCEEDING first */ if (p_m_d_ntmode && p_state==PORT_STATE_IN_SETUP) { @@ -2513,7 +2516,8 @@ void Pdss1::message_connect(unsigned int epoint_id, int message_id, union parame /* date & time */ if (p_m_d_ntmode || p_m_d_tespecial) { epoint = find_epoint_id(epoint_id); - enc_ie_date(l3m, now, p_settings.no_seconds); + time(¤t_time); + enc_ie_date(l3m, current_time, p_settings.no_seconds); } end_trace(); /* finally send message */ @@ -2552,7 +2556,7 @@ if (/* ||*/ p_state==PORT_STATE_OUT_SETUP) { end_trace(); p_m_mISDNport->ml3->to_layer3(p_m_mISDNport->ml3, MT_RELEASE_COMPLETE, p_m_d_l3id, l3m); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_d_delete); return; } diff --git a/dss1.h b/dss1.h index 6683a52..f2486ab 100644 --- a/dss1.h +++ b/dss1.h @@ -16,9 +16,9 @@ class Pdss1 : public PmISDN Pdss1(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode); ~Pdss1(); unsigned int p_m_d_l3id; /* current l3 process id */ + struct lcr_work p_m_d_delete; /* timer for audio transmission */ void message_isdn(unsigned int cmd, unsigned int pid, struct l3_msg *l3m); int p_m_d_ces; /* ntmode: tei&sapi */ - int handler(void); int message_epoint(unsigned int epoint_id, int message, union parameter *param); int p_m_d_ntmode; /* flags the nt-mode */ diff --git a/endpoint.cpp b/endpoint.cpp index 28ea12f..cda79c2 100644 --- a/endpoint.cpp +++ b/endpoint.cpp @@ -33,6 +33,7 @@ class Endpoint *find_epoint_id(unsigned int epoint_id) return(NULL); } +int delete_endpoint(struct lcr_work *work, void *instance, int index); /* * endpoint constructor (link with either port or join id) @@ -48,6 +49,8 @@ Endpoint::Endpoint(unsigned int port_id, unsigned int join_id) ep_portlist = NULL; ep_app = NULL; + memset(&ep_delete, 0, sizeof(ep_delete)); + add_work(&ep_delete, delete_endpoint, this, 0); ep_use = 1; /* add endpoint to chain */ @@ -125,6 +128,8 @@ Endpoint::~Endpoint(void) FATAL("Endpoint not in Endpoint's list.\n"); *tempp = next; + del_work(&ep_delete); + /* free */ PDEBUG(DEBUG_EPOINT, "removed endpoint %d.\n", ep_serial); } @@ -183,17 +188,13 @@ void Endpoint::free_portlist(struct port_list *portlist) } -/* handler for endpoint - */ -int Endpoint::handler(void) +int delete_endpoint(struct lcr_work *work, void *instance, int index) { - if (ep_use <= 0) { - delete this; - return(-1); - } + class Endpoint *ep = (class Endpoint *)instance; - /* call application handler */ - if (ep_app) - return(ep_app->handler()); - return(0); + if (ep->ep_use <= 0) + delete ep; + + return 0; } + diff --git a/endpoint.h b/endpoint.h index 963b5dd..a3080bc 100644 --- a/endpoint.h +++ b/endpoint.h @@ -26,7 +26,6 @@ class Endpoint ~Endpoint(); class Endpoint *next; /* next in list */ unsigned int ep_serial; /* a unique serial to identify */ - int handler(void); /* applocaton relation */ class EndpointApp *ep_app; /* link to application class */ @@ -41,6 +40,7 @@ class Endpoint /* if still used by threads */ int ep_use; + struct lcr_work ep_delete; /* application indipendant states */ int ep_park; /* indicates that the epoint is parked */ diff --git a/endpointapp.cpp b/endpointapp.cpp index a3c6f56..158b3d6 100644 --- a/endpointapp.cpp +++ b/endpointapp.cpp @@ -28,11 +28,6 @@ EndpointApp::~EndpointApp(void) classuse--; } -int EndpointApp::handler(void) -{ - return(0); -} - /* mini application for test purpose only */ void EndpointApp::ea_message_port(unsigned int port_id, int message_type, union parameter *param) diff --git a/endpointapp.h b/endpointapp.h index a330a63..a24faae 100644 --- a/endpointapp.h +++ b/endpointapp.h @@ -18,7 +18,6 @@ class EndpointApp virtual ~EndpointApp(); class Endpoint *ea_endpoint; - virtual int handler(void); virtual void ea_message_port(unsigned int port_id, int message, union parameter *param); virtual void ea_message_join(unsigned int join_id, int message, union parameter *param); }; diff --git a/gsm.cpp b/gsm.cpp index e747ff0..3358329 100644 --- a/gsm.cpp +++ b/gsm.cpp @@ -32,8 +32,16 @@ extern int bsc_shutdown_net(struct gsm_network *net); void talloc_ctx_init(void); void on_dso_load_token(void); void on_dso_load_rrlp(void); +void on_dso_load_ho_dec(void); +int bts_model_unknown_init(void); +int bts_model_bs11_init(void); +int bts_model_nanobts_init(void); static struct debug_target *stderr_target; +/* timer to store statistics */ +#define DB_SYNC_INTERVAL 60, 0 +static struct timer_list db_sync_timer; + #include "gsm_audio.h" #undef AF_ISDN @@ -47,6 +55,18 @@ struct lcr_gsm *gsm = NULL; static unsigned int new_callref = 1; +/* timer handling */ +static int _db_store_counter(struct counter *counter, void *data) +{ + return db_store_counter(counter); +} + +static void db_sync_timer_cb(void *data) +{ + /* store counters to database and re-schedule */ + counters_for_each(_db_store_counter, NULL); + bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL); +} /* * create and send mncc message @@ -70,6 +90,7 @@ static int send_and_free_mncc(struct gsm_network *net, unsigned int msg_type, vo return ret; } +static int delete_event(struct lcr_work *work, void *instance, int index); /* * constructor @@ -77,6 +98,8 @@ static int send_and_free_mncc(struct gsm_network *net, unsigned int msg_type, vo Pgsm::Pgsm(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode) : PmISDN(type, mISDNport, portname, settings, channel, exclusive, mode) { p_callerinfo.itype = (mISDNport->ifport->interface->extension)?INFO_ITYPE_ISDN_EXTENSION:INFO_ITYPE_ISDN; + memset(&p_m_g_delete, 0, sizeof(p_m_g_delete)); + add_work(&p_m_g_delete, delete_event, this, 0); p_m_g_callref = 0; p_m_g_mode = 0; p_m_g_gsm_b_sock = -1; @@ -87,7 +110,7 @@ Pgsm::Pgsm(int type, struct mISDNport *mISDNport, char *portname, struct port_se p_m_g_encoder = gsm_audio_create(); if (!p_m_g_encoder || !p_m_g_decoder) { PERROR("Failed to create GSM audio codec instance\n"); - p_m_delete = 1; + trigger_work(&p_m_g_delete); } p_m_g_rxpos = 0; p_m_g_tch_connected = 0; @@ -102,6 +125,8 @@ Pgsm::~Pgsm() { PDEBUG(DEBUG_GSM, "Destroyed GSM process(%s).\n", p_name); + del_work(&p_m_g_delete); + /* remove queued message */ if (p_m_g_notify_pending) message_free(p_m_g_notify_pending); @@ -121,18 +146,21 @@ Pgsm::~Pgsm() /* close bsc side bchannel */ void Pgsm::bchannel_close(void) { - if (p_m_g_gsm_b_sock > -1) + if (p_m_g_gsm_b_sock > -1) { + unregister_fd(&p_m_g_gsm_b_fd); close(p_m_g_gsm_b_sock); + } p_m_g_gsm_b_sock = -1; p_m_g_gsm_b_index = -1; p_m_g_gsm_b_active = 0; } +static int b_handler(struct lcr_fd *fd, unsigned int what, void *instance, int index); + /* open bsc side bchannel */ int Pgsm::bchannel_open(int index) { int ret; - unsigned int on = 1; struct sockaddr_mISDN addr; struct mISDNhead act; @@ -148,14 +176,10 @@ int Pgsm::bchannel_open(int index) bchannel_close(); return(ret); } - - /* set nonblocking io */ - ret = ioctl(p_m_g_gsm_b_sock, FIONBIO, &on); - if (ret < 0) { - PERROR("Failed to set bchannel-socket index %d into nonblocking IO\n", index); - bchannel_close(); - return(ret); - } + memset(&p_m_g_gsm_b_fd, 0, sizeof(p_m_g_gsm_b_fd.fd)); + p_m_g_gsm_b_fd.fd = p_m_g_gsm_b_sock; + register_fd(&p_m_g_gsm_b_fd, LCR_FD_READ, b_handler, this, 0); + /* bind socket to bchannel */ addr.family = AF_ISDN; @@ -374,7 +398,7 @@ void Pgsm::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_mnc end_trace(); send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_g_delete); return; } p_m_g_callref = callref; @@ -395,7 +419,7 @@ void Pgsm::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_mnc end_trace(); send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_g_delete); return; } @@ -467,7 +491,7 @@ void Pgsm::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_mnc end_trace(); send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_g_delete); return; } bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE); @@ -725,7 +749,7 @@ void Pgsm::disc_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc free_epointlist(p_epointlist); } new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_g_delete); } /* CC_RELEASE INDICATION */ @@ -754,7 +778,7 @@ void Pgsm::rel_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc free_epointlist(p_epointlist); } new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_g_delete); } /* NOTIFY INDICATION */ @@ -985,7 +1009,7 @@ void Pgsm::message_setup(unsigned int epoint_id, int message_id, union parameter message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; message_put(message); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_g_delete); return; } @@ -999,7 +1023,7 @@ void Pgsm::message_setup(unsigned int epoint_id, int message_id, union parameter message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; message_put(message); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_g_delete); return; } @@ -1019,7 +1043,7 @@ void Pgsm::message_setup(unsigned int epoint_id, int message_id, union parameter message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; message_put(message); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_g_delete); return; } bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE); @@ -1351,7 +1375,7 @@ void Pgsm::message_release(unsigned int epoint_id, int message_id, union paramet send_and_free_mncc((struct gsm_network *)gsm->network, mncc->msg_type, mncc); new_state(PORT_STATE_RELEASE); - p_m_delete = 1; + trigger_work(&p_m_g_delete); return; } @@ -1434,28 +1458,29 @@ int Pgsm::message_epoint(unsigned int epoint_id, int message_id, union parameter } +/* deletes only if l3id is release, otherwhise it will be triggered then */ +static int delete_event(struct lcr_work *work, void *instance, int index) +{ + class Pgsm *gsmport = (class Pgsm *)instance; + + delete gsmport; + + return 0; +} + /* - * handler + * handler of bchannel events */ -int Pgsm::handler(void) +static int b_handler(struct lcr_fd *fd, unsigned int what, void *instance, int index) { + class Pgsm *gsmport = (class Pgsm *)instance; int ret; - int work = 0; unsigned char buffer[2048+MISDN_HEADER_LEN]; struct mISDNhead *hh = (struct mISDNhead *)buffer; - if ((ret = PmISDN::handler())) - return(ret); - - /* handle destruction */ - if (p_m_delete) { - delete this; - return(-1); - } - /* handle message from bchannel */ - if (p_m_g_gsm_b_sock > -1) { - ret = recv(p_m_g_gsm_b_sock, buffer, sizeof(buffer), 0); + if (gsmport->p_m_g_gsm_b_sock > -1) { + ret = recv(gsmport->p_m_g_gsm_b_sock, buffer, sizeof(buffer), 0); if (ret >= (int)MISDN_HEADER_LEN) { switch(hh->prim) { /* we don't care about confirms, we use rx data to sync tx */ @@ -1463,37 +1488,21 @@ int Pgsm::handler(void) break; /* we receive audio data, we respond to it AND we send tones */ case PH_DATA_IND: - bchannel_receive(hh, buffer+MISDN_HEADER_LEN, ret-MISDN_HEADER_LEN); + gsmport->bchannel_receive(hh, buffer+MISDN_HEADER_LEN, ret-MISDN_HEADER_LEN); break; case PH_ACTIVATE_IND: - p_m_g_gsm_b_active = 1; + gsmport->p_m_g_gsm_b_active = 1; break; case PH_DEACTIVATE_IND: - p_m_g_gsm_b_active = 0; + gsmport->p_m_g_gsm_b_active = 0; break; } - work = 1; } else { if (ret < 0 && errno != EWOULDBLOCK) PERROR("Read from GSM port, index %d failed with return code %d\n", ret); } } - return(work); -} - - -/* - * handles bsc select function within LCR's main loop - */ -int handle_gsm(void) -{ - int ret1, ret2; - - ret1 = bsc_upqueue((struct gsm_network *)gsm->network); - ret2 = bsc_select_main(1); /* polling */ - if (ret1 || ret2) - return 1; return 0; } @@ -1599,10 +1608,21 @@ int gsm_init(void) mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; int pcapfd, rc; + debug_init(); tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); talloc_ctx_init(); on_dso_load_token(); on_dso_load_rrlp(); + on_dso_load_ho_dec(); + stderr_target = debug_target_create_stderr(); + debug_add_target(stderr_target); + + bts_model_unknown_init(); + bts_model_bs11_init(); + bts_model_nanobts_init(); + + /* enable filters */ + debug_set_all_filter(stderr_target, 1); /* seed the PRNG */ srand(time(NULL)); @@ -1618,7 +1638,6 @@ int gsm_init(void) } /* set debug */ - stderr_target = debug_target_create_stderr(); if (gsm->conf.debug[0]) debug_parse_category_mask(stderr_target, gsm->conf.debug); @@ -1655,6 +1674,11 @@ int gsm_init(void) } printf("DB: Database prepared.\n"); + /* setup the timer */ + db_sync_timer.cb = db_sync_timer_cb; + db_sync_timer.data = NULL; + bsc_schedule_timer(&db_sync_timer, DB_SYNC_INTERVAL); + /* bootstrap network */ if (gsm->conf.openbsc_cfg[0] == '/') SCPY(cfg, gsm->conf.openbsc_cfg); @@ -1675,3 +1699,18 @@ int gsm_init(void) return 0; } +/* + * handles bsc select function within LCR's main loop + */ +int handle_gsm(void) +{ + int ret1, ret2; + + ret1 = bsc_upqueue((struct gsm_network *)gsm->network); + debug_reset_context(); + ret2 = bsc_select_main(1); /* polling */ + if (ret1 || ret2) + return 1; + return 0; +} + diff --git a/gsm.h b/gsm.h index 15477ef..6c09fa8 100644 --- a/gsm.h +++ b/gsm.h @@ -33,9 +33,11 @@ class Pgsm : public PmISDN Pgsm(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode); ~Pgsm(); + struct lcr_work p_m_g_delete; /* timer for audio transmission */ unsigned int p_m_g_callref; /* ref by OpenBSC */ unsigned int p_m_g_mode; /* data/transparent mode */ int p_m_g_gsm_b_sock; /* gsm bchannel socket */ + struct lcr_fd p_m_g_gsm_b_fd; /* event node */ int p_m_g_gsm_b_index; /* gsm bchannel socket index to use */ int p_m_g_gsm_b_active; /* gsm bchannel socket is activated */ struct lcr_msg *p_m_g_notify_pending; /* queue for NOTIFY if not connected */ @@ -72,7 +74,6 @@ class Pgsm : public PmISDN void message_disconnect(unsigned int epoint_id, int message_id, union parameter *param); void message_release(unsigned int epoint_id, int message_id, union parameter *param); int message_epoint(unsigned int epoint_id, int message_id, union parameter *param); - int handler(void); }; extern char *gsm_conf_error; diff --git a/join.cpp b/join.cpp index b1a06bb..5dcbde2 100644 --- a/join.cpp +++ b/join.cpp @@ -91,15 +91,6 @@ void Join::message_epoint(unsigned int epoint_id, int message_type, union parame } -/* join process is called from the main loop - * it processes the current calling state. - * returns 0 if nothing was done - */ -int Join::handler(void) -{ - return(0); -} - /* free all join structures */ void join_free(void) { diff --git a/join.h b/join.h index 5c5f3f2..442aa5d 100644 --- a/join.h +++ b/join.h @@ -24,7 +24,6 @@ class Join virtual ~Join(); class Join *next; /* next node in list of joins */ virtual void message_epoint(unsigned int epoint_id, int message, union parameter *param); - virtual int handler(void); unsigned int j_type; /* join type (pbx or asterisk) */ unsigned int j_serial; /* serial/unique number of join */ diff --git a/joinpbx.cpp b/joinpbx.cpp index d3640b1..4b06540 100644 --- a/joinpbx.cpp +++ b/joinpbx.cpp @@ -202,6 +202,7 @@ void joinpbx_debug(class JoinPBX *joinpbx, const char *function) PDEBUG(DEBUG_JOIN, "end\n"); } +int update_bridge(struct lcr_work *work, void *instance, int index); /* * constructor for a new join @@ -223,11 +224,12 @@ JoinPBX::JoinPBX(class Endpoint *epoint) : Join() j_dialed[0] = '\0'; j_todial[0] = '\0'; j_pid = getpid(); - j_updatebridge = 0; j_partyline = 0; j_partyline_jingle = 0; j_multicause = 0; j_multilocation = 0; + memset(&j_updatebridge, 0, sizeof(j_updatebridge)); + add_work(&j_updatebridge, update_bridge, this, 0); /* initialize a relation only to the calling interface */ relation = j_relation = (struct join_relation *)MALLOC(sizeof(struct join_relation)); @@ -258,12 +260,23 @@ JoinPBX::~JoinPBX() cmemuse--; relation = rtemp; } + + del_work(&j_updatebridge); } /* bridge sets the audio flow of all bchannels assiociated to 'this' join * also it changes and notifies active/hold/conference states */ +int update_bridge(struct lcr_work *work, void *instance, int index) +{ + class JoinPBX *joinpbx = (class JoinPBX *)instance; + + joinpbx->bridge(); + + return 0; +} + void JoinPBX::bridge(void) { struct join_relation *relation; @@ -453,7 +466,7 @@ int JoinPBX::release(struct join_relation *relation, int location, int cause) /* remove from bridge */ if (relation->channel_state != 0) { relation->channel_state = 0; - j_updatebridge = 1; /* update bridge flag */ + trigger_work(&j_updatebridge); // note: if join is not released, bridge must be updated } @@ -677,7 +690,7 @@ void JoinPBX::message_epoint(unsigned int epoint_id, int message_type, union par SPRINT(message->param.connectinfo.id, "%d", j_partyline); message->param.connectinfo.ntype = INFO_NTYPE_UNKNOWN; message_put(message); - j_updatebridge = 1; /* update bridge flag */ + trigger_work(&j_updatebridge); if (j_partyline_jingle) play_jingle(1); break; @@ -686,7 +699,7 @@ void JoinPBX::message_epoint(unsigned int epoint_id, int message_type, union par PDEBUG(DEBUG_JOIN, "join received channel message: %d.\n", param->audiopath); if (relation->channel_state != param->audiopath) { relation->channel_state = param->audiopath; - j_updatebridge = 1; /* update bridge flag */ + trigger_work(&j_updatebridge); if (options.deb & DEBUG_JOIN) joinpbx_debug(this, "Join::message_epoint{after setting new channel state}"); } @@ -721,7 +734,7 @@ void JoinPBX::message_epoint(unsigned int epoint_id, int message_type, union par PDEBUG(DEBUG_JOIN, "join received channel message: %d.\n", param->audiopath); if (relation->channel_state != param->audiopath) { relation->channel_state = param->audiopath; - j_updatebridge = 1; /* update bridge flag */ + trigger_work(&j_updatebridge); if (options.deb & DEBUG_JOIN) joinpbx_debug(this, "Join::message_epoint{after setting new channel state}"); } @@ -739,7 +752,7 @@ void JoinPBX::message_epoint(unsigned int epoint_id, int message_type, union par new_state = track_notify(relation->rx_state, param->notifyinfo.notify); if (new_state != relation->rx_state) { relation->rx_state = new_state; - j_updatebridge = 1; + trigger_work(&j_updatebridge); if (options.deb & DEBUG_JOIN) joinpbx_debug(this, "Join::message_epoint{after setting new rx state}"); } @@ -895,30 +908,6 @@ void JoinPBX::message_epoint(unsigned int epoint_id, int message_type, union par } -/* join process is called from the main loop - * it processes the current calling state. - * returns 0 if join nothing was done - */ -int JoinPBX::handler(void) -{ -// struct join_relation *relation; -// char dialing[32][32]; -// int port[32]; -// int found; -// int i, j; -// char *p; - - /* the bridge must be updated */ - if (j_updatebridge) { - bridge(); - j_updatebridge = 0; - return(1); - } - - return(0); -} - - int track_notify(int oldstate, int notify) { int newstate = oldstate; diff --git a/joinpbx.h b/joinpbx.h index 18e38c0..7c05eee 100644 --- a/joinpbx.h +++ b/joinpbx.h @@ -50,7 +50,6 @@ class JoinPBX : public Join JoinPBX(class Endpoint *epoint); ~JoinPBX(); void message_epoint(unsigned int epoint_id, int message, union parameter *param); - int handler(void); int release(struct join_relation *relation, int location, int cause); char j_caller[32]; /* caller number */ @@ -60,7 +59,7 @@ class JoinPBX : public Join int j_multicause, j_multilocation; int j_pid; /* pid of join to generate bridge id */ - int j_updatebridge; /* bridge must be updated */ + struct lcr_work j_updatebridge; /* bridge must be updated */ struct join_relation *j_relation; /* list of endpoints that are related to the join */ int j_partyline; /* if set, join is conference room */ diff --git a/joinremote.cpp b/joinremote.cpp index a4f9380..167bbce 100644 --- a/joinremote.cpp +++ b/joinremote.cpp @@ -49,17 +49,6 @@ JoinRemote::~JoinRemote() { } - -/* join process is called from the main loop - * it processes the current calling state. - * returns 0 if join nothing was done - */ -int JoinRemote::handler(void) -{ - return(0); -} - - void JoinRemote::message_epoint(unsigned int epoint_id, int message_type, union parameter *param) { /* if endpoint has just been removed, but still a message in the que */ diff --git a/joinremote.h b/joinremote.h index b837da6..1582133 100644 --- a/joinremote.h +++ b/joinremote.h @@ -16,7 +16,6 @@ class JoinRemote : public Join ~JoinRemote(); void message_epoint(unsigned int epoint_id, int message, union parameter *param); void message_remote(int message_type, union parameter *param); - int handler(void); int j_remote_id; char j_remote_name[32]; diff --git a/lcradmin.c b/lcradmin.c index 222700b..0b6d627 100644 --- a/lcradmin.c +++ b/lcradmin.c @@ -25,6 +25,7 @@ #include "macro.h" #include "options.h" #include "join.h" +#include "select.h" #include "joinpbx.h" #include "extension.h" #include "message.h" diff --git a/mISDN.cpp b/mISDN.cpp index e531b14..3e7751e 100644 --- a/mISDN.cpp +++ b/mISDN.cpp @@ -45,6 +45,12 @@ int mISDN_rand_count = 0; unsigned int mt_assign_pid = ~0; int mISDNsocket = -1; +static int upqueue_pipe[2]; +static struct lcr_fd upqueue_fd; +int upqueue_avail = 0; + +static int mISDN_upqueue(struct lcr_fd *fd, unsigned int what, void *instance, int i); +static int mISDN_timeout(struct lcr_timer *timer, void *instance, int i); int mISDN_initialize(void) { @@ -74,6 +80,12 @@ int mISDN_initialize(void) } else mISDN_debug_init(0, NULL, NULL, NULL); + if (pipe(upqueue_pipe) < 0) + FATAL("Failed to open pipe\n"); + memset(&upqueue_fd, 0, sizeof(upqueue_fd.fd)); + upqueue_fd.fd = upqueue_pipe[0]; + register_fd(&upqueue_fd, LCR_FD_READ, mISDN_upqueue, NULL, 0); + return(0); } @@ -89,8 +101,17 @@ void mISDN_deinitialize(void) if (mISDNsocket > -1) close(mISDNsocket); + + if (upqueue_fd.inuse) { + unregister_fd(&upqueue_fd); + close(upqueue_pipe[0]); + close(upqueue_pipe[1]); + } + upqueue_avail = 0; } +int load_timer(struct lcr_timer *timer, void *instance, int index); + /* * constructor */ @@ -103,7 +124,6 @@ PmISDN::PmISDN(int type, mISDNport *mISDNport, char *portname, struct port_setti p_m_b_exclusive = 0; p_m_b_reserve = 0; p_m_b_mode = mode; - p_m_delete = 0; p_m_hold = 0; p_m_tx_gain = mISDNport->ifport->interface->tx_gain; p_m_rx_gain = mISDNport->ifport->interface->rx_gain; @@ -118,13 +138,15 @@ PmISDN::PmISDN(int type, mISDNport *mISDNport, char *portname, struct port_setti p_m_inband_send_on = 0; p_m_inband_receive_on = 0; p_m_dtmf = !mISDNport->ifport->nodtmf; - p_m_timeout = 0; - p_m_timer = 0; + memset(&p_m_timeout, 0, sizeof(p_m_timeout)); + add_timer(&p_m_timeout, mISDN_timeout, this, 0); p_m_remote_ref = 0; /* channel shall be exported to given remote */ p_m_remote_id = 0; /* remote admin socket */ SCPY(p_m_pipeline, mISDNport->ifport->interface->pipeline); /* audio */ + memset(&p_m_loadtimer, 0, sizeof(p_m_loadtimer)); + add_timer(&p_m_loadtimer, load_timer, this, 0); p_m_load = 0; p_m_last_tv_sec = 0; @@ -173,6 +195,9 @@ PmISDN::~PmISDN() { struct lcr_msg *message; + del_timer(&p_m_timeout); + del_timer(&p_m_loadtimer); + /* remove bchannel relation */ drop_bchannel(); @@ -357,6 +382,7 @@ void ph_control_block(struct mISDNport *mISDNport, class PmISDN *isdnport, int s end_trace(); } +static int b_sock_callback(struct lcr_fd *fd, unsigned int what, void *instance, int i); /* * subfunction for bchannel_event @@ -365,47 +391,40 @@ void ph_control_block(struct mISDNport *mISDNport, class PmISDN *isdnport, int s static int _bchannel_create(struct mISDNport *mISDNport, int i) { int ret; - unsigned int on = 1; struct sockaddr_mISDN addr; - if (mISDNport->b_socket[i] > -1) { + if (mISDNport->b_sock[i].inuse) { PERROR("Error: Socket already created for index %d\n", i); return(0); } /* open socket */ //#warning testing without DSP -// mISDNport->b_socket[i] = socket(PF_ISDN, SOCK_DGRAM, (mISDNport->b_mode[i]==B_MODE_HDLC)?ISDN_P_B_HDLC:ISDN_P_B_RAW); - mISDNport->b_socket[i] = socket(PF_ISDN, SOCK_DGRAM, (mISDNport->b_mode[i]==B_MODE_HDLC)?ISDN_P_B_L2DSPHDLC:ISDN_P_B_L2DSP); - if (mISDNport->b_socket[i] < 0) { +// mISDNport->b_sock[i].fd = socket(PF_ISDN, SOCK_DGRAM, (mISDNport->b_mode[i]==B_MODE_HDLC)?ISDN_P_B_HDLC:ISDN_P_B_RAW); + mISDNport->b_sock[i].fd = socket(PF_ISDN, SOCK_DGRAM, (mISDNport->b_mode[i]==B_MODE_HDLC)?ISDN_P_B_L2DSPHDLC:ISDN_P_B_L2DSP); + if (mISDNport->b_sock[i].fd < 0) { PERROR("Error: Failed to open bchannel-socket for index %d with mISDN-DSP layer. Did you load mISDN_dsp.ko?\n", i); return(0); } - - /* set nonblocking io */ - ret = ioctl(mISDNport->b_socket[i], FIONBIO, &on); - if (ret < 0) { - PERROR("Error: Failed to set bchannel-socket index %d into nonblocking IO\n", i); - close(mISDNport->b_socket[i]); - mISDNport->b_socket[i] = -1; - return(0); - } + /* register callback for read */ + register_fd(&mISDNport->b_sock[i], LCR_FD_READ, b_sock_callback, mISDNport, i); + /* bind socket to bchannel */ addr.family = AF_ISDN; addr.dev = mISDNport->portnum; addr.channel = i+1+(i>=15); - ret = bind(mISDNport->b_socket[i], (struct sockaddr *)&addr, sizeof(addr)); + ret = bind(mISDNport->b_sock[i].fd, (struct sockaddr *)&addr, sizeof(addr)); if (ret < 0) { PERROR("Error: Failed to bind bchannel-socket for index %d with mISDN-DSP layer (errno=%d). Did you load mISDN_dsp.ko?\n", i, errno); - close(mISDNport->b_socket[i]); - mISDNport->b_socket[i] = -1; + close(mISDNport->b_sock[i].fd); + unregister_fd(&mISDNport->b_sock[i]); return(0); } chan_trace_header(mISDNport, mISDNport->b_port[i], "BCHANNEL create socket", DIRECTION_OUT); add_trace("channel", NULL, "%d", i+1+(i>=15)); - add_trace("socket", NULL, "%d", mISDNport->b_socket[i]); + add_trace("socket", NULL, "%d", mISDNport->b_sock[i].fd); end_trace(); return(1); @@ -416,23 +435,23 @@ static int _bchannel_create(struct mISDNport *mISDNport, int i) * subfunction for bchannel_event * activate / deactivate request */ -static void _bchannel_activate(struct mISDNport *mISDNport, int i, int activate) +static void _bchannel_activate(struct mISDNport *mISDNport, int i, int activate, int timeout) { struct mISDNhead act; int ret; - if (mISDNport->b_socket[i] < 0) + if (!mISDNport->b_sock[i].inuse) return; act.prim = (activate)?PH_ACTIVATE_REQ:PH_DEACTIVATE_REQ; act.id = 0; - ret = sendto(mISDNport->b_socket[i], &act, MISDN_HEADER_LEN, 0, NULL, 0); + ret = sendto(mISDNport->b_sock[i].fd, &act, MISDN_HEADER_LEN, 0, NULL, 0); if (ret <= 0) - PERROR("Failed to send to socket %d\n", mISDNport->b_socket[i]); + PERROR("Failed to send to socket %d\n", mISDNport->b_sock[i].fd); /* trace */ chan_trace_header(mISDNport, mISDNport->b_port[i], activate ? "BCHANNEL activate" : "BCHANNEL deactivate", DIRECTION_OUT); add_trace("channel", NULL, "%d", i+1+(i>=15)); - if (mISDNport->b_timer[i]) + if (timeout) add_trace("event", NULL, "timeout recovery"); end_trace(); } @@ -447,9 +466,9 @@ static void _bchannel_configure(struct mISDNport *mISDNport, int i) struct PmISDN *port; int handle, mode; - if (mISDNport->b_socket[i] < 0) + if (!mISDNport->b_sock[i].inuse) return; - handle = mISDNport->b_socket[i]; + handle = mISDNport->b_sock[i].fd; port = mISDNport->b_port[i]; mode = mISDNport->b_mode[i]; if (!port) { @@ -491,7 +510,7 @@ void PmISDN::set_conf(int oldconf, int newconf) PDEBUG(DEBUG_BCHANNEL, "we change conference from conf=%d to conf=%d.\n", oldconf, newconf); if (p_m_b_index > -1) if (p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE) - ph_control(p_m_mISDNport, this, p_m_mISDNport->b_socket[p_m_b_index], (newconf)?DSP_CONF_JOIN:DSP_CONF_SPLIT, newconf, "DSP-CONF", newconf); + ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, (newconf)?DSP_CONF_JOIN:DSP_CONF_SPLIT, newconf, "DSP-CONF", newconf); } else PDEBUG(DEBUG_BCHANNEL, "we already have conf=%d.\n", newconf); } @@ -503,14 +522,14 @@ void PmISDN::set_conf(int oldconf, int newconf) */ static void _bchannel_destroy(struct mISDNport *mISDNport, int i) { - if (mISDNport->b_socket[i] < 0) + if (!mISDNport->b_sock[i].inuse) return; chan_trace_header(mISDNport, mISDNport->b_port[i], "BCHANNEL remove socket", DIRECTION_OUT); add_trace("channel", NULL, "%d", i+1+(i>=15)); - add_trace("socket", NULL, "%d", mISDNport->b_socket[i]); + add_trace("socket", NULL, "%d", mISDNport->b_sock[i].fd); end_trace(); - close(mISDNport->b_socket[i]); - mISDNport->b_socket[i] = -1; + close(mISDNport->b_sock[i].fd); + unregister_fd(&mISDNport->b_sock[i]); } @@ -610,7 +629,7 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) { class PmISDN *b_port = mISDNport->b_port[i]; int state = mISDNport->b_state[i]; - double timer = mISDNport->b_timer[i]; + int timer = -1; // no change unsigned int p_m_remote_ref = 0; unsigned int p_m_remote_id = 0; int p_m_tx_gain = 0; @@ -652,9 +671,9 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) } else { /* create stack and send activation request */ if (_bchannel_create(mISDNport, i)) { - _bchannel_activate(mISDNport, i, 1); + _bchannel_activate(mISDNport, i, 1, 0); state = B_STATE_ACTIVATING; - timer = now_d + B_TIMER_ACTIVATING; + timer = B_TIMER_ACTIVATING; } } break; @@ -711,9 +730,9 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) case B_STATE_ACTIVE: /* bchannel is active, so we deactivate */ - _bchannel_activate(mISDNport, i, 0); + _bchannel_activate(mISDNport, i, 0, 0); state = B_STATE_DEACTIVATING; - timer = now_d + B_TIMER_DEACTIVATING; + timer = B_TIMER_DEACTIVATING; break; default: @@ -773,9 +792,9 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) b_port->p_m_load = 0; } else { /* bchannel is active, but exported OR not used anymore (or has wrong stack config), so we deactivate */ - _bchannel_activate(mISDNport, i, 0); + _bchannel_activate(mISDNport, i, 0, 0); state = B_STATE_DEACTIVATING; - timer = now_d + B_TIMER_DEACTIVATING; + timer = B_TIMER_DEACTIVATING; } break; @@ -824,9 +843,9 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) case B_STATE_ACTIVE: /* bchannel is active, so we deactivate */ - _bchannel_activate(mISDNport, i, 0); + _bchannel_activate(mISDNport, i, 0, 0); state = B_STATE_DEACTIVATING; - timer = now_d + B_TIMER_DEACTIVATING; + timer = B_TIMER_DEACTIVATING; break; case B_STATE_REMOTE: @@ -872,9 +891,9 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) mISDNport->b_remote_ref[i] = p_m_remote_ref; } else { if (_bchannel_create(mISDNport, i)) { - _bchannel_activate(mISDNport, i, 1); + _bchannel_activate(mISDNport, i, 1, 0); state = B_STATE_ACTIVATING; - timer = now_d + B_TIMER_ACTIVATING; + timer = B_TIMER_ACTIVATING; } } } @@ -904,9 +923,9 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) mISDNport->b_remote_ref[i] = p_m_remote_ref; } else { if (_bchannel_create(mISDNport, i)) { - _bchannel_activate(mISDNport, i, 1); + _bchannel_activate(mISDNport, i, 1, 0); state = B_STATE_ACTIVATING; - timer = now_d + B_TIMER_ACTIVATING; + timer = B_TIMER_ACTIVATING; } } } @@ -926,13 +945,13 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) break; case B_STATE_ACTIVATING: - _bchannel_activate(mISDNport, i, 1); - timer = now_d + B_TIMER_ACTIVATING; + _bchannel_activate(mISDNport, i, 1, 1); + timer = B_TIMER_ACTIVATING; break; case B_STATE_DEACTIVATING: - _bchannel_activate(mISDNport, i, 0); - timer = now_d + B_TIMER_DEACTIVATING; + _bchannel_activate(mISDNport, i, 0, 1); + timer = B_TIMER_DEACTIVATING; break; default: @@ -945,7 +964,10 @@ void bchannel_event(struct mISDNport *mISDNport, int i, int event) } mISDNport->b_state[i] = state; - mISDNport->b_timer[i] = timer; + if (timer == 0) + unsched_timer(&mISDNport->b_timer[i]); + else if (timer > 0) + schedule_timer(&mISDNport->b_timer[i], timer, 0); } @@ -1204,31 +1226,46 @@ on empty load, remote-audio causes the load with the remote audio to be increase +--------------------+----------------------+ */ -int PmISDN::handler(void) +void PmISDN::update_load(void) +{ + /* don't trigger load event if: */ + if (!p_tone_name[0] && !p_m_crypt_msg_loops && !p_m_inband_send_on) + return; + + /* don't trigger load event if event already active */ + if (p_m_loadtimer.active) + return; + + schedule_timer(&p_m_loadtimer, 0, 0); /* no delay the first time */ +} + +int load_timer(struct lcr_timer *timer, void *instance, int index) +{ + class PmISDN *isdnport = (class PmISDN *)instance; + + isdnport->load_tx(); + + return 0; +} + +void PmISDN::load_tx(void) { - struct lcr_msg *message; int elapsed = 0; int ret; - - if ((ret = Port::handler())) - return(ret); + struct timeval current_time; /* get elapsed */ + gettimeofday(¤t_time, NULL); if (p_m_last_tv_sec) { - elapsed = 8000 * (now_tv.tv_sec - p_m_last_tv_sec) - + 8 * (now_tv.tv_usec/1000 - p_m_last_tv_msec); - } else { - /* set clock of first process ever in this instance */ - p_m_last_tv_sec = now_tv.tv_sec; - p_m_last_tv_msec = now_tv.tv_usec/1000; + elapsed = 8000 * (current_time.tv_sec - p_m_last_tv_sec) + + 8 * (current_time.tv_usec/1000 - p_m_last_tv_msec); } - /* process only if we have a minimum of samples, to make packets not too small */ - if (elapsed >= ISDN_TRANSMIT - && p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE) { - /* set clock of last process! */ - p_m_last_tv_sec = now_tv.tv_sec; - p_m_last_tv_msec = now_tv.tv_usec/1000; + /* set clock of last process! */ + p_m_last_tv_sec = current_time.tv_sec; + p_m_last_tv_msec = current_time.tv_usec/1000; + /* process only if we have samples and we are active */ + if (elapsed && p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE) { /* update load */ if (elapsed < p_m_load) p_m_load -= elapsed; @@ -1237,7 +1274,7 @@ int PmISDN::handler(void) /* to send data, tone must be on */ if ((p_tone_name[0] || p_m_crypt_msg_loops || p_m_inband_send_on) /* what tones? */ - && (p_m_load < ISDN_LOAD) /* enough load? */ + && (p_m_load < ISDN_LOAD) /* not too much load? */ && (p_state==PORT_STATE_CONNECT || p_m_mISDNport->tones || p_m_inband_send_on)) { /* connected or inband-tones? */ int tosend = ISDN_LOAD - p_m_load, length; unsigned char buf[MISDN_HEADER_LEN+tosend]; @@ -1267,6 +1304,8 @@ int PmISDN::handler(void) /* next loop */ p_m_crypt_msg_current = 0; p_m_crypt_msg_loops--; + if (!p_m_crypt_msg_loops) + update_rxoff(); // puts("eine loop weniger"); } @@ -1283,32 +1322,34 @@ int PmISDN::handler(void) if (ISDN_LOAD - p_m_load - tosend > 0) { frm->prim = PH_DATA_REQ; frm->id = 0; - ret = sendto(p_m_mISDNport->b_socket[p_m_b_index], buf, MISDN_HEADER_LEN+ISDN_LOAD-p_m_load-tosend, 0, NULL, 0); + ret = sendto(p_m_mISDNport->b_sock[p_m_b_index].fd, buf, MISDN_HEADER_LEN+ISDN_LOAD-p_m_load-tosend, 0, NULL, 0); if (ret <= 0) - PERROR("Failed to send to socket %d (samples = %d)\n", p_m_mISDNport->b_socket[p_m_b_index], ISDN_LOAD-p_m_load-tosend); + PERROR("Failed to send to socket %d (samples = %d)\n", p_m_mISDNport->b_sock[p_m_b_index].fd, ISDN_LOAD-p_m_load-tosend); p_m_load += ISDN_LOAD - p_m_load - tosend; } } } - // NOTE: deletion is done by the child class - - /* handle timeouts */ - if (p_m_timeout) { - if (p_m_timer+p_m_timeout < now_d) { - PDEBUG(DEBUG_ISDN, "(%s) timeout after %d seconds detected (state=%d).\n", p_name, p_m_timeout, p_state); - p_m_timeout = 0; - /* send timeout to endpoint */ - message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_TIMEOUT); - message->param.state = p_state; - message_put(message); - return(1); - } + if (p_tone_name[0] || p_m_crypt_msg_loops || p_m_inband_send_on || p_m_load) { + schedule_timer(&p_m_loadtimer, 0, ISDN_TRANSMIT*125); } - - return(0); /* nothing done */ } +/* handle timeouts */ +static int mISDN_timeout(struct lcr_timer *timer, void *instance, int i) +{ + class PmISDN *isdnport = (class PmISDN *)instance; + struct lcr_msg *message; + + PDEBUG(DEBUG_ISDN, "(%s) timeout after %d seconds detected (state=%d).\n", isdnport->p_name, isdnport->p_m_timeout.timeout.tv_sec, isdnport->p_state); + /* send timeout to endpoint */ + message = message_create(isdnport->p_serial, ACTIVE_EPOINT(isdnport->p_epointlist), PORT_TO_EPOINT, MESSAGE_TIMEOUT); + message->param.state = isdnport->p_state; + message_put(message); + + return 0; +} + /* * whenever we get audio data from bchannel, we process it here @@ -1466,7 +1507,7 @@ void PmISDN::set_echotest(int echo) PDEBUG(DEBUG_ISDN, "we set echo to echo=%d.\n", p_m_echo); if (p_m_b_channel) if (p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE) - ph_control(p_m_mISDNport, this, p_m_mISDNport->b_socket[p_m_b_index], p_m_echo?DSP_ECHO_ON:DSP_ECHO_OFF, 0, "DSP-ECHO", p_m_echo); + ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, p_m_echo?DSP_ECHO_ON:DSP_ECHO_OFF, 0, "DSP-ECHO", p_m_echo); } } @@ -1509,7 +1550,7 @@ void PmISDN::set_tone(const char *dir, const char *tone) if (p_m_b_index > -1) if (p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE && p_m_mISDNport->b_mode[p_m_b_index] == B_MODE_TRANSPARENT) { PDEBUG(DEBUG_ISDN, "we reset tone from id=%d to OFF.\n", p_m_tone); - ph_control(p_m_mISDNport, this, p_m_mISDNport->b_socket[p_m_b_index], DSP_TONE_PATT_OFF, 0, "DSP-TONE", 0); + ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_TONE_PATT_OFF, 0, "DSP-TONE", 0); } p_m_tone = 0; Port::set_tone(dir, tone); @@ -1581,7 +1622,7 @@ void PmISDN::set_tone(const char *dir, const char *tone) PDEBUG(DEBUG_ISDN, "we set tone to id=%d.\n", p_m_tone); if (p_m_b_index > -1) if (p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE && p_m_mISDNport->b_mode[p_m_b_index] == B_MODE_TRANSPARENT) - ph_control(p_m_mISDNport, this, p_m_mISDNport->b_socket[p_m_b_index], p_m_tone?DSP_TONE_PATT_ON:DSP_TONE_PATT_OFF, p_m_tone, "DSP-TONE", p_m_tone); + ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, p_m_tone?DSP_TONE_PATT_ON:DSP_TONE_PATT_OFF, p_m_tone, "DSP-TONE", p_m_tone); } /* turn user-space tones off in cases of no tone OR dsp tone */ Port::set_tone("",NULL); @@ -1600,7 +1641,7 @@ void PmISDN::message_mISDNsignal(unsigned int epoint_id, int message_id, union p PDEBUG(DEBUG_BCHANNEL, "we change tx-volume to shift=%d.\n", p_m_tx_gain); if (p_m_b_index > -1) if (p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE && p_m_mISDNport->b_mode[p_m_b_index] == B_MODE_TRANSPARENT) - ph_control(p_m_mISDNport, this, p_m_mISDNport->b_socket[p_m_b_index], DSP_VOL_CHANGE_TX, p_m_tx_gain, "DSP-TX_GAIN", p_m_tx_gain); + ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_VOL_CHANGE_TX, p_m_tx_gain, "DSP-TX_GAIN", p_m_tx_gain); } else PDEBUG(DEBUG_BCHANNEL, "we already have tx-volume shift=%d.\n", p_m_rx_gain); if (p_m_rx_gain != param->mISDNsignal.rx_gain) { @@ -1608,7 +1649,7 @@ void PmISDN::message_mISDNsignal(unsigned int epoint_id, int message_id, union p PDEBUG(DEBUG_BCHANNEL, "we change rx-volume to shift=%d.\n", p_m_rx_gain); if (p_m_b_index > -1) if (p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE && p_m_mISDNport->b_mode[p_m_b_index] == B_MODE_TRANSPARENT) - ph_control(p_m_mISDNport, this, p_m_mISDNport->b_socket[p_m_b_index], DSP_VOL_CHANGE_RX, p_m_rx_gain, "DSP-RX_GAIN", p_m_rx_gain); + ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_VOL_CHANGE_RX, p_m_rx_gain, "DSP-RX_GAIN", p_m_rx_gain); } else PDEBUG(DEBUG_BCHANNEL, "we already have rx-volume shift=%d.\n", p_m_rx_gain); break; @@ -1624,6 +1665,7 @@ void PmISDN::message_mISDNsignal(unsigned int epoint_id, int message_id, union p if (p_m_joindata != param->mISDNsignal.joindata) { p_m_joindata = param->mISDNsignal.joindata; PDEBUG(DEBUG_BCHANNEL, "we change to joindata=%d.\n", p_m_joindata); + update_rxoff(); } else PDEBUG(DEBUG_BCHANNEL, "we already have joindata=%d.\n", p_m_joindata); break; @@ -1634,7 +1676,7 @@ void PmISDN::message_mISDNsignal(unsigned int epoint_id, int message_id, union p PDEBUG(DEBUG_BCHANNEL, "we change delay mode to delay=%d.\n", p_m_delay); if (p_m_b_index > -1) if (p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE && p_m_mISDNport->b_mode[p_m_b_index] == B_MODE_TRANSPARENT) - ph_control(p_m_mISDNport, this, p_m_mISDNport->b_socket[p_m_b_index], p_m_delay?DSP_DELAY:DSP_JITTER, p_m_delay, "DSP-DELAY", p_m_delay); + ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, p_m_delay?DSP_DELAY:DSP_JITTER, p_m_delay, "DSP-DELAY", p_m_delay); } else PDEBUG(DEBUG_BCHANNEL, "we already have delay=%d.\n", p_m_delay); break; @@ -1665,7 +1707,7 @@ void PmISDN::message_crypt(unsigned int epoint_id, int message_id, union paramet PDEBUG(DEBUG_BCHANNEL, "we set encryption to crypt=%d. (0 means OFF)\n", p_m_crypt); if (p_m_b_index > -1) if (p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE && p_m_mISDNport->b_mode[p_m_b_index] == B_MODE_TRANSPARENT) - ph_control_block(p_m_mISDNport, this, p_m_mISDNport->b_socket[p_m_b_index], p_m_crypt?DSP_BF_ENABLE_KEY:DSP_BF_DISABLE, p_m_crypt_key, p_m_crypt_key_len, "DSP-CRYPT", p_m_crypt_key_len); + ph_control_block(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, p_m_crypt?DSP_BF_ENABLE_KEY:DSP_BF_DISABLE, p_m_crypt_key, p_m_crypt_key_len, "DSP-CRYPT", p_m_crypt_key_len); break; case CC_DACT_REQ: /* deactivate session encryption */ @@ -1675,11 +1717,13 @@ void PmISDN::message_crypt(unsigned int epoint_id, int message_id, union paramet case CR_LISTEN_REQ: /* start listening to messages */ p_m_crypt_listen = 1; + update_rxoff(); p_m_crypt_listen_state = 0; break; case CR_UNLISTEN_REQ: /* stop listening to messages */ p_m_crypt_listen = 0; + update_rxoff(); break; case CR_MESSAGE_REQ: /* send message */ @@ -1690,11 +1734,13 @@ void PmISDN::message_crypt(unsigned int epoint_id, int message_id, union paramet } p_m_crypt_msg_current = 0; /* reset */ p_m_crypt_msg_loops = 6; /* enable */ + update_rxoff(); + update_load(); #if 0 /* disable txmix, or we get corrupt data due to audio process */ if (p_m_txmix && p_m_b_index>=0 && p_m_mISDNport->b_mode[p_m_b_index] == B_MODE_TRANSPARENT) { PDEBUG(DEBUG_BCHANNEL, "for sending CR_MESSAGE_REQ, we reset txmix from txmix=%d.\n", p_m_txmix); - ph_control(p_m_mISDNport, this, p_mISDNport->b_socket[p_m_b_index], DSP_MIX_OFF, 0, "DSP-TXMIX", 0); + ph_control(p_m_mISDNport, this, p_mISDNport->b_sock[p_m_b_index].fd, DSP_MIX_OFF, 0, "DSP-TXMIX", 0); } #endif break; @@ -1732,131 +1778,68 @@ int PmISDN::message_epoint(unsigned int epoint_id, int message_id, union paramet return(0); } +void PmISDN::update_rxoff(void) +{ + /* call bridges in user space OR crypto OR recording */ + if (p_m_joindata || p_m_crypt_msg_loops || p_m_crypt_listen || p_record || p_m_inband_receive_on) { + /* rx IS required */ + if (p_m_rxoff) { + /* turn on RX */ + p_m_rxoff = 0; + PDEBUG(DEBUG_BCHANNEL, "%s: receive data is required, so we turn them on\n", __FUNCTION__); + if (p_m_b_index > -1) + if (p_m_mISDNport->b_port[p_m_b_index] && p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE) + ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_RECEIVE_ON, 0, "DSP-RXOFF", 0); + } + } else { + /* rx NOT required */ + if (!p_m_rxoff) { + /* turn off RX */ + p_m_rxoff = 1; + PDEBUG(DEBUG_BCHANNEL, "%s: receive data is not required, so we turn them off\n", __FUNCTION__); + if (p_m_b_index > -1) + if (p_m_mISDNport->b_port[p_m_b_index] && p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE) + ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_RECEIVE_OFF, 0, "DSP-RXOFF", 1); + } + } + /* recording */ + if (p_record) { + /* txdata IS required */ + if (!p_m_txdata) { + /* turn on RX */ + p_m_txdata = 1; + PDEBUG(DEBUG_BCHANNEL, "%s: transmit data is required, so we turn them on\n", __FUNCTION__); + if (p_m_b_index > -1) + if (p_m_mISDNport->b_port[p_m_b_index] && p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE) + ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_TXDATA_ON, 0, "DSP-TXDATA", 1); + } + } else { + /* txdata NOT required */ + if (p_m_txdata) { + /* turn off RX */ + p_m_txdata = 0; + PDEBUG(DEBUG_BCHANNEL, "%s: transmit data is not required, so we turn them off\n", __FUNCTION__); + if (p_m_b_index > -1) + if (p_m_mISDNport->b_port[p_m_b_index] && p_m_mISDNport->b_state[p_m_b_index] == B_STATE_ACTIVE) + ph_control(p_m_mISDNport, this, p_m_mISDNport->b_sock[p_m_b_index].fd, DSP_TXDATA_OFF, 0, "DSP-TXDATA", 0); + } + } +} -/* - * main loop for processing messages from mISDN - */ -int mISDN_handler(void) +static int mISDN_upqueue(struct lcr_fd *fd, unsigned int what, void *instance, int i) { - int ret, work = 0; struct mISDNport *mISDNport; - class PmISDN *isdnport; - int i; - unsigned char buffer[2048+MISDN_HEADER_LEN]; - struct mISDNhead *hh = (struct mISDNhead *)buffer; struct mbuffer *mb; struct l3_msg *l3m; + char byte; + + /* unset global semaphore */ + read(fd->fd, &byte, 1); + upqueue_avail = 0; /* process all ports */ mISDNport = mISDNport_first; while(mISDNport) { - /* process all bchannels */ - i = 0; - while(i < mISDNport->b_num) { - /* process timer events for bchannel handling */ - if (mISDNport->b_timer[i]) { - if (mISDNport->b_timer[i] <= now_d) - bchannel_event(mISDNport, i, B_EVENT_TIMEOUT); - } - /* handle port of bchannel */ - isdnport=mISDNport->b_port[i]; - if (isdnport) { - /* call bridges in user space OR crypto OR recording */ - if (isdnport->p_m_joindata || isdnport->p_m_crypt_msg_loops || isdnport->p_m_crypt_listen || isdnport->p_record || isdnport->p_m_inband_receive_on) { - /* rx IS required */ - if (isdnport->p_m_rxoff) { - /* turn on RX */ - isdnport->p_m_rxoff = 0; - PDEBUG(DEBUG_BCHANNEL, "%s: receive data is required, so we turn them on\n", __FUNCTION__); - if (mISDNport->b_port[i] && mISDNport->b_state[i] == B_STATE_ACTIVE) - ph_control(mISDNport, isdnport, mISDNport->b_socket[i], DSP_RECEIVE_ON, 0, "DSP-RXOFF", 0); - return(1); - } - } else { - /* rx NOT required */ - if (!isdnport->p_m_rxoff) { - /* turn off RX */ - isdnport->p_m_rxoff = 1; - PDEBUG(DEBUG_BCHANNEL, "%s: receive data is not required, so we turn them off\n", __FUNCTION__); - if (mISDNport->b_port[i] && mISDNport->b_state[i] == B_STATE_ACTIVE) - ph_control(mISDNport, isdnport, mISDNport->b_socket[i], DSP_RECEIVE_OFF, 0, "DSP-RXOFF", 1); - return(1); - } - } - /* recording */ - if (isdnport->p_record) { - /* txdata IS required */ - if (!isdnport->p_m_txdata) { - /* turn on RX */ - isdnport->p_m_txdata = 1; - PDEBUG(DEBUG_BCHANNEL, "%s: transmit data is required, so we turn them on\n", __FUNCTION__); - if (mISDNport->b_port[i] && mISDNport->b_state[i] == B_STATE_ACTIVE) - ph_control(mISDNport, isdnport, mISDNport->b_socket[i], DSP_TXDATA_ON, 0, "DSP-TXDATA", 1); - return(1); - } - } else { - /* txdata NOT required */ - if (isdnport->p_m_txdata) { - /* turn off RX */ - isdnport->p_m_txdata = 0; - PDEBUG(DEBUG_BCHANNEL, "%s: transmit data is not required, so we turn them off\n", __FUNCTION__); - if (mISDNport->b_port[i] && mISDNport->b_state[i] == B_STATE_ACTIVE) - ph_control(mISDNport, isdnport, mISDNport->b_socket[i], DSP_TXDATA_OFF, 0, "DSP-TXDATA", 0); - return(1); - } - } - } - - /* handle message from bchannel */ - if (mISDNport->b_socket[i] > -1) { - ret = recv(mISDNport->b_socket[i], buffer, sizeof(buffer), 0); - if (ret >= (int)MISDN_HEADER_LEN) { - work = 1; - switch(hh->prim) { - /* we don't care about confirms, we use rx data to sync tx */ - case PH_DATA_CNF: - break; - - /* we receive audio data, we respond to it AND we send tones */ - case PH_DATA_IND: - case DL_DATA_IND: - case PH_DATA_REQ: - case DL_DATA_REQ: - case PH_CONTROL_IND: - if (mISDNport->b_port[i]) - mISDNport->b_port[i]->bchannel_receive(hh, buffer+MISDN_HEADER_LEN, ret-MISDN_HEADER_LEN); - else - PDEBUG(DEBUG_BCHANNEL, "b-channel is not associated to an ISDNPort (socket %d), ignoring.\n", mISDNport->b_socket[i]); - break; - - case PH_ACTIVATE_IND: - case DL_ESTABLISH_IND: - case PH_ACTIVATE_CNF: - case DL_ESTABLISH_CNF: - PDEBUG(DEBUG_BCHANNEL, "DL_ESTABLISH confirm: bchannel is now activated (socket %d).\n", mISDNport->b_socket[i]); - bchannel_event(mISDNport, i, B_EVENT_ACTIVATED); - break; - - case PH_DEACTIVATE_IND: - case DL_RELEASE_IND: - case PH_DEACTIVATE_CNF: - case DL_RELEASE_CNF: - PDEBUG(DEBUG_BCHANNEL, "DL_RELEASE confirm: bchannel is now de-activated (socket %d).\n", mISDNport->b_socket[i]); - bchannel_event(mISDNport, i, B_EVENT_DEACTIVATED); - break; - - default: - PERROR("child message not handled: prim(0x%x) socket(%d) msg->len(%d)\n", hh->prim, mISDNport->b_socket[i], ret-MISDN_HEADER_LEN); - } - } else { - if (ret < 0 && errno != EWOULDBLOCK) - PERROR("Read from port %d, index %d failed with return code %d\n", mISDNport->portnum, i, ret); - } - } - - i++; - } - /* handle queued up-messages (d-channel) */ if (!mISDNport->gsm) { while ((mb = mdequeue(&mISDNport->upqueue))) { @@ -1916,8 +1899,8 @@ int mISDN_handler(void) if (l3m->pid < 128) mISDNport->l2mask[l3m->pid >> 3] |= (1 << (l3m->pid & 7)); if ((!mISDNport->ntmode || mISDNport->ptp) && l3m->pid < 127) { - if (mISDNport->l2establish) { - mISDNport->l2establish = 0; + if (mISDNport->l2establish.active) { + unsched_timer(&mISDNport->l2establish); PDEBUG(DEBUG_ISDN, "the link became active before l2establish timer expiry.\n"); } } @@ -1926,7 +1909,7 @@ int mISDN_handler(void) case MT_L2RELEASE: if (l3m->pid < 128) mISDNport->l2mask[l3m->pid >> 3] &= ~(1 << (l3m->pid & 7)); - if (!mISDNport->l2establish) { + if (!mISDNport->l2establish.active) { l1l2l3_trace_header(mISDNport, NULL, L2_RELEASE_IND, DIRECTION_IN); add_trace("tei", NULL, "%d", l3m->pid); end_trace(); @@ -1935,9 +1918,9 @@ int mISDN_handler(void) mISDNport->l2link = 0; } if (!mISDNport->gsm && (!mISDNport->ntmode || mISDNport->ptp) && l3m->pid < 127) { - if (!mISDNport->l2establish && mISDNport->l2hold) { + if (!mISDNport->l2establish.active && mISDNport->l2hold) { PDEBUG(DEBUG_ISDN, "set timer and establish.\n"); - time(&mISDNport->l2establish); + schedule_timer(&mISDNport->l2establish, 5, 0); mISDNport->ml3->to_layer3(mISDNport->ml3, MT_L2ESTABLISH, 0, NULL); } } @@ -1951,36 +1934,94 @@ int mISDN_handler(void) free_l3_msg(l3m); } } + mISDNport = mISDNport->next; + } + return 0; +} -#if 0 - if (mISDNport->l1timeout && now>mISDNport->l1timeout) - { ---} - PDEBUG(DEBUG_ISDN, "the L1 establish timer expired, we release all pending messages.\n", mISDNport->portnum); - mISDNport->l1timeout = 0; -#endif +/* l2 establish timer fires */ +static int l2establish_timeout(struct lcr_timer *timer, void *instance, int i) +{ + struct mISDNport *mISDNport = (struct mISDNport *)instance; - /* layer 2 establish timer */ - if (mISDNport->l2establish) { - if (now-mISDNport->l2establish > 5) { - mISDNport->l2establish = 0; - if (!mISDNport->gsm && mISDNport->l2hold && (mISDNport->ptp || !mISDNport->ntmode)) { + if (!mISDNport->gsm && 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 */ + } -// 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); - time(&mISDNport->l2establish); - return(1); - } - } - } + return 0; +} + +/* handle frames from bchannel */ +static int b_sock_callback(struct lcr_fd *fd, unsigned int what, void *instance, int i) +{ + struct mISDNport *mISDNport = (struct mISDNport *)instance; + unsigned char buffer[2048+MISDN_HEADER_LEN]; + struct mISDNhead *hh = (struct mISDNhead *)buffer; + int ret; + ret = recv(fd->fd, buffer, sizeof(buffer), 0); + if (ret < 0) { + PERROR("read error frame, errno %d\n", errno); + return 0; + } + if (ret < (int)MISDN_HEADER_LEN) { + PERROR("read short frame, got %d, expected %d\n", ret, (int)MISDN_HEADER_LEN); + return 0; + } + switch(hh->prim) { + /* we don't care about confirms, we use rx data to sync tx */ + case PH_DATA_CNF: + break; - mISDNport = mISDNport->next; + /* we receive audio data, we respond to it AND we send tones */ + case PH_DATA_IND: + case DL_DATA_IND: + case PH_DATA_REQ: + case DL_DATA_REQ: + case PH_CONTROL_IND: + if (mISDNport->b_port[i]) + mISDNport->b_port[i]->bchannel_receive(hh, buffer+MISDN_HEADER_LEN, ret-MISDN_HEADER_LEN); + else + PDEBUG(DEBUG_BCHANNEL, "b-channel is not associated to an ISDNPort (socket %d), ignoring.\n", fd->fd); + break; + + case PH_ACTIVATE_IND: + case DL_ESTABLISH_IND: + case PH_ACTIVATE_CNF: + case DL_ESTABLISH_CNF: + PDEBUG(DEBUG_BCHANNEL, "DL_ESTABLISH confirm: bchannel is now activated (socket %d).\n", fd->fd); + bchannel_event(mISDNport, i, B_EVENT_ACTIVATED); + break; + + case PH_DEACTIVATE_IND: + case DL_RELEASE_IND: + case PH_DEACTIVATE_CNF: + case DL_RELEASE_CNF: + PDEBUG(DEBUG_BCHANNEL, "DL_RELEASE confirm: bchannel is now de-activated (socket %d).\n", fd->fd); + bchannel_event(mISDNport, i, B_EVENT_DEACTIVATED); + break; + + default: + PERROR("child message not handled: prim(0x%x) socket(%d) msg->len(%d)\n", hh->prim, fd->fd, ret-MISDN_HEADER_LEN); } - /* if we received at least one b-frame, we will return 1 */ - return(work); + return 0; +} + +/* process timer events for bchannel handling */ +static int b_timer_timeout(struct lcr_timer *timer, void *instance, int i) +{ + struct mISDNport *mISDNport = (struct mISDNport *)instance; +puts("fires"); + + bchannel_event(mISDNport, i, B_EVENT_TIMEOUT); + + return 0; } + int do_layer3(struct mlayer3 *ml3, unsigned int cmd, unsigned int pid, struct l3_msg *l3m) { /* IMPORTAINT: @@ -2018,6 +2059,11 @@ int do_layer3(struct mlayer3 *ml3, unsigned int cmd, unsigned int pid, struct l3 l3m->type = cmd; l3m->pid = pid; mqueue_tail(&mISDNport->upqueue, mb); + if (!upqueue_avail) { + upqueue_avail = 1; + char byte = 0; + write(upqueue_pipe[1], &byte, 1); + } return 0; } @@ -2193,6 +2239,7 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport) while(*mISDNportp) 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 */ mISDNport->l1link = 1; @@ -2309,7 +2356,7 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport) i = 0; while(i < mISDNport->b_num) { mISDNport->b_state[i] = B_STATE_IDLE; - mISDNport->b_socket[i] = -1; + add_timer(&mISDNport->b_timer[i], b_timer_timeout, mISDNport, i); i++; } @@ -2319,7 +2366,7 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport) l1l2l3_trace_header(mISDNport, NULL, L2_ESTABLISH_REQ, DIRECTION_OUT); add_trace("tei", NULL, "%d", 0); end_trace(); - time(&mISDNport->l2establish); + schedule_timer(&mISDNport->l2establish, 5, 0); /* 5 seconds */ } /* for nt-mode ptmp the link is always up */ @@ -2415,12 +2462,16 @@ void mISDNport_close(struct mISDNport *mISDNport) /* free bchannels */ i = 0; while(i < mISDNport->b_num) { - if (mISDNport->b_socket[i] > -1) { + if (mISDNport->b_sock[i].inuse) { _bchannel_destroy(mISDNport, i); PDEBUG(DEBUG_BCHANNEL, "freeing %s port %d bchannel (index %d).\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, i); } + if (mISDNport->b_timer[i].inuse) { + del_timer(&mISDNport->b_timer[i]); + } i++; } + del_timer(&mISDNport->l2establish); /* close layer 3, if open */ if (!mISDNport->gsm && mISDNport->ml3) { @@ -2480,14 +2531,15 @@ void PmISDN::txfromup(unsigned char *data, int length) * if transmit buffer in DSP module is empty, * preload it to DSP_LOAD to prevent jitter gaps. */ - if (p_m_load==0 && ISDN_LOAD>0) { + if (p_m_load == 0 && ISDN_LOAD > 0) { hh->prim = PH_DATA_REQ; hh->id = 0; memset(buf+MISDN_HEADER_LEN, (options.law=='a')?0x2a:0xff, ISDN_LOAD); - ret = sendto(p_m_mISDNport->b_socket[p_m_b_index], buf, MISDN_HEADER_LEN+ISDN_LOAD, 0, NULL, 0); + ret = sendto(p_m_mISDNport->b_sock[p_m_b_index].fd, buf, MISDN_HEADER_LEN+ISDN_LOAD, 0, NULL, 0); if (ret <= 0) - PERROR("Failed to send to socket %d\n", p_m_mISDNport->b_socket[p_m_b_index]); + PERROR("Failed to send to socket %d\n", p_m_mISDNport->b_sock[p_m_b_index].fd); p_m_load += ISDN_LOAD; + schedule_timer(&p_m_loadtimer, 0, ISDN_TRANSMIT*125); } /* drop if load would exceed ISDN_MAXLOAD @@ -2500,9 +2552,9 @@ void PmISDN::txfromup(unsigned char *data, int length) hh->prim = PH_DATA_REQ; hh->id = 0; memcpy(buf+MISDN_HEADER_LEN, data, length); - ret = sendto(p_m_mISDNport->b_socket[p_m_b_index], buf, MISDN_HEADER_LEN+length, 0, NULL, 0); + ret = sendto(p_m_mISDNport->b_sock[p_m_b_index].fd, buf, MISDN_HEADER_LEN+length, 0, NULL, 0); if (ret <= 0) - PERROR("Failed to send to socket %d\n", p_m_mISDNport->b_socket[p_m_b_index]); + PERROR("Failed to send to socket %d\n", p_m_mISDNport->b_sock[p_m_b_index].fd); p_m_load += length; } @@ -2516,6 +2568,8 @@ void PmISDN::inband_send_on(void) { PDEBUG(DEBUG_PORT, "turning inband signalling send on.\n"); p_m_inband_send_on = 1; + /* trigger inband transmit */ + update_load(); } void PmISDN::inband_send_off(void) @@ -2537,12 +2591,14 @@ void PmISDN::inband_receive_on(void) /* this must work during constructor, see ss5.cpp */ PDEBUG(DEBUG_PORT, "turning inband signalling receive on.\n"); p_m_inband_receive_on = 1; + update_rxoff(); } void PmISDN::inband_receive_off(void) { PDEBUG(DEBUG_PORT, "turning inband signalling receive off.\n"); p_m_inband_receive_on = 0; + update_rxoff(); } void PmISDN::mute_on(void) diff --git a/mISDN.h b/mISDN.h index cbebcac..1a7d377 100644 --- a/mISDN.h +++ b/mISDN.h @@ -44,7 +44,7 @@ struct mISDNport { unsigned char l2mask[16]; /* 128 bits for each tei */ int l1hold; /* set, if layer 1 should be holt */ int l2hold; /* set, if layer 2 must be hold/checked */ - time_t l2establish; /* time until establishing after link failure */ + struct lcr_timer l2establish; /* time until establishing after link failure */ int use; /* counts the number of port that uses this port */ int ntmode; /* is TRUE if port is NT mode */ int tespecial; /* is TRUE if port uses special TE mode */ @@ -55,10 +55,10 @@ struct mISDNport { int b_reserved; /* number of bchannels reserved or in use */ class PmISDN *b_port[128]; /* bchannel assigned to port object */ struct mqueue upqueue; - int b_socket[128]; + struct lcr_fd b_sock[128]; /* socket list elements */ int b_mode[128]; /* B_MODE_* */ int b_state[128]; /* statemachine, 0 = IDLE */ - double b_timer[128]; /* timer for state machine */ + struct lcr_timer b_timer[128]; /* timer for bchannel state machine */ int b_remote_id[128]; /* the socket currently exported (0=none) */ unsigned int b_remote_ref[128]; /* the ref currently exported */ int locally; /* local causes are sent as local causes not remote */ @@ -97,7 +97,6 @@ void mISDNport_static(struct mISDNport *mISDNport); void mISDNport_close_all(void); void mISDNport_close(struct mISDNport *mISDNport); void mISDN_port_reorder(void); -int mISDN_handler(void); void enc_ie_cause_standalone(struct l3_msg *l3m, int location, int cause); int stack2manager(struct mISDNport *mISDNport, unsigned int cmd, unsigned int pid, struct l3_msg *l3m); void ph_control(struct mISDNport *mISDNport, class PmISDN *isdnport, unsigned int handle, unsigned int c1, unsigned int c2, const char *trace_name, int trace_value); @@ -115,7 +114,6 @@ class PmISDN : public Port PmISDN(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode); ~PmISDN(); void bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len); - int handler(void); void transmit(unsigned char *buffer, int length); int message_epoint(unsigned int epoint_id, int message, union parameter *param); void message_mISDNsignal(unsigned int epoint_id, int message_id, union parameter *param); @@ -133,8 +131,11 @@ class PmISDN : public Port int p_m_dtmf; /* dtmf decoding is enabled */ int p_m_joindata; /* the call requires data due to no briging capability */ + struct lcr_timer p_m_loadtimer; /* timer for audio transmission */ + virtual void update_load(void); + void load_tx(void); int p_m_load; /* current data in dsp tx buffer */ - unsigned int p_m_last_tv_sec; /* time stamp of last handler call, (to sync audio data */ + unsigned int p_m_last_tv_sec; /* time stamp of last tx_load call, (to sync audio data */ unsigned int p_m_last_tv_msec; // int p_m_fromup_buffer_readp; /* buffer for audio from remote endpoint */ // int p_m_fromup_buffer_writep; @@ -167,10 +168,8 @@ class PmISDN : public Port // long long p_m_jittercheck; /* time of audio data */ // long long p_m_jitterdropped; /* number of bytes dropped */ int p_m_b_mode; /* bchannel mode */ - int p_m_delete; /* true if obj. must del. */ int p_m_hold; /* if port is on hold */ - unsigned int p_m_timeout; /* timeout of timers */ - time_t p_m_timer; /* start of timer */ + struct lcr_timer p_m_timeout; /* timeout of timers */ unsigned int p_m_remote_ref; /* join to export bchannel to */ int p_m_remote_id; /* sock to export bchannel to */ @@ -185,6 +184,7 @@ class PmISDN : public Port void inband_receive_off(void); void mute_on(void); void mute_off(void); + void update_rxoff(void); int seize_bchannel(int channel, int exclusive); /* requests / reserves / links bchannels, but does not open it! */ void drop_bchannel(void); diff --git a/macro.h b/macro.h index 9e4bcd1..d97c41f 100644 --- a/macro.h +++ b/macro.h @@ -77,8 +77,8 @@ static inline void fatal(const char *function, int line, const char *fmt, ...) fprintf(stderr, "FATAL ERROR in function %s, line %d: %s", function, line, buffer); fprintf(stderr, "This error is not recoverable, must exit here.\n"); #ifdef DEBUG_FUNC - debug(function, line, "FATAL ERROR", buffer); - debug(function, line, "FATAL ERROR", (char *)"This error is not recoverable, must exit here.\n"); + debug(function, line, "FATAL", buffer); + debug(function, line, "FATAL", (char *)"This error is not recoverable, must exit here.\n"); #endif exit(EXIT_FAILURE); } diff --git a/main.c b/main.c index da21c23..b70ed05 100644 --- a/main.c +++ b/main.c @@ -11,19 +11,14 @@ #include "main.h" -MESSAGES +//MESSAGES -double now_d, last_d; -time_t now; -struct tm *now_tm; struct timeval now_tv; struct timezone now_tz; #define GET_NOW() \ { \ gettimeofday(&now_tv, &now_tz); \ now_d = ((double)(now_tv.tv_usec))/1000000 + now_tv.tv_sec; \ - now = now_tv.tv_sec; \ - now_tm = localtime(&now); \ } FILE *debug_fp = NULL; @@ -49,22 +44,21 @@ int classuse = 0; int fduse = 0; int fhuse = 0; -const char *debug_prefix = NULL; int debug_count = 0; int last_debug = 0; int debug_newline = 1; int nooutput = 0; -void debug_usleep(int msec, const char *file, int line, int hour, int min, int sec) -{ - usleep(msec); -} - void debug(const char *function, int line, const char *prefix, char *buffer) { + time_t now; + struct tm *now_tm; + /* if we have a new debug count, we add a mark */ if (last_debug != debug_count) { last_debug = debug_count; + time(&now); + now_tm = localtime(&now); if (!nooutput) printf("\033[34m--------------------- %04d.%02d.%02d %02d:%02d:%02d %06d\033[36m\n", now_tm->tm_year+1900, now_tm->tm_mon+1, now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec, debug_count%1000000); if (debug_fp) @@ -110,7 +104,7 @@ void _printdebug(const char *function, int line, unsigned int mask, const char * buffer[sizeof(buffer)-1]=0; va_end(args); - debug(function, line, debug_prefix, buffer); + debug(function, line, "DEBUG", buffer); pthread_mutex_unlock(&mutexd); } @@ -167,21 +161,18 @@ void sighandler(int sigset) */ int main(int argc, char *argv[]) { +#ifdef WITH_GSM + double now_d, last_d; + int all_idle; +#endif int ret = -1; int lockfd = -1; /* file lock */ struct lcr_msg *message; - class Port *port; - class Endpoint *epoint; - class Join *join; int i; - int all_idle; - char prefix_string[64]; struct sched_param schedp; - const char *debug_prefix = "alloc"; int created_mutexd = 0,/* created_mutext = 0,*/ created_mutexe = 0, created_lock = 0, created_signal = 0, created_debug = 0, created_misdn = 0; - int idletime = 0, idlecheck = 0; char tracetext[256], lock[128]; #if 0 @@ -192,9 +183,6 @@ int main(int argc, char *argv[]) /* lock LCR process */ // pthread_mutex_lock(&mutex_lcr); - /* current time */ - GET_NOW(); - /* show version */ printf("\n** %s Version %s\n\n", NAME, VERSION_STRING); @@ -437,15 +425,20 @@ int main(int argc, char *argv[]) signal(SIGPIPE,sighandler); created_signal = 1; + /* init message */ + init_message(); + /*** main loop ***/ SPRINT(tracetext, "%s %s started, waiting for calls...", NAME, VERSION_STRING); start_trace(-1, NULL, NULL, NULL, 0, 0, 0, tracetext); printf("%s\n", tracetext); end_trace(); - GET_NOW(); quit = 0; +#ifdef WITH_GSM + GET_NOW(); +#endif while(!quit) { - +#ifdef WITH_GSM last_d = now_d; GET_NOW(); if (now_d-last_d > 1.0) { @@ -454,165 +447,19 @@ int main(int argc, char *argv[]) /* all loops must be counted from the beginning since nodes might get freed during handler */ all_idle = 1; -//#warning debugging usleep crash -// debug_usleep(1, __FILE__, __LINE__, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec); - - /* handle mISDN messages from kernel */ - debug_prefix = "ISDN"; - if (mISDN_handler()) - all_idle = 0; -//#warning debugging usleep crash -// debug_usleep(1, __FILE__, __LINE__, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec); - -BUDETECT - - /* loop through all port ports and call their handler */ - port_again: - port = port_first; - while(port) { - debug_prefix = port->p_name; - debug_count++; - ret = port->handler(); - if (ret) - all_idle = 0; - if (ret < 0) /* port has been destroyed */ - goto port_again; - port = port->next; - } - - /* loop through all epoint and call their handler */ - epoint_again: - epoint = epoint_first; - while(epoint) { - debug_prefix = prefix_string; - SPRINT(prefix_string, "ep%ld", epoint->ep_serial); - debug_count++; - ret = epoint->handler(); - if (ret) - all_idle = 0; - if (ret < 0) /* epoint has been destroyed */ - goto epoint_again; - epoint = epoint->next; - } - - /* loop through all joins and call their handler */ - join_again: - join = join_first; - while(join) { - debug_prefix = "join"; - debug_count++; - ret = join->handler(); - if (ret) - all_idle = 0; - if (ret < 0) /* join has been destroyed */ - goto join_again; - join = join->next; - } - - debug_prefix = 0; - - /* process any message */ - debug_count++; - debug_prefix = "message"; - while ((message = message_get())) { + /* must be processed after all queues, so they are empty */ + if (select_main(1, NULL, NULL, NULL)) all_idle = 0; - switch(message->flow) { - case PORT_TO_EPOINT: - debug_prefix = "msg port->epoint"; - epoint = find_epoint_id(message->id_to); - if (epoint) { - if (epoint->ep_app) { - epoint->ep_app->ea_message_port(message->id_from, message->type, &message->param); - } else { - PDEBUG(DEBUG_MSG, "Warning: message %s from port %d to endpoint %d. endpoint doesn't have an application.\n", messages_txt[message->type], message->id_from, message->id_to); - } - } else { - PDEBUG(DEBUG_MSG, "Warning: message %s from port %d to endpoint %d. endpoint doesn't exist anymore.\n", messages_txt[message->type], message->id_from, message->id_to); - } - break; - - case EPOINT_TO_JOIN: - debug_prefix = "msg epoint->join"; - join = find_join_id(message->id_to); - if (join) { - join->message_epoint(message->id_from, message->type, &message->param); - } else { - PDEBUG(DEBUG_MSG, "Warning: message %s from endpoint %d to join %d. join doesn't exist anymore\n", messages_txt[message->type], message->id_from, message->id_to); - } - break; - - case JOIN_TO_EPOINT: - debug_prefix = "msg join->epoint"; - epoint = find_epoint_id(message->id_to); - if (epoint) { - if (epoint->ep_app) { - epoint->ep_app->ea_message_join(message->id_from, message->type, &message->param); - } else { - PDEBUG(DEBUG_MSG, "Warning: message %s from join %d to endpoint %d. endpoint doesn't have an application.\n", messages_txt[message->type], message->id_from, message->id_to); - } - } else { - PDEBUG(DEBUG_MSG, "Warning: message %s from join %d to endpoint %d. endpoint doesn't exist anymore.\n", messages_txt[message->type], message->id_from, message->id_to); - } - break; - - case EPOINT_TO_PORT: - debug_prefix = "msg epoint->port"; - port = find_port_id(message->id_to); - if (port) { - port->message_epoint(message->id_from, message->type, &message->param); -BUDETECT - } else { - PDEBUG(DEBUG_MSG, "Warning: message %s from endpoint %d to port %d. port doesn't exist anymore\n", messages_txt[message->type], message->id_from, message->id_to); - } - break; - - default: - PERROR("Message flow %d unknown.\n", message->flow); - } - message_free(message); - debug_count++; - debug_prefix = "message"; - } -BUDETECT - - /* handle socket */ - if (admin_handle()) - all_idle = 0; -BUDETECT - -#ifdef WITH_GSM /* handle gsm */ if (options.gsm) while(handle_gsm()) all_idle = 0; -#endif - -BUDETECT - -#if 0 - /* check for child to exit (eliminate zombies) */ - if (waitpid(-1, NULL, WNOHANG) > 0) { - PDEBUG(DEBUG_EPOINT, "a child process (created by endpoint) has exitted.\n"); - all_idle = 0; - } -#endif -//#warning debugging usleep crash -// debug_usleep(1, __FILE__, __LINE__, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec); - - /* do idle checking */ - if (idlecheck != now) { - PDEBUG(DEBUG_IDLETIME, "Idle time : %d%%\n", idletime/10000); - idletime = 0; - idlecheck = now; - } - - /* did we do nothing? so we wait to give time to other processes */ if (all_idle) { -// pthread_mutex_unlock(&mutex_lcr); // unlock LCR - debug_usleep(4000, __FILE__, __LINE__, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec); -// pthread_mutex_lock(&mutex_lcr); // lock LCR - idletime += 4000; + usleep(10000); } +#else + select_main(0, NULL, NULL, NULL); +#endif } SPRINT(tracetext, "%s terminated", NAME); printf("%s\n", tracetext); @@ -622,10 +469,12 @@ BUDETECT end_trace(); ret=0; + /* clean messacleane */ + cleanup_message(); + /* free all */ free: - /* set scheduler & priority */ if (options.schedule > 1) { @@ -642,7 +491,6 @@ free: } /* destroy objects */ - debug_prefix = "free"; while(port_first) { debug_count++; @@ -748,55 +596,4 @@ free: } -#ifdef BUDETECT_DEF -/* special debug function to detect buffer overflow - */ -int budetect_stop = 0; -void budetect(const char *file, int line, const char *function) -{ - if (budetect_stop) - return; - /* modify this function to detect race-bugs */ -#warning DID YOU MODIFY THIS FUNCTION TO DETECT THE BUFFER OVERFLOW BUG? - class Port *port; - class PmISDN *pmisdn; - struct mISDNport *mISDNport = mISDNport_first; - int i, ii; - - while(mISDNport) { - i = 0; - ii = mISDNport->b_num; - while(i < ii) { - if (mISDNport->b_port[i]) { - port = port_first; - while(port) { - if ((port->p_type&PORT_CLASS_MASK) == PORT_CLASS_ISDN) { - pmisdn = (class PmISDN *)port; - if (pmisdn->p_isdn_crypt_listen) { - PERROR_RUNTIME("************************************************\n"); - PERROR_RUNTIME("** BUG detected in %s, line %d, function %s\n", file, line, function); - PERROR_RUNTIME("** p_isdn_crypt_listen = %d\n", pmisdn->p_isdn_crypt_listen); - PERROR_RUNTIME("************************************************\n"); - budetect_stop = 1; - } - } - if (port == mISDNport->b_port[i]) - break; - port = port->next; - if (!port) { - PERROR_RUNTIME("************************************************\n"); - PERROR_RUNTIME("** BUG detected in %s, line %d, function %s\n", file, line, function); - PERROR_RUNTIME("** b_port not in list.\n"); - PERROR_RUNTIME("************************************************\n"); - budetect_stop = 1; - } - } - } - i++; - } - mISDNport = mISDNport->next; - } - -} -#endif diff --git a/main.h b/main.h index 0dc82d4..9b750e1 100644 --- a/main.h +++ b/main.h @@ -136,6 +136,7 @@ extern "C" { } #endif #include "macro.h" +#include "select.h" #include "options.h" #include "interface.h" #include "extension.h" @@ -168,11 +169,6 @@ extern "C" { #include "trace.h" extern int quit; -extern double now_d; -extern time_t now; -extern struct tm *now_tm; -extern struct timeval now_tv; -extern struct timezone now_tz; #define DIRECTION_NONE 0 #define DIRECTION_OUT 1 diff --git a/message.c b/message.c index 403253d..e2f01ec 100644 --- a/message.c +++ b/message.c @@ -15,6 +15,20 @@ MESSAGES struct lcr_msg *message_first = NULL; struct lcr_msg **messagepointer_end = &message_first; +struct lcr_work message_work; + +static int work_message(struct lcr_work *work, void *instance, int index); + +void init_message(void) +{ + memset(&message_work, 0, sizeof(message_work)); + add_work(&message_work, work_message, NULL, 0); +} + +void cleanup_message(void) +{ + del_work(&message_work); +} /* creates a new message with the given attributes. the message must be filled then. after filling, the message_put must be called */ struct lcr_msg *message_create(int id_from, int id_to, int flow, int type) @@ -50,6 +64,9 @@ void message_put(struct lcr_msg *message) messagepointer_end = &(message->next); /* Nullify next pointer if recycled messages */ *messagepointer_end=NULL; + + /* trigger work */ + trigger_work(&message_work); } struct lcr_msg *message_forward(int id_from, int id_to, int flow, union parameter *param) @@ -102,3 +119,66 @@ void message_free(struct lcr_msg *message) } +static int work_message(struct lcr_work *work, void *instance, int index) +{ + struct lcr_msg *message; + class Port *port; + class Endpoint *epoint; + class Join *join; + + while ((message = message_get())) { + switch(message->flow) { + case PORT_TO_EPOINT: + epoint = find_epoint_id(message->id_to); + if (epoint) { + if (epoint->ep_app) { + epoint->ep_app->ea_message_port(message->id_from, message->type, &message->param); + } else { + PDEBUG(DEBUG_MSG, "Warning: message %s from port %d to endpoint %d. endpoint doesn't have an application.\n", messages_txt[message->type], message->id_from, message->id_to); + } + } else { + PDEBUG(DEBUG_MSG, "Warning: message %s from port %d to endpoint %d. endpoint doesn't exist anymore.\n", messages_txt[message->type], message->id_from, message->id_to); + } + break; + + case EPOINT_TO_JOIN: + join = find_join_id(message->id_to); + if (join) { + join->message_epoint(message->id_from, message->type, &message->param); + } else { + PDEBUG(DEBUG_MSG, "Warning: message %s from endpoint %d to join %d. join doesn't exist anymore\n", messages_txt[message->type], message->id_from, message->id_to); + } + break; + + case JOIN_TO_EPOINT: + epoint = find_epoint_id(message->id_to); + if (epoint) { + if (epoint->ep_app) { + epoint->ep_app->ea_message_join(message->id_from, message->type, &message->param); + } else { + PDEBUG(DEBUG_MSG, "Warning: message %s from join %d to endpoint %d. endpoint doesn't have an application.\n", messages_txt[message->type], message->id_from, message->id_to); + } + } else { + PDEBUG(DEBUG_MSG, "Warning: message %s from join %d to endpoint %d. endpoint doesn't exist anymore.\n", messages_txt[message->type], message->id_from, message->id_to); + } + break; + + case EPOINT_TO_PORT: + port = find_port_id(message->id_to); + if (port) { + port->message_epoint(message->id_from, message->type, &message->param); +BUDETECT + } else { + PDEBUG(DEBUG_MSG, "Warning: message %s from endpoint %d to port %d. port doesn't exist anymore\n", messages_txt[message->type], message->id_from, message->id_to); + } + break; + + default: + PERROR("Message flow %d unknown.\n", message->flow); + } + message_free(message); + } + + return 0; +} + diff --git a/message.h b/message.h index 63abc65..a7f15b7 100644 --- a/message.h +++ b/message.h @@ -9,8 +9,6 @@ ** ** \*****************************************************************************/ -#define ISDN_TRANSMIT 256 // samples - enum { /* interface types */ INFO_ITYPE_ISDN, /* call from external */ INFO_ITYPE_ISDN_EXTENSION, /* call from internal extension */ @@ -271,10 +269,11 @@ struct park_info { int len; }; +#define ISDN_TRANSMIT 256 /* DATA */ struct param_data { - unsigned char data[ISDN_TRANSMIT]; /* audio/hdlc data */ - int len; /* audio/hdlc data */ + unsigned char data[ISDN_TRANSMIT]; /* audio data */ + int len; /* audio data */ }; struct param_play { @@ -444,6 +443,7 @@ void message_put(struct lcr_msg *message); struct lcr_msg *message_forward(int id_from, int id_to, int flow, union parameter *param); struct lcr_msg *message_get(void); void message_free(struct lcr_msg *message); - +void init_message(void); +void cleanup_message(void); diff --git a/options.c b/options.c index f58d7f9..eff38ce 100644 --- a/options.c +++ b/options.c @@ -9,11 +9,11 @@ ** ** \*****************************************************************************/ -#include "stdio.h" -#include "string.h" -#include "stdarg.h" -#include "unistd.h" -#include "stdlib.h" +#include +#include +#include +#include +#include #include "macro.h" #include "extension.h" #include "options.h" diff --git a/port.cpp b/port.cpp index 5998bc6..e88175b 100644 --- a/port.cpp +++ b/port.cpp @@ -316,6 +316,8 @@ void Port::set_tone(const char *dir, const char *name) SCPY(p_tone_dir, dir); SCPY(p_tone_name, name); } + /* trigger playback */ + update_load(); } else { p_tone_name[0]= '\0'; p_tone_dir[0]= '\0'; @@ -382,6 +384,8 @@ void Port::set_vbox_tone(const char *dir, const char *name) SPRINT(p_tone_dir, dir); SPRINT(p_tone_name, name); + /* trigger playback */ + update_load(); /* now we check if the cause exists, otherwhise we use error tone. */ if (p_tone_dir[0]) { @@ -585,13 +589,6 @@ try_loop: } -/* port handler: - * process transmission clock */ -int Port::handler(void) -{ - return(0); -} - /* endpoint sends messages to the port * this is called by the message_epoint inherited by child classes * therefor a return=1 means: stop, no more processing @@ -648,6 +645,8 @@ int Port::open_record(int type, int vbox, int skip, char *extension, int anon_ig /* RIFFxxxxWAVEfmt xxxx(fmt-size)dataxxxx... */ char dummyheader[8+4+8+sizeof(fmt)+8]; char filename[256]; + time_t now; + struct tm *now_tm; if (!extension) { PERROR("Port(%d) not an extension\n", p_serial); @@ -676,8 +675,11 @@ int Port::open_record(int type, int vbox, int skip, char *extension, int anon_ig if (vbox == 1) UPRINT(strchr(filename,'\0'), "/announcement"); - else + else { + time(&now); + now_tm = localtime(&now); UPRINT(strchr(filename,'\0'), "/%04d-%02d-%02d_%02d%02d%02d", now_tm->tm_year+1900, now_tm->tm_mon+1, now_tm->tm_mday, now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec); + } if (vbox == 2) { p_record_vbox_year = now_tm->tm_year; p_record_vbox_mon = now_tm->tm_mon; @@ -698,6 +700,7 @@ int Port::open_record(int type, int vbox, int skip, char *extension, int anon_ig PERROR("Port(%d) cannot record because file cannot be opened '%s'\n", p_serial, filename); return(0); } + update_rxoff(); fduse++; p_record_type = type; @@ -882,6 +885,7 @@ void Port::close_record(int beep, int mute) fclose(p_record); fduse--; p_record = NULL; + update_rxoff(); if (rename(p_record_filename, filename) < 0) { PERROR("Port(%d) cannot rename from '%s' to '%s'\n", p_serial, p_record_filename, filename); @@ -1139,4 +1143,11 @@ different_again: } +void Port::update_rxoff(void) +{ +} + +void Port::update_load(void) +{ +} diff --git a/port.h b/port.h index 5e8eba9..d71771f 100644 --- a/port.h +++ b/port.h @@ -137,11 +137,11 @@ class Port virtual ~Port(); class Port *next; /* next port in list */ int p_type; /* type of port */ - virtual int handler(void); virtual int message_epoint(unsigned int epoint_id, int message, union parameter *param); virtual void set_echotest(int echotest); virtual void set_tone(const char *dir, const char *name); virtual int read_audio(unsigned char *buffer, int length); + virtual void update_load(void); struct port_settings p_settings; @@ -205,6 +205,7 @@ class Port int p_record_anon_ignore; char p_record_vbox_email[128]; int p_record_vbox_email_file; + virtual void update_rxoff(void); /* inherited by mISDNport, to control rxoff */ void free_epointlist(struct epoint_list *epointlist); void free_epointid(unsigned int epoint_id); diff --git a/route.c b/route.c index 126424f..a448f39 100644 --- a/route.c +++ b/route.c @@ -1791,12 +1791,14 @@ struct route_action *EndpointAppPBX::route(struct route_ruleset *ruleset) int integer; char *string; FILE *tfp; - double timeout; + long long timeout, now_ll = 0, match_timeout = 0; + struct timeval current_time; struct mISDNport *mISDNport; struct admin_list *admin; + time_t now; + struct tm *now_tm; /* reset timeout action */ - e_match_timeout = 0; /* no timeout */ e_match_to_action = NULL; SCPY(callerid, numberrize_callerinfo(e_callerinfo.id, e_callerinfo.ntype, options.national, options.international)); @@ -1910,22 +1912,32 @@ struct route_action *EndpointAppPBX::route(struct route_ruleset *ruleset) goto match_string_prefix; case MATCH_TIME: + time(&now); + now_tm = localtime(&now); integer = now_tm->tm_hour*100 + now_tm->tm_min; goto match_integer; case MATCH_MDAY: + time(&now); + now_tm = localtime(&now); integer = now_tm->tm_mday; goto match_integer; case MATCH_MONTH: + time(&now); + now_tm = localtime(&now); integer = now_tm->tm_mon+1; goto match_integer; case MATCH_YEAR: + time(&now); + now_tm = localtime(&now); integer = now_tm->tm_year + 1900; goto match_integer; case MATCH_WDAY: + time(&now); + now_tm = localtime(&now); integer = now_tm->tm_wday; integer = integer?integer:7; /* correct sunday */ goto match_integer; @@ -1963,7 +1975,11 @@ struct route_action *EndpointAppPBX::route(struct route_ruleset *ruleset) break; case MATCH_TIMEOUT: - timeout = now_d + cond->integer_value; + if (!now_ll) { + gettimeofday(¤t_time, NULL); + now_ll = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; + } + timeout = now_ll + (cond->integer_value * MICRO_SECONDS); istrue = 1; break; @@ -2166,10 +2182,10 @@ struct route_action *EndpointAppPBX::route(struct route_ruleset *ruleset) cond = cond->next; } - if (timeout>now_d && match==1) /* the matching rule with timeout in the future */ - if (e_match_timeout<1 || timeoutnow_ll && match==1) /* the matching rule with timeout in the future */ + if (match_timeout == 0 || timeout < match_timeout) { /* first timeout or lower */ /* set timeout in the furture */ - e_match_timeout = timeout; + match_timeout = timeout; e_match_to_action = rule->action_first; e_match_to_extdialing = e_dialinginfo.id + dialing_required; match = 0; /* matches in the future */ @@ -2177,7 +2193,7 @@ struct route_action *EndpointAppPBX::route(struct route_ruleset *ruleset) if (match == 1) { /* matching, we return first action */ action = rule->action_first; - e_match_timeout = 0; /* no timeout */ + match_timeout = 0; /* no timeout */ e_match_to_action = NULL; e_extdialing = e_dialinginfo.id + dialing_required; break; @@ -2188,6 +2204,11 @@ struct route_action *EndpointAppPBX::route(struct route_ruleset *ruleset) } rule = rule->next; } + if (match_timeout == 0) + unsched_timer(&e_match_timeout); /* no timeout */ + else { + schedule_timer(&e_match_timeout, match_timeout / 1000000, match_timeout % 1000000); + } return(action); } diff --git a/select.c b/select.c new file mode 100644 index 0000000..ba9b563 --- /dev/null +++ b/select.c @@ -0,0 +1,434 @@ +/* based on code from OpenBSC */ + +#include +#include +#include +#include +#include +#include +#include +#include "macro.h" +#include "select.h" + +static int maxfd = 0; +static int unregistered; +static struct lcr_fd *fd_first = NULL; +static struct timeval *nearest_timer(struct timeval *select_timer, int *work); +static int next_work(void); + +int _register_fd(struct lcr_fd *fd, int when, int (*cb)(struct lcr_fd *fd, unsigned int what, void *instance, int index), void *instance, int index, const char *func) +{ + int flags; + + if (fd->inuse) + FATAL("FD that is registered in function %s is already in use\n", func); +// printf("registering fd %d %s\n", fd->fd, func); + + /* make FD nonblocking */ + flags = fcntl(fd->fd, F_GETFL); + if (flags < 0) + FATAL("Failed to F_GETFL\n"); + flags |= O_NONBLOCK; + flags = fcntl(fd->fd, F_SETFL, flags); + if (flags < 0) + FATAL("Failed to F_SETFL O_NONBLOCK\n"); + + /* Register FD */ + if (fd->fd > maxfd) + maxfd = fd->fd; + + /* append to list */ + fd->inuse = 1; + fd->when = when; + fd->cb = cb; + fd->cb_instance = instance; + fd->cb_index = index; + fd->next = fd_first; + fd_first = fd; + + return 0; +} + +void _unregister_fd(struct lcr_fd *fd, const char *func) +{ + struct lcr_fd **lcr_fdp; + + /* find pointer to fd */ + lcr_fdp = &fd_first; + while(*lcr_fdp) { + if (*lcr_fdp == fd) + break; + lcr_fdp = &((*lcr_fdp)->next); + } + if (!*lcr_fdp) { + FATAL("FD unregistered in function %s not in list\n", func); + } + + /* remove fd from list */ + fd->inuse = 0; + *lcr_fdp = fd->next; + unregistered = 1; +} + + +int select_main(int polling, int *global_change, void (*lock)(void), void (*unlock)(void)) +{ + struct lcr_fd *lcr_fd; + fd_set readset, writeset, exceptset; + int work = 0, temp, rc; + struct timeval no_time = {0, 0}; + struct timeval select_timer, *timer; + + /* goto again; + * + * this ensures that select is only called until: + * - no work event exists + * - and no timeout occurred + * + * if no future timeout exists, select will wait infinit. + */ + +again: + /* process all work events */ + if (next_work()) { + work = 1; + goto again; + } + + /* process timer events and get timeout for next timer event */ + temp = 0; + timer = nearest_timer(&select_timer, &temp); + if (temp) { + work = 1; + goto again; + } + if (polling) + timer = &no_time; +#warning TESTING + if (!timer) + printf("wait till infinity ..."); fflush(stdout); + + FD_ZERO(&readset); + FD_ZERO(&writeset); + FD_ZERO(&exceptset); + + /* prepare read and write fdsets */ + lcr_fd = fd_first; + while(lcr_fd) { + if (lcr_fd->when & LCR_FD_READ) + FD_SET(lcr_fd->fd, &readset); + if (lcr_fd->when & LCR_FD_WRITE) + FD_SET(lcr_fd->fd, &writeset); + if (lcr_fd->when & LCR_FD_EXCEPT) + FD_SET(lcr_fd->fd, &exceptset); + lcr_fd = lcr_fd->next; + } + + if (unlock) + unlock(); + rc = select(maxfd+1, &readset, &writeset, &exceptset, timer); + if (lock) + lock(); +#warning TESTING + if (!timer) + printf("interrupted.\n"); + if (rc < 0) + return 0; + if (global_change && *global_change) { + *global_change = 0; + return 1; + } + + /* fire timers */ +#if 0 + bsc_update_timers(); +#endif + + /* call registered callback functions */ +restart: + unregistered = 0; + lcr_fd = fd_first; + while(lcr_fd) { + int flags = 0; + + if (FD_ISSET(lcr_fd->fd, &readset)) { + flags |= LCR_FD_READ; + FD_CLR(lcr_fd->fd, &readset); + } + if (FD_ISSET(lcr_fd->fd, &writeset)) { + flags |= LCR_FD_WRITE; + FD_CLR(lcr_fd->fd, &writeset); + } + if (FD_ISSET(lcr_fd->fd, &exceptset)) { + flags |= LCR_FD_EXCEPT; + FD_CLR(lcr_fd->fd, &exceptset); + } + if (flags) { + work = 1; + lcr_fd->cb(lcr_fd, flags, lcr_fd->cb_instance, lcr_fd->cb_index); + if (unregistered) + goto restart; + return 1; + } + lcr_fd = lcr_fd->next; + } + return work; +} + + +static struct lcr_timer *timer_first = NULL; + +int _add_timer(struct lcr_timer *timer, int (*cb)(struct lcr_timer *timer, void *instance, int index), void *instance, int index, const char *func) +{ + if (timer->inuse) { + FATAL("timer that is registered in function %s is already in use\n", func); + } + +#if 0 + struct lcr_timer *test = timer_first; + while(test) { + if (test == timer) + FATAL("Timer already in list %s\n", func); + test = test->next; + } +#endif + + timer->inuse = 1; + timer->active = 0; + timer->timeout.tv_sec = 0; + timer->timeout.tv_usec = 0; + timer->cb = cb; + timer->cb_instance = instance; + timer->cb_index = index; + timer->next = timer_first; + timer_first = timer; + + return 0; +} + +void _del_timer(struct lcr_timer *timer, const char *func) +{ + struct lcr_timer **lcr_timerp; + + /* find pointer to timer */ + lcr_timerp = &timer_first; + while(*lcr_timerp) { + if (*lcr_timerp == timer) + break; + lcr_timerp = &((*lcr_timerp)->next); + } + if (!*lcr_timerp) { + FATAL("timer deleted in function %s not in list\n", func); + } + + /* remove timer from list */ + timer->inuse = 0; + *lcr_timerp = timer->next; +} + +void schedule_timer(struct lcr_timer *timer, int seconds, int microseconds) +{ + struct timeval current_time; + + if (!timer->inuse) { + FATAL("Timer not added\n"); + } + + gettimeofday(¤t_time, NULL); + unsigned long long currentTime = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; + currentTime += seconds * MICRO_SECONDS + microseconds; + timer->timeout.tv_sec = currentTime / MICRO_SECONDS; + timer->timeout.tv_usec = currentTime % MICRO_SECONDS; + timer->active = 1; +} + +void unsched_timer(struct lcr_timer *timer) +{ + timer->active = 0; +} + +/* if a timeout is reached, process timer, if not, return timer value for select */ +static struct timeval *nearest_timer(struct timeval *select_timer, int *work) +{ + struct timeval current; + struct timeval *nearest = NULL; + struct lcr_timer *lcr_timer, *lcr_nearest = NULL; + + /* find nearest timer, or NULL, if no timer active */ + lcr_timer = timer_first; + while(lcr_timer) { + if (lcr_timer->active && (!nearest || TIME_SMALLER(&lcr_timer->timeout, nearest))) { + nearest = &lcr_timer->timeout; + lcr_nearest = lcr_timer; + } + lcr_timer = lcr_timer->next; + } + + select_timer->tv_sec = 0; + select_timer->tv_usec = 0; + + if (!nearest) + return NULL; /* wait until infinity */ + + gettimeofday(¤t, NULL); + unsigned long long nearestTime = nearest->tv_sec * MICRO_SECONDS + nearest->tv_usec; + unsigned long long currentTime = current.tv_sec * MICRO_SECONDS + current.tv_usec; + + if (nearestTime > currentTime) { + select_timer->tv_sec = (nearestTime - currentTime) / MICRO_SECONDS; + select_timer->tv_usec = (nearestTime - currentTime) % MICRO_SECONDS; + return select_timer; + } else { + lcr_nearest->active = 0; + (*lcr_nearest->cb)(lcr_nearest, lcr_nearest->cb_instance, lcr_nearest->cb_index); + /* don't wait so we can process the queues, indicate "work=1" */ + select_timer->tv_sec = 0; + select_timer->tv_usec = 0; + *work = 1; + return select_timer; + } +} + + +static struct lcr_work *work_first = NULL; /* chain of work */ +static struct lcr_work *first_event = NULL, *last_event = NULL; /* chain of active events */ + +#ifdef DEBUG_WORK +void show_chain(const char *func) +{ + struct lcr_work *work = first_event; + printf("chain:%s\n", func); + while(work) { + printf("%p - %p - %p\n", work->prev_event, work, work->next_event); + work = work->next_event; + } +} +#endif + +int _add_work(struct lcr_work *work, int (*cb)(struct lcr_work *work, void *instance, int index), void *instance, int index, const char *func) +{ + if (work->inuse) { + FATAL("work that is registered in function %s is already in use\n", func); + } + +#ifdef DEBUG_WORK + printf("add work %p from function %s\n", work, func); + show_chain("before add"); +#endif + work->inuse = 1; + work->active = 0; + work->cb = cb; + work->cb_instance = instance; + work->cb_index = index; + work->next = work_first; + work_first = work; +#ifdef DEBUG_WORK + show_chain("after add"); +#endif + + return 0; +} + +void _del_work(struct lcr_work *work, const char *func) +{ + struct lcr_work **lcr_workp; + +#ifdef DEBUG_WORK + show_chain("before detach"); +#endif + if (work->active) { + /* first event removed */ + if (!work->prev_event) + first_event = work->next_event; + else + work->prev_event->next_event = work->next_event; + /* last event removed */ + if (!work->next_event) + last_event = work->prev_event; + else + work->next_event->prev_event = work->prev_event; + } +#ifdef DEBUG_WORK + show_chain("after detach"); +#endif + + /* find pointer to work */ + lcr_workp = &work_first; + while(*lcr_workp) { + if (*lcr_workp == work) + break; + lcr_workp = &((*lcr_workp)->next); + } + if (!*lcr_workp) { + FATAL("work deleted by '%s' not in list\n", func); + } + + /* remove work from list */ + work->inuse = 0; + *lcr_workp = work->next; +#ifdef DEBUG_WORK + show_chain("after delete"); +#endif +} + +void trigger_work(struct lcr_work *work) +{ + if (!work->inuse) { + FATAL("Work not added\n"); + } + + /* event already triggered */ + if (work->active) + return; + +#ifdef DEBUG_WORK + show_chain("before trigger"); +#endif + /* append to tail of chain */ + if (last_event) + last_event->next_event = work; + work->prev_event = last_event; + work->next_event = NULL; + last_event = work; + if (!first_event) + first_event = work; +#ifdef DEBUG_WORK + show_chain("after trigger"); +#endif + + work->active = 1; +} + +/* get first work and remove from event chain */ +static int next_work(void) +{ + struct lcr_work *lcr_work; + + if (!first_event) + return 0; + +#ifdef DEBUG_WORK + show_chain("before next_work"); +#endif + if (!first_event->inuse) { + FATAL("Work not added\n"); + } + + /* detach from event chain */ + lcr_work = first_event; + first_event = lcr_work->next_event; + if (!first_event) + last_event = NULL; + else + first_event->prev_event = NULL; + +#ifdef DEBUG_WORK + show_chain("after next_work"); +#endif + lcr_work->active = 0; + + (*lcr_work->cb)(lcr_work, lcr_work->cb_instance, lcr_work->cb_index); + + return 1; +} + diff --git a/select.h b/select.h new file mode 100644 index 0000000..c4e1418 --- /dev/null +++ b/select.h @@ -0,0 +1,62 @@ + +#define LCR_FD_READ 1 +#define LCR_FD_WRITE 2 +#define LCR_FD_EXCEPT 4 + +#define MICRO_SECONDS 1000000LL + +#define TIME_SMALLER(left, right) \ + (((left)->tv_sec*MICRO_SECONDS+(left)->tv_usec) <= ((right)->tv_sec*MICRO_SECONDS+(right)->tv_usec)) + +struct lcr_fd { + struct lcr_fd *next; /* pointer to next element in list */ + int inuse; /* if in use */ + int fd; /* file descriptior if in use */ + int when; /* select on what event */ + int (*cb)(struct lcr_fd *fd, unsigned int what, void *instance, int index); /* callback */ + void *cb_instance; + int cb_index; +}; + +#define register_fd(a, b, c, d, e) _register_fd(a, b, c, d, e, __func__); +int _register_fd(struct lcr_fd *fd, int when, int (*cb)(struct lcr_fd *fd, unsigned int what, void *instance, int index), void *instance, int index, const char *func); +#define unregister_fd(a) _unregister_fd(a, __func__); +void _unregister_fd(struct lcr_fd *fd, const char *func); +int select_main(int polling, int *global_change, void (*lock)(void), void (*unlock)(void)); + + +struct lcr_timer { + struct lcr_timer *next; /* pointer to next element in list */ + int inuse; /* if in use */ + int active; /* if timer is currently active */ + struct timeval timeout; /* timestamp when to timeout */ + int (*cb)(struct lcr_timer *timer, void *instance, int index); /* callback */ + void *cb_instance; + int cb_index; +}; + +#define add_timer(a, b, c, d) _add_timer(a, b, c, d, __func__); +int _add_timer(struct lcr_timer *timer, int (*cb)(struct lcr_timer *timer, void *instance, int index), void *instance, int index, const char *func); +#define del_timer(a) _del_timer(a, __func__); +void _del_timer(struct lcr_timer *timer, const char *func); +void schedule_timer(struct lcr_timer *timer, int seconds, int microseconds); +void unsched_timer(struct lcr_timer *timer); + + +struct lcr_work { + struct lcr_work *next; /* pointer to next element in list */ + struct lcr_work *prev_event, *next_event; /* pointer to previous/next event, if triggered */ + int inuse; /* if in use */ + int active; /* if timer is currently active */ + int (*cb)(struct lcr_work *work, void *instance, int index); /* callback */ + void *cb_instance; + int cb_index; +}; + +#define add_work(a, b, c, d) _add_work(a, b, c, d, __func__); +int _add_work(struct lcr_work *work, int (*cb)(struct lcr_work *work, void *instance, int index), void *instance, int index, const char *func); +#define del_work(a) _del_work(a, __func__); +void _del_work(struct lcr_work *work, const char *func); +void trigger_work(struct lcr_work *work); + + diff --git a/socket_server.c b/socket_server.c index a33f626..b67b91f 100644 --- a/socket_server.c +++ b/socket_server.c @@ -20,14 +20,15 @@ int sock = -1; struct sockaddr_un sock_address; struct admin_list *admin_first = NULL; +static struct lcr_fd admin_fd; + +int admin_handle(struct lcr_fd *fd, unsigned int what, void *instance, int index); /* * initialize admin socket */ int admin_init(void) { - unsigned int on = 1; - /* open and bind socket */ if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { PERROR("Failed to create admin socket. (errno=%d)\n", errno); @@ -55,14 +56,9 @@ int admin_init(void) PERROR("Failed to listen to socket \"%s\". (errno=%d)\n", sock_address.sun_path, errno); return(-1); } - if (ioctl(sock, FIONBIO, (unsigned char *)(&on)) < 0) { - close(sock); - unlink(socket_name); - fhuse--; - sock = -1; - PERROR("Failed to set socket \"%s\" into non-blocking mode. (errno=%d)\n", sock_address.sun_path, errno); - return(-1); - } + memset(&admin_fd, 0, sizeof(admin_fd)); + admin_fd.fd = sock; + register_fd(&admin_fd, LCR_FD_READ | LCR_FD_EXCEPT, admin_handle, NULL, 0); if (chmod(socket_name, options.socketrights) < 0) { PERROR("Failed to change socket rights to %d. (errno=%d)\n", options.socketrights, errno); } @@ -86,6 +82,7 @@ void free_connection(struct admin_list *admin) class Join *join, *joinnext; struct mISDNport *mISDNport; int i, ii; + struct admin_list **adminp; /* free remote joins */ if (admin->remote_name[0]) { @@ -107,7 +104,7 @@ void free_connection(struct admin_list *admin) while(i < ii) { if (mISDNport->b_remote_id[i] == admin->sock) { mISDNport->b_state[i] = B_STATE_IDLE; - mISDNport->b_timer[i] = 0; + unsched_timer(&mISDNport->b_timer[i]); mISDNport->b_remote_id[i] = 0; mISDNport->b_remote_ref[i] = 0; } @@ -131,22 +128,28 @@ void free_connection(struct admin_list *admin) } if (admin->sock >= 0) { + unregister_fd(&admin->fd); close(admin->sock); fhuse--; } -// printf("new\n", response); response = admin->response; while (response) { -//#warning -// printf("%x\n", response); temp = response->next; FREE(response, 0); memuse--; response = (struct admin_queue *)temp; } -// printf("new2\n", response); + + adminp = &admin_first; + while(*adminp) { + if (*adminp == admin) + break; + adminp = &((*adminp)->next); + } + if (*adminp) + *adminp = (*adminp)->next; + FREE(admin, 0); -// printf("new3\n", response); memuse--; } @@ -160,13 +163,13 @@ void admin_cleanup(void) admin = admin_first; while(admin) { -//printf("clean\n"); next = admin->next; free_connection(admin); admin = next; } if (sock >= 0) { + unregister_fd(&admin_fd); close(sock); fhuse--; } @@ -206,7 +209,6 @@ int admin_interface(struct admin_queue **responsep) /* attach to response chain */ *responsep = response; responsep = &response->next; - return(0); } @@ -274,7 +276,7 @@ int admin_route(struct admin_queue **responsep) } } else if (apppbx->e_state != EPOINT_STATE_CONNECT) { release: - apppbx->e_callback = 0; + unsched_timer(&apppbx->e_callback_timeout); apppbx->e_action = NULL; apppbx->release(RELEASE_ALL, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL); start_trace(-1, @@ -288,7 +290,7 @@ int admin_route(struct admin_queue **responsep) end_trace(); } - apppbx->e_action_timeout = 0; + unsched_timer(&apppbx->e_action_timeout); apppbx->e_rule = NULL; apppbx->e_ruleset = NULL; @@ -309,7 +311,6 @@ int admin_route(struct admin_queue **responsep) /* attach to response chain */ *responsep = response; responsep = &response->next; - return(0); } @@ -486,7 +487,7 @@ int admin_release(struct admin_queue **responsep, char *message) goto out; } - apppbx->e_callback = 0; + unsched_timer(&apppbx->e_callback_timeout); apppbx->release(RELEASE_ALL, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL, LOCATION_PRIVATE_LOCAL, CAUSE_NORMAL); out: @@ -518,7 +519,6 @@ int admin_call(struct admin_list *admin, struct admin_message *msg) apppbx->e_callerinfo.present = INFO_PRESENT_RESTRICTED; apppbx->e_callerinfo.screen = INFO_SCREEN_NETWORK; -//printf("hh=%d\n", apppbx->e_capainfo.hlc); apppbx->e_capainfo.bearer_capa = msg->u.call.bc_capa; apppbx->e_capainfo.bearer_mode = msg->u.call.bc_mode; @@ -569,7 +569,6 @@ void admin_call_response(int adminid, int message, const char *connected, int ca response->num = 1; /* message */ response->am[0].message = message; -// printf("MESSAGE: %d\n", message); SCPY(response->am[0].u.call.callerid, connected); response->am[0].u.call.cause = cause; @@ -579,6 +578,7 @@ void admin_call_response(int adminid, int message, const char *connected, int ca /* attach to response chain */ *responsep = response; responsep = &response->next; + admin->fd.when |= LCR_FD_WRITE; } @@ -722,7 +722,7 @@ int admin_message_from_join(int remote_id, unsigned int ref, int message_type, u (*responsep)->am[0].u.msg.type = message_type; (*responsep)->am[0].u.msg.ref = ref; memcpy(&(*responsep)->am[0].u.msg.param, param, sizeof(union parameter)); - + admin->fd.when |= LCR_FD_WRITE; return(0); } @@ -732,7 +732,6 @@ int admin_message_from_join(int remote_id, unsigned int ref, int message_type, u */ int admin_state(struct admin_queue **responsep) { - class Port *port; class EndpointAppPBX *apppbx; class Join *join; @@ -745,6 +744,8 @@ int admin_state(struct admin_queue **responsep) int anybusy; struct admin_queue *response; struct admin_list *admin; + struct tm *now_tm; + time_t now; /* create state response */ response = (struct admin_queue *)MALLOC(sizeof(struct admin_queue)+sizeof(admin_message)); @@ -755,6 +756,8 @@ int admin_state(struct admin_queue **responsep) /* version */ SCPY(response->am[0].u.s.version_string, VERSION_STRING); /* time */ + time(&now); + now_tm = localtime(&now); memcpy(&response->am[0].u.s.tm, now_tm, sizeof(struct tm)); /* log file */ SCPY(response->am[0].u.s.logfile, options.log); @@ -1064,68 +1067,54 @@ int sockserial = 1; // must start with 1, because 0 is used if no serial is set /* * handle admin socket (non blocking) */ -int admin_handle(void) +int admin_handle_con(struct lcr_fd *fd, unsigned int what, void *instance, int index); + +int admin_handle(struct lcr_fd *fd, unsigned int what, void *instance, int index) { - struct admin_list *admin, **adminp; - void *temp; - struct admin_message msg; - int len; int new_sock; socklen_t sock_len = sizeof(sock_address); - unsigned int on = 1; - int work = 0; /* if work was done */ - struct Endpoint *epoint; - - if (sock < 0) - return(0); + struct admin_list *admin; /* check for new incoming connections */ if ((new_sock = accept(sock, (struct sockaddr *)&sock_address, &sock_len)) >= 0) { - work = 1; /* insert new socket */ admin = (struct admin_list *)MALLOC(sizeof(struct admin_list)); - if (ioctl(new_sock, FIONBIO, (unsigned char *)(&on)) >= 0) { -//#warning -// PERROR("DEBUG incoming socket %d, serial=%d\n", new_sock, sockserial); - memuse++; - fhuse++; - admin->sockserial = sockserial++; - admin->next = admin_first; - admin_first = admin; - admin->sock = new_sock; - } else { - close(new_sock); - FREE(admin, sizeof(struct admin_list)); - } + memuse++; + fhuse++; + admin->sockserial = sockserial++; + admin->next = admin_first; + admin_first = admin; + admin->sock = new_sock; + admin->fd.fd = new_sock; + register_fd(&admin->fd, LCR_FD_READ | LCR_FD_EXCEPT, admin_handle_con, admin, 0); } else { if (errno != EWOULDBLOCK) { PERROR("Failed to accept connection from socket \"%s\". (errno=%d) Closing socket.\n", sock_address.sun_path, errno); admin_cleanup(); - return(1); + return 0; } } - /* loop all current socket connections */ - admin = admin_first; - adminp = &admin_first; - while(admin) { + return 0; +} + +int admin_handle_con(struct lcr_fd *fd, unsigned int what, void *instance, int index) +{ + struct admin_list *admin = (struct admin_list *)instance; + void *temp; + struct admin_message msg; + int len; + struct Endpoint *epoint; + + if ((what & LCR_FD_READ)) { /* read command */ len = read(admin->sock, &msg, sizeof(msg)); if (len < 0) { - if (errno != EWOULDBLOCK) { - work = 1; - brokenpipe: - PDEBUG(DEBUG_LOG, "Broken pipe on socket %d. (errno=%d).\n", admin->sock, errno); - *adminp = admin->next; - free_connection(admin); - admin = *adminp; - continue; - } - goto send_data; + brokenpipe: + PDEBUG(DEBUG_LOG, "Broken pipe on socket %d. (errno=%d).\n", admin->sock, errno); + free_connection(admin); + return 0; } - work = 1; -//#warning -//PERROR("DEBUG socket %d got data. serial=%d\n", admin->sock, admin->sockserial); if (len == 0) { end: @@ -1138,28 +1127,19 @@ int admin_handle(void) } } -//#warning -//PERROR("DEBUG socket %d closed by remote.\n", admin->sock); - *adminp = admin->next; free_connection(admin); - admin = *adminp; -//PERROR("DEBUG (admin_first=%x)\n", admin_first); - continue; + return 0; } if (len != sizeof(msg)) { PERROR("Short/long read on socket %d. (len=%d != size=%d).\n", admin->sock, len, sizeof(msg)); - *adminp = admin->next; free_connection(admin); - admin = *adminp; - continue; + return 0; } /* process socket command */ if (admin->response && msg.message != ADMIN_MESSAGE) { PERROR("Data from socket %d while sending response.\n", admin->sock); - *adminp = admin->next; free_connection(admin); - admin = *adminp; - continue; + return 0; } switch (msg.message) { case ADMIN_REQUEST_CMD_INTERFACE: @@ -1167,6 +1147,7 @@ int admin_handle(void) PERROR("Failed to create dial response for socket %d.\n", admin->sock); goto response_error; } + admin->fd.when |= LCR_FD_WRITE; break; case ADMIN_REQUEST_CMD_ROUTE: @@ -1174,6 +1155,7 @@ int admin_handle(void) PERROR("Failed to create dial response for socket %d.\n", admin->sock); goto response_error; } + admin->fd.when |= LCR_FD_WRITE; break; case ADMIN_REQUEST_CMD_DIAL: @@ -1181,6 +1163,7 @@ int admin_handle(void) PERROR("Failed to create dial response for socket %d.\n", admin->sock); goto response_error; } + admin->fd.when |= LCR_FD_WRITE; break; case ADMIN_REQUEST_CMD_RELEASE: @@ -1188,6 +1171,7 @@ int admin_handle(void) PERROR("Failed to create release response for socket %d.\n", admin->sock); goto response_error; } + admin->fd.when |= LCR_FD_WRITE; break; case ADMIN_REQUEST_STATE: @@ -1195,6 +1179,7 @@ int admin_handle(void) PERROR("Failed to create state response for socket %d.\n", admin->sock); goto response_error; } + admin->fd.when |= LCR_FD_WRITE; break; case ADMIN_TRACE_REQUEST: @@ -1202,6 +1187,7 @@ int admin_handle(void) PERROR("Failed to create trace response for socket %d.\n", admin->sock); goto response_error; } + admin->fd.when |= LCR_FD_WRITE; break; case ADMIN_REQUEST_CMD_BLOCK: @@ -1209,6 +1195,7 @@ int admin_handle(void) PERROR("Failed to create block response for socket %d.\n", admin->sock); goto response_error; } + admin->fd.when |= LCR_FD_WRITE; break; case ADMIN_MESSAGE: @@ -1216,71 +1203,46 @@ int admin_handle(void) PERROR("Failed to deliver message for socket %d.\n", admin->sock); goto response_error; } -#if 0 -#warning DEBUGGING -{ - struct admin_queue *response; - printf("Chain: "); - response = admin->response; - while(response) { - printf("%c", '0'+response->am[0].message); - response=response->next; - } - printf("\n"); -} -#endif break; case ADMIN_CALL_SETUP: if (admin_call(admin, &msg) < 0) { PERROR("Failed to create call for socket %d.\n", admin->sock); response_error: - *adminp = admin->next; free_connection(admin); - admin = *adminp; - continue; + return 0; } break; default: PERROR("Invalid message %d from socket %d.\n", msg.message, admin->sock); - *adminp = admin->next; free_connection(admin); - admin = *adminp; - continue; + return 0; } + } + + if ((what & LCR_FD_WRITE)) { /* write queue */ - send_data: if (admin->response) { -//#warning -//PERROR("DEBUG socket %d sending data.\n", admin->sock); len = write(admin->sock, ((unsigned char *)(admin->response->am))+admin->response->offset, sizeof(struct admin_message)*(admin->response->num)-admin->response->offset); if (len < 0) { - if (errno != EWOULDBLOCK) { - work = 1; - goto brokenpipe; - } - goto next; + goto brokenpipe; } - work = 1; if (len == 0) goto end; if (len < (int)(sizeof(struct admin_message)*(admin->response->num) - admin->response->offset)) { admin->response->offset+=len; - goto next; + return 0; } else { temp = admin->response; admin->response = admin->response->next; FREE(temp, 0); memuse--; } - } - /* done with socket instance */ - next: - adminp = &admin->next; - admin = admin->next; + } else + admin->fd.when &= ~LCR_FD_WRITE; } - return(work); + return 0; } diff --git a/socket_server.h b/socket_server.h index 4177139..b3ea89a 100644 --- a/socket_server.h +++ b/socket_server.h @@ -21,6 +21,7 @@ struct admin_queue { struct admin_list { struct admin_list *next; int sock; + struct lcr_fd fd; int sockserial; char remote_name[32]; /* socket is connected remote application */ struct admin_trace_req trace; /* stores trace, if detail != 0 */ @@ -31,7 +32,6 @@ struct admin_list { extern struct admin_list *admin_first; int admin_init(void); void admin_cleanup(void); -int admin_handle(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); diff --git a/ss5.cpp b/ss5.cpp index 259b647..09f97eb 100644 --- a/ss5.cpp +++ b/ss5.cpp @@ -333,6 +333,7 @@ class Pss5 *ss5_hunt_line(struct mISDNport *mISDNport) return NULL; } +int queue_event(struct lcr_work *work, void *instance, int index); /* * constructor @@ -352,8 +353,8 @@ Pss5::Pss5(int type, struct mISDNport *mISDNport, char *portname, struct port_se //p_m_s_decoder_buffer; p_m_s_sample_nr = 0; p_m_s_recog = 0; - p_m_s_timer = 0.0; - p_m_s_timer_fn = NULL; + memset(&p_m_s_queue, 0, sizeof(p_m_s_queue)); + add_work(&p_m_s_queue, queue_event, this, 0); p_m_s_answer = 0; p_m_s_busy_flash = 0; p_m_s_clear_back = 0; @@ -372,6 +373,7 @@ Pss5::Pss5(int type, struct mISDNport *mISDNport, char *portname, struct port_se */ Pss5::~Pss5() { + del_work(&p_m_s_queue); } @@ -383,7 +385,47 @@ void Pss5::_new_ss5_state(int state, const char *func, int line) PDEBUG(DEBUG_SS5, "%s(%s:%d): changing SS5 state from %s to %s\n", p_name, func, line, ss5_state_name[p_m_s_state], ss5_state_name[state]); p_m_s_state = state; p_m_s_signal = SS5_SIGNAL_NULL; + + if (p_m_s_state == SS5_STATE_IDLE && (p_m_s_answer || p_m_s_busy_flash || p_m_s_clear_back)) + trigger_work(&p_m_s_queue); } + +int queue_event(struct lcr_work *work, void *instance, int index) +{ + class Pss5 *ss5port = (class Pss5 *)instance; + + if (ss5port->p_m_s_state == SS5_STATE_IDLE) { + /* if answer signal is queued */ + if (ss5port->p_m_s_answer) { + ss5port->p_m_s_answer = 0; + /* start answer */ + ss5_trace_header(ss5port->p_m_mISDNport, ss5port, SS5_ANSWER_REQ, ss5port->p_m_b_channel); + end_trace(); + ss5port->start_signal(SS5_STATE_ANSWER); + } + + /* if busy-flash signal is queued */ + if (ss5port->p_m_s_busy_flash) { + ss5port->p_m_s_busy_flash = 0; + /* start busy-flash */ + ss5_trace_header(ss5port->p_m_mISDNport, ss5port, SS5_BUSY_FLASH_REQ, ss5port->p_m_b_channel); + end_trace(); + ss5port->start_signal(SS5_STATE_BUSY_FLASH); + } + + /* if clear-back signal is queued */ + if (ss5port->p_m_s_clear_back) { + ss5port->p_m_s_clear_back = 0; + /* start clear-back */ + ss5_trace_header(ss5port->p_m_mISDNport, ss5port, SS5_CLEAR_BACK_REQ, ss5port->p_m_b_channel); + end_trace(); + ss5port->start_signal(SS5_STATE_CLEAR_BACK); + } + } + + return 0; +} + void Pss5::_new_ss5_signal(int signal, const char *func, int line) { if (p_m_s_signal) @@ -1606,8 +1648,6 @@ void Pss5::do_release(int cause, int location) { struct lcr_msg *message; - p_m_s_timer = 0.0; - /* sending release to endpoint */ while(p_epointlist) { message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE); @@ -1672,52 +1712,6 @@ void Pss5::do_setup(char *dial, int complete) } -/* - * handler - */ -int Pss5::handler(void) -{ - int ret; - - if ((ret = PmISDN::handler())) - return(ret); - - /* handle timer */ - if (p_m_s_timer && p_m_s_timer < now) { - p_m_s_timer = 0.0; - (this->*(p_m_s_timer_fn))(); - } - - /* if answer signal is queued */ - if (p_m_s_answer && p_m_s_state == SS5_STATE_IDLE) { - p_m_s_answer = 0; - /* start answer */ - ss5_trace_header(p_m_mISDNport, this, SS5_ANSWER_REQ, p_m_b_channel); - end_trace(); - start_signal(SS5_STATE_ANSWER); - } - - /* if busy-flash signal is queued */ - if (p_m_s_busy_flash && p_m_s_state == SS5_STATE_IDLE) { - p_m_s_busy_flash = 0; - /* start busy-flash */ - ss5_trace_header(p_m_mISDNport, this, SS5_BUSY_FLASH_REQ, p_m_b_channel); - end_trace(); - start_signal(SS5_STATE_BUSY_FLASH); - } - - /* if clear-back signal is queued */ - if (p_m_s_clear_back && p_m_s_state == SS5_STATE_IDLE) { - p_m_s_clear_back = 0; - /* start clear-back */ - ss5_trace_header(p_m_mISDNport, this, SS5_CLEAR_BACK_REQ, p_m_b_channel); - end_trace(); - start_signal(SS5_STATE_CLEAR_BACK); - } - - return(0); -} - /* * handles all messages from endpoint @@ -1917,6 +1911,7 @@ void Pss5::message_connect(unsigned int epoint_id, int message_id, union paramet if (p_state != PORT_STATE_CONNECT) { new_state(PORT_STATE_CONNECT); p_m_s_answer = 1; + trigger_work(&p_m_s_queue); } set_tone("", NULL); @@ -1937,8 +1932,6 @@ if (0 || p_type==PORT_TYPE_SS5_OUT) { /* outgoing exchange */ start_signal(SS5_STATE_CLEAR_BACK); new_state(PORT_STATE_OUT_DISCONNECT); -// p_m_s_timer_fn = &Pss5::register_timeout; -// p_m_s_timer = now + 30.0; } /* MESSAGE_RELEASE */ diff --git a/ss5.h b/ss5.h index b1cc215..8a0c20d 100644 --- a/ss5.h +++ b/ss5.h @@ -25,7 +25,6 @@ class Pss5 : public PmISDN public: Pss5(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode); ~Pss5(); - int handler(void); int message_epoint(unsigned int epoint_id, int message, union parameter *param); void set_tone(const char *dir, const char *name); @@ -43,8 +42,7 @@ class Pss5 : public PmISDN unsigned char p_m_s_delay_mute[400/SS5_DECODER_NPOINTS]; /* 40 ms delay on mute, so a 'chirp' can be heared */ int p_m_s_sample_nr; /* decoder's sample number, counter */ int p_m_s_recog; /* sample counter to wait for signal recognition time */ - double p_m_s_timer; - void (Pss5::*p_m_s_timer_fn)(void); + struct lcr_work p_m_s_queue; int p_m_s_answer; /* queued signal */ int p_m_s_busy_flash; /* queued signal */ int p_m_s_clear_back; /* queued signal */ diff --git a/trace.c b/trace.c index 73686ff..b8dc001 100644 --- a/trace.c +++ b/trace.c @@ -22,6 +22,8 @@ static const char *spaces = " "; */ void _start_trace(const char *__file, int __line, int port, struct interface *interface, const char *caller, const char *dialing, int direction, int category, int serial, const char *name) { + struct timeval current_time; + if (trace.name[0]) PERROR("trace already started (name=%s) in file %s line %d\n", trace.name, __file, __line); memset(&trace, 0, sizeof(struct trace)); @@ -39,8 +41,9 @@ void _start_trace(const char *__file, int __line, int port, struct interface *in SCPY(trace.name, name); if (!trace.name[0]) SCPY(trace.name, ""); - trace.sec = now_tv.tv_sec; - trace.usec = now_tv.tv_usec; + gettimeofday(¤t_time, NULL); + trace.sec = current_time.tv_sec; + trace.usec = current_time.tv_usec; } @@ -270,7 +273,7 @@ void _end_trace(const char *__file, int __line) if (string) { /* process debug */ if (options.deb) - debug(NULL, 0, "trace", string); + debug(NULL, 0, "TRACE", string); /* process log */ if (options.log[0]) { fp = fopen(options.log, "a"); diff --git a/vbox.cpp b/vbox.cpp index 84d679e..6eb0ce2 100644 --- a/vbox.cpp +++ b/vbox.cpp @@ -17,6 +17,9 @@ /* note: recording log is written at endpoint */ +int announce_timer(struct lcr_timer *timer, void *instance, int index); +int record_timeout(struct lcr_timer *timer, void *instance, int index); + /* * initialize vbox port */ @@ -26,8 +29,11 @@ VBoxPort::VBoxPort(int type, struct port_settings *settings) : Port(type, "vbox" p_vbox_announce_fh = -1; p_vbox_audio_start = 0; p_vbox_audio_transferred = 0; - p_vbox_record_start = 0; p_vbox_record_limit = 0; + memset(&p_vbox_announce_timer, 0, sizeof(p_vbox_announce_timer)); + add_timer(&p_vbox_announce_timer, announce_timer, this, 0); + memset(&p_vbox_record_timeout, 0, sizeof(p_vbox_record_timeout)); + add_timer(&p_vbox_record_timeout, record_timeout, this, 0); } @@ -36,6 +42,8 @@ VBoxPort::VBoxPort(int type, struct port_settings *settings) : Port(type, "vbox" */ VBoxPort::~VBoxPort() { + del_timer(&p_vbox_announce_timer); + del_timer(&p_vbox_record_timeout); if (p_vbox_announce_fh >= 0) { close(p_vbox_announce_fh); p_vbox_announce_fh = -1; @@ -58,119 +66,130 @@ static void vbox_trace_header(class VBoxPort *vbox, const char *message, int dir } -/* - * handler of vbox - */ -int VBoxPort::handler(void) +int record_timeout(struct lcr_timer *timer, void *instance, int index) +{ + class VBoxPort *vboxport = (class VBoxPort *)instance; + struct lcr_msg *message; + + while(vboxport->p_epointlist) { + /* send release */ + message = message_create(vboxport->p_serial, vboxport->p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE); + message->param.disconnectinfo.cause = 16; + message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + message_put(message); + vbox_trace_header(vboxport, "RELEASE from VBox (recoding limit reached)", DIRECTION_IN); + add_trace("cause", "value", "%d", message->param.disconnectinfo.cause); + add_trace("cause", "location", "%d", message->param.disconnectinfo.location); + end_trace(); + /* remove epoint */ + vboxport->free_epointlist(vboxport->p_epointlist); + } + /* recording is close during destruction */ + delete vboxport; + return 0; +} + +int announce_timer(struct lcr_timer *timer, void *instance, int index) +{ + class VBoxPort *vboxport = (class VBoxPort *)instance; + + /* port my self destruct here */ + vboxport->send_announcement(); + + return 0; +} + +void VBoxPort::send_announcement(void) { struct lcr_msg *message; unsigned int tosend; unsigned char buffer[ISDN_TRANSMIT]; - time_t currenttime; class Endpoint *epoint; - int ret; - - if ((ret = Port::handler())) - return(ret); - - if (p_vbox_record_start && p_vbox_record_limit) { - time(¤ttime); - if (currenttime > (p_vbox_record_limit+p_vbox_record_start)) { - while(p_epointlist) { - /* send release */ - message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE); - message->param.disconnectinfo.cause = 16; - message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; - message_put(message); - vbox_trace_header(this, "RELEASE from VBox (recoding limit reached)", DIRECTION_IN); - add_trace("cause", "value", "%d", message->param.disconnectinfo.cause); - add_trace("cause", "location", "%d", message->param.disconnectinfo.location); - end_trace(); - /* remove epoint */ - free_epointlist(p_epointlist); - } - /* recording is close during destruction */ - delete this; - return(-1); /* must return because port is gone */ - } - } + int temp; + struct timeval current_time; + long long now; + + /* don't restart timer, if announcement is played */ + if (p_vbox_announce_fh < 0) + return; + + gettimeofday(¤t_time, NULL); + now = current_time.tv_sec * MICRO_SECONDS + current_time.tv_usec; /* set time the first time */ - if (p_vbox_audio_start < 1) { - p_vbox_audio_start = now_d; - return(0); - } + if (!p_vbox_audio_start) + p_vbox_audio_start = now - ISDN_TRANSMIT; /* calculate the number of bytes */ - tosend = (unsigned int)((now_d-p_vbox_audio_start)*8000) - p_vbox_audio_transferred; + tosend = (unsigned int)(now - p_vbox_audio_start) - p_vbox_audio_transferred; - /* wait for more */ - if (tosend < sizeof(buffer)) - return(0); - tosend = sizeof(buffer); + /* schedule next event */ + temp = ISDN_TRANSMIT + ISDN_TRANSMIT - tosend; + if (temp < 0) + temp = 0; + schedule_timer(&p_vbox_announce_timer, 0, temp*125); + + if (tosend > sizeof(buffer)) + tosend = sizeof(buffer); /* add the number of samples elapsed */ p_vbox_audio_transferred += tosend; /* if announcement is currently played, send audio data */ - if (p_vbox_announce_fh >=0) { - tosend = read_tone(p_vbox_announce_fh, buffer, p_vbox_announce_codec, tosend, p_vbox_announce_size, &p_vbox_announce_left, 1); - if (tosend <= 0) { - /* end of file */ - close(p_vbox_announce_fh); - p_vbox_announce_fh = -1; - fhuse--; - - time(¤ttime); - p_vbox_record_start = currenttime; - - /* connect if not already */ - epoint = find_epoint_id(ACTIVE_EPOINT(p_epointlist)); - if (epoint) { - /* if we sent our announcement during ringing, we must now connect */ - if (p_vbox_ext.vbox_free) { - /* send connect message */ - message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT); - memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info)); - message_put(message); - vbox_trace_header(this, "CONNECT from VBox (announcement is over)", DIRECTION_IN); - end_trace(); - new_state(PORT_STATE_CONNECT); - } - } + tosend = read_tone(p_vbox_announce_fh, buffer, p_vbox_announce_codec, tosend, p_vbox_announce_size, &p_vbox_announce_left, 1); + if (tosend <= 0) { + /* end of file */ + close(p_vbox_announce_fh); + p_vbox_announce_fh = -1; + fhuse--; - /* start recording, if not already */ - if (p_vbox_mode == VBOX_MODE_NORMAL) { - /* recording start */ - open_record(p_vbox_ext.vbox_codec, 2, 0, p_vbox_ext.number, p_vbox_ext.anon_ignore, p_vbox_ext.vbox_email, p_vbox_ext.vbox_email_file); - vbox_trace_header(this, "RECORDING (announcement is over)", DIRECTION_IN); - end_trace(); - } else // else!! - if (p_vbox_mode == VBOX_MODE_ANNOUNCEMENT) { - /* send release */ - message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE); - message->param.disconnectinfo.cause = 16; - message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + if (p_vbox_record_limit) + schedule_timer(&p_vbox_record_timeout, p_vbox_record_limit, 0); + + /* connect if not already */ + epoint = find_epoint_id(ACTIVE_EPOINT(p_epointlist)); + if (epoint) { + /* if we sent our announcement during ringing, we must now connect */ + if (p_vbox_ext.vbox_free) { + /* send connect message */ + message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT); + memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info)); message_put(message); - vbox_trace_header(this, "RELEASE from VBox (after annoucement)", DIRECTION_IN); - add_trace("cause", "value", "%d", message->param.disconnectinfo.cause); - add_trace("cause", "location", "%d", message->param.disconnectinfo.location); + vbox_trace_header(this, "CONNECT from VBox (announcement is over)", DIRECTION_IN); end_trace(); - /* recording is close during destruction */ - delete this; - return(-1); /* must return because port is gone */ + new_state(PORT_STATE_CONNECT); } - } else { - if (p_record) - record(buffer, tosend, 0); // from down - message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DATA); - message->param.data.len = tosend; - memcpy(message->param.data.data, buffer, tosend); + } + + /* start recording, if not already */ + if (p_vbox_mode == VBOX_MODE_NORMAL) { + /* recording start */ + open_record(p_vbox_ext.vbox_codec, 2, 0, p_vbox_ext.number, p_vbox_ext.anon_ignore, p_vbox_ext.vbox_email, p_vbox_ext.vbox_email_file); + vbox_trace_header(this, "RECORDING (announcement is over)", DIRECTION_IN); + end_trace(); + } else // else!! + if (p_vbox_mode == VBOX_MODE_ANNOUNCEMENT) { + /* send release */ + message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE); + message->param.disconnectinfo.cause = 16; + message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; message_put(message); + vbox_trace_header(this, "RELEASE from VBox (after annoucement)", DIRECTION_IN); + add_trace("cause", "value", "%d", message->param.disconnectinfo.cause); + add_trace("cause", "location", "%d", message->param.disconnectinfo.location); + end_trace(); + /* recording is close during destruction */ + delete this; + return; /* must return because port is gone */ } + } else { + if (p_record) + record(buffer, tosend, 0); // from down + message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DATA); + message->param.data.len = tosend; + memcpy(message->param.data.data, buffer, tosend); + message_put(message); } - - return(1); } @@ -288,6 +307,7 @@ int VBoxPort::message_epoint(unsigned int epoint_id, int message_id, union param /* play the announcement */ if ((p_vbox_announce_fh = open_tone(filename, &p_vbox_announce_codec, &p_vbox_announce_size, &p_vbox_announce_left)) >= 0) { fhuse++; + schedule_timer(&p_vbox_announce_timer, 0, 300000); } vbox_trace_header(this, "ANNOUNCEMENT", DIRECTION_OUT); add_trace("file", "name", "%s", filename); diff --git a/vbox.h b/vbox.h index b07ab5b..ac9ccfb 100644 --- a/vbox.h +++ b/vbox.h @@ -16,7 +16,7 @@ class VBoxPort : public Port VBoxPort(int type, struct port_settings *settings); ~VBoxPort(); int message_epoint(unsigned int epoint_id, int message, union parameter *param); - int handler(void); + void send_announcement(void); private: struct EndpointAppPBX *p_vbox_apppbx; /* pbx application */ @@ -29,9 +29,10 @@ class VBoxPort : public Port signed int p_vbox_announce_left; /* the number of bytes left of announcement sample */ signed int p_vbox_announce_size; /* size of current announcement (in bytes) */ int p_vbox_mode; /* type of recording VBOX_MODE_* */ - double p_vbox_audio_start; /* time stamp of starting of audio (<1 == not yet started) */ + long long p_vbox_audio_start; /* time stamp of starting of audio (0 == not yet started) */ unsigned int p_vbox_audio_transferred; /* number of samples sent to endpoint */ - signed int p_vbox_record_start; /* start for recording */ + struct lcr_timer p_vbox_announce_timer; /* timer for sending announcement */ + struct lcr_timer p_vbox_record_timeout; /* timer for recording limit */ signed int p_vbox_record_limit; /* limit for recording */ struct extension p_vbox_ext; /* save settings of extension */ -- 2.13.6