Added support for signalling system no. 5.

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
This commit is contained in:
Andreas Eversberg 2009-09-26 13:20:29 +02:00
parent 96124cb106
commit 323cbc387b
25 changed files with 2801 additions and 132 deletions

View File

@ -56,6 +56,14 @@ GSM_LIB = /usr/lib/libgsm.a ./openbsc/src/libbsc.a ./openbsc/src/libmsc.a ./open
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
@ -84,9 +92,9 @@ install-exec-hook:
$(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 \

View 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 \
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)
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 \
@ -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_SS5_FALSE = @ENABLE_SS5_FALSE@
ENABLE_SS5_TRUE = @ENABLE_SS5_TRUE@
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
#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
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 \
@ -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)/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@

3
README
View File

@ -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.
-> 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"
- Added experimental CCITT No. 5 signalling system. (for educational purpose)

View File

@ -136,7 +136,7 @@ void EndpointAppPBX::trace_header(const char *name, int direction)
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,
@ -648,92 +648,108 @@ foundif:
/* 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) {
#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("channel", NULL, "%d", *channel);
end_trace();
}
} 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);
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);
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);
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;
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 = i+1+(i>=15);
trace_header("CHANNEL SELECTION (selecting free channel)", DIRECTION_NONE);
*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;
}
i++;
break;
}
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);
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);
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);
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;
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; /* found channel */
selchannel = selchannel->next;
}
if (*channel)
break; /* found channel */
selchannel = selchannel->next;
}
/* 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);
#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
@ -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);
#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");
@ -1124,6 +1150,11 @@ void EndpointAppPBX::out_setup(void)
}
/* 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

35
configure vendored
View File

@ -640,6 +640,8 @@ ac_includes_default="\
ac_subst_vars='LTLIBOBJS
POW_LIB
LIBOBJS
ENABLE_SS5_FALSE
ENABLE_SS5_TRUE
ENABLE_GSM_FALSE
ENABLE_GSM_TRUE
LIBCRYPTO
@ -735,6 +737,7 @@ enable_dependency_tracking
with_asterisk
with_ssl
with_gsm
with_ss5
'
ac_precious_vars='build_alias
host_alias
@ -1385,7 +1388,9 @@ Optional Packages:
--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:
@ -6316,6 +6321,27 @@ else
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
@ -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
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

View File

@ -98,7 +98,7 @@ AS_IF([test "x$with_ssl" != xno],
# 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"])
@ -115,6 +115,16 @@ AS_IF([test "x$with_gsm" != xno],
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])

View File

@ -12,6 +12,7 @@
#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

View File

@ -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)
{

2
ie.cpp
View File

@ -616,7 +616,7 @@ void Pdss1::enc_ie_cause(struct l3_msg *l3m, int location, int cause)
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;
}

View File

@ -916,6 +916,56 @@ static int inter_gsm(struct interface *interface, char *filename, int line, char
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"},
#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}
};
@ -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
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
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;
@ -1366,7 +1432,7 @@ void load_port(struct interface_port *ifport)
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;
@ -1376,6 +1442,8 @@ void load_port(struct interface_port *ifport)
SCPY(ifport->portname, mISDNport->name);
/* set defaults */
set_defaults(ifport);
/* load static port instances */
mISDNport_static(mISDNport);
} else {
ifport->block = 2; /* not available */
}

View 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 */
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 */

View 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;
case ADMIN_STATE_RELEASE:
color(blue);
addstr("'release'");
break;
default:
color(blue);
addstr("'--NONE--'");
@ -729,7 +733,7 @@ const char *admin_state(int sock, char *argv[])
break;
case B_STATE_ACTIVE:
color(green);
addstr("busy ");
addstr("active ");
break;
case B_STATE_DEACTIVATING:
color(yellow);

View File

@ -188,4 +188,5 @@ enum {
ADMIN_STATE_CONNECT,
ADMIN_STATE_IN_DISCONNECT,
ADMIN_STATE_OUT_DISCONNECT,
ADMIN_STATE_RELEASE,
};

152
mISDN.cpp
View File

@ -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_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_inband_send_on = 0;
p_m_inband_receive_on = 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);
//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);
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);
@ -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);
}
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
@ -1218,15 +1235,20 @@ int PmISDN::handler(void)
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;
/* 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 */
@ -1372,6 +1394,10 @@ void PmISDN::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len
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.
*/
@ -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)
{
int oldconf, newconf;
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:
//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;
//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:
@ -1744,7 +1762,7 @@ int mISDN_handler(void)
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 */
@ -1947,7 +1965,7 @@ int mISDN_handler(void)
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);
@ -2027,7 +2045,7 @@ int mISDN_getportbyname(int sock, int cnt, char *portname)
/*
* 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;
@ -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));
if (gsm) {
/* gsm audio is always active */
if (gsm | ss5) {
/* gsm/ss5 link is always active */
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;
/* set ss5 params */
if (ss5) {
/* try to keep interface enabled */
l1hold = 1;
l2hold = 1;
}
/* 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->ss5 = ss5;
PDEBUG(DEBUG_ISDN, "Port has %d b-channels.\n", mISDNport->b_num);
i = 0;
while(i < mISDNport->b_num) {
@ -2306,11 +2331,32 @@ 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);
if (mISDNport->ss5)
add_trace("ccitt#5", NULL, "enabled");
end_trace();
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)
*/
@ -2418,7 +2464,7 @@ void PmISDN::txfromup(unsigned char *data, int length)
/* 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
@ -2451,3 +2497,61 @@ void PmISDN::txfromup(unsigned char *data, int 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);
}

20
mISDN.h
View File

@ -67,6 +67,9 @@ struct mISDNport {
/* 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;
@ -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);
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);
@ -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_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 */
@ -152,6 +157,7 @@ class PmISDN : public Port
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 */
@ -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 */
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);
};

6
main.c
View File

@ -360,6 +360,12 @@ int main(int argc, char *argv[])
/* 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");

6
main.h
View File

@ -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_SS5 0x0130
//#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_SS5
#include "ss5_encode.h"
#include "ss5_decode.h"
#include "ss5.h"
#endif
#include "vbox.h"
#include "join.h"
#include "joinpbx.h"

5
port.h
View File

@ -18,6 +18,7 @@
#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
@ -28,6 +29,10 @@
/* 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

View 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_RELEASE:
response->am[num].u.p.state = ADMIN_STATE_RELEASE;
break;
default:
response->am[num].u.p.state = ADMIN_STATE_IDLE;
}

1972
ss5.cpp Normal file

File diff suppressed because it is too large Load Diff

86
ss5.h Normal file
View File

@ -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);

181
ss5_decode.c Normal file
View File

@ -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
}

10
ss5_decode.h Normal file
View File

@ -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);

102
ss5_encode.c Normal file
View File

@ -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;
}

7
ss5_encode.h Normal file
View File

@ -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);