dahdi-linux: add support for HFC-S PCI BRI adapters 13/head
authorDaniel Golle <daniel@makrotopia.org>
Wed, 18 Feb 2015 15:10:34 +0000 (16:10 +0100)
committerDaniel Golle <daniel@makrotopia.org>
Wed, 18 Feb 2015 15:10:34 +0000 (16:10 +0100)
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
libs/dahdi-linux/Makefile
libs/dahdi-linux/patches/100-add-support-for-hfc-s-pci.patch [new file with mode: 0644]

index 2e05c6c739fcd14907eb6b19a3ce8c7acf719ab1..cefe288601b3fb37e1faeb555c61b5cb528f3d84 100644 (file)
@@ -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 (file)
index 0000000..cc73be5
--- /dev/null
@@ -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 <odicha@hotmail.com>
++ *
++ * 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 <jw_vzaphfc@headissue.com>
++ *
++ * Original author of this code is
++ * Daniele "Vihai" Orlandi <daniele@orlandi.com>
++ *
++ * Major rewrite of the driver made by
++ * Klaus-Peter Junghanns <kpj@junghanns.net>
++ *
++ * This program is free software and may be modified and
++ * distributed under the terms of the GNU Public License.
++ *
++ * Please read the README file for important infos.
++ */
++
++#include <linux/spinlock.h>
++#include <linux/init.h>
++#include <linux/pci.h>
++#include <linux/interrupt.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32))
++#include <linux/sched.h>
++#endif
++#include <linux/proc_fs.h>
++#include <linux/if_arp.h>
++
++#include <dahdi/kernel.h>
++
++#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 <jw_vzaphfc@headissue.com>, "
++              "Daniele (Vihai) Orlandi <daniele@orlandi.com>, "
++              "Jose A. Deniz <odicha@hotmail.com>");
++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 <odicha@hotmail.com>
++ *
++ * 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 <jw_vzaphfc@headissue.com>
++ *
++ * Orginal author of this code is
++ * Daniele "Vihai" Orlandi <daniele@orlandi.com>
++ *
++ * Major rewrite of the driver made by
++ * Klaus-Peter Junghanns <kpj@junghanns.net>
++ *
++ * This program is free software and may be modified and
++ * distributed under the terms of the GNU Public License.
++ *
++ */
++
++#ifndef _HFC_ZAPHFC_H
++#define _HFC_ZAPHFC_H
++
++#include <asm/io.h>
++
++#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 <daniele@orlandi.com>
++ *
++ * 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 <linux/kernel.h>
++
++#include <dahdi/kernel.h>
++
++#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 <daniele@orlandi.com>
++ *
++ * Major rewrite of the driver made by
++ * Klaus-Peter Junghanns <kpj@junghanns.net>
++ *
++ * This program is free software and may be modified and
++ * distributed under the terms of the GNU Public License.
++ *
++ */
++
++#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
++
++