very simple audio pipeline implementation, dsp modules can register themselves

as pipeline elements and the pipeline is configured with a configuration string
via a new PH_CONTROL parameter: PIPELINE_CFG.
The config-string is as follows: mec2(training=1)|kb1ec
With this example, the audio data would flow through mec2 (instantiated with the
parameter-string "training=1") and then through kb1ec (instantiated without
parameters).
The three echo cancellers are now build as separate modules, named mISDN_dsp_*.
This commit is contained in:
Nadi Sarrar 2007-03-23 13:48:57 +00:00
parent 8afd39d035
commit 9fb78bc0c9
16 changed files with 916 additions and 442 deletions

View File

@ -84,7 +84,7 @@ declare -a SCAN_port_opts
function parse_config
{
local CONFIG=$(${XSLTPROC} ${MISDN_CONF_XSL} ${MISDN_CONF})
local t p l line i tmpcmd curr tmpstr
local t p l line i tmpcmd curr tmpstr extra_modules
local IFS=$'\n'
START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install capi"
@ -113,6 +113,9 @@ function parse_config
MODULE:hfcmulti*)
HFCMULTI_options=${line:16}
;;
MODULE:mISDN_dsp_*)
extra_modules[${#extra_modules[@]}]=${line:7}
;;
MODULE:mISDN_dsp*)
MISDNDSP_options=${line:17}
;;
@ -191,6 +194,12 @@ function parse_config
fi
START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install mISDN_dsp ${MISDNDSP_options}"
i=1
while [ ! -z "${extra_modules[${i}]}" ]; do
START_COMMANDS[${#START_COMMANDS[@]}]="${MODPROBE} --ignore-install ${extra_modules[${i}]}"
let "i = ${i} + 1"
done
}
function run_start_commands
@ -211,7 +220,7 @@ function run_stop_commands
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 | hfcmulti | avmfritz)
mISDN_capi | mISDN_dsp* | l3udss1 | mISDN_l2 | mISDN_l1 | mISDN_isac | hfcmulti | avmfritz)
STOP_COMMANDS[0]="${STOP_COMMANDS[0]:-"${MODPROBE} -r --ignore-remove"} ${mod}"
;;
mISDN_core)
@ -332,6 +341,9 @@ Options: debug=<number>, options=<number>, poll=<number>,
<mISDNconf>
${TAB}<module poll=\"128\" debug=\"0\" timer=\"no\">hfcmulti</module>
${TAB}<module debug=\"0\" options=\"0\">mISDN_dsp</module>
${TAB}<module>mISDN_dsp_mec2</module>
${TAB}<module>mISDN_dsp_mg2ec</module>
${TAB}<module>mISDN_dsp_kb1ec</module>
${TAB}<devnode user=\"root\" group=\"root\" mode=\"644\">mISDN</devnode>"
local FOOTER="</mISDNconf>"
local i=0 j=0 MAIN=""

View File

@ -29,6 +29,12 @@
<xsl:call-template name="MISDNDSPmodule" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('MODULE:',.)" />
<xsl:text>
</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>

View File

@ -63,7 +63,7 @@ obj-$(CONFIG_MISDN_DRV) += xhfc.o
endif
ifdef CONFIG_MISDN_DSP
obj-$(CONFIG_MISDN_DRV) += mISDN_dsp.o
obj-$(CONFIG_MISDN_DRV) += mISDN_dsp.o mISDN_dsp_mec2.o mISDN_dsp_kb1ec.o mISDN_dsp_mg2ec.o
endif
ifdef CONFIG_MISDN_LOOP
@ -105,7 +105,10 @@ mISDN_capi-objs := capi.o contr.o listen.o appl.o plci.o app_plci.o ncci.o asn1.
asn1_basic_service.o asn1_address.o asn1_enc.o capi_enc.o \
supp_serv.o
mISDN_dtmf-objs := dtmf.o
mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_cancel.o
mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o
mISDN_dsp_mec2-objs := dsp_mec2.o
mISDN_dsp_kb1ec-objs := dsp_kb1ec.o
mISDN_dsp_mg2ec-objs := dsp_mg2ec.o
mISDN_loop-objs := loop.o
mISDN_x25dte-objs := x25_dte.o x25_l3.o
I4LmISDN-objs := i4l_mISDN.o

View File

