- move branch mqueue to HEAD
This commit is contained in:
parent
56f18961c0
commit
b267c6033f
|
@ -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 {} \;
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
|
@ -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
|
|
@ -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 */
|
|
@ -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 ------------------------------------------------------------*/
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************/
|
|
@ -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 ------------------------------------------------------------*/
|
|
@ -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
|
|
@ -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 */
|
||||
|
|
@ -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
|
|
@ -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 */
|
||||
|
|
@ -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
|
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
|
@ -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, \
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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_ */
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue