Compare commits
75 Commits
Author | SHA1 | Date |
---|---|---|
Andreas Eversberg | 91ceb56495 | |
Andreas Eversberg | 7aa290154a | |
Andreas Eversberg | 5268c0fc9b | |
Andreas Eversberg | c2bf2598b1 | |
Andreas Eversberg | f489ce381c | |
Andreas Eversberg | dd23410c52 | |
Andreas Eversberg | aa07ba4a48 | |
Andreas Eversberg | 40fddb83a7 | |
Andreas Eversberg | a53c366853 | |
Andreas Eversberg | 56467e746e | |
Andreas Eversberg | ebfab5792d | |
Andreas Eversberg | a4483db3af | |
Andreas Eversberg | e59752a1cb | |
Andreas Eversberg | c6d0c2452c | |
Andreas Eversberg | 8b00969b4a | |
Andreas Eversberg | 3aee692b1c | |
Andreas Eversberg | 687e963fd4 | |
Andreas Eversberg | 905a1b22cf | |
Andreas Eversberg | 85fe6f55bb | |
Andreas Eversberg | 61d531cedc | |
Andreas Eversberg | 9c0893403d | |
Andreas Eversberg | 36dc03731a | |
Andreas Eversberg | ef33793758 | |
Andreas Eversberg | 5d972a0077 | |
Andreas Eversberg | a260178387 | |
Andreas Eversberg | 3ebe29e28f | |
Andreas Eversberg | 5addae556a | |
Andreas Eversberg | ba46786de7 | |
Andreas Eversberg | 58937c291b | |
Andreas Eversberg | fbb97bdd73 | |
Andreas Eversberg | 920fada1bc | |
Andreas Eversberg | 276b423051 | |
Andreas Eversberg | 4994a59962 | |
Andreas Eversberg | 852990f2e3 | |
Andreas Eversberg | 6a4a58d4b1 | |
Andreas Eversberg | 513f3bba5a | |
Andreas Eversberg | aeae94c547 | |
Andreas Eversberg | 2a23a1c9cf | |
Andreas Eversberg | 65dac95ff0 | |
Andreas Eversberg | b64a17c748 | |
Andreas Eversberg | 7b4561f7cf | |
Andreas Eversberg | ca0d2f6091 | |
Andreas Eversberg | ff619cc54b | |
Andreas Eversberg | 595d1091bc | |
Andreas Eversberg | f457d272b6 | |
Andreas Eversberg | 3cabd7f1a6 | |
Andreas Eversberg | ff5e1d4947 | |
Andreas Eversberg | dec8a8adea | |
Andreas Eversberg | d91fd97210 | |
Andreas Eversberg | 33eea99531 | |
Andreas Eversberg | 83d8cbdd3a | |
Andreas Eversberg | 3586180ae2 | |
Andreas Eversberg | f5e54dfa77 | |
Andreas Eversberg | 0325fbedc1 | |
Andreas Eversberg | afa29770cc | |
Andreas Eversberg | a8428e073d | |
Andreas Eversberg | 331c19228e | |
Andreas Eversberg | e066033770 | |
Andreas Eversberg | a02b14892a | |
Andreas Eversberg | eeb77df89f | |
Andreas Eversberg | ae1a7de2b8 | |
Andreas Eversberg | 537301f656 | |
Andreas Eversberg | d9cea69a90 | |
Andreas Eversberg | ee6d366feb | |
Andreas Eversberg | ffeded74af | |
Harald Welte | 65dfe9080b | |
Andreas Eversberg | 5af684bed2 | |
Andreas Eversberg | fe6a4b91f8 | |
Andreas Eversberg | 1560a36dfc | |
Andreas Eversberg | 5fd7b682f4 | |
Andreas Eversberg | a77eb4020f | |
Andreas Eversberg | 0bb6303c34 | |
Andreas Eversberg | 04e99eb55f | |
Andreas Eversberg | 4f9292b698 | |
Andreas Eversberg | 10b269d24c |
|
@ -0,0 +1,46 @@
|
|||
Makefile
|
||||
Makefile.in
|
||||
.deps
|
||||
.libs
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
*.pc
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
config.h*
|
||||
config.sub
|
||||
config.log
|
||||
config.status
|
||||
config.guess
|
||||
configure
|
||||
compile
|
||||
depcomp
|
||||
missing
|
||||
ltmain.sh
|
||||
install-sh
|
||||
stamp-h1
|
||||
#libosmo-abis-*
|
||||
tests/*_test
|
||||
*~
|
||||
|
||||
# libtool and e.g. arm-poky-linux-gnueabi-libtool
|
||||
*libtool
|
||||
|
||||
.tarball-version
|
||||
.version
|
||||
.dirstamp
|
||||
|
||||
# tests
|
||||
tests/atconfig
|
||||
tests/package.m4
|
||||
tests/testsuite
|
||||
tests/testsuite.log
|
||||
|
||||
# vi/vim files
|
||||
*.sw?
|
||||
|
||||
src/libg711/libg711.a
|
||||
src/libecho/libecho.a
|
||||
src/osmo-v5-le
|
||||
tests/answer_detect
|
11
Makefile
11
Makefile
|
@ -1,11 +0,0 @@
|
|||
LIBS=-lasan -ltalloc -losmocore -losmovty -losmogsm -losmoctrl -losmoabis
|
||||
CFLAGS=-Wall -DPACKAGE_VERSION=\"0.0\"
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $^
|
||||
|
||||
v5le: main.o v5x_data.o lapv5.o
|
||||
$(CC) -o $@ $^ $(LIBS)
|
||||
|
||||
clean:
|
||||
@rm v5le *.o
|
|
@ -0,0 +1,8 @@
|
|||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
AM_CPPFLAGS = $(all_includes)
|
||||
SUBDIRS = src tests
|
||||
|
||||
dist-hook:
|
||||
echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
AC_INIT([osmo-v5],
|
||||
m4_esyscmd([./git-version-gen .tarball-version]),
|
||||
[openbsc@lists.osmocom.org])
|
||||
|
||||
dnl *This* is the root dir, even if an install-sh exists in ../ or ../../
|
||||
AC_CONFIG_AUX_DIR([.])
|
||||
|
||||
AM_INIT_AUTOMAKE([foreign dist-bzip2 no-dist-gzip 1.6 subdir-objects])
|
||||
AC_CONFIG_TESTDIR(tests)
|
||||
|
||||
CFLAGS="$CFLAGS -std=gnu11"
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl include release helper
|
||||
RELMAKE='-include osmo-release.mk'
|
||||
AC_SUBST([RELMAKE])
|
||||
|
||||
dnl checks for programs
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
LT_INIT([pic-only])
|
||||
|
||||
dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang
|
||||
AS_CASE(["$LD"],[*clang*],
|
||||
[AS_CASE(["${host_os}"],
|
||||
[*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])])
|
||||
|
||||
dnl check for pkg-config (explained in detail in libosmocore/configure.ac)
|
||||
AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no)
|
||||
if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
AC_MSG_WARN([You need to install pkg-config])
|
||||
fi
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
CFLAGS="$CFLAGS -Wall"
|
||||
CPPFLAGS="$CPPFLAGS -Wall"
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-sanitize],
|
||||
[Compile with address sanitizer enabled],
|
||||
)],
|
||||
[sanitize=$enableval], [sanitize="no"])
|
||||
if test x"$sanitize" = x"yes"
|
||||
then
|
||||
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
fi
|
||||
|
||||
# The following test is taken from WebKit's webkit.m4
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS -fvisibility=hidden "
|
||||
AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])],
|
||||
[ AC_MSG_RESULT([yes])
|
||||
SYMBOL_VISIBILITY="-fvisibility=hidden"],
|
||||
AC_MSG_RESULT([no]))
|
||||
CFLAGS="$saved_CFLAGS"
|
||||
AC_SUBST(SYMBOL_VISIBILITY)
|
||||
|
||||
dnl Generate the output
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOISDN, libosmoisdn >= 1.8.0)
|
||||
|
||||
AC_ARG_ENABLE(sanitize,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-sanitize],
|
||||
[Compile with address sanitizer enabled],
|
||||
)],
|
||||
[sanitize=$enableval], [sanitize="no"])
|
||||
if test x"$sanitize" = x"yes"
|
||||
then
|
||||
CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined"
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(werror,
|
||||
[AS_HELP_STRING(
|
||||
[--enable-werror],
|
||||
[Turn all compiler warnings into errors, with exceptions:
|
||||
a) deprecation (allow upstream to mark deprecation without breaking builds);
|
||||
b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
|
||||
]
|
||||
)],
|
||||
[werror=$enableval], [werror="no"])
|
||||
if test x"$werror" = x"yes"
|
||||
then
|
||||
WERROR_FLAGS="-Werror"
|
||||
WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
|
||||
WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
|
||||
CFLAGS="$CFLAGS $WERROR_FLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
|
||||
fi
|
||||
|
||||
AX_CHECK_X86_FEATURES()
|
||||
|
||||
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
||||
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
|
||||
|
||||
AC_OUTPUT(
|
||||
src/libecho/Makefile
|
||||
src/libg711/Makefile
|
||||
src/Makefile
|
||||
tests/Makefile
|
||||
Makefile)
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,151 @@
|
|||
#!/bin/sh
|
||||
# Print a version string.
|
||||
scriptversion=2010-01-28.01
|
||||
|
||||
# Copyright (C) 2007-2010 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
|
||||
# It may be run two ways:
|
||||
# - from a git repository in which the "git describe" command below
|
||||
# produces useful output (thus requiring at least one signed tag)
|
||||
# - from a non-git-repo directory containing a .tarball-version file, which
|
||||
# presumes this script is invoked like "./git-version-gen .tarball-version".
|
||||
|
||||
# In order to use intra-version strings in your project, you will need two
|
||||
# separate generated version string files:
|
||||
#
|
||||
# .tarball-version - present only in a distribution tarball, and not in
|
||||
# a checked-out repository. Created with contents that were learned at
|
||||
# the last time autoconf was run, and used by git-version-gen. Must not
|
||||
# be present in either $(srcdir) or $(builddir) for git-version-gen to
|
||||
# give accurate answers during normal development with a checked out tree,
|
||||
# but must be present in a tarball when there is no version control system.
|
||||
# Therefore, it cannot be used in any dependencies. GNUmakefile has
|
||||
# hooks to force a reconfigure at distribution time to get the value
|
||||
# correct, without penalizing normal development with extra reconfigures.
|
||||
#
|
||||
# .version - present in a checked-out repository and in a distribution
|
||||
# tarball. Usable in dependencies, particularly for files that don't
|
||||
# want to depend on config.h but do want to track version changes.
|
||||
# Delete this file prior to any autoconf run where you want to rebuild
|
||||
# files to pick up a version string change; and leave it stale to
|
||||
# minimize rebuild time after unrelated changes to configure sources.
|
||||
#
|
||||
# It is probably wise to add these two files to .gitignore, so that you
|
||||
# don't accidentally commit either generated file.
|
||||
#
|
||||
# Use the following line in your configure.ac, so that $(VERSION) will
|
||||
# automatically be up-to-date each time configure is run (and note that
|
||||
# since configure.ac no longer includes a version string, Makefile rules
|
||||
# should not depend on configure.ac for version updates).
|
||||
#
|
||||
# AC_INIT([GNU project],
|
||||
# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
|
||||
# [bug-project@example])
|
||||
#
|
||||
# Then use the following lines in your Makefile.am, so that .version
|
||||
# will be present for dependencies, and so that .tarball-version will
|
||||
# exist in distribution tarballs.
|
||||
#
|
||||
# BUILT_SOURCES = $(top_srcdir)/.version
|
||||
# $(top_srcdir)/.version:
|
||||
# echo $(VERSION) > $@-t && mv $@-t $@
|
||||
# dist-hook:
|
||||
# echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
case $# in
|
||||
1) ;;
|
||||
*) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
|
||||
esac
|
||||
|
||||
tarball_version_file=$1
|
||||
nl='
|
||||
'
|
||||
|
||||
# First see if there is a tarball-only version file.
|
||||
# then try "git describe", then default.
|
||||
if test -f $tarball_version_file
|
||||
then
|
||||
v=`cat $tarball_version_file` || exit 1
|
||||
case $v in
|
||||
*$nl*) v= ;; # reject multi-line output
|
||||
[0-9]*) ;;
|
||||
*) v= ;;
|
||||
esac
|
||||
test -z "$v" \
|
||||
&& echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
|
||||
fi
|
||||
|
||||
if test -n "$v"
|
||||
then
|
||||
: # use $v
|
||||
elif
|
||||
v=`git describe --abbrev=4 HEAD 2>/dev/null \
|
||||
|| git describe --abbrev=4 --match='v*' HEAD 2>/dev/null` \
|
||||
&& case $v in
|
||||
[0-9]*) ;;
|
||||
v[0-9]*) ;;
|
||||
*) (exit 1) ;;
|
||||
esac
|
||||
then
|
||||
# Is this a new git that lists number of commits since the last
|
||||
# tag or the previous older version that did not?
|
||||
# Newer: v6.10-77-g0f8faeb
|
||||
# Older: v6.10-g0f8faeb
|
||||
case $v in
|
||||
*-*-*) : git describe is okay three part flavor ;;
|
||||
*-*)
|
||||
: git describe is older two part flavor
|
||||
# Recreate the number of commits and rewrite such that the
|
||||
# result is the same as if we were using the newer version
|
||||
# of git describe.
|
||||
vtag=`echo "$v" | sed 's/-.*//'`
|
||||
numcommits=`git rev-list "$vtag"..HEAD | wc -l`
|
||||
v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
|
||||
;;
|
||||
esac
|
||||
|
||||
# Change the first '-' to a '.', so version-comparing tools work properly.
|
||||
# Remove the "g" in git describe's output string, to save a byte.
|
||||
v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
|
||||
else
|
||||
v=UNKNOWN
|
||||
fi
|
||||
|
||||
v=`echo "$v" |sed 's/^v//'`
|
||||
|
||||
# Don't declare a version "dirty" merely because a time stamp has changed.
|
||||
git status > /dev/null 2>&1
|
||||
|
||||
dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
|
||||
case "$dirty" in
|
||||
'') ;;
|
||||
*) # Append the suffix only if there isn't one already.
|
||||
case $v in
|
||||
*-dirty) ;;
|
||||
*) v="$v-dirty" ;;
|
||||
esac ;;
|
||||
esac
|
||||
|
||||
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
|
||||
echo "$v" | tr -d '\012'
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
21
lapv5.h
21
lapv5.h
|
@ -1,21 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <osmocom/abis/lapd.h>
|
||||
|
||||
struct lapv5_instance {
|
||||
struct llist_head list; /* list of LAPV5 instances */
|
||||
bool network_side;
|
||||
|
||||
void (*transmit_cb)(struct msgb *msg, void *cbdata);
|
||||
void *transmit_cbdata;
|
||||
void (*receive_cb)(struct osmo_dlsap_prim *odp, uint16_t dladdr, void *rx_cbdata);
|
||||
void *receive_cbdata;
|
||||
|
||||
struct lapd_profile profile; /* must be a copy */
|
||||
|
||||
struct llist_head sap_list; /* list of SAP/datalinks in this instance */
|
||||
int pcap_fd; /* PCAP file descriptor */
|
||||
char *name; /* human-readable name */
|
||||
};
|
||||
|
||||
int lapv5ef_rx(struct v5x_link *link, struct msgb *msg);
|
|
@ -0,0 +1,13 @@
|
|||
AC_DEFUN([AX_CHECK_X86_FEATURES],
|
||||
[m4_foreach_w(
|
||||
[ax_x86_feature],
|
||||
[mmx popcnt sse sse2 sse3 sse4.1 sse4.2 sse4a avx avx2 avx512f fma fma4 bmi bmi2],
|
||||
[AX_GCC_X86_CPU_SUPPORTS(ax_x86_feature,
|
||||
[X86_FEATURE_CFLAGS="$X86_FEATURE_CFLAGS -m[]ax_x86_feature"],
|
||||
[])
|
||||
])
|
||||
AC_SUBST([X86_FEATURE_CFLAGS])
|
||||
m4_ifval([$1],[$1],
|
||||
[CFLAGS="$CFLAGS $X86_FEATURE_CFLAGS"])
|
||||
$2
|
||||
])
|
|
@ -0,0 +1,44 @@
|
|||
AC_DEFUN_ONCE([_AX_GCC_X86_CPU_INIT],
|
||||
[AC_LANG_PUSH([C])
|
||||
AC_CACHE_CHECK([for gcc __builtin_cpu_init function],
|
||||
[ax_cv_gcc_check_x86_cpu_init],
|
||||
[AC_RUN_IFELSE(
|
||||
[AC_LANG_PROGRAM([#include <stdlib.h>],
|
||||
[__builtin_cpu_init ();])
|
||||
],
|
||||
[ax_cv_gcc_check_x86_cpu_init=yes],
|
||||
[ax_cv_gcc_check_x86_cpu_init=no])])
|
||||
AC_LANG_POP([C])
|
||||
AS_IF([test "X$ax_cv_gcc_check_x86_cpu_init" = "Xno"],
|
||||
[AC_MSG_ERROR([Need GCC to support X86 CPU features tests])])
|
||||
])
|
||||
|
||||
AC_DEFUN([AX_GCC_X86_CPU_SUPPORTS],
|
||||
[AC_REQUIRE([AC_PROG_CC])
|
||||
AC_REQUIRE([_AX_GCC_X86_CPU_INIT])
|
||||
AC_LANG_PUSH([C])
|
||||
AS_VAR_PUSHDEF([gcc_x86_feature], [AS_TR_SH([ax_cv_gcc_x86_cpu_supports_$1])])
|
||||
AC_CACHE_CHECK([for x86 $1 instruction support],
|
||||
[gcc_x86_feature],
|
||||
[AC_RUN_IFELSE(
|
||||
[AC_LANG_PROGRAM( [#include <stdlib.h> ],
|
||||
[ __builtin_cpu_init ();
|
||||
if (__builtin_cpu_supports("$1"))
|
||||
return 0;
|
||||
return 1;
|
||||
])],
|
||||
[gcc_x86_feature=yes],
|
||||
[gcc_x86_feature=no]
|
||||
)]
|
||||
)
|
||||
AC_LANG_POP([C])
|
||||
AS_VAR_IF([gcc_x86_feature],[yes],
|
||||
[AC_DEFINE(
|
||||
AS_TR_CPP([HAVE_$1_INSTRUCTIONS]),
|
||||
[1],
|
||||
[Define if $1 instructions are supported])
|
||||
$2],
|
||||
[$3]
|
||||
)
|
||||
AS_VAR_POPDEF([gcc_x86_feature])
|
||||
])
|
|
@ -1,12 +0,0 @@
|
|||
e1_input
|
||||
e1_line 0 driver dahdi
|
||||
e1_line 0 port 7
|
||||
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging print file 1
|
||||
logging level llapd debug
|
||||
logging level linp debug
|
|
@ -0,0 +1,31 @@
|
|||
AM_CPPFLAGS = $(all_includes)
|
||||
AM_CFLAGS= -Wall -Wextra -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOCTRL_CFLAGS)
|
||||
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
|
||||
COMMONLIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCTRL_LIBS)
|
||||
|
||||
SUBDIRS = libecho libg711
|
||||
|
||||
bin_PROGRAMS = osmo-v5-le
|
||||
|
||||
osmo_v5_le_SOURCES = logging.c \
|
||||
ph_socket.c \
|
||||
v5x_data.c \
|
||||
v5le_vty.c \
|
||||
lapv5.c \
|
||||
layer1.c \
|
||||
v5x_protocol.c \
|
||||
v5x_l1_fsm.c \
|
||||
v5x_le_ctrl_fsm.c \
|
||||
v5x_le_port_fsm.c \
|
||||
v5x_le_pstn_fsm.c \
|
||||
v52_le_lcp_fsm.c \
|
||||
v52_le_bcc_fsm.c \
|
||||
v52_le_pp_fsm.c \
|
||||
v5x_le_management.c \
|
||||
main.c
|
||||
|
||||
osmo_v5_le_LDADD = $(COMMON_LA) \
|
||||
$(COMMONLIBS) \
|
||||
libecho/libecho.a \
|
||||
libg711/libg711.a \
|
||||
-lm
|
|
@ -22,24 +22,26 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "lapv5.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/abis/lapd.h>
|
||||
#include <osmocom/abis/lapd_pcap.h>
|
||||
|
||||
#define LAPD_ADDR2(sapi, cr) ((((sapi) & 0x3f) << 2) | (((cr) & 0x1) << 1))
|
||||
#define LAPD_ADDR3(tei) ((((tei) & 0x7f) << 1) | 0x1)
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "v5x_le_port_fsm.h"
|
||||
#include "lapv5.h"
|
||||
#include "layer1.h"
|
||||
#include "logging.h"
|
||||
|
||||
#define LAPD_ADDR1(sapi, cr) ((((sapi) & 0x3f) << 2) | (((cr) & 0x1) << 1))
|
||||
#define LAPD_ADDR2(tei) ((((tei) & 0x7f) << 1) | 0x1)
|
||||
|
||||
#define LAPD_ADDR_SAPI(addr) ((addr) >> 2)
|
||||
#define LAPD_ADDR_CR(addr) (((addr) >> 1) & 0x1)
|
||||
|
@ -132,15 +134,16 @@ static struct lapv5_sap *lapv5_sap_alloc(struct lapv5_instance *li, uint16_t v5d
|
|||
if (!sap)
|
||||
return NULL;
|
||||
|
||||
LOGP(DLLAPD, LOGL_NOTICE, "(%s): LAPV5 Allocating SAP for V5_DLADDR=%u (dl=%p, sap=%p)\n",
|
||||
LOGP(DLLAPD, LOGL_DEBUG, "(%s): LAPV5 Allocating SAP for V5_DLADDR=%u (dl=%p, sap=%p)\n",
|
||||
name, v5dladdr, &sap->dl, sap);
|
||||
|
||||
sap->li = li;
|
||||
sap->dladdr = v5dladdr;
|
||||
dl = &sap->dl;
|
||||
profile = &li->profile;
|
||||
|
||||
k = profile->k[0];
|
||||
LOGP(DLLAPD, LOGL_NOTICE, "(%s): k=%d N200=%d N201=%d T200=%d.%d T203=%d.%d\n",
|
||||
LOGP(DLLAPD, LOGL_DEBUG, "(%s): k=%d N200=%d N201=%d T200=%d.%d T203=%d.%d\n",
|
||||
name, k, profile->n200, profile->n201, profile->t200_sec,
|
||||
profile->t200_usec, profile->t203_sec, profile->t203_usec);
|
||||
lapd_dl_init2(dl, k, 128, profile->n201, name);
|
||||
|
@ -152,8 +155,8 @@ static struct lapv5_sap *lapv5_sap_alloc(struct lapv5_instance *li, uint16_t v5d
|
|||
dl->t200_sec = profile->t200_sec; dl->t200_usec = profile->t200_usec;
|
||||
dl->t203_sec = profile->t203_sec; dl->t203_usec = profile->t203_usec;
|
||||
dl->lctx.dl = &sap->dl;
|
||||
dl->lctx.sapi = v5dladdr & 0xff;
|
||||
dl->lctx.tei = v5dladdr >> 8;
|
||||
dl->lctx.sapi = v5dladdr >> 7;
|
||||
dl->lctx.tei = v5dladdr & 0x7f;
|
||||
dl->lctx.n201 = profile->n201;
|
||||
|
||||
lapd_set_mode(&sap->dl, (li->network_side) ? LAPD_MODE_NETWORK : LAPD_MODE_USER);
|
||||
|
@ -166,7 +169,7 @@ static struct lapv5_sap *lapv5_sap_alloc(struct lapv5_instance *li, uint16_t v5d
|
|||
/* Free SAP instance, including the datalink */
|
||||
static void lapv5_sap_free(struct lapv5_sap *sap)
|
||||
{
|
||||
LOGSAP(sap, LOGL_NOTICE, "LAPV5 Freeing SAP for DLADDR=%u (dl=%p, sap=%p)\n", sap->dladdr, &sap->dl, sap);
|
||||
LOGSAP(sap, LOGL_DEBUG, "LAPV5 Freeing SAP for DLADDR=%u (dl=%p, sap=%p)\n", sap->dladdr, &sap->dl, sap);
|
||||
|
||||
/* free datalink structures and timers */
|
||||
lapd_dl_exit(&sap->dl);
|
||||
|
@ -175,39 +178,41 @@ static void lapv5_sap_free(struct lapv5_sap *sap)
|
|||
talloc_free(sap);
|
||||
}
|
||||
|
||||
/* General input function for any data received for this LAPV5 instance */
|
||||
int lapv5_receive(struct lapv5_instance *li, struct msgb *msg, int *error)
|
||||
/* Receive Data (PH-DATA indication) on the given LAPD Instance */
|
||||
int lapv5_ph_data_ind(struct lapv5_instance *li, struct msgb *msg, int *error)
|
||||
{
|
||||
struct lapd_msg_ctx lctx;
|
||||
struct lapv5_sap *sap;
|
||||
uint16_t dladdr;
|
||||
bool is_isdn;
|
||||
int i;
|
||||
|
||||
/* write to PCAP file, if enabled. */
|
||||
osmo_pcap_lapd_write(li->pcap_fd, OSMO_LAPD_PCAP_INPUT, msg);
|
||||
|
||||
if (!li->enabled) {
|
||||
LOGLI(li, LOGL_DEBUG, "LAPV5 frame ignored, because DL is disabled.\n");
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msgb_pull(msg, msg->l2h - msg->data);
|
||||
|
||||
LOGLI(li, LOGL_DEBUG, "RX: %s\n", osmo_hexdump(msg->data, msg->len));
|
||||
if (msg->len < 2) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPV5 frame receive len %d < 2, ignoring\n", msg->len);
|
||||
*error = LAPD_ERR_BAD_LEN;
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
};
|
||||
msg->l2h = msg->data;
|
||||
|
||||
memset(&lctx, 0, sizeof(lctx));
|
||||
|
||||
i = 0;
|
||||
/* adress field */
|
||||
dladdr = v51_l3_addr_dec(msg->l2h[0] << 8 | msg->l2h[1], &is_isdn);
|
||||
if (!is_isdn) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPV5 frame with single-octet addr not permitted\n");
|
||||
*error = LAPD_ERR_BAD_LEN;
|
||||
return -EINVAL;
|
||||
};
|
||||
dladdr = ((msg->l2h[0] & 0xfe) << 5) | (msg->l2h[1] >> 1);
|
||||
lctx.lpd = 0;
|
||||
lctx.tei = dladdr >> 8;
|
||||
lctx.sapi = dladdr & 0xff;
|
||||
lctx.sapi = dladdr >> 7;
|
||||
lctx.tei = dladdr & 0x7f;
|
||||
lctx.cr = LAPD_ADDR_CR(msg->l2h[0]);
|
||||
i += 2;
|
||||
|
||||
|
@ -219,6 +224,7 @@ int lapv5_receive(struct lapv5_instance *li, struct msgb *msg, int *error)
|
|||
if (msg->len < 4) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPV5 I frame receive len %d < 4, ignoring\n", msg->len);
|
||||
*error = LAPD_ERR_BAD_LEN;
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
lctx.n_recv = LAPD_CTRL_Nr(msg->l2h[i]);
|
||||
|
@ -230,6 +236,7 @@ int lapv5_receive(struct lapv5_instance *li, struct msgb *msg, int *error)
|
|||
if (msg->len < 4 && i == 3) {
|
||||
LOGLI(li, LOGL_ERROR, "LAPV5 S frame receive len %d < 4, ignoring\n", msg->len);
|
||||
*error = LAPD_ERR_BAD_LEN;
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
lctx.n_recv = LAPD_CTRL_Nr(msg->l2h[i]);
|
||||
|
@ -270,34 +277,34 @@ int lapv5_receive(struct lapv5_instance *li, struct msgb *msg, int *error)
|
|||
return lapd_ph_data_ind(msg, &lctx);
|
||||
}
|
||||
|
||||
/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
|
||||
int lapv5_sap_start(struct lapv5_instance *li, uint16_t dladdr)
|
||||
/* Start a (LE-side) SAP for the specified TEI/SAPI on the LAPD instance */
|
||||
int lapv5_dl_est_req(struct lapv5_instance *li, uint16_t dladdr)
|
||||
{
|
||||
struct lapv5_sap *sap;
|
||||
struct osmo_dlsap_prim dp;
|
||||
struct msgb *msg;
|
||||
|
||||
sap = lapv5_sap_find(li, dladdr);
|
||||
if (sap)
|
||||
return -EEXIST;
|
||||
if (!sap) {
|
||||
sap = lapv5_sap_alloc(li, dladdr);
|
||||
if (!sap)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sap = lapv5_sap_alloc(li, dladdr);
|
||||
if (!sap)
|
||||
return -ENOMEM;
|
||||
|
||||
LOGSAP(sap, LOGL_NOTICE, "LAPV5 DL-ESTABLISH request DLADDR=%u\n", dladdr);
|
||||
LOGSAP(sap, LOGL_DEBUG, "LAPV5 DL-ESTABLISH request DLADDR=%u\n", dladdr);
|
||||
|
||||
/* prepare prim */
|
||||
msg = msgb_alloc_headroom(DLSAP_MSGB_SIZE, DLSAP_MSGB_HEADROOM, "DL EST");
|
||||
msg->l3h = msg->data;
|
||||
memset(&dp, 0, sizeof(dp));
|
||||
osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg);
|
||||
|
||||
/* send to L2 */
|
||||
return lapd_recv_dlsap(&dp, &sap->dl.lctx);
|
||||
}
|
||||
|
||||
/* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
|
||||
int lapv5_sap_stop(struct lapv5_instance *li, uint16_t dladdr)
|
||||
/* Stop a (LE-side) SAP for the specified TEI/SAPI on the LAPD instance */
|
||||
int lapv5_dl_rel_req(struct lapv5_instance *li, uint16_t dladdr)
|
||||
{
|
||||
struct lapv5_sap *sap;
|
||||
struct osmo_dlsap_prim dp;
|
||||
|
@ -307,11 +314,12 @@ int lapv5_sap_stop(struct lapv5_instance *li, uint16_t dladdr)
|
|||
if (!sap)
|
||||
return -ENODEV;
|
||||
|
||||
LOGSAP(sap, LOGL_NOTICE, "LAPV5 DL-RELEASE request DLADDR=%u\n", dladdr);
|
||||
LOGSAP(sap, LOGL_DEBUG, "LAPV5 DL-RELEASE request DLADDR=%u\n", dladdr);
|
||||
|
||||
/* prepare prim */
|
||||
msg = msgb_alloc_headroom(DLSAP_MSGB_SIZE, DLSAP_MSGB_HEADROOM, "DL REL");
|
||||
msg->l3h = msg->data;
|
||||
memset(&dp, 0, sizeof(dp));
|
||||
osmo_prim_init(&dp.oph, 0, PRIM_DL_REL, PRIM_OP_REQUEST, msg);
|
||||
|
||||
/* send to L2 */
|
||||
|
@ -319,24 +327,25 @@ int lapv5_sap_stop(struct lapv5_instance *li, uint16_t dladdr)
|
|||
}
|
||||
|
||||
/* Transmit Data (DL-DATA request) on the given LAPD Instance / DLADDR */
|
||||
void lapv5_transmit(struct lapv5_instance *li, uint8_t dladdr, struct msgb *msg)
|
||||
int lapv5_dl_data_req(struct lapv5_instance *li, uint16_t dladdr, struct msgb *msg)
|
||||
{
|
||||
struct lapv5_sap *sap;
|
||||
struct osmo_dlsap_prim dp;
|
||||
|
||||
sap = lapv5_sap_find(li, dladdr);
|
||||
if (!sap) {
|
||||
LOGLI(li, LOGL_INFO, "LAPV5 Tx on unknown DLADDR=%u\n", dladdr);
|
||||
LOGLI(li, LOGL_NOTICE, "LAPV5 Tx on unknown DLADDR=%u\n", dladdr);
|
||||
msgb_free(msg);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* prepare prim */
|
||||
msg->l3h = msg->data;
|
||||
osmo_prim_init(&dp.oph, 0, PRIM_DL_DATA, PRIM_OP_REQUEST, msg);
|
||||
memset(&dp, 0, sizeof(dp));
|
||||
osmo_prim_init(&dp.oph, 0, PRIM_DL_DATA, PRIM_OP_REQUEST, msg);
|
||||
|
||||
/* send to L2 */
|
||||
lapd_recv_dlsap(&dp, &sap->dl.lctx);
|
||||
/* send to L2 */
|
||||
return lapd_recv_dlsap(&dp, &sap->dl.lctx);
|
||||
};
|
||||
|
||||
static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg)
|
||||
|
@ -368,15 +377,15 @@ static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg)
|
|||
}
|
||||
/* address field */
|
||||
msg->l2h = msgb_push(msg, 2);
|
||||
msg->l2h[0] = LAPD_ADDR2(lctx->sapi, lctx->cr);
|
||||
msg->l2h[1] = LAPD_ADDR3(lctx->tei);
|
||||
msg->l2h[0] = LAPD_ADDR1(lctx->sapi, lctx->cr);
|
||||
msg->l2h[1] = LAPD_ADDR2(lctx->tei);
|
||||
|
||||
/* write to PCAP file, if enabled. */
|
||||
osmo_pcap_lapd_write(li->pcap_fd, OSMO_LAPD_PCAP_OUTPUT, msg);
|
||||
|
||||
/* forward frame to L1 */
|
||||
LOGDL(dl, LOGL_DEBUG, "TX: %s\n", osmo_hexdump(msg->data, msg->len));
|
||||
li->transmit_cb(msg, li->transmit_cbdata);
|
||||
li->ph_data_req_cb(msg, li->ph_data_req_cbdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -392,14 +401,14 @@ static int send_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
|
|||
|
||||
li = sap->li;
|
||||
|
||||
dladdr = lctx->tei << 8 | lctx->sapi;
|
||||
dladdr = lctx->sapi << 7 | lctx->tei;
|
||||
|
||||
switch (dp->oph.primitive) {
|
||||
case PRIM_DL_EST:
|
||||
LOGDL(dl, LOGL_NOTICE, "LAPD DL-ESTABLISH %s DLADDR=%u\n", op, dladdr);
|
||||
LOGDL(dl, LOGL_DEBUG, "LAPD DL-ESTABLISH %s DLADDR=%u\n", op, dladdr);
|
||||
break;
|
||||
case PRIM_DL_REL:
|
||||
LOGDL(dl, LOGL_NOTICE, "LAPD DL-RELEASE %s DLADDR=%u\n", op, dladdr);
|
||||
LOGDL(dl, LOGL_DEBUG, "LAPD DL-RELEASE %s DLADDR=%u\n", op, dladdr);
|
||||
lapv5_sap_free(sap);
|
||||
/* note: sap and dl is now gone, don't use it anymore */
|
||||
break;
|
||||
|
@ -407,15 +416,15 @@ static int send_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
|
|||
;
|
||||
}
|
||||
|
||||
li->receive_cb(dp, dladdr, li->receive_cbdata);
|
||||
li->dl_receive_cb(dp, dladdr, li->dl_receive_cbdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocate a new LAPV5 instance */
|
||||
struct lapv5_instance *lapv5_instance_alloc(int network_side,
|
||||
void (*tx_cb)(struct msgb *msg, void *cbdata), void *tx_cbdata,
|
||||
void (*rx_cb)(struct osmo_dlsap_prim *odp, uint16_t dladdr, void *rx_cbdata), void *rx_cbdata,
|
||||
int (*ph_data_req_cb)(struct msgb *msg, void *cbdata), void *ph_data_req_cbdata,
|
||||
int (*dl_receive_cb)(struct osmo_dlsap_prim *odp, uint16_t dladdr, void *rx_cbdata), void *dl_receive_cbdata,
|
||||
const struct lapd_profile *profile, const char *name)
|
||||
{
|
||||
struct lapv5_instance *li;
|
||||
|
@ -425,10 +434,10 @@ struct lapv5_instance *lapv5_instance_alloc(int network_side,
|
|||
return NULL;
|
||||
|
||||
li->network_side = network_side;
|
||||
li->transmit_cb = tx_cb;
|
||||
li->transmit_cbdata = tx_cbdata;
|
||||
li->receive_cb = rx_cb;
|
||||
li->receive_cbdata = rx_cbdata;
|
||||
li->ph_data_req_cb = ph_data_req_cb;
|
||||
li->ph_data_req_cbdata = ph_data_req_cbdata;
|
||||
li->dl_receive_cb = dl_receive_cb;
|
||||
li->dl_receive_cbdata = dl_receive_cbdata;
|
||||
li->pcap_fd = -1;
|
||||
li->name = talloc_strdup(li, name);
|
||||
memcpy(&li->profile, profile, sizeof(li->profile));
|
||||
|
@ -470,62 +479,121 @@ void lapv5_instance_free(struct lapv5_instance *li)
|
|||
* while the last octet (before msg->tail) points to the last FCS octet. */
|
||||
int lapv5ef_rx(struct v5x_link *link, struct msgb *msg)
|
||||
{
|
||||
struct lapd_datalink *dl;
|
||||
struct lapv5_instance *li = NULL;
|
||||
struct v5x_user_port *v5up = NULL;
|
||||
uint16_t efaddr, efaddr_enc;
|
||||
bool is_isdn;
|
||||
int error;
|
||||
|
||||
msg->l1h = msg->data;
|
||||
|
||||
if (msgb_length(msg) < 2) {
|
||||
LOGP(DV5EF, LOGL_ERROR, "Frame too short.\n");
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msg->l2h = msg->l1h + 2;
|
||||
efaddr_enc = msg->l1h[0] << 8 | msg->l1h[1];
|
||||
efaddr = v51_l3_addr_dec(efaddr_enc, &is_isdn);
|
||||
efaddr_enc = msg->l2h[0] << 8 | msg->l2h[1];
|
||||
msg->l2h += 2;
|
||||
msgb_pull(msg, msg->l2h - msg->data);
|
||||
efaddr = v5x_l3_addr_dec(efaddr_enc, &is_isdn);
|
||||
|
||||
|
||||
if (!is_isdn) {
|
||||
/* EFaddr are structured like isdn-type L3 Address */
|
||||
LOGP(DV5EF, LOGL_ERROR, "EFaddr are structured like isdn-type L3 Address.\n");
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (efaddr) {
|
||||
case V51_DLADDR_PSTN:
|
||||
case V5X_DLADDR_PSTN:
|
||||
/* hand-over to LAPD-DL instance for PSTN */
|
||||
dl = &link->interface->pstn.dl;
|
||||
/* TODO */
|
||||
li = link->interface->pstn.li;
|
||||
break;
|
||||
case V51_DLADDR_CTRL:
|
||||
case V5X_DLADDR_CTRL:
|
||||
/* hand-over to LAPD-DL instance for CTRL */
|
||||
/* TODO */
|
||||
dl = &link->interface->control.dl;
|
||||
li = link->interface->control.li;
|
||||
break;
|
||||
case V52_DLADDR_BCC:
|
||||
dl = &link->interface->bcc.dl;
|
||||
/* TOOD: implement V5.2 */
|
||||
msgb_free(msg);
|
||||
/* hand-over to LAPD-DL instance for BCC */
|
||||
li = link->interface->bcc.li;
|
||||
break;
|
||||
case V52_DLADDR_PROTECTION:
|
||||
dl = &link->interface->protection[0].dl;
|
||||
/* TOOD: implement V5.2 */
|
||||
msgb_free(msg);
|
||||
/* hand-over to LAPD-DL instance for PROTECTION */
|
||||
if (link == link->interface->primary_link)
|
||||
li = link->interface->protection.li[0];
|
||||
if (link == link->interface->secondary_link)
|
||||
li = link->interface->protection.li[1];
|
||||
break;
|
||||
case V52_DLADDR_LCP:
|
||||
dl = &link->interface->lcp.dl;
|
||||
/* TOOD: implement V5.2 */
|
||||
msgb_free(msg);
|
||||
/* hand-over to LAPD-DL instance for LCP */
|
||||
li = link->interface->lcp.li;
|
||||
break;
|
||||
default:
|
||||
if (efaddr >= 8176) {
|
||||
/* reserved as per Section 9.2.2.2 of G.964 */
|
||||
LOGP(DV5EF, LOGL_ERROR, "No LAPV5 protocol for EFaddr %d.\n", efaddr);
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* relay function for LAPD of user ports */
|
||||
/* TODO */
|
||||
v5up = v5x_user_port_find(link->interface, efaddr, true);
|
||||
if (!v5up) {
|
||||
LOGP(DV5EF, LOGL_ERROR, "No ISDN user port instance for EFaddr %d created.\n", efaddr);
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!v5x_le_port_isdn_is_operational(v5up->port_fi)) {
|
||||
LOGP(DV5EF, LOGL_NOTICE, "Dropping D-channel (AN->LE) message of non-operational ISDN port for EFaddr %d.\n", efaddr);
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
LOGP(DV5EF, LOGL_DEBUG, "Recevied frame for EFaddr %d: %s\n", efaddr, osmo_hexdump(msg->data, msg->len));
|
||||
ph_socket_tx_msg(&v5up->ph_socket, 3, PH_PRIM_DATA_IND, msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!li) {
|
||||
LOGP(DV5EF, LOGL_ERROR, "No LAPV5 instance for EFaddr %d created.\n", efaddr);
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lapv5_ph_data_ind(li, msg, &error);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lapv5ef_tx(struct v5x_user_port *v5up, struct msgb *msg)
|
||||
{
|
||||
uint16_t efaddr = v5up->nr, efaddr_enc;
|
||||
|
||||
if (!v5x_le_port_isdn_is_operational(v5up->port_fi)) {
|
||||
LOGP(DV5EF, LOGL_NOTICE, "Dropping D-channel (LE->AN) message of non-operational ISDN port for EFaddr %d.\n", efaddr);
|
||||
msgb_free(msg);
|
||||
return -EIO;
|
||||
}
|
||||
/* relay function for LAPD of user ports */
|
||||
LOGP(DV5EF, LOGL_DEBUG, "Sending frame for EFaddr %d: %s\n", efaddr, osmo_hexdump(msg->data, msg->len));
|
||||
msg->l2h = msgb_push(msg, 2);
|
||||
efaddr_enc = v5x_l3_addr_enc(efaddr, 1);
|
||||
msg->l2h[0] = efaddr_enc >> 8;
|
||||
msg->l2h[1] = efaddr_enc;
|
||||
ph_data_req_hdlc(msg, v5up->interface);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set enabled state, also free all SAP, so it will disable/enable with a cleanly */
|
||||
void lapv5_set_enabled(struct lapv5_instance *li, bool enabled)
|
||||
{
|
||||
struct lapv5_sap *sap;
|
||||
|
||||
li->enabled = enabled;
|
||||
|
||||
/* reset DL instances */
|
||||
llist_for_each_entry(sap, &li->sap_list, list) {
|
||||
lapd_dl_reset(&sap->dl);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include <osmocom/abis/lapd.h>
|
||||
|
||||
struct lapv5_instance {
|
||||
struct llist_head list; /* list of LAPV5 instances */
|
||||
bool network_side;
|
||||
|
||||
int (*ph_data_req_cb)(struct msgb *msg, void *cbdata);
|
||||
void *ph_data_req_cbdata;
|
||||
int (*dl_receive_cb)(struct osmo_dlsap_prim *odp, uint16_t dladdr, void *rx_cbdata);
|
||||
void *dl_receive_cbdata;
|
||||
|
||||
struct lapd_profile profile; /* must be a copy */
|
||||
|
||||
struct llist_head sap_list; /* list of SAP/datalinks in this instance */
|
||||
int pcap_fd; /* PCAP file descriptor */
|
||||
char *name; /* human-readable name */
|
||||
|
||||
bool enabled; /* only receive messages when enabled */
|
||||
};
|
||||
|
||||
extern const struct lapd_profile lapd_profile_lapv5dl;
|
||||
|
||||
int lapv5ef_rx(struct v5x_link *link, struct msgb *msg);
|
||||
|
||||
int lapv5_dl_est_req(struct lapv5_instance *li, uint16_t dladdr);
|
||||
int lapv5_dl_rel_req(struct lapv5_instance *li, uint16_t dladdr);
|
||||
int lapv5_dl_data_req(struct lapv5_instance *li, uint16_t dladdr, struct msgb *msg);
|
||||
|
||||
int lapv5_ph_data_ind(struct lapv5_instance *li, struct msgb *msg, int *error);
|
||||
|
||||
struct lapv5_instance *lapv5_instance_alloc(int network_side,
|
||||
int (*ph_data_req_cb)(struct msgb *msg, void *cbdata), void *ph_data_req_cbdata,
|
||||
int (*dl_receive_cb)(struct osmo_dlsap_prim *odp, uint16_t dladdr, void *rx_cbdata), void *dl_receive_cbdata,
|
||||
const struct lapd_profile *profile, const char *name);
|
||||
void lapv5_instance_set_profile(struct lapv5_instance *li, const struct lapd_profile *profile);
|
||||
void lapv5_instance_free(struct lapv5_instance *li);
|
||||
|
||||
int lapv5ef_rx(struct v5x_link *link, struct msgb *msg);
|
||||
int lapv5ef_tx(struct v5x_user_port *v5up, struct msgb *msg);
|
||||
|
||||
void lapv5_set_enabled(struct lapv5_instance *li, bool enabled);
|
|
@ -0,0 +1,537 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <osmocom/core/gsmtap.h>
|
||||
#include <osmocom/core/gsmtap_util.h>
|
||||
#include <osmocom/core/signal.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "lapv5.h"
|
||||
#include "layer1.h"
|
||||
#include "v5x_l1_fsm.h"
|
||||
#include "v5x_le_management.h"
|
||||
#include "logging.h"
|
||||
#include "libg711/g711.h"
|
||||
|
||||
extern int ulaw;
|
||||
extern int test_sa7;
|
||||
extern const char *gsmtap_ip;
|
||||
|
||||
extern struct v5x_instance *v5i;
|
||||
|
||||
static struct gsmtap_inst *g_gti = NULL;
|
||||
|
||||
/* only temporarily until this is in libosmocore gsmtap.h */
|
||||
#ifndef GSMTAP_E1T1_V5EF
|
||||
#define GSMTAP_E1T1_V5EF 0x06
|
||||
#endif
|
||||
|
||||
/* Callback function to receive L1 signal from E1 port */
|
||||
static int inp_sig_cb(unsigned int subsys, unsigned int signal, void __attribute__((unused)) *handler_data,
|
||||
void *signal_data)
|
||||
{
|
||||
struct input_signal_data *isd = signal_data;
|
||||
struct v5x_link *v5l;
|
||||
|
||||
if (subsys != SS_L_INPUT)
|
||||
return 0;
|
||||
|
||||
/* not used by any link */
|
||||
if (!isd->line->ops)
|
||||
return 0;
|
||||
v5l = container_of(isd->line->ops, struct v5x_link, e1_line_ops);
|
||||
|
||||
switch (signal) {
|
||||
case S_L_INP_LINE_LOS:
|
||||
v5x_l1_signal_rcv(v5l, L1_SIGNAL_LOS);
|
||||
break;
|
||||
case S_L_INP_LINE_NOLOS:
|
||||
v5x_l1_signal_rcv(v5l, L1_SIGNAL_NO_LOS);
|
||||
break;
|
||||
case S_L_INP_LINE_RAI:
|
||||
v5x_l1_signal_rcv(v5l, L1_SIGNAL_RAI);
|
||||
break;
|
||||
case S_L_INP_LINE_NORAI:
|
||||
v5x_l1_signal_rcv(v5l, L1_SIGNAL_NO_RAI);
|
||||
break;
|
||||
case S_L_INP_LINE_AIS:
|
||||
v5x_l1_signal_rcv(v5l, L1_SIGNAL_AIS);
|
||||
break;
|
||||
case S_L_INP_LINE_NOAIS:
|
||||
v5x_l1_signal_rcv(v5l, L1_SIGNAL_NO_AIS);
|
||||
break;
|
||||
case S_L_INP_LINE_SA_BITS:
|
||||
if ((isd->sa_bits & 0x40)) {
|
||||
v5x_l1_signal_rcv(v5l, L1_SIGNAL_SA7_1);
|
||||
if (test_sa7) {
|
||||
printf("Currently Sa 7 is set to 1, changing it to 0. (Link ID %d)\n", v5l->id);
|
||||
v5x_l1_signal_snd(v5l, L1_SIGNAL_SA7_0);
|
||||
}
|
||||
} else {
|
||||
v5x_l1_signal_rcv(v5l, L1_SIGNAL_SA7_0);
|
||||
if (test_sa7) {
|
||||
printf("Currently Sa 7 is set to 0, changing it to 1. (Link ID %d)\n", v5l->id);
|
||||
v5x_l1_signal_snd(v5l, L1_SIGNAL_SA7_1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case S_L_INP_LINE_SLIP_RX:
|
||||
printf("RX slip detected on link %d.\n", v5l->id);
|
||||
break;
|
||||
case S_L_INP_LINE_SLIP_TX:
|
||||
printf("TX slip detected on link %d.\n", v5l->id);
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send L1 signal to E1 port */
|
||||
int v5x_l1_signal_snd(struct v5x_link *v5l, enum l1_signal_prim prim)
|
||||
{
|
||||
struct e1inp_line *e1_line = v5l->e1_line;
|
||||
|
||||
/* no line assigned */
|
||||
if (!e1_line)
|
||||
return 0;
|
||||
|
||||
switch (prim) {
|
||||
case L1_SIGNAL_SA7_0:
|
||||
e1inp_ts_set_sa_bits(e1_line, 0xbf);
|
||||
break;
|
||||
case L1_SIGNAL_SA7_1:
|
||||
e1inp_ts_set_sa_bits(e1_line, 0xff);
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* store TX to buffer */
|
||||
static void echo_tx(struct v5x_echo_proc *ep, uint8_t *data, int len)
|
||||
{
|
||||
int in;
|
||||
int16_t tx;
|
||||
|
||||
if (!ep->enabled)
|
||||
return;
|
||||
|
||||
while (len--) {
|
||||
if (ulaw)
|
||||
tx = g711_ulaw_flipped_to_linear[*data++];
|
||||
else
|
||||
tx = g711_alaw_flipped_to_linear[*data++];
|
||||
ep->tx_buffer[ep->tx_buffer_in] = tx;
|
||||
in = (ep->tx_buffer_in + 1) % EC_BUFFER;
|
||||
/* buffer overflow condition, should not happen, if application syncs to RX */
|
||||
if (in == ep->tx_buffer_out)
|
||||
break;
|
||||
ep->tx_buffer_in = in;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove echo from RX using TX from buffer */
|
||||
static void echo_rx(struct v5x_echo_proc *ep, uint8_t *data, int len)
|
||||
{
|
||||
struct v5x_user_port *v5up = ep->port;
|
||||
int16_t tx, rx, rx_can;
|
||||
int16_t answer_buffer[len];
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
if (!ep->enabled)
|
||||
return;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
/* buffer underrun condition, may happen before buffer was filled with first frame */
|
||||
if (ep->tx_buffer_out == ep->tx_buffer_in)
|
||||
tx = 0;
|
||||
else {
|
||||
tx = ep->tx_buffer[ep->tx_buffer_out];
|
||||
ep->tx_buffer_out = (ep->tx_buffer_out + 1) % EC_BUFFER;
|
||||
}
|
||||
if (ulaw) {
|
||||
rx = g711_ulaw_flipped_to_linear[*data];
|
||||
if (ep->port->use_line_echo == USE_ECHO_CANCELER)
|
||||
rx_can = echo_can_update(ep->ec, tx, rx);
|
||||
else
|
||||
rx_can = echo_sup_update(ep->es, tx, rx);
|
||||
*data++ = g711_linear_to_ulaw_flipped[(uint16_t)rx_can];
|
||||
} else {
|
||||
rx = g711_alaw_flipped_to_linear[*data];
|
||||
if (ep->port->use_line_echo == USE_ECHO_CANCELER)
|
||||
rx_can = echo_can_update(ep->ec, tx, rx);
|
||||
else
|
||||
rx_can = echo_sup_update(ep->es, tx, rx);
|
||||
*data++ = g711_linear_to_alaw_flipped[(uint16_t)rx_can];
|
||||
}
|
||||
answer_buffer[i] = rx + tx; /* yes, may overflow, but then it is no valid tone anyway */
|
||||
}
|
||||
|
||||
rc = answertone_process(&ep->at, answer_buffer, len, (ep->port->use_line_echo == USE_ECHO_CANCELER));
|
||||
if (rc > 0) {
|
||||
LOGP(DV5, LOGL_NOTICE, "Detected answer tone, disable echo %s of %s port %d.\n",
|
||||
(ep->port->use_line_echo == USE_ECHO_CANCELER) ? "canceler" : "suppressor",
|
||||
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
||||
ep->enabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* data L1 -> L2 from E1 interface */
|
||||
static void hdlc_rx_cb(struct e1inp_ts *ts, struct msgb *msg)
|
||||
{
|
||||
struct v5x_link *v5l;
|
||||
|
||||
/* not used by any link */
|
||||
if (!ts->line->ops) {
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
v5l = container_of(ts->line->ops, struct v5x_link, e1_line_ops);
|
||||
|
||||
LOGP(DLINP, LOGL_DEBUG, "Link %d L1->L2: %s\n", v5l->id, msgb_hexdump(msg));
|
||||
|
||||
/* send V5 data via gsmtap so wireshark can receive + decode it */
|
||||
if (g_gti) {
|
||||
gsmtap_send_ex(g_gti, GSMTAP_TYPE_E1T1, v5l->id, ts->num, GSMTAP_E1T1_V5EF,
|
||||
0, 0, 0, 0, msgb_data(msg), msgb_length(msg));
|
||||
}
|
||||
|
||||
lapv5ef_rx(v5l, msg);
|
||||
}
|
||||
|
||||
/* data B-channel data from E1 interface */
|
||||
static void raw_rx_cb(struct e1inp_ts *ts, struct msgb *msg)
|
||||
{
|
||||
struct v5x_link *v5l;
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
/* not used by any link */
|
||||
if (!ts->line->ops) {
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
v5l = container_of(ts->line->ops, struct v5x_link, e1_line_ops);
|
||||
v5up = v5l->ts[ts->num].v5up;
|
||||
|
||||
/* not used by any user port */
|
||||
if (!v5up) {
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* we want B-channel data flipped */
|
||||
osmo_revbytebits_buf(msg->data, msg->len);
|
||||
|
||||
/* if assigned and active, send B-channel data to socket interface */
|
||||
if (v5up->ts[0] && v5up->ts[0]->nr == ts->num && v5up->ts[0]->b_activated) {
|
||||
echo_rx(&v5up->ep[0], msg->data, msg->len);
|
||||
ph_socket_tx_msg(&v5up->ph_socket, 1, PH_PRIM_DATA_IND, msg->data, msg->len);
|
||||
}
|
||||
if (v5up->ts[1] && v5up->ts[1]->nr == ts->num && v5up->ts[1]->b_activated) {
|
||||
echo_rx(&v5up->ep[1], msg->data, msg->len);
|
||||
ph_socket_tx_msg(&v5up->ph_socket, 2, PH_PRIM_DATA_IND, msg->data, msg->len);
|
||||
}
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
/* send HDLC frame to signaling channel (from ISDN) */
|
||||
int ph_data_req_hdlc(struct msgb *msg, struct v5x_interface *v5if)
|
||||
{
|
||||
struct e1inp_line *e1_line = v5if->cc_link->e1_line;
|
||||
|
||||
if (!e1_line) {
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!v5x_l1_is_up(v5if->cc_link->l1)) {
|
||||
LOGP(DLINP, LOGL_DEBUG, "Link %d is down!\n", v5if->cc_link->id);
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOGP(DLINP, LOGL_DEBUG, "Link %d L2->L1: %s\n", v5if->cc_link->id, msgb_hexdump(msg));
|
||||
|
||||
struct e1inp_ts *ts = &e1_line->ts[v5if->cc_link->c_channel[0].ts->nr - 1];
|
||||
|
||||
/* send V5 data via gsmtap so wireshark can receive + decode it */
|
||||
if (g_gti) {
|
||||
gsmtap_send_ex(g_gti, GSMTAP_TYPE_E1T1, v5if->cc_link->id | GSMTAP_ARFCN_F_UPLINK, ts->num,
|
||||
GSMTAP_E1T1_V5EF, 0, 0, 0, 0, msgb_data(msg), msgb_length(msg));
|
||||
}
|
||||
|
||||
return e1inp_ts_send_hdlc(ts, msg);
|
||||
}
|
||||
|
||||
/* send HDLC frame to signaling channel (from DL) */
|
||||
int ph_data_req_dl_cc(struct msgb *msg, void *cbdata)
|
||||
{
|
||||
struct v5x_interface *v5if = (struct v5x_interface *)cbdata;
|
||||
struct e1inp_line *e1_line = v5if->cc_link->e1_line;
|
||||
|
||||
if (!e1_line) {
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!v5x_l1_is_up(v5if->cc_link->l1)) {
|
||||
LOGP(DLINP, LOGL_DEBUG, "Link %d is down!\n", v5if->cc_link->id);
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct e1inp_ts *ts = &e1_line->ts[v5if->cc_link->c_channel[0].ts->nr - 1];
|
||||
|
||||
/* add frame relay header */
|
||||
msg->l2h = msgb_push(msg, 2);
|
||||
msg->l2h[0] = msg->l2h[2] & 0xfd;
|
||||
msg->l2h[1] = msg->l2h[3];
|
||||
|
||||
LOGP(DLINP, LOGL_DEBUG, "Link %d L2->L1: %s\n", v5if->cc_link->id, msgb_hexdump(msg));
|
||||
|
||||
/* send V5 data via gsmtap so wireshark can receive + decode it */
|
||||
if (g_gti) {
|
||||
gsmtap_send_ex(g_gti, GSMTAP_TYPE_E1T1, v5if->cc_link->id | GSMTAP_ARFCN_F_UPLINK, ts->num,
|
||||
GSMTAP_E1T1_V5EF, 0, 0, 0, 0, msgb_data(msg), msgb_length(msg));
|
||||
}
|
||||
|
||||
return e1inp_ts_send_hdlc(ts, msg);
|
||||
}
|
||||
|
||||
/* send HDLC frame to protection link (from DL) */
|
||||
int ph_data_req_dl_prot(struct msgb *msg, void *cbdata)
|
||||
{
|
||||
struct v5x_link *v5l = (struct v5x_link *)cbdata;
|
||||
struct e1inp_line *e1_line = v5l->e1_line;
|
||||
|
||||
if (!e1_line) {
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!v5x_l1_is_up(v5l->l1)) {
|
||||
LOGP(DLINP, LOGL_DEBUG, "Link %d is down!\n", v5l->id);
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct e1inp_ts *ts = &e1_line->ts[v5l->c_channel[0].ts->nr - 1];
|
||||
|
||||
/* add frame relay header */
|
||||
msg->l2h = msgb_push(msg, 2);
|
||||
msg->l2h[0] = msg->l2h[2] & 0xfd;
|
||||
msg->l2h[1] = msg->l2h[3];
|
||||
|
||||
LOGP(DLINP, LOGL_DEBUG, "Link %d L2->L1: %s\n", v5l->id, msgb_hexdump(msg));
|
||||
|
||||
/* send V5 data via gsmtap so wireshark can receive + decode it */
|
||||
if (g_gti) {
|
||||
gsmtap_send_ex(g_gti, GSMTAP_TYPE_E1T1, v5l->id | GSMTAP_ARFCN_F_UPLINK, ts->num, GSMTAP_E1T1_V5EF,
|
||||
0, 0, 0, 0, msgb_data(msg), msgb_length(msg));
|
||||
}
|
||||
|
||||
return e1inp_ts_send_hdlc(ts, msg);
|
||||
}
|
||||
|
||||
int ph_activate_req(struct v5x_timeslot *ts)
|
||||
{
|
||||
struct e1inp_line *e1_line = ts->link->e1_line;
|
||||
struct e1inp_ts *e1_ts;
|
||||
int rc;
|
||||
|
||||
if (ts->b_activated)
|
||||
return 0;
|
||||
|
||||
if (!e1_line)
|
||||
return -EINVAL;
|
||||
e1_ts = &e1_line->ts[ts->nr - 1];
|
||||
|
||||
e1inp_ts_config_raw(e1_ts, e1_line, raw_rx_cb);
|
||||
rc = e1inp_line_update(e1_line);
|
||||
|
||||
ts->b_activated = 1;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ph_deactivate_req(struct v5x_timeslot *ts)
|
||||
{
|
||||
struct e1inp_line *e1_line = ts->link->e1_line;
|
||||
struct e1inp_ts *e1_ts;
|
||||
int rc;
|
||||
|
||||
if (!ts->b_activated)
|
||||
return 0;
|
||||
|
||||
if (!e1_line)
|
||||
return -EINVAL;
|
||||
e1_ts = &e1_line->ts[ts->nr - 1];
|
||||
|
||||
e1_ts->type = E1INP_TS_TYPE_NONE;
|
||||
rc = e1inp_line_update(e1_line);
|
||||
|
||||
ts->b_activated = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* send raw (B-channel) data to E1 interface */
|
||||
static int ph_data_req_raw(struct v5x_link *v5l, struct msgb *msg, int ts_nr)
|
||||
{
|
||||
struct e1inp_line *e1_line = v5l->e1_line;
|
||||
|
||||
/* no line assigned */
|
||||
if (!e1_line) {
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* we want B-channel data flipped */
|
||||
osmo_revbytebits_buf(msg->data, msg->len);
|
||||
|
||||
struct e1inp_ts *ts = &e1_line->ts[ts_nr-1];
|
||||
|
||||
return e1inp_ts_send_raw(ts, msg);
|
||||
}
|
||||
|
||||
/* receive message from PH-socket */
|
||||
void ph_socket_rx_cb(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length)
|
||||
{
|
||||
struct v5x_user_port *v5up = s->priv;
|
||||
|
||||
switch (prim) {
|
||||
case PH_PRIM_CTRL_REQ:
|
||||
/* deactivate channels, if active */
|
||||
if ((channel == 0 || channel == 3) && length && *data == PH_CTRL_BLOCK) {
|
||||
v5x_le_channel_unassign(v5up, 1);
|
||||
v5x_le_channel_unassign(v5up, 2);
|
||||
}
|
||||
v5x_le_nat_ph_rcv(v5up, prim, data, length);
|
||||
break;
|
||||
case PH_PRIM_ACT_REQ:
|
||||
if (channel == 1 || channel == 2) {
|
||||
v5x_le_channel_assign(v5up, channel);
|
||||
ph_socket_tx_msg(s, channel, PH_PRIM_ACT_IND, NULL, 0);
|
||||
break;
|
||||
}
|
||||
v5x_le_nat_ph_rcv(v5up, prim, data, length);
|
||||
break;
|
||||
case PH_PRIM_DACT_REQ:
|
||||
if (channel == 1 || channel == 2) {
|
||||
v5x_le_channel_unassign(v5up, channel);
|
||||
ph_socket_tx_msg(s, channel, PH_PRIM_DACT_IND, NULL, 0);
|
||||
break;
|
||||
}
|
||||
v5x_le_nat_ph_rcv(v5up, prim, data, length);
|
||||
break;
|
||||
case PH_PRIM_DATA_REQ:
|
||||
if (v5up->type == V5X_USER_TYPE_PSTN && channel == 0) {
|
||||
struct msgb *msg = msgb_alloc_headroom(length + 32, 32, "V5 PSTN MSG");
|
||||
memcpy(msgb_put(msg, length), data, length);
|
||||
v5x_le_nat_fe_rcv(v5up, msg);
|
||||
} else if (v5up->type == V5X_USER_TYPE_ISDN && channel == 3) {
|
||||
struct msgb *msg = msgb_alloc_headroom(length + 32, 32, "V5 EF MSG");
|
||||
memcpy(msgb_put(msg, length), data, length);
|
||||
lapv5ef_tx(v5up, msg);
|
||||
} else if ((channel == 1 || channel == 2) && v5up->ts[channel - 1] && v5up->ts[channel - 1]->b_activated) {
|
||||
echo_tx(&v5up->ep[channel - 1], data, length);
|
||||
struct msgb *msg = msgb_alloc_headroom(length + 32, 32, "B MSG");
|
||||
memcpy(msgb_put(msg, length), data, length);
|
||||
ph_data_req_raw(v5up->ts[channel - 1]->link, msg, v5up->ts[channel - 1]->nr);
|
||||
}
|
||||
/* always confirm */
|
||||
ph_socket_tx_msg(s, channel, PH_PRIM_DATA_CNF, NULL, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//FIXME: we use HDLC
|
||||
static int v5le_rx_sign(struct msgb *msg)
|
||||
{
|
||||
LOGP(DLMI, LOGL_NOTICE, "Rx: %s\n", msgb_hexdump(msg));
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* init gsmtap */
|
||||
int gsmtap_init(void)
|
||||
{
|
||||
if (gsmtap_ip) {
|
||||
g_gti = gsmtap_source_init(gsmtap_ip, GSMTAP_UDP_PORT, 0);
|
||||
if (!g_gti) {
|
||||
fprintf(stderr, "Failed to use '%s' as IP for GSMTAP\n", gsmtap_ip);
|
||||
return -EINVAL;
|
||||
}
|
||||
gsmtap_source_add_sink(g_gti);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* register signal to receive E1 events */
|
||||
int l1_signal_init(void)
|
||||
{
|
||||
osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* init given E1 line and return e1inp_line structure pointer */
|
||||
struct e1inp_line *e1_line_init(struct v5x_link *v5l, int e1_nr)
|
||||
{
|
||||
struct e1inp_line *e1_line;
|
||||
int ts;
|
||||
int rc;
|
||||
|
||||
e1_line = e1inp_line_find(e1_nr);
|
||||
if (!e1_line)
|
||||
return NULL;
|
||||
|
||||
/* link e1inp_line to v5l and vice versa */
|
||||
/* must set ops before setting TS */
|
||||
v5l->e1_line_ops.sign_link = v5le_rx_sign;
|
||||
e1inp_line_bind_ops(e1_line, &v5l->e1_line_ops);
|
||||
v5l->e1_line = e1_line;
|
||||
|
||||
for (ts = 1; ts <= 31; ts++) {
|
||||
struct e1inp_ts *e1_ts = &e1_line->ts[ts-1];
|
||||
if (ts == 16) { // FIXME: make this depending on c_channel
|
||||
//e1inp_ts_config_sign(e1_ts, e1_line);
|
||||
//e1inp_sign_link_create(e1_ts, E1INP_SIGN_NONE, NULL, 115/*TEI*/, 0/*SAPI*/);
|
||||
e1inp_ts_config_hdlc(e1_ts, e1_line, hdlc_rx_cb);
|
||||
} else
|
||||
e1_ts->type = E1INP_TS_TYPE_NONE;
|
||||
}
|
||||
|
||||
/* if config fails, remove link between e1inp_line and v5l */
|
||||
rc = e1inp_line_update(e1_line);
|
||||
if (rc < 0) {
|
||||
e1_line_exit(v5l);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return e1_line;
|
||||
}
|
||||
|
||||
void e1_line_exit(struct v5x_link *v5l)
|
||||
{
|
||||
struct e1inp_line *e1_line = v5l->e1_line;
|
||||
int ts;
|
||||
|
||||
for (ts = 1; ts <= 31; ts++) {
|
||||
struct e1inp_ts *e1_ts = &e1_line->ts[ts-1];
|
||||
e1_ts->type = E1INP_TS_TYPE_NONE;
|
||||
}
|
||||
e1inp_line_update(e1_line);
|
||||
|
||||
e1inp_line_bind_ops(e1_line, NULL);
|
||||
v5l->e1_line = NULL;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
enum l1_signal_prim {
|
||||
L1_SIGNAL_LOS,
|
||||
L1_SIGNAL_NO_LOS,
|
||||
L1_SIGNAL_RAI,
|
||||
L1_SIGNAL_NO_RAI,
|
||||
L1_SIGNAL_AIS,
|
||||
L1_SIGNAL_NO_AIS,
|
||||
L1_SIGNAL_SA7_1,
|
||||
L1_SIGNAL_SA7_0,
|
||||
};
|
||||
|
||||
int v5x_l1_signal_snd(struct v5x_link *v5l, enum l1_signal_prim prim);
|
||||
int ph_activate_req(struct v5x_timeslot *ts);
|
||||
int ph_deactivate_req(struct v5x_timeslot *ts);
|
||||
int ph_data_req_hdlc(struct msgb *msg, struct v5x_interface *v5if);
|
||||
int ph_data_req_dl_cc(struct msgb *msg, void *cbdata);
|
||||
int ph_data_req_dl_prot(struct msgb *msg, void *cbdata);
|
||||
void ph_socket_rx_cb(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length);
|
||||
int gsmtap_init(void);
|
||||
int l1_signal_init(void);
|
||||
struct e1inp_line *e1_line_init(struct v5x_link *v5l, int e1_nr);
|
||||
void e1_line_exit(struct v5x_link *v5l);
|
|
@ -0,0 +1,8 @@
|
|||
AM_CPPFLAGS = $(all_includes)
|
||||
AM_CFLAGS= -Wall -Wextra -g
|
||||
|
||||
noinst_LIBRARIES = libecho.a
|
||||
|
||||
libecho_a_SOURCES = echo_cancel.c \
|
||||
echo_suppress.c \
|
||||
answertone.c
|
|
@ -0,0 +1,276 @@
|
|||
/* An answer tone detection algrotihm using Goertzel to detect level and phase
|
||||
*
|
||||
* The answer tone is described in ITU V.8 and is also used to disable echo
|
||||
* suppression and cancelation in the network. The detection uses phase to
|
||||
* detect frequencies that are too far off, as well a phase reversal, to
|
||||
* distinguish the answer tone from other continuous tones.
|
||||
*
|
||||
* (C) 2023 by Andreas Eversberg <andreas@eversberg.eu>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include "answertone.h"
|
||||
|
||||
#define ANS_FREQUENCY 2100.0
|
||||
#define WINDOW_DURATION 0.010 /* 10 MS */
|
||||
#define DETECT_DURATION 0.400 /* < 425 MS */
|
||||
#define MIN_LEVEL 0.05 /* -26 dBm0 */
|
||||
#define MAX_NOISE 0.5 /* -6 dB */
|
||||
#define MAX_FREQUENCY 22 /* frequency error */
|
||||
#define MIN_REVERSAL 2.967 /* about 10 degrees off 180 (PI) */
|
||||
|
||||
// #define DEBUG
|
||||
// #define HEAVY_DEBUG
|
||||
|
||||
void goertzel_init(struct answer_tone *at, float frequency, float samplerate)
|
||||
{
|
||||
float omega;
|
||||
|
||||
omega = 2.0 * M_PI * frequency / samplerate;
|
||||
at->sine = sin(omega);
|
||||
at->cosine = cos(omega);
|
||||
at->coeff = 2.0 * at->cosine;
|
||||
}
|
||||
|
||||
/* with rectangular window, the frequency response is:
|
||||
* if it matches the filter frequency: 0 dB
|
||||
* if it is 0.5/duration off the filter frequency: about -4 dB
|
||||
* if it is 1/duration off the filter frequency; -INF dB
|
||||
* if it is 1.5/duration off the filter frequency: about -13.5 dB
|
||||
*/
|
||||
void goertzel_calculate(struct answer_tone *at, float *data, int length, float *level_p, float *magnitude_p, float *phase_p)
|
||||
{
|
||||
float q0 = 0, q1 = 0, q2 = 0;
|
||||
float real, imag;
|
||||
float scaling = (float)length / 2.0;
|
||||
float upper = 0, lower = 0;
|
||||
int i;
|
||||
|
||||
upper = lower = *data;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
if (*data > upper)
|
||||
upper = *data;
|
||||
else if (*data < lower)
|
||||
lower = *data;
|
||||
q0 = at->coeff * q1 - q2 + *data++;
|
||||
q2 = q1;
|
||||
q1 = q0;
|
||||
}
|
||||
|
||||
real = (q1 * at->cosine - q2) / scaling;
|
||||
imag = (q1 * at->sine) / scaling;
|
||||
|
||||
*level_p = (upper - lower) / 2.0;
|
||||
*magnitude_p = sqrtf(real*real + imag*imag);
|
||||
*phase_p = atan2(imag, real);
|
||||
}
|
||||
|
||||
int answertone_init(struct answer_tone *at, int samplerate)
|
||||
{
|
||||
memset(at, 0, sizeof(*at));
|
||||
|
||||
/* use 10 ms as window size */
|
||||
at->buffer_size = samplerate * WINDOW_DURATION;
|
||||
at->buffer = calloc(at->buffer_size, sizeof(*at->buffer));
|
||||
if (!at->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
goertzel_init(at, ANS_FREQUENCY, samplerate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void answertone_reset(struct answer_tone *at)
|
||||
{
|
||||
at->state = AT_STATE_NONE;
|
||||
}
|
||||
|
||||
void answertone_exit(struct answer_tone *at)
|
||||
{
|
||||
free(at->buffer);
|
||||
}
|
||||
|
||||
enum at_fail answertone_check(struct answer_tone *at, float *phase_p, float *frequency_p)
|
||||
{
|
||||
float level, magnitude, last_phase, shift;
|
||||
|
||||
goertzel_calculate(at, at->buffer, at->buffer_size, &level, &magnitude, phase_p);
|
||||
last_phase = at->last_phase;
|
||||
at->last_phase = *phase_p;
|
||||
shift = (*phase_p - last_phase);
|
||||
if (shift > M_PI)
|
||||
shift -= M_PI * 2.0;
|
||||
else if (shift < -M_PI)
|
||||
shift += M_PI * 2.0;
|
||||
*frequency_p = shift / M_PI / 2.0 / WINDOW_DURATION;
|
||||
|
||||
#if HEAVY_DEBUG
|
||||
printf("level=%6.3f magnitude=%6.3f snr=%6.3f phase=%9.3f frequency=%8.3f\n",
|
||||
level, magnitude, magnitude/level, *phase_p / M_PI * 180, *frequency_p);
|
||||
#endif
|
||||
|
||||
/* fails due to low level */
|
||||
if (magnitude < MIN_LEVEL)
|
||||
return AT_FAIL_MIN_LEVEL;
|
||||
|
||||
/* fails due to bad SNR */
|
||||
if (1.0 - magnitude/level > MAX_NOISE)
|
||||
return AT_FAIL_MAX_NOISE;
|
||||
|
||||
/* fails due to wrong frequency */
|
||||
if (fabsf(*frequency_p) > MAX_FREQUENCY)
|
||||
return AT_FAIL_MAX_FREQUENCY;
|
||||
|
||||
return AT_FAIL_NONE;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
static const char *answertone_cause[] = {
|
||||
"ok",
|
||||
"level too low",
|
||||
"too noisy",
|
||||
"wrong frequency",
|
||||
};
|
||||
#endif
|
||||
|
||||
static enum at_state answertone_chunk(struct answer_tone *at, bool phase_reversal)
|
||||
{
|
||||
float phase, frequency, shift;
|
||||
enum at_fail fail;
|
||||
int rc = 0;
|
||||
|
||||
fail = answertone_check(at, &phase, &frequency);
|
||||
|
||||
switch (at->state) {
|
||||
case AT_STATE_NONE:
|
||||
if (fail == AT_FAIL_NONE) {
|
||||
#if DEBUG
|
||||
printf("DETECTED TONE\n");
|
||||
#endif
|
||||
at->state = AT_STATE_DETECT;
|
||||
at->tone_duration = WINDOW_DURATION;
|
||||
/* count frequency values */
|
||||
at->frequency_sum = 0.0;
|
||||
at->count = 0;
|
||||
}
|
||||
break;
|
||||
case AT_STATE_DETECT:
|
||||
if (fail != AT_FAIL_NONE) {
|
||||
#if DEBUG
|
||||
printf("LOST TONE AFTER %.3f SECONDS (because %s)\n", at->tone_duration, cause[fail]);
|
||||
#endif
|
||||
at->state = AT_STATE_NONE;
|
||||
break;
|
||||
}
|
||||
at->frequency_sum += frequency;
|
||||
at->count++;
|
||||
at->tone_duration += WINDOW_DURATION;
|
||||
at->last2_valid_phase = at->last1_valid_phase;
|
||||
at->last1_valid_phase = phase;
|
||||
if (at->tone_duration >= DETECT_DURATION) {
|
||||
#if DEBUG
|
||||
printf("LONG TONE VALID WITH FREQUENCY OFFSET %.3f\n", at->frequency_sum / at->count);
|
||||
#endif
|
||||
at->state = AT_STATE_TONE;
|
||||
}
|
||||
break;
|
||||
case AT_STATE_TONE:
|
||||
if (fail != AT_FAIL_NONE) {
|
||||
if (!phase_reversal) {
|
||||
#if DEBUG
|
||||
printf("LONG TONE LOST\n");
|
||||
#endif
|
||||
at->state = AT_STATE_TONE;
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
#if DEBUG
|
||||
printf("LONG TONE CHANGED, CHECK PHASE REVERSAL\n");
|
||||
#endif
|
||||
at->last_valid_frequency = at->frequency_sum / at->count;
|
||||
at->state = AT_STATE_PAUSE;
|
||||
/* count pause chunks */
|
||||
at->count = 1;
|
||||
break;
|
||||
}
|
||||
at->frequency_sum += frequency;
|
||||
at->count++;
|
||||
at->tone_duration += WINDOW_DURATION;
|
||||
at->last2_valid_phase = at->last1_valid_phase;
|
||||
at->last1_valid_phase = phase;
|
||||
break;
|
||||
case AT_STATE_PAUSE:
|
||||
/* three pause chunks: two may be invalid due to phase reversal, one extra to get valid frequency */
|
||||
if (at->count++ < 3)
|
||||
break;
|
||||
/* phase change over 5 chunks */
|
||||
shift = (phase - at->last2_valid_phase);
|
||||
if (shift > M_PI)
|
||||
shift -= M_PI * 2.0;
|
||||
else if (shift < -M_PI)
|
||||
shift += M_PI * 2.0;
|
||||
/* substract phase change to be expected over 5 chunk */
|
||||
shift -= at->last_valid_frequency * WINDOW_DURATION * 5.0 * M_PI * 2.0;
|
||||
if (shift > M_PI)
|
||||
shift -= M_PI * 2.0;
|
||||
else if (shift < -M_PI)
|
||||
shift += M_PI * 2.0;
|
||||
/* if there is change of less than about 10 degrees off 180 */
|
||||
if (fabsf(shift) > MIN_REVERSAL) {
|
||||
#if DEBUG
|
||||
printf("PHASE REVERSAL VALID (phase shift %.0f deg)\n", shift / M_PI * 180.0);
|
||||
#endif
|
||||
at->frequency_sum = 0.0;
|
||||
at->count = 0;
|
||||
at->state = AT_STATE_DETECT;
|
||||
rc = 1;
|
||||
} else {
|
||||
#if DEBUG
|
||||
printf("LOST TONE, NO VALID PHASE REVERSAL\n");
|
||||
#endif
|
||||
at->state = AT_STATE_NONE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* convert stream of int16_t (ISDN) stream into chunks of float (dBm0); return 1, if there was a valid answer tone */
|
||||
int answertone_process(struct answer_tone *at, int16_t *data, int length, bool phase_reversal)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
while (length--) {
|
||||
/* convert ISDN data to float with dBm level (1 dBm = -3 dB below full int16_t scale) */
|
||||
at->buffer[at->buffer_pos++] = (float)(*data++) / 23170.0;
|
||||
if (at->buffer_pos == at->buffer_size) {
|
||||
rc |= answertone_chunk(at, phase_reversal);
|
||||
at->buffer_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
enum at_fail {
|
||||
AT_FAIL_NONE = 0,
|
||||
AT_FAIL_MIN_LEVEL,
|
||||
AT_FAIL_MAX_NOISE,
|
||||
AT_FAIL_MAX_FREQUENCY,
|
||||
};
|
||||
|
||||
enum at_state {
|
||||
AT_STATE_NONE = 0,
|
||||
AT_STATE_DETECT,
|
||||
AT_STATE_TONE,
|
||||
AT_STATE_PAUSE,
|
||||
};
|
||||
|
||||
struct answer_tone {
|
||||
float sine;
|
||||
float cosine;
|
||||
float coeff;
|
||||
float *buffer;
|
||||
int buffer_size;
|
||||
int buffer_pos;
|
||||
float last_phase;
|
||||
enum at_state state;
|
||||
float tone_duration;
|
||||
float frequency_sum;
|
||||
int count;
|
||||
float last1_valid_phase;
|
||||
float last2_valid_phase;
|
||||
float last_valid_frequency;
|
||||
};
|
||||
|
||||
int answertone_init(struct answer_tone *at, int samplerate);
|
||||
void answertone_reset(struct answer_tone *at);
|
||||
void answertone_exit(struct answer_tone *at);
|
||||
enum at_fail answertone_check(struct answer_tone *at, float *phase_p, float *frequency_p);
|
||||
int answertone_process(struct answer_tone *at, int16_t *data, int length, bool phase_reversal);
|
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
* SpanDSP - a series of DSP components for telephony
|
||||
*
|
||||
* bit_operations.h - Various bit level operations, such as bit reversal
|
||||
*
|
||||
* Written by Steve Underwood <steveu@coppice.org>
|
||||
*
|
||||
* Copyright (C) 2006 Steve Underwood
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: bit_operations.h,v 1.11 2006/11/28 15:37:03 steveu Exp $
|
||||
*/
|
||||
|
||||
/*! \file */
|
||||
|
||||
#if !defined(_BIT_OPERATIONS_H_)
|
||||
#define _BIT_OPERATIONS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
/*! \brief Find the bit position of the highest set bit in a word
|
||||
\param bits The word to be searched
|
||||
\return The bit number of the highest set bit, or -1 if the word is zero. */
|
||||
static __inline__ int top_bit(unsigned int bits)
|
||||
{
|
||||
int res;
|
||||
|
||||
__asm__ (" xorl %[res],%[res];\n"
|
||||
" decl %[res];\n"
|
||||
" bsrl %[bits],%[res]\n"
|
||||
: [res] "=&r" (res)
|
||||
: [bits] "rm" (bits));
|
||||
return res;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
/*! \brief Find the bit position of the lowest set bit in a word
|
||||
\param bits The word to be searched
|
||||
\return The bit number of the lowest set bit, or -1 if the word is zero. */
|
||||
static __inline__ int bottom_bit(unsigned int bits)
|
||||
{
|
||||
int res;
|
||||
|
||||
__asm__ (" xorl %[res],%[res];\n"
|
||||
" decl %[res];\n"
|
||||
" bsfl %[bits],%[res]\n"
|
||||
: [res] "=&r" (res)
|
||||
: [bits] "rm" (bits));
|
||||
return res;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
#else
|
||||
static __inline__ int top_bit(unsigned int bits)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (bits == 0)
|
||||
return -1;
|
||||
i = 0;
|
||||
if (bits & 0xFFFF0000)
|
||||
{
|
||||
bits &= 0xFFFF0000;
|
||||
i += 16;
|
||||
}
|
||||
if (bits & 0xFF00FF00)
|
||||
{
|
||||
bits &= 0xFF00FF00;
|
||||
i += 8;
|
||||
}
|
||||
if (bits & 0xF0F0F0F0)
|
||||
{
|
||||
bits &= 0xF0F0F0F0;
|
||||
i += 4;
|
||||
}
|
||||
if (bits & 0xCCCCCCCC)
|
||||
{
|
||||
bits &= 0xCCCCCCCC;
|
||||
i += 2;
|
||||
}
|
||||
if (bits & 0xAAAAAAAA)
|
||||
{
|
||||
bits &= 0xAAAAAAAA;
|
||||
i += 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static __inline__ int bottom_bit(unsigned int bits)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (bits == 0)
|
||||
return -1;
|
||||
i = 32;
|
||||
if (bits & 0x0000FFFF)
|
||||
{
|
||||
bits &= 0x0000FFFF;
|
||||
i -= 16;
|
||||
}
|
||||
if (bits & 0x00FF00FF)
|
||||
{
|
||||
bits &= 0x00FF00FF;
|
||||
i -= 8;
|
||||
}
|
||||
if (bits & 0x0F0F0F0F)
|
||||
{
|
||||
bits &= 0x0F0F0F0F;
|
||||
i -= 4;
|
||||
}
|
||||
if (bits & 0x33333333)
|
||||
{
|
||||
bits &= 0x33333333;
|
||||
i -= 2;
|
||||
}
|
||||
if (bits & 0x55555555)
|
||||
{
|
||||
bits &= 0x55555555;
|
||||
i -= 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
#endif
|
||||
|
||||
/*! \brief Bit reverse a byte.
|
||||
\param data The byte to be reversed.
|
||||
\return The bit reversed version of data. */
|
||||
static __inline__ uint8_t bit_reverse8(uint8_t x)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
/* If multiply is fast */
|
||||
return ((x*0x0802U & 0x22110U) | (x*0x8020U & 0x88440U))*0x10101U >> 16;
|
||||
#else
|
||||
/* If multiply is slow, but we have a barrel shifter */
|
||||
x = (x >> 4) | (x << 4);
|
||||
x = ((x & 0xCC) >> 2) | ((x & 0x33) << 2);
|
||||
return ((x & 0xAA) >> 1) | ((x & 0x55) << 1);
|
||||
#endif
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
/*! \brief Bit reverse a 16 bit word.
|
||||
\param data The word to be reversed.
|
||||
\return The bit reversed version of data. */
|
||||
uint16_t bit_reverse16(uint16_t data);
|
||||
|
||||
/*! \brief Bit reverse a 32 bit word.
|
||||
\param data The word to be reversed.
|
||||
\return The bit reversed version of data. */
|
||||
uint32_t bit_reverse32(uint32_t data);
|
||||
|
||||
/*! \brief Bit reverse each of the four bytes in a 32 bit word.
|
||||
\param data The word to be reversed.
|
||||
\return The bit reversed version of data. */
|
||||
uint32_t bit_reverse_4bytes(uint32_t data);
|
||||
|
||||
/*! \brief Find the number of set bits in a 32 bit word.
|
||||
\param x The word to be searched.
|
||||
\return The number of set bits. */
|
||||
int one_bits32(uint32_t x);
|
||||
|
||||
/*! \brief Create a mask as wide as the number in a 32 bit word.
|
||||
\param x The word to be searched.
|
||||
\return The mask. */
|
||||
uint32_t make_mask32(uint32_t x);
|
||||
|
||||
/*! \brief Create a mask as wide as the number in a 16 bit word.
|
||||
\param x The word to be searched.
|
||||
\return The mask. */
|
||||
uint16_t make_mask16(uint16_t x);
|
||||
|
||||
/*! \brief Find the least significant one in a word, and return a word
|
||||
with just that bit set.
|
||||
\param x The word to be searched.
|
||||
\return The word with the single set bit. */
|
||||
static __inline__ uint32_t least_significant_one32(uint32_t x)
|
||||
{
|
||||
return (x & (-(int32_t) x));
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
/*! \brief Find the most significant one in a word, and return a word
|
||||
with just that bit set.
|
||||
\param x The word to be searched.
|
||||
\return The word with the single set bit. */
|
||||
static __inline__ uint32_t most_significant_one32(uint32_t x)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
return 1 << top_bit(x);
|
||||
#else
|
||||
x = make_mask32(x);
|
||||
return (x ^ (x >> 1));
|
||||
#endif
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
/*! \brief Find the parity of a byte.
|
||||
\param x The byte to be checked.
|
||||
\return 1 for odd, or 0 for even. */
|
||||
static __inline__ int parity8(uint8_t x)
|
||||
{
|
||||
x = (x ^ (x >> 4)) & 0x0F;
|
||||
return (0x6996 >> x) & 1;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
/*! \brief Find the parity of a 16 bit word.
|
||||
\param x The word to be checked.
|
||||
\return 1 for odd, or 0 for even. */
|
||||
static __inline__ int parity16(uint16_t x)
|
||||
{
|
||||
x ^= (x >> 8);
|
||||
x = (x ^ (x >> 4)) & 0x0F;
|
||||
return (0x6996 >> x) & 1;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
/*! \brief Find the parity of a 32 bit word.
|
||||
\param x The word to be checked.
|
||||
\return 1 for odd, or 0 for even. */
|
||||
static __inline__ int parity32(uint32_t x)
|
||||
{
|
||||
x ^= (x >> 16);
|
||||
x ^= (x >> 8);
|
||||
x = (x ^ (x >> 4)) & 0x0F;
|
||||
return (0x6996 >> x) & 1;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/*- End of file ------------------------------------------------------------*/
|
|
@ -0,0 +1,658 @@
|
|||
/*
|
||||
* SpanDSP - a series of DSP components for telephony
|
||||
*
|
||||
* echo.c - A line echo canceller. This code is being developed
|
||||
* against and partially complies with G168.
|
||||
*
|
||||
* Written by Steve Underwood <steveu@coppice.org>
|
||||
* and David Rowe <david_at_rowetel_dot_com>
|
||||
*
|
||||
* Copyright (C) 2001, 2003 Steve Underwood, 2007 David Rowe
|
||||
*
|
||||
* Based on a bit from here, a bit from there, eye of toad, ear of
|
||||
* bat, 15 years of failed attempts by David and a few fried brain
|
||||
* cells.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: echo.c,v 1.20 2006/12/01 18:00:48 steveu Exp $
|
||||
*/
|
||||
|
||||
/*! \file */
|
||||
|
||||
/* Implementation Notes
|
||||
David Rowe
|
||||
April 2007
|
||||
|
||||
This code started life as Steve's NLMS algorithm with a tap
|
||||
rotation algorithm to handle divergence during double talk. I
|
||||
added a Geigel Double Talk Detector (DTD) [2] and performed some
|
||||
G168 tests. However I had trouble meeting the G168 requirements,
|
||||
especially for double talk - there were always cases where my DTD
|
||||
failed, for example where near end speech was under the 6dB
|
||||
threshold required for declaring double talk.
|
||||
|
||||
So I tried a two path algorithm [1], which has so far given better
|
||||
results. The original tap rotation/Geigel algorithm is available
|
||||
in SVN http://svn.rowetel.com/software/oslec/tags/before_16bit.
|
||||
It's probably possible to make it work if some one wants to put some
|
||||
serious work into it.
|
||||
|
||||
At present no special treatment is provided for tones, which
|
||||
generally cause NLMS algorithms to diverge. Initial runs of a
|
||||
subset of the G168 tests for tones (e.g ./echo_test 6) show the
|
||||
current algorithm is passing OK, which is kind of surprising. The
|
||||
full set of tests needs to be performed to confirm this result.
|
||||
|
||||
One other interesting change is that I have managed to get the NLMS
|
||||
code to work with 16 bit coefficients, rather than the original 32
|
||||
bit coefficents. This reduces the MIPs and storage required.
|
||||
I evaulated the 16 bit port using g168_tests.sh and listening tests
|
||||
on 4 real-world samples.
|
||||
|
||||
I also attempted the implementation of a block based NLMS update
|
||||
[2] but although this passes g168_tests.sh it didn't converge well
|
||||
on the real-world samples. I have no idea why, perhaps a scaling
|
||||
problem. The block based code is also available in SVN
|
||||
http://svn.rowetel.com/software/oslec/tags/before_16bit. If this
|
||||
code can be debugged, it will lead to further reduction in MIPS, as
|
||||
the block update code maps nicely onto DSP instruction sets (it's a
|
||||
dot product) compared to the current sample-by-sample update.
|
||||
|
||||
Steve also has some nice notes on echo cancellers in echo.h
|
||||
|
||||
|
||||
References:
|
||||
|
||||
[1] Ochiai, Areseki, and Ogihara, "Echo Canceller with Two Echo
|
||||
Path Models", IEEE Transactions on communications, COM-25,
|
||||
No. 6, June
|
||||
1977.
|
||||
http://www.rowetel.com/images/echo/dual_path_paper.pdf
|
||||
|
||||
[2] The classic, very useful paper that tells you how to
|
||||
actually build a real world echo canceller:
|
||||
Messerschmitt, Hedberg, Cole, Haoui, Winship, "Digital Voice
|
||||
Echo Canceller with a TMS320020,
|
||||
http://www.rowetel.com/images/echo/spra129.pdf
|
||||
|
||||
[3] I have written a series of blog posts on this work, here is
|
||||
Part 1: http://www.rowetel.com/blog/?p=18
|
||||
|
||||
[4] The source code http://svn.rowetel.com/software/oslec/
|
||||
|
||||
[5] A nice reference on LMS filters:
|
||||
http://en.wikipedia.org/wiki/Least_mean_squares_filter
|
||||
|
||||
Credits:
|
||||
|
||||
Thanks to Steve Underwood, Jean-Marc Valin, and Ramakrishnan
|
||||
Muthukrishnan for their suggestions and email discussions. Thanks
|
||||
also to those people who collected echo samples for me such as
|
||||
Mark, Pawel, and Pavel.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/kernel.h> /* We're doing kernel work */
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#define malloc(a) kmalloc((a), GFP_KERNEL)
|
||||
#define free(a) kfree(a)
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include "bit_operations.h"
|
||||
#include "echo_cancel.h"
|
||||
|
||||
#if !defined(NULL)
|
||||
#define NULL (void *) 0
|
||||
#endif
|
||||
#if !defined(FALSE)
|
||||
#define FALSE 0
|
||||
#endif
|
||||
#if !defined(TRUE)
|
||||
#define TRUE (!FALSE)
|
||||
#endif
|
||||
|
||||
#define MIN_TX_POWER_FOR_ADAPTION 64
|
||||
#define MIN_RX_POWER_FOR_ADAPTION 64
|
||||
#define DTD_HANGOVER 600 /* 600 samples, or 75ms */
|
||||
#define DC_LOG2BETA 3 /* log2() of DC filter Beta */
|
||||
|
||||
/*-----------------------------------------------------------------------*\
|
||||
|
||||
FUNCTIONS
|
||||
|
||||
\*-----------------------------------------------------------------------*/
|
||||
|
||||
/* adapting coeffs using the traditional stochastic descent (N)LMS algorithm */
|
||||
|
||||
|
||||
#ifdef __BLACKFIN_ASM__
|
||||
static void __inline__ lms_adapt_bg(echo_can_state_t *ec, int clean, int shift)
|
||||
{
|
||||
int i, j;
|
||||
int offset1;
|
||||
int offset2;
|
||||
int factor;
|
||||
int exp;
|
||||
int16_t *phist;
|
||||
int n;
|
||||
|
||||
if (shift > 0)
|
||||
factor = clean << shift;
|
||||
else
|
||||
factor = clean >> -shift;
|
||||
|
||||
/* Update the FIR taps */
|
||||
|
||||
offset2 = ec->curr_pos;
|
||||
offset1 = ec->taps - offset2;
|
||||
phist = &ec->fir_state_bg.history[offset2];
|
||||
|
||||
/* st: and en: help us locate the assembler in echo.s */
|
||||
|
||||
//asm("st:");
|
||||
n = ec->taps;
|
||||
for (i = 0, j = offset2; i < n; i++, j++)
|
||||
{
|
||||
exp = *phist++ * factor;
|
||||
ec->fir_taps16[1][i] += (int16_t) ((exp+(1<<14)) >> 15);
|
||||
}
|
||||
//asm("en:");
|
||||
|
||||
/* Note the asm for the inner loop above generated by Blackfin gcc
|
||||
4.1.1 is pretty good (note even parallel instructions used):
|
||||
|
||||
R0 = W [P0++] (X);
|
||||
R0 *= R2;
|
||||
R0 = R0 + R3 (NS) ||
|
||||
R1 = W [P1] (X) ||
|
||||
nop;
|
||||
R0 >>>= 15;
|
||||
R0 = R0 + R1;
|
||||
W [P1++] = R0;
|
||||
|
||||
A block based update algorithm would be much faster but the
|
||||
above can't be improved on much. Every instruction saved in
|
||||
the loop above is 2 MIPs/ch! The for loop above is where the
|
||||
Blackfin spends most of it's time - about 17 MIPs/ch measured
|
||||
with speedtest.c with 256 taps (32ms). Write-back and
|
||||
Write-through cache gave about the same performance.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
IDEAS for further optimisation of lms_adapt_bg():
|
||||
|
||||
1/ The rounding is quite costly. Could we keep as 32 bit coeffs
|
||||
then make filter pluck the MS 16-bits of the coeffs when filtering?
|
||||
However this would lower potential optimisation of filter, as I
|
||||
think the dual-MAC architecture requires packed 16 bit coeffs.
|
||||
|
||||
2/ Block based update would be more efficient, as per comments above,
|
||||
could use dual MAC architecture.
|
||||
|
||||
3/ Look for same sample Blackfin LMS code, see if we can get dual-MAC
|
||||
packing.
|
||||
|
||||
4/ Execute the whole e/c in a block of say 20ms rather than sample
|
||||
by sample. Processing a few samples every ms is inefficient.
|
||||
*/
|
||||
|
||||
#else
|
||||
static __inline__ void lms_adapt_bg(echo_can_state_t *ec, int clean, int shift)
|
||||
{
|
||||
int i;
|
||||
|
||||
int offset1;
|
||||
int offset2;
|
||||
int factor;
|
||||
int exp;
|
||||
|
||||
if (shift > 0)
|
||||
factor = clean << shift;
|
||||
else
|
||||
factor = clean >> -shift;
|
||||
|
||||
/* Update the FIR taps */
|
||||
|
||||
offset2 = ec->curr_pos;
|
||||
offset1 = ec->taps - offset2;
|
||||
|
||||
for (i = ec->taps - 1; i >= offset1; i--)
|
||||
{
|
||||
exp = (ec->fir_state_bg.history[i - offset1]*factor);
|
||||
ec->fir_taps16[1][i] += (int16_t) ((exp+(1<<14)) >> 15);
|
||||
}
|
||||
for ( ; i >= 0; i--)
|
||||
{
|
||||
exp = (ec->fir_state_bg.history[i + offset2]*factor);
|
||||
ec->fir_taps16[1][i] += (int16_t) ((exp+(1<<14)) >> 15);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
echo_can_state_t *echo_can_create(int len, int adaption_mode)
|
||||
{
|
||||
echo_can_state_t *ec;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
ec = (echo_can_state_t *) malloc(sizeof(*ec));
|
||||
if (ec == NULL)
|
||||
return NULL;
|
||||
memset(ec, 0, sizeof(*ec));
|
||||
|
||||
ec->taps = len;
|
||||
ec->log2taps = top_bit(len);
|
||||
ec->curr_pos = ec->taps - 1;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
if ((ec->fir_taps16[i] = (int16_t *) malloc((ec->taps)*sizeof(int16_t))) == NULL)
|
||||
{
|
||||
for (j = 0; j < i; j++)
|
||||
free(ec->fir_taps16[j]);
|
||||
free(ec);
|
||||
return NULL;
|
||||
}
|
||||
memset(ec->fir_taps16[i], 0, (ec->taps)*sizeof(int16_t));
|
||||
}
|
||||
|
||||
fir16_create(&ec->fir_state,
|
||||
ec->fir_taps16[0],
|
||||
ec->taps);
|
||||
fir16_create(&ec->fir_state_bg,
|
||||
ec->fir_taps16[1],
|
||||
ec->taps);
|
||||
|
||||
for(i=0; i<5; i++) {
|
||||
ec->xvtx[i] = ec->yvtx[i] = ec->xvrx[i] = ec->yvrx[i] = 0;
|
||||
}
|
||||
|
||||
ec->cng_level = 1000;
|
||||
echo_can_adaption_mode(ec, adaption_mode);
|
||||
|
||||
ec->snapshot = (int16_t*)malloc(ec->taps*sizeof(int16_t));
|
||||
memset(ec->snapshot, 0, sizeof(int16_t)*ec->taps);
|
||||
|
||||
ec->cond_met = 0;
|
||||
ec->Pstates = 0;
|
||||
ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0;
|
||||
ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0;
|
||||
ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0;
|
||||
ec->Lbgn = ec->Lbgn_acc = 0;
|
||||
ec->Lbgn_upper = 200;
|
||||
ec->Lbgn_upper_acc = ec->Lbgn_upper << 13;
|
||||
|
||||
return ec;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
void echo_can_free(echo_can_state_t *ec)
|
||||
{
|
||||
int i;
|
||||
|
||||
fir16_free(&ec->fir_state);
|
||||
fir16_free(&ec->fir_state_bg);
|
||||
for (i = 0; i < 2; i++)
|
||||
free(ec->fir_taps16[i]);
|
||||
free(ec->snapshot);
|
||||
free(ec);
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
void echo_can_adaption_mode(echo_can_state_t *ec, int adaption_mode)
|
||||
{
|
||||
ec->adaption_mode = adaption_mode;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
void echo_can_flush(echo_can_state_t *ec)
|
||||
{
|
||||
int i;
|
||||
|
||||
ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0;
|
||||
ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0;
|
||||
ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0;
|
||||
|
||||
ec->Lbgn = ec->Lbgn_acc = 0;
|
||||
ec->Lbgn_upper = 200;
|
||||
ec->Lbgn_upper_acc = ec->Lbgn_upper << 13;
|
||||
|
||||
ec->nonupdate_dwell = 0;
|
||||
|
||||
fir16_flush(&ec->fir_state);
|
||||
fir16_flush(&ec->fir_state_bg);
|
||||
ec->fir_state.curr_pos = ec->taps - 1;
|
||||
ec->fir_state_bg.curr_pos = ec->taps - 1;
|
||||
for (i = 0; i < 2; i++)
|
||||
memset(ec->fir_taps16[i], 0, ec->taps*sizeof(int16_t));
|
||||
|
||||
ec->curr_pos = ec->taps - 1;
|
||||
ec->Pstates = 0;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
void echo_can_snapshot(echo_can_state_t *ec) {
|
||||
memcpy(ec->snapshot, ec->fir_taps16[0], ec->taps*sizeof(int16_t));
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
/* Dual Path Echo Canceller ------------------------------------------------*/
|
||||
|
||||
int16_t echo_can_update(echo_can_state_t *ec, int16_t tx, int16_t rx)
|
||||
{
|
||||
int32_t echo_value;
|
||||
int clean_bg;
|
||||
int tmp, tmp1;
|
||||
|
||||
/* Input scaling was found be required to prevent problems when tx
|
||||
starts clipping. Another possible way to handle this would be the
|
||||
filter coefficent scaling. */
|
||||
|
||||
ec->tx = tx; ec->rx = rx;
|
||||
tx >>=1;
|
||||
rx >>=1;
|
||||
|
||||
/*
|
||||
Filter DC, 3dB point is 160Hz (I think), note 32 bit precision required
|
||||
otherwise values do not track down to 0. Zero at DC, Pole at (1-Beta)
|
||||
only real axis. Some chip sets (like Si labs) don't need
|
||||
this, but something like a $10 X100P card does. Any DC really slows
|
||||
down convergence.
|
||||
|
||||
Note: removes some low frequency from the signal, this reduces
|
||||
the speech quality when listening to samples through headphones
|
||||
but may not be obvious through a telephone handset.
|
||||
|
||||
Note that the 3dB frequency in radians is approx Beta, e.g. for
|
||||
Beta = 2^(-3) = 0.125, 3dB freq is 0.125 rads = 159Hz.
|
||||
*/
|
||||
|
||||
if (ec->adaption_mode & ECHO_CAN_USE_RX_HPF) {
|
||||
tmp = rx << 15;
|
||||
#if 1
|
||||
/* Make sure the gain of the HPF is 1.0. This can still saturate a little under
|
||||
impulse conditions, and it might roll to 32768 and need clipping on sustained peak
|
||||
level signals. However, the scale of such clipping is small, and the error due to
|
||||
any saturation should not markedly affect the downstream processing. */
|
||||
tmp -= (tmp >> 4);
|
||||
#endif
|
||||
ec->rx_1 += -(ec->rx_1>>DC_LOG2BETA) + tmp - ec->rx_2;
|
||||
|
||||
/* hard limit filter to prevent clipping. Note that at this stage
|
||||
rx should be limited to +/- 16383 due to right shift above */
|
||||
tmp1 = ec->rx_1 >> 15;
|
||||
if (tmp1 > 16383) tmp1 = 16383;
|
||||
if (tmp1 < -16383) tmp1 = -16383;
|
||||
rx = tmp1;
|
||||
ec->rx_2 = tmp;
|
||||
}
|
||||
|
||||
/* Block average of power in the filter states. Used for
|
||||
adaption power calculation. */
|
||||
|
||||
{
|
||||
int new, old;
|
||||
|
||||
/* efficient "out with the old and in with the new" algorithm so
|
||||
we don't have to recalculate over the whole block of
|
||||
samples. */
|
||||
new = (int)tx * (int)tx;
|
||||
old = (int)ec->fir_state.history[ec->fir_state.curr_pos] *
|
||||
(int)ec->fir_state.history[ec->fir_state.curr_pos];
|
||||
ec->Pstates += ((new - old) + (1<<(ec->log2taps-1))) >> ec->log2taps;
|
||||
if (ec->Pstates < 0) ec->Pstates = 0;
|
||||
}
|
||||
|
||||
/* Calculate short term average levels using simple single pole IIRs */
|
||||
|
||||
ec->Ltxacc += abs(tx) - ec->Ltx;
|
||||
ec->Ltx = (ec->Ltxacc + (1<<4)) >> 5;
|
||||
ec->Lrxacc += abs(rx) - ec->Lrx;
|
||||
ec->Lrx = (ec->Lrxacc + (1<<4)) >> 5;
|
||||
|
||||
/* Foreground filter ---------------------------------------------------*/
|
||||
|
||||
ec->fir_state.coeffs = ec->fir_taps16[0];
|
||||
echo_value = fir16(&ec->fir_state, tx);
|
||||
ec->clean = rx - echo_value;
|
||||
ec->Lcleanacc += abs(ec->clean) - ec->Lclean;
|
||||
ec->Lclean = (ec->Lcleanacc + (1<<4)) >> 5;
|
||||
|
||||
/* Background filter ---------------------------------------------------*/
|
||||
|
||||
echo_value = fir16(&ec->fir_state_bg, tx);
|
||||
clean_bg = rx - echo_value;
|
||||
ec->Lclean_bgacc += abs(clean_bg) - ec->Lclean_bg;
|
||||
ec->Lclean_bg = (ec->Lclean_bgacc + (1<<4)) >> 5;
|
||||
|
||||
/* Background Filter adaption -----------------------------------------*/
|
||||
|
||||
/* Almost always adap bg filter, just simple DT and energy
|
||||
detection to minimise adaption in cases of strong double talk.
|
||||
However this is not critical for the dual path algorithm.
|
||||
*/
|
||||
ec->factor = 0;
|
||||
ec->shift = 0;
|
||||
if ((ec->nonupdate_dwell == 0)) {
|
||||
int P, logP, shift;
|
||||
|
||||
/* Determine:
|
||||
|
||||
f = Beta * clean_bg_rx/P ------ (1)
|
||||
|
||||
where P is the total power in the filter states.
|
||||
|
||||
The Boffins have shown that if we obey (1) we converge
|
||||
quickly and avoid instability.
|
||||
|
||||
The correct factor f must be in Q30, as this is the fixed
|
||||
point format required by the lms_adapt_bg() function,
|
||||
therefore the scaled version of (1) is:
|
||||
|
||||
(2^30) * f = (2^30) * Beta * clean_bg_rx/P
|
||||
factor = (2^30) * Beta * clean_bg_rx/P ----- (2)
|
||||
|
||||
We have chosen Beta = 0.25 by experiment, so:
|
||||
|
||||
factor = (2^30) * (2^-2) * clean_bg_rx/P
|
||||
|
||||
(30 - 2 - log2(P))
|
||||
factor = clean_bg_rx 2 ----- (3)
|
||||
|
||||
To avoid a divide we approximate log2(P) as top_bit(P),
|
||||
which returns the position of the highest non-zero bit in
|
||||
P. This approximation introduces an error as large as a
|
||||
factor of 2, but the algorithm seems to handle it OK.
|
||||
|
||||
Come to think of it a divide may not be a big deal on a
|
||||
modern DSP, so its probably worth checking out the cycles
|
||||
for a divide versus a top_bit() implementation.
|
||||
*/
|
||||
|
||||
P = MIN_TX_POWER_FOR_ADAPTION + ec->Pstates;
|
||||
logP = top_bit(P) + ec->log2taps;
|
||||
shift = 30 - 2 - logP;
|
||||
ec->shift = shift;
|
||||
|
||||
lms_adapt_bg(ec, clean_bg, shift);
|
||||
}
|
||||
|
||||
/* very simple DTD to make sure we dont try and adapt with strong
|
||||
near end speech */
|
||||
|
||||
ec->adapt = 0;
|
||||
if ((ec->Lrx > MIN_RX_POWER_FOR_ADAPTION) && (ec->Lrx > ec->Ltx))
|
||||
ec->nonupdate_dwell = DTD_HANGOVER;
|
||||
if (ec->nonupdate_dwell)
|
||||
ec->nonupdate_dwell--;
|
||||
|
||||
/* Transfer logic ------------------------------------------------------*/
|
||||
|
||||
/* These conditions are from the dual path paper [1], I messed with
|
||||
them a bit to improve performance. */
|
||||
|
||||
if ((ec->adaption_mode & ECHO_CAN_USE_ADAPTION) &&
|
||||
(ec->nonupdate_dwell == 0) &&
|
||||
(8*ec->Lclean_bg < 7*ec->Lclean) /* (ec->Lclean_bg < 0.875*ec->Lclean) */ &&
|
||||
(8*ec->Lclean_bg < ec->Ltx) /* (ec->Lclean_bg < 0.125*ec->Ltx) */ )
|
||||
{
|
||||
if (ec->cond_met == 6) {
|
||||
/* BG filter has had better results for 6 consecutive samples */
|
||||
ec->adapt = 1;
|
||||
memcpy(ec->fir_taps16[0], ec->fir_taps16[1], ec->taps*sizeof(int16_t));
|
||||
}
|
||||
else
|
||||
ec->cond_met++;
|
||||
}
|
||||
else
|
||||
ec->cond_met = 0;
|
||||
|
||||
/* Non-Linear Processing ---------------------------------------------------*/
|
||||
|
||||
ec->clean_nlp = ec->clean;
|
||||
if (ec->adaption_mode & ECHO_CAN_USE_NLP)
|
||||
{
|
||||
/* Non-linear processor - a fancy way to say "zap small signals, to avoid
|
||||
residual echo due to (uLaw/ALaw) non-linearity in the channel.". */
|
||||
|
||||
if ((16*ec->Lclean < ec->Ltx))
|
||||
{
|
||||
/* Our e/c has improved echo by at least 24 dB (each factor of 2 is 6dB,
|
||||
so 2*2*2*2=16 is the same as 6+6+6+6=24dB) */
|
||||
if (ec->adaption_mode & ECHO_CAN_USE_CNG)
|
||||
{
|
||||
ec->cng_level = ec->Lbgn;
|
||||
|
||||
/* Very elementary comfort noise generation. Just random
|
||||
numbers rolled off very vaguely Hoth-like. DR: This
|
||||
noise doesn't sound quite right to me - I suspect there
|
||||
are some overlfow issues in the filtering as it's too
|
||||
"crackly". TODO: debug this, maybe just play noise at
|
||||
high level or look at spectrum.
|
||||
*/
|
||||
|
||||
ec->cng_rndnum = 1664525U*ec->cng_rndnum + 1013904223U;
|
||||
ec->cng_filter = ((ec->cng_rndnum & 0xFFFF) - 32768 + 5*ec->cng_filter) >> 3;
|
||||
ec->clean_nlp = (ec->cng_filter*ec->cng_level*8) >> 14;
|
||||
|
||||
}
|
||||
else if (ec->adaption_mode & ECHO_CAN_USE_CLIP)
|
||||
{
|
||||
/* This sounds much better than CNG */
|
||||
if (ec->clean_nlp > ec->Lbgn)
|
||||
ec->clean_nlp = ec->Lbgn;
|
||||
if (ec->clean_nlp < -ec->Lbgn)
|
||||
ec->clean_nlp = -ec->Lbgn;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* just mute the residual, doesn't sound very good, used mainly
|
||||
in G168 tests */
|
||||
ec->clean_nlp = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Background noise estimator. I tried a few algorithms
|
||||
here without much luck. This very simple one seems to
|
||||
work best, we just average the level using a slow (1 sec
|
||||
time const) filter if the current level is less than a
|
||||
(experimentally derived) constant. This means we dont
|
||||
include high level signals like near end speech. When
|
||||
combined with CNG or especially CLIP seems to work OK.
|
||||
*/
|
||||
if (ec->Lclean < 40) {
|
||||
ec->Lbgn_acc += abs(ec->clean) - ec->Lbgn;
|
||||
ec->Lbgn = (ec->Lbgn_acc + (1<<11)) >> 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Roll around the taps buffer */
|
||||
if (ec->curr_pos <= 0)
|
||||
ec->curr_pos = ec->taps;
|
||||
ec->curr_pos--;
|
||||
|
||||
if (ec->adaption_mode & ECHO_CAN_DISABLE)
|
||||
ec->clean_nlp = rx;
|
||||
|
||||
/* Output scaled back up again to match input scaling */
|
||||
|
||||
return (int16_t) ec->clean_nlp << 1;
|
||||
}
|
||||
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
/* This function is seperated from the echo canceller is it is usually called
|
||||
as part of the tx process. See rx HP (DC blocking) filter above, it's
|
||||
the same design.
|
||||
|
||||
Some soft phones send speech signals with a lot of low frequency
|
||||
energy, e.g. down to 20Hz. This can make the hybrid non-linear
|
||||
which causes the echo canceller to fall over. This filter can help
|
||||
by removing any low frequency before it gets to the tx port of the
|
||||
hybrid.
|
||||
|
||||
It can also help by removing and DC in the tx signal. DC is bad
|
||||
for LMS algorithms.
|
||||
|
||||
This is one of the classic DC removal filters, adjusted to provide sufficient
|
||||
bass rolloff to meet the above requirement to protect hybrids from things that
|
||||
upset them. The difference between successive samples produces a lousy HPF, and
|
||||
then a suitably placed pole flattens things out. The final result is a nicely
|
||||
rolled off bass end. The filtering is implemented with extended fractional
|
||||
precision, which noise shapes things, giving very clean DC removal.
|
||||
*/
|
||||
|
||||
int16_t echo_can_hpf_tx(echo_can_state_t *ec, int16_t tx) {
|
||||
int tmp, tmp1;
|
||||
|
||||
if (ec->adaption_mode & ECHO_CAN_USE_TX_HPF) {
|
||||
tmp = tx << 15;
|
||||
#if 1
|
||||
/* Make sure the gain of the HPF is 1.0. The first can still saturate a little under
|
||||
impulse conditions, and it might roll to 32768 and need clipping on sustained peak
|
||||
level signals. However, the scale of such clipping is small, and the error due to
|
||||
any saturation should not markedly affect the downstream processing. */
|
||||
tmp -= (tmp >> 4);
|
||||
#endif
|
||||
ec->tx_1 += -(ec->tx_1>>DC_LOG2BETA) + tmp - ec->tx_2;
|
||||
tmp1 = ec->tx_1 >> 15;
|
||||
if (tmp1 > 32767) tmp1 = 32767;
|
||||
if (tmp1 < -32767) tmp1 = -32767;
|
||||
tx = tmp1;
|
||||
ec->tx_2 = tmp;
|
||||
}
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
/*- End of file ------------------------------------------------------------*/
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* SpanDSP - a series of DSP components for telephony
|
||||
*
|
||||
* echo.c - A line echo canceller. This code is being developed
|
||||
* against and partially complies with G168.
|
||||
*
|
||||
* Written by Steve Underwood <steveu@coppice.org>
|
||||
* and David Rowe <david_at_rowetel_dot_com>
|
||||
*
|
||||
* Copyright (C) 2001 Steve Underwood and 2007 David Rowe
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: echo.h,v 1.9 2006/10/24 13:45:28 steveu Exp $
|
||||
*/
|
||||
|
||||
/*! \file */
|
||||
|
||||
#if !defined(_ECHO_H_)
|
||||
#define _ECHO_H_
|
||||
|
||||
/*! \page echo_can_page Line echo cancellation for voice
|
||||
|
||||
\section echo_can_page_sec_1 What does it do?
|
||||
This module aims to provide G.168-2002 compliant echo cancellation, to remove
|
||||
electrical echoes (e.g. from 2-4 wire hybrids) from voice calls.
|
||||
|
||||
\section echo_can_page_sec_2 How does it work?
|
||||
The heart of the echo cancellor is FIR filter. This is adapted to match the echo
|
||||
impulse response of the telephone line. It must be long enough to adequately cover
|
||||
the duration of that impulse response. The signal transmitted to the telephone line
|
||||
is passed through the FIR filter. Once the FIR is properly adapted, the resulting
|
||||
output is an estimate of the echo signal received from the line. This is subtracted
|
||||
from the received signal. The result is an estimate of the signal which originated
|
||||
at the far end of the line, free from echos of our own transmitted signal.
|
||||
|
||||
The least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and was
|
||||
introduced in 1960. It is the commonest form of filter adaption used in things
|
||||
like modem line equalisers and line echo cancellers. There it works very well.
|
||||
However, it only works well for signals of constant amplitude. It works very poorly
|
||||
for things like speech echo cancellation, where the signal level varies widely.
|
||||
This is quite easy to fix. If the signal level is normalised - similar to applying
|
||||
AGC - LMS can work as well for a signal of varying amplitude as it does for a modem
|
||||
signal. This normalised least mean squares (NLMS) algorithm is the commonest one used
|
||||
for speech echo cancellation. Many other algorithms exist - e.g. RLS (essentially
|
||||
the same as Kalman filtering), FAP, etc. Some perform significantly better than NLMS.
|
||||
However, factors such as computational complexity and patents favour the use of NLMS.
|
||||
|
||||
A simple refinement to NLMS can improve its performance with speech. NLMS tends
|
||||
to adapt best to the strongest parts of a signal. If the signal is white noise,
|
||||
the NLMS algorithm works very well. However, speech has more low frequency than
|
||||
high frequency content. Pre-whitening (i.e. filtering the signal to flatten
|
||||
its spectrum) the echo signal improves the adapt rate for speech, and ensures the
|
||||
final residual signal is not heavily biased towards high frequencies. A very low
|
||||
complexity filter is adequate for this, so pre-whitening adds little to the
|
||||
compute requirements of the echo canceller.
|
||||
|
||||
An FIR filter adapted using pre-whitened NLMS performs well, provided certain
|
||||
conditions are met:
|
||||
|
||||
- The transmitted signal has poor self-correlation.
|
||||
- There is no signal being generated within the environment being cancelled.
|
||||
|
||||
The difficulty is that neither of these can be guaranteed.
|
||||
|
||||
If the adaption is performed while transmitting noise (or something fairly noise
|
||||
like, such as voice) the adaption works very well. If the adaption is performed
|
||||
while transmitting something highly correlative (typically narrow band energy
|
||||
such as signalling tones or DTMF), the adaption can go seriously wrong. The reason
|
||||
is there is only one solution for the adaption on a near random signal - the impulse
|
||||
response of the line. For a repetitive signal, there are any number of solutions
|
||||
which converge the adaption, and nothing guides the adaption to choose the generalised
|
||||
one. Allowing an untrained canceller to converge on this kind of narrowband
|
||||
energy probably a good thing, since at least it cancels the tones. Allowing a well
|
||||
converged canceller to continue converging on such energy is just a way to ruin
|
||||
its generalised adaption. A narrowband detector is needed, so adapation can be
|
||||
suspended at appropriate times.
|
||||
|
||||
The adaption process is based on trying to eliminate the received signal. When
|
||||
there is any signal from within the environment being cancelled it may upset the
|
||||
adaption process. Similarly, if the signal we are transmitting is small, noise
|
||||
may dominate and disturb the adaption process. If we can ensure that the
|
||||
adaption is only performed when we are transmitting a significant signal level,
|
||||
and the environment is not, things will be OK. Clearly, it is easy to tell when
|
||||
we are sending a significant signal. Telling, if the environment is generating a
|
||||
significant signal, and doing it with sufficient speed that the adaption will
|
||||
not have diverged too much more we stop it, is a little harder.
|
||||
|
||||
The key problem in detecting when the environment is sourcing significant energy
|
||||
is that we must do this very quickly. Given a reasonably long sample of the
|
||||
received signal, there are a number of strategies which may be used to assess
|
||||
whether that signal contains a strong far end component. However, by the time
|
||||
that assessment is complete the far end signal will have already caused major
|
||||
mis-convergence in the adaption process. An assessment algorithm is needed which
|
||||
produces a fairly accurate result from a very short burst of far end energy.
|
||||
|
||||
\section echo_can_page_sec_3 How do I use it?
|
||||
The echo cancellor processes both the transmit and receive streams sample by
|
||||
sample. The processing function is not declared inline. Unfortunately,
|
||||
cancellation requires many operations per sample, so the call overhead is only a
|
||||
minor burden.
|
||||
*/
|
||||
|
||||
#include "../../config.h"
|
||||
|
||||
#ifdef HAVE_MMX_INSTRUCTIONS
|
||||
#define USE_MMX
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SSE2_INSTRUCTIONS
|
||||
#define USE_SSE2
|
||||
#endif
|
||||
|
||||
#include "fir.h"
|
||||
|
||||
/* Mask bits for the adaption mode */
|
||||
|
||||
#define ECHO_CAN_USE_ADAPTION 0x01
|
||||
#define ECHO_CAN_USE_NLP 0x02
|
||||
#define ECHO_CAN_USE_CNG 0x04
|
||||
#define ECHO_CAN_USE_CLIP 0x08
|
||||
#define ECHO_CAN_USE_TX_HPF 0x10
|
||||
#define ECHO_CAN_USE_RX_HPF 0x20
|
||||
#define ECHO_CAN_DISABLE 0x40
|
||||
|
||||
/*!
|
||||
G.168 echo canceller descriptor. This defines the working state for a line
|
||||
echo canceller.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int16_t tx,rx;
|
||||
int16_t clean;
|
||||
int16_t clean_nlp;
|
||||
|
||||
int nonupdate_dwell;
|
||||
int curr_pos;
|
||||
int taps;
|
||||
int log2taps;
|
||||
int adaption_mode;
|
||||
|
||||
int cond_met;
|
||||
int32_t Pstates;
|
||||
int16_t adapt;
|
||||
int32_t factor;
|
||||
int16_t shift;
|
||||
|
||||
/* Average levels and averaging filter states */
|
||||
int Ltxacc, Lrxacc, Lcleanacc, Lclean_bgacc;
|
||||
int Ltx, Lrx;
|
||||
int Lclean;
|
||||
int Lclean_bg;
|
||||
int Lbgn, Lbgn_acc, Lbgn_upper, Lbgn_upper_acc;
|
||||
|
||||
/* foreground and background filter states */
|
||||
fir16_state_t fir_state;
|
||||
fir16_state_t fir_state_bg;
|
||||
int16_t *fir_taps16[2];
|
||||
|
||||
/* DC blocking filter states */
|
||||
int tx_1, tx_2, rx_1, rx_2;
|
||||
|
||||
/* optional High Pass Filter states */
|
||||
int32_t xvtx[5], yvtx[5];
|
||||
int32_t xvrx[5], yvrx[5];
|
||||
|
||||
/* Parameters for the optional Hoth noise generator */
|
||||
int cng_level;
|
||||
int cng_rndnum;
|
||||
int cng_filter;
|
||||
|
||||
/* snapshot sample of coeffs used for development */
|
||||
int16_t *snapshot;
|
||||
|
||||
} echo_can_state_t;
|
||||
|
||||
/*! Create a voice echo canceller context.
|
||||
\param len The length of the canceller, in samples.
|
||||
\return The new canceller context, or NULL if the canceller could not be created.
|
||||
*/
|
||||
echo_can_state_t *echo_can_create(int len, int adaption_mode);
|
||||
|
||||
/*! Free a voice echo canceller context.
|
||||
\param ec The echo canceller context.
|
||||
*/
|
||||
void echo_can_free(echo_can_state_t *ec);
|
||||
|
||||
/*! Flush (reinitialise) a voice echo canceller context.
|
||||
\param ec The echo canceller context.
|
||||
*/
|
||||
void echo_can_flush(echo_can_state_t *ec);
|
||||
|
||||
/*! Set the adaption mode of a voice echo canceller context.
|
||||
\param ec The echo canceller context.
|
||||
\param adapt The mode.
|
||||
*/
|
||||
void echo_can_adaption_mode(echo_can_state_t *ec, int adaption_mode);
|
||||
|
||||
void echo_can_snapshot(echo_can_state_t *ec);
|
||||
|
||||
/*! Process a sample through a voice echo canceller.
|
||||
\param ec The echo canceller context.
|
||||
\param tx The transmitted audio sample.
|
||||
\param rx The received audio sample.
|
||||
\return The clean (echo cancelled) received sample.
|
||||
*/
|
||||
int16_t echo_can_update(echo_can_state_t *ec, int16_t tx, int16_t rx);
|
||||
|
||||
/*! Process to high pass filter the tx signal.
|
||||
\param ec The echo canceller context.
|
||||
\param tx The transmitted auio sample.
|
||||
\return The HP filtered transmit sample, send this to your D/A.
|
||||
*/
|
||||
int16_t echo_can_hpf_tx(echo_can_state_t *ec, int16_t tx);
|
||||
|
||||
#endif
|
||||
/*- End of file ------------------------------------------------------------*/
|
|
@ -0,0 +1,357 @@
|
|||
/*
|
||||
* (C) 2023 by Andreas Eversberg <andreas@eversberg.eu>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* This Echo Suppressor is based on ITU-T G.164, but it does not conform with it!
|
||||
*
|
||||
* The 'receive' signal (received by the interface) will cause an echo what is
|
||||
* included in the 'send' signal (sent by the interface). To remove echo, the
|
||||
* 'send' signal is muted when speech is received by the interface.
|
||||
*
|
||||
* Figure 6 / G,164 shows a diagram with the states in this code:
|
||||
*
|
||||
* The 'silence' state is depicted as X. It is entered when both sides do not
|
||||
* talk.
|
||||
*
|
||||
* The 'suppression' state is depticted as Z. It is entered when speech is
|
||||
* received by the interface.
|
||||
*
|
||||
* The 'break-in' state is depicted as W. It is entered when speech is sent to
|
||||
* the interface.
|
||||
*
|
||||
* The depiced area V is implemented as a 6 dB loss, so that a transition from
|
||||
* 'break-in' state to 'suppession' state requires 6 dB more level in the
|
||||
* receive path.
|
||||
*
|
||||
* The input is based on an int16_t that has a range of +3 dBm.
|
||||
*
|
||||
* Timers are used to enter state (operate) or leave state (hangover).
|
||||
*
|
||||
* Six filters are used, to detect the speech level, three in each direction:
|
||||
*
|
||||
* A low-pass filter and a high-pass filter are used to limit the band within
|
||||
* the 500-3400 Hz range.
|
||||
*
|
||||
* The the signal is rectified, amplified and filtered with a low-pass filter.
|
||||
* The resulting level represents the average volume of the input. (The peak
|
||||
* level of a sine wave at the input will result in same level on the output.)
|
||||
*
|
||||
* In the 'suppression' state, the send signal is muted.
|
||||
*
|
||||
* In the 'break-in' state, the receive signal is NOT changed. The loss is only
|
||||
* inserted in the processing, not is the actual signal.
|
||||
*/
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "echo_suppress.h"
|
||||
|
||||
#define db2level(db) pow(10, (double)db / 20.0)
|
||||
#define level2db(level) (20 * log10(level))
|
||||
|
||||
#define SUPPRESS_THRESHOLD -31.0
|
||||
#define RELEASE_SUPPRESS -31.0
|
||||
#define INSERTION_LOSS 6.0
|
||||
#define DT_MIN_LEVEL_DB -20
|
||||
#define DT_MAX_OFFSET_DB 0.2
|
||||
|
||||
#define SUPPRESS_OPERATE 0.000
|
||||
#define SUPPRESS_HANGOVER 0.050
|
||||
#define BREAK_IN_OPERATE 0.030
|
||||
#define BREAK_IN_HANGOVER 0.050
|
||||
#define DT_INTERVAL 0.250
|
||||
|
||||
#define LP_FREQUENCY 3400.0
|
||||
#define HP_FREQUENCY 500.0
|
||||
#define ENV_FREQUENCY 500.0
|
||||
#define DT_ENV_FREQUENCY 10.0
|
||||
|
||||
//#define DEBUG_ES
|
||||
|
||||
/*
|
||||
* IIR filter
|
||||
*/
|
||||
|
||||
static inline void iir_lowpass_init(iir_filter_t *filter, float frequency, int samplerate)
|
||||
{
|
||||
float Fc, Q, K, norm;
|
||||
|
||||
Q = pow(sqrt(0.5), 1.0); /* 0.7071 */
|
||||
Fc = frequency / (float)samplerate;
|
||||
K = tan(M_PI * Fc);
|
||||
norm = 1 / (1 + K / Q + K * K);
|
||||
filter->a0 = K * K * norm;
|
||||
filter->a1 = 2 * filter->a0;
|
||||
filter->a2 = filter->a0;
|
||||
filter->b1 = 2 * (K * K - 1) * norm;
|
||||
filter->b2 = (1 - K / Q + K * K) * norm;
|
||||
}
|
||||
|
||||
static inline void iir_highpass_init(iir_filter_t *filter, float frequency, int samplerate)
|
||||
{
|
||||
float Fc, Q, K, norm;
|
||||
|
||||
Q = pow(sqrt(0.5), 1.0); /* 0.7071 */
|
||||
Fc = frequency / (float)samplerate;
|
||||
K = tan(M_PI * Fc);
|
||||
norm = 1 / (1 + K / Q + K * K);
|
||||
filter->a0 = 1 * norm;
|
||||
filter->a1 = -2 * filter->a0;
|
||||
filter->a2 = filter->a0;
|
||||
filter->b1 = 2 * (K * K - 1) * norm;
|
||||
filter->b2 = (1 - K / Q + K * K) * norm;
|
||||
}
|
||||
|
||||
static inline void iir_process(iir_filter_t *filter, float *samples, int length)
|
||||
{
|
||||
float a0, a1, a2, b1, b2;
|
||||
float z1, z2;
|
||||
float in, out;
|
||||
int i;
|
||||
|
||||
/* get states */
|
||||
a0 = filter->a0;
|
||||
a1 = filter->a1;
|
||||
a2 = filter->a2;
|
||||
b1 = filter->b1;
|
||||
b2 = filter->b2;
|
||||
|
||||
z1 = filter->z1;
|
||||
z2 = filter->z2;
|
||||
|
||||
/* process filter */
|
||||
for (i = 0; i < length; i++) {
|
||||
/* add a small value, otherwise this loop will perform really bad on 0-samples */
|
||||
in = *samples + 0.000000001;
|
||||
out = in * a0 + z1;
|
||||
z1 = in * a1 + z2 - b1 * out;
|
||||
z2 = in * a2 - b2 * out;
|
||||
in = out;
|
||||
*samples++ = in;
|
||||
}
|
||||
|
||||
filter->z1 = z1;
|
||||
filter->z2 = z2;
|
||||
}
|
||||
|
||||
static inline void iir_reset(iir_filter_t *filter)
|
||||
{
|
||||
filter->z1 = 0.0;
|
||||
filter->z2 = 0.0;
|
||||
}
|
||||
|
||||
/*
|
||||
* echo suppressor
|
||||
*/
|
||||
|
||||
echo_sup_state_t *echo_sup_create(void *ctx, int samplerate)
|
||||
{
|
||||
echo_sup_state_t *es;
|
||||
|
||||
es = talloc_zero(ctx, struct echo_sup_state);
|
||||
if (!es)
|
||||
return NULL;
|
||||
|
||||
es->suppress_threshold = db2level(SUPPRESS_THRESHOLD);
|
||||
es->release_suppress = db2level(RELEASE_SUPPRESS);
|
||||
es->insertion_loss = db2level(INSERTION_LOSS);
|
||||
|
||||
es->suppress_operate = floor(SUPPRESS_OPERATE * (double)samplerate);
|
||||
es->suppress_hangover = floor(SUPPRESS_HANGOVER * (double)samplerate);
|
||||
es->break_in_operate = floor(BREAK_IN_OPERATE * (double)samplerate);
|
||||
es->break_in_hangover = floor(BREAK_IN_HANGOVER * (double)samplerate);
|
||||
|
||||
iir_lowpass_init(&es->lp_send, LP_FREQUENCY, samplerate);
|
||||
iir_lowpass_init(&es->lp_receive, LP_FREQUENCY, samplerate);
|
||||
iir_highpass_init(&es->hp_send, HP_FREQUENCY, samplerate);
|
||||
iir_highpass_init(&es->hp_receive, HP_FREQUENCY, samplerate);
|
||||
iir_lowpass_init(&es->env_send, ENV_FREQUENCY, samplerate);
|
||||
iir_lowpass_init(&es->env_receive, ENV_FREQUENCY, samplerate);
|
||||
iir_lowpass_init(&es->dt_receive, DT_ENV_FREQUENCY, samplerate);
|
||||
|
||||
es->dt_interval = floor(DT_INTERVAL * (double)samplerate);
|
||||
|
||||
return es;
|
||||
}
|
||||
|
||||
void echo_sup_free(echo_sup_state_t *es)
|
||||
{
|
||||
talloc_free(es);
|
||||
}
|
||||
|
||||
/* rx is what we received from user interface, aka 'send' */
|
||||
/* tx is what we sent to user interface, aka 'receive' */
|
||||
int16_t echo_sup_update(echo_sup_state_t *es, int16_t tx, int16_t rx)
|
||||
{
|
||||
float send, receive, dt, offset;
|
||||
|
||||
/* convert from integer samples to float (@ 0 dBm scale) */
|
||||
receive = (float)tx / 23196.0; /* towards user interface */
|
||||
send = (float)rx / 23196.0; /* from user interface */
|
||||
|
||||
/* filter band */
|
||||
iir_process(&es->lp_send, &send, 1);
|
||||
iir_process(&es->lp_receive, &receive, 1);
|
||||
iir_process(&es->hp_send, &send, 1);
|
||||
iir_process(&es->hp_receive, &receive, 1);
|
||||
|
||||
/* get absolute value (rectifying with 3 dB gain) */
|
||||
send = fabsf(send) * 1.4142136;
|
||||
receive = dt = fabsf(receive) * 1.4142136;
|
||||
|
||||
/* filter envelope */
|
||||
iir_process(&es->env_send, &send, 1);
|
||||
iir_process(&es->env_receive, &receive, 1);
|
||||
iir_process(&es->dt_receive, &dt, 1);
|
||||
|
||||
/* detect dial tone and do not suppress while it is detected */
|
||||
if (++es->dt_timer == es->dt_interval) {
|
||||
#ifdef DEBUG_ES
|
||||
printf("dt_level=%.3f dt_offset=%.3f*\n", level2db(es->dt_level), level2db(es->dt_offset));
|
||||
#endif
|
||||
if (level2db(es->dt_level) > DT_MIN_LEVEL_DB && level2db(es->dt_offset) < DT_MAX_OFFSET_DB) {
|
||||
if (!es->dt_disable) {
|
||||
#ifdef DEBUG_ES
|
||||
printf("Detected dial tone, disabling suppressor\n");
|
||||
#endif
|
||||
es->dt_disable = true;
|
||||
}
|
||||
} else {
|
||||
if (es->dt_disable) {
|
||||
#ifdef DEBUG_ES
|
||||
printf("dial tone ceases, enabling suppressor\n");
|
||||
#endif
|
||||
es->dt_disable = false;
|
||||
es->state = SUP_STATE_SILENCE;
|
||||
es->timer = 0;
|
||||
}
|
||||
}
|
||||
es->dt_timer = 0;
|
||||
es->dt_level = dt;
|
||||
es->dt_offset = 0.0;
|
||||
}
|
||||
offset = fabsf(es->dt_level / dt);
|
||||
if (offset > es->dt_offset)
|
||||
es->dt_offset = offset;
|
||||
if (es->dt_disable)
|
||||
return rx;
|
||||
|
||||
#ifdef DEBUG_ES
|
||||
static int debug_interval = 0;
|
||||
float send_max = 0, receive_max = 0;
|
||||
if (send > send_max)
|
||||
send_max = send;
|
||||
if (receive > receive_max)
|
||||
receive_max = receive;
|
||||
if (++debug_interval == 1000) {
|
||||
printf("Level (Ls=%.0f Lr=%.0f)\n", level2db(send_max), level2db(receive_max));
|
||||
debug_interval = 0;
|
||||
send_max = 0;
|
||||
receive_max = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (es->state) {
|
||||
case SUP_STATE_SILENCE:
|
||||
if (receive <= es->suppress_threshold && send <= es->suppress_threshold) {
|
||||
es->timer = 0;
|
||||
break;
|
||||
}
|
||||
if (receive < send) {
|
||||
if (++es->timer < es->break_in_operate)
|
||||
break;
|
||||
#ifdef DEBUG_ES
|
||||
printf("Change from silence to break-in\n");
|
||||
#endif
|
||||
es->state = SUP_STATE_BREAK_IN;
|
||||
} else {
|
||||
if (++es->timer < es->suppress_operate)
|
||||
break;
|
||||
#ifdef DEBUG_ES
|
||||
printf("Change from silence to suppression\n");
|
||||
#endif
|
||||
es->state = SUP_STATE_SUPPRESSION;
|
||||
}
|
||||
es->timer = 0;
|
||||
break;
|
||||
case SUP_STATE_SUPPRESSION:
|
||||
if (receive <= es->release_suppress && send <= es->release_suppress) {
|
||||
if (++es->timer < es->suppress_hangover)
|
||||
break;
|
||||
#ifdef DEBUG_ES
|
||||
printf("Change from suppression to silence\n");
|
||||
#endif
|
||||
es->state = SUP_STATE_SILENCE;
|
||||
es->timer = 0;
|
||||
break;
|
||||
}
|
||||
if (receive < send) {
|
||||
if (++es->timer < es->break_in_operate)
|
||||
break;
|
||||
#ifdef DEBUG_ES
|
||||
printf("Change from suppression to break-in\n");
|
||||
#endif
|
||||
es->state = SUP_STATE_BREAK_IN;
|
||||
}
|
||||
es->timer = 0;
|
||||
break;
|
||||
case SUP_STATE_BREAK_IN:
|
||||
if (receive <= es->suppress_threshold && send <= es->suppress_threshold) {
|
||||
if (++es->timer < es->break_in_hangover)
|
||||
break;
|
||||
#ifdef DEBUG_ES
|
||||
printf("Change from break-in to silence\n");
|
||||
#endif
|
||||
es->state = SUP_STATE_SILENCE;
|
||||
es->timer = 0;
|
||||
break;
|
||||
}
|
||||
/* insert loss, so that receive level must be 6 dB higher than send level */
|
||||
if (receive / es->insertion_loss > send) {
|
||||
if (++es->timer < es->break_in_hangover)
|
||||
break;
|
||||
#ifdef DEBUG_ES
|
||||
printf("Change from break-in to suppression\n");
|
||||
#endif
|
||||
es->state = SUP_STATE_SUPPRESSION;
|
||||
}
|
||||
es->timer = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (es->state == SUP_STATE_SUPPRESSION)
|
||||
return 0;
|
||||
return rx;
|
||||
}
|
||||
|
||||
void echo_sup_flush(echo_sup_state_t *es)
|
||||
{
|
||||
es->state = SUP_STATE_SILENCE;
|
||||
es->timer = 0;
|
||||
|
||||
iir_reset(&es->lp_send);
|
||||
iir_reset(&es->lp_receive);
|
||||
iir_reset(&es->hp_send);
|
||||
iir_reset(&es->hp_receive);
|
||||
iir_reset(&es->env_send);
|
||||
iir_reset(&es->env_receive);
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
typedef struct iir_filter {
|
||||
float a0, a1, a2, b1, b2;
|
||||
float z1, z2;
|
||||
} iir_filter_t;
|
||||
|
||||
enum sup_state {
|
||||
SUP_STATE_SILENCE,
|
||||
SUP_STATE_SUPPRESSION,
|
||||
SUP_STATE_BREAK_IN,
|
||||
};
|
||||
|
||||
typedef struct echo_sup_state {
|
||||
float suppress_threshold; /* linear value of suppression th */
|
||||
float release_suppress; /* linear value of release suppression th */
|
||||
float insertion_loss; /* linear value of insertion loss */
|
||||
int suppress_operate; /* number of sample until transition */
|
||||
int suppress_hangover;
|
||||
int break_in_operate;
|
||||
int break_in_hangover;
|
||||
enum sup_state state; /* current state */
|
||||
int timer; /* timer to delay transition */
|
||||
iir_filter_t lp_send, lp_receive; /* filter to cut off high frequencies */
|
||||
iir_filter_t hp_send, hp_receive; /* filter to cut off low frequencies */
|
||||
iir_filter_t env_send, env_receive; /* filter to get the envelope after rectifying */
|
||||
iir_filter_t dt_receive; /* filter to detect steady tone (dial tone) */
|
||||
int dt_timer, dt_interval; /* timer of dial tone detection interval */
|
||||
float dt_level, dt_offset; /* initial level and max offset during interval */
|
||||
bool dt_disable; /* true if suppressor is disabled, due to dial tone */
|
||||
} echo_sup_state_t;
|
||||
|
||||
echo_sup_state_t *echo_sup_create(void *ctx, int samplerate);
|
||||
void echo_sup_free(echo_sup_state_t *es);
|
||||
int16_t echo_sup_update(echo_sup_state_t *es, int16_t tx, int16_t rx);
|
||||
void echo_sup_flush(echo_sup_state_t *es);
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* SpanDSP - a series of DSP components for telephony
|
||||
*
|
||||
* fir.h - General telephony FIR routines
|
||||
*
|
||||
* Written by Steve Underwood <steveu@coppice.org>
|
||||
*
|
||||
* Copyright (C) 2002 Steve Underwood
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: fir.h,v 1.8 2006/10/24 13:45:28 steveu Exp $
|
||||
*/
|
||||
|
||||
/*! \page fir_page FIR filtering
|
||||
\section fir_page_sec_1 What does it do?
|
||||
???.
|
||||
|
||||
\section fir_page_sec_2 How does it work?
|
||||
???.
|
||||
*/
|
||||
|
||||
#if !defined(_FIR_H_)
|
||||
#define _FIR_H_
|
||||
|
||||
/*
|
||||
Blackfin NOTES & IDEAS:
|
||||
|
||||
A simple dot product function is used to implement the filter. This performs
|
||||
just one MAC/cycle which is inefficient but was easy to implement as a first
|
||||
pass. The current Blackfin code also uses an unrolled form of the filter
|
||||
history to avoid 0 length hardware loop issues. This is wasteful of
|
||||
memory.
|
||||
|
||||
Ideas for improvement:
|
||||
|
||||
1/ Rewrite filter for dual MAC inner loop. The issue here is handling
|
||||
history sample offsets that are 16 bit aligned - the dual MAC needs
|
||||
32 bit aligmnent. There are some good examples in libbfdsp.
|
||||
|
||||
2/ Use the hardware circular buffer facility tohalve memory usage.
|
||||
|
||||
3/ Consider using internal memory.
|
||||
|
||||
Using less memory might also improve speed as cache misses will be
|
||||
reduced. A drop in MIPs and memory approaching 50% should be
|
||||
possible.
|
||||
|
||||
The foreground and background filters currenlty use a total of
|
||||
about 10 MIPs/ch as measured with speedtest.c on a 256 TAP echo
|
||||
can.
|
||||
*/
|
||||
|
||||
#if defined(USE_MMX) || defined(USE_SSE2)
|
||||
#include "mmx.h"
|
||||
#endif
|
||||
|
||||
/*!
|
||||
16 bit integer FIR descriptor. This defines the working state for a single
|
||||
instance of an FIR filter using 16 bit integer coefficients.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int taps;
|
||||
int curr_pos;
|
||||
const int16_t *coeffs;
|
||||
int16_t *history;
|
||||
} fir16_state_t;
|
||||
|
||||
/*!
|
||||
32 bit integer FIR descriptor. This defines the working state for a single
|
||||
instance of an FIR filter using 32 bit integer coefficients, and filtering
|
||||
16 bit integer data.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int taps;
|
||||
int curr_pos;
|
||||
const int32_t *coeffs;
|
||||
int16_t *history;
|
||||
} fir32_state_t;
|
||||
|
||||
/*!
|
||||
Floating point FIR descriptor. This defines the working state for a single
|
||||
instance of an FIR filter using floating point coefficients and data.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int taps;
|
||||
int curr_pos;
|
||||
const float *coeffs;
|
||||
float *history;
|
||||
} fir_float_state_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static __inline__ const int16_t *fir16_create(fir16_state_t *fir,
|
||||
const int16_t *coeffs,
|
||||
int taps)
|
||||
{
|
||||
fir->taps = taps;
|
||||
fir->curr_pos = taps - 1;
|
||||
fir->coeffs = coeffs;
|
||||
#if defined(USE_MMX) || defined(USE_SSE2) || defined(__BLACKFIN_ASM__)
|
||||
if ((fir->history = malloc(2*taps*sizeof(int16_t))))
|
||||
memset(fir->history, 0, 2*taps*sizeof(int16_t));
|
||||
#else
|
||||
if ((fir->history = (int16_t *) malloc(taps*sizeof(int16_t))))
|
||||
memset(fir->history, 0, taps*sizeof(int16_t));
|
||||
#endif
|
||||
return fir->history;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static __inline__ void fir16_flush(fir16_state_t *fir)
|
||||
{
|
||||
#if defined(USE_MMX) || defined(USE_SSE2) || defined(__BLACKFIN_ASM__)
|
||||
memset(fir->history, 0, 2*fir->taps*sizeof(int16_t));
|
||||
#else
|
||||
memset(fir->history, 0, fir->taps*sizeof(int16_t));
|
||||
#endif
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static __inline__ void fir16_free(fir16_state_t *fir)
|
||||
{
|
||||
free(fir->history);
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#ifdef __BLACKFIN_ASM__
|
||||
static inline int32_t dot_asm(short *x, short *y, int len)
|
||||
{
|
||||
int dot;
|
||||
|
||||
len--;
|
||||
|
||||
__asm__
|
||||
(
|
||||
"I0 = %1;\n\t"
|
||||
"I1 = %2;\n\t"
|
||||
"A0 = 0;\n\t"
|
||||
"R0.L = W[I0++] || R1.L = W[I1++];\n\t"
|
||||
"LOOP dot%= LC0 = %3;\n\t"
|
||||
"LOOP_BEGIN dot%=;\n\t"
|
||||
"A0 += R0.L * R1.L (IS) || R0.L = W[I0++] || R1.L = W[I1++];\n\t"
|
||||
"LOOP_END dot%=;\n\t"
|
||||
"A0 += R0.L*R1.L (IS);\n\t"
|
||||
"R0 = A0;\n\t"
|
||||
"%0 = R0;\n\t"
|
||||
: "=&d" (dot)
|
||||
: "a" (x), "a" (y), "a" (len)
|
||||
: "I0", "I1", "A1", "A0", "R0", "R1"
|
||||
);
|
||||
|
||||
return dot;
|
||||
}
|
||||
#endif
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static __inline__ int16_t fir16(fir16_state_t *fir, int16_t sample)
|
||||
{
|
||||
int32_t y;
|
||||
#if defined(USE_MMX)
|
||||
int i;
|
||||
mmx_t *mmx_coeffs;
|
||||
mmx_t *mmx_hist;
|
||||
|
||||
fir->history[fir->curr_pos] = sample;
|
||||
fir->history[fir->curr_pos + fir->taps] = sample;
|
||||
|
||||
mmx_coeffs = (mmx_t *) fir->coeffs;
|
||||
mmx_hist = (mmx_t *) &fir->history[fir->curr_pos];
|
||||
i = fir->taps;
|
||||
pxor_r2r(mm4, mm4);
|
||||
/* 8 samples per iteration, so the filter must be a multiple of 8 long. */
|
||||
while (i > 0)
|
||||
{
|
||||
movq_m2r(mmx_coeffs[0], mm0);
|
||||
movq_m2r(mmx_coeffs[1], mm2);
|
||||
movq_m2r(mmx_hist[0], mm1);
|
||||
movq_m2r(mmx_hist[1], mm3);
|
||||
mmx_coeffs += 2;
|
||||
mmx_hist += 2;
|
||||
pmaddwd_r2r(mm1, mm0);
|
||||
pmaddwd_r2r(mm3, mm2);
|
||||
paddd_r2r(mm0, mm4);
|
||||
paddd_r2r(mm2, mm4);
|
||||
i -= 8;
|
||||
}
|
||||
movq_r2r(mm4, mm0);
|
||||
psrlq_i2r(32, mm0);
|
||||
paddd_r2r(mm0, mm4);
|
||||
movd_r2m(mm4, y);
|
||||
emms();
|
||||
#elif defined(USE_SSE2)
|
||||
int i;
|
||||
xmm_t *xmm_coeffs;
|
||||
xmm_t *xmm_hist;
|
||||
|
||||
fir->history[fir->curr_pos] = sample;
|
||||
fir->history[fir->curr_pos + fir->taps] = sample;
|
||||
|
||||
xmm_coeffs = (xmm_t *) fir->coeffs;
|
||||
xmm_hist = (xmm_t *) &fir->history[fir->curr_pos];
|
||||
i = fir->taps;
|
||||
pxor_r2r(xmm4, xmm4);
|
||||
/* 16 samples per iteration, so the filter must be a multiple of 16 long. */
|
||||
while (i > 0)
|
||||
{
|
||||
movdqu_m2r(xmm_coeffs[0], xmm0);
|
||||
movdqu_m2r(xmm_coeffs[1], xmm2);
|
||||
movdqu_m2r(xmm_hist[0], xmm1);
|
||||
movdqu_m2r(xmm_hist[1], xmm3);
|
||||
xmm_coeffs += 2;
|
||||
xmm_hist += 2;
|
||||
pmaddwd_r2r(xmm1, xmm0);
|
||||
pmaddwd_r2r(xmm3, xmm2);
|
||||
paddd_r2r(xmm0, xmm4);
|
||||
paddd_r2r(xmm2, xmm4);
|
||||
i -= 16;
|
||||
}
|
||||
movdqa_r2r(xmm4, xmm0);
|
||||
psrldq_i2r(8, xmm0);
|
||||
paddd_r2r(xmm0, xmm4);
|
||||
movdqa_r2r(xmm4, xmm0);
|
||||
psrldq_i2r(4, xmm0);
|
||||
paddd_r2r(xmm0, xmm4);
|
||||
movd_r2m(xmm4, y);
|
||||
#elif defined(__BLACKFIN_ASM__)
|
||||
fir->history[fir->curr_pos] = sample;
|
||||
fir->history[fir->curr_pos + fir->taps] = sample;
|
||||
y = dot_asm((int16_t*)fir->coeffs, &fir->history[fir->curr_pos], fir->taps);
|
||||
#else
|
||||
int i;
|
||||
int offset1;
|
||||
int offset2;
|
||||
|
||||
fir->history[fir->curr_pos] = sample;
|
||||
|
||||
offset2 = fir->curr_pos;
|
||||
offset1 = fir->taps - offset2;
|
||||
y = 0;
|
||||
for (i = fir->taps - 1; i >= offset1; i--)
|
||||
y += fir->coeffs[i]*fir->history[i - offset1];
|
||||
for ( ; i >= 0; i--)
|
||||
y += fir->coeffs[i]*fir->history[i + offset2];
|
||||
#endif
|
||||
if (fir->curr_pos <= 0)
|
||||
fir->curr_pos = fir->taps;
|
||||
fir->curr_pos--;
|
||||
return (int16_t) (y >> 15);
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static __inline__ const int16_t *fir32_create(fir32_state_t *fir,
|
||||
const int32_t *coeffs,
|
||||
int taps)
|
||||
{
|
||||
fir->taps = taps;
|
||||
fir->curr_pos = taps - 1;
|
||||
fir->coeffs = coeffs;
|
||||
fir->history = (int16_t *) malloc(taps*sizeof(int16_t));
|
||||
if (fir->history)
|
||||
memset(fir->history, '\0', taps*sizeof(int16_t));
|
||||
return fir->history;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static __inline__ void fir32_flush(fir32_state_t *fir)
|
||||
{
|
||||
memset(fir->history, 0, fir->taps*sizeof(int16_t));
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static __inline__ void fir32_free(fir32_state_t *fir)
|
||||
{
|
||||
free(fir->history);
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static __inline__ int16_t fir32(fir32_state_t *fir, int16_t sample)
|
||||
{
|
||||
int i;
|
||||
int32_t y;
|
||||
int offset1;
|
||||
int offset2;
|
||||
|
||||
fir->history[fir->curr_pos] = sample;
|
||||
offset2 = fir->curr_pos;
|
||||
offset1 = fir->taps - offset2;
|
||||
y = 0;
|
||||
for (i = fir->taps - 1; i >= offset1; i--)
|
||||
y += fir->coeffs[i]*fir->history[i - offset1];
|
||||
for ( ; i >= 0; i--)
|
||||
y += fir->coeffs[i]*fir->history[i + offset2];
|
||||
if (fir->curr_pos <= 0)
|
||||
fir->curr_pos = fir->taps;
|
||||
fir->curr_pos--;
|
||||
return (int16_t) (y >> 15);
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
#ifndef __KERNEL__
|
||||
static __inline__ const float *fir_float_create(fir_float_state_t *fir,
|
||||
const float *coeffs,
|
||||
int taps)
|
||||
{
|
||||
fir->taps = taps;
|
||||
fir->curr_pos = taps - 1;
|
||||
fir->coeffs = coeffs;
|
||||
fir->history = (float *) malloc(taps*sizeof(float));
|
||||
if (fir->history)
|
||||
memset(fir->history, '\0', taps*sizeof(float));
|
||||
return fir->history;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static __inline__ void fir_float_free(fir_float_state_t *fir)
|
||||
{
|
||||
free(fir->history);
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
|
||||
static __inline__ int16_t fir_float(fir_float_state_t *fir, int16_t sample)
|
||||
{
|
||||
int i;
|
||||
float y;
|
||||
int offset1;
|
||||
int offset2;
|
||||
|
||||
fir->history[fir->curr_pos] = sample;
|
||||
|
||||
offset2 = fir->curr_pos;
|
||||
offset1 = fir->taps - offset2;
|
||||
y = 0;
|
||||
for (i = fir->taps - 1; i >= offset1; i--)
|
||||
y += fir->coeffs[i]*fir->history[i - offset1];
|
||||
for ( ; i >= 0; i--)
|
||||
y += fir->coeffs[i]*fir->history[i + offset2];
|
||||
if (fir->curr_pos <= 0)
|
||||
fir->curr_pos = fir->taps;
|
||||
fir->curr_pos--;
|
||||
return (int16_t) y;
|
||||
}
|
||||
/*- End of function --------------------------------------------------------*/
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/*- End of file ------------------------------------------------------------*/
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* mmx.h
|
||||
* Copyright (C) 1997-2001 H. Dietz and R. Fisher
|
||||
*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FFmpeg is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef AVCODEC_I386MMX_H
|
||||
#define AVCODEC_I386MMX_H
|
||||
|
||||
/*
|
||||
* The type of an value that fits in an MMX register (note that long
|
||||
* long constant values MUST be suffixed by LL and unsigned long long
|
||||
* values by ULL, lest they be truncated by the compiler)
|
||||
*/
|
||||
|
||||
typedef union {
|
||||
long long q; /* Quadword (64-bit) value */
|
||||
unsigned long long uq; /* Unsigned Quadword */
|
||||
int d[2]; /* 2 Doubleword (32-bit) values */
|
||||
unsigned int ud[2]; /* 2 Unsigned Doubleword */
|
||||
short w[4]; /* 4 Word (16-bit) values */
|
||||
unsigned short uw[4]; /* 4 Unsigned Word */
|
||||
char b[8]; /* 8 Byte (8-bit) values */
|
||||
unsigned char ub[8]; /* 8 Unsigned Byte */
|
||||
float s[2]; /* Single-precision (32-bit) value */
|
||||
} mmx_t; /* On an 8-byte (64-bit) boundary */
|
||||
|
||||
/* SSE registers */
|
||||
typedef union {
|
||||
char b[16];
|
||||
} xmm_t;
|
||||
|
||||
|
||||
#define mmx_i2r(op,imm,reg) \
|
||||
__asm__ __volatile__ (#op " %0, %%" #reg \
|
||||
: /* nothing */ \
|
||||
: "i" (imm) )
|
||||
|
||||
#define mmx_m2r(op,mem,reg) \
|
||||
__asm__ __volatile__ (#op " %0, %%" #reg \
|
||||
: /* nothing */ \
|
||||
: "m" (mem))
|
||||
|
||||
#define mmx_r2m(op,reg,mem) \
|
||||
__asm__ __volatile__ (#op " %%" #reg ", %0" \
|
||||
: "=m" (mem) \
|
||||
: /* nothing */ )
|
||||
|
||||
#define mmx_r2r(op,regs,regd) \
|
||||
__asm__ __volatile__ (#op " %" #regs ", %" #regd)
|
||||
|
||||
|
||||
#define emms() __asm__ __volatile__ ("emms")
|
||||
|
||||
#define movd_m2r(var,reg) mmx_m2r (movd, var, reg)
|
||||
#define movd_r2m(reg,var) mmx_r2m (movd, reg, var)
|
||||
#define movd_r2r(regs,regd) mmx_r2r (movd, regs, regd)
|
||||
|
||||
#define movq_m2r(var,reg) mmx_m2r (movq, var, reg)
|
||||
#define movq_r2m(reg,var) mmx_r2m (movq, reg, var)
|
||||
#define movq_r2r(regs,regd) mmx_r2r (movq, regs, regd)
|
||||
|
||||
#define packssdw_m2r(var,reg) mmx_m2r (packssdw, var, reg)
|
||||
#define packssdw_r2r(regs,regd) mmx_r2r (packssdw, regs, regd)
|
||||
#define packsswb_m2r(var,reg) mmx_m2r (packsswb, var, reg)
|
||||
#define packsswb_r2r(regs,regd) mmx_r2r (packsswb, regs, regd)
|
||||
|
||||
#define packuswb_m2r(var,reg) mmx_m2r (packuswb, var, reg)
|
||||
#define packuswb_r2r(regs,regd) mmx_r2r (packuswb, regs, regd)
|
||||
|
||||
#define paddb_m2r(var,reg) mmx_m2r (paddb, var, reg)
|
||||
#define paddb_r2r(regs,regd) mmx_r2r (paddb, regs, regd)
|
||||
#define paddd_m2r(var,reg) mmx_m2r (paddd, var, reg)
|
||||
#define paddd_r2r(regs,regd) mmx_r2r (paddd, regs, regd)
|
||||
#define paddw_m2r(var,reg) mmx_m2r (paddw, var, reg)
|
||||
#define paddw_r2r(regs,regd) mmx_r2r (paddw, regs, regd)
|
||||
|
||||
#define paddsb_m2r(var,reg) mmx_m2r (paddsb, var, reg)
|
||||
#define paddsb_r2r(regs,regd) mmx_r2r (paddsb, regs, regd)
|
||||
#define paddsw_m2r(var,reg) mmx_m2r (paddsw, var, reg)
|
||||
#define paddsw_r2r(regs,regd) mmx_r2r (paddsw, regs, regd)
|
||||
|
||||
#define paddusb_m2r(var,reg) mmx_m2r (paddusb, var, reg)
|
||||
#define paddusb_r2r(regs,regd) mmx_r2r (paddusb, regs, regd)
|
||||
#define paddusw_m2r(var,reg) mmx_m2r (paddusw, var, reg)
|
||||
#define paddusw_r2r(regs,regd) mmx_r2r (paddusw, regs, regd)
|
||||
|
||||
#define pand_m2r(var,reg) mmx_m2r (pand, var, reg)
|
||||
#define pand_r2r(regs,regd) mmx_r2r (pand, regs, regd)
|
||||
|
||||
#define pandn_m2r(var,reg) mmx_m2r (pandn, var, reg)
|
||||
#define pandn_r2r(regs,regd) mmx_r2r (pandn, regs, regd)
|
||||
|
||||
#define pcmpeqb_m2r(var,reg) mmx_m2r (pcmpeqb, var, reg)
|
||||
#define pcmpeqb_r2r(regs,regd) mmx_r2r (pcmpeqb, regs, regd)
|
||||
#define pcmpeqd_m2r(var,reg) mmx_m2r (pcmpeqd, var, reg)
|
||||
#define pcmpeqd_r2r(regs,regd) mmx_r2r (pcmpeqd, regs, regd)
|
||||
#define pcmpeqw_m2r(var,reg) mmx_m2r (pcmpeqw, var, reg)
|
||||
#define pcmpeqw_r2r(regs,regd) mmx_r2r (pcmpeqw, regs, regd)
|
||||
|
||||
#define pcmpgtb_m2r(var,reg) mmx_m2r (pcmpgtb, var, reg)
|
||||
#define pcmpgtb_r2r(regs,regd) mmx_r2r (pcmpgtb, regs, regd)
|
||||
#define pcmpgtd_m2r(var,reg) mmx_m2r (pcmpgtd, var, reg)
|
||||
#define pcmpgtd_r2r(regs,regd) mmx_r2r (pcmpgtd, regs, regd)
|
||||
#define pcmpgtw_m2r(var,reg) mmx_m2r (pcmpgtw, var, reg)
|
||||
#define pcmpgtw_r2r(regs,regd) mmx_r2r (pcmpgtw, regs, regd)
|
||||
|
||||
#define pmaddwd_m2r(var,reg) mmx_m2r (pmaddwd, var, reg)
|
||||
#define pmaddwd_r2r(regs,regd) mmx_r2r (pmaddwd, regs, regd)
|
||||
|
||||
#define pmulhw_m2r(var,reg) mmx_m2r (pmulhw, var, reg)
|
||||
#define pmulhw_r2r(regs,regd) mmx_r2r (pmulhw, regs, regd)
|
||||
|
||||
#define pmullw_m2r(var,reg) mmx_m2r (pmullw, var, reg)
|
||||
#define pmullw_r2r(regs,regd) mmx_r2r (pmullw, regs, regd)
|
||||
|
||||
#define por_m2r(var,reg) mmx_m2r (por, var, reg)
|
||||
#define por_r2r(regs,regd) mmx_r2r (por, regs, regd)
|
||||
|
||||
#define pslld_i2r(imm,reg) mmx_i2r (pslld, imm, reg)
|
||||
#define pslld_m2r(var,reg) mmx_m2r (pslld, var, reg)
|
||||
#define pslld_r2r(regs,regd) mmx_r2r (pslld, regs, regd)
|
||||
#define psllq_i2r(imm,reg) mmx_i2r (psllq, imm, reg)
|
||||
#define psllq_m2r(var,reg) mmx_m2r (psllq, var, reg)
|
||||
#define psllq_r2r(regs,regd) mmx_r2r (psllq, regs, regd)
|
||||
#define psllw_i2r(imm,reg) mmx_i2r (psllw, imm, reg)
|
||||
#define psllw_m2r(var,reg) mmx_m2r (psllw, var, reg)
|
||||
#define psllw_r2r(regs,regd) mmx_r2r (psllw, regs, regd)
|
||||
|
||||
#define psrad_i2r(imm,reg) mmx_i2r (psrad, imm, reg)
|
||||
#define psrad_m2r(var,reg) mmx_m2r (psrad, var, reg)
|
||||
#define psrad_r2r(regs,regd) mmx_r2r (psrad, regs, regd)
|
||||
#define psraw_i2r(imm,reg) mmx_i2r (psraw, imm, reg)
|
||||
#define psraw_m2r(var,reg) mmx_m2r (psraw, var, reg)
|
||||
#define psraw_r2r(regs,regd) mmx_r2r (psraw, regs, regd)
|
||||
|
||||
#define psrld_i2r(imm,reg) mmx_i2r (psrld, imm, reg)
|
||||
#define psrld_m2r(var,reg) mmx_m2r (psrld, var, reg)
|
||||
#define psrld_r2r(regs,regd) mmx_r2r (psrld, regs, regd)
|
||||
#define psrlq_i2r(imm,reg) mmx_i2r (psrlq, imm, reg)
|
||||
#define psrlq_m2r(var,reg) mmx_m2r (psrlq, var, reg)
|
||||
#define psrlq_r2r(regs,regd) mmx_r2r (psrlq, regs, regd)
|
||||
#define psrlw_i2r(imm,reg) mmx_i2r (psrlw, imm, reg)
|
||||
#define psrlw_m2r(var,reg) mmx_m2r (psrlw, var, reg)
|
||||
#define psrlw_r2r(regs,regd) mmx_r2r (psrlw, regs, regd)
|
||||
|
||||
#define psubb_m2r(var,reg) mmx_m2r (psubb, var, reg)
|
||||
#define psubb_r2r(regs,regd) mmx_r2r (psubb, regs, regd)
|
||||
#define psubd_m2r(var,reg) mmx_m2r (psubd, var, reg)
|
||||
#define psubd_r2r(regs,regd) mmx_r2r (psubd, regs, regd)
|
||||
#define psubw_m2r(var,reg) mmx_m2r (psubw, var, reg)
|
||||
#define psubw_r2r(regs,regd) mmx_r2r (psubw, regs, regd)
|
||||
|
||||
#define psubsb_m2r(var,reg) mmx_m2r (psubsb, var, reg)
|
||||
#define psubsb_r2r(regs,regd) mmx_r2r (psubsb, regs, regd)
|
||||
#define psubsw_m2r(var,reg) mmx_m2r (psubsw, var, reg)
|
||||
#define psubsw_r2r(regs,regd) mmx_r2r (psubsw, regs, regd)
|
||||
|
||||
#define psubusb_m2r(var,reg) mmx_m2r (psubusb, var, reg)
|
||||
#define psubusb_r2r(regs,regd) mmx_r2r (psubusb, regs, regd)
|
||||
#define psubusw_m2r(var,reg) mmx_m2r (psubusw, var, reg)
|
||||
#define psubusw_r2r(regs,regd) mmx_r2r (psubusw, regs, regd)
|
||||
|
||||
#define punpckhbw_m2r(var,reg) mmx_m2r (punpckhbw, var, reg)
|
||||
#define punpckhbw_r2r(regs,regd) mmx_r2r (punpckhbw, regs, regd)
|
||||
#define punpckhdq_m2r(var,reg) mmx_m2r (punpckhdq, var, reg)
|
||||
#define punpckhdq_r2r(regs,regd) mmx_r2r (punpckhdq, regs, regd)
|
||||
#define punpckhwd_m2r(var,reg) mmx_m2r (punpckhwd, var, reg)
|
||||
#define punpckhwd_r2r(regs,regd) mmx_r2r (punpckhwd, regs, regd)
|
||||
|
||||
#define punpcklbw_m2r(var,reg) mmx_m2r (punpcklbw, var, reg)
|
||||
#define punpcklbw_r2r(regs,regd) mmx_r2r (punpcklbw, regs, regd)
|
||||
#define punpckldq_m2r(var,reg) mmx_m2r (punpckldq, var, reg)
|
||||
#define punpckldq_r2r(regs,regd) mmx_r2r (punpckldq, regs, regd)
|
||||
#define punpcklwd_m2r(var,reg) mmx_m2r (punpcklwd, var, reg)
|
||||
#define punpcklwd_r2r(regs,regd) mmx_r2r (punpcklwd, regs, regd)
|
||||
|
||||
#define pxor_m2r(var,reg) mmx_m2r (pxor, var, reg)
|
||||
#define pxor_r2r(regs,regd) mmx_r2r (pxor, regs, regd)
|
||||
|
||||
|
||||
/* 3DNOW extensions */
|
||||
|
||||
#define pavgusb_m2r(var,reg) mmx_m2r (pavgusb, var, reg)
|
||||
#define pavgusb_r2r(regs,regd) mmx_r2r (pavgusb, regs, regd)
|
||||
|
||||
|
||||
/* AMD MMX extensions - also available in intel SSE */
|
||||
|
||||
|
||||
#define mmx_m2ri(op,mem,reg,imm) \
|
||||
__asm__ __volatile__ (#op " %1, %0, %%" #reg \
|
||||
: /* nothing */ \
|
||||
: "m" (mem), "i" (imm))
|
||||
#define mmx_r2ri(op,regs,regd,imm) \
|
||||
__asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \
|
||||
: /* nothing */ \
|
||||
: "i" (imm) )
|
||||
|
||||
#define mmx_fetch(mem,hint) \
|
||||
__asm__ __volatile__ ("prefetch" #hint " %0" \
|
||||
: /* nothing */ \
|
||||
: "m" (mem))
|
||||
|
||||
|
||||
#define maskmovq(regs,maskreg) mmx_r2ri (maskmovq, regs, maskreg)
|
||||
|
||||
#define movntq_r2m(mmreg,var) mmx_r2m (movntq, mmreg, var)
|
||||
|
||||
#define pavgb_m2r(var,reg) mmx_m2r (pavgb, var, reg)
|
||||
#define pavgb_r2r(regs,regd) mmx_r2r (pavgb, regs, regd)
|
||||
#define pavgw_m2r(var,reg) mmx_m2r (pavgw, var, reg)
|
||||
#define pavgw_r2r(regs,regd) mmx_r2r (pavgw, regs, regd)
|
||||
|
||||
#define pextrw_r2r(mmreg,reg,imm) mmx_r2ri (pextrw, mmreg, reg, imm)
|
||||
|
||||
#define pinsrw_r2r(reg,mmreg,imm) mmx_r2ri (pinsrw, reg, mmreg, imm)
|
||||
|
||||
#define pmaxsw_m2r(var,reg) mmx_m2r (pmaxsw, var, reg)
|
||||
#define pmaxsw_r2r(regs,regd) mmx_r2r (pmaxsw, regs, regd)
|
||||
|
||||
#define pmaxub_m2r(var,reg) mmx_m2r (pmaxub, var, reg)
|
||||
#define pmaxub_r2r(regs,regd) mmx_r2r (pmaxub, regs, regd)
|
||||
|
||||
#define pminsw_m2r(var,reg) mmx_m2r (pminsw, var, reg)
|
||||
#define pminsw_r2r(regs,regd) mmx_r2r (pminsw, regs, regd)
|
||||
|
||||
#define pminub_m2r(var,reg) mmx_m2r (pminub, var, reg)
|
||||
#define pminub_r2r(regs,regd) mmx_r2r (pminub, regs, regd)
|
||||
|
||||
#define pmovmskb(mmreg,reg) \
|
||||
__asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg)
|
||||
|
||||
#define pmulhuw_m2r(var,reg) mmx_m2r (pmulhuw, var, reg)
|
||||
#define pmulhuw_r2r(regs,regd) mmx_r2r (pmulhuw, regs, regd)
|
||||
|
||||
#define prefetcht0(mem) mmx_fetch (mem, t0)
|
||||
#define prefetcht1(mem) mmx_fetch (mem, t1)
|
||||
#define prefetcht2(mem) mmx_fetch (mem, t2)
|
||||
#define prefetchnta(mem) mmx_fetch (mem, nta)
|
||||
|
||||
#define psadbw_m2r(var,reg) mmx_m2r (psadbw, var, reg)
|
||||
#define psadbw_r2r(regs,regd) mmx_r2r (psadbw, regs, regd)
|
||||
|
||||
#define pshufw_m2r(var,reg,imm) mmx_m2ri(pshufw, var, reg, imm)
|
||||
#define pshufw_r2r(regs,regd,imm) mmx_r2ri(pshufw, regs, regd, imm)
|
||||
|
||||
#define sfence() __asm__ __volatile__ ("sfence\n\t")
|
||||
|
||||
/* SSE2 */
|
||||
#define pshufhw_m2r(var,reg,imm) mmx_m2ri(pshufhw, var, reg, imm)
|
||||
#define pshufhw_r2r(regs,regd,imm) mmx_r2ri(pshufhw, regs, regd, imm)
|
||||
#define pshuflw_m2r(var,reg,imm) mmx_m2ri(pshuflw, var, reg, imm)
|
||||
#define pshuflw_r2r(regs,regd,imm) mmx_r2ri(pshuflw, regs, regd, imm)
|
||||
|
||||
#define pshufd_r2r(regs,regd,imm) mmx_r2ri(pshufd, regs, regd, imm)
|
||||
|
||||
#define movdqa_m2r(var,reg) mmx_m2r (movdqa, var, reg)
|
||||
#define movdqa_r2m(reg,var) mmx_r2m (movdqa, reg, var)
|
||||
#define movdqa_r2r(regs,regd) mmx_r2r (movdqa, regs, regd)
|
||||
#define movdqu_m2r(var,reg) mmx_m2r (movdqu, var, reg)
|
||||
#define movdqu_r2m(reg,var) mmx_r2m (movdqu, reg, var)
|
||||
#define movdqu_r2r(regs,regd) mmx_r2r (movdqu, regs, regd)
|
||||
|
||||
#define pmullw_r2m(reg,var) mmx_r2m (pmullw, reg, var)
|
||||
|
||||
#define pslldq_i2r(imm,reg) mmx_i2r (pslldq, imm, reg)
|
||||
#define psrldq_i2r(imm,reg) mmx_i2r (psrldq, imm, reg)
|
||||
|
||||
#define punpcklqdq_r2r(regs,regd) mmx_r2r (punpcklqdq, regs, regd)
|
||||
#define punpckhqdq_r2r(regs,regd) mmx_r2r (punpckhqdq, regs, regd)
|
||||
|
||||
|
||||
#endif /* AVCODEC_I386MMX_H */
|
|
@ -0,0 +1,8 @@
|
|||
AM_CPPFLAGS = $(all_includes)
|
||||
AM_CFLAGS= -Wall -Wextra -g
|
||||
|
||||
noinst_LIBRARIES = libg711.a
|
||||
|
||||
libg711_a_SOURCES = \
|
||||
g711.c
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
/*****************************************************************************\
|
||||
** **
|
||||
** PBX4Linux **
|
||||
** **
|
||||
**---------------------------------------------------------------------------**
|
||||
** Copyright: Andreas Eversberg (GPL) **
|
||||
** **
|
||||
** audio conversions for alaw and ulaw **
|
||||
** **
|
||||
\*****************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "g711.h"
|
||||
|
||||
/* ulaw -> signed 16-bit */
|
||||
static int16_t _ulaw_flipped_to_linear[256] =
|
||||
{
|
||||
0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
|
||||
0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
|
||||
0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
|
||||
0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
|
||||
0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
|
||||
0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
|
||||
0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
|
||||
0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
|
||||
0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
|
||||
0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
|
||||
0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
|
||||
0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
|
||||
0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
|
||||
0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
|
||||
0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
|
||||
0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0xffff,
|
||||
0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
|
||||
0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
|
||||
0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
|
||||
0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
|
||||
0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
|
||||
0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
|
||||
0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
|
||||
0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
|
||||
0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
|
||||
0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
|
||||
0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
|
||||
0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
|
||||
0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
|
||||
0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
|
||||
0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
|
||||
0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
|
||||
};
|
||||
|
||||
/* alaw -> signed 16-bit */
|
||||
static int16_t _alaw_flipped_to_linear[256] =
|
||||
{
|
||||
0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
|
||||
0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
|
||||
0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
|
||||
0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
|
||||
0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
|
||||
0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
|
||||
0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
|
||||
0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
|
||||
0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
|
||||
0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
|
||||
0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
|
||||
0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
|
||||
0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
|
||||
0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
|
||||
0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
|
||||
0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
|
||||
0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
|
||||
0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
|
||||
0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
|
||||
0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
|
||||
0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
|
||||
0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
|
||||
0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
|
||||
0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
|
||||
0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
|
||||
0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
|
||||
0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
|
||||
0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
|
||||
0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
|
||||
0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
|
||||
0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
|
||||
0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
|
||||
};
|
||||
|
||||
/* Xlaw -> signed 16-bit */
|
||||
static int16_t _alaw_to_linear[256];
|
||||
static int16_t _ulaw_to_linear[256];
|
||||
|
||||
/* signed 16-bit -> Xlaw */
|
||||
static uint8_t _linear_to_alaw_flipped[65536];
|
||||
static uint8_t _linear_to_ulaw_flipped[65536];
|
||||
static uint8_t _linear_to_alaw[65536];
|
||||
static uint8_t _linear_to_ulaw[65536];
|
||||
|
||||
/* table is used to generate linear_to_alaw */
|
||||
static int16_t _alaw_relations[] =
|
||||
{
|
||||
0x8684, 0x55, 0x8a84, 0xd5, 0x8e84, 0x15, 0x9284, 0x95,
|
||||
0x9684, 0x75, 0x9a84, 0xf5, 0x9e84, 0x35, 0xa284, 0xb5,
|
||||
0xa684, 0x45, 0xaa84, 0xc5, 0xae84, 0x05, 0xb284, 0x85,
|
||||
0xb684, 0x65, 0xba84, 0xe5, 0xbe84, 0x25, 0xc184, 0xa5,
|
||||
0xc384, 0x5d, 0xc584, 0xdd, 0xc784, 0x1d, 0xc984, 0x9d,
|
||||
0xcb84, 0x7d, 0xcd84, 0xfd, 0xcf84, 0x3d, 0xd184, 0xbd,
|
||||
0xd384, 0x4d, 0xd584, 0xcd, 0xd784, 0x0d, 0xd984, 0x8d,
|
||||
0xdb84, 0x6d, 0xdd84, 0xed, 0xdf84, 0x2d, 0xe104, 0xad,
|
||||
0xe204, 0x51, 0xe304, 0xd1, 0xe404, 0x11, 0xe504, 0x91,
|
||||
0xe604, 0x71, 0xe704, 0xf1, 0xe804, 0x31, 0xe904, 0xb1,
|
||||
0xea04, 0x41, 0xeb04, 0xc1, 0xec04, 0x01, 0xed04, 0x81,
|
||||
0xee04, 0x61, 0xef04, 0xe1, 0xf004, 0x21, 0xf0c4, 0x59,
|
||||
0xf0c4, 0xa1, 0xf144, 0xd9, 0xf1c4, 0x19, 0xf244, 0x99,
|
||||
0xf2c4, 0x79, 0xf344, 0xf9, 0xf3c4, 0x39, 0xf444, 0xb9,
|
||||
0xf4c4, 0x49, 0xf544, 0xc9, 0xf5c4, 0x09, 0xf644, 0x89,
|
||||
0xf6c4, 0x69, 0xf744, 0xe9, 0xf7c4, 0x29, 0xf844, 0x57,
|
||||
0xf844, 0xa9, 0xf8a4, 0xd7, 0xf8e4, 0x17, 0xf924, 0x97,
|
||||
0xf964, 0x77, 0xf9a4, 0xf7, 0xf9e4, 0x37, 0xfa24, 0xb7,
|
||||
0xfa64, 0x47, 0xfaa4, 0xc7, 0xfae4, 0x07, 0xfb24, 0x87,
|
||||
0xfb64, 0x67, 0xfba4, 0xe7, 0xfbe4, 0x27, 0xfc24, 0x5f,
|
||||
0xfc24, 0xa7, 0xfc64, 0x1f, 0xfc64, 0xdf, 0xfc94, 0x9f,
|
||||
0xfcb4, 0x7f, 0xfcd4, 0xff, 0xfcf4, 0x3f, 0xfd14, 0xbf,
|
||||
0xfd34, 0x4f, 0xfd54, 0xcf, 0xfd74, 0x0f, 0xfd94, 0x8f,
|
||||
0xfdb4, 0x6f, 0xfdd4, 0xef, 0xfdf4, 0x2f, 0xfe14, 0x53,
|
||||
0xfe14, 0xaf, 0xfe34, 0x13, 0xfe34, 0xd3, 0xfe54, 0x73,
|
||||
0xfe54, 0x93, 0xfe74, 0x33, 0xfe74, 0xf3, 0xfe8c, 0xb3,
|
||||
0xfe9c, 0x43, 0xfeac, 0xc3, 0xfebc, 0x03, 0xfecc, 0x83,
|
||||
0xfedc, 0x63, 0xfeec, 0xe3, 0xfefc, 0x23, 0xff0c, 0xa3,
|
||||
0xff1c, 0x5b, 0xff2c, 0xdb, 0xff3c, 0x1b, 0xff4c, 0x9b,
|
||||
0xff5c, 0x7b, 0xff6c, 0xfb, 0xff7c, 0x3b, 0xff88, 0xbb,
|
||||
0xff98, 0x4b, 0xffa8, 0xcb, 0xffb8, 0x0b, 0xffc8, 0x8b,
|
||||
0xffd8, 0x6b, 0xffe8, 0xeb, 0xfff8, 0x2b, 0xfff8, 0xab,
|
||||
0x0008, 0x2a, 0x0008, 0xaa, 0x0018, 0xea, 0x0028, 0x6a,
|
||||
0x0038, 0x8a, 0x0048, 0x0a, 0x0058, 0xca, 0x0068, 0x4a,
|
||||
0x0078, 0xba, 0x0084, 0x3a, 0x0094, 0xfa, 0x00a4, 0x7a,
|
||||
0x00b4, 0x9a, 0x00c4, 0x1a, 0x00d4, 0xda, 0x00e4, 0x5a,
|
||||
0x00f4, 0xa2, 0x0104, 0x22, 0x0114, 0xe2, 0x0124, 0x62,
|
||||
0x0134, 0x82, 0x0144, 0x02, 0x0154, 0xc2, 0x0164, 0x42,
|
||||
0x0174, 0xb2, 0x018c, 0x32, 0x018c, 0xf2, 0x01ac, 0x72,
|
||||
0x01ac, 0x92, 0x01cc, 0x12, 0x01cc, 0xd2, 0x01ec, 0x52,
|
||||
0x01ec, 0xae, 0x020c, 0x2e, 0x022c, 0xee, 0x024c, 0x6e,
|
||||
0x026c, 0x8e, 0x028c, 0x0e, 0x02ac, 0xce, 0x02cc, 0x4e,
|
||||
0x02ec, 0xbe, 0x030c, 0x3e, 0x032c, 0xfe, 0x034c, 0x7e,
|
||||
0x036c, 0x9e, 0x039c, 0x1e, 0x039c, 0xde, 0x03dc, 0x5e,
|
||||
0x03dc, 0xa6, 0x041c, 0x26, 0x045c, 0xe6, 0x049c, 0x66,
|
||||
0x04dc, 0x86, 0x051c, 0x06, 0x055c, 0xc6, 0x059c, 0x46,
|
||||
0x05dc, 0xb6, 0x061c, 0x36, 0x065c, 0xf6, 0x069c, 0x76,
|
||||
0x06dc, 0x96, 0x071c, 0x16, 0x075c, 0xd6, 0x07bc, 0x56,
|
||||
0x07bc, 0xa8, 0x083c, 0x28, 0x08bc, 0xe8, 0x093c, 0x68,
|
||||
0x09bc, 0x88, 0x0a3c, 0x08, 0x0abc, 0xc8, 0x0b3c, 0x48,
|
||||
0x0bbc, 0xb8, 0x0c3c, 0x38, 0x0cbc, 0xf8, 0x0d3c, 0x78,
|
||||
0x0dbc, 0x98, 0x0e3c, 0x18, 0x0ebc, 0xd8, 0x0f3c, 0x58,
|
||||
0x0f3c, 0xa0, 0x0ffc, 0x20, 0x10fc, 0xe0, 0x11fc, 0x60,
|
||||
0x12fc, 0x80, 0x13fc, 0x00, 0x14fc, 0xc0, 0x15fc, 0x40,
|
||||
0x16fc, 0xb0, 0x17fc, 0x30, 0x18fc, 0xf0, 0x19fc, 0x70,
|
||||
0x1afc, 0x90, 0x1bfc, 0x10, 0x1cfc, 0xd0, 0x1dfc, 0x50,
|
||||
0x1efc, 0xac, 0x207c, 0x2c, 0x227c, 0xec, 0x247c, 0x6c,
|
||||
0x267c, 0x8c, 0x287c, 0x0c, 0x2a7c, 0xcc, 0x2c7c, 0x4c,
|
||||
0x2e7c, 0xbc, 0x307c, 0x3c, 0x327c, 0xfc, 0x347c, 0x7c,
|
||||
0x367c, 0x9c, 0x387c, 0x1c, 0x3a7c, 0xdc, 0x3c7c, 0x5c,
|
||||
0x3e7c, 0xa4, 0x417c, 0x24, 0x457c, 0xe4, 0x497c, 0x64,
|
||||
0x4d7c, 0x84, 0x517c, 0x04, 0x557c, 0xc4, 0x597c, 0x44,
|
||||
0x5d7c, 0xb4, 0x617c, 0x34, 0x657c, 0xf4, 0x697c, 0x74,
|
||||
0x6d7c, 0x94, 0x717c, 0x14, 0x757c, 0xd4, 0x797c, 0x54
|
||||
};
|
||||
|
||||
uint8_t _flip[256];
|
||||
|
||||
/* Xlaw -> signed 16-bit */
|
||||
int16_t *g711_ulaw_flipped_to_linear = _ulaw_flipped_to_linear;
|
||||
int16_t *g711_alaw_flipped_to_linear = _alaw_flipped_to_linear;
|
||||
int16_t *g711_alaw_to_linear = _alaw_to_linear;
|
||||
int16_t *g711_ulaw_to_linear = _ulaw_to_linear;
|
||||
|
||||
/* signed 16-bit -> Xlaw */
|
||||
uint8_t *g711_linear_to_alaw_flipped = _linear_to_alaw_flipped;
|
||||
uint8_t *g711_linear_to_ulaw_flipped = _linear_to_ulaw_flipped;
|
||||
uint8_t *g711_linear_to_alaw = _linear_to_ulaw;
|
||||
uint8_t *g711_linear_to_ulaw = _linear_to_ulaw;
|
||||
|
||||
|
||||
/* generate tables
|
||||
*/
|
||||
void g711_init(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* flip tables */
|
||||
for (i = 0; i < 256; i++) {
|
||||
_flip[i]
|
||||
= ((i & 1) << 7)
|
||||
+ ((i & 2) << 5)
|
||||
+ ((i & 4) << 3)
|
||||
+ ((i & 8) << 1)
|
||||
+ ((i & 16) >> 1)
|
||||
+ ((i & 32) >> 3)
|
||||
+ ((i & 64) >> 5)
|
||||
+ ((i & 128) >> 7);
|
||||
_alaw_to_linear[i] = _alaw_flipped_to_linear[_flip[i]];
|
||||
_ulaw_to_linear[i] = _ulaw_flipped_to_linear[_flip[i]];
|
||||
}
|
||||
|
||||
/* linear to alaw tables */
|
||||
i = j = 0;
|
||||
while(i < 65536) {
|
||||
if (i - 32768 > _alaw_relations[j << 1])
|
||||
j++;
|
||||
if (j > 255)
|
||||
j = 255;
|
||||
_linear_to_alaw_flipped[(i - 32768) & 0xffff] = _alaw_relations[(j << 1) | 1];
|
||||
_linear_to_alaw[(i - 32768) & 0xffff] = _flip[_alaw_relations[(j << 1) | 1]];
|
||||
i++;
|
||||
}
|
||||
|
||||
/* linear to ulaw tables */
|
||||
i = j = 0;
|
||||
while(i < 32768) {
|
||||
if (i - 32768 > _ulaw_flipped_to_linear[j])
|
||||
j++;
|
||||
_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = j;
|
||||
_linear_to_ulaw[(i - 32768) & 0xffff] = _flip[j];
|
||||
i++;
|
||||
}
|
||||
j = 255;
|
||||
while(i < 65536) {
|
||||
if (i - 32768 > _alaw_flipped_to_linear[j])
|
||||
j--;
|
||||
_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = j;
|
||||
_linear_to_ulaw[(i - 32768) & 0xffff] = _flip[j];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
void g711_init(void);
|
||||
|
||||
/* Xlaw -> signed 16-bit */
|
||||
extern int16_t *g711_ulaw_flipped_to_linear;
|
||||
extern int16_t *g711_alaw_flipped_to_linear;
|
||||
extern int16_t *g711_alaw_to_linear;
|
||||
extern int16_t *g711_ulaw_to_linear;
|
||||
|
||||
/* signed 16-bit -> Xlaw */
|
||||
extern uint8_t *g711_linear_to_alaw_flipped;
|
||||
extern uint8_t *g711_linear_to_ulaw_flipped;
|
||||
extern uint8_t *g711_linear_to_alaw;
|
||||
extern uint8_t *g711_linear_to_ulaw;
|
|
@ -0,0 +1,77 @@
|
|||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include "logging.h"
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
[DV5] = {
|
||||
.name = "DV5",
|
||||
.description = "V5 protocol",
|
||||
.color = "\033[0;31m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DV5L1] = {
|
||||
.name = "DV5L1",
|
||||
.description = "V5 L1 link FSM",
|
||||
.color = "\033[0;36m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DV5CTRL] = {
|
||||
.name = "DV5CTRL",
|
||||
.description = "V5 control protocol",
|
||||
.color = "\033[1;36m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DV5PORT] = {
|
||||
.name = "DV5PORT",
|
||||
.description = "V5 port control protocol",
|
||||
.color = "\033[1;33m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DV5PSTN] = {
|
||||
.name = "DV5PSTN",
|
||||
.description = "V5 PSTN protocol",
|
||||
.color = "\033[0;37m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DV5LCP] = {
|
||||
.name = "DV5LCP",
|
||||
.description = "V5 LCP protocol",
|
||||
.color = "\033[0;33m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DV5BCC] = {
|
||||
.name = "DV5BCC",
|
||||
.description = "V5 BCC protocol",
|
||||
.color = "\033[1;32m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DV5PP] = {
|
||||
.name = "DV5PP",
|
||||
.description = "V5 Protection protocol",
|
||||
.color = "\033[0;35m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DV5MGMT] = {
|
||||
.name = "DV5MGMT",
|
||||
.description = "V5 management",
|
||||
.color = "\033[1;37m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DV5EF] = {
|
||||
.name = "DV5EF",
|
||||
.description = "V5 envelop frame",
|
||||
.color = "\033[1;31m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DPH] = {
|
||||
.name = "DPH",
|
||||
.description = "PH-socket",
|
||||
.color = "\033[0;33m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
.cat = log_categories,
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
enum {
|
||||
DV5,
|
||||
DV5L1,
|
||||
DV5CTRL,
|
||||
DV5PORT,
|
||||
DV5PSTN,
|
||||
DV5LCP,
|
||||
DV5BCC,
|
||||
DV5PP,
|
||||
DV5MGMT,
|
||||
DV5EF,
|
||||
DPH,
|
||||
};
|
||||
|
||||
extern const struct log_info log_info;
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* (C) 2022 by Harald Welte <laforge@osmocom.org>
|
||||
* (C) 2021 by Andreas Eversberg <andreas@eversberg.eu>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -26,7 +27,6 @@
|
|||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
@ -34,7 +34,6 @@
|
|||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/ctrl/control_vty.h>
|
||||
|
@ -49,90 +48,52 @@
|
|||
|
||||
#include <osmocom/abis/abis.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "layer1.h"
|
||||
#include "v5x_l1_fsm.h"
|
||||
#include "lapv5.h"
|
||||
#include "v5x_le_ctrl_fsm.h"
|
||||
#include "v5x_le_port_fsm.h"
|
||||
#include "v5x_le_pstn_fsm.h"
|
||||
#include "v52_le_lcp_fsm.h"
|
||||
#include "v52_le_bcc_fsm.h"
|
||||
#include "v52_le_pp_fsm.h"
|
||||
#include "v5x_le_management.h"
|
||||
#include "v5le_vty.h"
|
||||
#include "logging.h"
|
||||
#include "libg711/g711.h"
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
||||
/* can be changed once libosmocore 1.4.0 is released */
|
||||
#ifndef OSMO_CTRL_PORT_MGW
|
||||
#define OSMO_CTRL_PORT_MGW 4267
|
||||
#endif
|
||||
|
||||
void *tall_v5le_ctx = NULL;
|
||||
struct v5x_instance *v5i = NULL;
|
||||
|
||||
struct v5le_config {
|
||||
};
|
||||
|
||||
struct v5le_config *v5le_config_alloc(void *ctx)
|
||||
{
|
||||
struct v5le_config *cfg = talloc_zero(ctx, struct v5le_config);
|
||||
OSMO_ASSERT(cfg);
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
static int v5le_rx_sign(struct msgb *msg)
|
||||
{
|
||||
LOGP(DLMI, LOGL_NOTICE, "Rx: %s\n", msgb_hexdump(msg));
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct e1inp_line_ops v5le_e1_line_ops = {
|
||||
.sign_link_up = NULL,
|
||||
.sign_link_down = NULL,
|
||||
.sign_link = v5le_rx_sign,
|
||||
};
|
||||
|
||||
static void hdlc_rx_cb(struct e1inp_ts *ts, struct msgb *msg)
|
||||
{
|
||||
LOGP(DLINP, LOGL_NOTICE, "HDLC Rx: %s\n", msgb_hexdump(msg));
|
||||
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static int e1_init(void)
|
||||
{
|
||||
int line_nr = 1;
|
||||
struct e1inp_line *e1_line;
|
||||
int rc;
|
||||
|
||||
e1_line = e1inp_line_find(0);
|
||||
OSMO_ASSERT(e1_line);
|
||||
|
||||
e1inp_line_bind_ops(e1_line, &v5le_e1_line_ops);
|
||||
|
||||
struct e1inp_ts *sign_ts = &e1_line->ts[16-1];
|
||||
//e1inp_ts_config_sign(sign_ts, e1_line);
|
||||
//e1inp_sign_link_create(sign_ts, E1INP_SIGN_NONE, NULL, 115/*TEI*/, 0/*SAPI*/);
|
||||
e1inp_ts_config_hdlc(sign_ts, e1_line, hdlc_rx_cb);
|
||||
|
||||
return e1inp_line_update(e1_line);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static struct v5le_config *g_cfg;
|
||||
static int daemonize = 0;
|
||||
const char *gsmtap_ip = NULL;
|
||||
int test_sa7 = 0;
|
||||
int ulaw = 0;
|
||||
|
||||
const char *v5le_copyright =
|
||||
"Copyright (C) 2022 by Harald Welte\r\n"
|
||||
"Copyright (C) 2022 by Harald Welte & Andreas Eversberg\r\n"
|
||||
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
|
||||
"This is free software: you are free to change and redistribute it.\r\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\r\n";
|
||||
|
||||
static char *config_file = "osmo-v5le.cfg";
|
||||
static char *config_file = "osmo-v5-le.cfg";
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
printf("Some useful options:\n");
|
||||
printf(" -h --help is printing this text.\n");
|
||||
printf(" -c --config-file filename The config file to use.\n");
|
||||
printf(" -s --disable-color\n");
|
||||
printf(" -D --daemonize Fork the process into a background daemon\n");
|
||||
printf(" -V --version Print the version number\n");
|
||||
printf(" -h --help is printing this text.\n");
|
||||
printf(" -c --config-file filename The config file to use.\n");
|
||||
printf(" -s --disable-color Disable colors on debug output.\n");
|
||||
printf(" -D --daemonize Fork the process into a background daemon\n");
|
||||
printf(" -V --version Print the version number\n");
|
||||
printf(" -T --gsmtap IP Send GSMTAP messages to ip. (e.g. 224.0.0.1)\n");
|
||||
printf(" -7 --rest-sa7 Continously toggle Sa7 when received. (Do loop interface.)\n");
|
||||
printf(" --ulaw Use u-Law instead of a-Law for audio processing\n");
|
||||
|
||||
printf("\nVTY reference generation:\n");
|
||||
printf(" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n");
|
||||
|
@ -158,6 +119,9 @@ static void handle_long_options(const char *prog_name, const int long_option)
|
|||
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
|
||||
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
|
||||
exit(0);
|
||||
case 3:
|
||||
ulaw = 1;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
|
||||
exit(2);
|
||||
|
@ -177,10 +141,13 @@ static void handle_options(int argc, char **argv)
|
|||
{"disable-color", 0, 0, 's'},
|
||||
{"vty-ref-mode", 1, &long_option, 1},
|
||||
{"vty-ref-xml", 0, &long_option, 2},
|
||||
{"gsmtap", 1, 0, 'T'},
|
||||
{"test-sa7", 0, 0, '7'},
|
||||
{"ulaw", 0, &long_option, 3},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hc:sVD", long_options, &option_index);
|
||||
c = getopt_long(argc, argv, "hc:sVDT:7", long_options, &option_index);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
|
@ -206,6 +173,12 @@ static void handle_options(int argc, char **argv)
|
|||
case 'D':
|
||||
daemonize = 1;
|
||||
break;
|
||||
case 'T':
|
||||
gsmtap_ip = strdup(optarg);
|
||||
break;
|
||||
case '7':
|
||||
test_sa7 = 1;
|
||||
break;
|
||||
default:
|
||||
/* ignore */
|
||||
break;
|
||||
|
@ -217,50 +190,22 @@ static void handle_options(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
int v5le_vty_is_config_node(struct vty *vty, int node)
|
||||
static int quit = 0;
|
||||
|
||||
static void sighandler(int sigset)
|
||||
{
|
||||
switch (node) {
|
||||
case CONFIG_NODE:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
if (sigset == SIGHUP)
|
||||
return;
|
||||
if (sigset == SIGPIPE)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "Signal received: %d\n", sigset);
|
||||
|
||||
quit = 1;
|
||||
}
|
||||
|
||||
int v5le_vty_go_parent(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
default:
|
||||
if (v5le_vty_is_config_node(vty, vty->node))
|
||||
vty->node = CONFIG_NODE;
|
||||
else
|
||||
vty->node = ENABLE_NODE;
|
||||
|
||||
vty->index = NULL;
|
||||
}
|
||||
|
||||
return vty->node;
|
||||
}
|
||||
|
||||
|
||||
static struct vty_app_info vty_info = {
|
||||
.name = "OsmoV5LE",
|
||||
.version = PACKAGE_VERSION,
|
||||
.go_parent_cb = v5le_vty_go_parent,
|
||||
.is_config_node = v5le_vty_is_config_node,
|
||||
};
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
.cat = log_categories,
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
unsigned int flags;
|
||||
int rc;
|
||||
|
||||
tall_v5le_ctx = talloc_named_const(NULL, 1, "v5le");
|
||||
|
@ -272,18 +217,14 @@ int main(int argc, char **argv)
|
|||
osmo_init_logging2(tall_v5le_ctx, &log_info);
|
||||
libosmo_abis_init(tall_v5le_ctx);
|
||||
|
||||
g_cfg = v5le_config_alloc(tall_v5le_ctx);
|
||||
if (!g_cfg)
|
||||
return -1;
|
||||
|
||||
vty_info.copyright = v5le_copyright;
|
||||
|
||||
vty_init(&vty_info);
|
||||
logging_vty_add_cmds();
|
||||
osmo_talloc_vty_add_cmds();
|
||||
osmo_stats_vty_add_cmds();
|
||||
//mgcp_vty_init();
|
||||
ctrl_vty_init(g_cfg);
|
||||
v5le_vty_init();
|
||||
ctrl_vty_init(tall_v5le_ctx);
|
||||
e1inp_vty_init();
|
||||
osmo_cpu_sched_vty_init(tall_v5le_ctx);
|
||||
|
||||
|
@ -292,53 +233,42 @@ int main(int argc, char **argv)
|
|||
rate_ctr_init(tall_v5le_ctx);
|
||||
osmo_stats_init(tall_v5le_ctx);
|
||||
|
||||
/* global inits of protocols */
|
||||
v5x_l1_init();
|
||||
v5x_le_ctrl_init();
|
||||
v5x_le_port_init();
|
||||
v5x_le_pstn_init();
|
||||
v52_le_lcp_init();
|
||||
v52_le_bcc_init();
|
||||
v52_le_pp_init();
|
||||
v5x_le_mgmt_init();
|
||||
|
||||
/* G.711 init */
|
||||
g711_init();
|
||||
|
||||
/* create v5x instance */
|
||||
v5i = v5x_instance_alloc(tall_v5le_ctx);
|
||||
if (!v5i)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = vty_read_config_file(config_file, NULL);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = e1_init();
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
/* start telnet after reading config for vty_get_bind_addr() */
|
||||
rc = telnet_init_dynif(tall_v5le_ctx, NULL,
|
||||
vty_get_bind_addr(), OSMO_VTY_PORT_MGW);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
#if 0
|
||||
cfg->ctrl = v5le_ctrl_interface_setup(cfg, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
|
||||
if (!cfg->ctrl) {
|
||||
fprintf(stderr, "Failed to init the control interface on %s:%u. Exiting\n",
|
||||
ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_MGW);
|
||||
}
|
||||
|
||||
/* Set the reset callback function. This functions is called when the
|
||||
* mgcp-command "RSIP" (Reset in Progress) is received */
|
||||
cfg->reset_cb = mgcp_rsip_cb;
|
||||
|
||||
/* we need to bind a socket */
|
||||
flags = OSMO_SOCK_F_BIND;
|
||||
if (strlen(cfg->call_agent_addr))
|
||||
flags |= OSMO_SOCK_F_CONNECT;
|
||||
|
||||
rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
||||
cfg->source_addr, cfg->source_port,
|
||||
cfg->call_agent_addr, strlen(cfg->call_agent_addr) ? 2727 : 0, flags);
|
||||
if (rc < 0) {
|
||||
perror("Gateway failed to bind");
|
||||
return -1;
|
||||
fprintf(stderr, "Failed to read config file '%s'.\n", config_file);
|
||||
return rc;
|
||||
}
|
||||
|
||||
cfg->gw_fd.bfd.cb = read_call_agent;
|
||||
cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
|
||||
if (!cfg->gw_fd.bfd.data) {
|
||||
fprintf(stderr, "Gateway memory error.\n");
|
||||
return -1;
|
||||
}
|
||||
rc = gsmtap_init();
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP, listen on %s:%u\n",
|
||||
cfg->source_addr, cfg->source_port);
|
||||
#endif
|
||||
rc = l1_signal_init();
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* start telnet after reading config */
|
||||
rc = telnet_init_default(tall_v5le_ctx, NULL, OSMO_VTY_PORT_MGW);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* initialisation */
|
||||
srand(time(NULL));
|
||||
|
@ -351,11 +281,34 @@ int main(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
/* catch signals */
|
||||
signal(SIGINT, sighandler);
|
||||
signal(SIGHUP, sighandler);
|
||||
signal(SIGTERM, sighandler);
|
||||
signal(SIGPIPE, sighandler);
|
||||
|
||||
/* start management state machine */
|
||||
if (!llist_empty(&v5i->interfaces)) {
|
||||
struct v5x_interface *v5if;
|
||||
v5if = (struct v5x_interface *)v5i->interfaces.next;
|
||||
v5x_le_mgmt_start_delay(v5if, SOCKET_RETRY_TIMER + 1);
|
||||
}
|
||||
|
||||
/* main loop */
|
||||
while (1) {
|
||||
while (!quit) {
|
||||
osmo_select_main(0);
|
||||
}
|
||||
|
||||
/* reset signals */
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGHUP, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
|
||||
/* destroy the universe */
|
||||
v5x_instance_free(v5i);
|
||||
|
||||
free((char *)gsmtap_ip);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,340 @@
|
|||
/* PH-socket, a lightweight ISDN physical layer interface
|
||||
*
|
||||
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include "logging.h"
|
||||
#include "ph_socket.h"
|
||||
|
||||
static int ph_socket_listen_cb(struct osmo_fd *ofd, unsigned int __attribute__((unused)) what);
|
||||
static int ph_socket_connect_cb(struct osmo_fd *ofd, unsigned int __attribute__((unused)) what);
|
||||
static void ph_socket_timeout_cb(void *data);
|
||||
|
||||
static void open_connection(ph_socket_t *s)
|
||||
{
|
||||
uint8_t enable = PH_CTRL_UNBLOCK;
|
||||
int rc, flags;
|
||||
|
||||
if (s->connect_ofd.fd > 0)
|
||||
return;
|
||||
|
||||
LOGP(DPH, LOGL_DEBUG, "Trying to connect to PH-socket server.\n");
|
||||
rc = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (rc < 0) {
|
||||
LOGP(DPH, LOGL_ERROR, "Failed to create UNIX socket.\n");
|
||||
osmo_timer_schedule(&s->retry_timer, SOCKET_RETRY_TIMER, 0);
|
||||
return;
|
||||
}
|
||||
s->connect_ofd.fd = rc;
|
||||
s->connect_ofd.data = s;
|
||||
s->connect_ofd.when = BSC_FD_READ;
|
||||
s->connect_ofd.cb = ph_socket_connect_cb;
|
||||
osmo_fd_register(&s->connect_ofd);
|
||||
/* set nonblocking io, because we do multiple reads when handling read event */
|
||||
flags = fcntl(s->connect_ofd.fd, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(s->connect_ofd.fd, F_SETFL, flags);
|
||||
/* connect */
|
||||
rc = connect(s->connect_ofd.fd, (struct sockaddr *)&s->sock_address, sizeof(s->sock_address));
|
||||
if (rc < 0 && errno != EAGAIN) {
|
||||
if (!s->connect_failed)
|
||||
LOGP(DPH, LOGL_NOTICE, "Failed to connect UNIX socket, retrying...\n");
|
||||
close(s->connect_ofd.fd);
|
||||
s->connect_failed = 1;
|
||||
osmo_fd_unregister(&s->connect_ofd);
|
||||
s->connect_ofd.fd = 0;
|
||||
osmo_timer_schedule(&s->retry_timer, SOCKET_RETRY_TIMER, 0);
|
||||
return;
|
||||
}
|
||||
s->connect_failed = 0;
|
||||
LOGP(DPH, LOGL_INFO, "Connection to PH-socket server.\n");
|
||||
/* reset rx buffer */
|
||||
s->rx_header_index = 0;
|
||||
s->rx_data_index = 0;
|
||||
/* indicate established socket connection */
|
||||
s->ph_socket_rx_msg(s, 0, PH_PRIM_CTRL_IND, &enable, 1);
|
||||
}
|
||||
|
||||
static void close_connection(ph_socket_t *s)
|
||||
{
|
||||
struct socket_msg_list *ml;
|
||||
uint8_t disable = PH_CTRL_BLOCK;
|
||||
|
||||
if (s->connect_ofd.fd <= 0)
|
||||
return;
|
||||
|
||||
LOGP(DPH, LOGL_INFO, "Connection from PH-socket closed.\n");
|
||||
|
||||
/* indicate loss of socket connection */
|
||||
s->ph_socket_rx_msg(s, 0, (s->listen_ofd.fd > 0) ? PH_PRIM_CTRL_REQ : PH_PRIM_CTRL_IND, &disable, 1);
|
||||
|
||||
osmo_fd_unregister(&s->connect_ofd);
|
||||
close(s->connect_ofd.fd);
|
||||
s->connect_ofd.fd = 0;
|
||||
|
||||
while ((ml = s->tx_list)) {
|
||||
s->tx_list = ml->next;
|
||||
free(ml);
|
||||
}
|
||||
s->tx_list_tail = &s->tx_list;
|
||||
if (s->rx_msg) {
|
||||
free(s->rx_msg);
|
||||
s->rx_msg = NULL;
|
||||
}
|
||||
|
||||
if (s->listen_ofd.fd <= 0) {
|
||||
/* set timer, so that retry is delayed */
|
||||
osmo_timer_schedule(&s->retry_timer, SOCKET_RETRY_TIMER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int ph_socket_init(ph_socket_t *s, void (*ph_socket_rx_msg)(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data,
|
||||
int length), void *priv, const char *socket_name, int server)
|
||||
{
|
||||
int rc;
|
||||
|
||||
memset(s, 0, sizeof(*s));
|
||||
s->name = socket_name;
|
||||
s->ph_socket_rx_msg = ph_socket_rx_msg;
|
||||
s->priv = priv;
|
||||
s->tx_list_tail = &s->tx_list;
|
||||
|
||||
memset(&s->sock_address, 0, sizeof(s->sock_address));
|
||||
s->sock_address.sun_family = AF_UNIX;
|
||||
strcpy(s->sock_address.sun_path+1, socket_name);
|
||||
|
||||
s->retry_timer.data = s;
|
||||
s->retry_timer.cb = ph_socket_timeout_cb;
|
||||
|
||||
if (server) {
|
||||
rc = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (rc < 0) {
|
||||
LOGP(DPH, LOGL_ERROR, "Failed to create UNIX socket.\n");
|
||||
return rc;
|
||||
}
|
||||
s->listen_ofd.fd = rc;
|
||||
s->listen_ofd.data = s;
|
||||
s->listen_ofd.when = BSC_FD_READ;
|
||||
s->listen_ofd.cb = ph_socket_listen_cb;
|
||||
osmo_fd_register(&s->listen_ofd);
|
||||
|
||||
rc = bind(s->listen_ofd.fd, (struct sockaddr *)(&s->sock_address), sizeof(s->sock_address));
|
||||
if (rc < 0) {
|
||||
LOGP(DPH, LOGL_ERROR, "Failed to bind UNIX socket with path '%s' (errno = %d (%s)).\n",
|
||||
s->name, errno, strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = listen(s->listen_ofd.fd, 1);
|
||||
if (rc < 0) {
|
||||
LOGP(DPH, LOGL_ERROR, "Failed to listen to UNIX socket with path '%s' (errno = %d (%s)).\n",
|
||||
s->name, errno, strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
} else
|
||||
open_connection(s);
|
||||
|
||||
LOGP(DPH, LOGL_INFO, "Created PH-socket at '%s'.\n", s->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ph_socket_exit(ph_socket_t *s)
|
||||
{
|
||||
LOGP(DPH, LOGL_INFO, "Destroyed PH-socket.\n");
|
||||
|
||||
close_connection(s);
|
||||
if (s->listen_ofd.fd > 0) {
|
||||
osmo_fd_unregister(&s->listen_ofd);
|
||||
close(s->listen_ofd.fd);
|
||||
s->listen_ofd.fd = 0;
|
||||
}
|
||||
|
||||
if (osmo_timer_pending(&s->retry_timer))
|
||||
osmo_timer_del(&s->retry_timer);
|
||||
}
|
||||
|
||||
static int ph_socket_listen_cb(struct osmo_fd *ofd, unsigned int __attribute__((unused)) what)
|
||||
{
|
||||
ph_socket_t *s = ofd->data;
|
||||
struct sockaddr_un sock_address;
|
||||
uint8_t enable = PH_CTRL_UNBLOCK;
|
||||
int rc, flags;
|
||||
|
||||
socklen_t sock_len = sizeof(sock_address);
|
||||
/* see if there is an incoming connection */
|
||||
rc = accept(s->listen_ofd.fd, (struct sockaddr *)&sock_address, &sock_len);
|
||||
if (rc > 0) {
|
||||
if (s->connect_ofd.fd > 0) {
|
||||
LOGP(DPH, LOGL_ERROR, "Rejecting incoming connection, because we already have a client "
|
||||
"connected!\n");
|
||||
close(rc);
|
||||
} else {
|
||||
LOGP(DPH, LOGL_INFO, "Connection from PH-socket client.\n");
|
||||
s->connect_ofd.fd = rc;
|
||||
s->connect_ofd.data = s;
|
||||
s->connect_ofd.when = BSC_FD_READ;
|
||||
s->connect_ofd.cb = ph_socket_connect_cb;
|
||||
osmo_fd_register(&s->connect_ofd);
|
||||
/* set nonblocking io, because we do multiple reads when handling read event */
|
||||
flags = fcntl(s->connect_ofd.fd, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(s->connect_ofd.fd, F_SETFL, flags);
|
||||
/* reset rx buffer */
|
||||
s->rx_header_index = 0;
|
||||
s->rx_data_index = 0;
|
||||
/* indicate established socket connection */
|
||||
s->ph_socket_rx_msg(s, 0, PH_PRIM_CTRL_REQ, &enable, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ph_socket_connect_cb(struct osmo_fd *ofd, unsigned __attribute__((unused)) int what)
|
||||
{
|
||||
ph_socket_t *s = ofd->data;
|
||||
int rc;
|
||||
|
||||
if (what & BSC_FD_READ) {
|
||||
rx_again:
|
||||
if (!s->rx_msg)
|
||||
s->rx_msg = calloc(1, sizeof(*s->rx_msg));
|
||||
if (s->rx_header_index < (int)sizeof(s->rx_msg->msg.header)) {
|
||||
/* read header until complete */
|
||||
rc = recv(s->connect_ofd.fd, ((uint8_t *)&s->rx_msg->msg.header) + s->rx_header_index,
|
||||
sizeof(s->rx_msg->msg.header) - s->rx_header_index, 0);
|
||||
if (rc > 0) {
|
||||
s->rx_header_index += rc;
|
||||
goto rx_again;
|
||||
} else if (rc == 0 || errno != EAGAIN) {
|
||||
close_connection(s);
|
||||
return 0;
|
||||
}
|
||||
} else if (s->rx_data_index < s->rx_msg->msg.header.length) {
|
||||
/* read data until complete */
|
||||
rc = recv(s->connect_ofd.fd, s->rx_msg->msg.data + s->rx_data_index,
|
||||
s->rx_msg->msg.header.length - s->rx_data_index, 0);
|
||||
if (rc > 0) {
|
||||
s->rx_data_index += rc;
|
||||
goto rx_again;
|
||||
} else if (rc == 0 || errno != EAGAIN) {
|
||||
close_connection(s);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* process and free message */
|
||||
if (s->rx_msg->msg.header.prim != PH_PRIM_DATA_REQ
|
||||
&& s->rx_msg->msg.header.prim != PH_PRIM_DATA_IND
|
||||
&& s->rx_msg->msg.header.prim != PH_PRIM_DATA_CNF) {
|
||||
LOGP(DPH, LOGL_DEBUG, "message 0x%02x channel %d from socket\n",
|
||||
s->rx_msg->msg.header.prim, s->rx_msg->msg.header.channel);
|
||||
if (s->rx_msg->msg.header.length)
|
||||
LOGP(DPH, LOGL_DEBUG, " -> data:%s\n", osmo_hexdump(s->rx_msg->msg.data,
|
||||
s->rx_msg->msg.header.length));
|
||||
}
|
||||
s->ph_socket_rx_msg(s, s->rx_msg->msg.header.channel, s->rx_msg->msg.header.prim,
|
||||
s->rx_msg->msg.data, s->rx_msg->msg.header.length);
|
||||
free(s->rx_msg);
|
||||
s->rx_msg = NULL;
|
||||
/* reset rx buffer */
|
||||
s->rx_header_index = 0;
|
||||
s->rx_data_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (what & BSC_FD_WRITE) {
|
||||
if (s->tx_list) {
|
||||
/* some frame in tx list, so try sending it */
|
||||
rc = send(s->connect_ofd.fd, ((uint8_t *)&s->tx_list->msg.header),
|
||||
sizeof(s->tx_list->msg.header) + s->tx_list->msg.header.length, 0);
|
||||
if (rc > 0) {
|
||||
struct socket_msg_list *ml;
|
||||
if (rc != (int)sizeof(s->tx_list->msg.header) + s->tx_list->msg.header.length) {
|
||||
LOGP(DPH, LOGL_ERROR, "Short write, please fix handling!\n");
|
||||
}
|
||||
/* remove list entry */
|
||||
ml = s->tx_list;
|
||||
s->tx_list = ml->next;
|
||||
if (s->tx_list == NULL)
|
||||
s->tx_list_tail = &s->tx_list;
|
||||
free(ml);
|
||||
} else if (rc == 0 || errno != EAGAIN) {
|
||||
close_connection(s);
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
s->connect_ofd.when &= ~BSC_FD_WRITE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ph_socket_timeout_cb(void *data)
|
||||
{
|
||||
ph_socket_t *s = data;
|
||||
|
||||
open_connection(s);
|
||||
}
|
||||
|
||||
void ph_socket_tx_msg(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length)
|
||||
{
|
||||
struct socket_msg_list *tx_msg;
|
||||
|
||||
if (prim != PH_PRIM_DATA_REQ
|
||||
&& prim != PH_PRIM_DATA_IND
|
||||
&& prim != PH_PRIM_DATA_CNF) {
|
||||
LOGP(DPH, LOGL_DEBUG, "message 0x%02x channel %d to socket\n", prim, channel);
|
||||
if (length)
|
||||
LOGP(DPH, LOGL_DEBUG, " -> data:%s\n", osmo_hexdump(data, length));
|
||||
}
|
||||
|
||||
if (length > (int)sizeof(tx_msg->msg.data)) {
|
||||
LOGP(DPH, LOGL_NOTICE, "Frame from HDLC process too large for socket, dropping!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->connect_ofd.fd <= 0) {
|
||||
LOGP(DPH, LOGL_NOTICE, "Dropping message for socket, socket is closed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tx_msg = calloc(1, sizeof(*tx_msg));
|
||||
tx_msg->msg.header.channel = channel;
|
||||
tx_msg->msg.header.prim = prim;
|
||||
if (length) {
|
||||
tx_msg->msg.header.length = length;
|
||||
memcpy(tx_msg->msg.data, data, length);
|
||||
}
|
||||
/* move message to list */
|
||||
*s->tx_list_tail = tx_msg;
|
||||
s->tx_list_tail = &tx_msg->next;
|
||||
s->connect_ofd.when |= BSC_FD_WRITE;
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
|
||||
#include <sys/un.h>
|
||||
|
||||
/*
|
||||
* Procedure:
|
||||
*
|
||||
* If socket connection is establised, a PH_PRIM_CTRL_REQ message with
|
||||
* PH_CTRL_ENABLE information is received by the socket server user.
|
||||
* If the socket connection is lost, a PH_PRIM_CTRL_REQ message with
|
||||
* PH_CTRL_DISABLE information is received by the user.
|
||||
*
|
||||
* If socket connection is establised, a PH_PRIM_CTRL_IND message with
|
||||
* PH_CTRL_ENABLE information is received by the socket client user.
|
||||
* If the socket connection is lost, a PH_PRIM_CTRL_IND message with
|
||||
* PH_CTRL_DISABLE information is received by the user.
|
||||
*
|
||||
* The socket server should enable or disable interface depending on
|
||||
* the PH_CTRL_ENABLE / PH_CTRL_DISABLE information.
|
||||
*
|
||||
* The socket client user shall keep track of last PH_PRIM_ACT_IND /
|
||||
* PH_PRIM_DEACT_IND message and treat a PH_PRIM_CTRL_IND message with
|
||||
* PH_CTRL_DISABLE information as a deactivation of all channels that
|
||||
* were activated. Also it shall reject every PH_RIM_ACT_REQ with a
|
||||
* PH_PRIM_DACT_IND, if the socket is currently unavailable.
|
||||
*
|
||||
* PH_PRIM_CTRL_REQ and PH_PRIM_CTRL_IND messages with PH_CTRL_ENABLE
|
||||
* and PH_CTRL_DISABLE informations are not assoicated with a channel
|
||||
* number. The socket sender shall set it to 0, the receiver shall
|
||||
* ignore it.
|
||||
*
|
||||
* A missing MODE in PH_PRIM_ACT_REQ is interepreted as default:
|
||||
* HDLC on D-channel, TRANS on B-channel.
|
||||
*
|
||||
* Each packet on the socket shall have the follwoing header:
|
||||
* uint8_t channel;
|
||||
* uint8_t prim;
|
||||
* uint16_t length;
|
||||
*
|
||||
* The length shall be in host's endian on UN sockets and in network
|
||||
* endian on TCP sockets and not being transmitted on UDP sockets.
|
||||
*
|
||||
* 0 to 65535 bytes shall follow the header, depending on the length
|
||||
* information field.
|
||||
*/
|
||||
|
||||
/* all primitives */
|
||||
#define PH_PRIM_DATA_REQ 0x00 /* any data sent to channel from upper layer */
|
||||
#define PH_PRIM_DATA_IND 0x01 /* any data received from channel to upper layer */
|
||||
#define PH_PRIM_DATA_CNF 0x02 /* confirm data sent to channel */
|
||||
|
||||
#define PH_PRIM_CTRL_REQ 0x04 /* implementation specific requests towards interface */
|
||||
#define PH_PRIM_CTRL_IND 0x05 /* implementation specific indications from interface */
|
||||
|
||||
#define PH_PRIM_ACT_REQ 0x08 /* activation request of channel, mode is given as payload */
|
||||
#define PH_PRIM_ACT_IND 0x09 /* activation indication that the channel is now active */
|
||||
|
||||
#define PH_PRIM_DACT_REQ 0x0c /* deactivation request of channel */
|
||||
#define PH_PRIM_DACT_IND 0x0d /* deactivation indication that the channel is now inactive */
|
||||
|
||||
/* one byte sent activation request */
|
||||
#define PH_MODE_TRANS 0x00 /* raw data is sent via B-channel */
|
||||
#define PH_MODE_HDLC 0x01 /* HDLC transcoding is performed via B-channel */
|
||||
|
||||
/* one byte sent with control messages */
|
||||
#define PH_CTRL_BLOCK 0x00 /* disable (block) interface, when socket is disconnected */
|
||||
#define PH_CTRL_UNBLOCK 0x01 /* enable (unblock) interface, when socket is connected */
|
||||
#define PH_CTRL_LOOP_DISABLE 0x04 /* disable loopback */
|
||||
#define PH_CTRL_LOOP1_ENABLE 0x05 /* enable LT transceier loopback */
|
||||
#define PH_CTRL_LOOP2_ENABLE 0x06 /* enable NT transceier loopback */
|
||||
#define PH_CTRL_LOOP_ERROR 0x10 /* frame error report (loopback test) */
|
||||
#define PH_CTRL_VIOLATION_LT 0x11 /* code violation received by LT */
|
||||
#define PH_CTRL_VIOLATION_NT 0x12 /* code violation received by NT */
|
||||
|
||||
struct socket_msg {
|
||||
struct {
|
||||
uint8_t channel;
|
||||
uint8_t prim;
|
||||
uint16_t length;
|
||||
} header;
|
||||
uint8_t data[65536];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct socket_msg_list {
|
||||
struct socket_msg_list *next;
|
||||
struct socket_msg msg;
|
||||
};
|
||||
|
||||
#define SOCKET_RETRY_TIMER 2
|
||||
|
||||
typedef struct ph_socket {
|
||||
const char *name;
|
||||
void (*ph_socket_rx_msg)(struct ph_socket *s, int channel, uint8_t prim, uint8_t *data, int length);
|
||||
void *priv;
|
||||
struct sockaddr_un sock_address;
|
||||
struct osmo_fd listen_ofd; /* socket to listen to incoming connections */
|
||||
struct osmo_fd connect_ofd; /* socket of incoming connection */
|
||||
int connect_failed; /* used to print a failure only once */
|
||||
struct osmo_timer_list retry_timer; /* timer to connect again */
|
||||
struct socket_msg_list *tx_list, **tx_list_tail;
|
||||
struct socket_msg_list *rx_msg;
|
||||
int rx_header_index;
|
||||
int rx_data_index;
|
||||
} ph_socket_t;
|
||||
|
||||
int ph_socket_init(ph_socket_t *s, void (*ph_socket_rx_msg)(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length), void *priv, const char *socket_name, int server);
|
||||
void ph_socket_exit(ph_socket_t *s);
|
||||
void ph_socket_tx_msg(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length);
|
|
@ -0,0 +1,817 @@
|
|||
/* ITu-T G.965 Section 17 V5.2-interface BCC protocol - LE side */
|
||||
|
||||
/* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/***********************************************************************/
|
||||
/* internal data structures */
|
||||
/***********************************************************************/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "v5x_le_management.h"
|
||||
#include "v52_le_bcc_fsm.h"
|
||||
#include "logging.h"
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
#define TIMEOUT_Tbcc1 2 // 1500 ms
|
||||
#define TIMEOUT_Tbcc2 2
|
||||
#define TIMEOUT_Tbcc3 2
|
||||
#define TIMEOUT_Tbcc4 1
|
||||
|
||||
/***********************************************************************/
|
||||
/* state names, event names, primitives, ... */
|
||||
/***********************************************************************/
|
||||
|
||||
/* 17.2.1.2 */
|
||||
enum v52_bcc_fsm_state {
|
||||
V52_BCCFSM_S_LEBcc0_NULL,
|
||||
V52_BCCFSM_S_LEBcc1_WAITING_ALLOCATION,
|
||||
V52_BCCFSM_S_LEBcc2_ALLOCATION_ABORT,
|
||||
V52_BCCFSM_S_LEBcc3_WAITING_DE_ALLOCATION,
|
||||
V52_BCCFSM_S_LEBcc4_WAITING_AUDIT,
|
||||
};
|
||||
|
||||
enum v52_bcc_fsm_event {
|
||||
V52_BCCFSM_E_MDU_BCC_allocation_req,
|
||||
V52_BCCFSM_E_MDU_BCC_deallocation_req,
|
||||
V52_BCCFSM_E_MDU_BCC_audit_req,
|
||||
V52_BCCFSM_E_ALLOCATION_COMPLETE,
|
||||
V52_BCCFSM_E_ALLOCATION_REJECT,
|
||||
V52_BCCFSM_E_DE_ALLOCATION_COMPLETE,
|
||||
V52_BCCFSM_E_DE_ALLOCATION_REJECT,
|
||||
V52_BCCFSM_E_AUDIT_COMPLETE,
|
||||
V52_BCCFSM_E_AN_FAULT,
|
||||
V52_BCCFSM_E_PROTOCOL_ERROR,
|
||||
V52_BCCFSM_E_TIMEOUT_Tbcc1,
|
||||
V52_BCCFSM_E_TIMEOUT_Tbcc2,
|
||||
V52_BCCFSM_E_TIMEOUT_Tbcc3,
|
||||
V52_BCCFSM_E_TIMEOUT_Tbcc4,
|
||||
};
|
||||
|
||||
static const struct value_string v52_bcc_fsm_event_names[] = {
|
||||
{ V52_BCCFSM_E_MDU_BCC_allocation_req, "MDU-BCC (Allocation request)" },
|
||||
{ V52_BCCFSM_E_MDU_BCC_deallocation_req, "MDU-BCC (De-allocation request)" },
|
||||
{ V52_BCCFSM_E_MDU_BCC_audit_req, "MDU-BCC (Audit request)" },
|
||||
{ V52_BCCFSM_E_ALLOCATION_COMPLETE, "ALLOCATION COMPLETE" },
|
||||
{ V52_BCCFSM_E_ALLOCATION_REJECT, "ALLOCATION REJECT" },
|
||||
{ V52_BCCFSM_E_DE_ALLOCATION_COMPLETE, "DE-ALLOCATION COMPLETE" },
|
||||
{ V52_BCCFSM_E_DE_ALLOCATION_REJECT, "DE-ALLOCATION REJECT" },
|
||||
{ V52_BCCFSM_E_AUDIT_COMPLETE, "AUDIT COMPLETE" },
|
||||
{ V52_BCCFSM_E_AN_FAULT, "AN FAULT" },
|
||||
{ V52_BCCFSM_E_PROTOCOL_ERROR, "PROTOCOL ERROR" },
|
||||
{ V52_BCCFSM_E_TIMEOUT_Tbcc1, "Timeout Tbbc1" },
|
||||
{ V52_BCCFSM_E_TIMEOUT_Tbcc2, "Timeout Tbbc2" },
|
||||
{ V52_BCCFSM_E_TIMEOUT_Tbcc3, "Timeout Tbbc3" },
|
||||
{ V52_BCCFSM_E_TIMEOUT_Tbcc4, "Timeout Tbbc4" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/***********************************************************************
|
||||
* V5 Message encoding / sending
|
||||
***********************************************************************/
|
||||
|
||||
static inline uint16_t v52_bcc_ref_enc(uint16_t ref, uint8_t source_id)
|
||||
{
|
||||
uint8_t low, high;
|
||||
|
||||
high = (ref >> 6) | (source_id << 7);
|
||||
low = ref & 0x3f;
|
||||
|
||||
return (high << 8) | low;
|
||||
}
|
||||
|
||||
static inline uint16_t v52_bcc_ref_dec(uint16_t ie, uint8_t *source_id)
|
||||
{
|
||||
uint8_t low, high;
|
||||
|
||||
high = (ie >> 8) & 0x7f;
|
||||
low = ie & 0x3f;
|
||||
|
||||
*source_id = ie >> 15;
|
||||
return (high << 6) | low;
|
||||
}
|
||||
|
||||
static inline uint16_t v52_bcc_ts_enc(uint8_t link_id, uint8_t ts, uint8_t override)
|
||||
{
|
||||
OSMO_ASSERT(!(override >> 1));
|
||||
return (link_id << 8) | (override << 5) | ts;
|
||||
}
|
||||
|
||||
static inline void v52_bcc_ts_dec(uint16_t ie, uint8_t *link_id, uint8_t *ts, uint8_t *override)
|
||||
{
|
||||
*link_id = ie >> 8;
|
||||
*override = (ie >> 5) & 0x01;
|
||||
*ts = ie & 0x1f;
|
||||
}
|
||||
|
||||
static struct v52_bcc_proc *find_bcc_proto_ref(struct v5x_interface *v5if, uint16_t ref, uint8_t source_id)
|
||||
{
|
||||
struct v52_bcc_proc *bcc;
|
||||
|
||||
llist_for_each_entry(bcc, &v5if->bcc_procs, list) {
|
||||
if (bcc->ref == ref && bcc->source_id == source_id)
|
||||
return bcc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct v52_bcc_proc *find_bcc_proto_ts(struct v5x_interface *v5if, uint8_t link_id, uint8_t ts)
|
||||
{
|
||||
struct v52_bcc_proc *bcc;
|
||||
|
||||
llist_for_each_entry(bcc, &v5if->bcc_procs, list) {
|
||||
if (bcc->isdn_multislot)
|
||||
continue;
|
||||
if (bcc->link_id == link_id && bcc->ts == ts)
|
||||
return bcc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct v52_bcc_proc *find_bcc_proto_multislot(struct v5x_interface *v5if, uint8_t link_id,
|
||||
uint8_t *isdn_multislot)
|
||||
{
|
||||
struct v52_bcc_proc *bcc;
|
||||
|
||||
llist_for_each_entry(bcc, &v5if->bcc_procs, list) {
|
||||
if (!bcc->isdn_multislot)
|
||||
continue;
|
||||
if (bcc->link_id == link_id && !memcmp(bcc->isdn_multislot, isdn_multislot, 8))
|
||||
return bcc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* G.965 Section 17.3.1 / Table 29 */
|
||||
static struct msgb *v52_enc_allocation(uint16_t ref, uint8_t source_id, uint16_t user_port_id, bool is_isdn,
|
||||
uint8_t link_id, uint8_t ts, uint8_t override, uint8_t isdn_slot, uint8_t *isdn_multislot,
|
||||
uint8_t *capability)
|
||||
{
|
||||
uint16_t user_port_ie;
|
||||
uint8_t isdn_ts_ie, capa_ie;
|
||||
uint16_t v5_ts_ie;
|
||||
uint8_t multislot_ie[9];
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(v52_bcc_ref_enc(ref, source_id));
|
||||
l3h->msg_type = V52_CTRL_MSGT_ALLOCATION;
|
||||
/* user port identification */
|
||||
user_port_ie = htons(v5x_l3_addr_enc(user_port_id, is_isdn));
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_USER_PORT_ID, 2, (uint8_t *)&user_port_ie);
|
||||
if (is_isdn) {
|
||||
/* ISDN port time slot */
|
||||
isdn_ts_ie = 0x80 | isdn_slot;
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID, 1, &isdn_ts_ie);
|
||||
}
|
||||
if (!isdn_multislot) {
|
||||
/* V5 time slot */
|
||||
v5_ts_ie = htons(v52_bcc_ts_enc(link_id, ts, override));
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_V5_TS_ID, 2, (uint8_t *)&v5_ts_ie);
|
||||
} else {
|
||||
/* V5 multi slot */
|
||||
multislot_ie[0] = link_id;
|
||||
memcpy(multislot_ie + 1, isdn_multislot, 8);
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_MULTI_TS_MAP, 9, multislot_ie);
|
||||
}
|
||||
if (is_isdn && capability) {
|
||||
/* information transfer capability */
|
||||
capa_ie = 0x80 | *capability;
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY, 1, &capa_ie);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.965 Section 17.3.4 / Table 32 */
|
||||
static struct msgb *v52_enc_de_allocation(uint16_t ref, uint8_t source_id, uint16_t user_port_id, bool is_isdn,
|
||||
uint8_t link_id, uint8_t ts, uint8_t override, uint8_t isdn_slot, uint8_t *isdn_multislot)
|
||||
{
|
||||
uint16_t user_port_ie;
|
||||
uint8_t isdn_ts_ie;
|
||||
uint16_t v5_ts_ie;
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(v52_bcc_ref_enc(ref, source_id));
|
||||
l3h->msg_type = V52_CTRL_MSGT_DE_ALLOCATION;
|
||||
/* user port identification */
|
||||
user_port_ie = htons(v5x_l3_addr_enc(user_port_id, is_isdn));
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_USER_PORT_ID, 2, (uint8_t *)&user_port_ie);
|
||||
if (is_isdn) {
|
||||
/* ISDN port time slot */
|
||||
isdn_ts_ie = 0x80 | isdn_slot;
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID, 1, &isdn_ts_ie);
|
||||
}
|
||||
if (!isdn_multislot) {
|
||||
/* V5 time slot */
|
||||
v5_ts_ie = htons(v52_bcc_ts_enc(link_id, ts, override));
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_V5_TS_ID, 2, (uint8_t *)&v5_ts_ie);
|
||||
} else {
|
||||
/* V5 multi slot */
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_MULTI_TS_MAP, 9, isdn_multislot);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.965 Section 17.3.7 / Table 35 */
|
||||
static struct msgb *v52_enc_audit(uint16_t ref, uint8_t source_id, uint8_t link_id, uint8_t ts, uint8_t override)
|
||||
{
|
||||
uint16_t v5_ts_ie;
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(v52_bcc_ref_enc(ref, source_id));
|
||||
l3h->msg_type = V52_CTRL_MSGT_AUDIT;
|
||||
/* V5 time slot */
|
||||
v5_ts_ie = htons(v52_bcc_ts_enc(link_id, ts, override));
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_V5_TS_ID, 2, (uint8_t *)&v5_ts_ie);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.965 Section 17.3.10 / Table 38 */
|
||||
static struct msgb *v52_enc_an_fault_ack(uint16_t ref, uint8_t source_id)
|
||||
{
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(v52_bcc_ref_enc(ref, source_id));
|
||||
l3h->msg_type = V52_CTRL_MSGT_AN_FAULT_ACK;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
/* Messages to other layers */
|
||||
/***********************************************************************/
|
||||
|
||||
/* send message to upper (management) layer */
|
||||
static void rcv_mdu(struct osmo_fsm_inst *fi, enum v5x_mgmt_prim prim, const uint8_t *cause, uint8_t cause_len)
|
||||
{
|
||||
struct v52_bcc_proc *bcc = fi->priv;
|
||||
|
||||
v52_le_bcc_mdu_rcv(bcc->interface, bcc->link_id, bcc->ts, bcc->isdn_multislot, prim, cause, cause_len);
|
||||
}
|
||||
|
||||
/* message to lower (DL) layer */
|
||||
static void snd_msg(struct osmo_fsm_inst *fi, struct msgb *msg)
|
||||
{
|
||||
struct v52_bcc_proc *bcc = fi->priv;
|
||||
|
||||
if (!msg)
|
||||
return;
|
||||
v5x_dl_snd(bcc->interface, V52_DLADDR_BCC, msg);
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
/* LCP state FSM */
|
||||
/***********************************************************************/
|
||||
|
||||
static int v52_le_bcc_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct v52_bcc_proc *bcc = fi->priv;
|
||||
|
||||
bcc->expired++;
|
||||
osmo_fsm_inst_dispatch(fi, bcc->timer, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcc_fsm_lebcc0_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct v52_bcc_proc *bcc = fi->priv;
|
||||
const struct tlv_parsed *tp = data;
|
||||
uint8_t const *cause;
|
||||
int cause_len;
|
||||
|
||||
switch (event) {
|
||||
case V52_BCCFSM_E_MDU_BCC_allocation_req:
|
||||
/* Start Tbcc1 */
|
||||
bcc->timer = V52_BCCFSM_E_TIMEOUT_Tbcc1;
|
||||
bcc->expired = 0;
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc1_WAITING_ALLOCATION, TIMEOUT_Tbcc1, 1);
|
||||
/* Send ALLOCATION */
|
||||
snd_msg(fi, v52_enc_allocation(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->is_isdn, bcc->link_id,
|
||||
bcc->ts, bcc->override, bcc->isdn_slot, bcc->isdn_multislot,
|
||||
(bcc->use_capability) ? &bcc->capability : NULL));
|
||||
break;
|
||||
case V52_BCCFSM_E_ALLOCATION_COMPLETE:
|
||||
/* Send MDU-BCC (Allocation confirmation) */
|
||||
rcv_mdu(fi, MDU_BCC_allocation_conf, 0, 0);
|
||||
break;
|
||||
case V52_BCCFSM_E_ALLOCATION_REJECT:
|
||||
/* Send MDU-BCC (Allocation rejedct indication) */
|
||||
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
||||
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
||||
rcv_mdu(fi, MDU_BCC_allocation_reject_ind, cause, cause_len);
|
||||
break;
|
||||
case V52_BCCFSM_E_MDU_BCC_deallocation_req:
|
||||
/* Start Tbcc3 */
|
||||
bcc->timer = V52_BCCFSM_E_TIMEOUT_Tbcc3;
|
||||
bcc->expired = 0;
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc3_WAITING_DE_ALLOCATION, TIMEOUT_Tbcc3, 3);
|
||||
/* Send DE-ALLOCATION */
|
||||
snd_msg(fi, v52_enc_de_allocation(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->is_isdn,
|
||||
bcc->link_id, bcc->ts, bcc->override, bcc->isdn_slot, bcc->isdn_multislot));
|
||||
break;
|
||||
case V52_BCCFSM_E_DE_ALLOCATION_COMPLETE:
|
||||
/* Send MDU-BCC (De-allocation confirmation) */
|
||||
rcv_mdu(fi, MDU_BCC_deallocation_conf, 0, 0);
|
||||
break;
|
||||
case V52_BCCFSM_E_DE_ALLOCATION_REJECT:
|
||||
/* Send MDU-BCC (De-allocation reject indication) */
|
||||
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
||||
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
||||
rcv_mdu(fi, MDU_BCC_deallocation_reject_ind, cause, cause_len);
|
||||
break;
|
||||
case V52_BCCFSM_E_MDU_BCC_audit_req:
|
||||
/* Start Tbcc4 */
|
||||
bcc->timer = V52_BCCFSM_E_TIMEOUT_Tbcc4;
|
||||
bcc->expired = 0;
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc4_WAITING_AUDIT, TIMEOUT_Tbcc4, 4);
|
||||
/* Send ADUIT */
|
||||
snd_msg(fi, v52_enc_audit(bcc->ref, bcc->source_id, bcc->link_id, bcc->ts, bcc->override));
|
||||
break;
|
||||
case V52_BCCFSM_E_AN_FAULT:
|
||||
/* Send AN FAULT ACK */
|
||||
snd_msg(fi, v52_enc_an_fault_ack(bcc->ref, bcc->source_id));
|
||||
/* Send MDU-BCC (AN Fault indication) */
|
||||
rcv_mdu(fi, MDU_BCC_AN_fault_ind, 0, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void bcc_fsm_lebcc1_waiting_alloc(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct v52_bcc_proc *bcc = fi->priv;
|
||||
const struct tlv_parsed *tp = data;
|
||||
uint8_t const *cause;
|
||||
int cause_len;
|
||||
|
||||
switch (event) {
|
||||
case V52_BCCFSM_E_ALLOCATION_COMPLETE:
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (Allocation confirmation) */
|
||||
rcv_mdu(fi, MDU_BCC_allocation_conf, 0, 0);
|
||||
break;
|
||||
case V52_BCCFSM_E_ALLOCATION_REJECT:
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (Allocation reject indication) */
|
||||
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
||||
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
||||
rcv_mdu(fi, MDU_BCC_allocation_reject_ind, cause, cause_len);
|
||||
break;
|
||||
case V52_BCCFSM_E_MDU_BCC_deallocation_req:
|
||||
/* Start Tbcc2 */
|
||||
bcc->timer = V52_BCCFSM_E_TIMEOUT_Tbcc2;
|
||||
bcc->expired = 0;
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc2_ALLOCATION_ABORT, TIMEOUT_Tbcc2, 2);
|
||||
/* Send DE-ALLOCATION */
|
||||
snd_msg(fi, v52_enc_de_allocation(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->is_isdn,
|
||||
bcc->link_id, bcc->ts, bcc->override, bcc->isdn_slot, bcc->isdn_multislot));
|
||||
break;
|
||||
case V52_BCCFSM_E_TIMEOUT_Tbcc1:
|
||||
if (bcc->expired == 1) {
|
||||
/* Restart Tbcc1 */
|
||||
osmo_timer_schedule(&fi->timer, TIMEOUT_Tbcc1, 0);
|
||||
/* Send ALLOCATION */
|
||||
snd_msg(fi, v52_enc_allocation(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->is_isdn,
|
||||
bcc->link_id, bcc->ts, bcc->override, bcc->isdn_slot, bcc->isdn_multislot,
|
||||
(bcc->use_capability) ? &bcc->capability : NULL));
|
||||
break;
|
||||
}
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (Allocation error indication) */
|
||||
rcv_mdu(fi, MDU_BCC_allocation_error_ind, 0, 0);
|
||||
break;
|
||||
case V52_BCCFSM_E_PROTOCOL_ERROR:
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (Protocol error indication) */
|
||||
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
||||
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
||||
rcv_mdu(fi, MDU_BCC_protocol_error_ind, cause, cause_len);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void bcc_fsm_lebcc2_alloc_abort(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct v52_bcc_proc *bcc = fi->priv;
|
||||
const struct tlv_parsed *tp = data;
|
||||
uint8_t const *cause;
|
||||
int cause_len;
|
||||
|
||||
switch (event) {
|
||||
case V52_BCCFSM_E_ALLOCATION_COMPLETE:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_BCCFSM_E_ALLOCATION_REJECT:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_BCCFSM_E_DE_ALLOCATION_COMPLETE:
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (De-allocation confirmation) */
|
||||
rcv_mdu(fi, MDU_BCC_deallocation_conf, 0, 0);
|
||||
break;
|
||||
case V52_BCCFSM_E_DE_ALLOCATION_REJECT:
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (De-allocation reject indication) */
|
||||
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
||||
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
||||
rcv_mdu(fi, MDU_BCC_deallocation_reject_ind, cause, cause_len);
|
||||
break;
|
||||
case V52_BCCFSM_E_TIMEOUT_Tbcc2:
|
||||
if (bcc->expired == 1) {
|
||||
/* Restart Tbcc2 */
|
||||
osmo_timer_schedule(&fi->timer, TIMEOUT_Tbcc2, 0);
|
||||
/* Send DE-ALLOCATION */
|
||||
snd_msg(fi, v52_enc_de_allocation(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->is_isdn,
|
||||
bcc->link_id, bcc->ts, bcc->override, bcc->isdn_slot, bcc->isdn_multislot));
|
||||
break;
|
||||
}
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (De-allocation error indication) */
|
||||
rcv_mdu(fi, MDU_BCC_deallocation_error_ind, 0, 0);
|
||||
break;
|
||||
case V52_BCCFSM_E_PROTOCOL_ERROR:
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (Protocol error indication) */
|
||||
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
||||
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
||||
rcv_mdu(fi, MDU_BCC_protocol_error_ind, cause, cause_len);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void bcc_fsm_lebcc3_waiting_dealloc(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct v52_bcc_proc *bcc = fi->priv;
|
||||
const struct tlv_parsed *tp = data;
|
||||
uint8_t const *cause;
|
||||
int cause_len;
|
||||
|
||||
switch (event) {
|
||||
case V52_BCCFSM_E_DE_ALLOCATION_COMPLETE:
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (De-allocation confirmation) */
|
||||
rcv_mdu(fi, MDU_BCC_deallocation_conf, 0, 0);
|
||||
break;
|
||||
case V52_BCCFSM_E_DE_ALLOCATION_REJECT:
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (De-allocation reject indication) */
|
||||
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
||||
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
||||
rcv_mdu(fi, MDU_BCC_deallocation_reject_ind, cause, cause_len);
|
||||
break;
|
||||
case V52_BCCFSM_E_TIMEOUT_Tbcc3:
|
||||
if (bcc->expired == 1) {
|
||||
/* Restart Tbcc3 */
|
||||
osmo_timer_schedule(&fi->timer, TIMEOUT_Tbcc3, 0);
|
||||
/* Send DE-ALLOCATION */
|
||||
snd_msg(fi, v52_enc_de_allocation(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->is_isdn,
|
||||
bcc->link_id, bcc->ts, bcc->override, bcc->isdn_slot, bcc->isdn_multislot));
|
||||
break;
|
||||
}
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (De-allocation error indication) */
|
||||
rcv_mdu(fi, MDU_BCC_deallocation_error_ind, 0, 0);
|
||||
break;
|
||||
case V52_BCCFSM_E_PROTOCOL_ERROR:
|
||||
/* Send MDU-BCC (Protocol error indication) */
|
||||
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
||||
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
||||
rcv_mdu(fi, MDU_BCC_protocol_error_ind, cause, cause_len);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void bcc_fsm_lebcc4_waiting_audit(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct v52_bcc_proc *bcc = fi->priv;
|
||||
const struct tlv_parsed *tp = data;
|
||||
uint8_t const *cause;
|
||||
int cause_len;
|
||||
|
||||
switch (event) {
|
||||
case V52_BCCFSM_E_AUDIT_COMPLETE:
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (Audit confirmation) */
|
||||
rcv_mdu(fi, MDU_BCC_audit_conf, 0, 0);
|
||||
break;
|
||||
case V52_BCCFSM_E_TIMEOUT_Tbcc4:
|
||||
if (bcc->expired == 1) {
|
||||
/* Restart Tbcc4 */
|
||||
osmo_timer_schedule(&fi->timer, TIMEOUT_Tbcc4, 0);
|
||||
/* Send AUDIT */
|
||||
snd_msg(fi, v52_enc_audit(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->ts, bcc->override));
|
||||
break;
|
||||
}
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (Audit error indication) */
|
||||
rcv_mdu(fi, MDU_BCC_audit_error_ind, 0, 0);
|
||||
break;
|
||||
case V52_BCCFSM_E_PROTOCOL_ERROR:
|
||||
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
||||
/* Send MDU-BCC (Protocol error indication) */
|
||||
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
||||
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
||||
rcv_mdu(fi, MDU_BCC_protocol_error_ind, cause, cause_len);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Table 47/G.965 */
|
||||
static const struct osmo_fsm_state v52_le_bcc_fsm_states[] = {
|
||||
[V52_BCCFSM_S_LEBcc0_NULL] = {
|
||||
.name = "LEBcc0 BCC BULL",
|
||||
.in_event_mask = S(V52_BCCFSM_E_MDU_BCC_allocation_req) |
|
||||
S(V52_BCCFSM_E_ALLOCATION_COMPLETE) |
|
||||
S(V52_BCCFSM_E_ALLOCATION_REJECT) |
|
||||
S(V52_BCCFSM_E_MDU_BCC_deallocation_req) |
|
||||
S(V52_BCCFSM_E_DE_ALLOCATION_COMPLETE) |
|
||||
S(V52_BCCFSM_E_DE_ALLOCATION_REJECT) |
|
||||
S(V52_BCCFSM_E_MDU_BCC_audit_req) |
|
||||
S(V52_BCCFSM_E_AN_FAULT) ,
|
||||
.out_state_mask = S(V52_BCCFSM_S_LEBcc1_WAITING_ALLOCATION) |
|
||||
S(V52_BCCFSM_S_LEBcc3_WAITING_DE_ALLOCATION) |
|
||||
S(V52_BCCFSM_S_LEBcc4_WAITING_AUDIT),
|
||||
.action = bcc_fsm_lebcc0_null,
|
||||
},
|
||||
[V52_BCCFSM_S_LEBcc1_WAITING_ALLOCATION] = {
|
||||
.name = "LEBcc1 BCC WAITING ALLOCATION",
|
||||
.in_event_mask = S(V52_BCCFSM_E_ALLOCATION_COMPLETE) |
|
||||
S(V52_BCCFSM_E_ALLOCATION_REJECT) |
|
||||
S(V52_BCCFSM_E_MDU_BCC_deallocation_req) |
|
||||
S(V52_BCCFSM_E_TIMEOUT_Tbcc1) |
|
||||
S(V52_BCCFSM_E_PROTOCOL_ERROR),
|
||||
.out_state_mask = S(V52_BCCFSM_S_LEBcc0_NULL) |
|
||||
S(V52_BCCFSM_S_LEBcc2_ALLOCATION_ABORT),
|
||||
.action = bcc_fsm_lebcc1_waiting_alloc,
|
||||
},
|
||||
[V52_BCCFSM_S_LEBcc2_ALLOCATION_ABORT] = {
|
||||
.name = "LEBcc2 BCC ALLOCATION ABORT",
|
||||
.in_event_mask = S(V52_BCCFSM_E_ALLOCATION_COMPLETE) |
|
||||
S(V52_BCCFSM_E_ALLOCATION_REJECT) |
|
||||
S(V52_BCCFSM_E_DE_ALLOCATION_COMPLETE) |
|
||||
S(V52_BCCFSM_E_DE_ALLOCATION_REJECT) |
|
||||
S(V52_BCCFSM_E_TIMEOUT_Tbcc2) |
|
||||
S(V52_BCCFSM_E_PROTOCOL_ERROR),
|
||||
.out_state_mask = S(V52_BCCFSM_S_LEBcc0_NULL),
|
||||
.action = bcc_fsm_lebcc2_alloc_abort,
|
||||
},
|
||||
[V52_BCCFSM_S_LEBcc3_WAITING_DE_ALLOCATION] = {
|
||||
.name = "LEBcc3 BCC WAITING DE-ALLOCATION",
|
||||
.in_event_mask = S(V52_BCCFSM_E_DE_ALLOCATION_COMPLETE) |
|
||||
S(V52_BCCFSM_E_DE_ALLOCATION_REJECT) |
|
||||
S(V52_BCCFSM_E_TIMEOUT_Tbcc3) |
|
||||
S(V52_BCCFSM_E_PROTOCOL_ERROR),
|
||||
.out_state_mask = S(V52_BCCFSM_S_LEBcc0_NULL),
|
||||
.action = bcc_fsm_lebcc3_waiting_dealloc,
|
||||
},
|
||||
[V52_BCCFSM_S_LEBcc4_WAITING_AUDIT] = {
|
||||
.name = "LEBcc4 BCC wAITING AUDIT",
|
||||
.in_event_mask = S(V52_BCCFSM_E_MDU_BCC_audit_req) |
|
||||
S(V52_BCCFSM_E_TIMEOUT_Tbcc1) |
|
||||
S(V52_BCCFSM_E_PROTOCOL_ERROR),
|
||||
.out_state_mask = S(V52_BCCFSM_S_LEBcc0_NULL),
|
||||
.action = bcc_fsm_lebcc4_waiting_audit,
|
||||
},
|
||||
};
|
||||
|
||||
struct osmo_fsm v52_le_bcc_fsm = {
|
||||
.name = "V52_LE_BCC",
|
||||
.states = v52_le_bcc_fsm_states,
|
||||
.num_states = ARRAY_SIZE(v52_le_bcc_fsm_states),
|
||||
.timer_cb = v52_le_bcc_fsm_timer_cb,
|
||||
.log_subsys = DV5BCC,
|
||||
.event_names = v52_bcc_fsm_event_names,
|
||||
};
|
||||
|
||||
static uint16_t bcc_new_ref = 0;
|
||||
|
||||
static struct v52_bcc_proc *v52_le_bcc_create(struct v5x_interface *v5if, uint16_t user_port_id, bool is_isdn,
|
||||
uint8_t link_id, uint8_t ts, uint8_t override, uint8_t isdn_slot, uint8_t *isdn_multislot, uint8_t *capability,
|
||||
int source_id)
|
||||
{
|
||||
struct v52_bcc_proc *bcc;
|
||||
|
||||
OSMO_ASSERT(v5if);
|
||||
|
||||
LOGP(DV5BCC, LOGL_DEBUG, "Creating BCC instance, initiated by %s.\n", (source_id) ? "AN" : "LE");
|
||||
|
||||
bcc = talloc_zero(v5if, struct v52_bcc_proc);
|
||||
if (!bcc)
|
||||
return NULL;
|
||||
bcc->interface = v5if;
|
||||
|
||||
bcc->fi = osmo_fsm_inst_alloc(&v52_le_bcc_fsm, bcc, bcc, LOGL_DEBUG, NULL);
|
||||
if (!bcc->fi) {
|
||||
talloc_free(bcc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bcc->ref = (bcc_new_ref++) & 0x1fff;
|
||||
bcc->source_id = source_id;
|
||||
bcc->user_port_id = user_port_id;
|
||||
bcc->is_isdn = is_isdn;
|
||||
bcc->link_id = link_id;
|
||||
bcc->ts = ts;
|
||||
bcc->override = override;
|
||||
bcc->isdn_slot = isdn_slot;
|
||||
if (isdn_multislot) {
|
||||
/* copy multislot IE content */
|
||||
bcc->isdn_multislot = talloc_zero_size(bcc, 9);
|
||||
if (bcc->isdn_multislot) {
|
||||
bcc->isdn_multislot[0] = link_id;
|
||||
memcpy(bcc->isdn_multislot + 1, isdn_multislot, 8);
|
||||
}
|
||||
}
|
||||
if (capability) {
|
||||
bcc->use_capability = true;
|
||||
bcc->capability = *capability;
|
||||
}
|
||||
|
||||
llist_add_tail(&bcc->list, &v5if->bcc_procs);
|
||||
|
||||
return bcc;
|
||||
}
|
||||
|
||||
void v52_le_bcc_destroy(struct v52_bcc_proc *bcc)
|
||||
{
|
||||
LOGP(DV5BCC, LOGL_DEBUG, "Destroying BCC instance.\n");
|
||||
|
||||
if (bcc->fi)
|
||||
osmo_fsm_inst_free(bcc->fi);
|
||||
|
||||
llist_del(&bcc->list);
|
||||
|
||||
talloc_free(bcc);
|
||||
}
|
||||
|
||||
void v52_le_bcc_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = osmo_fsm_register(&v52_le_bcc_fsm);
|
||||
OSMO_ASSERT(!rc);
|
||||
LOGP(DV5BCC, LOGL_NOTICE, "Using V52 BCC protocol\n");
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Message receiving / decoding
|
||||
***********************************************************************/
|
||||
|
||||
/* receive message from lower (DL) layer */
|
||||
int v52_le_bcc_dl_rcv(struct v5x_interface *v5if, uint16_t l3_addr, uint8_t msg_type, const struct tlv_parsed *tp)
|
||||
{
|
||||
struct v52_bcc_proc *bcc;
|
||||
uint16_t ref;
|
||||
uint8_t source_id;
|
||||
|
||||
/* search for ref */
|
||||
ref = v52_bcc_ref_dec(l3_addr, &source_id);
|
||||
bcc = find_bcc_proto_ref(v5if, ref, source_id);
|
||||
|
||||
/* handle AN fault here, so there is no need to create a process */
|
||||
if (msg_type == V52_CTRL_MSGT_AN_FAULT) {
|
||||
LOGP(DV5BCC, LOGL_NOTICE, "Replying to AN FAULT message. No action is taken here.\n");
|
||||
/* Send AN FAULT ACK */
|
||||
v5x_dl_snd(v5if, V52_DLADDR_BCC, v52_enc_an_fault_ack(ref, source_id));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ignoring remote process (may happen due to message repetition in state 0) */
|
||||
if (!bcc)
|
||||
return 0;
|
||||
|
||||
switch (msg_type) {
|
||||
case V52_CTRL_MSGT_ALLOCATION_COMPLETE:
|
||||
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_ALLOCATION_COMPLETE, (void *)tp);
|
||||
break;
|
||||
case V52_CTRL_MSGT_ALLOCATION_REJECT:
|
||||
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_ALLOCATION_REJECT, (void *)tp);
|
||||
break;
|
||||
case V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE:
|
||||
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_DE_ALLOCATION_COMPLETE, (void *)tp);
|
||||
break;
|
||||
case V52_CTRL_MSGT_DE_ALLOCATION_REJECT:
|
||||
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_DE_ALLOCATION_REJECT, (void *)tp);
|
||||
break;
|
||||
case V52_CTRL_MSGT_AUDIT_COMPLETE:
|
||||
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_AUDIT_COMPLETE, (void *)tp);
|
||||
break;
|
||||
case V52_CTRL_MSGT_AN_FAULT:
|
||||
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_AN_FAULT, (void *)tp);
|
||||
break;
|
||||
case V52_CTRL_MSGT_PROTOCOL_ERROR:
|
||||
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_PROTOCOL_ERROR, (void *)tp);
|
||||
break;
|
||||
default:
|
||||
LOGP(DV5BCC, LOGL_NOTICE, "Invalid BCC protocol message %d receied from AN.\n", msg_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (bcc->fi->state == V52_BCCFSM_S_LEBcc0_NULL) {
|
||||
/* destroy, if NULL state is reached */
|
||||
v52_le_bcc_destroy(bcc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* receive message from upper (management) layer */
|
||||
int v52_le_bcc_mdu_snd(struct v5x_interface *v5if, uint16_t user_port_id, bool is_isdn, uint8_t link_id, uint8_t ts,
|
||||
uint8_t override, uint8_t isdn_slot, uint8_t *isdn_multislot, uint8_t *capability, enum v5x_mgmt_prim prim)
|
||||
{
|
||||
struct v52_bcc_proc *bcc;
|
||||
|
||||
/* search TS */
|
||||
if (isdn_multislot)
|
||||
bcc = find_bcc_proto_multislot(v5if, link_id, isdn_multislot);
|
||||
else
|
||||
bcc = find_bcc_proto_ts(v5if, link_id, ts);
|
||||
if (!bcc) {
|
||||
bcc = v52_le_bcc_create(v5if, user_port_id, is_isdn, link_id, ts, override, isdn_slot, isdn_multislot,
|
||||
capability, 0 /* LE */);
|
||||
if (!bcc)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
switch (prim) {
|
||||
case MDU_BCC_allocation_req:
|
||||
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_MDU_BCC_allocation_req, NULL);
|
||||
break;
|
||||
case MDU_BCC_deallocation_req:
|
||||
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_MDU_BCC_deallocation_req, NULL);
|
||||
break;
|
||||
case MDU_BCC_audit_req:
|
||||
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_MDU_BCC_audit_req, NULL);
|
||||
break;
|
||||
default:
|
||||
LOGP(DV5BCC, LOGL_NOTICE, "Invalid BCC protocol message %d receied from AN.\n", prim);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (bcc->fi->state == V52_BCCFSM_S_LEBcc0_NULL) {
|
||||
/* destroy, if NULL state is reached */
|
||||
v52_le_bcc_destroy(bcc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
void v52_le_bcc_destroy(struct v52_bcc_proc *bcc);
|
||||
void v52_le_bcc_init(void);
|
||||
int v52_le_bcc_dl_rcv(struct v5x_interface *v5if, uint16_t l3_addr, uint8_t msg_type, const struct tlv_parsed *tp);
|
||||
int v52_le_bcc_mdu_snd(struct v5x_interface *v5if, uint16_t user_port_id, bool is_isdn, uint8_t link_id, uint8_t ts,
|
||||
uint8_t override, uint8_t isdn_slot, uint8_t *isdn_multislot, uint8_t *capability, enum v5x_mgmt_prim prim);
|
|
@ -0,0 +1,941 @@
|
|||
/* ITu-T G.965 Section 16.2 V5.2-interface Link control FSM - LE side */
|
||||
|
||||
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/***********************************************************************/
|
||||
/* internal data structures */
|
||||
/***********************************************************************/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "layer1.h"
|
||||
#include "v5x_l1_fsm.h"
|
||||
#include "v5x_le_ctrl_fsm.h"
|
||||
#include "v5x_le_management.h"
|
||||
#include "v52_le_lcp_fsm.h"
|
||||
#include "logging.h"
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
/***********************************************************************/
|
||||
/* state names, event names, primitives, ... */
|
||||
/***********************************************************************/
|
||||
|
||||
/* 16.2.4.2.2 */
|
||||
enum v52_lcp_fsm_state {
|
||||
V52_LCPFSM_S_LE01_NOP_LINK_FAILURE,
|
||||
V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED,
|
||||
V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED,
|
||||
V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK,
|
||||
V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK,
|
||||
V52_LCPFSM_S_LE20_OP_OPERATIONAL,
|
||||
V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID,
|
||||
V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID,
|
||||
};
|
||||
|
||||
enum v52_lcp_fsm_event {
|
||||
V52_LCPFSM_E_MPH_AI, /* Activate Indication (L1 link operational) */
|
||||
V52_LCPFSM_E_MPH_DI, /* Deactivate Indication (L1 link not operational) */
|
||||
V52_LCPFSM_E_MDU_IDReq, /* Identification Request */
|
||||
V52_LCPFSM_E_FE_IDAck,
|
||||
V52_LCPFSM_E_MPH_IDI, /* Identification Indication */
|
||||
V52_LCPFSM_E_MPH_EIg, /* Identification Failure */
|
||||
V52_LCPFSM_E_FE_IDReq,
|
||||
V52_LCPFSM_E_MDU_IDAck, /* Send Link ID ACK */
|
||||
V52_LCPFSM_E_FE_IDRel,
|
||||
V52_LCPFSM_E_MDU_IDRej, /* Link ID Reject */
|
||||
V52_LCPFSM_E_FE_IDRej,
|
||||
V52_LCPFSM_E_MDU_LUBR, /* Link Unblock Request */
|
||||
V52_LCPFSM_E_MDU_LBI, /* Link Block Indication */
|
||||
V52_LCPFSM_E_FE302, /* AN Initiated unblocking */
|
||||
V52_LCPFSM_E_FE304, /* AN initiated link block */
|
||||
V52_LCPFSM_E_FE305, /* deferred link block request */
|
||||
V52_LCPFSM_E_FE306, /* non-deferred link block request */
|
||||
};
|
||||
|
||||
static const struct value_string v52_lcp_fsm_event_names[] = {
|
||||
{ V52_LCPFSM_E_MPH_AI, "MPH-AI" },
|
||||
{ V52_LCPFSM_E_MPH_DI, "MPH-DI" },
|
||||
{ V52_LCPFSM_E_MDU_IDReq, "MDU-IDReq" },
|
||||
{ V52_LCPFSM_E_FE_IDAck, "FE-IDAck" },
|
||||
{ V52_LCPFSM_E_MPH_IDI, "MPH-IDI" },
|
||||
{ V52_LCPFSM_E_MPH_EIg, "MPH-EIg" },
|
||||
{ V52_LCPFSM_E_FE_IDReq, "FE-IDReq" },
|
||||
{ V52_LCPFSM_E_MDU_IDAck, "MDU-IDack" },
|
||||
{ V52_LCPFSM_E_FE_IDRel, "FE-IDRel" },
|
||||
{ V52_LCPFSM_E_MDU_IDRej, "MDU-IDRej" },
|
||||
{ V52_LCPFSM_E_FE_IDRej, "FE-IDRej" },
|
||||
{ V52_LCPFSM_E_MDU_LUBR, "MDU-LUBR" },
|
||||
{ V52_LCPFSM_E_MDU_LBI, "MDU-LBI" },
|
||||
{ V52_LCPFSM_E_FE302, "FE302" },
|
||||
{ V52_LCPFSM_E_FE304, "FE304" },
|
||||
{ V52_LCPFSM_E_FE305, "FE305" },
|
||||
{ V52_LCPFSM_E_FE306, "FE306" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/***********************************************************************/
|
||||
/* Messages to other layers */
|
||||
/***********************************************************************/
|
||||
|
||||
/* send message to upper (management) layer */
|
||||
static void rcv_mdu(struct osmo_fsm_inst *fi, enum v5x_mgmt_prim prim)
|
||||
{
|
||||
struct v5x_link *v5l = fi->priv;
|
||||
|
||||
v52_le_lcp_mdu_rcv(v5l, prim);
|
||||
}
|
||||
|
||||
/* send message to lower (L1 FSM) layer */
|
||||
static void snd_mph(struct osmo_fsm_inst *fi, enum v5x_mph_prim prim)
|
||||
{
|
||||
struct v5x_link *v5l = fi->priv;
|
||||
|
||||
v5x_l1_mph_snd(v5l, prim);
|
||||
}
|
||||
|
||||
/* send message to lower (CTRL) layer */
|
||||
void snd_fe(struct osmo_fsm_inst *fi, enum v52_link_ctrl_func lcf)
|
||||
{
|
||||
struct v5x_link *v5l = fi->priv;
|
||||
|
||||
v52_le_ctrl_link_snd(v5l, lcf);
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
/* LCP state FSM */
|
||||
/***********************************************************************/
|
||||
|
||||
static void lcp_fsm_le01_link_failure(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
/* Send MDU-LAI */
|
||||
rcv_mdu(fi, MDU_LAI);
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* Send MDU-DI */
|
||||
rcv_mdu(fi, MDU_DI);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDRel:
|
||||
case V52_LCPFSM_E_FE_IDRej:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
/* Send MDU-DI */
|
||||
rcv_mdu(fi, MDU_DI);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
case V52_LCPFSM_E_FE302:
|
||||
case V52_LCPFSM_E_FE305:
|
||||
case V52_LCPFSM_E_FE306:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void lcp_fsm_le02_link_failure_blocked(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send MDU-LBI */
|
||||
rcv_mdu(fi, MDU_LBI);
|
||||
/* Send MDU-LAI */
|
||||
rcv_mdu(fi, MDU_LAI);
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* Send MDU-DI */
|
||||
rcv_mdu(fi, MDU_DI);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
/* Send MDU-DI */
|
||||
rcv_mdu(fi, MDU_DI);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
case V52_LCPFSM_E_FE302:
|
||||
case V52_LCPFSM_E_FE305:
|
||||
case V52_LCPFSM_E_FE306:
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void lcp_fsm_le10_link_blocked(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* Send MDU-LAI */
|
||||
rcv_mdu(fi, MDU_LAI);
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
/* Send MDU-DI */
|
||||
rcv_mdu(fi, MDU_DI);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* Send MDU-LBI */
|
||||
rcv_mdu(fi, MDU_LBI);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDRel:
|
||||
case V52_LCPFSM_E_FE_IDRej:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK, 0, 0);
|
||||
/* Send FE301 */
|
||||
snd_fe(fi, V52_LCP_FE_301_302_LINK_UNBLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
case V52_LCPFSM_E_FE305:
|
||||
case V52_LCPFSM_E_FE306:
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE302:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK, 0, 0);
|
||||
/* Send MDL-LUBR */
|
||||
rcv_mdu(fi, MDU_LUBR);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void lcp_fsm_le11_local_link_unblock(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
__attribute__((unused)) void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
/* Send MDU-DI */
|
||||
rcv_mdu(fi, MDU_DI);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send MDU-LBI */
|
||||
rcv_mdu(fi, MDU_LBI);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
/* Send FE-IDRej */
|
||||
snd_fe(fi, V52_LCP_FE_IDRej);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDRel:
|
||||
case V52_LCPFSM_E_FE_IDRej:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
/* Send FE301 */
|
||||
snd_fe(fi, V52_LCP_FE_301_302_LINK_UNBLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE302:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
/* Send MDU-LUBI */
|
||||
rcv_mdu(fi, MDU_LUBI);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send MDU-LBI */
|
||||
rcv_mdu(fi, MDU_LBI);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE305:
|
||||
case V52_LCPFSM_E_FE306:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
/* Send MDU-LBI */
|
||||
rcv_mdu(fi, MDU_LBI);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void lcp_fsm_le12_remote_link_unblock(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
/* Send MDU-DI */
|
||||
rcv_mdu(fi, MDU_DI);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* Send MDU-IDRej */
|
||||
rcv_mdu(fi, MDU_IDRej);
|
||||
/* Send MDU-LUBR */
|
||||
rcv_mdu(fi, MDU_LUBR);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
/* Send FE-IDRej */
|
||||
snd_fe(fi, V52_LCP_FE_IDRej);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
/* Send FE301 */
|
||||
snd_fe(fi, V52_LCP_FE_301_302_LINK_UNBLOCK);
|
||||
/* Send MDU-LUBI */
|
||||
rcv_mdu(fi, MDU_LUBI);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE302:
|
||||
/* Send MDU-LUBR */
|
||||
rcv_mdu(fi, MDU_LUBR);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send MDU-LBI */
|
||||
rcv_mdu(fi, MDU_LBI);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE305:
|
||||
case V52_LCPFSM_E_FE306:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
/* Send MDU-LBI */
|
||||
rcv_mdu(fi, MDU_LBI);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void lcp_fsm_le20_operational(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE01_NOP_LINK_FAILURE, 0, 0);
|
||||
/* Send MDU-DI */
|
||||
rcv_mdu(fi, MDU_DI);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID, 0, 0);
|
||||
/* Send FE-IDReq */
|
||||
snd_fe(fi, V52_LCP_FE_IDReq);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
/* Send MDU-IDReq */
|
||||
rcv_mdu(fi, MDU_IDReq);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDAck:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID, 0, 0);
|
||||
/* Send FE-IDAck */
|
||||
snd_fe(fi, V52_LCP_FE_IDAck);
|
||||
/* Send MPH-ID */
|
||||
snd_mph(fi, MPH_ID);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDRej:
|
||||
/* Send FE-IDRej */
|
||||
snd_fe(fi, V52_LCP_FE_IDRej);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
/* Send FE301 */
|
||||
snd_fe(fi, V52_LCP_FE_301_302_LINK_UNBLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE302:
|
||||
/* Send MDU-LUBI */
|
||||
rcv_mdu(fi, MDU_LUBI);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send MDU-LBI */
|
||||
rcv_mdu(fi, MDU_LBI);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE305:
|
||||
/* Send MDU-LBR */
|
||||
rcv_mdu(fi, MDU_LBR);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE306:
|
||||
/* Send MDU-LBRN */
|
||||
rcv_mdu(fi, MDU_LBRN);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void lcp_fsm_le21_op_remote_link_id(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE01_NOP_LINK_FAILURE, 0, 0);
|
||||
/* Send MPH-NOR */
|
||||
snd_mph(fi, MPH_NOR);
|
||||
/* Send MDU-DI */
|
||||
rcv_mdu(fi, MDU_DI);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* Send MDU-IDRej */
|
||||
rcv_mdu(fi, MDU_IDRej);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDAck:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDRel:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
/* Send MDU-IDRel */
|
||||
rcv_mdu(fi, MDU_IDRel);
|
||||
/* Send MPH-NOR */
|
||||
snd_mph(fi, MPH_NOR);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDRej:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
/* Send FE-IDRej */
|
||||
rcv_mdu(fi, MDU_IDRej);
|
||||
/* Send MPH-NOR */
|
||||
snd_mph(fi, MPH_NOR);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
/* Send FE301 */
|
||||
snd_fe(fi, V52_LCP_FE_301_302_LINK_UNBLOCK);
|
||||
/* Send MPH-NOR */
|
||||
snd_mph(fi, MPH_NOR);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
/* Send MPH-NOR */
|
||||
snd_mph(fi, MPH_NOR);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE302:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
/* Send MPH-NOR */
|
||||
snd_mph(fi, MPH_NOR);
|
||||
/* Send MDU-IDRel */
|
||||
rcv_mdu(fi, MDU_IDRel);
|
||||
/* Send MDU-LUBI */
|
||||
rcv_mdu(fi, MDU_LUBI);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send MPH-NOR */
|
||||
snd_mph(fi, MPH_NOR);
|
||||
/* Send MDU-LBI */
|
||||
rcv_mdu(fi, MDU_LBI);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE305:
|
||||
/* Send MDU-LBR */
|
||||
rcv_mdu(fi, MDU_LBR);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE306:
|
||||
/* Send MDU-LBRN */
|
||||
rcv_mdu(fi, MDU_LBRN);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void lcp_fsm_le22_op_local_link_id(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE01_NOP_LINK_FAILURE, 0, 0);
|
||||
/* Send FE-IDRel */
|
||||
snd_fe(fi, V52_LCP_FE_IDRel);
|
||||
/* Send MDU-DI */
|
||||
rcv_mdu(fi, MDU_DI);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDAck:
|
||||
/* Send MPH-IDR */
|
||||
snd_mph(fi, MPH_IDR);
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_IDI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
/* Send FE-IDRel */
|
||||
snd_fe(fi, V52_LCP_FE_IDRel);
|
||||
/* Send MDU-AI */
|
||||
rcv_mdu(fi, MDU_AI);
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_EIg:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
/* Send FE-IDRel */
|
||||
snd_fe(fi, V52_LCP_FE_IDRel);
|
||||
/* Send MDU-EIg */
|
||||
rcv_mdu(fi, MDU_EIg);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
/* Send FE-IDRej */
|
||||
snd_fe(fi, V52_LCP_FE_IDRej);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDRej:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
/* Send MDU-IDRej */
|
||||
rcv_mdu(fi, MDU_IDRej);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
/* Send FE301 */
|
||||
snd_fe(fi, V52_LCP_FE_301_302_LINK_UNBLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send FE303 */
|
||||
snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE302:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
/* Send MDU-IDRej */
|
||||
rcv_mdu(fi, MDU_IDRej);
|
||||
/* Send MDU-LUBI */
|
||||
rcv_mdu(fi, MDU_LUBI);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
/* Send MDU-LBI */
|
||||
rcv_mdu(fi, MDU_LBI);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE305:
|
||||
/* Send MDU-LBR */
|
||||
rcv_mdu(fi, MDU_LBR);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE306:
|
||||
/* Send MDU-LBRN */
|
||||
rcv_mdu(fi, MDU_LBRN);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Table 17G.965 */
|
||||
static const struct osmo_fsm_state v52_le_lcp_fsm_states[] = {
|
||||
[V52_LCPFSM_S_LE01_NOP_LINK_FAILURE] = {
|
||||
.name = "Link failure (LE0.1)",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDRel) |
|
||||
S(V52_LCPFSM_E_FE_IDRej) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) |
|
||||
S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED),
|
||||
.action = lcp_fsm_le01_link_failure,
|
||||
},
|
||||
[V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED] = {
|
||||
.name = "Link failure and blocked(LE0.2)",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
|
||||
.action = lcp_fsm_le02_link_failure_blocked,
|
||||
},
|
||||
[V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED] = {
|
||||
.name = "Link blocked (LE1.0)",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDRel) |
|
||||
S(V52_LCPFSM_E_FE_IDRej) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED) |
|
||||
S(V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK) |
|
||||
S(V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK),
|
||||
.action = lcp_fsm_le10_link_blocked,
|
||||
},
|
||||
[V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK] = {
|
||||
.name = "Local link unblock (LE1.1)",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDRel) |
|
||||
S(V52_LCPFSM_E_FE_IDRej) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED) |
|
||||
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED) |
|
||||
S(V52_LCPFSM_S_LE20_OP_OPERATIONAL),
|
||||
.action = lcp_fsm_le11_local_link_unblock,
|
||||
},
|
||||
[V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK] = {
|
||||
.name = "Remote link unblocke (LE1.2)",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED) |
|
||||
S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) |
|
||||
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
|
||||
.action = lcp_fsm_le12_remote_link_unblock,
|
||||
},
|
||||
[V52_LCPFSM_S_LE20_OP_OPERATIONAL] = {
|
||||
.name = "Link Operational (LE2.0)",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_MDU_IDAck) |
|
||||
S(V52_LCPFSM_E_MDU_IDRej) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE01_NOP_LINK_FAILURE) |
|
||||
S(V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID) |
|
||||
S(V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID) |
|
||||
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
|
||||
.action = lcp_fsm_le20_operational,
|
||||
},
|
||||
[V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID] = {
|
||||
.name = "Remote link identification (LE2.1)",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_MDU_IDAck) |
|
||||
S(V52_LCPFSM_E_FE_IDRel) |
|
||||
S(V52_LCPFSM_E_MDU_IDRej) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE01_NOP_LINK_FAILURE) |
|
||||
S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) |
|
||||
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
|
||||
.action = lcp_fsm_le21_op_remote_link_id,
|
||||
},
|
||||
[V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID] = {
|
||||
.name = "Local link identification (LE2.2)",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDAck) |
|
||||
S(V52_LCPFSM_E_MPH_IDI) |
|
||||
S(V52_LCPFSM_E_MPH_EIg) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDRej) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE01_NOP_LINK_FAILURE) |
|
||||
S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) |
|
||||
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
|
||||
.action =lcp_fsm_le22_op_local_link_id,
|
||||
},
|
||||
};
|
||||
|
||||
struct osmo_fsm v52_le_lcp_fsm = {
|
||||
.name = "V52_LE_LCP",
|
||||
.states = v52_le_lcp_fsm_states,
|
||||
.num_states = ARRAY_SIZE(v52_le_lcp_fsm_states),
|
||||
.timer_cb = NULL,
|
||||
.log_subsys = DV5LCP,
|
||||
.event_names = v52_lcp_fsm_event_names,
|
||||
};
|
||||
|
||||
struct osmo_fsm_inst *v52_le_lcp_create(void *ctx, struct v5x_link *v5l, uint8_t id)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
OSMO_ASSERT(v5l);
|
||||
|
||||
fi = osmo_fsm_inst_alloc(&v52_le_lcp_fsm, ctx, v5l, LOGL_DEBUG, NULL);
|
||||
if (!fi)
|
||||
return NULL;
|
||||
osmo_fsm_inst_update_id_f(fi, "%d", id);
|
||||
/* initial state for links that are not in failure state */
|
||||
fi->state = V52_LCPFSM_S_LE20_OP_OPERATIONAL;
|
||||
|
||||
return fi;
|
||||
}
|
||||
|
||||
void v52_le_lcp_destroy(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
if (fi)
|
||||
osmo_fsm_inst_free(fi);
|
||||
}
|
||||
|
||||
bool v52_le_lcp_is_operational(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
return (fi->state >= V52_LCPFSM_S_LE20_OP_OPERATIONAL);
|
||||
}
|
||||
|
||||
const char *v52_le_lcp_state_name(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
return v52_le_lcp_fsm_states[fi->state].name;
|
||||
}
|
||||
|
||||
void v52_le_lcp_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = osmo_fsm_register(&v52_le_lcp_fsm);
|
||||
OSMO_ASSERT(!rc);
|
||||
LOGP(DV5LCP, LOGL_NOTICE, "Using V52 link control protocol\n");
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Messages from other layers
|
||||
***********************************************************************/
|
||||
|
||||
/* receive message from lower (L1 FSM) layer */
|
||||
int v52_le_lcp_mph_rcv(struct v5x_link *v5l, enum v5x_mph_prim prim)
|
||||
{
|
||||
enum v52_lcp_fsm_event event = 9999;
|
||||
|
||||
switch (prim) {
|
||||
case MPH_AI:
|
||||
LOGP(DV5LCP, LOGL_NOTICE, "Received link activation indication from L1 (link ID %d).\n", v5l->id);
|
||||
event = V52_LCPFSM_E_MPH_AI;
|
||||
break;
|
||||
case MPH_DI:
|
||||
LOGP(DV5LCP, LOGL_NOTICE, "Received link deactivation indication from L1 (link ID %d).\n", v5l->id);
|
||||
event = V52_LCPFSM_E_MPH_DI;
|
||||
break;
|
||||
case MPH_EIa:
|
||||
LOGP(DV5LCP, LOGL_DEBUG, "Received link error indication from L1 (link ID %d): LOS\n", v5l->id);
|
||||
return 0;
|
||||
case MPH_EIb:
|
||||
LOGP(DV5LCP, LOGL_DEBUG, "Received link error indication from L1 (link ID %d): RAI\n", v5l->id);
|
||||
return 0;
|
||||
case MPH_EIc:
|
||||
LOGP(DV5LCP, LOGL_DEBUG, "Received link error indication from L1 (link ID %d): AIS\n", v5l->id);
|
||||
return 0;
|
||||
case MPH_EId:
|
||||
LOGP(DV5LCP, LOGL_DEBUG, "Received link error indication from L1 (link ID %d): Internal failure\n",
|
||||
v5l->id);
|
||||
return 0;
|
||||
case MPH_EIe:
|
||||
LOGP(DV5LCP, LOGL_DEBUG, "Received link error indication from L1 (link ID %d): CRC error\n", v5l->id);
|
||||
return 0;
|
||||
case MPH_EIf:
|
||||
LOGP(DV5LCP, LOGL_DEBUG, "Received link error indication from L1 (link ID %d): "
|
||||
"Remote CRC error indication\n", v5l->id);
|
||||
return 0;
|
||||
case MPH_IDI:
|
||||
LOGP(DV5LCP, LOGL_NOTICE, "Received link identification indication from L1 (link ID %d).\n", v5l->id);
|
||||
event = V52_LCPFSM_E_MPH_IDI;
|
||||
break;
|
||||
case MPH_EIg:
|
||||
LOGP(DV5LCP, LOGL_NOTICE, "Received link identification failure from L1 (link ID %d).\n", v5l->id);
|
||||
event = V52_LCPFSM_E_MPH_EIg;
|
||||
break;
|
||||
case MPH_EIdr:
|
||||
return 0;
|
||||
case MPH_EIbr:
|
||||
return 0;
|
||||
default:
|
||||
LOGP(DV5LCP, LOGL_NOTICE, "Invalid LCP primitive %d receied from L1/AN (link ID %d).\n", prim, v5l->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(event < 9999);
|
||||
osmo_fsm_inst_dispatch(v5l->fi, event, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* receive message from lower (CTRL) layer */
|
||||
int v52_le_lcp_fe_rcv(struct v5x_link *v5l, enum v52_link_ctrl_func lcf)
|
||||
{
|
||||
enum v52_lcp_fsm_event event;
|
||||
|
||||
switch (lcf) {
|
||||
case V52_LCP_FE_IDReq:
|
||||
event = V52_LCPFSM_E_FE_IDReq;
|
||||
break;
|
||||
case V52_LCP_FE_IDAck:
|
||||
event = V52_LCPFSM_E_FE_IDAck;
|
||||
break;
|
||||
case V52_LCP_FE_IDRel:
|
||||
event = V52_LCPFSM_E_FE_IDRel;
|
||||
break;
|
||||
case V52_LCP_FE_IDRej:
|
||||
event = V52_LCPFSM_E_FE_IDRej;
|
||||
break;
|
||||
case V52_LCP_FE_301_302_LINK_UNBLOCK:
|
||||
event = V52_LCPFSM_E_FE302;
|
||||
break;
|
||||
case V52_LCP_FE_303_304_LINK_BLOCK:
|
||||
event = V52_LCPFSM_E_FE304;
|
||||
break;
|
||||
case V52_LCP_FE_305_DEF_LINK_BLOCK_REQ:
|
||||
event = V52_LCPFSM_E_FE305;
|
||||
break;
|
||||
case V52_LCP_FE_306_NON_DEF_LINK_BLOCK_REQ:
|
||||
event = V52_LCPFSM_E_FE306;
|
||||
break;
|
||||
default:
|
||||
LOGP(DV5LCP, LOGL_NOTICE, "Invalid link control function primitive %d receied from AN.\n", lcf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
osmo_fsm_inst_dispatch(v5l->fi, event, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int v52_le_lcp_mdu_snd(struct v5x_link *v5l, enum v5x_mgmt_prim prim)
|
||||
{
|
||||
enum v52_lcp_fsm_event event;
|
||||
|
||||
switch (prim) {
|
||||
case MDU_IDReq:
|
||||
event = V52_LCPFSM_E_MDU_IDReq;
|
||||
break;
|
||||
case MDU_IDAck:
|
||||
event = V52_LCPFSM_E_MDU_IDAck;
|
||||
break;
|
||||
case MDU_IDRej:
|
||||
event = V52_LCPFSM_E_MDU_IDRej;
|
||||
break;
|
||||
case MDU_LUBR:
|
||||
event = V52_LCPFSM_E_MDU_LUBR;
|
||||
break;
|
||||
case MDU_LBI:
|
||||
event = V52_LCPFSM_E_MDU_LBI;
|
||||
break;
|
||||
default:
|
||||
LOGP(DV5LCP, LOGL_NOTICE, "Invalid MDU primitive %d receied from management.\n", prim);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
osmo_fsm_inst_dispatch(v5l->fi, event, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
struct osmo_fsm_inst *v52_le_lcp_create(void *ctx, struct v5x_link *v5l, uint8_t id);
|
||||
void v52_le_lcp_destroy(struct osmo_fsm_inst *fi);
|
||||
bool v52_le_lcp_is_operational(struct osmo_fsm_inst *fi);
|
||||
const char *v52_le_lcp_state_name(struct osmo_fsm_inst *fi);
|
||||
void v52_le_lcp_init(void);
|
||||
int v52_le_lcp_mph_rcv(struct v5x_link *v5l, enum v5x_mph_prim prim);
|
||||
int v52_le_lcp_fe_rcv(struct v5x_link *v5l, enum v52_link_ctrl_func lcf);
|
||||
int v52_le_lcp_mdu_snd(struct v5x_link *v5l, enum v5x_mgmt_prim prim);
|
|
@ -0,0 +1,845 @@
|
|||
/* ITu-T G.965 Section 18 V5.2-interface Protection protocol FSM - LE side */
|
||||
|
||||
/* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/***********************************************************************/
|
||||
/* internal data structures */
|
||||
/***********************************************************************/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "v5x_le_management.h"
|
||||
#include "v52_le_pp_fsm.h"
|
||||
#include "logging.h"
|
||||
|
||||
/* Table 64/G.965 Timers in the LE */
|
||||
#define TIMEOUT_TSO1 1500
|
||||
#define TIMEOUT_TSO2 1500
|
||||
#define TIMEOUT_TSO4 20000
|
||||
#define TIMEOUT_TSO5 10000
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
/***********************************************************************/
|
||||
/* state names, event names, primitives, ... */
|
||||
/***********************************************************************/
|
||||
|
||||
/* 18.3.1.2 */
|
||||
enum v52_le_pp_fsm_state {
|
||||
V52_PPFSM_S_SOLE0_NULL,
|
||||
V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE,
|
||||
V52_PPFSM_S_SOLE2_SWITCH_OVER_REQ_BY_AN,
|
||||
};
|
||||
|
||||
enum v52_le_pp_fsm_event {
|
||||
V52_PPFSM_E_MDU_Protection_switch_over_com,
|
||||
V52_PPFSM_E_MDU_Protection_OS_switch_over_com,
|
||||
V52_PPFSM_E_MDU_Protection_switch_over_reject,
|
||||
V52_PPFSM_E_MDU_Protection_reset_SN_req,
|
||||
V52_PPFSM_E_VP_S_VP_R_misalignment_detected,
|
||||
V52_PPFSM_E_SWITCH_OVER_REQ,
|
||||
V52_PPFSM_E_SWITCH_OVER_ACK,
|
||||
V52_PPFSM_E_SWITCH_OVER_REJECT,
|
||||
V52_PPFSM_E_PROTOCOL_ERROR,
|
||||
V52_PPFSM_E_RESET_SN_COM,
|
||||
V52_PPFSM_E_RESET_SN_ACK,
|
||||
V52_PPFSM_E_TSO1,
|
||||
V52_PPFSM_E_TSO2,
|
||||
V52_PPFSM_E_TSO4,
|
||||
V52_PPFSM_E_TSO5,
|
||||
};
|
||||
|
||||
static const struct value_string v52_le_pp_fsm_event_names[] = {
|
||||
{ V52_PPFSM_E_MDU_Protection_switch_over_com, "MDU-Protection (switch_over com)" },
|
||||
{ V52_PPFSM_E_MDU_Protection_OS_switch_over_com, "MDU-Protection (OS_switch-over com)" },
|
||||
{ V52_PPFSM_E_MDU_Protection_switch_over_reject, "MDU-Protection (switch-over reject)" },
|
||||
{ V52_PPFSM_E_MDU_Protection_reset_SN_req, "MDU-Protection (reset SN req)" },
|
||||
{ V52_PPFSM_E_VP_S_VP_R_misalignment_detected, "VP(S) VP(R) misalignment detected" },
|
||||
{ V52_PPFSM_E_SWITCH_OVER_REQ, "SWITCH-OVER REQ" },
|
||||
{ V52_PPFSM_E_SWITCH_OVER_ACK, "SWITCH-OVER ACK" },
|
||||
{ V52_PPFSM_E_SWITCH_OVER_REJECT, "SWITCH-OVER REJECT" },
|
||||
{ V52_PPFSM_E_PROTOCOL_ERROR, "PROTOCOL ERROR" },
|
||||
{ V52_PPFSM_E_RESET_SN_COM, "RESET SN COM" },
|
||||
{ V52_PPFSM_E_RESET_SN_ACK, "RESET SN ACK" },
|
||||
{ V52_PPFSM_E_TSO1, "expiry TSO1" },
|
||||
{ V52_PPFSM_E_TSO2, "expiry TSO2" },
|
||||
{ V52_PPFSM_E_TSO4, "expiry TSO4" },
|
||||
{ V52_PPFSM_E_TSO5, "expiry TSO5" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/***********************************************************************/
|
||||
/* Messages to other layers */
|
||||
/***********************************************************************/
|
||||
|
||||
static inline void v52_pp_pcc_dec(uint16_t ie, uint8_t *link_id, uint8_t *ts);
|
||||
|
||||
/* send message to upper (management) layer */
|
||||
static void mdu_rcv(struct osmo_fsm_inst *fi, enum v5x_mgmt_prim prim, const struct tlv_parsed *tp)
|
||||
{
|
||||
struct v52_pp_proto *pp = fi->priv;
|
||||
struct v5x_interface *v5if = pp->interface;
|
||||
const uint8_t *ie, *cause = NULL;
|
||||
uint8_t ie_len, link_id = 0, ts = 0, cause_len = 0;
|
||||
|
||||
if (tp) {
|
||||
if (TLVP_PRESENT(tp, V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID)) {
|
||||
ie = TLVP_VAL(tp, V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID);
|
||||
ie_len = TLVP_LEN(tp, V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID);
|
||||
if (ie_len == 2)
|
||||
v52_pp_pcc_dec(ntohs(*((uint16_t *)ie)), &link_id, &ts);
|
||||
|
||||
}
|
||||
if (TLVP_PRESENT(tp, V52_CTRL_IEI_PP_REJECTION_CAUSE)) {
|
||||
cause = TLVP_VAL(tp, V52_CTRL_IEI_PP_REJECTION_CAUSE);
|
||||
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_PP_REJECTION_CAUSE);
|
||||
}
|
||||
if (TLVP_PRESENT(tp, V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE)) {
|
||||
cause = TLVP_VAL(tp, V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE);
|
||||
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE);
|
||||
}
|
||||
}
|
||||
|
||||
v52_le_pp_mdu_rcv(v5if, prim, link_id, ts, cause, cause_len);
|
||||
}
|
||||
|
||||
/* send message to lower (DL) layer(s) */
|
||||
static void dl_send(struct osmo_fsm_inst *fi, struct msgb *msg)
|
||||
{
|
||||
struct v52_pp_proto *pp = fi->priv;
|
||||
|
||||
v5x_dl_snd(pp->interface, V52_DLADDR_PROTECTION, msg);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* V5 Message encoding / sending
|
||||
***********************************************************************/
|
||||
|
||||
static inline uint16_t v52_pp_pcc_enc(uint8_t link_id, uint8_t ts)
|
||||
{
|
||||
return (link_id << 8) | ts;
|
||||
}
|
||||
|
||||
static inline void v52_pp_pcc_dec(uint16_t ie, uint8_t *link_id, uint8_t *ts)
|
||||
{
|
||||
*link_id = ie >> 8;
|
||||
*ts = ie & 0x1f;
|
||||
}
|
||||
|
||||
/* G.965 Section 18.4.2/18.4.3 / Table 53/54 */
|
||||
static struct msgb *v52_enc_switch_over_com(struct v52_pp_proto *pp, uint16_t cc_id, uint8_t link_id, uint8_t ts,
|
||||
uint8_t msg_type)
|
||||
{
|
||||
uint8_t seq_ie = pp->vp_s | 0x80;
|
||||
uint16_t v5_cc_ie;
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(cc_id);
|
||||
l3h->msg_type = msg_type;
|
||||
|
||||
/* Sequence Number */
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_PP_SEQUENCE_NR, 1, &seq_ie);
|
||||
pp->vp_s = (pp->vp_s + 1) & 0x7f;
|
||||
/* Physical C-channel identification */
|
||||
v5_cc_ie = htons(v52_pp_pcc_enc(link_id, ts));
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID, 2, (uint8_t *)&v5_cc_ie);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.965 Section 18.4.5 / Table 56 */
|
||||
static struct msgb *v52_enc_switch_over_rej(struct v52_pp_proto *pp, uint16_t cc_id, uint8_t link_id, uint8_t ts,
|
||||
uint8_t cause)
|
||||
{
|
||||
uint8_t seq_ie = pp->vp_s | 0x80;
|
||||
uint16_t v5_cc_ie;
|
||||
uint8_t cause_ie = cause | 0x80;
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(cc_id);
|
||||
l3h->msg_type = V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT;
|
||||
|
||||
/* Sequence Number */
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_PP_SEQUENCE_NR, 1, &seq_ie);
|
||||
pp->vp_s = (pp->vp_s + 1) & 0x7f;
|
||||
/* Physical C-channel identification */
|
||||
v5_cc_ie = htons(v52_pp_pcc_enc(link_id, ts));
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID, 2, (uint8_t *)&v5_cc_ie);
|
||||
/* Rejection Cause */
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_PP_REJECTION_CAUSE, 1, &cause_ie);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.965 Section 18.4.6 / Table 57 */
|
||||
static struct msgb *v52_enc_protocol_error(struct v52_pp_proto *pp, uint16_t cc_id, uint8_t cause, uint8_t *diagnostic,
|
||||
int diagnostic_len)
|
||||
{
|
||||
uint8_t seq_ie = pp->vp_s | 0x80;
|
||||
uint8_t cause_ie[3] = { cause | 0x80 };
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(cc_id);
|
||||
l3h->msg_type = V52_CTRL_MSGT_PP_PROTOCOL_ERROR;
|
||||
|
||||
/* Sequence Number */
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_PP_SEQUENCE_NR, 1, &seq_ie);
|
||||
pp->vp_s = (pp->vp_s + 1) & 0x7f;
|
||||
/* Protocol Error Cause */
|
||||
if (diagnostic && diagnostic_len)
|
||||
memcpy(cause_ie + 1, diagnostic, diagnostic_len);
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE, 1 + diagnostic_len, (uint8_t *)&cause_ie);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.965 Section 18.4.7/18.4.8 / Table 58/59 */
|
||||
static struct msgb *v52_enc_reset_sn_xxx(uint16_t cc_id, uint8_t msg_type)
|
||||
{
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(cc_id);
|
||||
l3h->msg_type = msg_type;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
/* Protection Protocol state FSM */
|
||||
/***********************************************************************/
|
||||
|
||||
static void stop_timer(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
LOGP(DV5PP, LOGL_DEBUG, "Stop all timers\n");
|
||||
osmo_timer_del(&fi->timer);
|
||||
}
|
||||
|
||||
static void start_timer(struct osmo_fsm_inst *fi, enum v52_le_pp_fsm_event event, int count, struct v52_pp_mgmt_info *info)
|
||||
{
|
||||
struct v52_pp_proto *pp = fi->priv;
|
||||
int timeout = 0;
|
||||
const char *timer_name = NULL;
|
||||
|
||||
switch (event) {
|
||||
case V52_PPFSM_E_TSO1:
|
||||
timer_name = "TSO1";
|
||||
timeout = TIMEOUT_TSO1;
|
||||
fi->T = 1;
|
||||
break;
|
||||
case V52_PPFSM_E_TSO2:
|
||||
timer_name = "TSO2";
|
||||
timeout = TIMEOUT_TSO2;
|
||||
fi->T = 2;
|
||||
break;
|
||||
case V52_PPFSM_E_TSO4:
|
||||
timer_name = "TSO4";
|
||||
timeout = TIMEOUT_TSO4;
|
||||
fi->T = 3;
|
||||
break;
|
||||
case V52_PPFSM_E_TSO5:
|
||||
timer_name = "TSO5";
|
||||
timeout = TIMEOUT_TSO5;
|
||||
fi->T = 4;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
if (info)
|
||||
memcpy(&pp->info, info, sizeof(*info));
|
||||
|
||||
LOGP(DV5PP, LOGL_DEBUG, "Start timer %s (count = %d)\n", timer_name, count);
|
||||
pp->timeout_event = event;
|
||||
pp->timeout_count = count;
|
||||
osmo_timer_schedule(&fi->timer, timeout / 1000, timeout % 1000);
|
||||
}
|
||||
|
||||
static bool timer_pending(struct osmo_fsm_inst *fi, enum v52_le_pp_fsm_event event)
|
||||
{
|
||||
struct v52_pp_proto *pp = fi->priv;
|
||||
|
||||
if (pp->timeout_event != (int)event)
|
||||
return false;
|
||||
|
||||
return osmo_timer_pending(&fi->timer);
|
||||
}
|
||||
|
||||
static int v52_le_pp_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct v52_pp_proto *pp = fi->priv;
|
||||
|
||||
osmo_fsm_inst_dispatch(fi, pp->timeout_event, &pp->info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void pp_sole0_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct v52_pp_proto *pp = fi->priv;
|
||||
struct v52_pp_mgmt_info *info = data;
|
||||
|
||||
switch (event) {
|
||||
case V52_PPFSM_E_MDU_Protection_switch_over_com:
|
||||
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
|
||||
/* change state to SOLE1 */
|
||||
osmo_fsm_inst_state_chg_ms(fi, V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE, 0, 0);
|
||||
/* start TSO1 */
|
||||
start_timer(fi, V52_PPFSM_E_TSO1, 1, info);
|
||||
/* send SWITCH-OVER COM */
|
||||
dl_send(fi, v52_enc_switch_over_com(pp, pp->interface->protection.cc_id, info->link_id, info->ts, V52_CTRL_MSGT_PP_SWITCH_OVER_COM));
|
||||
} else {
|
||||
/* send MDU-Prot. (reset SN error ind) */
|
||||
mdu_rcv(fi, MDU_Protection_reset_SN_error_ind, NULL);
|
||||
}
|
||||
break;
|
||||
case V52_PPFSM_E_MDU_Protection_OS_switch_over_com:
|
||||
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
|
||||
/* change state to SOLE1 */
|
||||
osmo_fsm_inst_state_chg_ms(fi, V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE, 0, 0);
|
||||
/* start TSO2 */
|
||||
start_timer(fi, V52_PPFSM_E_TSO2, 1, info);
|
||||
/* send OS SWITCH-OVER COM */
|
||||
dl_send(fi, v52_enc_switch_over_com(pp, pp->interface->protection.cc_id, info->link_id, info->ts, V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM));
|
||||
} else {
|
||||
/* send MDU-Prot. (reset SN error ind) */
|
||||
mdu_rcv(fi, MDU_Protection_reset_SN_error_ind, NULL);
|
||||
}
|
||||
break;
|
||||
case V52_PPFSM_E_SWITCH_OVER_ACK:
|
||||
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
|
||||
/* send MDU-Prot. (switch-over ack) */
|
||||
mdu_rcv(fi, MDU_Protection_switch_over_ack, data);
|
||||
} else {
|
||||
/* ignore */
|
||||
}
|
||||
break;
|
||||
case V52_PPFSM_E_SWITCH_OVER_REQ:
|
||||
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
|
||||
/* change state to SOLE2 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE2_SWITCH_OVER_REQ_BY_AN, 0, 0);
|
||||
/* send MDU-Prot. (switch-over req) */
|
||||
mdu_rcv(fi, MDU_Protection_switch_over_req, data);
|
||||
} else {
|
||||
/* ignore */
|
||||
}
|
||||
break;
|
||||
case V52_PPFSM_E_SWITCH_OVER_REJECT:
|
||||
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
|
||||
/* send MDU-Prot. (switch-over reject ind) */
|
||||
mdu_rcv(fi, MDU_Protection_switch_over_reject_ind, data);
|
||||
} else {
|
||||
/* ignore */
|
||||
}
|
||||
break;
|
||||
case V52_PPFSM_E_VP_S_VP_R_misalignment_detected:
|
||||
case V52_PPFSM_E_MDU_Protection_reset_SN_req:
|
||||
/* start TSO4 */
|
||||
start_timer(fi, V52_PPFSM_E_TSO4, 1, NULL);
|
||||
/* send RESET SN COM */
|
||||
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_COM));
|
||||
/* set VP(S) = VP(R) = 0 */
|
||||
pp->vp_s = pp->vp_r = 0;
|
||||
/* send MDU-Prot. (reset SN com) */
|
||||
mdu_rcv(fi, MDU_Protection_reset_SN_com, NULL);
|
||||
break;
|
||||
case V52_PPFSM_E_RESET_SN_COM:
|
||||
if (!timer_pending(fi, V52_PPFSM_E_TSO5)) {
|
||||
/* start TSO5 */
|
||||
start_timer(fi, V52_PPFSM_E_TSO5, 1, NULL);
|
||||
/* set VP(S) = VP(R) = 0 */
|
||||
pp->vp_s = pp->vp_r = 0;
|
||||
/* send RESET SN ACK */
|
||||
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_ACK));
|
||||
/* send MDU-Prot. (reset SN ind) */
|
||||
mdu_rcv(fi, MDU_Protection_reset_SN_ind, NULL);
|
||||
} else {
|
||||
/* ignore */
|
||||
}
|
||||
break;
|
||||
case V52_PPFSM_E_RESET_SN_ACK:
|
||||
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
|
||||
/* ignore */
|
||||
} else {
|
||||
/* stop TSO4 */
|
||||
stop_timer(fi);
|
||||
/* send MDU-Prot. (reset SN ack) */
|
||||
mdu_rcv(fi, MDU_Protection_reset_SN_ack, NULL);
|
||||
}
|
||||
break;
|
||||
case V52_PPFSM_E_TSO4:
|
||||
if (pp->timeout_count < 2) {
|
||||
/* restart TSO4 */
|
||||
start_timer(fi, V52_PPFSM_E_TSO4, 2, NULL);
|
||||
/* send RESET SN COM */
|
||||
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_COM));
|
||||
/* set VP(S) = VP(R) = 0 */
|
||||
pp->vp_s = pp->vp_r = 0;
|
||||
/* send MDU-Prot. (reset SN com) */
|
||||
mdu_rcv(fi, MDU_Protection_reset_SN_com, NULL);
|
||||
} else {
|
||||
/* send MDU-Prot. (reset SN error ind) */
|
||||
mdu_rcv(fi, MDU_Protection_reset_SN_error_ind, NULL);
|
||||
}
|
||||
break;
|
||||
case V52_PPFSM_E_TSO5:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_PPFSM_E_PROTOCOL_ERROR:
|
||||
if (!timer_pending(fi, V52_PPFSM_E_TSO4)) {
|
||||
/* send MDU-Prot. (Protocol error ind) */
|
||||
mdu_rcv(fi, MDU_Protection_protocol_error_ind, data);
|
||||
} else {
|
||||
/* ignore */
|
||||
}
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void pp_sole1_switch_over_init_by_le(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct v52_pp_proto *pp = fi->priv;
|
||||
struct v52_pp_mgmt_info *info = data;
|
||||
|
||||
switch (event) {
|
||||
case V52_PPFSM_E_SWITCH_OVER_ACK:
|
||||
/* stop timer */
|
||||
stop_timer(fi);
|
||||
/* change state to SOLE0, stop timer */
|
||||
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
|
||||
/* send MDU-Prot. (switch-over ack) */
|
||||
mdu_rcv(fi, MDU_Protection_switch_over_ack, data);
|
||||
break;
|
||||
case V52_PPFSM_E_SWITCH_OVER_REQ:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_PPFSM_E_SWITCH_OVER_REJECT:
|
||||
/* stop timer */
|
||||
stop_timer(fi);
|
||||
/* change state to SOLE0 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
|
||||
/* send MDU-Prot. (switch-over reject ind) */
|
||||
mdu_rcv(fi, MDU_Protection_switch_over_reject_ind, data);
|
||||
break;
|
||||
case V52_PPFSM_E_TSO1:
|
||||
/* first timeout ? */
|
||||
if (pp->timeout_count < 2) {
|
||||
/* restart TSO1 */
|
||||
start_timer(fi, V52_PPFSM_E_TSO1, 2, info);
|
||||
/* send SWITCH-OVER COM */
|
||||
dl_send(fi, v52_enc_switch_over_com(pp, pp->interface->protection.cc_id, info->link_id, info->ts, V52_CTRL_MSGT_PP_SWITCH_OVER_COM));
|
||||
} else {
|
||||
/* change state to SOLE0 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
|
||||
/* send MDU-Prot. (switch-over error ind) */
|
||||
mdu_rcv(fi, MDU_Protection_switch_over_error_ind, NULL);
|
||||
}
|
||||
break;
|
||||
case V52_PPFSM_E_TSO2:
|
||||
if (pp->timeout_count < 2) {
|
||||
/* restart TSO2 */
|
||||
start_timer(fi, V52_PPFSM_E_TSO2, 2, info);
|
||||
/* send OS SWITCH-OVER COM */
|
||||
dl_send(fi, v52_enc_switch_over_com(pp, pp->interface->protection.cc_id, info->link_id, info->ts, V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM));
|
||||
} else {
|
||||
/* change state to SOLE0 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
|
||||
/* send MDU-Prot. (switch-over error ind) */
|
||||
mdu_rcv(fi, MDU_Protection_switch_over_error_ind, NULL);
|
||||
}
|
||||
break;
|
||||
case V52_PPFSM_E_VP_S_VP_R_misalignment_detected:
|
||||
case V52_PPFSM_E_MDU_Protection_reset_SN_req:
|
||||
/* stop timer */
|
||||
stop_timer(fi);
|
||||
/* change state to SOLE0, stop timer */
|
||||
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 4);
|
||||
/* start TSO4 */
|
||||
start_timer(fi, V52_PPFSM_E_TSO4, 1, NULL);
|
||||
/* send RESET SN COM */
|
||||
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_COM));
|
||||
/* set VP(S) = VP(R) = 0 */
|
||||
pp->vp_s = pp->vp_r = 0;
|
||||
/* send MDU-Prot. (reset SN com) */
|
||||
mdu_rcv(fi, MDU_Protection_reset_SN_com, NULL);
|
||||
break;
|
||||
case V52_PPFSM_E_RESET_SN_COM:
|
||||
/* first timeout ? */
|
||||
if (pp->timeout_count < 2) {
|
||||
/* stop timer */
|
||||
stop_timer(fi);
|
||||
/* change state to SOLE0 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
|
||||
/* start TSO5 */
|
||||
start_timer(fi, V52_PPFSM_E_TSO5, 1, NULL);
|
||||
/* set VP(S) = VP(R) = 0 */
|
||||
pp->vp_s = pp->vp_r = 0;
|
||||
/* send RESET SN ACK */
|
||||
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_ACK));
|
||||
/* send MDU-Prot. (reset SN ind) */
|
||||
mdu_rcv(fi, MDU_Protection_reset_SN_ind, NULL);
|
||||
} else {
|
||||
/* ignore */
|
||||
}
|
||||
break;
|
||||
case V52_PPFSM_E_RESET_SN_ACK:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_PPFSM_E_TSO5:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_PPFSM_E_PROTOCOL_ERROR:
|
||||
/* send MDU-Prot. (Protocol error ind) */
|
||||
mdu_rcv(fi, MDU_Protection_protocol_error_ind, data);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void pp_sole2_switch_over_req_by_an(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct v52_pp_proto *pp = fi->priv;
|
||||
struct v52_pp_mgmt_info *info = data;
|
||||
|
||||
switch (event) {
|
||||
case V52_PPFSM_E_MDU_Protection_switch_over_com:
|
||||
/* change state to SOLE1 */
|
||||
osmo_fsm_inst_state_chg_ms(fi, V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE, 0, 0);
|
||||
/* start TSO1 */
|
||||
start_timer(fi, V52_PPFSM_E_TSO1, 1, info);
|
||||
/* send SWITCH-OVER COM */
|
||||
dl_send(fi, v52_enc_switch_over_com(pp, pp->interface->protection.cc_id, info->link_id, info->ts, V52_CTRL_MSGT_PP_SWITCH_OVER_COM));
|
||||
break;
|
||||
case V52_PPFSM_E_MDU_Protection_OS_switch_over_com:
|
||||
/* change state to SOLE1 */
|
||||
osmo_fsm_inst_state_chg_ms(fi, V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE, 0, 0);
|
||||
/* start TSO2 */
|
||||
start_timer(fi, V52_PPFSM_E_TSO2, 1, info);
|
||||
/* send OS SWITCH-OVER COM */
|
||||
dl_send(fi, v52_enc_switch_over_com(pp, pp->interface->protection.cc_id, info->link_id, info->ts, V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM));
|
||||
break;
|
||||
case V52_PPFSM_E_MDU_Protection_switch_over_reject:
|
||||
/* change state to SOLE0 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
|
||||
/* send OS SWITCH-OVER REJ */
|
||||
dl_send(fi, v52_enc_switch_over_rej(pp, pp->interface->protection.cc_id, info->link_id, info->ts, info->cause));
|
||||
break;
|
||||
case V52_PPFSM_E_VP_S_VP_R_misalignment_detected:
|
||||
case V52_PPFSM_E_MDU_Protection_reset_SN_req:
|
||||
/* change state to SOLE0 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, TIMEOUT_TSO4, 0);
|
||||
/* start TSO4 */
|
||||
start_timer(fi, V52_PPFSM_E_TSO4, 1, NULL);
|
||||
/* send RESET SN COM */
|
||||
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_COM));
|
||||
/* set VP(S) = VP(R) = 0 */
|
||||
pp->vp_s = pp->vp_r = 0;
|
||||
/* send MDU-Prot. (reset SN com) */
|
||||
mdu_rcv(fi, MDU_Protection_reset_SN_com, NULL);
|
||||
break;
|
||||
case V52_PPFSM_E_RESET_SN_COM:
|
||||
/* first timeout ? */
|
||||
if (pp->timeout_count < 2) {
|
||||
/* change state to SOLE0, start TSO5 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_PPFSM_S_SOLE0_NULL, 0, 0);
|
||||
/* start TSO5 */
|
||||
start_timer(fi, V52_PPFSM_E_TSO5, 1, NULL);
|
||||
/* set VP(S) = VP(R) = 0 */
|
||||
pp->vp_s = pp->vp_r = 0;
|
||||
/* send RESET SN ACK */
|
||||
dl_send(fi, v52_enc_reset_sn_xxx(pp->interface->protection.cc_id, V52_CTRL_MSGT_PP_RESET_SN_ACK));
|
||||
/* send MDU-Prot. (reset SN ind) */
|
||||
mdu_rcv(fi, MDU_Protection_reset_SN_ind, NULL);
|
||||
} else {
|
||||
/* ignore */
|
||||
}
|
||||
break;
|
||||
case V52_PPFSM_E_RESET_SN_ACK:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_PPFSM_E_TSO5:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_PPFSM_E_PROTOCOL_ERROR:
|
||||
/* send MDU-Prot. (Protocol error ind) */
|
||||
mdu_rcv(fi, MDU_Protection_protocol_error_ind, data);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Table 66/G.965 LE protection protocol FSM */
|
||||
static const struct osmo_fsm_state v52_le_pp_fsm_states[] = {
|
||||
[V52_PPFSM_S_SOLE0_NULL] = {
|
||||
.name = "NULL (SOLE0)",
|
||||
.in_event_mask = S(V52_PPFSM_E_MDU_Protection_switch_over_com) |
|
||||
S(V52_PPFSM_E_MDU_Protection_OS_switch_over_com) |
|
||||
S(V52_PPFSM_E_SWITCH_OVER_ACK) |
|
||||
S(V52_PPFSM_E_SWITCH_OVER_REQ) |
|
||||
S(V52_PPFSM_E_SWITCH_OVER_REJECT) |
|
||||
S(V52_PPFSM_E_VP_S_VP_R_misalignment_detected) |
|
||||
S(V52_PPFSM_E_MDU_Protection_reset_SN_req) |
|
||||
S(V52_PPFSM_E_RESET_SN_COM) |
|
||||
S(V52_PPFSM_E_RESET_SN_ACK) |
|
||||
S(V52_PPFSM_E_TSO4) |
|
||||
S(V52_PPFSM_E_TSO5) |
|
||||
S(V52_PPFSM_E_PROTOCOL_ERROR),
|
||||
.out_state_mask = S(V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE) |
|
||||
S(V52_PPFSM_S_SOLE2_SWITCH_OVER_REQ_BY_AN),
|
||||
.action = pp_sole0_null,
|
||||
},
|
||||
[V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE] = {
|
||||
.name = "Switch over init by LE (SOLE1)",
|
||||
.in_event_mask = S(V52_PPFSM_E_SWITCH_OVER_ACK) |
|
||||
S(V52_PPFSM_E_SWITCH_OVER_REQ) |
|
||||
S(V52_PPFSM_E_SWITCH_OVER_REJECT) |
|
||||
S(V52_PPFSM_E_TSO1) |
|
||||
S(V52_PPFSM_E_TSO2) |
|
||||
S(V52_PPFSM_E_VP_S_VP_R_misalignment_detected) |
|
||||
S(V52_PPFSM_E_MDU_Protection_reset_SN_req) |
|
||||
S(V52_PPFSM_E_RESET_SN_COM) |
|
||||
S(V52_PPFSM_E_RESET_SN_ACK) |
|
||||
S(V52_PPFSM_E_TSO5) |
|
||||
S(V52_PPFSM_E_PROTOCOL_ERROR),
|
||||
.out_state_mask = S(V52_PPFSM_S_SOLE0_NULL),
|
||||
.action = pp_sole1_switch_over_init_by_le,
|
||||
},
|
||||
[V52_PPFSM_S_SOLE2_SWITCH_OVER_REQ_BY_AN] = {
|
||||
.name = "Switch over req by AN (SOLE2)",
|
||||
.in_event_mask = S(V52_PPFSM_E_MDU_Protection_switch_over_com) |
|
||||
S(V52_PPFSM_E_MDU_Protection_OS_switch_over_com) |
|
||||
S(V52_PPFSM_E_MDU_Protection_switch_over_reject) |
|
||||
S(V52_PPFSM_E_VP_S_VP_R_misalignment_detected) |
|
||||
S(V52_PPFSM_E_MDU_Protection_reset_SN_req) |
|
||||
S(V52_PPFSM_E_RESET_SN_COM) |
|
||||
S(V52_PPFSM_E_RESET_SN_ACK) |
|
||||
S(V52_PPFSM_E_TSO5) |
|
||||
S(V52_PPFSM_E_PROTOCOL_ERROR),
|
||||
.out_state_mask = S(V52_PPFSM_S_SOLE0_NULL) |
|
||||
S(V52_PPFSM_S_SOLE1_SWITCH_OVER_INIT_BY_LE),
|
||||
.action = pp_sole2_switch_over_req_by_an,
|
||||
},
|
||||
};
|
||||
|
||||
struct osmo_fsm v52_le_pp_fsm = {
|
||||
.name = "V52_LE_PP",
|
||||
.states = v52_le_pp_fsm_states,
|
||||
.num_states = ARRAY_SIZE(v52_le_pp_fsm_states),
|
||||
.allstate_event_mask = 0,
|
||||
.allstate_action = NULL,
|
||||
.cleanup = NULL,
|
||||
.timer_cb = v52_le_pp_fsm_timer_cb,
|
||||
.log_subsys = DV5PP,
|
||||
.event_names = v52_le_pp_fsm_event_names,
|
||||
};
|
||||
|
||||
struct v52_pp_proto *v52_le_pp_create(struct v5x_interface *v5if)
|
||||
{
|
||||
struct v52_pp_proto *pp;
|
||||
|
||||
OSMO_ASSERT(v5if);
|
||||
|
||||
pp = talloc_zero(v5if, struct v52_pp_proto);
|
||||
if (!pp)
|
||||
return NULL;
|
||||
pp->interface = v5if;
|
||||
|
||||
pp->fi = osmo_fsm_inst_alloc(&v52_le_pp_fsm, pp, pp, LOGL_DEBUG, NULL);
|
||||
if (!pp->fi)
|
||||
goto error;
|
||||
|
||||
return pp;
|
||||
|
||||
error:
|
||||
v52_le_pp_destroy(pp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void v52_le_pp_destroy(struct v52_pp_proto *pp)
|
||||
{
|
||||
OSMO_ASSERT(pp);
|
||||
|
||||
if (pp->fi) {
|
||||
osmo_fsm_inst_free(pp->fi);
|
||||
}
|
||||
|
||||
talloc_free(pp);
|
||||
}
|
||||
|
||||
void v52_le_pp_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = osmo_fsm_register(&v52_le_pp_fsm);
|
||||
OSMO_ASSERT(!rc);
|
||||
LOGP(DV5PP, LOGL_NOTICE, "Using V52 Protection protocol\n");
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* V5 Message receiving / decoding
|
||||
***********************************************************************/
|
||||
|
||||
/* receive message from lower (DL) layer */
|
||||
int v52_le_pp_dl_rcv(struct v5x_link *v5l, uint16_t cc_id, uint8_t msg_type, const struct tlv_parsed *tp)
|
||||
{
|
||||
struct v52_pp_proto *pp = v5l->interface->protection.pp;
|
||||
enum v52_le_pp_fsm_event event;
|
||||
int check_sn = 0;
|
||||
|
||||
if (!pp) {
|
||||
LOGP(DV5PP, LOGL_NOTICE, "Received message from DL, but there is no protection on this interface.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch (msg_type) {
|
||||
case V52_CTRL_MSGT_PP_SWITCH_OVER_REQ:
|
||||
event = V52_PPFSM_E_SWITCH_OVER_REQ;
|
||||
check_sn = 1;
|
||||
break;
|
||||
case V52_CTRL_MSGT_PP_SWITCH_OVER_ACK:
|
||||
event = V52_PPFSM_E_SWITCH_OVER_ACK;
|
||||
check_sn = 1;
|
||||
break;
|
||||
case V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT:
|
||||
event = V52_PPFSM_E_SWITCH_OVER_REJECT;
|
||||
check_sn = 1;
|
||||
break;
|
||||
case V52_CTRL_MSGT_PP_PROTOCOL_ERROR:
|
||||
event = V52_PPFSM_E_PROTOCOL_ERROR;
|
||||
check_sn = 1;
|
||||
break;
|
||||
case V52_CTRL_MSGT_PP_RESET_SN_COM:
|
||||
event = V52_PPFSM_E_RESET_SN_COM;
|
||||
break;
|
||||
case V52_CTRL_MSGT_PP_RESET_SN_ACK:
|
||||
event = V52_PPFSM_E_RESET_SN_ACK;
|
||||
break;
|
||||
default:
|
||||
LOGP(DV5PP, LOGL_NOTICE, "Invalid Protection protocol message %d receied from AN.\n", msg_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cc_id != pp->interface->protection.cc_id) {
|
||||
LOGP(DV5PP, LOGL_NOTICE, "Protection protocol of AN uses CC-ID %d, but we use CC-ID %d. Please check provisioning.\n", cc_id, pp->interface->protection.cc_id);
|
||||
dl_send(pp->fi, v52_enc_protocol_error(pp, pp->interface->protection.cc_id, V5X_CAUSE_T_REF_NR_CODING_ERROR, NULL, 0));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* check sequence number (see 18.6.2.2 G.965) */
|
||||
/* NOTE: If TSO4 is running, there is an ongoing sequence reset, so no check is performed and all received
|
||||
* message with sequence number are ignored by state machine. */
|
||||
if (check_sn && !timer_pending(pp->fi, V52_PPFSM_E_TSO4)) {
|
||||
uint8_t seq_nr = *TLVP_VAL(tp, V52_CTRL_IEI_PP_SEQUENCE_NR) & 0x7f, diff;
|
||||
|
||||
/* if we have our first message, we take the serial number */
|
||||
if (!pp->vp_r_set) {
|
||||
pp->vp_r = seq_nr;
|
||||
pp->vp_r_set = true;
|
||||
}
|
||||
|
||||
/* how much is the SN ahead of what we expect */
|
||||
diff = (seq_nr - pp->vp_r) & 0x7f;
|
||||
/* if VP(R)-5 <= SN <= VP(R)-1 (SN is 1..5 values lower than we want) */
|
||||
if (diff >= 128 - 5) {
|
||||
LOGP(DV5PP, LOGL_DEBUG, "Receiveds message with sequence %d, while we expect %d. Ignore, because it was already received earlier.\n", seq_nr, pp->vp_r);
|
||||
return 0;
|
||||
}
|
||||
/* if VP(R) <= SN <= VP(R) + 4 (SN is 0..4 higher than we want) */
|
||||
if (diff <= 4) {
|
||||
/* set received sequence number to the next one expected and deliver message */
|
||||
pp->vp_r = (seq_nr + 1) & 0x7f;
|
||||
} else {
|
||||
LOGP(DV5PP, LOGL_NOTICE, "Receiveds message with sequence %d, while we expect %d. This a a misalignment, perform an SN reset!\n", seq_nr, pp->vp_r);
|
||||
event = V52_PPFSM_E_VP_S_VP_R_misalignment_detected;
|
||||
}
|
||||
}
|
||||
|
||||
osmo_fsm_inst_dispatch(pp->fi, event, (void *)tp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
int v52_le_pp_mdu_snd(struct v5x_interface *v5if, enum v5x_mgmt_prim prim, uint8_t link_id, uint8_t ts, uint8_t cause)
|
||||
{
|
||||
enum v52_le_pp_fsm_event event;
|
||||
struct v52_pp_mgmt_info info = { link_id, ts, cause };
|
||||
|
||||
if (!v5if->protection.pp) {
|
||||
LOGP(DV5PP, LOGL_NOTICE, "Cannot perform action, because there is no protection on this interface.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
OSMO_ASSERT(v5if->protection.pp);
|
||||
|
||||
switch (prim) {
|
||||
case MDU_Protection_switch_over_com:
|
||||
event = V52_PPFSM_E_MDU_Protection_switch_over_com;
|
||||
break;
|
||||
case MDU_Protection_OS_switch_over_com:
|
||||
event = V52_PPFSM_E_MDU_Protection_OS_switch_over_com;
|
||||
break;
|
||||
case MDU_Protection_switch_over_rej:
|
||||
event = V52_PPFSM_E_MDU_Protection_switch_over_reject;
|
||||
break;
|
||||
case MDU_Protection_reset_SN_req:
|
||||
event = V52_PPFSM_E_MDU_Protection_reset_SN_req;
|
||||
break;
|
||||
default:
|
||||
LOGP(DV5PP, LOGL_NOTICE, "Invalid MDU primitive %d receied from management.\n", prim);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
osmo_fsm_inst_dispatch(v5if->protection.pp->fi, event, &info);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
struct v52_pp_proto *v52_le_pp_create(struct v5x_interface *v5if);
|
||||
void v52_le_pp_destroy(struct v52_pp_proto *pp);
|
||||
void v52_le_pp_init(void);
|
||||
int v52_le_pp_dl_rcv(struct v5x_link *v5l, uint16_t cc_id, uint8_t msg_type, const struct tlv_parsed *tp);
|
||||
int v52_le_pp_mdu_snd(struct v5x_interface *v5if, enum v5x_mgmt_prim prim, uint8_t link_id, uint8_t ts, uint8_t cause);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
|
||||
extern struct vty_app_info vty_info;
|
||||
int v5le_vty_init(void);
|
|
@ -0,0 +1,546 @@
|
|||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "lapv5.h"
|
||||
#include "layer1.h"
|
||||
#include "v5x_l1_fsm.h"
|
||||
#include "v5x_le_ctrl_fsm.h"
|
||||
#include "v5x_le_port_fsm.h"
|
||||
#include "v5x_le_pstn_fsm.h"
|
||||
#include "v52_le_lcp_fsm.h"
|
||||
#include "v52_le_bcc_fsm.h"
|
||||
#include "v52_le_pp_fsm.h"
|
||||
#include "v5x_le_management.h"
|
||||
#include "logging.h"
|
||||
|
||||
static LLIST_HEAD(v5_instances);
|
||||
|
||||
struct v5x_instance *v5x_instance_alloc(void *ctx)
|
||||
{
|
||||
struct v5x_instance *v5i = talloc_zero(ctx, struct v5x_instance);
|
||||
if (!v5i)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&v5i->interfaces);
|
||||
|
||||
llist_add_tail(&v5i->list, &v5_instances);
|
||||
|
||||
return v5i;
|
||||
}
|
||||
|
||||
void v5x_instance_free(struct v5x_instance *v5i)
|
||||
{
|
||||
struct v5x_interface *v5if, *v5if2;
|
||||
|
||||
llist_for_each_entry_safe(v5if, v5if2, &v5i->interfaces, list)
|
||||
v5x_interface_free(v5if);
|
||||
|
||||
talloc_free(v5i);
|
||||
}
|
||||
|
||||
static void v5x_ts_init(struct v5x_timeslot *v5ts, uint8_t nr, struct v5x_link *v5l, bool b_channel)
|
||||
{
|
||||
/* Never use slot 16 as B-channel, because some drivers (mISDN) will not allow this. */
|
||||
if (nr == 16)
|
||||
b_channel = false;
|
||||
|
||||
v5ts->nr = nr;
|
||||
v5ts->link = v5l;
|
||||
v5ts->b_channel = b_channel;
|
||||
}
|
||||
|
||||
static void v5x_cchan_init(struct v5x_c_channel *v5cc, struct v5x_link *v5l, struct v5x_timeslot *v5ts, bool active)
|
||||
{
|
||||
v5cc->link = v5l;
|
||||
v5cc->ts = v5ts;
|
||||
v5cc->active = active;
|
||||
}
|
||||
|
||||
struct v5x_link *v5x_link_create(struct v5x_interface *v5if, uint8_t id)
|
||||
{
|
||||
int count;
|
||||
struct v5x_link *v5l;
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
if (v5x_link_find_id(v5if, id)) {
|
||||
LOGP(DV5, LOGL_ERROR, "Link %d already exists.\n", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
count = v5x_link_count(v5if);
|
||||
|
||||
v5l = talloc_zero(v5if, struct v5x_link);
|
||||
if (!v5l)
|
||||
return NULL;
|
||||
|
||||
llist_add_tail(&v5l->list, &v5if->links);
|
||||
count++;
|
||||
|
||||
v5l->id = id;
|
||||
v5l->interface = v5if;
|
||||
|
||||
/* primary and secondary link will get TS 16 */
|
||||
if (count <= 2) {
|
||||
v5x_cchan_init(&v5l->c_channel[0], v5l, &v5l->ts[16], true);
|
||||
}
|
||||
|
||||
for (i = 1; i < ARRAY_SIZE(v5l->ts); i++) {
|
||||
bool b_channel = true;
|
||||
if (v5l->c_channel[0].ts && v5l->c_channel[0].ts->nr == i)
|
||||
b_channel = false;
|
||||
if (v5l->c_channel[1].ts && v5l->c_channel[1].ts->nr == i)
|
||||
b_channel = false;
|
||||
if (v5l->c_channel[2].ts && v5l->c_channel[2].ts->nr == i)
|
||||
b_channel = false;
|
||||
v5x_ts_init(&v5l->ts[i], i, v5l, b_channel);
|
||||
}
|
||||
|
||||
v5l->l1 = v5x_l1_fsm_create(v5if, v5l, id);
|
||||
if (!v5l->l1)
|
||||
goto error;
|
||||
|
||||
if (v5if->dialect == V5X_DIALECT_V52) {
|
||||
v5l->fi = v52_le_lcp_create(v5if, v5l, id);
|
||||
if (!v5l->fi)
|
||||
goto error;
|
||||
|
||||
v5l->ctrl = v5x_le_ctrl_create(V5X_CTRL_TYPE_LINK, v5if, v5l, id);
|
||||
if (!v5l->ctrl)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (count == 1) {
|
||||
v5if->primary_link = v5l;
|
||||
v5if->cc_link = v5l;
|
||||
}
|
||||
if (count == 2) {
|
||||
v5if->secondary_link = v5l;
|
||||
/* add PP */
|
||||
v5if->protection.pp = v52_le_pp_create(v5if);
|
||||
if (!v5if->protection.pp)
|
||||
goto error;
|
||||
v5if->protection.li[0] = lapv5_instance_alloc(1, &ph_data_req_dl_prot, v5if->primary_link, v5x_dl_rcv,
|
||||
v5if->primary_link, &lapd_profile_lapv5dl, "protection0");
|
||||
if (!v5if->protection.li[0])
|
||||
goto error;
|
||||
v5if->protection.li[1] = lapv5_instance_alloc(1, &ph_data_req_dl_prot, v5if->secondary_link, v5x_dl_rcv,
|
||||
v5if->secondary_link, &lapd_profile_lapv5dl, "protection1");
|
||||
if (!v5if->protection.li[1])
|
||||
goto error;
|
||||
}
|
||||
|
||||
return v5l;
|
||||
|
||||
error:
|
||||
rc = v5x_link_destroy(v5l);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int v5x_link_destroy(struct v5x_link *v5l)
|
||||
{
|
||||
struct v5x_interface *v5if = v5l->interface;
|
||||
int count;
|
||||
struct v5x_user_port *v5up;
|
||||
int i;
|
||||
|
||||
count = v5x_link_count(v5l->interface);
|
||||
if (count > 1 && v5l->interface->primary_link == v5l) {
|
||||
LOGP(DV5, LOGL_ERROR, "Link %d is primary. Cannot remove it, while other links exist.\n", v5l->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (count > 2 && v5l->interface->secondary_link == v5l) {
|
||||
LOGP(DV5, LOGL_ERROR, "Link %d is secondary (standby). Cannot remove it, while other (not primary) links exist.\n", v5l->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* detach user ports */
|
||||
for (i = 1; i < (int)ARRAY_SIZE(v5l->ts); i++) {
|
||||
if ((v5up = v5l->ts[i].v5up)) {
|
||||
if (v5up->ts[0] == &v5l->ts[i]) {
|
||||
v5up->ts[0] = NULL;
|
||||
ph_deactivate_req(&v5l->ts[i]);
|
||||
}
|
||||
if (v5up->ts[1] == &v5l->ts[i]) {
|
||||
v5up->ts[1] = NULL;
|
||||
ph_deactivate_req(&v5l->ts[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (v5l->ctrl)
|
||||
v5x_le_ctrl_destroy(v5l->ctrl);
|
||||
|
||||
if (v5l->fi)
|
||||
v52_le_lcp_destroy(v5l->fi);
|
||||
|
||||
if (v5l->l1)
|
||||
v5x_l1_fsm_destroy(v5l->l1);
|
||||
|
||||
if (v5l->interface->primary_link == v5l)
|
||||
v5l->interface->primary_link = NULL;
|
||||
if (v5l->interface->secondary_link == v5l)
|
||||
v5l->interface->secondary_link = NULL;
|
||||
if (v5l->interface->cc_link == v5l)
|
||||
v5l->interface->cc_link = NULL;
|
||||
|
||||
if (v5l->e1_line)
|
||||
e1_line_exit(v5l);
|
||||
|
||||
llist_del(&v5l->list);
|
||||
count--;
|
||||
|
||||
talloc_free(v5l);
|
||||
|
||||
/* if we have only one link left */
|
||||
if (count == 1) {
|
||||
/* remove PP */
|
||||
if (v5if->protection.li[0]) {
|
||||
lapv5_instance_free(v5if->protection.li[0]);
|
||||
v5if->protection.li[0] = NULL;
|
||||
}
|
||||
if (v5if->protection.li[1]) {
|
||||
lapv5_instance_free(v5if->protection.li[1]);
|
||||
v5if->protection.li[1] = NULL;
|
||||
}
|
||||
if (v5if->protection.pp) {
|
||||
v52_le_pp_destroy(v5if->protection.pp);
|
||||
v5if->protection.pp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect)
|
||||
{
|
||||
struct v5x_interface *v5if;
|
||||
struct v5x_link *v5l;
|
||||
// struct v5x_c_channel *primary_c_chan;
|
||||
|
||||
v5if = talloc_zero(v5i, struct v5x_interface);
|
||||
if (!v5if)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&v5if->links);
|
||||
INIT_LLIST_HEAD(&v5if->user_ports);
|
||||
INIT_LLIST_HEAD(&v5if->bcc_procs);
|
||||
|
||||
llist_add_tail(&v5if->list, &v5i->interfaces);
|
||||
|
||||
v5if->instance = v5i;
|
||||
v5if->dialect = dialect;
|
||||
|
||||
if (v5if->dialect == V5X_DIALECT_V51) {
|
||||
v5l = v5x_link_create(v5if, 0);
|
||||
if (!v5l)
|
||||
goto error;
|
||||
}
|
||||
|
||||
v5if->mgmt = v5x_le_mgmt_create(v5if);
|
||||
if (!v5if->mgmt)
|
||||
goto error;
|
||||
|
||||
v5if->control.ctrl = v5x_le_ctrl_create(V5X_CTRL_TYPE_COMMON, v5if, v5if, 0);
|
||||
if (!v5if->control.ctrl)
|
||||
goto error;
|
||||
v5if->control.li = lapv5_instance_alloc(1, &ph_data_req_dl_cc, v5if, v5x_dl_rcv, v5if,
|
||||
&lapd_profile_lapv5dl, "control");
|
||||
if (!v5if->control.li)
|
||||
goto error;
|
||||
|
||||
v5if->pstn.li = lapv5_instance_alloc(1, &ph_data_req_dl_cc, v5if, v5x_dl_rcv, v5if,
|
||||
&lapd_profile_lapv5dl, "pstn");
|
||||
if (!v5if->pstn.li)
|
||||
goto error;
|
||||
|
||||
if (v5if->dialect == V5X_DIALECT_V52) {
|
||||
v5if->lcp.li = lapv5_instance_alloc(1, &ph_data_req_dl_cc, v5if, v5x_dl_rcv, v5if,
|
||||
&lapd_profile_lapv5dl, "lcp");
|
||||
if (!v5if->lcp.li)
|
||||
goto error;
|
||||
|
||||
v5if->bcc.li = lapv5_instance_alloc(1, &ph_data_req_dl_cc, v5if, v5x_dl_rcv, v5if,
|
||||
&lapd_profile_lapv5dl, "bcc");
|
||||
if (!v5if->bcc.li)
|
||||
goto error;
|
||||
}
|
||||
|
||||
return v5if;
|
||||
|
||||
error:
|
||||
v5x_interface_free(v5if);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void v5x_interface_free(struct v5x_interface *v5if) {
|
||||
struct v5x_user_port *v5up, *v5up2;
|
||||
struct v52_bcc_proc *bcc, *bcc2;
|
||||
struct v5x_link *v5l;
|
||||
int rc;
|
||||
|
||||
if (!v5if)
|
||||
return;
|
||||
|
||||
llist_for_each_entry_safe(v5up, v5up2, &v5if->user_ports, list)
|
||||
v5x_user_port_destroy(v5up);
|
||||
|
||||
llist_for_each_entry_safe(bcc, bcc2, &v5if->bcc_procs, list)
|
||||
v52_le_bcc_destroy(bcc);
|
||||
|
||||
/* delete reversed safely */
|
||||
while (!llist_empty(&v5if->links)) {
|
||||
v5l = llist_last_entry(&v5if->links, struct v5x_link, list);
|
||||
rc = v5x_link_destroy(v5l);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
}
|
||||
|
||||
if (v5if->mgmt)
|
||||
v5x_le_mgmt_destroy(v5if->mgmt);
|
||||
|
||||
if (v5if->control.ctrl)
|
||||
v5x_le_ctrl_destroy(v5if->control.ctrl);
|
||||
if (v5if->control.li)
|
||||
lapv5_instance_free(v5if->control.li);
|
||||
|
||||
if (v5if->pstn.li)
|
||||
lapv5_instance_free(v5if->pstn.li);
|
||||
|
||||
if (v5if->lcp.li)
|
||||
lapv5_instance_free(v5if->lcp.li);
|
||||
|
||||
if (v5if->bcc.li)
|
||||
lapv5_instance_free(v5if->bcc.li);
|
||||
|
||||
llist_del(&v5if->list);
|
||||
|
||||
talloc_free(v5if);
|
||||
}
|
||||
|
||||
struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t nr, enum v5x_user_type type,
|
||||
uint8_t ts1, uint8_t ts2)
|
||||
{
|
||||
struct v5x_user_port *v5up;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
if (v5x_user_port_find(v5if, nr, (type == V5X_USER_TYPE_ISDN))) {
|
||||
LOGP(DV5, LOGL_ERROR, "User port %d already exists.\n", nr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (v5if->dialect == V5X_DIALECT_V51) {
|
||||
struct v5x_link *v5l;
|
||||
|
||||
v5l = llist_first_entry(&v5if->links, struct v5x_link, list);
|
||||
if (!v5l->ts[ts1].b_channel) {
|
||||
LOGP(DV5, LOGL_ERROR, "Time slot %d is not a B-channel, select a different one.\n", ts1);
|
||||
return NULL;
|
||||
}
|
||||
if ((v5up = v5l->ts[ts1].v5up)) {
|
||||
LOGP(DV5, LOGL_ERROR, "Time slot %d is already assigned to another user port.\n", ts1);
|
||||
return NULL;
|
||||
}
|
||||
if (type == V5X_USER_TYPE_ISDN) {
|
||||
if (!v5l->ts[ts2].b_channel) {
|
||||
LOGP(DV5, LOGL_ERROR, "Time slot %d is not a B-channel, select a different one.\n",
|
||||
ts2);
|
||||
return NULL;
|
||||
}
|
||||
if ((v5up = v5l->ts[ts2].v5up)) {
|
||||
LOGP(DV5, LOGL_ERROR, "Time slot %d is already assigned to another user port.\n", ts2);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v5up = talloc_zero(v5if, struct v5x_user_port);
|
||||
if (!v5up)
|
||||
return NULL;
|
||||
|
||||
llist_add_tail(&v5up->list, &v5if->user_ports);
|
||||
|
||||
v5up->interface = v5if;
|
||||
v5up->nr = nr;
|
||||
v5up->type = type;
|
||||
|
||||
/* assign ports */
|
||||
if (v5if->dialect == V5X_DIALECT_V51) {
|
||||
struct v5x_link *v5l;
|
||||
|
||||
v5l = llist_first_entry(&v5if->links, struct v5x_link, list);
|
||||
OSMO_ASSERT(ts1 >= 1 && ts1 <= 31);
|
||||
v5l->ts[ts1].v5up = v5up;
|
||||
v5up->ts[0] = &v5l->ts[ts1];
|
||||
if (type == V5X_USER_TYPE_ISDN) {
|
||||
OSMO_ASSERT(ts2 >= 1 && ts2 <= 31);
|
||||
v5l->ts[ts2].v5up = v5up;
|
||||
v5up->ts[1] = &v5l->ts[ts2];
|
||||
}
|
||||
}
|
||||
|
||||
v5up->ctrl = v5x_le_ctrl_create(V5X_CTRL_TYPE_PORT, v5up, v5up, nr);
|
||||
if (!v5up->ctrl) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create control protocol\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case V5X_USER_TYPE_ISDN:
|
||||
LOGP(DV5, LOGL_NOTICE, "Creating V5 ISDN user port with L3 addr %d\n", nr);
|
||||
sprintf(v5up->ifname, "isdn-%d", nr);
|
||||
OSMO_ASSERT(nr <= 8175);
|
||||
v5up->port_fi = v5x_le_port_isdn_create(v5up, nr);
|
||||
if (!v5up->port_fi) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create port control\n");
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case V5X_USER_TYPE_PSTN:
|
||||
LOGP(DV5, LOGL_NOTICE, "Creating V5 PSTN user port with L3 addr %d\n", nr);
|
||||
sprintf(v5up->ifname, "pstn-%d", nr);
|
||||
OSMO_ASSERT(nr <= 32767);
|
||||
v5up->port_fi = v5x_le_port_pstn_create(v5up, nr);
|
||||
if (!v5up->port_fi) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create port control\n");
|
||||
goto error;
|
||||
}
|
||||
v5up->pstn.proto = v5x_le_pstn_create(v5up, nr);
|
||||
if (!v5up->pstn.proto) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create PSTN protocol\n");
|
||||
goto error;
|
||||
}
|
||||
/* bring port into service */
|
||||
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_blocked);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
v5up->ep[i].port = v5up;
|
||||
/* EC needs to adapt, NLP causes better results on quick volume changes */
|
||||
v5up->ep[i].ec = echo_can_create(EC_TAPS, ECHO_CAN_USE_ADAPTION | ECHO_CAN_USE_NLP);
|
||||
if (!v5up->ep[i].ec) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create line echo canceler\n");
|
||||
goto error;
|
||||
}
|
||||
v5up->ep[i].es = echo_sup_create(v5up, 8000);
|
||||
if (!v5up->ep[i].es) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create line echo suppressor\n");
|
||||
goto error;
|
||||
}
|
||||
rc = answertone_init(&v5up->ep[i].at, 8000);
|
||||
if (rc < 0) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create answer tone detector\n");
|
||||
goto error;
|
||||
}
|
||||
if (type != V5X_USER_TYPE_ISDN)
|
||||
break;
|
||||
}
|
||||
|
||||
rc = ph_socket_init(&v5up->ph_socket, ph_socket_rx_cb, v5up, v5up->ifname, 1);
|
||||
if (rc < 0) {
|
||||
LOGP(DV5, LOGL_ERROR, "Failed to create PH-socket\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* start ctrl FSM for this port */
|
||||
if (v5if->control.established)
|
||||
v5x_le_ctrl_start(v5up->ctrl);
|
||||
|
||||
return v5up;
|
||||
|
||||
error:
|
||||
v5x_user_port_destroy(v5up);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void v5x_user_port_destroy(struct v5x_user_port *v5up)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOGP(DV5, LOGL_NOTICE, "Destroying V5 user port with L3 addr %d\n", v5up->nr);
|
||||
|
||||
/* close first, because it sends messages */
|
||||
ph_socket_exit(&v5up->ph_socket);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
/* destory line echo canceler */
|
||||
if (v5up->ep[i].ec) {
|
||||
echo_can_free(v5up->ep[i].ec);
|
||||
v5up->ep[i].ec = NULL;
|
||||
}
|
||||
/* destory line echo suppressor */
|
||||
if (v5up->ep[i].es) {
|
||||
echo_sup_free(v5up->ep[i].es);
|
||||
v5up->ep[i].es = NULL;
|
||||
}
|
||||
answertone_exit(&v5up->ep[i].at);
|
||||
|
||||
/* unassign ports */
|
||||
if (v5up->ts[i]) {
|
||||
v5up->ts[i]->v5up = NULL;
|
||||
v5up->ts[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (v5up->ctrl)
|
||||
v5x_le_ctrl_destroy(v5up->ctrl);
|
||||
|
||||
if (v5up->port_fi)
|
||||
osmo_fsm_inst_free(v5up->port_fi);
|
||||
|
||||
if (v5up->pstn.proto)
|
||||
v5x_le_pstn_destroy(v5up->pstn.proto);
|
||||
|
||||
llist_del(&v5up->list);
|
||||
|
||||
talloc_free(v5up);
|
||||
}
|
||||
|
||||
void v5x_echo_reset(struct v5x_echo_proc *ep, int enable)
|
||||
{
|
||||
ep->enabled = enable;
|
||||
answertone_reset(&ep->at);
|
||||
ep->tx_buffer_in = 0;
|
||||
ep->tx_buffer_out = 0;
|
||||
echo_can_flush(ep->ec);
|
||||
echo_sup_flush(ep->es);
|
||||
}
|
||||
|
||||
struct v5x_user_port *v5x_user_port_find(struct v5x_interface *v5if, uint16_t nr, bool is_isdn)
|
||||
{
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
llist_for_each_entry(v5up, &v5if->user_ports, list) {
|
||||
if (v5up->nr == nr && is_isdn == (v5up->type == V5X_USER_TYPE_ISDN))
|
||||
return v5up;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int v5x_link_count(struct v5x_interface *v5if)
|
||||
{
|
||||
struct v5x_link *v5l;
|
||||
int count = 0;
|
||||
|
||||
llist_for_each_entry(v5l, &v5if->links, list)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
struct v5x_link *v5x_link_find_id(struct v5x_interface *v5if, uint8_t id)
|
||||
{
|
||||
struct v5x_link *v5l;
|
||||
|
||||
llist_for_each_entry(v5l, &v5if->links, list) {
|
||||
if (v5l->id == id)
|
||||
return v5l;
|
||||
}
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,397 @@
|
|||
#pragma once
|
||||
|
||||
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/gsm/lapd_core.h>
|
||||
#include <osmocom/abis/e1_input.h>
|
||||
|
||||
#include "libecho/echo_cancel.h"
|
||||
#include "libecho/echo_suppress.h"
|
||||
#include "libecho/answertone.h"
|
||||
#include "ph_socket.h"
|
||||
|
||||
/* Table 35/G.964 */
|
||||
enum v5x_mph_prim {
|
||||
MPH_UBR, /* Unblock (req) */
|
||||
MPH_UBI, /* Unblock (ind) */
|
||||
MPH_BR, /* Block (req) */
|
||||
MPH_BI, /* Block (ind) */
|
||||
MPH_AR, /* Activate (req) */
|
||||
MPH_AI, /* Activate (ind) */
|
||||
MPH_AWI, /* Access activation by user (ind) */
|
||||
MPH_DSAI, /* DS Activated (ind) */
|
||||
MPH_DR, /* Deactivate (req) */
|
||||
MPH_DI, /* Deactivate (ind) */
|
||||
MPH_GI, /* Grading Information */
|
||||
MPH_DB, /* Block D-Channel from user port */
|
||||
MPH_DU, /* Unblock D-Channel from user port */
|
||||
MPH_ID, /* Identity Sa7=0 signal request (TX) */
|
||||
MPH_NOR, /* Normal Sa7=1 signal request (TX) */
|
||||
MPH_IDR, /* Request Sa7 signal (RX) */
|
||||
MPH_IDI, /* Indicate Sa7=0 signal (RX) */
|
||||
MPH_EIg, /* Indicate Sa7=1 singal (RX) */
|
||||
MPH_EIa, /* Indicate LOS */
|
||||
MPH_EIb, /* RAI */
|
||||
MPH_EIc, /* AIS */
|
||||
MPH_EId, /* Internal failure */
|
||||
MPH_EIe, /* CRC received */
|
||||
MPH_EIf, /* E-bit received */
|
||||
MPH_stop, /* See L1 FSM */
|
||||
MPH_proceed, /* See L1 FSM */
|
||||
MPH_EIbr, /* See L1 FSM */
|
||||
MPH_EIdr, /* See L1 FSM */
|
||||
};
|
||||
|
||||
/* Table 3/G.964 */
|
||||
enum v5x_fe_prim {
|
||||
FE_establish_req = 0x00,
|
||||
FE_establish_ack_ind = 0x03,
|
||||
FE_establish_ind = 0x01,
|
||||
FE_establish_ack_req = 0x02,
|
||||
FE_line_signal_req = 0x10,
|
||||
FE_line_signal_ind = 0x11,
|
||||
FE_protocol_param_req = 0x20,
|
||||
FE_disconnect_req = 0x30,
|
||||
FE_disconnect_compl_req = 0x32,
|
||||
FE_disconnect_compl_ind = 0x33,
|
||||
};
|
||||
|
||||
enum v5x_mgmt_prim {
|
||||
/* Table 3/G.964 */
|
||||
MDU_CTRL_port_blocked,
|
||||
MDU_CTRL_port_unblocked,
|
||||
MDU_CTRL_port_restart_req,
|
||||
MDU_CTRL_port_restart_ack,
|
||||
MDU_CTRL_port_restart_compl,
|
||||
MDU_error_ind,
|
||||
/* Table 14/G.965 */
|
||||
MDU_AI,
|
||||
MDU_DI,
|
||||
MDU_LAI,
|
||||
MDU_IDReq,
|
||||
MDU_IDAck,
|
||||
MDU_IDRel,
|
||||
MDU_IDRej,
|
||||
MDU_EIg,
|
||||
MDU_LUBR,
|
||||
MDU_LUBI,
|
||||
MDU_LBI,
|
||||
MDU_LBR,
|
||||
MDU_LBRN,
|
||||
/* Table 26/G.965 */
|
||||
MDU_BCC_allocation_req,
|
||||
MDU_BCC_allocation_conf,
|
||||
MDU_BCC_allocation_reject_ind,
|
||||
MDU_BCC_allocation_error_ind,
|
||||
MDU_BCC_deallocation_req,
|
||||
MDU_BCC_deallocation_conf,
|
||||
MDU_BCC_deallocation_reject_ind,
|
||||
MDU_BCC_deallocation_error_ind,
|
||||
MDU_BCC_audit_req,
|
||||
MDU_BCC_audit_conf,
|
||||
MDU_BCC_audit_error_ind,
|
||||
MDU_BCC_AN_fault_ind,
|
||||
MDU_BCC_protocol_error_ind,
|
||||
/* Table 50/G.965 */
|
||||
MDU_Protection_switch_over_com,
|
||||
MDU_Protection_OS_switch_over_com,
|
||||
MDU_Protection_switch_over_ack,
|
||||
MDU_Protection_switch_over_rej,
|
||||
MDU_Protection_switch_over_req,
|
||||
MDU_Protection_switch_over_reject_ind,
|
||||
MDU_Protection_switch_over_error_ind,
|
||||
MDU_Protection_reset_SN_ind,
|
||||
MDU_Protection_reset_SN_com,
|
||||
MDU_Protection_reset_SN_req,
|
||||
MDU_Protection_reset_SN_ack,
|
||||
MDU_Protection_reset_SN_error_ind,
|
||||
MDU_Protection_protocol_error_ind,
|
||||
/* Figure 11/G.964 */
|
||||
MDL_ESTABLISH_req,
|
||||
MDL_ESTABLISH_cnf,
|
||||
MDL_ESTABLISH_ind,
|
||||
MDL_RELEASE_ind,
|
||||
MDL_ERROR_ind,
|
||||
MDL_LAYER_1_FAILURE_ind,
|
||||
};
|
||||
|
||||
struct osmo_fsm_inst;
|
||||
|
||||
enum v5x_dialect {
|
||||
V5X_DIALECT_V51 = 1,
|
||||
V5X_DIALECT_V52 = 2,
|
||||
};
|
||||
|
||||
enum v5x_user_type {
|
||||
V5X_USER_TYPE_ISDN = 1,
|
||||
V5X_USER_TYPE_PSTN = 2,
|
||||
};
|
||||
|
||||
enum v5x_ctrl_type {
|
||||
V5X_CTRL_TYPE_COMMON = 1,
|
||||
V5X_CTRL_TYPE_PORT = 2,
|
||||
V5X_CTRL_TYPE_LINK = 3,
|
||||
};
|
||||
|
||||
/* forward-declarations */
|
||||
struct v5x_interface;
|
||||
struct v5x_instance;
|
||||
struct v5x_user_port;
|
||||
struct v5x_link;
|
||||
struct v5x_l1_proto;
|
||||
struct v52_pp_proto;
|
||||
|
||||
/* A C-channel is a 64k timeslot used for signalling */
|
||||
struct v5x_c_channel {
|
||||
struct v5x_link *link; /* back-pointer */
|
||||
struct v5x_timeslot *ts; /* E1 link timeslot. NULL = C-channel doesn't exist */
|
||||
bool active; /* false == standby */
|
||||
};
|
||||
|
||||
/* one physical E1 timeslot used on an E1 interface part of a V5.2 interface */
|
||||
struct v5x_timeslot {
|
||||
uint8_t nr;
|
||||
struct v5x_link *link; /* back-pointer */
|
||||
struct v5x_user_port *v5up; /* user port that this TS is assigned to */
|
||||
bool b_channel; /* channel can be used as b-channel */
|
||||
bool b_activated; /* activated (b-channel) at libosmo-abis */
|
||||
};
|
||||
|
||||
/* one physical E1 interface used within a V5.2 interface */
|
||||
struct v5x_link {
|
||||
struct llist_head list; /* interface.links */
|
||||
uint8_t id;
|
||||
struct v5x_interface *interface; /* back-pointer */
|
||||
struct v5x_timeslot ts[32]; /* 32 E1 slots; 0 not available */
|
||||
struct v5x_c_channel c_channel[3]; /* 64k signaling possible on TS16, TS15 and TS31 */
|
||||
struct v5x_l1_proto *l1; /* Layer 1 Link FSM protocol */
|
||||
struct v5x_ctrl_proto *ctrl; /* Link control protocol instance */
|
||||
struct osmo_fsm_inst *fi; /* Link Control FSM instance */
|
||||
struct e1inp_line *e1_line; /* E1 line to use or NULL if not */
|
||||
struct e1inp_line_ops e1_line_ops; /* use e1pinp_line->ops to point back to v5x_link */
|
||||
};
|
||||
|
||||
/* one V5.x interface between AN (Access Network) and LE (Local Exchange) */
|
||||
struct v5x_interface {
|
||||
struct llist_head list; /* instance.interfaces */
|
||||
struct v5x_instance *instance; /* back-pointer */
|
||||
enum v5x_dialect dialect;
|
||||
uint32_t id_local, id_remote; /* interface id */
|
||||
uint8_t variant_local, variant_remote; /* provitioning variant */
|
||||
bool id_remote_valid;
|
||||
bool variant_remote_valid;
|
||||
bool use_capability; /* use bearer transfer capability */
|
||||
uint8_t capability; /* bearer transfer capability */
|
||||
|
||||
struct v5x_mgmt_proto *mgmt; /* management prococol states */
|
||||
|
||||
struct {
|
||||
struct v5x_ctrl_proto *ctrl; /* common control protocol instance */
|
||||
struct lapv5_instance *li; /* Control data link */
|
||||
bool established; /* track if link is up or down */
|
||||
} control;
|
||||
struct {
|
||||
struct lapv5_instance *li; /* Link control data link */
|
||||
bool established; /* track if link is up or down */
|
||||
} lcp;
|
||||
struct {
|
||||
struct lapv5_instance *li; /* PSTN data link */
|
||||
bool established; /* track if link is up or down */
|
||||
} pstn;
|
||||
struct {
|
||||
struct lapv5_instance *li; /* BCC data link */
|
||||
bool established; /* track if link is up or down */
|
||||
} bcc;
|
||||
struct {
|
||||
uint16_t cc_id; /* CC-ID of protected C-channel */
|
||||
struct v52_pp_proto *pp; /* protection protocol instance */
|
||||
struct lapv5_instance *li[2]; /* Protection data link 1 + 2 */
|
||||
bool established[2]; /* track if link is up or down */
|
||||
} protection;
|
||||
|
||||
struct llist_head links; /* list of v5x_link */
|
||||
struct v5x_link *primary_link; /* primary link */
|
||||
struct v5x_link *secondary_link; /* standby link */
|
||||
struct v5x_link *cc_link; /* currently used link for signaling (primary on startup) */
|
||||
struct llist_head user_ports; /* list of v5x_user_port */
|
||||
struct llist_head bcc_procs; /* list of v52_bcc_procs */
|
||||
};
|
||||
|
||||
struct v5x_l1_proto {
|
||||
struct v5x_link *v5l; /* back pointer */
|
||||
struct osmo_fsm_inst *fi; /* L1 FSM */
|
||||
int los, rai, ais, sa7; /* RX signal states */
|
||||
};
|
||||
|
||||
struct v5x_ctrl_proto {
|
||||
enum v5x_ctrl_type type; /* type of control protocol: common/port/link */
|
||||
void *priv; /* back pointer to the user */
|
||||
struct osmo_fsm_inst *fi; /* control FSM */
|
||||
struct llist_head tx_queue; /* list of message to be transmitted */
|
||||
struct msgb *tx_msg; /* copy of unacked message, for second try */
|
||||
};
|
||||
|
||||
struct v5x_pstn_proto {
|
||||
struct v5x_user_port *v5up; /* back pointer, if port control is used */
|
||||
struct osmo_fsm_inst *fi; /* control FSM */
|
||||
uint8_t S_s, S_a, S_r; /* sequence numbers */
|
||||
struct msgb *tx_msg; /* copy of unacked message, for repitition */
|
||||
int timeout_event; /* event when timer times out */
|
||||
int timeout_count; /* how many times the timer was started/timed out */
|
||||
struct osmo_timer_list timer_Tr; /* extra timer for receive sequence ack */
|
||||
struct osmo_timer_list timer_Tt; /* extra timer for transmit sequence ack */
|
||||
};
|
||||
|
||||
/* indexes of blocking/unblocking instances */
|
||||
#define UNBLK_ALL_PSTN_ISDN 0
|
||||
#define UNBLK_ALL_PSTN 1
|
||||
#define UNBLK_ALL_ISDN 2
|
||||
#define BLK_ALL_PSTN 3
|
||||
#define BLK_ALL_ISDN 4
|
||||
|
||||
struct v5x_mgmt_proto {
|
||||
struct v5x_interface *interface; /* back-pointer to instance we're part of */
|
||||
bool auto_restart; /* restart automatically after TC8/TC9 timeout */
|
||||
bool pstn_rs_pending; /* pending restart flag */
|
||||
bool do_est; /* actively establish datalinks */
|
||||
bool do_align; /* actively perform alignment */
|
||||
bool acc_align; /* use accellerated alignment */
|
||||
struct osmo_fsm_inst *system_fi; /* system startup FSM */
|
||||
struct osmo_fsm_inst *pstn_dl_fi; /* PSTN DL startup FSM */
|
||||
struct osmo_fsm_inst *pstn_rs_fi; /* PSTN restart FSM */
|
||||
struct osmo_fsm_inst *unblk_all_fi[5]; /* unblock/block all FSM (5 instances) */
|
||||
struct osmo_timer_list timer_system; /* delayed start, so sockets can connect first */
|
||||
struct osmo_timer_list timer_tr1; /* see Table C.1/G.965 */
|
||||
struct osmo_timer_list timer_tr2;
|
||||
struct osmo_timer_list timer_tc1;
|
||||
struct osmo_timer_list timer_tc2;
|
||||
struct osmo_timer_list timer_tc3;
|
||||
struct osmo_timer_list timer_tc7;
|
||||
struct osmo_timer_list timer_tc8;
|
||||
struct osmo_timer_list timer_tc9;
|
||||
struct osmo_timer_list timer_tv1;
|
||||
};
|
||||
|
||||
#define EC_BUFFER 1024
|
||||
#define EC_TAPS 256
|
||||
|
||||
#define USE_ECHO_CANCELER 1
|
||||
#define USE_ECHO_SUPPRESSOR 2
|
||||
|
||||
struct v5x_echo_proc {
|
||||
struct v5x_user_port *port; /* back pointer to user port */
|
||||
int enabled; /* line echo canceler enabled (configured and not disabled by ANS) */
|
||||
echo_can_state_t *ec; /* echo canceler state */
|
||||
echo_sup_state_t *es; /* echo suppressor state */
|
||||
int16_t tx_buffer[EC_BUFFER]; /* buffer to store TX audio */
|
||||
int tx_buffer_in; /* next pos to write to buffer */
|
||||
int tx_buffer_out; /* next pos to read from buffer */
|
||||
struct answer_tone at; /* answer tone detector process */
|
||||
};
|
||||
|
||||
/* one user-facing port (subscriber line) */
|
||||
struct v5x_user_port {
|
||||
struct llist_head list; /* part of v5x_instance.ports */
|
||||
struct v5x_interface *interface; /* back-pointer to instance we're part of */
|
||||
|
||||
uint16_t nr; /* port-number in decoded form (0..32767) */
|
||||
char ifname[64]; /* name of interface, also used for PH-socket */
|
||||
enum v5x_user_type type; /* type of port (ISDN/PSTN) */
|
||||
|
||||
struct v5x_ctrl_proto *ctrl; /* port control protocol instance */
|
||||
struct osmo_fsm_inst *port_fi; /* port FSM */
|
||||
struct v5x_timeslot *ts[2]; /* two time slots */
|
||||
struct {
|
||||
} isdn;
|
||||
struct {
|
||||
struct v5x_pstn_proto *proto;
|
||||
} pstn;
|
||||
ph_socket_t ph_socket; /* unix socket to connect port to */
|
||||
bool le_unblocked; /* if port is not blocked by LE */
|
||||
bool an_unblocked; /* if port is not blocked by AN */
|
||||
int use_line_echo; /* line echo canceler/suppressor is configured */
|
||||
struct v5x_echo_proc ep[2]; /* echo canceler process */
|
||||
};
|
||||
|
||||
/* BCC process */
|
||||
struct v52_bcc_proc {
|
||||
struct llist_head list; /* part of v5x_instance.ports */
|
||||
struct v5x_interface *interface; /* back-pointer to instance we're part of */
|
||||
struct osmo_fsm_inst *fi; /* BCC FSM */
|
||||
uint16_t ref; /* reference of this process */
|
||||
uint8_t source_id; /* reference source */
|
||||
int timer; /* timer used */
|
||||
int expired; /* number of timeouts */
|
||||
uint16_t user_port_id; /* port ID */
|
||||
bool is_isdn; /* user port type */
|
||||
uint8_t link_id; /* link ID */
|
||||
uint8_t ts; /* TS nr */
|
||||
uint8_t override; /* override assigned channel */
|
||||
uint8_t isdn_slot; /* channel on ISDN interface */
|
||||
uint8_t *isdn_multislot; /* multiple slot assignment */
|
||||
bool use_capability; /* use bearer transfer capability */
|
||||
uint8_t capability; /* bearer transfer capability */
|
||||
};
|
||||
|
||||
struct v52_pp_mgmt_info {
|
||||
uint8_t link_id;
|
||||
uint8_t ts;
|
||||
uint8_t cause;
|
||||
};
|
||||
|
||||
/* PP process */
|
||||
struct v52_pp_proto {
|
||||
struct v5x_interface *interface; /* back pointer, if port control is used */
|
||||
struct osmo_fsm_inst *fi; /* PP FSM */
|
||||
uint8_t vp_s, vp_r; /* sequence numbers */
|
||||
bool vp_r_set; /* if we ever received a sequence number */
|
||||
struct v52_pp_mgmt_info info; /* keep management info for resend after timeout */
|
||||
int timeout_event; /* event when timer times out */
|
||||
int timeout_count; /* how many times the timer was started/timed out */
|
||||
};
|
||||
|
||||
struct v5x_instance {
|
||||
struct llist_head list; /* part of global list of instances */
|
||||
struct llist_head interfaces; /* v5x_interface.list */
|
||||
};
|
||||
|
||||
struct v5x_instance *v5x_instance_alloc(void *ctx);
|
||||
void v5x_instance_free(struct v5x_instance *v5i);
|
||||
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect);
|
||||
struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t nr, enum v5x_user_type,
|
||||
uint8_t ts1, uint8_t ts2);
|
||||
void v5x_interface_free(struct v5x_interface *v5if);
|
||||
struct v5x_user_port *v5x_user_port_find(struct v5x_interface *v5if, uint16_t nr, bool is_isdn);
|
||||
void v5x_user_port_destroy(struct v5x_user_port *v5up);
|
||||
void v5x_echo_reset(struct v5x_echo_proc *ep, int enable);
|
||||
struct v5x_link *v5x_link_create(struct v5x_interface *v5if, uint8_t id);
|
||||
int v5x_link_destroy(struct v5x_link *v5l);
|
||||
int v5x_link_count(struct v5x_interface *v5if);
|
||||
struct v5x_link *v5x_link_find_id(struct v5x_interface *v5if, uint8_t id);
|
|
@ -0,0 +1,742 @@
|
|||
/* ITU-T G.964 Section 14.3.3 V5.1-intterface Layer 1 FSM - AN and LE */
|
||||
|
||||
/* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/***********************************************************************/
|
||||
/* internal data structures */
|
||||
/***********************************************************************/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "v52_le_lcp_fsm.h"
|
||||
#include "layer1.h"
|
||||
#include "v5x_l1_fsm.h"
|
||||
#include "logging.h"
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
#define TIMEOUT 1
|
||||
|
||||
/***********************************************************************/
|
||||
/* state names, event names, primitives, ... */
|
||||
/***********************************************************************/
|
||||
|
||||
enum v5x_l1_fsm_state {
|
||||
V5X_L1FSM_S_ANLE1_NORMAL,
|
||||
V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL,
|
||||
V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL,
|
||||
V5X_L1FSM_S_ANLE4_INTERNAL_FAIL,
|
||||
V5X_L1FSM_S_ANLE51_LINK_ID_SENDING,
|
||||
V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED,
|
||||
};
|
||||
|
||||
enum v5x_l1_fsm_event {
|
||||
/* V5.x events */
|
||||
V5X_L1FSM_E_MPH_stop, /* Request to stop with error report */
|
||||
V5X_L1FSM_E_MPH_proceed,/* Request to proceed with error report */
|
||||
V5X_L1FSM_E_NORMAL, /* Normal frames, Sa7 == 1 */
|
||||
V5X_L1FSM_E_LOS, /* Loss of signal/frame */
|
||||
V5X_L1FSM_E_RAI, /* Remote alarm indication */
|
||||
V5X_L1FSM_E_AIS, /* Alarm indication signal */
|
||||
V5X_L1FSM_E_INTERNAL_F, /* Internal failure */
|
||||
V5X_L1FSM_E_INTERNAL_D, /* Internal failure disappears */
|
||||
V5X_L1FSM_E_TIMEOUT, /* Persistence check timer fired */
|
||||
/* V5.2 events */
|
||||
V5X_L1FSM_E_MPH_ID, /* Send link identification signal */
|
||||
V5X_L1FSM_E_MPH_NOR, /* Remove link identification signal */
|
||||
V5X_L1FSM_E_MPH_IDR, /* Link identification request */
|
||||
V5X_L1FSM_E_NORMAL_Sa7, /* Normal frames, Sa7 == 0 */
|
||||
};
|
||||
|
||||
static const struct value_string v5x_l1_fsm_event_names[] = {
|
||||
{ V5X_L1FSM_E_MPH_stop, "Request to stop with error report" },
|
||||
{ V5X_L1FSM_E_MPH_proceed, "Request to proceed with error report" },
|
||||
{ V5X_L1FSM_E_NORMAL, "Normal frames, Sa7 = ONE" },
|
||||
{ V5X_L1FSM_E_LOS, "Loss of signal/frame" },
|
||||
{ V5X_L1FSM_E_RAI, "Remote alarm indication" },
|
||||
{ V5X_L1FSM_E_AIS, "Alarm indication signal" },
|
||||
{ V5X_L1FSM_E_INTERNAL_F, "Internal failure" },
|
||||
{ V5X_L1FSM_E_INTERNAL_D, "Internal failure disappears" },
|
||||
{ V5X_L1FSM_E_TIMEOUT, "Expiry of persistence check timer" },
|
||||
{ V5X_L1FSM_E_MPH_ID, "Send link identification signal" },
|
||||
{ V5X_L1FSM_E_MPH_NOR, "Remove link identification signal" },
|
||||
{ V5X_L1FSM_E_MPH_IDR, "Link identification request" },
|
||||
{ V5X_L1FSM_E_NORMAL_Sa7, "Normal frames, Sa7 = ZERO" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/***********************************************************************/
|
||||
/* Messages to other layers */
|
||||
/***********************************************************************/
|
||||
|
||||
/* send message to upper (LCP) layer */
|
||||
static void mph_rcv(struct osmo_fsm_inst *fi, enum v5x_mph_prim prim)
|
||||
{
|
||||
struct v5x_l1_proto *l1 = fi->priv;
|
||||
struct v5x_link *v5l = l1->v5l;
|
||||
|
||||
if (v5l->fi)
|
||||
v52_le_lcp_mph_rcv(v5l, prim);
|
||||
// FIXME: send to other protocols too
|
||||
}
|
||||
|
||||
static void signal_snd(struct osmo_fsm_inst *fi, enum l1_signal_prim prim)
|
||||
{
|
||||
struct v5x_l1_proto *l1 = fi->priv;
|
||||
struct v5x_link *v5l = l1->v5l;
|
||||
|
||||
v5x_l1_signal_snd(v5l, prim);
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
/* L1 state FSM */
|
||||
/***********************************************************************/
|
||||
|
||||
static int v5x_l1_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_TIMEOUT, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void l1_fsm_le1_normal(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_L1FSM_E_NORMAL:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_L1FSM_E_LOS:
|
||||
/* Start timer */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, TIMEOUT, 0);
|
||||
/* send MPH-EIa */
|
||||
mph_rcv(fi, MPH_EIa);
|
||||
break;
|
||||
case V5X_L1FSM_E_RAI:
|
||||
/* Start timer */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL, TIMEOUT, 0);
|
||||
/* send MPH-EIb */
|
||||
mph_rcv(fi, MPH_EIb);
|
||||
break;
|
||||
case V5X_L1FSM_E_AIS:
|
||||
/* Start timer */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, TIMEOUT, 0);
|
||||
/* send MPH-EIc */
|
||||
mph_rcv(fi, MPH_EIc);
|
||||
break;
|
||||
case V5X_L1FSM_E_INTERNAL_F:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE4_INTERNAL_FAIL, 0, 0);
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
/* send MPH-EId */
|
||||
mph_rcv(fi, MPH_EId);
|
||||
break;
|
||||
case V5X_L1FSM_E_TIMEOUT:
|
||||
/* send MPH-AI */
|
||||
mph_rcv(fi, MPH_AI);
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_ID:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE51_LINK_ID_SENDING, 0, 0);
|
||||
/* send Sa7 = ZERO */
|
||||
signal_snd(fi, L1_SIGNAL_SA7_0);
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_NOR:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_L1FSM_E_NORMAL_Sa7:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED, 0, 0);
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_IDR:
|
||||
/* send MPH-EIg */
|
||||
mph_rcv(fi, MPH_EIg);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void l1_fsm_le2_locally_det_fail(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_L1FSM_E_NORMAL:
|
||||
/* Start timer */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE1_NORMAL, TIMEOUT, 0);
|
||||
break;
|
||||
case V5X_L1FSM_E_LOS:
|
||||
/* send MPH-EIa */
|
||||
mph_rcv(fi, MPH_EIa);
|
||||
break;
|
||||
case V5X_L1FSM_E_RAI:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL, 0, 0);
|
||||
/* send MPH-EIdr */
|
||||
mph_rcv(fi, MPH_EIdr);
|
||||
/* send MPH-EIb */
|
||||
mph_rcv(fi, MPH_EIb);
|
||||
break;
|
||||
case V5X_L1FSM_E_AIS:
|
||||
/* send MPH-EIc */
|
||||
mph_rcv(fi, MPH_EIc);
|
||||
break;
|
||||
case V5X_L1FSM_E_INTERNAL_F:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE4_INTERNAL_FAIL, 0, 0);
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
/* send MPH-EId */
|
||||
mph_rcv(fi, MPH_EId);
|
||||
break;
|
||||
case V5X_L1FSM_E_TIMEOUT:
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_ID:
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_NOR:
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
break;
|
||||
case V5X_L1FSM_E_NORMAL_Sa7:
|
||||
/* Start timer */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED, TIMEOUT, 0);
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_IDR:
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void l1_fsm_le3_remotely_det_fail(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_L1FSM_E_NORMAL:
|
||||
/* Start timer */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE1_NORMAL, TIMEOUT, 0);
|
||||
break;
|
||||
case V5X_L1FSM_E_LOS:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, 0, 0);
|
||||
/* send MPH-EIa */
|
||||
mph_rcv(fi, MPH_EIa);
|
||||
/* send MPH-EIbr */
|
||||
mph_rcv(fi, MPH_EIbr);
|
||||
break;
|
||||
case V5X_L1FSM_E_RAI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_L1FSM_E_AIS:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, 0, 0);
|
||||
/* send MPH-EIc */
|
||||
mph_rcv(fi, MPH_EIc);
|
||||
/* send MPH-EIbr */
|
||||
mph_rcv(fi, MPH_EIbr);
|
||||
break;
|
||||
case V5X_L1FSM_E_INTERNAL_F:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE4_INTERNAL_FAIL, 0, 0);
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
/* send MPH-EId */
|
||||
mph_rcv(fi, MPH_EId);
|
||||
break;
|
||||
case V5X_L1FSM_E_TIMEOUT:
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_ID:
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_NOR:
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
break;
|
||||
case V5X_L1FSM_E_NORMAL_Sa7:
|
||||
/* Start timer */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED, TIMEOUT, 0);
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_IDR:
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void l1_fsm_le4_internal_fail(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
struct v5x_link *v5l = fi->priv;
|
||||
struct v5x_l1_proto *l1 = v5l->l1;
|
||||
|
||||
switch (event) {
|
||||
case V5X_L1FSM_E_NORMAL:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_L1FSM_E_LOS:
|
||||
/* send MPH-EIa */
|
||||
mph_rcv(fi, MPH_EIa);
|
||||
break;
|
||||
case V5X_L1FSM_E_RAI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_L1FSM_E_AIS:
|
||||
/* send MPH-EIc */
|
||||
mph_rcv(fi, MPH_EIc);
|
||||
break;
|
||||
case V5X_L1FSM_E_INTERNAL_F:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_L1FSM_E_INTERNAL_D:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL, 0, 0);
|
||||
/* send MPH-EIbr */
|
||||
mph_rcv(fi, MPH_EIbr);
|
||||
/* Go on to normal state, when no alarm persists.
|
||||
* We must do this, because there is no frame reception to trigger this.
|
||||
*/
|
||||
if (!l1->los && !l1->rai && !l1->ais) {
|
||||
if (l1->sa7 == 0)
|
||||
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_NORMAL_Sa7, NULL);
|
||||
else
|
||||
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_NORMAL, NULL);
|
||||
}
|
||||
break;
|
||||
case V5X_L1FSM_E_TIMEOUT:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_ID:
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_NOR:
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
break;
|
||||
case V5X_L1FSM_E_NORMAL_Sa7:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_IDR:
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void l1_fsm_le51_link_id_sending(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_L1FSM_E_NORMAL:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_L1FSM_E_LOS:
|
||||
/* Start timer */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, TIMEOUT, 0);
|
||||
/* send Sa7 = ONE */
|
||||
signal_snd(fi, L1_SIGNAL_SA7_1);
|
||||
/* send MPH-EIa */
|
||||
mph_rcv(fi, MPH_EIa);
|
||||
break;
|
||||
case V5X_L1FSM_E_RAI:
|
||||
/* Start timer */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL, TIMEOUT, 0);
|
||||
/* send Sa7 = ONE */
|
||||
signal_snd(fi, L1_SIGNAL_SA7_1);
|
||||
/* send MPH-EIb */
|
||||
mph_rcv(fi, MPH_EIb);
|
||||
break;
|
||||
case V5X_L1FSM_E_AIS:
|
||||
/* Start timer */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, TIMEOUT, 0);
|
||||
/* send Sa7 = ONE */
|
||||
signal_snd(fi, L1_SIGNAL_SA7_1);
|
||||
/* send MPH-EIc */
|
||||
mph_rcv(fi, MPH_EIc);
|
||||
break;
|
||||
case V5X_L1FSM_E_INTERNAL_F:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE4_INTERNAL_FAIL, 0, 0);
|
||||
/* send Sa7 = ONE */
|
||||
signal_snd(fi, L1_SIGNAL_SA7_1);
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
/* send MPH-EId */
|
||||
mph_rcv(fi, MPH_EId);
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_ID:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_NOR:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE1_NORMAL, 0, 0);
|
||||
/* send Sa7 = ONE */
|
||||
signal_snd(fi, L1_SIGNAL_SA7_1);
|
||||
break;
|
||||
case V5X_L1FSM_E_NORMAL_Sa7:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void l1_fsm_le52_link_id_received(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_L1FSM_E_NORMAL:
|
||||
break;
|
||||
case V5X_L1FSM_E_LOS:
|
||||
/* Start timer */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, TIMEOUT, 0);
|
||||
/* send MPH-EIa */
|
||||
mph_rcv(fi, MPH_EIa);
|
||||
break;
|
||||
case V5X_L1FSM_E_RAI:
|
||||
/* Start timer */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL, TIMEOUT, 0);
|
||||
/* send MPH-EIb */
|
||||
mph_rcv(fi, MPH_EIb);
|
||||
break;
|
||||
case V5X_L1FSM_E_AIS:
|
||||
/* Start timer */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, TIMEOUT, 0);
|
||||
/* send MPH-EIc */
|
||||
mph_rcv(fi, MPH_EIc);
|
||||
break;
|
||||
case V5X_L1FSM_E_INTERNAL_F:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE4_INTERNAL_FAIL, 0, 0);
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI);
|
||||
/* send MPH-EId */
|
||||
mph_rcv(fi, MPH_EId);
|
||||
break;
|
||||
case V5X_L1FSM_E_TIMEOUT:
|
||||
/* send MPH-DI */
|
||||
mph_rcv(fi, MPH_AI);
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_ID:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE51_LINK_ID_SENDING, 0, 0);
|
||||
/* send Sa7 = ZERO */
|
||||
signal_snd(fi, L1_SIGNAL_SA7_0);
|
||||
break;
|
||||
case V5X_L1FSM_E_NORMAL_Sa7:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_L1FSM_E_MPH_IDR:
|
||||
/* send MPH-IDI */
|
||||
mph_rcv(fi, MPH_IDI);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Table 12/G.965 */
|
||||
static const struct osmo_fsm_state v5x_l1_fsm_states[] = {
|
||||
[V5X_L1FSM_S_ANLE1_NORMAL] = {
|
||||
.name = "AN/LE1 Normal",
|
||||
.in_event_mask = S(V5X_L1FSM_E_NORMAL) |
|
||||
S(V5X_L1FSM_E_LOS) |
|
||||
S(V5X_L1FSM_E_RAI) |
|
||||
S(V5X_L1FSM_E_AIS) |
|
||||
S(V5X_L1FSM_E_INTERNAL_F) |
|
||||
S(V5X_L1FSM_E_TIMEOUT) |
|
||||
S(V5X_L1FSM_E_MPH_ID) |
|
||||
S(V5X_L1FSM_E_MPH_NOR) |
|
||||
S(V5X_L1FSM_E_NORMAL_Sa7) |
|
||||
S(V5X_L1FSM_E_MPH_IDR),
|
||||
.out_state_mask = S(V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL) |
|
||||
S(V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL) |
|
||||
S(V5X_L1FSM_S_ANLE4_INTERNAL_FAIL) |
|
||||
S(V5X_L1FSM_S_ANLE51_LINK_ID_SENDING) |
|
||||
S(V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED),
|
||||
.action = l1_fsm_le1_normal,
|
||||
},
|
||||
[V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL] = {
|
||||
.name = "AN/LE2 Locally detected failure",
|
||||
.in_event_mask = S(V5X_L1FSM_E_NORMAL) |
|
||||
S(V5X_L1FSM_E_LOS) |
|
||||
S(V5X_L1FSM_E_RAI) |
|
||||
S(V5X_L1FSM_E_AIS) |
|
||||
S(V5X_L1FSM_E_INTERNAL_F) |
|
||||
S(V5X_L1FSM_E_TIMEOUT) |
|
||||
S(V5X_L1FSM_E_MPH_ID) |
|
||||
S(V5X_L1FSM_E_MPH_NOR) |
|
||||
S(V5X_L1FSM_E_NORMAL_Sa7) |
|
||||
S(V5X_L1FSM_E_MPH_IDR),
|
||||
.out_state_mask = S(V5X_L1FSM_S_ANLE1_NORMAL) |
|
||||
S(V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL) |
|
||||
S(V5X_L1FSM_S_ANLE4_INTERNAL_FAIL) |
|
||||
S(V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED),
|
||||
.action = l1_fsm_le2_locally_det_fail,
|
||||
},
|
||||
[V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL] = {
|
||||
.name = "AN/LE3 Remotely detected failure",
|
||||
.in_event_mask = S(V5X_L1FSM_E_NORMAL) |
|
||||
S(V5X_L1FSM_E_LOS) |
|
||||
S(V5X_L1FSM_E_RAI) |
|
||||
S(V5X_L1FSM_E_AIS) |
|
||||
S(V5X_L1FSM_E_INTERNAL_F) |
|
||||
S(V5X_L1FSM_E_TIMEOUT) |
|
||||
S(V5X_L1FSM_E_MPH_ID) |
|
||||
S(V5X_L1FSM_E_MPH_NOR) |
|
||||
S(V5X_L1FSM_E_NORMAL_Sa7) |
|
||||
S(V5X_L1FSM_E_MPH_IDR),
|
||||
.out_state_mask = S(V5X_L1FSM_S_ANLE1_NORMAL) |
|
||||
S(V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL) |
|
||||
S(V5X_L1FSM_S_ANLE4_INTERNAL_FAIL) |
|
||||
S(V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED),
|
||||
.action = l1_fsm_le3_remotely_det_fail,
|
||||
},
|
||||
[V5X_L1FSM_S_ANLE4_INTERNAL_FAIL] = {
|
||||
.name = "AN/LE4 Internal failure",
|
||||
.in_event_mask = S(V5X_L1FSM_E_NORMAL) |
|
||||
S(V5X_L1FSM_E_LOS) |
|
||||
S(V5X_L1FSM_E_RAI) |
|
||||
S(V5X_L1FSM_E_AIS) |
|
||||
S(V5X_L1FSM_E_INTERNAL_F) |
|
||||
S(V5X_L1FSM_E_INTERNAL_D) |
|
||||
S(V5X_L1FSM_E_TIMEOUT) |
|
||||
S(V5X_L1FSM_E_MPH_ID) |
|
||||
S(V5X_L1FSM_E_MPH_NOR) |
|
||||
S(V5X_L1FSM_E_NORMAL_Sa7) |
|
||||
S(V5X_L1FSM_E_MPH_IDR),
|
||||
.out_state_mask = S(V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL),
|
||||
.action = l1_fsm_le4_internal_fail,
|
||||
},
|
||||
[V5X_L1FSM_S_ANLE51_LINK_ID_SENDING] = {
|
||||
.name = "AN/LE5.1 Link ID sending",
|
||||
.in_event_mask = S(V5X_L1FSM_E_NORMAL) |
|
||||
S(V5X_L1FSM_E_LOS) |
|
||||
S(V5X_L1FSM_E_RAI) |
|
||||
S(V5X_L1FSM_E_AIS) |
|
||||
S(V5X_L1FSM_E_INTERNAL_F) |
|
||||
S(V5X_L1FSM_E_MPH_ID) |
|
||||
S(V5X_L1FSM_E_MPH_NOR) |
|
||||
S(V5X_L1FSM_E_NORMAL_Sa7),
|
||||
.out_state_mask = S(V5X_L1FSM_S_ANLE1_NORMAL) |
|
||||
S(V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL) |
|
||||
S(V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL) |
|
||||
S(V5X_L1FSM_S_ANLE4_INTERNAL_FAIL),
|
||||
.action = l1_fsm_le51_link_id_sending,
|
||||
},
|
||||
[V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED] = {
|
||||
.name = "AN/LE5.2 Link ID received",
|
||||
.in_event_mask = S(V5X_L1FSM_E_NORMAL) |
|
||||
S(V5X_L1FSM_E_LOS) |
|
||||
S(V5X_L1FSM_E_RAI) |
|
||||
S(V5X_L1FSM_E_AIS) |
|
||||
S(V5X_L1FSM_E_INTERNAL_F) |
|
||||
S(V5X_L1FSM_E_TIMEOUT) |
|
||||
S(V5X_L1FSM_E_MPH_ID) |
|
||||
S(V5X_L1FSM_E_NORMAL_Sa7) |
|
||||
S(V5X_L1FSM_E_MPH_IDR),
|
||||
.out_state_mask = S(V5X_L1FSM_S_ANLE1_NORMAL) |
|
||||
S(V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL) |
|
||||
S(V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL) |
|
||||
S(V5X_L1FSM_S_ANLE4_INTERNAL_FAIL) |
|
||||
S(V5X_L1FSM_S_ANLE51_LINK_ID_SENDING),
|
||||
.action = l1_fsm_le52_link_id_received,
|
||||
},
|
||||
};
|
||||
|
||||
struct osmo_fsm v5x_l1_fsm = {
|
||||
.name = "V5X_L1",
|
||||
.states = v5x_l1_fsm_states,
|
||||
.num_states = ARRAY_SIZE(v5x_l1_fsm_states),
|
||||
.timer_cb = v5x_l1_fsm_timer_cb,
|
||||
.log_subsys = DV5L1,
|
||||
.event_names = v5x_l1_fsm_event_names,
|
||||
};
|
||||
|
||||
struct v5x_l1_proto *v5x_l1_fsm_create(void *ctx, struct v5x_link *v5l, uint8_t id)
|
||||
{
|
||||
struct v5x_l1_proto *l1;
|
||||
|
||||
OSMO_ASSERT(v5l);
|
||||
|
||||
l1 = talloc_zero(ctx, struct v5x_l1_proto);
|
||||
if (!l1)
|
||||
return NULL;
|
||||
l1->v5l = v5l;
|
||||
l1->sa7 = -1;
|
||||
|
||||
l1->fi = osmo_fsm_inst_alloc(&v5x_l1_fsm, l1, l1, LOGL_DEBUG, NULL);
|
||||
if (!l1->fi)
|
||||
return NULL;
|
||||
osmo_fsm_inst_update_id_f(l1->fi, "%d", id);
|
||||
|
||||
return l1;
|
||||
}
|
||||
|
||||
void v5x_l1_fsm_destroy(struct v5x_l1_proto *l1)
|
||||
{
|
||||
if (l1->fi) {
|
||||
osmo_fsm_inst_free(l1->fi);
|
||||
}
|
||||
|
||||
talloc_free(l1);
|
||||
}
|
||||
|
||||
const char *v5x_l1_fsm_state_name(struct v5x_l1_proto *l1)
|
||||
{
|
||||
return v5x_l1_fsm_states[l1->fi->state].name;
|
||||
}
|
||||
|
||||
void v5x_l1_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = osmo_fsm_register(&v5x_l1_fsm);
|
||||
OSMO_ASSERT(!rc);
|
||||
LOGP(DV5L1, LOGL_NOTICE, "Using V5x L1 protocol\n");
|
||||
}
|
||||
|
||||
bool v5x_l1_is_up(struct v5x_l1_proto *l1)
|
||||
{
|
||||
return l1->fi->state == V5X_L1FSM_S_ANLE1_NORMAL
|
||||
|| l1->fi->state == V5X_L1FSM_S_ANLE51_LINK_ID_SENDING
|
||||
|| l1->fi->state == V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Messages from other layers
|
||||
***********************************************************************/
|
||||
|
||||
/* receive primitive from physical E1 layer */
|
||||
int v5x_l1_signal_rcv(struct v5x_link *v5l, enum l1_signal_prim prim)
|
||||
{
|
||||
struct v5x_l1_proto *l1 = v5l->l1;
|
||||
struct osmo_fsm_inst *fi = l1->fi;
|
||||
int state_changed = 0;
|
||||
|
||||
switch (prim) {
|
||||
case L1_SIGNAL_LOS:
|
||||
LOGP(DV5L1, LOGL_DEBUG, "Signal LOS detected on link %d.\n", v5l->id);
|
||||
if (!l1->los) {
|
||||
l1->los = 1;
|
||||
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_LOS, NULL);
|
||||
}
|
||||
break;
|
||||
case L1_SIGNAL_NO_LOS:
|
||||
LOGP(DV5L1, LOGL_DEBUG, "Signal LOS gone on link %d.\n", v5l->id);
|
||||
if (l1->los) {
|
||||
l1->los = 0;
|
||||
state_changed = 1;
|
||||
}
|
||||
break;
|
||||
case L1_SIGNAL_RAI:
|
||||
LOGP(DV5L1, LOGL_DEBUG, "Signal RAI detected on link %d.\n", v5l->id);
|
||||
if (!l1->rai) {
|
||||
l1->rai = 1;
|
||||
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_RAI, NULL);
|
||||
}
|
||||
break;
|
||||
case L1_SIGNAL_NO_RAI:
|
||||
LOGP(DV5L1, LOGL_DEBUG, "Signal RAI gone on link %d.\n", v5l->id);
|
||||
if (l1->rai) {
|
||||
l1->rai = 0;
|
||||
state_changed = 1;
|
||||
}
|
||||
break;
|
||||
case L1_SIGNAL_AIS:
|
||||
LOGP(DV5L1, LOGL_DEBUG, "Signal AIS detected on link %d.\n", v5l->id);
|
||||
if (!l1->ais) {
|
||||
l1->ais = 1;
|
||||
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_AIS, NULL);
|
||||
}
|
||||
break;
|
||||
case L1_SIGNAL_NO_AIS:
|
||||
LOGP(DV5L1, LOGL_DEBUG, "Signal AIS gone on link %d.\n", v5l->id);
|
||||
if (l1->ais) {
|
||||
l1->ais = 0;
|
||||
state_changed = 1;
|
||||
}
|
||||
break;
|
||||
case L1_SIGNAL_SA7_0:
|
||||
|
||||
if (l1->sa7 != 0) {
|
||||
LOGP(DV5L1, LOGL_DEBUG, "Bit Sa7=0 on link %d.\n", v5l->id);
|
||||
l1->sa7 = 0;
|
||||
state_changed = 1;
|
||||
}
|
||||
break;
|
||||
case L1_SIGNAL_SA7_1:
|
||||
if (l1->sa7 != 1) {
|
||||
LOGP(DV5L1, LOGL_DEBUG, "Bit Sa7=1 on link %d.\n", v5l->id);
|
||||
l1->sa7 = 1;
|
||||
state_changed = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGP(DV5L1, LOGL_NOTICE, "Invalid L1 primitive %d receied from physical layer.\n", prim);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Go on to normal state, when no alarm persists.
|
||||
* We must do this, because there is no frame reception to trigger this.
|
||||
*/
|
||||
if (state_changed && !l1->los && !l1->rai && !l1->ais) {
|
||||
if (l1->sa7 == 0)
|
||||
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_NORMAL_Sa7, NULL);
|
||||
else
|
||||
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_NORMAL, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* receive message from upper (LCP) layer */
|
||||
void v5x_l1_mph_snd(struct v5x_link *v5l, enum v5x_mph_prim prim)
|
||||
{
|
||||
struct v5x_l1_proto *l1 = v5l->l1;
|
||||
enum v5x_l1_fsm_event event;
|
||||
|
||||
switch (prim) {
|
||||
case MPH_ID:
|
||||
event = V5X_L1FSM_E_MPH_ID;
|
||||
break;
|
||||
case MPH_NOR:
|
||||
event = V5X_L1FSM_E_MPH_NOR;
|
||||
break;
|
||||
case MPH_IDR:
|
||||
event = V5X_L1FSM_E_MPH_IDR;
|
||||
break;
|
||||
case MPH_stop:
|
||||
// FIXME: do we need this?
|
||||
break;
|
||||
case MPH_proceed:
|
||||
// FIXME: do we need this?
|
||||
break;
|
||||
default:
|
||||
LOGP(DV5PORT, LOGL_NOTICE, "Got invalid prim %d at this protocol\n", prim);
|
||||
return;
|
||||
}
|
||||
|
||||
/* send event to FSM */
|
||||
osmo_fsm_inst_dispatch(l1->fi, event, NULL);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
void v5x_l1_mph_snd(struct v5x_link *v5i, enum v5x_mph_prim prim);
|
||||
struct v5x_l1_proto *v5x_l1_fsm_create(void *ctx, struct v5x_link *v5l, uint8_t id);
|
||||
void v5x_l1_fsm_destroy(struct v5x_l1_proto *l1);
|
||||
void v5x_l1_init(void);
|
||||
bool v5x_l1_is_up(struct v5x_l1_proto *l1);
|
||||
int v5x_l1_signal_rcv(struct v5x_link *v5i, enum l1_signal_prim prim);
|
||||
int v5x_l1_signal_snd(struct v5x_link *v5i, enum l1_signal_prim prim);
|
|
@ -0,0 +1,709 @@
|
|||
/* port/common/link CTRL protocol FSM / G.964 14.4 / G.965 16.3 */
|
||||
|
||||
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/***********************************************************************/
|
||||
/* internal data structures */
|
||||
/***********************************************************************/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "v5x_le_ctrl_fsm.h"
|
||||
#include "v5x_le_port_fsm.h"
|
||||
#include "v52_le_lcp_fsm.h"
|
||||
#include "v5x_le_management.h"
|
||||
#include "logging.h"
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
#define TIMEOUT 1 // T01 T02 TLCT01
|
||||
|
||||
/* uncomment to test lost TX messages at the first transmission */
|
||||
//#define TEST_TX_FAILURE
|
||||
|
||||
/***********************************************************************/
|
||||
/* state names, event names, primitives, ... */
|
||||
/***********************************************************************/
|
||||
|
||||
enum v5x_ctrl_fsm_state {
|
||||
V5X_CTRL_ST_OUT_OF_SERVICE = 0,
|
||||
V5X_CTRL_ST_IN_SERVICE,
|
||||
V5X_CTRL_ST_AWAIT_ACK,
|
||||
};
|
||||
|
||||
enum v5x_ctrl_fsm_event {
|
||||
V5X_CTRL_EV_MDU_START_TRAFFIC,
|
||||
V5X_CTRL_EV_MDU_STOP_TRAFFIC,
|
||||
V5X_CTRL_EV_MDU_CTRL,
|
||||
V5X_CTRL_EV_RX_CONTROL,
|
||||
V5X_CTRL_EV_RX_CONTROL_ACK,
|
||||
};
|
||||
|
||||
static const struct value_string v5x_ctrl_fsm_event_names[] = {
|
||||
{ V5X_CTRL_EV_MDU_START_TRAFFIC, "MDU-start_traffic" },
|
||||
{ V5X_CTRL_EV_MDU_STOP_TRAFFIC, "MDU-stop_traffic" },
|
||||
{ V5X_CTRL_EV_MDU_CTRL, "MDU-CTRL" },
|
||||
{ V5X_CTRL_EV_RX_CONTROL, "CONTROL" },
|
||||
{ V5X_CTRL_EV_RX_CONTROL_ACK, "CONTROL_ACK" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static void v5x_ctrl_mdu(struct osmo_fsm_inst *fi, const struct tlv_parsed *tp)
|
||||
{
|
||||
struct v5x_ctrl_proto *ctrl = fi->priv;
|
||||
struct v5x_interface *v5if = ctrl->priv;
|
||||
struct v5x_user_port *v5up = ctrl->priv;
|
||||
struct v5x_link *v5l = ctrl->priv;
|
||||
enum v5x_ctrl_func_id cfi;
|
||||
uint8_t variant;
|
||||
uint8_t rej_cause;
|
||||
uint32_t interface_id;
|
||||
enum v5x_ctrl_func_id cfe;
|
||||
uint8_t perf_grading;
|
||||
enum v52_link_ctrl_func lcf;
|
||||
|
||||
switch (ctrl->type) {
|
||||
case V5X_CTRL_TYPE_COMMON:
|
||||
cfi = *TLVP_VAL(tp, V5X_CTRL_IEI_CTRL_F_ID) & 0x7f;
|
||||
variant = 0;
|
||||
rej_cause = 0;
|
||||
interface_id = 0;
|
||||
|
||||
LOGP(DV5CTRL, LOGL_DEBUG, "MDU-CTRL received.\n");
|
||||
|
||||
if (TLVP_PRESENT(tp, V5X_CTRL_IEI_VARIANT))
|
||||
variant = *TLVP_VAL(tp, V5X_CTRL_IEI_VARIANT) & 0x7f;
|
||||
if (TLVP_PRESENT(tp, V5X_CTRL_IEI_REJECTION_CAUSE))
|
||||
rej_cause = *TLVP_VAL(tp, V5X_CTRL_IEI_REJECTION_CAUSE) & 0x0f;
|
||||
if (TLVP_PRESENT(tp, V5X_CTRL_IEI_INTERFACE_ID))
|
||||
interface_id = osmo_load32be_ext_2(TLVP_VAL(tp, V5X_CTRL_IEI_INTERFACE_ID), 3);
|
||||
v5x_le_common_ctrl_rcv(v5if, cfi, variant, rej_cause, interface_id);
|
||||
break;
|
||||
case V5X_CTRL_TYPE_PORT:
|
||||
cfe = *TLVP_VAL(tp, V5X_CTRL_IEI_CTRL_F_ELEMENT) & 0x7f;
|
||||
perf_grading = 0;
|
||||
|
||||
LOGP(DV5CTRL, LOGL_DEBUG, "FE received for address %d.\n", v5up->nr);
|
||||
|
||||
if (TLVP_PRESENT(tp, V5X_CTRL_IEI_PERFORMANCE_GRADING))
|
||||
perf_grading = *TLVP_VAL(tp, V5X_CTRL_IEI_PERFORMANCE_GRADING) & 0x0f;
|
||||
switch (v5up->type) {
|
||||
case V5X_USER_TYPE_ISDN:
|
||||
v5x_le_port_isdn_fe_rcv(v5up, cfe, perf_grading);
|
||||
break;
|
||||
case V5X_USER_TYPE_PSTN:
|
||||
v5x_le_port_pstn_fe_rcv(v5up, cfe);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case V5X_CTRL_TYPE_LINK:
|
||||
lcf = *TLVP_VAL(tp, V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION) & 0x7f;
|
||||
|
||||
LOGP(DV5CTRL, LOGL_DEBUG, "FE received for link ID %d.\n", v5l->id);
|
||||
|
||||
v52_le_lcp_fe_rcv(v5l, lcf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* display MDU-error_indication */
|
||||
static void v5x_mdu_error(struct osmo_fsm_inst __attribute__((unused)) *fi, const char *error)
|
||||
{
|
||||
LOGP(DV5CTRL, LOGL_NOTICE, "MDU-error_indication: %s\n", error);
|
||||
}
|
||||
|
||||
/* send control towards lower (DL) layer */
|
||||
static void v5x_ctrl_send(struct v5x_ctrl_proto *ctrl, struct msgb *msg)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = ctrl->fi;
|
||||
struct v5x_interface *v5if = ctrl->priv;
|
||||
struct v5x_user_port *v5up = ctrl->priv;
|
||||
struct v5x_link *v5l = ctrl->priv;
|
||||
|
||||
switch (fi->state) {
|
||||
case V5X_CTRL_ST_IN_SERVICE:
|
||||
LOGP(DV5CTRL, LOGL_DEBUG, "We are in service, so we send our message now.\n");
|
||||
/* no message, clone current message and store to be repeated */
|
||||
ctrl->tx_msg = msgb_copy(msg, NULL);
|
||||
/* go to AWAIT_PENDING_ACK */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_CTRL_ST_AWAIT_ACK, TIMEOUT, 0);
|
||||
/* send message towards DL */
|
||||
#ifdef TEST_TX_FAILURE
|
||||
msgb_free(msg);
|
||||
#else
|
||||
switch (ctrl->type) {
|
||||
case V5X_CTRL_TYPE_COMMON:
|
||||
v5x_dl_snd(v5if, V5X_DLADDR_CTRL, msg);
|
||||
break;
|
||||
case V5X_CTRL_TYPE_PORT:
|
||||
v5x_dl_snd(v5up->interface, V5X_DLADDR_CTRL, msg);
|
||||
break;
|
||||
case V5X_CTRL_TYPE_LINK:
|
||||
v5x_dl_snd(v5l->interface, V52_DLADDR_LCP, msg);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case V5X_CTRL_ST_AWAIT_ACK:
|
||||
LOGP(DV5CTRL, LOGL_DEBUG, "We are waiting for ack, so we queue our message.\n");
|
||||
/* pending message, save message in queue */
|
||||
msgb_enqueue(&ctrl->tx_queue, msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* receive acknowledge */
|
||||
static void v5x_ctrl_ack(struct osmo_fsm_inst *fi, const struct tlv_parsed __attribute__((unused)) *tp)
|
||||
{
|
||||
struct v5x_ctrl_proto *ctrl = fi->priv;
|
||||
struct msgb *msg;
|
||||
|
||||
LOGP(DV5CTRL, LOGL_DEBUG, "Received acknowledge, removing pending message, if any.\n");
|
||||
/* free pending copy of message, if acked before retry */
|
||||
if (ctrl->tx_msg) {
|
||||
msgb_free(ctrl->tx_msg);
|
||||
ctrl->tx_msg = NULL;
|
||||
}
|
||||
/* go to IN_SERVICE */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_CTRL_ST_IN_SERVICE, 0, 0);
|
||||
/* sending next pending message in queue */
|
||||
msg = msgb_dequeue(&ctrl->tx_queue);
|
||||
if (msg) {
|
||||
LOGP(DV5CTRL, LOGL_DEBUG, "Found pending message in queue.\n");
|
||||
v5x_ctrl_send(ctrl, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/* stop traffic */
|
||||
static void v5x_ctrl_stop(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct v5x_ctrl_proto *ctrl = fi->priv;
|
||||
struct msgb *msg;
|
||||
|
||||
/* flush pending messages */
|
||||
if (ctrl->tx_msg) {
|
||||
msgb_free(ctrl->tx_msg);
|
||||
ctrl->tx_msg = NULL;
|
||||
}
|
||||
while ((msg = msgb_dequeue(&ctrl->tx_queue)))
|
||||
msgb_free(msg);
|
||||
/* go to OUT_OF_SERVICE */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_CTRL_ST_OUT_OF_SERVICE, 0, 0);
|
||||
}
|
||||
|
||||
/* T01 / T02 */
|
||||
static int v5x_ctrl_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct v5x_ctrl_proto *ctrl = fi->priv;
|
||||
struct v5x_interface *v5if = ctrl->priv;
|
||||
struct v5x_user_port *v5up = ctrl->priv;
|
||||
struct v5x_link *v5l = ctrl->priv;
|
||||
struct msgb *msg;
|
||||
|
||||
if (ctrl->tx_msg) {
|
||||
LOGP(DV5CTRL, LOGL_DEBUG, "Timer fired the first time, resending message.\n");
|
||||
/* first expiry: repeat CONTROL; re-start T01 */
|
||||
/* send message towards DL */
|
||||
osmo_timer_schedule(&fi->timer, TIMEOUT, 0);
|
||||
msg = ctrl->tx_msg;
|
||||
ctrl->tx_msg = NULL;
|
||||
switch (ctrl->type) {
|
||||
case V5X_CTRL_TYPE_COMMON:
|
||||
v5x_dl_snd(v5if, V5X_DLADDR_CTRL, msg);
|
||||
break;
|
||||
case V5X_CTRL_TYPE_PORT:
|
||||
v5x_dl_snd(v5up->interface, V5X_DLADDR_CTRL, msg);
|
||||
break;
|
||||
case V5X_CTRL_TYPE_LINK:
|
||||
v5x_dl_snd(v5l->interface, V52_DLADDR_LCP, msg);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOGP(DV5CTRL, LOGL_DEBUG, "Timer fired the second time, indicate an error.\n");
|
||||
/* second expiry: send MDU-error_ind; go to IN_SERVICE */
|
||||
v5x_mdu_error(fi, "Second timeout while waiting for CONTROL ACK.");
|
||||
osmo_fsm_inst_state_chg(fi, V5X_CTRL_ST_IN_SERVICE, 0, 0);
|
||||
/* sending next pending message in queue */
|
||||
msg = msgb_dequeue(&ctrl->tx_queue);
|
||||
if (msg) {
|
||||
LOGP(DV5CTRL, LOGL_DEBUG, "Found pending message in queue.\n");
|
||||
v5x_ctrl_send(ctrl, msg);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* events in state OUT OF SERVICE */
|
||||
static void v5x_ctrl_fsm_oos(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_CTRL_EV_MDU_START_TRAFFIC:
|
||||
/* go to IN_SERVICE */
|
||||
osmo_fsm_inst_state_chg(fi, V5X_CTRL_ST_IN_SERVICE, 0, 0);
|
||||
break;
|
||||
case V5X_CTRL_EV_MDU_STOP_TRAFFIC:
|
||||
v5x_mdu_error(fi, "Got MDU-stop, but traffic not started.");
|
||||
break;
|
||||
case V5X_CTRL_EV_MDU_CTRL:
|
||||
msgb_free(data);
|
||||
v5x_mdu_error(fi, "Got MDU-CTRL, but traffic not started.");
|
||||
break;
|
||||
case V5X_CTRL_EV_RX_CONTROL:
|
||||
v5x_mdu_error(fi, "Received CONTROL, but traffic not started.");
|
||||
break;
|
||||
case V5X_CTRL_EV_RX_CONTROL_ACK:
|
||||
v5x_mdu_error(fi, "Received CONTROL ACK, but traffic not started.");
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* events in state IN SERVICE */
|
||||
static void v5x_ctrl_fsm_ins(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_CTRL_EV_MDU_START_TRAFFIC:
|
||||
break;
|
||||
case V5X_CTRL_EV_MDU_STOP_TRAFFIC:
|
||||
v5x_ctrl_stop(fi);
|
||||
break;
|
||||
case V5X_CTRL_EV_MDU_CTRL:
|
||||
v5x_ctrl_send(fi->priv, data);
|
||||
break;
|
||||
case V5X_CTRL_EV_RX_CONTROL:
|
||||
v5x_ctrl_mdu(fi, data);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* events in state AWAIT PORT/COMMON ACK */
|
||||
static void v5x_ctrl_fsm_aa(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_CTRL_EV_MDU_START_TRAFFIC:
|
||||
break;
|
||||
case V5X_CTRL_EV_MDU_STOP_TRAFFIC:
|
||||
v5x_ctrl_stop(fi);
|
||||
break;
|
||||
case V5X_CTRL_EV_MDU_CTRL:
|
||||
v5x_ctrl_send(fi->priv, data);
|
||||
break;
|
||||
case V5X_CTRL_EV_RX_CONTROL:
|
||||
v5x_ctrl_mdu(fi, data);
|
||||
break;
|
||||
case V5X_CTRL_EV_RX_CONTROL_ACK:
|
||||
v5x_ctrl_ack(fi, data);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct osmo_fsm_state v5x_ctrl_fsm_states[] = {
|
||||
[V5X_CTRL_ST_OUT_OF_SERVICE] = {
|
||||
.name = "OUT_OF_SERVICE",
|
||||
.in_event_mask = S(V5X_CTRL_EV_MDU_START_TRAFFIC) |
|
||||
S(V5X_CTRL_EV_MDU_STOP_TRAFFIC) |
|
||||
S(V5X_CTRL_EV_MDU_CTRL) |
|
||||
S(V5X_CTRL_EV_RX_CONTROL) |
|
||||
S(V5X_CTRL_EV_RX_CONTROL_ACK),
|
||||
.out_state_mask = S(V5X_CTRL_ST_IN_SERVICE),
|
||||
.action = v5x_ctrl_fsm_oos,
|
||||
},
|
||||
[V5X_CTRL_ST_IN_SERVICE] = {
|
||||
.name = "IN_SERVICE",
|
||||
.in_event_mask = S(V5X_CTRL_EV_MDU_START_TRAFFIC) |
|
||||
S(V5X_CTRL_EV_MDU_STOP_TRAFFIC) |
|
||||
S(V5X_CTRL_EV_MDU_CTRL) |
|
||||
S(V5X_CTRL_EV_RX_CONTROL),
|
||||
|
||||
.out_state_mask = S(V5X_CTRL_ST_OUT_OF_SERVICE) |
|
||||
S(V5X_CTRL_ST_AWAIT_ACK),
|
||||
.action = v5x_ctrl_fsm_ins,
|
||||
},
|
||||
[V5X_CTRL_ST_AWAIT_ACK] = {
|
||||
.name = "AWAIT_ACK",
|
||||
.in_event_mask = S(V5X_CTRL_EV_MDU_START_TRAFFIC) |
|
||||
S(V5X_CTRL_EV_MDU_STOP_TRAFFIC) |
|
||||
S(V5X_CTRL_EV_MDU_CTRL) |
|
||||
S(V5X_CTRL_EV_RX_CONTROL) |
|
||||
S(V5X_CTRL_EV_RX_CONTROL_ACK),
|
||||
.out_state_mask = S(V5X_CTRL_ST_OUT_OF_SERVICE) |
|
||||
S(V5X_CTRL_ST_IN_SERVICE),
|
||||
.action = v5x_ctrl_fsm_aa,
|
||||
},
|
||||
};
|
||||
|
||||
struct osmo_fsm v5x_ctrl_fsm = {
|
||||
.name = "V5X_CTRL",
|
||||
.states = v5x_ctrl_fsm_states,
|
||||
.num_states = ARRAY_SIZE(v5x_ctrl_fsm_states),
|
||||
.allstate_event_mask = 0,
|
||||
.allstate_action = NULL,
|
||||
.cleanup = NULL,
|
||||
.timer_cb = v5x_ctrl_fsm_timer_cb,
|
||||
.log_subsys = DV5CTRL,
|
||||
.event_names = v5x_ctrl_fsm_event_names,
|
||||
};
|
||||
|
||||
void v5x_le_ctrl_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = osmo_fsm_register(&v5x_ctrl_fsm);
|
||||
OSMO_ASSERT(!rc);
|
||||
LOGP(DV5CTRL, LOGL_NOTICE, "Using V5x control protocol\n");
|
||||
}
|
||||
|
||||
struct v5x_ctrl_proto *v5x_le_ctrl_create(enum v5x_ctrl_type type, void *ctx, void *priv, uint16_t nr)
|
||||
{
|
||||
struct v5x_ctrl_proto *ctrl;
|
||||
|
||||
OSMO_ASSERT(priv);
|
||||
|
||||
ctrl = talloc_zero(ctx, struct v5x_ctrl_proto);
|
||||
if (!ctrl)
|
||||
return NULL;
|
||||
ctrl->type = type;
|
||||
ctrl->priv = priv;
|
||||
INIT_LLIST_HEAD(&ctrl->tx_queue);
|
||||
|
||||
ctrl->fi = osmo_fsm_inst_alloc(&v5x_ctrl_fsm, ctrl, ctrl, LOGL_DEBUG, NULL);
|
||||
if (!ctrl->fi) {
|
||||
v5x_le_ctrl_destroy(ctrl);
|
||||
return NULL;
|
||||
}
|
||||
osmo_fsm_inst_update_id_f(ctrl->fi, "%d", nr);
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
void v5x_le_ctrl_destroy(struct v5x_ctrl_proto *ctrl)
|
||||
{
|
||||
/* get rid of pending messages */
|
||||
msgb_queue_free(&ctrl->tx_queue);
|
||||
if (ctrl->tx_msg)
|
||||
msgb_free(ctrl->tx_msg);
|
||||
|
||||
if (ctrl->fi)
|
||||
osmo_fsm_inst_free(ctrl->fi);
|
||||
|
||||
talloc_free(ctrl);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* V5 Message encoding / sending
|
||||
***********************************************************************/
|
||||
|
||||
/* G.964 Section 14.4.1.1 / Table 48 */
|
||||
static struct msgb *v5x_enc_ctrl_port(struct v5x_user_port *v5up, enum v5x_ctrl_func_el cfe, bool is_isdn)
|
||||
{
|
||||
uint8_t cfe_ie = cfe | 0x80;
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(v5x_l3_addr_enc(v5up->nr, is_isdn));
|
||||
l3h->msg_type = V5X_CTRL_MSGT_PORT_CTRL;
|
||||
|
||||
msgb_tlv_put(msg, V5X_CTRL_IEI_CTRL_F_ELEMENT, 1, &cfe_ie);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.964 Section 14.4.1.2 / Table 49 */
|
||||
static struct msgb *v5x_enc_ctrl_port_ack(struct v5x_user_port *v5up, enum v5x_ctrl_func_el cfe, bool is_isdn)
|
||||
{
|
||||
uint8_t cfe_ie = cfe | 0x80;
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(v5x_l3_addr_enc(v5up->nr, is_isdn));
|
||||
l3h->msg_type = V5X_CTRL_MSGT_PORT_CTRL_ACK;
|
||||
|
||||
msgb_tlv_put(msg, V5X_CTRL_IEI_CTRL_F_ELEMENT, 1, &cfe_ie);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.964 Section 14.4.1.3 / Table 50 */
|
||||
static struct msgb *v5x_enc_ctrl_common(enum v5x_ctrl_func_id cfi, uint8_t *variant, uint8_t *rej_cause,
|
||||
uint32_t *interface_id)
|
||||
{
|
||||
uint8_t cfi_ie = cfi | 0x80;
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(v5x_l3_addr_enc(V5X_DLADDR_CTRL, true));
|
||||
l3h->msg_type = V5X_CTRL_MSGT_COMMON_CTRL;
|
||||
|
||||
msgb_tlv_put(msg, V5X_CTRL_IEI_CTRL_F_ID, 1, &cfi_ie);
|
||||
if (variant) {
|
||||
/* Conditional: Variant */
|
||||
uint8_t variant_ie = *variant | 0x80;
|
||||
msgb_tlv_put(msg, V5X_CTRL_IEI_VARIANT, 1, &variant_ie);
|
||||
}
|
||||
if (rej_cause) {
|
||||
/* Conditional: Rejection Cause */
|
||||
msgb_put_u8(msg, 0xF0 | (*rej_cause & 0x0F));
|
||||
}
|
||||
if (interface_id) {
|
||||
/* Conditional: Interface Id */
|
||||
uint8_t iid_ie[3];
|
||||
osmo_store32be_ext(*interface_id, iid_ie, 3);
|
||||
msgb_tlv_put(msg, V5X_CTRL_IEI_INTERFACE_ID, sizeof(iid_ie), iid_ie);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.964 Section 14.4.1.4 / Table 51 */
|
||||
static struct msgb *v5x_enc_ctrl_common_ack(enum v5x_ctrl_func_id cfi)
|
||||
{
|
||||
uint8_t cfi_ie = cfi | 0x80;
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(v5x_l3_addr_enc(V5X_DLADDR_CTRL, true));
|
||||
l3h->msg_type = V5X_CTRL_MSGT_COMMON_CTRL_ACK;
|
||||
|
||||
msgb_tlv_put(msg, V5X_CTRL_IEI_CTRL_F_ID, 1, &cfi_ie);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.965 Section 16.3.1.1 / Table 19 */
|
||||
static struct msgb *v52_enc_ctrl_link_ack(struct v5x_link *v5l, enum v52_link_ctrl_func lcf)
|
||||
{
|
||||
uint8_t lcf_ie = lcf | 0x80;
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(v5l->id);
|
||||
l3h->msg_type = V52_CTRL_MSGT_LCP_LINK_CTRL_ACK;
|
||||
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION, 1, &lcf_ie);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.965 Section 16.3.1.2 / Table 20 */
|
||||
static struct msgb *v52_enc_ctrl_link(struct v5x_link *v5l, enum v52_link_ctrl_func lcf)
|
||||
{
|
||||
uint8_t lcf_ie = lcf | 0x80;
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V5X_CTRL_PDISC;
|
||||
l3h->l3_addr = htons(v5l->id);
|
||||
l3h->msg_type = V52_CTRL_MSGT_LCP_LINK_CTRL;
|
||||
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION, 1, &lcf_ie);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* V5 Message receiving / decoding
|
||||
***********************************************************************/
|
||||
|
||||
static int v5x_rcv_ctrl_port(struct v5x_user_port *v5up, uint8_t msg_type, const struct tlv_parsed *tp)
|
||||
{
|
||||
enum v5x_ctrl_func_el cfe = *TLVP_VAL(tp, V5X_CTRL_IEI_CTRL_F_ELEMENT) & 0x7f;
|
||||
|
||||
switch (msg_type) {
|
||||
case V5X_CTRL_MSGT_PORT_CTRL:
|
||||
/* send ACK to AN */
|
||||
v5x_dl_snd(v5up->interface, V5X_DLADDR_CTRL, v5x_enc_ctrl_port_ack(v5up, cfe, v5up->type == V5X_USER_TYPE_ISDN));
|
||||
/* FIXME: send event to FSM */
|
||||
osmo_fsm_inst_dispatch(v5up->ctrl->fi, V5X_CTRL_EV_RX_CONTROL, (void *)tp);
|
||||
return 0;
|
||||
case V5X_CTRL_MSGT_PORT_CTRL_ACK:
|
||||
osmo_fsm_inst_dispatch(v5up->ctrl->fi, V5X_CTRL_EV_RX_CONTROL_ACK, (void *)tp);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int v5x_rcv_ctrl_common(struct v5x_interface *v5if, uint8_t msg_type, const struct tlv_parsed *tp)
|
||||
{
|
||||
enum v5x_ctrl_func_id cfi = *TLVP_VAL(tp, V5X_CTRL_IEI_CTRL_F_ID) & 0x7f;
|
||||
|
||||
switch (msg_type) {
|
||||
case V5X_CTRL_MSGT_COMMON_CTRL:
|
||||
v5x_dl_snd(v5if, V5X_DLADDR_CTRL, v5x_enc_ctrl_common_ack(cfi));
|
||||
/* send event to FSM */
|
||||
osmo_fsm_inst_dispatch(v5if->control.ctrl->fi, V5X_CTRL_EV_RX_CONTROL, (void *)tp);
|
||||
/* send ACK to AN */
|
||||
return 0;
|
||||
case V5X_CTRL_MSGT_COMMON_CTRL_ACK:
|
||||
/* send event to FSM */
|
||||
osmo_fsm_inst_dispatch(v5if->control.ctrl->fi, V5X_CTRL_EV_RX_CONTROL_ACK, (void *)tp);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int v52_rcv_ctrl_link(struct v5x_link *v5l, uint8_t msg_type, const struct tlv_parsed *tp)
|
||||
{
|
||||
enum v52_link_ctrl_func lcf = *TLVP_VAL(tp, V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION) & 0x7f;
|
||||
|
||||
switch (msg_type) {
|
||||
case V52_CTRL_MSGT_LCP_LINK_CTRL:
|
||||
v5x_dl_snd(v5l->interface, V52_DLADDR_LCP, v52_enc_ctrl_link_ack(v5l, lcf));
|
||||
/* send event to FSM */
|
||||
osmo_fsm_inst_dispatch(v5l->ctrl->fi, V5X_CTRL_EV_RX_CONTROL, (void *)tp);
|
||||
/* send ACK to AN */
|
||||
return 0;
|
||||
case V52_CTRL_MSGT_LCP_LINK_CTRL_ACK:
|
||||
/* send event to FSM */
|
||||
osmo_fsm_inst_dispatch(v5l->ctrl->fi, V5X_CTRL_EV_RX_CONTROL_ACK, (void *)tp);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* receive message from lower (DL) layer */
|
||||
int v5x_le_ctrl_dl_rcv(struct v5x_interface *v5if, uint16_t l3_addr, bool is_isdn, uint8_t msg_type,
|
||||
const struct tlv_parsed *tp)
|
||||
{
|
||||
struct v5x_user_port *v5up;
|
||||
struct v5x_link *v5l;
|
||||
|
||||
switch (msg_type) {
|
||||
case V5X_CTRL_MSGT_PORT_CTRL:
|
||||
case V5X_CTRL_MSGT_PORT_CTRL_ACK:
|
||||
v5up = v5x_user_port_find(v5if, l3_addr, is_isdn);
|
||||
if (!v5up) {
|
||||
LOGP(DV5CTRL, LOGL_ERROR, "Received %s port control message with unknown layer 3 address %d. "
|
||||
"Please check provisioning!\n", is_isdn ? "ISDN" : "PSTN", l3_addr);
|
||||
return -ENODEV;
|
||||
}
|
||||
return v5x_rcv_ctrl_port(v5up, msg_type, tp);
|
||||
case V5X_CTRL_MSGT_COMMON_CTRL:
|
||||
case V5X_CTRL_MSGT_COMMON_CTRL_ACK:
|
||||
if (l3_addr != V5X_DLADDR_CTRL)
|
||||
return -EINVAL;
|
||||
return v5x_rcv_ctrl_common(v5if, msg_type, tp);
|
||||
case V52_CTRL_MSGT_LCP_LINK_CTRL:
|
||||
case V52_CTRL_MSGT_LCP_LINK_CTRL_ACK:
|
||||
v5l = v5x_link_find_id(v5if, l3_addr);
|
||||
if (!v5l) {
|
||||
LOGP(DV5CTRL, LOGL_ERROR, "Received link control message with unknown link ID %d. Please "
|
||||
"check provisioning!\n", l3_addr);
|
||||
return -ENODEV;
|
||||
}
|
||||
return v52_rcv_ctrl_link(v5l, msg_type, tp);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* V5 Message sending / encoding
|
||||
***********************************************************************/
|
||||
|
||||
/* send common message from upper layer */
|
||||
int v5x_le_ctrl_common_snd(struct v5x_interface *v5if, enum v5x_ctrl_func_id cfi,
|
||||
uint8_t *rej_cause, uint8_t *variant, uint32_t *interface_id)
|
||||
{
|
||||
LOGP(DV5CTRL, LOGL_DEBUG, "Sending MDU-CTRL (from common control).\n");
|
||||
|
||||
osmo_fsm_inst_dispatch(v5if->control.ctrl->fi, V5X_CTRL_EV_MDU_CTRL,
|
||||
v5x_enc_ctrl_common(cfi, variant, rej_cause, interface_id));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* send port message from upper layer */
|
||||
int v5x_le_ctrl_port_snd(struct v5x_user_port *v5up, enum v5x_ctrl_func_el cfe)
|
||||
{
|
||||
LOGP(DV5CTRL, LOGL_DEBUG, "Sending FE (from port).\n");
|
||||
|
||||
osmo_fsm_inst_dispatch(v5up->ctrl->fi, V5X_CTRL_EV_MDU_CTRL,
|
||||
v5x_enc_ctrl_port(v5up, cfe, v5up->type == V5X_USER_TYPE_ISDN));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* send link message from upper layer */
|
||||
int v52_le_ctrl_link_snd(struct v5x_link *v5l, enum v52_link_ctrl_func lcf)
|
||||
{
|
||||
LOGP(DV5CTRL, LOGL_DEBUG, "Sending FE (from LCP).\n");
|
||||
|
||||
osmo_fsm_inst_dispatch(v5l->ctrl->fi, V5X_CTRL_EV_MDU_CTRL,
|
||||
v52_enc_ctrl_link(v5l, lcf));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void v5x_le_ctrl_start(struct v5x_ctrl_proto *ctrl)
|
||||
{
|
||||
osmo_fsm_inst_dispatch(ctrl->fi, V5X_CTRL_EV_MDU_START_TRAFFIC, NULL);
|
||||
}
|
||||
void v5x_le_ctrl_stop(struct v5x_ctrl_proto *ctrl)
|
||||
{
|
||||
if (ctrl->fi->state == V5X_CTRL_ST_OUT_OF_SERVICE)
|
||||
return;
|
||||
osmo_fsm_inst_dispatch(ctrl->fi, V5X_CTRL_EV_MDU_STOP_TRAFFIC, NULL);
|
||||
}
|
||||
|
||||
bool v5x_le_ctrl_is_in_service(struct v5x_ctrl_proto *ctrl)
|
||||
{
|
||||
return (ctrl->fi->state >= V5X_CTRL_ST_IN_SERVICE);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
void v5x_le_ctrl_init(void);
|
||||
struct v5x_ctrl_proto *v5x_le_ctrl_create(enum v5x_ctrl_type type, void *ctx, void *priv, uint16_t nr);
|
||||
void v5x_le_ctrl_destroy(struct v5x_ctrl_proto *ctrl);
|
||||
int v5x_le_ctrl_dl_rcv(struct v5x_interface *v5if, uint16_t l3_addr, bool is_isdn, uint8_t msg_type,
|
||||
const struct tlv_parsed *tp);
|
||||
int v5x_le_ctrl_common_snd(struct v5x_interface *v5if, enum v5x_ctrl_func_id cfi,
|
||||
uint8_t *rej_cause, uint8_t *variant, uint32_t *interface_id);
|
||||
int v5x_le_ctrl_port_snd(struct v5x_user_port *v5up, enum v5x_ctrl_func_el cfe);
|
||||
int v52_le_ctrl_link_snd(struct v5x_link *v5l, enum v52_link_ctrl_func lcf);
|
||||
void v5x_le_ctrl_start(struct v5x_ctrl_proto *ctrl);
|
||||
void v5x_le_ctrl_stop(struct v5x_ctrl_proto *ctrl);
|
||||
bool v5x_le_ctrl_is_in_service(struct v5x_ctrl_proto *ctrl);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,31 @@
|
|||
|
||||
const char *v5x_le_system_fsm_state_name(struct osmo_fsm_inst *fi);
|
||||
const char *v5x_le_pstn_dl_fsm_state_name(struct osmo_fsm_inst *fi);
|
||||
const char *v5x_le_pstn_rs_fsm_state_name(struct osmo_fsm_inst *fi);
|
||||
const char *v5x_le_unblk_all_fsm_state_name(struct osmo_fsm_inst *fi);
|
||||
void v5x_le_mgmt_init(void);
|
||||
struct v5x_mgmt_proto *v5x_le_mgmt_create(struct v5x_interface *v5if);
|
||||
void v5x_le_mgmt_destroy(struct v5x_mgmt_proto *mgmt);
|
||||
void v5x_le_mgmt_start_delay(struct v5x_interface *v5if, int delay);
|
||||
int v5x_le_mgmt_start(struct v5x_interface *v5if);
|
||||
int v5x_le_pstn_restart(struct v5x_interface *v5if);
|
||||
int v5x_le_align_ports(struct v5x_interface *v5if, int accelerated);
|
||||
void v5x_le_mdl_rcv(struct v5x_interface *v5if, struct v5x_link *v5l, enum v5x_mgmt_prim prim, uint16_t dladdr);
|
||||
void v5x_le_common_ctrl_rcv(struct v5x_interface *v5if, uint8_t cfi, uint8_t variant, uint8_t rej_cause,
|
||||
uint32_t interface_id);
|
||||
void v5x_le_provisioning_start(struct v5x_interface *v5if);
|
||||
void v5x_le_mgmt_port_unblock(struct v5x_user_port *v5up);
|
||||
void v5x_le_mgmt_port_block(struct v5x_user_port *v5up);
|
||||
void v5x_le_nat_ph_rcv(struct v5x_user_port *v5up, uint8_t prim, uint8_t *data, int length);
|
||||
void v5x_le_port_mph_rcv(struct v5x_user_port *v5up, enum v5x_mph_prim prim, uint8_t perf_grading);
|
||||
void v5x_le_nat_fe_rcv(struct v5x_user_port *v5up, struct msgb *msg);
|
||||
void v5x_le_pstn_fe_rcv(struct v5x_user_port *v5up, enum v5x_fe_prim prim, struct msgb *msg);
|
||||
void v5x_le_pstn_mdu_rcv(struct v5x_user_port *v5up, enum v5x_mgmt_prim prim);
|
||||
void v52_le_lcp_mdu_rcv(struct v5x_link *v5l, enum v5x_mgmt_prim prim);
|
||||
int v52_le_pp_mdu_rcv(struct v5x_interface *v5if, enum v5x_mgmt_prim prim, uint8_t link_id, uint8_t ts,
|
||||
const uint8_t *cause, uint8_t cause_len);
|
||||
void v5x_le_channel_assign(struct v5x_user_port *v5up, int channel);
|
||||
void v5x_le_channel_unassign(struct v5x_user_port *v5up, int channel);
|
||||
int v52_le_bcc_mdu_rcv(struct v5x_interface *v5if, uint8_t link_id, uint8_t ts,
|
||||
uint8_t __attribute__((unused)) *isdn_multislot, enum v5x_mgmt_prim prim, const uint8_t *cause,
|
||||
uint8_t cause_len);
|
|
@ -0,0 +1,887 @@
|
|||
/* ITU-T G.964 Section 14.1.3.2.2 ISDN user port FSM - LE */
|
||||
|
||||
/***********************************************************************/
|
||||
/* internal data structures */
|
||||
/***********************************************************************/
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "v5x_le_ctrl_fsm.h"
|
||||
#include "v5x_le_management.h"
|
||||
#include "logging.h"
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
/***********************************************************************/
|
||||
/* state names, event names, primitives, ... */
|
||||
/***********************************************************************/
|
||||
|
||||
/* 14.1.3.2.2 ISDN user port FSM - LE(ISDN port) */
|
||||
enum v5x_le_isdn_port_state {
|
||||
V5X_LE_UP_I_S_LE10_NOP_BLOCKED, /* LE1.0 */
|
||||
V5X_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK, /* LE1.1 */
|
||||
V5X_LE_UP_I_S_LE12_NOP_REMOTE_UNBLOCK, /* LE1.2 */
|
||||
V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, /* LE2.0 */
|
||||
V5X_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED, /* LE2.1 */
|
||||
V5X_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED, /* LE2.2 */
|
||||
};
|
||||
|
||||
/* 14.2.3.2.2 PSTN user port FSM - LE(PSTN port) */
|
||||
enum v5x_le_pstn_port_state {
|
||||
V5X_LE_UP_P_S_LE10_NOP_BLOCKED, /* LE1.0 */
|
||||
V5X_LE_UP_P_S_LE11_NOP_LOCAL_UNBLOCK, /* LE1.1 */
|
||||
V5X_LE_UP_P_S_LE12_NOP_REMOTE_UNBLOCK, /* LE1.2 */
|
||||
V5X_LE_UP_P_S_LE20_OPERATIONAL, /* LE2.0 */
|
||||
};
|
||||
|
||||
enum v5x_le_ctrl_port_fsm_event {
|
||||
/* inbound function elements from AN (Table 34/G.964) */
|
||||
V5X_CUP_LE_FE102_ACTIV_INIT_USER_IND,
|
||||
V5X_CUP_LE_FE103_DS_ACTIVATED_IND,
|
||||
V5X_CUP_LE_FE104_ACCESS_ACTIVATED_IND,
|
||||
V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND,
|
||||
V5X_CUP_LE_FE202_UNBLOCK,
|
||||
V5X_CUP_LE_FE204_BLOCK_CMD,
|
||||
V5X_CUP_LE_FE205_BLOCK_REQ,
|
||||
V5X_CUP_LE_FE206_PREFORMANCE_GRADING_IND,
|
||||
/* inbound primitives from Mgmt (Table 35/G.964) */
|
||||
V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ,
|
||||
V5X_CUP_LE_MPH_BI_BLOCK_CMD,
|
||||
V5X_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ,
|
||||
V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ,
|
||||
V5X_CUP_LE_MPH_DB_BLOCK_DCHAN_REQ,
|
||||
V5X_CUP_LE_MPH_DU_BLOCK_DCHAN_REQ,
|
||||
};
|
||||
|
||||
static const struct value_string v5x_le_ctrl_port_fsm_event_names[] = {
|
||||
{ V5X_CUP_LE_FE102_ACTIV_INIT_USER_IND, "FE102 Activation by user indication" },
|
||||
{ V5X_CUP_LE_FE103_DS_ACTIVATED_IND, "FE103 DS Activated indication" },
|
||||
{ V5X_CUP_LE_FE104_ACCESS_ACTIVATED_IND, "FE104 Access activated indication" },
|
||||
{ V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND, "FE106 Access deactivated indication" },
|
||||
{ V5X_CUP_LE_FE202_UNBLOCK, "FE202 Unblock request or ack" },
|
||||
{ V5X_CUP_LE_FE204_BLOCK_CMD, "FE204 Block command" },
|
||||
{ V5X_CUP_LE_FE205_BLOCK_REQ, "FE205 Block request" },
|
||||
{ V5X_CUP_LE_FE206_PREFORMANCE_GRADING_IND, "FE206 Performance grading info" },
|
||||
{ V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ, "MPH-UBR Unblock request" },
|
||||
{ V5X_CUP_LE_MPH_BI_BLOCK_CMD, "MPH-BI Block command" },
|
||||
{ V5X_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ, "MPH-AR Activate access" },
|
||||
{ V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ, "MPH-DR Deactivate access" },
|
||||
{ V5X_CUP_LE_MPH_DB_BLOCK_DCHAN_REQ, "MPH-DB Block D-channel" },
|
||||
{ V5X_CUP_LE_MPH_DU_BLOCK_DCHAN_REQ, "MPH-DU Unblock D-channel" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/***********************************************************************/
|
||||
/* Messages to other layers */
|
||||
/***********************************************************************/
|
||||
|
||||
/* send message to management layer */
|
||||
static void mph_rcv(struct osmo_fsm_inst *fi, enum v5x_mph_prim prim, uint8_t perf_grading)
|
||||
{
|
||||
struct v5x_user_port *v5up = fi->priv;
|
||||
|
||||
v5x_le_port_mph_rcv(v5up, prim, perf_grading);
|
||||
}
|
||||
|
||||
static void ctrl_snd(struct osmo_fsm_inst *fi, enum v5x_ctrl_func_el prim)
|
||||
{
|
||||
struct v5x_user_port *v5up = fi->priv;
|
||||
|
||||
v5x_le_ctrl_port_snd(v5up, prim);
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
/* ISDN port state FSM */
|
||||
/***********************************************************************/
|
||||
|
||||
static void isdn_up_le10_blocked(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_CUP_LE_FE102_ACTIV_INIT_USER_IND:
|
||||
case V5X_CUP_LE_FE103_DS_ACTIVATED_IND:
|
||||
case V5X_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
|
||||
case V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
|
||||
case V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK, 0, 0);
|
||||
/* Send FE201 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE201_UNBLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
/* Send FE203 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE203_BLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_FE202_UNBLOCK:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE12_NOP_REMOTE_UNBLOCK, 0, 0);
|
||||
/* Send MPU-UBR */
|
||||
mph_rcv(fi, MPH_UBR, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE204_BLOCK_CMD:
|
||||
case V5X_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void isdn_up_le11_local_unblock(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_CUP_LE_FE102_ACTIV_INIT_USER_IND:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED, 0, 0);
|
||||
/* send MPG-AWI */
|
||||
mph_rcv(fi, MPH_AWI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE103_DS_ACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED, 0, 0);
|
||||
/* send MPH-AI */
|
||||
mph_rcv(fi, MPH_AI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
|
||||
case V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
/* send FE201 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE201_UNBLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* send FE203 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE203_BLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_FE202_UNBLOCK:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
|
||||
/* send MPH-UBI */
|
||||
mph_rcv(fi, MPH_UBI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE204_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* send MPH-BI */
|
||||
mph_rcv(fi, MPH_BI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void isdn_up_le12_remote_unblock(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_CUP_LE_FE103_DS_ACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
|
||||
case V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
|
||||
/* send FE201 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE201_UNBLOCK);
|
||||
/* send MPH-UBI */
|
||||
mph_rcv(fi, MPH_UBI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* send FE203 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE203_BLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_FE202_UNBLOCK:
|
||||
/* send MPH-UBR */
|
||||
mph_rcv(fi, MPH_UBR, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE204_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* send MPH-BI */
|
||||
mph_rcv(fi, MPH_BI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* LE 2.0 Operational deactivated */
|
||||
static void isdn_up_le20_op_deact(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED, 0, 0);
|
||||
/* Send FE101 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE101_ACTIVATE_ACCESS);
|
||||
break;
|
||||
case V5X_CUP_LE_FE102_ACTIV_INIT_USER_IND:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED, 0, 0);
|
||||
/* Send MPH-AWI */
|
||||
mph_rcv(fi, MPH_AWI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE103_DS_ACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED, 0, 0);
|
||||
/* Send MPH-AI */
|
||||
mph_rcv(fi, MPH_AI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
|
||||
/* Send FE105 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE105_DEACTIVATE_ACCESS);
|
||||
break;
|
||||
case V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
|
||||
/* Send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
/* Send FE201 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE201_UNBLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* Send FE203 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE203_BLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_FE202_UNBLOCK:
|
||||
/* Send MPH-UBI */
|
||||
mph_rcv(fi, MPH_UBI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE204_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* Send MPH-BI */
|
||||
mph_rcv(fi, MPH_BI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* Send MPH-BR */
|
||||
mph_rcv(fi, MPH_BR, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE206_PREFORMANCE_GRADING_IND:
|
||||
/* Send MPH-GI */
|
||||
mph_rcv(fi, MPH_GI, *(uint8_t *)data);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* LE 2.1 Access initiated */
|
||||
static void isdn_up_le21_op_act_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ:
|
||||
case V5X_CUP_LE_FE102_ACTIV_INIT_USER_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_CUP_LE_FE103_DS_ACTIVATED_IND:
|
||||
/* Send MPH-DSAI */
|
||||
mph_rcv(fi, MPH_DSAI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED, 0, 0);
|
||||
/* Send MPH-AI */
|
||||
mph_rcv(fi, MPH_AI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
|
||||
/* Send FE105 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE105_DEACTIVATE_ACCESS);
|
||||
/* Send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
|
||||
/* Send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
/* Send FE201 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE201_UNBLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* Send FE203 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE203_BLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_FE202_UNBLOCK:
|
||||
/* Send MPH-UBI */
|
||||
mph_rcv(fi, MPH_UBI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE204_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* Send MPH-BI */
|
||||
mph_rcv(fi, MPH_BI, 0);
|
||||
/* Send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* Send MPH-BR */
|
||||
mph_rcv(fi, MPH_BR, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE206_PREFORMANCE_GRADING_IND:
|
||||
/* Send MPH-GI */
|
||||
mph_rcv(fi, MPH_GI, *(uint8_t *)data);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* LE 2.2 Access activated */
|
||||
static void isdn_up_le22_op_acc_act(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
|
||||
/* Send FE105 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE105_DEACTIVATE_ACCESS);
|
||||
/* Send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
|
||||
/* Send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
/* Send FE201 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE201_UNBLOCK);
|
||||
/* Send MPH-AI */
|
||||
mph_rcv(fi, MPH_AI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* Send FE203 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE203_BLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_FE202_UNBLOCK:
|
||||
/* Send MPH-UBI */
|
||||
mph_rcv(fi, MPH_UBI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE204_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* Send MPH-BI */
|
||||
mph_rcv(fi, MPH_BI, 0);
|
||||
/* Send MPH-DI */
|
||||
mph_rcv(fi, MPH_DI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* Send MPH-BR */
|
||||
mph_rcv(fi, MPH_BR, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE206_PREFORMANCE_GRADING_IND:
|
||||
/* Send MPH-GI */
|
||||
mph_rcv(fi, MPH_GI, *(uint8_t *)data);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_DB_BLOCK_DCHAN_REQ:
|
||||
/* Send FE207 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE207_D_CHANNEL_BLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_DU_BLOCK_DCHAN_REQ:
|
||||
/* Send FE208 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE208_D_CHANNEL_UNBLOCK);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Table 38/G.964 LE (ISDN port) FSM for ISDN basic access user ports */
|
||||
static const struct osmo_fsm_state v5x_le_ctrl_isdn_port_fsm_states[] = {
|
||||
[V5X_LE_UP_I_S_LE10_NOP_BLOCKED] = {
|
||||
.name = "Blocked (LE1.0)",
|
||||
.in_event_mask = S(V5X_CUP_LE_FE102_ACTIV_INIT_USER_IND) |
|
||||
S(V5X_CUP_LE_FE103_DS_ACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
|
||||
S(V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V5X_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE202_UNBLOCK) |
|
||||
S(V5X_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE205_BLOCK_REQ),
|
||||
.out_state_mask = S(V5X_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK) |
|
||||
S(V5X_LE_UP_I_S_LE12_NOP_REMOTE_UNBLOCK),
|
||||
.action = isdn_up_le10_blocked,
|
||||
},
|
||||
[V5X_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK] = {
|
||||
.name = "Local unblock (LE1.1)",
|
||||
.in_event_mask = S(V5X_CUP_LE_FE102_ACTIV_INIT_USER_IND) |
|
||||
S(V5X_CUP_LE_FE103_DS_ACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
|
||||
S(V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V5X_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE202_UNBLOCK) |
|
||||
S(V5X_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE205_BLOCK_REQ),
|
||||
.out_state_mask = S(V5X_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED) |
|
||||
S(V5X_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED) |
|
||||
S(V5X_LE_UP_I_S_LE10_NOP_BLOCKED) |
|
||||
S(V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED),
|
||||
.action = isdn_up_le11_local_unblock,
|
||||
},
|
||||
[V5X_LE_UP_I_S_LE12_NOP_REMOTE_UNBLOCK] = {
|
||||
.name = "Remote unblock (LE1.2)",
|
||||
.in_event_mask = S(V5X_CUP_LE_FE103_DS_ACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
|
||||
S(V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V5X_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE202_UNBLOCK) |
|
||||
S(V5X_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE205_BLOCK_REQ),
|
||||
.out_state_mask = S(V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED) |
|
||||
S(V5X_LE_UP_I_S_LE10_NOP_BLOCKED),
|
||||
.action = isdn_up_le12_remote_unblock,
|
||||
},
|
||||
[V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED] = {
|
||||
.name = "Operational deactivated (LE2.0)",
|
||||
.in_event_mask = S(V5X_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ) |
|
||||
S(V5X_CUP_LE_FE102_ACTIV_INIT_USER_IND) |
|
||||
S(V5X_CUP_LE_FE103_DS_ACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
|
||||
S(V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V5X_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE202_UNBLOCK) |
|
||||
S(V5X_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE205_BLOCK_REQ) |
|
||||
S(V5X_CUP_LE_FE206_PREFORMANCE_GRADING_IND),
|
||||
.out_state_mask = S(V5X_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED) |
|
||||
S(V5X_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED) |
|
||||
S(V5X_LE_UP_I_S_LE10_NOP_BLOCKED),
|
||||
.action = isdn_up_le20_op_deact,
|
||||
},
|
||||
[V5X_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED] = {
|
||||
.name = "Activation initiated (LE2.1)",
|
||||
.in_event_mask = S(V5X_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ) |
|
||||
S(V5X_CUP_LE_FE102_ACTIV_INIT_USER_IND) |
|
||||
S(V5X_CUP_LE_FE103_DS_ACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
|
||||
S(V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V5X_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE202_UNBLOCK) |
|
||||
S(V5X_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE205_BLOCK_REQ),
|
||||
.out_state_mask = S(V5X_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED) |
|
||||
S(V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED) |
|
||||
S(V5X_LE_UP_I_S_LE10_NOP_BLOCKED),
|
||||
.action = isdn_up_le21_op_act_init,
|
||||
},
|
||||
[V5X_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED] = {
|
||||
.name = "Access activated (LE2.2)",
|
||||
.in_event_mask = S(V5X_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
|
||||
S(V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
|
||||
S(V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V5X_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE202_UNBLOCK) |
|
||||
S(V5X_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE205_BLOCK_REQ) |
|
||||
S(V5X_CUP_LE_FE206_PREFORMANCE_GRADING_IND) |
|
||||
S(V5X_CUP_LE_MPH_DB_BLOCK_DCHAN_REQ) |
|
||||
S(V5X_CUP_LE_MPH_DU_BLOCK_DCHAN_REQ),
|
||||
.out_state_mask = S(V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED) |
|
||||
S(V5X_LE_UP_I_S_LE10_NOP_BLOCKED),
|
||||
.action = isdn_up_le22_op_acc_act,
|
||||
},
|
||||
};
|
||||
|
||||
struct osmo_fsm v5x_le_ctrl_isdn_port_fsm = {
|
||||
.name = "V5X_CTRL_ISDN",
|
||||
.states = v5x_le_ctrl_isdn_port_fsm_states,
|
||||
.num_states = ARRAY_SIZE(v5x_le_ctrl_isdn_port_fsm_states),
|
||||
.allstate_event_mask = 0,
|
||||
.allstate_action = NULL,
|
||||
.cleanup = NULL,
|
||||
.log_subsys = DV5PORT,
|
||||
.event_names = v5x_le_ctrl_port_fsm_event_names,
|
||||
};
|
||||
|
||||
struct osmo_fsm_inst *v5x_le_port_isdn_create(struct v5x_user_port *v5up, uint16_t nr)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
OSMO_ASSERT(v5up);
|
||||
|
||||
fi = osmo_fsm_inst_alloc(&v5x_le_ctrl_isdn_port_fsm, v5up, v5up, LOGL_DEBUG, NULL);
|
||||
if (!fi)
|
||||
return NULL;
|
||||
osmo_fsm_inst_update_id_f(fi, "%d", nr);
|
||||
|
||||
return fi;
|
||||
}
|
||||
|
||||
bool v5x_le_port_isdn_is_operational(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
return (fi->state >= V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED);
|
||||
}
|
||||
|
||||
void v5x_le_port_isdn_block(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
LOGP(DV5PORT, LOGL_DEBUG, "Put ISDN port into blocked state\n");
|
||||
fi->state = V5X_LE_UP_I_S_LE10_NOP_BLOCKED;
|
||||
}
|
||||
|
||||
void v5x_le_port_isdn_unblock(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
LOGP(DV5PORT, LOGL_DEBUG, "Put ISDN port into unblocked state\n");
|
||||
fi->state = V5X_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED;
|
||||
}
|
||||
|
||||
const char *v5x_le_port_isdn_state_name(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
return v5x_le_ctrl_isdn_port_fsm_states[fi->state].name;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
/* PSTN port state FSM */
|
||||
/***********************************************************************/
|
||||
|
||||
static void pstn_up_le10_blocked(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_P_S_LE11_NOP_LOCAL_UNBLOCK, 0, 0);
|
||||
/* Send FE201 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE201_UNBLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
/* Send FE203 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE203_BLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_FE202_UNBLOCK:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_P_S_LE12_NOP_REMOTE_UNBLOCK, 0, 0);
|
||||
/* Send MPU-UBR */
|
||||
mph_rcv(fi, MPH_UBR, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE204_BLOCK_CMD:
|
||||
case V5X_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void pstn_up_le11_local_unblock(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
/* send FE201 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE201_UNBLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_P_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* send FE203 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE203_BLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_FE202_UNBLOCK:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_P_S_LE20_OPERATIONAL, 0, 0);
|
||||
/* send MPH-UBI */
|
||||
mph_rcv(fi, MPH_UBI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE204_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_P_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* send MPH-BI */
|
||||
mph_rcv(fi, MPH_BI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void pstn_up_le12_remote_unblock(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_P_S_LE20_OPERATIONAL, 0, 0);
|
||||
/* send FE201 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE201_UNBLOCK);
|
||||
/* send MPH-UBI */
|
||||
mph_rcv(fi, MPH_UBI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_P_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* send FE203 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE203_BLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_FE202_UNBLOCK:
|
||||
/* send MPH-UBR */
|
||||
mph_rcv(fi, MPH_UBR, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE204_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_P_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* send MPH-BI */
|
||||
mph_rcv(fi, MPH_BI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* LE 2.0 Operational deactivated */
|
||||
static void pstn_up_le20_operational(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
/* Send FE201 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE201_UNBLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_P_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* Send FE203 */
|
||||
ctrl_snd(fi, V5X_CTRL_FE203_BLOCK);
|
||||
break;
|
||||
case V5X_CUP_LE_FE202_UNBLOCK:
|
||||
/* Send MPH-UBI */
|
||||
mph_rcv(fi, MPH_UBI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE204_BLOCK_CMD:
|
||||
osmo_fsm_inst_state_chg(fi, V5X_LE_UP_P_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
/* Send MPH-BI */
|
||||
mph_rcv(fi, MPH_BI, 0);
|
||||
break;
|
||||
case V5X_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* Send MPH-BR */
|
||||
mph_rcv(fi, MPH_BR, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Table 43/G.964 LE (ISDN port) FSM for PSTN user ports */
|
||||
static const struct osmo_fsm_state v5x_le_ctrl_pstn_port_fsm_states[] = {
|
||||
[V5X_LE_UP_P_S_LE10_NOP_BLOCKED] = {
|
||||
.name = "Blocked (LE1.0)",
|
||||
.in_event_mask = S(V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V5X_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE202_UNBLOCK) |
|
||||
S(V5X_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE205_BLOCK_REQ),
|
||||
.out_state_mask = S(V5X_LE_UP_P_S_LE11_NOP_LOCAL_UNBLOCK) |
|
||||
S(V5X_LE_UP_P_S_LE12_NOP_REMOTE_UNBLOCK),
|
||||
.action = pstn_up_le10_blocked,
|
||||
},
|
||||
[V5X_LE_UP_P_S_LE11_NOP_LOCAL_UNBLOCK] = {
|
||||
.name = "Local unblock (LE1.1)",
|
||||
.in_event_mask = S(V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V5X_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE202_UNBLOCK) |
|
||||
S(V5X_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE205_BLOCK_REQ),
|
||||
.out_state_mask = S(V5X_LE_UP_P_S_LE10_NOP_BLOCKED) |
|
||||
S(V5X_LE_UP_P_S_LE20_OPERATIONAL),
|
||||
.action = pstn_up_le11_local_unblock,
|
||||
},
|
||||
[V5X_LE_UP_P_S_LE12_NOP_REMOTE_UNBLOCK] = {
|
||||
.name = "Remote unblock (LE1.2)",
|
||||
.in_event_mask = S(V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V5X_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE202_UNBLOCK) |
|
||||
S(V5X_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE205_BLOCK_REQ),
|
||||
.out_state_mask = S(V5X_LE_UP_P_S_LE20_OPERATIONAL) |
|
||||
S(V5X_LE_UP_P_S_LE10_NOP_BLOCKED),
|
||||
.action = pstn_up_le12_remote_unblock,
|
||||
},
|
||||
[V5X_LE_UP_P_S_LE20_OPERATIONAL] = {
|
||||
.name = "Operational (LE2.0)",
|
||||
.in_event_mask = S(V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V5X_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE202_UNBLOCK) |
|
||||
S(V5X_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V5X_CUP_LE_FE205_BLOCK_REQ),
|
||||
.out_state_mask = S(V5X_LE_UP_P_S_LE10_NOP_BLOCKED),
|
||||
.action = pstn_up_le20_operational,
|
||||
},
|
||||
};
|
||||
|
||||
struct osmo_fsm v5x_le_ctrl_pstn_port_fsm = {
|
||||
.name = "V5X_CTRL_PSTN",
|
||||
.states = v5x_le_ctrl_pstn_port_fsm_states,
|
||||
.num_states = ARRAY_SIZE(v5x_le_ctrl_pstn_port_fsm_states),
|
||||
.allstate_event_mask = 0,
|
||||
.allstate_action = NULL,
|
||||
.cleanup = NULL,
|
||||
.log_subsys = DV5PORT,
|
||||
.event_names = v5x_le_ctrl_port_fsm_event_names,
|
||||
};
|
||||
|
||||
struct osmo_fsm_inst *v5x_le_port_pstn_create(struct v5x_user_port *v5up, uint16_t nr)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
OSMO_ASSERT(v5up);
|
||||
|
||||
fi = osmo_fsm_inst_alloc(&v5x_le_ctrl_pstn_port_fsm, v5up, v5up, LOGL_DEBUG, NULL);
|
||||
if (!fi)
|
||||
return NULL;
|
||||
osmo_fsm_inst_update_id_f(fi, "%d", nr);
|
||||
|
||||
return fi;
|
||||
}
|
||||
|
||||
void v5x_le_port_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = osmo_fsm_register(&v5x_le_ctrl_isdn_port_fsm);
|
||||
OSMO_ASSERT(!rc);
|
||||
rc = osmo_fsm_register(&v5x_le_ctrl_pstn_port_fsm);
|
||||
OSMO_ASSERT(!rc);
|
||||
LOGP(DV5PORT, LOGL_NOTICE, "Using V5x port control protocol\n");
|
||||
}
|
||||
|
||||
bool v5x_le_port_pstn_is_operational(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
return (fi->state >= V5X_LE_UP_P_S_LE20_OPERATIONAL);
|
||||
}
|
||||
|
||||
void v5x_le_port_pstn_block(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
LOGP(DV5PORT, LOGL_DEBUG, "Put PSTN port into blocked state\n");
|
||||
fi->state = V5X_LE_UP_P_S_LE10_NOP_BLOCKED;
|
||||
}
|
||||
|
||||
void v5x_le_port_pstn_unblock(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
LOGP(DV5PORT, LOGL_DEBUG, "Put PSTN port into unblocked state\n");
|
||||
fi->state = V5X_LE_UP_P_S_LE20_OPERATIONAL;
|
||||
}
|
||||
|
||||
const char *v5x_le_port_pstn_state_name(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
return v5x_le_ctrl_pstn_port_fsm_states[fi->state].name;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Messages from other layers
|
||||
***********************************************************************/
|
||||
|
||||
/* message from DL layer */
|
||||
void v5x_le_port_pstn_fe_rcv(struct v5x_user_port *v5up, uint8_t cfe)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = v5up->port_fi;
|
||||
enum v5x_le_ctrl_port_fsm_event event;
|
||||
|
||||
switch (cfe) {
|
||||
case V5X_CTRL_FE201_UNBLOCK: // actually FE202
|
||||
event = V5X_CUP_LE_FE202_UNBLOCK;
|
||||
break;
|
||||
case V5X_CTRL_FE203_BLOCK: // actually FE204
|
||||
event = V5X_CUP_LE_FE204_BLOCK_CMD;
|
||||
break;
|
||||
case V5X_CTRL_FE205_BLOCK_REQ:
|
||||
event = V5X_CUP_LE_FE205_BLOCK_REQ;
|
||||
break;
|
||||
default:
|
||||
LOGP(DV5PORT, LOGL_NOTICE, "Received cfe %d not valid at this protocol\n", cfe);
|
||||
}
|
||||
|
||||
/* send event to FSM */
|
||||
osmo_fsm_inst_dispatch(fi, event, NULL);
|
||||
}
|
||||
|
||||
/* message from DL layer */
|
||||
void v5x_le_port_isdn_fe_rcv(struct v5x_user_port *v5up, uint8_t cfe, uint8_t perf_grading)
|
||||
{
|
||||
enum v5x_le_ctrl_port_fsm_event event;
|
||||
|
||||
switch (cfe) {
|
||||
case V5X_CTRL_FE102_ACT_INIT_BY_USER:
|
||||
event = V5X_CUP_LE_FE102_ACTIV_INIT_USER_IND;
|
||||
break;
|
||||
case V5X_CTRL_FE103_DS_ACTIVATED:
|
||||
event = V5X_CUP_LE_FE103_DS_ACTIVATED_IND;
|
||||
break;
|
||||
case V5X_CTRL_FE104_ACCESS_ACTIVATED:
|
||||
event = V5X_CUP_LE_FE104_ACCESS_ACTIVATED_IND;
|
||||
break;
|
||||
case V5X_CTRL_FE106_ACCESS_DEACTIVATED:
|
||||
event = V5X_CUP_LE_FE106_ACCESS_DEACTIVATED_IND;
|
||||
break;
|
||||
case V5X_CTRL_FE201_UNBLOCK: // actually FE202
|
||||
event = V5X_CUP_LE_FE202_UNBLOCK;
|
||||
break;
|
||||
case V5X_CTRL_FE203_BLOCK: // actually FE204
|
||||
event = V5X_CUP_LE_FE204_BLOCK_CMD;
|
||||
break;
|
||||
case V5X_CTRL_FE205_BLOCK_REQ:
|
||||
event = V5X_CUP_LE_FE205_BLOCK_REQ;
|
||||
break;
|
||||
case V5X_CTRL_FE206_PERFORMANCE_GRADING:
|
||||
event = V5X_CUP_LE_FE206_PREFORMANCE_GRADING_IND;
|
||||
break;
|
||||
default:
|
||||
LOGP(DV5PORT, LOGL_NOTICE, "Received cfe %d not valid at this protocol\n", cfe);
|
||||
break;
|
||||
}
|
||||
|
||||
/* send event to FSM */
|
||||
osmo_fsm_inst_dispatch(v5up->port_fi, event, &perf_grading);
|
||||
}
|
||||
|
||||
/* message from management layer */
|
||||
void v5x_port_mph_snd(struct v5x_user_port *v5up, enum v5x_mph_prim prim)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = v5up->port_fi;
|
||||
enum v5x_le_ctrl_port_fsm_event event;
|
||||
|
||||
switch (prim) {
|
||||
case MPH_UBR:
|
||||
event = V5X_CUP_LE_MPH_UBR_UNBLOCK_REQ;
|
||||
break;
|
||||
case MPH_BI:
|
||||
event = V5X_CUP_LE_MPH_BI_BLOCK_CMD;
|
||||
break;
|
||||
case MPH_AR:
|
||||
event = V5X_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ;
|
||||
break;
|
||||
case MPH_DR:
|
||||
event = V5X_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ;
|
||||
break;
|
||||
case MPH_DB:
|
||||
event = V5X_CUP_LE_MPH_DB_BLOCK_DCHAN_REQ;
|
||||
break;
|
||||
case MPH_DU:
|
||||
event = V5X_CUP_LE_MPH_DU_BLOCK_DCHAN_REQ;
|
||||
break;
|
||||
default:
|
||||
LOGP(DV5PORT, LOGL_NOTICE, "Got invalid prim %d at this protocol\n", prim);
|
||||
return;
|
||||
}
|
||||
|
||||
/* send event to FSM */
|
||||
osmo_fsm_inst_dispatch(fi, event, NULL);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
void v5x_port_mph_snd(struct v5x_user_port *v5up, enum v5x_mph_prim prim);
|
||||
|
||||
struct osmo_fsm_inst *v5x_le_port_isdn_create(struct v5x_user_port *v5up, uint16_t nr);
|
||||
struct osmo_fsm_inst *v5x_le_port_pstn_create(struct v5x_user_port *v5up, uint16_t nr);
|
||||
bool v5x_le_port_isdn_is_operational(struct osmo_fsm_inst *fi);
|
||||
void v5x_le_port_isdn_block(struct osmo_fsm_inst *fi);
|
||||
void v5x_le_port_isdn_unblock(struct osmo_fsm_inst *fi);
|
||||
bool v5x_le_port_pstn_is_operational(struct osmo_fsm_inst *fi);
|
||||
void v5x_le_port_pstn_block(struct osmo_fsm_inst *fi);
|
||||
void v5x_le_port_pstn_unblock(struct osmo_fsm_inst *fi);
|
||||
const char *v5x_le_port_isdn_state_name(struct osmo_fsm_inst *fi);
|
||||
const char *v5x_le_port_pstn_state_name(struct osmo_fsm_inst *fi);
|
||||
|
||||
void v5x_le_port_isdn_fe_rcv(struct v5x_user_port *v5up, uint8_t cfe, uint8_t perf_grading);
|
||||
void v5x_le_port_pstn_fe_rcv(struct v5x_user_port *v5up, uint8_t cfe);
|
||||
|
||||
void v5x_le_port_init(void);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
|||
|
||||
struct v5x_pstn_proto *v5x_le_pstn_create(struct v5x_user_port *v5up, uint16_t nr);
|
||||
void v5x_le_pstn_destroy(struct v5x_pstn_proto *pstn);
|
||||
const char *v5x_le_pstn_state_name(struct v5x_pstn_proto *pstn);
|
||||
const char *v5x_l1_fsm_state_name(struct v5x_l1_proto *l1);
|
||||
void v5x_le_pstn_init(void);
|
||||
void v5x_le_pstn_fe_snd(struct v5x_user_port *v5up, enum v5x_fe_prim prim, struct msgb *msg);
|
||||
void v5x_le_pstn_mdu_snd(struct v5x_user_port *v5up, enum v5x_mgmt_prim prim);
|
||||
int v5x_le_pstn_dl_rcv(struct v5x_user_port *v5up, uint8_t msg_type, const struct tlv_parsed *tp);
|
|
@ -0,0 +1,619 @@
|
|||
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
#include <osmocom/gsm/lapd_core.h>
|
||||
|
||||
#include "logging.h"
|
||||
#include "v5x_internal.h"
|
||||
#include "v5x_protocol.h"
|
||||
#include "lapv5.h"
|
||||
#include "v5x_le_ctrl_fsm.h"
|
||||
#include "v5x_le_port_fsm.h"
|
||||
#include "v5x_le_pstn_fsm.h"
|
||||
#include "v52_le_bcc_fsm.h"
|
||||
#include "v52_le_pp_fsm.h"
|
||||
#include "v5x_le_management.h"
|
||||
|
||||
const struct tlv_definition v5x_ctrl_tlv_def = {
|
||||
.def = {
|
||||
/* single byte: PSTN / G.964 Table 17 */
|
||||
[V5X_CTRL_IEI_PULSE_NOTIFICATION] = { TLV_TYPE_SINGLE_TV, 0 },
|
||||
[V5X_CTRL_IEI_LINE_INFORMATION] = { TLV_TYPE_SINGLE_TV, 0 },
|
||||
[V5X_CTRL_IEI_STATE] = { TLV_TYPE_SINGLE_TV, 0 },
|
||||
[V5X_CTRL_IEI_AUTONOMOUS_SIG_SEQ] = { TLV_TYPE_SINGLE_TV, 0 },
|
||||
[V5X_CTRL_IEI_SEQUENCE_RESPONSE] = { TLV_TYPE_SINGLE_TV, 0 },
|
||||
/* single byte: ISDN / G.964 Table 53 */
|
||||
[V5X_CTRL_IEI_PERFORMANCE_GRADING] = { TLV_TYPE_SINGLE_TV, 0 },
|
||||
[V5X_CTRL_IEI_REJECTION_CAUSE] = { TLV_TYPE_SINGLE_TV, 0 },
|
||||
|
||||
/* variable length: PSTN / G.964 Table 17 */
|
||||
[V5X_CTRL_IEI_SEQUENCE_NR] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_CADENCED_RINGING] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_PULSED_SIGNAL] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_STEADY_SIGNAL] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_DIGIT_SIGNAL] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_RECOGNITION_TIME] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_ENABLE_AUTONOMOUS_ACK] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_DISABLE_AUTONOMOUS_ACK] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_CAUSE] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_RESOURCE_UNAVAILABLE] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_ENABLE_METERING] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_METERING_REPORT] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_ATTENUATION] = { TLV_TYPE_TLV, 0 },
|
||||
|
||||
/* variable length: ISDN / G.964 Table 53 */
|
||||
[V5X_CTRL_IEI_CTRL_F_ELEMENT] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_CTRL_F_ID] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_VARIANT] = { TLV_TYPE_TLV, 0 },
|
||||
[V5X_CTRL_IEI_INTERFACE_ID] = { TLV_TYPE_TLV, 0 },
|
||||
|
||||
/* variable length: LCP / G.965 Table FIXME */
|
||||
[V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION] = { TLV_TYPE_TLV, 0 },
|
||||
/* variable length: BCC */
|
||||
[V52_CTRL_IEI_BCC_USER_PORT_ID] = { TLV_TYPE_TLV, 0 },
|
||||
[V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID] = { TLV_TYPE_TLV, 0 },
|
||||
[V52_CTRL_IEI_BCC_V5_TS_ID] = { TLV_TYPE_TLV, 0 },
|
||||
[V52_CTRL_IEI_BCC_MULTI_TS_MAP] = { TLV_TYPE_TLV, 0 },
|
||||
[V52_CTRL_IEI_BCC_REJECT_CAUSE] = { TLV_TYPE_TLV, 0 },
|
||||
[V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE] = { TLV_TYPE_TLV, 0 },
|
||||
[V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE] = { TLV_TYPE_TLV, 0 },
|
||||
[V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY] = { TLV_TYPE_TLV, 0 },
|
||||
/* variable-length: Protection */
|
||||
[V52_CTRL_IEI_PP_SEQUENCE_NR] = { TLV_TYPE_TLV, 0 },
|
||||
[V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID] = { TLV_TYPE_TLV, 0 },
|
||||
[V52_CTRL_IEI_PP_REJECTION_CAUSE] = { TLV_TYPE_TLV, 0 },
|
||||
[V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE] = { TLV_TYPE_TLV, 0 },
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
const struct value_string v5x_ctrl_msg_typ_str[] = {
|
||||
{ V5X_CTRL_MSGT_ESTABLISH, "ESTABLISH" },
|
||||
{ V5X_CTRL_MSGT_ESTABLISH_ACK, "ESTABLISH_ACK" },
|
||||
{ V5X_CTRL_MSGT_SIGNAL, "SIGNAL" },
|
||||
{ V5X_CTRL_MSGT_SIGNAL_ACK, "SIGNAL_ACK" },
|
||||
{ V5X_CTRL_MSGT_DISCONNECT, "DISCONNECT" },
|
||||
{ V5X_CTRL_MSGT_DISCONNECT_COMPLETE, "DISCONNECT_COMPLETE" },
|
||||
{ V5X_CTRL_MSGT_STATUS_ENQUIRY, "STATUS_ENQUIRY" },
|
||||
{ V5X_CTRL_MSGT_STATUS, "STATUS" },
|
||||
{ V5X_CTRL_MSGT_PROTOCOL_PARAMETER, "PROTOCOL_PARAMETER" },
|
||||
{ V5X_CTRL_MSGT_PORT_CTRL, "PORT_CTRL" },
|
||||
{ V5X_CTRL_MSGT_PORT_CTRL_ACK, "PORT_CTRL_ACK" },
|
||||
{ V5X_CTRL_MSGT_COMMON_CTRL, "COMMON_CTRL" },
|
||||
{ V5X_CTRL_MSGT_COMMON_CTRL_ACK, "COMMON_CTRL_ACK" },
|
||||
{ V52_CTRL_MSGT_PP_SWITCH_OVER_REQ, "SWITCH_OVER_REQ" },
|
||||
{ V52_CTRL_MSGT_PP_SWITCH_OVER_COM, "SWITCH_OVER_COM" },
|
||||
{ V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM, "OS_SWITCH_OVER_COM" },
|
||||
{ V52_CTRL_MSGT_PP_SWITCH_OVER_ACK, "SWITCH_OVER_ACK" },
|
||||
{ V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT, "SWITCH_OVER_REJECT" },
|
||||
{ V52_CTRL_MSGT_PP_PROTOCOL_ERROR, "PROTOCOL_ERROR" },
|
||||
{ V52_CTRL_MSGT_PP_RESET_SN_COM, "RESET_SN_COM" },
|
||||
{ V52_CTRL_MSGT_PP_RESET_SN_ACK, "RESET_SN_ACK" },
|
||||
{ V52_CTRL_MSGT_ALLOCATION, "ALLOCATION" },
|
||||
{ V52_CTRL_MSGT_ALLOCATION_COMPLETE, "ALLOCATION_COMPLETE" },
|
||||
{ V52_CTRL_MSGT_ALLOCATION_REJECT, "ALLOCATION_REJECT" },
|
||||
{ V52_CTRL_MSGT_DE_ALLOCATION, "DE_ALLOCATION" },
|
||||
{ V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE, "DE_ALLOCATION_COMPLETE" },
|
||||
{ V52_CTRL_MSGT_DE_ALLOCATION_REJECT, "DE_ALLOCATION_REJECT" },
|
||||
{ V52_CTRL_MSGT_AUDIT, "AUDIT" },
|
||||
{ V52_CTRL_MSGT_AUDIT_COMPLETE, "AUDIT_COMPLETE" },
|
||||
{ V52_CTRL_MSGT_AN_FAULT, "AN_FAULT" },
|
||||
{ V52_CTRL_MSGT_AN_FAULT_ACK, "AN_FAULT_ACKNOWLEDGE" },
|
||||
{ V52_CTRL_MSGT_PROTOCOL_ERROR, "PROTOCOL_ERROR" },
|
||||
{ V52_CTRL_MSGT_LCP_LINK_CTRL, "LINK_CTRL" },
|
||||
{ V52_CTRL_MSGT_LCP_LINK_CTRL_ACK, "LINK_CTRL_ACK" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string v5x_ctrl_iei_str[] = {
|
||||
{ V5X_CTRL_IEI_PULSE_NOTIFICATION, "PULSE_NOTIFICATION" },
|
||||
{ V5X_CTRL_IEI_LINE_INFORMATION, "LINE_INFORMATION" },
|
||||
{ V5X_CTRL_IEI_STATE, "STATE" },
|
||||
{ V5X_CTRL_IEI_AUTONOMOUS_SIG_SEQ, "AUTONOMOUS_SIG_SEQ" },
|
||||
{ V5X_CTRL_IEI_SEQUENCE_RESPONSE, "SEQUENCE_RESPONSE" },
|
||||
{ V5X_CTRL_IEI_PERFORMANCE_GRADING, "PERFORMANCE_GRADING" },
|
||||
{ V5X_CTRL_IEI_REJECTION_CAUSE, "REJECTION_CAUSE" },
|
||||
{ V5X_CTRL_IEI_SEQUENCE_NR, "SEQUENCE_NR" },
|
||||
{ V5X_CTRL_IEI_CADENCED_RINGING, "CADENCED_RINGING" },
|
||||
{ V5X_CTRL_IEI_PULSED_SIGNAL, "PULSED_SIGNAL" },
|
||||
{ V5X_CTRL_IEI_STEADY_SIGNAL, "STEADY_SIGNAL" },
|
||||
{ V5X_CTRL_IEI_DIGIT_SIGNAL, "DIGIT_SIGNAL" },
|
||||
{ V5X_CTRL_IEI_RECOGNITION_TIME, "RECOGNITION_TIME" },
|
||||
{ V5X_CTRL_IEI_ENABLE_AUTONOMOUS_ACK, "ENABLE_AUTONOMOUS_ACK" },
|
||||
{ V5X_CTRL_IEI_DISABLE_AUTONOMOUS_ACK, "DISABLE_AUTONOMOUS_ACK" },
|
||||
{ V5X_CTRL_IEI_CAUSE, "CAUSE" },
|
||||
{ V5X_CTRL_IEI_RESOURCE_UNAVAILABLE, "RESOURCE_UNAVAILABLE" },
|
||||
{ V5X_CTRL_IEI_ENABLE_METERING, "ENABLE_METERING" },
|
||||
{ V5X_CTRL_IEI_METERING_REPORT, "METERING_REPORT" },
|
||||
{ V5X_CTRL_IEI_ATTENUATION, "ATTENUATION" },
|
||||
{ V5X_CTRL_IEI_CTRL_F_ELEMENT, "CTRL_F_ELEMENT" },
|
||||
{ V5X_CTRL_IEI_CTRL_F_ID, "CTRL_F_ID" },
|
||||
{ V5X_CTRL_IEI_VARIANT, "VARIANT" },
|
||||
{ V5X_CTRL_IEI_INTERFACE_ID, "INTERFACE_ID" },
|
||||
{ V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION, "LCP_LINK_CTRL_FUNCTION" },
|
||||
{ V52_CTRL_IEI_BCC_USER_PORT_ID, "BCC_USER_PORT_ID" },
|
||||
{ V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID, "BCC_ISDN_PORT_TS_ID" },
|
||||
{ V52_CTRL_IEI_BCC_V5_TS_ID, "BCC_V5_TS_ID" },
|
||||
{ V52_CTRL_IEI_BCC_MULTI_TS_MAP, "BCC_MULTI_TS_MAP" },
|
||||
{ V52_CTRL_IEI_BCC_REJECT_CAUSE, "BCC_REJECT_CAUSE" },
|
||||
{ V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE, "BCC_PROTOCOL_ERROR_CAUSE" },
|
||||
{ V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE, "BCC_CONNECTION_INCOMPLETE" },
|
||||
{ V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY, "BCC_INFO_TRANSFER_CAPABILITY" },
|
||||
{ V52_CTRL_IEI_PP_SEQUENCE_NR, "PP_SEQUENCE_NR" },
|
||||
{ V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID, "PP_PHYSICAL_C_CHAN_ID" },
|
||||
{ V52_CTRL_IEI_PP_REJECTION_CAUSE, "PP_REJECTION_CAUSE" },
|
||||
{ V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE, "PP_PROTOCOL_ERROR_CAUSE" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string v5x_ctrl_func_el_str[] = {
|
||||
{ V5X_CTRL_FE101_ACTIVATE_ACCESS, "FE101_ACTIVATE_ACCESS" },
|
||||
{ V5X_CTRL_FE102_ACT_INIT_BY_USER, "FE102_ACT_INIT_BY_USER" },
|
||||
{ V5X_CTRL_FE103_DS_ACTIVATED, "FE103_DS_ACTIVATED" },
|
||||
{ V5X_CTRL_FE104_ACCESS_ACTIVATED, "FE104_ACCESS_ACTIVATED" },
|
||||
{ V5X_CTRL_FE105_DEACTIVATE_ACCESS, "FE105_DEACTIVATE_ACCESS" },
|
||||
{ V5X_CTRL_FE106_ACCESS_DEACTIVATED, "FE106_ACCESS_DEACTIVATED" },
|
||||
{ V5X_CTRL_FE201_UNBLOCK, "FE201_UNBLOCK" },
|
||||
{ V5X_CTRL_FE203_BLOCK, "FE203_BLOCK" },
|
||||
{ V5X_CTRL_FE205_BLOCK_REQ, "FE205_BLOCK_REQ" },
|
||||
{ V5X_CTRL_FE206_PERFORMANCE_GRADING, "FE206_PERFORMANCE_GRADING" },
|
||||
{ V5X_CTRL_FE207_D_CHANNEL_BLOCK, "FE207_D_CHANNEL_BLOCK" },
|
||||
{ V5X_CTRL_FE208_D_CHANNEL_UNBLOCK, "FE208_D_CHANNEL_UNBLOCK" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string v5x_ctrl_func_id_str[] = {
|
||||
{ V5X_CTRL_ID_VERIFY_RE_PROVISIONING, "VERIFY_RE_PROVISIONING" },
|
||||
{ V5X_CTRL_ID_READY_FOR_RE_PROVISIONING, "READY_FOR_RE_PROVISIONING" },
|
||||
{ V5X_CTRL_ID_NOT_READY_FOR_RE_PROVISIONING, "NOT_READY_FOR_RE_PROVISIONING" },
|
||||
{ V5X_CTRL_ID_SWITCH_OVER_TO_NEW_VARIANT, "SWITCH_OVER_TO_NEW_VARIANT" },
|
||||
{ V5X_CTRL_ID_RE_PROVISIONING_STARTED, "RE_PROVISIONING_STARTED" },
|
||||
{ V5X_CTRL_ID_CANNOT_RE_PROVISION, "CANNOT_RE_PROVISION" },
|
||||
{ V5X_CTRL_ID_REQUEST_VARIANT_AND_INTERFACE_ID, "REQUEST_VARIANT_AND_INTERFACE_ID" },
|
||||
{ V5X_CTRL_ID_VARIANT_AND_INTERFACE_ID, "VARIANT_AND_INTERFACE_ID" },
|
||||
{ V5X_CTRL_ID_BLOCKING_STARTED, "BLOCKING_STARTED" },
|
||||
{ V5X_CTRL_ID_RESTART_REQUEST, "RESTART_REQUEST" },
|
||||
{ V5X_CTRL_ID_RESTART_COMPLETE, "RESTART_COMPLETE" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string v5x_cause_type_str[] = {
|
||||
{ V5X_CAUSE_T_RESP_TO_STATUS_ENQ, "Response to status enquiry" },
|
||||
{ V5X_CAUSE_T_PROTOCOL_DISC_ERROR, "Protocol discriminator error" },
|
||||
{ V5X_CAUSE_T_MSG_TYPE_UNRECOGNIZED, "Message type unrecognized" },
|
||||
{ V5X_CAUSE_T_OUT_OF_SEQUENCE_IE, "Out of sequence information element" },
|
||||
{ V5X_CAUSE_T_REPEATED_OPT_IE, "Repeated optional information element" },
|
||||
{ V5X_CAUSE_T_MAND_IE_MISSING, "Mandatory information element missing" },
|
||||
{ V5X_CAUSE_T_UNRECOGNIZED_IE, "Unrecognized information element" },
|
||||
{ V5X_CAUSE_T_MAND_IE_CONTENT_ERROR, "Mandatory information element content error" },
|
||||
{ V5X_CAUSE_T_OPT_IE_CONTENT_ERROR, "Optional information element content error" },
|
||||
{ V5X_CAUSE_T_MSG_INCOMP_STATE, "Message not compatible with protocol state" },
|
||||
{ V5X_CAUSE_T_REPEATED_MAND_IE, "Repeated mandatory information element" },
|
||||
{ V5X_CAUSE_T_TOO_MANY_IE, "Too many information elements" },
|
||||
{ V5X_CAUSE_T_REF_NR_CODING_ERROR, "Reference Number coding error" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string v52_bcc_reject_cause_type_str[] = {
|
||||
{ V52_BCC_REJECT_CAUSE_T_UNSPECIFIED, "Unspecified" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_ACCESS_NET_FAUL, "Access network fault" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_ACCESS_NET_BLOCKED, "Access network blocked (internally)" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_CONN_PRESENT_PSTN,
|
||||
"Connection already present at the PSTN user port to a different V5-time slot" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_CONN_PRESENT_V5_TS,
|
||||
"Connection already present at the V5-time slot(s) to a different port or ISDN user port time slot" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_CONN_PRESENT_ISDN,
|
||||
"Connection already present at the ISDN user port time slot(s) to a different V5-time slot(s)" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_USER_PORT_UNAVAILABLE, "User port unavailable (blocked)" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_DEALLOC_INCOMPAT_DATA,
|
||||
"De-allocation cannot be completed due to incompatible data content" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_DEALLOC_V5_TS_DATA,
|
||||
"De-allocation cannot be completed due to V5-time slot(s) data incompatibility" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_DEALLOC_PORT_DATA,
|
||||
"De-allocation cannot be completed due to port data incompatibility" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_DEALLOC_USER_TS_DATA,
|
||||
"De-allocation cannot be completed due to user port time slot(s) data incompatibility" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_USER_NOT_PROVISIONED, "User port not provisioned" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_INVALID_V5_TS_IDENT, "Invalid V5-time slot(s) identification(s)" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_INVALID_V5_LINK_IDENT, "Invalid V5 2048 kbit/s link identification" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_INVALID_USER_TS_IDENT, "Invalid user port time slot(s) identification(s)" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_V5_TS_USED_CCHANNEL, "V5-time slot(s) being used as physical C-channel(s)" },
|
||||
{ V52_BCC_REJECT_CAUSE_T_V5_LINK_UNAVAILABLE, "V5-link unavailable (blocked)" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string v52_pp_reject_cause_type_str[] = {
|
||||
{ V52_PP_REJECT_CAUSE_T_NO_STANDBY_CC_AVAILABLE,"No standby C-channel available" },
|
||||
{ V52_PP_REJECT_CAUSE_T_TARGET_CC_NOT_OPER, "Target physical C-channel not operational" },
|
||||
{ V52_PP_REJECT_CAUSE_T_TARGET_CC_NOT_PROV, "Target physical C-channel not provisioned" },
|
||||
{ V52_PP_REJECT_CAUSE_T_PROT_SWITCHING_IMPOSS, "Protection switching impossible (AN/LE failure)" },
|
||||
{ V52_PP_REJECT_CAUSE_T_PROT_GROUP_MISMATCH, "Protection group mismatch" },
|
||||
{ V52_PP_REJECT_CAUSE_T_ALLOC_EXISTS_ALREADY, "Requested allocation exists already" },
|
||||
{ V52_PP_REJECT_CAUSE_T_TARGET_CC_ALREADY_CC, "Target physical C-channel already has logical C-channel" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
|
||||
static const uint8_t signal_mand_ies[] = { V5X_CTRL_IEI_SEQUENCE_NR };
|
||||
static const uint8_t signal_ack_mand_ies[] = { V5X_CTRL_IEI_SEQUENCE_NR };
|
||||
static const uint8_t status_mand_ies[] = { V5X_CTRL_IEI_STATE, V5X_CTRL_IEI_CAUSE };
|
||||
static const uint8_t prot_par_mand_ies[] = { V5X_CTRL_IEI_SEQUENCE_NR };
|
||||
static const uint8_t port_ctrl_mand_ies[] = { V5X_CTRL_IEI_CTRL_F_ELEMENT };
|
||||
static const uint8_t common_ctrl_mand_ies[] = { V5X_CTRL_IEI_CTRL_F_ID };
|
||||
static const uint8_t lcp_mand_ies[] = { V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION };
|
||||
static const uint8_t alloc_mand_ies[] = { V52_CTRL_IEI_BCC_USER_PORT_ID };
|
||||
static const uint8_t alloc_rej_mand_ies[] = { V52_CTRL_IEI_BCC_REJECT_CAUSE };
|
||||
static const uint8_t prot_err_mand_ies[] = { V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE };
|
||||
static const uint8_t pp_swo_mand_ies[] = {
|
||||
V52_CTRL_IEI_PP_SEQUENCE_NR,
|
||||
V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID
|
||||
};
|
||||
static const uint8_t pp_swo_rej_mand_ies[] = {
|
||||
V52_CTRL_IEI_PP_SEQUENCE_NR,
|
||||
V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID,
|
||||
V52_CTRL_IEI_PP_REJECTION_CAUSE,
|
||||
};
|
||||
static const uint8_t pp_perr_mand_ies[] = {
|
||||
V52_CTRL_IEI_PP_SEQUENCE_NR,
|
||||
V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE,
|
||||
};
|
||||
|
||||
|
||||
const struct osmo_tlv_prot_def v5x_ctrl_msg_tlv = {
|
||||
.name = "V5X_CTRL",
|
||||
.tlv_def = &v5x_ctrl_tlv_def,
|
||||
.msg_def = {
|
||||
/* G.964 Section 13.3 */
|
||||
[V5X_CTRL_MSGT_ESTABLISH] = MSG_DEF("ESTABLISH", NULL, 0),
|
||||
[V5X_CTRL_MSGT_ESTABLISH_ACK] = MSG_DEF("ESTABLISH", NULL, 0),
|
||||
[V5X_CTRL_MSGT_SIGNAL] = MSG_DEF("SIGNAL", signal_mand_ies, 0),
|
||||
[V5X_CTRL_MSGT_SIGNAL_ACK] = MSG_DEF("SIGNAL_ACK", signal_ack_mand_ies, 0),
|
||||
[V5X_CTRL_MSGT_STATUS] = MSG_DEF("STATUS", status_mand_ies, 0),
|
||||
[V5X_CTRL_MSGT_STATUS_ENQUIRY] = MSG_DEF("STATUS_ENQUIRY", NULL, 0),
|
||||
[V5X_CTRL_MSGT_DISCONNECT] = MSG_DEF("DISCONNECT", NULL, 0),
|
||||
[V5X_CTRL_MSGT_DISCONNECT_COMPLETE] = MSG_DEF("DISCONNECT_COMPLETE", NULL, 0),
|
||||
[V5X_CTRL_MSGT_PROTOCOL_PARAMETER] = MSG_DEF("PROTOCOL_PARAMETER", prot_par_mand_ies, 0),
|
||||
/* G.964 Section 14.4 */
|
||||
[V5X_CTRL_MSGT_PORT_CTRL] = MSG_DEF("PORT_CTRL", port_ctrl_mand_ies, 0),
|
||||
[V5X_CTRL_MSGT_PORT_CTRL_ACK] = MSG_DEF("PORT_CTRL_ACK", port_ctrl_mand_ies, 0),
|
||||
[V5X_CTRL_MSGT_COMMON_CTRL] = MSG_DEF("COMMON_CTRL", common_ctrl_mand_ies, 0),
|
||||
[V5X_CTRL_MSGT_COMMON_CTRL_ACK] = MSG_DEF("COMMON_CTRL_ACK", common_ctrl_mand_ies, 0),
|
||||
/* G.965 Section 16.3 LCP */
|
||||
[V52_CTRL_MSGT_LCP_LINK_CTRL] = MSG_DEF("LINK_CONTROL", lcp_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_LCP_LINK_CTRL_ACK] = MSG_DEF("LINK_CONTROL_ACK", lcp_mand_ies, 0),
|
||||
/* G.965 Section 17.3 BCC */
|
||||
[V52_CTRL_MSGT_ALLOCATION] = MSG_DEF("ALLOCATION", alloc_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_ALLOCATION_COMPLETE] = MSG_DEF("ALLOCATION_COMPLETE", NULL, 0),
|
||||
[V52_CTRL_MSGT_ALLOCATION_REJECT] = MSG_DEF("ALLOCATION_REJECT", alloc_rej_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_DE_ALLOCATION] = MSG_DEF("DE_ALLOCATION", alloc_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE] = MSG_DEF("DE_ALLOCATION_COMPLETE", NULL, 0),
|
||||
[V52_CTRL_MSGT_DE_ALLOCATION_REJECT] = MSG_DEF("DE_ALLOCATION_REJECT", alloc_rej_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_AUDIT] = MSG_DEF("AUDIT", NULL, 0),
|
||||
[V52_CTRL_MSGT_AUDIT_COMPLETE] = MSG_DEF("AUDIT_COMPLETE", NULL, 0),
|
||||
[V52_CTRL_MSGT_AN_FAULT] = MSG_DEF("AN_FAULT", NULL, 0),
|
||||
[V52_CTRL_MSGT_AN_FAULT_ACK] = MSG_DEF("AN_FAULT_ACK", NULL, 0),
|
||||
[V52_CTRL_MSGT_PROTOCOL_ERROR] = MSG_DEF("PROTOCOL_ERROR", prot_err_mand_ies, 0),
|
||||
/* G.965 Section 18.3 PP */
|
||||
[V52_CTRL_MSGT_PP_SWITCH_OVER_REQ] = MSG_DEF("SWITCH_OVER_REQ", pp_swo_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_PP_SWITCH_OVER_COM] = MSG_DEF("SWITCH_OVER_COM", pp_swo_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM] = MSG_DEF("OS_SWITCH_OVER_COM", pp_swo_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_PP_SWITCH_OVER_ACK] = MSG_DEF("SWITCH_OVER_ACK", pp_swo_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT] = MSG_DEF("SWITCH_OVER_REJECT", pp_swo_rej_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_PP_PROTOCOL_ERROR] = MSG_DEF("PROTOCOL_ERROR", pp_perr_mand_ies, 0),
|
||||
|
||||
[V52_CTRL_MSGT_PP_RESET_SN_COM] = MSG_DEF("RESET_SN_COM", NULL, 0),
|
||||
[V52_CTRL_MSGT_PP_RESET_SN_ACK] = MSG_DEF("RESET_SN_ACK", NULL, 0),
|
||||
|
||||
},
|
||||
.ie_def = {
|
||||
/* single byte, only upper nibble matches IEI */
|
||||
[V5X_CTRL_IEI_PULSE_NOTIFICATION] = { 0, "PULSE_NOTIFICATION" },
|
||||
[V5X_CTRL_IEI_LINE_INFORMATION] = { 0, "LINE_INFORMATION" },
|
||||
[V5X_CTRL_IEI_STATE] = { 0, "STATE" },
|
||||
[V5X_CTRL_IEI_AUTONOMOUS_SIG_SEQ] = { 0, "AUTONOMOUS_SIG_SEQ" },
|
||||
[V5X_CTRL_IEI_SEQUENCE_RESPONSE] = { 0, "SEQUENCE_RESPONSE" },
|
||||
/* single byte: ISDN */
|
||||
[V5X_CTRL_IEI_PERFORMANCE_GRADING] = { 0, "PERFORMANCE_GRADING" },
|
||||
[V5X_CTRL_IEI_REJECTION_CAUSE] = { 0, "REJECTION_CAUSE" },
|
||||
/* variable length: PSTN */
|
||||
[V5X_CTRL_IEI_SEQUENCE_NR] = { 1, "SEQUENCE_NR" },
|
||||
[V5X_CTRL_IEI_CADENCED_RINGING] = { 1, "CADENCED_RINGING" },
|
||||
[V5X_CTRL_IEI_PULSED_SIGNAL] = { 1, "PULSED_SIGNAL" },
|
||||
[V5X_CTRL_IEI_STEADY_SIGNAL] = { 1, "STEADY_SIGNAL" },
|
||||
[V5X_CTRL_IEI_DIGIT_SIGNAL] = { 1, "DIGIT_SIGNAL" },
|
||||
[V5X_CTRL_IEI_RECOGNITION_TIME] = { 2, "RECOGNITION_TIME" },
|
||||
[V5X_CTRL_IEI_ENABLE_AUTONOMOUS_ACK] = { 2, "ENABLE_AUTONOMOUS_ACK" },
|
||||
[V5X_CTRL_IEI_DISABLE_AUTONOMOUS_ACK] = { 1, "DISABLE_AUTONOMOUS_ACK" },
|
||||
[V5X_CTRL_IEI_CAUSE] = { 1, "CAUSE" },
|
||||
[V5X_CTRL_IEI_RESOURCE_UNAVAILABLE] = { 1, "RESOURCE_UNAVAILABLE" },
|
||||
[V5X_CTRL_IEI_ENABLE_METERING] = { 1, "ENABLE_METERING" },
|
||||
[V5X_CTRL_IEI_METERING_REPORT] = { 2, "METERING_REPORT" },
|
||||
[V5X_CTRL_IEI_ATTENUATION] = { 1, "ATTENUATION" },
|
||||
/* variable length: ISDN */
|
||||
[V5X_CTRL_IEI_CTRL_F_ELEMENT] = { 1, "CTRL_F_ELEMENT" },
|
||||
[V5X_CTRL_IEI_CTRL_F_ID] = { 1, "CTRL_F_ID" },
|
||||
[V5X_CTRL_IEI_VARIANT] = { 1, "VARIANT" },
|
||||
[V5X_CTRL_IEI_INTERFACE_ID] = { 3, "INTERFACE_ID" },
|
||||
/* variable length: LCP */
|
||||
[V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION] = { 1, "LINK_CTRL_FUNCTION" },
|
||||
/* variable length: BCC */
|
||||
[V52_CTRL_IEI_BCC_USER_PORT_ID] = { 2, "USER_PORT_ID" },
|
||||
[V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID] = { 1, "ISDN_PORT_TS_ID" },
|
||||
[V52_CTRL_IEI_BCC_V5_TS_ID] = { 2, "V5_TS_ID" },
|
||||
[V52_CTRL_IEI_BCC_MULTI_TS_MAP] = { 9, "MULTI_TS_MAP" },
|
||||
[V52_CTRL_IEI_BCC_REJECT_CAUSE] = { 1, "REJECT_CAUSE" },
|
||||
[V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE] = { 1, "PROTOCOL_ERR_CAUSE" },
|
||||
[V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE]= { 1, "CONNECTION_INCOMPLETE" },
|
||||
[V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY] = { 1, "INFO_TRANSFER_CAPABILITY" },
|
||||
/* variable length: PP */
|
||||
[V52_CTRL_IEI_PP_SEQUENCE_NR] = { 1, "SEQUENCE_NR" },
|
||||
[V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID] = { 2, "PHYSICAL_C_CHAN_ID" },
|
||||
[V52_CTRL_IEI_PP_REJECTION_CAUSE] = { 1, "REJECTION_CAUSE" },
|
||||
[V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE] = { 1, "PROTOCOL_ERR_CAUSE" },
|
||||
},
|
||||
.msgt_names = v5x_ctrl_msg_typ_str,
|
||||
};
|
||||
|
||||
/* main entry point for received V5 messages */
|
||||
int v5x_dl_rcv(struct osmo_dlsap_prim *odp, uint16_t dladdr, void *rx_cbdata)
|
||||
{
|
||||
struct v5x_interface *v5if = rx_cbdata;
|
||||
struct v5x_link *v5l = rx_cbdata;
|
||||
struct msgb *msg = odp->oph.msg;
|
||||
struct v5x_l3_hdr *l3h;
|
||||
struct tlv_parsed tp;
|
||||
uint16_t l3_addr;
|
||||
bool is_isdn;
|
||||
struct v5x_user_port *v5up;
|
||||
int rc;
|
||||
|
||||
switch (odp->oph.primitive) {
|
||||
case PRIM_DL_DATA:
|
||||
LOGP(DV5, LOGL_DEBUG, "DL-DATA indication received (ldaddr=%d)\n", dladdr);
|
||||
break;
|
||||
case PRIM_DL_EST:
|
||||
LOGP(DV5, LOGL_DEBUG, "DL-EST indication received (ldaddr=%d)\n", dladdr);
|
||||
switch (dladdr) {
|
||||
case V5X_DLADDR_CTRL:
|
||||
case V5X_DLADDR_PSTN:
|
||||
case V52_DLADDR_LCP:
|
||||
case V52_DLADDR_BCC:
|
||||
v5x_le_mdl_rcv(v5if, NULL, MDL_ESTABLISH_ind, dladdr);
|
||||
break;
|
||||
case V52_DLADDR_PROTECTION:
|
||||
v5x_le_mdl_rcv(v5l->interface, v5l, MDL_ESTABLISH_ind, dladdr);
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
case PRIM_DL_REL:
|
||||
LOGP(DV5, LOGL_DEBUG, "DL-REL indication received (ldaddr=%d)\n", dladdr);
|
||||
switch (dladdr) {
|
||||
case V5X_DLADDR_CTRL:
|
||||
case V5X_DLADDR_PSTN:
|
||||
case V52_DLADDR_LCP:
|
||||
case V52_DLADDR_BCC:
|
||||
v5x_le_mdl_rcv(v5if, NULL, MDL_RELEASE_ind, dladdr);
|
||||
break;
|
||||
case V52_DLADDR_PROTECTION:
|
||||
v5x_le_mdl_rcv(v5l->interface, v5l, MDL_RELEASE_ind, dladdr);
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
default:
|
||||
LOGP(DV5, LOGL_NOTICE, "Unhandled prim=%d (ldaddr=%d)\n", odp->oph.primitive, dladdr);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
l3h = msgb_l3(msg);
|
||||
|
||||
if (msgb_l3len(msg) < sizeof(*l3h)) {
|
||||
LOGP(DV5, LOGL_ERROR, "Received short message (%u bytes < %lu)\n", msgb_l3len(msg), sizeof(*l3h));
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (l3h->pdisc != V5X_CTRL_PDISC) {
|
||||
LOGP(DV5, LOGL_ERROR, "Received unsupported protocol discriminator 0x%02x\n", l3h->pdisc);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = osmo_tlv_prot_parse(&v5x_ctrl_msg_tlv, &tp, 1, l3h->msg_type, msgb_l3(msg) + sizeof(*l3h),
|
||||
msgb_l3len(msg) - sizeof(*l3h), 0, 0, DV5, __func__);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
LOGP(DV5, LOGL_DEBUG, "Received message from AN with msg_type %d\n", l3h->msg_type);
|
||||
|
||||
switch (dladdr) {
|
||||
case V5X_DLADDR_PSTN:
|
||||
/* PSTN signaling protocol */
|
||||
switch (l3h->msg_type) {
|
||||
case V5X_CTRL_MSGT_ESTABLISH:
|
||||
case V5X_CTRL_MSGT_ESTABLISH_ACK:
|
||||
case V5X_CTRL_MSGT_SIGNAL:
|
||||
case V5X_CTRL_MSGT_SIGNAL_ACK:
|
||||
case V5X_CTRL_MSGT_DISCONNECT:
|
||||
case V5X_CTRL_MSGT_DISCONNECT_COMPLETE:
|
||||
case V5X_CTRL_MSGT_STATUS_ENQUIRY:
|
||||
case V5X_CTRL_MSGT_STATUS:
|
||||
case V5X_CTRL_MSGT_PROTOCOL_PARAMETER:
|
||||
/* look-up user port based on L3 addr */
|
||||
l3_addr = v5x_l3_addr_dec(ntohs(l3h->l3_addr), &is_isdn);
|
||||
v5up = v5x_user_port_find(v5if, l3_addr, is_isdn);
|
||||
if (!v5up) {
|
||||
rc = -ENODEV;
|
||||
break;
|
||||
}
|
||||
rc = v5x_le_pstn_dl_rcv(v5up, l3h->msg_type, &tp);
|
||||
break;
|
||||
default:
|
||||
rc = -ENOTSUP;
|
||||
LOGP(DV5, LOGL_ERROR, "Received unsupported PSTN message type %s\n",
|
||||
osmo_tlv_prot_msg_name(&v5x_ctrl_msg_tlv, l3h->msg_type));
|
||||
}
|
||||
break;
|
||||
case V5X_DLADDR_CTRL:
|
||||
/* control protocol (Section 14 G.964) */
|
||||
switch (l3h->msg_type) {
|
||||
case V5X_CTRL_MSGT_PORT_CTRL:
|
||||
case V5X_CTRL_MSGT_PORT_CTRL_ACK:
|
||||
case V5X_CTRL_MSGT_COMMON_CTRL:
|
||||
case V5X_CTRL_MSGT_COMMON_CTRL_ACK:
|
||||
/* look-up user port based on L3 addr */
|
||||
l3_addr = v5x_l3_addr_dec(ntohs(l3h->l3_addr), &is_isdn);
|
||||
rc = v5x_le_ctrl_dl_rcv(v5if, l3_addr, is_isdn, l3h->msg_type, &tp);
|
||||
break;
|
||||
default:
|
||||
rc = -ENOTSUP;
|
||||
LOGP(DV5, LOGL_ERROR, "Received unsupported CTRL message type %s\n",
|
||||
osmo_tlv_prot_msg_name(&v5x_ctrl_msg_tlv, l3h->msg_type));
|
||||
}
|
||||
break;
|
||||
case V52_DLADDR_BCC:
|
||||
/* protection protocol (Section 18 G.965) */
|
||||
switch (l3h->msg_type) {
|
||||
case V52_CTRL_MSGT_ALLOCATION:
|
||||
case V52_CTRL_MSGT_ALLOCATION_COMPLETE:
|
||||
case V52_CTRL_MSGT_ALLOCATION_REJECT:
|
||||
case V52_CTRL_MSGT_DE_ALLOCATION:
|
||||
case V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE:
|
||||
case V52_CTRL_MSGT_DE_ALLOCATION_REJECT:
|
||||
case V52_CTRL_MSGT_AUDIT:
|
||||
case V52_CTRL_MSGT_AUDIT_COMPLETE:
|
||||
case V52_CTRL_MSGT_AN_FAULT:
|
||||
case V52_CTRL_MSGT_AN_FAULT_ACK:
|
||||
case V52_CTRL_MSGT_PROTOCOL_ERROR:
|
||||
rc = v52_le_bcc_dl_rcv(v5if, ntohs(l3h->l3_addr), l3h->msg_type, &tp);
|
||||
break;
|
||||
default:
|
||||
rc = -ENOTSUP;
|
||||
LOGP(DV5, LOGL_ERROR, "Received unsupported BCC message type %s\n",
|
||||
osmo_tlv_prot_msg_name(&v5x_ctrl_msg_tlv, l3h->msg_type));
|
||||
}
|
||||
break;
|
||||
case V52_DLADDR_PROTECTION:
|
||||
/* BCC protocol (Section 17 G.965) */
|
||||
switch (l3h->msg_type) {
|
||||
case V52_CTRL_MSGT_PP_SWITCH_OVER_REQ:
|
||||
case V52_CTRL_MSGT_PP_SWITCH_OVER_COM:
|
||||
case V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM:
|
||||
case V52_CTRL_MSGT_PP_SWITCH_OVER_ACK:
|
||||
case V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT:
|
||||
case V52_CTRL_MSGT_PP_PROTOCOL_ERROR:
|
||||
case V52_CTRL_MSGT_PP_RESET_SN_COM:
|
||||
case V52_CTRL_MSGT_PP_RESET_SN_ACK:
|
||||
rc = v52_le_pp_dl_rcv(v5l, ntohs(l3h->l3_addr), l3h->msg_type, &tp);
|
||||
break;
|
||||
default:
|
||||
rc = -ENOTSUP;
|
||||
LOGP(DV5, LOGL_ERROR, "Received unsupported PROTECTION message type %s\n",
|
||||
osmo_tlv_prot_msg_name(&v5x_ctrl_msg_tlv, l3h->msg_type));
|
||||
}
|
||||
break;
|
||||
case V52_DLADDR_LCP:
|
||||
/* Link control protocol (Section 16 G.965) */
|
||||
switch (l3h->msg_type) {
|
||||
case V52_CTRL_MSGT_LCP_LINK_CTRL:
|
||||
case V52_CTRL_MSGT_LCP_LINK_CTRL_ACK:
|
||||
rc = v5x_le_ctrl_dl_rcv(v5if, ntohs(l3h->l3_addr), 0, l3h->msg_type, &tp);
|
||||
break;
|
||||
default:
|
||||
rc = -ENOTSUP;
|
||||
LOGP(DV5, LOGL_ERROR, "Received unsupported LCP message type %s\n",
|
||||
osmo_tlv_prot_msg_name(&v5x_ctrl_msg_tlv, l3h->msg_type));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rc = -ENOTSUP;
|
||||
LOGP(DV5, LOGL_ERROR, "Received unsupported message type %s\n",
|
||||
osmo_tlv_prot_msg_name(&v5x_ctrl_msg_tlv, l3h->msg_type));
|
||||
}
|
||||
|
||||
out:
|
||||
if (msg)
|
||||
msgb_free(msg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* send V5 messages to DL instance, given by dladdr */
|
||||
int v5x_dl_snd(struct v5x_interface *v5if, uint16_t dladdr, struct msgb *msg)
|
||||
{
|
||||
struct lapv5_instance *li = NULL, *li2 = NULL;
|
||||
|
||||
switch (dladdr) {
|
||||
case V5X_DLADDR_PSTN:
|
||||
li = v5if->pstn.li;
|
||||
break;
|
||||
case V5X_DLADDR_CTRL:
|
||||
li = v5if->control.li;
|
||||
break;
|
||||
case V52_DLADDR_BCC:
|
||||
li = v5if->bcc.li;
|
||||
break;
|
||||
case V52_DLADDR_PROTECTION:
|
||||
li = v5if->protection.li[0];
|
||||
li2 = v5if->protection.li[1];
|
||||
break;
|
||||
case V52_DLADDR_LCP:
|
||||
li = v5if->lcp.li;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!li) {
|
||||
LOGP(DV5, LOGL_ERROR, "No instance for dladdr %d.\n", dladdr);
|
||||
msgb_free(msg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (li2)
|
||||
lapv5_dl_data_req(li2, dladdr, msgb_copy(msg, "V5 MSG clone"));
|
||||
|
||||
return lapv5_dl_data_req(li, dladdr, msg);
|
||||
}
|
||||
|
||||
// FIXME
|
||||
#define V5_MSGB_SIZE 128
|
||||
#define V5_MSGB_HEADROOM 56
|
||||
|
||||
struct msgb *msgb_alloc_v5x(void)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
msg = msgb_alloc_headroom(V5_MSGB_SIZE, V5_MSGB_HEADROOM, "V5 MSG");
|
||||
if (!msg)
|
||||
return NULL;
|
||||
msg->l3h = msg->data;
|
||||
|
||||
return msg;
|
||||
}
|
|
@ -0,0 +1,314 @@
|
|||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
/* Definitions related to the V5.1 and V5.2 interface between AN (access
|
||||
* network) and LE (local exchange) as per ITU-T G.964 + G.965.
|
||||
*
|
||||
* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
*/
|
||||
|
||||
/***********************************************************************/
|
||||
/* protocol wire format */
|
||||
/***********************************************************************/
|
||||
|
||||
/* Table 14/G.964 */
|
||||
#define V5X_CTRL_PDISC 0x48
|
||||
|
||||
/* Table 1/G.965 + Table 1/G.964 */
|
||||
#define V5X_DLADDR_PSTN 8176
|
||||
#define V5X_DLADDR_CTRL 8177
|
||||
#define V52_DLADDR_BCC 8178
|
||||
#define V52_DLADDR_PROTECTION 8179
|
||||
#define V52_DLADDR_LCP 8180
|
||||
|
||||
/* G.965 Section 13.1 */
|
||||
struct v5x_l3_hdr {
|
||||
uint8_t pdisc;
|
||||
uint16_t l3_addr; /* with C/R and EA bits! */
|
||||
uint8_t msg_type;
|
||||
uint8_t payload[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* G.964 Section 14.4.2.3 Figure 33 + 34 */
|
||||
static inline bool v5x_l3_addr_is_isdn(uint16_t in)
|
||||
{
|
||||
/* extract two bytes */
|
||||
uint8_t b1 = in >> 8;
|
||||
uint8_t b2 = in & 0xff;
|
||||
|
||||
if ((b1 & 0x03) == 0x00 && (b2 & 0x01))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline uint16_t v5x_l3_addr_dec(uint16_t in, bool *is_isdn)
|
||||
{
|
||||
/* extract two bytes */
|
||||
uint8_t b1 = in >> 8;
|
||||
uint8_t b2 = in & 0xff;
|
||||
|
||||
if (v5x_l3_addr_is_isdn(in)) {
|
||||
/* remove EA/C/R bits */
|
||||
uint8_t upper = b1 >> 2;
|
||||
uint8_t lower = b2 >> 1;
|
||||
if (is_isdn)
|
||||
*is_isdn = true;
|
||||
/* shift them together */
|
||||
return lower | (upper << 7);
|
||||
} else{
|
||||
uint8_t upper = b1 >> 1;
|
||||
uint8_t lower = b2;
|
||||
if (is_isdn)
|
||||
*is_isdn = false;
|
||||
return lower | (upper << 8);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint16_t v5x_l3_addr_enc(uint16_t in, bool is_isdn)
|
||||
{
|
||||
uint8_t b1, b2;
|
||||
if (is_isdn) {
|
||||
uint8_t upper = in >> 7;
|
||||
uint8_t lower = in & 0x7f;
|
||||
b1 = upper << 2;
|
||||
b2 = (lower << 1) | 0x01;
|
||||
} else {
|
||||
uint8_t upper = in >> 8;
|
||||
uint8_t lower = in & 0xff;
|
||||
b1 = (upper << 1) | 0x01;
|
||||
b2 = lower;
|
||||
}
|
||||
return (b1 << 8) | b2;
|
||||
}
|
||||
|
||||
/* Table 15 + 52/G.964 */
|
||||
enum v5x_ctrl_msg_type {
|
||||
/* 000xxxx: PSTN protocol message types */
|
||||
V5X_CTRL_MSGT_ESTABLISH = 0x00,
|
||||
V5X_CTRL_MSGT_ESTABLISH_ACK = 0x01,
|
||||
V5X_CTRL_MSGT_SIGNAL = 0x02,
|
||||
V5X_CTRL_MSGT_SIGNAL_ACK = 0x03,
|
||||
V5X_CTRL_MSGT_DISCONNECT = 0x08,
|
||||
V5X_CTRL_MSGT_DISCONNECT_COMPLETE = 0x09,
|
||||
V5X_CTRL_MSGT_STATUS_ENQUIRY = 0x0c,
|
||||
V5X_CTRL_MSGT_STATUS = 0x0d,
|
||||
V5X_CTRL_MSGT_PROTOCOL_PARAMETER = 0x0e,
|
||||
/* 0010xxx: Control protocol message types */
|
||||
V5X_CTRL_MSGT_PORT_CTRL = 0x10,
|
||||
V5X_CTRL_MSGT_PORT_CTRL_ACK = 0x11,
|
||||
V5X_CTRL_MSGT_COMMON_CTRL = 0x12,
|
||||
V5X_CTRL_MSGT_COMMON_CTRL_ACK = 0x13,
|
||||
/* 0011xxx: Protection protocol message types */
|
||||
V52_CTRL_MSGT_PP_SWITCH_OVER_REQ = 0x18,
|
||||
V52_CTRL_MSGT_PP_SWITCH_OVER_COM = 0x19,
|
||||
V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM = 0x1a,
|
||||
V52_CTRL_MSGT_PP_SWITCH_OVER_ACK = 0x1b,
|
||||
V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT = 0x1c,
|
||||
V52_CTRL_MSGT_PP_PROTOCOL_ERROR = 0x1d,
|
||||
V52_CTRL_MSGT_PP_RESET_SN_COM = 0x1e,
|
||||
V52_CTRL_MSGT_PP_RESET_SN_ACK = 0x1f,
|
||||
/* 010xxxx: BCC protocol message types */
|
||||
V52_CTRL_MSGT_ALLOCATION = 0x20,
|
||||
V52_CTRL_MSGT_ALLOCATION_COMPLETE = 0x21,
|
||||
V52_CTRL_MSGT_ALLOCATION_REJECT = 0x22,
|
||||
V52_CTRL_MSGT_DE_ALLOCATION = 0x23,
|
||||
V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE = 0x24,
|
||||
V52_CTRL_MSGT_DE_ALLOCATION_REJECT = 0x25,
|
||||
V52_CTRL_MSGT_AUDIT = 0x26,
|
||||
V52_CTRL_MSGT_AUDIT_COMPLETE = 0x27,
|
||||
V52_CTRL_MSGT_AN_FAULT = 0x28,
|
||||
V52_CTRL_MSGT_AN_FAULT_ACK = 0x29,
|
||||
V52_CTRL_MSGT_PROTOCOL_ERROR = 0x2a,
|
||||
/* 0110xxx: Link control protocol message types */
|
||||
V52_CTRL_MSGT_LCP_LINK_CTRL = 0x30,
|
||||
V52_CTRL_MSGT_LCP_LINK_CTRL_ACK = 0x31,
|
||||
};
|
||||
|
||||
extern const struct value_string v5x_ctrl_msg_typ_str[];
|
||||
|
||||
|
||||
/* Table 17 + 53/G.964 */
|
||||
enum v5x_ctrl_iei {
|
||||
/* single byte, only upper nibble matches IEI */
|
||||
V5X_CTRL_IEI_PULSE_NOTIFICATION = 0xc0,
|
||||
V5X_CTRL_IEI_LINE_INFORMATION = 0x80,
|
||||
V5X_CTRL_IEI_STATE = 0x90,
|
||||
V5X_CTRL_IEI_AUTONOMOUS_SIG_SEQ = 0xa0,
|
||||
V5X_CTRL_IEI_SEQUENCE_RESPONSE = 0xb0,
|
||||
/* single byte: ISDN */
|
||||
V5X_CTRL_IEI_PERFORMANCE_GRADING = 0xe0,
|
||||
V5X_CTRL_IEI_REJECTION_CAUSE = 0xf0,
|
||||
/* variable length: PSTN */
|
||||
V5X_CTRL_IEI_SEQUENCE_NR = 0x00,
|
||||
V5X_CTRL_IEI_CADENCED_RINGING = 0x01,
|
||||
V5X_CTRL_IEI_PULSED_SIGNAL = 0x02,
|
||||
V5X_CTRL_IEI_STEADY_SIGNAL = 0x03,
|
||||
V5X_CTRL_IEI_DIGIT_SIGNAL = 0x04,
|
||||
V5X_CTRL_IEI_RECOGNITION_TIME = 0x10,
|
||||
V5X_CTRL_IEI_ENABLE_AUTONOMOUS_ACK = 0x11,
|
||||
V5X_CTRL_IEI_DISABLE_AUTONOMOUS_ACK = 0x12,
|
||||
V5X_CTRL_IEI_CAUSE = 0x13,
|
||||
V5X_CTRL_IEI_RESOURCE_UNAVAILABLE = 0x14,
|
||||
V5X_CTRL_IEI_ENABLE_METERING = 0x32,
|
||||
V5X_CTRL_IEI_METERING_REPORT = 0x33,
|
||||
V5X_CTRL_IEI_ATTENUATION = 0x34,
|
||||
/* variable length: ISDN */
|
||||
V5X_CTRL_IEI_CTRL_F_ELEMENT = 0x20,
|
||||
V5X_CTRL_IEI_CTRL_F_ID = 0x21,
|
||||
V5X_CTRL_IEI_VARIANT = 0x22,
|
||||
V5X_CTRL_IEI_INTERFACE_ID = 0x23,
|
||||
/* variable length: LCP */
|
||||
V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION = 0x30,
|
||||
/* variable length: BCC */
|
||||
V52_CTRL_IEI_BCC_USER_PORT_ID = 0x40,
|
||||
V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID = 0x41,
|
||||
V52_CTRL_IEI_BCC_V5_TS_ID = 0x42,
|
||||
V52_CTRL_IEI_BCC_MULTI_TS_MAP = 0x43,
|
||||
V52_CTRL_IEI_BCC_REJECT_CAUSE = 0x44,
|
||||
V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE = 0x45,
|
||||
V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE = 0x46,
|
||||
V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY = 0x47,
|
||||
/* variable-length: Protection */
|
||||
V52_CTRL_IEI_PP_SEQUENCE_NR = 0x50,
|
||||
V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID = 0x51,
|
||||
V52_CTRL_IEI_PP_REJECTION_CAUSE = 0x52,
|
||||
V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE = 0x53,
|
||||
};
|
||||
|
||||
extern const struct value_string v5x_ctrl_iei_str[];
|
||||
|
||||
extern const struct tlv_definition v5x_ctrl_tlv_def;
|
||||
|
||||
extern const struct osmo_tlv_prot_def v5x_ctrl_msg_tlv;
|
||||
|
||||
/* 14.4.2.5.4 Table 56/G.964 - Coding of Control Function Element */
|
||||
enum v5x_ctrl_func_el {
|
||||
V5X_CTRL_FE101_ACTIVATE_ACCESS = 0x01,
|
||||
V5X_CTRL_FE102_ACT_INIT_BY_USER = 0x02,
|
||||
V5X_CTRL_FE103_DS_ACTIVATED = 0x03,
|
||||
V5X_CTRL_FE104_ACCESS_ACTIVATED = 0x04,
|
||||
V5X_CTRL_FE105_DEACTIVATE_ACCESS = 0x05,
|
||||
V5X_CTRL_FE106_ACCESS_DEACTIVATED = 0x06,
|
||||
V5X_CTRL_FE201_UNBLOCK = 0x11,
|
||||
V5X_CTRL_FE203_BLOCK = 0x13,
|
||||
V5X_CTRL_FE205_BLOCK_REQ = 0x15,
|
||||
V5X_CTRL_FE206_PERFORMANCE_GRADING = 0x16,
|
||||
V5X_CTRL_FE207_D_CHANNEL_BLOCK = 0x17,
|
||||
V5X_CTRL_FE208_D_CHANNEL_UNBLOCK = 0x18,
|
||||
};
|
||||
|
||||
extern const struct value_string v5x_ctrl_func_el_str[];
|
||||
|
||||
enum v5x_ctrl_func_id {
|
||||
/* 14.4.2.5.5 Table 57/G.964 - Coding of Control Function ID */
|
||||
V5X_CTRL_ID_VERIFY_RE_PROVISIONING = 0x00,
|
||||
V5X_CTRL_ID_READY_FOR_RE_PROVISIONING = 0x01,
|
||||
V5X_CTRL_ID_NOT_READY_FOR_RE_PROVISIONING = 0x02,
|
||||
V5X_CTRL_ID_SWITCH_OVER_TO_NEW_VARIANT = 0x03,
|
||||
V5X_CTRL_ID_RE_PROVISIONING_STARTED = 0x04,
|
||||
V5X_CTRL_ID_CANNOT_RE_PROVISION = 0x05,
|
||||
V5X_CTRL_ID_REQUEST_VARIANT_AND_INTERFACE_ID = 0x06,
|
||||
V5X_CTRL_ID_VARIANT_AND_INTERFACE_ID = 0x07,
|
||||
V5X_CTRL_ID_BLOCKING_STARTED = 0x08,
|
||||
V5X_CTRL_ID_RESTART_REQUEST = 0x10,
|
||||
V5X_CTRL_ID_RESTART_COMPLETE = 0x11,
|
||||
/* 15.4.2 Table 9a/G.965 - Coding of Control Function ID */
|
||||
V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_REQUEST = 0x12,
|
||||
V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_ACCEPTED = 0x13,
|
||||
V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_REJECTED = 0x14,
|
||||
V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_COMPLETED = 0x15,
|
||||
V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_REQUEST = 0x16,
|
||||
V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ACCEPTED = 0x17,
|
||||
V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_REJECTED = 0x18,
|
||||
V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_COMPLETED = 0x19,
|
||||
V5X_CTRL_ID_UNBLK_ALL_REL_ISDN_REQUEST = 0x1a,
|
||||
V5X_CTRL_ID_UNBLK_ALL_REL_ISDN_ACCEPTED = 0x1b,
|
||||
V5X_CTRL_ID_UNBLK_ALL_REL_ISDN_REJECTED = 0x1c,
|
||||
V5X_CTRL_ID_UNBLK_ALL_REL_ISDN_COMPLETED = 0x1d,
|
||||
V5X_CTRL_ID_BLK_ALL_PSTN_REQUEST = 0x1e,
|
||||
V5X_CTRL_ID_BLK_ALL_PSTN_ACCEPTED = 0x1f,
|
||||
V5X_CTRL_ID_BLK_ALL_PSTN_REJECTED = 0x20,
|
||||
V5X_CTRL_ID_BLK_ALL_PSTN_COMPLETED = 0x21,
|
||||
V5X_CTRL_ID_BLK_ALL_ISDN_REQUEST = 0x22,
|
||||
V5X_CTRL_ID_BLK_ALL_ISDN_ACCEPTED = 0x23,
|
||||
V5X_CTRL_ID_BLK_ALL_ISDN_REJECTED = 0x24,
|
||||
V5X_CTRL_ID_BLK_ALL_ISDN_COMPLETED = 0x25,
|
||||
|
||||
};
|
||||
|
||||
extern const struct value_string v5x_ctrl_func_id_str[];
|
||||
|
||||
/* 13.4.7.9 Cause / 17.4.2.6 Table 43/G.965 / 18.5.5 Table 62/G.965 */
|
||||
enum v5x_ctrl_cause_type {
|
||||
V5X_CAUSE_T_RESP_TO_STATUS_ENQ = 0x00,
|
||||
V5X_CAUSE_T_PROTOCOL_DISC_ERROR = 0x01,
|
||||
V5X_CAUSE_T_L3_ADDRESS_ERROR = 0x03,
|
||||
V5X_CAUSE_T_MSG_TYPE_UNRECOGNIZED = 0x04,
|
||||
V5X_CAUSE_T_OUT_OF_SEQUENCE_IE = 0x05,
|
||||
V5X_CAUSE_T_REPEATED_OPT_IE = 0x06,
|
||||
V5X_CAUSE_T_MAND_IE_MISSING = 0x07,
|
||||
V5X_CAUSE_T_UNRECOGNIZED_IE = 0x08,
|
||||
V5X_CAUSE_T_MAND_IE_CONTENT_ERROR = 0x09,
|
||||
V5X_CAUSE_T_OPT_IE_CONTENT_ERROR = 0x0a,
|
||||
V5X_CAUSE_T_MSG_INCOMP_STATE = 0x0b,
|
||||
V5X_CAUSE_T_REPEATED_MAND_IE = 0x0c,
|
||||
V5X_CAUSE_T_TOO_MANY_IE = 0x0d,
|
||||
V5X_CAUSE_T_REF_NR_CODING_ERROR = 0x0f,
|
||||
};
|
||||
|
||||
/* 17.4.2.5 Table 41/G.965 - Reject cause type */
|
||||
enum v52_bcc_reject_cause_type {
|
||||
V52_BCC_REJECT_CAUSE_T_UNSPECIFIED = 0x00,
|
||||
V52_BCC_REJECT_CAUSE_T_ACCESS_NET_FAUL = 0x01,
|
||||
V52_BCC_REJECT_CAUSE_T_ACCESS_NET_BLOCKED = 0x02,
|
||||
V52_BCC_REJECT_CAUSE_T_CONN_PRESENT_PSTN = 0x03,
|
||||
V52_BCC_REJECT_CAUSE_T_CONN_PRESENT_V5_TS = 0x04,
|
||||
V52_BCC_REJECT_CAUSE_T_CONN_PRESENT_ISDN = 0x05,
|
||||
V52_BCC_REJECT_CAUSE_T_USER_PORT_UNAVAILABLE = 0x06,
|
||||
V52_BCC_REJECT_CAUSE_T_DEALLOC_INCOMPAT_DATA = 0x07,
|
||||
V52_BCC_REJECT_CAUSE_T_DEALLOC_V5_TS_DATA = 0x08,
|
||||
V52_BCC_REJECT_CAUSE_T_DEALLOC_PORT_DATA = 0x09,
|
||||
V52_BCC_REJECT_CAUSE_T_DEALLOC_USER_TS_DATA = 0x0a,
|
||||
V52_BCC_REJECT_CAUSE_T_USER_NOT_PROVISIONED = 0x0b,
|
||||
V52_BCC_REJECT_CAUSE_T_INVALID_V5_TS_IDENT = 0x0c,
|
||||
V52_BCC_REJECT_CAUSE_T_INVALID_V5_LINK_IDENT = 0x0d,
|
||||
V52_BCC_REJECT_CAUSE_T_INVALID_USER_TS_IDENT = 0x0e,
|
||||
V52_BCC_REJECT_CAUSE_T_V5_TS_USED_CCHANNEL = 0x0f,
|
||||
V52_BCC_REJECT_CAUSE_T_V5_LINK_UNAVAILABLE = 0x10,
|
||||
};
|
||||
|
||||
/* 18.5.4 Table 61/G.965 - Reject cause type */
|
||||
enum v52_pp_reject_cause_type {
|
||||
V52_PP_REJECT_CAUSE_T_NO_STANDBY_CC_AVAILABLE = 0x00,
|
||||
V52_PP_REJECT_CAUSE_T_TARGET_CC_NOT_OPER = 0x01,
|
||||
V52_PP_REJECT_CAUSE_T_TARGET_CC_NOT_PROV = 0x02,
|
||||
V52_PP_REJECT_CAUSE_T_PROT_SWITCHING_IMPOSS = 0x03,
|
||||
V52_PP_REJECT_CAUSE_T_PROT_GROUP_MISMATCH = 0x04,
|
||||
V52_PP_REJECT_CAUSE_T_ALLOC_EXISTS_ALREADY = 0x05,
|
||||
V52_PP_REJECT_CAUSE_T_TARGET_CC_ALREADY_CC = 0x06,
|
||||
};
|
||||
|
||||
extern const struct value_string v5x_cause_type_str[];
|
||||
extern const struct value_string v52_bcc_reject_cause_type_str[];
|
||||
extern const struct value_string v52_pp_reject_cause_type_str[];
|
||||
|
||||
//extern const struct osmo_tlv_prot_def v51_ctrl_msg_tlv;
|
||||
|
||||
/* 16.3.2.2 Table 22/G.965 */
|
||||
enum v52_link_ctrl_func {
|
||||
V52_LCP_FE_IDReq = 0,
|
||||
V52_LCP_FE_IDAck = 1,
|
||||
V52_LCP_FE_IDRel = 2,
|
||||
V52_LCP_FE_IDRej = 3,
|
||||
V52_LCP_FE_301_302_LINK_UNBLOCK = 4,
|
||||
V52_LCP_FE_303_304_LINK_BLOCK = 5,
|
||||
V52_LCP_FE_305_DEF_LINK_BLOCK_REQ = 6,
|
||||
V52_LCP_FE_306_NON_DEF_LINK_BLOCK_REQ = 7,
|
||||
};
|
||||
|
||||
int v5x_dl_rcv(struct osmo_dlsap_prim *odp, uint16_t dladdr, void *rx_cbdata);
|
||||
int v5x_dl_snd(struct v5x_interface *v5if, uint16_t dladdr, struct msgb *msg);
|
||||
struct msgb *msgb_alloc_v5x(void);
|
|
@ -0,0 +1,13 @@
|
|||
AM_CPPFLAGS = $(all_includes)
|
||||
AM_CFLAGS= -Wall -Wextra -g $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(LIBOSMOCTRL_CFLAGS)
|
||||
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
|
||||
COMMONLIBS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCTRL_LIBS)
|
||||
|
||||
noinst_PROGRAMS = answer_detect
|
||||
|
||||
answer_detect_SOURCES = answer_detect.c
|
||||
|
||||
answer_detect_LDADD = $(COMMON_LA) \
|
||||
$(COMMONLIBS) \
|
||||
../src/libecho/libecho.a \
|
||||
-lm
|
|
@ -0,0 +1,179 @@
|
|||
/* Testing the answer tone algorithm of libecho
|
||||
*
|
||||
* (C) 2023 by Andreas Eversberg <andreas@eversberg.eu>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "../src/libecho/answertone.h"
|
||||
|
||||
#define db2level(db) pow(10, (double)(db) / 20.0)
|
||||
|
||||
#define ANS_dB -13.0
|
||||
#define ANS_DURATION 4
|
||||
#define ANS_PHASE_REV 0.450
|
||||
#define SAMPLERATE 8000
|
||||
#define BANDWIDTH 100.0
|
||||
|
||||
float tone[SAMPLERATE * ANS_DURATION];
|
||||
int16_t tone_int[SAMPLERATE * ANS_DURATION];
|
||||
|
||||
static void gen_ans(float frequency, float level)
|
||||
{
|
||||
double phase = 0.0, rev = 0.0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SAMPLERATE * ANS_DURATION; i++) {
|
||||
/* given tone */
|
||||
tone[i] = sin(2.0 * M_PI * frequency * (double)i / (double)SAMPLERATE + phase);
|
||||
/* 15 Hz amplitude modulation */
|
||||
tone[i] *= 1.0 + sin(2.0 * M_PI * 15.0 * (double)i / (double)SAMPLERATE) * 0.2;
|
||||
/* given level */
|
||||
tone[i] *= level;
|
||||
tone_int[i] = tone[i] * 32767.0;
|
||||
/* apply phase reversal */
|
||||
rev += 1.0 / (double)SAMPLERATE;
|
||||
if (rev >= ANS_PHASE_REV) {
|
||||
rev -= ANS_PHASE_REV;
|
||||
phase += M_PI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static enum at_fail perform_test(struct answer_tone *at, float frequency, float level, float *phase1_p, float *phase2_p)
|
||||
{
|
||||
enum at_fail fail;
|
||||
int offset = 0;
|
||||
float _frequency;
|
||||
|
||||
gen_ans(frequency, level);
|
||||
|
||||
answertone_init(at, SAMPLERATE);
|
||||
|
||||
memcpy(at->buffer, tone + offset, sizeof(*at->buffer) * at->buffer_size);
|
||||
offset += at->buffer_size;
|
||||
answertone_check(at, phase1_p, &_frequency);
|
||||
memcpy(at->buffer, tone + offset, sizeof(*at->buffer) * at->buffer_size);
|
||||
offset += at->buffer_size;
|
||||
fail = answertone_check(at, phase2_p, &_frequency);
|
||||
|
||||
switch (fail) {
|
||||
case AT_FAIL_MIN_LEVEL:
|
||||
printf("failed, level too low\n");
|
||||
break;
|
||||
case AT_FAIL_MAX_NOISE:
|
||||
printf("failed, too noisy\n");
|
||||
break;
|
||||
case AT_FAIL_MAX_FREQUENCY:
|
||||
printf("failed, frequency too much off\n");
|
||||
break;
|
||||
case AT_FAIL_NONE:
|
||||
printf("ok\n");
|
||||
}
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
static void _assert(bool what)
|
||||
{
|
||||
if (what) {
|
||||
printf("The result of this test was as expected.\n\n");
|
||||
return;
|
||||
}
|
||||
printf("*\nThe result of this test was not as expected, please FIX!.\n*\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct answer_tone answer_tone, *at = &answer_tone;
|
||||
int offset;
|
||||
enum at_fail fail;
|
||||
float phase1, phase2, _frequency;
|
||||
int rc;
|
||||
|
||||
printf("Test chunk with perfect tone: ");
|
||||
fail = perform_test(at, 2100, db2level(ANS_dB), &phase1, &phase2);
|
||||
_assert(!fail);
|
||||
printf("Check phase: %.3f == %.3f\n", phase1, phase2);
|
||||
_assert(fabsf(phase1 - phase2) < 0.001);
|
||||
printf("Test chunk with 2101 Hz tone: ");
|
||||
fail = perform_test(at, 2101, db2level(ANS_dB), &phase1, &phase2);
|
||||
_assert(!fail);
|
||||
printf("Check phase: %.3f != %.3f\n", phase1, phase2);
|
||||
_assert(fabsf(phase1 - phase2) > 0.001);
|
||||
printf("Test chunk with 2121 Hz tone: ");
|
||||
fail = perform_test(at, 2121, db2level(ANS_dB), &phase1, &phase2);
|
||||
_assert(!fail);
|
||||
printf("Test chunk with -26 dBm0 tone: ");
|
||||
fail = perform_test(at, 2100, db2level(-26), &phase1, &phase2);
|
||||
_assert(!fail);
|
||||
printf("Test chunk with 2123 Hz tone: ");
|
||||
fail = perform_test(at, 2123, db2level(ANS_dB), &phase1, &phase2);
|
||||
_assert(fail);
|
||||
printf("Test chunk with -28 dBm0 tone: ");
|
||||
fail = perform_test(at, 2100, db2level(-28), &phase1, &phase2);
|
||||
_assert(fail);
|
||||
|
||||
printf("Test chunk with long sequence of the tone: ");
|
||||
gen_ans(2100+10, db2level(ANS_dB-8));
|
||||
answertone_init(at, SAMPLERATE);
|
||||
for (offset = 0; offset <= (double)SAMPLERATE * ANS_PHASE_REV - at->buffer_size; offset += at->buffer_size) {
|
||||
memcpy(at->buffer, tone + offset, sizeof(*at->buffer) * at->buffer_size);
|
||||
fail = answertone_check(at, &phase1, &_frequency);
|
||||
switch (fail) {
|
||||
case AT_FAIL_MIN_LEVEL:
|
||||
printf("level too low\n");
|
||||
_assert(false);
|
||||
break;
|
||||
case AT_FAIL_MAX_NOISE:
|
||||
printf("too noisy\n");
|
||||
_assert(false);
|
||||
break;
|
||||
case AT_FAIL_MAX_FREQUENCY:
|
||||
if (offset == 0)
|
||||
break;
|
||||
printf("frequency too much off\n");
|
||||
_assert(false);
|
||||
break;
|
||||
case AT_FAIL_NONE:
|
||||
;
|
||||
}
|
||||
}
|
||||
printf("ok\n");
|
||||
_assert(true);
|
||||
|
||||
printf("Test complete process with long sequence of the tone: ");
|
||||
gen_ans(2100+10, db2level(ANS_dB-8));
|
||||
answertone_init(at, SAMPLERATE);
|
||||
rc = answertone_process(at, tone_int, SAMPLERATE * ANS_DURATION, true);
|
||||
if (rc > 0)
|
||||
printf("tone detected\n");
|
||||
else
|
||||
printf("no tone detected\n");
|
||||
answertone_exit(at);
|
||||
_assert(rc > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
35
v51_l1_fsm.c
35
v51_l1_fsm.c
|
@ -1,35 +0,0 @@
|
|||
/* ITU-T G.964 Section 14.3.3 V5.1-intterface Layer 1 FSM - AN and LE */
|
||||
|
||||
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "v5x_internal.h"
|
||||
|
||||
enum v51_l1_fsm_state {
|
||||
V51_L1FSM_S_LE1_NORMAL,
|
||||
V51_L1FSM_S_LE2_LOCALLY_DET_FAIL,
|
||||
V51_L1FSM_S_LE3_REMOTELY_DET_FAIL,
|
||||
V51_L1FSM_S_LE4_INTERNAL_FAIL,
|
||||
};
|
||||
|
||||
/* TODO */
|
371
v51_le_ctrl.c
371
v51_le_ctrl.c
|
@ -1,371 +0,0 @@
|
|||
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unitstd.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/util.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
|
||||
/* PSTN Protocol FSM */
|
||||
enum v51_le_ctrl_port_fsm_state {
|
||||
V51_PSTN_PROT_S_OUT_OF_SERVICE, /* LE0 */
|
||||
V51_PSTN_PROT_S_NULL /* LE1 */
|
||||
V51_PSTN_PROT_S_PATH_INIT_BY_LE /* LE2 */
|
||||
V51_PSTN_PROT_S_PATH_INIT_BY_AN /* LE3 */
|
||||
V51_PSTN_PROT_S_PATH_ACTIVE /* LE4 */
|
||||
V51_PSTN_PROT_S_PATH_DISC_REQ /* LE5 */
|
||||
V51_PSTN_PROT_S_PORT_BLOCKED /* LE6 */
|
||||
};
|
||||
|
||||
/***********************************************************************
|
||||
* user port CTRL protocol FSM / G.964 Section 14
|
||||
***********************************************************************/
|
||||
|
||||
enum v51_ctrl_port_fsm_state {
|
||||
V51_CTRL_PORT_ST_OUT_OF_SERVICE,
|
||||
V51_CTRL_PORT_ST_IN_SERVICE,
|
||||
V51_CTRL_PORT_AWAIT_PORT_ACK,
|
||||
};
|
||||
|
||||
enum v51_ctrl_port_fsm_event {
|
||||
V51_CTRL_PE_MDU_START_TRAFFIC,
|
||||
V51_CTRL_PE_MDU_STOP_TRAFFIC,
|
||||
V51_CTRL_PE_MDU_CTRL,
|
||||
V51_CTRL_PE_RX_PORT_CONTROL,
|
||||
V51_CTRL_PE_RX_PORT_CONTROL_ACK,
|
||||
};
|
||||
|
||||
static const struct value_string v51_ctrl_port_fsm_event_names[] = {
|
||||
{ V51_CTRL_PE_MDU_START_TRAFFIC, "MDU-start_traffic" },
|
||||
{ V51_CTRL_PE_MDU_STOP_TRAFFIC, "MDU-stop_traffic" },
|
||||
{ V51_CTRL_PE_MDU_CTRL, "MDU-CTRL" },
|
||||
{ V51_CTRL_PE_RX_PORT_CONTROL, "PORT_CONTROL" },
|
||||
{ V51_CTRL_PE_RX_PORT_CONTROL_ACK, "PORT_CONTROL_ACK" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static int v51_ctrl_port_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
if () {
|
||||
/* first expiry: repeat PORT_CONTROL; re-start T01 */
|
||||
} else {
|
||||
/* second expiry: send MDU-error_ind; go to IN_SERVICE */
|
||||
osmo_fsm_inst_state_chg(fi, V51_CTRL_PORT_ST_IN_SERVICE);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct osmo_fsm_state v51_ctrl_port_fsm_states[] = {
|
||||
[V51_CTRL_PORT_ST_OUT_OF_SERVICE] = {
|
||||
.name = "OUT_OF_SERVICE",
|
||||
.in_event_mask = S(V51_CTRL_PE_MDU_START_TRAFFIC) |
|
||||
S(V51_CTRL_PE_MDU_STOP_TRAFFIC) |
|
||||
S(V51_CTRL_PE_MDU_STOP_TRAFFIC) |
|
||||
S(V51_CTRL_PE_RX_PORT_CONTROL) |
|
||||
S(V51_CTRL_PE_RX_PORT_CONTROL_ACK),
|
||||
.out_state_mask = S(V51_CTRL_PORT_ST_IN_SERVICE),
|
||||
},
|
||||
[V51_CTRL_PORT_ST_IN_SERVICE] = {
|
||||
.name = "IN_SERVICE",
|
||||
.in_event_mask = S(V51_CTRL_PE_MDU_START_TRAFFIC) |
|
||||
S(V51_CTRL_PE_MDU_STOP_TRAFFIC) |
|
||||
S(V51_CTRL_PE_MDU_STOP_TRAFFIC) |
|
||||
S(V51_CTRL_PE_RX_PORT_CONTROL),
|
||||
|
||||
.out_state_mask = S(V51_CTRL_PORT_ST_OUT_OF_SERVICE) |
|
||||
S(V51_CTRL_PORT_AWAIT_PORT_ACK),
|
||||
},
|
||||
[V51_CTRL_PORT_AWAIT_PORT_ACK] = {
|
||||
.name = "AWAIT_PORT_ACK",
|
||||
.in_event_mask = S(V51_CTRL_PE_MDU_START_TRAFFIC) |
|
||||
S(V51_CTRL_PE_MDU_STOP_TRAFFIC) |
|
||||
S(V51_CTRL_PE_MDU_STOP_TRAFFIC) |
|
||||
S(V51_CTRL_PE_RX_PORT_CONTROL) |
|
||||
S(V51_CTRL_PE_RX_PORT_CONTROL_ACK),
|
||||
.out_state_mask = S(V51_CTRL_PORT_ST_OUT_OF_SERVICE) |
|
||||
S(V51_CTRL_PORT_ST_IN_SERVICE),
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm v51_ctrl_port_fsm = {
|
||||
.name = "V51_CTRL_PORT",
|
||||
.states = v51_ctrl_port_fsm_states,
|
||||
.num_states = ARRAY_SIZE(v51_ctrl_port_fsm_states),
|
||||
.allstate_event_mask = 0,
|
||||
.allstate_action = NULL,
|
||||
.cleanup = NULL,
|
||||
.timer_cb = v51_ctrl_port_fsm_timer_cb,
|
||||
.log_subsys = DV5CTRL,
|
||||
.event_names = v51_ctrl_port_fsm_event_names,
|
||||
};
|
||||
|
||||
/***********************************************************************
|
||||
* common CTRL protocol FSM / G.964 Section 14
|
||||
***********************************************************************/
|
||||
|
||||
enum v51_ctrl_common_fsm_state {
|
||||
V51_CTRL_COM_ST_OUT_OF_SERVICE,
|
||||
V51_CTRL_COM_ST_IN_SERVICE,
|
||||
V51_CTRL_COM_AWAIT_COMMON_ACK,
|
||||
};
|
||||
|
||||
enum v51_ctrl_common_fsm_event {
|
||||
V51_CTRL_CE_MDU_START_TRAFFIC,
|
||||
V51_CTRL_CE_MDU_STOP_TRAFFIC,
|
||||
V51_CTRL_CE_MDU_CTRL,
|
||||
V51_CTRL_CE_RX_COMMON_CONTROL,
|
||||
V51_CTRL_CE_RX_COMMON_CONTROL_ACK,
|
||||
};
|
||||
|
||||
static const struct value_string v51_ctrl_common_fsm_event_names[] = {
|
||||
{ V51_CTRL_CE_MDU_START_TRAFFIC, "MDU-start_traffic" },
|
||||
{ V51_CTRL_CE_MDU_STOP_TRAFFIC, "MDU-stop_traffic" },
|
||||
{ V51_CTRL_CE_MDU_CTRL, "MDU-CTRL" },
|
||||
{ V51_CTRL_CE_RX_COMMON_CONTROL, "COMMON_CONTROL" },
|
||||
{ V51_CTRL_CE_RX_COMMON_CONTROL_ACK, "COMMON_CONTROL_ACK" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static int v51_ctrl_common_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
if () {
|
||||
/* first expiry: repeat COMMON_CONTROL; re-start T02 */
|
||||
} else {
|
||||
/* second expiry: send MDU-error_ind; go to IN_SERVICE */
|
||||
osmo_fsm_inst_state_chg(fi, V51_CTRL_COM_ST_IN_SERVICE);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct osmo_fsm_state v51_ctrl_common_fsm_states[] = {
|
||||
[V51_CTRL_COM_ST_OUT_OF_SERVICE] = {
|
||||
.name = "OUT_OF_SERVICE",
|
||||
.in_event_mask = S(V51_CTRL_CE_MDU_START_TRAFFIC) |
|
||||
S(V51_CTRL_CE_MDU_STOP_TRAFFIC) |
|
||||
S(V51_CTRL_CE_MDU_CTRL) |
|
||||
S(V51_CTRL_CE_RX_COMMON_CONTROL) |
|
||||
S(V51_CTRL_CE_RX_COMMON_CONTROL_ACK),
|
||||
.out_state_mask = S(V51_CTRL_COM_ST_IN_SERVICE),
|
||||
},
|
||||
[V51_CTRL_COM_ST_IN_SERVICE] = {
|
||||
.name = "IN_SERVICE",
|
||||
.in_event_mask = S(V51_CTRL_CE_MDU_START_TRAFFIC) |
|
||||
S(V51_CTRL_CE_MDU_STOP_TRAFFIC) |
|
||||
S(V51_CTRL_CE_MDU_CTRL) |
|
||||
S(V51_CTRL_CE_RX_COMMON_CONTROL),
|
||||
.out_state_mask = S(V51_CTRL_COM_ST_OUT_OF_SERVICE) |
|
||||
S(V51_CTRL_COM_AWAIT_COMMON_ACK),
|
||||
},
|
||||
[V51_CTRL_COM_AWAIT_COMMON_ACK] = {
|
||||
.name = "AWAIT_COMMON_ACK",
|
||||
.in_event_mask = S(V51_CTRL_CE_MDU_START_TRAFFIC) |
|
||||
S(V51_CTRL_CE_MDU_STOP_TRAFFIC) |
|
||||
S(V51_CTRL_CE_MDU_CTRL) |
|
||||
S(V51_CTRL_CE_RX_COMMON_CONTROL) |
|
||||
S(V51_CTRL_CE_RX_COMMON_CONTROL_ACK),
|
||||
.out_state_mask = S(V51_CTRL_COM_ST_OUT_OF_SERVICE) |
|
||||
S(V51_CTRL_COM_ST_IN_SERVICE),
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm v51_ctrl_common_fsm = {
|
||||
.name = "V51_CTRL_COMMON",
|
||||
.states = v51_ctrl_common_fsm_states,
|
||||
.num_states = ARRAY_SIZE(v51_ctrl_common_fsm_states),
|
||||
.allstate_event_mask = 0,
|
||||
.allstate_action = NULL,
|
||||
.cleanup = NULL,
|
||||
.timer_cb = v51_ctrl_common_fsm_timer_cb,
|
||||
.log_subsys = DV5CTRL,
|
||||
.event_names = v51_ctrl_common_fsm_event_names,
|
||||
};
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* V5 Message encoding / sending
|
||||
***********************************************************************/
|
||||
|
||||
/* G.964 Section 14.4.1.1 / Table 48 */
|
||||
struct mgsb *v51_enc_ctrl_port(struct v5x_user_port *v5up, enum v51_ctrl_func_el cfe)
|
||||
{
|
||||
uint8_t cfe_ie = cfe;
|
||||
struct v51_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v51_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V51_CTRL_PDISC;
|
||||
l3h->l3_addr = v51_l3_addr_enc(v5up->nr, true);
|
||||
l3h->msg_type = V51_CTRL_MSGT_PORT_CTRL;
|
||||
|
||||
msgb_tlv_put(msg, V51_CTRL_IEI_CTRL_F_ELEMENT, 1, &cfe_ie);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.964 Section 14.4.1.2 / Table 49 */
|
||||
static struct msgb *v51_enc_ctrl_port_ack(struct v5x_user_port *v5up, enum v51_ctrl_func_el cfe)
|
||||
{
|
||||
uint8_t cfe_ie = cfe;
|
||||
struct v51_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v51_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V51_CTRL_PDISC;
|
||||
l3h->l3_addr = v51_l3_addr_enc(v5up->nr, true);
|
||||
l3h->msg_type = V51_CTRL_MSGT_PORT_CTRL_ACK;
|
||||
|
||||
msgb_tlv_put(msg, V51_CTRL_IEI_CTRL_F_ELEMENT, 1, &cfe_ie);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.964 Section 14.4.1.3 / Table 50 */
|
||||
static struct msgb *v51_enc_ctrl_common(struct v5x_instance *v5i, enum v51_ctrl_func_id cfi,
|
||||
uint8_t *rej_cause, uint8_t *variant, uint32_t *interface_id)
|
||||
{
|
||||
uint8_t cfi_ie = cfi;
|
||||
struct v51_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v51_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V51_CTRL_PDISC;
|
||||
l3h->l3_addr = v51_l3_addr_enc(V51_DLADDR_CTRL, true);
|
||||
l3h->msg_type = V51_CTRL_MSGT_COMMON_CTRL;
|
||||
|
||||
msgb_tlv_put(msg, V51_CTRL_IEI_CTRL_F_ID, 1, &cfi_ie);
|
||||
if (variant) {
|
||||
/* Conditional: Variant */
|
||||
msgb_tlv_put(msg, V51_CTRL_IEI_VARIANT, 1, variant);
|
||||
}
|
||||
if (rej_cause) {
|
||||
/* Conditional: Rejection Cause */
|
||||
msgb_put_u8(0xF0 | (*rej_cause & 0x0F));
|
||||
}
|
||||
if (interface_id) {
|
||||
/* Conditional: Interface Id */
|
||||
uint8_t iid_ie[3];
|
||||
osmo_store32be_ext(*interface_id, iid_ie, 3);
|
||||
msgb_tlv_put(msg, V51_CTRL_IEI_INTERFACE_ID, sizeof(iid_ie), iid_ie);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* G.964 Section 14.4.1.4 / Table 51 */
|
||||
static struct msgb *v51_enc_ctrl_common_ack(struct v5x_instance *v5i, enum v51_ctrl_func_id cfi)
|
||||
{
|
||||
uint8_t cfi_ie = cfi;
|
||||
struct v51_l3_hdr *l3h;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v51_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V51_CTRL_PDISC;
|
||||
l3h->l3_addr = v51_l3_addr_enc(V51_DLADDR_CTRL, true);
|
||||
l3h->msg_type = V51_CTRL_MSGT_COMMON_CTRL_ACK;
|
||||
|
||||
msgb_tlv_put(msg, V51_CTRL_IEI_CTRL_F_ID, 1, &cfi_ie);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* V5 Message receiving / decoding
|
||||
***********************************************************************/
|
||||
|
||||
static int v51_rcv_ctrl_port(struct v5x_user_port *v5up, struct msgb *msg, const struct tlv_parsed *tp)
|
||||
{
|
||||
uint16_t l3_addr;
|
||||
enum v51_ctrl_func_el cfe = *TLVP_VAL(tp, V51_CTRL_IEI_CTRL_F_ELEMENT);
|
||||
struct v5x_user_port *port = FIXME;
|
||||
|
||||
switch (l3h->msg_type) {
|
||||
case V51_CTRL_MSGT_PORT_CTRL:
|
||||
/* FIXME: send event to FSM */
|
||||
osmo_fsm_inst_dispatch(port->ctrl_fi, V51_CTRL_PE_RX_PORT_CONTROL, tp);
|
||||
/* send ACK to AN */
|
||||
return v51_tx(v5up->inst, v51_enc_ctrl_port_ack(v5up, cfe));
|
||||
case V51_CTRL_MSGT_PORT_CTRL_ACK:
|
||||
osmo_fsm_inst_dispatch(port->ctrl_fi, V51_CTRL_PE_RX_PORT_CONTROL_ACK, tp);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int v51_rcv_ctrl_common(struct v5x_instance *v5i, struct msgb *msg, const struct tlv_parsed *tp)
|
||||
{
|
||||
uint16_t l3_addr;
|
||||
enum v51_ctrl_func_id cfi = *TLVP_VAL(tp, V51_CTRL_IEI_CTRL_F_ID);
|
||||
|
||||
switch (l3h->msg_type) {
|
||||
case V51_CTRL_MSGT_COMMON_CTRL:
|
||||
/* send event to FSM */
|
||||
osmo_fsm_inst_dispatch(v5i->ctrl_fi, V51_CTRL_CE_RX_COMMON_CONTROL, tp);
|
||||
/* send ACK to AN */
|
||||
return v51_tx(v5up->inst, v51_enc_ctrl_common_ack(v5i, cfi));
|
||||
case V51_CTRL_MSGT_COMMON_CTRL_ACK:
|
||||
/* send event to FSM */
|
||||
osmo_fsm_inst_dispatch(v5i->ctrl_fi, V51_CTRL_CE_RX_COMMON_CONTROL_ACK, tp);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int v51_rcv_ctrl(struct v5x_instance *v5i, struct msgb *msg, const struct tlv_parsed *tp)
|
||||
{
|
||||
struct v51_l3_hdr *l3h = msgb_l3(msg);
|
||||
uint16_t l3_addr;
|
||||
bool is_isdn;
|
||||
int rc;
|
||||
|
||||
l3_addr = v51_l3_addr_dec(l3h->l3_addr, &is_isdn);
|
||||
if (!is_isdn)
|
||||
return -EINVAL;
|
||||
|
||||
switch (l3h->msg_type) {
|
||||
case V51_CTRL_MSGT_PORT_CTRL:
|
||||
case V51_CTRL_MSGT_PORT_CTRL_ACK:
|
||||
v5up = v5x_user_port_find(v5i, l4_addr);
|
||||
if (!v5up)
|
||||
return -ENODEV;
|
||||
return v5x_rcv_ctrl_port(v5up, msg, tp);
|
||||
case V51_CTRL_MSGT_COMMON_CTRL:
|
||||
case V51_CTRL_MSGT_COMMON_CTRL_ACK:
|
||||
if (l3_addr != V51_DLADDR_CTRL)
|
||||
return -EINVAL;
|
||||
return v5x_rcv_ctrl_common(v5i, msg, tp);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
612
v52_lcp_fsm.c
612
v52_lcp_fsm.c
|
@ -1,612 +0,0 @@
|
|||
/* ITu-T G.965 Section 16.2 V5.2-interface Link control FSM - LE side */
|
||||
|
||||
#include "v5x_internal.h"
|
||||
|
||||
/* 16.2.4.2.2 */
|
||||
enum v52_lcp_fsm_state {
|
||||
V52_LCPFSM_S_LE01_NOP_LINK_FAILURE,
|
||||
V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED,
|
||||
V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED,
|
||||
V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK,
|
||||
V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK,
|
||||
V52_LCPFSM_S_LE20_OP_OPERATIONAL,
|
||||
V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID,
|
||||
V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID,
|
||||
};
|
||||
|
||||
enum v52_lcp_event {
|
||||
V52_LCPFSM_E_MPH_AI, /* Activate Indication (L1 link operational) */
|
||||
V52_LCPFSM_E_MPH_DI, /* Deactivate Indication (L1 link not operational) */
|
||||
V52_LCPFSM_E_MDU_IDReq, /* Identification Request */
|
||||
V52_LCPFSM_E_FE_IDAck,
|
||||
V52_LCPFSM_E_MPH_IDI, /* Identification Indication */
|
||||
V52_LCPFSM_E_MPH_EIg, /* Identification Failure */
|
||||
V52_LCPFSM_E_FE_IDReq,
|
||||
V52_LCPFSM_E_MDU_IDAck, /* Send Link ID ACK */
|
||||
V52_LCPFSM_E_FE_IDRel,
|
||||
V52_LCPFSM_E_MDU_IDRej, /* Link ID Reject */
|
||||
V52_LCPFSM_E_FE_IDRej,
|
||||
V52_LCPFSM_E_MDU_LUBR, /* Link Unblock Request */
|
||||
V52_LCPFSM_E_MDU_LBI, /* Link Block Indication */
|
||||
V52_LCPFSM_E_FE302, /* AN Initiated unblocking */
|
||||
V52_LCPFSM_E_FE304, /* AN initiated link block */
|
||||
V52_LCPFSM_E_FE305, /* deferred link block request */
|
||||
V52_LCPFSM_E_FE306, /* non-deferred link block request */
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
static struct msgb *v51_enc_link_control(uint8_t link_id, enum v52_link_ctrl_func lcf)
|
||||
{
|
||||
struct v51_l3_hdr *l3h;
|
||||
uint8_t lcf_enc = lcf;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v51_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V51_LCP_PDISC;
|
||||
l3h->l3_addr = link_id
|
||||
l3h->msg_type = V52_CTRL_MSGT_LCP_LINK_CTRL;
|
||||
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION, 1, &lcf_enc);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static struct msgb *v51_enc_link_control_ack(uint8_t link_id, enum v52_link_ctrl_func lcf)
|
||||
{
|
||||
struct v51_l3_hdr *l3h;
|
||||
uint8_t lcf_enc = lcf;
|
||||
struct msgb *msg = msgb_alloc_v5x();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
l3h = (struct v51_l3_hdr *) msgb_put(msg, sizeof(*l3h));
|
||||
l3h->pdisc = V51_LCP_PDISC;
|
||||
l3h->l3_addr = link_id;
|
||||
l3h->msg_type = V52_CTRL_MSGT_LCP_LINK_CTRL_ACK;
|
||||
|
||||
msgb_tlv_put(msg, V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION, 1, &lcf_enc);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static void lcp_fsm_le01_link_failure(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* TODO: Send MDU-LAI */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* TODO: Send MDU-DI */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
/* TODO: Send FE303 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDRel:
|
||||
case V52_LCPFSM_E_FE_IDRej:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
/* TODO: Send MDU-DI */
|
||||
/* TODO: Send FE303 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
case V52_LCPFSM_E_FE302:
|
||||
case V52_LCPFSM_E_FE305:
|
||||
case V52_LCPFSM_E_FE306:
|
||||
/* TODO: Send FE303 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void lcp_fsm_le02_link_failure_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* TODO: Send MDU-LAI */
|
||||
/* TODO: Send MDU-LBl */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* TODO: Send MDU-DI */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
/* TODO: Send FE303 */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
/* TODO: Send MDU-DI */
|
||||
/* TODO: Send FE303 */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
case V52_LCPFSM_E_FE302:
|
||||
case V52_LCPFSM_E_FE305:
|
||||
case V52_LCPFSM_E_FE306:
|
||||
/* TODO: Send FE303 */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void lcp_fsm_le10_link_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* TODO: Send MDU-LAI */
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
/* TODO: Send MDU-DI */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* TODO: Send MDU-LBl */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
/* TODO: Send FE303 */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDRel:
|
||||
case V52_LCPFSM_E_FE_IDRej:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
/* TODO: Send FE303 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE302:
|
||||
/* TODO: Send MDL-LUBR */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
case V52_LCPFSM_E_FE305:
|
||||
case V52_LCPFSM_E_FE306:
|
||||
/* TODO: Send FE303 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void lcp_fsm_le11_local_link_unblock(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
/* TODO: Send MDU-DI */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* TODO: Send MDU-LBl */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
/* TODO: Send FE-IDRej */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDRel:
|
||||
case V52_LCPFSM_E_FE_IDRej:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
/* TODO: Send FE301 */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
/* TODO: Send FE303 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE302:
|
||||
/* TODO: Send MDU-LUBl */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
/* TODO: Send MDU-LBl */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE305:
|
||||
case V52_LCPFSM_E_FE306:
|
||||
/* TODO: Send FE303 */
|
||||
/* TODO: Send MDU-LBl */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void lcp_fsm_le12_remote_link_unblock(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
/* TODO: Send MDU-Dl */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* TODO: Send MDU-LUBR */
|
||||
/* TODO: Send MDU-IDRej */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
/* TODO: Send FE-IDRej */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
/* TODO: Send FE301 */
|
||||
/* TODO: Send MDU-LUBl */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
/* TODO: Send FE303 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE302:
|
||||
/* TODO: Send MDU-LUBR */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
/* TODO: Send MDU-LBl */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE305:
|
||||
case V52_LCPFSM_E_FE306:
|
||||
/* TODO: Send FE303 */
|
||||
/* TODO: Send MDU-LBl */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void lcp_fsm_le20_operational(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
/* TODO: Send MDU-DI */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE01_NOP_LINK_FAILURE, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* TODO: Send FE-IDReq */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDReq:
|
||||
/* TODO: Send MDU-IDReq */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDAck:
|
||||
/* TODO: Send MPH-ID */
|
||||
/* TODO: Send FE-IDAck */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDRej:
|
||||
/* TODO: Send FE-IDRej */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
/* TODO: Send FE301 */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
/* TODO: Send FE303 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE302:
|
||||
/* TODO: Send MDU-LUBI */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
/* TODO: Send MDU-LBI */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE305:
|
||||
/* TODO: Send MDU-LBR */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE306:
|
||||
/* TODO: Send MDU-LBRN */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void lcp_fsm_le21_op_remote_link_id(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
/* TODO: Send MDU-DI */
|
||||
/* TODO: Send MPH-NOR */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE01_NOP_LINK_FAILURE, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* TODO: Send MDU-IDRej */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDAck:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDRel:
|
||||
/* TODO: Send MDU-IDRel */
|
||||
/* TODO: Sen MPH-NOR */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDRej:
|
||||
/* TODO: Send FE-IDRej */
|
||||
/* TODO: Sen MPH-NOR */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
/* TODO: Send FE301 */
|
||||
/* TODO: Sen MPH-NOR */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
/* TODO: Send FE303 */
|
||||
/* TODO: Sen MPH-NOR */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE302:
|
||||
/* TODO: Send MDU-IDRel */
|
||||
/* TODO: Send MDU-LUBI */
|
||||
/* TODO: Sen MPH-NOR */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
/* TODO: Send MDU-LBI */
|
||||
/* TODO: Sen MPH-NOR */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE305:
|
||||
/* TODO: Send MDU-LBR */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE306:
|
||||
/* TODO: Send MDU-LBRN */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void lcp_fsm_le22_op_local_link_id(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V52_LCPFSM_E_MPH_AI:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_DI:
|
||||
/* TODO: Send MDU-DI */
|
||||
/* TODO: Send FE-IdRel */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE01_NOP_LINK_FAILURE, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDReq:
|
||||
/* ignore */
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_IDAck:
|
||||
/* TODO: Send MPH-IDR */
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_IDI:
|
||||
/* TODO: Send MDU-AI */
|
||||
/* TODO: Send FE-IDRel */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MPH_EIg:
|
||||
/* TODO: Send FE-IDRel */
|
||||
/* TODO: Send MDU-ELg */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE_IDRej:
|
||||
/* TODO: Send MDU-IDRej */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LUBR:
|
||||
/* TODO: Send FE301 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_MDU_LBI:
|
||||
/* TODO: Send FE303 */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE302:
|
||||
/* TODO: Send MDU-IDRej */
|
||||
/* TODO: Send MDU-LUBI */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE304:
|
||||
/* TODO: Send MDU-LBI */
|
||||
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V52_LCPFSM_E_FE305:
|
||||
/* TODO: Send MDU-LBR */
|
||||
break;
|
||||
case V52_LCPFSM_E_FE306:
|
||||
/* TODO: Send MDU-LBRN */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Table 17G.965 */
|
||||
static const struct osmo_fsm_state v52_lcp_le_fsm_states[] = {
|
||||
[V52_LCPFSM_S_LE01_NOP_LINK_FAILURE] = {
|
||||
.name = "LE0.1_LINK_FAILURE",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDRel) |
|
||||
S(V52_LCPFSM_E_FE_IDRej) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) |
|
||||
S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED),
|
||||
.action = lcp_fsm_le01_link_failure,
|
||||
},
|
||||
[V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED] = {
|
||||
.name = "LE0.2_LINK_FAILURE_AND_BLOCKED",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
|
||||
.action = lcp_fsm_le02_link_failure_blocked,
|
||||
},
|
||||
[V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED] = {
|
||||
.name = "LE1.0_NOP_LINK_BLOCKED",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDRel) |
|
||||
S(V52_LCPFSM_E_FE_IDRej) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED) |
|
||||
S(V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK) |
|
||||
S(V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID),
|
||||
.action = lcp_fsm_le10_link_blocked,
|
||||
},
|
||||
[V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK] = {
|
||||
.name = "LE1.1_NOP_LOCAL_LINK_UNBLOCK",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDRel) |
|
||||
S(V52_LCPFSM_E_FE_IDRej) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED) |
|
||||
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED) |
|
||||
S(V52_LCPFSM_S_LE20_OP_OPERATIONAL),
|
||||
.action = lcp_fsm_le11_local_link_unblock,
|
||||
},
|
||||
[V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK] = {
|
||||
.name = "LE1.2_NOP_REMOTE_LINK_UNBLOCK",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED) |
|
||||
S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) |
|
||||
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
|
||||
.action = lcp_fsm_le12_remote_link_unblock,
|
||||
},
|
||||
[V52_LCPFSM_S_LE20_OP_OPERATIONAL] = {
|
||||
.name = "LE2.0_OP_OPERATIONAL",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_MDU_IDAck) |
|
||||
S(V52_LCPFSM_E_MDU_IDRej) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE01_NOP_LINK_FAILURE) |
|
||||
S(V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID) |
|
||||
S(V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID) |
|
||||
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
|
||||
.action = lcp_fsm_le20_operational,
|
||||
},
|
||||
[V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID] = {
|
||||
.name = "LE2.1_OP_REMOTE_LINK_ID",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_MDU_IDAck) |
|
||||
S(V52_LCPFSM_E_MDU_IDRel) |
|
||||
S(V52_LCPFSM_E_MDU_IDRej) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE01_NOP_LINK_FAILURE) |
|
||||
S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) |
|
||||
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
|
||||
.action = lcp_fsm_le21_op_remote_link_id,
|
||||
},
|
||||
[V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID] = {
|
||||
.name = "LE2.2_OP_LOCAL_LINK_ID",
|
||||
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
|
||||
S(V52_LCPFSM_E_MPH_DI) |
|
||||
S(V52_LCPFSM_E_MDU_IDReq) |
|
||||
S(V52_LCPFSM_E_MDU_IDAck) |
|
||||
S(V52_LCPFSM_E_MPH_IDI) |
|
||||
S(V52_LCPFSM_E_MPH_EIg) |
|
||||
S(V52_LCPFSM_E_FE_IDReq) |
|
||||
S(V52_LCPFSM_E_MDU_LUBR) |
|
||||
S(V52_LCPFSM_E_MDU_LBI) |
|
||||
S(V52_LCPFSM_E_FE302) |
|
||||
S(V52_LCPFSM_E_FE304) |
|
||||
S(V52_LCPFSM_E_FE305) |
|
||||
S(V52_LCPFSM_E_FE306),
|
||||
.out_state_mask = S(V52_LCPFSM_S_LE01_NOP_LINK_FAILURE) |
|
||||
S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) |
|
||||
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
|
||||
.action =lcp_fsm_le22_op_local_link_id,
|
||||
},
|
||||
};
|
||||
|
||||
struct osmo_fsm v52_lcp_le_fsm = {
|
||||
.name = "V52_LCP_LE",
|
||||
.states = v52_lcp_le_fsm_states,
|
||||
.num_states = ARRAY_SIZE(v52_lcp_le_fsm_states),
|
||||
.timer_cb = NULL,
|
||||
.log_subsys = 0,
|
||||
.event_names = v52_lcp_le_fsm_event_names,
|
||||
};
|
|
@ -1,448 +0,0 @@
|
|||
/* ITU-T G.964 Section 14.1.3.2.2 ISDN user port FSM - LE */
|
||||
|
||||
/***********************************************************************/
|
||||
/* internal data structures */
|
||||
/***********************************************************************/
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
|
||||
|
||||
/***********************************************************************/
|
||||
/* state names, event names, primitives, ... */
|
||||
/***********************************************************************/
|
||||
|
||||
/* Table 35/G.964 */
|
||||
enum v51_mph_prim {
|
||||
V51_LE_MPH_UB, /* Unblock (req, ind)*/
|
||||
V51_LE_MPH_B, /* Block (req, ind) */
|
||||
V51_LE_MPH_A, /* Activate (req, ind) */
|
||||
V51_LE_MPH_AW, /* Access activation by user (ind) */
|
||||
V51_LE_MPH_DSA, /* DS Activated (ind) */
|
||||
V51_LE_MPH_D, /* Deactivate (req, ind) */
|
||||
V51_LE_MPH_G, /* Grading Information */
|
||||
V51_LE_MPH_DB, /* Block D-Channel from user port */
|
||||
V51_LE_MPH_DU, /* Unblock D-Channel from user port */
|
||||
};
|
||||
|
||||
/* 14.1.3.2.2 ISDN user port FSM - LE(ISDN port) */
|
||||
enum v51_le_isdn_port_state {
|
||||
V51_LE_UP_I_S_LE10_NOP_BLOCKED, /* LE1.0 */
|
||||
V51_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK, /* LE1.1 */
|
||||
V51_LE_UP_I_S_LE12_NOP_REMOTE_UNBLOCK, /* LE1.2 */
|
||||
V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, /* LE2.0 */
|
||||
V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED, /* LE2.1 */
|
||||
V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED, /* LE2.2 */
|
||||
};
|
||||
|
||||
/* 14.2.3.2.2 PSTN user port FSM - LE(PSTN port) */
|
||||
enum v51_le_pstn_port_state {
|
||||
V51_LE_UP_P_S_LE10_NOP_BLOCKED, /* LE1.0 */
|
||||
V51_LE_UP_P_S_LE11_NOP_LOCAL_UNBLOCK, /* LE1.1 */
|
||||
V51_LE_UP_P_S_LE12_NOP_REMOTE_UNBLOCK, /* LE1.2 */
|
||||
V51_LE_UP_P_S_LE22_OPERATIONAL, /* LE2.0 */
|
||||
};
|
||||
|
||||
enum v51_ctrl_le_port_fsm_event {
|
||||
/* inbound function elements from AN (Table 34/G.964) */
|
||||
V51_CUP_LE_FE102_ACTIV_INIT_USER_IND,
|
||||
V51_CUP_LE_FE103_DS_ACTIVATED_IND,
|
||||
V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND,
|
||||
V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND,
|
||||
V51_CUP_LE_FE202_UNBLOCK_REQ,
|
||||
V51_CUP_LE_FE202_UNBLOCK_ACK,
|
||||
V51_CUP_LE_FE204_BLOCK_CMD,
|
||||
V51_CUP_LE_FE205_BLOCK_REQ,
|
||||
V51_CUP_LE_FE206_PREFORMANCE_GRADING_IND,
|
||||
/* inbound primitives from Mgmt (Table 35/G.964) */
|
||||
V51_CUP_LE_MPH_UBR_UNBLOCK_REQ,
|
||||
V51_CUP_LE_MPH_BI_BLOCK_CMD,
|
||||
V51_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ,
|
||||
V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ,
|
||||
V51_CUP_LE_MPH_DB_BLOCK_DCHAN_REQ,
|
||||
V51_CUP_LE_MPH_DU_BLOCK_DCHAN_REQ,
|
||||
};
|
||||
|
||||
static void isdn_up_le10_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V51_CUP_LE_FE102_ACTIV_INIT_USER_IND:
|
||||
case V51_CUP_LE_FE103_DS_ACTIVATED_IND:
|
||||
case V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
|
||||
case V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
|
||||
case V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V51_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
/* Send FE201 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE201_UNBLOCK));
|
||||
osmo_fsm_inst_state_change(fi, V51_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
/* Send FE203 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE203_BLOCK));
|
||||
break;
|
||||
case V51_CUP_LE_FE202_UNBLOCK_ACK:
|
||||
/* TODO: Send MPU-UBR */
|
||||
osmo_fsm_inst_state_change(fi, V51_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE204_BLOCK_CMD:
|
||||
case V51_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void isdn_up_le11_local_unblock(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V51_CUP_LE_FE102_ACTIV_INIT_USER_IND:
|
||||
/* TODO: send MPG-AWI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE103_DS_ACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
|
||||
/* TODO: send MPH-AI*/
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
|
||||
case V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V51_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
/* send FE201 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE201_UNBLOCK));
|
||||
break;
|
||||
case V51_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
/* send FE203 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE203_BLOCK));
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE202_UNBLOCK_ACK:
|
||||
/* TODO: send MPH-UBI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE204_BLOCK_CMD:
|
||||
/* TODO: send MPH-BI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void isdn_up_le12_remote_unblock(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V51_CUP_LE_FE103_DS_ACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
|
||||
case V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V51_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
/* TODO: send MPH-UBI */
|
||||
/* send FE201 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE201_UNBLOCK));
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
/* send FE203 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE203_BLOCK));
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE202_UNBLOCK_ACK:
|
||||
/* TODO: send MPH-UBR */
|
||||
break;
|
||||
case V51_CUP_LE_FE204_BLOCK_CMD:
|
||||
/* TODO: send MPH-BI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* ignore */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* LE 2.0 Operational deactivated */
|
||||
static void isdn_up_le20_op_deact(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V51_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ:
|
||||
/* Send FE101 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE101_ACTIVATE_ACCESS));
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE102_ACTIV_INIT_USER_IND:
|
||||
/* TODO: Send MPH-AWI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE103_DS_ACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
|
||||
/* TODO: Send MPH-AI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
|
||||
/* Send FE105 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE105_DEACTIVATE_ACCESS));
|
||||
break;
|
||||
case V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
|
||||
/* TODO: Send MPH-DI */
|
||||
break;
|
||||
case V51_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
/* Send FE201 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE201_UNBLOCK));
|
||||
break;
|
||||
case V51_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
/* Send FE203 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE203_BLOCK));
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE202_UNBLOCK_ACK:
|
||||
/* TODO: Send MPH-UBI */
|
||||
break;
|
||||
case V51_CUP_LE_FE204_BLOCK_CMD:
|
||||
/* TODO: Send MPH-BI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* TODO: Send MPH-BR */
|
||||
break;
|
||||
case V51_CUP_LE_FE206_PREFORMANCE_GRADING_IND:
|
||||
/* TODO: Send MPH-GI */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* LE 2.1 Access initiated */
|
||||
static void isdn_up_le21_op_act_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V51_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ:
|
||||
case V51_CUP_LE_FE102_ACTIV_INIT_USER_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V51_CUP_LE_FE103_DS_ACTIVATED_IND:
|
||||
/* TODO: Send MPH-DSAI */
|
||||
break;
|
||||
case V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
|
||||
/* TODO: Send MPH-AI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
|
||||
/* Send FE105 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE105_DEACTIVATE_ACCESS));
|
||||
/* TODO: Send MPH-DI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
|
||||
/* TODO: Send MPH-DI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
/* Send FE201 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE201_UNBLOCK));
|
||||
break;
|
||||
case V51_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
/* Send FE203 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE203_BLOCK));
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE202_UNBLOCK_ACK:
|
||||
/* TODO: Send MPH-UBI */
|
||||
break;
|
||||
case V51_CUP_LE_FE204_BLOCK_CMD:
|
||||
/* TODO: Send MPH-BI */
|
||||
/* TODO: Send MPH-DI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* TODO: Send MPH-BR */
|
||||
break;
|
||||
case V51_CUP_LE_FE206_PREFORMANCE_GRADING_IND:
|
||||
/* TODO: Send MPH-GI */
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* LE 2.2 Access activated */
|
||||
static void isdn_up_le22_op_acc_act(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
|
||||
/* ignore */
|
||||
break;
|
||||
case V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
|
||||
/* Send FE105 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE105_DEACTIVATE_ACCESS));
|
||||
/* TODO: Send MPH-DI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
|
||||
/* TODO: Send MPH-DI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_MPH_UBR_UNBLOCK_REQ:
|
||||
/* TODO: Send MPH-AI */
|
||||
/* Send FE201 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE201_UNBLOCK));
|
||||
break;
|
||||
case V51_CUP_LE_MPH_BI_BLOCK_CMD:
|
||||
/* Send FE203 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE203_BLOCK));
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE202_UNBLOCK_ACK:
|
||||
/* TODO: Send MPH-UBI */
|
||||
break;
|
||||
case V51_CUP_LE_FE204_BLOCK_CMD:
|
||||
/* TODO: Send MPH-BI */
|
||||
/* TODO: Send MPH-DI */
|
||||
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
|
||||
break;
|
||||
case V51_CUP_LE_FE205_BLOCK_REQ:
|
||||
/* TODO: Send MPH-BR */
|
||||
break;
|
||||
case V51_CUP_LE_FE206_PREFORMANCE_GRADING_IND:
|
||||
/* TODO: Send MPH-GI */
|
||||
break;
|
||||
case V51_CUP_LE_MPH_DB_BLOCK_DCHAN_REQ:
|
||||
/* Send FE207 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE207_D_CHANNEL_BLOCK));
|
||||
break;
|
||||
case V51_CUP_LE_MPH_DU_BLOCK_DCHAN_REQ:
|
||||
/* Send FE208 */
|
||||
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE208_D_CHANNEL_UNBLOCK));
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Table 38/G.964 LE (ISDN port) FSM for ISDN basic access user ports */
|
||||
static const struct osmo_fsm_state v51_ctrl_le_port_fsm_states[] = {
|
||||
[V51_LE_UP_I_S_LE10_NOP_BLOCKED] = {
|
||||
.name = "LE1.0_BLOCKED",
|
||||
.in_event_mask = S(V51_CUP_LE_FE102_ACTIV_INIT_USER_IND) |
|
||||
S(V51_CUP_LE_FE103_DS_ACTIVATED_IND) |
|
||||
S(V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
|
||||
S(V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
|
||||
S(V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
|
||||
S(V51_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V51_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V51_CUP_LE_FE202_UNBLOCK_ACK) |
|
||||
S(V51_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V51_CUP_LE_FE205_BLOCK_REQ),
|
||||
.out_state_mask = S(V51_LE_UP_I_S_LE10_NOP_BLOCKED) |
|
||||
S(V51_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK) |
|
||||
S(V51_LE_UP_I_S_LE12_NOP_REMOTE_UNBLOCK),
|
||||
.action = isdn_up_le10_blocked,
|
||||
},
|
||||
[V51_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK] = {
|
||||
.name = "LE1.1_LOCAL_UNBLOCK",
|
||||
.in_event_mask = S(V51_CUP_LE_FE102_ACTIV_INIT_USER_IND) |
|
||||
S(V51_CUP_LE_FE103_DS_ACTIVATED_IND) |
|
||||
S(V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
|
||||
S(V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
|
||||
S(V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
|
||||
S(V51_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V51_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V51_CUP_LE_FE202_UNBLOCK_ACK) |
|
||||
S(V51_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V51_CUP_LE_FE205_BLOCK_REQ),
|
||||
.out_state_mask = S(V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED) |
|
||||
S(V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED) |
|
||||
S(V51_LE_UP_I_S_LE10_NOP_BLOCKED) |
|
||||
S(V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED),
|
||||
.action = isdn_up_le11_local_unblock,
|
||||
},
|
||||
[V51_LE_UP_I_S_LE12_NOP_REMOTE_UNBLOCK] = {
|
||||
.name = "LE1.2_REMOTE_UNBLOCK",
|
||||
.in_event_mask = S(V51_CUP_LE_FE103_DS_ACTIVATED_IND) |
|
||||
S(V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
|
||||
S(V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
|
||||
S(V51_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V51_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V51_CUP_LE_FE202_UNBLOCK_ACK) |
|
||||
S(V51_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V51_CUP_LE_FE205_BLOCK_REQ),
|
||||
.out_state_mask = S(V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED) |
|
||||
S(V51_LE_UP_I_S_LE10_NOP_BLOCKED),
|
||||
.action = isdn_up_le12_remote_unblock,
|
||||
},
|
||||
[V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED] = {
|
||||
.name = "LE2.0_OPERATIONAL_DEACTIVATED",
|
||||
.in_event_mask = S(V51_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ) |
|
||||
S(V51_CUP_LE_FE102_ACTIV_INIT_USER_IND) |
|
||||
S(V51_CUP_LE_FE103_DS_ACTIVATED_IND) |
|
||||
S(V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
|
||||
S(V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
|
||||
S(V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
|
||||
S(V51_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V51_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V51_CUP_LE_FE202_UNBLOCK_ACK) |
|
||||
S(V51_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V51_CUP_LE_FE205_BLOCK_REQ) |
|
||||
S(V51_CUP_LE_FE206_PREFORMANCE_GRADING_IND),
|
||||
.out_state_mask = S(V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED) |
|
||||
S(V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED) |
|
||||
S(V51_LE_UP_I_S_LE10_NOP_BLOCKED),
|
||||
.action = isdn_up_le21_op_deact,
|
||||
},
|
||||
[V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED] = {
|
||||
.name = "LE2.1_ACTIVATION_INITIATED",
|
||||
.in_event_mask = S(V51_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ) |
|
||||
S(V51_CUP_LE_FE102_ACTIV_INIT_USER_IND) |
|
||||
S(V51_CUP_LE_FE103_DS_ACTIVATED_IND) |
|
||||
S(V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
|
||||
S(V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
|
||||
S(V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
|
||||
S(V51_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V51_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V51_CUP_LE_FE202_UNBLOCK_ACK) |
|
||||
S(V51_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V51_CUP_LE_FE205_BLOCK_REQ),
|
||||
.out_state_mask = S(V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED) |
|
||||
S(V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED) |
|
||||
S(V51_LE_UP_I_S_LE10_NOP_BLOCKED),
|
||||
.action = isdn_up_le21_op_act_init,
|
||||
},
|
||||
[V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED] = {
|
||||
.name = "LE2.2_ACCESS_ACTIVATED",
|
||||
.in_event_mask = S(V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
|
||||
S(V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
|
||||
S(V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
|
||||
S(V51_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
|
||||
S(V51_CUP_LE_MPH_BI_BLOCK_CMD) |
|
||||
S(V51_CUP_LE_FE202_UNBLOCK_ACK) |
|
||||
S(V51_CUP_LE_FE204_BLOCK_CMD) |
|
||||
S(V51_CUP_LE_FE205_BLOCK_REQ) |
|
||||
S(V51_CUP_LE_FE206_PREFORMANCE_GRADING_IND) |
|
||||
S(V51_CUP_LE_MPH_DB_BLOCK_DCHAN_REQ) |
|
||||
S(V51_CUP_LE_MPH_DU_BLOCK_DCHAN_REQ),
|
||||
.out_state_mask = S(V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED) |
|
||||
S(V51_LE_UP_I_S_LE10_NOP_BLOCKED),
|
||||
.action = isdn_up_le22_op_acc_act,
|
||||
},
|
||||
};
|
||||
|
92
v5x_data.c
92
v5x_data.c
|
@ -1,92 +0,0 @@
|
|||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
#include "v5x_internal.h"
|
||||
|
||||
static LLIST_HEAD(v5_instances);
|
||||
|
||||
struct v5x_instance *v5x_instance_alloc(void *ctx)
|
||||
{
|
||||
struct v5x_instance *v5i = talloc_zero(ctx, struct v5x_instance);
|
||||
if (!v5i)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&v5i->interfaces);
|
||||
INIT_LLIST_HEAD(&v5i->user_ports);
|
||||
/* TODO: allocate ctrl_fi */
|
||||
|
||||
llist_add_tail(&v5i->list, &v5_instances);
|
||||
|
||||
return v5i;
|
||||
}
|
||||
|
||||
static void v5x_ts_init(struct v5x_timeslot *v5ts, uint8_t nr, struct v5x_link *v5l, uint16_t l3_addr)
|
||||
{
|
||||
v5ts->nr = nr;
|
||||
v5ts->link = v5l;
|
||||
v5ts->l3_address = l3_addr;
|
||||
}
|
||||
|
||||
static void v5x_cchan_init(struct v5x_c_channel *v5cc, struct v5x_link *v5l, struct v5x_timeslot *v5ts, bool active)
|
||||
{
|
||||
v5cc->link = v5l;
|
||||
v5cc->ts = v5ts;
|
||||
v5cc->active = active;
|
||||
}
|
||||
|
||||
static void v5x_link_init(struct v5x_link *v5l, struct v5x_interface *v5if, uint8_t id)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
v5l->id = id;
|
||||
v5l->interface = v5if;
|
||||
|
||||
for (i = 1; i < ARRAY_SIZE(v5l->ts); i++) {
|
||||
v5x_ts_init(&v5l->ts[i], i, v5l, 0 /*l3_addr*/);
|
||||
}
|
||||
|
||||
/* primary c-channel must always be present */
|
||||
v5x_cchan_init(&v5l->c_channel[0], v5l, &v5l->ts[16], true);
|
||||
}
|
||||
|
||||
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect)
|
||||
{
|
||||
struct v5x_interface *v5if = talloc_zero(v5i, struct v5x_interface);
|
||||
//struct v5x_link *v5l;
|
||||
struct v5x_c_channel *primary_c_chan;
|
||||
|
||||
v5if->instance = v5i;
|
||||
v5if->dialect = dialect;
|
||||
|
||||
/* primary link must alwasy be present */
|
||||
v5x_link_init(&v5if->links[0], v5if, 0);
|
||||
v5if->primary_link = &v5if->links[0];
|
||||
|
||||
primary_c_chan = &v5if->primary_link->c_channel[0];
|
||||
|
||||
|
||||
/* TODO: allocate fi */
|
||||
|
||||
//lapd_dl_init2(&v5if->control.dl, k, v_rnage, maxf, "control");
|
||||
v5if->control.c_chan = primary_c_chan;
|
||||
|
||||
//lapd_dl_init2(&v5if->pstn.dl, k, v_rnage, maxf, "pstn");
|
||||
v5if->pstn.c_chan = primary_c_chan;
|
||||
|
||||
if (v5if->dialect == V5X_DIALECT_V52) {
|
||||
//lapd_dl_init2(&v5if->lcp.dl, k, v_rnage, maxf, "lcp");
|
||||
v5if->lcp.c_chan = primary_c_chan;
|
||||
|
||||
//lapd_dl_init2(&v5if->bcc.dl, k, v_rnage, maxf, "bcc");
|
||||
v5if->bcc.c_chan = primary_c_chan;
|
||||
|
||||
//lapd_dl_init2(&v5if->protection[0].dl, k, v_rnage, maxf, "protection0");
|
||||
v5if->protection[0].c_chan = primary_c_chan;
|
||||
|
||||
//protection[1] ?
|
||||
}
|
||||
|
||||
llist_add_tail(&v5if->list, &v5i->interfaces);
|
||||
|
||||
return v5if;
|
||||
}
|
123
v5x_internal.h
123
v5x_internal.h
|
@ -1,123 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/lapd_core.h>
|
||||
|
||||
struct osmo_fsm_inst;
|
||||
|
||||
enum v5x_dialect {
|
||||
V5X_DIALECT_V51 = 1,
|
||||
V5X_DIALECT_V52 = 2,
|
||||
};
|
||||
|
||||
/* forward-declarations */
|
||||
struct v5x_interface;
|
||||
struct v5x_instance;
|
||||
struct v5x_link;
|
||||
|
||||
/* A C-channel is a 64k timeslot used for signalling */
|
||||
struct v5x_c_channel {
|
||||
struct v5x_link *link; /* back-pointer */
|
||||
struct v5x_timeslot *ts; /* E1 link timeslot. NULL = C-channel doesn't exist */
|
||||
bool active; /* false == standby */
|
||||
};
|
||||
|
||||
/* one physical E1 timeslot used on an E1 interface part of a V5.2 interface */
|
||||
struct v5x_timeslot {
|
||||
uint8_t nr;
|
||||
struct v5x_link *link; /* back-pointer */
|
||||
uint16_t l3_address;
|
||||
};
|
||||
|
||||
/* one physical E1 interface used within a V5.2 interface */
|
||||
struct v5x_link {
|
||||
uint8_t id;
|
||||
struct v5x_interface *interface; /* back-pointer */
|
||||
struct v5x_timeslot ts[32]; /* 32 E1 slots; 0 not available */
|
||||
struct v5x_c_channel c_channel[3]; /* 64k signaling possible on TS16, TS15 and TS31 */
|
||||
struct osmo_fsm_inst *l1_link_fi; /* Layer 1 Link FSM instance */
|
||||
};
|
||||
|
||||
/* one V5.x interface between AN (Access Network) and LE (Local Exchange) */
|
||||
struct v5x_interface {
|
||||
struct llist_head list; /* instance.interfaces */
|
||||
struct v5x_instance *instance; /* back-pointer */
|
||||
enum v5x_dialect dialect;
|
||||
struct v5x_link *primary_link; /* one of the links below */
|
||||
struct v5x_link *secondary_link; /* one of the links below */
|
||||
/* 1..16 links in one interface */
|
||||
struct v5x_link links[16];
|
||||
|
||||
struct osmo_fsm_inst *fi; /* Interface FSM instance */
|
||||
struct {
|
||||
struct lapd_datalink dl; /* Control data link */
|
||||
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
|
||||
} control;
|
||||
struct {
|
||||
struct lapd_datalink dl; /* Link control data link */
|
||||
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
|
||||
struct osmo_fsm_inst *fi; /* Link Control FSM instance */
|
||||
} lcp;
|
||||
struct {
|
||||
struct lapd_datalink dl; /* PSTN data link */
|
||||
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
|
||||
} pstn;
|
||||
struct {
|
||||
struct lapd_datalink dl; /* BCC data link */
|
||||
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
|
||||
} bcc;
|
||||
struct {
|
||||
struct lapd_datalink dl; /* Protection data link 1 + 2 */
|
||||
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
|
||||
} protection[2];
|
||||
};
|
||||
|
||||
/* one user-facing port (subscriber line) */
|
||||
struct v5x_user_port {
|
||||
struct llist_head list; /* part of v5x_instance.ports */
|
||||
struct v5x_instance *inst; /* back-pointer to instance we're part of */
|
||||
|
||||
uint16_t nr; /* port-number in decoded form (0..32767) */
|
||||
bool is_isdn; /* is this port an ISDN port? */
|
||||
|
||||
struct {
|
||||
struct osmo_fsm_inst *ctrl_fi; /* control protocol FSM instance */
|
||||
struct osmo_fsm_inst *fi; /* port state FSM instance */
|
||||
} isdn;
|
||||
struct {
|
||||
} pstn;
|
||||
};
|
||||
|
||||
struct v5x_instance {
|
||||
struct llist_head list; /* part of global list of instances */
|
||||
struct llist_head interfaces; /* v5x_interface.list */
|
||||
struct llist_head user_ports; /* list of v5x_user_port */
|
||||
struct osmo_fsm_inst *ctrl_fi; /* common control FSM */
|
||||
};
|
||||
|
||||
struct v5x_instance *v5x_instance_alloc(void *ctx);
|
||||
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect);
|
458
v5x_protocol.c
458
v5x_protocol.c
|
@ -1,458 +0,0 @@
|
|||
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unitstd.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/util.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include "v5x_protocol.h"
|
||||
|
||||
const struct tlv_definition v51_ctrl_tlv_def[] = {
|
||||
/* single byte: PSTN / G.964 Table 17 */
|
||||
[V51_CTRL_IEI_PULSE_NOTIFICATION] = TLV_TYPE_SINGLE_TV,
|
||||
[V51_CTLR_IEI_LINE_NOTIFICATION] = TLV_TYPE_SINGLE_TV,
|
||||
[V51_CTLR_IEI_STATE] = TLV_TYPE_SINGLE_TV,
|
||||
[V51_CTLR_IEI_AUTONOMOUS_SIG_SEQ] = TLV_TYPE_SINGLE_TV,
|
||||
[V51_CTLR_IEI_SEQUENCE_RESPONSE] = TLV_TYPE_SINGLE_TV,
|
||||
/* single byte: ISDN / G.964 Table 53 */
|
||||
[V51_CTRL_IEI_PERFORMANCE_GRADING] = TLV_TYPE_SINGLE_TV,
|
||||
[V51_CTRL_IEI_REJECTION_CAUSE] = TLV_TYPE_SINGLE_TV,
|
||||
|
||||
/* variable length: PSTN / G.964 Table 17 */
|
||||
[V51_CTRL_IEI_SEQUENCE_NR] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_CADENCED_RINGING] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_PULSED_SIGNAL] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_STEADY_SIGNAL] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_DIGIT_SIGNAL] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_RECOGNITION_TIME] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_ENABLE_AUTONOMOUS_ACK] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_DISABLE_AUTONOMOUS_ACK] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_CAUSE] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_RESOURCE_UNAVAILABLE] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_ENABLE_METERING] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_METERING_REPORT] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_ATTENUATION] = TLV_TYPE_TLV,
|
||||
|
||||
/* variable length: ISDN / G.964 Table 53 */
|
||||
[V51_CTRL_IEI_CTRL_F_ELEMENT] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_CTRL_F_ID] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_VARIANT] = TLV_TYPE_TLV,
|
||||
[V51_CTRL_IEI_INTERFACE_ID] = TLV_TYPE_TLV,
|
||||
|
||||
/* variable length: LCP / G.965 Table FIXME */
|
||||
[V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION] = TLV_TYPE_TLV,
|
||||
/* variable length: BCC */
|
||||
[V52_CTRL_IEI_BCC_USER_PORT_ID] = TLV_TYPE_TLV,
|
||||
[V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID] = TLV_TYPE_TLV,
|
||||
[V52_CTRL_IEI_BCC_V5_TS_ID] = TLV_TYPE_TLV,
|
||||
[V52_CTRL_IEI_BCC_MULTI_TS_MAP] = TLV_TYPE_TLV,
|
||||
[V52_CTRL_IEI_BCC_REJECT_CAUSE] = TLV_TYPE_TLV,
|
||||
[V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE] = TLV_TYPE_TLV,
|
||||
[V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE] = TLV_TYPE_TLV,
|
||||
[V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY] = TLV_TYPE_TLV,
|
||||
/* variable-length: Protection */
|
||||
[V52_CTRL_IEI_PP_SEQUENCE_NR] = TLV_TYPE_TLV,
|
||||
[V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID] = TLV_TYPE_TLV,
|
||||
[V52_CTRL_IEI_PP_REJECTION_CAUSE] = TLV_TYPE_TLV,
|
||||
[V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE] = TLV_TYPE_TLV,
|
||||
};
|
||||
|
||||
|
||||
const struct value_string v51_ctrl_msg_typ_str[] = {
|
||||
{ V51_CTRL_MSGT_ESTABLISH, "ESTABLISH" },
|
||||
{ V51_CTRL_MSGT_ESTABLISH_ACK, "ESTABLISH_ACK" },
|
||||
{ V51_CTRL_MSGT_SIGNAL, "SIGNAL" },
|
||||
{ V51_CTRL_MSGT_SIGNAL_ACK, "SIGNAL_ACK" },
|
||||
{ V51_CTRL_MSGT_DISCONNECT, "DISCONNECT" },
|
||||
{ V51_CTRL_MSGT_DISCONNECT_ACK, "DISCONNECT_ACK" },
|
||||
{ V51_CTRL_MSGT_STATUS_ENQUIRY, "STATUS_ENQUIRY" },
|
||||
{ V51_CTRL_MSGT_STATUS, "STATUS" },
|
||||
{ V51_CTRL_MSGT_PROTOCOL_PARAMETER, "PROTOCOL_PARAMETER" },
|
||||
{ V51_CTRL_MSGT_PORT_CTRL, "PORT_CTRL" },
|
||||
{ V51_CTRL_MSGT_PORT_CTRL_ACK, "PORT_CTRL_ACK" },
|
||||
{ V51_CTRL_MSGT_COMMON_CTRL, "COMMON_CTRL" },
|
||||
{ V51_CTRL_MSGT_COMMON_CTRL_ACK, "COMMON_CTRL_ACK" },
|
||||
{ V52_CTRL_MSGT_SWITCH_OVER_REQ, "SWITCH_OVER_REQ" },
|
||||
{ V52_CTRL_MSGT_SWITCH_OVER_COM, "SWITCH_OVER_COM" },
|
||||
{ V52_CTRL_MSGT_OS_SWITCH_OVER_COM, "OS_SWITCH_OVER_COM" },
|
||||
{ V52_CTRL_MSGT_SWITCH_OVER_ACK, "SWITCH_OVER_ACK" },
|
||||
{ V52_CTRL_MSGT_SWITCH_OVER_REJECT, "SWITCH_OVER_REJECT" },
|
||||
{ V52_CTRL_MSGT_RESET_SN_COM, "RESET_SN_COM" },
|
||||
{ V52_CTRL_MSGT_RESET_SN_ACK, "RESET_SN_ACK" },
|
||||
{ V52_CTRL_MSGT_ALLOCATION, "ALLOCATION" },
|
||||
{ V52_CTRL_MSGT_ALLOCATION_COMPLETE, "ALLOCATION_COMPLETE" },
|
||||
{ V52_CTRL_MSGT_ALLOCATION_REJECT, "ALLOCATION_REJECT" },
|
||||
{ V52_CTRL_MSGT_DE_ALLOCATION, "DE_ALLOCATION" },
|
||||
{ V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE, "DE_ALLOCATION_COMPLETE" },
|
||||
{ V52_CTRL_MSGT_DE_ALLOCATION_REJECT, "DE_ALLOCATION_REJECT" },
|
||||
{ V52_CTRL_MSGT_AUDIT, "AUDIT" },
|
||||
{ V52_CTRL_MSGT_AUDIT_COMPLETE, "AUDIT_COMPLETE" },
|
||||
{ V52_CTRL_MSGT_AN_FAULT, "AN_FAULT" },
|
||||
{ V52_CTRL_MSGT_AN_FAULT_ACKNOWLEDGE "AN_FAULT_ACKNOWLEDGE" },
|
||||
{ V52_CTRL_MSGT_PROTOCOL_ERROR "PROTOCOL_ERROR" },
|
||||
{ V52_CTRL_MSGT_LINK_CTRL, "LINK_CTRL" },
|
||||
{ V52_CTRL_MSGT_LINK_CTRL_ACK, "LINK_CTRL_ACK" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string v51_ctrl_iei_str[] = {
|
||||
{ V51_CTRL_IEI_PULSE_NOTIFICATION, "PULSE_NOTIFICATION" },
|
||||
{ V51_CTLR_IEI_LINE_NOTIFICATION, "LINE_NOTIFICATION" },
|
||||
{ V51_CTLR_IEI_STATE, "STATE" },
|
||||
{ V51_CTLR_IEI_AUTONOMOUS_SIG_SEQ, "AUTONOMOUS_SIG_SEQ" },
|
||||
{ V51_CTLR_IEI_SEQUENCE_RESPONSE, "SEQUENCE_RESPONSE" },
|
||||
{ V51_CTRL_IEI_PERFORMANCE_GRADING, "PERFORMANCE_GRADING" },
|
||||
{ V51_CTRL_IEI_REJECTION_CAUSE, "REJECTION_CAUSE" },
|
||||
{ V51_CTRL_IEI_SEQUENCE_NR, "SEQUENCE_NR" },
|
||||
{ V51_CTRL_IEI_CADENCED_RINGING, "CADENCED_RINGING" },
|
||||
{ V51_CTRL_IEI_PULSED_SIGNAL, "PULSED_SIGNAL" },
|
||||
{ V51_CTRL_IEI_STEADY_SIGNAL, "STEADY_SIGNAL" },
|
||||
{ V51_CTRL_IEI_DIGIT_SIGNAL, "DIGIT_SIGNAL" },
|
||||
{ V51_CTRL_IEI_RECOGNITION_TIME, "RECOGNITION_TIME" },
|
||||
{ V51_CTRL_IEI_ENABLE_AUTONOMOUS_ACK, "ENABLE_AUTONOMOUS_ACK" },
|
||||
{ V51_CTRL_IEI_DISABLE_AUTONOMOUS_ACK, "DISABLE_AUTONOMOUS_ACK" },
|
||||
{ V51_CTRL_IEI_CAUSE, "CAUSE" },
|
||||
{ V51_CTRL_IEI_RESOURCE_UNAVAILABLE, "RESOURCE_UNAVAILABLE" },
|
||||
{ V51_CTRL_IEI_ENABLE_METERING, "ENABLE_METERING" },
|
||||
{ V51_CTRL_IEI_METERING_REPORT, "METERING_REPORT" },
|
||||
{ V51_CTRL_IEI_ATTENUATION, "ATTENUATION" },
|
||||
{ V51_CTRL_IEI_CTRL_F_ELEMENT, "CTRL_F_ELEMENT" },
|
||||
{ V51_CTRL_IEI_CTRL_F_ID, "CTRL_F_ID" },
|
||||
{ V51_CTRL_IEI_VARIANT, "VARIANT" },
|
||||
{ V51_CTRL_IEI_INTERFACE_ID, "INTERFACE_ID" },
|
||||
{ V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION, "LCP_LINK_CTRL_FUNCTION" },
|
||||
{ V52_CTRL_IEI_BCC_USER_PORT_ID "BCC_USER_PORT_ID" },
|
||||
{ V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID, "BCC_ISDN_PORT_TS_ID" },
|
||||
{ V52_CTRL_IEI_BCC_V5_TS_ID, "BCC_V5_TS_ID" },
|
||||
{ V52_CTRL_IEI_BCC_MULTI_TS_MAP, "BCC_MULTI_TS_MAP" },
|
||||
{ V52_CTRL_IEI_BCC_REJECT_CAUSE, "BCC_REJECT_CAUSE" },
|
||||
{ V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE, "BCC_PROTOCOL_ERROR_CAUSE" },
|
||||
{ V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE, "BCC_CONNECTION_INCOMPLETE" },
|
||||
{ V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY, "BCC_INFO_TRANSFER_CAPABILITY" },
|
||||
{ V52_CTRL_IEI_PP_SEQUENCE_NR, "PP_SEQUENCE_NR" },
|
||||
{ V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID, "PP_PHYSICAL_C_CHAN_ID" },
|
||||
{ V52_CTRL_IEI_PP_REJECTION_CAUSE, "PP_REJECTION_CAUSE" },
|
||||
{ V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE, "PP_PROTOCOL_ERROR_CAUSE" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string v51_ctrl_func_el_str[] = {
|
||||
{ V51_CTRL_FE101_ACTIVATE_ACCESS, "FE101_ACTIVATE_ACCESS" },
|
||||
{ V51_CTRL_FE102_ACT_INIT_BY_USER, "FE102_ACT_INIT_BY_USER" },
|
||||
{ V51_CTRL_FE103_DS_ACTIVATED, "FE103_DS_ACTIVATED" },
|
||||
{ V51_CTRL_FE104_ACCESS_ACTIVATED, "FE104_ACCESS_ACTIVATED" },
|
||||
{ V51_CTRL_FE105_DEACTIVATE_ACCESS, "FE105_DEACTIVATE_ACCESS" },
|
||||
{ V51_CTRL_FE106_ACCESS_DEACTIVATED, "FE106_ACCESS_DEACTIVATED" },
|
||||
{ V51_CTRL_FE201_UNBLOCK, "FE201_UNBLOCK" },
|
||||
{ V51_CTRL_FE203_BLOCK, "FE203_BLOCK" },
|
||||
{ V51_CTRL_FE205_BLOCK_REQ, "FE205_BLOCK_REQ" },
|
||||
{ V51_CTRL_FE206_PERFORMANCE_GRADING, "FE206_PERFORMANCE_GRADING" },
|
||||
{ V51_CTRL_FE206_D_CHANNEL_BLOCK, "FE206_D_CHANNEL_BLOCK" },
|
||||
{ V51_CTRL_FE206_D_CHANNEL_UNBLOCK, "FE206_D_CHANNEL_UNBLOCK" }
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string v51_ctrl_func_id_str[] = {
|
||||
{ V51_CTRL_ID_VERIFY_RE_PROVISIONING, "VERIFY_RE_PROVISIONING" },
|
||||
{ V51_CTRL_ID_READY_FOR_RE_PROVISIONING, "READY_FOR_RE_PROVISIONING" },
|
||||
{ V51_CTRL_ID_NOT_READY_FOR_RE_PROVISIONING, "NOT_READY_FOR_RE_PROVISIONING" },
|
||||
{ V51_CTRL_ID_SWITCH_OVER_TO_NEW_VARIANT, "SWITCH_OVER_TO_NEW_VARIANT" },
|
||||
{ V51_CTRL_ID_RE_PROVISIONING_STARTED, "RE_PROVISIONING_STARTED" },
|
||||
{ V51_CTRL_ID_CANNOT_RE_PROVISION, "CANNOT_RE_PROVISION" },
|
||||
{ V51_CTRL_ID_REQUEST_VARIANT_AND_INTERFACE_ID, "REQUEST_VARIANT_AND_INTERFACE_ID" },
|
||||
{ V51_CTRL_ID_VARIANT_AND_INTERFACE_ID, "VARIANT_AND_INTERFACE_ID" },
|
||||
{ V51_CTRL_ID_BLOCKING_STARTED, "BLOCKING_STARTED" },
|
||||
{ V51_CTRL_ID_RESTART_REQUEST, "RESTART_REQUEST" },
|
||||
{ V51_CTRL_ID_RESTART_COMPLETE, "RESTART_COMPLETE" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string v51_cause_type_str[] = {
|
||||
{ V51_CTRL_CAUSE_T_RESP_TO_STATUS_ENQ, "RESP_TO_STATUS_ENQ" },
|
||||
{ V51_CTRL_CAUSE_T_L3_ADDRESS_ERROR, "L3_ADDRESS_ERROR" },
|
||||
{ V51_CTRL_CAUSE_T_MSG_TYPE_UNRECOGNIZED, "MSG_TYPE_UNRECOGNIZED" },
|
||||
{ V51_CTRL_CAUSE_T_OUT_OF_SEQUENCE_IE, "OUT_OF_SEQUENCE_IE" },
|
||||
{ V51_CTRL_CAUSE_T_REPEATED_OPT_IE, "REPEATED_OPT_IE" },
|
||||
{ V51_CTRL_CAUSE_T_MAND_IE_MISSING, "MAND_IE_MISSING" },
|
||||
{ V51_CTRL_CAUSE_T_UNRECOGNIZED_IE, "UNRECOGNIZED_IE" },
|
||||
{ V51_CTRL_CAUSE_T_MAND_IE_CONTENT_ERROR, "MAND_IE_CONTENT_ERROR" },
|
||||
{ V51_CTRL_CAUSE_T_OPT_IE_CONTENT_ERROR, "OPT_IE_CONTENT_ERROR" },
|
||||
{ V51_CTRL_CAUSE_T_MSG_INCOMP_PATH_STATE, "MSG_INCOMP_PATH_STATE" },
|
||||
{ V51_CTRL_CAUSE_T_REPEATED_MAND_IE, "REPEATED_MAND_IE" },
|
||||
{ V51_CTRL_CAUSE_T_TOO_MANY_IE, "TOO_MANY_IE" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const uint8_t signal_mand_ies[] = { V51_CTRL_IEI_SEQUENCE_NR };
|
||||
static const uint8_t signal_ack_mand_ies[] = { V51_CTRL_IEI_SEQUENCE_NR };
|
||||
static const uint8_t status_mand_ies[] = { V51_CTRL_IEI_STATE, V51_CTRL_IEI_CAUSE };
|
||||
static const uint8_t prot_par_mand_ies[] = { V51_CTRL_IEI_SEQUENCE_NR };
|
||||
static const uint8_t port_ctrl_mand_ies[] = { V51_CTRL_IEI_CTRL_F_ELEMENT };
|
||||
static const uint8_t lcp_mand_ies[] = { V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION };
|
||||
static const uint8_t alloc_mand_ies[] = { V52_CTRL_IEI_BCC_USER_PORT_ID };
|
||||
static const uint8_t alloc_rej_mand_ies[] = { V52_CTRL_IEI_BCC_REJECT_CAUSE };
|
||||
static const uint8_t prot_err_mand_ies[] = { V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE };
|
||||
static const uint8_t pp_swo_mand_ies[] = {
|
||||
V52_CTRL_IEI_PP_SEQUENCE_NR,
|
||||
V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID
|
||||
};
|
||||
static const uint8_t pp_swo_rej_mand_ies[] = {
|
||||
V52_CTRL_IEI_PP_SEQUENCE_NR,
|
||||
V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID,
|
||||
V52_CTRL_IEI_PP_REJECTION_CAUSE,
|
||||
};
|
||||
static const uint8_t pp_perr_mand_ies[] = {
|
||||
V52_CTRL_IEI_PP_SEQUENCE_NR,
|
||||
V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE,
|
||||
};
|
||||
|
||||
|
||||
const struct osmo_tlv_prot_def v51_ctrl_msg_tlv = {
|
||||
.name = "V51_CTRL",
|
||||
.tlv_def = v51_ctrl_tlv_def,
|
||||
.msg_def = {
|
||||
/* G.964 Section 13.3 */
|
||||
[V51_CTRL_MSGT_ESTABLISH] = MSG_DEF("ESTABLISH", NULL, 0),
|
||||
[V51_CTRL_MSGT_ESTABLISH_ACK] = MSG_DEF("ESTABLISH", NULL, 0),
|
||||
[V51_CTRL_MSGT_SIGNAL] = MSG_DEF("SIGNAL", signal_mand_ies, 0),
|
||||
[V51_CTRL_MSGT_SIGNAL_ACK] = MSG_DEF("SIGNAL_ACK", signal_ack_mand_ies, 0),
|
||||
[V51_CTRL_MSGT_STATUS] = MSG_DEF("STATUS", status_mand_ies, 0),
|
||||
[V51_CTRL_MSGT_STATUS_ENQUIRY] = MSG_DEF("STATUS_ENQUIRY", NULL, 0),
|
||||
[V51_CTRL_MSGT_DISCONNECT] = MSG_DEF("DISCONNECT", NULL, 0),
|
||||
[V51_CTRL_MSGT_DISCONNECT_ACK] = MSG_DEF("DISCONNECT_ACK", NULL, 0),
|
||||
[V51_CTRL_MSGT_PROTOCOL_PARAMETER] = MSG_DEF("PROTOCOL_PARAMETER", prot_par_mand_ies, 0),
|
||||
/* G.964 Section 14.4 */
|
||||
[V51_CTRL_MSGT_PORT_CTRL] = MSG_DEF("PORT_CTRL", port_ctrl_mand_ies, 0),
|
||||
[V51_CTRL_MSGT_PORT_CTRL_ACK] = MSG_DEF("PORT_CTRL_ACK", port_ctrl_mand_ies, 0),
|
||||
[V51_CTRL_MSGT_COMMON_CTRL] = MSG_DEF("COMMON_CTRL", port_ctrl_mand_ies, 0),
|
||||
[V51_CTRL_MSGT_COMMON_CTRL_ACK] = MSG_DEF("COMMON_CTRL_ACK", port_ctrl_mand_ies, 0),
|
||||
/* G.965 Section 16.3 LCP */
|
||||
[V52_CTRL_MSGT_LCP_LINK_CTRL] = MSG_DEF("LINK_CONTROL", lcp_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_LCP_LINK_CTRL_ACK] = MSG_DEF("LINK_CONTROL_ACK", lcp_mand_ies, 0),
|
||||
/* G.965 Section 17.3 BCC */
|
||||
[V52_CTRL_MSGT_ALLOCATION] = MSG_DEF("ALLOCATION", alloc_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_ALLOCATION_COMPLETE] = MSG_DEF("ALLOCATION_COMPLETE", NULL, 0),
|
||||
[V52_CTRL_MSGT_ALLOCATION_REJECT] = MSG_DEF("ALLOCATION_REJECT", alloc_rej_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_DE_ALLOCATION] = MSG_DEF("DE_ALLOCATION", alloc_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE] = MSG_DEF("DE_ALLOCATION_COMPLETE", NULL, 0),
|
||||
[V52_CTRL_MSGT_DE_ALLOCATION_REJECT] = MSG_DEF("DE_ALLOCATION_REJECT", alloc_rej_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_AUDIT] = MSG_DEF("AUDIT", NULL, 0),
|
||||
[V52_CTRL_MSGT_AUDIT_COMPLETE] = MSG_DEF("AUDIT_COMPLETE", NULL, 0),
|
||||
[V52_CTRL_MSGT_AN_FAULT] = MSG_DEF("AN_FAULT", NULL, 0),
|
||||
[V52_CTRL_MSGT_AN_FAULT_ACK] = MSG_DEF("AN_FAULT_ACK", NULL, 0),
|
||||
[V52_CTRL_MSGT_PROTOCOL_ERROR] = MSG_DEF("PROTOCOL_ERROR", prot_err_mand_ies, 0),
|
||||
/* G.965 Section 18.3 PP */
|
||||
[V52_CTRL_MSGT_PP_SWITCH_OVER_REQ] = MSG_DEF("SWITCH_OVER_REQ", pp_swo_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_PP_SWITCH_OVER_COM] = MSG_DEF("SWITCH_OVER_COM", pp_swo_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM] = MSG_DEF("OS_SWITCH_OVER_COM", pp_swo_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_PP_SWITCH_OVER_ACK] = MSG_DEF("SWITCH_OVER_ACK", pp_swo_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT] = MSG_DEF("SWITCH_OVER_REJECT", pp_swo_rej_mand_ies, 0),
|
||||
[V52_CTRL_MSGT_PP_PROTOCOL_ERROR] = MSG_DEF("PROTOCOL_ERROR", pp_perr_mand_ies, 0),
|
||||
|
||||
[V52_CTRL_MSGT_PP_RESET_SN_COM] = MSG_DEF("RESET_SN_COM", NULL, 0),
|
||||
[V52_CTRL_MSGT_PP_RESET_SN_ACK] = MSG_DEF("RESET_SN_ACK", NULL, 0),
|
||||
|
||||
},
|
||||
.ie_def = {
|
||||
/* single byte, only upper nibble matches IEI */
|
||||
[V51_CTRL_IEI_PULSE_NOTIFICATION] = { 0, "PULSE_NOTIFICATION" },
|
||||
[V51_CTLR_IEI_LINE_NOTIFICATION] = { 0, "LINE_NOTIFICATION" },
|
||||
[V51_CTLR_IEI_STATE] = { 0, "STATE" },
|
||||
[V51_CTLR_IEI_AUTONOMOUS_SIG_SEQ] = { 0, "AUTONOMOUS_SIG_SEQ" },
|
||||
[V51_CTLR_IEI_SEQUENCE_RESPONSE] = { 0, "SEQUENCE_RESPONSE" },
|
||||
/* single byte: ISDN */
|
||||
[V51_CTRL_IEI_PERFORMANCE_GRADING] = { 0, "PERFORMANCE_GRADING" },
|
||||
[V51_CTRL_IEI_REJECTION_CAUSE] = { 0, "REJECTION_CAUSE" },
|
||||
/* variable length: PSTN */
|
||||
[V51_CTRL_IEI_SEQUENCE_NR] = { 1, "SEQUENCE_NR" },
|
||||
[V51_CTRL_IEI_CADENCED_RINGING] = { 1, "CADENCED_RINGING" },
|
||||
[V51_CTRL_IEI_PULSED_SIGNAL] = { 2, "PULSED_SIGNAL" },
|
||||
[V51_CTRL_IEI_STEADY_SIGNAL] = { 1, "STEADY_SIGNAL" },
|
||||
[V51_CTRL_IEI_DIGIT_SIGNAL] = { 1, "DIGIT_SIGNAL" },
|
||||
[V51_CTRL_IEI_RECOGNITION_TIME] = { 2, "RECOGNITION_TIME" },
|
||||
[V51_CTRL_IEI_ENABLE_AUTONOMOUS_ACK] = { 2, "ENABLE_AUTONOMOUS_ACK" },
|
||||
[V51_CTRL_IEI_DISABLE_AUTONOMOUS_ACK] = { 1, "DISABLE_AUTONOMOUS_ACK" },
|
||||
[V51_CTRL_IEI_CAUSE] = { 2, "CAUSE" },
|
||||
[V51_CTRL_IEI_RESOURCE_UNAVAILABLE] = { 1, "RESOURCE_UNAVAILABLE" },
|
||||
[V51_CTRL_IEI_ENABLE_METERING] = { 1, "ENABLE_METERING" },
|
||||
[V51_CTRL_IEI_METERING_REPORT] = { 2, "METERING_REPORT" },
|
||||
[V51_CTRL_IEI_ATTENUATION] = { 1, "ATTENUATION" },
|
||||
/* variable length: ISDN */
|
||||
[V51_CTRL_IEI_CTRL_F_ELEMENT] = { 1, "CTRL_F_ELEMENT" },
|
||||
[V51_CTRL_IEI_CTRL_F_ID] = { 1, "CTRL_F_ID" },
|
||||
[V51_CTRL_IEI_VARIANT] = { 1, "VARIANT" },
|
||||
[V51_CTRL_IEI_INTERFACE_ID] = { 3, "INTERFACE_ID" },
|
||||
/* variable length: LCP */
|
||||
[V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION] = { 1, "LINK_CTRL_FUNCTION" },
|
||||
/* variable length: BCC */
|
||||
[V52_CTRL_IEI_BCC_USER_PORT_ID] = { 2, "USER_PORT_ID" },
|
||||
[V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID] = { 1, "ISDN_PORT_TS_ID" },
|
||||
[V52_CTRL_IEI_BCC_V5_TS_ID] = { 2, "V5_TS_ID" },
|
||||
[V52_CTRL_IEI_BCC_MULTI_TS_MAP] = { 9, "MULTI_TS_MAP" },
|
||||
[V52_CTRL_IEI_BCC_REJECT_CAUSE] = { 1, "REJECT_CAUSE" },
|
||||
[V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE] = { 1, "PROTOCOL_ERR_CAUSE" },
|
||||
[V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE]= { 1, "CONNECTION_INCOMPLETE" },
|
||||
[V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY] = { 1, "INFO_TRANSFER_CAPABILITY" },
|
||||
/* variable length: PP */
|
||||
[V52_CTRL_IEI_PP_SEQUENCE_NR] = { 1, "SEQUENCE_NR" },
|
||||
[V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID] = { 2, "PHYSICAL_C_CHAN_ID" },
|
||||
[V52_CTRL_IEI_PP_REJECTION_CAUSE] = { 1, "REJECTION_CAUSE" },
|
||||
[V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE] = { 1, "PROTOCOL_ERR_CAUSE" },
|
||||
},
|
||||
.msgt_names = v51_ctrl_msg_typ_str,
|
||||
};
|
||||
|
||||
|
||||
struct v5x_user_port *v5x_user_port_find(struct v5x_instance *v5i, uint16_t nr)
|
||||
{
|
||||
struct v5x_user_port *v5up;
|
||||
|
||||
llist_for_each_entry(v5up, &v5i->user_ports, list) {
|
||||
if (v5up->nr == l3_addr)
|
||||
return v5up;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int v51_rcv_pstn(struct v5x_user_port *v5i, struct msgb *msg, const struct tlv_parsed *tp)
|
||||
{
|
||||
struct v51_l3_hdr *l3h = msgb_l3(msg);
|
||||
|
||||
switch (l3h->msg_type) {
|
||||
case V51_CTRL_MSGT_ESTABLISH:
|
||||
case V51_CTRL_MSGT_ESTABLISH_ACK:
|
||||
case V51_CTRL_MSGT_SIGNAL:
|
||||
case V51_CTRL_MSGT_SIGNAL_ACK:
|
||||
case V51_CTRL_MSGT_DISCONNECT:
|
||||
case V51_CTRL_MSGT_DISCONNECT_ACK:
|
||||
case V51_CTRL_MSGT_STATUS_ENQUIRY:
|
||||
case V51_CTRL_MSGT_STATUS:
|
||||
case V51_CTRL_MSGT_PROTOCOL_PARAMETER:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int v52_rcv_pp(struct v5x_instance *v5i, struct msgb *msg, const struct tlv_parsed *tp)
|
||||
{
|
||||
struct v51_l3_hdr *l3h = msgb_l3(msg);
|
||||
}
|
||||
|
||||
static int v52_rcv_bcc(struct v5x_instance *v5i, struct msgb *msg, const struct tlv_parsed *tp)
|
||||
{
|
||||
struct v51_l3_hdr *l3h = msgb_l3(msg);
|
||||
}
|
||||
|
||||
static int v52_rcv_lcp(struct v5x_instance *v5i, struct msgb *msg, const struct tlv_parsed *tp)
|
||||
{
|
||||
struct v51_l3_hdr *l3h = msgb_l3(msg);
|
||||
}
|
||||
|
||||
|
||||
/* main entry point for received V5 messages */
|
||||
int v5x_rcv(struct v5x_instance *v5i, struct msgb *msg)
|
||||
{
|
||||
struct v51_l3_hdr *l3h = msgb_l3(msg);
|
||||
struct tlv_parsed tp;
|
||||
uint16_t l3_addr;
|
||||
|
||||
if (msgb_l3len(msg) < sizeof(*l3h)) {
|
||||
LOGP(DV5, LOGL_ERROR, "Received short message (%u bytes < %u)\n", msgb_l3len(msg), sizeof(*l3h));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (l3h->pdisc != V51_CTRL_PDISC) {
|
||||
LOGP(DV5, LOGL_ERROR, "Received unsupported protocol discriminator 0x%02x\n", l3h->pdisc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = osmo_tlv_prot_parse(v51_ctrl_msg_tlv, &tp, 1, l3h->msg_type, msgb_l3(msg) + sizeof(*l3h),
|
||||
msgb_l3len(msg) - sizeof(*l3h), 0, 0, DV5, __func__);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
l3_addr = v51_l3_addr_dec(l3h->l3_addr);
|
||||
|
||||
|
||||
switch (l3h->msg_type) {
|
||||
/* PSTN signaling protocol */
|
||||
case V51_CTRL_MSGT_ESTABLISH:
|
||||
case V51_CTRL_MSGT_ESTABLISH_ACK:
|
||||
case V51_CTRL_MSGT_SIGNAL:
|
||||
case V51_CTRL_MSGT_SIGNAL_ACK:
|
||||
case V51_CTRL_MSGT_DISCONNECT:
|
||||
case V51_CTRL_MSGT_DISCONNECT_ACK:
|
||||
case V51_CTRL_MSGT_STATUS_ENQUIRY:
|
||||
case V51_CTRL_MSGT_STATUS:
|
||||
case V51_CTRL_MSGT_PROTOCOL_PARAMETER:
|
||||
/* look-up user port based on L3 addr? */
|
||||
v5up = v5x_user_port_find(v5i, l3_addr);
|
||||
if (!v5up)
|
||||
return -ENODEV;
|
||||
rc = v51_rcv_pstn(v5up, msg, &tp);
|
||||
break;
|
||||
/* control protocol (Section 14 G.964) */
|
||||
case V51_CTRL_MSGT_PORT_CTRL:
|
||||
case V51_CTRL_MSGT_PORT_CTRL_ACK:
|
||||
case V51_CTRL_MSGT_COMMON_CTRL:
|
||||
case V51_CTRL_MSGT_COMMON_CTRL_ACK:
|
||||
rc = v51_rcv_ctrl(v5i, msg, &tp);
|
||||
break;
|
||||
/* protection protocol (Section 18 G.965) */
|
||||
case V52_CTRL_MSGT_PP_SWITCH_OVER_REQ:
|
||||
case V52_CTRL_MSGT_PP_SWITCH_OVER_COM:
|
||||
case V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM:
|
||||
case V52_CTRL_MSGT_PP_SWITCH_OVER_ACK:
|
||||
case V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT:
|
||||
case V52_CTRL_MSGT_PP_PROTOCOL_ERROR:
|
||||
case V52_CTRL_MSGT_PP_RESET_SN_COM:
|
||||
case V52_CTRL_MSGT_PP_RESET_SN_ACK:
|
||||
rc = v52_rcv_pp(v5i, msg, &tp);
|
||||
break;
|
||||
/* BCC protocol (Section 17 G.965) */
|
||||
case V52_CTRL_MSGT_ALLOCATION:
|
||||
case V52_CTRL_MSGT_ALLOCATION_COMPLETE:
|
||||
case V52_CTRL_MSGT_ALLOCATION_REJECT:
|
||||
case V52_CTRL_MSGT_DE_ALLOCATION:
|
||||
case V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE:
|
||||
case V52_CTRL_MSGT_DE_ALLOCATION_REJECT:
|
||||
case V52_CTRL_MSGT_AUDIT:
|
||||
case V52_CTRL_MSGT_AUDIT_COMPLETE:
|
||||
case V52_CTRL_MSGT_AN_FAULT:
|
||||
case V52_CTRL_MSGT_AN_FAULT_ACK:
|
||||
case V52_CTRL_MSGT_PROTOCOL_ERROR:
|
||||
rc = v52_rcv_bcc(v5i, msg, &tp);
|
||||
break;
|
||||
/* Link control protocol (Section 16 G.965) */
|
||||
case V52_CTRL_MSGT_LCP_LINK_CTRL:
|
||||
case V52_CTRL_MSGT_LCP_LINK_CTRL_ACK:
|
||||
rc = v52_rcv_lcp(v5i, msg, &tp);
|
||||
break;
|
||||
default:
|
||||
LOGP(DV5, LOGL_ERROR, "Received unsupported message type %s\n",
|
||||
osmo_tlv_prot_msg_name(v51_ctrl_msg_tlv, l3h->msg_type);
|
||||
}
|
||||
}
|
252
v5x_protocol.h
252
v5x_protocol.h
|
@ -1,252 +0,0 @@
|
|||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
/* Definitions related to the V5.1 and V5.2 interface between AN (access
|
||||
* network) and LE (local exchange) as per ITU-T G.964 + G.965.
|
||||
*
|
||||
* (C) 2021 by Harald Welte <laforge@gnumonks.org>
|
||||
*/
|
||||
|
||||
/***********************************************************************/
|
||||
/* protocol wire format */
|
||||
/***********************************************************************/
|
||||
|
||||
/* Table 14/G.964 */
|
||||
#define V51_CTRL_PDISC 0x48
|
||||
|
||||
/* Table 1/G.965 + Table 1/G.964 */
|
||||
#define V51_DLADDR_PSTN 8176
|
||||
#define V51_DLADDR_CTRL 8177
|
||||
#define V52_DLADDR_BCC 8178
|
||||
#define V52_DLADDR_PROTECTION 8179
|
||||
#define V52_DLADDR_LCP 8180
|
||||
|
||||
/* G.965 Section 13.1 */
|
||||
struct v51_l3_hdr {
|
||||
uint8_t pdisc;
|
||||
uint16_t l3_addr; /* with C/R and EA bits! */
|
||||
uint8_t msg_type;
|
||||
uint8_t payload[0];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* G.964 Section 14.4.2.3 Figure 33 + 34 */
|
||||
static inline bool v51_l3_addr_is_isdn(uint16_t in)
|
||||
{
|
||||
/* extract two bytes */
|
||||
uint8_t b1 = in >> 8;
|
||||
uint8_t b2 = in & 0xff;
|
||||
|
||||
if ((b1 & 0x03) == 0x00 && (b2 & 0x01))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline uint16_t v51_l3_addr_dec(uint16_t in, bool *is_isdn)
|
||||
{
|
||||
/* extract two bytes */
|
||||
uint8_t b1 = in >> 8;
|
||||
uint8_t b2 = in & 0xff;
|
||||
|
||||
if (v51_l3_addr_is_isdn(in)) {
|
||||
/* remove EA/C/R bits */
|
||||
uint8_t upper = b1 >> 2;
|
||||
uint8_t lower = b2 >> 1;
|
||||
if (is_isdn)
|
||||
*is_isdn = true;
|
||||
/* shift them together */
|
||||
return lower | (upper << 7);
|
||||
} else{
|
||||
uint8_t upper = b1 >> 1;
|
||||
uint8_t lower = b2;
|
||||
if (is_isdn)
|
||||
*is_isdn = false;
|
||||
return lower | (upper << 8);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint16_t v51_l3_addr_enc(uint16_t in, bool is_isdn)
|
||||
{
|
||||
uint8_t b1, b2;
|
||||
if (is_isdn) {
|
||||
uint8_t upper = in >> 7;
|
||||
uint8_t lower = in & 0x7f;
|
||||
b1 = upper << 2;
|
||||
b2 = (lower << 1) | 0x01;
|
||||
} else {
|
||||
uint8_t upper = in >> 8;
|
||||
uint8_t lower = in & 0xff;
|
||||
b1 = (upper << 1) | 0x01;
|
||||
b2 = lower;
|
||||
}
|
||||
return (b1 << 8) | b2;
|
||||
}
|
||||
|
||||
/* Table 15 + 52/G.964 */
|
||||
enum v51_ctrl_msg_type {
|
||||
/* 000xxxx: PSTN protocol message types */
|
||||
V51_CTRL_MSGT_ESTABLISH = 0x00,
|
||||
V51_CTRL_MSGT_ESTABLISH_ACK = 0x01,
|
||||
V51_CTRL_MSGT_SIGNAL = 0x02,
|
||||
V51_CTRL_MSGT_SIGNAL_ACK = 0x03,
|
||||
V51_CTRL_MSGT_DISCONNECT = 0x08,
|
||||
V51_CTRL_MSGT_DISCONNECT_ACK = 0x09,
|
||||
V51_CTRL_MSGT_STATUS_ENQUIRY = 0x0c,
|
||||
V51_CTRL_MSGT_STATUS = 0x0d,
|
||||
V51_CTRL_MSGT_PROTOCOL_PARAMETER = 0x0e,
|
||||
/* 0010xxx: Control protocol message types */
|
||||
V51_CTRL_MSGT_PORT_CTRL = 0x10,
|
||||
V51_CTRL_MSGT_PORT_CTRL_ACK = 0x11,
|
||||
V51_CTRL_MSGT_COMMON_CTRL = 0x12,
|
||||
V51_CTRL_MSGT_COMMON_CTRL_ACK = 0x13,
|
||||
/* 0011xxx: Protection protocol message types */
|
||||
V52_CTRL_MSGT_PP_SWITCH_OVER_REQ = 0x18,
|
||||
V52_CTRL_MSGT_PP_SWITCH_OVER_COM = 0x19,
|
||||
V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM = 0x1a,
|
||||
V52_CTRL_MSGT_PP_SWITCH_OVER_ACK = 0x1b,
|
||||
V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT = 0x1c,
|
||||
V52_CTRL_MSGT_PP_PROTOCOL_ERROR = 0x1d,
|
||||
V52_CTRL_MSGT_PP_RESET_SN_COM = 0x1e,
|
||||
V52_CTRL_MSGT_PP_RESET_SN_ACK = 0x1f,
|
||||
/* 010xxxx: BCC protocol message types */
|
||||
V52_CTRL_MSGT_ALLOCATION = 0x20,
|
||||
V52_CTRL_MSGT_ALLOCATION_COMPLETE = 0x21,
|
||||
V52_CTRL_MSGT_ALLOCATION_REJECT = 0x22,
|
||||
V52_CTRL_MSGT_DE_ALLOCATION = 0x23,
|
||||
V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE = 0x24,
|
||||
V52_CTRL_MSGT_DE_ALLOCATION_REJECT = 0x25,
|
||||
V52_CTRL_MSGT_AUDIT = 0x26,
|
||||
V52_CTRL_MSGT_AUDIT_COMPLETE = 0x27,
|
||||
V52_CTRL_MSGT_AN_FAULT = 0x28,
|
||||
V52_CTRL_MSGT_AN_FAULT_ACK = 0x29,
|
||||
V52_CTRL_MSGT_PROTOCOL_ERROR = 0x2a,
|
||||
/* 0110xxx: Link control protocol message types */
|
||||
V52_CTRL_MSGT_LCP_LINK_CTRL = 0x30,
|
||||
V52_CTRL_MSGT_LCP_LINK_CTRL_ACK = 0x31,
|
||||
};
|
||||
|
||||
extern const struct value_string v51_ctrl_msg_typ_str[];
|
||||
|
||||
|
||||
/* Table 17 + 53/G.964 */
|
||||
enum v51_ctrl_iei {
|
||||
/* single byte, only upper nibble matches IEI */
|
||||
V51_CTRL_IEI_PULSE_NOTIFICATION = 0xc0,
|
||||
V51_CTLR_IEI_LINE_NOTIFICATION = 0x80,
|
||||
V51_CTLR_IEI_STATE = 0x90,
|
||||
V51_CTLR_IEI_AUTONOMOUS_SIG_SEQ = 0xa0,
|
||||
V51_CTLR_IEI_SEQUENCE_RESPONSE = 0xb0,
|
||||
/* single byte: ISDN */
|
||||
V51_CTRL_IEI_PERFORMANCE_GRADING = 0xe0,
|
||||
V51_CTRL_IEI_REJECTION_CAUSE = 0xf0,
|
||||
/* variable length: PSTN */
|
||||
V51_CTRL_IEI_SEQUENCE_NR = 0x00,
|
||||
V51_CTRL_IEI_CADENCED_RINGING = 0x01,
|
||||
V51_CTRL_IEI_PULSED_SIGNAL = 0x02,
|
||||
V51_CTRL_IEI_STEADY_SIGNAL = 0x03,
|
||||
V51_CTRL_IEI_DIGIT_SIGNAL = 0x04,
|
||||
V51_CTRL_IEI_RECOGNITION_TIME = 0x10,
|
||||
V51_CTRL_IEI_ENABLE_AUTONOMOUS_ACK = 0x11,
|
||||
V51_CTRL_IEI_DISABLE_AUTONOMOUS_ACK = 0x12,
|
||||
V51_CTRL_IEI_CAUSE = 0x13,
|
||||
V51_CTRL_IEI_RESOURCE_UNAVAILABLE = 0x14,
|
||||
V51_CTRL_IEI_ENABLE_METERING = 0x32,
|
||||
V51_CTRL_IEI_METERING_REPORT = 0x33,
|
||||
V51_CTRL_IEI_ATTENUATION = 0x34,
|
||||
/* variable length: ISDN */
|
||||
V51_CTRL_IEI_CTRL_F_ELEMENT = 0x20,
|
||||
V51_CTRL_IEI_CTRL_F_ID = 0x21,
|
||||
V51_CTRL_IEI_VARIANT = 0x22,
|
||||
V51_CTRL_IEI_INTERFACE_ID = 0x23,
|
||||
/* variable length: LCP */
|
||||
V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION = 0x30,
|
||||
/* variable length: BCC */
|
||||
V52_CTRL_IEI_BCC_USER_PORT_ID = 0x40,
|
||||
V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID = 0x41,
|
||||
V52_CTRL_IEI_BCC_V5_TS_ID = 0x42,
|
||||
V52_CTRL_IEI_BCC_MULTI_TS_MAP = 0x43,
|
||||
V52_CTRL_IEI_BCC_REJECT_CAUSE = 0x44,
|
||||
V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE = 0x45,
|
||||
V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE = 0x46,
|
||||
V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY = 0x47,
|
||||
/* variable-length: Protection */
|
||||
V52_CTRL_IEI_PP_SEQUENCE_NR = 0x50,
|
||||
V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID = 0x51,
|
||||
V52_CTRL_IEI_PP_REJECTION_CAUSE = 0x52,
|
||||
V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE = 0x53,
|
||||
};
|
||||
|
||||
extern const struct value_string v51_ctrl_iei_str[];
|
||||
|
||||
extern const struct tlv_definition v51_ctrl_tlv_def[];
|
||||
|
||||
extern const struct osmo_tlv_prot_def v51_ctrl_msg_tlv;
|
||||
|
||||
/* 14.4.2.5.4 Table 56/G.964 - Coding of Control Function Element */
|
||||
enum v51_ctrl_func_el {
|
||||
V51_CTRL_FE101_ACTIVATE_ACCESS = 0x01,
|
||||
V51_CTRL_FE102_ACT_INIT_BY_USER = 0x02,
|
||||
V51_CTRL_FE103_DS_ACTIVATED = 0x03,
|
||||
V51_CTRL_FE104_ACCESS_ACTIVATED = 0x04,
|
||||
V51_CTRL_FE105_DEACTIVATE_ACCESS = 0x05,
|
||||
V51_CTRL_FE106_ACCESS_DEACTIVATED = 0x06,
|
||||
V51_CTRL_FE201_UNBLOCK = 0x11,
|
||||
V51_CTRL_FE203_BLOCK = 0x13,
|
||||
V51_CTRL_FE205_BLOCK_REQ = 0x15,
|
||||
V51_CTRL_FE206_PERFORMANCE_GRADING = 0x16,
|
||||
V51_CTRL_FE207_D_CHANNEL_BLOCK = 0x17,
|
||||
V51_CTRL_FE208_D_CHANNEL_UNBLOCK = 0x18,
|
||||
};
|
||||
|
||||
extern const struct value_string v51_ctrl_func_el_str[];
|
||||
|
||||
/* 14.4.2.5.5 Table 57/G.964 - Coding of Control Function ID */
|
||||
enum v51_ctrl_func_id {
|
||||
V51_CTRL_ID_VERIFY_RE_PROVISIONING = 0x00,
|
||||
V51_CTRL_ID_READY_FOR_RE_PROVISIONING = 0x01,
|
||||
V51_CTRL_ID_NOT_READY_FOR_RE_PROVISIONING = 0x02,
|
||||
V51_CTRL_ID_SWITCH_OVER_TO_NEW_VARIANT = 0x03,
|
||||
V51_CTRL_ID_RE_PROVISIONING_STARTED = 0x04,
|
||||
V51_CTRL_ID_CANNOT_RE_PROVISION = 0x05,
|
||||
V51_CTRL_ID_REQUEST_VARIANT_AND_INTERFACE_ID = 0x06,
|
||||
V51_CTRL_ID_VARIANT_AND_INTERFACE_ID = 0x07,
|
||||
V51_CTRL_ID_BLOCKING_STARTED = 0x08,
|
||||
V51_CTRL_ID_RESTART_REQUEST = 0x10,
|
||||
V51_CTRL_ID_RESTART_COMPLETE = 0x11,
|
||||
};
|
||||
|
||||
extern const struct value_string v51_ctrl_func_id_str[];
|
||||
|
||||
/* 13.4.7.9 Cause */
|
||||
enum v51_cause_type {
|
||||
V51_CTRL_CAUSE_T_RESP_TO_STATUS_ENQ = 0x00,
|
||||
V51_CTRL_CAUSE_T_L3_ADDRESS_ERROR = 0x03,
|
||||
V51_CTRL_CAUSE_T_MSG_TYPE_UNRECOGNIZED = 0x04,
|
||||
V51_CTRL_CAUSE_T_OUT_OF_SEQUENCE_IE = 0x05,
|
||||
V51_CTRL_CAUSE_T_REPEATED_OPT_IE = 0x06,
|
||||
V51_CTRL_CAUSE_T_MAND_IE_MISSING = 0x07,
|
||||
V51_CTRL_CAUSE_T_UNRECOGNIZED_IE = 0x08,
|
||||
V51_CTRL_CAUSE_T_MAND_IE_CONTENT_ERROR = 0x09,
|
||||
V51_CTRL_CAUSE_T_OPT_IE_CONTENT_ERROR = 0x0a,
|
||||
V51_CTRL_CAUSE_T_MSG_INCOMP_PATH_STATE = 0x0b,
|
||||
V51_CTRL_CAUSE_T_REPEATED_MAND_IE = 0x0c,
|
||||
V51_CTRL_CAUSE_T_TOO_MANY_IE = 0x0d,
|
||||
};
|
||||
|
||||
extern const struct value_string v51_cause_type_str[];
|
||||
|
||||
const struct osmo_tlv_prot_def v51_ctrl_msg_tlv;
|
||||
|
||||
/* 16.3.2.2 Table 22/G.965 */
|
||||
enum v52_link_ctrl_func {
|
||||
V52_LCP_FE_IDReq = 0,
|
||||
V52_LCP_FE_IDAck = 1,
|
||||
V52_LCP_FE_IDRel = 2,
|
||||
V52_LCP_FE_IDRej = 3,
|
||||
V52_LCP_FE_301_302_LINK_UNBLOCK = 4,
|
||||
V52_LCP_FE_303_304_LINK_BLOCK = 5,
|
||||
V52_LCP_FE_305_DEF_LINK_BLOCK_REQ = 6,
|
||||
V52_LCP_FE_306_NON_DEF_LINK_BLOCK_REQ = 7,
|
||||
};
|
Loading…
Reference in New Issue