2010-08-28 05:59:27 +08:00
/*
* Wildcard TDM2400P TDM FXS / FXO Interface Driver for DAHDI Telephony interface
*
* Written by Mark Spencer < markster @ digium . com >
* Support for TDM800P and VPM150M by Matthew Fredrickson < creslin @ digium . com >
*
* Support for Hx8 by Andrew Kohlsmith < akohlsmith @ mixdown . ca > and Matthew
* Fredrickson < creslin @ digium . com >
*
2011-06-29 06:28:53 +08:00
* Copyright ( C ) 2005 - 2011 Digium , Inc .
2010-08-28 05:59:27 +08:00
* All rights reserved .
*
* Sections for QRV cards written by Jim Dixon < jim @ lambdatel . com >
* Copyright ( C ) 2006 , Jim Dixon and QRV Communications
* All rights reserved .
*
*/
/*
* See http : //www.asterisk.org for more information about
* the Asterisk project . Please do not directly contact
* any of the maintainers of this project for assistance ;
* the project provides a web site , mailing lists and IRC
* channels for your use .
*
* This program is free software , distributed under the terms of
* the GNU General Public License Version 2 as published by the
* Free Software Foundation . See the LICENSE file included with
* this program for more details .
*/
/* For QRV DRI cards, gain is signed short, expressed in hundredths of
db ( in reference to 1 v Peak @ 1000 Hz ) , as follows :
Rx Gain : - 11.99 to 15.52 db
Tx Gain - No Pre - Emphasis : - 35.99 to 12.00 db
Tx Gain - W / Pre - Emphasis : - 23.99 to 0.00 db
*/
# include <linux/version.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/sched.h>
# include <linux/interrupt.h>
# include <linux/workqueue.h>
# include <linux/delay.h>
# include <linux/moduleparam.h>
# include <linux/firmware.h>
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
# include <linux/semaphore.h>
# else
# include <asm/semaphore.h>
# endif
# include <linux/crc32.h>
# include <stdbool.h>
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
/* Define this if you would like to load the modules in parallel. While this
* can speed up loads when multiple cards handled by this driver are installed ,
* it also makes it impossible to abort module loads with ctrl - c */
# undef USE_ASYNC_INIT
# include <linux/async.h>
# else
# undef USE_ASYNC_INIT
# endif
# include <dahdi/kernel.h>
# include <dahdi/wctdm_user.h>
# include "proslic.h"
# include "wctdm24xxp.h"
# include "xhfc.h"
# include "adt_lec.h"
# include "voicebus/GpakCust.h"
# include "voicebus/GpakApi.h"
# if VOICEBUS_SFRAME_SIZE != SFRAME_SIZE
# error SFRAME_SIZE must match the VOICEBUS_SFRAME_SIZE
# endif
/*
Experimental max loop current limit for the proslic
Loop current limit is from 20 mA to 41 mA in steps of 3
( according to datasheet )
So set the value below to :
0x00 : 20 mA ( default )
0x01 : 23 mA
0x02 : 26 mA
0x03 : 29 mA
0x04 : 32 mA
0x05 : 35 mA
0x06 : 37 mA
0x07 : 41 mA
*/
static int loopcurrent = 20 ;
/* Following define is a logical exclusive OR to determine if the polarity of an fxs line is to be reversed.
* The items taken into account are :
* overall polarity reversal for the module ,
* polarity reversal for the port ,
* and the state of the line reversal MWI indicator
*/
2011-06-03 04:03:10 +08:00
# define POLARITY_XOR(fxs) \
( ( reversepolarity ! = 0 ) ^ ( ( fxs ) - > reversepolarity ! = 0 ) ^ \
( ( fxs ) - > vmwi_linereverse ! = 0 ) )
2011-06-03 04:02:51 +08:00
2010-08-28 05:59:27 +08:00
static int reversepolarity = 0 ;
static alpha indirect_regs [ ] =
{
{ 0 , 255 , " DTMF_ROW_0_PEAK " , 0x55C2 } ,
{ 1 , 255 , " DTMF_ROW_1_PEAK " , 0x51E6 } ,
{ 2 , 255 , " DTMF_ROW2_PEAK " , 0x4B85 } ,
{ 3 , 255 , " DTMF_ROW3_PEAK " , 0x4937 } ,
{ 4 , 255 , " DTMF_COL1_PEAK " , 0x3333 } ,
{ 5 , 255 , " DTMF_FWD_TWIST " , 0x0202 } ,
{ 6 , 255 , " DTMF_RVS_TWIST " , 0x0202 } ,
{ 7 , 255 , " DTMF_ROW_RATIO_TRES " , 0x0198 } ,
{ 8 , 255 , " DTMF_COL_RATIO_TRES " , 0x0198 } ,
{ 9 , 255 , " DTMF_ROW_2ND_ARM " , 0x0611 } ,
{ 10 , 255 , " DTMF_COL_2ND_ARM " , 0x0202 } ,
{ 11 , 255 , " DTMF_PWR_MIN_TRES " , 0x00E5 } ,
{ 12 , 255 , " DTMF_OT_LIM_TRES " , 0x0A1C } ,
{ 13 , 0 , " OSC1_COEF " , 0x7B30 } ,
{ 14 , 1 , " OSC1X " , 0x0063 } ,
{ 15 , 2 , " OSC1Y " , 0x0000 } ,
{ 16 , 3 , " OSC2_COEF " , 0x7870 } ,
{ 17 , 4 , " OSC2X " , 0x007D } ,
{ 18 , 5 , " OSC2Y " , 0x0000 } ,
{ 19 , 6 , " RING_V_OFF " , 0x0000 } ,
{ 20 , 7 , " RING_OSC " , 0x7EF0 } ,
{ 21 , 8 , " RING_X " , 0x0160 } ,
{ 22 , 9 , " RING_Y " , 0x0000 } ,
{ 23 , 255 , " PULSE_ENVEL " , 0x2000 } ,
{ 24 , 255 , " PULSE_X " , 0x2000 } ,
{ 25 , 255 , " PULSE_Y " , 0x0000 } ,
//{26,13,"RECV_DIGITAL_GAIN",0x4000}, // playback volume set lower
{ 26 , 13 , " RECV_DIGITAL_GAIN " , 0x2000 } , // playback volume set lower
{ 27 , 14 , " XMIT_DIGITAL_GAIN " , 0x4000 } ,
//{27,14,"XMIT_DIGITAL_GAIN",0x2000},
{ 28 , 15 , " LOOP_CLOSE_TRES " , 0x1000 } ,
{ 29 , 16 , " RING_TRIP_TRES " , 0x3600 } ,
{ 30 , 17 , " COMMON_MIN_TRES " , 0x1000 } ,
{ 31 , 18 , " COMMON_MAX_TRES " , 0x0200 } ,
{ 32 , 19 , " PWR_ALARM_Q1Q2 " , 0x07C0 } ,
{ 33 , 20 , " PWR_ALARM_Q3Q4 " , 0x4C00 /* 0x2600 */ } ,
{ 34 , 21 , " PWR_ALARM_Q5Q6 " , 0x1B80 } ,
{ 35 , 22 , " LOOP_CLOSURE_FILTER " , 0x8000 } ,
{ 36 , 23 , " RING_TRIP_FILTER " , 0x0320 } ,
{ 37 , 24 , " TERM_LP_POLE_Q1Q2 " , 0x008C } ,
{ 38 , 25 , " TERM_LP_POLE_Q3Q4 " , 0x0100 } ,
{ 39 , 26 , " TERM_LP_POLE_Q5Q6 " , 0x0010 } ,
{ 40 , 27 , " CM_BIAS_RINGING " , 0x0C00 } ,
{ 41 , 64 , " DCDC_MIN_V " , 0x0C00 } ,
{ 42 , 255 , " DCDC_XTRA " , 0x1000 } ,
{ 43 , 66 , " LOOP_CLOSE_TRES_LOW " , 0x1000 } ,
} ;
2010-12-10 04:19:26 +08:00
/* names of HWEC modules */
static const char * vpmadt032_name = " VPMADT032 " ;
2011-06-29 06:28:53 +08:00
static const char * vpmoct_name = " VPMOCT032 " ;
2010-12-10 04:19:26 +08:00
2010-08-28 05:59:27 +08:00
/* Undefine to enable Power alarm / Transistor debug -- note: do not
enable for normal operation ! */
/* #define PAQ_DEBUG */
# define DEBUG_CARD (1 << 0)
# define DEBUG_ECHOCAN (1 << 1)
# include "fxo_modes.h"
struct wctdm_desc {
const char * name ;
const int flags ;
const int ports ;
} ;
static const struct wctdm_desc wctdm2400 = { " Wildcard TDM2400P " , 0 , 24 } ;
static const struct wctdm_desc wctdm800 = { " Wildcard TDM800P " , 0 , 8 } ;
static const struct wctdm_desc wctdm410 = { " Wildcard TDM410P " , 0 , 4 } ;
static const struct wctdm_desc wcaex2400 = { " Wildcard AEX2400 " , FLAG_EXPRESS , 24 } ;
static const struct wctdm_desc wcaex800 = { " Wildcard AEX800 " , FLAG_EXPRESS , 8 } ;
static const struct wctdm_desc wcaex410 = { " Wildcard AEX410 " , FLAG_EXPRESS , 4 } ;
static const struct wctdm_desc wcha80000 = { " HA8-0000 " , 0 , 8 } ;
static const struct wctdm_desc wchb80000 = { " HB8-0000 " , FLAG_EXPRESS , 8 } ;
/**
* Returns true if the card is one of the Hybrid Digital Analog Cards .
*/
static inline bool is_hx8 ( const struct wctdm * wc )
{
return ( & wcha80000 = = wc - > desc ) | | ( & wchb80000 = = wc - > desc ) ;
}
2011-06-03 04:03:15 +08:00
static inline struct dahdi_chan *
get_dahdi_chan ( const struct wctdm * wc , struct wctdm_module * const mod )
{
return wc - > aspan - > span . chans [ mod - > card ] ;
}
static inline void
mod_hooksig ( struct wctdm * wc , struct wctdm_module * mod , enum dahdi_rxsig rxsig )
{
dahdi_hooksig ( get_dahdi_chan ( wc , mod ) , rxsig ) ;
}
2010-08-28 05:59:27 +08:00
struct wctdm * ifaces [ WC_MAX_IFACES ] ;
2010-11-05 00:40:29 +08:00
DEFINE_SEMAPHORE ( ifacelock ) ;
2010-08-28 05:59:27 +08:00
static void wctdm_release ( struct wctdm * wc ) ;
static int fxovoltage = 0 ;
static unsigned int battdebounce ;
static unsigned int battalarm ;
static unsigned int battthresh ;
static int debug = 0 ;
# ifdef DEBUG
static int robust = 0 ;
2010-09-21 23:36:34 +08:00
static int digitalloopback ;
2010-08-28 05:59:27 +08:00
# endif
static int lowpower = 0 ;
static int boostringer = 0 ;
static int fastringer = 0 ;
static int _opermode = 0 ;
static char * opermode = " FCC " ;
static int fxshonormode = 0 ;
static int alawoverride = 0 ;
static char * companding = " auto " ;
2011-06-30 03:53:52 +08:00
static int fastpickup = - 1 ; /* -1 auto, 0 no, 1 yes */
2010-08-28 05:59:27 +08:00
static int fxotxgain = 0 ;
static int fxorxgain = 0 ;
static int fxstxgain = 0 ;
static int fxsrxgain = 0 ;
static int nativebridge = 0 ;
static int ringdebounce = DEFAULT_RING_DEBOUNCE ;
static int latency = VOICEBUS_DEFAULT_LATENCY ;
static unsigned int max_latency = VOICEBUS_DEFAULT_MAXLATENCY ;
static int forceload ;
# define MS_PER_HOOKCHECK (1)
# define NEONMWI_ON_DEBOUNCE (100 / MS_PER_HOOKCHECK)
static int neonmwi_monitor = 0 ; /* Note: this causes use of full wave ring detect */
static int neonmwi_level = 75 ; /* neon mwi trip voltage */
static int neonmwi_envelope = 10 ;
static int neonmwi_offlimit = 16000 ; /* Time in milliseconds the monitor is checked before saying no message is waiting */
static int neonmwi_offlimit_cycles ; /* Time in milliseconds the monitor is checked before saying no message is waiting */
static int vpmsupport = 1 ;
static int vpmnlptype = DEFAULT_NLPTYPE ;
static int vpmnlpthresh = DEFAULT_NLPTHRESH ;
static int vpmnlpmaxsupp = DEFAULT_NLPMAXSUPP ;
static void echocan_free ( struct dahdi_chan * chan , struct dahdi_echocan_state * ec ) ;
2010-12-10 04:19:26 +08:00
static const struct dahdi_echocan_features vpm_ec_features = {
2010-08-28 05:59:27 +08:00
. NLP_automatic = 1 ,
. CED_tx_detect = 1 ,
. CED_rx_detect = 1 ,
} ;
2010-12-10 04:19:26 +08:00
static const struct dahdi_echocan_ops vpm_ec_ops = {
2010-08-28 05:59:27 +08:00
. echocan_free = echocan_free ,
} ;
2011-06-03 04:03:15 +08:00
static int
wctdm_init_proslic ( struct wctdm * wc , struct wctdm_module * const mod , int fast ,
int manual , int sane ) ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:57 +08:00
static void set_offsets ( struct wctdm_module * const mod , int altcs )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:57 +08:00
int card = mod - > card ;
int bit ;
mod - > subaddr = ( altcs ) ? 0 : ( mod - > card & 0x3 ) ;
for ( bit = 0 ; bit < ARRAY_SIZE ( mod - > offsets ) ; + + bit ) {
/* Let's add some trickery to make the TDM410 work */
if ( altcs = = 3 ) {
if ( card = = 2 ) {
card = 4 ;
altcs = 0 ;
} else if ( card = = 3 ) {
card = 5 ;
altcs = 2 ;
}
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:57 +08:00
mod - > offsets [ bit ] = ( ( ( ( card & 0x3 ) * 3 + bit ) * 7 ) +
( card > > 2 ) + altcs +
( ( altcs ) ? - 21 : 0 ) ) ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:57 +08:00
}
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:57 +08:00
static inline __attribute_const__ int
CMD_BYTE ( const struct wctdm_module * const mod , const int bit )
{
return mod - > offsets [ bit ] ;
}
static inline __attribute_const__ int VPM_CMD_BYTE ( int timeslot , int bit )
{
return ( ( ( ( timeslot ) & 0x3 ) * 3 + ( bit ) ) * 7 ) + ( ( timeslot ) > > 2 ) ;
2010-08-28 05:59:27 +08:00
}
2011-08-31 00:37:52 +08:00
typedef int ( * bg_work_func_t ) ( struct wctdm * wc , unsigned long data ) ;
struct bg {
struct workqueue_struct * wq ;
struct work_struct work ;
struct completion complete ;
struct wctdm * wc ;
bg_work_func_t fn ;
unsigned long param ;
int ret ;
} ;
# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
static void bg_work_func ( void * data )
{
struct bg * bg = data ;
# else
static void bg_work_func ( struct work_struct * work )
{
struct bg * bg = container_of ( work , struct bg , work ) ;
# endif
bg - > ret = bg - > fn ( bg - > wc , bg - > param ) ;
complete ( & bg - > complete ) ;
}
/**
* bg_create - Call a function running in a background thread .
* @ wc : The board structure passed to fn
* @ fn : The function to run in it ' s own thread .
* @ parma : An extra parameter to pass to the fn .
*
* Returns NULL if the thread could not be created , otherwise a pointer to be
* passed to bg_join in order to get the return value .
*
* The function ' fn ' will be run in a new thread . The return value is the
* return from the bg_join function .
*
* This would probably be best served by concurrency managed workqueues before
* merging , but this will at least work on the older kernels tht DAHDI
* supports .
*/
static struct bg *
bg_create ( struct wctdm * wc , bg_work_func_t fn , unsigned long param )
{
struct bg * bg ;
bg = kzalloc ( sizeof ( * bg ) , GFP_KERNEL ) ;
if ( ! bg )
return NULL ;
bg - > wq = create_singlethread_workqueue ( " wctdm_bg " ) ;
if ( ! bg - > wq ) {
kfree ( bg ) ;
return NULL ;
}
init_completion ( & bg - > complete ) ;
# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
INIT_WORK ( & bg - > work , bg_work_func , bg ) ;
# else
INIT_WORK ( & bg - > work , bg_work_func ) ;
# endif
bg - > wc = wc ;
bg - > fn = fn ;
bg - > param = param ;
queue_work ( bg - > wq , & bg - > work ) ;
return bg ;
}
/**
* bg_join - Wait for a background function to complete and get the result .
* @ bg : Pointer returned from the bg_create call .
*
* Returns the result of the function passed to bg_create .
*/
static int bg_join ( struct bg * bg )
{
int ret = - ERESTARTSYS ;
if ( unlikely ( ! bg ) )
return - EINVAL ;
while ( ret )
ret = wait_for_completion_interruptible ( & bg - > complete ) ;
ret = bg - > ret ;
destroy_workqueue ( bg - > wq ) ;
kfree ( bg ) ;
return ret ;
}
2010-08-28 05:59:27 +08:00
static void
setchanconfig_from_state ( struct vpmadt032 * vpm , int channel ,
GpakChannelConfig_t * chanconfig )
{
GpakEcanParms_t * p ;
BUG_ON ( ! vpm ) ;
chanconfig - > PcmInPortA = 3 ;
chanconfig - > PcmInSlotA = channel ;
chanconfig - > PcmOutPortA = SerialPortNull ;
chanconfig - > PcmOutSlotA = channel ;
chanconfig - > PcmInPortB = 2 ;
chanconfig - > PcmInSlotB = channel ;
chanconfig - > PcmOutPortB = 3 ;
chanconfig - > PcmOutSlotB = channel ;
chanconfig - > ToneTypesA = Null_tone ;
chanconfig - > MuteToneA = Disabled ;
chanconfig - > FaxCngDetA = Disabled ;
chanconfig - > ToneTypesB = Null_tone ;
chanconfig - > EcanEnableA = Enabled ;
chanconfig - > EcanEnableB = Disabled ;
chanconfig - > MuteToneB = Disabled ;
chanconfig - > FaxCngDetB = Disabled ;
/* The software companding will be overridden on a channel by channel
* basis when the channel is enabled . */
chanconfig - > SoftwareCompand = cmpPCMU ;
chanconfig - > FrameRate = rate2ms ;
p = & chanconfig - > EcanParametersA ;
vpmadt032_get_default_parameters ( p ) ;
p - > EcanNlpType = vpm - > curecstate [ channel ] . nlp_type ;
p - > EcanNlpThreshold = vpm - > curecstate [ channel ] . nlp_threshold ;
p - > EcanNlpMaxSuppress = vpm - > curecstate [ channel ] . nlp_max_suppress ;
memcpy ( & chanconfig - > EcanParametersB ,
& chanconfig - > EcanParametersA ,
sizeof ( chanconfig - > EcanParametersB ) ) ;
}
2011-08-31 00:37:57 +08:00
struct vpmadt032_channel_setup {
struct work_struct work ;
struct wctdm * wc ;
} ;
# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
static void vpm_setup_work_func ( void * data )
2010-08-28 05:59:27 +08:00
{
2011-08-31 00:37:57 +08:00
struct vpmadt032_channel_setup * setup = data ;
# else
static void vpm_setup_work_func ( struct work_struct * work )
{
struct vpmadt032_channel_setup * setup =
container_of ( work , struct vpmadt032_channel_setup , work ) ;
# endif
int i ;
int res ;
2010-08-28 05:59:27 +08:00
GpakChannelConfig_t chanconfig ;
GPAK_ChannelConfigStat_t cstatus ;
GPAK_AlgControlStat_t algstatus ;
2011-08-31 00:37:57 +08:00
GpakPortConfig_t portconfig = { 0 } ;
gpakConfigPortStatus_t configportstatus ;
GPAK_PortConfigStat_t pstatus ;
struct vpmadt032 * vpm ;
struct wctdm * const wc = setup - > wc ;
WARN_ON ( ! wc ) ;
WARN_ON ( ! wc - > vpmadt032 ) ;
if ( unlikely ( ! wc | | ! wc - > vpmadt032 ) )
return ;
vpm = wc - > vpmadt032 ;
2010-08-28 05:59:27 +08:00
/* First Serial Port config */
portconfig . SlotsSelect1 = SlotCfgNone ;
portconfig . FirstBlockNum1 = 0 ;
portconfig . FirstSlotMask1 = 0x0000 ;
portconfig . SecBlockNum1 = 1 ;
portconfig . SecSlotMask1 = 0x0000 ;
portconfig . SerialWordSize1 = SerWordSize8 ;
portconfig . CompandingMode1 = cmpNone ;
portconfig . TxFrameSyncPolarity1 = FrameSyncActHigh ;
portconfig . RxFrameSyncPolarity1 = FrameSyncActHigh ;
portconfig . TxClockPolarity1 = SerClockActHigh ;
portconfig . RxClockPolarity1 = SerClockActHigh ;
portconfig . TxDataDelay1 = DataDelay0 ;
portconfig . RxDataDelay1 = DataDelay0 ;
portconfig . DxDelay1 = Disabled ;
portconfig . ThirdSlotMask1 = 0x0000 ;
portconfig . FouthSlotMask1 = 0x0000 ;
portconfig . FifthSlotMask1 = 0x0000 ;
portconfig . SixthSlotMask1 = 0x0000 ;
portconfig . SevenSlotMask1 = 0x0000 ;
portconfig . EightSlotMask1 = 0x0000 ;
/* Second Serial Port config */
portconfig . SlotsSelect2 = SlotCfg2Groups ;
portconfig . FirstBlockNum2 = 0 ;
portconfig . FirstSlotMask2 = 0xffff ;
portconfig . SecBlockNum2 = 1 ;
portconfig . SecSlotMask2 = 0xffff ;
portconfig . SerialWordSize2 = SerWordSize8 ;
portconfig . CompandingMode2 = cmpNone ;
portconfig . TxFrameSyncPolarity2 = FrameSyncActHigh ;
portconfig . RxFrameSyncPolarity2 = FrameSyncActHigh ;
portconfig . TxClockPolarity2 = SerClockActHigh ;
portconfig . RxClockPolarity2 = SerClockActLow ;
portconfig . TxDataDelay2 = DataDelay0 ;
portconfig . RxDataDelay2 = DataDelay0 ;
portconfig . DxDelay2 = Disabled ;
portconfig . ThirdSlotMask2 = 0x0000 ;
portconfig . FouthSlotMask2 = 0x0000 ;
portconfig . FifthSlotMask2 = 0x0000 ;
portconfig . SixthSlotMask2 = 0x0000 ;
portconfig . SevenSlotMask2 = 0x0000 ;
portconfig . EightSlotMask2 = 0x0000 ;
/* Third Serial Port Config */
portconfig . SlotsSelect3 = SlotCfg2Groups ;
portconfig . FirstBlockNum3 = 0 ;
portconfig . FirstSlotMask3 = 0xffff ;
portconfig . SecBlockNum3 = 1 ;
portconfig . SecSlotMask3 = 0xffff ;
portconfig . SerialWordSize3 = SerWordSize8 ;
portconfig . CompandingMode3 = cmpNone ;
portconfig . TxFrameSyncPolarity3 = FrameSyncActHigh ;
portconfig . RxFrameSyncPolarity3 = FrameSyncActHigh ;
portconfig . TxClockPolarity3 = SerClockActHigh ;
portconfig . RxClockPolarity3 = SerClockActLow ;
portconfig . TxDataDelay3 = DataDelay0 ;
portconfig . RxDataDelay3 = DataDelay0 ;
portconfig . DxDelay3 = Disabled ;
portconfig . ThirdSlotMask3 = 0x0000 ;
portconfig . FouthSlotMask3 = 0x0000 ;
portconfig . FifthSlotMask3 = 0x0000 ;
portconfig . SixthSlotMask3 = 0x0000 ;
portconfig . SevenSlotMask3 = 0x0000 ;
portconfig . EightSlotMask3 = 0x0000 ;
if ( ( configportstatus = gpakConfigurePorts ( vpm - > dspid , & portconfig , & pstatus ) ) ) {
dev_notice ( & wc - > vb . pdev - > dev , " Configuration of ports failed (%d)! \n " , configportstatus ) ;
2011-08-31 00:37:57 +08:00
return ;
2010-08-28 05:59:27 +08:00
} else {
if ( vpm - > options . debug & DEBUG_ECHOCAN )
dev_info ( & wc - > vb . pdev - > dev , " Configured McBSP ports successfully \n " ) ;
}
2011-06-29 06:28:53 +08:00
res = gpakPingDsp ( vpm - > dspid , & vpm - > version ) ;
if ( res ) {
2010-08-28 05:59:27 +08:00
dev_notice ( & wc - > vb . pdev - > dev , " Error pinging DSP (%d) \n " , res ) ;
2011-08-31 00:37:57 +08:00
return ;
2010-08-28 05:59:27 +08:00
}
for ( i = 0 ; i < vpm - > options . channels ; + + i ) {
vpm - > curecstate [ i ] . tap_length = 0 ;
vpm - > curecstate [ i ] . nlp_type = vpm - > options . vpmnlptype ;
vpm - > curecstate [ i ] . nlp_threshold = vpm - > options . vpmnlpthresh ;
2011-08-31 00:37:57 +08:00
vpm - > curecstate [ i ] . nlp_max_suppress =
vpm - > options . vpmnlpmaxsupp ;
vpm - > curecstate [ i ] . companding = ADT_COMP_ULAW ;
2010-08-28 05:59:27 +08:00
vpm - > setchanconfig_from_state ( vpm , i , & chanconfig ) ;
2011-08-31 00:37:57 +08:00
res = gpakConfigureChannel ( vpm - > dspid , i , tdmToTdm ,
& chanconfig , & cstatus ) ;
if ( res ) {
dev_notice ( & wc - > vb . pdev - > dev ,
" Unable to configure channel #%d (%d) " ,
i , res ) ;
if ( res = = 1 )
2010-08-28 05:59:27 +08:00
printk ( KERN_CONT " , reason %d " , cstatus ) ;
printk ( KERN_CONT " \n " ) ;
2011-08-31 00:37:57 +08:00
goto exit ;
2010-08-28 05:59:27 +08:00
}
2011-08-31 00:37:57 +08:00
res = gpakAlgControl ( vpm - > dspid , i , BypassEcanA , & algstatus ) ;
if ( res ) {
dev_notice ( & wc - > vb . pdev - > dev ,
" Unable to disable echo can on channel %d "
" (reason %d:%d) \n " , i + 1 , res , algstatus ) ;
goto exit ;
2010-08-28 05:59:27 +08:00
}
2011-08-31 00:37:57 +08:00
res = gpakAlgControl ( vpm - > dspid , i ,
BypassSwCompanding , & algstatus ) ;
if ( res ) {
dev_notice ( & wc - > vb . pdev - > dev ,
" Unable to disable echo can on channel %d "
" (reason %d:%d) \n " , i + 1 , res , algstatus ) ;
goto exit ;
2010-08-28 05:59:27 +08:00
}
}
2011-08-31 00:37:57 +08:00
res = gpakPingDsp ( vpm - > dspid , & vpm - > version ) ;
if ( res ) {
2010-08-28 05:59:27 +08:00
dev_notice ( & wc - > vb . pdev - > dev , " Error pinging DSP (%d) \n " , res ) ;
2011-08-31 00:37:57 +08:00
goto exit ;
2010-08-28 05:59:27 +08:00
}
set_bit ( VPM150M_ACTIVE , & vpm - > control ) ;
2011-08-31 00:37:57 +08:00
exit :
kfree ( setup ) ;
}
static int config_vpmadt032 ( struct vpmadt032 * vpm , struct wctdm * wc )
{
struct vpmadt032_channel_setup * setup ;
/* Because the channel configuration can take such a long time, let's
* move this out onto the VPM workqueue so the system can proceeded
* with startup . */
setup = kzalloc ( sizeof ( * setup ) , GFP_KERNEL ) ;
if ( ! setup )
return - ENOMEM ;
setup - > wc = wc ;
# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
INIT_WORK ( & setup - > work , vpm_setup_work_func , setup ) ;
# else
INIT_WORK ( & setup - > work , vpm_setup_work_func ) ;
# endif
queue_work ( vpm - > wq , & setup - > work ) ;
2010-08-28 05:59:27 +08:00
return 0 ;
}
/**
* is_good_frame ( ) - Whether the SFRAME received was one sent .
*
*/
static inline bool is_good_frame ( const u8 * sframe )
{
const u8 a = sframe [ 0 * ( EFRAME_SIZE + EFRAME_GAP ) + ( EFRAME_SIZE + 1 ) ] ;
const u8 b = sframe [ 1 * ( EFRAME_SIZE + EFRAME_GAP ) + ( EFRAME_SIZE + 1 ) ] ;
return a ! = b ;
}
2011-06-29 06:28:53 +08:00
static inline void cmd_dequeue_vpmoct ( struct wctdm * wc , u8 * eframe )
{
struct vpmoct * vpm = wc - > vpmoct ;
struct vpmoct_cmd * cmd ;
u8 i ;
/* Pop a command off pending list */
spin_lock ( & vpm - > list_lock ) ;
if ( list_empty ( & vpm - > pending_list ) ) {
spin_unlock ( & vpm - > list_lock ) ;
return ;
}
cmd = list_entry ( vpm - > pending_list . next , struct vpmoct_cmd , node ) ;
if ( is_vpmoct_cmd_read ( cmd ) )
list_move_tail ( & cmd - > node , & vpm - > active_list ) ;
else
list_del_init ( & cmd - > node ) ;
/* Skip audio (24 bytes) and ignore first 6 timeslots */
eframe + = 30 ;
/* Save ident so we can match the return eframe */
cmd - > txident = wc - > txident ;
/* We have four timeslots to work with for a regular spi packet */
/* TODO: Create debug flag for this in dev */
/* The vpmoct requires a "sync" spi command as the first three bytes
* of an eframe */
eframe [ 7 * 0 ] = 0x12 ;
eframe [ 7 * 1 ] = 0x34 ;
eframe [ 7 * 2 ] = 0x56 ;
eframe [ 7 * 3 ] = cmd - > command ;
eframe [ 7 * 4 ] = cmd - > address ;
eframe [ 7 * 5 ] = cmd - > data [ 0 ] ;
for ( i = 1 ; i < cmd - > chunksize ; i + + )
eframe [ ( 7 * 5 ) + 7 * i ] = cmd - > data [ i ] ;
/* Clean up fire-and-forget messages from memory */
if ( list_empty ( & cmd - > node ) )
kfree ( cmd ) ;
spin_unlock ( & vpm - > list_lock ) ;
}
2011-02-19 04:32:22 +08:00
static inline void cmd_dequeue_vpmadt032 ( struct wctdm * wc , u8 * eframe )
2010-08-28 05:59:27 +08:00
{
struct vpmadt032_cmd * curcmd = NULL ;
struct vpmadt032 * vpmadt032 = wc - > vpmadt032 ;
int x ;
2011-08-31 00:38:14 +08:00
unsigned char leds = ~ ( ( wc - > framecount / 1000 ) % 8 ) & 0x7 ;
2010-08-28 05:59:27 +08:00
/* Skip audio */
2011-02-19 04:32:22 +08:00
eframe + = 24 ;
2010-08-28 05:59:27 +08:00
2011-02-28 22:19:25 +08:00
if ( test_bit ( VPM150M_HPIRESET , & vpmadt032 - > control ) ) {
2010-08-28 05:59:27 +08:00
if ( debug & DEBUG_ECHOCAN )
dev_info ( & wc - > vb . pdev - > dev , " HW Resetting VPMADT032... \n " ) ;
for ( x = 24 ; x < 28 ; x + + ) {
if ( x = = 24 ) {
2011-02-28 22:19:25 +08:00
if ( test_and_clear_bit ( VPM150M_HPIRESET ,
& vpmadt032 - > control ) ) {
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( x , 0 ) ] = 0x0b ;
2011-02-28 22:19:25 +08:00
} else {
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( x , 0 ) ] = leds ;
2011-02-28 22:19:25 +08:00
}
} else {
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( x , 0 ) ] = leds ;
2011-02-28 22:19:25 +08:00
}
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( x , 1 ) ] = 0 ;
eframe [ VPM_CMD_BYTE ( x , 2 ) ] = 0x00 ;
2010-08-28 05:59:27 +08:00
}
return ;
}
if ( ( curcmd = vpmadt032_get_ready_cmd ( vpmadt032 ) ) ) {
curcmd - > txident = wc - > txident ;
#if 0
// if (printk_ratelimit())
dev_info ( & wc - > vb . pdev - > dev , " Transmitting txident = %d, desc = 0x%x, addr = 0x%x, data = 0x%x \n " , curcmd - > txident , curcmd - > desc , curcmd - > address , curcmd - > data ) ;
# endif
if ( curcmd - > desc & __VPM150M_RWPAGE ) {
/* Set CTRL access to page*/
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( 24 , 0 ) ] = ( 0x8 < < 4 ) ;
eframe [ VPM_CMD_BYTE ( 24 , 1 ) ] = 0 ;
eframe [ VPM_CMD_BYTE ( 24 , 2 ) ] = 0x20 ;
2010-08-28 05:59:27 +08:00
/* Do a page write */
if ( curcmd - > desc & __VPM150M_WR )
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( 25 , 0 ) ] = ( ( 0x8 | 0x4 ) < < 4 ) ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( 25 , 0 ) ] = ( ( 0x8 | 0x4 | 0x1 ) < < 4 ) ;
eframe [ VPM_CMD_BYTE ( 25 , 1 ) ] = 0 ;
2010-08-28 05:59:27 +08:00
if ( curcmd - > desc & __VPM150M_WR )
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( 25 , 2 ) ] = curcmd - > data & 0xf ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( 25 , 2 ) ] = 0 ;
2010-08-28 05:59:27 +08:00
/* Clear XADD */
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( 26 , 0 ) ] = ( 0x8 < < 4 ) ;
eframe [ VPM_CMD_BYTE ( 26 , 1 ) ] = 0 ;
eframe [ VPM_CMD_BYTE ( 26 , 2 ) ] = 0 ;
2010-08-28 05:59:27 +08:00
/* Fill in to buffer to size */
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( 27 , 0 ) ] = 0 ;
eframe [ VPM_CMD_BYTE ( 27 , 1 ) ] = 0 ;
eframe [ VPM_CMD_BYTE ( 27 , 2 ) ] = 0 ;
2010-08-28 05:59:27 +08:00
} else {
/* Set address */
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( 24 , 0 ) ] = ( ( 0x8 | 0x4 ) < < 4 ) ;
eframe [ VPM_CMD_BYTE ( 24 , 1 ) ] = ( curcmd - > address > > 8 ) & 0xff ;
eframe [ VPM_CMD_BYTE ( 24 , 2 ) ] = curcmd - > address & 0xff ;
2010-08-28 05:59:27 +08:00
/* Send/Get our data */
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( 25 , 0 ) ] = ( curcmd - > desc & __VPM150M_WR ) ?
2010-08-28 05:59:27 +08:00
( ( 0x8 | ( 0x3 < < 1 ) ) < < 4 ) : ( ( 0x8 | ( 0x3 < < 1 ) | 0x1 ) < < 4 ) ;
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( 25 , 1 ) ] = ( curcmd - > data > > 8 ) & 0xff ;
eframe [ VPM_CMD_BYTE ( 25 , 2 ) ] = curcmd - > data & 0xff ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( 26 , 0 ) ] = 0 ;
eframe [ VPM_CMD_BYTE ( 26 , 1 ) ] = 0 ;
eframe [ VPM_CMD_BYTE ( 26 , 2 ) ] = 0 ;
2010-08-28 05:59:27 +08:00
/* Fill in the rest */
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( 27 , 0 ) ] = 0 ;
eframe [ VPM_CMD_BYTE ( 27 , 1 ) ] = 0 ;
eframe [ VPM_CMD_BYTE ( 27 , 2 ) ] = 0 ;
2010-08-28 05:59:27 +08:00
}
} else if ( test_and_clear_bit ( VPM150M_SWRESET , & vpmadt032 - > control ) ) {
for ( x = 24 ; x < 28 ; x + + ) {
if ( x = = 24 )
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( x , 0 ) ] = ( 0x8 < < 4 ) ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( x , 0 ) ] = 0x00 ;
eframe [ VPM_CMD_BYTE ( x , 1 ) ] = 0 ;
2010-08-28 05:59:27 +08:00
if ( x = = 24 )
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( x , 2 ) ] = 0x01 ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( x , 2 ) ] = 0x00 ;
2010-08-28 05:59:27 +08:00
}
} else {
for ( x = 24 ; x < 28 ; x + + ) {
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( x , 0 ) ] = 0x00 ;
eframe [ VPM_CMD_BYTE ( x , 1 ) ] = 0x00 ;
eframe [ VPM_CMD_BYTE ( x , 2 ) ] = 0x00 ;
2010-08-28 05:59:27 +08:00
}
}
/* Add our leds in */
for ( x = 24 ; x < 28 ; x + + ) {
2011-06-03 04:03:57 +08:00
eframe [ VPM_CMD_BYTE ( x , 0 ) ] | = leds ;
2010-08-28 05:59:27 +08:00
}
}
2011-06-03 04:03:38 +08:00
/* Call with wc->reglock held and local interrupts disabled */
2011-06-03 04:03:20 +08:00
static void _cmd_dequeue ( struct wctdm * wc , u8 * eframe , int card , int pos )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:02:51 +08:00
struct wctdm_module * const mod = & wc - > mods [ card ] ;
2010-08-28 05:59:27 +08:00
unsigned int curcmd = 0 ;
2011-06-03 04:02:51 +08:00
/* QRV only use commands relating to the first channel */
2011-06-03 04:03:01 +08:00
if ( ( card & 0x03 ) & & ( mod - > type = = QRV ) )
2010-08-28 05:59:27 +08:00
return ;
/* Skip audio */
2011-02-19 04:32:22 +08:00
eframe + = 24 ;
2010-08-28 05:59:27 +08:00
/* Search for something waiting to transmit */
if ( pos ) {
2011-06-03 04:03:38 +08:00
if ( ! list_empty ( & mod - > pending_cmds ) ) {
struct wctdm_cmd * const cmd =
list_entry ( mod - > pending_cmds . next ,
struct wctdm_cmd , node ) ;
curcmd = cmd - > cmd ;
cmd - > ident = wc - > txident ;
list_move_tail ( & cmd - > node , & mod - > active_cmds ) ;
2010-08-28 05:59:27 +08:00
}
}
if ( ! curcmd ) {
/* If nothing else, use filler */
2011-06-03 04:03:20 +08:00
switch ( mod - > type ) {
case FXS :
2010-08-28 05:59:27 +08:00
curcmd = CMD_RD ( LINE_STATE ) ;
2011-06-03 04:03:20 +08:00
break ;
case FXO :
2010-08-28 05:59:27 +08:00
curcmd = CMD_RD ( 12 ) ;
2011-06-03 04:03:20 +08:00
break ;
case BRI :
2010-08-28 05:59:27 +08:00
curcmd = 0x101010 ;
2011-06-03 04:03:20 +08:00
break ;
case QRV :
2010-08-28 05:59:27 +08:00
curcmd = CMD_RD ( 3 ) ;
2011-06-03 04:03:20 +08:00
break ;
default :
break ;
}
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:20 +08:00
switch ( mod - > type ) {
case FXS :
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 0 ) ] = ( 1 < < ( mod - > subaddr ) ) ;
2010-08-28 05:59:27 +08:00
if ( curcmd & __CMD_WR )
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 1 ) ] = ( curcmd > > 8 ) & 0x7f ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 1 ) ] = 0x80 | ( ( curcmd > > 8 ) & 0x7f ) ;
eframe [ CMD_BYTE ( mod , 2 ) ] = curcmd & 0xff ;
2011-06-03 04:03:20 +08:00
break ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:20 +08:00
case FXO :
{
2011-06-03 04:03:57 +08:00
static const int ADDRS [ 4 ] = { 0x00 , 0x08 , 0x04 , 0x0c } ;
2010-08-28 05:59:27 +08:00
if ( curcmd & __CMD_WR )
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 0 ) ] = 0x20 | ADDRS [ mod - > subaddr ] ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 0 ) ] = 0x60 | ADDRS [ mod - > subaddr ] ;
eframe [ CMD_BYTE ( mod , 1 ) ] = ( curcmd > > 8 ) & 0xff ;
eframe [ CMD_BYTE ( mod , 2 ) ] = curcmd & 0xff ;
2011-06-03 04:03:20 +08:00
break ;
}
case FXSINIT :
2010-08-28 05:59:27 +08:00
/* Special case, we initialize the FXS's into the three-byte command mode then
switch to the regular mode . To send it into thee byte mode , treat the path as
6 two - byte commands and in the last one we initialize register 0 to 0x80 . All modules
read this as the command to switch to daisy chain mode and we ' re done . */
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 0 ) ] = 0x00 ;
eframe [ CMD_BYTE ( mod , 1 ) ] = 0x00 ;
2010-08-28 05:59:27 +08:00
if ( ( card & 0x1 ) = = 0x1 )
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 2 ) ] = 0x80 ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 2 ) ] = 0x00 ;
2011-06-03 04:03:20 +08:00
break ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:20 +08:00
case BRI :
2010-08-28 05:59:27 +08:00
if ( unlikely ( ( curcmd ! = 0x101010 ) & & ( curcmd & 0x1010 ) = = 0x1010 ) ) /* b400m CPLD */
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 0 ) ] = 0x55 ;
2010-08-28 05:59:27 +08:00
else /* xhfc */
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 0 ) ] = 0x10 ;
eframe [ CMD_BYTE ( mod , 1 ) ] = ( curcmd > > 8 ) & 0xff ;
eframe [ CMD_BYTE ( mod , 2 ) ] = curcmd & 0xff ;
2011-06-03 04:03:20 +08:00
break ;
2011-06-03 04:02:51 +08:00
2011-06-03 04:03:20 +08:00
case QRV :
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 0 ) ] = 0x00 ;
2010-08-28 05:59:27 +08:00
if ( ! curcmd ) {
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 1 ) ] = 0x00 ;
eframe [ CMD_BYTE ( mod , 2 ) ] = 0x00 ;
2010-08-28 05:59:27 +08:00
} else {
if ( curcmd & __CMD_WR )
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 1 ) ] = 0x40 | ( ( curcmd > > 8 ) & 0x3f ) ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 1 ) ] = 0xc0 | ( ( curcmd > > 8 ) & 0x3f ) ;
eframe [ CMD_BYTE ( mod , 2 ) ] = curcmd & 0xff ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:20 +08:00
break ;
case NONE :
2011-06-03 04:03:57 +08:00
eframe [ CMD_BYTE ( mod , 0 ) ] = 0x10 ;
eframe [ CMD_BYTE ( mod , 1 ) ] = 0x10 ;
eframe [ CMD_BYTE ( mod , 2 ) ] = 0x10 ;
2011-06-03 04:03:20 +08:00
break ;
2010-08-28 05:59:27 +08:00
}
}
2011-06-29 06:28:53 +08:00
static inline void cmd_decipher_vpmoct ( struct wctdm * wc , const u8 * eframe )
{
struct vpmoct * vpm = wc - > vpmoct ;
struct vpmoct_cmd * cmd ;
int i ;
/* Skip audio and first 6 timeslots */
eframe + = 30 ;
spin_lock ( & vpm - > list_lock ) ;
/* No command to handle, just exit */
if ( list_empty ( & vpm - > active_list ) ) {
spin_unlock ( & vpm - > list_lock ) ;
return ;
}
cmd = list_entry ( vpm - > active_list . next , struct vpmoct_cmd , node ) ;
if ( wc - > rxident = = cmd - > txident )
list_del_init ( & cmd - > node ) ;
else
cmd = NULL ;
spin_unlock ( & vpm - > list_lock ) ;
if ( ! cmd )
return ;
/* Store result, Ignoring the first "sync spi command" bytes */
cmd - > command = eframe [ 7 * 3 ] ;
cmd - > address = eframe [ 7 * 4 ] ;
for ( i = 0 ; i < cmd - > chunksize ; + + i )
cmd - > data [ i ] = eframe [ 7 * ( 5 + i ) ] ;
complete ( & cmd - > complete ) ;
}
2011-02-19 04:32:22 +08:00
static inline void cmd_decipher_vpmadt032 ( struct wctdm * wc , const u8 * eframe )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:20 +08:00
struct vpmadt032 * const vpm = wc - > vpmadt032 ;
2010-08-28 05:59:27 +08:00
struct vpmadt032_cmd * cmd ;
BUG_ON ( ! vpm ) ;
/* If the hardware is not processing any commands currently, then
* there is nothing for us to do here . */
if ( list_empty ( & vpm - > active_cmds ) ) {
return ;
}
2011-06-03 04:03:20 +08:00
spin_lock ( & vpm - > list_lock ) ;
2010-08-28 05:59:27 +08:00
cmd = list_entry ( vpm - > active_cmds . next , struct vpmadt032_cmd , node ) ;
if ( wc - > rxident = = cmd - > txident ) {
list_del_init ( & cmd - > node ) ;
} else {
cmd = NULL ;
}
2011-06-03 04:03:20 +08:00
spin_unlock ( & vpm - > list_lock ) ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:20 +08:00
if ( ! cmd )
2010-08-28 05:59:27 +08:00
return ;
/* Skip audio */
2011-02-19 04:32:22 +08:00
eframe + = 24 ;
2010-08-28 05:59:27 +08:00
/* Store result */
2011-06-03 04:03:57 +08:00
cmd - > data = ( 0xff & eframe [ VPM_CMD_BYTE ( 25 , 1 ) ] ) < < 8 ;
cmd - > data | = eframe [ VPM_CMD_BYTE ( 25 , 2 ) ] ;
2010-08-28 05:59:27 +08:00
if ( cmd - > desc & __VPM150M_WR ) {
kfree ( cmd ) ;
} else {
cmd - > desc | = __VPM150M_FIN ;
complete ( & cmd - > complete ) ;
}
}
2011-06-03 04:03:20 +08:00
/**
* Call with the reglock held and local interrupts disabled
*/
static void _cmd_decipher ( struct wctdm * wc , const u8 * eframe , int card )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:38 +08:00
enum { TDM_BYTES = 24 , } ;
2011-06-03 04:02:51 +08:00
struct wctdm_module * const mod = & wc - > mods [ card ] ;
2011-06-03 04:03:38 +08:00
struct wctdm_cmd * cmd ;
u8 address ;
u8 value ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:38 +08:00
if ( list_empty ( & mod - > active_cmds ) )
2010-08-28 05:59:27 +08:00
return ;
2011-06-03 04:03:38 +08:00
cmd = list_entry ( mod - > active_cmds . next , struct wctdm_cmd , node ) ;
if ( cmd - > ident ! = wc - > rxident )
return ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:38 +08:00
list_del ( & cmd - > node ) ;
if ( cmd - > cmd & __CMD_WR ) {
kfree ( cmd ) ;
return ;
}
address = ( cmd - > cmd > > 8 ) & 0xff ;
2011-06-03 04:03:57 +08:00
cmd - > cmd = eframe [ TDM_BYTES + CMD_BYTE ( mod , 2 ) ] ;
2011-06-03 04:03:38 +08:00
value = ( cmd - > cmd & 0xff ) ;
if ( cmd - > complete ) {
complete ( cmd - > complete ) ;
return ;
}
2011-08-19 03:21:44 +08:00
list_add ( & cmd - > node , & wc - > free_isr_commands ) ;
2011-06-03 04:03:38 +08:00
switch ( mod - > type ) {
case FXS :
2011-08-31 00:38:19 +08:00
if ( 68 = = address )
mod - > mod . fxs . hook_state_shadow = value ;
else
mod - > mod . fxs . linefeed_control_shadow = value ;
2011-06-03 04:03:38 +08:00
break ;
case FXO :
2011-08-31 00:38:19 +08:00
if ( 5 = = address )
mod - > mod . fxo . hook_ring_shadow = value ;
else
mod - > mod . fxo . line_voltage_status = value ;
2011-06-03 04:03:38 +08:00
break ;
case QRV :
/* wctdm_isr_getreg(wc, mod, 3); */ /* COR/CTCSS state */
/* TODO: This looks broken to me, but I have no way to
* resolved it . */
/* wc->mods[card & 0xfc].cmds[USER_COMMANDS + 1] = CMD_RD(3); */
break ;
default :
break ;
2010-08-28 05:59:27 +08:00
}
}
2011-06-03 04:03:38 +08:00
/* Call with wc.reglock held and local interrupts disabled. */
static void
wctdm_isr_getreg ( struct wctdm * wc , struct wctdm_module * const mod , u8 address )
{
struct wctdm_cmd * cmd ;
2011-08-19 03:21:44 +08:00
if ( ! list_empty ( & wc - > free_isr_commands ) ) {
cmd = list_entry ( wc - > free_isr_commands . next ,
struct wctdm_cmd , node ) ;
list_del ( & cmd - > node ) ;
} else {
cmd = kmalloc ( sizeof ( * cmd ) , GFP_ATOMIC ) ;
if ( unlikely ( ! cmd ) )
return ;
}
2011-06-03 04:03:38 +08:00
cmd - > cmd = CMD_RD ( address ) ;
cmd - > complete = NULL ;
list_add ( & cmd - > node , & mod - > pending_cmds ) ;
}
static inline void
wctdm_setreg_intr ( struct wctdm * wc , struct wctdm_module * mod ,
int addr , int val ) ;
2011-06-03 04:03:15 +08:00
static void cmd_checkisr ( struct wctdm * wc , struct wctdm_module * const mod )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:38 +08:00
if ( mod - > sethook ) {
wctdm_setreg_intr ( wc , mod , ( ( mod - > sethook > > 8 ) & 0xff ) ,
mod - > sethook & 0xff ) ;
mod - > sethook = 0 ;
return ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:38 +08:00
switch ( mod - > type ) {
case FXS :
wctdm_isr_getreg ( wc , mod , 68 ) ; /* Hook state */
2010-08-28 05:59:27 +08:00
# ifdef PAQ_DEBUG
2011-06-03 04:03:38 +08:00
wctdm_isr_getreg ( wc , mod , 19 ) ; /* Transistor interrupts */
2010-08-28 05:59:27 +08:00
# else
2011-06-03 04:03:38 +08:00
wctdm_isr_getreg ( wc , mod , LINE_STATE ) ;
2010-08-28 05:59:27 +08:00
# endif
2011-06-03 04:03:38 +08:00
break ;
case FXO :
wctdm_isr_getreg ( wc , mod , 5 ) ; /* Hook/Ring state */
wctdm_isr_getreg ( wc , mod , 29 ) ; /* Battery */
break ;
case QRV :
wctdm_isr_getreg ( wc , mod , 3 ) ; /* COR/CTCSS state */
/* TODO: This looks broken to me, but I have no way to
* resolved it . */
/* wc->mods[card & 0xfc].cmds[USER_COMMANDS + 1] = CMD_RD(3); */
break ;
case BRI :
/* TODO: Two calls needed here? */
wctdm_bri_checkisr ( wc , mod , 0 ) ;
wctdm_bri_checkisr ( wc , mod , 1 ) ;
break ;
default :
break ;
2010-08-28 05:59:27 +08:00
}
}
/**
* insert_tdm_data ( ) - Move TDM data from channels to sframe .
*
*/
static void insert_tdm_data ( const struct wctdm * wc , u8 * sframe )
{
int i ;
register u8 * chanchunk ;
for ( i = 0 ; i < wc - > avchannels ; i + = 4 ) {
chanchunk = & wc - > chans [ 0 + i ] - > chan . writechunk [ 0 ] ;
sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 0 ] = chanchunk [ 0 ] ;
sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 1 ] = chanchunk [ 1 ] ;
sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 2 ] = chanchunk [ 2 ] ;
sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 3 ] = chanchunk [ 3 ] ;
sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 4 ] = chanchunk [ 4 ] ;
sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 5 ] = chanchunk [ 5 ] ;
sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 6 ] = chanchunk [ 6 ] ;
sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 7 ] = chanchunk [ 7 ] ;
chanchunk = & wc - > chans [ 1 + i ] - > chan . writechunk [ 0 ] ;
sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 0 ] = chanchunk [ 0 ] ;
sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 1 ] = chanchunk [ 1 ] ;
sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 2 ] = chanchunk [ 2 ] ;
sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 3 ] = chanchunk [ 3 ] ;
sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 4 ] = chanchunk [ 4 ] ;
sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 5 ] = chanchunk [ 5 ] ;
sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 6 ] = chanchunk [ 6 ] ;
sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 7 ] = chanchunk [ 7 ] ;
chanchunk = & wc - > chans [ 2 + i ] - > chan . writechunk [ 0 ] ;
sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 0 ] = chanchunk [ 0 ] ;
sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 1 ] = chanchunk [ 1 ] ;
sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 2 ] = chanchunk [ 2 ] ;
sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 3 ] = chanchunk [ 3 ] ;
sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 4 ] = chanchunk [ 4 ] ;
sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 5 ] = chanchunk [ 5 ] ;
sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 6 ] = chanchunk [ 6 ] ;
sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 7 ] = chanchunk [ 7 ] ;
chanchunk = & wc - > chans [ 3 + i ] - > chan . writechunk [ 0 ] ;
sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 0 ] = chanchunk [ 0 ] ;
sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 1 ] = chanchunk [ 1 ] ;
sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 2 ] = chanchunk [ 2 ] ;
sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 3 ] = chanchunk [ 3 ] ;
sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 4 ] = chanchunk [ 4 ] ;
sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 5 ] = chanchunk [ 5 ] ;
sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 6 ] = chanchunk [ 6 ] ;
sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 7 ] = chanchunk [ 7 ] ;
}
}
2011-02-19 04:32:22 +08:00
static inline void wctdm_transmitprep ( struct wctdm * wc , unsigned char * sframe )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:20 +08:00
int x , y ;
2010-08-28 05:59:27 +08:00
struct dahdi_span * s ;
2011-02-19 04:32:22 +08:00
unsigned char * eframe = sframe ;
2010-08-28 05:59:27 +08:00
/* Calculate Transmission */
2011-06-29 06:28:53 +08:00
if ( likely ( is_initialized ( wc ) ) ) {
2010-08-28 05:59:27 +08:00
for ( x = 0 ; x < MAX_SPANS ; x + + ) {
if ( wc - > spans [ x ] ) {
s = & wc - > spans [ x ] - > span ;
2011-08-31 00:38:10 +08:00
_dahdi_transmit ( s ) ;
2010-08-28 05:59:27 +08:00
}
}
2011-02-19 04:32:22 +08:00
insert_tdm_data ( wc , sframe ) ;
2010-08-28 05:59:27 +08:00
# ifdef CONFIG_VOICEBUS_ECREFERENCE
for ( x = 0 ; x < wc - > avchannels ; + + x ) {
__dahdi_fifo_put ( wc - > ec_reference [ x ] ,
wc - > chans [ x ] - > chan . writechunk ,
DAHDI_CHUNKSIZE ) ;
}
# endif
}
2011-08-31 00:38:10 +08:00
spin_lock ( & wc - > reglock ) ;
2010-08-28 05:59:27 +08:00
for ( x = 0 ; x < DAHDI_CHUNKSIZE ; x + + ) {
/* Send a sample, as a 32-bit word */
/* TODO: ABK: hmm, this was originally mods_per_board, but we
* need to worry about all the active " voice " timeslots , since
* BRI modules have a different number of TDM channels than
* installed modules . */
for ( y = 0 ; y < wc - > avchannels ; y + + ) {
if ( y < wc - > mods_per_board )
2011-06-03 04:03:20 +08:00
_cmd_dequeue ( wc , eframe , y , x ) ;
2010-08-28 05:59:27 +08:00
}
2011-01-31 23:53:26 +08:00
2011-06-29 06:28:53 +08:00
if ( wc - > vpmadt032 )
2011-02-19 04:32:22 +08:00
cmd_dequeue_vpmadt032 ( wc , eframe ) ;
2011-06-29 06:28:53 +08:00
else if ( wc - > vpmoct )
cmd_dequeue_vpmoct ( wc , eframe ) ;
2010-08-28 05:59:27 +08:00
if ( x < DAHDI_CHUNKSIZE - 1 ) {
2011-02-19 04:32:22 +08:00
eframe [ EFRAME_SIZE ] = wc - > ctlreg ;
eframe [ EFRAME_SIZE + 1 ] = wc - > txident + + ;
2011-03-16 03:06:05 +08:00
if ( 4 = = wc - > desc - > ports )
eframe [ EFRAME_SIZE + 2 ] = wc - > tdm410leds ;
2010-08-28 05:59:27 +08:00
}
2011-02-19 04:32:22 +08:00
eframe + = ( EFRAME_SIZE + EFRAME_GAP ) ;
2010-08-28 05:59:27 +08:00
}
2011-08-31 00:38:10 +08:00
spin_unlock ( & wc - > reglock ) ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:38 +08:00
/* Must be called with wc.reglock held and local interrupts disabled */
static inline void
wctdm_setreg_intr ( struct wctdm * wc , struct wctdm_module * mod , int addr , int val )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:38 +08:00
struct wctdm_cmd * cmd ;
2011-01-20 22:32:58 +08:00
2011-06-03 04:03:38 +08:00
cmd = kmalloc ( sizeof ( * cmd ) , GFP_ATOMIC ) ;
if ( unlikely ( ! cmd ) )
return ;
cmd - > complete = NULL ;
cmd - > cmd = CMD_WR ( addr , val ) ;
2011-01-20 22:32:58 +08:00
2011-06-03 04:03:38 +08:00
list_add_tail ( & cmd - > node , & mod - > pending_cmds ) ;
2011-01-20 22:32:58 +08:00
}
2011-06-03 04:03:38 +08:00
int wctdm_setreg ( struct wctdm * wc , struct wctdm_module * mod , int addr , int val )
2011-01-20 22:32:58 +08:00
{
2011-06-03 04:03:38 +08:00
struct wctdm_cmd * cmd ;
unsigned long flags ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
#if 0 /* TODO */
2010-08-28 05:59:27 +08:00
/* QRV and BRI cards are only addressed at their first "port" */
2011-06-03 04:03:01 +08:00
if ( ( card & 0x03 ) & & ( ( wc - > mods [ card ] . type = = QRV ) | |
( wc - > mods [ card ] . type = = BRI ) ) )
2010-08-28 05:59:27 +08:00
return 0 ;
2011-06-03 04:03:15 +08:00
# endif
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:38 +08:00
cmd = kmalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( unlikely ( ! cmd ) )
return - ENOMEM ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:38 +08:00
cmd - > complete = NULL ;
cmd - > cmd = CMD_WR ( addr , val ) ;
2011-01-20 22:32:58 +08:00
spin_lock_irqsave ( & wc - > reglock , flags ) ;
2011-06-03 04:03:38 +08:00
list_add_tail ( & cmd - > node , & mod - > pending_cmds ) ;
2011-01-20 22:32:58 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
2011-06-03 04:03:38 +08:00
return 0 ;
2011-01-20 22:32:58 +08:00
}
2011-06-03 04:03:15 +08:00
int wctdm_getreg ( struct wctdm * wc , struct wctdm_module * const mod , int addr )
2011-01-20 22:32:58 +08:00
{
2011-06-03 04:03:38 +08:00
unsigned long flags ;
struct wctdm_cmd * cmd ;
int val ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
#if 0 /* TODO */
2010-08-28 05:59:27 +08:00
/* if a QRV card, use only its first channel */
2011-06-03 04:03:01 +08:00
if ( wc - > mods [ card ] . type = = QRV ) {
2011-01-20 22:32:58 +08:00
if ( card & 3 )
return 0 ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:15 +08:00
# endif
2011-01-20 22:32:58 +08:00
2011-06-03 04:03:38 +08:00
cmd = kmalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( ! cmd )
return - ENOMEM ;
2011-01-20 22:32:58 +08:00
2011-06-03 04:03:38 +08:00
cmd - > complete = kmalloc ( sizeof ( * cmd - > complete ) , GFP_KERNEL ) ;
if ( ! cmd - > complete ) {
kfree ( cmd ) ;
return - ENOMEM ;
}
init_completion ( cmd - > complete ) ;
cmd - > cmd = CMD_RD ( addr ) ;
spin_lock_irqsave ( & wc - > reglock , flags ) ;
list_add_tail ( & cmd - > node , & mod - > pending_cmds ) ;
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
wait_for_completion ( cmd - > complete ) ;
val = cmd - > cmd & 0xff ;
kfree ( cmd - > complete ) ;
kfree ( cmd ) ;
2011-01-20 22:32:58 +08:00
return val ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:48 +08:00
static int wctdm_getregs ( struct wctdm * wc , struct wctdm_module * const mod ,
int * const addresses , const size_t count )
{
int x ;
unsigned long flags ;
struct wctdm_cmd * cmd ;
struct wctdm_cmd * * cmds = kmalloc ( sizeof ( cmd ) * count , GFP_KERNEL ) ;
if ( ! cmds )
return - ENOMEM ;
for ( x = 0 ; x < count ; + + x ) {
cmd = kmalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( ! cmd ) {
kfree ( cmds ) ;
return - ENOMEM ;
}
cmd - > complete = kmalloc ( sizeof ( * cmd - > complete ) , GFP_KERNEL ) ;
if ( ! cmd - > complete ) {
kfree ( cmd ) ;
kfree ( cmds ) ;
return - ENOMEM ;
}
init_completion ( cmd - > complete ) ;
cmd - > cmd = CMD_RD ( addresses [ x ] ) ;
spin_lock_irqsave ( & wc - > reglock , flags ) ;
list_add_tail ( & cmd - > node , & mod - > pending_cmds ) ;
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
cmds [ x ] = cmd ;
}
for ( x = count - 1 ; x > = 0 ; - - x ) {
cmd = cmds [ x ] ;
wait_for_completion ( cmd - > complete ) ;
addresses [ x ] = cmd - > cmd & 0xff ;
kfree ( cmd - > complete ) ;
kfree ( cmd ) ;
}
kfree ( cmds ) ;
return 0 ;
}
2011-06-03 04:03:20 +08:00
/**
* call with wc - > reglock held and interrupts disabled .
*/
static void cmd_retransmit ( struct wctdm * wc )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:38 +08:00
int x ;
for ( x = 0 ; x < wc - > mods_per_board ; x + + ) {
struct wctdm_module * const mod = & wc - > mods [ x ] ;
if ( mod - > type = = BRI )
continue ;
list_splice_init ( & mod - > active_cmds , & mod - > pending_cmds ) ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:38 +08:00
2010-08-28 05:59:27 +08:00
# ifdef VPM_SUPPORT
if ( wc - > vpmadt032 )
vpmadt032_resend ( wc - > vpmadt032 ) ;
# endif
}
/**
* extract_tdm_data ( ) - Move TDM data from sframe to channels .
*
*/
static void extract_tdm_data ( struct wctdm * wc , const u8 * sframe )
{
int i ;
register u8 * chanchunk ;
for ( i = 0 ; i < wc - > avchannels ; i + = 4 ) {
chanchunk = & wc - > chans [ 0 + i ] - > chan . readchunk [ 0 ] ;
chanchunk [ 0 ] = sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 0 ] ;
chanchunk [ 1 ] = sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 1 ] ;
chanchunk [ 2 ] = sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 2 ] ;
chanchunk [ 3 ] = sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 3 ] ;
chanchunk [ 4 ] = sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 4 ] ;
chanchunk [ 5 ] = sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 5 ] ;
chanchunk [ 6 ] = sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 6 ] ;
chanchunk [ 7 ] = sframe [ 0 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 7 ] ;
chanchunk = & wc - > chans [ 1 + i ] - > chan . readchunk [ 0 ] ;
chanchunk [ 0 ] = sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 0 ] ;
chanchunk [ 1 ] = sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 1 ] ;
chanchunk [ 2 ] = sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 2 ] ;
chanchunk [ 3 ] = sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 3 ] ;
chanchunk [ 4 ] = sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 4 ] ;
chanchunk [ 5 ] = sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 5 ] ;
chanchunk [ 6 ] = sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 6 ] ;
chanchunk [ 7 ] = sframe [ 1 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 7 ] ;
chanchunk = & wc - > chans [ 2 + i ] - > chan . readchunk [ 0 ] ;
chanchunk [ 0 ] = sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 0 ] ;
chanchunk [ 1 ] = sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 1 ] ;
chanchunk [ 2 ] = sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 2 ] ;
chanchunk [ 3 ] = sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 3 ] ;
chanchunk [ 4 ] = sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 4 ] ;
chanchunk [ 5 ] = sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 5 ] ;
chanchunk [ 6 ] = sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 6 ] ;
chanchunk [ 7 ] = sframe [ 2 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 7 ] ;
chanchunk = & wc - > chans [ 3 + i ] - > chan . readchunk [ 0 ] ;
chanchunk [ 0 ] = sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 0 ] ;
chanchunk [ 1 ] = sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 1 ] ;
chanchunk [ 2 ] = sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 2 ] ;
chanchunk [ 3 ] = sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 3 ] ;
chanchunk [ 4 ] = sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 4 ] ;
chanchunk [ 5 ] = sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 5 ] ;
chanchunk [ 6 ] = sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 6 ] ;
chanchunk [ 7 ] = sframe [ 3 + i + ( EFRAME_SIZE + EFRAME_GAP ) * 7 ] ;
}
2011-06-29 06:28:53 +08:00
/* Pre-echo with the vpmoct overwrites the 24th timeslot with the
* specified channel ' s pre - echo audio stream . This data is ignored
* on all but the 24 xx card , so we store it in a temporary buffer .
*/
if ( wc - > vpmoct & & wc - > vpmoct - > preecho_enabled ) {
chanchunk = & wc - > vpmoct - > preecho_buf [ 0 ] ;
chanchunk [ 0 ] = sframe [ 23 + ( EFRAME_SIZE + EFRAME_GAP ) * 0 ] ;
chanchunk [ 1 ] = sframe [ 23 + ( EFRAME_SIZE + EFRAME_GAP ) * 1 ] ;
chanchunk [ 2 ] = sframe [ 23 + ( EFRAME_SIZE + EFRAME_GAP ) * 2 ] ;
chanchunk [ 3 ] = sframe [ 23 + ( EFRAME_SIZE + EFRAME_GAP ) * 3 ] ;
chanchunk [ 4 ] = sframe [ 23 + ( EFRAME_SIZE + EFRAME_GAP ) * 4 ] ;
chanchunk [ 5 ] = sframe [ 23 + ( EFRAME_SIZE + EFRAME_GAP ) * 5 ] ;
chanchunk [ 6 ] = sframe [ 23 + ( EFRAME_SIZE + EFRAME_GAP ) * 6 ] ;
chanchunk [ 7 ] = sframe [ 23 + ( EFRAME_SIZE + EFRAME_GAP ) * 7 ] ;
}
2010-08-28 05:59:27 +08:00
}
2011-02-19 04:32:22 +08:00
static inline void wctdm_receiveprep ( struct wctdm * wc , const u8 * sframe )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:20 +08:00
int x , y ;
bool irqmiss = false ;
2010-08-28 05:59:27 +08:00
unsigned char expected ;
2011-02-19 04:32:22 +08:00
const u8 * eframe = sframe ;
2010-08-28 05:59:27 +08:00
2011-02-19 04:32:22 +08:00
if ( unlikely ( ! is_good_frame ( sframe ) ) )
2010-08-28 05:59:27 +08:00
return ;
2011-08-31 00:38:10 +08:00
spin_lock ( & wc - > reglock ) ;
2011-06-29 06:28:53 +08:00
if ( likely ( is_initialized ( wc ) ) )
2011-02-19 04:32:22 +08:00
extract_tdm_data ( wc , sframe ) ;
2010-08-28 05:59:27 +08:00
for ( x = 0 ; x < DAHDI_CHUNKSIZE ; x + + ) {
if ( x < DAHDI_CHUNKSIZE - 1 ) {
2011-06-03 04:03:20 +08:00
expected = wc - > rxident + 1 ;
2011-02-19 04:32:22 +08:00
wc - > rxident = eframe [ EFRAME_SIZE + 1 ] ;
2010-08-28 05:59:27 +08:00
if ( wc - > rxident ! = expected ) {
2011-06-03 04:03:20 +08:00
irqmiss = true ;
2010-08-28 05:59:27 +08:00
cmd_retransmit ( wc ) ;
}
}
2011-01-31 23:53:26 +08:00
2011-06-03 04:03:20 +08:00
for ( y = 0 ; y < wc - > avchannels ; y + + )
_cmd_decipher ( wc , eframe , y ) ;
if ( wc - > vpmadt032 )
2011-02-19 04:32:22 +08:00
cmd_decipher_vpmadt032 ( wc , eframe ) ;
2011-06-29 06:28:53 +08:00
else if ( wc - > vpmoct )
cmd_decipher_vpmoct ( wc , eframe ) ;
2010-08-28 05:59:27 +08:00
2011-02-19 04:32:22 +08:00
eframe + = ( EFRAME_SIZE + EFRAME_GAP ) ;
2010-08-28 05:59:27 +08:00
}
2011-08-31 00:38:10 +08:00
spin_unlock ( & wc - > reglock ) ;
2010-08-28 05:59:27 +08:00
/* XXX We're wasting 8 taps. We should get closer :( */
2011-06-29 06:28:53 +08:00
if ( likely ( is_initialized ( wc ) ) ) {
2010-08-28 05:59:27 +08:00
for ( x = 0 ; x < wc - > avchannels ; x + + ) {
2011-06-03 04:03:15 +08:00
struct wctdm_chan * const wchan = wc - > chans [ x ] ;
struct dahdi_chan * const c = & wchan - > chan ;
2010-08-28 05:59:27 +08:00
# ifdef CONFIG_VOICEBUS_ECREFERENCE
unsigned char buffer [ DAHDI_CHUNKSIZE ] ;
__dahdi_fifo_get ( wc - > ec_reference [ x ] , buffer ,
ARRAY_SIZE ( buffer ) ) ;
2011-08-31 00:38:10 +08:00
_dahdi_ec_chunk ( c , c - > readchunk , buffer ) ;
2010-08-28 05:59:27 +08:00
# else
2011-06-29 06:28:53 +08:00
if ( ( wc - > vpmoct ) & &
( wchan - > timeslot = = wc - > vpmoct - > preecho_timeslot ) & &
( wc - > vpmoct - > preecho_enabled ) ) {
__dahdi_ec_chunk ( c , c - > readchunk ,
wc - > vpmoct - > preecho_buf ,
c - > writechunk ) ;
} else {
__dahdi_ec_chunk ( c , c - > readchunk , c - > readchunk ,
c - > writechunk ) ;
}
2010-08-28 05:59:27 +08:00
# endif
}
for ( x = 0 ; x < MAX_SPANS ; x + + ) {
if ( wc - > spans [ x ] ) {
struct dahdi_span * s = & wc - > spans [ x ] - > span ;
# if 1
/* Check for digital spans */
if ( s - > ops - > chanconfig = = b400m_chanconfig ) {
BUG_ON ( ! is_hx8 ( wc ) ) ;
if ( s - > flags & DAHDI_FLAG_RUNNING )
b400m_dchan ( s ) ;
}
# endif
2011-08-31 00:38:10 +08:00
_dahdi_receive ( s ) ;
2010-08-28 05:59:27 +08:00
if ( unlikely ( irqmiss ) )
2011-10-27 03:00:28 +08:00
wc - > ddev - > irqmisses + + ;
2010-08-28 05:59:27 +08:00
}
}
}
}
2011-06-03 04:03:15 +08:00
static int wait_access ( struct wctdm * wc , struct wctdm_module * const mod )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:15 +08:00
unsigned char data = 0 ;
int count = 0 ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
# define MAX 10 /* attempts */
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
/* Wait for indirect access */
while ( count + + < MAX ) {
data = wctdm_getreg ( wc , mod , I_STATUS ) ;
2010-08-28 05:59:27 +08:00
if ( ! data )
return 0 ;
2011-06-03 04:03:15 +08:00
}
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
if ( count > ( MAX - 1 ) ) {
dev_notice ( & wc - > vb . pdev - > dev ,
" ##### Loop error (%02x) ##### \n " , data ) ;
}
2010-08-28 05:59:27 +08:00
return 0 ;
}
static unsigned char translate_3215 ( unsigned char address )
{
int x ;
for ( x = 0 ; x < ARRAY_SIZE ( indirect_regs ) ; x + + ) {
if ( indirect_regs [ x ] . address = = address ) {
address = indirect_regs [ x ] . altaddr ;
break ;
}
}
return address ;
}
2011-06-03 04:03:15 +08:00
static int
wctdm_proslic_setreg_indirect ( struct wctdm * wc , struct wctdm_module * const mod ,
unsigned char address , unsigned short data )
2010-08-28 05:59:27 +08:00
{
int res = - 1 ;
2011-06-03 04:03:24 +08:00
address = translate_3215 ( address ) ;
if ( address = = 255 )
return 0 ;
2011-06-03 04:03:15 +08:00
if ( ! wait_access ( wc , mod ) ) {
wctdm_setreg ( wc , mod , IDA_LO , ( u8 ) ( data & 0xFF ) ) ;
wctdm_setreg ( wc , mod , IDA_HI , ( u8 ) ( ( data & 0xFF00 ) > > 8 ) ) ;
wctdm_setreg ( wc , mod , IAA , address ) ;
2010-08-28 05:59:27 +08:00
res = 0 ;
} ;
return res ;
}
2011-06-03 04:03:15 +08:00
static int
wctdm_proslic_getreg_indirect ( struct wctdm * wc , struct wctdm_module * const mod ,
unsigned char address )
2010-08-28 05:59:27 +08:00
{
int res = - 1 ;
char * p = NULL ;
2011-06-03 04:03:24 +08:00
address = translate_3215 ( address ) ;
if ( address = = 255 )
return 0 ;
2011-06-03 04:03:15 +08:00
if ( ! wait_access ( wc , mod ) ) {
wctdm_setreg ( wc , mod , IAA , address ) ;
if ( ! wait_access ( wc , mod ) ) {
2011-06-03 04:03:48 +08:00
int addresses [ 2 ] = { IDA_LO , IDA_HI } ;
wctdm_getregs ( wc , mod , addresses ,
ARRAY_SIZE ( addresses ) ) ;
res = addresses [ 0 ] | ( addresses [ 1 ] < < 8 ) ;
2010-08-28 05:59:27 +08:00
} else
p = " Failed to wait inside \n " ;
} else
p = " failed to wait \n " ;
if ( p )
dev_notice ( & wc - > vb . pdev - > dev , " %s " , p ) ;
return res ;
}
2011-06-03 04:03:15 +08:00
static int
wctdm_proslic_init_indirect_regs ( struct wctdm * wc , struct wctdm_module * mod )
2010-08-28 05:59:27 +08:00
{
unsigned char i ;
for ( i = 0 ; i < ARRAY_SIZE ( indirect_regs ) ; i + + ) {
2011-06-03 04:03:15 +08:00
if ( wctdm_proslic_setreg_indirect ( wc , mod ,
2010-08-28 05:59:27 +08:00
indirect_regs [ i ] . address ,
indirect_regs [ i ] . initial ) )
return - 1 ;
}
return 0 ;
}
2011-06-03 04:03:15 +08:00
static int
wctdm_proslic_verify_indirect_regs ( struct wctdm * wc , struct wctdm_module * mod )
2010-08-28 05:59:27 +08:00
{
int passed = 1 ;
unsigned short i , initial ;
int j ;
2011-06-03 04:03:15 +08:00
for ( i = 0 ; i < ARRAY_SIZE ( indirect_regs ) ; i + + ) {
j = wctdm_proslic_getreg_indirect ( wc , mod ,
( u8 ) indirect_regs [ i ] . address ) ;
2010-08-28 05:59:27 +08:00
if ( j < 0 ) {
dev_notice ( & wc - > vb . pdev - > dev , " Failed to read indirect register %d \n " , i ) ;
return - 1 ;
}
2011-06-03 04:03:15 +08:00
initial = indirect_regs [ i ] . initial ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:24 +08:00
if ( ( j ! = initial ) & & ( indirect_regs [ i ] . altaddr ! = 255 ) ) {
2011-06-03 04:02:51 +08:00
dev_notice ( & wc - > vb . pdev - > dev ,
" !!!!!!! %s iREG %X = %X should be %X \n " ,
indirect_regs [ i ] . name ,
indirect_regs [ i ] . address , j , initial ) ;
2010-08-28 05:59:27 +08:00
passed = 0 ;
}
}
2011-06-03 04:03:15 +08:00
if ( passed ) {
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Init Indirect Registers completed successfully. \n " ) ;
}
} else {
dev_notice ( & wc - > vb . pdev - > dev ,
" !!!!! Init Indirect Registers UNSUCCESSFULLY. \n " ) ;
2010-08-28 05:59:27 +08:00
return - 1 ;
2011-06-03 04:03:15 +08:00
}
return 0 ;
2010-08-28 05:59:27 +08:00
}
2011-08-31 00:38:23 +08:00
/**
* wctdm_proslic_check_oppending -
*
* Ensures that a write to the line feed register on the SLIC has been
* processed . If it hasn ' t after the timeout value , then it will resend the
* command and wait for another timeout period .
*
*/
2011-06-03 04:03:15 +08:00
static void
wctdm_proslic_check_oppending ( struct wctdm * wc , struct wctdm_module * const mod )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:02:51 +08:00
struct fxs * const fxs = & mod - > mod . fxs ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:06 +08:00
if ( ! ( fxs - > lasttxhook & SLIC_LF_OPPENDING ) )
return ;
2010-08-28 05:59:27 +08:00
/* Monitor the Pending LF state change, for the next 100ms */
2011-06-03 04:03:43 +08:00
spin_lock ( & wc - > reglock ) ;
2010-08-28 05:59:27 +08:00
2011-08-31 00:38:19 +08:00
2011-06-03 04:03:06 +08:00
if ( ! ( fxs - > lasttxhook & SLIC_LF_OPPENDING ) ) {
2011-06-03 04:03:43 +08:00
spin_unlock ( & wc - > reglock ) ;
2011-06-03 04:03:06 +08:00
return ;
}
2010-08-28 05:59:27 +08:00
2011-08-31 00:38:19 +08:00
if ( ( fxs - > linefeed_control_shadow & SLIC_LF_SETMASK ) = =
( fxs - > lasttxhook & SLIC_LF_SETMASK ) ) {
2011-06-03 04:03:06 +08:00
fxs - > lasttxhook & = SLIC_LF_SETMASK ;
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" SLIC_LF OK: card=%d shadow=%02x "
2011-08-31 00:38:14 +08:00
" lasttxhook=%02x framecount=%ld \n " , mod - > card ,
2011-08-31 00:38:19 +08:00
fxs - > linefeed_control_shadow ,
fxs - > lasttxhook , wc - > framecount ) ;
2010-08-28 05:59:27 +08:00
}
2011-08-31 00:38:23 +08:00
} else if ( time_after ( wc - > framecount , fxs - > oppending_timeout ) ) {
/* Check again in 100 ms */
fxs - > oppending_timeout = wc - > framecount + 100 ;
2011-06-03 04:03:38 +08:00
wctdm_setreg_intr ( wc , mod , LINE_STATE , fxs - > lasttxhook ) ;
2011-06-03 04:03:06 +08:00
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" SLIC_LF RETRY: card=%d shadow=%02x "
2011-08-31 00:38:14 +08:00
" lasttxhook=%02x framecount=%ld \n " , mod - > card ,
2011-08-31 00:38:19 +08:00
fxs - > linefeed_control_shadow ,
fxs - > lasttxhook , wc - > framecount ) ;
2011-06-03 04:03:06 +08:00
}
2010-08-28 05:59:27 +08:00
}
2011-08-31 00:38:23 +08:00
2011-06-03 04:03:43 +08:00
spin_unlock ( & wc - > reglock ) ;
2010-08-28 05:59:27 +08:00
}
/* 256ms interrupt */
2011-06-03 04:03:15 +08:00
static void
wctdm_proslic_recheck_sanity ( struct wctdm * wc , struct wctdm_module * const mod )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:02:51 +08:00
struct fxs * const fxs = & mod - > mod . fxs ;
2010-08-28 05:59:27 +08:00
int res ;
# ifdef PAQ_DEBUG
2011-06-03 04:03:38 +08:00
res = mod - > isrshadow [ 1 ] ;
2010-08-28 05:59:27 +08:00
res & = ~ 0x3 ;
if ( res ) {
2011-06-03 04:03:38 +08:00
mod - > isrshadow [ 1 ] = 0 ;
2010-08-28 05:59:27 +08:00
fxs - > palarms + + ;
if ( fxs - > palarms < MAX_ALARMS ) {
dev_notice ( & wc - > vb . pdev - > dev , " Power alarm (%02x) on module %d, resetting! \n " , res , card + 1 ) ;
2011-06-03 04:02:51 +08:00
mod - > sethook = CMD_WR ( 19 , res ) ;
2010-08-28 05:59:27 +08:00
/* Update shadow register to avoid extra power alarms until next read */
2011-06-03 04:03:38 +08:00
mod - > isrshadow [ 1 ] = 0 ;
2010-08-28 05:59:27 +08:00
} else {
if ( fxs - > palarms = = MAX_ALARMS )
dev_notice ( & wc - > vb . pdev - > dev , " Too many power alarms on card %d, NOT resetting! \n " , card + 1 ) ;
}
}
# else
2011-06-03 04:03:43 +08:00
spin_lock ( & wc - > reglock ) ;
2010-08-28 05:59:27 +08:00
2011-08-31 00:38:19 +08:00
/* reg 64 has to be zero at last isr read */
res = ! fxs - > linefeed_control_shadow & &
2010-08-28 05:59:27 +08:00
! ( fxs - > lasttxhook & SLIC_LF_OPPENDING ) & & /* not a transition */
fxs - > lasttxhook ; /* not an intended zero */
if ( res ) {
fxs - > palarms + + ;
if ( fxs - > palarms < MAX_ALARMS ) {
2011-06-03 04:03:15 +08:00
dev_notice ( & wc - > vb . pdev - > dev ,
" Power alarm on module %d, resetting! \n " ,
mod - > card + 1 ) ;
2010-08-28 05:59:27 +08:00
if ( fxs - > lasttxhook = = SLIC_LF_RINGING ) {
2011-06-03 04:03:10 +08:00
fxs - > lasttxhook = POLARITY_XOR ( fxs ) ?
2010-08-28 05:59:27 +08:00
SLIC_LF_ACTIVE_REV :
SLIC_LF_ACTIVE_FWD ; ;
}
fxs - > lasttxhook | = SLIC_LF_OPPENDING ;
2011-06-03 04:02:51 +08:00
mod - > sethook = CMD_WR ( LINE_STATE , fxs - > lasttxhook ) ;
2011-08-31 00:38:23 +08:00
fxs - > oppending_timeout = wc - > framecount + 100 ;
2010-08-28 05:59:27 +08:00
2011-08-31 00:38:19 +08:00
/* Update shadow register to avoid extra power alarms
* until next read */
fxs - > linefeed_control_shadow = fxs - > lasttxhook ;
2010-08-28 05:59:27 +08:00
} else {
2011-06-03 04:03:15 +08:00
if ( fxs - > palarms = = MAX_ALARMS ) {
dev_notice ( & wc - > vb . pdev - > dev ,
" Too many power alarms on card %d, "
" NOT resetting! \n " , mod - > card + 1 ) ;
}
2010-08-28 05:59:27 +08:00
}
}
2011-06-03 04:03:43 +08:00
spin_unlock ( & wc - > reglock ) ;
2010-08-28 05:59:27 +08:00
# endif
}
2011-06-03 04:03:15 +08:00
static void wctdm_qrvdri_check_hook ( struct wctdm * wc , int card )
2010-08-28 05:59:27 +08:00
{
signed char b , b1 ;
int qrvcard = card & 0xfc ;
2011-06-03 04:02:56 +08:00
if ( wc - > mods [ card ] . mod . qrv . debtime > = 2 )
wc - > mods [ card ] . mod . qrv . debtime - - ;
2011-08-31 00:38:19 +08:00
b = wc - > mods [ qrvcard ] . mod . qrv . isrshadow [ 0 ] ; /* Hook/Ring state */
2010-08-28 05:59:27 +08:00
b & = 0xcc ; /* use bits 3-4 and 6-7 only */
2011-06-03 04:02:56 +08:00
if ( wc - > mods [ qrvcard ] . mod . qrv . radmode & RADMODE_IGNORECOR )
2011-06-03 04:02:51 +08:00
b & = ~ 4 ;
2011-06-03 04:02:56 +08:00
else if ( ! ( wc - > mods [ qrvcard ] . mod . qrv . radmode & RADMODE_INVERTCOR ) )
2011-06-03 04:02:51 +08:00
b ^ = 4 ;
2011-06-03 04:02:56 +08:00
if ( wc - > mods [ qrvcard + 1 ] . mod . qrv . radmode | RADMODE_IGNORECOR )
2011-06-03 04:02:51 +08:00
b & = ~ 0x40 ;
2011-06-03 04:02:56 +08:00
else if ( ! ( wc - > mods [ qrvcard + 1 ] . mod . qrv . radmode | RADMODE_INVERTCOR ) )
2011-06-03 04:02:51 +08:00
b ^ = 0x40 ;
2011-06-03 04:02:56 +08:00
if ( ( wc - > mods [ qrvcard ] . mod . qrv . radmode & RADMODE_IGNORECT ) | |
( ! ( wc - > mods [ qrvcard ] . mod . qrv . radmode & RADMODE_EXTTONE ) ) )
2011-06-03 04:02:51 +08:00
b & = ~ 8 ;
2011-06-03 04:02:56 +08:00
else if ( ! ( wc - > mods [ qrvcard ] . mod . qrv . radmode & RADMODE_EXTINVERT ) )
2011-06-03 04:02:51 +08:00
b ^ = 8 ;
2011-06-03 04:02:56 +08:00
if ( ( wc - > mods [ qrvcard + 1 ] . mod . qrv . radmode & RADMODE_IGNORECT ) | |
( ! ( wc - > mods [ qrvcard + 1 ] . mod . qrv . radmode & RADMODE_EXTTONE ) ) )
2011-06-03 04:02:51 +08:00
b & = ~ 0x80 ;
2011-06-03 04:02:56 +08:00
else if ( ! ( wc - > mods [ qrvcard + 1 ] . mod . qrv . radmode & RADMODE_EXTINVERT ) )
2011-06-03 04:02:51 +08:00
b ^ = 0x80 ;
2010-08-28 05:59:27 +08:00
/* now b & MASK should be zero, if its active */
/* check for change in chan 0 */
2011-06-03 04:02:56 +08:00
if ( ( ! ( b & 0xc ) ) ! = wc - > mods [ qrvcard + 2 ] . mod . qrv . hook )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:02:56 +08:00
wc - > mods [ qrvcard ] . mod . qrv . debtime = wc - > mods [ qrvcard ] . mod . qrv . debouncetime ;
wc - > mods [ qrvcard + 2 ] . mod . qrv . hook = ! ( b & 0xc ) ;
2010-08-28 05:59:27 +08:00
}
/* if timed-out and ready */
2011-06-03 04:02:56 +08:00
if ( wc - > mods [ qrvcard ] . mod . qrv . debtime = = 1 ) {
b1 = wc - > mods [ qrvcard + 2 ] . mod . qrv . hook ;
2011-06-03 04:02:51 +08:00
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" QRV channel %d rx state changed to %d \n " ,
2011-06-03 04:02:56 +08:00
qrvcard , wc - > mods [ qrvcard + 2 ] . mod . qrv . hook ) ;
2011-06-03 04:02:51 +08:00
}
2010-08-28 05:59:27 +08:00
dahdi_hooksig ( wc - > aspan - > span . chans [ qrvcard ] ,
( b1 ) ? DAHDI_RXSIG_OFFHOOK : DAHDI_RXSIG_ONHOOK ) ;
2011-06-03 04:02:56 +08:00
wc - > mods [ card ] . mod . qrv . debtime = 0 ;
2010-08-28 05:59:27 +08:00
}
/* check for change in chan 1 */
2011-06-03 04:02:56 +08:00
if ( ( ! ( b & 0xc0 ) ) ! = wc - > mods [ qrvcard + 3 ] . mod . qrv . hook )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:02:56 +08:00
wc - > mods [ qrvcard + 1 ] . mod . qrv . debtime = QRV_DEBOUNCETIME ;
wc - > mods [ qrvcard + 3 ] . mod . qrv . hook = ! ( b & 0xc0 ) ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:02:56 +08:00
if ( wc - > mods [ qrvcard + 1 ] . mod . qrv . debtime = = 1 ) {
b1 = wc - > mods [ qrvcard + 3 ] . mod . qrv . hook ;
2011-06-03 04:02:51 +08:00
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" QRV channel %d rx state changed to %d \n " ,
2011-06-03 04:02:56 +08:00
qrvcard + 1 , wc - > mods [ qrvcard + 3 ] . mod . qrv . hook ) ;
2011-06-03 04:02:51 +08:00
}
2010-08-28 05:59:27 +08:00
dahdi_hooksig ( wc - > aspan - > span . chans [ qrvcard + 1 ] ,
( b1 ) ? DAHDI_RXSIG_OFFHOOK : DAHDI_RXSIG_ONHOOK ) ;
2011-06-03 04:02:56 +08:00
wc - > mods [ card ] . mod . qrv . debtime = 0 ;
2010-08-28 05:59:27 +08:00
}
return ;
}
2011-08-31 00:38:32 +08:00
static inline bool is_fxo_ringing ( const struct fxo * const fxo )
{
return ( ( fxo - > hook_ring_shadow & 0x60 ) & &
( fxo - > battery_state = = BATTERY_PRESENT ) ) ;
}
static inline bool is_fxo_ringing_positive ( const struct fxo * const fxo )
{
return ( ( ( fxo - > hook_ring_shadow & 0x60 ) = = 0x20 ) & &
( fxo - > battery_state = = BATTERY_PRESENT ) ) ;
}
static inline bool is_fxo_ringing_negative ( const struct fxo * const fxo )
{
return ( ( ( fxo - > hook_ring_shadow & 0x60 ) = = 0x40 ) & &
( fxo - > battery_state = = BATTERY_PRESENT ) ) ;
}
static inline void set_ring ( struct fxo * fxo , enum ring_detector_state new )
{
fxo - > ring_state = new ;
}
static void wctdm_fxo_ring_detect ( struct wctdm * wc , struct wctdm_module * mod )
{
struct fxo * const fxo = & mod - > mod . fxo ;
/* Look for ring status bits (Ring Detect Signal Negative and Ring
* Detect Signal Positive ) to transition back and forth some number of
* times to indicate that a ring is occurring . Provide some number of
* samples to allow for the transitions to occur before giving up .
* NOTE : neon mwi voltages will trigger one of these bits to go active
* but not to have transitions between the two bits ( i . e . no negative
* to positive or positive to negative traversals ) */
switch ( fxo - > ring_state ) {
case DEBOUNCING_RINGING_POSITIVE :
if ( is_fxo_ringing_negative ( fxo ) ) {
if ( + + fxo - > ring_polarity_change_count > 4 ) {
mod_hooksig ( wc , mod , DAHDI_RXSIG_RING ) ;
set_ring ( fxo , RINGING ) ;
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" RING on %s! \n " ,
get_dahdi_chan ( wc , mod ) - > name ) ;
}
} else {
set_ring ( fxo , DEBOUNCING_RINGING_NEGATIVE ) ;
}
} else if ( time_after ( wc - > framecount ,
fxo - > ringdebounce_timer ) ) {
set_ring ( fxo , RINGOFF ) ;
}
break ;
case DEBOUNCING_RINGING_NEGATIVE :
if ( is_fxo_ringing_positive ( fxo ) ) {
if ( + + fxo - > ring_polarity_change_count > 4 ) {
mod_hooksig ( wc , mod , DAHDI_RXSIG_RING ) ;
set_ring ( fxo , RINGING ) ;
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" RING on %s! \n " ,
get_dahdi_chan ( wc , mod ) - > name ) ;
}
} else {
set_ring ( fxo , DEBOUNCING_RINGING_POSITIVE ) ;
}
} else if ( time_after ( wc - > framecount ,
fxo - > ringdebounce_timer ) ) {
set_ring ( fxo , RINGOFF ) ;
}
break ;
case RINGING :
if ( ! is_fxo_ringing ( fxo ) ) {
set_ring ( fxo , DEBOUNCING_RINGOFF ) ;
fxo - > ringdebounce_timer =
2012-03-14 06:12:37 +08:00
wc - > framecount + ringdebounce / 8 ;
2011-08-31 00:38:32 +08:00
}
break ;
case DEBOUNCING_RINGOFF :
if ( ! is_fxo_ringing ( fxo ) ) {
if ( time_after ( wc - > framecount ,
fxo - > ringdebounce_timer ) ) {
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" NO RING on %s! \n " ,
get_dahdi_chan ( wc , mod ) - > name ) ;
}
mod_hooksig ( wc , mod , DAHDI_RXSIG_OFFHOOK ) ;
set_ring ( fxo , RINGOFF ) ;
}
} else {
set_ring ( fxo , RINGING ) ;
}
break ;
case RINGOFF :
if ( is_fxo_ringing ( fxo ) ) {
/* Look for positive/negative crossings in ring status
* reg */
if ( is_fxo_ringing_positive ( fxo ) )
set_ring ( fxo , DEBOUNCING_RINGING_POSITIVE ) ;
else
set_ring ( fxo , DEBOUNCING_RINGING_NEGATIVE ) ;
fxo - > ringdebounce_timer =
wc - > framecount + ringdebounce / 8 ;
fxo - > ring_polarity_change_count = 0 ;
}
break ;
}
}
2011-08-31 00:38:36 +08:00
# define MS_PER_CHECK_HOOK 1
2011-06-03 04:03:15 +08:00
static void
2011-08-31 00:38:36 +08:00
wctdm_check_battery_lost ( struct wctdm * wc , struct wctdm_module * const mod )
2010-08-28 05:59:27 +08:00
{
2011-08-31 00:38:36 +08:00
struct fxo * const fxo = & mod - > mod . fxo ;
2010-08-28 05:59:27 +08:00
2011-08-31 00:38:36 +08:00
/* 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 )
*/
switch ( fxo - > battery_state ) {
case BATTERY_DEBOUNCING_PRESENT :
/* we were going to BATTERY_PRESENT, but
* battery was lost again . */
fxo - > battery_state = BATTERY_LOST ;
break ;
case BATTERY_UNKNOWN :
mod_hooksig ( wc , mod , DAHDI_RXSIG_ONHOOK ) ;
case BATTERY_DEBOUNCING_PRESENT_ALARM : /* intentional drop through */
case BATTERY_PRESENT :
fxo - > battery_state = BATTERY_DEBOUNCING_LOST ;
fxo - > battdebounce_timer = wc - > framecount + battdebounce ;
break ;
case BATTERY_DEBOUNCING_LOST :
if ( time_after ( wc - > framecount , fxo - > battdebounce_timer ) ) {
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" NO BATTERY on %d/%d! \n " ,
wc - > aspan - > span . spanno ,
mod - > card + 1 ) ;
}
# ifdef JAPAN
if ( ! wc - > ohdebounce & & wc - > offhook ) {
dahdi_hooksig ( wc - > aspan - > chans [ card ] ,
DAHDI_RXSIG_ONHOOK ) ;
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Signalled On Hook \n " ) ;
}
# ifdef ZERO_BATT_RING
wc - > onhook + + ;
# endif
}
# else
mod_hooksig ( wc , mod , DAHDI_RXSIG_ONHOOK ) ;
# endif
/* set the alarm timer, taking into account that part
* of its time period has already passed while
* debouncing occurred */
fxo - > battery_state = BATTERY_DEBOUNCING_LOST_ALARM ;
fxo - > battdebounce_timer = wc - > framecount +
battalarm - battdebounce ;
}
break ;
case BATTERY_DEBOUNCING_LOST_ALARM :
if ( time_after ( wc - > framecount , fxo - > battdebounce_timer ) ) {
fxo - > battery_state = BATTERY_LOST ;
dahdi_alarm_channel ( get_dahdi_chan ( wc , mod ) ,
DAHDI_ALARM_RED ) ;
}
break ;
case BATTERY_LOST :
break ;
}
}
static void
wctdm_check_battery_present ( struct wctdm * wc , struct wctdm_module * const mod )
{
struct fxo * const fxo = & mod - > mod . fxo ;
switch ( fxo - > battery_state ) {
case BATTERY_DEBOUNCING_PRESENT :
if ( time_after ( jiffies , fxo - > battdebounce_timer ) ) {
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" BATTERY on %d/%d (%s)! \n " ,
wc - > aspan - > span . spanno , mod - > card + 1 ,
( fxo - > line_voltage_status < 0 ) ?
" - " : " + " ) ;
}
# ifdef ZERO_BATT_RING
if ( wc - > onhook ) {
wc - > onhook = 0 ;
dahdi_hooksig ( wc - > aspan - > chans [ card ] ,
DAHDI_RXSIG_OFFHOOK ) ;
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Signalled Off Hook \n " ) ;
}
}
# else
mod_hooksig ( wc , mod , DAHDI_RXSIG_OFFHOOK ) ;
# endif
/* set the alarm timer, taking into account that part
* of its time period has already passed while
* debouncing occurred */
fxo - > battery_state = BATTERY_DEBOUNCING_PRESENT_ALARM ;
fxo - > battdebounce_timer = jiffies +
msecs_to_jiffies ( battalarm - battdebounce ) ;
}
break ;
case BATTERY_DEBOUNCING_PRESENT_ALARM :
if ( time_after ( jiffies , fxo - > battdebounce_timer ) ) {
fxo - > battery_state = BATTERY_PRESENT ;
dahdi_alarm_channel ( get_dahdi_chan ( wc , mod ) ,
DAHDI_ALARM_NONE ) ;
}
break ;
case BATTERY_PRESENT :
break ;
case BATTERY_DEBOUNCING_LOST :
/* we were going to BATTERY_LOST, but battery appeared again,
* so clear the debounce timer */
fxo - > battery_state = BATTERY_PRESENT ;
break ;
case BATTERY_UNKNOWN :
mod_hooksig ( wc , mod , DAHDI_RXSIG_OFFHOOK ) ;
case BATTERY_LOST : /* intentional drop through */
case BATTERY_DEBOUNCING_LOST_ALARM :
fxo - > battery_state = BATTERY_DEBOUNCING_PRESENT ;
fxo - > battdebounce_timer = jiffies +
msecs_to_jiffies ( battdebounce ) ;
break ;
}
}
2011-08-31 00:38:41 +08:00
static void
wctdm_fxo_stop_debouncing_polarity ( struct wctdm * wc ,
struct wctdm_module * const mod )
{
struct fxo * const fxo = & mod - > mod . fxo ;
switch ( fxo - > polarity_state ) {
case UNKNOWN_POLARITY :
break ;
case POLARITY_DEBOUNCE_POSITIVE :
fxo - > polarity_state = POLARITY_NEGATIVE ;
break ;
case POLARITY_POSITIVE :
break ;
case POLARITY_DEBOUNCE_NEGATIVE :
fxo - > polarity_state = POLARITY_POSITIVE ;
break ;
case POLARITY_NEGATIVE :
break ;
} ;
}
static void
wctdm_fxo_check_polarity ( struct wctdm * wc , struct wctdm_module * const mod ,
const bool positive_polarity )
{
struct fxo * const fxo = & mod - > mod . fxo ;
switch ( fxo - > polarity_state ) {
case UNKNOWN_POLARITY :
fxo - > polarity_state = ( positive_polarity ) ? POLARITY_POSITIVE :
POLARITY_NEGATIVE ;
break ;
case POLARITY_DEBOUNCE_POSITIVE :
if ( ! positive_polarity ) {
fxo - > polarity_state = POLARITY_NEGATIVE ;
} else if ( time_after ( wc - > framecount , fxo - > poldebounce_timer ) ) {
fxo - > polarity_state = POLARITY_POSITIVE ;
dahdi_qevent_lock ( get_dahdi_chan ( wc , mod ) ,
DAHDI_EVENT_POLARITY ) ;
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" %s: Polarity NEGATIVE -> POSITIVE \n " ,
get_dahdi_chan ( wc , mod ) - > name ) ;
}
}
break ;
case POLARITY_POSITIVE :
if ( ! positive_polarity ) {
fxo - > polarity_state = POLARITY_DEBOUNCE_NEGATIVE ;
fxo - > poldebounce_timer = wc - > framecount +
POLARITY_DEBOUNCE ;
}
break ;
case POLARITY_DEBOUNCE_NEGATIVE :
if ( positive_polarity ) {
fxo - > polarity_state = POLARITY_POSITIVE ;
} else if ( time_after ( wc - > framecount , fxo - > poldebounce_timer ) ) {
dahdi_qevent_lock ( get_dahdi_chan ( wc , mod ) ,
DAHDI_EVENT_POLARITY ) ;
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" %s: Polarity POSITIVE -> NEGATIVE \n " ,
get_dahdi_chan ( wc , mod ) - > name ) ;
}
fxo - > polarity_state = POLARITY_NEGATIVE ;
}
break ;
case POLARITY_NEGATIVE :
if ( positive_polarity ) {
fxo - > polarity_state = POLARITY_DEBOUNCE_POSITIVE ;
fxo - > poldebounce_timer = wc - > framecount +
POLARITY_DEBOUNCE ;
}
break ;
} ;
}
2011-08-31 00:38:36 +08:00
static void
wctdm_voicedaa_check_hook ( struct wctdm * wc , struct wctdm_module * const mod )
{
2010-08-28 05:59:27 +08:00
signed char b ;
2011-08-31 00:38:36 +08:00
u8 abs_voltage ;
2011-06-03 04:02:51 +08:00
struct fxo * const fxo = & mod - > mod . fxo ;
2010-08-28 05:59:27 +08:00
/* Try to track issues that plague slot one FXO's */
2011-08-31 00:38:32 +08:00
b = fxo - > hook_ring_shadow & 0x9b ;
2010-08-28 05:59:27 +08:00
if ( fxo - > offhook ) {
if ( b ! = 0x9 )
2011-06-03 04:03:15 +08:00
wctdm_setreg_intr ( wc , mod , 5 , 0x9 ) ;
2010-08-28 05:59:27 +08:00
} else {
if ( b ! = 0x8 )
2011-06-03 04:03:15 +08:00
wctdm_setreg_intr ( wc , mod , 5 , 0x8 ) ;
2011-08-31 00:38:32 +08:00
wctdm_fxo_ring_detect ( wc , mod ) ;
2010-08-28 05:59:27 +08:00
}
2011-08-31 00:38:36 +08:00
abs_voltage = abs ( fxo - > line_voltage_status ) ;
2010-08-28 05:59:27 +08:00
2011-08-31 00:38:14 +08:00
if ( fxovoltage & & time_after ( wc - > framecount , fxo - > display_fxovoltage ) ) {
/* Every 100 ms */
fxo - > display_fxovoltage = wc - > framecount + 100 ;
dev_info ( & wc - > vb . pdev - > dev ,
2011-08-31 00:38:36 +08:00
" Port %d: Voltage: %d \n " ,
mod - > card + 1 , fxo - > line_voltage_status ) ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:15 +08:00
if ( unlikely ( DAHDI_RXSIG_INITIAL = = get_dahdi_chan ( wc , mod ) - > rxhooksig ) ) {
2010-08-28 05:59:27 +08:00
/*
* dahdi - base will set DAHDI_RXSIG_INITIAL after a
* DAHDI_STARTUP or DAHDI_CHANCONFIG ioctl so that new events
* will be queued on the channel with the current received
* hook state . Channels that use robbed - bit signalling always
* report the current received state via the dahdi_rbsbits
* call . Since we only call dahdi_hooksig when we ' ve detected
* a change to report , let ' s forget our current state in order
* to force us to report it again via dahdi_hooksig .
*
*/
2011-08-31 00:38:36 +08:00
fxo - > battery_state = BATTERY_UNKNOWN ;
2010-08-28 05:59:27 +08:00
}
if ( abs_voltage < battthresh ) {
2011-08-31 00:38:41 +08:00
wctdm_fxo_stop_debouncing_polarity ( wc , mod ) ;
2011-08-31 00:38:36 +08:00
wctdm_check_battery_lost ( wc , mod ) ;
2010-08-28 05:59:27 +08:00
} else {
2011-08-31 00:38:36 +08:00
wctdm_check_battery_present ( wc , mod ) ;
2011-08-31 00:38:41 +08:00
wctdm_fxo_check_polarity ( wc , mod ,
( fxo - > line_voltage_status > 0 ) ) ;
2010-08-28 05:59:27 +08:00
}
/* Look for neon mwi pulse */
2011-06-03 04:02:51 +08:00
if ( neonmwi_monitor & & ! fxo - > offhook ) {
2010-08-28 05:59:27 +08:00
/* Look for 4 consecutive voltage readings
* where the voltage is over the neon limit but
* does not vary greatly from the last reading
*/
2011-08-31 00:38:36 +08:00
if ( fxo - > battery_state = = BATTERY_PRESENT & &
2010-08-28 05:59:27 +08:00
abs_voltage > neonmwi_level & &
( 0 = = fxo - > neonmwi_last_voltage | |
2011-08-31 00:38:36 +08:00
( fxo - > line_voltage_status > = fxo - > neonmwi_last_voltage - neonmwi_envelope & &
fxo - > line_voltage_status < = fxo - > neonmwi_last_voltage + neonmwi_envelope ) ) ) {
fxo - > neonmwi_last_voltage = fxo - > line_voltage_status ;
2010-08-28 05:59:27 +08:00
if ( NEONMWI_ON_DEBOUNCE = = fxo - > neonmwi_debounce ) {
fxo - > neonmwi_offcounter = neonmwi_offlimit_cycles ;
if ( 0 = = fxo - > neonmwi_state ) {
2011-06-03 04:03:15 +08:00
dahdi_qevent_lock ( get_dahdi_chan ( wc , mod ) , DAHDI_EVENT_NEONMWI_ACTIVE ) ;
2010-08-28 05:59:27 +08:00
fxo - > neonmwi_state = 1 ;
if ( debug )
2011-06-03 04:03:15 +08:00
dev_info ( & wc - > vb . pdev - > dev , " NEON MWI active for card %d \n " , mod - > card + 1 ) ;
2010-08-28 05:59:27 +08:00
}
fxo - > neonmwi_debounce + + ; /* terminate the processing */
} else if ( NEONMWI_ON_DEBOUNCE > fxo - > neonmwi_debounce ) {
fxo - > neonmwi_debounce + + ;
} else { /* Insure the count gets reset */
fxo - > neonmwi_offcounter = neonmwi_offlimit_cycles ;
}
} else {
fxo - > neonmwi_debounce = 0 ;
fxo - > neonmwi_last_voltage = 0 ;
}
/* If no neon mwi pulse for given period of time, indicte no neon mwi state */
if ( fxo - > neonmwi_state & & 0 < fxo - > neonmwi_offcounter ) {
fxo - > neonmwi_offcounter - - ;
if ( 0 = = fxo - > neonmwi_offcounter ) {
2011-06-03 04:03:15 +08:00
dahdi_qevent_lock ( get_dahdi_chan ( wc , mod ) , DAHDI_EVENT_NEONMWI_INACTIVE ) ;
2010-08-28 05:59:27 +08:00
fxo - > neonmwi_state = 0 ;
if ( debug )
2011-06-03 04:03:15 +08:00
dev_info ( & wc - > vb . pdev - > dev , " NEON MWI cleared for card %d \n " , mod - > card + 1 ) ;
2010-08-28 05:59:27 +08:00
}
}
}
# undef MS_PER_CHECK_HOOK
}
2011-06-03 04:03:15 +08:00
static void
wctdm_fxs_hooksig ( struct wctdm * wc , struct wctdm_module * const mod ,
enum dahdi_txsig txsig )
2010-08-28 05:59:27 +08:00
{
int x = 0 ;
unsigned long flags ;
2011-06-03 04:03:15 +08:00
struct fxs * const fxs = & mod - > mod . fxs ;
2011-06-03 04:03:43 +08:00
spin_lock_irqsave ( & wc - > reglock , flags ) ;
2010-08-28 05:59:27 +08:00
switch ( txsig ) {
case DAHDI_TXSIG_ONHOOK :
2011-06-03 04:03:15 +08:00
switch ( get_dahdi_chan ( wc , mod ) - > sig ) {
2010-08-28 05:59:27 +08:00
case DAHDI_SIG_FXOGS :
2011-06-03 04:03:10 +08:00
x = ( POLARITY_XOR ( fxs ) ) ?
2010-08-28 05:59:27 +08:00
SLIC_LF_RING_OPEN :
SLIC_LF_TIP_OPEN ;
break ;
2012-01-04 03:25:46 +08:00
case DAHDI_SIG_EM :
case DAHDI_SIG_FXOKS :
case DAHDI_SIG_FXOLS :
2011-06-03 04:00:29 +08:00
default :
2012-01-04 03:25:46 +08:00
x = fxs - > idletxhookstate ;
2011-06-03 04:00:29 +08:00
break ;
2010-08-28 05:59:27 +08:00
}
break ;
case DAHDI_TXSIG_OFFHOOK :
2011-06-03 04:03:15 +08:00
switch ( get_dahdi_chan ( wc , mod ) - > sig ) {
2010-08-28 05:59:27 +08:00
case DAHDI_SIG_EM :
2011-06-03 04:03:10 +08:00
x = ( POLARITY_XOR ( fxs ) ) ?
2010-08-28 05:59:27 +08:00
SLIC_LF_ACTIVE_FWD :
SLIC_LF_ACTIVE_REV ;
break ;
default :
x = fxs - > idletxhookstate ;
break ;
}
break ;
case DAHDI_TXSIG_START :
x = SLIC_LF_RINGING ;
break ;
case DAHDI_TXSIG_KEWL :
x = SLIC_LF_OPEN ;
break ;
default :
2011-06-03 04:03:43 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
2010-08-28 05:59:27 +08:00
dev_notice ( & wc - > vb . pdev - > dev ,
" wctdm24xxp: Can't set tx state to %d \n " , txsig ) ;
return ;
}
if ( x ! = fxs - > lasttxhook ) {
fxs - > lasttxhook = x | SLIC_LF_OPPENDING ;
2011-06-03 04:03:15 +08:00
mod - > sethook = CMD_WR ( LINE_STATE , fxs - > lasttxhook ) ;
2011-08-31 00:38:23 +08:00
fxs - > oppending_timeout = wc - > framecount + 100 ;
2011-06-03 04:03:43 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
2010-08-28 05:59:27 +08:00
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev , " Setting FXS hook state "
2011-08-31 00:38:14 +08:00
" to %d (%02x) framecount=%ld \n " , txsig , x ,
wc - > framecount ) ;
2010-08-28 05:59:27 +08:00
}
} else {
2011-06-03 04:03:43 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
2010-08-28 05:59:27 +08:00
}
}
2011-06-03 04:03:15 +08:00
static void wctdm_fxs_off_hook ( struct wctdm * wc , struct wctdm_module * const mod )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:15 +08:00
struct fxs * const fxs = & mod - > mod . fxs ;
2010-08-28 05:59:27 +08:00
if ( debug & DEBUG_CARD )
dev_info ( & wc - > vb . pdev - > dev ,
2011-06-03 04:03:15 +08:00
" fxs_off_hook: Card %d Going off hook \n " , mod - > card ) ;
2010-08-28 05:59:27 +08:00
switch ( fxs - > lasttxhook ) {
case SLIC_LF_RINGING : /* Ringing */
case SLIC_LF_OHTRAN_FWD : /* Forward On Hook Transfer */
case SLIC_LF_OHTRAN_REV : /* Reverse On Hook Transfer */
/* just detected OffHook, during Ringing or OnHookTransfer */
2011-06-03 04:03:10 +08:00
fxs - > idletxhookstate = POLARITY_XOR ( fxs ) ?
2010-08-28 05:59:27 +08:00
SLIC_LF_ACTIVE_REV :
SLIC_LF_ACTIVE_FWD ;
break ;
}
2011-06-30 00:57:38 +08:00
if ( ( fxs - > lasttxhook & SLIC_LF_SETMASK ) ! = SLIC_LF_OPEN )
wctdm_fxs_hooksig ( wc , mod , DAHDI_TXSIG_OFFHOOK ) ;
2011-06-03 04:03:15 +08:00
dahdi_hooksig ( get_dahdi_chan ( wc , mod ) , DAHDI_RXSIG_OFFHOOK ) ;
2010-08-28 05:59:27 +08:00
# ifdef DEBUG
if ( robust )
2011-06-03 04:03:15 +08:00
wctdm_init_proslic ( wc , mod , 1 , 0 , 1 ) ;
2010-08-28 05:59:27 +08:00
# endif
}
2011-06-30 00:57:38 +08:00
/**
* wctdm_fxs_on_hook - Report on hook to DAHDI .
* @ wc : Board hosting the module .
* @ card : Index of the module / port to place on hook .
*
* If we are intentionally dropping battery to signal a forward
* disconnect we do not want to place the line " On-Hook " . In this
* case , the core of DAHDI will place us on hook when one of the RBS
* timers expires .
*
*/
2011-06-03 04:03:15 +08:00
static void wctdm_fxs_on_hook ( struct wctdm * wc , struct wctdm_module * const mod )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:15 +08:00
if ( debug & DEBUG_CARD ) {
2010-08-28 05:59:27 +08:00
dev_info ( & wc - > vb . pdev - > dev ,
2011-06-03 04:03:15 +08:00
" fxs_on_hook: Card %d Going on hook \n " , mod - > card ) ;
}
2011-08-31 00:38:27 +08:00
if ( ( mod - > mod . fxs . lasttxhook & SLIC_LF_SETMASK ) ! = SLIC_LF_OPEN )
2011-06-30 00:57:38 +08:00
wctdm_fxs_hooksig ( wc , mod , DAHDI_TXSIG_ONHOOK ) ;
2011-06-03 04:03:15 +08:00
dahdi_hooksig ( get_dahdi_chan ( wc , mod ) , DAHDI_RXSIG_ONHOOK ) ;
2010-08-28 05:59:27 +08:00
}
2010-12-10 04:19:26 +08:00
static const char * wctdm_echocan_name ( const struct dahdi_chan * chan )
{
struct wctdm * wc = chan - > pvt ;
2011-01-31 23:53:26 +08:00
if ( wc - > vpmadt032 )
2010-12-10 04:19:26 +08:00
return vpmadt032_name ;
2011-06-29 06:28:53 +08:00
else if ( wc - > vpmoct )
return vpmoct_name ;
2011-06-29 06:29:00 +08:00
2011-06-03 04:01:49 +08:00
return NULL ;
2010-12-10 04:19:26 +08:00
}
2010-08-28 05:59:27 +08:00
static int wctdm_echocan_create ( struct dahdi_chan * chan ,
struct dahdi_echocanparams * ecp ,
struct dahdi_echocanparam * p ,
struct dahdi_echocan_state * * ec )
{
struct wctdm * wc = chan - > pvt ;
struct wctdm_chan * wchan = container_of ( chan , struct wctdm_chan , chan ) ;
const struct dahdi_echocan_ops * ops ;
const struct dahdi_echocan_features * features ;
2011-01-31 23:53:26 +08:00
enum adt_companding comp ;
2010-08-28 05:59:27 +08:00
# ifdef VPM_SUPPORT
if ( ! vpmsupport )
return - ENODEV ;
# endif
2011-06-29 06:28:53 +08:00
if ( wc - > vpmadt032 ) {
ops = & vpm_ec_ops ;
features = & vpm_ec_features ;
* ec = & wchan - > ec ;
( * ec ) - > ops = ops ;
( * ec ) - > features = * features ;
comp = ( DAHDI_LAW_ALAW = = chan - > span - > deflaw ) ?
ADT_COMP_ALAW : ADT_COMP_ULAW ;
return vpmadt032_echocan_create ( wc - > vpmadt032 , wchan - > timeslot ,
comp , ecp , p ) ;
} else if ( wc - > vpmoct ) {
ops = & vpm_ec_ops ;
features = & vpm_ec_features ;
* ec = & wchan - > ec ;
( * ec ) - > ops = ops ;
( * ec ) - > features = * features ;
return vpmoct_echocan_create ( wc - > vpmoct , wchan - > timeslot ,
chan - > span - > deflaw ) ;
} else {
2010-08-28 05:59:27 +08:00
return - ENODEV ;
2011-06-29 06:28:53 +08:00
}
2010-08-28 05:59:27 +08:00
}
static void echocan_free ( struct dahdi_chan * chan , struct dahdi_echocan_state * ec )
{
struct wctdm * wc = chan - > pvt ;
struct wctdm_chan * wchan = container_of ( chan , struct wctdm_chan , chan ) ;
2011-01-31 23:53:26 +08:00
if ( wc - > vpmadt032 ) {
memset ( ec , 0 , sizeof ( * ec ) ) ;
2010-08-28 05:59:27 +08:00
vpmadt032_echocan_free ( wc - > vpmadt032 , wchan - > timeslot , ec ) ;
2011-06-29 06:28:53 +08:00
} else if ( wc - > vpmoct ) {
memset ( ec , 0 , sizeof ( * ec ) ) ;
vpmoct_echocan_free ( wc - > vpmoct , wchan - > timeslot ) ;
2010-08-28 05:59:27 +08:00
}
}
/* 1ms interrupt */
2011-06-03 04:03:15 +08:00
static void wctdm_isr_misc_fxs ( struct wctdm * wc , struct wctdm_module * const mod )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:15 +08:00
struct fxs * const fxs = & mod - > mod . fxs ;
2010-08-28 05:59:27 +08:00
2011-08-31 00:38:14 +08:00
if ( time_after ( wc - > framecount , fxs - > check_alarm ) ) {
2010-08-28 05:59:27 +08:00
/* Accept an alarm once per 10 seconds */
2011-08-31 00:38:14 +08:00
fxs - > check_alarm = wc - > framecount + ( 1000 * 10 ) ;
2010-08-28 05:59:27 +08:00
if ( fxs - > palarms )
fxs - > palarms - - ;
}
2011-08-31 00:38:27 +08:00
if ( fxs - > off_hook & & ! ( fxs - > hook_state_shadow & 1 ) ) {
wctdm_fxs_on_hook ( wc , mod ) ;
fxs - > off_hook = 0 ;
} else if ( ! fxs - > off_hook & & ( fxs - > hook_state_shadow & 1 ) ) {
wctdm_fxs_off_hook ( wc , mod ) ;
fxs - > off_hook = 1 ;
}
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
wctdm_proslic_check_oppending ( wc , mod ) ;
2010-08-28 05:59:27 +08:00
2011-08-31 00:38:14 +08:00
if ( time_after ( wc - > framecount , fxs - > check_proslic ) ) {
fxs - > check_proslic = wc - > framecount + 250 ; /* every 250ms */
2011-06-03 04:03:15 +08:00
wctdm_proslic_recheck_sanity ( wc , mod ) ;
2011-08-31 00:38:14 +08:00
}
2010-08-28 05:59:27 +08:00
if ( SLIC_LF_RINGING = = fxs - > lasttxhook ) {
/* RINGing, prepare for OHT */
2011-08-31 00:38:27 +08:00
fxs - > ohttimer = wc - > framecount + OHT_TIMER ;
2010-08-28 05:59:27 +08:00
/* OHT mode when idle */
2011-06-03 04:03:10 +08:00
fxs - > idletxhookstate = POLARITY_XOR ( fxs ) ? SLIC_LF_OHTRAN_REV :
2010-08-28 05:59:27 +08:00
SLIC_LF_OHTRAN_FWD ;
2011-08-31 00:38:27 +08:00
} else if ( fxs - > oht_active ) {
/* check if still OnHook */
if ( ! fxs - > off_hook ) {
if ( time_before ( wc - > framecount , fxs - > ohttimer ) )
2010-08-28 05:59:27 +08:00
return ;
/* Switch to active */
2011-06-03 04:03:10 +08:00
fxs - > idletxhookstate = POLARITY_XOR ( fxs ) ? SLIC_LF_ACTIVE_REV :
2010-08-28 05:59:27 +08:00
SLIC_LF_ACTIVE_FWD ;
2011-06-03 04:03:43 +08:00
spin_lock ( & wc - > reglock ) ;
2010-08-28 05:59:27 +08:00
if ( SLIC_LF_OHTRAN_FWD = = fxs - > lasttxhook ) {
/* Apply the change if appropriate */
fxs - > lasttxhook = SLIC_LF_OPPENDING | SLIC_LF_ACTIVE_FWD ;
/* Data enqueued here */
2011-06-03 04:03:15 +08:00
mod - > sethook = CMD_WR ( LINE_STATE , fxs - > lasttxhook ) ;
2010-08-28 05:59:27 +08:00
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Channel %d OnHookTransfer "
2011-06-03 04:03:15 +08:00
" stop \n " , mod - > card ) ;
2010-08-28 05:59:27 +08:00
}
} else if ( SLIC_LF_OHTRAN_REV = = fxs - > lasttxhook ) {
/* Apply the change if appropriate */
fxs - > lasttxhook = SLIC_LF_OPPENDING | SLIC_LF_ACTIVE_REV ;
/* Data enqueued here */
2011-06-03 04:03:15 +08:00
mod - > sethook = CMD_WR ( LINE_STATE , fxs - > lasttxhook ) ;
2010-08-28 05:59:27 +08:00
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Channel %d OnHookTransfer "
2011-06-03 04:03:15 +08:00
" stop \n " , mod - > card ) ;
2010-08-28 05:59:27 +08:00
}
}
2011-06-03 04:03:43 +08:00
spin_unlock ( & wc - > reglock ) ;
2010-08-28 05:59:27 +08:00
} else {
2011-08-31 00:38:27 +08:00
fxs - > oht_active = 0 ;
2010-08-28 05:59:27 +08:00
/* Switch to active */
2011-06-03 04:03:10 +08:00
fxs - > idletxhookstate = POLARITY_XOR ( fxs ) ? SLIC_LF_ACTIVE_REV : SLIC_LF_ACTIVE_FWD ;
2010-08-28 05:59:27 +08:00
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Channel %d OnHookTransfer abort \n " ,
2011-06-03 04:03:15 +08:00
mod - > card ) ;
2010-08-28 05:59:27 +08:00
}
}
}
}
/* 1ms interrupt */
static inline void wctdm_isr_misc ( struct wctdm * wc )
{
int x ;
2011-06-29 06:28:53 +08:00
if ( unlikely ( ! is_initialized ( wc ) ) )
2010-08-28 05:59:27 +08:00
return ;
for ( x = 0 ; x < wc - > mods_per_board ; x + + ) {
2011-06-03 04:03:15 +08:00
struct wctdm_module * const mod = & wc - > mods [ x ] ;
2011-06-03 04:02:51 +08:00
2011-06-03 04:02:42 +08:00
spin_lock ( & wc - > reglock ) ;
2011-06-03 04:03:15 +08:00
cmd_checkisr ( wc , mod ) ;
2011-06-03 04:02:42 +08:00
spin_unlock ( & wc - > reglock ) ;
2011-06-03 04:03:15 +08:00
switch ( mod - > type ) {
2011-06-03 04:03:01 +08:00
case FXS :
2011-06-03 04:03:15 +08:00
wctdm_isr_misc_fxs ( wc , mod ) ;
2011-06-03 04:02:42 +08:00
break ;
2011-06-03 04:03:01 +08:00
case FXO :
2011-06-03 04:03:15 +08:00
wctdm_voicedaa_check_hook ( wc , mod ) ;
2011-06-03 04:02:42 +08:00
break ;
2011-06-03 04:03:01 +08:00
case QRV :
2011-06-03 04:02:42 +08:00
wctdm_qrvdri_check_hook ( wc , x ) ;
break ;
2011-06-03 04:03:01 +08:00
default :
break ;
2010-08-28 05:59:27 +08:00
}
}
}
static void handle_receive ( struct voicebus * vb , struct list_head * buffers )
{
struct wctdm * wc = container_of ( vb , struct wctdm , vb ) ;
struct vbb * vbb ;
list_for_each_entry ( vbb , buffers , entry )
wctdm_receiveprep ( wc , vbb - > data ) ;
}
static void handle_transmit ( struct voicebus * vb , struct list_head * buffers )
{
struct wctdm * wc = container_of ( vb , struct wctdm , vb ) ;
struct vbb * vbb ;
list_for_each_entry ( vbb , buffers , entry ) {
memset ( vbb - > data , 0 , sizeof ( vbb - > data ) ) ;
wctdm_transmitprep ( wc , vbb - > data ) ;
wctdm_isr_misc ( wc ) ;
2011-08-31 00:38:14 +08:00
wc - > framecount + + ;
2010-08-28 05:59:27 +08:00
}
}
struct sframe_packet {
struct list_head node ;
u8 sframe [ SFRAME_SIZE ] ;
} ;
/**
* handle_hx8_bootmode_receive ( ) - queue up the receive packet for later . . .
*
* This function is called from interrupt context and isn ' t optimal , but it ' s
* not the main code path .
*/
static void handle_hx8_bootmode_receive ( struct wctdm * wc , const void * vbb )
{
struct sframe_packet * frame ;
frame = kzalloc ( sizeof ( * frame ) , GFP_ATOMIC ) ;
if ( unlikely ( ! frame ) ) {
WARN_ON ( 1 ) ;
return ;
}
memcpy ( frame - > sframe , vbb , sizeof ( frame - > sframe ) ) ;
spin_lock ( & wc - > frame_list_lock ) ;
list_add_tail ( & frame - > node , & wc - > frame_list ) ;
spin_unlock ( & wc - > frame_list_lock ) ;
/* Wake up anyone waiting for a new packet. */
wake_up ( & wc - > regq ) ;
return ;
}
static void handle_hx8_receive ( struct voicebus * vb , struct list_head * buffers )
{
struct wctdm * wc = container_of ( vb , struct wctdm , vb ) ;
struct vbb * vbb ;
list_for_each_entry ( vbb , buffers , entry )
handle_hx8_bootmode_receive ( wc , vbb - > data ) ;
}
static void handle_hx8_transmit ( struct voicebus * vb , struct list_head * buffers )
{
struct vbb * vbb , * n ;
list_for_each_entry_safe ( vbb , n , buffers , entry ) {
list_del ( & vbb - > entry ) ;
2010-09-24 05:38:52 +08:00
dma_pool_free ( vb - > pool , vbb , vbb - > dma_addr ) ;
2010-08-28 05:59:27 +08:00
}
}
2011-06-03 04:03:15 +08:00
static int wctdm_voicedaa_insane ( struct wctdm * wc , struct wctdm_module * mod )
2010-08-28 05:59:27 +08:00
{
2011-06-11 09:58:43 +08:00
int blah ;
blah = wctdm_getreg ( wc , mod , 2 ) ;
if ( blah ! = 0x3 )
2010-08-28 05:59:27 +08:00
return - 2 ;
2011-06-11 09:58:43 +08:00
blah = wctdm_getreg ( wc , mod , 11 ) ;
2011-06-03 04:03:48 +08:00
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
2011-06-11 09:58:43 +08:00
" VoiceDAA System: %02x \n " , blah & 0xf ) ;
2011-06-03 04:03:48 +08:00
}
2010-08-28 05:59:27 +08:00
return 0 ;
}
2011-06-03 04:03:15 +08:00
static int
wctdm_proslic_insane ( struct wctdm * wc , struct wctdm_module * const mod )
2010-08-28 05:59:27 +08:00
{
int blah , reg1 , insane_report ;
insane_report = 0 ;
2011-06-03 04:03:15 +08:00
blah = wctdm_getreg ( wc , mod , 0 ) ;
if ( blah ! = 0xff & & ( debug & DEBUG_CARD ) ) {
dev_info ( & wc - > vb . pdev - > dev ,
" ProSLIC on module %d, product %d, "
" version %d \n " , mod - > card , ( blah & 0x30 ) > > 4 ,
( blah & 0xf ) ) ;
}
2010-08-28 05:59:27 +08:00
#if 0
if ( ( blah & 0x30 ) > > 4 ) {
2011-06-03 04:03:15 +08:00
dev_info ( & wc - > vb . pdev - > dev ,
" ProSLIC on module %d is not a 3210. \n " , mod - > card ) ;
2010-08-28 05:59:27 +08:00
return - 1 ;
}
# endif
if ( ( ( blah & 0xf ) = = 0 ) | | ( ( blah & 0xf ) = = 0xf ) ) {
/* SLIC not loaded */
return - 1 ;
}
/* let's be really sure this is an FXS before we continue */
2011-06-03 04:03:15 +08:00
reg1 = wctdm_getreg ( wc , mod , 1 ) ;
2011-01-31 23:53:26 +08:00
if ( ( 0x80 ! = ( blah & 0xf0 ) ) | | ( 0x88 ! = reg1 ) ) {
2011-06-03 04:03:15 +08:00
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" DEBUG: not FXS b/c reg0=%x or "
" reg1 != 0x88 (%x). \n " , blah , reg1 ) ;
}
2010-08-28 05:59:27 +08:00
return - 1 ;
}
2011-06-03 04:03:15 +08:00
blah = wctdm_getreg ( wc , mod , 8 ) ;
2010-08-28 05:59:27 +08:00
if ( blah ! = 0x2 ) {
2011-06-03 04:03:15 +08:00
dev_notice ( & wc - > vb . pdev - > dev ,
" ProSLIC on module %d insane (1) %d should be 2 \n " ,
mod - > card , blah ) ;
2010-08-28 05:59:27 +08:00
return - 1 ;
2011-06-03 04:03:15 +08:00
} else if ( insane_report ) {
dev_notice ( & wc - > vb . pdev - > dev ,
" ProSLIC on module %d Reg 8 Reads %d Expected "
" is 0x2 \n " , mod - > card , blah ) ;
}
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
blah = wctdm_getreg ( wc , mod , 64 ) ;
2010-08-28 05:59:27 +08:00
if ( blah ! = 0x0 ) {
2011-06-03 04:03:15 +08:00
dev_notice ( & wc - > vb . pdev - > dev ,
" ProSLIC on module %d insane (2) \n " ,
mod - > card ) ;
2010-08-28 05:59:27 +08:00
return - 1 ;
2011-06-03 04:03:15 +08:00
} else if ( insane_report ) {
dev_notice ( & wc - > vb . pdev - > dev ,
" ProSLIC on module %d Reg 64 Reads %d Expected "
" is 0x0 \n " , mod - > card , blah ) ;
}
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
blah = wctdm_getreg ( wc , mod , 11 ) ;
2010-08-28 05:59:27 +08:00
if ( blah ! = 0x33 ) {
2011-06-03 04:03:15 +08:00
dev_notice ( & wc - > vb . pdev - > dev ,
" ProSLIC on module %d insane (3) \n " , mod - > card ) ;
2010-08-28 05:59:27 +08:00
return - 1 ;
2011-06-03 04:03:15 +08:00
} else if ( insane_report ) {
dev_notice ( & wc - > vb . pdev - > dev ,
" ProSLIC on module %d Reg 11 Reads %d "
" Expected is 0x33 \n " , mod - > card , blah ) ;
}
2010-08-28 05:59:27 +08:00
/* Just be sure it's setup right. */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 30 , 0 ) ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" ProSLIC on module %d seems sane. \n " , mod - > card ) ;
}
2010-08-28 05:59:27 +08:00
return 0 ;
}
2011-06-03 04:03:15 +08:00
static int
wctdm_proslic_powerleak_test ( struct wctdm * wc , struct wctdm_module * const mod )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:52 +08:00
unsigned long start ;
2010-08-28 05:59:27 +08:00
unsigned char vbat ;
/* Turn off linefeed */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , LINE_STATE , 0 ) ;
2010-08-28 05:59:27 +08:00
/* Power down */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 14 , 0x10 ) ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:52 +08:00
start = jiffies ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:52 +08:00
/* TODO: Why is this sleep necessary. Without it, the first read
2011-06-03 04:03:38 +08:00
* comes back with a 0 value . */
msleep ( 20 ) ;
2011-06-03 04:03:15 +08:00
while ( ( vbat = wctdm_getreg ( wc , mod , 82 ) ) > 0x6 ) {
2011-06-03 04:03:52 +08:00
if ( time_after ( jiffies , start + HZ / 4 ) )
break ;
2010-08-28 05:59:27 +08:00
}
if ( vbat < 0x06 ) {
2011-06-03 04:03:15 +08:00
dev_notice ( & wc - > vb . pdev - > dev ,
" Excessive leakage detected on module %d: %d "
" volts (%02x) after %d ms \n " , mod - > card ,
376 * vbat / 1000 , vbat ,
2011-06-03 04:03:52 +08:00
( int ) ( ( jiffies - start ) * 1000 / HZ ) ) ;
2010-08-28 05:59:27 +08:00
return - 1 ;
} else if ( debug & DEBUG_CARD ) {
2011-06-03 04:03:15 +08:00
dev_info ( & wc - > vb . pdev - > dev ,
" Post-leakage voltage: %d volts \n " , 376 * vbat / 1000 ) ;
2010-08-28 05:59:27 +08:00
}
return 0 ;
}
2011-06-03 04:03:15 +08:00
static int wctdm_powerup_proslic ( struct wctdm * wc ,
struct wctdm_module * mod , int fast )
2010-08-28 05:59:27 +08:00
{
unsigned char vbat ;
unsigned long origjiffies ;
int lim ;
/* Set period of DC-DC converter to 1/64 khz */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 92 , 0xc0 /* was 0xff */ ) ;
2010-08-28 05:59:27 +08:00
/* Wait for VBat to powerup */
origjiffies = jiffies ;
/* Disable powerdown */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 14 , 0 ) ;
2010-08-28 05:59:27 +08:00
/* If fast, don't bother checking anymore */
if ( fast )
return 0 ;
2011-06-03 04:03:15 +08:00
while ( ( vbat = wctdm_getreg ( wc , mod , 82 ) ) < 0xc0 ) {
2010-08-28 05:59:27 +08:00
/* Wait no more than 500ms */
if ( ( jiffies - origjiffies ) > HZ / 2 ) {
break ;
}
}
if ( vbat < 0xc0 ) {
dev_notice ( & wc - > vb . pdev - > dev , " ProSLIC on module %d failed to powerup within %d ms (%d mV only) \n \n -- DID YOU REMEMBER TO PLUG IN THE HD POWER CABLE TO THE TDM CARD?? \n " ,
2011-06-03 04:03:15 +08:00
mod - > card , ( int ) ( ( ( jiffies - origjiffies ) * 1000 / HZ ) ) ,
2010-08-28 05:59:27 +08:00
vbat * 375 ) ;
return - 1 ;
} else if ( debug & DEBUG_CARD ) {
2011-06-03 04:03:15 +08:00
dev_info ( & wc - > vb . pdev - > dev ,
" ProSLIC on module %d powered up to -%d volts (%02x) "
" in %d ms \n " , mod - > card , vbat * 376 / 1000 , vbat ,
( int ) ( ( ( jiffies - origjiffies ) * 1000 / HZ ) ) ) ;
2010-08-28 05:59:27 +08:00
}
/* Proslic max allowed loop current, reg 71 LOOP_I_LIMIT */
/* If out of range, just set it to the default value */
lim = ( loopcurrent - 20 ) / 3 ;
if ( loopcurrent > 41 ) {
lim = 0 ;
if ( debug & DEBUG_CARD )
dev_info ( & wc - > vb . pdev - > dev , " Loop current out of range! Setting to default 20mA! \n " ) ;
}
else if ( debug & DEBUG_CARD )
dev_info ( & wc - > vb . pdev - > dev , " Loop current set to %dmA! \n " , ( lim * 3 ) + 20 ) ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , LOOP_I_LIMIT , lim ) ;
2010-08-28 05:59:27 +08:00
/* Engage DC-DC converter */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 93 , 0x19 /* was 0x19 */ ) ;
2010-08-28 05:59:27 +08:00
return 0 ;
}
2011-06-03 04:03:15 +08:00
static int
wctdm_proslic_manual_calibrate ( struct wctdm * wc , struct wctdm_module * const mod )
2010-08-28 05:59:27 +08:00
{
unsigned long origjiffies ;
unsigned char i ;
2011-06-03 04:03:15 +08:00
/* Disable all interupts in DR21-23 */
wctdm_setreg ( wc , mod , 21 , 0 ) ;
wctdm_setreg ( wc , mod , 22 , 0 ) ;
wctdm_setreg ( wc , mod , 23 , 0 ) ;
wctdm_setreg ( wc , mod , 64 , 0 ) ;
/* (0x18) Calibrations without the ADC and DAC offset and without
* common mode calibration . */
wctdm_setreg ( wc , mod , 97 , 0x18 ) ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
/* (0x47) Calibrate common mode and differential DAC mode DAC + ILIM */
wctdm_setreg ( wc , mod , 96 , 0x47 ) ;
2010-08-28 05:59:27 +08:00
origjiffies = jiffies ;
2011-06-03 04:03:15 +08:00
while ( wctdm_getreg ( wc , mod , 96 ) ! = 0 ) {
2010-08-28 05:59:27 +08:00
if ( ( jiffies - origjiffies ) > 80 )
return - 1 ;
}
//Initialized DR 98 and 99 to get consistant results.
// 98 and 99 are the results registers and the search should have same intial conditions.
/*******************************The following is the manual gain mismatch calibration****************************/
/*******************************This is also available as a function *******************************************/
msleep ( 10 ) ;
2011-06-03 04:03:15 +08:00
wctdm_proslic_setreg_indirect ( wc , mod , 88 , 0 ) ;
wctdm_proslic_setreg_indirect ( wc , mod , 89 , 0 ) ;
wctdm_proslic_setreg_indirect ( wc , mod , 90 , 0 ) ;
wctdm_proslic_setreg_indirect ( wc , mod , 91 , 0 ) ;
wctdm_proslic_setreg_indirect ( wc , mod , 92 , 0 ) ;
wctdm_proslic_setreg_indirect ( wc , mod , 93 , 0 ) ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
/* This is necessary if the calibration occurs other than at reset */
wctdm_setreg ( wc , mod , 98 , 0x10 ) ;
wctdm_setreg ( wc , mod , 99 , 0x10 ) ;
2010-08-28 05:59:27 +08:00
for ( i = 0x1f ; i > 0 ; i - - )
{
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 98 , i ) ;
2010-08-28 05:59:27 +08:00
msleep ( 40 ) ;
2011-06-03 04:03:15 +08:00
if ( ( wctdm_getreg ( wc , mod , 88 ) ) = = 0 )
2010-08-28 05:59:27 +08:00
break ;
} // for
for ( i = 0x1f ; i > 0 ; i - - )
{
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 99 , i ) ;
2010-08-28 05:59:27 +08:00
msleep ( 40 ) ;
2011-06-03 04:03:15 +08:00
if ( ( wctdm_getreg ( wc , mod , 89 ) ) = = 0 )
2010-08-28 05:59:27 +08:00
break ;
} //for
/*******************************The preceding is the manual gain mismatch calibration****************************/
/**********************************The following is the longitudinal Balance Cal***********************************/
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 64 , 1 ) ;
2010-08-28 05:59:27 +08:00
msleep ( 100 ) ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 64 , 0 ) ;
/* enable interrupt for the balance Cal */
wctdm_setreg ( wc , mod , 23 , 0x4 ) ;
/* this is a singular calibration bit for longitudinal calibration */
wctdm_setreg ( wc , mod , 97 , 0x1 ) ;
wctdm_setreg ( wc , mod , 96 , 0x40 ) ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
wctdm_getreg ( wc , mod , 96 ) ; /* Read Reg 96 just cause */
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 21 , 0xFF ) ;
wctdm_setreg ( wc , mod , 22 , 0xFF ) ;
wctdm_setreg ( wc , mod , 23 , 0xFF ) ;
2010-08-28 05:59:27 +08:00
/**The preceding is the longitudinal Balance Cal***/
return ( 0 ) ;
}
2011-06-03 04:03:15 +08:00
static int
wctdm_proslic_calibrate ( struct wctdm * wc , struct wctdm_module * mod )
2010-08-28 05:59:27 +08:00
{
unsigned long origjiffies ;
int x ;
2011-06-03 04:03:15 +08:00
2010-08-28 05:59:27 +08:00
/* Perform all calibrations */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 97 , 0x1f ) ;
2010-08-28 05:59:27 +08:00
/* Begin, no speedup */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 96 , 0x5f ) ;
2010-08-28 05:59:27 +08:00
/* Wait for it to finish */
origjiffies = jiffies ;
2011-06-03 04:03:15 +08:00
while ( wctdm_getreg ( wc , mod , 96 ) ) {
2010-08-28 05:59:27 +08:00
if ( time_after ( jiffies , ( origjiffies + ( 2 * HZ ) ) ) ) {
2011-06-03 04:03:15 +08:00
dev_notice ( & wc - > vb . pdev - > dev ,
" Timeout waiting for calibration of "
" module %d \n " , mod - > card ) ;
2010-08-28 05:59:27 +08:00
return - 1 ;
}
}
if ( debug & DEBUG_CARD ) {
/* Print calibration parameters */
2011-06-03 04:03:15 +08:00
dev_info ( & wc - > vb . pdev - > dev ,
" Calibration Vector Regs 98 - 107: \n " ) ;
2010-08-28 05:59:27 +08:00
for ( x = 98 ; x < 108 ; x + + ) {
2011-06-03 04:03:15 +08:00
dev_info ( & wc - > vb . pdev - > dev ,
" %d: %02x \n " , x , wctdm_getreg ( wc , mod , x ) ) ;
2010-08-28 05:59:27 +08:00
}
}
return 0 ;
}
/*********************************************************************
* Set the hwgain on the analog modules
*
* card = the card position for this module ( 0 - 23 )
* gain = gain in dB x10 ( e . g . - 3.5 dB would be gain = - 35 )
* tx = ( 0 for rx ; 1 for tx )
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-06-03 04:03:15 +08:00
static int
wctdm_set_hwgain ( struct wctdm * wc , struct wctdm_module * mod ,
__s32 gain , __u32 tx )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:15 +08:00
if ( mod - > type ! = FXO ) {
dev_notice ( & wc - > vb . pdev - > dev ,
" Cannot adjust gain. Unsupported module type! \n " ) ;
2010-08-28 05:59:27 +08:00
return - 1 ;
}
2011-06-03 04:03:15 +08:00
2010-08-28 05:59:27 +08:00
if ( tx ) {
2011-06-03 04:03:15 +08:00
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" setting FXO tx gain for card=%d to %d \n " ,
mod - > card , gain ) ;
}
2010-08-28 05:59:27 +08:00
if ( gain > = - 150 & & gain < = 0 ) {
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 38 , 16 + ( gain / - 10 ) ) ;
wctdm_setreg ( wc , mod , 40 , 16 + ( - gain % 10 ) ) ;
2010-08-28 05:59:27 +08:00
} else if ( gain < = 120 & & gain > 0 ) {
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 38 , gain / 10 ) ;
wctdm_setreg ( wc , mod , 40 , ( gain % 10 ) ) ;
2010-08-28 05:59:27 +08:00
} else {
2011-06-03 04:03:15 +08:00
dev_notice ( & wc - > vb . pdev - > dev ,
" FXO tx gain is out of range (%d) \n " , gain ) ;
2010-08-28 05:59:27 +08:00
return - 1 ;
}
} else { /* rx */
2011-06-03 04:03:15 +08:00
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" setting FXO rx gain for card=%d to %d \n " ,
mod - > card , gain ) ;
}
2010-08-28 05:59:27 +08:00
if ( gain > = - 150 & & gain < = 0 ) {
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 39 , 16 + ( gain / - 10 ) ) ;
wctdm_setreg ( wc , mod , 41 , 16 + ( - gain % 10 ) ) ;
2010-08-28 05:59:27 +08:00
} else if ( gain < = 120 & & gain > 0 ) {
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 39 , gain / 10 ) ;
wctdm_setreg ( wc , mod , 41 , ( gain % 10 ) ) ;
2010-08-28 05:59:27 +08:00
} else {
2011-06-03 04:03:15 +08:00
dev_notice ( & wc - > vb . pdev - > dev ,
" FXO rx gain is out of range (%d) \n " , gain ) ;
2010-08-28 05:59:27 +08:00
return - 1 ;
}
}
return 0 ;
}
2011-06-03 04:03:43 +08:00
static int set_lasttxhook_interruptible ( struct wctdm * wc , struct fxs * fxs ,
unsigned newval , int * psethook )
2010-08-28 05:59:27 +08:00
{
int res = 0 ;
unsigned long flags ;
int timeout = 0 ;
do {
2011-06-03 04:03:43 +08:00
spin_lock_irqsave ( & wc - > reglock , flags ) ;
2010-08-28 05:59:27 +08:00
if ( SLIC_LF_OPPENDING & fxs - > lasttxhook ) {
2011-06-03 04:03:43 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
2010-08-28 05:59:27 +08:00
if ( timeout + + > 100 )
return - 1 ;
msleep ( 1 ) ;
} else {
fxs - > lasttxhook = ( newval & SLIC_LF_SETMASK ) | SLIC_LF_OPPENDING ;
* psethook = CMD_WR ( LINE_STATE , fxs - > lasttxhook ) ;
2011-06-03 04:03:43 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
2010-08-28 05:59:27 +08:00
break ;
}
} while ( 1 ) ;
return res ;
}
/* Must be called from within an interruptible context */
2011-06-03 04:03:15 +08:00
static int set_vmwi ( struct wctdm * wc , struct wctdm_module * const mod )
2010-08-28 05:59:27 +08:00
{
int x ;
2011-06-03 04:03:15 +08:00
struct fxs * const fxs = & mod - > mod . fxs ;
2010-08-28 05:59:27 +08:00
/* Presently only supports line reversal MWI */
if ( ( fxs - > vmwi_active_messages ) & &
( fxs - > vmwisetting . vmwi_type & DAHDI_VMWI_LREV ) )
fxs - > vmwi_linereverse = 1 ;
else
fxs - > vmwi_linereverse = 0 ;
/* Set line polarity for new VMWI state */
2011-06-03 04:03:10 +08:00
if ( POLARITY_XOR ( fxs ) ) {
2011-03-19 14:09:02 +08:00
fxs - > idletxhookstate | = SLIC_LF_REVMASK ;
2010-08-28 05:59:27 +08:00
/* Do not set while currently ringing or open */
if ( ( ( fxs - > lasttxhook & SLIC_LF_SETMASK ) ! = SLIC_LF_RINGING ) & &
( ( fxs - > lasttxhook & SLIC_LF_SETMASK ) ! = SLIC_LF_OPEN ) ) {
x = fxs - > lasttxhook ;
x | = SLIC_LF_REVMASK ;
2011-06-03 04:03:43 +08:00
set_lasttxhook_interruptible ( wc , fxs , x , & mod - > sethook ) ;
2010-08-28 05:59:27 +08:00
}
} else {
fxs - > idletxhookstate & = ~ SLIC_LF_REVMASK ;
/* Do not set while currently ringing or open */
if ( ( ( fxs - > lasttxhook & SLIC_LF_SETMASK ) ! = SLIC_LF_RINGING ) & &
( ( fxs - > lasttxhook & SLIC_LF_SETMASK ) ! = SLIC_LF_OPEN ) ) {
x = fxs - > lasttxhook ;
x & = ~ SLIC_LF_REVMASK ;
2011-06-03 04:03:43 +08:00
set_lasttxhook_interruptible ( wc , fxs , x , & mod - > sethook ) ;
2010-08-28 05:59:27 +08:00
}
}
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Setting VMWI on channel %d, messages=%d, lrev=%d \n " ,
2011-06-03 04:03:15 +08:00
mod - > card , fxs - > vmwi_active_messages ,
2010-08-28 05:59:27 +08:00
fxs - > vmwi_linereverse ) ;
}
return 0 ;
}
2011-06-03 04:03:15 +08:00
static void
wctdm_voicedaa_set_ts ( struct wctdm * wc , struct wctdm_module * mod , int ts )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 34 , ( ts * 8 ) & 0xff ) ;
wctdm_setreg ( wc , mod , 35 , ( ts * 8 ) > > 8 ) ;
wctdm_setreg ( wc , mod , 36 , ( ts * 8 ) & 0xff ) ;
wctdm_setreg ( wc , mod , 37 , ( ts * 8 ) > > 8 ) ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" voicedaa: card %d new timeslot: %d \n " ,
mod - > card + 1 , ts ) ;
}
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:15 +08:00
static int
wctdm_init_voicedaa ( struct wctdm * wc , struct wctdm_module * mod ,
int fast , int manual , int sane )
2010-08-28 05:59:27 +08:00
{
unsigned char reg16 = 0 , reg26 = 0 , reg30 = 0 , reg31 = 0 ;
unsigned long flags ;
long newjiffies ;
2011-06-03 04:03:15 +08:00
#if 0 /* TODO */
2011-06-03 04:03:01 +08:00
if ( ( wc - > mods [ card & 0xfc ] . type = = QRV ) | |
( wc - > mods [ card & 0xfc ] . type = = BRI ) )
2010-08-28 05:59:27 +08:00
return - 2 ;
2011-06-03 04:03:15 +08:00
# endif
2010-08-28 05:59:27 +08:00
spin_lock_irqsave ( & wc - > reglock , flags ) ;
2011-06-03 04:03:15 +08:00
mod - > type = NONE ;
2010-08-28 05:59:27 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
2011-06-03 04:03:52 +08:00
msleep ( 20 ) ;
2010-08-28 05:59:27 +08:00
spin_lock_irqsave ( & wc - > reglock , flags ) ;
2011-06-03 04:03:15 +08:00
mod - > type = FXO ;
2010-08-28 05:59:27 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
2011-06-03 04:03:52 +08:00
msleep ( 20 ) ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
if ( ! sane & & wctdm_voicedaa_insane ( wc , mod ) )
2010-08-28 05:59:27 +08:00
return - 2 ;
/* Software reset */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 1 , 0x80 ) ;
2010-11-05 00:41:02 +08:00
msleep ( 100 ) ;
2010-08-28 05:59:27 +08:00
/* Set On-hook speed, Ringer impedence, and ringer threshold */
reg16 | = ( fxo_modes [ _opermode ] . ohs < < 6 ) ;
reg16 | = ( fxo_modes [ _opermode ] . rz < < 1 ) ;
reg16 | = ( fxo_modes [ _opermode ] . rt ) ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 16 , reg16 ) ;
2010-08-28 05:59:27 +08:00
/* Enable ring detector full-wave rectifier mode */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 18 , 2 ) ;
wctdm_setreg ( wc , mod , 24 , 0 ) ;
2010-08-28 05:59:27 +08:00
/* Set DC Termination:
Tip / Ring voltage adjust , minimum operational current , current limitation */
reg26 | = ( fxo_modes [ _opermode ] . dcv < < 6 ) ;
reg26 | = ( fxo_modes [ _opermode ] . mini < < 4 ) ;
reg26 | = ( fxo_modes [ _opermode ] . ilim < < 1 ) ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 26 , reg26 ) ;
2010-08-28 05:59:27 +08:00
/* Set AC Impedence */
reg30 = ( fxo_modes [ _opermode ] . acim ) ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 30 , reg30 ) ;
2010-08-28 05:59:27 +08:00
/* Misc. DAA parameters */
2011-08-20 06:40:41 +08:00
/* If fast pickup is set, then the off hook counter will be set to 8
* ms , otherwise 128 ms . */
reg31 = ( fastpickup ) ? 0xe3 : 0xa3 ;
2011-06-30 03:53:52 +08:00
2010-08-28 05:59:27 +08:00
reg31 | = ( fxo_modes [ _opermode ] . ohs2 < < 3 ) ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 31 , reg31 ) ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
wctdm_voicedaa_set_ts ( wc , mod , mod - > card ) ;
2010-08-28 05:59:27 +08:00
/* Enable ISO-Cap */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 6 , 0x00 ) ;
2010-08-28 05:59:27 +08:00
2011-08-20 06:40:41 +08:00
/* Turn off the calibration delay when fastpickup is enabled. */
if ( fastpickup )
wctdm_setreg ( wc , mod , 17 , wctdm_getreg ( wc , mod , 17 ) | 0x20 ) ;
2010-08-28 05:59:27 +08:00
/* Wait 1000ms for ISO-cap to come up */
newjiffies = jiffies ;
newjiffies + = 2 * HZ ;
2011-06-03 04:03:15 +08:00
while ( ( jiffies < newjiffies ) & & ! ( wctdm_getreg ( wc , mod , 11 ) & 0xf0 ) )
2010-11-05 00:41:02 +08:00
msleep ( 100 ) ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
if ( ! ( wctdm_getreg ( wc , mod , 11 ) & 0xf0 ) ) {
2010-08-28 05:59:27 +08:00
dev_notice ( & wc - > vb . pdev - > dev , " VoiceDAA did not bring up ISO link properly! \n " ) ;
return - 1 ;
}
2011-06-03 04:03:15 +08:00
if ( debug & DEBUG_CARD ) {
2010-08-28 05:59:27 +08:00
dev_info ( & wc - > vb . pdev - > dev , " ISO-Cap is now up, line side: %02x rev %02x \n " ,
2011-06-03 04:03:15 +08:00
wctdm_getreg ( wc , mod , 11 ) > > 4 ,
( wctdm_getreg ( wc , mod , 13 ) > > 2 ) & 0xf ) ;
}
2010-08-28 05:59:27 +08:00
/* Enable on-hook line monitor */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 5 , 0x08 ) ;
2010-08-28 05:59:27 +08:00
/* Take values for fxotxgain and fxorxgain and apply them to module */
2011-06-03 04:03:15 +08:00
wctdm_set_hwgain ( wc , mod , fxotxgain , 1 ) ;
wctdm_set_hwgain ( wc , mod , fxorxgain , 0 ) ;
2010-08-28 05:59:27 +08:00
2010-09-21 23:36:34 +08:00
# ifdef DEBUG
if ( digitalloopback ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Turning on digital loopback for port %d. \n " ,
2011-06-03 04:03:15 +08:00
mod - > card + 1 ) ;
wctdm_setreg ( wc , mod , 10 , 0x01 ) ;
2010-09-21 23:36:34 +08:00
}
# endif
2011-06-03 04:03:15 +08:00
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" DEBUG fxotxgain:%i.%i fxorxgain:%i.%i \n " ,
( wctdm_getreg ( wc , mod , 38 ) / 16 ) ?
- ( wctdm_getreg ( wc , mod , 38 ) - 16 ) :
wctdm_getreg ( wc , mod , 38 ) ,
( wctdm_getreg ( wc , mod , 40 ) / 16 ) ?
- ( wctdm_getreg ( wc , mod , 40 ) - 16 ) :
wctdm_getreg ( wc , mod , 40 ) ,
( wctdm_getreg ( wc , mod , 39 ) / 16 ) ?
- ( wctdm_getreg ( wc , mod , 39 ) - 16 ) :
wctdm_getreg ( wc , mod , 39 ) ,
( wctdm_getreg ( wc , mod , 41 ) / 16 ) ?
- ( wctdm_getreg ( wc , mod , 41 ) - 16 ) :
wctdm_getreg ( wc , mod , 41 ) ) ;
}
2010-08-28 05:59:27 +08:00
return 0 ;
}
2011-06-03 04:03:15 +08:00
static void
wctdm_proslic_set_ts ( struct wctdm * wc , struct wctdm_module * mod , int ts )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 2 , ( ts * 8 ) & 0xff ) ; /* Tx Start low byte 0 */
wctdm_setreg ( wc , mod , 3 , ( ts * 8 ) > > 8 ) ; /* Tx Start high byte 0 */
wctdm_setreg ( wc , mod , 4 , ( ts * 8 ) & 0xff ) ; /* Rx Start low byte 0 */
wctdm_setreg ( wc , mod , 5 , ( ts * 8 ) > > 8 ) ; /* Rx Start high byte 0 */
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" proslic: card %d new timeslot: %d \n " ,
mod - > card + 1 , ts ) ;
}
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:15 +08:00
static int
wctdm_init_proslic ( struct wctdm * wc , struct wctdm_module * const mod ,
int fast , int manual , int sane )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:02:51 +08:00
struct fxs * const fxs = & mod - > mod . fxs ;
2010-08-28 05:59:27 +08:00
unsigned short tmp [ 5 ] ;
unsigned long flags ;
unsigned char r19 , r9 ;
int x ;
int fxsmode = 0 ;
2011-06-03 04:03:48 +08:00
int addresses [ NUM_CAL_REGS ] ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
#if 0 /* TODO */
if ( wc - > mods [ mod - > card & 0xfc ] . type = = QRV )
2011-06-03 04:02:51 +08:00
return - 2 ;
2011-06-03 04:03:15 +08:00
# endif
2010-08-28 05:59:27 +08:00
spin_lock_irqsave ( & wc - > reglock , flags ) ;
2011-06-03 04:03:01 +08:00
mod - > type = FXS ;
2010-08-28 05:59:27 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
2011-06-03 04:03:52 +08:00
/* msleep(100); */
2010-08-28 05:59:27 +08:00
/* Sanity check the ProSLIC */
2011-06-03 04:03:15 +08:00
if ( ! sane & & wctdm_proslic_insane ( wc , mod ) )
2010-08-28 05:59:27 +08:00
return - 2 ;
/* Initialize VMWI settings */
2011-06-03 04:02:51 +08:00
memset ( & ( fxs - > vmwisetting ) , 0 , sizeof ( fxs - > vmwisetting ) ) ;
fxs - > vmwi_linereverse = 0 ;
2010-08-28 05:59:27 +08:00
/* By default, don't send on hook */
2011-06-03 04:02:51 +08:00
if ( ! reversepolarity ! = ! fxs - > reversepolarity )
fxs - > idletxhookstate = SLIC_LF_ACTIVE_REV ;
else
fxs - > idletxhookstate = SLIC_LF_ACTIVE_FWD ;
2010-08-28 05:59:27 +08:00
if ( sane ) {
/* Make sure we turn off the DC->DC converter to prevent anything from blowing up */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 14 , 0x10 ) ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:15 +08:00
if ( wctdm_proslic_init_indirect_regs ( wc , mod ) ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Indirect Registers failed to initialize on "
" module %d. \n " , mod - > card ) ;
2010-08-28 05:59:27 +08:00
return - 1 ;
}
/* Clear scratch pad area */
2011-06-03 04:03:15 +08:00
wctdm_proslic_setreg_indirect ( wc , mod , 97 , 0 ) ;
2010-08-28 05:59:27 +08:00
/* Clear digital loopback */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 8 , 0 ) ;
2010-08-28 05:59:27 +08:00
/* Revision C optimization */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 108 , 0xeb ) ;
2010-08-28 05:59:27 +08:00
/* Disable automatic VBat switching for safety to prevent
2011-06-03 04:03:15 +08:00
* Q7 from accidently turning on and burning out .
* If pulse dialing has trouble at high REN loads change this to 0x17 */
wctdm_setreg ( wc , mod , 67 , 0x07 ) ;
2010-08-28 05:59:27 +08:00
/* Turn off Q7 */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 66 , 1 ) ;
2010-08-28 05:59:27 +08:00
/* Flush ProSLIC digital filters by setting to clear, while
saving old values */
for ( x = 0 ; x < 5 ; x + + ) {
2011-06-03 04:03:15 +08:00
tmp [ x ] = wctdm_proslic_getreg_indirect ( wc , mod , x + 35 ) ;
wctdm_proslic_setreg_indirect ( wc , mod , x + 35 , 0x8000 ) ;
2010-08-28 05:59:27 +08:00
}
/* Power up the DC-DC converter */
2011-06-03 04:03:15 +08:00
if ( wctdm_powerup_proslic ( wc , mod , fast ) ) {
dev_notice ( & wc - > vb . pdev - > dev ,
" Unable to do INITIAL ProSLIC powerup on "
" module %d \n " , mod - > card ) ;
2010-08-28 05:59:27 +08:00
return - 1 ;
}
if ( ! fast ) {
/* Check for power leaks */
2011-06-03 04:03:15 +08:00
if ( wctdm_proslic_powerleak_test ( wc , mod ) ) {
dev_notice ( & wc - > vb . pdev - > dev ,
" ProSLIC module %d failed leakage test. "
" Check for short circuit \n " , mod - > card ) ;
2010-08-28 05:59:27 +08:00
}
/* Power up again */
2011-06-03 04:03:15 +08:00
if ( wctdm_powerup_proslic ( wc , mod , fast ) ) {
dev_notice ( & wc - > vb . pdev - > dev ,
" Unable to do FINAL ProSLIC powerup on "
" module %d \n " , mod - > card ) ;
2010-08-28 05:59:27 +08:00
return - 1 ;
}
# ifndef NO_CALIBRATION
/* Perform calibration */
if ( manual ) {
2011-06-03 04:03:15 +08:00
if ( wctdm_proslic_manual_calibrate ( wc , mod ) ) {
2010-08-28 05:59:27 +08:00
//dev_notice(&wc->vb.pdev->dev, "Proslic failed on Manual Calibration\n");
2011-06-03 04:03:15 +08:00
if ( wctdm_proslic_manual_calibrate ( wc , mod ) ) {
2010-08-28 05:59:27 +08:00
dev_notice ( & wc - > vb . pdev - > dev , " Proslic Failed on Second Attempt to Calibrate Manually. (Try -DNO_CALIBRATION in Makefile) \n " ) ;
return - 1 ;
}
dev_info ( & wc - > vb . pdev - > dev , " Proslic Passed Manual Calibration on Second Attempt \n " ) ;
}
}
else {
2011-06-03 04:03:15 +08:00
if ( wctdm_proslic_calibrate ( wc , mod ) ) {
2010-08-28 05:59:27 +08:00
//dev_notice(&wc->vb.pdev->dev, "ProSlic died on Auto Calibration.\n");
2011-06-03 04:03:15 +08:00
if ( wctdm_proslic_calibrate ( wc , mod ) ) {
2010-08-28 05:59:27 +08:00
dev_notice ( & wc - > vb . pdev - > dev , " Proslic Failed on Second Attempt to Auto Calibrate \n " ) ;
return - 1 ;
}
dev_info ( & wc - > vb . pdev - > dev , " Proslic Passed Auto Calibration on Second Attempt \n " ) ;
}
}
/* Perform DC-DC calibration */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 93 , 0x99 ) ;
r19 = wctdm_getreg ( wc , mod , 107 ) ;
2010-08-28 05:59:27 +08:00
if ( ( r19 < 0x2 ) | | ( r19 > 0xd ) ) {
dev_notice ( & wc - > vb . pdev - > dev , " DC-DC cal has a surprising direct 107 of 0x%02x! \n " , r19 ) ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 107 , 0x8 ) ;
2010-08-28 05:59:27 +08:00
}
/* Save calibration vectors */
2011-06-03 04:02:51 +08:00
for ( x = 0 ; x < NUM_CAL_REGS ; x + + )
2011-06-03 04:03:48 +08:00
addresses [ x ] = 96 + x ;
wctdm_getregs ( wc , mod , addresses , ARRAY_SIZE ( addresses ) ) ;
for ( x = 0 ; x < NUM_CAL_REGS ; x + + )
fxs - > calregs . vals [ x ] = addresses [ x ] ;
2010-08-28 05:59:27 +08:00
# endif
} else {
/* Restore calibration registers */
2011-06-03 04:02:51 +08:00
for ( x = 0 ; x < NUM_CAL_REGS ; x + + )
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 96 + x , fxs - > calregs . vals [ x ] ) ;
2010-08-28 05:59:27 +08:00
}
/* Calibration complete, restore original values */
for ( x = 0 ; x < 5 ; x + + ) {
2011-06-03 04:03:15 +08:00
wctdm_proslic_setreg_indirect ( wc , mod , x + 35 , tmp [ x ] ) ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:15 +08:00
if ( wctdm_proslic_verify_indirect_regs ( wc , mod ) ) {
2010-08-28 05:59:27 +08:00
dev_info ( & wc - > vb . pdev - > dev , " Indirect Registers failed verification. \n " ) ;
return - 1 ;
}
#if 0
/* Disable Auto Power Alarm Detect and other "features" */
wctdm_setreg ( wc , card , 67 , 0x0e ) ;
blah = wctdm_getreg ( wc , card , 67 ) ;
# endif
#if 0
if ( wctdm_proslic_setreg_indirect ( wc , card , 97 , 0x0 ) ) { // Stanley: for the bad recording fix
dev_info ( & wc - > vb . pdev - > dev , " ProSlic IndirectReg Died. \n " ) ;
return - 1 ;
}
# endif
/* U-Law 8-bit interface */
2011-06-03 04:03:15 +08:00
wctdm_proslic_set_ts ( wc , mod , mod - > card ) ;
wctdm_setreg ( wc , mod , 18 , 0xff ) ; /* clear all interrupt */
wctdm_setreg ( wc , mod , 19 , 0xff ) ;
wctdm_setreg ( wc , mod , 20 , 0xff ) ;
wctdm_setreg ( wc , mod , 22 , 0xff ) ;
wctdm_setreg ( wc , mod , 73 , 0x04 ) ;
2010-08-28 05:59:27 +08:00
2011-08-31 00:38:27 +08:00
wctdm_setreg ( wc , mod , 69 , 0x4 ) ;
2010-08-28 05:59:27 +08:00
if ( fxshonormode ) {
static const int ACIM2TISS [ 16 ] = { 0x0 , 0x1 , 0x4 , 0x5 , 0x7 ,
0x0 , 0x0 , 0x6 , 0x0 , 0x0 ,
0x0 , 0x2 , 0x0 , 0x3 } ;
fxsmode = ACIM2TISS [ fxo_modes [ _opermode ] . acim ] ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 10 , 0x08 | fxsmode ) ;
if ( fxo_modes [ _opermode ] . ring_osc ) {
wctdm_proslic_setreg_indirect ( wc , mod , 20 ,
fxo_modes [ _opermode ] . ring_osc ) ;
}
if ( fxo_modes [ _opermode ] . ring_x ) {
wctdm_proslic_setreg_indirect ( wc , mod , 21 ,
fxo_modes [ _opermode ] . ring_x ) ;
}
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:15 +08:00
if ( lowpower )
wctdm_setreg ( wc , mod , 72 , 0x10 ) ;
2010-08-28 05:59:27 +08:00
#if 0
wctdm_setreg ( wc , card , 21 , 0x00 ) ; // enable interrupt
wctdm_setreg ( wc , card , 22 , 0x02 ) ; // Loop detection interrupt
wctdm_setreg ( wc , card , 23 , 0x01 ) ; // DTMF detection interrupt
# endif
#if 0
/* Enable loopback */
wctdm_setreg ( wc , card , 8 , 0x2 ) ;
wctdm_setreg ( wc , card , 14 , 0x0 ) ;
wctdm_setreg ( wc , card , 64 , 0x0 ) ;
wctdm_setreg ( wc , card , 1 , 0x08 ) ;
# endif
if ( fastringer ) {
/* Speed up Ringer */
2011-06-03 04:03:15 +08:00
wctdm_proslic_setreg_indirect ( wc , mod , 20 , 0x7e6d ) ;
wctdm_proslic_setreg_indirect ( wc , mod , 21 , 0x01b9 ) ;
2010-08-28 05:59:27 +08:00
/* Beef up Ringing voltage to 89V */
if ( boostringer ) {
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 74 , 0x3f ) ;
if ( wctdm_proslic_setreg_indirect ( wc , mod , 21 , 0x247 ) )
2010-08-28 05:59:27 +08:00
return - 1 ;
2011-06-03 04:03:15 +08:00
dev_info ( & wc - > vb . pdev - > dev ,
" Boosting fast ringer on slot %d (89V peak) \n " ,
mod - > card + 1 ) ;
2010-08-28 05:59:27 +08:00
} else if ( lowpower ) {
2011-06-03 04:03:15 +08:00
if ( wctdm_proslic_setreg_indirect ( wc , mod , 21 , 0x14b ) )
2010-08-28 05:59:27 +08:00
return - 1 ;
2011-06-03 04:03:15 +08:00
dev_info ( & wc - > vb . pdev - > dev ,
" Reducing fast ring power on slot %d "
" (50V peak) \n " , mod - > card + 1 ) ;
2010-08-28 05:59:27 +08:00
} else
2011-06-03 04:03:15 +08:00
dev_info ( & wc - > vb . pdev - > dev ,
" Speeding up ringer on slot %d (25Hz) \n " ,
mod - > card + 1 ) ;
2010-08-28 05:59:27 +08:00
} else {
/* Beef up Ringing voltage to 89V */
if ( boostringer ) {
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 74 , 0x3f ) ;
if ( wctdm_proslic_setreg_indirect ( wc , mod , 21 , 0x1d1 ) )
2010-08-28 05:59:27 +08:00
return - 1 ;
2011-06-03 04:03:15 +08:00
dev_info ( & wc - > vb . pdev - > dev ,
" Boosting ringer on slot %d (89V peak) \n " ,
mod - > card + 1 ) ;
2010-08-28 05:59:27 +08:00
} else if ( lowpower ) {
2011-06-03 04:03:15 +08:00
if ( wctdm_proslic_setreg_indirect ( wc , mod , 21 , 0x108 ) )
2010-08-28 05:59:27 +08:00
return - 1 ;
2011-06-03 04:03:15 +08:00
dev_info ( & wc - > vb . pdev - > dev ,
" Reducing ring power on slot %d "
" (50V peak) \n " , mod - > card + 1 ) ;
2010-08-28 05:59:27 +08:00
}
}
if ( fxstxgain | | fxsrxgain ) {
2011-06-03 04:03:15 +08:00
r9 = wctdm_getreg ( wc , mod , 9 ) ;
2010-08-28 05:59:27 +08:00
switch ( fxstxgain ) {
case 35 :
r9 + = 8 ;
break ;
case - 35 :
r9 + = 4 ;
break ;
case 0 :
break ;
}
switch ( fxsrxgain ) {
case 35 :
r9 + = 2 ;
break ;
case - 35 :
r9 + = 1 ;
break ;
case 0 :
break ;
}
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 9 , r9 ) ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:15 +08:00
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" DEBUG: fxstxgain:%s fxsrxgain:%s \n " ,
( ( wctdm_getreg ( wc , mod , 9 ) / 8 ) = = 1 ) ?
" 3.5 " : ( ( ( wctdm_getreg ( wc , mod , 9 ) / 4 ) = = 1 ) ?
" -3.5 " : " 0.0 " ) ,
( ( wctdm_getreg ( wc , mod , 9 ) / 2 ) = = 1 ) ?
" 3.5 " : ( ( wctdm_getreg ( wc , mod , 9 ) % 2 ) ?
" -3.5 " : " 0.0 " ) ) ;
}
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
fxs - > lasttxhook = fxs - > idletxhookstate ;
wctdm_setreg ( wc , mod , LINE_STATE , fxs - > lasttxhook ) ;
2011-06-03 04:02:42 +08:00
2011-08-31 00:38:19 +08:00
/* Preset the shadow register so that we won't get a power alarm when
* we finish initialization , otherwise the line state register may not
* have been read yet . */
fxs - > linefeed_control_shadow = fxs - > lasttxhook ;
2010-08-28 05:59:27 +08:00
return 0 ;
}
2011-06-03 04:03:15 +08:00
static void
wctdm_qrvdri_set_ts ( struct wctdm * wc , struct wctdm_module * mod , int ts )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 0x13 , ts + 0x80 ) ; /* codec 2 tx, ts0 */
wctdm_setreg ( wc , mod , 0x17 , ts + 0x80 ) ; /* codec 0 rx, ts0 */
wctdm_setreg ( wc , mod , 0x14 , ts + 0x81 ) ; /* codec 1 tx, ts1 */
wctdm_setreg ( wc , mod , 0x18 , ts + 0x81 ) ; /* codec 1 rx, ts1 */
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" qrvdri: card %d new timeslot: %d \n " ,
mod - > card + 1 , ts ) ;
}
2010-08-28 05:59:27 +08:00
}
static int wctdm_init_qrvdri ( struct wctdm * wc , int card )
{
2011-06-03 04:03:15 +08:00
struct wctdm_module * const mod = & wc - > mods [ card ] ;
2010-08-28 05:59:27 +08:00
unsigned char x , y ;
2011-06-03 04:03:01 +08:00
if ( BRI = = wc - > mods [ card & 0xfc ] . type )
2010-08-28 05:59:27 +08:00
return - 2 ;
/* have to set this, at least for now */
2011-06-03 04:03:15 +08:00
mod - > type = QRV ;
if ( ! ( card & 3 ) ) { /* if at base of card, reset and write it */
struct qrv * const qrv = & mod - > mod . qrv ;
struct qrv * const qrv1 = & wc - > mods [ card + 1 ] . mod . qrv ;
struct qrv * const qrv2 = & wc - > mods [ card + 2 ] . mod . qrv ;
struct qrv * const qrv3 = & wc - > mods [ card + 3 ] . mod . qrv ;
wctdm_setreg ( wc , mod , 0 , 0x80 ) ;
wctdm_setreg ( wc , mod , 0 , 0x55 ) ;
wctdm_setreg ( wc , mod , 1 , 0x69 ) ;
qrv - > hook = qrv1 - > hook = 0 ;
qrv2 - > hook = qrv3 - > hook = 0xff ;
qrv - > debouncetime = qrv1 - > debouncetime = QRV_DEBOUNCETIME ;
qrv - > debtime = qrv1 - > debtime = 0 ;
qrv - > radmode = qrv1 - > radmode = 0 ;
qrv - > txgain = qrv1 - > txgain = 3599 ;
qrv - > rxgain = qrv1 - > rxgain = 1199 ;
2010-08-28 05:59:27 +08:00
} else { /* channel is on same card as base, no need to test */
2011-06-03 04:03:01 +08:00
if ( wc - > mods [ card & 0x7c ] . type = = QRV ) {
2010-08-28 05:59:27 +08:00
/* only lower 2 are valid */
2011-06-03 04:03:01 +08:00
if ( ! ( card & 2 ) )
return 0 ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:15 +08:00
mod - > type = NONE ;
2010-08-28 05:59:27 +08:00
return 1 ;
}
2011-06-03 04:03:15 +08:00
x = wctdm_getreg ( wc , mod , 0 ) ;
y = wctdm_getreg ( wc , mod , 1 ) ;
2010-08-28 05:59:27 +08:00
/* if not a QRV card, return as such */
if ( ( x ! = 0x55 ) | | ( y ! = 0x69 ) )
{
2011-06-03 04:03:15 +08:00
mod - > type = NONE ;
2010-08-28 05:59:27 +08:00
return 1 ;
}
2011-06-03 04:03:15 +08:00
for ( x = 0 ; x < 0x30 ; x + + ) {
if ( ( x > = 0x1c ) & & ( x < = 0x1e ) )
wctdm_setreg ( wc , mod , x , 0xff ) ;
else
wctdm_setreg ( wc , mod , x , 0 ) ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 0 , 0x80 ) ;
2010-08-28 05:59:27 +08:00
msleep ( 100 ) ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 0 , 0x10 ) ;
wctdm_setreg ( wc , mod , 0 , 0x10 ) ;
2010-08-28 05:59:27 +08:00
msleep ( 100 ) ;
/* set up modes */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 0 , 0x1c ) ;
2010-08-28 05:59:27 +08:00
/* set up I/O directions */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 1 , 0x33 ) ;
wctdm_setreg ( wc , mod , 2 , 0x0f ) ;
wctdm_setreg ( wc , mod , 5 , 0x0f ) ;
2010-08-28 05:59:27 +08:00
/* set up I/O to quiescent state */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 3 , 0x11 ) ; /* D0-7 */
wctdm_setreg ( wc , mod , 4 , 0xa ) ; /* D8-11 */
wctdm_setreg ( wc , mod , 7 , 0 ) ; /* CS outputs */
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
wctdm_qrvdri_set_ts ( wc , mod , card ) ;
2010-08-28 05:59:27 +08:00
/* set up for max gains */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 0x26 , 0x24 ) ;
wctdm_setreg ( wc , mod , 0x27 , 0x24 ) ;
wctdm_setreg ( wc , mod , 0x0b , 0x01 ) ; /* "Transmit" gain codec 0 */
wctdm_setreg ( wc , mod , 0x0c , 0x01 ) ; /* "Transmit" gain codec 1 */
wctdm_setreg ( wc , mod , 0x0f , 0xff ) ; /* "Receive" gain codec 0 */
wctdm_setreg ( wc , mod , 0x10 , 0xff ) ; /* "Receive" gain codec 1 */
2010-08-28 05:59:27 +08:00
return 0 ;
}
2011-06-03 04:03:15 +08:00
static void qrv_dosetup ( struct dahdi_chan * chan , struct wctdm * wc )
2010-08-28 05:59:27 +08:00
{
2011-06-03 04:03:15 +08:00
struct wctdm_module * qrvmod ;
struct wctdm_module * nextqrvmod ;
2010-08-28 05:59:27 +08:00
int qrvcard ;
unsigned char r ;
long l ;
/* actually do something with the values */
qrvcard = ( chan - > chanpos - 1 ) & 0xfc ;
2011-06-03 04:03:15 +08:00
qrvmod = & wc - > mods [ qrvcard ] ;
nextqrvmod = & wc - > mods [ qrvcard + 1 ] ;
2011-06-03 04:02:51 +08:00
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" @@@@@ radmodes: %d,%d rxgains: %d,%d "
2011-06-03 04:02:56 +08:00
" txgains: %d,%d \n " , wc - > mods [ qrvcard ] . mod . qrv . radmode ,
2011-06-03 04:03:15 +08:00
nextqrvmod - > mod . qrv . radmode ,
wc - > mods [ qrvcard ] . mod . qrv . rxgain ,
nextqrvmod - > mod . qrv . rxgain ,
2011-06-03 04:02:56 +08:00
wc - > mods [ qrvcard ] . mod . qrv . txgain ,
2011-06-03 04:03:15 +08:00
nextqrvmod - > mod . qrv . txgain ) ;
2011-06-03 04:02:51 +08:00
}
2010-08-28 05:59:27 +08:00
r = 0 ;
2011-06-03 04:03:15 +08:00
if ( qrvmod - > mod . qrv . radmode & RADMODE_DEEMP )
2011-06-03 04:02:51 +08:00
r | = 4 ;
2011-06-03 04:03:15 +08:00
if ( nextqrvmod - > mod . qrv . radmode & RADMODE_DEEMP )
2011-06-03 04:02:51 +08:00
r | = 8 ;
2011-06-03 04:03:15 +08:00
if ( qrvmod - > mod . qrv . rxgain < 1200 )
2011-06-03 04:02:51 +08:00
r | = 1 ;
2011-06-03 04:03:15 +08:00
if ( nextqrvmod - > mod . qrv . rxgain < 1200 )
2011-06-03 04:02:51 +08:00
r | = 2 ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , qrvmod , 7 , r ) ;
2010-08-28 05:59:27 +08:00
if ( debug ) dev_info ( & wc - > vb . pdev - > dev , " @@@@@ setting reg 7 to %02x hex \n " , r ) ;
r = 0 ;
2011-06-03 04:03:15 +08:00
if ( qrvmod - > mod . qrv . radmode & RADMODE_PREEMP )
2011-06-03 04:02:51 +08:00
r | = 3 ;
2011-06-03 04:03:15 +08:00
else if ( qrvmod - > mod . qrv . txgain > = 3600 )
2011-06-03 04:02:51 +08:00
r | = 1 ;
2011-06-03 04:03:15 +08:00
else if ( qrvmod - > mod . qrv . txgain > = 1200 )
2011-06-03 04:02:51 +08:00
r | = 2 ;
2011-06-03 04:03:15 +08:00
if ( nextqrvmod - > mod . qrv . radmode & RADMODE_PREEMP )
2011-06-03 04:02:51 +08:00
r | = 0xc ;
2011-06-03 04:03:15 +08:00
else if ( nextqrvmod - > mod . qrv . txgain > = 3600 )
2011-06-03 04:02:51 +08:00
r | = 4 ;
2011-06-03 04:03:15 +08:00
else if ( nextqrvmod - > mod . qrv . txgain > = 1200 )
2011-06-03 04:02:51 +08:00
r | = 8 ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , qrvmod , 4 , r ) ;
2010-08-28 05:59:27 +08:00
if ( debug ) dev_info ( & wc - > vb . pdev - > dev , " @@@@@ setting reg 4 to %02x hex \n " , r ) ;
r = 0 ;
2011-06-03 04:03:15 +08:00
if ( qrvmod - > mod . qrv . rxgain > = 2400 )
2011-06-03 04:02:51 +08:00
r | = 1 ;
2011-06-03 04:03:15 +08:00
if ( nextqrvmod - > mod . qrv . rxgain > = 2400 )
2011-06-03 04:02:51 +08:00
r | = 2 ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , qrvmod , 0x25 , r ) ;
2010-08-28 05:59:27 +08:00
if ( debug ) dev_info ( & wc - > vb . pdev - > dev , " @@@@@ setting reg 0x25 to %02x hex \n " , r ) ;
r = 0 ;
2011-06-03 04:03:15 +08:00
if ( qrvmod - > mod . qrv . txgain < 2400 )
2011-06-03 04:02:51 +08:00
r | = 1 ;
else
r | = 4 ;
2011-06-03 04:03:15 +08:00
if ( nextqrvmod - > mod . qrv . txgain < 2400 )
2011-06-03 04:02:51 +08:00
r | = 8 ;
else
r | = 0x20 ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , qrvmod , 0x26 , r ) ;
2010-08-28 05:59:27 +08:00
if ( debug ) dev_info ( & wc - > vb . pdev - > dev , " @@@@@ setting reg 0x26 to %02x hex \n " , r ) ;
2011-06-03 04:03:15 +08:00
l = ( ( long ) ( qrvmod - > mod . qrv . rxgain % 1200 ) * 10000 ) / 46875 ;
2010-08-28 05:59:27 +08:00
if ( l = = 0 ) l = 1 ;
2011-06-03 04:03:15 +08:00
if ( qrvmod - > mod . qrv . rxgain > = 2400 )
2011-06-03 04:02:51 +08:00
l + = 181 ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , qrvmod , 0x0b , ( unsigned char ) l ) ;
2010-08-28 05:59:27 +08:00
if ( debug ) dev_info ( & wc - > vb . pdev - > dev , " @@@@@ setting reg 0x0b to %02x hex \n " , ( unsigned char ) l ) ;
2011-06-03 04:03:15 +08:00
l = ( ( long ) ( nextqrvmod - > mod . qrv . rxgain % 1200 ) * 10000 ) / 46875 ;
2010-08-28 05:59:27 +08:00
if ( l = = 0 ) l = 1 ;
2011-06-03 04:03:15 +08:00
if ( nextqrvmod - > mod . qrv . rxgain > = 2400 )
2011-06-03 04:02:51 +08:00
l + = 181 ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , qrvmod , 0x0c , ( unsigned char ) l ) ;
2010-08-28 05:59:27 +08:00
if ( debug ) dev_info ( & wc - > vb . pdev - > dev , " @@@@@ setting reg 0x0c to %02x hex \n " , ( unsigned char ) l ) ;
2011-06-03 04:03:15 +08:00
l = ( ( long ) ( qrvmod - > mod . qrv . txgain % 1200 ) * 10000 ) / 46875 ;
2010-08-28 05:59:27 +08:00
if ( l = = 0 ) l = 1 ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , qrvmod , 0x0f , ( unsigned char ) l ) ;
2010-08-28 05:59:27 +08:00
if ( debug ) dev_info ( & wc - > vb . pdev - > dev , " @@@@@ setting reg 0x0f to %02x hex \n " , ( unsigned char ) l ) ;
2011-06-03 04:03:15 +08:00
l = ( ( long ) ( nextqrvmod - > mod . qrv . txgain % 1200 ) * 10000 ) / 46875 ;
2010-08-28 05:59:27 +08:00
if ( l = = 0 ) l = 1 ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , qrvmod , 0x10 , ( unsigned char ) l ) ;
2010-08-28 05:59:27 +08:00
if ( debug ) dev_info ( & wc - > vb . pdev - > dev , " @@@@@ setting reg 0x10 to %02x hex \n " , ( unsigned char ) l ) ;
return ;
}
2011-06-03 04:03:15 +08:00
static void wctdm24xxp_get_fxs_regs ( struct wctdm * wc , struct wctdm_module * mod ,
2011-02-10 02:57:53 +08:00
struct wctdm_regs * regs )
{
int x ;
2011-06-03 04:03:15 +08:00
2011-02-10 02:57:53 +08:00
for ( x = 0 ; x < NUM_INDIRECT_REGS ; x + + )
2011-06-03 04:03:15 +08:00
regs - > indirect [ x ] = wctdm_proslic_getreg_indirect ( wc , mod , x ) ;
2011-02-10 02:57:53 +08:00
for ( x = 0 ; x < NUM_REGS ; x + + )
2011-06-03 04:03:15 +08:00
regs - > direct [ x ] = wctdm_getreg ( wc , mod , x ) ;
2011-02-10 02:57:53 +08:00
}
2011-06-03 04:03:15 +08:00
static void wctdm24xxp_get_fxo_regs ( struct wctdm * wc , struct wctdm_module * mod ,
2011-02-10 02:57:53 +08:00
struct wctdm_regs * regs )
{
int x ;
for ( x = 0 ; x < NUM_FXO_REGS ; x + + )
2011-06-03 04:03:15 +08:00
regs - > direct [ x ] = wctdm_getreg ( wc , mod , x ) ;
2011-02-10 02:57:53 +08:00
}
2011-06-03 04:03:15 +08:00
static void wctdm24xxp_get_qrv_regs ( struct wctdm * wc , struct wctdm_module * mod ,
2011-02-10 02:57:53 +08:00
struct wctdm_regs * regs )
{
int x ;
for ( x = 0 ; x < 0x32 ; x + + )
2011-06-03 04:03:15 +08:00
regs - > direct [ x ] = wctdm_getreg ( wc , mod , x ) ;
2011-02-10 02:57:53 +08:00
}
2010-08-28 05:59:27 +08:00
static int wctdm_ioctl ( struct dahdi_chan * chan , unsigned int cmd , unsigned long data )
{
struct wctdm_stats stats ;
struct wctdm_regop regop ;
struct wctdm_echo_coefs echoregs ;
struct dahdi_hwgain hwgain ;
struct wctdm * wc = chan - > pvt ;
int x ;
union {
struct dahdi_radio_stat s ;
struct dahdi_radio_param p ;
} stack ;
2011-06-03 04:02:51 +08:00
struct wctdm_module * const mod = & wc - > mods [ chan - > chanpos - 1 ] ;
struct fxs * const fxs = & mod - > mod . fxs ;
2010-08-28 05:59:27 +08:00
switch ( cmd ) {
case DAHDI_ONHOOKTRANSFER :
2011-06-03 04:03:01 +08:00
if ( mod - > type ! = FXS )
2010-08-28 05:59:27 +08:00
return - EINVAL ;
if ( get_user ( x , ( __user int * ) data ) )
return - EFAULT ;
/* Active mode when idle */
2011-06-03 04:03:10 +08:00
fxs - > idletxhookstate = POLARITY_XOR ( fxs ) ?
2010-08-28 05:59:27 +08:00
SLIC_LF_ACTIVE_REV :
SLIC_LF_ACTIVE_FWD ;
if ( ( ( fxs - > lasttxhook & SLIC_LF_SETMASK ) = = SLIC_LF_ACTIVE_FWD ) | |
( ( fxs - > lasttxhook & SLIC_LF_SETMASK ) = = SLIC_LF_ACTIVE_REV ) ) {
2012-01-11 04:45:30 +08:00
int res ;
2010-08-28 05:59:27 +08:00
2012-01-11 04:45:30 +08:00
res = set_lasttxhook_interruptible ( wc , fxs ,
2011-06-03 04:03:10 +08:00
( POLARITY_XOR ( fxs ) ?
2010-08-28 05:59:27 +08:00
SLIC_LF_OHTRAN_REV : SLIC_LF_OHTRAN_FWD ) ,
2011-06-03 04:02:51 +08:00
& mod - > sethook ) ;
2010-08-28 05:59:27 +08:00
if ( debug & DEBUG_CARD ) {
2012-01-11 04:45:30 +08:00
if ( res ) {
2010-08-28 05:59:27 +08:00
dev_info ( & wc - > vb . pdev - > dev ,
" Channel %d TIMEOUT: "
" OnHookTransfer start \n " ,
chan - > chanpos - 1 ) ;
} else {
dev_info ( & wc - > vb . pdev - > dev ,
" Channel %d OnHookTransfer "
" start \n " , chan - > chanpos - 1 ) ;
}
}
}
2011-08-31 00:38:27 +08:00
fxs - > ohttimer = wc - > framecount + x ;
fxs - > oht_active = 1 ;
2010-08-28 05:59:27 +08:00
break ;
case DAHDI_VMWI_CONFIG :
2011-06-03 04:03:01 +08:00
if ( mod - > type ! = FXS )
2010-08-28 05:59:27 +08:00
return - EINVAL ;
if ( copy_from_user ( & ( fxs - > vmwisetting ) ,
( __user void * ) data ,
sizeof ( fxs - > vmwisetting ) ) )
return - EFAULT ;
2011-06-03 04:03:15 +08:00
set_vmwi ( wc , mod ) ;
2010-08-28 05:59:27 +08:00
break ;
case DAHDI_VMWI :
2011-06-03 04:03:01 +08:00
if ( mod - > type ! = FXS )
2010-08-28 05:59:27 +08:00
return - EINVAL ;
if ( get_user ( x , ( __user int * ) data ) )
return - EFAULT ;
if ( 0 > x )
return - EFAULT ;
fxs - > vmwi_active_messages = x ;
2011-06-03 04:03:15 +08:00
set_vmwi ( wc , mod ) ;
2010-08-28 05:59:27 +08:00
break ;
case WCTDM_GET_STATS :
2011-06-03 04:03:01 +08:00
if ( mod - > type = = FXS ) {
2011-06-03 04:03:15 +08:00
stats . tipvolt = wctdm_getreg ( wc , mod , 80 ) * - 376 ;
stats . ringvolt = wctdm_getreg ( wc , mod , 81 ) * - 376 ;
stats . batvolt = wctdm_getreg ( wc , mod , 82 ) * - 376 ;
2011-06-03 04:03:01 +08:00
} else if ( mod - > type = = FXO ) {
2011-06-03 04:03:15 +08:00
stats . tipvolt = ( s8 ) wctdm_getreg ( wc , mod , 29 ) * 1000 ;
stats . ringvolt = ( s8 ) wctdm_getreg ( wc , mod , 29 ) * 1000 ;
stats . batvolt = ( s8 ) wctdm_getreg ( wc , mod , 29 ) * 1000 ;
2010-08-28 05:59:27 +08:00
} else
return - EINVAL ;
if ( copy_to_user ( ( __user void * ) data , & stats , sizeof ( stats ) ) )
return - EFAULT ;
break ;
case WCTDM_GET_REGS :
2011-02-10 02:57:53 +08:00
{
struct wctdm_regs * regs = kzalloc ( sizeof ( * regs ) , GFP_KERNEL ) ;
if ( ! regs )
return - ENOMEM ;
2011-06-03 04:03:01 +08:00
if ( mod - > type = = FXS )
2011-06-03 04:03:15 +08:00
wctdm24xxp_get_fxs_regs ( wc , mod , regs ) ;
2011-06-03 04:03:01 +08:00
else if ( mod - > type = = QRV )
2011-06-03 04:03:15 +08:00
wctdm24xxp_get_qrv_regs ( wc , mod , regs ) ;
2011-02-10 02:57:53 +08:00
else
2011-06-03 04:03:15 +08:00
wctdm24xxp_get_fxo_regs ( wc , mod , regs ) ;
2011-02-10 02:57:53 +08:00
if ( copy_to_user ( ( __user void * ) data , regs , sizeof ( * regs ) ) ) {
kfree ( regs ) ;
2010-08-28 05:59:27 +08:00
return - EFAULT ;
2011-02-10 02:57:53 +08:00
}
kfree ( regs ) ;
2010-08-28 05:59:27 +08:00
break ;
2011-02-10 02:57:53 +08:00
}
2010-08-28 05:59:27 +08:00
case WCTDM_SET_REG :
if ( copy_from_user ( & regop , ( __user void * ) data , sizeof ( regop ) ) )
return - EFAULT ;
if ( regop . indirect ) {
2011-06-03 04:03:01 +08:00
if ( mod - > type ! = FXS )
2010-08-28 05:59:27 +08:00
return - EINVAL ;
dev_info ( & wc - > vb . pdev - > dev , " Setting indirect %d to 0x%04x on %d \n " , regop . reg , regop . val , chan - > chanpos ) ;
2011-06-03 04:03:15 +08:00
wctdm_proslic_setreg_indirect ( wc , mod , regop . reg ,
regop . val ) ;
2010-08-28 05:59:27 +08:00
} else {
regop . val & = 0xff ;
if ( regop . reg = = LINE_STATE ) {
/* Set feedback register to indicate the new state that is being set */
fxs - > lasttxhook = ( regop . val & 0x0f ) | SLIC_LF_OPPENDING ;
}
dev_info ( & wc - > vb . pdev - > dev , " Setting direct %d to %04x on %d \n " , regop . reg , regop . val , chan - > chanpos ) ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , regop . reg , regop . val ) ;
2010-08-28 05:59:27 +08:00
}
break ;
case WCTDM_SET_ECHOTUNE :
dev_info ( & wc - > vb . pdev - > dev , " -- Setting echo registers: \n " ) ;
if ( copy_from_user ( & echoregs , ( __user void * ) data , sizeof ( echoregs ) ) )
return - EFAULT ;
2011-06-03 04:03:01 +08:00
if ( mod - > type = = FXO ) {
2010-08-28 05:59:27 +08:00
/* Set the ACIM register */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 30 , echoregs . acim ) ;
2010-08-28 05:59:27 +08:00
/* Set the digital echo canceller registers */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 45 , echoregs . coef1 ) ;
wctdm_setreg ( wc , mod , 46 , echoregs . coef2 ) ;
wctdm_setreg ( wc , mod , 47 , echoregs . coef3 ) ;
wctdm_setreg ( wc , mod , 48 , echoregs . coef4 ) ;
wctdm_setreg ( wc , mod , 49 , echoregs . coef5 ) ;
wctdm_setreg ( wc , mod , 50 , echoregs . coef6 ) ;
wctdm_setreg ( wc , mod , 51 , echoregs . coef7 ) ;
wctdm_setreg ( wc , mod , 52 , echoregs . coef8 ) ;
2010-08-28 05:59:27 +08:00
dev_info ( & wc - > vb . pdev - > dev , " -- Set echo registers successfully \n " ) ;
break ;
} else {
return - EINVAL ;
}
break ;
case DAHDI_SET_HWGAIN :
if ( copy_from_user ( & hwgain , ( __user void * ) data , sizeof ( hwgain ) ) )
return - EFAULT ;
2011-06-03 04:03:15 +08:00
wctdm_set_hwgain ( wc , mod , hwgain . newgain , hwgain . tx ) ;
2010-08-28 05:59:27 +08:00
if ( debug )
dev_info ( & wc - > vb . pdev - > dev , " Setting hwgain on channel %d to %d for %s direction \n " ,
chan - > chanpos - 1 , hwgain . newgain , hwgain . tx ? " tx " : " rx " ) ;
break ;
# ifdef VPM_SUPPORT
case DAHDI_TONEDETECT :
/* Hardware DTMF detection is not supported. */
return - ENOSYS ;
# endif
case DAHDI_SETPOLARITY :
if ( get_user ( x , ( __user int * ) data ) )
return - EFAULT ;
2011-06-03 04:03:01 +08:00
if ( mod - > type ! = FXS )
2010-08-28 05:59:27 +08:00
return - EINVAL ;
/* Can't change polarity while ringing or when open */
if ( ( ( fxs - > lasttxhook & SLIC_LF_SETMASK ) = = SLIC_LF_RINGING ) | |
( ( fxs - > lasttxhook & SLIC_LF_SETMASK ) = = SLIC_LF_OPEN ) ) {
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Channel %d Unable to Set Polarity \n " , chan - > chanpos - 1 ) ;
}
return - EINVAL ;
}
fxs - > reversepolarity = ( x ) ? 1 : 0 ;
2011-06-03 04:03:10 +08:00
if ( POLARITY_XOR ( fxs ) ) {
2010-08-28 05:59:27 +08:00
fxs - > idletxhookstate | = SLIC_LF_REVMASK ;
x = fxs - > lasttxhook & SLIC_LF_SETMASK ;
x | = SLIC_LF_REVMASK ;
if ( x ! = fxs - > lasttxhook ) {
2011-06-03 04:03:43 +08:00
x = set_lasttxhook_interruptible ( wc , fxs , x ,
2011-06-03 04:02:51 +08:00
& mod - > sethook ) ;
2010-08-28 05:59:27 +08:00
if ( ( debug & DEBUG_CARD ) & & x ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Channel %d TIMEOUT: Set Reverse "
" Polarity \n " , chan - > chanpos - 1 ) ;
} else if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Channel %d Set Reverse Polarity \n " ,
chan - > chanpos - 1 ) ;
}
}
} else {
fxs - > idletxhookstate & = ~ SLIC_LF_REVMASK ;
x = fxs - > lasttxhook & SLIC_LF_SETMASK ;
x & = ~ SLIC_LF_REVMASK ;
if ( x ! = fxs - > lasttxhook ) {
2011-06-03 04:03:43 +08:00
x = set_lasttxhook_interruptible ( wc , fxs , x ,
2011-06-03 04:02:51 +08:00
& mod - > sethook ) ;
2010-08-28 05:59:27 +08:00
if ( ( debug & DEBUG_CARD ) & x ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Channel %d TIMEOUT: Set Normal "
" Polarity \n " , chan - > chanpos - 1 ) ;
} else if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Channel %d Set Normal Polarity \n " ,
chan - > chanpos - 1 ) ;
}
}
}
break ;
case DAHDI_RADIO_GETPARAM :
2011-06-03 04:03:01 +08:00
if ( mod - > type ! = QRV )
2010-08-28 05:59:27 +08:00
return - ENOTTY ;
if ( copy_from_user ( & stack . p , ( __user void * ) data , sizeof ( stack . p ) ) )
return - EFAULT ;
stack . p . data = 0 ; /* start with 0 value in output */
switch ( stack . p . radpar ) {
case DAHDI_RADPAR_INVERTCOR :
2011-06-03 04:02:56 +08:00
if ( mod - > mod . qrv . radmode & RADMODE_INVERTCOR )
2010-08-28 05:59:27 +08:00
stack . p . data = 1 ;
break ;
case DAHDI_RADPAR_IGNORECOR :
2011-06-03 04:02:56 +08:00
if ( mod - > mod . qrv . radmode & RADMODE_IGNORECOR )
2010-08-28 05:59:27 +08:00
stack . p . data = 1 ;
break ;
case DAHDI_RADPAR_IGNORECT :
2011-06-03 04:02:56 +08:00
if ( mod - > mod . qrv . radmode & RADMODE_IGNORECT )
2010-08-28 05:59:27 +08:00
stack . p . data = 1 ;
break ;
case DAHDI_RADPAR_EXTRXTONE :
stack . p . data = 0 ;
2011-06-03 04:02:56 +08:00
if ( mod - > mod . qrv . radmode & RADMODE_EXTTONE ) {
2010-08-28 05:59:27 +08:00
stack . p . data = 1 ;
2011-06-03 04:02:56 +08:00
if ( mod - > mod . qrv . radmode & RADMODE_EXTINVERT )
2010-08-28 05:59:27 +08:00
stack . p . data = 2 ;
}
break ;
case DAHDI_RADPAR_DEBOUNCETIME :
2011-06-03 04:02:56 +08:00
stack . p . data = mod - > mod . qrv . debouncetime ;
2010-08-28 05:59:27 +08:00
break ;
case DAHDI_RADPAR_RXGAIN :
2011-06-03 04:02:56 +08:00
stack . p . data = mod - > mod . qrv . rxgain - 1199 ;
2010-08-28 05:59:27 +08:00
break ;
case DAHDI_RADPAR_TXGAIN :
2011-06-03 04:02:56 +08:00
stack . p . data = mod - > mod . qrv . txgain - 3599 ;
2010-08-28 05:59:27 +08:00
break ;
case DAHDI_RADPAR_DEEMP :
stack . p . data = 0 ;
2011-06-03 04:02:56 +08:00
if ( mod - > mod . qrv . radmode & RADMODE_DEEMP )
2010-08-28 05:59:27 +08:00
stack . p . data = 1 ;
break ;
case DAHDI_RADPAR_PREEMP :
stack . p . data = 0 ;
2011-06-03 04:02:56 +08:00
if ( mod - > mod . qrv . radmode & RADMODE_PREEMP )
2010-08-28 05:59:27 +08:00
stack . p . data = 1 ;
break ;
default :
return - EINVAL ;
}
if ( copy_to_user ( ( __user void * ) data , & stack . p , sizeof ( stack . p ) ) )
return - EFAULT ;
break ;
case DAHDI_RADIO_SETPARAM :
2011-06-03 04:03:01 +08:00
if ( mod - > type ! = QRV )
2010-08-28 05:59:27 +08:00
return - ENOTTY ;
if ( copy_from_user ( & stack . p , ( __user void * ) data , sizeof ( stack . p ) ) )
return - EFAULT ;
switch ( stack . p . radpar ) {
case DAHDI_RADPAR_INVERTCOR :
if ( stack . p . data )
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode | = RADMODE_INVERTCOR ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode & = ~ RADMODE_INVERTCOR ;
2010-08-28 05:59:27 +08:00
return 0 ;
case DAHDI_RADPAR_IGNORECOR :
if ( stack . p . data )
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode | = RADMODE_IGNORECOR ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode & = ~ RADMODE_IGNORECOR ;
2010-08-28 05:59:27 +08:00
return 0 ;
case DAHDI_RADPAR_IGNORECT :
if ( stack . p . data )
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode | = RADMODE_IGNORECT ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode & = ~ RADMODE_IGNORECT ;
2010-08-28 05:59:27 +08:00
return 0 ;
case DAHDI_RADPAR_EXTRXTONE :
if ( stack . p . data )
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode | = RADMODE_EXTTONE ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode & = ~ RADMODE_EXTTONE ;
2010-08-28 05:59:27 +08:00
if ( stack . p . data > 1 )
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode | = RADMODE_EXTINVERT ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode & = ~ RADMODE_EXTINVERT ;
2010-08-28 05:59:27 +08:00
return 0 ;
case DAHDI_RADPAR_DEBOUNCETIME :
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . debouncetime = stack . p . data ;
2010-08-28 05:59:27 +08:00
return 0 ;
case DAHDI_RADPAR_RXGAIN :
/* if out of range */
if ( ( stack . p . data < = - 1200 ) | | ( stack . p . data > 1552 ) )
{
return - EINVAL ;
}
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . rxgain = stack . p . data + 1199 ;
2010-08-28 05:59:27 +08:00
break ;
case DAHDI_RADPAR_TXGAIN :
/* if out of range */
2011-06-03 04:02:56 +08:00
if ( mod - > mod . qrv . radmode & RADMODE_PREEMP ) {
2011-06-03 04:02:51 +08:00
if ( ( stack . p . data < = - 2400 ) | |
( stack . p . data > 0 ) )
2010-08-28 05:59:27 +08:00
return - EINVAL ;
2011-06-03 04:02:51 +08:00
} else {
if ( ( stack . p . data < = - 3600 ) | |
( stack . p . data > 1200 ) )
2010-08-28 05:59:27 +08:00
return - EINVAL ;
}
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . txgain = stack . p . data + 3599 ;
2010-08-28 05:59:27 +08:00
break ;
case DAHDI_RADPAR_DEEMP :
if ( stack . p . data )
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode | = RADMODE_DEEMP ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode & = ~ RADMODE_DEEMP ;
mod - > mod . qrv . rxgain = 1199 ;
2010-08-28 05:59:27 +08:00
break ;
case DAHDI_RADPAR_PREEMP :
if ( stack . p . data )
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode | = RADMODE_PREEMP ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . radmode & = ~ RADMODE_PREEMP ;
mod - > mod . qrv . txgain = 3599 ;
2010-08-28 05:59:27 +08:00
break ;
default :
return - EINVAL ;
}
qrv_dosetup ( chan , wc ) ;
return 0 ;
default :
return - ENOTTY ;
}
return 0 ;
}
static int wctdm_open ( struct dahdi_chan * chan )
{
2011-06-03 04:02:51 +08:00
struct wctdm * const wc = chan - > pvt ;
2010-08-28 05:59:27 +08:00
unsigned long flags ;
2011-06-03 04:02:51 +08:00
struct wctdm_module * const mod = & wc - > mods [ chan - > chanpos - 1 ] ;
2010-08-28 05:59:27 +08:00
#if 0
if ( wc - > dead )
return - ENODEV ;
# endif
2011-06-03 04:03:01 +08:00
if ( mod - > type = = FXO ) {
2010-08-28 05:59:27 +08:00
/* Reset the mwi indicators */
spin_lock_irqsave ( & wc - > reglock , flags ) ;
2011-06-03 04:02:51 +08:00
mod - > mod . fxo . neonmwi_debounce = 0 ;
mod - > mod . fxo . neonmwi_offcounter = 0 ;
mod - > mod . fxo . neonmwi_state = 0 ;
2010-08-28 05:59:27 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
}
return 0 ;
}
static inline struct wctdm * span_to_wctdm ( struct dahdi_span * span )
{
struct wctdm_span * s = container_of ( span , struct wctdm_span , span ) ;
return s - > wc ;
}
static int wctdm_watchdog ( struct dahdi_span * span , int event )
{
struct wctdm * wc = span_to_wctdm ( span ) ;
dev_info ( & wc - > vb . pdev - > dev , " TDM: Called watchdog \n " ) ;
return 0 ;
}
static int wctdm_close ( struct dahdi_chan * chan )
{
struct wctdm * wc ;
int x ;
signed char reg ;
wc = chan - > pvt ;
for ( x = 0 ; x < wc - > mods_per_board ; x + + ) {
2011-06-03 04:02:51 +08:00
struct wctdm_module * const mod = & wc - > mods [ x ] ;
2011-06-03 04:03:01 +08:00
if ( FXS = = mod - > type ) {
2011-06-03 04:02:51 +08:00
mod - > mod . fxs . idletxhookstate =
2011-06-03 04:03:10 +08:00
POLARITY_XOR ( & mod - > mod . fxs ) ? SLIC_LF_ACTIVE_REV :
SLIC_LF_ACTIVE_FWD ;
2011-06-03 04:03:01 +08:00
} else if ( QRV = = mod - > type ) {
2010-08-28 05:59:27 +08:00
int qrvcard = x & 0xfc ;
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . hook = 0 ;
wc - > mods [ x + 2 ] . mod . qrv . hook = 0xff ;
mod - > mod . qrv . debouncetime = QRV_DEBOUNCETIME ;
mod - > mod . qrv . debtime = 0 ;
mod - > mod . qrv . radmode = 0 ;
mod - > mod . qrv . txgain = 3599 ;
mod - > mod . qrv . rxgain = 1199 ;
2010-08-28 05:59:27 +08:00
reg = 0 ;
2011-06-03 04:02:56 +08:00
if ( ! wc - > mods [ qrvcard ] . mod . qrv . hook )
2010-08-28 05:59:27 +08:00
reg | = 1 ;
2011-06-03 04:02:56 +08:00
if ( ! wc - > mods [ qrvcard + 1 ] . mod . qrv . hook )
2010-08-28 05:59:27 +08:00
reg | = 0x10 ;
2011-06-03 04:02:51 +08:00
wc - > mods [ qrvcard ] . sethook = CMD_WR ( 3 , reg ) ;
2010-08-28 05:59:27 +08:00
qrv_dosetup ( chan , wc ) ;
}
}
return 0 ;
}
static int wctdm_hooksig ( struct dahdi_chan * chan , enum dahdi_txsig txsig )
{
struct wctdm * wc = chan - > pvt ;
2011-06-03 04:02:56 +08:00
int reg = 0 ;
2011-06-03 04:02:51 +08:00
struct wctdm_module * const mod = & wc - > mods [ chan - > chanpos - 1 ] ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:01 +08:00
if ( mod - > type = = QRV ) {
2011-06-03 04:02:56 +08:00
const int qrvcard = ( chan - > chanpos - 1 ) & 0xfc ;
2010-08-28 05:59:27 +08:00
switch ( txsig ) {
case DAHDI_TXSIG_START :
case DAHDI_TXSIG_OFFHOOK :
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . hook = 1 ;
2010-08-28 05:59:27 +08:00
break ;
case DAHDI_TXSIG_ONHOOK :
2011-06-03 04:02:56 +08:00
mod - > mod . qrv . hook = 0 ;
2010-08-28 05:59:27 +08:00
break ;
default :
dev_notice ( & wc - > vb . pdev - > dev , " wctdm24xxp: Can't set tx state to %d \n " , txsig ) ;
}
reg = 0 ;
2011-06-03 04:02:56 +08:00
if ( ! wc - > mods [ qrvcard ] . mod . qrv . hook )
2011-06-03 04:02:51 +08:00
reg | = 1 ;
2011-06-03 04:02:56 +08:00
if ( ! wc - > mods [ qrvcard + 1 ] . mod . qrv . hook )
2011-06-03 04:02:51 +08:00
reg | = 0x10 ;
wc - > mods [ qrvcard ] . sethook = CMD_WR ( 3 , reg ) ;
2010-08-28 05:59:27 +08:00
/* wctdm_setreg(wc, qrvcard, 3, reg); */
2011-06-03 04:03:01 +08:00
} else if ( mod - > type = = FXO ) {
2010-08-28 05:59:27 +08:00
switch ( txsig ) {
case DAHDI_TXSIG_START :
case DAHDI_TXSIG_OFFHOOK :
2011-06-03 04:02:51 +08:00
mod - > mod . fxo . offhook = 1 ;
mod - > sethook = CMD_WR ( 5 , 0x9 ) ;
2010-08-28 05:59:27 +08:00
/* wctdm_setreg(wc, chan->chanpos - 1, 5, 0x9); */
break ;
case DAHDI_TXSIG_ONHOOK :
2011-06-03 04:02:51 +08:00
mod - > mod . fxo . offhook = 0 ;
mod - > sethook = CMD_WR ( 5 , 0x8 ) ;
2010-08-28 05:59:27 +08:00
/* wctdm_setreg(wc, chan->chanpos - 1, 5, 0x8); */
break ;
default :
dev_notice ( & wc - > vb . pdev - > dev , " wctdm24xxp: Can't set tx state to %d \n " , txsig ) ;
}
2011-06-03 04:03:01 +08:00
} else if ( mod - > type = = FXS ) {
2011-06-03 04:03:15 +08:00
wctdm_fxs_hooksig ( wc , mod , txsig ) ;
2010-08-28 05:59:27 +08:00
}
return 0 ;
}
static void wctdm_dacs_connect ( struct wctdm * wc , int srccard , int dstcard )
{
2011-06-03 04:03:15 +08:00
struct wctdm_module * const srcmod = & wc - > mods [ srccard ] ;
struct wctdm_module * const dstmod = & wc - > mods [ dstcard ] ;
2011-06-03 04:02:51 +08:00
unsigned int type ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:02:51 +08:00
if ( wc - > mods [ dstcard ] . dacssrc > - 1 ) {
2010-08-28 05:59:27 +08:00
dev_notice ( & wc - > vb . pdev - > dev , " wctdm_dacs_connect: Can't have double sourcing yet! \n " ) ;
return ;
}
2011-06-03 04:02:51 +08:00
type = wc - > mods [ srccard ] . type ;
2011-06-03 04:03:01 +08:00
if ( ( type = = FXS ) | | ( type = = FXO ) ) {
2011-06-03 04:02:51 +08:00
dev_notice ( & wc - > vb . pdev - > dev ,
" wctdm_dacs_connect: Unsupported modtype for "
" card %d \n " , srccard ) ;
2010-08-28 05:59:27 +08:00
return ;
}
2011-06-03 04:02:51 +08:00
type = wc - > mods [ dstcard ] . type ;
2011-06-03 04:03:01 +08:00
if ( ( type ! = FXS ) & & ( type ! = FXO ) ) {
2011-06-03 04:02:51 +08:00
dev_notice ( & wc - > vb . pdev - > dev ,
" wctdm_dacs_connect: Unsupported modtype "
" for card %d \n " , dstcard ) ;
2010-08-28 05:59:27 +08:00
return ;
}
2011-06-03 04:03:15 +08:00
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" connect %d => %d \n " , srccard , dstcard ) ;
}
dstmod - > dacssrc = srccard ;
2010-08-28 05:59:27 +08:00
/* make srccard transmit to srccard+24 on the TDM bus */
2011-06-03 04:03:15 +08:00
if ( srcmod - > type = = FXS ) {
2010-08-28 05:59:27 +08:00
/* proslic */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , srcmod , PCM_XMIT_START_COUNT_LSB ,
( ( srccard + 24 ) * 8 ) & 0xff ) ;
wctdm_setreg ( wc , srcmod , PCM_XMIT_START_COUNT_MSB ,
( ( srccard + 24 ) * 8 ) > > 8 ) ;
} else if ( srcmod - > type = = FXO ) {
/* daa TX */
wctdm_setreg ( wc , srcmod , 34 , ( ( srccard + 24 ) * 8 ) & 0xff ) ;
wctdm_setreg ( wc , srcmod , 35 , ( ( srccard + 24 ) * 8 ) > > 8 ) ;
2010-08-28 05:59:27 +08:00
}
/* have dstcard receive from srccard+24 on the TDM bus */
2011-06-03 04:03:15 +08:00
if ( dstmod - > type = = FXS ) {
2010-08-28 05:59:27 +08:00
/* proslic */
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , dstmod , PCM_RCV_START_COUNT_LSB ,
( ( srccard + 24 ) * 8 ) & 0xff ) ;
wctdm_setreg ( wc , dstmod , PCM_RCV_START_COUNT_MSB ,
( ( srccard + 24 ) * 8 ) > > 8 ) ;
} else if ( dstmod - > type = = FXO ) {
/* daa RX */
wctdm_setreg ( wc , dstmod , 36 , ( ( srccard + 24 ) * 8 ) & 0xff ) ;
wctdm_setreg ( wc , dstmod , 37 , ( ( srccard + 24 ) * 8 ) > > 8 ) ;
2010-08-28 05:59:27 +08:00
}
}
static void wctdm_dacs_disconnect ( struct wctdm * wc , int card )
{
2011-06-03 04:02:51 +08:00
struct wctdm_module * const mod = & wc - > mods [ card ] ;
2011-06-03 04:03:15 +08:00
struct wctdm_module * dacssrc ;
2011-06-03 04:02:51 +08:00
2011-06-03 04:03:15 +08:00
if ( mod - > dacssrc < = - 1 )
return ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
dacssrc = & wc - > mods [ mod - > dacssrc ] ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:15 +08:00
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev , " wctdm_dacs_disconnect: "
" restoring TX for %d and RX for %d \n " ,
mod - > dacssrc , card ) ;
}
/* restore TX (source card) */
if ( dacssrc - > type = = FXS ) {
wctdm_setreg ( wc , dacssrc , PCM_XMIT_START_COUNT_LSB ,
( mod - > dacssrc * 8 ) & 0xff ) ;
wctdm_setreg ( wc , dacssrc , PCM_XMIT_START_COUNT_MSB ,
( mod - > dacssrc * 8 ) > > 8 ) ;
} else if ( dacssrc - > type = = FXO ) {
wctdm_setreg ( wc , mod , 34 , ( card * 8 ) & 0xff ) ;
wctdm_setreg ( wc , mod , 35 , ( card * 8 ) > > 8 ) ;
} else {
dev_warn ( & wc - > vb . pdev - > dev ,
" WARNING: wctdm_dacs_disconnect() called "
" on unsupported modtype \n " ) ;
}
/* restore RX (this card) */
if ( FXS = = mod - > type ) {
wctdm_setreg ( wc , mod , PCM_RCV_START_COUNT_LSB ,
( card * 8 ) & 0xff ) ;
wctdm_setreg ( wc , mod , PCM_RCV_START_COUNT_MSB ,
( card * 8 ) > > 8 ) ;
} else if ( FXO = = mod - > type ) {
wctdm_setreg ( wc , mod , 36 , ( card * 8 ) & 0xff ) ;
wctdm_setreg ( wc , mod , 37 , ( card * 8 ) > > 8 ) ;
} else {
dev_warn ( & wc - > vb . pdev - > dev ,
" WARNING: wctdm_dacs_disconnect() called "
" on unsupported modtype \n " ) ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:15 +08:00
mod - > dacssrc = - 1 ;
2010-08-28 05:59:27 +08:00
}
static int wctdm_dacs ( struct dahdi_chan * dst , struct dahdi_chan * src )
{
struct wctdm * wc ;
if ( ! nativebridge )
return 0 ; /* should this return -1 since unsuccessful? */
wc = dst - > pvt ;
if ( src ) {
wctdm_dacs_connect ( wc , src - > chanpos - 1 , dst - > chanpos - 1 ) ;
if ( debug )
dev_info ( & wc - > vb . pdev - > dev , " dacs connecct: %d -> %d! \n \n " , src - > chanpos , dst - > chanpos ) ;
} else {
wctdm_dacs_disconnect ( wc , dst - > chanpos - 1 ) ;
if ( debug )
dev_info ( & wc - > vb . pdev - > dev , " dacs disconnect: %d! \n " , dst - > chanpos ) ;
}
return 0 ;
}
2011-06-03 04:01:54 +08:00
/**
* wctdm_wait_for_ready
*
* Check if the board has finished any setup and is ready to start processing
* calls .
*/
2011-06-29 06:28:53 +08:00
int wctdm_wait_for_ready ( struct wctdm * wc )
2011-06-03 04:01:54 +08:00
{
2011-06-29 06:28:53 +08:00
while ( ! is_initialized ( wc ) ) {
2011-06-03 04:01:54 +08:00
if ( fatal_signal_pending ( current ) )
return - EIO ;
msleep_interruptible ( 250 ) ;
}
return 0 ;
}
2011-06-29 06:28:53 +08:00
static int wctdm_enable_hw_preechocan ( struct dahdi_chan * chan )
{
struct wctdm * wc = chan - > pvt ;
struct wctdm_chan * wchan = container_of ( chan , struct wctdm_chan , chan ) ;
if ( ! wc - > vpmoct )
return 0 ;
return vpmoct_preecho_enable ( wc - > vpmoct , wchan - > timeslot ) ;
}
static void wctdm_disable_hw_preechocan ( struct dahdi_chan * chan )
{
struct wctdm * wc = chan - > pvt ;
struct wctdm_chan * wchan = container_of ( chan , struct wctdm_chan , chan ) ;
if ( ! wc - > vpmoct )
return ;
vpmoct_preecho_disable ( wc - > vpmoct , wchan - > timeslot ) ;
}
2011-06-03 04:01:54 +08:00
/**
* wctdm_chanconfig - Called when the channels are being configured .
*
* Ensure that the card is completely ready to go before we allow the channels
* to be completely configured . This is to allow lengthy initialization
* actions to take place in background on driver load and ensure we ' re synced
* up by the time dahdi_cfg is run .
*
*/
static int
wctdm_chanconfig ( struct file * file , struct dahdi_chan * chan , int sigtype )
{
struct wctdm * wc = chan - > pvt ;
2011-06-29 06:28:53 +08:00
if ( ( file - > f_flags & O_NONBLOCK ) & & ! is_initialized ( wc ) )
2011-06-03 04:01:54 +08:00
return - EAGAIN ;
return wctdm_wait_for_ready ( wc ) ;
}
2011-10-27 02:58:14 +08:00
/*
* wctdm24xxp_assigned - Called when span is assigned .
* @ span : The span that is now assigned .
*
* This function is called by the core of DAHDI after the span number and
* channel numbers have been assigned .
*
*/
static void wctdm24xxp_assigned ( struct dahdi_span * span )
{
struct dahdi_span * s ;
struct dahdi_device * ddev = span - > parent ;
struct wctdm * wc = NULL ;
list_for_each_entry ( s , & ddev - > spans , device_node ) {
wc = ( container_of ( s , struct wctdm_span , span ) ) - > wc ;
if ( ! test_bit ( DAHDI_FLAGBIT_REGISTERED , & s - > flags ) )
return ;
}
if ( wc ) {
WARN_ON ( 0 = = wc - > not_ready ) ;
- - wc - > not_ready ;
}
}
2010-08-28 05:59:27 +08:00
static const struct dahdi_span_ops wctdm24xxp_analog_span_ops = {
. owner = THIS_MODULE ,
. hooksig = wctdm_hooksig ,
. open = wctdm_open ,
. close = wctdm_close ,
. ioctl = wctdm_ioctl ,
. watchdog = wctdm_watchdog ,
2011-06-03 04:01:54 +08:00
. chanconfig = wctdm_chanconfig ,
2010-08-28 05:59:27 +08:00
. dacs = wctdm_dacs ,
2011-10-27 02:58:14 +08:00
. assigned = wctdm24xxp_assigned ,
2010-08-28 05:59:27 +08:00
# ifdef VPM_SUPPORT
2011-06-29 06:28:53 +08:00
. enable_hw_preechocan = wctdm_enable_hw_preechocan ,
. disable_hw_preechocan = wctdm_disable_hw_preechocan ,
2010-08-28 05:59:27 +08:00
. echocan_create = wctdm_echocan_create ,
2010-12-10 04:19:26 +08:00
. echocan_name = wctdm_echocan_name ,
2010-08-28 05:59:27 +08:00
# endif
} ;
static const struct dahdi_span_ops wctdm24xxp_digital_span_ops = {
. owner = THIS_MODULE ,
. open = wctdm_open ,
. close = wctdm_close ,
. ioctl = wctdm_ioctl ,
. watchdog = wctdm_watchdog ,
. hdlc_hard_xmit = wctdm_hdlc_hard_xmit ,
. spanconfig = b400m_spanconfig ,
. chanconfig = b400m_chanconfig ,
. dacs = wctdm_dacs ,
2011-10-27 02:58:14 +08:00
. assigned = wctdm24xxp_assigned ,
2010-08-28 05:59:27 +08:00
# ifdef VPM_SUPPORT
2011-06-29 06:28:53 +08:00
. enable_hw_preechocan = wctdm_enable_hw_preechocan ,
. disable_hw_preechocan = wctdm_disable_hw_preechocan ,
2010-08-28 05:59:27 +08:00
. echocan_create = wctdm_echocan_create ,
2010-12-10 04:19:26 +08:00
. echocan_name = wctdm_echocan_name ,
2010-08-28 05:59:27 +08:00
# endif
} ;
2011-06-03 04:03:29 +08:00
static struct wctdm_chan *
wctdm_init_chan ( struct wctdm * wc , struct wctdm_span * s , int chanoffset ,
int channo , unsigned int card_position )
2010-08-28 05:59:27 +08:00
{
struct wctdm_chan * c ;
c = kzalloc ( sizeof ( * c ) , GFP_KERNEL ) ;
if ( ! c )
return NULL ;
/* Do not change the procfs representation for non-hx8 cards. */
if ( dahdi_is_digital_span ( & s - > span ) ) {
2011-06-03 04:03:29 +08:00
sprintf ( c - > chan . name , " WCBRI/%d/%d/%d " , card_position ,
s - > spanno , channo ) ;
2010-08-28 05:59:27 +08:00
} else {
2011-06-03 04:03:29 +08:00
sprintf ( c - > chan . name , " WCTDM/%d/%d " , card_position , channo ) ;
2010-08-28 05:59:27 +08:00
}
c - > chan . chanpos = channo + 1 ;
c - > chan . span = & s - > span ;
c - > chan . pvt = wc ;
c - > timeslot = chanoffset + channo ;
return c ;
}
#if 0
/**
* wctdm_span_count ( ) - Return the number of spans exported by this board .
*
* This is only called during initialization so let ' s just count the spans each
* time we need this information as opposed to storing another variable in the
* wctdm structure .
*/
static int wctdm_span_count ( const struct wctdm * wc )
{
int i ;
int count = 0 ;
for ( i = 0 ; i < MAX_SPANS ; + + i ) {
if ( wc - > spans [ i ] )
+ + count ;
}
return count ;
}
# endif
2011-06-03 04:03:29 +08:00
static struct wctdm_span *
wctdm_init_span ( struct wctdm * wc , int spanno , int chanoffset , int chancount ,
int digital_span , unsigned int card_position )
2010-08-28 05:59:27 +08:00
{
int x ;
struct wctdm_chan * c ;
struct wctdm_span * s ;
static int spancount ;
s = kzalloc ( sizeof ( * s ) , GFP_KERNEL ) ;
if ( ! s )
return NULL ;
/* DAHDI stuff */
s - > span . offset = spanno ;
s - > spanno = spancount + + ;
s - > wc = wc ;
/* Do not change the procfs representation for non-hx8 cards. */
if ( digital_span )
2011-06-03 04:03:29 +08:00
sprintf ( s - > span . name , " WCBRI/%d/%d " , card_position , s - > spanno ) ;
2010-08-28 05:59:27 +08:00
else
2011-06-03 04:03:29 +08:00
sprintf ( s - > span . name , " WCTDM/%d " , card_position ) ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:04:02 +08:00
snprintf ( s - > span . desc , sizeof ( s - > span . desc ) - 1 , " %s " , wc - > desc - > name ) ;
2010-08-28 05:59:27 +08:00
if ( wc - > companding = = DAHDI_LAW_DEFAULT ) {
if ( wc - > digi_mods | | digital_span )
/* If we have a BRI module, Auto set to alaw */
s - > span . deflaw = DAHDI_LAW_ALAW ;
else
/* Auto set to ulaw */
s - > span . deflaw = DAHDI_LAW_MULAW ;
} else if ( wc - > companding = = DAHDI_LAW_ALAW ) {
/* Force everything to alaw */
s - > span . deflaw = DAHDI_LAW_ALAW ;
} else {
/* Auto set to ulaw */
s - > span . deflaw = DAHDI_LAW_MULAW ;
}
if ( digital_span ) {
s - > span . ops = & wctdm24xxp_digital_span_ops ;
s - > span . linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_B8ZS | DAHDI_CONFIG_D4 ;
s - > span . linecompat | = DAHDI_CONFIG_ESF | DAHDI_CONFIG_HDB3 | DAHDI_CONFIG_CCS | DAHDI_CONFIG_CRC4 ;
s - > span . linecompat | = DAHDI_CONFIG_NTTE | DAHDI_CONFIG_TERM ;
s - > span . spantype = " TE " ;
} else {
s - > span . ops = & wctdm24xxp_analog_span_ops ;
s - > span . flags = DAHDI_FLAG_RBS ;
/* analog sigcap handled in fixup_analog_span() */
}
s - > span . chans = kmalloc ( sizeof ( struct dahdi_chan * ) * chancount , GFP_KERNEL ) ;
if ( ! s - > span . chans )
return NULL ;
/* allocate channels for the span */
for ( x = 0 ; x < chancount ; x + + ) {
2011-06-03 04:03:29 +08:00
c = wctdm_init_chan ( wc , s , chanoffset , x , card_position ) ;
2010-08-28 05:59:27 +08:00
if ( ! c )
return NULL ;
wc - > chans [ chanoffset + x ] = c ;
s - > span . chans [ x ] = & c - > chan ;
}
s - > span . channels = chancount ;
if ( digital_span ) {
wc - > chans [ chanoffset + 0 ] - > chan . sigcap = DAHDI_SIG_CLEAR ;
wc - > chans [ chanoffset + 1 ] - > chan . sigcap = DAHDI_SIG_CLEAR ;
wc - > chans [ chanoffset + 2 ] - > chan . sigcap = DAHDI_SIG_HARDHDLC ;
}
wc - > spans [ spanno ] = s ;
return s ;
}
/**
* should_set_alaw ( ) - Should be called after all the spans are initialized .
*
* Returns true if the module companding should be set to alaw , otherwise
* false .
*/
static bool should_set_alaw ( const struct wctdm * wc )
{
if ( DAHDI_LAW_DEFAULT = = wc - > companding )
return ( wc - > digi_mods > 0 ) ;
else if ( DAHDI_LAW_ALAW = = wc - > companding )
return true ;
else
return false ;
}
static void wctdm_fixup_analog_span ( struct wctdm * wc , int spanno )
{
struct dahdi_span * s ;
int x , y ;
/* Finalize signalling */
y = 0 ;
s = & wc - > spans [ spanno ] - > span ;
for ( x = 0 ; x < wc - > desc - > ports ; x + + ) {
2011-06-03 04:02:51 +08:00
struct wctdm_module * const mod = & wc - > mods [ x ] ;
2010-08-28 05:59:27 +08:00
if ( debug ) {
dev_info ( & wc - > vb . pdev - > dev ,
" fixup_analog: x=%d, y=%d modtype=%d, "
2011-06-03 04:02:51 +08:00
" s->chans[%d]=%p \n " , x , y , mod - > type ,
2010-08-28 05:59:27 +08:00
y , s - > chans [ y ] ) ;
}
2011-06-03 04:03:01 +08:00
if ( mod - > type = = FXO ) {
2010-09-21 23:36:34 +08:00
int val ;
2010-08-28 05:59:27 +08:00
s - > chans [ y + + ] - > sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR ;
2010-09-21 23:36:34 +08:00
val = should_set_alaw ( wc ) ? 0x20 : 0x28 ;
# ifdef DEBUG
val = ( digitalloopback ) ? 0x30 : val ;
# endif
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 33 , val ) ;
2011-06-03 04:03:01 +08:00
} else if ( mod - > type = = FXS ) {
2010-08-28 05:59:27 +08:00
s - > chans [ y + + ] - > sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR ;
2011-06-03 04:03:15 +08:00
wctdm_setreg ( wc , mod , 1 ,
2010-08-28 05:59:27 +08:00
( should_set_alaw ( wc ) ? 0x20 : 0x28 ) ) ;
2011-06-03 04:03:01 +08:00
} else if ( mod - > type = = QRV ) {
2010-08-28 05:59:27 +08:00
s - > chans [ y + + ] - > sigcap = DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR ;
} else {
s - > chans [ y + + ] - > sigcap = 0 ;
}
}
}
static int wctdm_initialize_vpmadt032 ( struct wctdm * wc )
{
int res ;
struct vpmadt032_options options ;
2011-08-31 00:38:06 +08:00
struct vpmadt032 * vpm ;
unsigned long flags ;
2010-08-28 05:59:27 +08:00
options . debug = debug ;
options . vpmnlptype = vpmnlptype ;
options . vpmnlpthresh = vpmnlpthresh ;
options . vpmnlpmaxsupp = vpmnlpmaxsupp ;
2011-10-21 04:51:53 +08:00
if ( is_hx8 ( wc ) ) {
/* Hybrid cards potentially have 3 channels of EC on their
* ports since they may be BRI spans . */
options . channels = 3 * wc - > desc - > ports ;
} else {
options . channels = wc - > desc - > ports ;
}
BUG_ON ( options . channels > 24 ) ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:02:23 +08:00
wc - > vpmadt032 = vpmadt032_alloc ( & options ) ;
2010-08-28 05:59:27 +08:00
if ( ! wc - > vpmadt032 )
return - ENOMEM ;
wc - > vpmadt032 - > setchanconfig_from_state = setchanconfig_from_state ;
2011-08-31 00:37:57 +08:00
2010-08-28 05:59:27 +08:00
/* Pull the configuration information from the span holding
* the analog channels . */
2011-06-03 04:02:28 +08:00
res = vpmadt032_test ( wc - > vpmadt032 , & wc - > vb ) ;
if ( ! res )
res = vpmadt032_init ( wc - > vpmadt032 ) ;
2010-08-28 05:59:27 +08:00
if ( res ) {
2011-08-31 00:38:06 +08:00
vpm = wc - > vpmadt032 ;
spin_lock_irqsave ( & wc - > reglock , flags ) ;
2010-08-28 05:59:27 +08:00
wc - > vpmadt032 = NULL ;
2011-08-31 00:38:06 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
vpmadt032_free ( vpm ) ;
2010-08-28 05:59:27 +08:00
return res ;
}
/* Now we need to configure the VPMADT032 module for this
* particular board . */
res = config_vpmadt032 ( wc - > vpmadt032 , wc ) ;
if ( res ) {
2011-08-31 00:38:06 +08:00
vpm = wc - > vpmadt032 ;
spin_lock_irqsave ( & wc - > reglock , flags ) ;
2010-08-28 05:59:27 +08:00
wc - > vpmadt032 = NULL ;
2011-08-31 00:38:06 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
vpmadt032_free ( vpm ) ;
2010-08-28 05:59:27 +08:00
return res ;
}
return 0 ;
}
2011-06-29 06:29:00 +08:00
static void wctdm_vpm_load_complete ( struct device * dev , bool operational )
{
unsigned long flags ;
struct pci_dev * pdev = container_of ( dev , struct pci_dev , dev ) ;
struct wctdm * wc = pci_get_drvdata ( pdev ) ;
struct vpmoct * vpm = NULL ;
WARN_ON ( ! wc | | ! wc - > not_ready ) ;
if ( ! wc | | ! wc - > not_ready )
return ;
spin_lock_irqsave ( & wc - > reglock , flags ) ;
wc - > not_ready - - ;
if ( operational ) {
wc - > ctlreg | = 0x10 ;
} else {
vpm = wc - > vpmoct ;
wc - > vpmoct = NULL ;
}
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
if ( vpm )
vpmoct_free ( vpm ) ;
}
2011-08-31 00:37:57 +08:00
static int wctdm_initialize_vpm ( struct wctdm * wc , unsigned long unused )
2010-08-28 05:59:27 +08:00
{
int res = 0 ;
2011-08-31 00:37:57 +08:00
if ( ! vpmsupport ) {
dev_notice ( & wc - > vb . pdev - > dev , " VPM: Support Disabled \n " ) ;
return 0 ;
}
2011-01-31 23:53:26 +08:00
res = wctdm_initialize_vpmadt032 ( wc ) ;
2011-06-29 06:28:53 +08:00
if ( ! res ) {
2011-01-31 23:53:26 +08:00
wc - > ctlreg | = 0x10 ;
2011-08-31 00:37:57 +08:00
return 0 ;
2011-06-29 06:28:53 +08:00
} else {
struct vpmoct * vpm ;
unsigned long flags ;
vpm = vpmoct_alloc ( ) ;
if ( ! vpm ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Unable to allocate memory for struct vpmoct \n " ) ;
2011-08-31 00:37:57 +08:00
return - ENOMEM ;
2011-06-29 06:28:53 +08:00
}
vpm - > dev = & wc - > vb . pdev - > dev ;
spin_lock_irqsave ( & wc - > reglock , flags ) ;
wc - > vpmoct = vpm ;
2011-06-29 06:29:00 +08:00
wc - > not_ready + + ;
2011-06-29 06:28:53 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
2011-06-29 06:29:00 +08:00
res = vpmoct_init ( vpm , wctdm_vpm_load_complete ) ;
if ( - EINVAL = = res ) {
2011-06-29 06:28:53 +08:00
spin_lock_irqsave ( & wc - > reglock , flags ) ;
wc - > vpmoct = NULL ;
2011-06-29 06:29:00 +08:00
wc - > not_ready - - ;
2011-06-29 06:28:53 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
vpmoct_free ( vpm ) ;
}
}
2011-08-31 00:37:57 +08:00
return 0 ;
2010-08-28 05:59:27 +08:00
}
2011-08-31 00:37:57 +08:00
static int __wctdm_identify_module_group ( struct wctdm * wc , unsigned long base )
2010-08-28 05:59:27 +08:00
{
int x ;
unsigned long flags ;
2011-08-31 00:37:57 +08:00
for ( x = base ; x < base + 4 ; + + x ) {
2011-06-03 04:02:51 +08:00
struct wctdm_module * const mod = & wc - > mods [ x ] ;
2011-06-03 04:03:34 +08:00
enum { SANE = 1 , UNKNOWN = 0 } ;
int ret = 0 , readi = 0 ;
2011-06-03 04:03:57 +08:00
bool altcs = false ;
2010-08-28 05:59:27 +08:00
if ( fatal_signal_pending ( current ) )
break ;
retry :
2011-06-03 04:03:34 +08:00
ret = wctdm_init_proslic ( wc , mod , 0 , 0 , UNKNOWN ) ;
if ( ! ret ) {
2010-08-28 05:59:27 +08:00
if ( debug & DEBUG_CARD ) {
2011-06-03 04:03:15 +08:00
readi = wctdm_getreg ( wc , mod , LOOP_I_LIMIT ) ;
dev_info ( & wc - > vb . pdev - > dev ,
" Proslic module %d loop current "
" is %dmA \n " , x , ( ( readi * 3 ) + 20 ) ) ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:34 +08:00
continue ;
}
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:34 +08:00
if ( ret ! = - 2 ) {
/* Init with Manual Calibration */
if ( ! wctdm_init_proslic ( wc , mod , 0 , 1 , SANE ) ) {
if ( debug & DEBUG_CARD ) {
readi = wctdm_getreg ( wc , mod ,
LOOP_I_LIMIT ) ;
dev_info ( & wc - > vb . pdev - > dev ,
" Proslic module %d loop "
" current is %dmA \n " , x ,
( ( readi * 3 ) + 20 ) ) ;
2010-08-28 05:59:27 +08:00
}
} else {
2011-06-03 04:03:34 +08:00
dev_notice ( & wc - > vb . pdev - > dev ,
" Port %d: FAILED FXS (%s) \n " ,
x + 1 , fxshonormode ?
fxo_modes [ _opermode ] . name : " FCC " ) ;
}
continue ;
}
2010-12-02 00:59:20 +08:00
2011-06-03 04:03:34 +08:00
ret = wctdm_init_voicedaa ( wc , mod , 0 , 0 , UNKNOWN ) ;
2011-08-31 00:37:57 +08:00
if ( ! ret )
2011-06-03 04:03:34 +08:00
continue ;
2010-08-28 05:59:27 +08:00
2011-08-31 00:37:57 +08:00
if ( ! wctdm_init_qrvdri ( wc , x ) )
2011-06-03 04:03:34 +08:00
continue ;
2010-08-28 05:59:27 +08:00
2011-08-31 00:37:57 +08:00
if ( is_hx8 ( wc ) & & ! wctdm_init_b400m ( wc , x ) )
2011-06-03 04:03:34 +08:00
continue ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:57 +08:00
if ( ( wc - > desc - > ports ! = 24 ) & & ( ( x & 0x3 ) = = 1 ) & & ! altcs ) {
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:34 +08:00
spin_lock_irqsave ( & wc - > reglock , flags ) ;
2011-06-03 04:03:57 +08:00
set_offsets ( mod , 2 ) ;
altcs = true ;
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:34 +08:00
if ( wc - > desc - > ports = = 4 ) {
2011-06-03 04:03:57 +08:00
set_offsets ( & wc - > mods [ x + 1 ] , 3 ) ;
set_offsets ( & wc - > mods [ x + 2 ] , 3 ) ;
2011-06-03 04:03:34 +08:00
}
2010-08-28 05:59:27 +08:00
2011-06-03 04:03:34 +08:00
mod - > type = FXSINIT ;
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
2011-06-03 04:03:57 +08:00
udelay ( 1000 ) ;
udelay ( 1000 ) ;
2011-06-03 04:03:34 +08:00
spin_lock_irqsave ( & wc - > reglock , flags ) ;
mod - > type = FXS ;
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
if ( debug & DEBUG_CARD ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Trying port %d with alternate chip "
" select \n " , x + 1 ) ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:34 +08:00
goto retry ;
2010-08-28 05:59:27 +08:00
}
2011-06-03 04:03:34 +08:00
mod - > type = NONE ;
2011-08-31 00:37:57 +08:00
}
return 0 ;
}
/**
* wctdm_print_moule_configuration - Print the configuration to the kernel log
* @ wc : The card we ' re interested in .
*
* This is to ensure that the module configuration from each card shows up
* sequentially in the kernel log , as opposed to interleaved with one another .
*
*/
static void wctdm_print_module_configuration ( const struct wctdm * const wc )
{
int i ;
static DEFINE_MUTEX ( print ) ;
mutex_lock ( & print ) ;
for ( i = 0 ; i < wc - > mods_per_board ; + + i ) {
const struct wctdm_module * const mod = & wc - > mods [ i ] ;
switch ( mod - > type ) {
case FXO :
dev_info ( & wc - > vb . pdev - > dev , " Port %d: Installed -- "
" AUTO FXO (%s mode) \n " , i + 1 ,
fxo_modes [ _opermode ] . name ) ;
break ;
case FXS :
dev_info ( & wc - > vb . pdev - > dev ,
" Port %d: Installed -- AUTO FXS/DPO \n " , i + 1 ) ;
break ;
case BRI :
dev_info ( & wc - > vb . pdev - > dev , " Port %d: Installed -- BRI "
" quad-span module \n " , i + 1 ) ;
break ;
case QRV :
dev_info ( & wc - > vb . pdev - > dev ,
" Port %d: Installed -- QRV DRI card \n " , i + 1 ) ;
break ;
case NONE :
dev_info ( & wc - > vb . pdev - > dev ,
" Port %d: Not installed \n " , i + 1 ) ;
break ;
case FXSINIT :
break ;
}
}
mutex_unlock ( & print ) ;
}
static void wctdm_identify_modules ( struct wctdm * wc )
{
int x ;
unsigned long flags ;
struct bg * bg_work [ ARRAY_SIZE ( wc - > mods ) / 4 + 1 ] = { NULL , } ;
wc - > ctlreg = 0x00 ;
/*
* This looks a little weird .
*
* There are only 8 physical ports on the TDM / AEX800 , but the code
* immediately below sets 24 modules up . This has to do with the
* altcs magic that allows us to have single - port and quad - port
* modules on these products . The variable " mods_per_board " is set to
* the appropriate value just below the next code block .
*
* Now why this is important : The FXS modules come out of reset in a
* two - byte , non - chainable SPI mode . This is currently incompatible
* with how we do things , so we need to set them to a chained , 3 - byte
* command mode . This is done by setting the module type to FXSINIT
* for a little while so that cmd_dequeue will initialize the SLIC
* into the appropriate mode .
*
* This " go to 3-byte chained mode " command , however , wreaks havoc
* with HybridBRI .
*
* The solution : Since HybridBRI is only designed to work in an 8 - port
* card , and since the single - port modules " show up " in SPI slots > = 8
* in these cards , we only set SPI slots 8 - 23 to FXSINIT . The
* HybridBRI will never see the command that causes it to freak out
* and the single - port FXS cards get what they need so that when we
* probe with altcs we see them .
*/
/* Make sure all units go into daisy chain mode */
spin_lock_irqsave ( & wc - > reglock , flags ) ;
for ( x = 0 ; x < ARRAY_SIZE ( wc - > mods ) ; x + + )
wc - > mods [ x ] . type = FXSINIT ;
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
/* Wait just a bit; this makes sure that cmd_dequeue is emitting SPI
* commands in the appropriate mode ( s ) . */
msleep ( 20 ) ;
/* Now that all the cards have been reset, we can stop checking them
* all if there aren ' t as many */
spin_lock_irqsave ( & wc - > reglock , flags ) ;
wc - > mods_per_board = wc - > desc - > ports ;
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
BUG_ON ( wc - > desc - > ports % 4 ) ;
/* Detecting and configuring the modules over voicebus takes a
* significant amount of time . We can speed things up by performing
* this in parallel for each group of four modules . */
for ( x = 0 ; x < wc - > desc - > ports / 4 ; x + + )
bg_work [ x ] = bg_create ( wc , __wctdm_identify_module_group , x * 4 ) ;
for ( x = 0 ; bg_work [ x ] ; + + x )
bg_join ( bg_work [ x ] ) ;
wctdm_print_module_configuration ( wc ) ;
2010-08-28 05:59:27 +08:00
}
static struct pci_driver wctdm_driver ;
static void wctdm_back_out_gracefully ( struct wctdm * wc )
{
int i ;
unsigned long flags ;
2011-11-08 06:40:20 +08:00
struct vpmadt032 * vpm ;
2010-08-28 05:59:27 +08:00
LIST_HEAD ( local_list ) ;
2011-11-08 06:40:20 +08:00
spin_lock_irqsave ( & wc - > reglock , flags ) ;
if ( wc - > not_ready ) {
wc - > not_ready - - ;
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
while ( wctdm_wait_for_ready ( wc ) )
schedule ( ) ;
spin_lock_irqsave ( & wc - > reglock , flags ) ;
}
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
if ( wc - > vpmadt032 ) {
flush_workqueue ( wc - > vpmadt032 - > wq ) ;
clear_bit ( VPM150M_ACTIVE , & wc - > vpmadt032 - > control ) ;
flush_workqueue ( wc - > vpmadt032 - > wq ) ;
spin_lock_irqsave ( & wc - > reglock , flags ) ;
vpm = wc - > vpmadt032 ;
wc - > vpmadt032 = NULL ;
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
vpmadt032_free ( vpm ) ;
}
2010-08-28 05:59:27 +08:00
voicebus_release ( & wc - > vb ) ;
# ifdef CONFIG_VOICEBUS_ECREFERENCE
for ( i = 0 ; i < ARRAY_SIZE ( wc - > ec_reference ) ; + + i ) {
if ( wc - > ec_reference [ i ] )
dahdi_fifo_free ( wc - > ec_reference [ i ] ) ;
}
# endif
for ( i = 0 ; i < ARRAY_SIZE ( wc - > spans ) ; + + i ) {
if ( wc - > spans [ i ] & & wc - > spans [ i ] - > span . chans )
kfree ( wc - > spans [ i ] - > span . chans ) ;
kfree ( wc - > spans [ i ] ) ;
wc - > spans [ i ] = NULL ;
}
2011-06-03 04:03:38 +08:00
spin_lock_irqsave ( & wc - > reglock , flags ) ;
2011-06-03 04:03:15 +08:00
for ( i = 0 ; i < ARRAY_SIZE ( wc - > mods ) ; + + i ) {
2011-06-03 04:03:38 +08:00
struct wctdm_module * const mod = & wc - > mods [ i ] ;
2010-08-28 05:59:27 +08:00
kfree ( wc - > chans [ i ] ) ;
wc - > chans [ i ] = NULL ;
2011-06-03 04:03:38 +08:00
list_splice_init ( & mod - > pending_cmds , & local_list ) ;
list_splice_init ( & mod - > active_cmds , & local_list ) ;
}
2011-08-19 03:21:44 +08:00
list_splice_init ( & wc - > free_isr_commands , & local_list ) ;
2011-06-03 04:03:38 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
while ( ! list_empty ( & local_list ) ) {
struct wctdm_cmd * cmd ;
cmd = list_entry ( local_list . next ,
struct wctdm_cmd , node ) ;
list_del ( & cmd - > node ) ;
kfree ( cmd - > complete ) ;
kfree ( cmd ) ;
2010-08-28 05:59:27 +08:00
}
spin_lock_irqsave ( & wc - > frame_list_lock , flags ) ;
list_splice ( & wc - > frame_list , & local_list ) ;
spin_unlock_irqrestore ( & wc - > frame_list_lock , flags ) ;
while ( ! list_empty ( & local_list ) ) {
2011-06-03 04:03:38 +08:00
struct sframe_packet * frame ;
2010-08-28 05:59:27 +08:00
frame = list_entry ( local_list . next ,
struct sframe_packet , node ) ;
list_del ( & frame - > node ) ;
kfree ( frame ) ;
}
2011-06-03 04:02:47 +08:00
kfree ( wc - > board_name ) ;
2011-10-27 02:58:14 +08:00
kfree ( wc - > ddev - > devicetype ) ;
kfree ( wc - > ddev - > location ) ;
dahdi_free_device ( wc - > ddev ) ;
2010-08-28 05:59:27 +08:00
kfree ( wc ) ;
}
static const struct voicebus_operations voicebus_operations = {
. handle_receive = handle_receive ,
. handle_transmit = handle_transmit ,
} ;
static const struct voicebus_operations hx8_voicebus_operations = {
. handle_receive = handle_hx8_receive ,
. handle_transmit = handle_hx8_transmit ,
} ;
struct cmd_results {
u8 results [ 8 ] ;
} ;
static int hx8_send_command ( struct wctdm * wc , const u8 * command ,
size_t count , int checksum ,
int application , int bootloader ,
struct cmd_results * results )
{
int ret = 0 ;
struct vbb * vbb ;
struct sframe_packet * frame ;
const int MAX_COMMAND_LENGTH = 264 + 4 ;
unsigned long flags ;
2010-09-24 05:38:52 +08:00
dma_addr_t dma_addr ;
2010-08-28 05:59:27 +08:00
might_sleep ( ) ;
/* can't boot both into the application and the bootloader at once. */
WARN_ON ( ( application > 0 ) & & ( bootloader > 0 ) ) ;
if ( ( application > 0 ) & & ( bootloader > 0 ) )
return - EINVAL ;
WARN_ON ( count > MAX_COMMAND_LENGTH ) ;
if ( count > MAX_COMMAND_LENGTH )
return - EINVAL ;
2010-09-24 05:38:52 +08:00
vbb = dma_pool_alloc ( wc - > vb . pool , GFP_KERNEL , & dma_addr ) ;
2010-08-28 05:59:27 +08:00
WARN_ON ( ! vbb ) ;
if ( ! vbb )
return - ENOMEM ;
2010-09-24 05:38:52 +08:00
vbb - > dma_addr = dma_addr ;
2010-08-28 05:59:27 +08:00
memset ( vbb - > data , 0 , SFRAME_SIZE ) ;
memcpy ( & vbb - > data [ EFRAME_SIZE + EFRAME_GAP ] , command , count ) ;
vbb - > data [ EFRAME_SIZE ] = 0x80 | ( ( application ) ? 0 : 0x40 ) |
( ( checksum ) ? 0x20 : 0 ) | ( ( count & 0x100 ) > > 4 ) ;
vbb - > data [ EFRAME_SIZE + 1 ] = count & 0xff ;
if ( bootloader )
vbb - > data [ EFRAME_SIZE + 3 ] = 0xAA ;
spin_lock_irqsave ( & wc - > vb . lock , flags ) ;
voicebus_transmit ( & wc - > vb , vbb ) ;
spin_unlock_irqrestore ( & wc - > vb . lock , flags ) ;
/* Do not wait for the response if the caller doesn't care about the
* results . */
if ( NULL = = results )
return 0 ;
if ( ! wait_event_timeout ( wc - > regq , ! list_empty ( & wc - > frame_list ) , 2 * HZ ) ) {
dev_err ( & wc - > vb . pdev - > dev , " Timeout waiting "
" for receive frame. \n " ) ;
ret = - EIO ;
}
/* We only want the last packet received. Throw away anything else on
* the list */
frame = NULL ;
spin_lock_irqsave ( & wc - > frame_list_lock , flags ) ;
while ( ! list_empty ( & wc - > frame_list ) ) {
frame = list_entry ( wc - > frame_list . next ,
struct sframe_packet , node ) ;
list_del ( & frame - > node ) ;
if ( ! list_empty ( & wc - > frame_list ) ) {
kfree ( frame ) ;
frame = NULL ;
}
}
spin_unlock_irqrestore ( & wc - > frame_list_lock , flags ) ;
if ( frame ) {
memcpy ( results - > results , & frame - > sframe [ EFRAME_SIZE ] ,
sizeof ( results - > results ) ) ;
} else {
ret = - EIO ;
}
return ret ;
}
static int hx8_get_fpga_version ( struct wctdm * wc , u8 * major , u8 * minor )
{
int ret ;
struct cmd_results results ;
u8 command [ ] = { 0xD7 , 0x00 } ;
ret = hx8_send_command ( wc , command , ARRAY_SIZE ( command ) ,
0 , 0 , 0 , & results ) ;
if ( ret )
return ret ;
* major = results . results [ 0 ] ;
* minor = results . results [ 2 ] ;
return 0 ;
}
static void hx8_cleanup_frame_list ( struct wctdm * wc )
{
unsigned long flags ;
LIST_HEAD ( local_list ) ;
struct sframe_packet * frame ;
spin_lock_irqsave ( & wc - > frame_list_lock , flags ) ;
list_splice_init ( & wc - > frame_list , & local_list ) ;
spin_unlock_irqrestore ( & wc - > frame_list_lock , flags ) ;
while ( ! list_empty ( & local_list ) ) {
frame = list_entry ( local_list . next , struct sframe_packet , node ) ;
list_del ( & frame - > node ) ;
kfree ( frame ) ;
}
}
static int hx8_switch_to_application ( struct wctdm * wc )
{
int ret ;
u8 command [ ] = { 0xD7 , 0x00 } ;
ret = hx8_send_command ( wc , command , ARRAY_SIZE ( command ) ,
0 , 1 , 0 , NULL ) ;
if ( ret )
return ret ;
msleep ( 1000 ) ;
hx8_cleanup_frame_list ( wc ) ;
return 0 ;
}
/**
* hx8_switch_to_bootloader ( ) - Send packet to switch hx8 into bootloader
*
*/
static int hx8_switch_to_bootloader ( struct wctdm * wc )
{
int ret ;
u8 command [ ] = { 0xD7 , 0x00 } ;
ret = hx8_send_command ( wc , command , ARRAY_SIZE ( command ) ,
0 , 0 , 1 , NULL ) ;
if ( ret )
return ret ;
/* It takes some time for the FPGA to reload and switch it's
* configuration . */
msleep ( 300 ) ;
hx8_cleanup_frame_list ( wc ) ;
return 0 ;
}
struct ha80000_firmware {
u8 header [ 6 ] ;
u8 major_ver ;
u8 minor_ver ;
u8 data [ 54648 ] ;
2011-02-28 22:19:17 +08:00
__le32 chksum ;
2010-08-28 05:59:27 +08:00
} __attribute__ ( ( packed ) ) ;
static void hx8_send_dummy ( struct wctdm * wc )
{
u8 command [ ] = { 0xD7 , 0x00 } ;
hx8_send_command ( wc , command , ARRAY_SIZE ( command ) ,
0 , 0 , 0 , NULL ) ;
}
static int hx8_read_status_register ( struct wctdm * wc , u8 * status )
{
int ret ;
struct cmd_results results ;
u8 command [ ] = { 0xD7 , 0x00 } ;
ret = hx8_send_command ( wc , command , ARRAY_SIZE ( command ) ,
0 , 0 , 0 , & results ) ;
if ( ret )
return ret ;
* status = results . results [ 3 ] ;
return 0 ;
}
static const unsigned int HYBRID_PAGE_SIZE = 264 ;
static int hx8_write_buffer ( struct wctdm * wc , const u8 * buffer , size_t size )
{
int ret = 0 ;
struct cmd_results results ;
int padding_bytes = 0 ;
u8 * local_data ;
u8 command [ ] = { 0x84 , 0 , 0 , 0 } ;
if ( size > HYBRID_PAGE_SIZE )
return - EINVAL ;
if ( size < HYBRID_PAGE_SIZE )
padding_bytes = HYBRID_PAGE_SIZE - size ;
local_data = kmalloc ( sizeof ( command ) + size + padding_bytes , GFP_KERNEL ) ;
if ( ! local_data )
return - ENOMEM ;
memcpy ( local_data , command , sizeof ( command ) ) ;
memcpy ( & local_data [ sizeof ( command ) ] , buffer , size ) ;
memset ( & local_data [ sizeof ( command ) + size ] , 0xff , padding_bytes ) ;
ret = hx8_send_command ( wc , local_data ,
sizeof ( command ) + size + padding_bytes ,
1 , 0 , 0 , & results ) ;
if ( ret )
goto cleanup ;
cleanup :
kfree ( local_data ) ;
return ret ;
}
static int hx8_buffer_to_page ( struct wctdm * wc , const unsigned int page )
{
int ret ;
struct cmd_results results ;
u8 command [ ] = { 0x83 , ( page & 0x180 ) > > 7 , ( page & 0x7f ) < < 1 , 0x00 } ;
ret = hx8_send_command ( wc , command , sizeof ( command ) ,
1 , 0 , 0 , & results ) ;
if ( ret )
return ret ;
return 0 ;
}
static int hx8_wait_for_ready ( struct wctdm * wc , const int timeout )
{
int ret ;
u8 status ;
unsigned long local_timeout = jiffies + timeout ;
do {
ret = hx8_read_status_register ( wc , & status ) ;
if ( ret )
return ret ;
if ( ( status & 0x80 ) > 0 )
break ;
} while ( time_after ( local_timeout , jiffies ) ) ;
if ( time_after ( jiffies , local_timeout ) )
return - EIO ;
return 0 ;
}
/**
* hx8_reload_application - reload the application firmware
*
* NOTE : The caller should ensure that the board is in bootloader mode before
* calling this function .
*/
static int hx8_reload_application ( struct wctdm * wc , const struct ha80000_firmware * ha8_fw )
{
unsigned int cur_page ;
const u8 * data ;
u8 status ;
int ret = 0 ;
const int HYBRID_PAGE_COUNT = ( sizeof ( ha8_fw - > data ) ) / HYBRID_PAGE_SIZE ;
dev_info ( & wc - > vb . pdev - > dev , " Reloading firmware. Do not power down "
" the system until the process is complete. \n " ) ;
BUG_ON ( ! ha8_fw ) ;
might_sleep ( ) ;
data = & ha8_fw - > data [ 0 ] ;
ret = hx8_read_status_register ( wc , & status ) ;
if ( ret )
return ret ;
for ( cur_page = 0 ; cur_page < HYBRID_PAGE_COUNT ; + + cur_page ) {
ret = hx8_write_buffer ( wc , data , HYBRID_PAGE_SIZE ) ;
if ( ret )
return ret ;
/* The application starts out at page 0x100 */
ret = hx8_buffer_to_page ( wc , 0x100 + cur_page ) ;
if ( ret )
return ret ;
/* wait no more than a second for the write to the page to
* finish */
ret = hx8_wait_for_ready ( wc , HZ ) ;
if ( ret )
return ret ;
data + = HYBRID_PAGE_SIZE ;
}
return ret ;
}
static void print_hx8_recovery_message ( struct device * dev )
{
dev_warn ( dev , " The firmware may be corrupted. Please completely "
" power off your system, power on, and then reload the driver "
" with the 'forceload' module parameter set to 1 to attempt "
" recovery. \n " ) ;
}
/**
* hx8_check_firmware - Check the firmware version and load a new one possibly .
*
*/
static int hx8_check_firmware ( struct wctdm * wc )
{
int ret ;
u8 major ;
u8 minor ;
const struct firmware * fw ;
const struct ha80000_firmware * ha8_fw ;
struct device * dev = & wc - > vb . pdev - > dev ;
int retries = 10 ;
BUG_ON ( ! is_hx8 ( wc ) ) ;
might_sleep ( ) ;
do {
hx8_send_dummy ( wc ) ;
ret = hx8_get_fpga_version ( wc , & major , & minor ) ;
if ( ! ret )
break ;
if ( fatal_signal_pending ( current ) )
return - EINTR ;
} while ( - - retries ) ;
if ( ret ) {
print_hx8_recovery_message ( dev ) ;
return ret ;
}
/* If we're in the bootloader, try to jump into the application. */
if ( ( 1 = = major ) & & ( 0x80 = = minor ) & & ! forceload ) {
dev_dbg ( dev , " Switching to application. \n " ) ;
hx8_switch_to_application ( wc ) ;
ret = hx8_get_fpga_version ( wc , & major , & minor ) ;
if ( ret ) {
print_hx8_recovery_message ( dev ) ;
return ret ;
}
}
dev_dbg ( dev , " FPGA VERSION: %02x.%02x \n " , major , minor ) ;
ret = request_firmware ( & fw , " dahdi-fw-hx8.bin " , dev ) ;
if ( ret ) {
dev_warn ( dev , " Failed to load firmware from userspace, skipping "
" check. (%d) \n " , ret ) ;
return 0 ;
}
ha8_fw = ( const struct ha80000_firmware * ) fw - > data ;
if ( ( fw - > size ! = sizeof ( * ha8_fw ) ) | |
( 0 ! = memcmp ( " DIGIUM " , ha8_fw - > header , sizeof ( ha8_fw - > header ) ) ) | |
( ( crc32 ( ~ 0 , ( void * ) ha8_fw , sizeof ( * ha8_fw ) - sizeof ( u32 ) ) ^ ~ 0 ) ! =
2011-02-28 22:19:17 +08:00
le32_to_cpu ( ha8_fw - > chksum ) ) ) {
2010-08-28 05:59:27 +08:00
dev_warn ( dev , " Firmware file is invalid. Skipping load. \n " ) ;
ret = 0 ;
goto cleanup ;
}
dev_dbg ( dev , " FIRMWARE: %02x.%02x \n " , ha8_fw - > major_ver , ha8_fw - > minor_ver ) ;
if ( ha8_fw - > major_ver = = major & &
ha8_fw - > minor_ver = = minor ) {
dev_dbg ( dev , " Firmware versions match, skipping load. \n " ) ;
ret = 0 ;
goto cleanup ;
}
if ( 2 = = major ) {
hx8_switch_to_bootloader ( wc ) ;
ret = hx8_get_fpga_version ( wc , & major , & minor ) ;
if ( ret )
goto cleanup ;
}
/* so now we're in boot loader mode, ready to load the new firmware. */
ret = hx8_reload_application ( wc , ha8_fw ) ;
if ( ret )
goto cleanup ;
dev_dbg ( dev , " Firmware reloaded. Booting into application. \n " ) ;
hx8_switch_to_application ( wc ) ;
ret = hx8_get_fpga_version ( wc , & major , & minor ) ;
if ( ret )
goto cleanup ;
dev_dbg ( dev , " FPGA VERSION AFTER LOAD: %02x.%02x \n " , major , minor ) ;
if ( forceload ) {
dev_warn ( dev , " Please unset forceload if your card is able to "
" detect the installed modules. \n " ) ;
}
cleanup :
release_firmware ( fw ) ;
dev_info ( dev , " Hx8 firmware version: %d.%02d \n " , major , minor ) ;
return ret ;
}
# ifdef CONFIG_VOICEBUS_SYSFS
static ssize_t
voicebus_current_latency_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
unsigned long flags ;
struct wctdm * wc = dev_get_drvdata ( dev ) ;
unsigned int current_latency ;
spin_lock_irqsave ( & wc - > vb . lock , flags ) ;
current_latency = wc - > vb . min_tx_buffer_count ;
spin_unlock_irqrestore ( & wc - > vb . lock , flags ) ;
return sprintf ( buf , " %d \n " , current_latency ) ;
}
static DEVICE_ATTR ( voicebus_current_latency , 0400 ,
voicebus_current_latency_show , NULL ) ;
static ssize_t vpm_firmware_version_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
int res ;
u16 version = 0 ;
struct wctdm * wc = dev_get_drvdata ( dev ) ;
if ( wc - > vpmadt032 ) {
res = gpakPingDsp ( wc - > vpmadt032 - > dspid , & version ) ;
if ( res ) {
dev_info ( & wc - > vb . pdev - > dev , " Failed gpakPingDsp %d \n " , res ) ;
version = - 1 ;
}
}
return sprintf ( buf , " %x.%02x \n " , ( version & 0xff00 ) > > 8 , ( version & 0xff ) ) ;
}
static DEVICE_ATTR ( vpm_firmware_version , 0400 ,
vpm_firmware_version_show , NULL ) ;
2011-06-29 06:28:53 +08:00
static ssize_t
enable_vpm_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
unsigned long flags ;
struct wctdm * wc = dev_get_drvdata ( dev ) ;
unsigned int enable_vpm ;
spin_lock_irqsave ( & wc - > reglock , flags ) ;
enable_vpm = ( wc - > ctlreg & 0x10 ) ! = 0 ;
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
return sprintf ( buf , " %d \n " , enable_vpm ) ;
}
static ssize_t
enable_vpm_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
unsigned long flags ;
struct wctdm * wc = dev_get_drvdata ( dev ) ;
unsigned int enable_vpm ;
if ( count < 2 )
return - EINVAL ;
if ( ( ' 0 ' = = buf [ 0 ] ) | | ( 0 = = buf [ 0 ] ) )
enable_vpm = 0 ;
else
enable_vpm = 1 ;
spin_lock_irqsave ( & wc - > reglock , flags ) ;
if ( enable_vpm )
wc - > ctlreg | = 0x10 ;
else
wc - > ctlreg & = ~ 0x10 ;
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
return count ;
}
static DEVICE_ATTR ( enable_vpm , 0644 ,
enable_vpm_show , enable_vpm_store ) ;
2010-08-28 05:59:27 +08:00
static void create_sysfs_files ( struct wctdm * wc )
{
int ret ;
ret = device_create_file ( & wc - > vb . pdev - > dev ,
& dev_attr_voicebus_current_latency ) ;
if ( ret ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Failed to create device attributes. \n " ) ;
}
ret = device_create_file ( & wc - > vb . pdev - > dev ,
& dev_attr_vpm_firmware_version ) ;
if ( ret ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Failed to create device attributes. \n " ) ;
}
2011-06-29 06:28:53 +08:00
ret = device_create_file ( & wc - > vb . pdev - > dev ,
& dev_attr_enable_vpm ) ;
if ( ret ) {
dev_info ( & wc - > vb . pdev - > dev ,
" Failed to create device attributes. \n " ) ;
}
2010-08-28 05:59:27 +08:00
}
static void remove_sysfs_files ( struct wctdm * wc )
{
2011-06-29 06:28:53 +08:00
device_remove_file ( & wc - > vb . pdev - > dev ,
& dev_attr_enable_vpm ) ;
2010-08-28 05:59:27 +08:00
device_remove_file ( & wc - > vb . pdev - > dev ,
& dev_attr_vpm_firmware_version ) ;
device_remove_file ( & wc - > vb . pdev - > dev ,
& dev_attr_voicebus_current_latency ) ;
}
# else
static inline void create_sysfs_files ( struct wctdm * wc ) { return ; }
static inline void remove_sysfs_files ( struct wctdm * wc ) { return ; }
# endif /* CONFIG_VOICEBUS_SYSFS */
2011-03-16 03:06:05 +08:00
static void wctdm_set_tdm410_leds ( struct wctdm * wc )
{
int i ;
if ( 4 ! = wc - > desc - > ports )
return ;
wc - > tdm410leds = 0 ; /* all on by default */
for ( i = 0 ; i < wc - > desc - > ports ; + + i ) {
/* Turn off the LED for any module that isn't installed. */
2011-06-03 04:03:01 +08:00
if ( NONE = = wc - > mods [ i ] . type )
2011-03-16 03:06:05 +08:00
wc - > tdm410leds | = ( 1 < < i ) ;
}
}
2011-08-19 03:21:44 +08:00
/**
* wctdm_allocate_irq_commands - Preallocate some commands for use in interrupt context .
* @ wc : The board which we ' re allocating for .
* @ count : The number of IRQ commands to allocate .
*
* We need a minimum of 4 * the current latency worth of commands for each
* analog module . When the latency grows , new commands will be allocated , but
* this just represents are best guess as to the number of commands we ' ll need
* after probing for modules , and reduces the chance that we ' ll allocate
* memory in interrupt context when the driver first loads .
*
*/
static void wctdm_allocate_irq_commands ( struct wctdm * wc , unsigned int count )
{
unsigned long flags ;
LIST_HEAD ( local_list ) ;
if ( ! count )
return ;
while ( count - - ) {
struct wctdm_cmd * cmd ;
cmd = kzalloc ( sizeof ( * cmd ) , GFP_KERNEL ) ;
if ( ! cmd )
break ;
list_add ( & cmd - > node , & local_list ) ;
}
spin_lock_irqsave ( & wc - > reglock , flags ) ;
list_splice ( & local_list , & wc - > free_isr_commands ) ;
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
}
2010-08-28 05:59:27 +08:00
# ifdef USE_ASYNC_INIT
struct async_data {
struct pci_dev * pdev ;
const struct pci_device_id * ent ;
} ;
static int __devinit
__wctdm_init_one ( struct pci_dev * pdev , const struct pci_device_id * ent ,
async_cookie_t cookie )
# else
static int __devinit
__wctdm_init_one ( struct pci_dev * pdev , const struct pci_device_id * ent )
# endif
{
struct wctdm * wc ;
2011-06-03 04:03:29 +08:00
unsigned int pos ;
2010-08-28 05:59:27 +08:00
int i , ret ;
2011-08-31 00:37:57 +08:00
struct bg * vpm_work ;
2010-08-28 05:59:27 +08:00
int anamods , digimods , curchan , curspan ;
neonmwi_offlimit_cycles = neonmwi_offlimit / MS_PER_HOOKCHECK ;
wc = kzalloc ( sizeof ( * wc ) , GFP_KERNEL ) ;
if ( ! wc )
return - ENOMEM ;
2011-06-29 06:29:00 +08:00
wc - > not_ready = 1 ;
2011-06-29 06:28:53 +08:00
2010-08-28 05:59:27 +08:00
down ( & ifacelock ) ;
/* \todo this is a candidate for removal... */
2011-06-03 04:03:29 +08:00
for ( pos = 0 ; pos < WC_MAX_IFACES ; + + pos ) {
if ( ! ifaces [ pos ] ) {
ifaces [ pos ] = wc ;
2010-08-28 05:59:27 +08:00
break ;
}
}
up ( & ifacelock ) ;
2011-06-03 04:02:47 +08:00
wc - > desc = ( struct wctdm_desc * ) ent - > driver_data ;
/* This is to insure that the analog span is given lowest priority */
sema_init ( & wc - > syncsem , 1 ) ;
INIT_LIST_HEAD ( & wc - > frame_list ) ;
spin_lock_init ( & wc - > frame_list_lock ) ;
init_waitqueue_head ( & wc - > regq ) ;
spin_lock_init ( & wc - > reglock ) ;
2011-08-19 03:21:44 +08:00
INIT_LIST_HEAD ( & wc - > free_isr_commands ) ;
2011-06-03 04:02:47 +08:00
wc - > oldsync = - 1 ;
2011-06-03 04:03:29 +08:00
wc - > board_name = kasprintf ( GFP_KERNEL , " %s%d " , wctdm_driver . name , pos ) ;
2011-06-03 04:02:47 +08:00
if ( ! wc - > board_name ) {
wctdm_back_out_gracefully ( wc ) ;
return - ENOMEM ;
}
2010-08-28 05:59:27 +08:00
# ifdef CONFIG_VOICEBUS_ECREFERENCE
for ( i = 0 ; i < ARRAY_SIZE ( wc - > ec_reference ) ; + + i ) {
/* 256 is the smallest power of 2 that will contains the
* maximum possible amount of latency . */
wc - > ec_reference [ i ] = dahdi_fifo_alloc ( 256 , GFP_KERNEL ) ;
if ( IS_ERR ( wc - > ec_reference [ i ] ) ) {
ret = PTR_ERR ( wc - > ec_reference [ i ] ) ;
wc - > ec_reference [ i ] = NULL ;
wctdm_back_out_gracefully ( wc ) ;
return ret ;
}
}
# endif
pci_set_drvdata ( pdev , wc ) ;
wc - > vb . ops = & voicebus_operations ;
wc - > vb . pdev = pdev ;
wc - > vb . debug = & debug ;
if ( is_hx8 ( wc ) ) {
wc - > vb . ops = & hx8_voicebus_operations ;
ret = voicebus_boot_init ( & wc - > vb , wc - > board_name ) ;
} else {
wc - > vb . ops = & voicebus_operations ;
ret = voicebus_init ( & wc - > vb , wc - > board_name ) ;
voicebus_set_minlatency ( & wc - > vb , latency ) ;
voicebus_set_maxlatency ( & wc - > vb , max_latency ) ;
}
if ( ret ) {
2011-06-03 04:02:47 +08:00
wctdm_back_out_gracefully ( wc ) ;
2010-08-28 05:59:27 +08:00
return ret ;
}
create_sysfs_files ( wc ) ;
voicebus_lock_latency ( & wc - > vb ) ;
wc - > mods_per_board = NUM_MODULES ;
wc - > txident = 1 ;
if ( alawoverride ) {
companding = " alaw " ;
dev_info ( & wc - > vb . pdev - > dev , " The module parameter alawoverride " \
" has been deprecated. Please use the " \
" parameter companding=alaw instead " ) ;
}
if ( ! strcasecmp ( companding , " alaw " ) )
/* Force this card's companding to alaw */
wc - > companding = DAHDI_LAW_ALAW ;
else if ( ! strcasecmp ( companding , " ulaw " ) )
/* Force this card's companding to ulaw */
wc - > companding = DAHDI_LAW_MULAW ;
else
/* Auto detect this card's companding */
wc - > companding = DAHDI_LAW_DEFAULT ;
2011-06-03 04:03:38 +08:00
for ( i = 0 ; i < ARRAY_SIZE ( wc - > mods ) ; i + + ) {
2011-06-03 04:03:57 +08:00
struct wctdm_module * const mod = & wc - > mods [ i ] ;
INIT_LIST_HEAD ( & mod - > pending_cmds ) ;
INIT_LIST_HEAD ( & mod - > active_cmds ) ;
mod - > dacssrc = - 1 ;
mod - > card = i ;
set_offsets ( mod , 0 ) ;
2010-08-28 05:59:27 +08:00
}
/* Start the hardware processing. */
if ( voicebus_start ( & wc - > vb ) ) {
BUG_ON ( 1 ) ;
}
if ( is_hx8 ( wc ) ) {
ret = hx8_check_firmware ( wc ) ;
if ( ret ) {
2011-06-03 04:02:47 +08:00
wctdm_back_out_gracefully ( wc ) ;
2010-08-28 05:59:27 +08:00
return - EIO ;
}
/* Switch to the normal operating mode for this card. */
voicebus_stop ( & wc - > vb ) ;
wc - > vb . ops = & voicebus_operations ;
voicebus_set_minlatency ( & wc - > vb , latency ) ;
voicebus_set_maxlatency ( & wc - > vb , max_latency ) ;
voicebus_set_hx8_mode ( & wc - > vb ) ;
if ( voicebus_start ( & wc - > vb ) )
BUG_ON ( 1 ) ;
}
/* first we have to make sure that we process all module data, we'll fine-tune it later in this routine. */
wc - > avchannels = NUM_MODULES ;
2011-08-31 00:37:57 +08:00
vpm_work = bg_create ( wc , wctdm_initialize_vpm , 0 ) ;
if ( ! vpm_work ) {
wctdm_back_out_gracefully ( wc ) ;
return - ENOMEM ;
}
2010-08-28 05:59:27 +08:00
/* Now track down what modules are installed */
wctdm_identify_modules ( wc ) ;
2011-03-16 03:06:05 +08:00
wctdm_set_tdm410_leds ( wc ) ;
2010-08-28 05:59:27 +08:00
if ( fatal_signal_pending ( current ) ) {
wctdm_back_out_gracefully ( wc ) ;
2011-08-31 00:37:57 +08:00
bg_join ( vpm_work ) ;
2010-08-28 05:59:27 +08:00
return - EINTR ;
}
2011-08-31 00:37:57 +08:00
/* We need to wait for the vpm thread to finish before we setup the
* spans in order to ensure they are named properly . */
bg_join ( vpm_work ) ;
2010-08-28 05:59:27 +08:00
/*
* Walk the module list and create a 3 - channel span for every BRI module found .
* Empty and analog modules get a common span which is allocated outside of this loop .
*/
anamods = digimods = 0 ;
curchan = curspan = 0 ;
for ( i = 0 ; i < wc - > mods_per_board ; i + + ) {
2011-06-03 04:02:51 +08:00
struct wctdm_module * const mod = & wc - > mods [ i ] ;
2010-08-28 05:59:27 +08:00
struct b400m * b4 ;
2011-06-03 04:03:01 +08:00
if ( mod - > type = = NONE ) {
2010-08-28 05:59:27 +08:00
+ + curspan ;
continue ;
2011-06-03 04:03:01 +08:00
} else if ( mod - > type = = BRI ) {
2010-08-28 05:59:27 +08:00
if ( ! is_hx8 ( wc ) ) {
dev_info ( & wc - > vb . pdev - > dev , " Digital modules "
" detected on a non-hybrid card. "
" This is unsupported. \n " ) ;
wctdm_back_out_gracefully ( wc ) ;
2011-08-31 00:37:57 +08:00
bg_join ( vpm_work ) ;
2010-08-28 05:59:27 +08:00
return - EIO ;
}
2011-06-03 04:03:29 +08:00
wc - > spans [ curspan ] = wctdm_init_span ( wc , curspan ,
curchan , 3 , 1 ,
pos ) ;
2010-08-28 05:59:27 +08:00
if ( ! wc - > spans [ curspan ] ) {
wctdm_back_out_gracefully ( wc ) ;
2011-08-31 00:37:57 +08:00
bg_join ( vpm_work ) ;
2010-08-28 05:59:27 +08:00
return - EIO ;
}
2011-06-03 04:02:51 +08:00
b4 = mod - > mod . bri ;
2010-08-28 05:59:27 +08:00
b400m_set_dahdi_span ( b4 , i & 0x03 , wc - > spans [ curspan ] ) ;
+ + curspan ;
curchan + = 3 ;
if ( ! ( i & 0x03 ) ) {
b400m_post_init ( b4 ) ;
+ + digimods ;
}
} else {
/*
* FIXME : ABK :
* create a wctdm_chan for every analog module and link them into a span of their own down below .
* then evaluate all of the callbacks and hard - code whether they are receiving a dahdi_chan or wctdm_chan * .
* Finally , move the union from the wctdm structure to the dahdi_chan structure , and we should have something
* resembling a clean dynamic # of channels / dynamic # of spans driver .
*/
+ + curspan ;
+ + anamods ;
}
if ( digimods > 2 ) {
dev_info ( & wc - > vb . pdev - > dev , " More than two digital modules detected. This is unsupported. \n " ) ;
wctdm_back_out_gracefully ( wc ) ;
return - EIO ;
}
}
wc - > digi_mods = digimods ;
/* create an analog span if there are analog modules, or if there are no digital ones. */
if ( anamods | | ! digimods ) {
if ( ! digimods ) {
curspan = 0 ;
}
2011-06-03 04:03:29 +08:00
wctdm_init_span ( wc , curspan , curchan , wc - > desc - > ports , 0 , pos ) ;
2010-08-28 05:59:27 +08:00
wctdm_fixup_analog_span ( wc , curspan ) ;
wc - > aspan = wc - > spans [ curspan ] ;
curchan + = wc - > desc - > ports ;
+ + curspan ;
}
/* Now fix up the timeslots for the analog modules, since the digital
* modules are always first */
for ( i = 0 ; i < wc - > mods_per_board ; i + + ) {
2011-06-03 04:02:51 +08:00
struct wctdm_module * const mod = & wc - > mods [ i ] ;
switch ( mod - > type ) {
2011-06-03 04:03:01 +08:00
case FXS :
2011-06-03 04:03:15 +08:00
wctdm_proslic_set_ts ( wc , mod , ( digimods * 12 ) + i ) ;
2011-06-03 04:02:51 +08:00
break ;
2011-06-03 04:03:01 +08:00
case FXO :
2011-06-03 04:03:15 +08:00
wctdm_voicedaa_set_ts ( wc , mod , ( digimods * 12 ) + i ) ;
2011-06-03 04:02:51 +08:00
break ;
2011-06-03 04:03:01 +08:00
case QRV :
2011-06-03 04:03:15 +08:00
wctdm_qrvdri_set_ts ( wc , mod , ( digimods * 12 ) + i ) ;
2011-06-03 04:02:51 +08:00
break ;
default :
break ;
2010-08-28 05:59:27 +08:00
}
}
/* This shouldn't ever occur, but if we don't try to trap it, the driver
* will be scribbling into memory it doesn ' t own . */
BUG_ON ( curchan > 24 ) ;
wc - > avchannels = curchan ;
# ifdef USE_ASYNC_INIT
async_synchronize_cookie ( cookie ) ;
# endif
2011-10-27 02:58:14 +08:00
wc - > ddev = dahdi_create_device ( ) ;
wc - > ddev - > manufacturer = " Digium " ;
wc - > ddev - > location = kasprintf ( GFP_KERNEL , " PCI%s Bus %02d Slot %02d " ,
( wc - > desc - > flags & FLAG_EXPRESS ) ?
" Express " : " " ,
pdev - > bus - > number ,
PCI_SLOT ( pdev - > devfn ) + 1 ) ;
if ( ! wc - > ddev - > location ) {
wctdm_back_out_gracefully ( wc ) ;
return - ENOMEM ;
}
if ( wc - > vpmadt032 ) {
wc - > ddev - > devicetype = kasprintf ( GFP_KERNEL , " %s (VPMADT032) " ,
wc - > desc - > name ) ;
} else if ( wc - > vpmoct ) {
wc - > ddev - > devicetype = kasprintf ( GFP_KERNEL , " %s (VPMOCT032) " ,
wc - > desc - > name ) ;
} else {
wc - > ddev - > devicetype = kasprintf ( GFP_KERNEL , " %s " ,
wc - > desc - > name ) ;
}
if ( ! wc - > ddev - > devicetype ) {
wctdm_back_out_gracefully ( wc ) ;
return - ENOMEM ;
}
2010-08-28 05:59:27 +08:00
/* We should be ready for DAHDI to come in now. */
for ( i = 0 ; i < MAX_SPANS ; + + i ) {
2011-10-27 02:58:14 +08:00
struct dahdi_span * span ;
2010-08-28 05:59:27 +08:00
if ( ! wc - > spans [ i ] )
continue ;
2011-10-27 02:58:14 +08:00
span = & wc - > spans [ i ] - > span ;
list_add_tail ( & span - > device_node , & wc - > ddev - > spans ) ;
2010-08-28 05:59:27 +08:00
}
2011-08-19 03:21:44 +08:00
wctdm_allocate_irq_commands ( wc , anamods * latency * 4 ) ;
2011-10-27 02:58:14 +08:00
if ( dahdi_register_device ( wc - > ddev , & wc - > vb . pdev - > dev ) ) {
dev_notice ( & wc - > vb . pdev - > dev , " Unable to register device with DAHDI \n " ) ;
wctdm_back_out_gracefully ( wc ) ;
return - 1 ;
}
2010-08-28 05:59:27 +08:00
dev_info ( & wc - > vb . pdev - > dev ,
" Found a %s: %s (%d BRI spans, %d analog %s) \n " ,
( is_hx8 ( wc ) ) ? " Hybrid card " : " Wildcard TDM " ,
wc - > desc - > name , digimods * 4 , anamods ,
( anamods = = 1 ) ? " channel " : " channels " ) ;
ret = 0 ;
voicebus_unlock_latency ( & wc - > vb ) ;
return 0 ;
}
# ifdef USE_ASYNC_INIT
static __devinit void
wctdm_init_one_async ( void * data , async_cookie_t cookie )
{
struct async_data * dat = data ;
__wctdm_init_one ( dat - > pdev , dat - > ent , cookie ) ;
kfree ( dat ) ;
}
static int __devinit
wctdm_init_one ( struct pci_dev * pdev , const struct pci_device_id * ent )
{
struct async_data * dat ;
dat = kmalloc ( sizeof ( * dat ) , GFP_KERNEL ) ;
/* If we can't allocate the memory for the async_data, odds are we won't
* be able to initialize the device either , but let ' s try synchronously
* anyway . . . */
if ( ! dat )
return __wctdm_init_one ( pdev , ent , 0 ) ;
dat - > pdev = pdev ;
dat - > ent = ent ;
async_schedule ( wctdm_init_one_async , dat ) ;
return 0 ;
}
# else
static int __devinit
wctdm_init_one ( struct pci_dev * pdev , const struct pci_device_id * ent )
{
return __wctdm_init_one ( pdev , ent ) ;
}
# endif
static void wctdm_release ( struct wctdm * wc )
{
int i ;
2011-10-27 02:58:14 +08:00
if ( is_initialized ( wc ) )
dahdi_unregister_device ( wc - > ddev ) ;
2010-08-28 05:59:27 +08:00
down ( & ifacelock ) ;
for ( i = 0 ; i < WC_MAX_IFACES ; i + + )
if ( ifaces [ i ] = = wc )
break ;
ifaces [ i ] = NULL ;
up ( & ifacelock ) ;
wctdm_back_out_gracefully ( wc ) ;
}
static void __devexit wctdm_remove_one ( struct pci_dev * pdev )
{
int i ;
2011-06-29 06:29:00 +08:00
unsigned long flags ;
struct wctdm * wc = pci_get_drvdata ( pdev ) ;
struct vpmadt032 * vpmadt032 ;
struct vpmoct * vpmoct ;
2010-08-28 05:59:27 +08:00
2011-06-29 06:29:00 +08:00
if ( ! wc )
return ;
2010-08-28 05:59:27 +08:00
2011-06-29 06:29:00 +08:00
vpmadt032 = wc - > vpmadt032 ;
vpmoct = wc - > vpmoct ;
2010-08-28 05:59:27 +08:00
2011-06-29 06:29:00 +08:00
remove_sysfs_files ( wc ) ;
2010-08-28 05:59:27 +08:00
2011-06-29 06:29:00 +08:00
if ( vpmadt032 ) {
2011-08-31 00:37:57 +08:00
flush_workqueue ( vpmadt032 - > wq ) ;
2011-06-29 06:29:00 +08:00
clear_bit ( VPM150M_ACTIVE , & vpmadt032 - > control ) ;
2011-08-31 00:37:57 +08:00
flush_workqueue ( vpmadt032 - > wq ) ;
2011-06-29 06:29:00 +08:00
} else if ( vpmoct ) {
while ( wctdm_wait_for_ready ( wc ) )
schedule ( ) ;
}
2010-08-28 05:59:27 +08:00
2011-11-08 06:40:20 +08:00
flush_scheduled_work ( ) ;
2011-06-29 06:29:00 +08:00
/* shut down any BRI modules */
for ( i = 0 ; i < wc - > mods_per_board ; i + = 4 ) {
if ( wc - > mods [ i ] . type = = BRI )
wctdm_unload_b400m ( wc , i ) ;
}
2010-08-28 05:59:27 +08:00
2011-06-29 06:29:00 +08:00
voicebus_stop ( & wc - > vb ) ;
2010-08-28 05:59:27 +08:00
2011-06-29 06:29:00 +08:00
if ( vpmadt032 ) {
2011-08-31 00:38:06 +08:00
spin_lock_irqsave ( & wc - > reglock , flags ) ;
2011-06-29 06:29:00 +08:00
wc - > vpmadt032 = NULL ;
2011-08-31 00:38:06 +08:00
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
vpmadt032_free ( vpmadt032 ) ;
2011-06-29 06:29:00 +08:00
} else if ( vpmoct ) {
spin_lock_irqsave ( & wc - > reglock , flags ) ;
wc - > vpmoct = NULL ;
spin_unlock_irqrestore ( & wc - > reglock , flags ) ;
vpmoct_free ( vpmoct ) ;
2010-08-28 05:59:27 +08:00
}
2011-06-29 06:29:00 +08:00
dev_info ( & wc - > vb . pdev - > dev , " Freed a %s \n " ,
( is_hx8 ( wc ) ) ? " Hybrid card " : " Wildcard " ) ;
/* Release span */
wctdm_release ( wc ) ;
2010-08-28 05:59:27 +08:00
}
2011-01-04 02:26:24 +08:00
static DEFINE_PCI_DEVICE_TABLE ( wctdm_pci_tbl ) = {
2010-08-28 05:59:27 +08:00
{ 0xd161 , 0x2400 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , ( unsigned long ) & wctdm2400 } ,
{ 0xd161 , 0x0800 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , ( unsigned long ) & wctdm800 } ,
{ 0xd161 , 0x8002 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , ( unsigned long ) & wcaex800 } ,
{ 0xd161 , 0x8003 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , ( unsigned long ) & wcaex2400 } ,
{ 0xd161 , 0x8005 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , ( unsigned long ) & wctdm410 } ,
{ 0xd161 , 0x8006 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , ( unsigned long ) & wcaex410 } ,
{ 0xd161 , 0x8007 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , ( unsigned long ) & wcha80000 } ,
{ 0xd161 , 0x8008 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , ( unsigned long ) & wchb80000 } ,
{ 0 }
} ;
static void wctdm_shutdown ( struct pci_dev * pdev )
{
struct wctdm * wc = pci_get_drvdata ( pdev ) ;
2011-04-05 00:24:54 +08:00
voicebus_quiesce ( & wc - > vb ) ;
2010-08-28 05:59:27 +08:00
}
MODULE_DEVICE_TABLE ( pci , wctdm_pci_tbl ) ;
2011-04-05 00:25:09 +08:00
static int wctdm_suspend ( struct pci_dev * pdev , pm_message_t state )
{
return - ENOSYS ;
}
2010-08-28 05:59:27 +08:00
static struct pci_driver wctdm_driver = {
. name = " wctdm24xxp " ,
. probe = wctdm_init_one ,
. remove = __devexit_p ( wctdm_remove_one ) ,
. shutdown = wctdm_shutdown ,
2011-04-05 00:25:09 +08:00
. suspend = wctdm_suspend ,
2010-08-28 05:59:27 +08:00
. id_table = wctdm_pci_tbl ,
} ;
static int __init wctdm_init ( void )
{
int res ;
int x ;
for ( x = 0 ; x < ARRAY_SIZE ( fxo_modes ) ; x + + ) {
if ( ! strcmp ( fxo_modes [ x ] . name , opermode ) )
break ;
}
if ( x < ARRAY_SIZE ( fxo_modes ) ) {
_opermode = x ;
} else {
printk ( KERN_NOTICE " Invalid/unknown operating mode '%s' "
" specified. Please choose one of: \n " , opermode ) ;
for ( x = 0 ; x < ARRAY_SIZE ( fxo_modes ) ; x + + )
printk ( KERN_CONT " %s \n " , fxo_modes [ x ] . name ) ;
printk ( KERN_NOTICE " Note this option is CASE SENSITIVE! \n " ) ;
return - ENODEV ;
}
if ( ! strcmp ( opermode , " AUSTRALIA " ) ) {
boostringer = 1 ;
fxshonormode = 1 ;
}
2011-06-30 03:53:52 +08:00
if ( - 1 = = fastpickup ) {
if ( ! strcmp ( opermode , " JAPAN " ) )
fastpickup = 1 ;
else
fastpickup = 0 ;
}
2010-08-28 05:59:27 +08:00
/* for the voicedaa_check_hook defaults, if the user has not overridden
them by specifying them as module parameters , then get the values
from the selected operating mode
*/
if ( battdebounce = = 0 ) {
battdebounce = fxo_modes [ _opermode ] . battdebounce ;
}
if ( battalarm = = 0 ) {
battalarm = fxo_modes [ _opermode ] . battalarm ;
}
if ( battthresh = = 0 ) {
battthresh = fxo_modes [ _opermode ] . battthresh ;
}
b400m_module_init ( ) ;
res = dahdi_pci_module ( & wctdm_driver ) ;
if ( res )
return - ENODEV ;
# ifdef USE_ASYNC_INIT
async_synchronize_full ( ) ;
# endif
return 0 ;
}
static void __exit wctdm_cleanup ( void )
{
pci_unregister_driver ( & wctdm_driver ) ;
}
module_param ( debug , int , 0600 ) ;
2011-06-30 03:53:52 +08:00
module_param ( fastpickup , int , 0400 ) ;
MODULE_PARM_DESC ( fastpickup ,
" Set to 1 to shorten the calibration delay when taking " \
" an FXO port off hook. This can be required for Type-II " \
" CID. If -1 the calibration delay will depend on the " \
" current opermode. \n " ) ;
2010-08-28 05:59:27 +08:00
module_param ( fxovoltage , int , 0600 ) ;
module_param ( loopcurrent , int , 0600 ) ;
module_param ( reversepolarity , int , 0600 ) ;
# ifdef DEBUG
module_param ( robust , int , 0600 ) ;
2010-09-21 23:36:34 +08:00
module_param ( digitalloopback , int , 0400 ) ;
MODULE_PARM_DESC ( digitalloopback , " Set to 1 to place FXO modules into " \
" loopback mode for troubleshooting. " ) ;
2010-08-28 05:59:27 +08:00
# endif
module_param ( opermode , charp , 0600 ) ;
module_param ( lowpower , int , 0600 ) ;
module_param ( boostringer , int , 0600 ) ;
module_param ( fastringer , int , 0600 ) ;
module_param ( fxshonormode , int , 0600 ) ;
module_param ( battdebounce , uint , 0600 ) ;
module_param ( battalarm , uint , 0600 ) ;
module_param ( battthresh , uint , 0600 ) ;
module_param ( nativebridge , int , 0600 ) ;
module_param ( fxotxgain , int , 0600 ) ;
module_param ( fxorxgain , int , 0600 ) ;
module_param ( fxstxgain , int , 0600 ) ;
module_param ( fxsrxgain , int , 0600 ) ;
module_param ( ringdebounce , int , 0600 ) ;
module_param ( latency , int , 0400 ) ;
module_param ( max_latency , int , 0400 ) ;
module_param ( neonmwi_monitor , int , 0600 ) ;
module_param ( neonmwi_level , int , 0600 ) ;
module_param ( neonmwi_envelope , int , 0600 ) ;
module_param ( neonmwi_offlimit , int , 0600 ) ;
# ifdef VPM_SUPPORT
module_param ( vpmsupport , int , 0400 ) ;
module_param ( vpmnlptype , int , 0400 ) ;
module_param ( vpmnlpthresh , int , 0400 ) ;
module_param ( vpmnlpmaxsupp , int , 0400 ) ;
# endif
/* Module parameters backed by code in xhfc.c */
module_param ( bri_debug , int , 0600 ) ;
MODULE_PARM_DESC ( bri_debug , " bitmap: 1=general 2=dtmf 4=regops 8=fops 16=ec 32=st state 64=hdlc 128=alarm " ) ;
module_param ( bri_spanfilter , int , 0600 ) ;
MODULE_PARM_DESC ( bri_spanfilter , " debug filter for spans. bitmap: 1=port 1, 2=port 2, 4=port 3, 8=port 4 " ) ;
module_param ( bri_alarmdebounce , int , 0600 ) ;
MODULE_PARM_DESC ( bri_alarmdebounce , " msec to wait before set/clear alarm condition " ) ;
module_param ( bri_teignorered , int , 0600 ) ;
MODULE_PARM_DESC ( bri_teignorered , " 1=ignore (do not inform DAHDI) if a red alarm exists in TE mode " ) ;
module_param ( bri_persistentlayer1 , int , 0600 ) ;
MODULE_PARM_DESC ( bri_persistentlayer1 , " Set to 0 for disabling automatic layer 1 reactivation (when other end deactivates it) " ) ;
module_param ( timingcable , int , 0600 ) ;
MODULE_PARM_DESC ( timingcable , " Set to 1 for enabling timing cable. This means that *all* cards in the system are linked together with a single timing cable " ) ;
module_param ( forceload , int , 0600 ) ;
MODULE_PARM_DESC ( forceload , " Set to 1 in order to force an FPGA reload after power on (currently only for HA8/HB8 cards). " ) ;
module_param ( alawoverride , int , 0400 ) ;
2010-09-01 23:46:25 +08:00
MODULE_PARM_DESC ( alawoverride , " This option has been deprecated. Please use " \
2010-08-28 05:59:27 +08:00
" the parameter \" companding \" instead " ) ;
module_param ( companding , charp , 0400 ) ;
2010-09-01 23:46:25 +08:00
MODULE_PARM_DESC ( companding , " Change the companding to \" auto \" or \" alaw \" " \
" or \" ulaw \" . Auto (default) will set everything to ulaw " \
" unless a BRI module is installed. It will use alaw in that "
" case. " ) ;
2010-08-28 05:59:27 +08:00
MODULE_DESCRIPTION ( " VoiceBus Driver for Wildcard Analog and Hybrid Cards " ) ;
MODULE_AUTHOR ( " Digium Incorporated <support@digium.com> " ) ;
MODULE_ALIAS ( " wctdm8xxp " ) ;
MODULE_ALIAS ( " wctdm4xxp " ) ;
MODULE_ALIAS ( " wcaex24xx " ) ;
MODULE_ALIAS ( " wcaex8xx " ) ;
MODULE_ALIAS ( " wcaex4xx " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
module_init ( wctdm_init ) ;
module_exit ( wctdm_cleanup ) ;