| --- a/drivers/dahdi/Kbuild |
| +++ b/drivers/dahdi/Kbuild |
| @@ -12,6 +12,7 @@ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCT |
| obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTC4XXP) += wctc4xxp/ |
| obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM24XXP) += wctdm24xxp/ |
| 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 |
| --- a/drivers/dahdi/Kconfig |
| +++ b/drivers/dahdi/Kconfig |
| @@ -223,4 +223,14 @@ config DAHDI_DYNAMIC_LOC |
| 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" |
| --- /dev/null |
| +++ b/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 = pci_register_driver(&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 |
| --- /dev/null |
| +++ b/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 |
| --- /dev/null |
| +++ b/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)); |
| + } |
| +} |
| + |
| --- /dev/null |
| +++ b/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 |
| --- /dev/null |
| +++ b/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 |
| + |
| + |