diff options
author | kpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2007-02-23 23:57:10 +0000 |
---|---|---|
committer | kpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2007-02-23 23:57:10 +0000 |
commit | df1e7f7b0470bd759c9687eeffdcbeee02778008 (patch) | |
tree | fbd777063821d50d81aeb1ed0999f4eb6cfd1671 | |
parent | e30d85766ea8d6b623f0964cbf8129c6c7700462 (diff) |
partial update to support all the stuff that went into Zaptel 1.2 recently... this definitely still broken for 2.4 kernels, and maybe for 2.6 as well :-)
includes:
Makefile split and reorganize
TE120P driver
TC400B driver
HPEC support (not yet complete)
git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.4@2215 5390a7c7-147a-4af0-8ec9-7488f05a26cb
-rw-r--r-- | Makefile | 369 | ||||
-rw-r--r-- | Makefile.kernel26 | 15 | ||||
-rw-r--r-- | datamods/Makefile | 4 | ||||
-rw-r--r-- | firmware/firmware.xml | 9 | ||||
-rw-r--r-- | hpec/hpec.h | 47 | ||||
-rw-r--r-- | hpec/hpec_user.h | 40 | ||||
-rw-r--r-- | hpec/hpec_zaptel.h | 137 | ||||
-rw-r--r-- | wct4xxp/Makefile | 2 | ||||
-rw-r--r-- | wct4xxp/Makefile.kernel26 (renamed from wct4xxp/Kbuild) | 0 | ||||
-rw-r--r-- | wctc4xxp/Makefile | 13 | ||||
-rw-r--r-- | wctc4xxp/Makefile.kernel26 | 15 | ||||
-rw-r--r-- | wctc4xxp/base.c | 1813 | ||||
-rw-r--r-- | wctc4xxp/codec_test.c | 332 | ||||
-rw-r--r-- | wctc4xxp/tc400m-firmware.bin | bin | 0 -> 1400630 bytes | |||
-rw-r--r-- | wcte12xp.c | 2046 | ||||
-rw-r--r-- | zaptel-base.c (renamed from zaptel.c) | 0 | ||||
-rw-r--r-- | zaptel.h | 48 | ||||
-rw-r--r-- | zaptel.xml | 20 | ||||
-rw-r--r-- | zttranscode.c | 36 |
19 files changed, 4711 insertions, 235 deletions
@@ -1,21 +1,22 @@ # # Makefile for Zaptel driver modules and utilities # -# Copyright (C) 2001-2006 Digium, Inc. +# Copyright (C) 2001-2007 Digium, Inc. # # -.EXPORT_ALL_VARIABLES: - -.PHONY: menuselect distclean dist-clean clean version.h all _all install b410p devices programs modules linux24 linux26 xpp tests devel data stackcheck install-udev config update install-programs install-modules install-linux24 install-linux26 firmware install-include install-libs xpp-install xpp-utils +CFLAGS+=-DSTANDALONE_ZAPATA -DBUILDING_TONEZONE -PWD=$(shell pwd) +ifeq ($(MAKELEVEL),0) +PWD:=$(shell pwd) +endif ifeq ($(DEB_HOST_GNU_TYPE),) UNAME_M:=$(shell uname -m) else UNAME_M:=$(DEB_HOST_GNU_TYPE) endif + # If you want to build for a kernel other than the current kernel, set KVERS ifndef KVERS KVERS:=$(shell uname -r) @@ -31,6 +32,85 @@ endif KVERS_MAJ:=$(shell echo $(KVERS) | cut -d. -f1-2) KINCLUDES:=$(KSRC)/include +ifeq ($(KVERS_MAJ),2.4) + BUILDVER:=linux24 +else + BUILDVER:=linux26 +endif + +# Set HOTPLUG_FIRMWARE=no to override automatic building with hotplug support +# if it is enabled in the kernel. +ifeq ($(BUILDVER),linux26) + ifneq (,$(wildcard $(INSTALL_PREFIX)/etc/udev/rules.d)) + DYNFS=yes + UDEVRULES=yes + endif + HOTPLUG_FIRMWARE:=$(shell if grep CONFIG_FW_LOADER $(KINCLUDES)/linux/autoconf.h | grep -q undef; then echo "no"; else echo "yes"; fi) +endif + +ifeq ($(HOTPLUG_FIRMWARE),yes) + CFLAGS+=-DHOTPLUG_FIRMWARE +endif + +ifeq ($(ARCH),i386) +ifneq ($(wildcard $(PWD)/hpec/hpec_x86_32.o_shipped),) +HPEC_PRESENT=yes +endif +endif + +ifeq ($(ARCH),x86_64) +ifneq ($(wildcard $(PWD)/hpec/hpec_x86_64.o_shipped),) +HPEC_PRESENT=yes +endif +endif + +ifeq ($(BUILDVER),linux24) +MENUSELECT_MODULES+=xpp wctc4xxp zttranscode +endif + +ifeq ($(findstring xpp,$(MENUSELECT_MODULES)),) + BUILD_XPP:=yes +endif + +TOPDIR_MODULES:=pciradio tor2 torisa wcfxo wct1xxp wctdm wctdm24xxp wcte11xp wcusb zaptel ztd-eth ztd-loc ztdummy ztdynamic zttranscode wcte12xp +SUBDIR_MODULES:=wct4xxp wctc4xxp xpp +BUILD_TOPDIR_MODULES:=$(filter-out $(MENUSELECT_MODULES),$(TOPDIR_MODULES)) +BUILD_SUBDIR_MODULES:=$(filter-out $(MENUSELECT_MODULES),$(SUBDIR_MODULES)) +BUILD_MODULES:=$(BUILD_TOPDIR_MODULES) $(BUILD_SUBDIR_MODULES) + +MOD_DESTDIR:=zaptel + +#NOTE NOTE NOTE +# +# all variables set before the include of Makefile.kernel26 are needed by the 2.6 kernel module build process + +ifneq ($(KBUILD_EXTMOD),) + +obj-m:=$(BUILD_TOPDIR_MODULES:%=%.o) +obj-m+=$(BUILD_SUBDIR_MODULES:%=%/) + +include $(src)/Makefile.kernel26 + +else + +ifeq ($(BUILDVER),linux24) + INSTALL_MODULES:=$(BUILD_TOPDIR_MODULES:%=%.o) + INSTALL_MODULES+=$(BUILD_SUBDIR_MODULES:%=%/%.o) + ALL_MODULES:=$(TOPDIR_MODULES:%=%.o) + ALL_MODULES+=$(SUBDIR_MODULES:%=%/%.o) +else + INSTALL_MODULES:=$(BUILD_TOPDIR_MODULES:%=%.ko) + INSTALL_MODULES+=$(foreach mod,$(filter-out xpp,$(BUILD_SUBDIR_MODULES)),$(mod)/$(mod).ko) + ifneq ($(filter xpp,$(BUILD_SUBDIR_MODULES)),) + INSTALL_MODULES+=$(patsubst %,xpp/%.ko,xpp_usb xpd_fxo xpd_fxs xpp) + endif + + ALL_MODULES:=$(TOPDIR_MODULES:%=%.ko) + ALL_MODULES+=$(foreach mod,$(filter-out xpp,$(SUBDIR_MODULES)),$(mod)/$(mod).ko) + ALL_MODULES+=$(patsubst %,xpp/%.ko,xpp_usb xpd_fxo xpd_fxs xpp) + endif +endif + CFLAGS+=-I. -O4 -g -Wall -DBUILDING_TONEZONE #-DTONEZONE_DRIVER ifneq (,$(findstring ppc,$(UNAME_M))) CFLAGS_PPC:=-fsigned-char @@ -81,6 +161,8 @@ endif # Features are now configured in zconfig.h # +MODULE_ALIASES=wcfxs wctdm8xxp wct2xxp + KFLAGS+=-DSTANDALONE_ZAPATA CFLAGS+=-DSTANDALONE_ZAPATA KMAKE = $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) @@ -93,23 +175,6 @@ INSTALL_PREFIX=$(DESTDIR) CONFIG_FILE=$(INSTALL_PREFIX)/etc/zaptel.conf CFLAGS+=-DZAPTEL_CONFIG=\"$(CONFIG_FILE)\" -ifeq ($(KVERS_MAJ),2.4) - BUILDVER:=linux24 -else - BUILDVER:=linux26 -endif - -# This is not related to the version that we build. Rather, to the -# version that we runs. If we build for 2.4 using 2.4 headers on a 2.6 -# system with udev mounted on /dev , no point in installing files to /dev -# because they'll be wiped at next reboot. -DYNFS:=$(shell ps ax | grep -v grep | grep -qw 'devfsd\|udevd' && echo "yes") - -# Check for udev rules directories -ifneq (,$(wildcard $(ROOT_PREFIX)/etc/udev/rules.d)) - UDEVRULES=yes -endif - CHKCONFIG:=$(shell sh -c 'type -p chkconfig' 2> /dev/null) ifndef CHKCONFIG CHKCONFIG:=: @@ -130,56 +195,11 @@ LTZ_SO_OBJS:=zonedata.lo tonezone.lo LTZ_SO_MAJOR_VER:=1 LTZ_SO_MINOR_VER:=0 -ifeq ($(findstring xpp_usb,$(MENUSELECT_MODULES)),) - BUILD_XPP:=yes -endif -MODULES:=pciradio tor2 torisa wcfxo wct1xxp wctdm wctdm24xxp wcte11xp wcusb zaptel ztd-eth ztd-loc ztdummy ztdynamic zttranscode -MODULES:=$(filter-out $(MENUSELECT_MODULES),$(MODULES)) -MODULE_ALIASES=wcfxs wctdm8xxp wct2xxp - -MODULESO:=$(MODULES:%=%.o) -MODULESKO:=$(MODULES:%=%.ko) -ifeq ($(BUILDVER),linux26) -MODULES_BUILD:=$(MODULESKO) -HOTPLUG_FIRMWARE:=$(shell if grep CONFIG_FW_LOADER $(KINCLUDES)/linux/autoconf.h | grep -q undef; then echo "no"; else echo "yes"; fi) -else -MODULES_BUILD:=$(MODULESO) -endif - BIN_DIR:=$(INSTALL_PREFIX)/sbin LIB_DIR:=$(INSTALL_PREFIX)/usr/lib INC_DIR:=$(INSTALL_PREFIX)/usr/include MOD_DIR:=$(INSTALL_PREFIX)/lib/modules/$(KVERS)/misc -MOD_DESTDIR:=zaptel - -obj-m:=$(MODULESO) -ifeq ($(findstring wct4xxp,$(MENUSELECT_MODULES)),) -obj-m+=wct4xxp/ -MODULES+=wct4xxp -endif - -# Set this to override hotplug firmware loading and revert to classic header -#HOTPLUG_FIRMWARE=no -ifeq ($(HOTPLUG_FIRMWARE),yes) -CFLAGS+=-DHOTPLUG_FIRMWARE -endif - -# Also build xpp in the subdirectory xpp/ . But only for >=2.6.8 and only -# for i386 or x86_64. On other platforms it has still not been tested well -# enough. -# This does not affect kernel 2.4, because obj-m is only used for kernel -# 2.6's build. Note that it is only actually tested by the kernel 2.6 build -# system and not by the main makefile. -ifneq (,$(shell [ 0$(SUBLEVEL) -ge 8 ] && echo 1)) -ifeq ($(ARCH),i386) -obj-m+=xpp/ -endif -ifeq ($(ARCH),x86_64) -obj-m+=xpp/ -endif -endif - BINS:=fxotune fxstest sethdlc-new ztcfg ztdiag ztmonitor ztspeed zttest zttool UTILS:=$(filter-out zttool fxstest ztcfg,$(BINS)) UTILSO:=$(UTILS:%=%.o) @@ -188,7 +208,7 @@ BINS:=$(filter-out $(MENUSELECT_UTILS),$(BINS)) all: menuselect.makeopts @$(MAKE) _all -_all: $(if $(MODULES),modules) programs $(LTZ_SO) $(LTZ_A) +_all: $(if $(BUILD_MODULES),modules) programs $(LTZ_SO) $(LTZ_A) xpp-utils: ifeq (yes,$(BUILD_XPP)) @@ -197,15 +217,13 @@ endif programs: $(BINS) xpp-utils -modules: $(BUILDVER) - -linux24: prereq $(MODULESO) wct4xxp/wct4xxp.o - -linux26: prereq +modules: prereq @if [ -z "$(KSRC)" -o ! -d "$(KSRC)" ]; then echo "You do not appear to have the sources for the $(KVERS) kernel installed."; exit 1 ; fi - $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) HOTPLUG_FIRMWARE=$(HOTPLUG_FIRMWARE) modules - -xpp: linux26 +ifeq ($(BUILDVER),linux26) + $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) modules +else +modules: $(BUILD_MODULES) +endif version.h: @ZAPTELVERSION="${ZAPTELVERSION}" build_tools/make_version_h > $@.tmp @@ -214,32 +232,8 @@ version.h: fi @rm -f $@.tmp -wct4xxp/wct4xxp.o: - $(MAKE) -C wct4xxp KFLAGS="$(KFLAGS) -I.." CFLAGS="$(CFLAGS) -I.." - -devel: tor2ee - tests: patgen pattest patlooptest hdlcstress hdlctest hdlcgen hdlcverify timertest -tor2.o: tor2-hw.h tor2fw.h - -zaptel.o: digits.h arith.h sec.h mec.h sec-2.h mec2.h mec3.h zconfig.h - -wcusb.o: wcusb.h - -wcfxsusb.o: wcfxsusb.h - -wctdm.o: wctdm.h - -wctdm24xxp.o: wctdm.h - -pciradio.o: radfw.h - -ztdummy.o: ztdummy.h - -$(MODULESO): %.o: %.c zaptel.h - $(CC) $(KFLAGS) -o $@ -c $< - tor2ee.o: tor2-hw.h tor2ee: LDLIBS+=-lpci @@ -268,8 +262,6 @@ makefw: makefw.c gendigits: LDLIBS+=-lm gendigits: CFLAGS= -zaptel.c: tones.h - prereq: config.status tones.h tor2fw.h radfw.h version.h zttool.o: zttool.c zaptel.h @@ -317,6 +309,34 @@ fxstest: LDLIBS+=-lm fxotune: LDLIBS+=-lm fxsdump: LDLIBS+=-lm +devel: tor2ee + +ifeq ($(BUILDVER),linux24) +wct4xxp/wct4xxp.o: + $(MAKE) -C wct4xxp KFLAGS="$(KFLAGS) -I.." CFLAGS="$(CFLAGS) -I.." + +tor2.o: tor2-hw.h tor2fw.h + +zaptel.o: digits.h arith.h sec.h mec.h sec-2.h mec2.h mec3.h zconfig.h + +wcusb.o: wcusb.h + +wcfxsusb.o: wcfxsusb.h + +wctdm.o: wctdm.h + +wctdm24xxp.o: wctdm.h + +pciradio.o: radfw.h + +ztdummy.o: ztdummy.h + +$(BUILD_MODULES): %.o: %.c zaptel.h + $(CC) $(KFLAGS) -o $@ -c $< + +zaptel.c: tones.h +endif + stackcheck: checkstack $(BUILDVER) ./checkstack *.o @@ -346,51 +366,15 @@ $(UTILS): %: %.o $(UTILSO): %.o: %.c $(CC) $(CFLAGS) -o $@ -c $< -devices: -ifndef DYNFS - mkdir -p $(INSTALL_PREFIX)/dev/zap - rm -f $(INSTALL_PREFIX)/dev/zap/ctl - rm -f $(INSTALL_PREFIX)/dev/zap/channel - rm -f $(INSTALL_PREFIX)/dev/zap/pseudo - rm -f $(INSTALL_PREFIX)/dev/zap/timer - rm -f $(INSTALL_PREFIX)/dev/zap/transcode - rm -f $(INSTALL_PREFIX)/dev/zap/253 - rm -f $(INSTALL_PREFIX)/dev/zap/252 - rm -f $(INSTALL_PREFIX)/dev/zap/251 - rm -f $(INSTALL_PREFIX)/dev/zap/250 - mknod $(INSTALL_PREFIX)/dev/zap/ctl c 196 0 - mknod $(INSTALL_PREFIX)/dev/zap/transcode c 196 250 - mknod $(INSTALL_PREFIX)/dev/zap/timer c 196 253 - mknod $(INSTALL_PREFIX)/dev/zap/channel c 196 254 - mknod $(INSTALL_PREFIX)/dev/zap/pseudo c 196 255 - N=1; \ - while [ $$N -lt 250 ]; do \ - rm -f $(INSTALL_PREFIX)/dev/zap/$$N; \ - mknod $(INSTALL_PREFIX)/dev/zap/$$N c 196 $$N; \ - N=$$[$$N+1]; \ - done -else # DYNFS - ifdef UDEVRULES - build_tools/genudevrules > $(INSTALL_PREFIX)/etc/udev/rules.d/zaptel.rules - else # !UDEVRULES - @echo "**** Dynamic filesystem detected -- not creating device nodes" - endif -endif - hotplug-uninstall: $(MAKE) -C firmware hotplug-uninstall -xpp-install: -ifeq (yes,$(BUILD_XPP)) - @$(MAKE) -C xpp/utils install -endif - -install: all devices firmware install-libs install-include xpp-install +install: all devices install-modules install-libs install-include install-xpp ifeq ($(HOTPLUG_FIRMWARE),yes) $(MAKE) -C firmware hotplug-install endif if [ -f ztcfg ]; then \ - $(INSTALL) -D -m 755 ztcfg $(INSTALL_PREFIX)/sbin/ztcfg; \ + $(INSTALL -D -m 755 ztcfg $(INSTALL_PREFIX)/sbin/ztcfg; \ fi if [ -f sethdlc-new ]; then \ $(INSTALL) -D -m 755 sethdlc-new $(INSTALL_PREFIX)/sbin/sethdlc; \ @@ -400,30 +384,11 @@ endif if [ -f zttool ]; then \ $(INSTALL) -D -m 755 zttool $(INSTALL_PREFIX)/sbin/zttool; \ fi -ifeq ($(BUILDVER),linux26) - for x in $(MODULESKO); do \ - rm -f $(INSTALL_PREFIX)/lib/modules/$(KVERS)/extra/$$x ; \ - done; \ - $(KMAKE_INST); \ - if [ -f datamods/syncppp.ko ]; then \ - $(MAKE) -C datamods install; \ - else \ - rm -f $(INSTALL_PREFIX)/lib/modules/$(KVERS)/misc/{hdlc_*,syncppp}.ko; \ - fi -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; \ - fi; \ - rm -f $(INSTALL_PREFIX)/lib/modules/$(KVERS)/misc/wcfxs.o $(INSTALL) -m 644 doc/ztcfg.8 $(INSTALL_PREFIX)/usr/share/man/man8 $(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) "$(ROOT_PREFIX)" "$(filter-out zaptel,$(MODULES)) $(MODULE_ALIASES)" + build_tools/genmodconf $(BUILDVER) "$(ROOT_PREFIX)" "$(filter-out zaptel xpp zttranscode ztdynamic,$(BUILD_MODULES)) $(MODULE_ALIASES)" @if [ -d /etc/modutils ]; then \ /sbin/update-modules ; \ fi @@ -441,6 +406,11 @@ install-libs: $(LTZ_SO) $(LTZ_A) $(LIB_DIR)/$(LTZ_SO) if [ -z "$(INSTALL_PREFIX)" ] && [ -x /usr/sbin/sestatus ] && (/usr/sbin/sestatus | grep "SELinux status:" | grep -q "enabled") ; then /sbin/restorecon -v $(LIB_DIR)/$(LTZ_SO); fi +install-xpp: +ifeq (yes,$(BUILD_XPP)) + @$(MAKE) -C xpp/utils install +endif + install-include: $(INSTALL) -D -m 644 zaptel.h $(INSTALL_PREFIX)/usr/include/zaptel/zaptel.h $(INSTALL) -D -m 644 tonezone.h $(INSTALL_PREFIX)/usr/include/zaptel/tonezone.h @@ -450,8 +420,65 @@ install-include: rm -f $(INSTALL_PREFIX)/usr/include/torisa.h rm -f $(INSTALL_PREFIX)/usr/include/tonezone.h +devices: +ifndef DYNFS + mkdir -p $(INSTALL_PREFIX)/dev/zap + rm -f $(INSTALL_PREFIX)/dev/zap/ctl + rm -f $(INSTALL_PREFIX)/dev/zap/channel + rm -f $(INSTALL_PREFIX)/dev/zap/pseudo + rm -f $(INSTALL_PREFIX)/dev/zap/timer + rm -f $(INSTALL_PREFIX)/dev/zap/transcode + rm -f $(INSTALL_PREFIX)/dev/zap/253 + rm -f $(INSTALL_PREFIX)/dev/zap/252 + rm -f $(INSTALL_PREFIX)/dev/zap/251 + rm -f $(INSTALL_PREFIX)/dev/zap/250 + mknod $(INSTALL_PREFIX)/dev/zap/ctl c 196 0 + mknod $(INSTALL_PREFIX)/dev/zap/transcode c 196 250 + mknod $(INSTALL_PREFIX)/dev/zap/timer c 196 253 + mknod $(INSTALL_PREFIX)/dev/zap/channel c 196 254 + mknod $(INSTALL_PREFIX)/dev/zap/pseudo c 196 255 + N=1; \ + while [ $$N -lt 250 ]; do \ + rm -f $(INSTALL_PREFIX)/dev/zap/$$N; \ + mknod $(INSTALL_PREFIX)/dev/zap/$$N c 196 $$N; \ + N=$$[$$N+1]; \ + done +else # DYNFS + ifdef UDEVRULES + build_tools/genudevrules > $(INSTALL_PREFIX)/etc/udev/rules.d/zaptel.rules + else # !UDEVRULES + @echo "**** Dynamic filesystem detected -- not creating device nodes" + endif +endif + install-udev: devices +# make should *fail* and not silently succeed if a program did not build +install-programs: $(BINS) + $(INSTALL) -d $(BIN_DIR) + $(INSTALL) $(BINS) $(BIN_DIR) + +uninstall-modules: +ifeq ($(BUILDVER),linux24) +else + for x in $(ALL_MODULES); do \ + rm -f $(INSTALL_PREFIX)/lib/modules/$(KVERS)/extra/$$x ; \ + rm -f $(INSTALL_PREFIX)/lib/modules/$(KVERS)/misc/$$x ; \ + rm -f $(INSTALL_PREFIX)/lib/modules/$(KVERS)/zaptel/$$x ; \ + done; \ +endif + +install-modules: $(INSTALL_MODULES) uninstall-modules +ifeq ($(BUILDVER),linux24) + $(INSTALL) -d $(MOD_DIR) + $(INSTALL) -m 644 $(INSTALL_MODULES) $(MOD_DIR) +else + $(KMAKE_INST) + if [ -f datamods/syncppp.ko ]; then \ + $(MAKE) -C datamods install; \ + fi +endif + config: if [ -d $(INSTALL_PREFIX)/etc/rc.d/init.d ]; then \ $(INSTALL) -D -m 755 zaptel.init $(INSTALL_PREFIX)/etc/rc.d/init.d/zaptel; \ @@ -488,19 +515,6 @@ update: echo "Not under version control"; \ fi -# make should *fail* and not silently succeed if a program did not build -install-programs: $(BINS) install-libs install-include - $(INSTALL) -d $(BIN_DIR) - $(INSTALL) $(BINS) $(BIN_DIR) - -install-modules: install-$(BUILDVER) -install-linux24: $(MODULES_BUILD) - $(INSTALL) -d $(MOD_DIR) - $(INSTALL) -m 644 $(MODULES_BUILD) $(MOD_DIR) - -install-linux26: $(MODULESKO) - $(KMAKE_INST) - clean: -@$(MAKE) -C menuselect clean rm -f torisatool makefw tor2fw.h radfw.h @@ -509,13 +523,11 @@ clean: rm -f $(LTZ_SO) $(LTZ_A) *.lo ifeq ($(BUILDVER),linux26) $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) clean + $(MAKE) -C xpp/utils clean else $(MAKE) -C wct4xxp clean endif $(MAKE) -C firmware clean - -@$(MAKE) -C xpp/utils clean - rm -f xpp/*.ko xpp/*.mod.c xpp/.*o.cmd - rm -f xpp/*.o xpp/*.mod.o rm -rf .tmp_versions rm -f gendigits tones.h rm -f libtonezone* @@ -556,3 +568,8 @@ menuselect/menuselect: menuselect/menuselect.c menuselect/menuselect_curses.c me menuselect-tree: zaptel.xml firmware/firmware.xml @echo "Generating input for menuselect ..." @build_tools/make_tree > $@ + +.EXPORT_ALL_VARIABLES: + +.PHONY: menuselect distclean dist-clean clean version.h all _all install b410p devices programs modules tests devel data stackcheck install-udev config update install-programs install-modules install-include install-libs install-xpp xpp-utils uninstall-modules +endif diff --git a/Makefile.kernel26 b/Makefile.kernel26 new file mode 100644 index 0000000..97a4e59 --- /dev/null +++ b/Makefile.kernel26 @@ -0,0 +1,15 @@ +EXTRA_CFLAGS := -I$(src) + +zaptel-objs := zaptel-base.o + +ifeq ($(HPEC_PRESENT),yes) +ifeq ($(ARCH),i386) +zaptel-objs += hpec/hpec_x86_32.o +endif + +ifeq ($(ARCH),x86_64) +zaptel-objs += hpec/hpec_x86_64.o +endif + +EXTRA_CFLAGS += -DECHO_CAN_HPEC -I$(src)/hpec +endif diff --git a/datamods/Makefile b/datamods/Makefile index c84dc32..310073e 100644 --- a/datamods/Makefile +++ b/datamods/Makefile @@ -2,7 +2,7 @@ MODULES= \ hdlc_cisco hdlc_generic hdlc_raw syncppp \ hdlc_fr hdlc_ppp hdlc_raw_eth - + PWD=$(shell pwd) @@ -24,7 +24,7 @@ clean: install: $(MODULESKO) $(KMAKE_INST) - + datamods: @echo "To build: $(obj-m)" @echo $(KSRC) diff --git a/firmware/firmware.xml b/firmware/firmware.xml index f7baa46..272f9ee 100644 --- a/firmware/firmware.xml +++ b/firmware/firmware.xml @@ -1,11 +1,14 @@ <category name="MENUSELECT_FIRMWARE" displayname="Binary Firmware Packages" positive_output="yes"> - <member name="FIRMWARE-OCT6114-064" displayname="Oct6114 064 Echo Cancellation Firmware" > + <member name="FIRMWARE-OCT6114-064" displayname="Digium VPMOCT064M" > <defaultenabled>yes</defaultenabled> + <depend>wct4xxp</depend> </member> - <member name="FIRMWARE-OCT6114-128" displayname="Oct6114 128 Echo Cancellation Firmware" > + <member name="FIRMWARE-OCT6114-128" displayname="Digium VPMOCT128M/VPM450M"> <defaultenabled>yes</defaultenabled> + <depend>wct4xxp</depend> </member> - <member name="FIRMWARE-TC400M" displayname="TC400M Module Firmware" > + <member name="FIRMWARE-TC400M" displayname="Digium TC400M" > <defaultenabled>yes</defaultenabled> + <depend>wctc4xxp</depend> </member> </category> diff --git a/hpec/hpec.h b/hpec/hpec.h new file mode 100644 index 0000000..7e90f12 --- /dev/null +++ b/hpec/hpec.h @@ -0,0 +1,47 @@ +/* + * Zapata Telephony Interface to Digium High-Performance Echo Canceller + * + * 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. + */ + +#if !defined(_HPEC_H) +#define _HPEC_H + +struct echo_can_state; + +void __attribute__((regparm(0))) hpec_init(int __attribute__((regparm(0))) __attribute__((format (printf, 1, 2))) (*logger)(const char *format, ...), + unsigned int debug, + unsigned int chunk_size, + void * (*memalloc)(size_t len), + void (*memfree)(void *ptr)); + +void __attribute__((regparm(0))) hpec_shutdown(void); + +int __attribute__((regparm(0))) hpec_license_challenge(struct hpec_challenge *challenge); + +int __attribute__((regparm(0))) hpec_license_check(struct hpec_license *license); + +struct echo_can_state __attribute__((regparm(0))) *hpec_channel_alloc(unsigned int len); + +void __attribute__((regparm(0))) hpec_channel_free(struct echo_can_state *channel); + +void __attribute__((regparm(0))) hpec_channel_update(struct echo_can_state *channel, short *iref, short *isig); + +#endif /* !defined(_HPEC_H) */ + diff --git a/hpec/hpec_user.h b/hpec/hpec_user.h new file mode 100644 index 0000000..bf006eb --- /dev/null +++ b/hpec/hpec_user.h @@ -0,0 +1,40 @@ +/* + * Zapata Telephony Interface to Digium High-Performance Echo Canceller + * + * 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. + */ + +#if !defined(_HPEC_USER_H) +#define _HPEC_USER_H + +struct hpec_challenge { + __u8 challenge[16]; +}; + +struct hpec_license { + __u32 numchannels; + __u8 userinfo[256]; + __u8 response[16]; +}; + +#define ZT_EC_LICENSE_CHALLENGE _IOR(ZT_CODE, 60, struct hpec_challenge) +#define ZT_EC_LICENSE_RESPONSE _IOW(ZT_CODE, 61, struct hpec_license) + +#endif /* !defined(_HPEC_USER_H) */ + diff --git a/hpec/hpec_zaptel.h b/hpec/hpec_zaptel.h new file mode 100644 index 0000000..b03bfe6 --- /dev/null +++ b/hpec/hpec_zaptel.h @@ -0,0 +1,137 @@ +/* + * Zapata Telephony Interface to Digium High-Performance Echo Canceller + * + * 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. + */ + +#if !defined(_HPEC_ZAPTEL_H) +#define _HPEC_ZAPTEL_H + +#define ZT_EC_ARRAY_UPDATE + +#include "hpec_user.h" +#include "hpec.h" + +static int __attribute__((regparm(0))) __attribute__((format (printf, 1, 2))) logger(const char *format, ...) +{ + int res; + va_list args; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + va_start(args, format); + res = vprintk(format, args); + va_end(args); +#else + char buf[256]; + + va_start(args, format); + res = vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + printk(buf); +#endif + + return res; +} + +static void *memalloc(size_t len) +{ + return kmalloc(len, GFP_KERNEL); +} + +static void memfree(void *ptr) +{ + kfree(ptr); +} + +static void echo_can_init(void) +{ + printk("Zaptel Echo Canceller: Digium High-Performance Echo Canceller\n"); + hpec_init(logger, debug, ZT_CHUNKSIZE, memalloc, memfree); +} + +static void echo_can_shutdown(void) +{ + hpec_shutdown(); +} + +static inline void echo_can_free(struct echo_can_state *ec) +{ + hpec_channel_free(ec); +} + +static inline void echo_can_array_update(struct echo_can_state *ec, short *iref, short *isig) +{ + hpec_channel_update(ec, iref, isig); +} + +DECLARE_MUTEX(alloc_lock); + +static inline struct echo_can_state *echo_can_create(int len, int adaption_mode) +{ + struct echo_can_state *result = NULL; + + if (down_interruptible(&alloc_lock)) + return NULL; + + result = hpec_channel_alloc(len); + + up(&alloc_lock); + + return result; +} + +static inline int echo_can_traintap(struct echo_can_state *ec, int pos, short val) +{ + return 0; +} + +DECLARE_MUTEX(license_lock); + +static int hpec_license_ioctl(unsigned int cmd, unsigned long data) +{ + struct hpec_challenge challenge; + struct hpec_license license; + int result = 0; + + switch (cmd) { + case ZT_EC_LICENSE_CHALLENGE: + if (down_interruptible(&license_lock)) + return -EINTR; + memset(&challenge, 0, sizeof(challenge)); + if (hpec_license_challenge(&challenge)) + result = -ENODEV; + if (!result && copy_to_user((unsigned char *) data, &challenge, sizeof(challenge))) + result = -EFAULT; + up(&license_lock); + return result; + case ZT_EC_LICENSE_RESPONSE: + if (down_interruptible(&license_lock)) + return -EINTR; + if (copy_from_user(&license, (unsigned char *) data, sizeof(license))) + result = -EFAULT; + if (!result && hpec_license_check(&license)) + result = -EACCES; + up(&license_lock); + return result; + default: + return -ENOSYS; + } +} + +#endif /* !defined(_HPEC_ZAPTEL_H) */ diff --git a/wct4xxp/Makefile b/wct4xxp/Makefile index 2fd4ee3..37f69c4 100644 --- a/wct4xxp/Makefile +++ b/wct4xxp/Makefile @@ -1,6 +1,6 @@ ifneq ($(KBUILD_EXTMOD),) -include $(obj)/Kbuild +include $(obj)/Makefile.kernel26 else diff --git a/wct4xxp/Kbuild b/wct4xxp/Makefile.kernel26 index 105ca73..105ca73 100644 --- a/wct4xxp/Kbuild +++ b/wct4xxp/Makefile.kernel26 diff --git a/wctc4xxp/Makefile b/wctc4xxp/Makefile new file mode 100644 index 0000000..cad6216 --- /dev/null +++ b/wctc4xxp/Makefile @@ -0,0 +1,13 @@ +ifneq ($(KBUILD_EXTMOD),) + +include $(obj)/Makefile.kernel26 + +endif + +tests: codec_test + +codec_test: codec_test.c ../zaptel.h + $(CC) -o $@ $< $(CFLAGS) + +clean: + rm -rf codec_test diff --git a/wctc4xxp/Makefile.kernel26 b/wctc4xxp/Makefile.kernel26 new file mode 100644 index 0000000..b034819 --- /dev/null +++ b/wctc4xxp/Makefile.kernel26 @@ -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/base.c b/wctc4xxp/base.c new file mode 100644 index 0000000..b7138e3 --- /dev/null +++ b/wctc4xxp/base.c @@ -0,0 +1,1813 @@ +/* + * Wildcard TC400B Driver + * + * Written by John Sloan <jsloan@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/pci.h> +#include <linux/interrupt.h> +#include <linux/vmalloc.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> +#ifdef CONFIG_DEVFS_FS +#include <linux/devfs_fs_kernel.h> +#endif +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include <linux/zaptel.h> +#endif + +#ifdef HOTPLUG_FIRMWARE +static const char *dte_firmware = "tc400m-firmware.bin"; +#else +extern u8 _binary_tc400m_firmware_bin_start[]; +extern void _binary_tc400m_firmware_bin_size; +#endif + + +/* #define USE_TEST_HW */ +#define USE_TDM_CONFIG + +#define WC_MAX_IFACES 128 + +#define NUM_CARDS 24 +#define NUM_EC 4 + +/* 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 /* g.729 */ +#define G723_SAMPLES 240 /* g.723 */ + +#define G729_BYTES 20 /* g.729 */ +#define G723_BYTES 20 /* g.723 */ + + + +#define ACK_SPACE 20 + +#define MAX_COMMANDS (NUM_CHANNELS + ACK_SPACE) +#define MAX_RCV_COMMANDS 16 + +/* 1432 for boot, 274 for 30msec ulaw, 194 for 20mec ulaw */ +#define BOOT_CMD_LEN 1500 +#define OTHER_CMD_LEN 300 + +#define MAX_COMMAND_LEN BOOT_CMD_LEN /* Must be the larger of BOOT_CMD_LEN or 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_LEN 30 +#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_LEN 30 +#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_LEN 34 +#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_LEN 30 +#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_LEN 30 +#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_LEN 32 +#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_LEN 32 +#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_LEN 30 +#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_LEN 30 +#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_LEN 44 +#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_LEN 30 +#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_LEN 30 +#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_LEN 30 +#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_LEN 30 +#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_LEN 32 +#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_LEN 38 +#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_LEN 32 +#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_LEN 58 +#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_LEN 40 +#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_LEN 44 +#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_LEN 32 +#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_LEN 32 +#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_LEN 20 +#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_LEN 54 +#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} + + +#define zt_send_cmd(wc, command, length, hex) \ + ({ \ + int ret = 0; \ + do { \ + if (ret == 2) \ + { \ + wc->ztsnd_rtx++; \ + if (hex == 0x0010) \ + wc->ztsnd_0010_rtx++; \ + } \ + down(&wc->cmdqsem); \ + wc->last_command_sent = hex; \ + if ( (((wc->cmdq_wndx + 1) % MAX_COMMANDS) == wc->cmdq_rndx) && debug ) \ + printk("wcdte error: cmdq is full.\n"); \ + else { \ + unsigned char fifo[OTHER_CMD_LEN] = command; \ + int i; \ + wc->cmdq[wc->cmdq_wndx].cmdlen = length; \ + for (i = 0; i < length; i++) \ + wc->cmdq[wc->cmdq_wndx].cmd[i] = fifo[i]; \ + wc->cmdq_wndx = (wc->cmdq_wndx + 1) % MAX_COMMANDS; \ + } \ + __transmit_demand(wc); \ + up(&wc->cmdqsem); \ + ret = wcdte_waitfor_csmencaps(wc, RCV_CSMENCAPS, 0); \ + if (ret == 1) \ + return(1); \ + } while (ret == 2); \ + }) + + +struct cmdq { + unsigned int cmdlen; + unsigned int cmd[MAX_COMMAND_LEN]; +}; + +struct wcdte { + struct pci_dev *dev; + char *variety; + unsigned int intcount; + unsigned int rxints; + unsigned int txints; + unsigned int intmask; + int pos; + int freeregion; + int rdbl; + int tdbl; + int cards; + spinlock_t reglock; + wait_queue_head_t regq; + int rcvflags; + + struct semaphore chansem; + struct semaphore cmdqsem; + struct cmdq cmdq[MAX_COMMANDS]; + unsigned int cmdq_wndx; + unsigned int cmdq_rndx; + + 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 char numchannels; + unsigned char complexname[40]; + + unsigned long iobase; + dma_addr_t readdma; + dma_addr_t writedma; + dma_addr_t descripdma; + volatile unsigned int *writechunk; /* Double-word aligned write memory */ + volatile unsigned int *readchunk; /* Double-word aligned read memory */ + volatile unsigned int *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 { + char *name; + int flags; +}; + +static struct wcdte_desc wcdte = { "Wildcard TC400P+TC400M", 0 }; + +static struct wcdte *ifaces[WC_MAX_IFACES]; + + + +/* + * The following is the definition of the state structure + * used by the G.721/G.723 encoder and decoder to preserve their internal + * state between successive calls. The meanings of the majority + * of the state structure fields are explained in detail in the + * CCITT Recommendation G.721. The field names are essentially indentical + * to variable names in the bit level description of the coding algorithm + * included in this Recommendation. + */ +struct dte_state { + int encoder; /* If we're an encoder */ + struct wcdte *wc; + + unsigned int timestamp; + unsigned int seqno; + + unsigned int timeslot_in_num; /* DTE chennel on which results we be received from */ + unsigned int timeslot_out_num; /* DTE channel to send data to */ + + unsigned int chan_in_num; /* DTE chennel on which results we be received from */ + unsigned int chan_out_num; /* DTE channel to send data to */ + + unsigned int packets_sent; + unsigned int packets_received; + + unsigned int last_dte_seqno; + unsigned int dte_seqno_rcv; +}; + + +static struct zt_transcoder *uencode; +static struct zt_transcoder *udecode; +static struct dte_state *encoders; +static struct dte_state *decoders; +static int debug = 0; +static char *mode; +int debug_packets = 0; + + +static int wcdte_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 wcdte_destroy_channel(struct wcdte *wc, unsigned int chan1, unsigned int chan2); + +/* Sanity check values */ +static inline int zt_tc_sanitycheck(struct zt_transcode_header *zth, unsigned int outbytes) +{ + if (zth->dstoffset >= sizeof(zth->dstdata)) + return 0; + if (zth->dstlen >= sizeof(zth->dstdata)) + return 0; + if (outbytes >= sizeof(zth->dstdata)) + return 0; + if ((zth->dstoffset + zth->dstlen + outbytes) >= sizeof(zth->dstdata)) + return 0; + if (zth->srcoffset >= sizeof(zth->srcdata)) + return 0; + if (zth->srclen >= sizeof(zth->srcdata)) + return 0; + if ((zth->srcoffset + zth->srclen) > sizeof(zth->srcdata)) + return 0; + return 1; +} + +static void dte_init_state(struct dte_state *state_ptr, int encoder, unsigned int channel, struct wcdte *wc) +{ + state_ptr->encoder = encoder; + state_ptr->wc = wc; + state_ptr->timestamp = 0; + state_ptr->seqno = 0; + + state_ptr->packets_sent = 0; + state_ptr->packets_received = 0; + state_ptr->last_dte_seqno = 0; + state_ptr->dte_seqno_rcv = 0; + + state_ptr->chan_in_num = 999; + state_ptr->chan_out_num = 999; + + if (encoder == 1) + { + 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 unsigned int wcdte_zapfmt_to_dtefmt(unsigned int fmt) +{ + unsigned int pt; + + switch(fmt) + { + case ZT_FORMAT_G723_1: + pt = DTE_FORMAT_G723_1; + break; + case ZT_FORMAT_ULAW: + pt = DTE_FORMAT_ULAW; + break; + case ZT_FORMAT_ALAW: + pt = DTE_FORMAT_ALAW; + break; + case ZT_FORMAT_G729A: + pt = DTE_FORMAT_G729A; + break; + default: + pt = DTE_FORMAT_UNDEF; + } + + return(pt); +} + +static inline void __wcdte_setctl(struct wcdte *wc, unsigned int addr, unsigned int val) +{ + outl(val, wc->iobase + addr); +} + +static inline unsigned int __wcdte_getctl(struct wcdte *wc, unsigned int addr) +{ + return inl(wc->iobase + addr); +} + +static inline void wcdte_setctl(struct wcdte *wc, unsigned int addr, unsigned int val) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __wcdte_setctl(wc, addr, val); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline void wcdte_reinit_descriptor(struct wcdte *wc, int tx, int dbl, char *s) +{ + int o2 = 0; + o2 += dbl * 4; + + if (!tx) + o2 += ERING_SIZE * 4; + wc->descripchunk[o2] = cpu_to_le32(0x80000000); + + wcdte_setctl(wc, 0x0008, 0x00000000); +} + +static inline unsigned int wcdte_getctl(struct wcdte *wc, unsigned int addr) +{ + unsigned long flags; + unsigned int val; + spin_lock_irqsave(&wc->reglock, flags); + val = __wcdte_getctl(wc, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + return val; +} + +static inline int __transmit_demand(struct wcdte *wc) +{ + volatile unsigned char *writechunk; + int o2,i,j; + unsigned int reg, xmt_length; + + reg = wcdte_getctl(wc, 0x0028) & 0x00700000; + + /* Already transmiting, no need to demand another */ + if (!((reg == 0) || (reg = 6))) + return(1); + + /* Nothing to transmit */ + if (wc->cmdq_rndx == wc->cmdq_wndx) + return(1); + + /* Nothing to transmit */ + if (wc->cmdq[wc->cmdq_rndx].cmdlen == 0 ) + return(1); + + writechunk = (volatile unsigned char *)(wc->writechunk); + + writechunk += wc->tdbl * SFRAME_SIZE; + + o2 = wc->tdbl * 4; + + do + { + } while ((le32_to_cpu(wc->descripchunk[o2]) & 0x80000000)); + + xmt_length = wc->cmdq[wc->cmdq_rndx].cmdlen; + if (xmt_length < 64) + xmt_length = 64; + + wc->descripchunk[o2+1] = cpu_to_le32((le32_to_cpu(wc->descripchunk[o2+1]) & 0xFBFFF800) | xmt_length); + + for(i = 0; i < wc->cmdq[wc->cmdq_rndx].cmdlen; i++) + writechunk[i] = wc->cmdq[wc->cmdq_rndx].cmd[i]; + for (j = i; j < xmt_length; j++) + writechunk[j] = 0; + + if (debug_packets && (writechunk[12] == 0x88) && (writechunk[13] == 0x9B)) + { + printk("wcdte debug: TX: "); + for (i=0; i<debug_packets; i++) + printk("%02X ", writechunk[i]); + printk("\n"); + } + + wc->cmdq[wc->cmdq_rndx].cmdlen = 0; + + wc->descripchunk[o2] = cpu_to_le32(0x80000000); + wcdte_setctl(wc, 0x0008, 0x00000000); /* Transmit Poll Demand */ + + wc->tdbl = (wc->tdbl + 1) % ERING_SIZE; + + wc->cmdq_rndx = (wc->cmdq_rndx + 1) % MAX_COMMANDS; + + 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 i = 0; + int res = 0; + unsigned int ipchksum, ndx; + switch(op) { + case ZT_TCOP_ALLOCATE: + down(&wc->chansem); + if (ztc->chan_built == 0) + { + if (st->encoder == 1) + wcdte_create_channel(wc, wcdte_zapfmt_to_dtefmt(zth->srcfmt), wcdte_zapfmt_to_dtefmt(zth->dstfmt), + st->timeslot_in_num, st->timeslot_out_num, &(st->chan_in_num), &(st->chan_out_num)); + else + wcdte_create_channel(wc, wcdte_zapfmt_to_dtefmt(zth->dstfmt), wcdte_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 */ + ndx = st->timeslot_in_num/2; + if (st->encoder == 1) + compl_ztc = &(wc->udecode->channels[ndx]); + else + compl_ztc = &(wc->uencode->channels[ndx]); + 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); + ndx = st->timeslot_in_num/2; + + if (st->encoder == 1) + compl_ztc = &(wc->udecode->channels[ndx]); + else + compl_ztc = &(wc->uencode->channels[ndx]); + + /* If the channel complement (other half of the encoder/decoder pair) is not being used... */ + if ((compl_ztc->flags & ZT_TC_FLAG_BUSY) == 0) + { + if (st->encoder == 1) + wcdte_destroy_channel(wc, st->chan_in_num, st->chan_out_num); + else + wcdte_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)) ) + { + do + { + chars = (unsigned char *)(zth->srcdata + zth->srcoffset); + + if ((zth->srcfmt == ZT_FORMAT_ULAW) || (zth->srcfmt == ZT_FORMAT_ALAW)) + { + if (zth->dstfmt == ZT_FORMAT_G729A) { + inbytes = G729_SAMPLES; + timestamp_inc = G729_SAMPLES; + } else if (zth->dstfmt == ZT_FORMAT_G723_1) { + inbytes = G723_SAMPLES; + timestamp_inc = G723_SAMPLES; + } + + } else if (zth->srcfmt == ZT_FORMAT_G729A) + { + inbytes = G729_BYTES; + timestamp_inc = G729_SAMPLES; + } else if (zth->srcfmt == ZT_FORMAT_G723_1) + { + inbytes = G723_BYTES; + timestamp_inc = G723_SAMPLES; + } + + zth->srclen -= inbytes; + + { + unsigned char fifo[OTHER_CMD_LEN] = 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, + wcdte_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; + + for (i = 0; i < inbytes; i++) + fifo[i+CMD_MSG_IP_UDP_RTP_LEN]= chars[i]; + + down(&wc->cmdqsem); + + if ( (((wc->cmdq_wndx + 1) % MAX_COMMANDS) == wc->cmdq_rndx) && debug ) + printk("wcdte error: cmdq is full.\n"); + else + { + wc->cmdq[wc->cmdq_wndx].cmdlen = CMD_MSG_IP_UDP_RTP_LEN+inbytes; + for (i = 0; i < CMD_MSG_IP_UDP_RTP_LEN+inbytes; i++) + wc->cmdq[wc->cmdq_wndx].cmd[i] = fifo[i]; + wc->cmdq_wndx = (wc->cmdq_wndx + 1) % MAX_COMMANDS; + } + + __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 = 0; + break; + } + return res; +} + +static void wcdte_stop_dma(struct wcdte *wc); + +static inline void wcdte_receiveprep(struct wcdte *wc, int dbl) +{ + volatile unsigned char *readchunk; + 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; + unsigned char *chars = NULL; + unsigned int ztc_ndx; + + readchunk = (volatile unsigned char *)wc->readchunk; + readchunk += dbl * SFRAME_SIZE; + + o2 = dbl * 4; + o2 += ERING_SIZE * 4; + + /* Control in packet */ + if ((readchunk[12] == 0x88) && (readchunk[13] == 0x9B)) + { + if (debug_packets) + { + printk("wcdte debug: RX: "); + for (i=0; i<debug_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]; + + down(&wc->cmdqsem); + if ( (((wc->cmdq_wndx + 1) % MAX_COMMANDS) == wc->cmdq_rndx) && debug ) + printk("wcdte error: cmdq is full (rndx = %d, wndx = %d).\n", wc->cmdq_rndx, wc->cmdq_wndx); + else + { + unsigned char fifo[OTHER_CMD_LEN] = CMD_MSG_ACK(rseq++, rchannel); + + wc->cmdq[wc->cmdq_wndx].cmdlen = CMD_MSG_ACK_LEN; + for (i = 0; i < wc->cmdq[wc->cmdq_wndx].cmdlen; i++) + wc->cmdq[wc->cmdq_wndx].cmd[i] = fifo[i]; + wc->cmdq_wndx = (wc->cmdq_wndx + 1) % MAX_COMMANDS; + } + + __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); + } + } + + /* IP/UDP in packet */ + else if ((readchunk[12] == 0x08) && (readchunk[13] == 0x00) + && (readchunk[50] == 0x12) && (readchunk[51] == 0x34) && (readchunk[52] = 0x56) && (readchunk[53] == 0x78)) + { + 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/2; + + if (ztc_ndx >= wc->numchannels) + { + if (debug) + printk("wcdte error: Invalid channel number received (ztc_ndx = %d) (numchannels = %d)\n", ztc_ndx, wc->numchannels); + rcodec = DTE_FORMAT_UNDEF; + } + + if ((rcodec == 0x00) || (rcodec == 0x08)) /* ulaw or alaw (decoders) */ + { + ztc = &(wc->udecode->channels[ztc_ndx]); + zth = ztc->tch; + st = ztc->pvt; + + if (zth == NULL) + { + if (debug) + printk("wcdte error: Tried to put DTE data into a freed zth header!\n"); + rcodec = DTE_FORMAT_UNDEF; + } else { + chars = (unsigned char *)(zth->dstdata + zth->dstoffset + zth->dstlen); + st->packets_received++; + } + + } + + if ((rcodec == 0x04) || (rcodec == 0x12)) /* g.723 or g.729 (encoders) */ + { + ztc = &(wc->uencode->channels[ztc_ndx]); + zth = ztc->tch; + st = ztc->pvt; + + if (zth == NULL) + { + if (debug) + printk("wcdte error: Tried to put DTE data into a freed zth header!\n"); + rcodec = DTE_FORMAT_UNDEF; + } else { + chars = (unsigned char *)(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; + if ( (rtp_rseq != rtp_eseq) && debug ) + printk("wcdte error: Bad seqno from DTE! [%d][%d][%d]\n", rchannel, rtp_rseq, st->last_dte_seqno); + + st->last_dte_seqno = rtp_rseq; + } + + if (rcodec == 0x00) /* ulaw */ + { + if (zt_tc_sanitycheck(zth, rlen) && ((zth->srcfmt == ZT_FORMAT_G729A && rlen == G729_SAMPLES) || (zth->srcfmt == ZT_FORMAT_G723_1 && rlen == G723_SAMPLES))) { + for (i = 0; i < rlen; i++) + chars[i] = readchunk[i+54]; + + zth->dstlen += rlen; + zth->dstsamples = zth->dstlen; + + } else { + ztc->errorstatus = -EOVERFLOW; + } + zt_transcoder_alert(ztc); + } + else if (rcodec == 0x08) /* alaw */ + { + if (zt_tc_sanitycheck(zth, rlen) && ((zth->srcfmt == ZT_FORMAT_G729A && rlen == G729_SAMPLES) || (zth->srcfmt == ZT_FORMAT_G723_1 && rlen == G723_SAMPLES))) { + + for (i = 0; i < rlen; i++) + chars[i] = readchunk[i+54]; + + zth->dstlen += rlen; + zth->dstsamples = zth->dstlen; + + } else { + ztc->errorstatus = -EOVERFLOW; + } + zt_transcoder_alert(ztc); + } + else if (rcodec == 0x04) /* g.723.1 */ + { + if (zt_tc_sanitycheck(zth, rlen) && (rlen == G723_BYTES)) + { + for (i = 0; i < rlen; i++) + chars[i] = readchunk[i+54]; + + zth->dstlen += rlen; + zth->dstsamples = zth->dstlen * 12; + + } else { + ztc->errorstatus = -EOVERFLOW; + } + + if (!(zth->dstsamples % G723_SAMPLES)) + { + zt_transcoder_alert(ztc); + } + } + else if (rcodec == 0x12) /* g.729a */ + { + if (zt_tc_sanitycheck(zth, rlen) && (rlen == G729_BYTES)) + { + for (i = 0; i < rlen; i++) + chars[i] = readchunk[i+54]; + + zth->dstlen += rlen; + zth->dstsamples = zth->dstlen * 8; + + } else { + ztc->errorstatus = -EOVERFLOW; + } + + if (!(zth->dstsamples % G729_SAMPLES)) + { + zt_transcoder_alert(ztc); + } + } + } +} + + + + + +/* static inline int wcdte_check_descriptor(struct wcdte *wc) */ +static int wcdte_check_descriptor(struct wcdte *wc) +{ + int o2 = 0; + + o2 += ERING_SIZE * 4; + o2 += wc->rdbl * 4; + + if (!(le32_to_cpu(wc->descripchunk[o2]) & 0x80000000)) { + wc->rxints++; + wcdte_receiveprep(wc, wc->rdbl); + wcdte_reinit_descriptor(wc, 0, wc->rdbl, "rxchk"); + wc->rdbl = (wc->rdbl + 1) % ERING_SIZE; + + return 1; + } + return 0; +} + +static void wcdte_init_descriptors(struct wcdte *wc) +{ + volatile unsigned int *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; + } +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) +static void dte_wque_run(struct work_struct *work) +{ + struct wcdte *wc = container_of(work, struct wcdte, dte_work); +#else +static void dte_wque_run(void *work_data) +{ + struct wcdte *wc = work_data; +#endif + int res; + + if (wc->wqueints & 0x00000040) { + /* Loop descriptors is available */ + do { + res = wcdte_check_descriptor(wc); + } while(res); + } + + /* Handle TX interrupts */ + if (wc->wqueints & 0x00000001) + { + wc->txints++; + transmit_demand(wc); + wc->intcount++; + } +} + +ZAP_IRQ_HANDLER(wcdte_interrupt) +{ + struct wcdte *wc = dev_id; + unsigned int ints; + + /* Read and clear interrupts */ + ints = wcdte_getctl(wc, 0x0028); + wcdte_setctl(wc, 0x0028, ints); + + if (!ints) + return IRQ_NONE; + ints &= wc->intmask; + + if (ints & 0x00000041) { + wc->wqueints = ints; + queue_work(wc->dte_wq, &wc->dte_work); + } + + if ((ints & 0x00008000) && debug) + printk("wcdte: Abnormal Interrupt: "); + + if ((ints & 0x00002000) && debug) + printk("wcdte: Fatal Bus Error INT\n"); + + if ((ints & 0x00000100) && debug) + printk("wcdte: Receive Stopped INT\n"); + + if ((ints & 0x00000080) && debug) + printk("wcdte: Receive Desciptor Unavailable INT\n"); + + if ((ints & 0x00000020) && debug) + printk("wcdte: Transmit Under-flow INT\n"); + + if ((ints & 0x00000008) && debug) + printk("wcdte: Jabber Timer Time-out INT\n"); + + if ((ints & 0x00000004) && debug) + printk("wcdte: Transmit Descriptor Unavailable INT\n"); + + if ((ints & 0x00000002) && debug) + printk("wcdte: Transmit Processor Stopped INT\n"); + + return IRQ_RETVAL(1); + +} + +static int wcdte_hardware_init(struct wcdte *wc) +{ + /* Hardware stuff */ + unsigned int reg; + unsigned long newjiffies; + + /* Initialize descriptors */ + wcdte_init_descriptors(wc); + + /* Enable I/O Access */ + pci_read_config_dword(wc->dev, 0x0004, ®); + reg |= 0x00000007; + pci_write_config_dword(wc->dev, 0x0004, reg); + + wcdte_setctl(wc, 0x0000, 0xFFF88001); + + newjiffies = jiffies + HZ/10; + while(((reg = wcdte_getctl(wc,0x0000)) & 0x00000001) && (newjiffies > jiffies)); + + + /* Configure watchdogs, access, etc */ + wcdte_setctl(wc, 0x0030, 0x00280048); + wcdte_setctl(wc, 0x0078, 0x00000013 /* | (1 << 28) */); + + reg = wcdte_getctl(wc, 0x00fc); + wcdte_setctl(wc, 0x00fc, (reg & ~0x7) | 0x7); + + reg = wcdte_getctl(wc, 0x00fc); + + return 0; +} + +static void wcdte_setintmask(struct wcdte *wc, unsigned int intmask) +{ + wc->intmask = intmask; + wcdte_setctl(wc, 0x0038, intmask); +} + +static void wcdte_enable_interrupts(struct wcdte *wc) +{ + /* Enable interrupts */ + if (!debug) + wcdte_setintmask(wc, 0x00010041); + else + wcdte_setintmask(wc, 0x0001A1EB); +} + +static void wcdte_start_dma(struct wcdte *wc) +{ + unsigned int reg; + wmb(); + wcdte_setctl(wc, 0x0020, wc->descripdma); + wcdte_setctl(wc, 0x0018, wc->descripdma + (16 * ERING_SIZE)); + /* Start receiver/transmitter */ + reg = wcdte_getctl(wc, 0x0030); + wcdte_setctl(wc, 0x0030, reg | 0x00002002); /* Start XMT and RCD */ + wcdte_setctl(wc, 0x0010, 0x00000000); /* Receive Poll Demand */ + reg = wcdte_getctl(wc, 0x0028); + wcdte_setctl(wc, 0x0028, reg); + +} + +static void wcdte_stop_dma(struct wcdte *wc) +{ + /* Disable interrupts and reset */ + unsigned int reg; + /* Disable interrupts */ + wcdte_setintmask(wc, 0x00000000); + wcdte_setctl(wc, 0x0084, 0x00000000); + wcdte_setctl(wc, 0x0048, 0x00000000); + /* Reset the part to be on the safe side */ + reg = wcdte_getctl(wc, 0x0000); + reg |= 0x00000001; + wcdte_setctl(wc, 0x0000, reg); +} + +static void wcdte_disable_interrupts(struct wcdte *wc) +{ + /* Disable interrupts */ + wcdte_setintmask(wc, 0x00000000); + wcdte_setctl(wc, 0x0084, 0x00000000); +} + +static int wcdte_waitfor_csmencaps(struct wcdte *wc, unsigned int mask, int use_mask) +{ + int ret; + + + if (use_mask) + ret = wait_event_interruptible_timeout(wc->regq, (wc->rcvflags == mask), wc->timeout); + else + ret = wait_event_interruptible_timeout(wc->regq, (wc->last_rcommand == wc->last_command_sent), wc->timeout); + wc->rcvflags = 0; + wc->last_rcommand = 0; + wc->last_command_sent = 0; + + if (ret < 0) + { + if (debug) + printk("wcdte error: Wait interrupted, need to stop boot (ret = %d)\n", ret); + return(1); + } + if (ret == 0) + { + if (debug) + printk("wcdte error: Waitfor CSMENCAPS response timed out (ret = %d)\n", ret); + return(2); + } + return(0); +} + + +static int wcdte_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--) { + wcdte_setctl(wc, mdio_addr, MDIO_ENB | MDIO_DATA_WRITE1); + wcdte_getctl(wc, mdio_addr); + wcdte_setctl(wc, mdio_addr, MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK); + wcdte_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; + + wcdte_setctl(wc, mdio_addr, MDIO_ENB | dataval); + wcdte_getctl(wc, mdio_addr); + wcdte_setctl(wc, mdio_addr, MDIO_ENB | dataval | MDIO_SHIFT_CLK); + wcdte_getctl(wc, mdio_addr); + } + + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + wcdte_setctl(wc, mdio_addr, MDIO_ENB_IN); + wcdte_getctl(wc, mdio_addr); + retval = (retval << 1) | ((wcdte_getctl(wc, mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + wcdte_setctl(wc, mdio_addr, MDIO_ENB_IN | MDIO_SHIFT_CLK); + wcdte_getctl(wc, mdio_addr); + } + retval = (retval>>1) & 0xffff; + return retval; +} + +void wcdte_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--) { + wcdte_setctl(wc, mdio_addr, MDIO_ENB | MDIO_DATA_WRITE1); + wcdte_getctl(wc, mdio_addr); + wcdte_setctl(wc, mdio_addr, MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK); + wcdte_getctl(wc, mdio_addr); + } + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + wcdte_setctl(wc, mdio_addr, MDIO_ENB | dataval); + wcdte_getctl(wc, mdio_addr); + wcdte_setctl(wc, mdio_addr, MDIO_ENB | dataval | MDIO_SHIFT_CLK); + wcdte_getctl(wc, mdio_addr); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + wcdte_setctl(wc, mdio_addr, MDIO_ENB_IN); + wcdte_getctl(wc, mdio_addr); + wcdte_setctl(wc, mdio_addr, MDIO_ENB_IN | MDIO_SHIFT_CLK); + wcdte_getctl(wc, mdio_addr); + } + return; +} + +static int wcdte_boot_processor(struct wcdte *wc, const struct firmware *firmware) +{ + int i, j, byteloc, last_byteloc, length, delay_count; + unsigned int reg, ret; + +#ifndef USE_TEST_HW + /* Turn off auto negotiation */ + wcdte_write_phy(wc, 0, 0x2100); + if (debug) + printk("wcdte: PHY register 0 = %X", wcdte_read_phy(wc, 0)); + + /* Set reset */ + wcdte_setctl(wc, 0x00A0, 0x04000000); + + /* Wait 1000msec to ensure processor reset */ + mdelay(1000); + + /* Clear reset */ + wcdte_setctl(wc, 0x00A0, 0x04080000); + + /* Waitfor ethernet link */ + delay_count = 0; + do + { + reg = wcdte_getctl(wc, 0x00fc); + mdelay(100); + delay_count++; + + if (delay_count >= 100) + { + printk("wcdte error: Failed to link to DTE processor!\n"); + return(1); + } + } while ((reg & 0xE0000000) != 0xE0000000); + + + /* Turn off booted LED */ + wcdte_setctl(wc, 0x00A0, 0x04084000); + + +#endif + + reg = wcdte_getctl(wc, 0x00fc); + if (debug) + printk("wcdte: LINK STATUS: reg(0xfc) = %X\n", reg); + + reg = wcdte_getctl(wc, 0x00A0); + + byteloc = 17; + j = 0; + do + { + last_byteloc = byteloc; + + length = (firmware->data[byteloc] << 8) |firmware->data[byteloc+1]; + byteloc += 2; + + down(&wc->cmdqsem); + if ( (((wc->cmdq_wndx + 1) % MAX_COMMANDS) == wc->cmdq_rndx) && debug ) + printk("wcdte error: cmdq is full.\n"); + else + { + wc->cmdq[wc->cmdq_wndx].cmdlen = length; + for (i = 0; i < length; i++) + wc->cmdq[wc->cmdq_wndx].cmd[i] = firmware->data[byteloc++]; + wc->cmdq_wndx = (wc->cmdq_wndx + 1) % MAX_COMMANDS; + } + + __transmit_demand(wc); + up(&wc->cmdqsem); + + ret = wcdte_waitfor_csmencaps(wc, RCV_CSMENCAPS_ACK, 1); + if (ret == 1) + return(1); + else if (ret == 2) /* Retransmit if dte processor times out */ + byteloc = last_byteloc; + j++; + + } while (byteloc < firmware->size-20); + wc->timeout = 10 * HZ; + if (wcdte_waitfor_csmencaps(wc, RCV_CSMENCAPS, 1)) + return(1); + + /* Turn on booted LED */ + wcdte_setctl(wc, 0x00A0, 0x04080000); + if(debug) + printk("wcdte: Successfully booted DTE processor.\n"); + + return(0); +} + +static int wcdte_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 */ + zt_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 */ + zt_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 */ + zt_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); + zt_send_cmd(wc, CMD_MSG_VOIP_VCEOPT(wc->seq_num++, chan1, length, 0), CMD_MSG_VOIP_VCEOPT_LEN, 0x8001); + + /* Configure simple channel */ + zt_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); + zt_send_cmd(wc, CMD_MSG_VOIP_VCEOPT(wc->seq_num++, chan2, length, 0), CMD_MSG_VOIP_VCEOPT_LEN, 0x8001); + + zt_send_cmd(wc, CMD_MSG_TRANS_CONNECT(wc->seq_num++, 1, chan1, chan2, complicated, simple), CMD_MSG_TRANS_CONNECT_LEN, 0x9322); + zt_send_cmd(wc, CMD_MSG_VOIP_INDCTRL(wc->seq_num++, chan1), CMD_MSG_VOIP_INDCTRL_LEN, 0x8084); + zt_send_cmd(wc, CMD_MSG_VOIP_INDCTRL(wc->seq_num++, chan2), CMD_MSG_VOIP_INDCTRL_LEN, 0x8084); + zt_send_cmd(wc, CMD_MSG_VOIP_VOPENA(wc->seq_num++, chan1, complicated), CMD_MSG_VOIP_VOPENA_LEN, 0x8000); + zt_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 wcdte_destroy_channel(struct wcdte *wc, unsigned int chan1, unsigned int chan2) +{ + + /* Turn off both channels */ + zt_send_cmd(wc, CMD_MSG_VOIP_VOPENA_CLOSE(wc->seq_num++, chan1), CMD_MSG_VOIP_VOPENA_CLOSE_LEN, 0x8000); + zt_send_cmd(wc, CMD_MSG_VOIP_VOPENA_CLOSE(wc->seq_num++, chan2), CMD_MSG_VOIP_VOPENA_CLOSE_LEN, 0x8000); + + /* Disconnect the channels */ + zt_send_cmd(wc, CMD_MSG_TRANS_CONNECT(wc->seq_num++, 0, chan1, chan2, 0, 0), CMD_MSG_TRANS_CONNECT_LEN, 0x9322); + + /* Remove the channels */ + zt_send_cmd(wc, CMD_MSG_DESTROY_CHANNEL(wc->seq_num++, chan1), CMD_MSG_DESTROY_CHANNEL_LEN, 0x0011); + zt_send_cmd(wc, CMD_MSG_DESTROY_CHANNEL(wc->seq_num++, chan2), CMD_MSG_DESTROY_CHANNEL_LEN, 0x0011); + + return 1; +} + +static int wcdte_setup_channels(struct wcdte *wc) +{ +#ifndef USE_TEST_HW + zt_send_cmd(wc, CMD_MSG_SET_ARM_CLK(wc->seq_num++), CMD_MSG_SET_ARM_CLK_LEN, 0x0411); + zt_send_cmd(wc, CMD_MSG_SET_SPU_CLK(wc->seq_num++), CMD_MSG_SET_SPU_CLK_LEN, 0x0412); +#endif + +#ifdef USE_TDM_CONFIG + zt_send_cmd(wc, CMD_MSG_TDM_SELECT_BUS_MODE(wc->seq_num++), CMD_MSG_TDM_SELECT_BUS_MODE_LEN, 0x0417); + zt_send_cmd(wc, CMD_MSG_TDM_ENABLE_BUS(wc->seq_num++), CMD_MSG_TDM_ENABLE_BUS_LEN, 0x0405); + zt_send_cmd(wc, CMD_MSG_SUPVSR_SETUP_TDM_PARMS(wc->seq_num++, 0x03, 0x20, 0x00), CMD_MSG_SUPVSR_SETUP_TDM_PARMS_LEN, 0x0407); + zt_send_cmd(wc, CMD_MSG_SUPVSR_SETUP_TDM_PARMS(wc->seq_num++, 0x04, 0x80, 0x04), CMD_MSG_SUPVSR_SETUP_TDM_PARMS_LEN, 0x0407); + zt_send_cmd(wc, CMD_MSG_SUPVSR_SETUP_TDM_PARMS(wc->seq_num++, 0x05, 0x20, 0x08), CMD_MSG_SUPVSR_SETUP_TDM_PARMS_LEN, 0x0407); + zt_send_cmd(wc, CMD_MSG_SUPVSR_SETUP_TDM_PARMS(wc->seq_num++, 0x06, 0x80, 0x0C), CMD_MSG_SUPVSR_SETUP_TDM_PARMS_LEN, 0x0407); +#endif + + zt_send_cmd(wc, CMD_MSG_SET_ETH_HEADER(wc->seq_num++), CMD_MSG_SET_ETH_HEADER_LEN, 0x0100); + zt_send_cmd(wc, CMD_MSG_IP_SERVICE_CONFIG(wc->seq_num++), CMD_MSG_IP_SERVICE_CONFIG_LEN, 0x0302); + zt_send_cmd(wc, CMD_MSG_ARP_SERVICE_CONFIG(wc->seq_num++), CMD_MSG_ARP_SERVICE_CONFIG_LEN, 0x0105); + zt_send_cmd(wc, CMD_MSG_ICMP_SERVICE_CONFIG(wc->seq_num++), CMD_MSG_ICMP_SERVICE_CONFIG_LEN, 0x0304); + +#ifdef USE_TDM_CONFIG + zt_send_cmd(wc, CMD_MSG_DEVICE_SET_COUNTRY_CODE(wc->seq_num++), CMD_MSG_DEVICE_SET_COUNTRY_CODE_LEN, 0x041B); +#endif + + zt_send_cmd(wc, CMD_MSG_SPU_FEATURES_CONTROL(wc->seq_num++, 0x02), CMD_MSG_SPU_FEATURES_CONTROL_LEN, 0x0013); + zt_send_cmd(wc, CMD_MSG_IP_OPTIONS(wc->seq_num++), CMD_MSG_IP_OPTIONS_LEN, 0x0306); + zt_send_cmd(wc, CMD_MSG_SPU_FEATURES_CONTROL(wc->seq_num++, 0x04), CMD_MSG_SPU_FEATURES_CONTROL_LEN, 0x0013); + +#ifdef USE_TDM_CONFIG + zt_send_cmd(wc, CMD_MSG_TDM_OPT(wc->seq_num++), CMD_MSG_TDM_OPT_LEN, 0x0435); +#endif + + wc->timeout = HZ/100 + 1; /* 10msec */ + + return(0); +} + +static int __devinit wcdte_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int res, reg; + struct wcdte *wc; + struct wcdte_desc *d = (struct wcdte_desc *)ent->driver_data; + int x; + static int initd_ifaces=0; + unsigned char g729_numchannels, g723_numchannels, min_numchannels, dte_firmware_ver; + unsigned int complexfmts; + struct firmware embedded_firmware = { +#if !defined(HOTPLUG_FIRMWARE) + /* Yes... this is weird. objcopy gives us a symbol containing + the size of the firmware, not a pointer to a variable containing + the size. The only way we can get the value of the symbol + is to take its address, so we define it as a pointer and + then cast that value to the proper type. + */ + .size = (size_t) &_binary_tc400m_firmware_bin_size, + .data = _binary_tc400m_firmware_bin_start, +#endif + }; + const struct firmware *firmware = &embedded_firmware; + + if (!initd_ifaces) { + memset((void *)ifaces,0,(sizeof(struct wcdte *))*WC_MAX_IFACES); + initd_ifaces=1; + } + for (x=0;x<WC_MAX_IFACES;x++) + if (!ifaces[x]) break; + if (x >= WC_MAX_IFACES) { + printk("wcdte: Too many interfaces\n"); + return -EIO; + } + + if (pci_enable_device(pdev)) { + res = -EIO; + } else { + + wc = vmalloc(sizeof(struct wcdte)); + if (wc) { + ifaces[x] = wc; + memset(wc, 0, sizeof(struct wcdte)); + spin_lock_init(&wc->reglock); + sema_init(&wc->chansem, 1); + sema_init(&wc->cmdqsem, 1); + wc->cards = NUM_CARDS; + wc->iobase = pci_resource_start(pdev, 0); + wc->dev = pdev; + wc->pos = x; + wc->variety = d->name; + + wc->tdbl = 0; + wc->rdbl = 0; + wc->rcvflags = 0; + wc->last_command_sent = 0; + wc->last_rcommand = 0; + wc->last_rparm2 = 0; + wc->cmdq_wndx = 0; + wc->cmdq_rndx = 0; + wc->seq_num = 6; + wc->timeout = 1 * HZ; /* 1 sec */ + wc->ztsnd_rtx = 0; + wc->ztsnd_0010_rtx = 0; + + /* Keep track of whether we need to free the region */ + if (request_region(wc->iobase, 0xff, "wcdte")) + wc->freeregion = 1; + + /* Allocate enought memory for all TX buffers, RX buffers, and descriptors */ + wc->writechunk = (int *)pci_alloc_consistent(pdev, PCI_WINDOW_SIZE, &wc->writedma); + if (!wc->writechunk) { + printk("wcdte error: 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) / 4; /* in doublewords */ + wc->readdma = wc->writedma + (SFRAME_SIZE * ERING_SIZE); /* in bytes */ + + wc->descripchunk = wc->readchunk + (SFRAME_SIZE * ERING_SIZE) / 4; /* in doublewords */ + wc->descripdma = wc->readdma + (SFRAME_SIZE * ERING_SIZE); /* in bytes */ + + /* Initialize Write/Buffers to all blank data */ + memset((void *)wc->writechunk,0x00, SFRAME_SIZE * 2); + memset((void *)wc->readchunk, 0x00, SFRAME_SIZE * 2); + + init_waitqueue_head(&wc->regq); + + /* Initialize the work queue */ + wc->dte_wq = create_workqueue("tc400b"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) + INIT_WORK(&wc->dte_work, dte_wque_run); +#else + INIT_WORK(&wc->dte_work, dte_wque_run, wc); +#endif + +#ifdef HOTPLUG_FIRMWARE + if ((request_firmware(&firmware, dte_firmware, &wc->dev->dev) != 0) || !firmware) { + printk("TC400B: firmware %s not available from userspace\n", dte_firmware); + return -1; + } +#endif + dte_firmware_ver = firmware->data[0]; + g729_numchannels = firmware->data[1]; + g723_numchannels = firmware->data[2]; + + if (g723_numchannels < g729_numchannels) + min_numchannels = g723_numchannels; + else + min_numchannels = g729_numchannels; + + /* Setup Encoders and Decoders */ + + if (!mode || strlen(mode) < 4) + { + sprintf(wc->complexname, "g.729a / g.723.1 5.3kbps"); + complexfmts = ZT_FORMAT_G729A | ZT_FORMAT_G723_1; + wc->numchannels = min_numchannels; + } + else if (mode[3] == '9') /* "g729" */ + { + sprintf(wc->complexname, "g.729a"); + complexfmts = ZT_FORMAT_G729A; + wc->numchannels = g729_numchannels; + } + else if (mode[3] == '3') /* "g723" */ + { + sprintf(wc->complexname, "g.723.1 5.3kbps"); + complexfmts = ZT_FORMAT_G723_1; + wc->numchannels = g723_numchannels; + } + else + { + sprintf(wc->complexname, "g.729a / g.723.1 5.3kbps"); + complexfmts = ZT_FORMAT_G729A | ZT_FORMAT_G723_1; + wc->numchannels = min_numchannels; + } + + uencode = zt_transcoder_alloc(wc->numchannels); + udecode = zt_transcoder_alloc(wc->numchannels); + encoders = vmalloc(sizeof(struct dte_state) * wc->numchannels); + decoders = vmalloc(sizeof(struct dte_state) * wc->numchannels); + memset(encoders, 0, sizeof(struct dte_state) * wc->numchannels); + memset(decoders, 0, sizeof(struct dte_state) * wc->numchannels); + if (!uencode || !udecode || !encoders || !decoders) { + if (uencode) + zt_transcoder_free(uencode); + if (udecode) + zt_transcoder_free(udecode); + if (encoders) + vfree(encoders); + if (decoders) + vfree(decoders); + return -ENOMEM; + } + sprintf(udecode->name, "DTE Decoder"); + sprintf(uencode->name, "DTE Encoder"); + + udecode->srcfmts = uencode->dstfmts = complexfmts; + udecode->dstfmts = uencode->srcfmts = ZT_FORMAT_ULAW | ZT_FORMAT_ALAW; + + udecode->operation = uencode->operation = dte_operation; + + for (x=0;x<wc->numchannels;x++) { + dte_init_state(encoders + x, 1, x, wc); + encoders[x].encoder = 1; + decoders[x].encoder = 0; + dte_init_state(decoders + x, 0, x, wc); + uencode->channels[x].pvt = encoders + x; + udecode->channels[x].pvt = decoders + x; + } + + wc->uencode = uencode; + wc->udecode = udecode; + + zt_transcoder_register(uencode); + zt_transcoder_register(udecode); + + printk("Zaptel DTE (%s) Transcoder support LOADED (firm ver = %d)\n", wc->complexname, dte_firmware_ver); + + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* Keep track of which device we are */ + pci_set_drvdata(pdev, wc); + + if (request_irq(pdev->irq, wcdte_interrupt, SA_SHIRQ, "tc400b", wc)) { + printk("wcdte error: 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); + vfree(wc); + return -EIO; + } + + + if (wcdte_hardware_init(wc)) { + /* Set Reset Low */ + wcdte_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); + vfree(wc); + return -EIO; + + } + + /* Enable interrupts */ + wcdte_enable_interrupts(wc); + + /* Start DMA */ + wcdte_start_dma(wc); + if (wcdte_boot_processor(wc,firmware)) { + if (firmware != &embedded_firmware) + release_firmware(firmware); + + /* Set Reset Low */ + wcdte_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); + vfree(wc); + return -EIO; + } + + if (wcdte_setup_channels(wc)) + { + /* Set Reset Low */ + wcdte_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); + vfree(wc); + return -EIO; + } + + reg = wcdte_getctl(wc, 0x00fc); + if (debug) + printk("wcdte debug: (post-boot) Reg fc is %08x\n", reg); + + printk("Found and successfully installed a Wildcard TC: %s \n", wc->variety); + res = 0; + } else + res = -ENOMEM; + } + return res; +} + +static void wcdte_release(struct wcdte *wc) +{ + if (wc->freeregion) + release_region(wc->iobase, 0xff); + vfree(wc); +} + +static void __devexit wcdte_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) { + if (debug) + { + printk("wcdte debug: wc->ztsnd_rtx = %d\n", wc->ztsnd_rtx); + printk("wcdte debug: 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; + + printk("wcdte debug: 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); + printk("wcdte debug: 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); + } + } + + zt_transcoder_unregister(wc->udecode); + zt_transcoder_unregister(wc->uencode); + zt_transcoder_free(wc->uencode); + zt_transcoder_free(wc->udecode); + vfree(wc->uencode->channels[0].pvt); + vfree(wc->udecode->channels[0].pvt); + + /* Stop any DMA */ + wcdte_stop_dma(wc); + + /* In case hardware is still there */ + wcdte_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); + + /* Release span, possibly delayed */ + wcdte_release(wc); + } +} + +static struct pci_device_id wcdte_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, wcdte_pci_tbl); + +static struct pci_driver wcdte_driver = { + name: "wctc4xxp", + probe: wcdte_init_one, + remove: __devexit_p(wcdte_remove_one), + suspend: NULL, + resume: NULL, + id_table: wcdte_pci_tbl, +}; + +int ztdte_init(void) +{ + int res; + + res = pci_module_init(&wcdte_driver); + if (res) + return -ENODEV; + return 0; +} + +void ztdte_cleanup(void) +{ + pci_unregister_driver(&wcdte_driver); +} + +module_param(debug, int, S_IRUGO | S_IWUSR); +module_param(mode, charp, S_IRUGO | S_IWUSR); +MODULE_DESCRIPTION("Wildcard TC400P+TC400M Driver"); +MODULE_AUTHOR("John Sloan <jsloan@digium.com>"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +module_init(ztdte_init); +module_exit(ztdte_cleanup); diff --git a/wctc4xxp/codec_test.c b/wctc4xxp/codec_test.c new file mode 100644 index 0000000..287979d --- /dev/null +++ b/wctc4xxp/codec_test.c @@ -0,0 +1,332 @@ +/* + * 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> +#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 Binary files differnew file mode 100644 index 0000000..2e6c743 --- /dev/null +++ b/wctc4xxp/tc400m-firmware.bin diff --git a/wcte12xp.c b/wcte12xp.c new file mode 100644 index 0000000..52d9122 --- /dev/null +++ b/wcte12xp.c @@ -0,0 +1,2046 @@ +/* + * Digium, Inc. Wildcard TE12xP T1/PRI card Driver + * + * Written by Michael Spiceland <mspiceland@digium.com> + * + * Adapted from the wctdm24xxp and wcte11xp drivers originally + * written by Mark Spencer <markster@digium.com> + * Matthew Fredrickson <creslin@digium.com> + * William Meadows <wmeadows@digium.com> + * + * Copyright (C) 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/errno.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/proc_fs.h> + +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include <zaptel/zaptel.h> +#endif + +#ifdef LINUX26 +#include <linux/moduleparam.h> +#endif + +#include "wct4xxp/wct4xxp.h" /* For certain definitions */ + +#define WC_MAX_IFACES 8 + +#define SDI_CLK (0x00010000) +#define SDI_DOUT (0x00020000) +#define SDI_DREAD (0x00040000) +#define SDI_DIN (0x00080000) + +#define EFRAME_SIZE 108 +#define ERING_SIZE 16 /* Maximum ring size */ +#define EFRAME_GAP 20 +#define SFRAME_SIZE ((EFRAME_SIZE * ZT_CHUNKSIZE) + (EFRAME_GAP * (ZT_CHUNKSIZE - 1))) + +#define PCI_WINDOW_SIZE ((2 * 2 * 2 * SFRAME_SIZE) + (2 * ERING_SIZE * 4)) + +#define MAX_COMMANDS 7*7*2 /* 42 bytes /3 (cntl,addr,data) /2 (cs) */ + +#define ISR_COMMANDS 2 + +#define __CMD_ISR (1 << 17) /* flag for ISR reads */ +#define __CMD_PINS (1 << 18) /* CPLD pin read */ +#define __CMD_LEDS (1 << 19) /* LED Operation */ +#define __CMD_RD (1 << 20) /* Read Operation */ +#define __CMD_WR (1 << 21) /* Write Operation */ +#define __CMD_FIN (1 << 22) /* Has finished receive */ +#define __CMD_TX (1 << 23) /* Has been transmitted */ + +#define __LED_ORANGE (1<<3) +#define __LED_GREEN (1<<2) +#define __LED_RED (1<<1) + +#define SET_LED_ORANGE(a) a | __LED_ORANGE +#define SET_LED_RED(a) (a | __LED_RED) & ~__LED_GREEN +#define SET_LED_GREEN(a) (a | __LED_GREEN) & ~__LED_RED + +#define UNSET_LED_ORANGE(a) a & ~__LED_ORANGE +#define UNSET_LED_REDGREEN(a) a | __LED_RED | __LED_GREEN + +#define CMD_WR(a,b) (((a) << 8) | (b) | __CMD_WR) +#define CMD_RD(a) (((a) << 8) | __CMD_RD) +#define CMD_LEDS(a) (((a) << 8) | __CMD_LEDS) +#define CMD_BYTE(slot, a) (slot*6)+(a*2) /* only even slots */ + +#define TYPE_T1 1 +#define TYPE_E1 2 + +static struct pci_driver te12xp_driver; +#define module_printk(fmt, args...) printk("%s: " fmt, te12xp_driver.name, ## args) +#define debug_printk(level, fmt, args...) if (debug >= level) printk("%s (%s): " fmt, te12xp_driver.name, __FUNCTION__, ## args) + +static int chanmap_t1[] = +{ 2,1,0, + 6,5,4, + 10,9,8, + 14,13,12, + 18,17,16, + 22,21,20, + 26,25,24, + 30,29,28 }; + +static int chanmap_e1[] = +{ 2,1,0, + 7,6,5,4, + 11,10,9,8, + 15,14,13,12, + 19,18,17,16, + 23,22,21,20, + 27,26,25,24, + 31,30,29,28 }; + +static int chanmap_e1uc[] = +{ 3,2,1,0, + 7,6,5,4, + 11,10,9,8, + 15,14,13,12, + 19,18,17,16, + 23,22,21,20, + 27,26,25,24, + 31,30,29,28 }; + +struct command { + unsigned char address; + unsigned char data; + unsigned char ident; + unsigned int flags; + unsigned char cs_slot; +}; + +struct cmdq { + struct command cmds[MAX_COMMANDS]; + unsigned char isrshadow[ISR_COMMANDS]; +}; + +struct t1 { + struct pci_dev *dev; + spinlock_t reglock; + unsigned char txident; + unsigned char rxident; + int spantype; + struct { + unsigned int nmf:1; + unsigned int sendingyellow:1; + } flags; + unsigned char txsigs[16]; /* Copy of tx sig registers */ + int num; + int alarmcount; /* How much red alarm we've seen */ + int alarmdebounce; + char *variety; + unsigned int intcount; + int sync; + int dead; + int blinktimer; + int alarmtimer; + int yellowtimer; + int ledlastvalue; + int alarms_read; + int checktiming; /* Set >0 to cause the timing source to be checked */ + int loopupcnt; + int loopdowncnt; + int initialized; + int *chanmap; + unsigned char ledtestreg; + unsigned long iobase; + unsigned char ec_chunk1[32][ZT_CHUNKSIZE]; + unsigned char ec_chunk2[32][ZT_CHUNKSIZE]; + struct zt_span span; /* Span */ + struct zt_chan chans[32]; /* Channels */ + int freeregion; + unsigned int intmask; + wait_queue_head_t regq; + struct cmdq cmdq; + struct command dummy; /* preallocate for dummy noop command */ + unsigned char ctlreg; + int rdbl; + int tdbl; + unsigned int rxints; + unsigned int txints; + unsigned int sdi; + int usecount; + dma_addr_t readdma; + dma_addr_t writedma; + dma_addr_t descripdma; + volatile unsigned int *writechunk; + volatile unsigned int *readchunk; + volatile unsigned int *descripchunk; + unsigned int isrreaderrors; +}; + +static int debug = 0; +static int j1mode = 0; +static int alarmdebounce = 0; +static int loopback = 0; +static int t1e1override = -1; +static int unchannelized = 0; + +static struct t1 *ifaces[WC_MAX_IFACES]; + +struct t1_desc { + char *name; + int flags; +}; + +static struct t1_desc te12xp = { "Wildcard TE12xP", 0 }; + +static int schluffen(wait_queue_head_t *q) +{ + DECLARE_WAITQUEUE(wait, current); + add_wait_queue(q, &wait); + current->state = TASK_INTERRUPTIBLE; + if (!signal_pending(current)) schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(q, &wait); + if (signal_pending(current)) return -ERESTARTSYS; + return(0); +} + +static inline int empty_slot(struct t1 *wc) +{ + unsigned int x; + + for (x = 0; x < sizeof(wc->cmdq.cmds) / sizeof(wc->cmdq.cmds[0]); x++) { + if (!wc->cmdq.cmds[x].flags && !wc->cmdq.cmds[x].address) + return x; + } + return -1; +} + +static inline void __t1_setctl(struct t1 *wc, unsigned int addr, unsigned int val) +{ + outl(val, wc->iobase + addr); +} + +static inline void t1_setctl(struct t1 *wc, unsigned int addr, unsigned int val) +{ + unsigned long flags; + + spin_lock_irqsave(&wc->reglock, flags); + __t1_setctl(wc, addr, val); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline unsigned int __t1_getctl(struct t1 *wc, unsigned int addr) +{ + return inl(wc->iobase + addr); +} + +static inline unsigned int t1_getctl(struct t1 *wc, unsigned int addr) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&wc->reglock, flags); + val = __t1_getctl(wc, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + + return val; +} + +static void t1_init_descriptors(struct t1 *wc) +{ + volatile unsigned int *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] = 0x80000000; + descrip[1] = 0xe5800000 | (SFRAME_SIZE); + if (x % 2) + descrip[2] = writedma + SFRAME_SIZE; + else + descrip[2] = writedma; + descrip[3] = descripdma; + + /* Receive descriptor */ + descrip[0 + ERING_SIZE * 4] = 0x80000000; + descrip[1 + ERING_SIZE * 4] = 0x01000000 | (SFRAME_SIZE); + if (x % 2) + descrip[2 + ERING_SIZE * 4] = readdma + SFRAME_SIZE; + else + descrip[2 + ERING_SIZE * 4] = readdma; + descrip[3 + ERING_SIZE * 4] = descripdma + ERING_SIZE * 16; + + /* Advance descriptor */ + descrip += 4; + } +} + +static inline void t1_reinit_descriptor(struct t1 *wc, int tx, int dbl, char *s) +{ + int o2 = dbl * 4; + + if (!tx) + o2 += ERING_SIZE * 4; + + wc->descripchunk[o2] = 0x80000000; +} + +static inline void cmd_dequeue(struct t1 *wc, volatile unsigned char *writechunk, int eframe, int slot) +{ + unsigned long flags; + struct command *curcmd=NULL; + unsigned int x; + + /* Skip audio */ + writechunk += 66; + spin_lock_irqsave(&wc->reglock, flags); + /* Search for something waiting to transmit */ + if ((slot < 6) && (eframe) && (eframe < ZT_CHUNKSIZE - 1)) { + /* only 6 useable cs slots per */ + for (x = 0; x < sizeof(wc->cmdq.cmds) / sizeof(wc->cmdq.cmds[0]); x++) { + if ((wc->cmdq.cmds[x].flags & (__CMD_RD | __CMD_WR | __CMD_LEDS | __CMD_PINS)) && + !(wc->cmdq.cmds[x].flags & (__CMD_TX | __CMD_FIN))) { + curcmd = &wc->cmdq.cmds[x]; +#if 0 + printk("Transmitting command addr='%02x' data='%02x' flags='%08x' in txident=%d slot=%d CMD_BYTE=%d jiffies=%d\n", wc->cmdq.cmds[x].address, wc->cmdq.cmds[x].data, wc->cmdq.cmds[x].flags, wc->txident, slot, CMD_BYTE(slot,2), jiffies); +#endif + wc->cmdq.cmds[x].flags |= __CMD_TX; + wc->cmdq.cmds[x].ident = wc->txident; + break; + } + } + if (!curcmd) { + curcmd = &wc->dummy; + /* If nothing else, use filler */ + curcmd->address = 0x4a; + curcmd->data = 0x00; + curcmd->flags = __CMD_RD; + } + curcmd->cs_slot = slot; + if (curcmd->flags & __CMD_WR) + writechunk[CMD_BYTE(slot, 0)] = 0x0c; /* 0c write command */ + else if (curcmd->flags & __CMD_LEDS) + writechunk[CMD_BYTE(slot, 0)] = 0x10 | ((curcmd->address) & 0x0E); /* led set command */ + else if (curcmd->flags & __CMD_PINS) + writechunk[CMD_BYTE(slot, 0)] = 0x30; /* CPLD2 pin state */ + else + writechunk[CMD_BYTE(slot, 0)] = 0x0a; /* read command */ + writechunk[CMD_BYTE(slot, 1)] = curcmd->address; + writechunk[CMD_BYTE(slot, 2)] = curcmd->data; + } + + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline void cmd_decipher(struct t1 *wc, volatile unsigned char *readchunk) +{ + unsigned long flags; + unsigned char ident, cs_slot; + unsigned int x; + + /* Skip audio */ + readchunk += 66; + spin_lock_irqsave(&wc->reglock, flags); + /* Search for any pending results */ + for (x = 0; x < sizeof(wc->cmdq.cmds) / sizeof(wc->cmdq.cmds[0]); x++) { + if ((wc->cmdq.cmds[x].flags & (__CMD_RD | __CMD_WR | __CMD_LEDS | __CMD_PINS)) && + (wc->cmdq.cmds[x].flags & (__CMD_TX)) && + !(wc->cmdq.cmds[x].flags & (__CMD_FIN))) { + ident = wc->cmdq.cmds[x].ident; + cs_slot = wc->cmdq.cmds[x].cs_slot; + + if (ident == wc->rxident) { + /* Store result */ + wc->cmdq.cmds[x].data |= readchunk[CMD_BYTE(cs_slot, 2)]; + /*printk("answer in rxident=%d cs_slot=%d is %d CMD_BYTE=%d jiffies=%d\n", ident, cs_slot, last_read_command, CMD_BYTE(cs_slot, 2), jiffies); */ + wc->cmdq.cmds[x].flags |= __CMD_FIN; + if (wc->cmdq.cmds[x].flags & (__CMD_WR | __CMD_LEDS)) + /* clear out writes (and leds) since they need no ack */ + memset(&wc->cmdq.cmds[x], 0, sizeof(wc->cmdq.cmds[x])); + } + } + } + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline unsigned int __t1_sdi_clk(struct t1 *wc) +{ + unsigned int ret; + + wc->sdi &= ~SDI_CLK; + __t1_setctl(wc, 0x0048, wc->sdi); + ret = __t1_getctl(wc, 0x0048); + wc->sdi |= SDI_CLK; + __t1_setctl(wc, 0x0048, wc->sdi); + return ret & SDI_DIN; +} + +static inline void __t1_sdi_sendbits(struct t1 *wc, unsigned int bits, int count) +{ + wc->sdi &= ~SDI_DREAD; + __t1_setctl(wc, 0x0048, wc->sdi); + while (count--) { + if (bits & (1 << count)) + wc->sdi |= SDI_DOUT; + else + wc->sdi &= ~SDI_DOUT; + __t1_sdi_clk(wc); + } +} + +static inline unsigned int __t1_sdi_recvbits(struct t1 *wc, int count) +{ + unsigned int bits=0; + + wc->sdi |= SDI_DREAD; + __t1_setctl(wc, 0x0048, wc->sdi); + while (count--) { + bits <<= 1; + if (__t1_sdi_clk(wc)) + bits |= 1; + else + bits &= ~1; + } + return bits; +} + +static inline unsigned short __t1_getsdi(struct t1 *wc, unsigned char addr) +{ + unsigned int bits; + + /* Send preamble */ + bits = 0xffffffff; + __t1_sdi_sendbits(wc, bits, 32); + bits = (0x6 << 10) | (1 << 5) | (addr); + __t1_sdi_sendbits(wc, bits, 14); + + return __t1_sdi_recvbits(wc, 18); +} + +static inline unsigned short t1_getsdi(struct t1 *wc, unsigned char addr) +{ + unsigned long flags; + unsigned short val; + + spin_lock_irqsave(&wc->reglock, flags); + val = __t1_getsdi(wc, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + + return val; +} + +static inline void __t1_setsdi(struct t1 *wc, unsigned char addr, unsigned short value) +{ + unsigned int bits; + + /* Send preamble */ + bits = 0xffffffff; + __t1_sdi_sendbits(wc, bits, 32); + bits = (0x5 << 12) | (1 << 7) | (addr << 2) | 0x2; + __t1_sdi_sendbits(wc, bits, 16); + __t1_sdi_sendbits(wc, value, 16); +} + +static inline void t1_setsdi(struct t1 *wc, unsigned char addr, unsigned short value) +{ + unsigned long flags; + + spin_lock_irqsave(&wc->reglock, flags); + __t1_setsdi(wc, addr, value); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline int t1_setreg_full(struct t1 *wc, int addr, int val, int inisr) +{ + unsigned long flags; + int hit; + int ret; + + do { + spin_lock_irqsave(&wc->reglock, flags); + hit = empty_slot(wc); + if (hit > -1) { + wc->cmdq.cmds[hit].address = addr; + wc->cmdq.cmds[hit].data = val; + wc->cmdq.cmds[hit].flags |= __CMD_WR; + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (inisr) + break; + if (hit < 0) { + if ((ret = schluffen(&wc->regq))) + return ret; + } + } while (hit < 0); + + return (hit > -1) ? 0 : -1; +} + +static inline int t1_setreg(struct t1 *wc, int addr, int val) +{ + return t1_setreg_full(wc, addr, val, 0); +} + +/*************************************************************************** + * clean_leftovers() + * + * Check for unconsumed isr register reads and clean them up. + **************************************************************************/ +static inline void clean_leftovers(struct t1 *wc) +{ + unsigned long flags; + unsigned int x; + int count = 0; + + spin_lock_irqsave(&wc->reglock, flags); + /* find our requested command */ + for (x = 0; x < sizeof(wc->cmdq.cmds) / sizeof(wc->cmdq.cmds[0]); x++) { + if ((wc->cmdq.cmds[x].flags & __CMD_RD) && + (wc->cmdq.cmds[x].flags & __CMD_ISR) && + !(wc->cmdq.cmds[x].flags & __CMD_FIN)) { + debug_printk(1,"leftover isr read! %d", count); + memset(&wc->cmdq.cmds[x], 0, sizeof(wc->cmdq.cmds[x])); + } + } + spin_unlock_irqrestore(&wc->reglock, flags); +} + +/******************************************************************** + * t1_getreg_isr() + * + * Called in interrupt context to retrieve a value already requested + * by the normal t1_getreg(). + *******************************************************************/ +static inline int t1_getreg_isr(struct t1 *wc, int addr) +{ + unsigned long flags; + int hit=-1; + int ret; + unsigned int x; + + spin_lock_irqsave(&wc->reglock, flags); + /* find our requested command */ + for (x = 0;x < sizeof(wc->cmdq.cmds) / sizeof(wc->cmdq.cmds[0]); x++) { + if ((wc->cmdq.cmds[x].flags & __CMD_RD) && + (wc->cmdq.cmds[x].flags & __CMD_FIN) && + (wc->cmdq.cmds[x].address==addr)) { + hit = x; + break; + } + } + + if (hit < 0) { + spin_unlock_irqrestore(&wc->reglock, flags); + debug_printk(2, "t1_getreg_isr() no addr=%02x\n", addr); + return -1; /* oops, couldn't find it */ + } + + ret = wc->cmdq.cmds[hit].data; + memset(&wc->cmdq.cmds[hit], 0, sizeof(struct command)); + spin_unlock_irqrestore(&wc->reglock, flags); + + return ret; +} + +static inline int t1_getreg(struct t1 *wc, int addr, int inisr) +{ + unsigned long flags; + int hit; + int ret = 0; + + do { + spin_lock_irqsave(&wc->reglock, flags); + hit = empty_slot(wc); + if (hit > -1) { + wc->cmdq.cmds[hit].address = addr; + wc->cmdq.cmds[hit].data = 0x00; + wc->cmdq.cmds[hit].flags |= __CMD_RD; + if (inisr) + wc->cmdq.cmds[hit].flags |= __CMD_ISR; + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (inisr) /* must be requested in t1_getreg_isr() */ + return (hit > -1) ? 0 : -1; + if (hit < 0) { + if ((ret = schluffen(&wc->regq))) + return ret; + } + } while (hit < 0); + + do { + spin_lock_irqsave(&wc->reglock, flags); + if (wc->cmdq.cmds[hit].flags & __CMD_FIN) { + ret = wc->cmdq.cmds[hit].data; + memset(&wc->cmdq.cmds[hit], 0, sizeof(wc->cmdq.cmds[hit])); + hit = -1; + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (hit > -1) { + if ((ret = schluffen(&wc->regq))) + return ret; + } + } while (hit > -1); + + return ret; +} + +static inline int t1_setleds(struct t1 *wc, int leds, int inisr) +{ + unsigned long flags; + int hit; + int ret = 0; + + leds = ~leds & 0x0E; /* invert the LED bits (3 downto 1)*/ + + do { + spin_lock_irqsave(&wc->reglock, flags); + hit = empty_slot(wc); + if (hit > -1) { + wc->cmdq.cmds[hit].flags |= __CMD_LEDS; + wc->cmdq.cmds[hit].address = leds; + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (inisr) + break; + if (hit < 0) { + if ((ret = schluffen(&wc->regq))) + return ret; + } + } while (hit < 0); + + return (hit > -1) ? 0 : -1; +} + +static inline int t1_getpins(struct t1 *wc, int inisr) +{ + unsigned long flags; + int hit; + int ret = 0; + + do { + spin_lock_irqsave(&wc->reglock, flags); + hit = empty_slot(wc); + if (hit > -1) { + wc->cmdq.cmds[hit].address = 0x00; + wc->cmdq.cmds[hit].data = 0x00; + wc->cmdq.cmds[hit].flags |= __CMD_PINS; + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (inisr) + return (hit > -1) ? 0 : -1; + if (hit < 0) { + if ((ret = schluffen(&wc->regq))) + return ret; + } + } while (hit < 0); + + do { + spin_lock_irqsave(&wc->reglock, flags); + if (wc->cmdq.cmds[hit].flags & __CMD_FIN) { + ret = wc->cmdq.cmds[hit].data; + memset(&wc->cmdq.cmds[hit], 0, sizeof(wc->cmdq.cmds[hit])); + hit = -1; + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (hit > -1) { + if ((ret = schluffen(&wc->regq))) + return ret; + } + } while (hit > -1); + + return ret; +} + +static void t1_setintmask(struct t1 *wc, unsigned int intmask) +{ + wc->intmask = intmask; + t1_setctl(wc, 0x0038, intmask); +} + +static void t1_enable_interrupts(struct t1 *wc) +{ + /* Enable interrupts */ + t1_setintmask(wc, 0x00010041); /* only RX */ +} + +static void t1_disable_interrupts(struct t1 *wc) +{ + /* Disable interrupts */ + t1_setintmask(wc, 0x00000000); + t1_setctl(wc, 0x0084, 0x00000000); +} + +static void t1_start_dma(struct t1 *wc) +{ + unsigned int reg; + int x; + + wmb(); + t1_setctl(wc, 0x0020, wc->descripdma); + t1_setctl(wc, 0x0018, wc->descripdma + (16 * ERING_SIZE)); + /* Start receiver/transmitter */ + reg = t1_getctl(wc, 0x0030); + t1_setctl(wc, 0x0030, reg | 0x00002002); + t1_setctl(wc, 0x0008, 0x00000000); + t1_setctl(wc, 0x0010, 0x00000000); + reg = t1_getctl(wc, 0x0028); + t1_setctl(wc, 0x0028, reg); + + /* Set Reset - now with MAGIC TIPS */ + t1_setctl(wc, 0x0048, 0x00000000); + for (x = 0; x < 10; x++) + schluffen(&wc->regq); + /* Clear reset */ + t1_setctl(wc, 0x0048, 0x00010000); + for (x = 0; x < 10; x++) + schluffen(&wc->regq); + /* Switch to caring only about receive interrupts */ + t1_setintmask(wc, 0x00010040); +} + +static void t1_stop_dma(struct t1 *wc) +{ + /* Disable interrupts and reset */ + unsigned int reg; + + /* Disable interrupts */ + t1_setintmask(wc, 0x00000000); + t1_setctl(wc, 0x0084, 0x00000000); + t1_setctl(wc, 0x0048, 0x00000000); + /* Reset the part to be on the safe side */ + reg = t1_getctl(wc, 0x0000); + reg |= 0x00000001; + t1_setctl(wc, 0x0000, reg); +} + +static void __t1xxp_set_clear(struct t1 *wc, int channo) +{ + int i,j; + int ret; + unsigned short val=0; + + for (i = 0; i < 24; i++) { + j = (i / 8); + if (wc->span.chans[i].flags & ZT_FLAG_CLEAR) + val |= 1 << (7 - (i % 8)); + if (((i % 8)==7) && /* write byte every 8 channels */ + ((channo < 0) || /* channo=-1 means all channels */ + (j == (channo-1)/8) )) { /* only the register for this channo */ + ret = t1_setreg_full(wc, 0x2f + j, val, 1); + if (ret < 0) + module_printk("set_clear failed for chan %d!\n",i); + val = 0; + } + } +} + +static void t1_release(struct t1 *wc) +{ + zt_unregister(&wc->span); + if (wc->freeregion) + release_region(wc->iobase, 0xff); + kfree(wc); + printk("Freed a Wildcard TE12xP\n"); +} + +static void t4_serial_setup(struct t1 *wc) +{ + module_printk("Setting up global serial parameters for %s\n", + wc->spantype == TYPE_E1 ? (unchannelized ? "Unchannelized E1" : "E1") : "T1"); + + t1_setreg(wc, 0x85, 0xe0); /* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from channel 0 */ + t1_setreg(wc, 0x08, 0x05); /* IPC: Interrupt push/pull active low */ + + /* Global clocks (8.192 Mhz CLK) */ + t1_setreg(wc, 0x92, 0x00); + t1_setreg(wc, 0x93, 0x18); + t1_setreg(wc, 0x94, 0xfb); + t1_setreg(wc, 0x95, 0x0b); + t1_setreg(wc, 0x96, 0x00); + t1_setreg(wc, 0x97, 0x0b); + t1_setreg(wc, 0x98, 0xdb); + t1_setreg(wc, 0x99, 0xdf); + + /* Configure interrupts */ + t1_setreg(wc, 0x46, 0xc0); /* GCR: Interrupt on Activation/Deactivation of AIX, LOS */ + + /* Configure system interface */ + t1_setreg(wc, 0x3e, 0x0a /* 0x02 */); /* SIC1: 4.096 Mhz clock/bus, double buffer receive / transmit, byte interleaved */ + t1_setreg(wc, 0x3f, 0x00); /* SIC2: No FFS, no center receive eliastic buffer, phase 0 */ + t1_setreg(wc, 0x40, 0x04); /* SIC3: Edges for capture */ + t1_setreg(wc, 0x44, 0x30); /* CMR1: RCLK is at 8.192 Mhz dejittered */ + t1_setreg(wc, 0x45, 0x00); /* CMR2: We provide sync and clock for tx and rx. */ + t1_setreg(wc, 0x22, 0x00); /* XC0: Normal operation of Sa-bits */ + t1_setreg(wc, 0x23, 0x04); /* XC1: 0 offset */ + t1_setreg(wc, 0x24, 0x00); /* RC0: Just shy of 255 */ + t1_setreg(wc, 0x25, 0x05); /* RC1: The rest of RC0 */ + + /* Configure ports */ + t1_setreg(wc, 0x80, 0x00); /* PC1: SPYR/SPYX input on RPA/XPA */ + t1_setreg(wc, 0x81, 0x22); /* PC2: RMFB/XSIG output/input on RPB/XPB */ + t1_setreg(wc, 0x82, 0x65); /* PC3: Some unused stuff */ + t1_setreg(wc, 0x83, 0x35); /* PC4: Some more unused stuff */ + t1_setreg(wc, 0x84, 0x31); /* PC5: XMFS active low, SCLKR is input, RCLK is output */ + t1_setreg(wc, 0x86, 0x03); /* PC6: CLK1 is Tx Clock output, CLK2 is 8.192 Mhz from DCO-R */ + t1_setreg(wc, 0x3b, 0x00); /* Clear LCR1 */ +} + +static void t1_configure_t1(struct t1 *wc, int lineconfig, int txlevel) +{ + unsigned int fmr4, fmr2, fmr1, fmr0, lim2; + char *framing, *line; + int mytxlevel; + + if ((txlevel > 7) || (txlevel < 4)) + mytxlevel = 0; + else + mytxlevel = txlevel - 4; + fmr1 = 0x9e; /* FMR1: Mode 0, T1 mode, CRC on for ESF, 2.048 Mhz system data rate, no XAIS */ + fmr2 = 0x22; /* FMR2: no payload loopback, auto send yellow alarm */ + if (loopback) + fmr2 |= 0x4; + + if (j1mode) + fmr4 = 0x1c; + else + fmr4 = 0x0c; /* FMR4: Lose sync on 2 out of 5 framing bits, auto resync */ + + lim2 = 0x21; /* LIM2: 50% peak is a "1", Advanced Loss recovery */ + lim2 |= (mytxlevel << 6); /* LIM2: Add line buildout */ + t1_setreg(wc, 0x1d, fmr1); + t1_setreg(wc, 0x1e, fmr2); + + /* Configure line interface */ + if (lineconfig & ZT_CONFIG_AMI) { + line = "AMI"; + fmr0 = 0xa0; + } else { + line = "B8ZS"; + fmr0 = 0xf0; + } + if (lineconfig & ZT_CONFIG_D4) { + framing = "D4"; + } else { + framing = "ESF"; + fmr4 |= 0x2; + fmr2 |= 0xc0; + } + t1_setreg(wc, 0x1c, fmr0); + + t1_setreg(wc, 0x20, fmr4); + t1_setreg(wc, 0x21, 0x40); /* FMR5: Enable RBS mode */ + + t1_setreg(wc, 0x37, 0xf8); /* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */ + t1_setreg(wc, 0x36, 0x08); /* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */ + + t1_setreg(wc, 0x02, 0x50); /* CMDR: Reset the receiver and transmitter line interface */ + t1_setreg(wc, 0x02, 0x00); /* CMDR: Reset the receiver and transmitter line interface */ + + t1_setreg(wc, 0x3a, lim2); /* LIM2: 50% peak amplitude is a "1" */ + t1_setreg(wc, 0x38, 0x0a); /* PCD: LOS after 176 consecutive "zeros" */ + t1_setreg(wc, 0x39, 0x15); /* PCR: 22 "ones" clear LOS */ + + if (j1mode) + t1_setreg(wc, 0x24, 0x80); /* J1 overide */ + + /* Generate pulse mask for T1 */ + switch (mytxlevel) { + case 3: + t1_setreg(wc, 0x26, 0x07); /* XPM0 */ + t1_setreg(wc, 0x27, 0x01); /* XPM1 */ + t1_setreg(wc, 0x28, 0x00); /* XPM2 */ + break; + case 2: + t1_setreg(wc, 0x26, 0x8c); /* XPM0 */ + t1_setreg(wc, 0x27, 0x11); /* XPM1 */ + t1_setreg(wc, 0x28, 0x01); /* XPM2 */ + break; + case 1: + t1_setreg(wc, 0x26, 0x8c); /* XPM0 */ + t1_setreg(wc, 0x27, 0x01); /* XPM1 */ + t1_setreg(wc, 0x28, 0x00); /* XPM2 */ + break; + case 0: + default: + t1_setreg(wc, 0x26, 0xd7); /* XPM0 */ + t1_setreg(wc, 0x27, 0x22); /* XPM1 */ + t1_setreg(wc, 0x28, 0x01); /* XPM2 */ + break; + } + + module_printk("Span configured for %s/%s\n", framing, line); +} + +static void t1_configure_e1(struct t1 *wc, int lineconfig) +{ + unsigned int fmr2, fmr1, fmr0; + unsigned int cas = 0; + char *crc4 = ""; + char *framing, *line; + + fmr1 = 0x46; /* FMR1: E1 mode, Automatic force resync, PCM30 mode, 8.192 Mhz backplane, no XAIS */ + fmr2 = 0x03; /* FMR2: Auto transmit remote alarm, auto loss of multiframe recovery, no payload loopback */ + if (unchannelized) + fmr2 |= 0x30; + if (loopback) + fmr2 |= 0x4; + if (lineconfig & ZT_CONFIG_CRC4) { + fmr1 |= 0x08; /* CRC4 transmit */ + fmr2 |= 0xc0; /* CRC4 receive */ + crc4 = "/CRC4"; + } + t1_setreg(wc, 0x1d, fmr1); + t1_setreg(wc, 0x1e, fmr2); + + /* Configure line interface */ + if (lineconfig & ZT_CONFIG_AMI) { + line = "AMI"; + fmr0 = 0xa0; + } else { + line = "HDB3"; + fmr0 = 0xf0; + } + if (lineconfig & ZT_CONFIG_CCS) { + framing = "CCS"; + } else { + framing = "CAS"; + cas = 0x40; + } + t1_setreg(wc, 0x1c, fmr0); + + if (unchannelized) + t1_setreg(wc, 0x1f, 0x40); + + t1_setreg(wc, 0x37, 0xf0 /*| 0x6 */ ); /* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */ + t1_setreg(wc, 0x36, 0x08); /* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */ + + t1_setreg(wc, 0x02, 0x50); /* CMDR: Reset the receiver and transmitter line interface */ + t1_setreg(wc, 0x02, 0x00); /* CMDR: Reset the receiver and transmitter line interface */ + + /* Condition receive line interface for E1 after reset */ + t1_setreg(wc, 0xbb, 0x17); + t1_setreg(wc, 0xbc, 0x55); + t1_setreg(wc, 0xbb, 0x97); + t1_setreg(wc, 0xbb, 0x11); + t1_setreg(wc, 0xbc, 0xaa); + t1_setreg(wc, 0xbb, 0x91); + t1_setreg(wc, 0xbb, 0x12); + t1_setreg(wc, 0xbc, 0x55); + t1_setreg(wc, 0xbb, 0x92); + t1_setreg(wc, 0xbb, 0x0c); + t1_setreg(wc, 0xbb, 0x00); + t1_setreg(wc, 0xbb, 0x8c); + + t1_setreg(wc, 0x3a, 0x20); /* LIM2: 50% peak amplitude is a "1" */ + t1_setreg(wc, 0x38, 0x0a); /* PCD: LOS after 176 consecutive "zeros" */ + t1_setreg(wc, 0x39, 0x15); /* PCR: 22 "ones" clear LOS */ + + t1_setreg(wc, 0x20, 0x9f); /* XSW: Spare bits all to 1 */ + if (unchannelized) + t1_setreg(wc, 0x21, 0x3c); + else + t1_setreg(wc, 0x21, 0x1c|cas); /* XSP: E-bit set when async. AXS auto, XSIF to 1 */ + + + /* Generate pulse mask for E1 */ + t1_setreg(wc, 0x26, 0x54); /* XPM0 */ + t1_setreg(wc, 0x27, 0x02); /* XPM1 */ + t1_setreg(wc, 0x28, 0x00); /* XPM2 */ + module_printk("Span configured for %s/%s%s\n", framing, line, crc4); +} + +static void t1xxp_framer_start(struct t1 *wc, struct zt_span *span) +{ + int alreadyrunning = wc->span.flags & ZT_FLAG_RUNNING; + unsigned long flags; + + if (wc->spantype == TYPE_E1) { /* if this is an E1 card */ + t1_configure_e1(wc, span->lineconfig); + } else { /* is a T1 card */ + t1_configure_t1(wc, span->lineconfig, span->txlevel); + __t1xxp_set_clear(wc, -1); + } + + spin_lock_irqsave(&wc->reglock, flags); + if (!alreadyrunning) + wc->span.flags |= ZT_FLAG_RUNNING; + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static int t1xxp_startup(struct zt_span *span) +{ + struct t1 *wc = span->pvt; + int i; + + /* initialize the start value for the entire chunk of last ec buffer */ + for (i = 0; i < span->channels; i++) { + memset(wc->ec_chunk1[i], ZT_LIN2X(0, &span->chans[i]), ZT_CHUNKSIZE); + memset(wc->ec_chunk2[i], ZT_LIN2X(0, &span->chans[i]), ZT_CHUNKSIZE); + } + + /* Reset framer with proper parameters and start */ + t1xxp_framer_start(wc, span); + debug_printk(1, "Calling startup (flags is %d)\n", span->flags); + + return 0; +} + +static int t1xxp_shutdown(struct zt_span *span) +{ + struct t1 *wc = span->pvt; + unsigned long flags; + + t1_setreg(wc, 0x46, 0x41); /* GCR: Interrupt on Activation/Deactivation of AIX, LOS */ + spin_lock_irqsave(&wc->reglock, flags); + span->flags &= ~ZT_FLAG_RUNNING; + spin_unlock_irqrestore(&wc->reglock, flags); + return 0; +} + +static int t1xxp_chanconfig(struct zt_chan *chan, int sigtype) +{ + struct t1 *wc = chan->pvt; + int alreadyrunning = chan->span->flags & ZT_FLAG_RUNNING; + + if (alreadyrunning && (wc->spantype != TYPE_E1)) + __t1xxp_set_clear(wc, chan->channo); + + return 0; +} + +static int t1xxp_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) +{ + struct t1 *wc = span->pvt; + + span->lineconfig = lc->lineconfig; + span->txlevel = lc->lbo; + span->rxlevel = 0; + /* Do we want to SYNC on receive or not */ + wc->sync = lc->sync; + if (wc->sync) + wc->ctlreg |= 0x80; + else + wc->ctlreg &= ~0x80; + + /* If already running, apply changes immediately */ + if (span->flags & ZT_FLAG_RUNNING) + return t1xxp_startup(span); + + return 0; +} + +static int t1xxp_rbsbits(struct zt_chan *chan, int bits) +{ + u_char m,c; + int n,b; + struct t1 *wc = chan->pvt; + unsigned long flags; + + debug_printk(2, "Setting bits to %d on channel %s\n", bits, chan->name); + if (wc->spantype == TYPE_E1) { /* do it E1 way */ + if (chan->chanpos == 16) + return 0; + + n = chan->chanpos - 1; + if (chan->chanpos > 15) n--; + b = (n % 15); + spin_lock_irqsave(&wc->reglock, flags); + c = wc->txsigs[b]; + m = (n / 15) << 2; /* nibble selector */ + c &= (0xf << m); /* keep the other nibble */ + c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ + wc->txsigs[b] = c; + spin_unlock_irqrestore(&wc->reglock, flags); + /* output them to the chip */ + t1_setreg_full(wc,0x71 + b,c,1); + } else if (wc->span.lineconfig & ZT_CONFIG_D4) { + n = chan->chanpos - 1; + b = (n / 4); + spin_lock_irqsave(&wc->reglock, flags); + c = wc->txsigs[b]; + m = ((3 - (n % 4)) << 1); /* nibble selector */ + c &= ~(0x3 << m); /* keep the other nibble */ + c |= ((bits >> 2) & 0x3) << m; /* put our new nibble here */ + wc->txsigs[b] = c; + spin_unlock_irqrestore(&wc->reglock, flags); + /* output them to the chip */ + t1_setreg_full(wc,0x70 + b,c,1); + t1_setreg_full(wc,0x70 + b + 6,c,1); + } else if (wc->span.lineconfig & ZT_CONFIG_ESF) { + n = chan->chanpos - 1; + b = (n / 2); + spin_lock_irqsave(&wc->reglock, flags); + c = wc->txsigs[b]; + m = ((n % 2) << 2); /* nibble selector */ + c &= (0xf << m); /* keep the other nibble */ + c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ + wc->txsigs[b] = c; + spin_unlock_irqrestore(&wc->reglock, flags); + /* output them to the chip */ + t1_setreg_full(wc,0x70 + b,c,1); + } + debug_printk(2,"Finished setting RBS bits\n"); + + return 0; +} + +static inline void __t1_check_sigbits_reads(struct t1 *wc) +{ + int i; + + if (!(wc->span.flags & ZT_FLAG_RUNNING)) + return; + if (wc->spantype == TYPE_E1) { + for (i = 0; i < 15; i++) { + if (t1_getreg(wc, 0x71 + i, 1)) + wc->isrreaderrors++; + } + } else if (wc->span.lineconfig & ZT_CONFIG_D4) { + for (i = 0; i < 24; i+=4) { + if (t1_getreg(wc, 0x70 + (i >> 2), 1)) + wc->isrreaderrors++; + } + } else { + for (i = 0; i < 24; i+=2) { + if (t1_getreg(wc, 0x70 + (i >> 1), 1)) + wc->isrreaderrors++; + } + } +} + +static inline void __t1_check_sigbits(struct t1 *wc) +{ + int a,i,rxs; + + if (!(wc->span.flags & ZT_FLAG_RUNNING)) + return; + if (wc->spantype == TYPE_E1) { + for (i = 0; i < 15; i++) { + a = t1_getreg_isr(wc, 0x71 + i); + if (a > -1) { + /* Get high channel in low bits */ + rxs = (a & 0xf); + if (!(wc->span.chans[i+16].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i+16].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+16], rxs); + } + rxs = (a >> 4) & 0xf; + if (!(wc->span.chans[i].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i], rxs); + } + } else { + debug_printk(1, "no space to request register in isr\n"); + } + } + } else if (wc->span.lineconfig & ZT_CONFIG_D4) { + for (i = 0; i < 24; i+=4) { + a = t1_getreg_isr(wc, 0x70 + (i>>2)); + if (a > -1) { + /* Get high channel in low bits */ + rxs = (a & 0x3) << 2; + if (!(wc->span.chans[i+3].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i+3].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+3], rxs); + } + rxs = (a & 0xc); + if (!(wc->span.chans[i+2].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i+2].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+2], rxs); + } + rxs = (a >> 2) & 0xc; + if (!(wc->span.chans[i+1].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i+1].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+1], rxs); + } + rxs = (a >> 4) & 0xc; + if (!(wc->span.chans[i].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i], rxs); + } + } + } + } else { + for (i = 0; i < 24; i+=2) { + a = t1_getreg_isr(wc, 0x70 + (i>>1)); + if (a > -1) { + /* Get high channel in low bits */ + rxs = (a & 0xf); + if (!(wc->span.chans[i+1].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i+1].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i+1], rxs); + } + rxs = (a >> 4) & 0xf; + if (!(wc->span.chans[i].sig & ZT_SIG_CLEAR)) { + if (wc->span.chans[i].rxsig != rxs) + zt_rbsbits(&wc->span.chans[i], rxs); + } + } + } + } +} + +static int t1xxp_maint(struct zt_span *span, int cmd) +{ + struct t1 *wc = span->pvt; + + if (wc->spantype == TYPE_E1) { + switch (cmd) { + case ZT_MAINT_NONE: + module_printk("XXX Turn off local and remote loops E1 XXX\n"); + break; + case ZT_MAINT_LOCALLOOP: + module_printk("XXX Turn on local loopback E1 XXX\n"); + break; + case ZT_MAINT_REMOTELOOP: + module_printk("XXX Turn on remote loopback E1 XXX\n"); + break; + case ZT_MAINT_LOOPUP: + module_printk("XXX Send loopup code E1 XXX\n"); + break; + case ZT_MAINT_LOOPDOWN: + module_printk("XXX Send loopdown code E1 XXX\n"); + break; + case ZT_MAINT_LOOPSTOP: + module_printk("XXX Stop sending loop codes E1 XXX\n"); + break; + default: + module_printk("Unknown E1 maint command: %d\n", cmd); + break; + } + } else { + switch (cmd) { + case ZT_MAINT_NONE: + module_printk("XXX Turn off local and remote loops T1 XXX\n"); + break; + case ZT_MAINT_LOCALLOOP: + module_printk("XXX Turn on local loop and no remote loop XXX\n"); + break; + case ZT_MAINT_REMOTELOOP: + module_printk("XXX Turn on remote loopup XXX\n"); + break; + case ZT_MAINT_LOOPUP: + t1_setreg(wc, 0x21, 0x50); /* FMR5: Nothing but RBS mode */ + break; + case ZT_MAINT_LOOPDOWN: + t1_setreg(wc, 0x21, 0x60); /* FMR5: Nothing but RBS mode */ + break; + case ZT_MAINT_LOOPSTOP: + t1_setreg(wc, 0x21, 0x40); /* FMR5: Nothing but RBS mode */ + break; + default: + module_printk("Unknown T1 maint command: %d\n", cmd); + break; + } + } + + return 0; +} + +static int t1xxp_open(struct zt_chan *chan) +{ + struct t1 *wc = chan->pvt; + + if (wc->dead) + return -ENODEV; + wc->usecount++; + +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#else + try_module_get(THIS_MODULE); +#endif + + return 0; +} + +static int t1xxp_close(struct zt_chan *chan) +{ + struct t1 *wc = chan->pvt; + + wc->usecount--; + +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#else + module_put(THIS_MODULE); +#endif + + /* If we're dead, release us now */ + if (!wc->usecount && wc->dead) + t1_release(wc); + + return 0; +} + +static int t1xxp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) +{ + struct t4_regs regs; + unsigned int x; + struct t1 *wc; + + switch (cmd) { + case WCT4_GET_REGS: + wc = chan->pvt; + for (x = 0; x < sizeof(regs.pci) / sizeof(regs.pci[0]); x++) +#if 1 + regs.pci[x] = (inb(wc->iobase + (x << 2))) | + (inb(wc->iobase + (x << 2) + 1) << 8) | + (inb(wc->iobase + (x << 2) + 2) << 16) | + (inb(wc->iobase + (x << 2) + 3) << 24); +#else + regs.pci[x] = (inb(wc->iobase + x)); +#endif + + for (x = 0; x < sizeof(regs.regs) / sizeof(regs.regs[0]); x++) + regs.regs[x] = t1_getreg(wc, x, 0); + + if (copy_to_user((struct t4_regs *) data, ®s, sizeof(regs))) + return -EFAULT; + break; + default: + return -ENOTTY; + } + return 0; +} + +static int t1_software_init(struct t1 *wc) +{ + int x; + + /* Find position */ + for (x = 0; x < sizeof(ifaces) / sizeof(ifaces[0]); x++) { + if (ifaces[x] == wc) { + debug_printk(1, "software init for card %d\n",x); + break; + } + } + + if (x == sizeof(ifaces) / sizeof(ifaces[0])) + return -1; + + t4_serial_setup(wc); + + wc->num = x; + sprintf(wc->span.name, "WCT1/%d", wc->num); + sprintf(wc->span.desc, "%s Card %d", wc->variety, wc->num); + + wc->span.spanconfig = t1xxp_spanconfig; + wc->span.chanconfig = t1xxp_chanconfig; + wc->span.startup = t1xxp_startup; + wc->span.shutdown = t1xxp_shutdown; + wc->span.rbsbits = t1xxp_rbsbits; + wc->span.maint = t1xxp_maint; + wc->span.open = t1xxp_open; + wc->span.close = t1xxp_close; + wc->span.ioctl = t1xxp_ioctl; + + if (wc->spantype == TYPE_E1) { + if (unchannelized) + wc->span.channels = 32; + else + wc->span.channels = 31; + } else + wc->span.channels = 24; + wc->span.chans = wc->chans; + wc->span.flags = ZT_FLAG_RBS; + wc->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF; + wc->span.pvt = wc; + if (wc->spantype == TYPE_E1) + wc->span.deflaw = ZT_LAW_ALAW; + else + wc->span.deflaw = ZT_LAW_MULAW; + init_waitqueue_head(&wc->span.maintq); + for (x = 0; x < wc->span.channels; x++) { + sprintf(wc->chans[x].name, "WCT1/%d/%d", wc->num, x + 1); + wc->chans[x].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_EM_E1 | + ZT_SIG_FXSLS | ZT_SIG_FXSGS | + ZT_SIG_FXSKS | ZT_SIG_FXOLS | ZT_SIG_DACS_RBS | + ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS | ZT_SIG_SF; + wc->chans[x].pvt = wc; + wc->chans[x].chanpos = x + 1; + } + if (zt_register(&wc->span, 0)) { + module_printk("Unable to register span with Zaptel\n"); + return -1; + } + wc->initialized = 1; + + return 0; +} + +static int t1_hardware_post_init(struct t1 *wc) +{ + unsigned int reg; + int x; + + /* T1 or E1 */ + if (t1e1override > -1) { + if (t1e1override) + wc->spantype = TYPE_E1; + else + wc->spantype = TYPE_T1; + } else { + if (t1_getpins(wc,0) & 0x01) /* returns 1 for T1 mode */ + wc->spantype = TYPE_T1; + else + wc->spantype = TYPE_E1; + } + debug_printk(1, "spantype: %s\n", wc->spantype==1 ? "T1" : "E1"); + + if (wc->spantype == TYPE_E1) { + if (unchannelized) + wc->chanmap = chanmap_e1uc; + else + wc->chanmap = chanmap_e1; + } else + wc->chanmap = chanmap_t1; + /* what version of the FALC are we using? */ + reg = t1_setreg(wc, 0x4a, 0xaa); + reg = t1_getreg(wc, 0x4a, 0); + debug_printk(1, "FALC version: %08x\n", reg); + + /* make sure reads and writes work */ + for (x = 0; x < 256; x++) { + t1_setreg(wc, 0x14, x); + if ((reg = t1_getreg(wc, 0x14, 0)) != x) + module_printk("Wrote '%x' but read '%x'\n", x, reg); + } + + /* all LED's blank */ + wc->ledtestreg = UNSET_LED_ORANGE(wc->ledtestreg); + wc->ledtestreg = UNSET_LED_REDGREEN(wc->ledtestreg); + t1_setleds(wc, wc->ledtestreg, 0); + + return 0; +} + +static inline void __t1_check_alarms_reads(struct t1 *wc) +{ + if (!(wc->span.flags & ZT_FLAG_RUNNING)) + return; + + if (t1_getreg(wc, 0x4c, 1)) + wc->isrreaderrors++; + if (t1_getreg(wc, 0x20, 1)) + wc->isrreaderrors++; + if (t1_getreg(wc, 0x4d, 1)) + wc->isrreaderrors++; +} + +static inline void __t1_check_alarms(struct t1 *wc) +{ + unsigned char c,d; + int alarms; + int x,j; + unsigned char fmr4; /* must read this always */ + + if (!(wc->span.flags & ZT_FLAG_RUNNING)) + return; + + c = t1_getreg_isr(wc, 0x4c); + fmr4 = t1_getreg_isr(wc, 0x20); /* must read this even if we don't use it */ + d = t1_getreg_isr(wc, 0x4d); + + /* Assume no alarms */ + alarms = 0; + + /* And consider only carrier alarms */ + wc->span.alarms &= (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_NOTOPEN); + + if (wc->spantype == TYPE_E1) { + if (c & 0x04) { + /* No multiframe found, force RAI high after 400ms only if + we haven't found a multiframe since last loss + of frame */ + if (!wc->flags.nmf) { + t1_setreg_full(wc, 0x20, 0x9f | 0x20, 1); /* LIM0: Force RAI High */ + wc->flags.nmf = 1; + module_printk("NMF workaround on!\n"); + } + t1_setreg_full(wc, 0x1e, 0xc3, 1); /* Reset to CRC4 mode */ + t1_setreg_full(wc, 0x1c, 0xf2, 1); /* Force Resync */ + t1_setreg_full(wc, 0x1c, 0xf0, 1); /* Force Resync */ + } else if (!(c & 0x02)) { + if (wc->flags.nmf) { + t1_setreg_full(wc, 0x20, 0x9f, 1); /* LIM0: Clear forced RAI */ + wc->flags.nmf = 0; + module_printk("NMF workaround off!\n"); + } + } + } else { + /* Detect loopup code if we're not sending one */ + if ((!wc->span.mainttimer) && (d & 0x08)) { + /* Loop-up code detected */ + if ((wc->loopupcnt++ > 80) && (wc->span.maintstat != ZT_MAINT_REMOTELOOP)) { + t1_setreg_full(wc, 0x36, 0x08, 1); /* LIM0: Disable any local loop */ + t1_setreg_full(wc, 0x37, 0xf6, 1); /* LIM1: Enable remote loop */ + wc->span.maintstat = ZT_MAINT_REMOTELOOP; + } + } else + wc->loopupcnt = 0; + /* Same for loopdown code */ + if ((!wc->span.mainttimer) && (d & 0x10)) { + /* Loop-down code detected */ + if ((wc->loopdowncnt++ > 80) && (wc->span.maintstat == ZT_MAINT_REMOTELOOP)) { + t1_setreg_full(wc, 0x36, 0x08, 1); /* LIM0: Disable any local loop */ + t1_setreg_full(wc, 0x37, 0xf0, 1); /* LIM1: Disable remote loop */ + wc->span.maintstat = ZT_MAINT_NONE; + } + } else + wc->loopdowncnt = 0; + } + + if (wc->span.lineconfig & ZT_CONFIG_NOTOPEN) { + for (x=0,j=0;x < wc->span.channels;x++) + if ((wc->span.chans[x].flags & ZT_FLAG_OPEN) || + (wc->span.chans[x].flags & ZT_FLAG_NETDEV)) + j++; + if (!j) + alarms |= ZT_ALARM_NOTOPEN; + } + + if (c & 0xa0) { + if (wc->alarmcount >= alarmdebounce) { + if (!unchannelized) + alarms |= ZT_ALARM_RED; + } else + wc->alarmcount++; + } else + wc->alarmcount = 0; + if (c & 0x4) + alarms |= ZT_ALARM_BLUE; + + /* Keep track of recovering */ + if ((!alarms) && wc->span.alarms) + wc->alarmtimer = ZT_ALARMSETTLE_TIME; + if (wc->alarmtimer) + alarms |= ZT_ALARM_RECOVER; + + /* If receiving alarms, go into Yellow alarm state */ + if (alarms && !wc->flags.sendingyellow) { + module_printk("Setting yellow alarm\n"); + + /* We manually do yellow alarm to handle RECOVER and NOTOPEN, otherwise it's auto anyway */ + t1_setreg_full(wc, 0x20, fmr4 | 0x20, 1); + wc->flags.sendingyellow = 1; + } else if (!alarms && wc->flags.sendingyellow) { + module_printk("Clearing yellow alarm\n"); + /* We manually do yellow alarm to handle RECOVER */ + t1_setreg_full(wc, 0x20, fmr4 & ~0x20, 1); + wc->flags.sendingyellow = 0; + } + + if ((c & 0x10) && !unchannelized) + alarms |= ZT_ALARM_YELLOW; + if (wc->span.mainttimer || wc->span.maintstat) + alarms |= ZT_ALARM_LOOPBACK; + wc->span.alarms = alarms; + zt_alarm_notify(&wc->span); +} + +static inline void __handle_leds(struct t1 *wc) +{ + if (wc->span.alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE)) { + wc->blinktimer++; + if (wc->blinktimer == 160) + wc->ledtestreg = SET_LED_RED(wc->ledtestreg); + if (wc->blinktimer == 480) { + wc->ledtestreg = UNSET_LED_REDGREEN(wc->ledtestreg); + wc->blinktimer = 0; + } + } else if (wc->span.alarms & ZT_ALARM_YELLOW) { + wc->yellowtimer++; + if (!(wc->yellowtimer % 2)) + wc->ledtestreg = SET_LED_RED(wc->ledtestreg); + else + wc->ledtestreg = SET_LED_GREEN(wc->ledtestreg); + } else { + if (wc->span.maintstat != ZT_MAINT_NONE) + wc->ledtestreg = SET_LED_ORANGE(wc->ledtestreg); + else + wc->ledtestreg = UNSET_LED_ORANGE(wc->ledtestreg); + if (wc->span.flags & ZT_FLAG_RUNNING) + wc->ledtestreg = SET_LED_GREEN(wc->ledtestreg); + else + wc->ledtestreg = UNSET_LED_REDGREEN(wc->ledtestreg); + } + + if (wc->ledtestreg != wc->ledlastvalue) { + t1_setleds(wc, wc->ledtestreg, 1); + wc->ledlastvalue = wc->ledtestreg; + } +} + + +static void __t1_do_counters(struct t1 *wc) +{ + if (wc->alarmtimer) { + if (!--wc->alarmtimer) { + wc->span.alarms &= ~(ZT_ALARM_RECOVER); + zt_alarm_notify(&wc->span); + } + } +} + +static inline void t1_isr_misc(struct t1 *wc) +{ + unsigned int x; + + if (unlikely(!wc->initialized)) return; + + __handle_leds(wc); + + __t1_do_counters(wc); + + x = wc->intcount & 0xF; + switch (x) { + case 0: + __t1_check_sigbits_reads(wc); + break; + case 1: + if (!(wc->intcount & 0x30)) { + __t1_check_alarms_reads(wc); + wc->alarms_read=1; + } + break; + case 2: + break; + case 4: + break; + case 5: + break; + case 7: + __t1_check_sigbits(wc); + break; + case 8: + if (wc->alarms_read) { + __t1_check_alarms(wc); + wc->alarms_read=0; + } + break; + case 9: + clean_leftovers(wc); + break; + } +} + +static inline void t1_transmitprep(struct t1 *wc, int dbl) +{ + volatile unsigned char *writechunk; + int x; + int y; + int chan; + + dbl = dbl % 2; + + writechunk = (volatile unsigned char *)(wc->writechunk); + if (dbl) + /* Write is at interrupt address. Start writing from normal offset */ + writechunk += SFRAME_SIZE; + + /* Calculate Transmission */ + if (likely(wc->initialized)) + zt_transmit(&wc->span); + + for (x = 0; x < ZT_CHUNKSIZE; x++) { + if (likely(wc->initialized)) { + for (chan = 0; chan < wc->span.channels; chan++) + writechunk[(chan+1)*2] = wc->chans[chan].writechunk[x]; + } + + /* process the command queue */ + for (y = 0; y < 7; y++) { + cmd_dequeue(wc, writechunk, x, y); + } + + if (x < ZT_CHUNKSIZE - 1) { + writechunk[EFRAME_SIZE] = wc->ctlreg; + writechunk[EFRAME_SIZE + 1] = wc->txident++; + } + writechunk += (EFRAME_SIZE + EFRAME_GAP); + } +} + +static inline void cmd_retransmit(struct t1 *wc) +{ + unsigned int x; + unsigned long flags; + + spin_lock_irqsave(&wc->reglock, flags); + for (x = 0; x < sizeof(wc->cmdq.cmds) / sizeof(wc->cmdq.cmds[0]); x++) { + if (!(wc->cmdq.cmds[x].flags & __CMD_FIN)) { + wc->cmdq.cmds[x].flags &= ~(__CMD_TX) ; /* clear __CMD_TX */ + wc->cmdq.cmds[x].ident = 0; + } + } + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline void t1_receiveprep(struct t1 *wc, int dbl) +{ + volatile unsigned char *readchunk; + int x,chan; + unsigned char expected; + + dbl = dbl % 2; + + readchunk = (volatile unsigned char *)wc->readchunk; + if (dbl) + readchunk += SFRAME_SIZE; + for (x = 0; x < ZT_CHUNKSIZE; x++) { + if (likely(wc->initialized)) { + for (chan = 0; chan < wc->span.channels; chan++) { + wc->chans[chan].readchunk[x]= readchunk[(chan+1)*2]; + } + } + if (x < ZT_CHUNKSIZE - 1) { + expected = wc->rxident+1; + wc->rxident = readchunk[EFRAME_SIZE + 1]; + if (wc->rxident != expected) { + wc->span.irqmisses++; + cmd_retransmit(wc); + if (unlikely(debug && wc->initialized)) + module_printk("oops: rxident=%d expected=%d\n", wc->rxident, expected); + } + } + cmd_decipher(wc, readchunk); + readchunk += (EFRAME_SIZE + EFRAME_GAP); + } + + /* echo cancel */ + + if (likely(wc->initialized)) { + for (x = 0; x < wc->span.channels; x++) { + zt_ec_chunk(&wc->chans[x], wc->chans[x].readchunk, wc->ec_chunk2[x]); + memcpy(wc->ec_chunk2[x],wc->ec_chunk1[x],ZT_CHUNKSIZE); + memcpy(wc->ec_chunk1[x],wc->chans[x].writechunk,ZT_CHUNKSIZE); + } + zt_receive(&wc->span); + } + + /* Wake up anyone sleeping to read/write a new register */ + wake_up_interruptible(&wc->regq); +} + +static inline int t1_check_descriptor(struct t1 *wc, int tx) +{ + int o2 = 0; + + if (!tx) { + o2 += ERING_SIZE * 4; + o2 += wc->rdbl * 4; + } else { + o2 += wc->tdbl * 4; + } + + if (!(wc->descripchunk[o2] & 0x80000000)) { + if (tx) { + wc->txints++; + t1_transmitprep(wc, wc->tdbl); + t1_reinit_descriptor(wc, tx, wc->tdbl, "txchk"); + wc->tdbl = (wc->tdbl + 1) % ERING_SIZE; + wc->intcount++; + t1_isr_misc(wc); + } else { + wc->rxints++; + t1_receiveprep(wc, wc->rdbl); + t1_reinit_descriptor(wc, tx, wc->rdbl, "rxchk"); + wc->rdbl = (wc->rdbl + 1) % ERING_SIZE; + } + return 1; + } + return 0; +} + +static int t1_hardware_init(struct t1 *wc) +{ + /* Hardware stuff */ + unsigned int reg; + unsigned long newjiffies; + + /* Initialize descriptors */ + t1_init_descriptors(wc); + + /* Enable I/O Access */ + pci_read_config_dword(wc->dev, PCI_COMMAND, ®); + reg |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + pci_write_config_dword(wc->dev, PCI_COMMAND, reg); + debug_printk(1, "PCI Config reg is %08x\n", reg); + + t1_setctl(wc, 0x0000, 0xfff88001); + + newjiffies = jiffies + HZ/10; + while(((reg = t1_getctl(wc,0x0000)) & 0x00000001) && ( time_after(newjiffies,jiffies) )); + debug_printk(1, "ctlreg 0x0000 now=%08x!\n", reg); + + /* Configure watchdogs, access, etc */ + t1_setctl(wc, 0x0030, 0x00280048); + t1_setctl(wc, 0x0078, 0x00000013 /* | (1 << 28) */); + + reg = t1_getctl(wc, 0x00fc); + t1_setctl(wc, 0x00fc, (reg & ~0x7) | 0x7); /* normal mode */ + t1_setsdi(wc, 0x00, 0x0100); + t1_setsdi(wc, 0x16, 0x2100); + debug_printk(1, "Detected SDI REG0: %08x\n", t1_getsdi(wc, 0x00)); + debug_printk(1, "Detected SDI REG1: %08x\n", t1_getsdi(wc, 0x01)); + debug_printk(1, "Detected SDI REG2: %08x\n", t1_getsdi(wc, 0x02)); + + reg = t1_getctl(wc, 0x00fc); + debug_printk(1, "(pre) Reg fc is %08x\n", reg); + + t1_setctl(wc, 0x00fc, (reg & ~0x7) | 0x4); /* mac only */ + t1_setsdi(wc, 0x00, 0x0100); /* full duplex */ + t1_setsdi(wc, 0x16, 0x2100); + reg = t1_getctl(wc, 0x00fc); + debug_printk(1, "(post) ctlreg 0xfc=%08x\n", reg); + debug_printk(1, "Detected SDI REG2: %08x\n", t1_getsdi(wc, 0x02)); + debug_printk(1, "ctlreg 0x0088=%08x\n", t1_getctl(wc, 0x0088)); + + return 0; +} + + +ZAP_IRQ_HANDLER(te12xp_interrupt) +{ + struct t1 *wc = dev_id; + unsigned int ints; + int res; + + /* Read interrupts */ + ints = t1_getctl(wc, 0x0028); + + if (!ints) +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + + /* clear interrupts interrupts (we only get here if interrupt is for us) */ + t1_setctl(wc, 0x0028, ints); + ints &= wc->intmask; + + if (ints & 0x00000041) { + do { + res = t1_check_descriptor(wc, 0); + res |= t1_check_descriptor(wc, 1); + } while(res); + } + +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif +} + +static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct t1 *wc; + struct t1_desc *d = (struct t1_desc *) ent->driver_data; + unsigned int x; + + 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; + + wc = kmalloc(sizeof(*wc), GFP_KERNEL); + if (!wc) + return -ENOMEM; + + ifaces[x] = wc; + memset(wc, 0, sizeof(*wc)); + spin_lock_init(&wc->reglock); + wc->iobase = pci_resource_start(pdev, 0); + wc->dev = pdev; + wc->variety = d->name; + /* Keep track of whether we need to free the region */ + if (request_region(wc->iobase, 0xff, te12xp_driver.name)) + wc->freeregion = 1; + + /* Allocate enough memory for two zt chunks, receive and transmit. + * Each sample uses 32 bits. Allocate an extra set just for + * control too */ + wc->writechunk = (int *) 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); + kfree(wc); + return -ENOMEM; + } + + wc->readchunk = wc->writechunk + SFRAME_SIZE / 2; /* in doublewords */ + wc->readdma = wc->writedma + SFRAME_SIZE * 2; /* in bytes */ + + wc->descripchunk = wc->readchunk + SFRAME_SIZE / 2; /* in doublewords */ + wc->descripdma = wc->readdma + SFRAME_SIZE * 2; /* in bytes */ + + /* Initialize Write/Buffers to all blank data */ + memset((void *)wc->writechunk, 0x00, SFRAME_SIZE * 2); + memset((void *)wc->readchunk, 0x00, SFRAME_SIZE * 2); + + init_waitqueue_head(&wc->regq); + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* Keep track of which device we are */ + pci_set_drvdata(pdev, wc); + + if (request_irq(pdev->irq, te12xp_interrupt, SA_SHIRQ, te12xp_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); + return -EIO; + } + + if (t1_hardware_init(wc)) { + /* Set Reset Low */ + t1_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; + + } + + t1_enable_interrupts(wc); + t1_start_dma(wc); + t1_hardware_post_init(wc); + t1_software_init(wc); + module_printk("Found a %s\n", wc->variety); + + return 0; +} + +static void __devexit te12xp_remove_one(struct pci_dev *pdev) +{ + struct t1 *wc = pci_get_drvdata(pdev); + + if (!wc) + return; + + /* Stop any DMA */ + t1_stop_dma(wc); + + /* In case hardware is still there */ + t1_disable_interrupts(wc); + + if (debug && wc->isrreaderrors) + debug_printk(1, "isrreaderrors=%d\n", wc->isrreaderrors); + + /* Immediately free resources */ + pci_free_consistent(pdev, PCI_WINDOW_SIZE, (void *)wc->writechunk, wc->writedma); + free_irq(pdev->irq, wc); + + /* Release span, possibly delayed */ + if (!wc->usecount) + t1_release(wc); + else + wc->dead = 1; +} + +static struct pci_device_id te12xp_pci_tbl[] = { + { 0xd161, 0x0120, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &te12xp}, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, te12xp_pci_tbl); + +static struct pci_driver te12xp_driver = { + name: "wcte12xp", + probe: te12xp_init_one, +#ifdef LINUX26 + remove: __devexit_p(te12xp_remove_one), +#else + remove: te12xp_remove_one, +#endif + suspend: NULL, + resume: NULL, + id_table: te12xp_pci_tbl, +}; + +static int __init te12xp_init(void) +{ + int res; + + res = pci_module_init(&te12xp_driver); + + return res ? -ENODEV : 0; +} + + +static void __exit te12xp_cleanup(void) +{ + pci_unregister_driver(&te12xp_driver); +} + +#ifdef LINUX26 +module_param(debug, int, S_IRUGO | S_IWUSR); +module_param(loopback, int, S_IRUGO | S_IWUSR); +module_param(t1e1override, int, S_IRUGO | S_IWUSR); +module_param(j1mode, int, S_IRUGO | S_IWUSR); +module_param(alarmdebounce, int, S_IRUGO | S_IWUSR); +#else +MODULE_PARM(debug, "i"); +MODULE_PARM(loopback, "i"); +MODULE_PARM(t1e1override, "i"); +MODULE_PARM(j1mode, "i"); +MODULE_PARM(alarmdebounce, "i"); +#endif + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +module_init(te12xp_init); +module_exit(te12xp_cleanup); @@ -720,29 +720,31 @@ enum { #define ZT_TRANSCODE_MAGIC 0x74a9c0de /* Operations */ -#define ZT_TCOP_RESET 1 /* Reset the channel state / codec selection */ +#define ZT_TCOP_ALLOCATE 1 /* Allocate/reset 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 */ typedef struct zt_transcode_info { unsigned int op; unsigned int tcnum; char name[80]; + int numchannels; unsigned int srcfmts; unsigned int dstfmts; } ZT_TRANSCODE_INFO; -#define ZT_TCCONF_USETS (1 << 0) /* Use/update timestamp field */ -#define ZT_TCCONF_USESEQ (1 << 1) /* Use/update seqno field */ +#define ZT_TCCONF_USETS (1 << 0) /* Use/update timestamp field */ +#define ZT_TCCONF_USESEQ (1 << 1) /* Use/update seqno field */ -#define ZT_TCSTAT_DSTRDY (1 << 0) /* Destination data is ready */ -#define ZT_TCSTAT_DSTBUSY (1 << 1) /* Destination data is outstanding */ +#define ZT_TCSTAT_DSTRDY (1 << 0) /* Destination data is ready */ +#define ZT_TCSTAT_DSTBUSY (1 << 1) /* Destination data is outstanding */ -#define __ZT_TRANSCODE_BUFSIZ 16384 -#define ZT_TRANSCODE_HDRLEN 256 -#define ZT_TRANSCODE_BUFSIZ ((__ZT_TRANSCODE_BUFSIZ) - (ZT_TRANSCODE_HDRLEN)) -#define ZT_TRANSCODE_DSTOFFSET (((ZT_TRANSCODE_BUFSIZ) / 2) + ZT_TRANSCODE_HDRLEN) -#define ZT_TRANSCODE_SRCOFFSET (((ZT_TRANSCODE_BUFSIZ) / 2) + ZT_TRANSCODE_HDRLEN) +#define __ZT_TRANSCODE_BUFSIZ 16384 +#define ZT_TRANSCODE_HDRLEN 256 +#define ZT_TRANSCODE_BUFSIZ ((__ZT_TRANSCODE_BUFSIZ) - (ZT_TRANSCODE_HDRLEN)) +#define ZT_TRANSCODE_DSTOFFSET (((ZT_TRANSCODE_BUFSIZ) / 2) + ZT_TRANSCODE_HDRLEN) +#define ZT_TRANSCODE_SRCOFFSET (((ZT_TRANSCODE_BUFSIZ) / 2) + ZT_TRANSCODE_HDRLEN) typedef struct zt_transcode_header { unsigned int srcfmt; /* See formats.h -- use TCOP_RESET when you change */ @@ -761,8 +763,6 @@ typedef struct zt_transcode_header { unsigned int magic; /* Magic value -- ZT_TRANSCODE_MAGIC, read by user */ unsigned int config; /* Read/write by user */ unsigned int status; /* Read/write by user */ - - /* XXX: fix this to automatically calculate somehow */ unsigned char userhdr[ZT_TRANSCODE_HDRLEN - (sizeof(unsigned int) * 14)]; /* Storage for user parameters */ unsigned char srcdata[ZT_TRANSCODE_BUFSIZ / 2]; /* Storage of source data */ unsigned char dstdata[ZT_TRANSCODE_BUFSIZ / 2]; /* Storage of destination data */ @@ -1438,6 +1438,8 @@ struct zt_transcoder_channel { wait_queue_head_t ready; int errorstatus; int offset; + unsigned int chan_built; + unsigned int built_fmts; unsigned int flags; unsigned int srcfmt; unsigned int dstfmt; @@ -1539,26 +1541,6 @@ int zt_transcoder_unregister(struct zt_transcoder *tc); /* Alert a transcoder */ int zt_transcoder_alert(struct zt_transcoder_channel *ztc); -/* Sanity check values */ -static inline int zt_tc_sanitycheck(struct zt_transcode_header *zth, unsigned int outbytes) -{ - if (zth->dstoffset >= sizeof(zth->dstdata)) - return 0; - if (zth->dstlen >= sizeof(zth->dstdata)) - return 0; - if (outbytes >= sizeof(zth->dstdata)) - return 0; - if ((zth->dstoffset + zth->dstlen + outbytes) >= sizeof(zth->dstdata)) - return 0; - if (zth->srcoffset >= sizeof(zth->srcdata)) - return 0; - if (zth->srclen >= sizeof(zth->srcdata)) - return 0; - if ((zth->srcoffset + zth->srclen) > sizeof(zth->srcdata)) - return 0; - return 1; -} - /* Unregister a span */ int zt_unregister(struct zt_span *span); @@ -5,24 +5,26 @@ </member> <member name="torisa" displayname="Tormenta ISA" remove_on_change="torisa.o torisa.ko"> </member> - <member name="wcfxo" displayname="Wildcard X100P" remove_on_change="wcfxo.o wcfxo.ko"> + <member name="wcfxo" displayname="Digium Wildcard X100P" remove_on_change="wcfxo.o wcfxo.ko"> </member> - <member name="wct1xxp" displayname="Wildcard T100P / E100P" remove_on_change="wct1xxp.o wct1xxp.ko"> + <member name="wct1xxp" displayname="Digium Wildcard T100P / E100P" remove_on_change="wct1xxp.o wct1xxp.ko"> </member> - <member name="wct4xxp" displayname="Wildcard TE4XXP / TE2XXP" remove_on_change="wct4xxp/wct4xxp.o wct4xxp/wct4xxp.ko"> + <member name="wct4xxp" displayname="Digium Wildcard TE4XXP / TE2XXP" remove_on_change="wct4xxp/wct4xxp.o wct4xxp/wct4xxp.ko"> </member> - <member name="wctc4xxp" displayname="Wildcard TC400B" remove_on_change="wctc4xxp/wctc4xxp.o wctc4xxp/wctc4xxp.ko"> + <member name="wctc4xxp" displayname="Digium Wildcard TC400B" remove_on_change="wctc4xxp/wctc4xxp.o wctc4xxp/wctc4xxp.ko"> <depend>zttranscode</depend> </member> - <member name="wctdm" displayname="Wildcard TDM400P" remove_on_change="wctdm.o wctdm.ko"> + <member name="wctdm" displayname="Digium Wildcard TDM400P" remove_on_change="wctdm.o wctdm.ko"> </member> - <member name="wctdm24xxp" displayname="Wildcard TDM2400P / TDM800P" remove_on_change="wctdm24xxp.o wctdm24xxp.ko"> + <member name="wctdm24xxp" displayname="Digium Wildcard TDM2400P / TDM800P" remove_on_change="wctdm24xxp.o wctdm24xxp.ko"> </member> - <member name="wcte11xp" displayname="Wildcard TE110P" remove_on_change="wcte11xp.o wcte11xp.ko"> + <member name="wcte11xp" displayname="Digium Wildcard TE110P" remove_on_change="wcte11xp.o wcte11xp.ko"> </member> - <member name="wcusb" displayname="Wildcard S100U" remove_on_change="wcusb.o wcusb.ko"> + <member name="wcte12xp" displayname="Digium Wildcard TE120P" remove_on_change="wcte12xp.o wcte12xp.ko"> </member> - <member name="xpp_usb" displayname="Xorcom Astribank"> + <member name="wcusb" displayname="Digium S100U" remove_on_change="wcusb.o wcusb.ko"> + </member> + <member name="xpp" displayname="Xorcom Astribank"> </member> <member name="ztd-eth" displayname="TDM-over-Ethernet Virtual Span" remove_on_change="ztd-eth.o ztd-eth.ko"> <depend>ztdynamic</depend> diff --git a/zttranscode.c b/zttranscode.c index 7e3d287..cf99b8a 100644 --- a/zttranscode.c +++ b/zttranscode.c @@ -3,7 +3,7 @@ * * Written by Mark Spencer <markster@digium.com> * - * Copyright (C) 2006, Digium, Inc. + * Copyright (C) 2006-2007, Digium, Inc. * * All rights reserved. * @@ -42,7 +42,7 @@ #ifdef STANDALONE_ZAPATA #include "zaptel.h" #else -#include <zaptel/zaptel.h> +#include <linux/zaptel.h> #endif #ifdef LINUX26 #include <linux/moduleparam.h> @@ -74,6 +74,8 @@ struct zt_transcoder *zt_transcoder_alloc(int numchans) 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; @@ -207,15 +209,18 @@ static void ztc_release(struct zt_transcoder_channel *ztc) if (!ztc) return; - for (page = virt_to_page(zth); - page < virt_to_page((unsigned long) zth + sizeof(*zth)); - page++) - ClearPageReserved(page); - ztc->flags &= ~(ZT_TC_FLAG_BUSY); - if (ztc->tch) + + 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; + /* Actually reset the transcoder channel */ if (ztc->flags & ZT_TC_FLAG_TRANSIENT) kfree(ztc); if (debug) @@ -252,6 +257,8 @@ static int do_reset(struct zt_transcoder_channel **ztc) for (x = 0; x < tc->numchannels; x++) { if (tc->channels[x].flags & ZT_TC_FLAG_BUSY) continue; + if ((tc->channels[x].chan_built) && ((zth->srcfmt | zth->dstfmt) != tc->channels[x].built_fmts)) + continue; newztc = &tc->channels[x]; newztc->flags = ZT_TC_FLAG_BUSY; @@ -274,7 +281,7 @@ static int do_reset(struct zt_transcoder_channel **ztc) /* Actually reset the transcoder channel */ if ((*ztc)->parent && ((*ztc)->parent->operation)) - return (*ztc)->parent->operation((*ztc), ZT_TCOP_RESET); + return (*ztc)->parent->operation((*ztc), ZT_TCOP_ALLOCATE); return -EINVAL; } @@ -308,6 +315,7 @@ static int zt_tc_getinfo(unsigned long data) return -ENOSYS; strncpy(info.name, tc->name, sizeof(info.name) - 1); + info.numchannels = tc->numchannels; info.srcfmts = tc->srcfmts; info.dstfmts = tc->dstfmts; @@ -333,11 +341,17 @@ static int zt_tc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, case ZT_TCOP_GETINFO: ret = zt_tc_getinfo(data); break; - case ZT_TCOP_RESET: + case ZT_TCOP_ALLOCATE: /* Reset transcoder, possibly changing who we point to */ ret = do_reset(&ztc); file->private_data = ztc; break; + case ZT_TCOP_RELEASE: + ret = ztc->parent->operation(ztc, ZT_TCOP_RELEASE); + break; + case ZT_TCOP_TEST: + ret = ztc->parent->operation(ztc, ZT_TCOP_TEST); + break; case ZT_TCOP_TRANSCODE: if (!ztc->parent->operation) return -EINVAL; @@ -449,7 +463,7 @@ void zttranscode_cleanup(void) } #ifdef LINUX26 -module_param(debug, int, 0600); +module_param(debug, int, S_IRUGO | S_IWUSR); #else MODULE_PARM(debug, "i"); #endif |