deiban packaging of linux kernel with sysmocom modifications
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213259 lines
7.8 MiB

diff -Nru linux-source-4.19/drivers/dahdi/Kbuild linux-source-4.19-dahdi/drivers/dahdi/Kbuild
--- linux-source-4.19/drivers/dahdi/Kbuild 1970-01-01 01:00:00.000000000 +0100
+++ linux-source-4.19-dahdi/drivers/dahdi/Kbuild 2020-10-28 00:17:08.667192264 +0100
@@ -0,0 +1,159 @@
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI) += dahdi.o
+#obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DUMMY) += dahdi_dummy.o
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC) += dahdi_dynamic.o
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC_LOC) += dahdi_dynamic_loc.o
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC_ETH) += dahdi_dynamic_eth.o
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_DYNAMIC_ETHMF) += dahdi_dynamic_ethmf.o
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_TRANSCODE) += dahdi_transcode.o
+
+ifdef CONFIG_PCI
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OCT612X) += oct612x/
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCT4XXP) += wct4xxp/
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTC4XXP) += wctc4xxp/
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM24XXP) += wctdm24xxp/
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE13XP) += wcte13xp.o
+
+wcte13xp-objs := wcte13xp-base.o wcxb_spi.o wcxb.o wcxb_flash.o
+CFLAGS_wcte13xp-base.o += -I$(src)/oct612x -I$(src)/oct612x/include -I$(src)/oct612x/octdeviceapi -I$(src)/oct612x/octdeviceapi/oct6100api
+ifeq ($(HOTPLUG_FIRMWARE),yes)
+ CFLAGS_wcte13xp-base.o += -DHOTPLUG_FIRMWARE
+endif
+
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE43X) += wcte43x.o
+
+wcte43x-objs := wcte43x-base.o wcxb_spi.o wcxb.o wcxb_flash.o
+CFLAGS_wcte43x-base.o += -I$(src)/oct612x -I$(src)/oct612x/include -I$(src)/oct612x/octdeviceapi -I$(src)/oct612x/octdeviceapi/oct6100api
+ifeq ($(HOTPLUG_FIRMWARE),yes)
+ CFLAGS_wcte43x-base.o += -DHOTPLUG_FIRMWARE
+endif
+
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCAXX) += wcaxx.o
+
+wcaxx-objs := wcaxx-base.o wcxb_spi.o wcxb.o wcxb_flash.o
+CFLAGS_wcaxx-base.o += -I$(src)/oct612x/ -I$(src)/oct612x/include -I$(src)/oct612x/octdeviceapi -I$(src)/oct612x/octdeviceapi/oct6100api
+ifeq ($(HOTPLUG_FIRMWARE),yes)
+ CFLAGS_wcaxx-base.o += -DHOTPLUG_FIRMWARE
+endif
+
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_VOICEBUS) += voicebus/
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCB4XXP) += wcb4xxp/
+
+
+endif
+
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_XPP) += xpp/
+
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_JPAH) += dahdi_echocan_jpah.o
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_STEVE) += dahdi_echocan_sec.o
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_STEVE2) += dahdi_echocan_sec2.o
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_KB1) += dahdi_echocan_kb1.o
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_MG2) += dahdi_echocan_mg2.o
+
+obj-m += $(DAHDI_MODULES_EXTRA)
+
+# If you want to build OSLEC, include the code in the standard location:
+# drivers/staging/echo . The DAHDI OSLEC echo canceller will be built as
+# well:
+ifneq (,$(wildcard $(src)/../staging/echo/echo.c))
+obj-m += dahdi_echocan_oslec.o
+obj-m += ../staging/echo/echo.o
+endif
+
+CFLAGS_MODULE += -I$(DAHDI_INCLUDE) -I$(src) -Wno-format-truncation
+
+ifndef HOTPLUG_FIRMWARE
+ifneq (,$(filter y m,$(CONFIG_FW_LOADER)))
+HOTPLUG_FIRMWARE := yes
+else
+HOTPLUG_FIRMWARE := no
+endif
+export HOTPLUG_FIRMWARE
+endif
+
+# fix typo present in CentOS and RHEL 2.6.9 kernels
+BAD_KERNELS_VERS := 22 34 34.0.1 34.0.2
+BAD_KERNELS := $(foreach ver,$(BAD_KERNELS_VERS),2.6.9-$(ver).EL 2.6.9-$(ver).ELsmp)
+ifneq (,$(filter $(KVERS),$(BAD_KERNELS)))
+EXTRA_CFLAGS+=-Drw_lock_t=rwlock_t
+endif
+
+# A number of Fedora 10 (9 also?) kernels backported hrtimer to 2.6.27
+# as part of an ALSA backport. TODO: Any better way to detect that?
+ifeq (1,$(shell fgrep -q ' hrtimer_set_expires' include/linux/hrtimer.h 2>/dev/null && echo 1))
+EXTRA_CFLAGS+=-DHAVE_HRTIMER_ACCESSORS=1
+endif
+
+ifeq (1,$(shell fgrep -q 'wait_for_completion_timeout' include/linux/completion.h 2>/dev/null && echo 1))
+CFLAGS_MODULE+=-DHAVE_WAIT_FOR_COMPLETION_TIMEOUT=1
+endif
+
+# In 2.6.18 skb_linearize changed; however, some distros backported the change
+ifneq (,$(wildcard $(srctree)/include/linux/skbuff.h))
+ifeq ($(shell grep "skb_linearize.*(.*, .* gfp)" $(srctree)/include/linux/skbuff.h),)
+CFLAGS_dahdi_dynamic_eth.o := -DNEW_SKB_LINEARIZE
+CFLAGS_dahdi_dynamic_ethmf.o := -DNEW_SKB_LINEARIZE
+endif
+endif
+
+dahdi-objs := dahdi-base.o dahdi-sysfs.o dahdi-sysfs-chan.o dahdi-version.o
+
+###############################################################################
+# Find appropriate ARCH value for VPMADT032 and HPEC binary modules
+###############################################################################
+
+ifeq ($(ARCH),i386)
+ DAHDI_ARCH=x86_32
+else
+ ifeq ($(ARCH),x86_64)
+ DAHDI_ARCH=x86_64
+ else
+ ifeq ($(ARCH),x86)
+ ifeq ($(CONFIG_X86_32),y)
+ DAHDI_ARCH=x86_32
+ else
+ DAHDI_ARCH=x86_64
+ endif
+ else
+$(warning CPU Architecture '$(ARCH)' does not support VPMADT032 or HPEC. Skipping.)
+ endif
+ endif
+endif
+
+###############################################################################
+# VPMADT032 Loader
+###############################################################################
+
+dahdi_vpmadt032_loader-objs := vpmadt032_loader/dahdi_vpmadt032_loader.o
+
+ifneq ($(DAHDI_ARCH),)
+ ifneq ($(wildcard $(src)/vpmadt032_loader/vpmadt032_$(DAHDI_ARCH).o_shipped),)
+ VPMADT032_LOADER_PRESENT=yes
+ dahdi_vpmadt032_loader-objs += vpmadt032_loader/vpmadt032_$(DAHDI_ARCH).o
+ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_VPMADT032_LOADER) += dahdi_vpmadt032_loader.o
+ endif
+endif
+
+###############################################################################
+# HPEC Support
+###############################################################################
+
+dahdi_echocan_hpec-objs := hpec/dahdi_echocan_hpec.o
+CFLAGS_dahdi_echocan_hpec.o := -I$(src)/hpec
+
+ifneq ($(DAHDI_ARCH),)
+ ifneq ($(wildcard $(src)/hpec/hpec_$(DAHDI_ARCH).o_shipped),)
+ HPEC_PRESENT=yes
+ dahdi_echocan_hpec-objs += hpec/hpec_$(DAHDI_ARCH).o
+ endif
+endif
+
+ifeq ($(HPEC_PRESENT),yes)
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_HPEC) += dahdi_echocan_hpec.o
+endif
+
+
+hostprogs-y := makefw
+
+
+
+clean-files := radfw.h
diff -Nru linux-source-4.19/drivers/dahdi/Kconfig linux-source-4.19-dahdi/drivers/dahdi/Kconfig
--- linux-source-4.19/drivers/dahdi/Kconfig 1970-01-01 01:00:00.000000000 +0100
+++ linux-source-4.19-dahdi/drivers/dahdi/Kconfig 2020-10-28 00:17:08.667192264 +0100
@@ -0,0 +1,226 @@
+#
+# DAHDI configuration
+#
+
+menuconfig DAHDI
+ tristate "DAHDI support"
+ select CRC_CCITT
+ default m
+ ---help---
+ DAHDI basic infrastructure.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dahdi.
+
+ If unsure, say Y.
+
+config DAHDI_ECHOCAN
+ tristate "DADHI Echo Cancelers (software)"
+ depends on DAHDI
+ default DAHDI
+
+config DAHDI_ECHOCAN_MG2
+ tristate "DADHI MG2 Echo Canceler"
+ depends on DAHDI_ECHOCAN
+ default DAHDI_ECHOCAN
+ ---help---
+ To compile this driver as a module, choose M here: the
+ module will be called dahdi_echocancel_mg2.
+
+ If unsure, say Y.
+
+config DAHDI_ECHOCAN_KB1
+ tristate "DADHI KB1 Echo Canceler"
+ depends on DAHDI_ECHOCAN
+ default DAHDI_ECHOCAN
+ ---help---
+ To compile this driver as a module, choose M here: the
+ module will be called dahdi_echocancel_kb1.
+
+ If unsure, say Y.
+
+config DAHDI_ECHOCAN_SEC
+ tristate "DADHI SEC Echo Canceler"
+ depends on DAHDI_ECHOCAN
+ default DAHDI_ECHOCAN
+ ---help---
+ To compile this driver as a module, choose M here: the
+ module will be called dahdi_echocancel_sec.
+
+ If unsure, say Y.
+
+config DAHDI_ECHOCAN_SEC2
+ tristate "DADHI SEC2 Echo Canceler"
+ depends on DAHDI_ECHOCAN
+ default DAHDI_ECHOCAN
+ ---help---
+ To compile this driver as a module, choose M here: the
+ module will be called dahdi_echocancel_sec2.
+
+ If unsure, say Y.
+
+config DAHDI_ECHOCAN_HPEC
+ tristate "DADHI HPEC Echo Canceler"
+ depends on DAHDI_ECHOCAN
+ default DAHDI_ECHOCAN
+ ---help---
+ To compile this driver as a module, choose M here: the
+ module will be called dahdi_echocancel_hpec.
+
+ If unsure, say Y.
+
+config DAHDI_WCT4XXP
+ tristate "Digium Wildcard dual- and quad-T1/E1/J1 Support"
+ depends on DAHDI && PCI
+ default DAHDI
+ ---help---
+ This driver provides support for the following Digium
+ Wildcard products:
+
+ * TE205/206/207/210/211/212P (PCI/PCI-X)
+ * TE220 (PCI-E)
+ * TE405/406/407/410/411/412P (PCI/PCI-X)
+ * TE420 (PCI-E)
+
+ To compile this driver as a module, choose M here: the
+ module will be called wct4xxp.
+
+ If unsure, say Y.
+
+config DAHDI_TRANSCODE
+ tristate "DAHDI transcoding support"
+ depends on DAHDI
+ default DAHDI
+ ---help---
+ DAHDI transcoding infrastructure.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dahdi_transcode.
+
+ If unsure, say Y.
+
+config DAHDI_WCTC4XXP
+ tristate "Digium Wildcard TC400B Support"
+ depends on DAHDI_TRANSCODE && PCI
+ default DAHDI
+ ---help---
+ This driver provides support for the Digium Wildcard TC400B.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wctc4xxp.
+
+ If unsure, say Y.
+
+config DAHDI_VOICEBUS
+ tristate "VoiceBus(tm) Interface Library"
+ depends on PCI
+ default DAHDI
+ ---help---
+ This driver provides the common interface for telephony cards that
+ use the VoiceBus(tm) interface. It also contains common supporting
+ libraries for the VPMADT032 hardware echo cancelation module that
+ is available for the VoiceBus cards.
+
+ To compile this driver as a module, choose M here: the module will
+ be called voicebus.
+
+ If unsure, say Y.
+
+config DAHDI_WCTDM24XXP
+ tristate "Digium Wildcard VoiceBus analog card Support"
+ depends on DAHDI && DAHDI_VOICEBUS
+ default DAHDI
+ ---help---
+ This driver provides support for the following Digium
+ Wildcard products:
+
+ * TDM410P (PCI/PCI-X)
+ * AEX410 (PCI-E)
+ * TDM800P (PCI/PCI-X)
+ * AEX800 (PCI-E)
+ * TDM2400P (PCI/PCI-X)
+ * AEX2400 (PCI-E)
+
+ To compile this driver as a module, choose M here: the
+ module will be called wctdm24xxp.
+
+ If unsure, say Y.
+
+config DAHDI_PCIRADIO
+ tristate "PCI Radio Support"
+ depends on DAHDI && PCI
+ default DAHDI
+ ---help---
+ To compile this driver as a module, choose M here: the
+ module will be called pciradio.
+
+ If unsure, say Y.
+
+config DAHDI_DUMMY
+ tristate "Dummy (no hardware) Timing Support"
+ depends on DAHDI
+ default DAHDI
+ ---help---
+ This module provides timing support for applications that
+ use DAHDI conference mixing services, pseudo channels or
+ for other purposes.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dahdi_dummy.
+
+ If unsure, say Y.
+
+config DAHDI_DYNAMIC
+ tristate "Dynamic (virtual) Span Support"
+ depends on DAHDI
+ default DAHDI
+ ---help---
+ This module provides support for virtual spans, which are
+ emulated or provided using various technologies.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dahdi_dynamic.
+
+ If unsure, say Y.
+
+config DAHDI_DYNAMIC_ETH
+ tristate "Ethernet (TDMoE) Span Support"
+ depends on DAHDI && DAHDI_DYNAMIC
+ default DAHDI
+ ---help---
+ This module provides support for spans over Ethernet,
+ using the TDMoE protocol.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dahdi_dynamic_eth.
+
+ If unsure, say Y.
+
+config DAHDI_DYNAMIC_ETHMF
+ tristate "Ethernet (TDMoE) Multi-Frame Span Support"
+ depends on DAHDI && DAHDI_DYNAMIC
+ default DAHDI
+ ---help---
+ This module provides support for spans over Ethernet,
+ using the TDMoE-Multi-Frame protocol.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dahdi_dynamic_ethmf.
+
+ If unsure, say Y.
+
+config DAHDI_DYNAMIC_LOC
+ tristate "Local (loopback) Span Support"
+ depends on DAHDI && DAHDI_DYNAMIC
+ default DAHDI
+ ---help---
+ This module provides support for spans in the local system,
+ primarily used for looping and monitoring other spans.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dahdi_dynamic_loc.
+
+ If unsure, say Y.
+
+
+source "drivers/dahdi/xpp/Kconfig"
diff -Nru linux-source-4.19/drivers/dahdi/adt_lec.c linux-source-4.19-dahdi/drivers/dahdi/adt_lec.c
--- linux-source-4.19/drivers/dahdi/adt_lec.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-source-4.19-dahdi/drivers/dahdi/adt_lec.c 2020-10-28 00:17:08.667192264 +0100
@@ -0,0 +1,73 @@
+/*
+ * ADT Line Echo Canceller Parameter Parsing
+ *
+ * Copyright (C) 2008-2009 Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * 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 <linux/ctype.h>
+
+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 linux-source-4.19/drivers/dahdi/adt_lec.h linux-source-4.19-dahdi/drivers/dahdi/adt_lec.h
--- linux-source-4.19/drivers/dahdi/adt_lec.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-source-4.19-dahdi/drivers/dahdi/adt_lec.h 2020-10-28 00:17:08.667192264 +0100
@@ -0,0 +1,48 @@
+/*
+ * ADT Line Echo Canceller Parameter Parsing
+ *
+ * Copyright (C) 2008 Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * 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 linux-source-4.19/drivers/dahdi/arith.h linux-source-4.19-dahdi/drivers/dahdi/arith.h
--- linux-source-4.19/drivers/dahdi/arith.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-source-4.19-dahdi/drivers/dahdi/arith.h 2020-10-28 00:17:08.667192264 +0100
@@ -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<DAHDI_CHUNKSIZE;x+=8)
+ __ACSS(dst + x, src + x);
+}
+static inline void SCSS(volatile short *dst, const short *src)
+{
+ int x;
+ for (x=0;x<DAHDI_CHUNKSIZE;x+=8)
+ __SCSS(dst + x, src + x);
+}
+#else
+#error No MMX for DAHDI_CHUNKSIZE < 8
+#endif
+#endif
+static inline int CONVOLVE(const int *coeffs, const short *hist, int len)
+{
+ int sum;
+ /* Divide length by 16 */
+ len >>= 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<ntaps;i++) {
+ correction = history[i] * nsuppr;
+ taps[i] += correction;
+ }
+}
+
+static inline void UPDATE2(volatile int *taps, volatile short *taps_short, const short *history, const int nsuppr, const int ntaps)
+{
+ int i;
+ int correction;
+#if 0
+ ntaps >>= 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<ntaps;i++) {
+ correction = history[i] * nsuppr;
+ taps[i] += correction;
+ taps_short[i] = taps[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<len;k++) {
+ if (max < y[k]) {
+ bestpos = k;
+ max = y[k];
+ }
+ }
+ *pos = (len - 1 - bestpos);
+ return max;
+}
+
+
+
+#else
+
+#ifdef DAHDI_CHUNKSIZE
+static inline void ACSS(short *dst, short *src)
+{
+ int x;
+
+ /* Add src to dst with saturation, storing in dst */
+
+#ifdef BFIN
+ for (x = 0; x < DAHDI_CHUNKSIZE; x++)
+ dst[x] = __builtin_bfin_add_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
+}
+
+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<len;x++)
+ sum += (coeffs[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<len;x++)
+ sum += coeffs[x] * hist[x];
+ return sum;
+}
+
+static inline void UPDATE(int *taps, const short *history, const int nsuppr, const int ntaps)
+{
+ int i;
+ int correction;
+ for (i=0;i<ntaps;i++) {
+ correction = history[i] * nsuppr;
+ taps[i] += correction;
+ }
+}
+
+static inline void UPDATE2(int *taps, short *taps_short, const short *history, const int nsuppr, const int ntaps)
+{
+ int i;
+ int correction;
+ for (i=0;i<ntaps;i++) {
+ correction = history[i] * nsuppr;
+ taps[i] += correction;
+ taps_short[i] = taps[i] >> 16;
+ }
+}
+
+static inline short MAX16(const short *y, int len, int *pos)
+{
+ int k;
+ short max = 0;
+ int bestpos = 0;
+ for (k=0;k<len;k++) {
+ if (max < y[k]) {
+ bestpos = k;
+ max = y[k];
+ }
+ }
+ *pos = (len - 1 - bestpos);
+ return max;
+}
+
+#endif /* MMX */
+#endif /* _DAHDI_ARITH_H */
diff -Nru linux-source-4.19/drivers/dahdi/biquad.h linux-source-4.19-dahdi/drivers/dahdi/biquad.h
--- linux-source-4.19/drivers/dahdi/biquad.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-source-4.19-dahdi/drivers/dahdi/biquad.h 2020-10-28 00:17:08.667192264 +0100
@@ -0,0 +1,60 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * biquad.h - General telephony bi-quad section routines (currently this just
+ * handles canonic/type 2 form)
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * 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 linux-source-4.19/drivers/dahdi/dahdi-base.c linux-source-4.19-dahdi/drivers/dahdi/dahdi-base.c
--- linux-source-4.19/drivers/dahdi/dahdi-base.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-source-4.19-dahdi/drivers/dahdi/dahdi-base.c 2020-10-28 00:17:08.667192264 +0100
@@ -0,0 +1,10568 @@
+/*
+ * DAHDI Telephony Interface Driver
+ *
+ * Written by Mark Spencer <markster@digium.com>
+ * Based on previous works, designs, and architectures conceived and
+ * written by Jim Dixon <jim@lambdatel.com>.
+ *
+ * Special thanks to Steve Underwood <steve@coppice.org>
+ * for substantial contributions to signal processing functions
+ * in DAHDI and the Zapata library.
+ *
+ * Yury Bokhoncovich <byg@cf1.ru>
+ * 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 - 2012 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 <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/ctype.h>
+#include <linux/kmod.h>
+#include <linux/moduleparam.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/ktime.h>
+#include <linux/slab.h>
+
+#if defined(HAVE_UNLOCKED_IOCTL) && defined(CONFIG_BKL)
+#include <linux/smp_lock.h>
+#endif
+
+#include <linux/ppp_defs.h>
+
+#include <asm/atomic.h>
+
+#define DAHDI_PRINK_MACROS_USE_debug
+
+/* Grab fasthdlc with tables */
+#define FAST_HDLC_NEED_TABLES
+#include <dahdi/kernel.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+#include <linux/sched/signal.h>
+#endif /* 4.11.0 */
+
+#include "ecdis.h"
+#include "dahdi.h"
+
+#ifdef CONFIG_DAHDI_PPP
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/if_ppp.h>
+#endif
+
+#ifdef CONFIG_DAHDI_NET
+#include <linux/netdevice.h>
+#endif
+
+#include "hpec/hpec_user.h"
+
+#include <stdbool.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 <asm/i387.h>
+#endif
+
+#define hdlc_to_chan(h) (((struct dahdi_hdlc *)(h))->chan)
+#define netdev_to_chan(h) (((struct dahdi_hdlc *)(dev_to_hdlc(h)->priv))->chan)
+#define chan_to_netdev(h) ((h)->hdlcnetdev->netdev)
+
+/* macro-oni for determining a unit (channel) number */
+#define UNIT(file) MINOR(file->f_path.dentry->d_inode->i_rdev)
+
+EXPORT_SYMBOL(dahdi_transcode_fops);
+EXPORT_SYMBOL(dahdi_init_tone_state);
+EXPORT_SYMBOL(dahdi_mf_tone);
+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_rbsbits);
+EXPORT_SYMBOL(dahdi_qevent_nolock);
+EXPORT_SYMBOL(dahdi_qevent_lock);
+EXPORT_SYMBOL(dahdi_hooksig);
+EXPORT_SYMBOL(dahdi_alarm_notify);
+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_echocan_factory);
+EXPORT_SYMBOL(dahdi_unregister_echocan_factory);
+
+EXPORT_SYMBOL(dahdi_set_hpec_ioctl);
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *root_proc_entry;
+#endif
+
+static int deftaps = 64;
+
+int debug;
+#define DEBUG_MAIN (1 << 0)
+#define DEBUG_RBS (1 << 5)
+
+static int hwec_overrides_swec = 1;
+
+/*!
+ * \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_span;
+struct file_operations *dahdi_transcode_fops = NULL;
+
+
+#ifdef CONFIG_DAHDI_CONFLINK
+static struct {
+ int src; /* source conf number */
+ int dst; /* dst conf number */
+} conf_links[DAHDI_MAX_CONF + 1];
+
+static int maxlinks;
+
+#endif
+
+#ifdef CONFIG_DAHDI_CORE_TIMER
+
+static struct core_timer {
+ struct timer_list timer;
+ ktime_t start_interval;
+ unsigned long interval;
+ int dahdi_receive_used;
+ atomic_t count;
+ atomic_t shutdown;
+ atomic_t last_count;
+} core_timer;
+
+#endif /* CONFIG_DAHDI_CORE_TIMER */
+
+
+enum dahdi_digit_mode {
+ DIGIT_MODE_DTMF,
+ DIGIT_MODE_MFR1,
+ DIGIT_MODE_PULSE,
+ DIGIT_MODE_MFR2_FWD,
+ DIGIT_MODE_MFR2_REV,
+};
+
+/* At the end of silence, the tone stops */
+static struct dahdi_tone dtmf_silence = {
+ .tonesamples = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_DEFAULT_DTMF_LENGTH),
+};
+
+/* At the end of silence, the tone stops */
+static struct dahdi_tone mfr1_silence = {
+ .tonesamples = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_DEFAULT_MFR1_LENGTH),
+};
+
+/* At the end of silence, the tone stops */
+static struct dahdi_tone mfr2_silence = {
+ .tonesamples = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_DEFAULT_MFR2_LENGTH),
+};
+
+/* A pause in the dialing */
+static struct dahdi_tone tone_pause = {
+ .tonesamples = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_PAUSE_LENGTH),
+};
+
+static struct dahdi_dialparams global_dialparams = {
+ .dtmf_tonelen = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_DEFAULT_DTMF_LENGTH),
+ .mfv1_tonelen = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_DEFAULT_MFR1_LENGTH),
+ .mfr2_tonelen = DAHDI_MS_TO_SAMPLES(DAHDI_CONFIG_DEFAULT_MFR2_LENGTH),
+};
+
+static DEFINE_MUTEX(global_dialparamslock);
+
+static int dahdi_chan_ioctl(struct file *file, unsigned int cmd, unsigned long data);
+
+#if defined(CONFIG_DAHDI_MMX) || defined(ECHO_CAN_FP)
+#if (defined(CONFIG_X86) && !defined(CONFIG_X86_64)) || defined(CONFIG_I386)
+struct fpu_save_buf {
+ unsigned long cr0;
+ unsigned long fpu_buf[128];
+};
+
+static DEFINE_PER_CPU(struct fpu_save_buf, fpu_buf);
+
+/** dahdi_kernel_fpu_begin() - Save floating point registers
+ *
+ * This function is similar to kernel_fpu_begin() . However it is
+ * designed to work in an interrupt context. Restoring must be done with
+ * dahdi_kernel_fpu_end().
+ *
+ * Furthermore, the whole code between the call to
+ * dahdi_kernel_fpu_begin() and dahdi_kernel_fpu_end() must reside
+ * inside a spinlock. Otherwise the context might be restored to the
+ * wrong process.
+ *
+ * Current implementation is x86/ia32-specific and will not even build on
+ * x86_64)
+ * */
+static inline void dahdi_kernel_fpu_begin(void)
+{
+ struct fpu_save_buf *buf = &__get_cpu_var(fpu_buf);
+ __asm__ __volatile__ ("movl %%cr0,%0; clts" : "=r" (buf->cr0));
+ __asm__ __volatile__ ("fnsave %0" : "=m" (buf->fpu_buf));
+}
+
+/** dahdi_kernel_fpu_end() - restore floating point context
+ *
+ * Must be used with context saved by dahdi_kernel_fpu_begin(). See its
+ * documentation for further information.
+ */
+static inline void dahdi_kernel_fpu_end(void)
+{
+ struct fpu_save_buf *buf = &__get_cpu_var(fpu_buf);
+ __asm__ __volatile__ ("frstor %0" : "=m" (buf->fpu_buf));
+ __asm__ __volatile__ ("movl %0,%%cr0" : : "r" (buf->cr0));
+}
+
+#else /* We haven't fixed FP context saving/restoring yet */
+/* Very strange things can happen when the context is not properly
+ * restored. OTOH, some people do report success with this. Hence we
+ * so far just issue a warning */
+#warning CONFIG_DAHDI_MMX may behave randomly on this platform
+#define dahdi_kernel_fpu_begin kernel_fpu_begin
+#define dahdi_kernel_fpu_end kernel_fpu_end
+#endif
+
+#endif
+
+struct dahdi_timer {
+ spinlock_t lock;
+ 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(dahdi_timers);
+
+static DEFINE_SPINLOCK(dahdi_timer_lock);
+
+#define DEFAULT_TONE_ZONE (-1)
+
+struct dahdi_zone {
+ 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 */
+ struct list_head node;
+ struct kref refcount;
+ const char *name; /* Informational, only */
+ u8 num;
+};
+
+static void tone_zone_release(struct kref *kref)
+{
+ struct dahdi_zone *z = container_of(kref, struct dahdi_zone, refcount);
+ kfree(z->name);
+ kfree(z);
+}
+
+/**
+ * tone_zone_put() - Release the reference on the tone_zone.
+ *
+ * On old kernels, since kref_put does not have a return value, we'll just
+ * always report that we released the memory.
+ *
+ */
+static inline int tone_zone_put(struct dahdi_zone *z)
+{
+ return kref_put(&z->refcount, tone_zone_release);
+}
+
+static inline void tone_zone_get(struct dahdi_zone *z)
+{
+ kref_get(&z->refcount);
+}
+
+static DEFINE_SPINLOCK(zone_lock);
+
+/* The first zone on the list is the default zone. */
+static LIST_HEAD(tone_zones);
+
+static inline struct device *span_device(struct dahdi_span *span)
+{
+ return &span->parent->dev;
+}
+
+/* Protects the span_list and pseudo_chans lists from concurrent access in
+ * process context. The spin_lock is needed to synchronize with the interrupt
+ * handler. */
+static DEFINE_SPINLOCK(chan_lock);
+
+struct pseudo_chan {
+ struct dahdi_chan chan;
+ struct list_head node;
+};
+
+static inline struct pseudo_chan *chan_to_pseudo(struct dahdi_chan *chan)
+{
+ return container_of(chan, struct pseudo_chan, chan);
+}
+
+enum { FIRST_PSEUDO_CHANNEL = 0x8000, };
+/* This list is protected by the chan_lock. */
+static LIST_HEAD(pseudo_chans);
+
+/**
+ * is_pseudo_chan() - By definition pseudo channels are not on a span.
+ */
+static inline bool is_pseudo_chan(const struct dahdi_chan *chan)
+{
+ return (NULL == chan->span);
+}
+
+static DEFINE_MUTEX(registration_mutex);
+static LIST_HEAD(span_list);
+
+static unsigned long
+__for_each_channel(unsigned long (*func)(struct dahdi_chan *chan,
+ unsigned long data),
+ unsigned long data)
+{
+ int res;
+ struct dahdi_span *s;
+ struct pseudo_chan *pseudo;
+
+ list_for_each_entry(s, &span_list, spans_node) {
+ unsigned long x;
+ for (x = 0; x < s->channels; x++) {
+ struct dahdi_chan *const chan = s->chans[x];
+ res = func(chan, data);
+ if (res)
+ return res;
+ }
+ }
+
+ list_for_each_entry(pseudo, &pseudo_chans, node) {
+ res = func(&pseudo->chan, data);
+ if (res)
+ return res;
+ }
+ return 0;
+}
+
+/**
+ * _chan_from_num - Lookup a channel
+ *
+ * Must be called with the registration_mutex held.
+ *
+ */
+static struct dahdi_chan *_chan_from_num(unsigned int channo)
+{
+ struct dahdi_span *s;
+ struct pseudo_chan *pseudo;
+
+ if (channo >= FIRST_PSEUDO_CHANNEL) {
+ list_for_each_entry(pseudo, &pseudo_chans, node) {
+ if (pseudo->chan.channo == channo)
+ return &pseudo->chan;
+ }
+ return NULL;
+ }
+
+ /* When searching for the channel amongst the spans, we can use the
+ * fact that channels on a span must be numbered consecutively to skip
+ * checking each individual channel. */
+ list_for_each_entry(s, &span_list, spans_node) {
+ unsigned int basechan;
+ struct dahdi_chan *chan;
+
+ if (unlikely(!s->channels))
+ continue;
+
+ basechan = s->chans[0]->channo;
+ if (channo >= (basechan + s->channels))
+ continue;
+
+ /* Since all the spans should be on the list in sorted order,
+ * if channo is less than base chan, the caller must be
+ * looking for a channel that has already been removed. */
+ if (unlikely(channo < basechan))
+ return NULL;
+
+ chan = s->chans[channo - basechan];
+ WARN_ON(chan->channo != channo);
+ return chan;
+ }
+
+ return NULL;
+}
+
+static struct dahdi_chan *chan_from_num(unsigned int channo)
+{
+ struct dahdi_chan *chan;
+ mutex_lock(&registration_mutex);
+ chan = _chan_from_num(channo);
+ mutex_unlock(&registration_mutex);
+ return chan;
+}
+
+static inline struct dahdi_chan *chan_from_file(struct file *file)
+{
+ return (file->private_data) ?
+ file->private_data : chan_from_num(UNIT(file));
+}
+
+/**
+ * _find_span() - Find a span by span number.
+ *
+ * Must be called with registration_mutex held.
+ *
+ */
+static struct dahdi_span *_find_span(int spanno)
+{
+ struct dahdi_span *s;
+ list_for_each_entry(s, &span_list, spans_node) {
+ if (s->spanno == spanno) {
+ return s;
+ }
+ }
+ return NULL;
+}
+/**
+ * span_find_and_get() - Search for the span by number, and if found take out
+ * a reference on it.
+ *
+ * When you are no longer using the returned pointer, you must release it with
+ * a put_span call.
+ *
+ */
+static struct dahdi_span *span_find_and_get(int spanno)
+{
+ struct dahdi_span *found;
+
+ mutex_lock(&registration_mutex);
+ found = _find_span(spanno);
+ if (found && !get_span(found))
+ found = NULL;
+ mutex_unlock(&registration_mutex);
+ return found;
+}
+
+static unsigned int span_count(void)
+{
+ unsigned int count = 0;
+ struct dahdi_span *s;
+ unsigned long flags;
+ spin_lock_irqsave(&chan_lock, flags);
+ list_for_each_entry(s, &span_list, spans_node)
+ ++count;
+ spin_unlock_irqrestore(&chan_lock, flags);
+ return count;
+}
+
+static inline bool can_provide_timing(const struct dahdi_span *const s)
+{
+ return !s->cannot_provide_timing;
+}
+
+static int maxconfs;
+
+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];
+
+#define NUM_SIGS 10
+
+static DEFINE_SPINLOCK(ecfactory_list_lock);
+
+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;
+ struct ecfactory *new;
+
+ WARN_ON(!ec->owner);
+
+ new = kzalloc(sizeof(*new), GFP_KERNEL);
+ if (!new)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&new->list);
+
+ spin_lock(&ecfactory_list_lock);
+
+ /* make sure it isn't already registered */
+ list_for_each_entry(cur, &ecfactory_list, list) {
+ if (cur->ec == ec) {
+ spin_unlock(&ecfactory_list_lock);
+ kfree(new);
+ return -EPERM;
+ }
+ }
+
+ new->ec = ec;
+ list_add_tail(&new->list, &ecfactory_list);
+
+ spin_unlock(&ecfactory_list_lock);
+
+ return 0;
+}
+
+void dahdi_unregister_echocan_factory(const struct dahdi_echocan_factory *ec)
+{
+ struct ecfactory *cur, *next;
+
+ spin_lock(&ecfactory_list_lock);
+
+ list_for_each_entry_safe(cur, next, &ecfactory_list, list) {
+ if (cur->ec == ec) {
+ list_del(&cur->list);
+ kfree(cur);
+ break;
+ }
+ }
+
+ spin_unlock(&ecfactory_list_lock);
+}
+
+/* Is this span our syncronization master? */
+int dahdi_is_sync_master(const struct dahdi_span *span)
+{
+ return span == master_span;
+}
+
+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));
+}
+
+/**
+ * is_chan_dacsed() - True if chan is sourcing it's data from another channel.
+ *
+ */
+static inline bool is_chan_dacsed(const struct dahdi_chan *const chan)
+{
+ return (NULL != chan->dacs_chan);
+}
+
+/**
+ * can_dacs_chans() - Returns true if it may be possible to dacs two channels.
+ *
+ */
+static bool can_dacs_chans(struct dahdi_chan *dst, struct dahdi_chan *src)
+{
+ if (src && dst && src->span && dst->span && src->span->ops &&
+ dst->span->ops && src->span->ops->dacs &&
+ (src->span->ops->dacs == dst->span->ops->dacs))
+ return true;
+ else
+ return false;
+}
+
+/**
+ * dahdi_chan_dacs() - Cross (or uncross) connect two channels.
+ * @dst: Channel on which to transmit the src data.
+ * @src: NULL to disable cross connect, otherwise the source of the
+ * data.
+ *
+ * This allows those boards that support it to cross connect one channel to
+ * another in hardware. If the cards cannot be crossed, uncross the
+ * destination channel by default..
+ *
+ */
+static int dahdi_chan_dacs(struct dahdi_chan *dst, struct dahdi_chan *src)
+{
+ int ret = 0;
+ if (can_dacs_chans(dst, src))
+ ret = dst->span->ops->dacs(dst, src);
+ else if (dst->span && dst->span->ops->dacs)
+ ret = dst->span->ops->dacs(dst, NULL);
+ return ret;
+}
+
+static void dahdi_disable_dacs(struct dahdi_chan *chan)
+{
+ dahdi_chan_dacs(chan, NULL);
+}
+
+/*!
+ * \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_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->ops->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 */
+}
+
+enum spantypes dahdi_str2spantype(const char *name)
+{
+ if (strcasecmp("FXS", name) == 0)
+ return SPANTYPE_ANALOG_FXS;
+ else if (strcasecmp("FXO", name) == 0)
+ return SPANTYPE_ANALOG_FXO;
+ else if (strcasecmp("ANALOG_MIXED", name) == 0)
+ return SPANTYPE_ANALOG_MIXED;
+ else if (strcasecmp("E1", name) == 0)
+ return SPANTYPE_DIGITAL_E1;
+ else if (strcasecmp("T1", name) == 0)
+ return SPANTYPE_DIGITAL_T1;
+ else if (strcasecmp("J1", name) == 0)
+ return SPANTYPE_DIGITAL_J1;
+ else if (strcasecmp("BRI_NT", name) == 0)
+ return SPANTYPE_DIGITAL_BRI_NT;
+ else if (strcasecmp("BRI_TE", name) == 0)
+ return SPANTYPE_DIGITAL_BRI_TE;
+ else if (strcasecmp("BRI_SOFT", name) == 0)
+ return SPANTYPE_DIGITAL_BRI_SOFT;
+ else if (strcasecmp("DYNAMIC", name) == 0)
+ return SPANTYPE_DIGITAL_DYNAMIC;
+ else
+ return SPANTYPE_INVALID;
+}
+EXPORT_SYMBOL(dahdi_str2spantype);
+
+const char *dahdi_spantype2str(enum spantypes st)
+{
+ switch (st) {
+ case SPANTYPE_ANALOG_FXS: return "FXS";
+ case SPANTYPE_ANALOG_FXO: return "FXO";
+ case SPANTYPE_ANALOG_MIXED: return "ANALOG_MIXED";
+ case SPANTYPE_DIGITAL_E1: return "E1";
+ case SPANTYPE_DIGITAL_T1: return "T1";
+ case SPANTYPE_DIGITAL_J1: return "J1";
+ case SPANTYPE_DIGITAL_BRI_NT: return "BRI_NT";
+ case SPANTYPE_DIGITAL_BRI_TE: return "BRI_TE";
+ case SPANTYPE_DIGITAL_BRI_SOFT: return "BRI_SOFT";
+ case SPANTYPE_DIGITAL_DYNAMIC: return "DYNAMIC";
+ default:
+ case SPANTYPE_INVALID: return "INVALID";
+ };
+}
+EXPORT_SYMBOL(dahdi_spantype2str);
+
+
+const char *dahdi_lineconfig_bit_name(int lineconfig_bit)
+{
+ static const char * const table[] = {
+ /* These apply to T1 */
+ [4] = "D4",
+ [5] = "ESF",
+ [6] = "AMI",
+ [7] = "B8ZS",
+ /* These apply to E1 */
+ [8] = "CCS",
+ [9] = "HDB3",
+ [10] = "CRC4",
+ /* These apply to BRI */
+ [11] = "NTTE",
+ [12] = "TERM",
+ /* Finish */
+ [16] = "NOTOPEN",
+ };
+ if (lineconfig_bit < 0 || lineconfig_bit >= ARRAY_SIZE(table))
+ return NULL;
+ return table[lineconfig_bit];
+}
+EXPORT_SYMBOL(dahdi_lineconfig_bit_name);
+
+ssize_t lineconfig_str(int lineconfig, char buf[], size_t size)
+{
+ int framing_bit = 0;
+ int coding_bit = 0;
+ int crc4_bit = 0;
+ int len = 0;
+ int bit;
+ bool written = false;
+
+ for (bit = 4; bit <= 12; bit++) {
+ int mask = (1 << bit);
+ if (!(lineconfig & mask))
+ continue;
+ if (!framing_bit) {
+ switch (mask) {
+ case DAHDI_CONFIG_B8ZS:
+ case DAHDI_CONFIG_AMI:
+ case DAHDI_CONFIG_HDB3:
+ framing_bit = bit;
+ len += snprintf(buf + len, size, "%s%s",
+ (written) ? "/" : "",
+ dahdi_lineconfig_bit_name(bit));
+ written = true;
+ }
+ }
+ if (!coding_bit) {
+ switch (mask) {
+ case DAHDI_CONFIG_ESF:
+ case DAHDI_CONFIG_D4:
+ case DAHDI_CONFIG_CCS:
+ coding_bit = bit;
+ len += snprintf(buf + len, size, "%s%s",
+ (written) ? "/" : "",
+ dahdi_lineconfig_bit_name(bit));
+ written = true;
+ }
+ }
+ if (!crc4_bit && mask == DAHDI_CONFIG_CRC4) {
+ crc4_bit = bit;
+ len += snprintf(buf + len, size, "%s%s",
+ (written) ? "/" : "",
+ dahdi_lineconfig_bit_name(bit));
+ written = true;
+ }
+ }
+ return len;
+}
+EXPORT_SYMBOL(lineconfig_str);
+
+#ifdef CONFIG_PROC_FS
+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";
+ }
+}
+
+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 " : "");
+
+ return len;
+}
+
+/*
+ * Sequential proc interface
+ */
+
+static void seq_fill_alarm_string(struct seq_file *sfile, int alarms)
+{
+ char tmp[70];
+
+ if (fill_alarm_string(tmp, sizeof(tmp), alarms))
+ seq_printf(sfile, "%s", tmp);
+}
+
+static int dahdi_seq_show(struct seq_file *sfile, void *data)
+{
+ long spanno = (long)sfile->private;
+ int x;
+ struct dahdi_span *s;
+
+ s = span_find_and_get(spanno);
+ if (!s)
+ return -ENODEV;
+
+ if (s->name)
+ seq_printf(sfile, "Span %d: %s ", s->spanno, s->name);
+ if (s->desc)
+ seq_printf(sfile, "\"%s\"", s->desc);
+ else
+ seq_printf(sfile, "\"\"");
+
+ if (dahdi_is_sync_master(s))
+ seq_printf(sfile, " (MASTER)");
+
+ if (s->lineconfig) {
+ char tmpbuf[20];
+ lineconfig_str(s->lineconfig, tmpbuf, sizeof(tmpbuf));
+ seq_printf(sfile, " %s", tmpbuf);
+ }
+
+ seq_printf(sfile, " ");
+
+ /* list alarms */
+ seq_fill_alarm_string(sfile, s->alarms);
+ if (s->syncsrc &&
+ (s->syncsrc == s->spanno))
+ seq_printf(sfile, "ClockSource ");
+ seq_printf(sfile, "\n");
+ if (s->count.bpv)
+ seq_printf(sfile, "\tBPV count: %d\n", s->count.bpv);
+ if (s->count.crc4)
+ seq_printf(sfile, "\tCRC4 error count: %d\n", s->count.crc4);
+ if (s->count.ebit)
+ seq_printf(sfile, "\tE-bit error count: %d\n", s->count.ebit);
+ if (s->count.fas)
+ seq_printf(sfile, "\tFAS error count: %d\n", s->count.fas);
+ if (s->parent->irqmisses)
+ seq_printf(sfile, "\tIRQ misses: %d\n", s->parent->irqmisses);
+ if (s->count.timingslips)
+ seq_printf(sfile, "\tTiming slips: %d\n", s->count.timingslips);
+ seq_printf(sfile, "\n");
+
+ for (x = 0; x < s->channels; x++) {
+ struct dahdi_chan *chan = s->chans[x];
+
+ if (chan->name)
+ seq_printf(sfile, "\t%4d %s ", chan->channo,
+ chan->name);
+
+ if (chan->sig) {
+ if (chan->sig == DAHDI_SIG_SLAVE)
+ seq_printf(sfile, "%s ",
+ sigstr(chan->master->sig));
+ else {
+ seq_printf(sfile, "%s ", sigstr(chan->sig));
+ if (chan->nextslave &&
+ (chan->master->channo == chan->channo))
+ seq_printf(sfile, "Master ");
+ }
+ } else if (!chan->sigcap) {
+ seq_printf(sfile, "Reserved ");
+ }
+
+ if (test_bit(DAHDI_FLAGBIT_OPEN, &chan->flags))
+ seq_printf(sfile, "(In use) ");
+
+#ifdef OPTIMIZE_CHANMUTE
+ if (chan->chanmute)
+ seq_printf(sfile, "(no pcm) ");
+#endif
+
+ seq_fill_alarm_string(sfile, chan->chan_alarms);
+
+ mutex_lock(&chan->mutex);
+ if (chan->ec_factory) {
+ seq_printf(sfile, "(EC: %s - %s) ",
+ chan->ec_factory->get_name(chan),
+ chan->ec_state ? "ACTIVE" : "INACTIVE");
+ }
+ mutex_unlock(&chan->mutex);
+
+ seq_printf(sfile, "\n");
+ }
+ put_span(s);
+ return 0;
+}
+
+static int dahdi_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dahdi_seq_show, PDE_DATA(inode));
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
+static const struct proc_ops dahdi_proc_ops = {
+ .proc_open = dahdi_proc_open,
+ .proc_read = seq_read,
+ .proc_lseek = seq_lseek,
+ .proc_release = single_release,
+};
+#else
+static const struct file_operations dahdi_proc_ops = {
+ .owner = THIS_MODULE,
+ .open = dahdi_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
+#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<DAHDI_MAX_CONF;x++) {
+ if (!confrev[x])
+ return x;
+ }
+ return -1;
+}
+
+static void recalc_maxconfs(void)
+{
+ int x;
+
+ for (x = DAHDI_MAX_CONF - 1; x > 0; x--) {
+ if (confrev[x]) {
+ maxconfs = x + 1;
+ return;
+ }
+ }
+
+ maxconfs = 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 unsigned long _chan_in_conf(struct dahdi_chan *chan, unsigned long x)
+{
+ const int confmode = chan->confmode & DAHDI_CONF_MODE_MASK;
+ return (chan && (chan->confna == x) &&
+ (confmode == DAHDI_CONF_CONF ||
+ confmode == DAHDI_CONF_CONFANN ||
+ confmode == DAHDI_CONF_CONFMON ||
+ confmode == DAHDI_CONF_CONFANNMON ||
+ confmode == DAHDI_CONF_REALANDPSEUDO)) ? 1 : 0;
+}
+
+#ifdef CONFIG_DAHDI_CONFLINK
+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;
+}
+#endif
+
+static void dahdi_check_conf(int x)
+{
+ unsigned long res;
+ unsigned long flags;
+#ifdef CONFIG_DAHDI_CONFLINK
+ int i;
+#endif
+
+ /* return if no valid conf number */
+ if (x <= 0)
+ return;
+
+ /* Return if there is no alias */
+ if (!confalias[x])
+ return;
+
+ spin_lock_irqsave(&chan_lock, flags);
+ res = __for_each_channel(_chan_in_conf, x);
+ spin_unlock_irqrestore(&chan_lock, flags);
+ if (res)
+ 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();
+
+#ifdef CONFIG_DAHDI_CONFLINK
+ /* And unlink it from any conflinks */
+ for (i = DAHDI_MAX_CONF - 1; i > 0; i--) {
+ if (conf_links[i].src == x)
+ conf_links[i].src = 0;
+ if (conf_links[i].dst == x)
+ conf_links[i].dst = 0;
+ }
+ recalc_maxlinks();
+#endif
+}
+
+/* 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 */
+ wake_up_interruptible(&chan->waitq);
+
+ 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);
+}
+
+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;
+
+ 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)
+{
+ 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];
+}
+
+/* Push something onto the queue, or assume what
+ is there is valid if data is NULL */
+static int __buf_push(struct confq *q, const u_char *data)
+{
+ int oldinbuf = q->inbuf;
+ 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;
+ char *c;
+ const char *d;
+ char modname_buf[128] = "dahdi_echocan_";
+ unsigned int tried_once = 0;
+
+ name_upper = kmalloc(strlen(name) + 1, GFP_KERNEL);
+ if (!name_upper)
+ return NULL;
+
+ for (c = name_upper, d = name; *d; c++, d++) {
+ *c = toupper(*d);
+ }
+
+ *c = '\0';
+
+retry:
+ spin_lock(&ecfactory_list_lock);
+
+ list_for_each_entry(cur, &ecfactory_list, list) {
+ if (!strcmp(name_upper, cur->ec->get_name(NULL))) {
+ if (try_module_get(cur->ec->owner)) {
+ spin_unlock(&ecfactory_list_lock);
+ kfree(name_upper);
+ return cur->ec;
+ } else {
+ spin_unlock(&ecfactory_list_lock);
+ kfree(name_upper);
+ return NULL;
+ }
+ }
+ }
+
+ spin_unlock(&ecfactory_list_lock);
+
+ if (tried_once) {
+ kfree(name_upper);
+ 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);
+}
+
+/**
+ * is_gain_allocated() - True if gain tables were dynamically allocated.
+ * @chan: The channel to check.
+ */
+static inline bool is_gain_allocated(const struct dahdi_chan *chan)
+{
+ return (chan->rxgain && (chan->rxgain != defgain));
+}
+
+static const char *hwec_def_name = "HWEC";
+static const char *hwec_get_name(const struct dahdi_chan *chan)
+{
+ if (chan && chan->span && chan->span->ops->echocan_name)
+ return chan->span->ops->echocan_name(chan);
+ else
+ return hwec_def_name;
+}
+
+static int hwec_echocan_create(struct dahdi_chan *chan,
+ struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p,
+ struct dahdi_echocan_state **ec)
+{
+ if (chan->span && chan->span->ops->echocan_create)
+ return chan->span->ops->echocan_create(chan, ecp, p, ec);
+ else
+ return -ENODEV;
+}
+
+static const struct dahdi_echocan_factory hwec_factory = {
+ .get_name = hwec_get_name,
+ .owner = THIS_MODULE,
+ .echocan_create = hwec_echocan_create,
+};
+
+/**
+ * dahdi_enable_hw_preechocan - Let the board driver enable hwpreec if possible.
+ * @chan: The channel to monitor.
+ *
+ * Returns 0 on success, if there is a software echocanceler attached on
+ * the channel, or the span does not have an enable_hw_preechocan callback.
+ * Otherwise an error code.
+ *
+ */
+static int dahdi_enable_hw_preechocan(struct dahdi_chan *chan)
+{
+ int res;
+
+ mutex_lock(&chan->mutex);
+ if (chan->ec_factory != &hwec_factory)
+ res = -ENODEV;
+ else
+ res = 0;
+ mutex_unlock(&chan->mutex);
+
+ if (-ENODEV == res)
+ return 0;
+
+ if (chan->span->ops->enable_hw_preechocan)
+ return chan->span->ops->enable_hw_preechocan(chan);
+ else
+ return 0;
+}
+
+/**
+ * dahdi_disable_hw_preechocan - Disable any hardware pre echocan monitoring.
+ * @chan: The channel to stop monitoring.
+ *
+ * Give the board driver the option to free any resources needed to monitor
+ * the preechocan stream.
+ *
+ */
+static void dahdi_disable_hw_preechocan(struct dahdi_chan *chan)
+{
+ if (chan->span->ops->disable_hw_preechocan)
+ chan->span->ops->disable_hw_preechocan(chan);
+}
+
+/*
+ * close_channel - close the channel, resetting any channel variables
+ * @chan: the dahdi_chan to close
+ *
+ * This function is called before either the parent span is linked into the
+ * span list, or for pseudos, place on the psuedo_list. Therefore, this
+ * function nor it's callers should depend on the channel being findable
+ * via those methods.
+ */
+static void close_channel(struct dahdi_chan *chan)
+{
+ unsigned long flags;
+ const 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();
+
+ if (chan->conf_chan &&
+ ((DAHDI_CONF_MONITOR_RX_PREECHO == chan->confmode) ||
+ (DAHDI_CONF_MONITOR_TX_PREECHO == chan->confmode) ||
+ (DAHDI_CONF_MONITORBOTH_PREECHO == chan->confmode))) {
+ void *readchunkpreec;
+
+ spin_lock_irqsave(&chan->conf_chan->lock, flags);
+ readchunkpreec = chan->conf_chan->readchunkpreec;
+ chan->conf_chan->readchunkpreec = NULL;
+ spin_unlock_irqrestore(&chan->conf_chan->lock, flags);
+
+ if (readchunkpreec) {
+ dahdi_disable_hw_preechocan(chan->conf_chan);
+ kfree(readchunkpreec);
+ }
+ }
+
+ /* 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) {
+ struct dahdi_zone *zone = chan->curzone;
+ chan->curzone = NULL;
+ tone_zone_put(zone);
+ }
+ chan->cadencepos = 0;
+ chan->pdialcount = 0;
+ dahdi_hangup(chan);
+ chan->itimerset = chan->itimer = 0;
+ chan->pulsecount = 0;
+ chan->pulsetimer = 0;
+ chan->ringdebtimer = 0;
+ 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;
+ chan->confna = 0;
+ chan->confmode = 0;
+ if ((chan->sig & __DAHDI_SIG_DACS) != __DAHDI_SIG_DACS)
+ chan->dacs_chan = NULL;
+
+ chan->confmute = 0;
+ chan->gotgs = 0;
+ reset_conf(chan);
+ chan->dacs_chan = NULL;
+
+ if (is_gain_allocated(chan))
+ rxgain = chan->rxgain;
+
+ chan->rxgain = defgain;
+ chan->txgain = defgain;
+ chan->eventinidx = chan->eventoutidx = 0;
+ chan->flags &= ~(DAHDI_FLAG_LOOPED | DAHDI_FLAG_LINEAR | DAHDI_FLAG_PPP | DAHDI_FLAG_SIGFREEZE);
+
+ dahdi_set_law(chan, DAHDI_LAW_DEFAULT);
+
+ memset(chan->conflast, 0, sizeof(chan->conflast));
+ memset(chan->conflast1, 0, sizeof(chan->conflast1));
+ memset(chan->conflast2, 0, sizeof(chan->conflast2));
+
+ if (chan->span && oldconf)
+ dahdi_disable_dacs(chan);
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ if (ec_state) {
+ ec_state->ops->echocan_free(chan, ec_state);
+ release_echocan(ec_current);
+ }
+
+ /* release conference resource, if any to release */
+ if (oldconf)
+ dahdi_check_conf(oldconf);
+
+ if (rxgain)
+ kfree(rxgain);
+
+ if (readchunkpreec) {
+ dahdi_disable_hw_preechocan(chan);
+ 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 dahdi_ioctl_freezone(unsigned long data)
+{
+ struct dahdi_zone *z;
+ struct dahdi_zone *found = NULL;
+ int num;
+
+ if (get_user(num, (int __user *) data))
+ return -EFAULT;
+
+ spin_lock(&zone_lock);
+ list_for_each_entry(z, &tone_zones, node) {
+ if (z->num == num) {
+ found = z;
+ break;
+ }
+ }
+ if (found) {
+ list_del(&found->node);
+ }
+ spin_unlock(&zone_lock);
+
+ if (found) {
+ if (debug) {
+ module_printk(KERN_INFO,
+ "Unregistering tone zone %d (%s)\n",
+ found->num, found->name);
+ }
+ tone_zone_put(found);
+ }
+ return 0;
+}
+
+static int dahdi_register_tone_zone(struct dahdi_zone *zone)
+{
+ struct dahdi_zone *cur;
+ int res = 0;
+
+ kref_init(&zone->refcount);
+ spin_lock(&zone_lock);
+ list_for_each_entry(cur, &tone_zones, node) {
+ if (cur->num == zone->num) {
+ res = -EINVAL;
+ break;
+ }
+ }
+ if (!res) {
+ list_add_tail(&zone->node, &tone_zones);
+ if (debug) {
+ module_printk(KERN_INFO,
+ "Registered tone zone %d (%s)\n",
+ zone->num, zone->name);
+ }
+ }
+ spin_unlock(&zone_lock);
+
+ 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;
+}
+
+/**
+ * stop_tone - Stops any tones on a channel.
+ *
+ * Must be called with chan->lock held.
+ *
+ */
+static inline int stop_tone(struct dahdi_chan *chan)
+{
+ return start_tone(chan, -1);
+}
+
+static int set_tone_zone(struct dahdi_chan *chan, int zone)
+{
+ int res = 0;
+ struct dahdi_zone *cur;
+ struct dahdi_zone *z;
+ unsigned long flags;
+
+ z = NULL;
+ spin_lock(&zone_lock);
+ if ((DEFAULT_TONE_ZONE == zone) && !list_empty(&tone_zones)) {
+ z = list_entry(tone_zones.next, struct dahdi_zone, node);
+ tone_zone_get(z);
+ } else {
+ list_for_each_entry(cur, &tone_zones, node) {
+ if (cur->num != (u8)zone)
+ continue;
+ z = cur;
+ tone_zone_get(z);
+ break;
+ }
+ }
+ spin_unlock(&zone_lock);
+
+ if (unlikely(!z))
+ return -ENODATA;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ stop_tone(chan);
+
+ if (chan->curzone) {
+ struct dahdi_zone *zone = chan->curzone;
+ chan->curzone = NULL;
+ tone_zone_put(zone);
+ }
+ chan->curzone = z;
+ memcpy(chan->ringcadence, z->ringcadence, sizeof(chan->ringcadence));
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ return res;
+}
+
+static void dahdi_set_law(struct dahdi_chan *chan, int law)
+{
+ if (DAHDI_LAW_DEFAULT == 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
+ }
+}
+
+/**
+ * __dahdi_init_chan - Initialize the channel data structures.
+ * @chan: The channel to initialize
+ *
+ */
+static void __dahdi_init_chan(struct dahdi_chan *chan)
+{
+ might_sleep();
+
+ spin_lock_init(&chan->lock);
+ mutex_init(&chan->mutex);
+ init_waitqueue_head(&chan->waitq);
+ if (!chan->master)
+ chan->master = chan;
+ if (!chan->readchunk)
+ chan->readchunk = chan->sreadchunk;
+ if (!chan->writechunk)
+ chan->writechunk = chan->swritechunk;
+ chan->rxgain = NULL;
+ chan->txgain = NULL;
+ close_channel(chan);
+}
+
+/**
+ * dahdi_chan_reg - Mark the channel registered.
+ *
+ * This must be called after close channel during registration, normally
+ * covered by the call to __dahdi_init_chan, to avoid "HDLC hangage"
+ */
+static inline void dahdi_chan_reg(struct dahdi_chan *chan)
+{
+ set_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags);
+}
+
+/**
+ * dahdi_lboname() - Convert line build out number to string.
+ *
+ */
+const char *dahdi_lboname(int lbo)
+{
+ /* names of tx level settings */
+ static const char *const 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)"
+ };
+
+ if ((lbo < 0) || (lbo > 7))
+ return "Unknown";
+ return dahdi_txlevelnames[lbo];
+}
+EXPORT_SYMBOL(dahdi_lboname);
+
+#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;x<ss->writen[oldbuf];x++)
+ printk("%02x ", ss->writebuf[oldbuf][x]);
+ printk("\n");
+#endif
+}
+#endif
+
+#ifdef CONFIG_DAHDI_NET
+static inline struct net_device_stats *hdlc_stats(struct net_device *dev)
+{
+ return &dev->stats;
+}
+
+static int dahdi_net_open(struct net_device *dev)
+{
+ int res = hdlc_open(dev);
+ struct dahdi_chan *ms = netdev_to_chan(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;
+
+ 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 (!dahdi_have_netdev(ms)) {
+ module_printk(KERN_NOTICE, "%s is not a net device!\n", ms->name);
+ return -EINVAL;
+ }
+ ms->txbufpolicy = 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(chan_to_netdev(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;
+ return 0;
+}
+
+static int dahdi_net_stop(struct net_device *dev)
+{
+ hdlc_device *h = dev_to_hdlc(dev);
+ struct dahdi_hdlc *hdlc = h->priv;
+
+ struct dahdi_chan *ms = hdlc_to_chan(hdlc);
+ if (!ms) {
+ module_printk(KERN_NOTICE, "dahdi_net_stop: nothing??\n");
+ return 0;
+ }
+ if (!dahdi_have_netdev(ms)) {
+ module_printk(KERN_NOTICE, "dahdi_net_stop: %s is not a net device!\n", ms->name);
+ return 0;
+ }
+ /* Not much to do here. Just deallocate the buffers */
+ netif_stop_queue(chan_to_netdev(ms));
+ dahdi_reallocbufs(ms, 0, 0);
+ hdlc_close(dev);
+ return 0;
+}
+
+/* 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;
+}
+
+static struct dahdi_hdlc *dahdi_hdlc_alloc(void)
+{
+ return kzalloc(sizeof(struct dahdi_hdlc), GFP_KERNEL);
+}
+
+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 = netdev_to_chan(dev);
+ struct net_device_stats *stats = hdlc_stats(dev);
+
+ 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;x<skb->len;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(chan_to_netdev(ss));
+ }
+ if (ss->outwritebuf < 0) {
+ /* Let the interrupt handler know there's
+ some space for us */
+ ss->outwritebuf = oldbuf;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
+ netif_trans_update(dev);
+#else
+ dev->trans_start = jiffies;
+#endif
+ 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;
+}
+
+static int dahdi_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ return hdlc_ioctl(dev, ifr, cmd);
+}
+
+#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;x<skb->len + 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
+
+/**
+ * is_monitor_mode() - True if the confmode indicates that one channel is monitoring another.
+ *
+ */
+static bool is_monitor_mode(int confmode)
+{
+ confmode &= DAHDI_CONF_MODE_MASK;
+ if ((confmode == DAHDI_CONF_MONITOR) ||
+ (confmode == DAHDI_CONF_MONITORTX) ||
+ (confmode == DAHDI_CONF_MONITORBOTH) ||
+ (confmode == DAHDI_CONF_MONITOR_RX_PREECHO) ||
+ (confmode == DAHDI_CONF_MONITOR_TX_PREECHO) ||
+ (confmode == DAHDI_CONF_MONITORBOTH_PREECHO)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static unsigned long _chan_cleanup(struct dahdi_chan *pos, unsigned long data)
+{
+ unsigned long flags;
+ struct dahdi_chan *const chan = (struct dahdi_chan *)data;
+ /* Remove anyone pointing to us as master
+ and make them their own thing */
+ if (pos->master == chan)
+ pos->master = pos;
+
+ if (((pos->confna == chan->channo) &&
+ is_monitor_mode(pos->confmode)) ||
+ (pos->dacs_chan == chan) ||
+ (pos->conf_chan == chan)) {
+ /* Take them out of conference with us */
+ /* release conference resource if any */
+ if (pos->confna)
+ dahdi_check_conf(pos->confna);
+
+ dahdi_disable_dacs(pos);
+ spin_lock_irqsave(&pos->lock, flags);
+ pos->confna = 0;
+ pos->_confn = 0;
+ pos->confmode = 0;
+ pos->conf_chan = NULL;
+ pos->dacs_chan = NULL;
+ spin_unlock_irqrestore(&pos->lock, flags);
+ }
+
+ return 0;
+}
+
+static const struct file_operations nodev_fops;
+
+static void dahdi_chan_unreg(struct dahdi_chan *chan)
+{
+ unsigned long flags;
+
+ might_sleep();
+
+ /* In the case of surprise removal of hardware, make sure any open
+ * file handles to this channel are disassociated with the actual
+ * dahdi_chan. */
+ if (chan->file) {
+ module_printk(KERN_NOTICE,
+ "%s: surprise removal: chan %d\n",
+ __func__, chan->channo);
+ chan->file->private_data = NULL;
+ chan->file->f_op = &nodev_fops;
+ /*
+ * From now on, any file_operations for this device
+ * would call the nodev_fops methods.
+ */
+ }
+
+ mutex_lock(&chan->mutex);
+ release_echocan(chan->ec_factory);
+ chan->ec_factory = NULL;
+ mutex_unlock(&chan->mutex);
+
+#ifdef CONFIG_DAHDI_NET
+ if (dahdi_have_netdev(chan)) {
+ unregister_hdlc_device(chan->hdlcnetdev->netdev);
+ free_netdev(chan->hdlcnetdev->netdev);
+ kfree(chan->hdlcnetdev);
+ chan->hdlcnetdev = NULL;
+ }
+#endif
+ clear_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags);
+
+#ifdef CONFIG_DAHDI_PPP
+ if (chan->ppp) {
+ module_printk(KERN_NOTICE, "HUH??? PPP still attached??\n");
+ }
+#endif
+ spin_lock_irqsave(&chan_lock, flags);
+ __for_each_channel(_chan_cleanup, (unsigned long)chan);
+ spin_unlock_irqrestore(&chan_lock, flags);
+
+ chan->channo = -1;
+
+ /* Let processeses out of their poll_wait() */
+ wake_up_interruptible(&chan->waitq);
+
+ /* release tone_zone */
+ close_channel(chan);
+
+ if (chan->file) {
+ if (test_bit(DAHDI_FLAGBIT_OPEN, &chan->flags)) {
+ clear_bit(DAHDI_FLAGBIT_OPEN, &chan->flags);
+ if (chan->span) {
+ if (chan->span->ops->close) {
+ int res;
+
+ res = chan->span->ops->close(chan);
+ if (res)
+ module_printk(KERN_NOTICE,
+ "%s: close() failed: %d\n",
+ __func__, res);
+ }
+ }
+ }
+ msleep(20);
+ /*
+ * FIXME: THE BIG SLEEP above, is hiding a terrible
+ * race condition:
+ * - the module_put() ahead, would allow the low-level driver
+ * to free the channel.
+ * - We should make sure no-one reference this channel
+ * from now on.
+ */
+ if (chan->span)
+ put_span(chan->span);
+ }
+}
+
+static ssize_t dahdi_chan_read(struct file *file, char __user *usrbuf,
+ size_t count, loff_t *ppos)
+{
+ struct dahdi_chan *chan = file->private_data;
+ 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 (unlikely(!chan)) {
+ /*
+ * This should never happen. Surprise device removal
+ * should lead us to the nodev_* file_operations
+ */
+ msleep(5);
+ module_printk(KERN_ERR, "%s: NODEV\n", __func__);
+ return -ENODEV;
+ }
+
+ if (unlikely(count < 1))
+ return -EINVAL;
+
+ if (unlikely(!test_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags)))
+ return -ENODEV;
+
+ 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;
+ spin_unlock_irqrestore(&chan->lock, flags);
+ if (res >= 0)
+ break;
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ /* Wake up when data is available or when the board driver
+ * unregistered the channel. */
+ rv = wait_event_interruptible(chan->waitq,
+ (!chan->file->private_data || chan->outreadbuf > -1));
+ if (rv)
+ return rv;
+ if (unlikely(!chan->file->private_data))
+ return -ENODEV;
+ }
+ amnt = count;
+ 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->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 __user *usrbuf,
+ size_t count, loff_t *ppos)
+{
+ unsigned long flags;
+ struct dahdi_chan *chan = file->private_data;
+ int res, amnt, oldbuf, rv, x;
+
+ /* Make sure count never exceeds 65k, and make sure it's unsigned */
+ count &= 0xffff;
+
+ if (unlikely(!chan)) {
+ /*