Added support for signalling system no. 5.
authorAndreas Eversberg <jolly@eversberg.eu>
Sat, 26 Sep 2009 11:20:29 +0000 (13:20 +0200)
committerAndreas Eversberg <jolly@eversberg.eu>
Sat, 26 Sep 2009 11:20:29 +0000 (13:20 +0200)
More infos will follow on the isdn4linux mailing list.

modified:   Makefile.am
modified:   Makefile.in
modified:   README
modified:   apppbx.cpp
modified:   configure
modified:   configure.ac
modified:   default/options.conf
modified:   dss1.cpp
modified:   ie.cpp
modified:   interface.c
modified:   interface.h
modified:   lcradmin.c
modified:   lcrsocket.h
modified:   mISDN.cpp
modified:   mISDN.h
modified:   main.c
modified:   main.h
modified:   port.h
modified:   socket_server.c
new file:   ss5.cpp
new file:   ss5.h
new file:   ss5_decode.c
new file:   ss5_decode.h
new file:   ss5_encode.c
new file:   ss5_encode.h

25 files changed:
Makefile.am
Makefile.in
README
apppbx.cpp
configure
configure.ac
default/options.conf
dss1.cpp
ie.cpp
interface.c
interface.h
lcradmin.c
lcrsocket.h
mISDN.cpp
mISDN.h
main.c
main.h
port.h
socket_server.c
ss5.cpp [new file with mode: 0644]
ss5.h [new file with mode: 0644]
ss5_decode.c [new file with mode: 0644]
ss5_decode.h [new file with mode: 0644]
ss5_encode.c [new file with mode: 0644]
ss5_encode.h [new file with mode: 0644]

index 84bfd89..bce48d1 100644 (file)
@@ -56,6 +56,14 @@ GSM_LIB = /usr/lib/libgsm.a ./openbsc/src/libbsc.a ./openbsc/src/libmsc.a ./open
 
 endif
 
 
 endif
 
+if ENABLE_SS5
+
+SS5_INCLUDE = -DWITH_SS5
+
+SS5_SOURCE = ss5.cpp ss5_encode.c ss5_decode.c
+
+endif
+
 bin_PROGRAMS = lcradmin gentones genwave
 
 sbin_PROGRAMS = lcr genrc genextension
 bin_PROGRAMS = lcradmin gentones genwave
 
 sbin_PROGRAMS = lcr genrc genextension
@@ -84,9 +92,9 @@ install-exec-hook:
        $(INSTALL) chan_lcr.so $(astmoddir)
 endif
 
        $(INSTALL) chan_lcr.so $(astmoddir)
 endif
 
-INCLUDES = $(all_includes) $(GSM_INCLUDE) -Wall -I/usr/include/mISDNuser $(INSTALLATION_DEFINES)
+INCLUDES = $(all_includes) $(GSM_INCLUDE) $(SS5_INCLUDE) -Wall -I/usr/include/mISDNuser $(INSTALLATION_DEFINES)
 
 
-lcr_SOURCES = $(GSM_SOURCE) action.cpp       mISDN.cpp        tones.c \
+lcr_SOURCES = $(GSM_SOURCE) $(SS5_SOURCE) 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 \
        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 \
index 56f2e2d..efdabb3 100644 (file)
@@ -77,26 +77,29 @@ genwave_OBJECTS = genwave.$(OBJEXT)
 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 \
 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 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
+       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
 @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_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)
-am_lcr_OBJECTS = $(am__objects_1) 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)
+@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)
 lcr_OBJECTS = $(am_lcr_OBJECTS)
 am__DEPENDENCIES_1 =
 @ENABLE_GSM_TRUE@am__DEPENDENCIES_2 = /usr/lib/libgsm.a \
 lcr_OBJECTS = $(am_lcr_OBJECTS)
 am__DEPENDENCIES_1 =
 @ENABLE_GSM_TRUE@am__DEPENDENCIES_2 = /usr/lib/libgsm.a \
@@ -161,6 +164,8 @@ ENABLE_ASTERISK_CHANNEL_DRIVER_FALSE = @ENABLE_ASTERISK_CHANNEL_DRIVER_FALSE@
 ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE = @ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@
 ENABLE_GSM_FALSE = @ENABLE_GSM_FALSE@
 ENABLE_GSM_TRUE = @ENABLE_GSM_TRUE@
 ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE = @ENABLE_ASTERISK_CHANNEL_DRIVER_TRUE@
 ENABLE_GSM_FALSE = @ENABLE_GSM_FALSE@
 ENABLE_GSM_TRUE = @ENABLE_GSM_TRUE@
+ENABLE_SS5_FALSE = @ENABLE_SS5_FALSE@
+ENABLE_SS5_TRUE = @ENABLE_SS5_TRUE@
 EXEEXT = @EXEEXT@
 GREP = @GREP@
 INSTALL_DATA = @INSTALL_DATA@
 EXEEXT = @EXEEXT@
 GREP = @GREP@
 INSTALL_DATA = @INSTALL_DATA@
@@ -246,11 +251,16 @@ INSTALLATION_DEFINES = \
 @ENABLE_GSM_TRUE@GSM_INCLUDE = -DWITH_GSM -I./openbsc/include
 @ENABLE_GSM_TRUE@GSM_SOURCE = gsm_audio.c gsm.cpp gsm_conf.c openbsc/src/bsc_init.c openbsc/src/vty_interface.c openbsc/src/vty_interface_layer3.c
 @ENABLE_GSM_TRUE@GSM_LIB = /usr/lib/libgsm.a ./openbsc/src/libbsc.a ./openbsc/src/libmsc.a ./openbsc/src/libvty.a -ldbi -lcrypt
 @ENABLE_GSM_TRUE@GSM_INCLUDE = -DWITH_GSM -I./openbsc/include
 @ENABLE_GSM_TRUE@GSM_SOURCE = gsm_audio.c gsm.cpp gsm_conf.c openbsc/src/bsc_init.c openbsc/src/vty_interface.c openbsc/src/vty_interface_layer3.c
 @ENABLE_GSM_TRUE@GSM_LIB = /usr/lib/libgsm.a ./openbsc/src/libbsc.a ./openbsc/src/libmsc.a ./openbsc/src/libvty.a -ldbi -lcrypt
+
+#gsm_audio.po: gsm_audio.c gsm_audio.h
+#      $(CC) -D_GNU_SOURCE -fPIC -c gsm_audio.c -o gsm_audio.po
+@ENABLE_SS5_TRUE@SS5_INCLUDE = -DWITH_SS5
+@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_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
-INCLUDES = $(all_includes) $(GSM_INCLUDE) -Wall -I/usr/include/mISDNuser $(INSTALLATION_DEFINES)
-lcr_SOURCES = $(GSM_SOURCE) action.cpp       mISDN.cpp        tones.c \
+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 \
        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 \
        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 \
@@ -437,6 +447,9 @@ distclean-compile:
 @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)/socket_server.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)/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@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss5_encode.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tones.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trace.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vbox.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tones.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trace.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vbox.Po@am__quote@
diff --git a/README b/README
index 5496175..85e8d96 100644 (file)
--- a/README
+++ b/README
@@ -517,8 +517,9 @@ Changes after Version 1.6
 - Fixed bad call/conference bug in joinpbx.c
 - External interfaces must now be specified using 'extern' keyword.
  -> This prevents from selecting other interfaces when dialing out.
 - Fixed bad call/conference bug in joinpbx.c
 - External interfaces must now be specified using 'extern' keyword.
  -> This prevents from selecting other interfaces when dialing out.
- -> Just add 'extern' right below your external interface, or define
+ -> Just add 'extern' right below your external interface definition, or give
     external interface name in routing.conf: ": extern interfaces=XXXXX"
     external interface name in routing.conf: ": extern interfaces=XXXXX"
+- Added experimental CCITT No. 5 signalling system. (for educational purpose)
 
 
 
 
 
 
index 25521dd..06e3d92 100644 (file)
@@ -136,7 +136,7 @@ void EndpointAppPBX::trace_header(const char *name, int direction)
        SCPY(msgtext, name);
 
        /* init trace with given values */
        SCPY(msgtext, name);
 
        /* init trace with given values */
-       start_trace(0,
+       start_trace(-1,
                    NULL,
                    numberrize_callerinfo(e_callerinfo.id, e_callerinfo.ntype, options.national, options.international),
                    e_dialinginfo.id,
                    NULL,
                    numberrize_callerinfo(e_callerinfo.id, e_callerinfo.ntype, options.national, options.international),
                    e_dialinginfo.id,
@@ -648,92 +648,108 @@ foundif:
 
        /* check for channel form selection list */
        *channel = 0;
 
        /* check for channel form selection list */
        *channel = 0;
-       selchannel = ifport->out_channel;
-       while(selchannel) {
-               switch(selchannel->channel) {
-                       case CHANNEL_FREE: /* free channel */
-                       if (mISDNport->b_reserved >= mISDNport->b_num)
-                               break; /* all channel in use or reserverd */
-                       /* find channel */
-                       i = 0;
-                       while(i < mISDNport->b_num) {
-                               if (mISDNport->b_port[i] == NULL) {
-                                       *channel = i+1+(i>=15);
-                                       trace_header("CHANNEL SELECTION (selecting free channel)", DIRECTION_NONE);
-                                       add_trace("port", NULL, "%d", ifport->portnum);
-                                       add_trace("position", NULL, "%d", index);
-                                       add_trace("channel", NULL, "%d", *channel);
-                                       end_trace();
-                                       break;
-                               }
-                               i++;
-                       }
-                       if (*channel)
-                               break;
-                       trace_header("CHANNEL SELECTION (no channel is 'free')", DIRECTION_NONE);
+#ifdef WITH_SS5
+       if (mISDNport->ss5) {
+               class Pss5 *port;
+               port = ss5_hunt_line(mISDNport);
+               if (port) {
+                       *channel = port->p_m_b_channel;
+                       trace_header("CHANNEL SELECTION (selecting SS5 channel)", DIRECTION_NONE);
                        add_trace("port", NULL, "%d", ifport->portnum);
                        add_trace("position", NULL, "%d", index);
                        add_trace("port", NULL, "%d", ifport->portnum);
                        add_trace("position", NULL, "%d", index);
+                       add_trace("channel", NULL, "%d", *channel);
                        end_trace();
                        end_trace();
-                       break;
-
-                       case CHANNEL_ANY: /* don't ask for channel */
-                       if (mISDNport->b_reserved >= mISDNport->b_num) {
-                               trace_header("CHANNEL SELECTION (cannot ask for 'any' channel, all reserved)", DIRECTION_NONE);
+               }
+       } else
+#endif
+       {
+               selchannel = ifport->out_channel;
+               while(selchannel) {
+                       switch(selchannel->channel) {
+                               case CHANNEL_FREE: /* free channel */
+                               if (mISDNport->b_reserved >= mISDNport->b_num)
+                                       break; /* all channel in use or reserverd */
+                               /* find channel */
+                               i = 0;
+                               while(i < mISDNport->b_num) {
+                                       if (mISDNport->b_port[i] == NULL) {
+                                               *channel = i+1+(i>=15);
+                                               trace_header("CHANNEL SELECTION (selecting free channel)", DIRECTION_NONE);
+                                               add_trace("port", NULL, "%d", ifport->portnum);
+                                               add_trace("position", NULL, "%d", index);
+                                               add_trace("channel", NULL, "%d", *channel);
+                                               end_trace();
+                                               break;
+                                       }
+                                       i++;
+                               }
+                               if (*channel)
+                                       break;
+                               trace_header("CHANNEL SELECTION (no channel is 'free')", DIRECTION_NONE);
                                add_trace("port", NULL, "%d", ifport->portnum);
                                add_trace("position", NULL, "%d", index);
                                add_trace("port", NULL, "%d", ifport->portnum);
                                add_trace("position", NULL, "%d", index);
-                               add_trace("total", NULL, "%d", mISDNport->b_num);
-                               add_trace("reserved", NULL, "%d", mISDNport->b_reserved);
                                end_trace();
                                end_trace();
-                               break; /* all channel in use or reserverd */
-                       }
-                       trace_header("CHANNEL SELECTION (using 'any' channel)", DIRECTION_NONE);
-                       add_trace("port", NULL, "%d", ifport->portnum);
-                       add_trace("position", NULL, "%d", index);
-                       end_trace();
-                       *channel = CHANNEL_ANY;
-                       break;
-
-                       case CHANNEL_NO: /* call waiting */
-                       trace_header("CHANNEL SELECTION (using 'no' channel, call-waiting)", DIRECTION_NONE);
-                       add_trace("port", NULL, "%d", ifport->portnum);
-                       add_trace("position", NULL, "%d", index);
-                       end_trace();
-                       *channel = CHANNEL_NO;
-                       break;
+                               break;
 
 
-                       default:
-                       if (selchannel->channel<1 || selchannel->channel==16) {
-                               trace_header("CHANNEL SELECTION (channel out of range)", DIRECTION_NONE);
-                               add_trace("port", NULL, "%d", ifport->portnum);
-                               add_trace("position", NULL, "%d", index);
-                               add_trace("channel", NULL, "%d", selchannel->channel);
-                               end_trace();
-                               break; /* invalid channels */
-                       }
-                       i = selchannel->channel-1-(selchannel->channel>=17);
-                       if (i >= mISDNport->b_num) {
-                               trace_header("CHANNEL SELECTION (channel out of range)", DIRECTION_NONE);
+                               case CHANNEL_ANY: /* don't ask for channel */
+                               if (mISDNport->b_reserved >= mISDNport->b_num) {
+                                       trace_header("CHANNEL SELECTION (cannot ask for 'any' channel, all reserved)", DIRECTION_NONE);
+                                       add_trace("port", NULL, "%d", ifport->portnum);
+                                       add_trace("position", NULL, "%d", index);
+                                       add_trace("total", NULL, "%d", mISDNport->b_num);
+                                       add_trace("reserved", NULL, "%d", mISDNport->b_reserved);
+                                       end_trace();
+                                       break; /* all channel in use or reserverd */
+                               }
+                               trace_header("CHANNEL SELECTION (using 'any' channel)", DIRECTION_NONE);
                                add_trace("port", NULL, "%d", ifport->portnum);
                                add_trace("position", NULL, "%d", index);
                                add_trace("port", NULL, "%d", ifport->portnum);
                                add_trace("position", NULL, "%d", index);
-                               add_trace("channel", NULL, "%d", selchannel->channel);
-                               add_trace("channels", NULL, "%d", mISDNport->b_num);
                                end_trace();
                                end_trace();
-                               break; /* channel not in port */
-                       }
-                       if (mISDNport->b_port[i] == NULL) {
-                               *channel = selchannel->channel;
-                               trace_header("CHANNEL SELECTION (selecting given channel)", DIRECTION_NONE);
+                               *channel = CHANNEL_ANY;
+                               break;
+
+                               case CHANNEL_NO: /* call waiting */
+                               trace_header("CHANNEL SELECTION (using 'no' channel, call-waiting)", DIRECTION_NONE);
                                add_trace("port", NULL, "%d", ifport->portnum);
                                add_trace("position", NULL, "%d", index);
                                add_trace("port", NULL, "%d", ifport->portnum);
                                add_trace("position", NULL, "%d", index);
-                               add_trace("channel", NULL, "%d", *channel);
                                end_trace();
                                end_trace();
+                               *channel = CHANNEL_NO;
+                               break;
+
+                               default:
+                               if (selchannel->channel<1 || selchannel->channel==16) {
+                                       trace_header("CHANNEL SELECTION (channel out of range)", DIRECTION_NONE);
+                                       add_trace("port", NULL, "%d", ifport->portnum);
+                                       add_trace("position", NULL, "%d", index);
+                                       add_trace("channel", NULL, "%d", selchannel->channel);
+                                       end_trace();
+                                       break; /* invalid channels */
+                               }
+                               i = selchannel->channel-1-(selchannel->channel>=17);
+                               if (i >= mISDNport->b_num) {
+                                       trace_header("CHANNEL SELECTION (channel out of range)", DIRECTION_NONE);
+                                       add_trace("port", NULL, "%d", ifport->portnum);
+                                       add_trace("position", NULL, "%d", index);
+                                       add_trace("channel", NULL, "%d", selchannel->channel);
+                                       add_trace("channels", NULL, "%d", mISDNport->b_num);
+                                       end_trace();
+                                       break; /* channel not in port */
+                               }
+                               if (mISDNport->b_port[i] == NULL) {
+                                       *channel = selchannel->channel;
+                                       trace_header("CHANNEL SELECTION (selecting given channel)", DIRECTION_NONE);
+                                       add_trace("port", NULL, "%d", ifport->portnum);
+                                       add_trace("position", NULL, "%d", index);
+                                       add_trace("channel", NULL, "%d", *channel);
+                                       end_trace();
+                                       break;
+                               }
                                break;
                        }
                                break;
                        }
-                       break;
+                       if (*channel)
+                               break; /* found channel */
+                       selchannel = selchannel->next;
                }
                }
-               if (*channel)
-                       break; /* found channel */
-               selchannel = selchannel->next;
        }
 
        /* if channel was found, return mISDNport and channel */
        }
 
        /* if channel was found, return mISDNport and channel */
@@ -921,6 +937,11 @@ void EndpointAppPBX::out_setup(void)
                        }
                        /* creating INTERNAL port */
                        SPRINT(portname, "%s-%d-out", mISDNport->ifport->interface->name, mISDNport->portnum);
                        }
                        /* creating INTERNAL port */
                        SPRINT(portname, "%s-%d-out", mISDNport->ifport->interface->name, mISDNport->portnum);
+#ifdef WITH_SS5
+                       if (mISDNport->ss5)
+                               port = ss5_hunt_line(mISDNport);
+                       else
+#endif
                        if (!mISDNport->gsm)
                                port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode);
                        else
                        if (!mISDNport->gsm)
                                port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode);
                        else
@@ -1034,6 +1055,11 @@ void EndpointAppPBX::out_setup(void)
                                if (mISDNport) {
                                        /* creating EXTERNAL port*/
                                        SPRINT(portname, "%s-%d-out", mISDNport->ifport->interface->name, mISDNport->portnum);
                                if (mISDNport) {
                                        /* creating EXTERNAL port*/
                                        SPRINT(portname, "%s-%d-out", mISDNport->ifport->interface->name, mISDNport->portnum);
+#ifdef WITH_SS5
+                                       if (mISDNport->ss5)
+                                               port = ss5_hunt_line(mISDNport);
+                                       else
+#endif
                                                port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode);
                                        if (!port)
                                                FATAL("No memory for Port instance\n");
                                                port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode);
                                        if (!port)
                                                FATAL("No memory for Port instance\n");
@@ -1124,6 +1150,11 @@ void EndpointAppPBX::out_setup(void)
                        }
                        /* creating EXTERNAL port*/
                        SPRINT(portname, "%s-%d-out", mISDNport->ifport->interface->name, mISDNport->portnum);
                        }
                        /* creating EXTERNAL port*/
                        SPRINT(portname, "%s-%d-out", mISDNport->ifport->interface->name, mISDNport->portnum);
