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/Kbuild | 10 + drivers/dahdi/zaphfc/Makefile | 7 + drivers/dahdi/zaphfc/base.c | 1538 +++++++++++++++++++++++++++++++++++++++++ drivers/dahdi/zaphfc/fifo.c | 365 ++++++++++ drivers/dahdi/zaphfc/fifo.h | 131 ++++ drivers/dahdi/zaphfc/zaphfc.h | 409 +++++++++++ 6 files changed, 2460 insertions(+) create mode 100644 drivers/dahdi/zaphfc/Kbuild create mode 100644 drivers/dahdi/zaphfc/Makefile create mode 100644 drivers/dahdi/zaphfc/base.c create mode 100644 drivers/dahdi/zaphfc/fifo.c create mode 100644 drivers/dahdi/zaphfc/fifo.h create mode 100644 drivers/dahdi/zaphfc/zaphfc.h diff --git a/drivers/dahdi/zaphfc/Kbuild b/drivers/dahdi/zaphfc/Kbuild new file mode 100644 index 0000000..d28d113 --- /dev/null +++ b/drivers/dahdi/zaphfc/Kbuild @@ -0,0 +1,10 @@ +obj-m += zaphfc.o + +EXTRA_CFLAGS := -I$(src)/.. -Wno-undef + +zaphfc-objs := base.o fifo.o + +$(obj)/base.o: $(src)/zaphfc.h +$(obj)/fifo.o: $(src)/fifo.h + + diff --git a/drivers/dahdi/zaphfc/Makefile b/drivers/dahdi/zaphfc/Makefile new file mode 100644 index 0000000..866fdae --- /dev/null +++ b/drivers/dahdi/zaphfc/Makefile @@ -0,0 +1,7 @@ +ifdef KBUILD_EXTMOD +# We only get here on kernels 2.6.0-2.6.9 . +# For newer kernels, Kbuild will be included directly by the kernel +# build system. +include $(src)/Kbuild +else +endif 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 diff --git a/drivers/dahdi/zaphfc/fifo.c b/drivers/dahdi/zaphfc/fifo.c new file mode 100644 index 0000000..fa25f9a --- /dev/null +++ b/drivers/dahdi/zaphfc/fifo.c @@ -0,0 +1,365 @@ +/* + * fifo.c - HFC FIFO management routines + * + * Copyright (C) 2006 headissue GmbH; Jens Wilke + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Original author of this code is + * Daniele "Vihai" Orlandi + * + * This program is free software and may be modified and + * distributed under the terms of the GNU Public License. + * + */ + +#include + +#include + +#include "fifo.h" + +static void hfc_fifo_mem_read(struct hfc_chan_simplex *chan, + int z_start, + void *data, int size) +{ + int bytes_to_boundary = chan->z_max - z_start + 1; + if (bytes_to_boundary >= size) { + memcpy(data, + chan->z_base + z_start, + size); + } else { + // Buffer wrap + memcpy(data, + chan->z_base + z_start, + bytes_to_boundary); + + memcpy(data + bytes_to_boundary, + chan->fifo_base, + size - bytes_to_boundary); + } +} + +static void hfc_fifo_mem_write(struct hfc_chan_simplex *chan, + void *data, int size) +{ + int bytes_to_boundary = chan->z_max - *Z1_F1(chan) + 1; + if (bytes_to_boundary >= size) { + memcpy(chan->z_base + *Z1_F1(chan), + data, + size); + } else { + // FIFO wrap + + memcpy(chan->z_base + *Z1_F1(chan), + data, + bytes_to_boundary); + + memcpy(chan->fifo_base, + data + bytes_to_boundary, + size - bytes_to_boundary); + } +} + +int hfc_fifo_get(struct hfc_chan_simplex *chan, + void *data, int size) +{ + int available_bytes; + + // Some useless statistic + chan->bytes += size; + + available_bytes = hfc_fifo_used_rx(chan); + + if (available_bytes < size && !chan->fifo_underrun++) { + // print the warning only once + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX FIFO not enough (%d) bytes to receive!\n", + chan->chan->card->cardnum, + chan->chan->name, + available_bytes); + return -1; + } + + hfc_fifo_mem_read(chan, *Z2_F2(chan), data, size); + *Z2_F2(chan) = Z_inc(chan, *Z2_F2(chan), size); + return available_bytes - size; +} + +/* +static void hfc_fifo_drop(struct hfc_chan_simplex *chan, int size) +{ + int available_bytes = hfc_fifo_used_rx(chan); + if (available_bytes + 1 < size) { + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX FIFO not enough (%d) bytes to drop!\n", + chan->chan->card->cardnum, + chan->chan->name, + available_bytes); + + return; + } + + *Z2_F2(chan) = Z_inc(chan, *Z2_F2(chan), size); +} +*/ + +void hfc_fifo_put(struct hfc_chan_simplex *chan, + void *data, int size) +{ + struct hfc_card *card = chan->chan->card; + int used_bytes = hfc_fifo_used_tx(chan); + int free_bytes = hfc_fifo_free_tx(chan); + + if (!used_bytes && !chan->fifo_underrun++) { + // print warning only once, to make timing not worse + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX FIFO has become empty\n", + card->cardnum, + chan->chan->name); + } + if (free_bytes < size) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX FIFO full!\n", + chan->chan->card->cardnum, + chan->chan->name); + chan->fifo_full++; + hfc_clear_fifo_tx(chan); + } + + hfc_fifo_mem_write(chan, data, size); + chan->bytes += size; + *Z1_F1(chan) = Z_inc(chan, *Z1_F1(chan), size); +} + +int hfc_fifo_get_frame(struct hfc_chan_simplex *chan, void *data, int max_size) +{ + int frame_size; + u16 newz2 ; + + if (*chan->f1 == *chan->f2) { + // nothing received, strange uh? + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "get_frame called with no frame in FIFO.\n", + chan->chan->card->cardnum, + chan->chan->name); + + return -1; + } + + // frame_size includes CRC+CRC+STAT + frame_size = hfc_fifo_get_frame_size(chan); + +#ifdef DEBUG + if(debug_level == 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + frame_size); + } else if(debug_level >= 4) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "RX (f1=%02x, f2=%02x, z1=%04x, z2=%04x) len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + *chan->f1, *chan->f2, *Z1_F2(chan), *Z2_F2(chan), + frame_size); + } + + if(debug_level >= 3) { + int i; + for (i=0; i < frame_size; i++) { + printk("%02x", hfc_fifo_u8(chan, + Z_inc(chan, *Z2_F2(chan), i))); + } + + printk("\n"); + } +#endif + + if (frame_size <= 0) { +#ifdef DEBUG + if (debug_level >= 2) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "invalid (empty) frame received.\n", + chan->chan->card->cardnum, + chan->chan->name); + } +#endif + + hfc_fifo_drop_frame(chan); + return -1; + } + + // STAT is not really received + chan->bytes += frame_size - 1; + + // Calculate beginning of the next frame + newz2 = Z_inc(chan, *Z2_F2(chan), frame_size); + + // We cannot use hfc_fifo_get because of different semantic of + // "available bytes" and to avoid useless increment of Z2 + hfc_fifo_mem_read(chan, *Z2_F2(chan), data, + frame_size < max_size ? frame_size : max_size); + + if (hfc_fifo_u8(chan, Z_inc(chan, *Z2_F2(chan), + frame_size - 1)) != 0x00) { + // CRC not ok, frame broken, skipping +#ifdef DEBUG + if(debug_level >= 2) { + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "Received frame with wrong CRC\n", + chan->chan->card->cardnum, + chan->chan->name); + } +#endif + + chan->crc++; + + hfc_fifo_drop_frame(chan); + return -1; + } + + chan->frames++; + + *chan->f2 = F_inc(chan, *chan->f2, 1); + + // Set Z2 for the next frame we're going to receive + *Z2_F2(chan) = newz2; + + return frame_size; +} + +void hfc_fifo_drop_frame(struct hfc_chan_simplex *chan) +{ + int available_bytes; + u16 newz2; + + if (*chan->f1 == *chan->f2) { + // nothing received, strange eh? + printk(KERN_WARNING hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "skip_frame called with no frame in FIFO.\n", + chan->chan->card->cardnum, + chan->chan->name); + + return; + } + +// chan->drops++; + + available_bytes = hfc_fifo_used_rx(chan) + 1; + + // Calculate beginning of the next frame + newz2 = Z_inc(chan, *Z2_F2(chan), available_bytes); + + *chan->f2 = F_inc(chan, *chan->f2, 1); + + // Set Z2 for the next frame we're going to receive + *Z2_F2(chan) = newz2; +} + +void hfc_fifo_put_frame(struct hfc_chan_simplex *chan, + void *data, int size) +{ + u16 newz1; + int available_frames; + +#ifdef DEBUG + if (debug_level == 3) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + size); + } else if (debug_level >= 4) { + printk(KERN_DEBUG hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX (f1=%02x, f2=%02x, z1=%04x, z2=%04x) len %2d: ", + chan->chan->card->cardnum, + chan->chan->name, + *chan->f1, *chan->f2, *Z1_F1(chan), *Z2_F1(chan), + size); + } + + if (debug_level >= 3) { + int i; + for (i=0; i= chan->f_num) { + printk(KERN_CRIT hfc_DRIVER_PREFIX + "card %d: " + "chan %s: " + "TX FIFO total number of frames exceeded!\n", + chan->chan->card->cardnum, + chan->chan->name); + + chan->fifo_full++; + + return; + } + + hfc_fifo_put(chan, data, size); + + newz1 = *Z1_F1(chan); + + *chan->f1 = F_inc(chan, *chan->f1, 1); + + *Z1_F1(chan) = newz1; + + chan->frames++; +} + +void hfc_clear_fifo_rx(struct hfc_chan_simplex *chan) +{ + *chan->f2 = *chan->f1; + *Z2_F2(chan) = *Z1_F2(chan); +} + +void hfc_clear_fifo_tx(struct hfc_chan_simplex *chan) +{ + *chan->f1 = *chan->f2; + *Z1_F1(chan) = *Z2_F1(chan); + + if (chan->chan->status == open_voice) { + // Make sure that at least hfc_TX_FIFO_PRELOAD bytes are + // present in the TX FIFOs + + // Create hfc_TX_FIFO_PRELOAD bytes of empty data + // (0x7f is mute audio) + u8 empty_fifo[hfc_TX_FIFO_PRELOAD + DAHDI_CHUNKSIZE + hfc_RX_FIFO_PRELOAD]; + memset(empty_fifo, 0x7f, sizeof(empty_fifo)); + + hfc_fifo_put(chan, empty_fifo, sizeof(empty_fifo)); + } +} + diff --git a/drivers/dahdi/zaphfc/fifo.h b/drivers/dahdi/zaphfc/fifo.h new file mode 100644 index 0000000..08edb1e --- /dev/null +++ b/drivers/dahdi/zaphfc/fifo.h @@ -0,0 +1,131 @@ +/* + * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards + * + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * 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. + * + */ + +#ifndef _HFC_FIFO_H +#define _HFC_FIFO_H + +#include "zaphfc.h" + +static inline u16 *Z1_F1(struct hfc_chan_simplex *chan) +{ + return chan->z1_base + (*chan->f1 * 4); +} + +static inline u16 *Z2_F1(struct hfc_chan_simplex *chan) +{ + return chan->z2_base + (*chan->f1 * 4); +} + +static inline u16 *Z1_F2(struct hfc_chan_simplex *chan) +{ + return chan->z1_base + (*chan->f2 * 4); +} + +static inline u16 *Z2_F2(struct hfc_chan_simplex *chan) +{ + return chan->z2_base + (*chan->f2 * 4); +} + +static inline u16 Z_inc(struct hfc_chan_simplex *chan, u16 z, u16 inc) +{ + // declared as u32 in order to manage overflows + u32 newz = z + inc; + if (newz > chan->z_max) + newz -= chan->fifo_size; + + return newz; +} + +static inline u8 F_inc(struct hfc_chan_simplex *chan, u8 f, u8 inc) +{ + // declared as u16 in order to manage overflows + u16 newf = f + inc; + if (newf > chan->f_max) + newf -= chan->f_num; + + return newf; +} + +static inline u16 hfc_fifo_used_rx(struct hfc_chan_simplex *chan) +{ + return (*Z1_F2(chan) - *Z2_F2(chan) + chan->fifo_size) % chan->fifo_size; +} + +static inline u16 hfc_fifo_get_frame_size(struct hfc_chan_simplex *chan) +{ + // This +1 is needed because in frame mode the available bytes are Z2-Z1+1 + // while in transparent mode I wouldn't consider the byte pointed by Z2 to + // be available, otherwise, the FIFO would always contain one byte, even + // when Z1==Z2 + + return hfc_fifo_used_rx(chan) + 1; +} + +static inline u8 hfc_fifo_u8(struct hfc_chan_simplex *chan, u16 z) +{ + return *((u8 *)(chan->z_base + z)); +} + +static inline u16 hfc_fifo_used_tx(struct hfc_chan_simplex *chan) +{ + return (*Z1_F1(chan) - *Z2_F1(chan) + chan->fifo_size) % chan->fifo_size; +} + +static inline u16 hfc_fifo_free_rx(struct hfc_chan_simplex *chan) +{ + u16 free_bytes=*Z2_F1(chan) - *Z1_F1(chan); + + if (free_bytes > 0) + return free_bytes; + else + return free_bytes + chan->fifo_size; +} + +static inline u16 hfc_fifo_free_tx(struct hfc_chan_simplex *chan) +{ + u16 free_bytes=*Z2_F1(chan) - *Z1_F1(chan); + + if (free_bytes > 0) + return free_bytes; + else + return free_bytes + chan->fifo_size; +} + +static inline int hfc_fifo_has_frames(struct hfc_chan_simplex *chan) +{ + return *chan->f1 != *chan->f2; +} + +static inline u8 hfc_fifo_used_frames(struct hfc_chan_simplex *chan) +{ + return (*chan->f1 - *chan->f2 + chan->f_num) % chan->f_num; +} + +static inline u8 hfc_fifo_free_frames(struct hfc_chan_simplex *chan) +{ + return (*chan->f2 - *chan->f1 + chan->f_num) % chan->f_num; +} + +int hfc_fifo_get(struct hfc_chan_simplex *chan, void *data, int size); +void hfc_fifo_put(struct hfc_chan_simplex *chan, void *data, int size); +void hfc_fifo_drop(struct hfc_chan_simplex *chan, int size); +int hfc_fifo_get_frame(struct hfc_chan_simplex *chan, void *data, int max_size); +void hfc_fifo_drop_frame(struct hfc_chan_simplex *chan); +void hfc_fifo_put_frame(struct hfc_chan_simplex *chan, void *data, int size); +void hfc_clear_fifo_rx(struct hfc_chan_simplex *chan); +void hfc_clear_fifo_tx(struct hfc_chan_simplex *chan); + +#endif diff --git a/drivers/dahdi/zaphfc/zaphfc.h b/drivers/dahdi/zaphfc/zaphfc.h new file mode 100644 index 0000000..efa897b --- /dev/null +++ b/drivers/dahdi/zaphfc/zaphfc.h @@ -0,0 +1,409 @@ +/* + * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards + * + * Copyright (C) 2006 headissue GmbH; Jens Wilke + * Copyright (C) 2004 Daniele Orlandi + * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH + * + * Jens Wilke + * + * Orginal 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. + * + */ + +#ifndef _HFC_ZAPHFC_H +#define _HFC_ZAPHFC_H + +#include + +#define hfc_DRIVER_NAME "vzaphfc" +#define hfc_DRIVER_PREFIX hfc_DRIVER_NAME ": " +#define hfc_DRIVER_DESCR "HFC-S PCI A ISDN" +#define hfc_DRIVER_VERSION "1.42" +#define hfc_DRIVER_STRING hfc_DRIVER_DESCR " (V" hfc_DRIVER_VERSION ")" + +#define hfc_MAX_BOARDS 32 + +#ifndef PCI_DMA_32BIT +#define PCI_DMA_32BIT 0x00000000ffffffffULL +#endif + +#ifndef PCI_VENDOR_ID_SITECOM +#define PCI_VENDOR_ID_SITECOM 0x182D +#endif + +#ifndef PCI_DEVICE_ID_SITECOM_3069 +#define PCI_DEVICE_ID_SITECOM_3069 0x3069 +#endif + +#define hfc_RESET_DELAY 20 + +#define hfc_CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define hfc_CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + +/* PCI memory mapped I/O */ + +#define hfc_PCI_MEM_SIZE 0x0100 +#define hfc_PCI_MWBA 0x80 + +/* GCI/IOM bus monitor registers */ + +#define hfc_C_I 0x08 +#define hfc_TRxR 0x0C +#define hfc_MON1_D 0x28 +#define hfc_MON2_D 0x2C + + +/* GCI/IOM bus timeslot registers */ + +#define hfc_B1_SSL 0x80 +#define hfc_B2_SSL 0x84 +#define hfc_AUX1_SSL 0x88 +#define hfc_AUX2_SSL 0x8C +#define hfc_B1_RSL 0x90 +#define hfc_B2_RSL 0x94 +#define hfc_AUX1_RSL 0x98 +#define hfc_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ + +#define hfc_B1_D 0xA0 +#define hfc_B2_D 0xA4 +#define hfc_AUX1_D 0xA8 +#define hfc_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ + +#define hfc_MST_EMOD 0xB4 +#define hfc_MST_MODE 0xB8 +#define hfc_CONNECT 0xBC + + +/* Interrupt and status registers */ + +#define hfc_FIFO_EN 0x44 +#define hfc_TRM 0x48 +#define hfc_B_MODE 0x4C +#define hfc_CHIP_ID 0x58 +#define hfc_CIRM 0x60 +#define hfc_CTMT 0x64 +#define hfc_INT_M1 0x68 +#define hfc_INT_M2 0x6C +#define hfc_INT_S1 0x78 +#define hfc_INT_S2 0x7C +#define hfc_STATUS 0x70 + +/* S/T section registers */ + +#define hfc_STATES 0xC0 +#define hfc_SCTRL 0xC4 +#define hfc_SCTRL_E 0xC8 +#define hfc_SCTRL_R 0xCC +#define hfc_SQ 0xD0 +#define hfc_CLKDEL 0xDC +#define hfc_B1_REC 0xF0 +#define hfc_B1_SEND 0xF0 +#define hfc_B2_REC 0xF4 +#define hfc_B2_SEND 0xF4 +#define hfc_D_REC 0xF8 +#define hfc_D_SEND 0xF8 +#define hfc_E_REC 0xFC + +/* Bits and values in various HFC PCI registers */ + +/* bits in status register (READ) */ +#define hfc_STATUS_PCI_PROC 0x02 +#define hfc_STATUS_NBUSY 0x04 +#define hfc_STATUS_TIMER_ELAP 0x10 +#define hfc_STATUS_STATINT 0x20 +#define hfc_STATUS_FRAMEINT 0x40 +#define hfc_STATUS_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define hfc_CTMT_TRANSB1 0x01 +#define hfc_CTMT_TRANSB2 0x02 +#define hfc_CTMT_TIMER_CLEAR 0x80 +#define hfc_CTMT_TIMER_MASK 0x1C +#define hfc_CTMT_TIMER_3_125 (0x01 << 2) +#define hfc_CTMT_TIMER_6_25 (0x02 << 2) +#define hfc_CTMT_TIMER_12_5 (0x03 << 2) +#define hfc_CTMT_TIMER_25 (0x04 << 2) +#define hfc_CTMT_TIMER_50 (0x05 << 2) +#define hfc_CTMT_TIMER_400 (0x06 << 2) +#define hfc_CTMT_TIMER_800 (0x07 << 2) +#define hfc_CTMT_AUTO_TIMER 0x20 + +/* bits in CIRM (Write) */ +#define hfc_CIRM_AUX_MSK 0x07 +#define hfc_CIRM_RESET 0x08 +#define hfc_CIRM_B1_REV 0x40 +#define hfc_CIRM_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define hfc_INTS_B1TRANS 0x01 +#define hfc_INTS_B2TRANS 0x02 +#define hfc_INTS_DTRANS 0x04 +#define hfc_INTS_B1REC 0x08 +#define hfc_INTS_B2REC 0x10 +#define hfc_INTS_DREC 0x20 +#define hfc_INTS_L1STATE 0x40 +#define hfc_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define hfc_M2_PROC_TRANS 0x01 +#define hfc_M2_GCI_I_CHG 0x02 +#define hfc_M2_GCI_MON_REC 0x04 +#define hfc_M2_IRQ_ENABLE 0x08 +#define hfc_M2_PMESEL 0x80 + +/* bits in STATES */ +#define hfc_STATES_STATE_MASK 0x0F +#define hfc_STATES_LOAD_STATE 0x10 +#define hfc_STATES_ACTIVATE 0x20 +#define hfc_STATES_DO_ACTION 0x40 +#define hfc_STATES_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define hfc_MST_MODE_MASTER 0x01 +#define hfc_MST_MODE_SLAVE 0x00 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define hfc_SCTRL_B1_ENA 0x01 +#define hfc_SCTRL_B2_ENA 0x02 +#define hfc_SCTRL_MODE_TE 0x00 +#define hfc_SCTRL_MODE_NT 0x04 +#define hfc_SCTRL_LOW_PRIO 0x08 +#define hfc_SCTRL_SQ_ENA 0x10 +#define hfc_SCTRL_TEST 0x20 +#define hfc_SCTRL_NONE_CAP 0x40 +#define hfc_SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define hfc_SCTRL_E_AUTO_AWAKE 0x01 +#define hfc_SCTRL_E_DBIT_1 0x04 +#define hfc_SCTRL_E_IGNORE_COL 0x08 +#define hfc_SCTRL_E_CHG_B1_B2 0x80 + +/* bits in SCTRL_R */ +#define hfc_SCTRL_R_B1_ENA 0x01 +#define hfc_SCTRL_R_B2_ENA 0x02 + +/* bits in FIFO_EN register */ +#define hfc_FIFOEN_B1TX 0x01 +#define hfc_FIFOEN_B1RX 0x02 +#define hfc_FIFOEN_B2TX 0x04 +#define hfc_FIFOEN_B2RX 0x08 +#define hfc_FIFOEN_DTX 0x10 +#define hfc_FIFOEN_DRX 0x20 + +#define hfc_FIFOEN_B1 (hfc_FIFOEN_B1TX|hfc_FIFOEN_B1RX) +#define hfc_FIFOEN_B2 (hfc_FIFOEN_B2TX|hfc_FIFOEN_B2RX) +#define hfc_FIFOEN_D (hfc_FIFOEN_DTX|hfc_FIFOEN_DRX) + +/* bits in the CONNECT register */ +#define hfc_CONNECT_B1_HFC_from_ST 0x00 +#define hfc_CONNECT_B1_HFC_from_GCI 0x01 +#define hfc_CONNECT_B1_ST_from_HFC 0x00 +#define hfc_CONNECT_B1_ST_from_GCI 0x02 +#define hfc_CONNECT_B1_GCI_from_HFC 0x00 +#define hfc_CONNECT_B1_GCI_from_ST 0x04 + +#define hfc_CONNECT_B2_HFC_from_ST 0x00 +#define hfc_CONNECT_B2_HFC_from_GCI 0x08 +#define hfc_CONNECT_B2_ST_from_HFC 0x00 +#define hfc_CONNECT_B2_ST_from_GCI 0x10 +#define hfc_CONNECT_B2_GCI_from_HFC 0x00 +#define hfc_CONNECT_B2_GCI_from_ST 0x20 + +/* bits in the TRM register */ +#define hfc_TRM_TRANS_INT_00 0x00 +#define hfc_TRM_TRANS_INT_01 0x01 +#define hfc_TRM_TRANS_INT_10 0x02 +#define hfc_TRM_TRANS_INT_11 0x04 +#define hfc_TRM_ECHO 0x20 +#define hfc_TRM_B1_PLUS_B2 0x40 +#define hfc_TRM_IOM_TEST_LOOP 0x80 + +/* bits in the __SSL and __RSL registers */ +#define hfc_SRSL_STIO 0x40 +#define hfc_SRSL_ENABLE 0x80 +#define hfc_SRCL_SLOT_MASK 0x1f + +/* FIFO memory definitions */ + +#define hfc_FIFO_SIZE 0x8000 + +#define hfc_UGLY_FRAMEBUF 0x2000 + +#define hfc_TX_FIFO_PRELOAD DAHDI_CHUNKSIZE + 2 +#define hfc_RX_FIFO_PRELOAD 4 + +/* HDLC STUFF */ +#define hfc_HDLC_BUF_LEN 32 /* arbitrary, just the max # of byts we will send to DAHDI per call */ + + +/* NOTE: FIFO pointers are not declared volatile because accesses to the + * FIFOs are inherently safe. + */ + +#ifdef DEBUG +extern int debug_level; +#endif + +struct hfc_chan; + +struct hfc_chan_simplex { + struct hfc_chan_duplex *chan; + + u8 zaptel_buffer[DAHDI_CHUNKSIZE]; + + u8 ugly_framebuf[hfc_UGLY_FRAMEBUF]; + int ugly_framebuf_size; + u16 ugly_framebuf_off; + + void *z1_base,*z2_base; + void *fifo_base; + void *z_base; + u16 z_min; + u16 z_max; + u16 fifo_size; + + u8 *f1,*f2; + u8 f_min; + u8 f_max; + u8 f_num; + + unsigned long long frames; + unsigned long long bytes; + unsigned long long fifo_full; + unsigned long long crc; + unsigned long long fifo_underrun; +}; + +enum hfc_chan_status { + free, + open_framed, + open_voice, + sniff_aux, + loopback, +}; + +struct hfc_chan_duplex { + struct hfc_card *card; + + char *name; + int number; + + enum hfc_chan_status status; + int open_by_netdev; + int open_by_zaptel; + + unsigned short protocol; + + spinlock_t lock; + + struct hfc_chan_simplex rx; + struct hfc_chan_simplex tx; + +}; + +typedef struct hfc_card { + int cardnum; + struct pci_dev *pcidev; + struct dahdi_hfc *ztdev; + struct proc_dir_entry *proc_dir; + char proc_dir_name[32]; + + struct proc_dir_entry *proc_info; + struct proc_dir_entry *proc_fifos; + struct proc_dir_entry *proc_bufs; + + unsigned long io_bus_mem; + void *io_mem; + + dma_addr_t fifo_bus_mem; + void *fifo_mem; + void *fifos; + + int nt_mode; + int sync_loss_reported; + int late_irqs; + + u8 l1_state; + int fifo_suspended; + int ignore_first_timer_interrupt; + + struct { + u8 m1; + u8 m2; + u8 fifo_en; + u8 trm; + u8 connect; + u8 sctrl; + u8 sctrl_r; + u8 sctrl_e; + u8 ctmt; + u8 cirm; + } regs; + + struct hfc_chan_duplex chans[3]; + int echo_enabled; + + + + int debug_event; + + spinlock_t lock; + unsigned int irq; + unsigned int iomem; + int ticks; + int clicks; + unsigned char *pci_io; + void *fifomem; // start of the shared mem + + unsigned int pcibus; + unsigned int pcidevfn; + + int drecinframe; + + unsigned char cardno; + struct hfc_card *next; + + + +} hfc_card; + +typedef struct dahdi_hfc { + unsigned int usecount; + struct dahdi_span span; + struct dahdi_chan chans[3]; + struct dahdi_chan *_chans[3]; + struct hfc_card *card; + struct dahdi_chan *sigchan; /* pointer to the signalling channel for this span */ + int sigactive; /* nonzero means we're in the middle of sending an HDLC frame */ + atomic_t hdlc_pending; /* hdlc_hard_xmit() increments, hdlc_tx_frame() decrements */ + int frames_out; + int frames_in; +} dahdi_hfc; + + + +static inline u8 hfc_inb(struct hfc_card *card, int offset) +{ + return readb(card->io_mem + offset); +} + +static inline void hfc_outb(struct hfc_card *card, int offset, u8 value) +{ + writeb(value, card->io_mem + offset); +} + +#endif -- cgit v1.2.3