From fa31ad6b55d21a4c093e79f1ed41f2562d695cdc Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Wed, 18 Feb 2015 16:10:34 +0100 Subject: [PATCH] dahdi-linux: add support for HFC-S PCI BRI adapters Signed-off-by: Daniel Golle --- libs/dahdi-linux/Makefile | 15 + .../100-add-support-for-hfc-s-pci.patch | 2746 +++++++++++++++++ 2 files changed, 2761 insertions(+) create mode 100644 libs/dahdi-linux/patches/100-add-support-for-hfc-s-pci.patch diff --git a/libs/dahdi-linux/Makefile b/libs/dahdi-linux/Makefile index 2e05c6c..cefe288 100644 --- a/libs/dahdi-linux/Makefile +++ b/libs/dahdi-linux/Makefile @@ -51,6 +51,20 @@ define KernelPackage/dahdi-echocan-oslec/description This package contains DAHDI OSLEC echo canceller support. endef +define KernelPackage/dahdi-hfcs + SUBMENU:=Voice over IP + TITLE:=DAHDI driver for HFC-S PCI + DEPENDS:=@PCI_SUPPORT kmod-dahdi + URL:=http://sourceforge.net/projects/dahdi-hfcs/ + FILES:= $(PKG_BUILD_DIR)/drivers/dahdi/hfcs/dahdi_hfcs.$(LINUX_KMOD_SUFFIX) + AUTOLOAD:=$(call AutoProbe,dahdi_hfcs) +endef + +define KernelPackage/dahdi-hfcs/description + This package contains DAHDI driver for HFC-S based PCI BRI adapters. +endef + + define Build/Configure endef @@ -89,3 +103,4 @@ endef $(eval $(call KernelPackage,dahdi)) $(eval $(call KernelPackage,dahdi-echocan-oslec)) +$(eval $(call KernelPackage,dahdi-hfcs)) diff --git a/libs/dahdi-linux/patches/100-add-support-for-hfc-s-pci.patch b/libs/dahdi-linux/patches/100-add-support-for-hfc-s-pci.patch new file mode 100644 index 0000000..cc73be5 --- /dev/null +++ b/libs/dahdi-linux/patches/100-add-support-for-hfc-s-pci.patch @@ -0,0 +1,2746 @@ +Index: dahdi-linux-2.10.0.1/drivers/dahdi/Kbuild +=================================================================== +--- dahdi-linux-2.10.0.1.orig/drivers/dahdi/Kbuild ++++ dahdi-linux-2.10.0.1/drivers/dahdi/Kbuild +@@ -13,6 +13,7 @@ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCT + obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM24XXP) += wctdm24xxp/ + obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE12XP) += wcte12xp/ + obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE13XP) += wcte13xp.o ++obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_HFCS) += hfcs/ + + wcte13xp-objs := wcte13xp-base.o wcxb_spi.o wcxb.o wcxb_flash.o + CFLAGS_wcte13xp-base.o += -I$(src)/oct612x -I$(src)/oct612x/include -I$(src)/oct612x/octdeviceapi -I$(src)/oct612x/octdeviceapi/oct6100api +Index: dahdi-linux-2.10.0.1/drivers/dahdi/Kconfig +=================================================================== +--- dahdi-linux-2.10.0.1.orig/drivers/dahdi/Kconfig ++++ dahdi-linux-2.10.0.1/drivers/dahdi/Kconfig +@@ -291,4 +291,14 @@ config DAHDI_WCTE11XP + + If unsure, say Y. + ++config DAHDI_HFCS ++ tristate "Support for various HFC-S PCI BRI adapters" ++ depends on DAHDI && PCI ++ default DAHDI ++ ---help--- ++ To compile this driver as a module, choose M here: the ++ module will be called dahdi_hfcs. ++ ++ If unsure, say Y. ++ + source "drivers/dahdi/xpp/Kconfig" +Index: dahdi-linux-2.10.0.1/drivers/dahdi/hfcs/base.c +=================================================================== +--- /dev/null ++++ dahdi-linux-2.10.0.1/drivers/dahdi/hfcs/base.c +@@ -0,0 +1,1742 @@ ++/* ++ * dahdi_hfcs.c - Dahdi driver for HFC-S PCI A based ISDN BRI cards ++ * ++ * Dahdi rewrite in hardhdlc mode ++ * Jose A. Deniz ++ * ++ * Copyright (C) 2011, Raoul Bönisch ++ * Copyright (C) 2009, Jose A. Deniz ++ * Copyright (C) 2006, headissue 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 ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)) ++#include ++#endif ++#include ++#include ++ ++#include ++ ++#include "dahdi_hfcs.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 ++ ++/* ++ * Mode Te for all ++ */ ++static int modes; ++static int nt_modes[hfc_MAX_BOARDS]; ++static int nt_modes_count; ++static int force_l1_up; ++static struct proc_dir_entry *hfc_proc_dahdi_hfcs_dir; ++ ++#define DEBUG ++#ifdef DEBUG ++int debug_level; ++#endif ++ ++#ifndef FALSE ++#define FALSE 0 ++#endif ++#ifndef TRUE ++#define TRUE (!FALSE) ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) ++#define SET_PROC_DIRENTRY_OWNER(p) do { (p)->owner = THIS_MODULE; } while(0); ++#else ++#define SET_PROC_DIRENTRY_OWNER(p) do { } while(0); ++#endif ++ ++static DEFINE_PCI_DEVICE_TABLE(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); ++ ++static struct pci_driver hfc_driver = { ++ .name = hfc_DRIVER_NAME, ++ .id_table = hfc_pci_ids, ++ .probe = hfc_probe, ++ .remove = __devexit_p(hfc_remove), ++}; ++ ++/****************************************** ++ * HW routines ++ ******************************************/ ++ ++static void hfc_softreset(struct hfc_card *card) ++{ ++ printk(KERN_INFO hfc_DRIVER_PREFIX ++ "card %d: " ++ "resetting\n", ++ card->cardnum); ++ ++/* ++ * Softreset procedure. Put it on, wait and off again ++ */ ++ hfc_outb(card, hfc_CIRM, hfc_CIRM_RESET); ++ udelay(6); ++ hfc_outb(card, hfc_CIRM, 0); ++ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout((hfc_RESET_DELAY * HZ) / 1000); ++} ++ ++static void hfc_resetCard(struct hfc_card *card) ++{ ++ card->regs.m1 = 0; ++ hfc_outb(card, hfc_INT_M1, card->regs.m1); ++ ++ card->regs.m2 = 0; ++ hfc_outb(card, hfc_INT_M2, card->regs.m2); ++ ++ 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)) { ++ ++ hfc_outb(card, hfc_STATES, hfc_STATES_DO_ACTION | ++ hfc_STATES_ACTIVATE| ++ hfc_STATES_NT_G2_G3); ++ ++ /* ++ * 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 ++ } ++} ++ ++ ++/******************* ++ * Dahdi interface * ++ *******************/ ++ ++static int hfc_dahdi_open(struct dahdi_chan *dahdi_chan) ++{ ++ struct hfc_chan_duplex *chan = dahdi_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_dahdi = TRUE; ++ try_module_get(THIS_MODULE); ++ spin_unlock(&chan->lock); ++ ++ switch (chan->number) { ++ case D: ++ break; ++ ++ case B1: ++ card->regs.m2 |= hfc_M2_PROC_TRANS; ++ /* ++ * Enable transparent mode ++ */ ++ card->regs.ctmt |= hfc_CTMT_TRANSB1; ++ /* ++ * Reversed bit order ++ */ ++ card->regs.cirm |= hfc_CIRM_B1_REV; ++ /* ++ * Enable transmission ++ */ ++ card->regs.sctrl |= hfc_SCTRL_B1_ENA; ++ /* ++ * Enable reception ++ */ ++ card->regs.sctrl_r |= hfc_SCTRL_R_B1_ENA; ++ break; ++ ++ case B2: ++ card->regs.m2 |= hfc_M2_PROC_TRANS; ++ 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 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, ++ dahdi_chan->name); ++ ++ return 0; ++} ++ ++static int hfc_dahdi_close(struct dahdi_chan *dahdi_chan) ++{ ++ struct hfc_chan_duplex *chan = dahdi_chan->pvt; ++ struct hfc_card *card = chan->card; ++ ++ if (!card) { ++ printk(KERN_CRIT hfc_DRIVER_PREFIX ++ "hfc_dahdi_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_dahdi = FALSE; ++ ++ spin_unlock(&chan->lock); ++ ++ switch (chan->number) { ++ case D: ++ break; ++ ++ case 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: ++ 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, ++ dahdi_chan->name); ++ ++ return 0; ++} ++ ++static int hfc_dahdi_rbsbits(struct dahdi_chan *chan, int bits) ++{ ++ return 0; ++} ++ ++static int hfc_dahdi_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->dahdi_dev; ++ ++ atomic_inc(&hfccard->hdlc_pending); ++ ++} ++ ++static int hfc_dahdi_startup(struct file *file, struct dahdi_span *span) ++{ ++ struct dahdi_hfc *dahdi_hfcs = dahdi_hfc_from_span(span); ++ struct hfc_card *hfctmp = dahdi_hfcs->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_dahdi_shutdown(struct dahdi_span *span) ++{ ++ return 0; ++} ++ ++static int hfc_dahdi_maint(struct dahdi_span *span, int cmd) ++{ ++ return 0; ++} ++ ++static int hfc_dahdi_chanconfig(struct file *file, 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->dahdi_dev; ++ ++ if ((sigtype == DAHDI_SIG_HARDHDLC) && (hfccard->sigchan == d_chan)) { ++ hfccard->sigactive = 0; ++ atomic_set(&hfccard->hdlc_pending, 0); ++ } ++ ++ return 0; ++} ++ ++static int hfc_dahdi_spanconfig(struct file *file, struct dahdi_span *span, ++ struct dahdi_lineconfig *lc) ++{ ++ span->lineconfig = lc->lineconfig; ++ ++ return 0; ++} ++ ++static const struct dahdi_span_ops hfc_dahdi_span_ops = { ++ .owner = THIS_MODULE, ++ .chanconfig = hfc_dahdi_chanconfig, ++ .spanconfig = hfc_dahdi_spanconfig, ++ .startup = hfc_dahdi_startup, ++ .shutdown = hfc_dahdi_shutdown, ++ .maint = hfc_dahdi_maint, ++ .rbsbits = hfc_dahdi_rbsbits, ++ .open = hfc_dahdi_open, ++ .close = hfc_dahdi_close, ++ .ioctl = hfc_dahdi_ioctl, ++ .hdlc_hard_xmit = hfc_hdlc_hard_xmit ++}; ++ ++static int hfc_dahdi_initialize(struct dahdi_hfc *hfccard) ++{ ++ struct hfc_card *hfctmp = hfccard->card; ++ int i; ++ ++ hfccard->ddev = dahdi_create_device(); ++ if (!hfccard->ddev) ++ return -ENOMEM; ++ ++ memset(&hfccard->span, 0x0, sizeof(struct dahdi_span)); ++ ++ /* ++ * ZTHFC ++ * ++ * Cards' and channels' names shall contain "ZTHFC" ++ * as the dahdi-tools look for this string to guess framing. ++ * We don't want to modify dahdi-tools only in order to change this. ++ * ++ * So we choose for a span name: DAHDI HFC-S formerly known as ZTHFC. :-) ++ */ ++ ++ sprintf(hfccard->span.name, "DAHDI_HFCS_FKA_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 ? SPANTYPE_DIGITAL_BRI_NT : ++ SPANTYPE_DIGITAL_BRI_TE; ++ hfccard->ddev->manufacturer = "Cologne Chips"; ++ hfccard->span.flags = 0; ++ hfccard->span.ops = &hfc_dahdi_span_ops; ++ hfccard->ddev->devicetype = kasprintf(GFP_KERNEL, "HFC-S PCI-A ISDN"); ++ hfccard->ddev->location = kasprintf(GFP_KERNEL, "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; ++ hfccard->span.offset = 0; ++ ++ for (i = 0; i < hfccard->span.channels; i++) { ++ memset(&hfccard->chans[i], 0x0, sizeof(struct dahdi_chan)); ++ ++ sprintf(hfccard->chans[i].name, ++ "DAHDI_HFCS_FKA_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[DAHDI_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.dahdi_buffer; ++ ++ hfccard->chans[DAHDI_D].writechunk = ++ hfctmp->chans[D].tx.dahdi_buffer; ++ ++ hfccard->chans[DAHDI_D].pvt = &hfctmp->chans[D]; ++ ++ hfccard->chans[DAHDI_B1].readchunk = ++ hfctmp->chans[B1].rx.dahdi_buffer; ++ ++ hfccard->chans[DAHDI_B1].writechunk = ++ hfctmp->chans[B1].tx.dahdi_buffer; ++ ++ hfccard->chans[DAHDI_B1].pvt = &hfctmp->chans[B1]; ++ ++ hfccard->chans[DAHDI_B2].readchunk = ++ hfctmp->chans[B2].rx.dahdi_buffer; ++ ++ hfccard->chans[DAHDI_B2].writechunk = ++ hfctmp->chans[B2].tx.dahdi_buffer; ++ ++ hfccard->chans[DAHDI_B2].pvt = &hfctmp->chans[B2]; ++ ++ list_add_tail(&hfccard->span.device_node, &hfccard->ddev->spans); ++ if (dahdi_register_device(hfccard->ddev, &hfccard->card->pcidev->dev)) { ++ printk(KERN_NOTICE "Unable to register device with DAHDI\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static void hfc_dahdi_transmit(struct hfc_chan_simplex *chan) ++{ ++ hfc_fifo_put(chan, chan->dahdi_buffer, DAHDI_CHUNKSIZE); ++} ++ ++static void hfc_dahdi_receive(struct hfc_chan_simplex *chan) ++{ ++ hfc_fifo_get(chan, chan->dahdi_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 ++ * ++ * Are we still using unpatched libpri? Is this tested at runtime??? ++ * Does it only affect zaptel or DAHDI, too? ++ */ ++ ++ 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->dahdi_dev; ++ 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_dahdi_transmit(&card->chans[B1].tx); ++ if (card->regs.fifo_en & hfc_FIFOEN_B2TX) ++ hfc_dahdi_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); ++ } ++ } ++ } ++ /* ++ * dahdi hdlc frame tx done ++ */ ++ ++ if (card->regs.fifo_en & hfc_FIFOEN_B1RX) ++ hfc_dahdi_receive(&card->chans[B1].rx); ++ else ++ memset(&card->chans[B1].rx.dahdi_buffer, 0x7f, ++ sizeof(card->chans[B1].rx.dahdi_buffer)); ++ ++ if (card->regs.fifo_en & hfc_FIFOEN_B2RX) ++ hfc_dahdi_receive(&card->chans[B2].rx); ++ else ++ memset(&card->chans[B2].rx.dahdi_buffer, 0x7f, ++ sizeof(card->chans[B1].rx.dahdi_buffer)); ++ ++ /* ++ * Echo cancellation ++ */ ++ dahdi_ec_chunk(&hfccard->chans[DAHDI_B1], ++ card->chans[B1].rx.dahdi_buffer, ++ card->chans[B1].tx.dahdi_buffer); ++ dahdi_ec_chunk(&hfccard->chans[DAHDI_B2], ++ card->chans[B2].rx.dahdi_buffer, ++ card->chans[B2].tx.dahdi_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; ++ } ++ } ++ /* ++ * dahdi hdlc 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_dahdi && ++ 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_dahdi) { ++ 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; ++ int err; ++ int i; ++ ++ struct hfc_card *card = NULL; ++ struct dahdi_hfc *dahdi_hfcs = 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); ++ ++ err = pci_enable_device(pci_dev); ++ if (err) ++ goto err_pci_enable_device; ++ ++ err = pci_set_dma_mask(pci_dev, PCI_DMA_32BIT); ++ if (err) { ++ 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); ++ err = pci_request_regions(pci_dev, hfc_DRIVER_NAME); ++ if (err) { ++ 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; ++ } ++ ++ card->io_mem = ioremap(card->io_bus_mem, hfc_PCI_MEM_SIZE); ++ if (!(card->io_mem)) { ++ 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); ++ ++ 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 ++ ++ if (err) { ++ printk(KERN_CRIT hfc_DRIVER_PREFIX ++ "card %d: " ++ "unable to register irq\n", ++ card->cardnum); ++ goto err_request_irq; ++ } ++ ++ card->nt_mode = FALSE; ++ ++ if (modes & (1 << card->cardnum)) ++ card->nt_mode = TRUE; ++ ++ for (i = 0; i < nt_modes_count; i++) { ++ if (nt_modes[i] == card->cardnum) ++ card->nt_mode = TRUE; ++ } ++ ++ /* ++ * D Channel ++ */ ++ 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 Channel ++ */ ++ 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 Channel ++ */ ++ 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; ++ ++ /* ++ * All done ++ */ ++ ++ dahdi_hfcs = kmalloc(sizeof(struct dahdi_hfc), GFP_KERNEL); ++ if (!dahdi_hfcs) { ++ printk(KERN_CRIT hfc_DRIVER_PREFIX ++ "unable to kmalloc!\n"); ++ goto err_request_irq; ++ } ++ memset(dahdi_hfcs, 0x0, sizeof(struct dahdi_hfc)); ++ ++ dahdi_hfcs->card = card; ++ hfc_dahdi_initialize(dahdi_hfcs); ++ card->dahdi_dev = dahdi_hfcs; ++ ++ snprintf(card->proc_dir_name, ++ sizeof(card->proc_dir_name), ++ "%d", card->cardnum); ++ card->proc_dir = proc_mkdir(card->proc_dir_name, hfc_proc_dahdi_hfcs_dir); ++ SET_PROC_DIRENTRY_OWNER(card->proc_dir); ++ ++ 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); ++ ++ ++ printk(KERN_INFO hfc_DRIVER_PREFIX ++ "card %d: " ++ "shutting down card at %p.\n", ++ card->cardnum, ++ card->io_mem); ++ ++ if (!card) { ++ return; ++ } ++ ++ hfc_softreset(card); ++ ++ dahdi_unregister_device(card->dahdi_dev->ddev); ++ ++ ++ /* ++ * disable memio and bustmaster ++ */ ++ pci_write_config_word(pci_dev, PCI_COMMAND, 0); ++ ++/* ++BUG: these proc entries just cause Call traces, so removed. ++ 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_dahdi_hfcs_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"); ++#ifdef DEBUG ++printk(KERN_INFO hfc_DRIVER_PREFIX "Check /var/log/kern-debug.log for debugging output level %d.", debug_level); ++printk(KERN_DEBUG hfc_DRIVER_PREFIX "base.c is debugging."); ++#endif ++ ++#if (KERNEL_VERSION(2, 6, 26) <= LINUX_VERSION_CODE) ++ hfc_proc_dahdi_hfcs_dir = proc_mkdir(hfc_DRIVER_NAME, NULL); ++#else ++ hfc_proc_dahdi_hfcs_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 , " ++ "Jose A. Deniz "); ++MODULE_ALIAS("dahdi_hfcs"); ++#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 +Index: dahdi-linux-2.10.0.1/drivers/dahdi/hfcs/dahdi_hfcs.h +=================================================================== +--- /dev/null ++++ dahdi-linux-2.10.0.1/drivers/dahdi/hfcs/dahdi_hfcs.h +@@ -0,0 +1,419 @@ ++/* ++ * dahdi_hfcs.h - Dahdi driver for HFC-S PCI A based ISDN BRI cards ++ * ++ * Dahdi port by Jose A. Deniz ++ * ++ * Copyright (C) 2009 Jose A. Deniz ++ * 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 "dahdi_hfcs" ++#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 dahdi_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_dahdi; ++ ++ 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 *dahdi_dev; ++ 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 __iomem *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_device *ddev; ++ struct dahdi_span span; ++ struct dahdi_chan chans[3]; ++ struct dahdi_chan *_chans[3]; ++ struct hfc_card *card; ++ ++ /* pointer to the signalling channel for this span */ ++ struct dahdi_chan *sigchan; ++ /* nonzero means we're in the middle of sending an HDLC frame */ ++ int sigactive; ++ /* hdlc_hard_xmit() increments, hdlc_tx_frame() decrements */ ++ atomic_t hdlc_pending; ++ int frames_out; ++ int frames_in; ++ ++} dahdi_hfc; ++ ++static inline struct dahdi_hfc* dahdi_hfc_from_span(struct dahdi_span *span) { ++ return container_of(span, struct dahdi_hfc, span); ++} ++ ++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 +Index: dahdi-linux-2.10.0.1/drivers/dahdi/hfcs/fifo.c +=================================================================== +--- /dev/null ++++ dahdi-linux-2.10.0.1/drivers/dahdi/hfcs/fifo.c +@@ -0,0 +1,380 @@ ++/* ++ * 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. ++ * ++ */ ++ ++#define DEBUG ++#ifdef DEBUG ++extern int debug_level; ++#endif ++ ++#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; ++} ++ ++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; ++ } ++ ++ 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 < size; i++) ++ printk("%02x", ((u8 *)data)[i]); ++ ++ printk("\n"); ++ } ++#endif ++ ++ available_frames = hfc_fifo_free_frames(chan); ++ ++ if (available_frames >= 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)); ++ } ++} ++ +Index: dahdi-linux-2.10.0.1/drivers/dahdi/hfcs/fifo.h +=================================================================== +--- /dev/null ++++ dahdi-linux-2.10.0.1/drivers/dahdi/hfcs/fifo.h +@@ -0,0 +1,139 @@ ++/* ++ * fifo.h - Dahdi 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 "dahdi_hfcs.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 +Index: dahdi-linux-2.10.0.1/drivers/dahdi/hfcs/Kbuild +=================================================================== +--- /dev/null ++++ dahdi-linux-2.10.0.1/drivers/dahdi/hfcs/Kbuild +@@ -0,0 +1,10 @@ ++obj-m += dahdi_hfcs.o ++ ++EXTRA_CFLAGS := -I$(src)/.. -Wno-undef ++ ++dahdi_hfcs-objs := base.o fifo.o ++ ++$(obj)/base.o: $(src)/dahdi_hfcs.h ++$(obj)/fifo.o: $(src)/fifo.h ++ ++ -- 2.30.2