+#ifdef WITH_SS5
+                       if (mISDNport->ss5)
+                               port = ss5_hunt_line(mISDNport);
+                       else
+#endif
                        if (!mISDNport->gsm)
                                port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode);
                        else
                        if (!mISDNport->gsm)
                                port = new Pdss1((mISDNport->ntmode)?PORT_TYPE_DSS1_NT_OUT:PORT_TYPE_DSS1_TE_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode);
                        else
index a924db7..d9875f8 100755 (executable)
--- a/configure
+++ b/configure
@@ -640,6 +640,8 @@ ac_includes_default="\
 ac_subst_vars='LTLIBOBJS
 POW_LIB
 LIBOBJS
 ac_subst_vars='LTLIBOBJS
 POW_LIB
 LIBOBJS
+ENABLE_SS5_FALSE
+ENABLE_SS5_TRUE
 ENABLE_GSM_FALSE
 ENABLE_GSM_TRUE
 LIBCRYPTO
 ENABLE_GSM_FALSE
 ENABLE_GSM_TRUE
 LIBCRYPTO
@@ -735,6 +737,7 @@ enable_dependency_tracking
 with_asterisk
 with_ssl
 with_gsm
 with_asterisk
 with_ssl
 with_gsm
+with_ss5
 '
       ac_precious_vars='build_alias
 host_alias
 '
       ac_precious_vars='build_alias
 host_alias
@@ -1385,7 +1388,9 @@ Optional Packages:
 
   --with-ssl              compile with ssl support (libcrypto) [default=check]
 
 
   --with-ssl              compile with ssl support (libcrypto) [default=check]
 
-  --with-gsm              compile with OpenBSC support [default=no]
+  --with-gsm              compile with OpenBSC support [default=check]
+
+  --with-ss5              compile with CCITT No. 5 support [default=no]
 
 
 Some influential environment variables:
 
 
 Some influential environment variables:
@@ -6316,6 +6321,27 @@ else
 fi
 
 
 fi
 
 
+# check for ss5
+
+# Check whether --with-ss5 was given.
+if test "${with_ss5+set}" = set; then
+  withval=$with_ss5;
+else
+  with_ss5="check"
+fi
+
+
+
+
+if test "x$with_ss5" == "xyes" ; then
+  ENABLE_SS5_TRUE=
+  ENABLE_SS5_FALSE='#'
+else
+  ENABLE_SS5_TRUE='#'
+  ENABLE_SS5_FALSE=
+fi
+
+
 # Checks for libraries.
 
 { $as_echo "$as_me:$LINENO: checking for main in -lm" >&5
 # Checks for libraries.
 
 { $as_echo "$as_me:$LINENO: checking for main in -lm" >&5
@@ -9826,6 +9852,13 @@ $as_echo "$as_me: error: conditional \"ENABLE_GSM\" was never defined.
 Usually this means the macro was only invoked conditionally." >&2;}
    { (exit 1); exit 1; }; }
 fi
 Usually this means the macro was only invoked conditionally." >&2;}
    { (exit 1); exit 1; }; }
 fi
