summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2007-01-31 17:27:30 +0000
committerkpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2007-01-31 17:27:30 +0000
commit92e6d005b1107055cdd62f41fc06bda8e98b14b3 (patch)
tree530476168181ed67fa632f008cca8ac1040eef0a
parentec54d1fb2b09fc3a4027a58317d190a403b148eb (diff)
merge support for the Digium TC400B hardware transcoder
git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.2@2057 5390a7c7-147a-4af0-8ec9-7488f05a26cb
-rw-r--r--Makefile15
-rwxr-xr-xbuild_tools/genudevrules1
-rw-r--r--fxotune.c1
-rw-r--r--fxstest.c1
-rw-r--r--hdlcstress.c1
-rw-r--r--hdlctest.c1
-rw-r--r--patgen.c1
-rw-r--r--patlooptest.c1
-rw-r--r--pattest.c1
-rw-r--r--timertest.c1
-rw-r--r--tonezone.c1
-rw-r--r--torisatool.c1
-rw-r--r--usbfxstest.c1
-rw-r--r--wctc4xxp/Kbuild15
-rw-r--r--wctc4xxp/Makefile10
-rw-r--r--wctc4xxp/base.c1699
-rw-r--r--wctc4xxp/codec_test.c333
-rw-r--r--wctc4xxp/tc400m-firmware.binbin0 -> 1400630 bytes
-rw-r--r--zaptel.c52
-rw-r--r--zaptel.h120
-rw-r--r--zonedata.c1
-rw-r--r--ztcfg.c1
-rw-r--r--ztd-eth.c1
-rw-r--r--ztd-loc.c1
-rw-r--r--ztdiag.c1
-rw-r--r--ztdummy.c1
-rw-r--r--ztdynamic.c1
-rw-r--r--ztmonitor.c1
-rw-r--r--zttool.c1
-rw-r--r--zttranscode.c489
30 files changed, 2746 insertions, 8 deletions
diff --git a/Makefile b/Makefile
index 9f9cd03..e6dceb8 100644
--- a/Makefile
+++ b/Makefile
@@ -107,9 +107,8 @@ MODULES:=zaptel tor2 torisa wcusb wcfxo wctdm wctdm24xxp \
ztdynamic ztd-eth wct1xxp wcte11xp pciradio \
ztd-loc # ztdummy
#MODULES+=wcfxsusb
-# build ztdummy by default for 2.6 kernels
ifeq ($(BUILDVER),linux26)
-MODULES+=ztdummy
+MODULES+=ztdummy zttranscode
endif
MODULE_ALIASES=wcfxs wctdm8xxp wct2xxp
@@ -119,8 +118,8 @@ MODULESKO:=$(MODULES:%=%.ko)
MOD_DESTDIR:=zaptel
obj-m:=$(MODULESO)
-obj-m+=wct4xxp/
-MODULES+=wct4xxp
+obj-m+=wct4xxp/ wctc4xxp/
+MODULES+=wct4xxp wctc4xxp
# Also build xpp in the subdirectory xpp/ . But only for >=2.6.10 and only
# for i386. On other archs the module will probably build but panic.
@@ -158,6 +157,7 @@ wct4xxp/wct4xxp.o:
devel: tor2ee
tests: patgen pattest patlooptest hdlcstress hdlctest hdlcgen hdlcverify timertest
+ $(MAKE) -C wctc4xxp tests CFLAGS="$(CFLAGS) -I.."
tor2.o: tor2-hw.h tor2fw.h
@@ -333,6 +333,7 @@ else
for x in $(MODULESO) wct4xxp/wct4xxp.o; do \
install -D -m 644 $$x $(INSTALL_PREFIX)/lib/modules/$(KVERS)/misc/$$x ; \
done;
+
endif
if ! [ -f wcfxsusb.o ]; then \
rm -f $(INSTALL_PREFIX)/lib/modules/$(KVERS)/misc/wcfxsusb.o; \
@@ -353,7 +354,7 @@ endif
install -m 644 doc/zttool.8 $(INSTALL_PREFIX)/usr/share/man/man8
[ `id -u` = 0 ] && /sbin/depmod -a $(KVERS) || :
[ -f $(CONFIG_FILE) ] || install -D -m 644 zaptel.conf.sample $(CONFIG_FILE)
- build_tools/genmodconf $(BUILDVER) "$(INSTALL_PREFIX)" "$(filter-out zaptel,$(MODULES)) $(MODULE_ALIASES)"
+ build_tools/genmodconf $(BUILDVER) "$(INSTALL_PREFIX)" "$(filter-out zaptel ztdummy zttranscode wctc4xxp,$(MODULES)) $(MODULE_ALIASES)"
@if [ -d /etc/modutils ]; then \
/sbin/update-modules ; \
fi
@@ -364,9 +365,11 @@ firmware:
ifeq ($(HOTPLUG_FIRMWARE),yes)
if [ -d $(INSTALL_PREFIX)/usr/lib/hotplug/firmware ]; then \
install -m 644 wct4xxp/*.ima $(INSTALL_PREFIX)/usr/lib/hotplug/firmware; \
+ install -m 644 wctc4xxp/*.bin $(INSTALL_PREFIX)/usr/lib/hotplug/firmware; \
fi
if [ -d $(INSTALL_PREFIX)/lib/firmware ]; then \
install -m 644 wct4xxp/*.ima $(INSTALL_PREFIX)/lib/firmware; \
+ install -m 644 wctc4xxp/*.bin $(INSTALL_PREFIX)/lib/firmware; \
fi
@echo "Installed firmware"
else
@@ -408,12 +411,14 @@ update:
clean:
rm -f torisatool makefw tor2fw.h radfw.h
rm -f $(BINS)
+ rm -f patgen pattest patlooptest hdlcstress hdlctest hdlcgen hdlcverify timertest
rm -f *.o ztcfg tzdriver sethdlc sethdlc-new
rm -f $(TZOBJS) $(LIBTONEZONE_SO) *.lo
ifeq ($(BUILDVER),linux26)
$(MAKE) -C $(KSRC) SUBDIRS=$(PWD) clean
else
$(MAKE) -C wct4xxp clean
+ $(MAKE) -C wctc4xxp clean
endif
rm -rf .tmp_versions
rm -f gendigits tones.h
diff --git a/build_tools/genudevrules b/build_tools/genudevrules
index a536f75..3ba757d 100755
--- a/build_tools/genudevrules
+++ b/build_tools/genudevrules
@@ -26,5 +26,6 @@ KERNEL${match}"zapctl", NAME="zap/ctl", OWNER="asterisk", GROUP="asterisk", MODE
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"
+KERNEL${match}"zaptranscode", NAME="zap/transcode", OWNER="asterisk", GROUP="asterisk", MODE="0660"
KERNEL${match}"zap[0-9]*", NAME="zap/%n", OWNER="asterisk", GROUP="asterisk", MODE="0660"
EOF
diff --git a/fxotune.c b/fxotune.c
index c5db4d1..94d0912 100644
--- a/fxotune.c
+++ b/fxotune.c
@@ -22,6 +22,7 @@
#include <fcntl.h>
#include <math.h>
+#include <linux/types.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#else
diff --git a/fxstest.c b/fxstest.c
index 922a1a9..820ae3f 100644
--- a/fxstest.c
+++ b/fxstest.c
@@ -5,6 +5,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
+#include <linux/types.h>
#include "zaptel.h"
#include "tonezone.h"
#include "wctdm.h"
diff --git a/hdlcstress.c b/hdlcstress.c
index 87b03fc..3d3d942 100644
--- a/hdlcstress.c
+++ b/hdlcstress.c
@@ -2,6 +2,7 @@
#include <fcntl.h>
#include <string.h>
#include <errno.h>
+#include <linux/types.h>
#include <linux/zaptel.h>
#include <stdio.h>
#include <linux/types.h>
diff --git a/hdlctest.c b/hdlctest.c
index d69aaae..fd74bbb 100644
--- a/hdlctest.c
+++ b/hdlctest.c
@@ -2,6 +2,7 @@
#include <fcntl.h>
#include <string.h>
#include <errno.h>
+#include <linux/types.h>
#include <linux/zaptel.h>
#include <stdio.h>
#include <linux/types.h>
diff --git a/patgen.c b/patgen.c
index a137a2a..38d199c 100644
--- a/patgen.c
+++ b/patgen.c
@@ -10,6 +10,7 @@
#include <stdlib.h>
#include "bittest.h"
+#include <linux/types.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#else
diff --git a/patlooptest.c b/patlooptest.c
index fd1a054..51bec00 100644
--- a/patlooptest.c
+++ b/patlooptest.c
@@ -10,6 +10,7 @@
#include <stdlib.h>
#include <time.h>
+#include <linux/types.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#else
diff --git a/pattest.c b/pattest.c
index e95cc48..a25d265 100644
--- a/pattest.c
+++ b/pattest.c
@@ -10,6 +10,7 @@
#include <stdlib.h>
#include "bittest.h"
+#include <linux/types.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#else
diff --git a/timertest.c b/timertest.c
index df224b2..fd213e5 100644
--- a/timertest.c
+++ b/timertest.c
@@ -8,6 +8,7 @@
#include <sys/time.h>
#include <errno.h>
+#include <linux/types.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#else
diff --git a/tonezone.c b/tonezone.c
index deb1ac1..cb8754a 100644
--- a/tonezone.c
+++ b/tonezone.c
@@ -29,6 +29,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
+#include <linux/types.h>
#include "tonezone.h"
#define DEFAULT_ZT_DEV "/dev/zap/ctl"
diff --git a/torisatool.c b/torisatool.c
index 43dc7a9..75d54c7 100644
--- a/torisatool.c
+++ b/torisatool.c
@@ -28,6 +28,7 @@
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
+#include <linux/types.h>
#include "zaptel.h"
#include "torisa.h"
diff --git a/usbfxstest.c b/usbfxstest.c
index 23cd680..45c9b93 100644
--- a/usbfxstest.c
+++ b/usbfxstest.c
@@ -6,6 +6,7 @@
#include <unistd.h>
#include <sys/ioctl.h>
#include "zap.h"
+#include <linux/types.h>
#include <linux/zaptel.h>
int main(int argc, char *argv[])
diff --git a/wctc4xxp/Kbuild b/wctc4xxp/Kbuild
new file mode 100644
index 0000000..b034819
--- /dev/null
+++ b/wctc4xxp/Kbuild
@@ -0,0 +1,15 @@
+obj-m += wctc4xxp.o
+
+EXTRA_CFLAGS := -I$(src)/.. -Wno-undef
+
+wctc4xxp-objs := base.o
+
+ifneq ($(HOTPLUG_FIRMWARE),yes)
+wctc4xxp-objs += firmware_tc400m.o
+endif
+
+$(obj)/base.o: $(src)/../zaptel.h
+
+$(obj)/firmware_tc400m.o: $(src)/tc400m-firmware.bin $(obj)/base.o
+ @echo Making firmware object file for $(notdir $<)
+ @cd $(src) && ../build_tools/make_firmware_object $(notdir $<) $@ $(obj)/base.o
diff --git a/wctc4xxp/Makefile b/wctc4xxp/Makefile
new file mode 100644
index 0000000..51770a9
--- /dev/null
+++ b/wctc4xxp/Makefile
@@ -0,0 +1,10 @@
+ifneq ($(KBUILD_EXTMOD),)
+
+include $(obj)/Kbuild
+
+endif
+
+tests: codec_test
+
+codec_test: codec_test.c ../zaptel.h
+ $(CC) -o $@ $< $(CFLAGS)
diff --git a/wctc4xxp/base.c b/wctc4xxp/base.c
new file mode 100644
index 0000000..df87d43
--- /dev/null
+++ b/wctc4xxp/base.c
@@ -0,0 +1,1699 @@
+/*
+ * Wildcard TC400B Interface Driver for Zapata Telephony interface
+ *
+ * Written by John Sloan <jsloan@digium.com>
+ * and Kevin P. Fleming <kpfleming@digium.com>
+ *
+ * Copyright (C) 2006-2007, 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/pci.h>
+#include <linux/interrupt.h>
+#include <linux/mman.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/list.h>
+
+#ifdef STANDALONE_ZAPATA
+#include "zaptel.h"
+#else
+#include <linux/zaptel.h>
+#endif
+
+#if defined(HOTPLUG_FIRMWARE)
+static const char *tc400m_firmware = "tc400m-firmware.bin";
+#else
+extern u8 _binary_tc400m_firmware_bin_start[];
+extern void _binary_tc400m_firmware_bin_size;
+#endif
+
+static struct pci_driver driver;
+
+#define module_printk(fmt, args...) printk("%s: " fmt, driver.name, ## args)
+#define debug_printk(test, fmt, args...) if (debug && (test)) printk("%s (%s): " fmt, driver.name, __FUNCTION__, ## args)
+
+/* #define USE_TEST_HW */
+#define USE_TDM_CONFIG
+
+#define WC_MAX_IFACES 8
+
+/* NUM_CHANNELS must be checked if new firmware (dte_firm.h) is used */
+#define NUM_CHANNELS 97
+
+#define DTE_FORMAT_ULAW 0x00
+#define DTE_FORMAT_G723_1 0x04
+#define DTE_FORMAT_ALAW 0x08
+#define DTE_FORMAT_G729A 0x12
+#define DTE_FORMAT_UNDEF 0xFF
+
+#define G729_LENGTH 20
+#define G723_LENGTH 30
+
+#define G729_SAMPLES 160
+#define G723_SAMPLES 240
+
+#define G729_BYTES 20
+#define G723_BYTES 20
+
+/* 274 for 30ms ulaw, 194 for 20ms ulaw */
+#define OTHER_CMD_LEN 300
+
+#define MAX_COMMAND_LEN OTHER_CMD_LEN
+
+#define ERING_SIZE (NUM_CHANNELS / 2) /* Maximum ring size */
+
+#define SFRAME_SIZE MAX_COMMAND_LEN
+
+#define PCI_WINDOW_SIZE ((2 * 2 * ERING_SIZE * SFRAME_SIZE) + (2 * ERING_SIZE * 4))
+
+#define MDIO_SHIFT_CLK 0x10000
+#define MDIO_DATA_WRITE0 0x00000
+#define MDIO_DATA_WRITE1 0x20000
+#define MDIO_ENB 0x00000
+#define MDIO_ENB_IN 0x40000
+#define MDIO_DATA_READ 0x80000
+
+#define RCV_CSMENCAPS 1
+#define RCV_RTP 2
+#define RCV_CSMENCAPS_ACK 3
+#define RCV_OTHER 99
+
+/* TDM Commands */
+#define CMD_MSG_TDM_SELECT_BUS_MODE(s) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x01, 0x00,0x06,0x17,0x04, 0xFF,0xFF, \
+ 0x04,0x00 }
+#define CMD_MSG_TDM_ENABLE_BUS(s) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x02, 0x00,0x06,0x05,0x04, 0xFF,0xFF, \
+ 0x04,0x00 }
+#define CMD_MSG_SUPVSR_SETUP_TDM_PARMS(s,p1,p2,p3) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x10, p1, 0x00,0x06,0x07,0x04, 0xFF,0xFF, \
+ p2,0x83, 0x00,0x0C, 0x00,0x00, p3,0x00 }
+#define CMD_MSG_TDM_OPT(s) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x35,0x04, 0xFF,0xFF, \
+ 0x00,0x00 }
+#define CMD_MSG_DEVICE_SET_COUNTRY_CODE(s) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x1B,0x04, 0xFF,0xFF, \
+ 0x00,0x00 }
+
+/* CPU Commands */
+#define CMD_MSG_SET_ARM_CLK(s) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0C, 0x00, 0x00,0x06,0x11,0x04, 0x00,0x00, \
+ 0x2C,0x01, 0x00,0x00 }
+#define CMD_MSG_SET_SPU_CLK(s) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0C, 0x00, 0x00,0x06,0x12,0x04, 0x00,0x00, \
+ 0x2C,0x01, 0x00,0x00 }
+#define CMD_MSG_SPU_FEATURES_CONTROL(s,p1) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x13,0x00, 0xFF,0xFF, \
+ p1,0x00 }
+#define CMD_MSG_DEVICE_STATUS_CONFIG(s) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x0F,0x04, 0xFF,0xFF, \
+ 0x05,0x00 }
+
+/* General IP/RTP Commands */
+#define CMD_MSG_SET_ETH_HEADER(s) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x18, 0x00, 0x00,0x06,0x00,0x01, 0xFF,0xFF, \
+ 0x01,0x00, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x00,0x11,0x22,0x33,0x44,0x55, 0x08,0x00 }
+#define CMD_MSG_IP_SERVICE_CONFIG(s) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x02,0x03, 0xFF,0xFF, \
+ 0x00,0x02 }
+#define CMD_MSG_ARP_SERVICE_CONFIG(s) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x05,0x01, 0xFF,0xFF, \
+ 0x01,0x00 }
+#define CMD_MSG_ICMP_SERVICE_CONFIG(s) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x04,0x03, 0xFF,0xFF, \
+ 0x01,0xFF }
+#define CMD_MSG_IP_OPTIONS(s) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x06,0x03, 0xFF,0xFF, \
+ 0x02,0x00 }
+
+/* Supervisor channel commands */
+#define CMD_MSG_CREATE_CHANNEL(s,t) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0C, 0x00, 0x00,0x06,0x10,0x00, 0x00,0x00, \
+ 0x02,0x00, (t&0x00FF), ((t&0xFF00) >> 8) }
+#define CMD_MSG_TRANS_CONNECT(s,e,c1,c2,f1,f2) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x12, 0x00, 0x00,0x06,0x22,0x93, 0x00,0x00, \
+ e,0x00, (c1&0x00FF),((c1&0xFF00)>>8), f1,0x00, (c2&0x00FF),((c2&0xFF00)>>8), f2,0x00 }
+#define CMD_MSG_DESTROY_CHANNEL(s,t) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, 0xFF,0xFF, 0x0A, 0x00, 0x00,0x06,0x11,0x00, 0x00,0x00, \
+ (t&0x00FF),((t&0xFF00)>>8), 0x00, 0x00 }
+
+/* Individual channel config commands */
+#define CMD_MSG_SET_IP_HDR_CHANNEL(s,c,t2,t1) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, ((c&0xFF00) >> 8),(c&0x00FF), 0x26, 0x00, 0x00,0x02,0x00,0x90, 0x00,0x00, \
+ 0x00,0x00, 0x45,0x00, 0x00,0x00, 0x00,0x00, 0x40,0x00, 0x80,0x11, 0x00,0x00, \
+ 0xC0,0xA8,0x09,0x03, 0xC0,0xA8,0x09,0x03, \
+ ((t2&0xFF00)>>8)+0x50,(t2&0x00FF), ((t1&0xFF00)>>8)+0x50,(t1&0x00FF), 0x00,0x00, 0x00,0x00 }
+#define CMD_MSG_VOIP_VCEOPT(s,c,l,w) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, ((c&0xFF00)>>8),(c&0x00FF), 0x12, 0x00, 0x00,0x02,0x01,0x80, 0x00,0x00, \
+ 0x21,l, 0x00,0x1C, 0x04,0x00, 0x00,0x00, w,0x00, 0x80,0x11 }
+#define CMD_MSG_VOIP_VOPENA(s,c,f) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, ((c&0xFF00)>>8),(c&0x00FF), 0x16, 0x00, 0x00,0x02,0x00,0x80, 0x00,0x00, \
+ 0x01,0x00, 0x80,f, 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x12,0x34, 0x56,0x78, 0x00,0x00 }
+#define CMD_MSG_VOIP_VOPENA_CLOSE(s,c) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, ((c&0xFF00)>>8),(c&0x00FF), 0x0A, 0x00, 0x00,0x02,0x00,0x80, 0x00,0x00, \
+ 0x00,0x00, 0x00,0x00 }
+#define CMD_MSG_VOIP_INDCTRL(s,c) {0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s&0x0F, 0x01, ((c&0xFF00)>>8),(c&0x00FF), 0x0A, 0x00, 0x00,0x02,0x84,0x80, 0x00,0x00, \
+ 0x07,0x00, 0x00,0x00 }
+
+/* CPU ACK command */
+#define CMD_MSG_ACK(s,c) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x88,0x9B, \
+ 0x00,0x01, s, 0xE0, (c&0x00FF), ((c>>8)&0x00FF) }
+
+/* Wrapper for RTP packets */
+#define CMD_MSG_IP_UDP_RTP(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20) { \
+ 0x00,0x11,0x22,0x33,0x44,0x55, 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, 0x08,0x00, \
+ 0x45,0x00, p1,p2, 0x00,p3, 0x40,0x00, 0x80,0x11, p4,p5, \
+ 0xC0,0xA8,0x09,0x03, 0xC0,0xA8,0x09,0x03, p6,p7, p8,p9, p10,p11, p12,p13, \
+ 0x80,p14, p15,p16, p17,p18,p19,p20, 0x12,0x34,0x56,0x78}
+
+struct cmdq {
+ struct list_head list;
+ size_t cmdspace;
+ size_t cmdlen;
+ u8 cmd[0];
+};
+
+#define MAX_PACKET_SIZE 1500
+#define MAX_TOTAL_CMDQ 40
+
+struct wcdte {
+ struct pci_dev *dev;
+ const char *variety;
+ unsigned int intcount;
+ unsigned int rxints;
+ unsigned int txints;
+ unsigned int intmask;
+ int pos;
+ int freeregion;
+ int rdbl;
+ int tdbl;
+ spinlock_t reglock;
+ wait_queue_head_t regq;
+ int rcvflags;
+
+ struct semaphore chansem;
+ struct semaphore cmdqsem;
+ struct list_head pending_cmdq;
+ struct list_head free_cmdq;
+ u32 total_cmdq; /* total of all cmdq entries, both pending and free */
+
+ unsigned int last_command_sent;
+ unsigned int last_rcommand;
+ unsigned int last_rparm2;
+ unsigned int seq_num;
+ long timeout;
+
+ unsigned int ztsnd_rtx;
+ unsigned int ztsnd_0010_rtx;
+
+ unsigned int numchannels;
+ unsigned char complexname[40];
+
+ unsigned long iobase;
+ dma_addr_t readdma;
+ dma_addr_t writedma;
+ dma_addr_t descripdma;
+ volatile u8 *writechunk; /* write buffers */
+ volatile u8 *readchunk; /* read buffers */
+ volatile u32 *descripchunk; /* descriptors */
+
+ int wqueints;
+ struct workqueue_struct *dte_wq;
+ struct work_struct dte_work;
+
+ struct zt_transcoder *uencode;
+ struct zt_transcoder *udecode;
+};
+
+struct wcdte_desc {
+ const char *name;
+ int flags;
+};
+
+static const struct wcdte_desc wcdte = { "Wildcard TC400P+TC400M", 0 };
+
+static struct wcdte *ifaces[WC_MAX_IFACES];
+
+struct dte_state {
+ int encoder;
+ struct wcdte *wc;
+
+ unsigned int timestamp;
+ unsigned int seqno;
+
+ unsigned int timeslot_in_num;
+ unsigned int timeslot_out_num;
+
+ unsigned int chan_in_num;
+ unsigned int chan_out_num;
+
+ unsigned int packets_sent;
+ unsigned int packets_received;
+
+ unsigned int last_dte_seqno;
+ unsigned int dte_seqno_rcv;
+};
+
+static int debug = 0;
+static char *mode[2] = { "g729", "g723" };
+static int mode_count = 2;
+u32 debug_packets = 0;
+u32 debug_cmd_packets = 0;
+
+static int create_channel(struct wcdte *wc, int simple, int complicated, int part1_id, int part2_id, unsigned int *dte_chan1, unsigned int *dte_chan2);
+static int destroy_channel(struct wcdte *wc, unsigned int chan1, unsigned int chan2);
+
+/* Sanity check values */
+static inline int 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;
+}
+
+static void dump_cmdq(struct wcdte *wc)
+{
+ struct cmdq *cmdq;
+
+ debug_printk(1, "pending_cmdq: ");
+ list_for_each_entry(cmdq, &wc->pending_cmdq, list)
+ printk("%p(%zd) ", cmdq, cmdq->cmdspace);
+ printk("\n");
+
+ debug_printk(1, "free_cmdq: ");
+ list_for_each_entry(cmdq, &wc->free_cmdq, list)
+ printk("%p(%zd) ", cmdq, cmdq->cmdspace);
+ printk("\n");
+}
+
+static struct cmdq *get_free_cmdq(struct wcdte *wc, size_t size_needed)
+{
+ struct cmdq *winner = NULL;
+ struct cmdq *candidate = NULL;
+ size_t candidate_size = MAX_PACKET_SIZE;
+ struct cmdq *smallest_seen = NULL;
+ size_t smallest_seen_size = MAX_PACKET_SIZE;
+ struct cmdq *entry;
+
+ size_needed = ((size_needed / 16) + 1) * 16;
+
+ if (size_needed > MAX_PACKET_SIZE)
+ return NULL;
+
+ list_for_each_entry(entry, &wc->free_cmdq, list) {
+ if (entry->cmdspace == size_needed) {
+ winner = entry;
+ break;
+ } else if ((entry->cmdspace > size_needed) &&
+ (entry->cmdspace < candidate_size)) {
+ candidate = entry;
+ candidate_size = entry->cmdspace;
+ } else if (entry->cmdspace < smallest_seen_size) {
+ smallest_seen = entry;
+ smallest_seen_size = entry->cmdspace;
+ }
+ }
+
+ /* at this point, we either have a winner, a candidate,
+ a potentially freeable too-small entry, or nothing...
+ deal with the results
+ */
+
+ if (winner) {
+ list_del_init(&winner->list);
+ return winner;
+ } else if (candidate) {
+ list_del_init(&candidate->list);
+ return candidate;
+ } else if (wc->total_cmdq < MAX_TOTAL_CMDQ) {
+ /* we can make a new entry */
+ if (debug)
+ dump_cmdq(wc);
+ if ((winner = kmalloc(sizeof(*winner) + size_needed, GFP_KERNEL))) {
+ debug_printk(1, "created a '%zd' byte cmdq entry at '%p'\n", size_needed, winner);
+ winner->cmdspace = size_needed;
+ INIT_LIST_HEAD(&winner->list);
+ }
+ return winner;
+ } else if (smallest_seen) {
+ /* we can't allocate new entries, but we have a
+ too-small entry we can free and replace */
+ if (debug)
+ dump_cmdq(wc);
+ list_del(&smallest_seen->list);
+ kfree(smallest_seen);
+ if ((winner = kmalloc(sizeof(*winner) + size_needed, GFP_KERNEL))) {
+ debug_printk(1, "replaced a '%zd' byte cmdq entry at '%p' with a '%zd' byte one at '%p'\n", smallest_seen_size, smallest_seen, size_needed, winner);
+ winner->cmdspace = size_needed;
+ INIT_LIST_HEAD(&winner->list);
+ }
+ return winner;
+ } else {
+ /* we failed */
+ debug_printk(1, "no cmdq entries available\n");
+ return NULL;
+ }
+}
+
+static int queue_cmd(struct wcdte *wc, u8 *data, size_t length)
+{
+ struct cmdq *cmdq = get_free_cmdq(wc, length);
+
+ if (!cmdq)
+ return -1;
+
+ cmdq->cmdlen = length;
+ memcpy(cmdq->cmd, data, length);
+ list_add_tail(&cmdq->list, &wc->pending_cmdq);
+
+ return 0;
+}
+
+#define send_cmd(wc, command, length, hex) \
+ ({ \
+ int ret = 0; \
+ u8 fifo[] = command; \
+ do { \
+ if (ret == 2) { \
+ wc->ztsnd_rtx++; \
+ if (hex == 0x0010) \
+ wc->ztsnd_0010_rtx++; \
+ } \
+ down(&wc->cmdqsem); \
+ queue_cmd(wc, fifo, sizeof(fifo)); \
+ wc->last_command_sent = hex; \
+ __transmit_demand(wc); \
+ up(&wc->cmdqsem); \
+ ret = waitfor_csmencaps(wc, RCV_CSMENCAPS, 0); \
+ if (ret == 1) \
+ return 1; \
+ } while (ret == 2); \
+ })
+
+static void dte_init_state(struct dte_state *state_ptr, int encoder, unsigned int channel, struct wcdte *wc)
+{
+ memset(state_ptr, 0, sizeof(*state_ptr));
+
+ state_ptr->encoder = encoder;
+ state_ptr->wc = wc;
+
+ state_ptr->chan_in_num = 999;
+ state_ptr->chan_out_num = 999;
+
+ if (encoder) {
+ state_ptr->timeslot_in_num = channel * 2;
+ state_ptr->timeslot_out_num = channel * 2 + 1;
+ } else {
+ state_ptr->timeslot_in_num = channel * 2 + 1;
+ state_ptr->timeslot_out_num = channel * 2;
+ }
+}
+
+static inline unsigned int zapfmt_to_dtefmt(unsigned int fmt)
+{
+ switch (fmt) {
+ case ZT_FORMAT_G723_1:
+ return DTE_FORMAT_G723_1;
+ case ZT_FORMAT_ULAW:
+ return DTE_FORMAT_ULAW;
+ case ZT_FORMAT_ALAW:
+ return DTE_FORMAT_ALAW;
+ case ZT_FORMAT_G729A:
+ return DTE_FORMAT_G729A;
+ default:
+ return DTE_FORMAT_UNDEF;
+ }
+}
+
+static inline void __setctl(struct wcdte *wc, unsigned int addr, unsigned int val)
+{
+ outl(val, wc->iobase + addr);
+}
+
+static inline unsigned int __getctl(struct wcdte *wc, unsigned int addr)
+{
+ return inl(wc->iobase + addr);
+}
+
+static inline void setctl(struct wcdte *wc, unsigned int addr, unsigned int val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wc->reglock, flags);
+ __setctl(wc, addr, val);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+}
+
+static inline unsigned int getctl(struct wcdte *wc, unsigned int addr)
+{
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(&wc->reglock, flags);
+ val = __getctl(wc, addr);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+ return val;
+}
+
+static inline void reinit_descriptor(struct wcdte *wc, int tx, int dbl, char *s)
+{
+ unsigned int o2;
+
+ o2 = dbl * 4;
+
+ if (!tx)
+ o2 += ERING_SIZE * 4;
+ wc->descripchunk[o2] = cpu_to_le32(0x80000000);
+
+ setctl(wc, 0x0008, 0x00000000);
+}
+
+static inline void __transmit_one(struct wcdte *wc, u8 *data, size_t length)
+{
+ u32 o2 = wc->tdbl * 4;
+ volatile u8 *writechunk = (volatile u8 *) wc->writechunk + (wc->tdbl * SFRAME_SIZE);
+ size_t xmt_length;
+
+ /* Yes... this is a busy loop, that is not interruptible. However, it is
+ highly unlikely (and testing proves) that the wait for a descriptor
+ to become available will ever be long enough for this to be an issue.
+ */
+ do {} while ((le32_to_cpu(wc->descripchunk[o2]) & 0x80000000));
+
+ xmt_length = max(length, (size_t) 64);
+
+ wc->descripchunk[o2 + 1] = cpu_to_le32((le32_to_cpu(wc->descripchunk[o2 + 1]) & 0xFBFFF800) | xmt_length);
+
+ memcpy((void *) writechunk, data, length);
+ if (length < xmt_length)
+ memset((void *) writechunk + length, 0, xmt_length - length);
+
+ wc->descripchunk[o2] = cpu_to_le32(0x80000000);
+ setctl(wc, 0x0008, 0x00000000); /* Transmit Poll Demand */
+
+ wc->tdbl = (wc->tdbl + 1) % ERING_SIZE;
+}
+
+static inline int __transmit_demand(struct wcdte *wc)
+{
+ int i;
+ unsigned int reg;
+ struct cmdq *cmdq;
+
+ reg = getctl(wc, 0x0028) & 0x00700000;
+
+ /* Nothing to transmit */
+ if (list_empty(&wc->pending_cmdq))
+ return 1;
+
+ /* pop the first entry off the list */
+ cmdq = list_entry(wc->pending_cmdq.next, struct cmdq, list);
+ list_del_init(&cmdq->list);
+
+ debug_printk(1, "transmitting command at '%p' of '%zd' bytes\n", cmdq, cmdq->cmdlen);
+
+ __transmit_one(wc, cmdq->cmd, cmdq->cmdlen);
+
+ if (debug_packets) {
+ debug_printk(1, "TX: ");
+ for (i = 0; i < min((size_t) debug_packets, cmdq->cmdlen); i++)
+ printk("%02X ", cmdq->cmd[i]);
+ printk("\n");
+ }
+
+ if (debug_cmd_packets &&
+ (cmdq->cmd[12] == 0x88) &&
+ (cmdq->cmd[13] == 0x9B)) {
+ debug_printk(1, "TX: ");
+ for (i = 0; i < min((size_t) debug_cmd_packets, cmdq->cmdlen); i++)
+ printk("%02X ", cmdq->cmd[i]);
+ printk("\n");
+ }
+
+ list_add_tail(&cmdq->list, &wc->free_cmdq);
+
+ return 0;
+}
+
+static inline int transmit_demand(struct wcdte *wc)
+{
+ int val;
+
+ down(&wc->cmdqsem);
+ val = __transmit_demand(wc);
+ up(&wc->cmdqsem);
+
+ return val;
+}
+
+static int dte_operation(struct zt_transcoder_channel *ztc, int op)
+{
+ struct zt_transcoder_channel *compl_ztc;
+ struct dte_state *st = ztc->pvt, *compl_st;
+ struct zt_transcode_header *zth = ztc->tch;
+ struct wcdte *wc = st->wc;
+ unsigned char *chars;
+ unsigned int inbytes = 0;
+ unsigned int timestamp_inc = 0;
+ int res = 0;
+ u32 ipchksum;
+
+ switch (op) {
+ case ZT_TCOP_ALLOCATE:
+ if (ztc->chan_built)
+ break;
+ down(&wc->chansem);
+ if (st->encoder)
+ create_channel(wc, zapfmt_to_dtefmt(zth->srcfmt), zapfmt_to_dtefmt(zth->dstfmt),
+ st->timeslot_in_num, st->timeslot_out_num, &(st->chan_in_num),
+ &(st->chan_out_num));
+ else
+ create_channel(wc, zapfmt_to_dtefmt(zth->dstfmt), zapfmt_to_dtefmt(zth->srcfmt),
+ st->timeslot_out_num, st->timeslot_in_num, &(st->chan_out_num),
+ &(st->chan_in_num));
+ /* Mark this channel as built */
+ ztc->chan_built = 1;
+ ztc->built_fmts = zth->dstfmt | zth->srcfmt;
+
+ /* Mark the channel complement (other half of encoder/decoder pair) as built */
+ if (st->encoder)
+ compl_ztc = &(wc->udecode->channels[st->timeslot_in_num >> 1]);
+ else
+ compl_ztc = &(wc->uencode->channels[st->timeslot_in_num >> 1]);
+ compl_ztc->chan_built = 1;
+ compl_ztc->built_fmts = zth->dstfmt | zth->srcfmt;
+ compl_st = compl_ztc->pvt;
+ compl_st->chan_in_num = st->chan_out_num;
+ compl_st->chan_out_num = st->chan_in_num;
+ up(&wc->chansem);
+ break;
+ case ZT_TCOP_RELEASE:
+ down(&wc->chansem);
+
+ if (st->encoder)
+ compl_ztc = &(wc->udecode->channels[st->timeslot_in_num >> 1]);
+ else
+ compl_ztc = &(wc->uencode->channels[st->timeslot_in_num >> 1]);
+
+ /* If the channel complement (other half of the encoder/decoder pair) is not being used... */
+ if (!compl_ztc->busy) {
+ if (st->encoder)
+ destroy_channel(wc, st->chan_in_num, st->chan_out_num);
+ else
+ destroy_channel(wc, st->chan_out_num, st->chan_in_num);
+
+ /* Mark this channel as not built */
+ ztc->chan_built = 0;
+ ztc->built_fmts = 0;
+ st->chan_in_num = 999;
+ st->chan_out_num = 999;
+
+ /* Mark the channel complement as not built */
+ compl_ztc->chan_built = 0;
+ compl_ztc->built_fmts = 0;
+ compl_st = compl_ztc->pvt;
+ compl_st->chan_in_num = 999;
+ compl_st->chan_out_num = 999;
+ }
+ st->dte_seqno_rcv = 0;
+ up(&wc->chansem);
+ break;
+ case ZT_TCOP_TRANSCODE:
+ if ((((zth->srcfmt == ZT_FORMAT_ULAW) || (zth->srcfmt == ZT_FORMAT_ALAW)) &&
+ ((zth->dstfmt == ZT_FORMAT_G729A && zth->srclen >= G729_SAMPLES) ||
+ (zth->dstfmt == ZT_FORMAT_G723_1 && zth->srclen >= G723_SAMPLES))) ||
+ ((zth->srcfmt == ZT_FORMAT_G729A) && (zth->srclen >= G729_BYTES)) ||
+ ((zth->srcfmt == ZT_FORMAT_G723_1) && (zth->srclen >= G723_BYTES))) {
+ struct cmdq *cmdq;
+
+ do {
+ chars = (u8 *)(zth->srcdata + zth->srcoffset);
+
+ switch (zth->srcfmt) {
+ case ZT_FORMAT_ULAW:
+ case ZT_FORMAT_ALAW:
+ switch (zth->dstfmt) {
+ case ZT_FORMAT_G729A:
+ inbytes = G729_SAMPLES;
+ timestamp_inc = G729_SAMPLES;
+ break;
+ case ZT_FORMAT_G723_1:
+ inbytes = G723_SAMPLES;
+ timestamp_inc = G723_SAMPLES;
+ break;
+ }
+ break;
+ case ZT_FORMAT_G729A:
+ inbytes = G729_BYTES;
+ timestamp_inc = G729_SAMPLES;
+ break;
+ case ZT_FORMAT_G723_1:
+ inbytes = G723_BYTES;
+ timestamp_inc = G723_SAMPLES;
+ break;
+ }
+
+ zth->srclen -= inbytes;
+
+ {
+ u8 fifo[] = CMD_MSG_IP_UDP_RTP(
+ ((inbytes+40) >> 8) & 0xFF,
+ (inbytes+40) & 0xFF,
+ st->seqno & 0xFF,
+ 0x00,
+ 0x00,
+ (((st->timeslot_out_num) >> 8)+0x50) & 0xFF,
+ (st->timeslot_out_num) & 0xFF,
+ (((st->timeslot_in_num) >> 8)+0x50) & 0xFF,
+ (st->timeslot_in_num) & 0xFF,
+ ((inbytes+20) >> 8) & 0xFF,
+ (inbytes+20) & 0xFF,
+ 0x00,
+ 0x00,
+ zapfmt_to_dtefmt(zth->srcfmt),
+ ((st->seqno) >> 8) & 0xFF,
+ (st->seqno) & 0xFF,
+ ((st->timestamp) >> 24) & 0xFF,
+ ((st->timestamp) >> 16) & 0xFF,
+ ((st->timestamp) >> 8) & 0xFF,
+ (st->timestamp) & 0xFF);
+
+ ipchksum = 0x9869 + (fifo[16] << 8) + fifo[17] + (fifo[18] << 8) + fifo[19];
+ while (ipchksum >> 16)
+ ipchksum = (ipchksum & 0xFFFF) + (ipchksum >> 16);
+ ipchksum = (~ipchksum) & 0xFFFF;
+
+ fifo[24] = ipchksum >> 8;
+ fifo[25] = ipchksum & 0xFF;
+
+ st->seqno += 1;
+ st->timestamp += timestamp_inc;
+
+ down(&wc->cmdqsem);
+
+ if (!(cmdq = get_free_cmdq(wc, sizeof(fifo) + inbytes))) {
+ up(&wc->cmdqsem);
+ res = -EIO;
+ break;
+ }
+
+ memcpy(cmdq->cmd, fifo, sizeof(fifo));
+ memcpy(cmdq->cmd + sizeof(fifo), chars, inbytes);
+ cmdq->cmdlen = sizeof(fifo) + inbytes;
+ list_add_tail(&cmdq->list, &wc->pending_cmdq);
+ __transmit_demand(wc);
+ up(&wc->cmdqsem);
+ st->packets_sent++;
+ zth->srcoffset += inbytes;
+ }
+ } while ((((zth->srcfmt == ZT_FORMAT_ULAW) || (zth->srcfmt == ZT_FORMAT_ALAW)) &&
+ ((zth->dstfmt == ZT_FORMAT_G729A && zth->srclen >= G729_SAMPLES) ||
+ (zth->dstfmt == ZT_FORMAT_G723_1 && zth->srclen >= G723_SAMPLES))) ||
+ ((zth->srcfmt == ZT_FORMAT_G729A) && (zth->srclen >= G729_BYTES)) ||
+ ((zth->srcfmt == ZT_FORMAT_G723_1) && (zth->srclen >= G723_BYTES)));
+ } else {
+ zt_transcoder_alert(ztc);
+ res = -EINVAL;
+ }
+
+ break;
+ }
+
+ return res;
+}
+
+static void stop_dma(struct wcdte *wc);
+
+static inline void receiveprep(struct wcdte *wc, int dbl)
+{
+ volatile u8 *readchunk = wc->readchunk + (dbl * SFRAME_SIZE);
+ struct zt_transcoder_channel *ztc = NULL;
+ struct zt_transcode_header *zth = NULL;
+ struct dte_state *st = NULL;
+ int o2, i;
+ unsigned char rseq, rcodec;
+ unsigned int rcommand, rchannel, rlen, rtp_rseq, rtp_eseq;
+ u8 *chars = NULL;
+ unsigned int ztc_ndx;
+
+ o2 = dbl * 4;
+ o2 += ERING_SIZE * 4;
+
+ if (debug_packets) {
+ debug_printk(1, "RX: ");
+ for (i = 0; i < debug_packets; i++)
+ printk("%02X ", readchunk[i]);
+ printk("\n");
+ }
+
+ if ((readchunk[12] == 0x88) && (readchunk[13] == 0x9B)) {
+ /* Control in packet */
+ if (debug_cmd_packets) {
+ debug_printk(1, "RX: ");
+ for (i = 0; i < debug_cmd_packets; i++)
+ printk("%02X ", readchunk[i]);
+ printk("\n");
+ }
+ /* See if message must be ACK'd */
+ if ((readchunk[17] & 0xC0) == 0) {
+ rcommand = readchunk[24] | (readchunk[25] << 8);
+ rchannel = readchunk[18] | (readchunk[19] << 8);
+ rseq = readchunk[16];
+ {
+ u8 fifo[] = CMD_MSG_ACK(rseq++, rchannel);
+
+ down(&wc->cmdqsem);
+ queue_cmd(wc, fifo, sizeof(fifo));
+ __transmit_demand(wc);
+ wc->rcvflags = RCV_CSMENCAPS;
+ wc->last_rcommand = rcommand;
+ wc->last_rparm2 = readchunk[30] | (readchunk[31] << 8);
+ wake_up_interruptible(&wc->regq);
+ up(&wc->cmdqsem);
+ }
+ } else {
+ wc->rcvflags = RCV_CSMENCAPS_ACK;
+ wake_up_interruptible(&wc->regq);
+ }
+ } else if ((readchunk[12] == 0x08) && (readchunk[13] == 0x00) &&
+ (readchunk[50] == 0x12) && (readchunk[51] == 0x34) &&
+ (readchunk[52] = 0x56) && (readchunk[53] == 0x78)) {
+ /* IP/UDP in packet */
+ rchannel = (readchunk[37] | (readchunk[36] << 8)) - 0x5000;
+ rlen = (readchunk[39] | (readchunk[38] << 8)) - 20;
+ rtp_rseq = (readchunk[45] | (readchunk[44] << 8));
+ rcodec = readchunk[43];
+
+ ztc_ndx = rchannel >> 1;
+
+ if (ztc_ndx >= wc->numchannels) {
+ debug_printk(1, "Invalid channel number received (ztc_ndx = %d) (numchannels = %d)\n", ztc_ndx, wc->numchannels);
+ rcodec = DTE_FORMAT_UNDEF;
+ }
+
+ switch (rcodec) {
+ case 0x00: /* ulaw */
+ case 0x08: /* alaw */
+ ztc = &(wc->udecode->channels[ztc_ndx]);
+ break;
+ case 0x04: /* g.723.1 */
+ case 0x12: /* g.729 */
+ ztc = &(wc->uencode->channels[ztc_ndx]);
+ break;
+ }
+
+ zth = ztc->tch;
+ st = ztc->pvt;
+
+ if (!zth) {
+ debug_printk(1, "Tried to put data into a freed zth header\n");
+ rcodec = DTE_FORMAT_UNDEF;
+ } else {
+ chars = (u8 *)(zth->dstdata + zth->dstoffset + zth->dstlen);
+ st->packets_received++;
+ }
+
+ if (st->dte_seqno_rcv == 0) {
+ st->dte_seqno_rcv = 1;
+ st->last_dte_seqno = rtp_rseq;
+ } else {
+ rtp_eseq = (st->last_dte_seqno + 1) & 0xFFFF;
+ debug_printk(rtp_rseq != rtp_eseq, "Bad seqno from module [%d][%d][%d]\n", rchannel, rtp_rseq, st->last_dte_seqno);
+ st->last_dte_seqno = rtp_rseq;
+ }
+
+ switch (rcodec) {
+ case 0x00: /* ulaw */
+ case 0x08: /* alaw */
+ if (sanitycheck(zth, rlen) &&
+ ((zth->srcfmt == ZT_FORMAT_G729A && rlen == G729_SAMPLES) ||
+ (zth->srcfmt == ZT_FORMAT_G723_1 && rlen == G723_SAMPLES))) {
+ memcpy(chars, (void *) readchunk + 54, rlen);
+ zth->dstlen += rlen;
+ zth->dstsamples = zth->dstlen;
+ } else {
+ ztc->errorstatus = -EOVERFLOW;
+ }
+ zt_transcoder_alert(ztc);
+ break;
+ case 0x04: /* g.723.1 */
+ if (sanitycheck(zth, rlen) && (rlen == G723_BYTES)) {
+ memcpy(chars, (void *) readchunk + 54, rlen);
+ zth->dstlen += rlen;
+ zth->dstsamples = zth->dstlen * 12;
+ } else {
+ ztc->errorstatus = -EOVERFLOW;
+ }
+ if ((zth->dstsamples % G723_SAMPLES) == 0)
+ zt_transcoder_alert(ztc);
+ break;
+ case 0x12: /* g.729 */
+ if (sanitycheck(zth, rlen) && (rlen == G729_BYTES)) {
+ memcpy(chars, (void *) readchunk + 54, rlen);
+ zth->dstlen += rlen;
+ zth->dstsamples = zth->dstlen * 8;
+ } else {
+ ztc->errorstatus = -EOVERFLOW;
+ }
+
+ if ((zth->dstsamples % G729_SAMPLES) == 0)
+ zt_transcoder_alert(ztc);
+ }
+ }
+}
+
+static int check_descriptor(struct wcdte *wc)
+{
+ int o2 = (ERING_SIZE * 4) + (wc->rdbl * 4);
+
+ if (!(le32_to_cpu(wc->descripchunk[o2]) & 0x80000000)) {
+ wc->rxints++;
+ receiveprep(wc, wc->rdbl);
+ reinit_descriptor(wc, 0, wc->rdbl, "rxchk");
+ wc->rdbl = (wc->rdbl + 1) % ERING_SIZE;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void init_descriptors(struct wcdte *wc)
+{
+ volatile u32 *descrip;
+ dma_addr_t descripdma;
+ dma_addr_t writedma;
+ dma_addr_t readdma;
+ int x;
+
+ descrip = wc->descripchunk;
+ descripdma = wc->descripdma;
+ writedma = wc->writedma;
+ readdma = wc->readdma;
+
+ for (x = 0; x < ERING_SIZE; x++) {
+ if (x < ERING_SIZE - 1)
+ descripdma += 16;
+ else
+ descripdma = wc->descripdma;
+
+ /* Transmit descriptor */
+ descrip[0] = cpu_to_le32(0x00000000);
+ descrip[1] = cpu_to_le32(0xe5800000 | (SFRAME_SIZE));
+ descrip[2] = cpu_to_le32(writedma + x * SFRAME_SIZE);
+ descrip[3] = cpu_to_le32(descripdma);
+
+ /* Receive descriptor */
+ descrip[0 + ERING_SIZE * 4] = cpu_to_le32(0x80000000);
+ descrip[1 + ERING_SIZE * 4] = cpu_to_le32(0x01000000 | (SFRAME_SIZE));
+ descrip[2 + ERING_SIZE * 4] = cpu_to_le32(readdma + (x * SFRAME_SIZE));
+ descrip[3 + ERING_SIZE * 4] = cpu_to_le32(descripdma + (ERING_SIZE * 16));
+
+ /* Advance descriptor */
+ descrip += 4;
+ }
+}
+
+static void dte_wque_run(struct wcdte *wc)
+{
+ int res;
+
+ if (wc->wqueints & 0x00000040) {
+ /* Loop descriptors is available */
+ do {} while ((res = check_descriptor(wc)));
+ }
+
+ /* Handle TX interrupts */
+ if (wc->wqueints & 0x00000001) {
+ wc->txints++;
+ transmit_demand(wc);
+ wc->intcount++;
+ }
+}
+
+ZAP_IRQ_HANDLER(interrupt_handler)
+{
+ struct wcdte *wc = dev_id;
+ unsigned int ints;
+
+ if (!(ints = getctl(wc, 0x0028)))
+ return IRQ_NONE;
+
+ setctl(wc, 0x0028, ints);
+
+ ints &= wc->intmask;
+
+ if (ints & 0x00000041) {
+ wc->wqueints = ints;
+ queue_work(wc->dte_wq, &wc->dte_work);
+ }
+
+ if (!debug)
+ return IRQ_RETVAL(1);
+
+ debug_printk(ints & 0x00008000, "Abnormal Interrupt\n");
+ debug_printk(ints & 0x00002000, "Fatal Bus Error\n");
+ debug_printk(ints & 0x00000100, "Receive Stopped\n");
+ debug_printk(ints & 0x00000080, "Receive Desciptor Unavailable\n");
+ debug_printk(ints & 0x00000020, "Transmit Under-flow\n");
+ debug_printk(ints & 0x00000008, "Jabber Timer Time-out\n");
+ debug_printk(ints & 0x00000004, "Transmit Descriptor Unavailable\n");
+ debug_printk(ints & 0x00000002, "Transmit Processor Stopped\n");
+
+ return IRQ_RETVAL(1);
+}
+
+
+static int hardware_init(struct wcdte *wc)
+{
+ /* Hardware stuff */
+ unsigned int reg;
+ unsigned long newjiffies;
+
+ /* Initialize descriptors */
+ init_descriptors(wc);
+
+ /* Enable I/O Access */
+ pci_read_config_dword(wc->dev, 0x0004, &reg);
+ reg |= 0x00000007;
+ pci_write_config_dword(wc->dev, 0x0004, reg);
+
+ setctl(wc, 0x0000, 0xFFF88001);
+
+ newjiffies = jiffies + HZ/10;
+ while (((reg = getctl(wc, 0x0000)) & 0x00000001) && (newjiffies > jiffies));
+
+ /* Configure watchdogs, access, etc */
+ setctl(wc, 0x0030, 0x00280048);
+ setctl(wc, 0x0078, 0x00000013 /* | (1 << 28) */);
+
+ reg = getctl(wc, 0x00fc);
+ setctl(wc, 0x00fc, (reg & ~0x7) | 0x7);
+
+ reg = getctl(wc, 0x00fc);
+
+ return 0;
+}
+
+static inline void setintmask(struct wcdte *wc, unsigned int intmask)
+{
+ wc->intmask = intmask;
+ setctl(wc, 0x0038, intmask);
+}
+
+static inline void enable_interrupts(struct wcdte *wc)
+{
+ setintmask(wc, !debug ? 0x00010041 : 0x0001A1EB);
+}
+
+static void start_dma(struct wcdte *wc)
+{
+ unsigned int reg;
+
+ wmb();
+ setctl(wc, 0x0020, wc->descripdma);
+ setctl(wc, 0x0018, wc->descripdma + (16 * ERING_SIZE));
+ /* Start receiver/transmitter */
+ reg = getctl(wc, 0x0030);
+ setctl(wc, 0x0030, reg | 0x00002002); /* Start XMT and RCD */
+ setctl(wc, 0x0010, 0x00000000); /* Receive Poll Demand */
+ reg = getctl(wc, 0x0028);
+ setctl(wc, 0x0028, reg);
+}
+
+static void disable_interrupts(struct wcdte *wc)
+{
+ setintmask(wc, 0x00000000);
+ setctl(wc, 0x0084, 0x00000000);
+}
+
+static void stop_dma(struct wcdte *wc)
+{
+ unsigned int reg;
+
+ disable_interrupts(wc);
+ setctl(wc, 0x0048, 0x00000000);
+ /* Reset the part to be on the safe side */
+ reg = getctl(wc, 0x0000);
+ reg |= 0x00000001;
+ setctl(wc, 0x0000, reg);
+}
+
+static int waitfor_csmencaps(struct wcdte *wc, unsigned int mask, int use_mask)
+{
+ int ret;
+
+ ret = wait_event_interruptible_timeout(wc->regq,
+ use_mask ? (wc->rcvflags == mask) : (wc->last_rcommand == wc->last_command_sent),
+ wc->timeout);
+ wc->rcvflags = 0;
+ wc->last_rcommand = 0;
+ wc->last_command_sent = 0;
+
+ if (ret < 0) {
+ debug_printk(1, "Wait interrupted, need to stop boot (ret = %d)\n", ret);
+ return 1;
+ }
+
+ if (!ret) {
+ debug_printk(1, "Waitfor CSMENCAPS response timed out (ret = %d)\n", ret);
+ return 2;
+ }
+
+ return 0;
+}
+
+static int read_phy(struct wcdte *wc, int location)
+{
+ int i;
+ long mdio_addr = 0x0048;
+ int read_cmd = (0xf6 << 10) | (1 << 5) | location;
+ int retval = 0;
+
+ /* Establish sync by sending at least 32 logic ones. */
+ for (i = 32; i >= 0; i--) {
+ setctl(wc, mdio_addr, MDIO_ENB | MDIO_DATA_WRITE1);
+ getctl(wc, mdio_addr);
+ setctl(wc, mdio_addr, MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK);
+ getctl(wc, mdio_addr);
+ }
+ /* Shift the read command bits out. */
+ for (i = 17; i >= 0; i--) {
+ int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
+
+ setctl(wc, mdio_addr, MDIO_ENB | dataval);
+ getctl(wc, mdio_addr);
+ setctl(wc, mdio_addr, MDIO_ENB | dataval | MDIO_SHIFT_CLK);
+ getctl(wc, mdio_addr);
+ }
+
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ setctl(wc, mdio_addr, MDIO_ENB_IN);
+ getctl(wc, mdio_addr);
+ retval = (retval << 1) | ((getctl(wc, mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+ setctl(wc, mdio_addr, MDIO_ENB_IN | MDIO_SHIFT_CLK);
+ getctl(wc, mdio_addr);
+ }
+ retval = (retval >> 1) & 0xffff;
+
+ return retval;
+}
+
+void write_phy(struct wcdte *wc, int location, int value)
+{
+ int i;
+ int cmd = (0x5002 << 16) | (1 << 23) | (location<<18) | value;
+ long mdio_addr = 0x0048;
+
+ /* Establish sync by sending 32 logic ones. */
+ for (i = 32; i >= 0; i--) {
+ setctl(wc, mdio_addr, MDIO_ENB | MDIO_DATA_WRITE1);
+ getctl(wc, mdio_addr);
+ setctl(wc, mdio_addr, MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK);
+ getctl(wc, mdio_addr);
+ }
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
+ setctl(wc, mdio_addr, MDIO_ENB | dataval);
+ getctl(wc, mdio_addr);
+ setctl(wc, mdio_addr, MDIO_ENB | dataval | MDIO_SHIFT_CLK);
+ getctl(wc, mdio_addr);
+ }
+ /* Clear out extra bits. */
+ for (i = 2; i > 0; i--) {
+ setctl(wc, mdio_addr, MDIO_ENB_IN);
+ getctl(wc, mdio_addr);
+ setctl(wc, mdio_addr, MDIO_ENB_IN | MDIO_SHIFT_CLK);
+ getctl(wc, mdio_addr);
+ }
+ return;
+}
+
+static int boot_processor(struct wcdte *wc, const struct firmware *firmware)
+{
+ int byteloc, last_byteloc, length, delay_count;
+ unsigned int reg, ret;
+
+#if !defined(USE_TEST_HW)
+ /* Turn off auto negotiation */
+ write_phy(wc, 0, 0x2100);
+
+ debug_printk(1, "PHY register 0 = %X", read_phy(wc, 0));
+
+ /* Set reset */
+ setctl(wc, 0x00A0, 0x04000000);
+
+ /* Wait 1000ms to ensure processor reset */
+ mdelay(1000);
+
+ /* Clear reset */
+ setctl(wc, 0x00A0, 0x04080000);
+
+ /* Wait for Ethernet link */
+ for (delay_count = 0;
+ ((getctl(wc, 0x00fc) & 0xE0000000) != 0xE0000000) && delay_count < 100;
+ mdelay(100), delay_count++);
+
+ if (delay_count == 100) {
+ module_printk("Failed to link to DTE processor!\n");
+ return 1;
+ }
+
+ /* Turn off booted LED */
+ setctl(wc, 0x00A0, 0x04084000);
+#endif /* !defined(USE_TEST_HW) */
+
+ if (debug) {
+ reg = getctl(wc, 0x00fc);
+ debug_printk(1, "LINK STATUS: reg(0xfc) = %X\n", reg);
+ }
+
+ for (ret = 0, last_byteloc = byteloc = 17; byteloc < (firmware->size - 20); last_byteloc = byteloc) {
+ length = (firmware->data[byteloc] << 8) | firmware->data[byteloc + 1];
+ byteloc += 2;
+ __transmit_one(wc, firmware->data + byteloc, length);
+ byteloc += length;
+ ret = waitfor_csmencaps(wc, RCV_CSMENCAPS_ACK, 1);
+ if (ret == 1)
+ return 1;
+ else if (ret == 2) /* Retransmit if processor times out */
+ byteloc = last_byteloc;
+ }
+
+ wc->timeout = 10 * HZ;
+ if (waitfor_csmencaps(wc, RCV_CSMENCAPS, 1))
+ return 1;
+
+ /* Turn on booted LED */
+ setctl(wc, 0x00A0, 0x04080000);
+
+ debug_printk(1, "Successfully booted DTE processor.\n");
+
+ return 0;
+}
+
+static int create_channel(struct wcdte *wc, int simple, int complicated, int part1_id,
+ int part2_id, unsigned int *dte_chan1, unsigned int *dte_chan2)
+{
+ int length = 0;
+ unsigned char chan1, chan2;
+
+ if (complicated == DTE_FORMAT_G729A)
+ length = G729_LENGTH;
+ else if (complicated == DTE_FORMAT_G723_1)
+ length = G723_LENGTH;
+
+ /* Create complex channel */
+ send_cmd(wc, CMD_MSG_CREATE_CHANNEL(wc->seq_num++, part1_id), CMD_MSG_CREATE_CHANNEL_LEN, 0x0010);
+ chan1 = wc->last_rparm2;
+
+ /* Create simple channel */
+ send_cmd(wc, CMD_MSG_CREATE_CHANNEL(wc->seq_num++, part2_id), CMD_MSG_CREATE_CHANNEL_LEN, 0x0010);
+ chan2 = wc->last_rparm2;
+
+ /* Configure complex channel */
+ send_cmd(wc, CMD_MSG_SET_IP_HDR_CHANNEL(wc->seq_num++, chan1, part2_id, part1_id), CMD_MSG_SET_IP_HDR_CHANNEL_LEN, 0x9000);
+ send_cmd(wc, CMD_MSG_VOIP_VCEOPT(wc->seq_num++, chan1, length, 0), CMD_MSG_VOIP_VCEOPT_LEN, 0x8001);
+
+ /* Configure simple channel */
+ send_cmd(wc, CMD_MSG_SET_IP_HDR_CHANNEL(wc->seq_num++, chan2, part1_id, part2_id), CMD_MSG_SET_IP_HDR_CHANNEL_LEN, 0x9000);
+ send_cmd(wc, CMD_MSG_VOIP_VCEOPT(wc->seq_num++, chan2, length, 0), CMD_MSG_VOIP_VCEOPT_LEN, 0x8001);
+
+ send_cmd(wc, CMD_MSG_TRANS_CONNECT(wc->seq_num++, 1, chan1, chan2, complicated, simple), CMD_MSG_TRANS_CONNECT_LEN, 0x9322);
+ send_cmd(wc, CMD_MSG_VOIP_INDCTRL(wc->seq_num++, chan1), CMD_MSG_VOIP_INDCTRL_LEN, 0x8084);
+ send_cmd(wc, CMD_MSG_VOIP_INDCTRL(wc->seq_num++, chan2), CMD_MSG_VOIP_INDCTRL_LEN, 0x8084);
+ send_cmd(wc, CMD_MSG_VOIP_VOPENA(wc->seq_num++, chan1, complicated), CMD_MSG_VOIP_VOPENA_LEN, 0x8000);
+ send_cmd(wc, CMD_MSG_VOIP_VOPENA(wc->seq_num++, chan2, simple), CMD_MSG_VOIP_VOPENA_LEN, 0x8000);
+
+ *dte_chan1 = chan1;
+ *dte_chan2 = chan2;
+
+ return 1;
+}
+
+static int destroy_channel(struct wcdte *wc, unsigned int chan1, unsigned int chan2)
+{
+ /* Turn off both channels */
+ send_cmd(wc, CMD_MSG_VOIP_VOPENA_CLOSE(wc->seq_num++, chan1), CMD_MSG_VOIP_VOPENA_CLOSE_LEN, 0x8000);
+ send_cmd(wc, CMD_MSG_VOIP_VOPENA_CLOSE(wc->seq_num++, chan2), CMD_MSG_VOIP_VOPENA_CLOSE_LEN, 0x8000);
+
+ /* Disconnect the channels */
+ send_cmd(wc, CMD_MSG_TRANS_CONNECT(wc->seq_num++, 0, chan1, chan2, 0, 0), CMD_MSG_TRANS_CONNECT_LEN, 0x9322);
+
+ /* Remove the channels */
+ send_cmd(wc, CMD_MSG_DESTROY_CHANNEL(wc->seq_num++, chan1), CMD_MSG_DESTROY_CHANNEL_LEN, 0x0011);
+ send_cmd(wc, CMD_MSG_DESTROY_CHANNEL(wc->seq_num++, chan2), CMD_MSG_DESTROY_CHANNEL_LEN, 0x0011);
+
+ return 1;
+}
+
+static int setup_channels(struct wcdte *wc)
+{
+#ifndef USE_TEST_HW
+ send_cmd(wc, CMD_MSG_SET_ARM_CLK(wc->seq_num++), CMD_MSG_SET_ARM_CLK_LEN, 0x0411);
+ send_cmd(wc, CMD_MSG_SET_SPU_CLK(wc->seq_num++), CMD_MSG_SET_SPU_CLK_LEN, 0x0412);
+#endif
+
+#ifdef USE_TDM_CONFIG
+ send_cmd(wc, CMD_MSG_TDM_SELECT_BUS_MODE(wc->seq_num++), CMD_MSG_TDM_SELECT_BUS_MODE_LEN, 0x0417);
+ send_cmd(wc, CMD_MSG_TDM_ENABLE_BUS(wc->seq_num++), CMD_MSG_TDM_ENABLE_BUS_LEN, 0x0405);
+ send_cmd(wc, CMD_MSG_SUPVSR_SETUP_TDM_PARMS(wc->seq_num++, 0x03, 0x20, 0x00), CMD_MSG_SUPVSR_SETUP_TDM_PARMS_LEN, 0x0407);
+ send_cmd(wc, CMD_MSG_SUPVSR_SETUP_TDM_PARMS(wc->seq_num++, 0x04, 0x80, 0x04), CMD_MSG_SUPVSR_SETUP_TDM_PARMS_LEN, 0x0407);
+ send_cmd(wc, CMD_MSG_SUPVSR_SETUP_TDM_PARMS(wc->seq_num++, 0x05, 0x20, 0x08), CMD_MSG_SUPVSR_SETUP_TDM_PARMS_LEN, 0x0407);
+ send_cmd(wc, CMD_MSG_SUPVSR_SETUP_TDM_PARMS(wc->seq_num++, 0x06, 0x80, 0x0C), CMD_MSG_SUPVSR_SETUP_TDM_PARMS_LEN, 0x0407);
+#endif
+
+ send_cmd(wc, CMD_MSG_SET_ETH_HEADER(wc->seq_num++), CMD_MSG_SET_ETH_HEADER_LEN, 0x0100);
+ send_cmd(wc, CMD_MSG_IP_SERVICE_CONFIG(wc->seq_num++), CMD_MSG_IP_SERVICE_CONFIG_LEN, 0x0302);
+ send_cmd(wc, CMD_MSG_ARP_SERVICE_CONFIG(wc->seq_num++), CMD_MSG_ARP_SERVICE_CONFIG_LEN, 0x0105);
+ send_cmd(wc, CMD_MSG_ICMP_SERVICE_CONFIG(wc->seq_num++), CMD_MSG_ICMP_SERVICE_CONFIG_LEN, 0x0304);
+
+#ifdef USE_TDM_CONFIG
+ send_cmd(wc, CMD_MSG_DEVICE_SET_COUNTRY_CODE(wc->seq_num++), CMD_MSG_DEVICE_SET_COUNTRY_CODE_LEN, 0x041B);
+#endif
+
+ send_cmd(wc, CMD_MSG_SPU_FEATURES_CONTROL(wc->seq_num++, 0x02), CMD_MSG_SPU_FEATURES_CONTROL_LEN, 0x0013);
+ send_cmd(wc, CMD_MSG_IP_OPTIONS(wc->seq_num++), CMD_MSG_IP_OPTIONS_LEN, 0x0306);
+ send_cmd(wc, CMD_MSG_SPU_FEATURES_CONTROL(wc->seq_num++, 0x04), CMD_MSG_SPU_FEATURES_CONTROL_LEN, 0x0013);
+
+#ifdef USE_TDM_CONFIG
+ send_cmd(wc, CMD_MSG_TDM_OPT(wc->seq_num++), CMD_MSG_TDM_OPT_LEN, 0x0435);
+#endif
+
+ wc->timeout = HZ/100 + 1;
+
+ return 0;
+}
+
+static int __devinit init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int res = 0, reg;
+ struct wcdte *wc;
+ struct wcdte_desc *d = (struct wcdte_desc *) ent->driver_data;
+ int x;
+ unsigned int g729_numchannels, g723_numchannels;
+ u8 firmware_ver;
+ unsigned int complexfmts = 0;
+ struct firmware embedded_firmware;
+ const struct firmware *firmware = &embedded_firmware;
+ struct dte_state *encoders;
+ struct dte_state *decoders;
+
+ for (x = 0; x < (sizeof(ifaces) / sizeof(ifaces[0])); x++)
+ if (!ifaces[x]) break;
+
+ if (x == (sizeof(ifaces) / sizeof(ifaces[0]))) {
+ module_printk("Too many interfaces\n");
+ return -EIO;
+ }
+
+ if (pci_enable_device(pdev))
+ return -EIO;
+
+ if (!(wc = kmalloc(sizeof(*wc), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(wc, 0, sizeof(*wc));
+ ifaces[x] = wc;
+ spin_lock_init(&wc->reglock);
+ sema_init(&wc->chansem, 1);
+ sema_init(&wc->cmdqsem, 1);
+ wc->iobase = pci_resource_start(pdev, 0);
+ wc->dev = pdev;
+ wc->pos = x;
+ wc->variety = d->name;
+
+ wc->seq_num = 6;
+ wc->timeout = HZ;
+
+ INIT_LIST_HEAD(&wc->pending_cmdq);
+ INIT_LIST_HEAD(&wc->free_cmdq);
+
+ /* Keep track of whether we need to free the region */
+ if (request_region(wc->iobase, 0xff, "wctc4xxp"))
+ wc->freeregion = 1;
+
+ /* Allocate enough memory for all TX buffers, RX buffers, and descriptors */
+ wc->writechunk = pci_alloc_consistent(pdev, PCI_WINDOW_SIZE, &wc->writedma);
+ if (!wc->writechunk) {
+ module_printk("Unable to allocate DMA-able memory\n");
+ if (wc->freeregion)
+ release_region(wc->iobase, 0xff);
+
+ return -ENOMEM;
+ }
+
+ wc->readchunk = wc->writechunk + (SFRAME_SIZE * ERING_SIZE);
+ wc->readdma = wc->writedma + (SFRAME_SIZE * ERING_SIZE);
+
+ wc->descripchunk = (u32 *) (wc->readchunk + (SFRAME_SIZE * ERING_SIZE));
+ wc->descripdma = wc->readdma + (SFRAME_SIZE * ERING_SIZE);
+
+ init_waitqueue_head(&wc->regq);
+
+ /* Initialize the work queue */
+ wc->dte_wq = create_workqueue("wctc4xxp");
+
+ INIT_WORK(&wc->dte_work, (void (*)(void *)) dte_wque_run, wc);
+
+#ifdef HOTPLUG_FIRMWARE
+ if ((request_firmware(&firmware, tc400m_firmware, &wc->dev->dev) != 0) || !firmware) {
+ module_printk("Firmware %s not available from userspace\n", tc400m_firmware);
+ return -1;
+ }
+#else
+ embedded_firmware.data = _binary_tc400m_firmware_bin_start;
+ /* Yes... this is weird. objcopy gives us a symbol containing
+ the size of the firmware, not a pointer a variable containing
+ the size. The only way we can get the value of the symbol
+ is to take its address, so we do that and then cast that
+ value to the proper type.
+ */
+ embedded_firmware.size = (size_t) &_binary_tc400m_firmware_bin_size;
+#endif
+
+ firmware_ver = firmware->data[0];
+ g729_numchannels = firmware->data[1];
+ g723_numchannels = firmware->data[2];
+
+ wc->numchannels = 2048; /* someday... */
+
+ for (x = 0; x < mode_count; x++) {
+ if (!strcmp(mode[x], "g729") ||
+ !strcmp(mode[x], "G729")) {
+ if (!g729_numchannels) {
+ module_printk("Format '%s' not supported by the firmware for this module; ignored.\n", mode[x]);
+ continue;
+ }
+ strcat(wc->complexname, "G.729A / ");
+ complexfmts |= ZT_FORMAT_G729A;
+ wc->numchannels = min(wc->numchannels, g729_numchannels);
+ } else if (!strcmp(mode[x], "g723") ||
+ !strcmp(mode[x], "G723")) {
+ if (!g723_numchannels) {
+ module_printk("Format '%s' not supported by the firmware for this module; ignored.\n", mode[x]);
+ continue;
+ }
+ strcat(wc->complexname, "G.723.1 5.3Kbps / ");
+ complexfmts |= ZT_FORMAT_G723_1;
+ wc->numchannels = min(wc->numchannels, g723_numchannels);
+ } else {
+ module_printk("Invalid transcoder format specified: %s\n", mode[x]);
+ }
+ }
+
+ if (!complexfmts) {
+ module_printk("No valid transcoder formats specified; module will not be enabled.\n");
+ return -1;
+ }
+
+ wc->complexname[strlen(wc->complexname) - 3] = '\0';
+
+ wc->uencode = zt_transcoder_alloc(wc->numchannels);
+ wc->udecode = zt_transcoder_alloc(wc->numchannels);
+ encoders = kmalloc(sizeof(*encoders) * wc->numchannels, GFP_KERNEL);
+ decoders = kmalloc(sizeof(*decoders) * wc->numchannels, GFP_KERNEL);
+ if (!wc->uencode || !wc->udecode || !encoders || !decoders) {
+ if (wc->uencode)
+ zt_transcoder_free(wc->uencode);
+ if (wc->udecode)
+ zt_transcoder_free(wc->udecode);
+ if (encoders)
+ kfree(encoders);
+ if (decoders)
+ kfree(decoders);
+
+ return -ENOMEM;
+ }
+
+ strcpy(wc->udecode->name, wc->variety);
+ strcpy(wc->uencode->name, wc->variety);
+
+ wc->udecode->srcfmts = wc->uencode->dstfmts = complexfmts;
+ wc->udecode->dstfmts = wc->uencode->srcfmts = ZT_FORMAT_ULAW | ZT_FORMAT_ALAW;
+
+ wc->udecode->operation = wc->uencode->operation = dte_operation;
+
+ for (x = 0;x < wc->numchannels; x++) {
+ dte_init_state(&encoders[x], 1, x, wc);
+ dte_init_state(&decoders[x], 0, x, wc);
+ wc->uencode->channels[x].pvt = &encoders[x];
+ wc->udecode->channels[x].pvt = &decoders[x];
+ }
+
+ zt_transcoder_register(wc->uencode, THIS_MODULE);
+ zt_transcoder_register(wc->udecode, THIS_MODULE);
+
+ /* Enable bus mastering */
+ pci_set_master(pdev);
+
+ /* Keep track of which device we are */
+ pci_set_drvdata(pdev, wc);
+
+ if (request_irq(pdev->irq, interrupt_handler, SA_SHIRQ, driver.name, wc)) {
+ module_printk("Unable to request IRQ %d\n", pdev->irq);
+ if (wc->freeregion)
+ release_region(wc->iobase, 0xFF);
+ pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *) wc->writechunk, wc->writedma);
+ pci_set_drvdata(pdev, NULL);
+ kfree(wc);
+ /* TODO: what about all the encoders and decoders (do this earlier)? */
+ return -EIO;
+ }
+
+ if (hardware_init(wc)) {
+ /* Set Reset Low */
+ stop_dma(wc);
+ /* Free Resources */
+ free_irq(pdev->irq, wc);
+ if (wc->freeregion)
+ release_region(wc->iobase, 0xff);
+ pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *) wc->writechunk, wc->writedma);
+ pci_set_drvdata(pdev, NULL);
+ kfree(wc);
+ return -EIO;
+ }
+
+ /* Enable interrupts */
+ enable_interrupts(wc);
+
+ /* Start DMA */
+ start_dma(wc);
+
+ if (boot_processor(wc, firmware)) {
+ /* Set Reset Low */
+ stop_dma(wc);
+ /* Free Resources */
+ free_irq(pdev->irq, wc);
+ if (wc->freeregion)
+ release_region(wc->iobase, 0xff);
+ pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *) wc->writechunk, wc->writedma);
+ pci_set_drvdata(pdev, NULL);
+ kfree(wc);
+ return -EIO;
+ }
+
+ if (setup_channels(wc)) {
+ /* Set Reset Low */
+ stop_dma(wc);
+ /* Free Resources */
+ free_irq(pdev->irq, wc);
+ if (wc->freeregion)
+ release_region(wc->iobase, 0xff);
+ pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *) wc->writechunk, wc->writedma);
+ pci_set_drvdata(pdev, NULL);
+ kfree(wc);
+ return -EIO;
+ }
+
+ if (debug) {
+ reg = getctl(wc, 0x00fc);
+ debug_printk(1, "(post-boot) Reg fc is %08x\n", reg);
+ }
+
+ module_printk("%s supporting '%s' with firmware version '%d'\n", wc->variety, wc->complexname, firmware_ver);
+
+ res = 0;
+
+ return res;
+}
+
+static void release(struct wcdte *wc)
+{
+ struct cmdq *cmdq, *next;
+
+ if (wc->freeregion)
+ release_region(wc->iobase, 0xff);
+ list_for_each_entry_safe(cmdq, next, &wc->pending_cmdq, list) {
+ debug_printk(1, "freeing cmdq entry at '%p'\n", cmdq);
+ list_del(&cmdq->list);
+ kfree(cmdq);
+ }
+ list_for_each_entry_safe(cmdq, next, &wc->free_cmdq, list) {
+ debug_printk(1, "freeing cmdq entry at '%p'\n", cmdq);
+ list_del(&cmdq->list);
+ kfree(cmdq);
+ }
+ kfree(wc);
+}
+
+static void __devexit remove_one(struct pci_dev *pdev)
+{
+ int i;
+ struct wcdte *wc = pci_get_drvdata(pdev);
+ struct zt_transcoder_channel *ztc_en, *ztc_de;
+ struct dte_state *st_en, *st_de;
+
+ if (!wc)
+ return;
+
+ zt_transcoder_unregister(wc->udecode);
+ zt_transcoder_unregister(wc->uencode);
+
+ if (debug) {
+ debug_printk(1, "wc->ztsnd_rtx = %d\n", wc->ztsnd_rtx);
+ debug_printk(1, "wc->ztsnd_0010_rtx = %d\n", wc->ztsnd_0010_rtx);
+
+ for (i = 0; i < wc->numchannels; i++) {
+ ztc_en = &(wc->uencode->channels[i]);
+ st_en = ztc_en->pvt;
+
+ ztc_de = &(wc->udecode->channels[i]);
+ st_de = ztc_de->pvt;
+
+ debug_printk(1, "en[%d] snt = %d, rcv = %d [%d]\n", i, st_en->packets_sent, st_en->packets_received, st_en->packets_sent - st_en->packets_received);
+ debug_printk(1, "de[%d] snt = %d, rcv = %d [%d]\n", i, st_de->packets_sent, st_de->packets_received, st_de->packets_sent - st_de->packets_received);
+ }
+ }
+
+ /* Stop any DMA */
+ stop_dma(wc);
+
+ /* In case hardware is still there */
+ disable_interrupts(wc);
+
+ /* Kill workqueue */
+ destroy_workqueue(wc->dte_wq);
+
+ /* Immediately free resources */
+ pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *) wc->writechunk, wc->writedma);
+ free_irq(pdev->irq, wc);
+
+ kfree(wc->uencode->channels[0].pvt);
+ kfree(wc->udecode->channels[0].pvt);
+
+ zt_transcoder_free(wc->uencode);
+ zt_transcoder_free(wc->udecode);
+
+ /* Release span, possibly delayed */
+ release(wc);
+}
+
+static struct pci_device_id pci_tbl[] = {
+#ifndef USE_TEST_HW
+ { 0xd161, 0x3400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcdte }, /* digium board */
+#else
+ { 0x1317, 0x0985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcdte }, /* reference board */
+#endif
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct pci_driver driver = {
+ name: "wctc4xxp",
+ probe: init_one,
+ remove: __devexit_p(remove_one),
+ suspend: NULL,
+ resume: NULL,
+ id_table: pci_tbl,
+};
+
+static int init(void)
+{
+ return pci_module_init(&driver) ? -ENODEV : 0;
+}
+
+static void cleanup(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+module_param_array(mode, charp, &mode_count, S_IRUGO | S_IWUSR);
+module_param(debug_packets, uint, S_IRUGO | S_IWUSR);
+module_param(debug_cmd_packets, uint, S_IRUGO | S_IWUSR);
+MODULE_DESCRIPTION("Wildcard TC400P+TC400M Transcoder");
+MODULE_AUTHOR("John Sloan <jsloan@digium.com>");
+MODULE_LICENSE("GPL");
+
+module_init(init);
+module_exit(cleanup);
diff --git a/wctc4xxp/codec_test.c b/wctc4xxp/codec_test.c
new file mode 100644
index 0000000..5cef93e
--- /dev/null
+++ b/wctc4xxp/codec_test.c
@@ -0,0 +1,333 @@
+/*
+ * Wilcard TC400B Digium Transcoder Engine Interface Driver for Zapata Telephony interface test tool.
+ *
+ * Written by Matt O'Gorman <mogorman@digium.com>
+ *
+ * Copyright (C) 2006-2007, 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 <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <linux/types.h>
+#ifdef STANDALONE_ZAPATA
+#include "../zaptel.h"
+#else
+#include <linux/zaptel.h>
+#endif
+
+#define MAX_CARDS_TO_TEST 6
+#define MAX_CHANNELS_PER_CARD 96
+
+#define AST_FORMAT_ULAW (1 << 2)
+#define AST_FORMAT_G729A (1 << 8)
+
+#define AST_FRIENDLY_OFFSET 64
+
+static int debug = 0;
+
+static unsigned char ulaw_slin_ex[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char g729a_expected[] = {
+ 0xE9, 0x88, 0x4C, 0xA0, 0x00, 0xFA, 0xDD, 0xA2, 0x06, 0x2D,
+ 0x69, 0x88, 0x00, 0x60, 0x68, 0xD5, 0x9E, 0x20, 0x80, 0x50
+};
+
+
+struct format_map {
+ unsigned int map[32][32];
+};
+
+
+struct tcpvt {
+ int fd;
+ int fake;
+ int inuse;
+ struct zt_transcode_header *hdr;
+// struct ast_frame f;
+};
+
+struct tctest_info {
+ int numcards;
+ int numchans[MAX_CARDS_TO_TEST];
+ int total_chans;
+ int errors;
+ int overcnt_error; /* Too many cards found */
+ int undercnt_error; /* Too few cards found */
+ int timeout_error[MAX_CARDS_TO_TEST];
+ int data_error[MAX_CARDS_TO_TEST];
+ int numcards_werrors;
+};
+
+
+static int find_transcoders(struct tctest_info *tctest_info)
+{
+ struct zt_transcode_info info = { 0, };
+ int fd, res;
+
+ if ((fd = open("/dev/zap/transcode", O_RDWR)) < 0) {
+ printf("Warning: No Zaptel transcoder support!\n");
+ return 0;
+ }
+
+ tctest_info->total_chans = 0;
+ info.op = ZT_TCOP_GETINFO;
+ for (info.tcnum = 0; !(res = ioctl(fd, ZT_TRANSCODE_OP, &info)); info.tcnum++) {
+ if (debug)
+ printf("Found transcoder %d, '%s' with %d channels.\n", info.tcnum, info.name, info.numchannels);
+ if ((info.tcnum % 2) == 0)
+ {
+ tctest_info->numchans[info.tcnum/2] = info.numchannels;
+ tctest_info->total_chans += info.numchannels;
+ }
+ }
+ tctest_info->numcards = info.tcnum / 2;
+
+ close(fd);
+ if (!info.tcnum)
+ printf("No hardware transcoders found.\n");
+ return 0;
+}
+
+
+static int open_transcoder(struct tcpvt *ztp, int dest, int source)
+{
+ int fd;
+ unsigned int x = ZT_TCOP_ALLOCATE;
+ struct zt_transcode_header *hdr;
+ int flags;
+
+ if ((fd = open("/dev/zap/transcode", O_RDWR)) < 0)
+ return -1;
+ flags = fcntl(fd, F_GETFL);
+ if (flags > - 1) {
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
+ printf("Could not set non-block mode!\n");
+ }
+
+ if ((hdr = mmap(NULL, sizeof(*hdr), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ printf("Memory Map failed for transcoding (%s)\n", strerror(errno));
+ close(fd);
+
+ return -1;
+ }
+
+ if (hdr->magic != ZT_TRANSCODE_MAGIC) {
+ printf("Transcoder header (%08x) wasn't magic. Abandoning\n", hdr->magic);
+ munmap(hdr, sizeof(*hdr));
+ close(fd);
+
+ return -1;
+ }
+
+ hdr->srcfmt = source;
+ hdr->dstfmt = dest;
+
+ if (ioctl(fd, ZT_TRANSCODE_OP, &x)) {
+ printf("Unable to attach transcoder: %s\n", strerror(errno));
+ munmap(hdr, sizeof(*hdr));
+ close(fd);
+
+ return -1;
+ }
+
+ ztp->fd = fd;
+ ztp->hdr = hdr;
+
+ return 0;
+}
+
+
+static void close_transcoder(struct tcpvt *ztp)
+{
+ unsigned int x;
+
+ x = ZT_TCOP_RELEASE;
+ if (ioctl(ztp->fd, ZT_TRANSCODE_OP, &x))
+ printf("Failed to release transcoder channel: %s\n", strerror(errno));
+
+ munmap(ztp->hdr, sizeof(*ztp->hdr));
+ close(ztp->fd);
+}
+
+
+static int encode_packet(struct tcpvt *ztp, unsigned char *packet_in, unsigned char *packet_out)
+{
+ struct zt_transcode_header *hdr = ztp->hdr;
+ unsigned int x;
+
+ hdr->srcoffset = 0;
+
+ memcpy(hdr->srcdata + hdr->srcoffset + hdr->srclen, packet_in, 160);
+ hdr->srclen += 160;
+
+
+ hdr->dstoffset = AST_FRIENDLY_OFFSET;
+ x = ZT_TCOP_TRANSCODE;
+ if (ioctl(ztp->fd, ZT_TRANSCODE_OP, &x))
+ printf("Failed to transcode: %s\n", strerror(errno));
+
+ usleep(20000);
+ if (hdr->dstlen)
+ {
+ memcpy(packet_out, hdr->dstdata + hdr->dstoffset, hdr->dstlen);
+ return 0;
+ }
+ else
+ return -1;
+}
+
+
+static void print_failed()
+{
+ printf("______ ___ _____ _ _____ ______\n");
+ printf("| ___| / _ \\ |_ _| | | | ___| | _ \\\n");
+ printf("| |_ / /_\\ \\ | | | | | |__ | | | |\n");
+ printf("| _| | _ | | | | | | __| | | | |\n");
+ printf("| | | | | | _| |_ | |____ | |___ | |/ /\n");
+ printf("\\_| \\_| |_/ \\___/ \\_____/ \\____/ |___/ \n");
+}
+
+
+int main(int argc, char *argv[])
+{
+ int arg1, arg2, i, j, card_testing, chan_testing;
+ struct tcpvt ztp[MAX_CHANNELS_PER_CARD * MAX_CARDS_TO_TEST];
+ unsigned char packet_out[200];
+ struct tctest_info tctest_info;
+
+ memset(&tctest_info, 0, sizeof(tctest_info));
+
+ if ((argc < 2) || (argc > 3))
+ {
+ printf("codec_test requires one argument.\n");
+ printf(" arg1 = number of cards to test\n");
+ return -1;
+ }
+
+ if (argc == 2)
+ sscanf(argv[1], "%d", &arg1);
+ else if (argc == 3)
+ {
+ sscanf(argv[1], "%d %d", &arg1, &arg2);
+ debug = arg2;
+ }
+
+ printf("Beginning test of %d TC400B cards\n", arg1);
+
+
+ /* Search for TC400Bs */
+ find_transcoders(&tctest_info);
+
+ if (tctest_info.numcards > arg1)
+ {
+ tctest_info.errors++;
+ tctest_info.overcnt_error = 1;
+ }
+ if (tctest_info.numcards < arg1)
+ {
+ tctest_info.errors++;
+ tctest_info.undercnt_error = 1;
+ }
+
+ if (tctest_info.errors == 0)
+ {
+ /* Begin testing transcoder channels */
+ for (card_testing = 0; card_testing < tctest_info.numcards; card_testing++)
+ {
+ tctest_info.data_error[card_testing] = 0;
+ tctest_info.timeout_error[card_testing] = 0;
+ for (chan_testing = 0; chan_testing < tctest_info.numchans[card_testing]; chan_testing++)
+ {
+ i = chan_testing;
+ for(j = 0; j < card_testing; j++)
+ i += tctest_info.numchans[j];
+
+ open_transcoder(&ztp[i], AST_FORMAT_G729A, AST_FORMAT_ULAW);
+
+ if ((tctest_info.timeout_error[card_testing] = encode_packet(&ztp[i], ulaw_slin_ex, packet_out) == -1))
+ tctest_info.errors++;
+
+ if (memcmp(g729a_expected, packet_out, 20) != 0)
+ {
+ tctest_info.errors++;
+ tctest_info.data_error[card_testing] += 1;
+ }
+ }
+ if ( (tctest_info.data_error[card_testing]) || (tctest_info.timeout_error[card_testing]) )
+ tctest_info.numcards_werrors++;
+ }
+
+ for (i = 0; i < tctest_info.total_chans; i++)
+ close_transcoder(&ztp[i]);
+ }
+
+ if (debug)
+ {
+ printf("\n\n");
+ printf("tctest_info.errors = %d\n", tctest_info.errors);
+ printf("tctest_info.overcnt_error = %d\n", tctest_info.overcnt_error);
+ printf("tctest_info.undercnt_error = %d\n", tctest_info.undercnt_error);
+ printf("tctest_info.numcards_werrors = %d\n", tctest_info.numcards_werrors);
+
+ for (i = 0; i < tctest_info.numcards; i++)
+ {
+ printf("tctest_info.data_error[%d] = %d\n", i, tctest_info.data_error[i]);
+ printf("tctest_info.timeout_error[%d] = %d\n", i, tctest_info.timeout_error[i]);
+ }
+ }
+
+ if (tctest_info.errors)
+ {
+ printf("\n\n\n");
+ if (tctest_info.numcards_werrors)
+ printf("%d of %d cards\n", tctest_info.numcards_werrors, tctest_info.numcards);
+ print_failed();
+ if (tctest_info.overcnt_error)
+ printf("\n%d cards found, %d expected\n", tctest_info.numcards, arg1);
+ if (tctest_info.undercnt_error)
+ printf("\n%d cards found, %d expected\n", tctest_info.numcards, arg1);
+ printf("\n\n\n");
+ }
+ else
+ printf("%d of %d cards PASSED\n", tctest_info.numcards - tctest_info.numcards_werrors, tctest_info.numcards);
+
+ return 0;
+}
diff --git a/wctc4xxp/tc400m-firmware.bin b/wctc4xxp/tc400m-firmware.bin
new file mode 100644
index 0000000..2e6c743
--- /dev/null
+++ b/wctc4xxp/tc400m-firmware.bin
Binary files differ
diff --git a/zaptel.c b/zaptel.c
index 9ff991c..0625e2e 100644
--- a/zaptel.c
+++ b/zaptel.c
@@ -79,6 +79,7 @@
#define FAST_HDLC_NEED_TABLES
#include "fasthdlc.h"
+#include <linux/types.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#else
@@ -118,6 +119,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);
@@ -154,6 +156,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! */
@@ -249,6 +252,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
{
@@ -2316,10 +2320,26 @@ 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 LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ 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;
+ }
+#endif
if (unit == 253) {
if (maxspans) {
return zt_timing_open(inode, file);
@@ -2644,6 +2664,13 @@ static int zt_release(struct inode *inode, struct file *file)
if (unit == 253) {
return zt_timer_release(inode, file);
}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ if (unit == 250) {
+ res = zt_transcode_fops->release(inode, file);
+ module_put(zt_transcode_fops->owner);
+ return res;
+ }
+#endif
if (unit == 254) {
chan = file->private_data;
if (!chan)
@@ -4395,6 +4422,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 LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ if (unit == 250)
+ return zt_transcode_fops->ioctl(inode, file, cmd, data);
+#endif
if (unit == 253) {
timer = file->private_data;
if (timer)
@@ -6097,6 +6128,16 @@ 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 LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ if (unit == 250)
+ return zt_transcode_fops->mmap(file, vm);
+#endif
+ return -ENOSYS;
+}
+
static unsigned int zt_poll(struct file *file, struct poll_table_struct *wait_table)
{
int unit = UNIT(file);
@@ -6104,7 +6145,10 @@ static unsigned int zt_poll(struct file *file, struct poll_table_struct *wait_ta
if (!unit)
return -EINVAL;
-
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+ if (unit == 250)
+ return zt_transcode_fops->poll(file, wait_table);
+#endif
if (unit == 253)
return zt_timer_poll(file, wait_table);
@@ -6480,7 +6524,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,
@@ -6555,6 +6599,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");
@@ -6567,6 +6612,7 @@ static int __init zt_init(void) {
devfs_register_chrdev(ZT_MAJOR, "zaptel", &zt_fops);
zaptel_devfs_dir = devfs_mk_dir(NULL, "zap", NULL);
if (!zaptel_devfs_dir) return -EBUSY; /* This would be bad */
+ transcode = devfs_register(zaptel_devfs_dir, "transcode", DEVFS_FL_DEFAULT, ZT_MAJOR, 250, mode, &zt_fops, NULL);
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);
@@ -6606,6 +6652,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);
@@ -6614,6 +6661,7 @@ static void __exit zt_cleanup(void) {
#else
#ifdef CONFIG_ZAP_UDEV
class_device_destroy(zap_class, MKDEV(ZT_MAJOR, 253)); /* timer */
+ class_device_destroy(zap_class, MKDEV(ZT_MAJOR, 250)); /* transcode */
class_device_destroy(zap_class, MKDEV(ZT_MAJOR, 254)); /* channel */
class_device_destroy(zap_class, MKDEV(ZT_MAJOR, 255)); /* pseudo */
class_device_destroy(zap_class, MKDEV(ZT_MAJOR, 0)); /* ctl */
diff --git a/zaptel.h b/zaptel.h
index c7b60f7..fefad1f 100644
--- a/zaptel.h
+++ b/zaptel.h
@@ -45,6 +45,7 @@
#include <linux/interrupt.h>
#endif
#include <linux/fs.h>
+#include <linux/list.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
#define LINUX26
@@ -666,6 +667,11 @@ void *data;
*/
#define ZT_SETPOLARITY _IOW (ZT_CODE, 92, int)
+/*
+* Transcoder operations
+*/
+#define ZT_TRANSCODE_OP _IOWR(ZT_CODE, 93, int)
+
/*
* Startup or Shutdown a span
*/
@@ -695,6 +701,47 @@ void *data;
#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_ALLOCATE 1 /* Allocate DTE channel */
+#define ZT_TCOP_TRANSCODE 2 /* Begin transcoding a block */
+#define ZT_TCOP_GETINFO 3 /* Get information (use zt_transcode_info) */
+#define ZT_TCOP_RELEASE 4 /* Release DTE channel */
+#define ZT_TCOP_TEST 5 /* test DTE device */
+struct zt_transcode_info {
+ __u32 op;
+ __u32 tcnum;
+ __u8 name[80];
+ __u32 numchannels;
+ __u32 srcfmts;
+ __u32 dstfmts;
+};
+
+#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)
+
+struct zt_transcode_header {
+ __u32 srcfmt; /* See formats.h -- use TCOP_RESET when you change */
+ __u32 srcoffset; /* In bytes -- written by user */
+ __u32 srclen; /* In bytes -- written by user */
+
+ __u32 dstfmt; /* See formats.h -- use TCOP_RESET when you change */
+ __u32 dstoffset; /* In bytes -- written by user */
+ __u32 dstlen; /* In bytes -- read by user */
+ __u32 dstsamples; /* In timestamp units -- read by user */
+
+ __u32 magic; /* Magic value -- ZT_TRANSCODE_MAGIC, read by user */
+ __u32 config; /* Read/write by user */
+ __u32 busy:1; /* Read/write by user */
+ __u8 userhdr[ZT_TRANSCODE_HDRLEN - (sizeof(__u32) * 14)]; /* Storage for user parameters */
+ __u8 srcdata[ZT_TRANSCODE_BUFSIZ / 2]; /* Storage of source data */
+ __u8 dstdata[ZT_TRANSCODE_BUFSIZ / 2]; /* Storage of destination data */
+};
+
struct zt_ring_cadence {
int ringcadence [ZT_MAX_CADENCE];
};
@@ -1345,6 +1392,35 @@ 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 chan_built:1;
+ unsigned int have_reference:1;
+ unsigned int busy:1;
+ unsigned int transient:1;
+ unsigned int built_fmts;
+ unsigned int flags;
+ unsigned int srcfmt;
+ unsigned int dstfmt;
+ struct zt_transcode_header *tch;
+};
+
+struct zt_transcoder {
+ struct list_head list;
+ struct module *owner;
+ 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
@@ -1390,10 +1466,23 @@ extern int zt_receive(struct zt_span *span);
/* Prepare writechunk buffers on all channels for this span */
extern int zt_transmit(struct zt_span *span);
-
/* 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);
+
+/* 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, struct module *owner);
+
+/* Unregister a transcoder */
+int zt_transcoder_unregister(struct zt_transcoder *tc);
+
+/* Alert a transcoder */
+int zt_transcoder_alert(struct zt_transcoder_channel *ztc);
+
/* Unregister a span */
extern int zt_unregister(struct zt_span *span);
@@ -1432,6 +1521,8 @@ extern struct zt_tone *zt_dtmf_tone(char digit, int mf);
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);
+extern struct file_operations *zt_transcode_fops;
+
/* Don't use these directly -- they're not guaranteed to
be there. */
extern short __zt_mulaw[256];
@@ -1611,5 +1702,32 @@ 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)
#endif /* _LINUX_ZAPTEL_H */
diff --git a/zonedata.c b/zonedata.c
index dbfef0d..0d57f87 100644
--- a/zonedata.c
+++ b/zonedata.c
@@ -23,6 +23,7 @@
* UK information from BT SIN 350 Issue 1.1
* Helpful reference: http://www.itu.int/ITU-T/inr/forms/files/tones-0203.pdf
*/
+#include <linux/types.h>
#include "tonezone.h"
struct tone_zone builtin_zones[] =
diff --git a/ztcfg.c b/ztcfg.c
index ae03547..ec1fdf6 100644
--- a/ztcfg.c
+++ b/ztcfg.c
@@ -38,6 +38,7 @@
#include <fcntl.h>
#include <errno.h>
#include "ztcfg.h"
+#include <linux/types.h>
#include "tonezone.h"
#include "zaptel.h"
diff --git a/ztd-eth.c b/ztd-eth.c
index 26737b9..8dec7a2 100644
--- a/ztd-eth.c
+++ b/ztd-eth.c
@@ -36,6 +36,7 @@
#ifdef CONFIG_DEVFS_FS
#include <linux/devfs_fs_kernel.h>
#endif
+#include <linux/types.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#else
diff --git a/ztd-loc.c b/ztd-loc.c
index 100bd9f..ff9ba73 100644
--- a/ztd-loc.c
+++ b/ztd-loc.c
@@ -60,6 +60,7 @@
#ifdef CONFIG_DEVFS_FS
#include <linux/devfs_fs_kernel.h>
#endif
+#include <linux/types.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#else
diff --git a/ztdiag.c b/ztdiag.c
index e2be5f3..864f19a 100644
--- a/ztdiag.c
+++ b/ztdiag.c
@@ -4,6 +4,7 @@
#include <stdlib.h>
#include <unistd.h>
+#include <linux/types.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#else
diff --git a/ztdummy.c b/ztdummy.c
index df2f19b..0b2c111 100644
--- a/ztdummy.c
+++ b/ztdummy.c
@@ -59,6 +59,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
+#include <linux/types.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#else
diff --git a/ztdynamic.c b/ztdynamic.c
index 825f6ee..df68b5a 100644
--- a/ztdynamic.c
+++ b/ztdynamic.c
@@ -36,6 +36,7 @@
#ifdef CONFIG_DEVFS_FS
#include <linux/devfs_fs_kernel.h>
#endif
+#include <linux/types.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#else
diff --git a/ztmonitor.c b/ztmonitor.c
index 6098b66..be307d7 100644
--- a/ztmonitor.c
+++ b/ztmonitor.c
@@ -38,6 +38,7 @@
#include <sys/time.h>
#include <fcntl.h>
#include <errno.h>
+#include <linux/types.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#include "tonezone.h"
diff --git a/zttool.c b/zttool.c
index 2a3cb34..34b8f79 100644
--- a/zttool.c
+++ b/zttool.c
@@ -38,6 +38,7 @@
#include <fcntl.h>
#include <errno.h>
#include <newt.h>
+#include <linux/types.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#include "tonezone.h"
diff --git a/zttranscode.c b/zttranscode.c
new file mode 100644
index 0000000..60ff0ca
--- /dev/null
+++ b/zttranscode.c
@@ -0,0 +1,489 @@
+/*
+ * 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>
+#include <linux/types.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 LIST_HEAD(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;
+ INIT_LIST_HEAD(&ztc->list);
+ for (x=0;x<ztc->numchannels;x++) {
+ init_waitqueue_head(&ztc->channels[x].ready);
+ ztc->channels[x].parent = ztc;
+ ztc->channels[x].offset = x;
+ ztc->channels[x].chan_built = 0;
+ ztc->channels[x].built_fmts = 0;
+ }
+
+ 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);
+}
+
+int zt_transcoder_register(struct zt_transcoder *tc, struct module *owner)
+{
+ struct zt_transcoder *cur;
+
+ spin_lock(&translock);
+ list_for_each_entry(cur, &trans, list) {
+ if (cur == tc) {
+ spin_unlock(&translock);
+ return -EBUSY;
+ }
+ }
+
+ tc->owner = owner;
+ list_add(&tc->list, &trans);
+ printk("Registered codec translator '%s' with %d transcoders (srcs=%08x, dsts=%08x)\n",
+ tc->name, tc->numchannels, tc->srcfmts, tc->dstfmts);
+ spin_unlock(&translock);
+
+ return 0;
+}
+
+/* Unregister a transcoder */
+int zt_transcoder_unregister(struct zt_transcoder *tc)
+{
+ struct zt_transcoder *cur, *next;
+
+ spin_lock(&translock);
+ list_for_each_entry_safe(cur, next, &trans, list) {
+ if (cur == tc) {
+ list_del_init(&cur->list);
+ break;
+ }
+ }
+
+ if (!cur) {
+ spin_unlock(&translock);
+ return -EINVAL;
+ }
+
+ printk("Unregistered codec translator '%s' with %d transcoders (srcs=%08x, dsts=%08x)\n",
+ tc->name, tc->numchannels, tc->srcfmts, tc->dstfmts);
+ spin_unlock(&translock);
+
+ return 0;
+}
+
+/* Alert a transcoder */
+int zt_transcoder_alert(struct zt_transcoder_channel *ztc)
+{
+ if (debug)
+ printk("ZT Transcoder Alert!\n");
+ if (ztc->tch)
+ ztc->tch->busy = 0;
+ 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;
+ struct page *page;
+
+ if (!(ztc = kmalloc(sizeof(*ztc), GFP_KERNEL)))
+ return -ENOMEM;
+
+ if (!(zth = kmalloc(sizeof(*zth), GFP_KERNEL))) {
+ kfree(ztc);
+ return -ENOMEM;
+ }
+
+ memset(ztc, 0, sizeof(*ztc));
+ memset(zth, 0, sizeof(*zth));
+ ztc->transient = 1;
+ ztc->busy = 1;
+ ztc->tch = zth;
+ if (debug)
+ printk("Allocated Transcoder Channel, header is at %p!\n", zth);
+ zth->magic = ZT_TRANSCODE_MAGIC;
+ file->private_data = ztc;
+ for (page = virt_to_page(zth);
+ page < virt_to_page((unsigned long) zth + sizeof(*zth));
+ page++)
+ SetPageReserved(page);
+
+ return 0;
+}
+
+static void ztc_release(struct zt_transcoder_channel *ztc)
+{
+ struct zt_transcode_header *zth = ztc->tch;
+ struct page *page;
+
+ if (!ztc)
+ return;
+
+ ztc->busy = 0;
+
+ if (ztc->tch) {
+ for (page = virt_to_page(zth);
+ page < virt_to_page((unsigned long) zth + sizeof(*zth));
+ page++)
+ ClearPageReserved(page);
+ kfree(ztc->tch);
+ }
+
+ ztc->tch = NULL;
+
+ if (ztc->have_reference) {
+ module_put(ztc->parent->owner);
+ ztc->have_reference = 0;
+ }
+
+ if (ztc->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_allocate(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);
+ list_for_each_entry(tc, &trans, list) {
+ 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].busy)
+ continue;
+
+ if (tc->channels[x].chan_built &&
+ ((zth->srcfmt | zth->dstfmt) != tc->channels[x].built_fmts))
+ continue;
+
+ if (!try_module_get(tc->owner))
+ continue;
+
+ newztc = &tc->channels[x];
+ newztc->busy = 1;
+ newztc->have_reference = 1;
+ 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)->transient = 0;
+ ztc_release(origztc);
+ }
+
+ /* Actually reset the transcoder channel */
+ if ((*ztc)->parent && ((*ztc)->parent->operation))
+ return (*ztc)->parent->operation((*ztc), ZT_TCOP_ALLOCATE);
+
+ return -EINVAL;
+}
+
+static int wait_busy(struct zt_transcoder_channel *ztc)
+{
+ int ret;
+
+ for (;;) {
+ if (!(ztc->tch->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);
+ x = info.tcnum;
+ list_for_each_entry(tc, &trans, list) {
+ if (!x)
+ break;
+ else
+ x--;
+ }
+ spin_unlock(&translock);
+
+ if (x || !tc)
+ return -ENOSYS;
+
+ strncpy(info.name, tc->name, sizeof(info.name) - 1);
+ info.numchannels = tc->numchannels;
+ 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_ALLOCATE:
+ ret = do_allocate(&ztc);
+ file->private_data = ztc;
+ break;
+ case ZT_TCOP_RELEASE:
+ ret = ztc->parent->operation(ztc, ZT_TCOP_RELEASE);
+ if (ztc->have_reference) {
+ module_put(ztc->parent->owner);
+ ztc->have_reference = 0;
+ }
+ break;
+ case ZT_TCOP_TRANSCODE:
+ if (!ztc->parent->operation)
+ return -EINVAL;
+
+ ztc->tch->busy = 1;
+ if (!(ret = ztc->parent->operation(ztc, ZT_TCOP_TRANSCODE))) {
+ /* Wait for busy to go away if we are in blocking mode */
+ if (!(file->f_flags & O_NONBLOCK)) {
+ if (!(ret = wait_busy(ztc)))
+ ret = ztc->errorstatus;
+ }
+ } else
+ ztc->tch->busy = 0;
+ 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;
+ int res;
+
+ 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 LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+ res = remap_pfn_range(vma, vma->vm_start, physical >> PAGE_SHIFT, sizeof(struct zt_transcode_header), PAGE_SHARED);
+#else
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ res = remap_page_range(vma->vm_start, physical, sizeof(struct zt_transcode_header), PAGE_SHARED);
+ #else
+ res = remap_page_range(vma, vma->vm_start, physical, sizeof(struct zt_transcode_header), PAGE_SHARED);
+ #endif
+#endif
+ if (res) {
+ 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->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, S_IRUGO | S_IWUSR);
+#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);