@ -39,26 +39,6 @@
#include "dsp_ecdis.h"
/*
* You are now able to choose between the Mark2 and the
* kb1 Echo cancellor. Just comment the one and comment
* out the other.
*/
//#define AGGRESSIVE_SUPPRESSOR
//#include "dsp_mec2.h"
//#include "dsp_kb1ec.h"
#include "dsp_mg2ec.h"
/*
* uncomment this one to cancel echo more aggressive
*/
//#define AGGRESSIVE_SUPPRESSOR
extern int dsp_options;
extern int dsp_debug;
extern int dsp_poll;
@ -148,11 +128,13 @@ typedef struct _dtmf_t {
} dtmf_t;
/****************
* cancel stuff *
****************/
/******************
* pipeline stuff *
******************/
typedef struct _dsp_pipeline {
rwlock_t lock;
struct list_head list;
} dsp_pipeline_t;
/***************
* tones stuff *
@ -238,26 +220,7 @@ typedef struct _dsp {
u8 bf_data_out[9];
int bf_sync;
/* echo cancellation stuff */
int queue_cancel[3];
int cancel_enable;
int cancel_hardware; /*we are using hw echo canc*/
struct echo_can_state * ec; /**< == NULL: echo cancellation disabled;
!= NULL: echo cancellation enabled */
echo_can_disable_detector_state_t* ecdis_rd;
echo_can_disable_detector_state_t* ecdis_wr;
uint16_t echotimer;
uint16_t echostate;
uint16_t echolastupdate;
char txbuf[ECHOCAN_BUFLEN];
int txbuflen;
char rxbuf[ECHOCAN_BUFLEN];
int rxbuflen;
dsp_pipeline_t pipeline;
} dsp_t;
/* functions */
@ -290,8 +253,11 @@ extern void dsp_bf_decrypt(dsp_t *dsp, u8 *data, int len);
extern int dsp_bf_init(dsp_t *dsp, const u8 *key, unsigned int keylen);
extern void dsp_bf_cleanup(dsp_t *dsp);
extern void dsp_cancel_tx(dsp_t *dsp, u8 *data, int len);
extern void dsp_cancel_rx(dsp_t *dsp, u8 *data, int len);
extern int dsp_cancel_init(dsp_t *dsp, int taps, int training, int delay);
extern int dsp_pipeline_module_init (void);
extern void dsp_pipeline_module_exit (void);
extern int dsp_pipeline_init (dsp_pipeline_t *pipeline);
extern void dsp_pipeline_destroy (dsp_pipeline_t *pipeline);
extern int dsp_pipeline_build (dsp_pipeline_t *pipeline, const char *cfg);
extern void dsp_pipeline_process_tx (dsp_pipeline_t *pipeline, u8 *data, int len);
extern void dsp_pipeline_process_rx (dsp_pipeline_t *pipeline, u8 *data, int len);

View File

@ -153,9 +153,11 @@ s32 dsp_audio_alaw_to_s32[256] =
};
s32 *dsp_audio_law_to_s32;
EXPORT_SYMBOL(dsp_audio_law_to_s32);
/* signed 16-bit -> law */
u8 dsp_audio_s16_to_law[65536];
EXPORT_SYMBOL(dsp_audio_s16_to_law);
/* table is used to generate s16_to_alaw */
static short dsp_audio_alaw_relations[512] =

View File

@ -1,350 +0,0 @@
/*
*
* 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"
#ifdef ARCH_I386
#include <asm/i387.h>
#else
#define kernel_fpu_begin()
#define kernel_fpu_end()
#endif
/*
* how this works:
*
*
*
*/
/*
* send HW message to hfc card
*/
static void
dsp_cancel_hw_message(dsp_t *dsp, u32 message, u32 param)
{
struct sk_buff *nskb;
nskb = create_link_skb(PH_CONTROL | REQUEST, message, sizeof(param), &param, 0);
if (!nskb) {
printk(KERN_ERR "%s: No mem for skb.\n", __FUNCTION__);
return;
}
/* unlocking is not required, because we don't expect a response */
if (mISDN_queue_down(&dsp->inst, 0, nskb))
dev_kfree_skb(nskb);
}
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;
if (dsp->feature_state != FEAT_STATE_RECEIVED) {
dsp->queue_cancel[0]=deftaps;
dsp->queue_cancel[1]=training;
dsp->queue_cancel[2]=delay;
return 0;
}
//printk("DSP_CANCEL_INIT called\n");
if (delay < 0)
{
//printk(KERN_NOTICE "Disabling EC\n");
dsp->cancel_enable = 0;
dsp->txbuflen=0;
if (dsp->features.hfc_echocanhw) {
//printk(KERN_NOTICE "Disabling Hardware EC\n");
dsp_cancel_hw_message(dsp, HW_ECHOCAN_OFF, deftaps);
} else {
bchdev_echocancel_deactivate(dsp);
}
return(0);
}
if (dsp->features.hfc_echocanhw) {
//printk(KERN_NOTICE "Using Hardware EC taps [%d]\n",deftaps);
dsp_cancel_hw_message(dsp, HW_ECHOCAN_ON, deftaps);
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)
{
#if 0
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);
#endif
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_ATOMIC);
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_ATOMIC);
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* ss, uint8_t *rxchunk, uint8_t *txchunk, uint16_t size)
{
int16_t rxlin, txlin;
uint16_t x;
/* Perform echo cancellation on a chunk if requested */
if (ss->ec) {
if (ss->echostate & __ECHO_STATE_MUTE) {
/* Special stuff for training the echo can */
for (x=0;x<size;x++) {
rxlin = dsp_audio_law_to_s32[rxchunk[x]];
txlin = dsp_audio_law_to_s32[txchunk[x]];
if (ss->echostate == ECHO_STATE_PRETRAINING) {
if (--ss->echotimer <= 0) {
ss->echotimer = 0;
ss->echostate = ECHO_STATE_STARTTRAINING;
}
}
if ((ss->echostate == ECHO_STATE_AWAITINGECHO) && (txlin > 8000)) {
ss->echolastupdate = 0;
ss->echostate = ECHO_STATE_TRAINING;
}
if (ss->echostate == ECHO_STATE_TRAINING) {
if (echo_can_traintap(ss->ec, ss->echolastupdate++, rxlin)) {
#if 0
printk("Finished training (%d taps trained)!\n", ss->echolastupdate);
#endif
ss->echostate = ECHO_STATE_ACTIVE;
}
}
rxlin = 0;
rxchunk[x] = dsp_audio_s16_to_law[(int)rxlin];
}
} else {
for (x=0;x<size;x++) {
rxlin = dsp_audio_law_to_s32[rxchunk[x]&0xff];
txlin = dsp_audio_law_to_s32[txchunk[x]&0xff];
rxlin = echo_can_update(ss->ec, txlin, rxlin);
rxchunk[x] = dsp_audio_s16_to_law[rxlin &0xffff];
}
}
}
}
/******************************************************/

