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:
parent
8afd39d035
commit
9fb78bc0c9
16
config/mISDN
16
config/mISDN
|
@ -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=""
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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] =
|
||||
|
|
|
@ -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), ¶m, 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************/
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
16
misdn-init
16
misdn-init
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue