diff options
author | markster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2001-11-05 19:36:16 +0000 |
---|---|---|
committer | markster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2001-11-05 19:36:16 +0000 |
commit | 3e12fb4c61ed406d67695f65f2be7b543ff54454 (patch) | |
tree | fe9ca27015318bd3e50f94a4c12fe4b3e1cbf883 | |
parent | 9edf5e67c421257f00715425aee73e5b8ebfe1a9 (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-x | digits.h | 88 | ||||
-rwxr-xr-x | zaptel.c | 3715 |
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,¶m,sizeof(param)); + break; + case ZT_SET_PARAMS: /* set channel timing paramters */ + /* point to relevant structure */ + copy_from_user(¶m,(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); |