From 68336b33f33424c243ea99b5560cfcc984334291 Mon Sep 17 00:00:00 2001 From: "Jose A. Deniz" Date: Thu, 6 Aug 2009 23:35:42 +0000 Subject: 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 --- drivers/dahdi/zaphfc/base.c | 1538 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1538 insertions(+) create mode 100644 drivers/dahdi/zaphfc/base.c (limited to 'drivers/dahdi/zaphfc/base.c') 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 + * + * Copyright (C) 2006, headiisue GmbH; Jens Wilke + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Jens Wilke + * + * Original author of this code is + * Daniele "Vihai" Orlandi + * + * Major rewrite of the driver made by + * Klaus-Peter Junghanns + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; icardnum) + 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 , Daniele (Vihai) Orlandi "); +#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 -- cgit v1.2.3