| /**************************************************************************** |
| * 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); |
| } |
|