summaryrefslogtreecommitdiff
path: root/ztdynamic.c
diff options
context:
space:
mode:
authormarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2002-02-13 20:43:47 +0000
committermarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2002-02-13 20:43:47 +0000
commitaf7d6c09dfcfdfc473dc02c90f6063fc52b1661a (patch)
treebfc9073811f62d498037d3dac02b8b8f707e0c24 /ztdynamic.c
parent872af797f8f0588df93271ee4af5bd387e44cd48 (diff)
Version 0.1.6 from FTP
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@60 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'ztdynamic.c')
-rwxr-xr-xztdynamic.c712
1 files changed, 712 insertions, 0 deletions
diff --git a/ztdynamic.c b/ztdynamic.c
new file mode 100755
index 0000000..21677bc
--- /dev/null
+++ b/ztdynamic.c
@@ -0,0 +1,712 @@
+/*
+ * Dynamic Span Interface for Zaptel
+ *
+ * Written by Mark Spencer <markster@linux-support.net>
+ *
+ * Copyright (C) 2001, Linux Support Services, Inc.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
+#ifdef CONFIG_DEVFS_FS
+#include <linux/devfs_fs_kernel.h>
+#endif
+#ifdef STANDALONE_ZAPATA
+#include "zaptel.h"
+#else
+#include <linux/zaptel.h>
+#endif
+
+/*
+ * Dynamic spans implemented using TDM over X with standard message
+ * types. Message format is as follows:
+ *
+ * Byte #: Meaning
+ * 0 Number of samples per channel
+ * 1 Current flags on span
+ * Bit 0: Yellow Alarm
+ * Bit 1: Sig bits present
+ * Bits 2-7: reserved for future use
+ * 2-3 16-bit counter value for detecting drops, network byte order.
+ * 4-5 Number of channels in the message, network byte order
+ * 6... 16-bit words, containing sig bits for each
+ * four channels, least significant 4 bits being
+ * the least significant channel, network byte order.
+ * the rest data for each channel, all samples per channel
+ before moving to the next.
+ */
+
+/* Arbitrary limit to the max # of channels in a span */
+#define ZT_DYNAMIC_MAX_CHANS 256
+
+#define ZTD_FLAG_YELLOW_ALARM (1 << 0)
+#define ZTD_FLAG_SIGBITS_PRESENT (1 << 1)
+#define ZTD_FLAG_LOOPBACK (1 << 2)
+
+#define ERR_NSAMP (1 << 16)
+#define ERR_NCHAN (1 << 17)
+#define ERR_LEN (1 << 18)
+
+EXPORT_SYMBOL(zt_dynamic_register);
+EXPORT_SYMBOL(zt_dynamic_unregister);
+EXPORT_SYMBOL(zt_dynamic_receive);
+
+static struct zt_dynamic {
+ char addr[40];
+ char dname[20];
+ int err;
+ int alarm;
+ int usecount;
+ int dead;
+ long rxjif;
+ unsigned short txcnt;
+ unsigned short rxcnt;
+ struct zt_span span;
+ struct zt_chan *chans;
+ struct zt_dynamic *next;
+ struct zt_dynamic_driver *driver;
+ void *pvt;
+ int timing;
+ int master;
+ unsigned char *msgbuf;
+} *dspans;
+
+static struct zt_dynamic_driver *drivers = NULL;
+
+static int debug = 0;
+
+static int hasmaster = 0;
+
+static spinlock_t dlock = SPIN_LOCK_UNLOCKED;
+
+
+static void checkmaster(void)
+{
+ long flags;
+ int newhasmaster=0;
+ int best = 9999999;
+ struct zt_dynamic *z, *master=NULL;
+ spin_lock_irqsave(&dlock, flags);
+ z = dspans;
+ while(z) {
+ if (z->timing) {
+ if (z->timing) {
+ z->master = 0;
+ newhasmaster = 1;
+ if (!z->alarm && (z->timing < best) && !z->dead) {
+ /* If not in alarm and they're
+ a better timing source, use them */
+ master = z;
+ best = z->timing;
+ }
+ }
+ }
+ z = z->next;
+ }
+ hasmaster = newhasmaster;
+ /* Mark the new master if there is one */
+ if (master)
+ master->master = 1;
+ spin_unlock_irqrestore(&dlock, flags);
+ if (master)
+ printk("TDMoX: New master: %s\n", master->span.name);
+ else
+ printk("TDMoX: No master.\n");
+}
+
+static void ztd_sendmessage(struct zt_dynamic *z)
+{
+ unsigned char *buf = z->msgbuf;
+ unsigned short bits;
+ int msglen = 0;
+ int x;
+ int offset;
+
+ /* Byte 0: Number of samples per channel */
+ *buf = ZT_CHUNKSIZE;
+ buf++; msglen++;
+
+ /* Byte 1: Flags */
+ *buf = 0;
+ if (z->alarm & ZT_ALARM_RED)
+ *buf |= ZTD_FLAG_YELLOW_ALARM;
+ *buf |= ZTD_FLAG_SIGBITS_PRESENT;
+ buf++; msglen++;
+
+ /* Bytes 2-3: Transmit counter */
+ *((unsigned short *)buf) = htons((unsigned short)z->txcnt);
+ z->txcnt++;
+ buf++; msglen++;
+ buf++; msglen++;
+
+ /* Bytes 4-5: Number of channels */
+ *((unsigned short *)buf) = htons((unsigned short)z->span.channels);
+ buf++; msglen++;
+ buf++; msglen++;
+ bits = 0;
+ offset = 0;
+ for (x=0;x<z->span.channels;x++) {
+ offset = x % 4;
+ bits |= (z->chans[x].txsig & 0xf) << (offset << 2);
+ if (offset == 3) {
+ /* Write the bits when we have four channels */
+ *((unsigned short *)buf) = htons(bits);
+ buf++; msglen++;
+ buf++; msglen++;
+ bits = 0;
+ }
+ }
+
+ if (offset != 3) {
+ /* Finish it off if it's not done already */
+ *((unsigned short *)buf) = htons(bits);
+ buf++; msglen++;
+ buf++; msglen++;
+ }
+
+ for (x=0;x<z->span.channels;x++) {
+ memcpy(buf, z->chans[x].writechunk, ZT_CHUNKSIZE);
+ buf += 8;
+ msglen += 8;
+ }
+
+ z->driver->transmit(z->pvt, z->msgbuf, msglen);
+
+}
+
+static void ztdynamic_run(void)
+{
+ long flags;
+ struct zt_dynamic *z;
+ spin_lock_irqsave(&dlock, flags);
+ z = dspans;
+ while(z) {
+ if (!z->dead) {
+ /* Ignore dead spans */
+ zt_receive(&z->span);
+ zt_transmit(&z->span);
+ /* Handle all transmissions now */
+ ztd_sendmessage(z);
+ }
+ z = z->next;
+ }
+ spin_unlock_irqrestore(&dlock, flags);
+}
+
+void zt_dynamic_receive(struct zt_span *span, unsigned char *msg, int msglen)
+{
+ struct zt_dynamic *ztd = span->pvt;
+ int newerr=0;
+ long flags;
+ int sflags;
+ int xlen;
+ int x, bits, sig;
+ int nchans, master;
+ int newalarm;
+ unsigned short rxpos;
+
+
+ spin_lock_irqsave(&dlock, flags);
+ if (msglen < 6) {
+ spin_unlock_irqrestore(&dlock, flags);
+ newerr = ERR_LEN;
+ if (newerr != ztd->err) {
+ printk("Span %s: Insufficient samples for header (only %d)\n", span->name, msglen);
+ }
+ ztd->err = newerr;
+ return;
+ }
+
+ /* First, check the chunksize */
+ if (*msg != ZT_CHUNKSIZE) {
+ spin_unlock_irqrestore(&dlock, flags);
+ newerr = ERR_NSAMP | msg[0];
+ if (newerr != ztd->err) {
+ printk("Span %s: Expected %d samples, but receiving %d\n", span->name, ZT_CHUNKSIZE, msg[0]);
+ }
+ ztd->err = newerr;
+ return;
+ }
+ msg++;
+ sflags = *msg;
+ msg++;
+
+ rxpos = ntohs(*((unsigned short *)msg));
+ msg++;
+ msg++;
+
+ nchans = ntohs(*((unsigned short *)msg));
+ if (nchans != span->channels) {
+ spin_unlock_irqrestore(&dlock, flags);
+ newerr = ERR_NCHAN | nchans;
+ if (newerr != ztd->err) {
+ printk("Span %s: Expected %d channels, but receiving %d\n", span->name, span->channels, nchans);
+ }
+ ztd->err = newerr;
+ return;
+ }
+ msg++;
+ msg++;
+
+ /* Okay now we've accepted the header, lets check our message
+ length... */
+
+ /* Start with header */
+ xlen = 6;
+ /* Add samples of audio */
+ xlen += nchans * ZT_CHUNKSIZE;
+ /* If RBS info is there, add that */
+ if (sflags & ZTD_FLAG_SIGBITS_PRESENT) {
+ /* Account for sigbits -- one short per 4 channels*/
+ xlen += ((nchans + 3) / 4) * 2;
+ }
+
+ if (xlen != msglen) {
+ spin_unlock_irqrestore(&dlock, flags);
+ newerr = ERR_LEN | xlen;
+ if (newerr != ztd->err) {
+ printk("Span %s: Expected message size %d, but was %d instead\n", span->name, xlen, msglen);
+ }
+ ztd->err = newerr;
+ return;
+ }
+
+ bits = 0;
+
+ /* Record sigbits if present */
+ if (sflags & ZTD_FLAG_SIGBITS_PRESENT) {
+ for (x=0;x<nchans;x++) {
+ if (!(x%4)) {
+ /* Get new bits */
+ bits = ntohs(*((unsigned short *)msg));
+ msg++;
+ msg++;
+ }
+
+ /* Pick the right bits */
+ sig = (bits >> ((x % 4) << 2)) & 0xff;
+
+ /* Update signalling if appropriate */
+ if (sig != span->chans[x].rxsig)
+ zt_rbsbits(&span->chans[x], sig);
+
+ }
+ }
+
+ /* Record data for channels */
+ for (x=0;x<nchans;x++) {
+ memcpy(span->chans[x].readchunk, msg, ZT_CHUNKSIZE);
+ msg += ZT_CHUNKSIZE;
+ }
+
+ master = ztd->master;
+
+ spin_unlock_irqrestore(&dlock, flags);
+
+ /* Check for Yellow alarm */
+ newalarm = span->alarms & ~(ZT_ALARM_YELLOW | ZT_ALARM_RED);
+ if (sflags & ZTD_FLAG_YELLOW_ALARM)
+ newalarm |= ZT_ALARM_YELLOW;
+
+ if (newalarm != span->alarms) {
+ span->alarms = newalarm;
+ zt_alarm_notify(span);
+ }
+
+ /* Keep track of last received packet */
+ ztd->rxjif = jiffies;
+
+ /* If this is our master span, then run everything */
+ if (master)
+ ztdynamic_run();
+
+}
+
+static void dynamic_destroy(struct zt_dynamic *z)
+{
+ /* Unregister span if appropriate */
+ if (z->span.flags & ZT_FLAG_REGISTERED)
+ zt_unregister(&z->span);
+
+ /* Destroy the pvt stuff if there */
+ if (z->pvt)
+ z->driver->destroy(z->pvt);
+
+ /* Free message buffer if appropriate */
+ if (z->msgbuf)
+ kfree(z->msgbuf);
+
+ /* Free channels */
+ if (z->chans);
+ kfree(z->chans);
+ /* Free z */
+ kfree(z);
+
+ checkmaster();
+}
+
+static struct zt_dynamic *find_dynamic(ZT_DYNAMIC_SPAN *zds)
+{
+ struct zt_dynamic *z;
+ z = dspans;
+ while(z) {
+ if (!strcmp(z->dname, zds->driver) &&
+ !strcmp(z->addr, zds->addr))
+ break;
+ z = z->next;
+ }
+ return z;
+}
+
+static struct zt_dynamic_driver *find_driver(char *name)
+{
+ struct zt_dynamic_driver *ztd;
+ ztd = drivers;
+ while(drivers) {
+ /* here's our driver */
+ if (!strcmp(name, ztd->name))
+ break;
+ }
+ return ztd;
+}
+
+static int destroy_dynamic(ZT_DYNAMIC_SPAN *zds)
+{
+ long flags;
+ struct zt_dynamic *z, *cur, *prev=NULL;
+ spin_lock_irqsave(&dlock, flags);
+ z = find_dynamic(zds);
+ if (!z) {
+ spin_unlock_irqrestore(&dlock, flags);
+ return -EINVAL;
+ }
+ /* Unlink it */
+ cur = dspans;
+ while(cur) {
+ if (cur == z) {
+ if (prev)
+ prev->next = z->next;
+ else
+ dspans = z->next;
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ spin_unlock_irqrestore(&dlock, flags);
+
+ /* Destroy it */
+ dynamic_destroy(z);
+
+ return 0;
+}
+
+static int ztd_rbsbits(struct zt_chan *chan, int bits)
+{
+ /* Don't have to do anything */
+ return 0;
+}
+
+static int ztd_open(struct zt_chan *chan)
+{
+ struct zt_dynamic *z;
+ z = chan->span->pvt;
+ if (z) {
+ if (z->dead)
+ return -ENODEV;
+ z->usecount++;
+ }
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int ztd_chanconfig(struct zt_chan *chan, int sigtype)
+{
+ return 0;
+}
+
+static int ztd_close(struct zt_chan *chan)
+{
+ struct zt_dynamic *z;
+ z = chan->span->pvt;
+ if (z)
+ z->usecount--;
+ if (z->dead && !z->usecount)
+ dynamic_destroy(z);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int create_dynamic(ZT_DYNAMIC_SPAN *zds)
+{
+ struct zt_dynamic *z;
+ struct zt_dynamic_driver *ztd;
+ long flags;
+ int x;
+ int bufsize;
+
+ if (zds->numchans < 1) {
+ printk("Can't be less than 1 channel (%d)!\n", zds->numchans);
+ return -EINVAL;
+ }
+ if (zds->numchans >= ZT_DYNAMIC_MAX_CHANS) {
+ printk("Can't create dynamic span with greater than %d channels. See ztdynamic.c and increase ZT_DYNAMIC_MAX_CHANS\n", zds->numchans);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&dlock, flags);
+ z = find_dynamic(zds);
+ spin_unlock_irqrestore(&dlock, flags);
+ if (z)
+ return -EEXIST;
+
+ /* XXX There is a silly race here. We check it doesn't exist, but
+ someone could create it between now and then and we'd end up
+ with two of them. We don't want to hold the spinlock
+ for *too* long though, especially not if there is a possibility
+ of kmalloc. XXX */
+
+
+ /* Allocate memory */
+ z = (struct zt_dynamic *)kmalloc(sizeof(struct zt_dynamic), GFP_KERNEL);
+ if (!z)
+ return -ENOMEM;
+
+ /* Zero it out */
+ memset(z, 0, sizeof(struct zt_dynamic));
+
+ /* Allocate other memories */
+ z->chans = kmalloc(sizeof(struct zt_chan) * zds->numchans, GFP_KERNEL);
+ if (!z->chans) {
+ dynamic_destroy(z);
+ return -ENOMEM;
+ }
+
+ /* Zero out channel stuff */
+ memset(z->chans, 0, sizeof(struct zt_chan) * zds->numchans);
+
+ /* Allocate message buffer with sample space and header space */
+ bufsize = zds->numchans * ZT_CHUNKSIZE + zds->numchans / 4 + 48;
+
+ z->msgbuf = kmalloc(bufsize, GFP_KERNEL);
+
+ if (!z->msgbuf) {
+ dynamic_destroy(z);
+ return -ENOMEM;
+ }
+
+ /* Zero out -- probably not needed but why not */
+ memset(z->msgbuf, 0, bufsize);
+
+ /* Setup parameters properly assuming we're going to be okay. */
+ strncpy(z->dname, zds->driver, sizeof(z->driver) - 1);
+ strncpy(z->addr, zds->addr, sizeof(z->addr) - 1);
+ z->timing = zds->timing;
+ sprintf(z->span.name, "ZTD/%s/%s", zds->driver, zds->addr);
+ sprintf(z->span.desc, "Dynamic '%s' span at '%s'", zds->driver, zds->addr);
+ z->span.channels = zds->numchans;
+ z->span.pvt = z;
+ z->span.deflaw = ZT_LAW_MULAW;
+ z->span.flags |= ZT_FLAG_RBS;
+ z->span.chans = z->chans;
+ z->span.rbsbits = ztd_rbsbits;
+ z->span.open = ztd_open;
+ z->span.close = ztd_close;
+ z->span.chanconfig = ztd_chanconfig;
+ for (x=0;x<zds->numchans;x++) {
+ sprintf(z->chans[x].name, "ZTD/%s/%s/%d", zds->driver, zds->addr, x+1);
+ z->chans[x].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_FXSLS |
+ ZT_SIG_FXSKS | ZT_SIG_FXSGS | ZT_SIG_FXOLS |
+ ZT_SIG_FXOKS | ZT_SIG_FXOGS;
+ z->chans[x].chanpos = x + 1;
+ z->chans[x].pvt = z;
+ }
+
+ spin_lock_irqsave(&dlock, flags);
+ ztd = find_driver(zds->driver);
+ if (!ztd) {
+ /* Try loading the right module */
+ char fn[80];
+ spin_unlock_irqrestore(&dlock, flags);
+ sprintf(fn, "ztd-%s", zds->driver);
+ request_module(fn);
+ spin_unlock_irqrestore(&dlock, flags);
+ ztd = find_driver(zds->driver);
+ }
+ spin_unlock_irqrestore(&dlock, flags);
+
+
+ /* Another race -- should let the module get unloaded while we
+ have it here */
+ if (!ztd) {
+ printk("No such driver '%s' for dynamic span\n", zds->driver);
+ dynamic_destroy(z);
+ return -EINVAL;
+ }
+
+ /* Create the stuff */
+ z->pvt = ztd->create(&z->span, z->addr);
+ if (!z->pvt) {
+ printk("Driver '%s' (%s) rejected address '%s'\n", ztd->name, ztd->desc, z->addr);
+ /* Creation failed */
+ return -EINVAL;
+ }
+
+ /* Remember the driver */
+ z->driver = ztd;
+
+ /* Whee! We're created. Now register the span */
+ if (zt_register(&z->span, 0)) {
+ printk("Unable to register span '%s'\n", z->span.name);
+ dynamic_destroy(z);
+ return -EINVAL;
+ }
+
+ /* Okay, created and registered. add it to the list */
+ spin_lock_irqsave(&dlock, flags);
+ z->next = dspans;
+ dspans = z;
+ spin_unlock_irqrestore(&dlock, flags);
+
+ checkmaster();
+
+ /* All done */
+ return z->span.spanno;
+
+}
+
+static int ztdynamic_ioctl(unsigned int cmd, unsigned long data)
+{
+ ZT_DYNAMIC_SPAN zds;
+ int res;
+ switch(cmd) {
+ case 0:
+ /* This is called just before rotation. If none of our
+ spans are pulling timing, then now is the time to process
+ them */
+ if (!hasmaster)
+ ztdynamic_run();
+ return 0;
+ case ZT_DYNAMIC_CREATE:
+ if (copy_from_user(&zds, (ZT_DYNAMIC_SPAN *)data, sizeof(zds)))
+ return -EFAULT;
+ if (debug)
+ printk("Dynamic Create\n");
+ res = create_dynamic(&zds);
+ if (res < 0)
+ return res;
+ zds.spanno = res;
+ /* Let them know the new span number */
+ if (copy_to_user((ZT_DYNAMIC_SPAN *)data, &zds, sizeof(zds)))
+ return -EFAULT;
+ return 0;
+ case ZT_DYNAMIC_DESTROY:
+ if (copy_from_user(&zds, (ZT_DYNAMIC_SPAN *)data, sizeof(zds)))
+ return -EFAULT;
+ if (debug)
+ printk("Dynamic Destroy\n");
+ return destroy_dynamic(&zds);
+ }
+
+ return -ENOTTY;
+}
+
+int zt_dynamic_register(struct zt_dynamic_driver *dri)
+{
+ long flags;
+ int res = 0;
+ spin_lock_irqsave(&dlock, flags);
+ if (find_driver(dri->name))
+ res = -1;
+ else {
+ dri->next = drivers;
+ drivers = dri;
+ }
+ spin_unlock_irqrestore(&dlock, flags);
+ return res;
+}
+
+void zt_dynamic_unregister(struct zt_dynamic_driver *dri)
+{
+ struct zt_dynamic_driver *cur, *prev=NULL;
+ struct zt_dynamic *z, *zp, *zn;
+ long flags;
+ spin_lock_irqsave(&dlock, flags);
+ cur = drivers;
+ while(cur) {
+ if (cur == dri) {
+ if (prev)
+ prev->next = cur->next;
+ else
+ drivers = cur->next;
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ z = dspans;
+ zp = NULL;
+ while(z) {
+ zn = z->next;
+ if (z->driver == dri) {
+ /* Unlink */
+ if (zp)
+ zp->next = z->next;
+ else
+ dspans = z->next;
+ if (!z->usecount)
+ dynamic_destroy(z);
+ else
+ z->dead = 1;
+ } else {
+ zp = z;
+ }
+ z = zn;
+ }
+ spin_unlock_irqrestore(&dlock, flags);
+}
+
+int ztdynamic_init(void)
+{
+ zt_set_dynamic_ioctl(ztdynamic_ioctl);
+ printk("Zaptel Dynamic Span support LOADED\n");
+ return 0;
+}
+
+void ztdynamic_cleanup(void)
+{
+ zt_set_dynamic_ioctl(NULL);
+ printk("Zaptel Dynamic Span support unloaded\n");
+}
+
+MODULE_PARM(debug, "i");
+MODULE_DESCRIPTION("Zaptel Dynamic Span Support");
+MODULE_AUTHOR("Mark Spencer <markster@linux-support.net>");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+module_init(ztdynamic_init);
+module_exit(ztdynamic_cleanup);