added the new B410P HFC-4S based card. This card has Echocancelation in hardware. Modified mISDN_dsp to check for hw_echocan facilities. added possibility to control the gain from lower layers. Thanks to Matthew Fredrickson for most of the patches.

This commit is contained in:
Chrisian Richter 2006-09-06 17:24:22 +00:00
parent 18df072e60
commit 8ab0c38df3
11 changed files with 607 additions and 62 deletions

View File

@ -4,7 +4,7 @@ EXTRA_CFLAGS += -ggdb
#
ifdef MINCLUDES
CFLAGS += -I$(MINCLUDES)
CFLAGS += -I$(MINCLUDES) -g
endif
ifdef CONFIG_MISDN_MEMDEBUG
EXTRA_CFLAGS += -DMISDN_MEMDEBUG

View File

@ -4,7 +4,7 @@ EXTRA_CFLAGS += -ggdb
#
ifdef MINCLUDES
CFLAGS += -I$(MINCLUDES)
CFLAGS += -I$(MINCLUDES) -g
endif
ifdef CONFIG_MISDN_MEMDEBUG
EXTRA_CFLAGS += -DMISDN_MEMDEBUG

View File

@ -45,6 +45,9 @@
* out the other.
*/
//#define AGGRESSIVE_SUPPRESSOR
//#include "dsp_mec2.h"
//#include "dsp_kb1ec.h"
#include "dsp_mg2ec.h"
@ -175,6 +178,7 @@ struct dsp_features {
int hfc_id; /* unique id to identify the chip (or -1) */
int hfc_dtmf; /* set if HFCmulti card supports dtmf */
int hfc_loops; /* set if card supports tone loops */
int hfc_echocanhw; /* set if card supports echocancelation*/
int pcm_id; /* unique id to identify the pcm bus (or -1) */
int pcm_slots; /* number of slots on the pcm bus */
int pcm_banks; /* number of IO banks of pcm bus */
@ -235,7 +239,9 @@ typedef struct _dsp {
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 */

View File

@ -22,6 +22,27 @@
*
*
*/
/*
* 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);
@ -84,21 +105,40 @@ 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("Disabling EC\n");
printk(KERN_NOTICE "Disabling EC\n");
dsp->cancel_enable = 0;
dsp->txbuflen=0;
bchdev_echocancel_deactivate(dsp);
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;

View File

@ -631,6 +631,17 @@ dsp_from_down(mISDNinstance_t *inst, struct sk_buff *skb)
dev_kfree_skb(skb);
break;
case VOL_CHANGE_TX: /* change volume */
if (skb->len != sizeof(int)) {
ret = -EINVAL;
break;
}
dsp->tx_volume = *((int *)skb->data);
if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: change tx volume to %d\n", __FUNCTION__, dsp->tx_volume);
printk(KERN_DEBUG "%s: change tx volume to %d\n", __FUNCTION__, dsp->tx_volume);
dsp_cmx_hardware(dsp->conf, dsp);
break;
default:
if (dsp_debug & DEBUG_DSP_CORE)
printk(KERN_DEBUG "%s: ctrl ind %x unhandled %s\n", __FUNCTION__, hh->dinfo, dsp->inst.name);
@ -771,14 +782,16 @@ dsp_feat(void *arg)
break;
case FEAT_STATE_WAIT:
if (dsp_debug & DEBUG_DSP_MGR)
printk(KERN_DEBUG "%s: features of %s are: hfc_id=%d hfc_dtmf=%d hfc_loops=%d pcm_id=%d pcm_slots=%d pcm_banks=%d\n",
printk(KERN_DEBUG "%s: features of %s are: hfc_id=%d hfc_dtmf=%d hfc_loops=%d hfc_echocanhw:%d pcm_id=%d pcm_slots=%d pcm_banks=%d\n",
__FUNCTION__, dsp->inst.name,
dsp->features.hfc_id,
dsp->features.hfc_dtmf,
dsp->features.hfc_loops,
dsp->features.hfc_echocanhw,
dsp->features.pcm_id,
dsp->features.pcm_slots,
dsp->features.pcm_banks);
spin_lock(&dsp->feature_lock);
dsp->feature_state = FEAT_STATE_RECEIVED;
spin_unlock(&dsp->feature_lock);
@ -789,6 +802,15 @@ 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;
}

View File

@ -108,6 +108,9 @@
//#define IRQCOUNT_DEBUG
#include "hfc_multi.h"
#ifdef ECHOPREP
#include "gaintab.h"
#endif
//#warning
@ -184,6 +187,8 @@ static const PCI_ENTRY id_list[] =
"HFC-4S", 4, 1, 2},
{CCAG_VID, CCAG_VID, HFC4S_ID, 0xB560, VENDOR_CCD,
"HFC-4S Beronet Card", 4, 1, 2},
{0xD161, 0xD161, 0xB410, 0xB410, VENDOR_CCD,
"HFC-4S Digium Card", 4, 0, 2},
{CCAG_VID, CCAG_VID, HFC8S_ID, 0xB521, VENDOR_CCD,
"HFC-8S IOB4ST Recording", 8, 1, 0},
{CCAG_VID, CCAG_VID, HFC8S_ID, 0xB522, VENDOR_CCD,
@ -278,6 +283,324 @@ disable_hwirq(hfc_multi_t *hc)
HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl);
}
#define B410P_CARD
#define NUM_EC 2
#define MAX_TDM_CHAN 32
#ifdef B410P_CARD
inline void enablepcibridge(hfc_multi_t *c)
{
HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); /*was _io before*/
}
inline void disablepcibridge(hfc_multi_t *c)
{
HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x2); /*was _io before*/
}
inline unsigned char readpcibridge(hfc_multi_t *c, unsigned char address)
{
unsigned short cipv;
unsigned char data;
// slow down a PCI read access by 1 PCI clock cycle
HFC_outb(c, R_CTRL, 0x4); /*was _io before*/
if (address == 0)
cipv=0x4000;
else
cipv=0x5800;
// select local bridge port address by writing to CIP port
//data = HFC_inb(c, cipv); /*was _io before*/
outw(cipv, c->pci_iobase + 4);
data = inb(c->pci_iobase);
// restore R_CTRL for normal PCI read cycle speed
HFC_outb(c, R_CTRL, 0x0); /*was _io before*/
return data;
}
inline void writepcibridge(hfc_multi_t *hc, unsigned char address, unsigned char data)
{
unsigned short cipv;
unsigned int datav;
if (address == 0)
cipv=0x4000;
else
cipv=0x5800;
// select local bridge port address by writing to CIP port
outw(cipv, hc->pci_iobase + 4);
// define a 32 bit dword with 4 identical bytes for write sequence
datav=data | ( (__u32) data <<8) | ( (__u32) data <<16) | ( (__u32) data <<24);
// write this 32 bit dword to the bridge data port
// this will initiate a write sequence of up to 4 writes to the same address on the local bus
// interface
// the number of write accesses is undefined but >=1 and depends on the next PCI transaction
// during write sequence on the local bus
outl(datav, hc->pci_iobase);
}
inline void cpld_set_reg(hfc_multi_t *hc, unsigned char reg)
{
/* Do data pin read low byte */
HFC_outb(hc, R_GPIO_OUT1, reg);
}
inline void cpld_write_reg(hfc_multi_t *hc, unsigned char reg, unsigned char val)
{
cpld_set_reg(hc, reg);
enablepcibridge(hc);
writepcibridge(hc, 1, val);
disablepcibridge(hc);
return;
}
inline unsigned char cpld_read_reg(hfc_multi_t *hc, unsigned char reg)
{
unsigned char bytein;
cpld_set_reg(hc, reg);
/* Do data pin read low byte */
HFC_outb(hc, R_GPIO_OUT1, reg);
enablepcibridge(hc);
bytein = readpcibridge(hc, 1);
disablepcibridge(hc);
return bytein;
}
inline void vpm_write_address(hfc_multi_t *hc, unsigned short addr)
{
cpld_write_reg(hc, 0, 0xff & addr);
cpld_write_reg(hc, 1, 0x01 & (addr >> 8));
}
inline unsigned short vpm_read_address(hfc_multi_t *c)
{
unsigned short addr;
unsigned short highbit;
addr = cpld_read_reg(c, 0);
highbit = cpld_read_reg(c, 1);
addr = addr | (highbit << 8);
return addr & 0x1ff;
}
inline unsigned char vpm_in(hfc_multi_t *c, int which, unsigned short addr)
{
unsigned char res;
vpm_write_address(c, addr);
if (!which)
cpld_set_reg(c, 2);
else
cpld_set_reg(c, 3);
enablepcibridge(c);
res = readpcibridge(c, 1);
disablepcibridge(c);
cpld_set_reg(c, 0);
return res;
}
inline void vpm_out(hfc_multi_t *c, int which, unsigned short addr, unsigned char data)
{
vpm_write_address(c, addr);
enablepcibridge(c);
if (!which)
cpld_set_reg(c, 2);
else
cpld_set_reg(c, 3);
writepcibridge(c, 1, data);
cpld_set_reg(c, 0);
disablepcibridge(c);
{
unsigned char regin;
regin = vpm_in(c, which, addr);
if (regin != data)
printk("Wrote 0x%x to register 0x%x but got back 0x%x\n", data, addr, regin);
}
return;
}
void vpm_init(hfc_multi_t *wc)
{
unsigned char reg;
unsigned int mask;
unsigned int i, x, y;
unsigned int ver;
for (x=0;x<NUM_EC;x++) {
/* Setup GPIO's */
if (!x) {
ver = vpm_in(wc, x, 0x1a0);
printk("VPM: Chip %d: ver %02x\n", x, ver);
}
for (y=0;y<4;y++) {
vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */
vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */
vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */
}
/* Setup TDM path - sets fsync and tdm_clk as inputs */
reg = vpm_in(wc, x, 0x1a3); /* misc_con */
vpm_out(wc, x, 0x1a3, reg & ~2);
/* Setup Echo length (256 taps) */
vpm_out(wc, x, 0x022, 1);
vpm_out(wc, x, 0x023, 0xff);
/* Setup timeslots */
vpm_out(wc, x, 0x02f, 0x00);
mask = 0x02020202 << (x * 4);
/* Setup the tdm channel masks for all chips*/
for (i = 0; i < 4; i++)
vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff);
/* Setup convergence rate */
printk("VPM: A-law mode\n");
reg = 0x00 | 0x10 | 0x01;
vpm_out(wc,x,0x20,reg);
printk("VPM reg 0x20 is %x\n", reg);
//vpm_out(wc,x,0x20,(0x00 | 0x08 | 0x20 | 0x10));
vpm_out(wc, x, 0x24, 0x02);
reg = vpm_in(wc, x, 0x24);
printk("NLP Thresh is set to %d (0x%x)\n", reg, reg);
/* Initialize echo cans */
for (i = 0 ; i < MAX_TDM_CHAN; i++) {
if (mask & (0x00000001 << i))
vpm_out(wc,x,i,0x00);
}
udelay(10000);
/* Put in bypass mode */
for (i = 0 ; i < MAX_TDM_CHAN ; i++) {
if (mask & (0x00000001 << i)) {
vpm_out(wc,x,i,0x01);
}
}
/* Enable bypass */
for (i = 0 ; i < MAX_TDM_CHAN ; i++) {
if (mask & (0x00000001 << i))
vpm_out(wc,x,0x78 + i,0x01);
}
}
}
void vpm_check(hfc_multi_t *hctmp)
{
unsigned char gpi2;
gpi2 = HFC_inb(hctmp, R_GPI_IN2);
if ((gpi2 & 0x3) != 0x3) {
printk("Got interrupt 0x%x from VPM!\n", gpi2);
}
}
/*
* Interface to enable/disable the HW Echocan
*
* these functions are called within a spin_lock_irqsave on
* the channel instance lock, so we are not disturbed by irqs
*
* we can later easily change the interface to make other
* things configurable, for now we configure the taps
*
*/
void vpm_echocan_on(hfc_multi_t *hc, int ch, int taps)
{
unsigned int timeslot;
unsigned int unit;
channel_t *bch = hc->chan[ch].ch;
struct sk_buff *skb;
int txadj = -4;
if (hc->chan[ch].protocol != ISDN_PID_L1_B_64TRANS)
return;
if (!bch)
return;
skb = create_link_skb(PH_CONTROL | INDICATION, VOL_CHANGE_TX, sizeof(int), &txadj, 0);
if (mISDN_queue_up(&bch->inst, 0, skb))
dev_kfree_skb(skb);
timeslot = ((ch/4)*8) + ((ch%4)*4) + 1;
unit = ch % 4;
printk(KERN_NOTICE "vpm_echocan_on called taps [%d] on timeslot %d\n", taps, timeslot);
vpm_out(hc, unit, timeslot, 0x7e);
}
void vpm_echocan_off(hfc_multi_t *hc, int ch)
{
unsigned int timeslot;
unsigned int unit;
channel_t *bch = hc->chan[ch].ch;
struct sk_buff *skb;
int txadj = 0;
if (hc->chan[ch].protocol != ISDN_PID_L1_B_64TRANS)
return;
if (!bch)
return;
skb = create_link_skb(PH_CONTROL | INDICATION, VOL_CHANGE_TX, sizeof(int), &txadj, 0);
if (mISDN_queue_up(&bch->inst, 0, skb))
dev_kfree_skb(skb);
timeslot = ((ch/4)*8) + ((ch%4)*4) + 1;
unit = ch % 4;
printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n", timeslot);
/*FILLME*/
vpm_out(hc, unit, timeslot, 0x01);
}
#endif /* B410_CARD */
/******************************************/
/* free hardware resources used by driver */
/******************************************/
@ -328,7 +651,7 @@ init_chip(hfc_multi_t *hc)
u_long flags, val, val2 = 0, rev;
int cnt = 0;
int i, err = 0;
u_char r_conf_en, rval;
u_char r_conf_en,rval;
spin_lock_irqsave(&hc->lock, flags);
/* reset all registers */
@ -354,7 +677,7 @@ init_chip(hfc_multi_t *hc)
printk(KERN_WARNING "HFC_multi: WARNING: This driver doesn't consider chip revision = %ld. The chip / bridge may not work.\n", rev);
}
/* set s-ram size */
/* set s-ram size */
hc->Flen = 0x10;
hc->Zmin = 0x80;
hc->Zlen = 384;
@ -434,17 +757,36 @@ init_chip(hfc_multi_t *hc)
goto out;
}
if (test_bit(HFC_CHIP_DIGICARD,&hc->chip)) {
HFC_outb(hc, R_BRG_PCM_CFG, 0x2);
HFC_outb(hc, R_PCM_MD0, (0x9<<4) | 0x1);
HFC_outb(hc, R_PCM_MD1, 0);
printk(KERN_NOTICE "Setting GPIOs\n");
HFC_outb(hc, R_GPIO_SEL, 0x30);
HFC_outb(hc, R_GPIO_EN1, 0x3);
udelay(1000);
printk(KERN_NOTICE "calling vpm_init\n");
vpm_init(hc);
} else {
HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90);
if (hc->slots == 32)
HFC_outb(hc, R_PCM_MD1, 0x00);
if (hc->slots == 64)
HFC_outb(hc, R_PCM_MD1, 0x10);
if (hc->slots == 128)
HFC_outb(hc, R_PCM_MD1, 0x20);
HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0);
HFC_outb(hc, R_PCM_MD2, 0x00);
HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00);
}
i = 0;
HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90);
if (hc->slots == 32)
HFC_outb(hc, R_PCM_MD1, 0x00);
if (hc->slots == 64)
HFC_outb(hc, R_PCM_MD1, 0x10);
if (hc->slots == 128)
HFC_outb(hc, R_PCM_MD1, 0x20);
HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0);
HFC_outb(hc, R_PCM_MD2, 0x00);
HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00);
while (i < 256) {
HFC_outb_(hc, R_SLOT, i);
HFC_outb_(hc, A_SL_CFG, 0);
@ -478,14 +820,20 @@ init_chip(hfc_multi_t *hc)
}
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "HFC_multi F0_CNT %ld after %dms\n", val2, cnt);
if (val2 < val+4) {
printk(KERN_ERR "HFC_multi ERROR 125us pulse not counting.\n");
if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
printk(KERN_ERR "HFC_multi This happens in PCM slave mode without connected master.\n");
}
if (!test_bit(HFC_CHIP_CLOCK_IGNORE, &hc->chip)) {
err = -EIO;
goto out;
if (test_bit(HFC_CHIP_DIGICARD, &hc->chip)) {
printk(KERN_ERR "HFC_multi ingoring PCM clock for digicard.\n");
} else {
if (!test_bit(HFC_CHIP_CLOCK_IGNORE, &hc->chip) ){
err = -EIO;
goto out;
}
}
}
@ -691,13 +1039,27 @@ hfcmulti_leds(hfc_multi_t *hc)
led[i] = 0; /* led off */
i++;
}
//printk("leds %d %d %d %d\n", led[0], led[1], led[2], led[3]);
HFC_outb(hc, R_GPIO_EN1,
((led[0]>0)<<0) | ((led[1]>0)<<1) |
((led[2]>0)<<2) | ((led[3]>0)<<3));
HFC_outb(hc, R_GPIO_OUT1,
((led[0]&1)<<0) | ((led[1]&1)<<1) |
((led[2]&1)<<2) | ((led[3]&1)<<3));
if (test_bit(HFC_CHIP_DIGICARD, &hc->chip)) {
int leds=0;
for (i=0; i<4; i++) {
if (led[i]==1) {
/*green*/
leds |=( 0x2 <<(i*2));
} else if (led[i]==2) {
/*red*/
leds |=( 0x1 <<(i*2));
}
}
vpm_out(hc, 0, 0x1a8+3,leds);
} else {
HFC_outb(hc, R_GPIO_EN1,
((led[0]>0)<<0) | ((led[1]>0)<<1) |
((led[2]>0)<<2) | ((led[3]>0)<<3));
HFC_outb(hc, R_GPIO_OUT1,
((led[0]&1)<<0) | ((led[1]&1)<<1) |
((led[2]&1)<<2) | ((led[3]&1)<<3));
}
break;
case 3:
@ -951,7 +1313,12 @@ hfcmulti_tx(hfc_multi_t *hc, int ch, channel_t *chan)
//printk("debug: data: len = %d, txpending = %d!!!!\n", *len, txpending);
/* lets see how much data we will have left in buffer */
HFC_outb_(hc, R_FIFO, ch << 1);
if (test_bit(HFC_CHIP_DIGICARD, &hc->chip) && (hc->chan[ch].protocol == ISDN_PID_L1_B_64TRANS) && (hc->chan[ch].slot_rx < 0) && (hc->chan[ch].bank_rx == 0)
&& (hc->chan[ch].slot_tx < 0) && (hc->chan[ch].bank_tx == 0)) {
HFC_outb_(hc, R_FIFO, 0x20 | (ch << 1));
} else
HFC_outb_(hc, R_FIFO, ch << 1);
HFC_wait_(hc);
if (txpending == 2) {
/* reset fifo */
@ -1058,6 +1425,7 @@ next_frame:
__FUNCTION__, ch, Zspace, z1, z2, ii-i, len-i,
test_bit(FLG_HDLC, &chan->Flags) ? "HDLC":"TRANS");
/* Have to prep the audio data */
write_fifo_data(hc, d, ii - i);
chan->tx_idx = ii;
@ -1180,7 +1548,11 @@ hfcmulti_rx(hfc_multi_t *hc, int ch, channel_t *chan)
struct sk_buff *skb;
/* lets see how much data we received */
HFC_outb_(hc, R_FIFO, (ch<<1)|1);
if (test_bit(HFC_CHIP_DIGICARD, &hc->chip) && (hc->chan[ch].protocol == ISDN_PID_L1_B_64TRANS) && (hc->chan[ch].slot_rx < 0) && (hc->chan[ch].bank_rx == 0)
&& (hc->chan[ch].slot_tx < 0) && (hc->chan[ch].bank_tx == 0)) {
HFC_outb_(hc, R_FIFO, 0x20 | (ch<<1) | 1);
} else
HFC_outb_(hc, R_FIFO, (ch<<1)|1);
HFC_wait_(hc);
next_frame:
if (test_bit(FLG_HDLC, &chan->Flags)) {
@ -1593,6 +1965,11 @@ hfcmulti_interrupt(int intno, void *dev_id, struct pt_regs *regs)
j = 0;
while(j < 8) {
ch = (i<<2) + (j>>1);
if (ch >= 16) {
if (ch == 16)
printk("Shouldn't be servicing high FIFOs. Continuing.\n");
continue;
}
chan = hc->chan[ch].ch;
if (r_irq_fifo_bl & (1 << j)) {
if (chan && hc->created[hc->chan[ch].port] &&
@ -1769,23 +2146,70 @@ mode_hfcmulti(hfc_multi_t *hc, int ch, int protocol, int slot_tx, int bank_tx, i
}
break;
case (ISDN_PID_L1_B_64TRANS): /* B-channel */
/* enable TX fifo */
HFC_outb(hc, R_FIFO, ch<<1);
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_HDLC_TRP | V_IFF);
HFC_outb(hc, A_SUBCH_CFG, 0);
HFC_outb(hc, A_IRQ_MSK, 0);
HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
HFC_wait(hc);
HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence); /* tx silence */
/* enable RX fifo */
HFC_outb(hc, R_FIFO, (ch<<1)|1);
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | V_HDLC_TRP);
HFC_outb(hc, A_SUBCH_CFG, 0);
HFC_outb(hc, A_IRQ_MSK, 0);
HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
HFC_wait(hc);
if (test_bit(HFC_CHIP_DIGICARD, &hc->chip) && (hc->chan[ch].slot_rx < 0) && (hc->chan[ch].bank_rx == 0)
&& (hc->chan[ch].slot_tx < 0) && (hc->chan[ch].bank_tx == 0)) {
printk("Setting B-channel %d to echo cancelable state on PCM slot %d\n", ch,
((ch/4)*8) + ((ch%4)*4) + 1);
printk("Enabling pass through for channel\n");
vpm_out(hc, ch, ((ch/4)*8) + ((ch%4)*4) + 1, 0x01);
/* rx path */
/* S/T -> PCM */
HFC_outb(hc, R_FIFO, (ch << 1));
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF);
HFC_outb(hc, R_SLOT, (((ch/4)*8) + ((ch%4)*4) + 1) << 1);
HFC_outb(hc, A_SL_CFG, 0x80 | (ch << 1));
/* PCM -> FIFO */
HFC_outb(hc, R_FIFO, 0x20 | (ch << 1) | 1);
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF);
HFC_outb(hc, A_SUBCH_CFG, 0);
HFC_outb(hc, A_IRQ_MSK, 0);
HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
HFC_wait(hc);
HFC_outb(hc, R_SLOT, ((((ch/4)*8) + ((ch%4)*4) + 1) << 1) | 1);
HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1) | 1);
/* tx path */
/* PCM -> S/T */
HFC_outb(hc, R_FIFO, (ch << 1) | 1);
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF);
HFC_outb(hc, R_SLOT, ((((ch/4)*8) + ((ch%4)*4)) << 1) | 1);
HFC_outb(hc, A_SL_CFG, 0x80 | 0x40 | (ch << 1) | 1);
/* FIFO -> PCM */
HFC_outb(hc, R_FIFO, 0x20 | (ch << 1));
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF);
HFC_outb(hc, A_SUBCH_CFG, 0);
HFC_outb(hc, A_IRQ_MSK, 0);
HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
HFC_wait(hc);
HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence); /* tx silence */
HFC_outb(hc, R_SLOT, (((ch/4)*8) + ((ch%4)*4)) << 1);
HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1));
} else {
/* enable TX fifo */
HFC_outb(hc, R_FIFO, ch<<1);
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_HDLC_TRP | V_IFF);
HFC_outb(hc, A_SUBCH_CFG, 0);
HFC_outb(hc, A_IRQ_MSK, 0);
HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
HFC_wait(hc);
HFC_outb_(hc, A_FIFO_DATA0_NOINC, silence); /* tx silence */
/* enable RX fifo */
HFC_outb(hc, R_FIFO, (ch<<1)|1);
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | V_HDLC_TRP);
HFC_outb(hc, A_SUBCH_CFG, 0);
HFC_outb(hc, A_IRQ_MSK, 0);
HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
HFC_wait(hc);
}
if (hc->type != 1) {
hc->hw.a_st_ctrl0[hc->chan[ch].port] |= ((ch&0x3)==0)?V_B1_EN:V_B2_EN;
HFC_outb(hc, R_ST_SEL, hc->chan[ch].port);
@ -2094,7 +2518,9 @@ handle_dmsg(channel_t *ch, struct sk_buff *skb)
__FUNCTION__, hc->chan[ch->channel].port, hc->type-1);
spin_lock_irqsave(ch->inst.hwlock, flags);
hw_deactivate:
ch->state = 0;
//ch->state = 0;
ch->state = 1;
/* start deactivation */
if (hc->type == 1) {
if (debug & DEBUG_HFCMULTI_MSG)
@ -2160,6 +2586,7 @@ handle_bmsg(channel_t *ch, struct sk_buff *skb)
int slot_tx, slot_rx, bank_tx, bank_rx;
int ret = -EAGAIN;
struct dsp_features *features;
int taps;
if ((hh->prim == (PH_ACTIVATE | REQUEST)) ||
(hh->prim == (DL_ESTABLISH | REQUEST))) {
@ -2255,6 +2682,10 @@ handle_bmsg(channel_t *ch, struct sk_buff *skb)
features->pcm_id = hc->pcm;
features->pcm_slots = hc->slots;
features->pcm_banks = 2;
if (test_bit(HFC_CHIP_DIGICARD, &hc->chip))
features->hfc_echocanhw=1;
ret = 0;
break;
case HW_PCM_CONN: /* connect interface to pcm timeslot (0..N) */
@ -2323,6 +2754,24 @@ handle_bmsg(channel_t *ch, struct sk_buff *skb)
ret = 0;
break;
case HW_ECHOCAN_ON:
if (skb->len < sizeof(u32)) {
printk(KERN_WARNING "%s: HW_ECHOCAN_ON lacks parameters\n",
__FUNCTION__);
}
taps = ((u32 *)skb->data)[0];
vpm_echocan_on(hc, ch->channel, taps);
ret=0;
break;
case HW_ECHOCAN_OFF:
vpm_echocan_off(hc, ch->channel);
ret=0;
break;
default:
printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n",
__FUNCTION__, hh->dinfo);
@ -2750,7 +3199,7 @@ init_card(hfc_multi_t *hc)
}
printk(KERN_WARNING "HFC PCI: IRQ(%d) getting no interrupts during init (try %d)\n", hc->irq, cnt);
if (test_bit(HFC_CHIP_CLOCK_IGNORE, &hc->chip)) {
if (test_bit(HFC_CHIP_CLOCK_IGNORE, &hc->chip) || test_bit(HFC_CHIP_DIGICARD, &hc->chip)) {
printk(KERN_WARNING "HFC PCI: Ignoring Clock so we go on here\n");
return 0;
}
@ -2871,6 +3320,12 @@ setup_pci(hfc_multi_t *hc, struct pci_dev *pdev, int id_idx)
hc->pci_dev = pdev;
if (id_list[id_idx].clock2)
test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip);
#if 1
if (id_list[id_idx].device_id == 0xB410)
test_and_set_bit(HFC_CHIP_DIGICARD, &hc->chip);
#endif
if (hc->pci_dev->irq <= 0) {
printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n");
return (-EIO);
@ -3662,6 +4117,7 @@ static int __devinit hfcpci_probe(struct pci_dev *pdev, const struct pci_device_
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: Init modes card(%d)\n", __FUNCTION__, HFC_idx+1);
hfcmulti_initmode(hc);
/* check if Port Jumper config matches module param 'protocol' */
@ -3679,6 +4135,9 @@ static int __devinit hfcpci_probe(struct pci_dev *pdev, const struct pci_device_
// Port mode (TE/NT) jumpers
pmj = ((HFC_inb(hc, R_GPI_IN3) >> 4) & 0xf);
if (test_bit(HFC_CHIP_DIGICARD, &hc->chip))
pmj = ~pmj & 0xf;
printk(KERN_INFO "%s: DIPs(0x%x) jumpers(0x%x)\n", __FUNCTION__, dips, pmj);
pt = 0;
@ -3842,6 +4301,7 @@ static struct pci_device_id hfmultipci_ids[] __devinitdata = {
{ CCAG_VID, 0x08B4 , CCAG_VID, 0x08B4, 0, 0, 0 }, //Old Eval
{ CCAG_VID, 0x08B4 , CCAG_VID, 0xB520, 0, 0, 0 }, //IOB4ST
{ CCAG_VID, 0x08B4 , CCAG_VID, 0xB620, 0, 0, 0 }, //4S
{ 0xD161, 0xB410 , 0xD161, 0xB410, 0, 0, 0 }, //4S - Digium
/** Cards with HFC-8S Chip**/
{ CCAG_VID, 0x16B8 , CCAG_VID, 0xB562, 0, 0, 0 }, //BN8S

View File

@ -99,6 +99,8 @@ typedef struct hfcmulti_hw hfcmulti_hw_t;
#define HFC_CHIP_CRYSTAL_CLOCK 9 /* autarc clocking mode */
#define HFC_CHIP_WATCHDOG 10 /* wether we should send signals
to the watchdog */
#define HFC_CHIP_DIGICARD 11 /* wether we have a b410p with echocan in
hw */
struct hfc_multi {
struct list_head list;
@ -1235,31 +1237,37 @@ static void _HFC_wait(hfc_multi_t *a, char *function, int line)
#endif
/* usage: HFC_outX(card,register,value); */
static inline void HFC_outb(hfc_multi_t *a, u_char b, u_char c)
static inline void HFC_outb(hfc_multi_t *a, unsigned short b, u_char c)
{
outb(b,(a->pci_iobase)+4);
outw(b,(a->pci_iobase)+4);
outb(c,a->pci_iobase);
}
static inline void HFC_outl(hfc_multi_t *a, u_char b, u_long c)
static inline void HFC_outl(hfc_multi_t *a, unsigned short b, u_long c)
{
outb(b,(a->pci_iobase)+4);
outw(b,(a->pci_iobase)+4);
outl(c,a->pci_iobase);
}
/* usage: value=HFC_inX(card,register); */
static inline u_char HFC_inb(hfc_multi_t *a, u_char b)
static inline void HFC_outw(hfc_multi_t *a, unsigned short b, u_long c)
{
outb(b,(a->pci_iobase)+4);
outw(b,(a->pci_iobase)+4);
outw(c,a->pci_iobase);
}
/* usage: value=HFC_inX(card,register); */
static inline u_char HFC_inb(hfc_multi_t *a, unsigned short b)
{
outw(b,(a->pci_iobase)+4);
return (inb((volatile u_int)a->pci_iobase));
}
static inline u_short HFC_inw(hfc_multi_t *a, u_char b)
static inline u_short HFC_inw(hfc_multi_t *a, unsigned short b)
{
outb(b,(a->pci_iobase)+4);
outw(b,(a->pci_iobase)+4);
return (inw((volatile u_int)a->pci_iobase));
}
static inline u_long HFC_inl(hfc_multi_t *a, u_char b)
static inline u_long HFC_inl(hfc_multi_t *a, unsigned short b)
{
outb(b,(a->pci_iobase)+4);
outw(b,(a->pci_iobase)+4);
return (inl((volatile u_int)a->pci_iobase));
}

View File

@ -112,7 +112,7 @@ mISDN_unregister_sysfs_inst(mISDNinstance_t *inst)
{
char name[8];
if (inst->id) {
if (inst && inst->id) {
if (inst->st) {
sprintf(name,"layer.%d", inst->id & LAYER_ID_MASK);

View File

@ -238,11 +238,13 @@ mISDN_register_sysfs_stack(mISDNstack_t *st)
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;
#ifdef SYSFS_SUPPORT
err = sysfs_create_group(&st->class_dev.kobj, &new_pid_group);
if (err)
goto out_unreg;
#endif
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);

View File

@ -158,6 +158,8 @@
#define HW_RECEIVE_ON 0x0585
#define HW_SPL_LOOP_ON 0x0586
#define HW_SPL_LOOP_OFF 0x0587
#define HW_ECHOCAN_ON 0x0588
#define HW_ECHOCAN_OFF 0x0589
#define HW_TESTLOOP 0xFF00
#define HW_FIRM_START 0xFF10
#define HW_FIRM_DATA 0xFF11

View File

@ -390,6 +390,11 @@ function create_misdn_init_conf {
portcount=0
for line in $(${LSPCI} -n -d 0xd161:b410); do
addcard "0x4"
addport 4
done
for line in $(${LSPCI} -n | sed -n 's/^\(0000:\|\)\([0-9a-f]\{2\}:[0-9a-f]\{2\}.[0-9a-f]\{1\}\)\( Class \| \)[0-9a-f]\{4\}: 1397:\([0-9a-f]\{4\}\).*$/\4 \2/p'); do
case "${line}" in
30b1*)
@ -524,6 +529,7 @@ function create_misdn_init_conf {
#
# set this to 2 and you'll have software bridging instead of
# hardware bridging.
#
#
# dtmftreshold=<milliseconds>
#
@ -582,7 +588,6 @@ case "$1" in
echo "modprobe mISDN_dsp debug=0x0 options=$dsp_options $poll_option $dtmftreshold_option"
modprobe mISDN_dsp debug=0x0 options=$dsp_options $poll_option $dtmftreshold_option
sleep 1
if [ ! -e /dev/mISDN ]; then