- move branch mqueue to HEAD

This commit is contained in:
Karsten Keil 2006-03-06 12:58:31 +00:00
parent 56f18961c0
commit b267c6033f
33 changed files with 14689 additions and 182 deletions

77
Makefile Normal file
View File

@ -0,0 +1,77 @@
BASEDIR=$(shell pwd)
INSTALL_PREFIX := /
export INSTALL_PREFIX
#PATH to linux source/headers
#LINUX=/usr/src/linux
MODS=/lib/modules/$(shell uname -r)
LINUX=$(MODS)/build
LINUX_SOURCE=$(MODS)/source
MISDNDIR=$(BASEDIR)
MISDN_SRC=$(MISDNDIR)/drivers/isdn/hardware/mISDN
########################################
# USER CONFIGS END
########################################
CONFIGS+=CONFIG_MISDN_DRV=m CONFIG_MISDN_DSP=m
CONFIGS+=CONFIG_MISDN_HFCMULTI=m
CONFIGS+=CONFIG_MISDN_HFCPCI=m
CONFIGS+=CONFIG_MISDN_HFCUSB=m
CONFIGS+=CONFIG_MISDN_XHFC=m
CONFIGS+=CONFIG_MISDN_HFCMINI=m
CONFIGS+=CONFIG_MISDN_W6692=m
CONFIGS+=CONFIG_MISDN_SPEEDFAX=m
CONFIGS+=CONFIG_MISDN_AVM_FRITZ=m
MINCLUDES+=-I$(MISDNDIR)/include
all: test_old_misdn
@echo
@echo "Makeing mISDN"
@echo "============="
@echo
cp $(MISDNDIR)/drivers/isdn/hardware/mISDN/Makefile.v2.6 $(MISDNDIR)/drivers/isdn/hardware/mISDN/Makefile
export MINCLUDES=$(MISDNDIR)/include ; make -C $(LINUX) SUBDIRS=$(MISDN_SRC) modules $(CONFIGS)
install: all
cd $(LINUX) ; make SUBDIRS=$(MISDN_SRC) modules_install
cp $(MISDNDIR)/include/linux/*.h $(INSTALL_PREFIX)/usr/include/linux/
install -m755 misdn-init /etc/init.d/
depmod
test_old_misdn:
@if echo -ne "#include <linux/mISDNif.h>" | gcc -C -E - 2>/dev/null 1>/dev/null ; then \
if ! echo -ne "#include <linux/mISDNif.h>\n#ifndef FLG_MSG_DOWN\n#error old mISDNif.h\n#endif\n" | gcc -C -E - 2>/dev/null 1>/dev/null ; then \
echo -ne "\n!!You should remove the following files:\n\n$(LINUX)/include/linux/mISDNif.h\n$(LINUX)/include/linux/isdn_compat.h\n/usr/include/linux/mISDNif.h\n/usr/include/linux/isdn_compat.h\n\nIn order to upgrade to the mqueue branch\n\n"; \
echo -ne "I can do that for you, just type: make force\n\n" ; \
exit 1; \
fi ;\
fi
.PHONY: install all clean
force:
rm -f $(LINUX)/include/linux/mISDNif.h
rm -f $(LINUX)/include/linux/isdn_compat.h
rm -f /usr/include/linux/mISDNif.h
rm -f /usr/include/linux/isdn_compat.h
clean:
rm -rf drivers/isdn/hardware/mISDN/*.o
rm -rf drivers/isdn/hardware/mISDN/*.ko
rm -rf *~
find . -iname ".*.cmd" -exec rm -rf {} \;
find . -iname ".*.d" -exec rm -rf {} \;
find . -iname "*.mod.c" -exec rm -rf {} \;
find . -iname "*.mod" -exec rm -rf {} \;

55
README.misdn-init Normal file
View File

@ -0,0 +1,55 @@
misdn-init: init-script to auto-configure and load the mISDN kernel drivers
===========================================================================
This script makes it easy to configure and activate mISDN compatible
adapter cards. It scans an eyecandy config file named misdn-init.conf
for your card and port settings, then it loads the driver modules properly.
The misdn-init.conf can also be autogenerated by the misdn-init script.
---------------------------------------------------------------------------
Usage: /etc/init.d/misdn-init start|stop|restart|config|scan|help
--start scan /etc/misdn-init.conf and load the mISDN drivers
--stop unload the mISDN drivers
--restart see stop, then start
--config scan your PCI bus for mISDN compatible hardware and generate
a /etc/misdn-init.conf
--scan scan your PCI bus for mISDN compatible hardware and print
the results to the console
--help print the usage info
---------------------------------------------------------------------------
* Here is a quick overview on how to use misdn-init:
1) Get and install misdn-init:
$ wget http://www.beronet.com/downloads/chan_misdn/stable/chan_misdn.tar.gz
$ tar zxf chan_misdn.tar.gz
$ (as root) cp chan_misdn/misdn-init /etc/init.d/misdn-init
2) Let misdn-init scan your PCI bus for mISDN compatible hardware and write
the results into /etc/misdn-init.conf:
$ (as root) /etc/init.d/misdn-init config
3) (optional) Edit /etc/misdn-init.conf and set everything the way you want it.
This file is heavily commented, hence it should be self-explaining.
4) (optional, but recommended) Add misdn-init to your run level.
This is distribution dependend. Here an example for a debian system:
ATTENTION: If you have services in your runlevels that depend
on mISDN, make sure that misdn-init starts before, and
stops after them (this is done by changing the values
that are set to 60 in this example, more info: read the
manpage for update-rc.d).
$ (as root) update-rc.d misdn-init start 60 2 3 4 5 . stop 60 0 1 6 .
5) Run the following to start mISDN:
$ (as root) /etc/init.d/misdn-init start
---------------------------------------------------------------------------
* Report Bugs:
If you experience any bugs or have a feature request, please visit:
www.isdn4linux.de/mantis

View File

@ -0,0 +1,88 @@
/* $Id$
*
* Author (c) Karsten Keil <kkeil@suse.de>
*
* This file is released under the GPLv2
*
*/
#include <linux/module.h>
#include "channel.h"
#include "layer1.h"
int
mISDN_initchannel(channel_t *ch, ulong prop, int maxlen)
{
ch->log = kmalloc(MAX_LOG_SPACE, GFP_ATOMIC);
if (!ch->log) {
printk(KERN_WARNING
"mISDN: No memory for channel log\n");
return(-ENOMEM);
}
ch->Flags = prop;
ch->maxlen = maxlen;
ch->hw = NULL;
ch->rx_skb = NULL;
ch->tx_skb = NULL;
ch->tx_idx = 0;
ch->next_skb = NULL;
return(0);
}
int
mISDN_freechannel(channel_t *ch)
{
if (ch->tx_skb) {
dev_kfree_skb(ch->tx_skb);
ch->tx_skb = NULL;
}
if (ch->rx_skb) {
dev_kfree_skb(ch->rx_skb);
ch->rx_skb = NULL;
}
if (ch->next_skb) {
dev_kfree_skb(ch->next_skb);
ch->next_skb = NULL;
}
kfree(ch->log);
ch->log = NULL;
return(0);
}
/* need called with HW lock */
int
mISDN_setpara(channel_t *ch, mISDN_stPara_t *stp)
{
if (!stp) { // clear parameters
ch->maxlen = 0;
ch->up_headerlen = 0;
return(0);
}
if (stp->up_headerlen)
ch->up_headerlen = stp->up_headerlen;
if (stp->maxdatalen) {
if (ch->maxlen < stp->maxdatalen) {
if (ch->rx_skb) {
struct sk_buff *skb;
skb = alloc_skb(stp->maxdatalen +
ch->up_headerlen, GFP_ATOMIC);
if (!skb) {
int_errtxt("no skb for %d+%d", stp->maxdatalen, ch->up_headerlen);
return(-ENOMEM);
}
skb_reserve(skb, ch->up_headerlen);
memcpy(skb_put(skb, ch->rx_skb->len),
ch->rx_skb->data, ch->rx_skb->len);
dev_kfree_skb(ch->rx_skb);
ch->rx_skb = skb;
}
}
ch->maxlen = stp->maxdatalen;
}
return(0);
}
EXPORT_SYMBOL(mISDN_initchannel);
EXPORT_SYMBOL(mISDN_freechannel);
EXPORT_SYMBOL(mISDN_setpara);

View File

@ -0,0 +1,148 @@
/* $Id$
*
* Basic declarations for a mISDN HW channel
*
* Author (c) Karsten Keil <kkeil@suse.de>
*
* This file is released under the GPLv2
*
*/
#ifndef MISDN_CHANNEL_H
#define MISDN_CHANNEL_H
#include <linux/mISDNif.h>
#include <linux/timer.h>
#include <linux/skbuff.h>
#include "helper.h"
#ifdef MISDN_MEMDEBUG
#include "memdbg.h"
#endif
#define MAX_DFRAME_LEN_L1 300
#define MAX_MON_FRAME 32
#define MAX_LOG_SPACE 2048
#define MISDN_COPY_SIZE 32
/* channel->Flags bit field */
#define FLG_TX_BUSY 0 // tx_buf in use
#define FLG_TX_NEXT 1 // next_skb in use
#define FLG_L1_BUSY 2 // L1 is permanent busy
#define FLG_USED 5 // channel is in use
#define FLG_ACTIVE 6 // channel is activated
#define FLG_BUSY_TIMER 7
/* channel type */
#define FLG_DCHANNEL 8 // channel is D-channel
#define FLG_BCHANNEL 9 // channel is B-channel
#define FLG_ECHANNEL 10 // channel is E-channel
#define FLG_TRANSPARENT 12 // channel use transparent data
#define FLG_HDLC 13 // channel use hdlc data
#define FLG_L2DATA 14 // channel use L2 DATA primitivs
#define FLG_ORIGIN 15 // channel is on origin site
/* channel specific stuff */
/* arcofi specific */
#define FLG_ARCOFI_TIMER 16
#define FLG_ARCOFI_ERROR 17
/* isar specific */
#define FLG_INITIALIZED 16
#define FLG_DLEETX 17
#define FLG_LASTDLE 18
#define FLG_FIRST 19
#define FLG_LASTDATA 20
#define FLG_NMD_DATA 21
#define FLG_FTI_RUN 22
#define FLG_LL_OK 23
#define FLG_LL_CONN 24
#define FLG_DTMFSEND 25
#define MSK_INIT_DCHANNEL ((1<<FLG_DCHANNEL)|(1<<FLG_HDLC))
#define MSK_INIT_BCHANNEL (1<<FLG_BCHANNEL)
#define MSK_INIT_ECHANNEL (1<<FLG_ECHANNEL)
typedef struct _channel_t {
mISDNinstance_t inst;
int channel;
/* basic properties */
u_long Flags;
u_int type;
u_int state;
/* HW access */
u_char (*read_reg) (void *, u_char);
void (*write_reg) (void *, u_char, u_char);
void (*read_fifo) (void *, u_char *, int);
void (*write_fifo) (void *, u_char *, int);
void *hw;
struct timer_list timer;
/* receive data */
struct sk_buff *rx_skb;
int maxlen;
int up_headerlen;
/* send data */
struct sk_buff *next_skb;
struct sk_buff *tx_skb;
int tx_idx;
/* debug */
int debug;
char *log;
/* statistics */
int err_crc;
int err_tx;
int err_rx;
} channel_t;
extern int mISDN_initchannel(channel_t *, ulong, int);
extern int mISDN_freechannel(channel_t *);
extern int mISDN_setpara(channel_t *, mISDN_stPara_t *);
static inline void
queue_ch_frame(channel_t *ch, u_int pr, int dinfo, struct sk_buff *skb)
{
int err;
pr |= test_bit(FLG_L2DATA, &ch->Flags) ? DL_DATA : PH_DATA;
if (!skb)
err = mISDN_queue_data(&ch->inst, FLG_MSG_UP, pr, dinfo, 0, NULL, ch->up_headerlen);
else
err = mISDN_queueup_newhead(&ch->inst, 0, pr, dinfo, skb);
if (unlikely(err)) {
int_errtxt("err=%d", err);
if (skb)
dev_kfree_skb(skb);
}
}
static inline int
channel_senddata(channel_t *ch, int di, struct sk_buff *skb)
{
/* HW lock must be obtained */
/* check oversize */
if (skb->len <= 0) {
printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__);
return(-EINVAL);
}
if (skb->len > ch->maxlen) {
printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
__FUNCTION__, skb->len, ch->maxlen);
return(-EINVAL);
}
/* check for pending next_skb */
if (ch->next_skb) {
printk(KERN_WARNING "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n",
__FUNCTION__, skb->len, ch->next_skb->len);
return(-EBUSY);
}
if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
test_and_set_bit(FLG_TX_NEXT, &ch->Flags);
ch->next_skb = skb;
return(0);
} else {
/* write to fifo */
ch->tx_skb = skb;
ch->tx_idx = 0;
queue_ch_frame(ch, CONFIRM, di, NULL);
return(skb->len);
}
}
#endif

View File

@ -0,0 +1,347 @@
#ifndef _ZAPTEL_ARITH_H
#define _ZAPTEL_ARITH_H
/*
* Handy add/subtract functions to operate on chunks of shorts.
* Feel free to add customizations for additional architectures
*
*/
#ifdef CONFIG_ZAPTEL_MMX
#ifdef ZT_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"
#if 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"
#if CLOBBERMMX
, "%mm0", "%mm1", "%mm2", "%mm3"
#endif
);
}
#if (ZT_CHUNKSIZE == 8)
#define ACSS(a,b) __ACSS(a,b)
#define SCSS(a,b) __SCSS(a,b)
#elif (ZT_CHUNKSIZE > 8)
static inline void ACSS(volatile short *dst, const short *src)
{
int x;
for (x=0;x<ZT_CHUNKSIZE;x+=8)
__ACSS(dst + x, src + x);
}
static inline void SCSS(volatile short *dst, const short *src)
{
int x;
for (x=0;x<ZT_CHUNKSIZE;x+=8)
__SCSS(dst + x, src + x);
}
#else
#error No MMX for ZT_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 ZT_CHUNKSIZE
static inline void ACSS(short *dst, short *src)
{
int x,sum;
/* Add src to dst with saturation, storing in dst */
for (x=0;x<ZT_CHUNKSIZE;x++) {
sum = dst[x]+src[x];
if (sum > 32767)
sum = 32767;
else if (sum < -32768)
sum = -32768;
dst[x] = sum;
}
}
static inline void SCSS(short *dst, short *src)
{
int x,sum;
/* Add src to dst with saturation, storing in dst */
for (x=0;x<ZT_CHUNKSIZE;x++) {
sum = dst[x]-src[x];
if (sum > 32767)
sum = 32767;
else if (sum < -32768)
sum = -32768;
dst[x] = sum;
}
}
#endif /* ZT_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 /* _ZAPTEL_ARITH_H */

View File

@ -0,0 +1,73 @@
/*
* 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.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
typedef struct
{
int32_t gain;
int32_t a1;
int32_t a2;
int32_t b1;
int32_t b2;
int32_t z1;
int32_t z2;
} biquad2_state_t;
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 ------------------------------------------------------------*/

View File

@ -0,0 +1,317 @@
/*
*
* Simple but fast Echo cancellation for mISDN_dsp.
*
* Copyright Chrisian Richter
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include "layer1.h"
#include "helper.h"
#include "debug.h"
#include "dsp.h"
#include <asm/i387.h>
/*
* how this works:
*
*
*
*/
void bchdev_echocancel_chunk(dsp_t* dev, uint8_t *rxchunk, uint8_t *txchunk, uint16_t size);
int bchdev_echocancel_activate(dsp_t* dev, int deftaps, int train);
void bchdev_echocancel_deactivate(dsp_t* dev);
void
dsp_cancel_tx(dsp_t *dsp, u8 *data, int len)
{
if (!dsp ) return ;
if (!data) return;
if (dsp->txbuflen + len < ECHOCAN_BUFLEN) {
memcpy(&dsp->txbuf[dsp->txbuflen],data,len);
dsp->txbuflen+=len;
} else {
static int i=0;
if(i==4000) {
printk("ECHOCAN: i:%d TXBUF Overflow txbuflen:%d txcancellen:%d\n", i, dsp->txbuflen,len);
i=0;
}
i+=len;
dsp->txbuflen=0;
}
}
void
dsp_cancel_rx(dsp_t *dsp, u8 *data, int len)
{
if (!dsp ) return ;
if (!data) return;
if (len <= dsp->txbuflen) {
char tmp[ECHOCAN_BUFLEN];
int delta=dsp->txbuflen-len;
memcpy(tmp,&dsp->txbuf[len],delta);
kernel_fpu_begin();
bchdev_echocancel_chunk(dsp, data, dsp->txbuf, len);
kernel_fpu_end();
memcpy(dsp->txbuf,tmp,delta);
dsp->txbuflen=delta;
} else {
static int i=0;
if(i==4000) {
printk("ECHOCAN: i:%d TXBUF Underrun txbuflen:%d rxcancellen:%d\n",i,dsp->txbuflen,len);
i=0;
}
i+=len;
}
}
int
dsp_cancel_init(dsp_t *dsp, int deftaps, int training, int delay)
{
if (!dsp) return -1;
printk("DSP_CANCEL_INIT called\n");
if (delay < 0)
{
printk("Disabling EC\n");
dsp->cancel_enable = 0;
dsp->txbuflen=0;
bchdev_echocancel_deactivate(dsp);
return(0);
}
dsp->txbuflen=0;
dsp->rxbuflen=0;
bchdev_echocancel_activate(dsp,deftaps, training);
printk("Enabling EC\n");
dsp->cancel_enable = 1;
return(0);
}
/*****************************************************/
#define __ECHO_STATE_MUTE (1 << 8)
#define ECHO_STATE_IDLE (0)
#define ECHO_STATE_PRETRAINING (1 | (__ECHO_STATE_MUTE))
#define ECHO_STATE_STARTTRAINING (2 | (__ECHO_STATE_MUTE))
#define ECHO_STATE_AWAITINGECHO (3 | (__ECHO_STATE_MUTE))
#define ECHO_STATE_TRAINING (4 | (__ECHO_STATE_MUTE))
#define ECHO_STATE_ACTIVE (5)
#define AMI_MASK 0x55
/** @return string of given echo cancellation state */
char* bchdev_echocancel_statestr(uint16_t state)
{
switch(state) {
case ECHO_STATE_IDLE:
return "idle";
break;
case ECHO_STATE_PRETRAINING:
return "pre-training";
break;
case ECHO_STATE_STARTTRAINING:
return "transmit impulse";
break;
case ECHO_STATE_AWAITINGECHO:
return "awaiting echo";
break;
case ECHO_STATE_TRAINING:
return "training start";
break;
case ECHO_STATE_ACTIVE:
return "training finished";
break;
default:
return "unknown";
}
}
/** Changes state of echo cancellation to given state */
void bchdev_echocancel_setstate(dsp_t* dev, uint16_t state)
{
char* statestr = bchdev_echocancel_statestr(state);
printk("bchdev: echo cancel state %d (%s)\n", state & 0xff, statestr);
if (state == ECHO_STATE_ACTIVE)
printk("bchdev: %d taps trained\n", dev->echolastupdate);
dev->echostate = state;
}
static int buf_size=0;
static int ec_timer=2000;
//static int ec_timer=1000;
/** Activates echo cancellation for the given bch_dev, device must have been locked before! */
int bchdev_echocancel_activate(dsp_t* dev, int deftaps, int training)
{
int taps;
if (! dev) return -EINVAL;
if (dev->ec && dev->ecdis_rd && dev->ecdis_wr) {
// already active
return 0;
}
if (deftaps>0) {
taps=deftaps;
} else {
taps=128;
}
switch (buf_size) {
case 0: taps += 0; break;
case 1: taps += 256-128; break;
case 2: taps += 512-128; break;
default: taps += 1024-128;
}
if (!dev->ec) dev->ec = echo_can_create(taps, 0);
if (!dev->ec) {
return -ENOMEM;
}
dev->echolastupdate = 0;
if (!training) {
dev->echotimer=0;
bchdev_echocancel_setstate(dev, ECHO_STATE_IDLE);
} else {
if (training<10)
training= ec_timer;
dev->echotimer = training;
bchdev_echocancel_setstate(dev, ECHO_STATE_PRETRAINING);
}
if (!dev->ecdis_rd) dev->ecdis_rd = kmalloc(sizeof(echo_can_disable_detector_state_t), GFP_KERNEL);
if (!dev->ecdis_rd) {
kfree(dev->ec); dev->ec = NULL;
return -ENOMEM;
}
echo_can_disable_detector_init(dev->ecdis_rd);
if (!dev->ecdis_wr) dev->ecdis_wr = kmalloc(sizeof(echo_can_disable_detector_state_t), GFP_KERNEL);
if (!dev->ecdis_wr) {
kfree(dev->ec); dev->ec = NULL;
kfree(dev->ecdis_rd); dev->ecdis_rd = NULL;
return -ENOMEM;
}
echo_can_disable_detector_init(dev->ecdis_wr);
return 0;
}
/** Deactivates echo cancellation for the given bch_dev, device must have been locked before! */
void bchdev_echocancel_deactivate(dsp_t* dev)
{
if (! dev) return;
//chan_misdn_log("bchdev: deactivating echo cancellation on port=%04x, chan=%02x\n", dev->stack->port, dev->channel);
if (dev->ec) echo_can_free(dev->ec);
dev->ec = NULL;
dev->echolastupdate = 0;
dev->echotimer = 0;
bchdev_echocancel_setstate(dev, ECHO_STATE_IDLE);
if (dev->ecdis_rd) kfree(dev->ecdis_rd);
dev->ecdis_rd = NULL;
if (dev->ecdis_wr) kfree(dev->ecdis_wr);
dev->ecdis_wr = NULL;
}
/** Processes one TX- and one RX-packet with echocancellation */
void bchdev_echocancel_chunk(dsp_t* dev, uint8_t *rxchunk, uint8_t *txchunk, uint16_t size)
{
int16_t rxlin, txlin;
uint16_t pos;
/* Perform echo cancellation on a chunk if requested */
if (dev->ec) {
if (dev->echostate & __ECHO_STATE_MUTE) {
if (dev->echostate == ECHO_STATE_STARTTRAINING) {
// Transmit impulse now
txchunk[0] = dsp_audio_s16_to_law[16384 & 0xffff];
memset(txchunk+1, 0, size-1);
bchdev_echocancel_setstate(dev, ECHO_STATE_TRAINING); //AWAITINGECHO);
} else {
// train the echo cancellation
for (pos = 0; pos < size; pos++) {
rxlin = dsp_audio_law_to_s32[rxchunk[pos]];
txlin = dsp_audio_law_to_s32[txchunk[pos]];
if (dev->echostate == ECHO_STATE_PRETRAINING) {
if (dev->echotimer <= 0) {
dev->echotimer = 0;
bchdev_echocancel_setstate(dev, ECHO_STATE_STARTTRAINING);
} else {
dev->echotimer--;
}
}
if ((dev->echostate == ECHO_STATE_AWAITINGECHO) && (txlin > 8000)) {
dev->echolastupdate = 0;
bchdev_echocancel_setstate(dev, ECHO_STATE_TRAINING);
}
if (dev->echostate == ECHO_STATE_TRAINING) {
if (echo_can_traintap(dev->ec, dev->echolastupdate++, rxlin)) {
bchdev_echocancel_setstate(dev, ECHO_STATE_ACTIVE);
}
}
rxchunk[pos] = dsp_silence;
txchunk[pos] = dsp_silence;
}
}
} else {
for (pos = 0; pos < size; pos++) {
rxlin = dsp_audio_law_to_s32[rxchunk[pos]];
txlin = dsp_audio_law_to_s32[txchunk[pos]];
if (echo_can_disable_detector_update(dev->ecdis_rd, rxlin) ||
echo_can_disable_detector_update(dev->ecdis_wr, txlin)) {
bchdev_echocancel_deactivate(dev);
printk("EC: Disable tone detected\n");
return ;
} else {
rxlin = echo_can_update(dev->ec, txlin, rxlin);
rxchunk[pos] = dsp_audio_s16_to_law[rxlin & 0xffff];
}
}
}
}
}
/******************************************************/

View File

@ -0,0 +1,118 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* ec_disable_detector.h - A detector which should eventually meet the
* G.164/G.165 requirements for detecting the
* 2100Hz echo cancellor disable tone.
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2001 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "dsp_biquad.h"
typedef struct
{
biquad2_state_t notch;
int notch_level;
int channel_level;
int tone_present;
int tone_cycle_duration;
int good_cycles;
int hit;
} echo_can_disable_detector_state_t;
#define FALSE 0
#define TRUE (!FALSE)
static inline void echo_can_disable_detector_init (echo_can_disable_detector_state_t *det)
{
/* Elliptic notch */
/* This is actually centred at 2095Hz, but gets the balance we want, due
to the asymmetric walls of the notch */
biquad2_init (&det->notch,
(int32_t) (-0.7600000*32768.0),
(int32_t) (-0.1183852*32768.0),
(int32_t) (-0.5104039*32768.0),
(int32_t) ( 0.1567596*32768.0),
(int32_t) ( 1.0000000*32768.0));
det->channel_level = 0;
det->notch_level = 0;
det->tone_present = FALSE;
det->tone_cycle_duration = 0;
det->good_cycles = 0;
det->hit = 0;
}
/*- End of function --------------------------------------------------------*/
static inline int echo_can_disable_detector_update (echo_can_disable_detector_state_t *det,
int16_t amp)
{
int16_t notched;
notched = biquad2 (&det->notch, amp);
/* Estimate the overall energy in the channel, and the energy in
the notch (i.e. overall channel energy - tone energy => noise).
Use abs instead of multiply for speed (is it really faster?).
Damp the overall energy a little more for a stable result.
Damp the notch energy a little less, so we don't damp out the
blip every time the phase reverses */
det->channel_level += ((abs(amp) - det->channel_level) >> 5);
det->notch_level += ((abs(notched) - det->notch_level) >> 4);
if (det->channel_level > 280)
{
/* There is adequate energy in the channel. Is it mostly at 2100Hz? */
if (det->notch_level*6 < det->channel_level)
{
/* The notch says yes, so we have the tone. */
if (!det->tone_present)
{
/* Do we get a kick every 450+-25ms? */
if (det->tone_cycle_duration >= 425*8
&&
det->tone_cycle_duration <= 475*8)
{
det->good_cycles++;
if (det->good_cycles > 2)
det->hit = TRUE;
}
det->tone_cycle_duration = 0;
}
det->tone_present = TRUE;
}
else
{
det->tone_present = FALSE;
}
det->tone_cycle_duration++;
}
else
{
det->tone_present = FALSE;
det->tone_cycle_duration = 0;
det->good_cycles = 0;
}
return det->hit;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/

View File

@ -0,0 +1,574 @@
/*
* ECHO_CAN_KB1
*
* by Kris Boutilier
*
* Based upon mech2.h
*
* Copyright (C) 2002, Digium, Inc.
*
* This program is free software and may be used and
* distributed according to the terms of the GNU
* General Public License, incorporated herein by
* reference.
*
* 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/
*
*/
#ifndef _MARK2_ECHO_H
#define _MARK2_ECHO_H
#define EC_TYPE "KB1"
#ifdef __KERNEL__
#include <linux/kernel.h>
#include <linux/slab.h>
#define MALLOC(a) kmalloc((a), GFP_KERNEL)
#define FREE(a) kfree(a)
#else
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#define MALLOC(a) malloc(a)
#define FREE(a) free(a)
#endif
/* 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 "dsp_arith.h"
/* Bring in definitions for the various constants and thresholds */
#include "dsp_kb1ec_const.h"
#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;
/* Echo canceller definition */
struct echo_can_state {
/* 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
};
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 echo_can_state *ec, int N, int maxy, int maxu)
{
void *ptr = ec;
unsigned long tmp;
/* Double-word align past end of state */
ptr += sizeof(struct echo_can_state);
tmp = (unsigned long)ptr;
tmp += 3;
tmp &= ~3L;
ptr = (void *)tmp;
/* Reset parameters */
ec->N_d = N;
ec->beta2_i = DEFAULT_BETA1_I;
/* Allocate coefficient memory */
ec->a_i = ptr;
ptr += (sizeof(int) * ec->N_d);
ec->a_s = ptr;
ptr += (sizeof(short) * ec->N_d);
/* Reset Y circular buffer (short version) */
init_cb_s(&ec->y_s, maxy, ptr);
ptr += (sizeof(short) * (maxy) * 2);
/* Reset Sigma circular buffer (short version for FIR filter) */
init_cb_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr);
ptr += (sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) * 2);
init_cb_s(&ec->u_s, maxu, ptr);
ptr += (sizeof(short) * maxu * 2);
/* Allocate a buffer for the reference signal power computation */
init_cb_s(&ec->y_tilde_s, ec->N_d, ptr);
/* Reset the absolute time index */
ec->i_d = (int)0;
/* Reset the power computations (for y and u) */
ec->Ly_i = DEFAULT_CUTOFF_I;
ec->Lu_i = DEFAULT_CUTOFF_I;
#ifdef MEC2_STATS
/* set the identity */
ec->id = (int)&ptr;
/* Reset performance stats */
ec->cntr_nearend_speech_frames = (int)0;
ec->cntr_residualcorrected_frames = (int)0;
ec->cntr_residualcorrected_framesskipped = (int)0;
ec->cntr_coeff_updates = (int)0;
ec->cntr_coeff_missedupdates = (int)0;
ec->avg_Lu_i_toolow = (int)0;
ec->avg_Lu_i_ok = (int)0;
#endif
/* Reset the near-end speech detector */
ec->s_tilde_i = (int)0;
ec->y_tilde_i = (int)0;
ec->HCNTR_d = (int)0;
}
static inline void echo_can_free(struct echo_can_state *ec)
{
FREE(ec);
}
static inline short echo_can_update(struct echo_can_state *ec, 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 */
ec->y_tilde_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1 )) >> DEFAULT_ALPHA_YT_I;
/* Add the new sample to the power estimate accumulator */
ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I;
/* Push a copy of the new sample into its circular buffer */
add_cc_s(&ec->y_s, iref);
/* eq. (2): compute r in fixed-point */
rs = CONVOLVE2(ec->a_s,
ec->y_s.buf_d + ec->y_s.idx_d,
ec->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(&ec->u_s, u);
/* Update the Near-end hybrid signal circular buffers and accumulators */
/* ------------------------------------------------------------------- */
/* Delete the oldest sample from the power estimate accumulator */
ec->s_tilde_i -= abs(get_cc_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1 ));
/* Add the new sample to the power estimate accumulator */
ec->s_tilde_i += abs(isig);
/* Push a copy of the new sample into it's circular buffer */
add_cc_s(&ec->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(&ec->y_tilde_s, ec->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 (!ec->HCNTR_d) {
Py_i = (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * (ec->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 (ec->start_speech_d != 0 ){
if ( ec->i_d > (DEFAULT_T0 + ec->start_speech_d)*(SAMPLE_FREQ) ){
ec->beta2_d = max_cc_float(MIN_BETA, DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((ec->i_d/(float)SAMPLE_FREQ) - DEFAULT_T0 - ec->start_speech_d)));
}
} else {
ec->beta2_d = DEFAULT_BETA1;
}
#endif
/* Fixed point, inverted */
ec->beta2_i = DEFAULT_BETA1_I;
/* Fixed point version, inverted */
two_beta_i = (ec->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 */
ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ;
/* Add the new sample to the power estimate accumulator */
ec->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 */
ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ;
/* Add the new sample to the power estimate accumulator */
ec->Ly_i += abs(iref);
if (ec->Ly_i < DEFAULT_CUTOFF_I)
ec->Ly_i = DEFAULT_CUTOFF_I;
/* Update the Peak far-end receive signal detected */
/* ----------------------------------------------- */
if (ec->y_tilde_i > ec->max_y_tilde) {
/* New highest y_tilde with full life */
ec->max_y_tilde = ec->y_tilde_i;
ec->max_y_tilde_pos = ec->N_d - 1;
} else if (--ec->max_y_tilde_pos < 0) {
/* Time to find new max y tilde... */
ec->max_y_tilde = MAX16(ec->y_tilde_s.buf_d + ec->y_tilde_s.idx_d, ec->N_d, &ec->max_y_tilde_pos);
}
/* Determine if near end speech was detected in this sample */
/* -------------------------------------------------------- */
if (((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde)
&& (ec->max_y_tilde > 0)) {
/* Then start the Hangover counter */
ec->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", ec->s_tilde_i, (ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), ec->max_y_tilde);
#endif
#ifdef MEC2_STATS
++ec->cntr_nearend_speech_frames;
#endif
} else if (ec->HCNTR_d > (int)0) {
/* otherwise, if it's still non-zero, decrement the Hangover counter by one sample */
#ifdef MEC2_STATS
++ec->cntr_nearend_speech_frames;
#endif
ec->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 (!ec->HCNTR_d && /* no near-end speech present */
!(ec->i_d % DEFAULT_M)) { /* we only update on every DEFAULM_M'th sample from the stream */
if (ec->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: ec->Lu_i %9d\n", ec->Lu_i);
#endif
#ifdef MEC2_STATS
ec->avg_Lu_i_ok = ec->avg_Lu_i_ok + ec->Lu_i;
++ec->cntr_coeff_updates;
#endif
for (k=0; k < ec->N_d; k++) {
/* eq. (7): compute an expectation over M_d samples */
int grad2;
grad2 = CONVOLVE2(ec->u_s.buf_d + ec->u_s.idx_d,
ec->y_s.buf_d + ec->y_s.idx_d + k,
DEFAULT_M);
/* eq. (7): update the coefficient */
ec->a_i[k] += grad2 / two_beta_i;
ec->a_s[k] = ec->a_i[k] >> 16;
}
} else {
#ifdef MEC2_STATS_DETAILED
printk( KERN_INFO "insufficient signal to update coefficients ec->Lu_i %5d < %5d\n", ec->Lu_i, MIN_UPDATE_THRESH_I);
#endif
#ifdef MEC2_STATS
ec->avg_Lu_i_toolow = ec->avg_Lu_i_toolow + ec->Lu_i;
++ec->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 (ec->HCNTR_d == 0)
printk( KERN_INFO "possibily correcting frame with ec->Ly_i %9d ec->Lu_i %9d and expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1)));
#endif
#ifndef NO_ECHO_SUPPRESSOR
#ifdef AGGRESSIVE_SUPPRESSOR
if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) {
for (k=0; k < RESIDUAL_SUPRESSION_PASSES; k++) {
u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1);
}
#ifdef MEC2_STATS_DETAILED
printk( KERN_INFO "aggresively correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1)));
#endif
#ifdef MEC2_STATS
++ec->cntr_residualcorrected_frames;
#endif
}
#else
if (ec->HCNTR_d == 0) {
if ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I) {
for (k=0; k < RESIDUAL_SUPRESSION_PASSES; k++) {
u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1);
}
#ifdef MEC2_STATS_DETAILED
printk( KERN_INFO "correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1)));
#endif
#ifdef MEC2_STATS
++ec->cntr_residualcorrected_frames;
#endif
}
#ifdef MEC2_STATS
else {
++ec->cntr_residualcorrected_framesskipped;
}
#endif
}
#endif
#endif
#if 0
/* This will generate a non-linear supression factor, once converted */
if ((ec->HCNTR_d == 0) &&
((ec->Lu_d/ec->Ly_d) < DEFAULT_SUPPR) &&
(ec->Lu_d/ec->Ly_d > EC_MIN_DB_VALUE)) {
suppr_factor = (10 / (float)(SUPPR_FLOOR - SUPPR_CEIL)) * log(ec->Lu_d/ec->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 ((ec->i_d % MEC2_STATS) == 0) {
/* make sure to avoid div0's! */
if (ec->cntr_coeff_missedupdates > 0)
ec->avg_Lu_i_toolow = (int)(ec->avg_Lu_i_toolow / ec->cntr_coeff_missedupdates);
else
ec->avg_Lu_i_toolow = -1;
if (ec->cntr_coeff_updates > 0)
ec->avg_Lu_i_ok = (ec->avg_Lu_i_ok / ec->cntr_coeff_updates);
else
ec->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",
ec->id,
ec->cntr_nearend_speech_frames,
ec->cntr_residualcorrected_frames, ec->cntr_residualcorrected_framesskipped,
ec->cntr_coeff_updates, ec->cntr_coeff_missedupdates,
ec->avg_Lu_i_ok, ec->avg_Lu_i_toolow);
ec->cntr_nearend_speech_frames = 0;
ec->cntr_residualcorrected_frames = 0;
ec->cntr_residualcorrected_framesskipped = 0;
ec->cntr_coeff_updates = 0;
ec->cntr_coeff_missedupdates = 0;
ec->avg_Lu_i_ok = 0;
ec->avg_Lu_i_toolow = 0;
}
#endif
/* Increment the sample index and return the corrected sample */
ec->i_d++;
return u;
}
static inline struct echo_can_state *echo_can_create(int len, int adaption_mode)
{
struct echo_can_state *ec;
int maxy;
int maxu;
maxy = len + 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);
ec = (struct echo_can_state *)MALLOC(sizeof(struct echo_can_state) +
4 + /* align */
sizeof(int) * len + /* a_i */
sizeof(short) * len + /* 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) * len); /* y_tilde_s */
if (ec) {
memset(ec, 0, sizeof(struct echo_can_state) +
4 + /* align */
sizeof(int) * len + /* a_i */
sizeof(short) * len + /* 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) * len); /* y_tilde_s */
init_cc(ec, len, maxy, maxu);
}
return ec;
}
static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val)
{
/* Set the hangover counter to the length of the can to
* avoid adjustments occuring immediately after initial forced training
*/
ec->HCNTR_d = ec->N_d << 1;
if (pos >= ec->N_d)
return 1;
ec->a_i[pos] = val << 17;
ec->a_s[pos] = val << 1;
if (++pos >= ec->N_d)
return 1;
return 0;
}
#endif

View File

@ -0,0 +1,81 @@
/*
Important constants for tuning kb1 echo can
*/
#ifndef _MEC2_CONST_H
#define _MEC2_CONST_H
/* 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 */
/* This knob controls the number of passes the residual echo supression
* algorithm makes.
*/
#ifdef AGGRESSIVE_SUPPRESSOR
#define RESIDUAL_SUPRESSION_PASSES 2
#else
#define RESIDUAL_SUPRESSION_PASSES 1
#endif
/***************************************************************/
/* 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
#endif /* _MEC2_CONST_H */

View File

@ -0,0 +1,424 @@
/*
* Mark's Second Echo Canceller
*
* Copyright (C) 2002, Digium, Inc.
*
* This program is free software and may be used and
* distributed according to the terms of the GNU
* General Public License, incorporated herein by
* reference.
*
*/
#ifndef _MARK2_ECHO_H
#define _MARK2_ECHO_H
#define EC_TYPE "MARK2"
#ifdef __KERNEL__
#include <linux/kernel.h>
#include <linux/slab.h>
#define MALLOC(a) kmalloc((a), GFP_KERNEL)
#define FREE(a) kfree(a)
#else
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#define MALLOC(a) malloc(a)
#define FREE(a) free(a)
#endif
/* Get optimized routines for math */
#include "dsp_arith.h"
#ifndef NULL
#define NULL 0
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#include "dsp_mec2_const.h"
/* Circular buffer definition */
typedef struct {
int idx_d;
int size_d;
short *buf_d; /* Twice as large as we need */
} echo_can_cb_s;
// class definition
//
struct echo_can_state {
/* Echo canceller definition */
/* absolute time */
int i_d;
/* pre-computed constants */
int N_d;
int beta2_i;
// declare accumulators for power computations
//
int Ly_i;
int Lu_i;
// declare an accumulator for the near-end signal detector
//
int s_tilde_i;
int HCNTR_d;
// circular buffers and coefficients
//
int *a_i;
short *a_s;
echo_can_cb_s y_s;
echo_can_cb_s s_s;
echo_can_cb_s u_s;
echo_can_cb_s y_tilde_s;
int y_tilde_i;
/* Max memory */
short max_y_tilde;
int max_y_tilde_pos;
};
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)
{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 echo_can_state *ec, int N, int maxy, int maxu) {
void *ptr = ec;
unsigned long tmp;
/* double-word align past end of state */
ptr += sizeof(struct echo_can_state);
tmp = (unsigned long)ptr;
tmp += 3;
tmp &= ~3L;
ptr = (void *)tmp;
// reset parameters
//
ec->N_d = N;
ec->beta2_i = DEFAULT_BETA1_I;
// allocate coefficient memory
//
ec->a_i = ptr;
ptr += (sizeof(int) * ec->N_d);
ec->a_s = ptr;
ptr += (sizeof(short) * ec->N_d);
/* Reset Y circular buffer (short version) */
init_cb_s(&ec->y_s, maxy, ptr);
ptr += (sizeof(short) * (maxy) * 2);
/* Reset Sig circular buffer (short version for FIR filter) */
init_cb_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr);
ptr += (sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) * 2);
init_cb_s(&ec->u_s, maxu, ptr);
ptr += (sizeof(short) * maxu * 2);
// allocate a buffer for the reference signal power computation
//
init_cb_s(&ec->y_tilde_s, ec->N_d, ptr);
// reset absolute time
//
ec->i_d = (int)0;
// reset the power computations (for y and u)
//
ec->Ly_i = DEFAULT_CUTOFF_I;
ec->Lu_i = DEFAULT_CUTOFF_I;
// reset the near-end speech detector
//
ec->s_tilde_i = 0;
ec->y_tilde_i = 0;
ec->HCNTR_d = (int)0;
// exit gracefully
//
}
static inline void echo_can_free(struct echo_can_state *ec)
{
FREE(ec);
}
static inline short echo_can_update(struct echo_can_state *ec, 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); */
/* Delete last sample from power estimate */
ec->y_tilde_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1 )) >> DEFAULT_ALPHA_YT_I;
/* push the reference data onto the circular buffer */
add_cc_s(&ec->y_s, iref);
/* eq. (2): compute r in fixed-point */
rs = CONVOLVE2(ec->a_s, ec->y_s.buf_d + ec->y_s.idx_d, ec->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;
add_cc_s(&ec->u_s, u);
/* Delete oldest part of received s_tilde */
ec->s_tilde_i -= abs(get_cc_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1 ));
/* push the signal on the circular buffer, too */
add_cc_s(&ec->s_s, isig);
ec->s_tilde_i += abs(isig);
ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_YT_I;
/* Add to our list of recent y_tilde's */
add_cc_s(&ec->y_tilde_s, ec->y_tilde_i);
/****************************************************************************
//
// flow B on pg. 428
//
****************************************************************************/
/* compute the new convergence factor
*/
if (!ec->HCNTR_d) {
Py_i = (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * (ec->Ly_i >> DEFAULT_SIGMA_LY_I);
Py_i >>= 15;
} else {
Py_i = (1 << 15);
}
#if 0
printf("Py: %e, Py_i: %e\n", Py, Py_i * AMPL_SCALE_1);
#endif
/* 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
*/
#if 0
if (ec->start_speech_d != 0 ){
if ( ec->i_d > (DEFAULT_T0 + ec->start_speech_d)*(SAMPLE_FREQ) ){
ec->beta2_d = max_cc_float(MIN_BETA,
DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((ec->i_d/(float)SAMPLE_FREQ) -
DEFAULT_T0 -
ec->start_speech_d)));
}
}
else {ec->beta2_d = DEFAULT_BETA1;}
#endif
ec->beta2_i = DEFAULT_BETA1_I; /* Fixed point, inverted */
two_beta_i = (ec->beta2_i * Py_i) >> 15; /* Fixed point version, inverted */
if (!two_beta_i)
two_beta_i++;
/* Update Lu_i (Suppressed power estimate) */
ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ;
ec->Lu_i += abs(u);
/* eq. (10): update power estimate of the reference
*/
ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ;
ec->Ly_i += abs(iref);
if (ec->Ly_i < DEFAULT_CUTOFF_I)
ec->Ly_i = DEFAULT_CUTOFF_I;
#if 0
printf("Float: %e, Int: %e\n", ec->Ly_d, (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * AMPL_SCALE_1);
#endif
if (ec->y_tilde_i > ec->max_y_tilde) {
/* New highest y_tilde with full life */
ec->max_y_tilde = ec->y_tilde_i;
ec->max_y_tilde_pos = ec->N_d - 1;
} else if (--ec->max_y_tilde_pos < 0) {
/* Time to find new max y tilde... */
ec->max_y_tilde = MAX16(ec->y_tilde_s.buf_d + ec->y_tilde_s.idx_d, ec->N_d, &ec->max_y_tilde_pos);
}
if ((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde)
{
ec->HCNTR_d = DEFAULT_HANGT;
}
else if (ec->HCNTR_d > (int)0)
{
ec->HCNTR_d--;
}
/* update coefficients if no near-end speech and we have enough signal
* to bother trying to update.
*/
if (!ec->HCNTR_d && !(ec->i_d % DEFAULT_M) &&
(ec->Lu_i > MIN_UPDATE_THRESH_I)) {
// loop over all filter coefficients
//
for (k=0; k<ec->N_d; k++) {
// eq. (7): compute an expectation over M_d samples
//
int grad2;
grad2 = CONVOLVE2(ec->u_s.buf_d + ec->u_s.idx_d,
ec->y_s.buf_d + ec->y_s.idx_d + k, DEFAULT_M);
// eq. (7): update the coefficient
//
ec->a_i[k] += grad2 / two_beta_i;
ec->a_s[k] = ec->a_i[k] >> 16;
}
}
/* paragraph below eq. (15): if no near-end speech,
// check for residual error suppression
*/
#ifndef NO_ECHO_SUPPRESSOR
#ifdef AGGRESSIVE_SUPPRESSOR
#ifdef AGGRESSIVE_TIMELIMIT /* This allows the aggressive suppressor to turn off after set amount of time */
if (ec->i_d > AGGRESSIVE_TIMELIMIT ) {
if ((ec->HCNTR_d == 0) && ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I)) {
u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1);
}
}
else {
#endif
if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) {
u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1);
u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1);
}
#ifdef AGGRESSIVE_TIMELIMIT
}
#endif
#else
if ((ec->HCNTR_d == 0) && ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I)) {
u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1);
}
#endif
#endif
#if 0
if ((ec->HCNTR_d == 0) && ((ec->Lu_d/ec->Ly_d) < DEFAULT_SUPPR) &&
(ec->Lu_d/ec->Ly_d > EC_MIN_DB_VALUE)) {
suppr_factor = (10/(float)(SUPPR_FLOOR-SUPPR_CEIL))*log(ec->Lu_d/ec->Ly_d)
- SUPPR_CEIL/(float)(SUPPR_FLOOR - SUPPR_CEIL);
u_suppr = pow(10.0,(suppr_factor)*RES_SUPR_FACTOR/10.0)*u_suppr;
}
#endif
ec->i_d++;
return u;
}
static inline struct echo_can_state *echo_can_create(int len, int adaption_mode)
{
struct echo_can_state *ec;
int maxy;
int maxu;
maxy = len + 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);
ec = (struct echo_can_state *)MALLOC(sizeof(struct echo_can_state) +
4 + /* align */
sizeof(int) * len + /* a_i */
sizeof(short) * len + /* 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) * len); /* y_tilde_s */
if (ec) {
memset(ec, 0, sizeof(struct echo_can_state) +
4 + /* align */
sizeof(int) * len + /* a_i */
sizeof(short) * len + /* 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) * len); /* y_tilde_s */
init_cc(ec, len, maxy, maxu);
}
return ec;
}
static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val)
{
/* Reset hang counter to avoid adjustments after
initial forced training */
ec->HCNTR_d = ec->N_d << 1;
if (pos >= ec->N_d)
return 1;
ec->a_i[pos] = val << 17;
ec->a_s[pos] = val << 1;
if (++pos >= ec->N_d)
return 1;
return 0;
}
#endif