View File

@ -0,0 +1,180 @@
/*
* dsp_cancel.h: worker functions for various echo cancellers
*
* Copyright (C) 2007, Nadi Sarrar
*
* Nadi Sarrar <nadi@beronet.com>
*
* Derived from dsp_cancel.c written by Chrisian Richter.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The full GNU General Public License is included in this distribution in the
* file called LICENSE.
*
*/
#include "layer1.h"
#include "helper.h"
#include "debug.h"
#include "dsp.h"
#ifdef ARCH_I386
#include <asm/i387.h>
#else
#define kernel_fpu_begin()
#define kernel_fpu_end()
#endif
#define EC_TIMER 2000
#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
typedef struct _ec_prv_t {
struct echo_can_state * ec;
uint16_t echotimer;
uint16_t echostate;
uint16_t echolastupdate;
char txbuf[ECHOCAN_BUFLEN];
int txbuflen;
char rxbuf[ECHOCAN_BUFLEN];
int rxbuflen;
int underrun;
int overflow;
} ec_prv_t;
static inline void *dsp_cancel_new (int deftaps, int training)
{
ec_prv_t *p;
p = kmalloc(sizeof(ec_prv_t), GFP_KERNEL);
if (!p)
goto _err1;
p->ec = echo_can_create(deftaps > 0 ? deftaps : 128, 0);
if (!p->ec)
goto _err2;
p->echotimer = training ? training : 0;
p->echostate = training ? ECHO_STATE_PRETRAINING : ECHO_STATE_IDLE;
p->echolastupdate = 0;
p->txbuflen = 0;
p->rxbuflen = 0;
p->underrun = 0;
p->overflow = 0;
return p;
_err2:
kfree(p);
_err1:
return NULL;
}
static inline void dsp_cancel_free (ec_prv_t *p)
{
if (!p)
return;
echo_can_free(p->ec);
kfree(p);
}
/** Processes one TX- and one RX-packet with echocancellation */
static inline void echocancel_chunk(ec_prv_t* p, uint8_t *rxchunk, uint8_t *txchunk, uint16_t size)
{
int16_t rxlin, txlin;
uint16_t x;
if (p->echostate & __ECHO_STATE_MUTE) {
/* Special stuff for training the echo can */
for (x=0;x<size;x++) {
rxlin = dsp_audio_law_to_s32[rxchunk[x]];
txlin = dsp_audio_law_to_s32[txchunk[x]];
if (p->echostate == ECHO_STATE_PRETRAINING) {
if (--p->echotimer <= 0) {
p->echotimer = 0;
p->echostate = ECHO_STATE_STARTTRAINING;
}
}
if ((p->echostate == ECHO_STATE_AWAITINGECHO) && (txlin > 8000)) {
p->echolastupdate = 0;
p->echostate = ECHO_STATE_TRAINING;
}
if (p->echostate == ECHO_STATE_TRAINING) {
if (echo_can_traintap(p->ec, p->echolastupdate++, rxlin)) {
p->echostate = ECHO_STATE_ACTIVE;
}
}
rxlin = 0;
rxchunk[x] = dsp_audio_s16_to_law[(int)rxlin];
}
} else {
for (x=0;x<size;x++) {
rxlin = dsp_audio_law_to_s32[rxchunk[x]&0xff];
txlin = dsp_audio_law_to_s32[txchunk[x]&0xff];
rxlin = echo_can_update(p->ec, txlin, rxlin);
rxchunk[x] = dsp_audio_s16_to_law[rxlin &0xffff];
}
}
}
static inline void dsp_cancel_tx (ec_prv_t *p, u8 *data, int len)
{
if (!p || !data)
return;
if (p->txbuflen + len < ECHOCAN_BUFLEN) {
memcpy(&p->txbuf[p->txbuflen], data, len);
p->txbuflen += len;
} else {
if (p->overflow >= 4000) {
printk("ECHOCAN: TXBUF Overflow:%d txbuflen:%d txcancellen:%d\n", p->overflow, p->txbuflen, len);
p->overflow = 0;
}
p->overflow += len;
p->txbuflen = 0;
}
}
static inline void dsp_cancel_rx (ec_prv_t *p, u8 *data, int len)
{
if (!p || !data)
return;
if (len <= p->txbuflen) {
char tmp[ECHOCAN_BUFLEN];
int delta = p->txbuflen - len;
memcpy(tmp, &p->txbuf[len], delta);
kernel_fpu_begin();
echocancel_chunk(p, data, p->txbuf, len);
kernel_fpu_end();
memcpy(p->txbuf, tmp, delta);
p->txbuflen = delta;
} else {
if (p->underrun >= 4000) {
printk("ECHOCAN: TXBUF Underrun:%d txbuflen:%d rxcancellen:%d\n", p->underrun, p->txbuflen,len);
p->underrun = 0;
}
p->underrun += len;
}
}

