blob: d5f923bcdffeb907191297082b61cd5551226c39 [file] [log] [blame]
/****************************************************************************
* ixj.c
*
* Device Driver for Quicknet Technologies, Inc.'s Telephony cards
* including the Internet PhoneJACK, Internet PhoneJACK Lite,
* Internet PhoneJACK PCI, Internet LineJACK, Internet PhoneCARD and
* SmartCABLE
*
* (c) Copyright 1999-2001 Quicknet Technologies, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Author: Ed Okerson, <eokerson@quicknet.net>
*
* Contributors: Greg Herlein, <gherlein@quicknet.net>
* David W. Erhart, <derhart@quicknet.net>
* John Sellers, <jsellers@quicknet.net>
* Mike Preston, <mpreston@quicknet.net>
*
* Fixes: David Huggins-Daines, <dhd@cepstral.com>
* Fabio Ferrari, <fabio.ferrari@digitro.com.br>
* Artis Kugevics, <artis@mt.lv>
* Daniele Bellucci, <bellucda@tiscali.it>
*
* More information about the hardware related to this driver can be found
* at our website: http://www.quicknet.net
*
* IN NO EVENT SHALL QUICKNET TECHNOLOGIES, INC. BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF QUICKNET
* TECHNOLOGIES, INC. HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* QUICKNET TECHNOLOGIES, INC. SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND QUICKNET TECHNOLOGIES, INC. HAS NO OBLIGATION
* TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
***************************************************************************/
/*
* Revision 4.8 2003/07/09 19:39:00 Daniele Bellucci
* Audit some copy_*_user and minor cleanup.
*
* Revision 4.7 2001/08/13 06:19:33 craigs
* Added additional changes from Alan Cox and John Anderson for
* 2.2 to 2.4 cleanup and bounds checking
*
* Revision 4.6 2001/08/13 01:05:05 craigs
* Really fixed PHONE_QUERY_CODEC problem this time
*
* Revision 4.5 2001/08/13 00:11:03 craigs
* Fixed problem in handling of PHONE_QUERY_CODEC, thanks to Shane Anderson
*
* Revision 4.4 2001/08/07 07:58:12 craigs
* Changed back to three digit version numbers
* Added tagbuild target to allow automatic and easy tagging of versions
*
* Revision 4.3 2001/08/07 07:24:47 craigs
* Added ixj-ver.h to allow easy configuration management of driver
* Added display of version number in /prox/ixj
*
* Revision 4.2 2001/08/06 07:07:19 craigs
* Reverted IXJCTL_DSP_TYPE and IXJCTL_DSP_VERSION files to original
* behaviour of returning int rather than short *
*
* Revision 4.1 2001/08/05 00:17:37 craigs
* More changes for correct PCMCIA installation
* Start of changes for backward Linux compatibility
*
* Revision 4.0 2001/08/04 12:33:12 craigs
* New version using GNU autoconf
*
* Revision 3.105 2001/07/20 23:14:32 eokerson
* More work on CallerID generation when using ring cadences.
*
* Revision 3.104 2001/07/06 01:33:55 eokerson
* Some bugfixes from Robert Vojta <vojta@ipex.cz> and a few mods to the Makefile.
*
* Revision 3.103 2001/07/05 19:20:16 eokerson
* Updated HOWTO
* Changed mic gain to 30dB on Internet LineJACK mic/speaker port.
*
* Revision 3.102 2001/07/03 23:51:21 eokerson
* Un-mute mic on Internet LineJACK when in speakerphone mode.
*
* Revision 3.101 2001/07/02 19:26:56 eokerson
* Removed initialiazation of ixjdebug and ixj_convert_loaded so they will go in the .bss instead of the .data
*
* Revision 3.100 2001/07/02 19:18:27 eokerson
* Changed driver to make dynamic allocation possible. We now pass IXJ * between functions instead of array indexes.
* Fixed the way the POTS and PSTN ports interact during a PSTN call to allow local answering.
* Fixed speaker mode on Internet LineJACK.
*
* Revision 3.99 2001/05/09 14:11:16 eokerson
* Fixed kmalloc error in ixj_build_filter_cadence. Thanks David Chan <cat@waulogy.stanford.edu>.
*
* Revision 3.98 2001/05/08 19:55:33 eokerson
* Fixed POTS hookstate detection while it is connected to PSTN port.
*
* Revision 3.97 2001/05/08 00:01:04 eokerson
* Fixed kernel oops when sending caller ID data.
*
* Revision 3.96 2001/05/04 23:09:30 eokerson
* Now uses one kernel timer for each card, instead of one for the entire driver.
*
* Revision 3.95 2001/04/25 22:06:47 eokerson
* Fixed squawking at beginning of some G.723.1 calls.
*
* Revision 3.94 2001/04/03 23:42:00 eokerson
* Added linear volume ioctls
* Added raw filter load ioctl
*
* Revision 3.93 2001/02/27 01:00:06 eokerson
* Fixed blocking in CallerID.
* Reduced size of ixj structure for smaller driver footprint.
*
* Revision 3.92 2001/02/20 22:02:59 eokerson
* Fixed isapnp and pcmcia module compatibility for 2.4.x kernels.
* Improved PSTN ring detection.
* Fixed wink generation on POTS ports.
*
* Revision 3.91 2001/02/13 00:55:44 eokerson
* Turn AEC back on after changing frame sizes.
*
* Revision 3.90 2001/02/12 16:42:00 eokerson
* Added ALAW codec, thanks to Fabio Ferrari for the table based converters to make ALAW from ULAW.
*
* Revision 3.89 2001/02/12 15:41:16 eokerson
* Fix from Artis Kugevics - Tone gains were not being set correctly.
*
* Revision 3.88 2001/02/05 23:25:42 eokerson
* Fixed lockup bugs with deregister.
*
* Revision 3.87 2001/01/29 21:00:39 eokerson
* Fix from Fabio Ferrari <fabio.ferrari@digitro.com.br> to properly handle EAGAIN and EINTR during non-blocking write.
* Updated copyright date.
*
* Revision 3.86 2001/01/23 23:53:46 eokerson
* Fixes to G.729 compatibility.
*
* Revision 3.85 2001/01/23 21:30:36 eokerson
* Added verbage about cards supported.
* Removed commands that put the card in low power mode at some times that it should not be in low power mode.
*
* Revision 3.84 2001/01/22 23:32:10 eokerson
* Some bugfixes from David Huggins-Daines, <dhd@cepstral.com> and other cleanups.
*
* Revision 3.83 2001/01/19 14:51:41 eokerson
* Fixed ixj_WriteDSPCommand to decrement usage counter when command fails.
*
* Revision 3.82 2001/01/19 00:34:49 eokerson
* Added verbosity to write overlap errors.
*
* Revision 3.81 2001/01/18 23:56:54 eokerson
* Fixed PSTN line test functions.
*
* Revision 3.80 2001/01/18 22:29:27 eokerson
* Updated AEC/AGC values for different cards.
*
* Revision 3.79 2001/01/17 02:58:54 eokerson
* Fixed AEC reset after Caller ID.
* Fixed Codec lockup after Caller ID on Call Waiting when not using 30ms frames.
*
* Revision 3.78 2001/01/16 19:43:09 eokerson
* Added support for Linux 2.4.x kernels.
*
* Revision 3.77 2001/01/09 04:00:52 eokerson
* Linetest will now test the line, even if it has previously succeeded.
*
* Revision 3.76 2001/01/08 19:27:00 eokerson
* Fixed problem with standard cable on Internet PhoneCARD.
*
* Revision 3.75 2000/12/22 16:52:14 eokerson
* Modified to allow hookstate detection on the POTS port when the PSTN port is selected.
*
* Revision 3.74 2000/12/08 22:41:50 eokerson
* Added capability for G729B.
*
* Revision 3.73 2000/12/07 23:35:16 eokerson
* Added capability to have different ring pattern before CallerID data.
* Added hookstate checks in CallerID routines to stop FSK.
*
* Revision 3.72 2000/12/06 19:31:31 eokerson
* Modified signal behavior to only send one signal per event.
*
* Revision 3.71 2000/12/06 03:23:08 eokerson
* Fixed CallerID on Call Waiting.
*
* Revision 3.70 2000/12/04 21:29:37 eokerson
* Added checking to Smart Cable gain functions.
*
* Revision 3.69 2000/12/04 21:05:20 eokerson
* Changed ixjdebug levels.
* Added ioctls to change gains in Internet Phone CARD Smart Cable.
*
* Revision 3.68 2000/12/04 00:17:21 craigs
* Changed mixer voice gain to +6dB rather than 0dB
*
* Revision 3.67 2000/11/30 21:25:51 eokerson
* Fixed write signal errors.
*
* Revision 3.66 2000/11/29 22:42:44 eokerson
* Fixed PSTN ring detect problems.
*
* Revision 3.65 2000/11/29 07:31:55 craigs
* Added new 425Hz filter co-efficients
* Added card-specific DTMF prescaler initialisation
*
* Revision 3.64 2000/11/28 14:03:32 craigs
* Changed certain mixer initialisations to be 0dB rather than 12dB
* Added additional information to /proc/ixj
*
* Revision 3.63 2000/11/28 11:38:41 craigs
* Added display of AEC modes in AUTO and AGC mode
*
* Revision 3.62 2000/11/28 04:05:44 eokerson
* Improved PSTN ring detection routine.
*
* Revision 3.61 2000/11/27 21:53:12 eokerson
* Fixed flash detection.
*
* Revision 3.60 2000/11/27 15:57:29 eokerson
* More work on G.729 load routines.
*
* Revision 3.59 2000/11/25 21:55:12 eokerson
* Fixed errors in G.729 load routine.
*
* Revision 3.58 2000/11/25 04:08:29 eokerson
* Added board locks around G.729 and TS85 load routines.
*
* Revision 3.57 2000/11/24 05:35:17 craigs
* Added ability to retrieve mixer values on LineJACK
* Added complete initialisation of all mixer values at startup
* Fixed spelling mistake
*
* Revision 3.56 2000/11/23 02:52:11 robertj
* Added cvs change log keyword.
* Fixed bug in capabilities list when using G.729 module.
*
*/
#include "ixj-ver.h"
#define PERFMON_STATS
#define IXJDEBUG 0
#define MAXRINGS 5
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/kernel.h> /* printk() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/poll.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/isapnp.h>
#include "ixj.h"
#define TYPE(inode) (iminor(inode) >> 4)
#define NUM(inode) (iminor(inode) & 0xf)
static DEFINE_MUTEX(ixj_mutex);
static int ixjdebug;
static int hertz = HZ;
static int samplerate = 100;
module_param(ixjdebug, int, 0);
static DEFINE_PCI_DEVICE_TABLE(ixj_pci_tbl) = {
{ PCI_VENDOR_ID_QUICKNET, PCI_DEVICE_ID_QUICKNET_XJ,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{ }
};
MODULE_DEVICE_TABLE(pci, ixj_pci_tbl);
/************************************************************************
*
* ixjdebug meanings are now bit mapped instead of level based
* Values can be or'ed together to turn on multiple messages
*
* bit 0 (0x0001) = any failure
* bit 1 (0x0002) = general messages
* bit 2 (0x0004) = POTS ringing related
* bit 3 (0x0008) = PSTN events
* bit 4 (0x0010) = PSTN Cadence state details
* bit 5 (0x0020) = Tone detection triggers
* bit 6 (0x0040) = Tone detection cadence details
* bit 7 (0x0080) = ioctl tracking
* bit 8 (0x0100) = signal tracking
* bit 9 (0x0200) = CallerID generation details
*
************************************************************************/
#ifdef IXJ_DYN_ALLOC
static IXJ *ixj[IXJMAX];
#define get_ixj(b) ixj[(b)]
/*
* Allocate a free IXJ device
*/
static IXJ *ixj_alloc()
{
for(cnt=0; cnt<IXJMAX; cnt++)
{
if(ixj[cnt] == NULL || !ixj[cnt]->DSPbase)
{
j = kmalloc(sizeof(IXJ), GFP_KERNEL);
if (j == NULL)
return NULL;
ixj[cnt] = j;
return j;
}
}
return NULL;
}
static void ixj_fsk_free(IXJ *j)
{
kfree(j->fskdata);
j->fskdata = NULL;
}
static void ixj_fsk_alloc(IXJ *j)
{
if(!j->fskdata) {
j->fskdata = kmalloc(8000, GFP_KERNEL);
if (!j->fskdata) {
if(ixjdebug & 0x0200) {
printk("IXJ phone%d - allocate failed\n", j->board);
}
return;
} else {
j->fsksize = 8000;
if(ixjdebug & 0x0200) {
printk("IXJ phone%d - allocate succeeded\n", j->board);
}
}
}
}
#else
static IXJ ixj[IXJMAX];
#define get_ixj(b) (&ixj[(b)])
/*
* Allocate a free IXJ device
*/
static IXJ *ixj_alloc(void)
{
int cnt;
for(cnt=0; cnt<IXJMAX; cnt++) {
if(!ixj[cnt].DSPbase)
return &ixj[cnt];
}
return NULL;
}
static inline void ixj_fsk_free(IXJ *j) {;}
static inline void ixj_fsk_alloc(IXJ *j)
{
j->fsksize = 8000;
}
#endif
#ifdef PERFMON_STATS
#define ixj_perfmon(x) ((x)++)
#else
#define ixj_perfmon(x) do { } while(0)
#endif
static int ixj_convert_loaded;
static int ixj_WriteDSPCommand(unsigned short, IXJ *j);
/************************************************************************
*
* These are function definitions to allow external modules to register
* enhanced functionality call backs.
*
************************************************************************/
static int Stub(IXJ * J, unsigned long arg)
{
return 0;
}
static IXJ_REGFUNC ixj_PreRead = &Stub;
static IXJ_REGFUNC ixj_PostRead = &Stub;
static IXJ_REGFUNC ixj_PreWrite = &Stub;
static IXJ_REGFUNC ixj_PostWrite = &Stub;
static void ixj_read_frame(IXJ *j);
static void ixj_write_frame(IXJ *j);
static void ixj_init_timer(IXJ *j);
static void ixj_add_timer(IXJ * j);
static void ixj_timeout(unsigned long ptr);
static int read_filters(IXJ *j);
static int LineMonitor(IXJ *j);
static int ixj_fasync(int fd, struct file *, int mode);
static int ixj_set_port(IXJ *j, int arg);
static int ixj_set_pots(IXJ *j, int arg);
static int ixj_hookstate(IXJ *j);
static int ixj_record_start(IXJ *j);
static void ixj_record_stop(IXJ *j);
static void set_rec_volume(IXJ *j, int volume);
static int get_rec_volume(IXJ *j);
static int set_rec_codec(IXJ *j, int rate);
static void ixj_vad(IXJ *j, int arg);
static int ixj_play_start(IXJ *j);
static void ixj_play_stop(IXJ *j);
static int ixj_set_tone_on(unsigned short arg, IXJ *j);
static int ixj_set_tone_off(unsigned short, IXJ *j);
static int ixj_play_tone(IXJ *j, char tone);
static void ixj_aec_start(IXJ *j, int level);
static int idle(IXJ *j);
static void ixj_ring_on(IXJ *j);
static void ixj_ring_off(IXJ *j);
static void aec_stop(IXJ *j);
static void ixj_ringback(IXJ *j);
static void ixj_busytone(IXJ *j);
static void ixj_dialtone(IXJ *j);
static void ixj_cpt_stop(IXJ *j);
static char daa_int_read(IXJ *j);
static char daa_CR_read(IXJ *j, int cr);
static int daa_set_mode(IXJ *j, int mode);
static int ixj_linetest(IXJ *j);
static int ixj_daa_write(IXJ *j);
static int ixj_daa_cid_read(IXJ *j);
static void DAA_Coeff_US(IXJ *j);
static void DAA_Coeff_UK(IXJ *j);
static void DAA_Coeff_France(IXJ *j);
static void DAA_Coeff_Germany(IXJ *j);
static void DAA_Coeff_Australia(IXJ *j);
static void DAA_Coeff_Japan(IXJ *j);
static int ixj_init_filter(IXJ *j, IXJ_FILTER * jf);
static int ixj_init_filter_raw(IXJ *j, IXJ_FILTER_RAW * jfr);
static int ixj_init_tone(IXJ *j, IXJ_TONE * ti);
static int ixj_build_cadence(IXJ *j, IXJ_CADENCE __user * cp);
static int ixj_build_filter_cadence(IXJ *j, IXJ_FILTER_CADENCE __user * cp);
/* Serial Control Interface funtions */
static int SCI_Control(IXJ *j, int control);
static int SCI_Prepare(IXJ *j);
static int SCI_WaitHighSCI(IXJ *j);
static int SCI_WaitLowSCI(IXJ *j);
static DWORD PCIEE_GetSerialNumber(WORD wAddress);
static int ixj_PCcontrol_wait(IXJ *j);
static void ixj_pre_cid(IXJ *j);
static void ixj_write_cid(IXJ *j);
static void ixj_write_cid_bit(IXJ *j, int bit);
static int set_base_frame(IXJ *j, int size);
static int set_play_codec(IXJ *j, int rate);
static void set_rec_depth(IXJ *j, int depth);
static int ixj_mixer(long val, IXJ *j);
/************************************************************************
CT8020/CT8021 Host Programmers Model
Host address Function Access
DSPbase +
0-1 Aux Software Status Register (reserved) Read Only
2-3 Software Status Register Read Only
4-5 Aux Software Control Register (reserved) Read Write
6-7 Software Control Register Read Write
8-9 Hardware Status Register Read Only
A-B Hardware Control Register Read Write
C-D Host Transmit (Write) Data Buffer Access Port (buffer input)Write Only
E-F Host Receive (Read) Data Buffer Access Port (buffer input) Read Only
************************************************************************/
static inline void ixj_read_HSR(IXJ *j)
{
j->hsr.bytes.low = inb_p(j->DSPbase + 8);
j->hsr.bytes.high = inb_p(j->DSPbase + 9);
}
static inline int IsControlReady(IXJ *j)
{
ixj_read_HSR(j);
return j->hsr.bits.controlrdy ? 1 : 0;
}
static inline int IsPCControlReady(IXJ *j)
{
j->pccr1.byte = inb_p(j->XILINXbase + 3);
return j->pccr1.bits.crr ? 1 : 0;
}
static inline int IsStatusReady(IXJ *j)
{
ixj_read_HSR(j);
return j->hsr.bits.statusrdy ? 1 : 0;
}
static inline int IsRxReady(IXJ *j)
{
ixj_read_HSR(j);
ixj_perfmon(j->rxreadycheck);
return j->hsr.bits.rxrdy ? 1 : 0;
}
static inline int IsTxReady(IXJ *j)
{
ixj_read_HSR(j);
ixj_perfmon(j->txreadycheck);
return j->hsr.bits.txrdy ? 1 : 0;
}
static inline void set_play_volume(IXJ *j, int volume)
{
if (ixjdebug & 0x0002)
printk(KERN_INFO "IXJ: /dev/phone%d Setting Play Volume to 0x%4.4x\n", j->board, volume);
ixj_WriteDSPCommand(0xCF02, j);
ixj_WriteDSPCommand(volume, j);
}
static int set_play_volume_linear(IXJ *j, int volume)
{
int newvolume, dspplaymax;
if (ixjdebug & 0x0002)
printk(KERN_INFO "IXJ: /dev/phone %d Setting Linear Play Volume to 0x%4.4x\n", j->board, volume);
if(volume > 100 || volume < 0) {
return -1;
}
/* This should normalize the perceived volumes between the different cards caused by differences in the hardware */
switch (j->cardtype) {
case QTI_PHONEJACK:
dspplaymax = 0x380;
break;
case QTI_LINEJACK:
if(j->port == PORT_PSTN) {
dspplaymax = 0x48;
} else {
dspplaymax = 0x100;
}
break;
case QTI_PHONEJACK_LITE:
dspplaymax = 0x380;
break;
case QTI_PHONEJACK_PCI:
dspplaymax = 0x6C;
break;
case QTI_PHONECARD:
dspplaymax = 0x50;
break;
default:
return -1;
}
newvolume = (dspplaymax * volume) / 100;
set_play_volume(j, newvolume);
return 0;
}
static inline void set_play_depth(IXJ *j, int depth)
{
if (depth > 60)
depth = 60;
if (depth < 0)
depth = 0;
ixj_WriteDSPCommand(0x5280 + depth, j);
}
static inline int get_play_volume(IXJ *j)
{
ixj_WriteDSPCommand(0xCF00, j);
return j->ssr.high << 8 | j->ssr.low;
}
static int get_play_volume_linear(IXJ *j)
{
int volume, newvolume, dspplaymax;
/* This should normalize the perceived volumes between the different cards caused by differences in the hardware */
switch (j->cardtype) {
case QTI_PHONEJACK:
dspplaymax = 0x380;
break;
case QTI_LINEJACK:
if(j->port == PORT_PSTN) {
dspplaymax = 0x48;
} else {
dspplaymax = 0x100;
}
break;
case QTI_PHONEJACK_LITE:
dspplaymax = 0x380;
break;
case QTI_PHONEJACK_PCI:
dspplaymax = 0x6C;
break;
case QTI_PHONECARD:
dspplaymax = 100;
break;
default:
return -1;
}
volume = get_play_volume(j);
newvolume = (volume * 100) / dspplaymax;
if(newvolume > 100)
newvolume = 100;
return newvolume;
}
static inline BYTE SLIC_GetState(IXJ *j)
{
if (j->cardtype == QTI_PHONECARD) {
j->pccr1.byte = 0;
j->psccr.bits.dev = 3;
j->psccr.bits.rw = 1;
outw_p(j->psccr.byte << 8, j->XILINXbase + 0x00);
ixj_PCcontrol_wait(j);
j->pslic.byte = inw_p(j->XILINXbase + 0x00) & 0xFF;
ixj_PCcontrol_wait(j);
if (j->pslic.bits.powerdown)
return PLD_SLIC_STATE_OC;
else if (!j->pslic.bits.ring0 && !j->pslic.bits.ring1)
return PLD_SLIC_STATE_ACTIVE;
else
return PLD_SLIC_STATE_RINGING;
} else {
j->pld_slicr.byte = inb_p(j->XILINXbase + 0x01);
}
return j->pld_slicr.bits.state;
}
static bool SLIC_SetState(BYTE byState, IXJ *j)
{
bool fRetVal = false;
if (j->cardtype == QTI_PHONECARD) {
if (j->flags.pcmciasct) {
switch (byState) {
case PLD_SLIC_STATE_TIPOPEN:
case PLD_SLIC_STATE_OC:
j->pslic.bits.powerdown = 1;
j->pslic.bits.ring0 = j->pslic.bits.ring1 = 0;
fRetVal = true;
break;
case PLD_SLIC_STATE_RINGING:
if (j->readers || j->writers) {
j->pslic.bits.powerdown = 0;
j->pslic.bits.ring0 = 1;
j->pslic.bits.ring1 = 0;
fRetVal = true;
}
break;
case PLD_SLIC_STATE_OHT: /* On-hook transmit */
case PLD_SLIC_STATE_STANDBY:
case PLD_SLIC_STATE_ACTIVE:
if (j->readers || j->writers) {
j->pslic.bits.powerdown = 0;
} else {
j->pslic.bits.powerdown = 1;
}
j->pslic.bits.ring0 = j->pslic.bits.ring1 = 0;
fRetVal = true;
break;
case PLD_SLIC_STATE_APR: /* Active polarity reversal */
case PLD_SLIC_STATE_OHTPR: /* OHT polarity reversal */
default:
fRetVal = false;
break;
}
j->psccr.bits.dev = 3;
j->psccr.bits.rw = 0;
outw_p(j->psccr.byte << 8 | j->pslic.byte, j->XILINXbase + 0x00);
ixj_PCcontrol_wait(j);
}
} else {
/* Set the C1, C2, C3 & B2EN signals. */
switch (byState) {
case PLD_SLIC_STATE_OC:
j->pld_slicw.bits.c1 = 0;
j->pld_slicw.bits.c2 = 0;
j->pld_slicw.bits.c3 = 0;
j->pld_slicw.bits.b2en = 0;
outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01);
fRetVal = true;
break;
case PLD_SLIC_STATE_RINGING:
j->pld_slicw.bits.c1 = 1;
j->pld_slicw.bits.c2 = 0;
j->pld_slicw.bits.c3 = 0;
j->pld_slicw.bits.b2en = 1;
outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01);
fRetVal = true;
break;
case PLD_SLIC_STATE_ACTIVE:
j->pld_slicw.bits.c1 = 0;
j->pld_slicw.bits.c2 = 1;
j->pld_slicw.bits.c3 = 0;
j->pld_slicw.bits.b2en = 0;
outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01);
fRetVal = true;
break;
case PLD_SLIC_STATE_OHT: /* On-hook transmit */
j->pld_slicw.bits.c1 = 1;
j->pld_slicw.bits.c2 = 1;
j->pld_slicw.bits.c3 = 0;
j->pld_slicw.bits.b2en = 0;
outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01);
fRetVal = true;
break;
case PLD_SLIC_STATE_TIPOPEN:
j->pld_slicw.bits.c1 = 0;
j->pld_slicw.bits.c2 = 0;
j->pld_slicw.bits.c3 = 1;
j->pld_slicw.bits.b2en = 0;
outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01);
fRetVal = true;
break;
case PLD_SLIC_STATE_STANDBY:
j->pld_slicw.bits.c1 = 1;
j->pld_slicw.bits.c2 = 0;
j->pld_slicw.bits.c3 = 1;
j->pld_slicw.bits.b2en = 1;
outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01);
fRetVal = true;
break;
case PLD_SLIC_STATE_APR: /* Active polarity reversal */
j->pld_slicw.bits.c1 = 0;
j->pld_slicw.bits.c2 = 1;
j->pld_slicw.bits.c3 = 1;
j->pld_slicw.bits.b2en = 0;
outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01);
fRetVal = true;
break;
case PLD_SLIC_STATE_OHTPR: /* OHT polarity reversal */
j->pld_slicw.bits.c1 = 1;
j->pld_slicw.bits.c2 = 1;
j->pld_slicw.bits.c3 = 1;
j->pld_slicw.bits.b2en = 0;
outb_p(j->pld_slicw.byte, j->XILINXbase + 0x01);
fRetVal = true;
break;
default:
fRetVal = false;
break;
}
}
return fRetVal;
}
static int ixj_wink(IXJ *j)
{
BYTE slicnow;
slicnow = SLIC_GetState(j);
j->pots_winkstart = jiffies;
SLIC_SetState(PLD_SLIC_STATE_OC, j);
msleep(jiffies_to_msecs(j->winktime));
SLIC_SetState(slicnow, j);
return 0;
}
static void ixj_init_timer(IXJ *j)
{
init_timer(&j->timer);
j->timer.function = ixj_timeout;
j->timer.data = (unsigned long)j;
}
static void ixj_add_timer(IXJ *j)
{
j->timer.expires = jiffies + (hertz / samplerate);
add_timer(&j->timer);
}
static void ixj_tone_timeout(IXJ *j)
{
IXJ_TONE ti;
j->tone_state++;
if (j->tone_state == 3) {
j->tone_state = 0;
if (j->cadence_t) {
j->tone_cadence_state++;
if (j->tone_cadence_state >= j->cadence_t->elements_used) {
switch (j->cadence_t->termination) {
case PLAY_ONCE:
ixj_cpt_stop(j);
break;
case REPEAT_LAST_ELEMENT:
j->tone_cadence_state--;
ixj_play_tone(j, j->cadence_t->ce[j->tone_cadence_state].index);
break;
case REPEAT_ALL:
j->tone_cadence_state = 0;
if (j->cadence_t->ce[j->tone_cadence_state].freq0) {
ti.tone_index = j->cadence_t->ce[j->tone_cadence_state].index;
ti.freq0 = j->cadence_t->ce[j->tone_cadence_state].freq0;
ti.gain0 = j->cadence_t->ce[j->tone_cadence_state].gain0;
ti.freq1 = j->cadence_t->ce[j->tone_cadence_state].freq1;
ti.gain1 = j->cadence_t->ce[j->tone_cadence_state].gain1;
ixj_init_tone(j, &ti);
}
ixj_set_tone_on(j->cadence_t->ce[0].tone_on_time, j);
ixj_set_tone_off(j->cadence_t->ce[0].tone_off_time, j);
ixj_play_tone(j, j->cadence_t->ce[0].index);
break;
}
} else {
if (j->cadence_t->ce[j->tone_cadence_state].gain0) {
ti.tone_index = j->cadence_t->ce[j->tone_cadence_state].index;
ti.freq0 = j->cadence_t->ce[j->tone_cadence_state].freq0;
ti.gain0 = j->cadence_t->ce[j->tone_cadence_state].gain0;
ti.freq1 = j->cadence_t->ce[j->tone_cadence_state].freq1;
ti.gain1 = j->cadence_t->ce[j->tone_cadence_state].gain1;
ixj_init_tone(j, &ti);
}
ixj_set_tone_on(j->cadence_t->ce[j->tone_cadence_state].tone_on_time, j);
ixj_set_tone_off(j->cadence_t->ce[j->tone_cadence_state].tone_off_time, j);
ixj_play_tone(j, j->cadence_t->ce[j->tone_cadence_state].index);
}
}
}
}
static inline void ixj_kill_fasync(IXJ *j, IXJ_SIGEVENT event, int dir)
{
if(j->ixj_signals[event]) {
if(ixjdebug & 0x0100)
printk("Sending signal for event %d\n", event);
/* Send apps notice of change */
/* see config.h for macro definition */
kill_fasync(&(j->async_queue), j->ixj_signals[event], dir);
}
}
static void ixj_pstn_state(IXJ *j)
{
int var;
union XOPXR0 XR0, daaint;
var = 10;
XR0.reg = j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.reg;
daaint.reg = 0;
XR0.bitreg.RMR = j->m_DAAShadowRegs.SOP_REGS.SOP.cr1.bitreg.RMR;
j->pld_scrr.byte = inb_p(j->XILINXbase);
if (j->pld_scrr.bits.daaflag) {
daa_int_read(j);
if(j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.RING) {
if(time_after(jiffies, j->pstn_sleeptil) && !(j->flags.pots_pstn && j->hookstate)) {
daaint.bitreg.RING = 1;
if(ixjdebug & 0x0008) {
printk(KERN_INFO "IXJ DAA Ring Interrupt /dev/phone%d at %ld\n", j->board, jiffies);
}
} else {
daa_set_mode(j, SOP_PU_RESET);
}
}
if(j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.Caller_ID) {
daaint.bitreg.Caller_ID = 1;
j->pstn_cid_intr = 1;
j->pstn_cid_received = jiffies;
if(ixjdebug & 0x0008) {
printk(KERN_INFO "IXJ DAA Caller_ID Interrupt /dev/phone%d at %ld\n", j->board, jiffies);
}
}
if(j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.Cadence) {
daaint.bitreg.Cadence = 1;
if(ixjdebug & 0x0008) {
printk(KERN_INFO "IXJ DAA Cadence Interrupt /dev/phone%d at %ld\n", j->board, jiffies);
}
}
if(j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.VDD_OK != XR0.bitreg.VDD_OK) {
daaint.bitreg.VDD_OK = 1;
daaint.bitreg.SI_0 = j->m_DAAShadowRegs.XOP_REGS.XOP.xr0.bitreg.VDD_OK;
}
}
daa_CR_read(j, 1);
if(j->m_DAAShadowRegs.SOP_REGS.SOP.cr1.bitreg.RMR != XR0.bitreg.RMR && time_after(jiffies, j->pstn_sleeptil) && !(j->flags.pots_pstn && j->hookstate)) {
daaint.bitreg.RMR = 1;
daaint.bitreg.SI_1 = j->m_DAAShadowRegs.SOP_REGS.SOP.cr1.bitreg.RMR;
if(ixjdebug & 0x0008) {
printk(KERN_INFO "IXJ DAA RMR /dev/phone%d was %s for %ld\n", j->board, XR0.bitreg.RMR?"on":"off", jiffies - j->pstn_last_rmr);
}
j->pstn_prev_rmr = j->pstn_last_rmr;
j->pstn_last_rmr = jiffies;
}
switch(j->daa_mode) {
case SOP_PU_SLEEP:
if (daaint.bitreg.RING) {
if (!j->flags.pstn_ringing) {
if (j->daa_mode != SOP_PU_RINGING) {
j->pstn_ring_int = jiffies;
daa_set_mode(j, SOP_PU_RINGING);
}
}
}
break;
case SOP_PU_RINGING:
if (daaint.bitreg.RMR) {
if (ixjdebug & 0x0008) {
printk(KERN_INFO "IXJ Ring Cadence a state = %d /dev/phone%d at %ld\n", j->cadence_f[4].state, j->board, jiffies);
}
if (daaint.bitreg.SI_1) { /* Rising edge of RMR */
j->flags.pstn_rmr = 1;
j->pstn_ring_start = jiffies;
j->pstn_ring_stop = 0;
j->ex.bits.pstn_ring = 0;
if (j->cadence_f[4].state == 0) {
j->cadence_f[4].state = 1;
j->cadence_f[4].on1min = jiffies + (long)((j->cadence_f[4].on1 * hertz * (100 - var)) / 10000);
j->cadence_f[4].on1dot = jiffies + (long)((j->cadence_f[4].on1 * hertz * (100)) / 10000);
j->cadence_f[4].on1max = jiffies + (long)((j->cadence_f[4].on1 * hertz * (100 + var)) / 10000);
} else if (j->cadence_f[4].state == 2) {
if((time_after(jiffies, j->cadence_f[4].off1min) &&
time_before(jiffies, j->cadence_f[4].off1max))) {
if (j->cadence_f[4].on2) {
j->cadence_f[4].state = 3;
j->cadence_f[4].on2min = jiffies + (long)((j->cadence_f[4].on2 * (hertz * (100 - var)) / 10000));
j->cadence_f[4].on2dot = jiffies + (long)((j->cadence_f[4].on2 * (hertz * (100)) / 10000));
j->cadence_f[4].on2max = jiffies + (long)((j->cadence_f[4].on2 * (hertz * (100 + var)) / 10000));
} else {
j->cadence_f[4].state = 7;
}
} else {
if (ixjdebug & 0x0008) {
printk(KERN_INFO "IXJ Ring Cadence fail state = %d /dev/phone%d at %ld should be %d\n",
j->cadence_f[4].state, j->board, jiffies - j->pstn_prev_rmr,
j->cadence_f[4].off1);
}
j->cadence_f[4].state = 0;
}
} else if (j->cadence_f[4].state == 4) {
if((time_after(jiffies, j->cadence_f[4].off2min) &&
time_before(jiffies, j->cadence_f[4].off2max))) {
if (j->cadence_f[4].on3) {
j->cadence_f[4].state = 5;
j->cadence_f[4].on3min = jiffies + (long)((j->cadence_f[4].on3 * (hertz * (100 - var)) / 10000));
j->cadence_f[4].on3dot = jiffies + (long)((j->cadence_f[4].on3 * (hertz * (100)) / 10000));
j->cadence_f[4].on3max = jiffies + (long)((j->cadence_f[4].on3 * (hertz * (100 + var)) / 10000));
} else {
j->cadence_f[4].state = 7;
}
} else {
if (ixjdebug & 0x0008) {
printk(KERN_INFO "IXJ Ring Cadence fail state = %d /dev/phone%d at %ld should be %d\n",
j->cadence_f[4].state, j->board, jiffies - j->pstn_prev_rmr,
j->cadence_f[4].off2);
}
j->cadence_f[4].state = 0;
}
} else if (j->cadence_f[4].state == 6) {
if((time_after(jiffies, j->cadence_f[4].off3min) &&
time_before(jiffies, j->cadence_f[4].off3max))) {
j->cadence_f[4].state = 7;
} else {
if (ixjdebug & 0x0008) {
printk(KERN_INFO "IXJ Ring Cadence fail state = %d /dev/phone%d at %ld should be %d\n",
j->cadence_f[4].state, j->board, jiffies - j->pstn_prev_rmr,
j->cadence_f[4].off3);
}
j->cadence_f[4].state = 0;
}
} else {
j->cadence_f[4].state = 0;
}
} else { /* Falling edge of RMR */
j->pstn_ring_start = 0;
j->pstn_ring_stop = jiffies;
if (j->cadence_f[4].state == 1) {
if(!j->cadence_f[4].on1) {
j->cadence_f[4].state = 7;
} else if((time_after(jiffies, j->cadence_f[4].on1min) &&
time_before(jiffies, j->cadence_f[4].on1max))) {
if (j->cadence_f[4].off1) {
j->cadence_f[4].state = 2;
j->cadence_f[4].off1min = jiffies + (long)((j->cadence_f[4].off1 * (hertz * (100 - var)) / 10000));
j->cadence_f[4].off1dot = jiffies + (long)((j->cadence_f[4].off1 * (hertz * (100)) / 10000));
j->cadence_f[4].off1max = jiffies + (long)((j->cadence_f[4].off1 * (hertz * (100 + var)) / 10000));
} else {
j->cadence_f[4].state = 7;
}
} else {
if (ixjdebug & 0x0008) {
printk(KERN_INFO "IXJ Ring Cadence fail state = %d /dev/phone%d at %ld should be %d\n",
j->cadence_f[4].state, j->board, jiffies - j->pstn_prev_rmr,
j->cadence_f[4].on1);
}
j->cadence_f[4].state = 0;
}
} else if (j->cadence_f[4].state == 3) {
if((time_after(jiffies, j->cadence_f[4].on2min) &&
time_before(jiffies, j->cadence_f[4].on2max))) {
if (j->cadence_f[4].off2) {
j->cadence_f[4].state = 4;
j->cadence_f[4].off2min = jiffies + (long)((j->cadence_f[4].off2 * (hertz * (100 - var)) / 10000));
j->cadence_f[4].off2dot = jiffies + (long)((j->cadence_f[4].off2 * (hertz * (100)) / 10000));
j->cadence_f[4].off2max = jiffies + (long)((j->cadence_f[4].off2 * (hertz * (100 + var)) / 10000));
} else {
j->cadence_f[4].state = 7;
}
} else {
if (ixjdebug & 0x0008) {
printk(KERN_INFO "IXJ Ring Cadence fail state = %d /dev/phone%d at %ld should be %d\n",
j->cadence_f[4].state, j->board, jiffies - j->pstn_prev_rmr,
j->cadence_f[4].on2);
}
j->cadence_f[4].state = 0;
}
} else if (j->cadence_f[4].state == 5) {
if((time_after(jiffies, j->cadence_f[4].on3min) &&
time_before(jiffies, j->cadence_f[4].on3max))) {
if (j->cadence_f[4].off3) {
j->cadence_f[4].state = 6;
j->cadence_f[4].off3min = jiffies + (long)((j->cadence_f[4].off3 * (hertz * (100 - var)) / 10000));
j->cadence_f[4].off3dot = jiffies + (long)((j->cadence_f[4].off3 * (hertz * (100)) / 10000));
j->cadence_f[4].off3max = jiffies + (long)((j->cadence_f[4].off3 * (hertz * (100 + var)) / 10000));
} else {
j->cadence_f[4].state = 7;
}
} else {
j->cadence_f[4].state = 0;
}
} else {
if (ixjdebug & 0x0008) {
printk(KERN_INFO "IXJ Ring Cadence fail state = %d /dev/phone%d at %ld should be %d\n",
j->cadence_f[4].state, j->board, jiffies - j->pstn_prev_rmr,
j->cadence_f[4].on3);
}
j->cadence_f[4].state = 0;
}
}
if (ixjdebug & 0x0010) {
printk(KERN_INFO "IXJ Ring Cadence b state = %d /dev/phone%d at %ld\n", j->cadence_f[4].state, j->board, jiffies);
}
if (ixjdebug & 0x0010) {
switch(j->cadence_f[4].state) {
case 1:
printk(KERN_INFO "IXJ /dev/phone%d Next Ring Cadence state at %u min %ld - %ld - max %ld\n", j->board,
j->cadence_f[4].on1, j->cadence_f[4].on1min, j->cadence_f[4].on1dot, j->cadence_f[4].on1max);
break;
case 2:
printk(KERN_INFO "IXJ /dev/phone%d Next Ring Cadence state at %u min %ld - %ld - max %ld\n", j->board,
j->cadence_f[4].off1, j->cadence_f[4].off1min, j->cadence_f[4].off1dot, j->cadence_f[4].off1max);
break;
case 3:
printk(KERN_INFO "IXJ /dev/phone%d Next Ring Cadence state at %u min %ld - %ld - max %ld\n", j->board,
j->cadence_f[4].on2, j->cadence_f[4].on2min, j->cadence_f[4].on2dot, j->cadence_f[4].on2max);
break;
case 4:
printk(KERN_INFO "IXJ /dev/phone%d Next Ring Cadence state at %u min %ld - %ld - max %ld\n", j->board,
j->cadence_f[4].off2, j->cadence_f[4].off2min, j->cadence_f[4].off2dot, j->cadence_f[4].off2max);
break;
case 5:
printk(KERN_INFO "IXJ /dev/phone%d Next Ring Cadence state at %u min %ld - %ld - max %ld\n", j->board,
j->cadence_f[4].on3, j->cadence_f[4].on3min, j->cadence_f[4].on3dot, j->cadence_f[4].on3max);
break;
case 6:
printk(KERN_INFO "IXJ /dev/phone%d Next Ring Cadence state at %u min %ld - %ld - max %ld\n", j->board,
j->cadence_f[4].off3, j->cadence_f[4].off3min, j->cadence_f[4].off3dot, j->cadence_f[4].off3max);
break;
}
}
}
if (j->cadence_f[4].state == 7) {
j->cadence_f[4].state = 0;
j->pstn_ring_stop = jiffies;
j->ex.bits.pstn_ring = 1;
ixj_kill_fasync(j, SIG_PSTN_RING, POLL_IN);
if(ixjdebug & 0x0008) {
printk(KERN_INFO "IXJ Ring int set /dev/phone%d at %ld\n", j->board, jiffies);
}
}
if((j->pstn_ring_int != 0 && time_after(jiffies, j->pstn_ring_int + (hertz * 5)) && !j->flags.pstn_rmr) ||
(j->pstn_ring_stop != 0 && time_after(jiffies, j->pstn_ring_stop + (hertz * 5)))) {
if(ixjdebug & 0x0008) {
printk("IXJ DAA no ring in 5 seconds /dev/phone%d at %ld\n", j->board, jiffies);
printk("IXJ DAA pstn ring int /dev/phone%d at %ld\n", j->board, j->pstn_ring_int);
printk("IXJ DAA pstn ring stop /dev/phone%d at %ld\n", j->board, j->pstn_ring_stop);
}
j->pstn_ring_stop = j->pstn_ring_int = 0;
daa_set_mode(j, SOP_PU_SLEEP);
}
outb_p(j->pld_scrw.byte, j->XILINXbase);
if (j->pstn_cid_intr && time_after(jiffies, j->pstn_cid_received + hertz)) {
ixj_daa_cid_read(j);
j->ex.bits.caller_id = 1;
ixj_kill_fasync(j, SIG_CALLER_ID, POLL_IN);
j->pstn_cid_intr = 0;
}
if (daaint.bitreg.Cadence) {
if(ixjdebug & 0x0008) {
printk("IXJ DAA Cadence interrupt going to sleep /dev/phone%d\n", j->board);
}
daa_set_mode(j, SOP_PU_SLEEP);
j->ex.bits.pstn_ring = 0;
}
break;
case SOP_PU_CONVERSATION:
if (daaint.bitreg.VDD_OK) {
if(!daaint.bitreg.SI_0) {
if (!j->pstn_winkstart) {
if(ixjdebug & 0x0008) {
printk("IXJ DAA possible wink /dev/phone%d %ld\n", j->board, jiffies);
}
j->pstn_winkstart = jiffies;
}
} else {
if (j->pstn_winkstart) {
if(ixjdebug & 0x0008) {
printk("IXJ DAA possible wink end /dev/phone%d %ld\n", j->board, jiffies);
}
j->pstn_winkstart = 0;
}
}
}
if (j->pstn_winkstart && time_after(jiffies, j->pstn_winkstart + ((hertz * j->winktime) / 1000))) {
if(ixjdebug & 0x0008) {
printk("IXJ DAA wink detected going to sleep /dev/phone%d %ld\n", j->board, jiffies);
}
daa_set_mode(j, SOP_PU_SLEEP);
j->pstn_winkstart = 0;
j->ex.bits.pstn_wink = 1;
ixj_kill_fasync(j, SIG_PSTN_WINK, POLL_IN);
}
break;
}
}
static void ixj_timeout(unsigned long ptr)
{
int board;
unsigned long jifon;
IXJ *j = (IXJ *)ptr;
board = j->board;
if (j->DSPbase && atomic_read(&j->DSPWrite) == 0 && test_and_set_bit(board, (void *)&j->busyflags) == 0) {
ixj_perfmon(j->timerchecks);
j->hookstate = ixj_hookstate(j);
if (j->tone_state) {
if (!(j->hookstate)) {
ixj_cpt_stop(j);
if (j->m_hook) {
j->m_hook = 0;
j->ex.bits.hookstate = 1;
ixj_kill_fasync(j, SIG_HOOKSTATE, POLL_IN);
}
clear_bit(board, &j->busyflags);
ixj_add_timer(j);
return;
}
if (j->tone_state == 1)
jifon = ((hertz * j->tone_on_time) * 25 / 100000);
else
jifon = ((hertz * j->tone_on_time) * 25 / 100000) + ((hertz * j->tone_off_time) * 25 / 100000);
if (time_before(jiffies, j->tone_start_jif + jifon)) {
if (j->tone_state == 1) {
ixj_play_tone(j, j->tone_index);
if (j->dsp.low == 0x20) {
clear_bit(board, &j->busyflags);
ixj_add_timer(j);
return;
}
} else {
ixj_play_tone(j, 0);
if (j->dsp.low == 0x20) {
clear_bit(board, &j->busyflags);
ixj_add_timer(j);
return;
}
}
} else {
ixj_tone_timeout(j);
if (j->flags.dialtone) {
ixj_dialtone(j);
}
if (j->flags.busytone) {
ixj_busytone(j);
if (j->dsp.low == 0x20) {
clear_bit(board, &j->busyflags);
ixj_add_timer(j);
return;
}
}
if (j->flags.ringback) {
ixj_ringback(j);
if (j->dsp.low == 0x20) {
clear_bit(board, &j->busyflags);
ixj_add_timer(j);
return;
}
}
if (!j->tone_state) {
ixj_cpt_stop(j);
}
}
}
if (!(j->tone_state && j->dsp.low == 0x20)) {
if (IsRxReady(j)) {
ixj_read_frame(j);
}
if (IsTxReady(j)) {
ixj_write_frame(j);
}
}
if (j->flags.cringing) {
if (j->hookstate & 1) {
j->flags.cringing = 0;
ixj_ring_off(j);
} else if(j->cadence_f[5].enable && ((!j->cadence_f[5].en_filter) || (j->cadence_f[5].en_filter && j->flags.firstring))) {
switch(j->cadence_f[5].state) {
case 0:
j->cadence_f[5].on1dot = jiffies + (long)((j->cadence_f[5].on1 * (hertz * 100) / 10000));
if (time_before(jiffies, j->cadence_f[5].on1dot)) {
if(ixjdebug & 0x0004) {
printk("Ringing cadence state = %d - %ld\n", j->cadence_f[5].state, jiffies);
}
ixj_ring_on(j);
}
j->cadence_f[5].state = 1;
break;
case 1:
if (time_after(jiffies, j->cadence_f[5].on1dot)) {
j->cadence_f[5].off1dot = jiffies + (long)((j->cadence_f[5].off1 * (hertz * 100) / 10000));
if(ixjdebug & 0x0004) {
printk("Ringing cadence state = %d - %ld\n", j->cadence_f[5].state, jiffies);
}
ixj_ring_off(j);
j->cadence_f[5].state = 2;
}
break;
case 2:
if (time_after(jiffies, j->cadence_f[5].off1dot)) {
if(ixjdebug & 0x0004) {
printk("Ringing cadence state = %d - %ld\n", j->cadence_f[5].state, jiffies);
}
ixj_ring_on(j);
if (j->cadence_f[5].on2) {
j->cadence_f[5].on2dot = jiffies + (long)((j->cadence_f[5].on2 * (hertz * 100) / 10000));
j->cadence_f[5].state = 3;
} else {
j->cadence_f[5].state = 7;
}
}
break;
case 3:
if (time_after(jiffies, j->cadence_f[5].on2dot)) {
if(ixjdebug & 0x0004) {
printk("Ringing cadence state = %d - %ld\n", j->cadence_f[5].state, jiffies);
}
ixj_ring_off(j);
if (j->cadence_f[5].off2) {
j->cadence_f[5].off2dot = jiffies + (long)((j->cadence_f[5].off2 * (hertz * 100) / 10000));
j->cadence_f[5].state = 4;
} else {
j->cadence_f[5].state = 7;
}
}
break;
case 4:
if (time_after(jiffies, j->cadence_f[5].off2dot)) {
if(ixjdebug & 0x0004) {
printk("Ringing cadence state = %d - %ld\n", j->cadence_f[5].state, jiffies);
}
ixj_ring_on(j);
if (j->cadence_f[5].on3) {
j->cadence_f[5].on3dot = jiffies + (long)((j->cadence_f[5].on3 * (hertz * 100) / 10000));
j->cadence_f[5].state = 5;
} else {
j->cadence_f[5].state = 7;
}
}
break;
case 5:
if (time_after(jiffies, j->cadence_f[5].on3dot)) {
if(ixjdebug & 0x0004) {
printk("Ringing cadence state = %d - %ld\n", j->cadence_f[5].state, jiffies);
}
ixj_ring_off(j);
if (j->cadence_f[5].off3) {
j->cadence_f[5].off3dot = jiffies + (long)((j->cadence_f[5].off3 * (hertz * 100) / 10000));
j->cadence_f[5].state = 6;
} else {
j->cadence_f[5].state = 7;
}
}
break;
case 6:
if (time_after(jiffies, j->cadence_f[5].off3dot)) {
if(ixjdebug & 0x0004) {
printk("Ringing cadence state = %d - %ld\n", j->cadence_f[5].state, jiffies);
}
j->cadence_f[5].state = 7;
}
break;
case 7:
if(ixjdebug & 0x0004) {
printk("Ringing cadence state = %d - %ld\n", j->cadence_f[5].state, jiffies);
}
j->flags.cidring = 1;
j->cadence_f[5].state = 0;
break;
}
if (j->flags.cidring && !j->flags.cidsent) {
j->flags.cidsent = 1;
if(j->fskdcnt) {
SLIC_SetState(PLD_SLIC_STATE_OHT, j);
ixj_pre_cid(j);
}
j->flags.cidring = 0;
}
clear_bit(board, &j->busyflags);
ixj_add_timer(j);
return;
} else {
if (time_after(jiffies, j->ring_cadence_jif + (hertz / 2))) {
if (j->flags.cidring && !j->flags.cidsent) {
j->flags.cidsent = 1;
if(j->fskdcnt) {
SLIC_SetState(PLD_SLIC_STATE_OHT, j);
ixj_pre_cid(j);
}
j->flags.cidring = 0;
}
j->ring_cadence_t--;
if (j->ring_cadence_t == -1)
j->ring_cadence_t = 15;
j->ring_cadence_jif = jiffies;
if (j->ring_cadence & 1 << j->ring_cadence_t) {
if(j->flags.cidsent && j->cadence_f[5].en_filter)
j->flags.firstring = 1;
else
ixj_ring_on(j);
} else {
ixj_ring_off(j);
if(!j->flags.cidsent)
j->flags.cidring = 1;
}
}
clear_bit(board, &j->busyflags);
ixj_add_timer(j);
return;
}
}
if (!j->flags.ringing) {
if (j->hookstate) { /* & 1) { */
if (j->dsp.low != 0x20 &&
SLIC_GetState(j) != PLD_SLIC_STATE_ACTIVE) {
SLIC_SetState(PLD_SLIC_STATE_ACTIVE, j);
}
LineMonitor(j);
read_filters(j);
ixj_WriteDSPCommand(0x511B, j);
j->proc_load = j->ssr.high << 8 | j->ssr.low;
if (!j->m_hook && (j->hookstate & 1)) {
j->m_hook = j->ex.bits.hookstate = 1;
ixj_kill_fasync(j, SIG_HOOKSTATE, POLL_IN);
}
} else {
if (j->ex.bits.dtmf_ready) {
j->dtmf_wp = j->dtmf_rp = j->ex.bits.dtmf_ready = 0;
}
if (j->m_hook) {
j->m_hook = 0;
j->ex.bits.hookstate = 1;
ixj_kill_fasync(j, SIG_HOOKSTATE, POLL_IN);
}
}
}
if (j->cardtype == QTI_LINEJACK && !j->flags.pstncheck && j->flags.pstn_present) {
ixj_pstn_state(j);
}
if (j->ex.bytes) {
wake_up_interruptible(&j->poll_q); /* Wake any blocked selects */
}
clear_bit(board, &j->busyflags);
}
ixj_add_timer(j);
}
static int ixj_status_wait(IXJ *j)
{
unsigned long jif;
jif = jiffies + ((60 * hertz) / 100);
while (!IsStatusReady(j)) {
ixj_perfmon(j->statuswait);
if (time_after(jiffies, jif)) {
ixj_perfmon(j->statuswaitfail);
return -1;
}
}
return 0;
}
static int ixj_PCcontrol_wait(IXJ *j)
{
unsigned long jif;
jif = jiffies + ((60 * hertz) / 100);
while (!IsPCControlReady(j)) {
ixj_perfmon(j->pcontrolwait);
if (time_after(jiffies, jif)) {
ixj_perfmon(j->pcontrolwaitfail);
return -1;
}
}
return 0;
}
static int ixj_WriteDSPCommand(unsigned short cmd, IXJ *j)
{
BYTES bytes;
unsigned long jif;
atomic_inc(&j->DSPWrite);
if(atomic_read(&j->DSPWrite) > 1) {
printk("IXJ %d DSP write overlap attempting command 0x%4.4x\n", j->board, cmd);
return -1;
}
bytes.high = (cmd & 0xFF00) >> 8;
bytes.low = cmd & 0x00FF;
jif = jiffies + ((60 * hertz) / 100);
while (!IsControlReady(j)) {
ixj_perfmon(j->iscontrolready);
if (time_after(jiffies, jif)) {
ixj_perfmon(j->iscontrolreadyfail);
atomic_dec(&j->DSPWrite);
if(atomic_read(&j->DSPWrite) > 0) {
printk("IXJ %d DSP overlaped command 0x%4.4x during control ready failure.\n", j->board, cmd);
while(atomic_read(&j->DSPWrite) > 0) {
atomic_dec(&j->DSPWrite);
}
}
return -1;
}
}
outb(bytes.low, j->DSPbase + 6);
outb(bytes.high, j->DSPbase + 7);
if (ixj_status_wait(j)) {
j->ssr.low = 0xFF;
j->ssr.high = 0xFF;
atomic_dec(&j->DSPWrite);
if(atomic_read(&j->DSPWrite) > 0) {
printk("IXJ %d DSP overlaped command 0x%4.4x during status wait failure.\n", j->board, cmd);
while(atomic_read(&j->DSPWrite) > 0) {
atomic_dec(&j->DSPWrite);
}
}
return -1;
}
/* Read Software Status Register */
j->ssr.low = inb_p(j->DSPbase + 2);
j->ssr.high = inb_p(j->DSPbase + 3);
atomic_dec(&j->DSPWrite);
if(atomic_read(&j->DSPWrite) > 0) {
printk("IXJ %d DSP overlaped command 0x%4.4x\n", j->board, cmd);
while(atomic_read(&j->DSPWrite) > 0) {
atomic_dec(&j->DSPWrite);
}
}
return 0;
}
/***************************************************************************
*
* General Purpose IO Register read routine
*
***************************************************************************/
static inline int ixj_gpio_read(IXJ *j)
{
if (ixj_WriteDSPCommand(0x5143, j))
return -1;
j->gpio.bytes.low = j->ssr.low;
j->gpio.bytes.high = j->ssr.high;
return 0;
}
static inline void LED_SetState(int state, IXJ *j)
{
if (j->cardtype == QTI_LINEJACK) {
j->pld_scrw.bits.led1 = state & 0x1 ? 1 : 0;
j->pld_scrw.bits.led2 = state & 0x2 ? 1 : 0;
j->pld_scrw.bits.led3 = state & 0x4 ? 1 : 0;
j->pld_scrw.bits.led4 = state & 0x8 ? 1 : 0;
outb(j->pld_scrw.byte, j->XILINXbase);
}
}
/*********************************************************************
* GPIO Pins are configured as follows on the Quicknet Internet
* PhoneJACK Telephony Cards
*
* POTS Select GPIO_6=0 GPIO_7=0
* Mic/Speaker Select GPIO_6=0 GPIO_7=1
* Handset Select GPIO_6=1 GPIO_7=0
*
* SLIC Active GPIO_1=0 GPIO_2=1 GPIO_5=0
* SLIC Ringing GPIO_1=1 GPIO_2=1 GPIO_5=0
* SLIC Open Circuit GPIO_1=0 GPIO_2=0 GPIO_5=0
*
* Hook Switch changes reported on GPIO_3
*********************************************************************/
static int ixj_set_port(IXJ *j, int arg)
{
if (j->cardtype == QTI_PHONEJACK_LITE) {
if (arg != PORT_POTS)
return 10;
else
return 0;
}
switch (arg) {
case PORT_POTS:
j->port = PORT_POTS;
switch (j->cardtype) {
case QTI_PHONECARD:
if (j->flags.pcmciasct == 1)
SLIC_SetState(PLD_SLIC_STATE_ACTIVE, j);
else
return 11;
break;
case QTI_PHONEJACK_PCI:
j->pld_slicw.pcib.mic = 0;
j->pld_slicw.pcib.spk = 0;
outb(j->pld_slicw.byte, j->XILINXbase + 0x01);
break;
case QTI_LINEJACK:
ixj_set_pots(j, 0); /* Disconnect POTS/PSTN relay */
if (ixj_WriteDSPCommand(0xC528, j)) /* Write CODEC config to
Software Control Register */
return 2;
j->pld_scrw.bits.daafsyncen = 0; /* Turn off DAA Frame Sync */
outb(j->pld_scrw.byte, j->XILINXbase);
j->pld_clock.byte = 0;
outb(j->pld_clock.byte, j->XILINXbase + 0x04);
j->pld_slicw.bits.rly1 = 1;
j->pld_slicw.bits.spken = 0;
outb(j->pld_slicw.byte, j->XILINXbase + 0x01);
ixj_mixer(0x1200, j); /* Turn Off MIC switch on mixer left */
ixj_mixer(0x1401, j); /* Turn On Mono1 switch on mixer left */
ixj_mixer(0x1300, j); /* Turn Off MIC switch on mixer right */
ixj_mixer(0x1501, j); /* Turn On Mono1 switch on mixer right */
ixj_mixer(0x0E80, j); /*Mic mute */
ixj_mixer(0x0F00, j); /* Set mono out (SLIC) to 0dB */
ixj_mixer(0x0080, j); /* Mute Master Left volume */
ixj_mixer(0x0180, j); /* Mute Master Right volume */
SLIC_SetState(PLD_SLIC_STATE_STANDBY, j);
/* SLIC_SetState(PLD_SLIC_STATE_ACTIVE, j); */
break;
case QTI_PHONEJACK:
j->gpio.bytes.high = 0x0B;
j->gpio.bits.gpio6 = 0;
j->gpio.bits.gpio7 = 0;
ixj_WriteDSPCommand(j->gpio.word, j);
break;
}
break;
case PORT_PSTN:
if (j->cardtype == QTI_LINEJACK) {
ixj_WriteDSPCommand(0xC534, j); /* Write CODEC config to Software Control Register */
j->pld_slicw.bits.rly3 = 0;
j->pld_slicw.bits.rly1 = 1;
j->pld_slicw.bits.spken = 0;
outb(j->pld_slicw.byte, j->XILINXbase + 0x01);
j->port = PORT_PSTN;
} else {
return 4;
}
break;
case PORT_SPEAKER:
j->port = PORT_SPEAKER;
switch (j->cardtype) {
case QTI_PHONECARD:
if (j->flags.pcmciasct) {
SLIC_SetState(PLD_SLIC_STATE_OC, j);
}
break;
case QTI_PHONEJACK_PCI:
j->pld_slicw.pcib.mic = 1;
j->pld_slicw.pcib.spk = 1;
outb(j->pld_slicw.byte, j->XILINXbase + 0x01);
break;
case QTI_LINEJACK:
ixj_set_pots(j, 0); /* Disconnect POTS/PSTN relay */
if (ixj_WriteDSPCommand(0xC528, j)) /* Write CODEC config to
Software Control Register */
return 2;
j->pld_scrw.bits.daafsyncen = 0; /* Turn off DAA Frame Sync */
outb(j->pld_scrw.byte, j->XILINXbase);
j->pld_clock.byte = 0;
outb(j->pld_clock.byte, j->XILINXbase + 0x04);
j->pld_slicw.bits.rly1 = 1;
j->pld_slicw.bits.spken = 1;
outb(j->pld_slicw.byte, j->XILINXbase + 0x01);
ixj_mixer(0x1201, j); /* Turn On MIC switch on mixer left */
ixj_mixer(0x1400, j); /* Turn Off Mono1 switch on mixer left */
ixj_mixer(0x1301, j); /* Turn On MIC switch on mixer right */
ixj_mixer(0x1500, j); /* Turn Off Mono1 switch on mixer right */
ixj_mixer(0x0E06, j); /*Mic un-mute 0dB */
ixj_mixer(0x0F80, j); /* Mute mono out (SLIC) */
ixj_mixer(0x0000, j); /* Set Master Left volume to 0dB */
ixj_mixer(0x0100, j); /* Set Master Right volume to 0dB */
break;
case QTI_PHONEJACK:
j->gpio.bytes.high = 0x0B;
j->gpio.bits.gpio6 = 0;
j->gpio.bits.gpio7 = 1;
ixj_WriteDSPCommand(j->gpio.word, j);
break;
}
break;
case PORT_HANDSET:
if (j->cardtype != QTI_PHONEJACK) {
return 5;
} else {
j->gpio.bytes.high = 0x0B;
j->gpio.bits.gpio6 = 1;
j->gpio.bits.gpio7 = 0;
ixj_WriteDSPCommand(j->gpio.word, j);
j->port = PORT_HANDSET;
}
break;
default:
return 6;
break;
}
return 0;
}
static int ixj_set_pots(IXJ *j, int arg)
{
if (j->cardtype == QTI_LINEJACK) {
if (arg) {
if (j->port == PORT_PSTN) {
j->pld_slicw.bits.rly1 = 0;
outb(j->pld_slicw.byte, j->XILINXbase + 0x01);
j->flags.pots_pstn = 1;
return 1;
} else {
j->flags.pots_pstn = 0;
return 0;
}
} else {
j->pld_slicw.bits.rly1 = 1;
outb(j->pld_slicw.byte, j->XILINXbase + 0x01);
j->flags.pots_pstn = 0;
return 1;
}
} else {
return 0;
}
}
static void ixj_ring_on(IXJ *j)
{
if (j->dsp.low == 0x20) /* Internet PhoneJACK */
{
if (ixjdebug & 0x0004)
printk(KERN_INFO "IXJ Ring On /dev/phone%d\n", j->board);
j->gpio.bytes.high = 0x0B;
j->gpio.bytes.low = 0x00;
j->gpio.bits.gpio1 = 1;
j->gpio.bits.gpio2 = 1;
j->gpio.bits.gpio5 = 0;
ixj_WriteDSPCommand(j->gpio.word, j); /* send the ring signal */
} else /* Internet LineJACK, Internet PhoneJACK Lite or Internet PhoneJACK PCI */
{
if (ixjdebug & 0x0004)
printk(KERN_INFO "IXJ Ring On /dev/phone%d\n", j->board);
SLIC_SetState(PLD_SLIC_STATE_RINGING, j);
}
}
static int ixj_siadc(IXJ *j, int val)
{
if(j->cardtype == QTI_PHONECARD){
if(j->flags.pcmciascp){
if(val == -1)
return j->siadc.bits.rxg;
if(val < 0 || val > 0x1F)
return -1;
j->siadc.bits.hom = 0; /* Handset Out Mute */
j->siadc.bits.lom = 0; /* Line Out Mute */
j->siadc.bits.rxg = val; /*(0xC000 - 0x41C8) / 0x4EF; RX PGA Gain */
j->psccr.bits.addr = 6; /* R/W Smart Cable Register Address */
j->psccr.bits.rw = 0; /* Read / Write flag */
j->psccr.bits.dev = 0;
outb(j->siadc.byte, j->XILINXbase + 0x00);
outb(j->psccr.byte, j->XILINXbase + 0x01);
ixj_PCcontrol_wait(j);
return j->siadc.bits.rxg;
}
}
return -1;
}
static int ixj_sidac(IXJ *j, int val)
{
if(j->cardtype == QTI_PHONECARD){
if(j->flags.pcmciascp){
if(val == -1)
return j->sidac.bits.txg;
if(val < 0 || val > 0x1F)
return -1;
j->sidac.bits.srm = 1; /* Speaker Right Mute */
j->sidac.bits.slm = 1; /* Speaker Left Mute */
j->sidac.bits.txg = val; /* (0xC000 - 0x45E4) / 0x5D3; TX PGA Gain */
j->psccr.bits.addr = 7; /* R/W Smart Cable Register Address */
j->psccr.bits.rw = 0; /* Read / Write flag */
j->psccr.bits.dev = 0;
outb(j->sidac.byte, j->XILINXbase + 0x00);
outb(j->psccr.byte, j->XILINXbase + 0x01);
ixj_PCcontrol_wait(j);
return j->sidac.bits.txg;
}
}
return -1;
}
static int ixj_pcmcia_cable_check(IXJ *j)
{
j->pccr1.byte = inb_p(j->XILINXbase + 0x03);
if (!j->flags.pcmciastate) {
j->pccr2.byte = inb_p(j->XILINXbase + 0x02);
if (j->pccr1.bits.drf || j->pccr2.bits.rstc) {
j->flags.pcmciastate = 4;
return 0;
}
if (j->pccr1.bits.ed) {
j->pccr1.bits.ed = 0;
j->psccr.bits.dev = 3;
j->psccr.bits.rw = 1;
outw_p(j->psccr.byte << 8, j->XILINXbase + 0x00);
ixj_PCcontrol_wait(j);
j->pslic.byte = inw_p(j->XILINXbase + 0x00) & 0xFF;
j->pslic.bits.led2 = j->pslic.bits.det ? 1 : 0;
j->psccr.bits.dev = 3;
j->psccr.bits.rw = 0;
outw_p(j->psccr.byte << 8 | j->pslic.byte, j->XILINXbase + 0x00);
ixj_PCcontrol_wait(j);
return j->pslic.bits.led2 ? 1 : 0;
} else if (j->flags.pcmciasct) {
return j->r_hook;
} else {
return 1;
}
} else if (j->flags.pcmciastate == 4) {
if (!j->pccr1.bits.drf) {
j->flags.pcmciastate = 3;
}
return 0;
} else if (j->flags.pcmciastate == 3) {
j->pccr2.bits.pwr = 0;
j->pccr2.bits.rstc = 1;
outb(j->pccr2.byte, j->XILINXbase + 0x02);
j->checkwait = jiffies + (hertz * 2);
j->flags.incheck = 1;
j->flags.pcmciastate = 2;
return 0;
} else if (j->flags.pcmciastate == 2) {
if (j->flags.incheck) {
if (time_before(jiffies, j->checkwait)) {
return 0;
} else {
j->flags.incheck = 0;
}
}
j->pccr2.bits.pwr = 0;
j->pccr2.bits.rstc = 0;
outb_p(j->pccr2.byte, j->XILINXbase + 0x02);
j->flags.pcmciastate = 1;
return 0;
} else if (j->flags.pcmciastate == 1) {
j->flags.pcmciastate = 0;
if (!j->pccr1.bits.drf) {
j->psccr.bits.dev = 3;
j->psccr.bits.rw = 1;
outb_p(j->psccr.byte, j->XILINXbase + 0x01);
ixj_PCcontrol_wait(j);
j->flags.pcmciascp = 1; /* Set Cable Present Flag */
j->flags.pcmciasct = (inw_p(j->XILINXbase + 0x00) >> 8) & 0x03; /* Get Cable Type */
if (j->flags.pcmciasct == 3) {
j->flags.pcmciastate = 4;
return 0;
} else if (j->flags.pcmciasct == 0) {
j->pccr2.bits.pwr = 1;
j->pccr2.bits.rstc = 0;
outb_p(j->pccr2.byte, j->XILINXbase + 0x02);
j->port = PORT_SPEAKER;
} else {
j->port = PORT_POTS;
}
j->sic1.bits.cpd = 0; /* Chip Power Down */
j->sic1.bits.mpd = 0; /* MIC Bias Power Down */
j->sic1.bits.hpd = 0; /* Handset Bias Power Down */
j->sic1.bits.lpd = 0; /* Line Bias Power Down */
j->sic1.bits.spd = 1; /* Speaker Drive Power Down */
j->psccr.bits.addr = 1; /* R/W Smart Cable Register Address */
j->psccr.bits.rw = 0; /* Read / Write flag */
j->psccr.bits.dev = 0;
outb(j->sic1.byte, j->XILINXbase + 0x00);
outb(j->psccr.byte, j->XILINXbase + 0x01);
ixj_PCcontrol_wait(j);
j->sic2.bits.al = 0; /* Analog Loopback DAC analog -> ADC analog */
j->sic2.bits.dl2 = 0; /* Digital Loopback DAC -> ADC one bit */
j->sic2.bits.dl1 = 0; /* Digital Loopback ADC -> DAC one bit */
j->sic2.bits.pll = 0; /* 1 = div 10, 0 = div 5 */
j->sic2.bits.hpd = 0; /* HPF disable */
j->psccr.bits.addr = 2; /* R/W Smart Cable Register Address */
j->psccr.bits.rw = 0; /* Read / Write flag */
j->psccr.bits.dev = 0;
outb(j->sic2.byte, j->XILINXbase + 0x00);
outb(j->psccr.byte, j->XILINXbase + 0x01);
ixj_PCcontrol_wait(j);
j->psccr.bits.addr = 3; /* R/W Smart Cable Register Address */
j->psccr.bits.rw = 0; /* Read / Write flag */
j->psccr.bits.dev = 0;
outb(0x00, j->XILINXbase + 0x00); /* PLL Divide N1 */
outb(j->psccr.byte, j->XILINXbase + 0x01);
ixj_PCcontrol_wait(j);
j->psccr.bits.addr = 4; /* R/W Smart Cable Register Address */
j->psccr.bits.rw = 0; /* Read / Write flag */
j->psccr.bits.dev = 0;
outb(0x09, j->XILINXbase + 0x00); /* PLL Multiply M1 */
outb(j->psccr.byte, j->XILINXbase + 0x01);
ixj_PCcontrol_wait(j);
j->sirxg.bits.lig = 1; /* Line In Gain */
j->sirxg.bits.lim = 1; /* Line In Mute */
j->sirxg.bits.mcg = 0; /* MIC In Gain was 3 */
j->sirxg.bits.mcm = 0; /* MIC In Mute */
j->sirxg.bits.him = 0; /* Handset In Mute */
j->sirxg.bits.iir = 1; /* IIR */
j->psccr.bits.addr = 5; /* R/W Smart Cable Register Address */
j->psccr.bits.rw = 0; /* Read / Write flag */
j->psccr.bits.dev = 0;
outb(j->sirxg.byte, j->XILINXbase + 0x00);
outb(j->psccr.byte, j->XILINXbase + 0x01);
ixj_PCcontrol_wait(j);
ixj_siadc(j, 0x17);
ixj_sidac(j, 0x1D);
j->siaatt.bits.sot = 0;
j->psccr.bits.addr = 9; /* R/W Smart Cable Register Address */
j->psccr.bits.rw = 0; /* Read / Write flag */
j->psccr.bits.dev = 0;
outb(j->siaatt.byte, j->XILINXbase + 0x00);
outb(j->psccr.byte, j->XILINXbase + 0x01);
ixj_PCcontrol_wait(j);
if (j->flags.pcmciasct == 1 && !j->readers && !j->writers) {
j->psccr.byte = j->pslic.byte = 0;
j->pslic.bits.powerdown = 1;
j->psccr.bits.dev = 3;
j->psccr.bits.rw = 0;
outw_p(j->psccr.byte << 8 | j->pslic.byte, j->XILINXbase + 0x00);
ixj_PCcontrol_wait(j);
}
}
return 0;
} else {
j->flags.pcmciascp = 0;
return 0;
}
return 0;
}
static int ixj_hookstate(IXJ *j)
{
int fOffHook = 0;
switch (j->cardtype) {
case QTI_PHONEJACK:
ixj_gpio_read(j);
fOffHook = j->gpio.bits.gpio3read ? 1 : 0;
break;
case QTI_LINEJACK:
case QTI_PHONEJACK_LITE:
case QTI_PHONEJACK_PCI:
SLIC_GetState(j);
if(j->cardtype == QTI_LINEJACK && j->flags.pots_pstn == 1 && (j->readers || j->writers)) {
fOffHook = j->pld_slicr.bits.potspstn ? 1 : 0;
if(fOffHook != j->p_hook) {
if(!j->checkwait) {
j->checkwait = jiffies;
}
if(time_before(jiffies, j->checkwait + 2)) {
fOffHook ^= 1;
} else {
j->checkwait = 0;
}
j->p_hook = fOffHook;
printk("IXJ : /dev/phone%d pots-pstn hookstate check %d at %ld\n", j->board, fOffHook, jiffies);
}
} else {
if (j->pld_slicr.bits.state == PLD_SLIC_STATE_ACTIVE ||
j->pld_slicr.bits.state == PLD_SLIC_STATE_STANDBY) {
if (j->flags.ringing || j->flags.cringing) {
if (!in_interrupt()) {
msleep(20);
}
SLIC_GetState(j);
if (j->pld_slicr.bits.state == PLD_SLIC_STATE_RINGING) {
ixj_ring_on(j);
}
}
if (j->cardtype == QTI_PHONEJACK_PCI) {
j->pld_scrr.byte = inb_p(j->XILINXbase);
fOffHook = j->pld_scrr.pcib.det ? 1 : 0;
} else
fOffHook = j->pld_slicr.bits.det ? 1 : 0;
}
}
break;
case QTI_PHONECARD:
fOffHook = ixj_pcmcia_cable_check(j);
break;
}
if (j->r_hook != fOffHook) {
j->r_hook = fOffHook;
if (j->port == PORT_SPEAKER || j->port == PORT_HANDSET) { // || (j->port == PORT_PSTN && j->flags.pots_pstn == 0)) {
j->ex.bits.hookstate = 1;
ixj_kill_fasync(j, SIG_HOOKSTATE, POLL_IN);
} else if (!fOffHook) {
j->flash_end = jiffies + ((60 * hertz) / 100);
}
}
if (fOffHook) {
if(time_before(jiffies, j->flash_end)) {
j->ex.bits.flash = 1;
j->flash_end = 0;
ixj_kill_fasync(j, SIG_FLASH, POLL_IN);
}
} else {
if(time_before(jiffies, j->flash_end)) {
fOffHook = 1;
}
}
if (j->port == PORT_PSTN && j->daa_mode == SOP_PU_CONVERSATION)
fOffHook |= 2;
if (j->port == PORT_SPEAKER) {
if(j->cardtype == QTI_PHONECARD) {
if(j->flags.pcmciascp && j->flags.pcmciasct) {
fOffHook |= 2;
}
} else {
fOffHook |= 2;
}
}
if (j->port == PORT_HANDSET)
fOffHook |= 2;
return fOffHook;
}
static void ixj_ring_off(IXJ *j)
{
if (j->dsp.low == 0x20) /* Internet PhoneJACK */
{
if (ixjdebug & 0x0004)
printk(KERN_INFO "IXJ Ring Off\n");
j->gpio.bytes.high = 0x0B;
j->gpio.bytes.low = 0x00;
j->gpio.bits.gpio1 = 0;
j->gpio.bits.gpio2 = 1;
j->gpio.bits.gpio5 = 0;
ixj_WriteDSPCommand(j->gpio.word, j);
} else /* Internet LineJACK */
{
if (ixjdebug & 0x0004)
printk(KERN_INFO "IXJ Ring Off\n");
if(!j->flags.cidplay)
SLIC_SetState(PLD_SLIC_STATE_STANDBY, j);
SLIC_GetState(j);
}
}
static void ixj_ring_start(IXJ *j)
{
j->flags.cringing = 1;
if (ixjdebug & 0x0004)
printk(KERN_INFO "IXJ Cadence Ringing Start /dev/phone%d\n", j->board);
if (ixj_hookstate(j) & 1) {
if (j->port == PORT_POTS)
ixj_ring_off(j);
j->flags.cringing = 0;
if (ixjdebug & 0x0004)
printk(KERN_INFO "IXJ Cadence Ringing Stopped /dev/phone%d off hook\n", j->board);
} else if(j->cadence_f[5].enable && (!j->cadence_f[5].en_filter)) {
j->ring_cadence_jif = jiffies;
j->flags.cidsent = j->flags.cidring = 0;
j->cadence_f[5].state = 0;
if(j->cadence_f[5].on1)
ixj_ring_on(j);
} else {
j->ring_cadence_jif = jiffies;
j->ring_cadence_t = 15;
if (j->ring_cadence & 1 << j->ring_cadence_t) {
ixj_ring_on(j);
} else {
ixj_ring_off(j);
}
j->flags.cidsent = j->flags.cidring = j->flags.firstring = 0;
}
}
static int ixj_ring(IXJ *j)
{
char cntr;
unsigned long jif;
j->flags.ringing = 1;
if (ixj_hookstate(j) & 1) {
ixj_ring_off(j);
j->flags.ringing = 0;
return 1;
}
for (cntr = 0; cntr < j->maxrings; cntr++) {
jif = jiffies + (1 * hertz);
ixj_ring_on(j);
while (time_before(jiffies, jif)) {
if (ixj_hookstate(j) & 1) {
ixj_ring_off(j);
j->flags.ringing = 0;
return 1;
}
schedule_timeout_interruptible(1);
if (signal_pending(current))
break;
}
jif = jiffies + (3 * hertz);
ixj_ring_off(j);
while (time_before(jiffies, jif)) {
if (ixj_hookstate(j) & 1) {
msleep(10);
if (ixj_hookstate(j) & 1) {
j->flags.ringing = 0;
return 1;
}
}
schedule_timeout_interruptible(1);
if (signal_pending(current))
break;
}
}
ixj_ring_off(j);
j->flags.ringing = 0;
return 0;
}
static int ixj_open(struct phone_device *p, struct file *file_p)
{
IXJ *j = get_ixj(p->board);
file_p->private_data = j;
if (!j->DSPbase)
return -ENODEV;
if (file_p->f_mode & FMODE_READ) {
if(!j->readers) {
j->readers++;
} else {
return -EBUSY;
}
}
if (file_p->f_mode & FMODE_WRITE) {
if(!j->writers) {
j->writers++;
} else {
if (file_p->f_mode & FMODE_READ){
j->readers--;
}
return -EBUSY;
}
}
if (j->cardtype == QTI_PHONECARD) {
j->pslic.bits.powerdown = 0;
j->psccr.bits.dev = 3;
j->psccr.bits.rw = 0;
outw_p(j->psccr.byte << 8 | j->pslic.byte, j->XILINXbase + 0x00);
ixj_PCcontrol_wait(j);
}
j->flags.cidplay = 0;
j->flags.cidcw_ack = 0;
if (ixjdebug & 0x0002)
printk(KERN_INFO "Opening board %d\n", p->board);
j->framesread = j->frameswritten = 0;
return 0;
}
static int ixj_release(struct inode *inode, struct file *file_p)
{
IXJ_TONE ti;
int cnt;
IXJ *j = file_p->private_data;
int board = j->p.board;
/*
* Set up locks to ensure that only one process is talking to the DSP at a time.
* This is necessary to keep the DSP from locking up.
*/
while(test_and_set_bit(board, (void *)&j->busyflags) != 0)
schedule_timeout_interruptible(1);
if (ixjdebug & 0x0002)
printk(KERN_INFO "Closing board %d\n", NUM(inode));
if (j->cardtype == QTI_PHONECARD)
ixj_set_port(j, PORT_SPEAKER);
else
ixj_set_port(j, PORT_POTS);
aec_stop(j);
ixj_play_stop(j);
ixj_record_stop(j);
set_play_volume(j, 0x100);
set_rec_volume(j, 0x100);
ixj_ring_off(j);
/* Restore the tone table to default settings. */
ti.tone_index = 10;
ti.gain0 = 1;
ti.freq0 = hz941;
ti.gain1 = 0;
ti.freq1 = hz1209;
ixj_init_tone(j, &ti);
ti.tone_index = 11;
ti.gain0 = 1;
ti.freq0 = hz941;
ti.gain1 = 0;
ti.freq1 = hz1336;
ixj_init_tone(j, &ti);
ti.tone_index = 12;
ti.gain0 = 1;
ti.freq0 = hz941;
ti.gain1 = 0;
ti.freq1 = hz1477;
ixj_init_tone(j, &ti);
ti.tone_index = 13;
ti.gain0 = 1;
ti.freq0 = hz800;
ti.gain1 = 0;
ti.freq1 = 0;
ixj_init_tone(j, &ti);
ti.tone_index = 14;
ti.gain0 = 1;
ti.freq0 = hz1000;
ti.gain1 = 0;
ti.freq1 = 0;
ixj_init_tone(j, &ti);
ti.tone_index = 15;
ti.gain0 = 1;
ti.freq0 = hz1250;
ti.gain1 = 0;
ti.freq1 = 0;
ixj_init_tone(j, &ti);
ti.tone_index = 16;
ti.gain0 = 1;
ti.freq0 = hz950;
ti.gain1 = 0;
ti.freq1 = 0;
ixj_init_tone(j, &ti);
ti.tone_index = 17;
ti.gain0 = 1;
ti.freq0 = hz1100;
ti.gain1 = 0;
ti.freq1 = 0;
ixj_init_tone(j, &ti);
ti.tone_index = 18;
ti.gain0 = 1;
ti.freq0 = hz1400;
ti.gain1 = 0;
ti.freq1 = 0;
ixj_init_tone(j, &ti);
ti.tone_index = 19;
ti.gain0 = 1;
ti.freq0 = hz1500;
ti.gain1 = 0;
ti.freq1 = 0;
ixj_init_tone(j, &ti);
ti.tone_index = 20;
ti.gain0 = 1;
ti.freq0 = hz1600;
ti.gain1 = 0;
ti.freq1 = 0;
ixj_init_tone(j, &ti);
ti.tone_index = 21;
ti.gain0 = 1;
ti.freq0 = hz1800;
ti.gain1 = 0;
ti.freq1 = 0;
ixj_init_tone(j, &ti);
ti.tone_index = 22;
ti.gain0 = 1;
ti.freq0 = hz2100;
ti.gain1 = 0;
ti.freq1 = 0;
ixj_init_tone(j, &ti);
ti.tone_index = 23;
ti.gain0 = 1;
ti.freq0 = hz1300;
ti.gain1 = 0;
ti.freq1 = 0;
ixj_init_tone(j, &ti);
ti.tone_index = 24;
ti.gain0 = 1;
ti.freq0 = hz2450;
ti.gain1 = 0;
ti.freq1 = 0;
ixj_init_tone(j, &ti);
ti.tone_index = 25;
ti.gain0 = 1;
ti.freq0 = hz350;
ti.gain1 = 0;
ti.freq1 = hz440;
ixj_init_tone(j, &ti);
ti.tone_index = 26;
ti.gain0 = 1;
ti.freq0 = hz440;
ti.gain1 = 0;
ti.freq1 = hz480;
ixj_init_tone(j, &ti);
ti.tone_index = 27;
ti.gain0 = 1;
ti.freq0 = hz480;
ti.gain1 = 0;
ti.freq1 = hz620;
ixj_init_tone(j, &ti);
set_rec_depth(j, 2); /* Set Record Channel Limit to 2 frames */
set_play_depth(j, 2); /* Set Playback Channel Limit to 2 frames */
j->ex.bits.dtmf_ready = 0;
j->dtmf_state = 0;
j->dtmf_wp = j->dtmf_rp = 0;
j->rec_mode = j->play_mode = -1;
j->flags.ringing = 0;
j->maxrings = MAXRINGS;
j->ring_cadence = USA_RING_CADENCE;
if(j->cadence_f[5].enable) {
j->cadence_f[5].enable = j->cadence_f[5].en_filter = j->cadence_f[5].state = 0;
}
j->drybuffer = 0;
j->winktime = 320;
j->flags.dtmf_oob = 0;
for (cnt = 0; cnt < 4; cnt++)
j->cadence_f[cnt].enable = 0;
idle(j);
if(j->cardtype == QTI_PHONECARD) {
SLIC_SetState(PLD_SLIC_STATE_OC, j);
}
if (file_p->f_mode & FMODE_READ)
j->readers--;
if (file_p->f_mode & FMODE_WRITE)
j->writers--;
if (j->read_buffer && !j->readers) {
kfree(j->read_buffer);
j->read_buffer = NULL;
j->read_buffer_size = 0;
}
if (j->write_buffer && !j->writers) {
kfree(j->write_buffer);
j->write_buffer = NULL;
j->write_buffer_size = 0;
}
j->rec_codec = j->play_codec = 0;
j->rec_frame_size = j->play_frame_size = 0;
j->flags.cidsent = j->flags.cidring = 0;
if(j->cardtype == QTI_LINEJACK && !j->readers && !j->writers) {
ixj_set_port(j, PORT_PSTN);
daa_set_mode(j, SOP_PU_SLEEP);
ixj_set_pots(j, 1);
}
ixj_WriteDSPCommand(0x0FE3, j); /* Put the DSP in 1/5 power mode. */
/* Set up the default signals for events */
for (cnt = 0; cnt < 35; cnt++)
j->ixj_signals[cnt] = SIGIO;
/* Set the excetion signal enable flags */
j->ex_sig.bits.dtmf_ready = j->ex_sig.bits.hookstate = j->ex_sig.bits.flash = j->ex_sig.bits.pstn_ring =
j->ex_sig.bits.caller_id = j->ex_sig.bits.pstn_wink = j->ex_sig.bits.f0 = j->ex_sig.bits.f1 = j->ex_sig.bits.f2 =
j->ex_sig.bits.f3 = j->ex_sig.bits.fc0 = j->ex_sig.bits.fc1 = j->ex_sig.bits.fc2 = j->ex_sig.bits.fc3 = 1;
file_p->private_data = NULL;
clear_bit(board, &j->busyflags);
return 0;
}
static int read_filters(IXJ *j)
{
unsigned short fc, cnt, trg;
int var;
trg = 0;
if (ixj_WriteDSPCommand(0x5144, j)) {
if(ixjdebug & 0x0001) {
printk(KERN_INFO "Read Frame Counter failed!\n");
}
return -1;
}
fc = j->ssr.high << 8 | j->ssr.low;
if (fc == j->frame_count)
return 1;
j->frame_count = fc;
if (j->dtmf_proc)
return 1;
var = 10;
for (cnt = 0; cnt < 4; cnt++) {
if (ixj_WriteDSPCommand(0x5154 + cnt, j)) {
if(ixjdebug & 0x0001) {
printk(KERN_INFO "Select Filter %d failed!\n", cnt);
}
return -1;
}
if (ixj_WriteDSPCommand(0x515C, j)) {
if(ixjdebug & 0x0001) {
printk(KERN_INFO "Read Filter History %d failed!\n", cnt);
}
return -1;
}
j->filter_hist[cnt] = j->ssr.high << 8 | j->ssr.low;
if (j->cadence_f[cnt].enable) {
if (j->filter_hist[cnt] & 3 && !(j->filter_hist[cnt] & 12)) {
if (j->cadence_f[cnt].state == 0) {
j->cadence_f[cnt].state = 1;
j->cadence_f[cnt].on1min = jiffies + (long)((j->cadence_f[cnt].on1 * (hertz * (100 - var)) / 10000));
j->cadence_f[cnt].on1dot = jiffies + (long)((j->cadence_f[cnt].on1 * (hertz * (100)) / 10000));
j->cadence_f[cnt].on1max = jiffies + (long)((j->cadence_f[cnt].on1 * (hertz * (100 + var)) / 10000));
} else if (j->cadence_f[cnt].state == 2 &&
(time_after(jiffies, j->cadence_f[cnt].off1min) &&
time_before(jiffies, j->cadence_f[cnt].off1max))) {
if (j->cadence_f[cnt].on2) {
j->cadence_f[cnt].state = 3;
j->cadence_f[cnt].on2min = jiffies + (long)((j->cadence_f[cnt].on2 * (hertz * (100 - var)) / 10000));
j->cadence_f[cnt].on2dot = jiffies + (long)((j->cadence_f[cnt].on2 * (hertz * (100)) / 10000));
j->cadence_f[cnt].on2max = jiffies + (long)((j->cadence_f[cnt].on2 * (hertz * (100 + var)) / 10000));
} else {
j->cadence_f[cnt].state = 7;
}
} else if (j->cadence_f[cnt].state == 4 &&
(time_after(jiffies, j->cadence_f[cnt].off2min) &&
time_before(jiffies, j->cadence_f[cnt].off2max))) {
if (j->cadence_f[cnt].on3) {
j->cadence_f[cnt].state = 5;
j->cadence_f[cnt].on3min = jiffies + (long)((j->cadence_f[cnt].on3 * (hertz * (100 - var)) / 10000));
j->cadence_f[cnt].on3dot = jiffies + (long)((j->cadence_f[cnt].on3 * (hertz * (100)) / 10000));
j->cadence_f[cnt].on3max = jiffies + (long)((j->cadence_f[cnt].on3 * (hertz * (100 + var)) / 10000));
} else {
j->cadence_f[cnt].state = 7;
}
} else {
j->cadence_f[cnt].state = 0;
}
} else if (j->filter_hist[cnt] & 12 && !(j->filter_hist[cnt] & 3)) {
if (j->cadence_f[cnt].state == 1) {
if(!j->cadence_f[cnt].on1) {
j->cadence_f[cnt].state = 7;
} else if((time_after(jiffies, j->cadence_f[cnt].on1min) &&
time_before(jiffies, j->cadence_f[cnt].on1max))) {
if(j->cadence_f[cnt].off1) {
j->cadence_f[cnt].state = 2;
j->cadence_f[cnt].off1min = jiffies + (long)((j->cadence_f[cnt].off1 * (hertz * (100 - var)) / 10000));
j->cadence_f[cnt].off1dot = jiffies + (long)((j->cadence_f[cnt].off1 * (hertz * (100)) / 10000));
j->cadence_f[cnt].off1max = jiffies + (long)((j->cadence_f[cnt].off1 * (hertz * (100 + var)) / 10000));
} else {
j->cadence_f[cnt].state = 7;
}
} else {
j->cadence_f[cnt].state = 0;
}
} else if (j->cadence_f[cnt].state == 3) {
if((time_after(jiffies, j->cadence_f[cnt].on2min) &&
time_before(jiffies, j->cadence_f[cnt].on2max))) {
if(j->cadence_f[cnt].off2) {
j->cadence_f[cnt].state = 4;
j->cadence_f[cnt].off2min = jiffies + (long)((j->cadence_f[cnt].off2 * (hertz * (100 - var)) / 10000));
j->cadence_f[cnt].off2dot = jiffies + (long)((j->cadence_f[cnt].off2 * (hertz * (100)) / 10000));
j->cadence_f[cnt].off2max = jiffies + (long)((j->cadence_f[cnt].off2 * (hertz * (100 + var)) / 10000));
} else {
j->cadence_f[cnt].state = 7;
}
} else {
j->cadence_f[cnt].state = 0;
}
} else if (j->cadence_f[cnt].state == 5) {
if ((time_after(jiffies, j->cadence_f[cnt].on3min) &&
time_before(jiffies, j->cadence_f[cnt].on3max))) {
if(j->cadence_f[cnt].off3) {
j->cadence_f[cnt].state = 6;
j->cadence_f[cnt].off3min = jiffies + (long)((j->cadence_f[cnt].off3 * (hertz * (100 - var)) / 10000));
j->cadence_f[cnt].off3dot = jiffies + (long)((j->cadence_f[cnt].off3 * (hertz * (100)) / 10000));
j->cadence_f[cnt].off3max = jiffies + (long)((j->cadence_f[cnt].off3 * (hertz * (100 + var)) / 10000));
} else {
j->cadence_f[cnt].state = 7;
}
} else {
j->cadence_f[cnt].state = 0;
}
} else {
j->cadence_f[cnt].state = 0;
}
} else {
switch(j->cadence_f[cnt].state) {
case 1:
if(time_after(jiffies, j->cadence_f[cnt].on1dot) &&
!j->cadence_f[cnt].off1 &&
!j->cadence_f[cnt].on2 && !j->cadence_f[cnt].off2 &&
!j->cadence_f[cnt].on3 && !j->cadence_f[cnt].off3) {
j->cadence_f[cnt].state = 7;
}
break;
case 3:
if(time_after(jiffies, j->cadence_f[cnt].on2dot) &&
!j->cadence_f[cnt].off2 &&
!j->cadence_f[cnt].on3 && !j->cadence_f[cnt].off3) {
j->cadence_f[cnt].state = 7;
}
break;
case 5:
if(time_after(jiffies, j->cadence_f[cnt].on3dot) &&
!j->cadence_f[cnt].off3) {
j->cadence_f[cnt].state = 7;
}
break;
}
}
if (ixjdebug & 0x0040) {
printk(KERN_INFO "IXJ Tone Cadence state = %d /dev/phone%d at %ld\n", j->cadence_f[cnt].state, j->board, jiffies);
switch(j->cadence_f[cnt].state) {
case 0:
printk(KERN_INFO "IXJ /dev/phone%d No Tone detected\n", j->board);
break;
case 1:
printk(KERN_INFO "IXJ /dev/phone%d Next Tone Cadence state at %u %ld - %ld - %ld\n", j->board,
j->cadence_f[cnt].on1, j->cadence_f[cnt].on1min, j->cadence_f[cnt].on1dot, j->cadence_f[cnt].on1max);
break;
case 2:
printk(KERN_INFO "IXJ /dev/phone%d Next Tone Cadence state at %ld - %ld\n", j->board, j->cadence_f[cnt].off1min,
j->cadence_f[cnt].off1max);
break;
case 3:
printk(KERN_INFO "IXJ /dev/phone%d Next Tone Cadence state at %ld - %ld\n", j->board, j->cadence_f[cnt].on2min,
j->cadence_f[cnt].on2max);
break;
case 4:
printk(KERN_INFO "IXJ /dev/phone%d Next Tone Cadence state at %ld - %ld\n", j->board, j->cadence_f[cnt].off2min,
j->cadence_f[cnt].off2max);
break;
case 5:
printk(KERN_INFO "IXJ /dev/phone%d Next Tone Cadence state at %ld - %ld\n", j->board, j->cadence_f[cnt].on3min,
j->cadence_f[cnt].on3max);
break;
case 6:
printk(KERN_INFO "IXJ /dev/phone%d Next Tone Cadence state at %ld - %ld\n", j->board, j->cadence_f[cnt].off3min,
j->cadence_f[cnt].off3max);
break;
}
}
}
if (j->cadence_f[cnt].state == 7) {
j->cadence_f[cnt].state = 0;
if (j->cadence_f[cnt].enable == 1)
j->cadence_f[cnt].enable = 0;
switch (cnt) {
case 0:
if(ixjdebug & 0x0020) {
printk(KERN_INFO "Filter Cadence 0 triggered %ld\n", jiffies);
}
j->ex.bits.fc0 = 1;
ixj_kill_fasync(j, SIG_FC0, POLL_IN);
break;
case 1:
if(ixjdebug & 0x0020) {
printk(KERN_INFO "Filter Cadence 1 triggered %ld\n", jiffies);
}
j->ex.bits.fc1 = 1;
ixj_kill_fasync(j, SIG_FC1, POLL_IN);
break;
case 2:
if(ixjdebug & 0x0020) {
printk(KERN_INFO "Filter Cadence 2 triggered %ld\n", jiffies);
}
j->ex.bits.fc2 = 1;
ixj_kill_fasync(j, SIG_FC2, POLL_IN);
break;
case 3:
if(ixjdebug & 0x0020) {
printk(KERN_INFO "Filter Cadence 3 triggered %ld\n", jiffies);
}
j->ex.bits.fc3 = 1;
ixj_kill_fasync(j, SIG_FC3, POLL_IN);
break;
}
}
if (j->filter_en[cnt] && ((j->filter_hist[cnt] & 3 && !(j->filter_hist[cnt] & 12)) ||
(j->filter_hist[cnt] & 12 && !(j->filter_hist[cnt] & 3)))) {
if((j->filter_hist[cnt] & 3 && !(j->filter_hist[cnt] & 12))) {
trg = 1;
} else if((j->filter_hist[cnt] & 12 && !(j->filter_hist[cnt] & 3))) {
trg = 0;
}
switch (cnt) {
case 0:
if(ixjdebug & 0x0020) {
printk(KERN_INFO "Filter 0 triggered %d at %ld\n", trg, jiffies);
}
j->ex.bits.f0 = 1;
ixj_kill_fasync(j, SIG_F0, POLL_IN);
break;
case 1:
if(ixjdebug & 0x0020) {
printk(KERN_INFO "Filter 1 triggered %d at %ld\n", trg, jiffies);
}