/**************************************************************************** * 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, * * Contributors: Greg Herlein, * David W. Erhart, * John Sellers, * Mike Preston, * * Fixes: David Huggins-Daines, * Fabio Ferrari, * Artis Kugevics, * Daniele Bellucci, * * 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. * ***************************************************************************/ /* * $Log: ixj.c,v $ * * 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 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 . * * 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 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, 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 succeded. * * 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 #include #include #include /* printk() */ #include /* everything... */ #include /* error codes */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ixj.h" #define TYPE(inode) (iminor(inode) >> 4) #define NUM(inode) (iminor(inode) & 0xf) static int ixjdebug; static int hertz = HZ; static int samplerate = 100; module_param(ixjdebug, int, 0); /************************************************************************ * * 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; cntDSPbase) { 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 succeded\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; cntfsksize = 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 Recieve (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 *)&