/* $Id$ * * mISDN driver for Colognechip HFC-S mini Evaluation Card * * Authors : Martin Bachem, Joerg Ciesielski * Contact : info@colognechip.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, 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. * ******************************************************************************* * * MODULE PARAMETERS: * (NOTE: layermask and protocol must be given for all ports, * not for the number of cards.) * * - protocol=[,p2,p3...] * Values: * D-channel protocol id * Flags for special features * Spare (set to 0) * * D-channel protocol ids * - 1 1TR6 (not released yet) * - 2 DSS1 * * Feature Flags * 0x0010 Net side stack (NT mode) * 0x0020 PCI mode (0=master, 1=slave) * 0x0040 not in use * 0x0080 B channel loop (for layer1 tests) * * - layermask=[,l2,l3...] (32bit): * mask of layers to be used for D-channel stack * * - debug: * enable debugging (see hfcs_mini.h for debug options) * */ #include #include #include #include #include "layer1.h" #include "debug.h" #include "hfcs_mini.h" #include "hfcsmcc.h" #if HFCBRIDGE == BRIDGE_HFCPCI #include #endif static const char hfcsmini_rev[] = "$Revision$"; #define MAX_CARDS 8 static int card_cnt; static u_int protocol[MAX_CARDS]; static int layermask[MAX_CARDS]; #ifdef MODULE MODULE_LICENSE("GPL"); #define MODULE_PARM_T "1-8i" MODULE_PARM(debug, "1i"); MODULE_PARM(protocol, MODULE_PARM_T); MODULE_PARM(layermask, MODULE_PARM_T); #endif static mISDNobject_t hw_mISDNObj; static int debug = 0; #if HFCBRIDGE == BRIDGE_HFCPCI static inline void hfcsmini_sel_reg(hfcsmini_hw * hw, __u8 reg_addr) { outb(6, hw->iobase + 3); /* A0 = 1, reset = 1 */ outb(reg_addr, hw->iobase + 1); /* write register number */ outb(4, hw->iobase + 3); /* A0 = 0, reset = 1 */ } static inline __u8 read_hfcsmini(hfcsmini_hw * hw, __u8 reg_addr) { register u_char ret; #ifdef SPIN_LOCK_HFCSMINI_REGISTER spin_lock_irq(&hw->rlock); #endif hfcsmini_sel_reg(hw, reg_addr); ret = inb(hw->iobase + 1); #ifdef SPIN_LOCK_HFCSMINI_REGISTER spin_unlock_irq(&hw->rlock); #endif return(ret); } /* read register in already spin-locked irq context */ static inline __u8 read_hfcsmini_irq(hfcsmini_hw * hw, __u8 reg_addr) { register u_char ret; hfcsmini_sel_reg(hw, reg_addr); ret = inb(hw->iobase + 1); return(ret); } static inline __u8 read_hfcsmini_stable(hfcsmini_hw * hw, __u8 reg_addr) { register u_char in1, in2; #ifdef SPIN_LOCK_HFCSMINI_REGISTER spin_lock_irq(&hw->rlock); #endif hfcsmini_sel_reg(hw, reg_addr); in1 = inb(hw->iobase + 1); // loop until 2 equal accesses while((in2=inb(hw->iobase + 1))!=in1) in1=in2; #ifdef SPIN_LOCK_HFCSMINI_REGISTER spin_unlock_irq(&hw->rlock); #endif return(in1); } static inline void write_hfcsmini(hfcsmini_hw * hw, __u8 reg_addr, __u8 value) { #ifdef SPIN_LOCK_HFCSMINI_REGISTER spin_lock_irq(&hw->rlock); #endif hfcsmini_sel_reg(hw, reg_addr); outb(value, hw->iobase + 1); #ifdef SPIN_LOCK_HFCSMINI_REGISTER spin_unlock_irq(&hw->rlock); #endif } #endif static void hfcsmini_ph_command(channel_t * dch, u_char command) { hfcsmini_hw *hw = dch->hw; if (dch->debug) mISDN_debugprint(&dch->inst, "%s command(%i) channel(%i)", __FUNCTION__, command, dch->channel); switch (command) { case HFC_L1_ACTIVATE_TE: if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) { mISDN_debugprint(&dch->inst, "HFC_L1_ACTIVATE_TE channel(%i) command(%i)", dch->channel, command); } write_hfcsmini(hw, R_ST_WR_STA, (M_ST_LD_STA | (M1_ST_SET_STA*4))); udelay(125); /* to be sure INFO1 signals are sent */ write_hfcsmini(hw, R_ST_WR_STA, (M1_ST_SET_STA * 4)); break; case HFC_L1_FORCE_DEACTIVATE_TE: write_hfcsmini(hw, R_ST_WR_STA, (M_ST_LD_STA | (M1_ST_SET_STA*3))); udelay(7); /* wait at least 5,21 us */ write_hfcsmini(hw, R_ST_WR_STA, (M1_ST_SET_STA*3)); break; case HFC_L1_ACTIVATE_NT: if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) mISDN_debugprint(&dch->inst, "HFC_L1_ACTIVATE_NT channel(%i)"); write_hfcsmini(hw, R_ST_WR_STA, (M1_ST_ACT | M_SET_G2_G3)); break; case HFC_L1_DEACTIVATE_NT: if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) mISDN_debugprint(&dch->inst, "HFC_L1_DEACTIVATE_NT channel(%i)"); write_hfcsmini(hw, R_ST_WR_STA, (M1_ST_ACT * 2)); break; case HFC_L1_TESTLOOP_B1: break; case HFC_L1_TESTLOOP_B2: break; } } /*********************************/ /* S0 state change event handler */ /*********************************/ static void s0_new_state(channel_t * dch) { u_int prim = PH_SIGNAL | INDICATION; u_int para = 0; hfcsmini_hw *hw = dch->hw; if (hw->portmode & PORT_MODE_TE) { if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) mISDN_debugprint(&dch->inst, "%s: TE %d", __FUNCTION__, dch->state); switch (dch->state) { case (0): prim = PH_CONTROL | INDICATION; para = HW_RESET; break; case (3): prim = PH_CONTROL | INDICATION; para = HW_DEACTIVATE; break; case (6): para = INFO2; break; case (7): para = INFO4_P8; break; case (5): case (8): para = ANYSIGNAL; break; default: return; } if (dch->state== 7) test_and_set_bit(FLG_ACTIVE, &dch->Flags); else test_and_clear_bit(FLG_ACTIVE, &dch->Flags); } // PORT_MODE_TE if (hw->portmode & PORT_MODE_NT) { if ((dch->debug) & (debug & DEBUG_HFC_S0_STATES)) mISDN_debugprint(&dch->inst, "%s: NT %d", __FUNCTION__, dch->state); switch (dch->state) { case (1): hw->nt_timer = 0; hw->portmode &= ~NT_TIMER; prim = PH_DEACTIVATE | INDICATION; test_and_clear_bit(FLG_ACTIVE, &dch->Flags); para = 0; break; case (2): if (hw->nt_timer < 0) { hw->nt_timer = 0; hw->portmode &= ~NT_TIMER; hfcsmini_ph_command(dch, HFC_L1_DEACTIVATE_NT); } else { hw->nt_timer = NT_T1_COUNT; hw->portmode |= NT_TIMER; write_hfcsmini(hw, R_ST_WR_STA, M_SET_G2_G3); } return; case (3): hw->nt_timer = 0; hw->portmode &= ~NT_TIMER; prim = PH_ACTIVATE | INDICATION; test_and_set_bit(FLG_ACTIVE, &dch->Flags); para = 0; break; case (4): hw->nt_timer = 0; hw->portmode &= ~NT_TIMER; return; default: break; } } // PORT_MODE_NT mISDN_queue_data(&dch->inst, FLG_MSG_UP, prim, para, 0, NULL, 0); } /*************************************/ /* Layer 1 D-channel hardware access */ /*************************************/ static int handle_dmsg(channel_t *dch, struct sk_buff *skb) { int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); hfcsmini_hw *hw = dch->hw; u_long flags; if (hh->prim == (PH_SIGNAL | REQUEST)) { ret = -EINVAL; } else if (hh->prim == (PH_CONTROL | REQUEST)) { spin_lock_irqsave(&hw->rlock, flags); if (hh->dinfo == HW_RESET) { if (dch->state != 0) hfcsmini_ph_command(dch, HFC_L1_ACTIVATE_TE); spin_unlock_irqrestore(&hw->rlock, flags); skb_trim(skb, 0); return(mISDN_queueup_newhead(&dch->inst, 0, PH_CONTROL | INDICATION,HW_POWERUP, skb)); } else if (hh->dinfo == HW_DEACTIVATE) { if (dch->next_skb) { dev_kfree_skb(dch->next_skb); dch->next_skb = NULL; } test_and_clear_bit(FLG_TX_NEXT, &dch->Flags); test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); #ifdef FIXME if (test_and_clear_bit(FLG_L1_DBUSY, &dch->Flags)) dchannel_sched_event(dch, D_CLEARBUSY); #endif } else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) { if (1 & hh->dinfo) hfcsmini_ph_command(dch, HFC_L1_TESTLOOP_B1); if (2 & hh->dinfo) hfcsmini_ph_command(dch, HFC_L1_TESTLOOP_B2); } else if (hh->dinfo == HW_POWERUP) { hfcsmini_ph_command(dch, HFC_L1_FORCE_DEACTIVATE_TE); } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "hfcsmini_l1hw unknown ctrl %x", hh->dinfo); ret = -EINVAL; } spin_unlock_irqrestore(&hw->rlock, flags); } else if (hh->prim == (PH_ACTIVATE | REQUEST)) { spin_lock_irqsave(&hw->rlock, flags); if (hw->portmode & PORT_MODE_NT) { hfcsmini_ph_command(dch, HFC_L1_ACTIVATE_NT); } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "%s: PH_ACTIVATE none NT mode", __FUNCTION__); ret = -EINVAL; } spin_unlock_irqrestore(&hw->rlock, flags); } else if (hh->prim == (PH_DEACTIVATE | REQUEST)) { spin_lock_irqsave(&hw->rlock, flags); if (hw->portmode & PORT_MODE_NT) { hfcsmini_ph_command(dch, HFC_L1_DEACTIVATE_NT); if (test_and_clear_bit(FLG_TX_NEXT, &dch->Flags)) { dev_kfree_skb(dch->next_skb); dch->next_skb = NULL; } if (dch->tx_skb) { dev_kfree_skb(dch->tx_skb); dch->tx_skb = NULL; } dch->tx_idx = 0; if (dch->rx_skb) { dev_kfree_skb(dch->rx_skb); dch->rx_skb = NULL; } test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); test_and_clear_bit(FLG_ACTIVE, &dch->Flags); } else { if (dch->debug & L1_DEB_WARN) mISDN_debugprint(&dch->inst, "%s: PH_DEACTIVATE none NT mode", __FUNCTION__); ret = -EINVAL; } spin_unlock_irqrestore(&hw->rlock, flags); } else if ((hh->prim & MISDN_CMD_MASK) == MGR_SHORTSTATUS) { u_int temp = hh->dinfo & SSTATUS_ALL; // remove SSTATUS_BROADCAST_BIT if ((hw->portmode & PORT_MODE_NT) && (temp == SSTATUS_ALL || temp == SSTATUS_L1)) { if (hh->dinfo & SSTATUS_BROADCAST_BIT) temp = dch->inst.id | MSG_BROADCAST; else temp = hh->addr | FLG_MSG_TARGET; skb_trim(skb, 0); hh->dinfo = test_bit(FLG_ACTIVE, &dch->Flags) ? SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED; hh->prim = MGR_SHORTSTATUS | CONFIRM; return(mISDN_queue_message(&dch->inst, temp, skb)); } ret = -EOPNOTSUPP; } else { printk(KERN_WARNING "%s %s: unknown prim(%x)\n", hw->card_name, __FUNCTION__, hh->prim); ret = -EAGAIN; } if (!ret) dev_kfree_skb(skb); return (ret); } /*************************************/ /* Layer 1 B-channel hardware access */ /*************************************/ static int handle_bmsg(channel_t *bch, struct sk_buff *skb) { hfcsmini_hw *hw = bch->hw; int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; if ((hh->prim == (PH_ACTIVATE | REQUEST)) || (hh->prim == (DL_ESTABLISH | REQUEST))) { if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { spin_lock_irqsave(&hw->rlock, flags); ret = setup_channel(hw, bch->channel, bch->inst.pid.protocol[1]); if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS) test_and_set_bit(FLG_L2DATA, &bch->Flags); spin_unlock_irqrestore(&hw->rlock, flags); } #ifdef FIXME 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); #endif skb_trim(skb, 0); return(mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, ret, skb)); } else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) || (hh->prim == (DL_RELEASE | REQUEST)) || ((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) { spin_lock_irqsave(&hw->rlock, flags); if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { dev_kfree_skb(bch->next_skb); bch->next_skb = NULL; } if (bch->tx_skb) { dev_kfree_skb(bch->tx_skb); bch->tx_skb = NULL; } bch->tx_idx = 0; if (bch->rx_skb) { dev_kfree_skb(bch->rx_skb); bch->rx_skb = NULL; } test_and_clear_bit(FLG_L2DATA, &bch->Flags); test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); setup_channel(hw, bch->channel, ISDN_PID_NONE); test_and_clear_bit(FLG_ACTIVE, &bch->Flags); spin_unlock_irqrestore(&hw->rlock, flags); skb_trim(skb, 0); if (hh->prim != (PH_CONTROL | REQUEST)) { #ifdef FIXME 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); #endif if (!mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, 0, skb)) return(0); } } else if (hh->prim == (PH_CONTROL | REQUEST)) { // do not handle PH_CONTROL | REQUEST ?? } else { printk(KERN_WARNING "%s %s: unknown prim(%x)\n", hw->card_name, __FUNCTION__, hh->prim); ret = -EAGAIN; } if (!ret) dev_kfree_skb(skb); return (ret); } /******************************/ /* Layer2 -> Layer 1 Transfer */ /******************************/ static int hfcsmini_l2l1(mISDNinstance_t *inst, struct sk_buff *skb) { channel_t *chan = container_of(inst, channel_t, inst); hfcsmini_hw *hw = chan->hw; int ret = 0; mISDN_head_t *hh = mISDN_HEAD_P(skb); u_long flags; if ((hh->prim == PH_DATA_REQ) || (hh->prim == DL_DATA_REQ)) { spin_lock_irqsave(inst->hwlock, flags); ret = channel_senddata(chan, hh->dinfo, skb); if (ret > 0) { /* direct TX */ tasklet_schedule(&hw->tasklet); ret = 0; } spin_unlock_irqrestore(inst->hwlock, flags); return(ret); } if (test_bit(FLG_DCHANNEL, &chan->Flags)) { ret = handle_dmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (test_bit(FLG_BCHANNEL, &chan->Flags)) { ret = handle_bmsg(chan, skb); if (ret != -EAGAIN) return(ret); ret = -EINVAL; } if (!ret) dev_kfree_skb(skb); return(ret); } static int hfcsmini_manager(void *data, u_int prim, void *arg) { hfcsmini_hw *hw = NULL; mISDNinstance_t *inst = data; struct sk_buff *skb; int channel = -1; int i; channel_t *chan = NULL; u_long flags; if (!data) { MGR_HASPROTOCOL_HANDLER(prim, arg, &hw_mISDNObj) printk(KERN_ERR "%s %s: no data prim %x arg %p\n", hw->card_name, __FUNCTION__, prim, arg); return (-EINVAL); } spin_lock_irqsave(&hw_mISDNObj.lock, flags); /* find channel and card */ list_for_each_entry(hw, &hw_mISDNObj.ilist, list) { i = 0; while (i < MAX_CHAN) { if (hw->chan[i].Flags && &hw->chan[i].inst == inst) { channel = i; chan = &hw->chan[i]; break; } i++; } if (channel >= 0) break; } spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); if (channel < 0) { printk(KERN_ERR "%s: no card/channel found data %p prim %x arg %p\n", __FUNCTION__, data, prim, arg); return (-EINVAL); } switch (prim) { case MGR_REGLAYER | CONFIRM: mISDN_setpara(chan, &inst->st->para); break; case MGR_UNREGLAYER | REQUEST: if ((skb = create_link_skb(PH_CONTROL | REQUEST, HW_DEACTIVATE, 0, NULL, 0))) { if (hfcsmini_l2l1(inst, skb)) dev_kfree_skb(skb); } else printk(KERN_WARNING "no SKB in %s MGR_UNREGLAYER | REQUEST\n", __FUNCTION__); hw_mISDNObj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL); break; case MGR_CLRSTPARA | INDICATION: arg = NULL; case MGR_ADDSTPARA | INDICATION: mISDN_setpara(chan, arg); break; case MGR_RELEASE | INDICATION: if (channel == 2) { release_card(hw); } else { hw_mISDNObj.refcnt--; } break; case MGR_SETSTACK | INDICATION: if ((channel != 2) && (inst->pid.global == 2)) { if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) { if (hfcsmini_l2l1(inst, skb)) dev_kfree_skb(skb); } if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION, 0, 0, NULL, 0); else mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION, 0, 0, NULL, 0); } break; case MGR_GLOBALOPT | REQUEST: if (arg) { // FIXME: detect cards with HEADSET u_int *gopt = arg; *gopt = GLOBALOPT_INTERNAL_CTRL | GLOBALOPT_EXTERNAL_EQUIPMENT | GLOBALOPT_HANDSET; } else return (-EINVAL); break; case MGR_SELCHANNEL | REQUEST: // no special procedure return (-EINVAL); PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION); default: printk(KERN_WARNING "%s %s: prim %x not handled\n", hw->card_name, __FUNCTION__, prim); return (-EINVAL); } return (0); } /***********************************/ /* check if new buffer for channel */ /* is waitinng is transmitt queue */ /***********************************/ int next_tx_frame(hfcsmini_hw * hw, __u8 channel) { channel_t *ch = &hw->chan[channel]; if (ch->tx_skb) dev_kfree_skb(ch->tx_skb); if (test_and_clear_bit(FLG_TX_NEXT, &ch->Flags)) { ch->tx_skb = ch->next_skb; if (ch->tx_skb) { mISDN_head_t *hh = mISDN_HEAD_P(ch->tx_skb); ch->next_skb = NULL; test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); ch->tx_idx = 0; queue_ch_frame(ch, CONFIRM, hh->dinfo, NULL); return (1); } else { printk(KERN_WARNING "%s channel(%i) TX_NEXT without skb\n", hw->card_name, channel); test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); } } else ch->tx_skb = NULL; test_and_clear_bit(FLG_TX_BUSY, &ch->Flags); return (0); } static inline void hfcsmini_waitbusy(hfcsmini_hw *hw) { while (read_hfcsmini(hw, R_STATUS) & M_BUSY); } static inline void hfcsmini_selfifo(hfcsmini_hw *hw, __u8 fifo) { write_hfcsmini(hw, R_FIFO, fifo); hfcsmini_waitbusy(hw); } static inline void hfcsmini_inc_f(hfcsmini_hw *hw) { write_hfcsmini(hw, A_INC_RES_FIFO, M_INC_F); hfcsmini_waitbusy(hw); } static inline void hfcsmini_resetfifo(hfcsmini_hw *hw) { write_hfcsmini(hw, A_INC_RES_FIFO, M_RES_FIFO); hfcsmini_waitbusy(hw); } /**************************/ /* fill fifo with TX data */ /**************************/ void hfcsmini_write_fifo(hfcsmini_hw *hw, __u8 channel) { __u8 fcnt, tcnt, i; __u8 free; __u8 f1, f2; __u8 fstat; __u8 *data; int remain; channel_t *ch = &hw->chan[channel]; send_buffer: if (!ch->tx_skb) return; remain = ch->tx_skb->len - ch->tx_idx; if (remain <= 0) return; hfcsmini_selfifo(hw, (channel * 2)); free = (hw->max_z - (read_hfcsmini_stable(hw, A_USAGE))); tcnt = (free >= remain) ? remain : free; fstat = read_hfcsmini(hw, R_ST_RD_STA); f1 = read_hfcsmini_stable(hw, A_F1); f2 = read_hfcsmini(hw, A_F2); fcnt = 0x07 - ((f1 - f2) & 0x07); /* free frame count in tx fifo */ if (debug & DEBUG_HFC_FIFO) { mISDN_debugprint(&ch->inst, "%s channel(%i) len(%i) idx(%i) f1(%i) " "f2(%i) fcnt(%i) tcnt(%i) free(%i) fstat(%i)", __FUNCTION__, channel, ch->tx_skb->len, ch->tx_idx, f1, f2, fcnt, tcnt, free, fstat); } if (free && fcnt && tcnt) { data = ch->tx_skb->data + ch->tx_idx; ch->tx_idx += tcnt; if (debug & DEBUG_HFC_FIFO) { printk(KERN_DEBUG "%s channel(%i) writing: ", hw->card_name, channel); } i = tcnt; /* write data to Fifo */ while (i--) { if (debug & DEBUG_HFC_FIFO) printk("%02x ", *data); write_hfcsmini(hw, A_FIFO_DATA, *data++); } if (debug & DEBUG_HFC_FIFO) printk("\n"); if (ch->tx_idx == ch->tx_skb->len) { if (test_bit(FLG_HDLC, &ch->Flags)) { /* terminate frame */ hfcsmini_inc_f(hw); } else { hfcsmini_selfifo(hw, (channel * 2)); } if (debug & DEBUG_HFC_BTRACE) mISDN_debugprint(&ch->inst, "TX frame channel(%i) completed", channel); if (next_tx_frame(hw, channel)) { if (debug & DEBUG_HFC_BTRACE) mISDN_debugprint(&ch->inst, "channel(%i) has next_tx_frame", channel); if ((free - tcnt) > 8) { if (debug & DEBUG_HFC_BTRACE) mISDN_debugprint(&ch->inst, "channel(%i) continue B-TX immediatetly", channel); goto send_buffer; } } } else { /* tx buffer not complete, but fifo filled to maximum */ hfcsmini_selfifo(hw, (channel * 2)); } } } /****************************/ /* read RX data out of fifo */ /****************************/ void hfcsmini_read_fifo(hfcsmini_hw *hw, __u8 channel) { __u8 f1 = 0, f2 = 0, z1, z2; __u8 fstat = 0; int i; int rcnt; /* read rcnt bytes out of fifo */ __u8 *data; /* new data pointer */ struct sk_buff *skb; /* data buffer for upper layer */ channel_t *ch = &hw->chan[channel]; receive_buffer: hfcsmini_selfifo(hw, (channel * 2) + 1); if (test_bit(FLG_HDLC, &ch->Flags)) { /* hdlc rcnt */ f1 = read_hfcsmini_stable(hw, A_F1); f2 = read_hfcsmini(hw, A_F2); z1 = read_hfcsmini_stable(hw, A_Z1); z2 = read_hfcsmini(hw, A_Z2); fstat = read_hfcsmini(hw, R_ST_RD_STA); rcnt = (z1 - z2) & hw->max_z; if (f1 != f2) rcnt++; } else { /* transparent rcnt */ rcnt = read_hfcsmini_stable(hw, A_USAGE) - 1; f1=f2=z1=z2=0; } if (debug & DEBUG_HFC_FIFO) { if (ch->rx_skb) i = ch->rx_skb->len; else i = 0; mISDN_debugprint(&ch->inst, "reading %i bytes channel(%i) " "irq_cnt(%i) fstat(%i) idx(%i) f1(%i) f2(%i) z1(%i) z2(%i)", rcnt, channel, hw->irq_cnt, fstat, i, f1, f2, z1, z2); } if (rcnt > 0) { if (!ch->rx_skb) { ch->rx_skb = alloc_stack_skb(ch->maxlen + 3, ch->up_headerlen); if (!ch->rx_skb) { printk(KERN_DEBUG "%s: No mem for rx_skb\n", __FUNCTION__); return; } } data = skb_put(ch->rx_skb, rcnt); /* read data from FIFO*/ while (rcnt--) *data++ = read_hfcsmini(hw, A_FIFO_DATA); } else return; if (test_bit(FLG_HDLC, &ch->Flags)) { if (f1 != f2) { hfcsmini_inc_f(hw); /* check minimum frame size */ if (ch->rx_skb->len < 4) { if (debug & DEBUG_HFC_FIFO_ERR) mISDN_debugprint(&ch->inst, "%s: frame in channel(%i) < minimum size", __FUNCTION__, channel); goto read_exit; } /* check crc */ if (ch->rx_skb->data[ch->rx_skb->len - 1]) { if (debug & DEBUG_HFC_FIFO_ERR) mISDN_debugprint(&ch->inst, "%s: channel(%i) CRC-error", __FUNCTION__, channel); goto read_exit; } /* remove cksum */ skb_trim(ch->rx_skb, ch->rx_skb->len - 3); if (ch->rx_skb->len < MISDN_COPY_SIZE) { skb = alloc_stack_skb(ch->rx_skb->len, ch->up_headerlen); if (skb) { memcpy(skb_put(skb, ch->rx_skb->len), ch->rx_skb->data, ch->rx_skb->len); skb_trim(ch->rx_skb, 0); } else { skb = ch->rx_skb; ch->rx_skb = NULL; } } else { skb = ch->rx_skb; ch->rx_skb = NULL; } if ((ch->debug) && (debug & DEBUG_HFC_DTRACE)) { mISDN_debugprint(&ch->inst, "channel(%i) new RX len(%i): ", channel, skb->len); i = 0; printk(" "); while (i < skb->len) printk("%02x ", skb->data[i++]); printk("\n"); } queue_ch_frame(ch, INDICATION, MISDN_ID_ANY, skb); read_exit: if (ch->rx_skb) skb_trim(ch->rx_skb, 0); if (read_hfcsmini_stable(hw, A_USAGE) > 8) { if (debug & DEBUG_HFC_FIFO) mISDN_debugprint(&ch->inst, "%s: channel(%i) continue hfcsmini_read_fifo", __FUNCTION__, channel); goto receive_buffer; } return; } else { hfcsmini_selfifo(hw, (channel * 2) + 1); } } else { /* transparent data */ hfcsmini_selfifo(hw, (channel * 2) + 1); if (ch->rx_skb->len >= 128) { /* deliver transparent data to layer2 */ queue_ch_frame(ch, INDICATION, MISDN_ID_ANY, ch->rx_skb); ch->rx_skb = NULL; } } } /*************************************/ /* bottom half handler for interrupt */ /*************************************/ static void hfcsmini_bh_handler(unsigned long ul_hw) { hfcsmini_hw *hw = (hfcsmini_hw *) ul_hw; reg_r_st_rd_sta state; int i; /* Timer Int */ if (hw->misc_irq.bit.v_ti_irq) { hw->misc_irq.bit.v_ti_irq = 0; /* add Fifo-Fill info into int_s1 bitfield */ hw->fifo_irq.reg |= ((read_hfcsmini(hw, R_FILL) ^ FIFO_MASK_TX) & hw->fifomask); /* Handle TX Fifos */ for (i = 0; i < hw->max_fifo; i++) { if ((1 << (i * 2)) & (hw->fifo_irq.reg)) { hw->fifo_irq.reg &= ~(1 << (i * 2)); if (test_bit(FLG_TX_BUSY, &hw->chan[i].Flags)) hfcsmini_write_fifo(hw, i); } } /* handle NT Timer */ if ((hw->portmode & PORT_MODE_NT) && (hw->portmode & NT_TIMER)) if ((--hw->nt_timer) < 0) s0_new_state(&hw->chan[2]); } /* Handle RX Fifos */ for (i = 0; i < hw->max_fifo; i++) { if ((1 << (i * 2 + 1)) & (hw->fifo_irq.reg)) { hw->fifo_irq.reg &= ~(1 << (i * 2 + 1)); hfcsmini_read_fifo(hw, i); } } /* state machine IRQ */ if (hw->misc_irq.bit.v_st_irq) { hw->misc_irq.bit.v_st_irq = 0; state.reg = read_hfcsmini(hw, R_ST_RD_STA); /* mISDN_debugprint(&dch->inst, "new_l1_state(0x%02x)", state.bit.v_st_sta); */ if (state.bit.v_st_sta != hw->chan[2].state) { hw->chan[2].state = state.bit.v_st_sta; s0_new_state(&hw->chan[2]); } } return; } /*********************/ /* Interrupt handler */ /*********************/ static irqreturn_t hfcsmini_interrupt(int intno, void *dev_id, struct pt_regs *regs) { __u8 fifo_irq, misc_irq; hfcsmini_hw *hw = dev_id; spin_lock(&hw->rlock); if (!(hw->misc_irqmsk.bit.v_irq_en)) { if (!(hw->testirq)) printk(KERN_INFO "%s %s GLOBAL INTERRUPT DISABLED\n", hw->card_name, __FUNCTION__); spin_unlock(&hw->rlock); return IRQ_NONE; } fifo_irq = read_hfcsmini_irq(hw, R_FIFO_IRQ) & hw->fifo_irqmsk.reg; misc_irq = read_hfcsmini_irq(hw, R_MISC_IRQ) & hw->misc_irqmsk.reg; if (!fifo_irq && !misc_irq) { spin_unlock(&hw->rlock); return IRQ_NONE; /* other hardware interrupted */ } hw->irq_cnt++; hw->fifo_irq.reg |= fifo_irq; hw->misc_irq.reg |= misc_irq; /* queue bottom half */ if (!(hw->testirq)) { tasklet_schedule(&hw->tasklet); } spin_unlock(&hw->rlock); return IRQ_HANDLED; } /*************************************/ /* free memory for all used channels */ /*************************************/ void release_channels(hfcsmini_hw * hw) { int i = 0; while (i < MAX_CHAN) { if (hw->chan[i].Flags) { if (debug & DEBUG_HFC_INIT) printk(KERN_DEBUG "%s %s: free channel %d\n", hw->card_name, __FUNCTION__, i); mISDN_freechannel(&hw->chan[i]); hw_mISDNObj.ctrl(&hw->chan[i].inst, MGR_UNREGLAYER | REQUEST, NULL); } i++; } } /******************************************/ /* Setup Fifo using HDLC_PAR and CON_HDLC */ /******************************************/ void setup_fifo(hfcsmini_hw * hw, int fifo, __u8 hdlcreg, __u8 con_reg, __u8 irq_enable, __u8 enable) { if (enable) /* mark fifo to be 'in use' */ hw->fifomask |= (1 << fifo); else hw->fifomask &= ~(1 << fifo); if (irq_enable) hw->fifo_irqmsk.reg |= (1 << fifo); else hw->fifo_irqmsk.reg &= ~(1 << fifo); write_hfcsmini(hw, R_FIFO_IRQMSK, hw->fifo_irqmsk.reg); hfcsmini_selfifo(hw, fifo); write_hfcsmini(hw, A_HDLC_PAR, hdlcreg); write_hfcsmini(hw, A_CON_HDLC, con_reg); hfcsmini_resetfifo(hw); } /*************************************************/ /* Setup ST interface, enable/disable B-Channels */ /*************************************************/ void setup_st(hfcsmini_hw * hw, __u8 bc, __u8 enable) { if (!((bc == 0) || (bc == 1))) { printk(KERN_INFO "%s %s: ERROR: bc(%i) unvalid!\n", hw->card_name, __FUNCTION__, bc); return; } if (bc) { hw->st_ctrl0.bit.v_b2_en = (enable?1:0); hw->st_ctrl2.bit.v_b2_rx_en = (enable?1:0); } else { hw->st_ctrl0.bit.v_b1_en = (enable?1:0); hw->st_ctrl2.bit.v_b1_rx_en = (enable?1:0); } write_hfcsmini(hw, R_ST_CTRL0, hw->st_ctrl0.reg); write_hfcsmini(hw, R_ST_CTRL2, hw->st_ctrl2.reg); if (debug & DEBUG_HFC_MODE) { printk(KERN_INFO "%s %s: bc(%i) %s, R_ST_CTRL0(0x%02x) R_ST_CTRL2(0x%02x)\n", hw->card_name, __FUNCTION__, bc, enable?"enable":"disable", hw->st_ctrl0.reg, hw->st_ctrl2.reg); } } /*********************************************/ /* (dis-) connect D/B-Channel using protocol */ /*********************************************/ int setup_channel(hfcsmini_hw *hw, __u8 channel, int protocol) { if (test_bit(FLG_BCHANNEL, &hw->chan[channel].Flags)) { if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&hw->chan[channel].inst, "channel(%i) protocol %x-->%x", channel, hw->chan[channel].state, protocol); switch (protocol) { case (-1): /* used for init */ hw->chan[channel].state = -1; hw->chan[channel].channel = channel; /* fall trough */ case (ISDN_PID_NONE): if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&hw->chan[channel].inst, "ISDN_PID_NONE"); if (hw->chan[channel].state == ISDN_PID_NONE) return (0); /* already in idle state */ hw->chan[channel].state = ISDN_PID_NONE; /* B-TX */ setup_fifo(hw, (channel << 1), 0, 0, FIFO_IRQ_OFF, FIFO_DISABLE); /* B-RX */ setup_fifo(hw, (channel << 1) + 1, 0, 0, FIFO_IRQ_OFF, FIFO_DISABLE); setup_st(hw, channel, 0); test_and_clear_bit(FLG_HDLC, &hw->chan[channel].Flags); test_and_clear_bit(FLG_TRANSPARENT, &hw->chan[channel].Flags); break; case (ISDN_PID_L1_B_64TRANS): if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&hw->chan[channel].inst, "ISDN_PID_L1_B_64TRANS"); /* B-TX */ setup_fifo(hw, (channel << 1), HDLC_PAR_BCH, CON_HDLC_B_TRANS, FIFO_IRQ_OFF, FIFO_ENABLE); /* B-RX */ setup_fifo(hw, (channel << 1) + 1, HDLC_PAR_BCH, CON_HDLC_B_TRANS, FIFO_IRQ_OFF, FIFO_ENABLE); setup_st(hw, channel, 1); hw->chan[channel].state = ISDN_PID_L1_B_64TRANS; test_and_set_bit(FLG_TRANSPARENT, &hw->chan[channel].Flags); break; case (ISDN_PID_L1_B_64HDLC): if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&hw->chan[channel].inst, "ISDN_PID_L1_B_64HDLC"); /* B-TX */ setup_fifo(hw, (channel << 1), HDLC_PAR_BCH, CON_HDLC_B_HDLC, FIFO_IRQ_OFF, FIFO_ENABLE); /* B-RX */ setup_fifo(hw, (channel << 1) + 1, HDLC_PAR_BCH, CON_HDLC_B_HDLC, FIFO_IRQ_ON, FIFO_ENABLE); setup_st(hw, channel, 1); hw->chan[channel].state = ISDN_PID_L1_B_64HDLC; test_and_set_bit(FLG_HDLC, &hw->chan[channel].Flags); break; default: mISDN_debugprint(&hw->chan[channel].inst, "prot not known %x", protocol); return (-ENOPROTOOPT); } return (0); } if (test_bit(FLG_DCHANNEL, &hw->chan[channel].Flags)) { if (debug & DEBUG_HFC_MODE) mISDN_debugprint(&hw->chan[channel].inst, "D channel(%i) protocol(%i)",channel, protocol); /* init the D-channel fifos */ /* D-TX */ setup_fifo(hw, (channel << 1), HDLC_PAR_DCH, CON_HDLC_D_HDLC, FIFO_IRQ_OFF, FIFO_ENABLE); /* D-RX */ setup_fifo(hw, (channel << 1) + 1, HDLC_PAR_DCH, CON_HDLC_D_HDLC, FIFO_IRQ_ON, FIFO_DISABLE); return (0); } printk(KERN_INFO "%s %s ERROR: channel(%i) is NEITHER B nor D !!!\n", hw->card_name, __FUNCTION__, channel); return (-1); } /*****************************************************/ /* register ISDN stack for one HFC-S mini instance */ /* - register all ports and channels */ /* - set param_idx */ /* */ /* channel mapping in mISDN in hw->chan[] */ /* 0=B1, 1=B2, 2=D, 3=PCM */ /*****************************************************/ int init_mISDN_channels(hfcsmini_hw * hw) { int err; int ch; int b; mISDN_pid_t pid; u_long flags; /* clear PCM */ memset(&hw->chan[3], 0, sizeof(channel_t)); /* init D channels */ ch = 2; if (debug & DEBUG_HFC_INIT) printk(KERN_INFO "%s %s: Registering D-channel, card(%d) protocol(%x)\n", hw->card_name, __FUNCTION__, hw->cardnum, hw->dpid); memset(&hw->chan[ch], 0, sizeof(channel_t)); hw->chan[ch].channel = ch; hw->chan[ch].debug = debug; hw->chan[ch].inst.obj = &hw_mISDNObj; hw->chan[ch].inst.hwlock = &hw->mlock; hw->chan[ch].inst.class_dev.dev = &hw->pdev->dev; mISDN_init_instance(&hw->chan[ch].inst, &hw_mISDNObj, hw, hfcsmini_l2l1); hw->chan[ch].inst.pid.layermask = ISDN_LAYER(0); sprintf(hw->chan[ch].inst.name, "%s", hw->card_name); err = mISDN_initchannel(&hw->chan[ch], MSK_INIT_DCHANNEL, MAX_DFRAME_LEN_L1); if (err) goto free_channels; hw->chan[ch].hw = hw; /* init B channels */ for (b = 0; b < 2; b++) { if (debug & DEBUG_HFC_INIT) printk(KERN_DEBUG "%s %s: Registering B-channel, card(%d) " "ch(%d)\n", hw->card_name, __FUNCTION__, hw->cardnum, b); memset(&hw->chan[b], 0, sizeof(channel_t)); hw->chan[b].channel = b; hw->chan[b].debug = debug; mISDN_init_instance(&hw->chan[b].inst, &hw_mISDNObj, hw, hfcsmini_l2l1); hw->chan[b].inst.pid.layermask = ISDN_LAYER(0); hw->chan[b].inst.hwlock = &hw->mlock; hw->chan[b].inst.class_dev.dev = &hw->pdev->dev; sprintf(hw->chan[b].inst.name, "%s B%d", hw->chan[ch].inst.name, b + 1); if (mISDN_initchannel(&hw->chan[b], MSK_INIT_BCHANNEL, MAX_DATA_MEM)) { err = -ENOMEM; goto free_channels; } hw->chan[b].hw = hw; } mISDN_set_dchannel_pid(&pid, hw->dpid, layermask[hw->param_idx]); /* set protocol for NT/TE */ if (hw->portmode & PORT_MODE_NT) { /* NT-mode */ hw->portmode |= NT_TIMER; hw->nt_timer = 0; hw->chan[ch].inst.pid.protocol[0] = ISDN_PID_L0_NT_S0; hw->chan[ch].inst.pid.protocol[1] = ISDN_PID_L1_NT_S0; pid.protocol[0] = ISDN_PID_L0_NT_S0; pid.protocol[1] = ISDN_PID_L1_NT_S0; hw->chan[ch].inst.pid.layermask |= ISDN_LAYER(1); pid.layermask |= ISDN_LAYER(1); if (layermask[hw->param_idx] & ISDN_LAYER(2)) pid.protocol[2] = ISDN_PID_L2_LAPD_NET; } else { /* TE-mode */ hw->portmode |= PORT_MODE_TE; hw->chan[ch].inst.pid.protocol[0] = ISDN_PID_L0_TE_S0; pid.protocol[0] = ISDN_PID_L0_TE_S0; } if (debug & DEBUG_HFC_INIT) printk(KERN_INFO "%s %s: registering Stack\n", hw->card_name, __FUNCTION__); /* register stack */ err = hw_mISDNObj.ctrl(NULL, MGR_NEWSTACK | REQUEST, &hw->chan[ch].inst); if (err) { printk(KERN_ERR "%s %s: MGR_NEWSTACK | REQUEST err(%d)\n", hw->card_name, __FUNCTION__, err); goto free_channels; } hw->chan[ch].state = 0; for (b = 0; b < 2; b++) { err = hw_mISDNObj.ctrl(hw->chan[ch].inst.st, MGR_NEWSTACK | REQUEST, &hw->chan[b].inst); if (err) { printk(KERN_ERR "%s %s: MGR_ADDSTACK bchan error %d\n", hw->card_name, __FUNCTION__, err); goto free_stack; } } err = hw_mISDNObj.ctrl(hw->chan[ch].inst.st, MGR_SETSTACK | REQUEST, &pid); if (err) { printk(KERN_ERR "%s %s: MGR_SETSTACK REQUEST dch err(%d)\n", hw->card_name, __FUNCTION__, err); hw_mISDNObj.ctrl(hw->chan[ch].inst.st, MGR_DELSTACK | REQUEST, NULL); goto free_stack; } setup_channel(hw, hw->chan[ch].channel, -1); for (b = 0; b < 2; b++) { setup_channel(hw, b, -1); } /* delay some time */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ hw_mISDNObj.ctrl(hw->chan[ch].inst.st, MGR_CTRLREADY | INDICATION, NULL); return (0); free_stack: hw_mISDNObj.ctrl(hw->chan[ch].inst.st, MGR_DELSTACK | REQUEST, NULL); free_channels: spin_lock_irqsave(&hw_mISDNObj.lock, flags); release_channels(hw); list_del(&hw->list); spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); return (err); } /********************************/ /* parse module paramaters like */ /* NE/TE and S0/Up port mode */ /********************************/ void parse_module_params(hfcsmini_hw * hw) { /* D-Channel protocol: (2=DSS1) */ hw->dpid = (protocol[hw->param_idx] & 0x0F); if (hw->dpid == 0) { printk(KERN_INFO "%s %s: WARNING: wrong value for protocol[%i], " "assuming 0x02 (DSS1)...\n", hw->card_name, __FUNCTION__, hw->param_idx); hw->dpid = 0x02; } /* Line Interface TE or NT */ if (protocol[hw->param_idx] & 0x10) hw->portmode |= PORT_MODE_NT; else hw->portmode |= PORT_MODE_TE; /* Line Interface in S0 or Up mode */ if (!(protocol[hw->param_idx] & 0x40)) hw->portmode |= PORT_MODE_BUS_MASTER; /* link B-channel loop */ if (protocol[hw->param_idx] & 0x80) hw->portmode |= PORT_MODE_LOOP; if (debug & DEBUG_HFC_INIT) printk ("%s %s: protocol[%i]=0x%02x, dpid=%d,%s bus-mode:%s %s\n", hw->card_name, __FUNCTION__, hw->param_idx, protocol[hw->param_idx], hw->dpid, (hw->portmode & PORT_MODE_TE)?"TE":"NT", (hw->portmode & PORT_MODE_BUS_MASTER)?"MASTER":"SLAVE", (hw->portmode & PORT_MODE_LOOP)?"B-LOOP":"" ); } /*****************************************/ /* initialise the HFC-S mini ISDN Chip */ /* return 0 on success. */ /*****************************************/ int init_hfcsmini(hfcsmini_hw * hw) { int err = 0; reg_r_fifo_thres threshold; #if HFCBRIDGE == BRIDGE_HFCPCI err = init_pci_bridge(hw); if (err) return(-ENODEV); #endif hw->chip_id.reg = read_hfcsmini(hw, R_CHIP_ID); if (debug & DEBUG_HFC_INIT) printk(KERN_INFO "%s %s ChipID: 0x%x\n", hw->card_name, __FUNCTION__, hw->chip_id.bit.v_chip_id); switch (hw->chip_id.bit.v_chip_id) { case CHIP_ID_HFCSMINI: hw->max_fifo = 4; hw->ti.reg = 5; /* 8 ms timer interval */ hw->max_z = 0x7F; break; default: err = -ENODEV; } if (err) { if (debug & DEBUG_HFC_INIT) printk(KERN_ERR "%s %s: unkown Chip ID 0x%x\n", hw->card_name, __FUNCTION__, hw->chip_id.bit.v_chip_id); return (err); } /* reset card */ write_hfcsmini(hw, R_CIRM, M_SRES); /* Reset On */ udelay(10); write_hfcsmini(hw, R_CIRM, 0); /* Reset Off */ /* wait until fifo controller init sequence is finished */ hfcsmini_waitbusy(hw); /* reset D-Channel S/T controller */ write_hfcsmini(hw, R_ST_CTRL1, M_D_RES); if (hw->portmode & PORT_MODE_TE) { /* TE mode */ hw->st_ctrl0.reg = 0; write_hfcsmini(hw, R_ST_CLK_DLY, (M1_ST_CLK_DLY* 0xF)); write_hfcsmini(hw, R_ST_CTRL1, 0); } else { /* NT mode */ hw->st_ctrl0.reg = 4; write_hfcsmini(hw, R_ST_CLK_DLY, ((M1_ST_SMPL * 0x6) | (M1_ST_CLK_DLY*0xC))); write_hfcsmini(hw, R_ST_CTRL1, M_E_IGNO); } hw->st_ctrl2.reg = 0; write_hfcsmini(hw, R_ST_CTRL0, hw->st_ctrl0.reg); write_hfcsmini(hw, R_ST_CTRL2, hw->st_ctrl2.reg); /* HFC Master/Slave Mode */ if (hw->portmode & PORT_MODE_BUS_MASTER) hw->pcm_md0.bit.v_pcm_md = 1; else hw->pcm_md0.bit.v_pcm_md = 0; write_hfcsmini(hw, R_PCM_MD0, hw->pcm_md0.reg); write_hfcsmini(hw, R_PCM_MD1, 0); write_hfcsmini(hw, R_PCM_MD2, 0); /* setup threshold register */ threshold.bit.v_thres_tx = (HFCSMINI_TX_THRESHOLD / 8); threshold.bit.v_thres_rx = (HFCSMINI_RX_THRESHOLD / 8); write_hfcsmini(hw, R_FIFO_THRES, threshold.reg); /* test timer irq */ enable_interrupts(hw); mdelay(((1 << hw->ti.reg)+1)*2); hw->testirq = 0; if (hw->irq_cnt) { printk(KERN_INFO "%s %s: test IRQ OK, irq_cnt %i\n", hw->card_name, __FUNCTION__, hw->irq_cnt); disable_interrupts(hw); return (0); } else { if (debug & DEBUG_HFC_INIT) printk(KERN_INFO "%s %s: ERROR getting IRQ (irq_cnt %i)\n", hw->card_name, __FUNCTION__, hw->irq_cnt); disable_interrupts(hw); free_irq(hw->irq, hw); return (-EIO); } } /*****************************************************/ /* disable all interrupts by disabling M_GLOB_IRQ_EN */ /*****************************************************/ void disable_interrupts(hfcsmini_hw * hw) { u_long flags; if (debug & DEBUG_HFC_IRQ) printk(KERN_INFO "%s %s\n", hw->card_name, __FUNCTION__); spin_lock_irqsave(&hw->mlock, flags); hw->fifo_irqmsk.reg = 0; hw->misc_irqmsk.reg = 0; write_hfcsmini(hw, R_FIFO_IRQMSK, hw->fifo_irqmsk.reg); write_hfcsmini(hw, R_MISC_IRQMSK, hw->misc_irqmsk.reg); spin_unlock_irqrestore(&hw->mlock, flags); } /******************************************/ /* start interrupt and set interrupt mask */ /******************************************/ void enable_interrupts(hfcsmini_hw * hw) { u_long flags; if (debug & DEBUG_HFC_IRQ) printk(KERN_INFO "%s %s\n", hw->card_name, __FUNCTION__); spin_lock_irqsave(&hw->mlock, flags); hw->fifo_irq.reg = 0; hw->misc_irq.reg = 0; write_hfcsmini(hw, R_TI, hw->ti.reg); /* D-RX and D-TX interrupts enable */ hw->fifo_irqmsk.bit.v_fifo2_tx_irqmsk = 1; hw->fifo_irqmsk.bit.v_fifo2_rx_irqmsk = 1; /* clear pending ints */ if (read_hfcsmini(hw, R_FIFO_IRQ)); if (read_hfcsmini(hw, R_MISC_IRQ)); /* Finally enable IRQ output */ hw->misc_irqmsk.bit.v_st_irqmsk = 1; /* enable L1-state change irq */ hw->misc_irqmsk.bit.v_ti_irqmsk = 1; /* enable timer irq */ hw->misc_irqmsk.bit.v_irq_en = 1; /* IRQ global enable */ write_hfcsmini(hw, R_MISC_IRQMSK, hw->misc_irqmsk.reg); spin_unlock_irqrestore(&hw->mlock, flags); return; } /**************************************/ /* initialise the HFC-S mini hardware */ /* return 0 on success. */ /**************************************/ static int __devinit setup_instance(hfcsmini_hw * hw) { int err; hfcsmini_hw *previous_hw; u_long flags; if (debug & DEBUG_HFC_INIT) printk(KERN_WARNING "%s %s\n", hw->card_name, __FUNCTION__); spin_lock_init(&hw->mlock); spin_lock_init(&hw->rlock); tasklet_init(&hw->tasklet, hfcsmini_bh_handler, (unsigned long) hw); /* search previous instances to index protocol[] array */ list_for_each_entry(previous_hw, &hw_mISDNObj.ilist, list) hw->param_idx++; /* add this instance to hardware list */ spin_lock_irqsave(&hw_mISDNObj.lock, flags); list_add_tail(&hw->list, &hw_mISDNObj.ilist); spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); /* init interrupt engine */ hw->testirq = 1; if (debug & DEBUG_HFC_INIT) printk(KERN_WARNING "%s %s: requesting IRQ %d\n", hw->card_name, __FUNCTION__, hw->irq); if (request_irq(hw->irq, hfcsmini_interrupt, SA_SHIRQ, "HFC-S mini", hw)) { printk(KERN_WARNING "%s %s: couldn't get interrupt %d\n", hw->card_name, __FUNCTION__, hw->irq); hw->irq = 0; err = -EIO; goto out; } parse_module_params(hw); err = init_hfcsmini(hw); if (err) goto out; /* register all channels at ISDN procol stack */ err = init_mISDN_channels(hw); if (err) goto out; /* delay some time to have mISDN initialazed complete */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ /* Clear already pending ints */ if (read_hfcsmini(hw, R_FIFO_IRQ)); enable_interrupts(hw); /* enable state machine */ write_hfcsmini(hw, R_ST_RD_STA, 0x0); return(0); out: return (err); } #if HFCBRIDGE == BRIDGE_HFCPCI /***********************/ /* PCI Bridge ID List */ /***********************/ static struct pci_device_id hfcsmini_ids[] = { {.vendor = PCI_VENDOR_ID_CCD, .device = 0xA001, .subvendor = PCI_VENDOR_ID_CCD, .subdevice = 0xFFFF, .driver_data = (unsigned long) &((hfcsmini_param) {0xFF, "HFC-S mini Evaluation Board"}), }, {} }; /******************************/ /* initialise the PCI Bridge */ /* return 0 on success. */ /******************************/ int init_pci_bridge(hfcsmini_hw * hw) { outb(0x58, hw->iobase + 4); /* ID-register of bridge */ if ((inb(hw->iobase) & 0xf0) != 0x30) { printk(KERN_INFO "%s %s: chip ID for PCI bridge invalid\n", hw->card_name, __FUNCTION__); release_region(hw->iobase, 8); return(-EIO); } outb(0x60, hw->iobase + 4); /* CIRM register of bridge */ outb(0x07, hw->iobase); /* 15 PCI clocks aux access */ /* reset sequence */ outb(2, hw->iobase + 3); /* A0 = 1, reset = 0 (active) */ udelay(10); outb(6, hw->iobase + 3); /* A0 = 1, reset = 1 (inactive) */ outb(0, hw->iobase + 1); /* write dummy register number */ /* wait until reset sequence finished, can be redefined after schematic review */ mdelay(300); return (0); } /************************/ /* release single card */ /************************/ static void release_card(hfcsmini_hw * hw) { u_long flags; disable_interrupts(hw); free_irq(hw->irq, hw); /* wait for pending tasklet to finish */ set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ spin_lock_irqsave(&hw_mISDNObj.lock, flags); release_channels(hw); list_del(&hw->list); spin_unlock_irqrestore(&hw_mISDNObj.lock, flags); kfree(hw); } /*****************************************/ /* PCI hotplug interface: probe new card */ /*****************************************/ static int __devinit hfcsmini_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { hfcsmini_param *driver_data = (hfcsmini_param *) ent->driver_data; hfcsmini_hw *hw; int err = -ENOMEM; if (!(hw = kmalloc(sizeof(hfcsmini_hw), GFP_ATOMIC))) { printk(KERN_ERR "%s %s: No kmem for HFC-S mini card\n", hw->card_name, __FUNCTION__); return (err); } memset(hw, 0, sizeof(hfcsmini_hw)); hw->pdev = pdev; err = pci_enable_device(pdev); if (err) goto out; hw->cardnum = card_cnt; sprintf(hw->card_name, "%s_%d", DRIVER_NAME, hw->cardnum); printk(KERN_INFO "%s %s: adapter '%s' found on PCI bus %02x dev %02x\n", hw->card_name, __FUNCTION__, driver_data->device_name, pdev->bus->number, pdev->devfn); hw->driver_data = *driver_data; hw->irq = pdev->irq; hw->iobase = (u_int) get_pcibase(pdev, 0); if (!hw->iobase) { printk(KERN_WARNING "%s no IO for PCI card found\n", hw->card_name); return(-EIO); } if (!request_region(hw->iobase, 8, "hfcmulti")) { printk(KERN_WARNING "%s failed to request " "address space at 0x%04x\n", hw->card_name, hw->iobase); } printk(KERN_INFO "%s defined at IOBASE 0x%#x IRQ %d HZ %d\n", hw->card_name, (u_int) hw->iobase, hw->irq, HZ); /* enable IO */ pci_write_config_word(pdev, PCI_COMMAND, 0x01); pci_set_drvdata(pdev, hw); err = setup_instance(hw); if (!err) { card_cnt++; return (0); } else { goto out; } out: kfree(hw); return (err); }; /**************************************/ /* PCI hotplug interface: remove card */ /**************************************/ static void __devexit hfcsmini_pci_remove(struct pci_dev *pdev) { hfcsmini_hw *hw = pci_get_drvdata(pdev); printk(KERN_INFO "%s %s: removing card\n", hw->card_name, __FUNCTION__); release_card(hw); card_cnt--; pci_disable_device(pdev); return; }; /*****************************/ /* Module PCI driver exports */ /*****************************/ static struct pci_driver hfcsmini_driver = { name:DRIVER_NAME, probe:hfcsmini_pci_probe, remove:__devexit_p(hfcsmini_pci_remove), id_table:hfcsmini_ids, }; MODULE_DEVICE_TABLE(pci, hfcsmini_ids); #endif /***************/ /* Module init */ /***************/ static int __init hfcsmini_init(void) { int err; printk(KERN_INFO "HFC-S mini: %s driver Rev. %s (debug=%i)\n", __FUNCTION__, mISDN_getrev(hfcsmini_rev), debug); #ifdef MODULE hw_mISDNObj.owner = THIS_MODULE; #endif INIT_LIST_HEAD(&hw_mISDNObj.ilist); spin_lock_init(&hw_mISDNObj.lock); hw_mISDNObj.name = DRIVER_NAME; hw_mISDNObj.own_ctrl = hfcsmini_manager; hw_mISDNObj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 | ISDN_PID_L0_NT_S0; hw_mISDNObj.DPROTO.protocol[1] = ISDN_PID_L1_NT_S0; hw_mISDNObj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS | ISDN_PID_L1_B_64HDLC; hw_mISDNObj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS | ISDN_PID_L2_B_RAWDEV; card_cnt = 0; if ((err = mISDN_register(&hw_mISDNObj))) { printk(KERN_ERR "HFC-S mini: can't register HFC-S mini, error(%d)\n", err); goto out; } #if HFCBRIDGE == BRIDGE_HFCPCI err = pci_register_driver(&hfcsmini_driver); if (err < 0) { goto out; } #if !defined(CONFIG_HOTPLUG) if (err == 0) { err = -ENODEV; pci_unregister_driver(&hfcsmini_driver); goto out; } #endif #endif printk(KERN_INFO "HFC-S mini: %d cards installed\n", card_cnt); return 0; out: return (err); } static void __exit hfcsmini_cleanup(void) { int err; #if HFCBRIDGE == BRIDGE_HFCPCI pci_unregister_driver(&hfcsmini_driver); #endif if ((err = mISDN_unregister(&hw_mISDNObj))) { printk(KERN_ERR "HFC-S mini: can't unregister HFC-S mini, error(%d)\n", err); } printk(KERN_INFO "%s: driver removed\n", __FUNCTION__); } module_init(hfcsmini_init); module_exit(hfcsmini_cleanup);