diff -Nru dahdi-linux-2.2.1-rc2/build_tools/builder dahdi-cnet-linux-2.2.1-rc2/build_tools/builder --- dahdi-linux-2.2.1-rc2/build_tools/builder 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/build_tools/builder 2008-06-28 17:02:27.000000000 -0500 @@ -0,0 +1,168 @@ +#!/bin/sh + +# build_test - a build testing script +# +# Copyright (C) 2008 by Xorcom +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Setup: +# +# 0. Copy this script under build_tools/ and +# +# chmod +x build_tools/builder +# +# 1. Make sure you have git and sqlite3 installed. If the sqlite3 binary +# is called differently, fix the line "SQLITE=" in the script or in +# build_tools/test_build.conf . +# +# 2. Run: +# +# ./build_tools/test_kernel_git init /path/to/some/dir +# +# /path/to/some/dir must exist . This will download a recent kernel +# git repository to /path/to/some/dir/linux-2.6 . Use +# './build_tools/test_kernel_git update' to pull a fresh update there. +# +# 3. Run: +# +# ./build_tools/builder init +# +# +# Usage: +# +# ./build_tools build +# +# The past results are in a sqlite database in the logs subdirectory. For +# a simple list of results: +# +# ./build_tools report +# +# You can also look at the build log for a specific build in the logs +# directory. + +BIN_DIR=`dirname $0` +BASE_DIR=`dirname $BIN_DIR` +SQLITE=sqlite3 +HOSTS="localhost" +LOGS_DIR="$BASE_DIR/logs" +DB=$LOGS_DIR/builds.db +BUILD_SCRIPT=$BIN_DIR/test_kernel_git +KERNELS_localhost="2.6.12 2.6.18 2.6.25" + +usage() { + me=`basename $0` + echo "$me: test building Zaptel/DAHDI with various kernels" + echo "" + echo "Usage: $0 command " + echo " init Create results directory and database." + echo " build [] Run the test builds. The default list: " + echo " $KERNELS_localhost" + echo " report [] Print all results [matching ]" + echo " Default is to print all the resaults." + echo "" + echo "Filters:" + echo " failed: Only failed tests." + echo " fail_type Where fail_type matches ." + echo " 2.6* Only builds for a matching kernel version." + echo " Else: Match a string from the build name, which " + echo " is essentially the time it started." + echo "" +} + +set -e + +if [ -r $BIN_DIR/test_build.conf ]; then . $BIN_DIR/test_build.conf; fi + +# Runs the test script, logs the result, and fails if the test command +# has failed. +build_and_check() { + test_name="$1" + test_cmd="$2" + log_file="$3" + results_str="$4" + fail_type='' + + set +e + $BUILD_SCRIPT $test_cmd >$log_file 2>&1 + rc=$? + set -e + if [ $rc != 0 ]; then + fail_type="$test_name" + echo "$results_str, $rc, '$fail_type', '$log_file');" | $SQLITE $DB + fi + return $rc +} + +build_zaptel() { + build_name="$1" + host="$2" + kvers="$3" + log_base="build__${build_name}__${host}__${kvers}" + log_base_full="$LOGS_DIR/$log_base" + log_file="$log_base_full.log" + results_str="INSERT INTO results VALUES ('$build_name', '$host', '$kvers'" + # Due to 'set -e' a failed test exists the script. + build_and_check setver "setver $kvers" "$log_file" "$results_str" + build_and_check clean "test clean" "$log_file" "$results_str" + build_and_check build "build" "$log_file" "$results_str" + + # If we got here, all was well. + echo "$results_str, 0, 'complete', '$log_file');" | $SQLITE $DB +} + +case "$1" in +init) + mkdir -p $LOGS_DIR + cat <&2 "$0: Unknown command '$1'. Aborting." + exit 1 +esac diff -Nru dahdi-linux-2.2.1-rc2/build_tools/genmodconf dahdi-cnet-linux-2.2.1-rc2/build_tools/genmodconf --- dahdi-linux-2.2.1-rc2/build_tools/genmodconf 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/build_tools/genmodconf 2008-07-01 15:55:52.000000000 -0500 @@ -0,0 +1,80 @@ +#!/bin/sh + +# this script makes an attempt to build a proper set of rules +# for loading the DAHDI modules and automatically running dahdi_xcfg +# +# it accepts two parameters: +# the root prefix to be used for finding/creating the files +# the list of module names being installed +# +# the process is as follows: +# +# the file can be located at /etc/modprobe.conf (combined with all +# other rules), /etc/modprobe.d/dahdi (DAHDI only) or /etc/modules.d/dahdi +# (DAHDI only) +# +# when the file is DAHDI rules only, then we don't preserve the existing +# contents of the file; the system administrator can put desired options and +# overrides in a separate file with a name that appears earlier in the sort +# order, so there is no need to edit the file produced by this script +# +# when the file is combined with all other rules, then we make a backup +# of it and remove all the old DAHDI rules we can find, replacing them with +# new ones +# +# in addition, versions of module-init-tools 3.2.0 and later +# have the ability to pass module parameters specified on the modprobe command +# line to commands in 'install' rules, thus keeping them from being lost, so +# we try to determine what version is installed and take advantage of that + +toolver=`/sbin/modprobe --version 2>/dev/null| awk '{print $3}' | cut -d. -f2 | cut -d- -f1` +if [ ${toolver} -ge 2 ]; then + cmdopts=\$CMDLINE_OPTS +fi +if [ -d ${1}/etc/modprobe.d ]; then + target=${1}/etc/modprobe.d/dahdi +elif [ -d ${1}/etc/modules.d ]; then + target=${1}/etc/modules.d/dahdi +elif [ -f ${1}/etc/modprobe.conf ]; then + target=${1}/etc/modprobe.conf + combined=1 +elif [ -f ${1}/etc/conf.modules ]; then + target=${1}/etc/conf.modules + combined=1 +else + echo No suitable location for module rules can be found... exiting. + exit 1 +fi + +if [ -n "${combined}" ]; then + if [ -f ${target} ]; then + mv ${target} ${target}.bak + cat ${target}.bak | grep -v "alias char-major-250" | grep -v "alias char-major-196" > ${target} + fi +else + if [ -f ${target} ]; then + mv ${target} ${target}.bak + fi + echo "# automatically generated file; do not edit" > ${target} +fi + +echo Building ${target}... + +for mod in ${2}; do + if ! grep -q "install ${mod} " ${target}; then + echo "install ${mod} /sbin/modprobe --ignore-install ${mod} ${cmdopts} && /sbin/ztcfg" >> ${target} + fi +done + +if [ -z "${combined}" ]; then + echo "***" + echo "*** WARNING:" + echo "*** If you had custom settings in ${target}," + echo "*** they have been moved to ${target}.bak." + echo "***" + echo "*** In the future, do not edit ${target}, but" + echo "*** instead put your changes in another file" + echo "*** in the same directory so that they will not" + echo "*** be overwritten by future DAHDI updates." + echo "***" +fi diff -Nru dahdi-linux-2.2.1-rc2/build_tools/genudevrules dahdi-cnet-linux-2.2.1-rc2/build_tools/genudevrules --- dahdi-linux-2.2.1-rc2/build_tools/genudevrules 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/build_tools/genudevrules 2008-10-22 13:49:41.000000000 -0500 @@ -0,0 +1,40 @@ +#!/bin/sh + +ver=`udevinfo -V | cut -f3 -d" "` + +if [ -z "${ver}" ]; then + # Not found - try udevadm + ver=`udevadm info -V | cut -f3 -d" "` + + if [ -z "${ver}" ]; then + # nobody has that old version, anyway. + ver=54 + fi +fi + +# udev versions prior to 055 use a single '=' for matching key values +# udev versions 055 and later support '==' for that purpose, and versions +# beyond 092 will probably make it mandatory +# +# very old versions of udev required naming rules and permissions rules to be +# in separate files, but it's not clear at what version number that changed + +if [ ${ver} -gt 54 ]; then + match="==" +else + match="=" +fi + +cat <drivers; + print join(" ", @drivers); + ' +} + +# Add modules for existing hardware on the system for the list of +# modules to load. +# +# As module loading is manual with insmod, some manual fixes are needed. +set_modules_to_load() { + for mod in `dahdi_drivers`; do + case "$mod" in + xpp_usb) + MODULES_LOAD="$MODULES_LOAD xpp/xpp xpp/xpd_fxs" + MODULES_LOAD="$MODULES_LOAD xpp/xpd_fxo xpp/xpd_pri" + if [ -r "$MODULES_DIR/xpp/xpd_bri.ko" ]; then + MODULES_LOAD="$MODULES_LOAD xpp/xpd_bri" + fi + MODULES_LOAD="$MODULES_LOAD xpp/xpp_usb" + ;; + wctdm24xxp | wct4xxp | wcte12xp | wctc4xp) + MODULES_LOAD="$MODULES_LOAD $mod/$mod" + ;; + wanpipe) + : # requires different handling + ;; + *) + MODULES_LOAD="$MODULES_LOAD $mod" + ;; + esac + done +} + +# Initialize the Xorcom Astribank (xpp/) using perl utiliites: +# intended to replace all the the three functions below if user has +# installed the dahdi-perl utilities. +xpp_startup() { + # do nothing if there are no astribank devices: + if ! grep -q connected /proc/xpp/xbuses 2>/dev/null; then return 0; fi + + echo "Waiting for Astribank devices to initialize:" + $TOOLS_DIR/xpp/waitfor_xpds # Asusmes a recent dahdi-tools + + # overriding locales for the above two, as perl can be noisy + # when locales are missing. + # No register all the devices if they didn't auto-register: + LC_ALL=C dahdi_registration on + + # this one could actually be run after dahdi_cfg: + LC_ALL=C xpp_sync "$XPP_SYNC" +} + +# recursively unload a module and its dependencies, if possible. +# where's modprobe -r when you need it? +# inputs: module to unload. +# returns: the result from +unload_module() { + module="$1" + line=`lsmod 2>/dev/null | grep "^$1 " || :` + if [ "$line" = '' ]; then return; fi # module was not loaded + + set -- $line + # $1: the original module, $2: size, $3: refcount, $4: deps list + mods=`echo $4 | tr , ' '` + # xpp_usb keeps the xpds below busy if an xpp hardware is + # connected. Hence must be removed before them: + case "$module" in xpd_*) mods="xpp_usb $mods";; esac + for mod in $mods; do + # run in a subshell, so it won't step over our vars: + (unload_module $mod) + # TODO: the following is probably the error handling we want: + # if [ $? != 0 ]; then return 1; fi + done + rmmod $module +} + +usage() { + me=`basename $0` + echo "$me: Run DAHDI in a test environment" + echo 'Version: $Id: live_dahdi 6487 2009-04-25 16:35:33Z tzafrir $' + echo '' + echo "Usage: equivalent of:" + echo "$me configure ./configure" + echo "$me install make install" + echo "$me config make config" + echo "$me unload /etc/init.d/dahdi stop" + echo "$me load /etc/init.d/dahdi start" + echo "$me reload /etc/init.d/dahdi restart" + echo "$me xpp-firm (Reset and load xpp firmware)" + echo "$me rsync TARGET (copy filea to /tmp/live in host TARGET)" + echo "$me exec COMMAND (Run COMMAND in 'live' environment)" + echo "" + echo "dahdi-linux: $LINUX_DIR" + echo "dahdi-tools: $TOOLS_DIR" +} + +case "$1" in +configure) + shift + cd "$TOOLS_DIR"; ./configure --with-dahdi="$LINUX_DIR_FULL" "$@" + ;; +install) + shift + cd "$LINUX_DIR"; make install DESTDIR=$DESTDIR "$@" + cd "$TOOLS_DIR"; make install DESTDIR=$DESTDIR DYNFS=yes "$@" + ;; +config) + shift + cd "$TOOLS_DIR"; make config DESTDIR=$DESTDIR "$@" + mkdir -p $DESTDIR/etc/asterisk + ;; +rsync) + if [ $# -ne 2 ]; then + echo >&2 "$0: Error: rsync requires a target parameter". + exit 1 + fi + # copy the script itself and the installed directory to the + # target host: + rsync -ai "$0" $DESTDIR "$2:/tmp/" + ;; +unload) + # OK for Asterisk not to be running. TODO: a better test? + $AST_SCRIPT stop || : + for mod in $REMOVE_MODULES; do + unload_module $mod + done + ;; +load) + # TODO: Find a way to use modprobe. + # Or implement a way to pass arguments to modules here (yuck) + set_modules_to_load + for module in $MODULES_LOAD; do + eval module_args="\$`basename ${module}`_ARGS" + insmod $MODULES_DIR/$module.ko $module_args + done + xpp_startup + GENCONF_PARAMETERS=$DESTDIR/etc/dahdi/genconf_parameters \ + DAHDI_CONF_FILE=$DESTDIR/etc/dahdi/system.conf \ + DAHDI_MODS_FILE=$DESTDIR/etc/dahdi/modules \ + CHAN_DAHDI_CHANNELS_FILE=$DESTDIR/etc/asterisk/dahdi-channels.conf \ + dahdi_genconf + dahdi_cfg -c $DESTDIR/etc/dahdi/system.conf + # TODO: fxotune, hpec + # or find a way to reuse init.d start sequence. + + # TODO: A local copy of Asterisk, configured with dahdi_gnconf. + # doable, but trickier. + $AST_SCRIPT start + ;; +reload) + $0 unload + $0 load + ;; +exec) + if [ $# -lt 2 ]; then + # No command given: start a subshell in the environemnt + # of the "live" system: + echo >&2 "$0: Error: exec requires a command to run" + exit 1 + fi + + # Command given: run it: + shift + "$@" + ;; +xpp-firm) + # Still broken. Needs to be run several times. + # set XPP_HOTPLUG_DISABLED=yes in /etc/dahdi/init.conf + XPP_FIRMWARE_DIR=$FIRMWARE_DIR \ + sh "$TOOLS_DIR"/xpp/xpp_fxloader reset + sleep 5 + XPP_FIRMWARE_DIR=$FIRMWARE_DIR \ + sh "$TOOLS_DIR"/xpp/xpp_fxloader load + ;; +help) + usage + ;; +*) + echo >&2 "$0: Error: incorrect command \"$1\". Aborting" + usage + exit 1 +esac diff -Nru dahdi-linux-2.2.1-rc2/build_tools/make_version dahdi-cnet-linux-2.2.1-rc2/build_tools/make_version --- dahdi-linux-2.2.1-rc2/build_tools/make_version 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/build_tools/make_version 2008-05-23 10:32:46.000000000 -0500 @@ -0,0 +1,56 @@ +#!/bin/sh + +if [ -f ${1}/.version ]; then + cat ${1}.version +elif [ -f ${1}/.svnrevision ]; then + echo SVN-`cat ${1}/.svnbranch`-r`cat ${1}/.svnrevision` +elif [ -d .svn ]; then + PARTS=`LANG=C svn info ${1} | grep URL | awk '{print $2;}' | sed -e s:^.*/svn/${2}/:: | sed -e 's:/: :g'` + BRANCH=0 + TEAM=0 + + REV=`svnversion -c ${1} | cut -d: -f2` + + if [ "${PARTS}" = "trunk" ] + then + echo SVN-'trunk'-r${REV} + exit 0 + fi + + for PART in $PARTS + do + if [ ${BRANCH} != 0 ] + then + RESULT="${RESULT}-${PART}" + break + fi + + if [ ${TEAM} != 0 ] + then + RESULT="${RESULT}-${PART}" + continue + fi + + if [ "${PART}" = "branches" ] + then + BRANCH=1 + RESULT="branch" + continue + fi + + if [ "${PART}" = "tags" ] + then + BRANCH=1 + RESULT="tag" + continue + fi + + if [ "${PART}" = "team" ] + then + TEAM=1 + continue + fi + done + + echo SVN-${RESULT##-}-r${REV} +fi diff -Nru dahdi-linux-2.2.1-rc2/build_tools/make_version_h dahdi-cnet-linux-2.2.1-rc2/build_tools/make_version_h --- dahdi-linux-2.2.1-rc2/build_tools/make_version_h 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/build_tools/make_version_h 2008-05-21 11:59:46.000000000 -0500 @@ -0,0 +1,9 @@ +#!/bin/sh +cat << END +/* + * version.h + * Automatically generated + */ +#define DAHDI_VERSION "${DAHDIVERSION}" + +END diff -Nru dahdi-linux-2.2.1-rc2/build_tools/test_kernel_git dahdi-cnet-linux-2.2.1-rc2/build_tools/test_kernel_git --- dahdi-linux-2.2.1-rc2/build_tools/test_kernel_git 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/build_tools/test_kernel_git 2008-06-28 17:02:27.000000000 -0500 @@ -0,0 +1,101 @@ +#!/bin/sh + +set -e + +GIT_URL=git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git +CONF_FILE=build_tools/git_test.conf + +usage() { + me=`basename $0` + echo "$me: test building DAHDI vs. kernel from git" + echo "Usage:" + echo " $me checkout Pull a kernel version into " + echo " $me update Update (pull) the kernel tree." + echo " $me setver Set the kernel version" + echo " $me versions Print available versions" + echo " $me version Print current (kernel) version" + echo " $me version_driver Print the version of DAHDI" + echo " $me build Test-build" + echo " $me git Run " + echo "" + echo " $me versions [pattern] List available versions." +} + +# Set a variable in $CONF_FILE +# The format of CONF_FILE is assumed to be: +# VAR=value +# in shell syntax. "value" may be quoted. +# "value should not contain a '|' character. +set_var() { + var="$1" + val="$2" + if grep -q "^$var=" $CONF_FILE 2>/dev/null; then + sed -i -e "s|^$var=.*|$var=\"$val\"|" $CONF_FILE + else + echo "$var=\"$val\"" >>$CONF_FILE + fi +} + +if [ -r "$CONF_FILE" ]; then . "$CONF_FILE"; fi + +if echo "$CONF_FILE" | grep -qv '^/'; then + # make CONF_FILE an absolute path: + CONF_FILE="$PWD/$CONF_FILE" +fi + +command="$1" + +case "$command" in + checkout) + kernel_dir="$2" + cd "$kernel_dir" + git clone $GIT_URL + set_var kernel_dir "$kernel_dir/linux-2.6" + ;; + update) + cd "$kernel_dir" + git pull + ;; + git) + cd "$kernel_dir" + shift + git "$@" + ;; + versions) + cd "$kernel_dir" + git tag -l $2 | cut -c2- + ;; + version) + cd "$kernel_dir" + echo "Configured: $kernel_ver" + echo -n "Actual: " + git describe | cut -c2- + ;; + version_driver) + version_h=include/dahdi/version.h + make $version_h >/dev/null + awk -F'"' '/DAHDI_VERSION/{print $2}' $version_h + ;; + setver) + kernel_ver="$2" + tag="v$kernel_ver" + cd "$kernel_dir" + git-reset --hard "$tag" + make distclean + make defconfig modules_prepare + set_var kernel_ver "$kernel_ver" + ;; + test|build) + # you can pass extra parameters to the make command in + # two ways: + # 1. Set the value of MAKE_PARAMS in git_test.conf . + # 2. Any extra command-line parameter. + shift + make KSRC="$kernel_dir" KVERS=$kernel_ver $MAKE_PARAMS "$@" + ;; + *) + echo "$0: no such command $command. Aborting." + usage + exit 1 + ;; +esac diff -Nru dahdi-linux-2.2.1-rc2/build_tools/uninstall-modules dahdi-cnet-linux-2.2.1-rc2/build_tools/uninstall-modules --- dahdi-linux-2.2.1-rc2/build_tools/uninstall-modules 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/build_tools/uninstall-modules 2008-07-08 15:18:38.000000000 -0500 @@ -0,0 +1,64 @@ +#!/bin/sh + +# This script takes two arguments: a top-level module name, and a kernel version string +# +# It will search the entire /lib/modules directory tree for the given kernel version, +# and find all modules that are dependent (even indirectly) on the specified module. +# After producing that list, it will remove all those modules. + +base="${1}" +deptree="${base}" +rmlist="" +founddep=1 + +checkmod() { + SAVEIFS="${IFS}" + IFS="," + modname=`basename ${1}` + modname=${modname%.ko} + if test "${modname}" = "${base}"; then + rmlist="${rmlist} ${1}" + IFS="${SAVEIFS}" + return + fi + for dep in `modinfo -F depends ${1}`; do + for mod in ${deptree}; do + if test "${dep}" = "${mod}"; then + addit=1 + for checkmod in ${deptree}; do + if test "${checkmod}" = "${modname}"; then + addit=0 + break + fi + done + if test "${addit}" = "1"; then + deptree="${deptree},${modname%.ko}" + rmlist="${rmlist} ${1}" + founddep=1 + fi + fi + done + done + IFS="${SAVEIFS}" +} + + +while test "${founddep}" = "1"; do + founddep=0 + find /lib/modules/${2}/misc -name \*.ko -print > /tmp/modlist.$$ 2> /dev/null + find /lib/modules/${2}/extra -name \*.ko -print >> /tmp/modlist.$$ 2> /dev/null + find /lib/modules/${2}/zaptel -name \*.ko -print >> /tmp/modlist.$$ 2> /dev/null + find /lib/modules/${2}/dahdi -name \*.ko -print >> /tmp/modlist.$$ 2> /dev/null + exec 9<&0 < /tmp/modlist.$$ + while read mod; do + checkmod ${mod} + done + exec 0<&9 9<&- + rm /tmp/modlist.$$ +done + +if test -n "${rmlist}"; then + for mod in ${rmlist}; do + rm -f ${mod} + done +fi diff -Nru dahdi-linux-2.2.1-rc2/ChangeLog dahdi-cnet-linux-2.2.1-rc2/ChangeLog --- dahdi-linux-2.2.1-rc2/ChangeLog 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/ChangeLog 2009-12-15 15:48:28.000000000 -0600 @@ -0,0 +1,2259 @@ +2009-12-11 23:22 +0000 [r7683-7684] Shaun Ruffell + + * dahdi-linux version 2.2.1-rc2 released. + + * /, drivers/dahdi/dahdi-base.c: Merged revisions 7682 via svnmerge + from https://origsvn.digium.com/svn/dahdi/linux/trunk ........ + r7682 | sruffell | 2009-12-11 17:20:03 -0600 (Fri, 11 Dec 2009) | + 5 lines dahdi-base: Do not wait for impulse when echotraining. + Waiting here for the impulse on the transmit can cause the + echotraining logic to stick the channel into muted state. This is + especially apparent on systems that regularly do not service the + interrupts every millisecond. DAHDI-387. ........ + + * /, drivers/dahdi/dahdi-base.c: Merged revisions 7681 via svnmerge + from https://origsvn.digium.com/svn/dahdi/linux/trunk ........ + r7681 | sruffell | 2009-12-11 17:20:02 -0600 (Fri, 11 Dec 2009) | + 6 lines dahdi-base: Reduce the max allocation size in + dahdi_reallocbufs. Lower the maximum contiguous chunk that DAHDI + asks for from DAHDI_MAX_BUFFER_SIZE*2 to DAHDI_MAX_BUFFER_SIZE. + With 4K pages, this can allow the kernel to try a little harder + to find the memory it needs since the request goes from order 4 + to order 3. ........ + +2009-11-23 19:20 +0000 [r7636] Shaun Ruffell + + * dahdi-linux version 2.2.1-rc1 released. + + * drivers/dahdi/wcte12xp/base.c, /: Merged revisions 7632 via + svnmerge from https://origsvn.digium.com/svn/dahdi/linux/trunk + ........ r7632 | mspiceland | 2009-11-23 13:13:17 -0600 (Mon, 23 + Nov 2009) | 4 lines Even if we are debouncing the LOS + declaration, we still expect the LED to turn red as soon as we + unplug the physical cable. This impliments this on the wcte12xp + just as it already does on the wct4xxp. ........ + +2009-11-22 11:43 +0000 [r7616-7620] Tzafrir Cohen + + * /, drivers/dahdi/xpp/firmwares/USB_FW.hex: xpp firmware: USB + firmware with support of MPP 1.4 xpp rev: 7419 Merged revisions + 7615 via svnmerge from + http://svn.digium.com/svn/dahdi/linux/trunk + + * drivers/dahdi/xpp/firmwares/PIC_TYPE_1.hex, /: xpp firmware: fix + FXS indirect register reading. xpp rev: 7498 Merged revisions + 7614 via svnmerge from + http://svn.digium.com/svn/dahdi/linux/trunk + +2009-11-19 22:52 +0000 [r7610] Tzafrir Cohen + + * drivers/dahdi/xpp/xpp.rules, /: xpp: rules for loading USB + firmware into 1163 devices as well Merged revisions 7595 via + svnmerge from http://svn.digium.com/svn/dahdi/linux/trunk + +2009-11-19 19:15 +0000 [r7604] Shaun Ruffell + + * /, drivers/dahdi/wctdm24xxp/base.c: Merged revisions 7601 via + svnmerge from https://origsvn.digium.com/svn/dahdi/linux/trunk + ........ r7601 | sruffell | 2009-11-19 10:11:20 -0600 (Thu, 19 + Nov 2009) | 1 line wctdm24xxp: Honor the alawoverride parameter + when using the VPMADT032. ........ + +2009-11-12 23:45 +0000 [r7572-7584] Shaun Ruffell + + * drivers/dahdi/wcte12xp/base.c, /, + drivers/dahdi/voicebus/GpakCust.c: Merged revisions 7582 via + svnmerge from https://origsvn.digium.com/svn/dahdi/linux/trunk + ........ r7582 | sruffell | 2009-11-12 17:19:13 -0600 (Thu, 12 + Nov 2009) | 1 line wcte12xp, voicebus: Set the companding mode + correctly on E1. ........ + + * drivers/dahdi/voicebus/GpakCust.h, drivers/dahdi/wcte12xp/base.c, + /, include/dahdi/kernel.h, drivers/dahdi/adt_lec.c, + drivers/dahdi/wctdm24xxp/base.c, drivers/dahdi/adt_lec.h, + drivers/dahdi/voicebus/GpakCust.c: Merged revisions + 7309,7348,7565-7571 via svnmerge from + https://origsvn.digium.com/svn/dahdi/linux/trunk ........ r7309 | + mattf | 2009-10-02 11:31:58 -0500 (Fri, 02 Oct 2009) | 1 line + Implement API update to do per-channel companding selection for + VPMADT032 ........ r7348 | mattf | 2009-10-07 16:26:08 -0500 + (Wed, 07 Oct 2009) | 1 line Fix a logic error in the companding + check. Duh.... ........ r7565 | sruffell | 2009-11-12 13:22:06 + -0600 (Thu, 12 Nov 2009) | 7 lines voicebus: Fix race when + enabling/disabling hardware echocan. This closes a race condition + where it was possible for the driver to believe it has enabled + the VPMADT032 when in fact, it really has not. This fixes a + regression introduced in dahdi-linux 2.2.0. (issue #15724) + ........ r7566 | sruffell | 2009-11-12 13:22:06 -0600 (Thu, 12 + Nov 2009) | 1 line wctdm24xxp, wcte12xp: We no longer have any + DTMF events to check for. ........ r7567 | sruffell | 2009-11-12 + 13:22:07 -0600 (Thu, 12 Nov 2009) | 1 line voicebus: Remove + unused curtone from 'struct vpmadt032' ........ r7568 | sruffell + | 2009-11-12 13:22:07 -0600 (Thu, 12 Nov 2009) | 1 line voicebus: + Remove redundant MAX_CHANNELS_FROM_SPAN ........ r7569 | sruffell + | 2009-11-12 13:22:08 -0600 (Thu, 12 Nov 2009) | 3 lines + voicebus: Use dev_xxx macro when printing vpm messages. We also + do not need the unused context member of the vpmadt032 structure. + ........ r7570 | sruffell | 2009-11-12 13:22:08 -0600 (Thu, 12 + Nov 2009) | 4 lines wcte12xp: Change serial port configuration + setting for hw echocan. The wcte12xp, like the wctdm24xpp, should + have the PcmOutPortA set to SerialPortNull. ........ r7571 | + sruffell | 2009-11-12 13:56:49 -0600 (Thu, 12 Nov 2009) | 1 line + kernel.h: Define 'list_replace' for kernels < 2.6.18 ........ + +2009-11-10 15:47 +0000 [r7539-7550] Shaun Ruffell + + * drivers/dahdi/wcte12xp/base.c, /: Merged revisions 7549 via + svnmerge from https://origsvn.digium.com/svn/dahdi/linux/trunk + ........ r7549 | sruffell | 2009-11-10 09:46:18 -0600 (Tue, 10 + Nov 2009) | 9 lines wcte12xp: Export features and operations for + VPMADT032. Fixes a regression in dahdi-linux 2.2.0 where it was + impossible for userspace to reset the state of a channel in the + VPM. (issue #15724) Patches: mantis-15724-2.patch uploaded by + sruffell (license 456) Tested by: alecdavis ........ + + * drivers/dahdi/wcte12xp/base.c, drivers/dahdi/wct4xxp/base.c: + wct4xxp, wcte12xp: Don't export new module parms by default on + the 2.2 branch Most people are going to use the defaults, and + this eliminates a potential interface change that would cause + problems if someone wants to go between 2.2.1 and 2.2.0.4. + + * drivers/dahdi/voicebus/voicebus.h, + drivers/dahdi/wcb4xxp/wcb4xxp.h, + drivers/dahdi/wct4xxp/wct4xxp-diag.c, Makefile, + drivers/dahdi/voicebus/GpakCust.h, + drivers/dahdi/hpec/dahdi_echocan_hpec.c, + drivers/dahdi/firmware/Makefile, drivers/dahdi/wcb4xxp/base.c, + drivers/dahdi/dahdi_dynamic.c, + drivers/dahdi/wctdm24xxp/wctdm24xxp.h, drivers/dahdi/wctdm.c, + drivers/dahdi/proslic.h, drivers/dahdi/dahdi-base.c, + drivers/dahdi/dahdi_dummy.c, README, drivers/dahdi/xpp/xdefs.h, + drivers/dahdi/wcfxo.c, drivers/dahdi/wcte12xp/base.c, + include/dahdi/user.h, /, include/dahdi/kernel.h, + drivers/dahdi/wct4xxp/base.c, drivers/dahdi/voicebus/voicebus.c, + drivers/dahdi/wctc4xxp/base.c, drivers/dahdi/wctdm24xxp/base.c, + drivers/dahdi/wcte12xp/wcte12xp.h, + drivers/dahdi/voicebus/GpakCust.c: Merged revisions + 6699,6706,6714,6768,6771,6785,6812-6818,6821,6838,6928-6929,6941,6945-6946,6952,6981-6982,7003-7004,7008,7023,7027,7094-7097,7110,7117-7118,7125,7140,7147,7155,7194,7284,7293,7319,7437-7438,7445,7480,7486-7487,7512-7524,7527-7528,7534-7536 + via svnmerge from + https://origsvn.digium.com/svn/dahdi/linux/trunk ........ r6699 | + kpfleming | 2009-06-23 15:32:01 -0500 (Tue, 23 Jun 2009) | 3 + lines Use the same mutex lock for channel allocation and license + checking in dahdi_echocan_hpec, so that channel allocation won't + happen while the license is being checked (or rechecked) ........ + r6706 | sruffell | 2009-06-23 18:21:25 -0500 (Tue, 23 Jun 2009) | + 1 line README: Minor corrections to the README. ........ r6714 | + twilson | 2009-06-24 15:23:07 -0500 (Wed, 24 Jun 2009) | 1 line + Remove unused torisa code from header ........ r6768 | sruffell | + 2009-06-26 12:07:39 -0500 (Fri, 26 Jun 2009) | 1 line wct4xxp: + Unmap the same size DMA window that was mapped. ........ r6771 | + sruffell | 2009-06-26 12:22:45 -0500 (Fri, 26 Jun 2009) | 6 lines + dahdi_dynamic: Release the dlock before calling accross modules. + Resolves a hard lock due to a recursive spinlock grab at startup. + Reported by: mapacheco (closes issue #15210) ........ r6785 | + sruffell | 2009-06-28 23:47:26 -0500 (Sun, 28 Jun 2009) | 5 lines + echocan: Properly keep the reference counts for the echocan + modules. (closes issue #13504) (closes issue #15327) Reported by: + sruffell, tzafrir ........ r6812 | tzafrir | 2009-07-02 14:25:54 + -0500 (Thu, 02 Jul 2009) | 5 lines wcb4xxp: Don't assume we have + 4 spans (ports). First part of extra hfcmulti drivers: the number + of ports is still hardwired to 4, but just in a single place. + ........ r6813 | tzafrir | 2009-07-02 14:34:05 -0500 (Thu, 02 Jul + 2009) | 4 lines wcb4xxp: Extra bit macros that a needed for + kernels < 2.6.24 Macros borrowed from xpp/ . ........ r6814 | + tzafrir | 2009-07-02 14:42:25 -0500 (Thu, 02 Jul 2009) | 2 lines + wcb4xxp: Adjust debug filter code to number of ports. ........ + r6815 | tzafrir | 2009-07-02 14:52:14 -0500 (Thu, 02 Jul 2009) | + 6 lines wcb4xxp: support for other HFC-xS cards (info, not code) + This commit includes skeleton for the support of other + HFC-[248]S-based cards. It still does not include all the + different cases for different cards. ........ r6816 | tzafrir | + 2009-07-02 14:59:44 -0500 (Thu, 02 Jul 2009) | 2 lines The B410P + differs from other HFC-xS cards with respect to the EC unit + ........ r6817 | tzafrir | 2009-07-02 15:07:36 -0500 (Thu, 02 Jul + 2009) | 7 lines More B410P differences: Clock and NT/TE. * The + B410P reads the NT/TE switches the other way around from other + cards. * Its clock is also 1/2 of that of other cards, which + causes wierd PCM on an unmodified driver. ........ r6818 | + tzafrir | 2009-07-02 15:14:39 -0500 (Thu, 02 Jul 2009) | 4 lines + wcb4xxp: Fix PCM handling for various cards. HFC-8S cards behave + quite differently than HFC-4S cards here. ........ r6821 | + tzafrir | 2009-07-02 15:18:59 -0500 (Thu, 02 Jul 2009) | 6 lines + wcb4xxp: Fix LED handling in OpenVox cards (maybe also others) + This commit adds extra functions to handle LEDs in the non-B410P + cards. Only tested on OpenVox cards. OpenVox cards are known to + have slightly different LEDs so this is likely to be slightly + broken for others. ........ r6838 | sruffell | 2009-07-13 + 09:33:39 -0500 (Mon, 13 Jul 2009) | 1 line wctc4xxp: Remove + deprecated DMA_xxBIT_MASK usage. ........ r6928 | sruffell | + 2009-08-04 11:22:23 -0500 (Tue, 04 Aug 2009) | 1 line wcte12xp: + Remove unused 'schluffen' declaration in wcte12xp.h ........ + r6929 | sruffell | 2009-08-04 11:22:26 -0500 (Tue, 04 Aug 2009) | + 4 lines wctdm24xxp: Eliminate 'variety' and 'type' members from + 'struct wctdm'. struct wctdm can hold a pointer to struct + wctdm_desc directly, and eliminate the need to copy members of + wctdm_desc into wctdm. ........ r6941 | dbailey | 2009-08-05 + 09:40:45 -0500 (Wed, 05 Aug 2009) | 14 lines Change proslic + linefeed register setting Insure that proslic linefeed register + is not transitioned from Active to On-Hook Transmission while the + channel is off-hook. Replaced magic numbers assigned to linefeed + associated variables with more descriptive constants. (issue + #15352) Reported by: alecdavis Patches: + wctdm_prevent_ohttimer_click.diff3.txt uploaded by dbailey + (license 819) Tested by: alecdavis, dbailey, vmikhelson ........ + r6945 | sruffell | 2009-08-05 14:39:10 -0500 (Wed, 05 Aug 2009) | + 6 lines wctdm24xxp, wctdm: Formatting changes. Update the lines + affected by revision 6941. I'm taking every opportunity to move + DAHDI closer to the kernel coding conventions short of just + reformatting for the sake of reformatting. The majority of these + changes are to bring the line lengths under 80 chars. ........ + r6946 | sruffell | 2009-08-05 14:39:13 -0500 (Wed, 05 Aug 2009) | + 1 line dahdi_dummy: Remove some trailing whitespace. ........ + r6952 | sruffell | 2009-08-11 13:47:21 -0500 (Tue, 11 Aug 2009) | + 3 lines wctdm24xxp: Remove unused members related to hardware + DTMF detection. Not used anymore, so they are gone. ........ + r6981 | sruffell | 2009-08-13 09:42:05 -0500 (Thu, 13 Aug 2009) | + 1 line wctc4xxp: Remove flag member that is not used. ........ + r6982 | sruffell | 2009-08-13 09:42:08 -0500 (Thu, 13 Aug 2009) | + 1 line wctdm24xxp: Use the ARRAY_SIZE macro where appropriate. + ........ r7003 | sruffell | 2009-08-13 19:46:26 -0500 (Thu, 13 + Aug 2009) | 9 lines wctdm24xxp, wctdm: Detect if our hookstate + has been set back to the initial state. Check if our hookstate + has been set back to the initial state, typically the result of a + chanconfig, and if so, if we're an FXO port, forget our current + battery state. This allows the driver to determine and report + again what the hook state of the port is. (related to issue + #14577) (closes issue #15429) ........ r7004 | sruffell | + 2009-08-13 19:46:28 -0500 (Thu, 13 Aug 2009) | 1 line dahdi-base: + Add comment to explain why rxhooksig is reset on span start. + ........ r7008 | sruffell | 2009-08-14 10:47:39 -0500 (Fri, 14 + Aug 2009) | 11 lines wcfxo: Reset the DAA on module + initialization. The X100p and clones will sometimes work and + sometimes not depending on wether the DAA powers up in running + state- this seems to be related to the power supply. This problem + is caused by the driver not reseting the DAA and may be the + source of a great many intermittent problems with this card. + (closes issue #14232) Reported by: tallen8840 Patch by: + tallen8840 Tested by: explidous, Flavio ........ r7023 | sruffell + | 2009-08-17 09:07:06 -0500 (Mon, 17 Aug 2009) | 4 lines README: + Update known issues section. Remove note about echocanceler + reference counts, since that is fixed, and add a note about issue + with KB1 when configured with more than 128 taps. ........ r7027 + | seanbright | 2009-08-17 14:31:54 -0500 (Mon, 17 Aug 2009) | 7 + lines Silence spurious warnings when trying to remove Zaptel + directories during install. (closes issue #15479) Reported by: + pprindeville Patches: dahdi-linux-rm.patch uploaded by + pprindeville (license 347) ........ r7094 | sruffell | 2009-09-07 + 16:40:19 -0500 (Mon, 07 Sep 2009) | 4 lines wctdm24xxp: Remove a + few more unneeded 'volatile' keywords. The writechunk and + readchunk parameters are never accessed by hardware at the same + time that the software is accessing them anymore. ........ r7095 + | sruffell | 2009-09-07 16:40:22 -0500 (Mon, 07 Sep 2009) | 5 + lines dahdi-base: Reduce the stack usage of dahdi_common_ioctl. + Split the DAHDI_GETGAINS and DAHDI_SETGAINS ioctls into their own + functions and dynamically allocate the 'struct dahdi_gains' + structure to reduce the pressure on the stack. ........ r7096 | + sruffell | 2009-09-07 16:40:25 -0500 (Mon, 07 Sep 2009) | 1 line + dahdi-base: Prevent compilation if both EMPULSE and EMFLASH are + defined. ........ r7097 | sruffell | 2009-09-07 16:40:29 -0500 + (Mon, 07 Sep 2009) | 4 lines dahdi-base: Fix flag check in + dahdi_rbs_sethook. As long as any of the flags were set, this + check would have always passed. ........ r7110 | rmeyerriecks | + 2009-09-14 15:30:24 -0500 (Mon, 14 Sep 2009) | 1 line Fixed issue + where the clear channel flags were not being set at the + appropriate time causing a channel re-configure to mis-set the + last channel in each span ........ r7117 | dbailey | 2009-09-14 + 15:51:56 -0500 (Mon, 14 Sep 2009) | 17 lines Race condition in + handling writes to proslic LINEFEED register (64) The wctdm24xxp + driver has a problem where a VMWI IOCTL call followed immediately + by a ONHOOKTRANSFER IOCTL call will cause the ONHOOK transfer + request to be dropped. This occurs if the write to the proslic's + LINEFEED register for the VMWI ICTL call is not completed when + the ONHOOK transfer request IOCTL is processed. I also cleaned + out some magic numbers used in setting the linefeed register. + (closes issue #15875) Reported by: dbailey Patches: + 15875-wctdm24xxp.diff uploaded by dbailey (license 819) Tested + by: dbailey ........ r7118 | seanbright | 2009-09-14 16:10:38 + -0500 (Mon, 14 Sep 2009) | 1 line Change zap -> dahdi. ........ + r7125 | rmeyerriecks | 2009-09-15 09:59:06 -0500 (Tue, 15 Sep + 2009) | 2 lines dahdi-base: Minor syntax change to meet style + guidelines ........ r7140 | dbailey | 2009-09-15 15:50:45 -0500 + (Tue, 15 Sep 2009) | 12 lines Change WCTDM SPI clock off state + polarity and read timing Change the off state of the SPI clock to + high and provide more time for data to settle out on SPI reads. + (closes issue #15261) Reported by: alecdavis Patches: + wctdm_spi_clocking.diff2.txt uploaded by alecdavis (license 585) + Tested by: alecdavis, dbailey ........ r7147 | sruffell | + 2009-09-16 13:19:00 -0500 (Wed, 16 Sep 2009) | 4 lines wct4xxp: + Check the alarm state if we're debouncing a red alarm. This fixes + a problem where if you set the alarmdebounce module parameter on + gen2+ cards, you never detect when you go into red alarm. + ........ r7155 | sruffell | 2009-09-21 10:24:36 -0500 (Mon, 21 + Sep 2009) | 8 lines dahdi-base: dahdi_ioctl_[get|set]gains should + return the res value. In function dahdi_ioctl_getgains() and + dahdi_ioctl_setgains() return value assigned to res variable, but + these function always return 0 which is an error. (closes issue + #15916.) Patch by: ys ........ r7194 | dbailey | 2009-09-22 + 09:03:53 -0500 (Tue, 22 Sep 2009) | 12 lines wctdm: Add missing + break A break was missing that caused DAHDI_ONHOOKTRANSFER ioctl + call to fall into DAHDI_SETPOLARITY ioctl call. (issue #14261) + Reported by: alecdavis Patches: wctdm_fix_ONHOOKTRANSFER.diff.txt + uploaded by alecdavis (license 585) Tested by: alecdavis ........ + r7284 | mattf | 2009-09-30 11:34:11 -0500 (Wed, 30 Sep 2009) | 1 + line Update echocan API so it only uses channel offset in free + routine ........ r7293 | tzafrir | 2009-09-30 13:09:42 -0500 + (Wed, 30 Sep 2009) | 2 lines move the dev->bus_id fix from xpp to + kernel.h: needed elsewhere ........ r7319 | sruffell | 2009-10-02 + 16:09:01 -0500 (Fri, 02 Oct 2009) | 1 line wcte12xp: The timer is + called every 200ms, not every 100ms. Fix comment. ........ r7437 + | sruffell | 2009-10-29 13:26:16 -0500 (Thu, 29 Oct 2009) | 10 + lines dahdi-base: Do not allow jumps in system time to lock up + the system w/core_timer Since dahdi coretimer uses the number of + milliseconds that has actually passed to determine how many times + to call dahdi_receive, it is possible that if the system time + shifts after dahdi is started, that the system can appear to lock + up while the core timer attempts to catch up. This change + prevents soft lock ups under these conditions. This is brings the + dahdi_dummy changes in r6933 into dahdi-base. (related to issue + #15647) ........ r7438 | sruffell | 2009-10-29 13:26:17 -0500 + (Thu, 29 Oct 2009) | 1 line wcte12xp, wctdm24xxp: VPMADT032 + firmware update to 1.20. ........ r7445 | mspiceland | 2009-10-29 + 16:37:45 -0500 (Thu, 29 Oct 2009) | 3 lines Debounce alarms by + default for wct4xxp per AT&T 54016. Also, the various alarm + conditions can be debounced separately. ........ r7480 | sruffell + | 2009-11-04 14:43:05 -0600 (Wed, 04 Nov 2009) | 4 lines + voicebus: Increase the NLP converged threshold to 18. Brings in + the change from r7065 that was on the + team/sruffell/dahdi-linux-vpm119 branch. ........ r7486 | + mspiceland | 2009-11-04 17:25:32 -0600 (Wed, 04 Nov 2009) | 9 + lines Adding alarm debounce to single span driver (wcte12xp). + Debounce yellow alarm also. In wcte12xp, change check alarm + frequency to 100ms for better debounce granularity. Fix lines + over 80 cols from last alarm debounce commit. ........ r7487 | + mspiceland | 2009-11-04 17:28:21 -0600 (Wed, 04 Nov 2009) | 2 + lines Remove commented out code block that was unintentionally + left in. ........ r7512 | sruffell | 2009-11-06 18:35:38 -0600 + (Fri, 06 Nov 2009) | 1 line wcte12xp: Remove unused flag member + and make const the t1_descs. ........ r7513 | sruffell | + 2009-11-06 18:35:38 -0600 (Fri, 06 Nov 2009) | 4 lines voicebus: + Remove the VB_PRINTK macro. Unnecessarily duplicates the dev_xxx + macros. Also removes the need for the board_name member from + struct voicebus. ........ r7514 | sruffell | 2009-11-06 18:35:39 + -0600 (Fri, 06 Nov 2009) | 4 lines voicebus: Remove sdi member + from 'struct voicebus' This is only used during startup so we + don't need to carry it around in the structure at all times. + ........ r7515 | sruffell | 2009-11-06 18:35:40 -0600 (Fri, 06 + Nov 2009) | 1 line voicebus: Set the DMA_BIT_MASK ........ r7516 + | sruffell | 2009-11-06 18:35:40 -0600 (Fri, 06 Nov 2009) | 1 + line voicebus: Use DAHDI_IRQ_SHARED instead of defining our own. + ........ r7517 | sruffell | 2009-11-06 18:35:40 -0600 (Fri, 06 + Nov 2009) | 24 lines voicebus: Send 'idle' buffers when the + transmit descriptor underruns. Previously, when the host system + fails to service the interrupt in a timely fashion, the transmit + descriptor ring for the voicebus card would "go empty" since the + interface wouldn't have another descriptor to read in. The driver + only knows that it went empty, not how far behind it actually + was. Therefore, the driver could just increase the latency by a + millisecond and keep going waiting for another bump. + Additionally, when the transmit descriptor actually goes empty, + there are some cases where an in process SPI transaction to one + of the modules is interrupted, which may result in corrupted + module register writes on rare occassions. This now makes it + possible for the voicebus drivers to coexist with some devices + that periodically lock interrupts for longer than 25ms. Before + this patch, the latency would constantly increase until either + the modules received a corrupted frame. This patch preconfigures + all the receive descriptors to send an "idle" packet that will be + transmitted to the onboard modules when the host doesn't service + the interrupt within (latency - 2)ms. There are now two kinds of + underruns, softunderuns where the driver can detect that these + idlebuffers have made it to the TX FIFO, and the normal hard + underrun where the part signals a transmit descriptor unavailable + interrupt. DAHDI-278. ........ r7518 | sruffell | 2009-11-06 + 18:35:41 -0600 (Fri, 06 Nov 2009) | 7 lines voicebus: Add + function to lock the latency. Now that increases in the latency + produce less undefined behavior on the SPI busses, provide an + interface for client drivers to inform the voicebus library to + not increase the latency if underruns are detected. This can + speed up loads of the driver since latency bumps do not trigger a + restart of the driver initialization. DAHDI-278. ........ r7519 | + sruffell | 2009-11-06 18:35:42 -0600 (Fri, 06 Nov 2009) | 4 lines + wcte12xp: Lock latency when loading No longer need to restart + board initialization if the latency would have increased during + initialization. DAHDI-278. ........ r7520 | sruffell | 2009-11-06 + 18:35:42 -0600 (Fri, 06 Nov 2009) | 4 lines wctdm24xxp: Lock + latency when loading We no longer need to retry board + initialization if the latency would have increased during the + initialization. DAHDI-278 ........ r7521 | sruffell | 2009-11-06 + 18:35:43 -0600 (Fri, 06 Nov 2009) | 4 lines voicebus, wctdm24xxp, + wcte12xp: Move a print out of the interrupt handler. This can be + handled just as well in process context and printing to a serial + console from the interrupt handler has the potential to cause + long latencies. ........ r7522 | sruffell | 2009-11-06 18:35:44 + -0600 (Fri, 06 Nov 2009) | 5 lines voicebus: Add optional sysfs + entry for reading a boards current latency. This is off by + default since it hasn't been tested on a full range of kernels, + but can be useful for quickly seeing differences for latencies on + different cards installed in the system. ........ r7523 | + sruffell | 2009-11-06 18:35:44 -0600 (Fri, 06 Nov 2009) | 1 line + voicebus: Remove 'assert' macros and use BUG_ON/WARN_ON directly. + ........ r7524 | sruffell | 2009-11-06 18:35:45 -0600 (Fri, 06 + Nov 2009) | 1 line voicebus: Be just a little more graceful if we + cannot grab our interrupt line. ........ r7527 | sruffell | + 2009-11-06 18:58:03 -0600 (Fri, 06 Nov 2009) | 1 line wcte12xp: + Fix up some continued strings. ........ r7528 | sruffell | + 2009-11-06 18:58:03 -0600 (Fri, 06 Nov 2009) | 1 line wct4xxp: + Fix up some continued strings. ........ r7534 | sruffell | + 2009-11-09 12:02:40 -0600 (Mon, 09 Nov 2009) | 1 line wct4xxp: + Only print the new debounce messages when debug is set. ........ + r7535 | sruffell | 2009-11-09 12:02:41 -0600 (Mon, 09 Nov 2009) | + 1 line wcte12xp: Only print the new debounce messages when debug + is set. ........ r7536 | sruffell | 2009-11-09 12:11:06 -0600 + (Mon, 09 Nov 2009) | 4 lines wcte12xp: use the dev_xxx macro for + the debounce messages. We want to know which device is reporting + the debounce when there are more than one card in the system. + ........ + +2009-11-05 12:06 +0000 [r7485-7493] Tzafrir Cohen + + * drivers/dahdi/xpp/xpp_dahdi.c, /, drivers/dahdi/pciradio.c, + drivers/dahdi/xpp/xbus-sysfs.c, drivers/dahdi/wctdm.c, + drivers/dahdi/wctdm24xxp/base.c, drivers/dahdi/xpp/xbus-core.c, + drivers/dahdi/dahdi-base.c: backport a number of build fixes from + trunk to 2.2 Merged revisions 7226,7356,7392 via svnmerge from + http://svn.digium.com/svn/dahdi/linux/trunk ........ r7226 | + tzafrir | 2009-09-28 10:57:07 +0200 (Mon, 28 Sep 2009) | 8 lines + xpp: Use proper get/set for device->driver_data 2.6.32-rc1 broke + direct access to the member 'driver_data' of 'struct device'. + However direct access to wasn't proper in the first place. This + commit replaces direct access to dev->driver_data with + dev_get_drvdata() and dev_set_drvdata(). ........ r7356 | + sruffell | 2009-10-09 07:22:55 +0200 (Fri, 09 Oct 2009) | 6 lines + dahdi-base: Include linux/sched.h Commit a99bba to the mainline + kernel removed sched.h from poll.h. So dahdi-base.c needs to + include sched.h directly now. + http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=a99bba + ........ r7392 | sruffell | 2009-10-12 22:05:35 +0200 (Mon, 12 + Oct 2009) | 4 lines headers: sched.h was also removed from + interrupts.h Commit d43c36 made it necessary to add sched.h to + more of the board drivers. + http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=d43c36 + ........ + + * /, drivers/dahdi/xpp/xbus-sysfs.c: xpp: Add sysfs xpd attribute + 'timing_priority' Merged revisions 7237 via svnmerge from + http://svn.digium.com/svn/dahdi/linux/trunk + + * drivers/dahdi/xpp/xbus-pcm.c, + drivers/dahdi/xpp/firmwares/PIC_TYPE_4.hex, + drivers/dahdi/xpp/init_card_4_30, drivers/dahdi/xpp/xpp_dahdi.c, + /, drivers/dahdi/xpp/card_pri.c, drivers/dahdi/xpp/xbus-pcm.h, + drivers/dahdi/xpp/card_bri.c, drivers/dahdi/xpp/xproto.c: T1 CAS + support in the xpp "pri" module Merged revisions + 7244-7245,7266,7276,7457 via svnmerge from + http://svn.digium.com/svn/dahdi/linux/trunk ........ r7244 | + tzafrir | 2009-09-29 18:23:13 +0200 (Tue, 29 Sep 2009) | 5 lines + xpp: make card_hooksig an optional card method. Done in + preperation for T1 CAS support, as the PRI module will use RBS + instead. ........ r7245 | tzafrir | 2009-09-29 18:45:15 +0200 + (Tue, 29 Sep 2009) | 2 lines xpp: refactor pcm mask handling. + ........ r7266 | tzafrir | 2009-09-30 00:43:05 +0200 (Wed, 30 Sep + 2009) | 2 lines xpp: T1 CAS support ........ r7276 | tzafrir | + 2009-09-30 15:49:48 +0200 (Wed, 30 Sep 2009) | 2 lines xpp: PRI + PIC firmware: T1-CAS fixes ........ r7457 | tzafrir | 2009-11-03 + 22:24:13 +0200 (Tue, 03 Nov 2009) | 4 lines xpp: More E1/T1 CAS + fixes. Mostly connect/disconnect xpp revs: mostly 7458, 7466 + ........ + +2009-08-19 16:44 +0000 [r7039] Kevin P. Fleming + + * drivers/dahdi/wcte12xp/base.c, /, drivers/dahdi/wct4xxp/base.c, + drivers/dahdi/wctdm24xxp/base.c: Merged revisions 7038 via + svnmerge from https://origsvn.digium.com/svn/dahdi/linux/trunk + ........ r7038 | kpfleming | 2009-08-19 11:40:13 -0500 (Wed, 19 + Aug 2009) | 9 lines Ensure that dahdi_scan correctly reports VPM + presence. The wct4xxp driver (for the dual/quad span T1/E1 cards) + and the wcte12xp driver (for the single span VoiceBus-based T1/E1 + cards) did not properly update the 'devicetype' field reported by + dahdi_scan when a VPM was found during the card startup process. + As a result, dahdi_scan did not show that a VPM was present on + the card, even if it was. ........ + +2009-08-13 22:04 +0000 [r7001] Tzafrir Cohen + + * /, drivers/dahdi/xpp/firmwares/FPGA_1161.hex: xpp firmware: Fixes + PCM issue with FXO that is not a timing source Fixes PCM issue + with an Astribank2 (116x) FXO module that is installed alongside + a PRI/BRI module. xpp: FPGA_1161.hex r7276 . Merged revisions + 6938,6966 via svnmerge from + http://svn.digium.com/svn/dahdi/linux/trunk + +2009-08-06 17:35 +0000 [r6950] Shaun Ruffell + + * /, drivers/dahdi/dahdi-base.c: Merged revisions 6890-6891 via + svnmerge from https://origsvn.digium.com/svn/dahdi/linux/trunk + ........ r6890 | sruffell | 2009-07-23 17:01:04 -0500 (Thu, 23 + Jul 2009) | 1 line dahdi-base: Reduce the stack usage of + ioctl_load_zone. ........ r6891 | sruffell | 2009-07-23 17:26:25 + -0500 (Thu, 23 Jul 2009) | 3 lines dahdi-base: Update formatting + in ioctl_load_zone. Fixes checkpatch.pl formatting errors from + the previous commit. ........ + +2009-08-04 16:38 +0000 [r6934-6937] Shaun Ruffell + + * /, drivers/dahdi/wctc4xxp/base.c: Merged revisions 6717,6760 via + svnmerge from https://origsvn.digium.com/svn/dahdi/linux/trunk + ........ r6717 | sruffell | 2009-06-24 21:30:19 -0500 (Wed, 24 + Jun 2009) | 3 lines wctc4xxp: Update to use struct + net_device_ops. Accomodates a change in the linux kernel network + device interface. ........ r6760 | sruffell | 2009-06-25 17:16:34 + -0500 (Thu, 25 Jun 2009) | 1 line wctc4xxp: Make the + wctc4xxp_netdev_ops structure static. ........ + + * drivers/dahdi/dahdi_dummy.c, /: Merged revisions 6933 via + svnmerge from https://origsvn.digium.com/svn/dahdi/linux/trunk + ........ r6933 | sruffell | 2009-08-04 11:22:39 -0500 (Tue, 04 + Aug 2009) | 10 lines dahdi_dummy: Do not allow jumps in system + time to lock up the system. Since dahdi_dummy uses the number of + milliseconds that has actually passed to determine how many times + to call dahdi_receive, it is possible that if the system time + shifts after dahdi is started, that the system can appear to lock + up while dahdi_dummy attempts to catch up. This change prevents + soft lock ups under these conditions. (closes issue #15647) + Reported by: missnebun ........ + +2009-07-21 18:11 +0000 [r6864] Shaun Ruffell + + * drivers/dahdi/tor2.c, drivers/dahdi/wcte12xp/base.c, /, + include/dahdi/dahdi_config.h, drivers/dahdi/wct4xxp/base.c, + drivers/dahdi/wcte11xp.c, drivers/dahdi/dahdi-base.c: Merged + revisions 6844,6852,6862-6863 via svnmerge from + https://origsvn.digium.com/svn/dahdi/linux/trunk ........ r6844 | + sruffell | 2009-07-16 12:29:53 -0500 (Thu, 16 Jul 2009) | 10 + lines wcte12xp: Disable vpmadt032 companding by default. This + fixes a regression in 2.2.0 where certain configurations will + fail patloop test or have repeated HDLC aborts because the + VPMADT032 is modifying the clear channel or d channel data + streams. This restores the behavior to how it was in dahdi-linux + 2.1.0.4. (closes issue #15498) Reported by: alecdavis Tested by: + alecdavis ........ r6852 | tzafrir | 2009-07-19 10:45:40 -0500 + (Sun, 19 Jul 2009) | 12 lines tor2: allow using port4 as timing + source Fix a silly regression introduced when strict check on the + timing parameter was added (sync-1 is the array index, not sync + itself. And 0 is a special case). (closes issue #15408) Reported + by: dferrer Patches: tor2-4th_sync.patch uploaded by dferrer + (license 525) ........ r6862 | sruffell | 2009-07-21 12:52:59 + -0500 (Tue, 21 Jul 2009) | 4 lines Revert "wct4xxp, wcte11xp: Use + the default configuration by default at startup." This reverts + the change introduced by revision 6712. This change can cause + problems when there is a VPM module installed on the quad-span + digital cards. ........ r6863 | sruffell | 2009-07-21 12:53:02 + -0500 (Tue, 21 Jul 2009) | 12 lines dahdi-base: Add support for + core timing. This essentially moves the function of dahdi_dummy + into the core of DAHDI. It ensures that if DAHDI is loaded, it + will always be able to provide timing, regardless of whether + there are board drivers loaded, or if the board drivers are + properly calling dahdi_receive. If there is a master span loaded + which is calling dahdi_receive, then the behavior will be like it + is normally. This functionality is off by default, uncomment + CONFIG_DAHDI_CORE_TIMER in include/dahdi/config_dahdi.h in order + to enable it. ........ + +2009-07-21 18:11 +0000 [r6858-6864] Shaun Ruffell + + * dahdi-linux version 2.2.0.2 released. + + * wcte12xp: Disable vpmadt032 companding by default. This + fixes a regression in 2.2.0 where certain configurations will + fail patloop test or have repeated HDLC aborts because the + VPMADT032 is modifying the clear channel or d channel data + streams. This restores the behavior to how it was in dahdi-linux + 2.1.0.4. (closes issue #15498) Reported by: alecdavis Tested by: + alecdavis + + * tor2: allow using port4 as timing source Fix a silly regression + introduced when strict check on the timing parameter was added + (sync-1 is the array index, not sync itself. And 0 is a special + case). (closes issue #15408) Reported by: dferrer Patches: + tor2-4th_sync.patch uploaded by dferrer (license 525) + + * Revert "wct4xxp, wcte11xp: Use the default configuration by default + at startup." This reverts the change introduced by revision 6712. + This change can cause problems when there is a VPM module installed + on the quad-span digital cards. + + * dahdi-base: Add support for core timing. This essentially moves the + function of dahdi_dummy into the core of DAHDI. It ensures that if + DAHDI is loaded, it will always be able to provide timing, + regardless of whether there are board drivers loaded, or if the + board drivers are properly calling dahdi_receive. If there is a + master span loaded which is calling dahdi_receive, then the behavior + will be like it is normally. This functionality is off by default, + uncomment CONFIG_DAHDI_CORE_TIMER in include/dahdi/config_dahdi.h in + order to enable it. + +2009-06-30 Shaun Ruffell + + * dahdi-linux version 2.2.0.1 released. + + * Fix for kernel panic when echotraining is enabled on echocan that + does not support it. + + * Fix for kernel panic on RHEL4 when using VPMADT032. + + * Fix to allow wct4xxp, wcb4xxp, and wcte11xp to provide early timing. + +2009-06-23 15:44 +0000 [r6695] Shaun Ruffell + + * dahdi-linux version 2.2.0 released. + + * README: Adding a known issues section to the README + files. + +2009-06-18 18:03 +0000 [r6692] Shaun Ruffell + + * drivers/dahdi/wcfxo.c, drivers/dahdi/wctdm.c, + drivers/dahdi/wctdm24xxp/base.c: Fix calls to dahdi_hooksig. When + JAPAN, AUDIO_RINGCHECK, or ZERO_BATT_RING compile time options are + selected it is possible to get a kernel panic due to an invalid + pointer passed to the dahdi_hooksig function. (closes issue #15350) + Patch by: alecdavis + +2009-06-12 22:30 +0000 [r6688] Jason Parker + + * drivers/dahdi/Kbuild: Make complex conditionals work with GNU + make 3.80. Much uglier, but it works on RHEL4. + +2009-06-04 21:14 +0000 [r6675] Shaun Ruffell + + * drivers/dahdi/dahdi-base.c: Fix bug in procfs + handling. Fix bug in procfs handling where it was possible to get + a warning in lib/vsprintf.c when reading from /proc/dahdi/x. + Patch by: biohumanoid (closes issue #15252) + +2009-05-27 Shaun Ruffell + + * dahdi-linux version 2.2.0-rc5 released. + +2009-05-27 12:48 +0000 [r6659] Tzafrir Cohen + + * drivers/dahdi/xpp/card_pri.c, drivers/dahdi/xpp/card_bri.c, + drivers/dahdi/xpp/card_fxo.c, drivers/dahdi/xpp/card_fxs.c, + drivers/dahdi/xpp/card_global.c: Fix more 'owner' for 2.6.30 + to be happy. Finishing the work of r6642. Completely shut issue + #14964. + +2009-05-25 08:23 +0000 [r6651-6653] Tzafrir Cohen + + * drivers/dahdi/xpp/firmwares/PIC_TYPE_3.hex, + drivers/dahdi/xpp/firmwares/PIC_TYPE_4.hex, + drivers/dahdi/xpp/firmwares/FPGA_1161.hex, + drivers/dahdi/xpp/firmwares/PIC_TYPE_1.hex, + drivers/dahdi/xpp/firmwares/PIC_TYPE_2.hex: xpp firmwares: fixes + sync issues of FXO module in a BRI/PRI Astribank * Also fixes LED + blinking issues in PRI modules * Various bugfixes in the PICs. + + * drivers/dahdi/xpp/firmwares/USB_FW.hex: USB_FW.hex: Allow setting + caps. even when FPGA is loaded. Note that They will only take + effect after a reset. Firmware rev. 7071. + +2009-05-19 16:09 +0000 [r6635-6642] Tzafrir Cohen + + * drivers/dahdi/xpp/xpp_dahdi.c, drivers/dahdi/xpp/xpp_usb.c, + drivers/dahdi/xpp/xbus-core.c, drivers/dahdi/xpp/xdefs.h: xpp: + 'owner' property of procfs was dropped in 2.6.30. This adds a + compatibility macro for older versions that is a noop for kernels + >= 2.6.30. (closes issue #14964) + + * drivers/dahdi/xpp/xproto.h, drivers/dahdi/xpp/xbus-sysfs.c, + drivers/dahdi/xpp/xbus-core.c, drivers/dahdi/xpp/card_global.c, + drivers/dahdi/xpp/xproto.c, drivers/dahdi/xpp/xbus-core.h, + drivers/dahdi/xpp/xbus-pcm.c: xpp: fix the Astribank state + machine This generally is a case that would not happen in the + wild, though. + + * drivers/dahdi/xpp/card_bri.c, drivers/dahdi/xpp/xdefs.h: xpp: + report in sysfs if bri module uses hardhdlc support + + * drivers/dahdi/xpp/calibrate_slics (removed): xpp: remove obsolete + script calibrate_slics + + * drivers/dahdi/xpp/card_pri.c, drivers/dahdi/xpp/mmapbus.c, + drivers/dahdi/xpp/card_bri.c, drivers/dahdi/xpp/card_fxo.c, + drivers/dahdi/xpp/mmapdrv.c, drivers/dahdi/xpp/xbus-sysfs.c, + drivers/dahdi/xpp/Kbuild, drivers/dahdi/xpp/card_fxs.c, + drivers/dahdi/xpp/xdefs.h: Replaece member bus_id with dev_name() + and set_dev_name() As of 2.6.26 the macros dev_name() and + set_dev_name() are used to read and set (respectively) the bus_id + member in sysfs. As of 2.6.30 bus_id is gone. This patch provides + comaptiobility macros for older kernel versions and removes + direct usage of bus_id. (closes issue #14965) Patches: + xpp_2630_dev_name.diff uploaded by tzafrir (license 46) + + * drivers/dahdi/voicebus, drivers/dahdi/vpmadt032_loader: ignore + generated files in voicebus and vpmadt032_loader + +2009-05-15 23:37 +0000 [r6625-6628] Shaun Ruffell + + * drivers/dahdi/voicebus/GpakCust.c: voicebus: Use '&' not '|' when + checking for a bit. + + * drivers/dahdi/voicebus/GpakCust.c: voicebus: Make the + enable/disable echocan messages debug again. I accidentally + changed to print everytime. They should only be printed if + DEBUG_ECHOCAN is specified in the debug module parameter. + +2009-05-14 14:49 +0000 [r6621] Shaun Ruffell + + * drivers/dahdi/wcte12xp/base.c: wcte12xp: Set the syncsrc in the + span appropriately. Before this commit, dahdi_tool would report + "Internally clocked" for boards supported by the wcte12xp driver + both when receiving timing from the span and providing it to the + span. Now it reports "Internally clocked" if providing timeing to + the span, and the card if receiving timeing from the span. + DAHDI-65. + +2009-05-12 22:30 +0000 [r6606-6610] Kevin P. Fleming + + * drivers/dahdi/Kbuild: use proper case for variable name :-) + + * drivers/dahdi/Kbuild: Allow VPMADT032 and HPEC binary modules to + be used on platforms where ARCH is set to the new 'generic' x86 + flavor available in recent Linux kernel releases + + * drivers/dahdi/Kbuild: emit warning messages when DAHDI is being + built on a CPU architecture that does not support HPEC or the + VPMADT032 firmware loader, so the user will know why they are not + included clean up the conditional logic for these items in the + Kbuild file + + * Makefile: remove another unused variable + +2009-05-11 17:48 +0000 [r6589-6590] Shaun Ruffell + + * dahdi-linux version 2.2.0-rc4 released. + + * drivers/dahdi/wcte12xp/base.c: wcte12xp: Expose vpm parameters as + module parameters. Expose the vpmnlptype, vpmnlpthresh, and + vpmnlpmaxsupp as module parameters like for the wctdm24xxp. + DAHDI-261 + + * drivers/dahdi/wcte12xp/base.c, drivers/dahdi/wctdm24xxp/base.c, + drivers/dahdi/voicebus/GpakApi.h, + drivers/dahdi/voicebus/GpakCust.c, + drivers/dahdi/voicebus/GpakCust.h: voicebus: Update the default + vpmadt032 parameters. Move the echo can channel parameters into a + common location for both the wcte12xp and wctdm24xxp drivers that + use the voicebus module. This is intended to make it clearer + which differences are required between the clients. Additionally, + update the default parameters to the new recommended values. + VPMADT032-37 + +2009-05-07 19:42 +0000 [r6572] Shaun Ruffell + + * drivers/dahdi/wcte12xp/base.c, drivers/dahdi/wctdm24xxp/base.c, + drivers/dahdi/voicebus/GpakCust.c, + drivers/dahdi/voicebus/GpakCust.h: voicebus: Create workqueue for + each vpmadt032 instance. Depending on the system latency, the + deferred work for the vpmadt032 can take up to 200ms. This change + allows each vpmadt032 to use its own workqueue, and not the + global system workqueue. This prevents vpm operations from + blocking the main system workqueue for extended periods. This + restores the behavior to the way it was before the common + vpmadt032 code was moved out of the wctdm24xxp and wcte12xp + drivers. DAHDI-260 voicebus-squash: Adding the wq name. + +2009-05-07 19:42 +0000 [r6568-6572] Shaun Ruffell + + * dahdi-linux version 2.2.0-rc3 released. + + * drivers/dahdi/wcte12xp/base.c, drivers/dahdi/wctdm24xxp/base.c, + drivers/dahdi/voicebus/GpakCust.c, + drivers/dahdi/voicebus/GpakCust.h: voicebus: Create workqueue for + each vpmadt032 instance. Depending on the system latency, the + deferred work for the vpmadt032 can take up to 200ms. This change + allows each vpmadt032 to use its own workqueue, and not the + global system workqueue. This prevents vpm operations from + blocking the main system workqueue for extended periods. This + restores the behavior to the way it was before the common + vpmadt032 code was moved out of the wctdm24xxp and wcte12xp + drivers. + + * drivers/dahdi/wcte12xp/base.c, drivers/dahdi/wctdm24xxp/base.c, + drivers/dahdi/voicebus/GpakCust.h: voicebus: Changing default NLP + type to 'suppression'. The 'suppression' default for the NLP + provides better echo canceling performance. Also ensures that the + wctdm24xxp and wcte12xp driver use the same default values. + +2009-05-05 17:32 +0000 [r6564] Shaun Ruffell + + * drivers/dahdi/voicebus/GpakCust.c: voicebus: Use the companding + type on the span when enabling echocan. + +2009-05-04 20:36 +0000 [r6562] Doug Bailey + + * drivers/dahdi/voicebus/GpakCust.c: Insure that vpmnlptype, + vpmnlpmaxsupp, and vpmnlpthresh are set back to module level + defaults when echo can is freed. (Previously they were zero'd + out) DAHDI-257 + +2009-05-02 07:53 +0000 [r6556] Kevin P. Fleming + + * Makefile: Remove explicit passing of ARCH to kernel build system + There is no value in setting a value for ARCH and passing it to + the kernel build system; the configured kernel headers/sources + already have an architecture specified and can't be used for any + other architecture anyway. + +2009-05-01 16:43 +0000 [r6549-6554] Shaun Ruffell + + * drivers/dahdi/dahdi-base.c: dahdi-base: define + __RW_LOCK_UNLOCKED() Linux 2.6.9 does not contain that + definition, but the older definition is deprecated since it + defeats lock state checking. DAHDI-253 + + * drivers/dahdi/voicebus/Makefile (added): voicebus: Need Makefile + to build on 2.6.9 DAHDI-253 + + * drivers/dahdi/wcb4xxp/base.c: wcb4xxp: Define mmiowb if not + already defined. Linux kernel 2.6.9 does not define mmiowb. + DAHDI-253 + + * drivers/dahdi/wctc4xxp/base.c: wctc4xxp: spin_trylock_irqsave is + not defined on some kernels. DAHDI-253 + + * drivers/dahdi/wctc4xxp/base.c: wctc4xxp: Defined gfp_t for + earlier kernels. This definition was just copied from the xpp + driver. DAHDI-253 + + * drivers/dahdi/wctc4xxp/base.c: wctc4xxp: Fix inclusion of + linux/io.h on 2.6.9 kernels. DAHDI-253 + +2009-04-30 20:59 +0000 [r6544-6546] Kevin P. Fleming + + * include/dahdi/user.h: Fix compilation of applications that use + DAHDI ioctls Defining ioctl codes in this file requires that + linux/ioctl.h be included first. + + * drivers/dahdi/wcte12xp/base.c, drivers/dahdi/wct4xxp/base.c, + drivers/dahdi/wctdm24xxp/base.c: Ensure that vpmsupport=0 module + parameter takes proper effect For these drivers, when the + vpmsupport module parameter is set to zero, don't even register + the span as supporting echo cancellation. DAHDI-250 + +2009-04-30 13:59 +0000 [r6542] Tzafrir Cohen + + * drivers/dahdi/xpp/firmwares/FPGA_1161.hex: xpp: A new FPGA + firmware to hopefully help with BRI/FXO. + +2009-04-29 18:24 +0000 [r6523-6529] Shaun Ruffell + + * dahdi-linux version 2.2.0-rc2 released. + + * drivers/dahdi/dahdi_echocan_kb1.c, drivers/dahdi/wcte12xp/base.c, + include/dahdi/user.h, include/dahdi/kernel.h, + drivers/dahdi/adt_lec.c, drivers/dahdi/dahdi_echocan_jpah.c, + drivers/dahdi/wct4xxp/base.c, drivers/dahdi/ecdis.h, + drivers/dahdi/wctdm24xxp/base.c, + drivers/dahdi/dahdi_echocan_mg2.c, + drivers/dahdi/wcte12xp/wcte12xp.h, + drivers/dahdi/voicebus/GpakCust.c, drivers/dahdi/hpec/hpec.h, + drivers/dahdi/wcb4xxp/wcb4xxp.h, + drivers/dahdi/dahdi_echocan_sec2.c, + drivers/dahdi/voicebus/GpakCust.h, + drivers/dahdi/hpec/dahdi_echocan_hpec.c, + include/dahdi/dahdi_config.h, drivers/dahdi/wcb4xxp/base.c, + drivers/dahdi/wctdm24xxp/wctdm24xxp.h, + drivers/dahdi/dahdi_echocan_oslec.c, drivers/dahdi/dahdi-base.c, + drivers/dahdi/dahdi_echocan_sec.c: echocan: Improve interface for + echo cancelers. Echo cancelers are now able to report if they are + able to automatically disable their NLP portions in the presence + of tones in the audio stream. Also, the interface is changed to + allow user space to just disable the NLP portion of the echo + canceler. These changes improve fax and modem handling in DAHDI. + This commit merges in the changes on + http://svn.digium.com/svn/dahdi/linux/team/kpfleming/echocan_work + Patch by: kpfleming Also contains improvements to CED tone + detection. (closes issue #13286) Reported by: viniciusfontes + + * drivers/dahdi/wct4xxp/base.c: wct4xxp: Fix problem when timing + source is via external cable. + + * drivers/dahdi/wcte12xp/base.c, drivers/dahdi/wcte12xp/voicebus.c + (removed), drivers/dahdi/wcte12xp/vpmadt032.c (removed), + drivers/dahdi/voicebus (added), drivers/dahdi/voicebus/voicebus.c + (added), drivers/dahdi/wctdm24xxp/base.c, drivers/dahdi/Kconfig, + drivers/dahdi/wcte12xp/wcte12xp.h, + drivers/dahdi/wcte12xp/GpakErrs.h (removed), + drivers/dahdi/wctdm24xxp/GpakApi.c (removed), + drivers/dahdi/wcte12xp/vpmadt032.h (removed), + drivers/dahdi/wctdm24xxp/voicebus.c (removed), + drivers/dahdi/voicebus/voicebus.h (added), Makefile, + drivers/dahdi/wctdm24xxp/GpakApi.h (removed), + drivers/dahdi/vpmadt032_loader/dahdi_vpmadt032_loader.c (added), + drivers/dahdi/wcte12xp/Kbuild, drivers/dahdi/voicebus/Kbuild + (added), drivers/dahdi/wctdm24xxp/GpakHpi.h (removed), + drivers/dahdi/wctdm24xxp/Kbuild, drivers/dahdi/voicebus.c + (removed), drivers/dahdi/voicebus.h (removed), + drivers/dahdi/vpmadt032_loader (added), + drivers/dahdi/wcte12xp/GpakApi.c (removed), + drivers/dahdi/wcte12xp/gpakenum.h (removed), + drivers/dahdi/Kbuild, drivers/dahdi/voicebus/gpakenum.h (added), + drivers/dahdi/voicebus/GpakApi.c (added), + drivers/dahdi/adt_lec.c, drivers/dahdi/wcte12xp/GpakApi.h + (removed), drivers/dahdi/voicebus/GpakApi.h (added), + drivers/dahdi/voicebus/GpakCust.c (added), + drivers/dahdi/wctdm24xxp/gpakenum.h (removed), + drivers/dahdi/voicebus/gpakErrs.h (added), + drivers/dahdi/wctdm24xxp/GpakCust.c (removed), + drivers/dahdi/voicebus/GpakCust.h (added), + drivers/dahdi/voicebus/GpakHpi.h (added), + drivers/dahdi/wctdm24xxp/gpakErrs.h (removed), + drivers/dahdi/firmware/Makefile, + drivers/dahdi/voicebus/vpmadtreg.c (added), + drivers/dahdi/wctdm24xxp/GpakCust.h (removed), + drivers/dahdi/voicebus/vpmadtreg.h (added), + drivers/dahdi/wctdm24xxp/wctdm24xxp.h: voicebus: Move common + vpmadt032 interface into voicebus module. The voicebus library + was previously linked into both the wcte12xp and wctdm24xxp + drivers. It is now broken out into it's own module and the common + parts of the vpmadt032 interface are now located in that module + to reduce duplication between the wcte12xp and wctdm24xxp + drivers. + + * drivers/dahdi/wctc4xxp/base.c: wctc4xxp: Change netif_rx_xxx to + napi_xxx The netif_rx_xxx functions were dropped from the linux + kernel source on 2009-01-21 in commit + 288379f050284087578b77e04f040b57db3db3f8. (closes issue #14963) + Reported by: tzafrir + + * drivers/dahdi/wcte12xp/base.c, + drivers/dahdi/wcte12xp/vpmadt032.c, + drivers/dahdi/wcte12xp/wcte12xp.h: wcte12xp: Update cmdqueue + processing. The command queue for reading from the registers on + the framer is now stored in a linked_list instead of an array. + Allows for the locks to protect this structure to be held for + shorter periods of time and reduces the need to cycle through all + the elements in the array to decide if there is a command in the + queue to process. Remove the usecount and dead members from + struct t1 since the module reference count will allow us to know + when it's safe to free up the memory. This change also moves + alarm processing out of the interrupt handler and removes the + need for special interrupt handling of commands. + + * drivers/dahdi/dahdi_dummy.c: dahdi_dummy: Remove real-time clock + support. This removes support for using the real-time clock as a + timing source in dahdi_dummy. Instead, the normal kernel timers + method is now more accurate since it keeps track of how much real + time has passed to determine how many times to call dahdi_receive + and dahdi_transmit. This method was originally suggested by bmd. + (closes issue #13930) (closes issue #14884) Reported by: tzafrir + Tested by: dbackeberg, ask + + * drivers/dahdi/voicebus.c: voicebus: Removing unused code blocks + and space in flag definitions. + +2009-04-27 20:03 +0000 [r6513] Tzafrir Cohen + + * drivers/dahdi/xpp/card_bri.c, drivers/dahdi/xpp/Kbuild: xpp: + hard_hdlc support for the BRI module. The BRI module will now use + hardhdlc unless the DAHDI tree has been patched with the + bri_dchan patch, in which case the old "bristuffed" code will be + used. Thus it is now built by default. + +2009-04-25 16:35 +0000 [r6487] Tzafrir Cohen + + * build_tools/live_dahdi: adapt live_dahdi to current xpp tools * + More varibles to set through the environment * Different way to + list drivers + +2009-04-23 15:11 +0000 [r6457-6466] Tzafrir Cohen + + * drivers/dahdi/xpp/firmwares/PIC_TYPE_3.hex, + drivers/dahdi/xpp/firmwares/PIC_TYPE_4.hex, + drivers/dahdi/xpp/firmwares/USB_FW.hex, + drivers/dahdi/xpp/firmwares/FPGA_1161.hex, + drivers/dahdi/xpp/firmwares/PIC_TYPE_1.hex, + drivers/dahdi/xpp/firmwares/PIC_TYPE_2.hex: Don't set the Id + keyword on firmwares; restore original ID The Id SVN keyword is + set locally in Xorcom and used as an identifier. That Id should + not be overriden by this SVN repository. + + * drivers/dahdi/xpp/firmwares/FPGA_1161.hex: xpp: FPGA_1161.hex: + fix FXO PCM issues (new boards only) FPGA_1161.hex rev. 7024. + +2009-04-22 12:53 +0000 [r6444] Kevin P. Fleming + + * drivers/dahdi/dahdi-base.c: don't refer to macros from + dahdi_config.h until after it has been included use the proper + type for the flags variable in dahdi_ppp_xmit() + +2009-04-21 22:16 +0000 [r6430] Tzafrir Cohen + + * include/dahdi/user.h, include/dahdi/kernel.h: Move + DAHDI_DEFAULT_MTU_MRU from kernel.h to user.h The macro + DAHDI_DEFAULT_MTU_MRU needs to be exposed to userspace to build + tools/ppp/dahdi.c . + +2009-04-20 10:49 +0000 [r6407-6409] Tzafrir Cohen + + * drivers/dahdi/xpp/firmwares/PIC_TYPE_3.hex, + drivers/dahdi/xpp/firmwares/PIC_TYPE_4.hex, + drivers/dahdi/xpp/firmwares/FPGA_1161.hex, + drivers/dahdi/xpp/firmwares/PIC_TYPE_1.hex, + drivers/dahdi/xpp/firmwares/PIC_TYPE_2.hex: xpp firmware: + stability fixes for firmwares of new Astribanks FPGA_1161.hex: + xpp rev 7007 PIC_TYPE_*.hex: xpp rev 7000 + + * drivers/dahdi/xpp/xpp_dahdi.c, drivers/dahdi/xpp/xproto.h, + drivers/dahdi/xpp/card_pri.c, drivers/dahdi/xpp/card_bri.c, + drivers/dahdi/xpp/card_fxo.c, drivers/dahdi/xpp/xpp_dahdi.h, + drivers/dahdi/xpp/xbus-core.c, drivers/dahdi/xpp/card_fxs.c: xpp: + Do use information about number of ports the Astribank provides + + * drivers/dahdi/xpp/init_card_1_30: xpp: Fix FXS calibration (dec + rather than hex) + +2009-04-16 19:35 +0000 [r6376-6393] Tzafrir Cohen + + * drivers/dahdi/xpp/card_fxs.c, drivers/dahdi/xpp/init_card_1_30, + drivers/dahdi/xpp/init_card_2_30: xpp fxs/fxo: PCM and DTMF fixes + + * drivers/dahdi/xpp/card_fxs.c: xpp fxs: Notify the user just one + about wrong VMWI config From xpp rev. 6974. + + * drivers/dahdi/xpp/card_fxs.c: xpp fxs: Ignore registers of + disabled SLICs From xpp rev. 6979 + + * drivers/dahdi/xpp/card_bri.c: xpp bri: explicitly turn off leds + on startup If NT/TE was changed (e.g: happened because of + firmware bug) We would be left with a lit led we don't know + about. From xpp rev. 6990 + +2009-04-10 09:53 +0000 [r6344] Tzafrir Cohen + + * drivers/dahdi/xpp/astribank_hook.sample (removed): Move + astribank_hook from linux to tools. Install it by default + +2009-04-04 14:22 +0000 [r6325] Tzafrir Cohen + + * Makefile: Also install dahdi_config.h to /usr/include/dahdi + +2009-04-02 20:34 +0000 [r6301-6312] Tzafrir Cohen + + * drivers/dahdi/xpp/astribank_hook.sample: Update the sample udev + astribank_hook for TwinStar + + * drivers/dahdi/xpp/firmwares/PIC_TYPE_3.hex (added), + drivers/dahdi/xpp/xpp.rules, + drivers/dahdi/xpp/firmwares/PIC_TYPE_4.hex (added), + drivers/dahdi/xpp/.version, + drivers/dahdi/xpp/firmwares/FPGA_1161.hex (added), + drivers/dahdi/xpp/firmwares/USB_FW.hex, + drivers/dahdi/xpp/firmwares/Makefile, + drivers/dahdi/xpp/firmwares/PIC_TYPE_1.hex (added), + drivers/dahdi/xpp/firmwares/PIC_TYPE_2.hex (added): XPP: support + for 116x Astribanks. * New software to load in the udev rules * + New control protocol ("MPP") * More modular FPGA firmware From + Xorcom rev. 6963. + + * drivers/dahdi/xpp/xbus-pcm.c: Reduce the rate for a potentially + annoying message This message is used when an xpp span is a DAHDI + sync master but also set to take timing from the DAHDI master. + This means wrong settings: user is wasting CPU cycles. However + notifying the user about it every second is still too much. + +2009-04-02 17:27 +0000 [r6285-6294] Kevin P. Fleming + + * drivers/dahdi/dahdi-base.c: ensure that the structure being + returned by DAHDI_GET_BUFINFO is completely initialized + +2009-03-26 18:33 +0000 [r6262] Wendell Thompson + + * drivers/dahdi/voicebus.c: Fixes DAHDI-214 crash on driver unload. + Affects wcte12xp and wctdm24xxp modules. + +2009-03-24 19:08 +0000 [r6237-6246] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: Eliminate unnecessary checks for + NULL before freeing memory. + + * drivers/dahdi/wctc4xxp/base.c: Do not allocate more memory than + is needed when sending packets. + + * drivers/dahdi/wctc4xxp/base.c: Block runt packets from the + transcoder. Tested by: Walter Klomp + + * drivers/dahdi/wctc4xxp/base.c: Do not define the debug flags if + CONFIG_SLUB is not set. Some versions of the kernel + (2.6.18-92.1.22.el5) have patches in them to panic if a slab + cache is created with unsupported flags. + +2009-03-24 15:59 +0000 [r6236] Tzafrir Cohen + + * drivers/dahdi/dahdi-base.c: Make sure the requested echo + canceller name is NULL-terminated. Make sure that the that the + name of the echo canceller requested in the DAHDI ioctl + DAHDI_ATTACH_ECHOCAN is NULL-terminated. + +2009-03-23 23:49 +0000 [r6217-6228] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: Verify checksum on the RTP IP + header before queueing. + + * drivers/dahdi/wctc4xxp/base.c: Poll the card in a kernel timer + when several channels are open. Polling the driver increases + overall system throughput when there are several transcoding + channels open by reducing the number of interrupts the the TC400M + generates. + + * drivers/dahdi/wctc4xxp/base.c: Optionally show total number of + packets dropped when channel is closed. + + * drivers/dahdi/wctc4xxp/base.c: Do not handle duplicate reponses. + It is possible for system activity to prevent the wctc4xxp driver + from responding the the firmware on the TC400M for a period of + time. If this occurs, the TC400M will resend a response to a + command that we sent. This duplicate reponse will then sometimes + confuse the driver. Normally this would manifest as an attempt to + connect the same channel together in a transcoding session. + + * drivers/dahdi/wctc4xxp/base.c: Set TX_COMPLETE atomically with + changes to the waiting_for_response_list. This change is to catch + a condition where it is possible, for whatever reason, for a + response to come in before the request is marked tx complete. If + this happened, it was possible to leak the response packet and + double complete the command. + + * drivers/dahdi/wctc4xxp/base.c: split send_trans_connect into a + connect / disconnect pair. Trivial change that makes the code + read more naturally. Also changes the order of members in the + channel_pvt structure. For more natural alignment. Both + non-functional changes. + + * drivers/dahdi/wctc4xxp/base.c: If the driver fails to register, + make sure we cleanup the command cache. + + * drivers/dahdi/wctc4xxp/base.c: Setup the TC400M to poll the own + bit on the descriptor ring. On certain systems having the + hardware poll the descriptor ring provides more reliable + operation that strobbing the transmit demand poll and receive + demand poll register. + + * drivers/dahdi/wctc4xxp/base.c: Make sure that messages sitting in + the outbound queue cause the timer to reschedule. + + * drivers/dahdi/wctc4xxp/base.c: Refactor channel command handling. + Makes commands and reponses clearer and easier to trouble shoot, + reduces pressure on the stack, and brings driver closer to kernel + coding standards. + +2009-03-18 18:48 +0000 [r6201] Jason Parker + + * drivers/dahdi/dahdi_dynamic_loc.c: Fix a typo + +2009-03-17 17:59 +0000 [r6170-6191] Tzafrir Cohen + + * drivers/dahdi/xpp/firmwares/USB_FW.hex: xpp USB_FW.hex rev 6885: + fixes reading label from USB Previous USB_FW.hex, 6770, has + failed to properly read the USB iSerial field ("Label" in xpp + terms). This is fixed here. + + * include/dahdi/dahdi_config.h (added), include/dahdi/kernel.h, + drivers/dahdi/xpp/Kbuild, drivers/dahdi/dahdi-base.c, + drivers/dahdi/dahdi_config.h (removed): Move dahdi_config.h to be + under include/dahdi/ This makes dahdi_config.h part of the + "offcial" interface to external modules. Practically most of its + configuration items are internal to dahdi-base. But some are + intended for other drivers. + + * drivers/dahdi/biquad.h, include/dahdi/kernel.h, + drivers/dahdi/ecdis.h, drivers/dahdi/dahdi-base.c: Make ecdis.h + used by dahdi-base.h alone ecdis.h is no longer #include-d in + kernel.h . it was there because of decleration of some structs + that are part of struct dahdi_chan. The declerations of those + structs were moved into kernel.h directly. + +2009-03-13 10:21 +0000 [r6134-6147] Tzafrir Cohen + + * drivers/dahdi/dahdi-base.c: Fix handling of 'w' in a pulse dial + string Make the special "digit" 'w' work in pulse dialing as it + works with tone dialing (a delay of 0.5 second till the next + digit). Note that the digit gets uppercased before it gets to + this function. (closes issue #13999) Reported by: IgorG Patches: + dahdi-base.c.pulse2.diff uploaded by tzafrir (license 46) Tested + by: litnimax + + * drivers/dahdi/xpp/xpp.conf: A better sample xpp.conf + + * drivers/dahdi/xpp/init_card_3_30, + drivers/dahdi/xpp/init_card_4_30, + drivers/dahdi/xpp/init_card_1_30, + drivers/dahdi/xpp/init_card_2_30: xpp init_card_* scripts now + less verbose * Demote some messages to be debug messages. * + Rephrase the message about defaults for the PRI module (the + driver's defaults are used, which is OK) + + * drivers/dahdi/dahdi-base.c: Fix handling of DAHDI_GETGAINS_V1 + missing from r6124 . + +2009-03-11 14:51 +0000 [r6113-6126] Shaun Ruffell + + * drivers/dahdi/voicebus.c: Allow 10ms for voicebus hardware to + settle after reset. The voicebus hardware needs more time to + settle after a reset. The short settle time explains why there + was frequently one IRQ miss reported in the proc file for the + spans. Reported by: jsloan + + * drivers/dahdi/wct4xxp/base.c: Relax ident wheel requirements. Do + not require the first card to be set to 0 and allow skips in the + ident wheel numbers. The ident wheel allows a user to determine + the order that cards register there spans with DAHDI. (closes + issue #13078) Reported by: opticron Patch by: opticron + + * include/dahdi/user.h, drivers/dahdi/dahdi-base.c: Fix direction + bits on several ioctls. (related to issue #14499) Reported by: ys + + * include/dahdi/kernel.h, drivers/dahdi/dahdi_transcode.c: Keep + transcoders on a list in registration order. This fixes a bug + where it was possible for there to be a transcoder in position + "1" but not in position "0" if a transcoder hardware driver was + loaded, unloaded, and reloaded again without also reloading + dahdi_transcode. The result is that codec_dahdi fails to + enumerate all the transcoders in the system. (closes issue + #14627) Reported by: xblurone + + * drivers/dahdi/dahdi-base.c: Eliminating an unused parameter to + dahdi_specchan_open. + +2009-03-06 21:43 +0000 [r6096] Wendell Thompson + + * drivers/dahdi/xpp/xpp_usb.c, drivers/dahdi/wctc4xxp/base.c, + drivers/dahdi/voicebus.c: Workarounds for SLUB sysfs problems in + kernel 2.6.22 with CONFIG_SLUB and CONFIG_SLUB_DEBUG, as in + Fedora Core 6. Fixes kernel oops when loading/unloading dahdi + modules. DAHDI-226 + +2009-03-05 18:53 +0000 [r6079] Tzafrir Cohen + + * drivers/dahdi/xpp/firmwares/FPGA_1141.hex, + drivers/dahdi/xpp/firmwares/FPGA_1151.hex, + drivers/dahdi/xpp/firmwares/FPGA_FXS.hex: xpp: FPGA firmwares + 6799 New FPGA firmware with a number of bug fixes. + +2009-03-01 13:56 +0000 [r6046] Tzafrir Cohen + + * drivers/dahdi/xpp/init_card_3_30, drivers/dahdi/xpp/xpp_dahdi.c, + drivers/dahdi/xpp/xproto.h, drivers/dahdi/xpp/.version, + drivers/dahdi/xpp/card_fxo.c, drivers/dahdi/xpp/xpp_dahdi.h, + drivers/dahdi/xpp/firmwares/USB_FW.hex, + drivers/dahdi/xpp/xbus-sysfs.c, drivers/dahdi/xpp/xframe_queue.c, + drivers/dahdi/xpp/xbus-core.c, drivers/dahdi/xpp/card_global.c, + drivers/dahdi/xpp/xbus-core.h, drivers/dahdi/xpp/Changelog_xpp, + drivers/dahdi/xpp/card_global.h, drivers/dahdi/xpp/xbus-pcm.c: + New XPP code: xpp rev 6795: * Fix cases where the command_queue + overflowed during initialization. - Also add a + 'command_queue_length' parameter to xpp.ko * More migrations to + sysfs: - Add a 'transport' attribute to our astribank devices + which points to the usb device we use. E.g: + /sys/bus/astribanks/devices/xbus-00/transport is symlinked to + ../../../../../../devices/pci0000:00/0000:00:10.4/usb5/5-4 - Move + /proc/xpp/XBUS-??/XPD-??/span to + /sys/bus/xpds/devices/??:?:?/span - Migrate from /proc/xpp/sync + to: /sys/bus/astribanks/drivers/xppdrv/sync - New 'offhook' + attribute in: /sys/bus/xpds/devices/??:?:?/offhook * PRI: change + the "timing" priority to match the convention used by other PRI + cards -- I.e: lower numbers (not 0) have higher priority. * FXO: + - Power denial: create two module parameters instead of + hard-coded constants (power_denial_safezone, + power_denial_minlen). For sites that get non-standard + power-denial signals from central office on offhook. - Don't + hangup on power-denial, just notify Dahdi and wait for - Fix + caller-id detection for the case central office sends it before + first ring without any indication before. Asterisk's desicion. * + USB_FW.hex: - Fixes cases where firmware loading would fail. + +2009-02-11 05:41 +0000 [r6005] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: Use the proper pci_device when + handling dma buffers. + +2009-02-10 14:07 +0000 [r5997] Matthew Fredrickson + + * drivers/dahdi/wcb4xxp/base.c: Set default alarm debounce time to + 500ms to debounce NT L1 deactivations + +2009-02-09 06:03 +0000 [r5987] Tzafrir Cohen + + * drivers/dahdi/xpp/xproto.c: Fix building DAHDI with module + unloading disabled As moduel_refcount is only used for debugging, + disable it in this non-common case. (Closes issue #14402) + +2009-02-02 14:13 +0000 [r5936] Kevin P. Fleming + + * drivers/dahdi/dahdi_echocan_kb1.c, + drivers/dahdi/hpec/dahdi_echocan_hpec.c, include/dahdi/kernel.h, + drivers/dahdi/dahdi_echocan_jpah.c, + drivers/dahdi/dahdi_echocan_mg2.c, + drivers/dahdi/dahdi_echocan_oslec.c, drivers/dahdi/dahdi-base.c, + drivers/dahdi/dahdi_echocan_sec.c, drivers/dahdi/hpec/hpec.h, + drivers/dahdi/dahdi_echocan_sec2.c: Array-style echo canceller + updates first appeared in Zaptel, because HPEC only supports that + mode. However, when the function for doing array-style updates + was written, the argument names were reversed. In Zaptel this did + no harm, because HPEC was the only module that used array-style + updates. When DAHDI was created, non-array-style updates were + removed, and the existing modules were converted to using + array-style updates. Unfortunately the new code was written based + on the argument names, which were incorrect. This caused all the + echo cancellers to be broken (except HPEC, although we did not + know that at the time), and it was corrected by reversing the + order of the arguments passed when the array-style update + function was called (leading to a confusing mismatch). This fixed + all the non-HPEC modules, but left HPEC broken, which was just + discovered. This commit corrects all these problems, so that the + argument names and the data passed actually make sense, and all + the modules work properly. + +2009-01-30 23:42 +0000 [r5924] Matthew Fredrickson + + * drivers/dahdi/wcb4xxp/base.c: Make sure that we pass alarm + notification up the stack whenever alarms occur on the B410P + +2009-01-30 16:53 +0000 [r5916] Mike Spiceland + + * drivers/dahdi/wctdm24xxp/base.c: Do a stricter test for FXS + modules. FXO modules will be hi-z during this time and the value + will be undefined. This test ensures that FXO modules will not + falsely trigger during FXS probes. The value of 0x88 from + register 1 has been confirmed during this stage on quad and + single port modules. + +2009-01-28 23:17 +0000 [r5895] Richard Mudgett + + * include/dahdi/user.h: Minor comment rearangement to avoid + possible confusion. + +2009-01-28 04:41 +0000 [r5870] Shaun Ruffell + + * drivers/dahdi/wcb4xxp/base.c: Ensure the teignorered parameter is + exposed as a module parameter. Related to issue #14031 . + +2009-01-27 Shaun Ruffell + + * dahdi-linux version 2.1.0.4 released. + + * Fix for a kernel panic regression when heavily using pseudo + channels (issue #14183) (merged r5811 and r5819 from the trunk) + + * Fix the safety check in tor2 to be for SPANS_PER_CARD + (issue #13954) (merged r5590 from the trunk). + +2008-12-17 Shaun Ruffell + + * dahdi-linux version 2.1.0.3 released. + +2008-12-17 15:57 +0000 [r5535-5576] Shaun Ruffell + + * drivers/dahdi/wcb4xxp/base.c: Do not propogate received + HDLC frames on channels that are not configured. Issue: DAHDI-217 + + * drivers/dahdi/wcb4xxp/base.c: Use %p to print out pointer values. + + * drivers/dahdi/wcb4xxp/base.c: Remove an endless while loop. + + * drivers/dahdi/wcte12xp/base.c: Quiet some warnings about + possible use of uninitialized variables. + + * build_tools/live_dahdi: live_dahdi: Use the nonrelative path for + the dahdi-tools folder in live_dahdi. + +2008-12-15 Shaun Ruffell + + * dahdi-linux version 2.1.0.2 released. + +2008-12-15 20:31 +0000 [r5534-5535] Shaun Ruffell + + * build_tools/live_dahdi: live_dahdi: Use the nonrelative path for + the dahdi-tools folder in live_dahdi. + + * drivers/dahdi/wcte12xp/base.c: wcte12xp: do not release a + spinlock that we did not acquire. + +2008-12-12 14:32 +0000 [r5523] Kevin P. Fleming + + * drivers/dahdi/dahdi-base.c: use a format string for + request_module, so the compiler will do the right thing + +2008-12-11 Shaun Ruffell + + * dahdi-linux version 2.1.0.1 released. + +2008-12-11 21:46 +0000 [r5509] Shaun Ruffell + + * include/dahdi/kernel.h: Add definition of dev_notice for kernels + < 2.6.17. + +2008-12-11 21:03 +0000 [r5498-5504] Tzafrir Cohen + + * drivers/dahdi/xpp/xbus-sysfs.c: One more place whe + old-but-not-ancient hotplug is used. + + * drivers/dahdi/dahdi_echocan_oslec.c: Send all samples to OSLEC, + rather than just the first. (closes issue #14036) Reported by: + marcotasto Patches: dahdi_echocan_oslec.patch uploaded by + marcotasto (license 635) + + * drivers/dahdi/xpp/xbus-sysfs.c, drivers/dahdi/xpp/xbus-core.h, + drivers/dahdi/xpp/xdefs.h: Add a separate case for hotplug of + kernels <= 2.6.9 . + +2008-12-11 20:19 +0000 [r5497] Shaun Ruffell + + * drivers/dahdi/dahdi_dummy.c: If we're using the + system tick, don't use the hrtimer interface. + +2008-12-11 18:57 +0000 [r5482-5492] Tzafrir Cohen + + * drivers/dahdi/xpp/xbus-sysfs.c: Fix the xpp OLD_HOTPLUG fix. + + * drivers/dahdi/dahdi_dummy.c: Fix building dahdi_dummy for kernels + 2.6.13, 2.6.14: Those kernels don't have RTC yet. + + * drivers/dahdi/wcb4xxp: Yet Another directory in which to ignore + modules.order + + * drivers/dahdi/wcb4xxp/base.c: Remove an include that is not + available before 2.6.15 and is not needed + + * drivers/dahdi/xpp/xbus-sysfs.c, drivers/dahdi/xpp/xbus-core.h: A + bit less maigc with OLD_HOTPLUG_SUPPORT + + * drivers/dahdi/dahdi-base.c: Fixed a typo that broke building + dahdi-base with kernels < 2.6.13 + +2008-12-11 16:43 +0000 [r5481] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: Fix compilation issues + on 2.6.15 and below kernels. Thanks tzafrir. + +2008-12-09 Shaun Ruffell + + * dahdi-linux version 2.1.0 released. + +2008-12-09 18:49 +0000 [r5453] Tzafrir Cohen + + * live_dahdi (added): live_dahdi - test dahdi without fully + installing it. + +2008-12-06 22:23 +0000 [r5444] Tzafrir Cohen + + * drivers/dahdi/xpp, drivers/dahdi/wcte12xp, drivers/dahdi, + drivers/dahdi/wct4xxp, drivers/dahdi/wctc4xxp, + drivers/dahdi/wctdm24xxp: Ignore modules.order in modules + directories. + +2008-12-04 20:57 +0000 [r5433-5434] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: Fix an erroneous warning and + ensure that the sample size is set correctly when decoding G723 + packets. Issue: DAHDI-198 + + * drivers/dahdi/dahdi_transcode.c: Do not use an already built + channel if the source and destination formats do not match the + formats we want. This fixes a regression introduced by the new + transcoder interface where a translation path from one complex + codec to another can result in garbled audio. + +2008-12-01 17:58 +0000 [r5413-5420] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: Service the transmit descriptor + ring before the receive descriptor ring so that commands that are + still sitting on the transmit descriptor ring are not completed + twice. + +2008-11-27 09:59 +0000 [r5397-5403] Tzafrir Cohen + + * Makefile: dahdi-linux: Remove README.Astribank.html generation. + + * drivers/dahdi/xpp/README.Astribank (removed): Moving + README.Astribank to dahdi-tools . + + * drivers/dahdi/xpp/README.Astribank: Fixes and some work in + progress on DAHDI Astribank README. + +2008-11-25 20:00 +0000 [r5384] Shaun Ruffell + + * drivers/dahdi/wcte11xp.c, drivers/dahdi/wct1xxp.c: Validate the + timing priority on the wcte11xp and wct1xxp driver. + +2008-11-25 Shaun Ruffell + + * dahdi-linux version 2.1.0-rc5 released. + +2008-11-25 20:00 +0000 [r5383-5384] Shaun Ruffell + + * drivers/dahdi/wcte11xp.c, drivers/dahdi/wct1xxp.c: Validate the + timing priority on the wcte11xp and wct1xxp driver. + + * drivers/dahdi/tor2.c: Validate that the span priority is valid in + the tor2 driver. Patch provided by tzafrir. + +2008-11-24 05:32 +0000 [r5367-5374] Shaun Ruffell + + * drivers/dahdi/wcb4xxp/base.c: Additional debugging code. Patch + provided by akohlsmith. Issue DAHDI-173. + + * drivers/dahdi/wcb4xxp/base.c: Add a 'spanfilter' module parameter + in order to isolate debugging information to just the spans of + interest. Patch provided by akohlsmith. Issue DAHDI-173. + + * drivers/dahdi/wcb4xxp/base.c, drivers/dahdi/wcb4xxp/wcb4xxp.h: + Use counter of pending HDLC frames in order to eliminate the need + to send a zero-byte frame to kick start the transmission process. + Patch provided by akohlsmith. Issue DAHDI-173. + + * drivers/dahdi/wcb4xxp/base.c, drivers/dahdi/wcb4xxp/wcb4xxp.h: Do + not make assumptions about the number of ready HDLC frames on + HDLC RX interrupt. This prevents libpri from becoming confused + when many HDLC frames arrive before the driver can service them + or a false RX interrupt is received. Patch provided by akolsmith. + Issue DAHDI-173. + +2008-11-21 20:15 +0000 [r5360] Jason Parker + + * drivers/dahdi/dahdi-base.c: Fix a think-o in numeric comparison. + Swap order to make it more clear. (closes issue #13813) Reported + by: ys + +2008-11-21 04:42 +0000 [r5350-5355] Shaun Ruffell + + * drivers/dahdi/dahdi-base.c: Convert some uses of sprintf to + snprintf in dahdi_proc_read in order to eliminate a buffer + overrun. Issue: DAHDI-209 + + * drivers/dahdi/wcb4xxp/base.c: Force the FIFO to reset when the + file handle is closed. Patch provided by akohlsmith. Issue: + DAHDI-178 + +2008-11-20 12:31 +0000 [r5340-5345] Tzafrir Cohen + + * drivers/dahdi/tor2.c: Make tor2 load properly. Seems to fix + #13487. Thanks to heyuqi for the testing. + + * drivers/dahdi/xpp/README.Astribank: * Some extrra Zap->DAHDI (and + Dahdi->DAHDI) fixes in the Astribank README. * Some extra + asciidoc formatting fixes. + +2008-11-19 21:25 +0000 [r5335] Kevin P. Fleming + + * drivers/dahdi/wcb4xxp/Makefile (added): support wcb4xxp build on + kernels that don't directly use Kbuild + +2008-11-17 18:17 +0000 [r5321] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: - Fix for race condition of + encoder and decoder are allocated at the same time in the driver. + This would result in -EBUSY returns from the DAHDI_TC_ALLOCATE + ioctl. - Increase the length of the receive descriptor ring from + 8 to 32 to reduce the probability of running out of receive + descriptors. + +2008-11-17 Shaun Ruffell + + * dahdi-linux version 2.1.0-rc4 released. + +2008-11-17 18:17 +0000 [r5321] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: - Fix for race condition of + encoder and decoder are allocated at the same time in the driver. + This would result in -EBUSY returns from the DAHDI_TC_ALLOCATE + ioctl. - Increase the length of the receive descriptor ring from + 8 to 32 to reduce the probability of running out of receive + descriptors. + +2008-11-17 18:01 +0000 [r5320] Tzafrir Cohen + + * drivers/dahdi/xpp/init_card_4_30, drivers/dahdi/xpp/card_pri.c: + xpp_pri: Fix T1 CRC initialization Clock synchronization when + sync is not from first port. + +2008-11-17 17:44 +0000 [r5315] Shaun Ruffell + + * drivers/dahdi/wcb4xxp/base.c, drivers/dahdi/wcb4xxp/wcb4xxp.h: A + fix for an issue with corruption on the D-Channels. Patch + provided by akohlsmith. Issue: DAHDI-173. + +2008-11-16 19:30 +0000 [r5310] Tzafrir Cohen + + * drivers/dahdi/xpp/firmwares/USB_FW.hex: Fixed USB firmware that + caused some bad bioses to hang on boot. The BIOSes included in a + number of motherboards could hang from an Astribank firmware + newer than 1.2.20.1 / 1.4.5.1 . This was due to an some incorrect + USB information in the firmware. This firmware fixes it. Merged + Zaptel revisions 4580 via svnmerge from + http://svn.digium.com/svn/zaptel/branches/1.2 + +2008-11-13 22:10 +0000 [r5303] Shaun Ruffell + + * drivers/dahdi/wcte12xp/base.c, drivers/dahdi/wctdm24xxp/base.c, + drivers/dahdi/voicebus.c, drivers/dahdi/voicebus.h: Make a + message about the host cacheline size being unsupported a debug + only message. The voicebus interface still works whether the + cacheline size is supported or not, but the message is confusing + to users. + +2008-11-10 20:37 +0000 [r5288] Tzafrir Cohen + + * drivers/dahdi/xpp/init_card_2_30: xpp init_card_2_30: no need to + check environment in verify mode. Fixes #13832 in Zaptel. + +2008-11-10 Shaun Ruffell + + * dahdi-linux version 2.1.0-rc3 released. + +2008-11-10 19:48 +0000 [r5275] Shaun Ruffell + + * drivers/dahdi/wcte12xp/base.c, drivers/dahdi/dahdi-base.c: - Do + not hold any locks while calling close_channel, which can result + in calls to the echocan modules which do not necesarrily assume + they are being called in atomic context. - Remove the bigzaplock + around calls to psuedo_alloc and pseudo_free. The structures + protected by this lock are already protected by the chan_lock in + these two cases. - Remove calls to in_atomic() that were + previously added to work around this, but did not cover all the + cases. Issue: DAHDI-195, DAHDI-170 + +2008-11-09 00:33 +0000 [r5269-5270] Sean Bright + + * drivers/dahdi/dahdi-base.c: We only use print_debug_writebuf when + CONFIG_DAHDI_NET or CONFIG_DAHDI_PPP are defined, so only define + it in those cases as well. Reported & Tested by: KP7 via + #asterisk-dev + + * drivers/dahdi/dahdi-base.c: Add missing semi-colon. Reported & + Tested by: KP7 via #asterisk-dev + +2008-11-05 23:45 +0000 [r5249-5257] Shaun Ruffell + + * drivers/dahdi/wcb4xxp/base.c: Whitespace changes for the coding + standard. + + * drivers/dahdi/wcb4xxp/base.c: rate limiting a diagnostic printk. + +2008-11-05 Shaun Ruffell + + * dahdi-linux version 2.1.0-rc2 released. + +2008-11-05 20:17 +0000 [r5230-5237] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: Fix warning messages in order to + build on 2.6.27. (Closes issue #13757) Patch provided by tzafrir. + + * drivers/dahdi/wcte12xp/vpmadt032.c, + drivers/dahdi/wctdm24xxp/GpakCust.c: Check the return value of + the down_interruptible call in order to quiet a warning. This + semaphore does not protect any host data structures, but only + accesses to the VPMADT032 module. The worse thing that could + happen is that the internal state of the VPM module is corrupted, + and then would only happen on module loading because otherwise + access because that is the only time this function is called in + the context of a user process. In this case, the module would + need to be reloaded again anyway. (Closes issue #13742) Reported + by smurfix + + * drivers/dahdi/wcb4xxp/base.c: Remove the loopback module + parameter since it's not implemented yet. + + * drivers/dahdi/wcb4xxp/base.c: Allow the wcb4xxp to take all the + signalling types. + +2008-11-03 Shaun Ruffell + + * dahdi-linux version 2.1.0-rc1 released. + +2008-11-03 12:01 +0000 [r5203-5211] Tzafrir Cohen + + * drivers/dahdi/dahdi_dummy.c: Adjust DAHDI to the new timers + interface of kernel 2.6.28 + + * drivers/dahdi/dahdi-base.c: dahdi-base: hw_echocancel_off: return + 0 if no hardware EC If the span has no hardware EC, return 0, + rather than a random uninitialized value (which was no harm, as + that return value is always ignored anyway). + + * README: README: no need to generate Kbuild for OSLEC. No need to + create a Kbuild file. staging/echo has a Makefile that works fine + for us. + +2008-10-31 22:23 +0000 [r5196] Shaun Ruffell + + * drivers/dahdi/wcb4xxp/base.c: Changing the spantype to indicate + whether it is a TE or NT BRI port. + +2008-10-31 21:33 +0000 [r5195] Matthew Fredrickson + + * drivers/dahdi/wcb4xxp/base.c: Fix the FIFO configuration to use + the data from the EC correctly + +2008-10-31 21:05 +0000 [r5190-5191] Tzafrir Cohen + + * drivers/dahdi/wcb4xxp/base.c: wcb4xxp: Set the spantype to "BRI" + (to show e.g. in dahdi_scan) + + * drivers/dahdi/wcb4xxp/base.c: wcb4xxp: Claim to provide DACS for + dahdi_scan dahdi_scan can tell that a span is digital if its + first channel has DACS signalling capability. While this is + probably not supported by the current driver, it is also + harmless. Without this, dahdi_scan shows the spans of this card + as analog. (Done after consulting with sruffel) + +2008-10-31 17:11 +0000 [r5186] Shaun Ruffell + + * drivers/dahdi/wcb4xxp/base.c: Fix for a case where a span might + not always come back up after a disconnect. Patch provided by + akohlsmith. Issue: DAHDI-174 + +2008-10-30 19:16 +0000 [r5179-5180] Tzafrir Cohen + + * drivers/dahdi/dahdi-base.c: Use correct length for the, well, + dahdi transcoder device name. + + * drivers/dahdi/dahdi-base.c: Use ! to hint udev about directory + separator. Udev knows how to convert a '!' in the device name to + a directory separator. Thus the name 'dahdi!ctl' will create the + device /dev/dahdi/ctl . We still keep older udev rules in this + release for compatibility, but eventually we'll only need them to + set permissions. + +2008-10-29 16:48 +0000 [r5171-5175] Shaun Ruffell + + * drivers/dahdi/wcb4xxp/base.c: The /proc/wcb4xxp should not be + created once for each card but rather once for all cards. + + * drivers/dahdi/wcb4xxp/base.c: By default, do not create the + procfs entry for the wcb4xxp driver. + +2008-10-29 15:09 +0000 [r5167] Tzafrir Cohen + + * drivers/dahdi/xpp/xbus-core.c: xpp: Increase the maximal size of + the command queue to 500 . Temporarily increase the maximal size + of the command queue from 300 to 500 as a workaround of an issue + at initialization time (mainly of BRI+FXS). + +2008-10-28 21:59 +0000 [r5156-5163] Kevin P. Fleming + + * drivers/dahdi: update to latest octasic_api tag with NULL + definition fix + + * drivers/dahdi/tor2.c, drivers/dahdi/wcfxo.c, + drivers/dahdi/wcte12xp/base.c, drivers/dahdi/wcte12xp/GpakApi.c, + drivers/dahdi/pciradio.c, drivers/dahdi/wct4xxp/base.c, + drivers/dahdi/wcte12xp/vpmadt032.c, + drivers/dahdi/wctc4xxp/base.c, drivers/dahdi/dahdi_echocan_mg2.c, + drivers/dahdi/wctdm24xxp/base.c, + drivers/dahdi/wctdm24xxp/GpakApi.c, + drivers/dahdi/wcte12xp/vpmadt032.h, + drivers/dahdi/wctdm24xxp/GpakCust.c, + drivers/dahdi/wcb4xxp/base.c, drivers/dahdi/dahdi_dynamic_loc.c, + drivers/dahdi/dahdi_transcode.c, drivers/dahdi/wcte11xp.c, + drivers/dahdi/dahdi_dynamic.c, drivers/dahdi/dahdi_dynamic_eth.c, + drivers/dahdi/wct1xxp.c, drivers/dahdi/wctdm.c, + drivers/dahdi/wcb4xxp, drivers/dahdi/voicebus.c, + drivers/dahdi/dahdi-base.c, drivers/dahdi/dahdi_dummy.c: fix a + large number of warnings found by sparse, the kernel code sanity + checking tool. some of these fixes are non-optimal (casting + 'unsigned long' to '__user void *'), but are unavoidable in many + cases. started from tzafrir's patch, did most of the work myself. + (closes issue #13763) Reported by: tzafrir Patches: + sparse_fixes_1.diff uploaded by tzafrir (license 46) + + * README: various cleanups, primarily proper capitalization + +2008-10-28 18:26 +0000 [r5148-5150] Shaun Ruffell + + * drivers/dahdi/wcb4xxp/Kbuild (added), drivers/dahdi/Kbuild, + drivers/dahdi/wcb4xxp/base.c (added), drivers/dahdi/wcb4xxp + (added), drivers/dahdi/wcb4xxp/wcb4xxp.h (added), README: Adding + the wcb4xxp driver, a native dahdi driver for the B410P module. + +2008-10-27 19:27 +0000 [r5127-5138] Tzafrir Cohen + + * README: dahdi linux README: Clarify OSLEC EC build procedure. + + * drivers/dahdi/xpp/card_fxo.c: xpp fxo: Add sysfs battery + attribute. + + * drivers/dahdi/xpp/xpp_dahdi.c, drivers/dahdi/xpp/xbus-core.c, + drivers/dahdi/xpp/xbus-core.h: xpp: remove an unused manual + reference count field. + + * drivers/dahdi/xpp/xpp_dahdi.c, drivers/dahdi/xpp/xbus-sysfs.c: + xpp: Make some definitions static, as per sparse. Fixes the xpp + warnings of #13763 (except some false alarms). + + * drivers/dahdi/xpp/card_pri.c, drivers/dahdi/xpp/card_bri.c, + drivers/dahdi/xpp/card_fxo.c, drivers/dahdi/xpp/xbus-sysfs.c, + drivers/dahdi/xpp/card_fxs.c, drivers/dahdi/xpp/xbus-core.h, + drivers/dahdi/xpp/xdefs.h: Fix xpp compile problems on kernel < + 2.6.16 Support for kernels that use the older hotplug support + rather than the newer uevent. Fixes the xpp issue from #13427. + + * include/dahdi/kernel.h: kernel.h: cleanup DAHDI_FLAG_* defines, + no functional change. Define the DAHDI_FLAG_* using the + DAHDI_FLAGBIT_* enum values. + +2008-10-22 18:49 +0000 [r5121-5124] Jason Parker + + * build_tools/genudevrules: Need to make sure we check for udevadm + in addition to udevinfo. Some silly person (or people) decided + that it wasn't useful to have in their distro... *cough* + + * drivers/dahdi/wctdm.c: Fix building on big endian machines. + (closes issue #13754) Reported by: shrift Patches: + wctdm-powerpc.patch uploaded by irroot (license 52) Tested by: + shrift + +2008-10-22 11:44 +0000 [r5105-5118] Tzafrir Cohen + + * drivers/dahdi/wct1xxp.c: wct1xxp: fix error handling at device + startup. And also give more useful error messages if things go + bad. (closes issue #13607) Patches: wct1xxp_pci.diff uploaded by + tzafrir (license 46) Tested by: klaus3000 + + * drivers/dahdi/xpp/init_card_1_30: xpp FXS init script: Do use + high-pass filter. + + * drivers/dahdi/Kbuild, drivers/dahdi/dahdi_echocan_oslec.c + (added), README: An experimental OSLEC echocan module. + + * /: dahdi-trunk: ignore the generated README.html . + + * README: dahdi-linux README: better place for build requirements. + (We'll have to put a content there one of these days) + + * README: Fix headers numbering. + +2008-10-16 17:40 +0000 [r5097] Tzafrir Cohen + + * drivers/dahdi/xpp/xpp.rules, drivers/dahdi/xpp/xpp_usb.c, + drivers/dahdi/xpp/card_pri.c, drivers/dahdi/xpp/card_fxo.c, + drivers/dahdi/xpp/xframe_queue.c, drivers/dahdi/xpp/xbus-sysfs.c, + drivers/dahdi/xpp/Kbuild, drivers/dahdi/xpp/card_fxs.c, + drivers/dahdi/xpp/card_global.c, drivers/dahdi/xpp/xproto.c, + drivers/dahdi/xpp/xframe_queue.h, + drivers/dahdi/xpp/astribank_hook.sample (added), + drivers/dahdi/xpp/init_card_1_30, drivers/dahdi/xpp/xbus-pcm.c, + drivers/dahdi/xpp/init_card_2_30, + drivers/dahdi/xpp/card_global.h, + drivers/dahdi/xpp/init_card_3_30, drivers/dahdi/xpp/xproto.h, + drivers/dahdi/xpp/xpp_dahdi.c, drivers/dahdi/xpp/xpd.h, + drivers/dahdi/xpp/init_card_4_30, drivers/dahdi/xpp/xpp_dahdi.h, + drivers/dahdi/xpp/card_bri.c, drivers/dahdi/xpp/mmapdrv.c, + drivers/dahdi/xpp/xbus-core.c, drivers/dahdi/xpp/xbus-core.h, + drivers/dahdi/xpp/xdefs.h: xpp: start migration from procfs to + sysfs. * Sysfs representation for XPDs: + /sys/bus/xpds/devices/:: * Astribanks sysfs + directories now include the XPDs as subdirectories: e.g. + /sys/bus/astribanks/devices/xbus-00/00:3:0 * procfs control + interface deprecated: conditioned by OLD_PROC (defaults to off). + Control functionality moved to sysfs: * xbus attributes: cls + connector label status timing waitfor_xpds xbus_state * XPDs can + have driver-specific attributes. Common attriubtes: blink + chipregs span * PRI-specific attributes: pri_clocking pri_dchan + pri_cas pri_alarms pri_layer1 pri_localloop pri_protocol * The + Astribank attribute "xbus_state" is read/write. Reading it shows + the current state of the Astribank. Writing "start" or "stop" + allows a software equivalent of connect or disconnect + respectively. * When an Astribank is ready it sends an "online" + event. Whenever its not ready (e.g. at the time of disconnect) it + sends an "offline" event. Use astribank_hook.sample to handle + those. + +2008-10-14 22:11 +0000 [r5090] Shaun Ruffell + + * drivers/dahdi/wcte12xp/base.c: If the vpmadt032 firmware needs to + be reloaded, make sure we use the same slot in the ifaces array. + +2008-10-10 22:38 +0000 [r5084] Shaun Ruffell + + * drivers/dahdi/dahdi_transcode.c, drivers/dahdi/wctc4xxp/base.c: - + Ensure that the source format is considered when selecting a + transcoder. - When a command is to be retried, turn off the + TX_COMPLETE flag before resubmitting it to the hardware. This + should elimate some of the warnings printed to the kernel log in + the wctc4xxp_transmit_cmd function. + +2008-10-09 02:54 +0000 [r5068] Kevin P. Fleming + + * drivers/dahdi/dahdi-base.c: a micro-optimization found while + creslin and i spent four or five hours tracking down a very + complex problem + +2008-10-06 20:52 +0000 [r5064] Kevin P. Fleming + + * drivers/dahdi/dahdi-base.c: use the same logic here as elsewhere + for releasing echocan module references + +2008-10-06 17:48 +0000 [r5060] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: Embed room for the complete packet + to the DTE in the transcoder buffer structure. Simplifies + alignment management at the cost of a little wasted memory, but + the end results is that operation is more reliable on more + systems. + +2008-10-06 16:55 +0000 [r5056] Sean Bright + + * drivers/dahdi/dahdi-base.c: Fix a few compile errors that only + show up when CONFIG_DAHDI_PPP is defined. (closes issue #13608) + Reported by: Nik Soggia Fix suggested by: Nik Soggia Tested by: + seanbright + +2008-10-03 20:32 +0000 [r5046-5051] Tzafrir Cohen + + * drivers/dahdi/dahdi-base.c: Fix building with CONFIG_DAHDI_NET in + kernel 2.6.22 The leftovers of issues #13542 (which was mostly + resolved in previous committ. This closes it. + + * drivers/dahdi/dahdi-base.c: Fix building with CONFIG_DAHDI_NET . + It builds, but will it run? Patch dahdi-base.c.hdlc.patch by + biohumanoid that fixes some aparant copy&paste errors. + +2008-10-03 20:09 +0000 [r5045] Sean Bright + + * drivers/dahdi/dahdi-base.c, drivers/dahdi/dahdi_config.h: Fix + some compilation problems that show up when CONFIG_DAHDI_DEBUG is + defined. + +2008-10-03 15:39 +0000 [r5021-5034] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: There are two possible valid + statues when booting the TC400M. + + * drivers/dahdi/wctc4xxp/base.c: Keep hold of the channel lock when + setting the data_ready flag for the channel after writing to the + receive queue. Prevents a warning that data was on the recieve + queue but the data ready flag was not set. Issue: DAHDI-42 + + * drivers/dahdi/wctc4xxp/base.c: wctc4xxp_cleanup_channel_private + needs a pointer to the zt_transcoder_channel and not just the + private portion now in order to manage the data ready flag state. + + * drivers/dahdi/wctc4xxp/base.c: Mark that there is not any data + waiting whenever we cleanup the private channel structures. + Issue: DAHDI-42 + + * drivers/dahdi/dahdi-base.c: DAHDI should always make data + received from the PSTN available to user mode immediately. Only + allow the transmit buffering policy to be changed in order to + reduce the chance of underruns to the PSTN. + +2008-09-30 20:29 +0000 [r5017] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: Removing references to setup_timer + in pre 2.6.18 kernels. + +2008-09-29 Shaun Ruffell + + * dahdi-linux 2.0.0 released. + +2008-09-28 17:29 +0000 [r5002-5007] Shaun Ruffell + + * drivers/dahdi/wctdm.c: Improve reliablity of UK caller ID for the + TDM400P by not allowing the ringdebounce to be decremented when + it is at 0 already. Related to issue #12531. Reported mattbrown, + fix suggested by benbrown. + + * drivers/dahdi/wcte12xp/base.c: Fixed type of flags parameter to + spin_lock_irqsave functions. Fixes compilation issues on + platforms where int and long do not have the same size. Closes + Issues #0013575. Reported by Ulmo. + + * drivers/dahdi/dahdi_echocan_kb1.c, + drivers/dahdi/dahdi_echocan_mg2.c, drivers/dahdi/dahdi-base.c, + drivers/dahdi/dahdi_echocan_sec.c, + drivers/dahdi/dahdi_echocan_sec2.c: Fixes failure of modular echo + cancelers in DAHDI. Reported by lots of people, fix suggested by + mattf. + +2008-09-26 03:20 +0000 [r4990] Shaun Ruffell + + * drivers/dahdi/dahdi-base.c: The channel master should not be set + to 0, but rather should be 'cleared' by setting the channel to be + it's own master. (related to issue 11611) + +2008-09-25 16:50 +0000 [r4979-4986] Tzafrir Cohen + + * drivers/dahdi/xpp/firmwares/FPGA_1151.hex: XPP firmware: Only + send out CAS D-channel messages when in CAS mode. + + * drivers/dahdi/xpp/init_card_3_30, + drivers/dahdi/xpp/card_global.c, + drivers/dahdi/xpp/init_card_1_30, + drivers/dahdi/xpp/init_card_2_30: Fix display of indirect + registers and streamline their setting. * This commit fixes + display of indirect registers through the chipregs (formly + "slics") procfs file. Only the low byte was displayed. * It also + deprecates previous {RW}S in favour of {RW}I. The prevois style + is still allowed but deprecated, and thus previous scripts will + still work. + + * drivers/dahdi/xpp/card_fxo.c: XPP FXO: Add caller-id workaround + for ESTI-DTMF (for #9096) * Also rename the CID_STYLE_* constants + to formal names. + +2008-09-24 06:15 +0000 [r4971] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: Adding back in the mode module + parameter for the wctc4xxp driver. This is primarily used to + increase the number of channels available when only transcoding + to/from g729 by setting it to 'g729'. + +2008-09-18 21:23 +0000 [r4957] Shaun Ruffell + + * drivers/dahdi/wcte12xp/base.c: Adding a needed header for the + in_atomic call. + +2008-09-18 21:23 +0000 [r4956-4957] Shaun Ruffell + + * drivers/dahdi/wcte12xp/base.c, drivers/dahdi/dahdi-base.c: + close_channel is called with a spin_lock held, which means that + GFP_KERNEL can not be used for the memory allocations down that + call path. Have allocations in this call path check if they are + in atomic context and use the appropriate flags. Issue: DAHDI-195 + +2008-09-17 19:07 +0000 [r4917-4930] Shaun Ruffell + + * README: Just moved a note into it's own paragraph so that + asciidoc can make the appropriate callout. + + * README: Added a note about installing support for the B410P in + the installation section of the README file. + + * drivers/dahdi/dahdi-base.c: Turn off reference counting on the + echo canceller modules in order to prevent misconfigurations from + preventing the drivers from unloading. NOTE: This is only a + temporary workaround, since it also means that the echocanceller + can be unloaded by an administrator while in use, which would + most likely result in a kernel oops. Related to issue #13504. + +2008-09-15 20:49 +0000 [r4905-4909] Shaun Ruffell + + * drivers/dahdi/wctdm.c, drivers/dahdi/dahdi-base.c: Edit some + comments and error strings. Issue: DAHDI-13 + + * drivers/dahdi/wcte12xp/vpmadt032.c, + drivers/dahdi/wctdm24xxp/base.c: Fixed two typos. + +2008-09-11 23:00 +0000 [r4894-4900] Tzafrir Cohen + + * drivers/dahdi/dahdi-base.c: Fix a type used in nethdlc mode, as + pointed out in #13427 . + + * drivers/dahdi/xpp/xpp.rules (added), Makefile: Move udev rules + xpp.rules from dahdi-tools to dahdi-linux . + +2008-09-08 Russell Bryant + + * dahdi-linux version 2.0.0-rc4 released. + +2008-09-06 20:27 +0000 [r4868-4870] Matthew Fredrickson + + * drivers/dahdi/dahdi-base.c: Fix buglet in #define for 2.6.9 + + * drivers/dahdi/wct4xxp/base.c: Revert unnecessary default hardhdlc + mode from 56K to 64K + + * drivers/dahdi/wct4xxp/base.c, drivers/dahdi/dahdi-base.c: Fix + class_simple on old 2.6.9 kernels + +2008-09-04 21:42 +0000 [r4865] Matthew Fredrickson + + * include/dahdi/fasthdlc.h: Some picky switches require the LSB to + be 1 for 56k links + +2008-09-04 21:29 +0000 [r4861-4864] Shaun Ruffell + + * drivers/dahdi/wct4xxp/base.c: Remove a couple of 'magic numbers' + to make it clear all the ports of the framer should be + configured. + + * drivers/dahdi/wct4xxp/base.c: Fixes an issue where the dual-span + cards are not properly configured which can cause data loss. Fix + provided by opticron and possibly related to issue #0013393. + +2008-08-29 21:46 +0000 [r4856] Matthew Fredrickson + + * drivers/dahdi/dahdi-base.c: Remove useless kzalloc + +2008-08-27 17:12 +0000 [r4848-4849] Kevin P. Fleming + + * drivers/dahdi/dahdi_dummy.c: remove some more ztdummy references + + * drivers/dahdi/wcfxo.c, drivers/dahdi/wcte12xp/base.c, + drivers/dahdi/wct4xxp/base.c, drivers/dahdi/wcte11xp.c, + drivers/dahdi/wct1xxp.c, drivers/dahdi/wctc4xxp/base.c, + drivers/dahdi/wctdm.c, drivers/dahdi/wctdm24xxp/base.c, README: a + bit of attribution cleanup + +2008-08-27 16:48 +0000 [r4847] Jason Parker + + * Makefile: Set a list of headers to install/uninstall, so the + lists don't get out of sync (note the previously missing + fasthdlc.h in the uninstall-include target) + +2008-08-26 13:04 +0000 [r4829-4841] Tzafrir Cohen + + * Makefile: "docs" target to generate documentation. Generate docs + with asciidoc. + + * drivers/dahdi/xpp/README.Astribank: Fix asciidoc. + + * Makefile: Don't try to clean modules if there's no kernel source + available (like in Zaptel). + + * README: Reverting unwanted changes in the README (from r4830) + Also fixed ZT references in the ABI compatibility section. The + numerical ioctl values need to be recalculated, though. + + * README: Make the README file more relevant to modules. + + * drivers/dahdi/Kbuild, Makefile: Support MODULES_EXTRA and + SUBDIRS_EXTRA to add extra modules from the make command line. + +2008-08-25 17:50 +0000 [r4828] Jason Parker + + * Makefile: Make sure we remove headers that we installed in + install-include + +2008-08-25 14:54 +0000 [r4823] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: Remove this warning, which could + occur if the driver is loaded on a system without a wctc4xxp, and + then subsequently unloaded. + +2008-08-24 05:53 +0000 [r4817] Matthew Fredrickson + + * include/dahdi/user.h, include/dahdi/kernel.h, + drivers/dahdi/dahdi-base.c, include/dahdi/fasthdlc.h: Add support + for 56 KB HDLC as well as selectable rate via ioctl + +2008-08-20 22:20 +0000 [r4805] Kevin P. Fleming + + * drivers/dahdi/dahdi_dynamic.c: use the new separate allocation + method for channel structures here too replace "ZTD" references + in channel/span names with "DYN" (closes issue #13302) Reported + by: KNK + +2008-08-20 Kevin P. Fleming + + * dahdi-linux version 2.0.0-rc3 released. + +2008-08-20 22:20 +0000 [r4801-4805] Kevin P. Fleming + + * drivers/dahdi/dahdi_dynamic.c: use the new separate allocation + method for channel structures here too replace "ZTD" references + in channel/span names with "DYN" (closes issue #13302) Reported + by: KNK + + * drivers/dahdi/dahdi_dynamic.c: update code to match version in + Zaptel + + * drivers/dahdi/xpp/xpd.h, drivers/dahdi/wctdm24xxp/wctdm24xxp.h, + drivers/dahdi/wcte12xp/vpmadt032.c, + drivers/dahdi/wctdm24xxp/base.c, + drivers/dahdi/wctdm24xxp/GpakApi.c, drivers/dahdi/dahdi-base.c, + drivers/dahdi/wctdm24xxp/GpakCust.c: improve compatibility with + 2.6.26 and 2.6.27 kernels (closes issue #13253) Reported by: + raiden Patches: zap-dev.patch uploaded by smurfix on issue #13277 + (license 547) zap-sema.patch uploaded by smurfix on issue #13277 + (license 547) + +2008-08-20 19:31 +0000 [r4798] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: Change to support both 5.3kbps and + 6.3kbps bit rates when using the G723.1 codec. + +2008-08-19 20:49 +0000 [r4795] Kevin P. Fleming + + * drivers/dahdi/firmware/Makefile: port over improvements to + firmware Makefile from Zaptel + +2008-08-19 20:25 +0000 [r4791-4794] Shaun Ruffell + + * drivers/dahdi/wctc4xxp/base.c: Fixed calculation of the + timestamp. + + * include/dahdi/kernel.h, drivers/dahdi/dahdi_transcode.c, + drivers/dahdi/wctc4xxp/base.c: Couple of fixes for the + transcoder: - In dahdi_transcode.c, Embed the identifiation + number, assigned sequentially when the transcoders are + registered, in the transcoder structure. This allows + DAHDI_TC_GETINFO to work as expected even though the transcoders + are rotated on the list in order to spread the load. - In + wctc4xxp, fix bug where all transcoders are named tc400b0. + +2008-08-18 23:24 +0000 [r4788] Kevin P. Fleming + + * Makefile: minor cleanups, and allow DAHDI_BUILD_ALL to be + overriden on the command line + +2008-08-14 21:37 +0000 [r4776-4784] Tzafrir Cohen + + * drivers/dahdi/xpp/.version, drivers/dahdi/xpp/Changelog_xpp: Set + xpp version to 6056 and reset XPP changelog. + + * drivers/dahdi/xpp/firmwares/Makefile (added), Makefile: Also + install Astribank firmwares and init scripts to /usr/share/dahdi + . + + * drivers/dahdi/xpp/card_fxo.c: xpp: FXO: display signed voltage + values (from xpp r6055) This is only an issue with the displayed + value. In case you wondered why you have battery voltage of more + than 220V. + +2008-08-14 01:09 +0000 [r4773] Kevin P. Fleming + + * drivers/dahdi/dahdi_transcode.c: remove devfs support, and use + consistent include file path + +2008-08-13 21:04 +0000 [r4770] Doug Bailey + + * drivers/dahdi/wctdm24xxp/wctdm24xxp.h, + drivers/dahdi/wctdm24xxp/base.c: import the neon mwi detection + +2008-08-11 16:22 +0000 [r4758-4761] Tzafrir Cohen + + * drivers/dahdi/xpp/card_pri.c, drivers/dahdi/xpp/card_bri.c, + drivers/dahdi/xpp/firmwares/FPGA_1151.hex, + drivers/dahdi/xpp/xbus-core.c, drivers/dahdi/xpp/card_global.c, + drivers/dahdi/xpp/xbus-pcm.c: xpp: CAS/E1 support in the PRI + module, and minor fixes. * Add support for CAS in the PRI module. + Use firmware rev. 5975. * Debugging parameter pcmtx_chan now + accepts a dahdi channel number. * Do initialize a reserved + protocol field (card_global). * The name DAHDI as used in + proc/xpp/sync has 5 (not 6) letters. * Fix DTMF "channel leak" + regression in the FXS module. + + * drivers/dahdi/xpp/card_fxo.c: xpp: fxo: Fix support for + CID_STYLE_PASS_ALWAYS + + * drivers/dahdi/xpp/Kbuild: A more robust test for bri_dchan + support. + + * drivers/dahdi/xpp/xpd.h, drivers/dahdi/xpp/xpp_dahdi.c: xpp_blink + is a bit mask of ports, and not boolean anymore. + +2008-08-08 Kevin P. Fleming + + * dahdi-linux version 2.0.0-rc2 released. + +2008-08-07 20:21 +0000 [r4742] Shaun Ruffell : + + * drivers/dahdi/dahdi_transcode.c: Make sure types are same size on + 64-bit machines. + +2008-08-06 Kevin P. Fleming + + * dahdi-linux version 2.0.0-rc1 released. diff -Nru dahdi-linux-2.2.1-rc2/dkms.conf dahdi-cnet-linux-2.2.1-rc2/dkms.conf --- dahdi-linux-2.2.1-rc2/dkms.conf 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/dkms.conf 2010-01-09 21:29:01.955618012 -0600 @@ -0,0 +1,108 @@ +# dkms.conf for dahdi-cnet-linux +PACKAGE_VERSION="2.2.1-rc2" +PACKAGE_NAME="dahdi-cnet-linux" +AUTOINSTALL="yes" +MAKE[0]="KVERS=$kernelver make" +CLEAN="make clean" + +BUILT_MODULE_NAME[0]="dahdi_dynamic_loc" +BUILT_MODULE_NAME[1]="wct1xxp" +BUILT_MODULE_NAME[2]="tor2" +BUILT_MODULE_NAME[3]="dahdi_dummy" +BUILT_MODULE_NAME[4]="wcb4xxp" +BUILT_MODULE_NAME[5]="dahdi_transcode" +BUILT_MODULE_NAME[6]="wcfxo" +BUILT_MODULE_NAME[7]="wctdm24xxp" +BUILT_MODULE_NAME[8]="wcte11xp" +BUILT_MODULE_NAME[9]="dahdi_echocan_jpah" +BUILT_MODULE_NAME[10]="dahdi" +BUILT_MODULE_NAME[11]="dahdi_echocan_oslec" +BUILT_MODULE_NAME[12]="dahdi_voicebus" +BUILT_MODULE_NAME[13]="xpp" +BUILT_MODULE_NAME[14]="xpd_bri" +BUILT_MODULE_NAME[15]="xpd_fxo" +BUILT_MODULE_NAME[16]="xpp_usb" +BUILT_MODULE_NAME[17]="xpd_pri" +BUILT_MODULE_NAME[18]="xpd_fxs" +BUILT_MODULE_NAME[19]="dahdi_echocan_sec" +BUILT_MODULE_NAME[20]="dahdi_echocan_mg2" +BUILT_MODULE_NAME[21]="pciradio" +BUILT_MODULE_NAME[22]="dahdi_echocan_kb1" +BUILT_MODULE_NAME[23]="dahdi_dynamic_eth" +BUILT_MODULE_NAME[24]="dahdi_echocan_sec2" +BUILT_MODULE_NAME[25]="dahdi_dynamic" +BUILT_MODULE_NAME[26]="wctc4xxp" +BUILT_MODULE_NAME[27]="wcte12xp" +BUILT_MODULE_NAME[28]="dahdi_vpmadt032_loader" +BUILT_MODULE_NAME[29]="wct4xxp" +BUILT_MODULE_NAME[30]="wctdm" +BUILT_MODULE_NAME[31]="echo" + +BUILT_MODULE_LOCATION[0]="drivers/dahdi/" +BUILT_MODULE_LOCATION[1]="drivers/dahdi/" +BUILT_MODULE_LOCATION[2]="drivers/dahdi/" +BUILT_MODULE_LOCATION[3]="drivers/dahdi/" +BUILT_MODULE_LOCATION[4]="drivers/dahdi/wcb4xxp/" +BUILT_MODULE_LOCATION[5]="drivers/dahdi/" +BUILT_MODULE_LOCATION[6]="drivers/dahdi/" +BUILT_MODULE_LOCATION[7]="drivers/dahdi/wctdm24xxp/" +BUILT_MODULE_LOCATION[8]="drivers/dahdi/" +BUILT_MODULE_LOCATION[9]="drivers/dahdi/" +BUILT_MODULE_LOCATION[10]="drivers/dahdi/" +BUILT_MODULE_LOCATION[11]="drivers/dahdi/" +BUILT_MODULE_LOCATION[12]="drivers/dahdi/voicebus/" +BUILT_MODULE_LOCATION[13]="drivers/dahdi/xpp/" +BUILT_MODULE_LOCATION[14]="drivers/dahdi/xpp/" +BUILT_MODULE_LOCATION[15]="drivers/dahdi/xpp/" +BUILT_MODULE_LOCATION[16]="drivers/dahdi/xpp/" +BUILT_MODULE_LOCATION[17]="drivers/dahdi/xpp/" +BUILT_MODULE_LOCATION[18]="drivers/dahdi/xpp/" +BUILT_MODULE_LOCATION[19]="drivers/dahdi/" +BUILT_MODULE_LOCATION[20]="drivers/dahdi/" +BUILT_MODULE_LOCATION[21]="drivers/dahdi/" +BUILT_MODULE_LOCATION[22]="drivers/dahdi/" +BUILT_MODULE_LOCATION[23]="drivers/dahdi/" +BUILT_MODULE_LOCATION[24]="drivers/dahdi/" +BUILT_MODULE_LOCATION[25]="drivers/dahdi/" +BUILT_MODULE_LOCATION[26]="drivers/dahdi/wctc4xxp/" +BUILT_MODULE_LOCATION[27]="drivers/dahdi/wcte12xp/" +BUILT_MODULE_LOCATION[28]="drivers/dahdi/" +BUILT_MODULE_LOCATION[29]="drivers/dahdi/wct4xxp/" +BUILT_MODULE_LOCATION[30]="drivers/dahdi/" +BUILT_MODULE_LOCATION[31]="drivers/staging/echo" + +DEST_MODULE_LOCATION[0]="/extra/" +DEST_MODULE_LOCATION[1]="/extra/" +DEST_MODULE_LOCATION[2]="/extra/" +DEST_MODULE_LOCATION[3]="/extra/" +DEST_MODULE_LOCATION[4]="/extra/wcb4xxp/" +DEST_MODULE_LOCATION[5]="/extra/" +DEST_MODULE_LOCATION[6]="/extra/" +DEST_MODULE_LOCATION[7]="/extra/wctdm24xxp/" +DEST_MODULE_LOCATION[8]="/extra/" +DEST_MODULE_LOCATION[9]="/extra/" +DEST_MODULE_LOCATION[10]="/extra/" +DEST_MODULE_LOCATION[11]="/extra/" +DEST_MODULE_LOCATION[12]="/extra/voicebus/" +DEST_MODULE_LOCATION[13]="/extra/xpp/" +DEST_MODULE_LOCATION[14]="/extra/xpp/" +DEST_MODULE_LOCATION[15]="/extra/xpp/" +DEST_MODULE_LOCATION[16]="/extra/xpp/" +DEST_MODULE_LOCATION[17]="/extra/xpp/" +DEST_MODULE_LOCATION[18]="/extra/xpp/" +DEST_MODULE_LOCATION[19]="/extra/" +DEST_MODULE_LOCATION[20]="/extra/" +DEST_MODULE_LOCATION[21]="/extra/" +DEST_MODULE_LOCATION[22]="/extra/" +DEST_MODULE_LOCATION[23]="/extra/" +DEST_MODULE_LOCATION[24]="/extra/" +DEST_MODULE_LOCATION[25]="/extra/" +DEST_MODULE_LOCATION[26]="/extra/wctc4xxp/" +DEST_MODULE_LOCATION[27]="/extra/wcte12xp/" +DEST_MODULE_LOCATION[28]="/extra/" +DEST_MODULE_LOCATION[29]="/extra/wct4xxp/" +DEST_MODULE_LOCATION[30]="/extra/" +DEST_MODULE_LOCATION[31]="/extra/" + +POST_INSTALL="dkms-post-install ${source_tree}/$PACKAGE_NAME-$PACKAGE_VERSION" +POST_REMOVE="dkms-post-remove ${source_tree}/$PACKAGE_NAME-$PACKAGE_VERSION $PACKAGE_NAME $kernelver" diff -Nru dahdi-linux-2.2.1-rc2/dkms-post-install dahdi-cnet-linux-2.2.1-rc2/dkms-post-install --- dahdi-linux-2.2.1-rc2/dkms-post-install 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/dkms-post-install 2010-01-09 21:29:01.955618012 -0600 @@ -0,0 +1,6 @@ +#!/bin/bash +echo Running post-install... +make -C $1 install-devices +make -C $1 install-include +make -C $1 install-firmware +make -C $1 install-xpp-firm diff -Nru dahdi-linux-2.2.1-rc2/dkms-post-remove dahdi-cnet-linux-2.2.1-rc2/dkms-post-remove --- dahdi-linux-2.2.1-rc2/dkms-post-remove 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/dkms-post-remove 2010-01-09 21:29:01.955618012 -0600 @@ -0,0 +1,11 @@ +#!/bin/bash +modtest=`dkms status -m $2 | grep -v $3` +if [ -z "$modtest" ] +then + echo Completing dahdi-cnet uninstall. + make -C $1 uninstall-firmware + make -C $1 uninstall-include + make -C $1 uninstall-devices +else + echo "$2 still installed for other kernel versions." +fi diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/adt_lec.c dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/adt_lec.c --- dahdi-linux-2.2.1-rc2/drivers/dahdi/adt_lec.c 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/adt_lec.c 2009-11-12 14:02:24.000000000 -0600 @@ -0,0 +1,73 @@ +/* + * ADT Line Echo Canceller Parameter Parsing + * + * Copyright (C) 2008-2009 Digium, Inc. + * + * Kevin P. Fleming + * + * All rights reserved. + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#ifndef _ADT_LEC_C +#define _ADT_LEC_C + +#include + +static inline void adt_lec_init_defaults(struct adt_lec_params *params, __u32 tap_length) +{ + params->tap_length = tap_length; + params->nlp_type = 0; + params->nlp_max_suppress = 0; + params->nlp_threshold = 0; +} + +static int adt_lec_parse_params(struct adt_lec_params *params, + struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p) +{ + unsigned int x; + char *c; + + params->tap_length = ecp->tap_length; + + for (x = 0; x < ecp->param_count; x++) { + for (c = p[x].name; *c; c++) + *c = tolower(*c); + if (!strcmp(p[x].name, "nlp_type")) { + switch (p[x].value) { + case ADT_LEC_NLP_OFF: + case ADT_LEC_NLP_MUTE: + case ADT_LEC_RANDOM_NOISE: + case ADT_LEC_HOTH_NOISE: + case ADT_LEC_SUPPRESS: + params->nlp_type = p[x].value; + break; + default: + return -EINVAL; + } + } else if (!strcmp(p[x].name, "nlp_thresh")) { + params->nlp_threshold = p[x].value; + } else if (!strcmp(p[x].name, "nlp_suppress")) { + params->nlp_max_suppress = p[x].value; + } else { + return -EINVAL; + } + } + + return 0; +} + +#endif /* _ADT_LEC_C */ diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/adt_lec.h dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/adt_lec.h --- dahdi-linux-2.2.1-rc2/drivers/dahdi/adt_lec.h 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/adt_lec.h 2009-11-12 14:02:24.000000000 -0600 @@ -0,0 +1,48 @@ +/* + * ADT Line Echo Canceller Parameter Parsing + * + * Copyright (C) 2008 Digium, Inc. + * + * Kevin P. Fleming + * + * All rights reserved. + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#ifndef _ADT_LEC_H +#define _ADT_LEC_H + +enum adt_lec_nlp_type { + ADT_LEC_NLP_OFF = 0, + ADT_LEC_NLP_MUTE, + ADT_LEC_RANDOM_NOISE, + ADT_LEC_HOTH_NOISE, + ADT_LEC_SUPPRESS, +}; + +enum adt_companding { + ADT_COMP_ULAW = 0, + ADT_COMP_ALAW, +}; + +struct adt_lec_params { + __u32 tap_length; + enum adt_lec_nlp_type nlp_type; + __u32 nlp_threshold; + __u32 nlp_max_suppress; + enum adt_companding companding; +}; + +#endif /* _ADT_LEC_H */ diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/arith.h dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/arith.h --- dahdi-linux-2.2.1-rc2/drivers/dahdi/arith.h 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/arith.h 2008-08-05 18:56:00.000000000 -0500 @@ -0,0 +1,378 @@ +/* + * Handy add/subtract functions to operate on chunks of shorts. + * Feel free to add customizations for additional architectures + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#ifndef _DAHDI_ARITH_H +#define _DAHDI_ARITH_H + +#ifdef CONFIG_DAHDI_MMX +#ifdef DAHDI_CHUNKSIZE +static inline void __ACSS(volatile short *dst, const short *src) +{ + __asm__ __volatile__ ( + "movq 0(%0), %%mm0;\n" + "movq 0(%1), %%mm1;\n" + "movq 8(%0), %%mm2;\n" + "movq 8(%1), %%mm3;\n" + "paddsw %%mm1, %%mm0;\n" + "paddsw %%mm3, %%mm2;\n" + "movq %%mm0, 0(%0);\n" + "movq %%mm2, 8(%0);\n" + : "=r" (dst) + : "r" (src), "0" (dst) + : "memory" +#ifdef CLOBBERMMX + , "%mm0", "%mm1", "%mm2", "%mm3" +#endif + ); + +} +static inline void __SCSS(volatile short *dst, const short *src) +{ + __asm__ __volatile__ ( + "movq 0(%0), %%mm0;\n" + "movq 0(%1), %%mm1;\n" + "movq 8(%0), %%mm2;\n" + "movq 8(%1), %%mm3;\n" + "psubsw %%mm1, %%mm0;\n" + "psubsw %%mm3, %%mm2;\n" + "movq %%mm0, 0(%0);\n" + "movq %%mm2, 8(%0);\n" + : "=r" (dst) + : "r" (src), "0" (dst) + : "memory" +#ifdef CLOBBERMMX + , "%mm0", "%mm1", "%mm2", "%mm3" +#endif + ); + +} + +#if (DAHDI_CHUNKSIZE == 8) +#define ACSS(a,b) __ACSS(a,b) +#define SCSS(a,b) __SCSS(a,b) +#elif (DAHDI_CHUNKSIZE > 8) +static inline void ACSS(volatile short *dst, const short *src) +{ + int x; + for (x=0;x>= 4; + + /* Clear our accumulator, mm4 */ + + /* + + For every set of eight... + + Load 16 coefficients into four registers... + Shift each word right 16 to make them shorts... + Pack the resulting shorts into two registers... + With the coefficients now in mm0 and mm2, load the + history into mm1 and mm3... + Multiply/add mm1 into mm0, and mm3 into mm2... + Add mm2 into mm0 (without saturation, alas). Now we have two half-results. + Accumulate in mm4 (again, without saturation, alas) + */ + __asm__ ( + "pxor %%mm4, %%mm4;\n" + "mov %1, %%edi;\n" + "mov %2, %%esi;\n" + "mov %3, %%ecx;\n" + "1:" + "movq 0(%%edi), %%mm0;\n" + "movq 8(%%edi), %%mm1;\n" + "movq 16(%%edi), %%mm2;\n" + "movq 24(%%edi), %%mm3;\n" + /* can't use 4/5 since 4 is the accumulator for us */ + "movq 32(%%edi), %%mm6;\n" + "movq 40(%%edi), %%mm7;\n" + "psrad $16, %%mm0;\n" + "psrad $16, %%mm1;\n" + "psrad $16, %%mm2;\n" + "psrad $16, %%mm3;\n" + "psrad $16, %%mm6;\n" + "psrad $16, %%mm7;\n" + "packssdw %%mm1, %%mm0;\n" + "packssdw %%mm3, %%mm2;\n" + "packssdw %%mm7, %%mm6;\n" + "movq 0(%%esi), %%mm1;\n" + "movq 8(%%esi), %%mm3;\n" + "movq 16(%%esi), %%mm7;\n" + "pmaddwd %%mm1, %%mm0;\n" + "pmaddwd %%mm3, %%mm2;\n" + "pmaddwd %%mm7, %%mm6;\n" + "paddd %%mm6, %%mm4;\n" + "paddd %%mm2, %%mm4;\n" + "paddd %%mm0, %%mm4;\n" + /* Come back and do for the last few bytes */ + "movq 48(%%edi), %%mm6;\n" + "movq 56(%%edi), %%mm7;\n" + "psrad $16, %%mm6;\n" + "psrad $16, %%mm7;\n" + "packssdw %%mm7, %%mm6;\n" + "movq 24(%%esi), %%mm7;\n" + "pmaddwd %%mm7, %%mm6;\n" + "paddd %%mm6, %%mm4;\n" + "add $64, %%edi;\n" + "add $32, %%esi;\n" + "dec %%ecx;\n" + "jnz 1b;\n" + "movq %%mm4, %%mm0;\n" + "psrlq $32, %%mm0;\n" + "paddd %%mm0, %%mm4;\n" + "movd %%mm4, %0;\n" + : "=r" (sum) + : "r" (coeffs), "r" (hist), "r" (len) + : "%ecx", "%edi", "%esi" + ); + + return sum; +} + +static inline void UPDATE(volatile int *taps, const short *history, const int nsuppr, const int ntaps) +{ + int i; + int correction; + for (i=0;i>= 4; + /* First, load up taps, */ + __asm__ ( + "pxor %%mm4, %%mm4;\n" + "mov %0, %%edi;\n" + "mov %1, %%esi;\n" + "mov %3, %%ecx;\n" + "1:" + "jnz 1b;\n" + "movq %%mm4, %%mm0;\n" + "psrlq $32, %%mm0;\n" + "paddd %%mm0, %%mm4;\n" + "movd %%mm4, %0;\n" + : "=r" (taps), "=r" (taps_short) + : "r" (history), "r" (nsuppr), "r" (ntaps), "0" (taps) + : "%ecx", "%edi", "%esi" + ); +#endif +#if 1 + for (i=0;i> 16; + } +#endif +} + +static inline int CONVOLVE2(const short *coeffs, const short *hist, int len) +{ + int sum; + /* Divide length by 16 */ + len >>= 4; + + /* Clear our accumulator, mm4 */ + + /* + + For every set of eight... + Load in eight coefficients and eight historic samples, multliply add and + accumulate the result + */ + __asm__ ( + "pxor %%mm4, %%mm4;\n" + "mov %1, %%edi;\n" + "mov %2, %%esi;\n" + "mov %3, %%ecx;\n" + "1:" + "movq 0(%%edi), %%mm0;\n" + "movq 8(%%edi), %%mm2;\n" + "movq 0(%%esi), %%mm1;\n" + "movq 8(%%esi), %%mm3;\n" + "pmaddwd %%mm1, %%mm0;\n" + "pmaddwd %%mm3, %%mm2;\n" + "paddd %%mm2, %%mm4;\n" + "paddd %%mm0, %%mm4;\n" + "movq 16(%%edi), %%mm0;\n" + "movq 24(%%edi), %%mm2;\n" + "movq 16(%%esi), %%mm1;\n" + "movq 24(%%esi), %%mm3;\n" + "pmaddwd %%mm1, %%mm0;\n" + "pmaddwd %%mm3, %%mm2;\n" + "paddd %%mm2, %%mm4;\n" + "paddd %%mm0, %%mm4;\n" + "add $32, %%edi;\n" + "add $32, %%esi;\n" + "dec %%ecx;\n" + "jnz 1b;\n" + "movq %%mm4, %%mm0;\n" + "psrlq $32, %%mm0;\n" + "paddd %%mm0, %%mm4;\n" + "movd %%mm4, %0;\n" + : "=r" (sum) + : "r" (coeffs), "r" (hist), "r" (len) + : "%ecx", "%edi", "%esi" + ); + + return sum; +} +static inline short MAX16(const short *y, int len, int *pos) +{ + int k; + short max = 0; + int bestpos = 0; + for (k=0;k 32767) + sum = 32767; + else if (sum < -32768) + sum = -32768; + dst[x] = sum; + } +#endif +} + +static inline void SCSS(short *dst, short *src) +{ + int x; + + /* Subtract src from dst with saturation, storing in dst */ +#ifdef BFIN + for (x = 0; x < DAHDI_CHUNKSIZE; x++) + dst[x] = __builtin_bfin_sub_fr1x16(dst[x], src[x]); +#else + int sum; + + for (x = 0; x < DAHDI_CHUNKSIZE; x++) { + sum = dst[x] - src[x]; + if (sum > 32767) + sum = 32767; + else if (sum < -32768) + sum = -32768; + dst[x] = sum; + } +#endif +} + +#endif /* DAHDI_CHUNKSIZE */ + +static inline int CONVOLVE(const int *coeffs, const short *hist, int len) +{ + int x; + int sum = 0; + for (x=0;x> 16) * hist[x]; + return sum; +} + +static inline int CONVOLVE2(const short *coeffs, const short *hist, int len) +{ + int x; + int sum = 0; + for (x=0;x> 16; + } +} + +static inline short MAX16(const short *y, int len, int *pos) +{ + int k; + short max = 0; + int bestpos = 0; + for (k=0;k + * + * Copyright (C) 2001 Steve Underwood + * + * All rights reserved. + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +static inline void biquad2_init (biquad2_state_t *bq, + int32_t gain, + int32_t a1, + int32_t a2, + int32_t b1, + int32_t b2) +{ + bq->gain = gain; + bq->a1 = a1; + bq->a2 = a2; + bq->b1 = b1; + bq->b2 = b2; + + bq->z1 = 0; + bq->z2 = 0; +} +/*- End of function --------------------------------------------------------*/ + +static inline int16_t biquad2 (biquad2_state_t *bq, int16_t sample) +{ + int32_t y; + int32_t z0; + + z0 = sample*bq->gain + bq->z1*bq->a1 + bq->z2*bq->a2; + y = z0 + bq->z1*bq->b1 + bq->z2*bq->b2; + + bq->z2 = bq->z1; + bq->z1 = z0 >> 15; + y >>= 15; + return y; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/ diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi-base.c dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi-base.c --- dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi-base.c 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi-base.c 2010-01-09 21:29:01.959619081 -0600 @@ -0,0 +1,8370 @@ +/* + * DAHDI Telephony Interface Driver + * + * Written by Mark Spencer + * Based on previous works, designs, and architectures conceived and + * written by Jim Dixon . + * + * Special thanks to Steve Underwood + * for substantial contributions to signal processing functions + * in DAHDI and the Zapata library. + * + * Yury Bokhoncovich + * Adaptation for 2.4.20+ kernels (HDLC API was changed) + * The work has been performed as a part of our move + * from Cisco 3620 to IBM x305 here in F1 Group + * + * Copyright (C) 2001 Jim Dixon / Zapata Telephony. + * Copyright (C) 2001 - 2008 Digium, Inc. + * + * All rights reserved. + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define module_printk(level, fmt, args...) printk(level "%s: " fmt, THIS_MODULE->name, ## args) + +/* #define BUF_MUNGE */ + +#include +/* Grab fasthdlc with tables */ +#define FAST_HDLC_NEED_TABLES +#include +#include "ecdis.h" + +#ifndef CONFIG_OLD_HDLC_API +#define NEW_HDLC_INTERFACE +#endif + +#ifdef CONFIG_DAHDI_PPP +#include +#include +#include +#endif + +#ifdef CONFIG_DAHDI_NET +#include +#endif + +#include "hpec/hpec_user.h" + +#if defined(EMPULSE) && defined(EMFLASH) +#error "You cannot define both EMPULSE and EMFLASH" +#endif + +/* Get helper arithmetic */ +#include "arith.h" +#if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP) +#include +#endif + +#define hdlc_to_ztchan(h) (((struct dahdi_hdlc *)(h))->chan) +#define dev_to_ztchan(h) (((struct dahdi_hdlc *)(dev_to_hdlc(h)->priv))->chan) +#define ztchan_to_dev(h) ((h)->hdlcnetdev->netdev) + +/* macro-oni for determining a unit (channel) number */ +#define UNIT(file) MINOR(file->f_dentry->d_inode->i_rdev) + +/* names of tx level settings */ +static char *dahdi_txlevelnames[] = { +"0 db (CSU)/0-133 feet (DSX-1)", +"133-266 feet (DSX-1)", +"266-399 feet (DSX-1)", +"399-533 feet (DSX-1)", +"533-655 feet (DSX-1)", +"-7.5db (CSU)", +"-15db (CSU)", +"-22.5db (CSU)" +} ; + +EXPORT_SYMBOL(dahdi_transcode_fops); +EXPORT_SYMBOL(dahdi_init_tone_state); +EXPORT_SYMBOL(dahdi_mf_tone); +EXPORT_SYMBOL(dahdi_register); +EXPORT_SYMBOL(dahdi_unregister); +EXPORT_SYMBOL(__dahdi_mulaw); +EXPORT_SYMBOL(__dahdi_alaw); +#ifdef CONFIG_CALC_XLAW +EXPORT_SYMBOL(__dahdi_lineartoulaw); +EXPORT_SYMBOL(__dahdi_lineartoalaw); +#else +EXPORT_SYMBOL(__dahdi_lin2mu); +EXPORT_SYMBOL(__dahdi_lin2a); +#endif +EXPORT_SYMBOL(dahdi_lboname); +EXPORT_SYMBOL(dahdi_transmit); +EXPORT_SYMBOL(dahdi_receive); +EXPORT_SYMBOL(dahdi_rbsbits); +EXPORT_SYMBOL(dahdi_qevent_nolock); +EXPORT_SYMBOL(dahdi_qevent_lock); +EXPORT_SYMBOL(dahdi_hooksig); +EXPORT_SYMBOL(dahdi_alarm_notify); +EXPORT_SYMBOL(dahdi_set_dynamic_ioctl); +EXPORT_SYMBOL(dahdi_ec_chunk); +EXPORT_SYMBOL(dahdi_ec_span); +EXPORT_SYMBOL(dahdi_hdlc_abort); +EXPORT_SYMBOL(dahdi_hdlc_finish); +EXPORT_SYMBOL(dahdi_hdlc_getbuf); +EXPORT_SYMBOL(dahdi_hdlc_putbuf); +EXPORT_SYMBOL(dahdi_alarm_channel); +EXPORT_SYMBOL(dahdi_register_chardev); +EXPORT_SYMBOL(dahdi_unregister_chardev); + +EXPORT_SYMBOL(dahdi_register_echocan_factory); +EXPORT_SYMBOL(dahdi_unregister_echocan_factory); + +EXPORT_SYMBOL(dahdi_set_hpec_ioctl); + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *proc_entries[DAHDI_MAX_SPANS]; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) +#define CLASS_DEV_CREATE(class, devt, device, name) \ + device_create(class, device, devt, NULL, "%s", name) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#define CLASS_DEV_CREATE(class, devt, device, name) \ + device_create(class, device, devt, name) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) +#define CLASS_DEV_CREATE(class, devt, device, name) \ + class_device_create(class, NULL, devt, device, name) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13) +#define CLASS_DEV_CREATE(class, devt, device, name) \ + class_device_create(class, devt, device, name) +#else +#define CLASS_DEV_CREATE(class, devt, device, name) \ + class_simple_device_add(class, devt, device, name) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#define CLASS_DEV_DESTROY(class, devt) \ + device_destroy(class, devt) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13) +#define CLASS_DEV_DESTROY(class, devt) \ + class_device_destroy(class, devt) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9) +#define CLASS_DEV_DESTROY(class, devt) \ + class_simple_device_remove(devt) +#else +#define CLASS_DEV_DESTROY(class, devt) \ + class_simple_device_remove(class, devt) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13) +static struct class *dahdi_class = NULL; +#else +static struct class_simple *dahdi_class = NULL; +#define class_create class_simple_create +#define class_destroy class_simple_destroy +#endif + +static int deftaps = 64; + +static int debug; + +/*! + * \brief states for transmit signalling + */ +enum dahdi_txstate { + DAHDI_TXSTATE_ONHOOK, + DAHDI_TXSTATE_OFFHOOK, + DAHDI_TXSTATE_START, + DAHDI_TXSTATE_PREWINK, + DAHDI_TXSTATE_WINK, + DAHDI_TXSTATE_PREFLASH, + DAHDI_TXSTATE_FLASH, + DAHDI_TXSTATE_DEBOUNCE, + DAHDI_TXSTATE_AFTERSTART, + DAHDI_TXSTATE_RINGON, + DAHDI_TXSTATE_RINGOFF, + DAHDI_TXSTATE_KEWL, + DAHDI_TXSTATE_AFTERKEWL, + DAHDI_TXSTATE_PULSEBREAK, + DAHDI_TXSTATE_PULSEMAKE, + DAHDI_TXSTATE_PULSEAFTER, +}; + +typedef short sumtype[DAHDI_MAX_CHUNKSIZE]; + +static sumtype sums[(DAHDI_MAX_CONF + 1) * 3]; + +/* Translate conference aliases into actual conferences + and vice-versa */ +static short confalias[DAHDI_MAX_CONF + 1]; +static short confrev[DAHDI_MAX_CONF + 1]; + +static sumtype *conf_sums_next; +static sumtype *conf_sums; +static sumtype *conf_sums_prev; + +static struct dahdi_span *master; +static struct file_operations dahdi_fops; +struct file_operations *dahdi_transcode_fops = NULL; + +static struct { + int src; /* source conf number */ + int dst; /* dst conf number */ +} conf_links[DAHDI_MAX_CONF + 1]; + +#ifdef CONFIG_DAHDI_CORE_TIMER + +static struct core_timer { + struct timer_list timer; + struct timespec start_interval; + atomic_t count; + atomic_t shutdown; + atomic_t last_count; +} core_timer; + +#endif /* CONFIG_DAHDI_CORE_TIMER */ + + + +/* There are three sets of conference sum accumulators. One for the current +sample chunk (conf_sums), one for the next sample chunk (conf_sums_next), and +one for the previous sample chunk (conf_sums_prev). The following routine +(rotate_sums) "rotates" the pointers to these accululator arrays as part +of the events of sample chink processing as follows: + +The following sequence is designed to be looked at from the reference point +of the receive routine of the master span. + +1. All (real span) receive chunks are processed (with putbuf). The last one +to be processed is the master span. The data received is loaded into the +accumulators for the next chunk (conf_sums_next), to be in alignment with +current data after rotate_sums() is called (which immediately follows). +Keep in mind that putbuf is *also* a transmit routine for the pseudo parts +of channels that are in the REALANDPSEUDO conference mode. These channels +are processed from data in the current sample chunk (conf_sums), being +that this is a "transmit" function (for the pseudo part). + +2. rotate_sums() is called. + +3. All pseudo channel receive chunks are processed. This data is loaded into +the current sample chunk accumulators (conf_sums). + +4. All conference links are processed (being that all receive data for this +chunk has already been processed by now). + +5. All pseudo channel transmit chunks are processed. This data is loaded from +the current sample chunk accumulators (conf_sums). + +6. All (real span) transmit chunks are processed (with getbuf). This data is +loaded from the current sample chunk accumulators (conf_sums). Keep in mind +that getbuf is *also* a receive routine for the pseudo part of channels that +are in the REALANDPSEUDO conference mode. These samples are loaded into +the next sample chunk accumulators (conf_sums_next) to be processed as part +of the next sample chunk's data (next time around the world). + +*/ + +enum dahdi_digit_mode { + DIGIT_MODE_DTMF, + DIGIT_MODE_MFR1, + DIGIT_MODE_PULSE, + DIGIT_MODE_MFR2_FWD, + DIGIT_MODE_MFR2_REV, +}; + +#include "digits.h" + +static struct dahdi_dialparams global_dialparams = { + .dtmf_tonelen = DEFAULT_DTMF_LENGTH, + .mfv1_tonelen = DEFAULT_MFR1_LENGTH, + .mfr2_tonelen = DEFAULT_MFR2_LENGTH, +}; + +static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit); + +#if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP) +#define dahdi_kernel_fpu_begin kernel_fpu_begin +#endif + +struct dahdi_timer { + int ms; /* Countdown */ + int pos; /* Position */ + int ping; /* Whether we've been ping'd */ + int tripped; /* Whether we're tripped */ + struct list_head list; + wait_queue_head_t sel; +}; + +static LIST_HEAD(zaptimers); + +#ifdef DEFINE_SPINLOCK +static DEFINE_SPINLOCK(zaptimerlock); +static DEFINE_SPINLOCK(bigzaplock); +#else +static spinlock_t zaptimerlock = SPIN_LOCK_UNLOCKED; +static spinlock_t bigzaplock = SPIN_LOCK_UNLOCKED; +#endif + +struct dahdi_zone { + atomic_t refcount; + char name[40]; /* Informational, only */ + int ringcadence[DAHDI_MAX_CADENCE]; + struct dahdi_tone *tones[DAHDI_TONE_MAX]; + /* Each of these is a circular list + of dahdi_tones to generate what we + want. Use NULL if the tone is + unavailable */ + struct dahdi_tone dtmf[16]; /* DTMF tones for this zone, with desired length */ + struct dahdi_tone dtmf_continuous[16]; /* DTMF tones for this zone, continuous play */ + struct dahdi_tone mfr1[15]; /* MFR1 tones for this zone, with desired length */ + struct dahdi_tone mfr2_fwd[15]; /* MFR2 FWD tones for this zone, with desired length */ + struct dahdi_tone mfr2_rev[15]; /* MFR2 REV tones for this zone, with desired length */ + struct dahdi_tone mfr2_fwd_continuous[16]; /* MFR2 FWD tones for this zone, continuous play */ + struct dahdi_tone mfr2_rev_continuous[16]; /* MFR2 REV tones for this zone, continuous play */ +}; + +static struct dahdi_span *spans[DAHDI_MAX_SPANS]; +static struct dahdi_chan *chans[DAHDI_MAX_CHANNELS]; + +static int maxspans = 0; +static int maxchans = 0; +static int maxconfs = 0; +static int maxlinks = 0; + +static int default_zone = -1; + +short __dahdi_mulaw[256]; +short __dahdi_alaw[256]; + +#ifndef CONFIG_CALC_XLAW +u_char __dahdi_lin2mu[16384]; + +u_char __dahdi_lin2a[16384]; +#endif + +static u_char defgain[256]; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) +#define __RW_LOCK_UNLOCKED() RW_LOCK_UNLOCKED +#endif + +#ifdef DEFINE_RWLOCK +static DEFINE_RWLOCK(zone_lock); +static DEFINE_RWLOCK(chan_lock); +#else +static rwlock_t zone_lock = RW_LOCK_UNLOCKED; +static rwlock_t chan_lock = RW_LOCK_UNLOCKED; +#endif + +static struct dahdi_zone *tone_zones[DAHDI_TONE_ZONE_MAX]; + +#define NUM_SIGS 10 + +#ifdef DEFINE_RWLOCK +static DEFINE_RWLOCK(ecfactory_list_lock); +#else +static rwlock_t ecfactory_list_lock = __RW_LOCK_UNLOCKED(); +#endif + +static LIST_HEAD(ecfactory_list); + +struct ecfactory { + const struct dahdi_echocan_factory *ec; + struct list_head list; +}; + +int dahdi_register_echocan_factory(const struct dahdi_echocan_factory *ec) +{ + struct ecfactory *cur; + + WARN_ON(!ec->owner); + + write_lock(&ecfactory_list_lock); + + /* make sure it isn't already registered */ + list_for_each_entry(cur, &ecfactory_list, list) { + if (cur->ec == ec) { + write_unlock(&ecfactory_list_lock); + return -EPERM; + } + } + + if (!(cur = kzalloc(sizeof(*cur), GFP_KERNEL))) { + write_unlock(&ecfactory_list_lock); + return -ENOMEM; + } + + cur->ec = ec; + INIT_LIST_HEAD(&cur->list); + + list_add_tail(&cur->list, &ecfactory_list); + + write_unlock(&ecfactory_list_lock); + + return 0; +} + +void dahdi_unregister_echocan_factory(const struct dahdi_echocan_factory *ec) +{ + struct ecfactory *cur, *next; + + write_lock(&ecfactory_list_lock); + + list_for_each_entry_safe(cur, next, &ecfactory_list, list) { + if (cur->ec == ec) { + list_del(&cur->list); + break; + } + } + + write_unlock(&ecfactory_list_lock); +} + +static inline void rotate_sums(void) +{ + /* Rotate where we sum and so forth */ + static int pos = 0; + conf_sums_prev = sums + (DAHDI_MAX_CONF + 1) * pos; + conf_sums = sums + (DAHDI_MAX_CONF + 1) * ((pos + 1) % 3); + conf_sums_next = sums + (DAHDI_MAX_CONF + 1) * ((pos + 2) % 3); + pos = (pos + 1) % 3; + memset(conf_sums_next, 0, maxconfs * sizeof(sumtype)); +} + +/*! + * \return quiescent (idle) signalling states, for the various signalling types + */ +static int dahdi_q_sig(struct dahdi_chan *chan) +{ + int x; + static const unsigned int in_sig[NUM_SIGS][2] = { + { DAHDI_SIG_NONE, 0 }, + { DAHDI_SIG_EM, (DAHDI_ABIT << 8) }, + { DAHDI_SIG_FXSLS, DAHDI_BBIT | (DAHDI_BBIT << 8) }, + { DAHDI_SIG_FXSGS, DAHDI_ABIT | DAHDI_BBIT | ((DAHDI_ABIT | DAHDI_BBIT) << 8) }, + { DAHDI_SIG_FXSKS, DAHDI_BBIT | DAHDI_BBIT | ((DAHDI_ABIT | DAHDI_BBIT) << 8) }, + { DAHDI_SIG_FXOLS, (DAHDI_ABIT << 8) }, + { DAHDI_SIG_FXOGS, DAHDI_BBIT | ((DAHDI_ABIT | DAHDI_BBIT) << 8) }, + { DAHDI_SIG_FXOKS, (DAHDI_ABIT << 8) }, + { DAHDI_SIG_SF, 0 }, + { DAHDI_SIG_EM_E1, DAHDI_DBIT | ((DAHDI_ABIT | DAHDI_DBIT) << 8) }, + }; + + /* must have span to begin with */ + if (!chan->span) + return -1; + + /* if RBS does not apply, return error */ + if (!(chan->span->flags & DAHDI_FLAG_RBS) || !chan->span->rbsbits) + return -1; + + if (chan->sig == DAHDI_SIG_CAS) + return chan->idlebits; + + for (x = 0; x < NUM_SIGS; x++) { + if (in_sig[x][0] == chan->sig) + return in_sig[x][1]; + } + + return -1; /* not found -- error */ +} + +#ifdef CONFIG_PROC_FS +static const char *sigstr(int sig) +{ + switch (sig) { + case DAHDI_SIG_FXSLS: + return "FXSLS"; + case DAHDI_SIG_FXSKS: + return "FXSKS"; + case DAHDI_SIG_FXSGS: + return "FXSGS"; + case DAHDI_SIG_FXOLS: + return "FXOLS"; + case DAHDI_SIG_FXOKS: + return "FXOKS"; + case DAHDI_SIG_FXOGS: + return "FXOGS"; + case DAHDI_SIG_EM: + return "E&M"; + case DAHDI_SIG_EM_E1: + return "E&M-E1"; + case DAHDI_SIG_CLEAR: + return "Clear"; + case DAHDI_SIG_HDLCRAW: + return "HDLCRAW"; + case DAHDI_SIG_HDLCFCS: + return "HDLCFCS"; + case DAHDI_SIG_HDLCNET: + return "HDLCNET"; + case DAHDI_SIG_HARDHDLC: + return "Hardware-assisted HDLC"; + case DAHDI_SIG_MTP2: + return "MTP2"; + case DAHDI_SIG_SLAVE: + return "Slave"; + case DAHDI_SIG_CAS: + return "CAS"; + case DAHDI_SIG_DACS: + return "DACS"; + case DAHDI_SIG_DACS_RBS: + return "DACS+RBS"; + case DAHDI_SIG_SF: + return "SF (ToneOnly)"; + case DAHDI_SIG_NONE: + default: + return "Unconfigured"; + } +} + +static int fill_alarm_string(char *buf, int count, int alarms) +{ + int len; + + if (alarms <= 0) + return 0; + + len = snprintf(buf, count, "%s%s%s%s%s%s", + (alarms & DAHDI_ALARM_BLUE) ? "BLUE " : "", + (alarms & DAHDI_ALARM_YELLOW) ? "YELLOW " : "", + (alarms & DAHDI_ALARM_RED) ? "RED " : "", + (alarms & DAHDI_ALARM_LOOPBACK) ? "LOOP " : "", + (alarms & DAHDI_ALARM_RECOVER) ? "RECOVERING " : "", + (alarms & DAHDI_ALARM_NOTOPEN) ? "NOTOPEN " : ""); + + if (len > 0) + buf[--len] = '\0'; /* strip last space */ + + return len; +} + +static int dahdi_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int x, len = 0, real_count; + long span; + + /* In Linux 2.6, page is always PROC_BLOCK_SIZE=(PAGE_SIZE-1024) bytes. + * 0name) + len += snprintf(page + len, count - len, "Span %ld: %s ", + span, spans[span]->name); + if (spans[span]->desc) + len += snprintf(page + len, count - len, "\"%s\"", + spans[span]->desc); + else + len += snprintf(page + len, count - len, "\"\""); + + if (spans[span] == master) + len += snprintf(page + len, count - len, " (MASTER)"); + + if (spans[span]->lineconfig) { + /* framing first */ + if (spans[span]->lineconfig & DAHDI_CONFIG_B8ZS) + len += snprintf(page + len, count - len, " B8ZS/"); + else if (spans[span]->lineconfig & DAHDI_CONFIG_AMI) + len += snprintf(page + len, count - len, " AMI/"); + else if (spans[span]->lineconfig & DAHDI_CONFIG_HDB3) + len += snprintf(page + len, count - len, " HDB3/"); + /* then coding */ + if (spans[span]->lineconfig & DAHDI_CONFIG_ESF) + len += snprintf(page + len, count - len, "ESF"); + else if (spans[span]->lineconfig & DAHDI_CONFIG_D4) + len += snprintf(page + len, count - len, "D4"); + else if (spans[span]->lineconfig & DAHDI_CONFIG_CCS) + len += snprintf(page + len, count - len, "CCS"); + /* E1's can enable CRC checking */ + if (spans[span]->lineconfig & DAHDI_CONFIG_CRC4) + len += snprintf(page + len, count - len, "/CRC4"); + } + + len += snprintf(page + len, count - len, " "); + + /* list alarms */ + len += fill_alarm_string(page + len, count - len, spans[span]->alarms); + if (spans[span]->syncsrc && + (spans[span]->syncsrc == spans[span]->spanno)) + len += snprintf(page + len, count - len, "ClockSource "); + len += snprintf(page + len, count - len, "\n"); + if (spans[span]->bpvcount) + len += snprintf(page + len, count - len, "\tBPV count: %d\n", + spans[span]->bpvcount); + if (spans[span]->crc4count) + len += snprintf(page + len, count - len, + "\tCRC4 error count: %d\n", + spans[span]->crc4count); + if (spans[span]->ebitcount) + len += snprintf(page + len, count - len, + "\tE-bit error count: %d\n", + spans[span]->ebitcount); + if (spans[span]->fascount) + len += snprintf(page + len, count - len, + "\tFAS error count: %d\n", + spans[span]->fascount); + if (spans[span]->irqmisses) + len += snprintf(page + len, count - len, + "\tIRQ misses: %d\n", + spans[span]->irqmisses); + if (spans[span]->timingslips) + len += snprintf(page + len, count - len, + "\tTiming slips: %d\n", + spans[span]->timingslips); + len += snprintf(page + len, count - len, "\n"); + + for (x = 0; x < spans[span]->channels; x++) { + struct dahdi_chan *chan = spans[span]->chans[x]; + + if (chan->name) + len += snprintf(page + len, count - len, + "\t%4d %s ", chan->channo, chan->name); + + if (chan->sig) { + if (chan->sig == DAHDI_SIG_SLAVE) + len += snprintf(page+len, count-len, "%s ", + sigstr(chan->master->sig)); + else { + len += snprintf(page+len, count-len, "%s ", + sigstr(chan->sig)); + if (chan->nextslave && + (chan->master->channo == chan->channo)) + len += snprintf(page+len, count-len, + "Master "); + } + } + + if (test_bit(DAHDI_FLAGBIT_OPEN, &chan->flags)) + len += snprintf(page + len, count - len, "(In use) "); + +#ifdef OPTIMIZE_CHANMUTE + if (chan->chanmute) + len += snprintf(page+len, count-len, "(no pcm) "); +#endif + + len += fill_alarm_string(page+len, count-len, + chan->chan_alarms); + + if (chan->ec_factory) + len += snprintf(page+len, count-len, "(SWEC: %s) ", + chan->ec_factory->name); + + if (chan->ec_state) + len += snprintf(page+len, count-len, "(EC: %s) ", + chan->ec_state->ops->name); + + len += snprintf(page+len, count-len, "\n"); + + /* If everything printed so far is before beginning + * of request */ + if (len <= off) { + off -= len; + len = 0; + } + + /* stop if we've already generated enough */ + if (len > off + count) + break; + /* stop if we're NEAR danger limit. let it be -128 bytes. */ + if (len > count-128) + break; + } + count = real_count; + /* If everything printed so far is before beginning of request */ + if (len <= off) { + off = 0; + len = 0; + } + *start = page + off; + len -= off; /* un-count any remaining offset */ + *eof = 1; + if (len > count) + len = count; /* don't return bytes not asked for */ + return len; +} +#endif + +static int dahdi_first_empty_alias(void) +{ + /* Find the first conference which has no alias pointing to it */ + int x; + for (x=1;x 0; x--) { + if (confrev[x]) { + maxconfs = x + 1; + return; + } + } + + maxconfs = 0; +} + +static void recalc_maxlinks(void) +{ + int x; + + for (x = DAHDI_MAX_CONF - 1; x > 0; x--) { + if (conf_links[x].src || conf_links[x].dst) { + maxlinks = x + 1; + return; + } + } + + maxlinks = 0; +} + +static int dahdi_first_empty_conference(void) +{ + /* Find the first conference which has no alias */ + int x; + + for (x = DAHDI_MAX_CONF - 1; x > 0; x--) { + if (!confalias[x]) + return x; + } + + return -1; +} + +static int dahdi_get_conf_alias(int x) +{ + int a; + + if (confalias[x]) + return confalias[x]; + + /* Allocate an alias */ + a = dahdi_first_empty_alias(); + confalias[x] = a; + confrev[a] = x; + + /* Highest conference may have changed */ + recalc_maxconfs(); + + return a; +} + +static void dahdi_check_conf(int x) +{ + int y; + + /* return if no valid conf number */ + if (x <= 0) + return; + + /* Return if there is no alias */ + if (!confalias[x]) + return; + + for (y = 0; y < maxchans; y++) { + if (chans[y] && (chans[y]->confna == x) && + ((chans[y]->confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_CONF || + (chans[y]->confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_CONFANN || + (chans[y]->confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_CONFMON || + (chans[y]->confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_CONFANNMON || + (chans[y]->confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_REALANDPSEUDO)) { + return; + } + } + + /* If we get here, nobody is in the conference anymore. Clear it out + both forward and reverse */ + confrev[confalias[x]] = 0; + confalias[x] = 0; + + /* Highest conference may have changed */ + recalc_maxconfs(); +} + +/* enqueue an event on a channel */ +static void __qevent(struct dahdi_chan *chan, int event) +{ + /* if full, ignore */ + if ((chan->eventoutidx == 0) && (chan->eventinidx == (DAHDI_MAX_EVENTSIZE - 1))) + return; + + /* if full, ignore */ + if (chan->eventinidx == (chan->eventoutidx - 1)) + return; + + /* save the event */ + chan->eventbuf[chan->eventinidx++] = event; + + /* wrap the index, if necessary */ + if (chan->eventinidx >= DAHDI_MAX_EVENTSIZE) + chan->eventinidx = 0; + + /* wake em all up */ + if (chan->iomask & DAHDI_IOMUX_SIGEVENT) + wake_up_interruptible(&chan->eventbufq); + + wake_up_interruptible(&chan->readbufq); + wake_up_interruptible(&chan->writebufq); + wake_up_interruptible(&chan->sel); + + return; +} + +void dahdi_qevent_nolock(struct dahdi_chan *chan, int event) +{ + __qevent(chan, event); +} + +void dahdi_qevent_lock(struct dahdi_chan *chan, int event) +{ + unsigned long flags; + spin_lock_irqsave(&chan->lock, flags); + __qevent(chan, event); + spin_unlock_irqrestore(&chan->lock, flags); +} + +/* sleep in user space until woken up. Equivilant of tsleep() in BSD */ +static int schluffen(wait_queue_head_t *q) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(q, &wait); + current->state = TASK_INTERRUPTIBLE; + + if (!signal_pending(current)) + schedule(); + + current->state = TASK_RUNNING; + remove_wait_queue(q, &wait); + + if (signal_pending(current)) + return -ERESTARTSYS; + + return 0; +} + +static inline void calc_fcs(struct dahdi_chan *ss, int inwritebuf) +{ + int x; + unsigned int fcs = PPP_INITFCS; + unsigned char *data = ss->writebuf[inwritebuf]; + int len = ss->writen[inwritebuf]; + + /* Not enough space to do FCS calculation */ + if (len < 2) + return; + + for (x = 0; x < len - 2; x++) + fcs = PPP_FCS(fcs, data[x]); + + fcs ^= 0xffff; + /* Send out the FCS */ + data[len - 2] = (fcs & 0xff); + data[len - 1] = (fcs >> 8) & 0xff; +} + +static int dahdi_reallocbufs(struct dahdi_chan *ss, int blocksize, int numbufs) +{ + unsigned char *newtxbuf = NULL; + unsigned char *newrxbuf = NULL; + unsigned char *oldtxbuf = NULL; + unsigned char *oldrxbuf = NULL; + unsigned long flags; + int x; + + /* Check numbufs */ + if (numbufs < 2) + numbufs = 2; + + if (numbufs > DAHDI_MAX_NUM_BUFS) + numbufs = DAHDI_MAX_NUM_BUFS; + + /* We need to allocate our buffers now */ + if (blocksize) { + newtxbuf = kzalloc(blocksize * numbufs, GFP_KERNEL); + if (NULL == newtxbuf) + return -ENOMEM; + newrxbuf = kzalloc(blocksize * numbufs, GFP_KERNEL); + if (NULL == newrxbuf) { + kfree(newtxbuf); + return -ENOMEM; + } + } + + /* Now that we've allocated our new buffers, we can safely + move things around... */ + + spin_lock_irqsave(&ss->lock, flags); + + ss->blocksize = blocksize; /* set the blocksize */ + oldrxbuf = ss->readbuf[0]; /* Keep track of the old buffer */ + oldtxbuf = ss->writebuf[0]; + ss->readbuf[0] = NULL; + + if (newrxbuf) { + BUG_ON(NULL == newtxbuf); + for (x = 0; x < numbufs; x++) { + ss->readbuf[x] = newrxbuf + x * blocksize; + ss->writebuf[x] = newtxbuf + x * blocksize; + } + } else { + for (x = 0; x < numbufs; x++) { + ss->readbuf[x] = NULL; + ss->writebuf[x] = NULL; + } + } + + /* Mark all buffers as empty */ + for (x = 0; x < numbufs; x++) { + ss->writen[x] = + ss->writeidx[x]= + ss->readn[x]= + ss->readidx[x] = 0; + } + + /* Keep track of where our data goes (if it goes + anywhere at all) */ + if (newrxbuf) { + ss->inreadbuf = 0; + ss->inwritebuf = 0; + } else { + ss->inreadbuf = -1; + ss->inwritebuf = -1; + } + + ss->outreadbuf = -1; + ss->outwritebuf = -1; + ss->numbufs = numbufs; + + if ((ss->txbufpolicy == DAHDI_POLICY_WHEN_FULL) || (ss->txbufpolicy == DAHDI_POLICY_HALF_FULL)) + ss->txdisable = 1; + else + ss->txdisable = 0; + + if (ss->rxbufpolicy == DAHDI_POLICY_WHEN_FULL) + ss->rxdisable = 1; + else + ss->rxdisable = 0; + + spin_unlock_irqrestore(&ss->lock, flags); + + kfree(oldtxbuf); + kfree(oldrxbuf); + + return 0; +} + +static int dahdi_hangup(struct dahdi_chan *chan); +static void dahdi_set_law(struct dahdi_chan *chan, int law); + +/* Pull a DAHDI_CHUNKSIZE piece off the queue. Returns + 0 on success or -1 on failure. If failed, provides + silence */ +static int __buf_pull(struct confq *q, u_char *data, struct dahdi_chan *c, char *label) +{ + int oldoutbuf = q->outbuf; + /* Ain't nuffin to read */ + if (q->outbuf < 0) { + if (data) + memset(data, DAHDI_LIN2X(0,c), DAHDI_CHUNKSIZE); + return -1; + } + if (data) + memcpy(data, q->buf[q->outbuf], DAHDI_CHUNKSIZE); + q->outbuf = (q->outbuf + 1) % DAHDI_CB_SIZE; + + /* Won't be nuffin next time */ + if (q->outbuf == q->inbuf) { + q->outbuf = -1; + } + + /* If they thought there was no space then + there is now where we just read */ + if (q->inbuf < 0) + q->inbuf = oldoutbuf; + return 0; +} + +/* Returns a place to put stuff, or NULL if there is + no room */ + +static u_char *__buf_pushpeek(struct confq *q) +{ + if (q->inbuf < 0) + return NULL; + return q->buf[q->inbuf]; +} + +static u_char *__buf_peek(struct confq *q) +{ + if (q->outbuf < 0) + return NULL; + return q->buf[q->outbuf]; +} + +#ifdef BUF_MUNGE +static u_char *__buf_cpush(struct confq *q) +{ + int pos; + /* If we have no space, return where the + last space that we *did* have was */ + if (q->inbuf > -1) + return NULL; + pos = q->outbuf - 1; + if (pos < 0) + pos += DAHDI_CB_SIZE; + return q->buf[pos]; +} + +static void __buf_munge(struct dahdi_chan *chan, u_char *old, u_char *new) +{ + /* Run a weighted average of the old and new, in order to + mask a missing sample */ + int x; + int val; + for (x=0;xinbuf; + if (q->inbuf < 0) { + return -1; + } + if (data) + /* Copy in the data */ + memcpy(q->buf[q->inbuf], data, DAHDI_CHUNKSIZE); + + /* Advance the inbuf pointer */ + q->inbuf = (q->inbuf + 1) % DAHDI_CB_SIZE; + + if (q->inbuf == q->outbuf) { + /* No space anymore... */ + q->inbuf = -1; + } + /* If they don't think data is ready, let + them know it is now */ + if (q->outbuf < 0) { + q->outbuf = oldinbuf; + } + return 0; +} + +static void reset_conf(struct dahdi_chan *chan) +{ + int x; + + /* Empty out buffers and reset to initialization */ + + for (x = 0; x < DAHDI_CB_SIZE; x++) + chan->confin.buf[x] = chan->confin.buffer + DAHDI_CHUNKSIZE * x; + + chan->confin.inbuf = 0; + chan->confin.outbuf = -1; + + for (x = 0; x < DAHDI_CB_SIZE; x++) + chan->confout.buf[x] = chan->confout.buffer + DAHDI_CHUNKSIZE * x; + + chan->confout.inbuf = 0; + chan->confout.outbuf = -1; +} + + +static const struct dahdi_echocan_factory *find_echocan(const char *name) +{ + struct ecfactory *cur; + char name_upper[strlen(name) + 1]; + char *c; + const char *d; + char modname_buf[128] = "dahdi_echocan_"; + unsigned int tried_once = 0; + + for (c = name_upper, d = name; *d; c++, d++) { + *c = toupper(*d); + } + + *c = '\0'; + +retry: + read_lock(&ecfactory_list_lock); + + list_for_each_entry(cur, &ecfactory_list, list) { + if (!strcmp(name_upper, cur->ec->name)) { + if (try_module_get(cur->ec->owner)) { + read_unlock(&ecfactory_list_lock); + return cur->ec; + } else { + read_unlock(&ecfactory_list_lock); + return NULL; + } + } + } + + read_unlock(&ecfactory_list_lock); + + if (tried_once) { + return NULL; + } + + /* couldn't find it, let's try to load it */ + + for (c = &modname_buf[strlen(modname_buf)], d = name; *d; c++, d++) { + *c = tolower(*d); + } + + request_module("%s", modname_buf); + + tried_once = 1; + + /* and try one more time */ + goto retry; +} + +static void release_echocan(const struct dahdi_echocan_factory *ec) +{ + if (ec) + module_put(ec->owner); +} + +/** + * close_channel - close the channel, resetting any channel variables + * @chan: the dahdi_chan to close + * + * This function might be called before the channel is placed on the global + * array of channels, (chans), and therefore, neither this function nor it's + * children should depend on the dahdi_chan.channo member which is not set yet. + */ +static void close_channel(struct dahdi_chan *chan) +{ + unsigned long flags; + void *rxgain = NULL; + struct dahdi_echocan_state *ec_state; + const struct dahdi_echocan_factory *ec_current; + int oldconf; + short *readchunkpreec; +#ifdef CONFIG_DAHDI_PPP + struct ppp_channel *ppp; +#endif + + might_sleep(); + + /* XXX Buffers should be send out before reallocation!!! XXX */ + if (!(chan->flags & DAHDI_FLAG_NOSTDTXRX)) + dahdi_reallocbufs(chan, 0, 0); + spin_lock_irqsave(&chan->lock, flags); +#ifdef CONFIG_DAHDI_PPP + ppp = chan->ppp; + chan->ppp = NULL; +#endif + ec_state = chan->ec_state; + chan->ec_state = NULL; + ec_current = chan->ec_current; + chan->ec_current = NULL; + readchunkpreec = chan->readchunkpreec; + chan->readchunkpreec = NULL; + chan->curtone = NULL; + if (chan->curzone) + atomic_dec(&chan->curzone->refcount); + chan->curzone = NULL; + chan->cadencepos = 0; + chan->pdialcount = 0; + dahdi_hangup(chan); + chan->itimerset = chan->itimer = 0; + chan->pulsecount = 0; + chan->pulsetimer = 0; + chan->ringdebtimer = 0; + init_waitqueue_head(&chan->sel); + init_waitqueue_head(&chan->readbufq); + init_waitqueue_head(&chan->writebufq); + init_waitqueue_head(&chan->eventbufq); + init_waitqueue_head(&chan->txstateq); + chan->txdialbuf[0] = '\0'; + chan->digitmode = DIGIT_MODE_DTMF; + chan->dialing = 0; + chan->afterdialingtimer = 0; + /* initialize IO MUX mask */ + chan->iomask = 0; + /* save old conf number, if any */ + oldconf = chan->confna; + /* initialize conference variables */ + chan->_confn = 0; + if ((chan->sig & __DAHDI_SIG_DACS) != __DAHDI_SIG_DACS) { + chan->confna = 0; + chan->confmode = 0; + } + chan->confmute = 0; + /* release conference resource, if any to release */ + if (oldconf) dahdi_check_conf(oldconf); + chan->gotgs = 0; + reset_conf(chan); + + if (chan->gainalloc && chan->rxgain) + rxgain = chan->rxgain; + + chan->rxgain = defgain; + chan->txgain = defgain; + chan->gainalloc = 0; + chan->eventinidx = chan->eventoutidx = 0; + chan->flags &= ~(DAHDI_FLAG_LOOPED | DAHDI_FLAG_LINEAR | DAHDI_FLAG_PPP | DAHDI_FLAG_SIGFREEZE); + + dahdi_set_law(chan,0); + + memset(chan->conflast, 0, sizeof(chan->conflast)); + memset(chan->conflast1, 0, sizeof(chan->conflast1)); + memset(chan->conflast2, 0, sizeof(chan->conflast2)); + + if (chan->span && chan->span->dacs && oldconf) + chan->span->dacs(chan, NULL); + + if (ec_state) { + ec_state->ops->echocan_free(chan, ec_state); + release_echocan(ec_current); + } + + spin_unlock_irqrestore(&chan->lock, flags); + + if (rxgain) + kfree(rxgain); + if (readchunkpreec) + kfree(readchunkpreec); + +#ifdef CONFIG_DAHDI_PPP + if (ppp) { + tasklet_kill(&chan->ppp_calls); + skb_queue_purge(&chan->ppp_rq); + ppp_unregister_channel(ppp); + kfree(ppp); + } +#endif + +} + +static int free_tone_zone(int num) +{ + struct dahdi_zone *z = NULL; + int res = 0; + + if ((num >= DAHDI_TONE_ZONE_MAX) || (num < 0)) + return -EINVAL; + + write_lock(&zone_lock); + if (tone_zones[num]) { + if (!atomic_read(&tone_zones[num]->refcount)) { + z = tone_zones[num]; + tone_zones[num] = NULL; + } else { + res = -EBUSY; + } + } + write_unlock(&zone_lock); + + if (z) + kfree(z); + + return res; +} + +static int dahdi_register_tone_zone(int num, struct dahdi_zone *zone) +{ + int res = 0; + + if ((num >= DAHDI_TONE_ZONE_MAX) || (num < 0)) + return -EINVAL; + + write_lock(&zone_lock); + if (tone_zones[num]) { + res = -EINVAL; + } else { + res = 0; + tone_zones[num] = zone; + } + write_unlock(&zone_lock); + + if (!res) + module_printk(KERN_INFO, "Registered tone zone %d (%s)\n", num, zone->name); + + return res; +} + +static int start_tone_digit(struct dahdi_chan *chan, int tone) +{ + struct dahdi_tone *playtone = NULL; + int base, max; + + if (!chan->curzone) + return -ENODATA; + + switch (chan->digitmode) { + case DIGIT_MODE_DTMF: + /* Set dialing so that a dial operation doesn't interrupt this tone */ + chan->dialing = 1; + base = DAHDI_TONE_DTMF_BASE; + max = DAHDI_TONE_DTMF_MAX; + break; + case DIGIT_MODE_MFR2_FWD: + base = DAHDI_TONE_MFR2_FWD_BASE; + max = DAHDI_TONE_MFR2_FWD_MAX; + break; + case DIGIT_MODE_MFR2_REV: + base = DAHDI_TONE_MFR2_REV_BASE; + max = DAHDI_TONE_MFR2_REV_MAX; + break; + default: + return -EINVAL; + } + + if ((tone < base) || (tone > max)) + return -EINVAL; + + switch (chan->digitmode) { + case DIGIT_MODE_DTMF: + playtone = &chan->curzone->dtmf_continuous[tone - base]; + break; + case DIGIT_MODE_MFR2_FWD: + playtone = &chan->curzone->mfr2_fwd_continuous[tone - base]; + break; + case DIGIT_MODE_MFR2_REV: + playtone = &chan->curzone->mfr2_rev_continuous[tone - base]; + break; + } + + if (!playtone || !playtone->tonesamples) + return -ENOSYS; + + chan->curtone = playtone; + + return 0; +} + +static int start_tone(struct dahdi_chan *chan, int tone) +{ + int res = -EINVAL; + + /* Stop the current tone, no matter what */ + chan->tonep = 0; + chan->curtone = NULL; + chan->pdialcount = 0; + chan->txdialbuf[0] = '\0'; + chan->dialing = 0; + + if (tone == -1) { + /* Just stop the current tone */ + res = 0; + } else if (!chan->curzone) { + static int __warnonce = 1; + if (__warnonce) { + __warnonce = 0; + /* The tonezones are loaded by dahdi_cfg based on /etc/dahdi/system.conf. */ + module_printk(KERN_WARNING, "DAHDI: Cannot start tones until tone zone is loaded.\n"); + } + /* Note that no tone zone exists at the moment */ + res = -ENODATA; + } else if ((tone >= 0 && tone <= DAHDI_TONE_MAX)) { + /* Have a tone zone */ + if (chan->curzone->tones[tone]) { + chan->curtone = chan->curzone->tones[tone]; + res = 0; + } else { /* Indicate that zone is loaded but no such tone exists */ + res = -ENOSYS; + } + } else if (chan->digitmode == DIGIT_MODE_DTMF || + chan->digitmode == DIGIT_MODE_MFR2_FWD || + chan->digitmode == DIGIT_MODE_MFR2_REV) { + res = start_tone_digit(chan, tone); + } else { + chan->dialing = 0; + res = -EINVAL; + } + + if (chan->curtone) + dahdi_init_tone_state(&chan->ts, chan->curtone); + + return res; +} + +static int set_tone_zone(struct dahdi_chan *chan, int zone) +{ + int res = 0; + struct dahdi_zone *z; + + /* Do not call with the channel locked. */ + + if (zone == -1) + zone = default_zone; + + if ((zone >= DAHDI_TONE_ZONE_MAX) || (zone < 0)) + return -EINVAL; + + read_lock(&zone_lock); + + if ((z = tone_zones[zone])) { + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + + if (chan->curzone) + atomic_dec(&chan->curzone->refcount); + + atomic_inc(&z->refcount); + chan->curzone = z; + chan->tonezone = zone; + memcpy(chan->ringcadence, z->ringcadence, sizeof(chan->ringcadence)); + + spin_unlock_irqrestore(&chan->lock, flags); + } else { + res = -ENODATA; + } + + read_unlock(&zone_lock); + + return res; +} + +static void dahdi_set_law(struct dahdi_chan *chan, int law) +{ + if (!law) { + if (chan->deflaw) + law = chan->deflaw; + else + if (chan->span) law = chan->span->deflaw; + else law = DAHDI_LAW_MULAW; + } + if (law == DAHDI_LAW_ALAW) { + chan->xlaw = __dahdi_alaw; +#ifdef CONFIG_CALC_XLAW + chan->lineartoxlaw = __dahdi_lineartoalaw; +#else + chan->lin2x = __dahdi_lin2a; +#endif + } else { + chan->xlaw = __dahdi_mulaw; +#ifdef CONFIG_CALC_XLAW + chan->lineartoxlaw = __dahdi_lineartoulaw; +#else + chan->lin2x = __dahdi_lin2mu; +#endif + } +} + +static int dahdi_chan_reg(struct dahdi_chan *chan) +{ + int x; + unsigned long flags; + + might_sleep(); + + spin_lock_init(&chan->lock); + if (!chan->master) + chan->master = chan; + if (!chan->readchunk) + chan->readchunk = chan->sreadchunk; + if (!chan->writechunk) + chan->writechunk = chan->swritechunk; + dahdi_set_law(chan, 0); + close_channel(chan); + + write_lock_irqsave(&chan_lock, flags); + for (x = 1; x < DAHDI_MAX_CHANNELS; x++) { + if (chans[x]) + continue; + + chans[x] = chan; + if (maxchans < x + 1) + maxchans = x + 1; + chan->channo = x; + write_unlock_irqrestore(&chan_lock, flags); + /* set this AFTER running close_channel() so that + HDLC channels wont cause hangage */ + set_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags); + break; + } + + if (DAHDI_MAX_CHANNELS == x) { + write_unlock_irqrestore(&chan_lock, flags); + module_printk(KERN_ERR, "No more channels available\n"); + return -ENOMEM; + } + + return 0; +} + +char *dahdi_lboname(int x) +{ + if ((x < 0) || (x > 7)) + return "Unknown"; + return dahdi_txlevelnames[x]; +} + +#if defined(CONFIG_DAHDI_NET) || defined(CONFIG_DAHDI_PPP) +static inline void print_debug_writebuf(struct dahdi_chan* ss, struct sk_buff *skb, int oldbuf) +{ +#ifdef CONFIG_DAHDI_DEBUG + int x; + + module_printk(KERN_NOTICE, "Buffered %d bytes to go out in buffer %d\n", ss->writen[oldbuf], oldbuf); + module_printk(KERN_DEBUG ""); + for (x=0;xwriten[oldbuf];x++) + printk("%02x ", ss->writebuf[oldbuf][x]); + printk("\n"); +#endif +} +#endif + +#ifdef CONFIG_DAHDI_NET +#ifdef NEW_HDLC_INTERFACE +static int dahdi_net_open(struct net_device *dev) +{ + int res = hdlc_open(dev); + struct dahdi_chan *ms = dev_to_ztchan(dev); + +/* if (!dev->hard_start_xmit) return res; is this really necessary? --byg */ + if (res) /* this is necessary to avoid kernel panic when UNSPEC link encap, proven --byg */ + return res; +#else +static int dahdi_net_open(hdlc_device *hdlc) +{ + struct dahdi_chan *ms = hdlc_to_ztchan(hdlc); + int res; +#endif + if (!ms) { + module_printk(KERN_NOTICE, "dahdi_net_open: nothing??\n"); + return -EINVAL; + } + if (test_bit(DAHDI_FLAGBIT_OPEN, &ms->flags)) { + module_printk(KERN_NOTICE, "%s is already open!\n", ms->name); + return -EBUSY; + } + if (!(ms->flags & DAHDI_FLAG_NETDEV)) { + module_printk(KERN_NOTICE, "%s is not a net device!\n", ms->name); + return -EINVAL; + } + ms->txbufpolicy = DAHDI_POLICY_IMMEDIATE; + ms->rxbufpolicy = DAHDI_POLICY_IMMEDIATE; + + res = dahdi_reallocbufs(ms, DAHDI_DEFAULT_MTU_MRU, DAHDI_DEFAULT_NUM_BUFS); + if (res) + return res; + + fasthdlc_init(&ms->rxhdlc, (ms->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64); + fasthdlc_init(&ms->txhdlc, (ms->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64); + ms->infcs = PPP_INITFCS; + + netif_start_queue(ztchan_to_dev(ms)); + +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "DAHDINET: Opened channel %d name %s\n", ms->channo, ms->name); +#endif + return 0; +} + +static int dahdi_register_hdlc_device(struct net_device *dev, const char *dev_name) +{ + int result; + + if (dev_name && *dev_name) { + if ((result = dev_alloc_name(dev, dev_name)) < 0) + return result; + } + result = register_netdev(dev); + if (result != 0) + return -EIO; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,14) + if (netif_carrier_ok(dev)) + netif_carrier_off(dev); /* no carrier until DCD goes up */ +#endif + return 0; +} + +#ifdef NEW_HDLC_INTERFACE +static int dahdi_net_stop(struct net_device *dev) +{ + hdlc_device *h = dev_to_hdlc(dev); + struct dahdi_hdlc *hdlc = h->priv; + +#else +static void dahdi_net_close(hdlc_device *hdlc) +{ +#endif + struct dahdi_chan *ms = hdlc_to_ztchan(hdlc); + if (!ms) { +#ifdef NEW_HDLC_INTERFACE + module_printk(KERN_NOTICE, "dahdi_net_stop: nothing??\n"); + return 0; +#else + module_printk(KERN_NOTICE, "dahdi_net_close: nothing??\n"); + return; +#endif + } + if (!(ms->flags & DAHDI_FLAG_NETDEV)) { +#ifdef NEW_HDLC_INTERFACE + module_printk(KERN_NOTICE, "dahdi_net_stop: %s is not a net device!\n", ms->name); + return 0; +#else + module_printk(KERN_NOTICE, "dahdi_net_close: %s is not a net device!\n", ms->name); + return; +#endif + } + /* Not much to do here. Just deallocate the buffers */ + netif_stop_queue(ztchan_to_dev(ms)); + dahdi_reallocbufs(ms, 0, 0); + hdlc_close(dev); +#ifdef NEW_HDLC_INTERFACE + return 0; +#else + return; +#endif +} + +#ifdef NEW_HDLC_INTERFACE +/* kernel 2.4.20+ has introduced attach function, dunno what to do, + just copy sources from dscc4 to be sure and ready for further mastering, + NOOP right now (i.e. really a stub) --byg */ +static int dahdi_net_attach(struct net_device *dev, unsigned short encoding, + unsigned short parity) +{ +/* struct net_device *dev = hdlc_to_dev(hdlc); + struct dscc4_dev_priv *dpriv = dscc4_priv(dev); + + if (encoding != ENCODING_NRZ && + encoding != ENCODING_NRZI && + encoding != ENCODING_FM_MARK && + encoding != ENCODING_FM_SPACE && + encoding != ENCODING_MANCHESTER) + return -EINVAL; + + if (parity != PARITY_NONE && + parity != PARITY_CRC16_PR0_CCITT && + parity != PARITY_CRC16_PR1_CCITT && + parity != PARITY_CRC32_PR0_CCITT && + parity != PARITY_CRC32_PR1_CCITT) + return -EINVAL; + + dpriv->encoding = encoding; + dpriv->parity = parity;*/ + return 0; +} +#endif + +static struct dahdi_hdlc *dahdi_hdlc_alloc(void) +{ + return kzalloc(sizeof(struct dahdi_hdlc), GFP_KERNEL); +} + +#ifdef NEW_HDLC_INTERFACE +static int dahdi_xmit(struct sk_buff *skb, struct net_device *dev) +{ + /* FIXME: this construction seems to be not very optimal for me but I could find nothing better at the moment (Friday, 10PM :( ) --byg */ +/* struct dahdi_chan *ss = hdlc_to_ztchan(list_entry(dev, struct dahdi_hdlc, netdev.netdev));*/ + struct dahdi_chan *ss = dev_to_ztchan(dev); + struct net_device_stats *stats = hdlc_stats(dev); + +#else +static int dahdi_xmit(hdlc_device *hdlc, struct sk_buff *skb) +{ + struct dahdi_chan *ss = hdlc_to_ztchan(hdlc); + struct net_device *dev = &ss->hdlcnetdev->netdev.netdev; + struct net_device_stats *stats = &ss->hdlcnetdev->netdev.stats; +#endif + int retval = 1; + int x,oldbuf; + unsigned int fcs; + unsigned char *data; + unsigned long flags; + /* See if we have any buffers */ + spin_lock_irqsave(&ss->lock, flags); + if (skb->len > ss->blocksize - 2) { + module_printk(KERN_ERR, "dahdi_xmit(%s): skb is too large (%d > %d)\n", dev->name, skb->len, ss->blocksize -2); + stats->tx_dropped++; + retval = 0; + } else if (ss->inwritebuf >= 0) { + /* We have a place to put this packet */ + /* XXX We should keep the SKB and avoid the memcpy XXX */ + data = ss->writebuf[ss->inwritebuf]; + memcpy(data, skb->data, skb->len); + ss->writen[ss->inwritebuf] = skb->len; + ss->writeidx[ss->inwritebuf] = 0; + /* Calculate the FCS */ + fcs = PPP_INITFCS; + for (x=0;xlen;x++) + fcs = PPP_FCS(fcs, data[x]); + /* Invert it */ + fcs ^= 0xffff; + /* Send it out LSB first */ + data[ss->writen[ss->inwritebuf]++] = (fcs & 0xff); + data[ss->writen[ss->inwritebuf]++] = (fcs >> 8) & 0xff; + /* Advance to next window */ + oldbuf = ss->inwritebuf; + ss->inwritebuf = (ss->inwritebuf + 1) % ss->numbufs; + + if (ss->inwritebuf == ss->outwritebuf) { + /* Whoops, no more space. */ + ss->inwritebuf = -1; + + netif_stop_queue(ztchan_to_dev(ss)); + } + if (ss->outwritebuf < 0) { + /* Let the interrupt handler know there's + some space for us */ + ss->outwritebuf = oldbuf; + } + dev->trans_start = jiffies; + stats->tx_packets++; + stats->tx_bytes += ss->writen[oldbuf]; + print_debug_writebuf(ss, skb, oldbuf); + retval = 0; + /* Free the SKB */ + dev_kfree_skb_any(skb); + } + spin_unlock_irqrestore(&ss->lock, flags); + return retval; +} + +#ifdef NEW_HDLC_INTERFACE +static int dahdi_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + return hdlc_ioctl(dev, ifr, cmd); +} +#else +static int dahdi_net_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd) +{ + return -EIO; +} +#endif + +#endif + +#ifdef CONFIG_DAHDI_PPP + +static int dahdi_ppp_xmit(struct ppp_channel *ppp, struct sk_buff *skb) +{ + + /* + * If we can't handle the packet right now, return 0. If we + * we handle or drop it, return 1. Always free if we return + * 1 and never if we return 0 + */ + struct dahdi_chan *ss = ppp->private; + int x,oldbuf; + unsigned int fcs; + unsigned char *data; + unsigned long flags; + int retval = 0; + + /* See if we have any buffers */ + spin_lock_irqsave(&ss->lock, flags); + if (!(test_bit(DAHDI_FLAGBIT_OPEN, &ss->flags))) { + module_printk(KERN_ERR, "Can't transmit on closed channel\n"); + retval = 1; + } else if (skb->len > ss->blocksize - 4) { + module_printk(KERN_ERR, "dahdi_ppp_xmit(%s): skb is too large (%d > %d)\n", ss->name, skb->len, ss->blocksize -2); + retval = 1; + } else if (ss->inwritebuf >= 0) { + /* We have a place to put this packet */ + /* XXX We should keep the SKB and avoid the memcpy XXX */ + data = ss->writebuf[ss->inwritebuf]; + /* Start with header of two bytes */ + /* Add "ALL STATIONS" and "UNNUMBERED" */ + data[0] = 0xff; + data[1] = 0x03; + ss->writen[ss->inwritebuf] = 2; + + /* Copy real data and increment amount written */ + memcpy(data + 2, skb->data, skb->len); + + ss->writen[ss->inwritebuf] += skb->len; + + /* Re-set index back to zero */ + ss->writeidx[ss->inwritebuf] = 0; + + /* Calculate the FCS */ + fcs = PPP_INITFCS; + for (x=0;xlen + 2;x++) + fcs = PPP_FCS(fcs, data[x]); + /* Invert it */ + fcs ^= 0xffff; + + /* Point past the real data now */ + data += (skb->len + 2); + + /* Send FCS out LSB first */ + data[0] = (fcs & 0xff); + data[1] = (fcs >> 8) & 0xff; + + /* Account for FCS length */ + ss->writen[ss->inwritebuf]+=2; + + /* Advance to next window */ + oldbuf = ss->inwritebuf; + ss->inwritebuf = (ss->inwritebuf + 1) % ss->numbufs; + + if (ss->inwritebuf == ss->outwritebuf) { + /* Whoops, no more space. */ + ss->inwritebuf = -1; + } + if (ss->outwritebuf < 0) { + /* Let the interrupt handler know there's + some space for us */ + ss->outwritebuf = oldbuf; + } + print_debug_writebuf(ss, skb, oldbuf); + retval = 1; + } + spin_unlock_irqrestore(&ss->lock, flags); + if (retval) { + /* Get rid of the SKB if we're returning non-zero */ + /* N.B. this is called in process or BH context so + dev_kfree_skb is OK. */ + dev_kfree_skb(skb); + } + return retval; +} + +static int dahdi_ppp_ioctl(struct ppp_channel *ppp, unsigned int cmd, unsigned long flags) +{ + return -EIO; +} + +static struct ppp_channel_ops ztppp_ops = +{ + .start_xmit = dahdi_ppp_xmit, + .ioctl = dahdi_ppp_ioctl, +}; + +#endif + +static void dahdi_chan_unreg(struct dahdi_chan *chan) +{ + int x; + unsigned long flags; + + might_sleep(); + + release_echocan(chan->ec_factory); + +#ifdef CONFIG_DAHDI_NET + if (chan->flags & DAHDI_FLAG_NETDEV) { + unregister_hdlc_device(chan->hdlcnetdev->netdev); + free_netdev(chan->hdlcnetdev->netdev); + kfree(chan->hdlcnetdev); + chan->hdlcnetdev = NULL; + } +#endif + write_lock_irqsave(&chan_lock, flags); + if (test_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags)) { + chans[chan->channo] = NULL; + clear_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags); + } +#ifdef CONFIG_DAHDI_PPP + if (chan->ppp) { + module_printk(KERN_NOTICE, "HUH??? PPP still attached??\n"); + } +#endif + maxchans = 0; + for (x=1;xmaster == chan) { + chans[x]->master = chans[x]; + } + if ((chans[x]->confna == chan->channo) && + ((chans[x]->confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITOR || + (chans[x]->confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITORTX || + (chans[x]->confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITORBOTH || + (chans[x]->confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITOR_RX_PREECHO || + (chans[x]->confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITOR_TX_PREECHO || + (chans[x]->confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITORBOTH_PREECHO || + (chans[x]->confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_DIGITALMON)) { + /* Take them out of conference with us */ + /* release conference resource if any */ + if (chans[x]->confna) { + dahdi_check_conf(chans[x]->confna); + if (chans[x]->span && chans[x]->span->dacs) + chans[x]->span->dacs(chans[x], NULL); + } + chans[x]->confna = 0; + chans[x]->_confn = 0; + chans[x]->confmode = 0; + } + } + chan->channo = -1; + write_unlock_irqrestore(&chan_lock, flags); +} + +static ssize_t dahdi_chan_read(struct file *file, char *usrbuf, size_t count, int unit) +{ + struct dahdi_chan *chan = chans[unit]; + int amnt; + int res, rv; + int oldbuf,x; + unsigned long flags; + + /* Make sure count never exceeds 65k, and make sure it's unsigned */ + count &= 0xffff; + + if (!chan) + return -EINVAL; + + if (count < 1) + return -EINVAL; + + for (;;) { + spin_lock_irqsave(&chan->lock, flags); + if (chan->eventinidx != chan->eventoutidx) { + spin_unlock_irqrestore(&chan->lock, flags); + return -ELAST /* - chan->eventbuf[chan->eventoutidx]*/; + } + res = chan->outreadbuf; + if (chan->rxdisable) + res = -1; + spin_unlock_irqrestore(&chan->lock, flags); + if (res >= 0) + break; + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + rv = schluffen(&chan->readbufq); + if (rv) + return rv; + } + amnt = count; +/* added */ +#if 0 + if ((unit == 24) || (unit == 48) || (unit == 16) || (unit == 47)) { + int myamnt = amnt; + int x; + if (amnt > chan->readn[res]) + myamnt = chan->readn[res]; + module_printk(KERN_NOTICE, "dahdi_chan_read(unit: %d, inwritebuf: %d, outwritebuf: %d amnt: %d\n", + unit, chan->inwritebuf, chan->outwritebuf, myamnt); + + module_printk(KERN_DEBUG, "\t("); + for (x = 0; x < myamnt; x++) + printk((x ? " %02x" : "%02x"), (unsigned char)usrbuf[x]); + printk(")\n"); + } +#endif +/* end addition */ + if (chan->flags & DAHDI_FLAG_LINEAR) { + if (amnt > (chan->readn[res] << 1)) + amnt = chan->readn[res] << 1; + if (amnt) { + /* There seems to be a max stack size, so we have + to do this in smaller pieces */ + short lindata[128]; + int left = amnt >> 1; /* amnt is in bytes */ + int pos = 0; + int pass; + while (left) { + pass = left; + if (pass > 128) + pass = 128; + for (x = 0; x < pass; x++) + lindata[x] = DAHDI_XLAW(chan->readbuf[res][x + pos], chan); + if (copy_to_user(usrbuf + (pos << 1), lindata, pass << 1)) + return -EFAULT; + left -= pass; + pos += pass; + } + } + } else { + if (amnt > chan->readn[res]) + amnt = chan->readn[res]; + if (amnt) { + if (copy_to_user(usrbuf, chan->readbuf[res], amnt)) + return -EFAULT; + } + } + spin_lock_irqsave(&chan->lock, flags); + chan->readidx[res] = 0; + chan->readn[res] = 0; + oldbuf = res; + chan->outreadbuf = (res + 1) % chan->numbufs; + if (chan->outreadbuf == chan->inreadbuf) { + /* Out of stuff */ + chan->outreadbuf = -1; + if (chan->rxbufpolicy == DAHDI_POLICY_WHEN_FULL) + chan->rxdisable = 1; + } + if (chan->inreadbuf < 0) { + /* Notify interrupt handler that we have some space now */ + chan->inreadbuf = oldbuf; + } + spin_unlock_irqrestore(&chan->lock, flags); + + return amnt; +} + +static int num_filled_bufs(struct dahdi_chan *chan) +{ + int range1, range2; + + if (chan->inwritebuf < 0) { + return chan->numbufs; + } + + if (chan->outwritebuf < 0) { + return 0; + } + + if (chan->outwritebuf <= chan->inwritebuf) { + return chan->inwritebuf - chan->outwritebuf; + } + + /* This means (in > out) and we have wrap around */ + range1 = chan->numbufs - chan->outwritebuf; + range2 = chan->inwritebuf; + + return range1 + range2; +} + +static ssize_t dahdi_chan_write(struct file *file, const char *usrbuf, size_t count, int unit) +{ + unsigned long flags; + struct dahdi_chan *chan = chans[unit]; + int res, amnt, oldbuf, rv, x; + + /* Make sure count never exceeds 65k, and make sure it's unsigned */ + count &= 0xffff; + + if (!chan) + return -EINVAL; + + if (count < 1) { + return -EINVAL; + } + + for (;;) { + spin_lock_irqsave(&chan->lock, flags); + if ((chan->curtone || chan->pdialcount) && !(chan->flags & DAHDI_FLAG_PSEUDO)) { + chan->curtone = NULL; + chan->tonep = 0; + chan->dialing = 0; + chan->txdialbuf[0] = '\0'; + chan->pdialcount = 0; + } + if (chan->eventinidx != chan->eventoutidx) { + spin_unlock_irqrestore(&chan->lock, flags); + return -ELAST; + } + res = chan->inwritebuf; + spin_unlock_irqrestore(&chan->lock, flags); + if (res >= 0) + break; + if (file->f_flags & O_NONBLOCK) { +#ifdef BUFFER_DEBUG + printk("Error: Nonblock\n"); +#endif + return -EAGAIN; + } + /* Wait for something to be available */ + rv = schluffen(&chan->writebufq); + if (rv) { + return rv; + } + } + + amnt = count; + if (chan->flags & DAHDI_FLAG_LINEAR) { + if (amnt > (chan->blocksize << 1)) + amnt = chan->blocksize << 1; + } else { + if (amnt > chan->blocksize) + amnt = chan->blocksize; + } + +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "dahdi_chan_write(unit: %d, res: %d, outwritebuf: %d amnt: %d\n", + unit, res, chan->outwritebuf, amnt); +#endif +#if 0 + if ((unit == 24) || (unit == 48) || (unit == 16) || (unit == 47)) { + int x; + module_printk(KERN_NOTICE, "dahdi_chan_write/in(unit: %d, res: %d, outwritebuf: %d amnt: %d, txdisable: %d)\n", + unit, res, chan->outwritebuf, amnt, chan->txdisable); + module_printk(KERN_DEBUG, "\t("); for (x = 0; x < amnt; x++) module_printk(KERN_DEBUG, (x ? " %02x" : "%02x"), (unsigned char)usrbuf[x]); + module_printk(KERN_DEBUG, ")\n"); + } +#endif + + if (amnt) { + if (chan->flags & DAHDI_FLAG_LINEAR) { + /* There seems to be a max stack size, so we have + to do this in smaller pieces */ + short lindata[128]; + int left = amnt >> 1; /* amnt is in bytes */ + int pos = 0; + int pass; + while (left) { + pass = left; + if (pass > 128) + pass = 128; + if (copy_from_user(lindata, usrbuf + (pos << 1), pass << 1)) { + return -EFAULT; + } + left -= pass; + for (x = 0; x < pass; x++) + chan->writebuf[res][x + pos] = DAHDI_LIN2X(lindata[x], chan); + pos += pass; + } + chan->writen[res] = amnt >> 1; + } else { + if (copy_from_user(chan->writebuf[res], usrbuf, amnt)) { + return -EFAULT; + } + chan->writen[res] = amnt; + } + chan->writeidx[res] = 0; + if (chan->flags & DAHDI_FLAG_FCS) + calc_fcs(chan, res); + oldbuf = res; + spin_lock_irqsave(&chan->lock, flags); + chan->inwritebuf = (res + 1) % chan->numbufs; + + if (chan->inwritebuf == chan->outwritebuf) { + /* Don't stomp on the transmitter, just wait for them to + wake us up */ + chan->inwritebuf = -1; + /* Make sure the transmitter is transmitting in case of POLICY_WHEN_FULL */ + chan->txdisable = 0; + } + + if (chan->outwritebuf < 0) { + /* Okay, the interrupt handler has been waiting for us. Give them a buffer */ + chan->outwritebuf = oldbuf; + } + + if ((chan->txbufpolicy == DAHDI_POLICY_HALF_FULL) && (chan->txdisable)) { + if (num_filled_bufs(chan) >= (chan->numbufs >> 1)) { +#ifdef BUFFER_DEBUG + printk("Reached buffer fill mark of %d\n", num_filled_bufs(chan)); +#endif + chan->txdisable = 0; + } + } + +#ifdef BUFFER_DEBUG + if ((chan->statcount <= 0) || (amnt != 128) || (num_filled_bufs(chan) != chan->lastnumbufs)) { + printk("amnt: %d Number of filled buffers: %d\n", amnt, num_filled_bufs(chan)); + chan->statcount = 32000; + chan->lastnumbufs = num_filled_bufs(chan); + } +#endif + + spin_unlock_irqrestore(&chan->lock, flags); + + if (chan->flags & DAHDI_FLAG_NOSTDTXRX && chan->span->hdlc_hard_xmit) + chan->span->hdlc_hard_xmit(chan); + } + return amnt; +} + +static int dahdi_ctl_open(struct inode *inode, struct file *file) +{ + /* Nothing to do, really */ + return 0; +} + +static int dahdi_chan_open(struct inode *inode, struct file *file) +{ + /* Nothing to do here for now either */ + return 0; +} + +static int dahdi_ctl_release(struct inode *inode, struct file *file) +{ + /* Nothing to do */ + return 0; +} + +static int dahdi_chan_release(struct inode *inode, struct file *file) +{ + /* Nothing to do for now */ + return 0; +} + +static void set_txtone(struct dahdi_chan *ss, int fac, int init_v2, int init_v3) +{ + if (fac == 0) { + ss->v2_1 = 0; + ss->v3_1 = 0; + return; + } + ss->txtone = fac; + ss->v1_1 = 0; + ss->v2_1 = init_v2; + ss->v3_1 = init_v3; + return; +} + +static void dahdi_rbs_sethook(struct dahdi_chan *chan, int txsig, int txstate, + int timeout) +{ + static const struct { + unsigned int sig_type; + /* Index is dahdi_txsig enum */ + unsigned int bits[DAHDI_TXSIG_TOTAL]; + } outs[NUM_SIGS] = { + { + /* + * We set the idle case of the DAHDI_SIG_NONE to this pattern to make idle E1 CAS + * channels happy. Should not matter with T1, since on an un-configured channel, + * who cares what the sig bits are as long as they are stable + */ + .sig_type = DAHDI_SIG_NONE, + .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_ACD, + }, { + .sig_type = DAHDI_SIG_EM, + .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_ABCD, + .bits[DAHDI_TXSIG_START] = DAHDI_BITS_ABCD, + }, { + .sig_type = DAHDI_SIG_FXSLS, + .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_BD, + .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_ABCD, + .bits[DAHDI_TXSIG_START] = DAHDI_BITS_ABCD, + }, { + .sig_type = DAHDI_SIG_FXSGS, + .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_BD, + .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_ABCD, +#ifndef CONFIG_CAC_GROUNDSTART + .bits[DAHDI_TXSIG_START] = DAHDI_BITS_AC, +#endif + }, { + .sig_type = DAHDI_SIG_FXSKS, + .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_BD, + .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_ABCD, + .bits[DAHDI_TXSIG_START] = DAHDI_BITS_ABCD, + }, { + .sig_type = DAHDI_SIG_FXOLS, + .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_BD, + .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_BD, + }, { + .sig_type = DAHDI_SIG_FXOGS, + .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_ABCD, + .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_BD, + }, { + .sig_type = DAHDI_SIG_FXOKS, + .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_BD, + .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_BD, + .bits[DAHDI_TXSIG_KEWL] = DAHDI_BITS_ABCD, + }, { + .sig_type = DAHDI_SIG_SF, + .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_BITS_BCD, + .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_ABCD, + .bits[DAHDI_TXSIG_START] = DAHDI_BITS_ABCD, + .bits[DAHDI_TXSIG_KEWL] = DAHDI_BITS_BCD, + }, { + .sig_type = DAHDI_SIG_EM_E1, + .bits[DAHDI_TXSIG_ONHOOK] = DAHDI_DBIT, + .bits[DAHDI_TXSIG_OFFHOOK] = DAHDI_BITS_ABD, + .bits[DAHDI_TXSIG_START] = DAHDI_BITS_ABD, + .bits[DAHDI_TXSIG_KEWL] = DAHDI_DBIT, + } + }; + int x; + + /* if no span, return doing nothing */ + if (!chan->span) + return; + + if (!(chan->span->flags & DAHDI_FLAG_RBS)) { + module_printk(KERN_NOTICE, "dahdi_rbs: Tried to set RBS hook state on non-RBS channel %s\n", chan->name); + return; + } + if ((txsig > 3) || (txsig < 0)) { + module_printk(KERN_NOTICE, "dahdi_rbs: Tried to set RBS hook state %d (> 3) on channel %s\n", txsig, chan->name); + return; + } + if (!chan->span->rbsbits && !chan->span->hooksig) { + module_printk(KERN_NOTICE, "dahdi_rbs: Tried to set RBS hook state %d on channel %s while span %s lacks rbsbits or hooksig function\n", + txsig, chan->name, chan->span->name); + return; + } + /* Don't do anything for RBS */ + if (chan->sig == DAHDI_SIG_DACS_RBS) + return; + chan->txstate = txstate; + + /* if tone signalling */ + if (chan->sig == DAHDI_SIG_SF) { + chan->txhooksig = txsig; + if (chan->txtone) { /* if set to make tone for tx */ + if ((txsig && !(chan->toneflags & DAHDI_REVERSE_TXTONE)) || + ((!txsig) && (chan->toneflags & DAHDI_REVERSE_TXTONE))) { + set_txtone(chan,chan->txtone,chan->tx_v2,chan->tx_v3); + } else { + set_txtone(chan,0,0,0); + } + } + chan->otimer = timeout * DAHDI_CHUNKSIZE; /* Otimer is timer in samples */ + return; + } + if (chan->span->hooksig) { + if (chan->txhooksig != txsig) { + chan->txhooksig = txsig; + chan->span->hooksig(chan, txsig); + } + chan->otimer = timeout * DAHDI_CHUNKSIZE; /* Otimer is timer in samples */ + return; + } else { + for (x = 0; x < NUM_SIGS; x++) { + if (outs[x].sig_type == chan->sig) { +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "Setting bits to %d for channel %s state %d in %d signalling\n", outs[x].bits[txsig], chan->name, txsig, chan->sig); +#endif + chan->txhooksig = txsig; + chan->txsig = outs[x].bits[txsig]; + chan->span->rbsbits(chan, chan->txsig); + chan->otimer = timeout * DAHDI_CHUNKSIZE; /* Otimer is timer in samples */ + return; + } + } + } + module_printk(KERN_NOTICE, "dahdi_rbs: Don't know RBS signalling type %d on channel %s\n", chan->sig, chan->name); +} + +static int dahdi_cas_setbits(struct dahdi_chan *chan, int bits) +{ + /* if no span, return as error */ + if (!chan->span) + return -1; + if (chan->span->rbsbits) { + chan->txsig = bits; + chan->span->rbsbits(chan, bits); + } else { + module_printk(KERN_NOTICE, "Huh? CAS setbits, but no RBS bits function\n"); + } + + return 0; +} + +static int dahdi_hangup(struct dahdi_chan *chan) +{ + int x, res = 0; + + /* Can't hangup pseudo channels */ + if (!chan->span) + return 0; + + /* Can't hang up a clear channel */ + if (chan->flags & (DAHDI_FLAG_CLEAR | DAHDI_FLAG_NOSTDTXRX)) + return -EINVAL; + + chan->kewlonhook = 0; + + if ((chan->sig == DAHDI_SIG_FXSLS) || (chan->sig == DAHDI_SIG_FXSKS) || + (chan->sig == DAHDI_SIG_FXSGS)) { + chan->ringdebtimer = RING_DEBOUNCE_TIME; + } + + if (chan->span->flags & DAHDI_FLAG_RBS) { + if (chan->sig == DAHDI_SIG_CAS) { + dahdi_cas_setbits(chan, chan->idlebits); + } else if ((chan->sig == DAHDI_SIG_FXOKS) && (chan->txstate != DAHDI_TXSTATE_ONHOOK) + /* if other party is already on-hook we shouldn't do any battery drop */ + && !((chan->rxhooksig == DAHDI_RXSIG_ONHOOK) && (chan->itimer <= 0))) { + /* Do RBS signalling on the channel's behalf */ + dahdi_rbs_sethook(chan, DAHDI_TXSIG_KEWL, DAHDI_TXSTATE_KEWL, DAHDI_KEWLTIME); + } else + dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_ONHOOK, 0); + } else { + /* Let the driver hang up the line if it wants to */ + if (chan->span->sethook) { + if (chan->txhooksig != DAHDI_ONHOOK) { + chan->txhooksig = DAHDI_ONHOOK; + res = chan->span->sethook(chan, DAHDI_ONHOOK); + } else + res = 0; + } + } + + /* if not registered yet, just return here */ + if (!test_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags)) + return res; + + /* Mark all buffers as empty */ + for (x = 0; x < chan->numbufs; x++) { + chan->writen[x] = + chan->writeidx[x]= + chan->readn[x]= + chan->readidx[x] = 0; + } + + if (chan->readbuf[0]) { + chan->inreadbuf = 0; + chan->inwritebuf = 0; + } else { + chan->inreadbuf = -1; + chan->inwritebuf = -1; + } + chan->outreadbuf = -1; + chan->outwritebuf = -1; + chan->dialing = 0; + chan->afterdialingtimer = 0; + chan->curtone = NULL; + chan->pdialcount = 0; + chan->cadencepos = 0; + chan->txdialbuf[0] = 0; + + return res; +} + +static int initialize_channel(struct dahdi_chan *chan) +{ + int res; + unsigned long flags; + void *rxgain=NULL; + struct dahdi_echocan_state *ec_state; + const struct dahdi_echocan_factory *ec_current; + + if ((res = dahdi_reallocbufs(chan, DAHDI_DEFAULT_BLOCKSIZE, DAHDI_DEFAULT_NUM_BUFS))) + return res; + + spin_lock_irqsave(&chan->lock, flags); + + chan->rxbufpolicy = DAHDI_POLICY_IMMEDIATE; + chan->txbufpolicy = DAHDI_POLICY_IMMEDIATE; + + ec_state = chan->ec_state; + chan->ec_state = NULL; + ec_current = chan->ec_current; + chan->ec_current = NULL; + + chan->txdisable = 0; + chan->rxdisable = 0; + + chan->digitmode = DIGIT_MODE_DTMF; + chan->dialing = 0; + chan->afterdialingtimer = 0; + + chan->cadencepos = 0; + chan->firstcadencepos = 0; /* By default loop back to first cadence position */ + + /* HDLC & FCS stuff */ + fasthdlc_init(&chan->rxhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64); + fasthdlc_init(&chan->txhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64); + chan->infcs = PPP_INITFCS; + + /* Timings for RBS */ + chan->prewinktime = DAHDI_DEFAULT_PREWINKTIME; + chan->preflashtime = DAHDI_DEFAULT_PREFLASHTIME; + chan->winktime = DAHDI_DEFAULT_WINKTIME; + chan->flashtime = DAHDI_DEFAULT_FLASHTIME; + + if (chan->sig & __DAHDI_SIG_FXO) + chan->starttime = DAHDI_DEFAULT_RINGTIME; + else + chan->starttime = DAHDI_DEFAULT_STARTTIME; + chan->rxwinktime = DAHDI_DEFAULT_RXWINKTIME; + chan->rxflashtime = DAHDI_DEFAULT_RXFLASHTIME; + chan->debouncetime = DAHDI_DEFAULT_DEBOUNCETIME; + chan->pulsemaketime = DAHDI_DEFAULT_PULSEMAKETIME; + chan->pulsebreaktime = DAHDI_DEFAULT_PULSEBREAKTIME; + chan->pulseaftertime = DAHDI_DEFAULT_PULSEAFTERTIME; + + /* Initialize RBS timers */ + chan->itimerset = chan->itimer = chan->otimer = 0; + chan->ringdebtimer = 0; + + init_waitqueue_head(&chan->sel); + init_waitqueue_head(&chan->readbufq); + init_waitqueue_head(&chan->writebufq); + init_waitqueue_head(&chan->eventbufq); + init_waitqueue_head(&chan->txstateq); + + /* Reset conferences */ + reset_conf(chan); + + /* I/O Mask, etc */ + chan->iomask = 0; + /* release conference resource if any */ + if (chan->confna) + dahdi_check_conf(chan->confna); + if ((chan->sig & __DAHDI_SIG_DACS) != __DAHDI_SIG_DACS) { + chan->confna = 0; + chan->confmode = 0; + if (chan->span && chan->span->dacs) + chan->span->dacs(chan, NULL); + } + chan->_confn = 0; + memset(chan->conflast, 0, sizeof(chan->conflast)); + memset(chan->conflast1, 0, sizeof(chan->conflast1)); + memset(chan->conflast2, 0, sizeof(chan->conflast2)); + chan->confmute = 0; + chan->gotgs = 0; + chan->curtone = NULL; + chan->tonep = 0; + chan->pdialcount = 0; + if (chan->gainalloc && chan->rxgain) + rxgain = chan->rxgain; + chan->rxgain = defgain; + chan->txgain = defgain; + chan->gainalloc = 0; + chan->eventinidx = chan->eventoutidx = 0; + dahdi_set_law(chan,0); + dahdi_hangup(chan); + + /* Make sure that the audio flag is cleared on a clear channel */ + if ((chan->sig & DAHDI_SIG_CLEAR) || (chan->sig & DAHDI_SIG_HARDHDLC)) + chan->flags &= ~DAHDI_FLAG_AUDIO; + + if ((chan->sig == DAHDI_SIG_CLEAR) || (chan->sig == DAHDI_SIG_HARDHDLC)) + chan->flags &= ~(DAHDI_FLAG_PPP | DAHDI_FLAG_FCS | DAHDI_FLAG_HDLC); + + chan->flags &= ~DAHDI_FLAG_LINEAR; + if (chan->curzone) { + /* Take cadence from tone zone */ + memcpy(chan->ringcadence, chan->curzone->ringcadence, sizeof(chan->ringcadence)); + } else { + /* Do a default */ + memset(chan->ringcadence, 0, sizeof(chan->ringcadence)); + chan->ringcadence[0] = chan->starttime; + chan->ringcadence[1] = DAHDI_RINGOFFTIME; + } + + if (ec_state) { + ec_state->ops->echocan_free(chan, ec_state); + release_echocan(ec_current); + } + + spin_unlock_irqrestore(&chan->lock, flags); + + set_tone_zone(chan, -1); + + if (rxgain) + kfree(rxgain); + + return 0; +} + +static int dahdi_timing_open(struct inode *inode, struct file *file) +{ + struct dahdi_timer *t; + unsigned long flags; + + if (!(t = kzalloc(sizeof(*t), GFP_KERNEL))) + return -ENOMEM; + + init_waitqueue_head(&t->sel); + INIT_LIST_HEAD(&t->list); + file->private_data = t; + + spin_lock_irqsave(&zaptimerlock, flags); + list_add(&t->list, &zaptimers); + spin_unlock_irqrestore(&zaptimerlock, flags); + + return 0; +} + +static int dahdi_timer_release(struct inode *inode, struct file *file) +{ + struct dahdi_timer *t, *cur, *next; + unsigned long flags; + + if (!(t = file->private_data)) + return 0; + + spin_lock_irqsave(&zaptimerlock, flags); + + list_for_each_entry_safe(cur, next, &zaptimers, list) { + if (t == cur) { + list_del(&cur->list); + break; + } + } + + spin_unlock_irqrestore(&zaptimerlock, flags); + + if (!cur) { + module_printk(KERN_NOTICE, "Timer: Not on list??\n"); + return 0; + } + + kfree(cur); + + return 0; +} + +static int dahdi_specchan_open(struct inode *inode, struct file *file, int unit) +{ + int res = 0; + + if (chans[unit] && chans[unit]->sig) { + /* Make sure we're not already open, a net device, or a slave device */ + if (chans[unit]->flags & DAHDI_FLAG_NETDEV) + res = -EBUSY; + else if (chans[unit]->master != chans[unit]) + res = -EBUSY; + else if ((chans[unit]->sig & __DAHDI_SIG_DACS) == __DAHDI_SIG_DACS) + res = -EBUSY; + else if (!test_and_set_bit(DAHDI_FLAGBIT_OPEN, &chans[unit]->flags)) { + unsigned long flags; + res = initialize_channel(chans[unit]); + if (res) { + /* Reallocbufs must have failed */ + clear_bit(DAHDI_FLAGBIT_OPEN, &chans[unit]->flags); + return res; + } + spin_lock_irqsave(&chans[unit]->lock, flags); + if (chans[unit]->flags & DAHDI_FLAG_PSEUDO) + chans[unit]->flags |= DAHDI_FLAG_AUDIO; + if (chans[unit]->span && chans[unit]->span->open) { + res = chans[unit]->span->open(chans[unit]); + } + if (!res) { + chans[unit]->file = file; + spin_unlock_irqrestore(&chans[unit]->lock, flags); + } else { + spin_unlock_irqrestore(&chans[unit]->lock, flags); + close_channel(chans[unit]); + clear_bit(DAHDI_FLAGBIT_OPEN, &chans[unit]->flags); + } + } else + res = -EBUSY; + } else + res = -ENXIO; + return res; +} + +static int dahdi_specchan_release(struct inode *node, struct file *file, int unit) +{ + int res=0; + unsigned long flags; + + if (chans[unit]) { + /* Chan lock protects contents against potentially non atomic accesses. + * So if the pointer setting is not atomic, we should protect */ + spin_lock_irqsave(&chans[unit]->lock, flags); + chans[unit]->file = NULL; + spin_unlock_irqrestore(&chans[unit]->lock, flags); + close_channel(chans[unit]); + if (chans[unit]->span && chans[unit]->span->close) + res = chans[unit]->span->close(chans[unit]); + /* The channel might be destroyed by low-level driver span->close() */ + if(chans[unit]) + clear_bit(DAHDI_FLAGBIT_OPEN, &chans[unit]->flags); + } else + res = -ENXIO; + return res; +} + +static int can_open_timer(void) +{ +#ifdef CONFIG_DAHDI_CORE_TIMER + return 1; +#else + return maxspans > 0; +#endif +} + +static struct dahdi_chan *dahdi_alloc_pseudo(void) +{ + struct dahdi_chan *pseudo; + + /* Don't allow /dev/dahdi/pseudo to open if there is not a timing + * source. */ + if (!can_open_timer()) + return NULL; + + if (!(pseudo = kzalloc(sizeof(*pseudo), GFP_KERNEL))) + return NULL; + + pseudo->sig = DAHDI_SIG_CLEAR; + pseudo->sigcap = DAHDI_SIG_CLEAR; + pseudo->flags = DAHDI_FLAG_PSEUDO | DAHDI_FLAG_AUDIO; + + if (dahdi_chan_reg(pseudo)) { + kfree(pseudo); + pseudo = NULL; + } else { + snprintf(pseudo->name, sizeof(pseudo->name)-1,"Pseudo/%d", pseudo->channo); + } + + return pseudo; +} + +static void dahdi_free_pseudo(struct dahdi_chan *pseudo) +{ + if (pseudo) { + dahdi_chan_unreg(pseudo); + kfree(pseudo); + } +} + +static int dahdi_open(struct inode *inode, struct file *file) +{ + int unit = UNIT(file); + struct dahdi_chan *chan; + /* Minor 0: Special "control" descriptor */ + if (!unit) + return dahdi_ctl_open(inode, file); + if (unit == 250) { + if (!dahdi_transcode_fops) { + if (request_module("dahdi_transcode")) { + return -ENXIO; + } + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + __MOD_INC_USE_COUNT (dahdi_transcode_fops->owner); +#else + if (!try_module_get(dahdi_transcode_fops->owner)) { + return -ENXIO; + } +#endif + if (dahdi_transcode_fops && dahdi_transcode_fops->open) { + return dahdi_transcode_fops->open(inode, file); + } else { + /* dahdi_transcode module should have exported a + * file_operations table. */ + WARN_ON(1); + } + return -ENXIO; + } + if (unit == 253) { + if (can_open_timer()) { + return dahdi_timing_open(inode, file); + } else { + return -ENXIO; + } + } + if (unit == 254) + return dahdi_chan_open(inode, file); + if (unit == 255) { + chan = dahdi_alloc_pseudo(); + if (chan) { + file->private_data = chan; + return dahdi_specchan_open(inode, file, chan->channo); + } else { + return -ENXIO; + } + } + return dahdi_specchan_open(inode, file, unit); +} + +#if 0 +static int dahdi_open(struct inode *inode, struct file *file) +{ + int res; + unsigned long flags; + spin_lock_irqsave(&bigzaplock, flags); + res = __dahdi_open(inode, file); + spin_unlock_irqrestore(&bigzaplock, flags); + return res; +} +#endif + +static ssize_t dahdi_read(struct file *file, char *usrbuf, size_t count, loff_t *ppos) +{ + int unit = UNIT(file); + struct dahdi_chan *chan; + + /* Can't read from control */ + if (!unit) { + return -EINVAL; + } + + if (unit == 253) + return -EINVAL; + + if (unit == 254) { + chan = file->private_data; + if (!chan) + return -EINVAL; + return dahdi_chan_read(file, usrbuf, count, chan->channo); + } + + if (unit == 255) { + chan = file->private_data; + if (!chan) { + module_printk(KERN_NOTICE, "No pseudo channel structure to read?\n"); + return -EINVAL; + } + return dahdi_chan_read(file, usrbuf, count, chan->channo); + } + if (count < 0) + return -EINVAL; + + return dahdi_chan_read(file, usrbuf, count, unit); +} + +static ssize_t dahdi_write(struct file *file, const char *usrbuf, size_t count, loff_t *ppos) +{ + int unit = UNIT(file); + struct dahdi_chan *chan; + /* Can't read from control */ + if (!unit) + return -EINVAL; + if (count < 0) + return -EINVAL; + if (unit == 253) + return -EINVAL; + if (unit == 254) { + chan = file->private_data; + if (!chan) + return -EINVAL; + return dahdi_chan_write(file, usrbuf, count, chan->channo); + } + if (unit == 255) { + chan = file->private_data; + if (!chan) { + module_printk(KERN_NOTICE, "No pseudo channel structure to read?\n"); + return -EINVAL; + } + return dahdi_chan_write(file, usrbuf, count, chan->channo); + } + return dahdi_chan_write(file, usrbuf, count, unit); + +} + +static int dahdi_set_default_zone(int defzone) +{ + if ((defzone < 0) || (defzone >= DAHDI_TONE_ZONE_MAX)) + return -EINVAL; + write_lock(&zone_lock); + if (!tone_zones[defzone]) { + write_unlock(&zone_lock); + return -EINVAL; + } + if ((default_zone != -1) && tone_zones[default_zone]) + atomic_dec(&tone_zones[default_zone]->refcount); + atomic_inc(&tone_zones[defzone]->refcount); + default_zone = defzone; + write_unlock(&zone_lock); + return 0; +} + +/* No bigger than 32k for everything per tone zone */ +#define MAX_SIZE 32768 +/* No more than 128 subtones */ +#define MAX_TONES 128 + +/* The tones to be loaded can (will) be a mix of regular tones, + DTMF tones and MF tones. We need to load DTMF and MF tones + a bit differently than regular tones because their storage + format is much simpler (an array structure field of the zone + structure, rather an array of pointers). +*/ +static int ioctl_load_zone(unsigned long data) +{ + struct load_zone_workarea { + struct dahdi_tone *samples[MAX_TONES]; + short next[MAX_TONES]; + struct dahdi_tone_def_header th; + struct dahdi_tone_def td; + } *work; + + size_t space; + size_t size; + int res; + int x; + void *slab, *ptr; + struct dahdi_zone *z; + struct dahdi_tone *t; + + work = kzalloc(sizeof(*work), GFP_KERNEL); + if (!work) + return -ENOMEM; + + if (copy_from_user(&work->th, (struct dahdi_tone_def_header *)data, + sizeof(work->th))) { + kfree(work); + return -EFAULT; + } + + data += sizeof(work->th); + + if ((work->th.count < 0) || (work->th.count > MAX_TONES)) { + module_printk(KERN_NOTICE, "Too many tones included\n"); + kfree(work); + return -EINVAL; + } + + space = size = sizeof(*z) + work->th.count * sizeof(*t); + + if (size > MAX_SIZE) { + kfree(work); + return -E2BIG; + } + + z = ptr = slab = kzalloc(size, GFP_KERNEL); + if (!z) { + kfree(work); + return -ENOMEM; + } + + ptr += sizeof(*z); + space -= sizeof(*z); + + dahdi_copy_string(z->name, work->th.name, sizeof(z->name)); + + for (x = 0; x < DAHDI_MAX_CADENCE; x++) + z->ringcadence[x] = work->th.ringcadence[x]; + + atomic_set(&z->refcount, 0); + + for (x = 0; x < work->th.count; x++) { + enum { + REGULAR_TONE, + DTMF_TONE, + MFR1_TONE, + MFR2_FWD_TONE, + MFR2_REV_TONE, + } tone_type; + + if (space < sizeof(*t)) { + kfree(slab); + kfree(work); + module_printk(KERN_NOTICE, "Insufficient tone zone space\n"); + return -EINVAL; + } + + res = copy_from_user(&work->td, (struct dahdi_tone_def *)data, + sizeof(work->td)); + if (res) { + kfree(slab); + kfree(work); + return -EFAULT; + } + + data += sizeof(work->td); + + if ((work->td.tone >= 0) && (work->td.tone < DAHDI_TONE_MAX)) { + tone_type = REGULAR_TONE; + + t = work->samples[x] = ptr; + + space -= sizeof(*t); + ptr += sizeof(*t); + + /* Remember which sample is work->next */ + work->next[x] = work->td.next; + + /* Make sure the "next" one is sane */ + if ((work->next[x] >= work->th.count) || + (work->next[x] < 0)) { + module_printk(KERN_NOTICE, + "Invalid 'next' pointer: %d\n", + work->next[x]); + kfree(slab); + kfree(work); + return -EINVAL; + } + } else if ((work->td.tone >= DAHDI_TONE_DTMF_BASE) && + (work->td.tone <= DAHDI_TONE_DTMF_MAX)) { + tone_type = DTMF_TONE; + work->td.tone -= DAHDI_TONE_DTMF_BASE; + t = &z->dtmf[work->td.tone]; + } else if ((work->td.tone >= DAHDI_TONE_MFR1_BASE) && + (work->td.tone <= DAHDI_TONE_MFR1_MAX)) { + tone_type = MFR1_TONE; + work->td.tone -= DAHDI_TONE_MFR1_BASE; + t = &z->mfr1[work->td.tone]; + } else if ((work->td.tone >= DAHDI_TONE_MFR2_FWD_BASE) && + (work->td.tone <= DAHDI_TONE_MFR2_FWD_MAX)) { + tone_type = MFR2_FWD_TONE; + work->td.tone -= DAHDI_TONE_MFR2_FWD_BASE; + t = &z->mfr2_fwd[work->td.tone]; + } else if ((work->td.tone >= DAHDI_TONE_MFR2_REV_BASE) && + (work->td.tone <= DAHDI_TONE_MFR2_REV_MAX)) { + tone_type = MFR2_REV_TONE; + work->td.tone -= DAHDI_TONE_MFR2_REV_BASE; + t = &z->mfr2_rev[work->td.tone]; + } else { + module_printk(KERN_NOTICE, + "Invalid tone (%d) defined\n", + work->td.tone); + kfree(slab); + kfree(work); + return -EINVAL; + } + + t->fac1 = work->td.fac1; + t->init_v2_1 = work->td.init_v2_1; + t->init_v3_1 = work->td.init_v3_1; + t->fac2 = work->td.fac2; + t->init_v2_2 = work->td.init_v2_2; + t->init_v3_2 = work->td.init_v3_2; + t->modulate = work->td.modulate; + + switch (tone_type) { + case REGULAR_TONE: + t->tonesamples = work->td.samples; + if (!z->tones[work->td.tone]) + z->tones[work->td.tone] = t; + break; + case DTMF_TONE: + t->tonesamples = global_dialparams.dtmf_tonelen; + t->next = &dtmf_silence; + z->dtmf_continuous[work->td.tone] = *t; + z->dtmf_continuous[work->td.tone].next = + &z->dtmf_continuous[work->td.tone]; + break; + case MFR1_TONE: + switch (work->td.tone + DAHDI_TONE_MFR1_BASE) { + case DAHDI_TONE_MFR1_KP: + case DAHDI_TONE_MFR1_ST: + case DAHDI_TONE_MFR1_STP: + case DAHDI_TONE_MFR1_ST2P: + case DAHDI_TONE_MFR1_ST3P: + /* signaling control tones are always 100ms */ + t->tonesamples = 100 * DAHDI_CHUNKSIZE; + break; + default: + t->tonesamples = global_dialparams.mfv1_tonelen; + break; + } + t->next = &mfr1_silence; + break; + case MFR2_FWD_TONE: + t->tonesamples = global_dialparams.mfr2_tonelen; + t->next = &dtmf_silence; + z->mfr2_fwd_continuous[work->td.tone] = *t; + z->mfr2_fwd_continuous[work->td.tone].next = + &z->mfr2_fwd_continuous[work->td.tone]; + break; + case MFR2_REV_TONE: + t->tonesamples = global_dialparams.mfr2_tonelen; + t->next = &dtmf_silence; + z->mfr2_rev_continuous[work->td.tone] = *t; + z->mfr2_rev_continuous[work->td.tone].next = + &z->mfr2_rev_continuous[work->td.tone]; + break; + } + } + + for (x = 0; x < work->th.count; x++) { + if (work->samples[x]) + work->samples[x]->next = work->samples[work->next[x]]; + } + + res = dahdi_register_tone_zone(work->th.zone, z); + if (res) { + kfree(slab); + } else { + if ( -1 == default_zone ) { + dahdi_set_default_zone(work->th.zone); + } + } + + kfree(work); + return res; +} + +void dahdi_init_tone_state(struct dahdi_tone_state *ts, struct dahdi_tone *zt) +{ + ts->v1_1 = 0; + ts->v2_1 = zt->init_v2_1; + ts->v3_1 = zt->init_v3_1; + ts->v1_2 = 0; + ts->v2_2 = zt->init_v2_2; + ts->v3_2 = zt->init_v3_2; + ts->modulate = zt->modulate; +} + +struct dahdi_tone *dahdi_mf_tone(const struct dahdi_chan *chan, char digit, int digitmode) +{ + unsigned int tone_index; + + if (!chan->curzone) { + static int __warnonce = 1; + if (__warnonce) { + __warnonce = 0; + /* The tonezones are loaded by dahdi_cfg based on /etc/dahdi/system.conf. */ + module_printk(KERN_WARNING, "Cannot get dtmf tone until tone zone is loaded.\n"); + } + return NULL; + } + + switch (digitmode) { + case DIGIT_MODE_PULSE: + /* We should only get here with a pulse digit if we need + * to "dial" 'W' (wait 0.5 second) + */ + if (digit == 'W') + return &tone_pause; + + return NULL; + /* You should not get here */ + case DIGIT_MODE_DTMF: + switch (digit) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + tone_index = DAHDI_TONE_DTMF_0 + (digit - '0'); + break; + case '*': + tone_index = DAHDI_TONE_DTMF_s; + break; + case '#': + tone_index = DAHDI_TONE_DTMF_p; + break; + case 'A': + case 'B': + case 'C': + case 'D': + tone_index = DAHDI_TONE_DTMF_A + (digit - 'A'); + case 'W': + return &tone_pause; + default: + return NULL; + } + return &chan->curzone->dtmf[tone_index - DAHDI_TONE_DTMF_BASE]; + case DIGIT_MODE_MFR1: + switch (digit) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + tone_index = DAHDI_TONE_MFR1_0 + (digit - '0'); + break; + case '*': + tone_index = DAHDI_TONE_MFR1_KP; + break; + case '#': + tone_index = DAHDI_TONE_MFR1_ST; + break; + case 'A': + tone_index = DAHDI_TONE_MFR1_STP; + break; + case 'B': + tone_index = DAHDI_TONE_MFR1_ST2P; + break; + case 'C': + tone_index = DAHDI_TONE_MFR1_ST3P; + break; + case 'W': + return &tone_pause; + default: + return NULL; + } + return &chan->curzone->mfr1[tone_index - DAHDI_TONE_MFR1_BASE]; + case DIGIT_MODE_MFR2_FWD: + switch (digit) { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + tone_index = DAHDI_TONE_MFR2_FWD_1 + (digit - '1'); + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + tone_index = DAHDI_TONE_MFR2_FWD_10 + (digit - 'A'); + break; + case 'W': + return &tone_pause; + default: + return NULL; + } + return &chan->curzone->mfr2_fwd[tone_index - DAHDI_TONE_MFR2_FWD_BASE]; + case DIGIT_MODE_MFR2_REV: + switch (digit) { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + tone_index = DAHDI_TONE_MFR2_REV_1 + (digit - '1'); + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + tone_index = DAHDI_TONE_MFR2_REV_10 + (digit - 'A'); + break; + case 'W': + return &tone_pause; + default: + return NULL; + } + return &chan->curzone->mfr2_rev[tone_index - DAHDI_TONE_MFR2_REV_BASE]; + default: + return NULL; + } +} + +static void __do_dtmf(struct dahdi_chan *chan) +{ + char c; + + /* Called with chan->lock held */ + while ((c = chan->txdialbuf[0])) { + memmove(chan->txdialbuf, chan->txdialbuf + 1, sizeof(chan->txdialbuf) - 1); + switch (c) { + case 'T': + chan->digitmode = DIGIT_MODE_DTMF; + chan->tonep = 0; + break; + case 'M': + chan->digitmode = DIGIT_MODE_MFR1; + chan->tonep = 0; + break; + case 'O': + chan->digitmode = DIGIT_MODE_MFR2_FWD; + chan->tonep = 0; + break; + case 'R': + chan->digitmode = DIGIT_MODE_MFR2_REV; + chan->tonep = 0; + break; + case 'P': + chan->digitmode = DIGIT_MODE_PULSE; + chan->tonep = 0; + break; + default: + if ((c != 'W') && (chan->digitmode == DIGIT_MODE_PULSE)) { + if ((c >= '0') && (c <= '9') && (chan->txhooksig == DAHDI_TXSIG_OFFHOOK)) { + /* (CNET) Dial pulse mappings for FXO ports */ + switch(chan->map_pulse) + { + case MAP_PULSE_NZ_OSLO: /* 0=10 pulses, 1=9 pulses, ... 9=1 pulse */ + chan->pdialcount = 10 - (c - '0'); break; + case MAP_PULSE_SWEDEN: /* 0=1 pulse, 1=2 pulses, ... 9=10 pulses */ + chan->pdialcount = (c - '0') + 1; break; + default: /* standard dial pulse mapping */ + chan->pdialcount = (c == '0') ? 10 : c - '0'; break; + } + dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_PULSEBREAK, + chan->pulsebreaktime); + return; + } + } else { + chan->curtone = dahdi_mf_tone(chan, c, chan->digitmode); + chan->tonep = 0; + if (chan->curtone) { + dahdi_init_tone_state(&chan->ts, chan->curtone); + return; + } + } + } + } + + /* Notify userspace process if there is nothing left */ + chan->dialing = 0; + __qevent(chan, DAHDI_EVENT_DIALCOMPLETE); +} + +static int dahdi_release(struct inode *inode, struct file *file) +{ + int unit = UNIT(file); + int res; + struct dahdi_chan *chan; + + if (!unit) + return dahdi_ctl_release(inode, file); + if (unit == 253) { + return dahdi_timer_release(inode, file); + } + if (unit == 250) { + /* We should not be here because the dahdi_transcode.ko module + * should have updated the file_operations for this file + * handle when the file was opened. */ + WARN_ON(1); + return -EFAULT; + } + if (unit == 254) { + chan = file->private_data; + if (!chan) + return dahdi_chan_release(inode, file); + else + return dahdi_specchan_release(inode, file, chan->channo); + } + if (unit == 255) { + chan = file->private_data; + if (chan) { + res = dahdi_specchan_release(inode, file, chan->channo); + dahdi_free_pseudo(chan); + } else { + module_printk(KERN_NOTICE, "Pseudo release and no private data??\n"); + res = 0; + } + return res; + } + return dahdi_specchan_release(inode, file, unit); +} + +#if 0 +static int dahdi_release(struct inode *inode, struct file *file) +{ + /* Lock the big zap lock when handling a release */ + unsigned long flags; + int res; + spin_lock_irqsave(&bigzaplock, flags); + res = __dahdi_release(inode, file); + spin_unlock_irqrestore(&bigzaplock, flags); + return res; +} +#endif + + +void dahdi_alarm_channel(struct dahdi_chan *chan, int alarms) +{ + unsigned long flags; + + spin_lock_irqsave(&chan->lock, flags); + if (chan->chan_alarms != alarms) { + chan->chan_alarms = alarms; + dahdi_qevent_nolock(chan, alarms ? DAHDI_EVENT_ALARM : DAHDI_EVENT_NOALARM); + } + spin_unlock_irqrestore(&chan->lock, flags); +} + +void dahdi_alarm_notify(struct dahdi_span *span) +{ + int x; + + span->alarms &= ~DAHDI_ALARM_LOOPBACK; + /* Determine maint status */ + if (span->maintstat || span->mainttimer) + span->alarms |= DAHDI_ALARM_LOOPBACK; + /* DON'T CHANGE THIS AGAIN. THIS WAS DONE FOR A REASON. + The expression (a != b) does *NOT* do the same thing + as ((!a) != (!b)) */ + /* if change in general state */ + if ((!span->alarms) != (!span->lastalarms)) { + span->lastalarms = span->alarms; + for (x = 0; x < span->channels; x++) + dahdi_alarm_channel(span->chans[x], span->alarms); + /* Switch to other master if current master in alarm */ + for (x=1; xalarms && (spans[x]->flags & DAHDI_FLAG_RUNNING)) { + if(master != spans[x]) + module_printk(KERN_NOTICE, "Master changed to %s\n", spans[x]->name); + master = spans[x]; + break; + } + } + } +} + +#define VALID_SPAN(j) do { \ + if ((j >= DAHDI_MAX_SPANS) || (j < 1)) \ + return -EINVAL; \ + if (!spans[j]) \ + return -ENXIO; \ +} while(0) + +#define CHECK_VALID_SPAN(j) do { \ + /* Start a given span */ \ + if (get_user(j, (int *)data)) \ + return -EFAULT; \ + VALID_SPAN(j); \ +} while(0) + +#define VALID_CHANNEL(j) do { \ + if ((j >= DAHDI_MAX_CHANNELS) || (j < 1)) \ + return -EINVAL; \ + if (!chans[j]) \ + return -ENXIO; \ +} while(0) + +static int dahdi_timer_ioctl(struct inode *node, struct file *file, unsigned int cmd, unsigned long data, struct dahdi_timer *timer) +{ + int j; + unsigned long flags; + switch(cmd) { + case DAHDI_TIMERCONFIG: + get_user(j, (int *)data); + if (j < 0) + j = 0; + spin_lock_irqsave(&zaptimerlock, flags); + timer->ms = timer->pos = j; + spin_unlock_irqrestore(&zaptimerlock, flags); + break; + case DAHDI_TIMERACK: + get_user(j, (int *)data); + spin_lock_irqsave(&zaptimerlock, flags); + if ((j < 1) || (j > timer->tripped)) + j = timer->tripped; + timer->tripped -= j; + spin_unlock_irqrestore(&zaptimerlock, flags); + break; + case DAHDI_GETEVENT: /* Get event on queue */ + j = DAHDI_EVENT_NONE; + spin_lock_irqsave(&zaptimerlock, flags); + /* set up for no event */ + if (timer->tripped) + j = DAHDI_EVENT_TIMER_EXPIRED; + if (timer->ping) + j = DAHDI_EVENT_TIMER_PING; + spin_unlock_irqrestore(&zaptimerlock, flags); + put_user(j,(int *)data); + break; + case DAHDI_TIMERPING: + spin_lock_irqsave(&zaptimerlock, flags); + timer->ping = 1; + wake_up_interruptible(&timer->sel); + spin_unlock_irqrestore(&zaptimerlock, flags); + break; + case DAHDI_TIMERPONG: + spin_lock_irqsave(&zaptimerlock, flags); + timer->ping = 0; + spin_unlock_irqrestore(&zaptimerlock, flags); + break; + default: + return -ENOTTY; + } + return 0; +} + +static int dahdi_ioctl_getgains(struct inode *node, struct file *file, + unsigned int cmd, unsigned long data, int unit) +{ + int res = 0; + struct dahdi_gains *gain; + int i, j; + + gain = kzalloc(sizeof(*gain), GFP_KERNEL); + if (!gain) + return -ENOMEM; + + if (copy_from_user(gain, (struct dahdi_gains *)data, sizeof(*gain))) { + res = -EFAULT; + goto cleanup; + } + i = gain->chan; /* get channel no */ + /* if zero, use current channel no */ + if (!i) + i = unit; + + /* make sure channel number makes sense */ + if ((i < 0) || (i > DAHDI_MAX_CHANNELS) || !chans[i]) { + res = -EINVAL; + goto cleanup; + } + + if (!(chans[i]->flags & DAHDI_FLAG_AUDIO)) { + res = -EINVAL; + goto cleanup; + } + gain->chan = i; /* put the span # in here */ + for (j = 0; j < 256; ++j) { + gain->txgain[j] = chans[i]->txgain[j]; + gain->rxgain[j] = chans[i]->rxgain[j]; + } + if (copy_to_user((struct dahdi_gains *)data, gain, sizeof(*gain))) { + res = -EFAULT; + goto cleanup; + } +cleanup: + + kfree(gain); + return res; +} + +static int dahdi_ioctl_setgains(struct inode *node, struct file *file, + unsigned int cmd, unsigned long data, int unit) +{ + int res = 0; + struct dahdi_gains *gain; + unsigned char *txgain, *rxgain; + int i, j; + unsigned long flags; + const int GAIN_TABLE_SIZE = sizeof(defgain); + + gain = kzalloc(sizeof(*gain), GFP_KERNEL); + if (!gain) + return -ENOMEM; + + if (copy_from_user(gain, (struct dahdi_gains *)data, sizeof(*gain))) { + res = -EFAULT; + goto cleanup; + } + i = gain->chan; /* get channel no */ + /* if zero, use current channel no */ + if (!i) + i = unit; + /* make sure channel number makes sense */ + if ((i < 0) || (i > DAHDI_MAX_CHANNELS) || !chans[i]) { + res = -EINVAL; + goto cleanup; + } + if (!(chans[i]->flags & DAHDI_FLAG_AUDIO)) { + res = -EINVAL; + goto cleanup; + } + + rxgain = kzalloc(GAIN_TABLE_SIZE*2, GFP_KERNEL); + if (!rxgain) { + res = -ENOMEM; + goto cleanup; + } + + gain->chan = i; /* put the span # in here */ + txgain = rxgain + GAIN_TABLE_SIZE; + + for (j = 0; j < GAIN_TABLE_SIZE; ++j) { + rxgain[j] = gain->rxgain[j]; + txgain[j] = gain->txgain[j]; + } + + if (!memcmp(rxgain, defgain, GAIN_TABLE_SIZE) && + !memcmp(txgain, defgain, GAIN_TABLE_SIZE)) { + kfree(rxgain); + spin_lock_irqsave(&chans[i]->lock, flags); + if (chans[i]->gainalloc) + kfree(chans[i]->rxgain); + chans[i]->gainalloc = 0; + chans[i]->rxgain = defgain; + chans[i]->txgain = defgain; + spin_unlock_irqrestore(&chans[i]->lock, flags); + } else { + /* This is a custom gain setting */ + spin_lock_irqsave(&chans[i]->lock, flags); + if (chans[i]->gainalloc) + kfree(chans[i]->rxgain); + chans[i]->gainalloc = 1; + chans[i]->rxgain = rxgain; + chans[i]->txgain = txgain; + spin_unlock_irqrestore(&chans[i]->lock, flags); + } + + if (copy_to_user((struct dahdi_gains *)data, gain, sizeof(*gain))) { + res = -EFAULT; + goto cleanup; + } +cleanup: + + kfree(gain); + return res; +} + +static int dahdi_common_ioctl(struct inode *node, struct file *file, unsigned int cmd, unsigned long data, int unit) +{ + union { + struct dahdi_spaninfo spaninfo; + struct dahdi_params param; + } stack; + struct dahdi_chan *chan; + unsigned long flags; + int i,j; + int return_master = 0; + size_t size_to_copy; + + switch(cmd) { + /* get channel parameters */ + case DAHDI_GET_PARAMS_V1: /* Intentional drop through. */ + case DAHDI_GET_PARAMS: + size_to_copy = sizeof(struct dahdi_params); + if (copy_from_user(&stack.param, (struct dahdi_params *) data, size_to_copy)) + return -EFAULT; + + /* check to see if the caller wants to receive our master channel number */ + if (stack.param.channo & DAHDI_GET_PARAMS_RETURN_MASTER) { + return_master = 1; + stack.param.channo &= ~DAHDI_GET_PARAMS_RETURN_MASTER; + } + + /* Pick the right channo's */ + if (!stack.param.channo || unit) { + stack.param.channo = unit; + } + /* Check validity of channel */ + VALID_CHANNEL(stack.param.channo); + chan = chans[stack.param.channo]; + + /* point to relevant structure */ + stack.param.sigtype = chan->sig; /* get signalling type */ + /* return non-zero if rx not in idle state */ + if (chan->span) { + j = dahdi_q_sig(chan); + if (j >= 0) { /* if returned with success */ + stack.param.rxisoffhook = ((chan->rxsig & (j >> 8)) != (j & 0xff)); + } else { + stack.param.rxisoffhook = ((chan->rxhooksig != DAHDI_RXSIG_ONHOOK) && + (chan->rxhooksig != DAHDI_RXSIG_INITIAL)); + } + } else if ((chan->txstate == DAHDI_TXSTATE_KEWL) || (chan->txstate == DAHDI_TXSTATE_AFTERKEWL)) + stack.param.rxisoffhook = 1; + else + stack.param.rxisoffhook = 0; + if (chan->span && chan->span->rbsbits && !(chan->sig & DAHDI_SIG_CLEAR)) { + stack.param.rxbits = chan->rxsig; + stack.param.txbits = chan->txsig; + stack.param.idlebits = chan->idlebits; + } else { + stack.param.rxbits = -1; + stack.param.txbits = -1; + stack.param.idlebits = 0; + } + if (chan->span && (chan->span->rbsbits || chan->span->hooksig) && + !(chan->sig & DAHDI_SIG_CLEAR)) { + stack.param.rxhooksig = chan->rxhooksig; + stack.param.txhooksig = chan->txhooksig; + } else { + stack.param.rxhooksig = -1; + stack.param.txhooksig = -1; + } + stack.param.prewinktime = chan->prewinktime; + stack.param.preflashtime = chan->preflashtime; + stack.param.winktime = chan->winktime; + stack.param.flashtime = chan->flashtime; + stack.param.starttime = chan->starttime; + stack.param.rxwinktime = chan->rxwinktime; + stack.param.rxflashtime = chan->rxflashtime; + stack.param.debouncetime = chan->debouncetime; + stack.param.channo = chan->channo; + stack.param.chan_alarms = chan->chan_alarms; + + /* if requested, put the master channel number in the top 16 bits of the result */ + if (return_master) + stack.param.channo |= chan->master->channo << 16; + + stack.param.pulsemaketime = chan->pulsemaketime; + stack.param.pulsebreaktime = chan->pulsebreaktime; + stack.param.pulseaftertime = chan->pulseaftertime; + if (chan->span) stack.param.spanno = chan->span->spanno; + else stack.param.spanno = 0; + dahdi_copy_string(stack.param.name, chan->name, sizeof(stack.param.name)); + stack.param.chanpos = chan->chanpos; + stack.param.sigcap = chan->sigcap; + /* Return current law */ + if (chan->xlaw == __dahdi_alaw) + stack.param.curlaw = DAHDI_LAW_ALAW; + else + stack.param.curlaw = DAHDI_LAW_MULAW; + + if (copy_to_user((struct dahdi_params *) data, &stack.param, size_to_copy)) + return -EFAULT; + + break; + /* set channel parameters */ + case DAHDI_SET_PARAMS: + if (copy_from_user(&stack.param, (struct dahdi_params *) data, sizeof(struct dahdi_params))) + return -EFAULT; + + stack.param.chan_alarms = 0; /* be explicit about the above */ + + /* Pick the right channo's */ + if (!stack.param.channo || unit) { + stack.param.channo = unit; + } + /* Check validity of channel */ + VALID_CHANNEL(stack.param.channo); + chan = chans[stack.param.channo]; + /* point to relevant structure */ + /* NOTE: sigtype is *not* included in this */ + /* get timing stack.paramters */ + chan->prewinktime = stack.param.prewinktime; + chan->preflashtime = stack.param.preflashtime; + chan->winktime = stack.param.winktime; + chan->flashtime = stack.param.flashtime; + chan->starttime = stack.param.starttime; + /* Update ringtime if not using a tone zone */ + if (!chan->curzone) + chan->ringcadence[0] = chan->starttime; + chan->rxwinktime = stack.param.rxwinktime; + chan->rxflashtime = stack.param.rxflashtime; + chan->debouncetime = stack.param.debouncetime; + chan->pulsemaketime = stack.param.pulsemaketime; + chan->pulsebreaktime = stack.param.pulsebreaktime; + chan->pulseaftertime = stack.param.pulseaftertime; + break; + case DAHDI_GETGAINS_V1: /* Intentional drop through. */ + case DAHDI_GETGAINS: /* get gain stuff */ + return dahdi_ioctl_getgains(node, file, cmd, data, unit); + case DAHDI_SETGAINS: /* set gain stuff */ + return dahdi_ioctl_setgains(node, file, cmd, data, unit); + case DAHDI_SPANSTAT: + size_to_copy = sizeof(struct dahdi_spaninfo); + if (copy_from_user(&stack.spaninfo, (struct dahdi_spaninfo *) data, size_to_copy)) + return -EFAULT; + i = stack.spaninfo.spanno; /* get specified span number */ + if ((i < 0) || (i >= maxspans)) return(-EINVAL); /* if bad span no */ + if (i == 0) { + /* if to figure it out for this chan */ + if (!chans[unit]) + return -EINVAL; + i = chans[unit]->span->spanno; + } + if (!spans[i]) + return -EINVAL; + stack.spaninfo.spanno = i; /* put the span # in here */ + stack.spaninfo.totalspans = 0; + if (maxspans) stack.spaninfo.totalspans = maxspans - 1; /* put total number of spans here */ + dahdi_copy_string(stack.spaninfo.desc, spans[i]->desc, sizeof(stack.spaninfo.desc)); + dahdi_copy_string(stack.spaninfo.name, spans[i]->name, sizeof(stack.spaninfo.name)); + stack.spaninfo.alarms = spans[i]->alarms; /* get alarm status */ + stack.spaninfo.bpvcount = spans[i]->bpvcount; /* get BPV count */ + stack.spaninfo.rxlevel = spans[i]->rxlevel; /* get rx level */ + stack.spaninfo.txlevel = spans[i]->txlevel; /* get tx level */ + stack.spaninfo.crc4count = spans[i]->crc4count; /* get CRC4 error count */ + stack.spaninfo.ebitcount = spans[i]->ebitcount; /* get E-bit error count */ + stack.spaninfo.fascount = spans[i]->fascount; /* get FAS error count */ + stack.spaninfo.irqmisses = spans[i]->irqmisses; /* get IRQ miss count */ + stack.spaninfo.syncsrc = spans[i]->syncsrc; /* get active sync source */ + stack.spaninfo.totalchans = spans[i]->channels; + stack.spaninfo.numchans = 0; + for (j = 0; j < spans[i]->channels; j++) { + if (spans[i]->chans[j]->sig) + stack.spaninfo.numchans++; + } + stack.spaninfo.lbo = spans[i]->lbo; + stack.spaninfo.lineconfig = spans[i]->lineconfig; + stack.spaninfo.irq = spans[i]->irq; + stack.spaninfo.linecompat = spans[i]->linecompat; + dahdi_copy_string(stack.spaninfo.lboname, dahdi_lboname(spans[i]->lbo), sizeof(stack.spaninfo.lboname)); + if (spans[i]->manufacturer) + dahdi_copy_string(stack.spaninfo.manufacturer, spans[i]->manufacturer, + sizeof(stack.spaninfo.manufacturer)); + if (spans[i]->devicetype) + dahdi_copy_string(stack.spaninfo.devicetype, spans[i]->devicetype, sizeof(stack.spaninfo.devicetype)); + dahdi_copy_string(stack.spaninfo.location, spans[i]->location, sizeof(stack.spaninfo.location)); + if (spans[i]->spantype) + dahdi_copy_string(stack.spaninfo.spantype, spans[i]->spantype, sizeof(stack.spaninfo.spantype)); + + if (copy_to_user((struct dahdi_spaninfo *) data, &stack.spaninfo, size_to_copy)) + return -EFAULT; + break; + case DAHDI_CHANDIAG_V1: /* Intentional drop through. */ + case DAHDI_CHANDIAG: + { + /* there really is no need to initialize this structure because when it is used it has + * already been completely overwritten, but apparently the compiler cannot figure that + * out and warns about uninitialized usage... so initialize it. + */ + struct dahdi_echocan_state ec_state = { .ops = NULL, }; + + get_user(j, (int *) data); /* get channel number from user */ + /* make sure its a valid channel number */ + if ((j < 1) || (j >= maxchans)) + return -EINVAL; + /* if channel not mapped, not there */ + if (!chans[j]) + return -EINVAL; + + chan = kmalloc(sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + /* lock channel */ + spin_lock_irqsave(&chans[j]->lock, flags); + /* make static copy of channel */ + *chan = *chans[j]; + if (chan->ec_state) { + ec_state = *chan->ec_state; + } + /* release it. */ + spin_unlock_irqrestore(&chans[j]->lock, flags); + + module_printk(KERN_INFO, "Dump of DAHDI Channel %d (%s,%d,%d):\n\n",j, + chan->name, chan->channo, chan->chanpos); + module_printk(KERN_INFO, "flags: %x hex, writechunk: %p, readchunk: %p\n", + (unsigned int) chan->flags, chan->writechunk, chan->readchunk); + module_printk(KERN_INFO, "rxgain: %p, txgain: %p, gainalloc: %d\n", + chan->rxgain, chan->txgain, chan->gainalloc); + module_printk(KERN_INFO, "span: %p, sig: %x hex, sigcap: %x hex\n", + chan->span, chan->sig, chan->sigcap); + module_printk(KERN_INFO, "inreadbuf: %d, outreadbuf: %d, inwritebuf: %d, outwritebuf: %d\n", + chan->inreadbuf, chan->outreadbuf, chan->inwritebuf, chan->outwritebuf); + module_printk(KERN_INFO, "blocksize: %d, numbufs: %d, txbufpolicy: %d, txbufpolicy: %d\n", + chan->blocksize, chan->numbufs, chan->txbufpolicy, chan->rxbufpolicy); + module_printk(KERN_INFO, "txdisable: %d, rxdisable: %d, iomask: %d\n", + chan->txdisable, chan->rxdisable, chan->iomask); + module_printk(KERN_INFO, "curzone: %p, tonezone: %d, curtone: %p, tonep: %d\n", + chan->curzone, chan->tonezone, chan->curtone, chan->tonep); + module_printk(KERN_INFO, "digitmode: %d, txdialbuf: %s, dialing: %d, aftdialtimer: %d, cadpos. %d\n", + chan->digitmode, chan->txdialbuf, chan->dialing, + chan->afterdialingtimer, chan->cadencepos); + module_printk(KERN_INFO, "confna: %d, confn: %d, confmode: %d, confmute: %d\n", + chan->confna, chan->_confn, chan->confmode, chan->confmute); + module_printk(KERN_INFO, "ec: %p, deflaw: %d, xlaw: %p\n", + chan->ec_state, chan->deflaw, chan->xlaw); + if (chan->ec_state) { + module_printk(KERN_INFO, "echostate: %02x, echotimer: %d, echolastupdate: %d\n", + ec_state.status.mode, ec_state.status.pretrain_timer, ec_state.status.last_train_tap); + } + module_printk(KERN_INFO, "itimer: %d, otimer: %d, ringdebtimer: %d\n\n", + chan->itimer, chan->otimer, chan->ringdebtimer); + kfree(chan); + break; + } + default: + return -ENOTTY; + } + return 0; +} + +static int (*dahdi_dynamic_ioctl)(unsigned int cmd, unsigned long data); + +void dahdi_set_dynamic_ioctl(int (*func)(unsigned int cmd, unsigned long data)) +{ + dahdi_dynamic_ioctl = func; +} + +static int (*dahdi_hpec_ioctl)(unsigned int cmd, unsigned long data); + +void dahdi_set_hpec_ioctl(int (*func)(unsigned int cmd, unsigned long data)) +{ + dahdi_hpec_ioctl = func; +} + +static void recalc_slaves(struct dahdi_chan *chan) +{ + int x; + struct dahdi_chan *last = chan; + + /* Makes no sense if you don't have a span */ + if (!chan->span) + return; + +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "Recalculating slaves on %s\n", chan->name); +#endif + + /* Link all slaves appropriately */ + for (x=chan->chanpos;xspan->channels;x++) + if (chan->span->chans[x]->master == chan) { +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "Channel %s, slave to %s, last is %s, its next will be %d\n", + chan->span->chans[x]->name, chan->name, last->name, x); +#endif + last->nextslave = x; + last = chan->span->chans[x]; + } + /* Terminate list */ + last->nextslave = 0; +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "Done Recalculating slaves on %s (last is %s)\n", chan->name, last->name); +#endif +} + +static int dahdi_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data) +{ + /* I/O CTL's for control interface */ + int i,j; + int sigcap; + int res = 0; + int x,y; + struct dahdi_chan *newmaster; + unsigned long flags; + int rv; + switch(cmd) { + case DAHDI_INDIRECT: + { + struct dahdi_indirect_data ind; + + if (copy_from_user(&ind, (struct dahdi_indirect_data *)data, sizeof(ind))) + return -EFAULT; + VALID_CHANNEL(ind.chan); + return dahdi_chan_ioctl(inode, file, ind.op, (unsigned long) ind.data, ind.chan); + } + case DAHDI_SPANCONFIG: + { + struct dahdi_lineconfig lc; + + if (copy_from_user(&lc, (struct dahdi_lineconfig *)data, sizeof(lc))) + return -EFAULT; + VALID_SPAN(lc.span); + if ((lc.lineconfig & 0x07f0 & spans[lc.span]->linecompat) != (lc.lineconfig & 0x07f0)) + return -EINVAL; + if (spans[lc.span]->spanconfig) { + spans[lc.span]->lineconfig = lc.lineconfig; + spans[lc.span]->lbo = lc.lbo; + spans[lc.span]->txlevel = lc.lbo; + spans[lc.span]->rxlevel = 0; + + return spans[lc.span]->spanconfig(spans[lc.span], &lc); + } + return 0; + } + case DAHDI_STARTUP: + CHECK_VALID_SPAN(j); + if (spans[j]->flags & DAHDI_FLAG_RUNNING) + return 0; + if (spans[j]->startup) + res = spans[j]->startup(spans[j]); + if (!res) { + /* Mark as running and hangup any channels */ + spans[j]->flags |= DAHDI_FLAG_RUNNING; + for (x=0;xchannels;x++) { + y = dahdi_q_sig(spans[j]->chans[x]) & 0xff; + if (y >= 0) spans[j]->chans[x]->rxsig = (unsigned char)y; + spin_lock_irqsave(&spans[j]->chans[x]->lock, flags); + dahdi_hangup(spans[j]->chans[x]); + spin_unlock_irqrestore(&spans[j]->chans[x]->lock, flags); + /* + * Set the rxhooksig back to + * DAHDI_RXSIG_INITIAL so that new events are + * queued on the channel with the actual + * recieved hook state. + * + */ + spans[j]->chans[x]->rxhooksig = DAHDI_RXSIG_INITIAL; + } + } + return 0; + case DAHDI_SHUTDOWN: + CHECK_VALID_SPAN(j); + if (spans[j]->shutdown) + res = spans[j]->shutdown(spans[j]); + spans[j]->flags &= ~DAHDI_FLAG_RUNNING; + return 0; + case DAHDI_ATTACH_ECHOCAN: + { + struct dahdi_attach_echocan ae; + const struct dahdi_echocan_factory *new = NULL, *old; + + if (copy_from_user(&ae, (struct dahdi_attach_echocan *) data, sizeof(ae))) { + return -EFAULT; + } + + VALID_CHANNEL(ae.chan); + + ae.echocan[sizeof(ae.echocan) - 1] = 0; + if (ae.echocan[0]) { + if (!(new = find_echocan(ae.echocan))) { + return -EINVAL; + } + } + + spin_lock_irqsave(&chans[ae.chan]->lock, flags); + old = chans[ae.chan]->ec_factory; + chans[ae.chan]->ec_factory = new; + spin_unlock_irqrestore(&chans[ae.chan]->lock, flags); + + if (old) { + release_echocan(old); + } + + break; + } + case DAHDI_CHANCONFIG: + { + struct dahdi_chanconfig ch; + + if (copy_from_user(&ch, (struct dahdi_chanconfig *)data, sizeof(ch))) + return -EFAULT; + VALID_CHANNEL(ch.chan); + if (ch.sigtype == DAHDI_SIG_SLAVE) { + /* We have to use the master's sigtype */ + if ((ch.master < 1) || (ch.master >= DAHDI_MAX_CHANNELS)) + return -EINVAL; + if (!chans[ch.master]) + return -EINVAL; + ch.sigtype = chans[ch.master]->sig; + newmaster = chans[ch.master]; + } else if ((ch.sigtype & __DAHDI_SIG_DACS) == __DAHDI_SIG_DACS) { + newmaster = chans[ch.chan]; + if ((ch.idlebits < 1) || (ch.idlebits >= DAHDI_MAX_CHANNELS)) + return -EINVAL; + if (!chans[ch.idlebits]) + return -EINVAL; + } else { + newmaster = chans[ch.chan]; + } + spin_lock_irqsave(&chans[ch.chan]->lock, flags); +#ifdef CONFIG_DAHDI_NET + if (chans[ch.chan]->flags & DAHDI_FLAG_NETDEV) { + if (ztchan_to_dev(chans[ch.chan])->flags & IFF_UP) { + spin_unlock_irqrestore(&chans[ch.chan]->lock, flags); + module_printk(KERN_WARNING, "Can't switch HDLC net mode on channel %s, since current interface is up\n", chans[ch.chan]->name); + return -EBUSY; + } + spin_unlock_irqrestore(&chans[ch.chan]->lock, flags); + unregister_hdlc_device(chans[ch.chan]->hdlcnetdev->netdev); + spin_lock_irqsave(&chans[ch.chan]->lock, flags); + free_netdev(chans[ch.chan]->hdlcnetdev->netdev); + kfree(chans[ch.chan]->hdlcnetdev); + chans[ch.chan]->hdlcnetdev = NULL; + chans[ch.chan]->flags &= ~DAHDI_FLAG_NETDEV; + } +#else + if (ch.sigtype == DAHDI_SIG_HDLCNET) { + spin_unlock_irqrestore(&chans[ch.chan]->lock, flags); + module_printk(KERN_WARNING, "DAHDI networking not supported by this build.\n"); + return -ENOSYS; + } +#endif + sigcap = chans[ch.chan]->sigcap; + /* If they support clear channel, then they support the HDLC and such through + us. */ + if (sigcap & DAHDI_SIG_CLEAR) + sigcap |= (DAHDI_SIG_HDLCRAW | DAHDI_SIG_HDLCFCS | DAHDI_SIG_HDLCNET | DAHDI_SIG_DACS); + + if ((sigcap & ch.sigtype) != ch.sigtype) + res = -EINVAL; + + if (chans[ch.chan]->master != chans[ch.chan]) { + struct dahdi_chan *oldmaster = chans[ch.chan]->master; + + /* Clear the master channel */ + chans[ch.chan]->master = chans[ch.chan]; + chans[ch.chan]->nextslave = 0; + /* Unlink this channel from the master's channel list */ + recalc_slaves(oldmaster); + } + + if (!res) { + chans[ch.chan]->sig = ch.sigtype; + if (chans[ch.chan]->sig == DAHDI_SIG_CAS) + chans[ch.chan]->idlebits = ch.idlebits; + else + chans[ch.chan]->idlebits = 0; + if ((ch.sigtype & DAHDI_SIG_CLEAR) == DAHDI_SIG_CLEAR) { + /* Set clear channel flag if appropriate */ + chans[ch.chan]->flags &= ~DAHDI_FLAG_AUDIO; + chans[ch.chan]->flags |= DAHDI_FLAG_CLEAR; + } else { + /* Set audio flag and not clear channel otherwise */ + chans[ch.chan]->flags |= DAHDI_FLAG_AUDIO; + chans[ch.chan]->flags &= ~DAHDI_FLAG_CLEAR; + } + if ((ch.sigtype & DAHDI_SIG_HDLCRAW) == DAHDI_SIG_HDLCRAW) { + /* Set the HDLC flag */ + chans[ch.chan]->flags |= DAHDI_FLAG_HDLC; + } else { + /* Clear the HDLC flag */ + chans[ch.chan]->flags &= ~DAHDI_FLAG_HDLC; + } + if ((ch.sigtype & DAHDI_SIG_HDLCFCS) == DAHDI_SIG_HDLCFCS) { + /* Set FCS to be calculated if appropriate */ + chans[ch.chan]->flags |= DAHDI_FLAG_FCS; + } else { + /* Clear FCS flag */ + chans[ch.chan]->flags &= ~DAHDI_FLAG_FCS; + } + if ((ch.sigtype & __DAHDI_SIG_DACS) == __DAHDI_SIG_DACS) { + /* Setup conference properly */ + chans[ch.chan]->confmode = DAHDI_CONF_DIGITALMON; + chans[ch.chan]->confna = ch.idlebits; + if (chans[ch.chan]->span && + chans[ch.chan]->span->dacs && + chans[ch.idlebits] && + chans[ch.chan]->span && + (chans[ch.chan]->span->dacs == chans[ch.idlebits]->span->dacs)) + chans[ch.chan]->span->dacs(chans[ch.chan], chans[ch.idlebits]); + } else if (chans[ch.chan]->span && chans[ch.chan]->span->dacs) { + chans[ch.chan]->span->dacs(chans[ch.chan], NULL); + } + chans[ch.chan]->master = newmaster; + /* Note new slave if we are not our own master */ + if (newmaster != chans[ch.chan]) { + recalc_slaves(chans[ch.chan]->master); + } + if ((ch.sigtype & DAHDI_SIG_HARDHDLC) == DAHDI_SIG_HARDHDLC) { + chans[ch.chan]->flags &= ~DAHDI_FLAG_FCS; + chans[ch.chan]->flags &= ~DAHDI_FLAG_HDLC; + chans[ch.chan]->flags |= DAHDI_FLAG_NOSTDTXRX; + } else { + chans[ch.chan]->flags &= ~DAHDI_FLAG_NOSTDTXRX; + } + + if ((ch.sigtype & DAHDI_SIG_MTP2) == DAHDI_SIG_MTP2) + chans[ch.chan]->flags |= DAHDI_FLAG_MTP2; + else + chans[ch.chan]->flags &= ~DAHDI_FLAG_MTP2; + } + + if (!res && chans[ch.chan]->span->chanconfig) { + res = chans[ch.chan]->span->chanconfig(chans[ch.chan], + ch.sigtype); + } + +#ifdef CONFIG_DAHDI_NET + if (!res && + (newmaster == chans[ch.chan]) && + (chans[ch.chan]->sig == DAHDI_SIG_HDLCNET)) { + chans[ch.chan]->hdlcnetdev = dahdi_hdlc_alloc(); + if (chans[ch.chan]->hdlcnetdev) { +/* struct hdlc_device *hdlc = chans[ch.chan]->hdlcnetdev; + struct net_device *d = hdlc_to_dev(hdlc); mmm...get it right later --byg */ + + chans[ch.chan]->hdlcnetdev->netdev = alloc_hdlcdev(chans[ch.chan]->hdlcnetdev); + if (chans[ch.chan]->hdlcnetdev->netdev) { + chans[ch.chan]->hdlcnetdev->chan = chans[ch.chan]; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) + SET_MODULE_OWNER(chans[ch.chan]->hdlcnetdev->netdev); +#endif + chans[ch.chan]->hdlcnetdev->netdev->irq = chans[ch.chan]->span->irq; + chans[ch.chan]->hdlcnetdev->netdev->tx_queue_len = 50; + chans[ch.chan]->hdlcnetdev->netdev->do_ioctl = dahdi_net_ioctl; + chans[ch.chan]->hdlcnetdev->netdev->open = dahdi_net_open; + chans[ch.chan]->hdlcnetdev->netdev->stop = dahdi_net_stop; + dev_to_hdlc(chans[ch.chan]->hdlcnetdev->netdev)->attach = dahdi_net_attach; + dev_to_hdlc(chans[ch.chan]->hdlcnetdev->netdev)->xmit = dahdi_xmit; + spin_unlock_irqrestore(&chans[ch.chan]->lock, flags); + /* Briefly restore interrupts while we register the device */ + res = dahdi_register_hdlc_device(chans[ch.chan]->hdlcnetdev->netdev, ch.netdev_name); + spin_lock_irqsave(&chans[ch.chan]->lock, flags); + } else { + module_printk(KERN_NOTICE, "Unable to allocate hdlc: *shrug*\n"); + res = -1; + } + if (!res) + chans[ch.chan]->flags |= DAHDI_FLAG_NETDEV; + } else { + module_printk(KERN_NOTICE, "Unable to allocate netdev: out of memory\n"); + res = -1; + } + } +#endif + if ((chans[ch.chan]->sig == DAHDI_SIG_HDLCNET) && + (chans[ch.chan] == newmaster) && + !(chans[ch.chan]->flags & DAHDI_FLAG_NETDEV)) + module_printk(KERN_NOTICE, "Unable to register HDLC device for channel %s\n", chans[ch.chan]->name); + if (!res) { + /* (CNET) Ignore remote hookflash? */ + chans[ch.chan]->ignoreflash = ch.ignoreflash; + /* (CNET) Use Oslo/NZ or Swedish pulse mapping? */ + chans[ch.chan]->map_pulse = ch.map_pulse; + /* (CNET) Make outpulsing audible */ + chans[ch.chan]->hearpulsing = ch.hearpulsing; + /* Setup default law */ + chans[ch.chan]->deflaw = ch.deflaw; + /* Copy back any modified settings */ + spin_unlock_irqrestore(&chans[ch.chan]->lock, flags); + if (copy_to_user((struct dahdi_chanconfig *)data, &ch, sizeof(ch))) + return -EFAULT; + spin_lock_irqsave(&chans[ch.chan]->lock, flags); + /* And hangup */ + dahdi_hangup(chans[ch.chan]); + y = dahdi_q_sig(chans[ch.chan]) & 0xff; + if (y >= 0) + chans[ch.chan]->rxsig = (unsigned char) y; + chans[ch.chan]->rxhooksig = DAHDI_RXSIG_INITIAL; + } +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "Configured channel %s, flags %04lx, sig %04x\n", chans[ch.chan]->name, chans[ch.chan]->flags, chans[ch.chan]->sig); +#endif + spin_unlock_irqrestore(&chans[ch.chan]->lock, flags); + + return res; + } + case DAHDI_SFCONFIG: + { + struct dahdi_sfconfig sf; + + if (copy_from_user(&sf, (struct dahdi_chanconfig *)data, sizeof(sf))) + return -EFAULT; + VALID_CHANNEL(sf.chan); + if (chans[sf.chan]->sig != DAHDI_SIG_SF) return -EINVAL; + spin_lock_irqsave(&chans[sf.chan]->lock, flags); + chans[sf.chan]->rxp1 = sf.rxp1; + chans[sf.chan]->rxp2 = sf.rxp2; + chans[sf.chan]->rxp3 = sf.rxp3; + chans[sf.chan]->txtone = sf.txtone; + chans[sf.chan]->tx_v2 = sf.tx_v2; + chans[sf.chan]->tx_v3 = sf.tx_v3; + chans[sf.chan]->toneflags = sf.toneflag; + if (sf.txtone) /* if set to make tone for tx */ + { + if ((chans[sf.chan]->txhooksig && !(sf.toneflag & DAHDI_REVERSE_TXTONE)) || + ((!chans[sf.chan]->txhooksig) && (sf.toneflag & DAHDI_REVERSE_TXTONE))) + { + set_txtone(chans[sf.chan],sf.txtone,sf.tx_v2,sf.tx_v3); + } + else + { + set_txtone(chans[sf.chan],0,0,0); + } + } + spin_unlock_irqrestore(&chans[sf.chan]->lock, flags); + return res; + } + case DAHDI_DEFAULTZONE: + if (get_user(j,(int *)data)) + return -EFAULT; + return dahdi_set_default_zone(j); + case DAHDI_LOADZONE: + return ioctl_load_zone(data); + case DAHDI_FREEZONE: + get_user(j, (int *) data); + return free_tone_zone(j); + case DAHDI_SET_DIALPARAMS: + { + struct dahdi_dialparams tdp; + + if (copy_from_user(&tdp, (struct dahdi_dialparams *) data, sizeof(tdp))) + return -EFAULT; + + if ((tdp.dtmf_tonelen >= 10) && (tdp.dtmf_tonelen <= 4000)) { + global_dialparams.dtmf_tonelen = tdp.dtmf_tonelen; + } + if ((tdp.mfv1_tonelen >= 10) && (tdp.mfv1_tonelen <= 4000)) { + global_dialparams.mfv1_tonelen = tdp.mfv1_tonelen; + } + if ((tdp.mfr2_tonelen >= 10) && (tdp.mfr2_tonelen <= 4000)) { + global_dialparams.mfr2_tonelen = tdp.mfr2_tonelen; + } + + /* update the lengths in all currently loaded zones */ + write_lock(&zone_lock); + for (j = 0; j < ARRAY_SIZE(tone_zones); j++) { + struct dahdi_zone *z = tone_zones[j]; + + if (!z) + continue; + + for (i = 0; i < ARRAY_SIZE(z->dtmf); i++) { + z->dtmf[i].tonesamples = global_dialparams.dtmf_tonelen * DAHDI_CHUNKSIZE; + } + + /* for MFR1, we only adjust the length of the digits */ + for (i = DAHDI_TONE_MFR1_0; i <= DAHDI_TONE_MFR1_9; i++) { + z->mfr1[i - DAHDI_TONE_MFR1_BASE].tonesamples = global_dialparams.mfv1_tonelen * DAHDI_CHUNKSIZE; + } + + for (i = 0; i < ARRAY_SIZE(z->mfr2_fwd); i++) { + z->mfr2_fwd[i].tonesamples = global_dialparams.mfr2_tonelen * DAHDI_CHUNKSIZE; + } + + for (i = 0; i < ARRAY_SIZE(z->mfr2_rev); i++) { + z->mfr2_rev[i].tonesamples = global_dialparams.mfr2_tonelen * DAHDI_CHUNKSIZE; + } + } + write_unlock(&zone_lock); + + dtmf_silence.tonesamples = global_dialparams.dtmf_tonelen * DAHDI_CHUNKSIZE; + mfr1_silence.tonesamples = global_dialparams.mfv1_tonelen * DAHDI_CHUNKSIZE; + mfr2_silence.tonesamples = global_dialparams.mfr2_tonelen * DAHDI_CHUNKSIZE; + + break; + } + case DAHDI_GET_DIALPARAMS: + { + struct dahdi_dialparams tdp; + + tdp = global_dialparams; + if (copy_to_user((struct dahdi_dialparams *) data, &tdp, sizeof(tdp))) + return -EFAULT; + break; + } + case DAHDI_GETVERSION: + { + struct dahdi_versioninfo vi; + struct ecfactory *cur; + size_t space = sizeof(vi.echo_canceller) - 1; + + memset(&vi, 0, sizeof(vi)); + dahdi_copy_string(vi.version, DAHDI_VERSION, sizeof(vi.version)); + read_lock(&ecfactory_list_lock); + list_for_each_entry(cur, &ecfactory_list, list) { + strncat(vi.echo_canceller + strlen(vi.echo_canceller), cur->ec->name, space); + space -= strlen(cur->ec->name); + if (space < 1) { + break; + } + if (cur->list.next && (cur->list.next != &ecfactory_list)) { + strncat(vi.echo_canceller + strlen(vi.echo_canceller), ", ", space); + space -= 2; + if (space < 1) { + break; + } + } + } + read_unlock(&ecfactory_list_lock); + if (copy_to_user((struct dahdi_versioninfo *) data, &vi, sizeof(vi))) + return -EFAULT; + break; + } + case DAHDI_MAINT: /* do maintenance stuff */ + { + struct dahdi_maintinfo maint; + /* get struct from user */ + if (copy_from_user(&maint,(struct dahdi_maintinfo *) data, sizeof(maint))) + return -EFAULT; + /* must be valid span number */ + if ((maint.spanno < 1) || (maint.spanno > DAHDI_MAX_SPANS) || (!spans[maint.spanno])) + return -EINVAL; + if (!spans[maint.spanno]->maint) + return -ENOSYS; + spin_lock_irqsave(&spans[maint.spanno]->lock, flags); + /* save current maint state */ + i = spans[maint.spanno]->maintstat; + /* set maint mode */ + spans[maint.spanno]->maintstat = maint.command; + switch(maint.command) { + case DAHDI_MAINT_NONE: + case DAHDI_MAINT_LOCALLOOP: + case DAHDI_MAINT_REMOTELOOP: + /* if same, ignore it */ + if (i == maint.command) + break; + rv = spans[maint.spanno]->maint(spans[maint.spanno], maint.command); + spin_unlock_irqrestore(&spans[maint.spanno]->lock, flags); + if (rv) + return rv; + spin_lock_irqsave(&spans[maint.spanno]->lock, flags); + break; + case DAHDI_MAINT_LOOPUP: + case DAHDI_MAINT_LOOPDOWN: + spans[maint.spanno]->mainttimer = DAHDI_LOOPCODE_TIME * DAHDI_CHUNKSIZE; + rv = spans[maint.spanno]->maint(spans[maint.spanno], maint.command); + spin_unlock_irqrestore(&spans[maint.spanno]->lock, flags); + if (rv) + return rv; + rv = schluffen(&spans[maint.spanno]->maintq); + if (rv) + return rv; + spin_lock_irqsave(&spans[maint.spanno]->lock, flags); + break; + default: + module_printk(KERN_NOTICE, "Unknown maintenance event: %d\n", maint.command); + } + dahdi_alarm_notify(spans[maint.spanno]); /* process alarm-related events */ + spin_unlock_irqrestore(&spans[maint.spanno]->lock, flags); + break; + } + case DAHDI_DYNAMIC_CREATE: + case DAHDI_DYNAMIC_DESTROY: + if (dahdi_dynamic_ioctl) { + return dahdi_dynamic_ioctl(cmd, data); + } else { + request_module("dahdi_dynamic"); + if (dahdi_dynamic_ioctl) + return dahdi_dynamic_ioctl(cmd, data); + } + return -ENOSYS; + case DAHDI_EC_LICENSE_CHALLENGE: + case DAHDI_EC_LICENSE_RESPONSE: + if (dahdi_hpec_ioctl) { + return dahdi_hpec_ioctl(cmd, data); + } else { + request_module("dahdi_echocan_hpec"); + if (dahdi_hpec_ioctl) + return dahdi_hpec_ioctl(cmd, data); + } + return -ENOSYS; + default: + return dahdi_common_ioctl(inode, file, cmd, data, 0); + } + return 0; +} + +static int ioctl_dahdi_dial(struct dahdi_chan *chan, unsigned long data) +{ + struct dahdi_dialoperation *tdo; + unsigned long flags; + char *s; + int rv; + + tdo = kmalloc(sizeof(*tdo), GFP_KERNEL); + + if (!tdo) + return -ENOMEM; + + if (copy_from_user(tdo, (struct dahdi_dialoperation *)data, sizeof(*tdo))) + return -EFAULT; + rv = 0; + /* Force proper NULL termination and uppercase entry */ + tdo->dialstr[DAHDI_MAX_DTMF_BUF - 1] = '\0'; + for (s = tdo->dialstr; *s; s++) + *s = toupper(*s); + spin_lock_irqsave(&chan->lock, flags); + if (!chan->curzone) { + spin_unlock_irqrestore(&chan->lock, flags); + /* The tone zones are loaded by dahdi_cfg from /etc/dahdi/system.conf */ + module_printk(KERN_WARNING, "Cannot dial until a tone zone is loaded.\n"); + return -ENODATA; + } + switch (tdo->op) { + case DAHDI_DIAL_OP_CANCEL: + chan->curtone = NULL; + chan->dialing = 0; + chan->txdialbuf[0] = '\0'; + chan->tonep = 0; + chan->pdialcount = 0; + break; + case DAHDI_DIAL_OP_REPLACE: + strcpy(chan->txdialbuf, tdo->dialstr); + chan->dialing = 1; + __do_dtmf(chan); + break; + case DAHDI_DIAL_OP_APPEND: + if (strlen(tdo->dialstr) + strlen(chan->txdialbuf) >= (DAHDI_MAX_DTMF_BUF - 1)) { + rv = -EBUSY; + break; + } + dahdi_copy_string(chan->txdialbuf + strlen(chan->txdialbuf), tdo->dialstr, DAHDI_MAX_DTMF_BUF - strlen(chan->txdialbuf)); + if (!chan->dialing) { + chan->dialing = 1; + __do_dtmf(chan); + } + break; + default: + rv = -EINVAL; + } + spin_unlock_irqrestore(&chan->lock, flags); + return rv; +} + +static int dahdi_chanandpseudo_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit) +{ + struct dahdi_chan *chan = chans[unit]; + union { + struct dahdi_bufferinfo bi; + struct dahdi_confinfo conf; + struct dahdi_ring_cadence cad; + } stack; + unsigned long flags; + int i, j, k, rv; + int ret, c; + + if (!chan) + return -EINVAL; + switch(cmd) { + case DAHDI_DIALING: + spin_lock_irqsave(&chan->lock, flags); + j = chan->dialing; + spin_unlock_irqrestore(&chan->lock, flags); + if (copy_to_user((int *)data,&j,sizeof(int))) + return -EFAULT; + return 0; + case DAHDI_DIAL: + return ioctl_dahdi_dial(chan, data); + case DAHDI_GET_BUFINFO: + memset(&stack.bi, 0, sizeof(stack.bi)); + stack.bi.rxbufpolicy = chan->rxbufpolicy; + stack.bi.txbufpolicy = chan->txbufpolicy; + stack.bi.numbufs = chan->numbufs; + stack.bi.bufsize = chan->blocksize; + /* XXX FIXME! XXX */ + stack.bi.readbufs = -1; + stack.bi.writebufs = -1; + if (copy_to_user((struct dahdi_bufferinfo *)data, &stack.bi, sizeof(stack.bi))) + return -EFAULT; + break; + case DAHDI_SET_BUFINFO: + if (copy_from_user(&stack.bi, (struct dahdi_bufferinfo *)data, sizeof(stack.bi))) + return -EFAULT; + if (stack.bi.bufsize > DAHDI_MAX_BLOCKSIZE) + return -EINVAL; + if (stack.bi.bufsize < 16) + return -EINVAL; + if (stack.bi.bufsize * stack.bi.numbufs > DAHDI_MAX_BUF_SPACE) + return -EINVAL; + /* It does not make sense to allow user mode to change the + * receive buffering policy. DAHDI always provides received + * buffers to upper layers immediately. Transmission is + * different since we might want to allow the kernel to build + * up a buffer in order to prevent underruns from the + * interrupt context. */ + chan->txbufpolicy = stack.bi.txbufpolicy & 0x3; + if ((rv = dahdi_reallocbufs(chan, stack.bi.bufsize, stack.bi.numbufs))) + return (rv); + break; + case DAHDI_GET_BLOCKSIZE: /* get blocksize */ + put_user(chan->blocksize,(int *)data); /* return block size */ + break; + case DAHDI_SET_BLOCKSIZE: /* set blocksize */ + get_user(j,(int *)data); + /* cannot be larger than max amount */ + if (j > DAHDI_MAX_BLOCKSIZE) return(-EINVAL); + /* cannot be less then 16 */ + if (j < 16) return(-EINVAL); + /* allocate a single kernel buffer which we then + sub divide into four pieces */ + if ((rv = dahdi_reallocbufs(chan, j, chan->numbufs))) + return (rv); + break; + case DAHDI_FLUSH: /* flush input buffer, output buffer, and/or event queue */ + get_user(i,(int *)data); /* get param */ + spin_lock_irqsave(&chan->lock, flags); + if (i & DAHDI_FLUSH_READ) /* if for read (input) */ + { + /* initialize read buffers and pointers */ + chan->inreadbuf = 0; + chan->outreadbuf = -1; + for (j=0;jnumbufs;j++) { + /* Do we need this? */ + chan->readn[j] = 0; + chan->readidx[j] = 0; + } + wake_up_interruptible(&chan->readbufq); /* wake_up_interruptible waiting on read */ + wake_up_interruptible(&chan->sel); /* wake_up_interruptible waiting on select */ + } + if (i & DAHDI_FLUSH_WRITE) /* if for write (output) */ + { + /* initialize write buffers and pointers */ + chan->outwritebuf = -1; + chan->inwritebuf = 0; + for (j=0;jnumbufs;j++) { + /* Do we need this? */ + chan->writen[j] = 0; + chan->writeidx[j] = 0; + } + wake_up_interruptible(&chan->writebufq); /* wake_up_interruptible waiting on write */ + wake_up_interruptible(&chan->sel); /* wake_up_interruptible waiting on select */ + /* if IO MUX wait on write empty, well, this + certainly *did* empty the write */ + if (chan->iomask & DAHDI_IOMUX_WRITEEMPTY) + wake_up_interruptible(&chan->eventbufq); /* wake_up_interruptible waiting on IOMUX */ + } + if (i & DAHDI_FLUSH_EVENT) /* if for events */ + { + /* initialize the event pointers */ + chan->eventinidx = chan->eventoutidx = 0; + } + spin_unlock_irqrestore(&chan->lock, flags); + break; + case DAHDI_SYNC: /* wait for no tx */ + for(;;) /* loop forever */ + { + spin_lock_irqsave(&chan->lock, flags); + /* Know if there is a write pending */ + i = (chan->outwritebuf > -1); + spin_unlock_irqrestore(&chan->lock, flags); + if (!i) break; /* skip if none */ + rv = schluffen(&chan->writebufq); + if (rv) return(rv); + } + break; + case DAHDI_IOMUX: /* wait for something to happen */ + get_user(chan->iomask,(int*)data); /* save mask */ + if (!chan->iomask) return(-EINVAL); /* cant wait for nothing */ + for(;;) /* loop forever */ + { + /* has to have SOME mask */ + ret = 0; /* start with empty return value */ + spin_lock_irqsave(&chan->lock, flags); + /* if looking for read */ + if (chan->iomask & DAHDI_IOMUX_READ) + { + /* if read available */ + if ((chan->outreadbuf > -1) && !chan->rxdisable) + ret |= DAHDI_IOMUX_READ; + } + /* if looking for write avail */ + if (chan->iomask & DAHDI_IOMUX_WRITE) + { + if (chan->inwritebuf > -1) + ret |= DAHDI_IOMUX_WRITE; + } + /* if looking for write empty */ + if (chan->iomask & DAHDI_IOMUX_WRITEEMPTY) + { + /* if everything empty -- be sure the transmitter is enabled */ + chan->txdisable = 0; + if (chan->outwritebuf < 0) + ret |= DAHDI_IOMUX_WRITEEMPTY; + } + /* if looking for signalling event */ + if (chan->iomask & DAHDI_IOMUX_SIGEVENT) + { + /* if event */ + if (chan->eventinidx != chan->eventoutidx) + ret |= DAHDI_IOMUX_SIGEVENT; + } + spin_unlock_irqrestore(&chan->lock, flags); + /* if something to return, or not to wait */ + if (ret || (chan->iomask & DAHDI_IOMUX_NOWAIT)) + { + /* set return value */ + put_user(ret,(int *)data); + break; /* get out of loop */ + } + rv = schluffen(&chan->eventbufq); + if (rv) return(rv); + } + /* clear IO MUX mask */ + chan->iomask = 0; + break; + case DAHDI_GETEVENT: /* Get event on queue */ + /* set up for no event */ + j = DAHDI_EVENT_NONE; + spin_lock_irqsave(&chan->lock, flags); + /* if some event in queue */ + if (chan->eventinidx != chan->eventoutidx) + { + j = chan->eventbuf[chan->eventoutidx++]; + /* get the data, bump index */ + /* if index overflow, set to beginning */ + if (chan->eventoutidx >= DAHDI_MAX_EVENTSIZE) + chan->eventoutidx = 0; + } + spin_unlock_irqrestore(&chan->lock, flags); + put_user(j,(int *)data); + break; + case DAHDI_CONFMUTE: /* set confmute flag */ + get_user(j,(int *)data); /* get conf # */ + if (!(chan->flags & DAHDI_FLAG_AUDIO)) return (-EINVAL); + spin_lock_irqsave(&bigzaplock, flags); + chan->confmute = j; + spin_unlock_irqrestore(&bigzaplock, flags); + break; + case DAHDI_GETCONFMUTE: /* get confmute flag */ + if (!(chan->flags & DAHDI_FLAG_AUDIO)) return (-EINVAL); + j = chan->confmute; + put_user(j,(int *)data); /* get conf # */ + rv = 0; + break; + case DAHDI_SETTONEZONE: + get_user(j, (int *) data); + rv = set_tone_zone(chan, j); + return rv; + case DAHDI_GETTONEZONE: + spin_lock_irqsave(&chan->lock, flags); + if (chan->curzone) + j = chan->tonezone; + spin_unlock_irqrestore(&chan->lock, flags); + put_user(j, (int *) data); + break; + case DAHDI_SENDTONE: + get_user(j,(int *)data); + spin_lock_irqsave(&chan->lock, flags); + rv = start_tone(chan, j); + spin_unlock_irqrestore(&chan->lock, flags); + return rv; + case DAHDI_GETCONF_V1: /* intentional drop through */ + case DAHDI_GETCONF: /* get conf stuff */ + if (copy_from_user(&stack.conf,(struct dahdi_confinfo *) data,sizeof(stack.conf))) + return -EFAULT; + i = stack.conf.chan; /* get channel no */ + /* if zero, use current channel no */ + if (!i) i = chan->channo; + /* make sure channel number makes sense */ + if ((i < 0) || (i > DAHDI_MAX_CONF) || (!chans[i])) return(-EINVAL); + if (!(chans[i]->flags & DAHDI_FLAG_AUDIO)) return (-EINVAL); + stack.conf.chan = i; /* get channel number */ + stack.conf.confno = chans[i]->confna; /* get conference number */ + stack.conf.confmode = chans[i]->confmode; /* get conference mode */ + if (copy_to_user((struct dahdi_confinfo *) data,&stack.conf,sizeof(stack.conf))) + return -EFAULT; + break; + case DAHDI_SETCONF_V1: /* Intentional fall through. */ + case DAHDI_SETCONF: /* set conf stuff */ + if (copy_from_user(&stack.conf,(struct dahdi_confinfo *) data,sizeof(stack.conf))) + return -EFAULT; + i = stack.conf.chan; /* get channel no */ + /* if zero, use current channel no */ + if (!i) i = chan->channo; + /* make sure channel number makes sense */ + if ((i < 1) || (i > DAHDI_MAX_CHANNELS) || (!chans[i])) return(-EINVAL); + if (!(chans[i]->flags & DAHDI_FLAG_AUDIO)) return (-EINVAL); + if ((stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITOR || + (stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITORTX || + (stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITORBOTH || + (stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITOR_RX_PREECHO || + (stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITOR_TX_PREECHO || + (stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITORBOTH_PREECHO) { + /* Monitor mode -- it's a channel */ + if ((stack.conf.confno < 0) || (stack.conf.confno >= DAHDI_MAX_CHANNELS) || !chans[stack.conf.confno]) return(-EINVAL); + } else { + /* make sure conf number makes sense, too */ + if ((stack.conf.confno < -1) || (stack.conf.confno > DAHDI_MAX_CONF)) return(-EINVAL); + } + + /* if taking off of any conf, must have 0 mode */ + if ((!stack.conf.confno) && stack.conf.confmode) return(-EINVAL); + /* likewise if 0 mode must have no conf */ + if ((!stack.conf.confmode) && stack.conf.confno) return (-EINVAL); + stack.conf.chan = i; /* return with real channel # */ + spin_lock_irqsave(&bigzaplock, flags); + spin_lock(&chan->lock); + if (stack.conf.confno == -1) + stack.conf.confno = dahdi_first_empty_conference(); + if ((stack.conf.confno < 1) && (stack.conf.confmode)) { + /* No more empty conferences */ + spin_unlock(&chan->lock); + spin_unlock_irqrestore(&bigzaplock, flags); + return -EBUSY; + } + /* if changing confs, clear last added info */ + if (stack.conf.confno != chans[i]->confna) { + memset(chans[i]->conflast, 0, DAHDI_MAX_CHUNKSIZE); + memset(chans[i]->conflast1, 0, DAHDI_MAX_CHUNKSIZE); + memset(chans[i]->conflast2, 0, DAHDI_MAX_CHUNKSIZE); + } + j = chans[i]->confna; /* save old conference number */ + chans[i]->confna = stack.conf.confno; /* set conference number */ + chans[i]->confmode = stack.conf.confmode; /* set conference mode */ + chans[i]->_confn = 0; /* Clear confn */ + dahdi_check_conf(j); + dahdi_check_conf(stack.conf.confno); + if (chans[i]->span && chans[i]->span->dacs) { + if (((stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_DIGITALMON) && + chans[stack.conf.confno]->span && + chans[stack.conf.confno]->span->dacs == chans[i]->span->dacs && + chans[i]->txgain == defgain && + chans[i]->rxgain == defgain && + chans[stack.conf.confno]->txgain == defgain && + chans[stack.conf.confno]->rxgain == defgain) { + chans[i]->span->dacs(chans[i], chans[stack.conf.confno]); + } else { + chans[i]->span->dacs(chans[i], NULL); + } + } + /* if we are going onto a conf */ + if (stack.conf.confno && + ((stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_CONF || + (stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_CONFANN || + (stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_CONFMON || + (stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_CONFANNMON || + (stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_REALANDPSEUDO)) { + /* Get alias */ + chans[i]->_confn = dahdi_get_conf_alias(stack.conf.confno); + } + + if (chans[stack.conf.confno]) { + if ((stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITOR_RX_PREECHO || + (stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITOR_TX_PREECHO || + (stack.conf.confmode & DAHDI_CONF_MODE_MASK) == DAHDI_CONF_MONITORBOTH_PREECHO) + chans[stack.conf.confno]->readchunkpreec = kmalloc(sizeof(*chans[stack.conf.confno]->readchunkpreec) * DAHDI_CHUNKSIZE, GFP_ATOMIC); + else { + if (chans[stack.conf.confno]->readchunkpreec) { + kfree(chans[stack.conf.confno]->readchunkpreec); + chans[stack.conf.confno]->readchunkpreec = NULL; + } + } + } + + spin_unlock(&chan->lock); + spin_unlock_irqrestore(&bigzaplock, flags); + if (copy_to_user((struct dahdi_confinfo *) data,&stack.conf,sizeof(stack.conf))) + return -EFAULT; + break; + case DAHDI_CONFLINK: /* do conf link stuff */ + if (!(chan->flags & DAHDI_FLAG_AUDIO)) return (-EINVAL); + if (copy_from_user(&stack.conf,(struct dahdi_confinfo *) data,sizeof(stack.conf))) + return -EFAULT; + /* check sanity of arguments */ + if ((stack.conf.chan < 0) || (stack.conf.chan > DAHDI_MAX_CONF)) return(-EINVAL); + if ((stack.conf.confno < 0) || (stack.conf.confno > DAHDI_MAX_CONF)) return(-EINVAL); + /* cant listen to self!! */ + if (stack.conf.chan && (stack.conf.chan == stack.conf.confno)) return(-EINVAL); + spin_lock_irqsave(&bigzaplock, flags); + spin_lock(&chan->lock); + /* if to clear all links */ + if ((!stack.conf.chan) && (!stack.conf.confno)) + { + /* clear all the links */ + memset(conf_links,0,sizeof(conf_links)); + recalc_maxlinks(); + spin_unlock(&chan->lock); + spin_unlock_irqrestore(&bigzaplock, flags); + break; + } + rv = 0; /* clear return value */ + /* look for already existant specified combination */ + for(i = 1; i <= DAHDI_MAX_CONF; i++) + { + /* if found, exit */ + if ((conf_links[i].src == stack.conf.chan) && + (conf_links[i].dst == stack.conf.confno)) break; + } + if (i <= DAHDI_MAX_CONF) /* if found */ + { + if (!stack.conf.confmode) /* if to remove link */ + { + conf_links[i].src = conf_links[i].dst = 0; + } + else /* if to add and already there, error */ + { + rv = -EEXIST; + } + } + else /* if not found */ + { + if (stack.conf.confmode) /* if to add link */ + { + /* look for empty location */ + for(i = 1; i <= DAHDI_MAX_CONF; i++) + { + /* if empty, exit loop */ + if ((!conf_links[i].src) && + (!conf_links[i].dst)) break; + } + /* if empty spot found */ + if (i <= DAHDI_MAX_CONF) + { + conf_links[i].src = stack.conf.chan; + conf_links[i].dst = stack.conf.confno; + } + else /* if no empties -- error */ + { + rv = -ENOSPC; + } + } + else /* if to remove, and not found -- error */ + { + rv = -ENOENT; + } + } + recalc_maxlinks(); + spin_unlock(&chan->lock); + spin_unlock_irqrestore(&bigzaplock, flags); + return(rv); + case DAHDI_CONFDIAG_V1: /* Intention fall-through */ + case DAHDI_CONFDIAG: /* output diagnostic info to console */ + if (!(chan->flags & DAHDI_FLAG_AUDIO)) return (-EINVAL); + get_user(j,(int *)data); /* get conf # */ + /* loop thru the interesting ones */ + for(i = ((j) ? j : 1); i <= ((j) ? j : DAHDI_MAX_CONF); i++) + { + c = 0; + for(k = 1; k < DAHDI_MAX_CHANNELS; k++) + { + /* skip if no pointer */ + if (!chans[k]) continue; + /* skip if not in this conf */ + if (chans[k]->confna != i) continue; + if (!c) module_printk(KERN_NOTICE, "Conf #%d:\n",i); + c = 1; + module_printk(KERN_NOTICE, "chan %d, mode %x\n", k,chans[k]->confmode); + } + rv = 0; + for(k = 1; k <= DAHDI_MAX_CONF; k++) + { + if (conf_links[k].dst == i) + { + if (!c) module_printk(KERN_NOTICE, "Conf #%d:\n",i); + c = 1; + if (!rv) module_printk(KERN_NOTICE, "Snooping on:\n"); + rv = 1; + module_printk(KERN_NOTICE, "conf %d\n",conf_links[k].src); + } + } + if (c) module_printk(KERN_NOTICE, "\n"); + } + break; + case DAHDI_CHANNO: /* get channel number of stream */ + put_user(unit,(int *)data); /* return unit/channel number */ + break; + case DAHDI_SETLAW: + get_user(j, (int *)data); + if ((j < 0) || (j > DAHDI_LAW_ALAW)) + return -EINVAL; + dahdi_set_law(chan, j); + break; + case DAHDI_SETLINEAR: + get_user(j, (int *)data); + /* Makes no sense on non-audio channels */ + if (!(chan->flags & DAHDI_FLAG_AUDIO)) + return -EINVAL; + + if (j) + chan->flags |= DAHDI_FLAG_LINEAR; + else + chan->flags &= ~DAHDI_FLAG_LINEAR; + break; + case DAHDI_SETCADENCE: + if (data) { + /* Use specific ring cadence */ + if (copy_from_user(&stack.cad, (struct dahdi_ring_cadence *)data, sizeof(stack.cad))) + return -EFAULT; + memcpy(chan->ringcadence, &stack.cad, sizeof(chan->ringcadence)); + chan->firstcadencepos = 0; + /* Looking for negative ringing time indicating where to loop back into ringcadence */ + for (i=0; iringcadence[i]<0) { + chan->ringcadence[i] *= -1; + chan->firstcadencepos = i; + break; + } + } + } else { + /* Reset to default */ + chan->firstcadencepos = 0; + if (chan->curzone) { + memcpy(chan->ringcadence, chan->curzone->ringcadence, sizeof(chan->ringcadence)); + /* Looking for negative ringing time indicating where to loop back into ringcadence */ + for (i=0; iringcadence[i]<0) { + chan->ringcadence[i] *= -1; + chan->firstcadencepos = i; + break; + } + } + } else { + memset(chan->ringcadence, 0, sizeof(chan->ringcadence)); + chan->ringcadence[0] = chan->starttime; + chan->ringcadence[1] = DAHDI_RINGOFFTIME; + } + } + break; + default: + /* Check for common ioctl's and private ones */ + rv = dahdi_common_ioctl(inode, file, cmd, data, unit); + /* if no span, just return with value */ + if (!chan->span) return rv; + if ((rv == -ENOTTY) && chan->span->ioctl) + rv = chan->span->ioctl(chan, cmd, data); + return rv; + + } + return 0; +} + +#ifdef CONFIG_DAHDI_PPP +/* + * This is called at softirq (BH) level when there are calls + * we need to make to the ppp_generic layer. We do it this + * way because the ppp_generic layer functions may not be called + * at interrupt level. + */ +static void do_ppp_calls(unsigned long data) +{ + struct dahdi_chan *chan = (struct dahdi_chan *) data; + struct sk_buff *skb; + + if (!chan->ppp) + return; + if (chan->do_ppp_wakeup) { + chan->do_ppp_wakeup = 0; + ppp_output_wakeup(chan->ppp); + } + while ((skb = skb_dequeue(&chan->ppp_rq)) != NULL) + ppp_input(chan->ppp, skb); + if (chan->do_ppp_error) { + chan->do_ppp_error = 0; + ppp_input_error(chan->ppp, 0); + } +} +#endif + +static int ioctl_echocancel(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, void *data) +{ + struct dahdi_echocan_state *ec = NULL, *ec_state; + const struct dahdi_echocan_factory *ec_current; + struct dahdi_echocanparam *params; + int ret; + unsigned long flags; + + if (ecp->param_count > DAHDI_MAX_ECHOCANPARAMS) + return -E2BIG; + + if (ecp->tap_length == 0) { + /* disable mode, don't need to inspect params */ + spin_lock_irqsave(&chan->lock, flags); + ec_state = chan->ec_state; + chan->ec_state = NULL; + ec_current = chan->ec_current; + chan->ec_current = NULL; + spin_unlock_irqrestore(&chan->lock, flags); + if (ec_state) { + ec_state->ops->echocan_free(chan, ec_state); + release_echocan(ec_current); + } + + return 0; + } + + params = kmalloc(sizeof(params[0]) * DAHDI_MAX_ECHOCANPARAMS, GFP_KERNEL); + + if (!params) + return -ENOMEM; + + /* enable mode, need the params */ + + if (copy_from_user(params, (struct dahdi_echocanparam *) data, sizeof(params[0]) * ecp->param_count)) { + ret = -EFAULT; + goto exit_with_free; + } + + /* free any echocan that may be on the channel already */ + spin_lock_irqsave(&chan->lock, flags); + ec_state = chan->ec_state; + chan->ec_state = NULL; + ec_current = chan->ec_current; + chan->ec_current = NULL; + spin_unlock_irqrestore(&chan->lock, flags); + if (ec_state) { + ec_state->ops->echocan_free(chan, ec_state); + release_echocan(ec_current); + } + + switch (ecp->tap_length) { + case 32: + case 64: + case 128: + case 256: + case 512: + case 1024: + break; + default: + ecp->tap_length = deftaps; + } + + ret = -ENODEV; + ec_current = NULL; + + /* attempt to use the span's echo canceler; fall back to built-in + if it fails (but not if an error occurs) */ + if (chan->span && chan->span->echocan_create) + ret = chan->span->echocan_create(chan, ecp, params, &ec); + + if ((ret == -ENODEV) && chan->ec_factory) { + /* try to get another reference to the module providing + this channel's echo canceler */ + if (!try_module_get(chan->ec_factory->owner)) { + module_printk(KERN_ERR, "Cannot get a reference to the '%s' echo canceler\n", chan->ec_factory->name); + goto exit_with_free; + } + + /* got the reference, copy the pointer and use it for making + an echo canceler instance if possible */ + ec_current = chan->ec_factory; + + ret = ec_current->echocan_create(chan, ecp, params, &ec); + if (ret) { + release_echocan(ec_current); + + goto exit_with_free; + } + if (!ec) { + module_printk(KERN_ERR, "%s failed to allocate an " \ + "dahdi_echocan_state instance.\n", + ec_current->name); + ret = -EFAULT; + goto exit_with_free; + } + } + + if (ec) { + spin_lock_irqsave(&chan->lock, flags); + chan->ec_current = ec_current; + chan->ec_state = ec; + ec->status.mode = ECHO_MODE_ACTIVE; + if (!ec->features.CED_tx_detect) { + echo_can_disable_detector_init(&chan->ec_state->txecdis); + } + if (!ec->features.CED_rx_detect) { + echo_can_disable_detector_init(&chan->ec_state->rxecdis); + } + spin_unlock_irqrestore(&chan->lock, flags); + } + +exit_with_free: + kfree(params); + + return ret; +} + +static void set_echocan_fax_mode(struct dahdi_chan *chan, unsigned int channo, const char *reason, unsigned int enable) +{ + if (enable) { + if (!chan->ec_state) + module_printk(KERN_NOTICE, "Ignoring FAX mode request because of %s for channel %d with no echo canceller\n", reason, channo); + else if (chan->ec_state->status.mode == ECHO_MODE_FAX) + module_printk(KERN_NOTICE, "Ignoring FAX mode request because of %s for echo canceller already in FAX mode on channel %d\n", reason, channo); + else if (chan->ec_state->status.mode != ECHO_MODE_ACTIVE) + module_printk(KERN_NOTICE, "Ignoring FAX mode request because of %s for echo canceller not in active mode on channel %d\n", reason, channo); + else if (chan->ec_state->features.NLP_automatic) { + /* for echocans that automatically do the right thing, just + * mark it as being in FAX mode without making any + * changes, as none are necessary. + */ + chan->ec_state->status.mode = ECHO_MODE_FAX; + } else if (chan->ec_state->features.NLP_toggle) { + module_printk(KERN_NOTICE, "Disabled echo canceller NLP because of %s on channel %d\n", reason, channo); + dahdi_qevent_nolock(chan, DAHDI_EVENT_EC_NLP_DISABLED); + chan->ec_state->ops->echocan_NLP_toggle(chan->ec_state, 0); + chan->ec_state->status.mode = ECHO_MODE_FAX; + } else { + module_printk(KERN_NOTICE, "Idled echo canceller because of %s on channel %d\n", reason, channo); + chan->ec_state->status.mode = ECHO_MODE_IDLE; + } + } else { + if (!chan->ec_state) + module_printk(KERN_NOTICE, "Ignoring voice mode request because of %s for channel %d with no echo canceller\n", reason, channo); + else if (chan->ec_state->status.mode == ECHO_MODE_ACTIVE) + module_printk(KERN_NOTICE, "Ignoring voice mode request because of %s for echo canceller already in voice mode on channel %d\n", reason, channo); + else if ((chan->ec_state->status.mode != ECHO_MODE_FAX) && + (chan->ec_state->status.mode != ECHO_MODE_IDLE)) + module_printk(KERN_NOTICE, "Ignoring voice mode request because of %s for echo canceller not in FAX or idle mode on channel %d\n", reason, channo); + else if (chan->ec_state->features.NLP_automatic) { + /* for echocans that automatically do the right thing, just + * mark it as being in active mode without making any + * changes, as none are necessary. + */ + chan->ec_state->status.mode = ECHO_MODE_ACTIVE; + } else if (chan->ec_state->features.NLP_toggle) { + module_printk(KERN_NOTICE, "Enabled echo canceller NLP because of %s on channel %d\n", reason, channo); + dahdi_qevent_nolock(chan, DAHDI_EVENT_EC_NLP_ENABLED); + chan->ec_state->ops->echocan_NLP_toggle(chan->ec_state, 1); + chan->ec_state->status.mode = ECHO_MODE_ACTIVE; + } else { + module_printk(KERN_NOTICE, "Activated echo canceller because of %s on channel %d\n", reason, channo); + chan->ec_state->status.mode = ECHO_MODE_ACTIVE; + } + } +} + +static int dahdi_chan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit) +{ + struct dahdi_chan *chan = chans[unit]; + unsigned long flags; + int j, rv; + int ret; + int oldconf; + void *rxgain=NULL; + + WARN_ON(!chan->master); + if (!chan) + return -ENOSYS; + + switch(cmd) { + case DAHDI_SETSIGFREEZE: + get_user(j, (int *)data); + spin_lock_irqsave(&chan->lock, flags); + if (j) { + chan->flags |= DAHDI_FLAG_SIGFREEZE; + } else { + chan->flags &= ~DAHDI_FLAG_SIGFREEZE; + } + spin_unlock_irqrestore(&chan->lock, flags); + break; + case DAHDI_GETSIGFREEZE: + spin_lock_irqsave(&chan->lock, flags); + if (chan->flags & DAHDI_FLAG_SIGFREEZE) + j = 1; + else + j = 0; + spin_unlock_irqrestore(&chan->lock, flags); + put_user(j, (int *)data); + break; + case DAHDI_AUDIOMODE: + /* Only literal clear channels can be put in */ + if (chan->sig != DAHDI_SIG_CLEAR) return (-EINVAL); + get_user(j, (int *)data); + if (j) { + spin_lock_irqsave(&chan->lock, flags); + chan->flags |= DAHDI_FLAG_AUDIO; + chan->flags &= ~(DAHDI_FLAG_HDLC | DAHDI_FLAG_FCS); + spin_unlock_irqrestore(&chan->lock, flags); + } else { + /* Coming out of audio mode, also clear all + conferencing and gain related info as well + as echo canceller */ + struct dahdi_echocan_state *ec_state; + const struct dahdi_echocan_factory *ec_current; + + spin_lock_irqsave(&chan->lock, flags); + chan->flags &= ~DAHDI_FLAG_AUDIO; + /* save old conf number, if any */ + oldconf = chan->confna; + /* initialize conference variables */ + chan->_confn = 0; + chan->confna = 0; + if (chan->span && chan->span->dacs) + chan->span->dacs(chan, NULL); + chan->confmode = 0; + chan->confmute = 0; + memset(chan->conflast, 0, sizeof(chan->conflast)); + memset(chan->conflast1, 0, sizeof(chan->conflast1)); + memset(chan->conflast2, 0, sizeof(chan->conflast2)); + ec_state = chan->ec_state; + chan->ec_state = NULL; + ec_current = chan->ec_current; + chan->ec_current = NULL; + /* release conference resource, if any to release */ + reset_conf(chan); + if (chan->gainalloc && chan->rxgain) + rxgain = chan->rxgain; + else + rxgain = NULL; + + chan->rxgain = defgain; + chan->txgain = defgain; + chan->gainalloc = 0; + spin_unlock_irqrestore(&chan->lock, flags); + + if (ec_state) { + ec_state->ops->echocan_free(chan, ec_state); + release_echocan(ec_current); + } + + if (rxgain) + kfree(rxgain); + if (oldconf) dahdi_check_conf(oldconf); + } + break; + case DAHDI_HDLCPPP: +#ifdef CONFIG_DAHDI_PPP + if (chan->sig != DAHDI_SIG_CLEAR) return (-EINVAL); + get_user(j, (int *)data); + if (j) { + if (!chan->ppp) { + chan->ppp = kzalloc(sizeof(struct ppp_channel), GFP_KERNEL); + if (chan->ppp) { + struct dahdi_echocan_state *tec; + const struct dahdi_echocan_factory *ec_current; + + chan->ppp->private = chan; + chan->ppp->ops = &ztppp_ops; + chan->ppp->mtu = DAHDI_DEFAULT_MTU_MRU; + chan->ppp->hdrlen = 0; + skb_queue_head_init(&chan->ppp_rq); + chan->do_ppp_wakeup = 0; + tasklet_init(&chan->ppp_calls, do_ppp_calls, + (unsigned long)chan); + if ((ret = dahdi_reallocbufs(chan, DAHDI_DEFAULT_MTU_MRU, DAHDI_DEFAULT_NUM_BUFS))) { + kfree(chan->ppp); + chan->ppp = NULL; + return ret; + } + + if ((ret = ppp_register_channel(chan->ppp))) { + kfree(chan->ppp); + chan->ppp = NULL; + return ret; + } + tec = chan->ec_state; + chan->ec_state = NULL; + ec_current = chan->ec_current; + chan->ec_current = NULL; + /* Make sure there's no gain */ + if (chan->gainalloc) + kfree(chan->rxgain); + chan->rxgain = defgain; + chan->txgain = defgain; + chan->gainalloc = 0; + chan->flags &= ~DAHDI_FLAG_AUDIO; + chan->flags |= (DAHDI_FLAG_PPP | DAHDI_FLAG_HDLC | DAHDI_FLAG_FCS); + + if (tec) { + tec->ops->echocan_free(chan, tec); + release_echocan(ec_current); + } + } else + return -ENOMEM; + } + } else { + chan->flags &= ~(DAHDI_FLAG_PPP | DAHDI_FLAG_HDLC | DAHDI_FLAG_FCS); + if (chan->ppp) { + struct ppp_channel *ppp = chan->ppp; + chan->ppp = NULL; + tasklet_kill(&chan->ppp_calls); + skb_queue_purge(&chan->ppp_rq); + ppp_unregister_channel(ppp); + kfree(ppp); + } + } +#else + module_printk(KERN_NOTICE, "PPP support not compiled in\n"); + return -ENOSYS; +#endif + break; + case DAHDI_HDLCRAWMODE: + if (chan->sig != DAHDI_SIG_CLEAR) return (-EINVAL); + get_user(j, (int *)data); + chan->flags &= ~(DAHDI_FLAG_AUDIO | DAHDI_FLAG_HDLC | DAHDI_FLAG_FCS); + if (j) { + chan->flags |= DAHDI_FLAG_HDLC; + fasthdlc_init(&chan->rxhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64); + fasthdlc_init(&chan->txhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64); + } + break; + case DAHDI_HDLCFCSMODE: + if (chan->sig != DAHDI_SIG_CLEAR) return (-EINVAL); + get_user(j, (int *)data); + chan->flags &= ~(DAHDI_FLAG_AUDIO | DAHDI_FLAG_HDLC | DAHDI_FLAG_FCS); + if (j) { + chan->flags |= DAHDI_FLAG_HDLC | DAHDI_FLAG_FCS; + fasthdlc_init(&chan->rxhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64); + fasthdlc_init(&chan->txhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64); + } + break; + case DAHDI_HDLC_RATE: + get_user(j, (int *) data); + if (j == 56) { + chan->flags |= DAHDI_FLAG_HDLC56; + } else { + chan->flags &= ~DAHDI_FLAG_HDLC56; + } + + fasthdlc_init(&chan->rxhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64); + fasthdlc_init(&chan->txhdlc, (chan->flags & DAHDI_FLAG_HDLC56) ? FASTHDLC_MODE_56 : FASTHDLC_MODE_64); + break; + case DAHDI_ECHOCANCEL_PARAMS: + { + struct dahdi_echocanparams ecp; + + if (!(chan->flags & DAHDI_FLAG_AUDIO)) + return -EINVAL; + if (copy_from_user(&ecp, (struct dahdi_echocanparams *) data, sizeof(ecp))) + return -EFAULT; + data += sizeof(ecp); + if ((ret = ioctl_echocancel(chan, &ecp, (void *) data))) + return ret; + break; + } + case DAHDI_ECHOCANCEL: + { + struct dahdi_echocanparams ecp; + + if (!(chan->flags & DAHDI_FLAG_AUDIO)) + return -EINVAL; + get_user(j, (int *) data); + ecp.tap_length = j; + ecp.param_count = 0; + if ((ret = ioctl_echocancel(chan, &ecp, NULL))) + return ret; + break; + } + case DAHDI_ECHOTRAIN: + get_user(j, (int *)data); /* get pre-training time from user */ + if ((j < 0) || (j >= DAHDI_MAX_PRETRAINING)) + return -EINVAL; + j <<= 3; + spin_lock_irqsave(&chan->lock, flags); + if (chan->ec_state) { + /* Start pretraining stage */ + if (chan->ec_state->ops->echocan_traintap) { + chan->ec_state->status.mode = ECHO_MODE_PRETRAINING; + chan->ec_state->status.pretrain_timer = j; + } + spin_unlock_irqrestore(&chan->lock, flags); + } else { + spin_unlock_irqrestore(&chan->lock, flags); + return -EINVAL; + } + break; + case DAHDI_ECHOCANCEL_FAX_MODE: + if (!chan->ec_state) { + return -EINVAL; + } else { + get_user(j, (int *) data); + spin_lock_irqsave(&chan->lock, flags); + set_echocan_fax_mode(chan, chan->channo, "ioctl", j ? 1 : 0); + spin_unlock_irqrestore(&chan->lock, flags); + } + break; + case DAHDI_SETTXBITS: + if (chan->sig != DAHDI_SIG_CAS) + return -EINVAL; + get_user(j,(int *)data); + dahdi_cas_setbits(chan, j); + break; + case DAHDI_GETRXBITS: + put_user(chan->rxsig, (int *)data); + break; + case DAHDI_LOOPBACK: + get_user(j, (int *)data); + spin_lock_irqsave(&chan->lock, flags); + if (j) + chan->flags |= DAHDI_FLAG_LOOPED; + else + chan->flags &= ~DAHDI_FLAG_LOOPED; + spin_unlock_irqrestore(&chan->lock, flags); + break; + case DAHDI_HOOK: + get_user(j,(int *)data); + if (chan->flags & DAHDI_FLAG_CLEAR) + return -EINVAL; + if (chan->sig == DAHDI_SIG_CAS) + return -EINVAL; + /* if no span, just do nothing */ + if (!chan->span) return(0); + spin_lock_irqsave(&chan->lock, flags); + /* if dialing, stop it */ + chan->curtone = NULL; + chan->dialing = 0; + chan->txdialbuf[0] = '\0'; + chan->tonep = 0; + chan->pdialcount = 0; + spin_unlock_irqrestore(&chan->lock, flags); + if (chan->span->flags & DAHDI_FLAG_RBS) { + switch (j) { + case DAHDI_ONHOOK: + spin_lock_irqsave(&chan->lock, flags); + dahdi_hangup(chan); + spin_unlock_irqrestore(&chan->lock, flags); + break; + case DAHDI_OFFHOOK: + spin_lock_irqsave(&chan->lock, flags); + if ((chan->txstate == DAHDI_TXSTATE_KEWL) || + (chan->txstate == DAHDI_TXSTATE_AFTERKEWL)) { + spin_unlock_irqrestore(&chan->lock, flags); + return -EBUSY; + } + dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_DEBOUNCE, chan->debouncetime); + spin_unlock_irqrestore(&chan->lock, flags); + break; + case DAHDI_RING: + case DAHDI_START: + spin_lock_irqsave(&chan->lock, flags); + if (!chan->curzone) { + spin_unlock_irqrestore(&chan->lock, flags); + module_printk(KERN_WARNING, "Cannot start tone until a tone zone is loaded.\n"); + return -ENODATA; + } + if (chan->txstate != DAHDI_TXSTATE_ONHOOK) { + spin_unlock_irqrestore(&chan->lock, flags); + return -EBUSY; + } + if (chan->sig & __DAHDI_SIG_FXO) { + ret = 0; + chan->cadencepos = 0; + ret = chan->ringcadence[0]; + dahdi_rbs_sethook(chan, DAHDI_TXSIG_START, DAHDI_TXSTATE_RINGON, ret); + } else + dahdi_rbs_sethook(chan, DAHDI_TXSIG_START, DAHDI_TXSTATE_START, chan->starttime); + spin_unlock_irqrestore(&chan->lock, flags); + if (file->f_flags & O_NONBLOCK) + return -EINPROGRESS; +#if 0 + rv = schluffen(&chan->txstateq); + if (rv) return rv; +#endif + break; + case DAHDI_WINK: + spin_lock_irqsave(&chan->lock, flags); + if (chan->txstate != DAHDI_TXSTATE_ONHOOK) { + spin_unlock_irqrestore(&chan->lock, flags); + return -EBUSY; + } + dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_PREWINK, chan->prewinktime); + spin_unlock_irqrestore(&chan->lock, flags); + if (file->f_flags & O_NONBLOCK) + return -EINPROGRESS; + rv = schluffen(&chan->txstateq); + if (rv) return rv; + break; + case DAHDI_FLASH: + if(chan->ignoreflash) break; /* (CNET) Ignore remote hookflash */ + spin_lock_irqsave(&chan->lock, flags); + if (chan->txstate != DAHDI_TXSTATE_OFFHOOK) { + spin_unlock_irqrestore(&chan->lock, flags); + return -EBUSY; + } + dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_PREFLASH, chan->preflashtime); + spin_unlock_irqrestore(&chan->lock, flags); + if (file->f_flags & O_NONBLOCK) + return -EINPROGRESS; + rv = schluffen(&chan->txstateq); + if (rv) return rv; + break; + case DAHDI_RINGOFF: + spin_lock_irqsave(&chan->lock, flags); + dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_ONHOOK, 0); + spin_unlock_irqrestore(&chan->lock, flags); + break; + default: + return -EINVAL; + } + } else if (chan->span->sethook) { + if (chan->txhooksig != j) { + chan->txhooksig = j; + chan->span->sethook(chan, j); + } + } else + return -ENOSYS; + break; +#ifdef CONFIG_DAHDI_PPP + case PPPIOCGCHAN: + if (chan->flags & DAHDI_FLAG_PPP) + return put_user(ppp_channel_index(chan->ppp), (int *)data) ? -EFAULT : 0; + else + return -EINVAL; + break; + case PPPIOCGUNIT: + if (chan->flags & DAHDI_FLAG_PPP) + return put_user(ppp_unit_number(chan->ppp), (int *)data) ? -EFAULT : 0; + else + return -EINVAL; + break; +#endif + default: + return dahdi_chanandpseudo_ioctl(inode, file, cmd, data, unit); + } + return 0; +} + +static int dahdi_prechan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit) +{ + struct dahdi_chan *chan = file->private_data; + int channo; + int res; + + if (chan) { + module_printk(KERN_NOTICE, "Huh? Prechan already has private data??\n"); + } + switch(cmd) { + case DAHDI_SPECIFY: + get_user(channo,(int *)data); + if (channo < 1) + return -EINVAL; + if (channo > DAHDI_MAX_CHANNELS) + return -EINVAL; + res = dahdi_specchan_open(inode, file, channo); + if (!res) { + /* Setup the pointer for future stuff */ + chan = chans[channo]; + file->private_data = chan; + /* Return success */ + return 0; + } + return res; + default: + return -ENOSYS; + } + return 0; +} + +static int dahdi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data) +{ + int unit = UNIT(file); + struct dahdi_chan *chan; + struct dahdi_timer *timer; + + if (!unit) + return dahdi_ctl_ioctl(inode, file, cmd, data); + + if (unit == 250) { + /* dahdi_transcode should have updated the file_operations on + * this file object on open, so we shouldn't be here. */ + WARN_ON(1); + return -EFAULT; + } + + if (unit == 253) { + timer = file->private_data; + if (timer) + return dahdi_timer_ioctl(inode, file, cmd, data, timer); + else + return -EINVAL; + } + if (unit == 254) { + chan = file->private_data; + if (chan) + return dahdi_chan_ioctl(inode, file, cmd, data, chan->channo); + else + return dahdi_prechan_ioctl(inode, file, cmd, data, unit); + } + if (unit == 255) { + chan = file->private_data; + if (!chan) { + module_printk(KERN_NOTICE, "No pseudo channel structure to read?\n"); + return -EINVAL; + } + return dahdi_chanandpseudo_ioctl(inode, file, cmd, data, chan->channo); + } + return dahdi_chan_ioctl(inode, file, cmd, data, unit); +} + +int dahdi_register(struct dahdi_span *span, int prefmaster) +{ + int x; + + if (!span) + return -EINVAL; + + if (test_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags)) { + module_printk(KERN_ERR, "Span %s already appears to be registered\n", span->name); + return -EBUSY; + } + + for (x = 1; x < maxspans; x++) { + if (spans[x] == span) { + module_printk(KERN_ERR, "Span %s already in list\n", span->name); + return -EBUSY; + } + } + + for (x = 1; x < DAHDI_MAX_SPANS; x++) { + if (!spans[x]) + break; + } + + if (x < DAHDI_MAX_SPANS) { + spans[x] = span; + if (maxspans < x + 1) + maxspans = x + 1; + } else { + module_printk(KERN_ERR, "Too many DAHDI spans registered\n"); + return -EBUSY; + } + + set_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags); + span->spanno = x; + + spin_lock_init(&span->lock); + + if (!span->deflaw) { + module_printk(KERN_NOTICE, "Span %s didn't specify default law. " + "Assuming mulaw, please fix driver!\n", span->name); + span->deflaw = DAHDI_LAW_MULAW; + } + + for (x = 0; x < span->channels; x++) { + span->chans[x]->span = span; + dahdi_chan_reg(span->chans[x]); + } + +#ifdef CONFIG_PROC_FS + { + char tempfile[17]; + snprintf(tempfile, sizeof(tempfile), "dahdi/%d", span->spanno); + proc_entries[span->spanno] = create_proc_read_entry(tempfile, 0444, + NULL, dahdi_proc_read, (int *) (long) span->spanno); + } +#endif + + for (x = 0; x < span->channels; x++) { + if (span->chans[x]->channo < 250) { + char chan_name[32]; + snprintf(chan_name, sizeof(chan_name), "dahdi!%d", + span->chans[x]->channo); + CLASS_DEV_CREATE(dahdi_class, MKDEV(DAHDI_MAJOR, + span->chans[x]->channo), NULL, chan_name); + } + } + + if (debug) { + module_printk(KERN_NOTICE, "Registered Span %d ('%s') with " + "%d channels\n", span->spanno, span->name, span->channels); + } + + if (!master || prefmaster) { + master = span; + if (debug) { + module_printk(KERN_NOTICE, "Span ('%s') is new master\n", + span->name); + } + } + + return 0; +} + +int dahdi_unregister(struct dahdi_span *span) +{ + int x; + int new_maxspans; + static struct dahdi_span *new_master; + +#ifdef CONFIG_PROC_FS + char tempfile[17]; +#endif /* CONFIG_PROC_FS */ + + if (!test_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags)) { + module_printk(KERN_ERR, "Span %s does not appear to be registered\n", span->name); + return -1; + } + /* Shutdown the span if it's running */ + if (span->flags & DAHDI_FLAG_RUNNING) + if (span->shutdown) + span->shutdown(span); + + if (spans[span->spanno] != span) { + module_printk(KERN_ERR, "Span %s has spanno %d which is something else\n", span->name, span->spanno); + return -1; + } + if (debug) + module_printk(KERN_NOTICE, "Unregistering Span '%s' with %d channels\n", span->name, span->channels); +#ifdef CONFIG_PROC_FS + snprintf(tempfile, sizeof(tempfile)-1, "dahdi/%d", span->spanno); + remove_proc_entry(tempfile, NULL); +#endif /* CONFIG_PROC_FS */ + + for (x = 0; x < span->channels; x++) { + if (span->chans[x]->channo < 250) + CLASS_DEV_DESTROY(dahdi_class, MKDEV(DAHDI_MAJOR, span->chans[x]->channo)); + } + + spans[span->spanno] = NULL; + span->spanno = 0; + clear_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags); + for (x=0;xchannels;x++) + dahdi_chan_unreg(span->chans[x]); + new_maxspans = 0; + new_master = master; /* FIXME: locking */ + if (master == span) + new_master = NULL; + for (x=1;xname: "no master"); + master = new_master; + + return 0; +} + +/* +** This routine converts from linear to ulaw +** +** Craig Reese: IDA/Supercomputing Research Center +** Joe Campbell: Department of Defense +** 29 September 1989 +** +** References: +** 1) CCITT Recommendation G.711 (very difficult to follow) +** 2) "A New Digital Technique for Implementation of Any +** Continuous PCM Companding Law," Villeret, Michel, +** et al. 1973 IEEE Int. Conf. on Communications, Vol 1, +** 1973, pg. 11.12-11.17 +** 3) MIL-STD-188-113,"Interoperability and Performance Standards +** for Analog-to_Digital Conversion Techniques," +** 17 February 1987 +** +** Input: Signed 16 bit linear sample +** Output: 8 bit ulaw sample +*/ + +#define ZEROTRAP /* turn on the trap as per the MIL-STD */ +#define BIAS 0x84 /* define the add-in bias for 16 bit samples */ +#define CLIP 32635 + +#ifdef CONFIG_CALC_XLAW +unsigned char +#else +static unsigned char __init +#endif +__dahdi_lineartoulaw(short sample) +{ + static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7}; + int sign, exponent, mantissa; + unsigned char ulawbyte; + + /* Get the sample into sign-magnitude. */ + sign = (sample >> 8) & 0x80; /* set aside the sign */ + if (sign != 0) sample = -sample; /* get magnitude */ + if (sample > CLIP) sample = CLIP; /* clip the magnitude */ + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + BIAS; + exponent = exp_lut[(sample >> 7) & 0xFF]; + mantissa = (sample >> (exponent + 3)) & 0x0F; + ulawbyte = ~(sign | (exponent << 4) | mantissa); +#ifdef ZEROTRAP + if (ulawbyte == 0) ulawbyte = 0x02; /* optional CCITT trap */ +#endif + if (ulawbyte == 0xff) ulawbyte = 0x7f; /* never return 0xff */ + return(ulawbyte); +} + +#define AMI_MASK 0x55 + +#ifdef CONFIG_CALC_XLAW +unsigned char +#else +static inline unsigned char __init +#endif +__dahdi_lineartoalaw (short linear) +{ + int mask; + int seg; + int pcm_val; + static int seg_end[8] = + { + 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF + }; + + pcm_val = linear; + if (pcm_val >= 0) + { + /* Sign (7th) bit = 1 */ + mask = AMI_MASK | 0x80; + } + else + { + /* Sign bit = 0 */ + mask = AMI_MASK; + pcm_val = -pcm_val; + } + + /* Convert the scaled magnitude to segment number. */ + for (seg = 0; seg < 8; seg++) + { + if (pcm_val <= seg_end[seg]) + break; + } + /* Combine the sign, segment, and quantization bits. */ + return ((seg << 4) | ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask; +} +/*- End of function --------------------------------------------------------*/ + +static inline short int __init alaw2linear (uint8_t alaw) +{ + int i; + int seg; + + alaw ^= AMI_MASK; + i = ((alaw & 0x0F) << 4); + seg = (((int) alaw & 0x70) >> 4); + if (seg) + i = (i + 0x100) << (seg - 1); + return (short int) ((alaw & 0x80) ? i : -i); +} +/*- End of function --------------------------------------------------------*/ +static void __init dahdi_conv_init(void) +{ + int i; + + /* + * Set up mu-law conversion table + */ + for(i = 0;i < 256;i++) + { + short mu,e,f,y; + static short etab[]={0,132,396,924,1980,4092,8316,16764}; + + mu = 255-i; + e = (mu & 0x70)/16; + f = mu & 0x0f; + y = f * (1 << (e + 3)); + y += etab[e]; + if (mu & 0x80) y = -y; + __dahdi_mulaw[i] = y; + __dahdi_alaw[i] = alaw2linear(i); + /* Default (0.0 db) gain table */ + defgain[i] = i; + } +#ifndef CONFIG_CALC_XLAW + /* set up the reverse (mu-law) conversion table */ + for(i = -32768; i < 32768; i += 4) + { + __dahdi_lin2mu[((unsigned short)(short)i) >> 2] = __dahdi_lineartoulaw(i); + __dahdi_lin2a[((unsigned short)(short)i) >> 2] = __dahdi_lineartoalaw(i); + } +#endif +} + +static inline void __dahdi_process_getaudio_chunk(struct dahdi_chan *ss, unsigned char *txb) +{ + /* We transmit data from our master channel */ + /* Called with ss->lock held */ + struct dahdi_chan *ms = ss->master; + /* Linear representation */ + short getlin[DAHDI_CHUNKSIZE], k[DAHDI_CHUNKSIZE]; + int x; + + /* Okay, now we've got something to transmit */ + for (x=0;xec_state && (ms->ec_state->status.mode == ECHO_MODE_ACTIVE) && !ms->ec_state->features.CED_tx_detect) { + for (x = 0; x < DAHDI_CHUNKSIZE; x++) { + if (echo_can_disable_detector_update(&ms->ec_state->txecdis, getlin[x])) { + set_echocan_fax_mode(ms, ss->channo, "CED tx detected", 1); + dahdi_qevent_nolock(ms, DAHDI_EVENT_TX_CED_DETECTED); + break; + } + } + } + + if ((!ms->confmute && !ms->dialing) || (ms->flags & DAHDI_FLAG_PSEUDO)) { + /* Handle conferencing on non-clear channel and non-HDLC channels */ + switch(ms->confmode & DAHDI_CONF_MODE_MASK) { + case DAHDI_CONF_NORMAL: + /* Do nuffin */ + break; + case DAHDI_CONF_MONITOR: /* Monitor a channel's rx mode */ + /* if a pseudo-channel, ignore */ + if (ms->flags & DAHDI_FLAG_PSEUDO) break; + /* Add monitored channel */ + if (chans[ms->confna]->flags & DAHDI_FLAG_PSEUDO) { + ACSS(getlin, chans[ms->confna]->getlin); + } else { + ACSS(getlin, chans[ms->confna]->putlin); + } + for (x=0;xflags & DAHDI_FLAG_PSEUDO) break; + /* Add monitored channel */ + if (chans[ms->confna]->flags & DAHDI_FLAG_PSEUDO) { + ACSS(getlin, chans[ms->confna]->putlin); + } else { + ACSS(getlin, chans[ms->confna]->getlin); + } + + for (x=0;xflags & DAHDI_FLAG_PSEUDO) break; + ACSS(getlin, chans[ms->confna]->putlin); + ACSS(getlin, chans[ms->confna]->getlin); + for (x=0;xflags & DAHDI_FLAG_PSEUDO) + break; + + if (!chans[ms->confna]->readchunkpreec) + break; + + /* Add monitored channel */ + ACSS(getlin, chans[ms->confna]->flags & DAHDI_FLAG_PSEUDO ? + chans[ms->confna]->readchunkpreec : chans[ms->confna]->putlin); + for (x = 0; x < DAHDI_CHUNKSIZE; x++) + txb[x] = DAHDI_LIN2X(getlin[x], ms); + + break; + case DAHDI_CONF_MONITOR_TX_PREECHO: /* Monitor a channel's tx mode */ + /* if a pseudo-channel, ignore */ + if (ms->flags & DAHDI_FLAG_PSEUDO) + break; + + if (!chans[ms->confna]->readchunkpreec) + break; + + /* Add monitored channel */ + ACSS(getlin, chans[ms->confna]->flags & DAHDI_FLAG_PSEUDO ? + chans[ms->confna]->putlin : chans[ms->confna]->readchunkpreec); + for (x = 0; x < DAHDI_CHUNKSIZE; x++) + txb[x] = DAHDI_LIN2X(getlin[x], ms); + + break; + case DAHDI_CONF_MONITORBOTH_PREECHO: /* monitor a channel's rx and tx mode */ + /* if a pseudo-channel, ignore */ + if (ms->flags & DAHDI_FLAG_PSEUDO) + break; + + if (!chans[ms->confna]->readchunkpreec) + break; + + ACSS(getlin, chans[ms->confna]->putlin); + ACSS(getlin, chans[ms->confna]->readchunkpreec); + + for (x = 0; x < DAHDI_CHUNKSIZE; x++) + txb[x] = DAHDI_LIN2X(getlin[x], ms); + + break; + case DAHDI_CONF_REALANDPSEUDO: + /* This strange mode takes the transmit buffer and + puts it on the conference, minus its last sample, + then outputs from the conference minus the + real channel's last sample. */ + /* if to talk on conf */ + if (ms->confmode & DAHDI_CONF_PSEUDO_TALKER) { + /* Store temp value */ + memcpy(k, getlin, DAHDI_CHUNKSIZE * sizeof(short)); + /* Add conf value */ + ACSS(k, conf_sums_next[ms->_confn]); + /* save last one */ + memcpy(ms->conflast2, ms->conflast1, DAHDI_CHUNKSIZE * sizeof(short)); + memcpy(ms->conflast1, k, DAHDI_CHUNKSIZE * sizeof(short)); + /* get amount actually added */ + SCSS(ms->conflast1, conf_sums_next[ms->_confn]); + /* Really add in new value */ + ACSS(conf_sums_next[ms->_confn], ms->conflast1); + } else { + memset(ms->conflast1, 0, DAHDI_CHUNKSIZE * sizeof(short)); + memset(ms->conflast2, 0, DAHDI_CHUNKSIZE * sizeof(short)); + } + memset(getlin, 0, DAHDI_CHUNKSIZE * sizeof(short)); + txb[0] = DAHDI_LIN2X(0, ms); + memset(txb + 1, txb[0], DAHDI_CHUNKSIZE - 1); + /* fall through to normal conf mode */ + case DAHDI_CONF_CONF: /* Normal conference mode */ + if (ms->flags & DAHDI_FLAG_PSEUDO) /* if pseudo-channel */ + { + /* if to talk on conf */ + if (ms->confmode & DAHDI_CONF_TALKER) { + /* Store temp value */ + memcpy(k, getlin, DAHDI_CHUNKSIZE * sizeof(short)); + /* Add conf value */ + ACSS(k, conf_sums[ms->_confn]); + /* get amount actually added */ + memcpy(ms->conflast, k, DAHDI_CHUNKSIZE * sizeof(short)); + SCSS(ms->conflast, conf_sums[ms->_confn]); + /* Really add in new value */ + ACSS(conf_sums[ms->_confn], ms->conflast); + memcpy(ms->getlin, getlin, DAHDI_CHUNKSIZE * sizeof(short)); + } else { + memset(ms->conflast, 0, DAHDI_CHUNKSIZE * sizeof(short)); + memcpy(getlin, ms->getlin, DAHDI_CHUNKSIZE * sizeof(short)); + } + txb[0] = DAHDI_LIN2X(0, ms); + memset(txb + 1, txb[0], DAHDI_CHUNKSIZE - 1); + break; + } + /* fall through */ + case DAHDI_CONF_CONFMON: /* Conference monitor mode */ + if (ms->confmode & DAHDI_CONF_LISTENER) { + /* Subtract out last sample written to conf */ + SCSS(getlin, ms->conflast); + /* Add in conference */ + ACSS(getlin, conf_sums[ms->_confn]); + } + for (x=0;x_confn], getlin); + /* Start with silence */ + memset(getlin, 0, DAHDI_CHUNKSIZE * sizeof(short)); + /* If a listener on the conf... */ + if (ms->confmode & DAHDI_CONF_LISTENER) { + /* Subtract last value written */ + SCSS(getlin, ms->conflast); + /* Add in conf */ + ACSS(getlin, conf_sums[ms->_confn]); + } + for (x=0;xconfna]) + break; + if (chans[ms->confna]->flags & DAHDI_FLAG_PSEUDO) { + if (ms->ec_state) { + for (x=0;xconfna]->getlin[x], ms); + } else { + memcpy(txb, chans[ms->confna]->getraw, DAHDI_CHUNKSIZE); + } + } else { + if (ms->ec_state) { + for (x=0;xconfna]->putlin[x], ms); + } else { + memcpy(txb, chans[ms->confna]->putraw, DAHDI_CHUNKSIZE); + } + } + for (x=0;xconfmute || (ms->ec_state && (ms->ec_state->status.mode) & __ECHO_MODE_MUTE)) { + txb[0] = DAHDI_LIN2X(0, ms); + memset(txb + 1, txb[0], DAHDI_CHUNKSIZE - 1); + if (ms->ec_state && (ms->ec_state->status.mode == ECHO_MODE_STARTTRAINING)) { + /* Transmit impulse now */ + txb[0] = DAHDI_LIN2X(16384, ms); + ms->ec_state->status.mode = ECHO_MODE_AWAITINGECHO; + } + } + /* save value from last chunk */ + memcpy(ms->getlin_lastchunk, ms->getlin, DAHDI_CHUNKSIZE * sizeof(short)); + /* save value from current */ + memcpy(ms->getlin, getlin, DAHDI_CHUNKSIZE * sizeof(short)); + /* save value from current */ + memcpy(ms->getraw, txb, DAHDI_CHUNKSIZE); + /* if to make tx tone */ + if (ms->v1_1 || ms->v2_1 || ms->v3_1) + { + for (x=0;xtxgain[txb[x]]; +} + +static inline void __dahdi_getbuf_chunk(struct dahdi_chan *ss, unsigned char *txb) +{ + /* Called with ss->lock held */ + /* We transmit data from our master channel */ + struct dahdi_chan *ms = ss->master; + /* Buffer we're using */ + unsigned char *buf; + /* Old buffer number */ + int oldbuf; + /* Linear representation */ + int getlin; + /* How many bytes we need to process */ + int bytes = DAHDI_CHUNKSIZE, left; + int x; + + /* Let's pick something to transmit. First source to + try is our write-out buffer. Always check it first because + its our 'fast path' for whatever that's worth. */ + while(bytes) { + if ((ms->outwritebuf > -1) && !ms->txdisable) { + buf= ms->writebuf[ms->outwritebuf]; + left = ms->writen[ms->outwritebuf] - ms->writeidx[ms->outwritebuf]; + if (left > bytes) + left = bytes; + if (ms->flags & DAHDI_FLAG_HDLC) { + /* If this is an HDLC channel we only send a byte of + HDLC. */ + for(x=0;xtxhdlc)) + /* Load a byte of data only if needed */ + fasthdlc_tx_load_nocheck(&ms->txhdlc, buf[ms->writeidx[ms->outwritebuf]++]); + *(txb++) = fasthdlc_tx_run_nocheck(&ms->txhdlc); + } + bytes -= left; + } else { + memcpy(txb, buf + ms->writeidx[ms->outwritebuf], left); + ms->writeidx[ms->outwritebuf]+=left; + txb += left; + bytes -= left; + } + /* Check buffer status */ + if (ms->writeidx[ms->outwritebuf] >= ms->writen[ms->outwritebuf]) { + /* We've reached the end of our buffer. Go to the next. */ + oldbuf = ms->outwritebuf; + /* Clear out write index and such */ + ms->writeidx[oldbuf] = 0; + ms->outwritebuf = (ms->outwritebuf + 1) % ms->numbufs; + + if (!(ms->flags & DAHDI_FLAG_MTP2)) { + ms->writen[oldbuf] = 0; + if (ms->outwritebuf == ms->inwritebuf) { + /* Whoopsies, we're run out of buffers. Mark ours + as -1 and wait for the filler to notify us that + there is something to write */ + ms->outwritebuf = -1; + if (ms->iomask & (DAHDI_IOMUX_WRITE | DAHDI_IOMUX_WRITEEMPTY)) + wake_up_interruptible(&ms->eventbufq); + /* If we're only supposed to start when full, disable the transmitter */ + if ((ms->txbufpolicy == DAHDI_POLICY_WHEN_FULL) || + (ms->txbufpolicy == DAHDI_POLICY_HALF_FULL)) + ms->txdisable = 1; + } + } else { + if (ms->outwritebuf == ms->inwritebuf) { + ms->outwritebuf = oldbuf; + if (ms->iomask & (DAHDI_IOMUX_WRITE | DAHDI_IOMUX_WRITEEMPTY)) + wake_up_interruptible(&ms->eventbufq); + /* If we're only supposed to start when full, disable the transmitter */ + if ((ms->txbufpolicy == DAHDI_POLICY_WHEN_FULL) || + (ms->txbufpolicy == DAHDI_POLICY_HALF_FULL)) + ms->txdisable = 1; + } + } + if (ms->inwritebuf < 0) { + /* The filler doesn't have a place to put data. Now + that we're done with this buffer, notify them. */ + ms->inwritebuf = oldbuf; + } +/* In the very orignal driver, it was quite well known to me (Jim) that there +was a possibility that a channel sleeping on a write block needed to +be potentially woken up EVERY time a buffer was emptied, not just on the first +one, because if only done on the first one there is a slight timing potential +of missing the wakeup (between where it senses the (lack of) active condition +(with interrupts disabled) and where it does the sleep (interrupts enabled) +in the read or iomux call, etc). That is why the write and iomux calls start +with an infinite loop that gets broken out of upon an active condition, +otherwise keeps sleeping and looking. The part in this code got "optimized" +out in the later versions, and is put back now. */ + if (!(ms->flags & (DAHDI_FLAG_NETDEV | DAHDI_FLAG_PPP))) { + wake_up_interruptible(&ms->writebufq); + wake_up_interruptible(&ms->sel); + if (ms->iomask & DAHDI_IOMUX_WRITE) + wake_up_interruptible(&ms->eventbufq); + } + /* Transmit a flag if this is an HDLC channel */ + if (ms->flags & DAHDI_FLAG_HDLC) + fasthdlc_tx_frame_nocheck(&ms->txhdlc); +#ifdef CONFIG_DAHDI_NET + if (ms->flags & DAHDI_FLAG_NETDEV) + netif_wake_queue(ztchan_to_dev(ms)); +#endif +#ifdef CONFIG_DAHDI_PPP + if (ms->flags & DAHDI_FLAG_PPP) { + ms->do_ppp_wakeup = 1; + tasklet_schedule(&ms->ppp_calls); + } +#endif + } + } else if (ms->curtone && !(ms->flags & DAHDI_FLAG_PSEUDO)) { + left = ms->curtone->tonesamples - ms->tonep; + if (left > bytes) + left = bytes; + for (x=0;xts, ms->curtone); + *(txb++) = DAHDI_LIN2X(getlin, ms); + } + ms->tonep+=left; + bytes -= left; + if (ms->tonep >= ms->curtone->tonesamples) { + struct dahdi_tone *last; + /* Go to the next sample of the tone */ + ms->tonep = 0; + last = ms->curtone; + ms->curtone = ms->curtone->next; + if (!ms->curtone) { + /* No more tones... Is this dtmf or mf? If so, go to the next digit */ + if (ms->dialing) + __do_dtmf(ms); + } else { + if (last != ms->curtone) + dahdi_init_tone_state(&ms->ts, ms->curtone); + } + } + } else if (ms->flags & DAHDI_FLAG_LOOPED) { + for (x = 0; x < bytes; x++) + txb[x] = ms->readchunk[x]; + bytes = 0; + } else if (ms->flags & DAHDI_FLAG_HDLC) { + for (x=0;xtxhdlc)) + fasthdlc_tx_frame_nocheck(&ms->txhdlc); + *(txb++) = fasthdlc_tx_run_nocheck(&ms->txhdlc); + } + bytes = 0; + } else if (ms->flags & DAHDI_FLAG_CLEAR) { + /* Clear channels that are idle in audio mode need + to send silence; in non-audio mode, always send 0xff + so stupid switches won't consider the channel active + */ + if (ms->flags & DAHDI_FLAG_AUDIO) { + memset(txb, DAHDI_LIN2X(0, ms), bytes); + } else { + memset(txb, 0xFF, bytes); + } + bytes = 0; + } else { + memset(txb, DAHDI_LIN2X(0, ms), bytes); /* Lastly we use silence on telephony channels */ + bytes = 0; + } + } +} + +static inline void rbs_itimer_expire(struct dahdi_chan *chan) +{ + /* the only way this could have gotten here, is if a channel + went onf hook longer then the wink or flash detect timeout */ + /* Called with chan->lock held */ + switch(chan->sig) + { + case DAHDI_SIG_FXOLS: /* if FXO, its definitely on hook */ + case DAHDI_SIG_FXOGS: + case DAHDI_SIG_FXOKS: + __qevent(chan,DAHDI_EVENT_ONHOOK); + chan->gotgs = 0; + break; +#if defined(EMFLASH) || defined(EMPULSE) + case DAHDI_SIG_EM: + case DAHDI_SIG_EM_E1: + if (chan->rxhooksig == DAHDI_RXSIG_ONHOOK) { + __qevent(chan,DAHDI_EVENT_ONHOOK); + break; + } + __qevent(chan,DAHDI_EVENT_RINGOFFHOOK); + break; +#endif +#ifdef FXSFLASH + case DAHDI_SIG_FXSKS: + if (chan->rxhooksig == DAHDI_RXSIG_ONHOOK) { + __qevent(chan, DAHDI_EVENT_ONHOOK); + break; + } +#endif + /* fall thru intentionally */ + default: /* otherwise, its definitely off hook */ + __qevent(chan,DAHDI_EVENT_RINGOFFHOOK); + break; + } +} + +static inline void __rbs_otimer_expire(struct dahdi_chan *chan) +{ + int len = 0; + /* Called with chan->lock held */ + + chan->otimer = 0; + /* Move to the next timer state */ + switch(chan->txstate) { + case DAHDI_TXSTATE_RINGOFF: + /* Turn on the ringer now that the silent time has passed */ + ++chan->cadencepos; + if (chan->cadencepos >= DAHDI_MAX_CADENCE) + chan->cadencepos = chan->firstcadencepos; + len = chan->ringcadence[chan->cadencepos]; + + if (!len) { + chan->cadencepos = chan->firstcadencepos; + len = chan->ringcadence[chan->cadencepos]; + } + + dahdi_rbs_sethook(chan, DAHDI_TXSIG_START, DAHDI_TXSTATE_RINGON, len); + __qevent(chan, DAHDI_EVENT_RINGERON); + break; + + case DAHDI_TXSTATE_RINGON: + /* Turn off the ringer now that the loud time has passed */ + ++chan->cadencepos; + if (chan->cadencepos >= DAHDI_MAX_CADENCE) + chan->cadencepos = 0; + len = chan->ringcadence[chan->cadencepos]; + + if (!len) { + chan->cadencepos = 0; + len = chan->curzone->ringcadence[chan->cadencepos]; + } + + dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_RINGOFF, len); + __qevent(chan, DAHDI_EVENT_RINGEROFF); + break; + + case DAHDI_TXSTATE_START: + /* If we were starting, go off hook now ready to debounce */ + dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_AFTERSTART, DAHDI_AFTERSTART_TIME); + wake_up_interruptible(&chan->txstateq); + break; + + case DAHDI_TXSTATE_PREWINK: + /* Actually wink */ + dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_WINK, chan->winktime); + break; + + case DAHDI_TXSTATE_WINK: + /* Wink complete, go on hook and stabalize */ + dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_ONHOOK, 0); + if (chan->file && (chan->file->f_flags & O_NONBLOCK)) + __qevent(chan, DAHDI_EVENT_HOOKCOMPLETE); + wake_up_interruptible(&chan->txstateq); + break; + + case DAHDI_TXSTATE_PREFLASH: + /* Actually flash */ + dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_FLASH, chan->flashtime); + break; + + case DAHDI_TXSTATE_FLASH: + dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_OFFHOOK, 0); + if (chan->file && (chan->file->f_flags & O_NONBLOCK)) + __qevent(chan, DAHDI_EVENT_HOOKCOMPLETE); + wake_up_interruptible(&chan->txstateq); + break; + + case DAHDI_TXSTATE_DEBOUNCE: + dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_OFFHOOK, 0); + /* See if we've gone back on hook */ + if ((chan->rxhooksig == DAHDI_RXSIG_ONHOOK) && (chan->rxflashtime > 2)) + chan->itimerset = chan->itimer = chan->rxflashtime * DAHDI_CHUNKSIZE; + wake_up_interruptible(&chan->txstateq); + break; + + case DAHDI_TXSTATE_AFTERSTART: + dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_OFFHOOK, 0); + if (chan->file && (chan->file->f_flags & O_NONBLOCK)) + __qevent(chan, DAHDI_EVENT_HOOKCOMPLETE); + wake_up_interruptible(&chan->txstateq); + break; + + case DAHDI_TXSTATE_KEWL: + dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, DAHDI_TXSTATE_AFTERKEWL, DAHDI_AFTERKEWLTIME); + if (chan->file && (chan->file->f_flags & O_NONBLOCK)) + __qevent(chan, DAHDI_EVENT_HOOKCOMPLETE); + wake_up_interruptible(&chan->txstateq); + break; + + case DAHDI_TXSTATE_AFTERKEWL: + if (chan->kewlonhook) { + __qevent(chan,DAHDI_EVENT_ONHOOK); + } + chan->txstate = DAHDI_TXSTATE_ONHOOK; + chan->gotgs = 0; + break; + + case DAHDI_TXSTATE_PULSEBREAK: + dahdi_rbs_sethook(chan, DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_PULSEMAKE, + chan->pulsemaketime); + wake_up_interruptible(&chan->txstateq); + break; + + case DAHDI_TXSTATE_PULSEMAKE: + if (chan->pdialcount) + chan->pdialcount--; + if (chan->pdialcount) + { + dahdi_rbs_sethook(chan, DAHDI_TXSIG_ONHOOK, + DAHDI_TXSTATE_PULSEBREAK, chan->pulsebreaktime); + break; + } + chan->txstate = DAHDI_TXSTATE_PULSEAFTER; + chan->otimer = chan->pulseaftertime * DAHDI_CHUNKSIZE; + wake_up_interruptible(&chan->txstateq); + break; + + case DAHDI_TXSTATE_PULSEAFTER: + chan->txstate = DAHDI_TXSTATE_OFFHOOK; + __do_dtmf(chan); + wake_up_interruptible(&chan->txstateq); + break; + + default: + break; + } +} + +static void __dahdi_hooksig_pvt(struct dahdi_chan *chan, enum dahdi_rxsig rxsig) +{ + + /* State machines for receive hookstate transitions + called with chan->lock held */ + + if ((chan->rxhooksig) == rxsig) return; + + if ((chan->flags & DAHDI_FLAG_SIGFREEZE)) return; + + chan->rxhooksig = rxsig; +#ifdef RINGBEGIN + if ((chan->sig & __DAHDI_SIG_FXS) && (rxsig == DAHDI_RXSIG_RING) && + (!chan->ringdebtimer)) + __qevent(chan,DAHDI_EVENT_RINGBEGIN); +#endif + switch(chan->sig) { + case DAHDI_SIG_EM: /* E and M */ + case DAHDI_SIG_EM_E1: + switch(rxsig) { + case DAHDI_RXSIG_OFFHOOK: /* went off hook */ + /* The interface is going off hook */ +#ifdef EMFLASH + if (chan->itimer) + { + __qevent(chan,DAHDI_EVENT_WINKFLASH); + chan->itimerset = chan->itimer = 0; + break; + } +#endif +#ifdef EMPULSE + if (chan->itimer) /* if timer still running */ + { + int plen = chan->itimerset - chan->itimer; + if (plen <= DAHDI_MAXPULSETIME) + { + if (plen >= DAHDI_MINPULSETIME) + { + chan->pulsecount++; + + chan->pulsetimer = DAHDI_PULSETIMEOUT; + chan->itimerset = chan->itimer = 0; + if (chan->pulsecount == 1) + __qevent(chan,DAHDI_EVENT_PULSE_START); + } + } + break; + } +#endif + /* set wink timer */ + chan->itimerset = chan->itimer = chan->rxwinktime * DAHDI_CHUNKSIZE; + break; + case DAHDI_RXSIG_ONHOOK: /* went on hook */ + /* This interface is now going on hook. + Check for WINK, etc */ + if (chan->itimer) + __qevent(chan,DAHDI_EVENT_WINKFLASH); +#if defined(EMFLASH) || defined(EMPULSE) + else { +#ifdef EMFLASH + chan->itimerset = chan->itimer = chan->rxflashtime * DAHDI_CHUNKSIZE; + +#else /* EMFLASH */ + chan->itimerset = chan->itimer = chan->rxwinktime * DAHDI_CHUNKSIZE; + +#endif /* EMFLASH */ + chan->gotgs = 0; + break; + } +#else /* EMFLASH || EMPULSE */ + else { + __qevent(chan,DAHDI_EVENT_ONHOOK); + chan->gotgs = 0; + } +#endif + chan->itimerset = chan->itimer = 0; + break; + default: + break; + } + break; + case DAHDI_SIG_FXSKS: /* FXS Kewlstart */ + /* ignore a bit error if loop not closed and stable */ + if (chan->txstate != DAHDI_TXSTATE_OFFHOOK) break; +#ifdef FXSFLASH + if (rxsig == DAHDI_RXSIG_ONHOOK) { + chan->itimer = DAHDI_FXSFLASHMAXTIME * DAHDI_CHUNKSIZE; + break; + } else if (rxsig == DAHDI_RXSIG_OFFHOOK) { + if (chan->itimer) { + /* did the offhook occur in the window? if not, ignore both events */ + if (chan->itimer <= ((DAHDI_FXSFLASHMAXTIME - DAHDI_FXSFLASHMINTIME) * DAHDI_CHUNKSIZE)) + __qevent(chan, DAHDI_EVENT_WINKFLASH); + } + chan->itimer = 0; + break; + } +#endif + /* fall through intentionally */ + case DAHDI_SIG_FXSGS: /* FXS Groundstart */ + if (rxsig == DAHDI_RXSIG_ONHOOK) { + chan->ringdebtimer = RING_DEBOUNCE_TIME; + chan->ringtrailer = 0; + if (chan->txstate != DAHDI_TXSTATE_DEBOUNCE) { + chan->gotgs = 0; + __qevent(chan,DAHDI_EVENT_ONHOOK); + } + } + break; + case DAHDI_SIG_FXOGS: /* FXO Groundstart */ + if (rxsig == DAHDI_RXSIG_START) { + /* if havent got gs, report it */ + if (!chan->gotgs) { + __qevent(chan,DAHDI_EVENT_RINGOFFHOOK); + chan->gotgs = 1; + } + } + /* fall through intentionally */ + case DAHDI_SIG_FXOLS: /* FXO Loopstart */ + case DAHDI_SIG_FXOKS: /* FXO Kewlstart */ + switch(rxsig) { + case DAHDI_RXSIG_OFFHOOK: /* went off hook */ + /* if asserti ng ring, stop it */ + if (chan->txstate == DAHDI_TXSTATE_START) { + dahdi_rbs_sethook(chan,DAHDI_TXSIG_OFFHOOK, DAHDI_TXSTATE_AFTERSTART, DAHDI_AFTERSTART_TIME); + } + chan->kewlonhook = 0; +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "Off hook on channel %d, itimer = %d, gotgs = %d\n", chan->channo, chan->itimer, chan->gotgs); +#endif + if (chan->itimer) /* if timer still running */ + { + int plen = chan->itimerset - chan->itimer; + if (plen <= DAHDI_MAXPULSETIME) + { + if (plen >= DAHDI_MINPULSETIME) + { + chan->pulsecount++; + chan->pulsetimer = DAHDI_PULSETIMEOUT; + chan->itimer = chan->itimerset; + if (chan->pulsecount == 1) + __qevent(chan,DAHDI_EVENT_PULSE_START); + } + } else + __qevent(chan,DAHDI_EVENT_WINKFLASH); + } else { + /* if havent got GS detect */ + if (!chan->gotgs) { + __qevent(chan,DAHDI_EVENT_RINGOFFHOOK); + chan->gotgs = 1; + chan->itimerset = chan->itimer = 0; + } + } + chan->itimerset = chan->itimer = 0; + break; + case DAHDI_RXSIG_ONHOOK: /* went on hook */ + /* if not during offhook debounce time */ + if ((chan->txstate != DAHDI_TXSTATE_DEBOUNCE) && + (chan->txstate != DAHDI_TXSTATE_KEWL) && + (chan->txstate != DAHDI_TXSTATE_AFTERKEWL)) { + chan->itimerset = chan->itimer = chan->rxflashtime * DAHDI_CHUNKSIZE; + } + if (chan->txstate == DAHDI_TXSTATE_KEWL) + chan->kewlonhook = 1; + break; + default: + break; + } + default: + break; + } +} + +void dahdi_hooksig(struct dahdi_chan *chan, enum dahdi_rxsig rxsig) +{ + /* skip if no change */ + unsigned long flags; + spin_lock_irqsave(&chan->lock, flags); + __dahdi_hooksig_pvt(chan,rxsig); + spin_unlock_irqrestore(&chan->lock, flags); +} + +void dahdi_rbsbits(struct dahdi_chan *chan, int cursig) +{ + unsigned long flags; + if (cursig == chan->rxsig) + return; + + if ((chan->flags & DAHDI_FLAG_SIGFREEZE)) return; + + spin_lock_irqsave(&chan->lock, flags); + switch(chan->sig) { + case DAHDI_SIG_FXOGS: /* FXO Groundstart */ + /* B-bit only matters for FXO GS */ + if (!(cursig & DAHDI_BBIT)) { + __dahdi_hooksig_pvt(chan, DAHDI_RXSIG_START); + break; + } + /* Fall through */ + case DAHDI_SIG_EM: /* E and M */ + case DAHDI_SIG_EM_E1: + case DAHDI_SIG_FXOLS: /* FXO Loopstart */ + case DAHDI_SIG_FXOKS: /* FXO Kewlstart */ + if (cursig & DAHDI_ABIT) /* off hook */ + __dahdi_hooksig_pvt(chan,DAHDI_RXSIG_OFFHOOK); + else /* on hook */ + __dahdi_hooksig_pvt(chan,DAHDI_RXSIG_ONHOOK); + break; + + case DAHDI_SIG_FXSKS: /* FXS Kewlstart */ + case DAHDI_SIG_FXSGS: /* FXS Groundstart */ + /* Fall through */ + case DAHDI_SIG_FXSLS: + if (!(cursig & DAHDI_BBIT)) { + /* Check for ringing first */ + __dahdi_hooksig_pvt(chan, DAHDI_RXSIG_RING); + break; + } + if ((chan->sig != DAHDI_SIG_FXSLS) && (cursig & DAHDI_ABIT)) { + /* if went on hook */ + __dahdi_hooksig_pvt(chan, DAHDI_RXSIG_ONHOOK); + } else { + __dahdi_hooksig_pvt(chan, DAHDI_RXSIG_OFFHOOK); + } + break; + case DAHDI_SIG_CAS: + /* send event that something changed */ + __qevent(chan, DAHDI_EVENT_BITSCHANGED); + break; + + default: + break; + } + /* Keep track of signalling for next time */ + chan->rxsig = cursig; + spin_unlock_irqrestore(&chan->lock, flags); +} + +static void process_echocan_events(struct dahdi_chan *chan) +{ + union dahdi_echocan_events events = chan->ec_state->events; + + if (events.CED_tx_detected) { + dahdi_qevent_nolock(chan, DAHDI_EVENT_TX_CED_DETECTED); + if (chan->ec_state) { + if (chan->ec_state->status.mode == ECHO_MODE_ACTIVE) + set_echocan_fax_mode(chan, chan->channo, "CED tx detected", 1); + else + module_printk(KERN_NOTICE, "Detected CED tone (tx) on channel %d\n", chan->channo); + } + } + + if (events.CED_rx_detected) { + dahdi_qevent_nolock(chan, DAHDI_EVENT_RX_CED_DETECTED); + if (chan->ec_state) { + if (chan->ec_state->status.mode == ECHO_MODE_ACTIVE) + set_echocan_fax_mode(chan, chan->channo, "CED rx detected", 1); + else + module_printk(KERN_NOTICE, "Detected CED tone (rx) on channel %d\n", chan->channo); + } + } + + if (events.CNG_tx_detected) + dahdi_qevent_nolock(chan, DAHDI_EVENT_TX_CNG_DETECTED); + + if (events.CNG_rx_detected) + dahdi_qevent_nolock(chan, DAHDI_EVENT_RX_CNG_DETECTED); + + if (events.NLP_auto_disabled) { + dahdi_qevent_nolock(chan, DAHDI_EVENT_EC_NLP_DISABLED); + chan->ec_state->status.mode = ECHO_MODE_FAX; + } + + if (events.NLP_auto_enabled) { + dahdi_qevent_nolock(chan, DAHDI_EVENT_EC_NLP_ENABLED); + chan->ec_state->status.mode = ECHO_MODE_ACTIVE; + } +} + +static inline void __dahdi_ec_chunk(struct dahdi_chan *ss, unsigned char *rxchunk, const unsigned char *txchunk) +{ + short rxlin, txlin; + int x; + unsigned long flags; + + spin_lock_irqsave(&ss->lock, flags); + + if (ss->readchunkpreec) { + /* Save a copy of the audio before the echo can has its way with it */ + for (x = 0; x < DAHDI_CHUNKSIZE; x++) + /* We only ever really need to deal with signed linear - let's just convert it now */ + ss->readchunkpreec[x] = DAHDI_XLAW(rxchunk[x], ss); + } + + /* Perform echo cancellation on a chunk if necessary */ + if (ss->ec_state) { +#if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP) + dahdi_kernel_fpu_begin(); +#endif + if (ss->ec_state->status.mode & __ECHO_MODE_MUTE) { + /* Special stuff for training the echo can */ + for (x=0;xec_state->status.mode == ECHO_MODE_PRETRAINING) { + if (--ss->ec_state->status.pretrain_timer <= 0) { + ss->ec_state->status.pretrain_timer = 0; + ss->ec_state->status.mode = ECHO_MODE_STARTTRAINING; + } + } + if (ss->ec_state->status.mode == ECHO_MODE_AWAITINGECHO) { + ss->ec_state->status.last_train_tap = 0; + ss->ec_state->status.mode = ECHO_MODE_TRAINING; + } + if ((ss->ec_state->status.mode == ECHO_MODE_TRAINING) && + (ss->ec_state->ops->echocan_traintap)) { + if (ss->ec_state->ops->echocan_traintap(ss->ec_state, ss->ec_state->status.last_train_tap++, rxlin)) { +#if 0 + module_printk(KERN_NOTICE, "Finished training (%d taps trained)!\n", ss->ec_state->status.last_train_tap); +#endif + ss->ec_state->status.mode = ECHO_MODE_ACTIVE; + } + } + rxlin = 0; + rxchunk[x] = DAHDI_LIN2X((int)rxlin, ss); + } + } else if (ss->ec_state->status.mode != ECHO_MODE_IDLE) { + ss->ec_state->events.all = 0; + + if (ss->ec_state->ops->echocan_process) { + short rxlins[DAHDI_CHUNKSIZE], txlins[DAHDI_CHUNKSIZE]; + + for (x = 0; x < DAHDI_CHUNKSIZE; x++) { + rxlins[x] = DAHDI_XLAW(rxchunk[x], ss); + txlins[x] = DAHDI_XLAW(txchunk[x], ss); + } + ss->ec_state->ops->echocan_process(ss->ec_state, rxlins, txlins, DAHDI_CHUNKSIZE); + + for (x = 0; x < DAHDI_CHUNKSIZE; x++) + rxchunk[x] = DAHDI_LIN2X((int) rxlins[x], ss); + } else if (ss->ec_state->ops->echocan_events) + ss->ec_state->ops->echocan_events(ss->ec_state); + + if (ss->ec_state->events.all) + process_echocan_events(ss); + + } +#if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP) + kernel_fpu_end(); +#endif + } + spin_unlock_irqrestore(&ss->lock, flags); +} + +void dahdi_ec_chunk(struct dahdi_chan *ss, unsigned char *rxchunk, const unsigned char *txchunk) +{ + __dahdi_ec_chunk(ss, rxchunk, txchunk); +} + +void dahdi_ec_span(struct dahdi_span *span) +{ + int x; + for (x = 0; x < span->channels; x++) { + if (span->chans[x]->ec_current) + __dahdi_ec_chunk(span->chans[x], span->chans[x]->readchunk, span->chans[x]->writechunk); + } +} + +/* return 0 if nothing detected, 1 if lack of tone, 2 if presence of tone */ +/* modifies buffer pointed to by 'amp' with notched-out values */ +static inline int sf_detect(struct sf_detect_state *s, + short *amp, + int samples,long p1, long p2, long p3) +{ +int i,rv = 0; +long x,y; + +#define SF_DETECT_SAMPLES (DAHDI_CHUNKSIZE * 5) +#define SF_DETECT_MIN_ENERGY 500 +#define NB 14 /* number of bits to shift left */ + + /* determine energy level before filtering */ + for(i = 0; i < samples; i++) + { + if (amp[i] < 0) s->e1 -= amp[i]; + else s->e1 += amp[i]; + } + /* do 2nd order IIR notch filter at given freq. and calculate + energy */ + for(i = 0; i < samples; i++) + { + x = amp[i] << NB; + y = s->x2 + (p1 * (s->x1 >> NB)) + x; + y += (p2 * (s->y2 >> NB)) + + (p3 * (s->y1 >> NB)); + s->x2 = s->x1; + s->x1 = x; + s->y2 = s->y1; + s->y1 = y; + amp[i] = y >> NB; + if (amp[i] < 0) s->e2 -= amp[i]; + else s->e2 += amp[i]; + } + s->samps += i; + /* if time to do determination */ + if ((s->samps) >= SF_DETECT_SAMPLES) + { + rv = 1; /* default to no tone */ + /* if enough energy, it is determined to be a tone */ + if (((s->e1 - s->e2) / s->samps) > SF_DETECT_MIN_ENERGY) rv = 2; + /* reset energy processing variables */ + s->samps = 0; + s->e1 = s->e2 = 0; + } + return(rv); +} + +static inline void __dahdi_process_putaudio_chunk(struct dahdi_chan *ss, unsigned char *rxb) +{ + /* We transmit data from our master channel */ + /* Called with ss->lock held */ + struct dahdi_chan *ms = ss->master; + /* Linear version of received data */ + short putlin[DAHDI_CHUNKSIZE],k[DAHDI_CHUNKSIZE]; + int x,r; + + /* (CNET) Allow caller to hear outpulsing if hearpulsing is true. + NOTE: This also requires a patched chan_zap with its own hearpulsing option! */ + if(!ms->hearpulsing) { + if (ms->dialing) ms->afterdialingtimer = 50; + else if (ms->afterdialingtimer) ms->afterdialingtimer--; + if (ms->afterdialingtimer && (!(ms->flags & DAHDI_FLAG_PSEUDO))) { + /* Be careful since memset is likely a macro */ + rxb[0] = DAHDI_LIN2X(0, ms); + memset(&rxb[1], rxb[0], DAHDI_CHUNKSIZE - 1); /* receive as silence if dialing */ + } + } + for (x=0;xrxgain[rxb[x]]; + putlin[x] = DAHDI_XLAW(rxb[x], ms); + } + + if (ms->ec_state && (ms->ec_state->status.mode == ECHO_MODE_ACTIVE) && !ms->ec_state->features.CED_rx_detect) { + for (x = 0; x < DAHDI_CHUNKSIZE; x++) { + if (echo_can_disable_detector_update(&ms->ec_state->rxecdis, putlin[x])) { + set_echocan_fax_mode(ms, ss->channo, "CED rx detected", 1); + dahdi_qevent_nolock(ms, DAHDI_EVENT_RX_CED_DETECTED); + break; + } + } + } + + /* if doing rx tone decoding */ + if (ms->rxp1 && ms->rxp2 && ms->rxp3) + { + r = sf_detect(&ms->rd,putlin,DAHDI_CHUNKSIZE,ms->rxp1, + ms->rxp2,ms->rxp3); + /* Convert back */ + for(x=0;xrd.lastdetect) + { + if (((r == 2) && !(ms->toneflags & DAHDI_REVERSE_RXTONE)) || + ((r == 1) && (ms->toneflags & DAHDI_REVERSE_RXTONE))) + { + __qevent(ms,DAHDI_EVENT_RINGOFFHOOK); + } + else + { + __qevent(ms,DAHDI_EVENT_ONHOOK); + } + ms->rd.lastdetect = r; + } + } + } + + if (!(ms->flags & DAHDI_FLAG_PSEUDO)) { + memcpy(ms->putlin, putlin, DAHDI_CHUNKSIZE * sizeof(short)); + memcpy(ms->putraw, rxb, DAHDI_CHUNKSIZE); + } + + /* Take the rxc, twiddle it for conferencing if appropriate and put it + back */ + if ((!ms->confmute && !ms->afterdialingtimer) || + (ms->flags & DAHDI_FLAG_PSEUDO)) { + switch(ms->confmode & DAHDI_CONF_MODE_MASK) { + case DAHDI_CONF_NORMAL: /* Normal mode */ + /* Do nothing. rx goes output */ + break; + case DAHDI_CONF_MONITOR: /* Monitor a channel's rx mode */ + /* if not a pseudo-channel, ignore */ + if (!(ms->flags & DAHDI_FLAG_PSEUDO)) break; + /* Add monitored channel */ + if (chans[ms->confna]->flags & DAHDI_FLAG_PSEUDO) { + ACSS(putlin, chans[ms->confna]->getlin); + } else { + ACSS(putlin, chans[ms->confna]->putlin); + } + /* Convert back */ + for(x=0;xflags & DAHDI_FLAG_PSEUDO)) break; + /* Add monitored channel */ + if (chans[ms->confna]->flags & DAHDI_FLAG_PSEUDO) { + ACSS(putlin, chans[ms->confna]->putlin); + } else { + ACSS(putlin, chans[ms->confna]->getlin); + } + /* Convert back */ + for(x=0;xflags & DAHDI_FLAG_PSEUDO)) break; + /* Note: Technically, saturation should be done at + the end of the whole addition, but for performance + reasons, we don't do that. Besides, it only matters + when you're so loud you're clipping anyway */ + ACSS(putlin, chans[ms->confna]->getlin); + ACSS(putlin, chans[ms->confna]->putlin); + /* Convert back */ + for(x=0;xflags & DAHDI_FLAG_PSEUDO)) + break; + + if (!chans[ms->confna]->readchunkpreec) + break; + + /* Add monitored channel */ + ACSS(putlin, chans[ms->confna]->flags & DAHDI_FLAG_PSEUDO ? + chans[ms->confna]->getlin : chans[ms->confna]->readchunkpreec); + for (x = 0; x < DAHDI_CHUNKSIZE; x++) + rxb[x] = DAHDI_LIN2X(putlin[x], ms); + + break; + case DAHDI_CONF_MONITOR_TX_PREECHO: /* Monitor a channel's tx mode */ + /* if not a pseudo-channel, ignore */ + if (!(ms->flags & DAHDI_FLAG_PSEUDO)) + break; + + if (!chans[ms->confna]->readchunkpreec) + break; + + /* Add monitored channel */ + ACSS(putlin, chans[ms->confna]->flags & DAHDI_FLAG_PSEUDO ? + chans[ms->confna]->readchunkpreec : chans[ms->confna]->getlin); + for (x = 0; x < DAHDI_CHUNKSIZE; x++) + rxb[x] = DAHDI_LIN2X(putlin[x], ms); + + break; + case DAHDI_CONF_MONITORBOTH_PREECHO: /* Monitor a channel's tx and rx mode */ + /* if not a pseudo-channel, ignore */ + if (!(ms->flags & DAHDI_FLAG_PSEUDO)) + break; + + if (!chans[ms->confna]->readchunkpreec) + break; + + /* Note: Technically, saturation should be done at + the end of the whole addition, but for performance + reasons, we don't do that. Besides, it only matters + when you're so loud you're clipping anyway */ + ACSS(putlin, chans[ms->confna]->getlin); + ACSS(putlin, chans[ms->confna]->readchunkpreec); + for (x = 0; x < DAHDI_CHUNKSIZE; x++) + rxb[x] = DAHDI_LIN2X(putlin[x], ms); + + break; + case DAHDI_CONF_REALANDPSEUDO: + /* do normal conf mode processing */ + if (ms->confmode & DAHDI_CONF_TALKER) { + /* Store temp value */ + memcpy(k, putlin, DAHDI_CHUNKSIZE * sizeof(short)); + /* Add conf value */ + ACSS(k, conf_sums_next[ms->_confn]); + /* get amount actually added */ + memcpy(ms->conflast, k, DAHDI_CHUNKSIZE * sizeof(short)); + SCSS(ms->conflast, conf_sums_next[ms->_confn]); + /* Really add in new value */ + ACSS(conf_sums_next[ms->_confn], ms->conflast); + } else memset(ms->conflast, 0, DAHDI_CHUNKSIZE * sizeof(short)); + /* do the pseudo-channel part processing */ + memset(putlin, 0, DAHDI_CHUNKSIZE * sizeof(short)); + if (ms->confmode & DAHDI_CONF_PSEUDO_LISTENER) { + /* Subtract out previous last sample written to conf */ + SCSS(putlin, ms->conflast2); + /* Add in conference */ + ACSS(putlin, conf_sums[ms->_confn]); + } + /* Convert back */ + for(x=0;xflags & DAHDI_FLAG_PSEUDO) /* if a pseudo-channel */ + { + if (ms->confmode & DAHDI_CONF_LISTENER) { + /* Subtract out last sample written to conf */ + SCSS(putlin, ms->conflast); + /* Add in conference */ + ACSS(putlin, conf_sums[ms->_confn]); + } + /* Convert back */ + for(x=0;xputlin, putlin, DAHDI_CHUNKSIZE * sizeof(short)); + break; + } + /* fall through */ + case DAHDI_CONF_CONFANN: /* Conference with announce */ + if (ms->confmode & DAHDI_CONF_TALKER) { + /* Store temp value */ + memcpy(k, putlin, DAHDI_CHUNKSIZE * sizeof(short)); + /* Add conf value */ + ACSS(k, conf_sums_next[ms->_confn]); + /* get amount actually added */ + memcpy(ms->conflast, k, DAHDI_CHUNKSIZE * sizeof(short)); + SCSS(ms->conflast, conf_sums_next[ms->_confn]); + /* Really add in new value */ + ACSS(conf_sums_next[ms->_confn], ms->conflast); + } else + memset(ms->conflast, 0, DAHDI_CHUNKSIZE * sizeof(short)); + /* rxc unmodified */ + break; + case DAHDI_CONF_CONFMON: + case DAHDI_CONF_CONFANNMON: + if (ms->confmode & DAHDI_CONF_TALKER) { + /* Store temp value */ + memcpy(k, putlin, DAHDI_CHUNKSIZE * sizeof(short)); + /* Subtract last value */ + SCSS(conf_sums[ms->_confn], ms->conflast); + /* Add conf value */ + ACSS(k, conf_sums[ms->_confn]); + /* get amount actually added */ + memcpy(ms->conflast, k, DAHDI_CHUNKSIZE * sizeof(short)); + SCSS(ms->conflast, conf_sums[ms->_confn]); + /* Really add in new value */ + ACSS(conf_sums[ms->_confn], ms->conflast); + } else + memset(ms->conflast, 0, DAHDI_CHUNKSIZE * sizeof(short)); + for (x=0;x_confn][x], ms); + break; + case DAHDI_CONF_DIGITALMON: + /* if not a pseudo-channel, ignore */ + if (!(ms->flags & DAHDI_FLAG_PSEUDO)) break; + /* Add monitored channel */ + if (chans[ms->confna]->flags & DAHDI_FLAG_PSEUDO) { + memcpy(rxb, chans[ms->confna]->getraw, DAHDI_CHUNKSIZE); + } else { + memcpy(rxb, chans[ms->confna]->putraw, DAHDI_CHUNKSIZE); + } + break; + } + } +} + +/* HDLC (or other) receiver buffer functions for read side */ +static inline void __putbuf_chunk(struct dahdi_chan *ss, unsigned char *rxb, int bytes) +{ + /* We transmit data from our master channel */ + /* Called with ss->lock held */ + struct dahdi_chan *ms = ss->master; + /* Our receive buffer */ + unsigned char *buf; +#if defined(CONFIG_DAHDI_NET) || defined(CONFIG_DAHDI_PPP) + /* SKB for receiving network stuff */ + struct sk_buff *skb=NULL; +#endif + int oldbuf; + int eof=0; + int abort=0; + int res; + int left, x; + + while(bytes) { +#if defined(CONFIG_DAHDI_NET) || defined(CONFIG_DAHDI_PPP) + skb = NULL; +#endif + abort = 0; + eof = 0; + /* Next, figure out if we've got a buffer to receive into */ + if (ms->inreadbuf > -1) { + /* Read into the current buffer */ + buf = ms->readbuf[ms->inreadbuf]; + left = ms->blocksize - ms->readidx[ms->inreadbuf]; + if (left > bytes) + left = bytes; + if (ms->flags & DAHDI_FLAG_HDLC) { + for (x=0;xrxhdlc, *(rxb++)); + bytes--; + res = fasthdlc_rx_run(&ms->rxhdlc); + /* If there is nothing there, continue */ + if (res & RETURN_EMPTY_FLAG) + continue; + else if (res & RETURN_COMPLETE_FLAG) { + /* Only count this if it's a non-empty frame */ + if (ms->readidx[ms->inreadbuf]) { + if ((ms->flags & DAHDI_FLAG_FCS) && (ms->infcs != PPP_GOODFCS)) { + abort = DAHDI_EVENT_BADFCS; + } else + eof=1; + break; + } + continue; + } else if (res & RETURN_DISCARD_FLAG) { + /* This could be someone idling with + "idle" instead of "flag" */ + if (!ms->readidx[ms->inreadbuf]) + continue; + abort = DAHDI_EVENT_ABORT; + break; + } else { + unsigned char rxc; + rxc = res; + ms->infcs = PPP_FCS(ms->infcs, rxc); + buf[ms->readidx[ms->inreadbuf]++] = rxc; + /* Pay attention to the possibility of an overrun */ + if (ms->readidx[ms->inreadbuf] >= ms->blocksize) { + if (!ss->span->alarms) + module_printk(KERN_WARNING, "HDLC Receiver overrun on channel %s (master=%s)\n", ss->name, ss->master->name); + abort=DAHDI_EVENT_OVERRUN; + /* Force the HDLC state back to frame-search mode */ + ms->rxhdlc.state = 0; + ms->rxhdlc.bits = 0; + ms->readidx[ms->inreadbuf]=0; + break; + } + } + } + } else { + /* Not HDLC */ + memcpy(buf + ms->readidx[ms->inreadbuf], rxb, left); + rxb += left; + ms->readidx[ms->inreadbuf] += left; + bytes -= left; + /* End of frame is decided by block size of 'N' */ + eof = (ms->readidx[ms->inreadbuf] >= ms->blocksize); + if (eof && (ss->flags & DAHDI_FLAG_NOSTDTXRX)) { + eof = 0; + abort = DAHDI_EVENT_OVERRUN; + } + } + if (eof) { + /* Finished with this buffer, try another. */ + oldbuf = ms->inreadbuf; + ms->infcs = PPP_INITFCS; + ms->readn[ms->inreadbuf] = ms->readidx[ms->inreadbuf]; +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "EOF, len is %d\n", ms->readn[ms->inreadbuf]); +#endif +#if defined(CONFIG_DAHDI_NET) || defined(CONFIG_DAHDI_PPP) + if (ms->flags & (DAHDI_FLAG_NETDEV | DAHDI_FLAG_PPP)) { +#ifdef CONFIG_DAHDI_NET +#endif /* CONFIG_DAHDI_NET */ + /* Our network receiver logic is MUCH + different. We actually only use a single + buffer */ + if (ms->readn[ms->inreadbuf] > 1) { + /* Drop the FCS */ + ms->readn[ms->inreadbuf] -= 2; + /* Allocate an SKB */ +#ifdef CONFIG_DAHDI_PPP + if (!ms->do_ppp_error) +#endif + skb = dev_alloc_skb(ms->readn[ms->inreadbuf]); + if (skb) { + /* XXX Get rid of this memcpy XXX */ + memcpy(skb->data, ms->readbuf[ms->inreadbuf], ms->readn[ms->inreadbuf]); + skb_put(skb, ms->readn[ms->inreadbuf]); +#ifdef CONFIG_DAHDI_NET + if (ms->flags & DAHDI_FLAG_NETDEV) { + struct net_device_stats *stats = hdlc_stats(ms->hdlcnetdev->netdev); + stats->rx_packets++; + stats->rx_bytes += ms->readn[ms->inreadbuf]; + } +#endif + + } else { +#ifdef CONFIG_DAHDI_NET + if (ms->flags & DAHDI_FLAG_NETDEV) { + struct net_device_stats *stats = hdlc_stats(ms->hdlcnetdev->netdev); + stats->rx_dropped++; + } +#endif +#ifdef CONFIG_DAHDI_PPP + if (ms->flags & DAHDI_FLAG_PPP) { + abort = DAHDI_EVENT_OVERRUN; + } +#endif +#if 1 +#ifdef CONFIG_DAHDI_PPP + if (!ms->do_ppp_error) +#endif + module_printk(KERN_NOTICE, "Memory squeeze, dropped one\n"); +#endif + } + } + /* We don't cycle through buffers, just + reuse the same one */ + ms->readn[ms->inreadbuf] = 0; + ms->readidx[ms->inreadbuf] = 0; + } else +#endif + { + /* This logic might confuse and astound. Basically we need to find + * the previous buffer index. It should be safe because, regardless + * of whether or not it has been copied to user space, nothing should + * have messed around with it since then */ + + int comparemessage; + /* Shut compiler up */ + int myres = 0; + + if (ms->flags & DAHDI_FLAG_MTP2) { + comparemessage = (ms->inreadbuf - 1) & (ms->numbufs - 1); + + myres = memcmp(ms->readbuf[comparemessage], ms->readbuf[ms->inreadbuf], ms->readn[ms->inreadbuf]); + } + + if ((ms->flags & DAHDI_FLAG_MTP2) && !myres) { + /* Our messages are the same, so discard - + * Don't advance buffers, reset indexes and buffer sizes. */ + ms->readn[ms->inreadbuf] = 0; + ms->readidx[ms->inreadbuf] = 0; + } else { + ms->inreadbuf = (ms->inreadbuf + 1) % ms->numbufs; + if (ms->inreadbuf == ms->outreadbuf) { + /* Whoops, we're full, and have no where else + to store into at the moment. We'll drop it + until there's a buffer available */ +#ifdef BUFFER_DEBUG + module_printk(KERN_NOTICE, "Out of storage space\n"); +#endif + ms->inreadbuf = -1; + /* Enable the receiver in case they've got POLICY_WHEN_FULL */ + ms->rxdisable = 0; + } + if (ms->outreadbuf < 0) { /* start out buffer if not already */ + ms->outreadbuf = oldbuf; + /* if there are processes waiting in poll() on this channel, + wake them up */ + if (!ms->rxdisable) { + wake_up_interruptible(&ms->sel); + } + } +/* In the very orignal driver, it was quite well known to me (Jim) that there +was a possibility that a channel sleeping on a receive block needed to +be potentially woken up EVERY time a buffer was filled, not just on the first +one, because if only done on the first one there is a slight timing potential +of missing the wakeup (between where it senses the (lack of) active condition +(with interrupts disabled) and where it does the sleep (interrupts enabled) +in the read or iomux call, etc). That is why the read and iomux calls start +with an infinite loop that gets broken out of upon an active condition, +otherwise keeps sleeping and looking. The part in this code got "optimized" +out in the later versions, and is put back now. Note that this is *NOT* +needed for poll() waiters, because the poll_wait() function that is used there +is atomic enough for this purpose; it will not go to sleep before ensuring +that the waitqueue is empty. */ + if (!ms->rxdisable) { /* if receiver enabled */ + /* Notify a blocked reader that there is data available + to be read, unless we're waiting for it to be full */ +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "Notifying reader data in block %d\n", oldbuf); +#endif + wake_up_interruptible(&ms->readbufq); + if (ms->iomask & DAHDI_IOMUX_READ) + wake_up_interruptible(&ms->eventbufq); + } + } + } + } + if (abort) { + /* Start over reading frame */ + ms->readidx[ms->inreadbuf] = 0; + ms->infcs = PPP_INITFCS; + +#ifdef CONFIG_DAHDI_NET + if (ms->flags & DAHDI_FLAG_NETDEV) { + struct net_device_stats *stats = hdlc_stats(ms->hdlcnetdev->netdev); + stats->rx_errors++; + if (abort == DAHDI_EVENT_OVERRUN) + stats->rx_over_errors++; + if (abort == DAHDI_EVENT_BADFCS) + stats->rx_crc_errors++; + if (abort == DAHDI_EVENT_ABORT) + stats->rx_frame_errors++; + } else +#endif +#ifdef CONFIG_DAHDI_PPP + if (ms->flags & DAHDI_FLAG_PPP) { + ms->do_ppp_error = 1; + tasklet_schedule(&ms->ppp_calls); + } else +#endif + if (test_bit(DAHDI_FLAGBIT_OPEN, &ms->flags) && !ss->span->alarms) { + /* Notify the receiver... */ + __qevent(ss->master, abort); + } +#if 0 + module_printk(KERN_NOTICE, "torintr_receive: Aborted %d bytes of frame on %d\n", amt, ss->master); +#endif + + } + } else /* No place to receive -- drop on the floor */ + break; +#ifdef CONFIG_DAHDI_NET + if (skb && (ms->flags & DAHDI_FLAG_NETDEV)) +#ifdef NEW_HDLC_INTERFACE + { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) + skb->mac.raw = skb->data; +#else + skb_reset_mac_header(skb); +#endif + skb->dev = ztchan_to_dev(ms); +#ifdef DAHDI_HDLC_TYPE_TRANS + skb->protocol = hdlc_type_trans(skb, ztchan_to_dev(ms)); +#else + skb->protocol = htons (ETH_P_HDLC); +#endif + netif_rx(skb); + } +#else + hdlc_netif_rx(&ms->hdlcnetdev->netdev, skb); +#endif +#endif +#ifdef CONFIG_DAHDI_PPP + if (skb && (ms->flags & DAHDI_FLAG_PPP)) { + unsigned char *tmp; + tmp = skb->data; + skb_pull(skb, 2); + /* Make sure that it's addressed to ALL STATIONS and UNNUMBERED */ + if (!tmp || (tmp[0] != 0xff) || (tmp[1] != 0x03)) { + /* Invalid SKB -- drop */ + if (tmp) + module_printk(KERN_NOTICE, "Received invalid SKB (%02x, %02x)\n", tmp[0], tmp[1]); + dev_kfree_skb_irq(skb); + } else { + skb_queue_tail(&ms->ppp_rq, skb); + tasklet_schedule(&ms->ppp_calls); + } + } +#endif + } +} + +static inline void __dahdi_putbuf_chunk(struct dahdi_chan *ss, unsigned char *rxb) +{ + __putbuf_chunk(ss, rxb, DAHDI_CHUNKSIZE); +} + +static void __dahdi_hdlc_abort(struct dahdi_chan *ss, int event) +{ + if (ss->inreadbuf >= 0) + ss->readidx[ss->inreadbuf] = 0; + if (test_bit(DAHDI_FLAGBIT_OPEN, &ss->flags) && !ss->span->alarms) + __qevent(ss->master, event); +} + +void dahdi_hdlc_abort(struct dahdi_chan *ss, int event) +{ + unsigned long flags; + spin_lock_irqsave(&ss->lock, flags); + __dahdi_hdlc_abort(ss, event); + spin_unlock_irqrestore(&ss->lock, flags); +} + +void dahdi_hdlc_putbuf(struct dahdi_chan *ss, unsigned char *rxb, int bytes) +{ + unsigned long flags; + int res; + int left; + + spin_lock_irqsave(&ss->lock, flags); + if (ss->inreadbuf < 0) { +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "No place to receive HDLC frame\n"); +#endif + spin_unlock_irqrestore(&ss->lock, flags); + return; + } + /* Read into the current buffer */ + left = ss->blocksize - ss->readidx[ss->inreadbuf]; + if (left > bytes) + left = bytes; + if (left > 0) { + memcpy(ss->readbuf[ss->inreadbuf] + ss->readidx[ss->inreadbuf], rxb, left); + rxb += left; + ss->readidx[ss->inreadbuf] += left; + bytes -= left; + } + /* Something isn't fit into buffer */ + if (bytes) { +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "HDLC frame isn't fit into buffer space\n"); +#endif + __dahdi_hdlc_abort(ss, DAHDI_EVENT_OVERRUN); + } + res = left; + spin_unlock_irqrestore(&ss->lock, flags); +} + +void dahdi_hdlc_finish(struct dahdi_chan *ss) +{ + int oldreadbuf; + unsigned long flags; + + spin_lock_irqsave(&ss->lock, flags); + + if ((oldreadbuf = ss->inreadbuf) < 0) { +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "No buffers to finish\n"); +#endif + spin_unlock_irqrestore(&ss->lock, flags); + return; + } + + if (!ss->readidx[ss->inreadbuf]) { +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "Empty HDLC frame received\n"); +#endif + spin_unlock_irqrestore(&ss->lock, flags); + return; + } + + ss->readn[ss->inreadbuf] = ss->readidx[ss->inreadbuf]; + ss->inreadbuf = (ss->inreadbuf + 1) % ss->numbufs; + if (ss->inreadbuf == ss->outreadbuf) { + ss->inreadbuf = -1; +#ifdef CONFIG_DAHDI_DEBUG + module_printk(KERN_NOTICE, "Notifying reader data in block %d\n", oldreadbuf); +#endif + ss->rxdisable = 0; + } + if (ss->outreadbuf < 0) { + ss->outreadbuf = oldreadbuf; + } + + if (!ss->rxdisable) { + wake_up_interruptible(&ss->readbufq); + wake_up_interruptible(&ss->sel); + if (ss->iomask & DAHDI_IOMUX_READ) + wake_up_interruptible(&ss->eventbufq); + } + spin_unlock_irqrestore(&ss->lock, flags); +} + +/* Returns 1 if EOF, 0 if data is still in frame, -1 if EOF and no buffers left */ +int dahdi_hdlc_getbuf(struct dahdi_chan *ss, unsigned char *bufptr, unsigned int *size) +{ + unsigned char *buf; + unsigned long flags; + int left = 0; + int res; + int oldbuf; + + spin_lock_irqsave(&ss->lock, flags); + if (ss->outwritebuf > -1) { + buf = ss->writebuf[ss->outwritebuf]; + left = ss->writen[ss->outwritebuf] - ss->writeidx[ss->outwritebuf]; + /* Strip off the empty HDLC CRC end */ + left -= 2; + if (left <= *size) { + *size = left; + res = 1; + } else + res = 0; + + memcpy(bufptr, &buf[ss->writeidx[ss->outwritebuf]], *size); + ss->writeidx[ss->outwritebuf] += *size; + + if (res) { + /* Rotate buffers */ + oldbuf = ss->outwritebuf; + ss->writeidx[oldbuf] = 0; + ss->writen[oldbuf] = 0; + ss->outwritebuf = (ss->outwritebuf + 1) % ss->numbufs; + if (ss->outwritebuf == ss->inwritebuf) { + ss->outwritebuf = -1; + if (ss->iomask & (DAHDI_IOMUX_WRITE | DAHDI_IOMUX_WRITEEMPTY)) + wake_up_interruptible(&ss->eventbufq); + /* If we're only supposed to start when full, disable the transmitter */ + if ((ss->txbufpolicy == DAHDI_POLICY_WHEN_FULL) || (ss->txbufpolicy == DAHDI_POLICY_HALF_FULL)) + ss->txdisable = 1; + res = -1; + } + + if (ss->inwritebuf < 0) + ss->inwritebuf = oldbuf; + + if (!(ss->flags & (DAHDI_FLAG_NETDEV | DAHDI_FLAG_PPP))) { + wake_up_interruptible(&ss->writebufq); + wake_up_interruptible(&ss->sel); + if ((ss->iomask & DAHDI_IOMUX_WRITE) && (res >= 0)) + wake_up_interruptible(&ss->eventbufq); + } + } + } else { + res = -1; + *size = 0; + } + spin_unlock_irqrestore(&ss->lock, flags); + + return res; +} + + +static void process_timers(void) +{ + unsigned long flags; + struct dahdi_timer *cur; + + spin_lock_irqsave(&zaptimerlock, flags); + + list_for_each_entry(cur, &zaptimers, list) { + if (cur->ms) { + cur->pos -= DAHDI_CHUNKSIZE; + if (cur->pos <= 0) { + cur->tripped++; + cur->pos = cur->ms; + wake_up_interruptible(&cur->sel); + } + } + } + + spin_unlock_irqrestore(&zaptimerlock, flags); +} + +static unsigned int dahdi_timer_poll(struct file *file, struct poll_table_struct *wait_table) +{ + struct dahdi_timer *timer = file->private_data; + unsigned long flags; + int ret = 0; + if (timer) { + poll_wait(file, &timer->sel, wait_table); + spin_lock_irqsave(&zaptimerlock, flags); + if (timer->tripped || timer->ping) + ret |= POLLPRI; + spin_unlock_irqrestore(&zaptimerlock, flags); + } else + ret = -EINVAL; + return ret; +} + +/* device poll routine */ +static unsigned int +dahdi_chan_poll(struct file *file, struct poll_table_struct *wait_table, int unit) +{ + + struct dahdi_chan *chan = chans[unit]; + int ret; + unsigned long flags; + + /* do the poll wait */ + if (chan) { + poll_wait(file, &chan->sel, wait_table); + ret = 0; /* start with nothing to return */ + spin_lock_irqsave(&chan->lock, flags); + /* if at least 1 write buffer avail */ + if (chan->inwritebuf > -1) { + ret |= POLLOUT | POLLWRNORM; + } + if ((chan->outreadbuf > -1) && !chan->rxdisable) { + ret |= POLLIN | POLLRDNORM; + } + if (chan->eventoutidx != chan->eventinidx) + { + /* Indicate an exception */ + ret |= POLLPRI; + } + spin_unlock_irqrestore(&chan->lock, flags); + } else + ret = -EINVAL; + return(ret); /* return what we found */ +} + +static int dahdi_mmap(struct file *file, struct vm_area_struct *vm) +{ + int unit = UNIT(file); + if (unit == 250) + return dahdi_transcode_fops->mmap(file, vm); + return -ENOSYS; +} + +static unsigned int dahdi_poll(struct file *file, struct poll_table_struct *wait_table) +{ + int unit = UNIT(file); + struct dahdi_chan *chan; + + if (!unit) + return -EINVAL; + + if (unit == 250) + return dahdi_transcode_fops->poll(file, wait_table); + + if (unit == 253) + return dahdi_timer_poll(file, wait_table); + + if (unit == 254) { + chan = file->private_data; + if (!chan) + return -EINVAL; + return dahdi_chan_poll(file, wait_table,chan->channo); + } + if (unit == 255) { + chan = file->private_data; + if (!chan) { + module_printk(KERN_NOTICE, "No pseudo channel structure to read?\n"); + return -EINVAL; + } + return dahdi_chan_poll(file, wait_table, chan->channo); + } + return dahdi_chan_poll(file, wait_table, unit); +} + +static void __dahdi_transmit_chunk(struct dahdi_chan *chan, unsigned char *buf) +{ + unsigned char silly[DAHDI_CHUNKSIZE]; + /* Called with chan->lock locked */ +#ifdef OPTIMIZE_CHANMUTE + if(likely(chan->chanmute)) + return; +#endif + if (!buf) + buf = silly; + __dahdi_getbuf_chunk(chan, buf); + + if ((chan->flags & DAHDI_FLAG_AUDIO) || (chan->confmode)) { +#ifdef CONFIG_DAHDI_MMX + dahdi_kernel_fpu_begin(); +#endif + __dahdi_process_getaudio_chunk(chan, buf); +#ifdef CONFIG_DAHDI_MMX + kernel_fpu_end(); +#endif + } +} + +static inline void __dahdi_real_transmit(struct dahdi_chan *chan) +{ + /* Called with chan->lock held */ +#ifdef OPTIMIZE_CHANMUTE + if(likely(chan->chanmute)) + return; +#endif + if (chan->confmode) { + /* Pull queued data off the conference */ + __buf_pull(&chan->confout, chan->writechunk, chan, "dahdi_real_transmit"); + } else { + __dahdi_transmit_chunk(chan, chan->writechunk); + } +} + +static void __dahdi_getempty(struct dahdi_chan *ms, unsigned char *buf) +{ + int bytes = DAHDI_CHUNKSIZE; + int left; + unsigned char *txb = buf; + int x; + short getlin; + /* Called with ms->lock held */ + + while(bytes) { + /* Receive silence, or tone */ + if (ms->curtone) { + left = ms->curtone->tonesamples - ms->tonep; + if (left > bytes) + left = bytes; + for (x=0;xts, ms->curtone); + *(txb++) = DAHDI_LIN2X(getlin, ms); + } + ms->tonep+=left; + bytes -= left; + if (ms->tonep >= ms->curtone->tonesamples) { + struct dahdi_tone *last; + /* Go to the next sample of the tone */ + ms->tonep = 0; + last = ms->curtone; + ms->curtone = ms->curtone->next; + if (!ms->curtone) { + /* No more tones... Is this dtmf or mf? If so, go to the next digit */ + if (ms->dialing) + __do_dtmf(ms); + } else { + if (last != ms->curtone) + dahdi_init_tone_state(&ms->ts, ms->curtone); + } + } + } else { + /* Use silence */ + memset(txb, DAHDI_LIN2X(0, ms), bytes); + bytes = 0; + } + } + +} + +static void __dahdi_receive_chunk(struct dahdi_chan *chan, unsigned char *buf) +{ + /* Receive chunk of audio -- called with chan->lock held */ + unsigned char waste[DAHDI_CHUNKSIZE]; + +#ifdef OPTIMIZE_CHANMUTE + if(likely(chan->chanmute)) + return; +#endif + if (!buf) { + memset(waste, DAHDI_LIN2X(0, chan), sizeof(waste)); + buf = waste; + } + if ((chan->flags & DAHDI_FLAG_AUDIO) || (chan->confmode)) { +#ifdef CONFIG_DAHDI_MMX + dahdi_kernel_fpu_begin(); +#endif + __dahdi_process_putaudio_chunk(chan, buf); +#ifdef CONFIG_DAHDI_MMX + kernel_fpu_end(); +#endif + } + __dahdi_putbuf_chunk(chan, buf); +} + +static inline void __dahdi_real_receive(struct dahdi_chan *chan) +{ + /* Called with chan->lock held */ +#ifdef OPTIMIZE_CHANMUTE + if(likely(chan->chanmute)) + return; +#endif + if (chan->confmode) { + /* Load into queue if we have space */ + __buf_push(&chan->confin, chan->readchunk, "dahdi_real_receive"); + } else { + __dahdi_receive_chunk(chan, chan->readchunk); + } +} + +int dahdi_transmit(struct dahdi_span *span) +{ + int x,y,z; + unsigned long flags; + +#if 1 + for (x=0;xchannels;x++) { + spin_lock_irqsave(&span->chans[x]->lock, flags); + if (span->chans[x]->flags & DAHDI_FLAG_NOSTDTXRX) { + spin_unlock_irqrestore(&span->chans[x]->lock, flags); + continue; + } + if (span->chans[x] == span->chans[x]->master) { + if (span->chans[x]->otimer) { + span->chans[x]->otimer -= DAHDI_CHUNKSIZE; + if (span->chans[x]->otimer <= 0) { + __rbs_otimer_expire(span->chans[x]); + } + } + if (span->chans[x]->flags & DAHDI_FLAG_AUDIO) { + __dahdi_real_transmit(span->chans[x]); + } else { + if (span->chans[x]->nextslave) { + u_char data[DAHDI_CHUNKSIZE]; + int pos=DAHDI_CHUNKSIZE; + /* Process master/slaves one way */ + for (y=0;ychans[x], data); + pos = 0; + } + span->chans[z]->writechunk[y] = data[pos++]; + z = span->chans[z]->nextslave; + } while(z); + } + } else { + /* Process independents elsewise */ + __dahdi_real_transmit(span->chans[x]); + } + } + if (span->chans[x]->sig == DAHDI_SIG_DACS_RBS) { + if (chans[span->chans[x]->confna]) { + /* Just set bits for our destination */ + if (span->chans[x]->txsig != chans[span->chans[x]->confna]->rxsig) { + span->chans[x]->txsig = chans[span->chans[x]->confna]->rxsig; + span->rbsbits(span->chans[x], chans[span->chans[x]->confna]->rxsig); + } + } + } + + } + spin_unlock_irqrestore(&span->chans[x]->lock, flags); + } + if (span->mainttimer) { + span->mainttimer -= DAHDI_CHUNKSIZE; + if (span->mainttimer <= 0) { + span->mainttimer = 0; + if (span->maint) + span->maint(span, DAHDI_MAINT_LOOPSTOP); + span->maintstat = 0; + wake_up_interruptible(&span->maintq); + } + } +#endif + return 0; +} + +static void process_masterspan(void) +{ + unsigned long flags; + int x, y, z; + +#ifdef CONFIG_DAHDI_CORE_TIMER + /* We increment the calls since start here, so that if we switch over + * to the core timer, we know how many times we need to call + * process_masterspan in order to catch up since this function needs + * to be called 1000 times per second. */ + atomic_inc(&core_timer.count); +#endif + /* Hold the big zap lock for the duration of major + activities which touch all sorts of channels */ + spin_lock_irqsave(&bigzaplock, flags); + read_lock(&chan_lock); + /* Process any timers */ + process_timers(); + /* If we have dynamic stuff, call the ioctl with 0,0 parameters to + make it run */ + if (dahdi_dynamic_ioctl) + dahdi_dynamic_ioctl(0, 0); + + for (x = 1; x < maxchans; x++) { + if (chans[x] && chans[x]->confmode && + !(chans[x]->flags & DAHDI_FLAG_PSEUDO)) { + u_char *data; + spin_lock(&chans[x]->lock); + data = __buf_peek(&chans[x]->confin); + __dahdi_receive_chunk(chans[x], data); + if (data) { + __buf_pull(&chans[x]->confin, NULL, chans[x], + "confreceive"); + } + spin_unlock(&chans[x]->lock); + } + } + /* This is the master channel, so make things switch over */ + rotate_sums(); + /* do all the pseudo and/or conferenced channel receives (getbuf's) */ + for (x = 1; x < maxchans; x++) { + if (chans[x] && (chans[x]->flags & DAHDI_FLAG_PSEUDO)) { + spin_lock(&chans[x]->lock); + __dahdi_transmit_chunk(chans[x], NULL); + spin_unlock(&chans[x]->lock); + } + } + if (maxlinks) { +#ifdef CONFIG_DAHDI_MMX + dahdi_kernel_fpu_begin(); +#endif + /* process all the conf links */ + for (x = 1; x <= maxlinks; x++) { + /* if we have a destination conf */ + z = confalias[conf_links[x].dst]; + if (z) { + y = confalias[conf_links[x].src]; + if (y) + ACSS(conf_sums[z], conf_sums[y]); + } + } +#ifdef CONFIG_DAHDI_MMX + dahdi_kernel_fpu_end(); +#endif + } + /* do all the pseudo/conferenced channel transmits (putbuf's) */ + for (x = 1; x < maxchans; x++) { + if (chans[x] && (chans[x]->flags & DAHDI_FLAG_PSEUDO)) { + unsigned char tmp[DAHDI_CHUNKSIZE]; + spin_lock(&chans[x]->lock); + __dahdi_getempty(chans[x], tmp); + __dahdi_receive_chunk(chans[x], tmp); + spin_unlock(&chans[x]->lock); + } + } + for (x = 1; x < maxchans; x++) { + if (chans[x] && chans[x]->confmode && + !(chans[x]->flags & DAHDI_FLAG_PSEUDO)) { + u_char *data; + spin_lock(&chans[x]->lock); + data = __buf_pushpeek(&chans[x]->confout); + __dahdi_transmit_chunk(chans[x], data); + if (data) + __buf_push(&chans[x]->confout, NULL, + "conftransmit"); + spin_unlock(&chans[x]->lock); + } + } +#ifdef DAHDI_SYNC_TICK + for (x = 0; x < maxspans; x++) { + struct dahdi_span *const s = spans[x]; + if (s && s->sync_tick) + s->sync_tick(s, s == master); + } +#endif + read_unlock(&chan_lock); + spin_unlock_irqrestore(&bigzaplock, flags); +} + +#ifndef CONFIG_DAHDI_CORE_TIMER + +static void coretimer_init(void) +{ + return; +} + +static void coretimer_cleanup(void) +{ + return; +} + +#else + +static unsigned long core_diff_ms(struct timespec *t0, struct timespec *t1) +{ + long nanosec, sec; + unsigned long ms; + sec = (t1->tv_sec - t0->tv_sec); + nanosec = (t1->tv_nsec - t0->tv_nsec); + while (nanosec >= NSEC_PER_SEC) { + nanosec -= NSEC_PER_SEC; + ++sec; + } + while (nanosec < 0) { + nanosec += NSEC_PER_SEC; + --sec; + } + ms = (sec * 1000) + (nanosec / 1000000L); + return ms; +} + +static void coretimer_func(unsigned long param) +{ + unsigned long ms_since_start; + struct timespec now; + const unsigned long MAX_INTERVAL = 100000L; + const unsigned long FOURMS_INTERVAL = HZ/250; + const unsigned long ONESEC_INTERVAL = HZ; + const unsigned long MS_LIMIT = 3000; + + now = current_kernel_time(); + + if (atomic_read(&core_timer.count) == + atomic_read(&core_timer.last_count)) { + + /* This is the code path if a board driver is not calling + * dahdi_receive, and therefore the core of dahdi needs to + * perform the master span processing itself. */ + + if (!atomic_read(&core_timer.shutdown)) + mod_timer(&core_timer.timer, jiffies + FOURMS_INTERVAL); + + ms_since_start = core_diff_ms(&core_timer.start_interval, &now); + + /* + * If the system time has changed, it is possible for us to be + * far behind. If we are more than MS_LIMIT milliseconds + * behind, just reset our time base and continue so that we do + * not hang the system here. + * + */ + if (unlikely((ms_since_start - atomic_read(&core_timer.count)) > MS_LIMIT)) { + if (printk_ratelimit()) + module_printk(KERN_INFO, "Detected time shift.\n"); + atomic_set(&core_timer.count, 0); + atomic_set(&core_timer.last_count, 0); + core_timer.start_interval = now; + return; + } + + while (ms_since_start > atomic_read(&core_timer.count)) + process_masterspan(); + + if (ms_since_start > MAX_INTERVAL) { + atomic_set(&core_timer.count, 0); + atomic_set(&core_timer.last_count, 0); + core_timer.start_interval = now; + } else { + atomic_set(&core_timer.last_count, + atomic_read(&core_timer.count)); + } + + } else { + + /* It looks like a board driver is calling dahdi_receive. We + * will just check again in a second. */ + atomic_set(&core_timer.count, 0); + atomic_set(&core_timer.last_count, 0); + core_timer.start_interval = now; + if (!atomic_read(&core_timer.shutdown)) + mod_timer(&core_timer.timer, jiffies + ONESEC_INTERVAL); + } +} + +static void coretimer_init(void) +{ + init_timer(&core_timer.timer); + core_timer.timer.function = coretimer_func; + core_timer.start_interval = current_kernel_time(); + core_timer.timer.expires = jiffies + HZ; + atomic_set(&core_timer.count, 0); + atomic_set(&core_timer.shutdown, 0); + add_timer(&core_timer.timer); +} + +static void coretimer_cleanup(void) +{ + atomic_set(&core_timer.shutdown, 1); + del_timer_sync(&core_timer.timer); +} + +#endif /* CONFIG_DAHDI_CORE_TIMER */ + + +int dahdi_receive(struct dahdi_span *span) +{ + int x,y,z; + unsigned long flags; + +#ifdef CONFIG_DAHDI_WATCHDOG + span->watchcounter--; +#endif + for (x=0;xchannels;x++) { + if (span->chans[x]->master == span->chans[x]) { + spin_lock_irqsave(&span->chans[x]->lock, flags); + if (span->chans[x]->nextslave) { + /* Must process each slave at the same time */ + u_char data[DAHDI_CHUNKSIZE]; + int pos = 0; + for (y=0;ychans[z]->readchunk[y]; + if (pos == DAHDI_CHUNKSIZE) { + if(!(span->chans[x]->flags & DAHDI_FLAG_NOSTDTXRX)) + __dahdi_receive_chunk(span->chans[x], data); + pos = 0; + } + z=span->chans[z]->nextslave; + } while(z); + } + } else { + /* Process a normal channel */ + if (!(span->chans[x]->flags & DAHDI_FLAG_NOSTDTXRX)) + __dahdi_real_receive(span->chans[x]); + } + if (span->chans[x]->itimer) { + span->chans[x]->itimer -= DAHDI_CHUNKSIZE; + if (span->chans[x]->itimer <= 0) { + rbs_itimer_expire(span->chans[x]); + } + } + if (span->chans[x]->ringdebtimer) + span->chans[x]->ringdebtimer--; + if (span->chans[x]->sig & __DAHDI_SIG_FXS) { + if (span->chans[x]->rxhooksig == DAHDI_RXSIG_RING) + span->chans[x]->ringtrailer = DAHDI_RINGTRAILER; + else if (span->chans[x]->ringtrailer) { + span->chans[x]->ringtrailer-= DAHDI_CHUNKSIZE; + /* See if RING trailer is expired */ + if (!span->chans[x]->ringtrailer && !span->chans[x]->ringdebtimer) + __qevent(span->chans[x],DAHDI_EVENT_RINGOFFHOOK); + } + } + if (span->chans[x]->pulsetimer) + { + span->chans[x]->pulsetimer--; + if (span->chans[x]->pulsetimer <= 0) + { + if (span->chans[x]->pulsecount) + { + if (span->chans[x]->pulsecount > 12) { + + module_printk(KERN_NOTICE, "Got pulse digit %d on %s???\n", + span->chans[x]->pulsecount, + span->chans[x]->name); + } else if (span->chans[x]->pulsecount > 11) { + __qevent(span->chans[x], DAHDI_EVENT_PULSEDIGIT | '#'); + } else if (span->chans[x]->pulsecount > 10) { + __qevent(span->chans[x], DAHDI_EVENT_PULSEDIGIT | '*'); + } else if (span->chans[x]->map_pulse == MAP_PULSE_NZ_OSLO) { + /* (CNET) NZ/Oslo pluse mapping */ + __qevent(span->chans[x], DAHDI_EVENT_PULSEDIGIT | ('0' + + (10 - span->chans[x]->pulsecount))); + } else if (span->chans[x]->map_pulse == MAP_PULSE_SWEDEN) { + /* (CNET) Swedish pulse mapping */ + __qevent(span->chans[x], DAHDI_EVENT_PULSEDIGIT | ('0' + + (span->chans[x]->pulsecount - 1))); + } else if (span->chans[x]->pulsecount > 9) { + __qevent(span->chans[x], DAHDI_EVENT_PULSEDIGIT | '0'); + } else { + __qevent(span->chans[x], DAHDI_EVENT_PULSEDIGIT | ('0' + + span->chans[x]->pulsecount)); + } + span->chans[x]->pulsecount = 0; + } + } + } +#ifdef BUFFER_DEBUG + span->chans[x]->statcount -= DAHDI_CHUNKSIZE; +#endif + spin_unlock_irqrestore(&span->chans[x]->lock, flags); + } + } + + if (span == master) + process_masterspan(); + + return 0; +} + +MODULE_AUTHOR("Mark Spencer "); +MODULE_DESCRIPTION("DAHDI Telephony Interface"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DAHDI_VERSION); + +module_param(debug, int, 0644); +module_param(deftaps, int, 0644); + +static struct file_operations dahdi_fops = { + .owner = THIS_MODULE, + .llseek = NULL, + .open = dahdi_open, + .release = dahdi_release, + .ioctl = dahdi_ioctl, + .read = dahdi_read, + .write = dahdi_write, + .poll = dahdi_poll, + .mmap = dahdi_mmap, + .flush = NULL, + .fsync = NULL, + .fasync = NULL, +}; + +#ifdef CONFIG_DAHDI_WATCHDOG +static struct timer_list watchdogtimer; + +static void watchdog_check(unsigned long ignored) +{ + int x; + unsigned long flags; + static int wdcheck=0; + + local_irq_save(flags); + for (x=0;xflags & DAHDI_FLAG_RUNNING)) { + if (spans[x]->watchcounter == DAHDI_WATCHDOG_INIT) { + /* Whoops, dead card */ + if ((spans[x]->watchstate == DAHDI_WATCHSTATE_OK) || + (spans[x]->watchstate == DAHDI_WATCHSTATE_UNKNOWN)) { + spans[x]->watchstate = DAHDI_WATCHSTATE_RECOVERING; + if (spans[x]->watchdog) { + module_printk(KERN_NOTICE, "Kicking span %s\n", spans[x]->name); + spans[x]->watchdog(spans[x], DAHDI_WATCHDOG_NOINTS); + } else { + module_printk(KERN_NOTICE, "Span %s is dead with no revival\n", spans[x]->name); + spans[x]->watchstate = DAHDI_WATCHSTATE_FAILED; + } + } + } else { + if ((spans[x]->watchstate != DAHDI_WATCHSTATE_OK) && + (spans[x]->watchstate != DAHDI_WATCHSTATE_UNKNOWN)) + module_printk(KERN_NOTICE, "Span %s is alive!\n", spans[x]->name); + spans[x]->watchstate = DAHDI_WATCHSTATE_OK; + } + spans[x]->watchcounter = DAHDI_WATCHDOG_INIT; + } + } + local_irq_restore(flags); + if (!wdcheck) { + module_printk(KERN_NOTICE, "watchdog on duty!\n"); + wdcheck=1; + } + mod_timer(&watchdogtimer, jiffies + 2); +} + +static int __init watchdog_init(void) +{ + init_timer(&watchdogtimer); + watchdogtimer.expires = 0; + watchdogtimer.data =0; + watchdogtimer.function = watchdog_check; + /* Run every couple of jiffy or so */ + mod_timer(&watchdogtimer, jiffies + 2); + return 0; +} + +static void __exit watchdog_cleanup(void) +{ + del_timer(&watchdogtimer); +} + +#endif + +int dahdi_register_chardev(struct dahdi_chardev *dev) +{ + char udevname[strlen(dev->name) + sizeof("dahdi!")]; + + strcpy(udevname, "dahdi!"); + strcat(udevname, dev->name); + CLASS_DEV_CREATE(dahdi_class, MKDEV(DAHDI_MAJOR, dev->minor), NULL, udevname); + + return 0; +} + +int dahdi_unregister_chardev(struct dahdi_chardev *dev) +{ + CLASS_DEV_DESTROY(dahdi_class, MKDEV(DAHDI_MAJOR, dev->minor)); + + return 0; +} + +static int __init dahdi_init(void) +{ + int res = 0; + +#ifdef CONFIG_PROC_FS + proc_entries[0] = proc_mkdir("dahdi", NULL); +#endif + + if ((res = register_chrdev(DAHDI_MAJOR, "dahdi", &dahdi_fops))) { + module_printk(KERN_ERR, "Unable to register DAHDI character device handler on %d\n", DAHDI_MAJOR); + return res; + } + + dahdi_class = class_create(THIS_MODULE, "dahdi"); + CLASS_DEV_CREATE(dahdi_class, MKDEV(DAHDI_MAJOR, 253), NULL, "dahdi!timer"); + CLASS_DEV_CREATE(dahdi_class, MKDEV(DAHDI_MAJOR, 254), NULL, "dahdi!channel"); + CLASS_DEV_CREATE(dahdi_class, MKDEV(DAHDI_MAJOR, 255), NULL, "dahdi!pseudo"); + CLASS_DEV_CREATE(dahdi_class, MKDEV(DAHDI_MAJOR, 0), NULL, "dahdi!ctl"); + + module_printk(KERN_INFO, "Telephony Interface Registered on major %d\n", DAHDI_MAJOR); + module_printk(KERN_INFO, "Version: %s\n", DAHDI_VERSION); + dahdi_conv_init(); + fasthdlc_precalc(); + rotate_sums(); +#ifdef CONFIG_DAHDI_WATCHDOG + watchdog_init(); +#endif + coretimer_init(); + return res; +} + +static void __exit dahdi_cleanup(void) +{ + int x; + + coretimer_cleanup(); + + CLASS_DEV_DESTROY(dahdi_class, MKDEV(DAHDI_MAJOR, 253)); /* timer */ + CLASS_DEV_DESTROY(dahdi_class, MKDEV(DAHDI_MAJOR, 254)); /* channel */ + CLASS_DEV_DESTROY(dahdi_class, MKDEV(DAHDI_MAJOR, 255)); /* pseudo */ + CLASS_DEV_DESTROY(dahdi_class, MKDEV(DAHDI_MAJOR, 0)); /* ctl */ + class_destroy(dahdi_class); + + unregister_chrdev(DAHDI_MAJOR, "dahdi"); + +#ifdef CONFIG_PROC_FS + remove_proc_entry("dahdi", NULL); +#endif + + module_printk(KERN_INFO, "Telephony Interface Unloaded\n"); + for (x = 0; x < DAHDI_TONE_ZONE_MAX; x++) { + if (tone_zones[x]) + kfree(tone_zones[x]); + } + +#ifdef CONFIG_DAHDI_WATCHDOG + watchdog_cleanup(); +#endif +} + +module_init(dahdi_init); +module_exit(dahdi_cleanup); diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_dummy.c dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_dummy.c --- dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_dummy.c 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_dummy.c 2009-11-09 13:32:45.000000000 -0600 @@ -0,0 +1,282 @@ +/* + * Dummy DAHDI Driver for DAHDI Telephony interface + * + * Required: kernel > 2.6.0 + * + * Written by Robert Pleh + * 2.6 version by Tony Hoyle + * Unified by Mark Spencer + * + * Converted to use HighResTimers on i386 by Jeffery Palmer + * + * Copyright (C) 2002, Hermes Softlab + * Copyright (C) 2004-2009, Digium, Inc. + * + * All rights reserved. + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +/* + * To use the high resolution timers, in your kernel CONFIG_HIGH_RES_TIMERS + * needs to be enabled (Processor type and features -> High Resolution + * Timer Support), and optionally HPET (Processor type and features -> + * HPET Timer Support) provides a better clock source. + */ + +#include + +#if defined(CONFIG_HIGH_RES_TIMERS) && \ + LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +#define USE_HIGHRESTIMER +#endif + +#include +#include +#include +#include +#include +#include + +#if defined(USE_HIGHRESTIMER) +#include +#else +#include +#endif + +#include + +#ifndef HAVE_HRTIMER_ACCESSORS +#if defined(USE_HIGHRESTIMER) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) +/* Compatibility with new hrtimer interface */ +static inline ktime_t hrtimer_get_expires(const struct hrtimer *timer) +{ + return timer->expires; +} + +static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time) +{ + timer->expires = time; +} +#endif +#endif + +struct dahdi_dummy { + struct dahdi_span span; + struct dahdi_chan _chan; + struct dahdi_chan *chan; +#if !defined(USE_HIGHRESTIMER) + unsigned long calls_since_start; + struct timespec start_interval; +#endif +}; + +static struct dahdi_dummy *ztd; + +static int debug = 0; + +#ifdef USE_HIGHRESTIMER +#define CLOCK_SRC "HRtimer" +static struct hrtimer zaptimer; +#define DAHDI_RATE 1000 /* DAHDI ticks per second */ +#define DAHDI_TIME (1000000 / DAHDI_RATE) /* DAHDI tick time in us */ +#define DAHDI_TIME_NS (DAHDI_TIME * 1000) /* DAHDI tick time in ns */ +#else +#define CLOCK_SRC "Linux26" +static struct timer_list timer; +static atomic_t shutdown; +#define JIFFIES_INTERVAL (HZ/250) /* 4ms is fine for dahdi_dummy */ +#endif + +/* Different bits of the debug variable: */ +#define DEBUG_GENERAL (1 << 0) +#define DEBUG_TICKS (1 << 1) + +#if defined(USE_HIGHRESTIMER) +static enum hrtimer_restart dahdi_dummy_hr_int(struct hrtimer *htmr) +{ + unsigned long overrun; + + /* Trigger DAHDI */ + dahdi_receive(&ztd->span); + dahdi_transmit(&ztd->span); + + /* Overrun should always return 1, since we are in the timer that + * expired. + * We should worry if overrun is 2 or more; then we really missed + * a tick */ + overrun = hrtimer_forward(&zaptimer, hrtimer_get_expires(htmr), + ktime_set(0, DAHDI_TIME_NS)); + if(overrun > 1) { + if(printk_ratelimit()) + printk(KERN_NOTICE "dahdi_dummy: HRTimer missed %lu ticks\n", + overrun - 1); + } + + if(debug && DEBUG_TICKS) { + static int count = 0; + /* Printk every 5 seconds, good test to see if timer is + * running properly */ + if (count++ % 5000 == 0) + printk(KERN_DEBUG "dahdi_dummy: 5000 ticks from hrtimer\n"); + } + + /* Always restart the timer */ + return HRTIMER_RESTART; +} +#else +static unsigned long timespec_diff_ms(struct timespec *t0, struct timespec *t1) +{ + long nanosec, sec; + unsigned long ms; + sec = (t1->tv_sec - t0->tv_sec); + nanosec = (t1->tv_nsec - t0->tv_nsec); + while (nanosec >= NSEC_PER_SEC) { + nanosec -= NSEC_PER_SEC; + ++sec; + } + while (nanosec < 0) { + nanosec += NSEC_PER_SEC; + --sec; + } + ms = (sec * 1000) + (nanosec / 1000000L); + return ms; +} + +static void dahdi_dummy_timer(unsigned long param) +{ + unsigned long ms_since_start; + struct timespec now; + const unsigned long MAX_INTERVAL = 100000L; + const unsigned long MS_LIMIT = 3000; + + if (!atomic_read(&shutdown)) + mod_timer(&timer, jiffies + JIFFIES_INTERVAL); + + now = current_kernel_time(); + ms_since_start = timespec_diff_ms(&ztd->start_interval, &now); + + /* + * If the system time has changed, it is possible for us to be far + * behind. If we are more than MS_LIMIT milliseconds behind, just + * reset our time base and continue so that we do not hang the system + * here. + * + */ + if (unlikely((ms_since_start - ztd->calls_since_start) > MS_LIMIT)) { + if (printk_ratelimit()) { + printk(KERN_INFO + "dahdi_dummy: Detected time shift.\n"); + } + ztd->calls_since_start = 0; + ztd->start_interval = now; + return; + } + + while (ms_since_start > ztd->calls_since_start) { + ztd->calls_since_start++; + dahdi_receive(&ztd->span); + dahdi_transmit(&ztd->span); + } + + if (ms_since_start > MAX_INTERVAL) { + ztd->calls_since_start = 0; + ztd->start_interval = now; + } +} +#endif + +static int dahdi_dummy_initialize(struct dahdi_dummy *ztd) +{ + /* DAHDI stuff */ + ztd->chan = &ztd->_chan; + sprintf(ztd->span.name, "DAHDI_DUMMY/1"); + snprintf(ztd->span.desc, sizeof(ztd->span.desc) - 1, "%s (source: " CLOCK_SRC ") %d", ztd->span.name, 1); + sprintf(ztd->chan->name, "DAHDI_DUMMY/%d/%d", 1, 0); + dahdi_copy_string(ztd->span.devicetype, "DAHDI Dummy Timing", sizeof(ztd->span.devicetype)); + ztd->chan->chanpos = 1; + ztd->span.chans = &ztd->chan; + ztd->span.channels = 0; /* no channels on our span */ + ztd->span.deflaw = DAHDI_LAW_MULAW; + init_waitqueue_head(&ztd->span.maintq); + ztd->span.pvt = ztd; + ztd->chan->pvt = ztd; + if (dahdi_register(&ztd->span, 0)) { + return -1; + } + return 0; +} + +int init_module(void) +{ + ztd = kzalloc(sizeof(*ztd), GFP_KERNEL); + if (ztd == NULL) { + printk(KERN_ERR "dahdi_dummy: Unable to allocate memory\n"); + return -ENOMEM; + } + + if (dahdi_dummy_initialize(ztd)) { + printk(KERN_ERR "dahdi_dummy: Unable to intialize DAHDI driver\n"); + kfree(ztd); + return -ENODEV; + } + +#if defined(USE_HIGHRESTIMER) + printk(KERN_DEBUG "dahdi_dummy: Trying to load High Resolution Timer\n"); + hrtimer_init(&zaptimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + printk(KERN_DEBUG "dahdi_dummy: Initialized High Resolution Timer\n"); + + /* Set timer callback function */ + zaptimer.function = dahdi_dummy_hr_int; + + printk(KERN_DEBUG "dahdi_dummy: Starting High Resolution Timer\n"); + hrtimer_start(&zaptimer, ktime_set(0, DAHDI_TIME_NS), HRTIMER_MODE_REL); + printk(KERN_INFO "dahdi_dummy: High Resolution Timer started, good to go\n"); +#else + init_timer(&timer); + timer.function = dahdi_dummy_timer; + ztd->start_interval = current_kernel_time(); + timer.expires = jiffies + JIFFIES_INTERVAL; + atomic_set(&shutdown, 0); + add_timer(&timer); +#endif + + if (debug) + printk(KERN_DEBUG "dahdi_dummy: init() finished\n"); + return 0; +} + + +void cleanup_module(void) +{ +#if defined(USE_HIGHRESTIMER) + /* Stop high resolution timer */ + hrtimer_cancel(&zaptimer); +#else + atomic_set(&shutdown, 1); + del_timer_sync(&timer); +#endif + dahdi_unregister(&ztd->span); + kfree(ztd); + if (debug) + printk(KERN_DEBUG "dahdi_dummy: cleanup() finished\n"); +} + +module_param(debug, int, 0600); + +MODULE_DESCRIPTION("Timing-Only Driver"); +MODULE_AUTHOR("Robert Pleh "); +MODULE_LICENSE("GPL v2"); diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_dynamic.c dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_dynamic.c --- dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_dynamic.c 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_dynamic.c 2009-11-09 13:32:45.000000000 -0600 @@ -0,0 +1,859 @@ +/* + * Dynamic Span Interface for DAHDI + * + * Written by Mark Spencer + * + * Copyright (C) 2001-2008, Digium, Inc. + * + * All rights reserved. + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Tasklets provide better system interactive response at the cost of the + * possibility of losing a frame of data at very infrequent intervals. If + * you are more concerned with the performance of your machine, enable the + * tasklets. If you are strict about absolutely no drops, then do not enable + * tasklets. + */ + +#define ENABLE_TASKLETS + +/* + * Dynamic spans implemented using TDM over X with standard message + * types. Message format is as follows: + * + * Byte #: Meaning + * 0 Number of samples per channel + * 1 Current flags on span + * Bit 0: Yellow Alarm + * Bit 1: Sig bits present + * Bits 2-7: reserved for future use + * 2-3 16-bit counter value for detecting drops, network byte order. + * 4-5 Number of channels in the message, network byte order + * 6... 16-bit words, containing sig bits for each + * four channels, least significant 4 bits being + * the least significant channel, network byte order. + * the rest data for each channel, all samples per channel + before moving to the next. + */ + +/* Arbitrary limit to the max # of channels in a span */ +#define DAHDI_DYNAMIC_MAX_CHANS 256 + +#define ZTD_FLAG_YELLOW_ALARM (1 << 0) +#define ZTD_FLAG_SIGBITS_PRESENT (1 << 1) +#define ZTD_FLAG_LOOPBACK (1 << 2) + +#define ERR_NSAMP (1 << 16) +#define ERR_NCHAN (1 << 17) +#define ERR_LEN (1 << 18) + +EXPORT_SYMBOL(dahdi_dynamic_register); +EXPORT_SYMBOL(dahdi_dynamic_unregister); +EXPORT_SYMBOL(dahdi_dynamic_receive); + +#ifdef ENABLE_TASKLETS +static int taskletrun; +static int taskletsched; +static int taskletpending; +static int taskletexec; +static int txerrors; +static struct tasklet_struct ztd_tlet; + +static void ztd_tasklet(unsigned long data); +#endif + + +static struct dahdi_dynamic { + char addr[40]; + char dname[20]; + int err; + int usecount; + int dead; + long rxjif; + unsigned short txcnt; + unsigned short rxcnt; + struct dahdi_span span; + struct dahdi_chan *chans[DAHDI_DYNAMIC_MAX_CHANS]; + struct dahdi_dynamic *next; + struct dahdi_dynamic_driver *driver; + void *pvt; + int timing; + int master; + unsigned char *msgbuf; +} *dspans; + +static struct dahdi_dynamic_driver *drivers = NULL; + +static int debug = 0; + +static int hasmaster = 0; +#ifdef DEFINE_SPINLOCK +static DEFINE_SPINLOCK(dlock); +#else +static spinlock_t dlock = SPIN_LOCK_UNLOCKED; +#endif + +#ifdef DEFINE_RWLOCK +static DEFINE_RWLOCK(drvlock); +#else +static rwlock_t drvlock = RW_LOCK_UNLOCKED; +#endif + +static void checkmaster(void) +{ + unsigned long flags; + int newhasmaster=0; + int best = 9999999; + struct dahdi_dynamic *z, *master=NULL; + spin_lock_irqsave(&dlock, flags); + z = dspans; + while(z) { + if (z->timing) { + z->master = 0; + if (!(z->span.alarms & DAHDI_ALARM_RED) && + (z->timing < best) && !z->dead) { + /* If not in alarm and they're + a better timing source, use them */ + master = z; + best = z->timing; + newhasmaster = 1; + } + } + z = z->next; + } + hasmaster = newhasmaster; + /* Mark the new master if there is one */ + if (master) + master->master = 1; + spin_unlock_irqrestore(&dlock, flags); + if (master) + printk(KERN_INFO "TDMoX: New master: %s\n", master->span.name); + else + printk(KERN_INFO "TDMoX: No master.\n"); +} + +static void ztd_sendmessage(struct dahdi_dynamic *z) +{ + unsigned char *buf = z->msgbuf; + unsigned short bits; + int msglen = 0; + int x; + int offset; + + /* Byte 0: Number of samples per channel */ + *buf = DAHDI_CHUNKSIZE; + buf++; msglen++; + + /* Byte 1: Flags */ + *buf = 0; + if (z->span.alarms & DAHDI_ALARM_RED) + *buf |= ZTD_FLAG_YELLOW_ALARM; + *buf |= ZTD_FLAG_SIGBITS_PRESENT; + buf++; msglen++; + + /* Bytes 2-3: Transmit counter */ + *((unsigned short *)buf) = htons((unsigned short)z->txcnt); + z->txcnt++; + buf++; msglen++; + buf++; msglen++; + + /* Bytes 4-5: Number of channels */ + *((unsigned short *)buf) = htons((unsigned short)z->span.channels); + buf++; msglen++; + buf++; msglen++; + bits = 0; + offset = 0; + for (x=0;xspan.channels;x++) { + offset = x % 4; + bits |= (z->chans[x]->txsig & 0xf) << (offset << 2); + if (offset == 3) { + /* Write the bits when we have four channels */ + *((unsigned short *)buf) = htons(bits); + buf++; msglen++; + buf++; msglen++; + bits = 0; + } + } + + if (offset != 3) { + /* Finish it off if it's not done already */ + *((unsigned short *)buf) = htons(bits); + buf++; msglen++; + buf++; msglen++; + } + + for (x=0;xspan.channels;x++) { + memcpy(buf, z->chans[x]->writechunk, DAHDI_CHUNKSIZE); + buf += DAHDI_CHUNKSIZE; + msglen += DAHDI_CHUNKSIZE; + } + + z->driver->transmit(z->pvt, z->msgbuf, msglen); + +} + +static void __ztdynamic_run(void) +{ + unsigned long flags; + struct dahdi_dynamic *z; + struct dahdi_dynamic_driver *drv; + int y; + spin_lock_irqsave(&dlock, flags); + z = dspans; + while(z) { + if (!z->dead) { + /* Ignore dead spans */ + for (y=0;yspan.channels;y++) { + /* Echo cancel double buffered data */ + dahdi_ec_chunk(z->span.chans[y], z->span.chans[y]->readchunk, z->span.chans[y]->writechunk); + } + dahdi_receive(&z->span); + dahdi_transmit(&z->span); + /* Handle all transmissions now */ + spin_unlock_irqrestore(&dlock, flags); + ztd_sendmessage(z); + spin_lock_irqsave(&dlock, flags); + } + z = z->next; + } + spin_unlock_irqrestore(&dlock, flags); + + read_lock(&drvlock); + drv = drivers; + while(drv) { + /* Flush any traffic still pending in the driver */ + if (drv->flush) { + drv->flush(); + } + drv = drv->next; + } + read_unlock(&drvlock); +} + +#ifdef ENABLE_TASKLETS +static void ztdynamic_run(void) +{ + if (!taskletpending) { + taskletpending = 1; + taskletsched++; + tasklet_hi_schedule(&ztd_tlet); + } else { + txerrors++; + } +} +#else +#define ztdynamic_run __ztdynamic_run +#endif + +void dahdi_dynamic_receive(struct dahdi_span *span, unsigned char *msg, int msglen) +{ + struct dahdi_dynamic *ztd = span->pvt; + int newerr=0; + unsigned long flags; + int sflags; + int xlen; + int x, bits, sig; + int nchans, master; + int newalarm; + unsigned short rxpos, rxcnt; + + + spin_lock_irqsave(&dlock, flags); + if (msglen < 6) { + spin_unlock_irqrestore(&dlock, flags); + newerr = ERR_LEN; + if (newerr != ztd->err) { + printk(KERN_NOTICE "Span %s: Insufficient samples for header (only %d)\n", span->name, msglen); + } + ztd->err = newerr; + return; + } + + /* First, check the chunksize */ + if (*msg != DAHDI_CHUNKSIZE) { + spin_unlock_irqrestore(&dlock, flags); + newerr = ERR_NSAMP | msg[0]; + if (newerr != ztd->err) { + printk(KERN_NOTICE "Span %s: Expected %d samples, but receiving %d\n", span->name, DAHDI_CHUNKSIZE, msg[0]); + } + ztd->err = newerr; + return; + } + msg++; + sflags = *msg; + msg++; + + rxpos = ntohs(*((unsigned short *)msg)); + msg++; + msg++; + + nchans = ntohs(*((unsigned short *)msg)); + if (nchans != span->channels) { + spin_unlock_irqrestore(&dlock, flags); + newerr = ERR_NCHAN | nchans; + if (newerr != ztd->err) { + printk(KERN_NOTICE "Span %s: Expected %d channels, but receiving %d\n", span->name, span->channels, nchans); + } + ztd->err = newerr; + return; + } + msg++; + msg++; + + /* Okay now we've accepted the header, lets check our message + length... */ + + /* Start with header */ + xlen = 6; + /* Add samples of audio */ + xlen += nchans * DAHDI_CHUNKSIZE; + /* If RBS info is there, add that */ + if (sflags & ZTD_FLAG_SIGBITS_PRESENT) { + /* Account for sigbits -- one short per 4 channels*/ + xlen += ((nchans + 3) / 4) * 2; + } + + if (xlen != msglen) { + spin_unlock_irqrestore(&dlock, flags); + newerr = ERR_LEN | xlen; + if (newerr != ztd->err) { + printk(KERN_NOTICE "Span %s: Expected message size %d, but was %d instead\n", span->name, xlen, msglen); + } + ztd->err = newerr; + return; + } + + bits = 0; + + /* Record sigbits if present */ + if (sflags & ZTD_FLAG_SIGBITS_PRESENT) { + for (x=0;x> ((x % 4) << 2)) & 0xff; + + /* Update signalling if appropriate */ + if (sig != span->chans[x]->rxsig) + dahdi_rbsbits(span->chans[x], sig); + + } + } + + /* Record data for channels */ + for (x=0;xchans[x]->readchunk, msg, DAHDI_CHUNKSIZE); + msg += DAHDI_CHUNKSIZE; + } + + master = ztd->master; + + rxcnt = ztd->rxcnt; + ztd->rxcnt = rxpos+1; + + spin_unlock_irqrestore(&dlock, flags); + + /* Check for Yellow alarm */ + newalarm = span->alarms & ~(DAHDI_ALARM_YELLOW | DAHDI_ALARM_RED); + if (sflags & ZTD_FLAG_YELLOW_ALARM) + newalarm |= DAHDI_ALARM_YELLOW; + + if (newalarm != span->alarms) { + span->alarms = newalarm; + dahdi_alarm_notify(span); + checkmaster(); + } + + /* Keep track of last received packet */ + ztd->rxjif = jiffies; + + /* note if we had a missing packet */ + if (rxpos != rxcnt) + printk(KERN_NOTICE "Span %s: Expected seq no %d, but received %d instead\n", span->name, rxcnt, rxpos); + + /* If this is our master span, then run everything */ + if (master) + ztdynamic_run(); + +} + +static void dynamic_destroy(struct dahdi_dynamic *z) +{ + unsigned int x; + + /* Unregister span if appropriate */ + if (test_bit(DAHDI_FLAGBIT_REGISTERED, &z->span.flags)) + dahdi_unregister(&z->span); + + /* Destroy the pvt stuff if there */ + if (z->pvt) + z->driver->destroy(z->pvt); + + /* Free message buffer if appropriate */ + if (z->msgbuf) + kfree(z->msgbuf); + + /* Free channels */ + for (x = 0; x < z->span.channels; x++) { + kfree(z->chans[x]); + } + + /* Free z */ + kfree(z); + + checkmaster(); +} + +static struct dahdi_dynamic *find_dynamic(struct dahdi_dynamic_span *zds) +{ + struct dahdi_dynamic *z; + z = dspans; + while(z) { + if (!strcmp(z->dname, zds->driver) && + !strcmp(z->addr, zds->addr)) + break; + z = z->next; + } + return z; +} + +static struct dahdi_dynamic_driver *find_driver(char *name) +{ + struct dahdi_dynamic_driver *ztd; + ztd = drivers; + while(ztd) { + /* here's our driver */ + if (!strcmp(name, ztd->name)) + break; + ztd = ztd->next; + } + return ztd; +} + +static int destroy_dynamic(struct dahdi_dynamic_span *zds) +{ + unsigned long flags; + struct dahdi_dynamic *z, *cur, *prev=NULL; + spin_lock_irqsave(&dlock, flags); + z = find_dynamic(zds); + if (!z) { + spin_unlock_irqrestore(&dlock, flags); + return -EINVAL; + } + /* Don't destroy span until it is in use */ + if (z->usecount) { + spin_unlock_irqrestore(&dlock, flags); + printk(KERN_NOTICE "Attempt to destroy dynamic span while it is in use\n"); + return -EBUSY; + } + /* Unlink it */ + cur = dspans; + while(cur) { + if (cur == z) { + if (prev) + prev->next = z->next; + else + dspans = z->next; + break; + } + prev = cur; + cur = cur->next; + } + spin_unlock_irqrestore(&dlock, flags); + + /* Destroy it */ + dynamic_destroy(z); + + return 0; +} + +static int ztd_rbsbits(struct dahdi_chan *chan, int bits) +{ + /* Don't have to do anything */ + return 0; +} + +static int ztd_open(struct dahdi_chan *chan) +{ + struct dahdi_dynamic *z; + z = chan->span->pvt; + if (z) { + if (z->dead) + return -ENODEV; + z->usecount++; + } + if(!try_module_get(THIS_MODULE)) + printk(KERN_NOTICE "TDMoX: Unable to increment module use count\n"); + return 0; +} + +static int ztd_chanconfig(struct dahdi_chan *chan, int sigtype) +{ + return 0; +} + +static int ztd_close(struct dahdi_chan *chan) +{ + struct dahdi_dynamic *z; + z = chan->span->pvt; + if (z) + z->usecount--; + if (z->dead && !z->usecount) + dynamic_destroy(z); + module_put(THIS_MODULE); + return 0; +} + +static int create_dynamic(struct dahdi_dynamic_span *zds) +{ + struct dahdi_dynamic *z; + struct dahdi_dynamic_driver *ztd; + unsigned long flags; + int x; + int bufsize; + + if (zds->numchans < 1) { + printk(KERN_NOTICE "Can't be less than 1 channel (%d)!\n", zds->numchans); + return -EINVAL; + } + if (zds->numchans >= DAHDI_DYNAMIC_MAX_CHANS) { + printk(KERN_NOTICE "Can't create dynamic span with greater than %d channels. See ztdynamic.c and increase DAHDI_DYNAMIC_MAX_CHANS\n", zds->numchans); + return -EINVAL; + } + + spin_lock_irqsave(&dlock, flags); + z = find_dynamic(zds); + spin_unlock_irqrestore(&dlock, flags); + if (z) + return -EEXIST; + + /* XXX There is a silly race here. We check it doesn't exist, but + someone could create it between now and then and we'd end up + with two of them. We don't want to hold the spinlock + for *too* long though, especially not if there is a possibility + of kmalloc. XXX */ + + + /* Allocate memory */ + if (!(z = kmalloc(sizeof(*z), GFP_KERNEL))) { + return -ENOMEM; + } + + /* Zero it out */ + memset(z, 0, sizeof(*z)); + + for (x = 0; x < zds->numchans; x++) { + if (!(z->chans[x] = kmalloc(sizeof(*z->chans[x]), GFP_KERNEL))) { + dynamic_destroy(z); + return -ENOMEM; + } + + memset(z->chans[x], 0, sizeof(*z->chans[x])); + } + + /* Allocate message buffer with sample space and header space */ + bufsize = zds->numchans * DAHDI_CHUNKSIZE + zds->numchans / 4 + 48; + + z->msgbuf = kmalloc(bufsize, GFP_KERNEL); + + if (!z->msgbuf) { + dynamic_destroy(z); + return -ENOMEM; + } + + /* Zero out -- probably not needed but why not */ + memset(z->msgbuf, 0, bufsize); + + /* Setup parameters properly assuming we're going to be okay. */ + dahdi_copy_string(z->dname, zds->driver, sizeof(z->dname)); + dahdi_copy_string(z->addr, zds->addr, sizeof(z->addr)); + z->timing = zds->timing; + sprintf(z->span.name, "DYN/%s/%s", zds->driver, zds->addr); + sprintf(z->span.desc, "Dynamic '%s' span at '%s'", zds->driver, zds->addr); + z->span.channels = zds->numchans; + z->span.pvt = z; + z->span.deflaw = DAHDI_LAW_MULAW; + z->span.flags |= DAHDI_FLAG_RBS; + z->span.chans = z->chans; + z->span.rbsbits = ztd_rbsbits; + z->span.open = ztd_open; + z->span.close = ztd_close; + z->span.chanconfig = ztd_chanconfig; + for (x=0; x < z->span.channels; x++) { + sprintf(z->chans[x]->name, "DYN/%s/%s/%d", zds->driver, zds->addr, x+1); + z->chans[x]->sigcap = DAHDI_SIG_EM | DAHDI_SIG_CLEAR | DAHDI_SIG_FXSLS | + DAHDI_SIG_FXSKS | DAHDI_SIG_FXSGS | DAHDI_SIG_FXOLS | + DAHDI_SIG_FXOKS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | + DAHDI_SIG_DACS_RBS | DAHDI_SIG_CAS; + z->chans[x]->chanpos = x + 1; + z->chans[x]->pvt = z; + } + + spin_lock_irqsave(&dlock, flags); + ztd = find_driver(zds->driver); + if (!ztd) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70) + char fn[80]; +#endif + + spin_unlock_irqrestore(&dlock, flags); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,70) + request_module("dahdi_dynamic_%s", zds->driver); +#else + sprintf(fn, "dahdi_dynamic_%s", zds->driver); + request_module(fn); +#endif + spin_lock_irqsave(&dlock, flags); + ztd = find_driver(zds->driver); + } + spin_unlock_irqrestore(&dlock, flags); + + + /* Another race -- should let the module get unloaded while we + have it here */ + if (!ztd) { + printk(KERN_NOTICE "No such driver '%s' for dynamic span\n", zds->driver); + dynamic_destroy(z); + return -EINVAL; + } + + /* Create the stuff */ + z->pvt = ztd->create(&z->span, z->addr); + if (!z->pvt) { + printk(KERN_NOTICE "Driver '%s' (%s) rejected address '%s'\n", ztd->name, ztd->desc, z->addr); + /* Creation failed */ + return -EINVAL; + } + + /* Remember the driver */ + z->driver = ztd; + + /* Whee! We're created. Now register the span */ + if (dahdi_register(&z->span, 0)) { + printk(KERN_NOTICE "Unable to register span '%s'\n", z->span.name); + dynamic_destroy(z); + return -EINVAL; + } + + /* Okay, created and registered. add it to the list */ + spin_lock_irqsave(&dlock, flags); + z->next = dspans; + dspans = z; + spin_unlock_irqrestore(&dlock, flags); + + checkmaster(); + + /* All done */ + return z->span.spanno; + +} + +#ifdef ENABLE_TASKLETS +static void ztd_tasklet(unsigned long data) +{ + taskletrun++; + if (taskletpending) { + taskletexec++; + __ztdynamic_run(); + } + taskletpending = 0; +} +#endif + +static int ztdynamic_ioctl(unsigned int cmd, unsigned long data) +{ + struct dahdi_dynamic_span zds; + int res; + switch(cmd) { + case 0: + /* This is called just before rotation. If none of our + spans are pulling timing, then now is the time to process + them */ + if (!hasmaster) + ztdynamic_run(); + return 0; + case DAHDI_DYNAMIC_CREATE: + if (copy_from_user(&zds, (__user const void *) data, sizeof(zds))) + return -EFAULT; + if (debug) + printk(KERN_DEBUG "Dynamic Create\n"); + res = create_dynamic(&zds); + if (res < 0) + return res; + zds.spanno = res; + /* Let them know the new span number */ + if (copy_to_user((__user void *) data, &zds, sizeof(zds))) + return -EFAULT; + return 0; + case DAHDI_DYNAMIC_DESTROY: + if (copy_from_user(&zds, (__user const void *) data, sizeof(zds))) + return -EFAULT; + if (debug) + printk(KERN_DEBUG "Dynamic Destroy\n"); + return destroy_dynamic(&zds); + } + + return -ENOTTY; +} + +int dahdi_dynamic_register(struct dahdi_dynamic_driver *dri) +{ + unsigned long flags; + int res = 0; + write_lock_irqsave(&drvlock, flags); + if (find_driver(dri->name)) + res = -1; + else { + dri->next = drivers; + drivers = dri; + } + write_unlock_irqrestore(&drvlock, flags); + return res; +} + +void dahdi_dynamic_unregister(struct dahdi_dynamic_driver *dri) +{ + struct dahdi_dynamic_driver *cur, *prev=NULL; + struct dahdi_dynamic *z, *zp, *zn; + unsigned long flags; + write_lock_irqsave(&drvlock, flags); + cur = drivers; + while(cur) { + if (cur == dri) { + if (prev) + prev->next = cur->next; + else + drivers = cur->next; + break; + } + prev = cur; + cur = cur->next; + } + write_unlock_irqrestore(&drvlock, flags); + spin_lock_irqsave(&dlock, flags); + z = dspans; + zp = NULL; + while(z) { + zn = z->next; + if (z->driver == dri) { + /* Unlink */ + if (zp) + zp->next = z->next; + else + dspans = z->next; + if (!z->usecount) + dynamic_destroy(z); + else + z->dead = 1; + } else { + zp = z; + } + z = zn; + } + spin_unlock_irqrestore(&dlock, flags); +} + +static struct timer_list alarmcheck; + +static void check_for_red_alarm(unsigned long ignored) +{ + unsigned long flags; + int newalarm; + int alarmchanged = 0; + struct dahdi_dynamic *z; + spin_lock_irqsave(&dlock, flags); + z = dspans; + while(z) { + newalarm = z->span.alarms & ~DAHDI_ALARM_RED; + /* If nothing received for a second, consider that RED ALARM */ + if ((jiffies - z->rxjif) > 1 * HZ) { + newalarm |= DAHDI_ALARM_RED; + if (z->span.alarms != newalarm) { + z->span.alarms = newalarm; + dahdi_alarm_notify(&z->span); + alarmchanged++; + } + } + z = z->next; + } + spin_unlock_irqrestore(&dlock, flags); + if (alarmchanged) + checkmaster(); + + /* Do the next one */ + mod_timer(&alarmcheck, jiffies + 1 * HZ); + +} + +static int ztdynamic_init(void) +{ + dahdi_set_dynamic_ioctl(ztdynamic_ioctl); + /* Start process to check for RED ALARM */ + init_timer(&alarmcheck); + alarmcheck.expires = 0; + alarmcheck.data = 0; + alarmcheck.function = check_for_red_alarm; + /* Check once per second */ + mod_timer(&alarmcheck, jiffies + 1 * HZ); +#ifdef ENABLE_TASKLETS + tasklet_init(&ztd_tlet, ztd_tasklet, 0); +#endif + printk(KERN_INFO "DAHDI Dynamic Span support LOADED\n"); + return 0; +} + +static void ztdynamic_cleanup(void) +{ +#ifdef ENABLE_TASKLETS + if (taskletpending) { + tasklet_disable(&ztd_tlet); + tasklet_kill(&ztd_tlet); + } +#endif + dahdi_set_dynamic_ioctl(NULL); + del_timer(&alarmcheck); + printk(KERN_INFO "DAHDI Dynamic Span support unloaded\n"); +} + +module_param(debug, int, 0600); + +MODULE_DESCRIPTION("DAHDI Dynamic Span Support"); +MODULE_AUTHOR("Mark Spencer "); +MODULE_LICENSE("GPL v2"); + +module_init(ztdynamic_init); +module_exit(ztdynamic_cleanup); diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_dynamic_eth.c dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_dynamic_eth.c --- dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_dynamic_eth.c 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_dynamic_eth.c 2008-10-28 16:49:02.000000000 -0500 @@ -0,0 +1,438 @@ +/* + * Dynamic Span Interface for DAHDI (Ethernet Interface) + * + * Written by Mark Spencer + * + * Copyright (C) 2001-2008, Digium, Inc. + * + * All rights reserved. + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ETH_P_DAHDI_DETH 0xd00d + +struct ztdeth_header { + unsigned short subaddr; +}; + +/* We take the raw message, put it in an ethernet frame, and add a + two byte addressing header at the top for future use */ +#ifdef DEFINE_SPINLOCK +static DEFINE_SPINLOCK(zlock); +#else +static spinlock_t zlock = SPIN_LOCK_UNLOCKED; +#endif + +static struct sk_buff_head skbs; + +static struct ztdeth { + unsigned char addr[ETH_ALEN]; + unsigned short subaddr; /* Network byte order */ + struct dahdi_span *span; + char ethdev[IFNAMSIZ]; + struct net_device *dev; + struct ztdeth *next; +} *zdevs = NULL; + +static struct dahdi_span *ztdeth_getspan(unsigned char *addr, unsigned short subaddr) +{ + unsigned long flags; + struct ztdeth *z; + struct dahdi_span *span = NULL; + spin_lock_irqsave(&zlock, flags); + z = zdevs; + while(z) { + if (!memcmp(addr, z->addr, ETH_ALEN) && + z->subaddr == subaddr) + break; + z = z->next; + } + if (z) + span = z->span; + spin_unlock_irqrestore(&zlock, flags); + return span; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) +static int ztdeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) +#else +static int ztdeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) +#endif +{ + struct dahdi_span *span; + struct ztdeth_header *zh; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + zh = (struct ztdeth_header *)skb_network_header(skb); +#else + zh = (struct ztdeth_header *)skb->nh.raw; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9) + span = ztdeth_getspan(eth_hdr(skb)->h_source, zh->subaddr); +#else + span = ztdeth_getspan(skb->mac.ethernet->h_source, zh->subaddr); +#endif + if (span) { + skb_pull(skb, sizeof(struct ztdeth_header)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) + skb_linearize(skb); +#else + skb_linearize(skb, GFP_KERNEL); +#endif + dahdi_dynamic_receive(span, (unsigned char *)skb->data, skb->len); + } + kfree_skb(skb); + return 0; +} + +static int ztdeth_notifier(struct notifier_block *block, unsigned long event, void *ptr) +{ + struct net_device *dev = ptr; + struct ztdeth *z; + unsigned long flags; + switch(event) { + case NETDEV_GOING_DOWN: + case NETDEV_DOWN: + spin_lock_irqsave(&zlock, flags); + z = zdevs; + while(z) { + /* Note that the device no longer exists */ + if (z->dev == dev) + z->dev = NULL; + z = z->next; + } + spin_unlock_irqrestore(&zlock, flags); + break; + case NETDEV_UP: + spin_lock_irqsave(&zlock, flags); + z = zdevs; + while(z) { + /* Now that the device exists again, use it */ + if (!strcmp(z->ethdev, dev->name)) + z->dev = dev; + z = z->next; + } + spin_unlock_irqrestore(&zlock, flags); + break; + } + return 0; +} + +static int ztdeth_transmit(void *pvt, unsigned char *msg, int msglen) +{ + struct ztdeth *z; + struct sk_buff *skb; + struct ztdeth_header *zh; + unsigned long flags; + struct net_device *dev; + unsigned char addr[ETH_ALEN]; + unsigned short subaddr; /* Network byte order */ + + spin_lock_irqsave(&zlock, flags); + z = pvt; + if (z->dev) { + /* Copy fields to local variables to remove spinlock ASAP */ + dev = z->dev; + memcpy(addr, z->addr, sizeof(z->addr)); + subaddr = z->subaddr; + spin_unlock_irqrestore(&zlock, flags); + skb = dev_alloc_skb(msglen + dev->hard_header_len + sizeof(struct ztdeth_header) + 32); + if (skb) { + /* Reserve header space */ + skb_reserve(skb, dev->hard_header_len + sizeof(struct ztdeth_header)); + + /* Copy message body */ + memcpy(skb_put(skb, msglen), msg, msglen); + + /* Throw on header */ + zh = (struct ztdeth_header *)skb_push(skb, sizeof(struct ztdeth_header)); + zh->subaddr = subaddr; + + /* Setup protocol and such */ + skb->protocol = __constant_htons(ETH_P_DAHDI_DETH); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + skb_set_network_header(skb, 0); +#else + skb->nh.raw = skb->data; +#endif + skb->dev = dev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + dev_hard_header(skb, dev, ETH_P_DAHDI_DETH, addr, dev->dev_addr, skb->len); +#else + if (dev->hard_header) + dev->hard_header(skb, dev, ETH_P_DAHDI_DETH, addr, dev->dev_addr, skb->len); +#endif + skb_queue_tail(&skbs, skb); + } + } + else + spin_unlock_irqrestore(&zlock, flags); + return 0; +} + + +static int ztdeth_flush(void) +{ + struct sk_buff *skb; + + /* Handle all transmissions now */ + while ((skb = skb_dequeue(&skbs))) { + dev_queue_xmit(skb); + } + return 0; +} + +static struct packet_type ztdeth_ptype = { + .type = __constant_htons(ETH_P_DAHDI_DETH), /* Protocol */ + .dev = NULL, /* Device (NULL = wildcard) */ + .func = ztdeth_rcv, /* Receiver */ +}; + +static int digit2int(char d) +{ + switch(d) { + case 'F': + case 'E': + case 'D': + case 'C': + case 'B': + case 'A': + return d - 'A' + 10; + case 'f': + case 'e': + case 'd': + case 'c': + case 'b': + case 'a': + return d - 'a' + 10; + case '9': + case '8': + case '7': + case '6': + case '5': + case '4': + case '3': + case '2': + case '1': + case '0': + return d - '0'; + } + return -1; +} + +static int hex2int(char *s) +{ + int res; + int tmp; + /* Gotta be at least one digit */ + if (strlen(s) < 1) + return -1; + /* Can't be more than two */ + if (strlen(s) > 2) + return -1; + /* Grab the first digit */ + res = digit2int(s[0]); + if (res < 0) + return -1; + tmp = res; + /* Grab the next */ + if (strlen(s) > 1) { + res = digit2int(s[1]); + if (res < 0) + return -1; + tmp = tmp * 16 + res; + } + return tmp; +} + +static void ztdeth_destroy(void *pvt) +{ + struct ztdeth *z = pvt; + unsigned long flags; + struct ztdeth *prev=NULL, *cur; + spin_lock_irqsave(&zlock, flags); + cur = zdevs; + while(cur) { + if (cur == z) { + if (prev) + prev->next = cur->next; + else + zdevs = cur->next; + break; + } + prev = cur; + cur = cur->next; + } + spin_unlock_irqrestore(&zlock, flags); + if (cur == z) { /* Successfully removed */ + printk(KERN_INFO "TDMoE: Removed interface for %s\n", z->span->name); + kfree(z); + module_put(THIS_MODULE); + } +} + +static void *ztdeth_create(struct dahdi_span *span, char *addr) +{ + struct ztdeth *z; + char src[256]; + char tmp[256], *tmp2, *tmp3, *tmp4 = NULL; + int res,x; + unsigned long flags; + + z = kmalloc(sizeof(struct ztdeth), GFP_KERNEL); + if (z) { + /* Zero it out */ + memset(z, 0, sizeof(struct ztdeth)); + + /* Address should be /[/subaddr] */ + dahdi_copy_string(tmp, addr, sizeof(tmp)); + tmp2 = strchr(tmp, '/'); + if (tmp2) { + *tmp2 = '\0'; + tmp2++; + dahdi_copy_string(z->ethdev, tmp, sizeof(z->ethdev)); + } else { + printk(KERN_NOTICE "Invalid TDMoE address (no device) '%s'\n", addr); + kfree(z); + return NULL; + } + if (tmp2) { + tmp4 = strchr(tmp2+1, '/'); + if (tmp4) { + *tmp4 = '\0'; + tmp4++; + } + /* We don't have SSCANF :( Gotta do this the hard way */ + tmp3 = strchr(tmp2, ':'); + for (x=0;x<6;x++) { + if (tmp2) { + if (tmp3) { + *tmp3 = '\0'; + tmp3++; + } + res = hex2int(tmp2); + if (res < 0) + break; + z->addr[x] = res & 0xff; + } else + break; + if ((tmp2 = tmp3)) + tmp3 = strchr(tmp2, ':'); + } + if (x != 6) { + printk(KERN_NOTICE "TDMoE: Invalid MAC address in: %s\n", addr); + kfree(z); + return NULL; + } + } else { + printk(KERN_NOTICE "TDMoE: Missing MAC address\n"); + kfree(z); + return NULL; + } + if (tmp4) { + int sub = 0; + int mul = 1; + + /* We have a subaddr */ + tmp3 = tmp4 + strlen (tmp4) - 1; + while (tmp3 >= tmp4) { + if (*tmp3 >= '0' && *tmp3 <= '9') { + sub += (*tmp3 - '0') * mul; + } else { + printk(KERN_NOTICE "TDMoE: Invalid subaddress\n"); + kfree(z); + return NULL; + } + mul *= 10; + tmp3--; + } + z->subaddr = htons(sub); + } + z->dev = dev_get_by_name( +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + &init_net, +#endif + z->ethdev); + if (!z->dev) { + printk(KERN_NOTICE "TDMoE: Invalid device '%s'\n", z->ethdev); + kfree(z); + return NULL; + } + z->span = span; + src[0] ='\0'; + for (x=0;x<5;x++) + sprintf(src + strlen(src), "%02x:", z->dev->dev_addr[x]); + sprintf(src + strlen(src), "%02x", z->dev->dev_addr[5]); + printk(KERN_INFO "TDMoE: Added new interface for %s at %s (addr=%s, src=%s, subaddr=%d)\n", span->name, z->dev->name, addr, src, ntohs(z->subaddr)); + + spin_lock_irqsave(&zlock, flags); + z->next = zdevs; + zdevs = z; + spin_unlock_irqrestore(&zlock, flags); + if(!try_module_get(THIS_MODULE)) + printk(KERN_DEBUG "TDMoE: Unable to increment module use count\n"); + } + return z; +} + +static struct dahdi_dynamic_driver ztd_eth = { + "eth", + "Ethernet", + ztdeth_create, + ztdeth_destroy, + ztdeth_transmit, + ztdeth_flush +}; + +static struct notifier_block ztdeth_nblock = { + .notifier_call = ztdeth_notifier, +}; + +static int __init ztdeth_init(void) +{ + dev_add_pack(&ztdeth_ptype); + register_netdevice_notifier(&ztdeth_nblock); + dahdi_dynamic_register(&ztd_eth); + + skb_queue_head_init(&skbs); + + return 0; +} + +static void __exit ztdeth_exit(void) +{ + dev_remove_pack(&ztdeth_ptype); + unregister_netdevice_notifier(&ztdeth_nblock); + dahdi_dynamic_unregister(&ztd_eth); +} + +MODULE_DESCRIPTION("DAHDI Dynamic TDMoE Support"); +MODULE_AUTHOR("Mark Spencer "); +MODULE_LICENSE("GPL v2"); + +module_init(ztdeth_init); +module_exit(ztdeth_exit); diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_dynamic_loc.c dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_dynamic_loc.c --- dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_dynamic_loc.c 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_dynamic_loc.c 2009-03-18 13:48:41.000000000 -0500 @@ -0,0 +1,265 @@ +/* + * Dynamic Span Interface for DAHDI (Local Interface) + * + * Written by Nicolas Bougues + * + * Copyright (C) 2004, Axialys Interactive + * + * All rights reserved. + * + * Note : a DAHDI timing source must exist prior to loading this driver + * + * Address syntax : + * :[:] + * + * As of now, keys and ids are single digit only + * + * One span may have up to one "normal" peer, and one "monitor" peer + * + * Example : + * + * Say you have two spans cross connected, a third one monitoring RX on the + * first one, a fourth one monitoring RX on the second one + * + * 1:0 + * 1:1 + * 1:2:0 + * 1:3:1 + * + * Contrary to TDMoE, no frame loss can occur. + * + * See bug #2021 for more details + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef DEFINE_SPINLOCK +static DEFINE_SPINLOCK(zlock); +#else +static spinlock_t zlock = SPIN_LOCK_UNLOCKED; +#endif + +static struct ztdlocal { + unsigned short key; + unsigned short id; + struct ztdlocal *monitor_rx_peer; /* Indicates the peer span that monitors this span */ + struct ztdlocal *peer; /* Indicates the rw peer for this span */ + struct dahdi_span *span; + struct ztdlocal *next; +} *zdevs = NULL; + +static int ztdlocal_transmit(void *pvt, unsigned char *msg, int msglen) +{ + struct ztdlocal *z; + unsigned long flags; + + spin_lock_irqsave(&zlock, flags); + z = pvt; + if (z->peer && z->peer->span) { + dahdi_dynamic_receive(z->peer->span, msg, msglen); + } + if (z->monitor_rx_peer && z->monitor_rx_peer->span) { + dahdi_dynamic_receive(z->monitor_rx_peer->span, msg, msglen); + } + spin_unlock_irqrestore(&zlock, flags); + return 0; +} + +static int digit2int(char d) +{ + switch(d) { + case 'F': + case 'E': + case 'D': + case 'C': + case 'B': + case 'A': + return d - 'A' + 10; + case 'f': + case 'e': + case 'd': + case 'c': + case 'b': + case 'a': + return d - 'a' + 10; + case '9': + case '8': + case '7': + case '6': + case '5': + case '4': + case '3': + case '2': + case '1': + case '0': + return d - '0'; + } + return -1; +} + +static void ztdlocal_destroy(void *pvt) +{ + struct ztdlocal *z = pvt; + unsigned long flags; + struct ztdlocal *prev=NULL, *cur; + + spin_lock_irqsave(&zlock, flags); + cur = zdevs; + while(cur) { + if (cur->peer == z) + cur->peer = NULL; + if (cur->monitor_rx_peer == z) + cur->monitor_rx_peer = NULL; + cur = cur->next; + } + cur = zdevs; + while(cur) { + if (cur == z) { + if (prev) + prev->next = cur->next; + else + zdevs = cur->next; + break; + } + prev = cur; + cur = cur->next; + } + spin_unlock_irqrestore(&zlock, flags); + if (cur == z) { + printk(KERN_INFO "TDMoL: Removed interface for %s, key %d id %d\n", z->span->name, z->key, z->id); + module_put(THIS_MODULE); + kfree(z); + } +} + +static void *ztdlocal_create(struct dahdi_span *span, char *address) +{ + struct ztdlocal *z, *l; + unsigned long flags; + int key = -1, id = -1, monitor = -1; + + if (strlen(address) >= 3) { + if (address[1] != ':') + goto INVALID_ADDRESS; + key = digit2int(address[0]); + id = digit2int(address[2]); + } + if (strlen (address) == 5) { + if (address[3] != ':') + goto INVALID_ADDRESS; + monitor = digit2int(address[4]); + } + + if (key == -1 || id == -1) + goto INVALID_ADDRESS; + + z = kmalloc(sizeof(struct ztdlocal), GFP_KERNEL); + if (z) { + /* Zero it out */ + memset(z, 0, sizeof(struct ztdlocal)); + + z->key = key; + z->id = id; + z->span = span; + + spin_lock_irqsave(&zlock, flags); + /* Add this peer to any existing spans with same key + And add them as peers to this one */ + for (l = zdevs; l; l = l->next) + if (l->key == z->key) { + if (l->id == z->id) { + printk(KERN_DEBUG "TDMoL: Duplicate id (%d) for key %d\n", z->id, z->key); + goto CLEAR_AND_DEL_FROM_PEERS; + } + if (monitor == -1) { + if (l->peer) { + printk(KERN_DEBUG "TDMoL: Span with key %d and id %d already has a R/W peer\n", z->key, z->id); + goto CLEAR_AND_DEL_FROM_PEERS; + } else { + l->peer = z; + z->peer = l; + } + } + if (monitor == l->id) { + if (l->monitor_rx_peer) { + printk(KERN_DEBUG "TDMoL: Span with key %d and id %d already has a monitoring peer\n", z->key, z->id); + goto CLEAR_AND_DEL_FROM_PEERS; + } else { + l->monitor_rx_peer = z; + } + } + } + z->next = zdevs; + zdevs = z; + spin_unlock_irqrestore(&zlock, flags); + if(!try_module_get(THIS_MODULE)) + printk(KERN_DEBUG "TDMoL: Unable to increment module use count\n"); + + printk(KERN_INFO "TDMoL: Added new interface for %s, key %d id %d\n", span->name, z->key, z->id); + } + return z; + +CLEAR_AND_DEL_FROM_PEERS: + for (l = zdevs; l; l = l->next) { + if (l->peer == z) + l->peer = NULL; + if (l->monitor_rx_peer == z) + l->monitor_rx_peer = NULL; + } + kfree (z); + spin_unlock_irqrestore(&zlock, flags); + return NULL; + +INVALID_ADDRESS: + printk (KERN_NOTICE "TDMoL: Invalid address %s\n", address); + return NULL; +} + +static struct dahdi_dynamic_driver ztd_local = { + "loc", + "Local", + ztdlocal_create, + ztdlocal_destroy, + ztdlocal_transmit, + NULL /* flush */ +}; + +static int __init ztdlocal_init(void) +{ + dahdi_dynamic_register(&ztd_local); + return 0; +} + +static void __exit ztdlocal_exit(void) +{ + dahdi_dynamic_unregister(&ztd_local); +} + +module_init(ztdlocal_init); +module_exit(ztdlocal_exit); + +MODULE_LICENSE("GPL v2"); diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_jpah.c dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_jpah.c --- dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_jpah.c 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_jpah.c 2009-04-29 13:24:04.000000000 -0500 @@ -0,0 +1,144 @@ +/* + * ECHO_CAN_JPAH + * + * by Jason Parker + * + * Based upon mg2ec.h - sort of. + * This "echo can" will completely hose your audio. + * Don't use it unless you're absolutely sure you know what you're doing. + * + * Copyright (C) 2007-2008, Digium, Inc. + * + * All rights reserved. + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include + +static int debug; + +#define module_printk(level, fmt, args...) printk(level "%s: " fmt, THIS_MODULE->name, ## args) +#define debug_printk(level, fmt, args...) if (debug >= level) printk("%s (%s): " fmt, THIS_MODULE->name, __FUNCTION__, ## args) + +static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, + struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec); +static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec); +static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size); +static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val); + +static const struct dahdi_echocan_factory my_factory = { + .name = "JPAH", + .owner = THIS_MODULE, + .echocan_create = echo_can_create, +}; + +static const struct dahdi_echocan_ops my_ops = { + .name = "JPAH", + .echocan_free = echo_can_free, + .echocan_process = echo_can_process, + .echocan_traintap = echo_can_traintap, +}; + +struct ec_pvt { + struct dahdi_echocan_state dahdi; + int blah; +}; + +#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi) + +static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, + struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec) +{ + struct ec_pvt *pvt; + + if (ecp->param_count > 0) { + printk(KERN_WARNING "JPAH does not support parameters; failing request\n"); + return -EINVAL; + } + + pvt = kzalloc(sizeof(*pvt), GFP_KERNEL); + if (!pvt) + return -ENOMEM; + + pvt->dahdi.ops = &my_ops; + + *ec = &pvt->dahdi; + return 0; +} + +static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + + kfree(pvt); +} + +static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + u32 x; + + for (x = 0; x < size; x++) { + if (pvt->blah < 2) { + pvt->blah++; + + *isig++ = 0; + } else { + pvt->blah = 0; + + isig++; + } + } +} + +static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val) +{ + return 0; +} + +static int __init mod_init(void) +{ + if (dahdi_register_echocan_factory(&my_factory)) { + module_printk(KERN_ERR, "could not register with DAHDI core\n"); + + return -EPERM; + } + + module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", my_factory.name); + + return 0; +} + +static void __exit mod_exit(void) +{ + dahdi_unregister_echocan_factory(&my_factory); +} + +module_param(debug, int, S_IRUGO | S_IWUSR); + +MODULE_DESCRIPTION("DAHDI Jason Parker Audio Hoser"); +MODULE_AUTHOR("Jason Parker "); +MODULE_LICENSE("GPL v2"); + +module_init(mod_init); +module_exit(mod_exit); diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_kb1.c dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_kb1.c --- dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_kb1.c 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_kb1.c 2009-04-29 13:24:04.000000000 -0500 @@ -0,0 +1,744 @@ +/* + * ECHO_CAN_KB1 + * + * by Kris Boutilier + * + * Based upon mec2.h + * + * Copyright (C) 2002, Digium, Inc. + * + * Additional background on the techniques used in this code can be found in: + * + * Messerschmitt, David; Hedberg, David; Cole, Christopher; Haoui, Amine; + * Winship, Peter; "Digital Voice Echo Canceller with a TMS32020," + * in Digital Signal Processing Applications with the TMS320 Family, + * pp. 415-437, Texas Instruments, Inc., 1986. + * + * A pdf of which is available by searching on the document title at http://www.ti.com/ + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +static int debug; +static int aggressive; + +#define module_printk(level, fmt, args...) printk(level "%s: " fmt, THIS_MODULE->name, ## args) +#define debug_printk(level, fmt, args...) if (debug >= level) printk(KERN_DEBUG "%s (%s): " fmt, THIS_MODULE->name, __FUNCTION__, ## args) + +/* Uncomment to provide summary statistics for overall echo can performance every 4000 samples */ +/* #define MEC2_STATS 4000 */ + +/* Uncomment to generate per-sample statistics - this will severely degrade system performance and audio quality */ +/* #define MEC2_STATS_DETAILED */ + +/* Get optimized routines for math */ +#include "arith.h" + +/* + Important constants for tuning kb1 echo can + */ + +/* Convergence (aka. adaptation) speed -- higher means slower */ +#define DEFAULT_BETA1_I 2048 + +/* Constants for various power computations */ +#define DEFAULT_SIGMA_LY_I 7 +#define DEFAULT_SIGMA_LU_I 7 +#define DEFAULT_ALPHA_ST_I 5 /* near-end speech detection sensitivity factor */ +#define DEFAULT_ALPHA_YT_I 5 + +#define DEFAULT_CUTOFF_I 128 + +/* Define the near-end speech hangover counter: if near-end speech + * is declared, hcntr is set equal to hangt (see pg. 432) + */ +#define DEFAULT_HANGT 600 /* in samples, so 600 samples = 75ms */ + +/* define the residual error suppression threshold */ +#define DEFAULT_SUPPR_I 16 /* 16 = -24db */ + +/* This is the minimum reference signal power estimate level + * that will result in filter adaptation. + * If this is too low then background noise will cause the filter + * coefficients to constantly be updated. + */ +#define MIN_UPDATE_THRESH_I 4096 + +/* The number of samples used to update coefficients using the + * the block update method (M). It should be related back to the + * length of the echo can. + * ie. it only updates coefficients when (sample number MOD default_m) = 0 + * + * Getting this wrong may cause an oops. Consider yourself warned! + */ +#define DEFAULT_M 16 /* every 16th sample */ + +/* If AGGRESSIVE supression is enabled, then we start cancelling residual + * echos again even while there is potentially the very end of a near-side + * signal present. + * This defines how many samples of DEFAULT_HANGT can remain before we + * kick back in + */ +#define AGGRESSIVE_HCNTR 160 /* in samples, so 160 samples = 20ms */ + + +/***************************************************************/ +/* The following knobs are not implemented in the current code */ + +/* we need a dynamic level of suppression varying with the ratio of the + power of the echo to the power of the reference signal this is + done so that we have a smoother background. + we have a higher suppression when the power ratio is closer to + suppr_ceil and reduces logarithmically as we approach suppr_floor. + */ +#define SUPPR_FLOOR -64 +#define SUPPR_CEIL -24 + +/* in a second departure, we calculate the residual error suppression + * as a percentage of the reference signal energy level. The threshold + * is defined in terms of dB below the reference signal. + */ +#define RES_SUPR_FACTOR -20 + +#ifndef NULL +#define NULL 0 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +/* Generic circular buffer definition */ +typedef struct { + /* Pointer to the relative 'start' of the buffer */ + int idx_d; + /* The absolute size of the buffer */ + int size_d; + /* The actual sample - twice as large as we need, however we do store values at idx_d and idx_d+size_d */ + short *buf_d; +} echo_can_cb_s; + +static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, + struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec); +static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec); +static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size); +static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val); +static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable); + +static const struct dahdi_echocan_factory my_factory = { + .name = "KB1", + .owner = THIS_MODULE, + .echocan_create = echo_can_create, +}; + +static const struct dahdi_echocan_features my_features = { + .NLP_toggle = 1, +}; + +static const struct dahdi_echocan_ops my_ops = { + .name = "KB1", + .echocan_free = echo_can_free, + .echocan_process = echo_can_process, + .echocan_traintap = echo_can_traintap, + .echocan_NLP_toggle = echocan_NLP_toggle, +}; + +struct ec_pvt { + struct dahdi_echocan_state dahdi; + /* an arbitrary ID for this echo can - this really should be settable from the calling channel... */ + int id; + + /* absolute time - aka. sample number index - essentially the number of samples since this can was init'ed */ + int i_d; + + /* Pre-computed constants */ + /* ---------------------- */ + /* Number of filter coefficents */ + int N_d; + /* Rate of adaptation of filter */ + int beta2_i; + + /* Accumulators for power computations */ + /* ----------------------------------- */ + /* reference signal power estimate - aka. Average absolute value of y(k) */ + int Ly_i; + /* ... */ + int Lu_i; + + /* Accumulators for signal detectors */ + /* --------------------------------- */ + /* Power estimate of the recent past of the near-end hybrid signal - aka. Short-time average of: 2 x |s(i)| */ + int s_tilde_i; + /* Power estimate of the recent past of the far-end receive signal - aka. Short-time average of: |y(i)| */ + int y_tilde_i; + + /* Near end speech detection counter - stores Hangover counter time remaining, in samples */ + int HCNTR_d; + + /* Circular buffers and coefficients */ + /* --------------------------------- */ + /* ... */ + int *a_i; + /* ... */ + short *a_s; + /* Reference samples of far-end receive signal */ + echo_can_cb_s y_s; + /* Reference samples of near-end signal */ + echo_can_cb_s s_s; + /* Reference samples of near-end signal minus echo estimate */ + echo_can_cb_s u_s; + /* Reference samples of far-end receive signal used to calculate short-time average */ + echo_can_cb_s y_tilde_s; + + /* Peak far-end receive signal */ + /* --------------------------- */ + /* Highest y_tilde value in the sample buffer */ + short max_y_tilde; + /* Index of the sample containing the max_y_tilde value */ + int max_y_tilde_pos; + +#ifdef MEC2_STATS + /* Storage for performance statistics */ + int cntr_nearend_speech_frames; + int cntr_residualcorrected_frames; + int cntr_residualcorrected_framesskipped; + int cntr_coeff_updates; + int cntr_coeff_missedupdates; + + int avg_Lu_i_toolow; + int avg_Lu_i_ok; +#endif + unsigned int aggressive:1; + int use_nlp; +}; + +#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi) + +static inline void init_cb_s(echo_can_cb_s *cb, int len, void *where) +{ + cb->buf_d = (short *)where; + cb->idx_d = 0; + cb->size_d = len; +} + +static inline void add_cc_s(echo_can_cb_s *cb, short newval) +{ + /* Can't use modulus because N+M isn't a power of two (generally) */ + cb->idx_d--; + if (cb->idx_d < (int)0) + /* Whoops - the pointer to the 'start' wrapped around so reset it to the top of the buffer */ + cb->idx_d += cb->size_d; + + /* Load two copies into memory */ + cb->buf_d[cb->idx_d] = newval; + cb->buf_d[cb->idx_d + cb->size_d] = newval; +} + +static inline short get_cc_s(echo_can_cb_s *cb, int pos) +{ + /* Load two copies into memory */ + return cb->buf_d[cb->idx_d + pos]; +} + +static inline void init_cc(struct ec_pvt *pvt, int N, int maxy, int maxu) +{ + void *ptr = pvt; + unsigned long tmp; + + /* Double-word align past end of state */ + ptr += sizeof(*pvt); + tmp = (unsigned long)ptr; + tmp += 3; + tmp &= ~3L; + ptr = (void *)tmp; + + /* Reset parameters */ + pvt->N_d = N; + pvt->beta2_i = DEFAULT_BETA1_I; + + /* Allocate coefficient memory */ + pvt->a_i = ptr; + ptr += (sizeof(int) * pvt->N_d); + pvt->a_s = ptr; + ptr += (sizeof(short) * pvt->N_d); + + /* Reset Y circular buffer (short version) */ + init_cb_s(&pvt->y_s, maxy, ptr); + ptr += (sizeof(short) * (maxy) * 2); + + /* Reset Sigma circular buffer (short version for FIR filter) */ + init_cb_s(&pvt->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr); + ptr += (sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) * 2); + + init_cb_s(&pvt->u_s, maxu, ptr); + ptr += (sizeof(short) * maxu * 2); + + /* Allocate a buffer for the reference signal power computation */ + init_cb_s(&pvt->y_tilde_s, pvt->N_d, ptr); + + /* Reset the absolute time index */ + pvt->i_d = (int)0; + + /* Reset the power computations (for y and u) */ + pvt->Ly_i = DEFAULT_CUTOFF_I; + pvt->Lu_i = DEFAULT_CUTOFF_I; + +#ifdef MEC2_STATS + /* set the identity */ + pvt->id = (int)&ptr; + + /* Reset performance stats */ + pvt->cntr_nearend_speech_frames = (int)0; + pvt->cntr_residualcorrected_frames = (int)0; + pvt->cntr_residualcorrected_framesskipped = (int)0; + pvt->cntr_coeff_updates = (int)0; + pvt->cntr_coeff_missedupdates = (int)0; + + pvt->avg_Lu_i_toolow = (int)0; + pvt->avg_Lu_i_ok = (int)0; +#endif + + /* Reset the near-end speech detector */ + pvt->s_tilde_i = (int)0; + pvt->y_tilde_i = (int)0; + pvt->HCNTR_d = (int)0; + +} + +static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + + kfree(pvt); +} + +static inline short sample_update(struct ec_pvt *pvt, short iref, short isig) +{ + /* Declare local variables that are used more than once */ + /* ... */ + int k; + /* ... */ + int rs; + /* ... */ + short u; + /* ... */ + int Py_i; + /* ... */ + int two_beta_i; + + /* flow A on pg. 428 */ + /* eq. (16): high-pass filter the input to generate the next value; + * push the current value into the circular buffer + * + * sdc_im1_d = sdc_d; + * sdc_d = sig; + * s_i_d = sdc_d; + * s_d = s_i_d; + * s_i_d = (float)(1.0 - gamma_d) * s_i_d + * + (float)(0.5 * (1.0 - gamma_d)) * (sdc_d - sdc_im1_d); + */ + + /* Update the Far-end receive signal circular buffers and accumulators */ + /* ------------------------------------------------------------------- */ + /* Delete the oldest sample from the power estimate accumulator */ + pvt->y_tilde_i -= abs(get_cc_s(&pvt->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1)) >> DEFAULT_ALPHA_YT_I; + /* Add the new sample to the power estimate accumulator */ + pvt->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I; + /* Push a copy of the new sample into its circular buffer */ + add_cc_s(&pvt->y_s, iref); + + + /* eq. (2): compute r in fixed-point */ + rs = CONVOLVE2(pvt->a_s, + pvt->y_s.buf_d + pvt->y_s.idx_d, + pvt->N_d); + rs >>= 15; + + /* eq. (3): compute the output value (see figure 3) and the error + * note: the error is the same as the output signal when near-end + * speech is not present + */ + u = isig - rs; + + /* Push a copy of the output value sample into its circular buffer */ + add_cc_s(&pvt->u_s, u); + + + /* Update the Near-end hybrid signal circular buffers and accumulators */ + /* ------------------------------------------------------------------- */ + /* Delete the oldest sample from the power estimate accumulator */ + pvt->s_tilde_i -= abs(get_cc_s(&pvt->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1)); + /* Add the new sample to the power estimate accumulator */ + pvt->s_tilde_i += abs(isig); + /* Push a copy of the new sample into it's circular buffer */ + add_cc_s(&pvt->s_s, isig); + + + /* Push a copy of the current short-time average of the far-end receive signal into it's circular buffer */ + add_cc_s(&pvt->y_tilde_s, pvt->y_tilde_i); + + /* flow B on pg. 428 */ + + /* If the hangover timer isn't running then compute the new convergence factor, otherwise set Py_i to 32768 */ + if (!pvt->HCNTR_d) { + Py_i = (pvt->Ly_i >> DEFAULT_SIGMA_LY_I) * (pvt->Ly_i >> DEFAULT_SIGMA_LY_I); + Py_i >>= 15; + } else { + Py_i = (1 << 15); + } + +#if 0 + /* Vary rate of adaptation depending on position in the file + * Do not do this for the first (DEFAULT_UPDATE_TIME) secs after speech + * has begun of the file to allow the echo cancellor to estimate the + * channel accurately + * Still needs conversion! + */ + + if (pvt->start_speech_d != 0) { + if (pvt->i_d > (DEFAULT_T0 + pvt->start_speech_d)*(SAMPLE_FREQ)) { + pvt->beta2_d = max_cc_float(MIN_BETA, DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((pvt->i_d/(float)SAMPLE_FREQ) - DEFAULT_T0 - pvt->start_speech_d))); + } + } else { + pvt->beta2_d = DEFAULT_BETA1; + } +#endif + + /* Fixed point, inverted */ + pvt->beta2_i = DEFAULT_BETA1_I; + + /* Fixed point version, inverted */ + two_beta_i = (pvt->beta2_i * Py_i) >> 15; + if (!two_beta_i) + two_beta_i++; + + /* Update the Suppressed signal power estimate accumulator */ + /* ------------------------------------------------------- */ + /* Delete the oldest sample from the power estimate accumulator */ + pvt->Lu_i -= abs(get_cc_s(&pvt->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1)); + /* Add the new sample to the power estimate accumulator */ + pvt->Lu_i += abs(u); + + /* Update the Far-end reference signal power estimate accumulator */ + /* -------------------------------------------------------------- */ + /* eq. (10): update power estimate of the reference */ + /* Delete the oldest sample from the power estimate accumulator */ + pvt->Ly_i -= abs(get_cc_s(&pvt->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ; + /* Add the new sample to the power estimate accumulator */ + pvt->Ly_i += abs(iref); + + if (pvt->Ly_i < DEFAULT_CUTOFF_I) + pvt->Ly_i = DEFAULT_CUTOFF_I; + + + /* Update the Peak far-end receive signal detected */ + /* ----------------------------------------------- */ + if (pvt->y_tilde_i > pvt->max_y_tilde) { + /* New highest y_tilde with full life */ + pvt->max_y_tilde = pvt->y_tilde_i; + pvt->max_y_tilde_pos = pvt->N_d - 1; + } else if (--pvt->max_y_tilde_pos < 0) { + /* Time to find new max y tilde... */ + pvt->max_y_tilde = MAX16(pvt->y_tilde_s.buf_d + pvt->y_tilde_s.idx_d, pvt->N_d, &pvt->max_y_tilde_pos); + } + + /* Determine if near end speech was detected in this sample */ + /* -------------------------------------------------------- */ + if (((pvt->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > pvt->max_y_tilde) + && (pvt->max_y_tilde > 0)) { + /* Then start the Hangover counter */ + pvt->HCNTR_d = DEFAULT_HANGT; +#ifdef MEC2_STATS_DETAILED + printk(KERN_INFO "Reset near end speech timer with: s_tilde_i %d, stmnt %d, max_y_tilde %d\n", pvt->s_tilde_i, (pvt->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), pvt->max_y_tilde); +#endif +#ifdef MEC2_STATS + ++pvt->cntr_nearend_speech_frames; +#endif + } else if (pvt->HCNTR_d > (int)0) { + /* otherwise, if it's still non-zero, decrement the Hangover counter by one sample */ +#ifdef MEC2_STATS + ++pvt->cntr_nearend_speech_frames; +#endif + pvt->HCNTR_d--; + } + + /* Update coefficients if no near-end speech in this sample (ie. HCNTR_d = 0) + * and we have enough signal to bother trying to update. + * -------------------------------------------------------------------------- + */ + if (!pvt->HCNTR_d && /* no near-end speech present */ + !(pvt->i_d % DEFAULT_M)) { /* we only update on every DEFAULM_M'th sample from the stream */ + if (pvt->Lu_i > MIN_UPDATE_THRESH_I) { /* there is sufficient energy above the noise floor to contain meaningful data */ + /* so loop over all the filter coefficients */ +#ifdef MEC2_STATS_DETAILED + printk(KERN_INFO "updating coefficients with: pvt->Lu_i %9d\n", pvt->Lu_i); +#endif +#ifdef MEC2_STATS + pvt->avg_Lu_i_ok = pvt->avg_Lu_i_ok + pvt->Lu_i; + ++pvt->cntr_coeff_updates; +#endif + for (k = 0; k < pvt->N_d; k++) { + /* eq. (7): compute an expectation over M_d samples */ + int grad2; + grad2 = CONVOLVE2(pvt->u_s.buf_d + pvt->u_s.idx_d, + pvt->y_s.buf_d + pvt->y_s.idx_d + k, + DEFAULT_M); + /* eq. (7): update the coefficient */ + pvt->a_i[k] += grad2 / two_beta_i; + pvt->a_s[k] = pvt->a_i[k] >> 16; + } + } else { +#ifdef MEC2_STATS_DETAILED + printk(KERN_INFO "insufficient signal to update coefficients pvt->Lu_i %5d < %5d\n", pvt->Lu_i, MIN_UPDATE_THRESH_I); +#endif +#ifdef MEC2_STATS + pvt->avg_Lu_i_toolow = pvt->avg_Lu_i_toolow + pvt->Lu_i; + ++pvt->cntr_coeff_missedupdates; +#endif + } + } + + /* paragraph below eq. (15): if no near-end speech in the sample and + * the reference signal power estimate > cutoff threshold + * then perform residual error suppression + */ +#ifdef MEC2_STATS_DETAILED + if (pvt->HCNTR_d == 0) + printk(KERN_INFO "possibly correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d and expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1))); +#endif + +#ifndef NO_ECHO_SUPPRESSOR + if (pvt->use_nlp) { + if (pvt->aggressive) { + if ((pvt->HCNTR_d < AGGRESSIVE_HCNTR) && (pvt->Ly_i > (pvt->Lu_i << 1))) { + for (k = 0; k < 2; k++) { + u = u * (pvt->Lu_i >> DEFAULT_SIGMA_LU_I) / ((pvt->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1); + } +#ifdef MEC2_STATS_DETAILED + printk(KERN_INFO "aggresively correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1))); +#endif +#ifdef MEC2_STATS + ++pvt->cntr_residualcorrected_frames; +#endif + } + } else { + if (pvt->HCNTR_d == 0) { + if ((pvt->Ly_i/(pvt->Lu_i + 1)) > DEFAULT_SUPPR_I) { + for (k = 0; k < 1; k++) { + u = u * (pvt->Lu_i >> DEFAULT_SIGMA_LU_I) / ((pvt->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1); + } +#ifdef MEC2_STATS_DETAILED + printk(KERN_INFO "correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1))); +#endif +#ifdef MEC2_STATS + ++pvt->cntr_residualcorrected_frames; +#endif + } +#ifdef MEC2_STATS + else { + ++pvt->cntr_residualcorrected_framesskipped; + } +#endif + } + } + } +#endif + +#if 0 + /* This will generate a non-linear supression factor, once converted */ + if ((pvt->HCNTR_d == 0) && + ((pvt->Lu_d/pvt->Ly_d) < DEFAULT_SUPPR) && + (pvt->Lu_d/pvt->Ly_d > EC_MIN_DB_VALUE)) { + suppr_factor = (10 / (float)(SUPPR_FLOOR - SUPPR_CEIL)) * log(pvt->Lu_d/pvt->Ly_d) + - SUPPR_CEIL / (float)(SUPPR_FLOOR - SUPPR_CEIL); + u_suppr = pow(10.0, (suppr_factor) * RES_SUPR_FACTOR / 10.0) * u_suppr; + } +#endif + +#ifdef MEC2_STATS + /* Periodically dump performance stats */ + if ((pvt->i_d % MEC2_STATS) == 0) { + /* make sure to avoid div0's! */ + if (pvt->cntr_coeff_missedupdates > 0) + pvt->avg_Lu_i_toolow = (int)(pvt->avg_Lu_i_toolow / pvt->cntr_coeff_missedupdates); + else + pvt->avg_Lu_i_toolow = -1; + + if (pvt->cntr_coeff_updates > 0) + pvt->avg_Lu_i_ok = (pvt->avg_Lu_i_ok / pvt->cntr_coeff_updates); + else + pvt->avg_Lu_i_ok = -1; + + printk( KERN_INFO "%d: Near end speech: %5d Residuals corrected/skipped: %5d/%5d Coefficients updated ok/low sig: %3d/%3d Lu_i avg ok/low sig %6d/%5d\n", + pvt->id, + pvt->cntr_nearend_speech_frames, + pvt->cntr_residualcorrected_frames, pvt->cntr_residualcorrected_framesskipped, + pvt->cntr_coeff_updates, pvt->cntr_coeff_missedupdates, + pvt->avg_Lu_i_ok, pvt->avg_Lu_i_toolow); + + pvt->cntr_nearend_speech_frames = 0; + pvt->cntr_residualcorrected_frames = 0; + pvt->cntr_residualcorrected_framesskipped = 0; + pvt->cntr_coeff_updates = 0; + pvt->cntr_coeff_missedupdates = 0; + pvt->avg_Lu_i_ok = 0; + pvt->avg_Lu_i_toolow = 0; + } +#endif + + /* Increment the sample index and return the corrected sample */ + pvt->i_d++; + return u; +} + +static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + u32 x; + short result; + + for (x = 0; x < size; x++) { + result = sample_update(pvt, *iref, *isig); + *isig++ = result; + ++iref; + } +} + +static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, + struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec) +{ + int maxy; + int maxu; + size_t size; + unsigned int x; + char *c; + struct ec_pvt *pvt; + + maxy = ecp->tap_length + DEFAULT_M; + maxu = DEFAULT_M; + if (maxy < (1 << DEFAULT_ALPHA_YT_I)) + maxy = (1 << DEFAULT_ALPHA_YT_I); + if (maxy < (1 << DEFAULT_SIGMA_LY_I)) + maxy = (1 << DEFAULT_SIGMA_LY_I); + if (maxu < (1 << DEFAULT_SIGMA_LU_I)) + maxu = (1 << DEFAULT_SIGMA_LU_I); + + size = sizeof(*ec) + + 4 + /* align */ + sizeof(int) * ecp->tap_length + /* a_i */ + sizeof(short) * ecp->tap_length + /* a_s */ + 2 * sizeof(short) * (maxy) + /* y_s */ + 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */ + 2 * sizeof(short) * (maxu) + /* u_s */ + 2 * sizeof(short) * ecp->tap_length; /* y_tilde_s */ + + pvt = kzalloc(size, GFP_KERNEL); + if (!pvt) + return -ENOMEM; + + pvt->dahdi.ops = &my_ops; + + pvt->aggressive = aggressive; + pvt->dahdi.features = my_features; + + for (x = 0; x < ecp->param_count; x++) { + for (c = p[x].name; *c; c++) + *c = tolower(*c); + if (!strcmp(p[x].name, "aggressive")) { + pvt->aggressive = p[x].value ? 1 : 0; + } else { + printk(KERN_WARNING "Unknown parameter supplied to KB1 echo canceler: '%s'\n", p[x].name); + kfree(pvt); + + return -EINVAL; + } + } + + init_cc(pvt, ecp->tap_length, maxy, maxu); + /* Non-linear processor - a fancy way to say "zap small signals, to avoid + accumulating noise". */ + pvt->use_nlp = TRUE; + + *ec = &pvt->dahdi; + return 0; +} + +static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + + /* Set the hangover counter to the length of the can to + * avoid adjustments occuring immediately after initial forced training + */ + pvt->HCNTR_d = pvt->N_d << 1; + + if (pos >= pvt->N_d) + return 1; + + pvt->a_i[pos] = val << 17; + pvt->a_s[pos] = val << 1; + + if (++pos >= pvt->N_d) + return 1; + + return 0; +} + +static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + + pvt->use_nlp = enable ? 1 : 0; +} + +static int __init mod_init(void) +{ + if (dahdi_register_echocan_factory(&my_factory)) { + module_printk(KERN_ERR, "could not register with DAHDI core\n"); + + return -EPERM; + } + + module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", my_factory.name); + + return 0; +} + +static void __exit mod_exit(void) +{ + dahdi_unregister_echocan_factory(&my_factory); +} + +module_param(debug, int, S_IRUGO | S_IWUSR); +module_param(aggressive, int, S_IRUGO | S_IWUSR); + +MODULE_DESCRIPTION("DAHDI 'KB1' Echo Canceler"); +MODULE_AUTHOR("Kris Boutilier"); +MODULE_LICENSE("GPL v2"); + +module_init(mod_init); +module_exit(mod_exit); diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_mg2.c dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_mg2.c --- dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_mg2.c 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_mg2.c 2009-04-29 13:24:04.000000000 -0500 @@ -0,0 +1,891 @@ +/* + * ECHO_CAN_MG2 + * + * by Michael Gernoth + * + * Based upon kb1ec.h and mec2.h + * + * Copyright (C) 2002, Digium, Inc. + * + * Additional background on the techniques used in this code can be found in: + * + * Messerschmitt, David; Hedberg, David; Cole, Christopher; Haoui, Amine; + * Winship, Peter; "Digital Voice Echo Canceller with a TMS32020," + * in Digital Signal Processing Applications with the TMS320 Family, + * pp. 415-437, Texas Instruments, Inc., 1986. + * + * A pdf of which is available by searching on the document title at http://www.ti.com/ + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +static int debug; +static int aggressive; + +#define module_printk(level, fmt, args...) printk(level "%s: " fmt, THIS_MODULE->name, ## args) +#define debug_printk(level, fmt, args...) if (debug >= level) printk("%s (%s): " fmt, THIS_MODULE->name, __FUNCTION__, ## args) + +#define ABS(a) abs(a!=-32768?a:-32767) + +#define RESTORE_COEFFS {\ + int x;\ + memcpy(pvt->a_i, pvt->c_i, pvt->N_d*sizeof(int));\ + for (x = 0; x < pvt->N_d; x++) {\ + pvt->a_s[x] = pvt->a_i[x] >> 16;\ + }\ + pvt->backup = BACKUP;\ + } + +/* Uncomment to provide summary statistics for overall echo can performance every 4000 samples */ +/* #define MEC2_STATS 4000 */ + +/* Uncomment to generate per-sample statistics - this will severely degrade system performance and audio quality */ +/* #define MEC2_STATS_DETAILED */ + +/* Uncomment to generate per-call DC bias offset messages */ +/* #define MEC2_DCBIAS_MESSAGE */ + +/* Get optimized routines for math */ +#include "arith.h" + +/* + Important constants for tuning mg2 echo can + */ + +/* Convergence (aka. adaptation) speed -- higher means slower */ +#define DEFAULT_BETA1_I 2048 + +/* Constants for various power computations */ +#define DEFAULT_SIGMA_LY_I 7 +#define DEFAULT_SIGMA_LU_I 7 +#define DEFAULT_ALPHA_ST_I 5 /* near-end speech detection sensitivity factor */ +#define DEFAULT_ALPHA_YT_I 5 + +#define DEFAULT_CUTOFF_I 128 + +/* Define the near-end speech hangover counter: if near-end speech + * is declared, hcntr is set equal to hangt (see pg. 432) + */ +#define DEFAULT_HANGT 600 /* in samples, so 600 samples = 75ms */ + +/* define the residual error suppression threshold */ +#define DEFAULT_SUPPR_I 16 /* 16 = -24db */ + +/* This is the minimum reference signal power estimate level + * that will result in filter adaptation. + * If this is too low then background noise will cause the filter + * coefficients to constantly be updated. + */ +#define MIN_UPDATE_THRESH_I 2048 + +/* The number of samples used to update coefficients using the + * the block update method (M). It should be related back to the + * length of the echo can. + * ie. it only updates coefficients when (sample number MOD default_m) = 0 + * + * Getting this wrong may cause an oops. Consider yourself warned! + */ +#define DEFAULT_M 16 /* every 16th sample */ + +/* If AGGRESSIVE supression is enabled, then we start cancelling residual + * echos again even while there is potentially the very end of a near-side + * signal present. + * This defines how many samples of DEFAULT_HANGT can remain before we + * kick back in + */ +#define AGGRESSIVE_HCNTR 160 /* in samples, so 160 samples = 20ms */ + +/* Treat sample as error if it has a different sign as the + * input signal and is this number larger in ABS() as + * the input-signal */ +#define MAX_SIGN_ERROR 3000 + +/* Number of coefficients really used for calculating the + * simulated echo. The value specifies how many of the + * biggest coefficients are used for calculating rs. + * This helps on long echo-tails by artificially limiting + * the number of coefficients for the calculation and + * preventing overflows. + * Comment this to deactivate the code */ +#define USED_COEFFS 64 + +/* Backup coefficients every this number of samples */ +#define BACKUP 256 + +/***************************************************************/ +/* The following knobs are not implemented in the current code */ + +/* we need a dynamic level of suppression varying with the ratio of the + power of the echo to the power of the reference signal this is + done so that we have a smoother background. + we have a higher suppression when the power ratio is closer to + suppr_ceil and reduces logarithmically as we approach suppr_floor. + */ +#define SUPPR_FLOOR -64 +#define SUPPR_CEIL -24 + +/* in a second departure, we calculate the residual error suppression + * as a percentage of the reference signal energy level. The threshold + * is defined in terms of dB below the reference signal. + */ +#define RES_SUPR_FACTOR -20 + +#define DC_NORMALIZE + +#ifndef NULL +#define NULL 0 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +/* Generic circular buffer definition */ +typedef struct { + /* Pointer to the relative 'start' of the buffer */ + int idx_d; + /* The absolute size of the buffer */ + int size_d; + /* The actual sample - twice as large as we need, however we do store values at idx_d and idx_d+size_d */ + short *buf_d; +} echo_can_cb_s; + +static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, + struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec); +static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec); +static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size); +static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val); +static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable); + +static const struct dahdi_echocan_factory my_factory = { + .name = "MG2", + .owner = THIS_MODULE, + .echocan_create = echo_can_create, +}; + +static const struct dahdi_echocan_features my_features = { + .NLP_toggle = 1, +}; + +static const struct dahdi_echocan_ops my_ops = { + .name = "MG2", + .echocan_free = echo_can_free, + .echocan_process = echo_can_process, + .echocan_traintap = echo_can_traintap, + .echocan_NLP_toggle = echocan_NLP_toggle, +}; + +struct ec_pvt { + struct dahdi_echocan_state dahdi; + /* an arbitrary ID for this echo can - this really should be settable from the calling channel... */ + int id; + + /* absolute time - aka. sample number index - essentially the number of samples since this can was init'ed */ + int i_d; + + /* Pre-computed constants */ + /* ---------------------- */ + /* Number of filter coefficents */ + int N_d; + /* Rate of adaptation of filter */ + int beta2_i; + + /* Accumulators for power computations */ + /* ----------------------------------- */ + /* reference signal power estimate - aka. Average absolute value of y(k) */ + int Ly_i; + /* ... */ + int Lu_i; + + /* Accumulators for signal detectors */ + /* --------------------------------- */ + /* Power estimate of the recent past of the near-end hybrid signal - aka. Short-time average of: 2 x |s(i)| */ + int s_tilde_i; + /* Power estimate of the recent past of the far-end receive signal - aka. Short-time average of: |y(i)| */ + int y_tilde_i; + + /* Near end speech detection counter - stores Hangover counter time remaining, in samples */ + int HCNTR_d; + + /* Circular buffers and coefficients */ + /* --------------------------------- */ + /* ... */ + int *a_i; + /* ... */ + short *a_s; + /* Backups */ + int *b_i; + int *c_i; + /* Reference samples of far-end receive signal */ + echo_can_cb_s y_s; + /* Reference samples of near-end signal */ + echo_can_cb_s s_s; + /* Reference samples of near-end signal minus echo estimate */ + echo_can_cb_s u_s; + /* Reference samples of far-end receive signal used to calculate short-time average */ + echo_can_cb_s y_tilde_s; + + /* Peak far-end receive signal */ + /* --------------------------- */ + /* Highest y_tilde value in the sample buffer */ + short max_y_tilde; + /* Index of the sample containing the max_y_tilde value */ + int max_y_tilde_pos; + +#ifdef MEC2_STATS + /* Storage for performance statistics */ + int cntr_nearend_speech_frames; + int cntr_residualcorrected_frames; + int cntr_residualcorrected_framesskipped; + int cntr_coeff_updates; + int cntr_coeff_missedupdates; + + int avg_Lu_i_toolow; + int avg_Lu_i_ok; +#endif + unsigned int aggressive:1; + short lastsig; + int lastcount; + int backup; +#ifdef DC_NORMALIZE + int dc_estimate; +#endif + int use_nlp; +}; + +#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi) + +static inline void init_cb_s(echo_can_cb_s *cb, int len, void *where) +{ + cb->buf_d = (short *)where; + cb->idx_d = 0; + cb->size_d = len; +} + +static inline void add_cc_s(echo_can_cb_s *cb, short newval) +{ + /* Can't use modulus because N+M isn't a power of two (generally) */ + cb->idx_d--; + if (cb->idx_d < (int)0) + /* Whoops - the pointer to the 'start' wrapped around so reset it to the top of the buffer */ + cb->idx_d += cb->size_d; + + /* Load two copies into memory */ + cb->buf_d[cb->idx_d] = newval; + cb->buf_d[cb->idx_d + cb->size_d] = newval; +} + +static inline short get_cc_s(echo_can_cb_s *cb, int pos) +{ + /* Load two copies into memory */ + return cb->buf_d[cb->idx_d + pos]; +} + +static inline void init_cc(struct ec_pvt *pvt, int N, int maxy, int maxu) +{ + void *ptr = pvt; + unsigned long tmp; + + /* Double-word align past end of state */ + ptr += sizeof(*pvt); + tmp = (unsigned long)ptr; + tmp += 3; + tmp &= ~3L; + ptr = (void *)tmp; + + /* Reset parameters */ + pvt->N_d = N; + pvt->beta2_i = DEFAULT_BETA1_I; + + /* Allocate coefficient memory */ + pvt->a_i = ptr; + ptr += (sizeof(int) * pvt->N_d); + pvt->a_s = ptr; + ptr += (sizeof(short) * pvt->N_d); + + /* Allocate backup memory */ + pvt->b_i = ptr; + ptr += (sizeof(int) * pvt->N_d); + pvt->c_i = ptr; + ptr += (sizeof(int) * pvt->N_d); + + /* Reset Y circular buffer (short version) */ + init_cb_s(&pvt->y_s, maxy, ptr); + ptr += (sizeof(short) * (maxy) * 2); + + /* Reset Sigma circular buffer (short version for FIR filter) */ + init_cb_s(&pvt->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr); + ptr += (sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) * 2); + + init_cb_s(&pvt->u_s, maxu, ptr); + ptr += (sizeof(short) * maxu * 2); + + /* Allocate a buffer for the reference signal power computation */ + init_cb_s(&pvt->y_tilde_s, pvt->N_d, ptr); + + /* Reset the absolute time index */ + pvt->i_d = (int)0; + + /* Reset the power computations (for y and u) */ + pvt->Ly_i = DEFAULT_CUTOFF_I; + pvt->Lu_i = DEFAULT_CUTOFF_I; + +#ifdef MEC2_STATS + /* set the identity */ + pvt->id = (int)&ptr; + + /* Reset performance stats */ + pvt->cntr_nearend_speech_frames = (int)0; + pvt->cntr_residualcorrected_frames = (int)0; + pvt->cntr_residualcorrected_framesskipped = (int)0; + pvt->cntr_coeff_updates = (int)0; + pvt->cntr_coeff_missedupdates = (int)0; + + pvt->avg_Lu_i_toolow = (int)0; + pvt->avg_Lu_i_ok = (int)0; +#endif + + /* Reset the near-end speech detector */ + pvt->s_tilde_i = (int)0; + pvt->y_tilde_i = (int)0; + pvt->HCNTR_d = (int)0; + +} + +static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + +#if defined(DC_NORMALIZE) && defined(MEC2_DCBIAS_MESSAGE) + printk(KERN_INFO "EC: DC bias calculated: %d V\n", pvt->dc_estimate >> 15); +#endif + kfree(pvt); +} + +#ifdef DC_NORMALIZE +short inline dc_removal(int *dc_estimate, short samp) +{ + *dc_estimate += ((((int)samp << 15) - *dc_estimate) >> 9); + return samp - (*dc_estimate >> 15); +} +#endif + +static inline short sample_update(struct ec_pvt *pvt, short iref, short isig) +{ + /* Declare local variables that are used more than once */ + /* ... */ + int k; + /* ... */ + int rs; + /* ... */ + short u; + /* ... */ + int Py_i; + /* ... */ + int two_beta_i; + +#ifdef DC_NORMALIZE + isig = dc_removal(&pvt->dc_estimate, isig); +#endif + + /* flow A on pg. 428 */ + /* eq. (16): high-pass filter the input to generate the next value; + * push the current value into the circular buffer + * + * sdc_im1_d = sdc_d; + * sdc_d = sig; + * s_i_d = sdc_d; + * s_d = s_i_d; + * s_i_d = (float)(1.0 - gamma_d) * s_i_d + * + (float)(0.5 * (1.0 - gamma_d)) * (sdc_d - sdc_im1_d); + */ + + /* Update the Far-end receive signal circular buffers and accumulators */ + /* ------------------------------------------------------------------- */ + /* Delete the oldest sample from the power estimate accumulator */ + pvt->y_tilde_i -= abs(get_cc_s(&pvt->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1)) >> DEFAULT_ALPHA_YT_I; + /* Add the new sample to the power estimate accumulator */ + pvt->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I; + /* Push a copy of the new sample into its circular buffer */ + add_cc_s(&pvt->y_s, iref); + + + /* eq. (2): compute r in fixed-point */ + rs = CONVOLVE2(pvt->a_s, + pvt->y_s.buf_d + pvt->y_s.idx_d, + pvt->N_d); + rs >>= 15; + + if (pvt->lastsig == isig) { + pvt->lastcount++; + } else { + pvt->lastcount = 0; + pvt->lastsig = isig; + } + + if (isig == 0) { + u = 0; + } else if (pvt->lastcount > 255) { + /* We have seen the same input-signal more than 255 times, + * we should pass it through uncancelled, as we are likely on hold */ + u = isig; + } else { + int sign_error; + + if (rs < -32768) { + rs = -32768; + pvt->HCNTR_d = DEFAULT_HANGT; + RESTORE_COEFFS; + } else if (rs > 32767) { + rs = 32767; + pvt->HCNTR_d = DEFAULT_HANGT; + RESTORE_COEFFS; + } + + sign_error = ABS(rs) - ABS(isig); + + if (ABS(sign_error) > MAX_SIGN_ERROR) + { + rs = 0; + RESTORE_COEFFS; + } + + /* eq. (3): compute the output value (see figure 3) and the error + * note: the error is the same as the output signal when near-end + * speech is not present + */ + u = isig - rs; + + if (u / isig < 0) + u = isig - (rs >> 1); + } + + /* Push a copy of the output value sample into its circular buffer */ + add_cc_s(&pvt->u_s, u); + + if (!pvt->backup) { + /* Backup coefficients periodically */ + pvt->backup = BACKUP; + memcpy(pvt->c_i, pvt->b_i, pvt->N_d*sizeof(int)); + memcpy(pvt->b_i, pvt->a_i, pvt->N_d*sizeof(int)); + } else + pvt->backup--; + + + /* Update the Near-end hybrid signal circular buffers and accumulators */ + /* ------------------------------------------------------------------- */ + /* Delete the oldest sample from the power estimate accumulator */ + pvt->s_tilde_i -= abs(get_cc_s(&pvt->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1)); + /* Add the new sample to the power estimate accumulator */ + pvt->s_tilde_i += abs(isig); + /* Push a copy of the new sample into it's circular buffer */ + add_cc_s(&pvt->s_s, isig); + + + /* Push a copy of the current short-time average of the far-end receive signal into it's circular buffer */ + add_cc_s(&pvt->y_tilde_s, pvt->y_tilde_i); + + /* flow B on pg. 428 */ + + /* If the hangover timer isn't running then compute the new convergence factor, otherwise set Py_i to 32768 */ + if (!pvt->HCNTR_d) { + Py_i = (pvt->Ly_i >> DEFAULT_SIGMA_LY_I) * (pvt->Ly_i >> DEFAULT_SIGMA_LY_I); + Py_i >>= 15; + } else { + Py_i = (1 << 15); + } + +#if 0 + /* Vary rate of adaptation depending on position in the file + * Do not do this for the first (DEFAULT_UPDATE_TIME) secs after speech + * has begun of the file to allow the echo cancellor to estimate the + * channel accurately + * Still needs conversion! + */ + + if (pvt->start_speech_d != 0) { + if (pvt->i_d > (DEFAULT_T0 + pvt->start_speech_d)*(SAMPLE_FREQ)) { + pvt->beta2_d = max_cc_float(MIN_BETA, DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((pvt->i_d/(float)SAMPLE_FREQ) - DEFAULT_T0 - pvt->start_speech_d))); + } + } else { + pvt->beta2_d = DEFAULT_BETA1; + } +#endif + + /* Fixed point, inverted */ + pvt->beta2_i = DEFAULT_BETA1_I; + + /* Fixed point version, inverted */ + two_beta_i = (pvt->beta2_i * Py_i) >> 15; + if (!two_beta_i) + two_beta_i++; + + /* Update the Suppressed signal power estimate accumulator */ + /* ------------------------------------------------------- */ + /* Delete the oldest sample from the power estimate accumulator */ + pvt->Lu_i -= abs(get_cc_s(&pvt->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1)); + /* Add the new sample to the power estimate accumulator */ + pvt->Lu_i += abs(u); + + /* Update the Far-end reference signal power estimate accumulator */ + /* -------------------------------------------------------------- */ + /* eq. (10): update power estimate of the reference */ + /* Delete the oldest sample from the power estimate accumulator */ + pvt->Ly_i -= abs(get_cc_s(&pvt->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ; + /* Add the new sample to the power estimate accumulator */ + pvt->Ly_i += abs(iref); + + if (pvt->Ly_i < DEFAULT_CUTOFF_I) + pvt->Ly_i = DEFAULT_CUTOFF_I; + + + /* Update the Peak far-end receive signal detected */ + /* ----------------------------------------------- */ + if (pvt->y_tilde_i > pvt->max_y_tilde) { + /* New highest y_tilde with full life */ + pvt->max_y_tilde = pvt->y_tilde_i; + pvt->max_y_tilde_pos = pvt->N_d - 1; + } else if (--pvt->max_y_tilde_pos < 0) { + /* Time to find new max y tilde... */ + pvt->max_y_tilde = MAX16(pvt->y_tilde_s.buf_d + pvt->y_tilde_s.idx_d, pvt->N_d, &pvt->max_y_tilde_pos); + } + + /* Determine if near end speech was detected in this sample */ + /* -------------------------------------------------------- */ + if (((pvt->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > pvt->max_y_tilde) + && (pvt->max_y_tilde > 0)) { + /* Then start the Hangover counter */ + pvt->HCNTR_d = DEFAULT_HANGT; + RESTORE_COEFFS; +#ifdef MEC2_STATS_DETAILED + printk(KERN_INFO "Reset near end speech timer with: s_tilde_i %d, stmnt %d, max_y_tilde %d\n", pvt->s_tilde_i, (pvt->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), pvt->max_y_tilde); +#endif +#ifdef MEC2_STATS + ++pvt->cntr_nearend_speech_frames; +#endif + } else if (pvt->HCNTR_d > (int)0) { + /* otherwise, if it's still non-zero, decrement the Hangover counter by one sample */ +#ifdef MEC2_STATS + ++pvt->cntr_nearend_speech_frames; +#endif + pvt->HCNTR_d--; + } + + /* Update coefficients if no near-end speech in this sample (ie. HCNTR_d = 0) + * and we have enough signal to bother trying to update. + * -------------------------------------------------------------------------- + */ + if (!pvt->HCNTR_d && /* no near-end speech present */ + !(pvt->i_d % DEFAULT_M)) { /* we only update on every DEFAULM_M'th sample from the stream */ + if (pvt->Lu_i > MIN_UPDATE_THRESH_I) { /* there is sufficient energy above the noise floor to contain meaningful data */ + /* so loop over all the filter coefficients */ +#ifdef USED_COEFFS + int max_coeffs[USED_COEFFS]; + int *pos; + + if (pvt->N_d > USED_COEFFS) + memset(max_coeffs, 0, USED_COEFFS*sizeof(int)); +#endif +#ifdef MEC2_STATS_DETAILED + printk(KERN_INFO "updating coefficients with: pvt->Lu_i %9d\n", pvt->Lu_i); +#endif +#ifdef MEC2_STATS + pvt->avg_Lu_i_ok = pvt->avg_Lu_i_ok + pvt->Lu_i; + ++pvt->cntr_coeff_updates; +#endif + for (k = 0; k < pvt->N_d; k++) { + /* eq. (7): compute an expectation over M_d samples */ + int grad2; + grad2 = CONVOLVE2(pvt->u_s.buf_d + pvt->u_s.idx_d, + pvt->y_s.buf_d + pvt->y_s.idx_d + k, + DEFAULT_M); + /* eq. (7): update the coefficient */ + pvt->a_i[k] += grad2 / two_beta_i; + pvt->a_s[k] = pvt->a_i[k] >> 16; + +#ifdef USED_COEFFS + if (pvt->N_d > USED_COEFFS) { + if (abs(pvt->a_i[k]) > max_coeffs[USED_COEFFS-1]) { + /* More or less insertion-sort... */ + pos = max_coeffs; + while (*pos > abs(pvt->a_i[k])) + pos++; + + if (*pos > max_coeffs[USED_COEFFS-1]) + memmove(pos+1, pos, (USED_COEFFS-(pos-max_coeffs)-1)*sizeof(int)); + + *pos = abs(pvt->a_i[k]); + } + } +#endif + } + +#ifdef USED_COEFFS + /* Filter out irrelevant coefficients */ + if (pvt->N_d > USED_COEFFS) + for (k = 0; k < pvt->N_d; k++) + if (abs(pvt->a_i[k]) < max_coeffs[USED_COEFFS-1]) + pvt->a_i[k] = pvt->a_s[k] = 0; +#endif + } else { +#ifdef MEC2_STATS_DETAILED + printk(KERN_INFO "insufficient signal to update coefficients pvt->Lu_i %5d < %5d\n", pvt->Lu_i, MIN_UPDATE_THRESH_I); +#endif +#ifdef MEC2_STATS + pvt->avg_Lu_i_toolow = pvt->avg_Lu_i_toolow + pvt->Lu_i; + ++pvt->cntr_coeff_missedupdates; +#endif + } + } + + /* paragraph below eq. (15): if no near-end speech in the sample and + * the reference signal power estimate > cutoff threshold + * then perform residual error suppression + */ +#ifdef MEC2_STATS_DETAILED + if (pvt->HCNTR_d == 0) + printk(KERN_INFO "possibly correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d and expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1))); +#endif + +#ifndef NO_ECHO_SUPPRESSOR + if (pvt->use_nlp) { + if (pvt->aggressive) { + if ((pvt->HCNTR_d < AGGRESSIVE_HCNTR) && (pvt->Ly_i > (pvt->Lu_i << 1))) { + for (k = 0; k < 2; k++) { + u = u * (pvt->Lu_i >> DEFAULT_SIGMA_LU_I) / ((pvt->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1); + } +#ifdef MEC2_STATS_DETAILED + printk(KERN_INFO "aggresively correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1))); +#endif +#ifdef MEC2_STATS + ++pvt->cntr_residualcorrected_frames; +#endif + } + } else { + if (pvt->HCNTR_d == 0) { + if ((pvt->Ly_i/(pvt->Lu_i + 1)) > DEFAULT_SUPPR_I) { + for (k = 0; k < 1; k++) { + u = u * (pvt->Lu_i >> DEFAULT_SIGMA_LU_I) / ((pvt->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1); + } +#ifdef MEC2_STATS_DETAILED + printk(KERN_INFO "correcting frame with pvt->Ly_i %9d pvt->Lu_i %9d expression %d\n", pvt->Ly_i, pvt->Lu_i, (pvt->Ly_i/(pvt->Lu_i + 1))); +#endif +#ifdef MEC2_STATS + ++pvt->cntr_residualcorrected_frames; +#endif + } +#ifdef MEC2_STATS + else { + ++pvt->cntr_residualcorrected_framesskipped; + } +#endif + } + } + } +#endif + +#if 0 + /* This will generate a non-linear supression factor, once converted */ + if ((pvt->HCNTR_d == 0) && + ((pvt->Lu_d/pvt->Ly_d) < DEFAULT_SUPPR) && + (pvt->Lu_d/pvt->Ly_d > EC_MIN_DB_VALUE)) { + suppr_factor = (10 / (float)(SUPPR_FLOOR - SUPPR_CEIL)) * log(pvt->Lu_d/pvt->Ly_d) + - SUPPR_CEIL / (float)(SUPPR_FLOOR - SUPPR_CEIL); + u_suppr = pow(10.0, (suppr_factor) * RES_SUPR_FACTOR / 10.0) * u_suppr; + } +#endif + +#ifdef MEC2_STATS + /* Periodically dump performance stats */ + if ((pvt->i_d % MEC2_STATS) == 0) { + /* make sure to avoid div0's! */ + if (pvt->cntr_coeff_missedupdates > 0) + pvt->avg_Lu_i_toolow = (int)(pvt->avg_Lu_i_toolow / pvt->cntr_coeff_missedupdates); + else + pvt->avg_Lu_i_toolow = -1; + + if (pvt->cntr_coeff_updates > 0) + pvt->avg_Lu_i_ok = (pvt->avg_Lu_i_ok / pvt->cntr_coeff_updates); + else + pvt->avg_Lu_i_ok = -1; + + printk(KERN_INFO "%d: Near end speech: %5d Residuals corrected/skipped: %5d/%5d Coefficients updated ok/low sig: %3d/%3d Lu_i avg ok/low sig %6d/%5d\n", + pvt->id, + pvt->cntr_nearend_speech_frames, + pvt->cntr_residualcorrected_frames, pvt->cntr_residualcorrected_framesskipped, + pvt->cntr_coeff_updates, pvt->cntr_coeff_missedupdates, + pvt->avg_Lu_i_ok, pvt->avg_Lu_i_toolow); + + pvt->cntr_nearend_speech_frames = 0; + pvt->cntr_residualcorrected_frames = 0; + pvt->cntr_residualcorrected_framesskipped = 0; + pvt->cntr_coeff_updates = 0; + pvt->cntr_coeff_missedupdates = 0; + pvt->avg_Lu_i_ok = 0; + pvt->avg_Lu_i_toolow = 0; + } +#endif + + /* Increment the sample index and return the corrected sample */ + pvt->i_d++; + return u; +} + +static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + u32 x; + short result; + + for (x = 0; x < size; x++) { + result = sample_update(pvt, *iref, *isig); + *isig++ = result; + ++iref; + } +} + +static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, + struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec) +{ + int maxy; + int maxu; + size_t size; + unsigned int x; + char *c; + struct ec_pvt *pvt; + + maxy = ecp->tap_length + DEFAULT_M; + maxu = DEFAULT_M; + if (maxy < (1 << DEFAULT_ALPHA_YT_I)) + maxy = (1 << DEFAULT_ALPHA_YT_I); + if (maxy < (1 << DEFAULT_SIGMA_LY_I)) + maxy = (1 << DEFAULT_SIGMA_LY_I); + if (maxu < (1 << DEFAULT_SIGMA_LU_I)) + maxu = (1 << DEFAULT_SIGMA_LU_I); + size = sizeof(**ec) + + 4 + /* align */ + sizeof(int) * ecp->tap_length + /* a_i */ + sizeof(short) * ecp->tap_length + /* a_s */ + sizeof(int) * ecp->tap_length + /* b_i */ + sizeof(int) * ecp->tap_length + /* c_i */ + 2 * sizeof(short) * (maxy) + /* y_s */ + 2 * sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) + /* s_s */ + 2 * sizeof(short) * (maxu) + /* u_s */ + 2 * sizeof(short) * ecp->tap_length; /* y_tilde_s */ + + pvt = kzalloc(size, GFP_KERNEL); + if (!pvt) + return -ENOMEM; + + pvt->dahdi.ops = &my_ops; + + pvt->aggressive = aggressive; + pvt->dahdi.features = my_features; + + for (x = 0; x < ecp->param_count; x++) { + for (c = p[x].name; *c; c++) + *c = tolower(*c); + if (!strcmp(p[x].name, "aggressive")) { + pvt->aggressive = p[x].value ? 1 : 0; + } else { + printk(KERN_WARNING "Unknown parameter supplied to MG2 echo canceler: '%s'\n", p[x].name); + kfree(pvt); + + return -EINVAL; + } + } + + init_cc(pvt, ecp->tap_length, maxy, maxu); + /* Non-linear processor - a fancy way to say "zap small signals, to avoid + accumulating noise". */ + pvt->use_nlp = TRUE; + + *ec = &pvt->dahdi; + return 0; +} + +static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + + /* Set the hangover counter to the length of the can to + * avoid adjustments occuring immediately after initial forced training + */ + pvt->HCNTR_d = pvt->N_d << 1; + + if (pos >= pvt->N_d) { + memcpy(pvt->b_i, pvt->a_i, pvt->N_d*sizeof(int)); + memcpy(pvt->c_i, pvt->a_i, pvt->N_d*sizeof(int)); + return 1; + } + + pvt->a_i[pos] = val << 17; + pvt->a_s[pos] = val << 1; + + if (++pos >= pvt->N_d) { + memcpy(pvt->b_i, pvt->a_i, pvt->N_d*sizeof(int)); + memcpy(pvt->c_i, pvt->a_i, pvt->N_d*sizeof(int)); + return 1; + } + + return 0; +} + +static void echocan_NLP_toggle(struct dahdi_echocan_state *ec, unsigned int enable) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + + pvt->use_nlp = enable ? 1 : 0; +} + +static int __init mod_init(void) +{ + if (dahdi_register_echocan_factory(&my_factory)) { + module_printk(KERN_ERR, "could not register with DAHDI core\n"); + + return -EPERM; + } + + module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", my_factory.name); + + return 0; +} + +static void __exit mod_exit(void) +{ + dahdi_unregister_echocan_factory(&my_factory); +} + +module_param(debug, int, S_IRUGO | S_IWUSR); +module_param(aggressive, int, S_IRUGO | S_IWUSR); + +MODULE_DESCRIPTION("DAHDI 'MG2' Echo Canceler"); +MODULE_AUTHOR("Michael Gernoth"); +MODULE_LICENSE("GPL v2"); + +module_init(mod_init); +module_exit(mod_exit); diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_oslec.c dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_oslec.c --- dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_oslec.c 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_oslec.c 2009-04-29 13:24:04.000000000 -0500 @@ -0,0 +1,143 @@ +/* + * DAHDI Telephony Interface to the Open Source Line Echo Canceller (OSLEC) + * + * Written by Tzafrir Cohen + * Copyright (C) 2008 Xorcom, Inc. + * + * All rights reserved. + * + * Based on dahdi_echocan_hpec.c, Copyright (C) 2006-2008 Digium, Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +/* Fix this if OSLEC is elsewhere */ +#include "../staging/echo/oslec.h" +//#include + +#include + +#define module_printk(level, fmt, args...) printk(level "%s: " fmt, THIS_MODULE->name, ## args) + +static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, + struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec); +static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec); +static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size); +static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val); + +static const struct dahdi_echocan_factory my_factory = { + .name = "OSLEC", + .owner = THIS_MODULE, + .echocan_create = echo_can_create, +}; + +static const struct dahdi_echocan_ops my_ops = { + .name = "OSLEC", + .echocan_free = echo_can_free, + .echocan_process = echo_can_process, + .echocan_traintap = echo_can_traintap, +}; + +struct ec_pvt { + struct oslec_state *oslec; + struct dahdi_echocan_state dahdi; +}; + +#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi) + +static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + + oslec_free(pvt->oslec); + kfree(pvt); +} + +static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + u32 SampleNum; + + for (SampleNum = 0; SampleNum < size; SampleNum++, iref++) { + short iCleanSample; + + iCleanSample = oslec_update(pvt->oslec, *iref, *isig); + *isig++ = iCleanSample; + } +} + +static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, + struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec) +{ + struct ec_pvt *pvt; + + if (ecp->param_count > 0) { + printk(KERN_WARNING "OSLEC does not support parameters; failing request\n"); + return -EINVAL; + } + + pvt = kzalloc(sizeof(*pvt), GFP_KERNEL); + if (!pvt) + return -ENOMEM; + + pvt->dahdi.ops = &my_ops; + + pvt->oslec = oslec_create(ecp->tap_length, ECHO_CAN_USE_ADAPTION | ECHO_CAN_USE_NLP | ECHO_CAN_USE_CLIP | ECHO_CAN_USE_TX_HPF | ECHO_CAN_USE_RX_HPF); + + if (!pvt->oslec) { + kfree(pvt); + *ec = NULL; + return -ENOTTY; + } else { + *ec = &pvt->dahdi; + return 0; + } +} + +static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val) +{ + return 1; +} + +static int __init mod_init(void) +{ + if (dahdi_register_echocan_factory(&my_factory)) { + module_printk(KERN_ERR, "could not register with DAHDI core\n"); + + return -EPERM; + } + + module_printk(KERN_INFO, "Registered echo canceler '%s'\n", my_factory.name); + + return 0; +} + +static void __exit mod_exit(void) +{ + dahdi_unregister_echocan_factory(&my_factory); +} + +MODULE_DESCRIPTION("DAHDI OSLEC wrapper"); +MODULE_AUTHOR("Tzafrir Cohen "); +MODULE_LICENSE("GPL"); + +module_init(mod_init); +module_exit(mod_exit); diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_sec2.c dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_sec2.c --- dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_sec2.c 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_sec2.c 2009-04-29 13:24:04.000000000 -0500 @@ -0,0 +1,351 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * echo.c - An echo cancellor, suitable for electrical and acoustic + * cancellation. This code does not currently comply with + * any relevant standards (e.g. G.164/5/7/8). One day.... + * + * Written by Steve Underwood + * + * Copyright (C) 2001 Steve Underwood + * + * Based on a bit from here, a bit from there, eye of toad, + * ear of bat, etc - plus, of course, my own 2 cents. + * + * All rights reserved. + * + */ + +/* + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2 as published by the + * Free Software Foundation. See the LICENSE file included with + * this program for more details. + */ + +/* TODO: + Finish the echo suppressor option, however nasty suppression may be + Add an option to reintroduce side tone at -24dB under appropriate conditions. + Improve double talk detector (iterative!) +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +static int debug; + +#define module_printk(level, fmt, args...) printk(level "%s: " fmt, THIS_MODULE->name, ## args) +#define debug_printk(level, fmt, args...) if (debug >= level) printk(KERN_DEBUG "%s (%s): " fmt, THIS_MODULE->name, __FUNCTION__, ## args) + +#include "fir.h" + +#ifndef NULL +#define NULL 0 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#define NONUPDATE_DWELL_TIME 600 /* 600 samples, or 75ms */ + +/* + * According to Jim... + */ +#define MIN_TX_POWER_FOR_ADAPTION 512 +#define MIN_RX_POWER_FOR_ADAPTION 64 + +/* + * According to Steve... + */ +/* #define MIN_TX_POWER_FOR_ADAPTION 4096 +#define MIN_RX_POWER_FOR_ADAPTION 64 */ + +static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, + struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec); +static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec); +static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size); +static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val); + +static const struct dahdi_echocan_factory my_factory = { + .name = "SEC2", + .owner = THIS_MODULE, + .echocan_create = echo_can_create, +}; + +static const struct dahdi_echocan_ops my_ops = { + .name = "SEC2", + .echocan_free = echo_can_free, + .echocan_process = echo_can_process, + .echocan_traintap = echo_can_traintap, +}; + +struct ec_pvt { + struct dahdi_echocan_state dahdi; + int tx_power; + int rx_power; + int clean_rx_power; + + int rx_power_threshold; + int nonupdate_dwell; + + fir16_state_t fir_state; + int16_t *fir_taps16; /* 16-bit version of FIR taps */ + int32_t *fir_taps32; /* 32-bit version of FIR taps */ + + int curr_pos; + + int taps; + int tap_mask; + int use_nlp; + int use_suppressor; + + int32_t supp_test1; + int32_t supp_test2; + int32_t supp1; + int32_t supp2; + + int32_t latest_correction; /* Indication of the magnitude of the latest + adaption, or a code to indicate why adaption + was skipped, for test purposes */ +}; + +#define dahdi_to_pvt(a) container_of(a, struct ec_pvt, dahdi) + +static int echo_can_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, + struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec) +{ + struct ec_pvt *pvt; + size_t size; + + if (ecp->param_count > 0) { + printk(KERN_WARNING "SEC2 does not support parameters; failing request\n"); + return -EINVAL; + } + + size = sizeof(*pvt) + ecp->tap_length * sizeof(int32_t) + ecp->tap_length * 3 * sizeof(int16_t); + + pvt = kzalloc(size, GFP_KERNEL); + if (!pvt) + return -ENOMEM; + + pvt->dahdi.ops = &my_ops; + + if (ecp->param_count > 0) { + printk(KERN_WARNING "SEC-2 echo canceler does not support parameters; failing request\n"); + return -EINVAL; + } + + pvt->taps = ecp->tap_length; + pvt->curr_pos = ecp->tap_length - 1; + pvt->tap_mask = ecp->tap_length - 1; + pvt->fir_taps32 = (int32_t *) (pvt + sizeof(*pvt)); + pvt->fir_taps16 = (int16_t *) (pvt + sizeof(*pvt) + ecp->tap_length * sizeof(int32_t)); + /* Create FIR filter */ + fir16_create(&pvt->fir_state, pvt->fir_taps16, pvt->taps); + pvt->rx_power_threshold = 10000000; + pvt->use_suppressor = FALSE; + /* Non-linear processor - a fancy way to say "zap small signals, to avoid + accumulating noise". */ + pvt->use_nlp = FALSE; + + *ec = &pvt->dahdi; + return 0; +} + +static void echo_can_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + + fir16_free(&pvt->fir_state); + kfree(pvt); +} + +static inline int16_t sample_update(struct ec_pvt *pvt, int16_t tx, int16_t rx) +{ + int offset1; + int offset2; + int32_t echo_value; + int clean_rx; + int nsuppr; + int i; + int correction; + + /* Evaluate the echo - i.e. apply the FIR filter */ + /* Assume the gain of the FIR does not exceed unity. Exceeding unity + would seem like a rather poor thing for an echo cancellor to do :) + This means we can compute the result with a total disregard for + overflows. 16bits x 16bits -> 31bits, so no overflow can occur in + any multiply. While accumulating we may overflow and underflow the + 32 bit scale often. However, if the gain does not exceed unity, + everything should work itself out, and the final result will be + OK, without any saturation logic. */ + /* Overflow is very much possible here, and we do nothing about it because + of the compute costs */ + /* 16 bit coeffs for the LMS give lousy results (maths good, actual sound + bad!), but 32 bit coeffs require some shifting. On balance 32 bit seems + best */ + echo_value = fir16 (&pvt->fir_state, tx); + + /* And the answer is..... */ + clean_rx = rx - echo_value; + + /* That was the easy part. Now we need to adapt! */ + if (pvt->nonupdate_dwell > 0) + pvt->nonupdate_dwell--; + + /* If there is very little being transmitted, any attempt to train is + futile. We would either be training on the far end's noise or signal, + the channel's own noise, or our noise. Either way, this is hardly good + training, so don't do it (avoid trouble). */ + /* If the received power is very low, either we are sending very little or + we are already well adapted. There is little point in trying to improve + the adaption under these circumstanceson, so don't do it (reduce the + compute load). */ + if (pvt->tx_power > MIN_TX_POWER_FOR_ADAPTION && pvt->rx_power > MIN_RX_POWER_FOR_ADAPTION) { + /* This is a really crude piece of decision logic, but it does OK + for now. */ + if (pvt->tx_power > 2*pvt->rx_power) { + /* There is no far-end speech detected */ + if (pvt->nonupdate_dwell == 0) { + /* ... and we are not in the dwell time from previous speech. */ + /* nsuppr = saturate((clean_rx << 16)/pvt->tx_power); */ + nsuppr = clean_rx >> 3; + + /* Update the FIR taps */ + offset2 = pvt->curr_pos + 1; + offset1 = pvt->taps - offset2; + pvt->latest_correction = 0; + for (i = pvt->taps - 1; i >= offset1; i--) { + correction = pvt->fir_state.history[i - offset1]*nsuppr; + /* Leak to avoid false training on signals with multiple + strong correlations. */ + pvt->fir_taps32[i] -= (pvt->fir_taps32[i] >> 12); + pvt->fir_taps32[i] += correction; + pvt->fir_state.coeffs[i] = pvt->fir_taps32[i] >> 15; + pvt->latest_correction += abs(correction); + } + for ( ; i >= 0; i--) { + correction = pvt->fir_state.history[i + offset2]*nsuppr; + /* Leak to avoid false training on signals with multiple + strong correlations. */ + pvt->fir_taps32[i] -= (pvt->fir_taps32[i] >> 12); + pvt->fir_taps32[i] += correction; + pvt->fir_state.coeffs[i] = pvt->fir_taps32[i] >> 15; + pvt->latest_correction += abs(correction); + } + } else { + pvt->latest_correction = -1; + } + } else { + pvt->nonupdate_dwell = NONUPDATE_DWELL_TIME; + pvt->latest_correction = -2; + } + } else { + pvt->nonupdate_dwell = 0; + pvt->latest_correction = -3; + } + /* Calculate short term power levels using very simple single pole IIRs */ + /* TODO: Is the nasty modulus approach the fastest, or would a real + tx*tx power calculation actually be faster? */ + pvt->tx_power += ((abs(tx) - pvt->tx_power) >> 5); + pvt->rx_power += ((abs(rx) - pvt->rx_power) >> 5); + pvt->clean_rx_power += ((abs(clean_rx) - pvt->clean_rx_power) >> 5); + +#if defined(XYZZY) + if (pvt->use_suppressor) { + pvt->supp_test1 += (pvt->fir_state.history[pvt->curr_pos] - pvt->fir_state.history[(pvt->curr_pos - 7) & pvt->tap_mask]); + pvt->supp_test2 += (pvt->fir_state.history[(pvt->curr_pos - 24) & pvt->tap_mask] - pvt->fir_state.history[(pvt->curr_pos - 31) & pvt->tap_mask]); + if (pvt->supp_test1 > 42 && pvt->supp_test2 > 42) + supp_change = 25; + else + supp_change = 50; + supp = supp_change + k1*pvt->supp1 + k2*pvt->supp2; + pvt->supp2 = pvt->supp1; + pvt->supp1 = supp; + clean_rx *= (1 - supp); + } +#endif + + if (pvt->use_nlp && pvt->rx_power < 32) + clean_rx = 0; + + /* Roll around the rolling buffer */ + if (pvt->curr_pos <= 0) + pvt->curr_pos = pvt->taps; + pvt->curr_pos--; + + return clean_rx; +} + +static void echo_can_process(struct dahdi_echocan_state *ec, short *isig, const short *iref, u32 size) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + u32 x; + short result; + + for (x = 0; x < size; x++) { + result = sample_update(pvt, *iref, *isig); + *isig++ = result; + ++iref; + } +} + +static int echo_can_traintap(struct dahdi_echocan_state *ec, int pos, short val) +{ + struct ec_pvt *pvt = dahdi_to_pvt(ec); + + /* Reset hang counter to avoid adjustments after + initial forced training */ + pvt->nonupdate_dwell = pvt->taps << 1; + if (pos >= pvt->taps) + return 1; + pvt->fir_taps32[pos] = val << 17; + pvt->fir_taps16[pos] = val << 1; + if (++pos >= pvt->taps) + return 1; + else + return 0; +} + +static int __init mod_init(void) +{ + if (dahdi_register_echocan_factory(&my_factory)) { + module_printk(KERN_ERR, "could not register with DAHDI core\n"); + + return -EPERM; + } + + module_printk(KERN_NOTICE, "Registered echo canceler '%s'\n", my_factory.name); + + return 0; +} + +static void __exit mod_exit(void) +{ + dahdi_unregister_echocan_factory(&my_factory); +} + +module_param(debug, int, S_IRUGO | S_IWUSR); + +MODULE_DESCRIPTION("DAHDI 'SEC2' Echo Canceler"); +MODULE_AUTHOR("Steve Underwood "); +MODULE_LICENSE("GPL"); + +module_init(mod_init); +module_exit(mod_exit); diff -Nru dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_sec.c dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_sec.c --- dahdi-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_sec.c 1969-12-31 18:00:00.000000000 -0600 +++ dahdi-cnet-linux-2.2.1-rc2/drivers/dahdi/dahdi_echocan_sec.c 2009-04-29 13:24:04.000000000 -0500 @@ -0,0 +1,356 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * echo.c - An echo cancellor, suitable for electrical and acoustic + *