From cd81703764c4d2884c60f3632f15d5bfc44dc31d Mon Sep 17 00:00:00 2001 From: sruffell Date: Thu, 27 Mar 2008 21:17:46 +0000 Subject: - 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 --- doc/module-parameters.txt | 13 + kernel/fxo_modes.h | 588 ++++++++++++++++ kernel/voicebus.c | 1492 ++++++++++++++++++++++++++++++++++++++++ kernel/voicebus.h | 53 ++ kernel/wctdm.c | 397 +++++------ kernel/wctdm24xxp/Kbuild | 2 +- kernel/wctdm24xxp/base.c | 1057 +++++++++------------------- kernel/wctdm24xxp/wctdm24xxp.h | 41 +- kernel/wcte12xp/Kbuild | 2 +- kernel/wcte12xp/base.c | 612 +++------------- kernel/wcte12xp/vpmadt032.c | 84 +-- kernel/wcte12xp/wcte12xp.h | 19 +- kernel/zaptel-base.c | 49 +- 13 files changed, 2817 insertions(+), 1592 deletions(-) create mode 100644 kernel/fxo_modes.h create mode 100644 kernel/voicebus.c create mode 100644 kernel/voicebus.h 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 + * + * 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 + * and based on previous work by Mark Spencer , + * Matthew Fredrickson , and + * Michael Spiceland + * + * 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 +#include +#include +#include +#include +#include +#include + +#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, ¶m); +} +/*! \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 |= 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 + * and based on previous work by Mark Spencer , + * Matthew Fredrickson , and + * Michael Spiceland + * + * 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 #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 * Support for TDM800P and VPM150M by Matthew Fredrickson * - * Copyright (C) 2005,2006, Digium, Inc. + * Copyright (C) 2005 - 2008 Digium, Inc. * All rights reserved. * * Sections for QRV cards written by Jim Dixon @@ -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;xcards;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;xrxident+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;xtype;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;xtype;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;xcards;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;xdescripdma; - - /* 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;xcards;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 |= 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;xcards;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;yflags[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 + #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 * William Meadows * - * Copyright (C) 2007, Digium, Inc. + * Copyright (C) 2007-2008, Digium, Inc. * * All rights reserved. * @@ -34,9 +34,7 @@ #include #include #include -#include #include -#include #ifdef LINUX26 #include @@ -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, ®s, 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 |= 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 #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; -- cgit v1.2.3