View File

@ -0,0 +1,28 @@
/*
Important constants for tuning mec2 echo can
*/
#ifndef _MEC2_CONST_H
#define _MEC2_CONST_H
/* Convergence speed -- higher means slower */
#define DEFAULT_BETA1_I 2048
#define DEFAULT_SIGMA_LY_I 7
#define DEFAULT_SIGMA_LU_I 7
#define DEFAULT_ALPHA_ST_I 5
#define DEFAULT_ALPHA_YT_I 5
#define DEFAULT_CUTOFF_I 128
#define DEFAULT_HANGT 600
#define DEFAULT_SUPPR_I 16
#define MIN_UPDATE_THRESH_I 4096
#define DEFAULT_M 16
#define SUPPR_FLOOR -64
#define SUPPR_CEIL -24
#define RES_SUPR_FACTOR -20
#define AGGRESSIVE_HCNTR 160 /* 20ms */
/* Only use agressive echo cancellation for this amount of time then go back to normal cancelation */
/* #define AGGRESSIVE_TIMELIMIT 150000 */ /* 8 = 1ms */
#endif /* _MEC2_CONST_H */

View File

@ -0,0 +1,682 @@
/*
* ECHO_CAN_MG2
*
* by Michael Gernoth
*
* Based upon kb1ec.h and mec2.h
*
* Copyright (C) 2002, Digium, Inc.
*
* This program is free software and may be used and
* distributed according to the terms of the GNU
* General Public License, incorporated herein by
* reference.
*
* 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/
*
*/
#ifndef _MG2_ECHO_H
#define _MG2_ECHO_H
#define EC_TYPE "MG2"
#ifdef __KERNEL__
#include <linux/kernel.h>
#include <linux/slab.h>
#define MALLOC(a) kmalloc((a), GFP_KERNEL)
#define FREE(a) kfree(a)
#else
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#define MALLOC(a) malloc(a)
#define FREE(a) free(a)
#endif
#define ABS(a) abs(a!=-32768?a:-32767)
#define RESTORE_COEFFS {\
int x;\
memcpy(ec->a_i, ec->c_i, ec->N_d*sizeof(int));\
for (x=0;x<ec->N_d;x++) {\
ec->a_s[x] = ec->a_i[x] >> 16;\
}\
ec->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 */
/* Get optimized routines for math */
#include "dsp_arith.h"
/* Bring in definitions for the various constants and thresholds */
#include "dsp_mg2ec_const.h"
#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;
/* Echo canceller definition */
struct echo_can_state {
/* 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
short lastsig[256];
int lastpos;
int backup;
};
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 echo_can_state *ec, int N, int maxy, int maxu)
{
void *ptr = ec;
unsigned long tmp;
/* Double-word align past end of state */
ptr += sizeof(struct echo_can_state);
tmp = (unsigned long)ptr;
tmp += 3;
tmp &= ~3L;
ptr = (void *)tmp;
/* Reset parameters */
ec->N_d = N;
ec->beta2_i = DEFAULT_BETA1_I;
/* Allocate coefficient memory */
ec->a_i = ptr;
ptr += (sizeof(int) * ec->N_d);
ec->a_s = ptr;
ptr += (sizeof(short) * ec->N_d);
/* Allocate backup memory */
ec->b_i = ptr;
ptr += (sizeof(int) * ec->N_d);
ec->c_i = ptr;
ptr += (sizeof(int) * ec->N_d);
/* Reset Y circular buffer (short version) */
init_cb_s(&ec->y_s, maxy, ptr);
ptr += (sizeof(short) * (maxy) * 2);
/* Reset Sigma circular buffer (short version for FIR filter) */
init_cb_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I), ptr);
ptr += (sizeof(short) * (1 << DEFAULT_ALPHA_ST_I) * 2);
init_cb_s(&ec->u_s, maxu, ptr);
ptr += (sizeof(short) * maxu * 2);
/* Allocate a buffer for the reference signal power computation */
init_cb_s(&ec->y_tilde_s, ec->N_d, ptr);
/* Reset the absolute time index */
ec->i_d = (int)0;
/* Reset the power computations (for y and u) */
ec->Ly_i = DEFAULT_CUTOFF_I;
ec->Lu_i = DEFAULT_CUTOFF_I;
#ifdef MEC2_STATS
/* set the identity */
ec->id = (int)&ptr;
/* Reset performance stats */
ec->cntr_nearend_speech_frames = (int)0;
ec->cntr_residualcorrected_frames = (int)0;
ec->cntr_residualcorrected_framesskipped = (int)0;
ec->cntr_coeff_updates = (int)0;
ec->cntr_coeff_missedupdates = (int)0;
ec->avg_Lu_i_toolow = (int)0;
ec->avg_Lu_i_ok = (int)0;
#endif
/* Reset the near-end speech detector */
ec->s_tilde_i = (int)0;
ec->y_tilde_i = (int)0;
ec->HCNTR_d = (int)0;
}
static inline void echo_can_free(struct echo_can_state *ec)
{
FREE(ec);
}
static inline short echo_can_update(struct echo_can_state *ec, 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 */
ec->y_tilde_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_ALPHA_YT_I) - 1 )) >> DEFAULT_ALPHA_YT_I;
/* Add the new sample to the power estimate accumulator */
ec->y_tilde_i += abs(iref) >> DEFAULT_ALPHA_ST_I;
/* Push a copy of the new sample into its circular buffer */
add_cc_s(&ec->y_s, iref);
/* eq. (2): compute r in fixed-point */
rs = CONVOLVE2(ec->a_s,
ec->y_s.buf_d + ec->y_s.idx_d,
ec->N_d);
rs >>= 15;
ec->lastsig[ec->lastpos++] = isig;
if (ec->lastpos >= 256)
ec->lastpos = 0;
for (k=0; k < 256; k++) {
if (isig != ec->lastsig[k])
break;
}
if (isig == 0) {
u = 0;
} else if (k == 256) {
u = isig;
} else {
if (rs < -32768) {
rs = -32768;
ec->HCNTR_d = DEFAULT_HANGT;
RESTORE_COEFFS;
} else if (rs > 32767) {
rs = 32767;
ec->HCNTR_d = DEFAULT_HANGT;
RESTORE_COEFFS;
}
if (ABS(ABS(rs)-ABS(isig)) > 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(&ec->u_s, u);
if (!ec->backup) {
/* Backup coefficients periodically */
ec->backup = BACKUP;
memcpy(ec->c_i,ec->b_i,ec->N_d*sizeof(int));
memcpy(ec->b_i,ec->a_i,ec->N_d*sizeof(int));
} else
ec->backup--;
/* Update the Near-end hybrid signal circular buffers and accumulators */
/* ------------------------------------------------------------------- */
/* Delete the oldest sample from the power estimate accumulator */
ec->s_tilde_i -= abs(get_cc_s(&ec->s_s, (1 << DEFAULT_ALPHA_ST_I) - 1 ));
/* Add the new sample to the power estimate accumulator */
ec->s_tilde_i += abs(isig);
/* Push a copy of the new sample into it's circular buffer */
add_cc_s(&ec->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(&ec->y_tilde_s, ec->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 (!ec->HCNTR_d) {
Py_i = (ec->Ly_i >> DEFAULT_SIGMA_LY_I) * (ec->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 (ec->start_speech_d != 0 ){
if ( ec->i_d > (DEFAULT_T0 + ec->start_speech_d)*(SAMPLE_FREQ) ){
ec->beta2_d = max_cc_float(MIN_BETA, DEFAULT_BETA1 * exp((-1/DEFAULT_TAU)*((ec->i_d/(float)SAMPLE_FREQ) - DEFAULT_T0 - ec->start_speech_d)));
}
} else {
ec->beta2_d = DEFAULT_BETA1;
}
#endif
/* Fixed point, inverted */
ec->beta2_i = DEFAULT_BETA1_I;
/* Fixed point version, inverted */
two_beta_i = (ec->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 */
ec->Lu_i -= abs(get_cc_s(&ec->u_s, (1 << DEFAULT_SIGMA_LU_I) - 1 )) ;
/* Add the new sample to the power estimate accumulator */
ec->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 */
ec->Ly_i -= abs(get_cc_s(&ec->y_s, (1 << DEFAULT_SIGMA_LY_I) - 1)) ;
/* Add the new sample to the power estimate accumulator */
ec->Ly_i += abs(iref);
if (ec->Ly_i < DEFAULT_CUTOFF_I)
ec->Ly_i = DEFAULT_CUTOFF_I;
/* Update the Peak far-end receive signal detected */
/* ----------------------------------------------- */
if (ec->y_tilde_i > ec->max_y_tilde) {
/* New highest y_tilde with full life */
ec->max_y_tilde = ec->y_tilde_i;
ec->max_y_tilde_pos = ec->N_d - 1;
} else if (--ec->max_y_tilde_pos < 0) {
/* Time to find new max y tilde... */
ec->max_y_tilde = MAX16(ec->y_tilde_s.buf_d + ec->y_tilde_s.idx_d, ec->N_d, &ec->max_y_tilde_pos);
}
/* Determine if near end speech was detected in this sample */
/* -------------------------------------------------------- */
if (((ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)) > ec->max_y_tilde)
&& (ec->max_y_tilde > 0)) {
/* Then start the Hangover counter */
ec->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", ec->s_tilde_i, (ec->s_tilde_i >> (DEFAULT_ALPHA_ST_I - 1)), ec->max_y_tilde);
#endif
#ifdef MEC2_STATS
++ec->cntr_nearend_speech_frames;
#endif
} else if (ec->HCNTR_d > (int)0) {
/* otherwise, if it's still non-zero, decrement the Hangover counter by one sample */
#ifdef MEC2_STATS
++ec->cntr_nearend_speech_frames;
#endif
ec->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 (!ec->HCNTR_d && /* no near-end speech present */
!(ec->i_d % DEFAULT_M)) { /* we only update on every DEFAULM_M'th sample from the stream */
if (ec->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 (ec->N_d > USED_COEFFS)
memset(max_coeffs, 0, USED_COEFFS*sizeof(int));
#endif
#ifdef MEC2_STATS_DETAILED
printk( KERN_INFO "updating coefficients with: ec->Lu_i %9d\n", ec->Lu_i);
#endif
#ifdef MEC2_STATS
ec->avg_Lu_i_ok = ec->avg_Lu_i_ok + ec->Lu_i;
++ec->cntr_coeff_updates;
#endif
for (k=0; k < ec->N_d; k++) {
/* eq. (7): compute an expectation over M_d samples */
int grad2;
grad2 = CONVOLVE2(ec->u_s.buf_d + ec->u_s.idx_d,
ec->y_s.buf_d + ec->y_s.idx_d + k,
DEFAULT_M);
/* eq. (7): update the coefficient */
ec->a_i[k] += grad2 / two_beta_i;
ec->a_s[k] = ec->a_i[k] >> 16;
#ifdef USED_COEFFS
if (ec->N_d > USED_COEFFS) {
if (abs(ec->a_i[k]) > max_coeffs[USED_COEFFS-1]) {
/* More or less insertion-sort... */
pos = max_coeffs;
while (*pos > abs(ec->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(ec->a_i[k]);
}
}
#endif
}
#ifdef USED_COEFFS
/* Filter out irrelevant coefficients */
if (ec->N_d > USED_COEFFS)
for (k=0; k < ec->N_d; k++)
if (abs(ec->a_i[k]) < max_coeffs[USED_COEFFS-1])
ec->a_i[k] = ec->a_s[k] = 0;
#endif
} else {
#ifdef MEC2_STATS_DETAILED
printk( KERN_INFO "insufficient signal to update coefficients ec->Lu_i %5d < %5d\n", ec->Lu_i, MIN_UPDATE_THRESH_I);
#endif
#ifdef MEC2_STATS
ec->avg_Lu_i_toolow = ec->avg_Lu_i_toolow + ec->Lu_i;
++ec->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 (ec->HCNTR_d == 0)
printk( KERN_INFO "possibily correcting frame with ec->Ly_i %9d ec->Lu_i %9d and expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1)));
#endif
#ifndef NO_ECHO_SUPPRESSOR
#ifdef AGGRESSIVE_SUPPRESSOR
if ((ec->HCNTR_d < AGGRESSIVE_HCNTR) && (ec->Ly_i > (ec->Lu_i << 1))) {
for (k=0; k < RESIDUAL_SUPRESSION_PASSES; k++) {
u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I)) + 1);
}
#ifdef MEC2_STATS_DETAILED
printk( KERN_INFO "aggresively correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1)));
#endif
#ifdef MEC2_STATS
++ec->cntr_residualcorrected_frames;
#endif
}
#else
if (ec->HCNTR_d == 0) {
if ((ec->Ly_i/(ec->Lu_i + 1)) > DEFAULT_SUPPR_I) {
for (k=0; k < RESIDUAL_SUPRESSION_PASSES; k++) {
u = u * (ec->Lu_i >> DEFAULT_SIGMA_LU_I) / ((ec->Ly_i >> (DEFAULT_SIGMA_LY_I + 2)) + 1);
}
#ifdef MEC2_STATS_DETAILED
printk( KERN_INFO "correcting frame with ec->Ly_i %9d ec->Lu_i %9d expression %d\n", ec->Ly_i, ec->Lu_i, (ec->Ly_i/(ec->Lu_i + 1)));
#endif
#ifdef MEC2_STATS
++ec->cntr_residualcorrected_frames;
#endif
}
#ifdef MEC2_STATS
else {
++ec->cntr_residualcorrected_framesskipped;
}
#endif
}
#endif
#endif
#if 0
/* This will generate a non-linear supression factor, once converted */
if ((ec->HCNTR_d == 0) &&
((ec->Lu_d/ec->Ly_d) < DEFAULT_SUPPR) &&
(ec->Lu_d/ec->Ly_d > EC_MIN_DB_VALUE)) {
suppr_factor = (10 / (float)(SUPPR_FLOOR - SUPPR_CEIL)) * log(ec->Lu_d/ec->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 ((ec->i_d % MEC2_STATS) == 0) {
/* make sure to avoid div0's! */
if (ec->cntr_coeff_missedupdates > 0)
ec->avg_Lu_i_toolow = (int)(ec->avg_Lu_i_toolow / ec->cntr_coeff_missedupdates);
else
ec->avg_Lu_i_toolow = -1;
if (ec->cntr_coeff_updates > 0)
ec->avg_Lu_i_ok = (ec->avg_Lu_i_ok / ec->cntr_coeff_updates);
else
ec->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",
ec->id,
ec->cntr_nearend_speech_frames,
ec->cntr_residualcorrected_frames, ec->cntr_residualcorrected_framesskipped,
ec->cntr_coeff_updates, ec->cntr_coeff_missedupdates,
ec->avg_Lu_i_ok, ec->avg_Lu_i_toolow);
ec->cntr_nearend_speech_frames = 0;
ec->cntr_residualcorrected_frames = 0;
ec->cntr_residualcorrected_framesskipped = 0;
ec->cntr_coeff_updates = 0;
ec->cntr_coeff_missedupdates = 0;
ec->avg_Lu_i_ok = 0;
ec->avg_Lu_i_toolow = 0;
}
#endif
/* Increment the sample index and return the corrected sample */
ec->i_d++;
return u;
}
static inline struct echo_can_state *echo_can_create(int len, int adaption_mode)
{
struct echo_can_state *ec;
int maxy;
int maxu;
maxy = len + 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);
ec = (struct echo_can_state *)MALLOC(sizeof(struct echo_can_state) +
4 + /* align */
sizeof(int) * len + /* a_i */
sizeof(short) * len + /* a_s */
sizeof(int) * len + /* b_i */
sizeof(int) * len + /* 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) * len); /* y_tilde_s */
if (ec) {
memset(ec, 0, sizeof(struct echo_can_state) +
4 + /* align */
sizeof(int) * len + /* a_i */
sizeof(short) * len + /* a_s */
sizeof(int) * len + /* b_i */
sizeof(int) * len + /* 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) * len); /* y_tilde_s */
init_cc(ec, len, maxy, maxu);
}
return ec;
}
static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val)
{
/* Set the hangover counter to the length of the can to
* avoid adjustments occuring immediately after initial forced training
*/
ec->HCNTR_d = ec->N_d << 1;
if (pos >= ec->N_d) {
memcpy(ec->b_i,ec->a_i,ec->N_d*sizeof(int));
memcpy(ec->c_i,ec->a_i,ec->N_d*sizeof(int));
return 1;
}
ec->a_i[pos] = val << 17;
ec->a_s[pos] = val << 1;
if (++pos >= ec->N_d) {
memcpy(ec->b_i,ec->a_i,ec->N_d*sizeof(int));
memcpy(ec->c_i,ec->a_i,ec->N_d*sizeof(int));
return 1;
}
return 0;
}
#endif

View File

@ -0,0 +1,97 @@
/*
Important constants for tuning mg2 echo can
*/
#ifndef _MG2_CONST_H
#define _MG2_CONST_H
/* 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 */
/* This knob controls the number of passes the residual echo supression
* algorithm makes.
*/
#ifdef AGGRESSIVE_SUPPRESSOR
#define RESIDUAL_SUPRESSION_PASSES 2
#else
#define RESIDUAL_SUPRESSION_PASSES 1
#endif
/* 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
#endif /* _MG2_CONST_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,187 @@
/* $Id$
*
* mISDN driver for Colognechip HFC-S mini Evaluation Card
*
* Authors : Martin Bachem, Joerg Ciesielski
* Contact : info@colognechip.com
*
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef __HFCMINI_H__
#define __HFCMINI_H__
#include "channel.h"
#include "hfcsmcc.h"
#define BRIDGE_UNKWOWN 0
#define BRIDGE_HFCPCI 1
/* use HFC-S PCI as PCI Bridge */
#define HFCBRIDGE BRIDGE_HFCPCI
#define SPIN_LOCK_HFCSMINI_REGISTER
#define DRIVER_NAME "HFCMINI"
#define CHIP_ID_HFCSMINI CHIP_ID
#define MAX_CHAN 4 /* D, B1, B2, PCM */
/* flags in _u16 port mode */
#define PORT_UNUSED 0x0000
#define PORT_MODE_NT 0x0001
#define PORT_MODE_TE 0x0002
#define PORT_MODE_BUS_MASTER 0x0004
#define PORT_MODE_UP 0x0008
#define PORT_MODE_EXCH_POL 0x0010
#define PORT_MODE_LOOP 0x0020
#define NT_TIMER 0x8000
/* NT / TE defines */
#define NT_T1_COUNT 12 /* number of 8ms interrupts for G2 timeout */
#define CLK_DLY_TE 0x0e /* CLKDEL in TE mode */
#define CLK_DLY_NT 0x6c /* CLKDEL in NT mode */
#define STA_ACTIVATE 0x60 /* start activation in A_SU_WR_STA */
#define STA_DEACTIVATE 0x40 /* start deactivation in A_SU_WR_STA */
#define LIF_MODE_NT 0x04 /* Line Interface NT mode */
/* HFC-S mini Layer1 physical commands */
#define HFC_L1_ACTIVATE_TE 0x01
#define HFC_L1_FORCE_DEACTIVATE_TE 0x02
#define HFC_L1_ACTIVATE_NT 0x03
#define HFC_L1_DEACTIVATE_NT 0x04
#define HFC_L1_TESTLOOP_B1 0x05
#define HFC_L1_TESTLOOP_B2 0x06
/* FIFO handling support values */
#define FIFO_IRQ_OFF 0
#define FIFO_IRQ_ON 1
#define FIFO_DISABLE 0
#define FIFO_ENABLE 1
#define FIFO_MASK_TX 0x55
#define FIFO_MASK_RX 0xAA
#define HDLC_PAR_BCH 0 /* init value for all B-channel fifos */
#define HDLC_PAR_DCH (M1_BIT_CNT*2) /* same for D- and E-channel */
#define CON_HDLC_B_TRANS (M_HDLC_TRP | M1_TRP_IRQ*2) /* transparent mode B-channel 32 byte threshold */
#define CON_HDLC_B_HDLC (M1_TRP_IRQ*2) /* HDLC mode b-channel */
#define CON_HDLC_D_HDLC (M1_TRP_IRQ*2 | M_IFF) /* HDLC mode D-channel, 1 fill mode */
#define CON_HDLC_B_LOOP (M1_TRP_IRQ*2 | M1_DATA_FLOW*6) /* B-channel loopback mode */
#define HFCSMINI_RX_THRESHOLD 32
#define HFCSMINI_TX_THRESHOLD 96
#define DCH_RX_SIZE 267
#define BCH_RX_SIZE 2051
#define BCH_RX_SIZE_TRANS 64
/* DEBUG flags, use combined value for module parameter debug=x */
#define DEBUG_HFC_INIT 0x0001
#define DEBUG_HFC_MODE 0x0002
#define DEBUG_HFC_S0_STATES 0x0004
#define DEBUG_HFC_IRQ 0x0008
#define DEBUG_HFC_FIFO_ERR 0x0010
#define DEBUG_HFC_DTRACE 0x2000
#define DEBUG_HFC_BTRACE 0x4000 /* very(!) heavy messageslog load */
#define DEBUG_HFC_FIFO 0x8000 /* very(!) heavy messageslog load */
/* private driver_data */
typedef struct {
int chip_id;
char *device_name;
} hfcsmini_param;
struct _hfcmini_hw;
/**********************/
/* hardware structure */
/**********************/
typedef struct _hfcmini_hw {
struct list_head list;
__u32 irq_cnt; /* count irqs */
struct tasklet_struct tasklet; /* interrupt bottom half */
spinlock_t mlock; /* mISDN mq lock */
spinlock_t rlock; /* register access lock */
int cardnum;
__u8 param_idx; /* used to access module param arrays */
__u8 testirq;
int irq;
int iobase;
u_char *membase;
u_char *hw_membase;
struct pci_dev *pdev;
hfcsmini_param driver_data;
char card_name[60];
int max_fifo; /* always 4 fifos per port */
__u8 max_z; /* fifo depth -1 */
channel_t chan[MAX_CHAN]; /* line interfaces */
__u8 fifomask;
/* HFC-S MINI regsister */
reg_r_chip_id chip_id; /* Chip ID */
reg_r_pcm_md0 pcm_md0; /* PCM config */
reg_r_pcm_md1 pcm_md1; /* PCM config */
reg_r_pcm_md2 pcm_md2; /* PCM config */
reg_r_ti ti; /* timer interrupt configuration */
reg_r_fifo_irqmsk fifo_irqmsk; /* FIFO interrupt mask */
reg_r_fifo_irq fifo_irq; /* FIFO interrupt state */
reg_r_misc_irqmsk misc_irqmsk; /* MISC interrupt mask */
reg_r_misc_irq misc_irq; /* MISC interrupt state */
reg_r_st_ctrl0 st_ctrl0;
reg_r_st_ctrl2 st_ctrl2;
int nt_timer;
__u8 dpid; /* DChannel Protocoll ID */
__u16 portmode; /* NT/TE */
} hfcsmini_hw;
/* function prototypes */
int setup_channel(hfcsmini_hw * hw, __u8 channel, int protocol);
void hfcsmini_write_fifo(hfcsmini_hw * hw, __u8 channel);
void hfcsmini_read_fifo(hfcsmini_hw * hw, __u8 channel);
void print_fc(hfcsmini_hw * hw, __u8 fifo);
void setup_fifo(hfcsmini_hw * hw, int fifo, __u8 hdlcreg, __u8 con_reg, __u8 irq_enable, __u8 enable);
void setup_s(hfcsmini_hw * hw, __u8 bc, __u8 enable);
void disable_interrupts(hfcsmini_hw * hw);
void enable_interrupts(hfcsmini_hw * hw);
static void release_card(hfcsmini_hw * hw);
#if HFCBRIDGE == BRIDGE_HFCPCI
int init_pci_bridge(hfcsmini_hw * hw);
#endif
/* HFC-S MINI register access functions */
static inline void hfcsmini_sel_reg(hfcsmini_hw * hw, __u8 reg_addr);
static inline __u8 read_hfcsmini(hfcsmini_hw * hw, __u8 reg_addr);
static inline __u8 read_hfcsmini_irq(hfcsmini_hw * hw, __u8 reg_addr);
static inline __u8 read_hfcsmini_stable(hfcsmini_hw * hw, __u8 reg_addr);
static inline void write_hfcsmini(hfcsmini_hw * hw, __u8 reg_addr, __u8 value);
#endif /* __hfcsmini_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -1,182 +0,0 @@
/* $Id$
*
* hw_lock.h Hardware locking inline routines
*
* Author Karsten Keil (keil@isdn4linux.de)
*
* This file is (c) under GNU PUBLIC LICENSE
*
*/
/* Description of the locking mechanism
*
* The locking must grant serialisized and atomic
* access to the ISDN hardware registers, if the lock
* is aquired no other process or IRQ is alloed to
* access ISDN hardware registers.
*
* In general here are 3 possible entry points:
* 1. the ISDN interrupt routine
* 2. ISDN timer routines in the hardware module
* 3. messages that came from upper layers
*
* Since most work must be do in the interrupt routine
* (to grant minimum IRQ latency) and only few things with
* need direct HW access must be done for messages from upper
* layers, we should allow other IRQs in our IRQ routines and
* only block our own routines in this case. Since the common IRQ
* routines allready mask the same IRQ, we only need to protect us
* from timer and uper layers. The disadvantage is, that we need to
* disable local IRQ for entry points 2. and 3., but since the routines
* which need hardware access are well known and small, the impact
* is very small.
*
* We have a two stage locking to make this working:
* A spinlock which protect the state LOCK Flag (STATE_FLAG_BUSY) and
* also protect us from local IRQs from the entry points 2 and 3.
*
* In the hardware IRQ we aquire the spinlock, set the STATE_FLAG_BUSY
* LOCK Flag and then release the spinlock. It can never happen that
* the STATE_FLAG_BUSY is already set in this case, see later.
*
* In the other cases (from timer or upper layers) we aquire the spinlock
* test_and_set the STATE_FLAG_BUSY LOCK Flag, if it was allready set
* (a ISDN IRQ is running on the other CPU) we schedule timeout or add a other
* small timeout.
* If it was not set, we have the lock and we don't release the spinlock until we have
* done the harware work.
*
* To avoid any kind of deadlocking, it is important that we release the lock
* before we call functions that deliver to upper layers.
* To leave the impact of disabled local IRQ small, it is important to only protect
* small areas where hardware is accessed.
*
* The following routines handle the lock in the entry point from upper layers and other
* none IRQ cases (module init/exit stuff).
*
* They never called directly, but via the wrappers assigned to the instance
* inst.lock / inst.unlock pointers.
*
* Here are two defines which can be used for DEBUGING and PROFILING
* SPIN_DEBUG and LOCK_STATISTIC
*
*/
#ifndef __hw_lock__
#define __hw_lock__
typedef struct _mISDN_HWlock {
u_long flags;
spinlock_t lock;
#ifdef SPIN_DEBUG
void *spin_adr;
void *busy_adr;
#endif
volatile u_long state;
#ifdef LOCK_STATISTIC
u_int try_ok;
u_int try_wait;
u_int try_inirq;
u_int try_mult;
u_int irq_ok;
u_int irq_fail;
#endif
} mISDN_HWlock_t;
#define STATE_FLAG_BUSY 1
#define STATE_FLAG_INIRQ 2
/*
* returns 0 if the lock was aquired
* returns 1 if nowait != 0 and the lock is not aquired
*/
static inline int lock_HW(mISDN_HWlock_t *lock, int nowait)
{
u_long flags;
#ifdef LOCK_STATISTIC
int wait = 0;
#endif
spin_lock_irqsave(&lock->lock, flags);
#ifdef SPIN_DEBUG
lock->spin_adr = __builtin_return_address(0);
#endif
while (test_and_set_bit(STATE_FLAG_BUSY, &lock->state)) {
/* allready busy so we delay */
spin_unlock_irqrestore(&lock->lock, flags);
#ifdef SPIN_DEBUG
lock->spin_adr = NULL;
#endif
if (nowait) {
#ifdef LOCK_STATISTIC
lock->try_wait++;
#endif
return(1);
}
/* delay 1 jiffie is enought */
if (in_interrupt()) {
/* Should never happen */
#ifdef LOCK_STATISTIC
lock->try_inirq++;
#endif
printk(KERN_ERR "lock_HW: try to schedule in IRQ state(%lx)\n",
lock->state);
mdelay(1);
} else {
#ifdef LOCK_STATISTIC
if (wait++)
lock->try_mult++;
else
lock->try_wait++;
#endif
schedule_timeout(1);
}
spin_lock_irqsave(&lock->lock, flags);
#ifdef SPIN_DEBUG
lock->spin_adr = __builtin_return_address(0);
#endif
}
/* get the LOCK */
lock->flags = flags;
#ifdef SPIN_DEBUG
lock->busy_adr = __builtin_return_address(0);
#endif
#ifdef LOCK_STATISTIC
if (!wait)
lock->try_ok++;
#endif
return(0);
}
static inline void unlock_HW(mISDN_HWlock_t *lock)
{
if (!test_and_clear_bit(STATE_FLAG_BUSY, &lock->state)) {
printk(KERN_ERR "lock_HW: STATE_FLAG_BUSY not locked state(%lx)\n",
lock->state);
}
#ifdef SPIN_DEBUG
lock->busy_adr = NULL;
lock->spin_adr = NULL;
#endif
spin_unlock_irqrestore(&lock->lock, lock->flags);
}
static inline void lock_HW_init(mISDN_HWlock_t *lock)
{
spin_lock_init(&lock->lock);
lock->state = 0;
#ifdef SPIN_DEBUG
lock->busy_adr = NULL;
lock->spin_adr = NULL;
#endif
#ifdef LOCK_STATISTIC
lock->try_ok = 0;
lock->try_wait = 0;
lock->try_inirq = 0;
lock->try_mult = 0;
lock->irq_ok = 0;
lock->irq_fail = 0;
#endif
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,598 @@
/*
* loop.c loop driver for looped bchannel pairs
*
* Author Andreas Eversberg (jolly@jolly.de)
*
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/* module parameters:
* interfaces:
Number of loop interfaces. Default is 1.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/delay.h>
#include "channel.h"
#include "layer1.h"
#include "debug.h"
#include <linux/isdn_compat.h>
#include "loop.h"
static const char *loop_revision = "$Revision$";
static int loop_cnt;
static mISDNobject_t loop_obj;
static char LoopName[] = "loop";
/****************/
/* module stuff */
/****************/
static int interfaces;
static int debug;
#ifdef MODULE
MODULE_AUTHOR("Andreas Eversberg");
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif
MODULE_PARM(interfaces, "1i");
MODULE_PARM(debug, "1i");
#endif
/****************************/
/* Layer 1 D-channel access */
/****************************/
/* message transfer from layer 1.
*/
static int loop_l1hw(mISDNinstance_t *inst, struct sk_buff *skb)
{
channel_t *dch = container_of(inst, channel_t, inst);
loop_t *hc;
int ret = 0;
mISDN_head_t *hh;
hh = mISDN_HEAD_P(skb);
hc = dch->inst.privat;
if (debug & DEBUG_LOOP_MSG)
printk(KERN_DEBUG "%s: unsupported prim %x\n", __FUNCTION__, hh->prim);
ret = -EINVAL;
if (!ret)
dev_kfree_skb(skb);
return(ret);
}
/******************************/
/* Layer2 -> Layer 1 Transfer */
/******************************/
/* messages from layer 2 to layer 1 are processed here.
*/
static int
loop_l2l1(mISDNinstance_t *inst, struct sk_buff *skb)
{
int ch;
channel_t *bch = container_of(inst, channel_t, inst);
int ret = -EINVAL;
mISDN_head_t *hh;
loop_t *hc;
struct sk_buff *nskb;
hh = mISDN_HEAD_P(skb);
hc = bch->inst.privat;
ch = bch->channel;
if ((hh->prim == PH_DATA_REQ)
|| (hh->prim == (DL_DATA | REQUEST))) {
if (skb->len <= 0) {
printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__);
return(-EINVAL);
}
if (skb->len > MAX_DATA_MEM) {
printk(KERN_WARNING "%s: skb too large\n", __FUNCTION__);
return(-EINVAL);
}
if ((nskb = skb_clone(skb, GFP_ATOMIC)))
queue_ch_frame(hc->bch[ch^1], INDICATION, MISDN_ID_ANY, nskb);
skb_trim(skb, 0);
return(mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, hh->dinfo, skb));
} else if ((hh->prim == (PH_ACTIVATE | REQUEST))
|| (hh->prim == (DL_ESTABLISH | REQUEST))) {
/* activate B-channel if not already activated */
skb_trim(skb, 0);
return(mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, ret, skb));
} else if ((hh->prim == (PH_DEACTIVATE | REQUEST))
|| (hh->prim == (DL_RELEASE | REQUEST))
|| ((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) {
skb_trim(skb, 0);
return(mISDN_queueup_newhead(inst, 0, hh->prim | CONFIRM, ret, skb));
} else
if (hh->prim == (PH_CONTROL | REQUEST)) {
switch (hh->dinfo) {
default:
printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo);
ret = -EINVAL;
}
} else {
printk(KERN_WARNING "%s: unknown prim(%x)\n", __FUNCTION__, hh->prim);
ret = -EINVAL;
}
if (!ret) {
dev_kfree_skb(skb);
}
return(ret);
}
/**************************
* remove card from stack *
**************************/
static void
loop_delete(loop_t *hc)
{
int ch;
u_long flags;
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: entered\n", __FUNCTION__);
/* free channels */
if (hc->dch) {
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: free D-channel\n", __FUNCTION__);
mISDN_freechannel(hc->dch);
kfree(hc->dch);
hc->dch = NULL;
}
ch = 0;
while(ch < LOOP_CHANNELS) {
if (hc->bch[ch]) {
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: free B-channel %d\n", __FUNCTION__, ch);
mISDN_freechannel(hc->bch[ch]);
kfree(hc->bch[ch]);
hc->bch[ch] = NULL;
}
ch++;
}
/* remove us from list and delete */
if (debug & DEBUG_LOOP_INIT)
printk(KERN_WARNING "%s: remove instance from list\n", __FUNCTION__);
spin_lock_irqsave(&loop_obj.lock, flags);
list_del(&hc->list);
spin_unlock_irqrestore(&loop_obj.lock, flags);
if (debug & DEBUG_LOOP_INIT)
printk(KERN_WARNING "%s: delete instance\n", __FUNCTION__);
kfree(hc);
loop_cnt--;
if (debug & DEBUG_LOOP_INIT)
printk(KERN_WARNING "%s: card successfully removed\n", __FUNCTION__);
}
static int
loop_manager(void *data, u_int prim, void *arg)
{
loop_t *hc;
mISDNinstance_t *inst = data;
struct sk_buff *skb;
channel_t *dch = NULL;
channel_t *bch = NULL;
int ch = 0;
u_long flags;
if (!data) {
MGR_HASPROTOCOL_HANDLER(prim,arg,&loop_obj)
printk(KERN_ERR "%s: no data prim %x arg %p\n", __FUNCTION__, prim, arg);
return(-EINVAL);
}
/* find channel and card */
spin_lock_irqsave(&loop_obj.lock, flags);
list_for_each_entry(hc, &loop_obj.ilist, list) {
if (hc->dch) if (&hc->dch->inst == inst) {
dch = hc->dch;
spin_unlock_irqrestore(&loop_obj.lock, flags);
if (debug & DEBUG_LOOP_MGR)
printk(KERN_DEBUG "%s: D-channel data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg);
goto found;
}
ch = 0;
while(ch < LOOP_CHANNELS) {
if (hc->bch[ch]) if (&hc->bch[ch]->inst == inst) {
bch = hc->bch[ch];
spin_unlock_irqrestore(&loop_obj.lock, flags);
if (debug & DEBUG_LOOP_MGR)
printk(KERN_DEBUG "%s: B-channel %d (0..%d) data %p prim %x arg %p\n", __FUNCTION__, ch, LOOP_CHANNELS-1, data, prim, arg);
goto found;
}
}
}
spin_unlock_irqrestore(&loop_obj.lock, flags);
printk(KERN_ERR "%s: no card/channel found data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg);
return(-EINVAL);
found:
switch(prim) {
case MGR_REGLAYER | CONFIRM:
if (debug & DEBUG_LOOP_MGR)
printk(KERN_DEBUG "%s: MGR_REGLAYER\n", __FUNCTION__);
mISDN_setpara(dch, &inst->st->para);
break;
case MGR_UNREGLAYER | REQUEST:
if (debug & DEBUG_LOOP_MGR)
printk(KERN_DEBUG "%s: MGR_UNREGLAYER\n", __FUNCTION__);
if (dch) {
if ((skb = create_link_skb(PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL, 0))) {
if (loop_l1hw(inst, skb)) dev_kfree_skb(skb);
}
} else
if (bch) {
if ((skb = create_link_skb(PH_CONTROL | REQUEST, 0, 0, NULL, 0))) {
if (loop_l2l1(inst, skb)) dev_kfree_skb(skb);
}
}
loop_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
break;
case MGR_CLRSTPARA | INDICATION:
arg = NULL;
// fall through
case MGR_ADDSTPARA | INDICATION:
if (debug & DEBUG_LOOP_MGR)
printk(KERN_DEBUG "%s: MGR_***STPARA\n", __FUNCTION__);
mISDN_setpara(dch, arg);
break;
case MGR_RELEASE | INDICATION:
if (debug & DEBUG_LOOP_MGR)
printk(KERN_DEBUG "%s: MGR_RELEASE = remove port from mISDN\n", __FUNCTION__);
if (dch) {
loop_delete(hc); /* hc is free */
}
break;
#ifdef FIXME
case MGR_CONNECT | REQUEST:
if (debug & DEBUG_LOOP_MGR)
printk(KERN_DEBUG "%s: MGR_CONNECT\n", __FUNCTION__);
return(mISDN_ConnectIF(inst, arg));
case MGR_SETIF | REQUEST:
case MGR_SETIF | INDICATION:
if (debug & DEBUG_LOOP_MGR)
printk(KERN_DEBUG "%s: MGR_SETIF\n", __FUNCTION__);
if (dch)
return(mISDN_SetIF(inst, arg, prim, loop_l1hw, NULL, dch));
if (bch)
return(mISDN_SetIF(inst, arg, prim, loop_l2l1, NULL, bch));
break;
case MGR_DISCONNECT | REQUEST:
case MGR_DISCONNECT | INDICATION:
if (debug & DEBUG_LOOP_MGR)
printk(KERN_DEBUG "%s: MGR_DISCONNECT\n", __FUNCTION__);
return(mISDN_DisConnectIF(inst, arg));
#endif
#if 0
case MGR_SELCHANNEL | REQUEST:
if (debug & DEBUG_LOOP_MGR)
printk(KERN_DEBUG "%s: MGR_SELCHANNEL\n", __FUNCTION__);
if (!dch) {
printk(KERN_WARNING "%s(MGR_SELCHANNEL|REQUEST): selchannel not dinst\n", __FUNCTION__);
return(-EINVAL);
}
return(SelFreeBChannel(hc, ch, arg));
#endif
case MGR_SETSTACK | INDICATION:
if (debug & DEBUG_LOOP_MGR)
printk(KERN_DEBUG "%s: MGR_SETSTACK\n", __FUNCTION__);
if (bch && inst->pid.global==2) {
if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) {
if (loop_l2l1(inst, skb)) dev_kfree_skb(skb);
}
if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS)
mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION, 0, 0, NULL, 0);
else mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0);
}
break;
PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION);
PRIM_NOT_HANDLED(MGR_GLOBALOPT | REQUEST);
default:
printk(KERN_WARNING "%s: prim %x not handled\n", __FUNCTION__, prim);
return(-EINVAL);
}
return(0);
}
/*************************
* create cards instance *
*************************/
static int __devinit loop_new(void)
{
int ret_err=0;
int ch;
loop_t *hc;
mISDN_pid_t pid;
mISDNstack_t *dst = NULL; /* make gcc happy */
channel_t *dch;
channel_t *bch;
u_long flags;
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: Registering loop driver #%d\n", __FUNCTION__, loop_cnt+1);
/* allocate structure */
if (!(hc = kmalloc(sizeof(loop_t), GFP_ATOMIC))) {
printk(KERN_ERR "No kmem for loop driver\n");
ret_err = -ENOMEM;
goto free_object;
}
memset(hc, 0, sizeof(loop_t));
hc->idx = loop_cnt;
hc->id = loop_cnt + 1;
sprintf(hc->name, "LOOP#%d", loop_cnt+1);
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__);
spin_lock_irqsave(&loop_obj.lock, flags);
list_add_tail(&hc->list, &loop_obj.ilist);
spin_unlock_irqrestore(&loop_obj.lock, flags);
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__);
spin_lock_init(&hc->lock);
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: Registering D-channel, card(%d)\n", __FUNCTION__, loop_cnt+1);
dch = kmalloc(sizeof(channel_t), GFP_ATOMIC);
if (!dch) {
ret_err = -ENOMEM;
goto free_channels;
}
memset(dch, 0, sizeof(channel_t));
dch->channel = 0;
//dch->debug = debug;
dch->inst.obj = &loop_obj;
dch->inst.hwlock = &hc->lock;
mISDN_init_instance(&dch->inst, &loop_obj, hc, loop_l1hw);
dch->inst.pid.layermask = ISDN_LAYER(0);
sprintf(dch->inst.name, "LOOP%d", loop_cnt+1);
if (mISDN_initchannel(dch, MSK_INIT_DCHANNEL, MAX_DATA_MEM)) {
ret_err = -ENOMEM;
goto free_channels;
}
hc->dch = dch;
ch=0;
while(ch < LOOP_CHANNELS) {
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: Registering B-channel, card(%d) ch(%d)\n", __FUNCTION__, loop_cnt+1, ch);
bch = kmalloc(sizeof(channel_t), GFP_ATOMIC);
if (!bch) {
ret_err = -ENOMEM;
goto free_channels;
}
memset(bch, 0, sizeof(channel_t));
bch->channel = ch;
mISDN_init_instance(&bch->inst, &loop_obj, hc, loop_l2l1);
bch->inst.pid.layermask = ISDN_LAYER(0);
bch->inst.hwlock = &hc->lock;
//bch->debug = debug;
sprintf(bch->inst.name, "%s B%d",
dch->inst.name, ch+1);
if (mISDN_initchannel(bch, MSK_INIT_BCHANNEL, MAX_DATA_MEM)) {
kfree(bch);
ret_err = -ENOMEM;
goto free_channels;
}
hc->bch[ch] = bch;
#ifdef FIXME // TODO
if (bch->dev) {
bch->dev->wport.pif.func = loop_l2l1;
bch->dev->wport.pif.fdata = bch;
}
#endif
ch++;
}
/* set D-channel */
mISDN_set_dchannel_pid(&pid, 0x00, ISDN_LAYER(0));
pid.protocol[0] = ISDN_PID_L0_LOOP;
pid.layermask = ISDN_LAYER(0);
/* add stacks */
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: Adding d-stack: card(%d)\n", __FUNCTION__, loop_cnt+1);
if ((ret_err = loop_obj.ctrl(NULL, MGR_NEWSTACK | REQUEST, &dch->inst))) {
printk(KERN_ERR "MGR_ADDSTACK REQUEST dch err(%d)\n", ret_err);
free_release:
loop_delete(hc); /* hc is free */
goto free_object;
}
dst = dch->inst.st;
ch = 0;
while(ch < LOOP_CHANNELS) {
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: Adding b-stack: card(%d) B-channel(%d)\n", __FUNCTION__, loop_cnt+1, ch+1);
bch = hc->bch[ch];
if ((ret_err = loop_obj.ctrl(dst, MGR_NEWSTACK | REQUEST, &bch->inst))) {
printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", ret_err);
free_delstack:
loop_obj.ctrl(dst, MGR_DELSTACK | REQUEST, NULL);
goto free_release;
}
ch++;
}
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: (before MGR_SETSTACK REQUEST) layermask=0x%x\n", __FUNCTION__, pid.layermask);
if ((ret_err = loop_obj.ctrl(dst, MGR_SETSTACK | REQUEST, &pid))) {
printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", ret_err);
goto free_delstack;
}
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: (after MGR_SETSTACK REQUEST)\n", __FUNCTION__);
/* delay some time */
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout((100*HZ)/1000); /* Timeout 100ms */
/* tell stack, that we are ready */
loop_obj.ctrl(dst, MGR_CTRLREADY | INDICATION, NULL);
loop_cnt++;
return(0);
/* if an error ocurred */
free_channels:
if (hc->dch) {
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: free D-channel\n", __FUNCTION__);
mISDN_freechannel(hc->dch);
kfree(hc->dch);
hc->dch = NULL;
}
ch = 0;
while(ch < LOOP_CHANNELS) {
if (hc->bch[ch]) {
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: free B-channel %d\n", __FUNCTION__, ch);
mISDN_freechannel(hc->bch[ch]);
kfree(hc->bch[ch]);
hc->bch[ch] = NULL;
}
ch++;
}
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: before REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, loop_obj.refcnt);
spin_lock_irqsave(&loop_obj.lock, flags);
list_del(&hc->list);
spin_unlock_irqrestore(&loop_obj.lock, flags);
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: after REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, loop_obj.refcnt);
kfree(hc);
free_object:
return(ret_err);
}
static void __exit
loop_cleanup(void)
{
loop_t *hc,*next;
int err;
/* unregister mISDN object */
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: entered (refcnt = %d loop_cnt = %d)\n", __FUNCTION__, loop_obj.refcnt, loop_cnt);
if ((err = mISDN_unregister(&loop_obj))) {
printk(KERN_ERR "Can't unregister Loop cards error(%d)\n", err);
}
/* remove remaining devices, but this should never happen */
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: now checking ilist (refcnt = %d)\n", __FUNCTION__, loop_obj.refcnt);
list_for_each_entry_safe(hc, next, &loop_obj.ilist, list) {
printk(KERN_ERR "Loop card struct not empty refs %d\n", loop_obj.refcnt);
loop_delete(hc);
}
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: done (refcnt = %d loop_cnt = %d)\n", __FUNCTION__, loop_obj.refcnt, loop_cnt);
}
static int __init
loop_init(void)
{
int err;
char tmpstr[64];
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: init entered\n", __FUNCTION__);
strcpy(tmpstr, loop_revision);
printk(KERN_INFO "mISDN: loop-driver Rev. %s\n", mISDN_getrev(tmpstr));
memset(&loop_obj, 0, sizeof(loop_obj));
#ifdef MODULE
loop_obj.owner = THIS_MODULE;
#endif
spin_lock_init(&loop_obj.lock);
INIT_LIST_HEAD(&loop_obj.ilist);
loop_obj.name = LoopName;
loop_obj.own_ctrl = loop_manager;
loop_obj.DPROTO.protocol[0] = ISDN_PID_L0_LOOP;
loop_obj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC;
loop_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV;
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: registering loop_obj\n", __FUNCTION__);
if ((err = mISDN_register(&loop_obj))) {
printk(KERN_ERR "Can't register Loop cards error(%d)\n", err);
return(err);
}
if (debug & DEBUG_LOOP_INIT)
printk(KERN_DEBUG "%s: new mISDN object (refcnt = %d)\n", __FUNCTION__, loop_obj.refcnt);
if (interfaces < 1)
interfaces = 1;
loop_cnt = 0;
while(loop_cnt < interfaces)
{
if ((err = loop_new()))
break;
}
if (err)
{
printk(KERN_ERR "error registering pci driver:%x\n",err);
loop_cleanup();
return(err);
}
printk(KERN_INFO "%d devices registered\n", loop_cnt);
return(0);
}
#ifdef MODULE
module_init(loop_init);
module_exit(loop_cleanup);
#endif

View File

@ -0,0 +1,28 @@
/*
* see notice in loop.c
*/
#define DEBUG_LOOP_MSG 0x0001
#define DEBUG_LOOP_INIT 0x0004
#define DEBUG_LOOP_MGR 0x0008
#define MAX_FRAME_SIZE 2048
#define LOOP_CHANNELS 128
struct misdn_loop {
struct list_head list;
char name[32];
int idx; /* chip index for module parameters */
int id; /* chip number starting with 1 */
spinlock_t lock; /* the lock */
channel_t *dch;
channel_t *bch[LOOP_CHANNELS];
};
typedef struct misdn_loop loop_t;

View File

@ -0,0 +1,174 @@
/*
* socket handling, transfer, receive-process for Voice over IP
*
* Author Andreas Eversberg (jolly@jolly.de)
*
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/vmalloc.h>
#include <linux/config.h>
#include <linux/in.h>
#include <net/sock.h>
#include "debug.h"
#include "socket.h"
#include <linux/isdn_compat.h>
/* socket thread */
static int
mISDN_socket_thread(void *data)
{
mISDN_socket_t *ms = (mISDN_socket_t *)data;
/* make daemon */
daemonize("mISDN-socket");
allow_signal(SIGTERM);
/* read loop */
while(!signal_lending(current)) {
ms->iov.iov_base = ms->rcvbuf;
ms->iov_len = sizeof(ms->rcvbuf);
ms->oldfs = get_fs();
set_fs(KERNEL_DS);
ms->rcvlen = sock_recvmsg(ms->socket, &ms->msg, sizeof(ms->rcvbuf), 0);
set_fs(oldfs);
if (ms->rcvlen>0) {
ms->func(ms, ms->pri);
}
}
/* show that we terminate */
ms->thread_pid = 0;
/* if we got killed, signal completion */
if (ms->thread_complete)
complete(&ms->thread_complete);
return(0);
}
/*
* Adds a new socket with receive process.
* func will be the pointer to the receive process.
* -> The packet information is included in the mISDN_socket_t.
* pri will be the private data when calling receive process.
* local/remote ip/port will be the tupple for sending an receiving udp data.
* -> local-ip must be 0 if any local ip is accepted.
*/
mISDN_socket_t
*mISDN_add_socket(sock_func_t *func, void *pri, u32 local_ip, u32 remote_ip, u16 local_port, u16 remote_port)
{
mISDN_socket_t *ms;
/* alloc memory structure */
if (!(sm = vmalloc(sizeof(mISDN_sock_t)))) {
printk(KERN_ERR "%s: No memory for mISDN_sock_t.\n", __FUNCTION__);
return(NULL);
}
memset(ms, 0, sizeof(mISDN_sock_t));
ms->func = func;
ms->pri = pri;
/* create socket */
if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &ms->socket)) {
printk(KERN_ERR "%s: Failed to create socket.\n", __FUNCTION__);
mISDN_free_socket(ms);
return(NULL);
}
/* bind socket */
ms->server.sin_family = AF_INET;
ms->server.sin_addr.s_addr = (local_ip)?local_ip:INADDR_ANY;
ms->server.sin_port = htons((unsigned short)local_port);
if (ms->socket->ops->bind(ms->socket, (struct sockaddr *)ms->server, sizeof(ms->server))) {
printk(KERN_ERR "%s: Failed to bind socket.\n", __FUNCTION__);
mISDN_free_socket(ms);
return(NULL);
}
/* check sk */
if (ms->socket->sk == NULL) {
printk(KERN_ERR "%s: socket->sk == NULL\n", __FUNCTION__);
return(NULL);
}
/* build message */
ms->msg.msg_name = &ms->client;
ms->msg.msg_namelen = sizeof(ms->client);
ms->msg.msg_control = NULL;
ms->msg.msg_controllen = 0;
ms->msg.msg_iov = &ms->iov;
ms->msg.msg_iovlen = 1;
/* create receive process */
if ((ms->thread_pid = kernel_thread(mISDN_socket_thread, ms, CLONE_KERNEL)) < 0) {
printk(KERN_ERR "%s: Failed to create receive process.\n", __FUNCTION__);
mISDN_free_socket(ms);
return(NULL);
}
}
/*
* free given socket with all resources
*/
void
mISDN_free_socket(mISDN_sock_t *ms)
{
if (!ms)
return;
/* kill thread */
if (ms->thread_pid) {
DECLARE_COMPLETION(thread_complete);
ms->thread_complete = &thread_complete;
kill_proc(ms->thread_pid, SIGTERM, 0);
wait_for_completion(&thread_complete);
}
/* release socket */
if (ms->socket)
sock_release(ms->socket);
/* free memory structure */
vfree(sm);
}
/*
* transfer a frame via given socket
*/
int
mISDN_send_socket(mISDN_sock_t *ms, u8 *buf, int len)
{
/* send packet */
ms->send_iov.iov_base = ms->sndbuf;
ms->send_iov_len = sizeof(ms->sndbuf);
ms->send_oldfs = get_fs();
set_fs(KERNEL_DS);
ms->rcvlen = sock_recvmsg(ms->socket, &ms->msg, sizeof(ms->rcvbuf), 0);
set_fs(oldfs);
if (ms->rcvlen>0) {
ms->func(ms, ms->pri);
}
}

View File

@ -0,0 +1,39 @@
/*
* socket handling, transfer, receive-process for Voice over IP
*
* Author Andreas Eversberg (jolly@jolly.de)
*
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
struct _mISDN_socket_t;
typedef void (sock_func_t)(_mISDN_socket_t *ms, void *pri);
typedef struct _mISDN_socket_t {
sock_func_t *func;
void *pri;
int thread_pid;
struct completion *thread_complete;
struct socket *socket;
struct sockaddr_in server, client;
struct sockaddr *address;
struct msghdr msg;
struct iovec iov;
mm_segment_t oldfs;
u8 rcvbuf[1500];
int rcvlen;
} mISDN_socket_t;

View File

@ -0,0 +1,77 @@
/* $Id$
*
* Author Karsten Keil (keil@isdn4linux.de)
*
* mISDN sysfs common defines
*
* This file is (c) under GNU PUBLIC LICENSE
*
*/
#include <linux/stringify.h>
extern ssize_t mISDN_show_pid_protocol(mISDN_pid_t *, char *);
extern ssize_t mISDN_show_pid_parameter(mISDN_pid_t *, char *);
static inline ssize_t show_pid_layermask(mISDN_pid_t *pid, char *buf)
{
return sprintf(buf, "0x%08x\n", pid->layermask);
}
static inline ssize_t show_pid_global(mISDN_pid_t *pid, char *buf)
{
return sprintf(buf, "0x%04x\n", pid->global);
}
static inline ssize_t show_pid_maxplen(mISDN_pid_t *pid, char *buf)
{
return sprintf(buf, "%d\n", pid->maxplen);
}
#define MISDN_PROTO(_type, _name, _mode) \
static ssize_t show_protocol_##_name(struct class_device *class_dev, char *buf) \
{ \
_type##_t *p = to_##_type(class_dev); \
return(mISDN_show_pid_protocol(&p->_name, buf)); \
} \
struct class_device_attribute _type##_attr_protocol_##_name = \
__ATTR(protocol,_mode,show_protocol_##_name, NULL); \
static ssize_t show_parameter_##_name(struct class_device *class_dev, char *buf) \
{ \
_type##_t *p = to_##_type(class_dev); \
return(mISDN_show_pid_parameter(&p->_name, buf)); \
} \
struct class_device_attribute _type##_attr_parameter_##_name = \
__ATTR(parameter,_mode,show_parameter_##_name, NULL); \
static ssize_t show_layermask_##_name(struct class_device *class_dev, char *buf) \
{ \
_type##_t *p = to_##_type(class_dev); \
return(show_pid_layermask(&p->_name, buf)); \
} \
struct class_device_attribute _type##_attr_layermask_##_name = \
__ATTR(layermask,_mode,show_layermask_##_name, NULL); \
static ssize_t show_global_##_name(struct class_device *class_dev, char *buf) \
{ \
_type##_t *p = to_##_type(class_dev); \
return(show_pid_global(&p->_name, buf)); \
} \
struct class_device_attribute _type##_attr_global_##_name = \
__ATTR(global,_mode,show_global_##_name, NULL); \
static ssize_t show_maxplen_##_name(struct class_device *class_dev, char *buf) \
{ \
_type##_t *p = to_##_type(class_dev); \
return(show_pid_maxplen(&p->_name, buf)); \
} \
struct class_device_attribute _type##_attr_maxplen_##_name = \
__ATTR(maxplen,_mode,show_maxplen_##_name, NULL); \
static struct attribute *attr_##_name[] = { \
&_type##_attr_global_##_name.attr, \
&_type##_attr_layermask_##_name.attr, \
&_type##_attr_maxplen_##_name.attr, \
&_type##_attr_parameter_##_name.attr, \
&_type##_attr_protocol_##_name.attr, \
NULL \
}; \
static struct attribute_group _name##_group = { \
.name = __stringify(_name), \
.attrs = attr_##_name, \
}

View File

@ -0,0 +1,124 @@
/* $Id$
*
* Author Karsten Keil (keil@isdn4linux.de)
*
* mISDN sysfs stuff for isnstances
*
* This file is (c) under GNU PUBLIC LICENSE
*
*/
#include "core.h"
#include "sysfs.h"
#define to_mISDNinstance(d) container_of(d, mISDNinstance_t, class_dev)
static ssize_t show_inst_id(struct class_device *class_dev, char *buf)
{
mISDNinstance_t *inst = to_mISDNinstance(class_dev);
return sprintf(buf, "%08x\n", inst->id);
}
static CLASS_DEVICE_ATTR(id, S_IRUGO, show_inst_id, NULL);
static ssize_t show_inst_name(struct class_device *class_dev, char *buf)
{
mISDNinstance_t *inst = to_mISDNinstance(class_dev);
return sprintf(buf, "%s\n", inst->name);
}
static CLASS_DEVICE_ATTR(name, S_IRUGO, show_inst_name, NULL);
static ssize_t show_inst_extentions(struct class_device *class_dev, char *buf)
{
mISDNinstance_t *inst = to_mISDNinstance(class_dev);
return sprintf(buf, "%08x\n", inst->extentions);
}
static CLASS_DEVICE_ATTR(extentions, S_IRUGO, show_inst_extentions, NULL);
static ssize_t show_inst_regcnt(struct class_device *class_dev, char *buf)
{
mISDNinstance_t *inst = to_mISDNinstance(class_dev);
return sprintf(buf, "%d\n", inst->regcnt);
}
static CLASS_DEVICE_ATTR(regcnt, S_IRUGO, show_inst_regcnt, NULL);
MISDN_PROTO(mISDNinstance, pid, S_IRUGO);
static void release_mISDN_inst(struct class_device *dev)
{
mISDNinstance_t *inst = to_mISDNinstance(dev);
if (inst->obj)
sysfs_remove_link(&dev->kobj, "obj");
sysfs_remove_group(&inst->class_dev.kobj, &pid_group);
printk(KERN_INFO "release instance class dev %s\n", dev->class_id);
}
static struct class inst_dev_class = {
.name = "mISDN-instances",
#ifndef CLASS_WITHOUT_OWNER
.owner = THIS_MODULE,
#endif
.release = &release_mISDN_inst,
};
int
mISDN_register_sysfs_inst(mISDNinstance_t *inst) {
int err;
char name[8];
inst->class_dev.class = &inst_dev_class;
snprintf(inst->class_dev.class_id, BUS_ID_SIZE, "inst-%08x", inst->id);
err = class_device_register(&inst->class_dev);
if (err)
return(err);
class_device_create_file(&inst->class_dev, &class_device_attr_id);
class_device_create_file(&inst->class_dev, &class_device_attr_name);
class_device_create_file(&inst->class_dev, &class_device_attr_extentions);
class_device_create_file(&inst->class_dev, &class_device_attr_regcnt);
err = sysfs_create_group(&inst->class_dev.kobj, &pid_group);
if (err)
goto out_unreg;
if (inst->obj)
sysfs_create_link(&inst->class_dev.kobj, &inst->obj->class_dev.kobj, "obj");
if (inst->st) {
sprintf(name,"layer.%d", inst->id & LAYER_ID_MASK);
sysfs_create_link(&inst->st->class_dev.kobj, &inst->class_dev.kobj, name);
sysfs_create_link(&inst->class_dev.kobj, &inst->st->class_dev.kobj, "stack");
if (inst->st->mgr == inst) {
sysfs_create_link(&inst->st->class_dev.kobj, &inst->class_dev.kobj, "mgr");
}
}
return(err);
out_unreg:
class_device_unregister(&inst->class_dev);
return(err);
}
void
mISDN_unregister_sysfs_inst(mISDNinstance_t *inst)
{
char name[8];
if (inst->id) {
if (inst->st) {
sprintf(name,"layer.%d", inst->id & LAYER_ID_MASK);
sysfs_remove_link(&inst->st->class_dev.kobj, name);
sysfs_remove_link(&inst->class_dev.kobj, "stack");
if (inst->st->mgr == inst)
sysfs_remove_link(&inst->st->class_dev.kobj, "mgr");
}
class_device_unregister(&inst->class_dev);
}
}
int
mISDN_sysfs_inst_init(void)
{
return(class_register(&inst_dev_class));
}
void
mISDN_sysfs_inst_cleanup(void)
{
class_unregister(&inst_dev_class);
}

View File

@ -0,0 +1,153 @@
/* $Id$
*
* Author Karsten Keil (keil@isdn4linux.de)
*
* mISDN sysfs object and common stuff
*
* This file is (c) under GNU PUBLIC LICENSE
*
*/
#include "core.h"
#include "sysfs.h"
#define to_mISDNobject(d) container_of(d, mISDNobject_t, class_dev)
static ssize_t show_obj_name(struct class_device *class_dev, char *buf)
{
mISDNobject_t *obj = to_mISDNobject(class_dev);
return sprintf(buf, "%s\n", obj->name);
}
static CLASS_DEVICE_ATTR(name, S_IRUGO, show_obj_name, NULL);
static ssize_t show_obj_id(struct class_device *class_dev, char *buf)
{
mISDNobject_t *obj = to_mISDNobject(class_dev);
return sprintf(buf, "%d\n", obj->id);
}
static CLASS_DEVICE_ATTR(id, S_IRUGO, show_obj_id, NULL);
static ssize_t show_obj_refcnt(struct class_device *class_dev, char *buf)
{
mISDNobject_t *obj = to_mISDNobject(class_dev);
return sprintf(buf, "%d\n", obj->refcnt);
}
static CLASS_DEVICE_ATTR(refcnt, S_IRUGO, show_obj_refcnt, NULL);
ssize_t mISDN_show_pid_protocol(mISDN_pid_t *pid, char *buf)
{
char *p = buf;
uint i;
for (i=0; i <= MAX_LAYER_NR; i++)
p += sprintf(p,"0x%08x,", pid->protocol[i]);
p--;
*p++ = '\n';
return (p -buf);
}
ssize_t mISDN_show_pid_parameter(mISDN_pid_t *pid, char *buf)
{
char *p = buf, *t;
uint i, l;
for (i=0; i <= MAX_LAYER_NR; i++) {
if (pid->param[i]) {
t = pid->param[i];
l = *t++;
p += sprintf(p,"0x%02x,", l);
while(l--)
p += sprintf(p,"0x%02x,", *t++);
}else {
p += sprintf(p,"0x00,");
}
}
p--;
*p++ = '\n';
return (p -buf);
}
MISDN_PROTO(mISDNobject, BPROTO, S_IRUGO);
MISDN_PROTO(mISDNobject, DPROTO, S_IRUGO);
static void release_mISDN_obj(struct class_device *dev)
{
mISDNobject_t *obj = to_mISDNobject(dev);
printk(KERN_INFO "release object class dev %s\n", dev->class_id);
if (obj->owner)
#ifdef MODULE_MKOBJ_POINTER
if (obj->owner->mkobj)
#endif
sysfs_remove_link(&dev->kobj, "module");
sysfs_remove_group(&obj->class_dev.kobj, &BPROTO_group);
sysfs_remove_group(&obj->class_dev.kobj, &DPROTO_group);
}
static struct class obj_dev_class = {
.name = "mISDN-objects",
#ifndef CLASS_WITHOUT_OWNER
.owner = THIS_MODULE,
#endif
.release = &release_mISDN_obj,
};
int
mISDN_register_sysfs_obj(mISDNobject_t *obj) {
int err;
obj->class_dev.class = &obj_dev_class;
snprintf(obj->class_dev.class_id, BUS_ID_SIZE, "obj-%d", obj->id);
err = class_device_register(&obj->class_dev);
if (err)
goto out;
class_device_create_file(&obj->class_dev, &class_device_attr_id);
class_device_create_file(&obj->class_dev, &class_device_attr_name);
class_device_create_file(&obj->class_dev, &class_device_attr_refcnt);
err = sysfs_create_group(&obj->class_dev.kobj, &BPROTO_group);
if (err)
goto out_unreg;
err = sysfs_create_group(&obj->class_dev.kobj, &DPROTO_group);
if (err)
goto out_unreg;
if (obj->owner)
#ifdef MODULE_MKOBJ_POINTER
if (obj->owner->mkobj)
sysfs_create_link(&obj->class_dev.kobj, &obj->owner->mkobj->kobj, "module");
#else
sysfs_create_link(&obj->class_dev.kobj, &obj->owner->mkobj.kobj, "module");
#endif
return(err);
out_unreg:
class_device_unregister(&obj->class_dev);
out:
return(err);
}
int
mISDN_sysfs_init(void) {
int err;
err = class_register(&obj_dev_class);
if (err)
return(err);
err = mISDN_sysfs_inst_init();
if (err)
goto unreg_obj;
err = mISDN_sysfs_st_init();
if (err)
goto unreg_inst;
return(err);
unreg_inst:
mISDN_sysfs_inst_cleanup();
unreg_obj:
class_unregister(&obj_dev_class);
return(err);
}
void
mISDN_sysfs_cleanup(void) {
class_unregister(&obj_dev_class);
mISDN_sysfs_inst_cleanup();
mISDN_sysfs_st_cleanup();
}

View File

@ -0,0 +1,270 @@
/* $Id$
*
* Author Karsten Keil (keil@isdn4linux.de)
*
* mISDN sysfs stack stuff
*
* This file is (c) under GNU PUBLIC LICENSE
*
*/
#include "core.h"
#include "sysfs.h"
#define to_mISDNstack(d) container_of(d, mISDNstack_t, class_dev)
static ssize_t show_st_id(struct class_device *class_dev, char *buf)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
return sprintf(buf, "%08x\n", st->id);
}
static CLASS_DEVICE_ATTR(id, S_IRUGO, show_st_id, NULL);
static ssize_t show_st_status(struct class_device *class_dev, char *buf)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
return sprintf(buf, "0x%08lx\n", st->status);
}
static ssize_t store_st_status(struct class_device *class_dev, const char *buf, size_t count)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
ulong status;
int err;
status = simple_strtol(buf, NULL, 0);
printk(KERN_DEBUG "%s: status %08lx\n", __FUNCTION__, status);
if (status == (1<<mISDN_STACK_INIT)) {
/* we want to make st->new_pid activ */
err = clear_stack(st, 1);
if (err) {
int_errtxt("clear_stack:%d", err);
return(err);
}
err = set_stack(st ,&st->new_pid);
if (err) {
int_errtxt("set_stack:%d", err);
return(err);
}
return(count);
} else if (status == (1<<mISDN_STACK_THREADSTART)) {
/* we want to start a new process after abort */
err = mISDN_start_stack_thread(st);
if (err) {
int_errtxt("start_stack_thread:%d", err);
return(err);
}
return(count);
}
st->status = status;
wake_up_interruptible(&st->workq);
return(count);
}
static CLASS_DEVICE_ATTR(status, S_IRUGO | S_IWUSR, show_st_status, store_st_status);
static ssize_t store_st_protocol(struct class_device *class_dev, const char *buf, size_t count)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
ulong tmp;
char *p = (char *)buf;
u_int i;
memset(&st->new_pid.protocol, 0, (MAX_LAYER_NR + 1)*sizeof(st->new_pid.protocol[0]));
for (i=0; i<=MAX_LAYER_NR; i++) {
if (!*p)
break;
tmp = simple_strtol(p, &p, 0);
st->new_pid.protocol[i] = tmp;
if (*p)
p++;
}
if (*p)
int_errtxt("overflow");
return(count);
}
static ssize_t store_st_layermask(struct class_device *class_dev, const char *buf, size_t count)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
ulong mask = (1<<(MAX_LAYER_NR + 1)) -1;
st->new_pid.layermask = simple_strtol(buf, NULL, 0);
if (st->new_pid.layermask > mask) {
int_errtxt("overflow");
st->new_pid.layermask &= mask;
}
return(count);
}
static ssize_t store_st_parameter(struct class_device *class_dev, const char *buf, size_t count)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
ulong tmp;
char *d, *p = (char *)buf;
u_int i, j, l;
memset(&st->new_pid.param, 0, (MAX_LAYER_NR + 1)*sizeof(st->new_pid.param[0]));
kfree(st->new_pid.pbuf);
l = 0;
for (i=0; i<=MAX_LAYER_NR; i++) {
if (!*p)
break;
tmp = simple_strtol(p, &p, 0);
if (*p)
p++;
if (tmp) {
j = tmp;
l += j+1;
while(j--) {
if (!*p)
break;
tmp = simple_strtol(p, &p, 0);
if (*p)
p++;
else
break;
}
}
}
if (*p)
int_errtxt("overflow");
if (l == 0) {
st->new_pid.maxplen = 0;
return(count);
}
st->new_pid.pbuf = kmalloc(l, GFP_ATOMIC);
if (!st->new_pid.pbuf)
return(-ENOMEM);
st->new_pid.maxplen = l;
d = st->new_pid.pbuf;
memset(d, 0, l);
p = (char *)buf;
for (i=0; i<=MAX_LAYER_NR; i++) {
if (!*p)
break;
tmp = simple_strtol(p, &p, 0);
if (*p)
p++;
if (tmp) {
j = tmp;
st->new_pid.param[i] = d;
*d++ = tmp & 0xff;
while(j--) {
if (!*p)
break;
tmp = simple_strtol(p, &p, 0);
*d++ = tmp & 0xff;
if (*p)
p++;
else
break;
}
}
}
return(count);
}
MISDN_PROTO(mISDNstack, pid, S_IRUGO);
MISDN_PROTO(mISDNstack, new_pid, S_IRUGO);
static ssize_t show_st_qlen(struct class_device *class_dev, char *buf)
{
mISDNstack_t *st = to_mISDNstack(class_dev);
return sprintf(buf, "%d\n", skb_queue_len(&st->msgq));
}
static CLASS_DEVICE_ATTR(qlen, S_IRUGO, show_st_qlen, NULL);
static void release_mISDN_stack(struct class_device *dev)
{
mISDNstack_t *st = to_mISDNstack(dev);
char name[12];
sysfs_remove_group(&st->class_dev.kobj, &pid_group);
sysfs_remove_group(&st->class_dev.kobj, &new_pid_group);
printk(KERN_INFO "release stack class dev %s\n", dev->class_id);
if (st->parent) {
sysfs_remove_link(&dev->kobj, "parent");
snprintf(name, 12, "child%d", (CHILD_ID_MASK & st->id) >> 16);
sysfs_remove_link(&st->parent->class_dev.kobj, name);
}
if (st->master) {
sysfs_remove_link(&dev->kobj, "master");
snprintf(name, 12, "clone%d", (CLONE_ID_MASK & st->id) >> 16);
sysfs_remove_link(&st->master->class_dev.kobj, name);
}
}
static struct class stack_dev_class = {
.name = "mISDN-stacks",
#ifndef CLASS_WITHOUT_OWNER
.owner = THIS_MODULE,
#endif
.release = &release_mISDN_stack,
};
int
mISDN_register_sysfs_stack(mISDNstack_t *st)
{
int err;
char name[12];
st->class_dev.class = &stack_dev_class;
if (st->id & FLG_CHILD_STACK)
snprintf(st->class_dev.class_id, BUS_ID_SIZE, "chst-%08x", st->id);
else if (st->id & FLG_CLONE_STACK)
snprintf(st->class_dev.class_id, BUS_ID_SIZE, "clst-%08x", st->id);
else
snprintf(st->class_dev.class_id, BUS_ID_SIZE, "st-%08x", st->id);
if (st->mgr)
st->class_dev.dev = st->mgr->class_dev.dev;
err = class_device_register(&st->class_dev);
if (err)
return(err);
err = sysfs_create_group(&st->class_dev.kobj, &pid_group);
if (err)
goto out_unreg;
mISDNstack_attr_protocol_new_pid.attr.mode |= S_IWUSR;
mISDNstack_attr_protocol_new_pid.store = store_st_protocol;
mISDNstack_attr_parameter_new_pid.attr.mode |= S_IWUSR;
mISDNstack_attr_parameter_new_pid.store = store_st_parameter;
mISDNstack_attr_layermask_new_pid.attr.mode |= S_IWUSR;
mISDNstack_attr_layermask_new_pid.store = store_st_layermask;
err = sysfs_create_group(&st->class_dev.kobj, &new_pid_group);
if (err)
goto out_unreg;
class_device_create_file(&st->class_dev, &class_device_attr_id);
class_device_create_file(&st->class_dev, &class_device_attr_qlen);
class_device_create_file(&st->class_dev, &class_device_attr_status);
if (st->parent) {
sysfs_create_link(&st->class_dev.kobj, &st->parent->class_dev.kobj, "parent");
snprintf(name, 12, "child%d", (CHILD_ID_MASK & st->id) >> 16);
sysfs_create_link(&st->parent->class_dev.kobj, &st->class_dev.kobj, name);
}
if (st->master) {
sysfs_create_link(&st->class_dev.kobj, &st->master->class_dev.kobj, "master");
snprintf(name, 12, "clone%d", (CLONE_ID_MASK & st->id) >> 16);
sysfs_create_link(&st->master->class_dev.kobj, &st->class_dev.kobj, name);
}
return(err);
out_unreg:
class_device_unregister(&st->class_dev);
return(err);
}
void
mISDN_unregister_sysfs_st(mISDNstack_t *st)
{
class_device_unregister(&st->class_dev);
}
int
mISDN_sysfs_st_init(void)
{
return(class_register(&stack_dev_class));
}
void
mISDN_sysfs_st_cleanup(void)
{
class_unregister(&stack_dev_class);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,134 @@
/* $Id$
*
* PCI2PI Pci Bridge support for xhfc_su.c
*
* Authors : Martin Bachem, Joerg Ciesielski
* Contact : info@colognechip.com
*
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/delay.h>
#include "xhfc_su.h"
#include "xhfc_pci2pi.h"
static PCI2PI_cfg PCI2PI_config = {
/* default PI_INTELMX config */
.del_cs = 0,
.del_rd = 0,
.del_wr = 0,
.del_ale = 0,
.del_adr = 0,
.del_dout = 0,
.default_adr = 0x00,
.default_dout = 0x00,
.pi_mode = PI_MODE,
.setup = 1,
.hold = 1,
.cycle = 1,
.ale_adr_first = 0,
.ale_adr_setup = 0,
.ale_adr_hold = 1,
.ale_adr_wait = 0,
.pause_seq = 1,
.pause_end = 0,
.gpio_out = 0,
.status_int_enable = 1,
.pi_int_pol = 0,
.pi_wait_enable = 0,
.spi_cfg0 = 0,
.spi_cfg1 = 0,
.spi_cfg2 = 0,
.spi_cfg3 = 0,
.eep_recover = 4,
};
/***********************************/
/* initialise the XHFC PCI Bridge */
/* return 0 on success. */
/***********************************/
int
init_pci_bridge(xhfc_hw * hw)
{
int err = -ENODEV;
printk(KERN_INFO "%s %s: using PCI2PI Bridge at 0x%p\n",
hw->card_name, __FUNCTION__, hw->hw_membase);
/* test if Bridge regsiter accessable */
WritePCI2PI_u32(hw, PCI2PI_DEL_CS, 0x0);
if (ReadPCI2PI_u32(hw, PCI2PI_DEL_CS) == 0x00) {
WritePCI2PI_u32(hw, PCI2PI_DEL_CS, 0xFFFFFFFF);
if (ReadPCI2PI_u32(hw, PCI2PI_DEL_CS) == 0xF) {
err = 0;
}
}
if (err)
return (err);
/* enable hardware reset XHFC */
WritePCI2PI_u32(hw, PCI2PI_GPIO_OUT, GPIO_OUT_VAL);
WritePCI2PI_u32(hw, PCI2PI_PI_MODE, PCI2PI_config.pi_mode);
WritePCI2PI_u32(hw, PCI2PI_DEL_CS, PCI2PI_config.del_cs);
WritePCI2PI_u32(hw, PCI2PI_DEL_RD, PCI2PI_config.del_rd);
WritePCI2PI_u32(hw, PCI2PI_DEL_WR, PCI2PI_config.del_wr);
WritePCI2PI_u32(hw, PCI2PI_DEL_ALE, PCI2PI_config.del_ale);
WritePCI2PI_u32(hw, PCI2PI_DEL_ADR, PCI2PI_config.del_adr);
WritePCI2PI_u32(hw, PCI2PI_DEL_DOUT, PCI2PI_config.del_dout);
WritePCI2PI_u32(hw, PCI2PI_DEFAULT_ADR, PCI2PI_config.default_adr);
WritePCI2PI_u32(hw, PCI2PI_DEFAULT_DOUT,
PCI2PI_config.default_dout);
WritePCI2PI_u32(hw, PCI2PI_CYCLE_SHD, 0x80 * PCI2PI_config.setup
+ 0x40 * PCI2PI_config.hold + PCI2PI_config.cycle);
WritePCI2PI_u32(hw, PCI2PI_ALE_ADR_WHSF,
PCI2PI_config.ale_adr_first +
PCI2PI_config.ale_adr_setup * 2 +
PCI2PI_config.ale_adr_hold * 4 +
PCI2PI_config.ale_adr_wait * 8);
WritePCI2PI_u32(hw, PCI2PI_CYCLE_PAUSE,
0x10 * PCI2PI_config.pause_seq +
PCI2PI_config.pause_end);
WritePCI2PI_u32(hw, PCI2PI_STATUS_INT_ENABLE,
PCI2PI_config.status_int_enable);
WritePCI2PI_u32(hw, PCI2PI_PI_INT_POL,
2 * PCI2PI_config.pi_wait_enable +
PCI2PI_config.pi_int_pol);
WritePCI2PI_u32(hw, PCI2PI_SPI_CFG0, PCI2PI_config.spi_cfg0);
WritePCI2PI_u32(hw, PCI2PI_SPI_CFG1, PCI2PI_config.spi_cfg1);
WritePCI2PI_u32(hw, PCI2PI_SPI_CFG2, PCI2PI_config.spi_cfg2);
WritePCI2PI_u32(hw, PCI2PI_SPI_CFG3, PCI2PI_config.spi_cfg3);
WritePCI2PI_u32(hw, PCI2PI_EEP_RECOVER, PCI2PI_config.eep_recover);
ReadPCI2PI_u32(hw, PCI2PI_STATUS);
/* release hardware reset XHFC */
WritePCI2PI_u32(hw, PCI2PI_GPIO_OUT, GPIO_OUT_VAL | PCI2PI_GPIO7_NRST);
udelay(10);
return (err);
}

