summaryrefslogtreecommitdiff
path: root/kernel/zaptel-base.c
diff options
context:
space:
mode:
authortzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2008-02-04 23:00:48 +0000
committertzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2008-02-04 23:00:48 +0000
commit7e068801fbf82413ac0a5e63e586c268bd457434 (patch)
tree9b61e9a4e07167e0b7d347e4336245724befa29c /kernel/zaptel-base.c
parent29daeebad888269fa0ee2ca7e54e238c8498ca2d (diff)
Move kernel stuff to under kernel/
(merged branch /zaptel/team/tzafrir/move ) Closes issue #7117. git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.4@3793 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'kernel/zaptel-base.c')
-rw-r--r--kernel/zaptel-base.c7443
1 files changed, 7443 insertions, 0 deletions
diff --git a/kernel/zaptel-base.c b/kernel/zaptel-base.c
new file mode 100644
index 0000000..7203645
--- /dev/null
+++ b/kernel/zaptel-base.c
@@ -0,0 +1,7443 @@
+/*
+ * Zapata Telephony Interface Driver
+ *
+ * Written by Mark Spencer <markster@digium.com>
+ * Based on previous works, designs, and architectures conceived and
+ * written by Jim Dixon <jim@lambdatel.com>.
+ *
+ * Special thanks to Steve Underwood <steve@coppice.org>
+ * for substantial contributions to signal processing functions
+ * in zaptel and the zapata library.
+ *
+ * Yury Bokhoncovich <byg@cf1.ru>
+ * Adaptation for 2.4.20+ kernels (HDLC API was changed)
+ * The work has been performed as a part of our move
+ * from Cisco 3620 to IBM x305 here in F1 Group
+ *
+ * Copyright (C) 2001 Jim Dixon / Zapata Telephony.
+ * Copyright (C) 2001 -2006 Digium, Inc.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include "zconfig.h"
+#include "../version.h"
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/ctype.h>
+#include <linux/kmod.h>
+#ifdef CONFIG_DEVFS_FS
+#include <linux/devfs_fs_kernel.h>
+#endif /* CONFIG_DEVFS_FS */
+#ifdef CONFIG_ZAPATA_NET
+#include <linux/netdevice.h>
+#endif /* CONFIG_ZAPATA_NET */
+#include <linux/ppp_defs.h>
+#ifdef CONFIG_ZAPATA_PPP
+#include <linux/netdevice.h>
+#include <linux/if.h>
+#include <linux/if_ppp.h>
+#endif
+#include <asm/atomic.h>
+
+#ifndef CONFIG_OLD_HDLC_API
+#define NEW_HDLC_INTERFACE
+#endif
+
+#define __ECHO_STATE_MUTE (1 << 8)
+#define ECHO_STATE_IDLE (0)
+#define ECHO_STATE_PRETRAINING (1 | (__ECHO_STATE_MUTE))
+#define ECHO_STATE_STARTTRAINING (2 | (__ECHO_STATE_MUTE))
+#define ECHO_STATE_AWAITINGECHO (3 | (__ECHO_STATE_MUTE))
+#define ECHO_STATE_TRAINING (4 | (__ECHO_STATE_MUTE))
+#define ECHO_STATE_ACTIVE (5)
+
+/* #define BUF_MUNGE */
+
+/* Grab fasthdlc with tables */
+#define FAST_HDLC_NEED_TABLES
+#include "fasthdlc.h"
+
+#include "zaptel.h"
+
+#ifdef LINUX26
+#include <linux/moduleparam.h>
+#endif
+
+/* Get helper arithmetic */
+#include "arith.h"
+#if defined(CONFIG_ZAPTEL_MMX) || defined(ECHO_CAN_FP)
+#include <asm/i387.h>
+#endif
+
+#define hdlc_to_ztchan(h) (((struct zt_hdlc *)(h))->chan)
+#define dev_to_ztchan(h) (((struct zt_hdlc *)(dev_to_hdlc(h)->priv))->chan)
+#ifdef LINUX26
+#define ztchan_to_dev(h) ((h)->hdlcnetdev->netdev)
+#else
+#define ztchan_to_dev(h) (&((h)->hdlcnetdev->netdev.netdev))
+#endif
+
+/* macro-oni for determining a unit (channel) number */
+#define UNIT(file) MINOR(file->f_dentry->d_inode->i_rdev)
+
+/* names of tx level settings */
+static char *zt_txlevelnames[] = {
+"0 db (CSU)/0-133 feet (DSX-1)",
+"133-266 feet (DSX-1)",
+"266-399 feet (DSX-1)",
+"399-533 feet (DSX-1)",
+"533-655 feet (DSX-1)",
+"-7.5db (CSU)",
+"-15db (CSU)",
+"-22.5db (CSU)"
+} ;
+
+EXPORT_SYMBOL(zt_transcode_fops);
+EXPORT_SYMBOL(zt_init_tone_state);
+EXPORT_SYMBOL(zt_dtmf_tone);
+EXPORT_SYMBOL(zt_register);
+EXPORT_SYMBOL(zt_unregister);
+EXPORT_SYMBOL(__zt_mulaw);
+EXPORT_SYMBOL(__zt_alaw);
+#ifdef CONFIG_CALC_XLAW
+EXPORT_SYMBOL(__zt_lineartoulaw);
+EXPORT_SYMBOL(__zt_lineartoalaw);
+#else
+EXPORT_SYMBOL(__zt_lin2mu);
+EXPORT_SYMBOL(__zt_lin2a);
+#endif
+EXPORT_SYMBOL(zt_lboname);
+EXPORT_SYMBOL(zt_transmit);
+EXPORT_SYMBOL(zt_receive);
+EXPORT_SYMBOL(zt_rbsbits);
+EXPORT_SYMBOL(zt_qevent_nolock);
+EXPORT_SYMBOL(zt_qevent_lock);
+EXPORT_SYMBOL(zt_hooksig);
+EXPORT_SYMBOL(zt_alarm_notify);
+EXPORT_SYMBOL(zt_set_dynamic_ioctl);
+EXPORT_SYMBOL(zt_ec_chunk);
+EXPORT_SYMBOL(zt_ec_span);
+EXPORT_SYMBOL(zt_hdlc_abort);
+EXPORT_SYMBOL(zt_hdlc_finish);
+EXPORT_SYMBOL(zt_hdlc_getbuf);
+EXPORT_SYMBOL(zt_hdlc_putbuf);
+EXPORT_SYMBOL(zt_alarm_channel);
+EXPORT_SYMBOL(zt_register_chardev);
+EXPORT_SYMBOL(zt_unregister_chardev);
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *proc_entries[ZT_MAX_SPANS];
+#endif
+
+/* Here are a couple important little additions for devfs */
+#ifdef CONFIG_DEVFS_FS
+static devfs_handle_t zaptel_devfs_dir;
+static devfs_handle_t channel;
+static devfs_handle_t pseudo;
+static devfs_handle_t ctl;
+static devfs_handle_t timer;
+#endif
+
+/* udev necessary data structures. Yeah! */
+#ifdef CONFIG_ZAP_UDEV
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+#define CLASS_DEV_CREATE(class, devt, device, name) \
+ class_device_create(class, NULL, devt, device, name)
+#else
+#define CLASS_DEV_CREATE(class, devt, device, name) \
+ class_device_create(class, devt, device, name)
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
+static struct class *zap_class = NULL;
+#else
+static struct class_simple *zap_class = NULL;
+#define class_create class_simple_create
+#define class_destroy class_simple_destroy
+#define class_device_create class_simple_device_add
+#define class_device_destroy(a, b) class_simple_device_remove(b)
+#endif
+
+#endif /* CONFIG_ZAP_UDEV */
+
+
+/* There is a table like this in the PPP driver, too */
+
+static int deftaps = 64;
+
+#if !defined(LINUX26)
+static
+__u16 fcstab[256] =
+{
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+#endif
+
+static int debug;
+
+/* states for transmit signalling */
+typedef enum {ZT_TXSTATE_ONHOOK,ZT_TXSTATE_OFFHOOK,ZT_TXSTATE_START,
+ ZT_TXSTATE_PREWINK,ZT_TXSTATE_WINK,ZT_TXSTATE_PREFLASH,
+ ZT_TXSTATE_FLASH,ZT_TXSTATE_DEBOUNCE,ZT_TXSTATE_AFTERSTART,
+ ZT_TXSTATE_RINGON,ZT_TXSTATE_RINGOFF,ZT_TXSTATE_KEWL,
+ ZT_TXSTATE_AFTERKEWL,ZT_TXSTATE_PULSEBREAK,ZT_TXSTATE_PULSEMAKE,
+ ZT_TXSTATE_PULSEAFTER
+ } ZT_TXSTATE_t;
+
+typedef short sumtype[ZT_MAX_CHUNKSIZE];
+
+static sumtype sums[(ZT_MAX_CONF + 1) * 3];
+
+/* Translate conference aliases into actual conferences
+ and vice-versa */
+static short confalias[ZT_MAX_CONF + 1];
+static short confrev[ZT_MAX_CONF + 1];
+
+static sumtype *conf_sums_next;
+static sumtype *conf_sums;
+static sumtype *conf_sums_prev;
+
+static struct zt_span *master;
+static struct file_operations zt_fops;
+struct file_operations *zt_transcode_fops = NULL;
+
+static struct
+{
+ int src; /* source conf number */
+ int dst; /* dst conf number */
+} conf_links[ZT_MAX_CONF + 1];
+
+
+/* There are three sets of conference sum accumulators. One for the current
+sample chunk (conf_sums), one for the next sample chunk (conf_sums_next), and
+one for the previous sample chunk (conf_sums_prev). The following routine
+(rotate_sums) "rotates" the pointers to these accululator arrays as part
+of the events of sample chink processing as follows:
+
+The following sequence is designed to be looked at from the reference point
+of the receive routine of the master span.
+
+1. All (real span) receive chunks are processed (with putbuf). The last one
+to be processed is the master span. The data received is loaded into the
+accumulators for the next chunk (conf_sums_next), to be in alignment with
+current data after rotate_sums() is called (which immediately follows).
+Keep in mind that putbuf is *also* a transmit routine for the pseudo parts
+of channels that are in the REALANDPSEUDO conference mode. These channels
+are processed from data in the current sample chunk (conf_sums), being
+that this is a "transmit" function (for the pseudo part).
+
+2. rotate_sums() is called.
+
+3. All pseudo channel receive chunks are processed. This data is loaded into
+the current sample chunk accumulators (conf_sums).
+
+4. All conference links are processed (being that all receive data for this
+chunk has already been processed by now).
+
+5. All pseudo channel transmit chunks are processed. This data is loaded from
+the current sample chunk accumulators (conf_sums).
+
+6. All (real span) transmit chunks are processed (with getbuf). This data is
+loaded from the current sample chunk accumulators (conf_sums). Keep in mind
+that getbuf is *also* a receive routine for the pseudo part of channels that
+are in the REALANDPSEUDO conference mode. These samples are loaded into
+the next sample chunk accumulators (conf_sums_next) to be processed as part
+of the next sample chunk's data (next time around the world).
+
+*/
+
+#define DIGIT_MODE_DTMF 0
+#define DIGIT_MODE_MFV1 1
+#define DIGIT_MODE_PULSE 2
+
+#include "digits.h"
+
+static struct zt_dialparams global_dialparams = {
+ .dtmf_tonelen = DEFAULT_DTMF_LENGTH,
+ .mfv1_tonelen = DEFAULT_MFV1_LENGTH,
+};
+
+static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit);
+
+#if defined(CONFIG_ZAPTEL_MMX) || defined(ECHO_CAN_FP)
+/* XXX kernel_fpu_begin() is NOT exported properly (in 2.4), so we have to make
+ a local version. Somebody fix this! XXX */
+
+#ifndef LINUX26
+static inline void __save_init_fpu( struct task_struct *tsk )
+{
+ if ( cpu_has_fxsr ) {
+ asm volatile( "fxsave %0 ; fnclex"
+ : "=m" (tsk->thread.i387.fxsave) );
+ } else {
+ asm volatile( "fnsave %0 ; fwait"
+ : "=m" (tsk->thread.i387.fsave) );
+ }
+ tsk->flags &= ~PF_USEDFPU;
+}
+
+static inline void zt_kernel_fpu_begin(void)
+{
+ struct task_struct *tsk = current;
+ if (tsk->flags & PF_USEDFPU) {
+ __save_init_fpu(tsk);
+ return;
+ }
+ clts();
+}
+#else
+#define zt_kernel_fpu_begin kernel_fpu_begin
+#endif /* LINUX26 */
+#endif
+
+static struct zt_timer {
+ int ms; /* Countdown */
+ int pos; /* Position */
+ int ping; /* Whether we've been ping'd */
+ int tripped; /* Whether we're tripped */
+ struct zt_timer *next; /* Linked list */
+ wait_queue_head_t sel;
+} *zaptimers = NULL;
+
+#ifdef DEFINE_SPINLOCK
+static DEFINE_SPINLOCK(zaptimerlock);
+static DEFINE_SPINLOCK(bigzaplock);
+#else
+static spinlock_t zaptimerlock = SPIN_LOCK_UNLOCKED;
+static spinlock_t bigzaplock = SPIN_LOCK_UNLOCKED;
+#endif
+
+struct zt_zone {
+ atomic_t refcount;
+ char name[40]; /* Informational, only */
+ int ringcadence[ZT_MAX_CADENCE];
+ struct zt_tone *tones[ZT_TONE_MAX];
+ /* Each of these is a circular list
+ of zt_tones to generate what we
+ want. Use NULL if the tone is
+ unavailable */
+ struct zt_tone dtmf[16]; /* DTMF tones for this zone, with desired length */
+ struct zt_tone dtmf_continuous[16]; /* DTMF tones for this zone, continuous play */
+ struct zt_tone mf[15]; /* MF tones for this zone, with desired length */
+ struct zt_tone mf_continuous[15]; /* MF tones for this zone, continuous play */
+};
+
+static struct zt_span *spans[ZT_MAX_SPANS];
+static struct zt_chan *chans[ZT_MAX_CHANNELS];
+
+static int maxspans = 0;
+static int maxchans = 0;
+static int maxconfs = 0;
+static int maxlinks = 0;
+
+static int default_zone = -1;
+
+short __zt_mulaw[256];
+short __zt_alaw[256];
+
+#ifndef CONFIG_CALC_XLAW
+u_char __zt_lin2mu[16384];
+
+u_char __zt_lin2a[16384];
+#endif
+
+static u_char defgain[256];
+
+#ifdef DEFINE_RWLOCK
+static DEFINE_RWLOCK(zone_lock);
+static DEFINE_RWLOCK(chan_lock);
+#else
+static rwlock_t zone_lock = RW_LOCK_UNLOCKED;
+static rwlock_t chan_lock = RW_LOCK_UNLOCKED;
+#endif
+
+static struct zt_zone *tone_zones[ZT_TONE_ZONE_MAX];
+
+#define NUM_SIGS 10
+
+
+/* Echo cancellation */
+#if defined(ECHO_CAN_HPEC)
+#include "hpec/hpec_zaptel.h"
+#elif defined(ECHO_CAN_STEVE)
+#include "sec.h"
+#elif defined(ECHO_CAN_STEVE2)
+#include "sec-2.h"
+#elif defined(ECHO_CAN_KB1)
+#include "kb1ec.h"
+#elif defined(ECHO_CAN_MG2)
+#include "mg2ec.h"
+#elif defined(ECHO_CAN_JP1)
+#include "jpah.h"
+#endif
+
+static inline void rotate_sums(void)
+{
+ /* Rotate where we sum and so forth */
+ static int pos = 0;
+ conf_sums_prev = sums + (ZT_MAX_CONF + 1) * pos;
+ conf_sums = sums + (ZT_MAX_CONF + 1) * ((pos + 1) % 3);
+ conf_sums_next = sums + (ZT_MAX_CONF + 1) * ((pos + 2) % 3);
+ pos = (pos + 1) % 3;
+ memset(conf_sums_next, 0, maxconfs * sizeof(sumtype));
+}
+
+ /* return quiescent (idle) signalling states, for the various signalling types */
+static int zt_q_sig(struct zt_chan *chan)
+{
+int x;
+
+static unsigned int in_sig[NUM_SIGS][2] = {
+ { ZT_SIG_NONE, 0},
+ { ZT_SIG_EM, 0 | (ZT_ABIT << 8)},
+ { ZT_SIG_FXSLS,ZT_BBIT | (ZT_BBIT << 8)},
+ { ZT_SIG_FXSGS,ZT_ABIT | ZT_BBIT | ((ZT_ABIT | ZT_BBIT) << 8)},
+ { ZT_SIG_FXSKS,ZT_BBIT | ZT_BBIT | ((ZT_ABIT | ZT_BBIT) << 8)},
+ { ZT_SIG_FXOLS,0 | (ZT_ABIT << 8)},
+ { ZT_SIG_FXOGS,ZT_BBIT | ((ZT_ABIT | ZT_BBIT) << 8)},
+ { ZT_SIG_FXOKS,0 | (ZT_ABIT << 8)},
+ { ZT_SIG_SF, 0},
+ { ZT_SIG_EM_E1, ZT_DBIT | ((ZT_ABIT | ZT_DBIT) << 8) },
+ } ;
+
+ /* must have span to begin with */
+ if (!chan->span) return(-1);
+ /* if RBS does not apply, return error */
+ if (!(chan->span->flags & ZT_FLAG_RBS) ||
+ !chan->span->rbsbits) return(-1);
+ if (chan->sig == ZT_SIG_CAS)
+ return chan->idlebits;
+ for (x=0;x<NUM_SIGS;x++) {
+ if (in_sig[x][0] == chan->sig) return(in_sig[x][1]);
+ } return(-1); /* not found -- error */
+}
+
+#ifdef CONFIG_PROC_FS
+static char *sigstr(int sig)
+{
+ switch (sig) {
+ case ZT_SIG_FXSLS:
+ return "FXSLS";
+ case ZT_SIG_FXSKS:
+ return "FXSKS";
+ case ZT_SIG_FXSGS:
+ return "FXSGS";
+ case ZT_SIG_FXOLS:
+ return "FXOLS";
+ case ZT_SIG_FXOKS:
+ return "FXOKS";
+ case ZT_SIG_FXOGS:
+ return "FXOGS";
+ case ZT_SIG_EM:
+ return "E&M";
+ case ZT_SIG_EM_E1:
+ return "E&M-E1";
+ case ZT_SIG_CLEAR:
+ return "Clear";
+ case ZT_SIG_HDLCRAW:
+ return "HDLCRAW";
+ case ZT_SIG_HDLCFCS:
+ return "HDLCFCS";
+ case ZT_SIG_HDLCNET:
+ return "HDLCNET";
+ case ZT_SIG_HARDHDLC:
+ return "Hardware-assisted HDLC";
+ case ZT_SIG_SLAVE:
+ return "Slave";
+ case ZT_SIG_CAS:
+ return "CAS";
+ case ZT_SIG_DACS:
+ return "DACS";
+ case ZT_SIG_DACS_RBS:
+ return "DACS+RBS";
+ case ZT_SIG_SF:
+ return "SF (ToneOnly)";
+ case ZT_SIG_NONE:
+ default:
+ return "Unconfigured";
+ }
+
+}
+
+static int zaptel_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ int x, len = 0;
+ long span;
+
+ /* In Linux 2.6, this MUST NOT EXECEED 1024 bytes in one read! */
+
+ span = (long)data;
+
+ if (!span)
+ return 0;
+
+ if (spans[span]->name)
+ len += sprintf(page + len, "Span %ld: %s ", span, spans[span]->name);
+ if (spans[span]->desc)
+ len += sprintf(page + len, "\"%s\"", spans[span]->desc);
+ else
+ len += sprintf(page + len, "\"\"");
+
+ if(spans[span] == master)
+ len += sprintf(page + len, " (MASTER)");
+
+ if (spans[span]->lineconfig) {
+ /* framing first */
+ if (spans[span]->lineconfig & ZT_CONFIG_B8ZS)
+ len += sprintf(page + len, " B8ZS/");
+ else if (spans[span]->lineconfig & ZT_CONFIG_AMI)
+ len += sprintf(page + len, " AMI/");
+ else if (spans[span]->lineconfig & ZT_CONFIG_HDB3)
+ len += sprintf(page + len, " HDB3/");
+ /* then coding */
+ if (spans[span]->lineconfig & ZT_CONFIG_ESF)
+ len += sprintf(page + len, "ESF");
+ else if (spans[span]->lineconfig & ZT_CONFIG_D4)
+ len += sprintf(page + len, "D4");
+ else if (spans[span]->lineconfig & ZT_CONFIG_CCS)
+ len += sprintf(page + len, "CCS");
+ /* E1's can enable CRC checking */
+ if (spans[span]->lineconfig & ZT_CONFIG_CRC4)
+ len += sprintf(page + len, "/CRC4");
+ }
+
+ len += sprintf(page + len, " ");
+
+ /* list alarms */
+ if (spans[span]->alarms && (spans[span]->alarms > 0)) {
+ if (spans[span]->alarms & ZT_ALARM_BLUE)
+ len += sprintf(page + len, "BLUE ");
+ if (spans[span]->alarms & ZT_ALARM_YELLOW)
+ len += sprintf(page + len, "YELLOW ");
+ if (spans[span]->alarms & ZT_ALARM_RED)
+ len += sprintf(page + len, "RED ");
+ if (spans[span]->alarms & ZT_ALARM_LOOPBACK)
+ len += sprintf(page + len, "LOOP ");
+ if (spans[span]->alarms & ZT_ALARM_RECOVER)
+ len += sprintf(page + len, "RECOVERING ");
+ if (spans[span]->alarms & ZT_ALARM_NOTOPEN)
+ len += sprintf(page + len, "NOTOPEN ");
+
+ }
+ if (spans[span]->syncsrc && (spans[span]->syncsrc == spans[span]->spanno))
+ len += sprintf(page + len, "ClockSource ");
+ len += sprintf(page + len, "\n");
+ if (spans[span]->bpvcount)
+ len += sprintf(page + len, "\tBPV count: %d\n", spans[span]->bpvcount);
+ if (spans[span]->crc4count)
+ len += sprintf(page + len, "\tCRC4 error count: %d\n", spans[span]->crc4count);
+ if (spans[span]->ebitcount)
+ len += sprintf(page + len, "\tE-bit error count: %d\n", spans[span]->ebitcount);
+ if (spans[span]->fascount)
+ len += sprintf(page + len, "\tFAS error count: %d\n", spans[span]->fascount);
+ if (spans[span]->irqmisses)
+ len += sprintf(page + len, "\tIRQ misses: %d\n", spans[span]->irqmisses);
+ if (spans[span]->timingslips)
+ len += sprintf(page + len, "\tTiming slips: %d\n", spans[span]->timingslips);
+ len += sprintf(page + len, "\n");
+
+
+ for (x=1;x<ZT_MAX_CHANNELS;x++) {
+ if (chans[x]) {
+ if (chans[x]->span && (chans[x]->span->spanno == span)) {
+ if (chans[x]->name)
+ len += sprintf(page + len, "\t%4d %s ", x, chans[x]->name);
+ if (chans[x]->sig) {
+ if (chans[x]->sig == ZT_SIG_SLAVE)
+ len += sprintf(page + len, "%s ", sigstr(chans[x]->master->sig));
+ else {
+ len += sprintf(page + len, "%s ", sigstr(chans[x]->sig));
+ if (chans[x]->nextslave && chans[x]->master->channo == x)
+ len += sprintf(page + len, "Master ");
+ }
+ }
+ if ((chans[x]->flags & ZT_FLAG_OPEN)) {
+ len += sprintf(page + len, "(In use) ");
+ }
+#ifdef OPTIMIZE_CHANMUTE
+ if ((chans[x]->chanmute)) {
+ len += sprintf(page + len, "(no pcm) ");
+ }
+#endif
+ len += sprintf(page + len, "\n");
+ }
+ if (len <= off) { /* If everything printed so far is before beginning of request */
+ off -= len;
+ len = 0;
+ }
+ if (len > off+count) /* stop if we've already generated enough */
+ break;
+ }
+ }
+ if (len <= off) { /* If everything printed so far is before beginning of request */
+ off -= len;
+ len = 0;
+ }
+ *start = page + off;
+ len -= off; /* un-count any remaining offset */
+ if (len > count) len = count; /* don't return bytes not asked for */
+ return len;
+}
+#endif
+
+static int zt_first_empty_alias(void)
+{
+ /* Find the first conference which has no alias pointing to it */
+ int x;
+ for (x=1;x<ZT_MAX_CONF;x++) {
+ if (!confrev[x])
+ return x;
+ }
+ return -1;
+}
+
+static void recalc_maxconfs(void)
+{
+ int x;
+ for (x=ZT_MAX_CONF-1;x>0;x--) {
+ if (confrev[x]) {
+ maxconfs = x+1;
+ return;
+ }
+ }
+ maxconfs = 0;
+}
+
+static void recalc_maxlinks(void)
+{
+ int x;
+ for (x=ZT_MAX_CONF-1;x>0;x--) {
+ if (conf_links[x].src || conf_links[x].dst) {
+ maxlinks = x+1;
+ return;
+ }
+ }
+ maxlinks = 0;
+}
+
+static int zt_first_empty_conference(void)
+{
+ /* Find the first conference which has no alias */
+ int x;
+ for (x=ZT_MAX_CONF-1;x>0;x--) {
+ if (!confalias[x])
+ return x;
+ }
+ return -1;
+}
+
+static int zt_get_conf_alias(int x)
+{
+ int a;
+ if (confalias[x]) {
+ return confalias[x];
+ }
+
+ /* Allocate an alias */
+ a = zt_first_empty_alias();
+ confalias[x] = a;
+ confrev[a] = x;
+
+ /* Highest conference may have changed */
+ recalc_maxconfs();
+ return a;
+}
+
+static void zt_check_conf(int x)
+{
+ int y;
+
+ /* return if no valid conf number */
+ if (x <= 0) return;
+ /* Return if there is no alias */
+ if (!confalias[x])
+ return;
+ for (y=0;y<maxchans;y++) {
+ if (chans[y] && (chans[y]->confna == x) &&
+ ((chans[y]->confmode & ZT_CONF_MODE_MASK) == ZT_CONF_CONF ||
+ (chans[y]->confmode & ZT_CONF_MODE_MASK) == ZT_CONF_CONFANN ||
+ (chans[y]->confmode & ZT_CONF_MODE_MASK) == ZT_CONF_CONFMON ||
+ (chans[y]->confmode & ZT_CONF_MODE_MASK) == ZT_CONF_CONFANNMON ||
+ (chans[y]->confmode & ZT_CONF_MODE_MASK) == ZT_CONF_REALANDPSEUDO))
+ return;
+ }
+ /* If we get here, nobody is in the conference anymore. Clear it out
+ both forward and reverse */
+ confrev[confalias[x]] = 0;
+ confalias[x] = 0;
+
+ /* Highest conference may have changed */
+ recalc_maxconfs();
+}
+
+/* enqueue an event on a channel */
+static void __qevent(struct zt_chan *chan, int event)
+{
+
+ /* if full, ignore */
+ if ((chan->eventoutidx == 0) && (chan->eventinidx == (ZT_MAX_EVENTSIZE - 1)))
+ return;
+ /* if full, ignore */
+ if (chan->eventinidx == (chan->eventoutidx - 1)) return;
+ /* save the event */
+ chan->eventbuf[chan->eventinidx++] = event;
+ /* wrap the index, if necessary */
+ if (chan->eventinidx >= ZT_MAX_EVENTSIZE) chan->eventinidx = 0;
+ /* wake em all up */
+ if (chan->iomask & ZT_IOMUX_SIGEVENT) wake_up_interruptible(&chan->eventbufq);
+ wake_up_interruptible(&chan->readbufq);
+ wake_up_interruptible(&chan->writebufq);
+ wake_up_interruptible(&chan->sel);
+ return;
+}
+
+void zt_qevent_nolock(struct zt_chan *chan, int event)
+{
+ __qevent(chan, event);
+}
+
+void zt_qevent_lock(struct zt_chan *chan, int event)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&chan->lock, flags);
+ __qevent(chan, event);
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+/* sleep in user space until woken up. Equivilant of tsleep() in BSD */
+static int schluffen(wait_queue_head_t *q)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ add_wait_queue(q, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+ if (!signal_pending(current)) schedule();
+ current->state = TASK_RUNNING;
+ remove_wait_queue(q, &wait);
+ if (signal_pending(current)) {
+ return -ERESTARTSYS;
+ }
+ return(0);
+}
+
+static inline void calc_fcs(struct zt_chan *ss, int inwritebuf)
+{
+ int x;
+ unsigned int fcs=PPP_INITFCS;
+ unsigned char *data = ss->writebuf[inwritebuf];
+ int len = ss->writen[inwritebuf];
+ /* Not enough space to do FCS calculation */
+ if (len < 2)
+ return;
+ for (x=0;x<len-2;x++)
+ fcs = PPP_FCS(fcs, data[x]);
+ fcs ^= 0xffff;
+ /* Send out the FCS */
+ data[len-2] = (fcs & 0xff);
+ data[len-1] = (fcs >> 8) & 0xff;
+}
+
+static int zt_reallocbufs(struct zt_chan *ss, int j, int numbufs)
+{
+ unsigned char *newbuf, *oldbuf;
+ unsigned long flags;
+ int x;
+ /* Check numbufs */
+ if (numbufs < 2)
+ numbufs = 2;
+ if (numbufs > ZT_MAX_NUM_BUFS)
+ numbufs = ZT_MAX_NUM_BUFS;
+ /* We need to allocate our buffers now */
+ if (j) {
+ newbuf = kmalloc(j * 2 * numbufs, GFP_KERNEL);
+ if (!newbuf)
+ return (-ENOMEM);
+ } else
+ newbuf = NULL;
+ /* Now that we've allocated our new buffer, we can safely
+ move things around... */
+ spin_lock_irqsave(&ss->lock, flags);
+ ss->blocksize = j; /* set the blocksize */
+ oldbuf = ss->readbuf[0]; /* Keep track of the old buffer */
+ ss->readbuf[0] = NULL;
+ if (newbuf) {
+ for (x=0;x<numbufs;x++) {
+ ss->readbuf[x] = newbuf + x * j;
+ ss->writebuf[x] = newbuf + (numbufs + x) * j;
+ }
+ } else {
+ for (x=0;x<numbufs;x++) {
+ ss->readbuf[x] = NULL;
+ ss->writebuf[x] = NULL;
+ }
+ }
+ /* Mark all buffers as empty */
+ for (x=0;x<numbufs;x++)
+ ss->writen[x] =
+ ss->writeidx[x]=
+ ss->readn[x]=
+ ss->readidx[x] = 0;
+
+ /* Keep track of where our data goes (if it goes
+ anywhere at all) */
+ if (newbuf) {
+ ss->inreadbuf = 0;
+ ss->inwritebuf = 0;
+ } else {
+ ss->inreadbuf = -1;
+ ss->inwritebuf = -1;
+ }
+ ss->outreadbuf = -1;
+ ss->outwritebuf = -1;
+ ss->numbufs = numbufs;
+ if (ss->txbufpolicy == ZT_POLICY_WHEN_FULL)
+ ss->txdisable = 1;
+ else
+ ss->txdisable = 0;
+
+ if (ss->rxbufpolicy == ZT_POLICY_WHEN_FULL)
+ ss->rxdisable = 1;
+ else
+ ss->rxdisable = 0;
+
+ spin_unlock_irqrestore(&ss->lock, flags);
+ if (oldbuf)
+ kfree(oldbuf);
+ return 0;
+}
+
+static int zt_hangup(struct zt_chan *chan);
+static void zt_set_law(struct zt_chan *chan, int law);
+
+/* Pull a ZT_CHUNKSIZE piece off the queue. Returns
+ 0 on success or -1 on failure. If failed, provides
+ silence */
+static int __buf_pull(struct confq *q, u_char *data, struct zt_chan *c, char *label)
+{
+ int oldoutbuf = q->outbuf;
+ /* Ain't nuffin to read */
+ if (q->outbuf < 0) {
+ if (data)
+ memset(data, ZT_LIN2X(0,c), ZT_CHUNKSIZE);
+ return -1;
+ }
+ if (data)
+ memcpy(data, q->buf[q->outbuf], ZT_CHUNKSIZE);
+ q->outbuf = (q->outbuf + 1) % ZT_CB_SIZE;
+
+ /* Won't be nuffin next time */
+ if (q->outbuf == q->inbuf) {
+ q->outbuf = -1;
+ }
+
+ /* If they thought there was no space then
+ there is now where we just read */
+ if (q->inbuf < 0)
+ q->inbuf = oldoutbuf;
+ return 0;
+}
+
+/* Returns a place to put stuff, or NULL if there is
+ no room */
+
+static u_char *__buf_pushpeek(struct confq *q)
+{
+ if (q->inbuf < 0)
+ return NULL;
+ return q->buf[q->inbuf];
+}
+
+static u_char *__buf_peek(struct confq *q)
+{
+ if (q->outbuf < 0)
+ return NULL;
+ return q->buf[q->outbuf];
+}
+
+#ifdef BUF_MUNGE
+static u_char *__buf_cpush(struct confq *q)
+{
+ int pos;
+ /* If we have no space, return where the
+ last space that we *did* have was */
+ if (q->inbuf > -1)
+ return NULL;
+ pos = q->outbuf - 1;
+ if (pos < 0)
+ pos += ZT_CB_SIZE;
+ return q->buf[pos];
+}
+
+static void __buf_munge(struct zt_chan *chan, u_char *old, u_char *new)
+{
+ /* Run a weighted average of the old and new, in order to
+ mask a missing sample */
+ int x;
+ int val;
+ for (x=0;x<ZT_CHUNKSIZE;x++) {
+ val = x * ZT_XLAW(new[x], chan) + (ZT_CHUNKSIZE - x - 1) * ZT_XLAW(old[x], chan);
+ val = val / (ZT_CHUNKSIZE - 1);
+ old[x] = ZT_LIN2X(val, chan);
+ }
+}
+#endif
+/* Push something onto the queue, or assume what
+ is there is valid if data is NULL */
+static int __buf_push(struct confq *q, u_char *data, char *label)
+{
+ int oldinbuf = q->inbuf;
+ if (q->inbuf < 0) {
+ return -1;
+ }
+ if (data)
+ /* Copy in the data */
+ memcpy(q->buf[q->inbuf], data, ZT_CHUNKSIZE);
+
+ /* Advance the inbuf pointer */
+ q->inbuf = (q->inbuf + 1) % ZT_CB_SIZE;
+
+ if (q->inbuf == q->outbuf) {
+ /* No space anymore... */
+ q->inbuf = -1;
+ }
+ /* If they don't think data is ready, let
+ them know it is now */
+ if (q->outbuf < 0) {
+ q->outbuf = oldinbuf;
+ }
+ return 0;
+}
+
+static void reset_conf(struct zt_chan *chan)
+{
+ int x;
+ /* Empty out buffers and reset to initialization */
+ for (x=0;x<ZT_CB_SIZE;x++)
+ chan->confin.buf[x] = chan->confin.buffer + ZT_CHUNKSIZE * x;
+ chan->confin.inbuf = 0;
+ chan->confin.outbuf = -1;
+
+ for (x=0;x<ZT_CB_SIZE;x++)
+ chan->confout.buf[x] = chan->confout.buffer + ZT_CHUNKSIZE * x;
+ chan->confout.inbuf = 0;
+ chan->confout.outbuf = -1;
+}
+
+
+static void close_channel(struct zt_chan *chan)
+{
+ unsigned long flags;
+ void *rxgain = NULL;
+ struct echo_can_state *ec = NULL;
+ int oldconf;
+ short *readchunkpreec;
+#ifdef CONFIG_ZAPATA_PPP
+ struct ppp_channel *ppp;
+#endif
+
+ /* XXX Buffers should be send out before reallocation!!! XXX */
+ if (!(chan->flags & ZT_FLAG_NOSTDTXRX))
+ zt_reallocbufs(chan, 0, 0);
+ spin_lock_irqsave(&chan->lock, flags);
+#ifdef CONFIG_ZAPATA_PPP
+ ppp = chan->ppp;
+ chan->ppp = NULL;
+#endif
+ ec = chan->ec;
+ chan->ec = NULL;
+ readchunkpreec = chan->readchunkpreec;
+ chan->readchunkpreec = NULL;
+ chan->curtone = NULL;
+ if (chan->curzone)
+ atomic_dec(&chan->curzone->refcount);
+ chan->curzone = NULL;
+ chan->cadencepos = 0;
+ chan->pdialcount = 0;
+ zt_hangup(chan);
+ chan->itimerset = chan->itimer = 0;
+ chan->pulsecount = 0;
+ chan->pulsetimer = 0;
+ chan->ringdebtimer = 0;
+ init_waitqueue_head(&chan->sel);
+ init_waitqueue_head(&chan->readbufq);
+ init_waitqueue_head(&chan->writebufq);
+ init_waitqueue_head(&chan->eventbufq);
+ init_waitqueue_head(&chan->txstateq);
+ chan->txdialbuf[0] = '\0';
+ chan->digitmode = DIGIT_MODE_DTMF;
+ chan->dialing = 0;
+ chan->afterdialingtimer = 0;
+ /* initialize IO MUX mask */
+ chan->iomask = 0;
+ /* save old conf number, if any */
+ oldconf = chan->confna;
+ /* initialize conference variables */
+ chan->_confn = 0;
+ if ((chan->sig & __ZT_SIG_DACS) != __ZT_SIG_DACS) {
+ chan->confna = 0;
+ chan->confmode = 0;
+ }
+ chan->confmute = 0;
+ /* release conference resource, if any to release */
+ if (oldconf) zt_check_conf(oldconf);
+ chan->gotgs = 0;
+ reset_conf(chan);
+
+ if (chan->gainalloc && chan->rxgain)
+ rxgain = chan->rxgain;
+
+ chan->rxgain = defgain;
+ chan->txgain = defgain;
+ chan->gainalloc = 0;
+ chan->eventinidx = chan->eventoutidx = 0;
+ chan->flags &= ~(ZT_FLAG_LOOPED | ZT_FLAG_LINEAR | ZT_FLAG_PPP | ZT_FLAG_SIGFREEZE);
+
+ zt_set_law(chan,0);
+
+ memset(chan->conflast, 0, sizeof(chan->conflast));
+ memset(chan->conflast1, 0, sizeof(chan->conflast1));
+ memset(chan->conflast2, 0, sizeof(chan->conflast2));
+
+ if (chan->span && chan->span->dacs && oldconf)
+ chan->span->dacs(chan, NULL);
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ if (chan->span && chan->span->echocan)
+ chan->span->echocan(chan, 0);
+
+ if (rxgain)
+ kfree(rxgain);
+ if (ec)
+ echo_can_free(ec);
+ if (readchunkpreec)
+ kfree(readchunkpreec);
+
+#ifdef CONFIG_ZAPATA_PPP
+ if (ppp) {
+ tasklet_kill(&chan->ppp_calls);
+ skb_queue_purge(&chan->ppp_rq);
+ ppp_unregister_channel(ppp);
+ kfree(ppp);
+ }
+#endif
+
+}
+
+static int free_tone_zone(int num)
+{
+ struct zt_zone *z;
+
+ if ((num >= ZT_TONE_ZONE_MAX) || (num < 0))
+ return -EINVAL;
+
+ write_lock(&zone_lock);
+ z = tone_zones[num];
+ tone_zones[num] = NULL;
+ write_unlock(&zone_lock);
+ if (!z)
+ return 0;
+
+ if (atomic_read(&z->refcount)) {
+ /* channels are still using this zone so put it back */
+ write_lock(&zone_lock);
+ tone_zones[num] = z;
+ write_unlock(&zone_lock);
+
+ return -EBUSY;
+ } else {
+ kfree(z);
+
+ return 0;
+ }
+}
+
+static int zt_register_tone_zone(int num, struct zt_zone *zone)
+{
+ int res = 0;
+
+ if ((num >= ZT_TONE_ZONE_MAX) || (num < 0))
+ return -EINVAL;
+
+ write_lock(&zone_lock);
+ if (tone_zones[num]) {
+ res = -EINVAL;
+ } else {
+ res = 0;
+ tone_zones[num] = zone;
+ }
+ write_unlock(&zone_lock);
+
+ if (!res)
+ printk(KERN_INFO "Registered tone zone %d (%s)\n", num, zone->name);
+
+ return res;
+}
+
+static int start_tone(struct zt_chan *chan, int tone)
+{
+ int res = -EINVAL;
+
+ /* Stop the current tone, no matter what */
+ chan->tonep = 0;
+ chan->curtone = NULL;
+ chan->pdialcount = 0;
+ chan->txdialbuf[0] = '\0';
+ chan->dialing = 0;
+
+ if (tone == -1) {
+ /* Just stop the current tone */
+ res = 0;
+ } else if ((tone >= 0 && tone <= ZT_TONE_MAX)) {
+ if (chan->curzone) {
+ /* Have a tone zone */
+ if (chan->curzone->tones[tone]) {
+ chan->curtone = chan->curzone->tones[tone];
+ res = 0;
+ } else /* Indicate that zone is loaded but no such tone exists */
+ res = -ENOSYS;
+ } else /* Note that no tone zone exists at the moment */
+ res = -ENODATA;
+ } else if (tone >= ZT_TONE_DTMF_BASE && tone <= ZT_TONE_DTMF_MAX) {
+ /* ZT_SENDTONE should never be used on a channel configured for pulse dialing */
+ chan->dialing = 1;
+ res = 0;
+ if ((chan->digitmode == DIGIT_MODE_DTMF) &&
+ (tone >= ZT_TONE_DTMF_BASE) &&
+ (tone <= ZT_TONE_DTMF_MAX))
+ chan->curtone = &chan->curzone->dtmf_continuous[tone - ZT_TONE_DTMF_BASE];
+ else if ((chan->digitmode == DIGIT_MODE_MFV1) &&
+ (tone >= ZT_TONE_MF_BASE) &&
+ (tone <= ZT_TONE_MF_MAX))
+ chan->curtone = &chan->curzone->mf_continuous[tone - ZT_TONE_MF_BASE];
+ else {
+ chan->dialing = 0;
+ res = -EINVAL;
+ }
+ }
+
+ if (chan->curtone)
+ zt_init_tone_state(&chan->ts, chan->curtone);
+
+ return res;
+}
+
+static int set_tone_zone(struct zt_chan *chan, int zone)
+{
+ int res = 0;
+ struct zt_zone *z;
+ unsigned long flags;
+
+ /* Do not call with the channel locked. */
+
+ if (zone == -1)
+ zone = default_zone;
+
+ if ((zone >= ZT_TONE_ZONE_MAX) || (zone < 0))
+ return -EINVAL;
+
+ read_lock(&zone_lock);
+
+ if ((z = tone_zones[zone])) {
+ spin_lock_irqsave(&chan->lock, flags);
+ if (chan->curzone)
+ atomic_dec(&chan->curzone->refcount);
+
+ atomic_inc(&z->refcount);
+ chan->curzone = z;
+ chan->tonezone = zone;
+ memcpy(chan->ringcadence, z->ringcadence, sizeof(chan->ringcadence));
+ spin_unlock_irqrestore(&chan->lock, flags);
+ } else {
+ res = -ENODATA;
+ }
+
+ read_unlock(&zone_lock);
+
+ return res;
+}
+
+static void zt_set_law(struct zt_chan *chan, int law)
+{
+ if (!law) {
+ if (chan->deflaw)
+ law = chan->deflaw;
+ else
+ if (chan->span) law = chan->span->deflaw;
+ else law = ZT_LAW_MULAW;
+ }
+ if (law == ZT_LAW_ALAW) {
+ chan->xlaw = __zt_alaw;
+#ifdef CONFIG_CALC_XLAW
+ chan->lineartoxlaw = __zt_lineartoalaw;
+#else
+ chan->lin2x = __zt_lin2a;
+#endif
+ } else {
+ chan->xlaw = __zt_mulaw;
+#ifdef CONFIG_CALC_XLAW
+ chan->lineartoxlaw = __zt_lineartoulaw;
+#else
+ chan->lin2x = __zt_lin2mu;
+#endif
+ }
+}
+
+#ifdef CONFIG_DEVFS_FS
+static devfs_handle_t register_devfs_channel(struct zt_chan *chan, devfs_handle_t dir)
+{
+ char path[100];
+ char link[100];
+ char buf[50];
+ char tmp[100];
+ int link_offset = 0;
+ int tmp_offset = 0;
+ int path_offset = 0;
+ int err = 0;
+ devfs_handle_t chan_dev;
+ umode_t mode = S_IFCHR|S_IRUGO|S_IWUGO;
+ unsigned int flags = DEVFS_FL_AUTO_OWNER;
+
+ sprintf(path, "%d", chan->chanpos);
+ chan_dev = devfs_register(dir, path, flags, ZT_MAJOR, chan->channo, mode, &zt_fops, NULL);
+ if (!chan_dev) {
+ printk("zaptel: Something really bad happened. Unable to register devfs entry\n");
+ return NULL;
+ }
+
+ /* Set up the path of the destination of the link */
+ link_offset = devfs_generate_path(chan_dev, link, sizeof(link) - 1);
+ /* Now we need to strip off the leading "zap/". If we don't, then we build a broken symlink */
+ path_offset = devfs_generate_path(zaptel_devfs_dir, path, sizeof(path) - 1); /* We'll just "borrow" path for a second */
+ path_offset = strlen(path+path_offset);
+ link_offset += path_offset; /* Taking out the "zap" */
+ link_offset++; /* Add one more place for the '/'. The path generated does not contain the '/' we need to strip */
+
+ /* Set up the path of the file/link itself */
+ tmp_offset = devfs_generate_path(zaptel_devfs_dir, tmp, sizeof(tmp) - 1);
+ sprintf(buf, "/%d", chan->channo);
+ strncpy(path, tmp+tmp_offset, sizeof(path) - 1);
+ strncat(path, buf, sizeof(path) - 1);
+
+ err = devfs_mk_symlink(NULL, path, DEVFS_FL_DEFAULT, link+link_offset, &chan->fhandle_symlink, NULL);
+ if (err != 0) {
+ printk("Problem with making devfs symlink: %d\n", err);
+ }
+
+ return chan_dev;
+}
+#endif /* CONFIG_DEVFS_FS */
+
+static int zt_chan_reg(struct zt_chan *chan)
+{
+ int x;
+ int res=0;
+ unsigned long flags;
+
+ write_lock_irqsave(&chan_lock, flags);
+ for (x=1;x<ZT_MAX_CHANNELS;x++) {
+ if (!chans[x]) {
+ spin_lock_init(&chan->lock);
+ chans[x] = chan;
+ if (maxchans < x + 1)
+ maxchans = x + 1;
+ chan->channo = x;
+ if (!chan->master)
+ chan->master = chan;
+ if (!chan->readchunk)
+ chan->readchunk = chan->sreadchunk;
+ if (!chan->writechunk)
+ chan->writechunk = chan->swritechunk;
+ zt_set_law(chan, 0);
+ close_channel(chan);
+ /* set this AFTER running close_channel() so that
+ HDLC channels wont cause hangage */
+ chan->flags |= ZT_FLAG_REGISTERED;
+ res = 0;
+ break;
+ }
+ }
+ write_unlock_irqrestore(&chan_lock, flags);
+ if (x >= ZT_MAX_CHANNELS)
+ printk(KERN_ERR "No more channels available\n");
+ return res;
+}
+
+char *zt_lboname(int x)
+{
+ if ((x < 0) || ( x > 7))
+ return "Unknown";
+ return zt_txlevelnames[x];
+}
+
+#if defined(CONFIG_ZAPATA_NET) || defined(CONFIG_ZAPATA_PPP)
+#endif
+
+#ifdef CONFIG_ZAPATA_NET
+#ifdef NEW_HDLC_INTERFACE
+static int zt_net_open(struct net_device *dev)
+{
+#ifdef LINUX26
+ int res = hdlc_open(dev);
+ struct zt_chan *ms = dev_to_ztchan(dev);
+#else
+ hdlc_device *hdlc = dev_to_hdlc(dev);
+ struct zt_chan *ms = hdlc_to_ztchan(hdlc);
+ int res = hdlc_open(hdlc);
+#endif
+
+/* if (!dev->hard_start_xmit) return res; is this really necessary? --byg */
+ if (res) /* this is necessary to avoid kernel panic when UNSPEC link encap, proven --byg */
+ return res;
+#else
+static int zt_net_open(hdlc_device *hdlc)
+{
+ struct zt_chan *ms = hdlc_to_ztchan(hdlc);
+ int res;
+#endif
+ if (!ms) {
+ printk("zt_net_open: nothing??\n");
+ return -EINVAL;
+ }
+ if (ms->flags & ZT_FLAG_OPEN) {
+ printk("%s is already open!\n", ms->name);
+ return -EBUSY;
+ }
+ if (!(ms->flags & ZT_FLAG_NETDEV)) {
+ printk("%s is not a net device!\n", ms->name);
+ return -EINVAL;
+ }
+ ms->txbufpolicy = ZT_POLICY_IMMEDIATE;
+ ms->rxbufpolicy = ZT_POLICY_IMMEDIATE;
+
+ res = zt_reallocbufs(ms, ZT_DEFAULT_MTU_MRU, ZT_DEFAULT_NUM_BUFS);
+ if (res)
+ return res;
+
+ fasthdlc_init(&ms->rxhdlc);
+ fasthdlc_init(&ms->txhdlc);
+ ms->infcs = PPP_INITFCS;
+
+ netif_start_queue(ztchan_to_dev(ms));
+
+#ifndef LINUX26
+ MOD_INC_USE_COUNT;
+#endif
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("ZAPNET: Opened channel %d name %s\n", ms->channo, ms->name);
+#endif
+ return 0;
+}
+
+#ifdef LINUX26
+static int zt_register_hdlc_device(struct net_device *dev, const char *dev_name)
+{
+ int result;
+
+ if (dev_name && *dev_name) {
+ if ((result = dev_alloc_name(dev, dev_name)) < 0)
+ return result;
+ }
+ result = register_netdev(dev);
+ if (result != 0)
+ return -EIO;
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,14)
+ if (netif_carrier_ok(dev))
+ netif_carrier_off(dev); /* no carrier until DCD goes up */
+#endif
+ return 0;
+}
+#endif
+
+#ifdef NEW_HDLC_INTERFACE
+static int zt_net_stop(struct net_device *dev)
+{
+#ifdef LINUX26
+ hdlc_device *h = dev_to_hdlc(dev);
+ struct zt_hdlc *hdlc = h->priv;
+#else
+ hdlc_device *hdlc = dev_to_hdlc(dev);
+#endif
+
+#else
+static void zt_net_close(hdlc_device *hdlc)
+{
+#endif
+ struct zt_chan *ms = hdlc_to_ztchan(hdlc);
+ if (!ms) {
+#ifdef NEW_HDLC_INTERFACE
+ printk("zt_net_stop: nothing??\n");
+ return 0;
+#else
+ printk("zt_net_close: nothing??\n");
+ return;
+#endif
+ }
+ if (!(ms->flags & ZT_FLAG_NETDEV)) {
+#ifdef NEW_HDLC_INTERFACE
+ printk("zt_net_stop: %s is not a net device!\n", ms->name);
+ return 0;
+#else
+ printk("zt_net_close: %s is not a net device!\n", ms->name);
+ return;
+#endif
+ }
+ /* Not much to do here. Just deallocate the buffers */
+ netif_stop_queue(ztchan_to_dev(ms));
+ zt_reallocbufs(ms, 0, 0);
+#ifdef LINUX26
+ hdlc_close(dev);
+#else
+#ifndef CONFIG_OLD_HDLC_API
+ hdlc_close(hdlc);
+#endif
+#endif
+#ifndef LINUX26
+ MOD_DEC_USE_COUNT;
+#endif
+#ifdef NEW_HDLC_INTERFACE
+ return 0;
+#else
+ return;
+#endif
+}
+
+#ifdef NEW_HDLC_INTERFACE
+/* kernel 2.4.20+ has introduced attach function, dunno what to do,
+ just copy sources from dscc4 to be sure and ready for further mastering,
+ NOOP right now (i.e. really a stub) --byg */
+#ifdef LINUX26
+static int zt_net_attach(struct net_device *dev, unsigned short encoding,
+ unsigned short parity)
+#else
+static int zt_net_attach(hdlc_device *hdlc, unsigned short encoding,
+ unsigned short parity)
+#endif
+{
+/* struct net_device *dev = hdlc_to_dev(hdlc);
+ struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
+
+ if (encoding != ENCODING_NRZ &&
+ encoding != ENCODING_NRZI &&
+ encoding != ENCODING_FM_MARK &&
+ encoding != ENCODING_FM_SPACE &&
+ encoding != ENCODING_MANCHESTER)
+ return -EINVAL;
+
+ if (parity != PARITY_NONE &&
+ parity != PARITY_CRC16_PR0_CCITT &&
+ parity != PARITY_CRC16_PR1_CCITT &&
+ parity != PARITY_CRC32_PR0_CCITT &&
+ parity != PARITY_CRC32_PR1_CCITT)
+ return -EINVAL;
+
+ dpriv->encoding = encoding;
+ dpriv->parity = parity;*/
+ return 0;
+}
+#endif
+
+static struct zt_hdlc *zt_hdlc_alloc(void)
+{
+ struct zt_hdlc *tmp;
+ tmp = kmalloc(sizeof(struct zt_hdlc), GFP_KERNEL);
+ if (tmp) {
+ memset(tmp, 0, sizeof(struct zt_hdlc));
+ }
+ return tmp;
+}
+
+#ifdef NEW_HDLC_INTERFACE
+static int zt_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ /* FIXME: this construction seems to be not very optimal for me but I could find nothing better at the moment (Friday, 10PM :( ) --byg */
+/* struct zt_chan *ss = hdlc_to_ztchan(list_entry(dev, struct zt_hdlc, netdev.netdev));*/
+#ifdef LINUX26
+ struct zt_chan *ss = dev_to_ztchan(dev);
+ struct net_device_stats *stats = hdlc_stats(dev);
+#else
+ struct zt_chan *ss = (list_entry(dev, struct zt_hdlc, netdev.netdev)->chan);
+ struct net_device_stats *stats = &ss->hdlcnetdev->netdev.stats;
+#endif
+
+#else
+static int zt_xmit(hdlc_device *hdlc, struct sk_buff *skb)
+{
+ struct zt_chan *ss = hdlc_to_ztchan(hdlc);
+ struct net_device *dev = &ss->hdlcnetdev->netdev.netdev;
+ struct net_device_stats *stats = &ss->hdlcnetdev->netdev.stats;
+#endif
+ int retval = 1;
+ int x,oldbuf;
+ unsigned int fcs;
+ unsigned char *data;
+ unsigned long flags;
+ /* See if we have any buffers */
+ spin_lock_irqsave(&ss->lock, flags);
+ if (skb->len > ss->blocksize - 2) {
+ printk(KERN_ERR "zt_xmit(%s): skb is too large (%d > %d)\n", dev->name, skb->len, ss->blocksize -2);
+ stats->tx_dropped++;
+ retval = 0;
+ } else if (ss->inwritebuf >= 0) {
+ /* We have a place to put this packet */
+ /* XXX We should keep the SKB and avoid the memcpy XXX */
+ data = ss->writebuf[ss->inwritebuf];
+ memcpy(data, skb->data, skb->len);
+ ss->writen[ss->inwritebuf] = skb->len;
+ ss->writeidx[ss->inwritebuf] = 0;
+ /* Calculate the FCS */
+ fcs = PPP_INITFCS;
+ for (x=0;x<skb->len;x++)
+ fcs = PPP_FCS(fcs, data[x]);
+ /* Invert it */
+ fcs ^= 0xffff;
+ /* Send it out LSB first */
+ data[ss->writen[ss->inwritebuf]++] = (fcs & 0xff);
+ data[ss->writen[ss->inwritebuf]++] = (fcs >> 8) & 0xff;
+ /* Advance to next window */
+ oldbuf = ss->inwritebuf;
+ ss->inwritebuf = (ss->inwritebuf + 1) % ss->numbufs;
+
+ if (ss->inwritebuf == ss->outwritebuf) {
+ /* Whoops, no more space. */
+ ss->inwritebuf = -1;
+
+ netif_stop_queue(ztchan_to_dev(ss));
+ }
+ if (ss->outwritebuf < 0) {
+ /* Let the interrupt handler know there's
+ some space for us */
+ ss->outwritebuf = oldbuf;
+ }
+ dev->trans_start = jiffies;
+ stats->tx_packets++;
+ stats->tx_bytes += ss->writen[oldbuf];
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("Buffered %d bytes to go out in buffer %d\n", ss->writen[oldbuf], oldbuf);
+ for (x=0;x<ss->writen[oldbuf];x++)
+ printk("%02x ", ss->writebuf[oldbuf][x]);
+ printk("\n");
+#endif
+ retval = 0;
+ /* Free the SKB */
+ dev_kfree_skb_any(skb);
+ }
+ spin_unlock_irqrestore(&ss->lock, flags);
+ return retval;
+}
+
+#ifdef NEW_HDLC_INTERFACE
+static int zt_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ return hdlc_ioctl(dev, ifr, cmd);
+}
+#else
+static int zt_net_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd)
+{
+ return -EIO;
+}
+#endif
+
+#endif
+
+#ifdef CONFIG_ZAPATA_PPP
+
+static int zt_ppp_xmit(struct ppp_channel *ppp, struct sk_buff *skb)
+{
+
+ /*
+ * If we can't handle the packet right now, return 0. If we
+ * we handle or drop it, return 1. Always free if we return
+ * 1 and never if we return 0
+ */
+ struct zt_chan *ss = ppp->private;
+ int x,oldbuf;
+ unsigned int fcs;
+ unsigned char *data;
+ long flags;
+ int retval = 0;
+
+ /* See if we have any buffers */
+ spin_lock_irqsave(&ss->lock, flags);
+ if (!(ss->flags & ZT_FLAG_OPEN)) {
+ printk("Can't transmit on closed channel\n");
+ retval = 1;
+ } else if (skb->len > ss->blocksize - 4) {
+ printk(KERN_ERR "zt_ppp_xmit(%s): skb is too large (%d > %d)\n", ss->name, skb->len, ss->blocksize -2);
+ retval = 1;
+ } else if (ss->inwritebuf >= 0) {
+ /* We have a place to put this packet */
+ /* XXX We should keep the SKB and avoid the memcpy XXX */
+ data = ss->writebuf[ss->inwritebuf];
+ /* Start with header of two bytes */
+ /* Add "ALL STATIONS" and "UNNUMBERED" */
+ data[0] = 0xff;
+ data[1] = 0x03;
+ ss->writen[ss->inwritebuf] = 2;
+
+ /* Copy real data and increment amount written */
+ memcpy(data + 2, skb->data, skb->len);
+
+ ss->writen[ss->inwritebuf] += skb->len;
+
+ /* Re-set index back to zero */
+ ss->writeidx[ss->inwritebuf] = 0;
+
+ /* Calculate the FCS */
+ fcs = PPP_INITFCS;
+ for (x=0;x<skb->len + 2;x++)
+ fcs = PPP_FCS(fcs, data[x]);
+ /* Invert it */
+ fcs ^= 0xffff;
+
+ /* Point past the real data now */
+ data += (skb->len + 2);
+
+ /* Send FCS out LSB first */
+ data[0] = (fcs & 0xff);
+ data[1] = (fcs >> 8) & 0xff;
+
+ /* Account for FCS length */
+ ss->writen[ss->inwritebuf]+=2;
+
+ /* Advance to next window */
+ oldbuf = ss->inwritebuf;
+ ss->inwritebuf = (ss->inwritebuf + 1) % ss->numbufs;
+
+ if (ss->inwritebuf == ss->outwritebuf) {
+ /* Whoops, no more space. */
+ ss->inwritebuf = -1;
+ }
+ if (ss->outwritebuf < 0) {
+ /* Let the interrupt handler know there's
+ some space for us */
+ ss->outwritebuf = oldbuf;
+ }
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("Buffered %d bytes (skblen = %d) to go out in buffer %d\n", ss->writen[oldbuf], skb->len, oldbuf);
+ for (x=0;x<ss->writen[oldbuf];x++)
+ printk("%02x ", ss->writebuf[oldbuf][x]);
+ printk("\n");
+#endif
+ retval = 1;
+ }
+ spin_unlock_irqrestore(&ss->lock, flags);
+ if (retval) {
+ /* Get rid of the SKB if we're returning non-zero */
+ /* N.B. this is called in process or BH context so
+ dev_kfree_skb is OK. */
+ dev_kfree_skb(skb);
+ }
+ return retval;
+}
+
+static int zt_ppp_ioctl(struct ppp_channel *ppp, unsigned int cmd, unsigned long flags)
+{
+ return -EIO;
+}
+
+static struct ppp_channel_ops ztppp_ops =
+{
+ start_xmit: zt_ppp_xmit,
+ ioctl: zt_ppp_ioctl,
+};
+
+#endif
+
+static void zt_chan_unreg(struct zt_chan *chan)
+{
+ int x;
+ unsigned long flags;
+#ifdef CONFIG_ZAPATA_NET
+ if (chan->flags & ZT_FLAG_NETDEV) {
+#ifdef LINUX26
+ unregister_hdlc_device(chan->hdlcnetdev->netdev);
+ free_netdev(chan->hdlcnetdev->netdev);
+#else
+ unregister_hdlc_device(&chan->hdlcnetdev->netdev);
+#endif
+ kfree(chan->hdlcnetdev);
+ chan->hdlcnetdev = NULL;
+ }
+#endif
+ write_lock_irqsave(&chan_lock, flags);
+ if (chan->flags & ZT_FLAG_REGISTERED) {
+ chans[chan->channo] = NULL;
+ chan->flags &= ~ZT_FLAG_REGISTERED;
+ }
+#ifdef CONFIG_ZAPATA_PPP
+ if (chan->ppp) {
+ printk("HUH??? PPP still attached??\n");
+ }
+#endif
+ maxchans = 0;
+ for (x=1;x<ZT_MAX_CHANNELS;x++)
+ if (chans[x]) {
+ maxchans = x + 1;
+ /* Remove anyone pointing to us as master
+ and make them their own thing */
+ if (chans[x]->master == chan) {
+ chans[x]->master = chans[x];
+ }
+ if ((chans[x]->confna == chan->channo) &&
+ ((chans[x]->confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITOR ||
+ (chans[x]->confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITORTX ||
+ (chans[x]->confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITORBOTH ||
+ (chans[x]->confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITOR_RX_PREECHO ||
+ (chans[x]->confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITOR_TX_PREECHO ||
+ (chans[x]->confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITORBOTH_PREECHO ||
+ (chans[x]->confmode & ZT_CONF_MODE_MASK) == ZT_CONF_DIGITALMON)) {
+ /* Take them out of conference with us */
+ /* release conference resource if any */
+ if (chans[x]->confna) {
+ zt_check_conf(chans[x]->confna);
+ if (chans[x]->span && chans[x]->span->dacs)
+ chans[x]->span->dacs(chans[x], NULL);
+ }
+ chans[x]->confna = 0;
+ chans[x]->_confn = 0;
+ chans[x]->confmode = 0;
+ }
+ }
+ chan->channo = -1;
+ write_unlock_irqrestore(&chan_lock, flags);
+}
+
+static ssize_t zt_chan_read(struct file *file, char *usrbuf, size_t count, int unit)
+{
+ struct zt_chan *chan = chans[unit];
+ int amnt;
+ int res, rv;
+ int oldbuf,x;
+ unsigned long flags;
+ /* Make sure count never exceeds 65k, and make sure it's unsigned */
+ count &= 0xffff;
+ if (!chan)
+ return -EINVAL;
+ if (count < 1)
+ return -EINVAL;
+ for(;;) {
+ spin_lock_irqsave(&chan->lock, flags);
+ if (chan->eventinidx != chan->eventoutidx) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return -ELAST /* - chan->eventbuf[chan->eventoutidx]*/;
+ }
+ res = chan->outreadbuf;
+ if (chan->rxdisable)
+ res = -1;
+ spin_unlock_irqrestore(&chan->lock, flags);
+ if (res >= 0) break;
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ rv = schluffen(&chan->readbufq);
+ if (rv) return (rv);
+ }
+ amnt = count;
+/* added */
+#if 0
+ if ((unit == 24) || (unit == 48) || (unit == 16) || (unit == 47)) {
+ int myamnt = amnt;
+ int x;
+ if (amnt > chan->readn[res])
+ myamnt = chan->readn[res];
+ printk("zt_chan_read(unit: %d, inwritebuf: %d, outwritebuf: %d amnt: %d\n",
+ unit, chan->inwritebuf, chan->outwritebuf, myamnt);
+ printk("\t("); for (x = 0; x < myamnt; x++) printk((x ? " %02x" : "%02x"), (unsigned char)usrbuf[x]);
+ printk(")\n");
+ }
+#endif
+/* end addition */
+ if (chan->flags & ZT_FLAG_LINEAR) {
+ if (amnt > (chan->readn[res] << 1))
+ amnt = chan->readn[res] << 1;
+ if (amnt) {
+ /* There seems to be a max stack size, so we have
+ to do this in smaller pieces */
+ short lindata[128];
+ int left = amnt >> 1; /* amnt is in bytes */
+ int pos = 0;
+ int pass;
+ while(left) {
+ pass = left;
+ if (pass > 128)
+ pass = 128;
+ for (x=0;x<pass;x++)
+ lindata[x] = ZT_XLAW(chan->readbuf[res][x + pos], chan);
+ if (copy_to_user(usrbuf + (pos << 1), lindata, pass << 1))
+ return -EFAULT;
+ left -= pass;
+ pos += pass;
+ }
+ }
+ } else {
+ if (amnt > chan->readn[res])
+ amnt = chan->readn[res];
+ if (amnt) {
+ if (copy_to_user(usrbuf, chan->readbuf[res], amnt))
+ return -EFAULT;
+ }
+ }
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->readidx[res] = 0;
+ chan->readn[res] = 0;
+ oldbuf = res;
+ chan->outreadbuf = (res + 1) % chan->numbufs;
+ if (chan->outreadbuf == chan->inreadbuf) {
+ /* Out of stuff */
+ chan->outreadbuf = -1;
+ if (chan->rxbufpolicy == ZT_POLICY_WHEN_FULL)
+ chan->rxdisable = 1;
+ }
+ if (chan->inreadbuf < 0) {
+ /* Notify interrupt handler that we have some space now */
+ chan->inreadbuf = oldbuf;
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ return amnt;
+}
+
+static ssize_t zt_chan_write(struct file *file, const char *usrbuf, size_t count, int unit)
+{
+ unsigned long flags;
+ struct zt_chan *chan = chans[unit];
+ int res, amnt, oldbuf, rv,x;
+ /* Make sure count never exceeds 65k, and make sure it's unsigned */
+ count &= 0xffff;
+ if (!chan)
+ return -EINVAL;
+ if (count < 1)
+ return -EINVAL;
+ for(;;) {
+ spin_lock_irqsave(&chan->lock, flags);
+ if ((chan->curtone || chan->pdialcount) && !(chan->flags & ZT_FLAG_PSEUDO)) {
+ chan->curtone = NULL;
+ chan->tonep = 0;
+ chan->dialing = 0;
+ chan->txdialbuf[0] = '\0';
+ chan->pdialcount = 0;
+ }
+ if (chan->eventinidx != chan->eventoutidx) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return -ELAST;
+ }
+ res = chan->inwritebuf;
+ spin_unlock_irqrestore(&chan->lock, flags);
+ if (res >= 0)
+ break;
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ /* Wait for something to be available */
+ rv = schluffen(&chan->writebufq);
+ if (rv)
+ return rv;
+ }
+
+ amnt = count;
+ if (chan->flags & ZT_FLAG_LINEAR) {
+ if (amnt > (chan->blocksize << 1))
+ amnt = chan->blocksize << 1;
+ } else {
+ if (amnt > chan->blocksize)
+ amnt = chan->blocksize;
+ }
+
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("zt_chan_write(unit: %d, res: %d, outwritebuf: %d amnt: %d\n",
+ unit, chan->res, chan->outwritebuf, amnt);
+#endif
+#if 0
+ if ((unit == 24) || (unit == 48) || (unit == 16) || (unit == 47)) {
+ int x;
+ printk("zt_chan_write/in(unit: %d, res: %d, outwritebuf: %d amnt: %d, txdisable: %d)\n",
+ unit, res, chan->outwritebuf, amnt, chan->txdisable);
+ printk("\t("); for (x = 0; x < amnt; x++) printk((x ? " %02x" : "%02x"), (unsigned char)usrbuf[x]);
+ printk(")\n");
+ }
+#endif
+
+ if (amnt) {
+ if (chan->flags & ZT_FLAG_LINEAR) {
+ /* There seems to be a max stack size, so we have
+ to do this in smaller pieces */
+ short lindata[128];
+ int left = amnt >> 1; /* amnt is in bytes */
+ int pos = 0;
+ int pass;
+ while(left) {
+ pass = left;
+ if (pass > 128)
+ pass = 128;
+ if (copy_from_user(lindata, usrbuf + (pos << 1), pass << 1))
+ return -EFAULT;
+ left -= pass;
+ for (x=0;x<pass;x++)
+ chan->writebuf[res][x + pos] = ZT_LIN2X(lindata[x], chan);
+ pos += pass;
+ }
+ chan->writen[res] = amnt >> 1;
+ } else {
+ if (copy_from_user(chan->writebuf[res], usrbuf, amnt))
+ return -EFAULT;
+ chan->writen[res] = amnt;
+ }
+ chan->writeidx[res] = 0;
+ if (chan->flags & ZT_FLAG_FCS)
+ calc_fcs(chan, res);
+ oldbuf = res;
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->inwritebuf = (res + 1) % chan->numbufs;
+ if (chan->inwritebuf == chan->outwritebuf) {
+ /* Don't stomp on the transmitter, just wait for them to
+ wake us up */
+ chan->inwritebuf = -1;
+ /* Make sure the transmitter is transmitting in case of POLICY_WHEN_FULL */
+ chan->txdisable = 0;
+ }
+ if (chan->outwritebuf < 0) {
+ /* Okay, the interrupt handler has been waiting for us. Give them a buffer */
+ chan->outwritebuf = oldbuf;
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ if (chan->flags & ZT_FLAG_NOSTDTXRX && chan->span->hdlc_hard_xmit)
+ chan->span->hdlc_hard_xmit(chan);
+ }
+ return amnt;
+}
+
+static int zt_ctl_open(struct inode *inode, struct file *file)
+{
+ /* Nothing to do, really */
+#ifndef LINUX26
+ MOD_INC_USE_COUNT;
+#endif
+ return 0;
+}
+
+static int zt_chan_open(struct inode *inode, struct file *file)
+{
+ /* Nothing to do here for now either */
+#ifndef LINUX26
+ MOD_INC_USE_COUNT;
+#endif
+ return 0;
+}
+
+static int zt_ctl_release(struct inode *inode, struct file *file)
+{
+ /* Nothing to do */
+#ifndef LINUX26
+ MOD_DEC_USE_COUNT;
+#endif
+ return 0;
+}
+
+static int zt_chan_release(struct inode *inode, struct file *file)
+{
+ /* Nothing to do for now */
+#ifndef LINUX26
+ MOD_DEC_USE_COUNT;
+#endif
+ return 0;
+}
+
+static void set_txtone(struct zt_chan *ss,int fac, int init_v2, int init_v3)
+{
+ if (fac == 0)
+ {
+ ss->v2_1 = 0;
+ ss->v3_1 = 0;
+ return;
+ }
+ ss->txtone = fac;
+ ss->v1_1 = 0;
+ ss->v2_1 = init_v2;
+ ss->v3_1 = init_v3;
+ return;
+}
+
+static void zt_rbs_sethook(struct zt_chan *chan, int txsig, int txstate, int timeout)
+{
+static int outs[NUM_SIGS][5] = {
+/* We set the idle case of the ZT_SIG_NONE to this pattern to make idle E1 CAS
+channels happy. Should not matter with T1, since on an un-configured channel,
+who cares what the sig bits are as long as they are stable */
+ { ZT_SIG_NONE, ZT_ABIT | ZT_CBIT | ZT_DBIT, 0, 0, 0 }, /* no signalling */
+ { ZT_SIG_EM, 0, ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT,
+ ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT, 0 }, /* E and M */
+ { ZT_SIG_FXSLS, ZT_BBIT | ZT_DBIT,
+ ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT,
+ ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT, 0 }, /* FXS Loopstart */
+ { ZT_SIG_FXSGS, ZT_BBIT | ZT_DBIT,
+#ifdef CONFIG_CAC_GROUNDSTART
+ ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT, 0, 0 }, /* FXS Groundstart (CAC-style) */
+#else
+ ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT, ZT_ABIT | ZT_CBIT, 0 }, /* FXS Groundstart (normal) */
+#endif
+ { ZT_SIG_FXSKS, ZT_BBIT | ZT_DBIT,
+ ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT,
+ ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT, 0 }, /* FXS Kewlstart */
+ { ZT_SIG_FXOLS, ZT_BBIT | ZT_DBIT, ZT_BBIT | ZT_DBIT, 0, 0 }, /* FXO Loopstart */
+ { ZT_SIG_FXOGS, ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT,
+ ZT_BBIT | ZT_DBIT, 0, 0 }, /* FXO Groundstart */
+ { ZT_SIG_FXOKS, ZT_BBIT | ZT_DBIT, ZT_BBIT | ZT_DBIT, 0,
+ ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT }, /* FXO Kewlstart */
+ { ZT_SIG_SF, ZT_BBIT | ZT_CBIT | ZT_DBIT,
+ ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT,
+ ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT,
+ ZT_BBIT | ZT_CBIT | ZT_DBIT }, /* no signalling */
+ { ZT_SIG_EM_E1, ZT_DBIT, ZT_ABIT | ZT_BBIT | ZT_DBIT,
+ ZT_ABIT | ZT_BBIT | ZT_DBIT, ZT_DBIT }, /* E and M E1 */
+ } ;
+ int x;
+
+ /* if no span, return doing nothing */
+ if (!chan->span) return;
+ if (!chan->span->flags & ZT_FLAG_RBS) {
+ printk("zt_rbs: Tried to set RBS hook state on non-RBS channel %s\n", chan->name);
+ return;
+ }
+ if ((txsig > 3) || (txsig < 0)) {
+ printk("zt_rbs: Tried to set RBS hook state %d (> 3) on channel %s\n", txsig, chan->name);
+ return;
+ }
+ if (!chan->span->rbsbits && !chan->span->hooksig) {
+ printk("zt_rbs: Tried to set RBS hook state %d on channel %s while span %s lacks rbsbits or hooksig function\n",
+ txsig, chan->name, chan->span->name);
+ return;
+ }
+ /* Don't do anything for RBS */
+ if (chan->sig == ZT_SIG_DACS_RBS)
+ return;
+ chan->txstate = txstate;
+
+ /* if tone signalling */
+ if (chan->sig == ZT_SIG_SF)
+ {
+ chan->txhooksig = txsig;
+ if (chan->txtone) /* if set to make tone for tx */
+ {
+ if ((txsig && !(chan->toneflags & ZT_REVERSE_TXTONE)) ||
+ ((!txsig) && (chan->toneflags & ZT_REVERSE_TXTONE)))
+ {
+ set_txtone(chan,chan->txtone,chan->tx_v2,chan->tx_v3);
+ }
+ else
+ {
+ set_txtone(chan,0,0,0);
+ }
+ }
+ chan->otimer = timeout * ZT_CHUNKSIZE; /* Otimer is timer in samples */
+ return;
+ }
+ if (chan->span->hooksig) {
+ if (chan->txhooksig != txsig) {
+ chan->txhooksig = txsig;
+ chan->span->hooksig(chan, txsig);
+ }
+ chan->otimer = timeout * ZT_CHUNKSIZE; /* Otimer is timer in samples */
+ return;
+ } else {
+ for (x=0;x<NUM_SIGS;x++) {
+ if (outs[x][0] == chan->sig) {
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("Setting bits to %d for channel %s state %d in %d signalling\n", outs[x][txsig + 1], chan->name, txsig, chan->sig);
+#endif
+ chan->txhooksig = txsig;
+ chan->txsig = outs[x][txsig+1];
+ chan->span->rbsbits(chan, chan->txsig);
+ chan->otimer = timeout * ZT_CHUNKSIZE; /* Otimer is timer in samples */
+ return;
+ }
+ }
+ }
+ printk("zt_rbs: Don't know RBS signalling type %d on channel %s\n", chan->sig, chan->name);
+}
+
+static int zt_cas_setbits(struct zt_chan *chan, int bits)
+{
+ /* if no span, return as error */
+ if (!chan->span) return -1;
+ if (chan->span->rbsbits) {
+ chan->txsig = bits;
+ chan->span->rbsbits(chan, bits);
+ } else {
+ printk("Huh? CAS setbits, but no RBS bits function\n");
+ }
+ return 0;
+}
+
+static int zt_hangup(struct zt_chan *chan)
+{
+ int x,res=0;
+
+ /* Can't hangup pseudo channels */
+ if (!chan->span)
+ return 0;
+ /* Can't hang up a clear channel */
+ if (chan->flags & (ZT_FLAG_CLEAR | ZT_FLAG_NOSTDTXRX))
+ return -EINVAL;
+
+ chan->kewlonhook = 0;
+
+
+ if ((chan->sig == ZT_SIG_FXSLS) || (chan->sig == ZT_SIG_FXSKS) ||
+ (chan->sig == ZT_SIG_FXSGS)) chan->ringdebtimer = RING_DEBOUNCE_TIME;
+
+ if (chan->span->flags & ZT_FLAG_RBS) {
+ if (chan->sig == ZT_SIG_CAS) {
+ zt_cas_setbits(chan, chan->idlebits);
+ } else if ((chan->sig == ZT_SIG_FXOKS) && (chan->txstate != ZT_TXSTATE_ONHOOK)) {
+ /* Do RBS signalling on the channel's behalf */
+ zt_rbs_sethook(chan, ZT_TXSIG_KEWL, ZT_TXSTATE_KEWL, ZT_KEWLTIME);
+ } else
+ zt_rbs_sethook(chan, ZT_TXSIG_ONHOOK, ZT_TXSTATE_ONHOOK, 0);
+ } else {
+ /* Let the driver hang up the line if it wants to */
+ if (chan->span->sethook) {
+ if (chan->txhooksig != ZT_ONHOOK) {
+ chan->txhooksig = ZT_ONHOOK;
+ res = chan->span->sethook(chan, ZT_ONHOOK);
+ } else
+ res = 0;
+ }
+ }
+ /* if not registered yet, just return here */
+ if (!(chan->flags & ZT_FLAG_REGISTERED)) return res;
+ /* Mark all buffers as empty */
+ for (x = 0;x < chan->numbufs;x++) {
+ chan->writen[x] =
+ chan->writeidx[x]=
+ chan->readn[x]=
+ chan->readidx[x] = 0;
+ }
+ if (chan->readbuf[0]) {
+ chan->inreadbuf = 0;
+ chan->inwritebuf = 0;
+ } else {
+ chan->inreadbuf = -1;
+ chan->inwritebuf = -1;
+ }
+ chan->outreadbuf = -1;
+ chan->outwritebuf = -1;
+ chan->dialing = 0;
+ chan->afterdialingtimer = 0;
+ chan->curtone = NULL;
+ chan->pdialcount = 0;
+ chan->cadencepos = 0;
+ chan->txdialbuf[0] = 0;
+ return res;
+}
+
+static int initialize_channel(struct zt_chan *chan)
+{
+ int res;
+ unsigned long flags;
+ void *rxgain=NULL;
+ struct echo_can_state *ec=NULL;
+ if ((res = zt_reallocbufs(chan, ZT_DEFAULT_BLOCKSIZE, ZT_DEFAULT_NUM_BUFS)))
+ return res;
+
+ spin_lock_irqsave(&chan->lock, flags);
+
+ chan->rxbufpolicy = ZT_POLICY_IMMEDIATE;
+ chan->txbufpolicy = ZT_POLICY_IMMEDIATE;
+
+ /* Free up the echo canceller if there is one */
+ ec = chan->ec;
+ chan->ec = NULL;
+ chan->echocancel = 0;
+ chan->echostate = ECHO_STATE_IDLE;
+ chan->echolastupdate = 0;
+ chan->echotimer = 0;
+
+ chan->txdisable = 0;
+ chan->rxdisable = 0;
+
+ chan->digitmode = DIGIT_MODE_DTMF;
+ chan->dialing = 0;
+ chan->afterdialingtimer = 0;
+
+ chan->cadencepos = 0;
+ chan->firstcadencepos = 0; /* By default loop back to first cadence position */
+
+ /* HDLC & FCS stuff */
+ fasthdlc_init(&chan->rxhdlc);
+ fasthdlc_init(&chan->txhdlc);
+ chan->infcs = PPP_INITFCS;
+
+ /* Timings for RBS */
+ chan->prewinktime = ZT_DEFAULT_PREWINKTIME;
+ chan->preflashtime = ZT_DEFAULT_PREFLASHTIME;
+ chan->winktime = ZT_DEFAULT_WINKTIME;
+ chan->flashtime = ZT_DEFAULT_FLASHTIME;
+
+ if (chan->sig & __ZT_SIG_FXO)
+ chan->starttime = ZT_DEFAULT_RINGTIME;
+ else
+ chan->starttime = ZT_DEFAULT_STARTTIME;
+ chan->rxwinktime = ZT_DEFAULT_RXWINKTIME;
+ chan->rxflashtime = ZT_DEFAULT_RXFLASHTIME;
+ chan->debouncetime = ZT_DEFAULT_DEBOUNCETIME;
+ chan->pulsemaketime = ZT_DEFAULT_PULSEMAKETIME;
+ chan->pulsebreaktime = ZT_DEFAULT_PULSEBREAKTIME;
+ chan->pulseaftertime = ZT_DEFAULT_PULSEAFTERTIME;
+
+ /* Initialize RBS timers */
+ chan->itimerset = chan->itimer = chan->otimer = 0;
+ chan->ringdebtimer = 0;
+
+ init_waitqueue_head(&chan->sel);
+ init_waitqueue_head(&chan->readbufq);
+ init_waitqueue_head(&chan->writebufq);
+ init_waitqueue_head(&chan->eventbufq);
+ init_waitqueue_head(&chan->txstateq);
+
+ /* Reset conferences */
+ reset_conf(chan);
+
+ /* I/O Mask, etc */
+ chan->iomask = 0;
+ /* release conference resource if any */
+ if (chan->confna) zt_check_conf(chan->confna);
+ if ((chan->sig & __ZT_SIG_DACS) != __ZT_SIG_DACS) {
+ chan->confna = 0;
+ chan->confmode = 0;
+ if (chan->span && chan->span->dacs)
+ chan->span->dacs(chan, NULL);
+ }
+ chan->_confn = 0;
+ memset(chan->conflast, 0, sizeof(chan->conflast));
+ memset(chan->conflast1, 0, sizeof(chan->conflast1));
+ memset(chan->conflast2, 0, sizeof(chan->conflast2));
+ chan->confmute = 0;
+ chan->gotgs = 0;
+ chan->curtone = NULL;
+ chan->tonep = 0;
+ chan->pdialcount = 0;
+ if (chan->gainalloc && chan->rxgain)
+ rxgain = chan->rxgain;
+ chan->rxgain = defgain;
+ chan->txgain = defgain;
+ chan->gainalloc = 0;
+ chan->eventinidx = chan->eventoutidx = 0;
+ zt_set_law(chan,0);
+ zt_hangup(chan);
+
+ /* Make sure that the audio flag is cleared on a clear channel */
+ if ((chan->sig & ZT_SIG_CLEAR) || (chan->sig & ZT_SIG_HARDHDLC))
+ chan->flags &= ~ZT_FLAG_AUDIO;
+
+ if ((chan->sig == ZT_SIG_CLEAR) || (chan->sig == ZT_SIG_HARDHDLC))
+ chan->flags &= ~(ZT_FLAG_PPP | ZT_FLAG_FCS | ZT_FLAG_HDLC);
+
+ chan->flags &= ~ZT_FLAG_LINEAR;
+ if (chan->curzone) {
+ /* Take cadence from tone zone */
+ memcpy(chan->ringcadence, chan->curzone->ringcadence, sizeof(chan->ringcadence));
+ } else {
+ /* Do a default */
+ memset(chan->ringcadence, 0, sizeof(chan->ringcadence));
+ chan->ringcadence[0] = chan->starttime;
+ chan->ringcadence[1] = ZT_RINGOFFTIME;
+ }
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+ set_tone_zone(chan, -1);
+
+ if (chan->span && chan->span->echocan)
+ chan->span->echocan(chan, 0);
+
+ if (rxgain)
+ kfree(rxgain);
+ if (ec)
+ echo_can_free(ec);
+ return 0;
+}
+
+static int zt_timing_open(struct inode *inode, struct file *file)
+{
+ struct zt_timer *t;
+ unsigned long flags;
+ t = kmalloc(sizeof(struct zt_timer), GFP_KERNEL);
+ if (!t)
+ return -ENOMEM;
+ /* Allocate a new timer */
+ memset(t, 0, sizeof(struct zt_timer));
+ init_waitqueue_head(&t->sel);
+ file->private_data = t;
+#ifndef LINUX26
+ MOD_INC_USE_COUNT;
+#endif
+ spin_lock_irqsave(&zaptimerlock, flags);
+ t->next = zaptimers;
+ zaptimers = t;
+ spin_unlock_irqrestore(&zaptimerlock, flags);
+ return 0;
+}
+
+static int zt_timer_release(struct inode *inode, struct file *file)
+{
+ struct zt_timer *t, *cur, *prev;
+ unsigned long flags;
+ t = file->private_data;
+ if (t) {
+ spin_lock_irqsave(&zaptimerlock, flags);
+ prev = NULL;
+ cur = zaptimers;
+ while(cur) {
+ if (t == cur)
+ break;
+ prev = cur;
+ cur = cur->next;
+ }
+ if (cur) {
+ if (prev)
+ prev->next = cur->next;
+ else
+ zaptimers = cur->next;
+ }
+ spin_unlock_irqrestore(&zaptimerlock, flags);
+ if (!cur) {
+ printk("Zap Timer: Not on list??\n");
+ return 0;
+ }
+ kfree(t);
+#ifndef LINUX26
+ MOD_DEC_USE_COUNT;
+#endif
+ }
+ return 0;
+}
+
+static int zt_specchan_open(struct inode *inode, struct file *file, int unit, int inc)
+{
+ int res = 0;
+
+ if (chans[unit] && chans[unit]->sig) {
+ /* Make sure we're not already open, a net device, or a slave device */
+ if (chans[unit]->flags & ZT_FLAG_OPEN)
+ res = -EBUSY;
+ else if (chans[unit]->flags & ZT_FLAG_NETDEV)
+ res = -EBUSY;
+ else if (chans[unit]->master != chans[unit])
+ res = -EBUSY;
+ else if ((chans[unit]->sig & __ZT_SIG_DACS) == __ZT_SIG_DACS)
+ res = -EBUSY;
+ else {
+ unsigned long flags;
+ /* Assume everything is going to be okay */
+ res = initialize_channel(chans[unit]);
+ spin_lock_irqsave(&chans[unit]->lock, flags);
+ if (chans[unit]->flags & ZT_FLAG_PSEUDO)
+ chans[unit]->flags |= ZT_FLAG_AUDIO;
+ if (chans[unit]->span && chans[unit]->span->open) {
+ res = chans[unit]->span->open(chans[unit]);
+ }
+ if (!res) {
+ chans[unit]->file = file;
+#ifndef LINUX26
+ if (inc)
+ MOD_INC_USE_COUNT;
+#endif
+ chans[unit]->flags |= ZT_FLAG_OPEN;
+ spin_unlock_irqrestore(&chans[unit]->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&chans[unit]->lock, flags);
+ close_channel(chans[unit]);
+ }
+ }
+ } else
+ res = -ENXIO;
+ return res;
+}
+
+static int zt_specchan_release(struct inode *node, struct file *file, int unit)
+{
+ int res=0;
+ if (chans[unit]) {
+ unsigned long flags;
+ spin_lock_irqsave(&chans[unit]->lock, flags);
+ chans[unit]->flags &= ~ZT_FLAG_OPEN;
+ spin_unlock_irqrestore(&chans[unit]->lock, flags);
+ chans[unit]->file = NULL;
+ close_channel(chans[unit]);
+ if (chans[unit]->span && chans[unit]->span->close)
+ res = chans[unit]->span->close(chans[unit]);
+ } else
+ res = -ENXIO;
+#ifndef LINUX26
+ MOD_DEC_USE_COUNT;
+#endif
+ return res;
+}
+
+static struct zt_chan *zt_alloc_pseudo(void)
+{
+ struct zt_chan *pseudo;
+ unsigned long flags;
+ /* Don't allow /dev/zap/pseudo to open if there are no spans */
+ if (maxspans < 1)
+ return NULL;
+ pseudo = kmalloc(sizeof(struct zt_chan), GFP_KERNEL);
+ if (!pseudo)
+ return NULL;
+ memset(pseudo, 0, sizeof(struct zt_chan));
+ pseudo->sig = ZT_SIG_CLEAR;
+ pseudo->sigcap = ZT_SIG_CLEAR;
+ pseudo->flags = ZT_FLAG_PSEUDO | ZT_FLAG_AUDIO;
+ spin_lock_irqsave(&bigzaplock, flags);
+ if (zt_chan_reg(pseudo)) {
+ kfree(pseudo);
+ pseudo = NULL;
+ } else
+ sprintf(pseudo->name, "Pseudo/%d", pseudo->channo);
+ spin_unlock_irqrestore(&bigzaplock, flags);
+ return pseudo;
+}
+
+static void zt_free_pseudo(struct zt_chan *pseudo)
+{
+ unsigned long flags;
+ if (pseudo) {
+ spin_lock_irqsave(&bigzaplock, flags);
+ zt_chan_unreg(pseudo);
+ spin_unlock_irqrestore(&bigzaplock, flags);
+ kfree(pseudo);
+ }
+}
+
+static int zt_open(struct inode *inode, struct file *file)
+{
+ int unit = UNIT(file);
+ int ret = -ENXIO;
+ struct zt_chan *chan;
+ /* Minor 0: Special "control" descriptor */
+ if (!unit)
+ return zt_ctl_open(inode, file);
+ if (unit == 250) {
+ if (!zt_transcode_fops)
+ request_module("zttranscode");
+ if (zt_transcode_fops && zt_transcode_fops->open) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ if (zt_transcode_fops->owner) {
+ __MOD_INC_USE_COUNT (zt_transcode_fops->owner);
+#else
+ if (try_module_get(zt_transcode_fops->owner)) {
+#endif
+ ret = zt_transcode_fops->open(inode, file);
+ if (ret)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ __MOD_DEC_USE_COUNT (zt_transcode_fops->owner);
+#else
+ module_put(zt_transcode_fops->owner);
+#endif
+ }
+ return ret;
+ }
+ return -ENXIO;
+ }
+ if (unit == 253) {
+ if (maxspans) {
+ return zt_timing_open(inode, file);
+ } else {
+ return -ENXIO;
+ }
+ }
+ if (unit == 254)
+ return zt_chan_open(inode, file);
+ if (unit == 255) {
+ if (maxspans) {
+ chan = zt_alloc_pseudo();
+ if (chan) {
+ file->private_data = chan;
+ return zt_specchan_open(inode, file, chan->channo, 1);
+ } else {
+ return -ENXIO;
+ }
+ } else
+ return -ENXIO;
+ }
+ return zt_specchan_open(inode, file, unit, 1);
+}
+
+#if 0
+static int zt_open(struct inode *inode, struct file *file)
+{
+ int res;
+ unsigned long flags;
+ spin_lock_irqsave(&bigzaplock, flags);
+ res = __zt_open(inode, file);
+ spin_unlock_irqrestore(&bigzaplock, flags);
+ return res;
+}
+#endif
+
+static ssize_t zt_read(struct file *file, char *usrbuf, size_t count, loff_t *ppos)
+{
+ int unit = UNIT(file);
+ struct zt_chan *chan;
+
+ /* Can't read from control */
+ if (!unit) {
+ return -EINVAL;
+ }
+
+ if (unit == 253)
+ return -EINVAL;
+
+ if (unit == 254) {
+ chan = file->private_data;
+ if (!chan)
+ return -EINVAL;
+ return zt_chan_read(file, usrbuf, count, chan->channo);
+ }
+
+ if (unit == 255) {
+ chan = file->private_data;
+ if (!chan) {
+ printk("No pseudo channel structure to read?\n");
+ return -EINVAL;
+ }
+ return zt_chan_read(file, usrbuf, count, chan->channo);
+ }
+ if (count < 0)
+ return -EINVAL;
+
+ return zt_chan_read(file, usrbuf, count, unit);
+}
+
+static ssize_t zt_write(struct file *file, const char *usrbuf, size_t count, loff_t *ppos)
+{
+ int unit = UNIT(file);
+ struct zt_chan *chan;
+ /* Can't read from control */
+ if (!unit)
+ return -EINVAL;
+ if (count < 0)
+ return -EINVAL;
+ if (unit == 253)
+ return -EINVAL;
+ if (unit == 254) {
+ chan = file->private_data;
+ if (!chan)
+ return -EINVAL;
+ return zt_chan_write(file, usrbuf, count, chan->channo);
+ }
+ if (unit == 255) {
+ chan = file->private_data;
+ if (!chan) {
+ printk("No pseudo channel structure to read?\n");
+ return -EINVAL;
+ }
+ return zt_chan_write(file, usrbuf, count, chan->channo);
+ }
+ return zt_chan_write(file, usrbuf, count, unit);
+
+}
+
+/* No bigger than 32k for everything per tone zone */
+#define MAX_SIZE 32768
+/* No more than 128 subtones */
+#define MAX_TONES 128
+
+/* The tones to be loaded can (will) be a mix of regular tones,
+ DTMF tones and MF tones. We need to load DTMF and MF tones
+ a bit differently than regular tones because their storage
+ format is much simpler (an array structure field of the zone
+ structure, rather an array of pointers).
+*/
+static int ioctl_load_zone(unsigned long data)
+{
+ struct zt_tone *samples[MAX_TONES] = { NULL, };
+ short next[MAX_TONES] = { 0, };
+ struct zt_tone_def_header th;
+ struct zt_tone_def td;
+ struct zt_zone *z;
+ struct zt_tone *t;
+ void *slab, *ptr;
+ int x;
+ size_t space;
+ size_t size;
+ int res;
+
+ if (copy_from_user(&th, (struct zt_tone_def_header *) data, sizeof(th)))
+ return -EFAULT;
+
+ data += sizeof(th);
+
+ if ((th.count < 0) || (th.count > MAX_TONES)) {
+ printk("Too many tones included\n");
+ return -EINVAL;
+ }
+
+ space = size = sizeof(*z) + th.count * sizeof(*t);
+
+ if (size > MAX_SIZE)
+ return -E2BIG;
+
+ if (!(z = ptr = slab = kmalloc(size, GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(slab, 0, size);
+
+ ptr += sizeof(*z);
+ space -= sizeof(*z);
+
+ strncpy(z->name, th.name, sizeof(z->name) - 1);
+
+ for (x = 0; x < ZT_MAX_CADENCE; x++)
+ z->ringcadence[x] = th.ringcadence[x];
+
+ atomic_set(&z->refcount, 0);
+
+ for (x = 0; x < th.count; x++) {
+ enum {
+ REGULAR_TONE,
+ DTMF_TONE,
+ MF_TONE,
+ } tone_type;
+
+ if (space < sizeof(*t)) {
+ kfree(slab);
+ printk("Insufficient tone zone space\n");
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&td, (struct zt_tone_def *) data, sizeof(td))) {
+ kfree(slab);
+ return -EFAULT;
+ }
+
+ data += sizeof(td);
+
+ if ((td.tone >= 0) && (td.tone < ZT_TONE_MAX)) {
+ tone_type = REGULAR_TONE;
+
+ t = samples[x] = ptr;
+
+ space -= sizeof(*t);
+ ptr += sizeof(*t);
+
+ /* Remember which sample is next */
+ next[x] = td.next;
+
+ /* Make sure the "next" one is sane */
+ if ((next[x] >= th.count) || (next[x] < 0)) {
+ printk("Invalid 'next' pointer: %d\n", next[x]);
+ kfree(slab);
+ return -EINVAL;
+ }
+ } else if ((td.tone >= ZT_TONE_DTMF_BASE) &&
+ (td.tone <= ZT_TONE_DTMF_MAX)) {
+ tone_type = DTMF_TONE;
+
+ td.tone -= ZT_TONE_DTMF_BASE;
+ t = &z->dtmf[td.tone];
+ } else if ((td.tone >= ZT_TONE_MF_BASE) &&
+ (td.tone <= ZT_TONE_MF_MAX)) {
+ tone_type = MF_TONE;
+
+ td.tone -= ZT_TONE_MF_BASE;
+ t = &z->mf[td.tone];
+ } else {
+ printk("Invalid tone (%d) defined\n", td.tone);
+ kfree(slab);
+ return -EINVAL;
+ }
+
+ t->fac1 = td.fac1;
+ t->init_v2_1 = td.init_v2_1;
+ t->init_v3_1 = td.init_v3_1;
+ t->fac2 = td.fac2;
+ t->init_v2_2 = td.init_v2_2;
+ t->init_v3_2 = td.init_v3_2;
+ t->modulate = td.modulate;
+
+ switch (tone_type) {
+ case REGULAR_TONE:
+ t->tonesamples = td.samples;
+ if (!z->tones[td.tone])
+ z->tones[td.tone] = t;
+ break;
+ case DTMF_TONE:
+ t->tonesamples = global_dialparams.dtmf_tonelen;
+ t->next = &dtmf_silence;
+ z->dtmf_continuous[td.tone] = *t;
+ z->dtmf_continuous[td.tone].next = &z->dtmf_continuous[td.tone];
+ break;
+ case MF_TONE:
+ t->tonesamples = global_dialparams.mfv1_tonelen;
+ t->next = &mfv1_silence;
+ /* Special case for K/P tone */
+ if (td.tone == 10)
+ t->tonesamples *= 5 / 3;
+ z->mf_continuous[td.tone] = *t;
+ z->mf_continuous[td.tone].next = &z->mf_continuous[td.tone];
+ break;
+ }
+ }
+
+ for (x = 0; x < th.count; x++) {
+ if (samples[x])
+ samples[x]->next = samples[next[x]];
+ }
+
+ if ((res = zt_register_tone_zone(th.zone, z)))
+ kfree(slab);
+
+ return res;
+}
+
+void zt_init_tone_state(struct zt_tone_state *ts, struct zt_tone *zt)
+{
+ ts->v1_1 = 0;
+ ts->v2_1 = zt->init_v2_1;
+ ts->v3_1 = zt->init_v3_1;
+ ts->v1_2 = 0;
+ ts->v2_2 = zt->init_v2_2;
+ ts->v3_2 = zt->init_v3_2;
+ ts->modulate = zt->modulate;
+}
+
+struct zt_tone *zt_dtmf_tone(const struct zt_chan *chan, char digit)
+{
+ struct zt_tone *z;
+
+ switch (chan->digitmode) {
+ case DIGIT_MODE_DTMF:
+ z = &chan->curzone->dtmf[0];
+ break;
+ case DIGIT_MODE_MFV1:
+ z = &chan->curzone->mf[0];
+ break;
+ default:
+ z = NULL;
+ }
+
+ switch (digit) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return z + (digit - '0');
+ case '*':
+ return z + 10;
+ case '#':
+ return z + 11;
+ case 'A':
+ case 'B':
+ case 'C':
+ return z + (digit + 12 - 'A');
+ case 'D':
+ if (chan->digitmode == DIGIT_MODE_MFV1)
+ return NULL;
+ else
+ return z + (digit + 12 - 'A');
+ case 'W':
+ return &tone_pause;
+ }
+
+ return NULL;
+}
+
+static void __do_dtmf(struct zt_chan *chan)
+{
+ char c;
+
+ /* Called with chan->lock held */
+ while ((c = chan->txdialbuf[0])) {
+ memmove(chan->txdialbuf, chan->txdialbuf + 1, sizeof(chan->txdialbuf) - 1);
+ switch (c) {
+ case 'T':
+ chan->digitmode = DIGIT_MODE_DTMF;
+ chan->tonep = 0;
+ break;
+ case 'M':
+ chan->digitmode = DIGIT_MODE_MFV1;
+ chan->tonep = 0;
+ break;
+ case 'P':
+ chan->digitmode = DIGIT_MODE_PULSE;
+ chan->tonep = 0;
+ break;
+ default:
+ if ((c != 'W') && (chan->digitmode == DIGIT_MODE_PULSE)) {
+ if ((c >= '0') && (c <= '9') && (chan->txhooksig == ZT_TXSIG_OFFHOOK)) {
+ chan->pdialcount = (c == '0') ? 10 : c - '0';
+ zt_rbs_sethook(chan, ZT_TXSIG_ONHOOK, ZT_TXSTATE_PULSEBREAK,
+ chan->pulsebreaktime);
+ return;
+ }
+ } else {
+ chan->curtone = zt_dtmf_tone(chan, c);
+ chan->tonep = 0;
+ if (chan->curtone) {
+ zt_init_tone_state(&chan->ts, chan->curtone);
+ return;
+ }
+ }
+ }
+ }
+
+ /* Notify userspace process if there is nothing left */
+ chan->dialing = 0;
+ __qevent(chan, ZT_EVENT_DIALCOMPLETE);
+}
+
+static int zt_release(struct inode *inode, struct file *file)
+{
+ int unit = UNIT(file);
+ int res;
+ struct zt_chan *chan;
+
+ if (!unit)
+ return zt_ctl_release(inode, file);
+ if (unit == 253) {
+ return zt_timer_release(inode, file);
+ }
+ if (unit == 250) {
+ res = zt_transcode_fops->release(inode, file);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ if (zt_transcode_fops->owner)
+ __MOD_DEC_USE_COUNT (zt_transcode_fops->owner);
+#else
+ module_put(zt_transcode_fops->owner);
+#endif
+ return res;
+ }
+ if (unit == 254) {
+ chan = file->private_data;
+ if (!chan)
+ return zt_chan_release(inode, file);
+ else
+ return zt_specchan_release(inode, file, chan->channo);
+ }
+ if (unit == 255) {
+ chan = file->private_data;
+ if (chan) {
+ res = zt_specchan_release(inode, file, chan->channo);
+ zt_free_pseudo(chan);
+ } else {
+ printk("Pseudo release and no private data??\n");
+ res = 0;
+ }
+ return res;
+ }
+ return zt_specchan_release(inode, file, unit);
+}
+
+#if 0
+static int zt_release(struct inode *inode, struct file *file)
+{
+ /* Lock the big zap lock when handling a release */
+ unsigned long flags;
+ int res;
+ spin_lock_irqsave(&bigzaplock, flags);
+ res = __zt_release(inode, file);
+ spin_unlock_irqrestore(&bigzaplock, flags);
+ return res;
+}
+#endif
+
+
+void zt_alarm_channel(struct zt_chan *chan, int alarms)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->lock, flags);
+ if (chan->chan_alarms != alarms) {
+ chan->chan_alarms = alarms;
+ zt_qevent_nolock(chan, alarms ? ZT_EVENT_ALARM : ZT_EVENT_NOALARM);
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+void zt_alarm_notify(struct zt_span *span)
+{
+ int x;
+
+ span->alarms &= ~ZT_ALARM_LOOPBACK;
+ /* Determine maint status */
+ if (span->maintstat || span->mainttimer)
+ span->alarms |= ZT_ALARM_LOOPBACK;
+ /* DON'T CHANGE THIS AGAIN. THIS WAS DONE FOR A REASON.
+ The expression (a != b) does *NOT* do the same thing
+ as ((!a) != (!b)) */
+ /* if change in general state */
+ if ((!span->alarms) != (!span->lastalarms)) {
+ span->lastalarms = span->alarms;
+ for (x = 0; x < span->channels; x++)
+ zt_alarm_channel(&span->chans[x], span->alarms);
+ /* Switch to other master if current master in alarm */
+ for (x=1; x<maxspans; x++) {
+ if (spans[x] && !spans[x]->alarms && (spans[x]->flags & ZT_FLAG_RUNNING)) {
+ if(master != spans[x])
+ printk("Zaptel: Master changed to %s\n", spans[x]->name);
+ master = spans[x];
+ break;
+ }
+ }
+ }
+}
+
+#define VALID_SPAN(j) do { \
+ if ((j >= ZT_MAX_SPANS) || (j < 1)) \
+ return -EINVAL; \
+ if (!spans[j]) \
+ return -ENXIO; \
+} while(0)
+
+#define CHECK_VALID_SPAN(j) do { \
+ /* Start a given span */ \
+ if (get_user(j, (int *)data)) \
+ return -EFAULT; \
+ VALID_SPAN(j); \
+} while(0)
+
+#define VALID_CHANNEL(j) do { \
+ if ((j >= ZT_MAX_CHANNELS) || (j < 1)) \
+ return -EINVAL; \
+ if (!chans[j]) \
+ return -ENXIO; \
+} while(0)
+
+static int zt_timer_ioctl(struct inode *node, struct file *file, unsigned int cmd, unsigned long data, struct zt_timer *timer)
+{
+ int j;
+ unsigned long flags;
+ switch(cmd) {
+ case ZT_TIMERCONFIG:
+ get_user(j, (int *)data);
+ if (j < 0)
+ j = 0;
+ spin_lock_irqsave(&zaptimerlock, flags);
+ timer->ms = timer->pos = j;
+ spin_unlock_irqrestore(&zaptimerlock, flags);
+ break;
+ case ZT_TIMERACK:
+ get_user(j, (int *)data);
+ spin_lock_irqsave(&zaptimerlock, flags);
+ if ((j < 1) || (j > timer->tripped))
+ j = timer->tripped;
+ timer->tripped -= j;
+ spin_unlock_irqrestore(&zaptimerlock, flags);
+ break;
+ case ZT_GETEVENT: /* Get event on queue */
+ j = ZT_EVENT_NONE;
+ spin_lock_irqsave(&zaptimerlock, flags);
+ /* set up for no event */
+ if (timer->tripped)
+ j = ZT_EVENT_TIMER_EXPIRED;
+ if (timer->ping)
+ j = ZT_EVENT_TIMER_PING;
+ spin_unlock_irqrestore(&zaptimerlock, flags);
+ put_user(j,(int *)data);
+ break;
+ case ZT_TIMERPING:
+ spin_lock_irqsave(&zaptimerlock, flags);
+ timer->ping = 1;
+ wake_up_interruptible(&timer->sel);
+ spin_unlock_irqrestore(&zaptimerlock, flags);
+ break;
+ case ZT_TIMERPONG:
+ spin_lock_irqsave(&zaptimerlock, flags);
+ timer->ping = 0;
+ spin_unlock_irqrestore(&zaptimerlock, flags);
+ break;
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int zt_common_ioctl(struct inode *node, struct file *file, unsigned int cmd, unsigned long data, int unit)
+{
+ union {
+ struct zt_gains gain;
+ struct zt_spaninfo spaninfo;
+ struct zt_params param;
+ } stack;
+ struct zt_chan *chan;
+ unsigned long flags;
+ unsigned char *txgain, *rxgain;
+ struct zt_chan *mychan;
+ int i,j;
+ int return_master = 0;
+ size_t size_to_copy;
+
+ switch(cmd) {
+ /* get channel parameters */
+ case ZT_GET_PARAMS_V1:
+ case ZT_GET_PARAMS:
+ size_to_copy = (cmd == ZT_GET_PARAMS_V1) ? sizeof(struct zt_params_v1) :
+ sizeof(struct zt_params);
+ if (copy_from_user(&stack.param, (struct zt_params *) data, size_to_copy))
+ return -EFAULT;
+
+ /* check to see if the caller wants to receive our master channel number */
+ if (stack.param.channo & ZT_GET_PARAMS_RETURN_MASTER) {
+ return_master = 1;
+ stack.param.channo &= ~ZT_GET_PARAMS_RETURN_MASTER;
+ }
+
+ /* Pick the right channo's */
+ if (!stack.param.channo || unit) {
+ stack.param.channo = unit;
+ }
+ /* Check validity of channel */
+ VALID_CHANNEL(stack.param.channo);
+ chan = chans[stack.param.channo];
+
+ /* point to relevant structure */
+ stack.param.sigtype = chan->sig; /* get signalling type */
+ /* return non-zero if rx not in idle state */
+ if (chan->span) {
+ j = zt_q_sig(chan);
+ if (j >= 0) { /* if returned with success */
+ stack.param.rxisoffhook = ((chan->rxsig & (j >> 8)) != (j & 0xff));
+ } else {
+ stack.param.rxisoffhook = ((chan->rxhooksig != ZT_RXSIG_ONHOOK) &&
+ (chan->rxhooksig != ZT_RXSIG_INITIAL));
+ }
+ } else if ((chan->txstate == ZT_TXSTATE_KEWL) || (chan->txstate == ZT_TXSTATE_AFTERKEWL))
+ stack.param.rxisoffhook = 1;
+ else
+ stack.param.rxisoffhook = 0;
+ if (chan->span && chan->span->rbsbits && !(chan->sig & ZT_SIG_CLEAR)) {
+ stack.param.rxbits = chan->rxsig;
+ stack.param.txbits = chan->txsig;
+ stack.param.idlebits = chan->idlebits;
+ } else {
+ stack.param.rxbits = -1;
+ stack.param.txbits = -1;
+ stack.param.idlebits = 0;
+ }
+ if (chan->span && (chan->span->rbsbits || chan->span->hooksig) &&
+ !(chan->sig & ZT_SIG_CLEAR)) {
+ stack.param.rxhooksig = chan->rxhooksig;
+ stack.param.txhooksig = chan->txhooksig;
+ } else {
+ stack.param.rxhooksig = -1;
+ stack.param.txhooksig = -1;
+ }
+ stack.param.prewinktime = chan->prewinktime;
+ stack.param.preflashtime = chan->preflashtime;
+ stack.param.winktime = chan->winktime;
+ stack.param.flashtime = chan->flashtime;
+ stack.param.starttime = chan->starttime;
+ stack.param.rxwinktime = chan->rxwinktime;
+ stack.param.rxflashtime = chan->rxflashtime;
+ stack.param.debouncetime = chan->debouncetime;
+ stack.param.channo = chan->channo;
+ stack.param.chan_alarms = chan->chan_alarms;
+
+ /* if requested, put the master channel number in the top 16 bits of the result */
+ if (return_master)
+ stack.param.channo |= chan->master->channo << 16;
+
+ stack.param.pulsemaketime = chan->pulsemaketime;
+ stack.param.pulsebreaktime = chan->pulsebreaktime;
+ stack.param.pulseaftertime = chan->pulseaftertime;
+ if (chan->span) stack.param.spanno = chan->span->spanno;
+ else stack.param.spanno = 0;
+ strncpy(stack.param.name, chan->name, sizeof(stack.param.name) - 1);
+ stack.param.chanpos = chan->chanpos;
+ stack.param.sigcap = chan->sigcap;
+ /* Return current law */
+ if (chan->xlaw == __zt_alaw)
+ stack.param.curlaw = ZT_LAW_ALAW;
+ else
+ stack.param.curlaw = ZT_LAW_MULAW;
+
+ if (copy_to_user((struct zt_params *) data, &stack.param, size_to_copy))
+ return -EFAULT;
+
+ break;
+ /* set channel parameters */
+ case ZT_SET_PARAMS_V1:
+ case ZT_SET_PARAMS:
+ /* The difference between zt_params and zt_params_v1 is just the
+ * last field, which is read-only anyway. Thus we just read the
+ * size of the older struct.
+ */
+ if (copy_from_user(&stack.param, (struct zt_params *) data, sizeof(struct zt_params_v1)))
+ return -EFAULT;
+
+ stack.param.chan_alarms = 0; /* be explicit about the above */
+
+ /* Pick the right channo's */
+ if (!stack.param.channo || unit) {
+ stack.param.channo = unit;
+ }
+ /* Check validity of channel */
+ VALID_CHANNEL(stack.param.channo);
+ chan = chans[stack.param.channo];
+ /* point to relevant structure */
+ /* NOTE: sigtype is *not* included in this */
+ /* get timing stack.paramters */
+ chan->prewinktime = stack.param.prewinktime;
+ chan->preflashtime = stack.param.preflashtime;
+ chan->winktime = stack.param.winktime;
+ chan->flashtime = stack.param.flashtime;
+ chan->starttime = stack.param.starttime;
+ /* Update ringtime if not using a tone zone */
+ if (!chan->curzone)
+ chan->ringcadence[0] = chan->starttime;
+ chan->rxwinktime = stack.param.rxwinktime;
+ chan->rxflashtime = stack.param.rxflashtime;
+ chan->debouncetime = stack.param.debouncetime;
+ chan->pulsemaketime = stack.param.pulsemaketime;
+ chan->pulsebreaktime = stack.param.pulsebreaktime;
+ chan->pulseaftertime = stack.param.pulseaftertime;
+ break;
+ case ZT_GETGAINS: /* get gain stuff */
+ if (copy_from_user(&stack.gain,(struct zt_gains *) data,sizeof(stack.gain)))
+ return -EFAULT;
+ i = stack.gain.chan; /* get channel no */
+ /* if zero, use current channel no */
+ if (!i) i = unit;
+ /* make sure channel number makes sense */
+ if ((i < 0) || (i > ZT_MAX_CHANNELS) || !chans[i]) return(-EINVAL);
+
+ if (!(chans[i]->flags & ZT_FLAG_AUDIO)) return (-EINVAL);
+ stack.gain.chan = i; /* put the span # in here */
+ for (j=0;j<256;j++) {
+ stack.gain.txgain[j] = chans[i]->txgain[j];
+ stack.gain.rxgain[j] = chans[i]->rxgain[j];
+ }
+ if (copy_to_user((struct zt_gains *) data,&stack.gain,sizeof(stack.gain)))
+ return -EFAULT;
+ break;
+ case ZT_SETGAINS: /* set gain stuff */
+ if (copy_from_user(&stack.gain,(struct zt_gains *) data,sizeof(stack.gain)))
+ return -EFAULT;
+ i = stack.gain.chan; /* get channel no */
+ /* if zero, use current channel no */
+ if (!i) i = unit;
+ /* make sure channel number makes sense */
+ if ((i < 0) || (i > ZT_MAX_CHANNELS) || !chans[i]) return(-EINVAL);
+ if (!(chans[i]->flags & ZT_FLAG_AUDIO)) return (-EINVAL);
+
+ rxgain = kmalloc(512, GFP_KERNEL);
+ if (!rxgain)
+ return -ENOMEM;
+
+ stack.gain.chan = i; /* put the span # in here */
+ txgain = rxgain + 256;
+
+ for (j=0;j<256;j++) {
+ rxgain[j] = stack.gain.rxgain[j];
+ txgain[j] = stack.gain.txgain[j];
+ }
+
+ if (!memcmp(rxgain, defgain, 256) &&
+ !memcmp(txgain, defgain, 256)) {
+ if (rxgain)
+ kfree(rxgain);
+ spin_lock_irqsave(&chans[i]->lock, flags);
+ if (chans[i]->gainalloc)
+ kfree(chans[i]->rxgain);
+ chans[i]->gainalloc = 0;
+ chans[i]->rxgain = defgain;
+ chans[i]->txgain = defgain;
+ spin_unlock_irqrestore(&chans[i]->lock, flags);
+ } else {
+ /* This is a custom gain setting */
+ spin_lock_irqsave(&chans[i]->lock, flags);
+ if (chans[i]->gainalloc)
+ kfree(chans[i]->rxgain);
+ chans[i]->gainalloc = 1;
+ chans[i]->rxgain = rxgain;
+ chans[i]->txgain = txgain;
+ spin_unlock_irqrestore(&chans[i]->lock, flags);
+ }
+ if (copy_to_user((struct zt_gains *) data,&stack.gain,sizeof(stack.gain)))
+ return -EFAULT;
+ break;
+ case ZT_SPANSTAT_V1:
+ case ZT_SPANSTAT_V2:
+ case ZT_SPANSTAT:
+ size_to_copy = (cmd == ZT_SPANSTAT_V1) ? sizeof(struct zt_spaninfo_v1) :
+ (cmd == ZT_SPANSTAT_V2) ? sizeof(struct zt_spaninfo_v2) :
+ sizeof(struct zt_spaninfo);
+ if (copy_from_user(&stack.spaninfo, (struct zt_spaninfo *) data, size_to_copy))
+ return -EFAULT;
+ i = stack.spaninfo.spanno; /* get specified span number */
+ if ((i < 0) || (i >= maxspans)) return(-EINVAL); /* if bad span no */
+ if (i == 0) {
+ /* if to figure it out for this chan */
+ if (!chans[unit])
+ return -EINVAL;
+ i = chans[unit]->span->spanno;
+ }
+ if (!spans[i])
+ return -EINVAL;
+ stack.spaninfo.spanno = i; /* put the span # in here */
+ stack.spaninfo.totalspans = 0;
+ if (maxspans) stack.spaninfo.totalspans = maxspans - 1; /* put total number of spans here */
+ strncpy(stack.spaninfo.desc, spans[i]->desc, sizeof(stack.spaninfo.desc) - 1);
+ strncpy(stack.spaninfo.name, spans[i]->name, sizeof(stack.spaninfo.name) - 1);
+ stack.spaninfo.alarms = spans[i]->alarms; /* get alarm status */
+ stack.spaninfo.bpvcount = spans[i]->bpvcount; /* get BPV count */
+ stack.spaninfo.rxlevel = spans[i]->rxlevel; /* get rx level */
+ stack.spaninfo.txlevel = spans[i]->txlevel; /* get tx level */
+ stack.spaninfo.crc4count = spans[i]->crc4count; /* get CRC4 error count */
+ stack.spaninfo.ebitcount = spans[i]->ebitcount; /* get E-bit error count */
+ stack.spaninfo.fascount = spans[i]->fascount; /* get FAS error count */
+ stack.spaninfo.irqmisses = spans[i]->irqmisses; /* get IRQ miss count */
+ stack.spaninfo.syncsrc = spans[i]->syncsrc; /* get active sync source */
+ stack.spaninfo.totalchans = spans[i]->channels;
+ stack.spaninfo.numchans = 0;
+ for (j = 0; j < spans[i]->channels; j++) {
+ if (spans[i]->chans[j].sig)
+ stack.spaninfo.numchans++;
+ }
+ /* version 2 fields */
+ stack.spaninfo.lbo = spans[i]->lbo;
+ stack.spaninfo.lineconfig = spans[i]->lineconfig;
+ /* version 3 fields */
+ stack.spaninfo.irq = spans[i]->irq;
+ stack.spaninfo.linecompat = spans[i]->linecompat;
+ strncpy(stack.spaninfo.lboname, zt_lboname(spans[i]->lbo), sizeof(stack.spaninfo.lboname) - 1);
+ if (spans[i]->manufacturer)
+ strncpy(stack.spaninfo.manufacturer, spans[i]->manufacturer,
+ sizeof(stack.spaninfo.manufacturer) - 1);
+ if (spans[i]->devicetype)
+ strncpy(stack.spaninfo.devicetype, spans[i]->devicetype, sizeof(stack.spaninfo.devicetype) - 1);
+ strncpy(stack.spaninfo.location, spans[i]->location, sizeof(stack.spaninfo.location) - 1);
+ if (spans[i]->spantype)
+ strncpy(stack.spaninfo.spantype, spans[i]->spantype, sizeof(stack.spaninfo.spantype) - 1);
+
+ if (copy_to_user((struct zt_spaninfo *) data, &stack.spaninfo, size_to_copy))
+ return -EFAULT;
+ break;
+ case ZT_CHANDIAG:
+ get_user(j, (int *)data); /* get channel number from user */
+ /* make sure its a valid channel number */
+ if ((j < 1) || (j >= maxchans))
+ return -EINVAL;
+ /* if channel not mapped, not there */
+ if (!chans[j])
+ return -EINVAL;
+
+ if (!(mychan = kmalloc(sizeof(*mychan), GFP_KERNEL)))
+ return -ENOMEM;
+
+ /* lock channel */
+ spin_lock_irqsave(&chans[j]->lock, flags);
+ /* make static copy of channel */
+ memcpy(mychan, chans[j], sizeof(*mychan));
+ /* release it. */
+ spin_unlock_irqrestore(&chans[j]->lock, flags);
+
+ printk(KERN_INFO "Dump of Zaptel Channel %d (%s,%d,%d):\n\n",j,
+ mychan->name,mychan->channo,mychan->chanpos);
+ printk(KERN_INFO "flags: %x hex, writechunk: %08lx, readchunk: %08lx\n",
+ mychan->flags, (long) mychan->writechunk, (long) mychan->readchunk);
+ printk(KERN_INFO "rxgain: %08lx, txgain: %08lx, gainalloc: %d\n",
+ (long) mychan->rxgain, (long)mychan->txgain, mychan->gainalloc);
+ printk(KERN_INFO "span: %08lx, sig: %x hex, sigcap: %x hex\n",
+ (long)mychan->span, mychan->sig, mychan->sigcap);
+ printk(KERN_INFO "inreadbuf: %d, outreadbuf: %d, inwritebuf: %d, outwritebuf: %d\n",
+ mychan->inreadbuf, mychan->outreadbuf, mychan->inwritebuf, mychan->outwritebuf);
+ printk(KERN_INFO "blocksize: %d, numbufs: %d, txbufpolicy: %d, txbufpolicy: %d\n",
+ mychan->blocksize, mychan->numbufs, mychan->txbufpolicy, mychan->rxbufpolicy);
+ printk(KERN_INFO "txdisable: %d, rxdisable: %d, iomask: %d\n",
+ mychan->txdisable, mychan->rxdisable, mychan->iomask);
+ printk(KERN_INFO "curzone: %08lx, tonezone: %d, curtone: %08lx, tonep: %d\n",
+ (long) mychan->curzone, mychan->tonezone, (long) mychan->curtone, mychan->tonep);
+ printk(KERN_INFO "digitmode: %d, txdialbuf: %s, dialing: %d, aftdialtimer: %d, cadpos. %d\n",
+ mychan->digitmode, mychan->txdialbuf, mychan->dialing,
+ mychan->afterdialingtimer, mychan->cadencepos);
+ printk(KERN_INFO "confna: %d, confn: %d, confmode: %d, confmute: %d\n",
+ mychan->confna, mychan->_confn, mychan->confmode, mychan->confmute);
+ printk(KERN_INFO "ec: %08lx, echocancel: %d, deflaw: %d, xlaw: %08lx\n",
+ (long) mychan->ec, mychan->echocancel, mychan->deflaw, (long) mychan->xlaw);
+ printk(KERN_INFO "echostate: %02x, echotimer: %d, echolastupdate: %d\n",
+ (int) mychan->echostate, mychan->echotimer, mychan->echolastupdate);
+ printk(KERN_INFO "itimer: %d, otimer: %d, ringdebtimer: %d\n\n",
+ mychan->itimer, mychan->otimer, mychan->ringdebtimer);
+#if 0
+ if (mychan->ec) {
+ int x;
+ /* Dump the echo canceller parameters */
+ for (x=0;x<mychan->ec->taps;x++) {
+ printk(KERN_INFO "tap %d: %d\n", x, mychan->ec->fir_taps[x]);
+ }
+ }
+#endif
+ kfree(mychan);
+ break;
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int (*zt_dynamic_ioctl)(unsigned int cmd, unsigned long data);
+
+void zt_set_dynamic_ioctl(int (*func)(unsigned int cmd, unsigned long data))
+{
+ zt_dynamic_ioctl = func;
+}
+
+static void recalc_slaves(struct zt_chan *chan)
+{
+ int x;
+ struct zt_chan *last = chan;
+
+ /* Makes no sense if you don't have a span */
+ if (!chan->span)
+ return;
+
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("Recalculating slaves on %s\n", chan->name);
+#endif
+
+ /* Link all slaves appropriately */
+ for (x=chan->chanpos;x<chan->span->channels;x++)
+ if (chan->span->chans[x].master == chan) {
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("Channel %s, slave to %s, last is %s, its next will be %d\n",
+ chan->span->chans[x].name, chan->name, last->name, x);
+#endif
+ last->nextslave = x;
+ last = &chan->span->chans[x];
+ }
+ /* Terminate list */
+ last->nextslave = 0;
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("Done Recalculating slaves on %s (last is %s)\n", chan->name, last->name);
+#endif
+}
+
+static int zt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data)
+{
+ /* I/O CTL's for control interface */
+ int i,j;
+ struct zt_lineconfig lc;
+ struct zt_chanconfig ch;
+ struct zt_sfconfig sf;
+ int sigcap;
+ int res = 0;
+ int x,y;
+ struct zt_chan *newmaster;
+ struct zt_dialparams tdp;
+ struct zt_maintinfo maint;
+ struct zt_indirect_data ind;
+ struct zt_versioninfo vi;
+ unsigned long flags;
+ int rv;
+ switch(cmd) {
+ case ZT_INDIRECT:
+ if (copy_from_user(&ind, (struct zt_indirect_data *)data, sizeof(ind)))
+ return -EFAULT;
+ VALID_CHANNEL(ind.chan);
+ return zt_chan_ioctl(inode, file, ind.op, (unsigned long) ind.data, ind.chan);
+ case ZT_SPANCONFIG:
+ if (copy_from_user(&lc, (struct zt_lineconfig *)data, sizeof(lc)))
+ return -EFAULT;
+ VALID_SPAN(lc.span);
+ if ((lc.lineconfig & 0x07f0 & spans[lc.span]->linecompat) != (lc.lineconfig & 0x07f0))
+ return -EINVAL;
+ if (spans[lc.span]->spanconfig) {
+ spans[lc.span]->lineconfig = lc.lineconfig;
+ spans[lc.span]->lbo = lc.lbo;
+ spans[lc.span]->txlevel = lc.lbo;
+ spans[lc.span]->rxlevel = 0;
+
+ return spans[lc.span]->spanconfig(spans[lc.span], &lc);
+ }
+ return 0;
+ case ZT_STARTUP:
+ CHECK_VALID_SPAN(j);
+ if (spans[j]->flags & ZT_FLAG_RUNNING)
+ return 0;
+ if (spans[j]->startup)
+ res = spans[j]->startup(spans[j]);
+ if (!res) {
+ /* Mark as running and hangup any channels */
+ spans[j]->flags |= ZT_FLAG_RUNNING;
+ for (x=0;x<spans[j]->channels;x++) {
+ y = zt_q_sig(&spans[j]->chans[x]) & 0xff;
+ if (y >= 0) spans[j]->chans[x].rxsig = (unsigned char)y;
+ spin_lock_irqsave(&spans[j]->chans[x].lock, flags);
+ zt_hangup(&spans[j]->chans[x]);
+ spin_unlock_irqrestore(&spans[j]->chans[x].lock, flags);
+ spans[j]->chans[x].rxhooksig = ZT_RXSIG_INITIAL;
+ }
+ }
+ return 0;
+ case ZT_SHUTDOWN:
+ CHECK_VALID_SPAN(j);
+ if (spans[j]->shutdown)
+ res = spans[j]->shutdown(spans[j]);
+ spans[j]->flags &= ~ZT_FLAG_RUNNING;
+ return 0;
+ case ZT_CHANCONFIG:
+ if (copy_from_user(&ch, (struct zt_chanconfig *)data, sizeof(ch)))
+ return -EFAULT;
+ VALID_CHANNEL(ch.chan);
+ if (ch.sigtype == ZT_SIG_SLAVE) {
+ /* We have to use the master's sigtype */
+ if ((ch.master < 1) || (ch.master >= ZT_MAX_CHANNELS))
+ return -EINVAL;
+ if (!chans[ch.master])
+ return -EINVAL;
+ ch.sigtype = chans[ch.master]->sig;
+ newmaster = chans[ch.master];
+ } else if ((ch.sigtype & __ZT_SIG_DACS) == __ZT_SIG_DACS) {
+ newmaster = chans[ch.chan];
+ if ((ch.idlebits < 1) || (ch.idlebits >= ZT_MAX_CHANNELS))
+ return -EINVAL;
+ if (!chans[ch.idlebits])
+ return -EINVAL;
+ } else {
+ newmaster = chans[ch.chan];
+ }
+ spin_lock_irqsave(&chans[ch.chan]->lock, flags);
+#ifdef CONFIG_ZAPATA_NET
+ if (chans[ch.chan]->flags & ZT_FLAG_NETDEV) {
+ if (ztchan_to_dev(chans[ch.chan])->flags & IFF_UP) {
+ spin_unlock_irqrestore(&chans[ch.chan]->lock, flags);
+ printk(KERN_WARNING "Can't switch HDLC net mode on channel %s, since current interface is up\n", chans[ch.chan]->name);
+ return -EBUSY;
+ }
+#ifdef LINUX26
+ spin_unlock_irqrestore(&chans[ch.chan]->lock, flags);
+ unregister_hdlc_device(chans[ch.chan]->hdlcnetdev->netdev);
+ spin_lock_irqsave(&chans[ch.chan]->lock, flags);
+ free_netdev(chans[ch.chan]->hdlcnetdev->netdev);
+#else
+ unregister_hdlc_device(&chans[ch.chan]->hdlcnetdev->netdev);
+#endif
+ kfree(chans[ch.chan]->hdlcnetdev);
+ chans[ch.chan]->hdlcnetdev = NULL;
+ chans[ch.chan]->flags &= ~ZT_FLAG_NETDEV;
+ }
+#else
+ if (ch.sigtype == ZT_SIG_HDLCNET) {
+ spin_unlock_irqrestore(&chans[ch.chan]->lock, flags);
+ printk(KERN_WARNING "Zaptel networking not supported by this build.\n");
+ return -ENOSYS;
+ }
+#endif
+ sigcap = chans[ch.chan]->sigcap;
+ /* If they support clear channel, then they support the HDLC and such through
+ us. */
+ if (sigcap & ZT_SIG_CLEAR)
+ sigcap |= (ZT_SIG_HDLCRAW | ZT_SIG_HDLCFCS | ZT_SIG_HDLCNET | ZT_SIG_DACS);
+
+ if ((sigcap & ch.sigtype) != ch.sigtype)
+ res = -EINVAL;
+
+ if (!res && chans[ch.chan]->span->chanconfig)
+ res = chans[ch.chan]->span->chanconfig(chans[ch.chan], ch.sigtype);
+ if (chans[ch.chan]->master) {
+ /* Clear the master channel */
+ recalc_slaves(chans[ch.chan]->master);
+ chans[ch.chan]->nextslave = 0;
+ }
+ if (!res) {
+ chans[ch.chan]->sig = ch.sigtype;
+ if (chans[ch.chan]->sig == ZT_SIG_CAS)
+ chans[ch.chan]->idlebits = ch.idlebits;
+ else
+ chans[ch.chan]->idlebits = 0;
+ if ((ch.sigtype & ZT_SIG_CLEAR) == ZT_SIG_CLEAR) {
+ /* Set clear channel flag if appropriate */
+ chans[ch.chan]->flags &= ~ZT_FLAG_AUDIO;
+ chans[ch.chan]->flags |= ZT_FLAG_CLEAR;
+ } else {
+ /* Set audio flag and not clear channel otherwise */
+ chans[ch.chan]->flags |= ZT_FLAG_AUDIO;
+ chans[ch.chan]->flags &= ~ZT_FLAG_CLEAR;
+ }
+ if ((ch.sigtype & ZT_SIG_HDLCRAW) == ZT_SIG_HDLCRAW) {
+ /* Set the HDLC flag */
+ chans[ch.chan]->flags |= ZT_FLAG_HDLC;
+ } else {
+ /* Clear the HDLC flag */
+ chans[ch.chan]->flags &= ~ZT_FLAG_HDLC;
+ }
+ if ((ch.sigtype & ZT_SIG_HDLCFCS) == ZT_SIG_HDLCFCS) {
+ /* Set FCS to be calculated if appropriate */
+ chans[ch.chan]->flags |= ZT_FLAG_FCS;
+ } else {
+ /* Clear FCS flag */
+ chans[ch.chan]->flags &= ~ZT_FLAG_FCS;
+ }
+ if ((ch.sigtype & __ZT_SIG_DACS) == __ZT_SIG_DACS) {
+ /* Setup conference properly */
+ chans[ch.chan]->confmode = ZT_CONF_DIGITALMON;
+ chans[ch.chan]->confna = ch.idlebits;
+ if (chans[ch.chan]->span &&
+ chans[ch.chan]->span->dacs &&
+ chans[ch.idlebits] &&
+ chans[ch.chan]->span &&
+ (chans[ch.chan]->span->dacs == chans[ch.idlebits]->span->dacs))
+ chans[ch.chan]->span->dacs(chans[ch.chan], chans[ch.idlebits]);
+ } else if (chans[ch.chan]->span && chans[ch.chan]->span->dacs)
+ chans[ch.chan]->span->dacs(chans[ch.chan], NULL);
+ chans[ch.chan]->master = newmaster;
+ /* Note new slave if we are not our own master */
+ if (newmaster != chans[ch.chan]) {
+ recalc_slaves(chans[ch.chan]->master);
+ }
+ if ((ch.sigtype & ZT_SIG_HARDHDLC) == ZT_SIG_HARDHDLC) {
+ chans[ch.chan]->flags &= ~ZT_FLAG_FCS;
+ chans[ch.chan]->flags &= ~ZT_FLAG_HDLC;
+ chans[ch.chan]->flags |= ZT_FLAG_NOSTDTXRX;
+ } else
+ chans[ch.chan]->flags &= ~ZT_FLAG_NOSTDTXRX;
+ }
+#ifdef CONFIG_ZAPATA_NET
+ if (!res &&
+ (newmaster == chans[ch.chan]) &&
+ (chans[ch.chan]->sig == ZT_SIG_HDLCNET)) {
+ chans[ch.chan]->hdlcnetdev = zt_hdlc_alloc();
+ if (chans[ch.chan]->hdlcnetdev) {
+/* struct hdlc_device *hdlc = chans[ch.chan]->hdlcnetdev;
+ struct net_device *d = hdlc_to_dev(hdlc); mmm...get it right later --byg */
+#ifdef LINUX26
+ chans[ch.chan]->hdlcnetdev->netdev = alloc_hdlcdev(chans[ch.chan]->hdlcnetdev);
+ if (chans[ch.chan]->hdlcnetdev->netdev) {
+ chans[ch.chan]->hdlcnetdev->chan = chans[ch.chan];
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
+ SET_MODULE_OWNER(chans[ch.chan]->hdlcnetdev->netdev);
+#endif
+ chans[ch.chan]->hdlcnetdev->netdev->irq = chans[ch.chan]->span->irq;
+ chans[ch.chan]->hdlcnetdev->netdev->tx_queue_len = 50;
+ chans[ch.chan]->hdlcnetdev->netdev->do_ioctl = zt_net_ioctl;
+ chans[ch.chan]->hdlcnetdev->netdev->open = zt_net_open;
+ chans[ch.chan]->hdlcnetdev->netdev->stop = zt_net_stop;
+ dev_to_hdlc(chans[ch.chan]->hdlcnetdev->netdev)->attach = zt_net_attach;
+ dev_to_hdlc(chans[ch.chan]->hdlcnetdev->netdev)->xmit = zt_xmit;
+ spin_unlock_irqrestore(&chans[ch.chan]->lock, flags);
+ /* Briefly restore interrupts while we register the device */
+ res = zt_register_hdlc_device(chans[ch.chan]->hdlcnetdev->netdev, ch.netdev_name);
+ spin_lock_irqsave(&chans[ch.chan]->lock, flags);
+ } else {
+ printk("Unable to allocate hdlc: *shrug*\n");
+ res = -1;
+ }
+#else /* LINUX26 */
+ chans[ch.chan]->hdlcnetdev->chan = chans[ch.chan];
+#ifndef HDLC_MAINTAINERS_ARE_MORE_STUPID_THAN_I_THOUGHT
+ chans[ch.chan]->hdlcnetdev->netdev.ioctl = zt_net_ioctl;
+#endif
+ chans[ch.chan]->hdlcnetdev->netdev.netdev.do_ioctl = zt_net_ioctl;
+#ifdef NEW_HDLC_INTERFACE
+ chans[ch.chan]->hdlcnetdev->netdev.netdev.open = zt_net_open;
+ chans[ch.chan]->hdlcnetdev->netdev.netdev.stop = zt_net_stop;
+ chans[ch.chan]->hdlcnetdev->netdev.xmit = zt_xmit;
+ chans[ch.chan]->hdlcnetdev->netdev.attach = zt_net_attach;
+#else
+ chans[ch.chan]->hdlcnetdev->netdev.open = zt_net_open;
+ chans[ch.chan]->hdlcnetdev->netdev.close = zt_net_close;
+ chans[ch.chan]->hdlcnetdev->netdev.set_mode = NULL;
+ chans[ch.chan]->hdlcnetdev->netdev.xmit = zt_xmit;
+#endif /* NEW_HDLC_INTERFACE */
+ chans[ch.chan]->hdlcnetdev->netdev.netdev.irq = chans[ch.chan]->span->irq;
+ chans[ch.chan]->hdlcnetdev->netdev.netdev.tx_queue_len = 50;
+ res = register_hdlc_device(&chans[ch.chan]->hdlcnetdev->netdev);
+#endif /* LINUX26 */
+ if (!res)
+ chans[ch.chan]->flags |= ZT_FLAG_NETDEV;
+ } else {
+ printk("Unable to allocate netdev: out of memory\n");
+ res = -1;
+ }
+ }
+#endif
+ if ((chans[ch.chan]->sig == ZT_SIG_HDLCNET) &&
+ (chans[ch.chan] == newmaster) &&
+ !(chans[ch.chan]->flags & ZT_FLAG_NETDEV))
+ printk("Unable to register HDLC device for channel %s\n", chans[ch.chan]->name);
+ if (!res) {
+ /* Setup default law */
+ chans[ch.chan]->deflaw = ch.deflaw;
+ /* Copy back any modified settings */
+ spin_unlock_irqrestore(&chans[ch.chan]->lock, flags);
+ if (copy_to_user((struct zt_chanconfig *)data, &ch, sizeof(ch)))
+ return -EFAULT;
+ spin_lock_irqsave(&chans[ch.chan]->lock, flags);
+ /* And hangup */
+ zt_hangup(chans[ch.chan]);
+ y = zt_q_sig(chans[ch.chan]) & 0xff;
+ if (y >= 0) chans[ch.chan]->rxsig = (unsigned char)y;
+ chans[ch.chan]->rxhooksig = ZT_RXSIG_INITIAL;
+ }
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("Configured channel %s, flags %04x, sig %04x\n", chans[ch.chan]->name, chans[ch.chan]->flags, chans[ch.chan]->sig);
+#endif
+ spin_unlock_irqrestore(&chans[ch.chan]->lock, flags);
+ return res;
+ case ZT_SFCONFIG:
+ if (copy_from_user(&sf, (struct zt_chanconfig *)data, sizeof(sf)))
+ return -EFAULT;
+ VALID_CHANNEL(sf.chan);
+ if (chans[sf.chan]->sig != ZT_SIG_SF) return -EINVAL;
+ spin_lock_irqsave(&chans[sf.chan]->lock, flags);
+ chans[sf.chan]->rxp1 = sf.rxp1;
+ chans[sf.chan]->rxp2 = sf.rxp2;
+ chans[sf.chan]->rxp3 = sf.rxp3;
+ chans[sf.chan]->txtone = sf.txtone;
+ chans[sf.chan]->tx_v2 = sf.tx_v2;
+ chans[sf.chan]->tx_v3 = sf.tx_v3;
+ chans[sf.chan]->toneflags = sf.toneflag;
+ if (sf.txtone) /* if set to make tone for tx */
+ {
+ if ((chans[sf.chan]->txhooksig && !(sf.toneflag & ZT_REVERSE_TXTONE)) ||
+ ((!chans[sf.chan]->txhooksig) && (sf.toneflag & ZT_REVERSE_TXTONE)))
+ {
+ set_txtone(chans[sf.chan],sf.txtone,sf.tx_v2,sf.tx_v3);
+ }
+ else
+ {
+ set_txtone(chans[sf.chan],0,0,0);
+ }
+ }
+ spin_unlock_irqrestore(&chans[sf.chan]->lock, flags);
+ return res;
+ case ZT_DEFAULTZONE:
+ if (get_user(j,(int *)data))
+ return -EFAULT;
+ if ((j < 0) || (j >= ZT_TONE_ZONE_MAX))
+ return -EINVAL;
+ write_lock(&zone_lock);
+ if (!tone_zones[j]) {
+ write_unlock(&zone_lock);
+ return -EINVAL;
+ }
+ if ((default_zone != -1) && tone_zones[default_zone])
+ atomic_dec(&tone_zones[default_zone]->refcount);
+ atomic_inc(&tone_zones[j]->refcount);
+ default_zone = j;
+ write_unlock(&zone_lock);
+ break;
+ case ZT_LOADZONE:
+ return ioctl_load_zone(data);
+ case ZT_FREEZONE:
+ get_user(j, (int *) data);
+ return free_tone_zone(j);
+ case ZT_SET_DIALPARAMS:
+ if (copy_from_user(&tdp, (struct zt_dialparams *) data, sizeof(tdp)))
+ return -EFAULT;
+ if ((tdp.dtmf_tonelen > 4000) || (tdp.dtmf_tonelen < 10))
+ return -EINVAL;
+ if ((tdp.mfv1_tonelen > 4000) || (tdp.mfv1_tonelen < 10))
+ return -EINVAL;
+
+ global_dialparams = tdp;
+
+ /* update the lengths in all currently loaded zones */
+ write_lock(&zone_lock);
+ for (j = 0; j < sizeof(tone_zones) / sizeof(tone_zones[0]); j++) {
+ struct zt_zone *z = tone_zones[j];
+
+ if (!z)
+ continue;
+
+ for (i = 0; i < sizeof(z->dtmf) / sizeof(z->dtmf[0]); i++)
+ z->dtmf[i].tonesamples = tdp.dtmf_tonelen * ZT_CHUNKSIZE;
+
+ for (i = 0; i < sizeof(z->mf) / sizeof(z->mf[0]); i++)
+ z->mf[i].tonesamples = tdp.mfv1_tonelen * ZT_CHUNKSIZE;
+
+ /* Special case for K/P tone */
+ z->mf[10].tonesamples *= 5 / 3;
+ }
+ write_unlock(&zone_lock);
+
+ dtmf_silence.tonesamples = tdp.dtmf_tonelen * ZT_CHUNKSIZE;
+ mfv1_silence.tonesamples = tdp.mfv1_tonelen * ZT_CHUNKSIZE;
+
+ break;
+ case ZT_GET_DIALPARAMS:
+ tdp = global_dialparams;
+ if (copy_to_user((struct zt_dialparams *) data, &tdp, sizeof(tdp)))
+ return -EFAULT;
+ break;
+ case ZT_GETVERSION:
+ memset(&vi, 0, sizeof(vi));
+ strncpy(vi.version, ZAPTEL_VERSION, sizeof(vi.version) - 1);
+ echo_can_identify(vi.echo_canceller, sizeof(vi.echo_canceller) - 1);
+ if (copy_to_user((struct zt_versioninfo *) data, &vi, sizeof(vi)))
+ return -EFAULT;
+ break;
+ case ZT_MAINT: /* do maintenance stuff */
+ /* get struct from user */
+ if (copy_from_user(&maint,(struct zt_maintinfo *) data, sizeof(maint)))
+ return -EFAULT;
+ /* must be valid span number */
+ if ((maint.spanno < 1) || (maint.spanno > ZT_MAX_SPANS) || (!spans[maint.spanno]))
+ return -EINVAL;
+ if (!spans[maint.spanno]->maint)
+ return -ENOSYS;
+ spin_lock_irqsave(&spans[maint.spanno]->lock, flags);
+ /* save current maint state */
+ i = spans[maint.spanno]->maintstat;
+ /* set maint mode */
+ spans[maint.spanno]->maintstat = maint.command;
+ switch(maint.command) {
+ case ZT_MAINT_NONE:
+ case ZT_MAINT_LOCALLOOP:
+ case ZT_MAINT_REMOTELOOP:
+ /* if same, ignore it */
+ if (i == maint.command) break;
+ rv = spans[maint.spanno]->maint(spans[maint.spanno], maint.command);
+ spin_unlock_irqrestore(&spans[maint.spanno]->lock, flags);
+ if (rv) return rv;
+ spin_lock_irqsave(&spans[maint.spanno]->lock, flags);
+ break;
+ case ZT_MAINT_LOOPUP:
+ case ZT_MAINT_LOOPDOWN:
+ spans[maint.spanno]->mainttimer = ZT_LOOPCODE_TIME * ZT_CHUNKSIZE;
+ rv = spans[maint.spanno]->maint(spans[maint.spanno], maint.command);
+ spin_unlock_irqrestore(&spans[maint.spanno]->lock, flags);
+ if (rv) return rv;
+ rv = schluffen(&spans[maint.spanno]->maintq);
+ if (rv) return rv;
+ spin_lock_irqsave(&spans[maint.spanno]->lock, flags);
+ break;
+ default:
+ printk("zaptel: Unknown maintenance event: %d\n", maint.command);
+ }
+ zt_alarm_notify(spans[maint.spanno]); /* process alarm-related events */
+ spin_unlock_irqrestore(&spans[maint.spanno]->lock, flags);
+ break;
+ case ZT_DYNAMIC_CREATE:
+ case ZT_DYNAMIC_DESTROY:
+ if (zt_dynamic_ioctl)
+ return zt_dynamic_ioctl(cmd, data);
+ else {
+ request_module("ztdynamic");
+ if (zt_dynamic_ioctl)
+ return zt_dynamic_ioctl(cmd, data);
+ }
+ return -ENOSYS;
+#if defined(ECHO_CAN_HPEC)
+ case ZT_EC_LICENSE_CHALLENGE:
+ case ZT_EC_LICENSE_RESPONSE:
+ return hpec_license_ioctl(cmd, data);
+#endif /* defined(ECHO_CAN_HPEC) */
+ default:
+ return zt_common_ioctl(inode, file, cmd, data, 0);
+ }
+ return 0;
+}
+
+static int zt_chanandpseudo_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit)
+{
+ struct zt_chan *chan = chans[unit];
+ union {
+ struct zt_dialoperation tdo;
+ struct zt_bufferinfo bi;
+ struct zt_confinfo conf;
+ struct zt_ring_cadence cad;
+ } stack;
+ unsigned long flags, flagso;
+ int i, j, k, rv;
+ int ret, c;
+ char *s;
+
+ if (!chan)
+ return -EINVAL;
+ switch(cmd) {
+ case ZT_DIALING:
+ spin_lock_irqsave(&chan->lock, flags);
+ j = chan->dialing;
+ spin_unlock_irqrestore(&chan->lock, flags);
+ if (copy_to_user((int *)data,&j,sizeof(int)))
+ return -EFAULT;
+ return 0;
+ case ZT_DIAL:
+ if (copy_from_user(&stack.tdo, (struct zt_dialoperation *)data, sizeof(stack.tdo)))
+ return -EFAULT;
+ rv = 0;
+ /* Force proper NULL termination and uppercase entry */
+ stack.tdo.dialstr[ZT_MAX_DTMF_BUF - 1] = '\0';
+ for (s = stack.tdo.dialstr; *s; s++)
+ *s = toupper(*s);
+ spin_lock_irqsave(&chan->lock, flags);
+ switch (stack.tdo.op) {
+ case ZT_DIAL_OP_CANCEL:
+ chan->curtone = NULL;
+ chan->dialing = 0;
+ chan->txdialbuf[0] = '\0';
+ chan->tonep = 0;
+ chan->pdialcount = 0;
+ break;
+ case ZT_DIAL_OP_REPLACE:
+ strcpy(chan->txdialbuf, stack.tdo.dialstr);
+ chan->dialing = 1;
+ __do_dtmf(chan);
+ break;
+ case ZT_DIAL_OP_APPEND:
+ if (strlen(stack.tdo.dialstr) + strlen(chan->txdialbuf) >= (ZT_MAX_DTMF_BUF - 1)) {
+ rv = -EBUSY;
+ break;
+ }
+ strncpy(chan->txdialbuf + strlen(chan->txdialbuf), stack.tdo.dialstr, ZT_MAX_DTMF_BUF - strlen(chan->txdialbuf) - 1);
+ if (!chan->dialing) {
+ chan->dialing = 1;
+ __do_dtmf(chan);
+ }
+ break;
+ default:
+ rv = -EINVAL;
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return rv;
+ case ZT_GET_BUFINFO:
+ stack.bi.rxbufpolicy = chan->rxbufpolicy;
+ stack.bi.txbufpolicy = chan->txbufpolicy;
+ stack.bi.numbufs = chan->numbufs;
+ stack.bi.bufsize = chan->blocksize;
+ /* XXX FIXME! XXX */
+ stack.bi.readbufs = -1;
+ stack.bi.writebufs = -1;
+ if (copy_to_user((struct zt_bufferinfo *)data, &stack.bi, sizeof(stack.bi)))
+ return -EFAULT;
+ break;
+ case ZT_SET_BUFINFO:
+ if (copy_from_user(&stack.bi, (struct zt_bufferinfo *)data, sizeof(stack.bi)))
+ return -EFAULT;
+ if (stack.bi.bufsize > ZT_MAX_BLOCKSIZE)
+ return -EINVAL;
+ if (stack.bi.bufsize < 16)
+ return -EINVAL;
+ if (stack.bi.bufsize * stack.bi.numbufs > ZT_MAX_BUF_SPACE)
+ return -EINVAL;
+ chan->rxbufpolicy = stack.bi.rxbufpolicy & 0x1;
+ chan->txbufpolicy = stack.bi.txbufpolicy & 0x1;
+ if ((rv = zt_reallocbufs(chan, stack.bi.bufsize, stack.bi.numbufs)))
+ return (rv);
+ break;
+ case ZT_GET_BLOCKSIZE: /* get blocksize */
+ put_user(chan->blocksize,(int *)data); /* return block size */
+ break;
+ case ZT_SET_BLOCKSIZE: /* set blocksize */
+ get_user(j,(int *)data);
+ /* cannot be larger than max amount */
+ if (j > ZT_MAX_BLOCKSIZE) return(-EINVAL);
+ /* cannot be less then 16 */
+ if (j < 16) return(-EINVAL);
+ /* allocate a single kernel buffer which we then
+ sub divide into four pieces */
+ if ((rv = zt_reallocbufs(chan, j, chan->numbufs)))
+ return (rv);
+ break;
+ case ZT_FLUSH: /* flush input buffer, output buffer, and/or event queue */
+ get_user(i,(int *)data); /* get param */
+ spin_lock_irqsave(&chan->lock, flags);
+ if (i & ZT_FLUSH_READ) /* if for read (input) */
+ {
+ /* initialize read buffers and pointers */
+ chan->inreadbuf = 0;
+ chan->outreadbuf = -1;
+ for (j=0;j<chan->numbufs;j++) {
+ /* Do we need this? */
+ chan->readn[j] = 0;
+ chan->readidx[j] = 0;
+ }
+ wake_up_interruptible(&chan->readbufq); /* wake_up_interruptible waiting on read */
+ wake_up_interruptible(&chan->sel); /* wake_up_interruptible waiting on select */
+ }
+ if (i & ZT_FLUSH_WRITE) /* if for write (output) */
+ {
+ /* initialize write buffers and pointers */
+ chan->outwritebuf = -1;
+ chan->inwritebuf = 0;
+ for (j=0;j<chan->numbufs;j++) {
+ /* Do we need this? */
+ chan->writen[j] = 0;
+ chan->writeidx[j] = 0;
+ }
+ wake_up_interruptible(&chan->writebufq); /* wake_up_interruptible waiting on write */
+ wake_up_interruptible(&chan->sel); /* wake_up_interruptible waiting on select */
+ /* if IO MUX wait on write empty, well, this
+ certainly *did* empty the write */
+ if (chan->iomask & ZT_IOMUX_WRITEEMPTY)
+ wake_up_interruptible(&chan->eventbufq); /* wake_up_interruptible waiting on IOMUX */
+ }
+ if (i & ZT_FLUSH_EVENT) /* if for events */
+ {
+ /* initialize the event pointers */
+ chan->eventinidx = chan->eventoutidx = 0;
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+ break;
+ case ZT_SYNC: /* wait for no tx */
+ for(;;) /* loop forever */
+ {
+ spin_lock_irqsave(&chan->lock, flags);
+ /* Know if there is a write pending */
+ i = (chan->outwritebuf > -1);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ if (!i) break; /* skip if none */
+ rv = schluffen(&chan->writebufq);
+ if (rv) return(rv);
+ }
+ break;
+ case ZT_IOMUX: /* wait for something to happen */
+ get_user(chan->iomask,(int*)data); /* save mask */
+ if (!chan->iomask) return(-EINVAL); /* cant wait for nothing */
+ for(;;) /* loop forever */
+ {
+ /* has to have SOME mask */
+ ret = 0; /* start with empty return value */
+ spin_lock_irqsave(&chan->lock, flags);
+ /* if looking for read */
+ if (chan->iomask & ZT_IOMUX_READ)
+ {
+ /* if read available */
+ if ((chan->outreadbuf > -1) && !chan->rxdisable)
+ ret |= ZT_IOMUX_READ;
+ }
+ /* if looking for write avail */
+ if (chan->iomask & ZT_IOMUX_WRITE)
+ {
+ if (chan->inwritebuf > -1)
+ ret |= ZT_IOMUX_WRITE;
+ }
+ /* if looking for write empty */
+ if (chan->iomask & ZT_IOMUX_WRITEEMPTY)
+ {
+ /* if everything empty -- be sure the transmitter is enabled */
+ chan->txdisable = 0;
+ if (chan->outwritebuf < 0)
+ ret |= ZT_IOMUX_WRITEEMPTY;
+ }
+ /* if looking for signalling event */
+ if (chan->iomask & ZT_IOMUX_SIGEVENT)
+ {
+ /* if event */
+ if (chan->eventinidx != chan->eventoutidx)
+ ret |= ZT_IOMUX_SIGEVENT;
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+ /* if something to return, or not to wait */
+ if (ret || (chan->iomask & ZT_IOMUX_NOWAIT))
+ {
+ /* set return value */
+ put_user(ret,(int *)data);
+ break; /* get out of loop */
+ }
+ rv = schluffen(&chan->eventbufq);
+ if (rv) return(rv);
+ }
+ /* clear IO MUX mask */
+ chan->iomask = 0;
+ break;
+ case ZT_GETEVENT: /* Get event on queue */
+ /* set up for no event */
+ j = ZT_EVENT_NONE;
+ spin_lock_irqsave(&chan->lock, flags);
+ /* if some event in queue */
+ if (chan->eventinidx != chan->eventoutidx)
+ {
+ j = chan->eventbuf[chan->eventoutidx++];
+ /* get the data, bump index */
+ /* if index overflow, set to beginning */
+ if (chan->eventoutidx >= ZT_MAX_EVENTSIZE)
+ chan->eventoutidx = 0;
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+ put_user(j,(int *)data);
+ break;
+ case ZT_CONFMUTE: /* set confmute flag */
+ get_user(j,(int *)data); /* get conf # */
+ if (!(chan->flags & ZT_FLAG_AUDIO)) return (-EINVAL);
+ spin_lock_irqsave(&bigzaplock, flags);
+ chan->confmute = j;
+ spin_unlock_irqrestore(&bigzaplock, flags);
+ break;
+ case ZT_GETCONFMUTE: /* get confmute flag */
+ if (!(chan->flags & ZT_FLAG_AUDIO)) return (-EINVAL);
+ j = chan->confmute;
+ put_user(j,(int *)data); /* get conf # */
+ rv = 0;
+ break;
+ case ZT_SETTONEZONE:
+ get_user(j, (int *) data);
+ rv = set_tone_zone(chan, j);
+ return rv;
+ case ZT_GETTONEZONE:
+ spin_lock_irqsave(&chan->lock, flags);
+ if (chan->curzone)
+ j = chan->tonezone;
+ spin_unlock_irqrestore(&chan->lock, flags);
+ put_user(j, (int *) data);
+ break;
+ case ZT_SENDTONE:
+ get_user(j,(int *)data);
+ spin_lock_irqsave(&chan->lock, flags);
+ rv = start_tone(chan, j);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return rv;
+ case ZT_GETCONF: /* get conf stuff */
+ if (copy_from_user(&stack.conf,(struct zt_confinfo *) data,sizeof(stack.conf)))
+ return -EFAULT;
+ i = stack.conf.chan; /* get channel no */
+ /* if zero, use current channel no */
+ if (!i) i = chan->channo;
+ /* make sure channel number makes sense */
+ if ((i < 0) || (i > ZT_MAX_CONF) || (!chans[i])) return(-EINVAL);
+ if (!(chans[i]->flags & ZT_FLAG_AUDIO)) return (-EINVAL);
+ stack.conf.chan = i; /* get channel number */
+ stack.conf.confno = chans[i]->confna; /* get conference number */
+ stack.conf.confmode = chans[i]->confmode; /* get conference mode */
+ if (copy_to_user((struct zt_confinfo *) data,&stack.conf,sizeof(stack.conf)))
+ return -EFAULT;
+ break;
+ case ZT_SETCONF: /* set conf stuff */
+ if (copy_from_user(&stack.conf,(struct zt_confinfo *) data,sizeof(stack.conf)))
+ return -EFAULT;
+ i = stack.conf.chan; /* get channel no */
+ /* if zero, use current channel no */
+ if (!i) i = chan->channo;
+ /* make sure channel number makes sense */
+ if ((i < 1) || (i > ZT_MAX_CHANNELS) || (!chans[i])) return(-EINVAL);
+ if (!(chans[i]->flags & ZT_FLAG_AUDIO)) return (-EINVAL);
+ if ((stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITOR ||
+ (stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITORTX ||
+ (stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITORBOTH ||
+ (stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITOR_RX_PREECHO ||
+ (stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITOR_TX_PREECHO ||
+ (stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITORBOTH_PREECHO) {
+ /* Monitor mode -- it's a channel */
+ if ((stack.conf.confno < 0) || (stack.conf.confno >= ZT_MAX_CHANNELS) || !chans[stack.conf.confno]) return(-EINVAL);
+ } else {
+ /* make sure conf number makes sense, too */
+ if ((stack.conf.confno < -1) || (stack.conf.confno > ZT_MAX_CONF)) return(-EINVAL);
+ }
+
+ /* if taking off of any conf, must have 0 mode */
+ if ((!stack.conf.confno) && stack.conf.confmode) return(-EINVAL);
+ /* likewise if 0 mode must have no conf */
+ if ((!stack.conf.confmode) && stack.conf.confno) return (-EINVAL);
+ stack.conf.chan = i; /* return with real channel # */
+ spin_lock_irqsave(&bigzaplock, flagso);
+ spin_lock_irqsave(&chan->lock, flags);
+ if (stack.conf.confno == -1)
+ stack.conf.confno = zt_first_empty_conference();
+ if ((stack.conf.confno < 1) && (stack.conf.confmode)) {
+ /* No more empty conferences */
+ spin_unlock_irqrestore(&chan->lock, flags);
+ spin_unlock_irqrestore(&bigzaplock, flagso);
+ return -EBUSY;
+ }
+ /* if changing confs, clear last added info */
+ if (stack.conf.confno != chans[i]->confna) {
+ memset(chans[i]->conflast, 0, ZT_MAX_CHUNKSIZE);
+ memset(chans[i]->conflast1, 0, ZT_MAX_CHUNKSIZE);
+ memset(chans[i]->conflast2, 0, ZT_MAX_CHUNKSIZE);
+ }
+ j = chans[i]->confna; /* save old conference number */
+ chans[i]->confna = stack.conf.confno; /* set conference number */
+ chans[i]->confmode = stack.conf.confmode; /* set conference mode */
+ chans[i]->_confn = 0; /* Clear confn */
+ zt_check_conf(j);
+ zt_check_conf(stack.conf.confno);
+ if (chans[i]->span && chans[i]->span->dacs) {
+ if (((stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_DIGITALMON) &&
+ chans[stack.conf.confno]->span &&
+ chans[stack.conf.confno]->span->dacs == chans[i]->span->dacs &&
+ chans[i]->txgain == defgain &&
+ chans[i]->rxgain == defgain &&
+ chans[stack.conf.confno]->txgain == defgain &&
+ chans[stack.conf.confno]->rxgain == defgain) {
+ chans[i]->span->dacs(chans[i], chans[stack.conf.confno]);
+ } else {
+ chans[i]->span->dacs(chans[i], NULL);
+ }
+ }
+ /* if we are going onto a conf */
+ if (stack.conf.confno &&
+ ((stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_CONF ||
+ (stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_CONFANN ||
+ (stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_CONFMON ||
+ (stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_CONFANNMON ||
+ (stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_REALANDPSEUDO)) {
+ /* Get alias */
+ chans[i]->_confn = zt_get_conf_alias(stack.conf.confno);
+ }
+
+ if (chans[stack.conf.confno]) {
+ if ((stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITOR_RX_PREECHO ||
+ (stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITOR_TX_PREECHO ||
+ (stack.conf.confmode & ZT_CONF_MODE_MASK) == ZT_CONF_MONITORBOTH_PREECHO)
+ chans[stack.conf.confno]->readchunkpreec = kmalloc(sizeof(*chans[stack.conf.confno]->readchunkpreec) * ZT_CHUNKSIZE, GFP_ATOMIC);
+ else {
+ if (chans[stack.conf.confno]->readchunkpreec) {
+ kfree(chans[stack.conf.confno]->readchunkpreec);
+ chans[stack.conf.confno]->readchunkpreec = NULL;
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+ spin_unlock_irqrestore(&bigzaplock, flagso);
+ if (copy_to_user((struct zt_confinfo *) data,&stack.conf,sizeof(stack.conf)))
+ return -EFAULT;
+ break;
+ case ZT_CONFLINK: /* do conf link stuff */
+ if (!(chan->flags & ZT_FLAG_AUDIO)) return (-EINVAL);
+ if (copy_from_user(&stack.conf,(struct zt_confinfo *) data,sizeof(stack.conf)))
+ return -EFAULT;
+ /* check sanity of arguments */
+ if ((stack.conf.chan < 0) || (stack.conf.chan > ZT_MAX_CONF)) return(-EINVAL);
+ if ((stack.conf.confno < 0) || (stack.conf.confno > ZT_MAX_CONF)) return(-EINVAL);
+ /* cant listen to self!! */
+ if (stack.conf.chan && (stack.conf.chan == stack.conf.confno)) return(-EINVAL);
+ spin_lock_irqsave(&bigzaplock, flagso);
+ spin_lock_irqsave(&chan->lock, flags);
+ /* if to clear all links */
+ if ((!stack.conf.chan) && (!stack.conf.confno))
+ {
+ /* clear all the links */
+ memset(conf_links,0,sizeof(conf_links));
+ recalc_maxlinks();
+ spin_unlock_irqrestore(&chan->lock, flags);
+ spin_unlock_irqrestore(&bigzaplock, flagso);
+ break;
+ }
+ rv = 0; /* clear return value */
+ /* look for already existant specified combination */
+ for(i = 1; i <= ZT_MAX_CONF; i++)
+ {
+ /* if found, exit */
+ if ((conf_links[i].src == stack.conf.chan) &&
+ (conf_links[i].dst == stack.conf.confno)) break;
+ }
+ if (i <= ZT_MAX_CONF) /* if found */
+ {
+ if (!stack.conf.confmode) /* if to remove link */
+ {
+ conf_links[i].src = conf_links[i].dst = 0;
+ }
+ else /* if to add and already there, error */
+ {
+ rv = -EEXIST;
+ }
+ }
+ else /* if not found */
+ {
+ if (stack.conf.confmode) /* if to add link */
+ {
+ /* look for empty location */
+ for(i = 1; i <= ZT_MAX_CONF; i++)
+ {
+ /* if empty, exit loop */
+ if ((!conf_links[i].src) &&
+ (!conf_links[i].dst)) break;
+ }
+ /* if empty spot found */
+ if (i <= ZT_MAX_CONF)
+ {
+ conf_links[i].src = stack.conf.chan;
+ conf_links[i].dst = stack.conf.confno;
+ }
+ else /* if no empties -- error */
+ {
+ rv = -ENOSPC;
+ }
+ }
+ else /* if to remove, and not found -- error */
+ {
+ rv = -ENOENT;
+ }
+ }
+ recalc_maxlinks();
+ spin_unlock_irqrestore(&chan->lock, flags);
+ spin_unlock_irqrestore(&bigzaplock, flagso);
+ return(rv);
+ case ZT_CONFDIAG: /* output diagnostic info to console */
+ if (!(chan->flags & ZT_FLAG_AUDIO)) return (-EINVAL);
+ get_user(j,(int *)data); /* get conf # */
+ /* loop thru the interesting ones */
+ for(i = ((j) ? j : 1); i <= ((j) ? j : ZT_MAX_CONF); i++)
+ {
+ c = 0;
+ for(k = 1; k < ZT_MAX_CHANNELS; k++)
+ {
+ /* skip if no pointer */
+ if (!chans[k]) continue;
+ /* skip if not in this conf */
+ if (chans[k]->confna != i) continue;
+ if (!c) printk("Conf #%d:\n",i);
+ c = 1;
+ printk("chan %d, mode %x\n",
+ k,chans[k]->confmode);
+ }
+ rv = 0;
+ for(k = 1; k <= ZT_MAX_CONF; k++)
+ {
+ if (conf_links[k].dst == i)
+ {
+ if (!c) printk("Conf #%d:\n",i);
+ c = 1;
+ if (!rv) printk("Snooping on:\n");
+ rv = 1;
+ printk("conf %d\n",conf_links[k].src);
+ }
+ }
+ if (c) printk("\n");
+ }
+ break;
+ case ZT_CHANNO: /* get channel number of stream */
+ put_user(unit,(int *)data); /* return unit/channel number */
+ break;
+ case ZT_SETLAW:
+ get_user(j, (int *)data);
+ if ((j < 0) || (j > ZT_LAW_ALAW))
+ return -EINVAL;
+ zt_set_law(chan, j);
+ break;
+ case ZT_SETLINEAR:
+ get_user(j, (int *)data);
+ /* Makes no sense on non-audio channels */
+ if (!(chan->flags & ZT_FLAG_AUDIO))
+ return -EINVAL;
+
+ if (j)
+ chan->flags |= ZT_FLAG_LINEAR;
+ else
+ chan->flags &= ~ZT_FLAG_LINEAR;
+ break;
+ case ZT_SETCADENCE:
+ if (data) {
+ /* Use specific ring cadence */
+ if (copy_from_user(&stack.cad, (struct zt_ring_cadence *)data, sizeof(stack.cad)))
+ return -EFAULT;
+ memcpy(chan->ringcadence, &stack.cad, sizeof(chan->ringcadence));
+ chan->firstcadencepos = 0;
+ /* Looking for negative ringing time indicating where to loop back into ringcadence */
+ for (i=0; i<ZT_MAX_CADENCE; i+=2 ) {
+ if (chan->ringcadence[i]<0) {
+ chan->ringcadence[i] *= -1;
+ chan->firstcadencepos = i;
+ break;
+ }
+ }
+ } else {
+ /* Reset to default */
+ chan->firstcadencepos = 0;
+ if (chan->curzone) {
+ memcpy(chan->ringcadence, chan->curzone->ringcadence, sizeof(chan->ringcadence));
+ /* Looking for negative ringing time indicating where to loop back into ringcadence */
+ for (i=0; i<ZT_MAX_CADENCE; i+=2 ) {
+ if (chan->ringcadence[i]<0) {
+ chan->ringcadence[i] *= -1;
+ chan->firstcadencepos = i;
+ break;
+ }
+ }
+ } else {
+ memset(chan->ringcadence, 0, sizeof(chan->ringcadence));
+ chan->ringcadence[0] = chan->starttime;
+ chan->ringcadence[1] = ZT_RINGOFFTIME;
+ }
+ }
+ break;
+ default:
+ /* Check for common ioctl's and private ones */
+ rv = zt_common_ioctl(inode, file, cmd, data, unit);
+ /* if no span, just return with value */
+ if (!chan->span) return rv;
+ if ((rv == -ENOTTY) && chan->span->ioctl)
+ rv = chan->span->ioctl(chan, cmd, data);
+ return rv;
+
+ }
+ return 0;
+}
+
+#ifdef CONFIG_ZAPATA_PPP
+/*
+ * This is called at softirq (BH) level when there are calls
+ * we need to make to the ppp_generic layer. We do it this
+ * way because the ppp_generic layer functions may not be called
+ * at interrupt level.
+ */
+static void do_ppp_calls(unsigned long data)
+{
+ struct zt_chan *chan = (struct zt_chan *) data;
+ struct sk_buff *skb;
+
+ if (!chan->ppp)
+ return;
+ if (chan->do_ppp_wakeup) {
+ chan->do_ppp_wakeup = 0;
+ ppp_output_wakeup(chan->ppp);
+ }
+ while ((skb = skb_dequeue(&chan->ppp_rq)) != NULL)
+ ppp_input(chan->ppp, skb);
+ if (chan->do_ppp_error) {
+ chan->do_ppp_error = 0;
+ ppp_input_error(chan->ppp, 0);
+ }
+}
+#endif
+
+static int ioctl_echocancel(struct zt_chan *chan, struct zt_echocanparams *ecp, void *data)
+{
+ struct echo_can_state *ec = NULL, *tec;
+ struct zt_echocanparam params[ZT_MAX_ECHOCANPARAMS];
+ int ret;
+ unsigned long flags;
+
+ if (ecp->param_count > ZT_MAX_ECHOCANPARAMS)
+ return -E2BIG;
+
+ if (ecp->tap_length == 0) {
+ /* disable mode, don't need to inspect params */
+ spin_lock_irqsave(&chan->lock, flags);
+ tec = chan->ec;
+ chan->ec = NULL;
+ chan->echocancel = 0;
+ chan->echostate = ECHO_STATE_IDLE;
+ chan->echolastupdate = 0;
+ chan->echotimer = 0;
+ spin_unlock_irqrestore(&chan->lock, flags);
+ if (chan->span && chan->span->echocan)
+ chan->span->echocan(chan, 0);
+ if (tec)
+ echo_can_free(tec);
+
+ return 0;
+ }
+
+ /* if parameters were supplied and this channel's span provides an echocan,
+ but not one that takes params, then we must punt here and return an error */
+ if (ecp->param_count && chan->span && chan->span->echocan &&
+ !chan->span->echocan_with_params)
+ return -EINVAL;
+
+ /* enable mode, need the params */
+
+ if (copy_from_user(params, (struct zt_echocanparam *) data, sizeof(params[0]) * ecp->param_count))
+ return -EFAULT;
+
+ spin_lock_irqsave(&chan->lock, flags);
+ tec = chan->ec;
+ chan->ec = NULL;
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ if (tec)
+ echo_can_free(tec);
+
+ ret = -ENODEV;
+
+ /* attempt to use the span's echo canceler; fall back to built-in
+ if it fails (but not if an error occurs) */
+ if (chan->span) {
+ if (ecp->param_count) {
+ if (chan->span->echocan_with_params)
+ ret = chan->span->echocan_with_params(chan, ecp, params);
+ } else if (chan->span->echocan) {
+ ret = chan->span->echocan(chan, ecp->tap_length);
+ } else if (chan->span->echocan_with_params) {
+ ret = chan->span->echocan_with_params(chan, ecp, NULL);
+ }
+ }
+
+ if (ret == -ENODEV) {
+ switch (ecp->tap_length) {
+ case 32:
+ case 64:
+ case 128:
+ case 256:
+ case 512:
+ case 1024:
+ break;
+ default:
+ ecp->tap_length = deftaps;
+ }
+
+ if ((ret = echo_can_create(ecp, params, &ec)))
+ return ret;
+
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->echocancel = ecp->tap_length;
+ chan->ec = ec;
+ chan->echostate = ECHO_STATE_IDLE;
+ chan->echolastupdate = 0;
+ chan->echotimer = 0;
+ echo_can_disable_detector_init(&chan->txecdis);
+ echo_can_disable_detector_init(&chan->rxecdis);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ }
+
+ return ret;
+}
+
+static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit)
+{
+ struct zt_chan *chan = chans[unit];
+ unsigned long flags;
+ int j, rv;
+ int ret;
+ int oldconf;
+ void *rxgain=NULL;
+ struct echo_can_state *ec;
+ struct zt_echocanparams ecp;
+
+ if (!chan)
+ return -ENOSYS;
+
+ switch(cmd) {
+ case ZT_SIGFREEZE:
+ get_user(j, (int *)data);
+ spin_lock_irqsave(&chan->lock, flags);
+ if (j) {
+ chan->flags |= ZT_FLAG_SIGFREEZE;
+ } else {
+ chan->flags &= ~ZT_FLAG_SIGFREEZE;
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+ break;
+ case ZT_GETSIGFREEZE:
+ spin_lock_irqsave(&chan->lock, flags);
+ if (chan->flags & ZT_FLAG_SIGFREEZE)
+ j = 1;
+ else
+ j = 0;
+ spin_unlock_irqrestore(&chan->lock, flags);
+ put_user(j, (int *)data);
+ break;
+ case ZT_AUDIOMODE:
+ /* Only literal clear channels can be put in */
+ if (chan->sig != ZT_SIG_CLEAR) return (-EINVAL);
+ get_user(j, (int *)data);
+ if (j) {
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->flags |= ZT_FLAG_AUDIO;
+ chan->flags &= ~(ZT_FLAG_HDLC | ZT_FLAG_FCS);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ } else {
+ /* Coming out of audio mode, also clear all
+ conferencing and gain related info as well
+ as echo canceller */
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->flags &= ~ZT_FLAG_AUDIO;
+ /* save old conf number, if any */
+ oldconf = chan->confna;
+ /* initialize conference variables */
+ chan->_confn = 0;
+ chan->confna = 0;
+ if (chan->span && chan->span->dacs)
+ chan->span->dacs(chan, NULL);
+ chan->confmode = 0;
+ chan->confmute = 0;
+ memset(chan->conflast, 0, sizeof(chan->conflast));
+ memset(chan->conflast1, 0, sizeof(chan->conflast1));
+ memset(chan->conflast2, 0, sizeof(chan->conflast2));
+ ec = chan->ec;
+ chan->ec = NULL;
+ /* release conference resource, if any to release */
+ reset_conf(chan);
+ if (chan->gainalloc && chan->rxgain)
+ rxgain = chan->rxgain;
+ else
+ rxgain = NULL;
+
+ chan->rxgain = defgain;
+ chan->txgain = defgain;
+ chan->gainalloc = 0;
+ /* Disable any native echo cancellation as well */
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ if (chan->span && chan->span->echocan)
+ chan->span->echocan(chan, 0);
+
+ if (rxgain)
+ kfree(rxgain);
+ if (ec)
+ echo_can_free(ec);
+ if (oldconf) zt_check_conf(oldconf);
+ }
+ break;
+ case ZT_HDLCPPP:
+#ifdef CONFIG_ZAPATA_PPP
+ if (chan->sig != ZT_SIG_CLEAR) return (-EINVAL);
+ get_user(j, (int *)data);
+ if (j) {
+ if (!chan->ppp) {
+ chan->ppp = kmalloc(sizeof(struct ppp_channel), GFP_KERNEL);
+ if (chan->ppp) {
+ struct echo_can_state *tec;
+ memset(chan->ppp, 0, sizeof(struct ppp_channel));
+ chan->ppp->private = chan;
+ chan->ppp->ops = &ztppp_ops;
+ chan->ppp->mtu = ZT_DEFAULT_MTU_MRU;
+ chan->ppp->hdrlen = 0;
+ skb_queue_head_init(&chan->ppp_rq);
+ chan->do_ppp_wakeup = 0;
+ tasklet_init(&chan->ppp_calls, do_ppp_calls,
+ (unsigned long)chan);
+ if ((ret = zt_reallocbufs(chan, ZT_DEFAULT_MTU_MRU, ZT_DEFAULT_NUM_BUFS))) {
+ kfree(chan->ppp);
+ chan->ppp = NULL;
+ return ret;
+ }
+
+ if ((ret = ppp_register_channel(chan->ppp))) {
+ kfree(chan->ppp);
+ chan->ppp = NULL;
+ return ret;
+ }
+ tec = chan->ec;
+ chan->ec = NULL;
+ chan->echocancel = 0;
+ chan->echostate = ECHO_STATE_IDLE;
+ chan->echolastupdate = 0;
+ chan->echotimer = 0;
+ /* Make sure there's no gain */
+ if (chan->gainalloc)
+ kfree(chan->rxgain);
+ chan->rxgain = defgain;
+ chan->txgain = defgain;
+ chan->gainalloc = 0;
+ chan->flags &= ~ZT_FLAG_AUDIO;
+ chan->flags |= (ZT_FLAG_PPP | ZT_FLAG_HDLC | ZT_FLAG_FCS);
+ if (chan->span && chan->span->echocan)
+ chan->span->echocan(chan, 0);
+ if (tec)
+ echo_can_free(tec);
+ } else
+ return -ENOMEM;
+ }
+ } else {
+ chan->flags &= ~(ZT_FLAG_PPP | ZT_FLAG_HDLC | ZT_FLAG_FCS);
+ if (chan->ppp) {
+ struct ppp_channel *ppp = chan->ppp;
+ chan->ppp = NULL;
+ tasklet_kill(&chan->ppp_calls);
+ skb_queue_purge(&chan->ppp_rq);
+ ppp_unregister_channel(ppp);
+ kfree(ppp);
+ }
+ }
+#else
+ printk("Zaptel: Zaptel PPP support not compiled in\n");
+ return -ENOSYS;
+#endif
+ break;
+ case ZT_HDLCRAWMODE:
+ if (chan->sig != ZT_SIG_CLEAR) return (-EINVAL);
+ get_user(j, (int *)data);
+ chan->flags &= ~(ZT_FLAG_AUDIO | ZT_FLAG_HDLC | ZT_FLAG_FCS);
+ if (j) {
+ chan->flags |= ZT_FLAG_HDLC;
+ fasthdlc_init(&chan->rxhdlc);
+ fasthdlc_init(&chan->txhdlc);
+ }
+ break;
+ case ZT_HDLCFCSMODE:
+ if (chan->sig != ZT_SIG_CLEAR) return (-EINVAL);
+ get_user(j, (int *)data);
+ chan->flags &= ~(ZT_FLAG_AUDIO | ZT_FLAG_HDLC | ZT_FLAG_FCS);
+ if (j) {
+ chan->flags |= ZT_FLAG_HDLC | ZT_FLAG_FCS;
+ fasthdlc_init(&chan->rxhdlc);
+ fasthdlc_init(&chan->txhdlc);
+ }
+ break;
+ case ZT_ECHOCANCEL_PARAMS:
+ if (!(chan->flags & ZT_FLAG_AUDIO))
+ return -EINVAL;
+ if (copy_from_user(&ecp, (struct zt_echocanparams *) data, sizeof(ecp)))
+ return -EFAULT;
+ data += sizeof(ecp);
+ if ((ret = ioctl_echocancel(chan, &ecp, (void *) data)))
+ return ret;
+ break;
+ case ZT_ECHOCANCEL:
+ if (!(chan->flags & ZT_FLAG_AUDIO))
+ return -EINVAL;
+ get_user(j, (int *) data);
+ ecp.tap_length = j;
+ ecp.param_count = 0;
+ if ((ret = ioctl_echocancel(chan, &ecp, NULL)))
+ return ret;
+ break;
+ case ZT_ECHOTRAIN:
+ get_user(j, (int *)data); /* get pre-training time from user */
+ if ((j < 0) || (j >= ZT_MAX_PRETRAINING))
+ return -EINVAL;
+ j <<= 3;
+ if (chan->ec) {
+ /* Start pretraining stage */
+ chan->echostate = ECHO_STATE_PRETRAINING;
+ chan->echotimer = j;
+ } else
+ return -EINVAL;
+ break;
+ case ZT_SETTXBITS:
+ if (chan->sig != ZT_SIG_CAS)
+ return -EINVAL;
+ get_user(j,(int *)data);
+ zt_cas_setbits(chan, j);
+ rv = 0;
+ break;
+ case ZT_GETRXBITS:
+ put_user(chan->rxsig, (int *)data);
+ rv = 0;
+ break;
+ case ZT_LOOPBACK:
+ get_user(j, (int *)data);
+ spin_lock_irqsave(&chan->lock, flags);
+ if (j)
+ chan->flags |= ZT_FLAG_LOOPED;
+ else
+ chan->flags &= ~ZT_FLAG_LOOPED;
+ spin_unlock_irqrestore(&chan->lock, flags);
+ rv = 0;
+ break;
+ case ZT_HOOK:
+ get_user(j,(int *)data);
+ if (chan->flags & ZT_FLAG_CLEAR)
+ return -EINVAL;
+ if (chan->sig == ZT_SIG_CAS)
+ return -EINVAL;
+ /* if no span, just do nothing */
+ if (!chan->span) return(0);
+ spin_lock_irqsave(&chan->lock, flags);
+ /* if dialing, stop it */
+ chan->curtone = NULL;
+ chan->dialing = 0;
+ chan->txdialbuf[0] = '\0';
+ chan->tonep = 0;
+ chan->pdialcount = 0;
+ spin_unlock_irqrestore(&chan->lock, flags);
+ if (chan->span->flags & ZT_FLAG_RBS) {
+ switch (j) {
+ case ZT_ONHOOK:
+ spin_lock_irqsave(&chan->lock, flags);
+ zt_hangup(chan);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ break;
+ case ZT_OFFHOOK:
+ spin_lock_irqsave(&chan->lock, flags);
+ if ((chan->txstate == ZT_TXSTATE_KEWL) ||
+ (chan->txstate == ZT_TXSTATE_AFTERKEWL)) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return -EBUSY;
+ }
+ zt_rbs_sethook(chan, ZT_TXSIG_OFFHOOK, ZT_TXSTATE_DEBOUNCE, chan->debouncetime);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ break;
+ case ZT_RING:
+ case ZT_START:
+ spin_lock_irqsave(&chan->lock, flags);
+ if (chan->txstate != ZT_TXSTATE_ONHOOK) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return -EBUSY;
+ }
+ if (chan->sig & __ZT_SIG_FXO) {
+ ret = 0;
+ chan->cadencepos = 0;
+ ret = chan->ringcadence[0];
+ zt_rbs_sethook(chan, ZT_TXSIG_START, ZT_TXSTATE_RINGON, ret);
+ } else
+ zt_rbs_sethook(chan, ZT_TXSIG_START, ZT_TXSTATE_START, chan->starttime);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ if (file->f_flags & O_NONBLOCK)
+ return -EINPROGRESS;
+#if 0
+ rv = schluffen(&chan->txstateq);
+ if (rv) return rv;
+#endif
+ rv = 0;
+ break;
+ case ZT_WINK:
+ spin_lock_irqsave(&chan->lock, flags);
+ if (chan->txstate != ZT_TXSTATE_ONHOOK) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return -EBUSY;
+ }
+ zt_rbs_sethook(chan, ZT_TXSIG_ONHOOK, ZT_TXSTATE_PREWINK, chan->prewinktime);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ if (file->f_flags & O_NONBLOCK)
+ return -EINPROGRESS;
+ rv = schluffen(&chan->txstateq);
+ if (rv) return rv;
+ break;
+ case ZT_FLASH:
+ spin_lock_irqsave(&chan->lock, flags);
+ if (chan->txstate != ZT_TXSTATE_OFFHOOK) {
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return -EBUSY;
+ }
+ zt_rbs_sethook(chan, ZT_TXSIG_OFFHOOK, ZT_TXSTATE_PREFLASH, chan->preflashtime);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ if (file->f_flags & O_NONBLOCK)
+ return -EINPROGRESS;
+ rv = schluffen(&chan->txstateq);
+ if (rv) return rv;
+ break;
+ case ZT_RINGOFF:
+ spin_lock_irqsave(&chan->lock, flags);
+ zt_rbs_sethook(chan, ZT_TXSIG_ONHOOK, ZT_TXSTATE_ONHOOK, 0);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else if (chan->span->sethook) {
+ if (chan->txhooksig != j) {
+ chan->txhooksig = j;
+ chan->span->sethook(chan, j);
+ }
+ } else
+ return -ENOSYS;
+ break;
+#ifdef CONFIG_ZAPATA_PPP
+ case PPPIOCGCHAN:
+ if (chan->flags & ZT_FLAG_PPP)
+ return put_user(ppp_channel_index(chan->ppp), (int *)data) ? -EFAULT : 0;
+ else
+ return -EINVAL;
+ break;
+ case PPPIOCGUNIT:
+ if (chan->flags & ZT_FLAG_PPP)
+ return put_user(ppp_unit_number(chan->ppp), (int *)data) ? -EFAULT : 0;
+ else
+ return -EINVAL;
+ break;
+#endif
+ default:
+ return zt_chanandpseudo_ioctl(inode, file, cmd, data, unit);
+ }
+ return 0;
+}
+
+static int zt_prechan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit)
+{
+ struct zt_chan *chan = file->private_data;
+ int channo;
+ int res;
+
+ if (chan) {
+ printk("Huh? Prechan already has private data??\n");
+ }
+ switch(cmd) {
+ case ZT_SPECIFY:
+ get_user(channo,(int *)data);
+ if (channo < 1)
+ return -EINVAL;
+ if (channo > ZT_MAX_CHANNELS)
+ return -EINVAL;
+ res = zt_specchan_open(inode, file, channo, 0);
+ if (!res) {
+ /* Setup the pointer for future stuff */
+ chan = chans[channo];
+ file->private_data = chan;
+ /* Return success */
+ return 0;
+ }
+ return res;
+ default:
+ return -ENOSYS;
+ }
+ return 0;
+}
+
+static int zt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data)
+{
+ int unit = UNIT(file);
+ struct zt_chan *chan;
+ struct zt_timer *timer;
+
+ if (!unit)
+ return zt_ctl_ioctl(inode, file, cmd, data);
+
+ if (unit == 250)
+ return zt_transcode_fops->ioctl(inode, file, cmd, data);
+
+ if (unit == 253) {
+ timer = file->private_data;
+ if (timer)
+ return zt_timer_ioctl(inode, file, cmd, data, timer);
+ else
+ return -EINVAL;
+ }
+ if (unit == 254) {
+ chan = file->private_data;
+ if (chan)
+ return zt_chan_ioctl(inode, file, cmd, data, chan->channo);
+ else
+ return zt_prechan_ioctl(inode, file, cmd, data, unit);
+ }
+ if (unit == 255) {
+ chan = file->private_data;
+ if (!chan) {
+ printk("No pseudo channel structure to read?\n");
+ return -EINVAL;
+ }
+ return zt_chanandpseudo_ioctl(inode, file, cmd, data, chan->channo);
+ }
+ return zt_chan_ioctl(inode, file, cmd, data, unit);
+}
+
+int zt_register(struct zt_span *span, int prefmaster)
+{
+ int x;
+
+#ifdef CONFIG_PROC_FS
+ char tempfile[17];
+#endif
+ if (!span)
+ return -EINVAL;
+ if (span->flags & ZT_FLAG_REGISTERED) {
+ printk(KERN_ERR "Span %s already appears to be registered\n", span->name);
+ return -EBUSY;
+ }
+ for (x=1;x<maxspans;x++)
+ if (spans[x] == span) {
+ printk(KERN_ERR "Span %s already in list\n", span->name);
+ return -EBUSY;
+ }
+ for (x=1;x<ZT_MAX_SPANS;x++)
+ if (!spans[x])
+ break;
+ if (x < ZT_MAX_SPANS) {
+ spans[x] = span;
+ if (maxspans < x + 1)
+ maxspans = x + 1;
+ } else {
+ printk(KERN_ERR "Too many zapata spans registered\n");
+ return -EBUSY;
+ }
+ span->flags |= ZT_FLAG_REGISTERED;
+ span->spanno = x;
+ spin_lock_init(&span->lock);
+ if (!span->deflaw) {
+ printk("zaptel: Span %s didn't specify default law. Assuming mulaw, please fix driver!\n", span->name);
+ span->deflaw = ZT_LAW_MULAW;
+ }
+
+ for (x=0;x<span->channels;x++) {
+ span->chans[x].span = span;
+ zt_chan_reg(&span->chans[x]);
+ }
+
+#ifdef CONFIG_PROC_FS
+ sprintf(tempfile, "zaptel/%d", span->spanno);
+ proc_entries[span->spanno] = create_proc_read_entry(tempfile, 0444, NULL , zaptel_proc_read, (int *)(long)span->spanno);
+#endif
+
+#ifdef CONFIG_DEVFS_FS
+ {
+ char span_name[50];
+ sprintf(span_name, "span%d", span->spanno);
+ span->dhandle = devfs_mk_dir(zaptel_devfs_dir, span_name, NULL);
+ for (x = 0; x < span->channels; x++) {
+ struct zt_chan *chan = &span->chans[x];
+ chan->fhandle = register_devfs_channel(chan, chan->span->dhandle); /* Register our stuff with devfs */
+ }
+ }
+#endif /* CONFIG_DEVFS_FS */
+
+#ifdef CONFIG_ZAP_UDEV
+ for (x = 0; x < span->channels; x++) {
+ char chan_name[50];
+ if (span->chans[x].channo < 250) {
+ sprintf(chan_name, "zap%d", span->chans[x].channo);
+ CLASS_DEV_CREATE(zap_class, MKDEV(ZT_MAJOR, span->chans[x].channo), NULL, chan_name);
+ }
+ }
+#endif /* CONFIG_ZAP_UDEV */
+
+ if (debug)
+ printk("Registered Span %d ('%s') with %d channels\n", span->spanno, span->name, span->channels);
+ if (!master || prefmaster) {
+ master = span;
+ if (debug)
+ printk("Span ('%s') is new master\n", span->name);
+ }
+ return 0;
+}
+
+int zt_unregister(struct zt_span *span)
+{
+ int x;
+ int new_maxspans;
+ static struct zt_span *new_master;
+
+#ifdef CONFIG_PROC_FS
+ char tempfile[17];
+#endif /* CONFIG_PROC_FS */
+
+ if (!(span->flags & ZT_FLAG_REGISTERED)) {
+ printk(KERN_ERR "Span %s does not appear to be registered\n", span->name);
+ return -1;
+ }
+ /* Shutdown the span if it's running */
+ if (span->flags & ZT_FLAG_RUNNING)
+ if (span->shutdown)
+ span->shutdown(span);
+
+ if (spans[span->spanno] != span) {
+ printk(KERN_ERR "Span %s has spanno %d which is something else\n", span->name, span->spanno);
+ return -1;
+ }
+ if (debug)
+ printk("Unregistering Span '%s' with %d channels\n", span->name, span->channels);
+#ifdef CONFIG_PROC_FS
+ sprintf(tempfile, "zaptel/%d", span->spanno);
+ remove_proc_entry(tempfile, NULL);
+#endif /* CONFIG_PROC_FS */
+#ifdef CONFIG_DEVFS_FS
+ for (x = 0; x < span->channels; x++) {
+ devfs_unregister(span->chans[x].fhandle);
+ devfs_unregister(span->chans[x].fhandle_symlink);
+ }
+ devfs_unregister(span->dhandle);
+#endif /* CONFIG_DEVFS_FS */
+
+#ifdef CONFIG_ZAP_UDEV
+ for (x = 0; x < span->channels; x++) {
+ if (span->chans[x].channo < 250)
+ class_device_destroy(zap_class, MKDEV(ZT_MAJOR, span->chans[x].channo));
+ }
+#endif /* CONFIG_ZAP_UDEV */
+
+ spans[span->spanno] = NULL;
+ span->spanno = 0;
+ span->flags &= ~ZT_FLAG_REGISTERED;
+ for (x=0;x<span->channels;x++)
+ zt_chan_unreg(&span->chans[x]);
+ new_maxspans = 0;
+ new_master = master; /* FIXME: locking */
+ if (master == span)
+ new_master = NULL;
+ for (x=1;x<ZT_MAX_SPANS;x++) {
+ if (spans[x]) {
+ new_maxspans = x+1;
+ if (!new_master)
+ new_master = spans[x];
+ }
+ }
+ maxspans = new_maxspans;
+ if (master != new_master)
+ if (debug)
+ printk("%s: Span ('%s') is new master\n", __FUNCTION__,
+ (new_master)? new_master->name: "no master");
+ master = new_master;
+
+ return 0;
+}
+
+/*
+** This routine converts from linear to ulaw
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711 (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+** Continuous PCM Companding Law," Villeret, Michel,
+** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+** 1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+** for Analog-to_Digital Conversion Techniques,"
+** 17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+
+#define ZEROTRAP /* turn on the trap as per the MIL-STD */
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+#ifdef CONFIG_CALC_XLAW
+unsigned char
+#else
+static unsigned char __init
+#endif
+__zt_lineartoulaw(short sample)
+{
+ static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if (sign != 0) sample = -sample; /* get magnitude */
+ if (sample > CLIP) sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[(sample >> 7) & 0xFF];
+ mantissa = (sample >> (exponent + 3)) & 0x0F;
+ ulawbyte = ~(sign | (exponent << 4) | mantissa);
+#ifdef ZEROTRAP
+ if (ulawbyte == 0) ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+ if (ulawbyte == 0xff) ulawbyte = 0x7f; /* never return 0xff */
+ return(ulawbyte);
+}
+
+#define AMI_MASK 0x55
+
+#ifdef CONFIG_CALC_XLAW
+unsigned char
+#else
+static inline unsigned char __init
+#endif
+__zt_lineartoalaw (short linear)
+{
+ int mask;
+ int seg;
+ int pcm_val;
+ static int seg_end[8] =
+ {
+ 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
+ };
+
+ pcm_val = linear;
+ if (pcm_val >= 0)
+ {
+ /* Sign (7th) bit = 1 */
+ mask = AMI_MASK | 0x80;
+ }
+ else
+ {
+ /* Sign bit = 0 */
+ mask = AMI_MASK;
+ pcm_val = -pcm_val;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ for (seg = 0; seg < 8; seg++)
+ {
+ if (pcm_val <= seg_end[seg])
+ break;
+ }
+ /* Combine the sign, segment, and quantization bits. */
+ return ((seg << 4) | ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask;
+}
+/*- End of function --------------------------------------------------------*/
+
+static inline short int __init alaw2linear (uint8_t alaw)
+{
+ int i;
+ int seg;
+
+ alaw ^= AMI_MASK;
+ i = ((alaw & 0x0F) << 4);
+ seg = (((int) alaw & 0x70) >> 4);
+ if (seg)
+ i = (i + 0x100) << (seg - 1);
+ return (short int) ((alaw & 0x80) ? i : -i);
+}
+/*- End of function --------------------------------------------------------*/
+static void __init zt_conv_init(void)
+{
+ int i;
+
+ /*
+ * Set up mu-law conversion table
+ */
+ for(i = 0;i < 256;i++)
+ {
+ short mu,e,f,y;
+ static short etab[]={0,132,396,924,1980,4092,8316,16764};
+
+ mu = 255-i;
+ e = (mu & 0x70)/16;
+ f = mu & 0x0f;
+ y = f * (1 << (e + 3));
+ y += etab[e];
+ if (mu & 0x80) y = -y;
+ __zt_mulaw[i] = y;
+ __zt_alaw[i] = alaw2linear(i);
+ /* Default (0.0 db) gain table */
+ defgain[i] = i;
+ }
+#ifndef CONFIG_CALC_XLAW
+ /* set up the reverse (mu-law) conversion table */
+ for(i = -32768; i < 32768; i += 4)
+ {
+ __zt_lin2mu[((unsigned short)(short)i) >> 2] = __zt_lineartoulaw(i);
+ __zt_lin2a[((unsigned short)(short)i) >> 2] = __zt_lineartoalaw(i);
+ }
+#endif
+}
+
+static inline void __zt_process_getaudio_chunk(struct zt_chan *ss, unsigned char *txb)
+{
+ /* We transmit data from our master channel */
+ /* Called with ss->lock held */
+ struct zt_chan *ms = ss->master;
+ /* Linear representation */
+ short getlin[ZT_CHUNKSIZE], k[ZT_CHUNKSIZE];
+ int x;
+
+ /* Okay, now we've got something to transmit */
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ getlin[x] = ZT_XLAW(txb[x], ms);
+#ifndef NO_ECHOCAN_DISABLE
+ if (ms->ec) {
+ for (x=0;x<ZT_CHUNKSIZE;x++) {
+ /* Check for echo cancel disabling tone */
+ if (echo_can_disable_detector_update(&ms->txecdis, getlin[x])) {
+ printk("zaptel Disabled echo canceller because of tone (tx) on channel %d\n", ss->channo);
+ ms->echocancel = 0;
+ ms->echostate = ECHO_STATE_IDLE;
+ ms->echolastupdate = 0;
+ ms->echotimer = 0;
+ echo_can_free(ms->ec);
+ ms->ec = NULL;
+ __qevent(ss, ZT_EVENT_EC_DISABLED);
+ break;
+ }
+ }
+ }
+#endif
+ if ((!ms->confmute && !ms->dialing) || (ms->flags & ZT_FLAG_PSEUDO)) {
+ /* Handle conferencing on non-clear channel and non-HDLC channels */
+ switch(ms->confmode & ZT_CONF_MODE_MASK) {
+ case ZT_CONF_NORMAL:
+ /* Do nuffin */
+ break;
+ case ZT_CONF_MONITOR: /* Monitor a channel's rx mode */
+ /* if a pseudo-channel, ignore */
+ if (ms->flags & ZT_FLAG_PSEUDO) break;
+ /* Add monitored channel */
+ if (chans[ms->confna]->flags & ZT_FLAG_PSEUDO) {
+ ACSS(getlin, chans[ms->confna]->getlin);
+ } else {
+ ACSS(getlin, chans[ms->confna]->putlin);
+ }
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ txb[x] = ZT_LIN2X(getlin[x], ms);
+ break;
+ case ZT_CONF_MONITORTX: /* Monitor a channel's tx mode */
+ /* if a pseudo-channel, ignore */
+ if (ms->flags & ZT_FLAG_PSEUDO) break;
+ /* Add monitored channel */
+ if (chans[ms->confna]->flags & ZT_FLAG_PSEUDO) {
+ ACSS(getlin, chans[ms->confna]->putlin);
+ } else {
+ ACSS(getlin, chans[ms->confna]->getlin);
+ }
+
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ txb[x] = ZT_LIN2X(getlin[x], ms);
+ break;
+ case ZT_CONF_MONITORBOTH: /* monitor a channel's rx and tx mode */
+ /* if a pseudo-channel, ignore */
+ if (ms->flags & ZT_FLAG_PSEUDO) break;
+ ACSS(getlin, chans[ms->confna]->putlin);
+ ACSS(getlin, chans[ms->confna]->getlin);
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ txb[x] = ZT_LIN2X(getlin[x], ms);
+ break;
+ case ZT_CONF_MONITOR_RX_PREECHO: /* Monitor a channel's rx mode */
+ /* if a pseudo-channel, ignore */
+ if (ms->flags & ZT_FLAG_PSEUDO)
+ break;
+
+ if (!chans[ms->confna]->readchunkpreec)
+ break;
+
+ /* Add monitored channel */
+ ACSS(getlin, chans[ms->confna]->flags & ZT_FLAG_PSEUDO ?
+ chans[ms->confna]->readchunkpreec : chans[ms->confna]->putlin);
+ for (x = 0; x < ZT_CHUNKSIZE; x++)
+ txb[x] = ZT_LIN2X(getlin[x], ms);
+
+ break;
+ case ZT_CONF_MONITOR_TX_PREECHO: /* Monitor a channel's tx mode */
+ /* if a pseudo-channel, ignore */
+ if (ms->flags & ZT_FLAG_PSEUDO)
+ break;
+
+ if (!chans[ms->confna]->readchunkpreec)
+ break;
+
+ /* Add monitored channel */
+ ACSS(getlin, chans[ms->confna]->flags & ZT_FLAG_PSEUDO ?
+ chans[ms->confna]->putlin : chans[ms->confna]->readchunkpreec);
+ for (x = 0; x < ZT_CHUNKSIZE; x++)
+ txb[x] = ZT_LIN2X(getlin[x], ms);
+
+ break;
+ case ZT_CONF_MONITORBOTH_PREECHO: /* monitor a channel's rx and tx mode */
+ /* if a pseudo-channel, ignore */
+ if (ms->flags & ZT_FLAG_PSEUDO)
+ break;
+
+ if (!chans[ms->confna]->readchunkpreec)
+ break;
+
+ ACSS(getlin, chans[ms->confna]->putlin);
+ ACSS(getlin, chans[ms->confna]->readchunkpreec);
+
+ for (x = 0; x < ZT_CHUNKSIZE; x++)
+ txb[x] = ZT_LIN2X(getlin[x], ms);
+
+ break;
+ case ZT_CONF_REALANDPSEUDO:
+ /* This strange mode takes the transmit buffer and
+ puts it on the conference, minus its last sample,
+ then outputs from the conference minus the
+ real channel's last sample. */
+ /* if to talk on conf */
+ if (ms->confmode & ZT_CONF_PSEUDO_TALKER) {
+ /* Store temp value */
+ memcpy(k, getlin, ZT_CHUNKSIZE * sizeof(short));
+ /* Add conf value */
+ ACSS(k, conf_sums_next[ms->_confn]);
+ /* save last one */
+ memcpy(ms->conflast2, ms->conflast1, ZT_CHUNKSIZE * sizeof(short));
+ memcpy(ms->conflast1, k, ZT_CHUNKSIZE * sizeof(short));
+ /* get amount actually added */
+ SCSS(ms->conflast1, conf_sums_next[ms->_confn]);
+ /* Really add in new value */
+ ACSS(conf_sums_next[ms->_confn], ms->conflast1);
+ } else {
+ memset(ms->conflast1, 0, ZT_CHUNKSIZE * sizeof(short));
+ memset(ms->conflast2, 0, ZT_CHUNKSIZE * sizeof(short));
+ }
+ memset(getlin, 0, ZT_CHUNKSIZE * sizeof(short));
+ txb[0] = ZT_LIN2X(0, ms);
+ memset(txb + 1, txb[0], ZT_CHUNKSIZE - 1);
+ /* fall through to normal conf mode */
+ case ZT_CONF_CONF: /* Normal conference mode */
+ if (ms->flags & ZT_FLAG_PSEUDO) /* if pseudo-channel */
+ {
+ /* if to talk on conf */
+ if (ms->confmode & ZT_CONF_TALKER) {
+ /* Store temp value */
+ memcpy(k, getlin, ZT_CHUNKSIZE * sizeof(short));
+ /* Add conf value */
+ ACSS(k, conf_sums[ms->_confn]);
+ /* get amount actually added */
+ memcpy(ms->conflast, k, ZT_CHUNKSIZE * sizeof(short));
+ SCSS(ms->conflast, conf_sums[ms->_confn]);
+ /* Really add in new value */
+ ACSS(conf_sums[ms->_confn], ms->conflast);
+ } else memset(ms->conflast, 0, ZT_CHUNKSIZE * sizeof(short));
+ memcpy(getlin, ms->getlin, ZT_CHUNKSIZE * sizeof(short));
+ txb[0] = ZT_LIN2X(0, ms);
+ memset(txb + 1, txb[0], ZT_CHUNKSIZE - 1);
+ break;
+ }
+ /* fall through */
+ case ZT_CONF_CONFMON: /* Conference monitor mode */
+ if (ms->confmode & ZT_CONF_LISTENER) {
+ /* Subtract out last sample written to conf */
+ SCSS(getlin, ms->conflast);
+ /* Add in conference */
+ ACSS(getlin, conf_sums[ms->_confn]);
+ }
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ txb[x] = ZT_LIN2X(getlin[x], ms);
+ break;
+ case ZT_CONF_CONFANN:
+ case ZT_CONF_CONFANNMON:
+ /* First, add tx buffer to conf */
+ ACSS(conf_sums_next[ms->_confn], getlin);
+ /* Start with silence */
+ memset(getlin, 0, ZT_CHUNKSIZE * sizeof(short));
+ /* If a listener on the conf... */
+ if (ms->confmode & ZT_CONF_LISTENER) {
+ /* Subtract last value written */
+ SCSS(getlin, ms->conflast);
+ /* Add in conf */
+ ACSS(getlin, conf_sums[ms->_confn]);
+ }
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ txb[x] = ZT_LIN2X(getlin[x], ms);
+ break;
+ case ZT_CONF_DIGITALMON:
+ /* Real digital monitoring, but still echo cancel if desired */
+ if (!chans[ms->confna])
+ break;
+ if (chans[ms->confna]->flags & ZT_FLAG_PSEUDO) {
+ if (ms->ec) {
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ txb[x] = ZT_LIN2X(chans[ms->confna]->getlin[x], ms);
+ } else {
+ memcpy(txb, chans[ms->confna]->getraw, ZT_CHUNKSIZE);
+ }
+ } else {
+ if (ms->ec) {
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ txb[x] = ZT_LIN2X(chans[ms->confna]->putlin[x], ms);
+ } else {
+ memcpy(txb, chans[ms->confna]->putraw, ZT_CHUNKSIZE);
+ }
+ }
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ getlin[x] = ZT_XLAW(txb[x], ms);
+ break;
+ }
+ }
+ if (ms->confmute || (ms->echostate & __ECHO_STATE_MUTE)) {
+ txb[0] = ZT_LIN2X(0, ms);
+ memset(txb + 1, txb[0], ZT_CHUNKSIZE - 1);
+ if (ms->echostate == ECHO_STATE_STARTTRAINING) {
+ /* Transmit impulse now */
+ txb[0] = ZT_LIN2X(16384, ms);
+ ms->echostate = ECHO_STATE_AWAITINGECHO;
+ }
+ }
+ /* save value from last chunk */
+ memcpy(ms->getlin_lastchunk, ms->getlin, ZT_CHUNKSIZE * sizeof(short));
+ /* save value from current */
+ memcpy(ms->getlin, getlin, ZT_CHUNKSIZE * sizeof(short));
+ /* save value from current */
+ memcpy(ms->getraw, txb, ZT_CHUNKSIZE);
+ /* if to make tx tone */
+ if (ms->v1_1 || ms->v2_1 || ms->v3_1)
+ {
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ {
+ getlin[x] += zt_txtone_nextsample(ms);
+ txb[x] = ZT_LIN2X(getlin[x], ms);
+ }
+ }
+ /* This is what to send (after having applied gain) */
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ txb[x] = ms->txgain[txb[x]];
+}
+
+static inline void __zt_getbuf_chunk(struct zt_chan *ss, unsigned char *txb)
+{
+ /* Called with ss->lock held */
+ /* We transmit data from our master channel */
+ struct zt_chan *ms = ss->master;
+ /* Buffer we're using */
+ unsigned char *buf;
+ /* Old buffer number */
+ int oldbuf;
+ /* Linear representation */
+ int getlin;
+ /* How many bytes we need to process */
+ int bytes = ZT_CHUNKSIZE, left;
+ int x;
+
+ /* Let's pick something to transmit. First source to
+ try is our write-out buffer. Always check it first because
+ its our 'fast path' for whatever that's worth. */
+ while(bytes) {
+ if ((ms->outwritebuf > -1) && !ms->txdisable) {
+ buf= ms->writebuf[ms->outwritebuf];
+ left = ms->writen[ms->outwritebuf] - ms->writeidx[ms->outwritebuf];
+ if (left > bytes)
+ left = bytes;
+ if (ms->flags & ZT_FLAG_HDLC) {
+ /* If this is an HDLC channel we only send a byte of
+ HDLC. */
+ for(x=0;x<left;x++) {
+ if (ms->txhdlc.bits < 8)
+ /* Load a byte of data only if needed */
+ fasthdlc_tx_load_nocheck(&ms->txhdlc, buf[ms->writeidx[ms->outwritebuf]++]);
+ *(txb++) = fasthdlc_tx_run_nocheck(&ms->txhdlc);
+ }
+ bytes -= left;
+ } else {
+ memcpy(txb, buf + ms->writeidx[ms->outwritebuf], left);
+ ms->writeidx[ms->outwritebuf]+=left;
+ txb += left;
+ bytes -= left;
+ }
+ /* Check buffer status */
+ if (ms->writeidx[ms->outwritebuf] >= ms->writen[ms->outwritebuf]) {
+ /* We've reached the end of our buffer. Go to the next. */
+ oldbuf = ms->outwritebuf;
+ /* Clear out write index and such */
+ ms->writeidx[oldbuf] = 0;
+ ms->writen[oldbuf] = 0;
+ ms->outwritebuf = (ms->outwritebuf + 1) % ms->numbufs;
+ if (ms->outwritebuf == ms->inwritebuf) {
+ /* Whoopsies, we're run out of buffers. Mark ours
+ as -1 and wait for the filler to notify us that
+ there is something to write */
+ ms->outwritebuf = -1;
+ if (ms->iomask & (ZT_IOMUX_WRITE | ZT_IOMUX_WRITEEMPTY))
+ wake_up_interruptible(&ms->eventbufq);
+ /* If we're only supposed to start when full, disable the transmitter */
+ if (ms->txbufpolicy == ZT_POLICY_WHEN_FULL)
+ ms->txdisable = 1;
+ }
+ if (ms->inwritebuf < 0) {
+ /* The filler doesn't have a place to put data. Now
+ that we're done with this buffer, notify them. */
+ ms->inwritebuf = oldbuf;
+ }
+/* In the very orignal driver, it was quite well known to me (Jim) that there
+was a possibility that a channel sleeping on a write block needed to
+be potentially woken up EVERY time a buffer was emptied, not just on the first
+one, because if only done on the first one there is a slight timing potential
+of missing the wakeup (between where it senses the (lack of) active condition
+(with interrupts disabled) and where it does the sleep (interrupts enabled)
+in the read or iomux call, etc). That is why the write and iomux calls start
+with an infinite loop that gets broken out of upon an active condition,
+otherwise keeps sleeping and looking. The part in this code got "optimized"
+out in the later versions, and is put back now. */
+ if (!(ms->flags & (ZT_FLAG_NETDEV | ZT_FLAG_PPP))) {
+ wake_up_interruptible(&ms->writebufq);
+ wake_up_interruptible(&ms->sel);
+ if (ms->iomask & ZT_IOMUX_WRITE)
+ wake_up_interruptible(&ms->eventbufq);
+ }
+ /* Transmit a flag if this is an HDLC channel */
+ if (ms->flags & ZT_FLAG_HDLC)
+ fasthdlc_tx_frame_nocheck(&ms->txhdlc);
+#ifdef CONFIG_ZAPATA_NET
+ if (ms->flags & ZT_FLAG_NETDEV)
+ netif_wake_queue(ztchan_to_dev(ms));
+#endif
+#ifdef CONFIG_ZAPATA_PPP
+ if (ms->flags & ZT_FLAG_PPP) {
+ ms->do_ppp_wakeup = 1;
+ tasklet_schedule(&ms->ppp_calls);
+ }
+#endif
+ }
+ } else if (ms->curtone && !(ms->flags & ZT_FLAG_PSEUDO)) {
+ left = ms->curtone->tonesamples - ms->tonep;
+ if (left > bytes)
+ left = bytes;
+ for (x=0;x<left;x++) {
+ /* Pick our default value from the next sample of the current tone */
+ getlin = zt_tone_nextsample(&ms->ts, ms->curtone);
+ *(txb++) = ZT_LIN2X(getlin, ms);
+ }
+ ms->tonep+=left;
+ bytes -= left;
+ if (ms->tonep >= ms->curtone->tonesamples) {
+ struct zt_tone *last;
+ /* Go to the next sample of the tone */
+ ms->tonep = 0;
+ last = ms->curtone;
+ ms->curtone = ms->curtone->next;
+ if (!ms->curtone) {
+ /* No more tones... Is this dtmf or mf? If so, go to the next digit */
+ if (ms->dialing)
+ __do_dtmf(ms);
+ } else {
+ if (last != ms->curtone)
+ zt_init_tone_state(&ms->ts, ms->curtone);
+ }
+ }
+ } else if (ms->flags & ZT_FLAG_LOOPED) {
+ for (x = 0; x < bytes; x++)
+ txb[x] = ms->readchunk[x];
+ bytes = 0;
+ } else if (ms->flags & ZT_FLAG_HDLC) {
+ for (x=0;x<bytes;x++) {
+ /* Okay, if we're HDLC, then transmit a flag by default */
+ if (ms->txhdlc.bits < 8)
+ fasthdlc_tx_frame_nocheck(&ms->txhdlc);
+ *(txb++) = fasthdlc_tx_run_nocheck(&ms->txhdlc);
+ }
+ bytes = 0;
+ } else if (ms->flags & ZT_FLAG_CLEAR) {
+ /* Clear channels that are idle in audio mode need
+ to send silence; in non-audio mode, always send 0xff
+ so stupid switches won't consider the channel active
+ */
+ if (ms->flags & ZT_FLAG_AUDIO) {
+ memset(txb, ZT_LIN2X(0, ms), bytes);
+ } else {
+ memset(txb, 0xFF, bytes);
+ }
+ bytes = 0;
+ } else {
+ memset(txb, ZT_LIN2X(0, ms), bytes); /* Lastly we use silence on telephony channels */
+ bytes = 0;
+ }
+ }
+}
+
+static inline void rbs_itimer_expire(struct zt_chan *chan)
+{
+ /* the only way this could have gotten here, is if a channel
+ went onf hook longer then the wink or flash detect timeout */
+ /* Called with chan->lock held */
+ switch(chan->sig)
+ {
+ case ZT_SIG_FXOLS: /* if FXO, its definitely on hook */
+ case ZT_SIG_FXOGS:
+ case ZT_SIG_FXOKS:
+ __qevent(chan,ZT_EVENT_ONHOOK);
+ chan->gotgs = 0;
+ break;
+#if defined(EMFLASH) || defined(EMPULSE)
+ case ZT_SIG_EM:
+ case ZT_SIG_EM_E1:
+ if (chan->rxhooksig == ZT_RXSIG_ONHOOK) {
+ __qevent(chan,ZT_EVENT_ONHOOK);
+ break;
+ }
+ __qevent(chan,ZT_EVENT_RINGOFFHOOK);
+ break;
+#endif
+#ifdef FXSFLASH
+ case ZT_SIG_FXSKS:
+ if (chan->rxhooksig == ZT_RXSIG_ONHOOK) {
+ __qevent(chan, ZT_EVENT_ONHOOK);
+ break;
+ }
+#endif
+ /* fall thru intentionally */
+ default: /* otherwise, its definitely off hook */
+ __qevent(chan,ZT_EVENT_RINGOFFHOOK);
+ break;
+ }
+}
+
+static inline void __rbs_otimer_expire(struct zt_chan *chan)
+{
+ int len = 0;
+ /* Called with chan->lock held */
+
+ chan->otimer = 0;
+ /* Move to the next timer state */
+ switch(chan->txstate) {
+ case ZT_TXSTATE_RINGOFF:
+ /* Turn on the ringer now that the silent time has passed */
+ ++chan->cadencepos;
+ if (chan->cadencepos >= ZT_MAX_CADENCE)
+ chan->cadencepos = chan->firstcadencepos;
+ len = chan->ringcadence[chan->cadencepos];
+
+ if (!len) {
+ chan->cadencepos = chan->firstcadencepos;
+ len = chan->ringcadence[chan->cadencepos];
+ }
+
+ zt_rbs_sethook(chan, ZT_TXSIG_START, ZT_TXSTATE_RINGON, len);
+ __qevent(chan, ZT_EVENT_RINGERON);
+ break;
+
+ case ZT_TXSTATE_RINGON:
+ /* Turn off the ringer now that the loud time has passed */
+ ++chan->cadencepos;
+ if (chan->cadencepos >= ZT_MAX_CADENCE)
+ chan->cadencepos = 0;
+ len = chan->ringcadence[chan->cadencepos];
+
+ if (!len) {
+ chan->cadencepos = 0;
+ len = chan->curzone->ringcadence[chan->cadencepos];
+ }
+
+ zt_rbs_sethook(chan, ZT_TXSIG_OFFHOOK, ZT_TXSTATE_RINGOFF, len);
+ __qevent(chan, ZT_EVENT_RINGEROFF);
+ break;
+
+ case ZT_TXSTATE_START:
+ /* If we were starting, go off hook now ready to debounce */
+ zt_rbs_sethook(chan, ZT_TXSIG_OFFHOOK, ZT_TXSTATE_AFTERSTART, ZT_AFTERSTART_TIME);
+ wake_up_interruptible(&chan->txstateq);
+ break;
+
+ case ZT_TXSTATE_PREWINK:
+ /* Actually wink */
+ zt_rbs_sethook(chan, ZT_TXSIG_OFFHOOK, ZT_TXSTATE_WINK, chan->winktime);
+ break;
+
+ case ZT_TXSTATE_WINK:
+ /* Wink complete, go on hook and stabalize */
+ zt_rbs_sethook(chan, ZT_TXSIG_ONHOOK, ZT_TXSTATE_ONHOOK, 0);
+ if (chan->file && (chan->file->f_flags & O_NONBLOCK))
+ __qevent(chan, ZT_EVENT_HOOKCOMPLETE);
+ wake_up_interruptible(&chan->txstateq);
+ break;
+
+ case ZT_TXSTATE_PREFLASH:
+ /* Actually flash */
+ zt_rbs_sethook(chan, ZT_TXSIG_ONHOOK, ZT_TXSTATE_FLASH, chan->flashtime);
+ break;
+
+ case ZT_TXSTATE_FLASH:
+ zt_rbs_sethook(chan, ZT_TXSIG_OFFHOOK, ZT_TXSTATE_OFFHOOK, 0);
+ if (chan->file && (chan->file->f_flags & O_NONBLOCK))
+ __qevent(chan, ZT_EVENT_HOOKCOMPLETE);
+ wake_up_interruptible(&chan->txstateq);
+ break;
+
+ case ZT_TXSTATE_DEBOUNCE:
+ zt_rbs_sethook(chan, ZT_TXSIG_OFFHOOK, ZT_TXSTATE_OFFHOOK, 0);
+ /* See if we've gone back on hook */
+ if ((chan->rxhooksig == ZT_RXSIG_ONHOOK) && (chan->rxflashtime > 2))
+ chan->itimerset = chan->itimer = chan->rxflashtime * ZT_CHUNKSIZE;
+ wake_up_interruptible(&chan->txstateq);
+ break;
+
+ case ZT_TXSTATE_AFTERSTART:
+ zt_rbs_sethook(chan, ZT_TXSIG_OFFHOOK, ZT_TXSTATE_OFFHOOK, 0);
+ if (chan->file && (chan->file->f_flags & O_NONBLOCK))
+ __qevent(chan, ZT_EVENT_HOOKCOMPLETE);
+ wake_up_interruptible(&chan->txstateq);
+ break;
+
+ case ZT_TXSTATE_KEWL:
+ zt_rbs_sethook(chan, ZT_TXSIG_ONHOOK, ZT_TXSTATE_AFTERKEWL, ZT_AFTERKEWLTIME);
+ if (chan->file && (chan->file->f_flags & O_NONBLOCK))
+ __qevent(chan, ZT_EVENT_HOOKCOMPLETE);
+ wake_up_interruptible(&chan->txstateq);
+ break;
+
+ case ZT_TXSTATE_AFTERKEWL:
+ if (chan->kewlonhook) {
+ __qevent(chan,ZT_EVENT_ONHOOK);
+ }
+ chan->txstate = ZT_TXSTATE_ONHOOK;
+ chan->gotgs = 0;
+ break;
+
+ case ZT_TXSTATE_PULSEBREAK:
+ zt_rbs_sethook(chan, ZT_TXSIG_OFFHOOK, ZT_TXSTATE_PULSEMAKE,
+ chan->pulsemaketime);
+ wake_up_interruptible(&chan->txstateq);
+ break;
+
+ case ZT_TXSTATE_PULSEMAKE:
+ if (chan->pdialcount)
+ chan->pdialcount--;
+ if (chan->pdialcount)
+ {
+ zt_rbs_sethook(chan, ZT_TXSIG_ONHOOK,
+ ZT_TXSTATE_PULSEBREAK, chan->pulsebreaktime);
+ break;
+ }
+ chan->txstate = ZT_TXSTATE_PULSEAFTER;
+ chan->otimer = chan->pulseaftertime * ZT_CHUNKSIZE;
+ wake_up_interruptible(&chan->txstateq);
+ break;
+
+ case ZT_TXSTATE_PULSEAFTER:
+ chan->txstate = ZT_TXSTATE_OFFHOOK;
+ __do_dtmf(chan);
+ wake_up_interruptible(&chan->txstateq);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void __zt_hooksig_pvt(struct zt_chan *chan, zt_rxsig_t rxsig)
+{
+
+ /* State machines for receive hookstate transitions
+ called with chan->lock held */
+
+ if ((chan->rxhooksig) == rxsig) return;
+
+ if ((chan->flags & ZT_FLAG_SIGFREEZE)) return;
+
+ chan->rxhooksig = rxsig;
+#ifdef RINGBEGIN
+ if ((chan->sig & __ZT_SIG_FXS) && (rxsig == ZT_RXSIG_RING) &&
+ (!chan->ringdebtimer))
+ __qevent(chan,ZT_EVENT_RINGBEGIN);
+#endif
+ switch(chan->sig) {
+ case ZT_SIG_EM: /* E and M */
+ case ZT_SIG_EM_E1:
+ switch(rxsig) {
+ case ZT_RXSIG_OFFHOOK: /* went off hook */
+ /* The interface is going off hook */
+#ifdef EMFLASH
+ if (chan->itimer)
+ {
+ __qevent(chan,ZT_EVENT_WINKFLASH);
+ chan->itimerset = chan->itimer = 0;
+ break;
+ }
+#endif
+#ifdef EMPULSE
+ if (chan->itimer) /* if timer still running */
+ {
+ int plen = chan->itimerset - chan->itimer;
+ if (plen <= ZT_MAXPULSETIME)
+ {
+ if (plen >= ZT_MINPULSETIME)
+ {
+ chan->pulsecount++;
+
+ chan->pulsetimer = ZT_PULSETIMEOUT;
+ chan->itimerset = chan->itimer = 0;
+ if (chan->pulsecount == 1)
+ __qevent(chan,ZT_EVENT_PULSE_START);
+ }
+ }
+ break;
+ }
+#endif
+ /* set wink timer */
+ chan->itimerset = chan->itimer = chan->rxwinktime * ZT_CHUNKSIZE;
+ break;
+ case ZT_RXSIG_ONHOOK: /* went on hook */
+ /* This interface is now going on hook.
+ Check for WINK, etc */
+ if (chan->itimer)
+ __qevent(chan,ZT_EVENT_WINKFLASH);
+#if defined(EMFLASH) || defined(EMPULSE)
+ else {
+#ifdef EMFLASH
+ chan->itimerset = chan->itimer = chan->rxflashtime * ZT_CHUNKSIZE;
+
+#else /* EMFLASH */
+ chan->itimerset = chan->itimer = chan->rxwinktime * ZT_CHUNKSIZE;
+
+#endif /* EMFLASH */
+ chan->gotgs = 0;
+ break;
+ }
+#else /* EMFLASH || EMPULSE */
+ else {
+ __qevent(chan,ZT_EVENT_ONHOOK);
+ chan->gotgs = 0;
+ }
+#endif
+ chan->itimerset = chan->itimer = 0;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ZT_SIG_FXSKS: /* FXS Kewlstart */
+ /* ignore a bit poopy if loop not closed and stable */
+ if (chan->txstate != ZT_TXSTATE_OFFHOOK) break;
+#ifdef FXSFLASH
+ if (rxsig == ZT_RXSIG_ONHOOK) {
+ chan->itimer = ZT_FXSFLASHMAXTIME * ZT_CHUNKSIZE;
+ break;
+ } else if (rxsig == ZT_RXSIG_OFFHOOK) {
+ if (chan->itimer) {
+ /* did the offhook occur in the window? if not, ignore both events */
+ if (chan->itimer <= ((ZT_FXSFLASHMAXTIME - ZT_FXSFLASHMINTIME) * ZT_CHUNKSIZE))
+ __qevent(chan, ZT_EVENT_WINKFLASH);
+ }
+ chan->itimer = 0;
+ break;
+ }
+#endif
+ /* fall through intentionally */
+ case ZT_SIG_FXSGS: /* FXS Groundstart */
+ if (rxsig == ZT_RXSIG_ONHOOK) {
+ chan->ringdebtimer = RING_DEBOUNCE_TIME;
+ chan->ringtrailer = 0;
+ if (chan->txstate != ZT_TXSTATE_DEBOUNCE) {
+ chan->gotgs = 0;
+ __qevent(chan,ZT_EVENT_ONHOOK);
+ }
+ }
+ break;
+ case ZT_SIG_FXOGS: /* FXO Groundstart */
+ if (rxsig == ZT_RXSIG_START) {
+ /* if havent got gs, report it */
+ if (!chan->gotgs) {
+ __qevent(chan,ZT_EVENT_RINGOFFHOOK);
+ chan->gotgs = 1;
+ }
+ }
+ /* fall through intentionally */
+ case ZT_SIG_FXOLS: /* FXO Loopstart */
+ case ZT_SIG_FXOKS: /* FXO Kewlstart */
+ switch(rxsig) {
+ case ZT_RXSIG_OFFHOOK: /* went off hook */
+ /* if asserti ng ring, stop it */
+ if (chan->txstate == ZT_TXSTATE_START) {
+ zt_rbs_sethook(chan,ZT_TXSIG_OFFHOOK, ZT_TXSTATE_AFTERSTART, ZT_AFTERSTART_TIME);
+ }
+ chan->kewlonhook = 0;
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("Off hook on channel %d, itimer = %d, gotgs = %d\n", chan->channo, chan->itimer, chan->gotgs);
+#endif
+ if (chan->itimer) /* if timer still running */
+ {
+ int plen = chan->itimerset - chan->itimer;
+ if (plen <= ZT_MAXPULSETIME)
+ {
+ if (plen >= ZT_MINPULSETIME)
+ {
+ chan->pulsecount++;
+ chan->pulsetimer = ZT_PULSETIMEOUT;
+ chan->itimer = chan->itimerset;
+ if (chan->pulsecount == 1)
+ __qevent(chan,ZT_EVENT_PULSE_START);
+ }
+ } else
+ __qevent(chan,ZT_EVENT_WINKFLASH);
+ } else {
+ /* if havent got GS detect */
+ if (!chan->gotgs) {
+ __qevent(chan,ZT_EVENT_RINGOFFHOOK);
+ chan->gotgs = 1;
+ chan->itimerset = chan->itimer = 0;
+ }
+ }
+ chan->itimerset = chan->itimer = 0;
+ break;
+ case ZT_RXSIG_ONHOOK: /* went on hook */
+ /* if not during offhook debounce time */
+ if ((chan->txstate != ZT_TXSTATE_DEBOUNCE) &&
+ (chan->txstate != ZT_TXSTATE_KEWL) &&
+ (chan->txstate != ZT_TXSTATE_AFTERKEWL)) {
+ chan->itimerset = chan->itimer = chan->rxflashtime * ZT_CHUNKSIZE;
+ }
+ if (chan->txstate == ZT_TXSTATE_KEWL)
+ chan->kewlonhook = 1;
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void zt_hooksig(struct zt_chan *chan, zt_rxsig_t rxsig)
+{
+ /* skip if no change */
+ unsigned long flags;
+ spin_lock_irqsave(&chan->lock, flags);
+ __zt_hooksig_pvt(chan,rxsig);
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+void zt_rbsbits(struct zt_chan *chan, int cursig)
+{
+ unsigned long flags;
+ if (cursig == chan->rxsig)
+ return;
+
+ if ((chan->flags & ZT_FLAG_SIGFREEZE)) return;
+
+ spin_lock_irqsave(&chan->lock, flags);
+ switch(chan->sig) {
+ case ZT_SIG_FXOGS: /* FXO Groundstart */
+ /* B-bit only matters for FXO GS */
+ if (!(cursig & ZT_BBIT)) {
+ __zt_hooksig_pvt(chan, ZT_RXSIG_START);
+ break;
+ }
+ /* Fall through */
+ case ZT_SIG_EM: /* E and M */
+ case ZT_SIG_EM_E1:
+ case ZT_SIG_FXOLS: /* FXO Loopstart */
+ case ZT_SIG_FXOKS: /* FXO Kewlstart */
+ if (cursig & ZT_ABIT) /* off hook */
+ __zt_hooksig_pvt(chan,ZT_RXSIG_OFFHOOK);
+ else /* on hook */
+ __zt_hooksig_pvt(chan,ZT_RXSIG_ONHOOK);
+ break;
+
+ case ZT_SIG_FXSKS: /* FXS Kewlstart */
+ case ZT_SIG_FXSGS: /* FXS Groundstart */
+ /* Fall through */
+ case ZT_SIG_FXSLS:
+ if (!(cursig & ZT_BBIT)) {
+ /* Check for ringing first */
+ __zt_hooksig_pvt(chan, ZT_RXSIG_RING);
+ break;
+ }
+ if ((chan->sig != ZT_SIG_FXSLS) && (cursig & ZT_ABIT)) {
+ /* if went on hook */
+ __zt_hooksig_pvt(chan, ZT_RXSIG_ONHOOK);
+ } else {
+ __zt_hooksig_pvt(chan, ZT_RXSIG_OFFHOOK);
+ }
+ break;
+ case ZT_SIG_CAS:
+ /* send event that something changed */
+ __qevent(chan, ZT_EVENT_BITSCHANGED);
+ break;
+
+ default:
+ break;
+ }
+ /* Keep track of signalling for next time */
+ chan->rxsig = cursig;
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+
+static inline void __zt_ec_chunk(struct zt_chan *ss, unsigned char *rxchunk, const unsigned char *txchunk)
+{
+ short rxlin, txlin;
+ int x;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ss->lock, flags);
+
+ if (ss->readchunkpreec) {
+ /* Save a copy of the audio before the echo can has its way with it */
+ for (x = 0; x < ZT_CHUNKSIZE; x++)
+ /* We only ever really need to deal with signed linear - let's just convert it now */
+ ss->readchunkpreec[x] = ZT_XLAW(rxchunk[x], ss);
+ }
+
+ /* Perform echo cancellation on a chunk if necessary */
+ if (ss->ec) {
+#if defined(CONFIG_ZAPTEL_MMX) || defined(ECHO_CAN_FP)
+ zt_kernel_fpu_begin();
+#endif
+ if (ss->echostate & __ECHO_STATE_MUTE) {
+ /* Special stuff for training the echo can */
+ for (x=0;x<ZT_CHUNKSIZE;x++) {
+ rxlin = ZT_XLAW(rxchunk[x], ss);
+ txlin = ZT_XLAW(txchunk[x], ss);
+ if (ss->echostate == ECHO_STATE_PRETRAINING) {
+ if (--ss->echotimer <= 0) {
+ ss->echotimer = 0;
+ ss->echostate = ECHO_STATE_STARTTRAINING;
+ }
+ }
+ if ((ss->echostate == ECHO_STATE_AWAITINGECHO) && (txlin > 8000)) {
+ ss->echolastupdate = 0;
+ ss->echostate = ECHO_STATE_TRAINING;
+ }
+ if (ss->echostate == ECHO_STATE_TRAINING) {
+ if (echo_can_traintap(ss->ec, ss->echolastupdate++, rxlin)) {
+#if 0
+ printk("Finished training (%d taps trained)!\n", ss->echolastupdate);
+#endif
+ ss->echostate = ECHO_STATE_ACTIVE;
+ }
+ }
+ rxlin = 0;
+ rxchunk[x] = ZT_LIN2X((int)rxlin, ss);
+ }
+ } else {
+#if !defined(ZT_EC_ARRAY_UPDATE)
+ for (x=0;x<ZT_CHUNKSIZE;x++) {
+ rxlin = ZT_XLAW(rxchunk[x], ss);
+ rxlin = echo_can_update(ss->ec, ZT_XLAW(txchunk[x], ss), rxlin);
+ rxchunk[x] = ZT_LIN2X((int) rxlin, ss);
+ }
+#else /* defined(ZT_EC_ARRAY_UPDATE) */
+ short rxlins[ZT_CHUNKSIZE], txlins[ZT_CHUNKSIZE];
+ for (x = 0; x < ZT_CHUNKSIZE; x++) {
+ rxlins[x] = ZT_XLAW(rxchunk[x], ss);
+ txlins[x] = ZT_XLAW(txchunk[x], ss);
+ }
+ echo_can_array_update(ss->ec, rxlins, txlins);
+ for (x = 0; x < ZT_CHUNKSIZE; x++)
+ rxchunk[x] = ZT_LIN2X((int) rxlins[x], ss);
+#endif /* defined(ZT_EC_ARRAY_UPDATE) */
+ }
+#if defined(CONFIG_ZAPTEL_MMX) || defined(ECHO_CAN_FP)
+ kernel_fpu_end();
+#endif
+ }
+ spin_unlock_irqrestore(&ss->lock, flags);
+}
+
+void zt_ec_chunk(struct zt_chan *ss, unsigned char *rxchunk, const unsigned char *txchunk)
+{
+ __zt_ec_chunk(ss, rxchunk, txchunk);
+}
+
+void zt_ec_span(struct zt_span *span)
+{
+ int x;
+ for (x = 0; x < span->channels; x++) {
+ if (span->chans[x].ec)
+ __zt_ec_chunk(&span->chans[x], span->chans[x].readchunk, span->chans[x].writechunk);
+ }
+}
+
+/* return 0 if nothing detected, 1 if lack of tone, 2 if presence of tone */
+/* modifies buffer pointed to by 'amp' with notched-out values */
+static inline int sf_detect (sf_detect_state_t *s,
+ short *amp,
+ int samples,long p1, long p2, long p3)
+{
+int i,rv = 0;
+long x,y;
+
+#define SF_DETECT_SAMPLES (ZT_CHUNKSIZE * 5)
+#define SF_DETECT_MIN_ENERGY 500
+#define NB 14 /* number of bits to shift left */
+
+ /* determine energy level before filtering */
+ for(i = 0; i < samples; i++)
+ {
+ if (amp[i] < 0) s->e1 -= amp[i];
+ else s->e1 += amp[i];
+ }
+ /* do 2nd order IIR notch filter at given freq. and calculate
+ energy */
+ for(i = 0; i < samples; i++)
+ {
+ x = amp[i] << NB;
+ y = s->x2 + (p1 * (s->x1 >> NB)) + x;
+ y += (p2 * (s->y2 >> NB)) +
+ (p3 * (s->y1 >> NB));
+ s->x2 = s->x1;
+ s->x1 = x;
+ s->y2 = s->y1;
+ s->y1 = y;
+ amp[i] = y >> NB;
+ if (amp[i] < 0) s->e2 -= amp[i];
+ else s->e2 += amp[i];
+ }
+ s->samps += i;
+ /* if time to do determination */
+ if ((s->samps) >= SF_DETECT_SAMPLES)
+ {
+ rv = 1; /* default to no tone */
+ /* if enough energy, it is determined to be a tone */
+ if (((s->e1 - s->e2) / s->samps) > SF_DETECT_MIN_ENERGY) rv = 2;
+ /* reset energy processing variables */
+ s->samps = 0;
+ s->e1 = s->e2 = 0;
+ }
+ return(rv);
+}
+
+static inline void __zt_process_putaudio_chunk(struct zt_chan *ss, unsigned char *rxb)
+{
+ /* We transmit data from our master channel */
+ /* Called with ss->lock held */
+ struct zt_chan *ms = ss->master;
+ /* Linear version of received data */
+ short putlin[ZT_CHUNKSIZE],k[ZT_CHUNKSIZE];
+ int x,r;
+
+ if (ms->dialing) ms->afterdialingtimer = 50;
+ else if (ms->afterdialingtimer) ms->afterdialingtimer--;
+ if (ms->afterdialingtimer && (!(ms->flags & ZT_FLAG_PSEUDO))) {
+ /* Be careful since memset is likely a macro */
+ rxb[0] = ZT_LIN2X(0, ms);
+ memset(&rxb[1], rxb[0], ZT_CHUNKSIZE - 1); /* receive as silence if dialing */
+ }
+ for (x=0;x<ZT_CHUNKSIZE;x++) {
+ rxb[x] = ms->rxgain[rxb[x]];
+ putlin[x] = ZT_XLAW(rxb[x], ms);
+ }
+
+#ifndef NO_ECHOCAN_DISABLE
+ if (ms->ec) {
+ for (x=0;x<ZT_CHUNKSIZE;x++) {
+ if (echo_can_disable_detector_update(&ms->rxecdis, putlin[x])) {
+ printk("zaptel Disabled echo canceller because of tone (rx) on channel %d\n", ss->channo);
+ ms->echocancel = 0;
+ ms->echostate = ECHO_STATE_IDLE;
+ ms->echolastupdate = 0;
+ ms->echotimer = 0;
+ echo_can_free(ms->ec);
+ ms->ec = NULL;
+ break;
+ }
+ }
+ }
+#endif
+ /* if doing rx tone decoding */
+ if (ms->rxp1 && ms->rxp2 && ms->rxp3)
+ {
+ r = sf_detect(&ms->rd,putlin,ZT_CHUNKSIZE,ms->rxp1,
+ ms->rxp2,ms->rxp3);
+ /* Convert back */
+ for(x=0;x<ZT_CHUNKSIZE;x++)
+ rxb[x] = ZT_LIN2X(putlin[x], ms);
+ if (r) /* if something happened */
+ {
+ if (r != ms->rd.lastdetect)
+ {
+ if (((r == 2) && !(ms->toneflags & ZT_REVERSE_RXTONE)) ||
+ ((r == 1) && (ms->toneflags & ZT_REVERSE_RXTONE)))
+ {
+ __qevent(ms,ZT_EVENT_RINGOFFHOOK);
+ }
+ else
+ {
+ __qevent(ms,ZT_EVENT_ONHOOK);
+ }
+ ms->rd.lastdetect = r;
+ }
+ }
+ }
+
+ if (!(ms->flags & ZT_FLAG_PSEUDO)) {
+ memcpy(ms->putlin, putlin, ZT_CHUNKSIZE * sizeof(short));
+ memcpy(ms->putraw, rxb, ZT_CHUNKSIZE);
+ }
+
+ /* Take the rxc, twiddle it for conferencing if appropriate and put it
+ back */
+ if ((!ms->confmute && !ms->afterdialingtimer) ||
+ (ms->flags & ZT_FLAG_PSEUDO)) {
+ switch(ms->confmode & ZT_CONF_MODE_MASK) {
+ case ZT_CONF_NORMAL: /* Normal mode */
+ /* Do nothing. rx goes output */
+ break;
+ case ZT_CONF_MONITOR: /* Monitor a channel's rx mode */
+ /* if not a pseudo-channel, ignore */
+ if (!(ms->flags & ZT_FLAG_PSEUDO)) break;
+ /* Add monitored channel */
+ if (chans[ms->confna]->flags & ZT_FLAG_PSEUDO) {
+ ACSS(putlin, chans[ms->confna]->getlin);
+ } else {
+ ACSS(putlin, chans[ms->confna]->putlin);
+ }
+ /* Convert back */
+ for(x=0;x<ZT_CHUNKSIZE;x++)
+ rxb[x] = ZT_LIN2X(putlin[x], ms);
+ break;
+ case ZT_CONF_MONITORTX: /* Monitor a channel's tx mode */
+ /* if not a pseudo-channel, ignore */
+ if (!(ms->flags & ZT_FLAG_PSEUDO)) break;
+ /* Add monitored channel */
+ if (chans[ms->confna]->flags & ZT_FLAG_PSEUDO) {
+ ACSS(putlin, chans[ms->confna]->putlin);
+ } else {
+ ACSS(putlin, chans[ms->confna]->getlin);
+ }
+ /* Convert back */
+ for(x=0;x<ZT_CHUNKSIZE;x++)
+ rxb[x] = ZT_LIN2X(putlin[x], ms);
+ break;
+ case ZT_CONF_MONITORBOTH: /* Monitor a channel's tx and rx mode */
+ /* if not a pseudo-channel, ignore */
+ if (!(ms->flags & ZT_FLAG_PSEUDO)) break;
+ /* Note: Technically, saturation should be done at
+ the end of the whole addition, but for performance
+ reasons, we don't do that. Besides, it only matters
+ when you're so loud you're clipping anyway */
+ ACSS(putlin, chans[ms->confna]->getlin);
+ ACSS(putlin, chans[ms->confna]->putlin);
+ /* Convert back */
+ for(x=0;x<ZT_CHUNKSIZE;x++)
+ rxb[x] = ZT_LIN2X(putlin[x], ms);
+ break;
+ case ZT_CONF_MONITOR_RX_PREECHO: /* Monitor a channel's rx mode */
+ /* if not a pseudo-channel, ignore */
+ if (!(ms->flags & ZT_FLAG_PSEUDO))
+ break;
+
+ if (!chans[ms->confna]->readchunkpreec)
+ break;
+
+ /* Add monitored channel */
+ ACSS(putlin, chans[ms->confna]->flags & ZT_FLAG_PSEUDO ?
+ chans[ms->confna]->getlin : chans[ms->confna]->readchunkpreec);
+ for (x = 0; x < ZT_CHUNKSIZE; x++)
+ rxb[x] = ZT_LIN2X(putlin[x], ms);
+
+ break;
+ case ZT_CONF_MONITOR_TX_PREECHO: /* Monitor a channel's tx mode */
+ /* if not a pseudo-channel, ignore */
+ if (!(ms->flags & ZT_FLAG_PSEUDO))
+ break;
+
+ if (!chans[ms->confna]->readchunkpreec)
+ break;
+
+ /* Add monitored channel */
+ ACSS(putlin, chans[ms->confna]->flags & ZT_FLAG_PSEUDO ?
+ chans[ms->confna]->readchunkpreec : chans[ms->confna]->getlin);
+ for (x = 0; x < ZT_CHUNKSIZE; x++)
+ rxb[x] = ZT_LIN2X(putlin[x], ms);
+
+ break;
+ case ZT_CONF_MONITORBOTH_PREECHO: /* Monitor a channel's tx and rx mode */
+ /* if not a pseudo-channel, ignore */
+ if (!(ms->flags & ZT_FLAG_PSEUDO))
+ break;
+
+ if (!chans[ms->confna]->readchunkpreec)
+ break;
+
+ /* Note: Technically, saturation should be done at
+ the end of the whole addition, but for performance
+ reasons, we don't do that. Besides, it only matters
+ when you're so loud you're clipping anyway */
+ ACSS(putlin, chans[ms->confna]->getlin);
+ ACSS(putlin, chans[ms->confna]->readchunkpreec);
+ for (x = 0; x < ZT_CHUNKSIZE; x++)
+ rxb[x] = ZT_LIN2X(putlin[x], ms);
+
+ break;
+ case ZT_CONF_REALANDPSEUDO:
+ /* do normal conf mode processing */
+ if (ms->confmode & ZT_CONF_TALKER) {
+ /* Store temp value */
+ memcpy(k, putlin, ZT_CHUNKSIZE * sizeof(short));
+ /* Add conf value */
+ ACSS(k, conf_sums_next[ms->_confn]);
+ /* get amount actually added */
+ memcpy(ms->conflast, k, ZT_CHUNKSIZE * sizeof(short));
+ SCSS(ms->conflast, conf_sums_next[ms->_confn]);
+ /* Really add in new value */
+ ACSS(conf_sums_next[ms->_confn], ms->conflast);
+ } else memset(ms->conflast, 0, ZT_CHUNKSIZE * sizeof(short));
+ /* do the pseudo-channel part processing */
+ memset(putlin, 0, ZT_CHUNKSIZE * sizeof(short));
+ if (ms->confmode & ZT_CONF_PSEUDO_LISTENER) {
+ /* Subtract out previous last sample written to conf */
+ SCSS(putlin, ms->conflast2);
+ /* Add in conference */
+ ACSS(putlin, conf_sums[ms->_confn]);
+ }
+ /* Convert back */
+ for(x=0;x<ZT_CHUNKSIZE;x++)
+ rxb[x] = ZT_LIN2X(putlin[x], ms);
+ break;
+ case ZT_CONF_CONF: /* Normal conference mode */
+ if (ms->flags & ZT_FLAG_PSEUDO) /* if a pseudo-channel */
+ {
+ if (ms->confmode & ZT_CONF_LISTENER) {
+ /* Subtract out last sample written to conf */
+ SCSS(putlin, ms->conflast);
+ /* Add in conference */
+ ACSS(putlin, conf_sums[ms->_confn]);
+ }
+ /* Convert back */
+ for(x=0;x<ZT_CHUNKSIZE;x++)
+ rxb[x] = ZT_LIN2X(putlin[x], ms);
+ memcpy(ss->getlin, putlin, ZT_CHUNKSIZE * sizeof(short));
+ break;
+ }
+ /* fall through */
+ case ZT_CONF_CONFANN: /* Conference with announce */
+ if (ms->confmode & ZT_CONF_TALKER) {
+ /* Store temp value */
+ memcpy(k, putlin, ZT_CHUNKSIZE * sizeof(short));
+ /* Add conf value */
+ ACSS(k, conf_sums_next[ms->_confn]);
+ /* get amount actually added */
+ memcpy(ms->conflast, k, ZT_CHUNKSIZE * sizeof(short));
+ SCSS(ms->conflast, conf_sums_next[ms->_confn]);
+ /* Really add in new value */
+ ACSS(conf_sums_next[ms->_confn], ms->conflast);
+ } else
+ memset(ms->conflast, 0, ZT_CHUNKSIZE * sizeof(short));
+ /* rxc unmodified */
+ break;
+ case ZT_CONF_CONFMON:
+ case ZT_CONF_CONFANNMON:
+ if (ms->confmode & ZT_CONF_TALKER) {
+ /* Store temp value */
+ memcpy(k, putlin, ZT_CHUNKSIZE * sizeof(short));
+ /* Subtract last value */
+ SCSS(conf_sums[ms->_confn], ms->conflast);
+ /* Add conf value */
+ ACSS(k, conf_sums[ms->_confn]);
+ /* get amount actually added */
+ memcpy(ms->conflast, k, ZT_CHUNKSIZE * sizeof(short));
+ SCSS(ms->conflast, conf_sums[ms->_confn]);
+ /* Really add in new value */
+ ACSS(conf_sums[ms->_confn], ms->conflast);
+ } else
+ memset(ms->conflast, 0, ZT_CHUNKSIZE * sizeof(short));
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ rxb[x] = ZT_LIN2X((int)conf_sums_prev[ms->_confn][x], ms);
+ break;
+ case ZT_CONF_DIGITALMON:
+ /* if not a pseudo-channel, ignore */
+ if (!(ms->flags & ZT_FLAG_PSEUDO)) break;
+ /* Add monitored channel */
+ if (chans[ms->confna]->flags & ZT_FLAG_PSEUDO) {
+ memcpy(rxb, chans[ms->confna]->getraw, ZT_CHUNKSIZE);
+ } else {
+ memcpy(rxb, chans[ms->confna]->putraw, ZT_CHUNKSIZE);
+ }
+ break;
+ }
+ }
+}
+
+/* HDLC (or other) receiver buffer functions for read side */
+static inline void __putbuf_chunk(struct zt_chan *ss, unsigned char *rxb, int bytes)
+{
+ /* We transmit data from our master channel */
+ /* Called with ss->lock held */
+ struct zt_chan *ms = ss->master;
+ /* Our receive buffer */
+ unsigned char *buf;
+#if defined(CONFIG_ZAPATA_NET) || defined(CONFIG_ZAPATA_PPP)
+ /* SKB for receiving network stuff */
+ struct sk_buff *skb=NULL;
+#endif
+ int oldbuf;
+ int eof=0;
+ int abort=0;
+ int res;
+ int left, x;
+
+ while(bytes) {
+#if defined(CONFIG_ZAPATA_NET) || defined(CONFIG_ZAPATA_PPP)
+ skb = NULL;
+#endif
+ abort = 0;
+ eof = 0;
+ /* Next, figure out if we've got a buffer to receive into */
+ if (ms->inreadbuf > -1) {
+ /* Read into the current buffer */
+ buf = ms->readbuf[ms->inreadbuf];
+ left = ms->blocksize - ms->readidx[ms->inreadbuf];
+ if (left > bytes)
+ left = bytes;
+ if (ms->flags & ZT_FLAG_HDLC) {
+ for (x=0;x<left;x++) {
+ /* Handle HDLC deframing */
+ fasthdlc_rx_load_nocheck(&ms->rxhdlc, *(rxb++));
+ bytes--;
+ res = fasthdlc_rx_run(&ms->rxhdlc);
+ /* If there is nothing there, continue */
+ if (res & RETURN_EMPTY_FLAG)
+ continue;
+ else if (res & RETURN_COMPLETE_FLAG) {
+ /* Only count this if it's a non-empty frame */
+ if (ms->readidx[ms->inreadbuf]) {
+ if ((ms->flags & ZT_FLAG_FCS) && (ms->infcs != PPP_GOODFCS)) {
+ abort = ZT_EVENT_BADFCS;
+ } else
+ eof=1;
+ break;
+ }
+ continue;
+ } else if (res & RETURN_DISCARD_FLAG) {
+ /* This could be someone idling with
+ "idle" instead of "flag" */
+ if (!ms->readidx[ms->inreadbuf])
+ continue;
+ abort = ZT_EVENT_ABORT;
+ break;
+ } else {
+ unsigned char rxc;
+ rxc = res;
+ ms->infcs = PPP_FCS(ms->infcs, rxc);
+ buf[ms->readidx[ms->inreadbuf]++] = rxc;
+ /* Pay attention to the possibility of an overrun */
+ if (ms->readidx[ms->inreadbuf] >= ms->blocksize) {
+ if (!ss->span->alarms)
+ printk(KERN_WARNING "HDLC Receiver overrun on channel %s (master=%s)\n", ss->name, ss->master->name);
+ abort=ZT_EVENT_OVERRUN;
+ /* Force the HDLC state back to frame-search mode */
+ ms->rxhdlc.state = 0;
+ ms->rxhdlc.bits = 0;
+ ms->readidx[ms->inreadbuf]=0;
+ break;
+ }
+ }
+ }
+ } else {
+ /* Not HDLC */
+ memcpy(buf + ms->readidx[ms->inreadbuf], rxb, left);
+ rxb += left;
+ ms->readidx[ms->inreadbuf] += left;
+ bytes -= left;
+ /* End of frame is decided by block size of 'N' */
+ eof = (ms->readidx[ms->inreadbuf] >= ms->blocksize);
+ if (eof && (ss->flags & ZT_FLAG_NOSTDTXRX)) {
+ eof = 0;
+ abort = ZT_EVENT_OVERRUN;
+ }
+ }
+ if (eof) {
+ /* Finished with this buffer, try another. */
+ oldbuf = ms->inreadbuf;
+ ms->infcs = PPP_INITFCS;
+ ms->readn[ms->inreadbuf] = ms->readidx[ms->inreadbuf];
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("EOF, len is %d\n", ms->readn[ms->inreadbuf]);
+#endif
+#if defined(CONFIG_ZAPATA_NET) || defined(CONFIG_ZAPATA_PPP)
+ if (ms->flags & (ZT_FLAG_NETDEV | ZT_FLAG_PPP)) {
+#ifdef CONFIG_ZAPATA_NET
+#endif /* CONFIG_ZAPATA_NET */
+ /* Our network receiver logic is MUCH
+ different. We actually only use a single
+ buffer */
+ if (ms->readn[ms->inreadbuf] > 1) {
+ /* Drop the FCS */
+ ms->readn[ms->inreadbuf] -= 2;
+ /* Allocate an SKB */
+#ifdef CONFIG_ZAPATA_PPP
+ if (!ms->do_ppp_error)
+#endif
+ skb = dev_alloc_skb(ms->readn[ms->inreadbuf]);
+ if (skb) {
+ /* XXX Get rid of this memcpy XXX */
+ memcpy(skb->data, ms->readbuf[ms->inreadbuf], ms->readn[ms->inreadbuf]);
+ skb_put(skb, ms->readn[ms->inreadbuf]);
+#ifdef CONFIG_ZAPATA_NET
+ if (ms->flags & ZT_FLAG_NETDEV) {
+#ifdef LINUX26
+ struct net_device_stats *stats = hdlc_stats(ms->hdlcnetdev->netdev);
+#else /* LINUX26 */
+ struct net_device_stats *stats = &ms->hdlcnetdev->netdev.stats;
+#endif /* LINUX26 */
+ stats->rx_packets++;
+ stats->rx_bytes += ms->readn[ms->inreadbuf];
+ }
+#endif
+
+ } else {
+#ifdef CONFIG_ZAPATA_NET
+ if (ms->flags & ZT_FLAG_NETDEV) {
+#ifdef LINUX26
+ struct net_device_stats *stats = hdlc_stats(ms->hdlcnetdev->netdev);
+#else /* LINUX26 */
+ struct net_device_stats *stats = &ms->hdlcnetdev->netdev.stats;
+#endif /* LINUX26 */
+ stats->rx_dropped++;
+ }
+#endif
+#ifdef CONFIG_ZAPATA_PPP
+ if (ms->flags & ZT_FLAG_PPP) {
+ abort = ZT_EVENT_OVERRUN;
+ }
+#endif
+#if 1
+#ifdef CONFIG_ZAPATA_PPP
+ if (!ms->do_ppp_error)
+#endif
+ printk("Memory squeeze, dropped one\n");
+#endif
+ }
+ }
+ /* We don't cycle through buffers, just
+ reuse the same one */
+ ms->readn[ms->inreadbuf] = 0;
+ ms->readidx[ms->inreadbuf] = 0;
+ } else
+#endif
+ {
+ ms->inreadbuf = (ms->inreadbuf + 1) % ms->numbufs;
+ if (ms->inreadbuf == ms->outreadbuf) {
+ /* Whoops, we're full, and have no where else
+ to store into at the moment. We'll drop it
+ until there's a buffer available */
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("Out of storage space\n");
+#endif
+ ms->inreadbuf = -1;
+ /* Enable the receiver in case they've got POLICY_WHEN_FULL */
+ ms->rxdisable = 0;
+ }
+ if (ms->outreadbuf < 0) { /* start out buffer if not already */
+ ms->outreadbuf = oldbuf;
+ }
+/* In the very orignal driver, it was quite well known to me (Jim) that there
+was a possibility that a channel sleeping on a receive block needed to
+be potentially woken up EVERY time a buffer was filled, not just on the first
+one, because if only done on the first one there is a slight timing potential
+of missing the wakeup (between where it senses the (lack of) active condition
+(with interrupts disabled) and where it does the sleep (interrupts enabled)
+in the read or iomux call, etc). That is why the read and iomux calls start
+with an infinite loop that gets broken out of upon an active condition,
+otherwise keeps sleeping and looking. The part in this code got "optimized"
+out in the later versions, and is put back now. */
+ if (!ms->rxdisable) { /* if receiver enabled */
+ /* Notify a blocked reader that there is data available
+ to be read, unless we're waiting for it to be full */
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("Notifying reader data in block %d\n", oldbuf);
+#endif
+ wake_up_interruptible(&ms->readbufq);
+ wake_up_interruptible(&ms->sel);
+ if (ms->iomask & ZT_IOMUX_READ)
+ wake_up_interruptible(&ms->eventbufq);
+ }
+ }
+ }
+ if (abort) {
+ /* Start over reading frame */
+ ms->readidx[ms->inreadbuf] = 0;
+ ms->infcs = PPP_INITFCS;
+
+#ifdef CONFIG_ZAPATA_NET
+ if (ms->flags & ZT_FLAG_NETDEV) {
+#ifdef LINUX26
+ struct net_device_stats *stats = hdlc_stats(ms->hdlcnetdev->netdev);
+#else /* LINUX26 */
+ struct net_device_stats *stats = &ms->hdlcnetdev->netdev.stats;
+#endif /* LINUX26 */
+ stats->rx_errors++;
+ if (abort == ZT_EVENT_OVERRUN)
+ stats->rx_over_errors++;
+ if (abort == ZT_EVENT_BADFCS)
+ stats->rx_crc_errors++;
+ if (abort == ZT_EVENT_ABORT)
+ stats->rx_frame_errors++;
+ } else
+#endif
+#ifdef CONFIG_ZAPATA_PPP
+ if (ms->flags & ZT_FLAG_PPP) {
+ ms->do_ppp_error = 1;
+ tasklet_schedule(&ms->ppp_calls);
+ } else
+#endif
+
+ if ((ms->flags & ZT_FLAG_OPEN) && !ss->span->alarms)
+ /* Notify the receiver... */
+ __qevent(ss->master, abort);
+#if 0
+ printk("torintr_receive: Aborted %d bytes of frame on %d\n", amt, ss->master);
+#endif
+
+ }
+ } else /* No place to receive -- drop on the floor */
+ break;
+#ifdef CONFIG_ZAPATA_NET
+ if (skb && (ms->flags & ZT_FLAG_NETDEV))
+#ifdef NEW_HDLC_INTERFACE
+ {
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)
+ skb->mac.raw = skb->data;
+#else
+ skb_reset_mac_header(skb);
+#endif
+ skb->dev = ztchan_to_dev(ms);
+#ifdef ZAP_HDLC_TYPE_TRANS
+ skb->protocol = hdlc_type_trans(skb, ztchan_to_dev(ms));
+#else
+ skb->protocol = htons (ETH_P_HDLC);
+#endif
+ netif_rx(skb);
+ }
+#else
+ hdlc_netif_rx(&ms->hdlcnetdev->netdev, skb);
+#endif
+#endif
+#ifdef CONFIG_ZAPATA_PPP
+ if (skb && (ms->flags & ZT_FLAG_PPP)) {
+ unsigned char *tmp;
+ tmp = skb->data;
+ skb_pull(skb, 2);
+ /* Make sure that it's addressed to ALL STATIONS and UNNUMBERED */
+ if (!tmp || (tmp[0] != 0xff) || (tmp[1] != 0x03)) {
+ /* Invalid SKB -- drop */
+ if (tmp)
+ printk("Received invalid SKB (%02x, %02x)\n", tmp[0], tmp[1]);
+ dev_kfree_skb_irq(skb);
+ } else {
+ skb_queue_tail(&ms->ppp_rq, skb);
+ tasklet_schedule(&ms->ppp_calls);
+ }
+ }
+#endif
+ }
+}
+
+static inline void __zt_putbuf_chunk(struct zt_chan *ss, unsigned char *rxb)
+{
+ __putbuf_chunk(ss, rxb, ZT_CHUNKSIZE);
+}
+
+static void __zt_hdlc_abort(struct zt_chan *ss, int event)
+{
+ if (ss->inreadbuf >= 0)
+ ss->readidx[ss->inreadbuf] = 0;
+ if ((ss->flags & ZT_FLAG_OPEN) && !ss->span->alarms)
+ __qevent(ss->master, event);
+}
+
+extern void zt_hdlc_abort(struct zt_chan *ss, int event)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&ss->lock, flags);
+ __zt_hdlc_abort(ss, event);
+ spin_unlock_irqrestore(&ss->lock, flags);
+}
+
+extern void zt_hdlc_putbuf(struct zt_chan *ss, unsigned char *rxb, int bytes)
+{
+ unsigned long flags;
+ int res;
+ int left;
+
+ spin_lock_irqsave(&ss->lock, flags);
+ if (ss->inreadbuf < 0) {
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("No place to receive HDLC frame\n");
+#endif
+ spin_unlock_irqrestore(&ss->lock, flags);
+ return;
+ }
+ /* Read into the current buffer */
+ left = ss->blocksize - ss->readidx[ss->inreadbuf];
+ if (left > bytes)
+ left = bytes;
+ if (left > 0) {
+ memcpy(ss->readbuf[ss->inreadbuf] + ss->readidx[ss->inreadbuf], rxb, left);
+ rxb += left;
+ ss->readidx[ss->inreadbuf] += left;
+ bytes -= left;
+ }
+ /* Something isn't fit into buffer */
+ if (bytes) {
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("HDLC frame isn't fit into buffer space\n");
+#endif
+ __zt_hdlc_abort(ss, ZT_EVENT_OVERRUN);
+ }
+ res = left;
+ spin_unlock_irqrestore(&ss->lock, flags);
+}
+
+extern void zt_hdlc_finish(struct zt_chan *ss)
+{
+ int oldreadbuf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ss->lock, flags);
+
+ if ((oldreadbuf = ss->inreadbuf) < 0) {
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("No buffers to finish\n");
+#endif
+ spin_unlock_irqrestore(&ss->lock, flags);
+ return;
+ }
+
+ if (!ss->readidx[ss->inreadbuf]) {
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("Empty HDLC frame received\n");
+#endif
+ spin_unlock_irqrestore(&ss->lock, flags);
+ return;
+ }
+
+ ss->readn[ss->inreadbuf] = ss->readidx[ss->inreadbuf];
+ ss->inreadbuf = (ss->inreadbuf + 1) % ss->numbufs;
+ if (ss->inreadbuf == ss->outreadbuf) {
+ ss->inreadbuf = -1;
+#ifdef CONFIG_ZAPATA_DEBUG
+ printk("Notifying reader data in block %d\n", oldreadbuf);
+#endif
+ ss->rxdisable = 0;
+ }
+ if (ss->outreadbuf < 0) {
+ ss->outreadbuf = oldreadbuf;
+ }
+
+ if (!ss->rxdisable) {
+ wake_up_interruptible(&ss->readbufq);
+ wake_up_interruptible(&ss->sel);
+ if (ss->iomask & ZT_IOMUX_READ)
+ wake_up_interruptible(&ss->eventbufq);
+ }
+ spin_unlock_irqrestore(&ss->lock, flags);
+}
+
+/* Returns 1 if EOF, 0 if data is still in frame, -1 if EOF and no buffers left */
+extern int zt_hdlc_getbuf(struct zt_chan *ss, unsigned char *bufptr, unsigned int *size)
+{
+ unsigned char *buf;
+ unsigned long flags;
+ int left = 0;
+ int res;
+ int oldbuf;
+
+ spin_lock_irqsave(&ss->lock, flags);
+ if (ss->outwritebuf > -1) {
+ buf = ss->writebuf[ss->outwritebuf];
+ left = ss->writen[ss->outwritebuf] - ss->writeidx[ss->outwritebuf];
+ /* Strip off the empty HDLC CRC end */
+ left -= 2;
+ if (left <= *size) {
+ *size = left;
+ res = 1;
+ } else
+ res = 0;
+
+ memcpy(bufptr, &buf[ss->writeidx[ss->outwritebuf]], *size);
+ ss->writeidx[ss->outwritebuf] += *size;
+
+ if (res) {
+ /* Rotate buffers */
+ oldbuf = ss->outwritebuf;
+ ss->writeidx[oldbuf] = 0;
+ ss->writen[oldbuf] = 0;
+ ss->outwritebuf = (ss->outwritebuf + 1) % ss->numbufs;
+ if (ss->outwritebuf == ss->inwritebuf) {
+ ss->outwritebuf = -1;
+ if (ss->iomask & (ZT_IOMUX_WRITE | ZT_IOMUX_WRITEEMPTY))
+ wake_up_interruptible(&ss->eventbufq);
+ /* If we're only supposed to start when full, disable the transmitter */
+ if (ss->txbufpolicy == ZT_POLICY_WHEN_FULL)
+ ss->txdisable = 1;
+ res = -1;
+ }
+
+ if (ss->inwritebuf < 0)
+ ss->inwritebuf = oldbuf;
+
+ if (!(ss->flags & (ZT_FLAG_NETDEV | ZT_FLAG_PPP))) {
+ wake_up_interruptible(&ss->writebufq);
+ wake_up_interruptible(&ss->sel);
+ if ((ss->iomask & ZT_IOMUX_WRITE) && (res >= 0))
+ wake_up_interruptible(&ss->eventbufq);
+ }
+ }
+ } else {
+ res = -1;
+ *size = 0;
+ }
+ spin_unlock_irqrestore(&ss->lock, flags);
+
+ return res;
+}
+
+
+static void process_timers(void)
+{
+ unsigned long flags;
+ struct zt_timer *cur;
+ spin_lock_irqsave(&zaptimerlock, flags);
+ cur = zaptimers;
+ while(cur) {
+ if (cur->ms) {
+ cur->pos -= ZT_CHUNKSIZE;
+ if (cur->pos <= 0) {
+ cur->tripped++;
+ cur->pos = cur->ms;
+ wake_up_interruptible(&cur->sel);
+ }
+ }
+ cur = cur->next;
+ }
+ spin_unlock_irqrestore(&zaptimerlock, flags);
+}
+
+static unsigned int zt_timer_poll(struct file *file, struct poll_table_struct *wait_table)
+{
+ struct zt_timer *timer = file->private_data;
+ unsigned long flags;
+ int ret = 0;
+ if (timer) {
+ poll_wait(file, &timer->sel, wait_table);
+ spin_lock_irqsave(&zaptimerlock, flags);
+ if (timer->tripped || timer->ping)
+ ret |= POLLPRI;
+ spin_unlock_irqrestore(&zaptimerlock, flags);
+ } else
+ ret = -EINVAL;
+ return ret;
+}
+
+/* device poll routine */
+static unsigned int
+zt_chan_poll(struct file *file, struct poll_table_struct *wait_table, int unit)
+{
+
+ struct zt_chan *chan = chans[unit];
+ int ret;
+ unsigned long flags;
+
+ /* do the poll wait */
+ if (chan) {
+ poll_wait(file, &chan->sel, wait_table);
+ ret = 0; /* start with nothing to return */
+ spin_lock_irqsave(&chan->lock, flags);
+ /* if at least 1 write buffer avail */
+ if (chan->inwritebuf > -1) {
+ ret |= POLLOUT | POLLWRNORM;
+ }
+ if ((chan->outreadbuf > -1) && !chan->rxdisable) {
+ ret |= POLLIN | POLLRDNORM;
+ }
+ if (chan->eventoutidx != chan->eventinidx)
+ {
+ /* Indicate an exception */
+ ret |= POLLPRI;
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+ } else
+ ret = -EINVAL;
+ return(ret); /* return what we found */
+}
+
+static int zt_mmap(struct file *file, struct vm_area_struct *vm)
+{
+ int unit = UNIT(file);
+ if (unit == 250)
+ return zt_transcode_fops->mmap(file, vm);
+ return -ENOSYS;
+}
+
+static unsigned int zt_poll(struct file *file, struct poll_table_struct *wait_table)
+{
+ int unit = UNIT(file);
+ struct zt_chan *chan;
+
+ if (!unit)
+ return -EINVAL;
+
+ if (unit == 250)
+ return zt_transcode_fops->poll(file, wait_table);
+
+ if (unit == 253)
+ return zt_timer_poll(file, wait_table);
+
+ if (unit == 254) {
+ chan = file->private_data;
+ if (!chan)
+ return -EINVAL;
+ return zt_chan_poll(file, wait_table,chan->channo);
+ }
+ if (unit == 255) {
+ chan = file->private_data;
+ if (!chan) {
+ printk("No pseudo channel structure to read?\n");
+ return -EINVAL;
+ }
+ return zt_chan_poll(file, wait_table, chan->channo);
+ }
+ return zt_chan_poll(file, wait_table, unit);
+}
+
+static void __zt_transmit_chunk(struct zt_chan *chan, unsigned char *buf)
+{
+ unsigned char silly[ZT_CHUNKSIZE];
+ /* Called with chan->lock locked */
+#ifdef OPTIMIZE_CHANMUTE
+ if(likely(chan->chanmute))
+ return;
+#endif
+ if (!buf)
+ buf = silly;
+ __zt_getbuf_chunk(chan, buf);
+
+ if ((chan->flags & ZT_FLAG_AUDIO) || (chan->confmode)) {
+#ifdef CONFIG_ZAPTEL_MMX
+ zt_kernel_fpu_begin();
+#endif
+ __zt_process_getaudio_chunk(chan, buf);
+#ifdef CONFIG_ZAPTEL_MMX
+ kernel_fpu_end();
+#endif
+ }
+}
+
+static inline void __zt_real_transmit(struct zt_chan *chan)
+{
+ /* Called with chan->lock held */
+#ifdef OPTIMIZE_CHANMUTE
+ if(likely(chan->chanmute))
+ return;
+#endif
+ if (chan->confmode) {
+ /* Pull queued data off the conference */
+ __buf_pull(&chan->confout, chan->writechunk, chan, "zt_real_transmit");
+ } else {
+ __zt_transmit_chunk(chan, chan->writechunk);
+ }
+}
+
+static void __zt_getempty(struct zt_chan *ms, unsigned char *buf)
+{
+ int bytes = ZT_CHUNKSIZE;
+ int left;
+ unsigned char *txb = buf;
+ int x;
+ short getlin;
+ /* Called with ms->lock held */
+
+ while(bytes) {
+ /* Receive silence, or tone */
+ if (ms->curtone) {
+ left = ms->curtone->tonesamples - ms->tonep;
+ if (left > bytes)
+ left = bytes;
+ for (x=0;x<left;x++) {
+ /* Pick our default value from the next sample of the current tone */
+ getlin = zt_tone_nextsample(&ms->ts, ms->curtone);
+ *(txb++) = ZT_LIN2X(getlin, ms);
+ }
+ ms->tonep+=left;
+ bytes -= left;
+ if (ms->tonep >= ms->curtone->tonesamples) {
+ struct zt_tone *last;
+ /* Go to the next sample of the tone */
+ ms->tonep = 0;
+ last = ms->curtone;
+ ms->curtone = ms->curtone->next;
+ if (!ms->curtone) {
+ /* No more tones... Is this dtmf or mf? If so, go to the next digit */
+ if (ms->dialing)
+ __do_dtmf(ms);
+ } else {
+ if (last != ms->curtone)
+ zt_init_tone_state(&ms->ts, ms->curtone);
+ }
+ }
+ } else {
+ /* Use silence */
+ memset(txb, ZT_LIN2X(0, ms), bytes);
+ bytes = 0;
+ }
+ }
+
+}
+
+static void __zt_receive_chunk(struct zt_chan *chan, unsigned char *buf)
+{
+ /* Receive chunk of audio -- called with chan->lock held */
+ unsigned char waste[ZT_CHUNKSIZE];
+
+#ifdef OPTIMIZE_CHANMUTE
+ if(likely(chan->chanmute))
+ return;
+#endif
+ if (!buf) {
+ memset(waste, ZT_LIN2X(0, chan), sizeof(waste));
+ buf = waste;
+ }
+ if ((chan->flags & ZT_FLAG_AUDIO) || (chan->confmode)) {
+#ifdef CONFIG_ZAPTEL_MMX
+ zt_kernel_fpu_begin();
+#endif
+ __zt_process_putaudio_chunk(chan, buf);
+#ifdef CONFIG_ZAPTEL_MMX
+ kernel_fpu_end();
+#endif
+ }
+ __zt_putbuf_chunk(chan, buf);
+}
+
+static inline void __zt_real_receive(struct zt_chan *chan)
+{
+ /* Called with chan->lock held */
+#ifdef OPTIMIZE_CHANMUTE
+ if(likely(chan->chanmute))
+ return;
+#endif
+ if (chan->confmode) {
+ /* Load into queue if we have space */
+ __buf_push(&chan->confin, chan->readchunk, "zt_real_receive");
+ } else {
+ __zt_receive_chunk(chan, chan->readchunk);
+ }
+}
+
+int zt_transmit(struct zt_span *span)
+{
+ int x,y,z;
+ unsigned long flags;
+
+#if 1
+ for (x=0;x<span->channels;x++) {
+ spin_lock_irqsave(&span->chans[x].lock, flags);
+ if (span->chans[x].flags & ZT_FLAG_NOSTDTXRX) {
+ spin_unlock_irqrestore(&span->chans[x].lock, flags);
+ continue;
+ }
+ if (&span->chans[x] == span->chans[x].master) {
+ if (span->chans[x].otimer) {
+ span->chans[x].otimer -= ZT_CHUNKSIZE;
+ if (span->chans[x].otimer <= 0) {
+ __rbs_otimer_expire(&span->chans[x]);
+ }
+ }
+ if (span->chans[x].flags & ZT_FLAG_AUDIO) {
+ __zt_real_transmit(&span->chans[x]);
+ } else {
+ if (span->chans[x].nextslave) {
+ u_char data[ZT_CHUNKSIZE];
+ int pos=ZT_CHUNKSIZE;
+ /* Process master/slaves one way */
+ for (y=0;y<ZT_CHUNKSIZE;y++) {
+ /* Process slaves for this byte too */
+ z = x;
+ do {
+ if (pos==ZT_CHUNKSIZE) {
+ /* Get next chunk */
+ __zt_transmit_chunk(&span->chans[x], data);
+ pos = 0;
+ }
+ span->chans[z].writechunk[y] = data[pos++];
+ z = span->chans[z].nextslave;
+ } while(z);
+ }
+ } else {
+ /* Process independents elsewise */
+ __zt_real_transmit(&span->chans[x]);
+ }
+ }
+ if (span->chans[x].sig == ZT_SIG_DACS_RBS) {
+ if (chans[span->chans[x].confna]) {
+ /* Just set bits for our destination */
+ if (span->chans[x].txsig != chans[span->chans[x].confna]->rxsig) {
+ span->chans[x].txsig = chans[span->chans[x].confna]->rxsig;
+ span->rbsbits(&span->chans[x], chans[span->chans[x].confna]->rxsig);
+ }
+ }
+ }
+
+ }
+ spin_unlock_irqrestore(&span->chans[x].lock, flags);
+ }
+ if (span->mainttimer) {
+ span->mainttimer -= ZT_CHUNKSIZE;
+ if (span->mainttimer <= 0) {
+ span->mainttimer = 0;
+ if (span->maint)
+ span->maint(span, ZT_MAINT_LOOPSTOP);
+ span->maintstat = 0;
+ wake_up_interruptible(&span->maintq);
+ }
+ }
+#endif
+ return 0;
+}
+
+int zt_receive(struct zt_span *span)
+{
+ int x,y,z;
+ unsigned long flags, flagso;
+
+#if 1
+#ifdef CONFIG_ZAPTEL_WATCHDOG
+ span->watchcounter--;
+#endif
+ for (x=0;x<span->channels;x++) {
+ if (span->chans[x].master == &span->chans[x]) {
+ spin_lock_irqsave(&span->chans[x].lock, flags);
+ if (span->chans[x].nextslave) {
+ /* Must process each slave at the same time */
+ u_char data[ZT_CHUNKSIZE];
+ int pos = 0;
+ for (y=0;y<ZT_CHUNKSIZE;y++) {
+ /* Put all its slaves, too */
+ z = x;
+ do {
+ data[pos++] = span->chans[z].readchunk[y];
+ if (pos == ZT_CHUNKSIZE) {
+ if(!(span->chans[x].flags & ZT_FLAG_NOSTDTXRX))
+ __zt_receive_chunk(&span->chans[x], data);
+ pos = 0;
+ }
+ z=span->chans[z].nextslave;
+ } while(z);
+ }
+ } else {
+ /* Process a normal channel */
+ if (!(span->chans[x].flags & ZT_FLAG_NOSTDTXRX))
+ __zt_real_receive(&span->chans[x]);
+ }
+ if (span->chans[x].itimer) {
+ span->chans[x].itimer -= ZT_CHUNKSIZE;
+ if (span->chans[x].itimer <= 0) {
+ rbs_itimer_expire(&span->chans[x]);
+ }
+ }
+ if (span->chans[x].ringdebtimer)
+ span->chans[x].ringdebtimer--;
+ if (span->chans[x].sig & __ZT_SIG_FXS) {
+ if (span->chans[x].rxhooksig == ZT_RXSIG_RING)
+ span->chans[x].ringtrailer = ZT_RINGTRAILER;
+ else if (span->chans[x].ringtrailer) {
+ span->chans[x].ringtrailer-= ZT_CHUNKSIZE;
+ /* See if RING trailer is expired */
+ if (!span->chans[x].ringtrailer && !span->chans[x].ringdebtimer)
+ __qevent(&span->chans[x],ZT_EVENT_RINGOFFHOOK);
+ }
+ }
+ if (span->chans[x].pulsetimer)
+ {
+ span->chans[x].pulsetimer--;
+ if (span->chans[x].pulsetimer <= 0)
+ {
+ if (span->chans[x].pulsecount)
+ {
+ if (span->chans[x].pulsecount > 12) {
+
+ printk("Got pulse digit %d on %s???\n",
+ span->chans[x].pulsecount,
+ span->chans[x].name);
+ } else if (span->chans[x].pulsecount > 11) {
+ __qevent(&span->chans[x], ZT_EVENT_PULSEDIGIT | '#');
+ } else if (span->chans[x].pulsecount > 10) {
+ __qevent(&span->chans[x], ZT_EVENT_PULSEDIGIT | '*');
+ } else if (span->chans[x].pulsecount > 9) {
+ __qevent(&span->chans[x], ZT_EVENT_PULSEDIGIT | '0');
+ } else {
+ __qevent(&span->chans[x], ZT_EVENT_PULSEDIGIT | ('0' +
+ span->chans[x].pulsecount));
+ }
+ span->chans[x].pulsecount = 0;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&span->chans[x].lock, flags);
+ }
+ }
+
+ if (span == master) {
+ /* Hold the big zap lock for the duration of major
+ activities which touch all sorts of channels */
+ spin_lock_irqsave(&bigzaplock, flagso);
+ /* Process any timers */
+ process_timers();
+ /* If we have dynamic stuff, call the ioctl with 0,0 parameters to
+ make it run */
+ if (zt_dynamic_ioctl)
+ zt_dynamic_ioctl(0,0);
+ for (x=1;x<maxchans;x++) {
+ if (chans[x] && chans[x]->confmode && !(chans[x]->flags & ZT_FLAG_PSEUDO)) {
+ u_char *data;
+ spin_lock_irqsave(&chans[x]->lock, flags);
+ data = __buf_peek(&chans[x]->confin);
+ __zt_receive_chunk(chans[x], data);
+ if (data)
+ __buf_pull(&chans[x]->confin, NULL,chans[x], "confreceive");
+ spin_unlock_irqrestore(&chans[x]->lock, flags);
+ }
+ }
+ /* This is the master channel, so make things switch over */
+ rotate_sums();
+ /* do all the pseudo and/or conferenced channel receives (getbuf's) */
+ for (x=1;x<maxchans;x++) {
+ if (chans[x] && (chans[x]->flags & ZT_FLAG_PSEUDO)) {
+ spin_lock_irqsave(&chans[x]->lock, flags);
+ __zt_transmit_chunk(chans[x], NULL);
+ spin_unlock_irqrestore(&chans[x]->lock, flags);
+ }
+ }
+ if (maxlinks) {
+#ifdef CONFIG_ZAPTEL_MMX
+ zt_kernel_fpu_begin();
+#endif
+ /* process all the conf links */
+ for(x = 1; x <= maxlinks; x++) {
+ /* if we have a destination conf */
+ if (((z = confalias[conf_links[x].dst]) > 0) &&
+ ((y = confalias[conf_links[x].src]) > 0)) {
+ ACSS(conf_sums[z], conf_sums[y]);
+ }
+ }
+#ifdef CONFIG_ZAPTEL_MMX
+ kernel_fpu_end();
+#endif
+ }
+ /* do all the pseudo/conferenced channel transmits (putbuf's) */
+ for (x=1;x<maxchans;x++) {
+ if (chans[x] && (chans[x]->flags & ZT_FLAG_PSEUDO)) {
+ unsigned char tmp[ZT_CHUNKSIZE];
+ spin_lock_irqsave(&chans[x]->lock, flags);
+ __zt_getempty(chans[x], tmp);
+ __zt_receive_chunk(chans[x], tmp);
+ spin_unlock_irqrestore(&chans[x]->lock, flags);
+ }
+ }
+ for (x=1;x<maxchans;x++) {
+ if (chans[x] && chans[x]->confmode && !(chans[x]->flags & ZT_FLAG_PSEUDO)) {
+ u_char *data;
+ spin_lock_irqsave(&chans[x]->lock, flags);
+ data = __buf_pushpeek(&chans[x]->confout);
+ __zt_transmit_chunk(chans[x], data);
+ if (data)
+ __buf_push(&chans[x]->confout, NULL, "conftransmit");
+ spin_unlock_irqrestore(&chans[x]->lock, flags);
+ }
+ }
+#ifdef ZAPTEL_SYNC_TICK
+ for (x=0;x<maxspans;x++) {
+ struct zt_span *s = spans[x];
+
+ if (s && s->sync_tick)
+ s->sync_tick(s, s == master);
+ }
+#endif
+ spin_unlock_irqrestore(&bigzaplock, flagso);
+ }
+#endif
+ return 0;
+}
+
+MODULE_AUTHOR("Mark Spencer <markster@digium.com>");
+MODULE_DESCRIPTION("Zapata Telephony Interface");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+#ifdef MODULE_VERSION
+MODULE_VERSION(ZAPTEL_VERSION);
+#endif
+
+#ifdef LINUX26
+module_param(debug, int, 0600);
+module_param(deftaps, int, 0600);
+#else
+MODULE_PARM(debug, "i");
+MODULE_PARM(deftaps, "i");
+#endif
+
+static struct file_operations zt_fops = {
+ owner: THIS_MODULE,
+ llseek: NULL,
+ open: zt_open,
+ release: zt_release,
+ ioctl: zt_ioctl,
+ read: zt_read,
+ write: zt_write,
+ poll: zt_poll,
+ mmap: zt_mmap,
+ flush: NULL,
+ fsync: NULL,
+ fasync: NULL,
+};
+
+#ifdef CONFIG_ZAPTEL_WATCHDOG
+static struct timer_list watchdogtimer;
+
+static void watchdog_check(unsigned long ignored)
+{
+ int x;
+ unsigned long flags;
+ static int wdcheck=0;
+
+ local_irq_save(flags);
+ for (x=0;x<maxspans;x++) {
+ if (spans[x] && (spans[x]->flags & ZT_FLAG_RUNNING)) {
+ if (spans[x]->watchcounter == ZT_WATCHDOG_INIT) {
+ /* Whoops, dead card */
+ if ((spans[x]->watchstate == ZT_WATCHSTATE_OK) ||
+ (spans[x]->watchstate == ZT_WATCHSTATE_UNKNOWN)) {
+ spans[x]->watchstate = ZT_WATCHSTATE_RECOVERING;
+ if (spans[x]->watchdog) {
+ printk("Kicking span %s\n", spans[x]->name);
+ spans[x]->watchdog(spans[x], ZT_WATCHDOG_NOINTS);
+ } else {
+ printk("Span %s is dead with no revival\n", spans[x]->name);
+ spans[x]->watchstate = ZT_WATCHSTATE_FAILED;
+ }
+ }
+ } else {
+ if ((spans[x]->watchstate != ZT_WATCHSTATE_OK) &&
+ (spans[x]->watchstate != ZT_WATCHSTATE_UNKNOWN))
+ printk("Span %s is alive!\n", spans[x]->name);
+ spans[x]->watchstate = ZT_WATCHSTATE_OK;
+ }
+ spans[x]->watchcounter = ZT_WATCHDOG_INIT;
+ }
+ }
+ local_irq_restore(flags);
+ if (!wdcheck) {
+ printk("Zaptel watchdog on duty!\n");
+ wdcheck=1;
+ }
+ mod_timer(&watchdogtimer, jiffies + 2);
+}
+
+static int __init watchdog_init(void)
+{
+ init_timer(&watchdogtimer);
+ watchdogtimer.expires = 0;
+ watchdogtimer.data =0;
+ watchdogtimer.function = watchdog_check;
+ /* Run every couple of jiffy or so */
+ mod_timer(&watchdogtimer, jiffies + 2);
+ return 0;
+}
+
+static void __exit watchdog_cleanup(void)
+{
+ del_timer(&watchdogtimer);
+}
+
+#endif
+
+int zt_register_chardev(struct zt_chardev *dev)
+{
+#ifdef CONFIG_ZAP_UDEV
+ char udevname[strlen(dev->name) + 3];
+
+ strcpy(udevname, "zap");
+ strcat(udevname, dev->name);
+ CLASS_DEV_CREATE(zap_class, MKDEV(ZT_MAJOR, dev->minor), NULL, udevname);
+#endif /* CONFIG_ZAP_UDEV */
+
+#ifdef CONFIG_DEVFS_FS
+ dev->devfs_handle = devfs_register(zaptel_devfs_dir, dev->name, DEVFS_FL_DEFAULT, ZT_MAJOR, dev->minor, mode, &zt_fops, NULL);
+#endif /* CONFIG_DEVFS_FS */
+
+ return 0;
+}
+
+int zt_unregister_chardev(struct zt_chardev *dev)
+{
+#ifdef CONFIG_ZAP_UDEV
+ class_device_destroy(zap_class, MKDEV(ZT_MAJOR, dev->minor));
+#endif /* CONFIG_ZAP_UDEV */
+
+#ifdef CONFIG_DEVFS_FS
+ devfs_unregister(dev->devfs_handle);
+#endif /* CONFIG_DEVFS_FS */
+
+ return 0;
+}
+
+static int __init zt_init(void) {
+ int res = 0;
+
+#ifdef CONFIG_PROC_FS
+ proc_entries[0] = proc_mkdir("zaptel", NULL);
+#endif
+
+#ifdef CONFIG_ZAP_UDEV /* udev support functions */
+ zap_class = class_create(THIS_MODULE, "zaptel");
+ CLASS_DEV_CREATE(zap_class, MKDEV(ZT_MAJOR, 253), NULL, "zaptimer");
+ CLASS_DEV_CREATE(zap_class, MKDEV(ZT_MAJOR, 254), NULL, "zapchannel");
+ CLASS_DEV_CREATE(zap_class, MKDEV(ZT_MAJOR, 255), NULL, "zappseudo");
+ CLASS_DEV_CREATE(zap_class, MKDEV(ZT_MAJOR, 0), NULL, "zapctl");
+#endif /* CONFIG_ZAP_UDEV */
+
+#ifdef CONFIG_DEVFS_FS
+ {
+ umode_t mode = S_IFCHR|S_IRUGO|S_IWUGO;
+
+ devfs_register_chrdev(ZT_MAJOR, "zaptel", &zt_fops);
+ if (!(zaptel_devfs_dir = devfs_mk_dir(NULL, "zap", NULL)))
+ return -EBUSY; /* This would be bad */
+ timer = devfs_register(zaptel_devfs_dir, "timer", DEVFS_FL_DEFAULT, ZT_MAJOR, 253, mode, &zt_fops, NULL);
+ channel = devfs_register(zaptel_devfs_dir, "channel", DEVFS_FL_DEFAULT, ZT_MAJOR, 254, mode, &zt_fops, NULL);
+ pseudo = devfs_register(zaptel_devfs_dir, "pseudo", DEVFS_FL_DEFAULT, ZT_MAJOR, 255, mode, &zt_fops, NULL);
+ ctl = devfs_register(zaptel_devfs_dir, "ctl", DEVFS_FL_DEFAULT, ZT_MAJOR, 0, mode, &zt_fops, NULL);
+ }
+#else
+ if ((res = register_chrdev(ZT_MAJOR, "zaptel", &zt_fops))) {
+ printk(KERN_ERR "Unable to register Zaptel character device handler on %d\n", ZT_MAJOR);
+ return res;
+ }
+#endif /* CONFIG_DEVFS_FS */
+
+ printk(KERN_INFO "Zapata Telephony Interface Registered on major %d\n", ZT_MAJOR);
+ printk(KERN_INFO "Zaptel Version: %s\n", ZAPTEL_VERSION);
+ echo_can_init();
+ zt_conv_init();
+ fasthdlc_precalc();
+ rotate_sums();
+ rwlock_init(&chan_lock);
+#ifdef CONFIG_ZAPTEL_WATCHDOG
+ watchdog_init();
+#endif
+ return res;
+}
+
+static void __exit zt_cleanup(void) {
+ int x;
+
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("zaptel", NULL);
+#endif
+
+ printk(KERN_INFO "Zapata Telephony Interface Unloaded\n");
+ for (x = 0; x < ZT_TONE_ZONE_MAX; x++) {
+ if (tone_zones[x])
+ kfree(tone_zones[x]);
+ }
+
+#ifdef CONFIG_DEVFS_FS
+ devfs_unregister(timer);
+ devfs_unregister(channel);
+ devfs_unregister(pseudo);
+ devfs_unregister(ctl);
+ devfs_unregister(zaptel_devfs_dir);
+ devfs_unregister_chrdev(ZT_MAJOR, "zaptel");
+#else
+#ifdef CONFIG_ZAP_UDEV
+ class_device_destroy(zap_class, MKDEV(ZT_MAJOR, 253)); /* timer */
+ class_device_destroy(zap_class, MKDEV(ZT_MAJOR, 254)); /* channel */
+ class_device_destroy(zap_class, MKDEV(ZT_MAJOR, 255)); /* pseudo */
+ class_device_destroy(zap_class, MKDEV(ZT_MAJOR, 0)); /* ctl */
+ class_destroy(zap_class);
+#endif /* CONFIG_ZAP_UDEV */
+ unregister_chrdev(ZT_MAJOR, "zaptel");
+#endif
+#ifdef CONFIG_ZAPTEL_WATCHDOG
+ watchdog_cleanup();
+#endif
+
+ echo_can_shutdown();
+}
+
+module_init(zt_init);
+module_exit(zt_cleanup);