View File

@ -526,12 +526,14 @@ dsp_cmx_hardware(conference_t *conf, dsp_t *dsp)
printk(KERN_DEBUG "%s dsp %s cannot form a conf, because encryption is enabled\n", __FUNCTION__, member->dsp->inst.name);
goto conf_software;
}
#if 0
/* check if echo cancellation is enabled */
if (member->dsp->cancel_enable) {
if (dsp_debug & DEBUG_DSP_CMX)
printk(KERN_DEBUG "%s dsp %s cannot form a conf, because echo cancellation is enabled\n", __FUNCTION__, member->dsp->inst.name);
goto conf_software;
}
#endif
/* check if member is on a card with PCM support */
if (member->dsp->features.pcm_id < 0) {
if (dsp_debug & DEBUG_DSP_CMX)
@ -1220,9 +1222,11 @@ send_packet:
if (dsp->tx_volume)
dsp_change_volume(nskb, dsp->tx_volume);
#if 0
/* cancel echo */
if (dsp->cancel_enable)
dsp_cancel_tx(dsp, nskb->data, nskb->len);
#endif
/* crypt */
if (dsp->bf_enable)

View File

@ -32,7 +32,7 @@
* - (4) echo generation for delay test
* - (5) volume control
* - (6) disable receive data
* - (7) echo cancelation
* - (7) pipelined audio processing
* - (8) encryption/decryption
*
* Look:
@ -71,7 +71,7 @@
* +----+-------------+----+
* |(7) |
* | |
* | Echo Cancellation |
* | Pipelined Processing |
* | |
* | |
* +----+-------------+----+
@ -364,26 +364,12 @@ dsp_control_req(dsp_t *dsp, mISDN_head_t *hh, struct sk_buff *skb)
if (dsp_debug & DEBUG_DSP_CMX)
dsp_cmx_debug(dsp);
break;
case ECHOCAN_ON: /* turn echo calcellation on */
if (len<4) {
case PIPELINE_CFG:
if (len > 0 && ((char *)data)[len - 1]) {
printk(KERN_DEBUG "%s: pipeline config string is not NULL terminated!\n", __FUNCTION__);
ret = -EINVAL;
} else {
int ec_arr[2];
memcpy(&ec_arr,data,sizeof(ec_arr));
if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: turn echo cancelation on (delay=%d attenuation-shift=%d\n",
__FUNCTION__, ec_arr[0], ec_arr[1]);
ret = dsp_cancel_init(dsp, ec_arr[0], ec_arr[1] ,1);
dsp_cmx_hardware(dsp->conf, dsp);
}
break;
case ECHOCAN_OFF: /* turn echo calcellation off */
if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: turn echo cancelation off\n", __FUNCTION__);
ret = dsp_cancel_init(dsp, 0,0,-1);
dsp_cmx_hardware(dsp->conf, dsp);
} else
ret = dsp_pipeline_build(&dsp->pipeline, len > 0 ? (char *)data : NULL);
break;
case BF_ENABLE_KEY: /* turn blowfish on */
if (len<4 || len>56) {
@ -461,9 +447,8 @@ dsp_from_up(mISDNinstance_t *inst, struct sk_buff *skb)
if (dsp->tx_volume)
dsp_change_volume(skb, dsp->tx_volume);
/* cancel echo */
if (dsp->cancel_enable)
dsp_cancel_tx(dsp, skb->data, skb->len);
/* pipeline */
dsp_pipeline_process_tx(&dsp->pipeline, skb->data, skb->len);
/* crypt */
if (dsp->bf_enable)
dsp_bf_encrypt(dsp, skb->data, skb->len);
@ -564,9 +549,8 @@ dsp_from_down(mISDNinstance_t *inst, struct sk_buff *skb)
/* decrypt if enabled */
if (dsp->bf_enable)
dsp_bf_decrypt(dsp, skb->data, skb->len);
/* if echo cancellation is enabled */
if (dsp->cancel_enable)
dsp_cancel_rx(dsp, skb->data, skb->len);
/* pipeline */
dsp_pipeline_process_rx(&dsp->pipeline, skb->data, skb->len);
/* check if dtmf soft decoding is turned on */
if (dsp->dtmf.software) {
digits = dsp_dtmf_goertzel_decode(dsp, skb->data, skb->len, (dsp_options&DSP_OPT_ULAW)?1:0);
@ -738,6 +722,8 @@ release_dsp(dsp_t *dsp)
}
}
dsp_pipeline_destroy(&dsp->pipeline);
if (dsp_debug & DEBUG_DSP_MGR)
printk(KERN_DEBUG "%s: remove & destroy object %s\n", __FUNCTION__, dsp->inst.name);
list_del(&dsp->list);
@ -802,15 +788,6 @@ dsp_feat(void *arg)
if (dsp_debug & DEBUG_DSP_CMX)
dsp_cmx_debug(dsp);
}
if (dsp->queue_cancel[2]) {
dsp_cancel_init(dsp,
dsp->queue_cancel[0],
dsp->queue_cancel[1],
dsp->queue_cancel[2]
);
}
break;
}
@ -838,6 +815,9 @@ new_dsp(mISDNstack_t *st, mISDN_pid_t *pid)
}
memset(ndsp, 0, sizeof(dsp_t));
memcpy(&ndsp->inst.pid, pid, sizeof(mISDN_pid_t));
dsp_pipeline_init(&ndsp->pipeline);
mISDN_init_instance(&ndsp->inst, &dsp_obj, ndsp, dsp_function);
if (!mISDN_SetHandledPID(&dsp_obj, &ndsp->inst.pid)) {
int_error();
@ -968,7 +948,7 @@ static int dsp_init(void)
dsp_debug = debug;
/* display revision */
printk(KERN_INFO "mISDN_dsp: Audio DSP Rev. %s (debug=0x%x) EchoCancellor %s dtmfthreshold(%d)\n", mISDN_getrev(dsp_revision), debug, EC_TYPE, dtmfthreshold);
printk(KERN_INFO "mISDN_dsp: Audio DSP Rev. %s (debug=0x%x) dtmfthreshold(%d)\n", mISDN_getrev(dsp_revision), debug, dtmfthreshold);
/* set packet size */
if (poll == 0) {
@ -1017,6 +997,12 @@ static int dsp_init(void)
dsp_audio_generate_ulaw_samples();
dsp_audio_generate_volume_changes();
/* initialize pipeline */
if ((err = dsp_pipeline_module_init())) {
printk(KERN_ERR "mISDN_dsp: Can't initialize pipeline, %s, error(%d)\n", DSPName, err);
return(err);
}
/* register object */
if ((err = mISDN_register(&dsp_obj))) {
printk(KERN_ERR "mISDN_dsp: Can't register %s error(%d)\n", DSPName, err);
@ -1065,6 +1051,8 @@ static void dsp_cleanup(void)
if (!list_empty(&Conf_list)) {
printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not all memory freed.\n");
}
dsp_pipeline_module_exit();
}
#ifdef MODULE

View File

@ -0,0 +1,120 @@
/*
* dsp_kb1ec.c: mISDN dsp pipeline element for the kb1ec echo canceller
*
* Copyright (C) 2007, Nadi Sarrar
*
* Nadi Sarrar <nadi@beronet.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 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The full GNU General Public License is included in this distribution in the
* file called LICENSE.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mISDNdsp.h>
#include "dsp_kb1ec.h"
#include "dsp_cancel.h"
static void* new (const char *arg)
{
int deftaps = 128,
training = 0,
len;
if (!arg)
goto _out;
len = strlen(arg);
if (!len)
goto _out;
{
char _dup[len + 1];
char *dup, *tok, *name, *val;
int tmp;
strcpy(_dup, arg);
dup = _dup;
while ((tok = strsep(&dup, ","))) {
if (!strlen(tok))
continue;
name = strsep(&tok, "=");
val = tok;
if (!val)
continue;
if (!strcmp(name, "deftaps")) {
if (sscanf(val, "%d", &tmp) == 1)
deftaps = tmp;
} else if (!strcmp(name, "training")) {
if (sscanf(val, "%d", &tmp) == 1)
training = tmp;
}
}
}
_out:
printk(KERN_DEBUG "%s: creating %s with deftaps=%d and training=%d\n", __FUNCTION__, EC_TYPE, deftaps, training);
return dsp_cancel_new(deftaps, training);
}
static void free (void *p)
{
dsp_cancel_free(p);
}
static void process_tx (void *p, u8 *data, int len)
{
dsp_cancel_tx(p, data, len);
}
static void process_rx (void *p, u8 *data, int len)
{
dsp_cancel_rx(p, data, len);
}
static mISDN_dsp_element_t dsp_kb1ec = {
.new = new,
.free = free,
.process_tx = process_tx,
.process_rx = process_rx,
.name = "kb1ec",
};
#ifdef MODULE
static int __init dsp_kb1ec_init (void)
{
mISDN_dsp_element_register(&dsp_kb1ec);
return 0;
}
static void __exit dsp_kb1ec_exit (void)
{
mISDN_dsp_element_unregister(&dsp_kb1ec);
}
module_init(dsp_kb1ec_init);
module_exit(dsp_kb1ec_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nadi Sarrar <nadi@beronet.com>");
#endif

View File

@ -0,0 +1,121 @@
/*
* dsp_mec2.c: mISDN dsp pipeline element for the mec2 echo canceller
*
* Copyright (C) 2007, Nadi Sarrar
*
* Nadi Sarrar <nadi@beronet.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 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The full GNU General Public License is included in this distribution in the
* file called LICENSE.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/mISDNdsp.h>
#include "dsp_mec2.h"
#include "dsp_cancel.h"
static void* new (const char *arg)
{
int deftaps = 128,
training = 0,
len;
if (!arg)
goto _out;
len = strlen(arg);
if (!len)
goto _out;
{
char _dup[len + 1];
char *dup, *tok, *name, *val;
int tmp;
strcpy(_dup, arg);
dup = _dup;
while ((tok = strsep(&dup, ","))) {
if (!strlen(tok))
continue;
name = strsep(&tok, "=");
val = tok;
if (!val)
continue;
if (!strcmp(name, "deftaps")) {
if (sscanf(val, "%d", &tmp) == 1)
deftaps = tmp;
} else if (!strcmp(name, "training")) {
if (sscanf(val, "%d", &tmp) == 1)
training = tmp;
}
}
}
_out:
printk(KERN_DEBUG "%s: creating %s with deftaps=%d and training=%d\n", __FUNCTION__, EC_TYPE, deftaps, training);
return dsp_cancel_new(deftaps, training);
}
static void free (void *p)
{
dsp_cancel_free(p);
}
static void process_tx (void *p, u8 *data, int len)
{
dsp_cancel_tx(p, data, len);
}
static void process_rx (void *p, u8 *data, int len)
{
dsp_cancel_rx(p, data, len);
}
static mISDN_dsp_element_t dsp_mec2 = {
.new = new,
.free = free,
.process_tx = process_tx,
.process_rx = process_rx,
.name = "mec2",
};
#ifdef MODULE
static int __init dsp_mec2_init (void)
{
mISDN_dsp_element_register(&dsp_mec2);
return 0;
}
static void __exit dsp_mec2_exit (void)
{
mISDN_dsp_element_unregister(&dsp_mec2);
}
module_init(dsp_mec2_init);
module_exit(dsp_mec2_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nadi Sarrar <nadi@beronet.com>");
#endif

View File

@ -0,0 +1,120 @@
/*
* dsp_mg2ec.c: mISDN dsp pipeline element for the mg2ec echo canceller
*
* Copyright (C) 2007, Nadi Sarrar
*
* Nadi Sarrar <nadi@beronet.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 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The full GNU General Public License is included in this distribution in the
* file called LICENSE.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mISDNdsp.h>
#include "dsp_mg2ec.h"
#include "dsp_cancel.h"
static void* new (const char *arg)
{
int deftaps = 128,
training = 0,
len;
if (!arg)
goto _out;
len = strlen(arg);
if (!len)
goto _out;
{
char _dup[len + 1];
char *dup, *tok, *name, *val;
int tmp;
strcpy(_dup, arg);
dup = _dup;
while ((tok = strsep(&dup, ","))) {
if (!strlen(tok))
continue;
name = strsep(&tok, "=");
val = tok;
if (!val)
continue;
if (!strcmp(name, "deftaps")) {
if (sscanf(val, "%d", &tmp) == 1)
deftaps = tmp;
} else if (!strcmp(name, "training")) {
if (sscanf(val, "%d", &tmp) == 1)
training = tmp;
}
}
}
_out:
printk(KERN_DEBUG "%s: creating %s with deftaps=%d and training=%d\n", __FUNCTION__, EC_TYPE, deftaps, training);
return dsp_cancel_new(deftaps, training);
}
static void free (void *p)
{
dsp_cancel_free(p);
}
static void process_tx (void *p, u8 *data, int len)
{
dsp_cancel_tx(p, data, len);
}
static void process_rx (void *p, u8 *data, int len)
{
dsp_cancel_rx(p, data, len);
}
static mISDN_dsp_element_t dsp_mg2ec = {
.new = new,
.free = free,
.process_tx = process_tx,
.process_rx = process_rx,
.name = "mg2ec",
};
#ifdef MODULE
static int __init dsp_mg2ec_init (void)
{
mISDN_dsp_element_register(&dsp_mg2ec);
return 0;
}
static void __exit dsp_mg2ec_exit (void)
{
mISDN_dsp_element_unregister(&dsp_mg2ec);
}
module_init(dsp_mg2ec_init);
module_exit(dsp_mg2ec_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nadi Sarrar <nadi@beronet.com>");
#endif

View File

@ -0,0 +1,271 @@
/*
* dsp_pipeline.c: pipelined audio processing
*
* Copyright (C) 2007, Nadi Sarrar
*
* Nadi Sarrar <nadi@beronet.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 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The full GNU General Public License is included in this distribution in the
* file called LICENSE.
*
*/
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/mISDNif.h>
#include <linux/mISDNdsp.h>
#include "layer1.h"
#include "dsp.h"
/* Structures */
typedef struct _dsp_pipeline_entry {
mISDN_dsp_element_t *elem;
void *p;
struct list_head list;
} dsp_pipeline_entry_t;
typedef struct _dsp_element_entry {
mISDN_dsp_element_t *elem;
struct list_head list;
} dsp_element_entry_t;
/* Static Variables */
rwlock_t dsp_elements_lock;
static LIST_HEAD(dsp_elements);
/* Code */
int mISDN_dsp_element_register (mISDN_dsp_element_t *elem)
{
dsp_element_entry_t *entry;
u_long flags;
if (!elem)
return -EINVAL;
entry = kmalloc(sizeof(dsp_element_entry_t), GFP_KERNEL);
if (!entry)
return -ENOMEM;
entry->elem = elem;
write_lock_irqsave(&dsp_elements_lock, flags);
list_add_tail(&entry->list, &dsp_elements);
write_unlock_irqrestore(&dsp_elements_lock, flags);
printk(KERN_DEBUG "%s: %s registered\n", __FUNCTION__, elem->name);
return 0;
}
void mISDN_dsp_element_unregister (mISDN_dsp_element_t *elem)
{
dsp_element_entry_t *entry, *n;
u_long flags;
if (!elem)
return;
write_lock_irqsave(&dsp_elements_lock, flags);
list_for_each_entry_safe(entry, n, &dsp_elements, list)
if (entry->elem == elem) {
list_del(&entry->list);
write_unlock_irqrestore(&dsp_elements_lock, flags);
kfree(entry);
printk(KERN_DEBUG "%s: %s unregistered\n", __FUNCTION__, elem->name);
return;
}
write_unlock_irqrestore(&dsp_elements_lock, flags);
}
int dsp_pipeline_module_init (void)
{
rwlock_init(&dsp_elements_lock);
printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __FUNCTION__);
return 0;
}
void dsp_pipeline_module_exit (void)
{
dsp_element_entry_t *entry, *n;
u_long flags;
write_lock_irqsave(&dsp_elements_lock, flags);
list_for_each_entry_safe(entry, n, &dsp_elements, list) {
list_del(&entry->list);
printk(KERN_DEBUG "%s: element was still registered: %s\n", __FUNCTION__, entry->elem->name);
kfree(entry);
}
write_unlock_irqrestore(&dsp_elements_lock, flags);
printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __FUNCTION__);
}
int dsp_pipeline_init (dsp_pipeline_t *pipeline)
{
if (!pipeline)
return -EINVAL;
INIT_LIST_HEAD(&pipeline->list);
rwlock_init(&pipeline->lock);
printk(KERN_DEBUG "%s: dsp pipeline ready\n", __FUNCTION__);
return 0;
}
static inline void _dsp_pipeline_destroy (dsp_pipeline_t *pipeline)
{
dsp_pipeline_entry_t *entry, *n;
list_for_each_entry_safe(entry, n, &pipeline->list, list) {
list_del(&entry->list);
entry->elem->free(entry->p);
kfree(entry);
}
}
void dsp_pipeline_destroy (dsp_pipeline_t *pipeline)
{
u_long flags;
if (!pipeline)
return;
write_lock_irqsave(&pipeline->lock, flags);
_dsp_pipeline_destroy(pipeline);
write_unlock_irqrestore(&pipeline->lock, flags);
printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __FUNCTION__);
}
int dsp_pipeline_build (dsp_pipeline_t *pipeline, const char *cfg)
{
int len, incomplete = 0, found = 0;
char *dup, *tok, *name, *args;
dsp_element_entry_t *entry, *n;
dsp_pipeline_entry_t *pipeline_entry;
mISDN_dsp_element_t *elem;
u_long elements_flags, pipeline_flags;
if (!pipeline)
return -EINVAL;
write_lock_irqsave(&pipeline->lock, pipeline_flags);
if (!list_empty(&pipeline->list))
_dsp_pipeline_destroy(pipeline);
if (!cfg) {
write_unlock_irqrestore(&pipeline->lock, pipeline_flags);
return 0;
}
len = strlen(cfg);
if (!len) {
write_unlock_irqrestore(&pipeline->lock, pipeline_flags);
return 0;
}
char _dup[len + 1];
strcpy(_dup, cfg);
dup = _dup;
while ((tok = strsep(&dup, "|"))) {
if (!strlen(tok))
continue;
name = strsep(&tok, "(");
args = strsep(&tok, ")");
if (args && !*args)
args = 0;
read_lock_irqsave(&dsp_elements_lock, elements_flags);
list_for_each_entry_safe(entry, n, &dsp_elements, list)
if (!strcmp(entry->elem->name, name)) {
elem = entry->elem;
read_unlock_irqrestore(&dsp_elements_lock, elements_flags);
pipeline_entry = kmalloc(sizeof(dsp_pipeline_entry_t), GFP_KERNEL);
if (!pipeline_entry) {
printk(KERN_DEBUG "%s: failed to add entry to pipeline: %s (out of memory)\n", __FUNCTION__, elem->name);
incomplete = 1;
goto _out;
}
pipeline_entry->elem = elem;
pipeline_entry->p = elem->new(args);
list_add_tail(&pipeline_entry->list, &pipeline->list);
printk(KERN_DEBUG "%s: created instance of %s%s%s\n", __FUNCTION__, name, args ? " with args " : "", args ? args : "");
found = 1;
break;
}
if (found)
found = 0;
else {
read_unlock_irqrestore(&dsp_elements_lock, elements_flags);
printk(KERN_DEBUG "%s: element not found, skipping: %s\n", __FUNCTION__, name);
incomplete = 1;
}
}
_out:
write_unlock_irqrestore(&pipeline->lock, pipeline_flags);
printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n", __FUNCTION__, incomplete ? " incomplete" : "", cfg);
return 0;
}
void dsp_pipeline_process_tx (dsp_pipeline_t *pipeline, u8 *data, int len)
{
dsp_pipeline_entry_t *entry;
if (!pipeline)
return;
if (!read_trylock(&pipeline->lock)) {
printk(KERN_DEBUG "%s: bypassing pipeline because it is locked (TX)\n", __FUNCTION__);
return;
}
list_for_each_entry(entry, &pipeline->list, list)
entry->elem->process_tx(entry->p, data, len);
read_unlock(&pipeline->lock);
}
void dsp_pipeline_process_rx (dsp_pipeline_t *pipeline, u8 *data, int len)
{
dsp_pipeline_entry_t *entry;
if (!pipeline)
return;
if (!read_trylock(&pipeline->lock)) {
printk(KERN_DEBUG "%s: bypassing pipeline because it is locked (RX)\n", __FUNCTION__);
return;
}
list_for_each_entry_reverse(entry, &pipeline->list, list)
entry->elem->process_rx(entry->p, data, len);
read_unlock(&pipeline->lock);
}
EXPORT_SYMBOL(mISDN_dsp_element_register);
EXPORT_SYMBOL(mISDN_dsp_element_unregister);

