mISDN/drivers/isdn/hardware/mISDN/hfc_multi.c

3104 lines
90 KiB
C

/*
* hfc_multi.c low level driver for hfc-4s/hfc-8s/hfc-e1 based cards
*
* Author Andreas Eversberg (jolly@jolly.de)
*
* inspired by existing hfc-pci driver:
* Copyright 1999 by Werner Cornelius (werner@isdn-development.de)
* Copyright 2001 by Karsten Keil (keil@isdn4linux.de)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* Thanx to Cologne Chip AG for this great controller!
*/
/* module parameters:
* type:
Value 1 = HFC-E1 (1 port) 0x01
Value 4 = HFC-4S (4 ports) 0x04
Value 8 = HFC-8S (8 ports) 0x08
Bit 8 = uLaw (instead of aLaw)
Bit 9 = Enable DTMF detection on all B-channels
Bit 10 = spare
Bit 11 = Set PCM bus into slave mode.
Bit 14 = Use external ram (128K)
Bit 15 = Use external ram (512K)
Bit 16 = Use 64 timeslots instead of 32
Bit 17 = Use 128 timeslots instead of anything else
* protocol:
NOTE: Must be given for all ports, not for the number of cards.
HFC-4S/HFC-8S/HFC-E1 bits:
Bit 0-3 = protocol
Bit 4 = NT-Mode
Bit 5 = PTP (instead of multipoint)
HFC-4S/HFC-8S only bits:
Bit 16 = Use master clock for this S/T interface (ony once per chip).
Bit 17 = transmitter line setup (non capacitive mode) DONT CARE!
Bit 18 = Disable E-channel. (No E-channel processing)
Bit 19 = Register E-channel as D-stack (TE-mode only)
HFC-E1 only bits:
Bit 16 = interface: 0=copper, 1=optical
Bit 17 = reserved (later for 32 B-channels transparent mode)
Bit 18 = Report LOS
Bit 19 = Report AIS
Bit 20 = Report SLIP
Bit 21-22 = elastic jitter buffer (1-3), Use 0 for default.
(all other bits are reserved and shall be 0)
* layermask:
NOTE: Must be given for all ports, not for the number of cards.
mask of layers to be used for D-channel stack
* debug:
NOTE: only one debug value must be given for all cards
enable debugging (see hfc_multi.h for debug options)
* poll:
NOTE: only one debug value must be given for all cards
Give the number of samples for each fifo process.
By default 128 is used. Decrease to reduce delay, increase to
reduce cpu load. If unsure, don't mess with it!
Valid is 8, 16, 32, 64, 128, 256.
*/
// debug using register map (never use this, it will flood your system log)
//#define HFC_REGISTER_MAP
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include "dchannel.h"
#include "bchannel.h"
#include "layer1.h"
#include "helper.h"
#include "debug.h"
#include <linux/isdn_compat.h>
#define SPIN_DEBUG
#define LOCK_STATISTIC
#include "hw_lock.h"
#include "hfc_multi.h"
extern const char *CardType[];
static const char *hfcmulti_revision = "$Revision$";
static int HFC_cnt;
static mISDNobject_t HFCM_obj;
static char HFCName[] = "HFC_multi";
/* table entry in the PCI devices list */
typedef struct {
int vendor_id;
int vendor_sub;
int device_id;
int device_sub;
char *vendor_name;
char *card_name;
int type;
int clock2;
int leds;
} PCI_ENTRY;
static int poll_timer = 6; /* default = 128 samples = 16ms */
/* number of POLL_TIMER interrupts for G2 timeout (min 120ms) */
static int nt_t1_count[] = { 480, 240, 120, 60, 30, 15, 8, 4 };
#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */
#define CLKDEL_NT 0x0c /* CLKDEL in NT mode (0x60 MUST not be included!) */
static u_char silence = 0xff; /* silence by LAW */
/* enable 32 bit fifo access (PC usage) */
#define FIFO_32BIT_ACCESS
#define VENDOR_CCD "Cologne Chip AG"
static const PCI_ENTRY id_list[] =
{
{0x1397, 0x1397, 0x08B4, 0x08B4, VENDOR_CCD,
"HFC-4S Eval", 4, 0, 1},
{0x1397, 0x1397, 0x16B8, 0x16B8, VENDOR_CCD,
"HFC-8S Eval", 8, 0, 0},
{0x1397, 0x1397, 0x30B1, 0x30B1, VENDOR_CCD,
"HFC-E1 Eval", 1, 0, 0},
{0x1397, 0x1397, 0x08B4, 0xB520, VENDOR_CCD,
"HFC-4S OEM IOB4ST", 4, 1, 2},
{0x1397, 0x1397, 0x16B8, 0xB521, VENDOR_CCD,
"HFC-8S OEM IOB4ST Recording", 8, 1, 0},
{0x1397, 0x1397, 0x16B8, 0xB522, VENDOR_CCD,
"HFC-8S OEM IOB8ST", 8, 1, 0},
{0x1397, 0x1397, 0x30B1, 0xB523, VENDOR_CCD,
"HFC-E1 OEM IOB1E1", 1, 0, 0},
{0, 0, 0, 0, NULL, NULL, 0, 0, 0},
};
#warning COLOGNE welcher takt beim GUDE-E1-board ?
#warning COLOGNE gibt es ein E1 eval ?
/****************/
/* module stuff */
/****************/
/* NOTE: MAX_PORTS must be 8*MAX_CARDS */
#define MAX_CARDS 8
#define MAX_PORTS 64
#define MODULE_CARDS_T "1-8i"
#define MODULE_PORTS_T "1-64i" /* 8 cards can have 64 ports */
static u_int type[MAX_CARDS];
static u_int protocol[MAX_PORTS];
static int layermask[MAX_PORTS];
static int debug;
static int poll;
#ifdef MODULE
MODULE_AUTHOR("Andreas Eversberg");
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif
MODULE_PARM(debug, "1i");
MODULE_PARM(poll, "1i");
MODULE_PARM(type, MODULE_CARDS_T);
MODULE_PARM(protocol, MODULE_PORTS_T);
MODULE_PARM(layermask, MODULE_PORTS_T);
#endif
/*************************/
/* lock and unlock stuff */
/*************************/
static int
lock_dev(void *data, int nowait)
{
register mISDN_HWlock_t *lock = &((hfc_multi_t *)data)->lock;
if (debug & DEBUG_HFCMULTI_LOCK)
printk(KERN_DEBUG "%s\n", __FUNCTION__);
return(lock_HW(lock, nowait));
}
static void
unlock_dev(void *data)
{
register mISDN_HWlock_t *lock = &((hfc_multi_t *)data)->lock;
if (debug & DEBUG_HFCMULTI_LOCK)
printk(KERN_DEBUG "%s\n", __FUNCTION__);
unlock_HW(lock);
}
/******************************************/
/* free hardware resources used by driver */
/******************************************/
static void
release_io_hfcmulti(hfc_multi_t *hc)
{
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: entered\n", __FUNCTION__);
/* irq off */
HFC_outb(hc, R_IRQ_CTRL, 0);
/* soft reset */
hc->hw.r_cirm |= V_SRES;
HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
udelay(10);
hc->hw.r_cirm &= ~V_SRES;
HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
HFC_wait(hc);
/* disable memory mapped ports + busmaster */
pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0);
iounmap((void *)hc->pci_io);
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: done\n", __FUNCTION__);
}
/****************************************************************************/
/* function called to reset the HFC chip. A complete software reset of chip */
/* and fifos is done. All configuration of the chip is done. */
/****************************************************************************/
static int
init_chip(hfc_multi_t *hc)
{
unsigned long val, val2 = 0, rev;
int cnt = 0;
int i;
/* reset all registers */
memset(&hc->hw, 0, sizeof(hfcmulti_hw_t));
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: entered\n", __FUNCTION__);
val = HFC_inb(hc, R_CHIP_ID);
rev = HFC_inb(hc, R_CHIP_RV);
printk(KERN_INFO "HFC_multi: resetting HFC with chip ID=0x%lx revision=%ld%s\n", val>>4, rev, (rev==0)?" (old FIFO handling)":"");
if (rev == 0) {
test_and_set_bit(HFC_CHIP_REVISION0, &hc->chip);
printk(KERN_WARNING "HFC_multi: NOTE: Your chip is revision 0, ask Cologne Chip for update. Newer chips have a better FIFO handling. Old chips still work but may have slightly lower HDLC transmit performance.\n");
}
/* set s-ram size */
hc->Flen = 0x10;
hc->Zmin = 0x80;
hc->Zlen = 384;
hc->DTMFbase = 0x1000;
if (test_bit(HFC_CHIP_EXRAM_128, &hc->chip)) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: changing to 128K extenal RAM\n", __FUNCTION__);
hc->hw.r_ctrl |= V_EXT_RAM;
hc->hw.r_ram_sz = 1;
hc->Flen = 0x20;
hc->Zmin = 0xc0;
hc->Zlen = 1856;
hc->DTMFbase = 0x2000;
}
if (test_bit(HFC_CHIP_EXRAM_512, &hc->chip)) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: changing to 512K extenal RAM\n", __FUNCTION__);
hc->hw.r_ctrl |= V_EXT_RAM;
hc->hw.r_ram_sz = 2;
hc->Flen = 0x20;
hc->Zmin = 0xc0;
hc->Zlen = 8000;
hc->DTMFbase = 0x2000;
}
/* we only want the real Z2 read-pointer for revision > 0 */
if (!test_bit(HFC_CHIP_REVISION0, &hc->chip))
hc->hw.r_ram_sz |= V_FZ_MD;
/* soft reset */
HFC_outb(hc, R_CTRL, hc->hw.r_ctrl);
HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz);
HFC_outb(hc, R_FIFO_MD, 0);
hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES | V_RLD_EPR;
HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
udelay(10);
hc->hw.r_cirm = 0;
HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
HFC_wait(hc);
HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz);
/* set pcm mode & reset */
if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: setting PCM into slave mode\n", __FUNCTION__);
} else {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: setting PCM into master mode\n", __FUNCTION__);
hc->hw.r_pcm_mo0 |= V_PCM_MO;
}
i = 0;
HFC_outb(hc, R_PCM_MO0, hc->hw.r_pcm_mo0 | 0x90);
if (hc->slots == 32)
HFC_outb(hc, R_PCM_MO1, 0x00);
if (hc->slots == 64)
HFC_outb(hc, R_PCM_MO1, 0x10);
if (hc->slots == 128)
HFC_outb(hc, R_PCM_MO1, 0x20);
HFC_outb(hc, R_PCM_MO0, hc->hw.r_pcm_mo0 | 0x00);
while (i < 256) {
HFC_outb(hc, R_SLOT, i);
HFC_outb(hc, A_SL_CFG, 0);
i++;
}
/* set clock speed */
if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: setting double clock\n", __FUNCTION__);
HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK);
}
/* check if R_F0_CNT counts */
val = HFC_inw(hc, R_F0_CNT);
printk(KERN_DEBUG "HFC_multi F0_CNT %ld after status ok\n", val);
while (cnt < 50) { /* max 50 ms */
udelay(1000);
cnt++;
val2 = HFC_inw(hc, R_F0_CNT);
if (val2 >= val+4) /* wait 4 pulses */
break;
}
printk(KERN_DEBUG "HFC_multi F0_CNT %ld after %dms\n", val2, cnt);
if (val2 < val+2) {
printk(KERN_ERR "HFC_multi ERROR 125us pulse still not counting.\n");
printk(KERN_ERR "HFC_multi This happens in PCM slave mode without connected master.\n");
return(-EIO);
}
/* set up timer */
HFC_outb(hc, R_TI_WD, poll_timer);
hc->hw.r_irqmsk_misc |= V_TI_IRQMSK;
/* set DTMF detection */
if (test_bit(HFC_CHIP_DTMF, &hc->chip)) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: enabling DTMF detection for all B-channel\n", __FUNCTION__);
hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP;
if (test_bit(HFC_CHIP_ULAW, &hc->chip))
hc->hw.r_dtmf |= V_ULAW_SEL;
HFC_outb(hc, R_DTMF_N, 102-1);
hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK;
}
/* set master clock */
if (hc->masterclk >= 0) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: setting ST master clock to port %d (0..%d)\n", __FUNCTION__, hc->masterclk, hc->type-1);
hc->hw.r_st_sync = hc->masterclk | V_AUTO_SYNC;
HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync);
}
/* setting misc irq */
HFC_outb(hc, R_IRQMSK_MISC, hc->hw.r_irqmsk_misc);
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: done\n", __FUNCTION__);
return(0);
}
/***************/
/* output leds */
/***************/
static void
hfcmulti_leds(hfc_multi_t *hc)
{
int i, state;
dchannel_t *dch;
int led[4];
hc->ledcount += poll;
if (hc->ledcount > 4096)
hc->ledcount -= 4096;
i = 0;
while(i < 4) {
state = 0;
if ((dch = hc->chan[(i<<2)|2].dch))
state = dch->ph_state;
if (state==3)
led[i] = 2; /* led green */
else if (state && (hc->ledcount>>10)==(i^(i>1)))
led[i] = 1; /* led red */
else
led[i] = 0; /* led off */
i++;
}
switch(hc->leds) {
case 1: /* Eval Board */
break;
case 2: /* OEM Board */
//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));
break;
}
}
#warning COLOGNE ansteuerung der LEDS
/**************************/
/* read dtmf coefficients */
/**************************/
static void
hfcmulti_dtmf(hfc_multi_t *hc)
{
signed long coeff[16];
unsigned long mantissa;
int co, ch;
bchannel_t *bch = NULL;
unsigned char exponent;
int dtmf = 0;
int addr;
unsigned short w_float;
if (debug & DEBUG_HFCMULTI_DTMF)
printk(KERN_DEBUG "%s: dtmf detection irq\n", __FUNCTION__);
ch = 0;
while(ch < 32) {
/* only process enabled B-channels */
if (!(bch = hc->chan[ch].bch)) {
ch++;
continue;
}
if (!hc->created[hc->chan[ch].port]) {
ch++;
continue;
}
if (bch->protocol != ISDN_PID_L1_B_64TRANS) {
ch++;
continue;
}
if (debug & DEBUG_HFCMULTI_DTMF)
printk(KERN_DEBUG "%s: dtmf channel %d:", __FUNCTION__, ch);
dtmf = 1;
co = 0;
while(co < 8) {
/* read W(n-1) coefficient */
addr = hc->DTMFbase + ((co<<7) | (ch<<2));
HFC_outb(hc, R_RAM_ADDR0, addr);
HFC_outb(hc, R_RAM_ADDR1, addr>>8);
HFC_outb(hc, R_RAM_ADDR2, (addr>>16) | V_ADDR_INC);
w_float = HFC_inb(hc, R_RAM_DATA);
w_float |= (HFC_inb(hc, R_RAM_DATA) << 8);
if (debug & DEBUG_HFCMULTI_DTMF)
printk(" %04x", w_float);
/* decode float (see chip doc) */
mantissa = w_float & 0x0fff;
if (w_float & 0x8000)
mantissa |= 0xfffff000;
exponent = (w_float>>12) & 0x7;
if (exponent) {
mantissa ^= 0x1000;
mantissa <<= (exponent-1);
}
/* store coefficient */
coeff[co<<1] = mantissa;
/* read W(n) coefficient */
w_float = HFC_inb(hc, R_RAM_DATA);
w_float |= (HFC_inb(hc, R_RAM_DATA) << 8);
if (debug & DEBUG_HFCMULTI_DTMF)
printk(" %04x", w_float);
/* decode float (see chip doc) */
mantissa = w_float & 0x0fff;
if (w_float & 0x8000)
mantissa |= 0xfffff000;
exponent = (w_float>>12) & 0x7;
if (exponent) {
mantissa ^= 0x1000;
mantissa <<= (exponent-1);
}
/* store coefficient */
coeff[(co<<1)|1] = mantissa;
co++;
}
if (debug & DEBUG_HFCMULTI_DTMF)
printk("\n");
if (hc->chan[ch].dtmf_skb) {
#warning
// printk(KERN_WARNING "%s: dtmf_skb still exist\n", __FUNCTION__);
ch++;
continue;
}
hc->chan[ch].dtmf_skb = create_link_skb(PH_CONTROL | INDICATION, HW_HFC_COEFF, sizeof(coeff), coeff, 0);
if (!hc->chan[ch].dtmf_skb) {
printk(KERN_WARNING "%s: No memory for skb\n", __FUNCTION__);
ch++;
continue;
}
bch_sched_event(bch, B_DTMFREADY);
ch++;
}
/* restart DTMF processing */
hc->dtmf = dtmf;
if (dtmf)
HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF);
}
/*********************************/
/* fill fifo as much as possible */
/*********************************/
static void
hfcmulti_tx(hfc_multi_t *hc, int ch, dchannel_t *dch, bchannel_t *bch)
{
int i, ii, temp;
int Zspace, z1, z2;
int Fspace, f1, f2;
unsigned char *d, *dd, *buf = NULL;
int *len = NULL, *idx = NULL; /* = NULL, to make GCC happy */
int hdlc = 0;
/* get skb, fifo & mode */
if (dch) {
buf = dch->tx_buf;
len = &dch->tx_len;
idx = &dch->tx_idx;
hdlc = 1;
}
if (bch) {
buf = bch->tx_buf;
len = &bch->tx_len;
idx = &bch->tx_idx;
if (bch->protocol == ISDN_PID_L1_B_64HDLC)
hdlc = 1;
}
if (!(*len))
return; /* no data */
/* lets see how much data we will have left in buffer */
HFC_outb(hc, R_FIFO, ch<<1);
HFC_wait(hc);
next_frame:
if (hdlc) {
f1 = HFC_inb(hc, A_F1);
f2 = HFC_inb(hc, A_F2);
while (f2 != (temp=HFC_inb(hc, A_F2))) {
if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s: reread f2 because %d!=%d\n", __FUNCTION__, temp, f2);
f2 = temp; /* repeat until F2 is equal */
}
Fspace = f2-f1-1;
if (Fspace < 0)
Fspace += hc->Flen;
/* Old FIFO handling doesn't give us the current Z2 read
* pointer, so we cannot send the next frame before the fifo
* is empty. It makes no difference except for a slightly
* lower performance.
*/
if (test_bit(HFC_CHIP_REVISION0, &hc->chip)) {
if (f1 != f2)
Fspace = 0;
else
Fspace = 1;
}
/* one frame only for ST D-channels, to allow resending */
if (hc->type!=1 && dch) {
if (f1 != f2)
Fspace = 0;
}
/* F-counter full condition */
if (Fspace == 0)
return;
}
z1 = HFC_inw(hc, A_Z1) - hc->Zmin;
z2 = HFC_inw(hc, A_Z2) - hc->Zmin;
while(z2 != (temp=(HFC_inw(hc, A_Z2) - hc->Zmin))) {
if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s: reread z2 because %d!=%d\n", __FUNCTION__, temp, z2);
z2 = temp; /* repeat unti Z2 is equal */
}
Zspace = z2-z1-1;
if (Zspace < 0)
Zspace += hc->Zlen;
/* buffer too full, there must be at least one more byte for 0-volume */
if (Zspace < 4) /* just to be safe */
return;
/* fill fifo to what we have left */
i = *idx;
ii = *len;
d = buf + i;
if (ii-i > Zspace)
ii = Zspace+i;
if (debug & DEBUG_HFCMULTI_FIFO) {
printk(KERN_DEBUG "%s: fifo(%d) has %d bytes space left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n",
__FUNCTION__, ch, Zspace, z1, z2, ii-i, (*len)-i, hdlc?"HDLC":"TRANS");
}
#ifdef FIFO_32BIT_ACCESS
dd = d + ((ii-i)&0xfffc);
i += (ii-i) & 0xfffc;
while(d != dd) {
HFC_outl(hc, A_FIFO_DATA0, *((unsigned long *)d));
// if (debug & DEBUG_HFCMULTI_FIFO)
// printk("%02x %02x %02x %02x ", d[0], d[1], d[2], d[3]);
d+=4;
}
#endif
dd = d + (ii-i);
while(d != dd) {
HFC_outb(hc, A_FIFO_DATA0, *d);
// if (debug & DEBUG_HFCMULTI_FIFO)
// printk("%02x ", d[0]);
d++;
}
// if (debug & DEBUG_HFCMULTI_FIFO)
// printk("\n");
*idx = ii;
/* if not all data has been written */
if (ii != *len) {
/* NOTE: fifo is started by the calling function */
return;
}
/* if all data has been written */
if (hdlc) {
/* increment f-counter */
HFC_outb(hc, R_INC_RES_FIFO, V_INC_F);
HFC_wait(hc);
}
if (dch) {
/* check for next frame */
if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) {
if (dch->next_skb) {
dch->tx_idx = 0;
dch->tx_len = dch->next_skb->len;
memcpy(dch->tx_buf, dch->next_skb->data, dch->tx_len);
dchannel_sched_event(dch, D_XMTBUFREADY);
goto next_frame;
} else
printk(KERN_WARNING "%s: tx irq TX_NEXT without skb\n", __FUNCTION__);
}
test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
dch->tx_idx = dch->tx_len = 0;
}
if (bch) {
/* check for next frame */
if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
if (bch->next_skb) {
bch->tx_idx = 0;
bch->tx_len = bch->next_skb->len;
memcpy(bch->tx_buf, bch->next_skb->data, bch->tx_len);
bch_sched_event(bch, B_XMTBUFREADY);
goto next_frame;
} else
printk(KERN_WARNING "%s: tx irq TX_NEXT without skb\n", __FUNCTION__);
}
test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
bch->tx_idx = bch->tx_len = 0;
}
/* now we have no more data, so in case of transparent,
* we set the last byte in fifo to 'silence' in case we will get
* no more data at all. this prevents sending an undefined value.
*/
if (!hdlc)
HFC_outb(hc, A_FIFO_DATA0_NOINC, silence);
}
/**************/
/* empty fifo */
/**************/
static void
hfcmulti_rx(hfc_multi_t *hc, int ch, dchannel_t *dch, bchannel_t *bch)
{
int ii, temp;
int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */
int f1 = 0, f2 = 0; /* = 0, to make GCC happy */
unsigned char *d, *dd, *buf = NULL;
int *idx = NULL, max = 0; /* = 0, to make GCC happy */
int hdlc = 0;
struct sk_buff *skb;
/* get skb, fifo & mode */
if (dch) {
buf = hc->chan[ch].rx_buf;
idx = &hc->chan[ch].rx_idx;
max = MAX_DFRAME_LEN_L1;
hdlc = 1;
}
if (bch) {
buf = bch->rx_buf;
idx = &bch->rx_idx;
max = MAX_DATA_MEM;
if (bch->protocol == ISDN_PID_L1_B_64HDLC)
hdlc = 1;
}
/* lets see how much data we received */
HFC_outb(hc, R_FIFO, (ch<<1)|1);
HFC_wait(hc);
next_frame:
#if 0
/* set Z2(F1) */
HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz & ~V_FZ_MD);
#endif
if (hdlc) {
f1 = HFC_inb(hc, A_F1);
while (f1 != (temp=HFC_inb(hc, A_F1))) {
if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s: reread f1 because %d!=%d\n", __FUNCTION__, temp, f1);
f1 = temp; /* repeat until F1 is equal */
}
f2 = HFC_inb(hc, A_F2);
}
z1 = HFC_inw(hc, A_Z1) - hc->Zmin;
while(z1 != (temp=(HFC_inw(hc, A_Z1) - hc->Zmin))) {
if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s: reread z2 because %d!=%d\n", __FUNCTION__, temp, z2);
z1 = temp; /* repeat unti Z1 is equal */
}
z2 = HFC_inw(hc, A_Z2) - hc->Zmin;
Zsize = z1-z2;
if (hdlc && f1!=f2) /* complete hdlc frame */
Zsize++;
if (Zsize < 0)
Zsize += hc->Zlen;
/* if buffer is empty */
if (Zsize <= 0)
return;
/* empty fifo with what we have */
if (hdlc) {
if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s: fifo(%d) reading %d bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) got=%d\n",
__FUNCTION__, ch, Zsize, z1, z2, (f1==f2)?"fragment":"COMPLETE", f1, f2, Zsize+*idx);
/* HDLC */
ii = Zsize;
if ((ii + *idx) > max) {
printk(KERN_DEBUG "%s: hdlc-frame too large.\n", __FUNCTION__);
*idx = 0;
HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
HFC_wait(hc);
return;
}
d = buf + *idx;
#ifdef FIFO_32BIT_ACCESS
dd = d + (ii&0xfffc);
while(d != dd) {
*((unsigned long *)d) = HFC_inl(hc, A_FIFO_DATA0);
//#warning *****************
//printk("%02x %02x %02x %02x ", d[0], d[1], d[2], d[3]);
d+=4;
}
//printk("\n");
#endif
dd = d + (ii&0x0003);
while(d != dd) {
*d++ = HFC_inb(hc, A_FIFO_DATA0);
}
*idx += ii;
if (f1 != f2) {
/* increment Z2,F2-counter */
HFC_outb(hc, R_INC_RES_FIFO, V_INC_F);
HFC_wait(hc);
/* check size */
if (*idx < 4) {
printk(KERN_DEBUG "%s: Frame below minimum size\n", __FUNCTION__);
return;
}
/* there is at least one complete frame, check crc */
if (buf[(*idx)-1]) {
if (debug & DEBUG_HFCMULTI_CRC)
printk(KERN_DEBUG "%s: CRC-error\n", __FUNCTION__);
return;
}
if (!(skb = alloc_stack_skb((*idx)-3, (bch)?bch->up_headerlen:dch->up_headerlen))) {
printk(KERN_DEBUG "%s: No mem for skb\n", __FUNCTION__);
return;
}
memcpy(skb_put(skb, (*idx)-3), buf, (*idx)-3);
// if (debug & DEBUG_HFCMULTI_FIFO) {
// temp = 0;
// while(temp < (*idx)-3)
// printk("%02x ", skb->data[temp++]);
// printk("\n");
// }
if (dch) {
if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s: sending D-channel frame to user space.\n", __FUNCTION__);
/* schedule D-channel event */
skb_queue_tail(&dch->rqueue, skb);
dchannel_sched_event(dch, D_RCVBUFREADY);
}
if (bch) {
/* schedule B-channel event */
skb_queue_tail(&bch->rqueue, skb);
bch_sched_event(bch, B_RCVBUFREADY);
}
*idx = 0;
goto next_frame;
}
/* there is an incomplete frame */
} else {
/* transparent */
ii = Zsize;
if (ii > MAX_DATA_MEM)
ii = MAX_DATA_MEM;
if (!(skb = alloc_stack_skb(ii, bch->up_headerlen))) {
printk(KERN_DEBUG "%s: No mem for skb\n", __FUNCTION__);
HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
HFC_wait(hc);
return;
}
if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s: fifo(%d) reading %d bytes (z1=%04x, z2=%04x) TRANS\n",
__FUNCTION__, ch, ii, z1, z2);
d = skb_put(skb, ii);
#ifdef FIFO_32BIT_ACCESS
dd = d + (ii&0xfffc);
while(d != dd) {
*((unsigned long *)d) = HFC_inl(hc, A_FIFO_DATA0);
d+=4;
}
#endif
dd = d + (ii&0x0003);
while(d != dd) {
*d++ = HFC_inb(hc, A_FIFO_DATA0);
}
if (dch) {
/* schedule D-channel event */
skb_queue_tail(&dch->rqueue, skb);
dchannel_sched_event(dch, D_RCVBUFREADY);
}
if (bch) {
/* schedule B-channel event */
skb_queue_tail(&bch->rqueue, skb);
bch_sched_event(bch, B_RCVBUFREADY);
}
}
}
/*********************/
/* Interrupt handler */
/*********************/
static irqreturn_t
hfcmulti_interrupt(int intno, void *dev_id, struct pt_regs *regs)
{
hfc_multi_t *hc = dev_id;
bchannel_t *bch;
dchannel_t *dch;
u_long flags;
u_char r_irq_statech, status, r_irq_misc, r_irq_oview, r_irq_fifo_bl;
int ch;
int i, j;
int temp;
spin_lock_irqsave(&hc->lock.lock, flags);
#ifdef SPIN_DEBUG
hc->lock.spin_adr = (void *)0x3001;
#endif
if (!hc) {
printk(KERN_WARNING "HFC-multi: Spurious interrupt!\n");
irq_notforus:
#ifdef SPIN_DEBUG
hc->lock.spin_adr = NULL;
#endif
spin_unlock_irqrestore(&hc->lock.lock, flags);
return(IRQ_NONE);
}
status = HFC_inb(hc, R_STATUS);
r_irq_statech = HFC_inb(hc, R_IRQ_STATECH);
if (!r_irq_statech && !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA | V_MISC_IRQSTA | V_FR_IRQSTA))) {
/* irq is not for us */
goto irq_notforus;
}
hc->irqcnt++;
if (test_and_set_bit(STATE_FLAG_BUSY, &hc->lock.state)) {
printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n",
__FUNCTION__, hc->lock.state);
#ifdef SPIN_DEBUG
printk(KERN_ERR "%s: previous lock:%p\n",
__FUNCTION__, hc->lock.busy_adr);
#endif
#ifdef LOCK_STATISTIC
hc->lock.irq_fail++;
#endif
} else {
#ifdef LOCK_STATISTIC
hc->lock.irq_ok++;
#endif
#ifdef SPIN_DEBUG
hc->lock.busy_adr = hfcmulti_interrupt;
#endif
}
test_and_set_bit(STATE_FLAG_INIRQ, &hc->lock.state);
#ifdef SPIN_DEBUG
hc->lock.spin_adr= NULL;
#endif
spin_unlock_irqrestore(&hc->lock.lock, flags);
if (r_irq_statech) {
if (hc->type == 1) {
/* state machine */
dch = hc->chan[16].dch;
dch->ph_state = HFC_inb(hc, R_E1_RD_STA) & 0x7;
dchannel_sched_event(dch, D_L1STATECHANGE);
if (debug & DEBUG_HFCMULTI_STATE)
printk(KERN_DEBUG "%s: E1 newstate %x\n", __FUNCTION__, dch->ph_state);
} else {
/* state machine */
ch = 0;
while(ch < 32) {
if ((dch = hc->chan[ch].dch)) {
if (r_irq_statech & 1) {
HFC_outb(hc, R_ST_SEL, hc->chan[ch].port);
dch->ph_state = HFC_inb(hc, A_ST_RD_STATE) & 0x0f;
dchannel_sched_event(dch, D_L1STATECHANGE);
if (debug & DEBUG_HFCMULTI_STATE)
printk(KERN_DEBUG "%s: S/T newstate %x port %d\n", __FUNCTION__, dch->ph_state, hc->chan[ch].port);
}
r_irq_statech >>= 1;
}
ch++;
}
}
}
if (status & V_EXT_IRQSTA) {
/* external IRQ */
}
if (status & V_LOST_STA) {
/* LOST IRQ */
HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */
}
if (status & V_MISC_IRQSTA) {
/* misc IRQ */
r_irq_misc = HFC_inb(hc, R_IRQ_MISC);
if (r_irq_misc & V_TI_IRQ) {
/* -> timer IRQ */
ch = 0;
while(ch < 32) {
if ((dch = hc->chan[ch].dch))
if (hc->created[hc->chan[ch].port]) {
hfcmulti_tx(hc, ch, dch, NULL);
/* fifo is started when switching to rx-fifo */
hfcmulti_rx(hc, ch, dch, NULL);
if (hc->chan[ch].nt_timer > -1) {
if (!(--hc->chan[ch].nt_timer)) {
dchannel_sched_event(dch, D_L1STATECHANGE);
if (debug & DEBUG_HFCMULTI_STATE)
printk(KERN_DEBUG "%s: nt_timer at state %x\n", __FUNCTION__, dch->ph_state);
}
}
}
if ((bch = hc->chan[ch].bch))
if (hc->created[hc->chan[ch].port]) {
if (bch->protocol != ISDN_PID_NONE) {
hfcmulti_tx(hc, ch, NULL, bch);
/* fifo is started when switching to rx-fifo */
hfcmulti_rx(hc, ch, NULL, bch);
}
}
ch++;
}
if (hc->type == 1)
if (hc->created[0]) {
if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[16].cfg)) {
/* LOS */
temp = HFC_inb(hc, R_RX_STA0) & V_SIG_LOS;
if (!temp && hc->chan[16].los)
dchannel_sched_event(dch, D_LOS);
if (temp && !hc->chan[16].los)
dchannel_sched_event(dch, D_LOS_OFF);
hc->chan[16].los = temp;
}
if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[16].cfg)) {
/* AIS */
temp = HFC_inb(hc, R_RX_STA0) & V_AIS;
if (!temp && hc->chan[16].ais)
dchannel_sched_event(dch, D_AIS);
if (!temp && hc->chan[16].ais)
dchannel_sched_event(dch, D_AIS_OFF);
hc->chan[16].ais = temp;
}
if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[16].cfg)) {
/* SLIP */
temp = HFC_inb(hc, R_SLIP) & V_FOSLIP_RX;
if (!temp && hc->chan[16].slip_rx)
dchannel_sched_event(dch, D_SLIP_RX);
hc->chan[16].slip_rx = temp;
temp = HFC_inb(hc, R_SLIP) & V_FOSLIP_TX;
if (!temp && hc->chan[16].slip_tx)
dchannel_sched_event(dch, D_SLIP_TX);
hc->chan[16].slip_tx = temp;
}
temp = HFC_inb(hc, R_JATT_DIR);
switch(!hc->chan[16].sync) {
case 0:
if ((temp&0x60) == 0x60) {
if (debug & DEBUG_HFCMULTI_SYNC)
printk(KERN_DEBUG "%s: E1 now in clock sync\n", __FUNCTION__);
HFC_outb(hc, R_RX_OFF, hc->chan[16].jitter | V_RX_INIT);
HFC_outb(hc, R_TX_OFF, hc->chan[16].jitter | V_RX_INIT);
hc->chan[16].sync = 1;
goto check_framesync;
}
break;
case 1:
if ((temp&0x60) != 0x60) {
if (debug & DEBUG_HFCMULTI_SYNC)
printk(KERN_DEBUG "%s: E1 lost clock sync\n", __FUNCTION__);
hc->chan[16].sync = 0;
break;
}
check_framesync:
temp = HFC_inb(hc, R_RX_STA0);
if (temp == 0x27) {
if (debug & DEBUG_HFCMULTI_SYNC)
printk(KERN_DEBUG "%s: E1 now in frame sync\n", __FUNCTION__);
hc->chan[16].sync = 2;
}
break;
case 2:
if ((temp&0x60) != 0x60) {
if (debug & DEBUG_HFCMULTI_SYNC)
printk(KERN_DEBUG "%s: E1 lost clock & frame sync\n", __FUNCTION__);
hc->chan[16].sync = 0;
break;
}
temp = HFC_inb(hc, R_RX_STA0);
if (temp != 0x27) {
if (debug & DEBUG_HFCMULTI_SYNC)
printk(KERN_DEBUG "%s: E1 lost frame sync\n", __FUNCTION__);
hc->chan[16].sync = 1;
}
break;
}
}
if (hc->leds)
hfcmulti_leds(hc);
}
if (r_irq_misc & V_DTMF_IRQ) {
/* -> DTMF IRQ */
hfcmulti_dtmf(hc);
}
}
if (status & V_FR_IRQSTA) {
/* FIFO IRQ */
r_irq_oview = HFC_inb(hc, R_IRQ_OVIEW);
i = 0;
while(i < 8) {
if (r_irq_oview & (1 << i)) {
r_irq_fifo_bl = HFC_inb(hc, R_IRQ_FIFO_BL0 + i);
j = 0;
while(j < 8) {
ch = (i<<2) + (j>>1);
if (r_irq_fifo_bl & (1 << j)) {
if ((dch = hc->chan[ch].dch))
if (hc->created[hc->chan[ch].port]) {
hfcmulti_tx(hc, ch, dch, NULL);
/* start fifo */
HFC_outb(hc, R_FIFO, 0);
HFC_wait(hc);
}
if ((bch = hc->chan[ch].bch))
if (hc->created[hc->chan[ch].port]) {
if (bch->protocol != ISDN_PID_NONE) {
hfcmulti_tx(hc, ch, NULL, bch);
/* start fifo */
HFC_outb(hc, R_FIFO, 0);
HFC_wait(hc);
}
}
}
j++;
if (r_irq_fifo_bl & (1 << j)) {
if ((dch = hc->chan[ch].dch))
if (hc->created[hc->chan[ch].port]) {
hfcmulti_rx(hc, ch, dch, NULL);
}
if ((bch = hc->chan[ch].bch))
if (hc->created[hc->chan[ch].port]) {
if (bch->protocol != ISDN_PID_NONE) {
hfcmulti_rx(hc, ch, NULL, bch);
}
}
}
j++;
}
}
i++;
}
}
spin_lock_irqsave(&hc->lock.lock, flags);
#ifdef SPIN_DEBUG
hc->lock.spin_adr = (void *)0x3002;
#endif
if (!test_and_clear_bit(STATE_FLAG_INIRQ, &hc->lock.state)) {
}
if (!test_and_clear_bit(STATE_FLAG_BUSY, &hc->lock.state)) {
printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n",
__FUNCTION__, hc->lock.state);
}
#ifdef SPIN_DEBUG
hc->lock.busy_adr = NULL;
hc->lock.spin_adr = NULL;
#endif
spin_unlock_irqrestore(&hc->lock.lock, flags);
return IRQ_HANDLED;
}
/********************************************************************/
/* timer callback for D-chan busy resolution. Currently no function */
/********************************************************************/
static void
hfcmulti_dbusy_timer(hfc_multi_t *hc)
{
}
/***************************************************************/
/* activate/deactivate hardware for selected channels and mode */
/***************************************************************/
/* configure B-channel with the given protocol
* ch eqals to the HFC-channel (0-31)
* ch is the number of channel (0-4,4-7,8-11,12-15,16-19,20-23,24-27,28-31 for S/T, 1-31 for E1)
* the hdlc interrupts will be set/unset
*
*/
static int
mode_hfcmulti(hfc_multi_t *hc, int ch, int protocol, int slot, int dir)
{
int flow = 0, routing = 0;
int oslot = hc->chan[ch].slot;
int conf = hc->chan[ch].conf;
if (debug & DEBUG_HFCMULTI_MODE)
printk(KERN_DEBUG "%s: channel %d protocol %x slot %d dir %d\n", __FUNCTION__, ch, protocol, slot, dir);
if (oslot>=0 && slot!=oslot) {
/* remove from slot */
if (debug & DEBUG_HFCMULTI_MODE)
printk(KERN_DEBUG "%s: remove from slot %d\n", __FUNCTION__, oslot);
HFC_outb(hc, R_SLOT, oslot<<1);
HFC_outb(hc, A_SL_CFG, 0);
HFC_outb(hc, A_CONF, 0);
HFC_outb(hc, R_SLOT, (oslot<<1) | V_SL_DIR);
HFC_outb(hc, A_SL_CFG, 0);
HFC_outb(hc, A_CONF, 0);
}
if (slot < 0) {
/* disable pcm slot */
flow = 0x80; /* FIFO->ST, ST->(FIFO,PCM) */
hc->chan[ch].slot = -1;
} else {
/* set pcm slot */
flow = 0xa0; /* PCM->ST, ST->(FIFO,PCM) */
/* put on slot */
routing = dir?0xc0:0x80;
if (conf || dir>1)
routing = 0x40; /* loop */
if (debug & DEBUG_HFCMULTI_MODE)
printk(KERN_DEBUG "%s: put to slot %d dir %d flow %02x routing %02x conf %d\n", __FUNCTION__, slot, dir, flow, routing, conf);
HFC_outb(hc, R_SLOT, slot<<1);
HFC_outb(hc, A_SL_CFG, (ch<<1) | V_CH_DIR | routing);
HFC_outb(hc, A_CONF, conf);
HFC_outb(hc, R_SLOT, (slot<<1) | V_SL_DIR);
HFC_outb(hc, A_SL_CFG, (ch<<1) | routing);
HFC_outb(hc, A_CONF, 0);
hc->chan[ch].slot = slot;
hc->chan[ch].dir = dir;
}
switch (protocol) {
case (ISDN_PID_NONE):
/* disable TX fifo */
HFC_outb(hc, R_FIFO, ch<<1);
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, flow | 0x00 | 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);
/* disable RX fifo */
HFC_outb(hc, R_FIFO, (ch<<1)|1);
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, flow | 0x00);
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) {
} else if ((ch&0x3)<2) {
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);
HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[hc->chan[ch].port]);
}
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 | 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);
/* enable RX fifo */
HFC_outb(hc, R_FIFO, (ch<<1)|1);
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, flow | 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);
HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[hc->chan[ch].port]);
}
break;
case (ISDN_PID_L1_B_64HDLC): /* B-channel */
case (ISDN_PID_L1_TE_E1): /* D-channel E1 */
case (ISDN_PID_L1_NT_E1):
/* enable TX fifo */
HFC_outb(hc, R_FIFO, ch<<1);
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, flow | 0x04);
HFC_outb(hc, A_SUBCH_CFG, 0);
HFC_outb(hc, A_IRQ_MSK, V_IRQ);
HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
HFC_wait(hc);
/* enable RX fifo */
HFC_outb(hc, R_FIFO, (ch<<1)|1);
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, flow | 0x04);
HFC_outb(hc, A_SUBCH_CFG, 0);
HFC_outb(hc, A_IRQ_MSK, V_IRQ);
HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
HFC_wait(hc);
if (hc->type == 1) {
} else {
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);
HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[hc->chan[ch].port]);
}
break;
case (ISDN_PID_L1_TE_S0): /* D-channel S0 */
case (ISDN_PID_L1_NT_S0):
/* enable TX fifo */
HFC_outb(hc, R_FIFO, ch<<1);
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, flow | 0x04 | V_IFF);
HFC_outb(hc, A_SUBCH_CFG, 2);
HFC_outb(hc, A_IRQ_MSK, V_IRQ);
HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
HFC_wait(hc);
/* enable RX fifo */
HFC_outb(hc, R_FIFO, (ch<<1)|1);
HFC_wait(hc);
HFC_outb(hc, A_CON_HDLC, flow | 0x04);
HFC_outb(hc, A_SUBCH_CFG, 2);
HFC_outb(hc, A_IRQ_MSK, V_IRQ);
HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
HFC_wait(hc);
break;
default:
printk(KERN_DEBUG "%s: protocol not known %x\n", __FUNCTION__, protocol);
hc->chan[ch].protocol = ISDN_PID_NONE;
return(-ENOPROTOOPT);
}
hc->chan[ch].protocol = protocol;
return(0);
}
/**************************/
/* connect/disconnect PCM */
/**************************/
static void
hfcmulti_pcm(hfc_multi_t *hc, int ch, int slot, int dir)
{
if (slot < 0) {
/* disable PCM */
mode_hfcmulti(hc, ch, hc->chan[ch].protocol, -1, 0);
return;
}
/* enable pcm */
mode_hfcmulti(hc, ch, hc->chan[ch].protocol, slot, dir);
}
/**************************/
/* set/disable conference */
/**************************/
static void
hfcmulti_conf(hfc_multi_t *hc, int ch, int num)
{
if (num)
hc->chan[ch].conf = V_CONF_EN | num;
else
hc->chan[ch].conf = 0;
mode_hfcmulti(hc, ch, hc->chan[ch].protocol, hc->chan[ch].slot, hc->chan[ch].dir);
}
/*************************************/
/* Layer 1 D-channel hardware access */
/*************************************/
/* message transfer from layer 1 to hardware.
*/
static int
hfcmulti_l1hw(mISDNif_t *hif, struct sk_buff *skb)
{
dchannel_t *dch;
hfc_multi_t *hc;
int ret = -EINVAL;
mISDN_head_t *hh;
if (!hif || !skb)
return(ret);
hh = mISDN_HEAD_P(skb);
dch = hif->fdata;
hc = dch->inst.data;
ret = 0;
if (hh->prim == PH_DATA_REQ) {
/* check oversize */
if (skb->len == 0) {
printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__);
return(-EINVAL);
}
if (skb->len > MAX_DFRAME_LEN_L1) {
printk(KERN_WARNING "%s: skb too large\n", __FUNCTION__);
return(-EINVAL);
}
/* check for pending next_skb */
if (dch->next_skb) {
printk(KERN_WARNING "%s: next_skb exist ERROR\n", __FUNCTION__);
return(-EBUSY);
}
/* if we have currently a pending tx skb */
dch->inst.lock(dch->inst.data, 0);
if (test_and_set_bit(FLG_TX_BUSY, &dch->DFlags)) {
test_and_set_bit(FLG_TX_NEXT, &dch->DFlags);
dch->inst.unlock(dch->inst.data);
dch->next_skb = skb;
return(0);
}
/* write to fifo */
dch->tx_len = skb->len;
memcpy(dch->tx_buf, skb->data, dch->tx_len);
// if (debug & DEBUG_HFCMULTI_FIFO) {
// printk(KERN_DEBUG "%s:from user space:\n", __FUNCTION__);
// i = 0;
// while(i < dch->tx_len)
// printk(" %02x", dch->tx_buf[i++]);
// printk("\n");
// }
dch->tx_idx = 0;
hfcmulti_tx(hc, dch->channel, dch, NULL);
/* start fifo */
HFC_outb(hc, R_FIFO, 0);
HFC_wait(hc);
dch->inst.unlock(dch->inst.data);
skb_trim(skb, 0);
return(if_newhead(&dch->inst.up, PH_DATA_CNF,
hh->dinfo, skb));
} else
if (hh->prim == (PH_SIGNAL | REQUEST)) {
dch->inst.lock(dch->inst.data, 0);
switch (hh->dinfo) {
#if 0
case INFO3_P8:
case INFO3_P10:
if (debug & DEBUG_HFCMULTI_MSG)
printk(KERN_DEBUG "%s: INFO3_P%d\n", __FUNCTION__, (hh->dinfo==INFO3_P8)?8:10);
if (test_bit(HFC_CHIP_MASTER, &hc->chip))
hc->hw.mst_m |= HFCPCI_MASTER;
Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
break;
#endif
default:
printk(KERN_DEBUG "%s: unknown PH_SIGNAL info %x\n", __FUNCTION__, hh->dinfo);
ret = -EINVAL;
}
dch->inst.unlock(dch->inst.data);
} else
if (hh->prim == (PH_CONTROL | REQUEST)) {
dch->inst.lock(dch->inst.data, 0);
switch (hh->dinfo) {
case HW_DEACTIVATE:
if (debug & DEBUG_HFCMULTI_MSG)
printk(KERN_DEBUG "%s: HW_DEACTIVATE\n", __FUNCTION__);
goto hw_deactivate; /* after lock */
default:
printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo);
ret = -EINVAL;
}
dch->inst.unlock(dch->inst.data);
} else
if (hh->prim == (PH_ACTIVATE | REQUEST)) {
if (test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)) {
if (debug & DEBUG_HFCMULTI_MSG)
printk(KERN_DEBUG "%s: PH_ACTIVATE port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->type-1);
dch->inst.lock(dch->inst.data, 0);
/* start activation */
if (hc->type == 1) {
HFC_outb(hc, R_E1_WR_STA, V_E1_LD_STA | 1);
udelay(6); /* wait at least 5,21us */
HFC_outb(hc, R_E1_WR_STA, 1);
} else {
HFC_outb(hc, R_ST_SEL, hc->chan[dch->channel].port);
HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1); /* G1 */
udelay(6); /* wait at least 5,21us */
HFC_outb(hc, A_ST_WR_STATE, 1);
HFC_outb(hc, A_ST_WR_STATE, 1 | (V_ST_ACT*3)); /* activate */
}
dch->ph_state = 1;
dch->inst.unlock(dch->inst.data);
} else {
if (debug & DEBUG_HFCMULTI_MSG)
printk(KERN_DEBUG "%s: PH_ACTIVATE no NT-mode port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->type-1);
ret = -EINVAL;
}
} else
if (hh->prim == (PH_DEACTIVATE | REQUEST)) {
if (test_bit(HFC_CFG_NTMODE, &hc->chan[dch->channel].cfg)) {
if (debug & DEBUG_HFCMULTI_MSG)
printk(KERN_DEBUG "%s: PH_DEACTIVATE port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->type-1);
dch->inst.lock(dch->inst.data, 0);
hw_deactivate: /* after lock */
dch->ph_state = 0;
/* start deactivation */
if (hc->type == 1) {
#warning COLOGNE: wie schaltet man beim E1 die statemachine auf ACT / DACT ?
} else {
HFC_outb(hc, R_ST_SEL, hc->chan[dch->channel].port);
HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT*2); /* deactivate */
}
discard_queue(&dch->rqueue);
if (dch->next_skb) {
dev_kfree_skb(dch->next_skb);
dch->next_skb = NULL;
}
dch->tx_idx = dch->tx_len = hc->chan[dch->channel].rx_idx = 0;
test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags);
test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
del_timer(&dch->dbusytimer);
if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
dchannel_sched_event(dch, D_CLEARBUSY);
dch->inst.unlock(dch->inst.data);
} else {
if (debug & DEBUG_HFCMULTI_MSG)
printk(KERN_DEBUG "%s: PH_DEACTIVATE no NT-mode port %d (0..%d)\n", __FUNCTION__, hc->chan[dch->channel].port, hc->type-1);
ret = -EINVAL;
}
} else {
if (debug & DEBUG_HFCMULTI_MSG)
printk(KERN_DEBUG "%s: unknown prim %x\n", __FUNCTION__, hh->prim);
ret = -EINVAL;
}
if (!ret)
dev_kfree_skb(skb);
return(ret);
}
/******************************/
/* Layer2 -> Layer 1 Transfer */
/******************************/
/* messages from layer 2 to layer 1 are processed here.
*/
static int
hfcmulti_l2l1(mISDNif_t *hif, struct sk_buff *skb)
{
u_long num, dir;
bchannel_t *bch;
int ret = -EINVAL;
mISDN_head_t *hh;
hfc_multi_t *hc;
if (!hif || !skb)
return(ret);
hh = mISDN_HEAD_P(skb);
bch = hif->fdata;
hc = bch->inst.data;
if ((hh->prim == PH_DATA_REQ)
|| (hh->prim == (DL_DATA | REQUEST))) {
if (skb->len == 0) {
printk(KERN_WARNING "%s: skb too small\n", __FUNCTION__);
return(-EINVAL);
}
if (skb->len > MAX_DATA_MEM) {
printk(KERN_WARNING "%s: skb too large\n", __FUNCTION__);
return(-EINVAL);
}
/* check for pending next_skb */
if (bch->next_skb) {
printk(KERN_WARNING "%s: next_skb exist ERROR\n", __FUNCTION__);
return(-EBUSY);
}
/* if we have currently a pending tx skb */
bch->inst.lock(bch->inst.data, 0);
if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) {
test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag);
bch->inst.unlock(bch->inst.data);
bch->next_skb = skb;
return(0);
}
/* write to fifo */
bch->tx_len = skb->len;
memcpy(bch->tx_buf, skb->data, bch->tx_len);
bch->tx_idx = 0;
hfcmulti_tx(hc, bch->channel, NULL, bch);
/* start fifo */
HFC_outb(hc, R_FIFO, 0);
HFC_wait(hc);
bch->inst.unlock(bch->inst.data);
if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
&& bch->dev)
hif = &bch->dev->rport.pif;
else
hif = &bch->inst.up;
skb_trim(skb, 0);
return(if_newhead(hif, hh->prim | CONFIRM,
hh->dinfo, skb));
} else
if ((hh->prim == (PH_ACTIVATE | REQUEST))
|| (hh->prim == (DL_ESTABLISH | REQUEST))) {
/* activate B-channel if not already activated */
if (debug & DEBUG_HFCMULTI_MSG)
printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n", __FUNCTION__, bch->channel);
if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag))
ret = 0;
else {
bch->inst.lock(bch->inst.data, 0);
ret = mode_hfcmulti(bch->inst.data, bch->channel,
bch->inst.pid.protocol[1], hc->chan[bch->channel].slot, hc->chan[bch->channel].dir);
if (!ret) {
bch->protocol = bch->inst.pid.protocol[1];
if (bch->protocol)
bch_sched_event(bch, B_XMTBUFREADY);
if (bch->protocol==ISDN_PID_L1_B_64TRANS && !hc->dtmf) {
/* start decoder */
hc->dtmf = 1;
if (debug & DEBUG_HFCMULTI_DTMF)
printk(KERN_DEBUG "%s: start dtmf decoder\n", __FUNCTION__);
HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF);
}
}
bch->inst.unlock(bch->inst.data);
}
if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
if (bch->dev)
if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0);
skb_trim(skb, 0);
return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, ret, skb));
} else
if ((hh->prim == (PH_DEACTIVATE | REQUEST))
|| (hh->prim == (DL_RELEASE | REQUEST))
|| (hh->prim == (MGR_DISCONNECT | REQUEST))) {
if (debug & DEBUG_HFCMULTI_MSG)
printk(KERN_DEBUG "%s: PH_DEACTIVATE ch %d (0..32)\n", __FUNCTION__, bch->channel);
/* deactivate B-channel if not already deactivated */
bch->inst.lock(bch->inst.data, 0);
if (bch->next_skb) {
test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag);
dev_kfree_skb(bch->next_skb);
bch->next_skb = NULL;
}
bch->tx_idx = bch->tx_len = bch->rx_idx = 0;
test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
mode_hfcmulti(bch->inst.data, bch->channel, ISDN_PID_NONE, hc->chan[bch->channel].slot, hc->chan[bch->channel].dir);
bch->protocol = ISDN_PID_NONE;
test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag);
bch->inst.unlock(bch->inst.data);
skb_trim(skb, 0);
if (hh->prim != (MGR_DISCONNECT | REQUEST)) {
if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
if (bch->dev)
if_link(&bch->dev->rport.pif, hh->prim | CONFIRM, 0, 0, NULL, 0);
if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, 0, skb))
return(0);
}
ret = 0;
} else
if (hh->prim == (PH_CONTROL | REQUEST)) {
bch->inst.lock(bch->inst.data, 0);
switch (hh->dinfo) {
/* connect interface to pcm timeslot (0..N) */
case HW_PCM_CONN:
if (skb->len < 2*sizeof(u_long)) {
printk(KERN_WARNING "%s: HW_PCM_CONN lacks parameters\n", __FUNCTION__);
break;
}
num = ((u_long *)skb->data)[0];
dir = ((u_long *)skb->data)[1];
if (debug & DEBUG_HFCMULTI_MSG)
printk(KERN_DEBUG "%s: HW_PCM_CONN slot %ld dir %ld\n", __FUNCTION__, num, dir);
if (num<=hc->slots && dir<=2)
hfcmulti_pcm(hc, bch->channel, num, dir);
else
printk(KERN_WARNING "%s: HW_PCM_CONN slot %ld dir %ld out of range\n", __FUNCTION__, num, dir);
break;
/* release interface from pcm timeslot */
case HW_PCM_DISC:
if (debug & DEBUG_HFCMULTI_MSG)
printk(KERN_DEBUG "%s: HW_PCM_DISC\n", __FUNCTION__);
hfcmulti_pcm(hc, bch->channel, -1, -1);
break;
/* join conference (0..7) */
case HW_CONF_JOIN:
if (skb->len < sizeof(u_long)) {
printk(KERN_WARNING "%s: HW_CONF_JOIN lacks parameters\n", __FUNCTION__);
break;
}
num = ((u_long *)skb->data)[0];
if (debug & DEBUG_HFCMULTI_MSG)
printk(KERN_DEBUG "%s: HW_CONF_JOIN conf %ld\n", __FUNCTION__, num);
if (num <= 7)
hfcmulti_conf(hc, bch->channel, num);
else
printk(KERN_WARNING "%s: HW_CONF_JOIN conf %ld out of range\n", __FUNCTION__, num);
break;
/* split conference */
case HW_CONF_SPLIT:
if (debug & DEBUG_HFCMULTI_MSG)
printk(KERN_DEBUG "%s: HW_CONF_SPLIT\n", __FUNCTION__);
hfcmulti_conf(hc, bch->channel, -1);
break;
default:
printk(KERN_DEBUG "%s: unknown PH_CONTROL info %x\n", __FUNCTION__, hh->dinfo);
ret = -EINVAL;
}
bch->inst.unlock(bch->inst.data);
} else {
printk(KERN_WARNING "%s: unknown prim(%x)\n", __FUNCTION__, hh->prim);
ret = -EINVAL;
}
if (!ret)
dev_kfree_skb(skb);
return(ret);
}
/***************************/
/* handle D-channel events */
/***************************/
/* handle state change event
*/
static void
hfcmulti_dch_bh(dchannel_t *dch)
{
hfc_multi_t *hc = dch->inst.data;
u_int prim = PH_SIGNAL | INDICATION;
u_int para = 0;
mISDNif_t *upif = &dch->inst.up;
mISDN_head_t *hh;
int ch;
struct sk_buff *skb;
if (!dch) {
printk(KERN_WARNING "%s: ERROR given dch is NULL\n", __FUNCTION__);
return;
}
ch = dch->channel;
/* Loss Of Signal */
if (test_and_clear_bit(D_LOS, &dch->event)) {
if (debug & DEBUG_HFCMULTI_STATE)
printk(KERN_DEBUG "%s: LOS detected\n", __FUNCTION__);
skb = create_link_skb(PH_CONTROL | INDICATION, HW_LOS, 0, NULL, 0);
if (skb) {
hh = mISDN_HEAD_P(skb);
if (if_newhead(upif, hh->prim, hh->dinfo, skb))
dev_kfree_skb(skb);
}
}
/* Loss Of Signal OFF */
if (test_and_clear_bit(D_LOS_OFF, &dch->event)) {
if (debug & DEBUG_HFCMULTI_STATE)
printk(KERN_DEBUG "%s: LOS gone\n", __FUNCTION__);
skb = create_link_skb(PH_CONTROL | INDICATION, HW_LOS_OFF, 0, NULL, 0);
if (skb) {
hh = mISDN_HEAD_P(skb);
if (if_newhead(upif, hh->prim, hh->dinfo, skb))
dev_kfree_skb(skb);
}
}
/* Alarm Indication Signal */
if (test_and_clear_bit(D_AIS, &dch->event)) {
if (debug & DEBUG_HFCMULTI_STATE)
printk(KERN_DEBUG "%s: AIS detected\n", __FUNCTION__);
skb = create_link_skb(PH_CONTROL | INDICATION, HW_AIS, 0, NULL, 0);
if (skb) {
hh = mISDN_HEAD_P(skb);
if (if_newhead(upif, hh->prim, hh->dinfo, skb))
dev_kfree_skb(skb);
}
}
/* Alarm Indication Signal OFF */
if (test_and_clear_bit(D_AIS_OFF, &dch->event)) {
if (debug & DEBUG_HFCMULTI_STATE)
printk(KERN_DEBUG "%s: AIS gone\n", __FUNCTION__);
skb = create_link_skb(PH_CONTROL | INDICATION, HW_AIS_OFF, 0, NULL, 0);
if (skb) {
hh = mISDN_HEAD_P(skb);
if (if_newhead(upif, hh->prim, hh->dinfo, skb))
dev_kfree_skb(skb);
}
}
/* SLIP detected */
if (test_and_clear_bit(D_SLIP_TX, &dch->event)) {
if (debug & DEBUG_HFCMULTI_STATE)
printk(KERN_DEBUG "%s: bit SLIP detected TX\n", __FUNCTION__);
skb = create_link_skb(PH_CONTROL | INDICATION, HW_SLIP_TX, 0, NULL, 0);
if (skb) {
hh = mISDN_HEAD_P(skb);
if (if_newhead(upif, hh->prim, hh->dinfo, skb))
dev_kfree_skb(skb);
}
}
if (test_and_clear_bit(D_SLIP_RX, &dch->event)) {
if (debug & DEBUG_HFCMULTI_STATE)
printk(KERN_DEBUG "%s: bit SLIP detected RX\n", __FUNCTION__);
skb = create_link_skb(PH_CONTROL | INDICATION, HW_SLIP_RX, 0, NULL, 0);
if (skb) {
hh = mISDN_HEAD_P(skb);
if (if_newhead(upif, hh->prim, hh->dinfo, skb))
dev_kfree_skb(skb);
}
}
if (test_and_clear_bit(D_L1STATECHANGE, &dch->event)) {
if (!test_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg)) {
if (debug & DEBUG_HFCMULTI_STATE)
printk(KERN_DEBUG "%s: TE newstate %x\n", __FUNCTION__, dch->ph_state);
switch (dch->ph_state) {
case (0):
prim = PH_CONTROL | INDICATION;
para = HW_RESET;
break;
case (3):
prim = PH_CONTROL | INDICATION;
para = HW_DEACTIVATE;
break;
case (5):
case (8):
para = ANYSIGNAL;
break;
case (6):
para = INFO2;
break;
case (7):
para = INFO4_P8;
break;
default:
return;
}
} else {
if (debug & DEBUG_HFCMULTI_STATE)
printk(KERN_DEBUG "%s: NT newstate %x\n", __FUNCTION__, dch->ph_state);
dch->inst.lock(dch->inst.data, 0);
switch (dch->ph_state) {
case (2):
if (hc->chan[ch].nt_timer == 0) {
hc->chan[ch].nt_timer = -1;
HFC_outb(hc, R_ST_SEL, hc->chan[ch].port);
HFC_outb(hc, A_ST_WR_STATE, 4 | V_ST_LD_STA); /* G4 */
udelay(6); /* wait at least 5,21us */
HFC_outb(hc, A_ST_WR_STATE, 4);
dch->ph_state = 4;
} else {
/* one extra count for the next event */
hc->chan[ch].nt_timer = nt_t1_count[poll_timer] + 1;
HFC_outb(hc, R_ST_SEL, hc->chan[ch].port);
HFC_outb(hc, A_ST_WR_STATE, 2 | V_SET_G2_G3); /* allow G2 -> G3 transition */
}
upif = NULL;
break;
case (1):
prim = PH_DEACTIVATE | INDICATION;
para = 0;
hc->chan[ch].nt_timer = -1;
break;
case (4):
hc->chan[ch].nt_timer = -1;
upif = NULL;
break;
case (3):
prim = PH_ACTIVATE | INDICATION;
para = 0;
hc->chan[ch].nt_timer = -1;
break;
default:
break;
}
dch->inst.unlock(dch->inst.data);
}
/* transmit new state to upper layer if available */
if (hc->created[hc->chan[ch].port]) {
while(upif) {
if_link(upif, prim, para, 0, NULL, 0);
upif = upif->next;
}
}
}
}
/***************************/
/* handle D-channel events */
/***************************/
/* handle DTMF event
*/
static void
hfcmulti_bch_bh(bchannel_t *bch)
{
hfc_multi_t *hc = bch->inst.data;
struct sk_buff *skb;
mISDN_head_t *hh;
mISDNif_t *hif = 0; /* make gcc happy */
u_long *coeff;
if (!bch) {
printk(KERN_WARNING "%s: ERROR given bch is NULL\n", __FUNCTION__);
return;
}
/* DTMF event */
if (test_and_clear_bit(B_DTMFREADY, &bch->event)) {
if ((skb = hc->chan[bch->channel].dtmf_skb)) {
if (debug & DEBUG_HFCMULTI_DTMF) {
coeff = (u_long *)skb->data;
printk("%s: DTMF ready %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx len=%d\n", __FUNCTION__,
coeff[0], coeff[1], coeff[2], coeff[3], coeff[4], coeff[5], coeff[6], coeff[7], skb->len);
}
hc->chan[bch->channel].dtmf_skb = NULL;
hh = mISDN_HEAD_P(skb);
if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV) && bch->dev)
hif = &bch->dev->rport.pif;
else
hif = &bch->inst.up;
if (if_newhead(hif, hh->prim, hh->dinfo, skb))
dev_kfree_skb(skb);
}
}
}
/*************************************/
/* called for card mode init message */
/*************************************/
static void
hfcmulti_initmode(hfc_multi_t *hc)
{
int nt_mode;
unsigned char r_sci_msk, a_st_wr_state, r_e1_wr_sta;
int i, port;
dchannel_t *dch;
if (debug & DEBUG_HFCMULTI_INIT)
printk("%s: entered\n", __FUNCTION__);
lock_dev(hc, 0);
if (hc->type == 1) {
nt_mode = test_bit(HFC_CFG_NTMODE, &hc->chan[16].cfg);
hc->chan[16].slot = -1;
mode_hfcmulti(hc, 16, nt_mode?ISDN_PID_L1_NT_E1:ISDN_PID_L1_TE_E1, -1, 0);
hc->chan[16].dch->hw_bh = hfcmulti_dch_bh;
hc->chan[16].dch->dbusytimer.function = (void *) hfcmulti_dbusy_timer;
hc->chan[16].dch->dbusytimer.data = (long) &hc->chan[16].dch;
init_timer(&hc->chan[16].dch->dbusytimer);
i = 0;
while (i < 30) {
hc->chan[i+1+(i>=15)].slot = -1;
hc->chan[i+1+(i>=15)].bch->hw_bh = hfcmulti_bch_bh;
mode_hfcmulti(hc, i+1+(i>=15), ISDN_PID_NONE, -1, 0);
hc->chan[i+1+(i>=15)].bch->protocol = ISDN_PID_NONE;
i++;
}
} else {
i = 0;
while (i < hc->type) {
nt_mode = test_bit(HFC_CFG_NTMODE, &hc->chan[(i<<2)+2].cfg);
hc->chan[(i<<2)+2].slot = -1;
mode_hfcmulti(hc, (i<<2)+2, nt_mode?ISDN_PID_L1_NT_S0:ISDN_PID_L1_TE_S0, -1, 0);
hc->chan[(i<<2)+2].dch->hw_bh = hfcmulti_dch_bh;
hc->chan[(i<<2)+2].dch->dbusytimer.function = (void *) hfcmulti_dbusy_timer;
hc->chan[(i<<2)+2].dch->dbusytimer.data = (long) &hc->chan[(i<<2)+2].dch;
init_timer(&hc->chan[(i<<2)+2].dch->dbusytimer);
hc->chan[i<<2].bch->hw_bh = hfcmulti_bch_bh;
hc->chan[i<<2].slot = -1;
mode_hfcmulti(hc, i<<2, ISDN_PID_NONE, -1, 0);
hc->chan[i<<2].bch->protocol = ISDN_PID_NONE;
hc->chan[(i<<2)+1].bch->hw_bh = hfcmulti_bch_bh;
hc->chan[(i<<2)+1].slot = -1;
mode_hfcmulti(hc, (i<<2)+1, ISDN_PID_NONE, -1, 0);
hc->chan[(i<<2)+1].bch->protocol = ISDN_PID_NONE;
i++;
}
}
/* set up interface */
if (hc->type != 1) {
/* ST */
r_sci_msk = 0;
i = 0;
while(i < 32) {
if (!(dch = hc->chan[i].dch)) {
i++;
continue;
}
port = hc->chan[i].port;
/* select interface */
HFC_outb(hc, R_ST_SEL, port);
if (test_bit(HFC_CFG_NTMODE, &hc->chan[i].cfg)) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: ST port %d is NT-mode\n", __FUNCTION__, port);
/* clock delay */
HFC_outb(hc, A_ST_CLK_DLY, CLKDEL_NT | 0x60);
a_st_wr_state = 1; /* G1 */
hc->hw.a_st_ctrl0[port] = V_ST_MD;
} else {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: ST port %d is TE-mode\n", __FUNCTION__, port);
/* clock delay */
HFC_outb(hc, A_ST_CLK_DLY, CLKDEL_TE);
a_st_wr_state = 2; /* F2 */
hc->hw.a_st_ctrl0[port] = 0;
}
if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg)) {
hc->hw.a_st_ctrl0[port] |= V_TX_LI;
}
/* line setup */
HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[port]);
/* disable E-channel */
if (test_bit(HFC_CFG_NTMODE, &hc->chan[i].cfg)
|| test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg))
HFC_outb(hc, A_ST_CTRL1, V_E_IGNO);
/* enable B-channel receive */
HFC_outb(hc, A_ST_CTRL2, V_B1_RX_EN | V_B2_RX_EN);
/* state machine setup */
HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state | V_ST_LD_STA);
udelay(6); /* wait at least 5,21us */
HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state);
r_sci_msk |= 1 << port;
i++;
}
/* state machine interrupts */
HFC_outb(hc, R_SCI_MSK, r_sci_msk);
} else {
/* E1 */
if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[16].cfg)) {
HFC_outb(hc, R_LOS0, 255); /* 2 ms */
HFC_outb(hc, R_LOS1, 255); /* 512 ms */
}
if (test_bit(HFC_CFG_OPTICAL, &hc->chan[16].cfg)) {
HFC_outb(hc, R_RX0, 0);
HFC_outb(hc, R_TX0, 0 | V_OUT_EN);
} else {
HFC_outb(hc, R_RX0, 1);
HFC_outb(hc, R_TX0, 1 | V_OUT_EN);
}
HFC_outb(hc, R_TX1, V_ATX | V_NTRI);
HFC_outb(hc, R_TX_FR0, 0x00);
HFC_outb(hc, R_TX_FR1, 0xf8);
HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E);
HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0);
HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC);
if (test_bit(HFC_CFG_NTMODE, &hc->chan[(i<<2)+2].cfg)) {
/* NT mode */
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: E1 port LT-mode\n", __FUNCTION__);
r_e1_wr_sta = 1; /* G1 */
HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS | V_PCM_SYNC);
} else {
/* TE mode */
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: E1 port is TE-mode\n", __FUNCTION__);
r_e1_wr_sta = 2; /* F2 */
HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS);
}
HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */
HFC_outb(hc, R_SYNC_OUT, V_IPATS0 | V_IPATS1 | V_IPATS2);
HFC_outb(hc, R_PWM_MD, V_PWM0_MD);
HFC_outb(hc, R_PWM0, 0x50);
HFC_outb(hc, R_PWM1, 0xff);
/* state machine setup */
HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta | V_E1_LD_STA);
udelay(6); /* wait at least 5,21us */
HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta);
}
/* set interrupts & global interrupt */
hc->hw.r_irq_ctrl = V_FIFO_IRQ | V_GLOB_IRQ_EN;
unlock_dev(hc);
if (debug & DEBUG_HFCMULTI_INIT)
printk("%s: done\n", __FUNCTION__);
}
/***********************/
/* initialize the card */
/***********************/
/* start timer irq, wait some time and check if we have interrupts.
* if not, reset chip and try again.
*/
static int
init_card(hfc_multi_t *hc)
{
int cnt = 1; /* as long as there is no trouble */
int err = -EIO;
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: entered\n", __FUNCTION__);
lock_dev(hc, 0);
if (request_irq(hc->pci_dev->irq, hfcmulti_interrupt, SA_SHIRQ, "HFC-multi", hc)) {
printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n", hc->pci_dev->irq);
unlock_dev(hc);
return(-EIO);
}
//printk(KERN_DEBUG "INIT_CARD hc->pci_io = %lx\n", hc->pci_io);
hc->irq = hc->pci_dev->irq;
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: IRQ %d count %d\n", __FUNCTION__, hc->irq, hc->irqcnt);
while (cnt) {
if ((err = init_chip(hc)))
goto error;
/* Finally enable IRQ output
* this is only allowed, if an IRQ routine is allready
* established for this HFC, so don't do that earlier
*/
unlock_dev(hc);
//arning ********************
HFC_outb(hc, R_IRQ_CTRL, V_GLOB_IRQ_EN);
//printk(KERN_DEBUG "jollydebug: a\n");
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout((100*HZ)/1000); /* Timeout 100ms */
/* turn IRQ off until chip is completely initialized */
HFC_outb(hc, R_IRQ_CTRL, 0);
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: IRQ %d count %d\n", __FUNCTION__, hc->irq, hc->irqcnt);
//printk(KERN_DEBUG "jollydebug: b\n");
if (hc->irqcnt) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: done\n", __FUNCTION__);
return(0);
}
lock_dev(hc, 0);
printk(KERN_WARNING "HFC PCI: IRQ(%d) getting no interrupts during init (try %d)\n", hc->irq, cnt);
cnt--;
err = -EIO;
}
error:
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_WARNING "%s: free irq %d\n", __FUNCTION__, hc->irq);
free_irq(hc->irq, hc);
hc->irq = 0;
unlock_dev(hc);
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: done (err=%d)\n", __FUNCTION__, err);
return(err);
}
/*********************************************************/
/* select free channel and return OK(0), -EBUSY, -EINVAL */
/*********************************************************/
static int
SelFreeBChannel(hfc_multi_t *hc, int ch, channel_info_t *ci)
{
bchannel_t *bch;
hfc_multi_t *hfc;
mISDNstack_t *bst;
int cnr;
int port = hc->chan[ch].port;
if (port < 0 || port>=hc->type) {
printk(KERN_WARNING "%s: port(%d) out of range", __FUNCTION__, port);
return(-EINVAL);
}
if (!ci)
return(-EINVAL);
ci->st.p = NULL;
bst = hc->chan[ch].dch->inst.st->child;
cnr=0;
while(bst) {
if (cnr == ((hc->type==1)?30:2)) /* 30 or 2 B-channels */ {
printk(KERN_WARNING "%s: fatal error: more b-stacks than ports", __FUNCTION__);
return(-EINVAL);
}
if(!bst->mgr) {
int_errtxt("no mgr st(%p)", bst);
return(-EINVAL);
}
hfc = bst->mgr->data;
if (!hfc) {
int_errtxt("no mgr->data st(%p)", bst);
return(-EINVAL);
}
if (hc->type==1)
bch = hc->chan[cnr + 1 + (cnr>=15)].bch;
else
bch = hc->chan[(port<<2) + cnr].bch;
if (!(ci->channel & (~CHANNEL_NUMBER))) {
/* only number is set */
if ((ci->channel & 0x3) == (cnr + 1)) {
if (bch->protocol != ISDN_PID_NONE)
return(-EBUSY);
ci->st.p = bst;
return(0);
}
}
if ((ci->channel & (~CHANNEL_NUMBER)) == 0x00a18300) {
if (bch->protocol == ISDN_PID_NONE) {
ci->st.p = bst;
return(0);
}
}
cnr++;
bst = bst->next;
}
return(-EBUSY);
}
/*********************************/
/* find pci device and set it up */
/*********************************/
/* this variable is used as card index when more than one cards are present */
static struct pci_dev *dev_hfcmulti = NULL;
static int
setup_pci(hfc_multi_t *hc)
{
int i;
struct pci_dev *tmp_hfcmulti = NULL;
/* go into 0-state (we might already be due to zero-filled-object */
i = 0;
while(i < 32) {
if (hc->chan[i].dch)
hc->chan[i].dch->ph_state = 0;
i++;
}
/* loop all card ids */
i = 0;
while (id_list[i].vendor_id) {
// printk(KERN_DEBUG "setup_pci(): investigating card entry %d\n", i);
/* only the given type is searched */
if (id_list[i].type != hc->type) {
i++;
continue;
}
tmp_hfcmulti = pci_find_subsys(id_list[i].vendor_id, id_list[i].device_id, id_list[i].vendor_sub, id_list[i].device_sub, dev_hfcmulti);
if (tmp_hfcmulti) {
break;
}
i++;
}
if (!tmp_hfcmulti) {
printk(KERN_WARNING "HFC-multi: No PCI card found\n");
return (-ENODEV);
}
/* found a card */
printk(KERN_INFO "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", id_list[i].vendor_name, id_list[i].card_name, (id_list[i].clock2)?"double":"normal");
dev_hfcmulti = tmp_hfcmulti; /* old device */
hc->pci_dev = dev_hfcmulti;
if (id_list[i].clock2)
test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip);
if (hc->pci_dev->irq <= 0) {
printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n");
return (-EIO);
}
if (pci_enable_device(dev_hfcmulti)) {
printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n");
return (-EIO);
}
hc->pci_io = (char *) get_pcibase(dev_hfcmulti, 1);
if (!hc->pci_io) {
printk(KERN_WARNING "HFC-multi: No IO-Mem for PCI card found\n");
return (-EIO);
}
hc->pci_io = ioremap((ulong) hc->pci_io, 256);
hc->leds = id_list[i].leds;
printk(KERN_INFO "%s: defined at IO %#x IRQ %d HZ %d leds-type %d\n", hc->name, (u_int) hc->pci_io, hc->pci_dev->irq, HZ, hc->leds);
pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO);
switch(hc->leds) {
case 1: /* eval */
break;
case 2: /* OEM */
HFC_outb(hc, R_GPIO_SEL, 0xf0);
HFC_outb(hc, R_GPIO_EN1, 0xff);
HFC_outb(hc, R_GPIO_OUT1, 0x00);
break;
}
/* At this point the needed PCI config is done */
/* fifos are still not enabled */
lock_dev(hc, 0);
#ifdef SPIN_DEBUG
printk(KERN_ERR "spin_lock_adr=%p now(%p)\n", &hc->lock.spin_adr, hc->lock.spin_adr);
printk(KERN_ERR "busy_lock_adr=%p now(%p)\n", &hc->lock.busy_adr, hc->lock.busy_adr);
#endif
unlock_dev(hc);
return (0);
}
/*******************************************
* remove port or complete card from stack *
*******************************************/
static void
release_port(hfc_multi_t *hc, int port)
{
int i = 0;
int all = 1, any = 0;
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: entered\n", __FUNCTION__);
#ifdef LOCK_STATISTIC
printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n", hc->lock.try_ok, hc->lock.try_wait, hc->lock.try_mult, hc->lock.try_inirq);
printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n",
hc->lock.irq_ok, hc->lock.irq_fail);
#endif
if (port >= hc->type) {
printk(KERN_WARNING "%s: ERROR port out of range (%d).\n", __FUNCTION__, port);
return;
}
// if (debug & DEBUG_HFCMULTI_INIT)
// printk(KERN_DEBUG "%s: before lock_dev\n", __FUNCTION__);
lock_dev(hc, 0);
// if (debug & DEBUG_HFCMULTI_INIT)
// printk(KERN_DEBUG "%s: after lock_dev\n", __FUNCTION__);
if (port > -1) {
i = 0;
while(i < hc->type) {
if (hc->created[i] && i!=port)
all = 0;
if (hc->created[i])
any = 1;
i++;
}
if (!any) {
printk(KERN_WARNING "%s: ERROR card has no used stacks anymore.\n", __FUNCTION__);
unlock_dev(hc);
return;
}
}
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: releasing port=%d all=%d any=%d\n", __FUNCTION__, port, all, any);
if (port>-1 && !hc->created[port]) {
printk(KERN_WARNING "%s: ERROR given stack is not used by card (port=%d).\n", __FUNCTION__, port);
unlock_dev(hc);
return;
}
if (all) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_WARNING "%s: card has no more used stacks, so we release hardware.\n", __FUNCTION__);
if (hc->irq) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_WARNING "%s: free irq %d\n", __FUNCTION__, hc->irq);
free_irq(hc->irq, hc);
hc->irq = 0;
}
}
/* disable D-channels & B-channels */
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: disable all channels (d and b)\n", __FUNCTION__);
if (hc->type == 1) {
hc->chan[16].slot = -1;
mode_hfcmulti(hc, 16, ISDN_PID_NONE, -1, 0);//d
i = 0;
while(i < 30) {
hc->chan[i+1+(i>=15)].slot = -1;
hc->chan[i+1+(i>=15)].conf = 0;
mode_hfcmulti(hc, i+1+(i>=15), ISDN_PID_NONE, -1, 0); //b
i++;
}
} else {
i = 0;
while(i < hc->type) {
if (all || port==i)
if (hc->created[i]) {
hc->chan[(i<<2)+2].slot = -1;
mode_hfcmulti(hc, (i<<2)+2, ISDN_PID_NONE, -1, 0); //d
hc->chan[i<<2].slot = -1;
hc->chan[i<<2].conf = 0;
mode_hfcmulti(hc, i<<2, ISDN_PID_NONE, -1, 0); //b1
hc->chan[(i<<2)+1].slot = -1;
hc->chan[(i<<2)+1].conf = 0;
mode_hfcmulti(hc, (i<<2)+1, ISDN_PID_NONE, -1, 0); //b2
}
i++;
}
}
i = 0;
while(i < 32) {
if (hc->chan[i].dch)
if (hc->created[hc->chan[i].port])
if (hc->chan[i].dch->dbusytimer.function != NULL && (all || port==i)) {
del_timer(&hc->chan[i].dch->dbusytimer);
hc->chan[i].dch->dbusytimer.function = NULL;
}
i++;
}
/* free channels */
i = 0;
while(i < 32) {
if (hc->created[hc->chan[i].port])
if (hc->chan[i].port==port || all) {
if (hc->chan[i].dch) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: free port %d D-channel %d (1..32)\n", __FUNCTION__, hc->chan[i].port, i);
free_dchannel(hc->chan[i].dch);
kfree(hc->chan[i].dch);
hc->chan[i].dch = NULL;
}
if (hc->chan[i].rx_buf) {
kfree(hc->chan[i].rx_buf);
hc->chan[i].rx_buf = NULL;
}
if (hc->chan[i].bch) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: free port %d B-channel %d (1..32)\n", __FUNCTION__, hc->chan[i].port, i);
free_bchannel(hc->chan[i].bch);
kfree(hc->chan[i].bch);
hc->chan[i].bch = NULL;
}
if (hc->chan[i].dtmf_skb) {
dev_kfree_skb(hc->chan[i].dtmf_skb);
hc->chan[i].dtmf_skb = NULL;
}
hc->created[hc->chan[i].port] = 0;
}
i++;
}
/* dimm leds */
if (hc->leds)
hfcmulti_leds(hc);
/* release IO & remove card */
if (all)
if (debug & DEBUG_HFCMULTI_INIT) {
printk(KERN_DEBUG "%s: do release_io_hfcmulti\n", __FUNCTION__);
release_io_hfcmulti(hc);
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: removing object from listbase\n", __FUNCTION__);
REMOVE_FROM_LISTBASE(hc, ((hfc_multi_t *)HFCM_obj.ilist));
kfree(hc);
}
unlock_dev(hc);
}
static int
HFC_manager(void *data, u_int prim, void *arg)
{
hfc_multi_t *hc = HFCM_obj.ilist;
mISDNinstance_t *inst = data;
struct sk_buff *skb;
dchannel_t *dch = NULL;
bchannel_t *bch = NULL;
int ch = -1;
int i;
if (!data) {
printk(KERN_ERR "%s: no data prim %x arg %p\n", __FUNCTION__, prim, arg);
return(-EINVAL);
}
/* find channel and card */
while(hc) {
i = 0;
while(i < 32) {
//printk(KERN_DEBUG "comparing (D-channel) card=%08x inst=%08x with inst=%08x\n", hc, &hc->dch[i].inst, inst);
if (hc->chan[i].dch)
if (&hc->chan[i].dch->inst == inst) {
ch = i;
dch = hc->chan[i].dch;
break;
}
if (hc->chan[i].bch)
if (&hc->chan[i].bch->inst == inst) {
ch = i;
bch = hc->chan[i].bch;
break;
}
i++;
}
if (ch >= 0)
break;
hc = hc->next;
}
if (ch < 0) {
printk(KERN_ERR "%s: no card/channel found data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg);
return(-EINVAL);
}
if (debug & DEBUG_HFCMULTI_MGR)
printk(KERN_DEBUG "%s: channel %d (0..31) data %p prim %x arg %p\n", __FUNCTION__, ch, data, prim, arg);
switch(prim) {
case MGR_REGLAYER | CONFIRM:
if (debug & DEBUG_HFCMULTI_MGR)
printk(KERN_DEBUG "%s: MGR_REGLAYER\n", __FUNCTION__);
if (dch)
dch_set_para(dch, &inst->st->para);
if (bch)
bch_set_para(bch, &inst->st->para);
break;
case MGR_UNREGLAYER | REQUEST:
if (debug & DEBUG_HFCMULTI_MGR) {
printk(KERN_DEBUG "%s: MGR_UNREGLAYER\n", __FUNCTION__);
}
if (dch) {
inst->down.fdata = dch;
if ((skb = create_link_skb(PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL, 0))) {
if (hfcmulti_l1hw(&inst->down, skb))
dev_kfree_skb(skb);
}
} else
if (bch) {
inst->down.fdata = bch;
if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST, 0, 0, NULL, 0))) {
if (hfcmulti_l2l1(&inst->down, skb))
dev_kfree_skb(skb);
}
}
HFCM_obj.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up);
HFCM_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
break;
case MGR_CLRSTPARA | INDICATION:
arg = NULL;
// fall through
case MGR_ADDSTPARA | INDICATION:
if (debug & DEBUG_HFCMULTI_MGR)
printk(KERN_DEBUG "%s: MGR_***STPARA\n", __FUNCTION__);
if (dch)
dch_set_para(dch, arg);
if (bch)
bch_set_para(bch, arg);
break;
case MGR_RELEASE | INDICATION:
if (debug & DEBUG_HFCMULTI_MGR)
printk(KERN_DEBUG "%s: MGR_RELEASE = remove port from mISDN\n", __FUNCTION__);
if (dch) {
release_port(hc, hc->chan[ch].port);
HFCM_obj.refcnt--;
}
if (bch)
HFCM_obj.refcnt--;
break;
case MGR_CONNECT | REQUEST:
if (debug & DEBUG_HFCMULTI_MGR)
printk(KERN_DEBUG "%s: MGR_CONNECT\n", __FUNCTION__);
return(ConnectIF(inst, arg));
//break;
case MGR_SETIF | REQUEST:
case MGR_SETIF | INDICATION:
if (debug & DEBUG_HFCMULTI_MGR)
printk(KERN_DEBUG "%s: MGR_SETIF\n", __FUNCTION__);
if (dch)
return(SetIF(inst, arg, prim, hfcmulti_l1hw, NULL, dch));
if (bch)
return(SetIF(inst, arg, prim, hfcmulti_l2l1, NULL, bch));
//break;
case MGR_DISCONNECT | REQUEST:
case MGR_DISCONNECT | INDICATION:
if (debug & DEBUG_HFCMULTI_MGR)
printk(KERN_DEBUG "%s: MGR_DISCONNECT\n", __FUNCTION__);
return(DisConnectIF(inst, arg));
//break;
case MGR_SELCHANNEL | REQUEST:
if (debug & DEBUG_HFCMULTI_MGR)
printk(KERN_DEBUG "%s: MGR_SELCHANNEL\n", __FUNCTION__);
if (!dch) {
printk(KERN_WARNING "%s(MGR_SELCHANNEL|REQUEST): selchannel not dinst\n", __FUNCTION__);
return(-EINVAL);
}
return(SelFreeBChannel(hc, ch, arg));
//break;
case MGR_SETSTACK | CONFIRM:
if (debug & DEBUG_HFCMULTI_MGR)
printk(KERN_DEBUG "%s: MGR_SETSTACK\n", __FUNCTION__);
if (bch && inst->pid.global==2) {
inst->down.fdata = bch;
if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) {
if (hfcmulti_l2l1(&inst->down, skb))
dev_kfree_skb(skb);
}
if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS)
if_link(&inst->up, DL_ESTABLISH | INDICATION, 0, 0, NULL, 0);
else
if_link(&inst->up, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0);
}
break;
default:
printk(KERN_WARNING "%s: prim %x not handled\n", __FUNCTION__, prim);
return(-EINVAL);
}
return(0);
}
static int __init
HFCmulti_init(void)
{
int err, err2, i;
hfc_multi_t *hc;
mISDN_pid_t pid, pids[MAX_CARDS];
mISDNstack_t *dst = NULL; /* make gcc happy */
int port_cnt;
int bchperport, pt;
int ch, ch2;
dchannel_t *dch;
bchannel_t *bch;
char tmp[64];
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: init entered\n", __FUNCTION__);
#ifdef __BIG_ENDIAN
#error "not running on big endian machines now"
#endif
strcpy(tmp, hfcmulti_revision);
printk(KERN_INFO "mISDN: HFC-multi driver Rev. %s\n", mISDN_getrev(tmp));
switch(poll) {
case 8:
poll_timer = 2;
break;
case 16:
poll_timer = 3;
break;
case 32:
poll_timer = 4;
break;
case 64:
poll_timer = 5;
break;
case 128: case 0:
poll_timer = 6;
break;
case 256:
poll_timer = 7;
break;
default:
printk(KERN_ERR "%s: Wrong poll value (%d).\n", __FUNCTION__, poll);
err = -EINVAL;
return(err);
}
memset(&HFCM_obj, 0, sizeof(HFCM_obj));
SET_MODULE_OWNER(&HFCM_obj);
HFCM_obj.name = HFCName;
HFCM_obj.own_ctrl = HFC_manager;
HFCM_obj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | ISDN_PID_L0_NT_S0
| ISDN_PID_L0_TE_E1 | ISDN_PID_L0_NT_E1;
HFCM_obj.DPROTO.protocol[1] = ISDN_PID_L1_NT_S0
/*| ISDN_PID_L1_TE_E1 */| ISDN_PID_L1_NT_E1;
HFCM_obj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC;
HFCM_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV;
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: registering HFCM_obj\n", __FUNCTION__);
if ((err = mISDN_register(&HFCM_obj))) {
printk(KERN_ERR "Can't register HFC-Multi cards error(%d)\n", err);
return(err);
}
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: new mISDN object (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
/* Note: ALL ports are one "card" object */
port_cnt = HFC_cnt = 0;
while (HFC_cnt < MAX_CARDS && type[HFC_cnt] > 0) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: Registering chip type %d (0x%x)\n", __FUNCTION__, type[HFC_cnt] & 0xff, type[HFC_cnt]);
/* check card type */
switch (type[HFC_cnt] & 0xff) {
case 1:
bchperport = 30;
break;
case 4:
bchperport = 2;
break;
case 8:
bchperport = 2;
break;
default:
printk(KERN_ERR "Card type(%d) not supported.\n", type[HFC_cnt] & 0xff);
err = -EINVAL;
goto free_object;
}
/* allocate card+fifo structure */
if (!(hc = kmalloc(sizeof(hfc_multi_t), GFP_ATOMIC))) {
printk(KERN_ERR "No kmem for HFC-Multi card\n");
err = -ENOMEM;
goto free_object;
}
memset(hc, 0, sizeof(hfc_multi_t));
/* set chip specific features */
hc->masterclk = -1;
hc->type = type[HFC_cnt] & 0xff;
if (type[HFC_cnt] & 0x100) {
test_and_set_bit(HFC_CHIP_ULAW, &hc->chip);
silence = 0xff; /* ulaw silence */
} else
silence = 0x2a; /* alaw silence */
if (type[HFC_cnt] & 0x200)
test_and_set_bit(HFC_CHIP_DTMF, &hc->chip);
// if ((type[HFC_cnt]&0x400) && hc->type==4)
// test_and_set_bit(HFC_CHIP_LEDS, &hc->chip);
if (type[HFC_cnt] & 0x800)
test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
if (type[HFC_cnt] & 0x4000)
test_and_set_bit(HFC_CHIP_EXRAM_128, &hc->chip);
if (type[HFC_cnt] & 0x8000)
test_and_set_bit(HFC_CHIP_EXRAM_512, &hc->chip);
hc->slots = 32;
if (type[HFC_cnt] & 0x10000)
hc->slots = 64;
if (type[HFC_cnt] & 0x20000)
hc->slots = 128;
sprintf(hc->name, (hc->type==1)?"HFC-E1":"HFC-%dS#%d", hc->type, HFC_cnt+1);
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__);
APPEND_TO_LIST(hc, ((hfc_multi_t *)HFCM_obj.ilist));
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: (after APPEND_TO_LIST)\n", __FUNCTION__);
lock_HW_init(&hc->lock);
pt = 0;
while (pt < hc->type) {
if (protocol[port_cnt] == 0) {
printk(KERN_ERR "Not enough 'protocol' values given.\n");
err = -EINVAL;
goto free_channels;
}
if (hc->type == 1)
ch = 16;
else
ch = (pt<<2)+2;
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: Registering D-channel, card(%d) ch(%d) port(%d) protocol(%x)\n", __FUNCTION__, HFC_cnt+1, ch, pt+1, protocol[port_cnt]);
hc->chan[ch].port = pt;
hc->chan[ch].nt_timer = -1;
dch = kmalloc(sizeof(dchannel_t), GFP_ATOMIC);
if (!dch) {
err = -ENOMEM;
goto free_channels;
}
memset(dch, 0, sizeof(dchannel_t));
dch->channel = ch;
//dch->debug = debug;
dch->inst.obj = &HFCM_obj;
dch->inst.lock = lock_dev;
dch->inst.unlock = unlock_dev;
init_mISDNinstance(&dch->inst, &HFCM_obj, hc);
dch->inst.pid.layermask = ISDN_LAYER(0);
sprintf(dch->inst.name, "HFCm%d/%d", HFC_cnt+1, pt+1);
if (!(hc->chan[ch].rx_buf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) {
err = -ENOMEM;
goto free_channels;
}
if (init_dchannel(dch)) {
err = -ENOMEM;
goto free_channels;
}
hc->chan[ch].dch = dch;
i=0;
while(i < bchperport) {
if (hc->type == 1)
ch2 = i + 1 + (i>=15);
else
ch2 = (pt<<2)+i;
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: Registering B-channel, card(%d) ch(%d) port(%d) channel(%d)\n", __FUNCTION__, HFC_cnt+1, ch2, pt+1, i);
hc->chan[ch2].port = pt;
bch = kmalloc(sizeof(bchannel_t), GFP_ATOMIC);
if (!bch) {
err = -ENOMEM;
goto free_channels;
}
memset(bch, 0, sizeof(bchannel_t));
bch->channel = ch2;
init_mISDNinstance(&bch->inst, &HFCM_obj, hc);
bch->inst.pid.layermask = ISDN_LAYER(0);
bch->inst.lock = lock_dev;
bch->inst.unlock = unlock_dev;
//bch->debug = debug;
sprintf(bch->inst.name, "%s B%d",
dch->inst.name, i+1);
if (init_bchannel(bch)) {
kfree(bch);
err = -ENOMEM;
goto free_channels;
}
hc->chan[ch2].bch = bch;
if (bch->dev) {
bch->dev->wport.pif.func =
hfcmulti_l2l1;
bch->dev->wport.pif.fdata =
bch;
}
i++;
}
/* set D-channel */
set_dchannel_pid(&pid, protocol[port_cnt], layermask[port_cnt]);
/* set protocol type */
if (protocol[port_cnt] & 0x10) {
/* NT-mode */
dch->inst.pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_NT_E1:ISDN_PID_L0_NT_S0;
dch->inst.pid.protocol[1] = (hc->type==1)?ISDN_PID_L1_NT_E1:ISDN_PID_L1_NT_S0;
pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_NT_E1:ISDN_PID_L0_NT_S0;
pid.protocol[1] = (hc->type==1)?ISDN_PID_L1_NT_E1:ISDN_PID_L1_NT_S0;
dch->inst.pid.layermask |= ISDN_LAYER(1);
pid.layermask |= ISDN_LAYER(1);
if (layermask[port_cnt] & ISDN_LAYER(2))
pid.protocol[2] = ISDN_PID_L2_LAPD_NET;
test_and_set_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg);
} else {
/* TE-mode (LT-mode) */
dch->inst.pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_TE_E1:ISDN_PID_L0_TE_S0;
pid.protocol[0] = (hc->type==1)?ISDN_PID_L0_TE_E1:ISDN_PID_L0_TE_S0;
/*if (hc->type == 1) {
dch->inst.pid.protocol[1] = ISDN_PID_L1_TE_E1;
pid.protocol[1] = ISDN_PID_L1_TE_E1;
dch->inst.pid.layermask |= ISDN_LAYER(1);
pid.layermask |= ISDN_LAYER(1);
if (layermask[port_cnt] & ISDN_LAYER(2))
pid.protocol[2] = ISDN_PID_L2_LAPD;
}*/
}
if (hc->type != 1) {
/* S/T */
/* set master clock */
if (protocol[port_cnt] & 0x10000) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: PROTOCOL set master clock: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
if (test_bit(HFC_CFG_NTMODE, &hc->chan[ch].cfg)) {
printk(KERN_ERR "Error: Master clock for port(%d) of card(%d) is only possible with TE-mode\n", pt+1, HFC_cnt+1);
err = -EINVAL;
goto free_channels;
}
if (hc->masterclk < 0) {
printk(KERN_ERR "Error: Master clock for port(%d) of card(%d) already defined for port(%d)\n", pt+1, HFC_cnt+1, hc->masterclk+1);
err = -EINVAL;
goto free_channels;
}
hc->masterclk = pt;
}
/* set transmitter line to non capacitive */
if (protocol[port_cnt] & 0x20000) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: PROTOCOL set non capacitive transmitter: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
test_and_set_bit(HFC_CFG_NONCAP_TX, &hc->chan[ch].cfg);
}
/* disable E-channel */
if (protocol[port_cnt] & 0x40000) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: PROTOCOL disable E-channel: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
test_and_set_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[ch].cfg);
}
/* register E-channel */
if (protocol[port_cnt] & 0x80000) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: PROTOCOL register E-channel: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
test_and_set_bit(HFC_CFG_REG_ECHANNEL, &hc->chan[ch].cfg);
}
} else {
/* E1 */
/* set optical line type */
if (protocol[port_cnt] & 0x10000) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: PROTOCOL set optical interfacs: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
test_and_set_bit(HFC_CFG_OPTICAL, &hc->chan[ch].cfg);
}
/* set LOS report */
if (protocol[port_cnt] & 0x40000) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: PROTOCOL set LOS report: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
test_and_set_bit(HFC_CFG_REPORT_LOS, &hc->chan[ch].cfg);
}
/* set AIS report */
if (protocol[port_cnt] & 0x80000) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: PROTOCOL set AIS report: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
test_and_set_bit(HFC_CFG_REPORT_AIS, &hc->chan[ch].cfg);
}
/* set SLIP report */
if (protocol[port_cnt] & 0x100000) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: PROTOCOL set SLIP report: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt);
test_and_set_bit(HFC_CFG_REPORT_SLIP, &hc->chan[ch].cfg);
}
/* set elastic jitter buffer */
if (protocol[port_cnt] & 0x600000) {
if (debug & DEBUG_HFCMULTI_INIT)
hc->chan[ch].jitter = (protocol[port_cnt]>>21) & 0x3;
printk(KERN_DEBUG "%s: PROTOCOL set elastic buffer to %d: card(%d) port(%d)\n", __FUNCTION__, hc->chan[ch].jitter, HFC_cnt+1, pt);
} else
hc->chan[ch].jitter = 2; /* default */
}
memcpy(&pids[pt], &pid, sizeof(pid));
pt++;
port_cnt++;
}
/* run card setup */
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: Setting up card(%d)\n", __FUNCTION__, HFC_cnt+1);
if ((err = setup_pci(hc))) {
goto free_channels;
}
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: Initializing card(%d)\n", __FUNCTION__, HFC_cnt+1);
if ((err = init_card(hc))) {
if (debug & DEBUG_HFCMULTI_INIT) {
printk(KERN_DEBUG "%s: do release_io_hfcmulti\n", __FUNCTION__);
release_io_hfcmulti(hc);
}
goto free_channels;
}
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: Init modes card(%d)\n", __FUNCTION__, HFC_cnt+1);
hfcmulti_initmode(hc);
/* add stacks */
pt = 0;
while(pt < hc->type) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: Adding d-stack: card(%d) port(%d)\n", __FUNCTION__, HFC_cnt+1, pt+1);
if (hc->type == 1)
dch = hc->chan[16].dch;
else
dch = hc->chan[(pt<<2)+2].dch;
if ((err = HFCM_obj.ctrl(NULL, MGR_NEWSTACK | REQUEST, &dch->inst))) {
printk(KERN_ERR "MGR_ADDSTACK REQUEST dch err(%d)\n", err);
free_release:
release_port(hc, -1); /* all ports */
goto free_object;
}
/* indicate that this stack is created */
hc->created[pt] = 1;
dst = dch->inst.st;
i = 0;
while(i < bchperport) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: Adding b-stack: card(%d) port(%d) B-channel(%d)\n", __FUNCTION__, HFC_cnt+1, pt+1, i+1);
if (hc->type == 1)
bch = hc->chan[i + 1 + (i>=15)].bch;
else
bch = hc->chan[(pt<<2) + i].bch;
if ((err = HFCM_obj.ctrl(dst, MGR_NEWSTACK | REQUEST, &bch->inst))) {
printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err);
free_delstack:
HFCM_obj.ctrl(dst, MGR_DELSTACK | REQUEST, NULL);
goto free_release;
}
bch->st = bch->inst.st;
i++;
}
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: (before MGR_SETSTACK REQUEST) layermask=0x%x\n", __FUNCTION__, pids[pt].layermask);
if ((err = HFCM_obj.ctrl(dst, MGR_SETSTACK | REQUEST, &pids[pt]))) {
printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err);
goto free_delstack;
}
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: (after MGR_SETSTACK REQUEST)\n", __FUNCTION__);
pt++;
}
/* now turning on irq */
HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl);
HFC_cnt++;
}
if (HFC_cnt == 0) {
printk(KERN_INFO "hfc_multi: No cards defined, read the documentation.\n");
err = -EINVAL;
goto free_object;
}
printk(KERN_INFO "hfc_multi driver: %d cards with total of %d ports installed.\n", HFC_cnt, port_cnt);
return(0);
/* DONE */
/* if an error ocurred */
free_channels:
i = 0;
while(i < 32) {
if (hc->chan[i].dch) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: free D-channel %d (1..32)\n", __FUNCTION__, i);
free_dchannel(hc->chan[i].dch);
kfree(hc->chan[i].dch);
hc->chan[i].dch = NULL;
}
if (hc->chan[i].rx_buf) {
kfree(hc->chan[i].rx_buf);
hc->chan[i].rx_buf = NULL;
}
if (hc->chan[i].bch) {
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: free B-channel %d (1..32)\n", __FUNCTION__, i);
free_bchannel(hc->chan[i].bch);
kfree(hc->chan[i].bch);
hc->chan[i].bch = NULL;
}
i++;
}
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: before REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
REMOVE_FROM_LISTBASE(hc, ((hfc_multi_t *)HFCM_obj.ilist));
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: after REMOVE_FROM_LIST (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
kfree(hc);
free_object:
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: brefore mISDN_unregister (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
if ((err2 = mISDN_unregister(&HFCM_obj))) {
printk(KERN_ERR "Can't unregister HFC-Multi cards error(%d)\n", err);
}
while(HFCM_obj.ilist) {
printk(KERN_ERR "HFC PCI card struct not empty refs %d\n", HFCM_obj.refcnt);
release_port(HFCM_obj.ilist, -1); /* all ports */
}
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: after mISDN_unregister (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: exitting with error %d\n", __FUNCTION__, err);
return(err);
}
#ifdef MODULE
static void __exit
HFCmulti_cleanup(void)
{
int err;
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: entered (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
if ((err = mISDN_unregister(&HFCM_obj))) {
printk(KERN_ERR "Can't unregister HFC-Multi cards error(%d)\n", err);
}
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: now checking ilist (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
while(HFCM_obj.ilist) {
printk(KERN_ERR "HFC PCI card struct not empty refs %d\n", HFCM_obj.refcnt);
release_port(HFCM_obj.ilist, -1); /* all ports */
}
if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: done (refcnt = %d)\n", __FUNCTION__, HFCM_obj.refcnt);
return;
}
module_init(HFCmulti_init);
module_exit(HFCmulti_cleanup);
#endif