Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 302561 Details for
Bug 404407
request for net-misc/dahdi-2.5.0.2-r3 - updated tzafrir patch ...
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
99-non-digium-and-oslec.diff
99-tzafrir-2.5.patch (text/plain), 519.99 KB, created by
Jaco Kroon
on 2012-02-20 08:03:38 UTC
(
hide
)
Description:
99-non-digium-and-oslec.diff
Filename:
MIME Type:
Creator:
Jaco Kroon
Created:
2012-02-20 08:03:38 UTC
Size:
519.99 KB
patch
obsolete
>diff --git a/drivers/dahdi/Kbuild b/drivers/dahdi/Kbuild >index 8bbfea0..c69f56f 100644 >--- a/drivers/dahdi/Kbuild >+++ b/drivers/dahdi/Kbuild >@@ -29,5 +29,15 @@ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_STEVE2) += dahdi_echocan_sec2.o > obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_KB1) += dahdi_echocan_kb1.o > obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_MG2) += dahdi_echocan_mg2.o > >+ifdef CONFIG_PCI >+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_AP400) += ap400/ >+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXD115) += opvxd115/ >+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXA1200) += opvxa1200/ >+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCOPENPCI) += wcopenpci.o >+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ZAPHFC) += zaphfc/ >+endif >+obj-$(DAHDI_BUILD_ALL)$(CONFIG_ECHO) += ../staging/echo/ >+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_ECHOCAN_OSLEC) += dahdi_echocan_oslec.o >+ > obj-m += $(DAHDI_MODULES_EXTRA) > >diff --git a/drivers/dahdi/Kconfig b/drivers/dahdi/Kconfig >index 6952c6a..3ad3e6c 100644 >--- a/drivers/dahdi/Kconfig >+++ b/drivers/dahdi/Kconfig >@@ -292,3 +292,76 @@ config DAHDI_WCTE11XP > If unsure, say Y. > > source "drivers/dahdi/xpp/Kconfig" >+ >+ >+config DAHDI_OPVXD115 >+ tristate "OpenVox Single-T1/E1/J1 Support" >+ depends on DAHDI && PCI >+ default DAHDI >+ ---help--- >+ This driver provides support for the following OpenVox >+ Wildcard products: >+ >+ * D115P/DE115P/D130P/DE130P (PCI) >+ * D115E/DE115E/D130E/DE130E (PCI-E) >+ >+ To compile this driver as a module, choose M here: the >+ module will be called opvxd115. >+ >+ If unsure, say Y. >+ >+config DAHDI_OPVXA1200 >+ tristate "OpenVox 8/12 ports analog card Support" >+ depends on DAHDI && PCI >+ default DAHDI >+ ---help--- >+ This driver provides support for the following OpenVox >+ Wildcard products: >+ >+ * A1200P (PCI) >+ * A1200E (PCI-E) >+ * A800P (PCI) >+ * A800E (PCI-E) >+ >+ To compile this driver as a module, choose M here: the >+ module will be called opvxa1200. >+ >+ If unsure, say Y. >+ >+config DAHDI_WCOPENPCI >+ tristate "Voicetronix OpenPCI Interface DAHDI driver" >+ depends on DAHDI && PCI >+ default DAHDI >+ ---help--- >+ This driver provides support for the Voicetronix OpenPCI Interface. >+ >+ To compile this driver as a module, choose M here: the >+ module will be called wcopenpci. >+ >+ If unsure, say Y. >+ >+config DAHDI_ZAPHFC >+ tristate "HFC-S DAHDI Driver" >+ depends on DAHDI && PCI >+ default DAHDI >+ ---help--- >+ This driver provides DAHDI support for various HFC-S single-port >+ ISDN (BRI) cards. >+ >+ To compile this driver as a module, choose M here: the >+ module will be called zaphfc. >+ >+ If unsure, say Y. >+ >+config ECHO >+ tristate "Line Echo Canceller support" >+ default DAHDI >+ --help-- >+ This driver provides line echo cancelling support for mISDN and >+ DAHDI drivers. >+ >+ To compile this driver as a module, choose M here: the >+ module will be called echo. >+ >+ If unsure, say Y. >+ >diff --git a/drivers/dahdi/ap400/Kbuild b/drivers/dahdi/ap400/Kbuild >new file mode 100644 >index 0000000..d124261 >--- /dev/null >+++ b/drivers/dahdi/ap400/Kbuild >@@ -0,0 +1,26 @@ >+obj-m += ap400.o >+ >+EXTRA_CFLAGS := -I$(src)/.. >+ >+ap400-objs := ap400_drv.o >+ >+# APEC_SUPPORT >+ECHO_FIRMWARE := $(wildcard $(src)/OCT61*.ima) >+ifneq ($(strip $(ECHO_FIRMWARE)),) >+ EXTRA_CFLAGS+=-DAPEC_SUPPORT $(shell $(src)/../oct612x/octasic-helper cflags $(src)/../oct612x) -Wno-undef >+ ap400-objs += apec.o $(shell $(src)/../oct612x/octasic-helper objects ../oct612x) firmware_oct6104e-64d.o firmware_oct6104e-128d.o >+endif >+ >+$(obj)/apec.o: $(src)/apec.h $(src)/../oct612x/include/oct6100api/oct6100_api.h >+ >+$(obj)/firmware_oct6104e-64d.o: $(src)/OCT6104E-64D.ima $(obj)/ap400_drv.o $(src)/../firmware/make_firmware_object >+ @echo Making firmware object file for $(notdir $<) >+ @cd $(src) && ../firmware/make_firmware_object $(notdir $<) $@ $(obj)/ap400_drv.o >+ >+$(obj)/firmware_oct6104e-128d.o: $(src)/OCT6104E-128D.ima $(obj)/ap400_drv.o $(src)/../firmware/make_firmware_object >+ @echo Making firmware object file for $(notdir $<) >+ @cd $(src) && ../firmware/make_firmware_object $(notdir $<) $@ $(obj)/ap400_drv.o >+ >+$(src)/../firmware/make_firmware_object: >+ +$(MAKE) -C $(src)/../firmware make_firmware_object >+ >diff --git a/drivers/dahdi/ap400/ap400.h b/drivers/dahdi/ap400/ap400.h >new file mode 100644 >index 0000000..ba233d1 >--- /dev/null >+++ b/drivers/dahdi/ap400/ap400.h >@@ -0,0 +1,107 @@ >+/* >+ * AP4XX T1/E1 PCI Driver >+ * >+ * Written by Ronaldo Valiati <aligera@aligera.com.br> >+ * >+ * Based on previous works, designs, and archetectures conceived and >+ * written by Jim Dixon <jim@lambdatel.com> and Mark Spencer <markster@digium.com>. >+ * >+ * Copyright (C) 2001 Jim Dixon / Zapata Telephony. >+ * Copyright (C) 2001-2005, Digium, Inc. >+ * >+ * All rights reserved. >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation; either version 2 of the License, or >+ * (at your option) any later version. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. >+ * >+ */ >+ >+#include <linux/ioctl.h> >+ >+ >+#define AP4_GET_ALARMS _IOW (DAHDI_CODE, 60, int) >+#define AP4_GET_SLIPS _IOW (DAHDI_CODE, 61, int) >+ >+#define AP4XX_CARD_ID 0x41434532 // "ACE2" >+#define APE4XX_CARD_ID 0x41504534 // "APE4" >+ >+#define AP_CAS_BASE 0x0080 >+#define AP_DATA_BASE 0x0100 >+ >+#define AP_CARD_TYPE_REG 0x0001 >+#define AP_T1E1_CONFIG_REG 0x0003 >+#define AP_E1_CONFIG_REG 0x0004 >+#define AP_E1_STATUS_REG 0x0005 >+#define AP_LEDS_REG 0x0006 >+#define AP_CLKSRC_REG 0x0007 >+#define AP_HWCONFIG_REG 0x0008 >+#define AP_INT_CONTROL_REG 0x0009 >+#define AP_CNT_IRQ_REG 0x000B >+#define AP_CNT_CV_REG 0x000C >+#define AP_CNT_CRC_REG 0x000D >+#define AP_CLEAR_IRQ_REG 0x000E >+#define AP_CNT_SLIP_REG 0x000F >+ >+#define AP_HWID_MASK 0x00F0 >+ >+#define AP_CLKSRC_MASK 0x07 >+ >+#define AP_LIU1_LINECODE 0x0080 >+#define AP_LIU2_LINECODE 0x0100 >+#define AP_LIU_RESET_BIT 0x0200 >+ >+#define AP_E1_AIS_STATUS 0x01 >+#define AP_E1_BFAE_STATUS 0x02 >+#define AP_E1_MFAE_STATUS 0x04 >+#define AP_E1_SYNC_STATUS 0x08 >+#define AP_E1_CAS_STATUS 0x10 >+#define AP_E1_LOS_STATUS 0x20 >+#define AP_E1_RAI_STATUS 0x40 >+ >+#define AP_E1_RAI_CONFIG 0x01 >+#define AP_E1_LOOP_CONFIG 0x10 >+#define AP_E1_CASEN_CONFIG 0x20 >+#define AP_E1_PCM30_CONFIG 0x40 >+#define AP_E1_CRCEN_CONFIG 0x80 >+ >+#define AP_INT_CTL_ENABLE 0x01 >+#define AP_INT_CTL_ACTIVE 0x02 >+ >+#define AP_HWID_1E1_RJ 0x01 >+#define AP_HWID_2E1_RJ 0x00 >+#define AP_HWID_4E1_RJ 0x02 >+#define AP_HWID_T1 0x04 >+ >+#define AP4_T1_NE1_SEL 0x04 >+#define AP4_T1_ESF_NSF 0x02 >+#define AP4_T1_CAS_ENABLE 0x01 >+ >+#define AP4_T1_FRAME_SYNC 0x01 >+ >+ >+typedef enum { >+ AP_PULS_E1_75 = 0, >+ AP_PULS_E1_120, >+ AP_PULS_DSX1_0FT, >+ AP_PULS_DSX1_133FT, >+ AP_PULS_DSX1_266FT, >+ AP_PULS_DSX1_399FT, >+ AP_PULS_DSX1_533FT, >+ AP_PULS_J1_110, >+ AP_PULS_DS1_0DB, >+ AP_PULS_DS1_M075DB, >+ AP_PULS_DS1_M150DB, >+ AP_PULS_DS1_M225DB >+} liu_mode; >+ >diff --git a/drivers/dahdi/ap400/ap400_drv.c b/drivers/dahdi/ap400/ap400_drv.c >new file mode 100644 >index 0000000..f625f12 >--- /dev/null >+++ b/drivers/dahdi/ap400/ap400_drv.c >@@ -0,0 +1,2362 @@ >+/* >+ * AP4XX PCI Card Driver >+ * >+ * Written by Ronaldo Valiati <aligera@aligera.com.br> >+ * >+ * Based on previous works, designs, and architectures conceived and >+ * written by Jim Dixon <jim@lambdatel.com> and Mark Spencer <markster@digium.com>. >+ * >+ * Copyright (C) 2001 Jim Dixon / Zapata Telephony. >+ * Copyright (C) 2001-2005, Digium, Inc. >+ * >+ * All rights reserved. >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation; either version 2 of the License, or >+ * (at your option) any later version. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. >+ * >+ */ >+ >+#include <linux/kernel.h> >+#include <linux/errno.h> >+#include <linux/module.h> >+#include <linux/pci.h> >+#include <linux/init.h> >+#include <linux/sched.h> >+#include <linux/interrupt.h> >+#include <linux/time.h> >+#include <linux/delay.h> >+#include <linux/proc_fs.h> >+#include <dahdi/kernel.h> >+#include <linux/moduleparam.h> >+ >+#include "ap400.h" >+ >+//#define AP400_DEBUG >+#ifdef AP400_DEBUG >+#define PDEBUG(fmt, args...) { \ >+ printk(KERN_DEBUG "AP400 (%d): ",__LINE__); \ >+ printk(fmt "\n", ## args); \ >+} >+#else >+#define PDEBUG(fmt, args...) >+#endif >+ >+/* >+ * Tasklets provide better system interactive response at the cost of the >+ * possibility of losing a frame of data at very infrequent intervals. If >+ * you are more concerned with the performance of your machine, enable the >+ * tasklets. If you are strict about absolutely no drops, then do not enable >+ * tasklets. >+ */ >+ >+/* #define ENABLE_TASKLETS */ >+ >+ >+/* Work queues are a way to better distribute load on SMP systems */ >+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) >+/* >+ * Work queues can significantly improve performance and scalability >+ * on multi-processor machines, but requires bypassing some kernel >+ * API's, so it's not guaranteed to be compatible with all kernels. >+ */ >+/* #define ENABLE_WORKQUEUES */ >+#endif >+ >+/* Enable HDLC support by hardware */ >+#ifdef AP400_HDLC >+#include "ap400_hdlc/ap400_hdlc.c" >+#endif >+ >+//#define APEC_SUPPORT >+#ifdef APEC_SUPPORT >+#include "apec.h" >+#endif >+ >+/* Workarounds */ >+#ifndef IRQF_SHARED >+#define IRQF_SHARED SA_SHIRQ >+#endif >+#ifndef IRQF_DISABLED >+#define IRQF_DISABLED SA_INTERRUPT >+#endif >+#ifndef __iomem >+#define __iomem >+#endif >+ >+/* Enable prefetching may help performance */ >+#define ENABLE_PREFETCH >+ >+/* Define to get more attention-grabbing but slightly more I/O using >+ alarm status */ >+#define FANCY_ALARM >+ >+#define DEBUG_MAIN (1 << 0) >+#define DEBUG_DTMF (1 << 1) >+#define DEBUG_REGS (1 << 2) >+#define DEBUG_TSI (1 << 3) >+#define DEBUG_ECHOCAN (1 << 4) >+#define DEBUG_RBS (1 << 5) >+#define DEBUG_FRAMER (1 << 6) >+ >+static int clock_source = -1; >+static int tdm_loop = 0; >+static int apec_enable = 1; >+module_param(tdm_loop, int, 0600); >+module_param(apec_enable, int, 0600); >+ >+#ifdef ENABLE_WORKQUEUES >+#include <linux/cpumask.h> >+ >+/* XXX UGLY!!!! XXX We have to access the direct structures of the workqueue which >+ are only defined within workqueue.c because they don't give us a routine to allow us >+ to nail a work to a particular thread of the CPU. Nailing to threads gives us substantially >+ higher scalability in multi-CPU environments though! */ >+ >+/* >+ * The per-CPU workqueue (if single thread, we always use cpu 0's). >+ * >+ * The sequence counters are for flush_scheduled_work(). It wants to wait >+ * until until all currently-scheduled works are completed, but it doesn't >+ * want to be livelocked by new, incoming ones. So it waits until >+ * remove_sequence is >= the insert_sequence which pertained when >+ * flush_scheduled_work() was called. >+ */ >+ >+struct cpu_workqueue_struct { >+ >+ spinlock_t lock; >+ >+ long remove_sequence; /* Least-recently added (next to run) */ >+ long insert_sequence; /* Next to add */ >+ >+ struct list_head worklist; >+ wait_queue_head_t more_work; >+ wait_queue_head_t work_done; >+ >+ struct workqueue_struct *wq; >+ task_t *thread; >+ >+ int run_depth; /* Detect run_workqueue() recursion depth */ >+} ____cacheline_aligned; >+ >+/* >+ * The externally visible workqueue abstraction is an array of >+ * per-CPU workqueues: >+ */ >+struct workqueue_struct { >+ struct cpu_workqueue_struct cpu_wq[NR_CPUS]; >+ const char *name; >+ struct list_head list; /* Empty if single thread */ >+}; >+ >+/* Preempt must be disabled. */ >+static void __ap4_queue_work(struct cpu_workqueue_struct *cwq, >+ struct work_struct *work) >+{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&cwq->lock, flags); >+ work->wq_data = cwq; >+ list_add_tail(&work->entry, &cwq->worklist); >+ cwq->insert_sequence++; >+ wake_up(&cwq->more_work); >+ spin_unlock_irqrestore(&cwq->lock, flags); >+} >+ >+/* >+ * Queue work on a workqueue. Return non-zero if it was successfully >+ * added. >+ * >+ * We queue the work to the CPU it was submitted, but there is no >+ * guarantee that it will be processed by that CPU. >+ */ >+static inline int ap4_queue_work(struct workqueue_struct *wq, struct work_struct *work, int cpu) >+{ >+ int ret = 0; >+ >+ if (!test_and_set_bit(0, &work->pending)) { >+ BUG_ON(!list_empty(&work->entry)); >+ __ap4_queue_work(wq->cpu_wq + cpu, work); >+ ret = 1; >+ } >+ return ret; >+} >+ >+#endif >+ >+static int debug=0; >+static int timingcable; >+static int highestorder; >+static int t1e1override = -1; >+static int j1mode = 0; >+static int loopback = 0; >+static int alarmdebounce = 0; >+ >+/* Enabling bursting can more efficiently utilize PCI bus bandwidth, but >+ can also cause PCI bus starvation, especially in combination with other >+ aggressive cards. Please note that burst mode has no effect on CPU >+ utilization / max number of calls / etc. */ >+static int noburst = 1; >+static int debugslips = 0; >+static int polling = 0; >+ >+#ifdef FANCY_ALARM >+static int altab[] = { >+0, 0, 0, 1, 2, 3, 4, 6, 8, 9, 11, 13, 16, 18, 20, 22, 24, 25, 27, 28, 29, 30, 31, 31, 32, 31, 31, 30, 29, 28, 27, 25, 23, 22, 20, 18, 16, 13, 11, 9, 8, 6, 4, 3, 2, 1, 0, 0, >+}; >+#endif >+ >+#define FLAG_STARTED (1 << 0) >+#define FLAG_NMF (1 << 1) >+#define FLAG_SENDINGYELLOW (1 << 2) >+ >+#define TYPE_T1 1 /* is a T1 card */ >+#define TYPE_E1 2 /* is an E1 card */ >+#define TYPE_J1 3 /* is a running J1 */ >+ >+struct devtype { >+ char *desc; >+ unsigned int flags; >+}; >+ >+static struct devtype ap401 = { "Aligera AP401", 0 }; >+static struct devtype ap402 = { "Aligera AP402", 0 }; >+static struct devtype ap404 = { "Aligera AP404", 0 }; >+static struct devtype ape401 = { "Aligera APE401", 0 }; >+static struct devtype ape402 = { "Aligera APE402", 0 }; >+static struct devtype ape404 = { "Aligera APE404", 0 }; >+ >+struct ap4; >+ >+struct ap4_span { >+ struct ap4 *owner; >+ unsigned int *writechunk; /* Double-word aligned write memory */ >+ unsigned int *readchunk; /* Double-word aligned read memory */ >+ int spantype; /* card type, T1 or E1 or J1 */ >+ int sync; >+ int psync; >+ int alarmtimer; >+ int redalarms; >+ int notclear; >+ int alarmcount; >+ int spanflags; >+ int syncpos; >+ int e1check; /* E1 check */ >+ int reload_cas; >+ unsigned char casbuf[15]; >+ unsigned int slipcount; >+ struct dahdi_span span; >+ unsigned char txsigs[16]; /* Transmit sigs */ >+ int loopupcnt; >+ int loopdowncnt; >+ unsigned char ec_chunk1[31][DAHDI_CHUNKSIZE]; /* first EC chunk buffer */ >+ unsigned char ec_chunk2[31][DAHDI_CHUNKSIZE]; /* second EC chunk buffer */ >+ int irqmisses; >+#ifdef ENABLE_WORKQUEUES >+ struct work_struct swork; >+#endif >+ struct dahdi_chan *chans[32]; /* Individual channels */ >+}; >+ >+struct ap4_regs { >+ volatile u32 card_id; // 00h R0 >+ volatile u16 fpga_ver; // 04h R1 >+ volatile u16 span_num; // 06h R1 >+ u32 __unused; // 08h R2 >+ volatile u32 liu_config; // 0Ch R3 >+ volatile u32 e1_config; // 10h R4 >+ volatile u32 e1_status; // 14h R5 >+ volatile u32 leds; // 18h R6 >+ volatile u32 clock_source; // 1Ch R7 >+ u32 __unused3[8]; // 20h - 3Ch R8 - R15 >+ volatile u32 echo_ctrl; // 40h R16 >+ volatile u32 echo_data; // 44h R17 >+ volatile u32 t1_status; // 48h R18 >+ volatile u32 t1_config; // 4Ch R19 >+}; >+ >+struct ap4 { >+ /* This structure exists one per card */ >+ struct pci_dev *dev; /* Pointer to PCI device */ >+ struct ap4_regs *hw_regs; >+ unsigned int intcount; >+ int flag_1st_irq; >+ int num; /* Which card we are */ >+ int fpgaver; /* version of FPGA */ >+ int hwid; /* hardware ID */ >+ int globalconfig; /* Whether global setup has been done */ >+ int syncsrc; /* active sync source */ >+ struct ap4_span *tspans[4]; /* Individual spans */ >+ int numspans; /* Number of spans on the card */ >+ int blinktimer[4]; >+#ifdef FANCY_ALARM >+ int alarmpos[4]; >+#endif >+ int irq; /* IRQ used by device */ >+ int order; /* Order */ >+ int flags; /* Device flags */ >+ int ledreg; /* LED Register */ >+ int e1recover; /* E1 recovery timer */ >+ unsigned long memaddr; /* Base address of card */ >+ unsigned long memlen; >+ volatile unsigned int *membase; /* Base address of card */ >+ int spansstarted; /* number of spans started */ >+ /* spinlock_t lock; */ /* lock context */ >+ spinlock_t reglock; /* lock register access */ >+ volatile unsigned int *writechunk; /* Double-word aligned write memory */ >+ volatile unsigned int *readchunk; /* Double-word aligned read memory */ >+#ifdef ENABLE_WORKQUEUES >+ atomic_t worklist; >+ struct workqueue_struct *workq; >+#else >+#ifdef ENABLE_TASKLETS >+ int taskletrun; >+ int taskletsched; >+ int taskletpending; >+ int taskletexec; >+ int txerrors; >+ struct tasklet_struct ap4_tlet; >+#endif >+#endif >+ unsigned int passno; /* number of interrupt passes */ >+ struct devtype *dt; >+ char *variety; >+ int last0; /* for detecting double-missed IRQ */ >+ int checktiming; /* Set >0 to cause the timing source to be checked */ >+#ifdef AP400_HDLC >+ struct card_s *hdlc_card; >+#endif >+#ifdef APEC_SUPPORT >+ int apec_enable; >+ struct apec_s *apec; >+#endif >+}; >+ >+ >+static void __set_clear(struct ap4 *wc, int span); >+static int ap4_startup(struct file *file, struct dahdi_span *span); >+static int ap4_shutdown(struct dahdi_span *span); >+static int ap4_rbsbits(struct dahdi_chan *chan, int bits); >+static int ap4_maint(struct dahdi_span *span, int cmd); >+static int ap4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data); >+static void __ap4_set_timing_source(struct ap4 *wc, int unit); >+static void __ap4_check_alarms(struct ap4 *wc, int span); >+static void __ap4_check_sigbits(struct ap4 *wc, int span); >+ >+ >+#define AP_ACTIVATE (1 << 12) >+ >+#define AP_OFF (0) >+#define AP_ON (1) >+ >+#define MAX_AP4_CARDS 64 >+ >+#ifdef ENABLE_TASKLETS >+static void ap4_tasklet(unsigned long data); >+#endif >+ >+static struct ap4 *cards[MAX_AP4_CARDS]; >+ >+//#define ap_debugk(fmt,args...) printk("ap400 -> %s: "fmt, __PRETTY_FUNCTION__, ##args) >+#define ap_debugk(fmt,args...) >+ >+//#define TIMER_DEBUG 1 >+ >+#ifdef TIMER_DEBUG >+struct timer_list ap4xx_opt_timer; >+unsigned int delay = 1000; >+module_param(delay, uint, S_IRUGO); >+#endif >+ >+#define PCI_DEVICE_ID_AP4XX 0x1004 >+ >+static inline void __ap4_set_led(struct ap4 *wc, int span, int color) >+{ >+ wc->ledreg &= ~(AP_ON << span); >+ wc->ledreg |= (color << span); >+ *(wc->membase+AP_LEDS_REG) &= ~0x0000000F; >+ *(wc->membase+AP_LEDS_REG) |= ((wc->ledreg)&0x0F); >+} >+ >+static inline void ap4_activate(struct ap4 *wc) >+{ >+ wc->ledreg |= AP_ACTIVATE; >+} >+ >+static void __set_clear(struct ap4 *wc, int span) >+{ >+ int i,j; >+ int oldnotclear; >+ unsigned short val=0; >+ struct ap4_span *ts = wc->tspans[span]; >+ >+ oldnotclear = ts->notclear; >+ if (ts->spantype == TYPE_T1) { >+ for (i=0;i<24;i++) { >+ j = (i/8); >+ if (ts->span.chans[i]->flags & DAHDI_FLAG_CLEAR) { >+ val |= 1 << (7 - (i % 8)); >+ ts->notclear &= ~(1 << i); >+ } else >+ ts->notclear |= (1 << i); >+ if ((i % 8)==7) { >+ val = 0; >+ } >+ } >+ } else { >+ for (i=0;i<31;i++) { >+ if (ts->span.chans[i]->flags & DAHDI_FLAG_CLEAR) >+ ts->notclear &= ~(1 << i); >+ else >+ ts->notclear |= (1 << i); >+ } >+ } >+} >+ >+#ifdef APEC_SUPPORT >+ >+#define APEC_CTRL_RESET 0x80000000 >+#define APEC_CTRL_DDR_NCKE 0x40000000 >+#define APEC_CTRL_EC_DISABLE 0x20000000 >+#define APEC_CTRL_DAS 0x00080000 >+#define APEC_CTRL_RD 0x00040000 >+#define APEC_CTRL_REQ 0x00020000 >+#define APEC_CTRL_READY 0x00010000 >+ >+#define APEC_ACCESS_TIMEOUT 1000 >+ >+static inline u16 oct_raw_read (struct ap4_regs *regs, unsigned short addr) >+{ >+ unsigned short data; >+ // Poll ready bit >+ while ((regs->echo_ctrl & APEC_CTRL_READY) == 0); >+ // Write control bits and address >+ regs->echo_ctrl = APEC_CTRL_RD | APEC_CTRL_REQ | (addr & 0xFFFF); >+ while ((regs->echo_ctrl & APEC_CTRL_READY) == 0); >+ data = regs->echo_data & 0xFFFF; >+ //PDEBUG("Raw Read 0x%04hX @ 0x%08X", data, addr); >+ return data; >+} >+ >+static inline void oct_raw_write (struct ap4_regs *regs, unsigned short addr, >+ unsigned short data) >+{ >+ // Poll ready bit >+ while ((regs->echo_ctrl & APEC_CTRL_READY) == 0); >+ // Write data, then control bits and address >+ regs->echo_data = data & 0xFFFF; >+ regs->echo_ctrl = APEC_CTRL_REQ | (addr & 0xFFFF); >+ // Poll ready bit >+ while ((regs->echo_ctrl & APEC_CTRL_READY) == 0); >+ //PDEBUG("Raw Write 0x%04hX @ 0x%08X", data, addr); >+ //oct_raw_read(regs, addr); >+} >+ >+static inline int oct_ext_wait (struct ap4_regs *regs) >+{ >+ int i = APEC_ACCESS_TIMEOUT; >+ while ((oct_raw_read(regs, 0x0) & 0x100) && (i-- > 0)); >+ if (i == -1) { >+ printk(KERN_WARNING "Wait access_req timeout\n"); >+ return -1; >+ } >+ return 0; >+} >+ >+static inline u16 oct_ind_read (struct ap4_regs *regs, unsigned int addr) >+{ >+ // Poll access_req bit >+ if (oct_ext_wait(regs)) >+ return 0; >+ // Write extended indirect registers >+ oct_raw_write(regs, 0x8, (addr >> 20) & 0x1FFF); >+ oct_raw_write(regs, 0xA, (addr >> 4) & 0xFFFF); >+ oct_raw_write(regs, 0x0, ((addr & 0xE) << 8) | 0x101); >+ // Poll access_req bit >+ if (oct_ext_wait(regs)) >+ return 0; >+ // Return data >+ return oct_raw_read(regs, 0x4); >+} >+ >+static inline void oct_ind_write (struct ap4_regs *regs, unsigned int addr, >+ unsigned short data) >+{ >+ // Poll access_req bit >+ if (oct_ext_wait(regs)) >+ return; >+ oct_raw_write(regs, 0x8, (addr >> 20) & 0x1FFF); >+ oct_raw_write(regs, 0xA, (addr >> 4) & 0xFFFF); >+ oct_raw_write(regs, 0x4, data); >+ oct_raw_write(regs, 0x0, ((addr & 0xE) << 8) | 0x3101); >+ // Poll access_req bit >+ if (oct_ext_wait(regs)) >+ return; >+} >+ >+static inline u16 oct_dir_read (struct ap4_regs *regs, unsigned int addr) >+{ >+ // Poll access_req bit >+ if (oct_ext_wait(regs)) >+ return 0; >+ // Write extended direct registers >+ oct_raw_write(regs, 0x8, (addr >> 20) & 0x1FFF); >+ oct_raw_write(regs, 0xA, (addr >> 4) & 0xFFFF); >+ oct_raw_write(regs, 0x0, 0x1); >+ regs->echo_ctrl = APEC_CTRL_DAS | APEC_CTRL_RD | APEC_CTRL_REQ | (addr & 0xFFFF); >+ while ((regs->echo_ctrl & APEC_CTRL_READY) == 0); >+ // Return data >+ return regs->echo_data; >+} >+ >+static inline void oct_dir_write (struct ap4_regs *regs, unsigned int addr, >+ unsigned short data) >+{ >+ // Poll access_req bit >+ if (oct_ext_wait(regs)) >+ return; >+ // Write extended direct registers >+ oct_raw_write(regs, 0x8, (addr >> 20) & 0x1FFF); >+ oct_raw_write(regs, 0xA, (addr >> 4) & 0xFFFF); >+ oct_raw_write(regs, 0x0, 0x3001); >+ regs->echo_data = data & 0xFFFF; >+ regs->echo_ctrl = APEC_CTRL_DAS | APEC_CTRL_REQ | (addr & 0xFFFF); >+ while ((regs->echo_ctrl & APEC_CTRL_READY) == 0); >+} >+ >+ >+unsigned int oct_read (void *card, unsigned int addr) >+{ >+ struct ap4 *wc = card; >+ int flags; >+ unsigned short data; >+ spin_lock_irqsave(&wc->reglock, flags); >+ data = oct_ind_read(wc->hw_regs, addr); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ PDEBUG("Read 0x%04hX @ 0x%08X", data, addr); >+ return data; >+} >+ >+void oct_write (void *card, unsigned int addr, unsigned int data) >+{ >+ struct ap4 *wc = card; >+ int flags; >+ spin_lock_irqsave(&wc->reglock, flags); >+ oct_ind_write(wc->hw_regs, addr, data); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ PDEBUG("Write 0x%04hX @ 0x%08X", data, addr); >+} >+ >+static int ap4_apec_init(struct ap4 *wc) >+{ >+ int laws[4]; >+ int i; >+ unsigned int apec_capacity; >+ struct firmware embedded_firmware; >+ const struct firmware *firmware = &embedded_firmware; >+#if !defined(HOTPLUG_FIRMWARE) >+ extern void _binary_OCT6104E_64D_ima_size; >+ extern u8 _binary_OCT6104E_64D_ima_start[]; >+ extern void _binary_OCT6104E_128D_ima_size; >+ extern u8 _binary_OCT6104E_128D_ima_start[]; >+#else >+ static const char oct64_firmware[] = "OCT6104E-64D.ima"; >+ static const char oct128_firmware[] = "OCT6104E-128D.ima"; >+#endif >+ >+ // Enable DDR and Reset Octasic >+ wc->hw_regs->echo_ctrl |= APEC_CTRL_RESET; >+ wc->hw_regs->echo_ctrl |= APEC_CTRL_DDR_NCKE; >+ udelay(500); >+ wc->hw_regs->echo_ctrl &= APEC_CTRL_RESET; >+ wc->hw_regs->echo_ctrl &= APEC_CTRL_DDR_NCKE; >+ wc->hw_regs->echo_ctrl &= APEC_CTRL_EC_DISABLE; >+ >+ /* Setup alaw vs ulaw rules */ >+ for (i = 0; i < wc->numspans; i++) { >+ if (wc->tspans[i]->span.channels > 24) >+ laws[i] = 1; // E1: alaw >+ else >+ laws[i] = 0; // T1: ulaw >+ } >+ >+ switch ((apec_capacity = apec_capacity_get(wc))) { >+ case 64: >+#if defined(HOTPLUG_FIRMWARE) >+ if ((request_firmware(&firmware, oct64_firmware, &wc->dev->dev) != 0) || >+ !firmware) { >+ printk("%s: firmware %s not available from userspace\n", >+ wc->variety, oct64_firmware); >+ return -1; >+ } >+#else >+ embedded_firmware.data = _binary_OCT6104E_64D_ima_start; >+ /* Yes... this is weird. objcopy gives us a symbol containing >+ the size of the firmware, not a pointer to a variable containing >+ the size. The only way we can get the value of the symbol >+ is to take its address, so we define it as a pointer and >+ then cast that value to the proper type. >+ */ >+ embedded_firmware.size = (size_t) &_binary_OCT6104E_64D_ima_size; >+#endif >+ break; >+ case 128: >+#if defined(HOTPLUG_FIRMWARE) >+ if ((request_firmware(&firmware, oct128_firmware, &wc->dev->dev) != 0) || >+ !firmware) { >+ printk("%s: firmware %s not available from userspace\n", >+ wc->variety, oct128_firmware); >+ return -1; >+ } >+#else >+ embedded_firmware.data = _binary_OCT6104E_128D_ima_start; >+ /* Yes... this is weird. objcopy gives us a symbol containing >+ the size of the firmware, not a pointer to a variable containing >+ the size. The only way we can get the value of the symbol >+ is to take its address, so we define it as a pointer and >+ then cast that value to the proper type. >+ */ >+ embedded_firmware.size = (size_t) &_binary_OCT6104E_128D_ima_size; >+#endif >+ break; >+ default: >+ printk(KERN_INFO "Unsupported channel capacity found on" >+ "echo cancellation module (%d).\n", apec_capacity); >+ return -1; >+ } >+ >+ if (!(wc->apec = apec_init(wc, laws, wc->numspans, firmware))) { >+ printk(KERN_WARNING "APEC: Failed to initialize\n"); >+ if (firmware != &embedded_firmware) >+ release_firmware(firmware); >+ return -1; >+ } >+ >+ if (firmware != &embedded_firmware) >+ release_firmware(firmware); >+ >+ printk(KERN_INFO "APEC: Present and operational servicing %d span(s)\n", wc->numspans); >+ return 0; >+} >+ >+void ap4_apec_release(struct ap4 *wc) >+{ >+ // Disabel DDR and reset Octasic >+ wc->hw_regs->echo_ctrl |= APEC_CTRL_RESET; >+ wc->hw_regs->echo_ctrl |= APEC_CTRL_DDR_NCKE; >+ wc->hw_regs->echo_ctrl |= APEC_CTRL_EC_DISABLE; >+ if (wc->apec) >+ apec_release(wc->apec); >+} >+ >+ >+static int ap4_echocan(struct dahdi_chan *chan, int eclen) >+{ >+ struct ap4 *wc = chan->pvt; >+ int channel; >+ >+ if (!wc->apec) >+ return -ENODEV; >+ if (debug) >+ printk(KERN_DEBUG "AP400: ap4_echocan @ Span %d Channel %d Length: %d\n", >+ chan->span->offset, chan->chanpos, eclen); >+ channel = (chan->chanpos << 2) | chan->span->offset; >+ apec_setec(wc->apec, channel, eclen); >+ return 0; >+} >+ >+#endif // APEC_SUPPORT >+ >+ >+static int ap4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) >+{ >+ struct ap4 *wc = chan->pvt; >+ int span = 0; >+ int alarms = 0; >+ unsigned char c, e1_cfg; >+ >+ switch(cmd) { >+ case AP4_GET_ALARMS: >+ if (copy_from_user(&span, (int *)data, sizeof(int))) >+ return -EFAULT; >+ // span starts in zero >+ span--; >+ if (wc->tspans[span]->spantype == TYPE_E1) { >+ /* le status e configuracao do E1 */ >+ c = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*span)); >+ e1_cfg = ((*(wc->membase+AP_E1_CONFIG_REG))>>(8*span)); >+ if( c & AP_E1_LOS_STATUS) { >+ alarms = 0x01; >+ } else if( c & AP_E1_AIS_STATUS) { >+ alarms = 0x02; >+ } else if(!(c & AP_E1_BFAE_STATUS)) { >+ alarms = 0x04; >+ if (c & AP_E1_RAI_STATUS) >+ alarms |= 0x08; >+ // Erro de MFA: 00 - MFA desabilitado, 01 - erro de MFA, 10 - MFA OK >+ if ( (c & AP_E1_MFAE_STATUS) && (e1_cfg & AP_E1_CRCEN_CONFIG) ) >+ alarms |= 0x10; >+ else if ( (!(c & AP_E1_MFAE_STATUS)) && (e1_cfg & AP_E1_CRCEN_CONFIG) ) >+ alarms |= 0x20; >+ // Erro de CAS: 00 - desabilitado, 01 - erro de CAS, 10 - CAS OK >+ if ( (!(c & AP_E1_CAS_STATUS)) && (e1_cfg & AP_E1_PCM30_CONFIG)) >+ alarms |= 0x40; >+ else if ( (c & AP_E1_CAS_STATUS) && (e1_cfg & AP_E1_PCM30_CONFIG)) >+ alarms |= 0x80; >+ } >+ } else { >+ /* le status e configuracao do E1 */ >+ c = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*span)); >+ if( c & AP_E1_LOS_STATUS) >+ alarms = 0x01; >+ else { >+ c = wc->hw_regs->t1_status >> (8*span); >+ if (!(c & AP4_T1_FRAME_SYNC)) >+ alarms = 0x04; >+ } >+ } >+ if(debug) printk("AP4_GET_ALARMS: span = %d, alarms = 0x%02x\n", span+1, alarms); >+ if (copy_to_user((int *)data, &alarms, sizeof(int))) >+ return -EFAULT; >+ break; >+ >+ case AP4_GET_SLIPS: >+ if (copy_from_user(&span, (int *)data, sizeof(int))) >+ return -EFAULT; >+ // span starts in zero >+ span--; >+ if((span < wc->numspans) && (span >=0)) >+ alarms = wc->tspans[span]->slipcount; >+ if(debug) printk("AP4_GET_SLIPS: span = %d, slips = 0x%02x\n", span+1, alarms); >+ if (copy_to_user((int *)data, &alarms, sizeof(int))) >+ return -EFAULT; >+ break; >+ >+ default: >+ PDEBUG("%s: Unknown IOCTL CODE!", wc->variety); >+ return -ENOTTY; >+ } >+ return 0; >+} >+ >+static inline struct ap4_span* ap4_span_from_span(struct dahdi_span *span) { >+ return container_of(span, struct ap4_span, span); >+} >+ >+static int ap4_maint(struct dahdi_span *span, int cmd) >+{ >+ struct ap4_span *ts = ap4_span_from_span(span); >+ struct ap4 *wc = ts->owner; >+ >+ >+ if (ts->spantype == TYPE_E1) { >+ switch(cmd) { >+ case DAHDI_MAINT_NONE: >+ printk("XXX Turn off local and remote loops E1 XXX\n"); >+ *(wc->membase+AP_E1_CONFIG_REG) &= ~(AP_E1_LOOP_CONFIG<<((span->spanno-1)*8)); >+ break; >+ case DAHDI_MAINT_LOCALLOOP: >+ printk("XXX Turn on local loopback E1 XXX\n"); >+ break; >+ case DAHDI_MAINT_REMOTELOOP: >+ printk("XXX Turn on remote loopback E1 XXX\n"); >+ break; >+ case DAHDI_MAINT_LOOPUP: >+ printk("XXX Turn on local loopback on E1 #%d instead of send loopup code XXX\n", span->spanno); >+ *(wc->membase+AP_E1_CONFIG_REG) |= (AP_E1_LOOP_CONFIG<<((span->spanno-1)*8)); >+ break; >+ case DAHDI_MAINT_LOOPDOWN: >+ printk("XXX Turn on local loopback on E1 #%d instead of send loopdown code XXX\n", span->spanno); >+ *(wc->membase+AP_E1_CONFIG_REG) |= (AP_E1_LOOP_CONFIG<<((span->spanno-1)*8)); >+ break; >+ default: >+ printk("%s: Unknown E1 maint command: %d\n", wc->variety, cmd); >+ break; >+ } >+ } else { >+ switch(cmd) { >+ case DAHDI_MAINT_NONE: >+ printk("XXX Turn off local and remote loops T1 XXX\n"); >+ break; >+ case DAHDI_MAINT_LOCALLOOP: >+ printk("XXX Turn on local loop and no remote loop XXX\n"); >+ break; >+ case DAHDI_MAINT_REMOTELOOP: >+ printk("XXX Turn on remote loopup XXX\n"); >+ break; >+ case DAHDI_MAINT_LOOPUP: >+ break; >+ case DAHDI_MAINT_LOOPDOWN: >+ break; >+ default: >+ printk("%s: Unknown T1 maint command: %d\n", wc->variety, cmd); >+ break; >+ } >+ } >+ return 0; >+} >+ >+static int ap4_rbsbits(struct dahdi_chan *chan, int bits) >+{ >+ u_char m,c; >+ int k,n,b; >+ struct ap4 *wc = chan->pvt; >+ struct ap4_span *ts = wc->tspans[chan->span->offset]; >+ unsigned long flags; >+ volatile unsigned int *writecas = (wc->membase+AP_CAS_BASE); >+ unsigned int allspansbits; >+ >+ //ap_debugk("chan->channo = %d, int bits = 0x%08x\n", chan->channo, bits); >+ if(debug & DEBUG_RBS) printk("Setting bits to %d on channel %s\n", bits, chan->name); >+ spin_lock_irqsave(&wc->reglock, flags); >+ k = chan->span->offset; >+ if (ts->spantype == TYPE_E1) { /* do it E1 way */ >+ if (chan->chanpos == 16) { >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ return 0; >+ } >+ n = chan->chanpos - 1; >+ if (chan->chanpos > 15) n--; >+ b = (n % 15); >+ c = ts->txsigs[b]; >+ m = (n / 15) << 2; /* nibble selector */ >+ c &= (0xf << m); /* keep the other nibble */ >+ c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ >+ ts->txsigs[b] = c; >+ /* monta a word de 32 bits com informacao de todos os spans */ >+ allspansbits = wc->tspans[0]->txsigs[b]; >+ if (wc->numspans > 1) { >+ allspansbits |= (wc->tspans[1]->txsigs[b] << 8); >+ } >+ if (wc->numspans == 4) { >+ allspansbits |= (wc->tspans[2]->txsigs[b] << 16) | >+ (wc->tspans[3]->txsigs[b] << 24); >+ } >+ /* output them to the chip */ >+ writecas[b] = allspansbits; >+ ap_debugk("escrito 0x%08x para ser transmitido pelo CAS (b = %d)\n", allspansbits, b); >+#if 0 >+ } else if (ts->span.lineconfig & DAHDI_CONFIG_D4) { >+ n = chan->chanpos - 1; >+ b = (n/4); >+ c = ts->txsigs[b]; >+ m = ((3 - (n % 4)) << 1); /* nibble selector */ >+ c &= ~(0x3 << m); /* keep the other nibble */ >+ c |= ((bits >> 2) & 0x3) << m; /* put our new nibble here */ >+ ts->txsigs[b] = c; >+ /* output them to the chip */ >+ //__ap4_out( ... ); >+ } else if (ts->span.lineconfig & DAHDI_CONFIG_ESF) { >+#endif >+ } else { >+ n = chan->chanpos - 1; >+ b = (n/2); >+ c = ts->txsigs[b]; >+ m = ((n % 2) << 2); /* nibble selector */ >+ c &= (0xf << m); /* keep the other nibble */ >+ c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ >+ ts->txsigs[b] = c; >+ /* output them to the chip */ >+ /* monta a word de 32 bits com informacao de todos os spans */ >+ allspansbits = wc->tspans[0]->txsigs[b]; >+ if (wc->numspans > 1) { >+ allspansbits |= (wc->tspans[1]->txsigs[b] << 8); >+ } >+ if (wc->numspans == 4) { >+ allspansbits |= (wc->tspans[2]->txsigs[b] << 16) | >+ (wc->tspans[3]->txsigs[b] << 24); >+ } >+ /* output them to the chip */ >+ writecas[b] = allspansbits; >+ ap_debugk("escrito 0x%08x para ser transmitido pelo CAS (b = %d)\n", allspansbits, b); >+ } >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ if (debug & DEBUG_RBS) >+ printk("Finished setting RBS bits\n"); >+ return 0; >+} >+ >+static int ap4_shutdown(struct dahdi_span *span) >+{ >+ int tspan; >+ int wasrunning; >+ unsigned long flags; >+ struct ap4_span *ts = ap4_span_from_span(span); >+ struct ap4 *wc = ts->owner; >+ >+ tspan = span->offset + 1; >+ if (tspan < 0) { >+ printk("%s: '%d' isn't us?\n", wc->variety, span->spanno); >+ return -1; >+ } >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ wasrunning = span->flags & DAHDI_FLAG_RUNNING; >+ >+ span->flags &= ~DAHDI_FLAG_RUNNING; >+ if (wasrunning) >+ wc->spansstarted--; >+ __ap4_set_led(wc, span->offset, AP_OFF); >+ if (((wc->numspans == 4) && >+ (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING)) && >+ (!(wc->tspans[1]->span.flags & DAHDI_FLAG_RUNNING)) && >+ (!(wc->tspans[2]->span.flags & DAHDI_FLAG_RUNNING)) && >+ (!(wc->tspans[3]->span.flags & DAHDI_FLAG_RUNNING))) >+ || >+ ((wc->numspans == 2) && >+ (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING)) && >+ (!(wc->tspans[1]->span.flags & DAHDI_FLAG_RUNNING))) >+ || >+ ((wc->numspans == 1) && >+ (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING)))) { >+ /* No longer in use, disable interrupts */ >+ printk("%s: Disabling interrupts since there are no active spans\n", >+ wc->variety); >+ } else wc->checktiming = 1; >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ if (debug & DEBUG_MAIN) >+ printk("Span %d (%s) shutdown\n", span->spanno, span->name); >+ return 0; >+} >+ >+static int ap4_spanconfig(struct file *file, struct dahdi_span *span, >+ struct dahdi_lineconfig *lc) >+{ >+ int i; >+ struct ap4_span *ts = ap4_span_from_span(span); >+ struct ap4 *wc = ts->owner; >+ unsigned int val; >+ >+ printk("About to enter spanconfig!\n"); >+ if (debug & DEBUG_MAIN) >+ printk("%s: Configuring span %d\n", wc->variety, span->spanno); >+ /* XXX We assume lineconfig is okay and shouldn't XXX */ >+ span->lineconfig = lc->lineconfig; >+ span->txlevel = lc->lbo; >+ span->rxlevel = 0; >+ if (lc->sync < 0) >+ lc->sync = 0; >+ if (lc->sync > 4) >+ lc->sync = 0; >+ >+ /* remove this span number from the current sync sources, if there */ >+ for(i = 0; i < wc->numspans; i++) { >+ if (wc->tspans[i]->sync == span->spanno) { >+ wc->tspans[i]->sync = 0; >+ wc->tspans[i]->psync = 0; >+ } >+ } >+ wc->tspans[span->offset]->syncpos = lc->sync; >+ /* if a sync src, put it in proper place */ >+ if (lc->sync) { >+ wc->tspans[lc->sync - 1]->sync = span->spanno; >+ wc->tspans[lc->sync - 1]->psync = span->offset + 1; >+ } >+ wc->checktiming = 1; >+ /* If we're already running, then go ahead and apply the changes */ >+ if (span->flags & DAHDI_FLAG_RUNNING) >+ return ap4_startup(file, span); >+ >+ // Limpa contadores de slips, crc e bpv >+ val = (*(wc->membase + AP_CNT_SLIP_REG)); >+ val = (*(wc->membase + AP_CNT_CRC_REG)); >+ val = (*(wc->membase + AP_CNT_CV_REG)); >+ >+ ap_debugk("habilitando interrupcao!\n"); >+ // Nao considera as primeiras interrupcoes na soma das IRQs perdidas >+ wc->flag_1st_irq = 16; >+ // Enable interrupt >+ *(wc->membase + AP_INT_CONTROL_REG) |= AP_INT_CTL_ENABLE; >+ // Limpa interrupcao da FPGA para forcar borda de subida na proxima >+ val = *(wc->membase + AP_CLEAR_IRQ_REG); >+ >+ printk("Done with spanconfig!\n"); >+ return 0; >+} >+ >+static int ap4_chanconfig(struct file *file, struct dahdi_chan *chan, >+ int sigtype) >+{ >+ int alreadyrunning; >+ unsigned long flags; >+ struct ap4 *wc = chan->pvt; >+ >+ alreadyrunning = wc->tspans[chan->span->offset]->span.flags & DAHDI_FLAG_RUNNING; >+ if (debug & DEBUG_MAIN) { >+ if (alreadyrunning) >+ printk("%s: Reconfigured channel %d (%s) sigtype %d\n", >+ wc->variety, chan->channo, chan->name, sigtype); >+ else >+ printk("%s: Configured channel %d (%s) sigtype %d\n", >+ wc->variety, chan->channo, chan->name, sigtype); >+ } >+ spin_lock_irqsave(&wc->reglock, flags); >+ if (alreadyrunning) >+ __set_clear(wc, chan->span->offset); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ return 0; >+} >+ >+static int ap4_open(struct dahdi_chan *chan) >+{ >+ try_module_get(THIS_MODULE); >+ return 0; >+} >+ >+static int ap4_close(struct dahdi_chan *chan) >+{ >+ module_put(THIS_MODULE); >+ return 0; >+} >+ >+static const struct dahdi_span_ops ap4_span_ops = { >+ .owner = THIS_MODULE, >+ .spanconfig = ap4_spanconfig, >+ .chanconfig = ap4_chanconfig, >+ .startup = ap4_startup, >+ .shutdown = ap4_shutdown, >+ .rbsbits = ap4_rbsbits, >+ .maint = ap4_maint, >+ .open = ap4_open, >+ .close = ap4_close, >+#ifdef APEC_SUPPORT >+ .echocan = ap4_echocan, >+#endif >+ .ioctl = ap4_ioctl >+}; >+ >+static void init_spans(struct ap4 *wc) >+{ >+ int x,y; >+ struct ap4_span *ts; >+ >+ for (x=0;x<wc->numspans;x++) { >+ ts = wc->tspans[x]; >+ sprintf(ts->span.name, "AP4%d%d/%d/%d", 0, wc->numspans, wc->num, x + 1); >+ snprintf(ts->span.desc, sizeof(ts->span.desc) - 1, "AP4%d%d Card %d Span %d", 0, wc->numspans, wc->num+1, x+1); >+ snprintf(ts->span.location, sizeof(ts->span.location) - 1, >+ "PCI Bus %02d Slot %02d", wc->dev->bus->number, PCI_SLOT(wc->dev->devfn) + 1); >+ ts->span.manufacturer = "Aligera"; >+ dahdi_copy_string(ts->span.devicetype, wc->variety, sizeof(ts->span.devicetype)); >+ ts->span.ops = &ap4_span_ops; >+ if (ts->spantype == TYPE_E1) { >+ ts->span.channels = 31; >+ ts->span.spantype = "E1"; >+ ts->span.linecompat = DAHDI_CONFIG_HDB3 | DAHDI_CONFIG_CCS | DAHDI_CONFIG_CRC4; >+ ts->span.deflaw = DAHDI_LAW_ALAW; >+ } else { >+ ts->span.channels = 24; >+ ts->span.spantype = "T1"; >+ ts->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_B8ZS | DAHDI_CONFIG_D4 | DAHDI_CONFIG_ESF; >+ ts->span.deflaw = DAHDI_LAW_MULAW; >+ } >+ ts->span.chans = ts->chans; >+ ts->span.flags = DAHDI_FLAG_RBS; >+ ts->owner = wc; >+ ts->span.offset = x; >+ ts->writechunk = (void *)(wc->writechunk + x * 32 * 2); >+ ts->readchunk = (void *)(wc->readchunk + x * 32 * 2); >+ for (y=0;y<wc->tspans[x]->span.channels;y++) { >+ struct dahdi_chan *mychans = ts->chans[y]; >+ sprintf(mychans->name, "AP4%d%d/%d/%d/%d", 0, wc->numspans, wc->num, x + 1, y + 1); >+ mychans->sigcap = DAHDI_SIG_EM | DAHDI_SIG_CLEAR | DAHDI_SIG_FXSLS | DAHDI_SIG_FXSGS | DAHDI_SIG_FXSKS | >+ DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_FXOKS | DAHDI_SIG_CAS | DAHDI_SIG_EM_E1 | DAHDI_SIG_DACS_RBS; >+ mychans->pvt = wc; >+ mychans->chanpos = y + 1; >+ } >+ } >+ printk("%s: Spans initialized\n", wc->variety); >+} >+ >+ >+ >+static void __ap4_set_timing_source(struct ap4 *wc, int unit) >+{ >+ unsigned int timing; >+ int x; >+ >+ if (unit != wc->syncsrc) { >+ if ((unit > -1) && (unit < 4)) { >+ /* define fonte de clock para interface escolhida */ >+ timing = *(wc->membase+AP_CLKSRC_REG); >+ timing &= ~AP_CLKSRC_MASK; >+ timing |= unit+1; >+ *(wc->membase+AP_CLKSRC_REG) = timing; >+ } else { >+ /* define clock para interno */ >+ timing = *(wc->membase+AP_CLKSRC_REG); >+ timing &= ~AP_CLKSRC_MASK; >+ *(wc->membase+AP_CLKSRC_REG) = timing; >+ } >+ wc->syncsrc = unit; >+ if ((unit < 0) || (unit > 3)) >+ unit = 0; >+ else >+ unit++; >+ for (x=0;x<wc->numspans;x++) >+ wc->tspans[x]->span.syncsrc = unit; >+ } else { >+ if (debug & DEBUG_MAIN) >+ printk("%s: Timing source already set to %d\n", >+ wc->variety, unit); >+ } >+ printk("%s: Timing source set to %d (clksrc_reg = 0x%08x)\n", >+ wc->variety, unit, *(wc->membase+AP_CLKSRC_REG)); >+} >+ >+static void __ap4_set_timing_source_auto(struct ap4 *wc) >+{ >+ int x; >+ >+ wc->checktiming = 0; >+ for (x=0;x<wc->numspans;x++) { >+ if (wc->tspans[x]->sync) { >+ if ((wc->tspans[wc->tspans[x]->psync - 1]->span.flags & DAHDI_FLAG_RUNNING) && >+ !(wc->tspans[wc->tspans[x]->psync - 1]->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE) )) { >+ /* Valid timing source */ >+ __ap4_set_timing_source(wc, wc->tspans[x]->psync - 1); >+ return; >+ } >+ } >+ } >+ __ap4_set_timing_source(wc, 4); >+} >+ >+static void __ap4_configure_t1(struct ap4 *wc, int unit, int lineconfig, int txlevel) >+{ >+ char *framing, *line; >+ unsigned int config = 0; >+ unsigned int param = 0; >+ unsigned int linecode = 0; >+ >+ wc->tspans[unit]->spantype = TYPE_T1; >+ wc->tspans[unit]->span.channels = 24; >+ wc->tspans[unit]->span.deflaw = DAHDI_LAW_MULAW; >+ >+ /* Configure line code */ >+ if (unit < 2) >+ linecode = AP_LIU1_LINECODE; >+ else >+ linecode = AP_LIU2_LINECODE; >+ if (lineconfig & DAHDI_CONFIG_AMI) { >+ *(wc->membase+AP_LEDS_REG) |= linecode; >+ line = "AMI"; >+ } else { >+ *(wc->membase+AP_LEDS_REG) &= ~linecode; >+ line = "B8ZS"; >+ } >+ >+ /* loopback test*/ >+ //wc->hw_regs->e1_config |= (AP_E1_LOOP_CONFIG << (8 * unit)); >+ //printk("E1 config = 0x%08x\n", wc->hw_regs->e1_config); >+ >+ /* Configure T1 */ >+ config = wc->hw_regs->liu_config; >+ config &= ~(0x000000ff << (8 * unit)); >+ config |= (AP_PULS_DSX1_0FT << (8 * unit)); >+ wc->hw_regs->liu_config = config; >+ >+ param = AP4_T1_NE1_SEL | AP4_T1_CAS_ENABLE; >+ if (lineconfig & DAHDI_CONFIG_D4) { >+ framing = "D4"; >+ } else { >+ framing = "ESF"; >+ param |= AP4_T1_ESF_NSF; >+ } >+ config = wc->hw_regs->t1_config; >+ config &= ~(0x000000ff << (8 * unit)); >+ config |= (param << (8 * unit)); >+ wc->hw_regs->t1_config = config; >+ >+ printk("T1 Status: 0x%08x\tT1 Config: 0x%08x\tPARAM: 0x%08x\n", >+ wc->hw_regs->t1_status, wc->hw_regs->t1_config, param); >+ >+ if (!polling) { >+ __ap4_check_alarms(wc, unit); >+ __ap4_check_sigbits(wc, unit); >+ } >+ printk("%s: Span %d configured for %s/%s\n", wc->variety, unit + 1, framing, line); >+} >+ >+static void __ap4_configure_e1(struct ap4 *wc, int unit, int lineconfig) >+{ >+ char *crc4 = ""; >+ char *framing, *line; >+ unsigned int e1s_cfg, config = 0; >+ unsigned int linecode = 0; >+ >+ wc->tspans[unit]->spantype = TYPE_E1; >+ wc->tspans[unit]->span.channels = 31; >+ wc->tspans[unit]->span.deflaw = DAHDI_LAW_ALAW; >+ >+ if (loopback) { >+ } >+ >+ if (lineconfig & DAHDI_CONFIG_CRC4) { >+ crc4 = "/CRC4"; >+ config |= AP_E1_CRCEN_CONFIG; >+ } >+ >+ if(unit < 2) >+ linecode = AP_LIU1_LINECODE; >+ else >+ linecode = AP_LIU2_LINECODE; >+ /* Configure line interface */ >+ if (lineconfig & DAHDI_CONFIG_AMI) { >+ *(wc->membase+AP_LEDS_REG) |= linecode; >+ line = "AMI"; >+ } else { >+ *(wc->membase+AP_LEDS_REG) &= ~linecode; >+ line = "HDB3"; >+ } >+ >+ if (lineconfig & DAHDI_CONFIG_CCS) { >+ framing = "CCS"; >+ } else { >+ framing = "CAS"; >+ config |= (AP_E1_CASEN_CONFIG | AP_E1_PCM30_CONFIG); >+ } >+ >+ e1s_cfg = *(wc->membase+AP_E1_CONFIG_REG); >+ e1s_cfg &= ~(0x000000ff<<(8*unit)); >+ e1s_cfg |= (config<<(8*unit)); >+ *(wc->membase+AP_E1_CONFIG_REG) = e1s_cfg; >+ >+ /* Disable T1 framer */ >+ config = wc->hw_regs->t1_config; >+ config &= ~(0x000000ff << (8 * unit)); >+ wc->hw_regs->t1_config = config; >+ >+ /* Configure LIU Signalling */ >+ e1s_cfg = *(wc->membase+AP_T1E1_CONFIG_REG); >+ e1s_cfg &= ~(0x000000ff<<(8*unit)); >+ e1s_cfg |= (AP_PULS_E1_120<<(8*unit)); >+ *(wc->membase+AP_T1E1_CONFIG_REG) = e1s_cfg; >+ >+ if (!polling) { >+ __ap4_check_alarms(wc, unit); >+ __ap4_check_sigbits(wc, unit); >+ } >+ printk("%s: Span %d configured for %s/%s%s\n", >+ wc->variety, unit + 1, framing, line, crc4); >+} >+ >+static int ap4_startup(struct file *file, struct dahdi_span *span) >+{ >+ int i; >+ int tspan; >+ unsigned long flags; >+ int alreadyrunning; >+ struct ap4_span *ts = ap4_span_from_span(span); >+ struct ap4 *wc = ts->owner; >+ >+ printk("About to enter startup!\n"); >+ tspan = span->offset + 1; >+ if (tspan < 0) { >+ printk("%s: Span '%d' isn't us?\n", wc->variety, span->spanno); >+ return -1; >+ } >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ >+ alreadyrunning = span->flags & DAHDI_FLAG_RUNNING; >+ >+ /* initialize the start value for the entire chunk of last ec buffer */ >+ for(i = 0; i < span->channels; i++) >+ { >+ memset(ts->ec_chunk1[i], >+ DAHDI_LIN2X(0, span->chans[i]),DAHDI_CHUNKSIZE); >+ memset(ts->ec_chunk2[i], >+ DAHDI_LIN2X(0, span->chans[i]),DAHDI_CHUNKSIZE); >+ } >+ >+ /* Force re-evaluation fo timing source */ >+// if (timingcable) >+ wc->syncsrc = -1; >+ >+ if ((span->lineconfig & DAHDI_CONFIG_D4) || (span->lineconfig & DAHDI_CONFIG_ESF)) { >+ /* is a T1 card */ >+ __ap4_configure_t1(wc, span->offset, span->lineconfig, span->txlevel); >+ } else { /* is a E1 card */ >+ __ap4_configure_e1(wc, span->offset, span->lineconfig); >+ } >+ >+ /* Note clear channel status */ >+ wc->tspans[span->offset]->notclear = 0; >+ __set_clear(wc, span->offset); >+ >+ if (!alreadyrunning) { >+ span->flags |= DAHDI_FLAG_RUNNING; >+ wc->spansstarted++; >+ /* enable interrupts */ >+ >+ if (!polling) { >+ __ap4_check_alarms(wc, span->offset); >+ __ap4_check_sigbits(wc, span->offset); >+ } >+ } >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ >+ if (wc->tspans[0]->sync == span->spanno) printk("SPAN %d: Primary Sync Source\n",span->spanno); >+ if (wc->numspans > 1) { >+ if (wc->tspans[1]->sync == span->spanno) printk("SPAN %d: Secondary Sync Source\n",span->spanno); >+ } >+ if (wc->numspans == 4) { >+ if (wc->tspans[2]->sync == span->spanno) printk("SPAN %d: Tertiary Sync Source\n",span->spanno); >+ if (wc->tspans[3]->sync == span->spanno) printk("SPAN %d: Quaternary Sync Source\n",span->spanno); >+ } >+ >+#ifdef APEC_SUPPORT >+ if (!apec_enable || !wc->apec_enable) >+ wc->hw_regs->echo_ctrl = 0xe0000000; >+ else if (!alreadyrunning && !wc->apec) >+ if (ap4_apec_init(wc)) >+ ap4_apec_release(wc); >+#else >+ wc->hw_regs->echo_ctrl = 0xe0000000; >+#endif >+ >+ printk("Completed startup!\n"); >+ return 0; >+} >+ >+ >+static void ap4_receiveprep(struct ap4 *wc) >+{ >+ volatile unsigned int *readchunk; >+ unsigned int buffer[32]; >+ unsigned char *byte = (unsigned char *) buffer; >+ int i, j, k; >+ >+ readchunk = (wc->membase + (AP_DATA_BASE)); >+ for (i = 0; i < DAHDI_CHUNKSIZE; i++) { >+ /* Prefetch Card data */ >+ for (j = 0; j < 32; ++j) { >+ buffer[j] = readchunk[j]; >+ } >+ for (j = 0; j < wc->numspans; j++) { >+ /* Set first timeslot for first channel */ >+ if (wc->tspans[j]->spantype == TYPE_E1) { >+ for (k = 0; k < 31; ++k) { >+ /* Skip first timeslot from E1 */ >+ wc->tspans[j]->span.chans[k]->readchunk[i] = >+ byte[4*(k+1)+j]; >+ } >+ } >+ else { >+ for (k = 0; k < 24; ++k) { >+ wc->tspans[j]->span.chans[k]->readchunk[i] = >+ byte[4*k+j]; >+ } >+ } >+ } >+ readchunk += 32; >+ } >+ >+ for (i = 0; i < wc->numspans; i++) { >+ if (wc->tspans[i]->span.flags & DAHDI_FLAG_RUNNING) { >+ for (j = 0; j < wc->tspans[i]->span.channels; j++) { >+ /* Echo cancel double buffered data */ >+ dahdi_ec_chunk(wc->tspans[i]->span.chans[j], >+ wc->tspans[i]->span.chans[j]->readchunk, >+ wc->tspans[i]->ec_chunk2[j]); >+ memcpy(wc->tspans[i]->ec_chunk2[j],wc->tspans[i]->ec_chunk1[j], >+ DAHDI_CHUNKSIZE); >+ memcpy(wc->tspans[i]->ec_chunk1[j], >+ wc->tspans[i]->span.chans[j]->writechunk, >+ DAHDI_CHUNKSIZE); >+ } >+ dahdi_receive(&wc->tspans[i]->span); >+ } >+ } >+} >+ >+#if (DAHDI_CHUNKSIZE != 8) >+#error Sorry, AP400 driver does not support chunksize != 8 >+#endif >+ >+#ifdef ENABLE_WORKQUEUES >+static void workq_handlespan(void *data) >+{ >+ struct ap4_span *ts = data; >+ struct ap4 *wc = ts->owner; >+ >+// __receive_span(ts); >+// __transmit_span(ts); >+ atomic_dec(&wc->worklist); >+ atomic_read(&wc->worklist); >+ >+} >+#endif >+ >+static void ap4_transmitprep(struct ap4 *wc) >+{ >+ volatile unsigned int *writechunk; >+ int x,y,z; >+ unsigned int tmp; >+ >+ for (y=0;y<wc->numspans;y++) { >+ if (wc->tspans[y]->span.flags & DAHDI_FLAG_RUNNING) >+ dahdi_transmit(&wc->tspans[y]->span); >+ } >+ >+ writechunk = (wc->membase+(AP_DATA_BASE)); >+ for (x=0;x<DAHDI_CHUNKSIZE;x++) { >+ // Once per chunk >+ for (z=0;z<32;z++) { >+ // All channels >+ tmp = 0; >+ for (y = 0; y < wc->numspans; ++y) { >+ if (wc->tspans[y]->spantype == TYPE_T1 && z < 24) >+ tmp |= (wc->tspans[y]->span.chans[z]->writechunk[x] >+ << (8*y)); >+ else /* Span Type is E1 */ >+ if (z > 0) /* Skip first timeslot */ >+ tmp |= (wc->tspans[y]->span.chans[z-1]->writechunk[x] >+ << (8*y)); >+ } >+ writechunk[z] = tmp; >+ } >+ // Advance pointer by 4 TDM frame lengths >+ writechunk += 32; >+ } >+ >+} >+ >+static void ap4_tdm_loop(struct ap4 *wc) >+{ >+ volatile unsigned int *buf_ptr; >+ int x,z; >+ unsigned int tmp; >+ >+ buf_ptr = (wc->membase+AP_DATA_BASE); >+ >+ for (x=0;x<DAHDI_CHUNKSIZE;x++) { >+ // Once per chunk >+ for (z=0;z<32;z++) { >+ tmp = buf_ptr[z]; >+ buf_ptr[z] = tmp; >+ } >+ buf_ptr += 32; >+ } >+} >+ >+static void __ap4_check_sigbits(struct ap4 *wc, int span) >+{ >+ int a,i,rxs; >+ struct ap4_span *ts = wc->tspans[span]; >+ volatile unsigned int *readcas = (wc->membase+AP_CAS_BASE); >+ >+// if (debug & DEBUG_RBS) >+// printk("Checking sigbits on span %d\n", span + 1); >+ >+ if (!(ts->span.flags & DAHDI_FLAG_RUNNING)) >+ return; >+ // se span estiver com alarme RED ou BLUE... >+ if( (ts->span.alarms & DAHDI_ALARM_RED) || (ts->span.alarms & DAHDI_ALARM_BLUE) ) { >+ ts->reload_cas = 4; >+ } else if(ts->reload_cas > 0) { >+ // da mais um tempo para framer recuperar e enviar bits de CAS validos >+ ts->reload_cas--; >+ } >+ >+ if (ts->spantype == TYPE_E1) { >+ for (i = 0; i < 15; i++) { >+ >+ // Se estamos em alarme ou recuperando de um entao mascara os bits para "1101" (bloqueado) >+ if(ts->reload_cas) { >+ a = 0xdd; >+ } else { >+ a = (int) ts->casbuf[i]; >+ } >+ ts->casbuf[i] = (unsigned char) (readcas[i] >> (8*span))&0xff; >+ >+ /* Get high channel in low bits */ >+ rxs = (a & 0xf); >+ if (!(ts->span.chans[i+16]->sig & DAHDI_SIG_CLEAR)) { >+ if (ts->span.chans[i+16]->rxsig != rxs) { >+ ap_debugk("CAS no canal %d mudou de 0x%02x para 0x%02x\n", i+16, ts->span.chans[i+16]->rxsig, rxs); >+ dahdi_rbsbits(ts->span.chans[i+16], rxs); >+ } >+ } >+ rxs = (a >> 4) & 0xf; >+ if (!(ts->span.chans[i]->sig & DAHDI_SIG_CLEAR)) { >+ if (ts->span.chans[i]->rxsig != rxs) { >+ ap_debugk("CAS no canal %d mudou de 0x%02x para 0x%02x\n", i, ts->span.chans[i]->rxsig, rxs); >+ dahdi_rbsbits(ts->span.chans[i], rxs); >+ } >+ } >+ } >+ } else if (ts->span.lineconfig & DAHDI_CONFIG_D4) { >+ for (i = 0; i < 12; i++) { >+ a = (unsigned char) (readcas[i] >> (8*span)) & 0xcc; >+ rxs = a & 0xc; >+ //rxs = (a & 0xc) >> 2; >+ if (!(ts->span.chans[2*i]->sig & DAHDI_SIG_CLEAR)) { >+ if (ts->span.chans[2*i]->rxsig != rxs) >+ dahdi_rbsbits(ts->span.chans[2*i], rxs); >+ } >+ rxs = (a >> 4) & 0xc; >+ //rxs = ((a >> 4) & 0xc) >> 2; >+ if (!(ts->span.chans[2*i+1]->sig & DAHDI_SIG_CLEAR)) { >+ if (ts->span.chans[2*i+1]->rxsig != rxs) >+ dahdi_rbsbits(ts->span.chans[2*i+1], rxs); >+ } >+ } >+ } else { // ESF >+ for (i = 0; i < 12; i++) { >+ a = (unsigned char) (readcas[i] >> (8*span)) & 0xff; >+ rxs = (a & 0xf); >+ if (!(ts->span.chans[2*i+1]->sig & DAHDI_SIG_CLEAR)) { >+ /* XXX Not really reset on every trans! XXX */ >+ if (ts->span.chans[2*i+1]->rxsig != rxs) { >+ dahdi_rbsbits(ts->span.chans[2*i+1], rxs); >+ } >+ } >+ rxs = (a >> 4) & 0xf; >+ if (!(ts->span.chans[2*i]->sig & DAHDI_SIG_CLEAR)) { >+ /* XXX Not really reset on every trans! XXX */ >+ if (ts->span.chans[2*i]->rxsig != rxs) { >+ dahdi_rbsbits(ts->span.chans[2*i], rxs); >+ } >+ } >+ } >+ } >+} >+ >+static void __ap4_check_alarms(struct ap4 *wc, int span) >+{ >+ unsigned char c; >+ int alarms; >+ int x,j; >+ struct ap4_span *ts = wc->tspans[span]; >+ unsigned int e1_cfg; >+ >+ if (!(ts->span.flags & DAHDI_FLAG_RUNNING)) >+ return; >+ >+ /* Assume no alarms */ >+ alarms = DAHDI_ALARM_NONE; >+ >+ /* And consider only carrier alarms */ >+ ts->span.alarms &= (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE | DAHDI_ALARM_NOTOPEN); >+ >+ if (ts->span.lineconfig & DAHDI_CONFIG_NOTOPEN) { >+ for (x=0,j=0;x < ts->span.channels;x++) >+ if ((ts->span.chans[x]->flags & DAHDI_FLAG_OPEN) >+#ifdef CONFIG_DAHDI_NET >+ || >+ (ts->span.chans[x]->flags & DAHDI_FLAG_NETDEV) >+#endif >+ ) >+ j++; >+ if (!j) >+ alarms |= DAHDI_ALARM_NOTOPEN; >+ } >+ >+/* le status e configuracao do E1 */ >+ if (wc->tspans[span]->spantype == TYPE_E1) { >+ c = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*span)); >+ e1_cfg = ((*(wc->membase+AP_E1_CONFIG_REG))>>(8*span)); >+ >+ if ((c & AP_E1_LOS_STATUS)||(c & AP_E1_BFAE_STATUS)||(c & AP_E1_AIS_STATUS)) { >+ if (ts->alarmcount >= alarmdebounce) >+ alarms |= DAHDI_ALARM_RED; >+ else >+ ts->alarmcount++; >+ } else >+ ts->alarmcount = 0; >+ >+ if ( c & AP_E1_MFAE_STATUS ) >+ alarms |= DAHDI_ALARM_BLUE; >+ >+ if ( (!(c & AP_E1_CAS_STATUS)) && (e1_cfg & AP_E1_PCM30_CONFIG)) >+ alarms |= DAHDI_ALARM_BLUE; >+ } else { >+ c = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*span)); >+ if (c & AP_E1_LOS_STATUS) { >+ if (ts->alarmcount >= alarmdebounce) >+ alarms |= DAHDI_ALARM_RED; >+ else >+ ts->alarmcount++; >+ } else >+ ts->alarmcount = 0; >+ c = wc->hw_regs->t1_status >> (8 * span); >+ if (!(c & AP4_T1_FRAME_SYNC)) >+ alarms |= DAHDI_ALARM_RED; >+ } >+ >+ if (((!ts->span.alarms) && alarms) || >+ (ts->span.alarms && (!alarms))) >+ wc->checktiming = 1; >+ >+ /* Keep track of recovering */ >+ if ((!alarms) && ts->span.alarms) >+ ts->alarmtimer = DAHDI_ALARMSETTLE_TIME; >+ if (ts->alarmtimer) >+ alarms |= DAHDI_ALARM_RECOVER; >+ >+ >+ // If receiving alarms, go into Yellow alarm state >+ if (alarms && !(ts->spanflags & FLAG_SENDINGYELLOW)) { >+ printk("Setting yellow alarm on span %d\n", span + 1); >+ e1_cfg = *(wc->membase+AP_E1_CONFIG_REG); >+ e1_cfg |= (AP_E1_RAI_CONFIG<<(8*span)); >+ *(wc->membase+AP_E1_CONFIG_REG) = e1_cfg; >+ ts->spanflags |= FLAG_SENDINGYELLOW; >+ } else if ((!alarms) && (ts->spanflags & FLAG_SENDINGYELLOW)) { >+ printk("Clearing yellow alarm on span %d\n", span + 1); >+ e1_cfg = *(wc->membase+AP_E1_CONFIG_REG); >+ e1_cfg &= ~(AP_E1_RAI_CONFIG<<(8*span)); >+ *(wc->membase+AP_E1_CONFIG_REG) = e1_cfg; >+ ts->spanflags &= ~FLAG_SENDINGYELLOW; >+ } >+ >+ // Re-check the timing source when we enter/leave alarm, not withstanding yellow alarm >+ if (c & AP_E1_RAI_STATUS) >+ alarms |= DAHDI_ALARM_YELLOW; >+ >+ if (ts->span.mainttimer || ts->span.maintstat) >+ alarms |= DAHDI_ALARM_LOOPBACK; >+ >+ ts->span.alarms = alarms; >+ dahdi_alarm_notify(&ts->span); >+} >+ >+static void __ap4_do_counters(struct ap4 *wc) >+{ >+ int span; >+ >+ for (span=0;span<wc->numspans;span++) { >+ struct ap4_span *ts = wc->tspans[span]; >+ int docheck=0; >+ if (ts->loopupcnt || ts->loopdowncnt) >+ docheck++; >+ if (ts->alarmtimer) { >+ if (!--ts->alarmtimer) { >+ docheck++; >+ ts->span.alarms &= ~(DAHDI_ALARM_RECOVER); >+ } >+ } >+ if (docheck) { >+ if (!polling) >+ __ap4_check_alarms(wc, span); >+ dahdi_alarm_notify(&ts->span); >+ } >+ } >+} >+ >+static inline void __handle_leds(struct ap4 *wc) >+{ >+ int x, span_status; >+ #define MAX_BLINKTIMER 0x14 >+ >+ for (x=0;x<wc->numspans;x++) { >+ struct ap4_span *ts = wc->tspans[x]; >+ /* le status do E1 (para avaliar LOS) */ >+ span_status = ((*(wc->membase+AP_E1_STATUS_REG))>>(8*x)); >+ if (ts->span.flags & DAHDI_FLAG_RUNNING) { >+ if(span_status&AP_E1_LOS_STATUS) { >+ if (wc->blinktimer[x] >= (altab[wc->alarmpos[x]] /*>> 1*/)) { >+ __ap4_set_led(wc, x, AP_ON); >+ } >+ if (wc->blinktimer[x] >= (MAX_BLINKTIMER-1)) { >+ __ap4_set_led(wc, x, AP_OFF); >+ } >+ wc->blinktimer[x] += 1; >+ } else if (ts->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE)) { >+ if (wc->blinktimer[x] >= (altab[wc->alarmpos[x]] /*>> 1*/)) { >+ __ap4_set_led(wc, x, AP_ON); >+ } >+ if (wc->blinktimer[x] >= (MAX_BLINKTIMER-2)) { >+ __ap4_set_led(wc, x, AP_OFF); >+ } >+ wc->blinktimer[x] += 3; >+ } /*else if (ts->span.alarms & DAHDI_ALARM_YELLOW) { >+ // Yellow Alarm >+ __ap4_set_led(wc, x, AP_ON); >+ } else if (ts->span.mainttimer || ts->span.maintstat) { >+ >+ if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) { >+ __ap4_set_led(wc, x, AP_GREEN); >+ } >+ if (wc->blinktimer == 0xf) { >+ __ap4_set_led(wc, x, AP_OFF); >+ } >+ >+ } */else { >+ /* No Alarm */ >+ __ap4_set_led(wc, x, AP_ON); >+ } >+ } else >+ __ap4_set_led(wc, x, AP_OFF); >+ >+ if (wc->blinktimer[x] > MAX_BLINKTIMER) { >+ wc->blinktimer[x] = 0; >+ wc->alarmpos[x]++; >+ if (wc->alarmpos[x] >= (sizeof(altab) / sizeof(altab[0]))) >+ wc->alarmpos[x] = 0; >+ } >+ >+ } >+} >+ >+ >+DAHDI_IRQ_HANDLER(ap4_interrupt) >+{ >+ struct ap4 *wc = dev_id; >+ unsigned long flags; >+ int x; >+ static unsigned int val, cfg; >+ unsigned int cnt_irq_misses; >+ static unsigned int cnt_tmp; >+ int ret = 0; >+ >+ /* retorna se interrupcao nao foi habilitada ou nao esta ativa */ >+ cfg = *(wc->membase + AP_INT_CONTROL_REG); >+ if((cfg & AP_INT_CTL_ENABLE) == 0 || (cfg & AP_INT_CTL_ACTIVE) == 0) { >+ ret = 0; >+ goto out; >+ } >+ /* se chegamos aqui eh porque a interrupcao esta habilitada >+ * e esta ativa, ou seja, foi gerada pelo nosso cartao. >+ * Agora damos o ack da interrupcao */ >+ val = *(wc->membase + AP_CLEAR_IRQ_REG); >+ >+ /* conta interrupcoes perdidas */ >+ if (wc->flag_1st_irq > 0) { >+ // nao considera as primeiras passagens pela rotina >+ cnt_irq_misses = (*(wc->membase+AP_CNT_IRQ_REG)); >+ // so considera int. para o cartao >+ if(cnt_irq_misses) { >+ wc->flag_1st_irq--; >+ *(wc->membase+AP_CNT_IRQ_REG)=0; >+ for(x=0;x<(wc->numspans);x++) >+ wc->tspans[x]->span.irqmisses = 0; >+ } >+ // zera erro de CRC >+ cnt_tmp = (*(wc->membase + AP_CNT_CRC_REG)); >+ } else { >+ // neste registro da FPGA temos o numero de interrupcoes que aconteceram >+ // desde o ultimo reset do contador de interrupcoes. O normal eh ler 1. >+ cnt_irq_misses = (*(wc->membase+AP_CNT_IRQ_REG)); >+ // Se for zero significa que a interrupcao nao foi gerada pelo nosso cartao >+ if(cnt_irq_misses == 0) { >+ if(debug) printk("Interrupcao gerada mas nao pela FPGA?!\n"); >+ ret = 0; >+ goto out; >+ } >+ // reseta o contador >+ *(wc->membase+AP_CNT_IRQ_REG)=0; >+ for(x=0;x<(wc->numspans);x++) >+ wc->tspans[x]->span.irqmisses += (cnt_irq_misses-1); >+ } >+ >+ if (!wc->spansstarted) { >+ /* Not prepped yet! */ >+ ret = 0; >+ goto out; >+ } >+ >+ wc->intcount++; >+ >+#ifdef ENABLE_WORKQUEUES >+ int cpus = num_online_cpus(); >+ atomic_set(&wc->worklist, wc->numspans); >+ if (wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING) >+ ap4_queue_work(wc->workq, &wc->tspans[0]->swork, 0); >+ else >+ atomic_dec(&wc->worklist); >+ if (wc->numspans > 1) { >+ if (wc->tspans[1]->span.flags & DAHDI_FLAG_RUNNING) >+ ap4_queue_work(wc->workq, &wc->tspans[1]->swork, 1 % cpus); >+ else >+ atomic_dec(&wc->worklist); >+ } >+ if (wc->numspans == 4) { >+ if (wc->tspans[2]->span.flags & DAHDI_FLAG_RUNNING) >+ ap4_queue_work(wc->workq, &wc->tspans[2]->swork, 2 % cpus); >+ else >+ atomic_dec(&wc->worklist); >+ if (wc->tspans[3]->span.flags & DAHDI_FLAG_RUNNING) >+ ap4_queue_work(wc->workq, &wc->tspans[3]->swork, 3 % cpus); >+ else >+ atomic_dec(&wc->worklist); >+ } >+#else >+ if (tdm_loop == 1) >+ ap4_tdm_loop(wc); >+ else { >+ ap4_receiveprep(wc); >+ ap4_transmitprep(wc); >+ } >+#endif >+ >+ // Estatisticas a cada 128ms >+ if(!(wc->intcount&0x7f)){ >+ clock_source = wc->hw_regs->clock_source; >+ cnt_tmp = (*(wc->membase + AP_CNT_CV_REG)); >+ for(x=0;x<(wc->numspans);x++) >+ wc->tspans[x]->span.count.bpv += (cnt_tmp>>(8*x))&0xff; >+ cnt_tmp = (*(wc->membase + AP_CNT_CRC_REG)); >+ for(x=0;x<(wc->numspans);x++) >+ wc->tspans[x]->span.count.crc4 += (cnt_tmp>>(8*x))&0xff; >+ cnt_tmp = (*(wc->membase + AP_CNT_SLIP_REG)); >+ for(x=0;x<(wc->numspans);x++) { >+ if (((cnt_tmp>>(8*x))&0xff) && (!(wc->tspans[x]->span.alarms & DAHDI_ALARM_RED)) ){ >+ wc->tspans[x]->slipcount++; >+ if(debug) printk("Slip detected on span %d: slipcount = %d\n", x+1, wc->tspans[x]->slipcount); >+ } >+ } >+ } >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ >+ __handle_leds(wc); >+ >+ __ap4_do_counters(wc); >+ >+ //x = wc->intcount & 15; >+ x = wc->intcount & 7; >+ switch(x) { >+ case 0: >+ case 1: >+ case 2: >+ case 3: >+ __ap4_check_alarms(wc, x); >+ break; >+ case 4: >+ case 5: >+ case 6: >+ case 7: >+ __ap4_check_sigbits(wc, x - 4); >+ break; >+ } >+ >+ if (wc->checktiming > 0) >+ __ap4_set_timing_source_auto(wc); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ /* IRQ was treated */ >+ ret = 1; >+out: >+#ifdef AP400_HDLC >+ /* Call AP400_HDLC_CARD IRQ handler before leave */ >+ ret |= ap400_intr_handler(irq, wc->hdlc_card); >+#endif >+ >+ return IRQ_RETVAL(ret); >+} >+ >+ >+static int __devinit ap4_launch(struct ap4 *wc) >+{ >+ int x; >+ unsigned long flags; >+ >+ if (wc->tspans[0]->span.flags & DAHDI_FLAG_REGISTERED) >+ return 0; >+ printk("%s: Launching card: %d\n", wc->variety, wc->order); >+ >+ /* Setup serial parameters and system interface */ >+ for (x=0;x<4;x++) { >+ //ap4_serial_setup(wc, x); >+ wc->globalconfig = 1; >+ } >+ >+ if (dahdi_register(&wc->tspans[0]->span, 0)) { >+ printk(KERN_ERR "Unable to register span %s\n", wc->tspans[0]->span.name); >+ return -1; >+ } >+ if (wc->numspans > 1) { >+ if (dahdi_register(&wc->tspans[1]->span, 0)) { >+ printk(KERN_ERR "Unable to register span %s\n", wc->tspans[1]->span.name); >+ dahdi_unregister(&wc->tspans[0]->span); >+ return -1; >+ } >+ } >+ if (wc->numspans == 4) { >+ if (dahdi_register(&wc->tspans[2]->span, 0)) { >+ printk(KERN_ERR "Unable to register span %s\n", wc->tspans[2]->span.name); >+ dahdi_unregister(&wc->tspans[0]->span); >+ dahdi_unregister(&wc->tspans[1]->span); >+ return -1; >+ } >+ if (dahdi_register(&wc->tspans[3]->span, 0)) { >+ printk(KERN_ERR "Unable to register span %s\n", wc->tspans[3]->span.name); >+ dahdi_unregister(&wc->tspans[0]->span); >+ dahdi_unregister(&wc->tspans[1]->span); >+ dahdi_unregister(&wc->tspans[2]->span); >+ return -1; >+ } >+ } >+ wc->checktiming = 1; >+ spin_lock_irqsave(&wc->reglock, flags); >+// __ap4_set_timing_source(wc,4); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+#ifdef ENABLE_TASKLETS >+ tasklet_init(&wc->ap4_tlet, ap4_tasklet, (unsigned long)wc); >+#endif >+ return 0; >+} >+ >+ >+static int ap4xx_liu_reset(struct ap4 *wc) >+{ >+ unsigned int jiffies_hold = jiffies; >+ *(wc->membase+AP_LEDS_REG) |= AP_LIU_RESET_BIT; >+ while(jiffies<=(jiffies_hold+2)); >+ *(wc->membase+AP_LEDS_REG) &= ~AP_LIU_RESET_BIT; >+ return 0; >+} >+ >+ >+static int ap4xx_bus_test(struct ap4 *wc) >+{ >+ int tst_result = 0; >+ unsigned int val; >+ >+ *(wc->membase+AP_E1_CONFIG_REG) = 0xAAAAAAAA; >+ *wc->membase = 0; // flush >+ val = *(wc->membase+AP_E1_CONFIG_REG); >+ if(val != 0xAAAAAAAA) { >+ printk("Escrito 0xAAAAAAAA, lido 0x%08X!\n", val); >+ tst_result++; >+ } >+ *(wc->membase+AP_E1_CONFIG_REG) = 0x55555555; >+ *wc->membase = 0; // flush >+ val = *(wc->membase+AP_E1_CONFIG_REG); >+ if(val != 0x55555555) { >+ printk("Escrito 0x55555555, lido 0x%08X!\n", val); >+ tst_result++; >+ } >+ *(wc->membase+AP_E1_CONFIG_REG) = 0xFFFFFFFF; >+ *wc->membase = 0; // flush >+ val = *(wc->membase+AP_E1_CONFIG_REG); >+ if(val != 0xFFFFFFFF) { >+ printk("Escrito 0xFFFFFFFF, lido 0x%08X!\n", val); >+ tst_result++; >+ } >+ *(wc->membase+AP_E1_CONFIG_REG) = 0x00000000; >+ *wc->membase = 0xFFFFFFFF; // flush >+ val = *(wc->membase+AP_E1_CONFIG_REG); >+ if(val != 0x00000000) { >+ printk("Escrito 0x00000000, lido 0x%08X!\n", val); >+ tst_result++; >+ } >+ return tst_result; >+} >+ >+#ifdef TIMER_DEBUG >+void ap4xx_opt_timeout(unsigned long arg) >+{ >+ struct pci_dev *dev = (struct pci_dev *)arg; >+ struct ap4 *wc = pci_get_drvdata(dev); >+ >+// ap_debugk("wc->tspans[0]->span.chans[1].readchunk[1] = 0x%02x\n", wc->tspans[0]->span.chans[0].readchunk[1]); >+// ap_debugk("e1s_cfg = 0x%08x\n", *(wc->membase+AP_E1_CONFIG_REG)); >+// ap_debugk("e1_status = 0x%08x\n", *(wc->membase + AP_E1_STATUS_REG)); >+// ap_debugk("clk_cfg = 0x%08x\n", *(wc->membase+0x07)); >+// ap_debugk("e1_data = 0x%08x\n", *(wc->membase + (AP_DATA_BASE + 1))); >+// ap_debugk("cas_data = 0x%08x\n", *(wc->membase + AP_CAS_BASE)); >+ >+ // dispara timer novamente >+ init_timer(&ap4xx_opt_timer); >+ ap4xx_opt_timer.function = ap4xx_opt_timeout; >+ ap4xx_opt_timer.data = arg; >+ ap4xx_opt_timer.expires = jiffies + (delay/4); >+ add_timer(&ap4xx_opt_timer); >+ >+} >+#endif >+ >+static inline int ap4_card_detect (struct ap4 *wc) { >+ int i; >+ if ((wc->hw_regs->card_id != AP4XX_CARD_ID) && >+ (wc->hw_regs->card_id != APE4XX_CARD_ID)) { >+ printk("AP400: Unknown card ID(0x%08X)! Aborting...\n", wc->hw_regs->card_id); >+ return -EPERM; >+ } >+ // Test bus integrity >+ for (i=0; i < 1000; i++) { >+ if (ap4xx_bus_test(wc)) { >+ printk("AP400: Bus integrity test failed! Aborting...\n"); >+ return -EIO; >+ } >+ } >+ printk("AP400: Bus integrity OK!\n"); >+ >+ wc->fpgaver = wc->hw_regs->fpga_ver; >+ wc->numspans = wc->hw_regs->span_num; >+ wc->hwid = ((*(wc->membase+AP_HWCONFIG_REG))&AP_HWID_MASK)>>4; >+ >+ if ((wc->hwid == AP_HWID_1E1_RJ && wc->numspans != 1) || >+ (wc->hwid == AP_HWID_2E1_RJ && wc->numspans != 2) || >+ (wc->hwid == AP_HWID_4E1_RJ && wc->numspans != 4)) { >+ printk("AP400: Incompatible Hardware ID(0x%02x)! Aborting...\n", wc->hwid); >+ return -EIO; >+ } >+ >+ if (wc->hw_regs->card_id == AP4XX_CARD_ID) >+ switch (wc->numspans) { >+ case 1: >+ wc->dt = (struct devtype *) &ap401; >+ break; >+ case 2: >+ wc->dt = (struct devtype *) &ap402; >+ break; >+ case 4: >+ wc->dt = (struct devtype *) &ap404; >+ break; >+ default: >+ printk("AP400: Unsupported spans number(%d)! Aborting...\n", >+ wc->numspans); >+ return -EPERM; >+ } >+ else >+ switch (wc->numspans) { >+ case 1: >+ wc->dt = (struct devtype *) &ape401; >+ break; >+ case 2: >+ wc->dt = (struct devtype *) &ape402; >+ break; >+ case 4: >+ wc->dt = (struct devtype *) &ape404; >+ break; >+ default: >+ printk("APE400: Unsupported spans number(%d)! Aborting...\n", >+ wc->numspans); >+ return -EPERM; >+ } >+ >+ wc->variety = wc->dt->desc; >+ printk("Found a %s (firmware version %d.%d) at base address %08lx, remapped to %p\n", >+ wc->variety, wc->fpgaver >> 8, wc->fpgaver & 0xFF, >+ wc->memaddr, wc->membase); >+ >+ return 0; >+} >+ >+static void __devexit ap4_remove_one(struct pci_dev *pdev); >+ >+static int __devinit ap4_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) >+{ >+ int res; >+ struct ap4 *wc; >+ int x,f; >+ int basesize; >+ static int initd_ifaces=0; >+ // Initialize pointer struct >+ if(!initd_ifaces){ >+ memset((void *)cards,0,(sizeof(struct ap4 *))*MAX_AP4_CARDS); >+ initd_ifaces=1; >+ } >+ >+ if ((res = pci_enable_device(pdev)) != 0) { >+ goto out; >+ } >+ // Allocate card struct >+ wc = kmalloc(sizeof(struct ap4), GFP_KERNEL); >+ if (wc == NULL) { >+ res = -ENOMEM; >+ goto out; >+ } >+ >+ memset(wc, 0x0, sizeof(struct ap4)); >+ spin_lock_init(&wc->reglock); >+ >+ basesize = DAHDI_MAX_CHUNKSIZE * 32 * 2 * 4; >+ >+ // Request PCI regions >+ if ((res = pci_request_regions(pdev, "ap400")) != 0) { >+ printk("AP400: Unable to request regions!\n"); >+ goto out; >+ } >+ >+ // Remap PCI address >+ wc->memaddr = pci_resource_start(pdev, 2); >+ wc->memlen = pci_resource_len(pdev, 2); >+ wc->membase = ioremap_nocache(wc->memaddr, wc->memlen); >+ if(wc->membase == NULL) { >+ printk("AP400: ioremap failed!\n"); >+ res = -EIO; >+ goto out; >+ } >+ wc->hw_regs = (struct ap4_regs *) wc->membase; >+ >+ // Detect Card model >+ if ((res = ap4_card_detect(wc)) != 0) >+ goto out; >+ >+ ap4xx_liu_reset(wc); >+ >+ // This rids of the Double missed interrupt message after loading >+ wc->last0 = 1; >+ >+ wc->dev = pdev; >+ >+ // 32 channels, Double-buffer, Read/Write, 4 spans >+ wc->writechunk = kmalloc(basesize * 2, GFP_KERNEL); >+ if (!wc->writechunk) { >+ printk("%s: Unable to allocate memory!\n", wc->variety); >+ res = -ENOMEM; >+ goto out; >+ } >+ >+ // Read is after the whole write piece (in words) >+ wc->readchunk = wc->writechunk + basesize / 4; >+ >+ >+ // Initialize Write/Buffers to all blank data >+ memset((void *) wc->writechunk, 0x00, basesize); >+ memset((void *) wc->readchunk, 0xff, basesize); >+ >+ /* Keep track of which device we are */ >+ pci_set_drvdata(pdev, wc); >+ >+ /* inicializa contador de interrupcao */ >+ wc->intcount = 0; >+ >+ for(x = 0; x < MAX_AP4_CARDS; x++) { >+ if (!cards[x]) break; >+ } >+ >+ if (x >= MAX_AP4_CARDS) { >+ printk("No cards[] slot available!!\n"); >+ res = -ENOMEM; >+ goto out; >+ } >+ >+ wc->num = x; >+ cards[x] = wc; >+ >+ /* Allocate pieces we need here, consider 31 channels for E1*/ >+ for (x=0;x<4;x++) { >+ wc->tspans[x] = kmalloc(sizeof(struct ap4_span), GFP_KERNEL); >+ if (wc->tspans[x]) { >+ memset(wc->tspans[x], 0, sizeof(struct ap4_span)); >+ wc->tspans[x]->spantype = TYPE_E1; >+ } else { >+ res = -ENOMEM; >+ goto out; >+ } >+ for (f = 0; f < 31; f++) { >+ if (!(wc->tspans[x]->chans[f] = kmalloc(sizeof(*wc->tspans[x]->chans[f]), GFP_KERNEL))) { >+ res = -ENOMEM; >+ goto out; >+ } >+ memset(wc->tspans[x]->chans[f], 0, sizeof(*wc->tspans[x]->chans[f])); >+ } >+#ifdef ENABLE_WORKQUEUES >+ INIT_WORK(&wc->tspans[x]->swork, workq_handlespan, wc->tspans[x]); >+#endif >+ wc->tspans[x]->spanflags |= wc->dt->flags; >+ } >+ >+ if (request_irq(pdev->irq, ap4_interrupt, IRQF_DISABLED | IRQF_SHARED, "ap400", wc)) >+ { >+ printk("%s: Unable to request IRQ %d\n", wc->variety, pdev->irq); >+ res = -EIO; >+ goto out; >+ } >+ >+ init_spans(wc); >+ >+ /* Launch cards as appropriate */ >+ x = 0; >+ for(;;) { >+ /* Find a card to activate */ >+ f = 0; >+ for (x=0;cards[x];x++) { >+ if (cards[x]->order <= highestorder) { >+ ap4_launch(cards[x]); >+ if (cards[x]->order == highestorder) >+ f = 1; >+ } >+ } >+ /* If we found at least one, increment the highest order and search again, otherwise stop */ >+ if (f) >+ highestorder++; >+ else >+ break; >+ } >+ >+#ifdef APEC_SUPPORT >+ if (wc->fpgaver >= 0x0400) >+ wc->apec_enable = 1; >+#endif >+ >+#ifdef TIMER_DEBUG >+ // dispara timer de debug >+ init_timer(&ap4xx_opt_timer); >+ ap4xx_opt_timer.function = ap4xx_opt_timeout; >+ ap4xx_opt_timer.data = (unsigned long) pdev; >+ ap4xx_opt_timer.expires = jiffies + 100; >+ add_timer(&ap4xx_opt_timer); >+#endif >+ >+ /* Initialize HDLC_CARD */ >+#ifdef AP400_HDLC >+ u8 __iomem *base_addr[3]; >+ unsigned int bar_size[3]; >+ int i; >+ base_addr[2] = (void *) wc->membase; >+ bar_size[2] = wc->memlen; >+ for (i = 0; i < 2; i++) { >+ bar_size[i] = (u32) pci_resource_len(pdev, i); >+ base_addr[i] = ioremap_nocache(pci_resource_start(pdev, i), >+ bar_size[i]); >+ if (base_addr[i] == NULL) { >+ printk(KERN_ERR "Memory map failed\n"); >+ res = -ENODEV; >+ goto out; >+ } >+ } >+ ap400_card_init(&wc->hdlc_card, base_addr, bar_size); >+ ap400_intr_enable(wc->hdlc_card); >+#endif >+ >+ res = 0; >+out: >+ if (res != 0) { >+ ap4_remove_one(pdev); >+ } >+ return res; >+} >+ >+static void __devexit ap4_remove_one(struct pci_dev *pdev) >+{ >+ struct ap4 *wc = pci_get_drvdata(pdev); >+ int x; >+ >+ if (wc) { >+ ap_debugk("desabilita interrupcao!\n"); >+ // desabilita interrupcao >+ *(wc->membase + AP_INT_CONTROL_REG) &= ~AP_INT_CTL_ENABLE; >+ >+#ifdef APEC_SUPPORT >+ // Stop echo cancellation module >+ ap4_apec_release(wc); >+#endif >+ /* Unregister spans */ >+ if (wc->tspans[0]->span.flags & DAHDI_FLAG_REGISTERED) >+ dahdi_unregister(&wc->tspans[0]->span); >+ if (wc->numspans > 1) { >+ if (wc->tspans[1]->span.flags & DAHDI_FLAG_REGISTERED) >+ dahdi_unregister(&wc->tspans[1]->span); >+ } >+ if (wc->numspans == 4) { >+ if (wc->tspans[2]->span.flags & DAHDI_FLAG_REGISTERED) >+ dahdi_unregister(&wc->tspans[2]->span); >+ if (wc->tspans[3]->span.flags & DAHDI_FLAG_REGISTERED) >+ dahdi_unregister(&wc->tspans[3]->span); >+ } >+#ifdef ENABLE_WORKQUEUES >+ if (wc->workq) { >+ flush_workqueue(wc->workq); >+ destroy_workqueue(wc->workq); >+ } >+#endif >+ >+#ifdef TIMER_DEBUG >+ del_timer(&ap4xx_opt_timer); >+#endif >+ >+ wc->hw_regs = NULL; >+ if(wc->membase) >+ iounmap((void *)wc->membase); >+ >+ /* Immediately free resources */ >+ kfree((void *) wc->writechunk); >+ >+#ifdef AP400_HDLC >+ /* Remove HDLC Card */ >+ ap400_card_remove(wc->hdlc_card); >+ if (wc->hdlc_card->cfg_base_addr) >+ iounmap(wc->hdlc_card->cfg_base_addr); >+ if (wc->hdlc_card->buf_base_addr) >+ iounmap(wc->hdlc_card->buf_base_addr); >+ kfree(wc->hdlc_card); >+#endif >+ free_irq(pdev->irq, wc); >+ >+ cards[wc->num] = NULL; >+ for (x=0;x<wc->numspans;x++) { >+ if (wc->tspans[x]) >+ kfree(wc->tspans[x]); >+ } >+ kfree(wc); >+ } >+ pci_release_regions(pdev); >+ pci_disable_device(pdev); >+ pci_set_drvdata(pdev, NULL); >+ printk(KERN_INFO "AP400 driver removed\n"); >+} >+ >+ >+static struct pci_device_id ap4_pci_tbl[] __devinitdata = >+{ >+ { PCI_DEVICE(PCI_VENDOR_ID_XILINX, PCI_DEVICE_ID_AP4XX), }, >+ { 0, } >+}; >+ >+ >+static struct pci_driver ap4_driver = { >+ .name = "Unified ap4xx driver", >+ .probe = ap4_init_one, >+#ifdef LINUX26 >+ .remove = __devexit_p(ap4_remove_one), >+#else >+ .remove = ap4_remove_one, >+#endif >+ .id_table = ap4_pci_tbl, >+}; >+ >+static int __init ap4_init(void) >+{ >+ int res; >+ printk("Unified AP4XX PCI Card Driver\n"); >+ res = dahdi_pci_module(&ap4_driver); >+ if (res) { >+ return -ENODEV; >+ } >+ return 0; >+} >+ >+static void __exit ap4_cleanup(void) >+{ >+ printk("Unified AP4XX PCI Card Driver Cleanup\n"); >+ pci_unregister_driver(&ap4_driver); >+} >+ >+ >+MODULE_AUTHOR("Aligera (aligera@aligera.com.br)"); >+MODULE_DESCRIPTION("Unified AP4XX PCI Card Driver"); >+#ifdef MODULE_LICENSE >+MODULE_LICENSE("GPL"); >+#endif >+module_param(debug, int, 0600); >+module_param(loopback, int, 0600); >+module_param(noburst, int, 0600); >+module_param(debugslips, int, 0600); >+module_param(polling, int, 0600); >+module_param(timingcable, int, 0600); >+module_param(t1e1override, int, 0600); >+module_param(alarmdebounce, int, 0600); >+module_param(j1mode, int, 0600); >+ >+MODULE_DEVICE_TABLE(pci, ap4_pci_tbl); >+ >+module_init(ap4_init); >+module_exit(ap4_cleanup); >diff --git a/drivers/dahdi/ap400/apec.c b/drivers/dahdi/ap400/apec.c >new file mode 100644 >index 0000000..b43655e >--- /dev/null >+++ b/drivers/dahdi/ap400/apec.c >@@ -0,0 +1,390 @@ >+/* >+ * AP400 Echo Cancelation Hardware support >+ * >+ * Written by Wagner Gegler <aligera@aligera.com.br> >+ * >+ * Based on previous work written by Mark Spencer <markster@digium.com> >+ * >+ * Copyright (C) 2005-2006 Digium, Inc. >+ * >+ * Mark Spencer <markster@digium.com> >+ * >+ * All Rights Reserved >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation; either version 2 of the License, or >+ * (at your option) any later version. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. >+ * >+ */ >+ >+#include <linux/slab.h> >+#include <linux/vmalloc.h> >+#include <linux/string.h> >+#include <linux/time.h> >+#include <linux/version.h> >+#include <linux/delay.h> >+ >+#include "apec.h" >+#include "oct6100api/oct6100_api.h" >+ >+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) >+#include <linux/config.h> >+#else >+#include <linux/autoconf.h> >+#endif >+ >+/* API for Octasic access */ >+UINT32 Oct6100UserGetTime(tPOCT6100_GET_TIME f_pTime) >+{ >+ /* Why couldn't they just take a timeval like everyone else? */ >+ struct timeval tv; >+ unsigned long long total_usecs; >+ unsigned int mask = ~0; >+ >+ do_gettimeofday(&tv); >+ total_usecs = (((unsigned long long)(tv.tv_sec)) * 1000000) + >+ (((unsigned long long)(tv.tv_usec))); >+ f_pTime->aulWallTimeUs[0] = (total_usecs & mask); >+ f_pTime->aulWallTimeUs[1] = (total_usecs >> 32); >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserMemSet(PVOID f_pAddress, UINT32 f_ulPattern, UINT32 f_ulLength) >+{ >+ memset(f_pAddress, f_ulPattern, f_ulLength); >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserMemCopy(PVOID f_pDestination, const void *f_pSource, UINT32 f_ulLength) >+{ >+ memcpy(f_pDestination, f_pSource, f_ulLength); >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserCreateSerializeObject(tPOCT6100_CREATE_SERIALIZE_OBJECT f_pCreate) >+{ >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserDestroySerializeObject(tPOCT6100_DESTROY_SERIALIZE_OBJECT f_pDestroy) >+{ >+#ifdef OCTASIC_DEBUG >+ printk("I should never be called! (destroy serialize object)\n"); >+#endif >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserSeizeSerializeObject(tPOCT6100_SEIZE_SERIALIZE_OBJECT f_pSeize) >+{ >+ /* Not needed */ >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserReleaseSerializeObject(tPOCT6100_RELEASE_SERIALIZE_OBJECT f_pRelease) >+{ >+ /* Not needed */ >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserDriverWriteApi(tPOCT6100_WRITE_PARAMS f_pWriteParams) >+{ >+ oct_write(f_pWriteParams->pProcessContext, f_pWriteParams->ulWriteAddress, f_pWriteParams->usWriteData); >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserDriverWriteSmearApi(tPOCT6100_WRITE_SMEAR_PARAMS f_pSmearParams) >+{ >+ unsigned int x; >+ for (x=0;x<f_pSmearParams->ulWriteLength;x++) { >+ oct_write(f_pSmearParams->pProcessContext, f_pSmearParams->ulWriteAddress + (x << 1), f_pSmearParams->usWriteData); >+ } >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserDriverWriteBurstApi(tPOCT6100_WRITE_BURST_PARAMS f_pBurstParams) >+{ >+ unsigned int x; >+ for (x=0;x<f_pBurstParams->ulWriteLength;x++) { >+ oct_write(f_pBurstParams->pProcessContext, f_pBurstParams->ulWriteAddress + (x << 1), f_pBurstParams->pusWriteData[x]); >+ } >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserDriverReadApi(tPOCT6100_READ_PARAMS f_pReadParams) >+{ >+ *(f_pReadParams->pusReadData) = oct_read(f_pReadParams->pProcessContext, f_pReadParams->ulReadAddress); >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams) >+{ >+ unsigned int x; >+ for (x=0;x<f_pBurstParams->ulReadLength;x++) { >+ f_pBurstParams->pusReadData[x] = oct_read(f_pBurstParams->pProcessContext, f_pBurstParams->ulReadAddress + (x << 1)); >+ } >+ return cOCT6100_ERR_OK; >+} >+ >+#if 0 >+#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_HT_FREEZE >+#else >+#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_POWER_DOWN >+#endif >+ >+struct apec_s { >+ tPOCT6100_INSTANCE_API pApiInstance; >+ UINT32 aulEchoChanHndl[128]; >+ int chanflags[128]; >+ int ecmode[128]; >+ int numchans; >+}; >+ >+#define FLAG_DTMF (1 << 0) >+#define FLAG_MUTE (1 << 1) >+#define FLAG_ECHO (1 << 2) >+ >+static void apec_setecmode(struct apec_s *apec, int channel, int mode) >+{ >+ tOCT6100_CHANNEL_MODIFY *modify; >+ UINT32 ulResult; >+ >+ if (apec->ecmode[channel] == mode) >+ return; >+ modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC); >+ if (!modify) { >+ printk("APEC: Unable to allocate memory for setec!\n"); >+ return; >+ } >+ Oct6100ChannelModifyDef(modify); >+ modify->ulEchoOperationMode = mode; >+ modify->ulChannelHndl = apec->aulEchoChanHndl[channel]; >+ ulResult = Oct6100ChannelModify(apec->pApiInstance, modify); >+ if (ulResult != GENERIC_OK) { >+ printk("Failed to apply echo can changes on channel %d!\n", channel); >+ } else { >+#ifdef OCTASIC_DEBUG >+ printk("Echo can on channel %d set to %d\n", channel, mode); >+#endif >+ apec->ecmode[channel] = mode; >+ } >+ kfree(modify); >+} >+ >+void apec_setec(struct apec_s *apec, int channel, int eclen) >+{ >+ if (eclen) { >+ apec->chanflags[channel] |= FLAG_ECHO; >+ apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); >+ apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_NORMAL); >+ } else { >+ apec->chanflags[channel] &= ~FLAG_ECHO; >+ if (apec->chanflags[channel] & (FLAG_DTMF | FLAG_MUTE)) { >+ apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); >+ apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE); >+ } else >+ apec_setecmode(apec, channel, cOCT6100_ECHO_OP_MODE_DIGITAL); >+ } >+ printk("APEC: Setting EC on channel %d to %d\n", channel, eclen); >+} >+ >+int apec_checkirq(struct apec_s *apec) >+{ >+ tOCT6100_INTERRUPT_FLAGS InterruptFlags; >+ >+ Oct6100InterruptServiceRoutineDef(&InterruptFlags); >+ Oct6100InterruptServiceRoutine(apec->pApiInstance, &InterruptFlags); >+ >+ return InterruptFlags.fToneEventsPending ? 1 : 0; >+} >+ >+unsigned int apec_capacity_get(void *wc) >+{ >+ UINT32 ulResult; >+ >+ tOCT6100_API_GET_CAPACITY_PINS CapacityPins; >+ >+ Oct6100ApiGetCapacityPinsDef(&CapacityPins); >+ CapacityPins.pProcessContext = wc; >+ CapacityPins.ulMemoryType = cOCT6100_MEM_TYPE_DDR; >+ CapacityPins.fEnableMemClkOut = TRUE; >+ CapacityPins.ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; >+ >+ ulResult = Oct6100ApiGetCapacityPins(&CapacityPins); >+ if (ulResult != cOCT6100_ERR_OK) { >+ printk("Failed to get chip capacity, code %08x!\n", ulResult); >+ return 0; >+ } >+ return CapacityPins.ulCapacityValue; >+} >+ >+struct apec_s *apec_init(void *wc, int *isalaw, int numspans, const struct firmware *firmware) >+{ >+ tOCT6100_CHIP_OPEN *ChipOpen; >+ tOCT6100_GET_INSTANCE_SIZE InstanceSize; >+ tOCT6100_CHANNEL_OPEN *ChannelOpen; >+ UINT32 ulResult; >+ struct apec_s *apec; >+ int x, law; >+#ifdef CONFIG_4KSTACKS >+ unsigned long flags; >+#endif >+ >+ if (!(apec = kmalloc(sizeof(struct apec_s), GFP_KERNEL))) >+ return NULL; >+ >+ memset(apec, 0, sizeof(struct apec_s)); >+ >+ if (!(ChipOpen = kmalloc(sizeof(tOCT6100_CHIP_OPEN), GFP_KERNEL))) { >+ kfree(apec); >+ return NULL; >+ } >+ >+ memset(ChipOpen, 0, sizeof(tOCT6100_CHIP_OPEN)); >+ >+ if (!(ChannelOpen = kmalloc(sizeof(tOCT6100_CHANNEL_OPEN), GFP_KERNEL))) { >+ kfree(apec); >+ kfree(ChipOpen); >+ return NULL; >+ } >+ >+ memset(ChannelOpen, 0, sizeof(tOCT6100_CHANNEL_OPEN)); >+ >+ for (x=0;x<128;x++) >+ apec->ecmode[x] = -1; >+ >+ apec->numchans = numspans * 32; >+ printk("APEC: echo cancellation for %d channels\n", apec->numchans); >+ >+ Oct6100ChipOpenDef(ChipOpen); >+ >+ /* Setup Chip Open Parameters */ >+ ChipOpen->ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ; >+ Oct6100GetInstanceSizeDef(&InstanceSize); >+ >+ ChipOpen->pProcessContext = wc; >+ >+ ChipOpen->pbyImageFile = firmware->data; >+ ChipOpen->ulImageSize = firmware->size; >+ >+ ChipOpen->fEnableMemClkOut = TRUE; >+ ChipOpen->ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; >+ ChipOpen->ulMaxChannels = apec->numchans; >+ ChipOpen->ulMemoryType = cOCT6100_MEM_TYPE_DDR; >+ ChipOpen->ulMemoryChipSize = cOCT6100_MEMORY_CHIP_SIZE_32MB; >+ ChipOpen->ulNumMemoryChips = 1; >+ ChipOpen->ulMaxTdmStreams = 4; >+ ChipOpen->aulTdmStreamFreqs[0] = cOCT6100_TDM_STREAM_FREQ_8MHZ; >+ ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_FALLING_EDGE; >+#if 0 >+ ChipOpen->fEnableAcousticEcho = TRUE; >+#endif >+ >+ ulResult = Oct6100GetInstanceSize(ChipOpen, &InstanceSize); >+ if (ulResult != cOCT6100_ERR_OK) { >+ printk("Failed to get instance size, code %08x!\n", ulResult); >+ kfree(apec); >+ return NULL; >+ } >+ >+ >+ apec->pApiInstance = vmalloc(InstanceSize.ulApiInstanceSize); >+ if (!apec->pApiInstance) { >+ printk("Out of memory (can't allocate %d bytes)!\n", InstanceSize.ulApiInstanceSize); >+ kfree(apec); >+ kfree(ChipOpen); >+ kfree(ChannelOpen); >+ return NULL; >+ } >+ >+ /* I don't know what to curse more in this comment, the problems caused by >+ * the 4K kernel stack limit change or the octasic API for being so darn >+ * stack unfriendly. Stupid, stupid, stupid. So we disable IRQs so we >+ * don't run the risk of overflowing the stack while we initialize the >+ * octasic. */ >+#ifdef CONFIG_4KSTACKS >+ local_irq_save(flags); >+#endif >+ ulResult = Oct6100ChipOpen(apec->pApiInstance, ChipOpen); >+ if (ulResult != cOCT6100_ERR_OK) { >+ printk("Failed to open chip, code %08x!\n", ulResult); >+#ifdef CONFIG_4KSTACKS >+ local_irq_restore(flags); >+#endif >+ kfree(apec); >+ kfree(ChipOpen); >+ kfree(ChannelOpen); >+ return NULL; >+ } >+ for (x=0; x < 128; x++) { >+ /* execute this loop always on 4 span cards but >+ * on 2 span cards only execute for the channels related to our spans */ >+ if ((x & 0x3) < numspans) { >+ /* span timeslots are interleaved 12341234... >+ * therefore, the lower 2 bits tell us which span this >+ * timeslot/channel >+ */ >+ if (isalaw[x & 0x03]) >+ law = cOCT6100_PCM_A_LAW; >+ else >+ law = cOCT6100_PCM_U_LAW; >+ Oct6100ChannelOpenDef(ChannelOpen); >+ ChannelOpen->pulChannelHndl = &apec->aulEchoChanHndl[x]; >+ ChannelOpen->ulUserChanId = x; >+ ChannelOpen->TdmConfig.ulRinPcmLaw = law; >+ ChannelOpen->TdmConfig.ulRinStream = 0; >+ ChannelOpen->TdmConfig.ulRinTimeslot = x; >+ ChannelOpen->TdmConfig.ulSinPcmLaw = law; >+ ChannelOpen->TdmConfig.ulSinStream = 1; >+ ChannelOpen->TdmConfig.ulSinTimeslot = x; >+ ChannelOpen->TdmConfig.ulSoutPcmLaw = law; >+ ChannelOpen->TdmConfig.ulSoutStream = 2; >+ ChannelOpen->TdmConfig.ulSoutTimeslot = x; >+ ChannelOpen->TdmConfig.ulRoutPcmLaw = law; >+ ChannelOpen->TdmConfig.ulRoutStream = 3; >+ ChannelOpen->TdmConfig.ulRoutTimeslot = x; >+ ChannelOpen->VqeConfig.fEnableNlp = TRUE; >+ ChannelOpen->VqeConfig.fRinDcOffsetRemoval = TRUE; >+ ChannelOpen->VqeConfig.fSinDcOffsetRemoval = TRUE; >+ >+ ChannelOpen->fEnableToneDisabler = TRUE; >+ ChannelOpen->ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_DIGITAL; >+ >+ ulResult = Oct6100ChannelOpen(apec->pApiInstance, ChannelOpen); >+ if (ulResult != GENERIC_OK) { >+ printk("Failed to open channel %d!\n", x); >+ } >+ } >+ } >+ >+#ifdef CONFIG_4KSTACKS >+ local_irq_restore(flags); >+#endif >+ kfree(ChipOpen); >+ kfree(ChannelOpen); >+ return apec; >+} >+ >+void apec_release(struct apec_s *apec) >+{ >+ UINT32 ulResult; >+ tOCT6100_CHIP_CLOSE ChipClose; >+ >+ Oct6100ChipCloseDef(&ChipClose); >+ ulResult = Oct6100ChipClose(apec->pApiInstance, &ChipClose); >+ if (ulResult != cOCT6100_ERR_OK) { >+ printk("Failed to close chip, code %08x!\n", ulResult); >+ } >+ vfree(apec->pApiInstance); >+ kfree(apec); >+ printk(KERN_INFO "APEC: Releasing...\n"); >+} >diff --git a/drivers/dahdi/ap400/apec.h b/drivers/dahdi/ap400/apec.h >new file mode 100644 >index 0000000..483b182 >--- /dev/null >+++ b/drivers/dahdi/ap400/apec.h >@@ -0,0 +1,48 @@ >+/* >+ * AP400 Echo Cancelation Hardware support >+ * >+ * Written by Wagner Gegler <aligera@aligera.com.br> >+ * >+ * Based on previous work written by Mark Spencer <markster@digium.com> >+ * >+ * Copyright (C) 2005-2006 Digium, Inc. >+ * >+ * Mark Spencer <markster@digium.com> >+ * >+ * All Rights Reserved >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation; either version 2 of the License, or >+ * (at your option) any later version. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. >+ * >+ */ >+ >+#ifndef _APEC_H_ >+#define _APEC_H_ >+ >+#include <linux/firmware.h> >+ >+struct apec_s; >+ >+/* From AP400 */ >+unsigned int oct_read(void *card, unsigned int addr); >+void oct_write(void *card, unsigned int addr, unsigned int data); >+ >+/* From APEC */ >+struct apec_s *apec_init(void *wc, int *isalaw, int numspans, const struct firmware *firmware); >+unsigned int apec_capacity_get(void *wc); >+void apec_setec(struct apec_s *instance, int channel, int eclen); >+int apec_checkirq(struct apec_s *apec); >+void apec_release(struct apec_s *instance); >+ >+#endif /*_APEC_H_*/ >diff --git a/drivers/dahdi/opvxa1200/Kbuild b/drivers/dahdi/opvxa1200/Kbuild >new file mode 100644 >index 0000000..8f90819 >--- /dev/null >+++ b/drivers/dahdi/opvxa1200/Kbuild >@@ -0,0 +1,19 @@ >+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXA1200) += opvxa1200.o >+ >+EXTRA_CFLAGS += -I$(src)/.. -Wno-undef >+ >+opvxa1200-objs := base.o >+ >+DAHDI_KERNEL_H_NAME:=kernel.h >+DAHDI_KERNEL_H_PATH:=$(DAHDI_INCLUDE)/dahdi/$(DAHDI_KERNEL_H_NAME) >+ifneq ($(DAHDI_KERNEL_H_PATH),) >+ DAHDI_SPAN_MODULE:=$(shell if grep -C 5 "struct dahdi_span {" $(DAHDI_KERNEL_H_PATH) | grep -q "struct module \*owner"; then echo "yes"; else echo "no"; fi) >+ DAHDI_SPAN_OPS:=$(shell if grep -q "struct dahdi_span_ops {" $(DAHDI_KERNEL_H_PATH); then echo "yes"; else echo "no"; fi) >+ ifeq ($(DAHDI_SPAN_MODULE),yes) >+ EXTRA_CFLAGS+=-DDAHDI_SPAN_MODULE >+ else >+ ifeq ($(DAHDI_SPAN_OPS),yes) >+ EXTRA_CFLAGS+=-DDAHDI_SPAN_OPS >+ endif >+ endif >+endif >diff --git a/drivers/dahdi/opvxa1200/Makefile b/drivers/dahdi/opvxa1200/Makefile >new file mode 100644 >index 0000000..baaab35 >--- /dev/null >+++ b/drivers/dahdi/opvxa1200/Makefile >@@ -0,0 +1,8 @@ >+ifdef KBUILD_EXTMOD >+# We only get here on kernels 2.6.0-2.6.9 . >+# For newer kernels, Kbuild will be included directly by the kernel >+# build system. >+include $(src)/Kbuild >+ >+else >+endif >diff --git a/drivers/dahdi/opvxa1200/base.c b/drivers/dahdi/opvxa1200/base.c >new file mode 100644 >index 0000000..32da0e0 >--- /dev/null >+++ b/drivers/dahdi/opvxa1200/base.c >@@ -0,0 +1,3049 @@ >+/* >+ * OpenVox A1200P FXS/FXO Interface Driver for DAHDI Telephony interface >+ * >+ * Written by MiaoLin<miaolin@openvox.cn> >+ * >+ * Copyright (C) 2005-2010 OpenVox Communication Co. Ltd, >+ * >+ * All rights reserved. >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation; either version 2 of the License, or >+ * (at your option) any later version. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. >+ * >+ */ >+ >+/* Rev histroy >+ * >+ * Rev 0.10 initial version >+ * Rev 0.11 >+ * fixed the led light on/off bug. >+ * modify some wctdm print to opvxa1200 >+ * support firmware version 1.2, faster i/o operation, and better LED control. >+ * >+ * Rev 0.12 patched to support new pci id 0x8519 >+ * Rev 0.13 patched to remove the warning during compile under kernel 2.6.22 >+ * Rev 0.14 patched to remove the bug for ZAP_IRQ_SHARED , 3/9/2007 >+ * Rev 0.15 patched to support new pci ID 0X9532 by james.zhu, 23/10/2007 >+ * Rev 0.16 support new pci id 0x9559 by Miao Lin 21/3/2008 >+ * Rev 0.17 >+ * patched a few bugs, >+ * add hwgain support. >+ * fixed A800P version check >+ * Rev 1.4.9.2 >+ * Only generate 8 channels for A800P >+ * Version number synced to zaptel distribution. >+ * Rev 1.4.9.2.a >+ * Fixed freeregion. >+ * >+ * Rev 1.4.9.2.b >+ * Add cid before first ring support. >+ * New Paremeters: >+ * cidbeforering : set to 1 will cause the card enable cidbeforering function. default 0 >+ * cidbuflen : length of cid buffer, in msec, default 3000 msec. >+ * cidtimeout : time out of a ring, default 6000msec >+ * User must set cidstart=polarity in zapata.conf to use with this feature >+ * cidsignalling = signalling format send before 1st ring. most likely dtmf. >+ * >+ * Rev 1.4.9.2.c >+ * add driver parameter cidtimeout. >+ * >+ * Rev 1.4.9.2.d >+ * add debug stuff to test fxs power alarm >+ * >+ * Rev 1.4.11 >+ * Support enhanced full scale tx/rx for FXO required by europe standard (Register 30, acim) (module parm fxofullscale) >+ * >+ * Rev 1.4.12 2008/10/17 >+ * Fixed bug cause FXS module report fake power alarm. >+ * Power alarm debug stuff removed. >+ * >+ * Rev 2.0 DAHDI 2008/10/17 >+ * >+ * Rev 2.0.1 add new pci id 0x9599 >+ * Re 2.0.2 12/01/2009 >+ add fixedtimepolarity: set time(ms) when send polarity after 1st ring happen. >+ * Sometimes the dtmf cid is sent just after first ring off, and the system do not have >+ * enough time to start detect 1st dtmf. >+ * 0 means send polarity at the end of 1st ring. >+ * x means send ploarity after x ms of 1st ring begin. >+ * >+ * Rev 2.0.3 12/01/2009 >+ * Add touch_softlockup_watchdog() in wctdm_hardware_init, to avoid cpu softlockup system message for FXS. >+ * >+ * >+ * Rev 1.4.12.4 17/04/2009 James.zhu >+ * Changed wctdm_voicedaa_check_hook() to detect FXO battery and solved the problem with dial(dahdi/go/XXXXXXXXXX) >+ * add alarm detection for FXO >+ * >+ * Rev 1.4.12.5 01/10/2009 james.zhu >+ * Add jiffies for 5 second in wctdm_hardware_init >+ * >+ * >+ */ >+ >+#include <linux/kernel.h> >+#include <linux/errno.h> >+#include <linux/module.h> >+#include <linux/init.h> >+#include <linux/errno.h> >+#include <linux/pci.h> >+#include <linux/interrupt.h> >+#include <linux/moduleparam.h> >+#include <asm/io.h> >+#include <linux/sched.h> >+#include "proslic.h" >+ >+/* MiaoLin debug start */ >+#include <linux/string.h> >+#include <asm/uaccess.h> /* get_fs(), set_fs(), KERNEL_DS */ >+#include <linux/file.h> /* fput() */ >+/* MiaoLin debug end */ >+ >+ >+/* >+ * Define for audio vs. register based ring detection >+ * >+ */ >+/* #define AUDIO_RINGCHECK */ >+ >+/* >+ Experimental max loop current limit for the proslic >+ Loop current limit is from 20 mA to 41 mA in steps of 3 >+ (according to datasheet) >+ So set the value below to: >+ 0x00 : 20mA (default) >+ 0x01 : 23mA >+ 0x02 : 26mA >+ 0x03 : 29mA >+ 0x04 : 32mA >+ 0x05 : 35mA >+ 0x06 : 37mA >+ 0x07 : 41mA >+*/ >+static int loopcurrent = 20; >+ >+static int reversepolarity = 0; >+ >+static alpha indirect_regs[] = >+{ >+{0,255,"DTMF_ROW_0_PEAK",0x55C2}, >+{1,255,"DTMF_ROW_1_PEAK",0x51E6}, >+{2,255,"DTMF_ROW2_PEAK",0x4B85}, >+{3,255,"DTMF_ROW3_PEAK",0x4937}, >+{4,255,"DTMF_COL1_PEAK",0x3333}, >+{5,255,"DTMF_FWD_TWIST",0x0202}, >+{6,255,"DTMF_RVS_TWIST",0x0202}, >+{7,255,"DTMF_ROW_RATIO_TRES",0x0198}, >+{8,255,"DTMF_COL_RATIO_TRES",0x0198}, >+{9,255,"DTMF_ROW_2ND_ARM",0x0611}, >+{10,255,"DTMF_COL_2ND_ARM",0x0202}, >+{11,255,"DTMF_PWR_MIN_TRES",0x00E5}, >+{12,255,"DTMF_OT_LIM_TRES",0x0A1C}, >+{13,0,"OSC1_COEF",0x7B30}, >+{14,1,"OSC1X",0x0063}, >+{15,2,"OSC1Y",0x0000}, >+{16,3,"OSC2_COEF",0x7870}, >+{17,4,"OSC2X",0x007D}, >+{18,5,"OSC2Y",0x0000}, >+{19,6,"RING_V_OFF",0x0000}, >+{20,7,"RING_OSC",0x7EF0}, >+{21,8,"RING_X",0x0160}, >+{22,9,"RING_Y",0x0000}, >+{23,255,"PULSE_ENVEL",0x2000}, >+{24,255,"PULSE_X",0x2000}, >+{25,255,"PULSE_Y",0x0000}, >+//{26,13,"RECV_DIGITAL_GAIN",0x4000}, // playback volume set lower >+{26,13,"RECV_DIGITAL_GAIN",0x2000}, // playback volume set lower >+{27,14,"XMIT_DIGITAL_GAIN",0x4000}, >+//{27,14,"XMIT_DIGITAL_GAIN",0x2000}, >+{28,15,"LOOP_CLOSE_TRES",0x1000}, >+{29,16,"RING_TRIP_TRES",0x3600}, >+{30,17,"COMMON_MIN_TRES",0x1000}, >+{31,18,"COMMON_MAX_TRES",0x0200}, >+{32,19,"PWR_ALARM_Q1Q2",0x07C0}, >+{33,20,"PWR_ALARM_Q3Q4",0x2600}, >+{34,21,"PWR_ALARM_Q5Q6",0x1B80}, >+{35,22,"LOOP_CLOSURE_FILTER",0x8000}, >+{36,23,"RING_TRIP_FILTER",0x0320}, >+{37,24,"TERM_LP_POLE_Q1Q2",0x008C}, >+{38,25,"TERM_LP_POLE_Q3Q4",0x0100}, >+{39,26,"TERM_LP_POLE_Q5Q6",0x0010}, >+{40,27,"CM_BIAS_RINGING",0x0C00}, >+{41,64,"DCDC_MIN_V",0x0C00}, >+{42,255,"DCDC_XTRA",0x1000}, >+{43,66,"LOOP_CLOSE_TRES_LOW",0x1000}, >+}; >+ >+ >+#include <dahdi/kernel.h> >+#include <dahdi/wctdm_user.h> >+ >+#include "fxo_modes.h" >+ >+#define NUM_FXO_REGS 60 >+ >+#define WC_MAX_IFACES 128 >+ >+#define WC_OFFSET 4 /* Offset between transmit and receive, in bytes. */ >+#define WC_SYNCFLAG 0xca1ef1ac >+ >+#define WC_CNTL 0x00 >+#define WC_OPER 0x01 >+#define WC_AUXC 0x02 >+#define WC_AUXD 0x03 >+#define WC_MASK0 0x04 >+#define WC_MASK1 0x05 >+#define WC_INTSTAT 0x06 >+#define WC_AUXR 0x07 >+ >+#define WC_DMAWS 0x08 >+#define WC_DMAWI 0x0c >+#define WC_DMAWE 0x10 >+#define WC_DMARS 0x18 >+#define WC_DMARI 0x1c >+#define WC_DMARE 0x20 >+ >+#define WC_AUXFUNC 0x2b >+#define WC_SERCTL 0x2d >+#define WC_FSCDELAY 0x2f >+ >+#define WC_REGBASE 0xc0 >+ >+#define WC_VER 0x0 >+#define WC_CS 0x1 >+#define WC_SPICTRL 0x2 >+#define WC_SPIDATA 0x3 >+ >+#define BIT_SPI_BYHW (1 << 0) >+#define BIT_SPI_BUSY (1 << 1) // 0=can read/write spi, 1=spi working. >+#define BIT_SPI_START (1 << 2) >+ >+ >+#define BIT_LED_CLK (1 << 0) // MiaoLin add to control the led. >+#define BIT_LED_DATA (1 << 1) // MiaoLin add to control the led. >+ >+#define BIT_CS (1 << 2) >+#define BIT_SCLK (1 << 3) >+#define BIT_SDI (1 << 4) >+#define BIT_SDO (1 << 5) >+ >+#define FLAG_EMPTY 0 >+#define FLAG_WRITE 1 >+#define FLAG_READ 2 >+#define DEFAULT_RING_DEBOUNCE 64 /* Ringer Debounce (64 ms) */ >+#define POLARITY_DEBOUNCE 64 /* Polarity debounce (64 ms) */ >+#define OHT_TIMER 6000 /* How long after RING to retain OHT */ >+ >+#define FLAG_3215 (1 << 0) >+#define FLAG_A800 (1 << 7) >+ >+#define MAX_NUM_CARDS 12 >+#define NUM_CARDS 12 >+#define NUM_FLAG 4 /* number of flag channels. */ >+ >+ >+enum cid_hook_state { >+ CID_STATE_IDLE = 0, >+ CID_STATE_RING_ON, >+ CID_STATE_RING_OFF, >+ CID_STATE_WAIT_RING_FINISH >+}; >+ >+/* if you want to record the last 8 sec voice before the driver unload, uncomment it and rebuild. */ >+/* #define TEST_LOG_INCOME_VOICE */ >+#define voc_buffer_size (8000*8) >+ >+ >+#define MAX_ALARMS 10 >+ >+#define MOD_TYPE_FXS 0 >+#define MOD_TYPE_FXO 1 >+ >+#define MINPEGTIME 10 * 8 /* 30 ms peak to peak gets us no more than 100 Hz */ >+#define PEGTIME 50 * 8 /* 50ms peak to peak gets us rings of 10 Hz or more */ >+#define PEGCOUNT 5 /* 5 cycles of pegging means RING */ >+ >+#define NUM_CAL_REGS 12 >+ >+struct calregs { >+ unsigned char vals[NUM_CAL_REGS]; >+}; >+ >+enum proslic_power_warn { >+ PROSLIC_POWER_UNKNOWN = 0, >+ PROSLIC_POWER_ON, >+ PROSLIC_POWER_WARNED, >+}; >+ >+enum battery_state { >+ BATTERY_UNKNOWN = 0, >+ BATTERY_PRESENT, >+ BATTERY_LOST, >+}; >+struct wctdm { >+ struct pci_dev *dev; >+ char *variety; >+ struct dahdi_span span; >+ unsigned char ios; >+ int usecount; >+ unsigned int intcount; >+ int dead; >+ int pos; >+ int flags[MAX_NUM_CARDS]; >+ int freeregion; >+ int alt; >+ int curcard; >+ int cardflag; /* Bit-map of present cards */ >+ enum proslic_power_warn proslic_power; >+ spinlock_t lock; >+ >+ union { >+ struct fxo { >+#ifdef AUDIO_RINGCHECK >+ unsigned int pegtimer; >+ int pegcount; >+ int peg; >+ int ring; >+#else >+ int wasringing; >+ int lastrdtx; >+#endif >+ int ringdebounce; >+ int offhook; >+ unsigned int battdebounce; >+ unsigned int battalarm; >+ enum battery_state battery; >+ int lastpol; >+ int polarity; >+ int polaritydebounce; >+ } fxo; >+ struct fxs { >+ int oldrxhook; >+ int debouncehook; >+ int lastrxhook; >+ int debounce; >+ int ohttimer; >+ int idletxhookstate; /* IDLE changing hook state */ >+ int lasttxhook; >+ int palarms; >+ struct calregs calregs; >+ } fxs; >+ } mod[MAX_NUM_CARDS]; >+ >+ /* Receive hook state and debouncing */ >+ int modtype[MAX_NUM_CARDS]; >+ unsigned char reg0shadow[MAX_NUM_CARDS]; >+ unsigned char reg1shadow[MAX_NUM_CARDS]; >+ >+ unsigned long ioaddr; >+ unsigned long mem_region; /* 32 bit Region allocated to tiger320 */ >+ unsigned long mem_len; /* Length of 32 bit region */ >+ volatile unsigned long mem32; /* Virtual representation of 32 bit memory area */ >+ >+ dma_addr_t readdma; >+ dma_addr_t writedma; >+ volatile unsigned char *writechunk; /* Double-word aligned write memory */ >+ volatile unsigned char *readchunk; /* Double-word aligned read memory */ >+ /*struct dahdi_chan chans[MAX_NUM_CARDS];*/ >+ struct dahdi_chan _chans[NUM_CARDS]; >+ struct dahdi_chan *chans[NUM_CARDS]; >+ >+ >+#ifdef TEST_LOG_INCOME_VOICE >+ char * voc_buf[MAX_NUM_CARDS + NUM_FLAG]; >+ int voc_ptr[MAX_NUM_CARDS + NUM_FLAG]; >+#endif >+ int lastchan; >+ unsigned short ledstate; >+ unsigned char fwversion; >+ int max_cards; >+ char *card_name; >+ >+ char *cid_history_buf[MAX_NUM_CARDS]; >+ int cid_history_ptr[MAX_NUM_CARDS]; >+ int cid_history_clone_cnt[MAX_NUM_CARDS]; >+ enum cid_hook_state cid_state[MAX_NUM_CARDS]; >+ int cid_ring_on_time[MAX_NUM_CARDS]; >+}; >+ >+static char* A1200P_Name = "A1200P"; >+static char* A800P_Name = "A800P"; >+ >+struct wctdm_desc { >+ char *name; >+ int flags; >+}; >+ >+static struct wctdm_desc wctdme = { "OpenVox A1200P/A800P", 0 }; >+static int acim2tiss[16] = { 0x0, 0x1, 0x4, 0x5, 0x7, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0, 0x3 }; >+ >+static struct wctdm *ifaces[WC_MAX_IFACES]; >+ >+static void wctdm_release(struct wctdm *wc); >+ >+static unsigned int battdebounce; >+static unsigned int battalarm; >+static unsigned int battthresh; >+static int ringdebounce = DEFAULT_RING_DEBOUNCE; >+/* times 4, because must be a multiple of 4ms: */ >+static int dialdebounce = 8 * 8; >+static int fwringdetect = 0; >+static int debug = 0; >+static int robust = 0; >+static int timingonly = 0; >+static int lowpower = 0; >+static int boostringer = 0; >+static int fastringer = 0; >+static int _opermode = 0; >+static char *opermode = "FCC"; >+static int fxshonormode = 0; >+static int alawoverride = 0; >+static int fastpickup = 0; >+static int fxotxgain = 0; >+static int fxorxgain = 0; >+static int fxstxgain = 0; >+static int fxsrxgain = 0; >+/* special h/w control command */ >+static int spibyhw = 1; >+static int usememio = 1; >+static int cidbeforering = 0; >+static int cidbuflen = 3000; /* in msec, default 3000 */ >+static int cidtimeout = 6*1000; /* in msec, default 6000 */ >+static int fxofullscale = 0; /* fxo full scale tx/rx, register 30, acim */ >+static int fixedtimepolarity=0; /* time delay in ms when send polarity after rise edge of 1st ring.*/ >+ >+static int wctdm_init_proslic(struct wctdm *wc, int card, int fast , int manual, int sane); >+ >+static void wctdm_set_led(struct wctdm* wc, int card, int onoff) >+{ >+ int i; >+ unsigned char c; >+ >+ wc->ledstate &= ~(0x01<<card); >+ wc->ledstate |= (onoff<<card); >+ c = (inb(wc->ioaddr + WC_AUXD)&~BIT_LED_CLK)|BIT_LED_DATA; >+ outb( c, wc->ioaddr + WC_AUXD); >+ for(i=MAX_NUM_CARDS-1; i>=0; i--) >+ { >+ if(wc->ledstate & (0x0001<<i)) >+ if(wc->fwversion == 0x11) >+ c &= ~BIT_LED_DATA; >+ else >+ c |= BIT_LED_DATA; >+ else >+ if(wc->fwversion == 0x11) >+ c |= BIT_LED_DATA; >+ else >+ c &= ~BIT_LED_DATA; >+ >+ outb( c, wc->ioaddr + WC_AUXD); >+ outb( c|BIT_LED_CLK, wc->ioaddr + WC_AUXD); >+ outb( (c&~BIT_LED_CLK)|BIT_LED_DATA, wc->ioaddr + WC_AUXD); >+ } >+} >+ >+ >+static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char ints) >+{ >+ int x, y, chan_offset, pos; >+ volatile unsigned char *txbuf; >+ >+ if (ints & /*0x01*/ 0x04) >+ /* Write is at interrupt address. Start writing from normal offset */ >+ txbuf = wc->writechunk; >+ else >+ txbuf = wc->writechunk + DAHDI_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG); >+ >+ /* Calculate Transmission */ >+ dahdi_transmit(&wc->span); >+ >+ if(wc->lastchan == -1) // not in sync. >+ return; >+ >+ chan_offset = (wc->lastchan*4 + 4 ) % (MAX_NUM_CARDS+NUM_FLAG); >+ >+ for (y=0;y<DAHDI_CHUNKSIZE;y++) { >+#ifdef __BIG_ENDIAN >+ // operation pending... >+#else >+ for (x=0;x<(MAX_NUM_CARDS+NUM_FLAG);x++) { >+ pos = y * (MAX_NUM_CARDS+NUM_FLAG) + ((x + chan_offset + MAX_NUM_CARDS+NUM_FLAG - WC_OFFSET)&0x0f); >+ if(x<wc->max_cards/*MAX_NUM_CARDS*/) >+ txbuf[pos] = wc->chans[x]->writechunk[y]; >+ else >+ txbuf[pos] = 0; >+ } >+#endif >+ } >+} >+ >+ >+#ifdef AUDIO_RINGCHECK >+static inline void ring_check(struct wctdm *wc, int card) >+{ >+ int x; >+ short sample; >+ if (wc->modtype[card] != MOD_TYPE_FXO) >+ return; >+ wc->mod[card].fxo.pegtimer += DAHDI_CHUNKSIZE; >+ for (x=0;x<DAHDI_CHUNKSIZE;x++) { >+ /* Look for pegging to indicate ringing */ >+ sample = DAHDI_XLAW(wc->chans[card].readchunk[x], (&(wc->chans[card]))); >+ if ((sample > 10000) && (wc->mod[card].fxo.peg != 1)) { >+ if (debug > 1) printk(KERN_DEBUG "High peg!\n"); >+ if ((wc->mod[card].fxo.pegtimer < PEGTIME) && (wc->mod[card].fxo.pegtimer > MINPEGTIME)) >+ wc->mod[card].fxo.pegcount++; >+ wc->mod[card].fxo.pegtimer = 0; >+ wc->mod[card].fxo.peg = 1; >+ } else if ((sample < -10000) && (wc->mod[card].fxo.peg != -1)) { >+ if (debug > 1) printk(KERN_DEBUG "Low peg!\n"); >+ if ((wc->mod[card].fxo.pegtimer < (PEGTIME >> 2)) && (wc->mod[card].fxo.pegtimer > (MINPEGTIME >> 2))) >+ wc->mod[card].fxo.pegcount++; >+ wc->mod[card].fxo.pegtimer = 0; >+ wc->mod[card].fxo.peg = -1; >+ } >+ } >+ if (wc->mod[card].fxo.pegtimer > PEGTIME) { >+ /* Reset pegcount if our timer expires */ >+ wc->mod[card].fxo.pegcount = 0; >+ } >+ /* Decrement debouncer if appropriate */ >+ if (wc->mod[card].fxo.ringdebounce) >+ wc->mod[card].fxo.ringdebounce--; >+ if (!wc->mod[card].fxo.offhook && !wc->mod[card].fxo.ringdebounce) { >+ if (!wc->mod[card].fxo.ring && (wc->mod[card].fxo.pegcount > PEGCOUNT)) { >+ /* It's ringing */ >+ if (debug) >+ printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); >+ if (!wc->mod[card].fxo.offhook) >+ dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_RING); >+ wc->mod[card].fxo.ring = 1; >+ } >+ if (wc->mod[card].fxo.ring && !wc->mod[card].fxo.pegcount) { >+ /* No more ring */ >+ if (debug) >+ printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); >+ dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ wc->mod[card].fxo.ring = 0; >+ } >+ } >+} >+#endif >+ >+ >+static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char ints) >+{ >+ volatile unsigned char *rxbuf; >+ int x, y, chan_offset; >+ >+ >+ if (ints & 0x08/*0x04*/) >+ /* Read is at interrupt address. Valid data is available at normal offset */ >+ rxbuf = wc->readchunk; >+ else >+ rxbuf = wc->readchunk + DAHDI_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG); >+ >+ for(x=0; x<4; x++) >+ if( *(int*)(rxbuf+x*4) == WC_SYNCFLAG) >+ break; >+ if(x==4) >+ { >+ printk("buffer sync misseed!\n"); >+ wc->lastchan = -1; >+ return; >+ } >+ else if(wc->lastchan != x) >+ { >+ printk("buffer re-sync occur from %d to %d\n", wc->lastchan, x); >+ wc->lastchan = x; >+ } >+ chan_offset = (wc->lastchan*4 + 4 ) % (MAX_NUM_CARDS+NUM_FLAG); >+ >+ for (x=0;x<DAHDI_CHUNKSIZE;x++) { >+#ifdef __BIG_ENDIAN >+ // operation pending... >+#else >+ for (y=0;y<wc->max_cards/*MAX_NUM_CARDS*/;y++) { >+ if (wc->cardflag & (1 << y)) >+ wc->chans[y]->readchunk[x] = rxbuf[(MAX_NUM_CARDS+NUM_FLAG) * x + ((y + chan_offset ) & 0x0f)]; >+#ifdef TEST_LOG_INCOME_VOICE >+ wc->voc_buf[y][wc->voc_ptr[y]] = rxbuf[(MAX_NUM_CARDS+NUM_FLAG) * x + ((y + chan_offset) & 0x0f)]; >+ wc->voc_ptr[y]++; >+ if(wc->voc_ptr[y] >= voc_buffer_size) >+ wc->voc_ptr[y] = 0; >+#endif >+ } >+#endif >+ } >+ >+ if(cidbeforering) >+ { >+ for(x=0; x<wc->max_cards; x++) >+ { >+ if (wc->modtype[wc->chans[x]->chanpos - 1] == MOD_TYPE_FXO) >+ if(wc->mod[wc->chans[x]->chanpos - 1].fxo.offhook == 0) >+ { >+ /*unsigned int *p_readchunk, *p_cid_history; >+ >+ p_readchunk = (unsigned int*)wc->chans[x].readchunk; >+ p_cid_history = (unsigned int*)(wc->cid_history_buf[x] + wc->cid_history_ptr[x]);*/ >+ >+ if(wc->cid_state[x] == CID_STATE_IDLE) /* we need copy data to the cid voice buffer */ >+ { >+ memcpy(wc->cid_history_buf[x] + wc->cid_history_ptr[x], wc->chans[x]->readchunk, DAHDI_CHUNKSIZE); >+ wc->cid_history_ptr[x] = (wc->cid_history_ptr[x] + DAHDI_CHUNKSIZE)%(cidbuflen * DAHDI_MAX_CHUNKSIZE); >+ } >+ else if (wc->cid_state[x] == CID_STATE_RING_ON) >+ wc->cid_history_clone_cnt[x] = cidbuflen; >+ else if (wc->cid_state[x] == CID_STATE_RING_OFF) >+ { >+ if(wc->cid_history_clone_cnt[x]) >+ { >+ memcpy(wc->chans[x]->readchunk, wc->cid_history_buf[x] + wc->cid_history_ptr[x], DAHDI_MAX_CHUNKSIZE); >+ wc->cid_history_clone_cnt[x]--; >+ wc->cid_history_ptr[x] = (wc->cid_history_ptr[x] + DAHDI_MAX_CHUNKSIZE)%(cidbuflen * DAHDI_MAX_CHUNKSIZE); >+ } >+ else >+ { >+ wc->cid_state[x] = CID_STATE_WAIT_RING_FINISH; >+ wc->cid_history_clone_cnt[x] = cidtimeout; /* wait 6 sec, if no ring, return to idle */ >+ } >+ } >+ else if(wc->cid_state[x] == CID_STATE_WAIT_RING_FINISH) >+ { >+ if(wc->cid_history_clone_cnt[x] > 0) >+ wc->cid_history_clone_cnt[x]--; >+ else >+ { >+ wc->cid_state[x] = CID_STATE_IDLE; >+ wc->cid_history_ptr[x] = 0; >+ wc->cid_history_clone_cnt[x] = 0; >+ } >+ } >+ } >+ } >+ } >+ >+#ifdef AUDIO_RINGCHECK >+ for (x=0;x<wc->max_cards;x++) >+ ring_check(wc, x); >+#endif >+ /* XXX We're wasting 8 taps. We should get closer :( */ >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { >+ if (wc->cardflag & (1 << x)) >+ dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk); >+ } >+ dahdi_receive(&wc->span); >+} >+ >+static void wctdm_stop_dma(struct wctdm *wc); >+static void wctdm_reset_tdm(struct wctdm *wc); >+static void wctdm_restart_dma(struct wctdm *wc); >+ >+ >+static unsigned char __wctdm_getcreg(struct wctdm *wc, unsigned char reg); >+static void __wctdm_setcreg(struct wctdm *wc, unsigned char reg, unsigned char val); >+ >+ >+static inline void __write_8bits(struct wctdm *wc, unsigned char bits) >+{ >+ if(spibyhw == 0) >+ { >+ int x; >+ /* Drop chip select */ >+ wc->ios |= BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ wc->ios &= ~BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ for (x=0;x<8;x++) { >+ /* Send out each bit, MSB first, drop SCLK as we do so */ >+ if (bits & 0x80) >+ wc->ios |= BIT_SDI; >+ else >+ wc->ios &= ~BIT_SDI; >+ wc->ios &= ~BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Now raise SCLK high again and repeat */ >+ wc->ios |= BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ bits <<= 1; >+ } >+ /* Finally raise CS back high again */ >+ wc->ios |= BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ } >+ else >+ { >+ __wctdm_setcreg(wc, WC_SPIDATA, bits); >+ __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW | BIT_SPI_START); >+ while ((__wctdm_getcreg(wc, WC_SPICTRL) & BIT_SPI_BUSY) != 0); >+ __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); >+ } >+} >+ >+ >+static inline void __reset_spi(struct wctdm *wc) >+{ >+ __wctdm_setcreg(wc, WC_SPICTRL, 0); >+ >+ /* Drop chip select and clock once and raise and clock once */ >+ wc->ios |= BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ wc->ios &= ~BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ wc->ios |= BIT_SDI; >+ wc->ios &= ~BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Now raise SCLK high again and repeat */ >+ wc->ios |= BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Finally raise CS back high again */ >+ wc->ios |= BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Clock again */ >+ wc->ios &= ~BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Now raise SCLK high again and repeat */ >+ wc->ios |= BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ >+ __wctdm_setcreg(wc, WC_SPICTRL, spibyhw); >+ >+} >+ >+static inline unsigned char __read_8bits(struct wctdm *wc) >+{ >+ unsigned char res=0, c; >+ int x; >+ if(spibyhw == 0) >+ { >+ wc->ios &= ~BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Drop chip select */ >+ wc->ios &= ~BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ for (x=0;x<8;x++) { >+ res <<= 1; >+ /* Get SCLK */ >+ wc->ios &= ~BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ /* Read back the value */ >+ c = inb(wc->ioaddr + WC_AUXR); >+ if (c & BIT_SDO) >+ res |= 1; >+ /* Now raise SCLK high again */ >+ wc->ios |= BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ } >+ /* Finally raise CS back high again */ >+ wc->ios |= BIT_CS; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ wc->ios &= ~BIT_SCLK; >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ } >+ else >+ { >+ __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW | BIT_SPI_START); >+ while ((__wctdm_getcreg(wc, WC_SPICTRL) & BIT_SPI_BUSY) != 0); >+ res = __wctdm_getcreg(wc, WC_SPIDATA); >+ __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); >+ } >+ >+ /* And return our result */ >+ return res; >+} >+ >+static void __wctdm_setcreg_mem(struct wctdm *wc, unsigned char reg, unsigned char val) >+{ >+ unsigned int *p = (unsigned int*)(wc->mem32 + WC_REGBASE + ((reg & 0xf) << 2)); >+ *p = val; >+} >+ >+static unsigned char __wctdm_getcreg_mem(struct wctdm *wc, unsigned char reg) >+{ >+ unsigned int *p = (unsigned int*)(wc->mem32 + WC_REGBASE + ((reg & 0xf) << 2)); >+ return (*p)&0x00ff; >+} >+ >+ >+static void __wctdm_setcreg(struct wctdm *wc, unsigned char reg, unsigned char val) >+{ >+ if(usememio) >+ __wctdm_setcreg_mem(wc, reg, val); >+ else >+ outb(val, wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); >+} >+ >+static unsigned char __wctdm_getcreg(struct wctdm *wc, unsigned char reg) >+{ >+ if(usememio) >+ return __wctdm_getcreg_mem(wc, reg); >+ else >+ return inb(wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2)); >+} >+ >+static inline void __wctdm_setcard(struct wctdm *wc, int card) >+{ >+ if (wc->curcard != card) { >+ __wctdm_setcreg(wc, WC_CS, card); >+ wc->curcard = card; >+ //printk("Select card %d\n", card); >+ } >+} >+ >+static void __wctdm_setreg(struct wctdm *wc, int card, unsigned char reg, unsigned char value) >+{ >+ __wctdm_setcard(wc, card); >+ if (wc->modtype[card] == MOD_TYPE_FXO) { >+ __write_8bits(wc, 0x20); >+ __write_8bits(wc, reg & 0x7f); >+ } else { >+ __write_8bits(wc, reg & 0x7f); >+ } >+ __write_8bits(wc, value); >+} >+ >+static void wctdm_setreg(struct wctdm *wc, int card, unsigned char reg, unsigned char value) >+{ >+ unsigned long flags; >+ spin_lock_irqsave(&wc->lock, flags); >+ __wctdm_setreg(wc, card, reg, value); >+ spin_unlock_irqrestore(&wc->lock, flags); >+} >+ >+static unsigned char __wctdm_getreg(struct wctdm *wc, int card, unsigned char reg) >+{ >+ __wctdm_setcard(wc, card); >+ if (wc->modtype[card] == MOD_TYPE_FXO) { >+ __write_8bits(wc, 0x60); >+ __write_8bits(wc, reg & 0x7f); >+ } else { >+ __write_8bits(wc, reg | 0x80); >+ } >+ return __read_8bits(wc); >+} >+ >+static inline void reset_spi(struct wctdm *wc, int card) >+{ >+ unsigned long flags; >+ spin_lock_irqsave(&wc->lock, flags); >+ __wctdm_setcard(wc, card); >+ __reset_spi(wc); >+ __reset_spi(wc); >+ spin_unlock_irqrestore(&wc->lock, flags); >+} >+ >+static unsigned char wctdm_getreg(struct wctdm *wc, int card, unsigned char reg) >+{ >+ unsigned long flags; >+ unsigned char res; >+ spin_lock_irqsave(&wc->lock, flags); >+ res = __wctdm_getreg(wc, card, reg); >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return res; >+} >+ >+static int __wait_access(struct wctdm *wc, int card) >+{ >+ unsigned char data = 0; >+ long origjiffies; >+ int count = 0; >+ >+ #define MAX 6000 /* attempts */ >+ >+ >+ origjiffies = jiffies; >+ /* Wait for indirect access */ >+ while (count++ < MAX) >+ { >+ data = __wctdm_getreg(wc, card, I_STATUS); >+ >+ if (!data) >+ return 0; >+ >+ } >+ >+ if(count > (MAX-1)) printk(KERN_NOTICE " ##### Loop error (%02x) #####\n", data); >+ >+ return 0; >+} >+ >+static unsigned char translate_3215(unsigned char address) >+{ >+ int x; >+ for (x=0;x<sizeof(indirect_regs)/sizeof(indirect_regs[0]);x++) { >+ if (indirect_regs[x].address == address) { >+ address = indirect_regs[x].altaddr; >+ break; >+ } >+ } >+ return address; >+} >+ >+static int wctdm_proslic_setreg_indirect(struct wctdm *wc, int card, unsigned char address, unsigned short data) >+{ >+ unsigned long flags; >+ int res = -1; >+ /* Translate 3215 addresses */ >+ if (wc->flags[card] & FLAG_3215) { >+ address = translate_3215(address); >+ if (address == 255) >+ return 0; >+ } >+ spin_lock_irqsave(&wc->lock, flags); >+ if(!__wait_access(wc, card)) { >+ __wctdm_setreg(wc, card, IDA_LO,(unsigned char)(data & 0xFF)); >+ __wctdm_setreg(wc, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8)); >+ __wctdm_setreg(wc, card, IAA,address); >+ res = 0; >+ }; >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return res; >+} >+ >+static int wctdm_proslic_getreg_indirect(struct wctdm *wc, int card, unsigned char address) >+{ >+ unsigned long flags; >+ int res = -1; >+ char *p=NULL; >+ /* Translate 3215 addresses */ >+ if (wc->flags[card] & FLAG_3215) { >+ address = translate_3215(address); >+ if (address == 255) >+ return 0; >+ } >+ spin_lock_irqsave(&wc->lock, flags); >+ if (!__wait_access(wc, card)) { >+ __wctdm_setreg(wc, card, IAA, address); >+ if (!__wait_access(wc, card)) { >+ unsigned char data1, data2; >+ data1 = __wctdm_getreg(wc, card, IDA_LO); >+ data2 = __wctdm_getreg(wc, card, IDA_HI); >+ res = data1 | (data2 << 8); >+ } else >+ p = "Failed to wait inside\n"; >+ } else >+ p = "failed to wait\n"; >+ spin_unlock_irqrestore(&wc->lock, flags); >+ if (p) >+ printk(KERN_NOTICE "%s", p); >+ return res; >+} >+ >+static int wctdm_proslic_init_indirect_regs(struct wctdm *wc, int card) >+{ >+ unsigned char i; >+ >+ for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++) >+ { >+ if(wctdm_proslic_setreg_indirect(wc, card, indirect_regs[i].address,indirect_regs[i].initial)) >+ return -1; >+ } >+ >+ return 0; >+} >+ >+static int wctdm_proslic_verify_indirect_regs(struct wctdm *wc, int card) >+{ >+ int passed = 1; >+ unsigned short i, initial; >+ int j; >+ >+ for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++) >+ { >+ if((j = wctdm_proslic_getreg_indirect(wc, card, (unsigned char) indirect_regs[i].address)) < 0) { >+ printk(KERN_NOTICE "Failed to read indirect register %d\n", i); >+ return -1; >+ } >+ initial= indirect_regs[i].initial; >+ >+ if ( j != initial && (!(wc->flags[card] & FLAG_3215) || (indirect_regs[i].altaddr != 255))) >+ { >+ printk(KERN_NOTICE "!!!!!!! %s iREG %X = %X should be %X\n", >+ indirect_regs[i].name,indirect_regs[i].address,j,initial ); >+ passed = 0; >+ } >+ } >+ >+ if (passed) { >+ if (debug) >+ printk(KERN_DEBUG "Init Indirect Registers completed successfully.\n"); >+ } else { >+ printk(KERN_NOTICE " !!!!! Init Indirect Registers UNSUCCESSFULLY.\n"); >+ return -1; >+ } >+ return 0; >+} >+ >+static inline void wctdm_proslic_recheck_sanity(struct wctdm *wc, int card) >+{ >+ int res; >+ /* Check loopback */ >+ res = wc->reg1shadow[card]; >+ >+ if (!res && (res != wc->mod[card].fxs.lasttxhook)) // read real state from register By wx >+ res=wctdm_getreg(wc, card, 64); >+ >+ if (!res && (res != wc->mod[card].fxs.lasttxhook)) { >+ res = wctdm_getreg(wc, card, 8); >+ if (res) { >+ printk(KERN_NOTICE "Ouch, part reset, quickly restoring reality (%d)\n", card); >+ wctdm_init_proslic(wc, card, 1, 0, 1); >+ } else { >+ if (wc->mod[card].fxs.palarms++ < MAX_ALARMS) { >+ printk(KERN_NOTICE "Power alarm on module %d, resetting!\n", card + 1); >+ if (wc->mod[card].fxs.lasttxhook == 4) >+ wc->mod[card].fxs.lasttxhook = 1; >+ wctdm_setreg(wc, card, 64, wc->mod[card].fxs.lasttxhook); >+ } else { >+ if (wc->mod[card].fxs.palarms == MAX_ALARMS) >+ printk(KERN_NOTICE "Too many power alarms on card %d, NOT resetting!\n", card + 1); >+ } >+ } >+ } >+} >+static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) >+{ >+#define MS_PER_CHECK_HOOK 16 >+ >+#ifndef AUDIO_RINGCHECK >+ unsigned char res; >+#endif >+ signed char b; >+ int errors = 0; >+ struct fxo *fxo = &wc->mod[card].fxo; >+ >+ /* Try to track issues that plague slot one FXO's */ >+ b = wc->reg0shadow[card]; >+ if ((b & 0x2) || !(b & 0x8)) { >+ /* Not good -- don't look at anything else */ >+ if (debug) >+ printk(KERN_DEBUG "Error (%02x) on card %d!\n", b, card + 1); >+ errors++; >+ } >+ b &= 0x9b; >+ if (fxo->offhook) { >+ if (b != 0x9) >+ wctdm_setreg(wc, card, 5, 0x9); >+ } else { >+ if (b != 0x8) >+ wctdm_setreg(wc, card, 5, 0x8); >+ } >+ if (errors) >+ return; >+ if (!fxo->offhook) { >+ if(fixedtimepolarity) { >+ if ( wc->cid_state[card] == CID_STATE_RING_ON && wc->cid_ring_on_time[card]>0) >+ { >+ if(wc->cid_ring_on_time[card]>=fixedtimepolarity ) >+ { >+ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); >+ wc->cid_ring_on_time[card] = -1; /* the polarity already sent */ >+ } >+ else >+ wc->cid_ring_on_time[card] += 16; >+ } >+} >+ if (fwringdetect) { >+ res = wc->reg0shadow[card] & 0x60; >+ if (fxo->ringdebounce) { >+ --fxo->ringdebounce; >+ if (res && (res != fxo->lastrdtx) && >+ (fxo->battery == BATTERY_PRESENT)) { >+ if (!fxo->wasringing) { >+ fxo->wasringing = 1; >+ if (debug) >+ printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); >+ if(cidbeforering) >+ { >+ if(wc->cid_state[card] == CID_STATE_IDLE) >+ { >+ wc->cid_state[card] = CID_STATE_RING_ON; >+ wc->cid_ring_on_time[card] = 16; /* check every 16ms */ >+ } >+ else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); >+ } >+ else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); >+ } >+ fxo->lastrdtx = res; >+ fxo->ringdebounce = 10; >+ } else if (!res) { >+ if ((fxo->ringdebounce == 0) && fxo->wasringing) { >+ fxo->wasringing = 0; >+ if (debug) >+ printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); >+ if(cidbeforering) >+ { >+ if(wc->cid_state[card] == CID_STATE_RING_ON) >+ { >+ if(fixedtimepolarity==0) >+ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); >+ wc->cid_state[card] = CID_STATE_RING_OFF; >+ } >+ else >+ { >+ if(wc->cid_state[card] == CID_STATE_WAIT_RING_FINISH) >+ wc->cid_history_clone_cnt[card] = cidtimeout; >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ } >+ } >+ else >+ >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ } >+ } >+ } else if (res && (fxo->battery == BATTERY_PRESENT)) { >+ fxo->lastrdtx = res; >+ fxo->ringdebounce = 10; >+ } >+ } else { >+ res = wc->reg0shadow[card]; >+ if ((res & 0x60) && (fxo->battery == BATTERY_PRESENT)) { >+ fxo->ringdebounce += (DAHDI_CHUNKSIZE * 16); >+ if (fxo->ringdebounce >= DAHDI_CHUNKSIZE * ringdebounce) { >+ if (!fxo->wasringing) { >+ fxo->wasringing = 1; >+ if(cidbeforering) >+ { >+ if(wc->cid_state[card] == CID_STATE_IDLE) >+ { >+ wc->cid_state[card] = CID_STATE_RING_ON; >+ wc->cid_ring_on_time[card] = 16; /* check every 16ms */ >+ } >+ else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); >+ } >+ else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); >+ if (debug) >+ printk(KERN_DEBUG "RING on %d/%d!\n", wc->span.spanno, card + 1); >+ } >+ fxo->ringdebounce = DAHDI_CHUNKSIZE * ringdebounce; >+ } >+ } else { >+ fxo->ringdebounce -= DAHDI_CHUNKSIZE * 4; >+ if (fxo->ringdebounce <= 0) { >+ if (fxo->wasringing) { >+ fxo->wasringing = 0; >+ if(cidbeforering) >+ { >+ if(wc->cid_state[card] == CID_STATE_RING_ON) >+ { >+ if(fixedtimepolarity==0) >+ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); >+ wc->cid_state[card] = CID_STATE_RING_OFF; >+ } >+ else >+ { >+ if(wc->cid_state[card] == CID_STATE_WAIT_RING_FINISH) >+ wc->cid_history_clone_cnt[card] = cidtimeout; >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ } >+ } >+ else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ if (debug) >+ printk(KERN_DEBUG "NO RING on %d/%d!\n", wc->span.spanno, card + 1); >+ } >+ fxo->ringdebounce = 0; >+ } >+ } >+ } >+ } >+ >+ b = wc->reg1shadow[card]; >+ if (abs(b) < battthresh) { >+ /* possible existing states: >+ battery lost, no debounce timer >+ battery lost, debounce timer (going to battery present) >+ battery present or unknown, no debounce timer >+ battery present or unknown, debounce timer (going to battery lost) >+ */ >+ >+ if (fxo->battery == BATTERY_LOST) { >+ if (fxo->battdebounce) { >+ /* we were going to BATTERY_PRESENT, but battery was lost again, >+ so clear the debounce timer */ >+ fxo->battdebounce = 0; >+ } >+ } else { >+ if (fxo->battdebounce) { >+ /* going to BATTERY_LOST, see if we are there yet */ >+ if (--fxo->battdebounce == 0) { >+ fxo->battery = BATTERY_LOST; >+ if (debug) >+ printk(KERN_DEBUG "NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1); >+#ifdef JAPAN >+ if (!wc->ohdebounce && wc->offhook) { >+ dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_ONHOOK); >+ if (debug) >+ printk(KERN_DEBUG "Signalled On Hook\n"); >+#ifdef ZERO_BATT_RING >+ wc->onhook++; >+#endif >+ } >+#else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); >+ /* set the alarm timer, taking into account that part of its time >+ period has already passed while debouncing occurred */ >+ fxo->battalarm = (battalarm - battdebounce) / MS_PER_CHECK_HOOK; >+#endif >+ } >+ } else { >+ /* start the debounce timer to verify that battery has been lost */ >+ fxo->battdebounce = battdebounce / MS_PER_CHECK_HOOK; >+ } >+ } >+ } else { >+ /* possible existing states: >+ battery lost or unknown, no debounce timer >+ battery lost or unknown, debounce timer (going to battery present) >+ battery present, no debounce timer >+ battery present, debounce timer (going to battery lost) >+ */ >+ >+ if (fxo->battery == BATTERY_PRESENT) { >+ if (fxo->battdebounce) { >+ /* we were going to BATTERY_LOST, but battery appeared again, >+ so clear the debounce timer */ >+ fxo->battdebounce = 0; >+ } >+ } else { >+ if (fxo->battdebounce) { >+ /* going to BATTERY_PRESENT, see if we are there yet */ >+ if (--fxo->battdebounce == 0) { >+ fxo->battery = BATTERY_PRESENT; >+ if (debug) >+ printk(KERN_DEBUG "BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1, >+ (b < 0) ? "-" : "+"); >+#ifdef ZERO_BATT_RING >+ if (wc->onhook) { >+ wc->onhook = 0; >+ dahdi_hooksig(&wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ if (debug) >+ printk(KERN_DEBUG "Signalled Off Hook\n"); >+ } >+#else >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+#endif >+ /* set the alarm timer, taking into account that part of its time >+ period has already passed while debouncing occurred */ >+ fxo->battalarm = (battalarm - battdebounce) / MS_PER_CHECK_HOOK; >+ } >+ } else { >+ /* start the debounce timer to verify that battery has appeared */ >+ fxo->battdebounce = battdebounce / MS_PER_CHECK_HOOK; >+ } >+ } >+ } >+ >+ if (fxo->lastpol >= 0) { >+ if (b < 0) { >+ fxo->lastpol = -1; >+ fxo->polaritydebounce = POLARITY_DEBOUNCE / MS_PER_CHECK_HOOK; >+ } >+ } >+ if (fxo->lastpol <= 0) { >+ if (b > 0) { >+ fxo->lastpol = 1; >+ fxo->polaritydebounce = POLARITY_DEBOUNCE / MS_PER_CHECK_HOOK; >+ } >+ } >+ >+ if (fxo->battalarm) { >+ if (--fxo->battalarm == 0) { >+ /* the alarm timer has expired, so update the battery alarm state >+ for this channel */ >+ dahdi_alarm_channel(wc->chans[card], fxo->battery == BATTERY_LOST ? DAHDI_ALARM_RED : DAHDI_ALARM_NONE); >+ } >+ } >+ >+ if (fxo->polaritydebounce) { >+ if (--fxo->polaritydebounce == 0) { >+ if (fxo->lastpol != fxo->polarity) { >+ if (debug) >+ printk(KERN_DEBUG "%lu Polarity reversed (%d -> %d)\n", jiffies, >+ fxo->polarity, >+ fxo->lastpol); >+ if (fxo->polarity) >+ dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); >+ fxo->polarity = fxo->lastpol; >+ } >+ } >+ } >+#undef MS_PER_CHECK_HOOK >+} >+ >+static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card) >+{ >+ char res; >+ int hook; >+ >+ /* For some reason we have to debounce the >+ hook detector. */ >+ >+ res = wc->reg0shadow[card]; >+ hook = (res & 1); >+ if (hook != wc->mod[card].fxs.lastrxhook) { >+ /* Reset the debounce (must be multiple of 4ms) */ >+ wc->mod[card].fxs.debounce = dialdebounce * 4; >+ >+#if 0 >+ printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n", card, hook, wc->mod[card].fxs.debounce); >+#endif >+ } else { >+ if (wc->mod[card].fxs.debounce > 0) { >+ wc->mod[card].fxs.debounce-= 16 * DAHDI_CHUNKSIZE; >+#if 0 >+ printk(KERN_DEBUG "Sustaining hook %d, %d\n", hook, wc->mod[card].fxs.debounce); >+#endif >+ if (!wc->mod[card].fxs.debounce) { >+#if 0 >+ printk(KERN_DEBUG "Counted down debounce, newhook: %d...\n", hook); >+#endif >+ wc->mod[card].fxs.debouncehook = hook; >+ } >+ if (!wc->mod[card].fxs.oldrxhook && wc->mod[card].fxs.debouncehook) { >+ /* Off hook */ >+#if 1 >+ if (debug) >+#endif >+ printk(KERN_DEBUG "opvxa1200: Card %d Going off hook\n", card); >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); >+ if (robust) >+ wctdm_init_proslic(wc, card, 1, 0, 1); >+ wc->mod[card].fxs.oldrxhook = 1; >+ >+ } else if (wc->mod[card].fxs.oldrxhook && !wc->mod[card].fxs.debouncehook) { >+ /* On hook */ >+#if 1 >+ if (debug) >+#endif >+ printk(KERN_DEBUG "opvxa1200: Card %d Going on hook\n", card); >+ dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); >+ wc->mod[card].fxs.oldrxhook = 0; >+ } >+ } >+ } >+ wc->mod[card].fxs.lastrxhook = hook; >+} >+ >+DAHDI_IRQ_HANDLER(wctdm_interrupt) >+{ >+ struct wctdm *wc = dev_id; >+ unsigned char ints; >+ int x, y, z; >+ int mode; >+ >+ ints = inb(wc->ioaddr + WC_INTSTAT); >+ >+ if (!ints) >+ return IRQ_NONE; >+ >+ outb(ints, wc->ioaddr + WC_INTSTAT); >+ >+ if (ints & 0x10) { >+ /* Stop DMA, wait for watchdog */ >+ printk(KERN_INFO "TDM PCI Master abort\n"); >+ wctdm_stop_dma(wc); >+ return IRQ_RETVAL(1); >+ } >+ >+ if (ints & 0x20) { >+ printk(KERN_INFO "PCI Target abort\n"); >+ return IRQ_RETVAL(1); >+ } >+ >+ for (x=0;x<wc->max_cards/*4*3*/;x++) { >+ if (wc->cardflag & (1 << x) && >+ (wc->modtype[x] == MOD_TYPE_FXS)) { >+ if (wc->mod[x].fxs.lasttxhook == 0x4) { >+ /* RINGing, prepare for OHT */ >+ wc->mod[x].fxs.ohttimer = OHT_TIMER << 3; >+ if (reversepolarity) >+ wc->mod[x].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ >+ else >+ wc->mod[x].fxs.idletxhookstate = 0x2; >+ } else { >+ if (wc->mod[x].fxs.ohttimer) { >+ wc->mod[x].fxs.ohttimer-= DAHDI_CHUNKSIZE; >+ if (!wc->mod[x].fxs.ohttimer) { >+ if (reversepolarity) >+ wc->mod[x].fxs.idletxhookstate = 0x5; /* Switch to active */ >+ else >+ wc->mod[x].fxs.idletxhookstate = 0x1; >+ if ((wc->mod[x].fxs.lasttxhook == 0x2) || (wc->mod[x].fxs.lasttxhook == 0x6)) { >+ /* Apply the change if appropriate */ >+ if (reversepolarity) >+ wc->mod[x].fxs.lasttxhook = 0x5; >+ else >+ wc->mod[x].fxs.lasttxhook = 0x1; >+ wctdm_setreg(wc, x, 64, wc->mod[x].fxs.lasttxhook); >+ } >+ } >+ } >+ } >+ } >+ } >+ >+ if (ints & 0x0f) { >+ wc->intcount++; >+ z = wc->intcount & 0x3; >+ mode = wc->intcount & 0xc; >+ for(y=0; y<wc->max_cards/4/*3*/; y++) >+ { >+ x = z + y*4; >+ if (wc->cardflag & (1 << x ) ) >+ { >+ switch(mode) >+ { >+ case 0: >+ /* Rest */ >+ break; >+ case 4: >+ /* Read first shadow reg */ >+ if (wc->modtype[x] == MOD_TYPE_FXS) >+ wc->reg0shadow[x] = wctdm_getreg(wc, x, 68); >+ else if (wc->modtype[x] == MOD_TYPE_FXO) >+ wc->reg0shadow[x] = wctdm_getreg(wc, x, 5); >+ break; >+ case 8: >+ /* Read second shadow reg */ >+ if (wc->modtype[x] == MOD_TYPE_FXS) >+ wc->reg1shadow[x] = wctdm_getreg(wc, x, 64); >+ else if (wc->modtype[x] == MOD_TYPE_FXO) >+ wc->reg1shadow[x] = wctdm_getreg(wc, x, 29); >+ break; >+ case 12: >+ /* Perform processing */ >+ if (wc->modtype[x] == MOD_TYPE_FXS) { >+ wctdm_proslic_check_hook(wc, x); >+ if (!(wc->intcount & 0xf0)) >+ wctdm_proslic_recheck_sanity(wc, x); >+ } else if (wc->modtype[x] == MOD_TYPE_FXO) { >+ wctdm_voicedaa_check_hook(wc, x); >+ } >+ break; >+ } >+ } >+ } >+ if (!(wc->intcount % 10000)) { >+ /* Accept an alarm once per 10 seconds */ >+ for (x=0;x<wc->max_cards/*4*3*/;x++) >+ if (wc->modtype[x] == MOD_TYPE_FXS) { >+ if (wc->mod[x].fxs.palarms) >+ wc->mod[x].fxs.palarms--; >+ } >+ } >+ wctdm_receiveprep(wc, ints); >+ wctdm_transmitprep(wc, ints); >+ } >+ >+ return IRQ_RETVAL(1); >+ >+} >+ >+static int wctdm_voicedaa_insane(struct wctdm *wc, int card) >+{ >+ int blah; >+ blah = wctdm_getreg(wc, card, 2); >+ if (blah != 0x3) >+ return -2; >+ blah = wctdm_getreg(wc, card, 11); >+ if (debug) >+ printk(KERN_DEBUG "VoiceDAA System: %02x\n", blah & 0xf); >+ return 0; >+} >+ >+static int wctdm_proslic_insane(struct wctdm *wc, int card) >+{ >+ int blah,insane_report; >+ insane_report=0; >+ >+ blah = wctdm_getreg(wc, card, 0); >+ if (debug) >+ printk(KERN_DEBUG "ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf)); >+ >+#if 0 >+ if ((blah & 0x30) >> 4) { >+ printk(KERN_DEBUG "ProSLIC on module %d is not a 3210.\n", card); >+ return -1; >+ } >+#endif >+ if (((blah & 0xf) == 0) || ((blah & 0xf) == 0xf)) { >+ /* SLIC not loaded */ >+ return -1; >+ } >+ if ((blah & 0xf) < 2) { >+ printk(KERN_NOTICE "ProSLIC 3210 version %d is too old\n", blah & 0xf); >+ return -1; >+ } >+ if (wctdm_getreg(wc, card, 1) & 0x80) >+ /* ProSLIC 3215, not a 3210 */ >+ wc->flags[card] |= FLAG_3215; >+ >+ blah = wctdm_getreg(wc, card, 8); >+ if (blah != 0x2) { >+ printk(KERN_NOTICE "ProSLIC on module %d insane (1) %d should be 2\n", card, blah); >+ return -1; >+ } else if ( insane_report) >+ printk(KERN_NOTICE "ProSLIC on module %d Reg 8 Reads %d Expected is 0x2\n",card,blah); >+ >+ blah = wctdm_getreg(wc, card, 64); >+ if (blah != 0x0) { >+ printk(KERN_NOTICE "ProSLIC on module %d insane (2)\n", card); >+ return -1; >+ } else if ( insane_report) >+ printk(KERN_NOTICE "ProSLIC on module %d Reg 64 Reads %d Expected is 0x0\n",card,blah); >+ >+ blah = wctdm_getreg(wc, card, 11); >+ if (blah != 0x33) { >+ printk(KERN_NOTICE "ProSLIC on module %d insane (3)\n", card); >+ return -1; >+ } else if ( insane_report) >+ printk(KERN_NOTICE "ProSLIC on module %d Reg 11 Reads %d Expected is 0x33\n",card,blah); >+ >+ /* Just be sure it's setup right. */ >+ wctdm_setreg(wc, card, 30, 0); >+ >+ if (debug) >+ printk(KERN_DEBUG "ProSLIC on module %d seems sane.\n", card); >+ return 0; >+} >+ >+static int wctdm_proslic_powerleak_test(struct wctdm *wc, int card) >+{ >+ unsigned long origjiffies; >+ unsigned char vbat; >+ >+ /* Turn off linefeed */ >+ wctdm_setreg(wc, card, 64, 0); >+ >+ /* Power down */ >+ wctdm_setreg(wc, card, 14, 0x10); >+ >+ /* Wait for one second */ >+ origjiffies = jiffies; >+ >+ while((vbat = wctdm_getreg(wc, card, 82)) > 0x6) { >+ if ((jiffies - origjiffies) >= (HZ/2)) >+ break; >+ } >+ >+ if (vbat < 0x06) { >+ printk(KERN_NOTICE "Excessive leakage detected on module %d: %d volts (%02x) after %d ms\n", card, >+ 376 * vbat / 1000, vbat, (int)((jiffies - origjiffies) * 1000 / HZ)); >+ return -1; >+ } else if (debug) { >+ printk(KERN_NOTICE "Post-leakage voltage: %d volts\n", 376 * vbat / 1000); >+ } >+ return 0; >+} >+ >+static int wctdm_powerup_proslic(struct wctdm *wc, int card, int fast) >+{ >+ unsigned char vbat; >+ unsigned long origjiffies; >+ int lim; >+ >+ /* Set period of DC-DC converter to 1/64 khz */ >+ wctdm_setreg(wc, card, 92, 0xff /* was 0xff */); >+ >+ /* Wait for VBat to powerup */ >+ origjiffies = jiffies; >+ >+ /* Disable powerdown */ >+ wctdm_setreg(wc, card, 14, 0); >+ >+ /* If fast, don't bother checking anymore */ >+ if (fast) >+ return 0; >+ >+ while((vbat = wctdm_getreg(wc, card, 82)) < 0xc0) { >+ /* Wait no more than 500ms */ >+ if ((jiffies - origjiffies) > HZ/2) { >+ break; >+ } >+ } >+ >+ if (vbat < 0xc0) { >+ if (wc->proslic_power == PROSLIC_POWER_UNKNOWN) >+ printk(KERN_NOTICE "ProSLIC on module %d failed to powerup within %d ms (%d mV only)\n\n -- DID YOU REMEMBER TO PLUG IN THE HD POWER CABLE TO THE A1200P??\n", >+ card, (int)(((jiffies - origjiffies) * 1000 / HZ)), >+ vbat * 375); >+ wc->proslic_power = PROSLIC_POWER_WARNED; >+ return -1; >+ } else if (debug) { >+ printk(KERN_DEBUG "ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", >+ card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); >+ } >+ wc->proslic_power = PROSLIC_POWER_ON; >+ >+ /* Proslic max allowed loop current, reg 71 LOOP_I_LIMIT */ >+ /* If out of range, just set it to the default value */ >+ lim = (loopcurrent - 20) / 3; >+ if ( loopcurrent > 41 ) { >+ lim = 0; >+ if (debug) >+ printk(KERN_DEBUG "Loop current out of range! Setting to default 20mA!\n"); >+ } >+ else if (debug) >+ printk(KERN_DEBUG "Loop current set to %dmA!\n",(lim*3)+20); >+ wctdm_setreg(wc,card,LOOP_I_LIMIT,lim); >+ >+ /* Engage DC-DC converter */ >+ wctdm_setreg(wc, card, 93, 0x19 /* was 0x19 */); >+#if 0 >+ origjiffies = jiffies; >+ while(0x80 & wctdm_getreg(wc, card, 93)) { >+ if ((jiffies - origjiffies) > 2 * HZ) { >+ printk(KERN_DEBUG "Timeout waiting for DC-DC calibration on module %d\n", card); >+ return -1; >+ } >+ } >+ >+#if 0 >+ /* Wait a full two seconds */ >+ while((jiffies - origjiffies) < 2 * HZ); >+ >+ /* Just check to be sure */ >+ vbat = wctdm_getreg(wc, card, 82); >+ printk(KERN_DEBUG "ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", >+ card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); >+#endif >+#endif >+ return 0; >+ >+} >+ >+static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card){ >+ unsigned long origjiffies; >+ unsigned char i; >+ >+ wctdm_setreg(wc, card, 21, 0);//(0) Disable all interupts in DR21 >+ wctdm_setreg(wc, card, 22, 0);//(0)Disable all interupts in DR21 >+ wctdm_setreg(wc, card, 23, 0);//(0)Disable all interupts in DR21 >+ wctdm_setreg(wc, card, 64, 0);//(0) >+ >+ wctdm_setreg(wc, card, 97, 0x18); //(0x18)Calibrations without the ADC and DAC offset and without common mode calibration. >+ wctdm_setreg(wc, card, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM >+ >+ origjiffies=jiffies; >+ while( wctdm_getreg(wc,card,96)!=0 ){ >+ if((jiffies-origjiffies)>80) >+ return -1; >+ } >+//Initialized DR 98 and 99 to get consistant results. >+// 98 and 99 are the results registers and the search should have same intial conditions. >+ >+/*******************************The following is the manual gain mismatch calibration****************************/ >+/*******************************This is also available as a function *******************************************/ >+ // Delay 10ms >+ origjiffies=jiffies; >+ while((jiffies-origjiffies)<1); >+ wctdm_proslic_setreg_indirect(wc, card, 88,0); >+ wctdm_proslic_setreg_indirect(wc,card,89,0); >+ wctdm_proslic_setreg_indirect(wc,card,90,0); >+ wctdm_proslic_setreg_indirect(wc,card,91,0); >+ wctdm_proslic_setreg_indirect(wc,card,92,0); >+ wctdm_proslic_setreg_indirect(wc,card,93,0); >+ >+ wctdm_setreg(wc, card, 98,0x10); // This is necessary if the calibration occurs other than at reset time >+ wctdm_setreg(wc, card, 99,0x10); >+ >+ for ( i=0x1f; i>0; i--) >+ { >+ wctdm_setreg(wc, card, 98,i); >+ origjiffies=jiffies; >+ while((jiffies-origjiffies)<4); >+ if((wctdm_getreg(wc,card,88)) == 0) >+ break; >+ } // for >+ >+ for ( i=0x1f; i>0; i--) >+ { >+ wctdm_setreg(wc, card, 99,i); >+ origjiffies=jiffies; >+ while((jiffies-origjiffies)<4); >+ if((wctdm_getreg(wc,card,89)) == 0) >+ break; >+ }//for >+ >+/*******************************The preceding is the manual gain mismatch calibration****************************/ >+/**********************************The following is the longitudinal Balance Cal***********************************/ >+ wctdm_setreg(wc,card,64,1); >+ while((jiffies-origjiffies)<10); // Sleep 100? >+ >+ wctdm_setreg(wc, card, 64, 0); >+ wctdm_setreg(wc, card, 23, 0x4); // enable interrupt for the balance Cal >+ wctdm_setreg(wc, card, 97, 0x1); // this is a singular calibration bit for longitudinal calibration >+ wctdm_setreg(wc, card, 96,0x40); >+ >+ wctdm_getreg(wc,card,96); /* Read Reg 96 just cause */ >+ >+ wctdm_setreg(wc, card, 21, 0xFF); >+ wctdm_setreg(wc, card, 22, 0xFF); >+ wctdm_setreg(wc, card, 23, 0xFF); >+ >+ /**The preceding is the longitudinal Balance Cal***/ >+ return(0); >+ >+} >+#if 1 >+static int wctdm_proslic_calibrate(struct wctdm *wc, int card) >+{ >+ unsigned long origjiffies; >+ int x; >+ /* Perform all calibrations */ >+ wctdm_setreg(wc, card, 97, 0x1f); >+ >+ /* Begin, no speedup */ >+ wctdm_setreg(wc, card, 96, 0x5f); >+ >+ /* Wait for it to finish */ >+ origjiffies = jiffies; >+ while(wctdm_getreg(wc, card, 96)) { >+ if ((jiffies - origjiffies) > 2 * HZ) { >+ printk(KERN_NOTICE "Timeout waiting for calibration of module %d\n", card); >+ return -1; >+ } >+ } >+ >+ if (debug) { >+ /* Print calibration parameters */ >+ printk(KERN_DEBUG "Calibration Vector Regs 98 - 107: \n"); >+ for (x=98;x<108;x++) { >+ printk(KERN_DEBUG "%d: %02x\n", x, wctdm_getreg(wc, card, x)); >+ } >+ } >+ return 0; >+} >+#endif >+ >+static void wait_just_a_bit(int foo) >+{ >+ long newjiffies; >+ newjiffies = jiffies + foo; >+ while(jiffies < newjiffies); >+} >+ >+/********************************************************************* >+ * Set the hwgain on the analog modules >+ * >+ * card = the card position for this module (0-23) >+ * gain = gain in dB x10 (e.g. -3.5dB would be gain=-35) >+ * tx = (0 for rx; 1 for tx) >+ * >+ *******************************************************************/ >+static int wctdm_set_hwgain(struct wctdm *wc, int card, __s32 gain, __u32 tx) >+{ >+ if (!(wc->modtype[card] == MOD_TYPE_FXO)) { >+ printk(KERN_NOTICE "Cannot adjust gain. Unsupported module type!\n"); >+ return -1; >+ } >+ if (tx) { >+ if (debug) >+ printk(KERN_DEBUG "setting FXO tx gain for card=%d to %d\n", card, gain); >+ if (gain >= -150 && gain <= 0) { >+ wctdm_setreg(wc, card, 38, 16 + (gain/-10)); >+ wctdm_setreg(wc, card, 40, 16 + (-gain%10)); >+ } else if (gain <= 120 && gain > 0) { >+ wctdm_setreg(wc, card, 38, gain/10); >+ wctdm_setreg(wc, card, 40, (gain%10)); >+ } else { >+ printk(KERN_INFO "FXO tx gain is out of range (%d)\n", gain); >+ return -1; >+ } >+ } else { /* rx */ >+ if (debug) >+ printk(KERN_DEBUG "setting FXO rx gain for card=%d to %d\n", card, gain); >+ if (gain >= -150 && gain <= 0) { >+ wctdm_setreg(wc, card, 39, 16+ (gain/-10)); >+ wctdm_setreg(wc, card, 41, 16 + (-gain%10)); >+ } else if (gain <= 120 && gain > 0) { >+ wctdm_setreg(wc, card, 39, gain/10); >+ wctdm_setreg(wc, card, 41, (gain%10)); >+ } else { >+ printk(KERN_INFO "FXO rx gain is out of range (%d)\n", gain); >+ return -1; >+ } >+ } >+ >+ return 0; >+} >+ >+static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, int sane) >+{ >+ unsigned char reg16=0, reg26=0, reg30=0, reg31=0; >+ long newjiffies; >+ wc->modtype[card] = MOD_TYPE_FXO; >+ /* Sanity check the ProSLIC */ >+ reset_spi(wc, card); >+ if (!sane && wctdm_voicedaa_insane(wc, card)) >+ return -2; >+ >+ /* Software reset */ >+ wctdm_setreg(wc, card, 1, 0x80); >+ >+ /* Wait just a bit */ >+ wait_just_a_bit(HZ/10); >+ >+ /* Enable PCM, ulaw */ >+ if (alawoverride) >+ wctdm_setreg(wc, card, 33, 0x20); >+ else >+ wctdm_setreg(wc, card, 33, 0x28); >+ >+ /* Set On-hook speed, Ringer impedence, and ringer threshold */ >+ reg16 |= (fxo_modes[_opermode].ohs << 6); >+ reg16 |= (fxo_modes[_opermode].rz << 1); >+ reg16 |= (fxo_modes[_opermode].rt); >+ wctdm_setreg(wc, card, 16, reg16); >+ >+ if(fwringdetect) { >+ /* Enable ring detector full-wave rectifier mode */ >+ wctdm_setreg(wc, card, 18, 2); >+ wctdm_setreg(wc, card, 24, 0); >+ } else { >+ /* Set to the device defaults */ >+ wctdm_setreg(wc, card, 18, 0); >+ wctdm_setreg(wc, card, 24, 0x19); >+ } >+ >+ /* Set DC Termination: >+ Tip/Ring voltage adjust, minimum operational current, current limitation */ >+ reg26 |= (fxo_modes[_opermode].dcv << 6); >+ reg26 |= (fxo_modes[_opermode].mini << 4); >+ reg26 |= (fxo_modes[_opermode].ilim << 1); >+ wctdm_setreg(wc, card, 26, reg26); >+ >+ /* Set AC Impedence */ >+ reg30 = (fxofullscale==1) ? (fxo_modes[_opermode].acim|0x10) : (fxo_modes[_opermode].acim); >+ wctdm_setreg(wc, card, 30, reg30); >+ >+ /* Misc. DAA parameters */ >+ if (fastpickup) >+ reg31 = 0xb3; >+ else >+ reg31 = 0xa3; >+ >+ reg31 |= (fxo_modes[_opermode].ohs2 << 3); >+ wctdm_setreg(wc, card, 31, reg31); >+ >+ /* Set Transmit/Receive timeslot */ >+ //printk("set card %d to %d\n", card, (3-(card%4)) * 8 + (card/4) * 64); >+ wctdm_setreg(wc, card, 34, (3-(card%4)) * 8 + (card/4) * 64); >+ wctdm_setreg(wc, card, 35, 0x00); >+ wctdm_setreg(wc, card, 36, (3-(card%4)) * 8 + (card/4) * 64); >+ wctdm_setreg(wc, card, 37, 0x00); >+ >+ /* Enable ISO-Cap */ >+ wctdm_setreg(wc, card, 6, 0x00); >+ >+ if (fastpickup) >+ wctdm_setreg(wc, card, 17, wctdm_getreg(wc, card, 17) | 0x20); >+ >+ /* Wait 1000ms for ISO-cap to come up */ >+ newjiffies = jiffies; >+ newjiffies += 2 * HZ; >+ while((jiffies < newjiffies) && !(wctdm_getreg(wc, card, 11) & 0xf0)) >+ wait_just_a_bit(HZ/10); >+ >+ if (!(wctdm_getreg(wc, card, 11) & 0xf0)) { >+ printk(KERN_NOTICE "VoiceDAA did not bring up ISO link properly!\n"); >+ return -1; >+ } >+ if (debug) >+ printk(KERN_DEBUG "ISO-Cap is now up, line side: %02x rev %02x\n", >+ wctdm_getreg(wc, card, 11) >> 4, >+ (wctdm_getreg(wc, card, 13) >> 2) & 0xf); >+ /* Enable on-hook line monitor */ >+ wctdm_setreg(wc, card, 5, 0x08); >+ >+ /* Take values for fxotxgain and fxorxgain and apply them to module */ >+ wctdm_set_hwgain(wc, card, fxotxgain, 1); >+ wctdm_set_hwgain(wc, card, fxorxgain, 0); >+ >+ /* NZ -- crank the tx gain up by 7 dB */ >+ if (!strcmp(fxo_modes[_opermode].name, "NEWZEALAND")) { >+ printk(KERN_INFO "Adjusting gain\n"); >+ wctdm_set_hwgain(wc, card, 7, 1); >+ } >+ >+ if(debug) >+ printk(KERN_DEBUG "DEBUG fxotxgain:%i.%i fxorxgain:%i.%i\n", (wctdm_getreg(wc, card, 38)/16)?-(wctdm_getreg(wc, card, 38) - 16) : wctdm_getreg(wc, card, 38), (wctdm_getreg(wc, card, 40)/16)? -(wctdm_getreg(wc, card, 40) - 16):wctdm_getreg(wc, card, 40), (wctdm_getreg(wc, card, 39)/16)? -(wctdm_getreg(wc, card, 39) - 16) : wctdm_getreg(wc, card, 39),(wctdm_getreg(wc, card, 41)/16)?-(wctdm_getreg(wc, card, 41) - 16):wctdm_getreg(wc, card, 41)); >+ >+ return 0; >+ >+} >+ >+static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, int sane) >+{ >+ >+ unsigned short tmp[5]; >+ unsigned char r19, r9; >+ int x; >+ int fxsmode=0; >+ >+ /* Sanity check the ProSLIC */ >+ if (!sane && wctdm_proslic_insane(wc, card)) >+ return -2; >+ >+ /* By default, don't send on hook */ >+ if (reversepolarity) >+ wc->mod[card].fxs.idletxhookstate = 5; >+ else >+ wc->mod[card].fxs.idletxhookstate = 1; >+ >+ if (sane) { >+ /* Make sure we turn off the DC->DC converter to prevent anything from blowing up */ >+ wctdm_setreg(wc, card, 14, 0x10); >+ } >+ >+ if (wctdm_proslic_init_indirect_regs(wc, card)) { >+ printk(KERN_INFO "Indirect Registers failed to initialize on module %d.\n", card); >+ return -1; >+ } >+ >+ /* Clear scratch pad area */ >+ wctdm_proslic_setreg_indirect(wc, card, 97,0); >+ >+ /* Clear digital loopback */ >+ wctdm_setreg(wc, card, 8, 0); >+ >+ /* Revision C optimization */ >+ wctdm_setreg(wc, card, 108, 0xeb); >+ >+ /* Disable automatic VBat switching for safety to prevent >+ Q7 from accidently turning on and burning out. */ >+ wctdm_setreg(wc, card, 67, 0x07); /* Note, if pulse dialing has problems at high REN loads >+ change this to 0x17 */ >+ >+ /* Turn off Q7 */ >+ wctdm_setreg(wc, card, 66, 1); >+ >+ /* Flush ProSLIC digital filters by setting to clear, while >+ saving old values */ >+ for (x=0;x<5;x++) { >+ tmp[x] = wctdm_proslic_getreg_indirect(wc, card, x + 35); >+ wctdm_proslic_setreg_indirect(wc, card, x + 35, 0x8000); >+ } >+ >+ /* Power up the DC-DC converter */ >+ if (wctdm_powerup_proslic(wc, card, fast)) { >+ printk(KERN_NOTICE "Unable to do INITIAL ProSLIC powerup on module %d\n", card); >+ return -1; >+ } >+ >+ if (!fast) { >+ >+ /* Check for power leaks */ >+ if (wctdm_proslic_powerleak_test(wc, card)) { >+ printk(KERN_NOTICE "ProSLIC module %d failed leakage test. Check for short circuit\n", card); >+ } >+ /* Power up again */ >+ if (wctdm_powerup_proslic(wc, card, fast)) { >+ printk(KERN_NOTICE "Unable to do FINAL ProSLIC powerup on module %d\n", card); >+ return -1; >+ } >+#ifndef NO_CALIBRATION >+ /* Perform calibration */ >+ if(manual) { >+ if (wctdm_proslic_manual_calibrate(wc, card)) { >+ //printk(KERN_NOTICE "Proslic failed on Manual Calibration\n"); >+ if (wctdm_proslic_manual_calibrate(wc, card)) { >+ printk(KERN_NOTICE "Proslic Failed on Second Attempt to Calibrate Manually. (Try -DNO_CALIBRATION in Makefile)\n"); >+ return -1; >+ } >+ printk(KERN_NOTICE "Proslic Passed Manual Calibration on Second Attempt\n"); >+ } >+ } >+ else { >+ if(wctdm_proslic_calibrate(wc, card)) { >+ //printk(KERN_NOTICE "ProSlic died on Auto Calibration.\n"); >+ if (wctdm_proslic_calibrate(wc, card)) { >+ printk(KERN_NOTICE "Proslic Failed on Second Attempt to Auto Calibrate\n"); >+ return -1; >+ } >+ printk(KERN_NOTICE "Proslic Passed Auto Calibration on Second Attempt\n"); >+ } >+ } >+ /* Perform DC-DC calibration */ >+ wctdm_setreg(wc, card, 93, 0x99); >+ r19 = wctdm_getreg(wc, card, 107); >+ if ((r19 < 0x2) || (r19 > 0xd)) { >+ printk(KERN_NOTICE "DC-DC cal has a surprising direct 107 of 0x%02x!\n", r19); >+ wctdm_setreg(wc, card, 107, 0x8); >+ } >+ >+ /* Save calibration vectors */ >+ for (x=0;x<NUM_CAL_REGS;x++) >+ wc->mod[card].fxs.calregs.vals[x] = wctdm_getreg(wc, card, 96 + x); >+#endif >+ >+ } else { >+ /* Restore calibration registers */ >+ for (x=0;x<NUM_CAL_REGS;x++) >+ wctdm_setreg(wc, card, 96 + x, wc->mod[card].fxs.calregs.vals[x]); >+ } >+ /* Calibration complete, restore original values */ >+ for (x=0;x<5;x++) { >+ wctdm_proslic_setreg_indirect(wc, card, x + 35, tmp[x]); >+ } >+ >+ if (wctdm_proslic_verify_indirect_regs(wc, card)) { >+ printk(KERN_INFO "Indirect Registers failed verification.\n"); >+ return -1; >+ } >+ >+ >+#if 0 >+ /* Disable Auto Power Alarm Detect and other "features" */ >+ wctdm_setreg(wc, card, 67, 0x0e); >+ blah = wctdm_getreg(wc, card, 67); >+#endif >+ >+#if 0 >+ if (wctdm_proslic_setreg_indirect(wc, card, 97, 0x0)) { // Stanley: for the bad recording fix >+ printk(KERN_INFO "ProSlic IndirectReg Died.\n"); >+ return -1; >+ } >+#endif >+ >+ if (alawoverride) >+ wctdm_setreg(wc, card, 1, 0x20); >+ else >+ wctdm_setreg(wc, card, 1, 0x28); >+ // U-Law 8-bit interface >+ wctdm_setreg(wc, card, 2, (3-(card%4)) * 8 + (card/4) * 64); // Tx Start count low byte 0 >+ wctdm_setreg(wc, card, 3, 0); // Tx Start count high byte 0 >+ wctdm_setreg(wc, card, 4, (3-(card%4)) * 8 + (card/4) * 64); // Rx Start count low byte 0 >+ wctdm_setreg(wc, card, 5, 0); // Rx Start count high byte 0 >+ wctdm_setreg(wc, card, 18, 0xff); // clear all interrupt >+ wctdm_setreg(wc, card, 19, 0xff); >+ wctdm_setreg(wc, card, 20, 0xff); >+ wctdm_setreg(wc, card, 73, 0x04); >+ if (fxshonormode) { >+ fxsmode = acim2tiss[fxo_modes[_opermode].acim]; >+ wctdm_setreg(wc, card, 10, 0x08 | fxsmode); >+ if (fxo_modes[_opermode].ring_osc) >+ wctdm_proslic_setreg_indirect(wc, card, 20, fxo_modes[_opermode].ring_osc); >+ if (fxo_modes[_opermode].ring_x) >+ wctdm_proslic_setreg_indirect(wc, card, 21, fxo_modes[_opermode].ring_x); >+ } >+ if (lowpower) >+ wctdm_setreg(wc, card, 72, 0x10); >+ >+#if 0 >+ wctdm_setreg(wc, card, 21, 0x00); // enable interrupt >+ wctdm_setreg(wc, card, 22, 0x02); // Loop detection interrupt >+ wctdm_setreg(wc, card, 23, 0x01); // DTMF detection interrupt >+#endif >+ >+#if 0 >+ /* Enable loopback */ >+ wctdm_setreg(wc, card, 8, 0x2); >+ wctdm_setreg(wc, card, 14, 0x0); >+ wctdm_setreg(wc, card, 64, 0x0); >+ wctdm_setreg(wc, card, 1, 0x08); >+#endif >+ >+ if (fastringer) { >+ /* Speed up Ringer */ >+ wctdm_proslic_setreg_indirect(wc, card, 20, 0x7e6d); >+ wctdm_proslic_setreg_indirect(wc, card, 21, 0x01b9); >+ /* Beef up Ringing voltage to 89V */ >+ if (boostringer) { >+ wctdm_setreg(wc, card, 74, 0x3f); >+ if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x247)) >+ return -1; >+ printk(KERN_INFO "Boosting fast ringer on slot %d (89V peak)\n", card + 1); >+ } else if (lowpower) { >+ if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x14b)) >+ return -1; >+ printk(KERN_INFO "Reducing fast ring power on slot %d (50V peak)\n", card + 1); >+ } else >+ printk(KERN_INFO "Speeding up ringer on slot %d (25Hz)\n", card + 1); >+ } else { >+ /* Beef up Ringing voltage to 89V */ >+ if (boostringer) { >+ wctdm_setreg(wc, card, 74, 0x3f); >+ if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x1d1)) >+ return -1; >+ printk(KERN_INFO "Boosting ringer on slot %d (89V peak)\n", card + 1); >+ } else if (lowpower) { >+ if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x108)) >+ return -1; >+ printk(KERN_INFO "Reducing ring power on slot %d (50V peak)\n", card + 1); >+ } >+ } >+ >+ if(fxstxgain || fxsrxgain) { >+ r9 = wctdm_getreg(wc, card, 9); >+ switch (fxstxgain) { >+ >+ case 35: >+ r9+=8; >+ break; >+ case -35: >+ r9+=4; >+ break; >+ case 0: >+ break; >+ } >+ >+ switch (fxsrxgain) { >+ >+ case 35: >+ r9+=2; >+ break; >+ case -35: >+ r9+=1; >+ break; >+ case 0: >+ break; >+ } >+ wctdm_setreg(wc,card,9,r9); >+ } >+ >+ if(debug) >+ printk(KERN_DEBUG "DEBUG: fxstxgain:%s fxsrxgain:%s\n",((wctdm_getreg(wc, card, 9)/8) == 1)?"3.5":(((wctdm_getreg(wc,card,9)/4) == 1)?"-3.5":"0.0"),((wctdm_getreg(wc, card, 9)/2) == 1)?"3.5":((wctdm_getreg(wc,card,9)%2)?"-3.5":"0.0")); >+ >+ wctdm_setreg(wc, card, 64, 0x01); >+ return 0; >+} >+ >+ >+static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) >+{ >+ struct wctdm_stats stats; >+ struct wctdm_regs regs; >+ struct wctdm_regop regop; >+ struct wctdm_echo_coefs echoregs; >+ struct dahdi_hwgain hwgain; >+ struct wctdm *wc = chan->pvt; >+ int x; >+ switch (cmd) { >+ case DAHDI_ONHOOKTRANSFER: >+ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) >+ return -EINVAL; >+ if (get_user(x, (__user int *)data)) >+ return -EFAULT; >+ wc->mod[chan->chanpos - 1].fxs.ohttimer = x << 3; >+ if (reversepolarity) >+ wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ >+ else >+ wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x2; >+ if (wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x1 || wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x5) { >+ /* Apply the change if appropriate */ >+ if (reversepolarity) >+ wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x6; >+ else >+ wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x2; >+ wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); >+ } >+ break; >+ case DAHDI_SETPOLARITY: >+ if (get_user(x, (__user int *)data)) >+ return -EFAULT; >+ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) >+ return -EINVAL; >+ /* Can't change polarity while ringing or when open */ >+ if ((wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x04) || >+ (wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x00)) >+ return -EINVAL; >+ >+ if ((x && !reversepolarity) || (!x && reversepolarity)) >+ wc->mod[chan->chanpos - 1].fxs.lasttxhook |= 0x04; >+ else >+ wc->mod[chan->chanpos - 1].fxs.lasttxhook &= ~0x04; >+ wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook); >+ break; >+ case WCTDM_GET_STATS: >+ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { >+ stats.tipvolt = wctdm_getreg(wc, chan->chanpos - 1, 80) * -376; >+ stats.ringvolt = wctdm_getreg(wc, chan->chanpos - 1, 81) * -376; >+ stats.batvolt = wctdm_getreg(wc, chan->chanpos - 1, 82) * -376; >+ } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { >+ stats.tipvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; >+ stats.ringvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; >+ stats.batvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000; >+ } else >+ return -EINVAL; >+ if (copy_to_user((__user void *)data, &stats, sizeof(stats))) >+ return -EFAULT; >+ break; >+ case WCTDM_GET_REGS: >+ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { >+ for (x=0;x<NUM_INDIRECT_REGS;x++) >+ regs.indirect[x] = wctdm_proslic_getreg_indirect(wc, chan->chanpos -1, x); >+ for (x=0;x<NUM_REGS;x++) >+ regs.direct[x] = wctdm_getreg(wc, chan->chanpos - 1, x); >+ } else { >+ memset(®s, 0, sizeof(regs)); >+ for (x=0;x<NUM_FXO_REGS;x++) >+ regs.direct[x] = wctdm_getreg(wc, chan->chanpos - 1, x); >+ } >+ if (copy_to_user((__user void *)data, ®s, sizeof(regs))) >+ return -EFAULT; >+ break; >+ case WCTDM_SET_REG: >+ if (copy_from_user(®op, (__user void *)data, sizeof(regop))) >+ return -EFAULT; >+ if (regop.indirect) { >+ if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS) >+ return -EINVAL; >+ printk(KERN_INFO "Setting indirect %d to 0x%04x on %d\n", regop.reg, regop.val, chan->chanpos); >+ wctdm_proslic_setreg_indirect(wc, chan->chanpos - 1, regop.reg, regop.val); >+ } else { >+ regop.val &= 0xff; >+ printk(KERN_INFO "Setting direct %d to %04x on %d\n", regop.reg, regop.val, chan->chanpos); >+ wctdm_setreg(wc, chan->chanpos - 1, regop.reg, regop.val); >+ } >+ break; >+ case WCTDM_SET_ECHOTUNE: >+ printk(KERN_INFO "-- Setting echo registers: \n"); >+ if (copy_from_user(&echoregs, (__user void *)data, sizeof(echoregs))) >+ return -EFAULT; >+ >+ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { >+ /* Set the ACIM register */ >+ wctdm_setreg(wc, chan->chanpos - 1, 30, (fxofullscale==1) ? (echoregs.acim|0x10) : echoregs.acim); >+ >+ /* Set the digital echo canceller registers */ >+ wctdm_setreg(wc, chan->chanpos - 1, 45, echoregs.coef1); >+ wctdm_setreg(wc, chan->chanpos - 1, 46, echoregs.coef2); >+ wctdm_setreg(wc, chan->chanpos - 1, 47, echoregs.coef3); >+ wctdm_setreg(wc, chan->chanpos - 1, 48, echoregs.coef4); >+ wctdm_setreg(wc, chan->chanpos - 1, 49, echoregs.coef5); >+ wctdm_setreg(wc, chan->chanpos - 1, 50, echoregs.coef6); >+ wctdm_setreg(wc, chan->chanpos - 1, 51, echoregs.coef7); >+ wctdm_setreg(wc, chan->chanpos - 1, 52, echoregs.coef8); >+ >+ printk(KERN_INFO "-- Set echo registers successfully\n"); >+ >+ break; >+ } else { >+ return -EINVAL; >+ >+ } >+ break; >+ case DAHDI_SET_HWGAIN: >+ if (copy_from_user(&hwgain, (__user void *) data, sizeof(hwgain))) >+ return -EFAULT; >+ >+ wctdm_set_hwgain(wc, chan->chanpos-1, hwgain.newgain, hwgain.tx); >+ >+ if (debug) >+ printk(KERN_DEBUG "Setting hwgain on channel %d to %d for %s direction\n", >+ chan->chanpos-1, hwgain.newgain, hwgain.tx ? "tx" : "rx"); >+ break; >+ default: >+ return -ENOTTY; >+ } >+ return 0; >+ >+} >+ >+static int wctdm_open(struct dahdi_chan *chan) >+{ >+ struct wctdm *wc = chan->pvt; >+ if (!(wc->cardflag & (1 << (chan->chanpos - 1)))) >+ return -ENODEV; >+ if (wc->dead) >+ return -ENODEV; >+ wc->usecount++; >+ >+ /*MOD_INC_USE_COUNT; */ >+ try_module_get(THIS_MODULE); >+ return 0; >+} >+ >+static inline struct wctdm *wctdm_from_span(struct dahdi_span *span) >+{ >+ return container_of(span, struct wctdm, span); >+} >+ >+static int wctdm_watchdog(struct dahdi_span *span, int event) >+{ >+ printk(KERN_INFO "opvxa1200: Restarting DMA\n"); >+ wctdm_restart_dma(wctdm_from_span(span)); >+ return 0; >+} >+ >+static int wctdm_close(struct dahdi_chan *chan) >+{ >+ struct wctdm *wc = chan->pvt; >+ wc->usecount--; >+ >+ /*MOD_DEC_USE_COUNT;*/ >+ module_put(THIS_MODULE); >+ >+ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { >+ if (reversepolarity) >+ wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 5; >+ else >+ wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 1; >+ } >+ /* If we're dead, release us now */ >+ if (!wc->usecount && wc->dead) >+ wctdm_release(wc); >+ return 0; >+} >+ >+static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) >+{ >+ struct wctdm *wc = chan->pvt; >+ int reg=0; >+ if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) { >+ /* XXX Enable hooksig for FXO XXX */ >+ switch(txsig) { >+ case DAHDI_TXSIG_START: >+ case DAHDI_TXSIG_OFFHOOK: >+ wc->mod[chan->chanpos - 1].fxo.offhook = 1; >+ wctdm_setreg(wc, chan->chanpos - 1, 5, 0x9); >+ if(cidbeforering) >+ { >+ wc->cid_state[chan->chanpos - 1] = CID_STATE_IDLE; >+ wc->cid_history_clone_cnt[chan->chanpos - 1] = 0; >+ wc->cid_history_ptr[chan->chanpos - 1] = 0; >+ memset(wc->cid_history_buf[chan->chanpos - 1], DAHDI_LIN2X(0, chan), cidbuflen * DAHDI_MAX_CHUNKSIZE); >+ } >+ break; >+ case DAHDI_TXSIG_ONHOOK: >+ wc->mod[chan->chanpos - 1].fxo.offhook = 0; >+ wctdm_setreg(wc, chan->chanpos - 1, 5, 0x8); >+ break; >+ default: >+ printk(KERN_NOTICE "wcfxo: Can't set tx state to %d\n", txsig); >+ } >+ } else { >+ switch(txsig) { >+ case DAHDI_TXSIG_ONHOOK: >+ switch(chan->sig) { >+ case DAHDI_SIG_EM: >+ case DAHDI_SIG_FXOKS: >+ case DAHDI_SIG_FXOLS: >+ wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; >+ break; >+ case DAHDI_SIG_FXOGS: >+ wc->mod[chan->chanpos-1].fxs.lasttxhook = 3; >+ break; >+ } >+ break; >+ case DAHDI_TXSIG_OFFHOOK: >+ switch(chan->sig) { >+ case DAHDI_SIG_EM: >+ wc->mod[chan->chanpos-1].fxs.lasttxhook = 5; >+ break; >+ default: >+ wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate; >+ break; >+ } >+ break; >+ case DAHDI_TXSIG_START: >+ wc->mod[chan->chanpos-1].fxs.lasttxhook = 4; >+ break; >+ case DAHDI_TXSIG_KEWL: >+ wc->mod[chan->chanpos-1].fxs.lasttxhook = 0; >+ break; >+ default: >+ printk(KERN_NOTICE "opvxa1200: Can't set tx state to %d\n", txsig); >+ } >+ if (debug) >+ printk(KERN_DEBUG "Setting FXS hook state to %d (%02x)\n", txsig, reg); >+ >+#if 1 >+ wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos-1].fxs.lasttxhook); >+#endif >+ } >+ return 0; >+} >+ >+#ifdef DAHDI_SPAN_OPS >+static const struct dahdi_span_ops wctdm_span_ops = { >+ .owner = THIS_MODULE, >+ .hooksig = wctdm_hooksig, >+ .open = wctdm_open, >+ .close = wctdm_close, >+ .ioctl = wctdm_ioctl, >+ .watchdog = wctdm_watchdog, >+}; >+#endif >+ >+static int wctdm_initialize(struct wctdm *wc) >+{ >+ int x; >+ >+ /* Dahdi stuff */ >+ sprintf(wc->span.name, "OPVXA1200/%d", wc->pos); >+ snprintf(wc->span.desc, sizeof(wc->span.desc)-1, "%s Board %d", wc->variety, wc->pos + 1); >+ snprintf(wc->span.location, sizeof(wc->span.location) - 1, >+ "PCI Bus %02d Slot %02d", wc->dev->bus->number, PCI_SLOT(wc->dev->devfn) + 1); >+ wc->span.manufacturer = "OpenVox"; >+ dahdi_copy_string(wc->span.devicetype, wc->variety, sizeof(wc->span.devicetype)); >+ if (alawoverride) { >+ printk(KERN_INFO "ALAW override parameter detected. Device will be operating in ALAW\n"); >+ wc->span.deflaw = DAHDI_LAW_ALAW; >+ } else >+ wc->span.deflaw = DAHDI_LAW_MULAW; >+ >+ x = __wctdm_getcreg(wc, WC_VER); >+ wc->fwversion = x; >+ if( x & FLAG_A800) >+ { >+ wc->card_name = A800P_Name; >+ wc->max_cards = 8; >+ } >+ else >+ { >+ wc->card_name = A1200P_Name; >+ wc->max_cards = 12; >+ } >+ >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { >+ sprintf(wc->chans[x]->name, "OPVXA1200/%d/%d", wc->pos, x); >+ wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; >+ wc->chans[x]->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; >+ wc->chans[x]->chanpos = x+1; >+ wc->chans[x]->pvt = wc; >+ } >+ >+#ifdef DAHDI_SPAN_MODULE >+ wc->span.owner = THIS_MODULE; >+#endif >+ >+#ifdef DAHDI_SPAN_OPS >+ wc->span.ops = &wctdm_span_ops; >+#else >+ wc->span.hooksig = wctdm_hooksig, >+ wc->span.watchdog = wctdm_watchdog, >+ wc->span.open = wctdm_open; >+ wc->span.close = wctdm_close; >+ wc->span.ioctl = wctdm_ioctl; >+ wc->span.pvt = wc; >+#endif >+ wc->span.chans = wc->chans; >+ wc->span.channels = wc->max_cards; /*MAX_NUM_CARDS;*/ >+ wc->span.irq = wc->dev->irq; >+ wc->span.flags = DAHDI_FLAG_RBS; >+ wc->span.ops = &wctdm_span_ops; >+ >+ if (dahdi_register(&wc->span, 0)) { >+ printk(KERN_NOTICE "Unable to register span with Dahdi\n"); >+ return -1; >+ } >+ return 0; >+} >+ >+static void wctdm_post_initialize(struct wctdm *wc) >+{ >+ int x; >+ >+ /* Finalize signalling */ >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { >+ if (wc->cardflag & (1 << x)) { >+ if (wc->modtype[x] == MOD_TYPE_FXO) >+ wc->chans[x]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; >+ else >+ wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; >+ } else if (!(wc->chans[x]->sigcap & DAHDI_SIG_BROKEN)) { >+ wc->chans[x]->sigcap = 0; >+ } >+ } >+} >+ >+static int wctdm_hardware_init(struct wctdm *wc) >+{ >+ /* Hardware stuff */ >+ unsigned char ver; >+ unsigned char x,y; >+ int failed; >+ long origjiffies; //ml. >+ >+ /* Signal Reset */ >+ printk("before raise reset\n"); >+ outb(0x01, wc->ioaddr + WC_CNTL); >+ >+ /* Wait for 5 second */ >+ >+ origjiffies = jiffies; >+ >+ while(1) >+ { >+ if ((jiffies - origjiffies) >= (HZ*5)) >+ break;; >+ } >+ >+ /* printk(KERN_INFO "after raise reset\n");*/ >+ >+ /* Check OpenVox chip */ >+ x=inb(wc->ioaddr + WC_CNTL); >+ ver = __wctdm_getcreg(wc, WC_VER); >+ wc->fwversion = ver; >+ /*if( ver & FLAG_A800) >+ { >+ wc->card_name = A800P_Name; >+ wc->max_cards = 8; >+ } >+ else >+ { >+ wc->card_name = A1200P_Name; >+ wc->max_cards = 12; >+ }*/ >+ printk(KERN_NOTICE "OpenVox %s version: %01x.%01x\n", wc->card_name, (ver&(~FLAG_A800))>>4, ver&0x0f); >+ >+ failed = 0; >+ if (ver != 0x00) { >+ for (x=0;x<16;x++) { >+ /* Test registers */ >+ __wctdm_setcreg(wc, WC_CS, x); >+ y = __wctdm_getcreg(wc, WC_CS) & 0x0f; >+ if (x != y) { >+ printk(KERN_INFO "%02x != %02x\n", x, y); >+ failed++; >+ } >+ } >+ >+ if (!failed) { >+ printk(KERN_INFO "OpenVox %s passed register test\n", wc->card_name); >+ } else { >+ printk(KERN_NOTICE "OpenVox %s failed register test\n", wc->card_name); >+ return -1; >+ } >+ } else { >+ printk(KERN_INFO "No OpenVox chip %02x\n", ver); >+ } >+ >+ if (spibyhw) >+ __wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW); // spi controled by hw MiaoLin; >+ else >+ __wctdm_setcreg(wc, WC_SPICTRL, 0); >+ >+ /* Reset PCI Interface chip and registers (and serial) */ >+ outb(0x06, wc->ioaddr + WC_CNTL); >+ /* Setup our proper outputs for when we switch for our "serial" port */ >+ wc->ios = BIT_CS | BIT_SCLK | BIT_SDI; >+ >+ outb(wc->ios, wc->ioaddr + WC_AUXD); >+ >+ /* Set all to outputs except AUX 5, which is an input */ >+ outb(0xdf, wc->ioaddr + WC_AUXC); >+ >+ /* Select alternate function for AUX0 */ /* Useless in OpenVox by MiaoLin. */ >+ /* outb(0x4, wc->ioaddr + WC_AUXFUNC); */ >+ >+ /* Wait 1/4 of a sec */ >+ wait_just_a_bit(HZ/4); >+ >+ /* Back to normal, with automatic DMA wrap around */ >+ outb(0x30 | 0x01, wc->ioaddr + WC_CNTL); >+ wc->ledstate = 0; >+ wctdm_set_led(wc, 0, 0); >+ >+ /* Make sure serial port and DMA are out of reset */ >+ outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, wc->ioaddr + WC_CNTL); >+ >+ /* Configure serial port for MSB->LSB operation */ >+ outb(0xc1, wc->ioaddr + WC_SERCTL); >+ >+ /* Delay FSC by 0 so it's properly aligned */ >+ outb(0x01, wc->ioaddr + WC_FSCDELAY); /* Modify to 1 by MiaoLin */ >+ >+ /* Setup DMA Addresses */ >+ outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ >+ outl(wc->writedma + DAHDI_CHUNKSIZE * 4 * 4 - 4, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ >+ outl(wc->writedma + DAHDI_CHUNKSIZE * 8 * 4 - 4, wc->ioaddr + WC_DMAWE); /* End */ >+ >+ outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ >+ outl(wc->readdma + DAHDI_CHUNKSIZE * 4 * 4 - 4, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ >+ outl(wc->readdma + DAHDI_CHUNKSIZE * 8 * 4 - 4, wc->ioaddr + WC_DMARE); /* End */ >+ >+ /* Clear interrupts */ >+ outb(0xff, wc->ioaddr + WC_INTSTAT); >+ >+ /* Wait 1/4 of a second more */ >+ wait_just_a_bit(HZ/4); >+ >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { >+ int sane=0,ret=0,readi=0; >+#if 1 >+ touch_softlockup_watchdog(); // avoid showing CPU softlock message >+ /* Init with Auto Calibration */ >+ if (!(ret=wctdm_init_proslic(wc, x, 0, 0, sane))) { >+ wc->cardflag |= (1 << x); >+ if (debug) { >+ readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); >+ printk("Proslic module %d loop current is %dmA\n",x, >+ ((readi*3)+20)); >+ } >+ printk(KERN_INFO "Module %d: Installed -- AUTO FXS/DPO\n",x); >+ wctdm_set_led(wc, (unsigned int)x, 1); >+ } else { >+ if(ret!=-2) { >+ sane=1; >+ >+ printk(KERN_INFO "Init ProSlic with Manual Calibration \n"); >+ /* Init with Manual Calibration */ >+ if (!wctdm_init_proslic(wc, x, 0, 1, sane)) { >+ wc->cardflag |= (1 << x); >+ if (debug) { >+ readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); >+ printk("Proslic module %d loop current is %dmA\n",x, >+ ((readi*3)+20)); >+ } >+ printk(KERN_INFO "Module %d: Installed -- MANUAL FXS\n",x); >+ } else { >+ printk(KERN_NOTICE "Module %d: FAILED FXS (%s)\n", x, fxshonormode ? fxo_modes[_opermode].name : "FCC"); >+ wc->chans[x]->sigcap = __DAHDI_SIG_FXO | DAHDI_SIG_BROKEN; >+ } >+ } else if (!(ret = wctdm_init_voicedaa(wc, x, 0, 0, sane))) { >+ wc->cardflag |= (1 << x); >+ printk(KERN_INFO "Module %d: Installed -- AUTO FXO (%s mode)\n",x, fxo_modes[_opermode].name); >+ wctdm_set_led(wc, (unsigned int)x, 1); >+ } else >+ printk(KERN_NOTICE "Module %d: Not installed\n", x); >+ } >+#endif >+ } >+ >+ /* Return error if nothing initialized okay. */ >+ if (!wc->cardflag && !timingonly) >+ return -1; >+ /*__wctdm_setcreg(wc, WC_SYNC, (wc->cardflag << 1) | 0x1); */ /* removed by MiaoLin */ >+ return 0; >+} >+ >+static void wctdm_enable_interrupts(struct wctdm *wc) >+{ >+ /* Clear interrupts */ >+ outb(0xff, wc->ioaddr + WC_INTSTAT); >+ >+ /* Enable interrupts (we care about all of them) */ >+ outb(0x3c, wc->ioaddr + WC_MASK0); >+ /* No external interrupts */ >+ outb(0x00, wc->ioaddr + WC_MASK1); >+} >+ >+static void wctdm_restart_dma(struct wctdm *wc) >+{ >+ /* Reset Master and TDM */ >+ outb(0x01, wc->ioaddr + WC_CNTL); >+ outb(0x01, wc->ioaddr + WC_OPER); >+} >+ >+static void wctdm_start_dma(struct wctdm *wc) >+{ >+ /* Reset Master and TDM */ >+ outb(0x0f, wc->ioaddr + WC_CNTL); >+ set_current_state(TASK_INTERRUPTIBLE); >+ schedule_timeout(1); >+ outb(0x01, wc->ioaddr + WC_CNTL); >+ outb(0x01, wc->ioaddr + WC_OPER); >+} >+ >+static void wctdm_stop_dma(struct wctdm *wc) >+{ >+ outb(0x00, wc->ioaddr + WC_OPER); >+} >+ >+static void wctdm_reset_tdm(struct wctdm *wc) >+{ >+ /* Reset TDM */ >+ outb(0x0f, wc->ioaddr + WC_CNTL); >+} >+ >+static void wctdm_disable_interrupts(struct wctdm *wc) >+{ >+ outb(0x00, wc->ioaddr + WC_MASK0); >+ outb(0x00, wc->ioaddr + WC_MASK1); >+} >+ >+static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) >+{ >+ int res; >+ struct wctdm *wc; >+ struct wctdm_desc *d = (struct wctdm_desc *)ent->driver_data; >+ int x; >+ int y; >+ >+ static int initd_ifaces=0; >+ >+ if(initd_ifaces){ >+ memset((void *)ifaces,0,(sizeof(struct wctdm *))*WC_MAX_IFACES); >+ initd_ifaces=1; >+ } >+ for (x=0;x<WC_MAX_IFACES;x++) >+ if (!ifaces[x]) break; >+ if (x >= WC_MAX_IFACES) { >+ printk(KERN_NOTICE "Too many interfaces\n"); >+ return -EIO; >+ } >+ >+ if (pci_enable_device(pdev)) { >+ res = -EIO; >+ } else { >+ wc = kmalloc(sizeof(struct wctdm), GFP_KERNEL); >+ if (wc) { >+ int cardcount = 0; >+ >+ wc->lastchan = -1; /* first channel offset = -1; */ >+ wc->ledstate = 0; >+ >+ ifaces[x] = wc; >+ memset(wc, 0, sizeof(struct wctdm)); >+ for (x=0; x < sizeof(wc->chans)/sizeof(wc->chans[0]); ++x) { >+ wc->chans[x] = &wc->_chans[x]; >+ } >+ >+ spin_lock_init(&wc->lock); >+ wc->curcard = -1; >+ wc->ioaddr = pci_resource_start(pdev, 0); >+ wc->mem_region = pci_resource_start(pdev, 1); >+ wc->mem_len = pci_resource_len(pdev, 1); >+ wc->mem32 = (unsigned long)ioremap(wc->mem_region, wc->mem_len); >+ wc->dev = pdev; >+ wc->pos = x; >+ wc->variety = d->name; >+ for (y=0;y<MAX_NUM_CARDS;y++) >+ wc->flags[y] = d->flags; >+ /* Keep track of whether we need to free the region */ >+ if (request_region(wc->ioaddr, 0xff, "opvxa1200")) >+ wc->freeregion = 1; >+ else >+ wc->freeregion = 0; >+ >+ if (request_mem_region(wc->mem_region, wc->mem_len, "opvxa1200")) >+ wc->freeregion |= 0x02; >+ >+ /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses >+ 8 bits. */ >+ wc->writechunk = pci_alloc_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, &wc->writedma); >+ if (!wc->writechunk) { >+ printk(KERN_NOTICE "opvxa1200: Unable to allocate DMA-able memory\n"); >+ if (wc->freeregion & 0x01) >+ release_region(wc->ioaddr, 0xff); >+ if (wc->freeregion & 0x02) >+ { >+ release_mem_region(wc->mem_region, wc->mem_len); >+ iounmap((void *)wc->mem32); >+ } >+ return -ENOMEM; >+ } >+ >+ wc->readchunk = wc->writechunk + DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2; /* in bytes */ >+ wc->readdma = wc->writedma + DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2; /* in bytes */ >+ >+ if (wctdm_initialize(wc)) { >+ printk(KERN_NOTICE "opvxa1200: Unable to intialize FXS\n"); >+ /* Set Reset Low */ >+ x=inb(wc->ioaddr + WC_CNTL); >+ outb((~0x1)&x, wc->ioaddr + WC_CNTL); >+ /* Free Resources */ >+ free_irq(pdev->irq, wc); >+ if (wc->freeregion & 0x01) >+ release_region(wc->ioaddr, 0xff); >+ if (wc->freeregion & 0x02) >+ { >+ release_mem_region(wc->mem_region, wc->mem_len); >+ iounmap((void *)wc->mem32); >+ } >+ } >+ >+ /* Enable bus mastering */ >+ pci_set_master(pdev); >+ >+ /* Keep track of which device we are */ >+ pci_set_drvdata(pdev, wc); >+ >+ >+ if (request_irq(pdev->irq, wctdm_interrupt, DAHDI_IRQ_SHARED, "opvxa1200", wc)) { >+ printk(KERN_NOTICE "opvxa1200: Unable to request IRQ %d\n", pdev->irq); >+ if (wc->freeregion & 0x01) >+ release_region(wc->ioaddr, 0xff); >+ if (wc->freeregion & 0x02) >+ { >+ release_mem_region(wc->mem_region, wc->mem_len); >+ iounmap((void *)wc->mem32); >+ } >+ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); >+ pci_set_drvdata(pdev, NULL); >+ kfree(wc); >+ return -EIO; >+ } >+ >+ if (wctdm_hardware_init(wc)) { >+ unsigned char w; >+ >+ /* Set Reset Low */ >+ w=inb(wc->ioaddr + WC_CNTL); >+ outb((~0x1)&w, wc->ioaddr + WC_CNTL); >+ /* Free Resources */ >+ free_irq(pdev->irq, wc); >+ if (wc->freeregion & 0x01) >+ release_region(wc->ioaddr, 0xff); >+ if (wc->freeregion & 0x02) >+ { >+ release_mem_region(wc->mem_region, wc->mem_len); >+ iounmap((void *)wc->mem32); >+ } >+ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); >+ pci_set_drvdata(pdev, NULL); >+ dahdi_unregister(&wc->span); >+ kfree(wc); >+ return -EIO; >+ >+ } >+ >+#ifdef TEST_LOG_INCOME_VOICE >+ for(x=0; x<MAX_NUM_CARDS+NUM_FLAG; x++) >+ { >+ wc->voc_buf[x] = kmalloc(voc_buffer_size, GFP_KERNEL); >+ wc->voc_ptr[x] = 0; >+ } >+#endif >+ >+ if(cidbeforering) >+ { >+ int len = cidbuflen * DAHDI_MAX_CHUNKSIZE; >+ if(debug) >+ printk("cidbeforering support enabled, length is %d msec\n", cidbuflen); >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) >+ { >+ wc->cid_history_buf[x] = kmalloc(len, GFP_KERNEL); >+ wc->cid_history_ptr[x] = 0; >+ wc->cid_history_clone_cnt[x] = 0; >+ wc->cid_state[x] = CID_STATE_IDLE; >+ } >+ } >+ >+ wctdm_post_initialize(wc); >+ >+ /* Enable interrupts */ >+ wctdm_enable_interrupts(wc); >+ /* Initialize Write/Buffers to all blank data */ >+ memset((void *)wc->writechunk,0, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2); >+ >+ /* Start DMA */ >+ wctdm_start_dma(wc); >+ >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) { >+ if (wc->cardflag & (1 << x)) >+ cardcount++; >+ } >+ >+ printk(KERN_INFO "Found an OpenVox %s: Version %x.%x (%d modules)\n", wc->card_name, (wc->fwversion&(~FLAG_A800))>>4, wc->fwversion&0x0f, cardcount); >+ if(debug) >+ printk(KERN_DEBUG "OpenVox %s debug On\n", wc->card_name); >+ >+ res = 0; >+ } else >+ res = -ENOMEM; >+ } >+ return res; >+} >+ >+static void wctdm_release(struct wctdm *wc) >+{ >+#ifdef TEST_LOG_INCOME_VOICE >+ struct file * f = NULL; >+ mm_segment_t orig_fs; >+ int i; >+ char fname[20]; >+#endif >+ >+ dahdi_unregister(&wc->span); >+ if (wc->freeregion & 0x01) >+ release_region(wc->ioaddr, 0xff); >+ if (wc->freeregion & 0x02) >+ { >+ release_mem_region(wc->mem_region, wc->mem_len); >+ iounmap((void *)wc->mem32); >+ } >+ >+#ifdef TEST_LOG_INCOME_VOICE >+ for(i=0; i<MAX_NUM_CARDS + NUM_FLAG; i++) >+ { >+ sprintf(fname, "//usr//%d.pcm", i); >+ f = filp_open(fname, O_RDWR|O_CREAT, 00); >+ >+ if (!f || !f->f_op || !f->f_op->read) >+ { >+ printk("WARNING: File (read) object is a null pointer!!!\n"); >+ continue; >+ } >+ >+ f->f_pos = 0; >+ >+ orig_fs = get_fs(); >+ set_fs(KERNEL_DS); >+ >+ if(wc->voc_buf[i]) >+ { >+ f->f_op->write(f, wc->voc_buf[i], voc_buffer_size, &f->f_pos); >+ kfree(wc->voc_buf[i]); >+ } >+ >+ set_fs(orig_fs); >+ fput(f); >+ } >+#endif >+ >+ if(cidbeforering) >+ { >+ int x; >+ for (x = 0; x < wc->max_cards/*MAX_NUM_CARDS*/; x++) >+ kfree(wc->cid_history_buf[x]); >+ } >+ >+ kfree(wc); >+ printk(KERN_INFO "Free an OpenVox A1200 card\n"); >+} >+ >+static void __devexit wctdm_remove_one(struct pci_dev *pdev) >+{ >+ struct wctdm *wc = pci_get_drvdata(pdev); >+ if (wc) { >+ >+ /* Stop any DMA */ >+ wctdm_stop_dma(wc); >+ wctdm_reset_tdm(wc); >+ >+ /* In case hardware is still there */ >+ wctdm_disable_interrupts(wc); >+ >+ /* Immediately free resources */ >+ pci_free_consistent(pdev, DAHDI_MAX_CHUNKSIZE * (MAX_NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma); >+ free_irq(pdev->irq, wc); >+ >+ /* Reset PCI chip and registers */ >+ if(wc->fwversion > 0x11) >+ outb(0x0e, wc->ioaddr + WC_CNTL); >+ else >+ { >+ wc->ledstate = 0; >+ wctdm_set_led(wc,0,0); // power off all leds. >+ } >+ >+ /* Release span, possibly delayed */ >+ if (!wc->usecount) >+ wctdm_release(wc); >+ else >+ wc->dead = 1; >+ } >+} >+ >+static struct pci_device_id wctdm_pci_tbl[] = { >+ { 0xe159, 0x0001, 0x9100, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x9519, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x95D9, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x9500, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x9532, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x8519, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x9559, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0xe159, 0x0001, 0x9599, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme }, >+ { 0 } >+}; >+ >+MODULE_DEVICE_TABLE(pci, wctdm_pci_tbl); >+ >+static struct pci_driver wctdm_driver = { >+ .name = "opvxa1200", >+ .probe = wctdm_init_one, >+ .remove = __devexit_p(wctdm_remove_one), >+ .suspend = NULL, >+ .resume = NULL, >+ .id_table = wctdm_pci_tbl, >+}; >+ >+static int __init wctdm_init(void) >+{ >+ int res; >+ int x; >+ for (x=0;x<(sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) { >+ if (!strcmp(fxo_modes[x].name, opermode)) >+ break; >+ } >+ if (x < sizeof(fxo_modes) / sizeof(fxo_modes[0])) { >+ _opermode = x; >+ } else { >+ printk(KERN_NOTICE "Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode); >+ for (x=0;x<sizeof(fxo_modes) / sizeof(fxo_modes[0]); x++) >+ printk(KERN_INFO " %s\n", fxo_modes[x].name); >+ printk(KERN_INFO "Note this option is CASE SENSITIVE!\n"); >+ return -ENODEV; >+ } >+ if (!strcmp(fxo_modes[_opermode].name, "AUSTRALIA")) { >+ boostringer=1; >+ fxshonormode=1; >+} >+ if (battdebounce == 0) { >+ battdebounce = fxo_modes[_opermode].battdebounce; >+ } >+ if (battalarm == 0) { >+ battalarm = fxo_modes[_opermode].battalarm; >+ } >+ if (battthresh == 0) { >+ battthresh = fxo_modes[_opermode].battthresh; >+ } >+ >+ res = dahdi_pci_module(&wctdm_driver); >+ if (res) >+ return -ENODEV; >+ return 0; >+} >+ >+static void __exit wctdm_cleanup(void) >+{ >+ pci_unregister_driver(&wctdm_driver); >+} >+ >+module_param(debug, int, 0600); >+module_param(loopcurrent, int, 0600); >+module_param(reversepolarity, int, 0600); >+module_param(robust, int, 0600); >+module_param(opermode, charp, 0600); >+module_param(timingonly, int, 0600); >+module_param(lowpower, int, 0600); >+module_param(boostringer, int, 0600); >+module_param(fastringer, int, 0600); >+module_param(fxshonormode, int, 0600); >+module_param(battdebounce, uint, 0600); >+module_param(battthresh, uint, 0600); >+module_param(battalarm, uint, 0600); >+module_param(ringdebounce, int, 0600); >+module_param(dialdebounce, int, 0600); >+module_param(fwringdetect, int, 0600); >+module_param(alawoverride, int, 0600); >+module_param(fastpickup, int, 0600); >+module_param(fxotxgain, int, 0600); >+module_param(fxorxgain, int, 0600); >+module_param(fxstxgain, int, 0600); >+module_param(fxsrxgain, int, 0600); >+module_param(spibyhw, int, 0600); >+module_param(usememio, int, 0600); >+module_param(cidbeforering, int, 0600); >+module_param(cidbuflen, int, 0600); >+module_param(cidtimeout, int, 0600); >+module_param(fxofullscale, int, 0600); >+module_param(fixedtimepolarity, int, 0600); >+ >+MODULE_DESCRIPTION("OpenVox A1200 Driver"); >+MODULE_AUTHOR("MiaoLin <miaolin@openvox.com.cn>"); >+MODULE_LICENSE("GPL v2"); >+ >+module_init(wctdm_init); >+module_exit(wctdm_cleanup); >diff --git a/drivers/dahdi/opvxd115/Kbuild b/drivers/dahdi/opvxd115/Kbuild >new file mode 100644 >index 0000000..474997d >--- /dev/null >+++ b/drivers/dahdi/opvxd115/Kbuild >@@ -0,0 +1,32 @@ >+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_OPVXD115) += opvxd115.o >+ >+FIRM_DIR := ../firmware >+ >+EXTRA_CFLAGS += -I$(src)/.. $(shell $(src)/../oct612x/octasic-helper cflags $(src)/../oct612x) -Wno-undef >+ >+ifeq ($(HOTPLUG_FIRMWARE),yes) >+ EXTRA_CFLAGS+=-DHOTPLUG_FIRMWARE >+endif >+ >+opvxd115-objs := base.o vpm450m.o $(shell $(src)/../oct612x/octasic-helper objects ../oct612x) >+ >+DAHDI_KERNEL_H_NAME:=kernel.h >+DAHDI_KERNEL_H_PATH:=$(DAHDI_INCLUDE)/dahdi/$(DAHDI_KERNEL_H_NAME) >+ifneq ($(DAHDI_KERNEL_H_PATH),) >+ DAHDI_SPAN_MODULE:=$(shell if grep -C 5 "struct dahdi_span {" $(DAHDI_KERNEL_H_PATH) | grep -q "struct module \*owner"; then echo "yes"; else echo "no"; fi) >+ DAHDI_SPAN_OPS:=$(shell if grep -q "struct dahdi_span_ops {" $(DAHDI_KERNEL_H_PATH); then echo "yes"; else echo "no"; fi) >+ ifeq ($(DAHDI_SPAN_MODULE),yes) >+ EXTRA_CFLAGS+=-DDAHDI_SPAN_MODULE >+ else >+ ifeq ($(DAHDI_SPAN_OPS),yes) >+ EXTRA_CFLAGS+=-DDAHDI_SPAN_OPS >+ endif >+ endif >+endif >+ >+ifneq ($(HOTPLUG_FIRMWARE),yes) >+opvxd115-objs += $(FIRM_DIR)/dahdi-fw-oct6114-032.o >+endif >+ >+$(obj)/$(FIRM_DIR)/dahdi-fw-oct6114-032.o: $(obj)/base.o >+ $(MAKE) -C $(obj)/$(FIRM_DIR) dahdi-fw-oct6114-032.o >diff --git a/drivers/dahdi/opvxd115/Makefile b/drivers/dahdi/opvxd115/Makefile >new file mode 100644 >index 0000000..baaab35 >--- /dev/null >+++ b/drivers/dahdi/opvxd115/Makefile >@@ -0,0 +1,8 @@ >+ifdef KBUILD_EXTMOD >+# We only get here on kernels 2.6.0-2.6.9 . >+# For newer kernels, Kbuild will be included directly by the kernel >+# build system. >+include $(src)/Kbuild >+ >+else >+endif >diff --git a/drivers/dahdi/opvxd115/base.c b/drivers/dahdi/opvxd115/base.c >new file mode 100644 >index 0000000..123a2e0 >--- /dev/null >+++ b/drivers/dahdi/opvxd115/base.c >@@ -0,0 +1,4903 @@ >+/* >+ * OpenVox D115P/D115E PCI/PCI-E Driver version 0.1 01/07/2011 >+ * >+ * Written by Mark Spencer <markster@digium.com> >+ * Modify from wct4xxp module by mark.liu@openvox.cn >+ >+ * Based on previous works, designs, and archetectures conceived and >+ * written by Jim Dixon <jim@lambdatel.com>. >+ * >+ * Copyright (C) 2001 Jim Dixon / Zapata Telephony. >+ * Copyright (C) 2001-2010, Digium, Inc. >+ * >+ * All rights reserved. >+ * >+ */ >+ >+/* >+ * See http://www.asterisk.org for more information about >+ * the Asterisk project. Please do not directly contact >+ * any of the maintainers of this project for assistance; >+ * the project provides a web site, mailing lists and IRC >+ * channels for your use. >+ * >+ * This program is free software, distributed under the terms of >+ * the GNU General Public License Version 2 as published by the >+ * Free Software Foundation. See the LICENSE file included with >+ * this program for more details. >+ */ >+ >+#include <linux/kernel.h> >+#include <linux/errno.h> >+#include <linux/module.h> >+#include <linux/pci.h> >+#include <linux/init.h> >+#include <linux/sched.h> >+#include <linux/interrupt.h> >+#include <linux/spinlock.h> >+#include <asm/io.h> >+#include <linux/version.h> >+#include <linux/delay.h> >+#include <linux/moduleparam.h> >+ >+#include <dahdi/kernel.h> >+ >+#include "opvxd115.h" >+#include "vpm450m.h" >+ >+/* Work queues are a way to better distribute load on SMP systems */ >+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)) >+/* >+ * Work queues can significantly improve performance and scalability >+ * on multi-processor machines, but requires bypassing some kernel >+ * API's, so it's not guaranteed to be compatible with all kernels. >+ */ >+/* #define ENABLE_WORKQUEUES */ >+#endif >+ >+/* Enable prefetching may help performance */ >+#define ENABLE_PREFETCH >+ >+/* Support first generation cards? */ >+#define SUPPORT_GEN1 >+ >+/* Define to get more attention-grabbing but slightly more I/O using >+ alarm status */ >+#define FANCY_ALARM >+ >+/* Define to support Digium Voice Processing Module expansion card */ >+#define VPM_SUPPORT >+ >+#define DEBUG_MAIN (1 << 0) >+#define DEBUG_DTMF (1 << 1) >+#define DEBUG_REGS (1 << 2) >+#define DEBUG_TSI (1 << 3) >+#define DEBUG_ECHOCAN (1 << 4) >+#define DEBUG_RBS (1 << 5) >+#define DEBUG_FRAMER (1 << 6) >+ >+/* Maximum latency to be used with Gen 5 */ >+#define GEN5_MAX_LATENCY 127 >+ >+#define T4_BASE_SIZE (DAHDI_MAX_CHUNKSIZE * 32 * 4) >+ >+#ifdef ENABLE_WORKQUEUES >+#include <linux/cpu.h> >+ >+/* XXX UGLY!!!! XXX We have to access the direct structures of the workqueue which >+ are only defined within workqueue.c because they don't give us a routine to allow us >+ to nail a work to a particular thread of the CPU. Nailing to threads gives us substantially >+ higher scalability in multi-CPU environments though! */ >+ >+/* >+ * The per-CPU workqueue (if single thread, we always use cpu 0's). >+ * >+ * The sequence counters are for flush_scheduled_work(). It wants to wait >+ * until until all currently-scheduled works are completed, but it doesn't >+ * want to be livelocked by new, incoming ones. So it waits until >+ * remove_sequence is >= the insert_sequence which pertained when >+ * flush_scheduled_work() was called. >+ */ >+ >+struct cpu_workqueue_struct { >+ >+ spinlock_t lock; >+ >+ long remove_sequence; /* Least-recently added (next to run) */ >+ long insert_sequence; /* Next to add */ >+ >+ struct list_head worklist; >+ wait_queue_head_t more_work; >+ wait_queue_head_t work_done; >+ >+ struct workqueue_struct *wq; >+ task_t *thread; >+ >+ int run_depth; /* Detect run_workqueue() recursion depth */ >+} ____cacheline_aligned; >+ >+/* >+ * The externally visible workqueue abstraction is an array of >+ * per-CPU workqueues: >+ */ >+struct workqueue_struct { >+ /* TODO: Find out exactly where the API changed */ >+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) >+ struct cpu_workqueue_struct *cpu_wq; >+#else >+ struct cpu_workqueue_struct cpu_wq[NR_CPUS]; >+#endif >+ const char *name; >+ struct list_head list; /* Empty if single thread */ >+}; >+ >+/* Preempt must be disabled. */ >+static void __t4_queue_work(struct cpu_workqueue_struct *cwq, >+ struct work_struct *work) >+{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&cwq->lock, flags); >+ work->wq_data = cwq; >+ list_add_tail(&work->entry, &cwq->worklist); >+ cwq->insert_sequence++; >+ wake_up(&cwq->more_work); >+ spin_unlock_irqrestore(&cwq->lock, flags); >+} >+ >+/* >+ * Queue work on a workqueue. Return non-zero if it was successfully >+ * added. >+ * >+ * We queue the work to the CPU it was submitted, but there is no >+ * guarantee that it will be processed by that CPU. >+ */ >+static inline int t4_queue_work(struct workqueue_struct *wq, struct work_struct *work, int cpu) >+{ >+ int ret = 0; >+ get_cpu(); >+ if (!test_and_set_bit(0, &work->pending)) { >+ BUG_ON(!list_empty(&work->entry)); >+ __t4_queue_work(wq->cpu_wq + cpu, work); >+ ret = 1; >+ } >+ put_cpu(); >+ return ret; >+} >+ >+#endif >+ >+/* >+ * Define CONFIG_EXTENDED_RESET to allow the qfalc framer extra time >+ * to reset itself upon hardware initialization. This exits for rare >+ * cases for customers who are seeing the qfalc returning unexpected >+ * information at initialization >+ */ >+#undef CONFIG_EXTENDED_RESET >+ >+static int pedanticpci = 1; >+static int debug=0; >+static int timingcable = 0; >+static int t1e1override = -1; /* 0xff for E1, 0x00 for T1 */ >+static int j1mode = 0; >+static int sigmode = FRMR_MODE_NO_ADDR_CMP; >+static int alarmdebounce = 2500; /* LOF/LFA def to 2.5s AT&T TR54016*/ >+static int losalarmdebounce = 2500;/* LOS def to 2.5s AT&T TR54016*/ >+static int aisalarmdebounce = 2500;/* AIS(blue) def to 2.5s AT&T TR54016*/ >+static int yelalarmdebounce = 500;/* RAI(yellow) def to 0.5s AT&T devguide */ >+static int max_latency = GEN5_MAX_LATENCY; /* Used to set a maximum latency (if you don't wish it to hard cap it at a certain value) in milliseconds */ >+#ifdef VPM_SUPPORT >+static int vpmsupport = 1; >+/* If set to auto, vpmdtmfsupport is enabled for VPM400M and disabled for VPM450M */ >+static int vpmdtmfsupport = -1; /* -1=auto, 0=disabled, 1=enabled*/ >+static int vpmspans = 1; >+#define VPM_DEFAULT_DTMFTHRESHOLD 1000 >+static int dtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD; >+static int lastdtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD; >+#endif >+/* Enabling bursting can more efficiently utilize PCI bus bandwidth, but >+ can also cause PCI bus starvation, especially in combination with other >+ aggressive cards. Please note that burst mode has no effect on CPU >+ utilization / max number of calls / etc. */ >+static int noburst; >+/* For 56kbps links, set this module parameter to 0x7f */ >+static int hardhdlcmode = 0xff; >+ >+static int latency = 1; >+ >+static int ms_per_irq = 1; >+ >+#ifdef FANCY_ALARM >+static int altab[] = { >+0, 0, 0, 1, 2, 3, 4, 6, 8, 9, 11, 13, 16, 18, 20, 22, 24, 25, 27, 28, 29, 30, 31, 31, 32, 31, 31, 30, 29, 28, 27, 25, 23, 22, 20, 18, 16, 13, 11, 9, 8, 6, 4, 3, 2, 1, 0, 0, >+}; >+#endif >+ >+#define MAX_SPANS 16 >+ >+#define FLAG_STARTED (1 << 0) >+#define FLAG_NMF (1 << 1) >+#define FLAG_SENDINGYELLOW (1 << 2) >+ >+ >+#define TYPE_T1 1 /* is a T1 card */ >+#define TYPE_E1 2 /* is an E1 card */ >+#define TYPE_J1 3 /* is a running J1 */ >+ >+#define FLAG_2NDGEN (1 << 3) >+#define FLAG_2PORT (1 << 4) >+#define FLAG_VPM2GEN (1 << 5) >+#define FLAG_OCTOPT (1 << 6) >+#define FLAG_3RDGEN (1 << 7) >+#define FLAG_BURST (1 << 8) >+#define FLAG_EXPRESS (1 << 9) >+#define FLAG_5THGEN (1 << 10) >+ >+#define CANARY 0xc0de >+ >+ >+#define PORTS_PER_FRAMER 4 >+ >+struct devtype { >+ char *desc; >+ unsigned int flags; >+}; >+ >+static struct devtype opvxd115 = { "OpenVox D115P/D115E ", FLAG_2NDGEN}; >+static struct devtype opvxd130 = { "OpenVox D130P/D130E", FLAG_5THGEN | FLAG_BURST | FLAG_2NDGEN | FLAG_3RDGEN}; >+ >+ >+struct t4; >+ >+struct t4_span { >+ struct t4 *owner; >+ unsigned int *writechunk; /* Double-word aligned write memory */ >+ unsigned int *readchunk; /* Double-word aligned read memory */ >+ int spantype; /* card type, T1 or E1 or J1 */ >+ int sync; >+ int psync; >+ int alarmtimer; >+ int redalarms; >+ int notclear; >+ int alarmcount; >+ int losalarmcount; >+ int aisalarmcount; >+ int yelalarmcount; >+ int spanflags; >+ int syncpos; >+#ifdef SUPPORT_GEN1 >+ int e1check; /* E1 check */ >+#endif >+ struct dahdi_span span; >+ unsigned char txsigs[16]; /* Transmit sigs */ >+ int loopupcnt; >+ int loopdowncnt; >+#ifdef SUPPORT_GEN1 >+ unsigned char ec_chunk1[31][DAHDI_CHUNKSIZE]; /* first EC chunk buffer */ >+ unsigned char ec_chunk2[31][DAHDI_CHUNKSIZE]; /* second EC chunk buffer */ >+#endif >+ int irqmisses; >+ >+ /* HDLC controller fields */ >+ struct dahdi_chan *sigchan; >+ unsigned char sigmode; >+ int sigactive; >+ int frames_out; >+ int frames_in; >+ >+#ifdef VPM_SUPPORT >+ unsigned long dtmfactive; >+ unsigned long dtmfmask; >+ unsigned long dtmfmutemask; >+ short dtmfenergy[31]; >+ short dtmfdigit[31]; >+#endif >+#ifdef ENABLE_WORKQUEUES >+ struct work_struct swork; >+#endif >+ struct dahdi_chan *chans[32]; /* Individual channels */ >+ struct dahdi_echocan_state *ec[32]; /* Echocan state for each channel */ >+}; >+ >+struct t4 { >+ /* This structure exists one per card */ >+ struct pci_dev *dev; /* Pointer to PCI device */ >+ unsigned int intcount; >+ int num; /* Which card we are */ >+ int t1e1; /* T1/E1 select pins */ >+ int globalconfig; /* Whether global setup has been done */ >+ int syncsrc; /* active sync source */ >+ struct t4_span *tspans[4]; /* Individual spans */ >+ int numspans; /* Number of spans on the card */ >+ int blinktimer; >+#ifdef FANCY_ALARM >+ int alarmpos; >+#endif >+ int irq; /* IRQ used by device */ >+ int order; /* Order */ >+ int flags; /* Device flags */ >+ unsigned int falc31 : 1; /* are we falc v3.1 (atomic not necessary) */ >+ int master; /* Are we master */ >+ int ledreg; /* LED Register */ >+ unsigned int gpio; >+ unsigned int gpioctl; >+ int e1recover; /* E1 recovery timer */ >+ spinlock_t reglock; /* lock register access */ >+ int spansstarted; /* number of spans started */ >+ volatile unsigned int *writechunk; /* Double-word aligned write memory */ >+ volatile unsigned int *readchunk; /* Double-word aligned read memory */ >+ unsigned short canary; >+#ifdef ENABLE_WORKQUEUES >+ atomic_t worklist; >+ struct workqueue_struct *workq; >+#endif >+ unsigned int passno; /* number of interrupt passes */ >+ char *variety; >+ int last0; /* for detecting double-missed IRQ */ >+ >+ /* DMA related fields */ >+ unsigned int dmactrl; >+ dma_addr_t readdma; >+ dma_addr_t writedma; >+ unsigned long memaddr; /* Base address of card */ >+ unsigned long memlen; >+ __iomem volatile unsigned int *membase; /* Base address of card */ >+ >+ /* Add this for our softlockup protector */ >+ unsigned int oct_rw_count; >+ >+ /* Flags for our bottom half */ >+ unsigned long checkflag; >+ struct tasklet_struct t4_tlet; >+ unsigned int vpm400checkstatus; >+ /* Latency related additions */ >+ unsigned char rxident; >+ unsigned char lastindex; >+ int numbufs; >+ int needed_latency; >+ >+#ifdef VPM_SUPPORT >+ struct vpm450m *vpm450m; >+ int vpm; >+#endif >+ >+}; >+ >+#define T4_VPM_PRESENT (1 << 28) >+ >+#ifdef VPM_SUPPORT >+static void t4_vpm400_init(struct t4 *wc); >+static void t4_vpm450_init(struct t4 *wc); >+static void t4_vpm_set_dtmf_threshold(struct t4 *wc, unsigned int threshold); >+ >+static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec); >+ >+static const struct dahdi_echocan_features vpm400m_ec_features = { >+ .NLP_automatic = 1, >+ .CED_tx_detect = 1, >+ .CED_rx_detect = 1, >+}; >+ >+static const struct dahdi_echocan_features vpm450m_ec_features = { >+ .NLP_automatic = 1, >+ .CED_tx_detect = 1, >+ .CED_rx_detect = 1, >+}; >+ >+static const struct dahdi_echocan_ops vpm400m_ec_ops = { >+ .echocan_free = echocan_free, >+}; >+ >+static const struct dahdi_echocan_ops vpm450m_ec_ops = { >+ .echocan_free = echocan_free, >+}; >+#endif >+ >+static void __set_clear(struct t4 *wc, int span); >+static int t4_startup(struct file *file, struct dahdi_span *span); >+static int t4_shutdown(struct dahdi_span *span); >+static int t4_rbsbits(struct dahdi_chan *chan, int bits); >+static int t4_maint(struct dahdi_span *span, int cmd); >+static int t4_clear_maint(struct dahdi_span *span); >+#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) >+static int t4_reset_counters(struct dahdi_span *span); >+#endif >+#ifdef SUPPORT_GEN1 >+static int t4_reset_dma(struct t4 *wc); >+#endif >+static void t4_hdlc_hard_xmit(struct dahdi_chan *chan); >+static int t4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data); >+static void t4_tsi_assign(struct t4 *wc, int fromspan, int fromchan, int tospan, int tochan); >+static void t4_tsi_unassign(struct t4 *wc, int tospan, int tochan); >+static void __t4_set_rclk_src(struct t4 *wc, int span); >+static void __t4_set_sclk_src(struct t4 *wc, int mode, int master, int slave); >+static void t4_check_alarms(struct t4 *wc, int span); >+static void t4_check_sigbits(struct t4 *wc, int span); >+ >+#define WC_RDADDR 0 >+#define WC_WRADDR 1 >+#define WC_COUNT 2 >+#define WC_DMACTRL 3 >+#define WC_INTR 4 >+/* #define WC_GPIO 5 */ >+#define WC_VERSION 6 >+#define WC_LEDS 7 >+#define WC_GPIOCTL 8 >+#define WC_GPIO 9 >+#define WC_LADDR 10 >+#define WC_LDATA 11 >+#define WC_LCS (1 << 11) >+#define WC_LCS2 (1 << 12) >+#define WC_LALE (1 << 13) >+#define WC_LFRMR_CS (1 << 10) /* Framer's ChipSelect signal */ >+#define WC_ACTIVATE (1 << 12) >+#define WC_LREAD (1 << 15) >+#define WC_LWRITE (1 << 16) >+ >+#define WC_OFF (0) >+#define WC_RED (1) >+#define WC_GREEN (2) >+#define WC_YELLOW (3) >+ >+#define WC_RECOVER 0 >+#define WC_SELF 1 >+ >+#define LIM0_T 0x36 /* Line interface mode 0 register */ >+#define LIM0_LL (1 << 1) /* Local Loop */ >+#define LIM1_T 0x37 /* Line interface mode 1 register */ >+#define LIM1_RL (1 << 1) /* Remote Loop */ >+ >+#define FMR0 0x1C /* Framer Mode Register 0 */ >+#define FMR0_SIM (1 << 0) /* Alarm Simulation */ >+#define FMR1_T 0x1D /* Framer Mode Register 1 */ >+#define FMR1_ECM (1 << 2) /* Error Counter 1sec Interrupt Enable */ >+#define DEC_T 0x60 /* Diable Error Counter */ >+#define IERR_T 0x1B /* Single Bit Defect Insertion Register */ >+#define IBV 0 /* Bipolar violation */ >+#define IPE (1 << 1) /* PRBS defect */ >+#define ICASE (1 << 2) /* CAS defect */ >+#define ICRCE (1 << 3) /* CRC defect */ >+#define IMFE (1 << 4) /* Multiframe defect */ >+#define IFASE (1 << 5) /* FAS defect */ >+#define ISR3_SEC (1 << 6) /* Internal one-second interrupt bit mask */ >+#define ISR3_ES (1 << 7) /* Errored Second interrupt bit mask */ >+#define ESM 0x47 /* Errored Second mask register */ >+ >+#define FMR2_T 0x1E /* Framer Mode Register 2 */ >+#define FMR2_PLB (1 << 2) /* Framer Mode Register 2 */ >+ >+#define FECL_T 0x50 /* Framing Error Counter Lower Byte */ >+#define FECH_T 0x51 /* Framing Error Counter Higher Byte */ >+#define CVCL_T 0x52 /* Code Violation Counter Lower Byte */ >+#define CVCH_T 0x53 /* Code Violation Counter Higher Byte */ >+#define CEC1L_T 0x54 /* CRC Error Counter 1 Lower Byte */ >+#define CEC1H_T 0x55 /* CRC Error Counter 1 Higher Byte */ >+#define EBCL_T 0x56 /* E-Bit Error Counter Lower Byte */ >+#define EBCH_T 0x57 /* E-Bit Error Counter Higher Byte */ >+#define BECL_T 0x58 /* Bit Error Counter Lower Byte */ >+#define BECH_T 0x59 /* Bit Error Counter Higher Byte */ >+#define COEC_T 0x5A /* COFA Event Counter */ >+#define PRBSSTA_T 0xDA /* PRBS Status Register */ >+ >+#define LCR1_T 0x3B /* Loop Code Register 1 */ >+#define EPRM (1 << 7) /* Enable PRBS rx */ >+#define XPRBS (1 << 6) /* Enable PRBS tx */ >+#define FLLB (1 << 1) /* Framed line loop/Invert */ >+#define LLBP (1 << 0) /* Line Loopback Pattern */ >+#define TPC0_T 0xA8 /* Test Pattern Control Register */ >+#define FRA (1 << 6) /* Framed/Unframed Selection */ >+#define PRBS23 (3 << 4) /* Pattern selection (23 poly) */ >+#define PRM (1 << 2) /* Non framed mode */ >+#define FRS1_T 0x4D /* Framer Receive Status Reg 1 */ >+#define LLBDD (1 << 4) >+#define LLBAD (1 << 3) >+ >+#define MAX_T4_CARDS 64 >+ >+static void t4_isr_bh(unsigned long data); >+ >+static struct t4 *cards[MAX_T4_CARDS]; >+ >+ >+#define MAX_TDM_CHAN 32 >+#define MAX_DTMF_DET 16 >+ >+#define HDLC_IMR0_MASK (FRMR_IMR0_RME | FRMR_IMR0_RPF) >+#if 0 >+#define HDLC_IMR1_MASK (FRMR_IMR1_ALLS | FRMR_IMR1_XDU | FRMR_IMR1_XPR) >+#else >+#define HDLC_IMR1_MASK (FRMR_IMR1_XDU | FRMR_IMR1_XPR) >+#endif >+ >+static inline unsigned int __t4_pci_in(struct t4 *wc, const unsigned int addr) >+{ >+ unsigned int res = readl(&wc->membase[addr]); >+ return res; >+} >+ >+static inline void __t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value) >+{ >+ unsigned int tmp; >+ writel(value, &wc->membase[addr]); >+ if (pedanticpci) { >+ tmp = __t4_pci_in(wc, WC_VERSION); >+ if ((tmp & 0xffff0000) != 0xc01a0000) >+ dev_notice(&wc->dev->dev, >+ "Version Synchronization Error!\n"); >+ } >+#if 0 >+ tmp = __t4_pci_in(wc, addr); >+ if ((value != tmp) && (addr != WC_LEDS) && (addr != WC_LDATA) && >+ (addr != WC_GPIO) && (addr != WC_INTR)) >+ dev_info(&wc->dev->dev, "Tried to load %08x into %08x, " >+ "but got %08x instead\n", value, addr, tmp); >+#endif >+} >+ >+static inline void __t4_gpio_set(struct t4 *wc, unsigned bits, unsigned int val) >+{ >+ unsigned int newgpio; >+ newgpio = wc->gpio & (~bits); >+ newgpio |= val; >+ if (newgpio != wc->gpio) { >+ wc->gpio = newgpio; >+ __t4_pci_out(wc, WC_GPIO, wc->gpio); >+ } >+} >+ >+static inline void __t4_gpio_setdir(struct t4 *wc, unsigned int bits, unsigned int val) >+{ >+ unsigned int newgpioctl; >+ newgpioctl = wc->gpioctl & (~bits); >+ newgpioctl |= val; >+ if (newgpioctl != wc->gpioctl) { >+ wc->gpioctl = newgpioctl; >+ __t4_pci_out(wc, WC_GPIOCTL, wc->gpioctl); >+ } >+} >+ >+static inline void t4_gpio_setdir(struct t4 *wc, unsigned int bits, unsigned int val) >+{ >+ unsigned long flags; >+ spin_lock_irqsave(&wc->reglock, flags); >+ __t4_gpio_setdir(wc, bits, val); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+} >+ >+static inline void t4_gpio_set(struct t4 *wc, unsigned int bits, unsigned int val) >+{ >+ unsigned long flags; >+ spin_lock_irqsave(&wc->reglock, flags); >+ __t4_gpio_set(wc, bits, val); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+} >+ >+static inline void t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value) >+{ >+ unsigned long flags; >+ spin_lock_irqsave(&wc->reglock, flags); >+ __t4_pci_out(wc, addr, value); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+} >+ >+static inline void __t4_set_led(struct t4 *wc, int span, int color) >+{ >+ int oldreg = wc->ledreg; >+ wc->ledreg &= ~(0x3 << (span << 1)); >+ wc->ledreg |= (color << (span << 1)); >+ if (oldreg != wc->ledreg) >+ __t4_pci_out(wc, WC_LEDS, wc->ledreg); >+} >+ >+static inline void t4_activate(struct t4 *wc) >+{ >+ wc->ledreg |= WC_ACTIVATE; >+ t4_pci_out(wc, WC_LEDS, wc->ledreg); >+} >+ >+static inline unsigned int t4_pci_in(struct t4 *wc, const unsigned int addr) >+{ >+ unsigned int ret; >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ ret = __t4_pci_in(wc, addr); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ return ret; >+} >+ >+static inline unsigned int __t4_framer_in(struct t4 *wc, int unit, const unsigned int addr) >+{ >+ unsigned int ret; >+ unit &= 0x3; >+ __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); >+ if (!pedanticpci) >+ __t4_pci_in(wc, WC_VERSION); >+ __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | WC_LFRMR_CS | WC_LREAD); >+ if (!pedanticpci) { >+ __t4_pci_in(wc, WC_VERSION); >+ } else { >+ __t4_pci_out(wc, WC_VERSION, 0); >+ } >+ ret = __t4_pci_in(wc, WC_LDATA); >+ __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); >+ >+ if (unlikely(debug & DEBUG_REGS)) >+ dev_info(&wc->dev->dev, "Reading unit %d address %02x is " >+ "%02x\n", unit, addr, ret & 0xff); >+ >+ if (!pedanticpci) >+ __t4_pci_in(wc, WC_VERSION); >+ >+ return ret & 0xff; >+} >+ >+static inline unsigned int t4_framer_in(struct t4 *wc, int unit, const unsigned int addr) >+{ >+ unsigned long flags; >+ unsigned int ret; >+ spin_lock_irqsave(&wc->reglock, flags); >+ ret = __t4_framer_in(wc, unit, addr); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ return ret; >+ >+} >+ >+static inline void __t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) >+{ >+ unit &= 0x3; >+ if (unlikely(debug & DEBUG_REGS)) >+ dev_info(&wc->dev->dev, "Writing %02x to address %02x of " >+ "unit %d\n", value, addr, unit); >+ __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); >+ __t4_pci_out(wc, WC_LDATA, value); >+ if (!pedanticpci) >+ __t4_pci_in(wc, WC_VERSION); >+ __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | WC_LFRMR_CS | WC_LWRITE); >+ if (!pedanticpci) >+ __t4_pci_in(wc, WC_VERSION); >+ __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); >+ if (!pedanticpci) >+ __t4_pci_in(wc, WC_VERSION); >+ if (unlikely(debug & DEBUG_REGS)) >+ dev_info(&wc->dev->dev, "Write complete\n"); >+#if 0 >+ if ((addr != FRMR_TXFIFO) && (addr != FRMR_CMDR) && (addr != 0xbc)) >+ { unsigned int tmp; >+ tmp = __t4_framer_in(wc, unit, addr); >+ if (tmp != value) { >+ dev_notice(&wc->dev->dev, "Expected %d from unit %d " >+ "register %d but got %d instead\n", >+ value, unit, addr, tmp); >+ } } >+#endif >+} >+ >+static inline void t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) >+{ >+ unsigned long flags; >+ spin_lock_irqsave(&wc->reglock, flags); >+ __t4_framer_out(wc, unit, addr, value); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+} >+ >+#ifdef VPM_SUPPORT >+ >+static inline void wait_a_little(void) >+{ >+ unsigned long newjiffies=jiffies+2; >+ while(jiffies < newjiffies); >+} >+ >+static inline unsigned int __t4_vpm_in(struct t4 *wc, int unit, const unsigned int addr) >+{ >+ unsigned int ret; >+ unit &= 0x7; >+ __t4_pci_out(wc, WC_LADDR, (addr & 0x1ff) | ( unit << 12)); >+ __t4_pci_out(wc, WC_LADDR, (addr & 0x1ff) | ( unit << 12) | (1 << 11) | WC_LREAD); >+ ret = __t4_pci_in(wc, WC_LDATA); >+ __t4_pci_out(wc, WC_LADDR, 0); >+ return ret & 0xff; >+} >+ >+static inline void __t4_raw_oct_out(struct t4 *wc, const unsigned int addr, const unsigned int value) >+{ >+ int octopt = wc->tspans[0]->spanflags & FLAG_OCTOPT; >+ if (!octopt) >+ __t4_gpio_set(wc, 0xff, (addr >> 8)); >+ __t4_pci_out(wc, WC_LDATA, 0x10000 | (addr & 0xffff)); >+ if (!octopt) >+ __t4_pci_out(wc, WC_LADDR, (WC_LWRITE)); >+ __t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE)); >+ if (!pedanticpci) >+ __t4_pci_in(wc, WC_VERSION); >+ if (!octopt) >+ __t4_gpio_set(wc, 0xff, (value >> 8)); >+ __t4_pci_out(wc, WC_LDATA, (value & 0xffff)); >+ __t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE | WC_LCS)); >+ if (!pedanticpci) >+ __t4_pci_in(wc, WC_VERSION); >+ __t4_pci_out(wc, WC_LADDR, (0)); >+ if (!pedanticpci) >+ __t4_pci_in(wc, WC_VERSION); >+} >+ >+static inline unsigned int __t4_raw_oct_in(struct t4 *wc, const unsigned int addr) >+{ >+ unsigned int ret; >+ int octopt = wc->tspans[0]->spanflags & FLAG_OCTOPT; >+ if (!octopt) >+ __t4_gpio_set(wc, 0xff, (addr >> 8)); >+ __t4_pci_out(wc, WC_LDATA, 0x10000 | (addr & 0xffff)); >+ if (!octopt) >+ __t4_pci_out(wc, WC_LADDR, (WC_LWRITE)); >+ if (!pedanticpci) >+ __t4_pci_in(wc, WC_VERSION); >+ __t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE)); >+ if (!pedanticpci) >+ __t4_pci_in(wc, WC_VERSION); >+#ifdef PEDANTIC_OCTASIC_CHECKING >+ __t4_pci_out(wc, WC_LADDR, (WC_LALE)); >+ if (!pedanticpci) >+ __t4_pci_in(wc, WC_VERSION); >+#endif >+ if (!octopt) { >+ __t4_gpio_setdir(wc, 0xff, 0x00); >+ __t4_gpio_set(wc, 0xff, 0x00); >+ } >+ __t4_pci_out(wc, WC_LADDR, (WC_LREAD | WC_LALE | WC_LCS)); >+ if (!pedanticpci) >+ __t4_pci_in(wc, WC_VERSION); >+ if (octopt) { >+ ret = __t4_pci_in(wc, WC_LDATA) & 0xffff; >+ } else { >+ ret = __t4_pci_in(wc, WC_LDATA) & 0xff; >+ ret |= (__t4_pci_in(wc, WC_GPIO) & 0xff) << 8; >+ } >+ __t4_pci_out(wc, WC_LADDR, (0)); >+ if (!pedanticpci) >+ __t4_pci_in(wc, WC_VERSION); >+ if (!octopt) >+ __t4_gpio_setdir(wc, 0xff, 0xff); >+ return ret & 0xffff; >+} >+ >+static inline unsigned int __t4_oct_in(struct t4 *wc, unsigned int addr) >+{ >+#ifdef PEDANTIC_OCTASIC_CHECKING >+ int count = 1000; >+#endif >+ __t4_raw_oct_out(wc, 0x0008, (addr >> 20)); >+ __t4_raw_oct_out(wc, 0x000a, (addr >> 4) & ((1 << 16) - 1)); >+ __t4_raw_oct_out(wc, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (1)); >+#ifdef PEDANTIC_OCTASIC_CHECKING >+ while((__t4_raw_oct_in(wc, 0x0000) & (1 << 8)) && --count); >+ if (count != 1000) >+ dev_notice(&wc->dev->dev, "Yah, read can be slow...\n"); >+ if (!count) >+ dev_notice(&wc->dev->dev, "Read timed out!\n"); >+#endif >+ return __t4_raw_oct_in(wc, 0x0004); >+} >+ >+static inline unsigned int t4_oct_in(struct t4 *wc, const unsigned int addr) >+{ >+ unsigned long flags; >+ unsigned int ret; >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ ret = __t4_oct_in(wc, addr); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ return ret; >+} >+ >+static inline unsigned int t4_vpm_in(struct t4 *wc, int unit, const unsigned int addr) >+{ >+ unsigned long flags; >+ unsigned int ret; >+ spin_lock_irqsave(&wc->reglock, flags); >+ ret = __t4_vpm_in(wc, unit, addr); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ return ret; >+} >+ >+static inline void __t4_vpm_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) >+{ >+ unit &= 0x7; >+ if (debug & DEBUG_REGS) >+ dev_notice(&wc->dev->dev, "Writing %02x to address %02x of " >+ "ec unit %d\n", value, addr, unit); >+ __t4_pci_out(wc, WC_LADDR, (addr & 0xff)); >+ __t4_pci_out(wc, WC_LDATA, value); >+ __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11)); >+ __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11) | WC_LWRITE); >+ __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11)); >+ __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff)); >+ __t4_pci_out(wc, WC_LADDR, 0); >+ if (debug & DEBUG_REGS) >+ dev_notice(&wc->dev->dev, "Write complete\n"); >+ >+ >+#if 0 >+ { unsigned int tmp; >+ tmp = t4_vpm_in(wc, unit, addr); >+ if (tmp != value) { >+ dev_notice(&wc->dev->dev, "Expected %d from unit %d echo " >+ "register %d but got %d instead\n", >+ value, unit, addr, tmp); >+ } } >+#endif >+} >+ >+static inline void __t4_oct_out(struct t4 *wc, unsigned int addr, unsigned int value) >+{ >+#ifdef PEDANTIC_OCTASIC_CHECKING >+ int count = 1000; >+#endif >+ __t4_raw_oct_out(wc, 0x0008, (addr >> 20)); >+ __t4_raw_oct_out(wc, 0x000a, (addr >> 4) & ((1 << 16) - 1)); >+ __t4_raw_oct_out(wc, 0x0004, value); >+ __t4_raw_oct_out(wc, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (3 << 12) | 1); >+#ifdef PEDANTIC_OCTASIC_CHECKING >+ while((__t4_raw_oct_in(wc, 0x0000) & (1 << 8)) && --count); >+ if (count != 1000) >+ dev_notice(&wc->dev->dev, "Yah, write can be slow\n"); >+ if (!count) >+ dev_notice(&wc->dev->dev, "Write timed out!\n"); >+#endif >+} >+ >+static inline void t4_oct_out(struct t4 *wc, const unsigned int addr, const unsigned int value) >+{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ __t4_oct_out(wc, addr, value); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+} >+ >+static inline void t4_vpm_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) >+{ >+ unsigned long flags; >+ spin_lock_irqsave(&wc->reglock, flags); >+ __t4_vpm_out(wc, unit, addr, value); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+} >+ >+static const char vpm_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', '*', '#'}; >+ >+static void t4_check_vpm450(struct t4 *wc) >+{ >+ int channel, tone, start, span; >+ >+ if (vpm450m_checkirq(wc->vpm450m)) { >+ while(vpm450m_getdtmf(wc->vpm450m, &channel, &tone, &start)) { >+ span = channel & 0x3; >+ channel >>= 2; >+ if (!wc->t1e1) >+ channel -= 5; >+ else >+ channel -= 1; >+ if (unlikely(debug)) >+ dev_info(&wc->dev->dev, "Got tone %s of '%c' " >+ "on channel %d of span %d\n", >+ (start ? "START" : "STOP"), >+ tone, channel, span + 1); >+ if (test_bit(channel, &wc->tspans[span]->dtmfmask) && (tone != 'u')) { >+ if (start) { >+ /* The octasic is supposed to mute us, but... Yah, you >+ guessed it. */ >+ if (test_bit(channel, &wc->tspans[span]->dtmfmutemask)) { >+ unsigned long flags; >+ struct dahdi_chan *chan = wc->tspans[span]->span.chans[channel]; >+ int y; >+ spin_lock_irqsave(&chan->lock, flags); >+ for (y=0;y<chan->numbufs;y++) { >+ if ((chan->inreadbuf > -1) && (chan->readidx[y])) >+ memset(chan->readbuf[chan->inreadbuf], DAHDI_XLAW(0, chan), chan->readidx[y]); >+ } >+ spin_unlock_irqrestore(&chan->lock, flags); >+ } >+ set_bit(channel, &wc->tspans[span]->dtmfactive); >+ dahdi_qevent_lock(wc->tspans[span]->span.chans[channel], (DAHDI_EVENT_DTMFDOWN | tone)); >+ } else { >+ clear_bit(channel, &wc->tspans[span]->dtmfactive); >+ dahdi_qevent_lock(wc->tspans[span]->span.chans[channel], (DAHDI_EVENT_DTMFUP | tone)); >+ } >+ } >+ } >+ } >+} >+ >+static void t4_check_vpm400(struct t4 *wc, unsigned int newio) >+{ >+ unsigned int digit, regval = 0; >+ unsigned int regbyte; >+ int x, i; >+ short energy=0; >+ static unsigned int lastio = 0; >+ struct t4_span *ts; >+ >+ if (debug && (newio != lastio)) >+ dev_notice(&wc->dev->dev, "Last was %08x, new is %08x\n", >+ lastio, newio); >+ >+ lastio = newio; >+ >+ for(x = 0; x < 8; x++) { >+ if (newio & (1 << (7 - x))) >+ continue; >+ ts = wc->tspans[x%4]; >+ /* Start of DTMF detection process */ >+ regbyte = t4_vpm_in(wc, x, 0xb8); >+ t4_vpm_out(wc, x, 0xb8, regbyte); /* Write 1 to clear */ >+ regval = regbyte << 8; >+ regbyte = t4_vpm_in(wc, x, 0xb9); >+ t4_vpm_out(wc, x, 0xb9, regbyte); >+ regval |= regbyte; >+ >+ for(i = 0; (i < MAX_DTMF_DET) && regval; i++) { >+ if(regval & 0x0001) { >+ int channel = (i << 1) + (x >> 2); >+ int base = channel - 1; >+ >+ if (!wc->t1e1) >+ base -= 4; >+ regbyte = t4_vpm_in(wc, x, 0xa8 + i); >+ digit = vpm_digits[regbyte]; >+ if (!(wc->tspans[0]->spanflags & FLAG_VPM2GEN)) { >+ energy = t4_vpm_in(wc, x, 0x58 + channel); >+ energy = DAHDI_XLAW(energy, ts->chans[0]); >+ ts->dtmfenergy[base] = energy; >+ } >+ set_bit(base, &ts->dtmfactive); >+ if (ts->dtmfdigit[base]) { >+ if (ts->dtmfmask & (1 << base)) >+ dahdi_qevent_lock(ts->span.chans[base], (DAHDI_EVENT_DTMFUP | ts->dtmfdigit[base])); >+ } >+ ts->dtmfdigit[base] = digit; >+ if (test_bit(base, &ts->dtmfmask)) >+ dahdi_qevent_lock(ts->span.chans[base], (DAHDI_EVENT_DTMFDOWN | digit)); >+ if (test_bit(base, &ts->dtmfmutemask)) { >+ /* Mute active receive buffer*/ >+ unsigned long flags; >+ struct dahdi_chan *chan = ts->span.chans[base]; >+ int y; >+ spin_lock_irqsave(&chan->lock, flags); >+ for (y=0;y<chan->numbufs;y++) { >+ if ((chan->inreadbuf > -1) && (chan->readidx[y])) >+ memset(chan->readbuf[chan->inreadbuf], DAHDI_XLAW(0, chan), chan->readidx[y]); >+ } >+ spin_unlock_irqrestore(&chan->lock, flags); >+ } >+ if (debug) >+ dev_notice(&wc->dev->dev, "Digit " >+ "Seen: %d, Span: %d, channel:" >+ " %d, energy: %02x, 'channel " >+ "%d' chip %d\n", digit, x % 4, >+ base + 1, energy, channel, x); >+ >+ } >+ regval = regval >> 1; >+ } >+ if (!(wc->tspans[0]->spanflags & FLAG_VPM2GEN)) >+ continue; >+ >+ /* Start of DTMF off detection process */ >+ regbyte = t4_vpm_in(wc, x, 0xbc); >+ t4_vpm_out(wc, x, 0xbc, regbyte); /* Write 1 to clear */ >+ regval = regbyte << 8; >+ regbyte = t4_vpm_in(wc, x, 0xbd); >+ t4_vpm_out(wc, x, 0xbd, regbyte); >+ regval |= regbyte; >+ >+ for(i = 0; (i < MAX_DTMF_DET) && regval; i++) { >+ if(regval & 0x0001) { >+ int channel = (i << 1) + (x >> 2); >+ int base = channel - 1; >+ >+ if (!wc->t1e1) >+ base -= 4; >+ clear_bit(base, &ts->dtmfactive); >+ if (ts->dtmfdigit[base]) { >+ if (test_bit(base, &ts->dtmfmask)) >+ dahdi_qevent_lock(ts->span.chans[base], (DAHDI_EVENT_DTMFUP | ts->dtmfdigit[base])); >+ } >+ digit = ts->dtmfdigit[base]; >+ ts->dtmfdigit[base] = 0; >+ if (debug) >+ dev_notice(&wc->dev->dev, "Digit " >+ "Gone: %d, Span: %d, channel:" >+ " %d, energy: %02x, 'channel " >+ "%d' chip %d\n", digit, x % 4, >+ base + 1, energy, channel, x); >+ >+ } >+ regval = regval >> 1; >+ } >+ >+ } >+} >+#endif >+ >+static void hdlc_stop(struct t4 *wc, unsigned int span) >+{ >+ struct t4_span *t = wc->tspans[span]; >+ unsigned char imr0, imr1, mode; >+ int i = 0; >+ >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "Stopping HDLC controller on span " >+ "%d\n", span+1); >+ >+ /* Clear receive and transmit timeslots */ >+ for (i = 0; i < 4; i++) { >+ t4_framer_out(wc, span, FRMR_RTR_BASE + i, 0x00); >+ t4_framer_out(wc, span, FRMR_TTR_BASE + i, 0x00); >+ } >+ >+ imr0 = t4_framer_in(wc, span, FRMR_IMR0); >+ imr1 = t4_framer_in(wc, span, FRMR_IMR1); >+ >+ /* Disable HDLC interrupts */ >+ imr0 |= HDLC_IMR0_MASK; >+ t4_framer_out(wc, span, FRMR_IMR0, imr0); >+ >+ imr1 |= HDLC_IMR1_MASK; >+ t4_framer_out(wc, span, FRMR_IMR1, imr1); >+ >+ mode = t4_framer_in(wc, span, FRMR_MODE); >+ mode &= ~FRMR_MODE_HRAC; >+ t4_framer_out(wc, span, FRMR_MODE, mode); >+ >+ t->sigactive = 0; >+} >+ >+static inline void __t4_framer_cmd(struct t4 *wc, unsigned int span, int cmd) >+{ >+ __t4_framer_out(wc, span, FRMR_CMDR, cmd); >+} >+ >+static inline void t4_framer_cmd_wait(struct t4 *wc, unsigned int span, int cmd) >+{ >+ int sis; >+ int loops = 0; >+ >+ /* XXX could be time consuming XXX */ >+ for (;;) { >+ sis = t4_framer_in(wc, span, FRMR_SIS); >+ if (!(sis & 0x04)) >+ break; >+ if (!loops++ && (debug & DEBUG_FRAMER)) { >+ dev_notice(&wc->dev->dev, "!!!SIS Waiting before cmd " >+ "%02x\n", cmd); >+ } >+ } >+ if (loops && (debug & DEBUG_FRAMER)) >+ dev_notice(&wc->dev->dev, "!!!SIS waited %d loops\n", loops); >+ >+ t4_framer_out(wc, span, FRMR_CMDR, cmd); >+} >+ >+static int hdlc_start(struct t4 *wc, unsigned int span, struct dahdi_chan *chan, unsigned char mode) >+{ >+ struct t4_span *t = wc->tspans[span]; >+ unsigned char imr0, imr1; >+ int offset = chan->chanpos; >+ unsigned long flags; >+ >+ if (debug & DEBUG_FRAMER) >+ dev_info(&wc->dev->dev, "Starting HDLC controller for channel " >+ "%d span %d\n", offset, span+1); >+ >+ if (mode != FRMR_MODE_NO_ADDR_CMP) >+ return -1; >+ >+ mode |= FRMR_MODE_HRAC; >+ >+ /* Make sure we're in the right mode */ >+ t4_framer_out(wc, span, FRMR_MODE, mode); >+ t4_framer_out(wc, span, FRMR_TSEO, 0x00); >+ t4_framer_out(wc, span, FRMR_TSBS1, hardhdlcmode); >+ >+ /* Set the interframe gaps, etc */ >+ t4_framer_out(wc, span, FRMR_CCR1, FRMR_CCR1_ITF|FRMR_CCR1_EITS); >+ >+ t4_framer_out(wc, span, FRMR_CCR2, FRMR_CCR2_RCRC); >+ >+ /* Set up the time slot that we want to tx/rx on */ >+ t4_framer_out(wc, span, FRMR_TTR_BASE + (offset / 8), (0x80 >> (offset % 8))); >+ t4_framer_out(wc, span, FRMR_RTR_BASE + (offset / 8), (0x80 >> (offset % 8))); >+ >+ imr0 = t4_framer_in(wc, span, FRMR_IMR0); >+ imr1 = t4_framer_in(wc, span, FRMR_IMR1); >+ >+ /* Enable our interrupts again */ >+ imr0 &= ~HDLC_IMR0_MASK; >+ t4_framer_out(wc, span, FRMR_IMR0, imr0); >+ >+ imr1 &= ~HDLC_IMR1_MASK; >+ t4_framer_out(wc, span, FRMR_IMR1, imr1); >+ >+ /* Reset the signaling controller */ >+ t4_framer_cmd_wait(wc, span, FRMR_CMDR_SRES); >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ t->sigchan = chan; >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ >+ t->sigactive = 0; >+ >+ return 0; >+} >+ >+static void __set_clear(struct t4 *wc, int span) >+{ >+ int i,j; >+ int oldnotclear; >+ unsigned short val=0; >+ struct t4_span *ts = wc->tspans[span]; >+ >+ oldnotclear = ts->notclear; >+ if ((ts->spantype == TYPE_T1) || (ts->spantype == TYPE_J1)) { >+ for (i=0;i<24;i++) { >+ j = (i/8); >+ if (ts->span.chans[i]->flags & DAHDI_FLAG_CLEAR) { >+ val |= 1 << (7 - (i % 8)); >+ ts->notclear &= ~(1 << i); >+ } else >+ ts->notclear |= (1 << i); >+ if ((i % 8)==7) { >+ if (debug) >+ dev_notice(&wc->dev->dev, "Putting %d " >+ "in register %02x on span %d" >+ "\n", val, 0x2f + j, span + 1); >+ __t4_framer_out(wc, span, 0x2f + j, val); >+ val = 0; >+ } >+ } >+ } else { >+ for (i=0;i<31;i++) { >+ if (ts->span.chans[i]->flags & DAHDI_FLAG_CLEAR) >+ ts->notclear &= ~(1 << i); >+ else >+ ts->notclear |= (1 << i); >+ } >+ } >+ if (ts->notclear != oldnotclear) { >+ unsigned char reg; >+ reg = __t4_framer_in(wc, span, FRMR_IMR0); >+ if (ts->notclear) >+ reg &= ~0x08; >+ else >+ reg |= 0x08; >+ __t4_framer_out(wc, span, FRMR_IMR0, reg); >+ } >+} >+ >+#if 0 >+static void set_clear(struct t4 *wc, int span) >+{ >+ unsigned long flags; >+ spin_lock_irqsave(&wc->reglock, flags); >+ __set_clear(wc, span); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+} >+#endif >+ >+static int t4_dacs(struct dahdi_chan *dst, struct dahdi_chan *src) >+{ >+ struct t4 *wc; >+ struct t4_span *ts; >+ wc = dst->pvt; >+ ts = wc->tspans[dst->span->offset]; >+ if (src && (src->pvt != dst->pvt)) { >+ if (ts->spanflags & FLAG_2NDGEN) >+ t4_tsi_unassign(wc, dst->span->offset, dst->chanpos); >+ wc = src->pvt; >+ if (ts->spanflags & FLAG_2NDGEN) >+ t4_tsi_unassign(wc, src->span->offset, src->chanpos); >+ if (debug) >+ dev_notice(&wc->dev->dev, "Unassigning %d/%d by " >+ "default and...\n", src->span->offset, >+ src->chanpos); >+ if (debug) >+ dev_notice(&wc->dev->dev, "Unassigning %d/%d by " >+ "default\n", dst->span->offset, dst->chanpos); >+ return -1; >+ } >+ if (src) { >+ t4_tsi_assign(wc, src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); >+ if (debug) >+ dev_notice(&wc->dev->dev, "Assigning channel %d/%d -> " >+ "%d/%d!\n", src->span->offset, src->chanpos, >+ dst->span->offset, dst->chanpos); >+ } else { >+ t4_tsi_unassign(wc, dst->span->offset, dst->chanpos); >+ if (debug) >+ dev_notice(&wc->dev->dev, "Unassigning channel %d/%d!" >+ "\n", dst->span->offset, dst->chanpos); >+ } >+ return 0; >+} >+ >+#ifdef VPM_SUPPORT >+ >+void oct_set_reg(void *data, unsigned int reg, unsigned int val) >+{ >+ struct t4 *wc = data; >+ t4_oct_out(wc, reg, val); >+} >+ >+unsigned int oct_get_reg(void *data, unsigned int reg) >+{ >+ struct t4 *wc = data; >+ unsigned int ret; >+ ret = t4_oct_in(wc, reg); >+ return ret; >+} >+ >+static int t4_vpm_unit(int span, int channel) >+{ >+ int unit = 0; >+ switch(vpmspans) { >+ case 4: >+ unit = span; >+ unit += (channel & 1) << 2; >+ break; >+ case 2: >+ unit = span; >+ unit += (channel & 0x3) << 1; >+ break; >+ case 1: >+ unit = span; >+ unit += (channel & 0x7); >+ } >+ return unit; >+} >+ >+static inline struct t4_span *t4_from_span(struct dahdi_span *span) >+{ >+ return container_of(span, struct t4_span, span); >+} >+ >+static int t4_echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *ecp, >+ struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec) >+{ >+ struct t4 *wc = chan->pvt; >+ struct t4_span *tspan = container_of(chan->span, struct t4_span, span); >+ int channel; >+ const struct dahdi_echocan_ops *ops; >+ const struct dahdi_echocan_features *features; >+ >+ if (!vpmsupport || !wc->vpm) >+ return -ENODEV; >+ >+ if (chan->span->offset >= vpmspans) >+ return -ENODEV; >+ >+ if (wc->vpm450m) { >+ ops = &vpm450m_ec_ops; >+ features = &vpm450m_ec_features; >+ } else { >+ ops = &vpm400m_ec_ops; >+ features = &vpm400m_ec_features; >+ } >+ >+ if (ecp->param_count > 0) { >+ dev_warn(&wc->dev->dev, "echo canceller does not support " >+ "parameters; failing request\n"); >+ return -EINVAL; >+ } >+ >+ *ec = tspan->ec[chan->chanpos - 1]; >+ (*ec)->ops = ops; >+ (*ec)->features = *features; >+ >+ channel = wc->t1e1 ? chan->chanpos : chan->chanpos + 4; >+ >+ if (wc->vpm450m) { >+ channel = channel << 2; >+ channel |= chan->span->offset; >+ if (debug & DEBUG_ECHOCAN) >+ dev_notice(&wc->dev->dev, "echocan: Card is %d, " >+ "Channel is %d, Span is %d, offset is %d " >+ "length %d\n", wc->num, chan->chanpos, >+ chan->span->offset, channel, ecp->tap_length); >+ vpm450m_setec(wc->vpm450m, channel, ecp->tap_length); >+ } else { >+ int unit = t4_vpm_unit(chan->span->offset, channel); >+ >+ if (debug & DEBUG_ECHOCAN) >+ dev_notice(&wc->dev->dev, "echocan: Card is %d, " >+ "Channel is %d, Span is %d, unit is %d, " >+ "unit offset is %d length %d\n", wc->num, >+ chan->chanpos, chan->span->offset, unit, >+ channel, ecp->tap_length); >+ t4_vpm_out(wc, unit, channel, 0x3e); >+ } >+ >+ return 0; >+} >+ >+static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec) >+{ >+ struct t4 *wc = chan->pvt; >+ int channel; >+ >+ memset(ec, 0, sizeof(*ec)); >+ >+ channel = wc->t1e1 ? chan->chanpos : chan->chanpos + 4; >+ >+ if (wc->vpm450m) { >+ channel = channel << 2; >+ channel |= chan->span->offset; >+ if (debug & DEBUG_ECHOCAN) >+ dev_notice(&wc->dev->dev, "echocan: Card is %d, " >+ "Channel is %d, Span is %d, offset is %d " >+ "length 0\n", wc->num, chan->chanpos, >+ chan->span->offset, channel); >+ vpm450m_setec(wc->vpm450m, channel, 0); >+ } else { >+ int unit = t4_vpm_unit(chan->span->offset, channel); >+ >+ if (debug & DEBUG_ECHOCAN) >+ dev_notice(&wc->dev->dev, "echocan: Card is %d, " >+ "Channel is %d, Span is %d, unit is %d, " >+ "unit offset is %d length 0\n", wc->num, >+ chan->chanpos, chan->span->offset, unit, >+ channel); >+ t4_vpm_out(wc, unit, channel, 0x01); >+ } >+} >+#endif >+ >+static int t4_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) >+{ >+ struct t4_regs regs; >+ int x; >+ struct t4 *wc = chan->pvt; >+#ifdef VPM_SUPPORT >+ int j; >+ int channel; >+ struct t4_span *ts = wc->tspans[chan->span->offset]; >+#endif >+ >+#ifdef VPM_SUPPORT >+ if (dtmfthreshold == 0) >+ dtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD; >+ if (lastdtmfthreshold != dtmfthreshold) { >+ lastdtmfthreshold = dtmfthreshold; >+ t4_vpm_set_dtmf_threshold(wc, dtmfthreshold); >+ } >+#endif >+ >+ switch(cmd) { >+ case WCT4_GET_REGS: >+ for (x=0;x<NUM_PCI;x++) >+ regs.pci[x] = t4_pci_in(wc, x); >+ for (x=0;x<NUM_REGS;x++) >+ regs.regs[x] = t4_framer_in(wc, chan->span->offset, x); >+ if (copy_to_user((__user void *) data, ®s, sizeof(regs))) >+ return -EFAULT; >+ break; >+#ifdef VPM_SUPPORT >+ case DAHDI_TONEDETECT: >+ if (get_user(j, (__user int *) data)) >+ return -EFAULT; >+ if (!wc->vpm) >+ return -ENOSYS; >+ if (j && (vpmdtmfsupport == 0)) >+ return -ENOSYS; >+ if (j & DAHDI_TONEDETECT_ON) >+ set_bit(chan->chanpos - 1, &ts->dtmfmask); >+ else >+ clear_bit(chan->chanpos - 1, &ts->dtmfmask); >+ if (j & DAHDI_TONEDETECT_MUTE) >+ set_bit(chan->chanpos - 1, &ts->dtmfmutemask); >+ else >+ clear_bit(chan->chanpos - 1, &ts->dtmfmutemask); >+ if (wc->vpm450m) { >+ channel = (chan->chanpos) << 2; >+ if (!wc->t1e1) >+ channel += (4 << 2); >+ channel |= chan->span->offset; >+ vpm450m_setdtmf(wc->vpm450m, channel, j & DAHDI_TONEDETECT_ON, j & DAHDI_TONEDETECT_MUTE); >+ } >+ return 0; >+#endif >+ default: >+ return -ENOTTY; >+ } >+ return 0; >+} >+ >+static void inline t4_hdlc_xmit_fifo(struct t4 *wc, unsigned int span, struct t4_span *ts) >+{ >+ int res, i; >+ unsigned int size = 32; >+ unsigned char buf[32]; >+ >+ res = dahdi_hdlc_getbuf(ts->sigchan, buf, &size); >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "Got buffer sized %d and res %d " >+ "for %d\n", size, res, span); >+ if (size > 0) { >+ ts->sigactive = 1; >+ >+ if (debug & DEBUG_FRAMER) { >+ dev_notice(&wc->dev->dev, "TX("); >+ for (i = 0; i < size; i++) >+ dev_notice(&wc->dev->dev, "%s%02x", >+ (i ? " " : ""), buf[i]); >+ dev_notice(&wc->dev->dev, ")\n"); >+ } >+ >+ for (i = 0; i < size; i++) >+ t4_framer_out(wc, span, FRMR_TXFIFO, buf[i]); >+ >+ if (res) /* End of message */ { >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, >+ "transmiting XHF|XME\n"); >+ t4_framer_cmd_wait(wc, span, FRMR_CMDR_XHF | FRMR_CMDR_XME); >+#if 0 >+ ts->sigactive = (__t4_framer_in(wc, span, FRMR_SIS) & FRMR_SIS_XFW) ? 0 : 1; >+#endif >+ ++ts->frames_out; >+ if ((debug & DEBUG_FRAMER) && !(ts->frames_out & 0x0f)) >+ dev_notice(&wc->dev->dev, "Transmitted %d " >+ "frames on span %d\n", ts->frames_out, >+ span); >+ } else { /* Still more to transmit */ >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "transmiting XHF\n"); >+ t4_framer_cmd_wait(wc, span, FRMR_CMDR_XHF); >+ } >+ } >+ else if (res < 0) >+ ts->sigactive = 0; >+} >+ >+static void t4_hdlc_hard_xmit(struct dahdi_chan *chan) >+{ >+ struct t4 *wc = chan->pvt; >+ int span = chan->span->offset; >+ struct t4_span *ts = wc->tspans[span]; >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ if (!ts->sigchan) { >+ dev_notice(&wc->dev->dev, "t4_hdlc_hard_xmit: Invalid (NULL) " >+ "signalling channel\n"); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ return; >+ } >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "t4_hdlc_hard_xmit on channel %s " >+ "(sigchan %s), sigactive=%d\n", chan->name, >+ ts->sigchan->name, ts->sigactive); >+ >+ if ((ts->sigchan == chan) && !ts->sigactive) >+ t4_hdlc_xmit_fifo(wc, span, ts); >+} >+ >+static int t4_maint(struct dahdi_span *span, int cmd) >+{ >+ struct t4_span *ts = t4_from_span(span); >+ struct t4 *wc = ts->owner; >+ unsigned int reg; >+#ifdef DAHDI_SPAN_OPS >+ unsigned long flags; >+#endif >+ >+ if (ts->spantype == TYPE_E1) { >+ switch(cmd) { >+ case DAHDI_MAINT_NONE: >+ dev_info(&wc->dev->dev, "Clearing all maint modes\n"); >+ t4_clear_maint(span); >+ break; >+ case DAHDI_MAINT_LOCALLOOP: >+ dev_info(&wc->dev->dev, >+ "Turning on local loopback\n"); >+ t4_clear_maint(span); >+ reg = t4_framer_in(wc, span->offset, LIM0_T); >+ t4_framer_out(wc, span->offset, LIM0_T, (reg|LIM0_LL)); >+ break; >+#ifdef DAHDI_SPAN_OPS >+ case DAHDI_MAINT_NETWORKLINELOOP: >+ dev_info(&wc->dev->dev, >+ "Turning on network line loopback\n"); >+ t4_clear_maint(span); >+ reg = t4_framer_in(wc, span->offset, LIM1_T); >+ t4_framer_out(wc, span->offset, LIM1_T, (reg|LIM1_RL)); >+ break; >+ case DAHDI_MAINT_NETWORKPAYLOADLOOP: >+ dev_info(&wc->dev->dev, >+ "Turning on network payload loopback\n"); >+ t4_clear_maint(span); >+ reg = t4_framer_in(wc, span->offset, FMR2_T); >+ t4_framer_out(wc, span->offset, FMR2_T, (reg|FMR2_PLB)); >+ break; >+#endif >+ case DAHDI_MAINT_LOOPUP: >+ case DAHDI_MAINT_LOOPDOWN: >+ dev_info(&wc->dev->dev, >+ "Loopup & loopdown supported in E1 mode\n"); >+ return -ENOSYS; >+#ifdef DAHDI_SPAN_OPS >+ case DAHDI_MAINT_FAS_DEFECT: >+ t4_framer_out(wc, span->offset, IERR_T, IFASE); >+ break; >+ case DAHDI_MAINT_MULTI_DEFECT: >+ t4_framer_out(wc, span->offset, IERR_T, IMFE); >+ break; >+ case DAHDI_MAINT_CRC_DEFECT: >+ t4_framer_out(wc, span->offset, IERR_T, ICRCE); >+ break; >+ case DAHDI_MAINT_CAS_DEFECT: >+ t4_framer_out(wc, span->offset, IERR_T, ICASE); >+ break; >+ case DAHDI_MAINT_PRBS_DEFECT: >+ t4_framer_out(wc, span->offset, IERR_T, IPE); >+ break; >+ case DAHDI_MAINT_BIPOLAR_DEFECT: >+ t4_framer_out(wc, span->offset, IERR_T, IBV); >+ break; >+ case DAHDI_RESET_COUNTERS: >+ t4_reset_counters(span); >+ break; >+ case DAHDI_MAINT_ALARM_SIM: >+ dev_info(&wc->dev->dev, "Invoking alarm state"); >+ reg = t4_framer_in(wc, span->offset, FMR0); >+ t4_framer_out(wc, span->offset, FMR0, (reg|FMR0_SIM)); >+ break; >+#endif >+ default: >+ dev_info(&wc->dev->dev, >+ "Unknown E1 maint command: %d\n", cmd); >+ return -ENOSYS; >+ } >+ } else { >+ switch(cmd) { >+ case DAHDI_MAINT_NONE: >+ dev_info(&wc->dev->dev, "Clearing all maint modes\n"); >+ t4_clear_maint(span); >+ break; >+ case DAHDI_MAINT_LOCALLOOP: >+ dev_info(&wc->dev->dev, >+ "Turning on local loopback\n"); >+ t4_clear_maint(span); >+ reg = t4_framer_in(wc, span->offset, LIM0_T); >+ t4_framer_out(wc, span->offset, LIM0_T, (reg|LIM0_LL)); >+ break; >+#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) >+ case DAHDI_MAINT_NETWORKLINELOOP: >+ dev_info(&wc->dev->dev, >+ "Turning on network line loopback\n"); >+ t4_clear_maint(span); >+ reg = t4_framer_in(wc, span->offset, LIM1_T); >+ t4_framer_out(wc, span->offset, LIM1_T, (reg|LIM1_RL)); >+ break; >+ case DAHDI_MAINT_NETWORKPAYLOADLOOP: >+ dev_info(&wc->dev->dev, >+ "Turning on network payload loopback\n"); >+ t4_clear_maint(span); >+ reg = t4_framer_in(wc, span->offset, FMR2_T); >+ t4_framer_out(wc, span->offset, FMR2_T, (reg|FMR2_PLB)); >+ break; >+#endif >+ case DAHDI_MAINT_LOOPUP: >+ dev_info(&wc->dev->dev, "Transmitting loopup code\n"); >+ t4_clear_maint(span); >+ t4_framer_out(wc, span->offset, 0x21, 0x50); >+ break; >+ case DAHDI_MAINT_LOOPDOWN: >+ dev_info(&wc->dev->dev, "Transmitting loopdown code\n"); >+ t4_clear_maint(span); >+ t4_framer_out(wc, span->offset, 0x21, 0x60); >+ break; >+#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) >+ case DAHDI_MAINT_FAS_DEFECT: >+ t4_framer_out(wc, span->offset, IERR_T, IFASE); >+ break; >+ case DAHDI_MAINT_MULTI_DEFECT: >+ t4_framer_out(wc, span->offset, IERR_T, IMFE); >+ break; >+ case DAHDI_MAINT_CRC_DEFECT: >+ t4_framer_out(wc, span->offset, IERR_T, ICRCE); >+ break; >+ case DAHDI_MAINT_CAS_DEFECT: >+ t4_framer_out(wc, span->offset, IERR_T, ICASE); >+ break; >+ case DAHDI_MAINT_PRBS_DEFECT: >+ t4_framer_out(wc, span->offset, IERR_T, IPE); >+ break; >+ case DAHDI_MAINT_BIPOLAR_DEFECT: >+ t4_framer_out(wc, span->offset, IERR_T, IBV); >+ break; >+ case DAHDI_MAINT_PRBS: >+ dev_info(&wc->dev->dev, "PRBS not supported\n"); >+#if 0 >+ dev_notice(&wc->dev->dev, "Enabling PRBS!\n"); >+ span->mainttimer = 1; >+ /* Enable PRBS monitor */ >+ reg = t4_framer_in(wc, span->offset, LCR1_T); >+ reg |= EPRM; >+ >+ /* Setup PRBS xmit */ >+ t4_framer_out(wc, span->offset, TPC0_T, 0); >+ >+ /* Enable PRBS transmit */ >+ reg |= XPRBS; >+ reg &= ~LLBP; >+ reg &= ~FLLB; >+ t4_framer_out(wc, span->offset, LCR1_T, reg); >+#endif >+ return -ENOSYS; >+ case DAHDI_RESET_COUNTERS: >+ t4_reset_counters(span); >+ break; >+#endif >+#ifdef DAHDI_SPAN_OPS >+ case DAHDI_MAINT_ALARM_SIM: >+ reg = t4_framer_in(wc, span->offset, FMR0); >+ >+ /* >+ * The alarm simulation state machine requires us to >+ * bring this bit up and down for at least 1 clock cycle >+ */ >+ spin_lock_irqsave(&wc->reglock, flags); >+ __t4_framer_out(wc, span->offset, >+ FMR0, (reg | FMR0_SIM)); >+ udelay(1); >+ __t4_framer_out(wc, span->offset, >+ FMR0, (reg & ~FMR0_SIM)); >+ udelay(1); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ >+ reg = t4_framer_in(wc, span->offset, 0x4e); >+ if (debug & DEBUG_MAIN) { >+ dev_info(&wc->dev->dev, >+ "FRS2(alarm state): %d\n", >+ ((reg & 0xe0) >> 5)); >+ } >+ break; >+#endif >+ default: >+ dev_info(&wc->dev->dev, "Unknown T1 maint command:%d\n", >+ cmd); >+ break; >+ } >+ } >+ return 0; >+} >+ >+static int t4_clear_maint(struct dahdi_span *span) >+{ >+ struct t4_span *ts = t4_from_span(span); >+ struct t4 *wc = ts->owner; >+ unsigned int reg; >+ >+ /* Clear local loop */ >+ reg = t4_framer_in(wc, span->offset, LIM0_T); >+ t4_framer_out(wc, span->offset, LIM0_T, (reg & ~LIM0_LL)); >+ >+ /* Clear Remote Loop */ >+ reg = t4_framer_in(wc, span->offset, LIM1_T); >+ t4_framer_out(wc, span->offset, LIM1_T, (reg & ~LIM1_RL)); >+ >+ /* Clear Remote Payload Loop */ >+ reg = t4_framer_in(wc, span->offset, FMR2_T); >+ t4_framer_out(wc, span->offset, FMR2_T, (reg & ~FMR2_PLB)); >+ >+ /* Clear PRBS */ >+ reg = t4_framer_in(wc, span->offset, LCR1_T); >+ t4_framer_out(wc, span->offset, LCR1_T, (reg & ~(XPRBS | EPRM))); >+ >+ span->mainttimer = 0; >+ >+ return 0; >+} >+ >+#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) >+static int t4_reset_counters(struct dahdi_span *span) >+{ >+ struct t4_span *ts = t4_from_span(span); >+ memset(&ts->span.count, 0, sizeof(ts->span.count)); >+ return 0; >+} >+#endif >+ >+static int t4_rbsbits(struct dahdi_chan *chan, int bits) >+{ >+ u_char m,c; >+ int k,n,b; >+ struct t4 *wc = chan->pvt; >+ struct t4_span *ts = wc->tspans[chan->span->offset]; >+ unsigned long flags; >+ >+ if (debug & DEBUG_RBS) >+ dev_notice(&wc->dev->dev, "Setting bits to %d on channel %s\n", >+ bits, chan->name); >+ spin_lock_irqsave(&wc->reglock, flags); >+ k = chan->span->offset; >+ if (ts->spantype == TYPE_E1) { /* do it E1 way */ >+ if (chan->chanpos == 16) { >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ return 0; >+ } >+ n = chan->chanpos - 1; >+ if (chan->chanpos > 15) n--; >+ b = (n % 15); >+ c = ts->txsigs[b]; >+ m = (n / 15) << 2; /* nibble selector */ >+ c &= (0xf << m); /* keep the other nibble */ >+ c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ >+ ts->txsigs[b] = c; >+ /* output them to the chip */ >+ __t4_framer_out(wc,k,0x71 + b,c); >+ } else if (ts->span.lineconfig & DAHDI_CONFIG_D4) { >+ n = chan->chanpos - 1; >+ b = (n/4); >+ c = ts->txsigs[b]; >+ m = ((3 - (n % 4)) << 1); /* nibble selector */ >+ c &= ~(0x3 << m); /* keep the other nibble */ >+ c |= ((bits >> 2) & 0x3) << m; /* put our new nibble here */ >+ ts->txsigs[b] = c; >+ /* output them to the chip */ >+ __t4_framer_out(wc,k,0x70 + b,c); >+ __t4_framer_out(wc,k,0x70 + b + 6,c); >+ } else if (ts->span.lineconfig & DAHDI_CONFIG_ESF) { >+ n = chan->chanpos - 1; >+ b = (n/2); >+ c = ts->txsigs[b]; >+ m = ((n % 2) << 2); /* nibble selector */ >+ c &= (0xf << m); /* keep the other nibble */ >+ c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ >+ ts->txsigs[b] = c; >+ /* output them to the chip */ >+ __t4_framer_out(wc,k,0x70 + b,c); >+ } >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ if (debug & DEBUG_RBS) >+ dev_notice(&wc->dev->dev, "Finished setting RBS bits\n"); >+ return 0; >+} >+ >+static int t4_shutdown(struct dahdi_span *span) >+{ >+ int tspan; >+ int wasrunning; >+ unsigned long flags; >+ struct t4_span *ts = t4_from_span(span); >+ struct t4 *wc = ts->owner; >+ >+ tspan = span->offset + 1; >+ if (tspan < 0) { >+ dev_notice(&wc->dev->dev, "opvxd115: Span '%d' isn't us?\n", >+ span->spanno); >+ return -1; >+ } >+ >+ if (debug & DEBUG_MAIN) >+ dev_notice(&wc->dev->dev, "Shutting down span %d (%s)\n", >+ span->spanno, span->name); >+ >+ /* Stop HDLC controller if runned */ >+ if (ts->sigchan) >+ hdlc_stop(wc, span->offset); >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ wasrunning = span->flags & DAHDI_FLAG_RUNNING; >+ >+ span->flags &= ~DAHDI_FLAG_RUNNING; >+ __t4_set_led(wc, span->offset, WC_OFF); >+ if ((wc->numspans == 1) && >+ (!(wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING))) { >+ /* No longer in use, disable interrupts */ >+ dev_info(&wc->dev->dev, "opvxd115: Disabling interrupts since " >+ "there are no active spans\n"); >+ set_bit(T4_STOP_DMA, &wc->checkflag); >+ } else >+ set_bit(T4_CHECK_TIMING, &wc->checkflag); >+ >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ >+ /* Wait for interrupt routine to shut itself down */ >+ msleep(10); >+ if (wasrunning) >+ wc->spansstarted--; >+ >+ if (debug & DEBUG_MAIN) >+ dev_notice(&wc->dev->dev, "Span %d (%s) shutdown\n", >+ span->spanno, span->name); >+ return 0; >+} >+ >+static void t4_chan_set_sigcap(struct dahdi_span *span, int x) >+{ >+ struct t4_span *wc = container_of(span, struct t4_span, span); >+ struct dahdi_chan *chan = wc->chans[x]; >+ chan->sigcap = DAHDI_SIG_CLEAR; >+ /* E&M variant supported depends on span type */ >+ if (wc->spantype == TYPE_E1) { >+ /* E1 sigcap setup */ >+ if (span->lineconfig & DAHDI_CONFIG_CCS) { >+ /* CCS setup */ >+ chan->sigcap |= DAHDI_SIG_MTP2 | DAHDI_SIG_SF | >+ DAHDI_SIG_HARDHDLC; >+ return; >+ } >+ /* clear out sig and sigcap for channel 16 on E1 CAS >+ * lines, otherwise, set it correctly */ >+ if (x == 15) { >+ /* CAS signaling channel setup */ >+ wc->chans[15]->sigcap = 0; >+ wc->chans[15]->sig = 0; >+ return; >+ } >+ /* normal CAS setup */ >+ chan->sigcap |= DAHDI_SIG_EM_E1 | DAHDI_SIG_FXSLS | >+ DAHDI_SIG_FXSGS | DAHDI_SIG_FXSKS | DAHDI_SIG_SF | >+ DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_FXOKS | >+ DAHDI_SIG_CAS | DAHDI_SIG_DACS_RBS; >+ } else { >+ /* T1 sigcap setup */ >+ chan->sigcap |= DAHDI_SIG_EM | DAHDI_SIG_FXSLS | >+ DAHDI_SIG_FXSGS | DAHDI_SIG_FXSKS | DAHDI_SIG_MTP2 | >+ DAHDI_SIG_SF | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | >+ DAHDI_SIG_FXOKS | DAHDI_SIG_CAS | DAHDI_SIG_DACS_RBS | >+ DAHDI_SIG_HARDHDLC; >+ } >+} >+ >+static int t4_spanconfig(struct file *file, struct dahdi_span *span, >+ struct dahdi_lineconfig *lc) >+{ >+ int i; >+ struct t4_span *ts = t4_from_span(span); >+ struct t4 *wc = ts->owner; >+ >+ if (debug) >+ dev_info(&wc->dev->dev, "About to enter spanconfig!\n"); >+ if (debug & DEBUG_MAIN) >+ dev_notice(&wc->dev->dev, "opvxd115: Configuring span %d\n", >+ span->spanno); >+ >+ if (lc->sync < 0) >+ lc->sync = 0; >+ if (lc->sync > wc->numspans) >+ lc->sync = 0; >+ >+ /* remove this span number from the current sync sources, if there */ >+ for(i = 0; i < wc->numspans; i++) { >+ if (wc->tspans[i]->sync == span->spanno) { >+ wc->tspans[i]->sync = 0; >+ wc->tspans[i]->psync = 0; >+ } >+ } >+ wc->tspans[span->offset]->syncpos = lc->sync; >+ /* if a sync src, put it in proper place */ >+ if (lc->sync) { >+ wc->tspans[lc->sync - 1]->sync = span->spanno; >+ wc->tspans[lc->sync - 1]->psync = span->offset + 1; >+ } >+ set_bit(T4_CHECK_TIMING, &wc->checkflag); >+ >+ /* Make sure this is clear in case of multiple startup and shutdown >+ * iterations */ >+ clear_bit(T4_STOP_DMA, &wc->checkflag); >+ >+ /* make sure that sigcaps gets updated if necessary */ >+ for (i = 0; i < span->channels; i++) >+ t4_chan_set_sigcap(span, i); >+ >+ /* If we're already running, then go ahead and apply the changes */ >+ if (span->flags & DAHDI_FLAG_RUNNING) >+ return t4_startup(file, span); >+ >+ if (debug) >+ dev_info(&wc->dev->dev, "Done with spanconfig!\n"); >+ return 0; >+} >+ >+static int t4_chanconfig(struct file *file, struct dahdi_chan *chan, >+ int sigtype) >+{ >+ int alreadyrunning; >+ unsigned long flags; >+ struct t4 *wc = chan->pvt; >+ struct t4_span *ts = wc->tspans[chan->span->offset]; >+ >+ alreadyrunning = ts->span.flags & DAHDI_FLAG_RUNNING; >+ if (debug & DEBUG_MAIN) { >+ if (alreadyrunning) >+ dev_notice(&wc->dev->dev, "opvxd115: Reconfigured " >+ "channel %d (%s) sigtype %d\n", >+ chan->channo, chan->name, sigtype); >+ else >+ dev_notice(&wc->dev->dev, "opvxd115: Configured channel" >+ " %d (%s) sigtype %d\n", >+ chan->channo, chan->name, sigtype); >+ } >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ >+ if (alreadyrunning) >+ __set_clear(wc, chan->span->offset); >+ >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ >+ /* (re)configure signalling channel */ >+ if ((sigtype == DAHDI_SIG_HARDHDLC) || (ts->sigchan == chan)) { >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "%sonfiguring hardware HDLC " >+ "on %s\n", >+ ((sigtype == DAHDI_SIG_HARDHDLC) ? "C" : "Unc"), >+ chan->name); >+ if (alreadyrunning) { >+ if (ts->sigchan) >+ hdlc_stop(wc, ts->sigchan->span->offset); >+ if (sigtype == DAHDI_SIG_HARDHDLC) { >+ if (hdlc_start(wc, chan->span->offset, chan, ts->sigmode)) { >+ dev_notice(&wc->dev->dev, "Error " >+ "initializing signalling " >+ "controller\n"); >+ return -1; >+ } >+ } else { >+ spin_lock_irqsave(&wc->reglock, flags); >+ ts->sigchan = NULL; >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ } >+ >+ } >+ else { >+ spin_lock_irqsave(&wc->reglock, flags); >+ ts->sigchan = (sigtype == DAHDI_SIG_HARDHDLC) ? chan : NULL; >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ ts->sigactive = 0; >+ } >+ } >+ return 0; >+} >+ >+static int t4_open(struct dahdi_chan *chan) >+{ >+ return 0; >+} >+ >+static int t4_close(struct dahdi_chan *chan) >+{ >+ return 0; >+} >+ >+static void set_span_devicetype(struct t4 *wc) >+{ >+ int x; >+ struct t4_span *ts; >+ >+ for (x = 0; x < wc->numspans; x++) { >+ ts = wc->tspans[x]; >+ dahdi_copy_string(ts->span.devicetype, wc->variety, sizeof(ts->span.devicetype)); >+ if (wc->vpm == T4_VPM_PRESENT) { >+ if (!wc->vpm450m) >+ strncat(ts->span.devicetype, " (VPM400M)", sizeof(ts->span.devicetype) - 1); >+ else >+ strncat(ts->span.devicetype, " (VPMOCT032)", >+ sizeof(ts->span.devicetype) - 1); >+ } >+ } >+} >+ >+/* The number of cards we have seen with each >+ possible 'order' switch setting. >+*/ >+static unsigned int order_index[16]; >+ >+static void setup_chunks(struct t4 *wc, int which) >+{ >+ struct t4_span *ts; >+ int offset = 1; >+ int x, y; >+ int gen2; >+ >+ if (!wc->t1e1) >+ offset += 4; >+ >+ gen2 = (wc->tspans[0]->spanflags & FLAG_2NDGEN); >+ >+ for (x = 0; x < wc->numspans; x++) { >+ ts = wc->tspans[x]; >+ ts->writechunk = (void *)(wc->writechunk + (x * 32 * 2) + (which * (1024 >> 2))); >+ ts->readchunk = (void *)(wc->readchunk + (x * 32 * 2) + (which * (1024 >> 2))); >+ for (y=0;y<wc->tspans[x]->span.channels;y++) { >+ struct dahdi_chan *mychans = ts->chans[y]; >+ if (gen2) { >+ mychans->writechunk = (void *)(wc->writechunk + ((x * 32 + y + offset) * 2) + (which * (1024 >> 2))); >+ mychans->readchunk = (void *)(wc->readchunk + ((x * 32 + y + offset) * 2) + (which * (1024 >> 2))); >+ } >+ } >+ } >+} >+ >+#ifdef DAHDI_SPAN_OPS >+static const struct dahdi_span_ops t4_gen1_span_ops = { >+ .owner = THIS_MODULE, >+ .spanconfig = t4_spanconfig, >+ .chanconfig = t4_chanconfig, >+ .startup = t4_startup, >+ .shutdown = t4_shutdown, >+ .rbsbits = t4_rbsbits, >+ .maint = t4_maint, >+ .open = t4_open, >+ .close = t4_close, >+ .ioctl = t4_ioctl, >+ .hdlc_hard_xmit = t4_hdlc_hard_xmit, >+}; >+ >+static const struct dahdi_span_ops t4_gen2_span_ops = { >+ .owner = THIS_MODULE, >+ .spanconfig = t4_spanconfig, >+ .chanconfig = t4_chanconfig, >+ .startup = t4_startup, >+ .shutdown = t4_shutdown, >+ .rbsbits = t4_rbsbits, >+ .maint = t4_maint, >+ .open = t4_open, >+ .close = t4_close, >+ .ioctl = t4_ioctl, >+ .hdlc_hard_xmit = t4_hdlc_hard_xmit, >+ .dacs = t4_dacs, >+#ifdef VPM_SUPPORT >+ .echocan_create = t4_echocan_create, >+#endif >+}; >+#endif >+ >+static void init_spans(struct t4 *wc) >+{ >+ int x,y; >+ int gen2; >+ struct t4_span *ts; >+ unsigned int reg; >+ >+ gen2 = (wc->tspans[0]->spanflags & FLAG_2NDGEN); >+ for (x = 0; x < wc->numspans; x++) { >+ ts = wc->tspans[x]; >+ sprintf(ts->span.name, "D115/D130/%d/%d", wc->num, x + 1); >+ snprintf(ts->span.desc, sizeof(ts->span.desc) - 1, >+ "D115/D130 (E1|T1) Card %d Span %d", wc->num, x+1); >+ ts->span.manufacturer = "OpenVox"; >+ if (order_index[wc->order] == 1) >+ snprintf(ts->span.location, sizeof(ts->span.location) - 1, "Board ID Switch %d", wc->order); >+ else >+ snprintf(ts->span.location, sizeof(ts->span.location) - 1, >+ "PCI%s Bus %02d Slot %02d", (ts->spanflags & FLAG_EXPRESS) ? " Express" : " ", >+ wc->dev->bus->number, PCI_SLOT(wc->dev->devfn) + 1); >+ switch (ts->spantype) { >+ case TYPE_T1: >+ ts->span.spantype = "T1"; >+ break; >+ case TYPE_E1: >+ ts->span.spantype = "E1"; >+ break; >+ case TYPE_J1: >+ ts->span.spantype = "J1"; >+ break; >+ } >+#ifdef DAHDI_SPAN_MODULE >+ ts->span.owner = THIS_MODULE; >+#endif >+#ifdef DAHDI_SPAN_OPS >+ if (gen2) { >+ ts->span.ops = &t4_gen2_span_ops; >+ } else { >+ ts->span.ops = &t4_gen1_span_ops; >+ } >+#else >+ ts->span.spanconfig = t4_spanconfig; >+ ts->span.chanconfig = t4_chanconfig; >+ ts->span.startup = t4_startup; >+ ts->span.shutdown = t4_shutdown; >+ ts->span.rbsbits = t4_rbsbits; >+ ts->span.maint = t4_maint; >+ ts->span.open = t4_open; >+ ts->span.close = t4_close; >+ ts->span.ioctl = t4_ioctl; >+ ts->span.hdlc_hard_xmit = t4_hdlc_hard_xmit; >+ if (gen2) { >+#ifdef VPM_SUPPORT >+ if (vpmsupport) >+ ts->span.echocan_create = t4_echocan_create; >+#endif >+ ts->span.dacs = t4_dacs; >+ } >+ ts->span.pvt = ts; >+#endif >+ ts->span.irq = wc->dev->irq; >+ >+ /* HDLC Specific init */ >+ ts->sigchan = NULL; >+ ts->sigmode = sigmode; >+ ts->sigactive = 0; >+ >+ if (ts->spantype == TYPE_T1 || ts->spantype == TYPE_J1) { >+ ts->span.channels = 24; >+ ts->span.deflaw = DAHDI_LAW_MULAW; >+ ts->span.linecompat = DAHDI_CONFIG_AMI | >+ DAHDI_CONFIG_B8ZS | DAHDI_CONFIG_D4 | >+ DAHDI_CONFIG_ESF; >+ } else { >+ ts->span.channels = 31; >+ ts->span.deflaw = DAHDI_LAW_ALAW; >+ ts->span.linecompat = DAHDI_CONFIG_AMI | >+ DAHDI_CONFIG_HDB3 | DAHDI_CONFIG_CCS | >+ DAHDI_CONFIG_CRC4; >+ } >+ ts->span.chans = ts->chans; >+ ts->span.flags = DAHDI_FLAG_RBS; >+ >+ ts->owner = wc; >+ ts->span.offset = x; >+ ts->writechunk = (void *)(wc->writechunk + x * 32 * 2); >+ ts->readchunk = (void *)(wc->readchunk + x * 32 * 2); >+ >+ for (y=0;y<wc->tspans[x]->span.channels;y++) { >+ struct dahdi_chan *mychans = ts->chans[y]; >+ sprintf(mychans->name, "D115/D130/%d/%d/%d", wc->num, x + 1, y + 1); >+ t4_chan_set_sigcap(&ts->span, x); >+ mychans->pvt = wc; >+ mychans->chanpos = y + 1; >+ } >+ >+ /* Enable 1sec timer interrupt */ >+ reg = t4_framer_in(wc, x, FMR1_T); >+ t4_framer_out(wc, x, FMR1_T, (reg | FMR1_ECM)); >+ >+ /* Enable Errored Second interrupt */ >+ t4_framer_out(wc, x, ESM, 0); >+ >+#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) >+ t4_reset_counters(&ts->span); >+#endif >+ } >+ >+ set_span_devicetype(wc); >+ setup_chunks(wc, 0); >+ wc->lastindex = 0; >+} >+ >+static void t4_serial_setup(struct t4 *wc, int unit) >+{ >+ if (!wc->globalconfig) { >+ wc->globalconfig = 1; >+ if (debug) >+ dev_info(&wc->dev->dev, "opvxd115: Setting up global " >+ "serial parameters\n"); >+ t4_framer_out(wc, 0, 0x85, 0xe0); /* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from channel 0 */ >+ t4_framer_out(wc, 0, 0x08, 0x01); /* IPC: Interrupt push/pull active low */ >+ >+ /* Global clocks (8.192 Mhz CLK) */ >+ t4_framer_out(wc, 0, 0x92, 0x00); >+ t4_framer_out(wc, 0, 0x93, 0x18); >+ t4_framer_out(wc, 0, 0x94, 0xfb); >+ t4_framer_out(wc, 0, 0x95, 0x0b); >+ t4_framer_out(wc, 0, 0x96, 0x00); >+ t4_framer_out(wc, 0, 0x97, 0x0b); >+ t4_framer_out(wc, 0, 0x98, 0xdb); >+ t4_framer_out(wc, 0, 0x99, 0xdf); >+ } >+ >+ /* Configure interrupts */ >+ t4_framer_out(wc, unit, FRMR_GCR, 0x00); /* GCR: Interrupt on Activation/Deactivation of each */ >+ >+ /* Configure system interface */ >+ t4_framer_out(wc, unit, FRMR_SIC1, 0xc2); /* SIC1: 8.192 Mhz clock/bus, double buffer receive / transmit, byte interleaved */ >+ t4_framer_out(wc, unit, FRMR_SIC2, 0x20 | (unit << 1)); /* SIC2: No FFS, no center receive eliastic buffer, phase */ >+ t4_framer_out(wc, unit, FRMR_SIC3, 0x04); /* SIC3: Edges for capture */ >+ t4_framer_out(wc, unit, FRMR_CMR2, 0x00); /* CMR2: We provide sync and clock for tx and rx. */ >+ if (!wc->t1e1) { /* T1 mode */ >+ t4_framer_out(wc, unit, FRMR_XC0, 0x03); /* XC0: Normal operation of Sa-bits */ >+ t4_framer_out(wc, unit, FRMR_XC1, 0x84); /* XC1: 0 offset */ >+ if (wc->tspans[unit]->spantype == TYPE_J1) >+ t4_framer_out(wc, unit, FRMR_RC0, 0x83); /* RC0: Just shy of 1023 */ >+ else >+ t4_framer_out(wc, unit, FRMR_RC0, 0x03); /* RC0: Just shy of 1023 */ >+ t4_framer_out(wc, unit, FRMR_RC1, 0x84); /* RC1: The rest of RC0 */ >+ } else { /* E1 mode */ >+ t4_framer_out(wc, unit, FRMR_XC0, 0x00); /* XC0: Normal operation of Sa-bits */ >+ t4_framer_out(wc, unit, FRMR_XC1, 0x04); /* XC1: 0 offset */ >+ t4_framer_out(wc, unit, FRMR_RC0, 0x04); /* RC0: Just shy of 1023 */ >+ t4_framer_out(wc, unit, FRMR_RC1, 0x04); /* RC1: The rest of RC0 */ >+ } >+ >+ /* Configure ports */ >+ t4_framer_out(wc, unit, 0x80, 0x00); /* PC1: SPYR/SPYX input on RPA/XPA */ >+ if (wc->falc31) { >+ t4_framer_out(wc, unit, 0x81, 0xBB); /* PC2: RMFB/XSIG output/input on RPB/XPB */ >+ t4_framer_out(wc, unit, 0x82, 0xBB); /* PC3: Some unused stuff */ >+ t4_framer_out(wc, unit, 0x83, 0xBB); /* PC4: Some more unused stuff */ >+ } else { >+ t4_framer_out(wc, unit, 0x81, 0x22); /* PC2: RMFB/XSIG output/input on RPB/XPB */ >+ t4_framer_out(wc, unit, 0x82, 0x65); /* PC3: Some unused stuff */ >+ t4_framer_out(wc, unit, 0x83, 0x35); /* PC4: Some more unused stuff */ >+ } >+ t4_framer_out(wc, unit, 0x84, 0x01); /* PC5: XMFS active low, SCLKR is input, RCLK is output */ >+ if (debug & DEBUG_MAIN) >+ dev_notice(&wc->dev->dev, "Successfully initialized serial " >+ "bus for unit %d\n", unit); >+} >+ >+static int syncsrc = 0; >+static int syncnum = 0 /* -1 */; >+static int syncspan = 0; >+#ifdef DEFINE_SPINLOCK >+static DEFINE_SPINLOCK(synclock); >+#else >+static spinlock_t synclock = SPIN_LOCK_UNLOCKED; >+#endif >+ >+static void __t4_set_rclk_src(struct t4 *wc, int span) >+{ >+ int cmr1 = 0x38; /* Clock Mode: RCLK sourced by DCO-R1 >+ by default, Disable Clock-Switching */ >+ >+ cmr1 |= (span << 6); >+ __t4_framer_out(wc, 0, 0x44, cmr1); >+ >+ dev_info(&wc->dev->dev, "RCLK source set to span %d\n", span+1); >+} >+ >+static void __t4_set_sclk_src(struct t4 *wc, int mode, int master, int slave) >+{ >+ if (slave) { >+ wc->dmactrl |= (1 << 25); >+ dev_info(&wc->dev->dev, "SCLK is slaved to timing cable\n"); >+ } else { >+ wc->dmactrl &= ~(1 << 25); >+ } >+ >+ if (master) { >+ wc->dmactrl |= (1 << 24); >+ dev_info(&wc->dev->dev, "SCLK is master to timing cable\n"); >+ } else { >+ wc->dmactrl &= ~(1 << 24); >+ } >+ >+ if (mode == WC_RECOVER) >+ wc->dmactrl |= (1 << 29); /* Recover timing from RCLK */ >+ >+ if (mode == WC_SELF) >+ wc->dmactrl &= ~(1 << 29);/* Provide timing from MCLK */ >+ >+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+} >+ >+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)) >+static ssize_t t4_timing_master_show(struct device *dev, >+ struct device_attribute *attr, >+ char *buf) >+{ >+ struct t4 *wc = dev_get_drvdata(dev); >+ if (wc->dmactrl & (1 << 29)) >+ return sprintf(buf, "%d\n", wc->syncsrc); >+ else >+ return sprintf(buf, "%d\n", -1); >+} >+ >+static DEVICE_ATTR(timing_master, 0400, t4_timing_master_show, NULL); >+ >+static void create_sysfs_files(struct t4 *wc) >+{ >+ int ret; >+ ret = device_create_file(&wc->dev->dev, >+ &dev_attr_timing_master); >+ if (ret) { >+ dev_info(&wc->dev->dev, >+ "Failed to create device attributes.\n"); >+ } >+} >+ >+static void remove_sysfs_files(struct t4 *wc) >+{ >+ device_remove_file(&wc->dev->dev, >+ &dev_attr_timing_master); >+} >+ >+#else >+ >+static inline void create_sysfs_files(struct t4 *wc) { return; } >+static inline void remove_sysfs_files(struct t4 *wc) { return; } >+ >+#endif /* LINUX_KERNEL > 2.6.18 */ >+ >+static inline void __t4_update_timing(struct t4 *wc) >+{ >+ int i; >+ /* update sync src info */ >+ if (wc->syncsrc != syncsrc) { >+ dev_info(&wc->dev->dev, "Swapping card %d from %d to %d\n", >+ wc->num, wc->syncsrc, syncsrc); >+ wc->syncsrc = syncsrc; >+ /* Update sync sources */ >+ for (i = 0; i < wc->numspans; i++) { >+ wc->tspans[i]->span.syncsrc = wc->syncsrc; >+ } >+ if (syncnum == wc->num) { >+ __t4_set_rclk_src(wc, syncspan-1); >+ __t4_set_sclk_src(wc, WC_RECOVER, 1, 0); >+ if (debug) >+ dev_notice(&wc->dev->dev, "Card %d, using sync " >+ "span %d, master\n", wc->num, syncspan); >+ } else { >+ __t4_set_sclk_src(wc, WC_RECOVER, 0, 1); >+ if (debug) >+ dev_notice(&wc->dev->dev, "Card %d, using " >+ "Timing Bus, NOT master\n", wc->num); >+ } >+ } >+} >+ >+static int __t4_findsync(struct t4 *wc) >+{ >+ int i; >+ int x; >+ unsigned long flags; >+ int p; >+ int nonzero; >+ int newsyncsrc = 0; /* DAHDI span number */ >+ int newsyncnum = 0; /* opvxd115 card number */ >+ int newsyncspan = 0; /* span on given opvxd115 card */ >+ spin_lock_irqsave(&synclock, flags); >+#if 1 >+ if (!wc->num) { >+ /* If we're the first card, go through all the motions, up to 8 levels >+ of sync source */ >+ p = 1; >+ while (p < 8) { >+ nonzero = 0; >+ for (x=0;cards[x];x++) { >+ for (i = 0; i < wc->numspans; i++) { >+ if (cards[x]->tspans[i]->syncpos) { >+ nonzero = 1; >+ if ((cards[x]->tspans[i]->syncpos == p) && >+ !(cards[x]->tspans[i]->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE | DAHDI_ALARM_LOOPBACK)) && >+ (cards[x]->tspans[i]->span.flags & DAHDI_FLAG_RUNNING)) { >+ /* This makes a good sync source */ >+ newsyncsrc = cards[x]->tspans[i]->span.spanno; >+ newsyncnum = x; >+ newsyncspan = i + 1; >+ /* Jump out */ >+ goto found; >+ } >+ } >+ } >+ } >+ if (nonzero) >+ p++; >+ else >+ break; >+ } >+found: >+ if ((syncnum != newsyncnum) || (syncsrc != newsyncsrc) || (newsyncspan != syncspan)) { >+ if (debug) >+ dev_notice(&wc->dev->dev, "New syncnum: %d " >+ "(was %d), syncsrc: %d (was %d), " >+ "syncspan: %d (was %d)\n", newsyncnum, >+ syncnum, newsyncsrc, syncsrc, >+ newsyncspan, syncspan); >+ syncnum = newsyncnum; >+ syncsrc = newsyncsrc; >+ syncspan = newsyncspan; >+ for (x=0;cards[x];x++) { >+ __t4_update_timing(cards[x]); >+ } >+ } >+ } >+ __t4_update_timing(wc); >+#endif >+ spin_unlock_irqrestore(&synclock, flags); >+ return 0; >+} >+ >+static void __t4_set_timing_source_auto(struct t4 *wc) >+{ >+ int x; >+ int firstprio, secondprio; >+ firstprio = secondprio = 4; >+ >+ if (debug) >+ dev_info(&wc->dev->dev, "timing source auto\n"); >+ clear_bit(T4_CHECK_TIMING, &wc->checkflag); >+ if (timingcable) { >+ __t4_findsync(wc); >+ } else { >+ if (debug) >+ dev_info(&wc->dev->dev, "Evaluating spans for timing " >+ "source\n"); >+ for (x=0;x<wc->numspans;x++) { >+ if ((wc->tspans[x]->span.flags & DAHDI_FLAG_RUNNING) && >+ !(wc->tspans[x]->span.alarms & (DAHDI_ALARM_RED | >+ DAHDI_ALARM_BLUE))) { >+ if (debug) >+ dev_info(&wc->dev->dev, "span %d is " >+ "green : syncpos %d\n", x+1, >+ wc->tspans[x]->syncpos); >+ if (wc->tspans[x]->syncpos) { >+ /* Valid rsync source in recovered >+ timing mode */ >+ if (firstprio == 4) >+ firstprio = x; >+ else if (wc->tspans[x]->syncpos < >+ wc->tspans[firstprio]->syncpos) >+ firstprio = x; >+ } else { >+ /* Valid rsync source in system timing >+ mode */ >+ if (secondprio == 4) >+ secondprio = x; >+ } >+ } >+ } >+ if (firstprio != 4) { >+ wc->syncsrc = firstprio; >+ __t4_set_rclk_src(wc, firstprio); >+ __t4_set_sclk_src(wc, WC_RECOVER, 0, 0); >+ dev_info(&wc->dev->dev, "Recovered timing mode, "\ >+ "RCLK set to span %d\n", >+ firstprio+1); >+ } else if (secondprio != 4) { >+ wc->syncsrc = -1; >+ __t4_set_rclk_src(wc, secondprio); >+ __t4_set_sclk_src(wc, WC_SELF, 0, 0); >+ dev_info(&wc->dev->dev, "System timing mode, "\ >+ "RCLK set to span %d\n", >+ secondprio+1); >+ } else { >+ wc->syncsrc = -1; >+ dev_info(&wc->dev->dev, "All spans in alarm : No valid"\ >+ "span to source RCLK from\n"); >+ /* Default rclk to lock with span 1 */ >+ __t4_set_rclk_src(wc, 0); >+ __t4_set_sclk_src(wc, WC_SELF, 0, 0); >+ } >+ } >+} >+ >+static void __t4_configure_t1(struct t4 *wc, int unit, int lineconfig, int txlevel) >+{ >+ unsigned int fmr4, fmr2, fmr1, fmr0, lim2; >+ char *framing, *line; >+ int mytxlevel; >+ if ((txlevel > 7) || (txlevel < 4)) >+ mytxlevel = 0; >+ else >+ mytxlevel = txlevel - 4; >+ fmr1 = 0x9c; /* FMR1: Mode 1, T1 mode, CRC on for ESF, 8.192 Mhz system data rate, no XAIS */ >+ fmr2 = 0x20; /* FMR2: no payload loopback, don't auto yellow */ >+ fmr4 = 0x0c; /* FMR4: Lose sync on 2 out of 5 framing bits, auto resync */ >+ lim2 = 0x21; /* LIM2: 50% peak is a "1", Advanced Loss recovery */ >+ lim2 |= (mytxlevel << 6); /* LIM2: Add line buildout */ >+ __t4_framer_out(wc, unit, 0x1d, fmr1); >+ __t4_framer_out(wc, unit, 0x1e, fmr2); >+ >+ /* Configure line interface */ >+ if (lineconfig & DAHDI_CONFIG_AMI) { >+ line = "AMI"; >+ /* workaround for errata #2 in ES v3 09-10-16 */ >+ fmr0 = (wc->falc31) ? 0xb0 : 0xa0; >+ } else { >+ line = "B8ZS"; >+ fmr0 = 0xf0; >+ } >+ if (lineconfig & DAHDI_CONFIG_D4) { >+ framing = "D4"; >+ } else { >+ framing = "ESF"; >+ fmr4 |= 0x2; >+ fmr2 |= 0xc0; >+ } >+ __t4_framer_out(wc, unit, 0x1c, fmr0); >+ __t4_framer_out(wc, unit, 0x20, fmr4); >+ __t4_framer_out(wc, unit, 0x21, 0x40); /* FMR5: Enable RBS mode */ >+ >+ __t4_framer_out(wc, unit, 0x37, 0xf0 ); /* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */ >+ __t4_framer_out(wc, unit, 0x36, 0x08); /* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */ >+ >+ __t4_framer_out(wc, unit, 0x02, 0x50); /* CMDR: Reset the receiver and transmitter line interface */ >+ __t4_framer_out(wc, unit, 0x02, 0x00); /* CMDR: Reset the receiver and transmitter line interface */ >+ >+ if (wc->falc31) { >+ if (debug) >+ dev_info(&wc->dev->dev, "card %d span %d: setting Rtx " >+ "to 0ohm for T1\n", wc->num, unit); >+ __t4_framer_out(wc, unit, 0x86, 0x00); /* PC6: set Rtx to 0ohm for T1 */ >+ >+ // Hitting the bugfix register to fix errata #3 >+ __t4_framer_out(wc, unit, 0xbd, 0x05); >+ } >+ >+ __t4_framer_out(wc, unit, 0x3a, lim2); /* LIM2: 50% peak amplitude is a "1" */ >+ __t4_framer_out(wc, unit, 0x38, 0x0a); /* PCD: LOS after 176 consecutive "zeros" */ >+ __t4_framer_out(wc, unit, 0x39, 0x15); /* PCR: 22 "ones" clear LOS */ >+ >+ /* Generate pulse mask for T1 */ >+ switch(mytxlevel) { >+ case 3: >+ __t4_framer_out(wc, unit, 0x26, 0x07); /* XPM0 */ >+ __t4_framer_out(wc, unit, 0x27, 0x01); /* XPM1 */ >+ __t4_framer_out(wc, unit, 0x28, 0x00); /* XPM2 */ >+ break; >+ case 2: >+ __t4_framer_out(wc, unit, 0x26, 0x8c); /* XPM0 */ >+ __t4_framer_out(wc, unit, 0x27, 0x11); /* XPM1 */ >+ __t4_framer_out(wc, unit, 0x28, 0x01); /* XPM2 */ >+ break; >+ case 1: >+ __t4_framer_out(wc, unit, 0x26, 0x8c); /* XPM0 */ >+ __t4_framer_out(wc, unit, 0x27, 0x01); /* XPM1 */ >+ __t4_framer_out(wc, unit, 0x28, 0x00); /* XPM2 */ >+ break; >+ case 0: >+ default: >+ __t4_framer_out(wc, unit, 0x26, 0xd7); /* XPM0 */ >+ __t4_framer_out(wc, unit, 0x27, 0x22); /* XPM1 */ >+ __t4_framer_out(wc, unit, 0x28, 0x01); /* XPM2 */ >+ break; >+ } >+ >+ /* Don't mask framer interrupts if hardware HDLC is in use */ >+ __t4_framer_out(wc, unit, FRMR_IMR0, 0xff & ~((wc->tspans[unit]->sigchan) ? HDLC_IMR0_MASK : 0)); /* IMR0: We care about CAS changes, etc */ >+ __t4_framer_out(wc, unit, FRMR_IMR1, 0xff & ~((wc->tspans[unit]->sigchan) ? HDLC_IMR1_MASK : 0)); /* IMR1: We care about nothing */ >+ __t4_framer_out(wc, unit, 0x16, 0x00); /* IMR2: All the alarm stuff! */ >+ __t4_framer_out(wc, unit, 0x17, 0x34); /* IMR3: AIS and friends */ >+ __t4_framer_out(wc, unit, 0x18, 0x3f); /* IMR4: Slips on transmit */ >+ >+ dev_info(&wc->dev->dev, "Span %d configured for %s/%s\n", unit + 1, >+ framing, line); >+} >+ >+static void __t4_configure_e1(struct t4 *wc, int unit, int lineconfig) >+{ >+ unsigned int fmr2, fmr1, fmr0; >+ unsigned int cas = 0; >+ unsigned int imr3extra=0; >+ char *crc4 = ""; >+ char *framing, *line; >+ fmr1 = 0x44; /* FMR1: E1 mode, Automatic force resync, PCM30 mode, 8.192 Mhz backplane, no XAIS */ >+ fmr2 = 0x03; /* FMR2: Auto transmit remote alarm, auto loss of multiframe recovery, no payload loopback */ >+ if (lineconfig & DAHDI_CONFIG_CRC4) { >+ fmr1 |= 0x08; /* CRC4 transmit */ >+ fmr2 |= 0xc0; /* CRC4 receive */ >+ crc4 = "/CRC4"; >+ } >+ __t4_framer_out(wc, unit, 0x1d, fmr1); >+ __t4_framer_out(wc, unit, 0x1e, fmr2); >+ >+ /* Configure line interface */ >+ if (lineconfig & DAHDI_CONFIG_AMI) { >+ line = "AMI"; >+ /* workaround for errata #2 in ES v3 09-10-16 */ >+ fmr0 = (wc->falc31) ? 0xb0 : 0xa0; >+ } else { >+ line = "HDB3"; >+ fmr0 = 0xf0; >+ } >+ if (lineconfig & DAHDI_CONFIG_CCS) { >+ framing = "CCS"; >+ imr3extra = 0x28; >+ } else { >+ framing = "CAS"; >+ cas = 0x40; >+ } >+ __t4_framer_out(wc, unit, 0x1c, fmr0); >+ >+ __t4_framer_out(wc, unit, 0x37, 0xf0 /*| 0x6 */ ); /* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */ >+ __t4_framer_out(wc, unit, 0x36, 0x08); /* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */ >+ >+ __t4_framer_out(wc, unit, 0x02, 0x50); /* CMDR: Reset the receiver and transmitter line interface */ >+ __t4_framer_out(wc, unit, 0x02, 0x00); /* CMDR: Reset the receiver and transmitter line interface */ >+ >+ if (wc->falc31) { >+ if (debug) >+ dev_info(&wc->dev->dev, >+ "setting Rtx to 7.5ohm for E1\n"); >+ __t4_framer_out(wc, unit, 0x86, 0x40); /* PC6: turn on 7.5ohm Rtx for E1 */ >+ } >+ >+ /* Condition receive line interface for E1 after reset */ >+ __t4_framer_out(wc, unit, 0xbb, 0x17); >+ __t4_framer_out(wc, unit, 0xbc, 0x55); >+ __t4_framer_out(wc, unit, 0xbb, 0x97); >+ __t4_framer_out(wc, unit, 0xbb, 0x11); >+ __t4_framer_out(wc, unit, 0xbc, 0xaa); >+ __t4_framer_out(wc, unit, 0xbb, 0x91); >+ __t4_framer_out(wc, unit, 0xbb, 0x12); >+ __t4_framer_out(wc, unit, 0xbc, 0x55); >+ __t4_framer_out(wc, unit, 0xbb, 0x92); >+ __t4_framer_out(wc, unit, 0xbb, 0x0c); >+ __t4_framer_out(wc, unit, 0xbb, 0x00); >+ __t4_framer_out(wc, unit, 0xbb, 0x8c); >+ >+ __t4_framer_out(wc, unit, 0x3a, 0x20); /* LIM2: 50% peak amplitude is a "1" */ >+ __t4_framer_out(wc, unit, 0x38, 0x0a); /* PCD: LOS after 176 consecutive "zeros" */ >+ __t4_framer_out(wc, unit, 0x39, 0x15); /* PCR: 22 "ones" clear LOS */ >+ >+ __t4_framer_out(wc, unit, 0x20, 0x9f); /* XSW: Spare bits all to 1 */ >+ __t4_framer_out(wc, unit, 0x21, 0x1c|cas); /* XSP: E-bit set when async. AXS auto, XSIF to 1 */ >+ >+ >+ /* Generate pulse mask for E1 */ >+ __t4_framer_out(wc, unit, 0x26, 0x54); /* XPM0 */ >+ __t4_framer_out(wc, unit, 0x27, 0x02); /* XPM1 */ >+ __t4_framer_out(wc, unit, 0x28, 0x00); /* XPM2 */ >+ >+ /* Don't mask framer interrupts if hardware HDLC is in use */ >+ __t4_framer_out(wc, unit, FRMR_IMR0, 0xff & ~((wc->tspans[unit]->sigchan) ? HDLC_IMR0_MASK : 0)); /* IMR0: We care about CRC errors, CAS changes, etc */ >+ __t4_framer_out(wc, unit, FRMR_IMR1, 0x3f & ~((wc->tspans[unit]->sigchan) ? HDLC_IMR1_MASK : 0)); /* IMR1: We care about loopup / loopdown */ >+ __t4_framer_out(wc, unit, 0x16, 0x00); /* IMR2: We care about all the alarm stuff! */ >+ __t4_framer_out(wc, unit, 0x17, 0x04 | imr3extra); /* IMR3: AIS */ >+ __t4_framer_out(wc, unit, 0x18, 0x3f); /* IMR4: We care about slips on transmit */ >+ >+ dev_info(&wc->dev->dev, "opvxd115: Span %d configured for %s/%s%s\n", >+ unit + 1, framing, line, crc4); >+} >+ >+static int t4_startup(struct file *file, struct dahdi_span *span) >+{ >+#ifdef SUPPORT_GEN1 >+ int i; >+#endif >+ int tspan; >+ unsigned long flags; >+ int alreadyrunning; >+ struct t4_span *ts = t4_from_span(span); >+ struct t4 *wc = ts->owner; >+ >+ set_bit(T4_IGNORE_LATENCY, &wc->checkflag); >+ if (debug) >+ dev_info(&wc->dev->dev, "About to enter startup!\n"); >+ tspan = span->offset + 1; >+ if (tspan < 0) { >+ dev_info(&wc->dev->dev, "opvxd115: Span '%d' isn't us?\n", >+ span->spanno); >+ return -1; >+ } >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ >+ alreadyrunning = span->flags & DAHDI_FLAG_RUNNING; >+ >+#ifdef SUPPORT_GEN1 >+ /* initialize the start value for the entire chunk of last ec buffer */ >+ for(i = 0; i < span->channels; i++) >+ { >+ memset(ts->ec_chunk1[i], >+ DAHDI_LIN2X(0,span->chans[i]),DAHDI_CHUNKSIZE); >+ memset(ts->ec_chunk2[i], >+ DAHDI_LIN2X(0,span->chans[i]),DAHDI_CHUNKSIZE); >+ } >+#endif >+ /* Force re-evaluation of timing source */ >+ wc->syncsrc = -1; >+ set_bit(T4_CHECK_TIMING, &wc->checkflag); >+ >+ if (ts->spantype == TYPE_E1) { /* if this is an E1 card */ >+ __t4_configure_e1(wc, span->offset, span->lineconfig); >+ } else { /* is a T1 card */ >+ __t4_configure_t1(wc, span->offset, span->lineconfig, span->txlevel); >+ } >+ >+ /* Note clear channel status */ >+ wc->tspans[span->offset]->notclear = 0; >+ __set_clear(wc, span->offset); >+ >+ if (!alreadyrunning) { >+ span->flags |= DAHDI_FLAG_RUNNING; >+ wc->spansstarted++; >+ >+ if (wc->flags & FLAG_5THGEN) >+ __t4_pci_out(wc, 5, (ms_per_irq << 16) | wc->numbufs); >+ /* enable interrupts */ >+ /* Start DMA, enabling DMA interrupts on read only */ >+#if 0 >+ /* Enable framer only interrupts */ >+ wc->dmactrl |= 1 << 27; >+#endif >+ wc->dmactrl |= (ts->spanflags & FLAG_2NDGEN) ? 0xc0000000 : 0xc0000003; >+#ifdef VPM_SUPPORT >+ wc->dmactrl |= wc->vpm; >+#endif >+ /* Seed interrupt register */ >+ __t4_pci_out(wc, WC_INTR, 0x0c); >+ if (noburst || !(ts->spanflags & FLAG_BURST)) >+ wc->dmactrl |= (1 << 26); >+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+ >+ /* Startup HDLC controller too */ >+ } >+ >+ if (ts->sigchan) { >+ struct dahdi_chan *sigchan = ts->sigchan; >+ >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ if (hdlc_start(wc, span->offset, sigchan, ts->sigmode)) { >+ dev_notice(&wc->dev->dev, "Error initializing " >+ "signalling controller\n"); >+ return -1; >+ } >+ spin_lock_irqsave(&wc->reglock, flags); >+ } >+ >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ >+ t4_check_alarms(wc, span->offset); >+ t4_check_sigbits(wc, span->offset); >+ >+ if (wc->tspans[0]->sync == span->spanno) >+ dev_info(&wc->dev->dev, "SPAN %d: Primary Sync Source\n", >+ span->spanno); >+#ifdef VPM_SUPPORT >+ if (!alreadyrunning && !wc->vpm) { >+ wait_a_little(); >+ t4_vpm400_init(wc); >+ if (!wc->vpm) >+ t4_vpm450_init(wc); >+ wc->dmactrl |= wc->vpm; >+ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+ if (wc->vpm) >+ set_span_devicetype(wc); >+ } >+#endif >+ if (debug) >+ dev_info(&wc->dev->dev, "Completed startup!\n"); >+ clear_bit(T4_IGNORE_LATENCY, &wc->checkflag); >+ return 0; >+} >+ >+#ifdef SUPPORT_GEN1 >+static inline void e1_check(struct t4 *wc, int span, int val) >+{ >+ struct t4_span *ts = wc->tspans[span]; >+ if ((ts->span.channels > 24) && >+ (ts->span.flags & DAHDI_FLAG_RUNNING) && >+ !(ts->span.alarms) && >+ (!wc->e1recover)) { >+ if (val != 0x1b) { >+ ts->e1check++; >+ } else >+ ts->e1check = 0; >+ if (ts->e1check > 100) { >+ /* Wait 1000 ms */ >+ wc->e1recover = 1000 * 8; >+ wc->tspans[0]->e1check = 0; >+ if (debug & DEBUG_MAIN) >+ dev_notice(&wc->dev->dev, "Detected loss of " >+ "E1 alignment on span %d!\n", span); >+ t4_reset_dma(wc); >+ } >+ } >+} >+ >+static void t4_receiveprep(struct t4 *wc, int irq) >+{ >+ volatile unsigned int *readchunk; >+ int dbl = 0; >+ int x,y,z; >+ unsigned int tmp; >+ int offset=0; >+ if (!wc->t1e1) >+ offset = 4; >+ if (irq & 1) { >+ /* First part */ >+ readchunk = wc->readchunk; >+ if (!wc->last0) >+ dbl = 1; >+ wc->last0 = 0; >+ } else { >+ readchunk = wc->readchunk + DAHDI_CHUNKSIZE * 32; >+ if (wc->last0) >+ dbl = 1; >+ wc->last0 = 1; >+ } >+ if (dbl) { >+ for (x=0;x<wc->numspans;x++) >+ wc->tspans[x]->irqmisses++; >+ if (debug & DEBUG_MAIN) >+ dev_notice(&wc->dev->dev, "opvxd115: Double/missed " >+ "interrupt detected\n"); >+ } >+ for (x=0;x<DAHDI_CHUNKSIZE;x++) { >+ for (z=0;z<24;z++) { >+ /* All T1/E1 channels */ >+ tmp = readchunk[z+1+offset]; >+ wc->tspans[0]->span.chans[z]->readchunk[x] = tmp >> 24; >+ } >+ if (wc->t1e1) { >+ if (wc->e1recover > 0) >+ wc->e1recover--; >+ tmp = readchunk[0]; >+ e1_check(wc, 0, (tmp & 0x7f000000) >> 24); >+ for (z=24;z<31;z++) { >+ /* Only E1 channels now */ >+ tmp = readchunk[z+1]; >+ if (wc->tspans[0]->span.channels > 24) >+ wc->tspans[0]->span.chans[z]->readchunk[x] = tmp >> 24; >+ } >+ } >+ /* Advance pointer by 4 TDM frame lengths */ >+ readchunk += 32; >+ } >+ for (x=0;x<wc->numspans;x++) { >+ if (wc->tspans[x]->span.flags & DAHDI_FLAG_RUNNING) { >+ for (y=0;y<wc->tspans[x]->span.channels;y++) { >+ /* Echo cancel double buffered data */ >+ dahdi_ec_chunk(wc->tspans[x]->span.chans[y], >+ wc->tspans[x]->span.chans[y]->readchunk, >+ wc->tspans[x]->ec_chunk2[y]); >+ memcpy(wc->tspans[x]->ec_chunk2[y],wc->tspans[x]->ec_chunk1[y], >+ DAHDI_CHUNKSIZE); >+ memcpy(wc->tspans[x]->ec_chunk1[y], >+ wc->tspans[x]->span.chans[y]->writechunk, >+ DAHDI_CHUNKSIZE); >+ } >+ dahdi_receive(&wc->tspans[x]->span); >+ } >+ } >+} >+#endif >+ >+#if (DAHDI_CHUNKSIZE != 8) >+#error Sorry, nextgen does not support chunksize != 8 >+#endif >+ >+static inline void __receive_span(struct t4_span *ts) >+{ >+#ifdef VPM_SUPPORT >+ int y; >+ unsigned long merged; >+ merged = ts->dtmfactive & ts->dtmfmutemask; >+ if (merged) { >+ for (y=0;y<ts->span.channels;y++) { >+ /* Mute any DTMFs which are supposed to be muted */ >+ if (test_bit(y, &merged)) { >+ memset(ts->span.chans[y]->readchunk, DAHDI_XLAW(0, ts->span.chans[y]), DAHDI_CHUNKSIZE); >+ } >+ } >+ } >+#endif >+ >+#ifdef ENABLE_PREFETCH >+ prefetch((void *)(ts->readchunk)); >+ prefetch((void *)(ts->writechunk)); >+ prefetch((void *)(ts->readchunk + 8)); >+ prefetch((void *)(ts->writechunk + 8)); >+ prefetch((void *)(ts->readchunk + 16)); >+ prefetch((void *)(ts->writechunk + 16)); >+ prefetch((void *)(ts->readchunk + 24)); >+ prefetch((void *)(ts->writechunk + 24)); >+ prefetch((void *)(ts->readchunk + 32)); >+ prefetch((void *)(ts->writechunk + 32)); >+ prefetch((void *)(ts->readchunk + 40)); >+ prefetch((void *)(ts->writechunk + 40)); >+ prefetch((void *)(ts->readchunk + 48)); >+ prefetch((void *)(ts->writechunk + 48)); >+ prefetch((void *)(ts->readchunk + 56)); >+ prefetch((void *)(ts->writechunk + 56)); >+#endif >+ >+ dahdi_ec_span(&ts->span); >+ dahdi_receive(&ts->span); >+} >+ >+static inline void __transmit_span(struct t4_span *ts) >+{ >+ dahdi_transmit(&ts->span); >+} >+ >+#ifdef ENABLE_WORKQUEUES >+static void workq_handlespan(void *data) >+{ >+ struct t4_span *ts = data; >+ struct t4 *wc = ts->owner; >+ >+ __receive_span(ts); >+ __transmit_span(ts); >+ atomic_dec(&wc->worklist); >+ if (!atomic_read(&wc->worklist)) >+ t4_pci_out(wc, WC_INTR, 0); >+} >+#else >+static void t4_prep_gen2(struct t4 *wc) >+{ >+ int x; >+ for (x=0;x<wc->numspans;x++) { >+ if (wc->tspans[x]->span.flags & DAHDI_FLAG_RUNNING) { >+ __receive_span(wc->tspans[x]); >+ __transmit_span(wc->tspans[x]); >+ } >+ } >+} >+ >+#endif >+#ifdef SUPPORT_GEN1 >+static void t4_transmitprep(struct t4 *wc, int irq) >+{ >+ volatile unsigned int *writechunk; >+ int x,y,z; >+ unsigned int tmp; >+ int offset=0; >+ if (!wc->t1e1) >+ offset = 4; >+ if (irq & 1) { >+ /* First part */ >+ writechunk = wc->writechunk + 1; >+ } else { >+ writechunk = wc->writechunk + DAHDI_CHUNKSIZE * 32 + 1; >+ } >+ for (y=0;y<wc->numspans;y++) { >+ if (wc->tspans[y]->span.flags & DAHDI_FLAG_RUNNING) >+ dahdi_transmit(&wc->tspans[y]->span); >+ } >+ >+ for (x=0;x<DAHDI_CHUNKSIZE;x++) { >+ /* Once per chunk */ >+ for (z=0;z<24;z++) { >+ /* All T1/E1 channels */ >+ tmp = (wc->tspans[0]->span.chans[z]->writechunk[x] << 24); >+ writechunk[z+offset] = tmp; >+ } >+ if (wc->t1e1) { >+ for (z=24;z<31;z++) { >+ /* Only E1 channels now */ >+ tmp = 0; >+ if (wc->tspans[0]->span.channels > 24) >+ tmp |= (wc->tspans[0]->span.chans[z]->writechunk[x] << 24); >+ writechunk[z] = tmp; >+ } >+ } >+ /* Advance pointer by 4 TDM frame lengths */ >+ writechunk += 32; >+ } >+ >+} >+#endif >+ >+static void t4_check_sigbits(struct t4 *wc, int span) >+{ >+ int a,i,rxs; >+ struct t4_span *ts = wc->tspans[span]; >+ >+ if (debug & DEBUG_RBS) >+ dev_notice(&wc->dev->dev, "Checking sigbits on span %d\n", >+ span + 1); >+ >+ if (!(ts->span.flags & DAHDI_FLAG_RUNNING)) >+ return; >+ if (ts->spantype == TYPE_E1) { >+ for (i = 0; i < 15; i++) { >+ a = t4_framer_in(wc, span, 0x71 + i); >+ /* Get high channel in low bits */ >+ rxs = (a & 0xf); >+ if (!(ts->span.chans[i+16]->sig & DAHDI_SIG_CLEAR)) { >+ if (ts->span.chans[i+16]->rxsig != rxs) >+ dahdi_rbsbits(ts->span.chans[i+16], rxs); >+ } >+ rxs = (a >> 4) & 0xf; >+ if (!(ts->span.chans[i]->sig & DAHDI_SIG_CLEAR)) { >+ if (ts->span.chans[i]->rxsig != rxs) >+ dahdi_rbsbits(ts->span.chans[i], rxs); >+ } >+ } >+ } else if (ts->span.lineconfig & DAHDI_CONFIG_D4) { >+ for (i = 0; i < 24; i+=4) { >+ a = t4_framer_in(wc, span, 0x70 + (i>>2)); >+ /* Get high channel in low bits */ >+ rxs = (a & 0x3) << 2; >+ if (!(ts->span.chans[i+3]->sig & DAHDI_SIG_CLEAR)) { >+ if (ts->span.chans[i+3]->rxsig != rxs) >+ dahdi_rbsbits(ts->span.chans[i+3], rxs); >+ } >+ rxs = (a & 0xc); >+ if (!(ts->span.chans[i+2]->sig & DAHDI_SIG_CLEAR)) { >+ if (ts->span.chans[i+2]->rxsig != rxs) >+ dahdi_rbsbits(ts->span.chans[i+2], rxs); >+ } >+ rxs = (a >> 2) & 0xc; >+ if (!(ts->span.chans[i+1]->sig & DAHDI_SIG_CLEAR)) { >+ if (ts->span.chans[i+1]->rxsig != rxs) >+ dahdi_rbsbits(ts->span.chans[i+1], rxs); >+ } >+ rxs = (a >> 4) & 0xc; >+ if (!(ts->span.chans[i]->sig & DAHDI_SIG_CLEAR)) { >+ if (ts->span.chans[i]->rxsig != rxs) >+ dahdi_rbsbits(ts->span.chans[i], rxs); >+ } >+ } >+ } else { >+ for (i = 0; i < 24; i+=2) { >+ a = t4_framer_in(wc, span, 0x70 + (i>>1)); >+ /* Get high channel in low bits */ >+ rxs = (a & 0xf); >+ if (!(ts->span.chans[i+1]->sig & DAHDI_SIG_CLEAR)) { >+ /* XXX Not really reset on every trans! XXX */ >+ if (ts->span.chans[i+1]->rxsig != rxs) { >+ dahdi_rbsbits(ts->span.chans[i+1], rxs); >+ } >+ } >+ rxs = (a >> 4) & 0xf; >+ if (!(ts->span.chans[i]->sig & DAHDI_SIG_CLEAR)) { >+ /* XXX Not really reset on every trans! XXX */ >+ if (ts->span.chans[i]->rxsig != rxs) { >+ dahdi_rbsbits(ts->span.chans[i], rxs); >+ } >+ } >+ } >+ } >+} >+ >+static void t4_check_alarms(struct t4 *wc, int span) >+{ >+ unsigned char c, d, e; >+ int alarms; >+ int x,j; >+ struct t4_span *ts = wc->tspans[span]; >+ unsigned long flags; >+ >+ if (!(ts->span.flags & DAHDI_FLAG_RUNNING)) >+ return; >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ >+ c = __t4_framer_in(wc, span, 0x4c); >+ d = __t4_framer_in(wc, span, 0x4d); >+ >+ /* Assume no alarms */ >+ alarms = 0; >+ >+ /* And consider only carrier alarms */ >+ ts->span.alarms &= (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE | DAHDI_ALARM_NOTOPEN); >+ >+ if (ts->spantype == TYPE_E1) { >+ if (c & 0x04) { >+ /* No multiframe found, force RAI high after 400ms only if >+ we haven't found a multiframe since last loss >+ of frame */ >+ if (!(ts->spanflags & FLAG_NMF)) { >+ __t4_framer_out(wc, span, 0x20, 0x9f | 0x20); /* LIM0: Force RAI High */ >+ ts->spanflags |= FLAG_NMF; >+ dev_notice(&wc->dev->dev, >+ "NMF workaround on!\n"); >+ } >+ __t4_framer_out(wc, span, 0x1e, 0xc3); /* Reset to CRC4 mode */ >+ __t4_framer_out(wc, span, 0x1c, 0xf2); /* Force Resync */ >+ __t4_framer_out(wc, span, 0x1c, 0xf0); /* Force Resync */ >+ } else if (!(c & 0x02)) { >+ if ((ts->spanflags & FLAG_NMF)) { >+ __t4_framer_out(wc, span, 0x20, 0x9f); /* LIM0: Clear forced RAI */ >+ ts->spanflags &= ~FLAG_NMF; >+ dev_notice(&wc->dev->dev, >+ "NMF workaround off!\n"); >+ } >+ } >+ } else { >+ /* Detect loopup code if we're not sending one */ >+ if ((!ts->span.mainttimer) && (d & 0x08)) { >+ /* Loop-up code detected */ >+ if ((ts->loopupcnt++ > 80) && (ts->span.maintstat != DAHDI_MAINT_REMOTELOOP)) { >+ __t4_framer_out(wc, span, 0x36, 0x08); /* LIM0: Disable any local loop */ >+ __t4_framer_out(wc, span, 0x37, 0xf6 ); /* LIM1: Enable remote loop */ >+ ts->span.maintstat = DAHDI_MAINT_REMOTELOOP; >+ } >+ } else >+ ts->loopupcnt = 0; >+ /* Same for loopdown code */ >+ if ((!ts->span.mainttimer) && (d & 0x10)) { >+ /* Loop-down code detected */ >+ if ((ts->loopdowncnt++ > 80) && (ts->span.maintstat == DAHDI_MAINT_REMOTELOOP)) { >+ __t4_framer_out(wc, span, 0x36, 0x08); /* LIM0: Disable any local loop */ >+ __t4_framer_out(wc, span, 0x37, 0xf0 ); /* LIM1: Disable remote loop */ >+ ts->span.maintstat = DAHDI_MAINT_NONE; >+ } >+ } else >+ ts->loopdowncnt = 0; >+ } >+ >+ if (ts->span.lineconfig & DAHDI_CONFIG_NOTOPEN) { >+ for (x=0,j=0;x < ts->span.channels;x++) >+ if ((ts->span.chans[x]->flags & DAHDI_FLAG_OPEN) >+#ifdef CONFIG_DAHDI_NET >+ || >+ (ts->span.chans[x]->flags & DAHDI_FLAG_NETDEV) >+#endif >+ ) >+ j++; >+ if (!j) >+ alarms |= DAHDI_ALARM_NOTOPEN; >+ } >+ >+ /* Loss of Frame Alignment */ >+ if (c & 0x20) { >+ if (ts->alarmcount >= alarmdebounce) { >+ >+ /* Disable Slip Interrupts */ >+ e = __t4_framer_in(wc, span, 0x17); >+ __t4_framer_out(wc, span, 0x17, (e|0x03)); >+ >+ alarms |= DAHDI_ALARM_RED; >+ } else { >+ if (unlikely(debug && !ts->alarmcount)) { >+ /* starting to debounce LOF/LFA */ >+ dev_info(&wc->dev->dev, "opvxd115: LOF/LFA " >+ "detected on span %d but debouncing " >+ "for %d ms\n", span + 1, >+ alarmdebounce); >+ } >+ ts->alarmcount++; >+ } >+ } else >+ ts->alarmcount = 0; >+ >+ /* Loss of Signal */ >+ if (c & 0x80) { >+ if (ts->losalarmcount >= losalarmdebounce) { >+ /* Disable Slip Interrupts */ >+ e = __t4_framer_in(wc, span, 0x17); >+ __t4_framer_out(wc, span, 0x17, (e|0x03)); >+ >+ alarms |= DAHDI_ALARM_RED; >+ } else { >+ if (unlikely(debug && !ts->losalarmcount)) { >+ /* starting to debounce LOS */ >+ dev_info(&wc->dev->dev, "opvxd115: LOS " >+ "detected on span %d but debouncing " >+ "for %d ms\n", >+ span + 1, losalarmdebounce); >+ } >+ ts->losalarmcount++; >+ } >+ } else >+ ts->losalarmcount = 0; >+ >+ /* Alarm Indication Signal */ >+ if (c & 0x40) { >+ if (ts->aisalarmcount >= aisalarmdebounce) >+ alarms |= DAHDI_ALARM_BLUE; >+ else { >+ if (unlikely(debug && !ts->aisalarmcount)) { >+ /* starting to debounce AIS */ >+ dev_info(&wc->dev->dev, "opvxd115: AIS " >+ "detected on span %d but debouncing " >+ "for %d ms\n", >+ span + 1, aisalarmdebounce); >+ } >+ ts->aisalarmcount++; >+ } >+ } else >+ ts->aisalarmcount = 0; >+ >+#ifdef DAHDI_SPAN_OPS >+ /* Add detailed alarm status information to a red alarm state */ >+ if (alarms & DAHDI_ALARM_RED) { >+ if (c & FRS0_LOS) >+ alarms |= DAHDI_ALARM_LOS; >+ if (c & FRS0_LFA) >+ alarms |= DAHDI_ALARM_LFA; >+ if (c & FRS0_LMFA) >+ alarms |= DAHDI_ALARM_LMFA; >+ } >+ >+ if (unlikely(debug)) { >+ /* Check to ensure the xmit line isn't shorted */ >+ if (unlikely(d & FRS1_XLS)) { >+ dev_info(&wc->dev->dev, >+ "Detected a possible hardware malfunction"\ >+ " this card may need servicing\n"); >+ } >+ } >+#endif >+ >+ if (((!ts->span.alarms) && alarms) || >+ (ts->span.alarms && (!alarms))) >+ set_bit(T4_CHECK_TIMING, &wc->checkflag); >+ >+ /* Keep track of recovering */ >+ if ((!alarms) && ts->span.alarms) >+ ts->alarmtimer = DAHDI_ALARMSETTLE_TIME; >+ if (ts->alarmtimer) >+ alarms |= DAHDI_ALARM_RECOVER; >+ >+ /* If receiving alarms, go into Yellow alarm state */ >+ if (alarms && !(ts->spanflags & FLAG_SENDINGYELLOW)) { >+ /* We manually do yellow alarm to handle RECOVER and NOTOPEN, otherwise it's auto anyway */ >+ unsigned char fmr4; >+ fmr4 = __t4_framer_in(wc, span, 0x20); >+ __t4_framer_out(wc, span, 0x20, fmr4 | 0x20); >+ dev_info(&wc->dev->dev, "Setting yellow alarm span %d\n", >+ span+1); >+ ts->spanflags |= FLAG_SENDINGYELLOW; >+ } else if ((!alarms) && (ts->spanflags & FLAG_SENDINGYELLOW)) { >+ unsigned char fmr4; >+ /* We manually do yellow alarm to handle RECOVER */ >+ fmr4 = __t4_framer_in(wc, span, 0x20); >+ __t4_framer_out(wc, span, 0x20, fmr4 & ~0x20); >+ dev_info(&wc->dev->dev, "Clearing yellow alarm span %d\n", >+ span+1); >+ >+ /* Re-enable timing slip interrupts */ >+ e = __t4_framer_in(wc, span, 0x17); >+ >+ __t4_framer_out(wc, span, 0x17, (e & ~(0x03))); >+ >+ ts->spanflags &= ~FLAG_SENDINGYELLOW; >+ } >+ >+ /* Re-check the timing source when we enter/leave alarm, not withstanding >+ yellow alarm */ >+ if (c & 0x10) { /* receiving yellow (RAI) */ >+ if (ts->yelalarmcount >= yelalarmdebounce) >+ alarms |= DAHDI_ALARM_YELLOW; >+ else { >+ if (unlikely(debug && !ts->yelalarmcount)) { >+ /* starting to debounce AIS */ >+ dev_info(&wc->dev->dev, "wct%dxxp: yellow " >+ "(RAI) detected on span %d but " >+ "debouncing for %d ms\n", >+ wc->numspans, span + 1, >+ yelalarmdebounce); >+ } >+ ts->yelalarmcount++; >+ } >+ } else >+ ts->yelalarmcount = 0; >+ >+ if (ts->span.mainttimer || ts->span.maintstat) >+ alarms |= DAHDI_ALARM_LOOPBACK; >+ ts->span.alarms = alarms; >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ dahdi_alarm_notify(&ts->span); >+} >+ >+static void t4_do_counters(struct t4 *wc) >+{ >+ int span; >+ for (span=0;span<wc->numspans;span++) { >+ struct t4_span *ts = wc->tspans[span]; >+ int docheck=0; >+ >+ spin_lock(&wc->reglock); >+ if (ts->loopupcnt || ts->loopdowncnt || ts->alarmcount >+ || ts->losalarmcount || ts->aisalarmcount >+ || ts->yelalarmcount) >+ docheck++; >+ >+ if (ts->alarmtimer) { >+ if (!--ts->alarmtimer) { >+ docheck++; >+ ts->span.alarms &= ~(DAHDI_ALARM_RECOVER); >+ } >+ } >+ spin_unlock(&wc->reglock); >+ if (docheck) { >+ t4_check_alarms(wc, span); >+ dahdi_alarm_notify(&ts->span); >+ } >+ } >+} >+ >+static inline void __handle_leds(struct t4 *wc) >+{ >+ int x; >+ >+ wc->blinktimer++; >+ for (x=0;x<wc->numspans;x++) { >+ struct t4_span *ts = wc->tspans[x]; >+ if (ts->span.flags & DAHDI_FLAG_RUNNING) { >+ if ((ts->span.alarms & (DAHDI_ALARM_RED | DAHDI_ALARM_BLUE)) || ts->losalarmcount) { >+#ifdef FANCY_ALARM >+ if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) { >+ __t4_set_led(wc, x, WC_RED); >+ } >+ if (wc->blinktimer == 0xf) { >+ __t4_set_led(wc, x, WC_OFF); >+ } >+#else >+ if (wc->blinktimer == 160) { >+ __t4_set_led(wc, x, WC_RED); >+ } else if (wc->blinktimer == 480) { >+ __t4_set_led(wc, x, WC_OFF); >+ } >+#endif >+ } else if (ts->span.alarms & DAHDI_ALARM_YELLOW) { >+ /* Yellow Alarm */ >+ __t4_set_led(wc, x, WC_YELLOW); >+ } else if (ts->span.mainttimer || ts->span.maintstat) { >+#ifdef FANCY_ALARM >+ if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) { >+ __t4_set_led(wc, x, WC_GREEN); >+ } >+ if (wc->blinktimer == 0xf) { >+ __t4_set_led(wc, x, WC_OFF); >+ } >+#else >+ if (wc->blinktimer == 160) { >+ __t4_set_led(wc, x, WC_GREEN); >+ } else if (wc->blinktimer == 480) { >+ __t4_set_led(wc, x, WC_OFF); >+ } >+#endif >+ } else { >+ /* No Alarm */ >+ __t4_set_led(wc, x, WC_GREEN); >+ } >+ } else >+ __t4_set_led(wc, x, WC_OFF); >+ >+ } >+#ifdef FANCY_ALARM >+ if (wc->blinktimer == 0xf) { >+ wc->blinktimer = -1; >+ wc->alarmpos++; >+ if (wc->alarmpos >= (sizeof(altab) / sizeof(altab[0]))) >+ wc->alarmpos = 0; >+ } >+#else >+ if (wc->blinktimer == 480) >+ wc->blinktimer = 0; >+#endif >+} >+ >+static inline void t4_framer_interrupt(struct t4 *wc, int span) >+{ >+ unsigned char gis, isr0, isr1, isr2, isr3, isr4; >+#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) >+ /* Check interrupts for a given span */ >+ unsigned char reg; >+#endif >+ int readsize = -1; >+ struct t4_span *ts = wc->tspans[span]; >+ struct dahdi_chan *sigchan; >+ unsigned long flags; >+ >+ >+ /* 1st gen cards isn't used interrupts */ >+ gis = t4_framer_in(wc, span, FRMR_GIS); >+ isr0 = (gis & FRMR_GIS_ISR0) ? t4_framer_in(wc, span, FRMR_ISR0) : 0; >+ isr1 = (gis & FRMR_GIS_ISR1) ? t4_framer_in(wc, span, FRMR_ISR1) : 0; >+ isr2 = (gis & FRMR_GIS_ISR2) ? t4_framer_in(wc, span, FRMR_ISR2) : 0; >+ isr3 = (gis & FRMR_GIS_ISR3) ? t4_framer_in(wc, span, FRMR_ISR3) : 0; >+ isr4 = (gis & FRMR_GIS_ISR4) ? t4_framer_in(wc, span, FRMR_ISR4) : 0; >+ >+ if ((debug & DEBUG_FRAMER) && !(isr3 & ISR3_SEC)) { >+ dev_info(&wc->dev->dev, "gis: %02x, isr0: %02x, isr1: %02x, "\ >+ "isr2: %02x, isr3: %08x, isr4: %02x, intcount=%u\n", >+ gis, isr0, isr1, isr2, isr3, isr4, wc->intcount); >+ } >+ >+#if (defined(DAHDI_SPAN_OPS) || defined(DAHDI_SPAN_MODULE) ) >+ /* Collect performance counters once per second */ >+ if (isr3 & ISR3_SEC) { >+ ts->span.count.fe += t4_framer_in(wc, span, FECL_T); >+ ts->span.count.crc4 += t4_framer_in(wc, span, CEC1L_T); >+ ts->span.count.cv += t4_framer_in(wc, span, CVCL_T); >+ ts->span.count.ebit += t4_framer_in(wc, span, EBCL_T); >+ ts->span.count.be += t4_framer_in(wc, span, BECL_T); >+ ts->span.count.prbs = t4_framer_in(wc, span, FRS1_T); >+ } >+ >+ /* Collect errored second counter once per second */ >+ if (isr3 & ISR3_ES) { >+ ts->span.count.errsec += 1; >+ } >+ >+ if (isr3 & 0x08) { >+ reg = t4_framer_in(wc, span, FRS1_T); >+ dev_info(&wc->dev->dev, "FRS1: %d\n", reg); >+ if (reg & LLBDD) { >+ dev_info(&wc->dev->dev, "Line loop-back activation "\ >+ "signal detected with status: %01d "\ >+ "for span %d\n", reg & LLBAD, span+1); >+ } >+ } >+#endif >+ >+ if (isr0) >+ t4_check_sigbits(wc, span); >+ >+ if (ts->spantype == TYPE_E1) { >+ /* E1 checks */ >+ if ((isr3 & 0x38) || isr2 || isr1) >+ t4_check_alarms(wc, span); >+ } else { >+ /* T1 checks */ >+ if (isr2 || (isr3 & 0x08)) >+ t4_check_alarms(wc, span); >+ } >+ if (!ts->span.alarms) { >+ if ((isr3 & 0x3) || (isr4 & 0xc0)) >+ ts->span.timingslips++; >+ >+ if (debug & DEBUG_MAIN) { >+ if (isr3 & 0x02) >+ dev_notice(&wc->dev->dev, "opvxd115: RECEIVE " >+ "slip NEGATIVE on span %d\n", >+ span + 1); >+ if (isr3 & 0x01) >+ dev_notice(&wc->dev->dev, "opvxd115: RECEIVE " >+ "slip POSITIVE on span %d\n", >+ span + 1); >+ if (isr4 & 0x80) >+ dev_notice(&wc->dev->dev, "opvxd115: TRANSMIT " >+ "slip POSITIVE on span %d\n", >+ span + 1); >+ if (isr4 & 0x40) >+ dev_notice(&wc->dev->dev, "opvxd115: TRANSMIT " >+ "slip NEGATIVE on span %d\n", >+ span + 1); >+ } >+ } else >+ ts->span.timingslips = 0; >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ /* HDLC controller checks - receive side */ >+ if (!ts->sigchan) { >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ return; >+ } >+ >+ sigchan = ts->sigchan; >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ >+ if (isr0 & FRMR_ISR0_RME) { >+ readsize = (t4_framer_in(wc, span, FRMR_RBCH) << 8) | t4_framer_in(wc, span, FRMR_RBCL); >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "Received data length is %d " >+ "(%d)\n", readsize, >+ readsize & FRMR_RBCL_MAX_SIZE); >+ /* RPF isn't set on last part of frame */ >+ if ((readsize > 0) && ((readsize &= FRMR_RBCL_MAX_SIZE) == 0)) >+ readsize = FRMR_RBCL_MAX_SIZE + 1; >+ } else if (isr0 & FRMR_ISR0_RPF) >+ readsize = FRMR_RBCL_MAX_SIZE + 1; >+ >+ if (readsize > 0) { >+ int i; >+ unsigned char readbuf[FRMR_RBCL_MAX_SIZE + 1]; >+ >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "Framer %d: Got RPF/RME! " >+ "readsize is %d\n", sigchan->span->offset, >+ readsize); >+ >+ for (i = 0; i < readsize; i++) >+ readbuf[i] = t4_framer_in(wc, span, FRMR_RXFIFO); >+ >+ /* Tell the framer to clear the RFIFO */ >+ t4_framer_cmd_wait(wc, span, FRMR_CMDR_RMC); >+ >+ if (debug & DEBUG_FRAMER) { >+ dev_notice(&wc->dev->dev, "RX("); >+ for (i = 0; i < readsize; i++) >+ dev_notice(&wc->dev->dev, "%s%02x", >+ (i ? " " : ""), readbuf[i]); >+ dev_notice(&wc->dev->dev, ")\n"); >+ } >+ >+ if (isr0 & FRMR_ISR0_RME) { >+ /* Do checks for HDLC problems */ >+ unsigned char rsis = readbuf[readsize-1]; >+#if 0 >+ unsigned int olddebug = debug; >+#endif >+ unsigned char rsis_reg = t4_framer_in(wc, span, FRMR_RSIS); >+ >+#if 0 >+ if ((rsis != 0xA2) || (rsis != rsis_reg)) >+ debug |= DEBUG_FRAMER; >+#endif >+ >+ ++ts->frames_in; >+ if ((debug & DEBUG_FRAMER) && !(ts->frames_in & 0x0f)) >+ dev_notice(&wc->dev->dev, "Received %d frames " >+ "on span %d\n", ts->frames_in, span); >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "Received HDLC frame" >+ " %d. RSIS = 0x%x (%x)\n", >+ ts->frames_in, rsis, rsis_reg); >+ if (!(rsis & FRMR_RSIS_CRC16)) { >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "CRC check " >+ "failed %d\n", span); >+ dahdi_hdlc_abort(sigchan, DAHDI_EVENT_BADFCS); >+ } else if (rsis & FRMR_RSIS_RAB) { >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "ABORT of " >+ "current frame due to " >+ "overflow %d\n", span); >+ dahdi_hdlc_abort(sigchan, DAHDI_EVENT_ABORT); >+ } else if (rsis & FRMR_RSIS_RDO) { >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "HDLC " >+ "overflow occured %d\n", >+ span); >+ dahdi_hdlc_abort(sigchan, DAHDI_EVENT_OVERRUN); >+ } else if (!(rsis & FRMR_RSIS_VFR)) { >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "Valid Frame" >+ " check failed on span %d\n", >+ span); >+ dahdi_hdlc_abort(sigchan, DAHDI_EVENT_ABORT); >+ } else { >+ dahdi_hdlc_putbuf(sigchan, readbuf, readsize - 1); >+ dahdi_hdlc_finish(sigchan); >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "Received " >+ "valid HDLC frame on span %d" >+ "\n", span); >+ } >+#if 0 >+ debug = olddebug; >+#endif >+ } else if (isr0 & FRMR_ISR0_RPF) >+ dahdi_hdlc_putbuf(sigchan, readbuf, readsize); >+ } >+ >+ /* Transmit side */ >+ if (isr1 & FRMR_ISR1_XDU) { >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "XDU: Resetting signal " >+ "controller!\n"); >+ t4_framer_cmd_wait(wc, span, FRMR_CMDR_SRES); >+ } else if (isr1 & FRMR_ISR1_XPR) { >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "Sigchan %d is %p\n", >+ sigchan->chanpos, sigchan); >+ >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "Framer %d: Got XPR!\n", >+ sigchan->span->offset); >+ t4_hdlc_xmit_fifo(wc, span, ts); >+ } >+ >+ if (isr1 & FRMR_ISR1_ALLS) { >+ if (debug & DEBUG_FRAMER) >+ dev_notice(&wc->dev->dev, "ALLS received\n"); >+ } >+} >+ >+#ifdef SUPPORT_GEN1 >+DAHDI_IRQ_HANDLER(t4_interrupt) >+{ >+ struct t4 *wc = dev_id; >+ unsigned long flags; >+ int x; >+ >+ unsigned int status; >+ unsigned int status2; >+ >+#if 0 >+ if (wc->intcount < 20) >+ dev_notice(&wc->dev->dev, "Pre-interrupt\n"); >+#endif >+ >+ /* Make sure it's really for us */ >+ status = __t4_pci_in(wc, WC_INTR); >+ >+ /* Process framer interrupts */ >+ status2 = t4_framer_in(wc, 0, FRMR_CIS); >+ if (status2 & 0x0f) { >+ for (x = 0; x < wc->numspans; ++x) { >+ if (status2 & (1 << x)) >+ t4_framer_interrupt(wc, x); >+ } >+ } >+ >+ /* Ignore if it's not for us */ >+ if (!status) >+ return IRQ_NONE; >+ >+ __t4_pci_out(wc, WC_INTR, 0); >+ >+ if (!wc->spansstarted) { >+ dev_notice(&wc->dev->dev, "Not prepped yet!\n"); >+ return IRQ_NONE; >+ } >+ >+ wc->intcount++; >+#if 0 >+ if (wc->intcount < 20) >+ dev_notice(&wc->dev->dev, "Got interrupt, status = %08x\n", >+ status); >+#endif >+ >+ if (status & 0x3) { >+ t4_receiveprep(wc, status); >+ t4_transmitprep(wc, status); >+ } >+ >+#if 0 >+ if ((wc->intcount < 10) || !(wc->intcount % 1000)) { >+ status2 = t4_framer_in(wc, 0, FRMR_CIS); >+ dev_notice(&wc->dev->dev, "Status2: %04x\n", status2); >+ for (x = 0;x<wc->numspans;x++) { >+ status2 = t4_framer_in(wc, x, FRMR_FRS0); >+ dev_notice(&wc->dev->dev, "FRS0/%d: %04x\n", x, >+ status2); >+ } >+ } >+#endif >+ t4_do_counters(wc); >+ >+ x = wc->intcount & 15 /* 63 */; >+ switch(x) { >+ case 0: >+ case 1: >+ case 2: >+ case 3: >+ t4_check_sigbits(wc, x); >+ break; >+ case 4: >+ case 5: >+ case 6: >+ case 7: >+ t4_check_alarms(wc, x - 4); >+ break; >+ } >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ >+ __handle_leds(wc); >+ >+ if (test_bit(T4_CHECK_TIMING, &wc->checkflag)) >+ __t4_set_timing_source_auto(wc); >+ >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ >+ return IRQ_RETVAL(1); >+} >+#endif >+ >+static int t4_allocate_buffers(struct t4 *wc, int numbufs, volatile unsigned int **oldalloc, dma_addr_t *oldwritedma) >+{ >+ volatile unsigned int *alloc; >+ dma_addr_t writedma; >+ >+ alloc = >+ /* 32 channels, Double-buffer, Read/Write, 4 spans */ >+ (unsigned int *)pci_alloc_consistent(wc->dev, numbufs * T4_BASE_SIZE * 2, &writedma); >+ >+ if (!alloc) { >+ dev_notice(&wc->dev->dev, "wct%dxxp: Unable to allocate " >+ "DMA-able memory\n", wc->numspans); >+ return -ENOMEM; >+ } >+ >+ if (oldwritedma) >+ *oldwritedma = wc->writedma; >+ if (oldalloc) >+ *oldalloc = wc->writechunk; >+ >+ wc->writechunk = alloc; >+ wc->writedma = writedma; >+ >+ /* Read is after the whole write piece (in words) */ >+ wc->readchunk = wc->writechunk + (T4_BASE_SIZE * numbufs) / 4; >+ >+ /* Same thing but in bytes... */ >+ wc->readdma = wc->writedma + (T4_BASE_SIZE * numbufs); >+ >+ wc->numbufs = numbufs; >+ >+ /* Initialize Write/Buffers to all blank data */ >+ memset((void *)wc->writechunk,0x00, T4_BASE_SIZE * numbufs); >+ memset((void *)wc->readchunk,0xff, T4_BASE_SIZE * numbufs); >+ >+ dev_notice(&wc->dev->dev, "DMA memory base of size %d at %p. Read: " >+ "%p and Write %p\n", numbufs * T4_BASE_SIZE * 2, >+ wc->writechunk, wc->readchunk, wc->writechunk); >+ >+ return 0; >+} >+ >+static void t4_increase_latency(struct t4 *wc, int newlatency) >+{ >+ unsigned long flags; >+ volatile unsigned int *oldalloc; >+ dma_addr_t oldaddr; >+ int oldbufs; >+ >+ spin_lock_irqsave(&wc->reglock, flags); >+ >+ __t4_pci_out(wc, WC_DMACTRL, 0x00000000); >+ /* Acknowledge any pending interrupts */ >+ __t4_pci_out(wc, WC_INTR, 0x00000000); >+ >+ __t4_pci_in(wc, WC_VERSION); >+ >+ oldbufs = wc->numbufs; >+ >+ if (t4_allocate_buffers(wc, newlatency, &oldalloc, &oldaddr)) { >+ dev_info(&wc->dev->dev, "Error allocating latency buffers for " >+ "latency of %d\n", newlatency); >+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ return; >+ } >+ >+ __t4_pci_out(wc, WC_RDADDR, wc->readdma); >+ __t4_pci_out(wc, WC_WRADDR, wc->writedma); >+ >+ __t4_pci_in(wc, WC_VERSION); >+ >+ __t4_pci_out(wc, 5, (ms_per_irq << 16) | newlatency); >+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+ >+ __t4_pci_in(wc, WC_VERSION); >+ >+ wc->rxident = 0; >+ wc->lastindex = 0; >+ >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ >+ pci_free_consistent(wc->dev, T4_BASE_SIZE * oldbufs * 2, (void *)oldalloc, oldaddr); >+ >+ dev_info(&wc->dev->dev, "Increased latency to %d\n", newlatency); >+ >+} >+ >+static void t4_isr_bh(unsigned long data) >+{ >+ struct t4 *wc = (struct t4 *)data; >+ >+ if (test_bit(T4_CHANGE_LATENCY, &wc->checkflag)) { >+ if (wc->needed_latency != wc->numbufs) { >+ t4_increase_latency(wc, wc->needed_latency); >+ clear_bit(T4_CHANGE_LATENCY, &wc->checkflag); >+ } >+ } >+#ifdef VPM_SUPPORT >+ if (wc->vpm) { >+ if (test_and_clear_bit(T4_CHECK_VPM, &wc->checkflag)) { >+ if (wc->vpm450m) { >+ /* How stupid is it that the octasic can't generate an >+ interrupt when there's a tone, in spite of what their >+ documentation says? */ >+ t4_check_vpm450(wc); >+ } else >+ t4_check_vpm400(wc, wc->vpm400checkstatus); >+ } >+ } >+#endif >+} >+ >+DAHDI_IRQ_HANDLER(t4_interrupt_gen2) >+{ >+ struct t4 *wc = dev_id; >+ unsigned int status; >+ unsigned char rxident, expected; >+ >+ /* Check this first in case we get a spurious interrupt */ >+ if (unlikely(test_bit(T4_STOP_DMA, &wc->checkflag))) { >+ /* Stop DMA cleanly if requested */ >+ wc->dmactrl = 0x0; >+ t4_pci_out(wc, WC_DMACTRL, 0x00000000); >+ /* Acknowledge any pending interrupts */ >+ t4_pci_out(wc, WC_INTR, 0x00000000); >+ spin_lock(&wc->reglock); >+ __t4_set_sclk_src(wc, WC_SELF, 0, 0); >+ spin_unlock(&wc->reglock); >+ return IRQ_RETVAL(1); >+ } >+ >+ /* Make sure it's really for us */ >+ status = __t4_pci_in(wc, WC_INTR); >+ >+ /* Ignore if it's not for us */ >+ if (!(status & 0x7)) { >+ return IRQ_NONE; >+ } >+ >+#ifdef ENABLE_WORKQUEUES >+ __t4_pci_out(wc, WC_INTR, status & 0x00000008); >+#endif >+ >+ if (unlikely(!wc->spansstarted)) { >+ dev_info(&wc->dev->dev, "Not prepped yet!\n"); >+ return IRQ_NONE; >+ } >+ >+ wc->intcount++; >+ >+ if ((wc->flags & FLAG_5THGEN) && (status & 0x2)) { >+ rxident = (status >> 16) & 0x7f; >+ expected = (wc->rxident + ms_per_irq) % 128; >+ >+ if ((rxident != expected) && !test_bit(T4_IGNORE_LATENCY, &wc->checkflag)) { >+ int needed_latency; >+ int smallest_max; >+ >+ if (debug & DEBUG_MAIN) >+ dev_warn(&wc->dev->dev, "Missed interrupt. " >+ "Expected ident of %d and got ident " >+ "of %d\n", expected, rxident); >+ >+ if (test_bit(T4_IGNORE_LATENCY, &wc->checkflag)) { >+ dev_info(&wc->dev->dev, >+ "Should have ignored latency\n"); >+ } >+ if (rxident > wc->rxident) { >+ needed_latency = rxident - wc->rxident; >+ } else { >+ needed_latency = (128 - wc->rxident) + rxident; >+ } >+ >+ needed_latency += 1; >+ >+ smallest_max = (max_latency >= GEN5_MAX_LATENCY) ? GEN5_MAX_LATENCY : max_latency; >+ >+ if (needed_latency > smallest_max) { >+ dev_info(&wc->dev->dev, "Truncating latency " >+ "request to %d instead of %d\n", >+ smallest_max, needed_latency); >+ needed_latency = smallest_max; >+ } >+ >+ if (needed_latency > wc->numbufs) { >+ int x; >+ >+ dev_info(&wc->dev->dev, "Need to increase " >+ "latency. Estimated latency should " >+ "be %d\n", needed_latency); >+ for (x = 0; x < wc->numspans; x++) >+ wc->tspans[x]->span.irqmisses++; >+ wc->needed_latency = needed_latency; >+ __t4_pci_out(wc, WC_DMACTRL, 0x00000000); >+ set_bit(T4_CHANGE_LATENCY, &wc->checkflag); >+ goto out; >+ } >+ } >+ >+ wc->rxident = rxident; >+ } >+ >+ if (unlikely((wc->intcount < 20))) >+ >+ dev_info(&wc->dev->dev, "2G: Got interrupt, status = %08x, " >+ "CIS = %04x\n", status, t4_framer_in(wc, 0, FRMR_CIS)); >+ >+ if (likely(status & 0x2)) { >+#ifdef ENABLE_WORKQUEUES >+ int cpus = num_online_cpus(); >+ atomic_set(&wc->worklist, wc->numspans); >+ if (wc->tspans[0]->span.flags & DAHDI_FLAG_RUNNING) >+ t4_queue_work(wc->workq, &wc->tspans[0]->swork, 0); >+ else >+ atomic_dec(&wc->worklist); >+#else >+#if 1 >+ unsigned int reg5 = __t4_pci_in(wc, 5); >+ if (wc->intcount < 20) { >+ >+ dev_info(&wc->dev->dev, "Reg 5 is %08x\n", reg5); >+ } >+#endif >+ >+ if (wc->flags & FLAG_5THGEN) { >+ unsigned int current_index = (reg5 >> 8) & 0x7f; >+ >+ while (((wc->lastindex + 1) % wc->numbufs) != current_index) { >+ wc->lastindex = (wc->lastindex + 1) % wc->numbufs; >+ setup_chunks(wc, wc->lastindex); >+ t4_prep_gen2(wc); >+ } >+ } else { >+ t4_prep_gen2(wc); >+ } >+ >+#endif >+ t4_do_counters(wc); >+ spin_lock(&wc->reglock); >+ __handle_leds(wc); >+ spin_unlock(&wc->reglock); >+ >+ } >+ >+ if (unlikely(status & 0x1)) { >+ unsigned char cis; >+ >+ cis = t4_framer_in(wc, 0, FRMR_CIS); >+ if (cis & FRMR_CIS_GIS1) >+ t4_framer_interrupt(wc, 0); >+ if (cis & FRMR_CIS_GIS2) >+ t4_framer_interrupt(wc, 1); >+ if (cis & FRMR_CIS_GIS3) >+ t4_framer_interrupt(wc, 2); >+ if (cis & FRMR_CIS_GIS4) >+ t4_framer_interrupt(wc, 3); >+ } >+ >+ if (wc->vpm && vpmdtmfsupport) { >+ if (wc->vpm450m) { >+ /* How stupid is it that the octasic can't generate an >+ interrupt when there's a tone, in spite of what their >+ documentation says? */ >+ if (!(wc->intcount & 0xf)) { >+ set_bit(T4_CHECK_VPM, &wc->checkflag); >+ } >+ } else if ((status & 0xff00) != 0xff00) { >+ wc->vpm400checkstatus = (status & 0xff00) >> 8; >+ set_bit(T4_CHECK_VPM, &wc->checkflag); >+ } >+ } >+ >+ spin_lock(&wc->reglock); >+ >+ if (unlikely(test_bit(T4_CHECK_TIMING, &wc->checkflag))) { >+ __t4_set_timing_source_auto(wc); >+ } >+ >+ spin_unlock(&wc->reglock); >+ >+out: >+ if (unlikely(test_bit(T4_CHANGE_LATENCY, &wc->checkflag) || test_bit(T4_CHECK_VPM, &wc->checkflag))) >+ tasklet_schedule(&wc->t4_tlet); >+ >+#ifndef ENABLE_WORKQUEUES >+ __t4_pci_out(wc, WC_INTR, 0); >+#endif >+ >+ return IRQ_RETVAL(1); >+} >+ >+#ifdef SUPPORT_GEN1 >+static int t4_reset_dma(struct t4 *wc) >+{ >+ /* Turn off DMA and such */ >+ wc->dmactrl = 0x0; >+ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+ t4_pci_out(wc, WC_COUNT, 0); >+ t4_pci_out(wc, WC_RDADDR, 0); >+ t4_pci_out(wc, WC_WRADDR, 0); >+ t4_pci_out(wc, WC_INTR, 0); >+ /* Turn it all back on */ >+ t4_pci_out(wc, WC_RDADDR, wc->readdma); >+ t4_pci_out(wc, WC_WRADDR, wc->writedma); >+ t4_pci_out(wc, WC_COUNT, ((DAHDI_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((DAHDI_MAX_CHUNKSIZE * 2 * 32 - 1) << 2)); >+ t4_pci_out(wc, WC_INTR, 0); >+#ifdef VPM_SUPPORT >+ wc->dmactrl = 0xc0000000 | (1 << 29) | wc->vpm; >+#else >+ wc->dmactrl = 0xc0000000 | (1 << 29); >+#endif >+ if (noburst) >+ wc->dmactrl |= (1 << 26); >+ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+ return 0; >+} >+#endif >+ >+#ifdef VPM_SUPPORT >+static void t4_vpm_set_dtmf_threshold(struct t4 *wc, unsigned int threshold) >+{ >+ unsigned int x; >+ >+ for (x = 0; x < 8; x++) { >+ t4_vpm_out(wc, x, 0xC4, (threshold >> 8) & 0xFF); >+ t4_vpm_out(wc, x, 0xC5, (threshold & 0xFF)); >+ } >+ dev_info(&wc->dev->dev, "VPM: DTMF threshold set to %d\n", threshold); >+} >+ >+static unsigned int t4_vpm_mask(int chip) >+{ >+ unsigned int mask=0; >+ switch(vpmspans) { >+ case 4: >+ mask = 0x55555555 << (chip >> 2); >+ break; >+ case 2: >+ mask = 0x11111111 << (chip >> 1); >+ break; >+ case 1: >+ mask = 0x01010101 << chip; >+ break; >+ } >+ return mask; >+} >+ >+static int t4_vpm_spanno(int chip) >+{ >+ int spanno = 0; >+ switch(vpmspans) { >+ case 4: >+ spanno = chip & 0x3; >+ break; >+ case 2: >+ spanno = chip & 0x1; >+ break; >+ /* Case 1 is implicit */ >+ } >+ return spanno; >+} >+ >+static int t4_vpm_echotail(void) >+{ >+ int echotail = 0x01ff; >+ switch(vpmspans) { >+ case 4: >+ echotail = 0x007f; >+ break; >+ case 2: >+ echotail = 0x00ff; >+ break; >+ /* Case 1 is implicit */ >+ } >+ return echotail; >+} >+ >+static void t4_vpm450_init(struct t4 *wc) >+{ >+ unsigned int check1, check2; >+ int laws[1] = { 0, }; >+ int x; >+ unsigned int vpm_capacity; >+ struct firmware embedded_firmware; >+ const struct firmware *firmware = &embedded_firmware; >+#if !defined(HOTPLUG_FIRMWARE) >+ extern void _binary_dahdi_fw_oct6114_032_bin_size; >+ extern void _binary_dahdi_fw_oct6114_064_bin_size; >+ extern void _binary_dahdi_fw_oct6114_128_bin_size; >+ extern u8 _binary_dahdi_fw_oct6114_032_bin_start[]; >+ extern u8 _binary_dahdi_fw_oct6114_064_bin_start[]; >+ extern u8 _binary_dahdi_fw_oct6114_128_bin_start[]; >+#else >+ static const char oct032_firmware[] = "dahdi-fw-oct6114-032.bin"; >+ static const char oct064_firmware[] = "dahdi-fw-oct6114-064.bin"; >+ static const char oct128_firmware[] = "dahdi-fw-oct6114-128.bin"; >+#endif >+ >+ if (!vpmsupport) { >+ dev_info(&wc->dev->dev, "VPM450: Support Disabled\n"); >+ return; >+ } >+ >+ /* Turn on GPIO/DATA mux if supported */ >+ t4_gpio_setdir(wc, (1 << 24), (1 << 24)); >+ __t4_raw_oct_out(wc, 0x000a, 0x5678); >+ __t4_raw_oct_out(wc, 0x0004, 0x1234); >+ check1 = __t4_raw_oct_in(wc, 0x0004); >+ check2 = __t4_raw_oct_in(wc, 0x000a); >+ if (debug) >+ dev_notice(&wc->dev->dev, "OCT Result: %04x/%04x\n", >+ __t4_raw_oct_in(wc, 0x0004), >+ __t4_raw_oct_in(wc, 0x000a)); >+ if (__t4_raw_oct_in(wc, 0x0004) != 0x1234) { >+ dev_notice(&wc->dev->dev, "VPM450: Not Present\n"); >+ return; >+ } >+ >+ /* Setup alaw vs ulaw rules */ >+ for (x = 0;x < wc->numspans; x++) { >+ if (wc->tspans[x]->span.channels > 24) >+ laws[x] = 1; >+ } >+ >+ switch ((vpm_capacity = get_vpm450m_capacity(wc))) { >+ case 32: >+#if defined(HOTPLUG_FIRMWARE) >+ if ((request_firmware(&firmware, oct032_firmware, &wc->dev->dev) != 0) || >+ !firmware) { >+ dev_notice(&wc->dev->dev, "VPM450: firmware %s not " >+ "available from userspace\n", oct032_firmware); >+ return; >+ } >+#else >+ embedded_firmware.data = _binary_dahdi_fw_oct6114_032_bin_start; >+ /* Yes... this is weird. objcopy gives us a symbol containing >+ the size of the firmware, not a pointer a variable containing >+ the size. The only way we can get the value of the symbol >+ is to take its address, so we define it as a pointer and >+ then cast that value to the proper type. >+ */ >+ embedded_firmware.size = (size_t) &_binary_dahdi_fw_oct6114_032_bin_size; >+#endif >+ break; >+ case 64: >+#if defined(HOTPLUG_FIRMWARE) >+ if ((request_firmware(&firmware, oct064_firmware, &wc->dev->dev) != 0) || >+ !firmware) { >+ dev_notice(&wc->dev->dev, "VPM450: firmware %s not " >+ "available from userspace\n", oct064_firmware); >+ return; >+ } >+#else >+ embedded_firmware.data = _binary_dahdi_fw_oct6114_064_bin_start; >+ /* Yes... this is weird. objcopy gives us a symbol containing >+ the size of the firmware, not a pointer a variable containing >+ the size. The only way we can get the value of the symbol >+ is to take its address, so we define it as a pointer and >+ then cast that value to the proper type. >+ */ >+ embedded_firmware.size = (size_t) &_binary_dahdi_fw_oct6114_064_bin_size; >+#endif >+ break; >+ case 128: >+#if defined(HOTPLUG_FIRMWARE) >+ if ((request_firmware(&firmware, oct128_firmware, &wc->dev->dev) != 0) || >+ !firmware) { >+ dev_notice(&wc->dev->dev, "VPM450: firmware %s not " >+ "available from userspace\n", oct128_firmware); >+ return; >+ } >+#else >+ embedded_firmware.data = _binary_dahdi_fw_oct6114_128_bin_start; >+ /* Yes... this is weird. objcopy gives us a symbol containing >+ the size of the firmware, not a pointer a variable containing >+ the size. The only way we can get the value of the symbol >+ is to take its address, so we define it as a pointer and >+ then cast that value to the proper type. >+ */ >+ embedded_firmware.size = (size_t) &_binary_dahdi_fw_oct6114_128_bin_size; >+#endif >+ break; >+ default: >+ dev_notice(&wc->dev->dev, "Unsupported channel capacity found " >+ "on VPM module (%d).\n", vpm_capacity); >+ return; >+ } >+ >+ if (!(wc->vpm450m = init_vpm450m(wc, laws, wc->numspans, firmware))) { >+ dev_notice(&wc->dev->dev, "VPM450: Failed to initialize\n"); >+ if (firmware != &embedded_firmware) >+ release_firmware(firmware); >+ return; >+ } >+ >+ if (firmware != &embedded_firmware) >+ release_firmware(firmware); >+ >+ if (vpmdtmfsupport == -1) { >+ dev_notice(&wc->dev->dev, "VPM450: hardware DTMF disabled.\n"); >+ vpmdtmfsupport = 0; >+ } >+ >+ wc->vpm = T4_VPM_PRESENT; >+ dev_info(&wc->dev->dev, "VPM450: Present and operational servicing %d " >+ "span(s)\n", wc->numspans); >+ >+} >+ >+static void t4_vpm400_init(struct t4 *wc) >+{ >+ unsigned char reg; >+ unsigned int mask; >+ unsigned int ver; >+ unsigned int i, x, y, gen2vpm=0; >+ >+ if (!vpmsupport) { >+ dev_info(&wc->dev->dev, "VPM400: Support Disabled\n"); >+ return; >+ } >+ >+ switch(vpmspans) { >+ case 4: >+ case 2: >+ case 1: >+ break; >+ default: >+ dev_notice(&wc->dev->dev, "VPM400: %d is not a valid vpmspans " >+ "value, using 4\n", vpmspans); >+ vpmspans = 1; >+ } >+ >+ for (x=0;x<8;x++) { >+ int spanno = t4_vpm_spanno(x); >+ struct t4_span *ts = wc->tspans[spanno]; >+ int echotail = t4_vpm_echotail(); >+ >+ ver = t4_vpm_in(wc, x, 0x1a0); /* revision */ >+ if ((ver != 0x26) && (ver != 0x33)) { >+ if (x) >+ dev_notice(&wc->dev->dev, >+ "VPM400: Inoperable\n"); >+ return; >+ } >+ if (ver == 0x33) { >+ if (x && !gen2vpm) { >+ dev_notice(&wc->dev->dev, >+ "VPM400: Inconsistent\n"); >+ return; >+ } >+ ts->spanflags |= FLAG_VPM2GEN; >+ gen2vpm++; >+ } else if (gen2vpm) { >+ dev_notice(&wc->dev->dev, >+ "VPM400: Inconsistent\n"); >+ return; >+ } >+ >+ >+ /* Setup GPIO's */ >+ for (y=0;y<4;y++) { >+ t4_vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */ >+ t4_vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */ >+ t4_vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */ >+ } >+ >+ /* Setup TDM path - sets fsync and tdm_clk as inputs */ >+ reg = t4_vpm_in(wc, x, 0x1a3); /* misc_con */ >+ t4_vpm_out(wc, x, 0x1a3, reg & ~2); >+ >+ /* Setup timeslots */ >+ t4_vpm_out(wc, x, 0x02f, 0x20 | (spanno << 3)); >+ >+ /* Setup Echo length (128 taps) */ >+ t4_vpm_out(wc, x, 0x022, (echotail >> 8)); >+ t4_vpm_out(wc, x, 0x023, (echotail & 0xff)); >+ >+ /* Setup the tdm channel masks for all chips*/ >+ mask = t4_vpm_mask(x); >+ for (i = 0; i < 4; i++) >+ t4_vpm_out(wc, x, 0x30 + i, (mask >> (i << 3)) & 0xff); >+ >+ /* Setup convergence rate */ >+ reg = t4_vpm_in(wc,x,0x20); >+ reg &= 0xE0; >+ if (ts->spantype == TYPE_E1) { >+ if (x < vpmspans) >+ dev_info(&wc->dev->dev, "VPM400: Span %d " >+ "A-law mode\n", spanno); >+ reg |= 0x01; >+ } else { >+ if (x < vpmspans) >+ dev_info(&wc->dev->dev, "VPM400: Span %d " >+ "U-law mode\n", spanno); >+ reg &= ~0x01; >+ } >+ t4_vpm_out(wc,x,0x20,(reg | 0x20)); >+ >+ /* Initialize echo cans */ >+ for (i = 0 ; i < MAX_TDM_CHAN; i++) { >+ if (mask & (0x00000001 << i)) >+ t4_vpm_out(wc,x,i,0x00); >+ } >+ >+ wait_a_little(); >+ >+ /* Put in bypass mode */ >+ for (i = 0 ; i < MAX_TDM_CHAN ; i++) { >+ if (mask & (0x00000001 << i)) { >+ t4_vpm_out(wc,x,i,0x01); >+ } >+ } >+ >+ /* Enable bypass */ >+ for (i = 0 ; i < MAX_TDM_CHAN ; i++) { >+ if (mask & (0x00000001 << i)) >+ t4_vpm_out(wc,x,0x78 + i,0x01); >+ } >+ >+ /* set DTMF detection threshold */ >+ t4_vpm_set_dtmf_threshold(wc, dtmfthreshold); >+ >+ /* Enable DTMF detectors (always DTMF detect all spans) */ >+ for (i = 0; i < MAX_DTMF_DET; i++) { >+ t4_vpm_out(wc, x, 0x98 + i, 0x40 | (i * 2) | ((x < 4) ? 0 : 1)); >+ } >+ for (i = 0x34; i < 0x38; i++) >+ t4_vpm_out(wc, x, i, 0x00); >+ for (i = 0x3C; i < 0x40; i++) >+ t4_vpm_out(wc, x, i, 0x00); >+ >+ for (i = 0x48; i < 0x4B; i++) >+ t4_vpm_out(wc, x, i, 0x00); >+ for (i = 0x50; i < 0x53; i++) >+ t4_vpm_out(wc, x, i, 0x00); >+ for (i = 0xB8; i < 0xBE; i++) >+ t4_vpm_out(wc, x, i, 0xFF); >+ if (gen2vpm) { >+ for (i = 0xBE; i < 0xC0; i++) >+ t4_vpm_out(wc, x, i, 0xFF); >+ } else { >+ for (i = 0xBE; i < 0xC0; i++) >+ t4_vpm_out(wc, x, i, 0x00); >+ } >+ for (i = 0xC0; i < 0xC4; i++) >+ t4_vpm_out(wc, x, i, (x < 4) ? 0x55 : 0xAA); >+ >+ } >+ if (vpmdtmfsupport == -1) { >+ dev_info(&wc->dev->dev, "VPM400: hardware DTMF enabled.\n"); >+ vpmdtmfsupport = 0; >+ } >+ dev_info(&wc->dev->dev, "VPM400%s: Present and operational servicing " >+ "%d span(s)\n", (gen2vpm ? " (2nd Gen)" : ""), wc->numspans); >+ wc->vpm = T4_VPM_PRESENT; >+} >+ >+#endif >+ >+static void t4_tsi_reset(struct t4 *wc) >+{ >+ int x; >+ for (x=0;x<128;x++) { >+ wc->dmactrl &= ~0x00007fff; >+ wc->dmactrl |= (0x00004000 | (x << 7)); >+ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+ } >+ wc->dmactrl &= ~0x00007fff; >+ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+} >+ >+/* Note that channels here start from 1 */ >+static void t4_tsi_assign(struct t4 *wc, int fromspan, int fromchan, int tospan, int tochan) >+{ >+ unsigned long flags; >+ int fromts, tots; >+ >+ fromts = (fromspan << 5) |(fromchan); >+ tots = (tospan << 5) | (tochan); >+ >+ if (!wc->t1e1) { >+ fromts += 4; >+ tots += 4; >+ } >+ spin_lock_irqsave(&wc->reglock, flags); >+ wc->dmactrl &= ~0x00007fff; >+ wc->dmactrl |= (0x00004000 | (tots << 7) | (fromts)); >+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+ wc->dmactrl &= ~0x00007fff; >+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+} >+ >+static void t4_tsi_unassign(struct t4 *wc, int tospan, int tochan) >+{ >+ unsigned long flags; >+ int tots; >+ >+ tots = (tospan << 5) | (tochan); >+ >+ if (!wc->t1e1) >+ tots += 4; >+ spin_lock_irqsave(&wc->reglock, flags); >+ wc->dmactrl &= ~0x00007fff; >+ wc->dmactrl |= (0x00004000 | (tots << 7)); >+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+ if (debug & DEBUG_TSI) >+ dev_notice(&wc->dev->dev, "Sending '%08x\n", wc->dmactrl); >+ wc->dmactrl &= ~0x00007fff; >+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+} >+#ifdef CONFIG_EXTENDED_RESET >+static void t4_extended_reset(struct t4 *wc) >+{ >+ unsigned int oldreg = t4_pci_in(wc, 0x4); >+ >+ udelay(1000); >+ >+ t4_pci_out(wc, 0x4, 0x42000000); >+ t4_pci_out(wc, 0xa, 0x42000000); >+ t4_pci_out(wc, 0xa, 0x00080000); >+ t4_pci_out(wc, 0xa, 0x00080000); >+ t4_pci_out(wc, 0xa, 0x00080000); >+ t4_pci_out(wc, 0xa, 0x00180000); >+ t4_pci_out(wc, 0xa, 0x00080000); >+ t4_pci_out(wc, 0xa, 0x00180000); >+ t4_pci_out(wc, 0xa, 0x00080000); >+ t4_pci_out(wc, 0xa, 0x00180000); >+ t4_pci_out(wc, 0xa, 0x00080000); >+ t4_pci_out(wc, 0xa, 0x00180000); >+ t4_pci_out(wc, 0xa, 0x00080000); >+ t4_pci_out(wc, 0xa, 0x00180000); >+ t4_pci_out(wc, 0xa, 0x00080000); >+ t4_pci_out(wc, 0xa, 0x00180000); >+ t4_pci_out(wc, 0x4, oldreg); >+ >+ udelay(1000); >+} >+#endif >+ >+static int t4_hardware_init_1(struct t4 *wc, unsigned int cardflags) >+{ >+ unsigned int version; >+ >+ version = t4_pci_in(wc, WC_VERSION); >+ dev_info(&wc->dev->dev, "Firmware Version: %08x\n", version); >+ dev_info(&wc->dev->dev, "Burst Mode: %s\n", >+ (!(cardflags & FLAG_BURST) && noburst) ? "Off" : "On"); >+#ifdef ENABLE_WORKQUEUES >+ dev_info(&wc->dev->dev, "Work Queues: Enabled\n"); >+#endif >+ >+#ifdef CONFIG_EXTENDED_RESET >+ t4_extended_reset(wc); >+#endif >+ >+ /* Make sure DMA engine is not running and interrupts are acknowledged */ >+ wc->dmactrl = 0x0; >+ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); >+ /* Reset Framer and friends */ >+ t4_pci_out(wc, WC_LEDS, 0x00000000); >+ >+ /* Set DMA addresses */ >+ t4_pci_out(wc, WC_RDADDR, wc->readdma); >+ t4_pci_out(wc, WC_WRADDR, wc->writedma); >+ >+ /* Setup counters, interrupt flags (ignored in Gen2) */ >+ if (cardflags & FLAG_2NDGEN) { >+ t4_tsi_reset(wc); >+ } else { >+ t4_pci_out(wc, WC_COUNT, ((DAHDI_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((DAHDI_MAX_CHUNKSIZE * 2 * 32 - 1) << 2)); >+ } >+ >+ /* Reset pending interrupts */ >+ t4_pci_out(wc, WC_INTR, 0x00000000); >+ >+ /* Read T1/E1 status */ >+ if (t1e1override > -1) >+ wc->t1e1 = t1e1override; >+ else >+ wc->t1e1 = ((t4_pci_in(wc, WC_LEDS)) & 0x0f00) >> 8; >+ wc->order = ((t4_pci_in(wc, WC_LEDS)) & 0xf0000000) >> 28; >+ order_index[wc->order]++; >+ return 0; >+} >+ >+static int t4_hardware_init_2(struct t4 *wc) >+{ >+ int x; >+ unsigned int regval; >+ >+ if (t4_pci_in(wc, WC_VERSION) >= 0xc01a0165) { >+ wc->tspans[0]->spanflags |= FLAG_OCTOPT; >+ dev_info(&wc->dev->dev, "Octasic Optimizations: Enabled\n"); >+ } >+ /* Setup LEDS, take out of reset */ >+ t4_pci_out(wc, WC_LEDS, 0x000000ff); >+ t4_activate(wc); >+ >+ /* >+ * In order to find out the QFALC framer version, we have to temporarily term off compat >+ * mode and take a peak at VSTR. We turn compat back on when we are done. >+ */ >+ if (t4_framer_in(wc, 0, 0x4a) != 0x05) >+ dev_info(&wc->dev->dev, "WARNING: FALC framer not intialized " >+ "in compatibility mode.\n"); >+ regval = t4_framer_in(wc, 0 ,0xd6); >+ regval |= (1 << 5); /* set COMP_DIS*/ >+ t4_framer_out(wc, 0, 0xd6, regval); >+ regval = t4_framer_in(wc, 0, 0x4a); >+ if (regval == 0x05) >+ dev_info(&wc->dev->dev, "FALC Framer Version: 2.1 or " >+ "earlier\n"); >+ else if (regval == 0x20) { >+ dev_info(&wc->dev->dev, "FALC Framer Version: 3.1\n"); >+ wc->falc31 = 1; >+ } else >+ dev_info(&wc->dev->dev, "FALC Framer Version: Unknown " >+ "(VSTR = 0x%02x)\n", regval); >+ regval = t4_framer_in(wc, 0 ,0xd6); >+ regval &= ~(1 << 5); /* clear COMP_DIS*/ >+ t4_framer_out(wc, 0, 0xd6, regval); >+ >+ t4_framer_out(wc, 0, 0x4a, 0xaa); >+ dev_info(&wc->dev->dev, "Board ID: %02x\n", wc->order); >+ >+ for (x=0;x< 11;x++) >+ dev_info(&wc->dev->dev, "Reg %d: 0x%08x\n", x, >+ t4_pci_in(wc, x)); >+ return 0; >+} >+ >+static int __devinit t4_launch(struct t4 *wc) >+{ >+ int x; >+ unsigned long flags; >+ if (test_bit(DAHDI_FLAGBIT_REGISTERED, &wc->tspans[0]->span.flags)) >+ return 0; >+ dev_info(&wc->dev->dev, "opvxd115: Launching card: %d\n", >+ wc->order); >+ >+ /* Setup serial parameters and system interface */ >+ for (x=0;x<PORTS_PER_FRAMER;x++) >+ t4_serial_setup(wc, x); >+ >+ if (dahdi_register(&wc->tspans[0]->span, 0)) { >+ dev_err(&wc->dev->dev, "Unable to register span %s\n", >+ wc->tspans[0]->span.name); >+ return -1; >+ } >+ set_bit(T4_CHECK_TIMING, &wc->checkflag); >+ spin_lock_irqsave(&wc->reglock, flags); >+ __t4_set_sclk_src(wc, WC_SELF, 0, 0); >+ spin_unlock_irqrestore(&wc->reglock, flags); >+ tasklet_init(&wc->t4_tlet, t4_isr_bh, (unsigned long)wc); >+ return 0; >+} >+ >+static void free_wc(struct t4 *wc) >+{ >+ unsigned int x, y; >+ >+ for (x = 0; x < sizeof(wc->tspans)/sizeof(wc->tspans[0]); x++) { >+ if (!wc->tspans[x]) { >+ continue; >+ } >+ >+ for (y = 0; y < sizeof(wc->tspans[x]->chans)/sizeof(wc->tspans[x]->chans[0]); y++) { >+ if (wc->tspans[x]->chans[y]) { >+ kfree(wc->tspans[x]->chans[y]); >+ } >+ if (wc->tspans[x]->ec[y]) >+ kfree(wc->tspans[x]->ec[y]); >+ } >+ kfree(wc->tspans[x]); >+ } >+ kfree(wc); >+} >+ >+static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) >+{ >+ struct t4 *wc; >+ struct devtype *dt; >+ unsigned int x, f; >+ int init_latency; >+ >+ if (pci_enable_device(pdev)) { >+ return -EIO; >+ } >+ >+ if (!(wc = kmalloc(sizeof(*wc), GFP_KERNEL))) { >+ return -ENOMEM; >+ } >+ >+ memset(wc, 0x0, sizeof(*wc)); >+ spin_lock_init(&wc->reglock); >+ dt = (struct devtype *) (ent->driver_data); >+ >+ wc->flags = dt->flags; >+ >+ wc->numspans = 1; >+ >+ wc->variety = dt->desc; >+ >+ wc->memaddr = pci_resource_start(pdev, 0); >+ wc->memlen = pci_resource_len(pdev, 0); >+ wc->membase = ioremap(wc->memaddr, wc->memlen); >+ /* This rids of the Double missed interrupt message after loading */ >+ wc->last0 = 1; >+#if 0 >+ if (!request_mem_region(wc->memaddr, wc->memlen, wc->variety)) >+ dev_info(&wc->dev->dev, "opvxd115: Unable to request memory " >+ "region :(, using anyway...\n"); >+#endif >+ if (pci_request_regions(pdev, wc->variety)) >+ dev_info(&pdev->dev, "opvxd115: Unable to request regions\n"); >+ >+ dev_info(&pdev->dev, "Found opvxd115 at base address %08lx, remapped " >+ "to %p\n", wc->memaddr, wc->membase); >+ >+ wc->dev = pdev; >+ >+ /* Enable bus mastering */ >+ pci_set_master(pdev); >+ >+ /* Keep track of which device we are */ >+ pci_set_drvdata(pdev, wc); >+ >+ if (wc->flags & FLAG_5THGEN) { >+ if ((ms_per_irq > 1) && (latency <= ((ms_per_irq) << 1))) { >+ init_latency = ms_per_irq << 1; >+ } else { >+ if (latency > 2) >+ init_latency = latency; >+ else >+ init_latency = 2; >+ } >+ dev_info(&wc->dev->dev, "5th gen card with initial latency of " >+ "%d and %d ms per IRQ\n", init_latency, ms_per_irq); >+ } else { >+ if (wc->flags & FLAG_2NDGEN) >+ init_latency = 1; >+ else >+ init_latency = 2; >+ } >+ >+ if (max_latency < init_latency) { >+ printk(KERN_INFO "maxlatency must be set to something greater than %d ms, increasing it to %d\n", init_latency, init_latency); >+ max_latency = init_latency; >+ } >+ >+ if (t4_allocate_buffers(wc, init_latency, NULL, NULL)) { >+ return -ENOMEM; >+ } >+ >+ /* Initialize hardware */ >+ t4_hardware_init_1(wc, wc->flags); >+ >+ for(x = 0; x < MAX_T4_CARDS; x++) { >+ if (!cards[x]) >+ break; >+ } >+ >+ if (x >= MAX_T4_CARDS) { >+ dev_notice(&wc->dev->dev, "No cards[] slot available!!\n"); >+ kfree(wc); >+ return -ENOMEM; >+ } >+ >+ wc->num = x; >+ cards[x] = wc; >+ >+#ifdef ENABLE_WORKQUEUES >+ if (wc->flags & FLAG_2NDGEN) { >+ char tmp[20]; >+ >+ sprintf(tmp, "opvxd115"); >+ wc->workq = create_workqueue(tmp); >+ } >+#endif >+ >+ /* Allocate pieces we need here */ >+ for (x = 0; x < PORTS_PER_FRAMER; x++) { >+ if (!(wc->tspans[x] = kmalloc(sizeof(*wc->tspans[x]), GFP_KERNEL))) { >+ free_wc(wc); >+ return -ENOMEM; >+ } >+ >+ memset(wc->tspans[x], 0, sizeof(*wc->tspans[x])); >+ >+ if (wc->t1e1 & (1 << x)) { >+ wc->tspans[x]->spantype = TYPE_E1; >+ } else { >+ if (j1mode) >+ wc->tspans[x]->spantype = TYPE_J1; >+ else >+ wc->tspans[x]->spantype = TYPE_T1; >+ } >+ >+ for (f = 0; f < (wc->tspans[x]->spantype == TYPE_E1 ? 31 : 24); f++) { >+ if (!(wc->tspans[x]->chans[f] = kmalloc(sizeof(*wc->tspans[x]->chans[f]), GFP_KERNEL))) { >+ free_wc(wc); >+ return -ENOMEM; >+ } >+ memset(wc->tspans[x]->chans[f], 0, sizeof(*wc->tspans[x]->chans[f])); >+ if (!(wc->tspans[x]->ec[f] = kmalloc(sizeof(*wc->tspans[x]->ec[f]), GFP_KERNEL))) { >+ free_wc(wc); >+ return -ENOMEM; >+ } >+ memset(wc->tspans[x]->ec[f], 0, sizeof(*wc->tspans[x]->ec[f])); >+ } >+ >+#ifdef ENABLE_WORKQUEUES >+ INIT_WORK(&wc->tspans[x]->swork, workq_handlespan, wc->tspans[x]); >+#endif >+ wc->tspans[x]->spanflags |= wc->flags; >+ } >+ >+ /* Continue hardware intiialization */ >+ t4_hardware_init_2(wc); >+ >+#ifdef SUPPORT_GEN1 >+ if (request_irq(pdev->irq, (wc->flags & FLAG_2NDGEN) ? t4_interrupt_gen2 :t4_interrupt, DAHDI_IRQ_SHARED_DISABLED, "opvxd115", wc)) >+#else >+ if (!(wc->tspans[0]->spanflags & FLAG_2NDGEN)) { >+ dev_notice(&wc->dev->dev, "This driver does not " >+ "support 1st gen modules\n"); >+ free_wc(wc); >+ return -ENODEV; >+ } >+ if (request_irq(pdev->irq, t4_interrupt_gen2, DAHDI_IRQ_SHARED_DISABLED, "opvxd115", wc)) >+#endif >+ { >+ dev_notice(&wc->dev->dev, "opvxd115: Unable to request IRQ %d\n", >+ pdev->irq); >+ free_wc(wc); >+ return -EIO; >+ } >+ >+ init_spans(wc); >+ /* get the current number of probed cards and run a slice of a tail >+ * insertion sort */ >+ for (x = 0; x < MAX_T4_CARDS; x++) { >+ if (!cards[x+1]) >+ break; >+ } >+ for ( ; x > 0; x--) { >+ if (cards[x]->order < cards[x-1]->order) { >+ struct t4 *tmp = cards[x]; >+ cards[x] = cards[x-1]; >+ cards[x-1] = tmp; >+ } else { >+ /* if we're not moving it, we won't move any more >+ * since all cards are sorted on addition */ >+ break; >+ } >+ } >+ >+ dev_info(&wc->dev->dev, "Found an OpenVox Card: %s\n", wc->variety); >+ wc->gpio = 0x00000000; >+ t4_pci_out(wc, WC_GPIO, wc->gpio); >+ t4_gpio_setdir(wc, (1 << 17), (1 << 17)); >+ t4_gpio_setdir(wc, (0xff), (0xff)); >+ >+ create_sysfs_files(wc); >+ >+#if 0 >+ for (x=0;x<0x10000;x++) { >+ __t4_raw_oct_out(wc, 0x0004, x); >+ __t4_raw_oct_out(wc, 0x000a, x ^ 0xffff); >+ if (__t4_raw_oct_in(wc, 0x0004) != x) >+ dev_notice(&wc->dev->dev, "Register 4 failed %04x\n", >+ x); >+ if (__t4_raw_oct_in(wc, 0x000a) != (x ^ 0xffff)) >+ dev_notice(&wc->dev->dev, "Register 10 failed %04x\n", >+ x); >+ } >+#endif >+ >+ return 0; >+} >+ >+static int t4_hardware_stop(struct t4 *wc) >+{ >+ >+ /* Turn off DMA, leave interrupts enabled */ >+ set_bit(T4_STOP_DMA, &wc->checkflag); >+ >+ /* Wait for interrupts to stop */ >+ msleep(25); >+ >+ /* Turn off counter, address, etc */ >+ if (wc->tspans[0]->spanflags & FLAG_2NDGEN) { >+ t4_tsi_reset(wc); >+ } else { >+ t4_pci_out(wc, WC_COUNT, 0x000000); >+ } >+ t4_pci_out(wc, WC_RDADDR, 0x0000000); >+ t4_pci_out(wc, WC_WRADDR, 0x0000000); >+ wc->gpio = 0x00000000; >+ t4_pci_out(wc, WC_GPIO, wc->gpio); >+ t4_pci_out(wc, WC_LEDS, 0x00000000); >+ >+ dev_notice(&wc->dev->dev, "\nStopped opvxd115, Turned off DMA\n"); >+ return 0; >+} >+ >+static void __devexit t4_remove_one(struct pci_dev *pdev) >+{ >+ struct t4 *wc = pci_get_drvdata(pdev); >+ struct dahdi_span *span; >+ int basesize; >+ int i; >+ >+ if (!wc) { >+ return; >+ } >+ >+ remove_sysfs_files(wc); >+ >+ /* Stop hardware */ >+ t4_hardware_stop(wc); >+ >+ /* Release vpm450m */ >+ if (wc->vpm450m) >+ release_vpm450m(wc->vpm450m); >+ wc->vpm450m = NULL; >+ /* Unregister spans */ >+ >+ basesize = DAHDI_MAX_CHUNKSIZE * 32 * 4; >+ if (!(wc->tspans[0]->spanflags & FLAG_2NDGEN)) >+ basesize = basesize * 2; >+ >+ for (i = 0; i < wc->numspans; ++i) { >+ span = &wc->tspans[i]->span; >+ if (test_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags)) >+ dahdi_unregister(span); >+ } >+#ifdef ENABLE_WORKQUEUES >+ if (wc->workq) { >+ flush_workqueue(wc->workq); >+ destroy_workqueue(wc->workq); >+ } >+#endif >+ >+ free_irq(pdev->irq, wc); >+ >+ if (wc->membase) >+ iounmap(wc->membase); >+ >+ pci_release_regions(pdev); >+ >+ /* Immediately free resources */ >+ pci_free_consistent(pdev, T4_BASE_SIZE * wc->numbufs * 2, (void *)wc->writechunk, wc->writedma); >+ >+ order_index[wc->order]--; >+ >+ cards[wc->num] = NULL; >+ pci_set_drvdata(pdev, NULL); >+ free_wc(wc); >+} >+ >+ >+static struct pci_device_id t4_pci_tbl[] __devinitdata = >+{ >+ { 0x1b74, 0x0115, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&opvxd115 }, /* OpenVox D115P/D115E */ >+ { 0x1b74, 0xd130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&opvxd130 }, /* OpenVox D130P/D130E */ >+ { 0, } >+}; >+ >+static struct pci_driver t4_driver = { >+ .name = "opvxd115", >+ .probe = t4_init_one, >+ .remove = __devexit_p(t4_remove_one), >+ .id_table = t4_pci_tbl, >+}; >+ >+static int __init t4_init(void) >+{ >+ int res; >+ res = dahdi_pci_module(&t4_driver); >+ if (res) >+ return -ENODEV; >+ /* initialize cards since we have all of them */ >+ /* warn for missing zero and duplicate numbers */ >+ if (cards[0] && cards[0]->order != 0) { >+ printk(KERN_NOTICE "opvxd115: Ident of first card is not zero (%d)\n", >+ cards[0]->order); >+ } >+ for (res = 0; cards[res]; res++) { >+ /* warn the user of duplicate ident values it is probably >+ * unintended */ >+ if (debug && res < 15 && cards[res+1] && >+ cards[res]->order == cards[res+1]->order) { >+ printk(KERN_NOTICE "opvxd115: Duplicate ident value found (%d)\n", >+ cards[res]->order); >+ } >+ t4_launch(cards[res]); >+ } >+ return 0; >+} >+ >+static void __exit t4_cleanup(void) >+{ >+ pci_unregister_driver(&t4_driver); >+} >+ >+ >+MODULE_AUTHOR("mark.liu <mark.liu@openvox.cn>"); >+MODULE_DESCRIPTION("Unified OpenVox Single T1/E1/J1 Card Driver"); >+MODULE_ALIAS("opvxd115"); >+MODULE_LICENSE("GPL v2"); >+ >+module_param(pedanticpci, int, 0600); >+module_param(debug, int, 0600); >+module_param(noburst, int, 0600); >+module_param(timingcable, int, 0600); >+module_param(t1e1override, int, 0600); >+module_param(alarmdebounce, int, 0600); >+module_param(losalarmdebounce, int, 0600); >+module_param(aisalarmdebounce, int, 0600); >+module_param(yelalarmdebounce, int, 0600); >+module_param(max_latency, int, 0600); >+module_param(j1mode, int, 0600); >+module_param(sigmode, int, 0600); >+module_param(latency, int, 0600); >+module_param(ms_per_irq, int, 0600); >+#ifdef VPM_SUPPORT >+module_param(vpmsupport, int, 0600); >+module_param(vpmdtmfsupport, int, 0600); >+module_param(vpmspans, int, 0600); >+module_param(dtmfthreshold, int, 0600); >+#endif >+ >+MODULE_DEVICE_TABLE(pci, t4_pci_tbl); >+ >+module_init(t4_init); >+module_exit(t4_cleanup); >diff --git a/drivers/dahdi/opvxd115/opvxd115-diag.c b/drivers/dahdi/opvxd115/opvxd115-diag.c >new file mode 100644 >index 0000000..7d1dfdb >--- /dev/null >+++ b/drivers/dahdi/opvxd115/opvxd115-diag.c >@@ -0,0 +1,427 @@ >+/* >+ * See http://www.asterisk.org for more information about >+ * the Asterisk project. Please do not directly contact >+ * any of the maintainers of this project for assistance; >+ * the project provides a web site, mailing lists and IRC >+ * channels for your use. >+ * >+ * This program is free software, distributed under the terms of >+ * the GNU General Public License Version 2 as published by the >+ * Free Software Foundation. See the LICENSE file included with >+ * this program for more details. >+ */ >+ >+#include <fcntl.h> >+#include <stdio.h> >+#include <stdlib.h> >+#include <unistd.h> >+#include <sys/ioctl.h> >+#include <errno.h> >+#include <string.h> >+#include <dahdi/user.h> >+#include "opvxd115.h" >+ >+struct t4_reg_def { >+ int reg; >+ char *name; >+ int global; >+}; >+static struct t4_reg_def xreginfo[] = { >+ { 0x00, "RDADDR" }, >+ { 0x01, "WRADDR" }, >+ { 0x02, "COUNT" }, >+ { 0x03, "DMACTRL" }, >+ { 0x04, "WCINTR" }, >+ { 0x06, "VERSION" }, >+ { 0x07, "LEDS" }, >+ { 0x08, "GPIOCTL" }, >+ { 0x09, "GPIO" }, >+ { 0x0A, "LADDR" }, >+ { 0x0b, "LDATA" }, >+}; >+ >+static struct t4_reg_def reginfo[] = { >+ { 0x00, "XFIFO" }, >+ { 0x01, "XFIFO" }, >+ { 0x02, "CMDR" }, >+ { 0x03, "MODE" }, >+ { 0x04, "RAH1" }, >+ { 0x05, "RAH2" }, >+ { 0x06, "RAL1" }, >+ { 0x07, "RAL2" }, >+ { 0x08, "IPC", 1 }, >+ { 0x09, "CCR1" }, >+ { 0x0a, "CCR2" }, >+ { 0x0c, "RTR1" }, >+ { 0x0d, "RTR2" }, >+ { 0x0e, "RTR3" }, >+ { 0x0f, "RTR4" }, >+ { 0x10, "TTR1" }, >+ { 0x11, "TTR2" }, >+ { 0x12, "TTR3" }, >+ { 0x13, "TTR4" }, >+ { 0x14, "IMR0" }, >+ { 0x15, "IMR1" }, >+ { 0x16, "IMR2" }, >+ { 0x17, "IMR3" }, >+ { 0x18, "IMR4" }, >+ { 0x1b, "IERR" }, >+ { 0x1c, "FMR0" }, >+ { 0x1d, "FMR1" }, >+ { 0x1e, "FMR2" }, >+ { 0x1f, "LOOP" }, >+ { 0x20, "XSW" }, >+ { 0x21, "XSP" }, >+ { 0x22, "XC0" }, >+ { 0x23, "XC1" }, >+ { 0x24, "RC0" }, >+ { 0x25, "RC1" }, >+ { 0x26, "XPM0" }, >+ { 0x27, "XPM1" }, >+ { 0x28, "XPM2" }, >+ { 0x29, "TSWM" }, >+ { 0x2b, "IDLE" }, >+ { 0x2c, "XSA4" }, >+ { 0x2d, "XSA5" }, >+ { 0x2e, "XSA6" }, >+ { 0x2f, "XSA7" }, >+ { 0x30, "XSA8" }, >+ { 0x31, "FMR3" }, >+ { 0x32, "ICB1" }, >+ { 0x33, "ICB2" }, >+ { 0x34, "ICB3" }, >+ { 0x35, "ICB4" }, >+ { 0x36, "LIM0" }, >+ { 0x37, "LIM1" }, >+ { 0x38, "PCD" }, >+ { 0x39, "PCR" }, >+ { 0x3a, "LIM2" }, >+ { 0x3b, "LCR1" }, >+ { 0x3c, "LCR2" }, >+ { 0x3d, "LCR3" }, >+ { 0x3e, "SIC1" }, >+ { 0x3f, "SIC2" }, >+ { 0x40, "SIC3" }, >+ { 0x44, "CMR1" }, >+ { 0x45, "CMR2" }, >+ { 0x46, "GCR" }, >+ { 0x47, "ESM" }, >+ { 0x60, "DEC" }, >+ { 0x70, "XS1" }, >+ { 0x71, "XS2" }, >+ { 0x72, "XS3" }, >+ { 0x73, "XS4" }, >+ { 0x74, "XS5" }, >+ { 0x75, "XS6" }, >+ { 0x76, "XS7" }, >+ { 0x77, "XS8" }, >+ { 0x78, "XS9" }, >+ { 0x79, "XS10" }, >+ { 0x7a, "XS11" }, >+ { 0x7b, "XS12" }, >+ { 0x7c, "XS13" }, >+ { 0x7d, "XS14" }, >+ { 0x7e, "XS15" }, >+ { 0x7f, "XS16" }, >+ { 0x80, "PC1" }, >+ { 0x81, "PC2" }, >+ { 0x82, "PC3" }, >+ { 0x83, "PC4" }, >+ { 0x84, "PC5" }, >+ { 0x85, "GPC1", 1 }, >+ { 0x87, "CMDR2" }, >+ { 0x8d, "CCR5" }, >+ { 0x92, "GCM1", 1 }, >+ { 0x93, "GCM2", 1 }, >+ { 0x94, "GCM3", 1 }, >+ { 0x95, "GCM4", 1 }, >+ { 0x96, "GCM5", 1 }, >+ { 0x97, "GCM6", 1 }, >+ { 0x98, "GCM7", 1 }, >+ { 0x99, "GCM8", 1 }, >+ { 0xa0, "TSEO" }, >+ { 0xa1, "TSBS1" }, >+ { 0xa8, "TPC0" }, >+}; >+ >+static struct t4_reg_def t1_reginfo[] = { >+ { 0x00, "XFIFO" }, >+ { 0x01, "XFIFO" }, >+ { 0x02, "CMDR" }, >+ { 0x03, "MODE" }, >+ { 0x04, "RAH1" }, >+ { 0x05, "RAH2" }, >+ { 0x06, "RAL1" }, >+ { 0x07, "RAL2" }, >+ { 0x08, "IPC", 1 }, >+ { 0x09, "CCR1" }, >+ { 0x0a, "CCR2" }, >+ { 0x0c, "RTR1" }, >+ { 0x0d, "RTR2" }, >+ { 0x0e, "RTR3" }, >+ { 0x0f, "RTR4" }, >+ { 0x10, "TTR1" }, >+ { 0x11, "TTR2" }, >+ { 0x12, "TTR3" }, >+ { 0x13, "TTR4" }, >+ { 0x14, "IMR0" }, >+ { 0x15, "IMR1" }, >+ { 0x16, "IMR2" }, >+ { 0x17, "IMR3" }, >+ { 0x18, "IMR4" }, >+ { 0x1b, "IERR" }, >+ { 0x1c, "FMR0" }, >+ { 0x1d, "FMR1" }, >+ { 0x1e, "FMR2" }, >+ { 0x1f, "LOOP" }, >+ { 0x20, "FMR4" }, >+ { 0x21, "FMR5" }, >+ { 0x22, "XC0" }, >+ { 0x23, "XC1" }, >+ { 0x24, "RC0" }, >+ { 0x25, "RC1" }, >+ { 0x26, "XPM0" }, >+ { 0x27, "XPM1" }, >+ { 0x28, "XPM2" }, >+ { 0x2b, "IDLE" }, >+ { 0x2c, "XDL1" }, >+ { 0x2d, "XDL2" }, >+ { 0x2e, "XDL3" }, >+ { 0x2f, "CCB1" }, >+ { 0x30, "CCB2" }, >+ { 0x31, "CCB3" }, >+ { 0x32, "ICB1" }, >+ { 0x33, "ICB2" }, >+ { 0x34, "ICB3" }, >+ { 0x36, "LIM0" }, >+ { 0x37, "LIM1" }, >+ { 0x38, "PCD" }, >+ { 0x39, "PCR" }, >+ { 0x3a, "LIM2" }, >+ { 0x3b, "LCR1" }, >+ { 0x3c, "LCR2" }, >+ { 0x3d, "LCR3" }, >+ { 0x3e, "SIC1" }, >+ { 0x3f, "SIC2" }, >+ { 0x40, "SIC3" }, >+ { 0x44, "CMR1" }, >+ { 0x45, "CMR2" }, >+ { 0x46, "GCR" }, >+ { 0x47, "ESM" }, >+ { 0x60, "DEC" }, >+ { 0x70, "XS1" }, >+ { 0x71, "XS2" }, >+ { 0x72, "XS3" }, >+ { 0x73, "XS4" }, >+ { 0x74, "XS5" }, >+ { 0x75, "XS6" }, >+ { 0x76, "XS7" }, >+ { 0x77, "XS8" }, >+ { 0x78, "XS9" }, >+ { 0x79, "XS10" }, >+ { 0x7a, "XS11" }, >+ { 0x7b, "XS12" }, >+ { 0x80, "PC1" }, >+ { 0x81, "PC2" }, >+ { 0x82, "PC3" }, >+ { 0x83, "PC4" }, >+ { 0x84, "PC5" }, >+ { 0x85, "GPC1", 1 }, >+ { 0x87, "CMDR2" }, >+ { 0x8d, "CCR5" }, >+ { 0x92, "GCM1", 1 }, >+ { 0x93, "GCM2", 1 }, >+ { 0x94, "GCM3", 1 }, >+ { 0x95, "GCM4", 1 }, >+ { 0x96, "GCM5", 1 }, >+ { 0x97, "GCM6", 1 }, >+ { 0x98, "GCM7", 1 }, >+ { 0x99, "GCM8", 1 }, >+ { 0xa0, "TSEO" }, >+ { 0xa1, "TSBS1" }, >+ { 0xa8, "TPC0" }, >+}; >+ >+static struct t4_reg_def t1_sreginfo[] = { >+ { 0x00, "RFIFO" }, >+ { 0x01, "RFIFO" }, >+ { 0x49, "RBD" }, >+ { 0x4a, "VSTR", 1 }, >+ { 0x4b, "RES" }, >+ { 0x4c, "FRS0" }, >+ { 0x4d, "FRS1" }, >+ { 0x4e, "FRS2" }, >+ { 0x4f, "Old FRS1" }, >+ { 0x50, "FECL" }, >+ { 0x51, "FECH" }, >+ { 0x52, "CVCL" }, >+ { 0x53, "CVCH" }, >+ { 0x54, "CECL" }, >+ { 0x55, "CECH" }, >+ { 0x56, "EBCL" }, >+ { 0x57, "EBCH" }, >+ { 0x58, "BECL" }, >+ { 0x59, "BECH" }, >+ { 0x5a, "COEC" }, >+ { 0x5c, "RDL1" }, >+ { 0x5d, "RDL2" }, >+ { 0x5e, "RDL3" }, >+ { 0x62, "RSP1" }, >+ { 0x63, "RSP2" }, >+ { 0x64, "SIS" }, >+ { 0x65, "RSIS" }, >+ { 0x66, "RBCL" }, >+ { 0x67, "RBCH" }, >+ { 0x68, "ISR0" }, >+ { 0x69, "ISR1" }, >+ { 0x6a, "ISR2" }, >+ { 0x6b, "ISR3" }, >+ { 0x6c, "ISR4" }, >+ { 0x6e, "GIS" }, >+ { 0x6f, "CIS", 1 }, >+ { 0x70, "RS1" }, >+ { 0x71, "RS2" }, >+ { 0x72, "RS3" }, >+ { 0x73, "RS4" }, >+ { 0x74, "RS5" }, >+ { 0x75, "RS6" }, >+ { 0x76, "RS7" }, >+ { 0x77, "RS8" }, >+ { 0x78, "RS9" }, >+ { 0x79, "RS10" }, >+ { 0x7a, "RS11" }, >+ { 0x7b, "RS12" }, >+}; >+ >+static struct t4_reg_def sreginfo[] = { >+ { 0x00, "RFIFO" }, >+ { 0x01, "RFIFO" }, >+ { 0x49, "RBD" }, >+ { 0x4a, "VSTR", 1 }, >+ { 0x4b, "RES" }, >+ { 0x4c, "FRS0" }, >+ { 0x4d, "FRS1" }, >+ { 0x4e, "RSW" }, >+ { 0x4f, "RSP" }, >+ { 0x50, "FECL" }, >+ { 0x51, "FECH" }, >+ { 0x52, "CVCL" }, >+ { 0x53, "CVCH" }, >+ { 0x54, "CEC1L" }, >+ { 0x55, "CEC1H" }, >+ { 0x56, "EBCL" }, >+ { 0x57, "EBCH" }, >+ { 0x58, "CEC2L" }, >+ { 0x59, "CEC2H" }, >+ { 0x5a, "CEC3L" }, >+ { 0x5b, "CEC3H" }, >+ { 0x5c, "RSA4" }, >+ { 0x5d, "RSA5" }, >+ { 0x5e, "RSA6" }, >+ { 0x5f, "RSA7" }, >+ { 0x60, "RSA8" }, >+ { 0x61, "RSA6S" }, >+ { 0x62, "RSP1" }, >+ { 0x63, "RSP2" }, >+ { 0x64, "SIS" }, >+ { 0x65, "RSIS" }, >+ { 0x66, "RBCL" }, >+ { 0x67, "RBCH" }, >+ { 0x68, "ISR0" }, >+ { 0x69, "ISR1" }, >+ { 0x6a, "ISR2" }, >+ { 0x6b, "ISR3" }, >+ { 0x6c, "ISR4" }, >+ { 0x6e, "GIS" }, >+ { 0x6f, "CIS", 1 }, >+ { 0x70, "RS1" }, >+ { 0x71, "RS2" }, >+ { 0x72, "RS3" }, >+ { 0x73, "RS4" }, >+ { 0x74, "RS5" }, >+ { 0x75, "RS6" }, >+ { 0x76, "RS7" }, >+ { 0x77, "RS8" }, >+ { 0x78, "RS9" }, >+ { 0x79, "RS10" }, >+ { 0x7a, "RS11" }, >+ { 0x7b, "RS12" }, >+ { 0x7c, "RS13" }, >+ { 0x7d, "RS14" }, >+ { 0x7e, "RS15" }, >+ { 0x7f, "RS16" }, >+}; >+ >+static char *tobin(int x) >+{ >+ static char s[9] = ""; >+ int y,z=0; >+ for (y=7;y>=0;y--) { >+ if (x & (1 << y)) >+ s[z++] = '1'; >+ else >+ s[z++] = '0'; >+ } >+ s[z] = '\0'; >+ return s; >+} >+ >+static char *tobin32(unsigned int x) >+{ >+ static char s[33] = ""; >+ int y,z=0; >+ for (y=31;y>=0;y--) { >+ if (x & (1 << y)) >+ s[z++] = '1'; >+ else >+ s[z++] = '0'; >+ } >+ s[z] = '\0'; >+ return s; >+} >+ >+int main(int argc, char *argv[]) >+{ >+ int fd; >+ int x; >+ char fn[256]; >+ struct t4_regs regs; >+ if ((argc < 2) || ((*(argv[1]) != '/') && !atoi(argv[1]))) { >+ fprintf(stderr, "Usage: opvxd115-diag <channel>\n"); >+ exit(1); >+ } >+ if (*(argv[1]) == '/') >+ dahdi_copy_string(fn, argv[1], sizeof(fn)); >+ else >+ snprintf(fn, sizeof(fn), "/dev/dahdi/%d", atoi(argv[1])); >+ fd = open(fn, O_RDWR); >+ if (fd <0) { >+ fprintf(stderr, "Unable to open '%s': %s\n", fn, strerror(errno)); >+ exit(1); >+ } >+ if (ioctl(fd, WCT4_GET_REGS, ®s)) { >+ fprintf(stderr, "Unable to get registers: %s\n", strerror(errno)); >+ exit(1); >+ } >+ printf("PCI Registers:\n"); >+ for (x=0;x<sizeof(xreginfo) / sizeof(xreginfo[0]);x++) { >+ fprintf(stdout, "%s (%02x): %08x (%s)\n", xreginfo[x].name, xreginfo[x].reg, regs.pci[xreginfo[x].reg], tobin32(regs.pci[xreginfo[x].reg])); >+ } >+ printf("\nE1 Control Registers:\n"); >+ for (x=0;x<sizeof(reginfo) / sizeof(reginfo[0]);x++) { >+ fprintf(stdout, "%s (%02x): %02x (%s)\n", reginfo[x].name, reginfo[x].reg, regs.regs[reginfo[x].reg], tobin(regs.regs[reginfo[x].reg])); >+ } >+ printf("\nE1 Status Registers:\n"); >+ for (x=0;x<sizeof(sreginfo) / sizeof(sreginfo[0]);x++) { >+ fprintf(stdout, "%s (%02x): %02x (%s)\n", sreginfo[x].name, sreginfo[x].reg, regs.regs[sreginfo[x].reg], tobin(regs.regs[sreginfo[x].reg])); >+ } >+ printf("\nT1 Control Registers:\n"); >+ for (x=0;x<sizeof(t1_reginfo) / sizeof(t1_reginfo[0]);x++) { >+ fprintf(stdout, "%s (%02x): %02x (%s)\n", t1_reginfo[x].name, t1_reginfo[x].reg, regs.regs[t1_reginfo[x].reg], tobin(regs.regs[t1_reginfo[x].reg])); >+ } >+ printf("\nT1 Status Registers:\n"); >+ for (x=0;x<sizeof(t1_sreginfo) / sizeof(t1_sreginfo[0]);x++) { >+ fprintf(stdout, "%s (%02x): %02x (%s)\n", t1_sreginfo[x].name, t1_sreginfo[x].reg, regs.regs[t1_sreginfo[x].reg], tobin(regs.regs[t1_sreginfo[x].reg])); >+ } >+ exit(0); >+} >diff --git a/drivers/dahdi/opvxd115/opvxd115.h b/drivers/dahdi/opvxd115/opvxd115.h >new file mode 100644 >index 0000000..d89d5b3 >--- /dev/null >+++ b/drivers/dahdi/opvxd115/opvxd115.h >@@ -0,0 +1,124 @@ >+/* >+ * Wildcard T400P FXS Interface Driver for DAHDI Telephony interface >+ * >+ * Written by Mark Spencer <markster@linux-support.net> >+ * >+ * Copyright (C) 2001-2008, Digium, Inc. >+ * >+ * All rights reserved. >+ * >+ */ >+ >+/* >+ * See http://www.asterisk.org for more information about >+ * the Asterisk project. Please do not directly contact >+ * any of the maintainers of this project for assistance; >+ * the project provides a web site, mailing lists and IRC >+ * channels for your use. >+ * >+ * This program is free software, distributed under the terms of >+ * the GNU General Public License Version 2 as published by the >+ * Free Software Foundation. See the LICENSE file included with >+ * this program for more details. >+ */ >+ >+#include <linux/ioctl.h> >+ >+#define FRMR_TTR_BASE 0x10 >+#define FRMR_RTR_BASE 0x0c >+#define FRMR_TSEO 0xa0 >+#define FRMR_TSBS1 0xa1 >+#define FRMR_CCR1 0x09 >+#define FRMR_CCR1_ITF 0x08 >+#define FRMR_CCR1_EITS 0x10 >+#define FRMR_CCR2 0x0a >+#define FRMR_CCR2_RCRC 0x04 >+#define FRMR_CCR2_RADD 0x10 >+#define FRMR_MODE 0x03 >+#define FRMR_MODE_NO_ADDR_CMP 0x80 >+#define FRMR_MODE_SS7 0x20 >+#define FRMR_MODE_HRAC 0x08 >+#define FRMR_IMR0 0x14 >+#define FRMR_IMR0_RME 0x80 >+#define FRMR_IMR0_RPF 0x01 >+#define FRMR_IMR1 0x15 >+#define FRMR_IMR1_ALLS 0x20 >+#define FRMR_IMR1_XDU 0x10 >+#define FRMR_IMR1_XPR 0x01 >+#define FRMR_XC0 0x22 >+#define FRMR_XC1 0x23 >+#define FRMR_RC0 0x24 >+#define FRMR_RC1 0x25 >+#define FRMR_SIC1 0x3e >+#define FRMR_SIC2 0x3f >+#define FRMR_SIC3 0x40 >+#define FRMR_CMR1 0x44 >+#define FRMR_CMR2 0x45 >+#define FRMR_GCR 0x46 >+#define FRMR_ISR0 0x68 >+#define FRMR_ISR0_RME 0x80 >+#define FRMR_ISR0_RPF 0x01 >+#define FRMR_ISR1 0x69 >+#define FRMR_ISR1_ALLS 0x20 >+#define FRMR_ISR1_XDU 0x10 >+#define FRMR_ISR1_XPR 0x01 >+#define FRMR_ISR2 0x6a >+#define FRMR_ISR3 0x6b >+#define FRMR_ISR4 0x6c >+#define FRMR_GIS 0x6e >+#define FRMR_GIS_ISR0 0x01 >+#define FRMR_GIS_ISR1 0x02 >+#define FRMR_GIS_ISR2 0x04 >+#define FRMR_GIS_ISR3 0x08 >+#define FRMR_GIS_ISR4 0x10 >+#define FRMR_CIS 0x6f >+#define FRMR_CIS_GIS1 0x01 >+#define FRMR_CIS_GIS2 0x02 >+#define FRMR_CIS_GIS3 0x04 >+#define FRMR_CIS_GIS4 0x08 >+#define FRMR_CMDR 0x02 >+#define FRMR_CMDR_SRES 0x01 >+#define FRMR_CMDR_XRES 0x10 >+#define FRMR_CMDR_RMC 0x80 >+#define FRMR_CMDR_XTF 0x04 >+#define FRMR_CMDR_XHF 0x08 >+#define FRMR_CMDR_XME 0x02 >+#define FRMR_RSIS 0x65 >+#define FRMR_RSIS_VFR 0x80 >+#define FRMR_RSIS_RDO 0x40 >+#define FRMR_RSIS_CRC16 0x20 >+#define FRMR_RSIS_RAB 0x10 >+#define FRMR_RBCL 0x66 >+#define FRMR_RBCL_MAX_SIZE 0x1f >+#define FRMR_RBCH 0x67 >+#define FRMR_RXFIFO 0x00 >+#define FRMR_SIS 0x64 >+#define FRMR_SIS_XFW 0x40 >+#define FRMR_TXFIFO 0x00 >+ >+#define FRS0 0x4c >+#define FRS0_LOS (1<<7) >+#define FRS0_LFA (1<<5) >+#define FRS0_LMFA (1<<1) >+ >+#define FRS1 0x4d >+#define FRS1_XLS (1<<1) >+#define FRS1_XLO (1<<0) >+ >+#define NUM_REGS 0xa9 >+#define NUM_PCI 12 >+ >+struct t4_regs { >+ unsigned int pci[NUM_PCI]; >+ unsigned char regs[NUM_REGS]; >+}; >+ >+#define T4_CHECK_VPM 0 >+#define T4_LOADING_FW 1 >+#define T4_STOP_DMA 2 >+#define T4_CHECK_TIMING 3 >+#define T4_CHANGE_LATENCY 4 >+#define T4_IGNORE_LATENCY 5 >+ >+#define WCT4_GET_REGS _IOW (DAHDI_CODE, 60, struct t4_regs) >+ >diff --git a/drivers/dahdi/opvxd115/vpm450m.c b/drivers/dahdi/opvxd115/vpm450m.c >new file mode 100644 >index 0000000..f1ed421 >--- /dev/null >+++ b/drivers/dahdi/opvxd115/vpm450m.c >@@ -0,0 +1,587 @@ >+/* >+ * Copyright (C) 2005-2006 Digium, Inc. >+ * >+ * Mark Spencer <markster@digium.com> >+ * Modified by mark.liu@openvox.cn 06/16/2009 >+ >+ * All Rights Reserved >+ */ >+ >+/* >+ * See http://www.asterisk.org for more information about >+ * the Asterisk project. Please do not directly contact >+ * any of the maintainers of this project for assistance; >+ * the project provides a web site, mailing lists and IRC >+ * channels for your use. >+ * >+ * This program is free software, distributed under the terms of >+ * the GNU General Public License Version 2 as published by the >+ * Free Software Foundation. See the LICENSE file included with >+ * this program for more details. >+ */ >+ >+#include <linux/slab.h> >+#include <linux/vmalloc.h> >+#include <linux/string.h> >+#include <linux/time.h> >+#include <linux/version.h> >+ >+#include "vpm450m.h" >+#include "oct6100api/oct6100_api.h" >+ >+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) >+#include <linux/config.h> >+#endif >+ >+/* API for Octasic access */ >+UINT32 Oct6100UserGetTime(tPOCT6100_GET_TIME f_pTime) >+{ >+ /* Why couldn't they just take a timeval like everyone else? */ >+ struct timeval tv; >+ unsigned long long total_usecs; >+ unsigned int mask = ~0; >+ >+ do_gettimeofday(&tv); >+ total_usecs = (((unsigned long long)(tv.tv_sec)) * 1000000) + >+ (((unsigned long long)(tv.tv_usec))); >+ f_pTime->aulWallTimeUs[0] = (total_usecs & mask); >+ f_pTime->aulWallTimeUs[1] = (total_usecs >> 32); >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserMemSet(PVOID f_pAddress, UINT32 f_ulPattern, UINT32 f_ulLength) >+{ >+ memset(f_pAddress, f_ulPattern, f_ulLength); >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserMemCopy(PVOID f_pDestination, const void *f_pSource, UINT32 f_ulLength) >+{ >+ memcpy(f_pDestination, f_pSource, f_ulLength); >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserCreateSerializeObject(tPOCT6100_CREATE_SERIALIZE_OBJECT f_pCreate) >+{ >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserDestroySerializeObject(tPOCT6100_DESTROY_SERIALIZE_OBJECT f_pDestroy) >+{ >+#ifdef OCTASIC_DEBUG >+ printk(KERN_DEBUG "I should never be called! (destroy serialize object)\n"); >+#endif >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserSeizeSerializeObject(tPOCT6100_SEIZE_SERIALIZE_OBJECT f_pSeize) >+{ >+ /* Not needed */ >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserReleaseSerializeObject(tPOCT6100_RELEASE_SERIALIZE_OBJECT f_pRelease) >+{ >+ /* Not needed */ >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserDriverWriteApi(tPOCT6100_WRITE_PARAMS f_pWriteParams) >+{ >+ oct_set_reg(f_pWriteParams->pProcessContext, f_pWriteParams->ulWriteAddress, f_pWriteParams->usWriteData); >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserDriverWriteSmearApi(tPOCT6100_WRITE_SMEAR_PARAMS f_pSmearParams) >+{ >+ unsigned int x; >+ for (x=0;x<f_pSmearParams->ulWriteLength;x++) { >+ oct_set_reg(f_pSmearParams->pProcessContext, f_pSmearParams->ulWriteAddress + (x << 1), f_pSmearParams->usWriteData); >+ } >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserDriverWriteBurstApi(tPOCT6100_WRITE_BURST_PARAMS f_pBurstParams) >+{ >+ unsigned int x; >+ for (x=0;x<f_pBurstParams->ulWriteLength;x++) { >+ oct_set_reg(f_pBurstParams->pProcessContext, f_pBurstParams->ulWriteAddress + (x << 1), f_pBurstParams->pusWriteData[x]); >+ } >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserDriverReadApi(tPOCT6100_READ_PARAMS f_pReadParams) >+{ >+ *(f_pReadParams->pusReadData) = oct_get_reg(f_pReadParams->pProcessContext, f_pReadParams->ulReadAddress); >+ return cOCT6100_ERR_OK; >+} >+ >+UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams) >+{ >+ unsigned int x; >+ for (x=0;x<f_pBurstParams->ulReadLength;x++) { >+ f_pBurstParams->pusReadData[x] = oct_get_reg(f_pBurstParams->pProcessContext, f_pBurstParams->ulReadAddress + (x << 1)); >+ } >+ return cOCT6100_ERR_OK; >+} >+ >+#define SOUT_G168_1100GB_ON 0x40000004 >+#define SOUT_DTMF_1 0x40000011 >+#define SOUT_DTMF_2 0x40000012 >+#define SOUT_DTMF_3 0x40000013 >+#define SOUT_DTMF_A 0x4000001A >+#define SOUT_DTMF_4 0x40000014 >+#define SOUT_DTMF_5 0x40000015 >+#define SOUT_DTMF_6 0x40000016 >+#define SOUT_DTMF_B 0x4000001B >+#define SOUT_DTMF_7 0x40000017 >+#define SOUT_DTMF_8 0x40000018 >+#define SOUT_DTMF_9 0x40000019 >+#define SOUT_DTMF_C 0x4000001C >+#define SOUT_DTMF_STAR 0x4000001E >+#define SOUT_DTMF_0 0x40000010 >+#define SOUT_DTMF_POUND 0x4000001F >+#define SOUT_DTMF_D 0x4000001D >+ >+#define ROUT_G168_2100GB_ON 0x10000000 >+#define ROUT_G168_2100GB_WSPR 0x10000002 >+#define ROUT_SOUT_G168_2100HB_END 0x50000003 >+#define ROUT_G168_1100GB_ON 0x10000004 >+ >+#define ROUT_DTMF_1 0x10000011 >+#define ROUT_DTMF_2 0x10000012 >+#define ROUT_DTMF_3 0x10000013 >+#define ROUT_DTMF_A 0x1000001A >+#define ROUT_DTMF_4 0x10000014 >+#define ROUT_DTMF_5 0x10000015 >+#define ROUT_DTMF_6 0x10000016 >+#define ROUT_DTMF_B 0x1000001B >+#define ROUT_DTMF_7 0x10000017 >+#define ROUT_DTMF_8 0x10000018 >+#define ROUT_DTMF_9 0x10000019 >+#define ROUT_DTMF_C 0x1000001C >+#define ROUT_DTMF_STAR 0x1000001E >+#define ROUT_DTMF_0 0x10000010 >+#define ROUT_DTMF_POUND 0x1000001F >+#define ROUT_DTMF_D 0x1000001D >+ >+#if 0 >+#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_HT_FREEZE >+#else >+#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_POWER_DOWN >+#endif >+ >+struct vpm450m { >+ tPOCT6100_INSTANCE_API pApiInstance; >+ UINT32 aulEchoChanHndl[ 128 ]; >+ int chanflags[128]; >+ int ecmode[128]; >+ int numchans; >+}; >+ >+#define FLAG_DTMF (1 << 0) >+#define FLAG_MUTE (1 << 1) >+#define FLAG_ECHO (1 << 2) >+ >+static unsigned int tones[] = { >+ SOUT_DTMF_1, >+ SOUT_DTMF_2, >+ SOUT_DTMF_3, >+ SOUT_DTMF_A, >+ SOUT_DTMF_4, >+ SOUT_DTMF_5, >+ SOUT_DTMF_6, >+ SOUT_DTMF_B, >+ SOUT_DTMF_7, >+ SOUT_DTMF_8, >+ SOUT_DTMF_9, >+ SOUT_DTMF_C, >+ SOUT_DTMF_STAR, >+ SOUT_DTMF_0, >+ SOUT_DTMF_POUND, >+ SOUT_DTMF_D, >+ SOUT_G168_1100GB_ON, >+ >+ ROUT_DTMF_1, >+ ROUT_DTMF_2, >+ ROUT_DTMF_3, >+ ROUT_DTMF_A, >+ ROUT_DTMF_4, >+ ROUT_DTMF_5, >+ ROUT_DTMF_6, >+ ROUT_DTMF_B, >+ ROUT_DTMF_7, >+ ROUT_DTMF_8, >+ ROUT_DTMF_9, >+ ROUT_DTMF_C, >+ ROUT_DTMF_STAR, >+ ROUT_DTMF_0, >+ ROUT_DTMF_POUND, >+ ROUT_DTMF_D, >+ ROUT_G168_1100GB_ON, >+}; >+ >+static void vpm450m_setecmode(struct vpm450m *vpm450m, int channel, int mode) >+{ >+ tOCT6100_CHANNEL_MODIFY *modify; >+ UINT32 ulResult; >+ >+ if (vpm450m->ecmode[channel] == mode) >+ return; >+ modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC); >+ if (!modify) { >+ printk(KERN_NOTICE "opvxd115: Unable to allocate memory for setec!\n"); >+ return; >+ } >+ Oct6100ChannelModifyDef(modify); >+ modify->ulEchoOperationMode = mode; >+ modify->ulChannelHndl = vpm450m->aulEchoChanHndl[channel]; >+ ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify); >+ if (ulResult != GENERIC_OK) { >+ printk(KERN_NOTICE "Failed to apply echo can changes on channel %d!\n", channel); >+ } else { >+#ifdef OCTASIC_DEBUG >+ printk(KERN_DEBUG "Echo can on channel %d set to %d\n", channel, mode); >+#endif >+ vpm450m->ecmode[channel] = mode; >+ } >+ kfree(modify); >+} >+ >+void vpm450m_setdtmf(struct vpm450m *vpm450m, int channel, int detect, int mute) >+{ >+ tOCT6100_CHANNEL_MODIFY *modify; >+ UINT32 ulResult; >+ >+ modify = kmalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_KERNEL); >+ if (!modify) { >+ printk(KERN_NOTICE "opvxd115: Unable to allocate memory for setdtmf!\n"); >+ return; >+ } >+ Oct6100ChannelModifyDef(modify); >+ modify->ulChannelHndl = vpm450m->aulEchoChanHndl[channel]; >+ if (mute) { >+ vpm450m->chanflags[channel] |= FLAG_MUTE; >+ modify->VqeConfig.fDtmfToneRemoval = TRUE; >+ } else { >+ vpm450m->chanflags[channel] &= ~FLAG_MUTE; >+ modify->VqeConfig.fDtmfToneRemoval = FALSE; >+ } >+ if (detect) >+ vpm450m->chanflags[channel] |= FLAG_DTMF; >+ else >+ vpm450m->chanflags[channel] &= ~FLAG_DTMF; >+ if (vpm450m->chanflags[channel] & (FLAG_DTMF|FLAG_MUTE)) { >+ if (!(vpm450m->chanflags[channel] & FLAG_ECHO)) { >+ vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); >+ vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE); >+ } >+ } else { >+ if (!(vpm450m->chanflags[channel] & FLAG_ECHO)) >+ vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_DIGITAL); >+ } >+ >+ ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify); >+ if (ulResult != GENERIC_OK) { >+ printk(KERN_NOTICE "Failed to apply dtmf mute changes on channel %d!\n", channel); >+ } >+/* printk(KERN_DEBUG "VPM450m: Setting DTMF on channel %d: %s / %s\n", channel, (detect ? "DETECT" : "NO DETECT"), (mute ? "MUTE" : "NO MUTE")); */ >+ kfree(modify); >+} >+ >+void vpm450m_setec(struct vpm450m *vpm450m, int channel, int eclen) >+{ >+ if (eclen) { >+ vpm450m->chanflags[channel] |= FLAG_ECHO; >+ vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); >+ vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_NORMAL); >+ } else { >+ vpm450m->chanflags[channel] &= ~FLAG_ECHO; >+ if (vpm450m->chanflags[channel] & (FLAG_DTMF | FLAG_MUTE)) { >+ vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); >+ vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE); >+ } else >+ vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_DIGITAL); >+ } >+/* printk(KERN_DEBUG "VPM450m: Setting EC on channel %d to %d\n", channel, eclen); */ >+} >+ >+int vpm450m_checkirq(struct vpm450m *vpm450m) >+{ >+ tOCT6100_INTERRUPT_FLAGS InterruptFlags; >+ >+ Oct6100InterruptServiceRoutineDef(&InterruptFlags); >+ Oct6100InterruptServiceRoutine(vpm450m->pApiInstance, &InterruptFlags); >+ >+ return InterruptFlags.fToneEventsPending ? 1 : 0; >+} >+ >+int vpm450m_getdtmf(struct vpm450m *vpm450m, int *channel, int *tone, int *start) >+{ >+ tOCT6100_TONE_EVENT tonefound; >+ tOCT6100_EVENT_GET_TONE tonesearch; >+ UINT32 ulResult; >+ >+ Oct6100EventGetToneDef(&tonesearch); >+ tonesearch.pToneEvent = &tonefound; >+ tonesearch.ulMaxToneEvent = 1; >+ ulResult = Oct6100EventGetTone(vpm450m->pApiInstance, &tonesearch); >+ if (tonesearch.ulNumValidToneEvent) { >+ if (channel) >+ *channel = tonefound.ulUserChanId; >+ if (tone) { >+ switch(tonefound.ulToneDetected) { >+ case SOUT_DTMF_1: >+ *tone = '1'; >+ break; >+ case SOUT_DTMF_2: >+ *tone = '2'; >+ break; >+ case SOUT_DTMF_3: >+ *tone = '3'; >+ break; >+ case SOUT_DTMF_A: >+ *tone = 'A'; >+ break; >+ case SOUT_DTMF_4: >+ *tone = '4'; >+ break; >+ case SOUT_DTMF_5: >+ *tone = '5'; >+ break; >+ case SOUT_DTMF_6: >+ *tone = '6'; >+ break; >+ case SOUT_DTMF_B: >+ *tone = 'B'; >+ break; >+ case SOUT_DTMF_7: >+ *tone = '7'; >+ break; >+ case SOUT_DTMF_8: >+ *tone = '8'; >+ break; >+ case SOUT_DTMF_9: >+ *tone = '9'; >+ break; >+ case SOUT_DTMF_C: >+ *tone = 'C'; >+ break; >+ case SOUT_DTMF_STAR: >+ *tone = '*'; >+ break; >+ case SOUT_DTMF_0: >+ *tone = '0'; >+ break; >+ case SOUT_DTMF_POUND: >+ *tone = '#'; >+ break; >+ case SOUT_DTMF_D: >+ *tone = 'D'; >+ break; >+ case SOUT_G168_1100GB_ON: >+ *tone = 'f'; >+ break; >+ default: >+#ifdef OCTASIC_DEBUG >+ printk(KERN_DEBUG "Unknown tone value %08x\n", tonefound.ulToneDetected); >+#endif >+ *tone = 'u'; >+ break; >+ } >+ } >+ if (start) >+ *start = (tonefound.ulEventType == cOCT6100_TONE_PRESENT); >+ return 1; >+ } >+ return 0; >+} >+ >+unsigned int get_vpm450m_capacity(void *wc) >+{ >+ UINT32 ulResult; >+ >+ tOCT6100_API_GET_CAPACITY_PINS CapacityPins; >+ >+ Oct6100ApiGetCapacityPinsDef(&CapacityPins); >+ CapacityPins.pProcessContext = wc; >+ CapacityPins.ulMemoryType = cOCT6100_MEM_TYPE_DDR; >+ CapacityPins.fEnableMemClkOut = TRUE; >+ CapacityPins.ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; >+ >+ ulResult = Oct6100ApiGetCapacityPins(&CapacityPins); >+ if (ulResult != cOCT6100_ERR_OK) { >+ printk(KERN_DEBUG "Failed to get chip capacity, code %08x!\n", ulResult); >+ return 0; >+ } >+ >+ return CapacityPins.ulCapacityValue; >+} >+ >+struct vpm450m *init_vpm450m(void *wc, int *isalaw, int numspans, const struct firmware *firmware) >+{ >+ tOCT6100_CHIP_OPEN *ChipOpen; >+ tOCT6100_GET_INSTANCE_SIZE InstanceSize; >+ tOCT6100_CHANNEL_OPEN *ChannelOpen; >+ UINT32 ulResult; >+ struct vpm450m *vpm450m; >+ int x,y,law; >+#ifdef CONFIG_4KSTACKS >+ unsigned long flags; >+#endif >+ >+ if (!(vpm450m = kmalloc(sizeof(struct vpm450m), GFP_KERNEL))) >+ return NULL; >+ >+ memset(vpm450m, 0, sizeof(struct vpm450m)); >+ >+ if (!(ChipOpen = kmalloc(sizeof(tOCT6100_CHIP_OPEN), GFP_KERNEL))) { >+ kfree(vpm450m); >+ return NULL; >+ } >+ >+ memset(ChipOpen, 0, sizeof(tOCT6100_CHIP_OPEN)); >+ >+ if (!(ChannelOpen = kmalloc(sizeof(tOCT6100_CHANNEL_OPEN), GFP_KERNEL))) { >+ kfree(vpm450m); >+ kfree(ChipOpen); >+ return NULL; >+ } >+ >+ memset(ChannelOpen, 0, sizeof(tOCT6100_CHANNEL_OPEN)); >+ >+ for (x=0;x<128;x++) >+ vpm450m->ecmode[x] = -1; >+ >+ vpm450m->numchans = numspans * 32; >+ printk(KERN_INFO "VPM450: echo cancellation for %d channels\n", vpm450m->numchans); >+ >+ Oct6100ChipOpenDef(ChipOpen); >+ >+ /* Setup Chip Open Parameters */ >+ ChipOpen->ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ; >+ Oct6100GetInstanceSizeDef(&InstanceSize); >+ >+ ChipOpen->pProcessContext = wc; >+ >+ ChipOpen->pbyImageFile = firmware->data; >+ ChipOpen->ulImageSize = firmware->size; >+ ChipOpen->fEnableMemClkOut = TRUE; >+ ChipOpen->ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; >+ ChipOpen->ulMaxChannels = vpm450m->numchans; >+ ChipOpen->ulMemoryType = cOCT6100_MEM_TYPE_DDR; >+ ChipOpen->ulMemoryChipSize = cOCT6100_MEMORY_CHIP_SIZE_32MB; >+ ChipOpen->ulNumMemoryChips = 1; >+ ChipOpen->ulMaxTdmStreams = 4; >+ ChipOpen->aulTdmStreamFreqs[0] = cOCT6100_TDM_STREAM_FREQ_8MHZ; >+ ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_FALLING_EDGE; >+#if 0 >+ ChipOpen->fEnableAcousticEcho = TRUE; >+#endif >+ >+ ulResult = Oct6100GetInstanceSize(ChipOpen, &InstanceSize); >+ if (ulResult != cOCT6100_ERR_OK) { >+ printk(KERN_NOTICE "Failed to get instance size, code %08x!\n", ulResult); >+ kfree(vpm450m); >+ kfree(ChipOpen); >+ kfree(ChannelOpen); >+ return NULL; >+ } >+ >+ >+ vpm450m->pApiInstance = vmalloc(InstanceSize.ulApiInstanceSize); >+ if (!vpm450m->pApiInstance) { >+ printk(KERN_NOTICE "Out of memory (can't allocate %d bytes)!\n", InstanceSize.ulApiInstanceSize); >+ kfree(vpm450m); >+ kfree(ChipOpen); >+ kfree(ChannelOpen); >+ return NULL; >+ } >+ >+ /* I don't know what to curse more in this comment, the problems caused by >+ * the 4K kernel stack limit change or the octasic API for being so darn >+ * stack unfriendly. Stupid, stupid, stupid. So we disable IRQs so we >+ * don't run the risk of overflowing the stack while we initialize the >+ * octasic. */ >+#ifdef CONFIG_4KSTACKS >+ local_irq_save(flags); >+#endif >+ ulResult = Oct6100ChipOpen(vpm450m->pApiInstance, ChipOpen); >+ if (ulResult != cOCT6100_ERR_OK) { >+ printk(KERN_NOTICE "Failed to open chip, code %08x!\n", ulResult); >+#ifdef CONFIG_4KSTACKS >+ local_irq_restore(flags); >+#endif >+ kfree(vpm450m); >+ kfree(ChipOpen); >+ kfree(ChannelOpen); >+ return NULL; >+ } >+ for (x=0;x<128;x++) { >+ if ((x & 0x03) < numspans) { >+ /* span timeslots are interleaved 12341234... >+ * therefore, the lower 2 bits tell us which span this >+ * timeslot/channel >+ */ >+ if (isalaw[x & 0x03]) >+ law = cOCT6100_PCM_A_LAW; >+ else >+ law = cOCT6100_PCM_U_LAW; >+ Oct6100ChannelOpenDef(ChannelOpen); >+ ChannelOpen->pulChannelHndl = &vpm450m->aulEchoChanHndl[x]; >+ ChannelOpen->ulUserChanId = x; >+ ChannelOpen->TdmConfig.ulRinPcmLaw = law; >+ ChannelOpen->TdmConfig.ulRinStream = 0; >+ ChannelOpen->TdmConfig.ulRinTimeslot = x; >+ ChannelOpen->TdmConfig.ulSinPcmLaw = law; >+ ChannelOpen->TdmConfig.ulSinStream = 1; >+ ChannelOpen->TdmConfig.ulSinTimeslot = x; >+ ChannelOpen->TdmConfig.ulSoutPcmLaw = law; >+ ChannelOpen->TdmConfig.ulSoutStream = 2; >+ ChannelOpen->TdmConfig.ulSoutTimeslot = x; >+ ChannelOpen->TdmConfig.ulRoutPcmLaw = law; >+ ChannelOpen->TdmConfig.ulRoutStream = 3; >+ ChannelOpen->TdmConfig.ulRoutTimeslot = x; >+ ChannelOpen->VqeConfig.fEnableNlp = TRUE; >+ ChannelOpen->VqeConfig.fRinDcOffsetRemoval = TRUE; >+ ChannelOpen->VqeConfig.fSinDcOffsetRemoval = TRUE; >+ >+ ChannelOpen->fEnableToneDisabler = TRUE; >+ ChannelOpen->ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_DIGITAL; >+ >+ ulResult = Oct6100ChannelOpen(vpm450m->pApiInstance, ChannelOpen); >+ if (ulResult != GENERIC_OK) { >+ printk(KERN_NOTICE "Failed to open channel %d!\n", x); >+ } >+ for (y=0;y<sizeof(tones) / sizeof(tones[0]); y++) { >+ tOCT6100_TONE_DETECTION_ENABLE enable; >+ Oct6100ToneDetectionEnableDef(&enable); >+ enable.ulChannelHndl = vpm450m->aulEchoChanHndl[x]; >+ enable.ulToneNumber = tones[y]; >+ if (Oct6100ToneDetectionEnable(vpm450m->pApiInstance, &enable) != GENERIC_OK) >+ printk(KERN_NOTICE "Failed to enable tone detection on channel %d for tone %d!\n", x, y); >+ } >+ } >+ } >+ >+#ifdef CONFIG_4KSTACKS >+ local_irq_restore(flags); >+#endif >+ kfree(ChipOpen); >+ kfree(ChannelOpen); >+ return vpm450m; >+} >+ >+void release_vpm450m(struct vpm450m *vpm450m) >+{ >+ UINT32 ulResult; >+ tOCT6100_CHIP_CLOSE ChipClose; >+ >+ Oct6100ChipCloseDef(&ChipClose); >+ ulResult = Oct6100ChipClose(vpm450m->pApiInstance, &ChipClose); >+ if (ulResult != cOCT6100_ERR_OK) { >+ printk(KERN_NOTICE "Failed to close chip, code %08x!\n", ulResult); >+ } >+ vfree(vpm450m->pApiInstance); >+ kfree(vpm450m); >+} >diff --git a/drivers/dahdi/opvxd115/vpm450m.h b/drivers/dahdi/opvxd115/vpm450m.h >new file mode 100644 >index 0000000..735a2cc >--- /dev/null >+++ b/drivers/dahdi/opvxd115/vpm450m.h >@@ -0,0 +1,43 @@ >+/* >+ * Copyright (C) 2005-2006 Digium, Inc. >+ * >+ * Mark Spencer <markster@digium.com> >+ * >+ * All Rights Reserved >+ * >+ */ >+ >+/* >+ * See http://www.asterisk.org for more information about >+ * the Asterisk project. Please do not directly contact >+ * any of the maintainers of this project for assistance; >+ * the project provides a web site, mailing lists and IRC >+ * channels for your use. >+ * >+ * This program is free software, distributed under the terms of >+ * the GNU General Public License Version 2 as published by the >+ * Free Software Foundation. See the LICENSE file included with >+ * this program for more details. >+ */ >+ >+#ifndef _VPM450M_H >+#define _VPM450M_H >+ >+#include <linux/firmware.h> >+ >+struct vpm450m; >+ >+/* From driver */ >+unsigned int oct_get_reg(void *data, unsigned int reg); >+void oct_set_reg(void *data, unsigned int reg, unsigned int val); >+ >+/* From vpm450m */ >+struct vpm450m *init_vpm450m(void *wc, int *isalaw, int numspans, const struct firmware *firmware); >+unsigned int get_vpm450m_capacity(void *wc); >+void vpm450m_setec(struct vpm450m *instance, int channel, int eclen); >+void vpm450m_setdtmf(struct vpm450m *instance, int channel, int dtmfdetect, int dtmfmute); >+int vpm450m_checkirq(struct vpm450m *vpm450m); >+int vpm450m_getdtmf(struct vpm450m *vpm450m, int *channel, int *tone, int *start); >+void release_vpm450m(struct vpm450m *instance); >+ >+#endif >diff --git a/drivers/dahdi/wcopenpci.c b/drivers/dahdi/wcopenpci.c >new file mode 100644 >index 0000000..ef4d1f3 >--- /dev/null >+++ b/drivers/dahdi/wcopenpci.c >@@ -0,0 +1,1849 @@ >+/* >+ * Voicetronix OpenPCI Interface Driver for Zapata Telephony interface >+ * >+ * Written by Mark Spencer <markster@linux-support.net> >+ * Matthew Fredrickson <creslin@linux-support.net> >+ * Ben Kramer <ben@voicetronix.com.au> >+ * Ron Lee <ron@voicetronix.com.au> >+ * >+ * Copyright (C) 2001, Linux Support Services, Inc. >+ * Copyright (C) 2005 - 2007, Voicetronix >+ * >+ * All rights reserved. >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation; either version 2 of the License, or >+ * (at your option) any later version. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. >+ * >+ */ >+ >+/* Conditional debug options */ >+#define VERBOSE_TIMING 0 >+ >+/* Driver constants */ >+#define DRIVER_DESCRIPTION "Voicetronix OpenPCI DAHDI driver" >+#define DRIVER_AUTHOR "Mark Spencer <markster@digium.com> "\ >+ "Voicetronix <support@voicetronix.com.au>" >+ >+#define NAME "wcopenpci" >+#define MAX_PORTS 8 /* Maximum number of ports on each carrier */ >+#define MAX_CARDS 8 /* Maximum number of carriers per host */ >+ >+#define DEFAULT_COUNTRY "AUSTRALIA" >+ >+ >+#include <linux/init.h> >+#include <linux/module.h> >+#include <linux/pci.h> >+#include <linux/delay.h> >+#include <linux/sched.h> >+ >+#include <dahdi/kernel.h> >+#include <dahdi/version.h> >+#include "proslic.h" >+#include <dahdi/wctdm_user.h> >+ >+ >+ >+/* Compatibility helpers */ >+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) >+ #include <linux/interrupt.h> >+#else >+ typedef void irqreturn_t; >+ #define IRQ_NONE >+ #define IRQ_HANDLED >+ #define IRQ_RETVAL(x) >+ #define __devexit_p(x) x >+#endif >+ >+// Centos4.3 uses a modified 2.6.9 kernel, with no indication that >+// it is different from the mainstream (or even Centos4.2 2.6.9) >+// kernel, so we must crowbar off the dunce-hat manually here. >+#if !defined CENTOS4_3 && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14) >+ typedef int gfp_t; >+ static inline void *kzalloc( size_t n, gfp_t flags ){ >+ void *p = kmalloc(n,flags); >+ if (p) memset(p, 0, n); >+ return p; >+ } >+#endif >+ >+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) >+ #define DEFINE_MUTEX(x) DECLARE_MUTEX(x) >+ #define mutex_init(x) init_MUTEX(x) >+ #define mutex_lock(x) down(x) >+ #define mutex_lock_interruptible(x) down_interruptible(x) >+ #define mutex_trylock(x) down_trylock(x) >+ #define mutex_unlock(x) up(x) >+#else >+ #include <linux/mutex.h> >+#endif >+ >+ >+static struct fxo_mode { >+ char *name; >+ int ohs; >+ int ohs2; >+ int rz; >+ int rt; >+ int ilim; >+ int dcv; >+ int mini; >+ int acim; >+ int ring_osc; >+ int ring_x; >+} fxo_modes[] = >+{ >+ { "FCC", 0, 0, 0, 1, 0, 0x3, 0, 0, }, /* US, Canada */ >+ { "TBR21", 0, 0, 0, 0, 1, 0x3, 0, 0x2, 0x7e6c, 0x023a, }, >+ /* Austria, Belgium, Denmark, Finland, France, Germany, >+ Greece, Iceland, Ireland, Italy, Luxembourg, Netherlands, >+ Norway, Portugal, Spain, Sweden, Switzerland, and UK */ >+ { "ARGENTINA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "AUSTRALIA", 1, 0, 0, 0, 0, 0, 0x3, 0x3, }, >+ { "AUSTRIA", 0, 1, 0, 0, 1, 0x3, 0, 0x3, }, >+ { "BAHRAIN", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "BELGIUM", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "BRAZIL", 0, 0, 0, 0, 0, 0, 0x3, 0, }, >+ { "BULGARIA", 0, 0, 0, 0, 1, 0x3, 0x0, 0x3, }, >+ { "CANADA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "CHILE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "CHINA", 0, 0, 0, 0, 0, 0, 0x3, 0xf, }, >+ { "COLUMBIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "CROATIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "CYRPUS", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "CZECH", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "DENMARK", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "ECUADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "EGYPT", 0, 0, 0, 0, 0, 0, 0x3, 0, }, >+ { "ELSALVADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "FINLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "FRANCE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "GERMANY", 0, 1, 0, 0, 1, 0x3, 0, 0x3, }, >+ { "GREECE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "GUAM", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "HONGKONG", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "HUNGARY", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "ICELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "INDIA", 0, 0, 0, 0, 0, 0x3, 0, 0x4, }, >+ { "INDONESIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "IRELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "ISRAEL", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "ITALY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "JAPAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, >+ { "JORDAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, >+ { "KAZAKHSTAN", 0, 0, 0, 0, 0, 0x3, 0, }, >+ { "KUWAIT", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "LATVIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "LEBANON", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "LUXEMBOURG", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "MACAO", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "MALAYSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, /* Current loop >= 20ma */ >+ { "MALTA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "MEXICO", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "MOROCCO", 0, 0, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "NETHERLANDS", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "NEWZEALAND", 0, 0, 0, 0, 0, 0x3, 0, 0x4, }, >+ { "NIGERIA", 0, 0, 0, 0, 0x1, 0x3, 0, 0x2, }, >+ { "NORWAY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "OMAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, >+ { "PAKISTAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, >+ { "PERU", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "PHILIPPINES", 0, 0, 0, 0, 0, 0, 0x3, 0, }, >+ { "POLAND", 0, 0, 1, 1, 0, 0x3, 0, 0, }, >+ { "PORTUGAL", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "ROMANIA", 0, 0, 0, 0, 0, 3, 0, 0, }, >+ { "RUSSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, >+ { "SAUDIARABIA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "SINGAPORE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "SLOVAKIA", 0, 0, 0, 0, 0, 0x3, 0, 0x3, }, >+ { "SLOVENIA", 0, 0, 0, 0, 0, 0x3, 0, 0x2, }, >+ { "SOUTHAFRICA", 1, 0, 1, 0, 0, 0x3, 0, 0x3, }, >+ { "SOUTHKOREA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "SPAIN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "SWEDEN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "SWITZERLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, }, >+ { "SYRIA", 0, 0, 0, 0, 0, 0, 0x3, 0, }, >+ { "TAIWAN", 0, 0, 0, 0, 0, 0, 0x3, 0, }, >+ { "THAILAND", 0, 0, 0, 0, 0, 0, 0x3, 0, }, >+ { "UAE", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "UK", 0, 1, 0, 0, 1, 0x3, 0, 0x5, }, >+ { "USA", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+ { "YEMEN", 0, 0, 0, 0, 0, 0x3, 0, 0, }, >+}; >+ >+static struct ps_country_reg { >+ const char *country; >+ unsigned short value; >+} ps_country_regs[] = { >+ {"ARGENTINA", 0x8}, >+ {"AUSTRALIA", 0xD}, >+ {"AUSTRIA", 0xD}, >+ {"BAHRAIN", 0xC}, >+ {"BELGIUM", 0xC}, >+ {"BRAZIL", 0x8}, >+ {"BULGARIA", 0xD}, >+ {"CANADA", 0x8}, >+ {"CHILE", 0x8}, >+ {"CHINA", 0xC}, >+ {"COLOMBIA", 0x8}, >+ {"CROATIA", 0xC}, >+ {"CYPRUS", 0xC}, >+ {"CZECH", 0xC}, >+ {"DENMARK", 0xC}, >+ {"ECUADOR", 0x8}, >+ {"EGYPT", 0x8}, >+ {"ELSALVADOR", 0x8}, >+ {"FINLAND", 0xC}, >+ {"FRANCE", 0xC}, >+ {"GERMANY", 0xD}, >+ {"GREECE", 0xC}, >+ {"GUAM", 0x8}, >+ {"HONGKONG", 0x8}, >+ {"HUNGARY", 0x8}, >+ {"ICELAND", 0xC}, >+ {"INDIA", 0xF}, >+ {"INDONESIA", 0x8}, >+ {"IRELAND", 0xC}, >+ {"ISRAEL", 0xC}, >+ {"ITALY", 0xC}, >+ {"JAPAN", 0x8}, >+ {"JORDAN", 0x8}, >+ {"KAZAKHSTAN", 0x8}, >+ {"KUWAIT", 0x8}, >+ {"LATVIA", 0xC}, >+ {"LEBANON", 0xC}, >+ {"LUXEMBOURG", 0xC}, >+ {"MACAO", 0x8}, >+ {"MALAYSIA", 0x8}, >+ {"MALTA", 0xC}, >+ {"MEXICO", 0x8}, >+ {"MOROCCO", 0xC}, >+ {"NETHERLANDS",0xC}, >+ {"NEWZEALAND", 0xF}, >+ {"NIGERIA", 0xC}, >+ {"NORWAY", 0xC}, >+ {"OMAN", 0x8}, >+ {"PAKISTAN", 0x8}, >+ {"PERU", 0x8}, >+ {"PHILIPPINES",0x8}, >+ {"POLAND", 0x8}, >+ {"PORTUGAL", 0xC}, >+ {"ROMANIA", 0x8}, >+ {"RUSSIA", 0x8}, >+ {"SAUDIARABIA",0x8}, >+ {"SINGAPORE", 0x8}, >+ {"SLOVAKIA", 0xE}, >+ {"SLOVENIA", 0xE}, >+ {"SOUTHAFRICA",0xE}, >+ {"SOUTHKOREA", 0x8}, >+ {"SPAIN", 0xC}, >+ {"SWEDEN", 0xC}, >+ {"SWITZERLAND",0xC}, >+ {"SYRIA", 0x8}, >+ {"TAIWAN", 0x8}, >+ {"THAILAND", 0x8}, >+ {"UAE", 0x8}, >+ {"UK", 0xC}, >+ {"USA", 0x8}, >+ {"YEMEN", 0x8} >+}; >+ >+#define INOUT 2 >+ >+/* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses >+ 32 bits. Allocate an extra set just for control too */ >+#define VT_PCIDMA_BLOCKSIZE (DAHDI_MAX_CHUNKSIZE * INOUT * MAX_PORTS * 2 * 2) >+#define VT_PCIDMA_MIDDLE (DAHDI_MAX_CHUNKSIZE * MAX_PORTS - 4) >+#define VT_PCIDMA_END (DAHDI_MAX_CHUNKSIZE * MAX_PORTS * 2 - 4) >+ >+#define ID_DATA_MAXSIZE 30 >+ >+#define NUM_CAL_REGS 12 >+#define NUM_FXO_REGS 60 >+ >+#define TREG(addr) (wc->ioaddr + addr) >+ >+#define TJ_CNTL TREG(0x00) >+#define TJ_OPER TREG(0x01) >+#define TJ_AUXC TREG(0x02) >+#define TJ_AUXD TREG(0x03) >+#define TJ_MASK0 TREG(0x04) >+#define TJ_MASK1 TREG(0x05) >+#define TJ_INTSTAT TREG(0x06) >+#define TJ_AUXR TREG(0x07) >+ >+#define TJ_DMAWS TREG(0x08) >+#define TJ_DMAWI TREG(0x0c) >+#define TJ_DMAWE TREG(0x10) >+#define TJ_DMAWC TREG(0x14) >+#define TJ_DMARS TREG(0x18) >+#define TJ_DMARI TREG(0x1c) >+#define TJ_DMARE TREG(0x20) >+#define TJ_DMARC TREG(0x24) >+ >+#define TJ_AUXINTPOL TREG(0x2A) >+ >+#define TJ_AUXFUNC TREG(0x2b) >+#define TJ_SFDELAY TREG(0x2c) >+#define TJ_SERCTL TREG(0x2d) >+#define TJ_SFLC TREG(0x2e) >+#define TJ_FSCDELAY TREG(0x2f) >+ >+#define TJ_REGBASE TREG(0xc0) >+ >+#define PIB(addr) (TJ_REGBASE + addr * 4) >+ >+#define HTXF_READY (inb(PIB(0)) & 0x10) >+#define HRXF_READY (inb(PIB(0)) & 0x20) >+ >+ >+#define VT_PORT_EMPTY 0 >+#define VT_PORT_VDAA 1 /* Voice DAA - FXO */ >+#define VT_PORT_PROSLIC 2 /* ProSLIC - FXS */ >+ >+#define VBAT 0xC7 >+ >+#define HKMODE_FWDACT 1 >+#define HKMODE_FWDONACT 2 >+#define HKMODE_RINGING 4 >+ >+#define HOOK_ONHOOK 0 >+#define HOOK_OFFHOOK 1 >+ >+#define DSP_CODEC_RING 12 /* RING rising edge detected */ >+#define DSP_CODEC_HKOFF 22 /* station port off hook */ >+#define DSP_CODEC_HKON 23 /* station port on hook */ >+#define DSP_RING_OFF 24 /* RING falling edge detected */ >+#define DSP_DROP 25 >+ >+#define DSP_CODEC_FLASH 26 /* station port hook flash */ >+ >+#define DSP_LOOP_OFFHOOK 38 /* Loop Off hook from OpenPCI */ >+#define DSP_LOOP_ONHOOK 39 /* Loop On hook from OpenPCI */ >+#define DSP_LOOP_POLARITY 40 /* Loop Polarity from OpenPCI */ >+#define DSP_LOOP_NOBATT 41 >+ >+#define DSP_PROSLIC_SANITY 50 /* Sanity alert from a ProSLIC port */ >+#define DSP_PROSLIC_PWR_ALARM 51 /* Power Alarm from a ProSLIC port */ >+#define DSP_VDAA_ISO_FRAME_E 52 /* ISO-cap frame sync lost on VDAA port*/ >+ >+#if VERBOSE_TIMING >+ #define REPORT_WAIT(n,x) \ >+ cardinfo(card->cardnum, #n " wait at %d, " #x " = %d", __LINE__, x ) >+#else >+ #define REPORT_WAIT(n,x) >+#endif >+ >+#define BUSY_WAIT(countvar,cond,delay,iter,failret) \ >+ countvar=0; \ >+ while(cond){ \ >+ udelay(delay); \ >+ if(++countvar > iter){ \ >+ cardcrit(wc->boardnum, "busy wait FAILED at %d", __LINE__); \ >+ return failret; \ >+ } \ >+ } \ >+ REPORT_WAIT(busy,i) >+ >+#define LOCKED_WAIT(countvar,cond,delay,iter,failret) \ >+ countvar=0; \ >+ while(cond){ \ >+ udelay(delay); \ >+ if(++countvar > iter){ \ >+ dbginfo(wc->boardnum,"busy wait failed at %d",__LINE__); \ >+ spin_unlock_irqrestore(&wc->lock, flags); \ >+ return failret; \ >+ } \ >+ } \ >+ REPORT_WAIT(locked,i) >+ >+#define HTXF_WAIT() BUSY_WAIT(i,HTXF_READY,5,500,RET_FAIL) >+#define HRXF_WAIT() BUSY_WAIT(i,!HRXF_READY,5,70000,RET_FAIL) >+#define HTXF_WAIT_RET(failret) BUSY_WAIT(i,HTXF_READY,5,500,failret) >+#define HRXF_WAIT_RET(failret) BUSY_WAIT(i,!HRXF_READY,5,1000,failret) >+ >+#define HTXF_WAIT_LOCKED() LOCKED_WAIT(i,HTXF_READY,5,500,RET_FAIL) >+#define HRXF_WAIT_LOCKED() LOCKED_WAIT(i,!HRXF_READY,5,1000,RET_FAIL) >+#define HTXF_WAIT_LOCKED_RET(failret) LOCKED_WAIT(i,HTXF_READY,5,500,failret) >+#define HRXF_WAIT_LOCKED_RET(failret) LOCKED_WAIT(i,!HRXF_READY,5,1000,failret) >+ >+ >+struct openpci { >+ struct pci_dev *dev; >+ char *variety; >+ int boardnum; >+ int portcount; >+ int porttype[MAX_PORTS]; >+ >+ int firmware; >+ char serial[ID_DATA_MAXSIZE]; >+ >+ spinlock_t lock; >+ >+ //XXX Replace these with proper try_module_get locking in the dahdi driver. >+ //int usecount; //XXX >+ //int dead; //XXX >+ union { >+ struct { >+ int offhook; >+ } fxo; >+ struct { >+ int ohttimer; >+ int idletxhookstate; /* IDLE changing hook state */ >+ int lasttxhook; >+ } fxs; >+ } mod[MAX_PORTS]; >+ >+ unsigned long ioaddr; >+ dma_addr_t readdma; >+ dma_addr_t writedma; >+ volatile unsigned int *writechunk; /* Double-word aligned write memory */ >+ volatile unsigned int *readchunk; /* Double-word aligned read memory */ >+ >+ struct dahdi_chan _chans[MAX_PORTS]; >+ struct dahdi_chan *chans[MAX_PORTS]; >+ struct dahdi_span span; >+} *cards[MAX_CARDS]; >+ >+// You must hold this lock anytime you access or modify the cards[] array. >+DEFINE_MUTEX(cards_mutex); >+ >+static unsigned char fxo_port_lookup[8] = { 0x0, 0x8, 0x4, 0xc, 0x10, 0x18, 0x14, 0x1c}; >+static unsigned char fxs_port_lookup[8] = { 0x0, 0x1, 0x2, 0x3, 0x10, 0x11, 0x12, 0x13}; >+static char wcopenpci[] = "Voicetronix OpenPCI"; >+ >+static char *country = DEFAULT_COUNTRY; >+static int reversepolarity; // = 0 >+static int debug; // = 0 >+ >+module_param(country, charp, 0444); >+module_param(debug, int, 0600); >+module_param(reversepolarity, int, 0600); >+MODULE_PARM_DESC(country, "Set the default country name"); >+MODULE_PARM_DESC(debug, "Enable verbose logging"); >+ >+//#define DEBUG_LOOP_VOLTAGE 1 >+#ifdef DEBUG_LOOP_VOLTAGE >+ // This param is a 32 bit bitfield where bit 1 << cardnum * 8 << portnum >+ // will enable voltage monitoring on that port (fxo only presently) >+ static int voltmeter; // = 0 >+ module_param(voltmeter, int, 0600); >+ MODULE_PARM_DESC(voltmeter, "Enable loop voltage metering"); >+#endif >+ >+ >+/* boolean return values */ >+#define RET_OK 1 >+#define RET_FAIL 0 >+ >+/* Convenience macros for logging */ >+#define info(format,...) printk(KERN_INFO NAME ": " format "\n" , ## __VA_ARGS__) >+#define warn(format,...) printk(KERN_WARNING NAME ": " format "\n" , ## __VA_ARGS__) >+#define crit(format,...) printk(KERN_CRIT NAME ": " format "\n" , ## __VA_ARGS__) >+#define cardinfo(cardnum,format,...) info("[%02d] " format, cardnum , ## __VA_ARGS__) >+#define cardwarn(cardnum,format,...) warn("[%02d] " format, cardnum , ## __VA_ARGS__) >+#define cardcrit(cardnum,format,...) crit("[%02d] " format, cardnum , ## __VA_ARGS__) >+#define dbginfo(cardnum,format,...) if(debug) info("[%02d] " format, cardnum , ## __VA_ARGS__) >+ >+ >+static inline const char *porttype(struct openpci *wc, int port) >+{ //{{{ >+ switch( wc->porttype[port] ) { >+ case VT_PORT_VDAA: return "VDAA"; >+ case VT_PORT_PROSLIC: return "ProSLIC"; >+ case VT_PORT_EMPTY: return "empty port"; >+ default: return "unknown type"; >+ } >+} //}}} >+ >+ >+static int __read_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char *value) >+{ //{{{ >+ unsigned char portadr = fxo_port_lookup[port]; >+ int i; >+ >+ if (HRXF_READY) *value = inb(PIB(1)); >+ >+ outb(0x11, PIB(1)); HTXF_WAIT(); >+ outb(0x2, PIB(1)); HTXF_WAIT(); >+ outb(portadr, PIB(1)); HTXF_WAIT(); >+ outb(reg, PIB(1)); HTXF_WAIT(); >+ HRXF_WAIT(); *value = inb(PIB(1)); >+ >+ return RET_OK; >+} //}}} >+ >+static int read_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char *value) >+{ //{{{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ if( __read_reg_fxo(wc, port, reg, value) ){ >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ cardcrit(wc->boardnum, "FXO port %d, reg %d, read failed!", port, reg); >+ return RET_FAIL; >+} //}}} >+ >+static int __read_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char *value) >+{ //{{{ >+ unsigned char portadr = fxs_port_lookup[port]; >+ int i; >+ >+ if (HRXF_READY) *value = inb(PIB(1)); >+ >+ outb(0x13, PIB(1)); HTXF_WAIT(); >+ outb(0x2, PIB(1)); HTXF_WAIT(); >+ outb(portadr, PIB(1)); HTXF_WAIT(); >+ outb(reg, PIB(1)); HTXF_WAIT(); >+ HRXF_WAIT(); *value = inb(PIB(1)); >+ >+ return RET_OK; >+} //}}} >+ >+static int read_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char *value) >+{ //{{{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ if( __read_reg_fxs(wc, port, reg, value) ) { >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ cardcrit(wc->boardnum, "FXS port %d, reg %d, read failed!", port, reg); >+ return RET_FAIL; >+} //}}} >+ >+static int __write_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char value) >+{ //{{{ >+ unsigned char portadr = fxo_port_lookup[port]; >+ int i; >+ >+ outb(0x10, PIB(1) ); HTXF_WAIT(); >+ outb(0x3, PIB(1)); HTXF_WAIT(); >+ outb(portadr, PIB(1)); HTXF_WAIT(); >+ outb(reg, PIB(1)); HTXF_WAIT(); >+ outb(value, PIB(1)); HTXF_WAIT(); >+ >+ return RET_OK; >+} //}}} >+ >+static int write_reg_fxo(struct openpci *wc, int port, unsigned char reg, unsigned char value) >+{ //{{{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ if( __write_reg_fxo(wc, port, reg, value) ){ >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ cardcrit(wc->boardnum, "FXO port %d, reg %d, write(%d) failed!", port, reg, value); >+ return RET_FAIL; >+} //}}} >+ >+static int __write_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char value) >+{ //{{{ >+ unsigned char portadr = fxs_port_lookup[port]; >+ int i; >+ >+ outb(0x12, PIB(1) ); HTXF_WAIT(); >+ outb(0x3, PIB(1)); HTXF_WAIT(); >+ outb(portadr, PIB(1)); HTXF_WAIT(); >+ outb(reg, PIB(1)); HTXF_WAIT(); >+ outb(value, PIB(1)); HTXF_WAIT(); >+ >+ return RET_OK; >+} //}}} >+ >+static int write_reg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned char value) >+{ //{{{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ if( __write_reg_fxs(wc, port, reg, value) ){ >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ cardcrit(wc->boardnum, "FXS port %d, reg %d, write(%d) failed!", port, reg, value); >+ return RET_FAIL; >+} //}}} >+ >+static int __wait_indreg_fxs(struct openpci *wc, int port) >+{ //{{{ >+ unsigned char value; >+ int count = 100; >+ >+ while (--count) >+ { >+ if( __read_reg_fxs(wc, port, I_STATUS, &value) ){ >+ if( value == 0 ) >+ return RET_OK; >+ } else { >+ cardcrit(wc->boardnum, >+ "failed to read port %d PS_IND_ADDR_ST, retrying...", >+ port); >+ } >+ udelay(5); >+ } >+ cardcrit(wc->boardnum, "Failed to wait for indirect reg write to port %d", port); >+ return RET_FAIL; >+} //}}} >+ >+static int write_indreg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned short value) >+{ //{{{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ if( __wait_indreg_fxs(wc, port) >+ && __write_reg_fxs(wc, port, IDA_LO, value & 0xff) >+ && __write_reg_fxs(wc, port, IDA_HI, (value & 0xff00)>>8) >+ && __write_reg_fxs(wc, port, IAA, reg) >+ && __wait_indreg_fxs(wc, port) ) >+ { >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ cardcrit(wc->boardnum, "FXS indreg %d write failed on port %d", reg, port); >+ return RET_FAIL; >+} //}}} >+ >+static int read_indreg_fxs(struct openpci *wc, int port, unsigned char reg, unsigned short *value) >+{ //{{{ >+ unsigned long flags; >+ unsigned char lo, hi; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ if( __wait_indreg_fxs(wc, port) >+ && __write_reg_fxs(wc, port, IAA, reg) >+ && __wait_indreg_fxs(wc, port) >+ && __read_reg_fxs(wc, port, IDA_LO, &lo) >+ && __read_reg_fxs(wc, port, IDA_HI, &hi) ) >+ { >+ *value = lo | hi << 8; >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_FAIL; >+} //}}} >+ >+static void start_dma(struct openpci *wc) >+{ //{{{ >+ outb(0x0f, TJ_CNTL); >+ set_current_state(TASK_INTERRUPTIBLE); >+ schedule_timeout(1); >+ outb(0x01, TJ_CNTL); >+ outb(0x01, TJ_OPER); >+} //}}} >+ >+static void restart_dma(struct openpci *wc) >+{ //{{{ >+ /* Reset Master and TDM */ >+ outb(0x01, TJ_CNTL); >+ outb(0x01, TJ_OPER); >+} //}}} >+ >+/* You must hold the card spinlock to call this function */ >+static int __ping_arm(struct openpci *wc) >+{ //{{{ >+ int i; >+ int pong=0; >+ >+ while(pong != 0x02){ >+ outb(0x02, PIB(1)); HTXF_WAIT(); >+ HRXF_WAIT(); pong = inb(PIB(1)); >+ dbginfo(wc->boardnum, "ping_arm returned %x", pong); >+ } >+ while(pong == 0x02){ >+ // Poke no-ops into the arm while it is still returning data, >+ // if 500 usec elapses with no further response from it then >+ // the message queue is should be completely cleared. >+ outb(0x00, PIB(1)); HTXF_WAIT(); >+ i = 100; >+ while( !HRXF_READY && --i ) udelay(5); >+ if( i == 0 ) break; >+ pong = inb(PIB(1)); >+ dbginfo(wc->boardnum, "ping_arm returned %x.", pong); >+ } >+ return RET_OK; >+} //}}} >+ >+static void arm_event(struct openpci *wc, char *msg) >+{ //{{{ >+ int port = msg[0]; >+ >+ switch(msg[1]){ >+ case DSP_LOOP_OFFHOOK: >+ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); >+ dbginfo(wc->boardnum, "Port %d Loop OffHook", port); >+ break; >+ >+ case DSP_LOOP_ONHOOK: >+ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_ONHOOK); >+ dbginfo(wc->boardnum, "Port %d Loop OnHook", port); >+ break; >+ >+ case DSP_LOOP_POLARITY: >+ dahdi_qevent_lock(wc->chans[port], DAHDI_EVENT_POLARITY); >+ dbginfo(wc->boardnum, "Port %d Loop Polarity", port); >+ break; >+ >+ case DSP_CODEC_RING: >+ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_RING); >+ dbginfo(wc->boardnum, "Port %d Ring On", port); >+ break; >+ >+ case DSP_RING_OFF: >+ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); >+ dbginfo(wc->boardnum, "Port %d Ring Off", port); >+ break; >+ >+ case DSP_CODEC_HKOFF: >+ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_OFFHOOK); >+ dbginfo(wc->boardnum, "Port %d Station OffHook", port); >+ if (reversepolarity) >+ wc->mod[port].fxs.idletxhookstate = 5; >+ else >+ wc->mod[port].fxs.idletxhookstate = 1; >+ break; >+ >+ case DSP_CODEC_HKON: >+ dahdi_hooksig(wc->chans[port], DAHDI_RXSIG_ONHOOK); >+ dbginfo(wc->boardnum, "Port %d Station OnHook", port); >+ if (reversepolarity) >+ wc->mod[port].fxs.idletxhookstate = 6; >+ else >+ wc->mod[port].fxs.idletxhookstate = 2; >+ break; >+ >+ case DSP_CODEC_FLASH: >+ dahdi_qevent_lock(wc->chans[port], DAHDI_EVENT_WINKFLASH); >+ dbginfo(wc->boardnum, "Port %d Station Flash", port); >+ break; >+ >+ case DSP_DROP: >+ case DSP_LOOP_NOBATT: >+ break; >+ >+ //XXX What to do to recover from these? >+ case DSP_PROSLIC_SANITY: >+ dbginfo(wc->boardnum, "Port %d ProSlic has gone insane!", port); >+ break; >+ >+ case DSP_PROSLIC_PWR_ALARM: >+ { >+ char errbuf[32] = " Unknown", *p = errbuf; >+ int i = 49; >+ >+ msg[2] >>= 2; >+ for(; i < 55; ++i, msg[2] >>= 1 ) >+ if(msg[2] & 1){ *(++p)='Q'; *(++p)=i; *(++p)=','; } >+ if( p != errbuf ) *p = '\0'; >+ cardcrit(wc->boardnum,"%d: ProSlic power ALARM:%s",msg[0],errbuf); >+ //write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook ); >+ return; >+ } >+ >+ case DSP_VDAA_ISO_FRAME_E: >+ dbginfo(wc->boardnum, "Port %d VDAA has lost ISO-Cap frame lock", port); >+ break; >+ >+ default: >+ cardwarn(wc->boardnum, "Unknown message from Arm[%d] for port %d", >+ msg[1], port); >+ break; >+ } >+} //}}} >+ >+/* You must hold the card spinlock to call this function */ >+static inline int __read_arm_byte( struct openpci *wc, unsigned char *msg ) >+{ //{{{ >+ int i; >+ >+ HRXF_WAIT(); *msg = inb(PIB(1)); >+ return RET_OK; >+} //}}} >+ >+static inline int read_arm_msg( struct openpci *wc, unsigned char *msg ) >+{ //{{{ >+ unsigned long flags; >+ int i, d, count; >+ int ret = RET_OK; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ outb(0x08, PIB(1)); HTXF_WAIT_LOCKED(); >+ //XXX Do we need to clear the interrupt flag even if this fails? >+ HRXF_WAIT_LOCKED(); count = inb(PIB(1)); >+ if( count == 0 ){ >+ ret = RET_FAIL; >+ } else if( count < 3 || count > 4 ){ >+ cardcrit(wc->boardnum, "BOGUS arm message size %d, flushing queue", count); >+ // NB: This may take a while (up to 500usec or more) to complete >+ // and we are in the isr at present when this is called, so >+ // we may miss an interrupt or two while this is done in the >+ // bottom half, but we are already in trouble, so... >+ d = debug; debug = 5; __ping_arm( wc ); debug = d; >+ ret = RET_FAIL; >+ } else while( --count ){ >+ if( ! __read_arm_byte(wc, msg) ){ >+ cardcrit(wc->boardnum, >+ "Failed to read arm message %d more bytes expected", >+ count); >+ ret = RET_FAIL; >+ break; >+ } >+ ++msg; >+ } >+ outb(0x09, PIB(1)); HTXF_WAIT_LOCKED(); >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return ret; >+} //}}} >+ >+static void openpci_arm_work( void *cardptr ) >+{ //{{{ >+ struct openpci *wc = (struct openpci*)cardptr; >+ unsigned char armmsg[4]; >+ >+ if( read_arm_msg(wc, armmsg) ) arm_event(wc, armmsg); >+} //}}} >+ >+ >+static inline void openpci_write(struct openpci *wc, unsigned char flags) >+{ //{{{ >+ int x,y; >+ volatile unsigned int *writechunk; >+ >+ if (flags & 0x01) >+ writechunk = wc->writechunk; >+ else if (flags & 0x02) >+ writechunk = wc->writechunk + DAHDI_CHUNKSIZE*2; >+ else { >+ cardcrit(wc->boardnum, "bad write interrupt flags %x, at %x", >+ flags, inb(TJ_DMAWC) ); >+ return; >+ } >+ /* get data */ >+ dahdi_transmit(&wc->span); >+ for (y=0,x=0;x<DAHDI_CHUNKSIZE;++x) { >+ /* Send a sample, as a 32-bit word */ >+#ifdef __BIG_ENDIAN >+#error No big endian support (yet) >+#else >+ /* transmit second 4 ports */ >+ writechunk[y]=0; >+ if (wc->porttype[4]) >+ writechunk[y] |= (wc->chans[4]->writechunk[x] << 24); >+ else >+ writechunk[y] |= (0x01 << 24); >+ if (wc->porttype[5]) >+ writechunk[y] |= (wc->chans[5]->writechunk[x] << 16); >+ if (wc->porttype[6]) >+ writechunk[y] |= (wc->chans[6]->writechunk[x] << 8); >+ if (wc->porttype[7]) >+ writechunk[y] |= (wc->chans[7]->writechunk[x]); >+ ++y; >+ >+ /* transmit first 4 ports */ >+ writechunk[y]=0x01000000; >+ /* Make sure first port doesnt equal 0x00 */ >+ if (wc->porttype[0]){ >+ if (wc->chans[0]->writechunk[x] == 0) >+ writechunk[y] |= (0x01 << 24); >+ else >+ writechunk[y] |= (wc->chans[0]->writechunk[x] << 24); >+ } >+ //else writechunk[y] |= (0x00 << 24); >+ if (wc->porttype[1]) >+ writechunk[y] |= (wc->chans[1]->writechunk[x] << 16); >+ if (wc->porttype[2]) >+ writechunk[y] |= (wc->chans[2]->writechunk[x] << 8); >+ if (wc->porttype[3]) >+ writechunk[y] |= (wc->chans[3]->writechunk[x]); >+ ++y; >+#endif >+ } >+} //}}} >+ >+static inline void openpci_read(struct openpci *wc, unsigned char flags) >+{ //{{{ >+ int x,y; >+ volatile unsigned int *readchunk; >+ >+ if (flags & 0x08) >+ readchunk = wc->readchunk + DAHDI_CHUNKSIZE*2; >+ else if (flags & 0x04) >+ readchunk = wc->readchunk; >+ else { >+ cardcrit(wc->boardnum, "bad read interrupt flags %x, at %x", >+ flags, inb(TJ_DMARC)); >+ return; >+ } >+ >+ for (y=0,x=0;x<DAHDI_CHUNKSIZE;++x) { >+#ifdef __BIG_ENDIAN >+#error No big endian support (yet) >+#else >+ /* Receive first 4 ports */ >+ >+ if (wc->porttype[0]) >+ wc->chans[0]->readchunk[x] = (readchunk[y] >> 24) & 0xff; >+ if (wc->porttype[1]) >+ wc->chans[1]->readchunk[x] = (readchunk[y] >> 16) & 0xff; >+ if (wc->porttype[2]) >+ wc->chans[2]->readchunk[x] = (readchunk[y] >> 8) & 0xff; >+ if (wc->porttype[3]) >+ wc->chans[3]->readchunk[x] = (readchunk[y]) & 0xff; >+ ++y; >+ /* Receive second 4 ports */ >+ if (wc->porttype[4]) >+ wc->chans[4]->readchunk[x] = (readchunk[y] >> 24) & 0xff; >+ if (wc->porttype[5]) >+ wc->chans[5]->readchunk[x] = (readchunk[y] >> 16) & 0xff; >+ if (wc->porttype[6]) >+ wc->chans[6]->readchunk[x] = (readchunk[y] >> 8) & 0xff; >+ if (wc->porttype[7]) >+ wc->chans[7]->readchunk[x] = (readchunk[y]) & 0xff; >+ ++y; >+#endif >+ } >+ /* XXX We're wasting 8 taps. We should get closer :( */ >+ for (x = 0; x < MAX_PORTS; x++) { >+ if (wc->porttype[x]) >+ dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk); >+ } >+ dahdi_receive(&wc->span); >+} //}}} >+ >+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) >+static irqreturn_t openpci_isr(int irq, void *dev_id, struct pt_regs *regs) >+#else >+static irqreturn_t openpci_isr(int irq, void *dev_id) >+#endif >+{ //{{{ >+ struct openpci *wc = dev_id; >+ unsigned long flags; >+ unsigned char status; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ status = inb(TJ_INTSTAT); >+ outb(status, TJ_INTSTAT); >+ >+ if (!status) { >+ if(inb(TJ_AUXR) & 0x02) { >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return IRQ_NONE; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ openpci_arm_work(wc); >+ return IRQ_HANDLED; >+ } >+ if (status & 0x10){ >+ /* PCI Master abort */ >+ cardcrit(wc->boardnum, "PCI Master Abort."); >+ /* Stop DMA, wait for watchdog */ >+ outb(0x00, TJ_OPER); >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return IRQ_HANDLED; >+ } >+ spin_unlock_irqrestore(&wc->lock, flags); >+ >+ if (status & 0x20){ >+ /* PCI Target abort */ >+ cardcrit(wc->boardnum, "PCI Target Abort."); >+ return IRQ_HANDLED; >+ } >+ if (status & 0x03){ >+ openpci_write(wc, status); >+ } >+ if (status & 0x0c){ >+ #ifdef DEBUG_LOOP_VOLTAGE >+ //{{{ >+ static int counter[MAX_CARDS]; >+ int card = wc->boardnum; >+ int port = ++counter[card] & 0x07; >+ int ignore = counter[card] & 0xf0; >+ >+ if( ! ignore && (voltmeter & ((1 << (card * 8)) << port)) ) { >+ unsigned char lv; >+ if( wc->porttype[port] == VT_PORT_VDAA && read_reg_fxo(wc, port, 29, &lv) ) >+ cardinfo(wc->boardnum, "Port %d loop voltage %d", >+ port, lv < 128 ? lv : lv - 256); >+ } >+ //}}} >+ #endif >+ openpci_read(wc, status); >+ } >+ >+ return IRQ_HANDLED; >+} //}}} >+ >+static int openpci_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) >+{ //{{{ >+ struct wctdm_stats stats; >+ struct wctdm_regs regs; >+ struct wctdm_regop regop; >+ struct wctdm_echo_coefs echoregs; >+ struct openpci *wc = chan->pvt; >+ int port = chan->chanpos - 1; >+ int x; >+ >+ switch (cmd) { >+ case DAHDI_ONHOOKTRANSFER: >+ if (wc->porttype[port] != VT_PORT_PROSLIC) >+ return -EINVAL; >+ if (get_user(x, (int *)data)) >+ return -EFAULT; >+ wc->mod[port].fxs.ohttimer = x << 3; >+ if (reversepolarity) >+ wc->mod[port].fxs.idletxhookstate = 0x6; /* OHT mode when idle */ >+ else >+ wc->mod[port].fxs.idletxhookstate = 0x2; >+ switch(wc->mod[port].fxs.lasttxhook) { >+ case 0x1: >+ case 0x5: >+ if (reversepolarity) >+ wc->mod[port].fxs.lasttxhook = 0x6; >+ else >+ wc->mod[port].fxs.lasttxhook = 0x2; >+ if( ! write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook) ) >+ return -EIO; >+ } >+ break; >+ case DAHDI_SETPOLARITY: >+ if (get_user(x, (int *)data)) >+ return -EFAULT; >+ if (wc->porttype[port] != VT_PORT_PROSLIC) >+ return -EINVAL; >+ /* Can't change polarity while ringing or when open */ >+ if ((wc->mod[port].fxs.lasttxhook == 0x04) || >+ (wc->mod[port].fxs.lasttxhook == 0x00)) >+ return -EINVAL; >+ >+ if ((x && !reversepolarity) || (!x && reversepolarity)) >+ wc->mod[port].fxs.lasttxhook |= 0x04; >+ else >+ wc->mod[port].fxs.lasttxhook &= ~0x04; >+ if( ! write_reg_fxs(wc, port, 64, wc->mod[port].fxs.lasttxhook) ) >+ return -EIO; >+ break; >+ case WCTDM_GET_STATS: >+ if (wc->porttype[port] == VT_PORT_PROSLIC) { >+ unsigned char linevolt; >+ if( read_reg_fxs(wc, port, 80, &linevolt) ) >+ stats.tipvolt = linevolt * -376; >+ else >+ return -EIO; >+ if( read_reg_fxs(wc, port, 81, &linevolt) ) >+ stats.ringvolt = linevolt * -376; >+ else >+ return -EIO; >+ if( read_reg_fxs(wc, port, 82, &linevolt) ) >+ stats.batvolt = linevolt * -376; >+ else >+ return -EIO; >+ } else if (wc->porttype[port] == VT_PORT_VDAA) { >+ unsigned char linevolt; >+ if( read_reg_fxo(wc, port, 29, &linevolt) ) >+ stats.tipvolt = stats.ringvolt = stats.batvolt = linevolt * 1000; >+ else >+ return -EIO; >+ } else >+ return -EINVAL; >+ if (copy_to_user((struct wctdm_stats *)data, &stats, sizeof(stats))) >+ return -EFAULT; >+ break; >+ case WCTDM_GET_REGS: >+ if (wc->porttype[port] == VT_PORT_PROSLIC) { >+ for (x=0;x<NUM_INDIRECT_REGS;x++) >+ if( ! read_indreg_fxs(wc, port, x, ®s.indirect[x]) ) >+ return -EIO; >+ for (x=0;x<NUM_REGS;x++) >+ if( ! read_reg_fxs(wc, port, x, ®s.direct[x]) ) >+ return -EIO; >+ } else { >+ memset(®s, 0, sizeof(regs)); >+ for (x=0;x<NUM_FXO_REGS;x++){ >+ if( ! read_reg_fxo(wc, port, x, ®s.direct[x]) ) >+ return -EIO; >+ } >+ } >+ if (copy_to_user((struct wctdm_regs *)data, ®s, sizeof(regs))) >+ return -EFAULT; >+ break; >+ case WCTDM_SET_REG: >+ if (copy_from_user(®op, (struct wctdm_regop *)data, sizeof(regop))) >+ return -EFAULT; >+ if (regop.indirect) { >+ if (wc->porttype[port] != VT_PORT_PROSLIC) >+ return -EINVAL; >+ printk("Setting indirect %d to 0x%04x on %d\n", >+ regop.reg, regop.val, chan->chanpos); >+ if( ! write_indreg_fxs(wc, port, regop.reg, regop.val) ) >+ return -EIO; >+ } else { >+ regop.val &= 0xff; >+ printk("Setting direct %d to %04x on %d\n", >+ regop.reg, regop.val, chan->chanpos); >+ if (wc->porttype[port] == VT_PORT_PROSLIC) { >+ if( ! write_reg_fxs(wc, port, regop.reg, regop.val) ) >+ return -EIO; >+ } else { >+ if( ! write_reg_fxo(wc, port, regop.reg, regop.val) ) >+ return -EIO; >+ } >+ } >+ break; >+ case WCTDM_SET_ECHOTUNE: >+ cardinfo(wc->boardnum, "Setting echo registers"); >+ if (copy_from_user(&echoregs, (struct wctdm_echo_coefs*)data, sizeof(echoregs))) >+ return -EFAULT; >+ >+ if (wc->porttype[port] == VT_PORT_VDAA) { >+ /* Set the ACIM and digital echo canceller registers */ >+ if( ! write_reg_fxo(wc, port, 30, echoregs.acim) >+ || ! write_reg_fxo(wc, port, 45, echoregs.coef1) >+ || ! write_reg_fxo(wc, port, 46, echoregs.coef2) >+ || ! write_reg_fxo(wc, port, 47, echoregs.coef3) >+ || ! write_reg_fxo(wc, port, 48, echoregs.coef4) >+ || ! write_reg_fxo(wc, port, 49, echoregs.coef5) >+ || ! write_reg_fxo(wc, port, 50, echoregs.coef6) >+ || ! write_reg_fxo(wc, port, 51, echoregs.coef7) >+ || ! write_reg_fxo(wc, port, 52, echoregs.coef8) ) >+ { >+ cardcrit(wc->boardnum, "Failed to set echo registers"); >+ return -EIO; >+ } >+ break; >+ } else { >+ return -EINVAL; >+ } >+ break; >+ default: >+ return -ENOTTY; >+ } >+ return 0; >+} //}}} >+ >+static int openpci_open(struct dahdi_chan *chan) >+{ >+ struct openpci *wc = chan->pvt; >+ if( ! wc->porttype[chan->chanpos-1] ) >+ return -ENODEV; >+ >+ //XXX This is WRONG and can prang in a race. We must pass THIS_MODULE >+ // as the owner of the span that holds the pointer to this function, >+ // then bump the refcount in the dahdi code _BEFORE_ the potentially >+ // fatal call to an invalid pointer is made. >+ //if( wc->dead ) return -ENODEV; >+ //wc->usecount++; >+ try_module_get(THIS_MODULE); //XXX >+ >+ return 0; >+} >+ >+static inline struct openpci* openpci_from_span(struct dahdi_span *span) { >+ return container_of(span, struct openpci, span); >+} >+ >+static int openpci_watchdog(struct dahdi_span *span, int event) >+{ >+ info("TDM: Restarting DMA"); >+ restart_dma(openpci_from_span(span)); >+ return 0; >+} >+ >+static int openpci_close(struct dahdi_chan *chan) >+{ >+ struct openpci *wc = chan->pvt; >+ int port = chan->chanpos - 1; >+ >+ //XXX wc->usecount--; >+ //XXX This is WRONG and can prang in a race. We must pass THIS_MODULE >+ // as the owner of the span that holds the pointer to this function, >+ // then bump the refcount in the dahdi code _BEFORE_ the potentially >+ // fatal call to an invalid pointer is made. >+ module_put(THIS_MODULE); >+ if (wc->porttype[port] == VT_PORT_PROSLIC) { >+ if (reversepolarity) >+ wc->mod[port].fxs.idletxhookstate = 5; >+ else >+ wc->mod[port].fxs.idletxhookstate = 1; >+ } >+ /* If we're dead, release us now */ >+ //XXX if (!wc->usecount && wc->dead) openpci_release(wc); >+ >+ return 0; >+} >+ >+static int openpci_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) >+{ //{{{ >+ struct openpci *wc = chan->pvt; >+ int port = chan->chanpos - 1; >+ int new_hk_state; >+ >+ dbginfo(wc->boardnum, "Setting %s port %d hook state %s", >+ wc->porttype[port] == VT_PORT_VDAA ? "FXO" : "FXS", >+ port, >+ txsig == 0 ? "ONHOOK" : >+ txsig == 1 ? "OFFHOOK" : >+ txsig == 2 ? "START" : >+ txsig == 3 ? "KEWL" : "UNKNOWN" ); >+ >+ switch(wc->porttype[port]) { >+ case VT_PORT_VDAA: >+ switch(txsig) { >+ case DAHDI_TXSIG_START: >+ case DAHDI_TXSIG_OFFHOOK: >+ if( write_reg_fxo(wc, port, 5, 0x9) >+ && write_reg_fxo(wc, port, 0x20, 0x0) ) >+ wc->mod[port].fxo.offhook = 1; >+ else >+ cardcrit(wc->boardnum, "Failed set fxo off-hook"); >+ break; >+ >+ case DAHDI_TXSIG_ONHOOK: >+ if( write_reg_fxo(wc, port, 5, 0x8) >+ && write_reg_fxo(wc, port, 0x20, 0x3) ) >+ wc->mod[port].fxo.offhook = 0; >+ else >+ cardcrit(wc->boardnum, "Failed set fxo on-hook"); >+ break; >+ >+ default: >+ cardcrit(wc->boardnum, >+ "Can't set FXO port %d tx state to %d", >+ port, txsig); >+ } >+ break; >+ >+ case VT_PORT_PROSLIC: >+ new_hk_state = wc->mod[port].fxs.lasttxhook; >+ switch(txsig) { >+ case DAHDI_TXSIG_ONHOOK: >+ switch(chan->sig) { >+ case DAHDI_SIG_EM: >+ case DAHDI_SIG_FXOKS: >+ case DAHDI_SIG_FXOLS: >+ new_hk_state = wc->mod[port].fxs.idletxhookstate; >+ break; >+ case DAHDI_SIG_FXOGS: >+ new_hk_state = 3; >+ break; >+ } >+ break; >+ >+ case DAHDI_TXSIG_OFFHOOK: >+ switch(chan->sig) { >+ case DAHDI_SIG_EM: >+ new_hk_state = 5; >+ break; >+ default: >+ new_hk_state = wc->mod[port].fxs.idletxhookstate; >+ break; >+ } >+ break; >+ >+ case DAHDI_TXSIG_START: >+ new_hk_state = 4; >+ break; >+ >+ case DAHDI_TXSIG_KEWL: >+ new_hk_state = 0; >+ break; >+ >+ default: >+ cardinfo(wc->boardnum, >+ "Can't set FXS port %d tx state to %d", >+ port, txsig); >+ } >+ dbginfo(wc->boardnum, "%s port %d hook state old %d, new %d", >+ wc->porttype[port] == VT_PORT_VDAA ? "FXO" : "FXS", >+ port, wc->mod[port].fxs.lasttxhook, new_hk_state ); >+ >+ if (new_hk_state != wc->mod[port].fxs.lasttxhook){ >+ if( write_reg_fxs(wc, port, 64, new_hk_state) ) >+ wc->mod[port].fxs.lasttxhook = new_hk_state; >+ else >+ cardcrit(wc->boardnum, >+ "Failed to set port %d fxs hookstate from %d to %d", >+ port, wc->mod[port].fxs.lasttxhook, new_hk_state); >+ } >+ break; >+ >+ default: >+ cardcrit(wc->boardnum, >+ "Unknown module type %d in openpci_hooksig", >+ wc->porttype[port] ); >+ } >+ return 0; >+} //}}} >+ >+static const struct dahdi_span_ops openpci_span_ops = { >+ .owner = THIS_MODULE, >+ .hooksig = openpci_hooksig, >+ .open = openpci_open, >+ .close = openpci_close, >+ .ioctl = openpci_ioctl, >+ .watchdog = openpci_watchdog >+}; >+ >+static int span_initialize(struct openpci *wc) >+{ //{{{ >+ int x; >+ >+ //XXX Set a THIS_MODULE as the owner of the span... >+ /* Zapata stuff */ >+ sprintf(wc->span.name, "WCTDM/%d", wc->boardnum); >+ sprintf(wc->span.desc, "%s Board %d", wc->variety, wc->boardnum + 1); >+ for (x = 0; x < MAX_PORTS; x++) { >+ struct dahdi_chan *chan = &wc->_chans[x]; >+ wc->chans[x] = chan; >+ sprintf(chan->name, "WCTDM/%d/%d", wc->boardnum, x); >+ chan->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS >+ | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; >+ chan->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; >+ chan->chanpos = x+1; >+ chan->pvt = wc; >+ } >+ wc->span.deflaw = DAHDI_LAW_MULAW; >+ wc->span.chans = wc->chans; >+ wc->span.channels = MAX_PORTS; >+ wc->span.flags = DAHDI_FLAG_RBS; >+ wc->span.ops = &openpci_span_ops; >+ >+ if (dahdi_register(&wc->span, 0)) { >+ cardcrit(wc->boardnum, "Unable to register span with dahdi"); >+ return RET_FAIL; >+ } >+ return RET_OK; >+} //}}} >+ >+static int get_port_type(struct openpci *wc, int port) >+{ //{{{ >+ int i, type; >+ unsigned long flags; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ outb(0x20, PIB(1)); HTXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); >+ outb(port, PIB(1)); HTXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); >+ HRXF_WAIT_LOCKED_RET(VT_PORT_EMPTY); type = inb(PIB(1)); >+ spin_unlock_irqrestore(&wc->lock, flags); >+ >+ return type; >+} //}}} >+ >+static int check_ports(struct openpci *wc) >+{ //{{{ >+ int i = 0; >+ >+ wc->portcount = 0; >+ for(; i < MAX_PORTS; ++i ){ >+ wc->porttype[i] = get_port_type(wc, i); >+ dbginfo(wc->boardnum,"%d: %s", i, porttype(wc,i)); >+ >+ switch( wc->porttype[i] ) { >+ case VT_PORT_PROSLIC: >+ /* By default, don't send on hook */ >+ if (reversepolarity) >+ wc->mod[i].fxs.idletxhookstate = 5; >+ else >+ wc->mod[i].fxs.idletxhookstate = 1; >+ >+ case VT_PORT_VDAA: >+ ++wc->portcount; >+ } >+ } >+ // we 'succeed' if any ports were discovered. >+ return wc->portcount ? RET_OK : RET_FAIL; >+} //}}} >+ >+static int configure_vdaa_country(struct openpci *wc, int port, char *name) >+{ //{{{ >+ unsigned char value; >+ int i; >+ >+ for (i=0; i < sizeof(fxo_modes)/sizeof(struct fxo_mode); ++i){ >+ if(!strcmp(fxo_modes[i].name, name)){ >+ dbginfo(wc->boardnum, "%d: Setting country to %s", port, name); >+ goto part2; >+ } >+ } >+ i = 3; >+ cardinfo(wc->boardnum, "Using default country %s", fxo_modes[i].name); >+ >+ part2: >+ value = (fxo_modes[i].ohs << 6); >+ value |= (fxo_modes[i].rz << 1); >+ value |= (fxo_modes[i].rt << 0); >+ if( ! write_reg_fxo(wc, port, 16, value) ) goto hell; >+ >+ /* DC Termination Control - Register 26 */ >+ value = (fxo_modes[i].dcv << 6); >+ value |= (fxo_modes[i].mini << 4); >+ value |= (fxo_modes[i].ilim << 1); >+ if( ! write_reg_fxo(wc, port, 26, value) ) goto hell; >+ >+ /* AC Termination Control - Register 30 */ >+ value = (fxo_modes[i].acim << 0); >+ if( ! write_reg_fxo(wc, port, 30, value) ) goto hell; >+ >+ /* DAA Control 5 - Register 31 */ >+ msleep(1); >+ if( ! read_reg_fxo(wc, port, 31, &value) ) goto hell; >+ >+ value = (value & 0xf7) | (fxo_modes[i].ohs2 << 3); >+ value = value | 0x02; >+ if( ! write_reg_fxo(wc, port, 31, value) ) goto hell; >+ >+ return RET_OK; >+ >+ hell: >+ cardcrit(wc->boardnum, "port %d failed configure vdaa country", port); >+ return RET_FAIL; >+} //}}} >+ >+// Do not call this from an interrupt context, it may sleep. >+static void configure_vdaa_port(struct openpci *wc, int port) >+{ //{{{ >+ /* Set Country - default to Australia */ >+ if( configure_vdaa_country(wc, port, country) ) >+ ++wc->portcount; >+ else { >+ cardcrit(wc->boardnum, "FAILED to configure vdaa port %d. Disabled.", port); >+ wc->porttype[port] = VT_PORT_EMPTY; >+ } >+} //}}} >+ >+static int configure_proslic_country(struct openpci *wc, int port, const char *name) >+{ //{{{ >+ int i; >+ >+ for(i=0; i < sizeof(ps_country_regs)/sizeof(struct ps_country_reg); ++i) { >+ if(!strcmp(ps_country_regs[i].country, name)){ >+ dbginfo(wc->boardnum, "%d: Setting country to %s", port, name); >+ goto part2; >+ } >+ } >+ return -EINVAL; >+ >+ part2: >+ >+ if( ! write_reg_fxs(wc, port, 10, ps_country_regs[i].value) ){ >+ cardcrit(wc->boardnum,"%d: failed to write PS_IMPEDANCE", port); >+ return -EIO; >+ } >+ return 0; >+} //}}} >+ >+// Do not call this from an interrupt context, it may sleep. >+static void configure_proslic_port(struct openpci *wc, int port) >+{ //{{{ >+ /* Set Country - default to Australia */ >+ switch( configure_proslic_country(wc, port, country) ){ >+ case 0: >+ break; >+ >+ case -EINVAL: >+ cardwarn(wc->boardnum,"%d: Country '%s' unknown, using default", port, country); >+ if( configure_proslic_country(wc, port, DEFAULT_COUNTRY) == 0 ) >+ goto hell; >+ >+ default: >+ goto hell; >+ } >+ >+ ++wc->portcount; >+ return; >+ >+ hell: >+ cardcrit(wc->boardnum, "FAILED to configure proslic port %d. Disabled.", port); >+ wc->porttype[port] = VT_PORT_EMPTY; >+} //}}} >+ >+// Do not call this from an interrupt context, it may (indirectly) sleep. >+static int configure_ports(struct openpci *wc) >+{ //{{{ >+ unsigned long flags; >+ int i; >+ >+ wc->portcount = 0; >+ for(i=0; i < MAX_PORTS; ++i){ >+ switch (wc->porttype[i]){ >+ case VT_PORT_VDAA: configure_vdaa_port(wc,i); break; >+ case VT_PORT_PROSLIC: configure_proslic_port(wc,i); break; >+ } >+ } >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ outb(0x2c, PIB(1)); HTXF_WAIT_LOCKED(); >+ outb(0xff, PIB(1)); HTXF_WAIT_LOCKED(); >+ spin_unlock_irqrestore(&wc->lock, flags); >+ >+ // otherwise we 'succeed' if any ports were configured successfully. >+ return wc->portcount ? RET_OK : RET_FAIL; >+} //}}} >+ >+static int __get_arm_id(struct openpci *wc, int field, char *value) >+{ //{{{ >+ int i; >+ int x=0; >+ int count=0; >+ >+ outb(0x01, PIB(1)); HTXF_WAIT(); >+ outb(field, PIB(1)); HTXF_WAIT(); >+ HRXF_WAIT(); count = inb(PIB(1)); >+ if (count > ID_DATA_MAXSIZE){ >+ cardcrit(wc->boardnum, "Too many bytes of id(%d) data %d/%d", >+ field, count, ID_DATA_MAXSIZE); >+ return RET_FAIL; >+ } >+ //cardinfo(wc->boardnum, "get_arm_id(%d): byte count %d",field,count); >+ for(; x < count; ++x){ >+ HRXF_WAIT(); *value = inb(PIB(1)); >+ //cardinfo(wc->boardnum, "get_arm_id(%d): byte %d => 0x%02x",field,x,tmp); >+ ++value; >+ } >+ return RET_OK; >+} //}}} >+ >+static void enable_interrupts(struct openpci *wc) >+{ //{{{ >+ outb(0x3f, TJ_MASK0); >+ outb(0x02, TJ_MASK1); >+} //}}} >+ >+static void disable_interrupts(struct openpci *wc) >+{ //{{{ >+ outb(0x00, TJ_MASK0); >+ outb(0x00, TJ_MASK1); >+} //}}} >+ >+// Do not call this from an interrupt context, it may sleep. >+static int check_arm(struct openpci *wc) >+{ //{{{ >+ char model[ID_DATA_MAXSIZE+1] = { 0 }; >+ char date[ID_DATA_MAXSIZE+1] = { 0 }; >+ unsigned long flags; >+ int i=0; >+ int tmp=0; >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ while ((tmp != 0x88)&&(++i<100)){ >+ outb(0x88, PIB(0)); >+ msleep(1); >+ tmp = inb(PIB(1)); >+ } >+ if (i>=1000) goto limbo; >+ dbginfo(wc->boardnum, "Arm responded on attempt %d",i); >+ >+ // Flush out the queue if we sent several pings before a response. >+ if(i>1) __ping_arm(wc); >+ >+ if( ! __get_arm_id(wc, 0, model) ) goto hell; >+ sscanf(model, "OpenPCI8.%02d", &(wc->firmware)); >+ cardinfo(wc->boardnum, " model: %s", model); >+ >+ if( ! __get_arm_id(wc, 1, date) ) goto hell; >+ cardinfo(wc->boardnum, " date: %s", date); >+ >+ if( ! __get_arm_id(wc, 2, wc->serial) ) goto hell; >+ cardinfo(wc->boardnum, " serial: %s", wc->serial); >+ >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_OK; >+ >+ hell: >+ spin_unlock_irqrestore(&wc->lock, flags); >+ cardwarn(wc->boardnum, "Found ARM processor, dumb firmware."); >+ return RET_OK; >+ >+ limbo: >+ spin_unlock_irqrestore(&wc->lock, flags); >+ return RET_FAIL; >+} //}}} >+ >+static int arm_monitor(struct openpci *wc, int on) >+{ //{{{ >+ int i; >+ outb( on ? 0x06 : 0x07, PIB(1) ); HTXF_WAIT(); >+ return RET_OK; >+} //}}} >+ >+static int __devinit openpci_probe_board(struct pci_dev *pdev, const struct pci_device_id *ent) >+{ //{{{ >+ struct openpci *wc; >+ int boardnum = 0; >+ int failret = -ENOMEM; >+ int tmp = 0; >+ int i; >+ unsigned long flags; >+ >+ if( ent->driver_data != (kernel_ulong_t)&wcopenpci ) >+ { >+ info("Probe of non-OpenPCI card, ignoring."); >+ return -EINVAL; >+ } >+ wc = kzalloc(sizeof(struct openpci), GFP_KERNEL); >+ if (!wc){ >+ return -ENOMEM; >+ } >+ >+ mutex_lock(&cards_mutex); >+ for (; boardnum < MAX_CARDS && cards[boardnum]; ++boardnum); >+ if (boardnum >= MAX_CARDS){ >+ crit("Too many OpenPCI cards(%d), max is %d.", boardnum, MAX_CARDS); >+ mutex_unlock(&cards_mutex); >+ goto hell; >+ } >+ cards[boardnum] = wc; >+ mutex_unlock(&cards_mutex); >+ >+ spin_lock_init(&wc->lock); >+ pci_set_drvdata(pdev, wc); >+ >+ wc->boardnum = boardnum; >+ wc->dev = pdev; >+ wc->variety = wcopenpci; >+ >+ cardinfo(boardnum, "Initialising card"); >+ if (pci_enable_device(pdev)) { >+ failret = -EIO; >+ goto hell_2; >+ } >+ wc->ioaddr = pci_resource_start(pdev, 0); >+ if( ! request_region(wc->ioaddr, 0xff, NAME) ){ >+ cardcrit(boardnum, "Failed to lock IO region, another driver already using it"); >+ failret = -EBUSY; >+ goto hell_2; >+ } >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ outb(0xff, TJ_AUXD); /* Set up TJ to access the ARM */ >+ outb(0x78, TJ_AUXC); /* Set up for Jtag */ >+ outb(0x00, TJ_CNTL); /* pull ERST low */ >+ spin_unlock_irqrestore(&wc->lock, flags); >+ msleep(1); /* Wait a bit */ >+ >+ dbginfo(boardnum,"Starting ARM"); >+ spin_lock_irqsave(&wc->lock, flags); >+ outb(0x01, TJ_CNTL); /* pull ERST high again */ >+ spin_unlock_irqrestore(&wc->lock, flags); >+ msleep(100); /* Give it all a chance to boot */ >+ >+ if( ! check_arm(wc) ){ >+ cardcrit(boardnum, "Couldnt find ARM processor"); >+ failret = -EIO; >+ goto hell_3; >+ } >+ if( wc->firmware < 11 ){ >+ cardcrit(boardnum, >+ "Firmware version %d not supported by this driver", >+ wc->firmware); >+ cardcrit(boardnum, " contact Voicetronix to have it updated"); >+ failret = -ENODEV; >+ goto hell_3; >+ } >+ if( ! check_ports(wc) ){ >+ cardcrit(boardnum, "Couldnt find ports!"); >+ failret = -EIO; >+ goto hell_3; >+ } >+ >+ wc->writechunk = pci_alloc_consistent(pdev, VT_PCIDMA_BLOCKSIZE, &wc->writedma); >+ if (!wc->writechunk) { >+ cardcrit(boardnum, "Couldnt get DMA memory."); >+ goto hell_3; >+ } >+ wc->readchunk = wc->writechunk + DAHDI_MAX_CHUNKSIZE * (MAX_PORTS*2 / sizeof(int)); >+ wc->readdma = wc->writedma + DAHDI_MAX_CHUNKSIZE * (MAX_PORTS*2); >+ >+ memset((void*)wc->writechunk,0,VT_PCIDMA_BLOCKSIZE); >+ >+ spin_lock_irqsave(&wc->lock, flags); >+ outb(0xc1, TJ_SERCTL); >+ outb(0x0, TJ_FSCDELAY); >+ >+ outl(wc->writedma, TJ_DMAWS); >+ outl(wc->writedma + VT_PCIDMA_MIDDLE, TJ_DMAWI); >+ outl(wc->writedma + VT_PCIDMA_END, TJ_DMAWE); >+ outl(wc->readdma, TJ_DMARS); >+ outl(wc->readdma + VT_PCIDMA_MIDDLE, TJ_DMARI); >+ outl(wc->readdma + VT_PCIDMA_END, TJ_DMARE); >+ >+ /* Clear interrupts */ >+ outb(0xff, TJ_INTSTAT); >+ spin_unlock_irqrestore(&wc->lock, flags); >+ >+ if( ! arm_monitor(wc, 1) ){ >+ cardcrit(boardnum, "failed to start arm monitoring"); >+ failret = -EIO; >+ goto hell_4; >+ } >+ msleep(1000); >+ >+ i = 0; >+ while(tmp != 0x88 && ++i < 1000) { >+ outb(0x88, PIB(0)); >+ msleep(250); >+ tmp = inb(PIB(1)); >+ } >+ if(i>=1000) { >+ cardcrit(boardnum, "FAILED to initialise board"); >+ goto hell_4; >+ } >+ >+ if( ! check_ports(wc) ) { >+ cardcrit(boardnum, "FAILED to initialise ports"); >+ failret = -EIO; >+ goto hell_4; >+ } >+ if( ! configure_ports(wc) ){ >+ cardcrit(boardnum, "Failed to configure ports."); >+ failret = -EIO; >+ goto hell_4; >+ } >+ cardinfo(wc->boardnum, "have %d configured ports", wc->portcount); >+ >+ if( ! span_initialize(wc) ) { >+ cardcrit(boardnum, "Failed to register with dahdi driver"); >+ failret = -EFAULT; >+ goto hell_4; >+ } >+ >+ /* Finalize signalling */ >+ for (i=0; i < MAX_PORTS; ++i) { >+ if (wc->porttype[i] == VT_PORT_VDAA) >+ wc->chans[i]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS >+ | DAHDI_SIG_CLEAR | DAHDI_SIG_SF; >+ else if (wc->porttype[i] == VT_PORT_PROSLIC) >+ wc->chans[i]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS >+ | DAHDI_SIG_FXOGS | DAHDI_SIG_SF >+ | DAHDI_SIG_CLEAR | DAHDI_SIG_EM; >+ else if (wc->porttype[i]) >+ cardcrit(wc->boardnum, "Port %d has unknown type (%d)", >+ i, wc->porttype[i]); >+ } >+ >+ /* Enable bus mastering */ >+ pci_set_master(pdev); >+ >+ if (request_irq(pdev->irq, openpci_isr, DAHDI_IRQ_SHARED, NAME, wc)) { >+ cardcrit(boardnum, "Cant get IRQ!"); >+ failret = -EIO; >+ goto hell_5; >+ } >+ cardinfo(boardnum, "Got IRQ %d", pdev->irq); >+ >+ enable_interrupts(wc); >+ start_dma(wc); >+ >+ cardinfo(boardnum,"Initialised card."); >+ return 0; >+ >+ hell_5: >+ dahdi_unregister(&wc->span); >+ hell_4: >+ if (wc->writechunk){ >+ pci_free_consistent(pdev, VT_PCIDMA_BLOCKSIZE, >+ (void*)wc->writechunk, wc->writedma); >+ } >+ hell_3: >+ outb(0x00, TJ_CNTL); >+ release_region(wc->ioaddr, 0xff); >+ hell_2: >+ cards[boardnum] = NULL; >+ hell: >+ kfree(wc); >+ return failret; >+} //}}} >+ >+static void __devexit openpci_remove_board(struct pci_dev *pdev) >+{ //{{{ >+ struct openpci *wc = pci_get_drvdata(pdev); >+ >+ if(!wc) return; >+ >+ arm_monitor(wc,0); >+ >+ /* Stop DMA */ >+ outb(0x00, TJ_OPER); >+ disable_interrupts(wc); >+ >+ //XXX Replace this usecount business... >+ // and do this BEFORE we invalidate everything above... >+ // check that we wont try to write to it in the meantime. >+ /* Release span, possibly delayed */ >+ //XXX if (!wc->usecount) openpci_release(wc); else wc->dead = 1; >+ >+ dahdi_unregister(&wc->span); >+ outb(0x00, TJ_CNTL); >+ >+ pci_free_consistent(pdev, VT_PCIDMA_BLOCKSIZE, (void *)wc->writechunk, wc->writedma); >+ free_irq(pdev->irq, wc); >+ >+ release_region(wc->ioaddr, 0xff); >+ >+ mutex_lock(&cards_mutex); >+ cards[wc->boardnum] = NULL; >+ mutex_unlock(&cards_mutex); >+ >+ kfree(wc); >+ cardinfo(wc->boardnum, "Removed OpenPCI card."); >+} //}}} >+ >+static struct pci_device_id openpci_pci_tbl[] = { >+ { 0xe159, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t) &wcopenpci }, >+ { 0 } >+}; >+ >+MODULE_DEVICE_TABLE(pci, openpci_pci_tbl); >+ >+static struct pci_driver openpci_driver = { >+ name: NAME, >+ probe: openpci_probe_board, >+ remove: __devexit_p(openpci_remove_board), >+ suspend: NULL, >+ resume: NULL, >+ id_table: openpci_pci_tbl, >+}; >+ >+static int __init openpci_init(void) >+{ >+ if( dahdi_pci_module(&openpci_driver) ) >+ return -ENODEV; >+ >+ info("Module loaded %s", debug ? "with debug enabled" : ""); >+ return 0; >+} >+ >+static void __exit openpci_cleanup(void) >+{ >+ pci_unregister_driver(&openpci_driver); >+ info("Module exit"); >+} >+ >+module_init(openpci_init); >+module_exit(openpci_cleanup); >+ >+MODULE_DESCRIPTION(DRIVER_DESCRIPTION); >+MODULE_AUTHOR(DRIVER_AUTHOR); >+MODULE_VERSION(DAHDI_VERSION); >+MODULE_LICENSE("GPL"); >+ >diff --git a/drivers/dahdi/zaphfc/Kbuild b/drivers/dahdi/zaphfc/Kbuild >new file mode 100644 >index 0000000..960fb3a >--- /dev/null >+++ b/drivers/dahdi/zaphfc/Kbuild >@@ -0,0 +1,10 @@ >+obj-m += zaphfc.o >+ >+EXTRA_CFLAGS := -I$(src)/.. -Wno-undef >+ >+zaphfc-objs := base.o fifo.o >+ >+$(obj)/base.o: $(src)/zaphfc.h >+$(obj)/fifo.o: $(src)/fifo.h >+ >+ >diff --git a/drivers/dahdi/zaphfc/base.c b/drivers/dahdi/zaphfc/base.c >new file mode 100644 >index 0000000..841454c >--- /dev/null >+++ b/drivers/dahdi/zaphfc/base.c >@@ -0,0 +1,1710 @@ >+/* >+ * zaphfc.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) 2009, Jose A. Deniz >+ * Copyright (C) 2006, headiisue GmbH; Jens Wilke >+ * Copyright (C) 2004 Daniele Orlandi >+ * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH >+ * >+ * Jens Wilke <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> >+#include <linux/sched.h> >+#include <linux/proc_fs.h> >+#include <linux/if_arp.h> >+ >+#include <dahdi/kernel.h> >+ >+#include "zaphfc.h" >+#include "fifo.h" >+ >+#if CONFIG_PCI >+ >+#define DAHDI_B1 0 >+#define DAHDI_B2 1 >+#define DAHDI_D 2 >+ >+#define D 0 >+#define B1 1 >+#define B2 2 >+ >+/* >+ * 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_zaphfc_dir; >+ >+#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 struct pci_device_id hfc_pci_ids[] = { >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_3069, >+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, >+ {0,} >+}; >+ >+MODULE_DEVICE_TABLE(pci, hfc_pci_ids); >+ >+static int __devinit hfc_probe(struct pci_dev *dev >+ , const struct pci_device_id *ent); >+static void __devexit hfc_remove(struct pci_dev *dev); >+ >+static struct pci_driver hfc_driver = { >+ .name = hfc_DRIVER_NAME, >+ .id_table = hfc_pci_ids, >+ .probe = hfc_probe, >+ .remove = hfc_remove, >+}; >+ >+/****************************************** >+ * HW routines >+ ******************************************/ >+ >+static void hfc_softreset(struct hfc_card *card) >+{ >+ printk(KERN_INFO hfc_DRIVER_PREFIX >+ "card %d: " >+ "resetting\n", >+ card->cardnum); >+ >+/* >+ * 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_zap_open(struct dahdi_chan *zaptel_chan) >+{ >+ struct hfc_chan_duplex *chan = zaptel_chan->pvt; >+ struct hfc_card *card = chan->card; >+ >+ spin_lock(&chan->lock); >+ >+ switch (chan->number) { >+ case D: >+ if (chan->status != free && >+ chan->status != open_framed) { >+ spin_unlock(&chan->lock); >+ return -EBUSY; >+ } >+ chan->status = open_framed; >+ break; >+ >+ case B1: >+ case B2: >+ if (chan->status != free) { >+ spin_unlock(&chan->lock); >+ return -EBUSY; >+ } >+ chan->status = open_voice; >+ break; >+ } >+ >+ chan->open_by_zaptel = TRUE; >+ try_module_get(THIS_MODULE); >+ spin_unlock(&chan->lock); >+ >+ switch (chan->number) { >+ case D: >+ break; >+ >+ case B1: >+ 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, >+ zaptel_chan->name); >+ >+ return 0; >+} >+ >+static int hfc_zap_close(struct dahdi_chan *zaptel_chan) >+{ >+ struct hfc_chan_duplex *chan = zaptel_chan->pvt; >+ struct hfc_card *card = chan->card; >+ >+ if (!card) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "hfc_zap_close called with NULL card\n"); >+ return -1; >+ } >+ >+ spin_lock(&chan->lock); >+ >+ if (chan->status == free) { >+ spin_unlock(&chan->lock); >+ return -EINVAL; >+ } >+ >+ chan->status = free; >+ chan->open_by_zaptel = FALSE; >+ >+ spin_unlock(&chan->lock); >+ >+ switch (chan->number) { >+ case D: >+ break; >+ >+ case B1: >+ 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, >+ zaptel_chan->name); >+ >+ return 0; >+} >+ >+static int hfc_zap_rbsbits(struct dahdi_chan *chan, int bits) >+{ >+ return 0; >+} >+ >+static int hfc_zap_ioctl(struct dahdi_chan *chan, >+ unsigned int cmd, unsigned long data) >+{ >+ switch (cmd) { >+ >+ default: >+ return -ENOTTY; >+ } >+ >+ return 0; >+} >+ >+static void hfc_hdlc_hard_xmit(struct dahdi_chan *d_chan) >+{ >+ struct hfc_chan_duplex *chan = d_chan->pvt; >+ struct hfc_card *card = chan->card; >+ struct dahdi_hfc *hfccard = card->ztdev; >+ >+ atomic_inc(&hfccard->hdlc_pending); >+ >+} >+ >+static int hfc_zap_startup(struct file *file, struct dahdi_span *span) >+{ >+ struct dahdi_hfc *zthfc = dahdi_hfc_from_span(span); >+ struct hfc_card *hfctmp = zthfc->card; >+ int alreadyrunning; >+ >+ if (!hfctmp) { >+ printk(KERN_INFO hfc_DRIVER_PREFIX >+ "card %d: " >+ "no card for span at startup!\n", >+ hfctmp->cardnum); >+ } >+ >+ alreadyrunning = span->flags & DAHDI_FLAG_RUNNING; >+ >+ if (!alreadyrunning) >+ span->flags |= DAHDI_FLAG_RUNNING; >+ >+ return 0; >+} >+ >+static int hfc_zap_shutdown(struct dahdi_span *span) >+{ >+ return 0; >+} >+ >+static int hfc_zap_maint(struct dahdi_span *span, int cmd) >+{ >+ return 0; >+} >+ >+static int hfc_zap_chanconfig(struct 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->ztdev; >+ >+ if ((sigtype == DAHDI_SIG_HARDHDLC) && (hfccard->sigchan == d_chan)) { >+ hfccard->sigactive = 0; >+ atomic_set(&hfccard->hdlc_pending, 0); >+ } >+ >+ return 0; >+} >+ >+static int hfc_zap_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_zap_span_ops = { >+ .owner = THIS_MODULE, >+ .chanconfig = hfc_zap_chanconfig, >+ .spanconfig = hfc_zap_spanconfig, >+ .startup = hfc_zap_startup, >+ .shutdown = hfc_zap_shutdown, >+ .maint = hfc_zap_maint, >+ .rbsbits = hfc_zap_rbsbits, >+ .open = hfc_zap_open, >+ .close = hfc_zap_close, >+ .ioctl = hfc_zap_ioctl, >+ .hdlc_hard_xmit = hfc_hdlc_hard_xmit >+}; >+ >+static int hfc_zap_initialize(struct dahdi_hfc *hfccard) >+{ >+ struct hfc_card *hfctmp = hfccard->card; >+ int i; >+ >+ memset(&hfccard->span, 0x0, sizeof(struct dahdi_span)); >+ sprintf(hfccard->span.name, "ZTHFC%d", hfctmp->cardnum + 1); >+ sprintf(hfccard->span.desc, >+ "HFC-S PCI A ISDN card %d [%s] ", >+ hfctmp->cardnum, >+ hfctmp->nt_mode ? "NT" : "TE"); >+ hfccard->span.spantype = hfctmp->nt_mode ? "NT" : "TE"; >+ hfccard->span.manufacturer = "Cologne Chips"; >+ hfccard->span.flags = 0; >+ hfccard->span.ops = &hfc_zap_span_ops; >+ hfccard->span.irq = hfctmp->pcidev->irq; >+ dahdi_copy_string(hfccard->span.devicetype, "HFC-S PCI-A ISDN", >+ sizeof(hfccard->span.devicetype)); >+ sprintf(hfccard->span.location, "PCI Bus %02d Slot %02d", >+ hfctmp->pcidev->bus->number, >+ PCI_SLOT(hfctmp->pcidev->devfn) + 1); >+ hfccard->span.chans = hfccard->_chans; >+ hfccard->span.channels = 3; >+ for (i = 0; i < hfccard->span.channels; i++) >+ hfccard->_chans[i] = &hfccard->chans[i]; >+ hfccard->span.deflaw = DAHDI_LAW_ALAW; >+ hfccard->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_CCS; >+ 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, >+ "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.zaptel_buffer; >+ >+ hfccard->chans[DAHDI_D].writechunk = >+ hfctmp->chans[D].tx.zaptel_buffer; >+ >+ hfccard->chans[DAHDI_D].pvt = &hfctmp->chans[D]; >+ >+ hfccard->chans[DAHDI_B1].readchunk = >+ hfctmp->chans[B1].rx.zaptel_buffer; >+ >+ hfccard->chans[DAHDI_B1].writechunk = >+ hfctmp->chans[B1].tx.zaptel_buffer; >+ >+ hfccard->chans[DAHDI_B1].pvt = &hfctmp->chans[B1]; >+ >+ hfccard->chans[DAHDI_B2].readchunk = >+ hfctmp->chans[B2].rx.zaptel_buffer; >+ >+ hfccard->chans[DAHDI_B2].writechunk = >+ hfctmp->chans[B2].tx.zaptel_buffer; >+ >+ hfccard->chans[DAHDI_B2].pvt = &hfctmp->chans[B2]; >+ >+ if (dahdi_register(&hfccard->span, 0)) { >+ printk(KERN_CRIT "unable to register zaptel device!\n"); >+ return -1; >+ } >+ >+ return 0; >+} >+ >+static void hfc_zap_transmit(struct hfc_chan_simplex *chan) >+{ >+ hfc_fifo_put(chan, chan->zaptel_buffer, DAHDI_CHUNKSIZE); >+} >+ >+static void hfc_zap_receive(struct hfc_chan_simplex *chan) >+{ >+ hfc_fifo_get(chan, chan->zaptel_buffer, DAHDI_CHUNKSIZE); >+} >+ >+/****************************************** >+ * Interrupt Handler >+ ******************************************/ >+ >+static void hfc_handle_timer_interrupt(struct hfc_card *card); >+static void hfc_handle_state_interrupt(struct hfc_card *card); >+static void hfc_handle_processing_interrupt(struct hfc_card *card); >+static void hfc_frame_arrived(struct hfc_chan_duplex *chan); >+static void hfc_handle_voice(struct hfc_card *card); >+ >+#if (KERNEL_VERSION(2, 6, 24) < LINUX_VERSION_CODE) >+static irqreturn_t hfc_interrupt(int irq, void *dev_id) >+#else >+static irqreturn_t hfc_interrupt(int irq, void *dev_id, struct pt_regs *regs) >+#endif >+{ >+ struct hfc_card *card = dev_id; >+ unsigned long flags; >+ u8 status, s1, s2; >+ >+ if (!card) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "spurious interrupt (IRQ %d)\n", >+ irq); >+ return IRQ_NONE; >+ } >+ >+ spin_lock_irqsave(&card->lock, flags); >+ status = hfc_inb(card, hfc_STATUS); >+ if (!(status & hfc_STATUS_ANYINT)) { >+ /* >+ * maybe we are sharing the irq >+ */ >+ spin_unlock_irqrestore(&card->lock, flags); >+ return IRQ_NONE; >+ } >+ >+ /* We used to ingore the IRQ when the card was in processing >+ * state but apparently there is no restriction to access the >+ * card in such state: >+ * >+ * Joerg Ciesielski wrote: >+ * > There is no restriction for the IRQ handler to access >+ * > HFC-S PCI during processing phase. A IRQ latency of 375 us >+ * > is also no problem since there are no interrupt sources in >+ * > HFC-S PCI which must be handled very fast. >+ * > Due to its deep fifos the IRQ latency can be several ms with >+ * > out the risk of loosing data. Even the S/T state interrupts >+ * > must not be handled with a latency less than <5ms. >+ * > >+ * > The processing phase only indicates that HFC-S PCI is >+ * > processing the Fifos as PCI master so that data is read and >+ * > written in the 32k memory window. But there is no restriction >+ * > to access data in the memory window during this time. >+ * >+ * // if (status & hfc_STATUS_PCI_PROC) { >+ * // return IRQ_HANDLED; >+ * // } >+ */ >+ >+ s1 = hfc_inb(card, hfc_INT_S1); >+ s2 = hfc_inb(card, hfc_INT_S2); >+ >+ if (s1 != 0) { >+ if (s1 & hfc_INTS_TIMER) { >+ /* >+ * timer (bit 7) >+ */ >+ hfc_handle_timer_interrupt(card); >+ } >+ >+ if (s1 & hfc_INTS_L1STATE) { >+ /* >+ * state machine (bit 6) >+ */ >+ hfc_handle_state_interrupt(card); >+ } >+ >+ if (s1 & hfc_INTS_DREC) { >+ /* >+ * D chan RX (bit 5) >+ */ >+ hfc_frame_arrived(&card->chans[D]); >+ } >+ >+ if (s1 & hfc_INTS_B1REC) { >+ /* >+ * B1 chan RX (bit 3) >+ */ >+ hfc_frame_arrived(&card->chans[B1]); >+ } >+ >+ if (s1 & hfc_INTS_B2REC) { >+ /* >+ * B2 chan RX (bit 4) >+ */ >+ hfc_frame_arrived(&card->chans[B2]); >+ } >+ >+ if (s1 & hfc_INTS_DTRANS) { >+ /* >+ * D chan TX (bit 2) >+ */ >+ } >+ >+ if (s1 & hfc_INTS_B1TRANS) { >+ /* >+ * B1 chan TX (bit 0) >+ */ >+ } >+ >+ if (s1 & hfc_INTS_B2TRANS) { >+ /* >+ * B2 chan TX (bit 1) >+ */ >+ } >+ >+ } >+ >+ if (s2 != 0) { >+ if (s2 & hfc_M2_PMESEL) { >+ /* >+ * kaboom irq (bit 7) >+ * >+ * CologneChip says: >+ * >+ * the meaning of this fatal error bit is that HFC-S >+ * PCI as PCI master could not access the PCI bus >+ * within 125us to finish its data processing. If this >+ * happens only very seldom it does not cause big >+ * problems but of course some B-channel or D-channel >+ * data will be corrupted due to this event. >+ * >+ * Unfortunately this bit is only set once after the >+ * problem occurs and can only be reseted by a >+ * software reset. That means it is not easily >+ * possible to check how often this fatal error >+ * happens. >+ * >+ */ >+ >+ if (!card->sync_loss_reported) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "card %d: " >+ "sync lost, pci performance too low!\n", >+ card->cardnum); >+ >+ card->sync_loss_reported = TRUE; >+ } >+ } >+ >+ if (s2 & hfc_M2_GCI_MON_REC) { >+ /* >+ * RxR monitor channel (bit 2) >+ */ >+ } >+ >+ if (s2 & hfc_M2_GCI_I_CHG) { >+ /* >+ * GCI I-change (bit 1) >+ */ >+ } >+ >+ if (s2 & hfc_M2_PROC_TRANS) { >+ /* >+ * processing/non-processing transition (bit 0) >+ */ >+ hfc_handle_processing_interrupt(card); >+ } >+ >+ } >+ >+ spin_unlock_irqrestore(&card->lock, flags); >+ >+ return IRQ_HANDLED; >+} >+ >+static void hfc_handle_timer_interrupt(struct hfc_card *card) >+{ >+ if (card->ignore_first_timer_interrupt) { >+ card->ignore_first_timer_interrupt = FALSE; >+ return; >+ } >+ >+ if ((card->nt_mode && card->l1_state == 3) || >+ (!card->nt_mode && card->l1_state == 7)) { >+ >+ card->regs.ctmt &= ~hfc_CTMT_TIMER_MASK; >+ hfc_outb(card, hfc_CTMT, card->regs.ctmt); >+ >+ hfc_resume_fifo(card); >+ } >+} >+ >+static void hfc_handle_state_interrupt(struct hfc_card *card) >+{ >+ u8 new_state = hfc_inb(card, hfc_STATES) & hfc_STATES_STATE_MASK; >+ >+#ifdef DEBUG >+ if (debug_level >= 1) { >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "layer 1 state = %c%d\n", >+ card->cardnum, >+ card->nt_mode ? 'G' : 'F', >+ new_state); >+ } >+#endif >+ >+ if (card->nt_mode) { >+ /* >+ * NT mode >+ */ >+ >+ if (new_state == 3) { >+ /* >+ * fix to G3 state (see specs) >+ */ >+ hfc_outb(card, hfc_STATES, hfc_STATES_LOAD_STATE | 3); >+ } >+ >+ if (new_state == 3 && card->l1_state != 3) >+ hfc_resume_fifo(card); >+ >+ if (new_state != 3 && card->l1_state == 3) >+ hfc_suspend_fifo(card); >+ >+ } else { >+ if (new_state == 3) { >+ /* >+ * Keep L1 up... zaptel & libpri expects >+ * a always up L1... >+ * Enable only when using an unpatched libpri >+ */ >+ >+ if (force_l1_up) { >+ hfc_outb(card, hfc_STATES, >+ hfc_STATES_DO_ACTION | >+ hfc_STATES_ACTIVATE| >+ hfc_STATES_NT_G2_G3); >+ } >+ } >+ >+ if (new_state == 7 && card->l1_state != 7) { >+ /* >+ * TE is now active, schedule FIFO activation after >+ * some time, otherwise the first frames are lost >+ */ >+ >+ card->regs.ctmt |= hfc_CTMT_TIMER_50 | >+ hfc_CTMT_TIMER_CLEAR; >+ hfc_outb(card, hfc_CTMT, card->regs.ctmt); >+ >+ /* >+ * Activating the timer firest an >+ * interrupt immediately, we >+ * obviously need to ignore it >+ */ >+ card->ignore_first_timer_interrupt = TRUE; >+ } >+ >+ if (new_state != 7 && card->l1_state == 7) { >+ /* >+ * TE has become inactive, disable FIFO >+ */ >+ hfc_suspend_fifo(card); >+ } >+ } >+ >+ card->l1_state = new_state; >+} >+ >+static void hfc_handle_processing_interrupt(struct hfc_card *card) >+{ >+ int available_bytes = 0; >+ >+ /* >+ * Synchronize with the first enabled channel >+ */ >+ if (card->regs.fifo_en & hfc_FIFOEN_B1RX) >+ available_bytes = hfc_fifo_used_rx(&card->chans[B1].rx); >+ if (card->regs.fifo_en & hfc_FIFOEN_B2RX) >+ available_bytes = hfc_fifo_used_rx(&card->chans[B2].rx); >+ else >+ available_bytes = -1; >+ >+ if ((available_bytes == -1 && card->ticks == 8) || >+ available_bytes >= DAHDI_CHUNKSIZE + hfc_RX_FIFO_PRELOAD) { >+ card->ticks = 0; >+ >+ if (available_bytes > DAHDI_CHUNKSIZE*2 + hfc_RX_FIFO_PRELOAD) { >+ card->late_irqs++; >+ /* >+ * we are out of sync, clear fifos, jaw >+ */ >+ hfc_clear_fifo_rx(&card->chans[B1].rx); >+ hfc_clear_fifo_tx(&card->chans[B1].tx); >+ hfc_clear_fifo_rx(&card->chans[B2].rx); >+ hfc_clear_fifo_tx(&card->chans[B2].tx); >+ >+#ifdef DEBUG >+ if (debug_level >= 4) { >+ printk(KERN_DEBUG hfc_DRIVER_PREFIX >+ "card %d: " >+ "late IRQ, %d bytes late\n", >+ card->cardnum, >+ available_bytes - >+ (DAHDI_CHUNKSIZE + >+ hfc_RX_FIFO_PRELOAD)); >+ } >+#endif >+ } else { >+ hfc_handle_voice(card); >+ } >+ } >+ >+ card->ticks++; >+} >+ >+ >+static void hfc_handle_voice(struct hfc_card *card) >+{ >+ struct dahdi_hfc *hfccard = card->ztdev; >+ int frame_left, res; >+ unsigned char buf[hfc_HDLC_BUF_LEN]; >+ unsigned int size = sizeof(buf) / sizeof(buf[0]); >+ >+ >+ if (card->chans[B1].status != open_voice && >+ card->chans[B2].status != open_voice) >+ return; >+ >+ dahdi_transmit(&hfccard->span); >+ >+ if (card->regs.fifo_en & hfc_FIFOEN_B1TX) >+ hfc_zap_transmit(&card->chans[B1].tx); >+ if (card->regs.fifo_en & hfc_FIFOEN_B2TX) >+ hfc_zap_transmit(&card->chans[B2].tx); >+ >+ /* >+ * dahdi hdlc frame tx >+ */ >+ >+ if (atomic_read(&hfccard->hdlc_pending)) { >+ hfc_check_l1_up(card); >+ res = dahdi_hdlc_getbuf(hfccard->sigchan, buf, &size); >+ if (size > 0) { >+ hfccard->sigactive = 1; >+ memcpy(card->chans[D].tx.ugly_framebuf + >+ card->chans[D].tx.ugly_framebuf_size, >+ buf, size); >+ card->chans[D].tx.ugly_framebuf_size += size; >+ if (res != 0) { >+ hfc_fifo_put_frame(&card->chans[D].tx, >+ card->chans[D].tx.ugly_framebuf, >+ card->chans[D].tx.ugly_framebuf_size); >+ ++hfccard->frames_out; >+ hfccard->sigactive = 0; >+ card->chans[D].tx.ugly_framebuf_size >+ = 0; >+ atomic_dec(&hfccard->hdlc_pending); >+ } >+ } >+ } >+ /* >+ * dahdi hdlc frame tx done >+ */ >+ >+ if (card->regs.fifo_en & hfc_FIFOEN_B1RX) >+ hfc_zap_receive(&card->chans[B1].rx); >+ else >+ memset(&card->chans[B1].rx.zaptel_buffer, 0x7f, >+ sizeof(card->chans[B1].rx.zaptel_buffer)); >+ >+ if (card->regs.fifo_en & hfc_FIFOEN_B2RX) >+ hfc_zap_receive(&card->chans[B2].rx); >+ else >+ memset(&card->chans[B2].rx.zaptel_buffer, 0x7f, >+ sizeof(card->chans[B1].rx.zaptel_buffer)); >+ >+ /* >+ * Echo cancellation >+ */ >+ dahdi_ec_chunk(&hfccard->chans[DAHDI_B1], >+ card->chans[B1].rx.zaptel_buffer, >+ card->chans[B1].tx.zaptel_buffer); >+ dahdi_ec_chunk(&hfccard->chans[DAHDI_B2], >+ card->chans[B2].rx.zaptel_buffer, >+ card->chans[B2].tx.zaptel_buffer); >+ >+ /* >+ * dahdi hdlc frame rx >+ */ >+ if (hfc_fifo_has_frames(&card->chans[D].rx)) >+ hfc_frame_arrived(&card->chans[D]); >+ >+ if (card->chans[D].rx.ugly_framebuf_size) { >+ frame_left = card->chans[D].rx.ugly_framebuf_size - >+ card->chans[D].rx.ugly_framebuf_off ; >+ if (frame_left > hfc_HDLC_BUF_LEN) { >+ dahdi_hdlc_putbuf(hfccard->sigchan, >+ card->chans[D].rx.ugly_framebuf + >+ card->chans[D].rx.ugly_framebuf_off, >+ hfc_HDLC_BUF_LEN); >+ card->chans[D].rx.ugly_framebuf_off += >+ hfc_HDLC_BUF_LEN; >+ } else { >+ dahdi_hdlc_putbuf(hfccard->sigchan, >+ card->chans[D].rx.ugly_framebuf + >+ card->chans[D].rx.ugly_framebuf_off, >+ frame_left); >+ dahdi_hdlc_finish(hfccard->sigchan); >+ card->chans[D].rx.ugly_framebuf_size = 0; >+ card->chans[D].rx.ugly_framebuf_off = 0; >+ } >+ } >+ /* >+ * 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_zaptel && >+ card->chans[D].rx.ugly_framebuf_size) { >+ >+ /* >+ * We have to wait for Dahdi to transmit the >+ * frame... wait for next time >+ */ >+ >+ break; >+ } >+ >+ skb = dev_alloc_skb(frame_size - 3); >+ >+ if (!skb) { >+ printk(KERN_ERR hfc_DRIVER_PREFIX >+ "card %d: " >+ "chan %s: " >+ "cannot allocate skb: frame dropped\n", >+ card->cardnum, >+ chan->name); >+ >+ hfc_fifo_drop_frame(&chan->rx); >+ >+ >+ continue; >+ } >+ >+ >+ /* >+ * HFC does the checksum >+ */ >+#ifndef CHECKSUM_HW >+ skb->ip_summed = CHECKSUM_COMPLETE; >+#else >+ skb->ip_summed = CHECKSUM_HW; >+#endif >+ >+ if (chan->open_by_zaptel) { >+ card->chans[D].rx.ugly_framebuf_size = frame_size - 1; >+ >+ if (hfc_fifo_get_frame(&card->chans[D].rx, >+ card->chans[D].rx.ugly_framebuf, >+ frame_size - 1) == -1) { >+ dev_kfree_skb(skb); >+ continue; >+ } >+ >+ memcpy(skb_put(skb, frame_size - 3), >+ card->chans[D].rx.ugly_framebuf, >+ frame_size - 3); >+ } else { >+ if (hfc_fifo_get_frame(&chan->rx, >+ skb_put(skb, frame_size - 3), >+ frame_size - 3) == -1) { >+ dev_kfree_skb(skb); >+ continue; >+ } >+ } >+ } >+ >+ if (!antiloop) >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "card %d: " >+ "Infinite loop detected\n", >+ card->cardnum); >+} >+ >+/****************************************** >+ * Module initialization and cleanup >+ ******************************************/ >+ >+static int __devinit hfc_probe(struct pci_dev *pci_dev, >+ const struct pci_device_id *ent) >+{ >+ static int cardnum; >+ int err; >+ int i; >+ >+ struct hfc_card *card = NULL; >+ struct dahdi_hfc *zthfc = NULL; >+ card = kmalloc(sizeof(struct hfc_card), GFP_KERNEL); >+ if (!card) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "unable to kmalloc!\n"); >+ err = -ENOMEM; >+ goto err_alloc_hfccard; >+ } >+ >+ memset(card, 0x00, sizeof(struct hfc_card)); >+ card->cardnum = cardnum; >+ card->pcidev = pci_dev; >+ spin_lock_init(&card->lock); >+ >+ pci_set_drvdata(pci_dev, card); >+ >+ 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 >+ */ >+ >+ zthfc = kmalloc(sizeof(struct dahdi_hfc), GFP_KERNEL); >+ if (!zthfc) { >+ printk(KERN_CRIT hfc_DRIVER_PREFIX >+ "unable to kmalloc!\n"); >+ goto err_request_irq; >+ } >+ memset(zthfc, 0x0, sizeof(struct dahdi_hfc)); >+ >+ zthfc->card = card; >+ hfc_zap_initialize(zthfc); >+ card->ztdev = zthfc; >+ >+ snprintf(card->proc_dir_name, >+ sizeof(card->proc_dir_name), >+ "%d", card->cardnum); >+ card->proc_dir = proc_mkdir(card->proc_dir_name, hfc_proc_zaphfc_dir); >+ 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); >+ >+ hfc_softreset(card); >+ >+ dahdi_unregister(&card->ztdev->span); >+ >+ >+ /* >+ * disable memio and bustmaster >+ */ >+ pci_write_config_word(pci_dev, PCI_COMMAND, 0); >+ >+ remove_proc_entry("bufs", card->proc_dir); >+ remove_proc_entry("fifos", card->proc_dir); >+ remove_proc_entry("info", card->proc_dir); >+ remove_proc_entry(card->proc_dir_name, hfc_proc_zaphfc_dir); >+ >+ free_irq(pci_dev->irq, card); >+ >+ pci_free_consistent(pci_dev, hfc_FIFO_SIZE, >+ card->fifo_mem, card->fifo_bus_mem); >+ >+ iounmap(card->io_mem); >+ >+ pci_release_regions(pci_dev); >+ >+ pci_disable_device(pci_dev); >+ >+ kfree(card); >+} >+ >+/****************************************** >+ * Module stuff >+ ******************************************/ >+ >+static int __init hfc_init_module(void) >+{ >+ int ret; >+ >+ printk(KERN_INFO hfc_DRIVER_PREFIX >+ hfc_DRIVER_STRING " loading\n"); >+ >+#if (KERNEL_VERSION(2, 6, 26) <= LINUX_VERSION_CODE) >+ hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, NULL); >+#else >+ hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, proc_root_driver); >+#endif >+ >+ ret = dahdi_pci_module(&hfc_driver); >+ return ret; >+} >+ >+module_init(hfc_init_module); >+ >+static void __exit hfc_module_exit(void) >+{ >+ pci_unregister_driver(&hfc_driver); >+ >+#if (KERNEL_VERSION(2, 6, 26) <= LINUX_VERSION_CODE) >+ remove_proc_entry(hfc_DRIVER_NAME, NULL); >+#else >+ remove_proc_entry(hfc_DRIVER_NAME, proc_root_driver); >+#endif >+ >+ printk(KERN_INFO hfc_DRIVER_PREFIX >+ hfc_DRIVER_STRING " unloaded\n"); >+} >+ >+module_exit(hfc_module_exit); >+ >+#endif >+ >+MODULE_DESCRIPTION(hfc_DRIVER_DESCR); >+MODULE_AUTHOR("Jens Wilke <jw_vzaphfc@headissue.com>, " >+ "Daniele (Vihai) Orlandi <daniele@orlandi.com>, " >+ "Jose A. Deniz <odicha@hotmail.com>"); >+MODULE_ALIAS("vzaphfc"); >+#ifdef MODULE_LICENSE >+MODULE_LICENSE("GPL"); >+#endif >+ >+ >+module_param(modes, int, 0444); >+ >+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) >+module_param_array(nt_modes, int, &nt_modes_count, 0444); >+#else >+module_param_array(nt_modes, int, nt_modes_count, 0444); >+#endif >+ >+module_param(force_l1_up, int, 0444); >+#ifdef DEBUG >+module_param(debug_level, int, 0444); >+#endif >+ >+MODULE_PARM_DESC(modes, "[Deprecated] bit-mask to configure NT mode"); >+MODULE_PARM_DESC(nt_modes, >+ "Comma-separated list of card IDs to configure in NT mode"); >+MODULE_PARM_DESC(force_l1_up, "Don't allow L1 to go down"); >+#ifdef DEBUG >+MODULE_PARM_DESC(debug_level, "Debug verbosity level"); >+#endif >diff --git a/drivers/dahdi/zaphfc/fifo.c b/drivers/dahdi/zaphfc/fifo.c >new file mode 100644 >index 0000000..8c23fa3 >--- /dev/null >+++ b/drivers/dahdi/zaphfc/fifo.c >@@ -0,0 +1,375 @@ >+/* >+ * 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. >+ * >+ */ >+ >+#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)); >+ } >+} >+ >diff --git a/drivers/dahdi/zaphfc/fifo.h b/drivers/dahdi/zaphfc/fifo.h >new file mode 100644 >index 0000000..1866f30 >--- /dev/null >+++ b/drivers/dahdi/zaphfc/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 "zaphfc.h" >+ >+static inline u16 *Z1_F1(struct hfc_chan_simplex *chan) >+{ >+ return chan->z1_base + (*chan->f1 * 4); >+} >+ >+static inline u16 *Z2_F1(struct hfc_chan_simplex *chan) >+{ >+ return chan->z2_base + (*chan->f1 * 4); >+} >+ >+static inline u16 *Z1_F2(struct hfc_chan_simplex *chan) >+{ >+ return chan->z1_base + (*chan->f2 * 4); >+} >+ >+static inline u16 *Z2_F2(struct hfc_chan_simplex *chan) >+{ >+ return chan->z2_base + (*chan->f2 * 4); >+} >+ >+static inline u16 Z_inc(struct hfc_chan_simplex *chan, u16 z, u16 inc) >+{ >+ /* >+ * declared as u32 in order to manage overflows >+ */ >+ u32 newz = z + inc; >+ if (newz > chan->z_max) >+ newz -= chan->fifo_size; >+ >+ return newz; >+} >+ >+static inline u8 F_inc(struct hfc_chan_simplex *chan, u8 f, u8 inc) >+{ >+ /* >+ * declared as u16 in order to manage overflows >+ */ >+ u16 newf = f + inc; >+ if (newf > chan->f_max) >+ newf -= chan->f_num; >+ >+ return newf; >+} >+ >+static inline u16 hfc_fifo_used_rx(struct hfc_chan_simplex *chan) >+{ >+ return (*Z1_F2(chan) - *Z2_F2(chan) + >+ chan->fifo_size) % chan->fifo_size; >+} >+ >+static inline u16 hfc_fifo_get_frame_size(struct hfc_chan_simplex *chan) >+{ >+ /* >+ * This +1 is needed because in frame mode the available bytes are Z2-Z1+1 >+ * while in transparent mode I wouldn't consider the byte pointed by Z2 to >+ * be available, otherwise, the FIFO would always contain one byte, even >+ * when Z1==Z2 >+ */ >+ >+ return hfc_fifo_used_rx(chan) + 1; >+} >+ >+static inline u8 hfc_fifo_u8(struct hfc_chan_simplex *chan, u16 z) >+{ >+ return *((u8 *)(chan->z_base + z)); >+} >+ >+static inline u16 hfc_fifo_used_tx(struct hfc_chan_simplex *chan) >+{ >+ return (*Z1_F1(chan) - *Z2_F1(chan) + >+ chan->fifo_size) % chan->fifo_size; >+} >+ >+static inline u16 hfc_fifo_free_rx(struct hfc_chan_simplex *chan) >+{ >+ u16 free_bytes = *Z2_F1(chan) - *Z1_F1(chan); >+ >+ if (free_bytes > 0) >+ return free_bytes; >+ else >+ return free_bytes + chan->fifo_size; >+} >+ >+static inline u16 hfc_fifo_free_tx(struct hfc_chan_simplex *chan) >+{ >+ u16 free_bytes = *Z2_F1(chan) - *Z1_F1(chan); >+ >+ if (free_bytes > 0) >+ return free_bytes; >+ else >+ return free_bytes + chan->fifo_size; >+} >+ >+static inline int hfc_fifo_has_frames(struct hfc_chan_simplex *chan) >+{ >+ return *chan->f1 != *chan->f2; >+} >+ >+static inline u8 hfc_fifo_used_frames(struct hfc_chan_simplex *chan) >+{ >+ return (*chan->f1 - *chan->f2 + chan->f_num) % chan->f_num; >+} >+ >+static inline u8 hfc_fifo_free_frames(struct hfc_chan_simplex *chan) >+{ >+ return (*chan->f2 - *chan->f1 + chan->f_num) % chan->f_num; >+} >+ >+int hfc_fifo_get(struct hfc_chan_simplex *chan, void *data, int size); >+void hfc_fifo_put(struct hfc_chan_simplex *chan, void *data, int size); >+void hfc_fifo_drop(struct hfc_chan_simplex *chan, int size); >+int hfc_fifo_get_frame(struct hfc_chan_simplex *chan, void *data, int max_size); >+void hfc_fifo_drop_frame(struct hfc_chan_simplex *chan); >+void hfc_fifo_put_frame(struct hfc_chan_simplex *chan, void *data, int size); >+void hfc_clear_fifo_rx(struct hfc_chan_simplex *chan); >+void hfc_clear_fifo_tx(struct hfc_chan_simplex *chan); >+ >+#endif >diff --git a/drivers/dahdi/zaphfc/zaphfc.h b/drivers/dahdi/zaphfc/zaphfc.h >new file mode 100644 >index 0000000..29dd304 >--- /dev/null >+++ b/drivers/dahdi/zaphfc/zaphfc.h >@@ -0,0 +1,418 @@ >+/* >+ * zaphfc.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 "vzaphfc" >+#define hfc_DRIVER_PREFIX hfc_DRIVER_NAME ": " >+#define hfc_DRIVER_DESCR "HFC-S PCI A ISDN" >+#define hfc_DRIVER_VERSION "1.42" >+#define hfc_DRIVER_STRING hfc_DRIVER_DESCR " (V" hfc_DRIVER_VERSION ")" >+ >+#define hfc_MAX_BOARDS 32 >+ >+#ifndef PCI_DMA_32BIT >+#define PCI_DMA_32BIT 0x00000000ffffffffULL >+#endif >+ >+#ifndef PCI_VENDOR_ID_SITECOM >+#define PCI_VENDOR_ID_SITECOM 0x182D >+#endif >+ >+#ifndef PCI_DEVICE_ID_SITECOM_3069 >+#define PCI_DEVICE_ID_SITECOM_3069 0x3069 >+#endif >+ >+#define hfc_RESET_DELAY 20 >+ >+#define hfc_CLKDEL_TE 0x0f /* CLKDEL in TE mode */ >+#define hfc_CLKDEL_NT 0x6c /* CLKDEL in NT mode */ >+ >+/* PCI memory mapped I/O */ >+ >+#define hfc_PCI_MEM_SIZE 0x0100 >+#define hfc_PCI_MWBA 0x80 >+ >+/* GCI/IOM bus monitor registers */ >+ >+#define hfc_C_I 0x08 >+#define hfc_TRxR 0x0C >+#define hfc_MON1_D 0x28 >+#define hfc_MON2_D 0x2C >+ >+ >+/* GCI/IOM bus timeslot registers */ >+ >+#define hfc_B1_SSL 0x80 >+#define hfc_B2_SSL 0x84 >+#define hfc_AUX1_SSL 0x88 >+#define hfc_AUX2_SSL 0x8C >+#define hfc_B1_RSL 0x90 >+#define hfc_B2_RSL 0x94 >+#define hfc_AUX1_RSL 0x98 >+#define hfc_AUX2_RSL 0x9C >+ >+/* GCI/IOM bus data registers */ >+ >+#define hfc_B1_D 0xA0 >+#define hfc_B2_D 0xA4 >+#define hfc_AUX1_D 0xA8 >+#define hfc_AUX2_D 0xAC >+ >+/* GCI/IOM bus configuration registers */ >+ >+#define hfc_MST_EMOD 0xB4 >+#define hfc_MST_MODE 0xB8 >+#define hfc_CONNECT 0xBC >+ >+ >+/* Interrupt and status registers */ >+ >+#define hfc_FIFO_EN 0x44 >+#define hfc_TRM 0x48 >+#define hfc_B_MODE 0x4C >+#define hfc_CHIP_ID 0x58 >+#define hfc_CIRM 0x60 >+#define hfc_CTMT 0x64 >+#define hfc_INT_M1 0x68 >+#define hfc_INT_M2 0x6C >+#define hfc_INT_S1 0x78 >+#define hfc_INT_S2 0x7C >+#define hfc_STATUS 0x70 >+ >+/* S/T section registers */ >+ >+#define hfc_STATES 0xC0 >+#define hfc_SCTRL 0xC4 >+#define hfc_SCTRL_E 0xC8 >+#define hfc_SCTRL_R 0xCC >+#define hfc_SQ 0xD0 >+#define hfc_CLKDEL 0xDC >+#define hfc_B1_REC 0xF0 >+#define hfc_B1_SEND 0xF0 >+#define hfc_B2_REC 0xF4 >+#define hfc_B2_SEND 0xF4 >+#define hfc_D_REC 0xF8 >+#define hfc_D_SEND 0xF8 >+#define hfc_E_REC 0xFC >+ >+/* Bits and values in various HFC PCI registers */ >+ >+/* bits in status register (READ) */ >+#define hfc_STATUS_PCI_PROC 0x02 >+#define hfc_STATUS_NBUSY 0x04 >+#define hfc_STATUS_TIMER_ELAP 0x10 >+#define hfc_STATUS_STATINT 0x20 >+#define hfc_STATUS_FRAMEINT 0x40 >+#define hfc_STATUS_ANYINT 0x80 >+ >+/* bits in CTMT (Write) */ >+#define hfc_CTMT_TRANSB1 0x01 >+#define hfc_CTMT_TRANSB2 0x02 >+#define hfc_CTMT_TIMER_CLEAR 0x80 >+#define hfc_CTMT_TIMER_MASK 0x1C >+#define hfc_CTMT_TIMER_3_125 (0x01 << 2) >+#define hfc_CTMT_TIMER_6_25 (0x02 << 2) >+#define hfc_CTMT_TIMER_12_5 (0x03 << 2) >+#define hfc_CTMT_TIMER_25 (0x04 << 2) >+#define hfc_CTMT_TIMER_50 (0x05 << 2) >+#define hfc_CTMT_TIMER_400 (0x06 << 2) >+#define hfc_CTMT_TIMER_800 (0x07 << 2) >+#define hfc_CTMT_AUTO_TIMER 0x20 >+ >+/* bits in CIRM (Write) */ >+#define hfc_CIRM_AUX_MSK 0x07 >+#define hfc_CIRM_RESET 0x08 >+#define hfc_CIRM_B1_REV 0x40 >+#define hfc_CIRM_B2_REV 0x80 >+ >+/* bits in INT_M1 and INT_S1 */ >+#define hfc_INTS_B1TRANS 0x01 >+#define hfc_INTS_B2TRANS 0x02 >+#define hfc_INTS_DTRANS 0x04 >+#define hfc_INTS_B1REC 0x08 >+#define hfc_INTS_B2REC 0x10 >+#define hfc_INTS_DREC 0x20 >+#define hfc_INTS_L1STATE 0x40 >+#define hfc_INTS_TIMER 0x80 >+ >+/* bits in INT_M2 */ >+#define hfc_M2_PROC_TRANS 0x01 >+#define hfc_M2_GCI_I_CHG 0x02 >+#define hfc_M2_GCI_MON_REC 0x04 >+#define hfc_M2_IRQ_ENABLE 0x08 >+#define hfc_M2_PMESEL 0x80 >+ >+/* bits in STATES */ >+#define hfc_STATES_STATE_MASK 0x0F >+#define hfc_STATES_LOAD_STATE 0x10 >+#define hfc_STATES_ACTIVATE 0x20 >+#define hfc_STATES_DO_ACTION 0x40 >+#define hfc_STATES_NT_G2_G3 0x80 >+ >+/* bits in HFCD_MST_MODE */ >+#define hfc_MST_MODE_MASTER 0x01 >+#define hfc_MST_MODE_SLAVE 0x00 >+/* remaining bits are for codecs control */ >+ >+/* bits in HFCD_SCTRL */ >+#define hfc_SCTRL_B1_ENA 0x01 >+#define hfc_SCTRL_B2_ENA 0x02 >+#define hfc_SCTRL_MODE_TE 0x00 >+#define hfc_SCTRL_MODE_NT 0x04 >+#define hfc_SCTRL_LOW_PRIO 0x08 >+#define hfc_SCTRL_SQ_ENA 0x10 >+#define hfc_SCTRL_TEST 0x20 >+#define hfc_SCTRL_NONE_CAP 0x40 >+#define hfc_SCTRL_PWR_DOWN 0x80 >+ >+/* bits in SCTRL_E */ >+#define hfc_SCTRL_E_AUTO_AWAKE 0x01 >+#define hfc_SCTRL_E_DBIT_1 0x04 >+#define hfc_SCTRL_E_IGNORE_COL 0x08 >+#define hfc_SCTRL_E_CHG_B1_B2 0x80 >+ >+/* bits in SCTRL_R */ >+#define hfc_SCTRL_R_B1_ENA 0x01 >+#define hfc_SCTRL_R_B2_ENA 0x02 >+ >+/* bits in FIFO_EN register */ >+#define hfc_FIFOEN_B1TX 0x01 >+#define hfc_FIFOEN_B1RX 0x02 >+#define hfc_FIFOEN_B2TX 0x04 >+#define hfc_FIFOEN_B2RX 0x08 >+#define hfc_FIFOEN_DTX 0x10 >+#define hfc_FIFOEN_DRX 0x20 >+ >+#define hfc_FIFOEN_B1 (hfc_FIFOEN_B1TX|hfc_FIFOEN_B1RX) >+#define hfc_FIFOEN_B2 (hfc_FIFOEN_B2TX|hfc_FIFOEN_B2RX) >+#define hfc_FIFOEN_D (hfc_FIFOEN_DTX|hfc_FIFOEN_DRX) >+ >+/* bits in the CONNECT register */ >+#define hfc_CONNECT_B1_HFC_from_ST 0x00 >+#define hfc_CONNECT_B1_HFC_from_GCI 0x01 >+#define hfc_CONNECT_B1_ST_from_HFC 0x00 >+#define hfc_CONNECT_B1_ST_from_GCI 0x02 >+#define hfc_CONNECT_B1_GCI_from_HFC 0x00 >+#define hfc_CONNECT_B1_GCI_from_ST 0x04 >+ >+#define hfc_CONNECT_B2_HFC_from_ST 0x00 >+#define hfc_CONNECT_B2_HFC_from_GCI 0x08 >+#define hfc_CONNECT_B2_ST_from_HFC 0x00 >+#define hfc_CONNECT_B2_ST_from_GCI 0x10 >+#define hfc_CONNECT_B2_GCI_from_HFC 0x00 >+#define hfc_CONNECT_B2_GCI_from_ST 0x20 >+ >+/* bits in the TRM register */ >+#define hfc_TRM_TRANS_INT_00 0x00 >+#define hfc_TRM_TRANS_INT_01 0x01 >+#define hfc_TRM_TRANS_INT_10 0x02 >+#define hfc_TRM_TRANS_INT_11 0x04 >+#define hfc_TRM_ECHO 0x20 >+#define hfc_TRM_B1_PLUS_B2 0x40 >+#define hfc_TRM_IOM_TEST_LOOP 0x80 >+ >+/* bits in the __SSL and __RSL registers */ >+#define hfc_SRSL_STIO 0x40 >+#define hfc_SRSL_ENABLE 0x80 >+#define hfc_SRCL_SLOT_MASK 0x1f >+ >+/* FIFO memory definitions */ >+ >+#define hfc_FIFO_SIZE 0x8000 >+ >+#define hfc_UGLY_FRAMEBUF 0x2000 >+ >+#define hfc_TX_FIFO_PRELOAD (DAHDI_CHUNKSIZE + 2) >+#define hfc_RX_FIFO_PRELOAD 4 >+ >+/* HDLC STUFF */ >+#define hfc_HDLC_BUF_LEN 32 >+/* arbitrary, just the max # of byts we will send to DAHDI per call */ >+ >+ >+/* NOTE: FIFO pointers are not declared volatile because accesses to the >+ * FIFOs are inherently safe. >+ */ >+ >+#ifdef DEBUG >+extern int debug_level; >+#endif >+ >+struct hfc_chan; >+ >+struct hfc_chan_simplex { >+ struct hfc_chan_duplex *chan; >+ >+ u8 zaptel_buffer[DAHDI_CHUNKSIZE]; >+ >+ u8 ugly_framebuf[hfc_UGLY_FRAMEBUF]; >+ int ugly_framebuf_size; >+ u16 ugly_framebuf_off; >+ >+ void *z1_base, *z2_base; >+ void *fifo_base; >+ void *z_base; >+ u16 z_min; >+ u16 z_max; >+ u16 fifo_size; >+ >+ u8 *f1, *f2; >+ u8 f_min; >+ u8 f_max; >+ u8 f_num; >+ >+ unsigned long long frames; >+ unsigned long long bytes; >+ unsigned long long fifo_full; >+ unsigned long long crc; >+ unsigned long long fifo_underrun; >+}; >+ >+enum hfc_chan_status { >+ free, >+ open_framed, >+ open_voice, >+ sniff_aux, >+ loopback, >+}; >+ >+struct hfc_chan_duplex { >+ struct hfc_card *card; >+ >+ char *name; >+ int number; >+ >+ enum hfc_chan_status status; >+ int open_by_netdev; >+ int open_by_zaptel; >+ >+ unsigned short protocol; >+ >+ spinlock_t lock; >+ >+ struct hfc_chan_simplex rx; >+ struct hfc_chan_simplex tx; >+ >+}; >+ >+typedef struct hfc_card { >+ int cardnum; >+ struct pci_dev *pcidev; >+ struct dahdi_hfc *ztdev; >+ struct proc_dir_entry *proc_dir; >+ char proc_dir_name[32]; >+ >+ struct proc_dir_entry *proc_info; >+ struct proc_dir_entry *proc_fifos; >+ struct proc_dir_entry *proc_bufs; >+ >+ unsigned long io_bus_mem; >+ void __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_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 >diff --git a/drivers/staging/echo/Kbuild b/drivers/staging/echo/Kbuild >new file mode 100644 >index 0000000..f813006 >--- /dev/null >+++ b/drivers/staging/echo/Kbuild >@@ -0,0 +1,6 @@ >+ifdef DAHDI_USE_MMX >+EXTRA_CFLAGS += -DUSE_MMX >+endif >+ >+# An explicit 'obj-m' , unlike the Makefile >+obj-m += echo.o >diff --git a/drivers/staging/echo/echo.c b/drivers/staging/echo/echo.c >new file mode 100644 >index 0000000..4cc4f2e >--- /dev/null >+++ b/drivers/staging/echo/echo.c >@@ -0,0 +1,662 @@ >+/* >+ * SpanDSP - a series of DSP components for telephony >+ * >+ * echo.c - A line echo canceller. This code is being developed >+ * against and partially complies with G168. >+ * >+ * Written by Steve Underwood <steveu@coppice.org> >+ * and David Rowe <david_at_rowetel_dot_com> >+ * >+ * Copyright (C) 2001, 2003 Steve Underwood, 2007 David Rowe >+ * >+ * Based on a bit from here, a bit from there, eye of toad, ear of >+ * bat, 15 years of failed attempts by David and a few fried brain >+ * cells. >+ * >+ * All rights reserved. >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License version 2, as >+ * published by the Free Software Foundation. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. >+ */ >+ >+/*! \file */ >+ >+/* Implementation Notes >+ David Rowe >+ April 2007 >+ >+ This code started life as Steve's NLMS algorithm with a tap >+ rotation algorithm to handle divergence during double talk. I >+ added a Geigel Double Talk Detector (DTD) [2] and performed some >+ G168 tests. However I had trouble meeting the G168 requirements, >+ especially for double talk - there were always cases where my DTD >+ failed, for example where near end speech was under the 6dB >+ threshold required for declaring double talk. >+ >+ So I tried a two path algorithm [1], which has so far given better >+ results. The original tap rotation/Geigel algorithm is available >+ in SVN http://svn.rowetel.com/software/oslec/tags/before_16bit. >+ It's probably possible to make it work if some one wants to put some >+ serious work into it. >+ >+ At present no special treatment is provided for tones, which >+ generally cause NLMS algorithms to diverge. Initial runs of a >+ subset of the G168 tests for tones (e.g ./echo_test 6) show the >+ current algorithm is passing OK, which is kind of surprising. The >+ full set of tests needs to be performed to confirm this result. >+ >+ One other interesting change is that I have managed to get the NLMS >+ code to work with 16 bit coefficients, rather than the original 32 >+ bit coefficents. This reduces the MIPs and storage required. >+ I evaulated the 16 bit port using g168_tests.sh and listening tests >+ on 4 real-world samples. >+ >+ I also attempted the implementation of a block based NLMS update >+ [2] but although this passes g168_tests.sh it didn't converge well >+ on the real-world samples. I have no idea why, perhaps a scaling >+ problem. The block based code is also available in SVN >+ http://svn.rowetel.com/software/oslec/tags/before_16bit. If this >+ code can be debugged, it will lead to further reduction in MIPS, as >+ the block update code maps nicely onto DSP instruction sets (it's a >+ dot product) compared to the current sample-by-sample update. >+ >+ Steve also has some nice notes on echo cancellers in echo.h >+ >+ References: >+ >+ [1] Ochiai, Areseki, and Ogihara, "Echo Canceller with Two Echo >+ Path Models", IEEE Transactions on communications, COM-25, >+ No. 6, June >+ 1977. >+ http://www.rowetel.com/images/echo/dual_path_paper.pdf >+ >+ [2] The classic, very useful paper that tells you how to >+ actually build a real world echo canceller: >+ Messerschmitt, Hedberg, Cole, Haoui, Winship, "Digital Voice >+ Echo Canceller with a TMS320020, >+ http://www.rowetel.com/images/echo/spra129.pdf >+ >+ [3] I have written a series of blog posts on this work, here is >+ Part 1: http://www.rowetel.com/blog/?p=18 >+ >+ [4] The source code http://svn.rowetel.com/software/oslec/ >+ >+ [5] A nice reference on LMS filters: >+ http://en.wikipedia.org/wiki/Least_mean_squares_filter >+ >+ Credits: >+ >+ Thanks to Steve Underwood, Jean-Marc Valin, and Ramakrishnan >+ Muthukrishnan for their suggestions and email discussions. Thanks >+ also to those people who collected echo samples for me such as >+ Mark, Pawel, and Pavel. >+*/ >+ >+#include <linux/kernel.h> >+#include <linux/module.h> >+#include <linux/slab.h> >+ >+#include "echo.h" >+ >+#define MIN_TX_POWER_FOR_ADAPTION 64 >+#define MIN_RX_POWER_FOR_ADAPTION 64 >+#define DTD_HANGOVER 600 /* 600 samples, or 75ms */ >+#define DC_LOG2BETA 3 /* log2() of DC filter Beta */ >+ >+ >+/* adapting coeffs using the traditional stochastic descent (N)LMS algorithm */ >+ >+#ifdef __bfin__ >+static inline void lms_adapt_bg(struct oslec_state *ec, int clean, >+ int shift) >+{ >+ int i, j; >+ int offset1; >+ int offset2; >+ int factor; >+ int exp; >+ int16_t *phist; >+ int n; >+ >+ if (shift > 0) >+ factor = clean << shift; >+ else >+ factor = clean >> -shift; >+ >+ /* Update the FIR taps */ >+ >+ offset2 = ec->curr_pos; >+ offset1 = ec->taps - offset2; >+ phist = &ec->fir_state_bg.history[offset2]; >+ >+ /* st: and en: help us locate the assembler in echo.s */ >+ >+ /* asm("st:"); */ >+ n = ec->taps; >+ for (i = 0, j = offset2; i < n; i++, j++) { >+ exp = *phist++ * factor; >+ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15); >+ } >+ /* asm("en:"); */ >+ >+ /* Note the asm for the inner loop above generated by Blackfin gcc >+ 4.1.1 is pretty good (note even parallel instructions used): >+ >+ R0 = W [P0++] (X); >+ R0 *= R2; >+ R0 = R0 + R3 (NS) || >+ R1 = W [P1] (X) || >+ nop; >+ R0 >>>= 15; >+ R0 = R0 + R1; >+ W [P1++] = R0; >+ >+ A block based update algorithm would be much faster but the >+ above can't be improved on much. Every instruction saved in >+ the loop above is 2 MIPs/ch! The for loop above is where the >+ Blackfin spends most of it's time - about 17 MIPs/ch measured >+ with speedtest.c with 256 taps (32ms). Write-back and >+ Write-through cache gave about the same performance. >+ */ >+} >+ >+/* >+ IDEAS for further optimisation of lms_adapt_bg(): >+ >+ 1/ The rounding is quite costly. Could we keep as 32 bit coeffs >+ then make filter pluck the MS 16-bits of the coeffs when filtering? >+ However this would lower potential optimisation of filter, as I >+ think the dual-MAC architecture requires packed 16 bit coeffs. >+ >+ 2/ Block based update would be more efficient, as per comments above, >+ could use dual MAC architecture. >+ >+ 3/ Look for same sample Blackfin LMS code, see if we can get dual-MAC >+ packing. >+ >+ 4/ Execute the whole e/c in a block of say 20ms rather than sample >+ by sample. Processing a few samples every ms is inefficient. >+*/ >+ >+#else >+static inline void lms_adapt_bg(struct oslec_state *ec, int clean, >+ int shift) >+{ >+ int i; >+ >+ int offset1; >+ int offset2; >+ int factor; >+ int exp; >+ >+ if (shift > 0) >+ factor = clean << shift; >+ else >+ factor = clean >> -shift; >+ >+ /* Update the FIR taps */ >+ >+ offset2 = ec->curr_pos; >+ offset1 = ec->taps - offset2; >+ >+ for (i = ec->taps - 1; i >= offset1; i--) { >+ exp = (ec->fir_state_bg.history[i - offset1] * factor); >+ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15); >+ } >+ for (; i >= 0; i--) { >+ exp = (ec->fir_state_bg.history[i + offset2] * factor); >+ ec->fir_taps16[1][i] += (int16_t) ((exp + (1 << 14)) >> 15); >+ } >+} >+#endif >+ >+static inline int top_bit(unsigned int bits) >+{ >+ if (bits == 0) >+ return -1; >+ else >+ return (int)fls((int32_t)bits)-1; >+} >+ >+struct oslec_state *oslec_create(int len, int adaption_mode) >+{ >+ struct oslec_state *ec; >+ int i; >+ >+ ec = kzalloc(sizeof(*ec), GFP_KERNEL); >+ if (!ec) >+ return NULL; >+ >+ ec->taps = len; >+ ec->log2taps = top_bit(len); >+ ec->curr_pos = ec->taps - 1; >+ >+ for (i = 0; i < 2; i++) { >+ ec->fir_taps16[i] = >+ kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); >+ if (!ec->fir_taps16[i]) >+ goto error_oom; >+ } >+ >+ fir16_create(&ec->fir_state, ec->fir_taps16[0], ec->taps); >+ fir16_create(&ec->fir_state_bg, ec->fir_taps16[1], ec->taps); >+ >+ for (i = 0; i < 5; i++) >+ ec->xvtx[i] = ec->yvtx[i] = ec->xvrx[i] = ec->yvrx[i] = 0; >+ >+ ec->cng_level = 1000; >+ oslec_adaption_mode(ec, adaption_mode); >+ >+ ec->snapshot = kcalloc(ec->taps, sizeof(int16_t), GFP_KERNEL); >+ if (!ec->snapshot) >+ goto error_oom; >+ >+ ec->cond_met = 0; >+ ec->Pstates = 0; >+ ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0; >+ ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0; >+ ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0; >+ ec->Lbgn = ec->Lbgn_acc = 0; >+ ec->Lbgn_upper = 200; >+ ec->Lbgn_upper_acc = ec->Lbgn_upper << 13; >+ >+ return ec; >+ >+error_oom: >+ for (i = 0; i < 2; i++) >+ kfree(ec->fir_taps16[i]); >+ >+ kfree(ec); >+ return NULL; >+} >+EXPORT_SYMBOL_GPL(oslec_create); >+ >+void oslec_free(struct oslec_state *ec) >+{ >+ int i; >+ >+ fir16_free(&ec->fir_state); >+ fir16_free(&ec->fir_state_bg); >+ for (i = 0; i < 2; i++) >+ kfree(ec->fir_taps16[i]); >+ kfree(ec->snapshot); >+ kfree(ec); >+} >+EXPORT_SYMBOL_GPL(oslec_free); >+ >+void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode) >+{ >+ ec->adaption_mode = adaption_mode; >+} >+EXPORT_SYMBOL_GPL(oslec_adaption_mode); >+ >+void oslec_flush(struct oslec_state *ec) >+{ >+ int i; >+ >+ ec->Ltxacc = ec->Lrxacc = ec->Lcleanacc = ec->Lclean_bgacc = 0; >+ ec->Ltx = ec->Lrx = ec->Lclean = ec->Lclean_bg = 0; >+ ec->tx_1 = ec->tx_2 = ec->rx_1 = ec->rx_2 = 0; >+ >+ ec->Lbgn = ec->Lbgn_acc = 0; >+ ec->Lbgn_upper = 200; >+ ec->Lbgn_upper_acc = ec->Lbgn_upper << 13; >+ >+ ec->nonupdate_dwell = 0; >+ >+ fir16_flush(&ec->fir_state); >+ fir16_flush(&ec->fir_state_bg); >+ ec->fir_state.curr_pos = ec->taps - 1; >+ ec->fir_state_bg.curr_pos = ec->taps - 1; >+ for (i = 0; i < 2; i++) >+ memset(ec->fir_taps16[i], 0, ec->taps * sizeof(int16_t)); >+ >+ ec->curr_pos = ec->taps - 1; >+ ec->Pstates = 0; >+} >+EXPORT_SYMBOL_GPL(oslec_flush); >+ >+void oslec_snapshot(struct oslec_state *ec) >+{ >+ memcpy(ec->snapshot, ec->fir_taps16[0], ec->taps * sizeof(int16_t)); >+} >+EXPORT_SYMBOL_GPL(oslec_snapshot); >+ >+/* Dual Path Echo Canceller */ >+ >+int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx) >+{ >+ int32_t echo_value; >+ int clean_bg; >+ int tmp, tmp1; >+ >+ /* >+ * Input scaling was found be required to prevent problems when tx >+ * starts clipping. Another possible way to handle this would be the >+ * filter coefficent scaling. >+ */ >+ >+ ec->tx = tx; >+ ec->rx = rx; >+ tx >>= 1; >+ rx >>= 1; >+ >+ /* >+ * Filter DC, 3dB point is 160Hz (I think), note 32 bit precision >+ * required otherwise values do not track down to 0. Zero at DC, Pole >+ * at (1-Beta) on real axis. Some chip sets (like Si labs) don't >+ * need this, but something like a $10 X100P card does. Any DC really >+ * slows down convergence. >+ * >+ * Note: removes some low frequency from the signal, this reduces the >+ * speech quality when listening to samples through headphones but may >+ * not be obvious through a telephone handset. >+ * >+ * Note that the 3dB frequency in radians is approx Beta, e.g. for Beta >+ * = 2^(-3) = 0.125, 3dB freq is 0.125 rads = 159Hz. >+ */ >+ >+ if (ec->adaption_mode & ECHO_CAN_USE_RX_HPF) { >+ tmp = rx << 15; >+ >+ /* >+ * Make sure the gain of the HPF is 1.0. This can still >+ * saturate a little under impulse conditions, and it might >+ * roll to 32768 and need clipping on sustained peak level >+ * signals. However, the scale of such clipping is small, and >+ * the error due to any saturation should not markedly affect >+ * the downstream processing. >+ */ >+ tmp -= (tmp >> 4); >+ >+ ec->rx_1 += -(ec->rx_1 >> DC_LOG2BETA) + tmp - ec->rx_2; >+ >+ /* >+ * hard limit filter to prevent clipping. Note that at this >+ * stage rx should be limited to +/- 16383 due to right shift >+ * above >+ */ >+ tmp1 = ec->rx_1 >> 15; >+ if (tmp1 > 16383) >+ tmp1 = 16383; >+ if (tmp1 < -16383) >+ tmp1 = -16383; >+ rx = tmp1; >+ ec->rx_2 = tmp; >+ } >+ >+ /* Block average of power in the filter states. Used for >+ adaption power calculation. */ >+ >+ { >+ int new, old; >+ >+ /* efficient "out with the old and in with the new" algorithm so >+ we don't have to recalculate over the whole block of >+ samples. */ >+ new = (int)tx * (int)tx; >+ old = (int)ec->fir_state.history[ec->fir_state.curr_pos] * >+ (int)ec->fir_state.history[ec->fir_state.curr_pos]; >+ ec->Pstates += >+ ((new - old) + (1 << (ec->log2taps-1))) >> ec->log2taps; >+ if (ec->Pstates < 0) >+ ec->Pstates = 0; >+ } >+ >+ /* Calculate short term average levels using simple single pole IIRs */ >+ >+ ec->Ltxacc += abs(tx) - ec->Ltx; >+ ec->Ltx = (ec->Ltxacc + (1 << 4)) >> 5; >+ ec->Lrxacc += abs(rx) - ec->Lrx; >+ ec->Lrx = (ec->Lrxacc + (1 << 4)) >> 5; >+ >+ /* Foreground filter */ >+ >+ ec->fir_state.coeffs = ec->fir_taps16[0]; >+ echo_value = fir16(&ec->fir_state, tx); >+ ec->clean = rx - echo_value; >+ ec->Lcleanacc += abs(ec->clean) - ec->Lclean; >+ ec->Lclean = (ec->Lcleanacc + (1 << 4)) >> 5; >+ >+ /* Background filter */ >+ >+ echo_value = fir16(&ec->fir_state_bg, tx); >+ clean_bg = rx - echo_value; >+ ec->Lclean_bgacc += abs(clean_bg) - ec->Lclean_bg; >+ ec->Lclean_bg = (ec->Lclean_bgacc + (1 << 4)) >> 5; >+ >+ /* Background Filter adaption */ >+ >+ /* Almost always adap bg filter, just simple DT and energy >+ detection to minimise adaption in cases of strong double talk. >+ However this is not critical for the dual path algorithm. >+ */ >+ ec->factor = 0; >+ ec->shift = 0; >+ if ((ec->nonupdate_dwell == 0)) { >+ int P, logP, shift; >+ >+ /* Determine: >+ >+ f = Beta * clean_bg_rx/P ------ (1) >+ >+ where P is the total power in the filter states. >+ >+ The Boffins have shown that if we obey (1) we converge >+ quickly and avoid instability. >+ >+ The correct factor f must be in Q30, as this is the fixed >+ point format required by the lms_adapt_bg() function, >+ therefore the scaled version of (1) is: >+ >+ (2^30) * f = (2^30) * Beta * clean_bg_rx/P >+ factor = (2^30) * Beta * clean_bg_rx/P ----- (2) >+ >+ We have chosen Beta = 0.25 by experiment, so: >+ >+ factor = (2^30) * (2^-2) * clean_bg_rx/P >+ >+ (30 - 2 - log2(P)) >+ factor = clean_bg_rx 2 ----- (3) >+ >+ To avoid a divide we approximate log2(P) as top_bit(P), >+ which returns the position of the highest non-zero bit in >+ P. This approximation introduces an error as large as a >+ factor of 2, but the algorithm seems to handle it OK. >+ >+ Come to think of it a divide may not be a big deal on a >+ modern DSP, so its probably worth checking out the cycles >+ for a divide versus a top_bit() implementation. >+ */ >+ >+ P = MIN_TX_POWER_FOR_ADAPTION + ec->Pstates; >+ logP = top_bit(P) + ec->log2taps; >+ shift = 30 - 2 - logP; >+ ec->shift = shift; >+ >+ lms_adapt_bg(ec, clean_bg, shift); >+ } >+ >+ /* very simple DTD to make sure we dont try and adapt with strong >+ near end speech */ >+ >+ ec->adapt = 0; >+ if ((ec->Lrx > MIN_RX_POWER_FOR_ADAPTION) && (ec->Lrx > ec->Ltx)) >+ ec->nonupdate_dwell = DTD_HANGOVER; >+ if (ec->nonupdate_dwell) >+ ec->nonupdate_dwell--; >+ >+ /* Transfer logic */ >+ >+ /* These conditions are from the dual path paper [1], I messed with >+ them a bit to improve performance. */ >+ >+ if ((ec->adaption_mode & ECHO_CAN_USE_ADAPTION) && >+ (ec->nonupdate_dwell == 0) && >+ /* (ec->Lclean_bg < 0.875*ec->Lclean) */ >+ (8 * ec->Lclean_bg < 7 * ec->Lclean) && >+ /* (ec->Lclean_bg < 0.125*ec->Ltx) */ >+ (8 * ec->Lclean_bg < ec->Ltx)) { >+ if (ec->cond_met == 6) { >+ /* >+ * BG filter has had better results for 6 consecutive >+ * samples >+ */ >+ ec->adapt = 1; >+ memcpy(ec->fir_taps16[0], ec->fir_taps16[1], >+ ec->taps * sizeof(int16_t)); >+ } else >+ ec->cond_met++; >+ } else >+ ec->cond_met = 0; >+ >+ /* Non-Linear Processing */ >+ >+ ec->clean_nlp = ec->clean; >+ if (ec->adaption_mode & ECHO_CAN_USE_NLP) { >+ /* >+ * Non-linear processor - a fancy way to say "zap small >+ * signals, to avoid residual echo due to (uLaw/ALaw) >+ * non-linearity in the channel.". >+ */ >+ >+ if ((16 * ec->Lclean < ec->Ltx)) { >+ /* >+ * Our e/c has improved echo by at least 24 dB (each >+ * factor of 2 is 6dB, so 2*2*2*2=16 is the same as >+ * 6+6+6+6=24dB) >+ */ >+ if (ec->adaption_mode & ECHO_CAN_USE_CNG) { >+ ec->cng_level = ec->Lbgn; >+ >+ /* >+ * Very elementary comfort noise generation. >+ * Just random numbers rolled off very vaguely >+ * Hoth-like. DR: This noise doesn't sound >+ * quite right to me - I suspect there are some >+ * overlfow issues in the filtering as it's too >+ * "crackly". >+ * TODO: debug this, maybe just play noise at >+ * high level or look at spectrum. >+ */ >+ >+ ec->cng_rndnum = >+ 1664525U * ec->cng_rndnum + 1013904223U; >+ ec->cng_filter = >+ ((ec->cng_rndnum & 0xFFFF) - 32768 + >+ 5 * ec->cng_filter) >> 3; >+ ec->clean_nlp = >+ (ec->cng_filter * ec->cng_level * 8) >> 14; >+ >+ } else if (ec->adaption_mode & ECHO_CAN_USE_CLIP) { >+ /* This sounds much better than CNG */ >+ if (ec->clean_nlp > ec->Lbgn) >+ ec->clean_nlp = ec->Lbgn; >+ if (ec->clean_nlp < -ec->Lbgn) >+ ec->clean_nlp = -ec->Lbgn; >+ } else { >+ /* >+ * just mute the residual, doesn't sound very >+ * good, used mainly in G168 tests >+ */ >+ ec->clean_nlp = 0; >+ } >+ } else { >+ /* >+ * Background noise estimator. I tried a few >+ * algorithms here without much luck. This very simple >+ * one seems to work best, we just average the level >+ * using a slow (1 sec time const) filter if the >+ * current level is less than a (experimentally >+ * derived) constant. This means we dont include high >+ * level signals like near end speech. When combined >+ * with CNG or especially CLIP seems to work OK. >+ */ >+ if (ec->Lclean < 40) { >+ ec->Lbgn_acc += abs(ec->clean) - ec->Lbgn; >+ ec->Lbgn = (ec->Lbgn_acc + (1 << 11)) >> 12; >+ } >+ } >+ } >+ >+ /* Roll around the taps buffer */ >+ if (ec->curr_pos <= 0) >+ ec->curr_pos = ec->taps; >+ ec->curr_pos--; >+ >+ if (ec->adaption_mode & ECHO_CAN_DISABLE) >+ ec->clean_nlp = rx; >+ >+ /* Output scaled back up again to match input scaling */ >+ >+ return (int16_t) ec->clean_nlp << 1; >+} >+EXPORT_SYMBOL_GPL(oslec_update); >+ >+/* This function is seperated from the echo canceller is it is usually called >+ as part of the tx process. See rx HP (DC blocking) filter above, it's >+ the same design. >+ >+ Some soft phones send speech signals with a lot of low frequency >+ energy, e.g. down to 20Hz. This can make the hybrid non-linear >+ which causes the echo canceller to fall over. This filter can help >+ by removing any low frequency before it gets to the tx port of the >+ hybrid. >+ >+ It can also help by removing and DC in the tx signal. DC is bad >+ for LMS algorithms. >+ >+ This is one of the classic DC removal filters, adjusted to provide >+ sufficient bass rolloff to meet the above requirement to protect hybrids >+ from things that upset them. The difference between successive samples >+ produces a lousy HPF, and then a suitably placed pole flattens things out. >+ The final result is a nicely rolled off bass end. The filtering is >+ implemented with extended fractional precision, which noise shapes things, >+ giving very clean DC removal. >+*/ >+ >+int16_t oslec_hpf_tx(struct oslec_state *ec, int16_t tx) >+{ >+ int tmp, tmp1; >+ >+ if (ec->adaption_mode & ECHO_CAN_USE_TX_HPF) { >+ tmp = tx << 15; >+ >+ /* >+ * Make sure the gain of the HPF is 1.0. The first can still >+ * saturate a little under impulse conditions, and it might >+ * roll to 32768 and need clipping on sustained peak level >+ * signals. However, the scale of such clipping is small, and >+ * the error due to any saturation should not markedly affect >+ * the downstream processing. >+ */ >+ tmp -= (tmp >> 4); >+ >+ ec->tx_1 += -(ec->tx_1 >> DC_LOG2BETA) + tmp - ec->tx_2; >+ tmp1 = ec->tx_1 >> 15; >+ if (tmp1 > 32767) >+ tmp1 = 32767; >+ if (tmp1 < -32767) >+ tmp1 = -32767; >+ tx = tmp1; >+ ec->tx_2 = tmp; >+ } >+ >+ return tx; >+} >+EXPORT_SYMBOL_GPL(oslec_hpf_tx); >+ >+MODULE_LICENSE("GPL"); >+MODULE_AUTHOR("David Rowe"); >+MODULE_DESCRIPTION("Open Source Line Echo Canceller"); >+MODULE_VERSION("0.3.0"); >diff --git a/drivers/staging/echo/echo.h b/drivers/staging/echo/echo.h >new file mode 100644 >index 0000000..754e66d >--- /dev/null >+++ b/drivers/staging/echo/echo.h >@@ -0,0 +1,175 @@ >+/* >+ * SpanDSP - a series of DSP components for telephony >+ * >+ * echo.c - A line echo canceller. This code is being developed >+ * against and partially complies with G168. >+ * >+ * Written by Steve Underwood <steveu@coppice.org> >+ * and David Rowe <david_at_rowetel_dot_com> >+ * >+ * Copyright (C) 2001 Steve Underwood and 2007 David Rowe >+ * >+ * All rights reserved. >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License version 2, as >+ * published by the Free Software Foundation. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. >+ */ >+ >+#ifndef __ECHO_H >+#define __ECHO_H >+ >+/* >+Line echo cancellation for voice >+ >+What does it do? >+ >+This module aims to provide G.168-2002 compliant echo cancellation, to remove >+electrical echoes (e.g. from 2-4 wire hybrids) from voice calls. >+ >+ >+How does it work? >+ >+The heart of the echo cancellor is FIR filter. This is adapted to match the >+echo impulse response of the telephone line. It must be long enough to >+adequately cover the duration of that impulse response. The signal transmitted >+to the telephone line is passed through the FIR filter. Once the FIR is >+properly adapted, the resulting output is an estimate of the echo signal >+received from the line. This is subtracted from the received signal. The result >+is an estimate of the signal which originated at the far end of the line, free >+from echos of our own transmitted signal. >+ >+The least mean squares (LMS) algorithm is attributed to Widrow and Hoff, and >+was introduced in 1960. It is the commonest form of filter adaption used in >+things like modem line equalisers and line echo cancellers. There it works very >+well. However, it only works well for signals of constant amplitude. It works >+very poorly for things like speech echo cancellation, where the signal level >+varies widely. This is quite easy to fix. If the signal level is normalised - >+similar to applying AGC - LMS can work as well for a signal of varying >+amplitude as it does for a modem signal. This normalised least mean squares >+(NLMS) algorithm is the commonest one used for speech echo cancellation. Many >+other algorithms exist - e.g. RLS (essentially the same as Kalman filtering), >+FAP, etc. Some perform significantly better than NLMS. However, factors such >+as computational complexity and patents favour the use of NLMS. >+ >+A simple refinement to NLMS can improve its performance with speech. NLMS tends >+to adapt best to the strongest parts of a signal. If the signal is white noise, >+the NLMS algorithm works very well. However, speech has more low frequency than >+high frequency content. Pre-whitening (i.e. filtering the signal to flatten its >+spectrum) the echo signal improves the adapt rate for speech, and ensures the >+final residual signal is not heavily biased towards high frequencies. A very >+low complexity filter is adequate for this, so pre-whitening adds little to the >+compute requirements of the echo canceller. >+ >+An FIR filter adapted using pre-whitened NLMS performs well, provided certain >+conditions are met: >+ >+ - The transmitted signal has poor self-correlation. >+ - There is no signal being generated within the environment being >+ cancelled. >+ >+The difficulty is that neither of these can be guaranteed. >+ >+If the adaption is performed while transmitting noise (or something fairly >+noise like, such as voice) the adaption works very well. If the adaption is >+performed while transmitting something highly correlative (typically narrow >+band energy such as signalling tones or DTMF), the adaption can go seriously >+wrong. The reason is there is only one solution for the adaption on a near >+random signal - the impulse response of the line. For a repetitive signal, >+there are any number of solutions which converge the adaption, and nothing >+guides the adaption to choose the generalised one. Allowing an untrained >+canceller to converge on this kind of narrowband energy probably a good thing, >+since at least it cancels the tones. Allowing a well converged canceller to >+continue converging on such energy is just a way to ruin its generalised >+adaption. A narrowband detector is needed, so adapation can be suspended at >+appropriate times. >+ >+The adaption process is based on trying to eliminate the received signal. When >+there is any signal from within the environment being cancelled it may upset >+the adaption process. Similarly, if the signal we are transmitting is small, >+noise may dominate and disturb the adaption process. If we can ensure that the >+adaption is only performed when we are transmitting a significant signal level, >+and the environment is not, things will be OK. Clearly, it is easy to tell when >+we are sending a significant signal. Telling, if the environment is generating >+a significant signal, and doing it with sufficient speed that the adaption will >+not have diverged too much more we stop it, is a little harder. >+ >+The key problem in detecting when the environment is sourcing significant >+energy is that we must do this very quickly. Given a reasonably long sample of >+the received signal, there are a number of strategies which may be used to >+assess whether that signal contains a strong far end component. However, by the >+time that assessment is complete the far end signal will have already caused >+major mis-convergence in the adaption process. An assessment algorithm is >+needed which produces a fairly accurate result from a very short burst of far >+end energy. >+ >+How do I use it? >+ >+The echo cancellor processes both the transmit and receive streams sample by >+sample. The processing function is not declared inline. Unfortunately, >+cancellation requires many operations per sample, so the call overhead is only >+a minor burden. >+*/ >+ >+#include "fir.h" >+#include "oslec.h" >+ >+/* >+ G.168 echo canceller descriptor. This defines the working state for a line >+ echo canceller. >+*/ >+struct oslec_state { >+ int16_t tx, rx; >+ int16_t clean; >+ int16_t clean_nlp; >+ >+ int nonupdate_dwell; >+ int curr_pos; >+ int taps; >+ int log2taps; >+ int adaption_mode; >+ >+ int cond_met; >+ int32_t Pstates; >+ int16_t adapt; >+ int32_t factor; >+ int16_t shift; >+ >+ /* Average levels and averaging filter states */ >+ int Ltxacc, Lrxacc, Lcleanacc, Lclean_bgacc; >+ int Ltx, Lrx; >+ int Lclean; >+ int Lclean_bg; >+ int Lbgn, Lbgn_acc, Lbgn_upper, Lbgn_upper_acc; >+ >+ /* foreground and background filter states */ >+ struct fir16_state_t fir_state; >+ struct fir16_state_t fir_state_bg; >+ int16_t *fir_taps16[2]; >+ >+ /* DC blocking filter states */ >+ int tx_1, tx_2, rx_1, rx_2; >+ >+ /* optional High Pass Filter states */ >+ int32_t xvtx[5], yvtx[5]; >+ int32_t xvrx[5], yvrx[5]; >+ >+ /* Parameters for the optional Hoth noise generator */ >+ int cng_level; >+ int cng_rndnum; >+ int cng_filter; >+ >+ /* snapshot sample of coeffs used for development */ >+ int16_t *snapshot; >+}; >+ >+#endif /* __ECHO_H */ >diff --git a/drivers/staging/echo/fir.h b/drivers/staging/echo/fir.h >new file mode 100644 >index 0000000..288bffc >--- /dev/null >+++ b/drivers/staging/echo/fir.h >@@ -0,0 +1,286 @@ >+/* >+ * SpanDSP - a series of DSP components for telephony >+ * >+ * fir.h - General telephony FIR routines >+ * >+ * Written by Steve Underwood <steveu@coppice.org> >+ * >+ * Copyright (C) 2002 Steve Underwood >+ * >+ * All rights reserved. >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License version 2, as >+ * published by the Free Software Foundation. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. >+ */ >+ >+#if !defined(_FIR_H_) >+#define _FIR_H_ >+ >+/* >+ Blackfin NOTES & IDEAS: >+ >+ A simple dot product function is used to implement the filter. This performs >+ just one MAC/cycle which is inefficient but was easy to implement as a first >+ pass. The current Blackfin code also uses an unrolled form of the filter >+ history to avoid 0 length hardware loop issues. This is wasteful of >+ memory. >+ >+ Ideas for improvement: >+ >+ 1/ Rewrite filter for dual MAC inner loop. The issue here is handling >+ history sample offsets that are 16 bit aligned - the dual MAC needs >+ 32 bit aligmnent. There are some good examples in libbfdsp. >+ >+ 2/ Use the hardware circular buffer facility tohalve memory usage. >+ >+ 3/ Consider using internal memory. >+ >+ Using less memory might also improve speed as cache misses will be >+ reduced. A drop in MIPs and memory approaching 50% should be >+ possible. >+ >+ The foreground and background filters currenlty use a total of >+ about 10 MIPs/ch as measured with speedtest.c on a 256 TAP echo >+ can. >+*/ >+ >+#if defined(USE_MMX) || defined(USE_SSE2) >+#include "mmx.h" >+#endif >+ >+/* >+ * 16 bit integer FIR descriptor. This defines the working state for a single >+ * instance of an FIR filter using 16 bit integer coefficients. >+ */ >+struct fir16_state_t { >+ int taps; >+ int curr_pos; >+ const int16_t *coeffs; >+ int16_t *history; >+}; >+ >+/* >+ * 32 bit integer FIR descriptor. This defines the working state for a single >+ * instance of an FIR filter using 32 bit integer coefficients, and filtering >+ * 16 bit integer data. >+ */ >+struct fir32_state_t { >+ int taps; >+ int curr_pos; >+ const int32_t *coeffs; >+ int16_t *history; >+}; >+ >+/* >+ * Floating point FIR descriptor. This defines the working state for a single >+ * instance of an FIR filter using floating point coefficients and data. >+ */ >+struct fir_float_state_t { >+ int taps; >+ int curr_pos; >+ const float *coeffs; >+ float *history; >+}; >+ >+static inline const int16_t *fir16_create(struct fir16_state_t *fir, >+ const int16_t *coeffs, int taps) >+{ >+ fir->taps = taps; >+ fir->curr_pos = taps - 1; >+ fir->coeffs = coeffs; >+#if defined(USE_MMX) || defined(USE_SSE2) || defined(__bfin__) >+ fir->history = kcalloc(2 * taps, sizeof(int16_t), GFP_KERNEL); >+#else >+ fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL); >+#endif >+ return fir->history; >+} >+ >+static inline void fir16_flush(struct fir16_state_t *fir) >+{ >+#if defined(USE_MMX) || defined(USE_SSE2) || defined(__bfin__) >+ memset(fir->history, 0, 2 * fir->taps * sizeof(int16_t)); >+#else >+ memset(fir->history, 0, fir->taps * sizeof(int16_t)); >+#endif >+} >+ >+static inline void fir16_free(struct fir16_state_t *fir) >+{ >+ kfree(fir->history); >+} >+ >+#ifdef __bfin__ >+static inline int32_t dot_asm(short *x, short *y, int len) >+{ >+ int dot; >+ >+ len--; >+ >+ __asm__("I0 = %1;\n\t" >+ "I1 = %2;\n\t" >+ "A0 = 0;\n\t" >+ "R0.L = W[I0++] || R1.L = W[I1++];\n\t" >+ "LOOP dot%= LC0 = %3;\n\t" >+ "LOOP_BEGIN dot%=;\n\t" >+ "A0 += R0.L * R1.L (IS) || R0.L = W[I0++] || R1.L = W[I1++];\n\t" >+ "LOOP_END dot%=;\n\t" >+ "A0 += R0.L*R1.L (IS);\n\t" >+ "R0 = A0;\n\t" >+ "%0 = R0;\n\t" >+ : "=&d"(dot) >+ : "a"(x), "a"(y), "a"(len) >+ : "I0", "I1", "A1", "A0", "R0", "R1" >+ ); >+ >+ return dot; >+} >+#endif >+ >+static inline int16_t fir16(struct fir16_state_t *fir, int16_t sample) >+{ >+ int32_t y; >+#if defined(USE_MMX) >+ int i; >+ union mmx_t *mmx_coeffs; >+ union mmx_t *mmx_hist; >+ >+ fir->history[fir->curr_pos] = sample; >+ fir->history[fir->curr_pos + fir->taps] = sample; >+ >+ mmx_coeffs = (union mmx_t *) fir->coeffs; >+ mmx_hist = (union mmx_t *) &fir->history[fir->curr_pos]; >+ i = fir->taps; >+ pxor_r2r(mm4, mm4); >+ /* 8 samples per iteration, so the filter must be a multiple of >+ 8 long. */ >+ while (i > 0) { >+ movq_m2r(mmx_coeffs[0], mm0); >+ movq_m2r(mmx_coeffs[1], mm2); >+ movq_m2r(mmx_hist[0], mm1); >+ movq_m2r(mmx_hist[1], mm3); >+ mmx_coeffs += 2; >+ mmx_hist += 2; >+ pmaddwd_r2r(mm1, mm0); >+ pmaddwd_r2r(mm3, mm2); >+ paddd_r2r(mm0, mm4); >+ paddd_r2r(mm2, mm4); >+ i -= 8; >+ } >+ movq_r2r(mm4, mm0); >+ psrlq_i2r(32, mm0); >+ paddd_r2r(mm0, mm4); >+ movd_r2m(mm4, y); >+ emms(); >+#elif defined(USE_SSE2) >+ int i; >+ union xmm_t *xmm_coeffs; >+ union xmm_t *xmm_hist; >+ >+ fir->history[fir->curr_pos] = sample; >+ fir->history[fir->curr_pos + fir->taps] = sample; >+ >+ xmm_coeffs = (union xmm_t *) fir->coeffs; >+ xmm_hist = (union xmm_t *) &fir->history[fir->curr_pos]; >+ i = fir->taps; >+ pxor_r2r(xmm4, xmm4); >+ /* 16 samples per iteration, so the filter must be a multiple of >+ 16 long. */ >+ while (i > 0) { >+ movdqu_m2r(xmm_coeffs[0], xmm0); >+ movdqu_m2r(xmm_coeffs[1], xmm2); >+ movdqu_m2r(xmm_hist[0], xmm1); >+ movdqu_m2r(xmm_hist[1], xmm3); >+ xmm_coeffs += 2; >+ xmm_hist += 2; >+ pmaddwd_r2r(xmm1, xmm0); >+ pmaddwd_r2r(xmm3, xmm2); >+ paddd_r2r(xmm0, xmm4); >+ paddd_r2r(xmm2, xmm4); >+ i -= 16; >+ } >+ movdqa_r2r(xmm4, xmm0); >+ psrldq_i2r(8, xmm0); >+ paddd_r2r(xmm0, xmm4); >+ movdqa_r2r(xmm4, xmm0); >+ psrldq_i2r(4, xmm0); >+ paddd_r2r(xmm0, xmm4); >+ movd_r2m(xmm4, y); >+#elif defined(__bfin__) >+ fir->history[fir->curr_pos] = sample; >+ fir->history[fir->curr_pos + fir->taps] = sample; >+ y = dot_asm((int16_t *) fir->coeffs, &fir->history[fir->curr_pos], >+ fir->taps); >+#else >+ int i; >+ int offset1; >+ int offset2; >+ >+ fir->history[fir->curr_pos] = sample; >+ >+ offset2 = fir->curr_pos; >+ offset1 = fir->taps - offset2; >+ y = 0; >+ for (i = fir->taps - 1; i >= offset1; i--) >+ y += fir->coeffs[i] * fir->history[i - offset1]; >+ for (; i >= 0; i--) >+ y += fir->coeffs[i] * fir->history[i + offset2]; >+#endif >+ if (fir->curr_pos <= 0) >+ fir->curr_pos = fir->taps; >+ fir->curr_pos--; >+ return (int16_t) (y >> 15); >+} >+ >+static inline const int16_t *fir32_create(struct fir32_state_t *fir, >+ const int32_t *coeffs, int taps) >+{ >+ fir->taps = taps; >+ fir->curr_pos = taps - 1; >+ fir->coeffs = coeffs; >+ fir->history = kcalloc(taps, sizeof(int16_t), GFP_KERNEL); >+ return fir->history; >+} >+ >+static inline void fir32_flush(struct fir32_state_t *fir) >+{ >+ memset(fir->history, 0, fir->taps * sizeof(int16_t)); >+} >+ >+static inline void fir32_free(struct fir32_state_t *fir) >+{ >+ kfree(fir->history); >+} >+ >+static inline int16_t fir32(struct fir32_state_t *fir, int16_t sample) >+{ >+ int i; >+ int32_t y; >+ int offset1; >+ int offset2; >+ >+ fir->history[fir->curr_pos] = sample; >+ offset2 = fir->curr_pos; >+ offset1 = fir->taps - offset2; >+ y = 0; >+ for (i = fir->taps - 1; i >= offset1; i--) >+ y += fir->coeffs[i] * fir->history[i - offset1]; >+ for (; i >= 0; i--) >+ y += fir->coeffs[i] * fir->history[i + offset2]; >+ if (fir->curr_pos <= 0) >+ fir->curr_pos = fir->taps; >+ fir->curr_pos--; >+ return (int16_t) (y >> 15); >+} >+ >+#endif >diff --git a/drivers/staging/echo/mmx.h b/drivers/staging/echo/mmx.h >new file mode 100644 >index 0000000..c030cdf >--- /dev/null >+++ b/drivers/staging/echo/mmx.h >@@ -0,0 +1,288 @@ >+/* >+ * mmx.h >+ * Copyright (C) 1997-2001 H. Dietz and R. Fisher >+ * >+ * This file is part of FFmpeg. >+ * >+ * FFmpeg is free software; you can redistribute it and/or >+ * modify it under the terms of the GNU Lesser General Public >+ * License as published by the Free Software Foundation; either >+ * version 2.1 of the License, or (at your option) any later version. >+ * >+ * FFmpeg is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU >+ * Lesser General Public License for more details. >+ * >+ * You should have received a copy of the GNU Lesser General Public >+ * License along with FFmpeg; if not, write to the Free Software >+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA >+ */ >+#ifndef AVCODEC_I386MMX_H >+#define AVCODEC_I386MMX_H >+ >+/* >+ * The type of an value that fits in an MMX register (note that long >+ * long constant values MUST be suffixed by LL and unsigned long long >+ * values by ULL, lest they be truncated by the compiler) >+ */ >+ >+union mmx_t { >+ long long q; /* Quadword (64-bit) value */ >+ unsigned long long uq; /* Unsigned Quadword */ >+ int d[2]; /* 2 Doubleword (32-bit) values */ >+ unsigned int ud[2]; /* 2 Unsigned Doubleword */ >+ short w[4]; /* 4 Word (16-bit) values */ >+ unsigned short uw[4]; /* 4 Unsigned Word */ >+ char b[8]; /* 8 Byte (8-bit) values */ >+ unsigned char ub[8]; /* 8 Unsigned Byte */ >+ float s[2]; /* Single-precision (32-bit) value */ >+}; /* On an 8-byte (64-bit) boundary */ >+ >+/* SSE registers */ >+union xmm_t { >+ char b[16]; >+}; >+ >+ >+#define mmx_i2r(op, imm, reg) \ >+ __asm__ __volatile__ (#op " %0, %%" #reg \ >+ : /* nothing */ \ >+ : "i" (imm)) >+ >+#define mmx_m2r(op, mem, reg) \ >+ __asm__ __volatile__ (#op " %0, %%" #reg \ >+ : /* nothing */ \ >+ : "m" (mem)) >+ >+#define mmx_r2m(op, reg, mem) \ >+ __asm__ __volatile__ (#op " %%" #reg ", %0" \ >+ : "=m" (mem) \ >+ : /* nothing */) >+ >+#define mmx_r2r(op, regs, regd) \ >+ __asm__ __volatile__ (#op " %" #regs ", %" #regd) >+ >+ >+#define emms() __asm__ __volatile__ ("emms") >+ >+#define movd_m2r(var, reg) mmx_m2r(movd, var, reg) >+#define movd_r2m(reg, var) mmx_r2m(movd, reg, var) >+#define movd_r2r(regs, regd) mmx_r2r(movd, regs, regd) >+ >+#define movq_m2r(var, reg) mmx_m2r(movq, var, reg) >+#define movq_r2m(reg, var) mmx_r2m(movq, reg, var) >+#define movq_r2r(regs, regd) mmx_r2r(movq, regs, regd) >+ >+#define packssdw_m2r(var, reg) mmx_m2r(packssdw, var, reg) >+#define packssdw_r2r(regs, regd) mmx_r2r(packssdw, regs, regd) >+#define packsswb_m2r(var, reg) mmx_m2r(packsswb, var, reg) >+#define packsswb_r2r(regs, regd) mmx_r2r(packsswb, regs, regd) >+ >+#define packuswb_m2r(var, reg) mmx_m2r(packuswb, var, reg) >+#define packuswb_r2r(regs, regd) mmx_r2r(packuswb, regs, regd) >+ >+#define paddb_m2r(var, reg) mmx_m2r(paddb, var, reg) >+#define paddb_r2r(regs, regd) mmx_r2r(paddb, regs, regd) >+#define paddd_m2r(var, reg) mmx_m2r(paddd, var, reg) >+#define paddd_r2r(regs, regd) mmx_r2r(paddd, regs, regd) >+#define paddw_m2r(var, reg) mmx_m2r(paddw, var, reg) >+#define paddw_r2r(regs, regd) mmx_r2r(paddw, regs, regd) >+ >+#define paddsb_m2r(var, reg) mmx_m2r(paddsb, var, reg) >+#define paddsb_r2r(regs, regd) mmx_r2r(paddsb, regs, regd) >+#define paddsw_m2r(var, reg) mmx_m2r(paddsw, var, reg) >+#define paddsw_r2r(regs, regd) mmx_r2r(paddsw, regs, regd) >+ >+#define paddusb_m2r(var, reg) mmx_m2r(paddusb, var, reg) >+#define paddusb_r2r(regs, regd) mmx_r2r(paddusb, regs, regd) >+#define paddusw_m2r(var, reg) mmx_m2r(paddusw, var, reg) >+#define paddusw_r2r(regs, regd) mmx_r2r(paddusw, regs, regd) >+ >+#define pand_m2r(var, reg) mmx_m2r(pand, var, reg) >+#define pand_r2r(regs, regd) mmx_r2r(pand, regs, regd) >+ >+#define pandn_m2r(var, reg) mmx_m2r(pandn, var, reg) >+#define pandn_r2r(regs, regd) mmx_r2r(pandn, regs, regd) >+ >+#define pcmpeqb_m2r(var, reg) mmx_m2r(pcmpeqb, var, reg) >+#define pcmpeqb_r2r(regs, regd) mmx_r2r(pcmpeqb, regs, regd) >+#define pcmpeqd_m2r(var, reg) mmx_m2r(pcmpeqd, var, reg) >+#define pcmpeqd_r2r(regs, regd) mmx_r2r(pcmpeqd, regs, regd) >+#define pcmpeqw_m2r(var, reg) mmx_m2r(pcmpeqw, var, reg) >+#define pcmpeqw_r2r(regs, regd) mmx_r2r(pcmpeqw, regs, regd) >+ >+#define pcmpgtb_m2r(var, reg) mmx_m2r(pcmpgtb, var, reg) >+#define pcmpgtb_r2r(regs, regd) mmx_r2r(pcmpgtb, regs, regd) >+#define pcmpgtd_m2r(var, reg) mmx_m2r(pcmpgtd, var, reg) >+#define pcmpgtd_r2r(regs, regd) mmx_r2r(pcmpgtd, regs, regd) >+#define pcmpgtw_m2r(var, reg) mmx_m2r(pcmpgtw, var, reg) >+#define pcmpgtw_r2r(regs, regd) mmx_r2r(pcmpgtw, regs, regd) >+ >+#define pmaddwd_m2r(var, reg) mmx_m2r(pmaddwd, var, reg) >+#define pmaddwd_r2r(regs, regd) mmx_r2r(pmaddwd, regs, regd) >+ >+#define pmulhw_m2r(var, reg) mmx_m2r(pmulhw, var, reg) >+#define pmulhw_r2r(regs, regd) mmx_r2r(pmulhw, regs, regd) >+ >+#define pmullw_m2r(var, reg) mmx_m2r(pmullw, var, reg) >+#define pmullw_r2r(regs, regd) mmx_r2r(pmullw, regs, regd) >+ >+#define por_m2r(var, reg) mmx_m2r(por, var, reg) >+#define por_r2r(regs, regd) mmx_r2r(por, regs, regd) >+ >+#define pslld_i2r(imm, reg) mmx_i2r(pslld, imm, reg) >+#define pslld_m2r(var, reg) mmx_m2r(pslld, var, reg) >+#define pslld_r2r(regs, regd) mmx_r2r(pslld, regs, regd) >+#define psllq_i2r(imm, reg) mmx_i2r(psllq, imm, reg) >+#define psllq_m2r(var, reg) mmx_m2r(psllq, var, reg) >+#define psllq_r2r(regs, regd) mmx_r2r(psllq, regs, regd) >+#define psllw_i2r(imm, reg) mmx_i2r(psllw, imm, reg) >+#define psllw_m2r(var, reg) mmx_m2r(psllw, var, reg) >+#define psllw_r2r(regs, regd) mmx_r2r(psllw, regs, regd) >+ >+#define psrad_i2r(imm, reg) mmx_i2r(psrad, imm, reg) >+#define psrad_m2r(var, reg) mmx_m2r(psrad, var, reg) >+#define psrad_r2r(regs, regd) mmx_r2r(psrad, regs, regd) >+#define psraw_i2r(imm, reg) mmx_i2r(psraw, imm, reg) >+#define psraw_m2r(var, reg) mmx_m2r(psraw, var, reg) >+#define psraw_r2r(regs, regd) mmx_r2r(psraw, regs, regd) >+ >+#define psrld_i2r(imm, reg) mmx_i2r(psrld, imm, reg) >+#define psrld_m2r(var, reg) mmx_m2r(psrld, var, reg) >+#define psrld_r2r(regs, regd) mmx_r2r(psrld, regs, regd) >+#define psrlq_i2r(imm, reg) mmx_i2r(psrlq, imm, reg) >+#define psrlq_m2r(var, reg) mmx_m2r(psrlq, var, reg) >+#define psrlq_r2r(regs, regd) mmx_r2r(psrlq, regs, regd) >+#define psrlw_i2r(imm, reg) mmx_i2r(psrlw, imm, reg) >+#define psrlw_m2r(var, reg) mmx_m2r(psrlw, var, reg) >+#define psrlw_r2r(regs, regd) mmx_r2r(psrlw, regs, regd) >+ >+#define psubb_m2r(var, reg) mmx_m2r(psubb, var, reg) >+#define psubb_r2r(regs, regd) mmx_r2r(psubb, regs, regd) >+#define psubd_m2r(var, reg) mmx_m2r(psubd, var, reg) >+#define psubd_r2r(regs, regd) mmx_r2r(psubd, regs, regd) >+#define psubw_m2r(var, reg) mmx_m2r(psubw, var, reg) >+#define psubw_r2r(regs, regd) mmx_r2r(psubw, regs, regd) >+ >+#define psubsb_m2r(var, reg) mmx_m2r(psubsb, var, reg) >+#define psubsb_r2r(regs, regd) mmx_r2r(psubsb, regs, regd) >+#define psubsw_m2r(var, reg) mmx_m2r(psubsw, var, reg) >+#define psubsw_r2r(regs, regd) mmx_r2r(psubsw, regs, regd) >+ >+#define psubusb_m2r(var, reg) mmx_m2r(psubusb, var, reg) >+#define psubusb_r2r(regs, regd) mmx_r2r(psubusb, regs, regd) >+#define psubusw_m2r(var, reg) mmx_m2r(psubusw, var, reg) >+#define psubusw_r2r(regs, regd) mmx_r2r(psubusw, regs, regd) >+ >+#define punpckhbw_m2r(var, reg) mmx_m2r(punpckhbw, var, reg) >+#define punpckhbw_r2r(regs, regd) mmx_r2r(punpckhbw, regs, regd) >+#define punpckhdq_m2r(var, reg) mmx_m2r(punpckhdq, var, reg) >+#define punpckhdq_r2r(regs, regd) mmx_r2r(punpckhdq, regs, regd) >+#define punpckhwd_m2r(var, reg) mmx_m2r(punpckhwd, var, reg) >+#define punpckhwd_r2r(regs, regd) mmx_r2r(punpckhwd, regs, regd) >+ >+#define punpcklbw_m2r(var, reg) mmx_m2r(punpcklbw, var, reg) >+#define punpcklbw_r2r(regs, regd) mmx_r2r(punpcklbw, regs, regd) >+#define punpckldq_m2r(var, reg) mmx_m2r(punpckldq, var, reg) >+#define punpckldq_r2r(regs, regd) mmx_r2r(punpckldq, regs, regd) >+#define punpcklwd_m2r(var, reg) mmx_m2r(punpcklwd, var, reg) >+#define punpcklwd_r2r(regs, regd) mmx_r2r(punpcklwd, regs, regd) >+ >+#define pxor_m2r(var, reg) mmx_m2r(pxor, var, reg) >+#define pxor_r2r(regs, regd) mmx_r2r(pxor, regs, regd) >+ >+ >+/* 3DNOW extensions */ >+ >+#define pavgusb_m2r(var, reg) mmx_m2r(pavgusb, var, reg) >+#define pavgusb_r2r(regs, regd) mmx_r2r(pavgusb, regs, regd) >+ >+ >+/* AMD MMX extensions - also available in intel SSE */ >+ >+ >+#define mmx_m2ri(op, mem, reg, imm) \ >+ __asm__ __volatile__ (#op " %1, %0, %%" #reg \ >+ : /* nothing */ \ >+ : "m" (mem), "i" (imm)) >+#define mmx_r2ri(op, regs, regd, imm) \ >+ __asm__ __volatile__ (#op " %0, %%" #regs ", %%" #regd \ >+ : /* nothing */ \ >+ : "i" (imm)) >+ >+#define mmx_fetch(mem, hint) \ >+ __asm__ __volatile__ ("prefetch" #hint " %0" \ >+ : /* nothing */ \ >+ : "m" (mem)) >+ >+ >+#define maskmovq(regs, maskreg) mmx_r2ri(maskmovq, regs, maskreg) >+ >+#define movntq_r2m(mmreg, var) mmx_r2m(movntq, mmreg, var) >+ >+#define pavgb_m2r(var, reg) mmx_m2r(pavgb, var, reg) >+#define pavgb_r2r(regs, regd) mmx_r2r(pavgb, regs, regd) >+#define pavgw_m2r(var, reg) mmx_m2r(pavgw, var, reg) >+#define pavgw_r2r(regs, regd) mmx_r2r(pavgw, regs, regd) >+ >+#define pextrw_r2r(mmreg, reg, imm) mmx_r2ri(pextrw, mmreg, reg, imm) >+ >+#define pinsrw_r2r(reg, mmreg, imm) mmx_r2ri(pinsrw, reg, mmreg, imm) >+ >+#define pmaxsw_m2r(var, reg) mmx_m2r(pmaxsw, var, reg) >+#define pmaxsw_r2r(regs, regd) mmx_r2r(pmaxsw, regs, regd) >+ >+#define pmaxub_m2r(var, reg) mmx_m2r(pmaxub, var, reg) >+#define pmaxub_r2r(regs, regd) mmx_r2r(pmaxub, regs, regd) >+ >+#define pminsw_m2r(var, reg) mmx_m2r(pminsw, var, reg) >+#define pminsw_r2r(regs, regd) mmx_r2r(pminsw, regs, regd) >+ >+#define pminub_m2r(var, reg) mmx_m2r(pminub, var, reg) >+#define pminub_r2r(regs, regd) mmx_r2r(pminub, regs, regd) >+ >+#define pmovmskb(mmreg, reg) \ >+ __asm__ __volatile__ ("movmskps %" #mmreg ", %" #reg) >+ >+#define pmulhuw_m2r(var, reg) mmx_m2r(pmulhuw, var, reg) >+#define pmulhuw_r2r(regs, regd) mmx_r2r(pmulhuw, regs, regd) >+ >+#define prefetcht0(mem) mmx_fetch(mem, t0) >+#define prefetcht1(mem) mmx_fetch(mem, t1) >+#define prefetcht2(mem) mmx_fetch(mem, t2) >+#define prefetchnta(mem) mmx_fetch(mem, nta) >+ >+#define psadbw_m2r(var, reg) mmx_m2r(psadbw, var, reg) >+#define psadbw_r2r(regs, regd) mmx_r2r(psadbw, regs, regd) >+ >+#define pshufw_m2r(var, reg, imm) mmx_m2ri(pshufw, var, reg, imm) >+#define pshufw_r2r(regs, regd, imm) mmx_r2ri(pshufw, regs, regd, imm) >+ >+#define sfence() __asm__ __volatile__ ("sfence\n\t") >+ >+/* SSE2 */ >+#define pshufhw_m2r(var, reg, imm) mmx_m2ri(pshufhw, var, reg, imm) >+#define pshufhw_r2r(regs, regd, imm) mmx_r2ri(pshufhw, regs, regd, imm) >+#define pshuflw_m2r(var, reg, imm) mmx_m2ri(pshuflw, var, reg, imm) >+#define pshuflw_r2r(regs, regd, imm) mmx_r2ri(pshuflw, regs, regd, imm) >+ >+#define pshufd_r2r(regs, regd, imm) mmx_r2ri(pshufd, regs, regd, imm) >+ >+#define movdqa_m2r(var, reg) mmx_m2r(movdqa, var, reg) >+#define movdqa_r2m(reg, var) mmx_r2m(movdqa, reg, var) >+#define movdqa_r2r(regs, regd) mmx_r2r(movdqa, regs, regd) >+#define movdqu_m2r(var, reg) mmx_m2r(movdqu, var, reg) >+#define movdqu_r2m(reg, var) mmx_r2m(movdqu, reg, var) >+#define movdqu_r2r(regs, regd) mmx_r2r(movdqu, regs, regd) >+ >+#define pmullw_r2m(reg, var) mmx_r2m(pmullw, reg, var) >+ >+#define pslldq_i2r(imm, reg) mmx_i2r(pslldq, imm, reg) >+#define psrldq_i2r(imm, reg) mmx_i2r(psrldq, imm, reg) >+ >+#define punpcklqdq_r2r(regs, regd) mmx_r2r(punpcklqdq, regs, regd) >+#define punpckhqdq_r2r(regs, regd) mmx_r2r(punpckhqdq, regs, regd) >+ >+ >+#endif /* AVCODEC_I386MMX_H */ >diff --git a/drivers/staging/echo/oslec.h b/drivers/staging/echo/oslec.h >new file mode 100644 >index 0000000..f417536 >--- /dev/null >+++ b/drivers/staging/echo/oslec.h >@@ -0,0 +1,94 @@ >+/* >+ * OSLEC - A line echo canceller. This code is being developed >+ * against and partially complies with G168. Using code from SpanDSP >+ * >+ * Written by Steve Underwood <steveu@coppice.org> >+ * and David Rowe <david_at_rowetel_dot_com> >+ * >+ * Copyright (C) 2001 Steve Underwood and 2007-2008 David Rowe >+ * >+ * All rights reserved. >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License version 2, as >+ * published by the Free Software Foundation. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. >+ * >+ */ >+ >+#ifndef __OSLEC_H >+#define __OSLEC_H >+ >+/* Mask bits for the adaption mode */ >+#define ECHO_CAN_USE_ADAPTION 0x01 >+#define ECHO_CAN_USE_NLP 0x02 >+#define ECHO_CAN_USE_CNG 0x04 >+#define ECHO_CAN_USE_CLIP 0x08 >+#define ECHO_CAN_USE_TX_HPF 0x10 >+#define ECHO_CAN_USE_RX_HPF 0x20 >+#define ECHO_CAN_DISABLE 0x40 >+ >+/** >+ * oslec_state: G.168 echo canceller descriptor. >+ * >+ * This defines the working state for a line echo canceller. >+ */ >+struct oslec_state; >+ >+/** >+ * oslec_create - Create a voice echo canceller context. >+ * @len: The length of the canceller, in samples. >+ * @return: The new canceller context, or NULL if the canceller could not be >+ * created. >+ */ >+struct oslec_state *oslec_create(int len, int adaption_mode); >+ >+/** >+ * oslec_free - Free a voice echo canceller context. >+ * @ec: The echo canceller context. >+ */ >+void oslec_free(struct oslec_state *ec); >+ >+/** >+ * oslec_flush - Flush (reinitialise) a voice echo canceller context. >+ * @ec: The echo canceller context. >+ */ >+void oslec_flush(struct oslec_state *ec); >+ >+/** >+ * oslec_adaption_mode - set the adaption mode of a voice echo canceller context. >+ * @ec The echo canceller context. >+ * @adaption_mode: The mode. >+ */ >+void oslec_adaption_mode(struct oslec_state *ec, int adaption_mode); >+ >+void oslec_snapshot(struct oslec_state *ec); >+ >+/** >+ * oslec_update: Process a sample through a voice echo canceller. >+ * @ec: The echo canceller context. >+ * @tx: The transmitted audio sample. >+ * @rx: The received audio sample. >+ * >+ * The return value is the clean (echo cancelled) received sample. >+ */ >+int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx); >+ >+/** >+ * oslec_hpf_tx: Process to high pass filter the tx signal. >+ * @ec: The echo canceller context. >+ * @tx: The transmitted auio sample. >+ * >+ * The return value is the HP filtered transmit sample, send this to your D/A. >+ */ >+int16_t oslec_hpf_tx(struct oslec_state *ec, int16_t tx); >+ >+#endif /* __OSLEC_H */
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 404407
:
302387
| 302561 |
303643
|
303645