+if test -z "${ENABLE_SS5_TRUE}" && test -z "${ENABLE_SS5_FALSE}"; then
+  { { $as_echo "$as_me:$LINENO: error: conditional \"ENABLE_SS5\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+$as_echo "$as_me: error: conditional \"ENABLE_SS5\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
 
 : ${CONFIG_STATUS=./config.status}
 ac_write_fail=0
 
 : ${CONFIG_STATUS=./config.status}
 ac_write_fail=0
index eaa0af5..494b352 100644 (file)
@@ -98,7 +98,7 @@ AS_IF([test "x$with_ssl" != xno],
 # check for gsm
 AC_ARG_WITH([gsm],
        [AS_HELP_STRING([--with-gsm],
 # check for gsm
 AC_ARG_WITH([gsm],
        [AS_HELP_STRING([--with-gsm],
-                       [compile with OpenBSC support @<:@default=no@:>@])
+                       [compile with OpenBSC support @<:@default=check@:>@])
        ],
        [],
        [with_gsm="check"])
        ],
        [],
        [with_gsm="check"])
@@ -115,6 +115,16 @@ AS_IF([test "x$with_gsm" != xno],
 
 AM_CONDITIONAL(ENABLE_GSM, test "x$with_gsm" == "xyes" )
 
 
 AM_CONDITIONAL(ENABLE_GSM, test "x$with_gsm" == "xyes" )
 
+# check for ss5
+AC_ARG_WITH([ss5],
+       [AS_HELP_STRING([--with-ss5],
+                       [compile with CCITT No. 5 support @<:@default=no@:>@])
+       ],
+       [],
+       [with_ss5="check"])
+
+AM_CONDITIONAL(ENABLE_SS5, test "x$with_ss5" == "xyes" )
+
 # Checks for libraries.
 AC_CHECK_LIB([m], [main])
 AC_CHECK_LIB([ncurses], [main])
 # Checks for libraries.
 AC_CHECK_LIB([m], [main])
 AC_CHECK_LIB([ncurses], [main])
index 4b77211..4875773 100644 (file)
@@ -12,6 +12,7 @@
 #define DEBUG_PORT     0x0100
 #define DEBUG_ISDN     0x0110
 #define DEBUG_GSM      0x0120
 #define DEBUG_PORT     0x0100
 #define DEBUG_ISDN     0x0110
 #define DEBUG_GSM      0x0120
+#define DEBUG_SS5      0x0130
 #define DEBUG_VBOX     0x0180
 #define DEBUG_EPOINT   0x0200
 #define DEBUG_JOIN     0x0400
 #define DEBUG_VBOX     0x0180
 #define DEBUG_EPOINT   0x0200
 #define DEBUG_JOIN     0x0400
index a5d32bd..1cb52d1 100644 (file)
--- a/dss1.cpp
+++ b/dss1.cpp
@@ -1900,8 +1900,6 @@ void Pdss1::message_information(unsigned int epoint_id, int message_id, union pa
 }
 
 
 }
 
 
-int newteid = 0;
-
 /* MESSAGE_SETUP */
 void Pdss1::message_setup(unsigned int epoint_id, int message_id, union parameter *param)
 {
 /* MESSAGE_SETUP */
 void Pdss1::message_setup(unsigned int epoint_id, int message_id, union parameter *param)
 {
diff --git a/ie.cpp b/ie.cpp
index 933287e..ddb6c1b 100644 (file)
--- a/ie.cpp
+++ b/ie.cpp
@@ -616,7 +616,7 @@ void Pdss1::enc_ie_cause(struct l3_msg *l3m, int location, int cause)
        unsigned char p[256];
        int l;
 
        unsigned char p[256];
        int l;
 
-       if (location<0 || location>7) {
+       if (location<0 || location>10) {
                PERROR("location(%d) is out of range.\n", location);
                return;
        }
                PERROR("location(%d) is out of range.\n", location);
                return;
        }
index df39b78..42f409c 100644 (file)
@@ -916,6 +916,56 @@ static int inter_gsm(struct interface *interface, char *filename, int line, char
        return(0);
 #endif
 }
        return(0);
 #endif
 }
+#ifdef WITH_SS5
+static int inter_ss5(struct interface *interface, char *filename, int line, char *parameter, char *value)
+{
+       struct interface_port *ifport;
+       char *element;
+
+       /* port in chain ? */
+       if (!interface->ifport) {
+               SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects previous 'port' definition.\n", filename, line, parameter);
+               return(-1);
+       }
+       /* goto end of chain */
+       ifport = interface->ifport;
+       while(ifport->next)
+               ifport = ifport->next;
+       ifport->ss5 |= SS5_ENABLE;
+       while((element = strsep(&value, " "))) {
+               if (element[0] == '\0')
+                       continue;
+               if (!strcasecmp(element, "connect"))
+                       ifport->ss5 |= SS5_FEATURE_CONNECT;
+               else
+               if (!strcasecmp(element, "nodisconnect"))
+                       ifport->ss5 |= SS5_FEATURE_NODISCONNECT;
+               else
+               if (!strcasecmp(element, "releaseguardtimer"))
+                       ifport->ss5 |= SS5_FEATURE_RELEASEGUARDTIMER;
+               else
+               if (!strcasecmp(element, "bell"))
+                       ifport->ss5 |= SS5_FEATURE_BELL;
+               else
+               if (!strcasecmp(element, "pulsedialing"))
+                       ifport->ss5 |= SS5_FEATURE_PULSEDIALING;
+               else
+               if (!strcasecmp(element, "delay"))
+                       ifport->ss5 |= SS5_FEATURE_DELAY;
+               else
+               if (!strcasecmp(element, "starrelease"))
+                       ifport->ss5 |= SS5_FEATURE_STAR_RELEASE;
+               else
+               if (!strcasecmp(element, "suppress"))
+                       ifport->ss5 |= SS5_FEATURE_SUPPRESS;
+               else {
+                       SPRINT(interface_error, "Error in %s (line %d): parameter '%s' does not allow value element '%s'.\n", filename, line, parameter, element);
+                       return(-1);
+               }
+       }
+       return(0);
+}
+#endif
 
 
 /*
 
 
 /*
@@ -1049,6 +1099,22 @@ struct interface_param interface_param[] = {
        "This interface must be a loopback interface. The second loopback interface\n"
        "must be assigned to OpenBSC"},
 
        "This interface must be a loopback interface. The second loopback interface\n"
        "must be assigned to OpenBSC"},
 
+#ifdef WITH_SS5
+       {"ccitt5", &inter_ss5, "[<feature> [feature ...]]",
+       "Interface uses CCITT No. 5 inband signalling rather than D-channel.\n"
+       "This feature causes CPU load to rise and has no practical intend.\n"
+       "If you don't know what it is, you don't need it.\n"
+       "Features apply to protocol behaviour and blueboxing specials, they are:\n"
+       " connect - Connect incomming call to throughconnect audio, if required.\n"
+       " nodisconnect - Don't disconnect if incomming exchange disconnects.\n"
+       " releaseguardtimer - Tries to prevent Blueboxing by a longer release-guard.\n"
+       " bell - Allow releasing and pulse-dialing via 2600 Hz like old Bell systems.\n"
+       " pulsedialing - Use pulse dialing on outgoing exchange. (takes long!)\n"
+       " delay - Use on incomming exchange, to make you feel a delay when blueboxing.\n"
+       " starrelease - Pulse dialing a star (11 pulses per digit) clears current call.\n"
+       " suppress - Suppress received tones, as they will be recognized."},
+#endif
+
        {NULL, NULL, NULL, NULL}
 };
 
        {NULL, NULL, NULL, NULL}
 };
 
@@ -1275,12 +1341,12 @@ static void set_defaults(struct interface_port *ifport)
        if (ifport->interface->is_tones)
                ifport->mISDNport->tones = (ifport->interface->is_tones==IS_YES);
        else
        if (ifport->interface->is_tones)
                ifport->mISDNport->tones = (ifport->interface->is_tones==IS_YES);
        else
-               ifport->mISDNport->tones = (ifport->mISDNport->ntmode)?1:0;
+               ifport->mISDNport->tones = (ifport->mISDNport->ntmode || ifport->mISDNport->ss5)?1:0;
        /* default is_earlyb */
        if (ifport->interface->is_earlyb)
                ifport->mISDNport->earlyb = (ifport->interface->is_earlyb==IS_YES);
        else
        /* default is_earlyb */
        if (ifport->interface->is_earlyb)
                ifport->mISDNport->earlyb = (ifport->interface->is_earlyb==IS_YES);
        else
-               ifport->mISDNport->earlyb = (ifport->mISDNport->ntmode)?0:1;
+               ifport->mISDNport->earlyb = (ifport->mISDNport->ntmode && !ifport->mISDNport->ss5)?0:1;
        /* set locally flag */
        if (ifport->interface->extension)
                ifport->mISDNport->locally = 1;
        /* set locally flag */
        if (ifport->interface->extension)
                ifport->mISDNport->locally = 1;
@@ -1366,7 +1432,7 @@ void load_port(struct interface_port *ifport)
        struct mISDNport *mISDNport;
 
        /* open new port */
        struct mISDNport *mISDNport;
 
        /* open new port */
-       mISDNport = mISDNport_open(ifport->portnum, ifport->portname, ifport->ptp, ifport->nt, ifport->tespecial, ifport->l1hold, ifport->l2hold, ifport->interface, ifport->gsm);
+       mISDNport = mISDNport_open(ifport->portnum, ifport->portname, ifport->ptp, ifport->nt, ifport->tespecial, ifport->l1hold, ifport->l2hold, ifport->interface, ifport->gsm, ifport->ss5);
        if (mISDNport) {
                /* link port */
                ifport->mISDNport = mISDNport;
        if (mISDNport) {
                /* link port */
                ifport->mISDNport = mISDNport;
@@ -1376,6 +1442,8 @@ void load_port(struct interface_port *ifport)
                SCPY(ifport->portname, mISDNport->name);
                /* set defaults */
                set_defaults(ifport);
                SCPY(ifport->portname, mISDNport->name);
                /* set defaults */
                set_defaults(ifport);
+               /* load static port instances */
+               mISDNport_static(mISDNport);
        } else {
                ifport->block = 2; /* not available */
        }
        } else {
                ifport->block = 2; /* not available */
        }
index 2c2ee23..508a33e 100644 (file)
@@ -52,6 +52,7 @@ struct interface_port {
        int                     l1hold; /* hold layer 1 (1=on, 0=off) */
        int                     l2hold; /* hold layer 2 (1=force, -1=disable, 0=default) */
        int                     gsm; /* interface is an GSM interface */
        int                     l1hold; /* hold layer 1 (1=on, 0=off) */
        int                     l2hold; /* hold layer 2 (1=force, -1=disable, 0=default) */
        int                     gsm; /* interface is an GSM interface */
+       unsigned int            ss5; /* set, if SS5 signalling enabled, also holds feature bits */
        int                     channel_force; /* forces channel by protocol */
        int                     nodtmf; /* disables DTMF */
        struct select_channel   *out_channel; /* list of channels to select */
        int                     channel_force; /* forces channel by protocol */
        int                     nodtmf; /* disables DTMF */
        struct select_channel   *out_channel; /* list of channels to select */
index 0138de9..222700b 100644 (file)
@@ -179,6 +179,10 @@ int debug_port(struct admin_message *msg, struct admin_message *m, int line, int
                color(blue);
                addstr("'out >> disc'");
                break;
                color(blue);
                addstr("'out >> disc'");
                break;
+               case ADMIN_STATE_RELEASE:
+               color(blue);
+               addstr("'release'");
+               break;
                default:
                color(blue);
                addstr("'--NONE--'");
                default:
                color(blue);
                addstr("'--NONE--'");
@@ -729,7 +733,7 @@ const char *admin_state(int sock, char *argv[])
                                                                break;
                                                                case B_STATE_ACTIVE:
                                                                color(green);
                                                                break;
                                                                case B_STATE_ACTIVE:
                                                                color(green);
-                                                               addstr("busy    ");
+                                                               addstr("active  ");
                                                                break;
                                                                case B_STATE_DEACTIVATING:
                                                                color(yellow);
                                                                break;
                                                                case B_STATE_DEACTIVATING:
                                                                color(yellow);
index edb882b..43543c1 100644 (file)
@@ -188,4 +188,5 @@ enum {
        ADMIN_STATE_CONNECT,
        ADMIN_STATE_IN_DISCONNECT,
        ADMIN_STATE_OUT_DISCONNECT,
        ADMIN_STATE_CONNECT,
        ADMIN_STATE_IN_DISCONNECT,
        ADMIN_STATE_OUT_DISCONNECT,
+       ADMIN_STATE_RELEASE,
 };
 };
index 5f55a36..c92e427 100644 (file)
--- a/mISDN.cpp
+++ b/mISDN.cpp
@@ -108,12 +108,15 @@ PmISDN::PmISDN(int type, mISDNport *mISDNport, char *portname, struct port_setti
        p_m_tx_gain = mISDNport->ifport->interface->tx_gain;
        p_m_rx_gain = mISDNport->ifport->interface->rx_gain;
        p_m_conf = 0;
        p_m_tx_gain = mISDNport->ifport->interface->tx_gain;
        p_m_rx_gain = mISDNport->ifport->interface->rx_gain;
        p_m_conf = 0;
+       p_m_mute = 0;
        p_m_txdata = 0;
        p_m_delay = 0;
        p_m_echo = 0;
        p_m_tone = 0;
        p_m_rxoff = 0;
        p_m_joindata = 0;
        p_m_txdata = 0;
        p_m_delay = 0;
        p_m_echo = 0;
        p_m_tone = 0;
        p_m_rxoff = 0;
        p_m_joindata = 0;
+       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;
        p_m_dtmf = !mISDNport->ifport->nodtmf;
        p_m_timeout = 0;
        p_m_timer = 0;
@@ -159,6 +162,7 @@ PmISDN::PmISDN(int type, mISDNport *mISDNport, char *portname, struct port_setti
        /* we increase the number of objects: */
        mISDNport->use++;
        PDEBUG(DEBUG_ISDN, "Created new mISDNPort(%s). Currently %d objects use, port #%d\n", portname, mISDNport->use, p_m_portnum);
        /* we increase the number of objects: */
        mISDNport->use++;
        PDEBUG(DEBUG_ISDN, "Created new mISDNPort(%s). Currently %d objects use, port #%d\n", portname, mISDNport->use, p_m_portnum);
+//inband_receive_on();
 }
 
 
 }
 
 
@@ -464,7 +468,7 @@ static void _bchannel_configure(struct mISDNport *mISDNport, int i)
                ph_control(mISDNport, port, handle, DSP_VOL_CHANGE_RX, port->p_m_rx_gain, "DSP-RX_GAIN", port->p_m_rx_gain);
        if (port->p_m_pipeline[0] && mode == B_MODE_TRANSPARENT)
                ph_control_block(mISDNport, port, handle, DSP_PIPELINE_CFG, port->p_m_pipeline, strlen(port->p_m_pipeline)+1, "DSP-PIPELINE", 0);
                ph_control(mISDNport, port, handle, DSP_VOL_CHANGE_RX, port->p_m_rx_gain, "DSP-RX_GAIN", port->p_m_rx_gain);
        if (port->p_m_pipeline[0] && mode == B_MODE_TRANSPARENT)
                ph_control_block(mISDNport, port, handle, DSP_PIPELINE_CFG, port->p_m_pipeline, strlen(port->p_m_pipeline)+1, "DSP-PIPELINE", 0);
-       if (port->p_m_conf)
+       if (port->p_m_conf && !port->p_m_mute)
                ph_control(mISDNport, port, handle, DSP_CONF_JOIN, port->p_m_conf, "DSP-CONF", port->p_m_conf);
        if (port->p_m_echo)
                ph_control(mISDNport, port, handle, DSP_ECHO_ON, 0, "DSP-ECHO", 1);
                ph_control(mISDNport, port, handle, DSP_CONF_JOIN, port->p_m_conf, "DSP-CONF", port->p_m_conf);
        if (port->p_m_echo)
                ph_control(mISDNport, port, handle, DSP_ECHO_ON, 0, "DSP-ECHO", 1);
@@ -480,6 +484,19 @@ static void _bchannel_configure(struct mISDNport *mISDNport, int i)
                ph_control_block(mISDNport, port, handle, DSP_BF_ENABLE_KEY, port->p_m_crypt_key, port->p_m_crypt_key_len, "DSP-CRYPT", port->p_m_crypt_key_len);
 }
 
                ph_control_block(mISDNport, port, handle, DSP_BF_ENABLE_KEY, port->p_m_crypt_key, port->p_m_crypt_key_len, "DSP-CRYPT", port->p_m_crypt_key_len);
 }
 
+
+void PmISDN::set_conf(int oldconf, int newconf)
+{
+               if (oldconf != 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);
+               } else
+                       PDEBUG(DEBUG_BCHANNEL, "we already have conf=%d.\n", newconf);
+}
+
+
 /*
  * subfunction for bchannel_event
  * destroy stack
 /*
  * subfunction for bchannel_event
  * destroy stack
@@ -1218,15 +1235,20 @@ int PmISDN::handler(void)
                else
                        p_m_load = 0;
 
                else
                        p_m_load = 0;
 
-               /* to send data, tone must be active OR crypt messages must be on */
-               if ((p_tone_name[0] || p_m_crypt_msg_loops)
-                && (p_m_load < ISDN_LOAD)
-                && (p_state==PORT_STATE_CONNECT || p_m_mISDNport->tones)) {
+               /* 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_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];
                        struct mISDNhead *frm = (struct mISDNhead *)buf;
                        unsigned char *p = buf+MISDN_HEADER_LEN;
 
                        int tosend = ISDN_LOAD - p_m_load, length; 
                        unsigned char buf[MISDN_HEADER_LEN+tosend];
                        struct mISDNhead *frm = (struct mISDNhead *)buf;
                        unsigned char *p = buf+MISDN_HEADER_LEN;
 
+                       /* copy inband signalling (e.g. used by ss5) */
+                       if (p_m_inband_send_on && tosend) {
+                               tosend -= inband_send(p, tosend);
+                       }
+
                        /* copy crypto loops */
                        while (p_m_crypt_msg_loops && tosend) {
                                /* how much do we have to send */
                        /* copy crypto loops */
                        while (p_m_crypt_msg_loops && tosend) {
                                /* how much do we have to send */
@@ -1372,6 +1394,10 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
                return;
        }
 
                return;
        }
 
+       /* inband is processed */
+       if (p_m_inband_receive_on)
+               inband_receive(data, len);
+
        /* calls will not process any audio data unless
         * the call is connected OR tones feature is enabled.
         */
        /* calls will not process any audio data unless
         * the call is connected OR tones feature is enabled.
         */
@@ -1566,6 +1592,7 @@ void PmISDN::set_tone(const char *dir, const char *tone)
 //extern struct lcr_msg *dddebug;
 void PmISDN::message_mISDNsignal(unsigned int epoint_id, int message_id, union parameter *param)
 {
 //extern struct lcr_msg *dddebug;
 void PmISDN::message_mISDNsignal(unsigned int epoint_id, int message_id, union parameter *param)
 {
+       int oldconf, newconf;
        switch(param->mISDNsignal.message) {
                case mISDNSIGNAL_VOLUME:
                if (p_m_tx_gain != param->mISDNsignal.tx_gain) {
        switch(param->mISDNsignal.message) {
                case mISDNSIGNAL_VOLUME:
                if (p_m_tx_gain != param->mISDNsignal.tx_gain) {
@@ -1587,19 +1614,10 @@ void PmISDN::message_mISDNsignal(unsigned int epoint_id, int message_id, union p
                break;
 
                case mISDNSIGNAL_CONF:
                break;
 
                case mISDNSIGNAL_CONF:
-//if (dddebug) PDEBUG(DEBUG_ISDN, "dddebug = %d\n", dddebug->type);
-//tone         if (!p_m_tone && p_m_conf!=param->mISDNsignal.conf)
-               if (p_m_conf != param->mISDNsignal.conf) {
-                       p_m_conf = param->mISDNsignal.conf;
-                       PDEBUG(DEBUG_BCHANNEL, "we change conference to conf=%d.\n", p_m_conf);
-                       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], (p_m_conf)?DSP_CONF_JOIN:DSP_CONF_SPLIT, p_m_conf, "DSP-CONF", p_m_conf);
-               } else
-                       PDEBUG(DEBUG_BCHANNEL, "we already have conf=%d.\n", p_m_conf);
-               /* we must set, even if currently tone forbids conf */
+               oldconf = p_m_mute?0:p_m_conf;
                p_m_conf = param->mISDNsignal.conf;
                p_m_conf = param->mISDNsignal.conf;
-//if (dddebug) PDEBUG(DEBUG_ISDN, "dddebug = %d\n", dddebug->type);
+               newconf = p_m_mute?0:p_m_conf;
+               set_conf(oldconf, newconf);
                break;
 
                case mISDNSIGNAL_JOINDATA:
                break;
 
                case mISDNSIGNAL_JOINDATA:
@@ -1744,7 +1762,7 @@ int mISDN_handler(void)
                        isdnport=mISDNport->b_port[i];
                        if (isdnport) {
                                /* call bridges in user space OR crypto OR recording */
                        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) {
+                               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 */
                                        /* rx IS required */
                                        if (isdnport->p_m_rxoff) {
                                                /* turn on RX */
@@ -1947,7 +1965,7 @@ int mISDN_handler(void)
                                mISDNport->l2establish = 0;
                                if (!mISDNport->gsm && mISDNport->l2hold && (mISDNport->ptp || !mISDNport->ntmode)) {
 
                                mISDNport->l2establish = 0;
                                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);
+//                                     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);
                                        mISDNport->ml3->to_layer3(mISDNport->ml3, MT_L2ESTABLISH, 0, NULL);
                                        time(&mISDNport->l2establish);
                                        return(1);
@@ -2027,7 +2045,7 @@ int mISDN_getportbyname(int sock, int cnt, char *portname)
 /*
  * global function to add a new card (port)
  */
 /*
  * global function to add a new card (port)
  */
-struct mISDNport *mISDNport_open(int port, char *portname, int ptp, int force_nt, int te_special, int l1hold, int l2hold, struct interface *interface, int gsm)
+struct mISDNport *mISDNport_open(int port, char *portname, int ptp, int force_nt, int te_special, int l1hold, int l2hold, struct interface *interface, int gsm, unsigned int ss5)
 {
        int ret;
        struct mISDNport *mISDNport, **mISDNportp;
 {
        int ret;
        struct mISDNport *mISDNport, **mISDNportp;
@@ -2168,8 +2186,8 @@ struct mISDNport *mISDNport_open(int port, char *portname, int ptp, int force_nt
        while(*mISDNportp)
                mISDNportp = &((*mISDNportp)->next);
        mISDNport = (struct mISDNport *)MALLOC(sizeof(struct mISDNport));
        while(*mISDNportp)
                mISDNportp = &((*mISDNportp)->next);
        mISDNport = (struct mISDNport *)MALLOC(sizeof(struct mISDNport));
-       if (gsm) {
-               /* gsm audio is always active */
+       if (gsm | ss5) {
+               /* gsm/ss5 link is always active */
                mISDNport->l1link = 1;
                mISDNport->l2link = 1;
        } else {
                mISDNport->l1link = 1;
                mISDNport->l2link = 1;
        } else {
@@ -2183,7 +2201,13 @@ struct mISDNport *mISDNport_open(int port, char *portname, int ptp, int force_nt
        /* if pri, must set PTP */
        if (pri)
                ptp = 1;
        /* if pri, must set PTP */
        if (pri)
                ptp = 1;
-       
+
+       /* set ss5 params */
+       if (ss5) {
+               /* try to keep interface enabled */
+               l1hold = 1;
+               l2hold = 1;
+       }
        /* set l2hold */
        switch (l2hold) {
                case -1: // off
        /* set l2hold */
        switch (l2hold) {
                case -1: // off
@@ -2273,6 +2297,7 @@ struct mISDNport *mISDNport_open(int port, char *portname, int ptp, int force_nt
        mISDNport->ptp = ptp;
        mISDNport->l1hold = l1hold;
        mISDNport->l2hold = l2hold;
        mISDNport->ptp = ptp;
        mISDNport->l1hold = l1hold;
        mISDNport->l2hold = l2hold;
+       mISDNport->ss5 = ss5;
        PDEBUG(DEBUG_ISDN, "Port has %d b-channels.\n", mISDNport->b_num);
        i = 0;
        while(i < mISDNport->b_num) {
        PDEBUG(DEBUG_ISDN, "Port has %d b-channels.\n", mISDNport->b_num);
        i = 0;
        while(i < mISDNport->b_num) {
@@ -2306,12 +2331,33 @@ struct mISDNport *mISDNport_open(int port, char *portname, int ptp, int force_nt
                    "PORT (open)");
        add_trace("mode", NULL, (mISDNport->ntmode)?"network":"terminal");
        add_trace("channels", NULL, "%d", mISDNport->b_num);
                    "PORT (open)");
        add_trace("mode", NULL, (mISDNport->ntmode)?"network":"terminal");
        add_trace("channels", NULL, "%d", mISDNport->b_num);
+       if (mISDNport->ss5)
+               add_trace("ccitt#5", NULL, "enabled");
        end_trace();
        end_trace();
+
        return(mISDNport);
 }
 
 
 /*
        return(mISDNport);
 }
 
 
 /*
+ * load static port instances, if required by mISDNport
+ */
+void mISDNport_static(struct mISDNport *mISDNport)
+{
+       int i;
+
+       i = 0;
+       while(i < mISDNport->b_num) {
+#ifdef WITH_SS5
+               if (mISDNport->ss5)
+                       ss5_create_channel(mISDNport, i);
+#endif
+               i++;
+       }
+}
+
+
+/*
  * function to free ALL cards (ports)
  */
 void mISDNport_close_all(void)
  * function to free ALL cards (ports)
  */
 void mISDNport_close_all(void)
@@ -2418,7 +2464,7 @@ void PmISDN::txfromup(unsigned char *data, int length)
        /* check if high priority tones exist
         * ignore data in this case
         */
        /* check if high priority tones exist
         * ignore data in this case
         */
-       if (p_tone_name[0] || p_m_crypt_msg_loops)
+       if (p_tone_name[0] || p_m_crypt_msg_loops || p_m_inband_send_on)
                return;
 
        /* preload procedure
                return;
 
        /* preload procedure
@@ -2451,3 +2497,61 @@ void PmISDN::txfromup(unsigned char *data, int length)
        p_m_load += length;
 }
 
        p_m_load += length;
 }
 
+int PmISDN::inband_send(unsigned char *buffer, int len)
+{
+       PERROR("this function must be derived to function!\n");
+       return 0;
+}
+
+void PmISDN::inband_send_on(void)
+{
+       PDEBUG(DEBUG_PORT, "turning inband signalling send on.\n");
+       p_m_inband_send_on = 1;
+}
+
+void PmISDN::inband_send_off(void)
+{
+       PDEBUG(DEBUG_PORT, "turning inband signalling send off.\n");
+       p_m_inband_send_on = 0;
+}
+
+void PmISDN::inband_receive(unsigned char *buffer, int len)
+{
+//
+//     if (len >= SS5_DECODER_NPOINTS)
+//             ss5_decode(buffer, SS5_DECODER_NPOINTS);
+       PERROR("this function must be derived to function!\n");
+}
+
+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;
+}
+
+void PmISDN::inband_receive_off(void)
+{
+       PDEBUG(DEBUG_PORT, "turning inband signalling receive off.\n");
+       p_m_inband_receive_on = 0;
+}
+
+void PmISDN::mute_on(void)
+{
+       if (p_m_mute)
+               return;
+       PDEBUG(DEBUG_PORT, "turning mute on.\n");
+       p_m_mute = 1;
+       set_conf(p_m_conf, 0);
+}
+
+void PmISDN::mute_off(void)
+{
+       if (!p_m_mute)
+               return;
+       PDEBUG(DEBUG_PORT, "turning mute off.\n");
+       p_m_mute = 0;
+       set_conf(0, p_m_conf);
+}
+
+
diff --git a/mISDN.h b/mISDN.h
index 4f1e596..f8d2341 100644 (file)
--- a/mISDN.h
+++ b/mISDN.h
@@ -67,6 +67,9 @@ struct mISDNport {
        /* gsm */
        int gsm; /* this is the (only) GSM interface */
        int lcr_sock; /* socket of loopback on LCR side */
        /* gsm */
        int gsm; /* this is the (only) GSM interface */
        int lcr_sock; /* socket of loopback on LCR side */
+
+       /* ss5 */
+       unsigned int ss5; /* set, if SS5 signalling enabled, also holds feature bits */
 };
 extern mISDNport *mISDNport_first;
 
 };
 extern mISDNport *mISDNport_first;
 
@@ -89,7 +92,8 @@ calls with no bchannel (call waiting, call on hold).
 int mISDN_initialize(void);
 void mISDN_deinitialize(void);
 int mISDN_getportbyname(int sock, int cnt, char *portname);
 int mISDN_initialize(void);
 void mISDN_deinitialize(void);
 int mISDN_getportbyname(int sock, int cnt, char *portname);
-struct mISDNport *mISDNport_open(int port, char *portname, int ptp, int force_nt, int te_special, int l1hold, int l2hold, struct interface *interface, int gsm);
+struct mISDNport *mISDNport_open(int port, char *portname, int ptp, int force_nt, int te_special, int l1hold, int l2hold, struct interface *interface, int gsm, unsigned int ss5);
+void mISDNport_static(struct mISDNport *mISDNport);
 void mISDNport_close_all(void);
 void mISDNport_close(struct mISDNport *mISDNport);
 void mISDN_port_reorder(void);
 void mISDNport_close_all(void);
 void mISDNport_close(struct mISDNport *mISDNport);
 void mISDN_port_reorder(void);
@@ -121,6 +125,7 @@ class PmISDN : public Port
        int p_m_tx_gain, p_m_rx_gain;           /* volume shift (0 = no change) */
        char p_m_pipeline[256];                 /* filter pipeline */
        int p_m_echo, p_m_conf;                 /* remote echo, conference number */
        int p_m_tx_gain, p_m_rx_gain;           /* volume shift (0 = no change) */
        char p_m_pipeline[256];                 /* filter pipeline */
        int p_m_echo, p_m_conf;                 /* remote echo, conference number */
+       int p_m_mute;                           /* if set, conf is disconnected */
        int p_m_tone;                           /* current kernel space tone */
        int p_m_rxoff;                          /* rx from driver is disabled */
 //     int p_m_nodata;                         /* all parties within a conf are isdn ports, so pure bridging is possible */
        int p_m_tone;                           /* current kernel space tone */
        int p_m_rxoff;                          /* rx from driver is disabled */
 //     int p_m_nodata;                         /* all parties within a conf are isdn ports, so pure bridging is possible */
@@ -152,6 +157,7 @@ class PmISDN : public Port
 
        void set_tone(const char *dir, const char *name);
        void set_echotest(int echotest);
 
        void set_tone(const char *dir, const char *name);
        void set_echotest(int echotest);
+       void set_conf(int oldconf, int newconf);
 
        int p_m_portnum;                        /* used port number (1...n) */
        int p_m_b_index;                        /* index 0,1 0..29 */
 
        int p_m_portnum;                        /* used port number (1...n) */
        int p_m_b_index;                        /* index 0,1 0..29 */
@@ -168,6 +174,18 @@ class PmISDN : public Port
        unsigned int p_m_remote_ref;            /* join to export bchannel to */
        int p_m_remote_id;                      /* sock to export bchannel to */
 
        unsigned int p_m_remote_ref;            /* join to export bchannel to */
        int p_m_remote_id;                      /* sock to export bchannel to */
 
+       int p_m_inband_send_on;                 /* triggers optional send function */
+       int p_m_inband_receive_on;              /* triggers optional receive function */
+       int p_m_mute_on;                        /* if mute is on, bridge is removed */
+       virtual int inband_send(unsigned char *buffer, int len);
+       void inband_send_on(void);
+       void inband_send_off(void);
+       virtual void inband_receive(unsigned char *buffer, int len);
+       void inband_receive_on(void);
+       void inband_receive_off(void);
+       void mute_on(void);
+       void mute_off(void);
+
        int seize_bchannel(int channel, int exclusive); /* requests / reserves / links bchannels, but does not open it! */
        void drop_bchannel(void);
 };
        int seize_bchannel(int channel, int exclusive); /* requests / reserves / links bchannels, but does not open it! */
        void drop_bchannel(void);
 };
diff --git a/main.c b/main.c
index 8d06aea..da21c23 100644 (file)
--- a/main.c
+++ b/main.c
@@ -360,6 +360,12 @@ int main(int argc, char *argv[])
        /* generate alaw / ulaw tables */
        generate_tables(options.law);
 
        /* generate alaw / ulaw tables */
        generate_tables(options.law);
 
+#ifdef WITH_SS5
+       /* init ss5 sine tables */
+       ss5_sine_generate();
+       ss5_test_decode();
+#endif
+
        /* load tones (if requested) */
        if (fetch_tones() == 0) {
                fprintf(stderr, "Unable to fetch tones into memory.\n");
        /* load tones (if requested) */
        if (fetch_tones() == 0) {
                fprintf(stderr, "Unable to fetch tones into memory.\n");
diff --git a/main.h b/main.h
index 0b9f754..e5802de 100644 (file)
--- a/main.h
+++ b/main.h
@@ -68,6 +68,7 @@ void debug(const char *function, int line, const char *prefix, char *buffer);
 #define DEBUG_PORT     0x0100
 #define DEBUG_ISDN     0x0110
 #define DEBUG_GSM      0x0120
 #define DEBUG_PORT     0x0100
 #define DEBUG_ISDN     0x0110
 #define DEBUG_GSM      0x0120
+#define DEBUG_SS5      0x0130
 //#define DEBUG_KNOCK  0x0140
 #define DEBUG_VBOX     0x0180
 #define DEBUG_EPOINT   0x0200
 //#define DEBUG_KNOCK  0x0140
 #define DEBUG_VBOX     0x0180
 #define DEBUG_EPOINT   0x0200
@@ -151,6 +152,11 @@ extern "C" {
 #ifdef WITH_GSM
 #include "gsm.h"
 #endif
 #ifdef WITH_GSM
 #include "gsm.h"
 #endif
+#ifdef WITH_SS5
+#include "ss5_encode.h"
+#include "ss5_decode.h"
+#include "ss5.h"
+#endif
 #include "vbox.h"
 #include "join.h"
 #include "joinpbx.h"
 #include "vbox.h"
 #include "join.h"
 #include "joinpbx.h"
diff --git a/port.h b/port.h
index be2598f..5e8eba9 100644 (file)
--- a/port.h
+++ b/port.h
@@ -18,6 +18,7 @@
 #define PORT_CLASS_MASK                0xff00
 #define PORT_CLASS_mISDN_DSS1  0x0110
 #define PORT_CLASS_mISDN_GSM   0x0120
 #define PORT_CLASS_MASK                0xff00
 #define PORT_CLASS_mISDN_DSS1  0x0110
 #define PORT_CLASS_mISDN_GSM   0x0120
+#define PORT_CLASS_mISDN_SS5   0x0130
 #define PORT_CLASS_mISDN_MASK  0xfff0
        /* nt-mode */
 #define        PORT_TYPE_DSS1_NT_IN    0x0111
 #define PORT_CLASS_mISDN_MASK  0xfff0
        /* nt-mode */
 #define        PORT_TYPE_DSS1_NT_IN    0x0111
        /* gsm */
 #define        PORT_TYPE_GSM_IN        0x0121
 #define        PORT_TYPE_GSM_OUT       0x0122
        /* gsm */
 #define        PORT_TYPE_GSM_IN        0x0121
 #define        PORT_TYPE_GSM_OUT       0x0122
+       /* ss5 */
+#define        PORT_TYPE_SS5_IN        0x0131
+#define        PORT_TYPE_SS5_OUT       0x0132
+#define        PORT_TYPE_SS5_IDLE      0x0133
        /* answering machine */
 #define        PORT_TYPE_VBOX_OUT      0x0311
 
        /* answering machine */
 #define        PORT_TYPE_VBOX_OUT      0x0311
 
index cea929c..7a08680 100644 (file)
@@ -1035,6 +1035,9 @@ int admin_state(struct admin_queue **responsep)
                        case PORT_STATE_OUT_DISCONNECT:
                        response->am[num].u.p.state = ADMIN_STATE_OUT_DISCONNECT;
                        break;
                        case PORT_STATE_OUT_DISCONNECT:
                        response->am[num].u.p.state = ADMIN_STATE_OUT_DISCONNECT;
                        break;
+                       case PORT_STATE_RELEASE:
+                       response->am[num].u.p.state = ADMIN_STATE_RELEASE;
+                       break;
                        default:
                        response->am[num].u.p.state = ADMIN_STATE_IDLE;
                }
                        default:
                        response->am[num].u.p.state = ADMIN_STATE_IDLE;
                }
diff --git a/ss5.cpp b/ss5.cpp
new file mode 100644 (file)
index 0000000..d038769
--- /dev/null
+++ b/ss5.cpp
@@ -0,0 +1,1972 @@
+/*****************************************************************************\
+**                                                                           **
+** LCR                                                                       **
+**                                                                           **
+**---------------------------------------------------------------------------**
+** Copyright: Andreas Eversberg                                              **
+**                                                                           **
+** mISDN ss5                                                                 **
+**                                                                           **
+\*****************************************************************************/ 
+
+/*
+ * STATES:
+ *
+ * there are three types of states
+ *
+ * - the port state (p_state): used for current call state
+ * - the ss5 state (p_m_s_state): used for current tone
+ * - the ss5 signal state (p_m_s_signal): used for current signal state of current tone
+ *
+ * the port state differs from isdn state:
+ * 
+ * - PORT_STATE_IDLE: used until number is complete. outgoing overlap dialing is received in this state.
+ * - PORT_STATE_OUT_SETUP: the seizing procedure is started.
+ * - PORT_STATE_OUT_OVERLAP: the transmitter is sending the digits.
+ * - PORT_STATE_OUT_PROCEEDING: the digits are sent, we wait until someone answers.
+ * - PORT_STATE_CONNECT: a call is answered on either side.
+ * - PORT_STATE_IN_SETUP: the seizing is received, we wait for the first digit.
+ * - PORT_STATE_IN_OVERLAP: the digits are received.
+ * - PORT_STATE_IN_PROCEEDING: the number is complete, a SETUP is indicated.
+ * - PORT_STATE_IN_DISCONNECT: a clear-back was received, an DISCONNECT is indicated.
+ * - PORT_STATE_RELEASE: the clear forward procedure is started.
+ *
+ */
+
+#include "main.h"
+
+/* ss5 signal states */
+enum {
+       SS5_STATE_IDLE,                 /* no signal */
+       SS5_STATE_SEIZING,              /* seizing */
+       SS5_STATE_PROCEED_TO_SEND,      /* proceed-to-send */
+       SS5_STATE_BUSY_FLASH,           /* busy-flash / clear back */
+       SS5_STATE_ACK_BUSY_FLASH,       /* acknowledge of busy/answer/clear-back */
+       SS5_STATE_ANSWER,               /* answer */
+       SS5_STATE_ACK_ANSWER,           /* acknowledge of busy/answer/clear-back */
+       SS5_STATE_FORWARD_TRANSFER,     /* forward transfer */
+       SS5_STATE_CLEAR_BACK,           /* clear-back */
+       SS5_STATE_ACK_CLEAR_BACK,       /* acknowledge of busy/answer/clear-back */
+       SS5_STATE_CLEAR_FORWARD,        /* clear-forward */
+       SS5_STATE_RELEASE_GUARD,        /* release-guard */
+       SS5_STATE_DIAL_OUT,             /* dialing state (transmitter) */
+       SS5_STATE_DIAL_IN,              /* dialing state (receiver) */
+       SS5_STATE_DIAL_IN_PULSE,        /* dialing state (receiver with pulses) */
+       SS5_STATE_DELAY,                /* after signal wait until next signal can be sent */
+       SS5_STATE_DOUBLE_SEIZE,         /* in case of a double seize, we make the remote size recognize it */
+};
+const char *ss5_state_name[] = {
+       "STATE_IDLE",
+       "STATE_SEIZING",
+       "STATE_PROCEED_TO_SEND",
+       "STATE_BUSY_FLASH",
+       "STATE_ACK_BUSY_FLASH",
+       "STATE_ANSWER",
+       "STATE_ACK_ANSWER",
+       "STATE_FORWARD_TRANSFER",
+       "STATE_CLEAR_BACK",
+       "STATE_ACK_CLEAR_BACK",
+       "STATE_CLEAR_FORWARD",
+       "STATE_RELEASE_GUARD",
+       "STATE_DIAL_OUT",
+       "STATE_DIAL_IN",
+       "STATE_DIAL_IN_PULSE",
+       "STATE_DELAY",
+       "STATE_DOUBLE_SEIZE",
+};
+
+enum {
+       SS5_SIGNAL_NULL,
+       /* sending signal states */
+       SS5_SIGNAL_SEND_ON,             /* sending signal, waiting for acknowledge */
+       SS5_SIGNAL_SEND_ON_RECOG,       /* sending signal, receiving ack, waiting for recogition timer */
+       SS5_SIGNAL_SEND_OFF,            /* silence, receiving ack, waiting for stop */
+       /* receiving signal states */
+       SS5_SIGNAL_RECEIVE_RECOG,       /* receiving signal, waiting for recognition timer */
+       SS5_SIGNAL_RECEIVE,             /* receiving signal / send ack, waiting for stop */
+       SS5_SIGNAL_DELAY,               /* delay after release guard to prevent ping-pong */
+       /* sending / receiving digit states */
+       SS5_SIGNAL_DIGIT_PAUSE,         /* pausing before sending (next) digit */
+       SS5_SIGNAL_DIGIT_ON,            /* sending digit */
+       SS5_SIGNAL_PULSE_OFF,           /* make */
+       SS5_SIGNAL_PULSE_ON,            /* break */
+};
+const char *ss5_signal_name[] = {
+       "NULL",
+       "SIGNAL_SEND_ON",
+       "SIGNAL_SEND_ON_RECOG",
+       "SIGNAL_SEND_OFF",
+       "SIGNAL_RECEIVE_RECOG",
+       "SIGNAL_RECEIVE",
+       "SIGNAL_DELAY",
+       "SIGNAL_DIGIT_PAUSE",
+       "SIGNAL_DIGIT_ON",
+       "SIGNAL_PULSE_OFF",
+       "SIGNAL_PULSE_ON",
+};
+
+/* ss5 signal timers (in samples) */
+#define        SS5_TIMER_AFTER_SIGNAL  (100*8) /* wait after signal is terminated */
+#define SS5_TIMER_KP           (100*8) /* duration of KP1 or KP2 digit */
+#define SS5_TIMER_DIGIT                (55*8)  /* duration of all other digits */
+#define SS5_TIMER_PAUSE                (55*8)  /* pause between digits */
+#define SS5_TIMER_FORWARD      (850*8) /* forward transfer length */
+#define SS5_TIMER_RECOG_SEIZE  (40*8)  /* recognition time of seizing / proceed-to-send signal */
+#define SS5_TIMER_RECOG_OTHER  (125*8) /* recognition time of all other f1/f2 signals */
+#define SS5_TIMER_SIGNAL_LOSS  (15*8)  /* minimum time of signal loss for a continous signal */
+#define SS5_TIMER_DOUBLE_SEIZE (850*8) /* double seize length */
+#define SS5_TIMER_RELEASE_GUARD        (850*8) /* be sure to release after clear-forward */
+#define SS5_TIMER_RELEASE_MAX  (2000*8)/* maximum time for release guard to prevent 'double-releasing' */
+#define        SS5_TIMER_RELEASE_DELAY (4000*8)/* wait after release guard to prevent ping-pong */
+#define BELL_TIMER_BREAK       (50*8)  /* loop open, tone */
+#define BELL_TIMER_MAKE                (50*8)  /* loop closed, no tone */
+#define BELL_TIMER_PAUSE       (800*8) /* interdigit delay */
+#define BELL_TIMER_RECOG_HANGUP        (200*8) /* time to recognize hangup */
+#define BELL_TIMER_RECOG_END   (300*8) /* recognize end of digit */
+
+/* ss5 timers */
+#define SS5_TIMER_OVERLAP      5.0     /* timeout for overlap digits received on outgoing exchange */
+
+
+/*
+ * ss5 trace header
+ */
+enum { /* even values are indications, odd values are requests */
+       SS5_SEIZING_IND,
+       SS5_SEIZING_REQ,
+       SS5_PROCEED_TO_SEND_IND,
+       SS5_PROCEED_TO_SEND_REQ,
+       SS5_BUSY_FLASH_IND,
+       SS5_BUSY_FLASH_REQ,
+       SS5_ANSWER_IND,
+       SS5_ANSWER_REQ,
+       SS5_CLEAR_BACK_IND,
+       SS5_CLEAR_BACK_REQ,
+       SS5_CLEAR_FORWARD_IND,
+       SS5_CLEAR_FORWARD_REQ,
+       SS5_RELEASE_GUARD_IND,
+       SS5_RELEASE_GUARD_REQ,
+       SS5_ACKNOWLEDGE_IND,
+       SS5_ACKNOWLEDGE_REQ,
+       SS5_DOUBLE_SEIZURE_IND,
+       SS5_DOUBLE_SEIZURE_REQ,
+       SS5_DIALING_IND,
+       SS5_DIALING_REQ,
+       SS5_FORWARD_TRANSFER_IND,
+       SS5_FORWARD_TRANSFER_REQ,
+};
+static struct isdn_message {
+       const char *name;
+       unsigned int value;
+} ss5_message[] = {
+       {"SEIZING RECEIVED", SS5_SEIZING_IND},
+       {"SEIZING SENDING", SS5_SEIZING_REQ},
+       {"PROCEED-TO-SEND RECEIVED", SS5_PROCEED_TO_SEND_IND},
+       {"PROCEED-TO-SEND SENDING", SS5_PROCEED_TO_SEND_REQ},
+       {"BUSY-FLASH RECEIVED", SS5_BUSY_FLASH_IND},
+       {"BUSY-FLASH SENDING", SS5_BUSY_FLASH_REQ},
+       {"ANSWER RECEIVED", SS5_ANSWER_IND},
+       {"ANSWER SENDING", SS5_ANSWER_REQ},
+       {"CLEAR-BACK RECEIVED", SS5_CLEAR_BACK_IND},
+       {"CLEAR-BACK SENDING", SS5_CLEAR_BACK_REQ},
+       {"CLEAR-FORWARD RECEIVED", SS5_CLEAR_FORWARD_IND},
+       {"CLEAR-FORWARD SENDING", SS5_CLEAR_FORWARD_REQ},
+       {"RELEASE-GUARD RECEIVED", SS5_RELEASE_GUARD_IND},
+       {"RELEASE-GUARD SENDING", SS5_RELEASE_GUARD_REQ},
+       {"ACKNOWLEDGE RECEIVED", SS5_ACKNOWLEDGE_IND},
+       {"ACKNOWLEDGE SENDING", SS5_ACKNOWLEDGE_REQ},
+       {"DOUBLE-SEIZURE RECEIVED", SS5_DOUBLE_SEIZURE_IND},
+       {"DOUBLE-SEIZURE SENDING", SS5_DOUBLE_SEIZURE_REQ},
+       {"DIALING RECEIVED", SS5_DIALING_IND},
+       {"DIALING SENDING", SS5_DIALING_REQ},
+       {"FORWARD-TRANSFER RECEIVED", SS5_FORWARD_TRANSFER_IND},
+       {"FORWARD-TRANSFER SENDING", SS5_FORWARD_TRANSFER_REQ},
+       {NULL, 0},
+};
+static void ss5_trace_header(struct mISDNport *mISDNport, class PmISDN *port, unsigned int msg, int channel)
+{
+       int i;
+       char msgtext[64];
+
+       SCPY(msgtext, "<<UNKNOWN MESSAGE>>");
+       /* select message and primitive text */
+       i = 0;
+       while(ss5_message[i].name) {
+//             if (msg == L3_NOTIFY_REQ) printf("val = %x %s\n", isdn_message[i].value, isdn_message[i].name);
+               if (ss5_message[i].value == msg) {
+                       SCPY(msgtext, ss5_message[i].name);
+                       break;
+               }
+               i++;
+       }
+
+       /* init trace with given values */
+       start_trace(mISDNport?mISDNport->portnum:-1,
+                   mISDNport?(mISDNport->ifport?mISDNport->ifport->interface:NULL):NULL,
+                   port?numberrize_callerinfo(port->p_callerinfo.id, port->p_callerinfo.ntype, options.national, options.international):NULL,
+                   port?port->p_dialinginfo.id:NULL,
+                   (msg&1)?DIRECTION_OUT:DIRECTION_IN,
+                   CATEGORY_CH,
+                   port?port->p_serial:0,
+                   msgtext);
+       add_trace("channel", NULL, "%d", channel);
+       switch (port->p_type) {
+               case PORT_TYPE_SS5_OUT:
+               add_trace("state", NULL, "outgoing");
+               break;
+               case PORT_TYPE_SS5_IN:
+               add_trace("state", NULL, "incomming");
+               break;
+               default:
+               add_trace("state", NULL, "idle");
+               break;
+       }
+}
+
+
+/*
+ * changes release tone int busy signal
+ * this makes the line more authentic
+ */
+void Pss5::set_tone(const char *dir, const char *name)
+{
+       if (name && !strcmp(name, "cause_10"))
+               name = "busy";
+
+       PmISDN::set_tone(dir, name);
+}
+
+/*
+ * creation of static channels
+ */
+void ss5_create_channel(struct mISDNport *mISDNport, int i)
+{
+       class Pss5              *ss5port;
+       char                    portname[32];
+       struct port_settings    port_settings;
+
+       SPRINT(portname, "%s-%d", mISDNport->name, i+1);
+
+       memset(&port_settings, 0, sizeof(port_settings));
+       SCPY(port_settings.tones_dir, options.tones_dir);
+
+       ss5port = new Pss5(PORT_TYPE_SS5_IDLE, mISDNport, portname, &port_settings, i + (i>=15) + 1, 1, B_MODE_TRANSPARENT);
+       if (!ss5port)
+               FATAL("No memory for Pss5 class.\n");
+       if (!ss5port->p_m_b_channel)
+               FATAL("No bchannel on given index.\n");
+
+       /* connect channel */
+       bchannel_event(mISDNport, ss5port->p_m_b_index, B_EVENT_USE);
+
+}
+
+
+/*
+ * hunt for a free line
+ * this function returns a port object in idle state.
+ */
+class Pss5 *ss5_hunt_line(struct mISDNport *mISDNport)
+{
+       int i;
+       class Port      *port;
+       class Pss5      *ss5port = NULL;
+       struct select_channel *selchannel; 
+
+       PDEBUG(DEBUG_SS5, "Entered name=%s\n", mISDNport->name);
+       selchannel = mISDNport->ifport->out_channel;
+       while(selchannel) {
+               switch(selchannel->channel) {
+                       case CHANNEL_FREE: /* free channel */
+                       case CHANNEL_ANY: /* any channel */
+                       for (i = 0; i < mISDNport->b_num; i++) {
+                               port = mISDNport->b_port[i];
+                               PDEBUG(DEBUG_SS5, "Checking port %p on index\n", port, i);
+                               if (!port)
+                                       continue;
+                               if (port->p_type == PORT_TYPE_SS5_IN || port->p_type == PORT_TYPE_SS5_OUT)
+                                       PDEBUG(DEBUG_SS5, "Checking port %s: channel %d not available, because port not idle type.\n",
+                                               mISDNport->name, i);
+                               if (port->p_type != PORT_TYPE_SS5_IDLE)
+                                       continue;
+                               ss5port = (class Pss5 *)port;
+                               /* is really idle ? */
+                               if (ss5port->p_state == PORT_STATE_IDLE
+                                && ss5port->p_m_s_state == SS5_STATE_IDLE)
+                                       return ss5port;
+                               PDEBUG(DEBUG_SS5, "Checking port %s: channel %d not available, because p_state=%d, ss5_state=%d.\n",
+                                       mISDNport->name, i, ss5port->p_state,ss5port->p_m_s_state);
+                       }
+                       PDEBUG(DEBUG_SS5, "no free interface\n");
+                       return NULL;
+
+                       case CHANNEL_NO:
+                       break;
+
+                       default:
+                       if (selchannel->channel<1 || selchannel->channel==16)
+                               break;
+                       i = selchannel->channel-1-(selchannel->channel>=17);
+                       if (i >= mISDNport->b_num)
+                               break;
+                       port = mISDNport->b_port[i];
+                       if (!port)
+                               break;
+                       if (port->p_type == PORT_TYPE_SS5_IN || port->p_type == PORT_TYPE_SS5_OUT)
+                               PDEBUG(DEBUG_SS5, "Checking port %s: channel %d not available, because port not idle type.\n",
+                                       mISDNport->name, i);
+                       if (port->p_type != PORT_TYPE_SS5_IDLE)
+                               break;
+                       ss5port = (class Pss5 *)port;
+                       /* is really idle ? */
+                       if (ss5port->p_state == PORT_STATE_IDLE
+                        && ss5port->p_m_s_state == SS5_STATE_IDLE)
+                               return ss5port;
+                       PDEBUG(DEBUG_SS5, "Checking port %s: channel %d not available, because p_state=%d, ss5_state=%d.\n",
+                               mISDNport->name, i, ss5port->p_state,ss5port->p_m_s_state);
+               }
+               selchannel = selchannel->next;
+       }
+       PDEBUG(DEBUG_SS5, "no free interface in channel list\n");
+       return NULL;
+}
+
+
+/*
+ * constructor
+ */
+Pss5::Pss5(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;
+       p_m_s_state = SS5_STATE_IDLE;
+       p_m_s_signal = SS5_SIGNAL_NULL;
+       p_m_s_dial[0] = '\0';
+       p_m_s_digit_i = 0;
+       p_m_s_pulsecount = 0;
+       p_m_s_last_digit = ' ';
+       p_m_s_signal_loss = 0;
+       p_m_s_decoder_count = 0;
+       //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;
+       p_m_s_answer = 0;
+       p_m_s_busy_flash = 0;
+       p_m_s_clear_back = 0;
+       memset(p_m_s_delay_digits, ' ', sizeof(p_m_s_delay_digits));
+       memset(p_m_s_delay_mute, ' ', sizeof(p_m_s_delay_mute));
+
+       /* turn on signalling receiver */
+       inband_receive_on();
+
+       PDEBUG(DEBUG_SS5, "Created new mISDNPort(%s). Currently %d objects use.\n", portname, mISDNport->use);
+}
+
+
+/*
+ * destructor
+ */
+Pss5::~Pss5()
+{
+}
+
+
+/*
+ * change ss5 states
+ */
+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;
+}
+void Pss5::_new_ss5_signal(int signal, const char *func, int line)
+{
+       if (p_m_s_signal)
+               PDEBUG(DEBUG_SS5, "%s(%s:%d): changing SS5 signal state from %s to %s\n", p_name, func, line, ss5_signal_name[p_m_s_signal], ss5_signal_name[signal]);
+       else
+               PDEBUG(DEBUG_SS5, "%s(%s:%d): changing SS5 signal state to %s\n", p_name, func, line, ss5_signal_name[signal]);
+       p_m_s_signal = signal;
+}
+
+
+/*
+ * signalling receiver
+ *
+ * this function will be called for every audio received.
+ */
+void Pss5::inband_receive(unsigned char *buffer, int len)
+{
+       int count = 0, tocopy, space;
+       char digit;
+
+       again:
+       /* how much to copy ? */
+       tocopy = len - count;
+       space = SS5_DECODER_NPOINTS - p_m_s_decoder_count;
+       if (space < 0)
+               FATAL("p_m_s_decoder_count overflows\n");
+       if (space < tocopy)
+               tocopy = space;
+       /* copy an count */
+       memcpy(p_m_s_decoder_buffer+p_m_s_decoder_count, buffer+count, tocopy);
+       p_m_s_decoder_count += tocopy;
+       count += tocopy;
+       /* decoder buffer not completely filled ? */
+       if (tocopy < space)
+               return;
+
+       /* decode one frame */
+       digit = ss5_decode(p_m_s_decoder_buffer, SS5_DECODER_NPOINTS);
+       p_m_s_decoder_count = 0;
+
+#ifdef DEBUG_DETECT
+       if (p_m_s_last_digit != digit && digit != ' ')
+               PDEBUG(DEBUG_SS5, "%s: detecting signal '%c' start (state=%s signal=%s)\n", p_name, digit, ss5_state_name[p_m_s_state], ss5_signal_name[p_m_s_signal]);
+#endif
+
+       /* ignore short loss of signal */
+       if (p_m_s_signal_loss) {
+               if (digit == ' ') {
+                       /* still lost */
+                       if (p_m_s_signal_loss >= SS5_TIMER_SIGNAL_LOSS) {
+#ifdef DEBUG_DETECT
+                               PDEBUG(DEBUG_SS5, "%s: signal '%c' lost too long\n", p_name, p_m_s_last_digit);
+#endif
+                               /* long enough, we stop loss-timer */
+                               p_m_s_signal_loss = 0;
+                       } else {
+                               /* not long enough, so we use last signal */
+                               p_m_s_signal_loss += SS5_DECODER_NPOINTS;
+                               digit = p_m_s_last_digit;
+                       }
+               } else {
+                       /* signal is back, we stop timer and store */
+#ifdef DEBUG_DETECT
+                       PDEBUG(DEBUG_SS5, "%s: signal '%c' lost, but continues with '%c'\n", p_name, p_m_s_last_digit, digit);
+#endif
+                       p_m_s_signal_loss = 0;
+                       p_m_s_last_digit = digit;
+               }
+       } else {
+               if (p_m_s_last_digit != ' ' && digit == ' ') {
+#ifdef DEBUG_DETECT
+                       PDEBUG(DEBUG_SS5, "%s: signal '%c' lost\n", p_name, p_m_s_last_digit);
+#endif
+                       /* restore last digit until signal is really lost */
+                       p_m_s_last_digit = digit;
+                       /* starting to loose signal */
+                       p_m_s_signal_loss = SS5_DECODER_NPOINTS;
+               } else {
+                       /* storing last signal, in case it is lost */
+                       p_m_s_last_digit = digit;
+               }
+       }
+
+       /* update mute */
+       if ((p_m_mISDNport->ss5 & SS5_FEATURE_SUPPRESS)) {
+               int mdigit;
+               memcpy(p_m_s_delay_mute, p_m_s_delay_mute+1, sizeof(p_m_s_delay_mute)-1);
+               p_m_s_delay_mute[sizeof(p_m_s_delay_mute)-1] = digit;
+               mdigit = p_m_s_delay_mute[0];
+               if (p_m_mute) {
+                       /* mute is on */
+                       if (mdigit != 'A' && mdigit != 'B' && mdigit != 'C')
+                               mute_off();
+               } else {
+                       /* mute is off */
+                       if (mdigit == 'A' || mdigit == 'B' || mdigit == 'C')
+                               mute_on();
+               }
+       }
+
+       /* delay decoded tones */
+       if ((p_m_mISDNport->ss5 & SS5_FEATURE_DELAY)) {
+               /* shift buffer */
+               memcpy(p_m_s_delay_digits, p_m_s_delay_digits+1, sizeof(p_m_s_delay_digits)-1);
+               /* first in */
+               p_m_s_delay_digits[sizeof(p_m_s_delay_digits)-1] = digit;
+               /* first out */
+               digit = p_m_s_delay_digits[0];
+       }
+
+       /* clear forward is always recognized */
+       if (digit == 'C' && p_m_s_state != SS5_STATE_CLEAR_FORWARD && p_m_s_state != SS5_STATE_RELEASE_GUARD) {
+               switch (p_type) {
+                       case PORT_TYPE_SS5_OUT:
+                       PDEBUG(DEBUG_SS5, "%s: received release-guard, waiting for recognition\n", p_name);
+                       break;
+                       case PORT_TYPE_SS5_IN:
+                       PDEBUG(DEBUG_SS5, "%s: received clear-forward, waiting for recognition\n", p_name);
+                       break;
+                       default:
+                       PDEBUG(DEBUG_SS5, "%s: received clear-forward in idle state, waiting for recognition\n", p_name);
+                       break;
+               }
+               new_state(PORT_STATE_RELEASE);
+               new_ss5_state(SS5_STATE_RELEASE_GUARD);
+               new_ss5_signal(SS5_SIGNAL_RECEIVE_RECOG);
+               p_m_s_recog = 0;
+       } else
+       switch(p_m_s_state) {
+               case SS5_STATE_IDLE:
+               /* seizing only recognized in port idle state */
+               if (p_state == PORT_STATE_IDLE) {
+                       if (digit != 'A')
+                               break;
+                       seize:
+                       PDEBUG(DEBUG_SS5, "%s: received seize, waiting for recognition\n", p_name);
+                       p_type = PORT_TYPE_SS5_IN;
+                       new_ss5_state(SS5_STATE_PROCEED_TO_SEND);
+                       new_ss5_signal(SS5_SIGNAL_RECEIVE_RECOG);
+                       p_m_s_recog = 0;
+                       break;
+               }
+               /* other signals */
+               if (digit == 'A') {
+                       if (p_type != PORT_TYPE_SS5_OUT)
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: received answer, waiting for recognition\n", p_name);
+                       new_ss5_state(SS5_STATE_ACK_ANSWER);
+                       new_ss5_signal(SS5_SIGNAL_RECEIVE_RECOG);
+                       p_m_s_recog = 0;
+                       break;
+               }
+               if (digit == 'B') {
+                       if (p_type == PORT_TYPE_SS5_IN) {
+                               if ((p_m_mISDNport->ss5 & SS5_FEATURE_BELL)) {
+                                       new_ss5_state(SS5_STATE_DIAL_IN_PULSE); /* go pulsing state */
+                                       new_ss5_signal(SS5_SIGNAL_PULSE_OFF); /* we are starting with pulse off */
+                                       p_m_s_pulsecount = 0; /* init pulse counter */
+                                       p_m_s_dial[0] = '\0'; /* init dial string */
+                                       pulse_ind(1); /* also inits recogition timer... */
+                                       break;
+                               }
+                               PDEBUG(DEBUG_SS5, "%s: received forward-transfer, waiting for recognition\n", p_name);
+                               /* forward transfer on incomming lines */
+                               new_ss5_state(SS5_STATE_FORWARD_TRANSFER);
+                               new_ss5_signal(SS5_SIGNAL_RECEIVE_RECOG);
+                               p_m_s_recog = 0;
+                               break;
+                       }
+                       if (p_state == PORT_STATE_CONNECT) {
+                               PDEBUG(DEBUG_SS5, "%s: received clear-back, waiting for recognition\n", p_name);
+                               new_ss5_state(SS5_STATE_ACK_CLEAR_BACK);
+                       } else {
+                               PDEBUG(DEBUG_SS5, "%s: received busy-flash, waiting for recognition\n", p_name);
+                               new_ss5_state(SS5_STATE_ACK_BUSY_FLASH);
+                       }
+                       new_ss5_signal(SS5_SIGNAL_RECEIVE_RECOG);
+                       p_m_s_recog = 0;
+                       break;
+               }
+               /* dialing only allowed in incomming setup state */
+               if (p_state == PORT_STATE_IN_SETUP) {
+                       if (!strchr("1234567890*#abc", digit))
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: received dialing start with '%c'\n", p_name, digit);
+                       new_ss5_state(SS5_STATE_DIAL_IN);
+                       new_ss5_signal(SS5_SIGNAL_DIGIT_ON);
+                       p_m_s_dial[0] = '\0';
+                       digit_ind(digit);
+                       break;
+               }
+               break;
+               /* sending seizing */
+               case SS5_STATE_SEIZING:
+               switch (p_m_s_signal) {
+                       case SS5_SIGNAL_SEND_ON:
+                       if (digit == 'A') { /* double seize */
+                               PDEBUG(DEBUG_SS5, "%s: received double seizure\n", p_name, digit);
+                               double_seizure_ind();
+                               break;
+                       }
+                       if (digit == 'B') {
+                               PDEBUG(DEBUG_SS5, "%s: received answer to outgoing seize, waiting for recognition\n", p_name);
+                               /* set recognition timer */
+                               new_ss5_signal(SS5_SIGNAL_SEND_ON_RECOG);
+                               p_m_s_recog = 0;
+                       }
+                       break;
+                       case SS5_SIGNAL_SEND_ON_RECOG:
+                       if (digit != 'B') { /* seize */
+                               PDEBUG(DEBUG_SS5, "%s: answer to outgoing seize is gone before recognition\n", p_name);
+                               new_ss5_signal(SS5_SIGNAL_SEND_ON);
+//                             p_m_s_sample_nr = 0;
+//                             inband_send_on();
+                               break;
+                       }
+                       p_m_s_recog += SS5_DECODER_NPOINTS;
+                       if (p_m_s_recog < SS5_TIMER_RECOG_SEIZE)
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: answer to outgoing seize recognized, turning off, waiting for recognition\n", p_name);
+                       new_ss5_signal(SS5_SIGNAL_SEND_OFF);
+                       break;
+                       case SS5_SIGNAL_SEND_OFF:
+                       if (digit == 'B')
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: outgoing seizure is complete, proceeding...\n", p_name);
+                       new_ss5_state(SS5_STATE_IDLE);
+                       proceed_to_send_ind();
+                       break;
+               }
+               break;
+               /* answer to seize */
+               case SS5_STATE_PROCEED_TO_SEND:
+               if (p_m_s_signal == SS5_SIGNAL_RECEIVE_RECOG) {
+                       if (digit != 'A') {
+                               PDEBUG(DEBUG_SS5, "%s: incomming seize is gone before recognition\n", p_name);
+                               new_ss5_state(SS5_STATE_IDLE);
+                               p_type = PORT_TYPE_SS5_IDLE;
+                               break;
+                       }
+                       p_m_s_recog += SS5_DECODER_NPOINTS;
+                       if (p_m_s_recog < SS5_TIMER_RECOG_SEIZE)
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: incomming seize is recognized, responding...\n", p_name);
+                       new_ss5_signal(SS5_SIGNAL_RECEIVE);
+                       p_m_s_sample_nr = 0;
+                       inband_send_on();
+                       break;
+               }
+               if (digit != 'A') {
+                       PDEBUG(DEBUG_SS5, "%s: incomming seize is gone after responding\n", p_name);
+                       new_ss5_state(SS5_STATE_IDLE);
+                       seizing_ind();
+               }
+               break;
+               /* sending busy flash / answer / clear-back */
+               case SS5_STATE_BUSY_FLASH:
+               case SS5_STATE_ANSWER:
+               case SS5_STATE_CLEAR_BACK:
+               switch (p_m_s_signal) {
+                       case SS5_SIGNAL_SEND_ON:
+                       if (digit == 'A') {
+                               PDEBUG(DEBUG_SS5, "%s: received acknowledge, waiting for recognition\n", p_name);
+                               /* set recognition timer */
+                               new_ss5_signal(SS5_SIGNAL_SEND_ON_RECOG);
+                               p_m_s_recog = 0;
+                       }
+                       break;
+                       case SS5_SIGNAL_SEND_ON_RECOG:
+                       if (digit != 'A') {
+                               PDEBUG(DEBUG_SS5, "%s: acknowledge is gone before recognition\n", p_name);
+                               new_ss5_signal(SS5_SIGNAL_SEND_ON);
+//                             p_m_s_sample_nr = 0;
+//                             inband_send_on();
+                               break;
+                       }
+                       p_m_s_recog += SS5_DECODER_NPOINTS;
+                       if (p_m_s_recog < SS5_TIMER_RECOG_OTHER)
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: acknowledge recognized, turning off, waiting for recognition\n", p_name);
+                       new_ss5_signal(SS5_SIGNAL_SEND_OFF);
+                       break;
+                       case SS5_SIGNAL_SEND_OFF:
+                       if (digit == 'A')
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: outgoing signal is complete\n", p_name);
+                       new_ss5_state(SS5_STATE_IDLE);
+                       break;
+               }
+               break;
+               /* answer to busy-flash / clear back */
+               case SS5_STATE_ACK_BUSY_FLASH:
+               case SS5_STATE_ACK_CLEAR_BACK:
+               if (p_m_s_signal == SS5_SIGNAL_RECEIVE_RECOG) {
+                       if (digit != 'B') {
+                               PDEBUG(DEBUG_SS5, "%s: incomming clear-back/busy-flash is gone before recognition\n", p_name);
+                               new_ss5_state(SS5_STATE_IDLE);
+                               break;
+                       }
+                       p_m_s_recog += SS5_DECODER_NPOINTS;
+                       if (p_m_s_recog < SS5_TIMER_RECOG_OTHER)
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: incomming clear-back/busy-flash is recognized, responding...\n", p_name);
+                       new_ss5_signal(SS5_SIGNAL_RECEIVE);
+                       p_m_s_sample_nr = 0;
+                       inband_send_on();
+                       break;
+               }
+               if (digit != 'B') {
+                       PDEBUG(DEBUG_SS5, "%s: incomming clear-back/busy-flash is gone after responding\n", p_name);
+                       new_ss5_state(SS5_STATE_IDLE);
+                       if (p_m_s_state == SS5_STATE_ACK_BUSY_FLASH)
+                               busy_flash_ind();
+                       else
+                               clear_back_ind();
+               }
+               break;
+               /* answer to answer */
+               case SS5_STATE_ACK_ANSWER:
+               if (p_m_s_signal == SS5_SIGNAL_RECEIVE_RECOG) {
+                       if (digit != 'A') {
+                               PDEBUG(DEBUG_SS5, "%s: incomming answer is gone before recognition\n", p_name);
+                               new_ss5_state(SS5_STATE_IDLE);
+                               break;
+                       }
+                       p_m_s_recog += SS5_DECODER_NPOINTS;
+                       if (p_m_s_recog < SS5_TIMER_RECOG_OTHER)
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: incomming answer is recognized, responding...\n", p_name);
+                       new_ss5_signal(SS5_SIGNAL_RECEIVE);
+                       p_m_s_sample_nr = 0;
+                       inband_send_on();
+                       break;
+               }
+               if (digit != 'A') {
+                       PDEBUG(DEBUG_SS5, "%s: incomming answer is gone after responding\n", p_name);
+                       new_ss5_state(SS5_STATE_IDLE);
+                       answer_ind();
+               }
+               break;
+               /* sending clear-forward */
+               case SS5_STATE_CLEAR_FORWARD:
+               switch (p_m_s_signal) {
+                       case SS5_SIGNAL_SEND_ON:
+                       if (digit == 'C') {
+                               PDEBUG(DEBUG_SS5, "%s: received answer to clear-forward, waiting for recognition\n", p_name);
+                               /* set recognition timer */
+                               new_ss5_signal(SS5_SIGNAL_SEND_ON_RECOG);
+                               p_m_s_recog = 0;
+                       }
+                       break;
+                       case SS5_SIGNAL_SEND_ON_RECOG:
+                       if (digit != 'C') {
+                               PDEBUG(DEBUG_SS5, "%s: answer to clear-forward is gone before recognition\n", p_name);
+                               new_ss5_signal(SS5_SIGNAL_SEND_ON);
+//                             p_m_s_sample_nr = 0;
+//                             inband_send_on();
+                               break;
+                       }
+                       p_m_s_recog += SS5_DECODER_NPOINTS;
+                       if (p_m_s_recog < SS5_TIMER_RECOG_OTHER)
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: answer to clear-forward recognized, turning off, waiting for recognition\n", p_name);
+                       new_ss5_signal(SS5_SIGNAL_SEND_OFF);
+                       break;
+                       case SS5_SIGNAL_SEND_OFF:
+                       if (digit == 'A') {
+                               PDEBUG(DEBUG_SS5, "%s: received seize right after clear-forward answer, continue with seize\n", p_name);
+                               new_state(PORT_STATE_IDLE);
+                               goto seize;
+                       }
+                       if (digit == 'C')
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: answer to clear-forward is complete\n", p_name);
+                       release_guard_ind();
+                       new_ss5_signal(SS5_SIGNAL_DELAY);
+                       p_m_s_recog = 0; /* use recog to delay */
+                       PDEBUG(DEBUG_SS5, "%s: answer to clear-forward on outgoing interface starting delay to prevent ping-pong\n", p_name);
+                       break;
+                       case SS5_SIGNAL_DELAY:
+                       if (digit == 'A') {
+                               PDEBUG(DEBUG_SS5, "%s: received seize right after clear-forward answer, continue with seize\n", p_name);
+                               new_state(PORT_STATE_IDLE);
+                               goto seize;
+                       }
+                       p_m_s_recog += SS5_DECODER_NPOINTS;
+                       if (p_m_s_recog < SS5_TIMER_RELEASE_DELAY)
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: delay time over, going idle\n", p_name);
+                       new_ss5_state(SS5_STATE_IDLE);
+                       new_state(PORT_STATE_IDLE);
+                       p_type = PORT_TYPE_SS5_IDLE;
+                       break;
+               }
+               break;
+               /* answer to release-guard*/
+               case SS5_STATE_RELEASE_GUARD:
+               switch (p_m_s_signal) {
+                       case SS5_SIGNAL_RECEIVE_RECOG:
+                       if (digit != 'C') {
+                               if (p_type == PORT_TYPE_SS5_OUT)
+                                       PDEBUG(DEBUG_SS5, "%s: incomming release-guard is gone before recognition\n", p_name);
+                               else
+                                       PDEBUG(DEBUG_SS5, "%s: incomming clear forward is gone before recognition\n", p_name);
+                               new_ss5_state(SS5_STATE_IDLE);
+                               break;
+                       }
+                       p_m_s_recog += SS5_DECODER_NPOINTS;
+                       if (p_m_s_recog < SS5_TIMER_RECOG_OTHER)
+                               break;
+                       if (p_type == PORT_TYPE_SS5_OUT)
+                               PDEBUG(DEBUG_SS5, "%s: incomming release-guard is recognized, responding...\n", p_name);
+                       else
+                               PDEBUG(DEBUG_SS5, "%s: incomming clear-forward is recognized, responding...\n", p_name);
+                       new_ss5_signal(SS5_SIGNAL_RECEIVE);
+                       p_m_s_sample_nr = 0;
+                       inband_send_on();
+                       break;
+                       case SS5_SIGNAL_RECEIVE:
+                       if (digit == 'C'
+                        || p_m_s_sample_nr < 256) /* small hack to keep answer for at least some time */
+                        break;
+#if 0
+                       if (digit == 'A') {
+                               PDEBUG(DEBUG_SS5, "%s: received seize right after clear-forward is received\n", p_name);
+                               new_state(PORT_STATE_IDLE);
+                               goto seize;
+                       }
+#endif
+                       /* if clear forward stops right after recognition on the incomming side,
+                        * the release guard signal stops and may be too short to be recognized at the outgoing side.
+                        * to prevent this, a timer can be started to force a release guard that is long
+                        * enough to be recognized on the outgoing side.
+                        * this will prevent braking via blueboxing (other tricks may still be possible).
+                        */
+                       if ((p_m_mISDNport->ss5 & SS5_FEATURE_RELEASEGUARDTIMER)
+                        && p_m_s_sample_nr < SS5_TIMER_RELEASE_GUARD)
+                               break;
+                       if (p_type == PORT_TYPE_SS5_OUT)
+                               PDEBUG(DEBUG_SS5, "%s: incomming release-guard is gone after responding\n", p_name);
+                       else
+                               PDEBUG(DEBUG_SS5, "%s: incomming clear-forward is gone after responding\n", p_name);
+                       if (p_type == PORT_TYPE_SS5_OUT) {
+                               release_guard_ind();
+                               new_ss5_signal(SS5_SIGNAL_DELAY);
+                               p_m_s_recog = 0; /* use recog to delay */
+                               PDEBUG(DEBUG_SS5, "%s: incomming release-guard on outgoing interface starting delay to prevent ping-pong\n", p_name);
+                       } else {
+                               clear_forward_ind();
+                               new_ss5_state(SS5_STATE_IDLE);
+                       }
+                       break;
+                       case SS5_SIGNAL_DELAY:
+                       if (digit == 'A') {
+                               PDEBUG(DEBUG_SS5, "%s: received seize right after release guard is gone, continue with seize\n", p_name);
+                               new_state(PORT_STATE_IDLE);
+                               goto seize;
+                       }
+                       p_m_s_recog += SS5_DECODER_NPOINTS;
+                       if (p_m_s_recog < SS5_TIMER_RELEASE_DELAY)
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: delay time over, going idle\n", p_name);
+                       new_ss5_state(SS5_STATE_IDLE);
+                       new_state(PORT_STATE_IDLE);
+                       p_type = PORT_TYPE_SS5_IDLE;
+                       break;
+               }
+               break;
+               /* wait time to recognize forward transfer */
+               case SS5_STATE_FORWARD_TRANSFER:
+               if (p_m_s_signal == SS5_SIGNAL_RECEIVE_RECOG) {
+                       if (digit != 'B') {
+                               PDEBUG(DEBUG_SS5, "%s: incomming forward-transfer is gone before recognition\n", p_name);
+                               new_ss5_state(SS5_STATE_IDLE);
+                               break;
+                       }
+                       p_m_s_recog += SS5_DECODER_NPOINTS;
+                       if (p_m_s_recog < SS5_TIMER_RECOG_OTHER)
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: incomming forward-transfer is recognized, responding, if BELL feature was selected...\n", p_name);
+                       new_ss5_signal(SS5_SIGNAL_RECEIVE);
+#if 0
+                       p_m_s_sample_nr = 0;
+                       inband_send_on();
+#endif
+                       break;
+               }
+               if (digit != 'B') {
+                       PDEBUG(DEBUG_SS5, "%s: incomming forward-transfer is gone after recognition\n", p_name);
+                       new_ss5_state(SS5_STATE_IDLE);
+                       forward_transfer_ind();
+                       break;
+               }
+               break;
+               /* dialing is received */
+               case SS5_STATE_DIAL_IN:
+               if (strchr("1234567890*#abc", digit)) {
+                       if (p_m_s_signal != SS5_SIGNAL_DIGIT_PAUSE)
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: incomming digit '%c' is recognized\n", p_name, digit);
+                       new_ss5_signal(SS5_SIGNAL_DIGIT_ON);
+                       digit_ind(digit);
+               } else {
+                       if (p_m_s_signal != SS5_SIGNAL_DIGIT_ON)
+                               break;
+                       PDEBUG(DEBUG_SS5, "%s: incomming digit is gone after recognition\n", p_name);
+                       new_ss5_signal(SS5_SIGNAL_DIGIT_PAUSE);
+               }
+               break;
+               case SS5_STATE_DIAL_IN_PULSE:
+               if (digit == 'B')
+                       pulse_ind(1);
+               else
+                       pulse_ind(0);
+               break;
+       }
+
+       /* something more to decode ? */
+       if (count != len)
+               goto again;
+}
+
+
+/*
+ * signalling sender
+ *
+ * this function generates tones and assembles dial string with digits and pause
+ * the result is sent to mISDN. it uses the ss5_encode() function.
+ * except for dialing and forward-transfer, tones are continuous and will not change state.
+ */
+int Pss5::inband_send(unsigned char *buffer, int len)
+{
+       int count = 0; /* sample counter */
+       int duration;
+       char digit;
+       int tocode, tosend;
+
+       switch(p_m_s_state) {
+               /* turn off transmitter in idle state */
+               case SS5_STATE_IDLE:
+               inband_send_off();
+               break;
+
+               case SS5_STATE_SEIZING:
+               if (p_m_s_signal != SS5_SIGNAL_SEND_ON
+                && p_m_s_signal != SS5_SIGNAL_SEND_ON_RECOG)
+                       break;
+               duration = -1; /* continuous */
+               digit = 'A';
+               send:
+               /* how much samples do we have left */
+               if (duration < 0)
+                       tocode = len;
+               else
+                       tocode = duration - p_m_s_sample_nr;
+               if (tocode > 0) {
+                       if (tocode > len)
+                               tocode = len;
+                       ss5_encode(buffer, tocode, digit, p_m_s_sample_nr);
+                       /* increase counters */
+                       p_m_s_sample_nr += tocode;
+                       count += tocode;
+               }
+               /* more to come ? */
+               if (duration > 0 && p_m_s_sample_nr >= duration) {
+                       PDEBUG(DEBUG_SS5, "%s: sending tone '%c' complete, starting delay\n", p_name, digit);
+                       if (p_m_s_state == SS5_STATE_DOUBLE_SEIZE) {
+                               do_release(CAUSE_NOCHANNEL, LOCATION_BEYOND);
+                               break;
+                       }
+                       new_ss5_state(SS5_STATE_DELAY);
+                       p_m_s_sample_nr = 0;
+               }
+#if 0
+               /* stop sending if too long */
+               if (duration < 0 && p_m_s_sample_nr >= SS5_TIMER_MAX_SIGNAL) {
+                       PDEBUG(DEBUG_SS5, "%s: sending tone '%c' too long, stopping\n", p_name, digit);
+                       inband_send_off();
+                       break;
+               }
+#endif
+               break;
+               /* incomming seizing */
+               case SS5_STATE_PROCEED_TO_SEND:
+               if (p_m_s_signal != SS5_SIGNAL_RECEIVE)
+                       break;
+               duration = -1; /* continuous */
+               digit = 'B';
+               goto send;
+
+               case SS5_STATE_BUSY_FLASH:
+               case SS5_STATE_CLEAR_BACK:
+               if (p_m_s_signal != SS5_SIGNAL_SEND_ON
+                && p_m_s_signal != SS5_SIGNAL_SEND_ON_RECOG)
+                       break;
+               duration = -1; /* continuous */
+               digit = 'B';
+               goto send;
+
+               case SS5_STATE_ANSWER:
+               if (p_m_s_signal != SS5_SIGNAL_SEND_ON
+                && p_m_s_signal != SS5_SIGNAL_SEND_ON_RECOG)
+                       break;
+               duration = -1; /* continuous */
+               digit = 'A';
+               goto send;
+
+               case SS5_STATE_ACK_BUSY_FLASH:
+               case SS5_STATE_ACK_ANSWER:
+               case SS5_STATE_ACK_CLEAR_BACK:
+               if (p_m_s_signal != SS5_SIGNAL_RECEIVE)
+                       break;
+               duration = -1; /* continuous */
+               digit = 'A';
+               goto send;
+
+#if 0
+               case SS5_STATE_FORWARD_TRANSFER:
+               if (p_m_s_signal != SS5_SIGNAL_RECEIVE)
+                       break;
+               /* only on bell systems continue and acknowledge tone */
+               if (!(p_m_mISDNport->ss5 & SS5_FEATURE_BELL))
+                       break;
+               duration = SS5_TIMER_FORWARD;
+               digit = 'B';
+               goto send;
+#endif
+
+               case SS5_STATE_CLEAR_FORWARD:
+               if (p_m_s_signal != SS5_SIGNAL_SEND_ON
+                && p_m_s_signal != SS5_SIGNAL_SEND_ON_RECOG)
+                       break;
+               duration = -1; /* continuous */
+               digit = 'C';
+               goto send;
+
+               case SS5_STATE_RELEASE_GUARD:
+               if (p_m_s_signal != SS5_SIGNAL_RECEIVE
+                && p_m_s_signal != SS5_SIGNAL_DELAY)
+                       break;
+               /* prevent from sending release guard too long */
+               if (p_m_s_sample_nr >= SS5_TIMER_RELEASE_MAX)
+                       break;
+               duration = -1; /* continuous */
+               digit = 'C';
+               goto send;
+
+               case SS5_STATE_DIAL_OUT:
+               if ((p_m_mISDNport->ss5 & SS5_FEATURE_PULSEDIALING))
+                       count = inband_dial_pulse(buffer, len, count);
+               else
+                       count = inband_dial_mf(buffer, len, count);
+               break;
+               break;
+
+               case SS5_STATE_DELAY:
+               tosend = len - count;
+               memset(buffer+count, audio_s16_to_law[0], tosend);
+               p_m_s_sample_nr += tosend;
+               count += tosend;
+               if (p_m_s_sample_nr >= SS5_TIMER_AFTER_SIGNAL) {
+                       PDEBUG(DEBUG_SS5, "%s: delay done, ready for next signal\n", p_name);
+                       new_ss5_state(SS5_STATE_IDLE);
+                       inband_send_off();
+               }
+               break;
+
+               case SS5_STATE_DOUBLE_SEIZE:
+               duration = SS5_TIMER_DOUBLE_SEIZE;
+               digit = 'A';
+               goto send;
+
+               /* nothing to send */
+               default:
+               PERROR("inband signalling is turned on, but no signal is processed here.");
+               new_ss5_state(SS5_STATE_IDLE);
+               inband_send_off();
+               return 0;
+       }
+
+       /* return (partly) filled buffer */
+       return count;
+}
+
+
+int Pss5::inband_dial_mf(unsigned char *buffer, int len, int count)
+{
+       int duration;
+       int tocode, tosend;
+       char digit;
+
+       /* dialing
+        *
+        * p_m_s_dial: digits to be dialed
+        * p_m_s_digit_i: current digit counter
+        * p_m_s_signal: current signal state
+        * p_m_s_sample_nr: current sample number
+        */
+       again:
+       /* get digit and duration */
+       digit = p_m_s_dial[p_m_s_digit_i];
+       if (!digit) { /* if end of string reached */
+               new_ss5_state(SS5_STATE_DELAY);
+               p_m_s_sample_nr = 0;
+               return count;
+       }
+       if (p_m_s_signal == SS5_SIGNAL_DIGIT_ON) {
+               if (!p_m_s_digit_i) // first digit
+                       duration = SS5_TIMER_KP;
+               else
+                       duration = SS5_TIMER_DIGIT;
+       } else {
+               duration = SS5_TIMER_PAUSE;
+       }
+       /* end of digit/pause ? */
+       if (p_m_s_sample_nr >= duration) {
+               p_m_s_sample_nr = 0;
+               if (p_m_s_signal == SS5_SIGNAL_DIGIT_PAUSE)
+                       new_ss5_signal(SS5_SIGNAL_DIGIT_ON);
+               else {
+                       new_ss5_signal(SS5_SIGNAL_DIGIT_PAUSE);
+                       p_m_s_digit_i++;
+                       goto again;
+               }
+       }
+       /* how much samples do we have left */
+       tosend = len - count;
+       tocode = duration - p_m_s_sample_nr;
+       if (tocode < 0)
+               FATAL("sample_nr overrun duration");
+       if (tosend < tocode)
+               tocode = tosend;
+       /* digit or pause */
+       if (p_m_s_signal == SS5_SIGNAL_DIGIT_PAUSE) {
+               memset(buffer+count, audio_s16_to_law[0], tocode);
+//             printf("coding pause %d bytes\n", tocode);
+       } else {
+               ss5_encode(buffer+count, tocode, digit, p_m_s_sample_nr);
+//             printf("coding digit '%c' %d bytes\n", digit, tocode);
+       }
+       /* increase counters */
+       p_m_s_sample_nr += tocode;
+       count += tocode;
+       /* can we take more ? */
+       if (len != count)
+               goto again;
+       return count;
+}
+
+
+int Pss5::inband_dial_pulse(unsigned char *buffer, int len, int count)
+{
+       int tocode, tosend;
+       int duration;
+       char digit;
+
+       /* dialing
+        *
+        * p_m_s_dial: digits to be dialed
+        * p_m_s_digit_i: current digit counter
+        * p_m_s_signal: current signal state
+        * p_m_s_sample_nr: current sample number
+        */
+       again:
+       /* get digit */
+       digit = p_m_s_dial[p_m_s_digit_i];
+       if (!digit) { /* if end of string reached */
+               new_ss5_state(SS5_STATE_DELAY);
+               p_m_s_sample_nr = 0;
+               return count;
+       }
+       /* convert digit to pulse */
+       switch (digit) {
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+               case '8':
+               case '9':
+               digit -= '0';
+               break;
+               case '0':
+               digit = 10;
+               break;
+               case '*':
+               digit = 11;
+               break;
+               case '#':
+               digit = 12;
+               break;
+               default:
+               p_m_s_digit_i++;
+               goto again;
+       }
+       /* get duration */
+       if (p_m_s_signal == SS5_SIGNAL_DIGIT_ON) {
+               if (p_m_s_pulsecount & 1)
+                       duration = BELL_TIMER_MAKE; /* loop closed */
+               else
+                       duration = BELL_TIMER_BREAK; /* loop open, tone */
+       } else {
+               duration = BELL_TIMER_PAUSE;
+       }
+       /* end of digit/pause ? */
+       if (p_m_s_sample_nr >= duration) {
+               p_m_s_sample_nr = 0;
+               if (p_m_s_signal == SS5_SIGNAL_DIGIT_PAUSE) {
+                       new_ss5_signal(SS5_SIGNAL_DIGIT_ON);
+                       PDEBUG(DEBUG_SS5, "%s: starting pusling digit '%c'\n", p_name, digit);
+               } else {
+                       p_m_s_pulsecount++; /* toggle pulse */
+                       if (!(p_m_s_pulsecount & 1)) {
+                               /* pulse now on again, but if end is reached... */
+                               if (p_m_s_pulsecount == (digit<<1)) {
+                                       new_ss5_signal(SS5_SIGNAL_DIGIT_PAUSE);
+                                       p_m_s_pulsecount = 0;
+                                       p_m_s_digit_i++;
+                                       goto again;
+                               }
+                       }
+               }
+       }
+       /* how much samples do we have left */
+       tosend = len - count;
+       tocode = duration - p_m_s_sample_nr;
+       if (tocode < 0)
+               FATAL("sample_nr overrun duration");
+       if (tosend < tocode)
+               tocode = tosend;
+       /* digit or pause */
+       if (p_m_s_signal == SS5_SIGNAL_DIGIT_PAUSE
+        || (p_m_s_pulsecount&1)) /* ...or currently on and no pulse */
+               memset(buffer+count, audio_s16_to_law[0], tocode);
+       else
+               ss5_encode(buffer+count, tocode, 'B', p_m_s_sample_nr);
+       /* increase counters */
+       p_m_s_sample_nr += tocode;
+       count += tocode;
+       /* can we take more ? */
+       if (len != count)
+               goto again;
+       return count;
+}
+
+
+/*
+ * start signal
+ */ 
+void Pss5::start_signal(int state)
+{
+       PDEBUG(DEBUG_SS5, "%s: starting singal '%s'\n", p_name, ss5_state_name[state]);
+       /* start signal */
+       new_ss5_state(state);
+       if (state == SS5_STATE_DIAL_OUT) {
+               p_m_s_digit_i = 0;
+               p_m_s_pulsecount = 0;
+               new_ss5_signal(SS5_SIGNAL_DIGIT_ON);
+       } else
+               new_ss5_signal(SS5_SIGNAL_SEND_ON);
+
+       /* double seize must continue the current seize tone, so don't reset sample_nr */
+       if (state != SS5_STATE_DOUBLE_SEIZE) {
+               /* (re)set sound phase to 0 */
+               p_m_s_sample_nr = 0;
+       }
+
+       /* turn on inband transmitter */
+       inband_send_on();
+}
+
+
+/*
+ * handles all indications
+ */
+void Pss5::seizing_ind(void)
+{
+       ss5_trace_header(p_m_mISDNport, this, SS5_SEIZING_IND, p_m_b_channel);
+       end_trace();
+
+       new_state(PORT_STATE_IN_SETUP);
+}
+
+void Pss5::digit_ind(char digit)
+{
+       int i;
+       char string[128] = "", dial[128] = "";
+       int dash, first_digit, last_was_digit;
+
+       /* add digit */
+       SCCAT(p_m_s_dial, digit);
+
+       if (p_state == PORT_STATE_IN_SETUP)
+               new_state(PORT_STATE_IN_OVERLAP);
+
+       /* not last digit ? */
+       if (digit != 'c')
+               return;
+
+       /* parse string */
+       dash = 0; /* dash must be used next time */
+       first_digit = 1;
+       last_was_digit = 0;
+       p_dialinginfo.ntype = INFO_NTYPE_UNKNOWN;
+       for (i = 0; p_m_s_dial[i]; i++) {
+               if (dash || (last_was_digit && (p_m_s_dial[i]<'0' || p_m_s_dial[i]>'9')))
+                       SCCAT(string, '-');
+               dash = 0;
+               last_was_digit = 0;
+               switch(p_m_s_dial[i]) {
+                       case '1':
+                       case '2':
+                       case '3':
+                       case '4':
+                       case '5':
+                       case '6':
+                       case '7':
+                       case '8':
+                       case '9':
+                       case '0':
+                       if (first_digit)
+                               dash = 1;
+                       first_digit = 0;
+                       last_was_digit = 1;
+                       SCCAT(string, p_m_s_dial[i]);
+                       SCCAT(dial, p_m_s_dial[i]);
+                       break;
+                       case '*':
+                       SCAT(string, "C11");
+                       SCCAT(dial, p_m_s_dial[i]);
+                       dash = 1;
+                       break;
+                       case '#':
+                       SCAT(string, "C12");
+                       SCCAT(dial, p_m_s_dial[i]);
+                       dash = 1;
+                       break;
+                       case 'a':
+                       SCAT(string, "KP1");
+                       SCCAT(dial, p_m_s_dial[i]);
+                       dash = 1;
+                       break;
+                       case 'b':
+                       SCAT(string, "KP2");
+                       SCCAT(dial, p_m_s_dial[i]);
+                       dash = 1;
+                       break;
+                       case 'c':
+                       SCAT(string, "ST");
+                       dash = 1;
+                       break;
+                       default:
+                       break;
+               }
+       }
+       ss5_trace_header(p_m_mISDNport, this, SS5_DIALING_IND, p_m_b_channel);
+       add_trace("string", NULL, "%s", string);
+       add_trace("number", NULL, "%s", dial);
+       end_trace();
+       new_ss5_state(SS5_STATE_IDLE);
+
+       do_setup(dial);
+       new_state(PORT_STATE_IN_PROCEEDING);
+}
+
+void Pss5::pulse_ind(int on)
+{
+       struct lcr_msg *message;
+       char dial[3] = "a.";
+
+       if (p_m_s_signal == SS5_SIGNAL_PULSE_OFF) {
+               if (on) {
+                       /* pulse turns on */
+                       p_m_s_recog = 0;
+                       new_ss5_signal(SS5_SIGNAL_PULSE_ON);
+                       /* pulse turns of, count it */
+                       p_m_s_pulsecount++;
+                       PDEBUG(DEBUG_SS5, "%s: pulse turns on, counting\n", p_name);
+               } else {
+                       /* pulse remains off */
+                       p_m_s_recog += SS5_DECODER_NPOINTS;
+                       /* not recognized end of digit, we wait... */
+                       if (p_m_s_recog < BELL_TIMER_RECOG_END)
+                               return;
+                       PDEBUG(DEBUG_SS5, "%s: pulse remains off, counted %d pulses\n", p_name, p_m_s_pulsecount);
+                       if (p_m_s_pulsecount >= 12)
+                               dial[1] = '#';
+                       else if (p_m_s_pulsecount == 11)
+                               dial[1] = '*';
+                       else if (p_m_s_pulsecount == 10)
+                               dial[1] = '0';
+                       else
+                               dial[1] = p_m_s_pulsecount + '0';
+                       ss5_trace_header(p_m_mISDNport, this, SS5_DIALING_IND, p_m_b_channel);
+                       add_trace("digit", NULL, "%s", dial+1);
+                       add_trace("pulses", NULL, "%d", p_m_s_pulsecount);
+                       end_trace();
+                       /* special star release feature */
+                       if ((p_m_mISDNport->ss5 & SS5_FEATURE_STAR_RELEASE) && dial[1] == '*') {
+                               ss5_trace_header(p_m_mISDNport, this, SS5_DIALING_IND, p_m_b_channel);
+                               add_trace("star", NULL, "releases call");
+                               end_trace();
+                               goto star_release;
+                       }
+                       if (p_state == PORT_STATE_IN_SETUP) {
+                               /* sending digit as setup */
+                               do_setup(dial); /* include 'a' == KP1 */
+                               new_state(PORT_STATE_IN_OVERLAP);
+                       } else {
+                               /* sending digit as information */
+                               message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_INFORMATION);
+                               SCPY(message->param.information.id, dial+1);
+                               message_put(message);
+                       }
+                               new_ss5_state(SS5_STATE_IDLE);
+                       /* done rx pulses, return to idle */
+                       new_ss5_state(SS5_STATE_IDLE);
+               }
+       } else {
+               if (on) {
+                       /* pulse remains on */
+                       p_m_s_recog += SS5_DECODER_NPOINTS;
+               } else {
+                       /* pulse turns off */
+                       if (p_m_s_recog >= BELL_TIMER_RECOG_HANGUP) {
+                               PDEBUG(DEBUG_SS5, "%s: long pulse turns off, releasing\n", p_name);
+                               ss5_trace_header(p_m_mISDNport, this, SS5_DIALING_IND, p_m_b_channel);
+                               add_trace("longtone", NULL, "releases call");
+                               end_trace();
+                               star_release:
+                               /* long pulse is gone, release current connection, if any */
+                               while(p_epointlist) {
+                                       message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
+                                       message->param.disconnectinfo.location = LOCATION_BEYOND;
+                                       message->param.disconnectinfo.cause = CAUSE_NORMAL;
+                                       message_put(message);
+                                       free_epointlist(p_epointlist);
+                               }
+                               set_tone("", NULL);
+                               /* return to setup state */
+                               new_state(PORT_STATE_IN_SETUP);
+                               new_ss5_state(SS5_STATE_IDLE);
+                               return;
+                       }
+                       PDEBUG(DEBUG_SS5, "%s: short pulse turns off, releasing\n", p_name);
+                       p_m_s_recog = 0;
+                       new_ss5_signal(SS5_SIGNAL_PULSE_OFF);
+               }
+       }
+}
+
+void Pss5::proceed_to_send_ind(void)
+{
+       ss5_trace_header(p_m_mISDNport, this, SS5_PROCEED_TO_SEND_IND, p_m_b_channel);
+       end_trace();
+
+       SCPY(p_m_s_dial, p_dialinginfo.id);
+       start_signal(SS5_STATE_DIAL_OUT);
+
+       new_state(PORT_STATE_OUT_OVERLAP);
+}
+
+void Pss5::busy_flash_ind(void)
+{
+       struct lcr_msg *message;
+
+       ss5_trace_header(p_m_mISDNport, this, SS5_BUSY_FLASH_IND, p_m_b_channel);
+       end_trace();
+
+       /* busy before dialing ? */
+       if (!p_epointlist)
+               return;
+
+       if (!(p_m_mISDNport->ss5 & SS5_FEATURE_NODISCONNECT)) {
+               message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DISCONNECT);
+               message->param.disconnectinfo.location = LOCATION_BEYOND;
+               message->param.disconnectinfo.cause = CAUSE_BUSY;
+               message_put(message);
+       }
+
+       new_state(PORT_STATE_IN_DISCONNECT);
+}
+
+void Pss5::answer_ind(void)
+{
+       struct lcr_msg *message;
+
+       ss5_trace_header(p_m_mISDNport, this, SS5_ANSWER_IND, p_m_b_channel);
+       end_trace();
+
+       /* answer before dialing ? */
+       if (!p_epointlist)
+               return;
+
+       /* already connected */
+       if (!(p_m_mISDNport->ss5 & SS5_FEATURE_CONNECT)) {
+               message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
+               message_put(message);
+       }
+
+       new_state(PORT_STATE_CONNECT);
+}
+
+void Pss5::forward_transfer_ind(void)
+{
+//     struct lcr_msg *message;
+
+       ss5_trace_header(p_m_mISDNport, this, SS5_FORWARD_TRANSFER_IND, p_m_b_channel);
+       end_trace();
+
+#if 0
+       /* if BELL flavor bluebox flag is set, use it to seize a new line */
+       if (!(p_m_mISDNport->ss5 & SS5_FEATURE_BELL))
+               return;
+
+       /* special BELL flavor hack to clear a line and seize a new one */
+       while(p_epointlist) {
+               message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
+               message->param.disconnectinfo.location = LOCATION_BEYOND;
+               message->param.disconnectinfo.cause = CAUSE_NORMAL;
+               message_put(message);
+               free_epointlist(p_epointlist);
+       }
+       set_tone("", NULL);
+       new_state(PORT_STATE_IN_SETUP);
+#endif
+}
+
+void Pss5::clear_back_ind(void)
+{
+       struct lcr_msg *message;
+
+       ss5_trace_header(p_m_mISDNport, this, SS5_CLEAR_BACK_IND, p_m_b_channel);
+       end_trace();
+
+       /* nobody? */
+       if (!p_epointlist)
+               return;
+
+       if (!(p_m_mISDNport->ss5 & SS5_FEATURE_NODISCONNECT)) {
+               message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DISCONNECT);
+               message->param.disconnectinfo.location = LOCATION_BEYOND;
+               message->param.disconnectinfo.cause = CAUSE_NORMAL;
+               message_put(message);
+       }
+
+       new_state(PORT_STATE_IN_DISCONNECT);
+}
+
+void Pss5::clear_forward_ind(void)
+{
+       struct lcr_msg *message;
+
+       ss5_trace_header(p_m_mISDNport, this, SS5_CLEAR_FORWARD_IND, p_m_b_channel);
+       end_trace();
+
+       new_state(PORT_STATE_IDLE);
+       set_tone("", NULL);
+       p_type = PORT_TYPE_SS5_IDLE;
+
+       /* someone ? */
+       if (!p_epointlist)
+               return;
+
+       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
+       message->param.disconnectinfo.location = LOCATION_BEYOND;
+       message->param.disconnectinfo.cause = CAUSE_NORMAL;
+       message_put(message);
+       free_epointlist(p_epointlist);
+
+}
+
+void Pss5::release_guard_ind(void)
+{
+       struct lcr_msg *message;
+
+       ss5_trace_header(p_m_mISDNport, this, SS5_RELEASE_GUARD_IND, p_m_b_channel);
+       end_trace();
+
+       set_tone("", NULL);
+
+       /* someone ? */
+       if (!p_epointlist)
+               return;
+
+       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
+       message->param.disconnectinfo.location = LOCATION_BEYOND;
+       message->param.disconnectinfo.cause = CAUSE_NORMAL;
+       message_put(message);
+       free_epointlist(p_epointlist);
+}
+
+void Pss5::double_seizure_ind(void)
+{
+       ss5_trace_header(p_m_mISDNport, this, SS5_DOUBLE_SEIZURE_IND, p_m_b_channel);
+       end_trace();
+       ss5_trace_header(p_m_mISDNport, this, SS5_DOUBLE_SEIZURE_REQ, p_m_b_channel);
+       end_trace();
+
+       /* start double seizure sequence, so remote exchange will recognize it */
+       start_signal(SS5_STATE_DOUBLE_SEIZE);
+}
+
+
+/*
+ * shuts down by sending a clear forward and releasing endpoint
+ */
+void Pss5::do_release(int cause, int location)
+{
+       struct lcr_msg *message;
+
+       /* sending release to endpoint */
+       while(p_epointlist) {
+               message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
+               message->param.disconnectinfo.location = location;
+               message->param.disconnectinfo.cause = cause;
+               message_put(message);
+               free_epointlist(p_epointlist);
+       }
+
+       /* start clear-forward */
+       ss5_trace_header(p_m_mISDNport, this, SS5_CLEAR_FORWARD_REQ, p_m_b_channel);
+       end_trace();
+       start_signal(SS5_STATE_CLEAR_FORWARD);
+
+       new_state(PORT_STATE_RELEASE);
+}
+
+
+/*
+ * create endpoint and send setup
+ */
+void Pss5::do_setup(char *dial)
+{
+       class Endpoint *epoint;
+       struct lcr_msg *message;
+
+       SCPY(p_dialinginfo.id, dial);
+       p_dialinginfo.sending_complete = 1;
+       p_callerinfo.present = INFO_PRESENT_NOTAVAIL;
+       p_callerinfo.screen = INFO_SCREEN_NETWORK;
+       p_callerinfo.ntype = INFO_NTYPE_NOTPRESENT;
+       p_callerinfo.isdn_port = p_m_portnum;
+       SCPY(p_callerinfo.interface, p_m_mISDNport->ifport->interface->name);
+
+       p_capainfo.bearer_capa = INFO_BC_AUDIO;
+       p_capainfo.bearer_info1 = (options.law=='a')?3:2;
+       p_capainfo.bearer_mode = INFO_BMODE_CIRCUIT;
+       p_capainfo.hlc = INFO_HLC_NONE;
+       p_capainfo.exthlc = INFO_HLC_NONE;
+       p_capainfo.source_mode = B_MODE_TRANSPARENT;
+
+       /* create endpoint */
+       if (p_epointlist)
+               FATAL("Incoming call but already got an endpoint.\n");
+       if (!(epoint = new Endpoint(p_serial, 0)))
+               FATAL("No memory for Endpoint instance\n");
+       if (!(epoint->ep_app = new DEFAULT_ENDPOINT_APP(epoint, 0))) //incoming
+               FATAL("No memory for Endpoint Application instance\n");
+       epointlist_new(epoint->ep_serial);
+
+       /* send setup message to endpoit */
+       message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_SETUP);
+       message->param.setup.isdn_port = p_m_portnum;
+       message->param.setup.port_type = p_type;
+//     message->param.setup.dtmf = !p_m_mISDNport->ifport->nodtmf;
+       memcpy(&message->param.setup.dialinginfo, &p_dialinginfo, sizeof(struct dialing_info));
+       memcpy(&message->param.setup.callerinfo, &p_callerinfo, sizeof(struct caller_info));
+       memcpy(&message->param.setup.redirinfo, &p_redirinfo, sizeof(struct redir_info));
+       memcpy(&message->param.setup.capainfo, &p_capainfo, sizeof(struct capa_info));
+       message_put(message);
+
+}
+
+
+/*
+ * 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
+ */
+
+/* MESSAGE_SETUP */
+void Pss5::message_setup(unsigned int epoint_id, int message_id, union parameter *param)
+{
+       struct lcr_msg *message;
+       int i;
+       char string[128] = "", dial[128] = "";
+       int dash, first_digit, last_was_digit;
+
+       if (p_epointlist) {
+               PERROR("endpoint already exist.\n");
+               message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
+               message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
+               message->param.disconnectinfo.cause = CAUSE_UNSPECIFIED;
+               message_put(message);
+               return;
+       }
+
+       /* copy setup infos to port */
+       memcpy(&p_callerinfo, &param->setup.callerinfo, sizeof(p_callerinfo));
+       memcpy(&p_dialinginfo, &param->setup.dialinginfo, sizeof(p_dialinginfo));
+       memcpy(&p_capainfo, &param->setup.capainfo, sizeof(p_capainfo));
+       memcpy(&p_redirinfo, &param->setup.redirinfo, sizeof(p_redirinfo));
+       /* screen outgoing caller id */
+       do_screen(1, p_callerinfo.id, sizeof(p_callerinfo.id), &p_callerinfo.ntype, &p_callerinfo.present, p_m_mISDNport->ifport->interface);
+       do_screen(1, p_callerinfo.id2, sizeof(p_callerinfo.id2), &p_callerinfo.ntype2, &p_callerinfo.present2, p_m_mISDNport->ifport->interface);
+
+       /* parse dial string  */
+       dash = 0; /* dash must be used next time */
+       first_digit = 1;
+       last_was_digit = 0;
+       for (i = 0; p_dialinginfo.id[i]; i++) {
+               if (dash || (last_was_digit && (p_dialinginfo.id[i]<'0' || p_dialinginfo.id[i]>'9')))
+                       SCCAT(string, '-');
+               dash = 0;
+               last_was_digit = 0;
+               switch(p_dialinginfo.id[i]) {
+                       case '1':
+                       case '2':
+                       case '3':
+                       case '4':
+                       case '5':
+                       case '6':
+                       case '7':
+                       case '8':
+                       case '9':
+                       case '0':
+                       if (i && first_digit)
+                               dash = 1;
+                       first_digit = 0;
+                       last_was_digit = 1;
+                       SCCAT(string, p_dialinginfo.id[i]);
+                       SCCAT(dial, p_dialinginfo.id[i]);
+                       break;
+                       case '*':
+                       SCAT(string, "C11");
+                       SCCAT(dial, '*');
+                       dash = 1;
+                       break;
+                       case '#':
+                       SCAT(string, "C12");
+                       SCCAT(dial, '#');
+                       dash = 1;
+                       break;
+                       case 'a':
+                       SCAT(string, "KP1");
+                       SCCAT(dial, 'a');
+                       dash = 1;
+                       break;
+                       case 'b':
+                       SCAT(string, "KP2");
+                       SCCAT(dial, 'b');
+                       dash = 1;
+                       break;
+                       case 'c':
+                       SCAT(string, "ST");
+                       SCCAT(dial, 'c');
+                       dash = 1;
+                       case 'K':
+                       i++;
+                       if (p_dialinginfo.id[i] != 'P')
+                               goto dial_error;
+                       i++;
+                       if (p_dialinginfo.id[i] == '1') {
+                               SCAT(string, "KP1");
+                               SCCAT(dial, 'a');
+                               dash = 1;
+                               break;
+                       }
+                       if (p_dialinginfo.id[i] == '2') {
+                               SCAT(string, "KP2");
+                               SCCAT(dial, 'b');
+                               dash = 1;
+                               break;
+                       }
+                       goto dial_error;
+                       case 'C':
+                       i++;
+                       if (p_dialinginfo.id[i] != '1')
+                               goto dial_error;
+                       i++;
+                       if (p_dialinginfo.id[i] == '1') {
+                               SCAT(string, "C11");
+                               SCCAT(dial, 'a');
+                               dash = 1;
+                               break;
+                       }
+                       if (p_dialinginfo.id[i] == '2') {
+                               SCAT(string, "C12");
+                               SCCAT(dial, 'b');
+                               dash = 1;
+                               break;
+                       }
+                       goto dial_error;
+                       case 'S':
+                       i++;
+                       if (p_dialinginfo.id[i] != 'T')
+                               goto dial_error;
+                       SCAT(string, "ST");
+                       SCCAT(dial, 'c');
+                       dash = 1;
+                       break;
+                       default:
+                       break;
+               }
+               /* stop, if ST */
+               if (dial[0] && dial[strlen(dial)-1] == 'c')
+                       break;
+       }
+       /* terminate */
+       if (dial[0] && dial[strlen(dial)-1]!='c') {
+               SCCAT(string, '-');
+               SCAT(string, "ST");
+               SCCAT(dial, 'c');
+       }
+
+       /* error in dial string */
+       if (!dial[0]) {
+               dial_error:
+               ss5_trace_header(p_m_mISDNport, this, SS5_DIALING_REQ, p_m_b_channel);
+               add_trace("string", NULL, "%s", p_dialinginfo.id);
+               if (!dial[0])
+                       add_trace("error", NULL, "no number", dial);
+               else if (dial[0]!='a' && dial[0]!='b')
+                       add_trace("error", NULL, "number must start with KP1/KP2", dial);
+               else
+                       add_trace("error", NULL, "illegal format", dial);
+               end_trace();
+               message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
+               message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
+               message->param.disconnectinfo.cause = CAUSE_INVALID;
+               message_put(message);
+               return;
+       }
+
+       /* copy new dial string */
+       SCPY(p_dialinginfo.id, dial);
+
+       /* attach only if not already */
+       epointlist_new(epoint_id);
+
+       ss5_trace_header(p_m_mISDNport, this, SS5_DIALING_REQ, p_m_b_channel);
+       add_trace("string", NULL, "%s", string);
+       add_trace("type", NULL, "%s", (dial[0]=='b')?"international":"national");
+       add_trace("number", NULL, "%s", dial);
+       end_trace();
+       /* connect auto path */
+       if ((p_m_mISDNport->ss5 & SS5_FEATURE_CONNECT)) {
+               message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
+               message_put(message);
+       } else {
+               message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_PROCEEDING);
+               message_put(message);
+       }
+
+       /* start seizing */
+       ss5_trace_header(p_m_mISDNport, this, SS5_SEIZING_REQ, p_m_b_channel);
+       end_trace();
+       new_ss5_state(SS5_STATE_SEIZING);
+       new_ss5_signal(SS5_SIGNAL_SEND_ON);
+       p_m_s_sample_nr = 0;
+       inband_send_on();
+
+       p_type = PORT_TYPE_SS5_OUT;
+       new_state(PORT_STATE_OUT_SETUP);
+}
+
+/* MESSAGE_CONNECT */
+void Pss5::message_connect(unsigned int epoint_id, int message_id, union parameter *param)
+{
+       memcpy(&p_connectinfo, &param->connectinfo, sizeof(p_connectinfo));
+
+       if (p_state != PORT_STATE_CONNECT) {
+               new_state(PORT_STATE_CONNECT);
+               p_m_s_answer = 1;
+       }
+
+       set_tone("", NULL);
+}
+
+/* MESSAGE_DISCONNECT */
+void Pss5::message_disconnect(unsigned int epoint_id, int message_id, union parameter *param)
+{
+       /* disconnect and clear forward (release guard) */
+//     if ((p_type==PORT_TYPE_SS5_IN && !p_m_mISDNport->tones) /* incomming exchange with no tones */
+if (0   || p_type==PORT_TYPE_SS5_OUT) { /* outgoing exchange */
+               do_release(param->disconnectinfo.cause, param->disconnectinfo.location);
+               return;
+       }
+
+       ss5_trace_header(p_m_mISDNport, this, SS5_CLEAR_BACK_REQ, p_m_b_channel);
+       end_trace();
+       start_signal(SS5_STATE_CLEAR_BACK);
+
+       new_state(PORT_STATE_OUT_DISCONNECT);
+}
+
+/* MESSAGE_RELEASE */
+void Pss5::message_release(unsigned int epoint_id, int message_id, union parameter *param)
+{
+       do_release(param->disconnectinfo.cause, param->disconnectinfo.location);
+}
+
+
+/*
+ * endpoint sends messages to the port
+ */
+int Pss5::message_epoint(unsigned int epoint_id, int message_id, union parameter *param)
+{
+       if (PmISDN::message_epoint(epoint_id, message_id, param))
+               return(1);
+
+       switch(message_id) {
+               case MESSAGE_SETUP: /* dial-out command received from epoint */
+               if (p_state!=PORT_STATE_IDLE) {
+                       PERROR("Pss5(%s) ignoring setup because isdn port is not in idle state (or connected for sending display info).\n", p_name);
+                       break;
+               }
+               if (p_epointlist && p_state==PORT_STATE_IDLE)
+                       FATAL("Pss5(%s): epoint pointer is set in idle state, how bad!!\n", p_name);
+               message_setup(epoint_id, message_id, param);
+               break;
+
+               case MESSAGE_CONNECT: /* call of endpoint is connected */
+               message_connect(epoint_id, message_id, param);
+               break;
+
+               case MESSAGE_DISCONNECT: /* call has been disconnected */
+               message_disconnect(epoint_id, message_id, param);
+               break;
+
+               case MESSAGE_RELEASE: /* release isdn port */
+               message_release(epoint_id, message_id, param);
+               break;
+
+               default:
+               PDEBUG(DEBUG_SS5, "Pss5(%s) ss5 port with (caller id %s) received an unhandled message: %d\n", p_name, p_callerinfo.id, message_id);
+       }
+
+       return(1);
+}
+
diff --git a/ss5.h b/ss5.h
new file mode 100644 (file)
index 0000000..6897abd
--- /dev/null
+++ b/ss5.h
@@ -0,0 +1,86 @@
+/*****************************************************************************\
+**                                                                           **
+** LCR                                                                       **
+**                                                                           **
+**---------------------------------------------------------------------------**
+** Copyright: Andreas Eversberg                                              **
+**                                                                           **
+** ss5-port header file                                                      **
+**                                                                           **
+\*****************************************************************************/ 
+
+#define SS5_ENABLE                     0x00000001
+#define SS5_FEATURE_CONNECT            0x00000002
+#define SS5_FEATURE_NODISCONNECT       0x00000004
+#define SS5_FEATURE_RELEASEGUARDTIMER  0x00000008
+#define SS5_FEATURE_BELL               0x00000010
+#define SS5_FEATURE_PULSEDIALING       0x00000020
+#define SS5_FEATURE_DELAY              0x00000040
+#define SS5_FEATURE_STAR_RELEASE       0x00000080
+#define SS5_FEATURE_SUPPRESS           0x00000100
+
+/* SS5 port classes */
+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);
+
+       int p_m_s_state; /* current signalling state */
+       int p_m_s_signal; /* current state of current signal */
+       char p_m_s_dial[64]; /* current dialing register */
+       int p_m_s_digit_i; /* current digit of register counter */
+       int p_m_s_pulsecount; /* counts pule dialing half cycles */
+       char p_m_s_last_digit; /* stores last digit to fill short signal losses */
+       int p_m_s_signal_loss; /* sample counter for loss of signal check */
+       int p_m_s_decoder_count; /* samples currently decoded */
+       unsigned char p_m_s_decoder_buffer[SS5_DECODER_NPOINTS]; /* buffer for storing one goertzel window */
+       unsigned char p_m_s_delay_digits[3000/SS5_DECODER_NPOINTS]; /* delay buffer for received digits */
+       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);
+       int p_m_s_answer; /* queued signal */
+       int p_m_s_busy_flash; /* queued signal */
+       int p_m_s_clear_back; /* queued signal */
+
+       void _new_ss5_state(int state, const char *func, int line);
+       void _new_ss5_signal(int signal, const char *func, int line);
+       void inband_receive(unsigned char *buffer, int len);
+       int inband_send(unsigned char *buffer, int len);
+       int inband_dial_mf(unsigned char *buffer, int len, int count);
+       int inband_dial_pulse(unsigned char *buffer, int len, int count);
+       void start_signal(int);
+       void start_outgoing(void);
+       void do_release(int cause, int location);
+       void do_setup(char *digit);
+
+       void seizing_ind(void);
+       void digit_ind(char digit);
+       void pulse_ind(int on);
+       void proceed_to_send_ind(void);
+       void busy_flash_ind(void);
+       void answer_ind(void);
+       void forward_ind(void);
+       void clear_back_ind(void);
+       void clear_forward_ind(void);
+       void release_guard_ind(void);
+       void double_seizure_ind(void);
+       void forward_transfer_ind(void);
+       void message_setup(unsigned int epoint_id, int message_id, union parameter *param);
+       void message_connect(unsigned int epoint_id, int message_id, union parameter *param);
+       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);
+
+};
+
+#define new_ss5_state(a) _new_ss5_state(a, __FUNCTION__, __LINE__)
+#define new_ss5_signal(a) _new_ss5_signal(a, __FUNCTION__, __LINE__)
+
+void ss5_create_channel(struct mISDNport *mISDNport, int i);
+class Pss5 *ss5_hunt_line(struct mISDNport *mISDNport);
+
diff --git a/ss5_decode.c b/ss5_decode.c
new file mode 100644 (file)
index 0000000..541f6be
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * SS5 signal decoder.
+ *
+ * Copyright            by Andreas Eversberg (jolly@eversberg.eu)
+ *                     based on different decoders such as ISDN4Linux
+ *                     copyright by Karsten Keil
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "main.h"
+#include "ss5_decode.h"
+
+/* enable level debugging */
+//#define DEBUG_LEVELS
+
+#define NCOEFF         8       /* number of frequencies to be analyzed */
+
+#define MIN_DB         0.01995262 /* -17 db */
+#define DIFF_DB                0.31622777 /*  -5 db */
+#define SNR            1.3     /* noise may not exceed signal by that factor */
+
+/* For DTMF recognition:
+ * 2 * cos(2 * PI * k / N) precalculated for all k
+ */
+static signed long long cos2pik[NCOEFF] =
+{
+       /* k = 2*cos(2*PI*f/8000), k << 15 
+        * 700, 900, 1100, 1300, 1500, 1700, 2400, 2600 */
+       55879, 49834, 42562, 34242, 25080, 15299, -20252, -29753
+};
+
+/* detection matrix for two frequencies */
+static char decode_two[8][8] =
+{
+       {' ', '1', '2', '4', '7', '*', ' ', ' '}, /* * = code 11 */
+       {'1', ' ', '3', '5', '8', '#', ' ', ' '}, /* # = code 12 */
+       {'2', '3', ' ', '6', '9', 'a', ' ', ' '}, /* a = KP1 */
+       {'4', '5', '6', ' ', '0', 'b', ' ', ' '}, /* b = KP2 */
+       {'7', '8', '9', '0', ' ', 'c', ' ', ' '}, /* c = ST */
+       {'*', '#', 'a', 'b', 'c', ' ', ' ', ' '},
+       {' ', ' ', ' ', ' ', ' ', ' ', ' ', 'C'}, /* C = 2600+2400 */
+       {' ', ' ', ' ', ' ', ' ', ' ', 'C', ' '}
+};
+
+static char decode_one[8] =
+       {' ', ' ', ' ', ' ', ' ', ' ', 'A', 'B'}; /* A = 2400, B = 2600 */
+/*
+ * calculate the coefficients of the given sample and decode
+ */
+
+char ss5_decode(unsigned char *data, int len)
+{
+       signed short buf[len];
+       signed long sk, sk1, sk2, low, high;
+       int k, n, i;
+       int f1 = 0, f2 = 0, f3 = 0;
+       double result[NCOEFF], power, noise, snr;
+       signed long long cos2pik_;
+       char digit = ' ';
+
+       /* convert samples */
+       for (i = 0; i < len; i++)
+               buf[i] = audio_law_to_s32[*data++];
+
+       /* now we have a full buffer of signed long samples - we do goertzel */
+       for (k = 0; k < NCOEFF; k++) {
+               sk = 0;
+               sk1 = 0;
+               sk2 = 0;
+               cos2pik_ = cos2pik[k];
+               for (n = 0; n < len; n++) {
+                       sk = ((cos2pik_*sk1)>>15) - sk2 + buf[n];
+                       sk2 = sk1;
+                       sk1 = sk;
+               }
+               sk >>= 8;
+               sk2 >>= 8;
+               if (sk > 32767 || sk < -32767 || sk2 > 32767 || sk2 < -32767)
+                       PERROR("Tone-Detection overflow\n");
+               /* compute |X(k)|**2 */
+               result[k] = sqrt (
+                               (sk * sk) -
+                               (((cos2pik[k] * sk) >> 15) * sk2) +
+                               (sk2 * sk2)
+                       ) / len / 62; /* level of 1 is 0 db*/
+       }
+
+       /* now we do noise level calculation */
+       low = 32767;
+       high = -32768;
+       for (n = 0; n < len; n++) {
+               sk = buf[n];
+               if (sk < low)
+                       low = sk;
+               if (sk > high)
+                       high = sk;
+       }
+       noise = ((double)(high-low) / 65536.0);
+
+       /* find the two loudest frequencies + one less lower frequency to detect noise */
+       power = 0.0;
+       for (i = 0; i < NCOEFF; i++) {
+               if (result[i] > power) {
+                       power = result[i];
+                       f1 = i;
+               }
+       }
+       power = 0.0;
+       for (i = 0; i < NCOEFF; i++) {
+               if (i != f1 && result[i] > power) {
+                       power = result[i];
+                       f2 = i;
+               }
+       }
+       power = 0.0;
+       for (i = 0; i < NCOEFF; i++) {
+               if (i != f1  && i != f2 && result[i] > power) {
+                       power = result[i];
+                       f3 = i;
+               }
+       }
+
+#if 0
+       /* check one frequency */
+       if (result[f1] > MIN_DB /* must be at least -17 db */
+        && result[f1]*DIFF_DB > result[f2]) /* must be 5 db above other tones */
+               digit = decode_one[f1];
+       /* check two frequencies */
+       if (result[f1] > MIN_DB && result[f2] > MIN_DB /* must be at lease -17 db */
+        && result[f1]*DIFF_DB <= result[f2] /* f2 must be not less than 5 db below f1 */
+        && result[f1]*DIFF_DB > result[f3]) /* f1 must be 5 db above other tones */
+               digit = decode_two[f1][f2];
+#endif
+       snr = 0;
+       /* check one frequency */
+       if (result[f1] > MIN_DB /* must be at least -17 db */
+        && result[f1]*SNR > noise) { /*  */
+               digit = decode_one[f1];
+               snr = result[f1] / noise;
+       }
+       /* check two frequencies */
+       if (result[f1] > MIN_DB && result[f2] > MIN_DB /* must be at lease -17 db */
+        && result[f1]*DIFF_DB <= result[f2] /* f2 must be not less than 5 db below f1 */
+        && (result[f1]+result[f2])*SNR > noise) { /* */
+               digit = decode_two[f1][f2];
+               snr = (result[f1]+result[f2]) / noise;
+       }
+
+       /* debug powers */
+#ifdef DEBUG_LEVELS
+       for (i = 0; i < NCOEFF; i++)
+               printf("%d:%3d %c ", i, (int)(result[i]*100), (f1==i || f2==i)?'*':' ');
+       printf("N:%3d digit:%c snr=%3d\n", (int)(noise*100), digit, (int)(snr*100));
+#endif
+
+       return digit;
+}
+
+void ss5_test_decode(void)
+{
+#ifdef DEBUG_LEVELS
+       double phase;
+       int i, j;
+       signed short sample;
+
+       unsigned char buffer[SS5_DECODER_NPOINTS];
+       for (i = 0; i < 4000; i += 10) {
+               phase = 2.0 * 3.14159265 * i / 8000.0;
+               for (j = 0; j < SS5_DECODER_NPOINTS; j++) {
+                       sample = sin(phase * j) * 1000;
+                       buffer[j] = audio_s16_to_law[sample & 0xffff];
+               }
+               printf("FRQ:%04d:", i);
+               ss5_decode(buffer, SS5_DECODER_NPOINTS);
+       }
+#endif
+}
+
diff --git a/ss5_decode.h b/ss5_decode.h
new file mode 100644 (file)
index 0000000..4acaecb
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+ * SS5 signal decoder header file
+ *
+ */
+
+#define SS5_DECODER_NPOINTS             80 /* size of goertzel window */
+
+char ss5_decode(unsigned char *data, int len);
+void ss5_test_decode(void);
+
diff --git a/ss5_encode.c b/ss5_encode.c
new file mode 100644 (file)
index 0000000..0b2ab54
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * SS5 signal coder.
+ *
+ * Copyright            by Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "main.h"
+#include "ss5_encode.h"
+
+       /* 2*PI*f/8000 */
+static double ss5_freq[9][2] = {
+       {0.0, 0},               /* 0: 0 */
+       {700,   0.19952623},    /* 1: 700, -7db */
+       {900,   0.19952623},    /* 2: 900, -7db */
+       {1100,  0.19952623},    /* 3: 1100, -7db */
+       {1300,  0.19952623},    /* 4: 1300, -7db */
+       {1500,  0.19952623},    /* 5: 1500, -7db */
+       {1700,  0.19952623},    /* 6: 1700, -7db */
+       {2400,  0.12589254},    /* 7: 2400, -9db */
+       {2600,  0.12589254},    /* 8: 2600, -9db */
+};
+
+static char ss5_digits[][3] = {
+       {'1', 1, 2},
+       {'2', 1, 3},
+       {'3', 2, 3},
+       {'4', 1, 4},
+       {'5', 2, 4},
+       {'6', 3, 4},
+       {'7', 1, 5},
+       {'8', 2, 5},
+       {'9', 3, 5},
+       {'0', 4, 5},
+       {'*', 1, 6}, /* code 11 */
+       {'#', 2, 6}, /* code 12 */
+       {'a', 3, 6}, /* KP1 */
+       {'b', 4, 6}, /* KP2 */
+       {'c', 5, 6}, /* ST */
+       {'A', 7, 0}, /* 2400 answer, acknowledge */
+       {'B', 8, 0}, /* 2600 busy */
+       {'C', 7, 8}, /* 2600+2400 clear forward */
+       {0 , 0, 0},
+};
+
+static unsigned char sintab[15+3][8192]; /* sine tables of about one second sound (error <1Hz) */
+
+/* generate sine tables */
+void ss5_sine_generate(void)
+{
+       int i, j;
+       int cycles1, cycles2;
+       double vol1, vol2, phase1, phase2;
+       signed short sample;
+
+       for (i = 0; i < 15+3; i++) {
+               /* how many cycles are within 8192 samples (rounded!) */
+               cycles1 = (int)(ss5_freq[(int)ss5_digits[i][1]][0] / 8000.0 * 8192.0 + 0.5);
+               cycles2 = (int)(ss5_freq[(int)ss5_digits[i][2]][0] / 8000.0 * 8192.0 + 0.5);
+               /* how much phase shift within one cycle */
+               phase1 = 2.0 * 3.14159265 * cycles1 / 8192.0;
+               phase2 = 2.0 * 3.14159265 * cycles2 / 8192.0;
+               /* volume */
+               vol1 = ss5_freq[(int)ss5_digits[i][1]][1] * 32768.0;
+               vol2 = ss5_freq[(int)ss5_digits[i][2]][1] * 32768.0;
+               for (j = 0; j < 8192; j++) {
+                       sample = (int)(sin(phase1 * j) * vol1);
+                       sample += (int)(sin(phase2 * j) * vol2);
+                       sintab[i][j] = audio_s16_to_law[sample & 0xffff];
+               }
+       }
+}
+
+/* encode digit at given sample_nr with given lengt and return law-encoded audio */
+unsigned char *ss5_encode(unsigned char *buffer, int len, char digit, int sample_nr)
+{
+       int i, j;
+
+       /* get frequency from digit */
+       i = 0;
+       while(ss5_digits[i][0]) {
+               if (digit == ss5_digits[i][0])
+                       break;
+               i++;
+       }
+       if (!ss5_digits[i][0]) {
+               PERROR("Digit '%c' does not exist.\n", digit);
+               memset(buffer, audio_s16_to_law[0], sizeof(buffer));
+               return buffer;
+       }
+
+       /* copy tones */
+       for (j = 0; j < len; j++)
+               *buffer++ = sintab[i][sample_nr++ & 8191];
+
+       return buffer;
+}
+
+
diff --git a/ss5_encode.h b/ss5_encode.h
new file mode 100644 (file)
index 0000000..50e79cf
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * SS5 signal coder header file
+ */
+
+void ss5_sine_generate(void);
+unsigned char *ss5_encode(unsigned char *buffer, int len, char digit, int sample_nr);
+