summaryrefslogtreecommitdiff
path: root/drivers/dahdi/zaphfc/base.c
diff options
context:
space:
mode:
authorJose A. Deniz <odicha@hotmail.com>2009-08-06 23:35:42 +0000
committerTzafrir Cohen <tzafrir@cohens.org.il>2010-01-05 20:46:30 +0200
commit68336b33f33424c243ea99b5560cfcc984334291 (patch)
treea9efb17fbdd0ef2484fea67f46715a8c3eb88c0e /drivers/dahdi/zaphfc/base.c
parenta4ee69b5300fddb961c6f0c521d204d849e52a0c (diff)
Initial state. It works but a lot of stuff to clean at code
git-svn-id: http://zaphfc.googlecode.com/svn/branches/2.2@2 6b77f504-82de-11de-a8c8-95b3e4aa02d0
Diffstat (limited to 'drivers/dahdi/zaphfc/base.c')
-rw-r--r--drivers/dahdi/zaphfc/base.c1538
1 files changed, 1538 insertions, 0 deletions
diff --git a/drivers/dahdi/zaphfc/base.c b/drivers/dahdi/zaphfc/base.c
new file mode 100644
index 0000000..0ec8b60
--- /dev/null
+++ b/drivers/dahdi/zaphfc/base.c
@@ -0,0 +1,1538 @@
+/*
+ * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards
+ *
+ * Rewrite of d_chans in hardhdlc mode - May 2009
+ * Jose A. Deniz <odicha@hotmail.com>
+ *
+ * Copyright (C) 2006, headiisue GmbH; Jens Wilke
+ * Copyright (C) 2004 Daniele Orlandi
+ * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH
+ *
+ * Jens Wilke <jw_vzaphfc@headissue.com>
+ *
+ * Original author of this code is
+ * Daniele "Vihai" Orlandi <daniele@orlandi.com>
+ *
+ * Major rewrite of the driver made by
+ * Klaus-Peter Junghanns <kpj@junghanns.net>
+ *
+ * This program is free software and may be modified and
+ * distributed under the terms of the GNU Public License.
+ *
+ * Please read the README file for important infos.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+
+#include <dahdi/kernel.h>
+
+#include "zaphfc.h"
+#include "fifo.h"
+
+#if CONFIG_PCI
+
+#define DAHDI_B1 0
+#define DAHDI_B2 1
+#define DAHDI_D 2
+
+#define D 0
+#define B1 1
+#define B2 2
+
+static int modes = 0; // all TE
+static int nt_modes[hfc_MAX_BOARDS];
+static int nt_modes_count;
+static int force_l1_up = 0;
+static struct proc_dir_entry *hfc_proc_zaphfc_dir;
+
+#ifdef DEBUG
+int debug_level = 0;
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+
+static struct pci_device_id hfc_pci_ids[] = {
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_3069,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, hfc_pci_ids);
+
+static int __devinit hfc_probe(struct pci_dev *dev
+ , const struct pci_device_id *ent);
+static void __devexit hfc_remove(struct pci_dev *dev);
+
+struct pci_driver hfc_driver = {
+ .name = hfc_DRIVER_NAME,
+ .id_table = hfc_pci_ids,
+ .probe = hfc_probe,
+ .remove = hfc_remove,
+};
+
+/******************************************
+ * HW routines
+ ******************************************/
+
+static void hfc_softreset(struct hfc_card *card)
+{
+ printk(KERN_INFO hfc_DRIVER_PREFIX
+ "card %d: "
+ "resetting\n",
+ card->cardnum);
+
+ hfc_outb(card, hfc_CIRM, hfc_CIRM_RESET); // softreset on
+ udelay(6); // wait at least 5.21us
+ hfc_outb(card, hfc_CIRM, 0); // softreset off
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((hfc_RESET_DELAY * HZ) / 1000); // wait 20 ms
+}
+
+void hfc_resetCard(struct hfc_card *card)
+{
+ card->regs.m1 = 0;
+ hfc_outb(card, hfc_INT_M1, card->regs.m1); // no ints
+
+ card->regs.m2 = 0;
+ hfc_outb(card, hfc_INT_M2, card->regs.m2); // not at all
+
+ hfc_softreset(card);
+
+ card->regs.trm = 0;
+ hfc_outb(card, hfc_TRM, card->regs.trm);
+
+ // Select the non-capacitive line mode for the S/T interface */
+ card->regs.sctrl = hfc_SCTRL_NONE_CAP;
+
+ if (card->nt_mode) {
+ // ST-Bit delay for NT-Mode
+ hfc_outb(card, hfc_CLKDEL, hfc_CLKDEL_NT);
+
+ card->regs.sctrl |= hfc_SCTRL_MODE_NT;
+ } else {
+ // ST-Bit delay for TE-Mode
+ hfc_outb(card, hfc_CLKDEL, hfc_CLKDEL_TE);
+
+ card->regs.sctrl |= hfc_SCTRL_MODE_TE;
+ }
+
+ hfc_outb(card, hfc_SCTRL, card->regs.sctrl);
+
+ // S/T Auto awake
+ card->regs.sctrl_e = hfc_SCTRL_E_AUTO_AWAKE;
+ hfc_outb(card, hfc_SCTRL_E, card->regs.sctrl_e);
+
+ // No B-channel enabled at startup
+ card->regs.sctrl_r = 0;
+ hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r);
+
+ // HFC Master Mode
+ hfc_outb(card, hfc_MST_MODE, hfc_MST_MODE_MASTER);
+
+ // Connect internal blocks
+ card->regs.connect =
+ hfc_CONNECT_B1_HFC_from_ST |
+ hfc_CONNECT_B1_ST_from_HFC |
+ hfc_CONNECT_B1_GCI_from_HFC |
+ hfc_CONNECT_B2_HFC_from_ST |
+ hfc_CONNECT_B2_ST_from_HFC |
+ hfc_CONNECT_B2_GCI_from_HFC;
+ hfc_outb(card, hfc_CONNECT, card->regs.connect);
+
+ // All bchans are HDLC by default, not useful, actually
+ // since mode is set during open()
+ hfc_outb(card, hfc_CTMT, 0);
+
+ // bit order
+ hfc_outb(card, hfc_CIRM, 0);
+
+ // Enable D-rx FIFO. At least one FIFO must be enabled (by specs)
+ card->regs.fifo_en = hfc_FIFOEN_DRX;
+ hfc_outb(card, hfc_FIFO_EN, card->regs.fifo_en);
+
+ card->late_irqs=0;
+
+ // Clear already pending ints
+ hfc_inb(card, hfc_INT_S1);
+ hfc_inb(card, hfc_INT_S2);
+
+ // Enable IRQ output
+ card->regs.m1 = hfc_INTS_DREC | hfc_INTS_L1STATE | hfc_INTS_TIMER;
+ hfc_outb(card, hfc_INT_M1, card->regs.m1);
+
+ card->regs.m2 = hfc_M2_IRQ_ENABLE;
+ hfc_outb(card, hfc_INT_M2, card->regs.m2);
+
+ // Unlocks the states machine
+ hfc_outb(card, hfc_STATES, 0);
+
+ // There's no need to explicitly activate L1 now.
+ // Activation is managed inside the interrupt routine.
+}
+
+static void hfc_update_fifo_state(struct hfc_card *card)
+{
+ // I'm not sure if irqsave is needed but there could be a race
+ // condition since hfc_update_fifo_state could be called from
+ // both the IRQ handler and the *_(open|close) functions
+
+ unsigned long flags;
+ spin_lock_irqsave(&card->chans[B1].lock, flags);
+ if (!card->fifo_suspended &&
+ (card->chans[B1].status == open_framed ||
+ card->chans[B1].status == open_voice)) {
+
+ if(!(card->regs.fifo_en & hfc_FIFOEN_B1RX)) {
+ card->regs.fifo_en |= hfc_FIFOEN_B1RX;
+ hfc_clear_fifo_rx(&card->chans[B1].rx);
+ }
+
+ if(!(card->regs.fifo_en & hfc_FIFOEN_B1TX)) {
+ card->regs.fifo_en |= hfc_FIFOEN_B1TX;
+ hfc_clear_fifo_tx(&card->chans[B1].tx);
+ }
+ } else {
+ if(card->regs.fifo_en & hfc_FIFOEN_B1RX)
+ card->regs.fifo_en &= ~hfc_FIFOEN_B1RX;
+ if(card->regs.fifo_en & hfc_FIFOEN_B1TX)
+ card->regs.fifo_en &= ~hfc_FIFOEN_B1TX;
+ }
+ spin_unlock_irqrestore(&card->chans[B1].lock, flags);
+
+ spin_lock_irqsave(&card->chans[B2].lock, flags);
+ if (!card->fifo_suspended &&
+ (card->chans[B2].status == open_framed ||
+ card->chans[B2].status == open_voice ||
+ card->chans[B2].status == sniff_aux)) {
+
+ if(!(card->regs.fifo_en & hfc_FIFOEN_B2RX)) {
+ card->regs.fifo_en |= hfc_FIFOEN_B2RX;
+ hfc_clear_fifo_rx(&card->chans[B2].rx);
+ }
+
+ if(!(card->regs.fifo_en & hfc_FIFOEN_B2TX)) {
+ card->regs.fifo_en |= hfc_FIFOEN_B2TX;
+ hfc_clear_fifo_tx(&card->chans[B2].tx);
+ }
+ } else {
+ if(card->regs.fifo_en & hfc_FIFOEN_B2RX)
+ card->regs.fifo_en &= ~hfc_FIFOEN_B2RX;
+ if(card->regs.fifo_en & hfc_FIFOEN_B2TX)
+ card->regs.fifo_en &= ~hfc_FIFOEN_B2TX;
+ }
+ spin_unlock_irqrestore(&card->chans[B2].lock, flags);
+
+ spin_lock_irqsave(&card->chans[D].lock, flags);
+ if (!card->fifo_suspended &&
+ card->chans[D].status == open_framed) {
+
+ if(!(card->regs.fifo_en & hfc_FIFOEN_DTX)) {
+ card->regs.fifo_en |= hfc_FIFOEN_DTX;
+
+ card->chans[D].tx.ugly_framebuf_size = 0;
+ card->chans[D].tx.ugly_framebuf_off = 0;
+ }
+ } else {
+ if(card->regs.fifo_en & hfc_FIFOEN_DTX)
+ card->regs.fifo_en &= ~hfc_FIFOEN_DTX;
+ }
+ spin_unlock_irqrestore(&card->chans[D].lock, flags);
+
+ hfc_outb(card, hfc_FIFO_EN, card->regs.fifo_en);
+}
+
+static inline void hfc_suspend_fifo(struct hfc_card *card)
+{
+ card->fifo_suspended = TRUE;
+
+ hfc_update_fifo_state(card);
+
+ // When L1 goes down D rx receives garbage; it is nice to
+ // clear it to avoid a CRC error on reactivation
+ // udelay is needed because the FIFO deactivation happens
+ // in 250us
+ udelay(250);
+ hfc_clear_fifo_rx(&card->chans[D].rx);
+
+#ifdef DEBUG
+ if (debug_level >= 3) {
+ printk(KERN_DEBUG hfc_DRIVER_PREFIX
+ "card %d: "
+ "FIFOs suspended\n",
+ card->cardnum);
+ }
+#endif
+}
+
+static inline void hfc_resume_fifo(struct hfc_card *card)
+{
+ card->fifo_suspended = FALSE;
+
+ hfc_update_fifo_state(card);
+
+#ifdef DEBUG
+ if (debug_level >= 3) {
+ printk(KERN_DEBUG hfc_DRIVER_PREFIX
+ "card %d: "
+ "FIFOs resumed\n",
+ card->cardnum);
+ }
+#endif
+}
+
+static void hfc_check_l1_up(struct hfc_card *card)
+{
+ if ((!card->nt_mode && card->l1_state != 7) ||
+ (card->nt_mode && card->l1_state != 3)) {
+// 0 because this is quite verbose when an inferface is unconnected, jaw
+#if 0
+ if(debug_level >= 1) {
+ printk(KERN_DEBUG hfc_DRIVER_PREFIX
+ "card %d: "
+ "L1 is down, bringing up L1.\n",
+ card->cardnum);
+ }
+#endif
+
+ hfc_outb(card, hfc_STATES, hfc_STATES_DO_ACTION |
+ hfc_STATES_ACTIVATE|
+ hfc_STATES_NT_G2_G3);
+ }
+}
+
+
+/*******************
+ * Dahdi interface *
+ *******************/
+
+static int hfc_zap_open(struct dahdi_chan *zaptel_chan)
+{
+ struct hfc_chan_duplex *chan = zaptel_chan->pvt;
+ struct hfc_card *card = chan->card;
+
+ spin_lock(&chan->lock);
+
+ switch (chan->number) {
+ case D:
+ if (chan->status != free &&
+ chan->status != open_framed) {
+ spin_unlock(&chan->lock);
+ return -EBUSY;
+ }
+
+ chan->status = open_framed;
+ break;
+
+ case B1:
+ case B2:
+ if (chan->status != free) {
+ spin_unlock(&chan->lock);
+ return -EBUSY;
+ }
+
+ chan->status = open_voice;
+ break;
+ }
+
+ chan->open_by_zaptel = TRUE;
+
+ try_module_get(THIS_MODULE);
+
+ spin_unlock(&chan->lock);
+
+ switch (chan->number) {
+ case D:
+ break;
+
+ case B1:
+ // B1
+ card->regs.m2 |= hfc_M2_PROC_TRANS;
+ card->regs.ctmt |= hfc_CTMT_TRANSB1; // Enable transparent mode
+ card->regs.cirm |= hfc_CIRM_B1_REV; // Reversed bit order
+ card->regs.sctrl |= hfc_SCTRL_B1_ENA; // Enable transmission
+ card->regs.sctrl_r |= hfc_SCTRL_R_B1_ENA; // Enable reception
+ break;
+
+ case B2:
+ // B2
+ card->regs.m2 |= hfc_M2_PROC_TRANS;
+ card->regs.ctmt |= hfc_CTMT_TRANSB2; // Enable transparent mode
+ card->regs.cirm |= hfc_CIRM_B2_REV; // Reversed bit order
+ card->regs.sctrl |= hfc_SCTRL_B2_ENA; // Enable transmission
+ card->regs.sctrl_r |= hfc_SCTRL_R_B2_ENA; // Enable reception
+ break;
+
+ }
+
+ // If not already enabled, enable processing transition (8KHz)
+ // interrupt
+ hfc_outb(card, hfc_INT_M2, card->regs.m2);
+ hfc_outb(card, hfc_CTMT, card->regs.ctmt);
+ hfc_outb(card, hfc_CIRM, card->regs.cirm);
+ hfc_outb(card, hfc_SCTRL, card->regs.sctrl);
+ hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r);
+
+ hfc_update_fifo_state(card);
+
+ printk(KERN_INFO hfc_DRIVER_PREFIX
+ "card %d: "
+ "chan %s opened as %s.\n",
+ card->cardnum,
+ chan->name,
+ zaptel_chan->name);
+
+ return 0;
+}
+
+static int hfc_zap_close(struct dahdi_chan *zaptel_chan)
+{
+ struct hfc_chan_duplex *chan = zaptel_chan->pvt;
+ struct hfc_card *card = chan->card;
+
+ if (!card) {
+ printk(KERN_CRIT hfc_DRIVER_PREFIX
+ "hfc_zap_close called with NULL card\n");
+ return -1;
+ }
+
+ spin_lock(&chan->lock);
+
+ if (chan->status == free) {
+ spin_unlock(&chan->lock);
+ return -EINVAL;
+ }
+
+ chan->status = free;
+ chan->open_by_zaptel = FALSE;
+
+ spin_unlock(&chan->lock);
+
+ switch (chan->number) {
+ case D:
+ break;
+
+ case B1:
+ // B1
+ card->regs.ctmt &= ~hfc_CTMT_TRANSB1;
+ card->regs.cirm &= ~hfc_CIRM_B1_REV;
+ card->regs.sctrl &= ~hfc_SCTRL_B1_ENA;
+ card->regs.sctrl_r &= ~hfc_SCTRL_R_B1_ENA;
+ break;
+
+ case B2:
+ // B2
+ card->regs.ctmt &= ~hfc_CTMT_TRANSB2;
+ card->regs.cirm &= ~hfc_CIRM_B2_REV;
+ card->regs.sctrl &= ~hfc_SCTRL_B2_ENA;
+ card->regs.sctrl_r &= ~hfc_SCTRL_R_B2_ENA;
+ break;
+ }
+
+ if (card->chans[B1].status == free &&
+ card->chans[B2].status == free)
+ card->regs.m2 &= ~hfc_M2_PROC_TRANS;
+
+ hfc_outb(card, hfc_INT_M2, card->regs.m2);
+ hfc_outb(card, hfc_CTMT, card->regs.ctmt);
+ hfc_outb(card, hfc_CIRM, card->regs.cirm);
+ hfc_outb(card, hfc_SCTRL, card->regs.sctrl);
+ hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r);
+
+ hfc_update_fifo_state(card);
+
+ module_put(THIS_MODULE);
+
+ printk(KERN_INFO hfc_DRIVER_PREFIX
+ "card %d: "
+ "chan %s closed as %s.\n",
+ card->cardnum,
+ chan->name,
+ zaptel_chan->name);
+
+ return 0;
+}
+
+static int hfc_zap_rbsbits(struct dahdi_chan *chan, int bits)
+{
+ return 0;
+}
+
+static int hfc_zap_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data)
+{
+ switch(cmd) {
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+static void hfc_hdlc_hard_xmit(struct dahdi_chan *d_chan)
+{
+ struct hfc_chan_duplex *chan = d_chan->pvt;
+ struct hfc_card *card = chan->card;
+ struct dahdi_hfc *hfccard = card->ztdev;
+
+ atomic_inc(&hfccard->hdlc_pending);
+
+}
+
+static int hfc_zap_startup(struct dahdi_span *span) {
+ struct dahdi_hfc *zthfc = span->pvt;
+ struct hfc_card *hfctmp = zthfc->card;
+ int alreadyrunning;
+
+ if (!hfctmp) {
+ printk(KERN_INFO hfc_DRIVER_PREFIX
+ "card %d: "
+ "no card for span at startup!\n",
+ hfctmp->cardnum);
+ }
+
+ alreadyrunning = span->flags & DAHDI_FLAG_RUNNING;
+
+ if (!alreadyrunning) {
+ span->flags |= DAHDI_FLAG_RUNNING;
+ }
+
+ return 0;
+}
+
+static int hfc_zap_shutdown(struct dahdi_span *span)
+{
+ return 0;
+}
+
+static int hfc_zap_maint(struct dahdi_span *span, int cmd)
+{
+ return 0;
+}
+
+static int hfc_zap_chanconfig(struct dahdi_chan *d_chan, int sigtype)
+{
+ struct hfc_chan_duplex *chan = d_chan->pvt;
+ struct hfc_card *card = chan->card;
+ struct dahdi_hfc *hfccard = card->ztdev;
+
+ if ((sigtype == DAHDI_SIG_HARDHDLC) || (hfccard->sigchan == d_chan)) {
+ hfccard->sigchan = (sigtype == DAHDI_SIG_HARDHDLC) ? d_chan : NULL;
+ hfccard->sigactive = 0;
+ atomic_set(&hfccard->hdlc_pending, 0);
+ }
+
+ return 0;
+}
+
+static int hfc_zap_spanconfig(struct dahdi_span *span, struct dahdi_lineconfig *lc)
+{
+ span->lineconfig = lc->lineconfig;
+
+ return 0;
+}
+
+static int hfc_zap_initialize(struct dahdi_hfc *hfccard){
+ struct hfc_card *hfctmp = hfccard->card;
+ int i;
+
+ memset(&hfccard->span, 0x0, sizeof(struct dahdi_span));
+ sprintf(hfccard->span.name, "ZTHFC%d", hfctmp->cardnum + 1);
+ sprintf(hfccard->span.desc,
+ "HFC-S PCI A ISDN card %d [%s] ",
+ hfctmp->cardnum,
+ hfctmp->nt_mode?"NT":"TE");
+ hfccard->span.spantype = hfctmp->nt_mode?"NT":"TE";
+ hfccard->span.manufacturer = "Cologne Chips";
+ hfccard->span.spanconfig = hfc_zap_spanconfig;
+ hfccard->span.chanconfig = hfc_zap_chanconfig;
+ hfccard->span.startup = hfc_zap_startup;
+ hfccard->span.shutdown = hfc_zap_shutdown;
+ hfccard->span.maint = hfc_zap_maint;
+ hfccard->span.rbsbits = hfc_zap_rbsbits;
+ hfccard->span.open = hfc_zap_open;
+ hfccard->span.close = hfc_zap_close;
+ hfccard->span.ioctl = hfc_zap_ioctl;
+ hfccard->span.hdlc_hard_xmit = hfc_hdlc_hard_xmit;
+ hfccard->span.flags = 0;
+ hfccard->span.irq = hfctmp->pcidev->irq;
+ dahdi_copy_string(hfccard->span.devicetype, "HFC-S PCI-A ISDN", sizeof(hfccard->span.devicetype));
+ sprintf(hfccard->span.location, "PCI Bus %02d Slot %02d",
+ hfctmp->pcidev->bus->number, PCI_SLOT(hfctmp->pcidev->devfn) + 1);
+ hfccard->span.chans = hfccard->_chans;
+ hfccard->span.channels = 3;
+ for (i = 0; i < hfccard->span.channels; i++)
+ hfccard->_chans[i] = &hfccard->chans[i];
+ hfccard->span.deflaw = DAHDI_LAW_ALAW;
+ hfccard->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_CCS; // <--- this is really BS
+ hfccard->span.offset = 0;
+ init_waitqueue_head(&hfccard->span.maintq);
+ hfccard->span.pvt = hfccard;
+
+ for (i = 0; i < hfccard->span.channels; i++) {
+ memset(&hfccard->chans[i], 0x0, sizeof(struct dahdi_chan));
+
+ sprintf(hfccard->chans[i].name,
+ "ZTHFC%d/%d/%d",
+ hfctmp->cardnum + 1, 0, i + 1);
+
+ printk(KERN_INFO hfc_DRIVER_PREFIX
+ "card %d: "
+ "registered %s\n",
+ hfctmp->cardnum,
+ hfccard->chans[i].name);
+
+ if (i == hfccard->span.channels - 1) {
+ hfccard->chans[i].sigcap = DAHDI_SIG_HARDHDLC;
+ hfccard->sigchan = &hfccard->chans[D];
+ hfccard->sigactive = 0;
+ atomic_set(&hfccard->hdlc_pending, 0);
+ } else {
+ hfccard->chans[i].sigcap = DAHDI_SIG_CLEAR | DAHDI_SIG_DACS;
+ }
+
+ hfccard->chans[i].chanpos = i + 1;
+ }
+
+ hfccard->chans[DAHDI_D].readchunk = hfctmp->chans[D].rx.zaptel_buffer;
+ hfccard->chans[DAHDI_D].writechunk = hfctmp->chans[D].tx.zaptel_buffer;
+ hfccard->chans[DAHDI_D].pvt = &hfctmp->chans[D];
+
+ hfccard->chans[DAHDI_B1].readchunk = hfctmp->chans[B1].rx.zaptel_buffer;
+ hfccard->chans[DAHDI_B1].writechunk = hfctmp->chans[B1].tx.zaptel_buffer;
+ hfccard->chans[DAHDI_B1].pvt = &hfctmp->chans[B1];
+
+ hfccard->chans[DAHDI_B2].readchunk = hfctmp->chans[B2].rx.zaptel_buffer;
+ hfccard->chans[DAHDI_B2].writechunk = hfctmp->chans[B2].tx.zaptel_buffer;
+ hfccard->chans[DAHDI_B2].pvt = &hfctmp->chans[B2];
+
+ if (dahdi_register(&hfccard->span,0)) {
+ printk(KERN_CRIT "unable to register zaptel device!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void hfc_zap_transmit(struct hfc_chan_simplex *chan)
+{
+ hfc_fifo_put(chan, chan->zaptel_buffer, DAHDI_CHUNKSIZE);
+}
+
+static void hfc_zap_receive(struct hfc_chan_simplex *chan)
+{
+ hfc_fifo_get(chan, chan->zaptel_buffer, DAHDI_CHUNKSIZE);
+}
+
+/******************************************
+ * Interrupt Handler
+ ******************************************/
+
+static void hfc_handle_timer_interrupt(struct hfc_card *card);
+static void hfc_handle_state_interrupt(struct hfc_card *card);
+static void hfc_handle_processing_interrupt(struct hfc_card *card);
+static void hfc_frame_arrived(struct hfc_chan_duplex *chan);
+static void hfc_handle_voice(struct hfc_card *card);
+
+#if (KERNEL_VERSION(2,6,24) < LINUX_VERSION_CODE)
+static irqreturn_t hfc_interrupt(int irq, void *dev_id)
+#else
+static irqreturn_t hfc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+#endif
+{
+ struct hfc_card *card = dev_id;
+ unsigned long flags;
+ u8 status,s1,s2;
+
+ if (!card) {
+ printk(KERN_CRIT hfc_DRIVER_PREFIX
+ "spurious interrupt (IRQ %d)\n",
+ irq);
+ return IRQ_NONE;
+ }
+
+ spin_lock_irqsave(&card->lock, flags);
+ status = hfc_inb(card, hfc_STATUS);
+ if (!(status & hfc_STATUS_ANYINT)) {
+ // maybe we are sharing the irq
+ spin_unlock_irqrestore(&card->lock,flags);
+ return IRQ_NONE;
+ }
+
+ /* We used to ingore the IRQ when the card was in processing
+ * state but apparently there is no restriction to access the
+ * card in such state:
+ *
+ * Joerg Ciesielski wrote:
+ * > There is no restriction for the IRQ handler to access
+ * > HFC-S PCI during processing phase. A IRQ latency of 375 us
+ * > is also no problem since there are no interrupt sources in
+ * > HFC-S PCI which must be handled very fast.
+ * > Due to its deep fifos the IRQ latency can be several ms with
+ * > out the risk of loosing data. Even the S/T state interrupts
+ * > must not be handled with a latency less than <5ms.
+ * >
+ * > The processing phase only indicates that HFC-S PCI is
+ * > processing the Fifos as PCI master so that data is read and
+ * > written in the 32k memory window. But there is no restriction
+ * > to access data in the memory window during this time.
+ *
+ * // if (status & hfc_STATUS_PCI_PROC) {
+ * // return IRQ_HANDLED;
+ * // }
+ */
+
+ s1 = hfc_inb(card, hfc_INT_S1);
+ s2 = hfc_inb(card, hfc_INT_S2);
+
+ if (s1 != 0) {
+ if (s1 & hfc_INTS_TIMER) {
+ // timer (bit 7)
+ hfc_handle_timer_interrupt(card);
+ }
+
+ if (s1 & hfc_INTS_L1STATE) {
+ // state machine (bit 6)
+ hfc_handle_state_interrupt(card);
+ }
+
+ if (s1 & hfc_INTS_DREC) {
+ // D chan RX (bit 5)
+ hfc_frame_arrived(&card->chans[D]);
+ }
+
+ if (s1 & hfc_INTS_B1REC) {
+ // B1 chan RX (bit 3)
+ hfc_frame_arrived(&card->chans[B1]);
+ }
+
+ if (s1 & hfc_INTS_B2REC) {
+ // B2 chan RX (bit 4)
+ hfc_frame_arrived(&card->chans[B2]);
+ }
+
+ if (s1 & hfc_INTS_DTRANS) {
+ // D chan TX (bit 2)
+ }
+
+ if (s1 & hfc_INTS_B1TRANS) {
+ // B1 chan TX (bit 0)
+ }
+
+ if (s1 & hfc_INTS_B2TRANS) {
+ // B2 chan TX (bit 1)
+ }
+
+ }
+
+ if (s2 != 0) {
+ if (s2 & hfc_M2_PMESEL) {
+ // kaboom irq (bit 7)
+
+ /* CologneChip says:
+ *
+ * the meaning of this fatal error bit is that HFC-S PCI as PCI
+ * master could not access the PCI bus within 125us to finish its
+ * data processing. If this happens only very seldom it does not
+ * cause big problems but of course some B-channel or D-channel
+ * data will be corrupted due to this event.
+ *
+ * Unfortunately this bit is only set once after the problem occurs
+ * and can only be reseted by a software reset. That means it is not
+ * easily possible to check how often this fatal error happens.
+ */
+
+ if(!card->sync_loss_reported) {
+ printk(KERN_CRIT hfc_DRIVER_PREFIX
+ "card %d: "
+ "sync lost, pci performance too low!\n",
+ card->cardnum);
+
+ card->sync_loss_reported = TRUE;
+ }
+ }
+
+ if (s2 & hfc_M2_GCI_MON_REC) {
+ // RxR monitor channel (bit 2)
+ }
+
+ if (s2 & hfc_M2_GCI_I_CHG) {
+ // GCI I-change (bit 1)
+ }
+
+ if (s2 & hfc_M2_PROC_TRANS){
+ // processing/non-processing transition (bit 0)
+ hfc_handle_processing_interrupt(card);
+ }
+
+ }
+
+ spin_unlock_irqrestore(&card->lock,flags);
+
+ return IRQ_HANDLED;
+}
+
+static void hfc_handle_timer_interrupt(struct hfc_card *card)
+{
+ if(card->ignore_first_timer_interrupt) {
+ card->ignore_first_timer_interrupt = FALSE;
+ return;
+ }
+
+ if ((card->nt_mode && card->l1_state == 3) ||
+ (!card->nt_mode && card->l1_state == 7)) {
+
+ card->regs.ctmt &= ~hfc_CTMT_TIMER_MASK;
+ hfc_outb(card, hfc_CTMT, card->regs.ctmt);
+
+ hfc_resume_fifo(card);
+ }
+}
+
+static void hfc_handle_state_interrupt(struct hfc_card *card)
+{
+ u8 new_state = hfc_inb(card,hfc_STATES) & hfc_STATES_STATE_MASK;
+
+#ifdef DEBUG
+ if (debug_level >= 1) {
+ printk(KERN_DEBUG hfc_DRIVER_PREFIX
+ "card %d: "
+ "layer 1 state = %c%d\n",
+ card->cardnum,
+ card->nt_mode?'G':'F',
+ new_state);
+ }
+#endif
+
+ if (card->nt_mode) {
+ // NT mode
+
+ if (new_state == 3) {
+ // fix to G3 state (see specs)
+ hfc_outb(card, hfc_STATES, hfc_STATES_LOAD_STATE | 3);
+ }
+
+ if (new_state == 3 && card->l1_state != 3) {
+ hfc_resume_fifo(card);
+ }
+
+ if (new_state != 3 && card->l1_state == 3) {
+ hfc_suspend_fifo(card);
+ }
+ } else {
+ if (new_state == 3) {
+ // Keep L1 up... zaptel & libpri expects a always up L1...
+ // Enable only when using an unpatched libpri
+
+ if (force_l1_up) {
+ hfc_outb(card, hfc_STATES,
+ hfc_STATES_DO_ACTION |
+ hfc_STATES_ACTIVATE|
+ hfc_STATES_NT_G2_G3);
+ }
+ }
+
+ if (new_state == 7 && card->l1_state != 7) {
+ // TE is now active, schedule FIFO activation after
+ // some time, otherwise the first frames are lost
+
+ card->regs.ctmt |= hfc_CTMT_TIMER_50 | hfc_CTMT_TIMER_CLEAR;
+ hfc_outb(card, hfc_CTMT, card->regs.ctmt);
+
+ // Activating the timer firest an interrupt immediately, we
+ // obviously need to ignore it
+ card->ignore_first_timer_interrupt = TRUE;
+ }
+
+ if (new_state != 7 && card->l1_state == 7) {
+ // TE has become inactive, disable FIFO
+ hfc_suspend_fifo(card);
+ }
+ }
+
+ card->l1_state = new_state;
+}
+
+static void hfc_handle_processing_interrupt(struct hfc_card *card)
+{
+ int available_bytes=0;
+
+ // Synchronize with the first enabled channel
+ if(card->regs.fifo_en & hfc_FIFOEN_B1RX)
+ available_bytes = hfc_fifo_used_rx(&card->chans[B1].rx);
+ if(card->regs.fifo_en & hfc_FIFOEN_B2RX)
+ available_bytes = hfc_fifo_used_rx(&card->chans[B2].rx);
+ else
+ available_bytes = -1;
+
+ if ((available_bytes == -1 && card->ticks == 8) ||
+ available_bytes >= DAHDI_CHUNKSIZE + hfc_RX_FIFO_PRELOAD) {
+ card->ticks = 0;
+
+ if (available_bytes > DAHDI_CHUNKSIZE*2 + hfc_RX_FIFO_PRELOAD) {
+ card->late_irqs++;
+ // we are out of sync, clear fifos, jaw
+ hfc_clear_fifo_rx(&card->chans[B1].rx);
+ hfc_clear_fifo_tx(&card->chans[B1].tx);
+ hfc_clear_fifo_rx(&card->chans[B2].rx);
+ hfc_clear_fifo_tx(&card->chans[B2].tx);
+
+#ifdef DEBUG
+ if (debug_level >= 4) {
+ printk(KERN_DEBUG hfc_DRIVER_PREFIX
+ "card %d: "
+ "late IRQ, %d bytes late\n",
+ card->cardnum,
+ available_bytes -
+ (DAHDI_CHUNKSIZE +
+ hfc_RX_FIFO_PRELOAD));
+ }
+#endif
+ } else {
+ hfc_handle_voice(card);
+ }
+ }
+
+ card->ticks++;
+}
+
+
+static void hfc_handle_voice(struct hfc_card *card)
+{
+ struct dahdi_hfc *hfccard = card->ztdev;
+ int frame_left, res;
+ unsigned char buf[hfc_HDLC_BUF_LEN];
+ unsigned int size = sizeof(buf) / sizeof(buf[0]);
+
+
+ if (card->chans[B1].status != open_voice &&
+ card->chans[B2].status != open_voice)
+ return;
+
+ dahdi_transmit(&hfccard->span);
+
+ if (card->regs.fifo_en & hfc_FIFOEN_B1TX)
+ hfc_zap_transmit(&card->chans[B1].tx);
+ if (card->regs.fifo_en & hfc_FIFOEN_B2TX)
+ hfc_zap_transmit(&card->chans[B2].tx);
+
+// dahdi hdlc frame tx
+
+ if (atomic_read(&hfccard->hdlc_pending)) {
+ hfc_check_l1_up(card);
+ res = dahdi_hdlc_getbuf(hfccard->sigchan, buf, &size);
+ if (size > 0) {
+ hfccard->sigactive = 1;
+ memcpy(card->chans[D].tx.ugly_framebuf +
+ card->chans[D].tx.ugly_framebuf_size, buf, size);
+ card->chans[D].tx.ugly_framebuf_size += size;
+ if (res != 0) {
+ hfc_fifo_put_frame(&card->chans[D].tx,
+ card->chans[D].tx.ugly_framebuf,
+ card->chans[D].tx.ugly_framebuf_size);
+ ++hfccard->frames_out;
+ hfccard->sigactive = 0;
+ card->chans[D].tx.ugly_framebuf_size = 0;
+ atomic_dec(&hfccard->hdlc_pending);
+ }
+ }
+ } // frame tx done
+
+ if (card->regs.fifo_en & hfc_FIFOEN_B1RX)
+ hfc_zap_receive(&card->chans[B1].rx);
+ else
+ memset(&card->chans[B1].rx.zaptel_buffer, 0x7f,
+ sizeof(card->chans[B1].rx.zaptel_buffer));
+
+ if (card->regs.fifo_en & hfc_FIFOEN_B2RX)
+ hfc_zap_receive(&card->chans[B2].rx);
+ else
+ memset(&card->chans[B2].rx.zaptel_buffer, 0x7f,
+ sizeof(card->chans[B1].rx.zaptel_buffer));
+
+ // Echo cancellation
+ dahdi_ec_chunk(&hfccard->chans[DAHDI_B1],
+ card->chans[B1].rx.zaptel_buffer,
+ card->chans[B1].tx.zaptel_buffer);
+ dahdi_ec_chunk(&hfccard->chans[DAHDI_B2],
+ card->chans[B2].rx.zaptel_buffer,
+ card->chans[B2].tx.zaptel_buffer);
+
+ // dahdi hdlc frame rx
+ if (hfc_fifo_has_frames(&card->chans[D].rx)) {
+ hfc_frame_arrived(&card->chans[D]);
+ }
+
+ if (card->chans[D].rx.ugly_framebuf_size) {
+ frame_left = card->chans[D].rx.ugly_framebuf_size - card->chans[D].rx.ugly_framebuf_off ;
+ if (frame_left > hfc_HDLC_BUF_LEN){
+ dahdi_hdlc_putbuf(hfccard->sigchan, card->chans[D].rx.ugly_framebuf+
+ card->chans[D].rx.ugly_framebuf_off, hfc_HDLC_BUF_LEN);
+ card->chans[D].rx.ugly_framebuf_off += hfc_HDLC_BUF_LEN;
+ }else{
+ dahdi_hdlc_putbuf(hfccard->sigchan, card->chans[D].rx.ugly_framebuf+
+ card->chans[D].rx.ugly_framebuf_off, frame_left);
+ dahdi_hdlc_finish(hfccard->sigchan);
+ card->chans[D].rx.ugly_framebuf_size = 0;
+ card->chans[D].rx.ugly_framebuf_off = 0;
+ }
+ } //frame rx done
+
+ if (hfccard->span.flags & DAHDI_FLAG_RUNNING) {
+ dahdi_receive(&hfccard->span);
+ }
+}
+
+static void hfc_frame_arrived(struct hfc_chan_duplex *chan)
+{
+ struct hfc_card *card = chan->card;
+ int antiloop = 16;
+ struct sk_buff *skb;
+
+ while(hfc_fifo_has_frames(&chan->rx) && --antiloop) {
+ int frame_size = hfc_fifo_get_frame_size(&chan->rx);
+
+ if (frame_size < 3) {
+#ifdef DEBUG
+ if (debug_level>=2)
+ printk(KERN_DEBUG hfc_DRIVER_PREFIX
+ "card %d: "
+ "chan %s: "
+ "invalid frame received, just %d bytes\n",
+ card->cardnum,
+ chan->name,
+ frame_size);
+#endif
+
+ hfc_fifo_drop_frame(&chan->rx);
+
+
+ continue;
+ } else if(frame_size == 3) {
+#ifdef DEBUG
+ if (debug_level>=2)
+ printk(KERN_DEBUG hfc_DRIVER_PREFIX
+ "card %d: "
+ "chan %s: "
+ "empty frame received\n",
+ card->cardnum,
+ chan->name);
+#endif
+
+ hfc_fifo_drop_frame(&chan->rx);
+
+
+ continue;
+ }
+
+ if (chan->open_by_zaptel &&
+ card->chans[D].rx.ugly_framebuf_size) {
+ // We have to wait for Dahdi to transmit the
+ // frame... wait for next time
+
+ break;
+ }
+
+ skb = dev_alloc_skb(frame_size - 3);
+
+ if (!skb) {
+ printk(KERN_ERR hfc_DRIVER_PREFIX
+ "card %d: "
+ "chan %s: "
+ "cannot allocate skb: frame dropped\n",
+ card->cardnum,
+ chan->name);
+
+ hfc_fifo_drop_frame(&chan->rx);
+
+
+ continue;
+ }
+
+
+ // HFC does the checksum
+#ifndef CHECKSUM_HW
+ skb->ip_summed = CHECKSUM_COMPLETE;
+#else
+ skb->ip_summed = CHECKSUM_HW;
+#endif
+
+ if (chan->open_by_zaptel) {
+ card->chans[D].rx.ugly_framebuf_size = frame_size - 1;
+
+ if (hfc_fifo_get_frame(&card->chans[D].rx,
+ card->chans[D].rx.ugly_framebuf,
+ frame_size - 1) == -1) {
+ dev_kfree_skb(skb);
+ continue;
+ }
+
+ memcpy(skb_put(skb, frame_size - 3),
+ card->chans[D].rx.ugly_framebuf,
+ frame_size - 3);
+ } else {
+ if (hfc_fifo_get_frame(&chan->rx,
+ skb_put(skb, frame_size - 3),
+ frame_size - 3) == -1) {
+ dev_kfree_skb(skb);
+ continue;
+ }
+ }
+ }
+
+ if (!antiloop)
+ printk(KERN_CRIT hfc_DRIVER_PREFIX
+ "card %d: "
+ "Infinite loop detected\n",
+ card->cardnum);
+}
+
+/******************************************
+ * Module initialization and cleanup
+ ******************************************/
+
+static int __devinit hfc_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *ent)
+{
+ static int cardnum=0;
+ int err;
+ int i;
+
+ struct hfc_card *card = NULL;
+ struct dahdi_hfc *zthfc = NULL;
+ card = kmalloc(sizeof(struct hfc_card), GFP_KERNEL);
+ if (!card) {
+ printk(KERN_CRIT hfc_DRIVER_PREFIX
+ "unable to kmalloc!\n");
+ err = -ENOMEM;
+ goto err_alloc_hfccard;
+ }
+
+ memset(card, 0x00, sizeof(struct hfc_card));
+ card->cardnum = cardnum;
+ card->pcidev = pci_dev;
+ spin_lock_init(&card->lock);
+
+ pci_set_drvdata(pci_dev, card);
+
+ if ((err = pci_enable_device(pci_dev))) {
+ goto err_pci_enable_device;
+ }
+
+ if ((err = pci_set_dma_mask(pci_dev, PCI_DMA_32BIT))) {
+ printk(KERN_ERR hfc_DRIVER_PREFIX
+ "card %d: "
+ "No suitable DMA configuration available.\n",
+ card->cardnum);
+ goto err_pci_set_dma_mask;
+ }
+
+ pci_write_config_word(pci_dev, PCI_COMMAND, PCI_COMMAND_MEMORY);
+
+ if((err = pci_request_regions(pci_dev, hfc_DRIVER_NAME))) {
+ printk(KERN_CRIT hfc_DRIVER_PREFIX
+ "card %d: "
+ "cannot request I/O memory region\n",
+ card->cardnum);
+ goto err_pci_request_regions;
+ }
+
+ pci_set_master(pci_dev);
+
+ if (!pci_dev->irq) {
+ printk(KERN_CRIT hfc_DRIVER_PREFIX
+ "card %d: "
+ "no irq!\n",
+ card->cardnum);
+ err = -ENODEV;
+ goto err_noirq;
+ }
+
+ card->io_bus_mem = pci_resource_start(pci_dev,1);
+ if (!card->io_bus_mem) {
+ printk(KERN_CRIT hfc_DRIVER_PREFIX
+ "card %d: "
+ "no iomem!\n",
+ card->cardnum);
+ err = -ENODEV;
+ goto err_noiobase;
+ }
+
+ if(!(card->io_mem = ioremap(card->io_bus_mem, hfc_PCI_MEM_SIZE))) {
+ printk(KERN_CRIT hfc_DRIVER_PREFIX
+ "card %d: "
+ "cannot ioremap I/O memory\n",
+ card->cardnum);
+ err = -ENODEV;
+ goto err_ioremap;
+ }
+
+ // pci_alloc_consistent guarantees alignment (Documentation/DMA-mapping.txt)
+ card->fifo_mem = pci_alloc_consistent(pci_dev, hfc_FIFO_SIZE, &card->fifo_bus_mem);
+ if (!card->fifo_mem) {
+ printk(KERN_CRIT hfc_DRIVER_PREFIX
+ "card %d: "
+ "unable to allocate FIFO DMA memory!\n",
+ card->cardnum);
+ err = -ENOMEM;
+ goto err_alloc_fifo;
+ }
+
+ memset(card->fifo_mem, 0x00, hfc_FIFO_SIZE);
+
+ card->fifos = card->fifo_mem;
+
+ pci_write_config_dword(card->pcidev, hfc_PCI_MWBA, card->fifo_bus_mem);
+
+ if ((err = request_irq(card->pcidev->irq, &hfc_interrupt,
+
+#if (KERNEL_VERSION(2,6,23) < LINUX_VERSION_CODE)
+ IRQF_SHARED, hfc_DRIVER_NAME, card))) {
+#else
+ SA_SHIRQ, hfc_DRIVER_NAME, card))) {
+#endif
+
+ printk(KERN_CRIT hfc_DRIVER_PREFIX
+ "card %d: "
+ "unable to register irq\n",
+ card->cardnum);
+ goto err_request_irq;
+ }
+
+ card->nt_mode = FALSE;
+
+ if (modes & (1 << card->cardnum))
+ card->nt_mode = TRUE;
+
+ for (i=0; i<nt_modes_count; i++) {
+ if (nt_modes[i] == card->cardnum)
+ card->nt_mode=TRUE;
+ }
+
+//---------------------------------- D
+ card->chans[D].card = card;
+ card->chans[D].name = "D";
+ card->chans[D].status = free;
+ card->chans[D].number = D;
+ spin_lock_init(&card->chans[D].lock);
+
+ card->chans[D].rx.chan = &card->chans[D];
+ card->chans[D].rx.fifo_base = card->fifos + 0x4000;
+ card->chans[D].rx.z_base = card->fifos + 0x4000;
+ card->chans[D].rx.z1_base = card->fifos + 0x6080;
+ card->chans[D].rx.z2_base = card->fifos + 0x6082;
+ card->chans[D].rx.z_min = 0x0000;
+ card->chans[D].rx.z_max = 0x01FF;
+ card->chans[D].rx.f_min = 0x10;
+ card->chans[D].rx.f_max = 0x1F;
+ card->chans[D].rx.f1 = card->fifos + 0x60a0;
+ card->chans[D].rx.f2 = card->fifos + 0x60a1;
+ card->chans[D].rx.fifo_size = card->chans[D].rx.z_max - card->chans[D].rx.z_min + 1;
+ card->chans[D].rx.f_num = card->chans[D].rx.f_max - card->chans[D].rx.f_min + 1;
+
+ card->chans[D].tx.chan = &card->chans[D];
+ card->chans[D].tx.fifo_base = card->fifos + 0x0000;
+ card->chans[D].tx.z_base = card->fifos + 0x0000;
+ card->chans[D].tx.z1_base = card->fifos + 0x2080;
+ card->chans[D].tx.z2_base = card->fifos + 0x2082;
+ card->chans[D].tx.z_min = 0x0000;
+ card->chans[D].tx.z_max = 0x01FF;
+ card->chans[D].tx.f_min = 0x10;
+ card->chans[D].tx.f_max = 0x1F;
+ card->chans[D].tx.f1 = card->fifos + 0x20a0;
+ card->chans[D].tx.f2 = card->fifos + 0x20a1;
+ card->chans[D].tx.fifo_size = card->chans[D].tx.z_max - card->chans[D].tx.z_min + 1;
+ card->chans[D].tx.f_num = card->chans[D].tx.f_max - card->chans[D].tx.f_min + 1;
+
+
+//---------------------------------- B1
+ card->chans[B1].card = card;
+ card->chans[B1].name = "B1";
+ card->chans[B1].status = free;
+ card->chans[B1].number = B1;
+ card->chans[B1].protocol = 0;
+ spin_lock_init(&card->chans[B1].lock);
+
+ card->chans[B1].rx.chan = &card->chans[B1];
+ card->chans[B1].rx.fifo_base = card->fifos + 0x4200;
+ card->chans[B1].rx.z_base = card->fifos + 0x4000;
+ card->chans[B1].rx.z1_base = card->fifos + 0x6000;
+ card->chans[B1].rx.z2_base = card->fifos + 0x6002;
+ card->chans[B1].rx.z_min = 0x0200;
+ card->chans[B1].rx.z_max = 0x1FFF;
+ card->chans[B1].rx.f_min = 0x00;
+ card->chans[B1].rx.f_max = 0x1F;
+ card->chans[B1].rx.f1 = card->fifos + 0x6080;
+ card->chans[B1].rx.f2 = card->fifos + 0x6081;
+ card->chans[B1].rx.fifo_size = card->chans[B1].rx.z_max - card->chans[B1].rx.z_min + 1;
+ card->chans[B1].rx.f_num = card->chans[B1].rx.f_max - card->chans[B1].rx.f_min + 1;
+
+ card->chans[B1].tx.chan = &card->chans[B1];
+ card->chans[B1].tx.fifo_base = card->fifos + 0x0200;
+ card->chans[B1].tx.z_base = card->fifos + 0x0000;
+ card->chans[B1].tx.z1_base = card->fifos + 0x2000;
+ card->chans[B1].tx.z2_base = card->fifos + 0x2002;
+ card->chans[B1].tx.z_min = 0x0200;
+ card->chans[B1].tx.z_max = 0x1FFF;
+ card->chans[B1].tx.f_min = 0x00;
+ card->chans[B1].tx.f_max = 0x1F;
+ card->chans[B1].tx.f1 = card->fifos + 0x2080;
+ card->chans[B1].tx.f2 = card->fifos + 0x2081;
+ card->chans[B1].tx.fifo_size = card->chans[B1].tx.z_max - card->chans[B1].tx.z_min + 1;
+ card->chans[B1].tx.f_num = card->chans[B1].tx.f_max - card->chans[B1].tx.f_min + 1;
+
+//---------------------------------- B2
+ card->chans[B2].card = card;
+ card->chans[B2].name = "B2";
+ card->chans[B2].status = free;
+ card->chans[B2].number = B2;
+ card->chans[B2].protocol = 0;
+ spin_lock_init(&card->chans[B2].lock);
+
+ card->chans[B2].rx.chan = &card->chans[B2];
+ card->chans[B2].rx.fifo_base = card->fifos + 0x6200,
+ card->chans[B2].rx.z_base = card->fifos + 0x6000;
+ card->chans[B2].rx.z1_base = card->fifos + 0x6100;
+ card->chans[B2].rx.z2_base = card->fifos + 0x6102;
+ card->chans[B2].rx.z_min = 0x0200;
+ card->chans[B2].rx.z_max = 0x1FFF;
+ card->chans[B2].rx.f_min = 0x00;
+ card->chans[B2].rx.f_max = 0x1F;
+ card->chans[B2].rx.f1 = card->fifos + 0x6180;
+ card->chans[B2].rx.f2 = card->fifos + 0x6181;
+ card->chans[B2].rx.fifo_size = card->chans[B2].rx.z_max - card->chans[B2].rx.z_min + 1;
+ card->chans[B2].rx.f_num = card->chans[B2].rx.f_max - card->chans[B2].rx.f_min + 1;
+
+ card->chans[B2].tx.chan = &card->chans[B2];
+ card->chans[B2].tx.fifo_base = card->fifos + 0x2200;
+ card->chans[B2].tx.z_base = card->fifos + 0x2000;
+ card->chans[B2].tx.z1_base = card->fifos + 0x2100;
+ card->chans[B2].tx.z2_base = card->fifos + 0x2102;
+ card->chans[B2].tx.z_min = 0x0200;
+ card->chans[B2].tx.z_max = 0x1FFF;
+ card->chans[B2].tx.f_min = 0x00;
+ card->chans[B2].tx.f_max = 0x1F;
+ card->chans[B2].tx.f1 = card->fifos + 0x2180;
+ card->chans[B2].tx.f2 = card->fifos + 0x2181;
+ card->chans[B2].tx.fifo_size = card->chans[B2].tx.z_max - card->chans[B2].tx.z_min + 1;
+ card->chans[B2].tx.f_num = card->chans[B2].tx.f_max - card->chans[B2].tx.f_min + 1;
+
+// -------------------------------------------------------
+
+ zthfc = kmalloc(sizeof(struct dahdi_hfc),GFP_KERNEL);
+ if (!zthfc) {
+ printk(KERN_CRIT hfc_DRIVER_PREFIX
+ "unable to kmalloc!\n");
+ goto err_request_irq;
+ }
+ memset(zthfc, 0x0, sizeof(struct dahdi_hfc));
+
+ zthfc->card = card;
+ hfc_zap_initialize(zthfc);
+ card->ztdev = zthfc;
+
+ snprintf(card->proc_dir_name,
+ sizeof(card->proc_dir_name),
+ "%d", card->cardnum);
+ card->proc_dir = proc_mkdir(card->proc_dir_name, hfc_proc_zaphfc_dir);
+ card->proc_dir->owner = THIS_MODULE;
+
+ hfc_resetCard(card);
+
+ printk(KERN_INFO hfc_DRIVER_PREFIX
+ "card %d configured for %s mode at mem %#lx (0x%p) IRQ %u\n",
+ card->cardnum,
+ card->nt_mode?"NT":"TE",
+ card->io_bus_mem,
+ card->io_mem,
+ card->pcidev->irq);
+
+ cardnum++;
+
+ return 0;
+
+err_request_irq:
+ pci_free_consistent(pci_dev, hfc_FIFO_SIZE,
+ card->fifo_mem, card->fifo_bus_mem);
+err_alloc_fifo:
+ iounmap(card->io_mem);
+err_ioremap:
+err_noiobase:
+err_noirq:
+ pci_release_regions(pci_dev);
+err_pci_request_regions:
+err_pci_set_dma_mask:
+err_pci_enable_device:
+ kfree(card);
+err_alloc_hfccard:
+ return err;
+}
+
+static void __devexit hfc_remove(struct pci_dev *pci_dev)
+{
+ struct hfc_card *card = pci_get_drvdata(pci_dev);
+
+
+// unsigned long flags;
+// spin_lock_irqsave(&card->lock,flags);
+
+ printk(KERN_INFO hfc_DRIVER_PREFIX
+ "card %d: "
+ "shutting down card at %p.\n",
+ card->cardnum,
+ card->io_mem);
+
+ hfc_softreset(card);
+
+ dahdi_unregister(&card->ztdev->span);
+
+
+ // disable memio and bustmaster
+ pci_write_config_word(pci_dev, PCI_COMMAND, 0);
+
+// spin_unlock_irqrestore(&card->lock,flags);
+
+ remove_proc_entry("bufs", card->proc_dir);
+ remove_proc_entry("fifos", card->proc_dir);
+ remove_proc_entry("info", card->proc_dir);
+ remove_proc_entry(card->proc_dir_name, hfc_proc_zaphfc_dir);
+
+ free_irq(pci_dev->irq, card);
+
+ pci_free_consistent(pci_dev, hfc_FIFO_SIZE,
+ card->fifo_mem, card->fifo_bus_mem);
+
+ iounmap(card->io_mem);
+
+ pci_release_regions(pci_dev);
+
+ pci_disable_device(pci_dev);
+
+ kfree(card);
+}
+
+/******************************************
+ * Module stuff
+ ******************************************/
+
+static int __init hfc_init_module(void)
+{
+ int ret;
+
+ printk(KERN_INFO hfc_DRIVER_PREFIX
+ hfc_DRIVER_STRING " loading\n");
+
+#if (KERNEL_VERSION(2,6,26) <= LINUX_VERSION_CODE)
+ hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, NULL);
+#else
+ hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, proc_root_driver);
+#endif
+
+ ret = dahdi_pci_module(&hfc_driver);
+ return ret;
+}
+
+module_init(hfc_init_module);
+
+static void __exit hfc_module_exit(void)
+{
+ pci_unregister_driver(&hfc_driver);
+
+#if (KERNEL_VERSION(2,6,26) <= LINUX_VERSION_CODE)
+ remove_proc_entry(hfc_DRIVER_NAME, NULL);
+#else
+ remove_proc_entry(hfc_DRIVER_NAME, proc_root_driver);
+#endif
+
+ printk(KERN_INFO hfc_DRIVER_PREFIX
+ hfc_DRIVER_STRING " unloaded\n");
+}
+
+module_exit(hfc_module_exit);
+
+#endif
+
+MODULE_DESCRIPTION(hfc_DRIVER_DESCR);
+MODULE_AUTHOR("Jens Wilke <jw_vzaphfc@headissue.com>, Daniele (Vihai) Orlandi <daniele@orlandi.com>");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+
+module_param(modes, int, 0444);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+module_param_array(nt_modes, int, &nt_modes_count, 0444);
+#else
+module_param_array(nt_modes, int, nt_modes_count, 0444);
+#endif
+
+module_param(force_l1_up, int, 0444);
+#ifdef DEBUG
+module_param(debug_level, int, 0444);
+#endif
+
+MODULE_PARM_DESC(modes, "[Deprecated] bit-mask to configure NT mode");
+MODULE_PARM_DESC(nt_modes, "Comma-separated list of card IDs to configure in NT mode");
+MODULE_PARM_DESC(force_l1_up, "Don't allow L1 to go down");
+#ifdef DEBUG
+MODULE_PARM_DESC(debug_level, "Debug verbosity level");
+#endif