diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile index 99c7fb4..d10d910 100644 --- a/drivers/isdn/hardware/mISDN/Makefile +++ b/drivers/isdn/hardware/mISDN/Makefile @@ -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 diff --git a/drivers/isdn/hardware/mISDN/Makefile.v2.6 b/drivers/isdn/hardware/mISDN/Makefile.v2.6 index 99c7fb4..d10d910 100644 --- a/drivers/isdn/hardware/mISDN/Makefile.v2.6 +++ b/drivers/isdn/hardware/mISDN/Makefile.v2.6 @@ -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 diff --git a/drivers/isdn/hardware/mISDN/dsp.h b/drivers/isdn/hardware/mISDN/dsp.h index faa7b3e..3d729ea 100644 --- a/drivers/isdn/hardware/mISDN/dsp.h +++ b/drivers/isdn/hardware/mISDN/dsp.h @@ -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 */ diff --git a/drivers/isdn/hardware/mISDN/dsp_cancel.c b/drivers/isdn/hardware/mISDN/dsp_cancel.c index b84d971..abd754e 100644 --- a/drivers/isdn/hardware/mISDN/dsp_cancel.c +++ b/drivers/isdn/hardware/mISDN/dsp_cancel.c @@ -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), ¶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); @@ -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; diff --git a/drivers/isdn/hardware/mISDN/dsp_core.c b/drivers/isdn/hardware/mISDN/dsp_core.c index 6a0845d..0932eba 100644 --- a/drivers/isdn/hardware/mISDN/dsp_core.c +++ b/drivers/isdn/hardware/mISDN/dsp_core.c @@ -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; } diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.c b/drivers/isdn/hardware/mISDN/hfc_multi.c index aa418d3..9ec1575 100644 --- a/drivers/isdn/hardware/mISDN/hfc_multi.c +++ b/drivers/isdn/hardware/mISDN/hfc_multi.c @@ -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> (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 diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h index 6abb3c1..efd177e 100644 --- a/drivers/isdn/hardware/mISDN/hfc_multi.h +++ b/drivers/isdn/hardware/mISDN/hfc_multi.h @@ -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)); } diff --git a/drivers/isdn/hardware/mISDN/sysfs_inst.c b/drivers/isdn/hardware/mISDN/sysfs_inst.c index e6e6515..32ae851 100644 --- a/drivers/isdn/hardware/mISDN/sysfs_inst.c +++ b/drivers/isdn/hardware/mISDN/sysfs_inst.c @@ -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); diff --git a/drivers/isdn/hardware/mISDN/sysfs_st.c b/drivers/isdn/hardware/mISDN/sysfs_st.c index c5bfd47..6cf074e 100644 --- a/drivers/isdn/hardware/mISDN/sysfs_st.c +++ b/drivers/isdn/hardware/mISDN/sysfs_st.c @@ -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); diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index d7842a7..534de92 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -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 diff --git a/misdn-init b/misdn-init index b7c191c..41c514e 100755 --- a/misdn-init +++ b/misdn-init @@ -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= # @@ -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