View File

@ -0,0 +1,674 @@
/* $Id$
*
* PCI2PI Pci Bridge support for xhfc_su.c
*
* Authors : Martin Bachem, Joerg Ciesielski
* Contact : info@colognechip.com
*
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef _XHFC_PCI2PI_H_
#define _XHFC_PCI2PI_H_
#include "xhfc24succ.h"
/* differnt PCI modes supported by PCI2PI */
#define PI_INTELNOMX 0
#define PI_INTELMX 1
#define PI_MOT 2
#define PI_MOTMX 3
#define PI_SPI 4
#define PI_EEPPRG 6
#define PI_AUTOEEP 7
/* PI_MODE to GPIO mapping */
#define PI_INTELMX_GPIO PCI2PI_GPIO3_MODE1 /* ALE pulse switches to MULTIPLEXED */
#define PI_INTELNOMX_GPIO PCI2PI_GPIO3_MODE1
#define PI_MOT_GPIO PCI2PI_GPIO2_MODE0
#define PI_MOTMX_GPIO PCI2PI_GPIO2_MODE0
#define PI_SPI_GPIO 0
#define PI_AUTOEEP_GPIO PCI2PI_GPIO2_MODE0 | PCI2PI_GPIO3_MODE1
/* PCI2PI GPIO to XHFC signal mapping */
#define PCI2PI_GPIO7_NRST 0x80
#define PCI2PI_GPIO6_TLA3 0x40
#define PCI2PI_GPIO5_TLA2 0x20
#define PCI2PI_GPIO4_TLA1 0x10
#define PCI2PI_GPIO3_MODE1 0x08
#define PCI2PI_GPIO2_MODE0 0x04
#define PCI2PI_GPIO1_BOND1 0x02
#define PCI2PI_GPIO0_BOND0 0x01
/* PCI2PI GPIO XHFC Bond out selection */
#define XHFC_1SU_BOND 0
#define XHFC_2SU_BOND PCI2PI_GPIO0_BOND0
#define XHFC_2S4U_BOND PCI2PI_GPIO1_BOND1
#define XHFC_4SU_BOND PCI2PI_GPIO1_BOND1 | PCI2PI_GPIO0_BOND0
/*******************************************************/
/*******************************************************/
/* Select processor interface mode and Bond option */
/* of PCI2PI bridge */
#define PI_MODE PI_INTELMX
#define XHFC_BOND XHFC_4SU_BOND
/*******************************************************/
/*******************************************************/
#if (PI_MODE == PI_INTELNOMX)
#define GPIO_OUT_VAL XHFC_BOND | PI_INTELNOMX_GPIO
#elif (PI_MODE == PI_INTELMX)
#define GPIO_OUT_VAL XHFC_BOND | PI_INTELMX_GPIO
#elif (PI_MODE == PI_MOT)
#define GPIO_OUT_VAL XHFC_BOND | PI_MOT_GPIO
#elif (PI_MODE == PI_MOTMX)
#define GPIO_OUT_VAL XHFC_BOND | PI_MOTMX_GPIO
#elif (PI_MODE == PI_SPI)
#define GPIO_OUT_VAL XHFC_BOND | PI_SPI_GPIO
#elif (PI_MODE == PI_AUTOEEP)
#define GPIO_OUT_VAL XHFC_BOND | PI_AUTOEEP_GPIO
#endif
/*******************************************************/
#define PCI2PI_VENDORID 0x1397
#define PCI2PI_DEVICEID 0xA003
#define MAX_PCI2PI 2
#define RV_PCI2PI_OK 0
#define RV_PCI2PI_ERROR 1
/* PCI2PI register definitions */
#define PCI2PI_OFFSET 0x00000800
#define PCI2PI_DEL_CS 4*0x00 + PCI2PI_OFFSET
#define PCI2PI_DEL_RD 4*0x01 + PCI2PI_OFFSET
#define PCI2PI_DEL_WR 4*0x02 + PCI2PI_OFFSET
#define PCI2PI_DEL_ALE 4*0x03 + PCI2PI_OFFSET
#define PCI2PI_DEL_ADR 4*0x04 + PCI2PI_OFFSET
#define PCI2PI_DEL_DOUT 4*0x05 + PCI2PI_OFFSET
#define PCI2PI_DEFAULT_ADR 4*0x06 + PCI2PI_OFFSET
#define PCI2PI_DEFAULT_DOUT 4*0x07 + PCI2PI_OFFSET
/*
PCI2PI_PI_MODE bit 2..0:
000: Intel non multiplexed
001: Intel multiplexed
010: Motorola
100: SPI Motorola
110: EEPROM programming mode throug PCIIF00 Target
111: XHFC AutoEEPROM mode
*/
#define PCI2PI_PI_MODE 4*0x08 + PCI2PI_OFFSET
#define PCI2PI_CYCLE_SHD 4*0x09 + PCI2PI_OFFSET
#define PCI2PI_ALE_ADR_WHSF 4*0x0a + PCI2PI_OFFSET
#define PCI2PI_CYCLE_PAUSE 4*0x0b + PCI2PI_OFFSET
#define PCI2PI_GPIO_OUT 4*0x0c + PCI2PI_OFFSET
#define PCI2PI_G1 4*0x0e + PCI2PI_OFFSET
#define PCI2PI_G0 4*0x0f + PCI2PI_OFFSET
/*
bit0: is set by PI_INT active, is reseted by reading this register
bit1: is set by PI_WAIT active, is reseted by reading this register
*/
#define PCI2PI_STATUS 4*0x10 + PCI2PI_OFFSET
/* bit0: enable PCI interrupt output */
#define PCI2PI_STATUS_INT_ENABLE 4*0x11 + PCI2PI_OFFSET
/*
bit0: 0 = low active interrupt is detected at PI_INT
bit0: 1 = high active interrupt is detected at PI_INT
*/
#define PCI2PI_PI_INT_POL 4*0x12 + PCI2PI_OFFSET
/* SPI registers */
/* 32 bit SPI master data output register */
#define PCI2PI_SPI_MO_DATA 4*0x20 + PCI2PI_OFFSET
/* 32 bit SPI master data input register */
#define PCI2PI_SPI_MI_DATA 4*0x21 + PCI2PI_OFFSET
/*
bit 0: 0 SPI bits are processing on the serial input/output
bit 0: 1 SPI bits are processed, new data can be written or read
bit 1..31: unused
*/
#define PCI2PI_SPI_STATUS 4*0x22 + PCI2PI_OFFSET
/*
bit 0: spi clock polarity, defines level for SPISEL1
bit 1: spi clock phase, defines sampling edge, 0?, 1?
bit 2: 0MSB first (default) , 1LSB first
bit 3: 1SPI clock permanent during SPISEL1
*/
#define PCI2PI_SPI_CFG0 4*0x28 + PCI2PI_OFFSET
/*
bit 0..3: spi clock frequency, SPI clock period (value+1) x 2 x PCIperiod
0: 2 PCIperiods
1:
*/
#define PCI2PI_SPI_CFG1 4*0x29 + PCI2PI_OFFSET
/* bit 0..3: SPI Device SEL: defines level of D0..D3, XHFC SPI address */
#define PCI2PI_SPI_CFG2 4*0x2A + PCI2PI_OFFSET
/*
bit 0: 1spi master out permanent driven
bit 1: 1SPISEL remains low between bytes of a sequence
bit 2: 1SPISEL remains low permanent
*/
#define PCI2PI_SPI_CFG3 4*0x2B + PCI2PI_OFFSET
/* bit 0..3: default 0100 */
#define PCI2PI_EEP_RECOVER 4*0x30 + PCI2PI_OFFSET
typedef struct _PCI2PI_cfg {
__u32 del_cs; /* Bit 3..0, bit 3: 0.5 PCI clk,
bits 2..0: gate delay */
__u32 del_rd;
__u32 del_wr;
__u32 del_ale;
__u32 del_adr; /* delay between default address
value and selected address */
__u32 del_dout; /* delay between default data
value and written data */
__u32 default_adr; /* default address value bit 0..7 */
__u32 default_dout; /* default data value bit 0..7 */
__u32 pi_mode; /* pi_mode bit 2..0:
000: Intel non multiplexed
001: Intel multiplexed
010: Motorola
100: SPI Motorola
110: EEPROM programming mode throug PCIIF00 Target
111: XHFC AutoEEPROM mode
*/
__u32 setup; /* address/data setup berfore rd/wr/cs,
0 or 1 PCI clk */
__u32 hold; /* address/data hold after rd/wr/cs,
0 or 1 PCI clk */
__u32 cycle; /* bit 0 only
address/data adds 0 or 1 PCI clk to /rd/wr/cs
access time (cycle+1) PCI clk
*/
__u32 ale_adr_first; /* bit 0 = 0: address valid before ALE=1
bit 0 = 1: ALE=1 before adress valid */
__u32 ale_adr_setup; /* bit 0 = 0 setup for ALE/addr = 0s
bit 0 = 1:setup for ALE/addr = 1 PCI clk
ALE/addr depends on ale_adr_first setting */
__u32 ale_adr_hold; /* bit 0 = 0 hold for address after ALE: 0s
bit 0 = 1:hold for address after ALE: 1 PCI clk */
__u32 ale_adr_wait; /* bit 0 = 0: 0 PCI clk delay between address and data phase
bit 0 = 1: 1 PCI clk delay between address and data phase */
__u32 pause_seq; /* bit 0..3: number of PCI clocks between read/write
acceses due to DWORD/WORD burst access */
__u32 pause_end; /* bit 0..3: number of PCI clocks after DWORD/WORD
burst access /delays PCI_TRDY signal */
__u32 gpio_out; /* bit 0..7: GPIO output value */
__u32 status_int_enable; /* bit 0: enables PCI interrupt for PI_INT signal
bit 1: enables PCI interrupt for PI_NWAIT signal */
__u32 pi_int_pol; /* bit 0: polarity of PI_INT signal */
__u32 pi_wait_enable; /* access length can be controled by /wait signal
0 wait disabled, 1 wait enabled */
__u32 spi_cfg0;
__u32 spi_cfg1;
__u32 spi_cfg2;
__u32 spi_cfg3;
__u32 eep_recover;
} PCI2PI_cfg;
/*
read and write functions to access registers of the PCI bridge
*/
static inline __u8
ReadPCI2PI_u8(xhfc_hw * hw, __u16 reg_addr)
{
return (*(volatile __u8 *) (hw->membase + reg_addr));
}
static inline __u16
ReadPCI2PI_u16(xhfc_hw * hw, __u16 reg_addr)
{
return (*(volatile __u16 *) (hw->membase + reg_addr));
}
static inline __u32
ReadPCI2PI_u32(xhfc_hw * hw, __u16 reg_addr)
{
return (*(volatile __u32 *) (hw->membase + reg_addr));
}
static inline void
WritePCI2PI_u8(xhfc_hw * hw, __u16 reg_addr, __u8 value)
{
*((volatile __u8 *) (hw->membase + reg_addr)) = value;
}
static inline void
WritePCI2PI_u16(xhfc_hw * hw, __u16 reg_addr, __u16 value)
{
*((volatile __u16 *) (hw->membase + reg_addr)) = value;
}
static inline void
WritePCI2PI_u32(xhfc_hw * hw, __u16 reg_addr, __u32 value)
{
*((volatile __u32 *) (hw->membase + reg_addr)) = value;
}
/*
read and write functions to access a XHFC at the local bus interface of the PCI
bridge
there are two sets of functions to access the XHFC in the following different
interface modes:
multiplexed bus interface modes PI_INTELMX and PI_MOTMX
- these modes use a single (atomic) PCI cycle to read or write a XHFC register
non multiplexed bus interface modes PI_INTELNOMX, PI_MOTMX and PI_SPI
- these modes use a separate PCI cycles to select the XHFC register and to read
or write data. That means these register accesses are non atomic and could be
interrupted by an interrupt. The driver must take care that a register access in
these modes is not interrupted by its own interrupt handler.
*/
/*****************************************************************************/
#if ((PI_MODE==PI_INTELMX) || (PI_MODE==PI_MOTMX))
/*
functions for multiplexed access
*/
static inline __u8
read_xhfc(xhfc_hw * hw, __u8 reg_addr)
{
return (*(volatile __u8 *) (hw->membase + (reg_addr << 2)));
}
/*
read four bytes from the same register address
e.g. A_FIFO_DATA
this function is only defined for software compatibility here
*/
static inline __u32
read32_xhfc(xhfc_hw * hw, __u8 reg_addr)
{
__u32 value;
value = (*(volatile __u8 *) (hw->membase + (reg_addr << 2)));
value |= (*(volatile __u8 *) (hw->membase + (reg_addr << 2))) << 8;
value |= (*(volatile __u8 *) (hw->membase + (reg_addr << 2))) << 16;
value |= (*(volatile __u8 *) (hw->membase + (reg_addr << 2))) << 24;
return (value);
}
static inline void
write_xhfc(xhfc_hw * hw, __u8 reg_addr, __u8 value)
{
*((volatile __u8 *) (hw->membase + (reg_addr << 2))) = value;
}
/*
writes four bytes to the same register address
e.g. A_FIFO_DATA
this function is only defined for software compatibility here
*/
static inline void
write32_xhfc(xhfc_hw * hw, __u8 reg_addr, __u32 value)
{
*((volatile __u8 *) (hw->membase + (reg_addr << 2))) = value & 0xff;
*((volatile __u8 *) (hw->membase + (reg_addr << 2))) = (value >>8) & 0xff;
*((volatile __u8 *) (hw->membase + (reg_addr << 2))) = (value >>16) & 0xff;
*((volatile __u8 *) (hw->membase + (reg_addr << 2))) = (value >>24) & 0xff;
}
/* always reads a single byte with short read method
this allows to read ram based registers
that normally requires long read access times
*/
static inline __u8
sread_xhfc(xhfc_hw * hw, __u8 reg_addr)
{
(*(volatile __u8 *) (hw->membase + (reg_addr << 2)));
return (*(volatile __u8 *) (hw->membase + (R_INT_DATA << 2)));
}
/* this function reads the currently selected regsiter from XHFC and is only
required for non multiplexed access modes. For multiplexed access modes this
function is only defined for for software compatibility. */
static inline __u8
read_xhfcregptr(xhfc_hw * hw)
{
return 0;
}
/* this function writes the XHFC register address pointer and is only
required for non multiplexed access modes. For multiplexed access modes this
function is only defined for for software compatibility. */
static inline void
write_xhfcregptr(xhfc_hw * hw, __u8 reg_addr)
{
}
#endif /* PI_MODE==PI_INTELMX || PI_MODE==PI_MOTMX */
/*****************************************************************************/
#if PI_MODE==PI_INTELNOMX || PI_MODE==PI_MOT
/*
functions for non multiplexed access
XHFC register address pointer is accessed with PCI address A2=1 and XHFC data
port is accessed with PCI address A2=0
*/
static inline __u8
read_xhfc(xhfc_hw * hw, __u8 reg_addr)
{
*((volatile __u8 *) (hw->membase + 4)) = reg_addr;
return (*(volatile __u8 *) (hw->membase));
}
/*
read four bytes from the same register address by using a 32bit PCI access. The
PCI bridge generates for 8 bit data read cycles at the local bus interface.
*/
static inline __u32
read32_xhfc(xhfc_hw * hw, __u8 reg_addr)
{
*((volatile __u8 *) (hw->membase + 4)) = reg_addr;
return (*(volatile __u32 *) hw->membase);
}
static inline void
write_xhfc(xhfc_hw * hw, __u8 reg_addr, __u8 value)
{
*((volatile __u8 *) (hw->membase + 4)) = reg_addr;
*((volatile __u8 *) (hw->membase)) = value;
}
/*
writes four bytes to the same register address (e.g. A_FIFO_DATA) by using a
32bit PCI access. The PCI bridge generates for 8 bit data write cycles at the
local bus interface.
*/
static inline void
write32_xhfc(xhfc_hw * hw, __u8 reg_addr, __u32 value)
{
*((volatile __u8 *) (hw->membase + 4)) = reg_addr;
*((volatile __u32 *) (hw->membase)) = value;
}
/*
reads a single byte with short read method (r*). This allows to read ram based
registers that normally requires long read access times
*/
static inline __u8
sread_xhfc(xhfc_hw * hw, __u8 reg_addr)
{
*((volatile __u8 *) (hw->membase + 4)) = reg_addr;
(*(volatile __u8 *) (hw->membase));
*((volatile __u8 *) (hw->membase + 4)) = R_INT_DATA;
return (*(volatile __u8 *) (hw->membase));
}
/*
this function reads the currently selected regsiter from XHFC
*/
static inline __u8
read_xhfcregptr(xhfc_hw * hw)
{
return (*(volatile __u8 *) (hw->membase + 4));
}
/* this function writes the XHFC register address pointer */
static inline void
write_xhfcregptr(xhfc_hw * hw, __u8 reg_addr)
{
*((volatile __u8 *) (hw->membase + 4)) = reg_addr;
}
#endif /* PI_MODE==PI_INTELNOMX || PI_MODE==PI_MOT */
/*****************************************************************************/
#if PI_MODE == PI_SPI
// SPI mode transaction bit definitions
#define SPI_ADDR 0x40
#define SPI_DATA 0x00
#define SPI_RD 0x80
#define SPI_WR 0x00
#define SPI_BROAD 0x20
#define SPI_MULTI 0x20
/*
functions for SPI access
*/
static inline __u8
read_xhfc(xhfc_hw * hw, __u8 reg_addr)
{
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// initiate a 32 clock SPI master transfer
WritePCI2PI_u32(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR) << 24) | (reg_addr << 16) | ((SPI_DATA | SPI_RD) << 8));
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// read data from the SPI data receive register and return one byte
return (__u8) (ReadPCI2PI_u32(hw, PCI2PI_SPI_MI_DATA) & 0xFF);
}
/*
read four bytes from the same register address by using a SPI multiple read access
*/
static inline __u32
read32_xhfc(xhfc_hw * hw, __u8 reg_addr)
{
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// initiate a 16 clock SPI master transfer
WritePCI2PI_u16(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR) << 8) | reg_addr);
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// initiate a 8 clock SPI master transfer
WritePCI2PI_u8(hw, PCI2PI_SPI_MO_DATA, (SPI_DATA | SPI_RD | SPI_MULTI));
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// initiate a 32 clock SPI master transfer
// output data is arbitrary
WritePCI2PI_u32(hw, PCI2PI_SPI_MO_DATA, 0);
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// read data from the SPI data receive register and return four bytes
return (__u32) be32_to_cpu(ReadPCI2PI_u32(hw, PCI2PI_SPI_MI_DATA));
}
static inline void
write_xhfc(xhfc_hw * hw, __u8 reg_addr, __u8 value)
{
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// initiate a 32 clock SPI master transfer
WritePCI2PI_u32(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR) << 24) | (reg_addr << 16) | ((SPI_DATA | SPI_WR) << 8) | value);
}
/*
writes four bytes to the same register address (e.g. A_FIFO_DATA) by using a SPI
multiple write access
*/
static inline void
write32_xhfc(xhfc_hw * hw, __u8 reg_addr, __u32 value)
{
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// initiate a 16 clock SPI master transfer
WritePCI2PI_u16(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR) << 8) | reg_addr);
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// initiate a 8 clock SPI master transfer
WritePCI2PI_u8(hw, PCI2PI_SPI_MO_DATA, (SPI_DATA | SPI_WR | SPI_MULTI));
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// initiate a 32 clock SPI master transfer
WritePCI2PI_u32(hw, PCI2PI_SPI_MO_DATA, cpu_to_be32(value));
}
/*
reads a single byte with short read method (r*). This allows to read ram based
registers that normally requires long read access times
*/
static inline __u8
sread_xhfc(xhfc_hw * hw, __u8 reg_addr)
{
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// initiate a 32 clock SPI master transfer
WritePCI2PI_u32(hw, PCI2PI_SPI_MO_DATA ,((SPI_ADDR | SPI_WR) << 24) | (reg_addr << 16) | ((SPI_DATA | SPI_RD) << 8));
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// initiate a 32 clock SPI master transfer to read R_INT_DATA register
WritePCI2PI_u32(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR) << 24) | (R_INT_DATA << 16) | ((SPI_DATA | SPI_RD) << 8));
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// read data from the SPI data receive register and return one byte
return (__u8) (ReadPCI2PI_u32(hw, PCI2PI_SPI_MI_DATA) & 0xFF);
}
/*
this function reads the currently selected regsiter from XHFC
*/
static inline __u8
read_xhfcregptr(xhfc_hw * hw)
{
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// initiate a 16 clock SPI master transfer
WritePCI2PI_u16(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_RD) << 8));
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// read data from the SPI data receive register and return one byte
return (__u8) (ReadPCI2PI_u32(hw, PCI2PI_SPI_MI_DATA) & 0xFF);
}
/* this function writes the XHFC register address pointer */
static inline void
write_xhfcregptr(xhfc_hw * hw, __u8 reg_addr)
{
// wait until SPI master is idle
while (!(ReadPCI2PI_u32(hw, PCI2PI_SPI_STATUS) & 1));
// initiate a 16 clock SPI master transfer
WritePCI2PI_u16(hw, PCI2PI_SPI_MO_DATA, ((SPI_ADDR | SPI_WR) << 8) | reg_addr);
}
#endif /* PI_MODE == PI_SPI */
/* Function Prototypes */
int init_pci_bridge(xhfc_hw * hw);
#endif /* _XHFC_PCI2PI_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,211 @@
/* $Id$
*
* mISDN driver for Colognechip xHFC chip
*
* Authors : Martin Bachem, Joerg Ciesielski
* Contact : info@colognechip.com
*
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef _XHFC_SU_H_
#define _XHFC_SU_H_
#include <linux/timer.h>
#include "channel.h"
#include "xhfc24succ.h"
#define DRIVER_NAME "XHFC"
#ifndef CHIP_ID_2S4U
#define CHIP_ID_2S4U 0x62
#endif
#ifndef CHIP_ID_4SU
#define CHIP_ID_4SU 0x63
#endif
#ifndef CHIP_ID_1SU
#define CHIP_ID_1SU 0x60
#endif
#ifndef CHIP_ID_2SU
#define CHIP_ID_2SU 0x61
#endif
/* define bridge for chip register access */
#define BRIDGE_UNKWOWN 0
#define BRIDGE_PCI2PI 1 /* used at Cologne Chip AG's Evaluation Card */
#define BRIDGE BRIDGE_PCI2PI
#define MAX_PORT 4
#define CHAN_PER_PORT 4 /* D, B1, B2, PCM */
#define MAX_CHAN MAX_PORT * CHAN_PER_PORT
/* flags in _u16 port mode */
#define PORT_UNUSED 0x0000
#define PORT_MODE_NT 0x0001
#define PORT_MODE_TE 0x0002
#define PORT_MODE_S0 0x0004
#define PORT_MODE_UP 0x0008
#define PORT_MODE_EXCH_POL 0x0010
#define PORT_MODE_LOOP 0x0020
#define NT_TIMER 0x8000
/* NT / TE defines */
#define NT_T1_COUNT 25 /* number of 4ms interrupts for G2 timeout */
#define CLK_DLY_TE 0x0e /* CLKDEL in TE mode */
#define CLK_DLY_NT 0x6c /* CLKDEL in NT mode */
#define STA_ACTIVATE 0x60 /* start activation in A_SU_WR_STA */
#define STA_DEACTIVATE 0x40 /* start deactivation in A_SU_WR_STA */
#define LIF_MODE_NT 0x04 /* Line Interface NT mode */
#define XHFC_TIMER_T3 8000 /* 8s activation timer T3 */
#define XHFC_TIMER_T4 500 /* 500ms deactivation timer T4 */
/* xhfc Layer1 physical commands */
#define HFC_L1_ACTIVATE_TE 0x01
#define HFC_L1_FORCE_DEACTIVATE_TE 0x02
#define HFC_L1_ACTIVATE_NT 0x03
#define HFC_L1_DEACTIVATE_NT 0x04
#define HFC_L1_TESTLOOP_B1 0x05
#define HFC_L1_TESTLOOP_B2 0x06
/* xhfc Layer1 Flags (stored in xhfc_port_t->l1_flags) */
#define HFC_L1_ACTIVATING 1
#define HFC_L1_ACTIVATED 2
#define HFC_L1_DEACTTIMER 4
#define HFC_L1_ACTTIMER 8
#define FIFO_MASK_TX 0x55555555
#define FIFO_MASK_RX 0xAAAAAAAA
/* DEBUG flags, use combined value for module parameter debug=x */
#define DEBUG_HFC_INIT 0x0001
#define DEBUG_HFC_MODE 0x0002
#define DEBUG_HFC_S0_STATES 0x0004
#define DEBUG_HFC_IRQ 0x0008
#define DEBUG_HFC_FIFO_ERR 0x0010
#define DEBUG_HFC_DTRACE 0x2000
#define DEBUG_HFC_BTRACE 0x4000 /* very(!) heavy messageslog load */
#define DEBUG_HFC_FIFO 0x8000 /* very(!) heavy messageslog load */
#define USE_F0_COUNTER 1 /* akkumulate F0 counter diff every irq */
#define TRANSP_PACKET_SIZE 0 /* minium tranparent packet size for transmittion to upper layer */
/* private driver_data */
typedef struct {
int chip_id;
char *device_name;
} xhfc_param;
/* port struct for each S/U port */
typedef struct {
int idx;
__u8 dpid; /* DChannel Protocoll ID */
__u16 mode; /* NT/TE + ST/U */
int nt_timer;
u_long l1_flags;
struct timer_list t3_timer; /* timer 3 for activation/deactivation */
struct timer_list t4_timer; /* timer 4 for activation/deactivation */
/* chip registers */
reg_a_su_ctrl0 su_ctrl0;
reg_a_su_ctrl1 su_ctrl1;
reg_a_su_ctrl2 su_ctrl2;
reg_a_st_ctrl3 st_ctrl3;
} xhfc_port_t;
/* channel struct for each fifo */
typedef struct {
channel_t ch;
xhfc_port_t * port;
} xhfc_chan_t;
struct _xhfx_hw;
/**********************/
/* hardware structure */
/**********************/
typedef struct _xhfx_hw {
struct list_head list;
spinlock_t lock;
struct tasklet_struct tasklet; /* interrupt bottom half */
int cardnum;
__u8 param_idx; /* used to access module param arrays */
int ifnum;
__u8 testirq;
int irq;
int iobase;
int nt_mode;
u_char *membase;
u_char *hw_membase;
#if BRIDGE == BRIDGE_PCI2PI
struct pci_dev *pdev;
#endif
xhfc_param driver_data;
char card_name[60];
int chip_id;
int num_ports; /* number of S and U interfaces */
int max_fifo; /* always 4 fifos per port */
__u8 max_z; /* fifo depth -1 */
xhfc_port_t port[MAX_PORT]; /* one for each Line intercace */
xhfc_chan_t chan[MAX_CHAN]; /* one each D/B/PCM channel */
__u32 irq_cnt; /* count irqs */
__u32 f0_cnt; /* last F0 counter value */
__u32 f0_akku; /* akkumulated f0 counter deltas */
/* chip registers */
reg_r_irq_ctrl irq_ctrl;
reg_r_misc_irqmsk misc_irqmsk; /* mask of enabled interrupt sources */
reg_r_misc_irq misc_irq; /* collect interrupt status bits */
reg_r_su_irqmsk su_irqmsk; /* mask of line interface state change interrupts */
reg_r_su_irq su_irq; /* collect interrupt status bits */
reg_r_ti_wd ti_wd; /* timer interval */
reg_r_pcm_md0 pcm_md0;
reg_r_pcm_md1 pcm_md1;
__u32 fifo_irq; /* fifo bl irq */
__u32 fifo_irqmsk; /* fifo bl irq */
} xhfc_hw;
/* function prototypes */
int setup_channel(xhfc_hw * hw, __u8 channel, int protocol);
void xhfc_write_fifo(xhfc_hw * hw, __u8 channel);
void xhfc_read_fifo(xhfc_hw * hw, __u8 channel);
void print_fc(xhfc_hw * hw, __u8 fifo);
void setup_fifo(xhfc_hw * hw, __u8 fifo, __u8 conhdlc, __u8 subcfg,
__u8 fifoctrl, __u8 enable);
void setup_su(xhfc_hw * hw, __u8 pt, __u8 bc, __u8 enable);
#endif /* _XHFC_SU_H_ */

