summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rwxr-xr-xbuild_tools/genudevrules1
-rw-r--r--zaptel.c43
-rw-r--r--zaptel.h206
-rw-r--r--zttranscode.c441
5 files changed, 664 insertions, 30 deletions
diff --git a/Makefile b/Makefile
index 1b1590e..7537bd6 100644
--- a/Makefile
+++ b/Makefile
@@ -109,7 +109,7 @@ LIBTONEZONE_SO_MINOR_VER:=0
MODULES:=zaptel tor2 torisa wcusb wcfxo wctdm wctdm24xxp \
ztdynamic ztd-eth wct1xxp wct4xxp wcte11xp pciradio \
- ztd-loc # ztdummy
+ ztd-loc zttranscode
#MODULES+=wcfxsusb
# build ztdummy by default for 2.6 kernels
ifeq ($(BUILDVER),linux26)
@@ -317,6 +317,7 @@ ifndef DYNFS
rm -f $(INSTALL_PREFIX)/dev/zap/251
rm -f $(INSTALL_PREFIX)/dev/zap/250
mknod $(INSTALL_PREFIX)/dev/zap/ctl c 196 0
+ mknod $(INSTALL_PREFIX)/dev/zap/transcode c 196 250
mknod $(INSTALL_PREFIX)/dev/zap/timer c 196 253
mknod $(INSTALL_PREFIX)/dev/zap/channel c 196 254
mknod $(INSTALL_PREFIX)/dev/zap/pseudo c 196 255
diff --git a/build_tools/genudevrules b/build_tools/genudevrules
index a536f75..beb6b8d 100755
--- a/build_tools/genudevrules
+++ b/build_tools/genudevrules
@@ -23,6 +23,7 @@ fi
cat <<EOF
# zaptel devices with ownership/permissions for running as non-root
KERNEL${match}"zapctl", NAME="zap/ctl", OWNER="asterisk", GROUP="asterisk", MODE="0660"
+KERNEL${match}"zaptranscode", NAME="zap/transcode", OWNER="asterisk", GROUP="asterisk", MODE="0660"
KERNEL${match}"zaptimer", NAME="zap/timer", OWNER="asterisk", GROUP="asterisk", MODE="0660"
KERNEL${match}"zapchannel", NAME="zap/channel", OWNER="asterisk", GROUP="asterisk", MODE="0660"
KERNEL${match}"zappseudo", NAME="zap/pseudo", OWNER="asterisk", GROUP="asterisk", MODE="0660"
diff --git a/zaptel.c b/zaptel.c
index 58f723b..8e22356 100644
--- a/zaptel.c
+++ b/zaptel.c
@@ -150,6 +150,7 @@ static char *zt_txlevelnames[] = {
"-22.5db (CSU)"
} ;
+EXPORT_SYMBOL(zt_transcode_fops);
EXPORT_SYMBOL(zt_init_tone_state);
EXPORT_SYMBOL(zt_dtmf_tone);
EXPORT_SYMBOL(zt_register);
@@ -190,6 +191,7 @@ static devfs_handle_t channel;
static devfs_handle_t pseudo;
static devfs_handle_t ctl;
static devfs_handle_t timer;
+static devfs_handle_t transcode;
#endif
/* udev necessary data structures. Yeah! */
@@ -283,6 +285,7 @@ static sumtype *conf_sums_prev;
static struct zt_span *master;
static struct file_operations zt_fops;
+struct file_operations *zt_transcode_fops = NULL;
static struct
{
@@ -2389,10 +2392,24 @@ static void zt_free_pseudo(struct zt_chan *pseudo)
static int zt_open(struct inode *inode, struct file *file)
{
int unit = UNIT(file);
+ int ret = -ENXIO;
struct zt_chan *chan;
/* Minor 0: Special "control" descriptor */
if (!unit)
return zt_ctl_open(inode, file);
+ if (unit == 250) {
+ if (!zt_transcode_fops)
+ request_module("zttranscode");
+ if (zt_transcode_fops && zt_transcode_fops->open) {
+ if (try_module_get(zt_transcode_fops->owner)) {
+ ret = zt_transcode_fops->open(inode, file);
+ if (ret)
+ module_put(zt_transcode_fops->owner);
+ }
+ return ret;
+ }
+ return -ENXIO;
+ }
if (unit == 253) {
if (maxspans) {
return zt_timing_open(inode, file);
@@ -2715,6 +2732,11 @@ static int zt_release(struct inode *inode, struct file *file)
if (unit == 253) {
return zt_timer_release(inode, file);
}
+ if (unit == 250) {
+ res = zt_transcode_fops->release(inode, file);
+ module_put(zt_transcode_fops->owner);
+ return res;
+ }
if (unit == 254) {
chan = file->private_data;
if (!chan)
@@ -4492,6 +4514,10 @@ static int zt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un
if (!unit)
return zt_ctl_ioctl(inode, file, cmd, data);
+
+ if (unit == 250)
+ return zt_transcode_fops->ioctl(inode, file, cmd, data);
+
if (unit == 253) {
timer = file->private_data;
if (timer)
@@ -6369,6 +6395,14 @@ zt_chan_poll(struct file *file, struct poll_table_struct *wait_table, int unit)
return(ret); /* return what we found */
}
+static int zt_mmap(struct file *file, struct vm_area_struct *vm)
+{
+ int unit = UNIT(file);
+ if (unit == 250)
+ return zt_transcode_fops->mmap(file, vm);
+ return -ENOSYS;
+}
+
static unsigned int zt_poll(struct file *file, struct poll_table_struct *wait_table)
{
int unit = UNIT(file);
@@ -6377,6 +6411,9 @@ static unsigned int zt_poll(struct file *file, struct poll_table_struct *wait_ta
if (!unit)
return -EINVAL;
+ if (unit == 250)
+ return zt_transcode_fops->poll(file, wait_table);
+
if (unit == 253)
return zt_timer_poll(file, wait_table);
@@ -6758,7 +6795,7 @@ static struct file_operations zt_fops = {
read: zt_read,
write: zt_write,
poll: zt_poll,
- mmap: NULL,
+ mmap: zt_mmap,
flush: NULL,
fsync: NULL,
fasync: NULL,
@@ -6833,6 +6870,7 @@ static int __init zt_init(void) {
#ifdef CONFIG_ZAP_UDEV /* udev support functions */
zap_class = class_create(THIS_MODULE, "zaptel");
+ CLASS_DEV_CREATE(zap_class, MKDEV(ZT_MAJOR, 250), NULL, "zaptranscode");
CLASS_DEV_CREATE(zap_class, MKDEV(ZT_MAJOR, 253), NULL, "zaptimer");
CLASS_DEV_CREATE(zap_class, MKDEV(ZT_MAJOR, 254), NULL, "zapchannel");
CLASS_DEV_CREATE(zap_class, MKDEV(ZT_MAJOR, 255), NULL, "zappseudo");
@@ -6848,6 +6886,7 @@ static int __init zt_init(void) {
timer = devfs_register(zaptel_devfs_dir, "timer", DEVFS_FL_DEFAULT, ZT_MAJOR, 253, mode, &zt_fops, NULL);
channel = devfs_register(zaptel_devfs_dir, "channel", DEVFS_FL_DEFAULT, ZT_MAJOR, 254, mode, &zt_fops, NULL);
pseudo = devfs_register(zaptel_devfs_dir, "pseudo", DEVFS_FL_DEFAULT, ZT_MAJOR, 255, mode, &zt_fops, NULL);
+ transcode = devfs_register(zaptel_devfs_dir, "transcode", DEVFS_FL_DEFAULT, ZT_MAJOR, 250, mode, &zt_fops, NULL);
ctl = devfs_register(zaptel_devfs_dir, "ctl", DEVFS_FL_DEFAULT, ZT_MAJOR, 0, mode, &zt_fops, NULL);
}
#else
@@ -6884,6 +6923,7 @@ static void __exit zt_cleanup(void) {
kfree(tone_zones[x]);
#ifdef CONFIG_DEVFS_FS
devfs_unregister(timer);
+ devfs_unregister(transcode);
devfs_unregister(channel);
devfs_unregister(pseudo);
devfs_unregister(ctl);
@@ -6891,6 +6931,7 @@ static void __exit zt_cleanup(void) {
devfs_unregister_chrdev(ZT_MAJOR, "zaptel");
#else
#ifdef CONFIG_ZAP_UDEV
+ class_device_destroy(zap_class, MKDEV(ZT_MAJOR, 250)); /* transcode */
class_device_destroy(zap_class, MKDEV(ZT_MAJOR, 253)); /* timer */
class_device_destroy(zap_class, MKDEV(ZT_MAJOR, 254)); /* channel */
class_device_destroy(zap_class, MKDEV(ZT_MAJOR, 255)); /* pseudo */
diff --git a/zaptel.h b/zaptel.h
index 55542f3..d8aae5e 100644
--- a/zaptel.h
+++ b/zaptel.h
@@ -34,15 +34,18 @@
#include "zconfig.h"
#include <linux/config.h>
#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+
#ifdef CONFIG_ZAPATA_NET
#include <linux/hdlc.h>
#endif
+
#ifdef CONFIG_ZAPATA_PPP
#include <linux/ppp_channel.h>
#include <linux/skbuff.h>
#include <linux/interrupt.h>
#endif
-#include <linux/fs.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
#define LINUX26
@@ -56,7 +59,9 @@
#include "ecdis.h"
#include "fasthdlc.h"
-#endif
+
+#endif /* __KERNEL__ */
+
#ifdef CONFIG_DEVFS_FS
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#include <linux/devfs_fs_kernel.h>
@@ -65,7 +70,6 @@
//#warning "Zaptel doesn't support DEVFS in post 2.4 kernels. Disabling DEVFS in zaptel"
#endif
#endif /* CONFIG_DEVFS_FS */
-#include <linux/ioctl.h>
#ifndef ELAST
#define ELAST 500
@@ -635,6 +639,11 @@ struct zt_versioninfo {
*/
#define ZT_SETPOLARITY _IOW (ZT_CODE, 92, int)
+/*
+ * Transcoder operations
+ */
+#define ZT_TRANSCODE_OP _IOWR(ZT_CODE, 93, int)
+
/*
* Startup or Shutdown a span
*/
@@ -664,8 +673,59 @@ struct zt_versioninfo {
#define ZT_TONEDETECT_ON (1 << 0) /* Detect tones */
#define ZT_TONEDETECT_MUTE (1 << 1) /* Mute audio in received channel */
+#define ZT_TRANSCODE_MAGIC 0x74a9c0de
+
+/* Operations */
+#define ZT_TCOP_RESET 1 /* Reset the channel state / codec selection */
+#define ZT_TCOP_TRANSCODE 2 /* Begin transcoding a block */
+#define ZT_TCOP_GETINFO 3 /* Get information (use zt_transcode_info) */
+
+typedef struct zt_transcode_info {
+ unsigned int op;
+ unsigned int tcnum;
+ char name[80];
+ unsigned int srcfmts;
+ unsigned int dstfmts;
+} ZT_TRANSCODE_INFO;
+
+#define ZT_TCCONF_USETS (1 << 0) /* Use/update timestamp field */
+#define ZT_TCCONF_USESEQ (1 << 1) /* Use/update seqno field */
+
+#define ZT_TCSTAT_DSTRDY (1 << 0) /* Destination data is ready */
+#define ZT_TCSTAT_DSTBUSY (1 << 1) /* Destination data is outstanding */
+
+#define __ZT_TRANSCODE_BUFSIZ 16384
+#define ZT_TRANSCODE_HDRLEN 256
+#define ZT_TRANSCODE_BUFSIZ ((__ZT_TRANSCODE_BUFSIZ) - (ZT_TRANSCODE_HDRLEN))
+#define ZT_TRANSCODE_DSTOFFSET (((ZT_TRANSCODE_BUFSIZ) / 2) + ZT_TRANSCODE_HDRLEN)
+#define ZT_TRANSCODE_SRCOFFSET (((ZT_TRANSCODE_BUFSIZ) / 2) + ZT_TRANSCODE_HDRLEN)
+
+typedef struct zt_transcode_header {
+ unsigned int srcfmt; /* See formats.h -- use TCOP_RESET when you change */
+ unsigned int srcoffset; /* In bytes -- written by user */
+ unsigned int srclen; /* In bytes -- written by user */
+ unsigned int srctimestamp; /* In samples -- written by user (only used if ZT_TCCONF_USETS is set) */
+ unsigned int srcseqno; /* In units -- written by user (only used if ZT_TCCONF_USESEQ is set) */
+
+ unsigned int dstfmt; /* See formats.h -- use TCOP_RESET when you change */
+ unsigned int dstoffset; /* In bytes -- written by user */
+ unsigned int dsttimestamp; /* In samples -- read by user */
+ unsigned int dstseqno; /* In units -- read by user (only used if ZT_TCCONF_USESEQ is set) */
+ unsigned int dstlen; /* In bytes -- read by user */
+ unsigned int dstsamples; /* In timestamp units -- read by user */
+
+ unsigned int magic; /* Magic value -- ZT_TRANSCODE_MAGIC, read by user */
+ unsigned int config; /* Read/write by user */
+ unsigned int status; /* Read/write by user */
+
+ /* XXX: fix this to automatically calculate somehow */
+ unsigned char userhdr[ZT_TRANSCODE_HDRLEN - (sizeof(unsigned int) * 14)]; /* Storage for user parameters */
+ unsigned char srcdata[ZT_TRANSCODE_BUFSIZ / 2]; /* Storage of source data */
+ unsigned char dstdata[ZT_TRANSCODE_BUFSIZ / 2]; /* Storage of destination data */
+} ZT_TRANSCODE_HEADER;
+
struct zt_ring_cadence {
- int ringcadence [ZT_MAX_CADENCE];
+ int ringcadence[ZT_MAX_CADENCE];
};
struct zt_tone_def_header {
@@ -1321,6 +1381,33 @@ struct zt_span {
#endif
};
+struct zt_transcoder_channel {
+ void *pvt;
+ struct zt_transcoder *parent;
+ wait_queue_head_t ready;
+ int errorstatus;
+ int offset;
+ unsigned int flags;
+ unsigned int srcfmt;
+ unsigned int dstfmt;
+ struct zt_transcode_header *tch;
+};
+
+#define ZT_TC_FLAG_BUSY (1 << 0)
+#define ZT_TC_FLAG_TRANSIENT (1 << 1)
+
+
+struct zt_transcoder {
+ struct zt_transcoder *next;
+ char name[80];
+ int numchannels;
+ unsigned int srcfmts;
+ unsigned int dstfmts;
+ int (*operation)(struct zt_transcoder_channel *channel, int op);
+ /* Transcoder channels */
+ struct zt_transcoder_channel channels[0];
+};
+
#define ZT_WATCHDOG_NOINTS (1 << 0)
#define ZT_WATCHDOG_INIT 1000
@@ -1354,67 +1441,100 @@ struct zt_dynamic_driver {
};
/* Receive a dynamic span message */
-extern void zt_dynamic_receive(struct zt_span *span, unsigned char *msg, int msglen);
+void zt_dynamic_receive(struct zt_span *span, unsigned char *msg, int msglen);
/* Register a dynamic driver */
-extern int zt_dynamic_register(struct zt_dynamic_driver *driver);
+int zt_dynamic_register(struct zt_dynamic_driver *driver);
/* Unregister a dynamic driver */
-extern void zt_dynamic_unregister(struct zt_dynamic_driver *driver);
+void zt_dynamic_unregister(struct zt_dynamic_driver *driver);
/* Receive on a span. The zaptel interface will handle all the calculations for
all member channels of the span, pulling the data from the readchunk buffer */
-extern int zt_receive(struct zt_span *span);
+int zt_receive(struct zt_span *span);
/* Prepare writechunk buffers on all channels for this span */
-extern int zt_transmit(struct zt_span *span);
+int zt_transmit(struct zt_span *span);
/* Abort the buffer currently being receive with event "event" */
-extern void zt_hdlc_abort(struct zt_chan *ss, int event);
+void zt_hdlc_abort(struct zt_chan *ss, int event);
/* Indicate to zaptel that the end of frame was received and rotate buffers */
-extern void zt_hdlc_finish(struct zt_chan *ss);
+void zt_hdlc_finish(struct zt_chan *ss);
/* Put a chunk of data into the current receive buffer */
-extern void zt_hdlc_putbuf(struct zt_chan *ss, unsigned char *rxb, int bytes);
+void zt_hdlc_putbuf(struct zt_chan *ss, unsigned char *rxb, int bytes);
/* Get a chunk of data from the current transmit buffer. Returns -1 if no data
* is left to send, 0 if there is data remaining in the current message to be sent
* and 1 if the currently transmitted message is now done */
-extern int zt_hdlc_getbuf(struct zt_chan *ss, unsigned char *bufptr, unsigned int *size);
+int zt_hdlc_getbuf(struct zt_chan *ss, unsigned char *bufptr, unsigned int *size);
/* Register a span. Returns 0 on success, -1 on failure. Pref-master is non-zero if
we should have preference in being the master device */
-extern int zt_register(struct zt_span *span, int prefmaster);
+int zt_register(struct zt_span *span, int prefmaster);
+
+/* Allocate / free memory for a transcoder */
+struct zt_transcoder *zt_transcoder_alloc(int numchans);
+void zt_transcoder_free(struct zt_transcoder *ztc);
+
+/* Register a transcoder */
+int zt_transcoder_register(struct zt_transcoder *tc);
+
+/* Unregister a transcoder */
+int zt_transcoder_unregister(struct zt_transcoder *tc);
+
+/* Alert a transcoder */
+int zt_transcoder_alert(struct zt_transcoder_channel *ztc);
+
+/* Sanity check values */
+static inline int zt_tc_sanitycheck(struct zt_transcode_header *zth, unsigned int outbytes)
+{
+ if (zth->dstoffset >= sizeof(zth->dstdata))
+ return 0;
+ if (zth->dstlen >= sizeof(zth->dstdata))
+ return 0;
+ if (outbytes >= sizeof(zth->dstdata))
+ return 0;
+ if ((zth->dstoffset + zth->dstlen + outbytes) >= sizeof(zth->dstdata))
+ return 0;
+ if (zth->srcoffset >= sizeof(zth->srcdata))
+ return 0;
+ if (zth->srclen >= sizeof(zth->srcdata))
+ return 0;
+ if ((zth->srcoffset + zth->srclen) > sizeof(zth->srcdata))
+ return 0;
+ return 1;
+}
/* Unregister a span */
-extern int zt_unregister(struct zt_span *span);
+int zt_unregister(struct zt_span *span);
/* Gives a name to an LBO */
-extern char *zt_lboname(int lbo);
+char *zt_lboname(int lbo);
/* Tell Zaptel about changes in received rbs bits */
-extern void zt_rbsbits(struct zt_chan *chan, int bits);
+void zt_rbsbits(struct zt_chan *chan, int bits);
/* Tell Zaptel abou changes in received signalling */
-extern void zt_hooksig(struct zt_chan *chan, zt_rxsig_t rxsig);
+void zt_hooksig(struct zt_chan *chan, zt_rxsig_t rxsig);
/* Queue an event on a channel */
-extern void zt_qevent_nolock(struct zt_chan *chan, int event);
+void zt_qevent_nolock(struct zt_chan *chan, int event);
/* Queue an event on a channel, locking it first */
-extern void zt_qevent_lock(struct zt_chan *chan, int event);
+void zt_qevent_lock(struct zt_chan *chan, int event);
/* Notify a change possible change in alarm status */
-extern void zt_alarm_notify(struct zt_span *span);
+void zt_alarm_notify(struct zt_span *span);
/* Initialize a tone state */
-extern void zt_init_tone_state(struct zt_tone_state *ts, struct zt_tone *zt);
+void zt_init_tone_state(struct zt_tone_state *ts, struct zt_tone *zt);
/* Get a given DTMF or MF tone struct, suitable for zt_tone_nextsample.
Set 'mf' to 0 for DTMF or 1 for MFv1 */
-extern struct zt_tone *zt_dtmf_tone(char digit, int mf);
+struct zt_tone *zt_dtmf_tone(char digit, int mf);
/* Echo cancel a receive and transmit chunk for a given channel. This
should be called by the low-level driver as close to the interface
@@ -1422,23 +1542,25 @@ extern struct zt_tone *zt_dtmf_tone(char digit, int mf);
AT THE ZAPTEL LEVEL. zt_ec_chunk will not echo cancel if it should
not be doing so. rxchunk is modified in-place */
-extern void zt_ec_chunk(struct zt_chan *chan, unsigned char *rxchunk, const unsigned char *txchunk);
-extern void zt_ec_span(struct zt_span *span);
+void zt_ec_chunk(struct zt_chan *chan, unsigned char *rxchunk, const unsigned char *txchunk);
+void zt_ec_span(struct zt_span *span);
+
+extern struct file_operations *zt_transcode_fops;
/* Don't use these directly -- they're not guaranteed to
be there. */
extern short __zt_mulaw[256];
extern short __zt_alaw[256];
#ifdef CONFIG_CALC_XLAW
-extern u_char __zt_lineartoulaw(short a);
-extern u_char __zt_lineartoalaw(short a);
+u_char __zt_lineartoulaw(short a);
+u_char __zt_lineartoalaw(short a);
#else
extern u_char __zt_lin2mu[16384];
extern u_char __zt_lin2a[16384];
#endif
/* Used by dynamic zaptel -- don't use directly */
-extern void zt_set_dynamic_ioctl(int (*func)(unsigned int cmd, unsigned long data));
+void zt_set_dynamic_ioctl(int (*func)(unsigned int cmd, unsigned long data));
/* Used privately by zaptel. Avoid touching directly */
struct zt_tone {
@@ -1604,6 +1726,34 @@ struct zt_radio_param {
#define ZT_RADPAR_REMCOMMAND 17 /* Remote conrtol write data block & do cmd */
+/* Data formats for capabilities and frames alike (from Asterisk) */
+/*! G.723.1 compression */
+#define ZT_FORMAT_G723_1 (1 << 0)
+/*! GSM compression */
+#define ZT_FORMAT_GSM (1 << 1)
+/*! Raw mu-law data (G.711) */
+#define ZT_FORMAT_ULAW (1 << 2)
+/*! Raw A-law data (G.711) */
+#define ZT_FORMAT_ALAW (1 << 3)
+/*! ADPCM (G.726, 32kbps) */
+#define ZT_FORMAT_G726 (1 << 4)
+/*! ADPCM (IMA) */
+#define ZT_FORMAT_ADPCM (1 << 5)
+/*! Raw 16-bit Signed Linear (8000 Hz) PCM */
+#define ZT_FORMAT_SLINEAR (1 << 6)
+/*! LPC10, 180 samples/frame */
+#define ZT_FORMAT_LPC10 (1 << 7)
+/*! G.729A audio */
+#define ZT_FORMAT_G729A (1 << 8)
+/*! SpeeX Free Compression */
+#define ZT_FORMAT_SPEEX (1 << 9)
+/*! iLBC Free Compression */
+#define ZT_FORMAT_ILBC (1 << 10)
+/*! Maximum audio format */
+#define ZT_FORMAT_MAX_AUDIO (1 << 15)
+/*! Maximum audio mask */
+#define ZT_FORMAT_AUDIO_MASK ((1 << 16) - 1)
+
#define ZT_RADPAR_DEEMP 18 /* Audio De-empahsis (on or off) */
#define ZT_RADPAR_PREEMP 19 /* Audio Pre-empahsis (on or off) */
diff --git a/zttranscode.c b/zttranscode.c
new file mode 100644
index 0000000..14edb3b
--- /dev/null
+++ b/zttranscode.c
@@ -0,0 +1,441 @@
+/*
+ * Transcoder Interface for Zaptel
+ *
+ * Written by Mark Spencer <markster@digium.com>
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <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>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <asm/io.h>
+#ifdef CONFIG_DEVFS_FS
+#include <linux/devfs_fs_kernel.h>
+#endif
+#ifdef STANDALONE_ZAPATA
+#include "zaptel.h"
+#else
+#include <linux/zaptel.h>
+#endif
+#ifdef LINUX26
+#include <linux/moduleparam.h>
+#endif
+
+static int debug = 0;
+static struct zt_transcoder *trans;
+static spinlock_t translock = SPIN_LOCK_UNLOCKED;
+
+EXPORT_SYMBOL(zt_transcoder_register);
+EXPORT_SYMBOL(zt_transcoder_unregister);
+EXPORT_SYMBOL(zt_transcoder_alert);
+EXPORT_SYMBOL(zt_transcoder_alloc);
+EXPORT_SYMBOL(zt_transcoder_free);
+
+struct zt_transcoder *zt_transcoder_alloc(int numchans)
+{
+ struct zt_transcoder *ztc;
+ unsigned int x;
+ size_t size = sizeof(*ztc) + (sizeof(ztc->channels[0]) * numchans);
+
+ if (!(ztc = kmalloc(size, GFP_KERNEL)))
+ return NULL;
+
+ memset(ztc, 0, size);
+ strcpy(ztc->name, "<unspecified>");
+ ztc->numchannels = numchans;
+ for (x=0;x<ztc->numchannels;x++) {
+ init_waitqueue_head(&ztc->channels[x].ready);
+ ztc->channels[x].parent = ztc;
+ ztc->channels[x].offset = x;
+ }
+
+ return ztc;
+}
+
+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;
+}
+
+void zt_transcoder_free(struct zt_transcoder *ztc)
+{
+ kfree(ztc);
+}
+
+/* Register a transcoder */
+int zt_transcoder_register(struct zt_transcoder *tc)
+{
+ struct zt_transcoder *cur;
+ int res = -EBUSY;
+
+ spin_lock(&translock);
+ for (cur = trans; cur; cur = cur->next) {
+ if (cur == tc) {
+ spin_unlock(&translock);
+ return res;
+ }
+ }
+
+ tc->next = trans;
+ trans = tc;
+ printk("Registered codec translator '%s' with %d transcoders (srcs=%08x, dsts=%08x)\n",
+ tc->name, tc->numchannels, tc->srcfmts, tc->dstfmts);
+ res = 0;
+ spin_unlock(&translock);
+
+ return res;
+}
+
+/* Unregister a transcoder */
+int zt_transcoder_unregister(struct zt_transcoder *tc)
+{
+ struct zt_transcoder *cur, *prev;
+ int res = -EINVAL;
+
+ spin_lock(&translock);
+ for (cur = trans, prev = NULL; cur; prev = cur, cur = cur->next) {
+ if (cur == tc)
+ break;
+ }
+
+ if (!cur) {
+ spin_unlock(&translock);
+ return res;
+ }
+
+ if (prev)
+ prev->next = tc->next;
+ else
+ trans = tc->next;
+ tc->next = NULL;
+ printk("Unregistered codec translator '%s' with %d transcoders (srcs=%08x, dsts=%08x)\n",
+ tc->name, tc->numchannels, tc->srcfmts, tc->dstfmts);
+ res = 0;
+ spin_unlock(&translock);
+
+ return res;
+}
+
+/* Alert a transcoder */
+int zt_transcoder_alert(struct zt_transcoder_channel *ztc)
+{
+ if (debug)
+ printk("ZT Transcoder Alert!\n");
+ if (ztc->tch)
+ ztc->tch->status &= ~ZT_TC_FLAG_BUSY;
+ wake_up_interruptible(&ztc->ready);
+
+ return 0;
+}
+
+static int zt_tc_open(struct inode *inode, struct file *file)
+{
+ struct zt_transcoder_channel *ztc;
+ struct zt_transcode_header *zth;
+
+ ztc = kmalloc(sizeof(*ztc), GFP_KERNEL);
+ zth = kmalloc(sizeof(*zth), GFP_KERNEL | GFP_DMA);
+
+ if (ztc && zth) {
+ memset(ztc, 0, sizeof(*ztc));
+ memset(zth, 0, sizeof(*zth));
+ ztc->flags = ZT_TC_FLAG_TRANSIENT|ZT_TC_FLAG_BUSY;
+ ztc->tch = zth;
+ if (debug)
+ printk("Allocated Transcoder Channel, header is at %p!\n", zth);
+ zth->magic = ZT_TRANSCODE_MAGIC;
+ file->private_data = ztc;
+
+ return 0;
+ }
+
+ if (ztc)
+ kfree(ztc);
+ if (zth)
+ kfree(zth);
+ return -ENOMEM;
+}
+
+static void ztc_release(struct zt_transcoder_channel *ztc)
+{
+ if (!ztc)
+ return;
+
+ ztc->flags &= ~(ZT_TC_FLAG_BUSY);
+ if (ztc->tch)
+ kfree(ztc->tch);
+ ztc->tch = NULL;
+ if (ztc->flags & ZT_TC_FLAG_TRANSIENT)
+ kfree(ztc);
+ if (debug)
+ printk("Released Transcoder!\n");
+}
+
+static int zt_tc_release(struct inode *inode, struct file *file)
+{
+ ztc_release(file->private_data);
+
+ return 0;
+}
+
+static int do_reset(struct zt_transcoder_channel **ztc)
+{
+ struct zt_transcoder_channel *newztc = NULL, *origztc = NULL;
+ struct zt_transcode_header *zth = (*ztc)->tch;
+ struct zt_transcoder *tc;
+ unsigned int x;
+ unsigned int match = 0;
+
+ if (((*ztc)->srcfmt != zth->srcfmt) ||
+ ((*ztc)->dstfmt != zth->dstfmt)) {
+ /* Find new transcoder */
+ spin_lock(&translock);
+ for (tc = trans; tc && !newztc; tc = tc->next) {
+ if (!(tc->srcfmts & zth->srcfmt))
+ continue;
+
+ if (!(tc->dstfmts & zth->dstfmt))
+ continue;
+
+ match = 1;
+ for (x = 0; x < tc->numchannels; x++) {
+ if (tc->channels[x].flags & ZT_TC_FLAG_BUSY)
+ continue;
+
+ newztc = &tc->channels[x];
+ newztc->flags = ZT_TC_FLAG_BUSY;
+ break;
+ }
+ }
+ spin_unlock(&translock);
+
+ if (!newztc)
+ return match ? -EBUSY : -ENOSYS;
+
+ /* Move transcoder header over */
+ origztc = (*ztc);
+ (*ztc) = newztc;
+ (*ztc)->tch = origztc->tch;
+ origztc->tch = NULL;
+ (*ztc)->flags |= (origztc->flags & ~(ZT_TC_FLAG_TRANSIENT));
+ ztc_release(origztc);
+ }
+
+ /* Actually reset the transcoder channel */
+ if ((*ztc)->parent && ((*ztc)->parent->operation))
+ return (*ztc)->parent->operation((*ztc), ZT_TCOP_RESET);
+
+ return -EINVAL;
+}
+
+static int wait_busy(struct zt_transcoder_channel *ztc)
+{
+ int ret;
+
+ for (;;) {
+ if (!(ztc->tch->status & ZT_TC_FLAG_BUSY))
+ return 0;
+ if ((ret = schluffen(&ztc->ready)))
+ return ret;
+ }
+}
+
+static int zt_tc_getinfo(unsigned long data)
+{
+ struct zt_transcode_info info;
+ unsigned int x;
+ struct zt_transcoder *tc;
+
+ if (copy_from_user(&info, (struct zt_transcode_info *) data, sizeof(info)))
+ return -EFAULT;
+
+ spin_lock(&translock);
+ for (tc = trans, x = info.tcnum; tc && x; tc = tc->next, x--);
+ spin_unlock(&translock);
+
+ if (!tc)
+ return -ENOSYS;
+
+ strncpy(info.name, tc->name, sizeof(info.name) - 1);
+ info.srcfmts = tc->srcfmts;
+ info.dstfmts = tc->dstfmts;
+
+ return copy_to_user((struct zt_transcode_info *) data, &info, sizeof(info)) ? -EFAULT : 0;
+}
+
+static int zt_tc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data)
+{
+ int op;
+ int ret;
+ struct zt_transcoder_channel *ztc = file->private_data;
+
+ if (cmd != ZT_TRANSCODE_OP)
+ return -ENOSYS;
+
+ if (get_user(op, (int *) data))
+ return -EFAULT;
+
+ if (debug)
+ printk("ZT Transcode ioctl op = %d!\n", op);
+
+ switch(op) {
+ case ZT_TCOP_GETINFO:
+ ret = zt_tc_getinfo(data);
+ break;
+ case ZT_TCOP_RESET:
+ /* Reset transcoder, possibly changing who we point to */
+ ret = do_reset(&ztc);
+ file->private_data = ztc;
+ break;
+ case ZT_TCOP_TRANSCODE:
+ if (!ztc->parent->operation)
+ return -EINVAL;
+
+ ztc->tch->status |= ZT_TC_FLAG_BUSY;
+ if (!(ret = ztc->parent->operation(ztc, ZT_TCOP_TRANSCODE))) {
+ /* Wait for busy to go away if we're not non-blocking */
+ if (!(file->f_flags & O_NONBLOCK)) {
+ if (!(ret = wait_busy(ztc)))
+ ret = ztc->errorstatus;
+ }
+ } else
+ ztc->tch->status &= ~ZT_TC_FLAG_BUSY;
+ break;
+ default:
+ ret = -ENOSYS;
+ }
+
+ return ret;
+}
+
+static int zt_tc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct zt_transcoder_channel *ztc = file->private_data;
+ unsigned long physical;
+
+ if (!ztc)
+ return -EINVAL;
+
+ /* Do not allow an offset */
+ if (vma->vm_pgoff) {
+ if (debug)
+ printk("zttranscode: Attempted to mmap with offset!\n");
+ return -EINVAL;
+ }
+
+ if ((vma->vm_end - vma->vm_start) != sizeof(struct zt_transcode_header)) {
+ if (debug)
+ printk("zttranscode: Attempted to mmap with size %d != %zd!\n", (int) (vma->vm_end - vma->vm_start), sizeof(struct zt_transcode_header));
+ return -EINVAL;
+ }
+
+ physical = (unsigned long) virt_to_phys(ztc->tch);
+ if (remap_pfn_range(vma, vma->vm_start, physical >> PAGE_SHIFT, sizeof(struct zt_transcode_header), PAGE_SHARED)) {
+ if (debug)
+ printk("zttranscode: remap failed!\n");
+ return -EAGAIN;
+ }
+
+ if (debug)
+ printk("zttranscode: successfully mapped transcoder!\n");
+
+ return 0;
+}
+
+static unsigned int zt_tc_poll(struct file *file, struct poll_table_struct *wait_table)
+{
+ struct zt_transcoder_channel *ztc = file->private_data;
+
+ if (!ztc)
+ return -EINVAL;
+
+ poll_wait(file, &ztc->ready, wait_table);
+ return ztc->tch->status & ZT_TC_FLAG_BUSY ? 0 : POLLPRI;
+}
+
+struct file_operations __zt_transcode_fops = {
+ owner: THIS_MODULE,
+ llseek: NULL,
+ open: zt_tc_open,
+ release: zt_tc_release,
+ ioctl: zt_tc_ioctl,
+ read: NULL,
+ write: NULL,
+ poll: zt_tc_poll,
+ mmap: zt_tc_mmap,
+ flush: NULL,
+ fsync: NULL,
+ fasync: NULL,
+};
+
+int zttranscode_init(void)
+{
+ if (zt_transcode_fops) {
+ printk("Whoa, zt_transcode_fops already set?!\n");
+ return -EBUSY;
+ }
+
+ zt_transcode_fops = &__zt_transcode_fops;
+ printk("Zaptel Transcoder support loaded\n");
+
+ return 0;
+}
+
+void zttranscode_cleanup(void)
+{
+ zt_transcode_fops = NULL;
+ printk("Zaptel Transcoder support unloaded\n");
+}
+
+#ifdef LINUX26
+module_param(debug, int, 0600);
+#else
+MODULE_PARM(debug, "i");
+#endif
+MODULE_DESCRIPTION("Zaptel Transcoder Support");
+MODULE_AUTHOR("Mark Spencer <markster@digium.com>");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+module_init(zttranscode_init);
+module_exit(zttranscode_cleanup);