16
include/linux/mISDNdsp.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef __mISDNdsp_H__
#define __mISDNdsp_H__
typedef struct _mISDN_dsp_element {
void* (* new) (const char *arg);
void (* free) (void *p);
void (* process_tx) (void *p, u8 *data, int len);
void (* process_rx) (void *p, u8 *data, int len);
char name[];
} mISDN_dsp_element_t;
extern int mISDN_dsp_element_register (mISDN_dsp_element_t *elem);
extern void mISDN_dsp_element_unregister (mISDN_dsp_element_t *elem);
#endif

View File

@ -190,8 +190,7 @@
#define BF_DISABLE 0x2315
#define BF_ACCEPT 0x2316
#define BF_REJECT 0x2317
#define ECHOCAN_ON 0x2318
#define ECHOCAN_OFF 0x2319
#define PIPELINE_CFG 0x2318
#define HW_POTS_ON 0x1001
#define HW_POTS_OFF 0x1002
#define HW_POTS_SETMICVOL 0x1100

View File

@ -84,6 +84,7 @@ function check_cmd {
}
check_cmd modprobe
check_cmd modinfo
check_cmd rmmod
check_cmd insmod
check_cmd lspci
@ -690,6 +691,19 @@ function check_cfg_file {
fi
}
function dsp_modules {
moddir=$(LC_ALL=C ${MODINFO} mISDN_core | sed -ne 's/filename:[^\/]*\(.*\/\)mISDN_core.ko/\1/p')
for mod in $(ls ${moddir}); do
case "${mod}" in
mISDN_dsp_*)
echo "${1} ${mod%%.ko}"
${1} ${mod%%.ko}
;;
esac
done
}
# MAIN #############
case "$1" in
@ -707,6 +721,7 @@ case "$1" in
echo "${MODPROBE} mISDN_dsp debug=0x0 options=$dsp_options $dsp_poll_option $dtmfthreshold_option"
${MODPROBE} mISDN_dsp debug=0x0 options=$dsp_options $dsp_poll_option $dtmfthreshold_option
dsp_modules ${MODPROBE}
sleep 1
if [ ! -e /dev/mISDN ]; then
@ -725,6 +740,7 @@ case "$1" in
check_asterisk
dsp_modules "${MODPROBE} -r"
for mod in $(lsmod | sed -ne '/Module/!{s/\([^ ]*\).*/\1/;p}');
do
case "${mod}" in