From 93820c7b1970ab51552deeb444964b900522d5b0 Mon Sep 17 00:00:00 2001 From: kpfleming Date: Fri, 23 Jun 2006 15:11:45 +0000 Subject: add infrastructure for zaptel transcoders git-svn-id: http://svn.digium.com/svn/zaptel/trunk@1162 5390a7c7-147a-4af0-8ec9-7488f05a26cb --- Makefile | 3 +- build_tools/genudevrules | 1 + zaptel.c | 43 ++++- zaptel.h | 206 +++++++++++++++++++--- zttranscode.c | 441 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 664 insertions(+), 30 deletions(-) create mode 100644 zttranscode.c 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 <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 #include +#include +#include + #ifdef CONFIG_ZAPATA_NET #include #endif + #ifdef CONFIG_ZAPATA_PPP #include #include #include #endif -#include #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 @@ -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 #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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DEVFS_FS +#include +#endif +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include +#endif +#ifdef LINUX26 +#include +#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, ""); + ztc->numchannels = numchans; + for (x=0;xnumchannels;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 "); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +module_init(zttranscode_init); +module_exit(zttranscode_cleanup); -- cgit v1.2.3