summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2001-11-05 19:36:16 +0000
committermarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2001-11-05 19:36:16 +0000
commit3e12fb4c61ed406d67695f65f2be7b543ff54454 (patch)
treefe9ca27015318bd3e50f94a4c12fe4b3e1cbf883
parent9edf5e67c421257f00715425aee73e5b8ebfe1a9 (diff)
Version 0.1.0 from FTP
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@21 5390a7c7-147a-4af0-8ec9-7488f05a26cb
-rwxr-xr-xdigits.h88
-rwxr-xr-xzaptel.c3715
2 files changed, 3803 insertions, 0 deletions
diff --git a/digits.h b/digits.h
new file mode 100755
index 0000000..227f4b7
--- /dev/null
+++ b/digits.h
@@ -0,0 +1,88 @@
+/*
+ * Zapata Telephony Telephony
+ *
+ * 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.
+ *
+ * Use DTMF/MFv1 tables
+ */
+#ifndef _DIGITS_H
+#define _DIGITS_H
+
+#include "tones.h"
+
+#define DEFAULT_DTMF_LENGTH 100 * 8
+#define DEFAULT_MFV1_LENGTH 60 * 8
+#define PAUSE_LENGTH 500 * 8
+
+static unsigned char silence[40] =
+{ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f };
+
+/* At the end of silence, the tone stops */
+static struct zt_tone dtmf_silence =
+ { silence, sizeof(silence), DEFAULT_DTMF_LENGTH, NULL };
+
+/* At the end of silence, the tone stops */
+static struct zt_tone mfv1_silence =
+ { silence, sizeof(silence), DEFAULT_MFV1_LENGTH, NULL };
+
+/* A pause in the dialing */
+static struct zt_tone tone_pause =
+ { silence, sizeof(silence), PAUSE_LENGTH, NULL };
+
+static struct zt_tone dtmf_tones[16] =
+{
+ { dtmf_0, sizeof(dtmf_0), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_1, sizeof(dtmf_1), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_2, sizeof(dtmf_2), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_3, sizeof(dtmf_3), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_4, sizeof(dtmf_4), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_5, sizeof(dtmf_5), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_6, sizeof(dtmf_6), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_7, sizeof(dtmf_7), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_8, sizeof(dtmf_8), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_9, sizeof(dtmf_9), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_s, sizeof(dtmf_s), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_p, sizeof(dtmf_p), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_A, sizeof(dtmf_A), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_B, sizeof(dtmf_B), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_C, sizeof(dtmf_C), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+ { dtmf_D, sizeof(dtmf_D), DEFAULT_DTMF_LENGTH, &dtmf_silence },
+};
+
+static struct zt_tone mfv1_tones[15] =
+{
+ { mfv1_0, sizeof(mfv1_0), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+ { mfv1_1, sizeof(mfv1_1), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+ { mfv1_2, sizeof(mfv1_2), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+ { mfv1_3, sizeof(mfv1_3), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+ { mfv1_4, sizeof(mfv1_4), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+ { mfv1_5, sizeof(mfv1_5), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+ { mfv1_6, sizeof(mfv1_6), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+ { mfv1_7, sizeof(mfv1_7), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+ { mfv1_8, sizeof(mfv1_8), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+ { mfv1_9, sizeof(mfv1_9), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+ { mfv1_s, sizeof(mfv1_s), DEFAULT_MFV1_LENGTH * 5 / 3, &mfv1_silence },
+ { mfv1_p, sizeof(mfv1_p), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+ { mfv1_A, sizeof(mfv1_A), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+ { mfv1_B, sizeof(mfv1_B), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+ { mfv1_C, sizeof(mfv1_C), DEFAULT_MFV1_LENGTH, &mfv1_silence },
+};
+
+
+#endif
diff --git a/zaptel.c b/zaptel.c
new file mode 100755
index 0000000..1303290
--- /dev/null
+++ b/zaptel.c
@@ -0,0 +1,3715 @@
+/*
+ * Zapata Telephony Interface Driver
+ *
+ * Written by Mark Spencer <markster@linux-support.net>
+ * Based on previous works, designs, and architectures conceived and
+ * written by Jim Dixon <jim@lambdatel.com>.
+ *
+ * Copyright (C) 2001 Jim Dixon / Zapata Telephony.
+ * Copyright (C) 2001 Linux Support Services, 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 <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#ifdef CONFIG_ZAPATA_NET
+#include <linux/netdevice.h>
+#endif /* CONFIG_ZAPATA_NET */
+#include <linux/ppp_defs.h>
+
+/* Grab fasthdlc with tables */
+#define FAST_HDLC_NEED_TABLES
+#include "fasthdlc.h"
+
+#ifdef STANDALONE_ZAPATA
+#include "zaptel.h"
+#else
+#include <linux/zaptel.h>
+#endif
+
+#define hdlc_to_ztchan(h) ((struct zt_chan *)(h))
+
+/* 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_register);
+EXPORT_SYMBOL(zt_unregister);
+EXPORT_SYMBOL(zt_mulaw);
+EXPORT_SYMBOL(zt_lin2mu);
+EXPORT_SYMBOL(zt_lboname);
+EXPORT_SYMBOL(zt_transmit);
+EXPORT_SYMBOL(zt_receive);
+EXPORT_SYMBOL(zt_rbsbits);
+EXPORT_SYMBOL(zt_qevent);
+EXPORT_SYMBOL(zt_hooksig);
+EXPORT_SYMBOL(zt_alarm_notify);
+
+/* There is a table like this in the PPP driver, too */
+
+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
+};
+
+static int debug;
+
+struct zt_tone {
+ unsigned char *tonedata; /* u-law tone data */
+ int datalen; /* Length of tone data */
+ int tonesamples; /* How long to play this tone before
+ going to the next (in samples) */
+ struct zt_tone *next; /* Next tone in this sequence */
+};
+
+/* 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_t;
+
+
+static struct zt_tone dtmf_tones[16];
+static struct zt_tone mfv1_tones[15];
+
+typedef short sumtype[ZT_MAX_CHUNKSIZE];
+
+static sumtype sums[(ZT_MAX_CONF + 1) * 3];
+
+
+static sumtype *conf_sums_next;
+static sumtype *conf_sums;
+static sumtype *conf_sums_prev;
+
+static struct zt_span *master;
+
+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).
+
+*/
+
+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,(ZT_MAX_CONF + 1) * sizeof(sumtype));
+}
+
+#define DIGIT_MODE_DTMF 0
+#define DIGIT_MODE_MFV1 1
+
+#include "digits.h"
+
+
+struct zt_zone {
+ 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 */
+};
+
+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 default_zone = DEFAULT_TONE_ZONE;
+
+short zt_mulaw[256];
+u_char zt_lin2mu[65536];
+
+u_char defgain[256];
+
+static rwlock_t zone_lock;
+static rwlock_t chan_lock;
+
+static struct zt_zone *tone_zones[ZT_TONE_ZONE_MAX];
+
+#define NUM_SIGS 8
+
+ /* 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_SIG_FXSLS,ZT_BBIT},
+ { ZT_SIG_FXSGS,ZT_ABIT | ZT_BBIT},
+ { ZT_SIG_FXSKS,ZT_BBIT},
+ { ZT_SIG_FXOLS,ZT_BBIT},
+ { ZT_SIG_FXOGS,ZT_BBIT},
+ { ZT_SIG_FXOKS,ZT_BBIT}
+ } ;
+
+ /* if RBS does not apply, return error */
+ if (!(chan->span->flags & ZT_FLAG_RBS) ||
+ !chan->span->rbsbits) return(-1);
+ for (x=0;x<NUM_SIGS;x++) {
+ if (in_sig[x][0] == chan->sig) return(in_sig[x][1]);
+ }
+ return(-1); /* not found -- error */
+}
+
+static int zt_first_empty_conference(void)
+{
+ /* Find first conference to which no one is subscribed */
+ int x,y, inuse;
+ for (y=ZT_MAX_CONF;y>0;y--) {
+ inuse=0;
+ for (x=1;x<ZT_MAX_CHANNELS;x++)
+ if (chans[x] && (chans[x]->confn == y)) {
+ inuse++;
+ break;
+ }
+ if (!inuse)
+ return y;
+ }
+ return -1;
+}
+
+/* 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->eventoutidx == (chan->eventinidx - 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(struct zt_chan *chan, int event)
+{
+ qevent(chan, event);
+}
+
+/* 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 x;
+ unsigned int fcs=PPP_INITFCS;
+ unsigned char *data = ss->writebuf[ss->inwritebuf];
+ int len = ss->writen[ss->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;
+ 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 */
+
+ 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 close_channel(struct zt_chan *chan)
+{
+ unsigned int flags;
+ void *rxgain = NULL;
+ zt_reallocbufs(chan, 0, 0);
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->curtone = NULL;
+ chan->curzone = NULL;
+ chan->cadencepos = 0;
+ zt_hangup(chan);
+ chan->itimer = 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;
+ /* initialize conference variables */
+ chan->confn = 0;
+ chan->confmode = 0;
+ chan->confmute = 0;
+ chan->gotgs = 0;
+
+ if (chan->gainalloc && chan->rxgain)
+ rxgain = chan->rxgain;
+
+ chan->rxgain = defgain;
+ chan->txgain = defgain;
+ chan->gainalloc = 0;
+ chan->eventinidx = chan->eventoutidx = 0;
+
+ memset(chan->conflast, 0, sizeof(chan->conflast));
+ memset(chan->conflast1, 0, sizeof(chan->conflast1));
+ memset(chan->conflast2, 0, sizeof(chan->conflast2));
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ if (rxgain)
+ kfree(rxgain);
+}
+
+static int tone_zone_init(void)
+{
+ int x;
+ static int inited = 0;
+ write_lock(&zone_lock);
+ if (inited) {
+ write_unlock(&zone_lock);
+ return 0;
+ }
+ for (x=0;x<ZT_TONE_ZONE_MAX;x++)
+ tone_zones[x] = NULL;
+ inited++;
+ write_unlock(&zone_lock);
+ return 0;
+}
+
+static int free_tone_zone(int num)
+{
+ struct zt_zone *z;
+ if ((num < 0) || (num >= ZT_TONE_ZONE_MAX))
+ return -EINVAL;
+ write_lock(&zone_lock);
+ z = tone_zones[num];
+ tone_zones[num] = NULL;
+ write_unlock(&zone_lock);
+ 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;
+ if ((tone >= ZT_TONE_MAX) || (tone < -1))
+ return -EINVAL;
+ /* Just wanted to stop the tone anyway */
+ if (tone < 0)
+ return 0;
+ if (chan->curzone) {
+ /* Have a tone zone */
+ if (chan->curzone->tones[tone]) {
+ chan->curtone = chan->curzone->tones[tone];
+ res = 0;
+ } else /* Inidicate that zone is loaded but no such tone exists */
+ res = -ENOSYS;
+ } else /* Note that no tone zone exists at the moment */
+ res = -ENODATA;
+ return res;
+}
+
+static int set_tone_zone(struct zt_chan *chan, int zone)
+{
+ int res=0;
+ /* Assumes channel is already locked */
+ if ((zone >= ZT_TONE_ZONE_MAX) || (zone < -1))
+ return -EINVAL;
+
+ read_lock(&zone_lock);
+ if (zone == -1) {
+ zone = default_zone;
+ }
+ if (tone_zones[zone]) {
+ chan->curzone = tone_zones[zone];
+ chan->tonezone = zone;
+ } else {
+ res = -ENODATA;
+ }
+
+ read_unlock(&zone_lock);
+ return res;
+}
+
+static int zt_chan_reg(struct zt_chan *chan)
+{
+ int x;
+ int res=0;
+ unsigned int flags;
+
+ write_lock_irqsave(&chan_lock, flags);
+ for (x=1;x<ZT_MAX_CHANNELS;x++) {
+ if (!chans[x]) {
+ chans[x] = chan;
+ if (maxchans < x + 1)
+ maxchans = x + 1;
+ chan->channo = x;
+ chan->flags |= ZT_FLAG_REGISTERED;
+ if (!chan->master)
+ chan->master = chan;
+ if (!chan->readchunk)
+ chan->readchunk = chan->sreadchunk;
+ if (!chan->writechunk)
+ chan->writechunk = chan->swritechunk;
+ close_channel(chan);
+ res = 0;
+ break;
+ }
+ }
+ write_unlock_irqrestore(&chan_lock, flags);
+ if (x >= ZT_MAX_CHANNELS)
+ printk(KERN_ERR "No more channels avaialable\n");
+ return res;
+}
+
+char *zt_lboname(int x)
+{
+ if ((x < 0) || ( x > 7))
+ return "Unknown";
+ return zt_txlevelnames[x];
+}
+
+#ifdef CONFIG_ZAPATA_NET
+static int zt_net_open(hdlc_device *hdlc)
+{
+ struct zt_chan *ms = hdlc_to_ztchan(hdlc);
+ int res;
+ 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;
+
+ MOD_INC_USE_COUNT;
+#if 0
+ printk("ZAPNET: Opened channel %d\n", ms->master);
+#endif
+ return 0;
+}
+
+static void zt_net_close(hdlc_device *hdlc)
+{
+ struct zt_chan *ms = hdlc_to_ztchan(hdlc);
+ if (!ms) {
+ printk("zt_net_close: nothing??\n");
+ return;
+ }
+ if (!(ms->flags & ZT_FLAG_NETDEV)) {
+ printk("%s is not a net device!\n", ms->name);
+ return;
+ }
+ /* Not much to do here. Just deallocate the buffers */
+ zt_reallocbufs(ms, 0, 0);
+ MOD_DEC_USE_COUNT;
+ return;
+}
+
+static int zt_xmit(hdlc_device *hdlc, struct sk_buff *skb)
+{
+ struct zt_chan *ss = hdlc_to_ztchan(hdlc);
+ struct net_device *dev = &ss->netdev.netdev;
+ int retval = 1;
+ int x,oldbuf;
+ unsigned int fcs;
+ unsigned char *data;
+ 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);
+ ss->netdev.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;
+ }
+ if (ss->outwritebuf < 0) {
+ /* Let the interrupt handler know there's
+ some space for us */
+ ss->outwritebuf = oldbuf;
+ }
+ dev->trans_start = jiffies;
+#if 0
+ 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(skb);
+ }
+ spin_unlock_irqrestore(&ss->lock, flags);
+ return retval;
+}
+
+static int zt_net_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd)
+{
+ return -EIO;
+}
+
+#endif
+
+
+static void zt_chan_unreg(struct zt_chan *chan)
+{
+ int x;
+ unsigned int flags;
+ write_lock_irqsave(&chan_lock, flags);
+ if (chan->flags & ZT_FLAG_REGISTERED) {
+ chans[chan->channo] = NULL;
+ chan->flags &= ~ZT_FLAG_REGISTERED;
+ }
+#ifdef CONFIG_ZAPATA_NET
+ if (chan->flags & ZT_FLAG_NETDEV)
+ unregister_hdlc_device(&chan->netdev);
+#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]->confn == chan->channo) &&
+ (chans[x]->confmode >= ZT_CONF_MONITOR) &&
+ (chans[x]->confmode <= ZT_CONF_MONITORBOTH)) {
+ /* Take them out of conference with us */
+ chans[x]->confn = -1;
+ chans[x]->confmode = ZT_CONF_NORMAL;
+ }
+ }
+ 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;
+ unsigned int flags;
+ 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;
+ }
+ 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;
+ if (amnt > chan->readn[chan->outreadbuf])
+ amnt = chan->readn[chan->outreadbuf];
+ if (amnt) {
+ if (copy_to_user(usrbuf, chan->readbuf[chan->outreadbuf], amnt))
+ return -EFAULT;
+ }
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->readidx[chan->outreadbuf] = 0;
+ chan->readn[chan->outreadbuf] = 0;
+ oldbuf = chan->outreadbuf;
+ chan->outreadbuf = (chan->outreadbuf + 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 int flags;
+ struct zt_chan *chan = chans[unit];
+ int res, amnt, oldbuf, rv;
+ if (!chan)
+ return -EINVAL;
+ for(;;) {
+ spin_lock_irqsave(&chan->lock, flags);
+ if (chan->curtone) {
+ chan->curtone = NULL;
+ chan->tonep = 0;
+ chan->txdialbuf[0] = '\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 (amnt > chan->blocksize)
+ amnt = chan->blocksize;
+
+#if 0
+ printk("zt_chan_write(unit: %d, inwritebuf: %d, outwritebuf: %d amnt: %d\n",
+ unit, chan->inwritebuf, chan->outwritebuf, amnt);
+#endif
+
+ if (amnt) {
+ copy_from_user(chan->writebuf[chan->inwritebuf], usrbuf, amnt);
+ chan->writen[chan->inwritebuf] = amnt;
+ chan->writeidx[chan->inwritebuf] = 0;
+ if (chan->flags & ZT_FLAG_FCS)
+ calc_fcs(chan);
+ oldbuf = chan->inwritebuf;
+ spin_lock_irqsave(&chan->lock, flags);
+ chan->inwritebuf = (chan->inwritebuf + 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);
+ }
+ return amnt;
+}
+
+static int zt_ctl_open(struct inode *inode, struct file *file)
+{
+ /* Nothing to do, really */
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int zt_chan_open(struct inode *inode, struct file *file)
+{
+ /* Nothing to do here for now either */
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int zt_ctl_release(struct inode *inode, struct file *file)
+{
+ /* Nothing to do */
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int zt_chan_release(struct inode *inode, struct file *file)
+{
+ /* Nothing to do for now */
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static void zt_rbs_sethook(struct zt_chan *chan, int txsig, int txstate, int timeout)
+{
+static int outs[NUM_SIGS][5] = {
+ { ZT_SIG_NONE, 0, 0, 0, 0 }, /* no signalling */
+ { ZT_SIG_EM, 0, ZT_ABIT | ZT_BBIT,ZT_ABIT | ZT_BBIT, 0 }, /* E and M */
+ { ZT_SIG_FXSLS, ZT_BBIT, ZT_ABIT | ZT_BBIT, ZT_ABIT | ZT_BBIT, 0 }, /* FXS Loopstart */
+ { ZT_SIG_FXSGS, ZT_BBIT, ZT_ABIT | ZT_BBIT, ZT_ABIT, 0 }, /* FXS Groundstart */
+ { ZT_SIG_FXSKS, ZT_BBIT, ZT_ABIT | ZT_BBIT, ZT_ABIT | ZT_BBIT, 0 }, /* FXS Kewlstart */
+ { ZT_SIG_FXOLS, ZT_BBIT, ZT_BBIT, 0, 0 }, /* FXO Loopstart */
+ { ZT_SIG_FXOGS, ZT_ABIT | ZT_BBIT, ZT_BBIT, 0, 0 }, /* FXO Groundstart */
+ { ZT_SIG_FXOKS, ZT_BBIT, ZT_BBIT, 0, ZT_ABIT | ZT_BBIT } /* FXO Kewlstart */
+ } ;
+ int x;
+ 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;
+ }
+ chan->txstate = txstate;
+
+ if (chan->span->hooksig) {
+ chan->span->hooksig(chan, txsig);
+ chan->otimer = timeout * 8; /* Otimer is timer in samples */
+ return;
+ } else {
+ for (x=0;x<NUM_SIGS;x++) {
+ if (outs[x][0] == chan->sig) {
+#if 0
+ 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->span->rbsbits(chan, outs[x][txsig+1]);
+ chan->otimer = timeout * 8; /* 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_hangup(struct zt_chan *chan)
+{
+ int res=0;
+ unsigned int flags;
+ /* Can't hangup pseudo channels */
+ if (!chan->span)
+ return 0;
+ /* Can't hang up a clear channel */
+ if (chan->flags & ZT_FLAG_CLEAR)
+ return -EINVAL;
+ chan->kewlonhook = 0;
+ if (chan->span->flags & ZT_FLAG_RBS) {
+ /* Do RBS signalling on the channel's behalf */
+ spin_lock_irqsave(&chan->lock, flags);
+ if ((chan->sig == ZT_SIG_FXOKS) && (chan->txstate != ZT_TXSTATE_ONHOOK)) {
+ zt_rbs_sethook(chan, ZT_TXSIG_KEWL, ZT_TXSTATE_KEWL, ZT_KEWLTIME);
+ } else
+ zt_rbs_sethook(chan, ZT_TXSIG_ONHOOK, ZT_TXSTATE_ONHOOK, 0);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ } else {
+ /* Let the driver hang up the line if it wants to */
+ if (chan->span->sethook)
+ res = chan->span->sethook(chan, ZT_ONHOOK);
+ }
+ return res;
+}
+
+static int initialize_channel(struct zt_chan *chan)
+{
+ int res;
+ unsigned int flags;
+ void *rxgain=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;
+
+ chan->txdisable = 0;
+ chan->rxdisable = 0;
+
+ chan->digitmode = DIGIT_MODE_DTMF;
+ chan->dialing = 0;
+ chan->afterdialingtimer = 0;
+
+ chan->cadencepos = 0;
+
+ /* 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;
+
+ /* Initialize RBS timers */
+ chan->itimer = chan->otimer = 0;
+ chan->echocancel = 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);
+
+ /* I/O Mask, etc */
+ chan->iomask = 0;
+ chan->confn = 0;
+ chan->confmode = 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;
+ set_tone_zone(chan, -1);
+ if (chan->gainalloc && chan->rxgain)
+ rxgain = chan->rxgain;
+ chan->rxgain = defgain;
+ chan->txgain = defgain;
+ chan->gainalloc = 0;
+ chan->eventinidx = chan->eventoutidx = 0;
+ zt_hangup(chan);
+
+ /* Make sure that the audio flag is cleared on a clear channel */
+ if (chan->sig & ZT_SIG_CLEAR)
+ chan->flags &= ~ZT_FLAG_AUDIO;
+
+ spin_unlock_irqrestore(&chan->lock, flags);
+
+ if (rxgain)
+ kfree(rxgain);
+ 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 {
+ /* Assume everything is going to be okay */
+ res = initialize_channel(chans[unit]);
+ 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]->flags |= ZT_FLAG_OPEN;
+ chans[unit]->file = file;
+ if (inc)
+ MOD_INC_USE_COUNT;
+ } else {
+ 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]) {
+ chans[unit]->flags &= ~ZT_FLAG_OPEN;
+ 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;
+ MOD_DEC_USE_COUNT;
+ return res;
+}
+
+static struct zt_chan *zt_alloc_pseudo(void)
+{
+ struct zt_chan *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;
+ if (zt_chan_reg(pseudo)) {
+ kfree(pseudo);
+ return NULL;
+ }
+ sprintf(pseudo->name, "Pseudo/%d", pseudo->channo);
+ return pseudo;
+}
+
+static void zt_free_pseudo(struct zt_chan *pseudo)
+{
+ if (pseudo) {
+ zt_chan_unreg(pseudo);
+ kfree(pseudo);
+ }
+}
+
+static int zt_open(struct inode *inode, struct file *file)
+{
+ int unit = UNIT(file);
+ struct zt_chan *chan;
+ /* Minor 0: Special "control" descriptor */
+ if (!unit)
+ return zt_ctl_open(inode, file);
+ if (unit == 254)
+ return zt_chan_open(inode, file);
+ if (unit == 255) {
+ chan = zt_alloc_pseudo();
+ if (chan) {
+ file->private_data = chan;
+ return zt_specchan_open(inode, file, chan->channo, 1);
+ } else {
+ return -ENXIO;
+ }
+ }
+ return zt_specchan_open(inode, file, unit, 1);
+}
+
+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 == 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 == 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 30 subtones */
+#define MAX_TONES 30
+
+static int
+ioctl_load_zone(unsigned long data)
+{
+ struct zt_tone *samples[MAX_TONES];
+ int next[MAX_TONES];
+ struct zt_tone_def_header th;
+ void *slab, *ptr;
+ long size;
+ struct zt_zone *z;
+ struct zt_tone_def td;
+ struct zt_tone *t;
+ int x;
+ int space;
+ int res;
+ /* XXX Unnecessary XXX */
+ memset(samples, 0, sizeof(samples));
+ /* XXX Unnecessary XXX */
+ memset(next, 0, sizeof(next));
+ copy_from_user(&th, (struct zt_tone_def_header *)data, sizeof(th));
+ if ((th.count < 0) || (th.count > MAX_TONES))
+ return -EINVAL;
+ if ((th.size < 0) || (th.size > MAX_SIZE))
+ return -EINVAL;
+ space = size = sizeof(struct zt_zone) +
+ th.count * sizeof(struct zt_tone) +
+ th.size;
+ if ((size > MAX_SIZE) || (size < 0))
+ return -E2BIG;
+ ptr = slab = (char *)kmalloc(size, GFP_KERNEL);
+ if (!slab)
+ return -ENOMEM;
+ /* Zero it out for simplicity */
+ memset(slab, 0, size);
+ /* Grab the zone */
+ z = (struct zt_zone *)slab;
+ strncpy(z->name, th.name, sizeof(z->name) - 1);
+ for (x=0;x<ZT_MAX_CADENCE;x++)
+ z->ringcadence[x] = th.ringcadence[x];
+ data += sizeof(struct zt_tone_def_header);
+ ptr += sizeof(struct zt_zone);
+ space -= sizeof(struct zt_zone);
+ for (x=0;x<th.count;x++) {
+ if (space < sizeof(struct zt_tone)) {
+ /* Check space for zt_tone struct */
+ kfree(slab);
+ return -EINVAL;
+ }
+ if (copy_from_user(&td, (struct zt_tone_def *)data, sizeof(struct zt_tone_def))) {
+ kfree(slab);
+ return -EIO;
+ }
+ /* Index the current sample */
+ samples[x] = t = (struct zt_tone *)ptr;
+ /* Remember which sample is next */
+ next[x] = td.next;
+ /* Make sure the "next" one is sane */
+ if ((next[x] >= th.count) || (next[x] < 0)) {
+ kfree(slab);
+ return -EINVAL;
+ }
+ if (td.tone >= ZT_TONE_MAX) {
+ /* Make sure it's sane */
+ kfree(slab);
+ return -EINVAL;
+ }
+ /* Update pointers to account for zt_tone header */
+ space -= sizeof(struct zt_tone);
+ ptr += sizeof(struct zt_tone);
+ data += sizeof(struct zt_tone_def);
+ /* Fill in tonedata, datalen, and tonesamples fields */
+ t->tonedata = (unsigned char *)(ptr);
+ t->datalen = td.size;
+ t->tonesamples = td.samples;
+ t->next = NULL; /* XXX Unnecessary XXX */
+ /* Now some checks: Enough space? */
+ if (space < t->datalen) {
+ kfree(slab);
+ return -EINVAL;
+ }
+ /* Put actual tone data */
+ if (copy_from_user(t->tonedata, (unsigned char *)data, t->datalen)) {
+ kfree(slab);
+ return -EIO;
+ }
+ if (!z->tones[td.tone])
+ z->tones[td.tone] = t;
+ /* Update pointers and space */
+ data += t->datalen;
+ ptr += t->datalen;
+ space -= t->datalen;
+ }
+ for (x=0;x<th.count;x++)
+ /* Set "next" pointers */
+ samples[x]->next = samples[next[x]];
+
+ /* Actually register zone */
+ res = zt_register_tone_zone(th.zone, z);
+ if (res)
+ kfree(slab);
+ return res;
+}
+
+static inline void do_dtmf(struct zt_chan *chan)
+{
+ char c;
+ struct zt_tone *z;
+
+ while (strlen(chan->txdialbuf)) {
+ if (chan->digitmode == DIGIT_MODE_DTMF)
+ z = dtmf_tones;
+ else
+ z = mfv1_tones;
+ c = chan->txdialbuf[0];
+ /* Skooch */
+ memmove(chan->txdialbuf, chan->txdialbuf + 1, sizeof(chan->txdialbuf) - 1);
+ switch(c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ chan->curtone = z + (int)(c - '0');
+ chan->tonep = 0;
+ return;
+ case '*':
+ chan->curtone = z + 10;
+ chan->tonep = 0;
+ return;
+ case '#':
+ chan->curtone = z + 11;
+ return;
+ case 'A':
+ case 'B':
+ case 'C':
+ chan->curtone = z + (c + 12 - 'A');
+ chan->tonep = 0;
+ return;
+ case 'D':
+ if (chan->digitmode == DIGIT_MODE_DTMF)
+ chan->curtone = z + ( c + 12 - 'A');
+ chan->tonep = 0;
+ return;
+ case 'a':
+ case 'b':
+ case 'c':
+ chan->curtone = z + (c + 12 - 'a');
+ chan->tonep = 0;
+ return;
+ case 'd':
+ if (chan->digitmode == DIGIT_MODE_DTMF)
+ chan->curtone = z + ( c + 12 - 'a');
+ chan->tonep = 0;
+ return;
+ case 'T':
+ case 't':
+ chan->digitmode = DIGIT_MODE_DTMF;
+ chan->tonep = 0;
+ break;
+ case 'M':
+ case 'm':
+ chan->digitmode = DIGIT_MODE_MFV1;
+ chan->tonep = 0;
+ break;
+ case 'W':
+ case 'w':
+ chan->curtone = &tone_pause;
+ chan->tonep = 0;
+ return;
+ default:
+ }
+ }
+ /* 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 == 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);
+}
+
+void zt_alarm_notify(struct zt_span *span)
+{
+ int j;
+ 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)) {
+ if (span->alarms)
+ j = ZT_EVENT_ALARM;
+ else
+ j = ZT_EVENT_NOALARM;
+ span->lastalarms = span->alarms;
+ for (x=0;x < span->channels;x++)
+ qevent(&span->chans[x], j);
+ }
+}
+
+#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_common_ioctl(struct inode *node, struct file *file, unsigned int cmd, unsigned long data, int unit)
+{
+ struct zt_gains gain;
+ struct zt_spaninfo span;
+ int i,j;
+ switch(cmd) {
+ case ZT_GETGAINS: /* get gain stuff */
+ if (copy_from_user(&gain,(struct zt_gains *) data,sizeof(gain)))
+ return -EIO;
+ i = 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);
+ gain.chan = i; /* put the span # in here */
+ for (j=0;j<256;j++) {
+ gain.txgain[j] = chans[i]->txgain[j];
+ gain.rxgain[j] = chans[i]->rxgain[j];
+ }
+ if (copy_to_user((struct zt_gains *) data,&gain,sizeof(gain)))
+ return -EIO;
+ break;
+ case ZT_SETGAINS: /* set gain stuff */
+ if (copy_from_user(&gain,(struct zt_gains *) data,sizeof(gain)))
+ return -EIO;
+ i = 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);
+ if (!chans[i]->gainalloc) {
+ chans[i]->rxgain = kmalloc(512, GFP_KERNEL);
+ if (!chans[i]->rxgain) {
+ chans[i]->rxgain = defgain;
+ return -ENOMEM;
+ } else {
+ chans[i]->gainalloc = 1;
+ chans[i]->txgain = chans[i]->rxgain + 256;
+ }
+ }
+ gain.chan = i; /* put the span # in here */
+ for (j=0;j<256;j++) {
+ chans[i]->rxgain[j] = gain.rxgain[j];
+ chans[i]->txgain[j] = gain.txgain[j];
+ }
+ if (copy_to_user((struct zt_gains *) data,&gain,sizeof(gain)))
+ return -EIO;
+ break;
+ case ZT_SPANSTAT:
+ copy_from_user(&span,(struct tor_spaninfo *) data,sizeof(span));
+ i = span.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;
+ span.spanno = i; /* put the span # in here */
+ span.totalspans = 0;
+ if (maxspans) span.totalspans = maxspans - 1; /* put total number of spans here */
+ span.alarms = spans[i]->alarms; /* get alarm status */
+ span.bpvcount = spans[i]->bpvcount; /* get BPV count */
+ span.rxlevel = spans[i]->rxlevel; /* get rx level */
+ span.txlevel = spans[i]->txlevel; /* get tx level */
+ span.syncsrc = spans[i]->syncsrc; /* get active sync source */
+ span.numchans = 0;
+ for (j=0; j < spans[i]->channels; j++)
+ if (spans[i]->chans[j].sig)
+ span.numchans++;
+ copy_to_user((struct tor_spaninfo *) data,&span,sizeof(span));
+ break;
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+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;
+ int sigcap;
+ int res = 0;
+ int x,y;
+ struct zt_chan *newmaster;
+ struct zt_dialparams tdp;
+ struct zt_maintinfo maint;
+ unsigned int flags;
+ int rv;
+ switch(cmd) {
+ case ZT_SPANCONFIG:
+ if (copy_from_user(&lc, (struct zt_lineconfig *)data, sizeof(lc)))
+ return -EFAULT;
+ VALID_SPAN(lc.span);
+ if ((lc.lineconfig & 0xf0 & spans[lc.span]->linecompat) != (lc.lineconfig & 0xf0))
+ return -EINVAL;
+ if (spans[lc.span]->spanconfig)
+ 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]);
+ if (y >= 0) spans[j]->chans[x].rxsig = (unsigned char)y;
+ zt_hangup(&spans[j]->chans[x]);
+ 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 {
+ 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 (chans[ch.chan]->netdev.netdev.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;
+ }
+ unregister_hdlc_device(&chans[ch.chan]->netdev);
+ 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);
+
+ 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 (!res) {
+ chans[ch.chan]->sig = ch.sigtype;
+ 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;
+ }
+ chans[ch.chan]->master = newmaster;
+ }
+#ifdef CONFIG_ZAPATA_NET
+ if (!res &&
+ (newmaster == chans[ch.chan]) &&
+ (chans[ch.chan]->sig == ZT_SIG_HDLCNET)) {
+ memset(&chans[ch.chan]->netdev, 0, sizeof(chans[ch.chan]->netdev));
+ chans[ch.chan]->netdev.ioctl = zt_net_ioctl;
+ chans[ch.chan]->netdev.open = zt_net_open;
+ chans[ch.chan]->netdev.close = zt_net_close;
+ chans[ch.chan]->netdev.set_mode = NULL;
+ chans[ch.chan]->netdev.xmit = zt_xmit;
+ chans[ch.chan]->netdev.netdev.irq = chans[ch.chan]->span->irq;
+ chans[ch.chan]->netdev.netdev.tx_queue_len = 50;
+ res = register_hdlc_device(&chans[ch.chan]->netdev);
+ if (!res)
+ chans[ch.chan]->flags |= ZT_FLAG_NETDEV;
+ }
+#endif
+ spin_unlock_irqrestore(&chans[ch.chan]->lock, flags);
+ 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) {
+ /* Copy back any modified settings */
+ if (copy_to_user((struct zt_chanconfig *)data, &ch, sizeof(ch)))
+ return -EFAULT;
+ /* And hangup */
+ zt_hangup(chans[ch.chan]);
+ y = zt_q_sig(chans[ch.chan]);
+ if (y >= 0) chans[ch.chan]->rxsig = (unsigned char)y;
+ chans[ch.chan]->rxhooksig = ZT_RXSIG_INITIAL;
+ }
+#if 0
+ printk("Configured channel %s, flags %04x, sig %04x\n", chans[ch.chan]->name, chans[ch.chan]->flags, chans[ch.chan]->sig);
+#endif
+ return res;
+ case ZT_DEFAULTZONE:
+ if (get_user(j,(int *)data))
+ return -EFAULT; /* get conf # */
+ if ((j < 0) || (j >= ZT_TONE_ZONE_MAX)) return (-EINVAL);
+ write_lock(&zone_lock);
+ default_zone = j;
+ write_unlock(&zone_lock);
+ return 0;
+ case ZT_LOADZONE:
+ return ioctl_load_zone(data);
+ case ZT_FREEZONE:
+ get_user(j,(int *)data); /* get conf # */
+ if ((j < 0) || (j >= ZT_TONE_ZONE_MAX)) return (-EINVAL);
+ free_tone_zone(j);
+ return 0;
+ case ZT_SET_DIALPARAMS:
+ if (copy_from_user(&tdp, (struct zt_dialparams *)data, sizeof(tdp)))
+ return -EIO;
+ if ((tdp.dtmf_tonelen > 4000) || (tdp.dtmf_tonelen < 10))
+ return -EINVAL;
+ if ((tdp.mfv1_tonelen > 4000) || (tdp.mfv1_tonelen < 10))
+ return -EINVAL;
+ for (i=0;i<16;i++)
+ dtmf_tones[i].tonesamples = tdp.dtmf_tonelen * 8;
+ dtmf_silence.tonesamples = tdp.dtmf_tonelen * 8;
+ for (i=0;i<15;i++)
+ mfv1_tones[i].tonesamples = tdp.mfv1_tonelen * 8;
+ mfv1_silence.tonesamples = tdp.mfv1_tonelen * 8;
+ /* Special case for K/P tone */
+ mfv1_tones[10].tonesamples = tdp.mfv1_tonelen * 8 * 5 / 3;
+ break;
+ case ZT_GET_DIALPARAMS:
+ tdp.dtmf_tonelen = dtmf_tones[0].tonesamples / 8;
+ tdp.mfv1_tonelen = mfv1_tones[0].tonesamples / 8;
+ tdp.reserved[0] = 0;
+ tdp.reserved[1] = 0;
+ tdp.reserved[2] = 0;
+ tdp.reserved[3] = 0;
+ if (copy_to_user((struct zt_dialparams *)data, &tdp, sizeof(tdp)))
+ return -EIO;
+ break;
+ case ZT_MAINT: /* do maintence stuff */
+ /* get struct from user */
+ if (copy_from_user(&maint,(struct zt_maintinfo *) data,
+ sizeof(maint))) return -EIO;
+ /* 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;
+ spans[maint.spanno]->maint(spans[maint.spanno], maint.command);
+ break;
+ case ZT_MAINT_LOOPUP:
+ case ZT_MAINT_LOOPDOWN:
+ spans[maint.spanno]->mainttimer = ZT_LOOPCODE_TIME * 8;
+ spans[maint.spanno]->maint(spans[maint.spanno], maint.command);
+ spin_unlock_irqrestore(&spans[maint.spanno]->lock, flags);
+ 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;
+ 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];
+ struct zt_dialoperation tdo;
+ struct zt_bufferinfo bi;
+ struct zt_confinfo conf;
+ unsigned int flags;
+ int i, j, k, rv;
+ int ret, c;
+
+ if (!chan)
+ return -EINVAL;
+ switch(cmd) {
+ case ZT_DIALING:
+ spin_lock_irqsave(&chan->lock, flags);
+ if (copy_to_user((int *)data,&chan->dialing,sizeof(int)))
+ return -EIO;
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return 0;
+ case ZT_DIAL:
+ if (copy_from_user(&tdo, (struct zt_dialoperation *)data, sizeof(tdo)))
+ return -EIO;
+ rv = 0;
+ /* Force proper NULL termination */
+ tdo.dialstr[ZT_MAX_DTMF_BUF - 1] = '\0';
+ spin_lock_irqsave(&chan->lock, flags);
+ switch(tdo.op) {
+ case ZT_DIAL_OP_CANCEL:
+ chan->curtone = NULL;
+ chan->dialing = 0;
+ chan->txdialbuf[0] = '\0';
+ chan->tonep = 0;
+ break;
+ case ZT_DIAL_OP_REPLACE:
+ strcpy(chan->txdialbuf, tdo.dialstr);
+ chan->dialing = 1;
+ do_dtmf(chan);
+ break;
+ case ZT_DIAL_OP_APPEND:
+ if (strlen(tdo.dialstr) + strlen(chan->txdialbuf) >= ZT_MAX_DTMF_BUF)
+ return -EBUSY;
+ strncpy(chan->txdialbuf + strlen(chan->txdialbuf), tdo.dialstr, ZT_MAX_DTMF_BUF - strlen(chan->txdialbuf));
+ 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:
+ bi.rxbufpolicy = chan->rxbufpolicy;
+ bi.txbufpolicy = chan->txbufpolicy;
+ bi.numbufs = chan->numbufs;
+ bi.bufsize = chan->blocksize;
+ /* XXX FIXME! XXX */
+ bi.readbufs = -1;
+ bi.writebufs = -1;
+ if (copy_to_user((struct zt_bufferinfo *)data, &bi, sizeof(bi)))
+ return -EIO;
+ break;
+ case ZT_SET_BUFINFO:
+ if (copy_from_user(&bi, (struct zt_bufferinfo *)data, sizeof(bi)))
+ return -EIO;
+ if (bi.bufsize > ZT_MAX_BLOCKSIZE)
+ return -EINVAL;
+ if (bi.bufsize < 16)
+ return -EINVAL;
+ chan->rxbufpolicy = bi.rxbufpolicy & 0x1;
+ chan->txbufpolicy = bi.txbufpolicy & 0x1;
+ if ((rv = zt_reallocbufs(chan, bi.bufsize, 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 */
+ spin_lock_irqsave(&chan->lock, flags);
+ get_user(i,(int *)data); /* get param */
+ if (i & ZT_FLUSH_READ) /* if for read (input) */
+ {
+ /* initialize read buffers and pointers */
+ chan->inreadbuf = 0;
+ chan->outreadbuf = -1;
+#if 0
+ /* Do we need this? */
+ chan->readn[0] = chan->readn[1] = 0;
+ chan->readidx[0] = chan->readidx[1] = 0;
+#endif
+ 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;
+#if 0
+ /* Do we ned this? */
+ chan->writen[0] = chan->writen[1] = 0;
+ chan->writeidx[0] = chan->writeidx[1] = 0;
+#endif
+ 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_CONFMUTE: /* set confmute flag */
+ if (!(chan->flags & ZT_FLAG_AUDIO)) return (-EINVAL);
+ get_user(j,(int *)data); /* get conf # */
+ chan->confmute = j;
+ break;
+ case ZT_SETTONEZONE:
+ get_user(j,(int *)data);
+ spin_lock_irqsave(&chan->lock, flags);
+ rv = set_tone_zone(chan, j);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return rv;
+ case ZT_GETTONEZONE:
+ spin_lock_irqsave(&chan->lock, flags);
+ if (chan->curzone)
+ rv = chan->tonezone;
+ else
+ rv = default_zone;
+ spin_unlock_irqrestore(&chan->lock, flags);
+ put_user(rv,(int *)data); /* return value */
+ 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 */
+ copy_from_user(&conf,(struct zt_confinfo *) data,sizeof(conf));
+ i = 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);
+ conf.chan = i; /* get channel number */
+ conf.confno = chans[i]->confn; /* get conference number */
+ conf.confmode = chans[i]->confmode; /* get conference mode */
+ copy_to_user((struct zt_confinfo *) data,&conf,sizeof(conf));
+ break;
+ case ZT_SETCONF: /* set conf stuff */
+ copy_from_user(&conf,(struct zt_confinfo *) data,sizeof(conf));
+ i = 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 (conf.confmode && ((conf.confmode & ZT_CONF_MODE_MASK) < 4)) {
+ /* Monitor mode -- it's a channel */
+ if ((conf.confno < 0) || (conf.confno >= ZT_MAX_CHANNELS)) return(-EINVAL);
+ } else {
+ /* make sure conf number makes sense, too */
+ if ((conf.confno < -1) || (conf.confno > ZT_MAX_CONF)) return(-EINVAL);
+ }
+
+ /* if taking off of any conf, must have 0 mode */
+ if ((!conf.confno) && conf.confmode) return(-EINVAL);
+ conf.chan = i; /* return with real channel # */
+ spin_lock_irqsave(&chan->lock, flags);
+ if (conf.confno == -1) {
+ conf.confno = zt_first_empty_conference();
+ if (conf.confno < 1) return -EBUSY;
+ }
+ if ((conf.confno < 1) && (conf.confmode)) {
+ /* No more empty conferences */
+ spin_unlock_irqrestore(&chan->lock, flags);
+ return -EBUSY;
+ }
+ /* if changing confs, clear last added info */
+ if (conf.confno != chans[i]->confn) {
+ memset(chans[i]->conflast, 0, ZT_MAX_CHUNKSIZE);
+ memset(chans[i]->conflast1, 0, ZT_MAX_CHUNKSIZE);
+ memset(chans[i]->conflast2, 0, ZT_MAX_CHUNKSIZE);
+ }
+ chans[i]->confn = conf.confno; /* set conference number */
+ chans[i]->confmode = conf.confmode; /* set conference mode */
+ copy_to_user((struct zt_confinfo *) data,&conf,sizeof(conf));
+ spin_unlock_irqrestore(&chan->lock, flags);
+ break;
+ case ZT_CONFLINK: /* do conf link stuff */
+ if (!(chan->flags & ZT_FLAG_AUDIO)) return (-EINVAL);
+ copy_from_user(&conf,(struct zt_confinfo *) data,sizeof(conf));
+ /* check sanity of arguments */
+ if ((conf.chan < 0) || (conf.chan > ZT_MAX_CONF)) return(-EINVAL);
+ if ((conf.confno < 0) || (conf.confno > ZT_MAX_CONF)) return(-EINVAL);
+ /* cant listen to self!! */
+ if (conf.chan && (conf.chan == conf.confno)) return(-EINVAL);
+ spin_lock_irqsave(&chan->lock, flags);
+ /* if to clear all links */
+ if ((!conf.chan) && (!conf.confno))
+ {
+ /* clear all the links */
+ memset(conf_links,0,sizeof(conf_links));
+ spin_unlock_irqrestore(&chan->lock, flags);
+ 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 == conf.chan) &&
+ (conf_links[i].dst == conf.confno)) break;
+ }
+ if (i <= ZT_MAX_CONF) /* if found */
+ {
+ if (!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 (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 = conf.chan;
+ conf_links[i].dst = conf.confno;
+ }
+ else /* if no empties -- error */
+ {
+ rv = -ENOSPC;
+ }
+ }
+ else /* if to remove, and not found -- error */
+ {
+ rv = -ENOENT;
+ }
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+ 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]->confn != 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;
+ default:
+ /* Check for common ioctl's and private ones */
+ rv = zt_common_ioctl(inode, file, cmd, data, unit);
+ if ((rv == -ENOTTY) && chan->span->ioctl)
+ rv = chan->span->ioctl(chan, cmd, data);
+ return rv;
+
+ }
+ return 0;
+}
+
+static int zt_chan_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data, int unit)
+{
+ struct zt_params param;
+ struct zt_chan *chan = chans[unit];
+ unsigned int flags;
+ int j, rv;
+ int ret;
+
+ if (!chan)
+ return -ENOSYS;
+
+ switch(cmd) {
+ 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) {
+ chan->flags |= ZT_FLAG_AUDIO;
+ chan->flags &= ~(ZT_FLAG_HDLC | ZT_FLAG_FCS);
+ } else {
+ chan->flags &= ~ZT_FLAG_AUDIO;
+ }
+ 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_GET_PARAMS: /* get channel timing parameters */
+ /* point to relevant structure */
+ param.sigtype = chan->sig; /* get signalling type */
+ /* return non-zero if rx not in idle state */
+ j = zt_q_sig(chan);
+ if (j >= 0) /* if returned with success */
+ param.rxisoffhook = (chan->rxsig != (unsigned char)j);
+ else {
+ param.rxisoffhook = ((chan->rxhooksig != ZT_RXSIG_ONHOOK) &&
+ (chan->rxhooksig != ZT_RXSIG_INITIAL));
+ }
+ param.prewinktime = chan->prewinktime;
+ param.preflashtime = chan->preflashtime;
+ param.winktime = chan->winktime;
+ param.flashtime = chan->flashtime;
+ param.starttime = chan->starttime;
+ param.rxwinktime = chan->rxwinktime;
+ param.rxflashtime = chan->rxflashtime;
+ param.debouncetime = chan->debouncetime;
+ param.channo = chan->channo;
+ param.spanno = chan->span->spanno;
+ param.chanpos = chan->chanpos;
+ copy_to_user((struct zt_params *)data,&param,sizeof(param));
+ break;
+ case ZT_SET_PARAMS: /* set channel timing paramters */
+ /* point to relevant structure */
+ copy_from_user(&param,(struct zt_params *)data,sizeof(param));
+ /* NOTE: sigtype is *not* included in this */
+ /* get timing paramters */
+ chan->prewinktime = param.prewinktime;
+ chan->preflashtime = param.preflashtime;
+ chan->winktime = param.winktime;
+ chan->flashtime = param.flashtime;
+ chan->starttime = param.starttime;
+ chan->rxwinktime = param.rxwinktime;
+ chan->rxflashtime = param.rxflashtime;
+ chan->debouncetime = param.debouncetime;
+ break;
+ case ZT_GETEVENT: /* Get event on queue */
+ spin_lock_irqsave(&chan->lock, flags);
+ /* set up for no event */
+ put_user(ZT_EVENT_NONE,(int *)data);
+ /* if some event in queue */
+ if (chan->eventinidx != chan->eventoutidx)
+ {
+ /* get the data, bump index */
+ put_user(chan->eventbuf[chan->eventoutidx++],(int *)data);
+ /* if index overflow, set to beginning */
+ if (chan->eventoutidx >= ZT_MAX_EVENTSIZE)
+ chan->eventoutidx = 0;
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+ break;
+ case ZT_ECHOCANCEL:
+ if (!(chan->flags & ZT_FLAG_AUDIO))
+ return -EINVAL;
+ get_user(j, (int *)data);
+ if (j) {
+ chan->echocancel = 1;
+ init_cc(&chan->ec);
+ } else
+ chan->echocancel = 0;
+ break;
+ case ZT_HOOK:
+ if (chan->flags & ZT_FLAG_CLEAR)
+ return -EINVAL;
+ get_user(j,(int *)data);
+ if (chan->span->flags & ZT_FLAG_RBS) {
+ switch (j) {
+ case ZT_ONHOOK:
+ zt_hangup(chan);
+ 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;
+ if (chan->curzone) {
+ chan->cadencepos = 0;
+ ret = chan->curzone->ringcadence[0];
+ }
+ if (!ret)
+ ret = chan->starttime;
+ 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;
+ rv = schluffen(&chan->txstateq);
+ if (rv) return rv;
+ 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_PREFLASH, 0);
+ spin_unlock_irqrestore(&chan->lock, flags);
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else if (chan->span->sethook)
+ chan->span->sethook(chan, j);
+ else
+ return -ENOSYS;
+ break;
+ 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;
+
+ if (!unit)
+ return zt_ctl_ioctl(inode, file, cmd, data);
+ 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;
+ 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;
+ for (x=0;x<span->channels;x++) {
+ span->chans[x].span = span;
+ zt_chan_reg(&span->chans[x]);
+ }
+ 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;
+ 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);
+ 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]);
+ maxspans = 0;
+ if (master == span)
+ master = NULL;
+ for (x=1;x<ZT_MAX_SPANS;x++) {
+ if (spans[x]) {
+ maxspans = x+1;
+ if (!master)
+ master = spans[x];
+ }
+ }
+ 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
+
+static unsigned char __init linear2ulaw(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
+
+ return(ulawbyte);
+}
+
+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;
+ /* Default (0.0 db) gain table */
+ defgain[i] = i;
+ }
+ /* set up the reverse (mu-law) conversion table */
+ for(i = 0; i < 65536; i++)
+ {
+ zt_lin2mu[i] = linear2ulaw(i - 32768);
+ }
+
+}
+
+static inline unsigned char zt_getbuf_byte(struct zt_chan *ss, int pos)
+{
+ /* We transmit data from our master channel */
+ struct zt_chan *ms = ss->master;
+ /* ulaw-encoded transmit character */
+ unsigned char txc;
+ /* Buffer we're using */
+ unsigned char *buf;
+ /* Old buffer number */
+ int oldbuf;
+ /* Linear representation */
+ int getlin;
+ /* and how about another int */
+ int k;
+
+
+ /* 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. */
+ if ((ms->outwritebuf > -1) && !ms->txdisable) {
+ buf= ms->writebuf[ms->outwritebuf];
+ if (ms->flags & ZT_FLAG_HDLC) {
+ /* If this is an HDLC channel we only send a byte of
+ HDLC. */
+ if (ms->txhdlc.bits < 8)
+ /* Load a byte of data only if needed */
+ fasthdlc_tx_load_nocheck(&ms->txhdlc, buf[ms->writeidx[ms->outwritebuf]++]);
+ txc = fasthdlc_tx_run_nocheck(&ms->txhdlc);
+ } else {
+ txc = buf[ms->writeidx[ms->outwritebuf]++];
+ }
+ /* 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)) {
+ 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(&ms->netdev.netdev);
+#endif
+ }
+ } else if (ms->curtone) {
+ /* Pick our default value from the next sample of the current tone */
+ txc = ms->curtone->tonedata[ms->tonep++ % ms->curtone->datalen];
+ if (ms->tonep >= ms->curtone->tonesamples) {
+ /* Go to the next sample of the tone */
+ ms->tonep = 0;
+ 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 (ms->flags & ZT_FLAG_HDLC) {
+ /* Okay, if we're HDLC, then transmit a flag by default */
+ if (ms->txhdlc.bits < 8)
+ fasthdlc_tx_frame_nocheck(&ms->txhdlc);
+ txc = fasthdlc_tx_run_nocheck(&ms->txhdlc);
+ } else txc = 0x7f; /* Lastly we use 0x7f on telephony channels */
+
+ /* Okay, now we've got something to transmit */
+ getlin = zt_mulaw[txc];
+ if ((ms->flags & ZT_FLAG_AUDIO) && !ms->confmute && !ms->dialing) {
+ /* Handle conferencing on non-clear channel and non-HDLC channels */
+ switch(ms->confmode & ZT_CONF_MODE_MASK) {
+ case ZT_CONF_NORMAL:
+ /* Do nuffin */
+ txc = zt_lin2mu[getlin + 32768];
+ break;
+ case ZT_CONF_MONITOR: /* Monitor a channel's rx mode */
+ /* Add monitored channel */
+ if (chans[ms->confn]->flags & ZT_FLAG_PSEUDO) {
+ getlin += chans[ms->confn]->getlin[pos];
+ } else {
+ getlin += chans[ms->confn]->putlin[pos];
+ }
+ /* Clip */
+ if (getlin > 32767) getlin = 32767;
+ if (getlin < -32767) getlin = 32767;
+ txc = zt_lin2mu[getlin + 32768];
+ break;
+ case ZT_CONF_MONITORTX: /* Monitor a channel's tx mode */
+ /* Add monitored channel */
+ if (chans[ms->confn]->flags & ZT_FLAG_PSEUDO) {
+ getlin += chans[ms->confn]->putlin[pos];
+ } else {
+ getlin += chans[ms->confn]->getlin[pos];
+ }
+ /* Clip */
+ if (getlin > 32767) getlin = 32767;
+ if (getlin < -32767) getlin = 32767;
+ txc = zt_lin2mu[getlin + 32768];
+ break;
+ case ZT_CONF_MONITORBOTH: /* monitor a channel's rx and tx mode */
+ getlin += chans[ms->confn]->putlin[pos] +
+ chans[ms->confn]->getlin[pos];
+ /* Clip */
+ if (getlin > 32767) getlin = 32767;
+ if (getlin < -32767) getlin = 32767;
+ txc = zt_lin2mu[getlin + 32768];
+ 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 */
+ k = getlin;
+ /* Add conf value */
+ k += (int)conf_sums_next[ms->confn][pos];
+ if (k > 32767) k = 32767;
+ if (k < -32767) k = -32767;
+ /* save last one */
+ ms->conflast2[pos] = ms->conflast1[pos];
+ /* get amount actually added */
+ ms->conflast1[pos] = ((short)k) -
+ conf_sums_next[ms->confn][pos];
+ /* Really add in new value */
+ conf_sums_next[ms->confn][pos] += ms->conflast1[pos];
+ } else {
+ ms->conflast1[pos] = 0;
+ ms->conflast2[pos] = 0;
+ }
+ getlin = 0;
+ txc = 0;
+ /* 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 */
+ k = getlin;
+ /* Add conf value */
+ k += (int)conf_sums[ms->confn][pos];
+ if (k > 32767) k = 32767;
+ if (k < -32767) k = -32767;
+ /* get amount actually added */
+ ms->conflast[pos] = ((short)k) -
+ conf_sums[ms->confn][pos];
+ /* Really add in new value */
+ conf_sums[ms->confn][pos] += ms->conflast[pos];
+ } else ms->conflast[pos] = 0;
+ getlin = ms->getlin[pos];
+ txc = 0;
+ break;
+ }
+ /* fall through */
+ case ZT_CONF_CONFMON: /* Conference monitor mode */
+ if (ms->confmode & ZT_CONF_LISTENER) {
+ /* Subtract out last sample written to conf */
+ getlin -= ms->conflast[pos];
+ /* Add in conference */
+ getlin += conf_sums[ms->confn][pos];
+ }
+ /* Clip */
+ if (getlin > 32767) getlin = 32767;
+ if (getlin < -32767) getlin = 32767;
+ txc = zt_lin2mu[getlin + 32768];
+ break;
+ case ZT_CONF_CONFANN:
+ case ZT_CONF_CONFANNMON:
+ /* First, add tx buffer to conf */
+ getlin += conf_sums_next[ms->confn][pos];
+ /* Clip */
+ if (getlin > 32767) getlin = 32767;
+ if (getlin < -32767) getlin = -32767;
+ conf_sums_next[ms->confn][pos] = (short)getlin;
+ /* Start with silence */
+ getlin = 0;
+ /* If a listener on the conf... */
+ if (ms->confmode & ZT_CONF_LISTENER) {
+ /* Subtract last value written */
+ getlin -= ms->conflast[pos];
+ /* Add in conf */
+ getlin += conf_sums[ms->confn][pos];
+ }
+ /* Clip */
+ if (getlin > 32767) getlin = 32767;
+ if (getlin < -32767) getlin = -32767;
+ txc = zt_lin2mu[getlin + 32768];
+ break;
+ }
+ }
+ if (ms->confmute) txc = 0x7f;
+ /* save value from last chunk */
+ ms->getlin_lastchunk[pos] = ms->getlin[pos];
+ /* save value from current */
+ ms->getlin[pos] = getlin;
+ /* This is what to send */
+ return txc;
+}
+
+static inline void rbs_itimer_expire(struct zt_chan *chan)
+{
+ /* the only way this could have gotten here, is if a channel
+ went off hook longer then the wink or flash detect timeout */
+
+ 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;
+ 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;
+
+ 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 */
+ if (chan->curzone) {
+ ++chan->cadencepos;
+ if (chan->cadencepos >= ZT_MAX_CADENCE)
+ chan->cadencepos = 0;
+ len = chan->curzone->ringcadence[chan->cadencepos];
+
+ if (!len) {
+ chan->cadencepos = 0;
+ len = chan->curzone->ringcadence[chan->cadencepos];
+ }
+
+ } else
+ len = chan->starttime;
+ 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 */
+ if (chan->curzone) {
+ ++chan->cadencepos;
+ if (chan->cadencepos >= ZT_MAX_CADENCE)
+ chan->cadencepos = 0;
+ len = chan->curzone->ringcadence[chan->cadencepos];
+
+ if (!len) {
+ chan->cadencepos = 0;
+ len = chan->curzone->ringcadence[chan->cadencepos];
+ }
+
+ } else
+ len = ZT_RINGOFFTIME;
+ 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);
+ 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;
+
+ default:
+ break;
+ }
+}
+
+static void zt_hooksig_pvt(struct zt_chan *chan, zt_rxsig_t rxsig)
+{
+
+ chan->rxhooksig = rxsig;
+ switch(chan->sig) {
+ case ZT_SIG_EM: /* E and M */
+ switch(rxsig) {
+ case ZT_RXSIG_OFFHOOK: /* went off hook */
+ /* set wink timer */
+ chan->itimer = chan->rxwinktime * 8;
+ break;
+ case ZT_RXSIG_ONHOOK: /* went on hook */
+ if (chan->itimer)
+ qevent(chan,ZT_EVENT_WINKFLASH);
+ else {
+ qevent(chan,ZT_EVENT_ONHOOK);
+ chan->gotgs = 0;
+ }
+ chan->itimer = 0;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ZT_SIG_FXSLS: /* FXS loopstart */
+ if (rxsig == ZT_RXSIG_RING) {
+ qevent(chan,ZT_EVENT_RINGOFFHOOK);
+ }
+ break;
+ case ZT_SIG_FXSKS: /* FXS Kewlstart */
+ if (rxsig == ZT_RXSIG_RING) {
+ qevent(chan,ZT_EVENT_RINGOFFHOOK);
+ break;
+ }
+ /* ignore a bit poopy if loop no closed and stable */
+ if (chan->txstate != ZT_TXSTATE_OFFHOOK) break;
+ /* fall through intentionally */
+ case ZT_SIG_FXSGS: /* FXS Groundstart */
+ switch(rxsig) {
+ case ZT_RXSIG_RING:
+ qevent(chan,ZT_EVENT_RINGOFFHOOK);
+ break;
+ case ZT_RXSIG_ONHOOK: /* went on hook */
+ /* if not during offhook debounce time */
+ if (chan->txstate != ZT_TXSTATE_DEBOUNCE) {
+ chan->gotgs = 0;
+ qevent(chan,ZT_EVENT_ONHOOK);
+ }
+ break;
+ default:
+ break;
+ }
+ 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 asserting 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;
+#if 0
+ 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 */
+ qevent(chan,ZT_EVENT_WINKFLASH);
+ } else {
+ /* if havent got GS detect */
+ if (!chan->gotgs) {
+ qevent(chan,ZT_EVENT_RINGOFFHOOK);
+ chan->gotgs = 1;
+ chan->itimer = 0;
+ }
+ }
+ 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->itimer = chan->rxflashtime * 8;
+ }
+ 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 */
+ if ((chan->rxhooksig) == rxsig) return;
+ zt_hooksig_pvt(chan,rxsig);
+}
+
+void zt_rbsbits(struct zt_chan *chan, int cursig)
+{
+ /* if A bit has changed */
+ if ((chan->rxsig & ZT_ABIT) != (cursig & ZT_ABIT)) {
+ switch(chan->sig) {
+ case ZT_SIG_EM: /* E and M */
+ case ZT_SIG_FXOLS: /* FXO Loopstart */
+ case ZT_SIG_FXOGS: /* FXO Groundstart */
+ case ZT_SIG_FXOKS: /* FXO Kewlstart */
+ if (cursig & ZT_ABIT) /* went off hook */
+ zt_hooksig_pvt(chan,ZT_RXSIG_OFFHOOK);
+ else /* went on hook */
+ zt_hooksig_pvt(chan,ZT_RXSIG_ONHOOK);
+ break;
+ case ZT_SIG_FXSKS: /* FXS Kewlstart */
+ /* ignore a bit poopy if loop no closed and stable */
+ if (chan->txstate != ZT_TXSTATE_OFFHOOK) break;
+ /* fall through intentionally */
+ case ZT_SIG_FXSGS: /* FXS Groundstart */
+ if (cursig & ZT_ABIT) /* if went on hook */
+ zt_hooksig_pvt(chan,ZT_RXSIG_ONHOOK);
+ break;
+ default:
+ break;
+ }
+ }
+ /* if B bit has changed */
+ if ((chan->rxsig & ZT_BBIT) != (cursig & ZT_BBIT)) {
+ switch(chan->sig) {
+ case ZT_SIG_FXSLS: /* FXS Loopstart */
+ case ZT_SIG_FXSGS: /* FXS Groundstart */
+ case ZT_SIG_FXSKS: /* FXS Kewlstart */
+ if (cursig & ZT_BBIT) /* if trailing edge of ring */
+ zt_hooksig_pvt(chan,ZT_RXSIG_RING);
+ break;
+ case ZT_SIG_FXOGS: /* FXO Groundstart */
+ if (!(cursig & ZT_BBIT)) /* if ground detected */
+ zt_hooksig_pvt(chan,ZT_RXSIG_START);
+ break;
+ default:
+ break;
+ }
+ }
+ /* Keep track of signalling for next time */
+ chan->rxsig = cursig;
+}
+
+#if 0
+
+void zt_rbsbits(struct zt_chan *chan, int cursig)
+{
+ /* if A bit has changed */
+ if ((chan->rxsig & ZT_ABIT) != (cursig & ZT_ABIT))
+ {
+ switch(chan->sig)
+ {
+ case ZT_SIG_EM: /* E and M */
+ if (cursig & ZT_ABIT) /* went off hook */
+ {
+ /* set wink timer */
+ chan->itimer = chan->rxwinktime * 8;
+ }
+ else /* went on hook */
+ {
+ if (chan->itimer)
+ qevent(chan,ZT_EVENT_WINKFLASH);
+ else
+ {
+ qevent(chan,ZT_EVENT_ONHOOK);
+ chan->gotgs = 0;
+ }
+ chan->itimer = 0;
+ }
+ break;
+ case ZT_SIG_FXSKS: /* FXS Kewlstart */
+ /* ignore a bit poopy if loop no closed and stable */
+ if (chan->txstate != ZT_TXSTATE_OFFHOOK) break;
+ /* fall through intentionally */
+ case ZT_SIG_FXSGS: /* FXS Groundstart */
+ if (cursig & ZT_ABIT) /* if went on hook */
+ {
+ /* if not during offhook debounce time */
+ if (chan->txstate != ZT_TXSTATE_DEBOUNCE)
+ {
+ chan->gotgs = 0;
+ qevent(chan,ZT_EVENT_ONHOOK);
+ }
+ }
+ break;
+ case ZT_SIG_FXOLS: /* FXO Loopstart */
+ case ZT_SIG_FXOGS: /* FXO Groundstart */
+ case ZT_SIG_FXOKS: /* FXO Kewlstart */
+ if (cursig & ZT_ABIT) /* if went off hook */
+ {
+ /* if asserting ring, stop it */
+ if (chan->txstate == ZT_TXSTATE_START)
+ {
+ zt_rbs_sethook(chan,ZT_TXSIG_OFFHOOK, ZT_TXSTATE_AFTERSTART, ZT_AFTERSTART_TIME);
+ }
+ if (chan->itimer) /* if timer still running */
+ qevent(chan,ZT_EVENT_WINKFLASH);
+ else
+ {
+ /* if havent got GS detect */
+ if (!chan->gotgs)
+ {
+ qevent(chan,ZT_EVENT_RINGOFFHOOK);
+ chan->gotgs = 1;
+ chan->itimer = 0;
+ }
+ }
+ chan->itimer = 0;
+ }
+ if (!(cursig & ZT_ABIT)) /* if 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->itimer = chan->rxflashtime * 8;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ /* if B bit has changed */
+ if ((chan->rxsig & ZT_BBIT) != (cursig & ZT_BBIT))
+ {
+ switch(chan->sig)
+ {
+ case ZT_SIG_FXSLS: /* FXS Loopstart */
+ case ZT_SIG_FXSGS: /* FXS Groundstart */
+ case ZT_SIG_FXSKS: /* FXS Kewlstart */
+ if (cursig & ZT_BBIT) /* if trailing edge of ring */
+ qevent(chan,ZT_EVENT_RINGOFFHOOK);
+ break;
+ case ZT_SIG_FXOGS: /* FXO Groundstart */
+ if (!(cursig & ZT_BBIT)) /* if ground detected */
+ {
+ /* if havent got gs, report it */
+ if (!chan->gotgs)
+ {
+ qevent(chan,ZT_EVENT_RINGOFFHOOK);
+ chan->gotgs = 1;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ /* Keep track of signalling for next time */
+ chan->rxsig = cursig;
+}
+
+#endif
+
+static inline void zt_putbuf_byte(struct zt_chan *ss, unsigned char rxc, int pos)
+{
+ /* We transmit data from our master channel */
+ struct zt_chan *ms = ss->master;
+ /* Our receive buffer */
+ unsigned char *buf;
+ /* Linear version of received data */
+ int putlin,k;
+#ifdef CONFIG_ZAPATA_NET
+ /* SKB for receiving network stuff */
+ struct sk_buff *skb=NULL;
+#endif
+ int oldbuf;
+ int eof=0;
+ int abort=0;
+ int res;
+
+ if (ms->dialing) ms->afterdialingtimer = ZT_CHUNKSIZE * 50;
+ else if (ms->afterdialingtimer) ms->afterdialingtimer--;
+ if (ms->afterdialingtimer) rxc = 0x7f; /* receive as silence if dialing */
+ if (ms->echocancel) {
+ putlin = zt_mulaw[rxc];
+ putlin = process_cc(&ms->ec, ms->getlin_lastchunk[pos],
+ abs(ms->getlin_lastchunk[pos]), putlin, abs(putlin));
+ rxc = zt_lin2mu[(int)putlin + 32768];
+ }
+
+ /* Apply gain if appropriate */
+ if (ms->flags & ZT_FLAG_AUDIO)
+ rxc = ms->rxgain[rxc];
+ putlin = (int) zt_mulaw[rxc];
+
+ if (!(ms->flags & ZT_FLAG_PSEUDO))
+ ms->putlin[pos] = putlin;
+
+ /* Take the rxc, twiddle it for conferencing if appropriate and put it
+ back */
+ if ((ms->flags & ZT_FLAG_AUDIO) && !ms->confmute && !ms->afterdialingtimer) {
+ 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->confn]->flags & ZT_FLAG_PSEUDO) {
+ putlin += chans[ms->confn]->getlin[pos];
+ } else {
+ putlin += chans[ms->confn]->putlin[pos];
+ }
+ /* Clip */
+ if (putlin > 32767) putlin = 32767;
+ if (putlin < -32767) putlin = 32767;
+ rxc = zt_lin2mu[putlin + 32768];
+ 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->confn]->flags & ZT_FLAG_PSEUDO) {
+ putlin += chans[ms->confn]->putlin[pos];
+ } else {
+ putlin += chans[ms->confn]->getlin[pos];
+ }
+ /* Clip */
+ if (putlin > 32767) putlin = 32767;
+ if (putlin < -32767) putlin = 32767;
+ rxc = zt_lin2mu[putlin + 32768];
+ 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;
+ putlin += chans[ms->confn]->putlin[pos] +
+ chans[ms->confn]->getlin[pos];
+ /* Clip */
+ if (putlin > 32767) putlin = 32767;
+ if (putlin < -32767) putlin = 32767;
+ rxc = zt_lin2mu[putlin + 32768];
+ break;
+ case ZT_CONF_REALANDPSEUDO:
+ /* do normal conf mode processing */
+ if (ms->confmode & ZT_CONF_TALKER) {
+ /* Store temp value */
+ k = putlin;
+ /* Add conf value */
+ k += (int)conf_sums_next[ms->confn][pos];
+ if (k > 32767)
+ k = 32767;
+ if (k < -32767)
+ k = -32767;
+ /* get amount actually added */
+ ms->conflast[pos] = ((short)k) -
+ conf_sums_next[ms->confn][pos];
+ /* Really add in new value */
+ conf_sums_next[ms->confn][pos] += ms->conflast[pos];
+ } else ms->conflast[pos] = 0;
+ /* do the pseudo-channel part processing */
+ putlin = 0;
+ if (ms->confmode & ZT_CONF_PSEUDO_LISTENER) {
+ /* Subtract out previous last sample written to conf */
+ putlin -= ms->conflast2[pos];
+ /* Add in conference */
+ putlin += conf_sums[ms->confn][pos];
+ }
+ /* Clip */
+ if (putlin > 32767) putlin = 32767;
+ if (putlin < -32767) putlin = 32767;
+ rxc = zt_lin2mu[putlin + 32768];
+ 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 */
+ putlin -= ms->conflast[pos];
+ /* Add in conference */
+ putlin += conf_sums[ms->confn][pos];
+ }
+ /* Clip */
+ if (putlin > 32767) putlin = 32767;
+ if (putlin < -32767) putlin = 32767;
+ rxc = zt_lin2mu[putlin + 32768];
+ ss->getlin[pos] = zt_mulaw[rxc];
+ break;
+ }
+ /* fall through */
+ case ZT_CONF_CONFANN: /* Conference with announce */
+ if (ms->confmode & ZT_CONF_TALKER) {
+ /* Store temp value */
+ k = putlin;
+ /* Add conf value */
+ k += (int)conf_sums_next[ms->confn][pos];
+ if (k > 32767)
+ k = 32767;
+ if (k < -32767)
+ k = -32767;
+ /* get amount actually added */
+ ms->conflast[pos] = ((short)k) -
+ conf_sums_next[ms->confn][pos];
+ /* Really add in new value */
+ conf_sums_next[ms->confn][pos] += ms->conflast[pos];
+ } else ms->conflast[pos] = 0;
+ /* rxc unmodified */
+ break;
+ case ZT_CONF_CONFMON:
+ case ZT_CONF_CONFANNMON:
+ if (ms->confmode & ZT_CONF_TALKER) {
+ /* Store temp value */
+ k = putlin;
+ /* Subtract last value */
+ conf_sums[ms->confn][pos] -= ms->conflast[pos];
+ /* Add conf value */
+ k += (int)conf_sums[ms->confn][pos];
+ if (k > 32767)
+ k = 32767;
+ if (k < -32767)
+ k = -32767;
+ /* get amount actually added */
+ ms->conflast[pos] = ((short)k) -
+ conf_sums[ms->confn][pos];
+ /* Really add in new value */
+ conf_sums[ms->confn][pos] += ms->conflast[pos];
+ } else ms->conflast[pos] = 0;
+ rxc = zt_lin2mu[(int)conf_sums_prev[ms->confn][pos] + 32768];
+ break;
+ }
+ }
+
+ /* 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];
+ if (ms->flags & ZT_FLAG_HDLC) {
+ /* Handle HDLC deframing */
+ fasthdlc_rx_load_nocheck(&ms->rxhdlc, rxc);
+ res = fasthdlc_rx_run(&ms->rxhdlc);
+ /* Return immediately if there is nothing to receive */
+ if (res & RETURN_EMPTY_FLAG)
+ return;
+ 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;
+ }
+ } else if (res & RETURN_DISCARD_FLAG) {
+ /* This could be someone idling with
+ "idle" instead of "flag" */
+ if (!ms->readidx[ms->inreadbuf])
+ return;
+ abort = ZT_EVENT_ABORT;
+ } else {
+ 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;
+ }
+ }
+ } else {
+ buf[ms->readidx[ms->inreadbuf]++] = rxc;
+ /* End of frame is decided by block size of 'N' */
+ eof = (ms->readidx[ms->inreadbuf] >= ms->blocksize);
+ }
+ if (eof) {
+ /* Finished with this buffer, try another. */
+ oldbuf = ms->inreadbuf;
+ ms->infcs = PPP_INITFCS;
+ ms->readn[ms->inreadbuf] = ms->readidx[ms->inreadbuf];
+#if 0
+ printk("EOF, len is %d\n", ms->readn[ms->inreadbuf]);
+#endif
+#ifdef CONFIG_ZAPATA_NET
+ if (ms->flags & ZT_FLAG_NETDEV) {
+ /* Our network receiver logic is MUCH
+ different. We actually only us a single
+ buffer */
+ if (ms->readn[ms->inreadbuf] > 1) {
+ /* Drop the FCS */
+ ms->readn[ms->inreadbuf] -= 2;
+ /* Allocate an SKB */
+ 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]);
+ } else {
+ ms->netdev.stats.rx_dropped++;
+#if 1
+ 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 */
+#if 0
+ 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 */
+#if 0
+ 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) {
+ ms->netdev.stats.rx_errors++;
+ if (abort == ZT_EVENT_OVERRUN)
+ ms->netdev.stats.rx_over_errors++;
+ if (abort == ZT_EVENT_BADFCS)
+ ms->netdev.stats.rx_crc_errors++;
+ if (abort == ZT_EVENT_ABORT)
+ ms->netdev.stats.rx_frame_errors++;
+ } 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
+
+ }
+ }
+#ifdef CONFIG_ZAPATA_NET
+ if (skb)
+ hdlc_netif_rx(&ms->netdev, skb);
+#endif
+}
+
+
+/* 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;
+ 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 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 == 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);
+}
+
+int zt_transmit(struct zt_span *span)
+{
+ int x,y;
+ unsigned long flags;
+ for (x=0;x<span->channels;x++) {
+ spin_lock_irqsave(&span->chans[x].lock, flags);
+ 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) {
+ for (y=0;y<ZT_CHUNKSIZE;y++) {
+ span->chans[x].writechunk[y] = span->chans[x].txgain[zt_getbuf_byte(&span->chans[x], y)];
+ }
+ } else {
+ for (y=0;y<ZT_CHUNKSIZE;y++) {
+ span->chans[x].writechunk[y] = zt_getbuf_byte(&span->chans[x], y);
+ }
+ }
+ 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);
+ wake_up_interruptible(&span->maintq);
+ }
+ }
+ return 0;
+}
+
+int zt_receive(struct zt_span *span)
+{
+ int x,y,z,lin;
+ unsigned long flags;
+ for (x=0;x<span->channels;x++) {
+ spin_lock_irqsave(&span->chans[x].lock, flags);
+ for (y=0;y<ZT_CHUNKSIZE;y++) {
+ zt_putbuf_byte(&span->chans[x], span->chans[x].readchunk[y], y);
+ }
+ if (span->chans[x].itimer) {
+ span->chans[x].itimer -= ZT_CHUNKSIZE;
+ if (span->chans[x].itimer <= 0) {
+ rbs_itimer_expire(&span->chans[x]);
+ }
+ }
+ spin_unlock_irqrestore(&span->chans[x].lock, flags);
+ }
+ if (span == master) {
+ /* This is the master channel, so make things switch over */
+ rotate_sums();
+ /* do all the pseudo channel receives (getbuf's) */
+ for (x=1;x<ZT_MAX_CHANNELS;x++) {
+ if (chans[x] && (chans[x]->flags & ZT_FLAG_PSEUDO)) {
+ for (y=0;y<ZT_CHUNKSIZE;y++) {
+ (void) zt_getbuf_byte(chans[x], y);
+ }
+ }
+ }
+ /* process all the conf links */
+ for(x = 1; x <= ZT_MAX_CONF; x++) {
+ /* if we have a destination conf */
+ if ((z = conf_links[x].dst)) {
+ /* go thru whole chunk. Do it chunky style! */
+ for(y = 0; y < ZT_CHUNKSIZE; y++) {
+ /* get sum of us and them */
+ lin = conf_sums[conf_links[x].src][y] + conf_sums[z][y];
+ /* clip it..... clip it good.. da da da da da.. da da da da */
+ if (lin > 32767) lin = 32767;
+ if (lin < -32767) lin = -32767;
+ /* put it back into accumulator */
+ conf_sums[z][y] = lin;
+ }
+ }
+ }
+ /* do all the pseudo channel transmits (putbuf's) */
+ for (x=1;x<ZT_MAX_CHANNELS;x++) {
+ if (chans[x] && (chans[x]->flags & ZT_FLAG_PSEUDO)) {
+ for (y=0;y<ZT_CHUNKSIZE;y++) {
+ zt_putbuf_byte(chans[x], 0x7f, y);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+MODULE_AUTHOR("Mark Spencer <markster@linux-support.net>");
+MODULE_DESCRIPTION("Zapata Telephony Interface");
+MODULE_PARM(debug, "i");
+
+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: NULL,
+ flush: NULL,
+ fsync: NULL,
+ fasync: NULL,
+};
+
+static int __init zt_init(void) {
+ int res;
+ if ((res = register_chrdev(ZT_MAJOR, "zaptel", &zt_fops))) {
+ printk(KERN_ERR "Unable to register tor device on %d\n", ZT_MAJOR);
+ return res;
+ }
+ printk(KERN_INFO "Zapata Telephony Interface Registered on major %d\n", ZT_MAJOR);
+ zt_conv_init();
+ tone_zone_init();
+ fasthdlc_precalc();
+ rotate_sums();
+ return res;
+}
+
+static void __exit zt_cleanup(void) {
+ int x;
+ 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]);
+ unregister_chrdev(ZT_MAJOR, "zaptel");
+}
+
+module_init(zt_init);
+module_exit(zt_cleanup);