summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsruffell <sruffell@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2008-03-27 21:17:46 +0000
committersruffell <sruffell@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2008-03-27 21:17:46 +0000
commitcd81703764c4d2884c60f3632f15d5bfc44dc31d (patch)
tree93046f7721e2be48425642d4902bafe1ffee47f6
parenta15be82083adbbc27fe2c5b8ce648f14b6eb93d3 (diff)
- Updated wctdm24xxp and wcte12xp driver which are now more tolerant of
systems which do not exhibit good real-time characteristics. - Bringing in improvements to battery alarm generation that was on kpflemings battery_alarms branch. (Issue #12099) git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.4@4096 5390a7c7-147a-4af0-8ec9-7488f05a26cb
-rw-r--r--doc/module-parameters.txt13
-rw-r--r--kernel/fxo_modes.h588
-rw-r--r--kernel/voicebus.c1492
-rw-r--r--kernel/voicebus.h53
-rw-r--r--kernel/wctdm.c397
-rw-r--r--kernel/wctdm24xxp/Kbuild2
-rw-r--r--kernel/wctdm24xxp/base.c1057
-rw-r--r--kernel/wctdm24xxp/wctdm24xxp.h41
-rw-r--r--kernel/wcte12xp/Kbuild2
-rw-r--r--kernel/wcte12xp/base.c612
-rw-r--r--kernel/wcte12xp/vpmadt032.c84
-rw-r--r--kernel/wcte12xp/wcte12xp.h19
-rw-r--r--kernel/zaptel-base.c49
13 files changed, 2817 insertions, 1592 deletions
diff --git a/doc/module-parameters.txt b/doc/module-parameters.txt
index 893e2ca..aa404c6 100644
--- a/doc/module-parameters.txt
+++ b/doc/module-parameters.txt
@@ -3,6 +3,7 @@
(Note: this list is woefully incomplete, but is a starting point and
will be improved as time permits.)
+
--- VPMADT032 Parameters ---
The wctdm24xxp and wcte12xp drivers support cards which can have an
@@ -42,3 +43,15 @@ vpmnlpmaxsupp - 0 through 90, in decibels (dB), defaults to 0
When vpmnlptype is 'Suppress', this value indicates the
relative energy level decrease that should be applied to the
echo signal).
+
+ --- General Module Parameters ---
+
+latency - 3 to 32, defaults to 3
+
+ For the wctdm24xxp and wcte12xp drivers. Specifies the amount of
+ latency the driver inserts into the voice conversation. On systems
+ that are unable to service the interrupt in a timely manner,
+ increasing the latency can increase module init time since the driver
+ will not have to dynamically grow the latency during initialization.
+ However, the latency in the driver does add to the overall latency of
+ any voice conversation.
diff --git a/kernel/fxo_modes.h b/kernel/fxo_modes.h
new file mode 100644
index 0000000..83172a5
--- /dev/null
+++ b/kernel/fxo_modes.h
@@ -0,0 +1,588 @@
+/*
+ * FXO port mode settings for various regions
+ *
+ * Copyright (C) 2008 Digium, Inc.
+ *
+ * extracted from wctdm.c by
+ * Kevin P. Fleming <kpfleming@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 _FXO_MODES_H
+#define _FXO_MODES_H
+
+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;
+ unsigned int battdebounce; /* in milliseconds */
+ unsigned int battalarm; /* in milliseconds */
+ unsigned int battthresh; /* unknown units */
+} fxo_modes[] =
+{
+ /* US, Canada */
+ { .name = "FCC",
+ .rt = 1,
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ /* Austria, Belgium, Denmark, Finland, France, Germany,
+ Greece, Iceland, Ireland, Italy, Luxembourg, Netherlands,
+ Norway, Portugal, Spain, Sweden, Switzerland, and UK */
+ { .name = "TBR21",
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .ring_osc = 0x7e6c,
+ .ring_x = 0x023a,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "ARGENTINA",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "AUSTRALIA",
+ .ohs = 1,
+ .mini = 0x3,
+ .acim = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "AUSTRIA",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "BAHRAIN",
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "BELGIUM",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "BRAZIL",
+ .mini = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "BULGARIA",
+ .ilim = 1,
+ .dcv = 0x3,
+ .mini = 0x0,
+ .acim = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "CANADA",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "CHILE",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "CHINA",
+ .mini = 0x3,
+ .acim = 0xf,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "COLOMBIA",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "CROATIA",
+ .ilim = 1,
+ .dcv = 0x3,
+ .mini = 0,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "CYPRUS",
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "CZECH",
+ .ilim = 1,
+ .dcv = 0x3,
+ .mini = 0,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "DENMARK",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "ECUADOR",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "EGYPT",
+ .mini = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "ELSALVADOR",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "FINLAND",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "FRANCE",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .mini = 0,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "GERMANY",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "GREECE",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "GUAM",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "HONGKONG",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "HUNGARY",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "ICELAND",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "INDIA",
+ .dcv = 0x3,
+ .acim = 0x4,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "INDONESIA",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "IRELAND",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "ISRAEL",
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "ITALY",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "JAPAN",
+ .mini = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "JORDAN",
+ .mini = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "KAZAKHSTAN",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "KUWAIT",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "LATVIA",
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "LEBANON",
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "LUXEMBOURG",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "MACAO",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ /* Current loop >= 20ma */
+ { .name = "MALAYSIA",
+ .mini = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "MALTA",
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "MEXICO",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "MOROCCO",
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "NETHERLANDS",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "NEWZEALAND",
+ .dcv = 0x3,
+ .acim = 0x4,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "NIGERIA",
+ .ilim = 0x1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "NORWAY",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "OMAN",
+ .mini = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "PAKISTAN",
+ .mini = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "PERU",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "PHILIPPINES",
+ .mini = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "POLAND",
+ .rz = 1,
+ .rt = 1,
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "PORTUGAL",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "ROMANIA",
+ .dcv = 3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "RUSSIA",
+ .mini = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "SAUDIARABIA",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "SINGAPORE",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "SLOVAKIA",
+ .dcv = 0x3,
+ .acim = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "SLOVENIA",
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "SOUTHAFRICA",
+ .ohs = 1,
+ .rz = 1,
+ .dcv = 0x3,
+ .acim = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "SOUTHKOREA",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "SPAIN",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "SWEDEN",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "SWITZERLAND",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x2,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "SYRIA",
+ .mini = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "TAIWAN",
+ .mini = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "THAILAND",
+ .mini = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "UAE",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "UK",
+ .ohs2 = 1,
+ .ilim = 1,
+ .dcv = 0x3,
+ .acim = 0x5,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "USA",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+ { .name = "YEMEN",
+ .dcv = 0x3,
+ .battdebounce = 64,
+ .battalarm = 1000,
+ .battthresh = 3,
+ },
+};
+
+#endif /* _FXO_MODES_H */
diff --git a/kernel/voicebus.c b/kernel/voicebus.c
new file mode 100644
index 0000000..40494e5
--- /dev/null
+++ b/kernel/voicebus.c
@@ -0,0 +1,1492 @@
+/*
+ * VoiceBus(tm) Interface Library.
+ *
+ * Written by Shaun Ruffell <sruffell@digium.com>
+ * and based on previous work by Mark Spencer <markster@digium.com>,
+ * Matthew Fredrickson <creslin@digium.com>, and
+ * Michael Spiceland <mspiceland@digium.com>
+ *
+ * Copyright (C) 2007-2008 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.
+ *
+ * VoiceBus is a registered trademark of Digium.
+ *
+ * \todo Make the client drivers back out gracefully when presented with a
+ * signal.
+ * \todo Modify clients to sleep with timeout when waiting for interrupt.
+ * \todo Check on a 64-bit CPU / Kernel
+ */
+
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+
+#include "voicebus.h"
+
+#define assert(__x__) BUG_ON(!(__x__))
+
+#define INTERRUPT 0 /* Run the deferred processing in the ISR. */
+#define TASKLET 1 /* Run in a tasklet. */
+#define TIMER 2 /* Run in a system timer. */
+#define WORKQUEUE 3 /* Run in a workqueue. */
+#ifndef VOICEBUS_DEFERRED
+#define VOICEBUS_DEFERRED INTERRUPT
+#endif
+#if VOICEBUS_DEFERRED == WORKQUEUE
+#define VOICEBUS_ALLOC_FLAGS GFP_KERNEL
+#else
+#define VOICEBUS_ALLOC_FLAGS GFP_ATOMIC
+#endif
+
+#if VOICEBUS_DEFERRED == TIMER
+#if HZ < 1000
+/* \todo Put an error message here. */
+#endif
+#endif
+
+/*! The number of descriptors in both the tx and rx descriptor ring. */
+#define DRING_SIZE (1 << 5) /* Must be a power of 2 */
+#define DRING_MASK (DRING_SIZE-1)
+
+/* Interrupt status' reported in SR_CSR5 */
+#define TX_COMPLETE_INTERRUPT 0x00000001
+#define TX_STOPPED_INTERRUPT 0x00000002
+#define TX_UNAVAILABLE_INTERRUPT 0x00000004
+#define TX_JABBER_TIMEOUT_INTERRUPT 0x00000008
+#define TX_UNDERFLOW_INTERRUPT 0x00000020
+#define RX_COMPLETE_INTERRUPT 0x00000040
+#define RX_UNAVAILABLE_INTERRUPT 0x00000080
+#define RX_STOPPED_INTERRUPT 0x00000100
+#define RX_WATCHDOG_TIMEOUT_INTERRUPT 0x00000200
+#define TIMER_INTERRUPT 0x00000800
+#define FATAL_BUS_ERROR_INTERRUPT 0x00002000
+#define ABNORMAL_INTERRUPT_SUMMARY 0x00008000
+#define NORMAL_INTERRUPT_SUMMARY 0x00010000
+
+#define SR_CSR5 0x0028
+#define NAR_CSR6 0x0030
+
+#define IER_CSR7 0x0038
+#define CSR7_TCIE 0x00000001 /* tx complete */
+#define CSR7_TPSIE 0x00000002 /* tx processor stopped */
+#define CSR7_TDUIE 0x00000004 /* tx desc unavailable */
+#define CSR7_TUIE 0x00000020 /* tx underflow */
+#define CSR7_RCIE 0x00000040 /* rx complete */
+#define CSR7_RUIE 0x00000080 /* rx desc unavailable */
+#define CSR7_RSIE 0x00000100 /* rx processor stopped */
+#define CSR7_FBEIE 0x00002000 /* fatal bus error */
+#define CSR7_AIE 0x00008000 /* abnormal enable */
+#define CSR7_NIE 0x00010000 /* normal enable */
+
+#define DEFAULT_INTERRUPTS ( CSR7_TCIE | CSR7_TPSIE | CSR7_TDUIE | \
+ CSR7_RUIE | CSR7_RSIE | CSR7_FBEIE | \
+ CSR7_AIE | CSR7_NIE)
+
+#define CSR9 0x0048
+#define CSR9_MDC 0x00010000
+#define CSR9_MDO 0x00020000
+#define CSR9_MMC 0x00040000
+#define CSR9_MDI 0x00080000
+
+#define OWN_BIT (1 << 31)
+
+/* In memory structure shared by the host and the adapter. */
+struct voicebus_descriptor {
+ u32 des0;
+ u32 des1;
+ u32 buffer1;
+ u32 container; /* Unused */
+} __attribute__((packed));
+
+struct voicebus_descriptor_list {
+ /* Pointer to an array of descriptors to give to hardware. */
+ struct voicebus_descriptor* desc;
+ /* Read completed buffers from the head. */
+ unsigned int head;
+ /* Write ready buffers to the tail. */
+ unsigned int tail;
+ /* Array to save the kernel virtual address of pending buffers. */
+ void * pending[DRING_SIZE];
+ /* PCI Bus address of the descriptor list. */
+ dma_addr_t desc_dma;
+ /*! either DMA_FROM_DEVICE or DMA_TO_DEVICE */
+ unsigned int direction;
+ /*! The number of buffers currently submitted to the hardware. */
+ atomic_t count;
+ /*! The number of bytes to pad each descriptor for cache alignment. */
+ unsigned int padding;
+};
+
+
+/*! * \brief Represents a VoiceBus interface on a Digium telephony card.
+ */
+struct voicebus {
+ /*! Name of this card. */
+ const char *board_name;
+ /*! The system pci device for this VoiceBus interface. */
+ struct pci_dev *pdev;
+ /*! Protects access to card registers and this structure. You should
+ * hold this lock before accessing most of the members of this data
+ * structure or the card registers. */
+ spinlock_t lock;
+ /*! The size of the transmit and receive buffers for this card. */
+ u32 framesize;
+ /*! The number of u32s in the host system cache line. */
+ u8 cache_line_size;
+ /*! Pool to allocate memory for the tx and rx descriptor rings. */
+ struct voicebus_descriptor_list rxd;
+ struct voicebus_descriptor_list txd;
+ /*! Level of debugging information. 0=None, 5=Insane. */
+ atomic_t debuglevel;
+ /*! Cache of buffer objects. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ kmem_cache_t *buffer_cache;
+#else
+ struct kmem_cache *buffer_cache;
+#endif
+ /*! Base address of the VoiceBus interface registers in I/O space. */
+ u32 iobase;
+ /*! The IRQ line for this VoiceBus interface. */
+ unsigned int irq;
+#if VOICEBUS_DEFERRED == WORKQUEUE
+ /*! Process buffers in the context of this workqueue. */
+ struct workqueue_struct *workqueue;
+ /*! Work item to process tx / rx buffers. */
+ struct work_struct workitem;
+#elif VOICEBUS_DEFERRED == TASKLET
+ /*! Process buffers in the context of a tasklet. */
+ struct tasklet_struct tasklet;
+#elif VOICEBUS_DEFERRED == TIMER
+ /*! Process buffers in a timer without generating interrupts. */
+ struct timer_list timer;
+#endif
+ /*! Callback function to board specific module to process frames. */
+ void (*handle_receive)(void *vbb, void *context);
+ void (*handle_transmit)(void *vbb, void *context);
+ /*! Data to pass to the receive and transmit callback. */
+ void *context;
+ struct completion stopped_completion;
+ /*! Flags */
+ unsigned long flags;
+ /* \todo see about removing this... */
+ u32 sdi;
+ /*! Number of tx buffers to queue up before enabling interrupts. */
+ unsigned int min_tx_buffer_count;
+};
+
+/*
+ * Use the following macros to lock the VoiceBus interface, and it won't
+ * matter if the deferred processing is running inside the interrupt handler,
+ * in a tasklet, or in a workqueue.
+ */
+#if VOICEBUS_DEFERRED == WORKQUEUE
+/*
+ * When the deferred processing is running in a workqueue, voicebus will never
+ * be locked from the context of the interrupt handler, and therefore we do
+ * not need to lock interrupts.
+ */
+#define LOCKS_VOICEBUS
+#define LOCKS_FROM_DEFERRED
+#define VBLOCK(_vb_) spin_lock(&((_vb_)->lock))
+#define VBUNLOCK(_vb_) spin_unlock(&((_vb_)->lock))
+#define VBLOCK_FROM_DEFERRED(_vb_) spin_lock(&((_vb_)->lock))
+#define VBUNLOCK_FROM_DEFERRED(_vb_) spin_lock(&((_vb_)->lock))
+#else
+#define LOCKS_VOICEBUS unsigned long _irqflags
+#define LOCKS_FROM_DEFERRED
+#define VBLOCK(_vb_) spin_lock_irqsave(&((_vb_)->lock), _irqflags)
+#define VBUNLOCK(_vb_) spin_unlock_irqrestore(&((_vb_)->lock), _irqflags)
+#define VBLOCK_FROM_DEFERRED(_vb_) spin_lock(&((_vb_)->lock))
+#define VBUNLOCK_FROM_DEFERRED(_vb_) spin_lock(&((_vb_)->lock))
+#endif
+
+#define VB_PRINTK(_vb, _lvl, _fmt, _args...) \
+ printk(KERN_##_lvl "%s: " _fmt, (_vb)->board_name, ## _args)
+
+/* Bit definitions for struct voicebus.flags */
+#define TX_UNDERRUN 1
+#define RX_UNDERRUN 2
+#define IN_DEFERRED_PROCESSING 3
+#define STOP 4
+
+#if VOICEBUS_DEFERRED == WORKQUEUE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+/*! \brief Make the current task real-time. */
+static void
+vb_setup_deferred(void *data)
+#else
+static void
+vb_setup_deferred(struct work_struct *work)
+#endif
+{
+ struct sched_param param = { .sched_priority = 99 };
+ sched_setscheduler(current, SCHED_FIFO, &param);
+}
+/*! \brief Schedule a work item to make the voicebus workqueue real-time. */
+static void
+vb_set_workqueue_priority(struct voicebus *vb)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ DECLARE_WORK(deferred_setup, vb_setup_deferred, NULL);
+#else
+ DECLARE_WORK(deferred_setup, vb_setup_deferred);
+#endif
+ queue_work(vb->workqueue, &deferred_setup);
+ flush_workqueue(vb->workqueue);
+}
+#endif
+#endif
+
+#ifdef DBG
+static inline int
+assert_in_vb_deferred(struct voicebus *vb)
+{
+ assert(test_bit(IN_DEFERRED_PROCESSING, &vb->flags));
+}
+
+static inline void
+start_vb_deferred(struct voicebus *vb)
+{
+ set_bit(IN_DEFERRED_PROCESSING, &vb->flags);
+}
+
+static inline void
+stop_vb_deferred(struct voicebus *vb)
+{
+ clear_bit(IN_DEFERRED_PROCESSING, &vb->flags);
+}
+#else
+#define assert_in_vb_deferred(_x_) do {;} while(0)
+#define start_vb_deferred(_x_) do {;} while(0)
+#define stop_vb_deferred(_x_) do {;} while(0)
+#endif
+
+static inline struct voicebus_descriptor *
+vb_descriptor(struct voicebus_descriptor_list *dl, int index)
+{
+ struct voicebus_descriptor *d;
+ d = (struct voicebus_descriptor *)((u8*)dl->desc +
+ ((sizeof(*d) + dl->padding) * index));
+ return d;
+}
+
+static int
+vb_initialize_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl,
+ u32 des1, unsigned int direction)
+{
+ int i;
+ struct voicebus_descriptor *d;
+ const u32 END_OF_RING = 0x02000000;
+
+ assert(dl);
+
+ /*
+ * Add some padding to each descriptor to ensure that they are
+ * aligned on host system cache-line boundaries, but only for the
+ * cache-line sizes that we support.
+ *
+ */
+ if ((0x08 == vb->cache_line_size) || (0x10 == vb->cache_line_size) ||
+ (0x20 == vb->cache_line_size))
+ {
+ dl->padding = (vb->cache_line_size*sizeof(u32)) - sizeof(*d);
+ } else {
+ dl->padding = 0;
+ }
+
+ dl->desc = pci_alloc_consistent(vb->pdev,
+ (sizeof(*d) + dl->padding) * DRING_SIZE, &dl->desc_dma);
+ if (!dl->desc) {
+ return -ENOMEM;
+ }
+
+ memset(dl->desc, 0, (sizeof(*d) + dl->padding) * DRING_SIZE);
+ for ( i = 0; i < DRING_SIZE; ++i) {
+ d = vb_descriptor(dl, i);
+ d->des1 = des1;
+ }
+ d->des1 |= cpu_to_le32(END_OF_RING);
+ dl->direction = direction;
+ atomic_set(&dl->count, 0);
+ return 0;
+}
+
+static int
+vb_initialize_tx_descriptors(struct voicebus *vb)
+{
+ return vb_initialize_descriptors(
+ vb, &vb->txd, 0xe4800000 | vb->framesize, DMA_TO_DEVICE);
+}
+
+static int
+vb_initialize_rx_descriptors(struct voicebus *vb)
+{
+ return vb_initialize_descriptors(
+ vb, &vb->rxd, vb->framesize, DMA_FROM_DEVICE);
+}
+
+/*! \brief Use to set the minimum number of buffers queued to the hardware
+ * before enabling interrupts.
+ */
+int
+voicebus_set_minlatency(struct voicebus *vb, unsigned int ms)
+{
+ LOCKS_VOICEBUS;
+ /*
+ * One millisecond of latency means that we have 3 buffers pending,
+ * since two are always going to be waiting in the TX fifo on the
+ * interface chip.
+ *
+ */
+#define MESSAGE "%d ms is an invalid value for minumum latency. Setting to %d ms.\n"
+ if ( DRING_SIZE < ms ) {
+ VB_PRINTK(vb, WARNING, MESSAGE, ms, DRING_SIZE);
+ return -EINVAL;
+ } else if (VOICEBUS_DEFAULT_LATENCY > ms ) {
+ VB_PRINTK(vb, WARNING, MESSAGE, ms, VOICEBUS_DEFAULT_LATENCY);
+ return -EINVAL;
+ }
+ VBLOCK(vb);
+ vb->min_tx_buffer_count = ms;
+ VBUNLOCK(vb);
+ return 0;
+}
+
+/*! \brief Returns the number of buffers currently on the transmit queue. */
+int
+voicebus_current_latency(struct voicebus *vb)
+{
+ LOCKS_VOICEBUS;
+ int latency;
+ VBLOCK(vb);
+ latency = vb->min_tx_buffer_count;
+ VBUNLOCK(vb);
+ return latency;
+}
+
+/*!
+ * \brief Read one of the hardware control registers without acquiring locks.
+ */
+static inline u32
+__vb_getctl(struct voicebus *vb, u32 addr)
+{
+ return le32_to_cpu(inl(vb->iobase + addr));
+}
+
+/*!
+ * \brief Read one of the hardware control registers with locks held.
+ */
+static inline u32
+vb_getctl(struct voicebus *vb, u32 addr)
+{
+ LOCKS_VOICEBUS;
+ u32 val;
+ VBLOCK(vb);
+ val = __vb_getctl(vb, addr);
+ VBUNLOCK(vb);
+ return val;
+}
+
+/*!
+ * \brief Returns whether or not the interface is running.
+ *
+ * NOTE: Running in this case means whether or not the hardware reports the
+ * transmit processor in any state but stopped.
+ *
+ * \return 1 of the process is stopped, 0 if running.
+ */
+static int
+vb_is_stopped(struct voicebus *vb)
+{
+ u32 reg;
+ reg = vb_getctl(vb, SR_CSR5);
+ reg = (reg >> 17)&0x38;
+ return (0 == reg) ? 1 : 0;
+}
+
+static void
+vb_cleanup_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl)
+{
+ unsigned int i;
+ struct voicebus_descriptor *d;
+
+ assert(vb_is_stopped(vb));
+
+ for (i=0; i < DRING_SIZE; ++i) {
+ d = vb_descriptor(dl, i);
+ if (d->buffer1) {
+ d->buffer1 = 0;
+ assert(dl->pending[i]);
+ voicebus_free(vb, dl->pending[i]);
+ dl->pending[i] = NULL;
+ }
+ d->des0 &= ~OWN_BIT;
+ }
+ dl->head = 0;
+ dl->tail = 0;
+ atomic_set(&dl->count, 0);
+}
+
+static void
+vb_free_descriptors(struct voicebus *vb, struct voicebus_descriptor_list *dl)
+{
+ if (NULL == dl->desc) {
+ WARN_ON(1);
+ return;
+ }
+ vb_cleanup_descriptors(vb, dl);
+ pci_free_consistent(
+ vb->pdev,
+ (sizeof(struct voicebus_descriptor)+dl->padding)*DRING_SIZE,
+ dl->desc, dl->desc_dma);
+}
+
+/*!
+ * \brief Write one of the hardware control registers without acquiring locks.
+ */
+static inline void
+__vb_setctl(struct voicebus *vb, u32 addr, u32 val)
+{
+ wmb();
+ outl(cpu_to_le32(val), vb->iobase + addr);
+}
+
+/*!
+ * \brief Write one of the hardware control registers with locks held.
+ */
+static inline void
+vb_setctl(struct voicebus *vb, u32 addr, u32 val)
+{
+ LOCKS_VOICEBUS;
+ VBLOCK(vb);
+ __vb_setctl(vb, addr, val);
+ VBUNLOCK(vb);
+}
+
+static int
+__vb_sdi_clk(struct voicebus* vb)
+{
+ unsigned int ret;
+ vb->sdi &= ~CSR9_MDC;
+ __vb_setctl(vb, 0x0048, vb->sdi);
+ ret = __vb_getctl(vb, 0x0048);
+ vb->sdi |= CSR9_MDC;
+ __vb_setctl(vb, 0x0048, vb->sdi);
+ return (ret & CSR9_MDI) ? 1: 0;
+}
+
+static void
+__vb_sdi_sendbits(struct voicebus *vb, u32 bits, int count)
+{
+ vb->sdi &= ~CSR9_MMC;
+ __vb_setctl(vb, 0x0048, vb->sdi);
+ while(count--) {
+ if (bits & (1 << count)) {
+ vb->sdi |= CSR9_MDO;
+ } else {
+ vb->sdi &= ~CSR9_MDO;
+ }
+ __vb_sdi_clk(vb);
+ }
+}
+
+#if 0 /* this function might be useful in the future for debugging. */
+static unsigned int
+__vb_sdi_recvbits(struct voicebus *vb, int count)
+{
+ unsigned int bits=0;
+ vb->sdi |= CSR9_MMC;
+ __vb_setctl(vb, 0x0048, vb->sdi);
+ while(count--) {
+ bits <<= 1;
+ if (__vb_sdi_clk(vb))
+ bits |= 1;
+ else
+ bits &= ~1;
+ }
+ return bits;
+}
+#endif
+
+static void
+vb_setsdi(struct voicebus *vb, int addr, u16 val)
+{
+ LOCKS_VOICEBUS;
+ u32 bits;
+ /* Send preamble */
+ bits = 0xffffffff;
+ VBLOCK(vb);
+ __vb_sdi_sendbits(vb, bits, 32);
+ bits = (0x5 << 12) | (1 << 7) | (addr << 2) | 0x2;
+ __vb_sdi_sendbits(vb, bits, 16);
+ __vb_sdi_sendbits(vb, val, 16);
+ VBUNLOCK(vb);
+}
+
+static void
+vb_enable_io_access(struct voicebus *vb)
+{
+ LOCKS_VOICEBUS;
+ u32 reg;
+ assert(vb->pdev);
+ VBLOCK(vb);
+ pci_read_config_dword(vb->pdev, 0x0004, &reg);
+ reg |= 0x00000007;
+ pci_write_config_dword(vb->pdev, 0x0004, reg);
+ VBUNLOCK(vb);
+}
+
+/*! \todo Insert comments...
+ * context: !in_interrupt()
+ */
+void*
+voicebus_alloc(struct voicebus *vb)
+{
+ void *vbb;
+ vbb = kmem_cache_alloc(vb->buffer_cache, VOICEBUS_ALLOC_FLAGS);
+ return vbb;
+}
+
+void
+voicebus_setdebuglevel(struct voicebus *vb, u32 level)
+{
+ atomic_set(&vb->debuglevel, level);
+}
+
+int
+voicebus_getdebuglevel(struct voicebus *vb)
+{
+ return atomic_read(&vb->debuglevel);
+}
+
+/*! \brief Resets the voicebus hardware interface. */
+static int
+vb_reset_interface(struct voicebus *vb)
+{
+ unsigned long timeout;
+ u32 reg;
+ u32 pci_access;
+ const u32 DEFAULT_PCI_ACCESS = 0xfff80002;
+ BUG_ON(in_interrupt());
+
+ switch (vb->cache_line_size) {
+ case 0x08:
+ pci_access = DEFAULT_PCI_ACCESS | (0x1 << 14);
+ break;
+ case 0x10:
+ pci_access = DEFAULT_PCI_ACCESS | (0x2 << 14);
+ break;
+ case 0x20:
+ pci_access = DEFAULT_PCI_ACCESS | (0x3 << 14);
+ break;
+ default:
+ VB_PRINTK(vb, WARNING, "Host system set a cache size "\
+ "of %d which is not supported. " \
+ "Disabling memory write line and memory read line.",
+ vb->cache_line_size);
+ pci_access = 0xfe584202;
+ break;
+ }
+
+ /* The transmit and receive descriptors will have the same padding. */
+ pci_access |= ((vb->txd.padding / sizeof(u32)) << 2) & 0x7c;
+
+ vb_setctl(vb, 0x0000, pci_access | 1);
+
+ timeout = jiffies + HZ/10; /* 100ms interval */
+ do {
+ reg = vb_getctl(vb, 0x0000);
+ } while ((reg & 0x00000001) && time_before(jiffies, timeout));
+
+ if (reg & 0x00000001) {
+ VB_PRINTK(vb, ERR, "Hardware did not come out of reset "\
+ "within 100ms!");
+ return -EIO;
+ }
+
+ vb_setctl(vb, 0x0000, pci_access);
+
+ vb_cleanup_descriptors(vb, &vb->txd);
+ vb_cleanup_descriptors(vb, &vb->rxd);
+
+ /* Pass bad packets, runt packets, disable SQE function,
+ * store-and-forward */
+ vb_setctl(vb, 0x0030, 0x00280048);
+ /* ...disable jabber and the receive watchdog. */
+ vb_setctl(vb, 0x0078, 0x00000013);
+
+ /* Tell the card where the descriptors are in host memory. */
+ vb_setctl(vb, 0x0020, (u32)vb->txd.desc_dma);
+ vb_setctl(vb, 0x0018, (u32)vb->rxd.desc_dma);
+
+ reg = vb_getctl(vb, 0x00fc);
+ vb_setctl(vb, 0x00fc, (reg & ~0x7) | 0x7);
+ vb_setsdi(vb, 0x00, 0x0100);
+ vb_setsdi(vb, 0x16, 0x2100);
+
+ reg = vb_getctl(vb, 0x00fc);
+
+ vb_setctl(vb, 0x00fc, (reg & ~0x7) | 0x4);
+ vb_setsdi(vb, 0x00, 0x0100);
+ vb_setsdi(vb, 0x16, 0x2100);
+ reg = vb_getctl(vb, 0x00fc);
+
+
+ /*
+ * The calls to setsdi above toggle the reset line of the CPLD. Wait
+ * here to give the CPLD time to stabilize after reset.
+ */
+ mdelay(1);
+
+ return ((reg&0x7) == 0x4) ? 0 : -EIO;
+}
+
+#define OWNED(_d_) (((_d_)->des0)&OWN_BIT)
+#define SET_OWNED(_d_) do { wmb(); (_d_)->des0 |= OWN_BIT; wmb();} while (0)
+
+#ifdef DBG
+static void
+dump_descriptor(struct voicebus *vb, volatile struct voicebus_descriptor *d)
+{
+ VB_PRINTK(vb, DEBUG, "Displaying descriptor at address %08x\n", (unsigned int)d);
+ VB_PRINTK(vb, DEBUG, " des0: %08x\n", d->des0);
+ VB_PRINTK(vb, DEBUG, " des1: %08x\n", d->des1);
+ VB_PRINTK(vb, DEBUG, " buffer1: %08x\n", d->buffer1);
+ VB_PRINTK(vb, DEBUG, " container: %08x\n", d->container);
+}
+
+static void
+show_buffer(struct voicebus *vb, void *vbb)
+{
+ int x;
+ unsigned char *c;
+ c = vbb;
+ printk("Packet %d\n", count);
+ for (x = 1; x <= vb->framesize; ++x) {
+ printk("%02x ", c[x]);
+ if (x % 16 == 0) {
+ printk("\n");
+ }
+ }
+ printk("\n\n");
+}
+#endif
+
+static inline int
+vb_submit(struct voicebus *vb, struct voicebus_descriptor_list *dl, void *vbb)
+{
+ volatile struct voicebus_descriptor *d;
+ unsigned int tail = dl->tail;
+ assert_in_vb_deferred(vb);
+
+ d = vb_descriptor(dl, tail);
+
+ if (unlikely(d->buffer1)) {
+ /* Do not overwrite a buffer that is still in progress. */
+ WARN_ON(1);
+ voicebus_free(vb, vbb);
+ return -EBUSY;
+ }
+
+ dl->pending[tail] = vbb;
+ dl->tail = (++tail) & DRING_MASK;
+ d->buffer1 = dma_map_single(
+ &vb->pdev->dev, vbb, vb->framesize, dl->direction);
+ SET_OWNED(d); /* That's it until the hardware is done with it. */
+ atomic_inc(&dl->count);
+ return 0;
+}
+
+static inline void*
+vb_retrieve(struct voicebus *vb, struct voicebus_descriptor_list *dl)
+{
+ volatile struct voicebus_descriptor *d;
+ void *vbb;
+ unsigned int head = dl->head;
+ assert_in_vb_deferred(vb);
+ d = vb_descriptor(dl, head);
+ if (!OWNED(d)) {
+ dma_unmap_single(&vb->pdev->dev, d->buffer1,
+ vb->framesize, dl->direction);
+ vbb = dl->pending[head];
+ dl->head = (++head) & DRING_MASK;
+ d->buffer1 = 0;
+ atomic_dec(&dl->count);
+ return vbb;
+ } else {
+ return NULL;
+ }
+}
+
+/*!
+ * \brief Give a frame to the hardware to transmit.
+ *
+ */
+int
+voicebus_transmit(struct voicebus *vb, void *vbb)
+{
+ return vb_submit(vb, &vb->txd, vbb);
+}
+
+/*!
+ * \brief Give a frame to the hardware to use for receiving.
+ *
+ */
+static inline int
+vb_submit_rxb(struct voicebus *vb, void *vbb)
+{
+ return vb_submit(vb, &vb->rxd, vbb);
+}
+
+/*!
+ * \brief Remove the next completed transmit buffer (txb) from the tx
+ * descriptor ring.
+ *
+ * NOTE: This function doesn't need any locking because only one instance is
+ * ever running on the deferred processing routine and it only looks at
+ * the head pointer. The deferred routine should only ever be running
+ * on one processor at a time (no multithreaded workqueues allowed!)
+ *
+ * Context: Must be called from the voicebus deferred workqueue.
+ *
+ * \return Pointer to buffer, or NULL if not available.
+ */
+static inline void *
+vb_get_completed_txb(struct voicebus *vb)
+{
+ return vb_retrieve(vb, &vb->txd);
+}
+
+static inline void *
+vb_get_completed_rxb(struct voicebus *vb)
+{
+ return vb_retrieve(vb, &vb->rxd);
+}
+
+/*!
+ * \brief Free a buffer for reuse.
+ *
+ */
+void
+voicebus_free(struct voicebus *vb, void *vbb)
+{
+ kmem_cache_free(vb->buffer_cache, vbb);
+}
+
+/*!
+ * \brief Instruct the hardware to check for a new tx descriptor.
+ */
+inline static void
+__vb_tx_demand_poll(struct voicebus *vb)
+{
+ __vb_setctl(vb, 0x0008, 0x00000000);
+}
+
+/*!
+ * \brief Command the hardware to check if it owns the next transmit
+ * descriptor.
+ */
+static void
+vb_tx_demand_poll(struct voicebus *vb)
+{
+ LOCKS_VOICEBUS;
+ VBLOCK(vb);
+ __vb_tx_demand_poll(vb);
+ VBUNLOCK(vb);
+}
+
+/*!
+ * \brief Command the hardware to check if it owns the next receive
+ * descriptor.
+ */
+inline static void
+__vb_rx_demand_poll(struct voicebus *vb)
+{
+ __vb_setctl(vb, 0x0010, 0x00000000);
+}
+
+static void
+vb_rx_demand_poll(struct voicebus *vb)
+{
+ LOCKS_VOICEBUS;
+ VBLOCK(vb);
+ __vb_rx_demand_poll(vb);
+ VBUNLOCK(vb);
+}
+
+static void
+__vb_enable_interrupts(struct voicebus *vb)
+{
+ __vb_setctl(vb, IER_CSR7, DEFAULT_INTERRUPTS);
+}
+
+static void
+__vb_disable_interrupts(struct voicebus *vb)
+{
+ __vb_setctl(vb, IER_CSR7, 0);
+}
+
+static void
+vb_disable_interrupts(struct voicebus *vb)
+{
+ LOCKS_VOICEBUS;
+ VBLOCK(vb);
+ __vb_disable_interrupts(vb);
+ VBUNLOCK(vb);
+}
+
+/*!
+ * \brief Starts the VoiceBus interface.
+ *
+ * When the VoiceBus interface is started, it is actively transferring
+ * frames to and from the backend of the card. This means the card will
+ * generate interrupts.
+ *
+ * This function should only be called from process context, with interrupts
+ * enabled, since it can sleep while running the self checks.
+ *
+ * \return zero on success. -EBUSY if device is already running.
+ */
+int
+voicebus_start(struct voicebus *vb)
+{
+ LOCKS_VOICEBUS;
+ u32 reg;
+ int i;
+ void *vbb;
+ int ret;
+
+ assert(!in_interrupt());
+
+ if (!vb_is_stopped(vb)) {
+ return -EBUSY;
+ }
+
+ if ((ret=vb_reset_interface(vb))) {
+ return ret;
+ }
+
+ /* We must set up a minimum of three buffers to start with, since two
+ * are immediately read into the TX FIFO, and the descriptor of the
+ * third is read as soon as the first buffer is done.
+ */
+
+ /*
+ * NOTE: The very first buffer after coming out of reset is used to
+ * prime the pump and is lost. So we do not want the client driver to
+ * prepare it, since it will never see the corresponding receive
+ * buffer.
+ * NOTE: handle_transmit is normally only called in the context of the
+ * deferred processing thread. Since the deferred processing thread
+ * is known to not be running at this point, it is safe to call the
+ * handle transmit as if it were.
+ */
+ start_vb_deferred(vb);
+ /* Ensure that all the rx slots are ready for a buffer. */
+ for ( i = 0; i < DRING_SIZE; ++i) {
+ vbb = voicebus_alloc(vb);
+ if (unlikely(NULL == vbb)) {
+ BUG_ON(1);
+ /* \todo I need to make sure the driver can recover
+ * from this condition. .... */
+ } else {
+ vb_submit_rxb(vb, vbb);
+ }
+ }
+
+ for ( i=0; i < vb->min_tx_buffer_count; ++i) {
+ vbb = voicebus_alloc(vb);
+ if (unlikely(NULL == vbb)) {
+ BUG_ON(1);
+ } else {
+ vb->handle_transmit(vbb, vb->context);
+ }
+ }
+ stop_vb_deferred(vb);
+
+ VBLOCK(vb);
+ clear_bit(STOP, &vb->flags);
+#if VOICEBUS_DEFERRED == TIMER
+ vb->timer.expires = jiffies + HZ/1000;
+ add_timer(&vb->timer);
+#else
+ /* Clear the interrupt status register. */
+ __vb_setctl(vb, SR_CSR5, 0xffffffff);
+ __vb_enable_interrupts(vb);
+#endif
+ /* Start the transmit and receive processors. */
+ reg = __vb_getctl(vb, 0x0030);
+ __vb_setctl(vb, 0x0030, reg|0x00002002);
+ /* Tell the interface to poll the tx and rx descriptors. */
+ __vb_rx_demand_poll(vb);
+ __vb_tx_demand_poll(vb);
+ VBUNLOCK(vb);
+
+ assert(!vb_is_stopped(vb));
+
+ return 0;
+}
+
+static void
+vb_clear_start_transmit_bit(struct voicebus *vb)
+{
+ LOCKS_VOICEBUS;
+ u32 reg;
+ VBLOCK(vb);
+ reg = __vb_getctl(vb, NAR_CSR6);
+ reg &= ~0x00002000;
+ __vb_setctl(vb, NAR_CSR6, reg);
+ VBUNLOCK(vb);
+}
+
+static void
+vb_clear_start_receive_bit(struct voicebus *vb)
+{
+ LOCKS_VOICEBUS;
+ u32 reg;
+ VBLOCK(vb);
+ reg = __vb_getctl(vb, NAR_CSR6);
+ reg &= ~0x00000002;
+ __vb_setctl(vb, NAR_CSR6, reg);
+ VBUNLOCK(vb);
+}
+
+unsigned long
+vb_wait_for_completion_timeout(struct completion *x, unsigned long timeout)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
+ /* There is a race condition here. If x->done is reset to 0
+ * before the call to wait_for_completion after this thread wakes.
+ */
+ timeout = wait_event_timeout(x->wait, x->done, timeout);
+ if (timeout) {
+ wait_for_completion(x);
+ }
+ return timeout;
+#else
+ return wait_for_completion_timeout(x, timeout);
+#endif
+}
+
+/*!
+ * \brief Stops the VoiceBus interface.
+ *
+ * Stops the VoiceBus interface and waits for any outstanding DMA transactions
+ * to complete. When this functions returns the VoiceBus interface tx and rx
+ * states will both be suspended.
+ *
+ * Only call this function from process context, with interrupt enabled,
+ * without any locks held since it sleeps.
+ *
+ * \return zero on success, -1 on error.
+ */
+int
+voicebus_stop(struct voicebus *vb)
+{
+ assert(!in_interrupt());
+ if (vb_is_stopped(vb)) {
+ return 0;
+ }
+ INIT_COMPLETION(vb->stopped_completion);
+ set_bit(STOP, &vb->flags);
+ vb_clear_start_transmit_bit(vb);
+ if (vb_wait_for_completion_timeout(&vb->stopped_completion, HZ)) {
+#if VOICEBUS_DEFERRED == TIMER
+ del_timer_sync(&vb->timer);
+#else
+ vb_disable_interrupts(vb);
+#endif
+ assert(vb_is_stopped(vb));
+ clear_bit(STOP, &vb->flags);
+ }
+ else {
+ VB_PRINTK(vb, WARNING, "Timeout while waiting for board to "\
+ "stop.\n");
+ }
+ return 0;
+}
+
+/*!
+ * \brief Prepare the interface for module unload.
+ *
+ * Stop the interface and free all the resources allocated by the driver. The
+ * caller should have returned all VoiceBus buffers to the VoiceBus layer
+ * before calling this function.
+ *
+ * context: !in_interrupt()
+ */
+void
+voicebus_release(struct voicebus *vb)
+{
+ assert(!in_interrupt());
+
+ /* quiesce the hardware */
+ voicebus_stop(vb);
+#if VOICEBUS_DEFERRED == WORKQUEUE
+ destroy_workqueue(vb->workqueue);
+#elif VOICEBUS_DEFERRED == TASKLET
+ tasklet_kill(&vb->tasklet);
+#endif
+ vb_reset_interface(vb);
+#if VOICEBUS_DEFERRED != TIMER
+ free_irq(vb->pdev->irq, vb);
+#endif
+
+ /* Cleanup memory and software resources. */
+ vb_free_descriptors(vb, &vb->txd);
+ vb_free_descriptors(vb, &vb->rxd);
+ kmem_cache_destroy(vb->buffer_cache);
+ release_region(vb->iobase, 0xff);
+ pci_disable_device(vb->pdev);
+ kfree(vb);
+}
+
+void
+__vb_increase_latency(struct voicebus *vb)
+{
+ static int __warn_once = 1;
+ void *vbb;
+ int latency;
+
+ assert_in_vb_deferred(vb);
+
+ latency = atomic_read(&vb->txd.count);
+ if (DRING_SIZE == latency) {
+ if (__warn_once) {
+ /* We must subtract two from this number since there
+ * are always two buffers in the TX FIFO.
+ */
+ VB_PRINTK(vb,ERR,
+ "ERROR: Unable to service card within %d ms "\
+ "and unable to further increase latency.\n",
+ DRING_SIZE-2);
+ __warn_once = 0;
+ }
+ } else {
+ /* Because there are 2 buffers in the transmit FIFO on the
+ * hardware, setting 3 ms of latency means that the host needs
+ * to be able to service the cards within 1ms. This is because
+ * the interface will load up 2 buffers into the TX FIFO then
+ * attempt to read the 3rd descriptor. If the OWN bit isn't
+ * set, then the hardware will set the TX descriptor not
+ * available interrupt.
+ */
+ VB_PRINTK(vb, INFO, "Missed interrupt. " \
+ "Increasing latency to %d ms in order to compensate.\n",
+ latency+1);
+ /* Set the minimum latency in case we're restarted...we don't
+ * want to wait for the buffer to grow to this depth again in
+ * that case.
+ */
+ voicebus_set_minlatency(vb, latency+1);
+ vbb = voicebus_alloc(vb);
+ if (unlikely(NULL == vbb)) {
+ BUG_ON(1);
+ } else {
+ vb->handle_transmit(vbb, vb->context);
+ }
+ }
+}
+
+/*!
+ * \brief Actually process the completed transmit and receive buffers.
+ *
+ * NOTE: This function may be called either from a tasklet, workqueue, or
+ * directly in the interrupt service routine depending on
+ * VOICEBUS_DEFERRED.
+ */
+static inline void
+vb_deferred(struct voicebus *vb)
+{
+ void *vbb;
+#ifdef DBG
+ static int count = 0;
+#endif
+ int stopping = test_bit(STOP, &vb->flags);
+ int underrun = test_bit(TX_UNDERRUN, &vb->flags);
+
+
+ start_vb_deferred(vb);
+ if (unlikely(stopping)) {
+ while((vbb = vb_get_completed_txb(vb))) {
+ voicebus_free(vb, vbb);
+ }
+ while((vbb = vb_get_completed_rxb(vb))) {
+ voicebus_free(vb, vbb);
+ }
+ stop_vb_deferred(vb);
+ return;
+ }
+
+ if (unlikely(underrun)) {
+ /* When we've underrun our FIFO, for some reason we're not
+ * able to keep enough transmit descriptors pending. This can
+ * happen if either interrupts or this deferred processing
+ * function is not run soon enough (within 1ms when using the
+ * default 3 transmit buffers to start). In this case, we'll
+ * insert an additional transmit buffer onto the descriptor
+ * list which decreases the sensitivity to latency, but also
+ * adds more delay to the TDM and SPI data.
+ */
+ __vb_increase_latency(vb);
+ }
+
+ /* Always handle the transmit buffers first. */
+ while ((vbb = vb_get_completed_txb(vb))) {
+ vb->handle_transmit(vbb, vb->context);
+ }
+
+ if (unlikely(underrun)) {
+ vb_rx_demand_poll(vb);
+ vb_tx_demand_poll(vb);
+ clear_bit(TX_UNDERRUN, &vb->flags);
+ }
+
+ while ((vbb = vb_get_completed_rxb(vb))) {
+ vb->handle_receive(vbb, vb->context);
+ vb_submit_rxb(vb, vbb);
+ }
+
+ stop_vb_deferred(vb);
+}
+
+
+/*!
+ * \brief Interrupt handler for VoiceBus interface.
+ *
+ * NOTE: This handler is optimized for the case where only a single interrupt
+ * condition will be generated at a time.
+ *
+ * ALSO NOTE: Only access the interrupt status register from this function
+ * since it doesn't employ any locking on the voicebus interface.
+ */
+static irqreturn_t
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+vb_isr(int irq, void *dev_id, struct pt_regs *regs)
+#else
+vb_isr(int irq, void *dev_id)
+#endif
+{
+ struct voicebus *vb = dev_id;
+ u32 int_status;
+
+ int_status = __vb_getctl(vb, SR_CSR5);
+ /* Mask out the reserved bits. */
+ int_status &= ~(0xfc004010);
+ int_status &= 0x7fff;
+
+ if (!int_status) {
+ return IRQ_NONE;
+ }
+
+ if (likely(int_status & TX_COMPLETE_INTERRUPT)) {
+ /* ******************************************************** */
+ /* NORMAL INTERRUPT CASE */
+ /* ******************************************************** */
+# if VOICEBUS_DEFERRED == WORKQUEUE
+ queue_work(vb->workqueue, &vb->workitem);
+# elif VOICEBUS_DEFERRED == TASKLET
+ tasklet_schedule(&vb->tasklet);
+# else
+ vb_deferred(vb);
+# endif
+ __vb_setctl(vb, SR_CSR5, TX_COMPLETE_INTERRUPT);
+ } else {
+ /* ******************************************************** */
+ /* ABNORMAL / ERROR CONDITIONS */
+ /* ******************************************************** */
+ if ((int_status & TX_UNAVAILABLE_INTERRUPT) ) {
+ /* This can happen if the host fails to service the
+ * interrupt within the required time interval (1ms
+ * for each buffer on the queue). Increasing the
+ * depth of the tx queue (up to a maximum of
+ * DRING_SIZE) can make the driver / system more
+ * tolerant of interrupt latency under periods of
+ * heavy system load, but also increases the general
+ * latency that the driver adds to the voice
+ * conversations.
+ */
+ set_bit(TX_UNDERRUN, &vb->flags);
+# if VOICEBUS_DEFERRED == WORKQUEUE
+ queue_work(vb->workqueue, &vb->workitem);
+# elif VOICEBUS_DEFERRED == TASKLET
+ tasklet_schedule(&vb->tasklet);
+# else
+ vb_deferred(vb);
+# endif
+ }
+
+ if (int_status & FATAL_BUS_ERROR_INTERRUPT) {
+ VB_PRINTK(vb, ERR, "Fatal Bus Error detected!\n");
+ }
+
+ if (int_status & TX_STOPPED_INTERRUPT) {
+ assert(test_bit(STOP, &vb->flags));
+ vb_clear_start_receive_bit(vb);
+ __vb_setctl(vb, SR_CSR5, DEFAULT_INTERRUPTS);
+ __vb_disable_interrupts(vb);
+ complete(&vb->stopped_completion);
+ }
+ if (int_status & RX_STOPPED_INTERRUPT) {
+ assert(test_bit(STOP, &vb->flags));
+ if (vb_is_stopped(vb)) {
+ complete(&vb->stopped_completion);
+ }
+ }
+
+ /* Clear the interrupt(s) */
+ __vb_setctl(vb, SR_CSR5, int_status);
+ }
+
+ return IRQ_HANDLED;
+}
+
+#if VOICEBUS_DEFERRED == TIMER
+/*! \brief Called if the deferred processing is to happen in the context of
+ * the timer.
+ */
+static void
+vb_timer(unsigned long data)
+{
+ unsigned long start = jiffies;
+ struct voicebus *vb = (struct voicebus *)data;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ vb_isr(0, vb, 0);
+#else
+ vb_isr(0, vb);
+#endif
+ if (!vb_is_stopped(vb)) {
+ vb->timer.expires = start + HZ/1000;
+ add_timer(&vb->timer);
+ }
+}
+#endif
+
+#if VOICEBUS_DEFERRED == WORKQUEUE
+static void
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+vb_workfunc(void *data)
+{
+ struct voicebus *vb = data;
+#else
+vb_workfunc(struct work_struct *work)
+{
+ struct voicebus *vb = container_of(work, struct voicebus, workitem);
+#endif
+ vb_deferred(vb);
+}
+#elif VOICEBUS_DEFERRED == TASKLET
+static void
+vb_tasklet(unsigned long data)
+{
+ struct voicebus *vb = (struct voicebus*)data;
+ vb_deferred(vb);
+}
+#endif /* #if VOICEBUS_DEFERRED == WORKQUEUE */
+
+/*!
+ * \brief Initalize the voicebus interface.
+ *
+ * This function must be called in process context since it may sleep.
+ * \todo Complete this description.
+ */
+int
+voicebus_init(struct pci_dev *pdev, u32 framesize,
+ const char *board_name,
+ void (*handle_receive)(void *vbb, void *context),
+ void (*handle_transmit)(void *vbb, void *context),
+ void *context,
+ struct voicebus **vbp
+ )
+{
+ int retval = 0;
+ struct voicebus *vb;
+
+ assert(NULL != pdev);
+ assert(NULL != board_name);
+ assert(framesize);
+ assert(NULL != handle_receive);
+ assert(NULL != handle_transmit);
+
+ /* ----------------------------------------------------------------
+ Initialize the pure software constructs.
+ ---------------------------------------------------------------- */
+ *vbp = NULL;
+ vb = kmalloc(sizeof(*vb), GFP_KERNEL);
+ if (NULL == vb) {
+ VB_PRINTK(vb, DEBUG, "Failed to allocate memory for voicebus "\
+ "interface.\n");
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ memset(vb,0,sizeof(*vb));
+ /* \todo make sure there is a note that the caller needs to make sure
+ * board_name stays in memory until voicebus_release is called.
+ */
+ vb->board_name = board_name;
+ spin_lock_init(&vb->lock);
+ init_completion(&vb->stopped_completion);
+ vb->pdev = pdev;
+ set_bit(STOP, &vb->flags);
+ clear_bit(IN_DEFERRED_PROCESSING, &vb->flags);
+ vb->framesize = framesize;
+ vb->min_tx_buffer_count = VOICEBUS_DEFAULT_LATENCY;
+
+#if VOICEBUS_DEFERRED == WORKQUEUE
+ /* NOTE: This workqueue must be single threaded because locking is not
+ * used when buffers are removed or added to the descriptor list, and
+ * there should only be one producer / consumer (the hardware or the
+ * deferred processing function). */
+ vb->workqueue = create_singlethread_workqueue(board_name);
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ INIT_WORK(&vb->workitem, vb_workfunc, vb);
+# else
+ INIT_WORK(&vb->workitem, vb_workfunc);
+# endif
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
+ vb_set_workqueue_priority(vb);
+# endif
+#elif VOICEBUS_DEFERRED == TASKLET
+ tasklet_init(&vb->tasklet, vb_tasklet, (unsigned long)vb);
+#elif VOICEBUS_DEFERRED == TIMER
+ init_timer(&vb->timer);
+ vb->timer.function = vb_timer;
+ vb->timer.data = (unsigned long)vb;
+#endif
+
+ vb->handle_receive = handle_receive;
+ vb->handle_transmit = handle_transmit;
+ vb->context = context;
+
+ /* \todo This cache should be shared by all instances supported by
+ * this driver. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
+ vb->buffer_cache = kmem_cache_create(board_name, vb->framesize, 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+#else
+ vb->buffer_cache = kmem_cache_create(board_name, vb->framesize, 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+#endif
+ if (NULL == vb->buffer_cache) {
+ VB_PRINTK(vb, ERR, "Failed to allocate buffer cache.\n");
+ goto cleanup;
+ }
+
+
+ /* ----------------------------------------------------------------
+ Configure the hardware / kernel module interfaces.
+ ---------------------------------------------------------------- */
+ if (pci_read_config_byte(vb->pdev, 0x0c, &vb->cache_line_size)) {
+ VB_PRINTK(vb, ERR, "Failed read of cache line " \
+ "size from PCI configuration space.\n");
+ goto cleanup;
+ }
+
+ if (pci_enable_device(pdev)) {
+ VB_PRINTK(vb, ERR, "Failed call to pci_enable_device.\n");
+ retval = -EIO;
+ goto cleanup;
+ }
+
+ /* \todo This driver should be modified to use the memory mapped I/O
+ as opposed to IO space for portability and performance. */
+ if (0 == (pci_resource_flags(pdev, 0)&IORESOURCE_IO)) {
+ VB_PRINTK(vb, ERR, "BAR0 is not IO Memory.\n");
+ retval = -EIO;
+ goto cleanup;
+ }
+ vb->iobase = pci_resource_start(pdev, 0);
+ if(NULL == request_region(vb->iobase, 0xff, board_name)) {
+ VB_PRINTK(vb, ERR, "IO Registers are in use by another " \
+ "module.\n");
+ retval = -EIO;
+ goto cleanup;
+ }
+
+ if ((retval = vb_initialize_tx_descriptors(vb))) {
+ goto cleanup;
+ }
+ if ((retval = vb_initialize_rx_descriptors(vb))) {
+ goto cleanup;
+ }
+
+ /* ----------------------------------------------------------------
+ Configure the hardware interface.
+ ---------------------------------------------------------------- */
+ pci_set_master(pdev);
+ vb_enable_io_access(vb);
+
+#if VOICEBUS_DEFERRED != TIMER
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+# define VB_IRQ_SHARED SA_SHIRQ
+#else
+# define VB_IRQ_SHARED IRQF_SHARED
+#endif
+ if (request_irq(pdev->irq, vb_isr, VB_IRQ_SHARED, vb->board_name,
+ vb)) {
+ assert(0);
+ goto cleanup;
+ }
+#endif
+
+ *vbp = vb;
+ return retval;
+cleanup:
+ if (NULL == vb) {
+ return retval;
+ }
+#if VOICEBUS_DEFERRED == WORKQUEUE
+ if (vb->workqueue) {
+ destroy_workqueue(vb->workqueue);
+ }
+#elif VOICEBUS_DEFERRED == TASKLET
+ tasklet_kill(&vb->tasklet);
+#endif
+ /* Cleanup memory and software resources. */
+ if (vb->txd.desc) {
+ vb_free_descriptors(vb, &vb->txd);
+ }
+ if (vb->rxd.desc) {
+ vb_free_descriptors(vb, &vb->rxd);
+ }
+ if (vb->buffer_cache) {
+ kmem_cache_destroy(vb->buffer_cache);
+ }
+ if (vb->iobase) {
+ release_region(vb->iobase, 0xff);
+ }
+ if (vb->pdev) {
+ pci_disable_device(vb->pdev);
+ }
+ kfree(vb);
+ assert(0 != retval);
+ return retval;
+}
+
+
+/*! \brief Return the pci_dev in use by this voicebus interface. */
+struct pci_dev *
+voicebus_get_pci_dev(struct voicebus *vb)
+{
+ return vb->pdev;
+}
diff --git a/kernel/voicebus.h b/kernel/voicebus.h
new file mode 100644
index 0000000..c37b2b8
--- /dev/null
+++ b/kernel/voicebus.h
@@ -0,0 +1,53 @@
+/*
+ * VoiceBus(tm) Interface Library.
+ *
+ * Written by Shaun Ruffell <sruffell@digium.com>
+ * and based on previous work by Mark Spencer <markster@digium.com>,
+ * Matthew Fredrickson <creslin@digium.com>, and
+ * Michael Spiceland <mspiceland@digium.com>
+ *
+ * Copyright (C) 2007-2008 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.
+ *
+ */
+#ifndef __VOICEBUS_H__
+#define __VOICEBUS_H__
+
+struct voicebus;
+
+#define VOICEBUS_DEFAULT_LATENCY 3
+
+void voicebus_setdebuglevel(struct voicebus *vb, u32 level);
+int voicebus_getdebuglevel(struct voicebus *vb);
+struct pci_dev * voicebus_get_pci_dev(struct voicebus *vb);
+int voicebus_init(struct pci_dev* pdev, u32 framesize,
+ const char *board_name,
+ void (*handle_receive)(void *buffer, void *context),
+ void (*handle_transmit)(void *buffer, void *context),
+ void *context,
+ struct voicebus **vb_p);
+void voicebus_release(struct voicebus *vb);
+int voicebus_start(struct voicebus *vb);
+int voicebus_stop(struct voicebus *vb);
+void * voicebus_alloc(struct voicebus* vb);
+void voicebus_free(struct voicebus *vb, void *vbb);
+int voicebus_transmit(struct voicebus *vb, void *vbb);
+int voicebus_set_minlatency(struct voicebus *vb, unsigned int milliseconds);
+int voicebus_current_latency(struct voicebus *vb) ;
+
+#endif /* __VOICEBUS_H__ */
diff --git a/kernel/wctdm.c b/kernel/wctdm.c
index 02ffa40..f5a1118 100644
--- a/kernel/wctdm.c
+++ b/kernel/wctdm.c
@@ -108,101 +108,10 @@ static alpha indirect_regs[] =
{43,66,"LOOP_CLOSE_TRES_LOW",0x1000},
};
-static struct fxo_mode {
- char *name;
- /* FXO */
- 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, },
-};
-
#include "zaptel.h"
+#include "fxo_modes.h"
+
#ifdef LINUX26
#include <linux/moduleparam.h>
#endif
@@ -249,14 +158,7 @@ static struct fxo_mode {
#define DEFAULT_RING_DEBOUNCE 64 /* Ringer Debounce (64 ms) */
-/* the constants below control the 'debounce' periods enforced by the
- check_hook routines; these routines are called once every 4 interrupts
- (the interrupt cycles around the four modules), so the periods are
- specified in _4 millisecond_ increments
-*/
-#define DEFAULT_BATT_DEBOUNCE 4 /* Battery debounce (64 ms) */
-#define POLARITY_DEBOUNCE 4 /* Polarity debounce (64 ms) */
-#define DEFAULT_BATT_THRESH 3 /* Anything under this is "no battery" */
+#define POLARITY_DEBOUNCE 64 /* Polarity debounce (64 ms) */
#define OHT_TIMER 6000 /* How long after RING to retain OHT */
@@ -285,6 +187,12 @@ enum proslic_power_warn {
PROSLIC_POWER_WARNED,
};
+enum battery_state {
+ BATTERY_UNKNOWN = 0,
+ BATTERY_PRESENT,
+ BATTERY_LOST,
+};
+
struct wctdm {
struct pci_dev *dev;
char *variety;
@@ -303,7 +211,7 @@ struct wctdm {
spinlock_t lock;
union {
- struct {
+ struct fxo {
#ifdef AUDIO_RINGCHECK
unsigned int pegtimer;
int pegcount;
@@ -315,14 +223,14 @@ struct wctdm {
#endif
int ringdebounce;
int offhook;
- int battdebounce;
- int nobatttimer;
- int battery;
+ unsigned int battdebounce;
+ unsigned int battalarm;
+ enum battery_state battery;
int lastpol;
int polarity;
int polaritydebounce;
} fxo;
- struct {
+ struct fxs {
int oldrxhook;
int debouncehook;
int lastrxhook;
@@ -364,8 +272,9 @@ static struct wctdm *ifaces[WC_MAX_IFACES];
static void wctdm_release(struct wctdm *wc);
-static int battdebounce = DEFAULT_BATT_DEBOUNCE;
-static int battthresh = DEFAULT_BATT_THRESH;
+static unsigned int battdebounce;
+static unsigned int battalarm;
+static unsigned int battthresh;
static int ringdebounce = DEFAULT_RING_DEBOUNCE;
static int fwringdetect = 0;
static int debug = 0;
@@ -834,11 +743,15 @@ static inline void wctdm_proslic_recheck_sanity(struct wctdm *wc, int card)
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 poopy = 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)) {
@@ -848,7 +761,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
poopy++;
}
b &= 0x9b;
- if (wc->mod[card].fxo.offhook) {
+ if (fxo->offhook) {
if (b != 0x9)
wctdm_setreg(wc, card, 5, 0x9);
} else {
@@ -857,150 +770,182 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
}
if (poopy)
return;
-#ifndef AUDIO_RINGCHECK
- if (!wc->mod[card].fxo.offhook) {
+ if (!fxo->offhook) {
if (fwringdetect) {
res = wc->reg0shadow[card] & 0x60;
- if (wc->mod[card].fxo.ringdebounce--) {
- if (res && (res != wc->mod[card].fxo.lastrdtx) && (wc->mod[card].fxo.battery == 1)) {
- if (!wc->mod[card].fxo.wasringing) {
- wc->mod[card].fxo.wasringing = 1;
+ if (fxo->ringdebounce--) {
+ if (res && (res != fxo->lastrdtx) &&
+ (fxo->battery == BATTERY_PRESENT)) {
+ if (!fxo->wasringing) {
+ fxo->wasringing = 1;
if (debug)
printk("RING on %d/%d!\n", wc->span.spanno, card + 1);
zt_hooksig(&wc->chans[card], ZT_RXSIG_RING);
}
- wc->mod[card].fxo.lastrdtx = res;
- wc->mod[card].fxo.ringdebounce = 10;
+ fxo->lastrdtx = res;
+ fxo->ringdebounce = 10;
} else if (!res) {
- if ((wc->mod[card].fxo.ringdebounce == 0) && wc->mod[card].fxo.wasringing) {
- wc->mod[card].fxo.wasringing = 0;
+ if ((fxo->ringdebounce == 0) && fxo->wasringing) {
+ fxo->wasringing = 0;
if (debug)
printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1);
zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
}
}
- } else if (res && (wc->mod[card].fxo.battery == 1)) {
- wc->mod[card].fxo.lastrdtx = res;
- wc->mod[card].fxo.ringdebounce = 10;
+ } else if (res && (fxo->battery == BATTERY_PRESENT)) {
+ fxo->lastrdtx = res;
+ fxo->ringdebounce = 10;
}
} else {
res = wc->reg0shadow[card];
- if ((res & 0x60) && (wc->mod[card].fxo.battery == 1)) {
- wc->mod[card].fxo.ringdebounce += (ZT_CHUNKSIZE * 16);
- if (wc->mod[card].fxo.ringdebounce >= ZT_CHUNKSIZE * ringdebounce) {
- if (!wc->mod[card].fxo.wasringing) {
- wc->mod[card].fxo.wasringing = 1;
+ if ((res & 0x60) && (fxo->battery == BATTERY_PRESENT)) {
+ fxo->ringdebounce += (ZT_CHUNKSIZE * 16);
+ if (fxo->ringdebounce >= ZT_CHUNKSIZE * ringdebounce) {
+ if (!fxo->wasringing) {
+ fxo->wasringing = 1;
zt_hooksig(&wc->chans[card], ZT_RXSIG_RING);
if (debug)
printk("RING on %d/%d!\n", wc->span.spanno, card + 1);
}
- wc->mod[card].fxo.ringdebounce = ZT_CHUNKSIZE * ringdebounce;
+ fxo->ringdebounce = ZT_CHUNKSIZE * ringdebounce;
}
} else {
- wc->mod[card].fxo.ringdebounce -= ZT_CHUNKSIZE * 4;
- if (wc->mod[card].fxo.ringdebounce <= 0) {
- if (wc->mod[card].fxo.wasringing) {
- wc->mod[card].fxo.wasringing = 0;
+ fxo->ringdebounce -= ZT_CHUNKSIZE * 4;
+ if (fxo->ringdebounce <= 0) {
+ if (fxo->wasringing) {
+ fxo->wasringing = 0;
zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
if (debug)
printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1);
}
- wc->mod[card].fxo.ringdebounce = 0;
+ fxo->ringdebounce = 0;
}
}
}
}
-#endif
+
b = wc->reg1shadow[card];
-#if 0
- {
- static int count = 0;
- if (!(count++ % 100)) {
- printk("Card %d: Voltage: %d Debounce %d\n", card + 1,
- b, wc->mod[card].fxo.battdebounce);
- }
- }
-#endif
+
if (abs(b) < battthresh) {
- wc->mod[card].fxo.nobatttimer++;
-#if 0
- if (wc->mod[card].fxo.battery == 1)
- printk("Battery loss: %d (%d debounce)\n", b, wc->mod[card].fxo.battdebounce);
-#endif
- if (wc->mod[card].fxo.battery && !wc->mod[card].fxo.battdebounce) {
- if (debug)
- printk("NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1);
- wc->mod[card].fxo.battery = 0;
+ /* 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("NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1);
#ifdef JAPAN
- if ((!wc->ohdebounce) && wc->offhook) {
- zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK);
- if (debug)
- printk("Signalled On Hook\n");
+ if (!wc->ohdebounce && wc->offhook) {
+ zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK);
+ if (debug)
+ printk("Signalled On Hook\n");
#ifdef ZERO_BATT_RING
- wc->onhook++;
+ wc->onhook++;
#endif
- }
+ }
#else
- zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK);
- zt_alarm_channel(&wc->chans[card], ZT_ALARM_RED);
+ zt_hooksig(&wc->chans[card], ZT_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
- wc->mod[card].fxo.battdebounce = battdebounce;
- } else if (!wc->mod[card].fxo.battery)
- wc->mod[card].fxo.battdebounce = battdebounce;
- } else if (abs(b) > battthresh) {
- if ((wc->mod[card].fxo.battery < 1) && !wc->mod[card].fxo.battdebounce) {
- if (debug)
- printk("BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1,
- (b < 0) ? "-" : "+");
-#ifdef ZERO_BATT_RING
- if (wc->onhook) {
- wc->onhook = 0;
- zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
- if (debug)
- printk("Signalled Off Hook\n");
+ }
+ } 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("BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1,
+ (b < 0) ? "-" : "+");
+#ifdef ZERO_BATT_RING
+ if (wc->onhook) {
+ wc->onhook = 0;
+ zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
+ if (debug)
+ printk("Signalled Off Hook\n");
+ }
#else
- zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
- zt_alarm_channel(&wc->chans[card], ZT_ALARM_NONE);
+ zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
#endif
- wc->mod[card].fxo.battery = 1;
- wc->mod[card].fxo.nobatttimer = 0;
- wc->mod[card].fxo.battdebounce = battdebounce;
- } else if (wc->mod[card].fxo.battery == 1)
- wc->mod[card].fxo.battdebounce = battdebounce;
-
- if (wc->mod[card].fxo.lastpol >= 0) {
- if (b < 0) {
- wc->mod[card].fxo.lastpol = -1;
- wc->mod[card].fxo.polaritydebounce = POLARITY_DEBOUNCE;
- }
+ /* 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 (wc->mod[card].fxo.lastpol <= 0) {
- if (b > 0) {
- wc->mod[card].fxo.lastpol = 1;
- wc->mod[card].fxo.polaritydebounce = POLARITY_DEBOUNCE;
- }
+ if (fxo->lastpol <= 0) {
+ if (b > 0) {
+ fxo->lastpol = 1;
+ fxo->polaritydebounce = POLARITY_DEBOUNCE / MS_PER_CHECK_HOOK;
+ }
}
- } else {
- /* It's something else... */
- wc->mod[card].fxo.battdebounce = battdebounce;
- }
- if (wc->mod[card].fxo.battdebounce)
- wc->mod[card].fxo.battdebounce--;
- if (wc->mod[card].fxo.polaritydebounce) {
- wc->mod[card].fxo.polaritydebounce--;
- if (wc->mod[card].fxo.polaritydebounce < 1) {
- if (wc->mod[card].fxo.lastpol != wc->mod[card].fxo.polarity) {
+ }
+
+ if (fxo->battalarm) {
+ if (--fxo->battalarm == 0) {
+ /* the alarm timer has expired, so update the battery alarm state
+ for this channel */
+ zt_alarm_channel(&wc->chans[card], fxo->battery ? ZT_ALARM_NONE : ZT_ALARM_RED);
+ }
+ }
+
+ if (fxo->polaritydebounce) {
+ if (--fxo->polaritydebounce == 0) {
+ if (fxo->lastpol != fxo->polarity) {
if (debug)
printk("%lu Polarity reversed (%d -> %d)\n", jiffies,
- wc->mod[card].fxo.polarity,
- wc->mod[card].fxo.lastpol);
- if (wc->mod[card].fxo.polarity)
- zt_qevent_lock(&wc->chans[card], ZT_EVENT_POLARITY);
- wc->mod[card].fxo.polarity = wc->mod[card].fxo.lastpol;
+ fxo->polarity,
+ fxo->lastpol);
+ if (fxo->polarity)
+ zt_qevent_lock(&wc->chans[card], ZT_EVENT_POLARITY);
+ fxo->polarity = fxo->lastpol;
}
}
}
+#undef MS_PER_CHECK_HOOK
}
static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card)
@@ -1607,9 +1552,6 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual,
if(debug)
printk("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));
- /* battery state still unknown */
- wc->mod[card].fxo.battery = -1;
-
return 0;
}
@@ -2514,7 +2456,8 @@ static int __init wctdm_init(void)
{
int res;
int x;
- for (x=0;x<(sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) {
+
+ for (x = 0; x < (sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) {
if (!strcmp(fxo_modes[x].name, opermode))
break;
}
@@ -2522,16 +2465,30 @@ static int __init wctdm_init(void)
_opermode = x;
} else {
printk("Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode);
- for (x=0;x<sizeof(fxo_modes) / sizeof(fxo_modes[0]); x++)
+ for (x = 0; x < sizeof(fxo_modes) / sizeof(fxo_modes[0]); x++)
printk(" %s\n", fxo_modes[x].name);
printk("Note this option is CASE SENSITIVE!\n");
return -ENODEV;
}
- if (!strcmp(fxo_modes[_opermode].name, "AUSTRALIA")) {
- boostringer=1;
- fxshonormode=1;
+
+ if (!strcmp(opermode, "AUSTRALIA")) {
+ boostringer = 1;
+ fxshonormode = 1;
+ }
+
+ /* for the voicedaa_check_hook defaults, if the user has not overridden
+ them by specifying them as module parameters, then get the values
+ from the selected operating mode
+ */
+ 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 = zap_pci_module(&wctdm_driver);
if (res)
@@ -2549,15 +2506,15 @@ module_param(debug, int, 0600);
module_param(loopcurrent, int, 0600);
module_param(reversepolarity, int, 0600);
module_param(robust, int, 0600);
-module_param(_opermode, 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, int, 0600);
-module_param(battthresh, int, 0600);
+module_param(battdebounce, uint, 0600);
+module_param(battalarm, uint, 0600);
+module_param(battthresh, uint, 0600);
module_param(ringdebounce, int, 0600);
module_param(fwringdetect, int, 0600);
module_param(alawoverride, int, 0600);
@@ -2571,7 +2528,6 @@ MODULE_PARM(debug, "i");
MODULE_PARM(loopcurrent, "i");
MODULE_PARM(reversepolarity, "i");
MODULE_PARM(robust, "i");
-MODULE_PARM(_opermode, "i");
MODULE_PARM(opermode, "s");
MODULE_PARM(timingonly, "i");
MODULE_PARM(lowpower, "i");
@@ -2579,6 +2535,7 @@ MODULE_PARM(boostringer, "i");
MODULE_PARM(fastringer, "i");
MODULE_PARM(fxshonormode, "i");
MODULE_PARM(battdebounce, "i");
+MODULE_PARM(battalarm, "i");
MODULE_PARM(battthresh, "i");
MODULE_PARM(ringdebounce, "i");
MODULE_PARM(fwringdetect, "i");
diff --git a/kernel/wctdm24xxp/Kbuild b/kernel/wctdm24xxp/Kbuild
index 3bdbe19..6698a87 100644
--- a/kernel/wctdm24xxp/Kbuild
+++ b/kernel/wctdm24xxp/Kbuild
@@ -8,7 +8,7 @@ ifeq ($(HOTPLUG_FIRMWARE),yes)
EXTRA_CFLAGS+=-DHOTPLUG_FIRMWARE
endif
-wctdm24xxp-objs := base.o GpakCust.o GpakApi.o
+wctdm24xxp-objs := base.o GpakCust.o GpakApi.o ../voicebus.o
ifneq ($(HOTPLUG_FIRMWARE),yes)
wctdm24xxp-objs += $(FIRM_DIR)/zaptel-fw-vpmadt032.o
diff --git a/kernel/wctdm24xxp/base.c b/kernel/wctdm24xxp/base.c
index bd47e23..ca53fa9 100644
--- a/kernel/wctdm24xxp/base.c
+++ b/kernel/wctdm24xxp/base.c
@@ -4,7 +4,7 @@
* Written by Mark Spencer <markster@digium.com>
* Support for TDM800P and VPM150M by Matthew Fredrickson <creslin@digium.com>
*
- * Copyright (C) 2005,2006, Digium, Inc.
+ * Copyright (C) 2005 - 2008 Digium, Inc.
* All rights reserved.
*
* Sections for QRV cards written by Jim Dixon <jim@lambdatel.com>
@@ -146,102 +146,11 @@ static int ectrans[4] = { 0, 1, 3, 2 };
enable for normal operation! */
/* #define PAQ_DEBUG */
-static struct fxo_mode {
- char *name;
- /* FXO */
- 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, },
-};
-
#define DEBUG_CARD (1 << 0)
#define DEBUG_ECHOCAN (1 << 1)
+#include "fxo_modes.h"
+
struct wctdm_desc {
char *name;
int flags;
@@ -262,8 +171,9 @@ spinlock_t ifacelock = SPIN_LOCK_UNLOCKED;
static void wctdm_release(struct wctdm *wc);
static int fxovoltage = 0;
-static int battdebounce = DEFAULT_BATT_DEBOUNCE;
-static int battthresh = DEFAULT_BATT_THRESH;
+static unsigned int battdebounce;
+static unsigned int battalarm;
+static unsigned int battthresh;
static int debug = 0;
static int robust = 0;
static int lowpower = 0;
@@ -281,6 +191,7 @@ static int fxsrxgain = 0;
static int nativebridge = 0;
static int ringdebounce = DEFAULT_RING_DEBOUNCE;
static int fwringdetect = 0;
+static int latency = VOICEBUS_DEFAULT_LATENCY;
#ifdef VPM_SUPPORT
static int vpmsupport = 1;
static int vpmdtmfsupport = 0;
@@ -746,29 +657,26 @@ static inline void cmd_checkisr(struct wctdm *wc, int card)
}
}
-static inline void wctdm_transmitprep(struct wctdm *wc, int dbl)
+static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char *writechunk)
{
- volatile unsigned char *writechunk;
int x,y;
- dbl = dbl % 2;
-
- writechunk = (volatile unsigned char *)(wc->writechunk);
- if (dbl)
- /* Write is at interrupt address. Start writing from normal offset */
- writechunk += SFRAME_SIZE;
-
/* Calculate Transmission */
- zt_transmit(&wc->span);
+ if (likely(wc->initialized)) {
+ zt_transmit(&wc->span);
+ }
for (x=0;x<ZT_CHUNKSIZE;x++) {
/* Send a sample, as a 32-bit word */
for (y=0;y < wc->cards;y++) {
- if (!x)
+ if (!x) {
cmd_checkisr(wc, y);
+ }
- if (y < wc->type)
- writechunk[y] = wc->chans[y].writechunk[x];
+ if (likely(wc->initialized)) {
+ if (y < wc->type)
+ writechunk[y] = wc->chans[y].writechunk[x];
+ }
cmd_dequeue(wc, writechunk, y, x);
}
#ifdef VPM_SUPPORT
@@ -776,8 +684,9 @@ static inline void wctdm_transmitprep(struct wctdm *wc, int dbl)
wc->blinktimer++;
if (wc->vpm) {
for (y=24;y<28;y++) {
- if (!x)
+ if (!x) {
cmd_checkisr(wc, y);
+ }
cmd_dequeue(wc, writechunk, y, x);
}
#ifdef FANCY_ECHOCAN
@@ -808,24 +717,6 @@ static inline void wctdm_transmitprep(struct wctdm *wc, int dbl)
}
}
-static inline void __wctdm_setctl(struct wctdm *wc, unsigned int addr, unsigned int val)
-{
- outl(val, wc->iobase + addr);
-}
-
-static inline unsigned int __wctdm_getctl(struct wctdm *wc, unsigned int addr)
-{
- return inl(wc->iobase + addr);
-}
-
-static inline void wctdm_setctl(struct wctdm *wc, unsigned int addr, unsigned int val)
-{
- unsigned long flags;
- spin_lock_irqsave(&wc->reglock, flags);
- __wctdm_setctl(wc, addr, val);
- spin_unlock_irqrestore(&wc->reglock, flags);
-}
-
static inline int wctdm_setreg_full(struct wctdm *wc, int card, int addr, int val, int inisr)
{
unsigned long flags;
@@ -902,96 +793,6 @@ static inline int wctdm_getreg(struct wctdm *wc, int card, int addr)
return ret;
}
-static inline unsigned int wctdm_getctl(struct wctdm *wc, unsigned int addr)
-{
- unsigned long flags;
- unsigned int val;
- spin_lock_irqsave(&wc->reglock, flags);
- val = __wctdm_getctl(wc, addr);
- spin_unlock_irqrestore(&wc->reglock, flags);
- return val;
-}
-
-static inline int __wctdm_sdi_clk(struct wctdm *wc)
-{
- unsigned int ret;
- wc->sdi &= ~SDI_CLK;
- __wctdm_setctl(wc, 0x0048, wc->sdi);
- ret = __wctdm_getctl(wc, 0x0048);
- wc->sdi |= SDI_CLK;
- __wctdm_setctl(wc, 0x0048, wc->sdi);
- return ret & SDI_DIN;
-}
-
-static inline void __wctdm_sdi_sendbits(struct wctdm *wc, unsigned int bits, int count)
-{
- wc->sdi &= ~SDI_DREAD;
- __wctdm_setctl(wc, 0x0048, wc->sdi);
- while(count--) {
- if (bits & (1 << count))
- wc->sdi |= SDI_DOUT;
- else
- wc->sdi &= ~SDI_DOUT;
- __wctdm_sdi_clk(wc);
- }
-}
-
-static inline unsigned int __wctdm_sdi_recvbits(struct wctdm *wc, int count)
-{
- unsigned int bits=0;
- wc->sdi |= SDI_DREAD;
- __wctdm_setctl(wc, 0x0048, wc->sdi);
- while(count--) {
- bits <<= 1;
- if (__wctdm_sdi_clk(wc))
- bits |= 1;
- else
- bits &= ~1;
- }
- return bits;
-}
-
-static inline void __wctdm_setsdi(struct wctdm *wc, unsigned char addr, unsigned short value)
-{
- unsigned int bits;
- /* Send preamble */
- bits = 0xffffffff;
- __wctdm_sdi_sendbits(wc, bits, 32);
- bits = (0x5 << 12) | (1 << 7) | (addr << 2) | 0x2;
- __wctdm_sdi_sendbits(wc, bits, 16);
- __wctdm_sdi_sendbits(wc, value, 16);
-
-}
-
-static inline unsigned short __wctdm_getsdi(struct wctdm *wc, unsigned char addr)
-{
- unsigned int bits;
- /* Send preamble */
- bits = 0xffffffff;
- __wctdm_sdi_sendbits(wc, bits, 32);
- bits = (0x6 << 10) | (1 << 5) | (addr);
- __wctdm_sdi_sendbits(wc, bits, 14);
- return __wctdm_sdi_recvbits(wc, 18);
-}
-
-static inline void wctdm_setsdi(struct wctdm *wc, unsigned char addr, unsigned short value)
-{
- unsigned long flags;
- spin_lock_irqsave(&wc->reglock, flags);
- __wctdm_setsdi(wc, addr, value);
- spin_unlock_irqrestore(&wc->reglock, flags);
-}
-
-static inline unsigned short wctdm_getsdi(struct wctdm *wc, unsigned char addr)
-{
- unsigned long flags;
- unsigned short val;
- spin_lock_irqsave(&wc->reglock, flags);
- val = __wctdm_getsdi(wc, addr);
- spin_unlock_irqrestore(&wc->reglock, flags);
- return val;
-}
-
#ifdef VPM_SUPPORT
static inline unsigned char wctdm_vpm_in(struct wctdm *wc, int unit, const unsigned int addr)
{
@@ -1039,17 +840,13 @@ static inline void cmd_retransmit(struct wctdm *wc)
#endif
}
-static inline void wctdm_receiveprep(struct wctdm *wc, int dbl)
+static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char *readchunk)
{
- volatile unsigned char *readchunk;
int x,y;
unsigned char expected;
- dbl = dbl % 2;
+ BUG_ON(NULL == readchunk);
- readchunk = (volatile unsigned char *)wc->readchunk;
- if (dbl)
- readchunk += SFRAME_SIZE;
for (x=0;x<ZT_CHUNKSIZE;x++) {
if (x < ZT_CHUNKSIZE - 1) {
expected = wc->rxident+1;
@@ -1060,9 +857,11 @@ static inline void wctdm_receiveprep(struct wctdm *wc, int dbl)
}
}
for (y=0;y < wc->cards;y++) {
- if (y < wc->type) {
- wc->chans[y].readchunk[x] = readchunk[y];
- }
+ if (likely(wc->initialized)) {
+ if (y < wc->type) {
+ wc->chans[y].readchunk[x] = readchunk[y];
+ }
+ }
cmd_decifer(wc, readchunk, y);
}
#ifdef VPM_SUPPORT
@@ -1072,26 +871,21 @@ static inline void wctdm_receiveprep(struct wctdm *wc, int dbl)
} else if (wc->vpm150m)
cmd_decifer_vpm150m(wc, readchunk);
#endif
-#if 0
- if (cmddesc < 1024) {
- printk("RC Result: %02x\n", readchunk[EFRAME_SIZE+1]);
- }
-#endif
+
readchunk += (EFRAME_SIZE + EFRAME_GAP);
}
/* XXX We're wasting 8 taps. We should get closer :( */
- for (x=0;x<wc->type;x++) {
- if (wc->cardflag & (1 << x))
- zt_ec_chunk(&wc->chans[x], wc->chans[x].readchunk, wc->chans[x].writechunk);
+ if (likely(wc->initialized)) {
+ for (x=0;x<wc->type;x++) {
+ if (wc->cardflag & (1 << x))
+ zt_ec_chunk(&wc->chans[x], wc->chans[x].readchunk, wc->chans[x].writechunk);
+ }
+ zt_receive(&wc->span);
}
- zt_receive(&wc->span);
/* Wake up anyone sleeping to read/write a new register */
wake_up_interruptible(&wc->regq);
}
-static void wctdm_stop_dma(struct wctdm *wc);
-static void wctdm_restart_dma(struct wctdm *wc);
-
static int wait_access(struct wctdm *wc, int card)
{
unsigned char data=0;
@@ -1330,160 +1124,206 @@ if (debug) printk("QRV channel %d rx state changed to %d\n",qrvcard + 1,wc->qrvh
static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
{
+#define MS_PER_CHECK_HOOK 1
+
unsigned char res;
signed char b;
+ struct fxo *fxo = &wc->mods[card].fxo;
+
/* Try to track issues that plague slot one FXO's */
b = wc->cmdq[card].isrshadow[0]; /* Hook/Ring state */
b &= 0x9b;
- if (wc->mods[card].fxo.offhook) {
+ if (fxo->offhook) {
if (b != 0x9)
wctdm_setreg_intr(wc, card, 5, 0x9);
} else {
if (b != 0x8)
wctdm_setreg_intr(wc, card, 5, 0x8);
}
- if (!wc->mods[card].fxo.offhook) {
+ if (!fxo->offhook) {
if (fwringdetect) {
res = wc->cmdq[card].isrshadow[0] & 0x60;
- if (wc->mods[card].fxo.ringdebounce--) {
- if (res && (res != wc->mods[card].fxo.lastrdtx) && (wc->mods[card].fxo.battery == 1)) {
- if (!wc->mods[card].fxo.wasringing) {
- wc->mods[card].fxo.wasringing = 1;
+ if (fxo->ringdebounce--) {
+ if (res && (res != fxo->lastrdtx) && (fxo->battery == 1)) {
+ if (!fxo->wasringing) {
+ fxo->wasringing = 1;
if (debug)
printk("RING on %d/%d!\n", wc->span.spanno, card + 1);
zt_hooksig(&wc->chans[card], ZT_RXSIG_RING);
}
- wc->mods[card].fxo.lastrdtx = res;
- wc->mods[card].fxo.ringdebounce = 10;
+ fxo->lastrdtx = res;
+ fxo->ringdebounce = 10;
} else if (!res) {
- if ((wc->mods[card].fxo.ringdebounce == 0) && wc->mods[card].fxo.wasringing) {
- wc->mods[card].fxo.wasringing = 0;
+ if ((fxo->ringdebounce == 0) && fxo->wasringing) {
+ fxo->wasringing = 0;
if (debug)
printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1);
zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
}
}
- } else if (res && (wc->mods[card].fxo.battery == 1)) {
- wc->mods[card].fxo.lastrdtx = res;
- wc->mods[card].fxo.ringdebounce = 10;
+ } else if (res && (fxo->battery == BATTERY_PRESENT)) {
+ fxo->lastrdtx = res;
+ fxo->ringdebounce = 10;
}
} else {
res = wc->cmdq[card].isrshadow[0];
- if ((res & 0x60) && (wc->mods[card].fxo.battery == 1)) {
- wc->mods[card].fxo.ringdebounce += (ZT_CHUNKSIZE * 16);
- if (wc->mods[card].fxo.ringdebounce >= ZT_CHUNKSIZE * ringdebounce) {
- if (!wc->mods[card].fxo.wasringing) {
- wc->mods[card].fxo.wasringing = 1;
+ if ((res & 0x60) && (fxo->battery == BATTERY_PRESENT)) {
+ fxo->ringdebounce += (ZT_CHUNKSIZE * 16);
+ if (fxo->ringdebounce >= ZT_CHUNKSIZE * ringdebounce) {
+ if (!fxo->wasringing) {
+ fxo->wasringing = 1;
zt_hooksig(&wc->chans[card], ZT_RXSIG_RING);
if (debug)
printk("RING on %d/%d!\n", wc->span.spanno, card + 1);
}
- wc->mods[card].fxo.ringdebounce = ZT_CHUNKSIZE * ringdebounce;
+ fxo->ringdebounce = ZT_CHUNKSIZE * ringdebounce;
}
} else {
- wc->mods[card].fxo.ringdebounce -= ZT_CHUNKSIZE * 4;
- if (wc->mods[card].fxo.ringdebounce <= 0) {
- if (wc->mods[card].fxo.wasringing) {
- wc->mods[card].fxo.wasringing = 0;
+ fxo->ringdebounce -= ZT_CHUNKSIZE * 4;
+ if (fxo->ringdebounce <= 0) {
+ if (fxo->wasringing) {
+ fxo->wasringing = 0;
zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
if (debug)
printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1);
}
- wc->mods[card].fxo.ringdebounce = 0;
+ fxo->ringdebounce = 0;
}
}
}
}
+
b = wc->cmdq[card].isrshadow[1]; /* Voltage */
if (fxovoltage) {
if (!(wc->intcount % 100)) {
printk("Port %d: Voltage: %d Debounce %d\n", card + 1,
- b, wc->mods[card].fxo.battdebounce);
+ b, fxo->battdebounce);
}
}
if (abs(b) < battthresh) {
- wc->mods[card].fxo.nobatttimer++;
-#if 0
- if (wc->mods[card].fxo.battery == 1)
- printk("Battery loss: %d (%d debounce)\n", b, wc->mods[card].fxo.battdebounce);
-#endif
- if (wc->mods[card].fxo.battery && !wc->mods[card].fxo.battdebounce) {
- if (debug & DEBUG_CARD)
- printk("NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1);
- wc->mods[card].fxo.battery = 0;
+ /* 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("NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1);
#ifdef JAPAN
- if ((!wc->ohdebounce) && wc->offhook) {
- zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK);
- if (debug & DEBUG_CARD)
- printk("Signalled On Hook\n");
+ if (!wc->ohdebounce && wc->offhook) {
+ zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK);
+ if (debug)
+ printk("Signalled On Hook\n");
#ifdef ZERO_BATT_RING
- wc->onhook++;
+ wc->onhook++;
#endif
- }
+ }
#else
- zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK);
- zt_alarm_channel(&wc->chans[card], ZT_ALARM_RED);
+ zt_hooksig(&wc->chans[card], ZT_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
- wc->mods[card].fxo.battdebounce = battdebounce;
- } else if (!wc->mods[card].fxo.battery)
- wc->mods[card].fxo.battdebounce = battdebounce;
- } else if (abs(b) > battthresh) {
- if ((wc->mods[card].fxo.battery < 1) && !wc->mods[card].fxo.battdebounce) {
- if (debug & DEBUG_CARD)
- printk("BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1,
- (b < 0) ? "-" : "+");
-#ifdef ZERO_BATT_RING
- if (wc->onhook) {
- wc->onhook = 0;
- zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
- if (debug & DEBUG_CARD)
- printk("Signalled Off Hook\n");
+ }
+ } 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("BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1,
+ (b < 0) ? "-" : "+");
+#ifdef ZERO_BATT_RING
+ if (wc->onhook) {
+ wc->onhook = 0;
+ zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
+ if (debug)
+ printk("Signalled Off Hook\n");
+ }
#else
- zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
- zt_alarm_channel(&wc->chans[card], ZT_ALARM_NONE);
+ zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
#endif
- wc->mods[card].fxo.battery = 1;
- wc->mods[card].fxo.nobatttimer = 0;
- wc->mods[card].fxo.battdebounce = battdebounce;
- } else if (wc->mods[card].fxo.battery == 1)
- wc->mods[card].fxo.battdebounce = battdebounce;
-
- if (wc->mods[card].fxo.lastpol >= 0) {
- if (b < 0) {
- wc->mods[card].fxo.lastpol = -1;
- wc->mods[card].fxo.polaritydebounce = POLARITY_DEBOUNCE;
- }
+ /* 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 (wc->mods[card].fxo.lastpol <= 0) {
- if (b > 0) {
- wc->mods[card].fxo.lastpol = 1;
- wc->mods[card].fxo.polaritydebounce = POLARITY_DEBOUNCE;
- }
+ 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 */
+ zt_alarm_channel(&wc->chans[card], fxo->battery ? ZT_ALARM_NONE : ZT_ALARM_RED);
}
- } else {
- /* It's something else... */
- wc->mods[card].fxo.battdebounce = battdebounce;
}
- if (wc->mods[card].fxo.battdebounce)
- wc->mods[card].fxo.battdebounce--;
- if (wc->mods[card].fxo.polaritydebounce) {
- wc->mods[card].fxo.polaritydebounce--;
- if (wc->mods[card].fxo.polaritydebounce < 1) {
- if (wc->mods[card].fxo.lastpol != wc->mods[card].fxo.polarity) {
+
+ if (fxo->polaritydebounce) {
+ fxo->polaritydebounce--;
+ if (fxo->polaritydebounce < 1) {
+ if (fxo->lastpol != fxo->polarity) {
if (debug & DEBUG_CARD)
printk("%lu Polarity reversed (%d -> %d)\n", jiffies,
- wc->mods[card].fxo.polarity,
- wc->mods[card].fxo.lastpol);
- if (wc->mods[card].fxo.polarity)
- zt_qevent_lock(&wc->chans[card], ZT_EVENT_POLARITY);
- wc->mods[card].fxo.polarity = wc->mods[card].fxo.lastpol;
+ fxo->polarity,
+ fxo->lastpol);
+ if (fxo->polarity)
+ zt_qevent_lock(&wc->chans[card], ZT_EVENT_POLARITY);
+ fxo->polarity = fxo->lastpol;
}
}
}
+#undef MS_PER_CHECK_HOOK
}
static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card)
@@ -1496,7 +1336,7 @@ static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card)
res = wc->cmdq[card].isrshadow[0]; /* Hook state */
hook = (res & 1);
-
+
if (hook != wc->mods[card].fxs.lastrxhook) {
/* Reset the debounce (must be multiple of 4ms) */
wc->mods[card].fxs.debounce = 8 * (4 * 8);
@@ -1537,15 +1377,6 @@ static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card)
}
-static inline void wctdm_reinit_descriptor(struct wctdm *wc, int tx, int dbl, char *s)
-{
- int o2 = 0;
- o2 += dbl * 4;
- if (!tx)
- o2 += ERING_SIZE * 4;
- wc->descripchunk[o2] = 0x80000000;
-}
-
#ifdef VPM_SUPPORT
static inline void wctdm_vpm_check(struct wctdm *wc, int x)
{
@@ -1616,6 +1447,10 @@ static inline void wctdm_isr_misc(struct wctdm *wc)
{
int x;
+ if (unlikely(!wc->initialized)) {
+ return;
+ }
+
for (x=0;x<wc->cards;x++) {
if (wc->cardflag & (1 << x)) {
if (wc->modtype[x] == MOD_TYPE_FXS) {
@@ -1661,122 +1496,22 @@ static inline void wctdm_isr_misc(struct wctdm *wc)
#endif
}
-static inline int wctdm_check_descriptor(struct wctdm *wc, int tx)
+void handle_receive(void* vbb, void* context)
{
- int o2 = 0;
- if (!tx) {
- o2 += ERING_SIZE * 4;
- o2 += wc->rdbl * 4;
- } else {
- o2 += wc->tdbl * 4;
- }
- if (!(wc->descripchunk[o2] & 0x80000000)) {
- if (tx) {
- wc->txints++;
- wctdm_transmitprep(wc, wc->tdbl);
- wctdm_reinit_descriptor(wc, tx, wc->tdbl, "txchk");
- wc->tdbl = (wc->tdbl + 1) % ERING_SIZE;
- wctdm_isr_misc(wc);
- wc->intcount++;
- } else {
- wc->rxints++;
- wctdm_receiveprep(wc, wc->rdbl);
- wctdm_reinit_descriptor(wc, tx, wc->rdbl, "rxchk");
- wc->rdbl = (wc->rdbl + 1) % ERING_SIZE;
- }
- return 1;
- }
- return 0;
-}
-
-static void wctdm_init_descriptors(struct wctdm *wc)
-{
- volatile unsigned int *descrip;
- dma_addr_t descripdma;
- dma_addr_t writedma;
- dma_addr_t readdma;
- int x;
-
- descrip = wc->descripchunk;
- descripdma = wc->descripdma;
- writedma = wc->writedma;
- readdma = wc->readdma;
-
- for (x=0;x<ERING_SIZE;x++) {
- if (x < ERING_SIZE - 1)
- descripdma += 16;
- else
- descripdma = wc->descripdma;
-
- /* Transmit descriptor */
- descrip[0 ] = 0x80000000;
- descrip[1 ] = 0xe5800000 | (SFRAME_SIZE);
- if (x % 2)
- descrip[2 ] = writedma + SFRAME_SIZE;
- else
- descrip[2 ] = writedma;
- descrip[3 ] = descripdma;
-
- /* Receive descriptor */
- descrip[0 + ERING_SIZE * 4] = 0x80000000;
- descrip[1 + ERING_SIZE * 4] = 0x01000000 | (SFRAME_SIZE);
- if (x % 2)
- descrip[2 + ERING_SIZE * 4] = readdma + SFRAME_SIZE;
- else
- descrip[2 + ERING_SIZE * 4] = readdma;
- descrip[3 + ERING_SIZE * 4] = descripdma + ERING_SIZE * 16;
-
- /* Advance descriptor */
- descrip += 4;
- }
+ struct wctdm *wc = context;
+ wc->rxints++;
+ wctdm_receiveprep(wc, vbb);
}
-ZAP_IRQ_HANDLER(wctdm_interrupt)
+void handle_transmit(void* vbb, void* context)
{
- struct wctdm *wc = dev_id;
- unsigned int ints;
- int res;
-
- /* Read and clear interrupts */
- ints = wctdm_getctl(wc, 0x0028);
-
- if (!ints)
-#ifdef LINUX26
- return IRQ_NONE;
-#else
- return;
-#endif
-
- wctdm_setctl(wc, 0x0028, ints);
-
- ints &= wc->intmask;
- if (ints & 0x00000041) {
- do {
- res = wctdm_check_descriptor(wc, 0);
- res |= wctdm_check_descriptor(wc, 1);
- } while(res);
-#if 0
- while(wctdm_check_descriptor(wc, 0));
- wctdm_setctl(wc, 0x0010, 0x00000000);
- }
- if (ints & 0x00000005) {
- while(wctdm_check_descriptor(wc, 1));
- wctdm_setctl(wc, 0x0008, 0x00000000);
-#endif
- }
-
- if (ints & 0x0000a3ae) {
- /* This will allow us to recover if interrupts are held for a long period of time */
- if (debug & DEBUG_CARD)
- printk("Abnormal interrupt %08x detected\n", ints);
- wctdm_setctl(wc, 0x0008, 0x00000000);
- wctdm_setctl(wc, 0x0010, 0x00000000);
- }
-
-#ifdef LINUX26
- return IRQ_RETVAL(1);
-#endif
-
+ struct wctdm *wc = context;
+ memset(vbb, 0, SFRAME_SIZE);
+ wc->txints++;
+ wctdm_transmitprep(wc, vbb);
+ wctdm_isr_misc(wc);
+ wc->intcount++;
+ voicebus_transmit(wc->vb, vbb);
}
static int wctdm_voicedaa_insane(struct wctdm *wc, int card)
@@ -2150,6 +1885,10 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual,
wctdm_setreg(wc, card, 24, 0x19);
}
+ /* Enable ring detector full-wave rectifier mode */
+ wctdm_setreg(wc, card, 18, 2);
+ wctdm_setreg(wc, card, 24, 0);
+
/* Set DC Termination:
Tip/Ring voltage adjust, minimum operational current, current limitation */
reg26 |= (fxo_modes[_opermode].dcv << 6);
@@ -2199,9 +1938,6 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual,
if(debug)
printk("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));
- /* battery state still unknown */
- wc->mods[card].fxo.battery = -1;
-
return 0;
}
@@ -2590,33 +2326,6 @@ static int wctdm_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long dat
struct zt_radio_param p;
} stack;
-#if 0
- /* XXX */
- printk("RxInts: %d, TxInts: %d\n", wc->rxints, wc->txints);
- printk("RxIdent: %d, TxIdent: %d\n", wc->rxident, wc->txident);
- for (x=0;x<wc->cards;x++)
- printk("Card %d isrshadow: %02x/%02x\n", x, wc->cmdq[x].isrshadow[0], wc->cmdq[x].isrshadow[1]);
- cmddesc = 0;
-#endif
-#if 0
- if (wc->vpm) {
- char tmp[80];
- for (x=0;x<0x200;x++) {
- switch (x & 0xf) {
- case 0:
- sprintf(tmp, "%03x: %02x ", x, wctdm_vpm_in(wc, 0, x));
- break;
- case 0xf:
- printk("%s%02x\n", tmp, wctdm_vpm_in(wc, 0, x));
- break;
- default:
- sprintf(tmp + strlen(tmp), "%02x ", wctdm_vpm_in(wc, 0, x));
- break;
- }
- }
- }
-
-#endif
switch (cmd) {
case ZT_ONHOOKTRANSFER:
if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS)
@@ -2904,8 +2613,7 @@ static int wctdm_open(struct zt_chan *chan)
static int wctdm_watchdog(struct zt_span *span, int event)
{
- printk("TDM: Restarting DMA\n");
- wctdm_restart_dma(span->pvt);
+ printk("TDM: Called watchdog\n");
return 0;
}
@@ -3129,15 +2837,16 @@ static int wctdm_dacs(struct zt_chan *dst, struct zt_chan *src)
static int wctdm_initialize(struct wctdm *wc)
{
int x;
+ struct pci_dev *pdev = voicebus_get_pci_dev(wc->vb);
/* Zapata stuff */
sprintf(wc->span.name, "WCTDM/%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%s Bus %02d Slot %02d", (wc->flags[0] & FLAG_EXPRESS) ? " Express" : "",
- wc->dev->bus->number, PCI_SLOT(wc->dev->devfn) + 1);
+ pdev->bus->number, PCI_SLOT(pdev->devfn) + 1);
wc->span.manufacturer = "Digium";
- zap_copy_string(wc->span.devicetype, wc->variety, sizeof(wc->span.devicetype));
+ strncpy(wc->span.devicetype, wc->variety, sizeof(wc->span.devicetype) - 1);
if (alawoverride) {
printk("ALAW override parameter detected. Device will be operating in ALAW\n");
wc->span.deflaw = ZT_LAW_ALAW;
@@ -3152,7 +2861,7 @@ static int wctdm_initialize(struct wctdm *wc)
}
wc->span.chans = wc->chans;
wc->span.channels = wc->type;
- wc->span.irq = wc->dev->irq;
+ wc->span.irq = pdev->irq;
wc->span.hooksig = wctdm_hooksig;
wc->span.open = wctdm_open;
wc->span.close = wctdm_close;
@@ -3166,10 +2875,6 @@ static int wctdm_initialize(struct wctdm *wc)
init_waitqueue_head(&wc->span.maintq);
wc->span.pvt = wc;
- if (zt_register(&wc->span, 0)) {
- printk("Unable to register span with zaptel\n");
- return -1;
- }
return 0;
}
@@ -3197,115 +2902,6 @@ static void wctdm_post_initialize(struct wctdm *wc)
strncat(wc->span.devicetype, " with VPMADT032", sizeof(wc->span.devicetype) - 1);
}
-static int wctdm_hardware_init(struct wctdm *wc)
-{
- /* Hardware stuff */
- unsigned int reg;
- unsigned long newjiffies;
-
- /* Initialize descriptors */
- wctdm_init_descriptors(wc);
-
- /* Enable I/O Access */
- pci_read_config_dword(wc->dev, 0x0004, &reg);
- reg |= 0x00000007;
- pci_write_config_dword(wc->dev, 0x0004, reg);
- printk("PCI Config reg is %08x\n", reg);
-
- wctdm_setctl(wc, 0x0000, 0xfff88001);
-
- newjiffies = jiffies + HZ/10;
- while(((reg = wctdm_getctl(wc,0x0000)) & 0x00000001) && (newjiffies > jiffies));
- printk("%s: New Reg: %08x!\n", wc->variety, reg);
- wctdm_setctl(wc, 0x0000, 0xfff88000);
-
-
- /* Configure watchdogs, access, etc */
- wctdm_setctl(wc, 0x0030, 0x00080048);
- wctdm_setctl(wc, 0x0078, 0x00000013 /* | (1 << 28) */);
-
-#if 0
- /* XXX Enable loopback XXX */
- reg = wctdm_getctl(wc, 0x0030);
- wctdm_setctl(wc, 0x0030, reg | 0x00000400);
-
-#else
- reg = wctdm_getctl(wc, 0x00fc);
- wctdm_setctl(wc, 0x00fc, (reg & ~0x7) | 0x7);
- wctdm_setsdi(wc, 0x00, 0x0100);
- wctdm_setsdi(wc, 0x16, 0x2100);
- printk("Detected REG0: %08x\n", wctdm_getsdi(wc, 0x00));
- printk("Detected REG1: %08x\n", wctdm_getsdi(wc, 0x01));
- printk("Detected REG2: %08x\n", wctdm_getsdi(wc, 0x02));
-
- reg = wctdm_getctl(wc, 0x00fc);
- printk("(pre) Reg fc is %08x\n", reg);
-
- wctdm_setctl(wc, 0x00fc, (reg & ~0x7) | 0x4);
- wctdm_setsdi(wc, 0x00, 0x0100);
- wctdm_setsdi(wc, 0x16, 0x2100);
- reg = wctdm_getctl(wc, 0x00fc);
- printk("(post) Reg fc is %08x\n", reg);
- printk("Detected REG2: %08x\n", wctdm_getsdi(wc, 0x02));
-#endif
- printk("wctdm24xxp: reg is %08x\n", wctdm_getctl(wc, 0x0088));
-
- return 0;
-}
-
-static void wctdm_setintmask(struct wctdm *wc, unsigned int intmask)
-{
- wc->intmask = intmask;
- wctdm_setctl(wc, 0x0038, intmask);
-}
-
-static void wctdm_enable_interrupts(struct wctdm *wc)
-{
- /* Enable interrupts */
- wctdm_setintmask(wc, 0x00010041);
-}
-
-static void wctdm_restart_dma(struct wctdm *wc)
-{
-}
-
-static void wctdm_start_dma(struct wctdm *wc)
-{
- unsigned int reg;
- wmb();
- wctdm_setctl(wc, 0x0020, wc->descripdma);
- wctdm_setctl(wc, 0x0018, wc->descripdma + (16 * ERING_SIZE));
- /* Start receiver/transmitter */
- reg = wctdm_getctl(wc, 0x0030);
- wctdm_setctl(wc, 0x0030, reg | 0x00002002);
- wctdm_setctl(wc, 0x0008, 0x00000000);
- wctdm_setctl(wc, 0x0010, 0x00000000);
- reg = wctdm_getctl(wc, 0x0028);
- wctdm_setctl(wc, 0x0028, reg);
-
-}
-
-static void wctdm_stop_dma(struct wctdm *wc)
-{
- /* Disable interrupts and reset */
- unsigned int reg;
- /* Disable interrupts */
- wctdm_setintmask(wc, 0x00000000);
- wctdm_setctl(wc, 0x0084, 0x00000000);
- wctdm_setctl(wc, 0x0048, 0x00000000);
- /* Reset the part to be on the safe side */
- reg = wctdm_getctl(wc, 0x0000);
- reg |= 0x00000001;
- wctdm_setctl(wc, 0x0000, reg);
-}
-
-static void wctdm_disable_interrupts(struct wctdm *wc)
-{
- /* Disable interrupts */
- wctdm_setintmask(wc, 0x00000000);
- wctdm_setctl(wc, 0x0084, 0x00000000);
-}
-
#ifdef VPM_SUPPORT
#ifdef VPM150M_SUPPORT
@@ -3519,6 +3115,7 @@ static void vpm150m_bh(struct work_struct *data)
if (debug & DEBUG_ECHOCAN)
printk("Echocan enable took %d ms\n", wc->intcount - start);
} else {
+ res = gpakAlgControl(vpm150m->dspid, i, BypassEcanA, &pstatus);
if (debug & DEBUG_ECHOCAN)
printk("Echocan disable took %d ms\n", wc->intcount - start);
}
@@ -3702,6 +3299,7 @@ static enum vpmadt032_init_result wctdm_vpm150m_init(struct wctdm *wc)
struct vpm150m *vpm150m;
unsigned short reg;
unsigned long flags;
+ struct pci_dev* pdev = voicebus_get_pci_dev(wc->vb);
enum vpmadt032_init_result res = VPMADT032_FAILED;
#ifdef VPM150M_SUPPORT
@@ -3807,7 +3405,7 @@ static enum vpmadt032_init_result wctdm_vpm150m_init(struct wctdm *wc)
if (pingstatus || (version != 0x106)) {
#endif
#if defined(HOTPLUG_FIRMWARE)
- if ((request_firmware(&firmware, vpmadt032_firmware, &wc->dev->dev) != 0) ||
+ if ((request_firmware(&firmware, vpmadt032_firmware, &pdev->dev) != 0) ||
!firmware) {
printk("VPMADT032: firmware %s not available from userspace\n", vpmadt032_firmware);
goto failed_exit;
@@ -4024,21 +3622,8 @@ static int wctdm_locate_modules(struct wctdm *wc)
{
int x;
unsigned long flags;
- printk("Resetting the modules...\n");
- /* Initialize control register */
+ unsigned int startinglatency = voicebus_current_latency(wc->vb);
wc->ctlreg = 0x00;
- /* Set Reset */
- wctdm_setctl(wc, 0x0048, 0x00000000);
- for (x=0;x<10;x++)
- schluffen(&wc->regq);
- printk("During Resetting the modules...\n");
- /* Clear reset */
- wctdm_setctl(wc, 0x0048, 0x00010000);
- for (x=0;x<10;x++)
- schluffen(&wc->regq);
- printk("After resetting the modules...\n");
-
- wctdm_setintmask(wc, 0x0001f7fe);
/* Make sure all units go into daisy chain mode */
spin_lock_irqsave(&wc->reglock, flags);
@@ -4072,6 +3657,9 @@ static int wctdm_locate_modules(struct wctdm *wc)
for (x=0;x<wc->cards;x++) {
int sane=0,ret=0,readi=0;
retry:
+ if (voicebus_current_latency(wc->vb) > startinglatency) {
+ return -EAGAIN;
+ }
/* Init with Auto Calibration */
if (!(ret = wctdm_init_proslic(wc, x, 0, 0, sane))) {
wc->cardflag |= (1 << x);
@@ -4136,11 +3724,17 @@ retry:
printk("VPM: Present and operational (Rev %c)\n", 'A' + wc->vpm - 1);
wc->ctlreg |= 0x10;
} else {
+ enum vpmadt032_init_result res;
spin_lock_irqsave(&wc->reglock, flags);
for (x = NUM_CARDS; x < NUM_CARDS + NUM_EC; x++)
wc->modtype[x] = MOD_TYPE_NONE;
spin_unlock_irqrestore(&wc->reglock, flags);
- switch (wctdm_vpm150m_init(wc)) {
+ res = wctdm_vpm150m_init(wc);
+ /* In case there was an error while we were loading the VPM module. */
+ if (voicebus_current_latency(wc->vb) > startinglatency) {
+ return -EAGAIN;
+ }
+ switch (res) {
case VPMADT032_SUCCESS:
printk("VPMADT032: Present and operational (Firmware version %x)\n", wc->vpm150m->version);
wc->ctlreg |= 0x10;
@@ -4150,173 +3744,139 @@ retry:
/* nothing */
break;
default:
- return -1;
+ return -EIO;
}
}
#endif
-
+ /* In case there was an error while we were loading the VPM module. */
+ if (voicebus_current_latency(wc->vb) > startinglatency) {
+ return -EAGAIN;
+ }
return 0;
}
+static struct pci_driver wctdm_driver;
+
static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct wctdm *wc;
struct wctdm_desc *d = (struct wctdm_desc *)ent->driver_data;
- int x;
+ int i;
int y;
+ int ret;
- if (pci_enable_device(pdev))
- return -EIO;
-
- if (!(wc = kmalloc(sizeof(struct wctdm), GFP_KERNEL)))
+retry:
+ wc = kmalloc(sizeof(struct wctdm), GFP_KERNEL);
+ if (!wc) {
+ /* \todo Print debug message. */
return -ENOMEM;
-
- spin_lock(&ifacelock);
- for (x = 0; x < WC_MAX_IFACES; x++)
- if (!ifaces[x]) break;
-
- ifaces[x] = wc;
+ }
+ memset(wc, 0, sizeof(*wc));
+ spin_lock(&ifacelock);
+ /* \todo this is a candidate for removal... */
+ for (i = 0; i < WC_MAX_IFACES; ++i) {
+ if (!ifaces[i]) {
+ ifaces[i] = wc;
+ break;
+ }
+ }
spin_unlock(&ifacelock);
-
- memset(wc, 0, sizeof(struct wctdm));
+
+ snprintf(wc->board_name, sizeof(wc->board_name)-1, "%s%d",
+ wctdm_driver.name, i);
+ ret = voicebus_init(pdev, SFRAME_SIZE, wc->board_name,
+ handle_receive, handle_transmit, wc, &wc->vb);
+ if (ret) {
+ kfree(wc);
+ return ret;
+ }
+ BUG_ON(!wc->vb);
+
+ if (VOICEBUS_DEFAULT_LATENCY != latency) {
+ voicebus_set_minlatency(wc->vb, latency);
+ }
+
spin_lock_init(&wc->reglock);
wc->curcard = -1;
wc->cards = NUM_CARDS;
- wc->iobase = pci_resource_start(pdev, 0);
wc->type = d->ports;
- wc->dev = pdev;
- wc->pos = x;
+ wc->pos = i;
wc->variety = d->name;
+ wc->txident = 1;
for (y=0;y<NUM_CARDS;y++) {
wc->flags[y] = d->flags;
wc->dacssrc[y] = -1;
}
- /* Keep track of whether we need to free the region */
- if (request_region(wc->iobase, 0xff, "wctdm24xxp"))
- wc->freeregion = 1;
-
- /* Allocate enough memory for two zt chunks, receive and transmit. Each sample uses
- 32 bits. Allocate an extra set just for control too */
- wc->writechunk = pci_alloc_consistent(pdev, PCI_WINDOW_SIZE, &wc->writedma);
- if (!wc->writechunk) {
- printk("wctdm: Unable to allocate DMA-able memory\n");
- if (wc->freeregion)
- release_region(wc->iobase, 0xff);
- return -ENOMEM;
- }
-
- wc->readchunk = wc->writechunk + SFRAME_SIZE / 2; /* in doublewords */
- wc->readdma = wc->writedma + SFRAME_SIZE * 2; /* in bytes */
-
- wc->descripchunk = wc->readchunk + SFRAME_SIZE / 2; /* in doublewords */
- wc->descripdma = wc->readdma + SFRAME_SIZE * 2; /* in bytes */
-
- /* Initialize Write/Buffers to all blank data */
- memset((void *)wc->writechunk,0x00, SFRAME_SIZE * 2);
- memset((void *)wc->readchunk, 0x00, SFRAME_SIZE * 2);
-
+
init_waitqueue_head(&wc->regq);
-
+
if (wctdm_initialize(wc)) {
- printk("%s: Unable to register span with zaptel\n", wc->variety);
- /* Set Reset Low */
- wctdm_stop_dma(wc);
- /* Free Resources */
- if (wc->freeregion)
- release_region(wc->iobase, 0xff);
- pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma);
- zt_unregister(&wc->span);
+ voicebus_release(wc->vb);
+ wc->vb = NULL;
kfree(wc);
return -EIO;
}
-
- /* 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, ZAP_IRQ_SHARED, wc->variety, wc)) {
- printk("wctdm24xxp: Unable to request IRQ %d\n", pdev->irq);
- /* Set Reset Low */
- wctdm_stop_dma(wc);
- /* Free Resources */
- if (wc->freeregion)
- release_region(wc->iobase, 0xff);
- pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma);
- pci_set_drvdata(pdev, NULL);
- zt_unregister(&wc->span);
- kfree(wc);
- return -EIO;
- }
-
-
- if (wctdm_hardware_init(wc)) {
- /* Set Reset Low */
- wctdm_stop_dma(wc);
- /* Free Resources */
- free_irq(pdev->irq, wc);
- if (wc->freeregion)
- release_region(wc->iobase, 0xff);
- pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma);
- pci_set_drvdata(pdev, NULL);
- zt_unregister(&wc->span);
- kfree(wc);
- return -EIO;
-
+
+ /* Start the hardware processing. */
+ if (voicebus_start(wc->vb)) {
+ BUG_ON(1);
}
- /* Enable interrupts */
- wctdm_enable_interrupts(wc);
-
- /* Start DMA */
- wctdm_start_dma(wc);
-
/* Now track down what modules are installed */
- if (wctdm_locate_modules(wc)) {
- wctdm_disable_interrupts(wc);
- /* Set Reset Low */
- wctdm_stop_dma(wc);
- /* Free Resources */
- free_irq(pdev->irq, wc);
- if (wc->freeregion)
- release_region(wc->iobase, 0xff);
- pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma);
- pci_set_drvdata(pdev, NULL);
- zt_unregister(&wc->span);
- kfree(wc);
- return -EIO;
+ ret = wctdm_locate_modules(wc);
+ if (-EAGAIN == ret ) {
+ /* The voicebus library increased the latency during
+ * initialization. There is a chance that the hardware is in
+ * an inconsistent state, so lets increase the default latency
+ * and start the initialization over.
+ */
+ printk(KERN_NOTICE "%s: Restarting board initialization " \
+ "after increasing latency.\n", wc->board_name);
+ latency = voicebus_current_latency(wc->vb);
+ wctdm_release(wc);
+ goto retry;
}
/* Final initialization */
wctdm_post_initialize(wc);
- printk("Found a Wildcard TDM: %s (%d modules)\n", wc->variety, wc->type);
+ /* We should be ready for zaptel to come in now. */
+ if (zt_register(&wc->span, 0)) {
+ printk("Unable to register span with zaptel\n");
+ return -1;
+ }
- return 0;
+ wc->initialized = 1;
+
+ printk("Found a Wildcard TDM: %s (%d modules)\n", wc->variety, wc->type);
+ ret = 0;
+
+ return ret;
}
static void wctdm_release(struct wctdm *wc)
{
int i;
- zt_unregister(&wc->span);
+ if (wc->initialized) {
+ zt_unregister(&wc->span);
+ }
- if (wc->freeregion)
- release_region(wc->iobase, 0xff);
+ voicebus_release(wc->vb);
+ wc->vb = NULL;
spin_lock(&ifacelock);
-
for (i = 0; i < WC_MAX_IFACES; i++)
if (ifaces[i] == wc)
break;
-
ifaces[i] = NULL;
-
spin_unlock(&ifacelock);
kfree(wc);
- printk("Freed a Wildcard\n");
}
static void __devexit wctdm_remove_one(struct pci_dev *pdev)
@@ -4338,16 +3898,7 @@ static void __devexit wctdm_remove_one(struct pci_dev *pdev)
destroy_workqueue(vpm150m->wq);
}
#endif
-
- /* Stop any DMA */
- wctdm_stop_dma(wc);
-
- /* In case hardware is still there */
- wctdm_disable_interrupts(wc);
-
- /* Immediately free resources */
- free_irq(pdev->irq, wc);
- pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma);
+ voicebus_stop(wc->vb);
#ifdef VPM150M_SUPPORT
if (vpm150m) {
@@ -4359,8 +3910,10 @@ static void __devexit wctdm_remove_one(struct pci_dev *pdev)
}
#endif
/* Release span, possibly delayed */
- if (!wc->usecount)
+ if (!wc->usecount) {
wctdm_release(wc);
+ printk("Freed a Wildcard\n");
+ }
else
wc->dead = 1;
}
@@ -4395,7 +3948,7 @@ static int __init wctdm_init(void)
int res;
int x;
- for (x=0;x<(sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) {
+ for (x = 0; x < (sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) {
if (!strcmp(fxo_modes[x].name, opermode))
break;
}
@@ -4403,12 +3956,31 @@ static int __init wctdm_init(void)
_opermode = x;
} else {
printk("Invalid/unknown operating mode '%s' specified. Please choose one of:\n", opermode);
- for (x=0;x<sizeof(fxo_modes) / sizeof(fxo_modes[0]); x++)
+ for (x = 0; x < sizeof(fxo_modes) / sizeof(fxo_modes[0]); x++)
printk(" %s\n", fxo_modes[x].name);
printk("Note this option is CASE SENSITIVE!\n");
return -ENODEV;
}
+ if (!strcmp(opermode, "AUSTRALIA")) {
+ boostringer = 1;
+ fxshonormode = 1;
+ }
+
+ /* for the voicedaa_check_hook defaults, if the user has not overridden
+ them by specifying them as module parameters, then get the values
+ from the selected operating mode
+ */
+ 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 = zap_pci_module(&wctdm_driver);
if (res)
return -ENODEV;
@@ -4425,14 +3997,14 @@ module_param(debug, int, 0600);
module_param(fxovoltage, int, 0600);
module_param(loopcurrent, int, 0600);
module_param(robust, int, 0600);
-module_param(_opermode, int, 0600);
module_param(opermode, charp, 0600);
module_param(lowpower, int, 0600);
module_param(boostringer, int, 0600);
module_param(fastringer, int, 0600);
module_param(fxshonormode, int, 0600);
-module_param(battdebounce, int, 0600);
-module_param(battthresh, int, 0600);
+module_param(battdebounce, uint, 0600);
+module_param(battalarm, uint, 0600);
+module_param(battthresh, uint, 0600);
module_param(alawoverride, int, 0600);
module_param(nativebridge, int, 0600);
module_param(fxotxgain, int, 0600);
@@ -4441,6 +4013,7 @@ module_param(fxstxgain, int, 0600);
module_param(fxsrxgain, int, 0600);
module_param(ringdebounce, int, 0600);
module_param(fwringdetect, int, 0600);
+module_param(latency, int, 0600);
#ifdef VPM_SUPPORT
module_param(vpmsupport, int, 0600);
module_param(vpmdtmfsupport, int, 0600);
@@ -4454,13 +4027,13 @@ MODULE_PARM(debug, "i");
MODULE_PARM(fxovoltage, "i");
MODULE_PARM(loopcurrent, "i");
MODULE_PARM(robust, "i");
-MODULE_PARM(_opermode, "i");
MODULE_PARM(opermode, "s");
MODULE_PARM(lowpower, "i");
MODULE_PARM(boostringer, "i");
MODULE_PARM(fastringer, "i");
MODULE_PARM(fxshonormode, "i");
MODULE_PARM(battdebounce, "i");
+MODULE_PARM(battalarm, "i");
MODULE_PARM(battthresh, "i");
MODULE_PARM(alawoverride, "i");
MODULE_PARM(nativebridge, "i");
diff --git a/kernel/wctdm24xxp/wctdm24xxp.h b/kernel/wctdm24xxp/wctdm24xxp.h
index fd7f39b..30e4fb9 100644
--- a/kernel/wctdm24xxp/wctdm24xxp.h
+++ b/kernel/wctdm24xxp/wctdm24xxp.h
@@ -27,20 +27,20 @@
#ifndef _WCTDM24XXP_H
#define _WCTDM24XXP_H
+#include "../zaptel.h"
+#include "../voicebus.h"
+#include <asm/semaphore.h>
+
#define NUM_FXO_REGS 60
#define WC_MAX_IFACES 128
/*!
* \brief Default ringer debounce (in ms)
- *
- * \todo This value differs from that in wctdm. In that module, it is 64 ms
- * instead of 128 ms. Which one is more appropriate?
*/
#define DEFAULT_RING_DEBOUNCE 128
-#define DEFAULT_BATT_DEBOUNCE 64 /* Battery debounce (in ms) */
-#define POLARITY_DEBOUNCE 64 /* Polarity debounce (in ms) */
-#define DEFAULT_BATT_THRESH 3 /* Anything under this is "no battery" */
+
+#define POLARITY_DEBOUNCE 64 /* Polarity debounce (in ms) */
#define OHT_TIMER 6000 /* How long after RING to retain OHT */
@@ -173,9 +173,15 @@ struct cmdq {
unsigned char isrshadow[ISR_COMMANDS];
};
+enum battery_state {
+ BATTERY_UNKNOWN = 0,
+ BATTERY_PRESENT,
+ BATTERY_LOST,
+};
+
struct wctdm {
- struct pci_dev *dev;
char *variety;
+ char board_name[80];
struct zt_span span;
unsigned char ios;
unsigned int sdi;
@@ -183,16 +189,12 @@ struct wctdm {
unsigned int intcount;
unsigned int rxints;
unsigned int txints;
- unsigned int intmask;
unsigned char txident;
unsigned char rxident;
int dead;
int pos;
int flags[NUM_CARDS];
- int freeregion;
int alt;
- int rdbl;
- int tdbl;
int curcard;
unsigned char ctlreg;
int cards;
@@ -215,19 +217,19 @@ struct wctdm {
wait_queue_head_t regq;
/* FXO Stuff */
union {
- struct {
+ struct fxo {
int wasringing;
int lastrdtx;
int ringdebounce;
int offhook;
int battdebounce;
- int nobatttimer;
- int battery;
+ int battalarm;
+ enum battery_state battery;
int lastpol;
int polarity;
int polaritydebounce;
} fxo;
- struct {
+ struct fxs {
int oldrxhook;
int debouncehook;
int lastrxhook;
@@ -261,14 +263,9 @@ struct wctdm {
int blinktimer;
#endif
#endif
- unsigned long iobase;
- dma_addr_t readdma;
- dma_addr_t writedma;
- dma_addr_t descripdma;
- volatile unsigned int *writechunk; /* Double-word aligned write memory */
- volatile unsigned int *readchunk; /* Double-word aligned read memory */
- volatile unsigned int *descripchunk; /* Descriptors */
+ struct voicebus *vb;
struct zt_chan chans[NUM_CARDS];
+ int initialized;
};
diff --git a/kernel/wcte12xp/Kbuild b/kernel/wcte12xp/Kbuild
index b145e6a..5df6b9b 100644
--- a/kernel/wcte12xp/Kbuild
+++ b/kernel/wcte12xp/Kbuild
@@ -8,7 +8,7 @@ ifeq ($(HOTPLUG_FIRMWARE),yes)
EXTRA_CFLAGS+=-DHOTPLUG_FIRMWARE
endif
-wcte12xp-objs := base.o vpmadt032.o GpakApi.o
+wcte12xp-objs := base.o vpmadt032.o GpakApi.o ../voicebus.o
ifneq ($(HOTPLUG_FIRMWARE),yes)
wcte12xp-objs += $(FIRM_DIR)/zaptel-fw-vpmadt032.o
diff --git a/kernel/wcte12xp/base.c b/kernel/wcte12xp/base.c
index ee58db8..32bfe6f 100644
--- a/kernel/wcte12xp/base.c
+++ b/kernel/wcte12xp/base.c
@@ -8,7 +8,7 @@
* Matthew Fredrickson <creslin@digium.com>
* William Meadows <wmeadows@digium.com>
*
- * Copyright (C) 2007, Digium, Inc.
+ * Copyright (C) 2007-2008, Digium, Inc.
*
* All rights reserved.
*
@@ -34,9 +34,7 @@
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/pci.h>
-#include <linux/interrupt.h>
#include <linux/proc_fs.h>
-#include <linux/interrupt.h>
#ifdef LINUX26
#include <linux/moduleparam.h>
@@ -46,6 +44,7 @@
#include "../wct4xxp/wct4xxp.h" /* For certain definitions */
+#include "../voicebus.h"
#include "wcte12xp.h"
#if defined(VPM_SUPPORT)
@@ -91,6 +90,7 @@ static int alarmdebounce = 0;
static int loopback = 0;
static int t1e1override = -1;
static int unchannelized = 0;
+static int latency = VOICEBUS_DEFAULT_LATENCY;
#ifdef VPM_SUPPORT
int vpmsupport = 1;
int vpmdtmfsupport = 0;
@@ -135,89 +135,6 @@ static inline int empty_slot(struct t1 *wc)
return -1;
}
-static inline void __t1_setctl(struct t1 *wc, unsigned int addr, unsigned int val)
-{
- outl(val, wc->iobase + addr);
-}
-
-static inline void t1_setctl(struct t1 *wc, unsigned int addr, unsigned int val)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&wc->reglock, flags);
- __t1_setctl(wc, addr, val);
- spin_unlock_irqrestore(&wc->reglock, flags);
-}
-
-static inline unsigned int __t1_getctl(struct t1 *wc, unsigned int addr)
-{
- return inl(wc->iobase + addr);
-}
-
-static inline unsigned int t1_getctl(struct t1 *wc, unsigned int addr)
-{
- unsigned long flags;
- unsigned int val;
-
- spin_lock_irqsave(&wc->reglock, flags);
- val = __t1_getctl(wc, addr);
- spin_unlock_irqrestore(&wc->reglock, flags);
-
- return val;
-}
-
-static void t1_init_descriptors(struct t1 *wc)
-{
- volatile unsigned int *descrip;
- dma_addr_t descripdma;
- dma_addr_t writedma;
- dma_addr_t readdma;
- int x;
-
- descrip = wc->descripchunk;
- descripdma = wc->descripdma;
- writedma = wc->writedma;
- readdma = wc->readdma;
-
- for (x = 0; x < ERING_SIZE; x++) {
- if (x < ERING_SIZE - 1)
- descripdma += 16;
- else
- descripdma = wc->descripdma;
-
- /* Transmit descriptor */
- descrip[0] = 0x80000000;
- descrip[1] = 0xe5800000 | (SFRAME_SIZE);
- if (x % 2)
- descrip[2] = writedma + SFRAME_SIZE;
- else
- descrip[2] = writedma;
- descrip[3] = descripdma;
-
- /* Receive descriptor */
- descrip[0 + ERING_SIZE * 4] = 0x80000000;
- descrip[1 + ERING_SIZE * 4] = 0x01000000 | (SFRAME_SIZE);
- if (x % 2)
- descrip[2 + ERING_SIZE * 4] = readdma + SFRAME_SIZE;
- else
- descrip[2 + ERING_SIZE * 4] = readdma;
- descrip[3 + ERING_SIZE * 4] = descripdma + ERING_SIZE * 16;
-
- /* Advance descriptor */
- descrip += 4;
- }
-}
-
-static inline void t1_reinit_descriptor(struct t1 *wc, int tx, int dbl, char *s)
-{
- int o2 = dbl * 4;
-
- if (!tx)
- o2 += ERING_SIZE * 4;
-
- wc->descripchunk[o2] = 0x80000000;
-}
-
static inline void cmd_dequeue(struct t1 *wc, volatile unsigned char *writechunk, int eframe, int slot)
{
struct command *curcmd=NULL;
@@ -290,93 +207,6 @@ static inline void cmd_decipher(struct t1 *wc, volatile unsigned char *readchunk
}
}
-static inline unsigned int __t1_sdi_clk(struct t1 *wc)
-{
- unsigned int ret;
-
- wc->sdi &= ~SDI_CLK;
- __t1_setctl(wc, 0x0048, wc->sdi);
- ret = __t1_getctl(wc, 0x0048);
- wc->sdi |= SDI_CLK;
- __t1_setctl(wc, 0x0048, wc->sdi);
- return ret & SDI_DIN;
-}
-
-static inline void __t1_sdi_sendbits(struct t1 *wc, unsigned int bits, int count)
-{
- wc->sdi &= ~SDI_DREAD;
- __t1_setctl(wc, 0x0048, wc->sdi);
- while (count--) {
- if (bits & (1 << count))
- wc->sdi |= SDI_DOUT;
- else
- wc->sdi &= ~SDI_DOUT;
- __t1_sdi_clk(wc);
- }
-}
-
-static inline unsigned int __t1_sdi_recvbits(struct t1 *wc, int count)
-{
- unsigned int bits=0;
-
- wc->sdi |= SDI_DREAD;
- __t1_setctl(wc, 0x0048, wc->sdi);
- while (count--) {
- bits <<= 1;
- if (__t1_sdi_clk(wc))
- bits |= 1;
- else
- bits &= ~1;
- }
- return bits;
-}
-
-static inline unsigned short __t1_getsdi(struct t1 *wc, unsigned char addr)
-{
- unsigned int bits;
-
- /* Send preamble */
- bits = 0xffffffff;
- __t1_sdi_sendbits(wc, bits, 32);
- bits = (0x6 << 10) | (1 << 5) | (addr);
- __t1_sdi_sendbits(wc, bits, 14);
-
- return __t1_sdi_recvbits(wc, 18);
-}
-
-static inline unsigned short t1_getsdi(struct t1 *wc, unsigned char addr)
-{
- unsigned long flags;
- unsigned short val;
-
- spin_lock_irqsave(&wc->reglock, flags);
- val = __t1_getsdi(wc, addr);
- spin_unlock_irqrestore(&wc->reglock, flags);
-
- return val;
-}
-
-static inline void __t1_setsdi(struct t1 *wc, unsigned char addr, unsigned short value)
-{
- unsigned int bits;
-
- /* Send preamble */
- bits = 0xffffffff;
- __t1_sdi_sendbits(wc, bits, 32);
- bits = (0x5 << 12) | (1 << 7) | (addr << 2) | 0x2;
- __t1_sdi_sendbits(wc, bits, 16);
- __t1_sdi_sendbits(wc, value, 16);
-}
-
-static inline void t1_setsdi(struct t1 *wc, unsigned char addr, unsigned short value)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&wc->reglock, flags);
- __t1_setsdi(wc, addr, value);
- spin_unlock_irqrestore(&wc->reglock, flags);
-}
-
static inline int t1_setreg_full(struct t1 *wc, int addr, int val, int inisr, int vpm_num)
{
unsigned long flags;
@@ -451,10 +281,16 @@ static inline int t1_getreg_isr(struct t1 *wc, int addr)
/* find our requested command */
for (x = 0;x < sizeof(wc->cmdq.cmds) / sizeof(wc->cmdq.cmds[0]); x++) {
if ((wc->cmdq.cmds[x].flags & __CMD_RD) &&
- (wc->cmdq.cmds[x].flags & __CMD_FIN) &&
- (wc->cmdq.cmds[x].address==addr)) {
- hit = x;
- break;
+ (wc->cmdq.cmds[x].address==addr))
+ {
+ if (wc->cmdq.cmds[x].flags & __CMD_FIN) {
+ hit = x;
+ break;
+ }
+ else {
+ /* still in progress. */
+ return -1;
+ }
}
}
@@ -595,68 +431,6 @@ static inline int t1_getpins(struct t1 *wc, int inisr)
return ret;
}
-static void t1_setintmask(struct t1 *wc, unsigned int intmask)
-{
- wc->intmask = intmask;
- t1_setctl(wc, 0x0038, intmask);
-}
-
-static void t1_enable_interrupts(struct t1 *wc)
-{
- /* Enable interrupts */
- t1_setintmask(wc, 0x00010041); /* only RX */
-}
-
-static void t1_disable_interrupts(struct t1 *wc)
-{
- /* Disable interrupts */
- t1_setintmask(wc, 0x00000000);
- t1_setctl(wc, 0x0084, 0x00000000);
-}
-
-static void t1_start_dma(struct t1 *wc)
-{
- unsigned int reg;
- int x;
-
- wmb();
- t1_setctl(wc, 0x0020, wc->descripdma);
- t1_setctl(wc, 0x0018, wc->descripdma + (16 * ERING_SIZE));
- /* Start receiver/transmitter */
- reg = t1_getctl(wc, 0x0030);
- t1_setctl(wc, 0x0030, reg | 0x00002002);
- t1_setctl(wc, 0x0008, 0x00000000);
- t1_setctl(wc, 0x0010, 0x00000000);
- reg = t1_getctl(wc, 0x0028);
- t1_setctl(wc, 0x0028, reg);
-
- /* Set Reset - now with MAGIC TIPS */
- t1_setctl(wc, 0x0048, 0x00000000);
- for (x = 0; x < 10; x++)
- schluffen(&wc->regq);
- /* Clear reset */
- t1_setctl(wc, 0x0048, 0x00010000);
- for (x = 0; x < 10; x++)
- schluffen(&wc->regq);
- /* Switch to caring only about receive interrupts */
- t1_setintmask(wc, 0x00010040);
-}
-
-static void t1_stop_dma(struct t1 *wc)
-{
- /* Disable interrupts and reset */
- unsigned int reg;
-
- /* Disable interrupts */
- t1_setintmask(wc, 0x00000000);
- t1_setctl(wc, 0x0084, 0x00000000);
- t1_setctl(wc, 0x0048, 0x00000000);
- /* Reset the part to be on the safe side */
- reg = t1_getctl(wc, 0x0000);
- reg |= 0x00000001;
- t1_setctl(wc, 0x0000, reg);
-}
-
static void __t1xxp_set_clear(struct t1 *wc, int channo)
{
int i,j;
@@ -681,8 +455,6 @@ static void __t1xxp_set_clear(struct t1 *wc, int channo)
static void t1_release(struct t1 *wc)
{
zt_unregister(&wc->span);
- if (wc->freeregion)
- release_region(wc->iobase, 0xff);
kfree(wc);
printk("Freed a Wildcard TE12xP\n");
}
@@ -1076,8 +848,6 @@ static inline void __t1_check_sigbits(struct t1 *wc)
spin_lock(&wc->reglock);
}
}
- } else {
- debug_printk(1, "no space to request register in isr\n");
}
}
} else if (wc->span.lineconfig & ZT_CONFIG_D4) {
@@ -1240,28 +1010,17 @@ static int t1xxp_close(struct zt_chan *chan)
static int t1xxp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data)
{
- struct t4_regs regs;
unsigned int x;
struct t1 *wc = chan->pvt;
switch (cmd) {
case WCT4_GET_REGS:
- wc = chan->pvt;
- for (x = 0; x < sizeof(regs.pci) / sizeof(regs.pci[0]); x++)
-#if 1
- regs.pci[x] = (inb(wc->iobase + (x << 2))) |
- (inb(wc->iobase + (x << 2) + 1) << 8) |
- (inb(wc->iobase + (x << 2) + 2) << 16) |
- (inb(wc->iobase + (x << 2) + 3) << 24);
-#else
- regs.pci[x] = (inb(wc->iobase + x));
-#endif
-
- for (x = 0; x < sizeof(regs.regs) / sizeof(regs.regs[0]); x++)
- regs.regs[x] = t1_getreg(wc, x, 0);
-
- if (copy_to_user((struct t4_regs *) data, &regs, sizeof(regs)))
- return -EFAULT;
+ /* Since all register access was moved into the voicebus
+ * module....this was removed. Although...why does the client
+ * library need access to the registers (debugging)? \todo ..
+ */
+ WARN_ON(1);
+ return -ENOSYS;
break;
#ifdef VPM_SUPPORT
case ZT_TONEDETECT:
@@ -1339,6 +1098,9 @@ static int t1xxp_echocan_with_params(struct zt_chan *chan, struct zt_echocanpara
static int t1_software_init(struct t1 *wc)
{
int x;
+ struct pci_dev* dev;
+
+ dev = voicebus_get_pci_dev(wc->vb);
/* Find position */
for (x = 0; x < sizeof(ifaces) / sizeof(ifaces[0]); x++) {
@@ -1357,7 +1119,7 @@ static int t1_software_init(struct t1 *wc)
sprintf(wc->span.name, "WCT1/%d", wc->num);
snprintf(wc->span.desc, sizeof(wc->span.desc) - 1, "%s Card %d", wc->variety, wc->num);
wc->span.manufacturer = "Digium";
- zap_copy_string(wc->span.devicetype, wc->variety, sizeof(wc->span.devicetype));
+ strncpy(wc->span.devicetype, wc->variety, sizeof(wc->span.devicetype) - 1);
#if defined(VPM_SUPPORT)
if (wc->vpm150m)
@@ -1365,11 +1127,11 @@ static int t1_software_init(struct t1 *wc)
#endif
snprintf(wc->span.location, sizeof(wc->span.location) - 1,
- "PCI Bus %02d Slot %02d", wc->dev->bus->number, PCI_SLOT(wc->dev->devfn) + 1);
+ "PCI Bus %02d Slot %02d", dev->bus->number, PCI_SLOT(dev->devfn) + 1);
wc->span.spanconfig = t1xxp_spanconfig;
wc->span.chanconfig = t1xxp_chanconfig;
- wc->span.irq = wc->dev->irq;
+ wc->span.irq = dev->irq;
wc->span.startup = t1xxp_startup;
wc->span.shutdown = t1xxp_shutdown;
wc->span.rbsbits = t1xxp_rbsbits;
@@ -1660,7 +1422,8 @@ static void __t1_do_counters(struct t1 *wc)
static inline void t1_isr_misc(struct t1 *wc)
{
- unsigned int x;
+ const unsigned int x = wc->intcount & 0x3f;
+ int buffer_count = voicebus_current_latency(wc->vb);
if (unlikely(!wc->initialized)) return;
@@ -1668,52 +1431,35 @@ static inline void t1_isr_misc(struct t1 *wc)
__t1_do_counters(wc);
- x = wc->intcount & 0xF;
- switch (x) {
- case 0:
- __t1_check_sigbits_reads(wc);
- break;
- case 1:
- if (!(wc->intcount & 0x30)) {
- __t1_check_alarms_reads(wc);
- wc->alarms_read=1;
- }
- break;
- case 2:
- break;
- case 4:
- break;
- case 5:
- break;
- case 7:
- __t1_check_sigbits(wc);
- break;
- case 8:
- if (wc->alarms_read) {
- __t1_check_alarms(wc);
- wc->alarms_read=0;
- }
- break;
- case 9:
- clean_leftovers(wc);
- break;
+ if ( 0 == x ) {
+ __t1_check_sigbits_reads(wc);
+ }
+ else if ( 1 == x ) {
+ if (!(wc->intcount & 0x30)) {
+ __t1_check_alarms_reads(wc);
+ wc->alarms_read=1;
+ }
+ }
+ else if ( x == buffer_count*2) {
+ __t1_check_sigbits(wc);
+ }
+ else if ( x == (buffer_count*2)+1 ) {
+ if (wc->alarms_read) {
+ __t1_check_alarms(wc);
+ wc->alarms_read=0;
+ }
+ }
+ else if ( x == (buffer_count*2)+2) {
+ clean_leftovers(wc);
}
}
-static inline void t1_transmitprep(struct t1 *wc, int dbl)
+static inline void t1_transmitprep(struct t1 *wc, unsigned char* writechunk)
{
- volatile unsigned char *writechunk;
int x;
int y;
int chan;
- dbl = dbl % 2;
-
- writechunk = (volatile unsigned char *)(wc->writechunk);
- if (dbl)
- /* Write is at interrupt address. Start writing from normal offset */
- writechunk += SFRAME_SIZE;
-
/* Calculate Transmission */
if (likely(wc->initialized)) {
spin_unlock(&wc->reglock);
@@ -1757,17 +1503,11 @@ static inline void cmd_retransmit(struct t1 *wc)
}
}
-static inline void t1_receiveprep(struct t1 *wc, int dbl)
+static inline void t1_receiveprep(struct t1 *wc, unsigned char* readchunk)
{
- volatile unsigned char *readchunk;
int x,chan;
unsigned char expected;
- dbl = dbl % 2;
-
- readchunk = (volatile unsigned char *)wc->readchunk;
- if (dbl)
- readchunk += SFRAME_SIZE;
for (x = 0; x < ZT_CHUNKSIZE; x++) {
if (likely(wc->initialized)) {
for (chan = 0; chan < wc->span.channels; chan++) {
@@ -1782,7 +1522,7 @@ static inline void t1_receiveprep(struct t1 *wc, int dbl)
wc->span.irqmisses++;
cmd_retransmit(wc);
if (unlikely(debug && wc->initialized))
- module_printk("oops: rxident=%d expected=%d\n", wc->rxident, expected);
+ module_printk("oops: rxident=%d expected=%d x=%d\n", wc->rxident, expected, x);
}
}
cmd_decipher(wc, readchunk);
@@ -1809,127 +1549,36 @@ static inline void t1_receiveprep(struct t1 *wc, int dbl)
wake_up_interruptible(&wc->regq);
}
-static inline int t1_check_descriptor(struct t1 *wc, int tx)
-{
- int o2 = 0;
-
- if (!tx) {
- o2 += ERING_SIZE * 4;
- o2 += wc->rdbl * 4;
- } else {
- o2 += wc->tdbl * 4;
- }
-
- if (!(wc->descripchunk[o2] & 0x80000000)) {
- if (tx) {
- wc->txints++;
- t1_transmitprep(wc, wc->tdbl);
- t1_reinit_descriptor(wc, tx, wc->tdbl, "txchk");
- wc->tdbl = (wc->tdbl + 1) % ERING_SIZE;
- wc->intcount++;
- t1_isr_misc(wc);
- } else {
- wc->rxints++;
- t1_receiveprep(wc, wc->rdbl);
- t1_reinit_descriptor(wc, tx, wc->rdbl, "rxchk");
- wc->rdbl = (wc->rdbl + 1) % ERING_SIZE;
- }
- return 1;
- }
- return 0;
-}
-
-static int t1_hardware_init(struct t1 *wc)
+static void
+t1_handle_transmit(void* vbb, void* context)
{
- /* Hardware stuff */
- unsigned int reg;
- unsigned long newjiffies;
-
- /* Initialize descriptors */
- t1_init_descriptors(wc);
-
- /* Enable I/O Access */
- pci_read_config_dword(wc->dev, PCI_COMMAND, &reg);
- reg |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
- pci_write_config_dword(wc->dev, PCI_COMMAND, reg);
- debug_printk(1, "PCI Config reg is %08x\n", reg);
-
- t1_setctl(wc, 0x0000, 0xfff88001);
-
- newjiffies = jiffies + HZ/10;
- while(((reg = t1_getctl(wc,0x0000)) & 0x00000001) && ( time_after(newjiffies,jiffies) ));
- debug_printk(1, "ctlreg 0x0000 now=%08x!\n", reg);
-
- t1_setctl(wc, 0x0000, 0xfff88000);
-
- /* Configure watchdogs, access, etc */
- t1_setctl(wc, 0x0030, 0x00280048);
- t1_setctl(wc, 0x0078, 0x00000013 /* | (1 << 28) */);
-
- reg = t1_getctl(wc, 0x00fc);
- t1_setctl(wc, 0x00fc, (reg & ~0x7) | 0x7); /* normal mode */
- t1_setsdi(wc, 0x00, 0x0100);
- t1_setsdi(wc, 0x16, 0x2100);
- debug_printk(1, "Detected SDI REG0: %08x\n", t1_getsdi(wc, 0x00));
- debug_printk(1, "Detected SDI REG1: %08x\n", t1_getsdi(wc, 0x01));
- debug_printk(1, "Detected SDI REG2: %08x\n", t1_getsdi(wc, 0x02));
-
- reg = t1_getctl(wc, 0x00fc);
- debug_printk(1, "(pre) Reg fc is %08x\n", reg);
-
- t1_setctl(wc, 0x00fc, (reg & ~0x7) | 0x4); /* mac only */
- t1_setsdi(wc, 0x00, 0x0100); /* full duplex */
- t1_setsdi(wc, 0x16, 0x2100);
- reg = t1_getctl(wc, 0x00fc);
- debug_printk(1, "(post) ctlreg 0xfc=%08x\n", reg);
- debug_printk(1, "Detected SDI REG2: %08x\n", t1_getsdi(wc, 0x02));
- debug_printk(1, "ctlreg 0x0088=%08x\n", t1_getctl(wc, 0x0088));
-
- return 0;
+ struct t1* wc = context;
+ /* Either this function is called from within interrupt context, or
+ * the reglock will never be acquired from interrupt context, so it's
+ * safe to grab it without locking interrupt.
+ */
+ memset(vbb, 0, SFRAME_SIZE);
+ spin_lock(&wc->reglock);
+ wc->txints++;
+ t1_transmitprep(wc, vbb);
+ wc->intcount++;
+ t1_isr_misc(wc);
+ spin_unlock(&wc->reglock);
+ voicebus_transmit(wc->vb, vbb);
}
-
-ZAP_IRQ_HANDLER(te12xp_interrupt)
+static void
+t1_handle_receive(void* vbb, void* context)
{
- struct t1 *wc = dev_id;
- unsigned int ints;
- int res;
-
- /* Read interrupts */
+ struct t1* wc = context;
+ wc->rxints++;
+ /* Either this function is called from within interrupt context, or
+ * the reglock will never be acquired from interrupt context, so it's
+ * safe to grab it without locking interrupt.
+ */
spin_lock(&wc->reglock);
- ints = __t1_getctl(wc, 0x0028);
- ints &= 0x3fef; /* Just look at the interrupt conditions */
-
- if (!ints) {
- spin_unlock(&wc->reglock);
-#ifdef LINUX26
- return IRQ_NONE;
-#else
- return;
-#endif
- }
-
- /* clear interrupts interrupts (we only get here if interrupt is for us) */
- __t1_setctl(wc, 0x0028, ints);
-
- if (ints & 0x00000041) {
- do {
- res = t1_check_descriptor(wc, 0);
- res |= t1_check_descriptor(wc, 1);
- } while(res);
- }
-
- if (ints & 0x0000a3ae) {
- /* This will allow us to recover if interrupts are held for a long period of time */
- debug_printk(1, "Abnormal interrupt %08x detected\n", ints);
- __t1_setctl(wc, 0x0008, 0x00000000);
- __t1_setctl(wc, 0x0010, 0x00000000);
- }
+ t1_receiveprep(wc, vbb);
spin_unlock(&wc->reglock);
-
-#ifdef LINUX26
- return IRQ_RETVAL(1);
-#endif
}
static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -1937,6 +1586,8 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi
struct t1 *wc;
struct t1_desc *d = (struct t1_desc *) ent->driver_data;
unsigned int x;
+ int res;
+ int startinglatency;
for (x = 0; x < sizeof(ifaces) / sizeof(ifaces[0]); x++)
if (!ifaces[x]) break;
@@ -1946,9 +1597,7 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi
return -EIO;
}
- if (pci_enable_device(pdev))
- return -EIO;
-
+retry:
wc = kmalloc(sizeof(*wc), GFP_KERNEL);
if (!wc)
return -ENOMEM;
@@ -1956,72 +1605,44 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi
ifaces[x] = wc;
memset(wc, 0, sizeof(*wc));
spin_lock_init(&wc->reglock);
- wc->iobase = pci_resource_start(pdev, 0);
- wc->dev = pdev;
wc->variety = d->name;
- /* Keep track of whether we need to free the region */
- if (request_region(wc->iobase, 0xff, te12xp_driver.name))
- wc->freeregion = 1;
-
- /* Allocate enough memory for two zt chunks, receive and transmit.
- * Each sample uses 32 bits. Allocate an extra set just for
- * control too */
- wc->writechunk = (int *) pci_alloc_consistent(pdev, PCI_WINDOW_SIZE, &wc->writedma);
- if (!wc->writechunk) {
- module_printk("Unable to allocate DMA-able memory\n");
- if (wc->freeregion)
- release_region(wc->iobase, 0xff);
+ wc->txident = 1;
+
+ init_waitqueue_head(&wc->regq);
+ snprintf(wc->name, sizeof(wc->name)-1, "wcte12xp%d", x);
+ if ((res=voicebus_init(pdev, SFRAME_SIZE, wc->name,
+ t1_handle_receive, t1_handle_transmit, wc, &wc->vb)))
+ {
+ WARN_ON(1);
kfree(wc);
- return -ENOMEM;
+ return res;
}
- wc->readchunk = wc->writechunk + SFRAME_SIZE / 2; /* in doublewords */
- wc->readdma = wc->writedma + SFRAME_SIZE * 2; /* in bytes */
-
- wc->descripchunk = wc->readchunk + SFRAME_SIZE / 2; /* in doublewords */
- wc->descripdma = wc->readdma + SFRAME_SIZE * 2; /* in bytes */
-
- /* Initialize Write/Buffers to all blank data */
- memset((void *)wc->writechunk, 0x00, SFRAME_SIZE * 2);
- memset((void *)wc->readchunk, 0x00, SFRAME_SIZE * 2);
-
- init_waitqueue_head(&wc->regq);
-
- /* Enable bus mastering */
- pci_set_master(pdev);
-
/* Keep track of which device we are */
pci_set_drvdata(pdev, wc);
-
- if (request_irq(pdev->irq, te12xp_interrupt, ZAP_IRQ_SHARED, te12xp_driver.name, wc)) {
- module_printk("Unable to request IRQ %d\n", pdev->irq);
- if (wc->freeregion)
- release_region(wc->iobase, 0xff);
- pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *) wc->writechunk, wc->writedma);
- pci_set_drvdata(pdev, NULL);
- kfree(wc);
- return -EIO;
- }
-
- if (t1_hardware_init(wc)) {
- /* Set Reset Low */
- t1_stop_dma(wc);
- /* Free Resources */
- free_irq(pdev->irq, wc);
- if (wc->freeregion)
- release_region(wc->iobase, 0xff);
- pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *) wc->writechunk, wc->writedma);
- pci_set_drvdata(pdev, NULL);
-
- kfree(wc);
- return -EIO;
-
+ if (VOICEBUS_DEFAULT_LATENCY != latency) {
+ voicebus_set_minlatency(wc->vb, latency);
}
-
- t1_enable_interrupts(wc);
- t1_start_dma(wc);
+ voicebus_start(wc->vb);
+ startinglatency = voicebus_current_latency(wc->vb);
t1_hardware_post_init(wc);
t1_software_init(wc);
+ if (voicebus_current_latency(wc->vb) > startinglatency) {
+ /* The voicebus library increased the latency during
+ * initialization because the host wasn't able to service the
+ * interrupts from the adapter quickly enough. In this case,
+ * we'll increase our latency and restart the initialization.
+ */
+ printk(KERN_NOTICE "%s: Restarting board initialization " \
+ "after increasing latency.\n", wc->name);
+ latency = voicebus_current_latency(wc->vb);
+ zt_unregister(&wc->span);
+ voicebus_release(wc->vb);
+ wc->vb = NULL;
+ kfree(wc);
+ wc = NULL;
+ goto retry;
+ }
module_printk("Found a %s\n", wc->variety);
return 0;
@@ -2045,19 +1666,14 @@ static void __devexit te12xp_remove_one(struct pci_dev *pdev)
destroy_workqueue(vpm150m->wq);
}
#endif
- /* Stop any DMA */
- t1_stop_dma(wc);
-
- /* In case hardware is still there */
- t1_disable_interrupts(wc);
-
+
+ BUG_ON(!wc->vb);
+ voicebus_release(wc->vb);
+ wc->vb = NULL;
+
if (debug && wc->isrreaderrors)
debug_printk(1, "isrreaderrors=%d\n", wc->isrreaderrors);
- /* Immediately free resources */
- free_irq(pdev->irq, wc);
- pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma);
-
#ifdef VPM_SUPPORT
if(vpm150m) {
spin_lock_irqsave(&wc->reglock, flags);
@@ -2084,7 +1700,7 @@ static struct pci_device_id te12xp_pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, te12xp_pci_tbl);
struct pci_driver te12xp_driver = {
- name: "wcte12x[p]",
+ name: "wcte12xp",
probe: te12xp_init_one,
#ifdef LINUX26
remove: __devexit_p(te12xp_remove_one),
@@ -2117,13 +1733,11 @@ module_param(loopback, int, S_IRUGO | S_IWUSR);
module_param(t1e1override, int, S_IRUGO | S_IWUSR);
module_param(j1mode, int, S_IRUGO | S_IWUSR);
module_param(alarmdebounce, int, S_IRUGO | S_IWUSR);
+module_param(latency, int, S_IRUGO | S_IWUSR);
#ifdef VPM_SUPPORT
module_param(vpmsupport, int, S_IRUGO | S_IWUSR);
module_param(vpmdtmfsupport, int, S_IRUGO | S_IWUSR);
module_param(vpmtsisupport, int, S_IRUGO | S_IWUSR);
-module_param(vpmnlptype, int, S_IRUGO | S_IWUSR);
-module_param(vpmnlpthresh, int, S_IRUGO | S_IWUSR);
-module_param(vpmnlpmaxsupp, int, S_IRUGO | S_IWUSR);
#endif
#else
MODULE_PARM(debug, "i");
diff --git a/kernel/wcte12xp/vpmadt032.c b/kernel/wcte12xp/vpmadt032.c
index 0cb45a0..f118fd1 100644
--- a/kernel/wcte12xp/vpmadt032.c
+++ b/kernel/wcte12xp/vpmadt032.c
@@ -35,6 +35,7 @@
#include <linux/list.h>
#include "zaptel.h"
+#include "voicebus.h"
#include "wcte12xp.h"
#include "vpmadt032.h"
@@ -698,6 +699,7 @@ void t1_vpm150m_init(struct t1 *wc) {
extern u8 _binary_zaptel_fw_vpmadt032_bin_start[];
#else
static const char vpmadt032_firmware[] = "zaptel-fw-vpmadt032.bin";
+ struct pci_dev* pdev = voicebus_get_pci_dev(wc->vb);
#endif
#if 0
@@ -771,44 +773,6 @@ void t1_vpm150m_init(struct t1 *wc) {
}
debug_printk(1, "Passed\n");
-#if 0
- /* begin short test */
-#define TEST_SIZE 1
- {
- int i;
- unsigned short msg[TEST_SIZE];
-
- set_bit(VPM150M_HPIRESET, &vpm150m->control);
- msleep(2000);
-
- /* lets see whats in there to start with*/
- gpakReadDspMemory(vpm150m->dspid, 0x1000, TEST_SIZE, msg);
- printk("at first :");
- for (i = 0; i< TEST_SIZE; i++)
- printk("%04x ", msg[i]);
- printk("\n");
-
- /* what if we put dead in there*/
- for (i = 0; i< TEST_SIZE; i++)
- msg[i] = 0xdead;
- gpakWriteDspMemory(vpm150m->dspid, 0x1000, TEST_SIZE, msg);
- gpakReadDspMemory(vpm150m->dspid, 0x1000, TEST_SIZE, msg);
- printk("now :");
- for (i = 0; i< TEST_SIZE; i++)
- printk("%04x ", msg[i]);
- printk("\n");
-
- /* lets see if its in there now */
- gpakReadDspMemory(vpm150m->dspid, 0x1000, TEST_SIZE, msg);
- printk("try again:");
- for (i = 0; i< TEST_SIZE; i++)
- printk("%04x ", msg[i]);
- printk("\n");
- }
-
- goto failed_exit;
-#endif
-
#define TEST_SIZE 2
if (debug) {
int i;
@@ -866,51 +830,9 @@ void t1_vpm150m_init(struct t1 *wc) {
printk("%x ", msg[i]);
printk("\n");
}
-#if 0
- printk("Sending\n");
-
- for (i = 0; i < 4; i++) {
- unsigned short x = 0xffff;
- t1_vpm150m_setreg(wc, 1, 0x1000 + i, &x);
- }
-
- gpakReadDspMemory(vpm150m->dspid, 0x1000, 4, imsg);
-
- printk("Read back:\n");
- for (i = 0; i < 4; i++)
- printk("%x ", imsg[i]);
- printk("\n");
-
- printk("Sending\n");
- gpakWriteDspMemory(vpm150m->dspid, 0x1000, 4, omsg);
- for (i = 0; i < 4; i++)
- t1_vpm150m_getreg(wc, 1, 0x1000 + i, &imsg[i]);
- printk("Read back\n");
- for (i = 0; i < 4; i++)
- printk("%x ", imsg[i]);
- printk("\n");
-
-#endif
-
-#if 0
- /* Load the firmware */
- set_bit(VPM150M_SPIRESET, &vpm150m->control);
-
- /* Wait for it to boot */
- msleep(7000);
-
- pingstatus = gpakPingDsp(vpm150m->dspid, &version);
-
- if (pingstatus) {
- module_printk("Pingstatus %d, you failed!!! Ha ha ha ha\n", pingstatus);
- } else
- module_printk("version is 0x%08x\n", version);
-
- if (pingstatus || (version != 0x106)) {
-#endif
#if defined(HOTPLUG_FIRMWARE)
- if ((request_firmware(&firmware, vpmadt032_firmware, &wc->dev->dev) != 0) ||
+ if ((request_firmware(&firmware, vpmadt032_firmware, &pdev->dev) != 0) ||
!firmware) {
printk("VPMADT032: firmware %s not available from userspace\n", vpmadt032_firmware);
goto failed_exit;
diff --git a/kernel/wcte12xp/wcte12xp.h b/kernel/wcte12xp/wcte12xp.h
index 20e0558..aa6306b 100644
--- a/kernel/wcte12xp/wcte12xp.h
+++ b/kernel/wcte12xp/wcte12xp.h
@@ -54,9 +54,8 @@
#define PCI_WINDOW_SIZE ((2 * 2 * 2 * SFRAME_SIZE) + (2 * ERING_SIZE * 4))
-#define MAX_COMMANDS 7*7*2 /* 42 bytes /3 (cntl,addr,data) /2 (cs) */
+#define MAX_COMMANDS 7*7*2*2 /* 42 bytes /3 (cntl,addr,data) /2 (cs) */
-#define ISR_COMMANDS 2
#define NUM_EC 4
#define __CMD_VPM (1 << 16) /* flag for VPM action */
@@ -106,13 +105,11 @@ struct command {
struct cmdq {
struct command cmds[MAX_COMMANDS];
- unsigned char isrshadow[ISR_COMMANDS];
};
struct vpm150m;
struct t1 {
- struct pci_dev *dev;
spinlock_t reglock;
unsigned char txident;
unsigned char rxident;
@@ -127,6 +124,7 @@ struct t1 {
int alarmcount; /* How much red alarm we've seen */
int alarmdebounce;
char *variety;
+ char name[80];
unsigned int intcount;
int sync;
int dead;
@@ -141,29 +139,18 @@ struct t1 {
int initialized;
int *chanmap;
unsigned char ledtestreg;
- unsigned long iobase;
unsigned char ec_chunk1[32][ZT_CHUNKSIZE];
unsigned char ec_chunk2[32][ZT_CHUNKSIZE];
struct zt_span span; /* Span */
struct zt_chan chans[32]; /* Channels */
- int freeregion;
- unsigned int intmask;
wait_queue_head_t regq;
struct cmdq cmdq;
struct command dummy; /* preallocate for dummy noop command */
unsigned char ctlreg;
- int rdbl;
- int tdbl;
unsigned int rxints;
unsigned int txints;
- unsigned int sdi;
int usecount;
- dma_addr_t readdma;
- dma_addr_t writedma;
- dma_addr_t descripdma;
- volatile unsigned int *writechunk;
- volatile unsigned int *readchunk;
- volatile unsigned int *descripchunk;
+ struct voicebus* vb;
unsigned int isrreaderrors;
#ifdef VPM_SUPPORT
int vpm;
diff --git a/kernel/zaptel-base.c b/kernel/zaptel-base.c
index c4b4ccf..da503eb 100644
--- a/kernel/zaptel-base.c
+++ b/kernel/zaptel-base.c
@@ -1157,16 +1157,23 @@ static int start_tone(struct zt_chan *chan, int tone)
if (tone == -1) {
/* Just stop the current tone */
res = 0;
+ } else if (!chan->curzone) {
+ static int __warnonce = 1;
+ if (__warnonce) {
+ __warnonce = 0;
+ /* The tonezones are loaded by ztcfg based on /etc/zaptel.conf. */
+ printk(KERN_WARNING "zaptel: Cannot start tones until tone zone is loaded.\n");
+ }
+ /* Note that no tone zone exists at the moment */
+ res = -ENODATA;
} else if ((tone >= 0 && tone <= ZT_TONE_MAX)) {
- if (chan->curzone) {
- /* Have a tone zone */
- if (chan->curzone->tones[tone]) {
- chan->curtone = chan->curzone->tones[tone];
- res = 0;
- } else /* Indicate that zone is loaded but no such tone exists */
- res = -ENOSYS;
- } else /* Note that no tone zone exists at the moment */
- res = -ENODATA;
+ /* Have a tone zone */
+ if (chan->curzone->tones[tone]) {
+ chan->curtone = chan->curzone->tones[tone];
+ res = 0;
+ } else { /* Indicate that zone is loaded but no such tone exists */
+ res = -ENOSYS;
+ }
} else if (chan->digitmode == DIGIT_MODE_DTMF) {
if ((tone >= ZT_TONE_DTMF_BASE) && (tone <= ZT_TONE_DTMF_MAX)) {
chan->dialing = 1;
@@ -1187,8 +1194,9 @@ static int start_tone(struct zt_chan *chan, int tone)
}
} else {
res = -EINVAL;
- };
+ }
} else {
+ chan->dialing = 0;
res = -EINVAL;
}
@@ -2827,6 +2835,16 @@ struct zt_tone *zt_mf_tone(const struct zt_chan *chan, char digit, int digitmode
{
unsigned int tone_index;
+ if (!chan->curzone) {
+ static int __warnonce = 1;
+ if (__warnonce) {
+ __warnonce = 0;
+ /* The tonezones are loaded by ztcfg based on /etc/zaptel.conf. */
+ printk(KERN_WARNING "zaptel: Cannot get dtmf tone until tone zone is loaded.\n");
+ }
+ return NULL;
+ }
+
switch (digitmode) {
case DIGIT_MODE_DTMF:
switch (digit) {
@@ -3981,6 +3999,12 @@ static int zt_chanandpseudo_ioctl(struct inode *inode, struct file *file, unsign
for (s = stack.tdo.dialstr; *s; s++)
*s = toupper(*s);
spin_lock_irqsave(&chan->lock, flags);
+ if (!chan->curzone) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ /* The tone zones are loaded by ztcfg from /etc/zaptel.conf */
+ printk(KERN_WARNING "zaptel: Cannot dial until a tone zone is loaded.\n");
+ return -ENODATA;
+ }
switch (stack.tdo.op) {
case ZT_DIAL_OP_CANCEL:
chan->curtone = NULL;
@@ -4853,6 +4877,11 @@ static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cm
case ZT_RING:
case ZT_START:
spin_lock_irqsave(&chan->lock, flags);
+ if (!chan->curzone) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ printk(KERN_WARNING "zaptel: Cannot start tone until a tone zone is loaded.\n");
+ return -ENODATA;
+ }
if (chan->txstate != ZT_TXSTATE_ONHOOK) {
spin_unlock_irqrestore(&chan->lock, flags);
return -EBUSY;