/* * Zapata Telephony Interface Driver * * Written by Mark Spencer * Based on previous works, designs, and architectures conceived and * written by Jim Dixon . * * Special thanks to Steve Underwood * for substantial contributions to signal processing functions * in zaptel and the zapata library. * * 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 #include #include #include #include #include #ifdef CONFIG_ZAPATA_NET #include #endif /* CONFIG_ZAPATA_NET */ #include /* Grab fasthdlc with tables */ #define FAST_HDLC_NEED_TABLES #include "fasthdlc.h" #ifdef STANDALONE_ZAPATA #include "zaptel.h" #else #include #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_init_tone_state); EXPORT_SYMBOL(zt_dtmf_tone); 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; /* 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; 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[16384]; short __zt_alaw[256]; u_char __zt_lin2a[16384]; 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_ABIT << 8)}, { ZT_SIG_FXSLS,ZT_BBIT | (ZT_BBIT << 8)}, { ZT_SIG_FXSGS,ZT_ABIT | ZT_BBIT | ((ZT_ABIT | ZT_BBIT) << 8)}, { ZT_SIG_FXSKS,ZT_BBIT | ZT_BBIT | ((ZT_ABIT | ZT_BBIT) << 8)}, { ZT_SIG_FXOLS,0 | (ZT_ABIT << 8)}, { ZT_SIG_FXOGS,ZT_BBIT | ((ZT_ABIT | ZT_BBIT) << 8)}, { ZT_SIG_FXOKS,0 | (ZT_ABIT << 8)} } ; /* if RBS does not apply, return error */ if (!(chan->span->flags & ZT_FLAG_RBS) || !chan->span->rbsbits) return(-1); for (x=0;xsig) 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;xconfn == 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> 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;xreadbuf[x] = newbuf + x * j; ss->writebuf[x] = newbuf + (numbufs + x) * j; } } else { for (x=0;xreadbuf[x] = NULL; ss->writebuf[x] = NULL; } } /* Mark all buffers as empty */ for (x=0;xwriten[x] = ss->writeidx[x]= ss->readn[x]= ss->readidx[x] = 0; /* Keep track of where our data goes (if it goes anywhere at all) */ if (newbuf) { ss->inreadbuf = 0; ss->inwritebuf = 0; } else { ss->inreadbuf = -1; ss->inwritebuf = -1; } ss->outreadbuf = -1; ss->outwritebuf = -1; ss->numbufs = numbufs; if (ss->txbufpolicy == ZT_POLICY_WHEN_FULL) ss->txdisable = 1; else ss->txdisable = 0; if (ss->rxbufpolicy == ZT_POLICY_WHEN_FULL) ss->rxdisable = 1; else ss->rxdisable = 0; spin_unlock_irqrestore(&ss->lock, flags); if (oldbuf) kfree(oldbuf); return 0; } static int zt_hangup(struct zt_chan *chan); static void zt_set_law(struct zt_chan *chan, int law); 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; chan->flags &= ~ZT_FLAG_LINEAR; zt_set_law(chan,chan->deflaw); 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)) 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 /* Indicate that zone is loaded but no such tone exists */ res = -ENOSYS; } else /* Note that no tone zone exists at the moment */ res = -ENODATA; if (chan->curtone) zt_init_tone_state(&chan->ts, chan->curtone); return res; } static int set_tone_zone(struct zt_chan *chan, int zone) { int res=0; /* 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 void zt_set_law(struct zt_chan *chan, int law) { if (!law) { if (chan->deflaw) law = chan->deflaw; else if (chan->span) law = chan->span->deflaw; else law = ZT_LAW_MULAW; } if (law == ZT_LAW_ALAW) { chan->xlaw = __zt_alaw; chan->lin2x = __zt_lin2a; } else { chan->xlaw = __zt_mulaw; chan->lin2x = __zt_lin2mu; } } 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;xchanno = 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; zt_set_law(chan, 0); close_channel(chan); res = 0; break; } } write_unlock_irqrestore(&chan_lock, flags); if (x >= ZT_MAX_CHANNELS) printk(KERN_ERR "No more channels available\n"); return res; } char *zt_lboname(int x) { if ((x < 0) || ( x > 7)) return "Unknown"; return zt_txlevelnames[x]; } #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;xlen;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;xwriten[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;xmaster == 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,x; 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 (chan->flags & ZT_FLAG_LINEAR) { if (amnt > (chan->readn[chan->outreadbuf] << 1)) amnt = chan->readn[chan->outreadbuf] << 1; if (amnt) { /* There seems to be a max stack size, so we have to do this in smaller pieces */ short lindata[512]; int left = amnt; int pos = 0; int pass; while(left) { pass = left; if (pass > 512) pass = 512; for (x=0;xreadbuf[chan->outreadbuf][x + pos], chan); if (copy_to_user(usrbuf + (pos << 1), lindata, pass << 1)) return -EFAULT; left -= pass; pos += pass; } } } else { 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,x; 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 (chan->flags & ZT_FLAG_LINEAR) { if (amnt > (chan->blocksize << 1)) amnt = chan->blocksize << 1; } else { 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) { if (chan->flags & ZT_FLAG_LINEAR) { /* There seems to be a max stack size, so we have to do this in smaller pieces */ short lindata[512]; int left = amnt; int pos = 0; int pass; while(left) { pass = left; if (pass > 512) pass = 512; if (copy_from_user(lindata, usrbuf + (pos << 1), pass << 1)) return -EFAULT; left -= pass; for (x=0;xwritebuf[chan->inwritebuf][x + pos] = ZT_LIN2X(lindata[x], chan); pos += pass; } chan->writen[chan->inwritebuf] = amnt >> 1; } else { 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] = { /* We set the idle case of the ZT_SIG_NONE to this pattern to make idle E1 CAS channels happy. Should not matter with T1, since on an un-configured channel, who cares what the sig bits are as long as they are stable */ { ZT_SIG_NONE, ZT_ABIT | ZT_CBIT | ZT_DBIT, 0, 0, 0 }, /* no signalling */ { ZT_SIG_EM, 0, ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT, ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT, 0 }, /* E and M */ { ZT_SIG_FXSLS, ZT_BBIT | ZT_DBIT, ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT, ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT, 0 }, /* FXS Loopstart */ { ZT_SIG_FXSGS, ZT_BBIT | ZT_DBIT, ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT, ZT_ABIT | ZT_CBIT, 0 }, /* FXS Groundstart */ { ZT_SIG_FXSKS, ZT_BBIT | ZT_DBIT, ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT, ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT, 0 }, /* FXS Kewlstart */ { ZT_SIG_FXOLS, ZT_BBIT | ZT_DBIT, ZT_BBIT | ZT_DBIT, 0, 0 }, /* FXO Loopstart */ { ZT_SIG_FXOGS, ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT, ZT_BBIT | ZT_DBIT, 0, 0 }, /* FXO Groundstart */ { ZT_SIG_FXOKS, ZT_BBIT | ZT_DBIT, ZT_BBIT | ZT_DBIT, 0, ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT } /* FXO Kewlstart */ } ; 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->txhooksig = txsig; chan->span->hooksig(chan, txsig); chan->otimer = timeout * 8; /* Otimer is timer in samples */ return; } else { for (x=0;xsig) { #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->txhooksig = txsig; chan->txsig = outs[x][txsig+1]; chan->span->rbsbits(chan, chan->txsig); 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_set_law(chan,chan->deflaw); 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; chan->flags &= ~ZT_FLAG_LINEAR; 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; space = size = sizeof(struct zt_zone) + th.count * sizeof(struct zt_tone); 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;xringcadence[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) || (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->tonesamples = td.samples; t->fac1 = td.fac1; t->init_v2_1 = td.init_v2_1; t->init_v3_1 = td.init_v3_1; t->fac2 = td.fac2; t->init_v2_2 = td.init_v2_2; t->init_v3_2 = td.init_v3_2; t->next = NULL; /* XXX Unnecessary XXX */ if (!z->tones[td.tone]) z->tones[td.tone] = t; } for (x=0;xnext = samples[next[x]]; /* Actually register zone */ res = zt_register_tone_zone(th.zone, z); if (res) kfree(slab); return res; } void zt_init_tone_state(struct zt_tone_state *ts, struct zt_tone *zt) { ts->v1_1 = 0; ts->v2_1 = zt->init_v2_1; ts->v3_1 = zt->init_v3_1; ts->v1_2 = 0; ts->v2_2 = zt->init_v2_2; ts->v3_2 = zt->init_v3_2; } struct zt_tone *zt_dtmf_tone(char digit, int mf) { struct zt_tone *z; if (!mf) z = dtmf_tones; else z = mfv1_tones; switch(digit) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return z + (int)(digit - '0'); case '*': return z + 10; case '#': return z + 11; case 'A': case 'B': case 'C': return z + (digit + 12 - 'A'); case 'D': if (!mf) return z + ( digit + 12 - 'A'); return NULL; case 'a': case 'b': case 'c': return z + (digit + 12 - 'a'); case 'd': if (!mf) return z + ( digit + 12 - 'a'); return NULL; case 'W': case 'w': return &tone_pause; } return NULL; } static inline void do_dtmf(struct zt_chan *chan) { char c; while (strlen(chan->txdialbuf)) { c = chan->txdialbuf[0]; /* Skooch */ memmove(chan->txdialbuf, chan->txdialbuf + 1, sizeof(chan->txdialbuf) - 1); switch(c) { 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; default: chan->curtone = zt_dtmf_tone(c, (chan->digitmode == DIGIT_MODE_MFV1)); chan->tonep = 0; /* All done */ if (chan->curtone) { zt_init_tone_state(&chan->ts, chan->curtone); return; } } } /* Notify userspace process if there is nothing left */ chan->dialing = 0; qevent(chan, ZT_EVENT_DIALCOMPLETE); } static int zt_release(struct inode *inode, struct file *file) { int unit = UNIT(file); int res; struct zt_chan *chan; if (!unit) return zt_ctl_release(inode, file); if (unit == 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; struct zt_chan *chan; int i,j; struct zt_params param; switch(cmd) { case ZT_GET_PARAMS: /* get channel timing parameters */ copy_from_user(¶m,(struct zt_params *)data,sizeof(param)); /* Pick the right channo's */ if (!param.channo || unit) { param.channo = unit; } /* Check validity of channel */ VALID_CHANNEL(param.channo); chan = chans[param.channo]; /* point to relevant structure */ param.sigtype = chan->sig; /* get signalling type */ /* return non-zero if rx not in idle state */ if (chan->span) { j = zt_q_sig(chan); if (j >= 0) { /* if returned with success */ param.rxisoffhook = ((chan->rxsig & (j >> 8)) != (j & 0xff)); } else { param.rxisoffhook = ((chan->rxhooksig != ZT_RXSIG_ONHOOK) && (chan->rxhooksig != ZT_RXSIG_INITIAL)); } } else param.rxisoffhook = 0; if (chan->span && chan->span->rbsbits && !(chan->sig & ZT_SIG_CLEAR)) { param.rxbits = chan->rxsig; param.txbits = chan->txsig; } else { param.rxbits = -1; param.txbits = -1; } if (chan->span && (chan->span->rbsbits || chan->span->hooksig) && !(chan->sig & ZT_SIG_CLEAR)) { param.rxhooksig = chan->rxhooksig; param.txhooksig = chan->txhooksig; } else { param.rxhooksig = -1; param.txhooksig = -1; } 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; if (chan->span) param.spanno = chan->span->spanno; else param.spanno = 0; param.chanpos = chan->chanpos; /* Return current law */ if (chan->xlaw == __zt_alaw) param.curlaw = ZT_LAW_ALAW; else param.curlaw = ZT_LAW_MULAW; copy_to_user((struct zt_params *)data,¶m,sizeof(param)); break; case ZT_SET_PARAMS: /* set channel timing paramters */ copy_from_user(¶m,(struct zt_params *)data,sizeof(param)); /* Pick the right channo's */ if (!param.channo || unit) { param.channo = unit; } /* Check validity of channel */ VALID_CHANNEL(param.channo); chan = chans[param.channo]; /* point to relevant structure */ /* 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_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 (!memcmp(chans[i]->rxgain, defgain, 256) && !memcmp(chans[i]->txgain, defgain, 256)) { /* This is really just a normal gain, so deallocate the memory and go back to defaults */ kfree(chans[i]->rxgain); chans[i]->rxgain = defgain; chans[i]->txgain = defgain; chans[i]->gainalloc = 0; } 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 */ strncpy(span.desc, spans[i]->desc, sizeof(span.desc) - 1); strncpy(span.name, spans[i]->name, sizeof(span.name) - 1); 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.totalchans = spans[i]->channels; 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;xchannels;x++) { y = zt_q_sig(&spans[j]->chans[x]) & 0xff; 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) { /* Setup default law */ chans[ch.chan]->deflaw = ch.deflaw; /* 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]) & 0xff; 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; rv = spans[maint.spanno]->maint(spans[maint.spanno], maint.command); if (rv) return rv; break; case ZT_MAINT_LOOPUP: case ZT_MAINT_LOOPDOWN: spans[maint.spanno]->mainttimer = ZT_LOOPCODE_TIME * 8; rv = spans[maint.spanno]->maint(spans[maint.spanno], maint.command); if (rv) return rv; 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; case ZT_SETLAW: get_user(j, (int *)data); if ((j < 0) || (j > ZT_LAW_ALAW)) return -EINVAL; zt_set_law(chan, j); break; case ZT_SETLINEAR: get_user(j, (int *)data); /* Makes no sense on non-audio channels */ if (!(chan->flags & ZT_FLAG_AUDIO)) return -EINVAL; if (j) chan->flags |= ZT_FLAG_LINEAR; else chan->flags &= ~ZT_FLAG_LINEAR; break; 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_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_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;xname); return -EBUSY; } for (x=1;xflags |= ZT_FLAG_REGISTERED; span->spanno = x; if (!span->deflaw) { printk("zaptel: Span %s didn't specify default law. Assuming mulaw, please fix driver!\n", span->name); span->deflaw = ZT_LAW_MULAW; } for (x=0;xchannels;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;xchannels;x++) zt_chan_unreg(&span->chans[x]); maxspans = 0; if (master == span) master = NULL; for (x=1;x> 8) & 0x80; /* set aside the sign */ if (sign != 0) sample = -sample; /* get magnitude */ if (sample > CLIP) sample = CLIP; /* clip the magnitude */ /* Convert from 16 bit linear to ulaw. */ sample = sample + BIAS; exponent = exp_lut[(sample >> 7) & 0xFF]; mantissa = (sample >> (exponent + 3)) & 0x0F; ulawbyte = ~(sign | (exponent << 4) | mantissa); #ifdef ZEROTRAP if (ulawbyte == 0) ulawbyte = 0x02; /* optional CCITT trap */ #endif if (ulawbyte == 0xff) ulawbyte = 0x7f; /* never return 0xff */ return(ulawbyte); } #define AMI_MASK 0x55 static inline uint8_t linear2alaw (short int linear) { int mask; int seg; int pcm_val; static int seg_end[8] = { 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF }; pcm_val = linear; if (pcm_val >= 0) { /* Sign (7th) bit = 1 */ mask = AMI_MASK | 0x80; } else { /* Sign bit = 0 */ mask = AMI_MASK; pcm_val = -pcm_val; } /* Convert the scaled magnitude to segment number. */ for (seg = 0; seg < 8; seg++) { if (pcm_val <= seg_end[seg]) break; } /* Combine the sign, segment, and quantization bits. */ return ((seg << 4) | ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask; } /*- End of function --------------------------------------------------------*/ static inline short int alaw2linear (uint8_t alaw) { int i; int seg; alaw ^= AMI_MASK; i = ((alaw & 0x0F) << 4); seg = (((int) alaw & 0x70) >> 4); if (seg) i = (i + 0x100) << (seg - 1); return (short int) ((alaw & 0x80) ? i : -i); } /*- End of function --------------------------------------------------------*/ static void __init zt_conv_init(void) { int i; /* * Set up mu-law conversion table */ for(i = 0;i < 256;i++) { short mu,e,f,y; static short etab[]={0,132,396,924,1980,4092,8316,16764}; mu = 255-i; e = (mu & 0x70)/16; f = mu & 0x0f; y = f * (1 << (e + 3)); y += etab[e]; if (mu & 0x80) y = -y; __zt_mulaw[i] = y; __zt_alaw[i] = alaw2linear(i); /* Default (0.0 db) gain table */ defgain[i] = i; } /* set up the reverse (mu-law) conversion table */ for(i = -32768; i < 32768; i += 4) { __zt_lin2mu[((unsigned short)(short)i) >> 2] = linear2ulaw(i); __zt_lin2a[((unsigned short)(short)i) >> 2] = linear2alaw(i); } } 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 */ getlin = zt_tone_nextsample(&ms->ts, ms->curtone); txc = ZT_LIN2X(getlin, ms); ms->tonep++; if (ms->tonep >= ms->curtone->tonesamples) { struct zt_tone *last; /* Go to the next sample of the tone */ ms->tonep = 0; last = ms->curtone; ms->curtone = ms->curtone->next; if (!ms->curtone) { /* No more tones... Is this dtmf or mf? If so, go to the next digit */ if (ms->dialing) do_dtmf(ms); } else { if (last != ms->curtone) zt_init_tone_state(&ms->ts, ms->curtone); } } } else if (ms->flags & ZT_FLAG_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 = ZT_LIN2X(0, ms); /* Lastly we use silence on telephony channels */ /* Okay, now we've got something to transmit */ getlin = ZT_XLAW(txc, ms); 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_LIN2X(getlin, ms); 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_LIN2X(getlin, ms); 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_LIN2X(getlin, ms); 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_LIN2X(getlin, ms); break; case ZT_CONF_REALANDPSEUDO: /* This strange mode takes the transmit buffer and puts it on the conference, minus its last sample, then outputs from the conference minus the real channel's last sample. */ /* if to talk on conf */ if (ms->confmode & ZT_CONF_PSEUDO_TALKER) { /* Store temp value */ 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_LIN2X(getlin, ms); 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_LIN2X(getlin, ms); 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_XLAW(rxc, ms); putlin = process_cc(&ms->ec, ms->getlin_lastchunk[pos], abs(ms->getlin_lastchunk[pos]), putlin, abs(putlin)); rxc = ZT_LIN2X((int)putlin, ms); } /* Apply gain if appropriate */ if (ms->flags & ZT_FLAG_AUDIO) rxc = ms->rxgain[rxc]; putlin = ZT_XLAW(rxc, ms); 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_LIN2X(putlin, ms); break; case ZT_CONF_MONITORTX: /* Monitor a channel's tx mode */ /* if not a pseudo-channel, ignore */ if (!(ms->flags & ZT_FLAG_PSEUDO)) break; /* Add monitored channel */ if (chans[ms->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_LIN2X(putlin, ms); break; case ZT_CONF_MONITORBOTH: /* Monitor a channel's tx and rx mode */ /* if not a pseudo-channel, ignore */ if (!(ms->flags & ZT_FLAG_PSEUDO)) break; putlin += chans[ms->confn]->putlin[pos] + chans[ms->confn]->getlin[pos]; /* Clip */ if (putlin > 32767) putlin = 32767; if (putlin < -32767) putlin = 32767; rxc = ZT_LIN2X(putlin, ms); 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_LIN2X(putlin, ms); break; case ZT_CONF_CONF: /* Normal conference mode */ if (ms->flags & ZT_FLAG_PSEUDO) /* if a pseudo-channel */ { if (ms->confmode & ZT_CONF_LISTENER) { /* Subtract out last sample written to conf */ 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_LIN2X(putlin, ss); ss->getlin[pos] = ZT_XLAW(rxc, ss); 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_LIN2X((int)conf_sums_prev[ms->confn][pos], ms); 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;xchannels;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;ychans[x].writechunk[y] = span->chans[x].txgain[zt_getbuf_byte(&span->chans[x], y)]; } } else { for (y=0;ychans[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;xchannels;x++) { spin_lock_irqsave(&span->chans[x].lock, flags); for (y=0;ychans[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;xflags & ZT_FLAG_PSEUDO)) { for (y=0;y 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;xflags & ZT_FLAG_PSEUDO)) { for (y=0;y"); 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