544
misdn-init Executable file
View File

@ -0,0 +1,544 @@
#!/bin/bash
################################################################################
#
# misdn-init init script
#
# Copyright (C) 2005, Nadi Sarrar
#
# Nadi Sarrar <nadi@beronet.com>
#
# This program is free software, distributed under the terms of
# the GNU General Public License
#
#
# USAGE:
#
# /etc/init.d/misdn-init start|stop|restart|config|scan|help
#
#
# CONFIGURATION:
#
# Path to your misdn-init.conf:
#
misdn_init_conf="/etc/misdn-init.conf"
#
################################################################################
MODPROBE=modprobe
RMMOD=rmmod
INSMOD=insmod
LSPCI=lspci
MKNOD=mknod
# HFC 8/4 (S0) Options
master_clock=16
# HFC-E1 Options
optical=16
los=18
ais=19
slip=20
nocrc4=24
# Card Settings
ulaw=8
dtmf=9
pcm_slave=11
function remove_preloaded_modules {
tmp=$(lsmod)
cards="hfcmulti hfcpci avmfritz w6692 hfcusb"
for i in $cards ; do
echo $tmp | grep $i >/dev/null && (echo "$i already loaded, removing it"; modprobe -r $i)
done
}
function load_card_modules {
IFS=$'\n'
for line in $(sed -n -e '/^[^#]/p' ${misdn_init_conf});
do
var=`echo "${line}" | sed -e "s/^\(.*\)=.*/\1/"`
val=`echo "${line}" | sed -e "s/^.*=\(.*\)/\1/"`
case "${var}" in
card)
nr=`echo "${val}" | sed -e "s/^\([0-9]*\),.*/\1/"`
mod=`echo "${val}" | sed -e "s/^[^,]*,\([^,]*\).*/\1/"`
if [ ${#val} -gt $(echo "obase=10;${#nr}+${#mod}+1" | bc) ]; then
opns=`echo "${val}" | sed -e "s/^[^,]*,[^,]*,\(.*\)/\1/"`
else
opns=""
fi
case "${mod}" in
0x*)
hfcmulti[${nr}]=$(echo ${mod} | sed -e "s/^0x\([0-9]*\)/\1/")
let "hfcports = ${hfcports} + ${hfcmulti[${nr}]}"
IFS=$','
for li in ${opns}; do
hfcmulti[${nr}]=$(echo "obase=10;2^(${!li}-1)+${hfcmulti[${nr}]}" | bc)
done
IFS=$'\n'
;;
*)
other_card[${nr}]=${mod}
;;
esac
;;
te_ptp)
IFS=$','
for li in ${val}; do
layermask[${li}]="0xf"
protocol[${li}]=34 # 0x22 == 34
done
IFS=$'\n'
;;
te_ptmp)
IFS=$','
for li in ${val}; do
layermask[${li}]="0xf"
protocol[${li}]=2 # 0x2 == 2
done
IFS=$'\n'
;;
nt_*)
IFS=$','
for li in ${val}; do
layermask[${li}]="0x3"
protocol[${li}]=18 # 0x12 == 18
done
IFS=$'\n'
;;
te_capi_ptp)
IFS=$','
for li in ${val}; do
layermask[${li}]="0x0"
protocol[${li}]=34 # 0x22 == 34
done
IFS=$'\n'
addcapi=1
;;
te_capi_ptmp)
IFS=$','
for li in ${val}; do
layermask[${li}]="0x0"
protocol[${li}]=2 # 0x2 == 2
done
IFS=$'\n'
addcapi=1
;;
option)
port=`echo "${val}" | sed -e "s/^\([0-9]*\),.*/\1/"`
opt=`echo "${val}" | sed -e "s/^[0-9]*,\(.*\)/\1/"`
if [ -z ${protocol[${port}]} ]; then
protocol[${port}]="0"
fi
IFS=$','
for li in ${opt}; do
protocol[${port}]=$(echo "obase=10;2^(${!li}-1)+${protocol[${port}]}" | bc)
done
IFS=$'\n'
;;
poll)
poll=${val}
;;
pcm)
pcm=${val}
;;
debug)
debug=${val}
;;
*)
echo "unknown variable: ${var}"
;;
esac
done
echo "-----------------------------------------"
echo " Loading module(s) for your misdn-cards:"
echo "-----------------------------------------"
card_index=1
port_index=1
while [ ! -z ${hfcmulti[${card_index}]} ] || [ ! -z ${other_card[${card_index}]} ];
do
if [ ! -z ${hfcmulti[${card_index}]} ]; then
# MODPROBE COMMAND FOR hfcmulti CARD
hfcmulti_type="type="
hfcmulti_prot="protocol="
hfcmulti_layer="layermask="
while [ ! -z ${hfcmulti[${card_index}]} ];
do
hfcmulti_type="${hfcmulti_type}$(echo "obase=16;\"0x\";${hfcmulti[${card_index}]}" | bc ),"
let "card_index = ${card_index} + 1"
done
while [ ${hfcports} -gt 0 ];
do
if [ ! -z ${protocol[${port_index}]} ]; then
hfcmulti_prot="${hfcmulti_prot}$(echo "obase=16;\"0x\";${protocol[${port_index}]}" | bc),"
else
hfcmulti_prot="${hfcmulti_prot}0x2,"
fi
if [ ! -z ${layermask[${port_index}]} ]; then
hfcmulti_layer="${hfcmulti_layer}${layermask[${port_index}]},"
else
hfcmulti_layer="${hfcmulti_layer}0xf,"
fi
let "port_index = ${port_index} + 1"
let "hfcports = ${hfcports} - 1"
done
hfcmulti_type="$(echo ${hfcmulti_type} | sed -e 's/^\(.*\),$/\1/')"
hfcmulti_prot="$(echo ${hfcmulti_prot} | sed -e 's/^\(.*\),$/\1/')"
hfcmulti_layer="$(echo ${hfcmulti_layer} | sed -e 's/^\(.*\),$/\1/')"
hfcmulti_cmd="modprobe hfcmulti ${hfcmulti_type} ${hfcmulti_prot} ${hfcmulti_layer}"
if [ ! -z ${poll} ]; then
hfcmulti_cmd="${hfcmulti_cmd} poll=${poll}"
fi
if [ ! -z ${pcm} ]; then
hfcmulti_cmd="${hfcmulti_cmd} pcm=${pcm}"
fi
if [ ! -z ${debug} ]; then
hfcmulti_cmd="${hfcmulti_cmd} debug=${debug}"
fi
echo ${hfcmulti_cmd}
eval ${hfcmulti_cmd}
else
# MODPROBE COMMAND FOR _NON_ hfcmulti CARD
other_cmd="modprobe ${other_card[${card_index}]}"
if [ ! -z ${protocol[${port_index}]} ]; then
other_prot="protocol=$(echo "obase=16;\"0x\";${protocol[${port_index}]}" | bc),"
else
other_prot="protocol=0x2,"
fi
if [ ! -z ${layermask[${port_index}]} ]; then
other_layer="layermask=${layermask[${port_index}]},"
else
other_layer="layermask=0xf,"
fi
let "prev = ${card_index}"
let "card_index = ${card_index} + 1"
let "port_index = ${port_index} + 1"
while [ "${other_card[${card_index}]}" == "${other_card[${prev}]}" ];
do
if [ ! -z ${protocol[${port_index}]} ]; then
other_prot="${other_prot}$(echo "obase=16;\"0x\";${protocol[${port_index}]}" | bc),"
else
other_prot="${other_prot}0x2,"
fi
if [ ! -z ${layermask[${port_index}]} ]; then
other_layer="${other_layer}${layermask[${port_index}]},"
else
other_layer="${other_layer}0xf,"
fi
let "prev = ${card_index}"
let "card_index = ${card_index} + 1"
let "port_index = ${port_index} + 1"
done
other_prot="$(echo ${other_prot} | sed -e 's/^\(.*\),$/\1/')"
other_layer="$(echo ${other_layer} | sed -e 's/^\(.*\),$/\1/')"
other_cmd="${other_cmd} ${other_prot} ${other_layer}"
echo "${other_cmd}"
eval ${other_cmd}
fi
done
}
function unload_card_modules {
IFS=$'\n'
for line in $(sed -ne '/^[^#]/p' ${misdn_init_conf});
do
var=`echo "${line}" | sed -e "s/^\(.*\)=.*/\1/"`
val=`echo "${line}" | sed -e "s/^.*=\(.*\)/\1/"`
case "${var}" in
card)
nr=`echo "${val}" | sed -e "s/^\([0-9]*\),.*/\1/"`
mod=`echo "${val}" | sed -e "s/^[^,]*,\([^,]*\).*/\1/"`
case "${mod}" in
0x*)
modulelist[${nr}]=hfcmulti
;;
*)
modulelist[${nr}]=${mod}
;;
esac
;;
esac
done
echo "-------------------------------------------"
echo " Unloading module(s) for your misdn-cards:"
echo "-------------------------------------------"
rmmod_cmd="${RMMOD} ${modulelist[1]}"
echo "${rmmod_cmd}"
eval ${rmmod_cmd}
index=2
prev=1
while [ ! -z ${modulelist[${index}]} ];
do
if [ ${modulelist[${index}]} != ${modulelist[${prev}]} ]; then
rmmod_cmd="${RMMOD} ${modulelist[${index}]}"
echo "${rmmod_cmd}"
eval ${rmmod_cmd}
fi
let "prev = ${index}"
let "index = ${index} + 1"
done
}
function create_misdn_init_conf {
cardcount=1
cardconf=""
IFS=$'\n'
NL="
"
function die {
echo "[!!] ${1}"
exit 1
}
function addcard {
cardline="${cardline}card=${cardcount},${1}${NL}"
let "cardcount = ${cardcount} + 1"
}
function addport {
let "portcount = ${portcount} + ${1}"
}
portcount=0
for line in $(${LSPCI} -n -d 1397:30b1); do
addcard "0x1"
addport 1
done
for line in $(${LSPCI} -n -d 1397:08b4); do
addcard "0x4"
addport 4
done
for line in $(${LSPCI} -n -d 1397:16b8); do
addcard "0x8"
addport 8
done
for line in $(${LSPCI} -n | grep "1397:\(2bd\(0\|6\|7\|8\|9\|a\|b\|c\)\|b100\)\|1043:0675\|0871:ffa\(1\|2\)\|1051:0100\|15b0:2bd0\|114f:007\(0\|1\|2\|3\)\|13d1:2bd1\|182d:3069"); do
addcard "hfcpci"
addport 1
done
for line in $(${LSPCI} -n -d 1244:0xa00); do
addcard "avmfritz"
addport 1
done
for line in $(${LSPCI} -n -d 1050:6692); do
addcard "w6692pci"
addport 1
done
if [ "${1}" == "scan" ]; then
echo "[OK] found the following devices:"
echo "${cardline}[ii] run \"/etc/init.d/misdn-init config\" to store this information to ${misdn_init_conf}"
else
index=1
portline="te_ptmp="
while [ ${index} -le ${portcount} ]; do
portline="${portline}${index},"
let "index = ${index} + 1"
done
portline="$(echo ${portline} | sed -e 's/^\(.*\),$/\1/')"
misdn_cfg_pt1="#
# Configuration file for your misdn hardware
#
# Usage: /etc/init.d/misdn-init start|stop|restart|config|scan|help
#
#
# Card Settings
#
# Syntax: card=<number>,<type>[,<option>...]
#
# <number> count your cards beginning with 1
# <type> either 0x1,0x4 or 0x8 for your hfcmulti hardware,
# or the name of your card driver module.
# <option> ulaw - uLaw (instead of aLaw)
# dtmf - enable DTMF detection on all B-channels
# pcm_slave - set PCM bus into slave mode
#"
misdn_cfg_pt2="#
# Port settings
#
# Syntax: <port_type>=<port_number>[,<port_number>...]
#
# <port_type> te_ptp - TE-Mode, PTP
# te_ptmp - TE-Mode, PTMP
# te_capi_ptp - TE-Mode (capi), PTP
# te_capi_ptmp - TE-Mode (capi), PTMP
# nt_ptp - NT-Mode, PTP
# nt_ptmp - NT-Mode, PTMP
# <port_number> port that should be considered
#"
misdn_cfg_pt3="#
# Port Options
#
# Syntax: option=<port_number>,<option>[,<option>...]
#
# <option> master_clock - use master clock for this S/T interface
# (only once per chip, only for HFC 8/4)
# optical - optical (only HFC-E1)
# los - report LOS (only HFC-E1)
# ais - report AIS (only HFC-E1)
# slip - report SLIP (only HFC-E1)
# nocrc4 - turn off crc4 mode use double frame instead
# (only HFC-E1)
#
#option=1,master_clock
#option=2,ais,nocrc4
#option=3,optical,los,ais,slip
#
# General Options for your hfcmulti hardware
#
# poll=<number>
#
# Only one poll value must be given for all cards.
# Give the number of samples for each fifo process.
# By default 128 is used. Decrease to reduce delay, increase to
# reduce cpu load. If unsure, don't mess with it!!!
# Valid is 32, 64, 128, 256.
#
# pcm=<number>
#
# Give the id of the PCM bus. All PCM busses with the same ID
# are expected to be connected and have equal slots.
# Only one chip of the PCM bus must be master, the others slave.
# -1 means no support of PCM bus.
#
# debug=<number>
#
# Enable debugging (see hfc_multi.h for debug options).
#
poll=128
#pcm=1
debug=0"
if [ -f ${misdn_init_conf} ]; then
cp "${misdn_init_conf}" "${misdn_init_conf}.save" || die "could not backup your existing ${misdn_init_conf}!"
echo "[OK] ${misdn_init_conf} already present. backing it up to ${misdn_init_conf}.save"
fi
echo "${misdn_cfg_pt1}${NL}${cardline}${NL}${misdn_cfg_pt2}${NL}${portline}${NL}${NL}${misdn_cfg_pt3}" > ${misdn_init_conf} || die "could not write to /etc/misdn-init.conf!"
#echo "${misdn_cfg_pt1}${NL}${cardline}${NL}${misdn_cfg_pt2}${NL}${portline}${NL}${NL}${misdn_cfg_pt3}" > testconf || die "could not write to /etc/misdn-init.conf!"
echo "[OK] ${misdn_init_conf} created. It's now safe to run \"/etc/init.d/misdn-init start\""
if [ ${portcount} -gt 1 ]; then
echo "[ii] make your ports (1-${portcount}) available in asterisk by editing \"/etc/asterisk/misdn.conf\""
elif [ ${portcount} -eq 1 ]; then
echo "[ii] make your port (1) available in asterisk by editing \"/etc/asterisk/misdn.conf\""
fi
fi
}
function check_cfg_file {
if [ ! -f ${misdn_init_conf} ]; then
echo "[!!] failed to load: ${misdn_init_conf}"
echo "run \"/etc/init.d/misdn-init config\" to scan your devices and generate a basic config file."
exit 1
fi
}
# MAIN #############
case "$1" in
start|--start)
remove_preloaded_modules
check_cfg_file
$MODPROBE mISDN_core debug=0
$MODPROBE mISDN_l1 debug=0
$MODPROBE mISDN_l2 debug=0
$MODPROBE l3udss1 debug=0
$MODPROBE mISDN_dsp debug=0x0 options=0x0
$MODPROBE mISDN_capi
load_card_modules
sleep 1
if [ ! -e /dev/mISDN ]; then
$MKNOD /dev/mISDN c 46 0
echo "[i] creating device node: /dev/mISDN"
fi
;;
stop|--stop)
check_cfg_file
unload_card_modules
for mod in $(lsmod | sed -ne '/Module/!{s/\([^ ]*\).*/\1/;p}');
do
case "${mod}" in
mISDN_capi | mISDN_dsp | l3udss1 | mISDN_l2 | mISDN_l1 | mISDN_isac | mISDN_core)
eval "${RMMOD} ${mod}"
;;
esac
done
;;
restart|--restart)
check_cfg_file
sh $0 stop
sleep 2 # some phones will release tei when layer 1 is down
sh $0 start
;;
config|--config)
create_misdn_init_conf
;;
scan|--scan)
create_misdn_init_conf scan
;;
help|--help)
echo "Usage: $0 {start|stop|restart|config|scan|help}"
exit 0
;;
*)
echo "Usage: $0 {start|stop|restart|config|scan|help}"
exit 2
;;
esac