[Pkg-voip-commits] r1980 - in zaptel/trunk: . debian debian/patches

Tzafrir Cohen tzafrir-guest at costa.debian.org
Thu Jun 29 12:50:26 UTC 2006


Author: tzafrir-guest
Date: 2006-06-29 12:50:04 +0000 (Thu, 29 Jun 2006)
New Revision: 1980

Added:
   zaptel/trunk/debian/patches/xpp_xorcom_debian.dpatch
   zaptel/trunk/debian/zaptel.permissions.rules
   zaptel/trunk/opvxa1200.c
Removed:
   zaptel/trunk/debian/dirs
   zaptel/trunk/debian/genzaptelconf
   zaptel/trunk/debian/genzaptelconf.8
Modified:
   zaptel/trunk/debian/changelog
   zaptel/trunk/debian/control
   zaptel/trunk/debian/copyright
   zaptel/trunk/debian/docs
   zaptel/trunk/debian/modulestest
   zaptel/trunk/debian/patches/00list
   zaptel/trunk/debian/patches/Makefile_bristuff.dpatch
   zaptel/trunk/debian/rules
   zaptel/trunk/debian/zaptel.dirs
   zaptel/trunk/debian/zaptel.init
   zaptel/trunk/debian/zaptel.install
Log:
* debian/patches/Makefile_kbuild: a small part of the original one.
  Fixes building on Sarge
* xpp revision r1460 (branches/RELEASE-1.1.0):
- With EC
- FXS caller ID working
- Improved FXO
- Back to RBS
- genzaptelconf is now in zaptel
* xpp/utils/Makefile has a decent install target
* debian/rules: Use CURDIR
* debian/modulestest: Building modules for -3 kernels
* fix x bit of files in /usr/share/zaptel
* removed genzaptelconf from debian/
* Added support for the OpenVox A1200P card (http://www.openvox.com.cn/)
* debian/control: require libusb-dev for building xpp firmware loader.
* debian/control: Recommend package xpp-firmware (should be added to
  non-free)

Modified: zaptel/trunk/debian/changelog
===================================================================
--- zaptel/trunk/debian/changelog	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/changelog	2006-06-29 12:50:04 UTC (rev 1980)
@@ -9,6 +9,23 @@
   * Separating ZapBRI modules to directories, rather than patches
   * Example configs moved from zaptel-source to zaptel
   * Removing some unneeded dirs from zaptel-source
+  * debian/patches/Makefile_kbuild: a small part of the original one.
+    Fixes building on Sarge
+  * xpp revision r1460 (branches/RELEASE-1.1.0):
+  - With EC
+  - FXS caller ID working
+  - Improved FXO
+  - Back to RBS
+  - genzaptelconf is now in zaptel
+  * xpp/utils/Makefile has a decent install target
+  * debian/rules: Use CURDIR
+  * debian/modulestest: Building modules for -3 kernels
+  * fix x bit of files in /usr/share/zaptel
+  * removed genzaptelconf from debian/
+  * Added support for the OpenVox A1200P card (http://www.openvox.com.cn/)
+  * debian/control: require libusb-dev for building xpp firmware loader.
+  * debian/control: Recommend package xpp-firmware (should be added to
+    non-free)
 
  -- Kilian Krause <kilian at debian.org>  Sat, 17 Jun 2006 16:05:32 +0200
 

Modified: zaptel/trunk/debian/control
===================================================================
--- zaptel/trunk/debian/control	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/control	2006-06-29 12:50:04 UTC (rev 1980)
@@ -9,7 +9,10 @@
 Package: zaptel
 Section: comm
 Architecture: any
-Depends: ${shlibs:Depends}, procps
+# Xorcom packages depend on xpp-firmware. Debian zaptel will probably 
+# just recommend it.
+Depends: ${shlibs:Depends}, procps, fxload, xpp-firmware
+Recommends: xpp-firmware
 Description: zapata telephony utilities
  Userspace utilities for configuring the Zapata telephony kernel driver, 
  which supports various telephony hardware, such as Wildcard series of

Modified: zaptel/trunk/debian/copyright
===================================================================
--- zaptel/trunk/debian/copyright	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/copyright	2006-06-29 12:50:04 UTC (rev 1980)
@@ -39,3 +39,21 @@
 vzaphfc is derived from bristuff and since enhanced by:
  * Copyright (C) 2004-2006, Daniele "Vihai" Orlandi <daniele at orlandi.com>
  * Copyright (C) 2006, headissue GmbH; Jens Wilke <jw_vzaphfc at headissue.com>
+
+Files in the xpp/ subdirectory:
+
+* Written by Oron Peled <oron at actcom.co.il>
+* Copyright (C) 2004-2006, Xorcom
+
+The following two files are not yet available for download. However they 
+are based on other Zaptel drives and distributed under the terms of the 
+GNU GPL:
+
+opvxa1200.c:
+* OpenVox A1200P FXS/FXO Interface Driver for Zapata Telephony interface
+*
+* Modify from wctdm.c by MiaoLin<miaolin at openvox.com.cn>
+
+opvxa1200.c is not availble for download from the author's site yet 
+(it is distributed with the CD that is included with the product).
+Author promisses to set up a public mirror soon.

Deleted: zaptel/trunk/debian/dirs
===================================================================
--- zaptel/trunk/debian/dirs	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/dirs	2006-06-29 12:50:04 UTC (rev 1980)
@@ -1,3 +0,0 @@
-sbin
-usr/bin
-usr/sbin

Modified: zaptel/trunk/debian/docs
===================================================================
--- zaptel/trunk/debian/docs	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/docs	2006-06-29 12:50:04 UTC (rev 1980)
@@ -3,3 +3,4 @@
 README.fxsusb
 README.Linux26
 README.udev
+xpp/README.Astribank

Deleted: zaptel/trunk/debian/genzaptelconf
===================================================================
--- zaptel/trunk/debian/genzaptelconf	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/genzaptelconf	2006-06-29 12:50:04 UTC (rev 1980)
@@ -1,929 +0,0 @@
-#! /bin/bash
-
-# genzaptelconf: generate as smartly as you can:
-#		/etc/zaptel.conf
-#		/etc/asterisk/zapata-channels.conf (to be #include-d into zapata.conf)
-#	update:
-#		With '-M' /etc/modules (list of modules to load)
-#
-# Copyright (C) 2005 by Xorcom <support at xorcom.com>
-#
-# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#
-#
-# If you have any technical questions, contact 
-# Tzafrir Cohen <tzafrir.cohen at xorcom.com>
-#	
-
-# The script uses a number of bash-specific features
-# TODO: either ditch them or convert to perl
-# Don't override variables here. Override them in /etc/default/zaptel
-#
-# 0.5.1:
-# * Initial support for ztgsm (Junghanns's PCI GSM card)
-# * Wait for the xpp module to register if just loaded
-# 0.5.0:
-# * Not trying to read from zaptel channels: we can run genzaptelconf even 
-#   with asterisk up.
-# * Don't add ztdummy to the list of detected modules
-# 0.4.4:
-# * remove ztdummy when rewriting modules file
-# * Better ISDN PRI behaviour in Israel (il)
-# 0.4.3:
-# * Added -F: to disable writing about FXS ports in zapata.conf
-# * if we have an astribank: start zaptel rather than simple ztcfg (xpd sync)
-# 0.4.2: 
-# * support for digital input/output ports of Astribank
-# * Different ISDN parameters for the Netherlands (nl)
-# * unload zaptel and its dependencies, not a hard-coded list
-# * hence we can reduce the list of modules
-
-# /etc/default/zaptel may override the following variables
-VERSION=0.5.1
-VERSION_FULL="$VERSION $Id: /xorcom/zaptel/trunk/debian/genzaptelconf 292 2006-04-10T13:26:16.840155Z tzafrir  $"
-lc_country=us
-base_exten=6000
-# If set: no context changes are made in zapata-channels.conf
-#context_manual=yes
-context_lines=from-pstn      # context into which PSTN calls go
-context_phones=from-internal # context for internal phones calls.
-# The two below apply to input and output ports of the Xorcom Astribank:
-context_input=astbank-input
-context_output=astbank-output # useless, but helps marking the channels :-)
-# TODO: what about PRI/BRI?
-# If set: no group changes are made in zapata-channels.conf
-#group_manual=yes
-group_phones=5 # group for phones
-group_lines=0  # group for lines
-# set 'immediate=yes' for Asteribank input channels and 'immediate=no' 
-# for others. Note that if an Astribank is not detected, the script 
-# will set this to "no", so you can safely leave it as "yes".
-set_immediate=yes
-
-ZAPCONF_FILE=/etc/zaptel.conf
-ZAPATA_FILE=/etc/asterisk/zapata-channels.conf
-ZAPTEL_BOOT=/etc/default/zaptel
-MODLIST_FILE=/etc/modules
-exten_base_dir=/etc/asterisk/extensions-phones.d
-exten_defs_file=/etc/asterisk/extensions-defs.conf
-ZTCFG=/sbin/ztcfg
-
-# a temporary directory. Created when the switch -r is parsed on getopts
-# and deleted in the end on update_extensions_defs
-tmp_dir=
-
-# A list of all modules:
-# - the list of modules which will be probed (in this order) if -d is used
-# - The module that will be deleted from /etc/modules , if -d -M is used
-ALL_MODULES="zaphfc qozap ztgsm wctdm wctdm24xxp wcfxo wcfxs pciradio tor2 torisa wct1xxp wct4xxp wcte11xp wcusb xpp_usb"
-
-# read default configuration from /etc/default/zaptel
-if [ -r $ZAPTEL_BOOT ]; then . $ZAPTEL_BOOT; fi
-
-# it is safe to use -c twice: the last one will be used.
-ztcfg_cmd="$ZTCFG -c $ZAPCONF_FILE"
-
-# work around a bug (that was already fixed) in the installer:
-if [ "$lc_country" = '' ]; then lc_country=us; fi
-
-force_stop_ast=no
-do_detect=no
-do_unload=no
-do_module_list=no
-verbose=no
-fxsdisable=no
-rapid_extens=''
-# global: current extension number in extensions list. Should only be 
-# changed in print_pattern:
-rapid_cur_exten=1 
-# set the TRUNK in extensidialplan dialplan defs file  rapid_conf_mode=no
-
-die() {
-	echo "$@" >&2
-	exit 1
-}
-
-say() {
-	if [ "$verbose" = no ]; then
-		return
-	fi
-	echo "$@"   >&2
-}
-
-run_ztcfg() {
-	if [ "$verbose" = no ]; then
-		$ztcfg_cmd "$@"
-	else
-		say "Reconfiguring identified channels"
-		$ztcfg_cmd -vv "$@"
-	fi
-}
-
-update_module_list() {
-	del_args=`for i in $ALL_MODULES ztdummy
-	do
-		echo "$i" | sed s:.\*:-e\ '/^&/d':
-	done`
-	add_args=`for i in $*
-	do
-		echo "$i" | sed s:.\*:-e\ '\$a&':
-	done`
-	
-	sed -i.bak $del_args "$MODLIST_FILE"
-	for i in $*
-	do
-		echo "$i"
-	done >> "$MODLIST_FILE"
-}
-
-do_update() {
-	if [ ! -d `dirname ${ZAPTEL_BOOT}` ]
-	then
-		return
-	fi
-	sed -i.bak "s/^$1=.*\$/$1=\"$2\"/" ${ZAPTEL_BOOT}
-	if ! grep -q "^$1=" ${ZAPTEL_BOOT}; then
-		echo "$1=\"$2\"" >> ${ZAPTEL_BOOT}
-	fi
-}
-
-update_extensions_defs() {
-	if [ "$rapid_conf_mode" = 'yes' ]
-	then
-		say "DEBUG: Updating dialplan defs file $exten_defs_file"
-		if [ "`echo $tmp_dir/fxo_* | grep -v '*'`" != '' ]
-		then
-			trunk_nums=`cat $tmp_dir/fxo_* | sort -n | xargs`
-			say "Configuring TRUNK to be [first of] zaptel channels: $trunk_nums"
-			trunk_dev=`echo $trunk_nums| sed -e 's/ /\\\\\\&/g' -e 's/[0-9a-zA-Z]\+/Zap\\\\\\/&/g'`
-			echo >&2 sed -i "s/^TRUNK.*=>.*/TRUNK => $trunk_dev/" $exten_defs_file
-			sed -i "s/^TRUNK.*=>.*/TRUNK => $trunk_dev/" $exten_defs_file
-			if ! grep -q "^TRUNK =>" $exten_defs_file; then
-				trunk_dev=`echo $trunk_nums| sed -e 's/ /&/g' -e 's/[0-9a-zA-Z]*/Zap\\/&/g'`
-				echo "TRUNK => $trunk_dev" >> $exten_defs_file
-			fi
-		else
-			say "Warning: No FXO channel for trunk. Moving on."
-		fi
-		if [ "`echo $tmp_dir/fxs_* | grep -v '*'`" != '' ]
-		then
-			fxs_nums=`cat $tmp_dir/fxs_* | sort -n | xargs`
-			zap_nums=`grep '^[^;].*Zap/\${CHAN_ZAP_' $exten_base_dir/*.conf | \
-				sed -e 's/.*Zap\/\${CHAN_ZAP_\([0-9]*\)}.*/\1/' | sort -u | xargs`
-			say "Configuring channels: $fxs_nums as channel placeholders: $zap_nums"
-			j=1
-			for i in $zap_nums
-			do
-				chan=`echo $fxs_nums | awk "{print \\$$i}"`
-				if [ "$chan" = '' ]
-				then
-					# if the result is empty, we probably got past the last one.
-					# bail out.
-					say "Warning: No FXS channel for CHAN_ZAP_$i. Moving on"
-					break
-				fi
-				say "DEBUG: setting channel $chan to placeholder $i"
-				if grep -q "^CHAN_ZAP_$i " $exten_defs_file
-				then
-					sed -i -e "s/^CHAN_ZAP_$i .*/CHAN_ZAP_$i => Zap\/$chan/" $exten_defs_file
-				else
-					echo "CHAN_ZAP_$i => Zap/$chan" >> $exten_defs_file
-				fi
-			done
-		fi	
-		# cleaning up the temp dir
-	fi
-	if [ -d "$tmp_dir" ]; then rm -rf "$tmp_dir"; fi
-}
-
-check_for_astribank(){
-	if ! grep -q XPP_IN/ /proc/zaptel/* 2>/dev/null
-	then
-		# we only get here is if we find no Astribank input channels
-		# in /proc/zaptel . Hence we can safely disable their special settings:
-		set_immediate=no
-	fi
-}
-
-usage() {
-	program=`basename $0`
-
-	echo >&2 "$program: generate zaptel.conf and zapata.conf"
-	echo >&2 "(version $VERSION_FULL)"
-	echo >&2 "usage:"
-	echo >&2 " $program [-sdv] [-m k|l|g] [-c <country_code>] [-r |-e <base_exten>] "
-	echo >&2 " $program [-sdv] -l"
-	echo >&2 " $program -su"
-	echo >&2 " $program -h (this screen)"
-	echo >&2 ""
-	echo >&2 "Options:"
-	echo >&2 "  -c CODE: set the country code (default: $lc_country)"
-	echo >&2 "  -e NUM: set the base extension number (default: $base_exten)"
-	echo >&2 "  -F: Don't print FXSs in zapata.conf"
-	echo >&2 "  -l: output a list of detected channels instead of zaptel.conf"
-	echo >&2 "  -d: Perform hardware detection"
-	echo >&2 "  -u: Unload zaptel modules"
-	echo >&2 "  -v: verbose"
-	echo >&2 "  -s: Don't fail if asterisk is running. Stop it"
-	echo >&2 "  -r: rapid configuration mode: configure Zaptel FXS channels from "
-	echo >&2 "      existing Rapid extension files. FXOs will all be TRUNK "
-}
-
-# $1: channel number
-print_pattern() {
-	local astbank_type=''
-	OPTIND=1
-	while getopts 'a:' arg
-	do
-		case "$arg" in
-			a) case "$OPTARG" in input|output) astbank_type=$OPTARG;;esac ;;
-		esac
-	done
-	shift $(( $OPTIND-1 ))
-
-
-	local chan=$1
-	local sig=$2 #fxs/fxo
-	local mode=$3
-	local method='ks'
-	if [ "$lc_country" = il ] && [ "$sig" = 'fxs' ]
-	then method=ls
-	fi
-	case "$mode" in
-	zaptel)
-		# sadly, both input ports and output ports go into the same span as 
-		# the FXS ports. Thus we need to separate between them. See also 
-		# the zapata.conf section:
-		if [ "$astbank_type" != '' ]; then echo "# astbanktype: $astbank_type"; fi
-		echo "${sig}$method=$chan" 
-		;;
-	list) echo $chan $sig $astbanktype;;
-	zapata)
-		# zap2amp will rewrite those from zaptel.conf and hints there
-		if [ "$fxsdisable" = 'yes' ] && [ $sig = 'fxo' ]; then return; fi
-			
-		echo "signalling=${sig}_$method"
-		if [ "$sig" = 'fxo' ]
-		then
-			# to preconfigure channel 1's extension to 550, set
-			# chan_1_exten=550
-			# in, e.g, /etc/default/zaptel
-		  var_name=`echo chan_${chan}_exten`
-			cfg_exten=`echo ${!var_name} | tr -d -c 0-9`
-		  var_name=`echo chan_${chan}_vmbox`
-			cfg_vmbox=`echo ${!var_name} | tr -d -c 0-9`
-		  var_name=`echo chan_${chan}_cntxt`
-			cfg_cntxt=`echo ${!var_name} | tr -d -c 0-9`
-			
-			# if option -E was given, get configuration from current extension
-			if [ "$rapid_conf_mode" = 'yes' ]
-			then
-				rap_exten=`echo $rapid_extens |awk "{print \\$$rapid_cur_exten}"`
-				if [ "$rap_exten" != '' ]
-				then
-					rap_cfgfile="$exten_base_dir/$rap_exten.conf"
-					if [ -r "$rap_exten" ]
-					then
-						cfg_exten=$rap_exten
-						# the vmbox is the third parameter to stdexten
-						rap_vmbox=`grep '^[^;].*Macro(stdexten' $rap_exten | cut -d, -f3 \
-							| cut -d')' -f1 | tr -d -c '0-9 at a-zA-Z'`
-						if [ "$rap_vmbox" ]!= '' ; then cfg_vmbox=$rap_vmbox; fi
-					fi
-				fi
-				rapid_cur_exten=$(($rapid_cur_exten + 1))
-			fi
-			
-			if [ "$cfg_exten" = '' ]
-			then # No extension number set for this channel
-				exten=$(($chan+$base_exten))
-			else # use the pre-configured extension number
-				exten=$cfg_exten
-			fi
-			# is there any real need to set 'mailbox=' ?
-			if [ "x$cfg_vmbox" = x ]
-			then # No extension number set for this channel
-				vmbox=$exten
-			else # use the pre-configured extension number
-				vmbox=$cfg_vmbox
-			fi
-			echo "callerid=\"Channel $chan\" <$exten>"
-			echo "mailbox=$exten"
-			if [ "$group_manual" != "yes" ]
-			then 
-				echo "group=$group_phones"
-			fi
-			if [ "$context_manual" != "yes" ]
-			then
-				if [ "$astbank_type" != '' ];
-				then 
-					context_var_name=context_$astbank_type
-					echo context=${!context_var_name}
-				else
-					echo "context=$context_phones"
-				fi
-			fi
-		else # this is an FXO (trunk/phone: FXO signalling)
-		  # we have may have set it. So reset it:
-			echo "callerid=\"\" <0>"
-			echo "mailbox="
-			if [ "$group_manual" != "yes" ]
-			then 
-				echo "group=$group_lines"
-			fi
-			if [ "$context_manual" != "yes" ]
-			then 
-				echo "context=$context_lines"
-			fi
-			if [ "$lc_country" = 'uk' ]
-			then
-			  echo "cidsignalling=v23"
-			  case $line in 
-			  *WCFXO*) echo "cidstart=history";;
-			  *)       echo "cidstart=polarity";; #a TDM400
-			  esac
-			fi
-			echo ";;; line=\"$line\""
-			# if kewlstart is not used, busydetect has to be employed:
-			if [ "$method" = 'ls' ]
-			then echo 'busydetect=yes'
-			else echo 'busydetect=no'
-			fi
-		fi
-
-		if [ "$set_immediate" = 'yes' ]
-		then
-			if [ "$astbank_type" = 'input' ]
-			then echo 'immediate=yes'
-			else echo 'immediate=no'
-			fi
-		fi
-		echo "channel => $chan"
-		echo ""
-
-		# Keep a note of what channels we have identified
-		say "DEBUG: adding to channels list: channel: $chan, sig: $sig"
-		case "$sig" in
-		fxs)
-			echo $chan >$tmp_dir/fxo_$chan
-			say "DEBUG: FXO list now contains: `cat $tmp_dir/fxo_* |xargs`"
-			;;
-		fxo)
-			echo $chan >$tmp_dir/fxs_$chan
-			say "DEBUG: FXS list now contains: `cat $tmp_dir/fxs_* |xargs`"
-			;;
-		esac
-		;;
-	esac
-	
-}
-
-# the number of channels from /proc/zaptel
-# must always print a number as its output.
-count_proc_zap_lines() {
-	# if zaptel is not loaded there are 0 channels:
-	if [ ! -d /proc/zaptel ]; then echo '0'; return; fi
-	
-	(
-		for file in `echo /proc/zaptel/* |grep -v '\*'`
-		do sed -e 1,2d $file # remove the two header lines
-		done
-	) | wc -l # the total number of lines
-}
-
-load_modules() {
-	say "Test Loading modules:"
-	for i in zaptel $ALL_MODULES
-	do
-		if [ "$i" = ztdummy ]; then
-			continue	# No hardware to detect
-		fi
-		lines_before=`count_proc_zap_lines`
-		args="${i}_args"
-		eval "args=\$$args"
-		# a module is worth listing if it:
-		# a. loaded successfully, and
-		# b. added channels lines under /proc/zaptel/*
-		if /sbin/modprobe $i $args 2> /dev/null && \
-			[ $lines_before -lt `count_proc_zap_lines` ] 
-		then
-			probed_modules="$probed_modules $i"
-			say "	ok	$i	$args"
-		else
-			say "	- 	$i	$args"
-		fi
-	done
-}
-
-# recursively unload a module and its dependencies, if possible.
-# where's modprobe -r when you need it?
-# inputs: module to unload.
-# returns: the result from 
-unload_module() {
-	module="$1"
-	line=`lsmod 2>/dev/null | grep "^$1 "`
-	if [ "$line" = '' ]; then return; fi # module was not loaded
-
-	set -- $line
-	# $1: the original module, $2: size, $3: refcount, $4: deps list
-	mods=`echo $4 | tr , ' '`
-	# old versions of xpd_fxs actually depend on xpp, but forget to tell it.
-	# bug has already been fixed but the code will remain here for a while
-	# just in case
-	case "$module" in xpd_*) mods="xpp_usb $mods";; esac
-	for mod in $mods; do
-		# run in a subshell, so it won't step over our vars:
-		(unload_module $mod) 
-		# TODO: the following is probably the error handling we want:
-		# if [ $? != 0 ]; then return 1; fi
-	done
-	rmmod $module
-}
-
-unload_modules() {
-	if
-		pids="$(pgrep asterisk)"
-		[ "$pids" != '' ]
-	then
-		die "Before unloading -- STOP asterisk (pids=$pids)."
-	fi
-	say "Unloading zaptel modules:"
-	unload_module zaptel
-	say ''
-}
-
-# sleep a while until the xpp modules fully register
-wait_for_xpp() {
-	if [ -d /proc/xpp ] && \
-	   [ "`cat /sys/module/xpp/parameters/zap_autoreg`" = 'Y' ]
-	then
-		# wait for the XPDs to register:
-		for i in `seq 10`; do
-			sleep 1
-			if ! grep -q 0 /proc/xpp/*/*/zt_registration 2>/dev/null
-			then
-				# There are either no XPDs or all of them are 
-				# registered. Nothing to do
-				break
-			fi
-		done
-	fi
-}
-
-detect() {
-	unload_modules
-	load_modules
-	modlist="$probed_modules"
-	#for i in $ALL_MODULES
-	#do
-	#	if lsmod | grep "^$i  *" > /dev/null; then
-	#		modlist="$modlist $i"
-	#	fi
-	#done
-	modlist="$(echo $modlist)"		# clean spaces
-	if [ "$do_module_list" = yes ]
-	then
-		say "Updating '${MODLIST_FILE}'"
-		update_module_list "$modlist"
-	fi
-	if echo $modlist | grep -q xpp_usb; then wait_for_xpp; fi
-}
-
-# TODO: kill this function. It is now unreferenced from anywhere.
-check_tdm_sigtype() {
-	chan_num=$1
-	sig_type=$2
-	mode=$3
-	
-	case "$sig_type" in
-	fxs)chan_sig_type=fxo;;
-	fxo)chan_sig_type=fxs;;
-	esac
-
-#	print_pattern $chan_num $chan_sig_type $mode
-	
-  # if you get syntax error from this line, make sure you use 'bash' 
-  # rather than 'sh'
-	$ztcfg_cmd -c <(print_pattern $chan_num $chan_sig_type zaptel) 2>/dev/null  \
-		|| return 1
-	if head -c1 /dev/zap/$chan_num >/dev/null 2>/dev/null
-	then 
-		print_pattern $chan_num $chan_sig_type $mode
-		return 0
-	else
-		return 1
-	fi	
-}
-
-# output a list of extensions that need a channel
-get_rapid_extens() {
-	if [ "$rapid_conf_mode" = 'yes' ]
-	then
-		rapid_extens=`grep -l '^[^;].*Zap/\${CHAN_ZAP_' $exten_base_dir/*.conf 2>/dev/null | \
-			rev | cut -d/ -f1 | cut -d. -f2- | rev | xargs`
-		say "Need to configure extensions: $rapid_extens"
-	fi
-}
-
-genconf() {
-	local mode=$1
-
-	# reset FXO list (global)
-	#say "DEBUG: resetting channels lists"
-	rm -f $tmp_dir/fx{s,o}_*
-
-	if [ "$mode" = 'zapata' ]
-	then
-		rem_char=';'
-	else
-		rem_char='#'
-	fi
-
-	spanlist=`echo /proc/zaptel/* |  grep -v '\*'`
-
-	#if [ "$spanlist" == "" ]; then
-	#	die "No zapata interfaces in /proc/zaptel"
-	#fi
-
-
-	case "$mode" in 
-		zaptel)
-			cat <<EOF
-# Autogenerated by $0 -- do not hand edit
-# Zaptel Configuration File
-#
-# This file is parsed by the Zaptel Configurator, ztcfg
-#
-
-# It must be in the module loading order
-
-EOF
-			;;
-		zapata)
-			cat <<EOF
-; Autogenerated by $0 -- do not hand edit
-; Zaptel Channels Configurations (zapata.conf)
-;
-; This is not intended to be a complete zapata.conf. Rather, it is intended 
-; to be #include-d by /etc/zapata.conf that will include the global settings
-;
-EOF
-		;;
-	esac
-
-	# For each line in the spanlist: see if it represents a channel.
-	# if it does, test that the channel is usable.
-	# we do that by configuring it (using ztcfg with a 1-line config file)
-	# and then trying to read 1 byte from the device file.
-	#
-	# The '<(command)' syntax creates a temporary file whose content is is the
-	# output of 'command'.
-	#
-	# This approach failed with the T1 card we have: the read operation simply 
-	# hung. 
-	#
-	# Another problem with such an approach is how to include an existing 
-	# configuration file. For instance: how to include some default settings.
-	#
-	# Maybe an 'include' directive should be added to zaptel.conf ?
-	#cat $spanlist | 
-	for procfile in $spanlist
-	do
-		span_num=`basename $procfile`
-		# the first line is the title line. It states the model name
-		# the second line is empty
-		title=`head -n 1 $procfile`
-		echo ""
-		# stuff that needs to be remembered accross lines (for PRI support)
-		echo "$rem_char $title"
-		echo '-1'  >$tmp_dir/span_begin
-		echo '-1'  >$tmp_dir/span_end
-		echo '1'   >$tmp_dir/span_timing
-		echo '1'   >$tmp_dir/span_lbo
-		echo ''    >$tmp_dir/span_framing
-		echo 'ami' >$tmp_dir/span_coding
-		echo ''    >$tmp_dir/span_switchtype
-		echo ''    >$tmp_dir/span_signalling
-
-		if echo $title | egrep -q '((quad|octo)BRI PCI ISDN Card.* \[NT\]\ |octoBRI \[NT\] |HFC-S PCI A ISDN.* \[NT\] )'
-		then
-			echo 'nt' >$tmp_dir/span_termtype
-		else 
-			if echo $title | egrep -q '((quad|octo)BRI PCI ISDN Card.* \[TE\]\ |octoBRI \[TE\] |HFC-S PCI A ISDN.* \[TE\] )'
-			then
-				echo 'te' >$tmp_dir/span_termtype
-			fi
-		fi
-		# The rest of the lines are per-channel lines
-		sed -e 1,2d $procfile | \
-		while read line
-		do 
-			# in case this is a real channel. 
-			chan_num=`echo $line |awk '{print $1}'`
-			case "$line" in
-			*WCTDM/*) 
-				# this can be either FXS or FXO
-				maybe_fxs=0
-				maybe_fxo=0
-				$ztcfg_cmd -c <(print_pattern $chan_num fxo zaptel) &>/dev/null && maybe_fxs=1
-				$ztcfg_cmd -c <(print_pattern $chan_num fxs zaptel) &>/dev/null && maybe_fxo=1
-				if [ $maybe_fxs = 1 ] && [ $maybe_fxo = 1 ]
-				then 
-				  # An installed module won't accept both FXS and FXO signalling types:
-					# this is an empty slot.
-				  echo "$rem_char channel $chan_num, WCTDM, no module."
-					continue
-				fi
-				
-				if [ $maybe_fxs = 1 ]; then print_pattern $chan_num fxo $mode; fi
-				if [ $maybe_fxo = 1 ]; then print_pattern $chan_num fxs $mode; fi
-				;;
-			*WCFXO/*) 
-				print_pattern $chan_num fxs $mode || \
-				echo "$rem_char channel $chan_num, WCFXO, inactive."
-				;;
-			*XPP_FXO/*)
-				print_pattern $chan_num fxs $mode
-				;;
-			*XPP_FXS/*)
-				print_pattern $chan_num fxo $mode
-				;;
-			*XPP_OUT/*)
-				print_pattern -a output $chan_num fxo $mode
-				;;
-			*XPP_IN/*)
-				print_pattern -a input $chan_num fxo $mode
-				;;
-			*ZTHFC*/*|*ztqoz*/*|*ztgsm/*|*WCT1/*) # should also be used for other PRI channels
-				if [ "`cat $tmp_dir/span_begin`" = "-1" ]
-				then
-					echo $chan_num      >$tmp_dir/span_begin
-					echo $span_num      >$tmp_dir/span_num
-					case "$line" in
-					*ZTHFC*/*|*ztqoz*/*)
-						echo 'ccs'          >$tmp_dir/span_framing
-						echo 'euroisdn'     >$tmp_dir/span_switchtype
-						if [ "`cat $tmp_dir/span_termtype`" = 'nt' 2>/dev/null ]
-						then
-							echo 'bri_net' >$tmp_dir/span_signalling
-						else
-							echo 'bri_cpe' >$tmp_dir/span_signalling
-						fi
-						;;
-					*ztgsm*/*)
-						echo 'ccs'          >$tmp_dir/span_framing
-            # what switch type? Any meaning to it?
-						echo 'gsm'          >$tmp_dir/span_signalling
-						;;
-					*WCT1/*)
-						echo 'esf'       >$tmp_dir/span_framing
-						echo 'b8zs'      >$tmp_dir/span_coding
-						echo 'national'  >$tmp_dir/span_switchtype
-						echo 'pri_cpe'   >$tmp_dir/span_signalling
-						# an example of country-specific setup. This is probably not accurate
-						# Contributions are welcome
-						case "$lc_country" in 
-						nl)
-							# (Just an example for per-country info)
-							echo 'ami'       >$tmp_dir/span_framing
-							echo 'ccs'      >$tmp_dir/span_coding
-							#echo 'crc4'      >$tmp_dir/span_yellow
-							#echo 'euroisdn'  >$tmp_dir/span_switchtype
-							#echo 'pri_cpe'   >$tmp_dir/span_signalling
-							;;
-						il)
-							echo 'hdb3'      >$tmp_dir/span_framing
-							echo 'ccs'       >$tmp_dir/span_coding
-							echo 'crc4'      >$tmp_dir/span_yellow
-							echo 'euroisdn'  >$tmp_dir/span_switchtype
-						esac
-					;;
-					esac
-				fi
-				# span_lastd is always the one before last 
-				# channel. span_bchan is the last:
-				echo $chan_num      >$tmp_dir/span_end
-				;;
-			'') ;;		# Empty line (after span header)
-			*) echo "$rem_char ??: $line";;
-			esac
-		done
-		if [ "`cat $tmp_dir/span_begin`" != -1 ]
-		then # write PRI span ocnfig:
-			# read files to variables:
-			for suffix in num begin end timing lbo framing \
-				coding switchtype signalling yellow termtype
-			do
-				eval span_$suffix=`cat $tmp_dir/span_$suffix 2>/dev/null`
-			done
-			if [ "$span_yellow" != '' ]; then span_yellow=",$span_yellow"; fi
-			# exactly the same logic is used in asterisk's chan_zap.c.
-			# also not that $(( )) is bash-specific
-			case "$((1+ $span_end - $span_begin))" in
-			2|3|24) #ztgsm, BRI or T1
-			  dchan=$span_end
-				bchans="$span_begin-$(($span_end-1))"
-				;;
-			31) #E1
-			  dchan="$(($span_begin+15))"
-				bchans="$span_begin-$(($span_begin+14)),$(($span_begin+16))-$span_end"
-				;;
-			esac
-			case "$mode" in
-			zaptel)
-				echo span=$span_num,$span_timing,$span_lbo,$span_framing,$span_coding$span_yellow
-				if [ "$span_termtype" != '' ]
-				then echo "# termtype: $span_termtype"
-				fi
-				echo bchan=$bchans
-				echo dchan=$dchan
-				;;
-			zapata)
-				if [ "$span_termtype" != '' ]
-				then
-					# an ISDN card's span that we know if it is in NT mode or TE mode.
-					# NT is the same as FXS for us and TE is the same as FXO
-					if [ "$span_termtype" = 'nt' ]
-					then
-						#echo "callerid=\"Channel $chan\" <$exten>"
-						#echo "mailbox=$exten"
-						if [ "$group_manual" != "yes" ]
-						then 
-							echo "group=$group_phones"
-						fi
-						if [ "$context_manual" != "yes" ]
-						then 
-							echo "context=$context_phones"
-						fi
-					else # we have may have set it. So reset it:
-						#echo "callerid=\"\" <0>"
-						#echo "mailbox="
-						if [ "$group_manual" != "yes" ]
-						then 
-							echo "group=$group_lines"
-						fi
-						if [ "$context_manual" != "yes" ]
-						then 
-							echo "context=$context_lines"
-						fi
-					fi
-				fi
-				echo "switchtype = $span_switchtype"
-				echo "signalling = $span_signalling"
-				echo "channel => $bchans"
-				;;
-			list)
-				echo BRI/PRI: chans: $bchans, control: $dchan
-				;;
-			esac
-		fi
-	done
-
-	if [ "$mode" = 'zaptel' ]
-	then
-		cat <<EOF
-
-# Global data
-
-EOF
-		echo "loadzone	= $loadzone"
-		echo "defaultzone	= $defaultzone"
-	fi
-	
-	if [ "$mode" = 'zapata' ] || [ "$mode" = 'list' ]
-	then 
-		update_extensions_defs 
-	fi
-}
-
-while getopts 'c:de:Fhlm:Mrsuv' arg
-do
-	case "$arg" in
-		e) # guarantee that it is a number:
-			new_base_exten=`echo $OPTARG | tr -d -c 0-9`
-			if [ "x$new_base_exten" != x ]; then base_exten=$new_base_exten; fi
-			;;
-		c) lc_country=`echo $OPTARG | tr -d -c a-z` ;;
-		d) do_detect=yes ;;
-		F) fxsdisable=yes;;
-		u) do_unload=yes ;;
-		v) verbose=yes ;;
-		l) mode='list' ;;
-		M) do_module_list=yes; do_detect=yes ;;
-		s) force_stop_ast=yes ;;
-		r) 
-			rapid_conf_mode=yes 
-			;;
-		h) usage; exit 0;;
-		*) echo >&2 "unknown parameter -$arg, Aborting"; usage; exit 1;;
-	esac
-done
-shift $(( $OPTIND-1 ))
-if [ $# != 0 ]; then
-	echo >&2 "$0: too many parameters"
-	usage
-	exit 1
-fi
-
-tmp_dir=`mktemp -d -t` || \
-	die "$0: failed to create temporary directory. Aborting"
-
-
-case "$lc_country" in
-	# the list was generated from the source of zaptel:
-	#grep '{.*[0-9]\+,.*"[a-z][a-z]"' zonedata.c | cut -d'"' -f 2 | xargs |tr ' ' '|'
-	us|au|fr|nl|uk|fi|es|jp|no|at|nz|it|gr|tw|cl|se|be|sg|il|br|hu|lt|pl|za|pt|ee|mx|in|de|ch|dk|cz|cn):;;
-	*) 
-		lc_country=us
-		echo >&2 "unknown country-code $lc_country, defaulting to \"us\""
-		;;
-esac
-# any reason for loadzone and defaultzone to be different? If so, this is
-# the place to make that difference
-loadzone=$lc_country
-defaultzone=$loadzone
-
-# make sure asterisk is not in our way
-if [ "$force_stop_ast" = 'yes' ]
-then
-  /etc/init.d/asterisk stop 1>&2
-else
-  # if asterisk is running and we wanted to detect modules
-	# or simply to unload modules, asterisk needs to go away.
-	if ( [ "$do_unload" = yes ] || [ "$do_detect" = yes ] ) && \
-	  pidof asterisk >/dev/null 
-	then
-	  echo >&2 "Asterisk is already running. Configuration left untouched"
-		echo >&2 "You can use the option -s to shut down Asterisk for the"
-		echo >&2 "duration of the detection."
-		exit 1
-	fi
-fi
-
-if [ "$do_unload" = yes ]
-then
-	unload_modules
-	exit
-fi
-
-if [ "$do_detect" = yes ]
-then
-	detect
-fi
-
-if [ "$mode" = list ]; then
-	genconf list
-else
-	check_for_astribank
-	get_rapid_extens
-	say "Generating '${ZAPCONF_FILE}'"
-	mv "${ZAPCONF_FILE}" "${ZAPCONF_FILE}.bak"
-	genconf zaptel > "${ZAPCONF_FILE}"
-	say "Generating '${ZAPATA_FILE}'"
-	mv "${ZAPATA_FILE}" "${ZAPATA_FILE}.bak"
-	genconf zapata > "${ZAPATA_FILE}"
-	if [ "$set_immediate" = 'yes' ] && [ -x /etc/init.d/zaptel ]
-	then /etc/init.d/zaptel start
-	else run_ztcfg
-	fi
-fi
-
-if [ "$tmp_dir" != '' ]
-then
-  rm -rf "$tmp_dir"
-fi
-
-if [ "$force_stop_ast" = 'yes' ]
-then
-  if [ -x /etc/init.d/asterisk ]
-  then
-    /etc/init.d/asterisk start 1>&2
-  fi
-fi
-
-# if in verbose mode: verify that asterisk is running
-if [ "$verbose" != 'no' ] && [ "$force_stop_ast" = 'yes' ]
-	then
-	say "Checking channels configured in Asterisk:"
-	sleep 1 # give it some time. This is enough on our simple test server
-	if [ -x ast-cmd ]
-	then
-		ast-cmd cmd "zap show channels"
-	else
-		asterisk -rx "zap show channels"
-	fi
-fi
-
-# vim:ts=2:

Deleted: zaptel/trunk/debian/genzaptelconf.8
===================================================================
--- zaptel/trunk/debian/genzaptelconf.8	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/genzaptelconf.8	2006-06-29 12:50:04 UTC (rev 1980)
@@ -1,258 +0,0 @@
-.TH GENZAPTELCONF 8 "July 18th, 2005" "Xorcom Rapid Asterisk" "Linux Programmer's Manual"
-.SH "NAME" 
-.B genzaptelconf 
--- generates zaptel configuration (TDM adaptors)
-.SH SYNOPSIS
-.PP 
-.B genzaptelconf 
-[-sdv] [-c <country_code>] [-r |-e <base_exten>] [ -F ]
-
-.B genzaptelconf 
-[-sdv] -l -- only list to standard output
-   
-.B genzaptelconf 
--su -- only unload zaptel modules
-
-.B genzaptelconf 
--h -- Help screen
-    
-.SH DESCRIPTION
-.B genzaptelconf 
-is a script to detect zaptel devices (currently mostly TDM cards are 
-supported). It generates both 
-.I /etc/zaptel.conf
-and 
-.I /etc/asterisk/zapata-channels.conf
-
-.I PRI
-and 
-.I BRI
-(HFC, with ZapBRI) cards are basically identified as well. However the span
-configiration is a default that I only hope is sane. Looking for feedback
-
-.SH OPTIONS
-.B -c
-.I country_code
-.RS
-A two-letter country code. Sets the country-code for the zonezone 
-entries in 
-.I zaptel.conf
-, The default is the value of
-.I lc_country
-from 
-.I /etc/default/zaptel
-and failing that, "us".
-.RE
-
-.B -d
-.RS
-Also try to detect modules. Unloads all zaptel modules and loads them
-one by one. Considers a module useful if it loaded successfully and if 
-loading it has generated at least one zapata channel.
-
-The list of detected modules is written as the value of 
-.I ZAPTEL_MODS
-in 
-.I /etc/default/zaptel
-.RE
-
-.B -e
-.I base_exten_num
-.RS
-Configure channel 
-.I i 
-as extension 
-.I exten_num
-+
-.I i 
-. This is mostly for the caller-id values. Crude, but may be good enough. 
-See also
-.I -r
-.RE
-
-.B -F
-.RS
-Disable writing FXS extensions in zapata.conf
-.RE
-
-.B -l
-.RS
-Only list deceted channels and their signalling. Don't write 
-configuration files. Note, however that 
-.I -ld
-will still rewrite the modules line in 
-.I /etc/default/zaptel
-(see 
-.I -d
-above).
-.RE
-
-.B -M
-.RS
-Update
-.I /etc/modules
-with a list of our modules, thereby
-triggers their loading via modprobe on the next boot.
-
-This triggers the
-.I -d
-option as well.
-.RE
-
-.B -r
-.RS
-Try to guess a useful
-.I zapata-channels
-configuration for Xorcom Rapid .
-.RE
-
-.B -s
-.RS
-Stop asterisk for the duration of the test. The detection will only
-work if nobody uses the zaptel channels: 
-
-* To allow unloading of modules
-
-* to allow reading configuration files.
-
-By default the script will check if asterisk is running and alert if so.
-This option tells the script to stop asterisk (if it was running) and to 
-try to start it after the end of the test.
-.RE
-
-.B -v
-.RS
-Be verbose. lists the detected modules if 
-.I -d
-is used. Lists detected channls. In the end tries to connect to asterisk
-to get a list of configured zaptel channels.
-.RE
-.SH FILES
-.I /etc/zaptel.conf
-.RS
-The configuration file used by 
-.I ztcfg
-to configure zaptel devices. re-written by 
-.I genzaptelconf
-.RE
-
-.I /etc/zaptel.conf.bak
-.RS
-When 
-.I zaptel.conf
-The original zaptel.conf
-.RE
-
-.I /etc/asterisk/zapata.conf
-.RS
-The configuration file of Asterisk's 
-.I chan_zap.
-Not modified directly by 
-.I genzaptelconf.
-If you want genzaptelconf's setting to take effect, add the following 
-line at the end of 
-.I zapata.conf:
-.RS
-#include "zapata-channels.conf"
-.RE
-.RE
-
-.I /etc/asterisk/zapata-channels.conf
-.RS
-This is the snippet of 
-.I chan_zap
-configuration file that 
-.I genzaptelconf generates.
-.RE
-
-.I /etc/asterisk/zapata-channels.conf.bak
-.RS
-The backup copy of 
-.I zapata-channels.conf
-.RE
-
-.I /etc/default/zaptel
-.RS
-This file holds configuration for both 
-.I genzaptelconf
-and
-.I /etc/init.d/zaptel .
-It is sourced by both scripts and can thus be used to override settings 
-of variables from those scripts.
-Some of the variables that can be set in /etc/default/zaptel and affect 
-genzaptelconf:
-
-.I lc_country
-.RS
-The default country. Can be also overriden by the option -c
-.RE
-
-.I base_exten
-.RS
-The base number used for automatic numbering
-.RE
-
-.I context_manual
-.RS
-If set to 'yes', no context changes are made in zapata-channels.conf
-.RE
-
-.I context_lines
-.RS
-The context into which calls will go from zaptel trunks.
-.RE
-
-.I context_phones
-.RS
-The context into which calls will go from zaptel phones.
-.RE
-
-.I context_manual
-.RS
-If set to 'yes', no group settings are made in zapata-channels.conf
-.RE
-
-.I group_lines
-.RS
-The group number for zaptel trunks.
-.RE
-
-.I group_phones
-.RS
-The group number for zaptel phones.
-.RE
-
-.I ALL_MODULES
-.RS
-modules list. Used for unloading and modules detection. The order of modules
-is the same for both.
-.RE
-.RE
-
-.I /etc/modules
-.RS
-A debian-specific list of kernel modules to be loaded by modprobe at 
-boot time. When the option 
-.I -d
-(detect) is used, genzaptelconf will write in this file zaptel modules
-to be loaded. If you want to use a different file, set 
-.I MOD_FILELIST
-.RE
-
-.I /etc/modules.bak
-.RS
-The backup copy of 
-.I /etc/modules
-.RE
-
-.SH "SEE ALSO" 
-ztcfg(8) asterisk(8). 
-
-.SH "AUTHOR" 
-This manual page was written by Tzafrir Cohen <tzafrir.cohen at xorcom.com> 
-Permission is granted to copy, distribute and/or modify this document under 
-the terms of the GNU General Public License, Version 2 any  
-later version published by the Free Software Foundation. 
-
-On Debian systems, the complete text of the GNU General Public 
-License can be found in /usr/share/common-licenses/GPL. 

Modified: zaptel/trunk/debian/modulestest
===================================================================
--- zaptel/trunk/debian/modulestest	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/modulestest	2006-06-29 12:50:04 UTC (rev 1980)
@@ -21,7 +21,7 @@
 # .changes files even without signing
 MODASS="env SIGNCHANGES=1 DEBSIGNCOMMAND=not_an_executable m-a"
 
-while getopts ah:s:tx arg
+while getopts ah:s:txX arg
 do
   case "$arg" in
     a) # All of the kernel-headers packages installed:
@@ -37,7 +37,7 @@
       # it (Tzafrir)
       for conf in 386 686 686-smp k7 k7-smp
       do
-        for ver in 2.4.27-2 2.6.8-2
+        for ver in 2.4.27-2 2.4.27-3 2.6.8-2 2.6.8-3
         do
           KERNEL_HEADERS=$KERNEL_HEADERS,$ver-$conf
         done
@@ -52,6 +52,17 @@
       # remove the leading ',':
       KERNEL_HEADERS=${KERNEL_HEADERS#,}
       ;;
+    X)
+      for conf in 386 686 k7 rapidbox2
+      do
+        for ver in 2.6.12-1
+        do
+          KERNEL_HEADERS=$KERNEL_HEADERS,$ver-$conf
+        done
+      done
+      # remove the leading ',':
+      KERNEL_HEADERS=${KERNEL_HEADERS#,}
+      ;;
   esac
 done
 shift $(( $OPTIND-1 ))

Modified: zaptel/trunk/debian/patches/00list
===================================================================
--- zaptel/trunk/debian/patches/00list	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/patches/00list	2006-06-29 12:50:04 UTC (rev 1980)
@@ -1,6 +1,8 @@
+#Makefile_deps_kern
 Makefile_deps_utils
 Makefile_uname
 Makefile_targets
+Makefile_kbuild
 # probably depends on previus Makefile_* patches (specifically: Makefile_targets)
 Makefile_bristuff
 # touches the Makefile as well:

Modified: zaptel/trunk/debian/patches/Makefile_bristuff.dpatch
===================================================================
--- zaptel/trunk/debian/patches/Makefile_bristuff.dpatch	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/patches/Makefile_bristuff.dpatch	2006-06-29 12:50:04 UTC (rev 1980)
@@ -3,18 +3,20 @@
 ##
 ## All lines beginning with `## DP:' are a description of the patch.
 ## DP: A bit of extra Makefile changes to build bristuff
+## DP: Also adds two unrelated modules: opvxa1200
 
 @DPATCH@
 diff -urNad zaptel-1.2.5/Makefile /tmp/dpep.INsmJ5/zaptel-1.2.5/Makefile
 --- zaptel-1.2.5/Makefile	2006-05-09 20:59:48.000000000 +0300
 +++ /tmp/dpep.INsmJ5/zaptel-1.2.5/Makefile	2006-05-09 21:00:34.000000000 +0300
-@@ -144,6 +144,9 @@
+@@ -144,6 +144,10 @@
  MODULES:=zaptel tor2 torisa wcusb wcfxo wctdm wctdm24xxp \
  	 ztdynamic ztd-eth wct1xxp wct4xxp wcte11xp pciradio \
           ztd-loc # ztdummy
 +
 +BRIMODS=cwain qozap zaphfc ztgsm
 +MODULES+=$(BRIMODS)
++MODULES+=opvxa1200
  #MODULES+=wcfxsusb
  # build ztdummy by default for 2.6 kernels
  ifeq (${BUILDVER},linux26)

Added: zaptel/trunk/debian/patches/xpp_xorcom_debian.dpatch
===================================================================
--- zaptel/trunk/debian/patches/xpp_xorcom_debian.dpatch	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/patches/xpp_xorcom_debian.dpatch	2006-06-29 12:50:04 UTC (rev 1980)
@@ -0,0 +1,13283 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## xpp_xorcom_debian.dpatch by Tzafrir Cohen <tzafrir.cohen at xorcom.com>
+##
+## DP: Up-to-date zaptel drivers for the Xorcom Astribank and friends.
+## DP: Revision r1462 (branches/RELEASE-1.1.0)
+
+## DP: This version omites the .version file and the firmware files.
+
+ at DPATCH@
+diff -uNr -x .svn -x debian zaptel-1.2.6/.version zaptel-xpp-XJ8JZx_dist/.version
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/card_fxo.c zaptel-xpp-XJ8JZx_dist/xpp/card_fxo.c
+--- zaptel-1.2.6/xpp/card_fxo.c	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/card_fxo.c	2006-06-22 11:31:28.427315000 +0300
+@@ -0,0 +1,926 @@
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2006, Xorcom
++ *
++ * 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/init.h>
++#include <linux/module.h>
++#include <linux/fs.h>
++#include <linux/delay.h>
++#include <version.h>		/* For zaptel version */
++#include "xpd.h"
++#include "xproto.h"
++#include "xpp_zap.h"
++#include "card_fxo.h"
++#include "zap_debug.h"
++
++static const char rcsid[] = "$Id: card_fxo.c 1431 2006-06-22 08:31:28Z oron $";
++
++DEF_PARM(int, print_dbg, 0, "Print DBG statements");	/* must be before zap_debug.h */
++
++enum fxo_leds {
++	LED_GREEN,
++};
++
++#define	NUM_LEDS		1
++#define	DELAY_UNTIL_DIALTONE	3000
++
++/*---------------- FXO Protocol Commands ----------------------------------*/
++
++/* 0x0F */ DECLARE_CMD(FXO, CHAN_ENABLE, xpp_line_t lines, bool on);
++/* 0x0F */ DECLARE_CMD(FXO, CHAN_CID, int pos);
++/* 0x0F */ DECLARE_CMD(FXO, RING, int pos, bool on);
++/* 0x0F */ DECLARE_CMD(FXO, SETHOOK, int pos, bool offhook);
++/* 0x0F */ DECLARE_CMD(FXO, RELAY_OUT, byte which, bool on);
++/* 0x0F */ DECLARE_CMD(FXO, DAA_INIT);
++/* 0x0F */ DECLARE_CMD(FXO, DAA_QUERY, int pos, byte reg_num);
++
++static bool fxo_packet_is_valid(xpacket_t *pack);
++static void fxo_packet_dump(xpacket_t *pack);
++static int proc_fxo_info_read(char *page, char **start, off_t off, int count, int *eof, void *data);
++static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data);
++static int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data);
++
++#define	S_(s,l,...)					\
++	{						\
++		.lines = s,				\
++		{					\
++			.len = l,			\
++			.data = { __VA_ARGS__ },	\
++		}					\
++	}
++struct slic_init_data {
++	xpp_line_t	lines;
++	slic_data_t	slic_data;
++} slic_init_data[] = {
++#ifdef	OLD_CARD
++#include "init_data_4_19.inc"
++#else
++#include "init_data_4_20.inc"
++#endif
++};
++#undef	S_
++
++#define	PROC_DAA_FNAME		"slics"
++#define	PROC_FXO_INFO_FNAME	"fxo_info"
++
++struct FXO_priv_data {
++	struct proc_dir_entry		*xpd_slic;
++	struct proc_dir_entry		*fxo_info;
++	slic_reply_t			requested_reply;
++	slic_reply_t			last_reply;
++	xpp_line_t			battery;
++	xpp_line_t			ledstate[NUM_LEDS];	/* 0 - OFF, 1 - ON */
++	int				blinking[NUM_LEDS][CHANNELS_PERXPD];
++};
++
++/*---------------- FXO: Static functions ----------------------------------*/
++
++#define	IS_BLINKING(priv,pos,color)	((priv)->blinking[color][pos] != 0)
++#define	DO_BLINK(priv,pos,color,val)	((priv)->blinking[color][pos] = (val))
++
++/*
++ * LED control is done via DAA register 0x20
++ */
++static int do_led(xpd_t *xpd, lineno_t pos, byte which, bool on)
++{
++	int			ret = 0;
++	xpacket_t		*pack;
++	slic_cmd_t		*sc;
++	int			len;
++	struct FXO_priv_data	*priv;
++	xpp_line_t		lines;
++	xbus_t			*xbus;
++
++	BUG_ON(!xpd);
++	xbus = xpd->xbus;
++	priv = xpd->priv;
++	which = which % NUM_LEDS;
++	if(IS_SET(xpd->digital_outputs, pos) || IS_SET(xpd->digital_inputs, pos))
++		goto out;
++	if(pos == ALL_LINES) {
++		lines = ~0;
++		priv->ledstate[which] = (on) ? ~0 : 0;
++	} else {
++		lines = BIT(pos);
++		if(on) {
++			BIT_SET(priv->ledstate[which], pos);
++		} else {
++			BIT_CLR(priv->ledstate[which], pos);
++		}
++	}
++	if(!(lines & xpd->enabled_chans))	// Ignore disabled channels
++		goto out;
++	DBG("%s/%s: LED: lines=0x%04X which=%d -- %s\n", xbus->busname, xpd->xpdname, lines, which, (on) ? "on" : "off");
++	XPACKET_NEW(pack, xbus, FXO, DAA_WRITE, xpd->id);
++	sc = &RPACKET_FIELD(pack, FXO, DAA_WRITE, slic_cmd);
++	len = slic_cmd_direct_write(sc, lines, 0x20, on);
++	// DBG("LED pack: line=%d %s\n", i, (on)?"on":"off");
++	pack->datalen = len;
++	packet_send(xbus, pack);
++out:
++	return ret;
++}
++
++static void handle_fxo_leds(xpd_t *xpd)
++{
++	int		i;
++	unsigned long	flags;
++	const enum fxo_leds	color = LED_GREEN;
++	unsigned int	timer_count;
++	struct FXO_priv_data	*priv;
++
++	BUG_ON(!xpd);
++	spin_lock_irqsave(&xpd->lock, flags);
++	priv = xpd->priv;
++	timer_count = xpd->timer_count;
++	for_each_enabled_line(xpd, i) {
++		if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i))
++			continue;
++		if(IS_BLINKING(priv,i,color)) {
++			// led state is toggled
++			if((timer_count % LED_BLINK_PERIOD) == 0) {
++				DBG("%s/%s/%d: led_state=%s\n", xpd->xbus->busname, xpd->xpdname, i,
++						(IS_SET(priv->ledstate[color], i))?"ON":"OFF");
++				if(!IS_SET(priv->ledstate[color], i)) {
++					do_led(xpd, i, color, 1);
++				} else {
++					do_led(xpd, i, color, 0);
++				}
++			}
++		}
++	}
++	spin_unlock_irqrestore(&xpd->lock, flags);
++}
++
++static void do_sethook(xpd_t *xpd, int pos, bool offhook)
++{
++	unsigned long	flags;
++
++	BUG_ON(!xpd);
++	BUG_ON(xpd->direction == TO_PHONE);		// We can SETHOOK state only on PSTN
++	spin_lock_irqsave(&xpd->lock, flags);
++	xpd->ringing[pos] = 0;				// No more rings
++	CALL_XMETHOD(SETHOOK, xpd->xbus, xpd, pos, offhook);
++	if(offhook) {
++		BIT_SET(xpd->hookstate, pos);
++	} else {
++		BIT_CLR(xpd->hookstate, pos);
++		xpd->delay_until_dialtone[pos] = 0;
++	}
++	spin_unlock_irqrestore(&xpd->lock, flags);
++	if(offhook)
++		wake_up_interruptible(&xpd->txstateq[pos]);
++}
++
++/*---------------- FXO: Methods -------------------------------------------*/
++
++static xpd_t *FXO_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision)
++{
++	xpd_t		*xpd = NULL;
++	int		channels = min(8, CHANNELS_PERXPD);
++
++	xpd = xpd_alloc(sizeof(struct FXO_priv_data), xbus, xpd_num, proto_table, channels, revision);
++	if(!xpd)
++		return NULL;
++	xpd->direction = TO_PSTN;
++	xpd->revision = revision;
++	return xpd;
++}
++
++static void clean_proc(xbus_t *xbus, xpd_t *xpd)
++{
++	struct FXO_priv_data	*priv;
++
++	BUG_ON(!xpd);
++	priv = xpd->priv;
++	DBG("%s/%s\n", xbus->busname, xpd->xpdname);
++#ifdef	CONFIG_PROC_FS
++	if(priv->xpd_slic) {
++		DBG("Removing xpd DAA file %s/%s\n", xbus->busname, xpd->xpdname);
++		remove_proc_entry(PROC_DAA_FNAME, xpd->proc_xpd_dir);
++	}
++	if(priv->fxo_info) {
++		DBG("Removing xpd FXO_INFO file %s/%s\n", xbus->busname, xpd->xpdname);
++		remove_proc_entry(PROC_FXO_INFO_FNAME, xpd->proc_xpd_dir);
++	}
++#endif
++}
++
++static int FXO_card_init(xbus_t *xbus, xpd_t *xpd)
++{
++	struct FXO_priv_data	*priv;
++	int			ret = 0;
++
++	BUG_ON(!xpd);
++	priv = xpd->priv;
++#ifdef	CONFIG_PROC_FS
++	DBG("Creating FXO_INFO file for %s/%s\n", xbus->busname, xpd->xpdname);
++	priv->fxo_info = create_proc_read_entry(PROC_FXO_INFO_FNAME, 0444, xpd->proc_xpd_dir, proc_fxo_info_read, xpd);
++	if(!priv->fxo_info) {
++		ERR("Failed to create proc '%s' for %s/%s\n", PROC_FXO_INFO_FNAME, xbus->busname, xpd->xpdname);
++		ret = -ENOENT;
++		goto out;
++	}
++	DBG("Creating DAAs file for %s/%s\n", xbus->busname, xpd->xpdname);
++	priv->xpd_slic = create_proc_entry(PROC_DAA_FNAME, 0644, xpd->proc_xpd_dir);
++	if(!priv->xpd_slic) {
++		ERR("Failed to create proc file for DAAs of %s/%s\n", xbus->busname, xpd->xpdname);
++		goto out;
++	}
++	priv->xpd_slic->write_proc = proc_xpd_slic_write;
++	priv->xpd_slic->read_proc = proc_xpd_slic_read;
++	priv->xpd_slic->data = xpd;
++#endif
++#ifdef HARD_CODED_INIT
++	CALL_PROTO(FXO, DAA_INIT, xbus, xpd);
++#else
++	ret = run_initialize_registers(xpd);
++	if(ret < 0)
++		goto out;
++#endif
++	if(xpd->direction == TO_PSTN) {
++		int	i;
++
++		// Hanghup all lines
++		for_each_enabled_line(xpd, i) {
++			init_waitqueue_head(&xpd->txstateq[i]);
++			do_sethook(xpd, i, 0);
++		}
++	}
++out:
++	if(ret < 0) {
++		clean_proc(xbus, xpd);
++		ERR("%s/%s: Failed initializing registers (%d)\n", xbus->busname, xpd->xpdname, ret);
++	} else {
++		DBG("done: %s/%s\n", xbus->busname, xpd->xpdname);
++	}
++	return ret;
++}
++
++static int FXO_card_remove(xbus_t *xbus, xpd_t *xpd)
++{
++	struct FXO_priv_data	*priv;
++
++	BUG_ON(!xpd);
++	priv = xpd->priv;
++	DBG("%s/%s\n", xbus->busname, xpd->xpdname);
++	clean_proc(xbus, xpd);
++	return 0;
++}
++
++static int FXO_card_zaptel_registration(xpd_t *xpd, bool on)
++{
++	xbus_t			*xbus;
++	struct FXO_priv_data	*priv;
++	unsigned long		flags;
++	int			i;
++
++	BUG_ON(!xpd);
++	xbus = xpd->xbus;
++	BUG_ON(!xbus);
++	priv = xpd->priv;
++	BUG_ON(!priv);
++	DBG("%s/%s (%d)\n", xbus->busname, xpd->xpdname, on);
++	if(on) {
++		for_each_line(xpd, i) {
++			spin_lock_irqsave(&xpd->lock, flags);
++			do_led(xpd, i, LED_GREEN, LED_ON);
++			spin_unlock_irqrestore(&xpd->lock, flags);
++			mdelay(50);
++		}
++		for_each_line(xpd, i) {
++			spin_lock_irqsave(&xpd->lock, flags);
++			do_led(xpd, i, LED_GREEN, LED_OFF);
++			spin_unlock_irqrestore(&xpd->lock, flags);
++			mdelay(50);
++		}
++	} else {
++		for_each_line(xpd, i) {
++			spin_lock_irqsave(&xpd->lock, flags);
++			do_led(xpd, i, LED_GREEN, LED_ON);
++			spin_unlock_irqrestore(&xpd->lock, flags);
++			mdelay(100);
++			spin_lock_irqsave(&xpd->lock, flags);
++			do_led(xpd, i, LED_GREEN, LED_OFF);
++			spin_unlock_irqrestore(&xpd->lock, flags);
++		}
++	}
++	return 0;
++}
++
++#ifdef WITH_RBS
++int FXO_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
++{
++	DBG("%s/%s/%d: %s\n", xbus->busname, xpd->xpdname, pos, txsig2str(txsig));
++	BUG_ON(xpd->direction != TO_PSTN);
++	/* XXX Enable hooksig for FXO XXX */
++	switch(txsig) {
++		case ZT_TXSIG_START:
++			break;
++		case ZT_TXSIG_OFFHOOK:
++			do_sethook(xpd, pos, 1);
++			break;
++		case ZT_TXSIG_ONHOOK:
++			do_sethook(xpd, pos, 0);
++			break;
++		default:
++			NOTICE("Can't set tx state to %s (%d)\n", txsig2str(txsig), txsig);
++			return -EINVAL;
++	}
++	return 0;
++}
++
++#else
++int FXO_card_sethook(xbus_t *xbus, xpd_t *xpd, int pos, int hookstate)
++{
++	int	ret = 0;
++
++	DBG("%s/%s/%d: %s\n", xbus->busname, xpd->xpdname, pos, hookstate2str(hookstate));
++	switch(hookstate) {
++		/* On-hook, off-hook: The PBX is playing a phone on an FXO line. 
++		 * Can be ignored for an FXS line
++		 */
++		case ZT_ONHOOK:
++			do_sethook(xpd, pos, 0);
++			break;
++		case ZT_START:
++			DBG("%s/%s/%d: fall through ZT_OFFHOOK\n", xbus->busname, xpd->xpdname, pos);
++			xpd->delay_until_dialtone[pos] = DELAY_UNTIL_DIALTONE;
++			// Fall through
++		case ZT_OFFHOOK:
++			do_sethook(xpd, pos, 1);
++			wait_event_interruptible(xpd->txstateq[pos], xpd->delay_until_dialtone[pos] <= 0);
++			break;
++		case ZT_WINK:
++			WARN("No code yet\n");
++			break;
++		case ZT_FLASH:
++			WARN("No code yet\n");
++			break;
++		case ZT_RING:
++			DBG("%s/%s/%d: (ringing[%d]=%d)\n", xbus->busname, xpd->xpdname, pos, pos, xpd->ringing[pos]);
++			break;
++		case ZT_RINGOFF:
++			WARN("No code yet\n");
++			break;
++	}
++	return ret;
++}
++#endif
++
++static void poll_battery(xbus_t *xbus, xpd_t *xpd)
++{
++	int	i;
++
++	for_each_enabled_line(xpd, i) {
++		CALL_PROTO(FXO, DAA_QUERY, xbus, xpd, i, DAA_VBAT_REGISTER);
++	}
++}
++
++
++static int FXO_card_tick(xbus_t *xbus, xpd_t *xpd)
++{
++	static int		rate_limit = 0;
++	struct FXO_priv_data	*priv;
++
++	BUG_ON(!xpd);
++	priv = xpd->priv;
++	BUG_ON(!priv);
++	rate_limit++;
++	if((rate_limit % 100) == 0) {
++		poll_battery(xbus, xpd);
++	}
++	handle_fxo_leds(xpd);
++	return 0;
++}
++
++/*---------------- FXO: HOST COMMANDS -------------------------------------*/
++
++/* 0x0F */ HOSTCMD(FXO, CHAN_ENABLE, xpp_line_t lines, bool on)
++{
++	unsigned long		flags;
++	int	ret = 0;
++	int	i;
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	lines &= xpd->enabled_chans;	// Ignore disabled channels
++	if(!lines) {
++		return 0;
++	}
++	DBG("Channel Activation: 0x%4X %s\n", lines, (on) ? "on" : "off");
++	if(on) {
++		for_each_enabled_line(xpd, i) {
++			spin_lock_irqsave(&xpd->lock, flags);
++			do_led(xpd, i, LED_GREEN, LED_ON);
++			spin_unlock_irqrestore(&xpd->lock, flags);
++			mdelay(20);
++		}
++		for_each_enabled_line(xpd, i) {
++			spin_lock_irqsave(&xpd->lock, flags);
++			do_led(xpd, i, LED_GREEN, LED_OFF);
++			spin_unlock_irqrestore(&xpd->lock, flags);
++			mdelay(20);
++		}
++	}
++	return ret;
++}
++
++/* 0x0F */ HOSTCMD(FXO, CHAN_CID, int pos)
++{
++	int		ret = 0;
++	xpp_line_t	lines = BIT(pos);
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	lines &= xpd->enabled_chans;	// Ignore disabled channels
++	if(!lines) {
++		return 0;
++	}
++	DBG("%s/%s/%d:\n", xbus->busname, xpd->xpdname, pos);
++	return ret;
++}
++
++
++/* 0x0F */ HOSTCMD(FXO, RING, int pos, bool on)
++{
++	int		ret = 0;
++	xpacket_t	*pack;
++	slic_cmd_t	*sc;
++	xpp_line_t	mask = (1 << pos);
++	int		len;
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	mask &= xpd->enabled_chans;	// Ignore disabled channels
++	if(!mask) {
++		return 0;
++	}
++	DBG("%s/%s/%d %s\n", xpd->xbus->busname, xpd->xpdname, pos, (on) ? "on" : "off");
++	XPACKET_NEW(pack, xbus, FXO, DAA_WRITE, xpd->id);
++	sc = &RPACKET_FIELD(pack, FXO, DAA_WRITE, slic_cmd);
++	len = slic_cmd_direct_write(sc, mask, 0x40, (on)?0x04:0x01);
++	pack->datalen = len;
++
++	packet_send(xbus, pack);
++	return ret;
++}
++
++/* 0x0F */ HOSTCMD(FXO, SETHOOK, int pos, bool offhook)
++{
++	int		ret = 0;
++	xpacket_t	*pack;
++	slic_cmd_t	*sc;
++	int		len;
++	unsigned long	flags;
++	bool		value;
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	value = (offhook) ? 0x01 : 0x00;
++	// value |= BIT(3);	/* Bit 3 is for CID */
++	DBG("%s/%s/%d: SETHOOK: value=0x%02X %s\n", xbus->busname, xpd->xpdname, pos, value, (offhook)?"OFFHOOK":"ONHOOK");
++	spin_lock_irqsave(&xpd->lock, flags);
++	if(!IS_SET(xpd->enabled_chans, pos))
++		goto out;
++	XPACKET_NEW(pack, xbus, FXO, DAA_WRITE, xpd->id);
++	sc = &RPACKET_FIELD(pack, FXO, DAA_WRITE, slic_cmd);
++	len = slic_cmd_direct_write(sc, BIT(pos), 0x05, value);
++	pack->datalen = len;
++	packet_send(xbus, pack);
++	do_led(xpd, pos, LED_GREEN, (offhook)?LED_ON:LED_OFF);
++out:
++	spin_unlock_irqrestore(&xpd->lock, flags);
++	return ret;
++}
++
++/* 0x0F */ HOSTCMD(FXO, RELAY_OUT, byte which, bool on)
++{
++	return -ENOSYS;
++}
++
++/* 0x0F */ HOSTCMD(FXO, DAA_INIT)
++{
++	int	ret = 0;
++	xpacket_t		*pack;
++	slic_data_t		*slic;
++	struct slic_init_data	*source;
++	int			i;
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	DBG("INITIALIZING DAA\n");
++	for(i = 0; i < ARRAY_SIZE(slic_init_data); i++) {
++		source = &slic_init_data[i];
++		XPACKET_NEW(pack, xbus, FXO, DAA_INIT, xpd->id);
++		RPACKET_FIELD(pack, FXO, DAA_INIT, lines) = source->lines;
++
++		slic = &RPACKET_FIELD(pack, FXO, DAA_INIT, slic_data);
++		slic->len = source->slic_data.len;
++		memcpy(slic->data, source->slic_data.data, source->slic_data.len);
++		pack->datalen = sizeof(xpp_line_t) + slic->len + 1;
++//		dump_packet("DAA", pack, print_dbg);
++		packet_send(xbus, pack);
++		mdelay(1);	// FIXME: check with Dima
++	}
++	return ret;
++}
++
++/* 0x0F */ HOSTCMD(FXO, DAA_QUERY, int pos, byte reg_num)
++{
++	int	ret = 0;
++	xpacket_t	*pack;
++	slic_cmd_t	*sc;
++	int		len;
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	// DBG("\n");
++	XPACKET_NEW(pack, xbus, FXO, DAA_WRITE, xpd->id);
++	sc = &RPACKET_FIELD(pack, FXO, DAA_WRITE, slic_cmd);
++	len = slic_cmd_direct_read(sc, BIT(pos), reg_num);
++
++	pack->datalen = len;
++
++	packet_send(xbus, pack);
++	return ret;
++}
++
++/*---------------- FXO: Astribank Reply Handlers --------------------------*/
++
++HANDLER_DEF(FXO, SIG_CHANGED)
++{
++	xpp_line_t	sig_status = RPACKET_FIELD(pack, FXO, SIG_CHANGED, sig_status);
++	xpp_line_t	sig_toggles = RPACKET_FIELD(pack, FXO, SIG_CHANGED, sig_toggles);
++	unsigned long	flags;
++	int		i;
++
++	if(!xpd) {
++		NOTICE("%s: received %s for non-existing xpd: %d\n",
++				__FUNCTION__, cmd->name, XPD_NUM(pack->content.addr));
++		return -EPROTO;
++	}
++	DBG("%s/%s: (PSTN) sig_toggles=0x%04X sig_status=0x%04X\n", xpd->xbus->busname, xpd->xpdname, sig_toggles, sig_status);
++	spin_lock_irqsave(&xpd->lock, flags);
++	for_each_line(xpd, i) {
++		if(IS_SET(sig_status, i)) {
++			xpd->ringing[i] = 1;
++		} else {
++			xpd->ringing[i] = 0;
++		}
++	}
++	spin_unlock_irqrestore(&xpd->lock, flags);
++	return 0;
++}
++
++HANDLER_DEF(FXO, DAA_REPLY)
++{
++	slic_reply_t		*info = &RPACKET_FIELD(pack, FXO, DAA_REPLY, info);
++	xpp_line_t		lines = RPACKET_FIELD(pack, FXO, DAA_REPLY, lines);
++	unsigned long		flags;
++	struct FXO_priv_data	*priv;
++
++	if(!xpd) {
++		NOTICE("%s: received %s for non-existing xpd: %d\n",
++				__FUNCTION__, cmd->name, XPD_NUM(pack->content.addr));
++		return -EPROTO;
++	}
++	spin_lock_irqsave(&xpd->lock, flags);
++	priv = xpd->priv;
++	BUG_ON(!priv);
++	if(!info->indirect && info->reg_num == DAA_VBAT_REGISTER) {
++		xpp_line_t	last_batt_on = priv->battery;
++		xpp_line_t	changed_lines;
++		int		i;
++
++		if(abs(info->data_low) < BAT_THRESHOLD) {
++			priv->battery &= ~lines;
++			// DBG("BATTERY OFF (%04X) = %d\n", lines, info->data_low);
++		} else {
++			priv->battery |= lines;
++			// DBG("BATTERY ON  (%04X) = %d\n", lines, info->data_low);
++		}
++		changed_lines = last_batt_on ^ priv->battery;
++		for_each_line(xpd, i) {
++			if(!IS_SET(changed_lines, i) || IS_SET(xpd->hookstate, i))
++				continue;
++#if 0
++			/* FIXME: We don't want to affect the whole span */
++			if(IS_SET(priv->battery, i))
++				update_xpd_status(xpd, ZT_ALARM_NONE);
++			else
++				update_xpd_status(xpd, ZT_ALARM_RED);
++#endif
++		}
++	}
++#if 0
++	DBG("DAA_REPLY: xpd #%d %s reg_num=0x%X, dataL=0x%X dataH=0x%X\n",
++			xpd->id, (info->indirect)?"I":"D",
++			info->reg_num, info->data_low, info->data_high);
++#endif
++
++	/* Update /proc info only if reply relate to the last slic read request */
++	if(priv->requested_reply.indirect == info->indirect &&
++			priv->requested_reply.reg_num == info->reg_num) {
++		priv->last_reply = *info;
++	}
++	spin_unlock_irqrestore(&xpd->lock, flags);
++	return 0;
++}
++
++
++xproto_table_t PROTO_TABLE(FXO) = {
++	.owner = THIS_MODULE,
++	.entries = {
++		/*	Card	Opcode		*/
++		XENTRY(	FXO,	SIG_CHANGED	),
++		XENTRY(	FXO,	DAA_REPLY	),
++	},
++	.name = "FXO",
++	.type = XPD_TYPE_FXO,
++	.xops = {
++		.card_new	= FXO_card_new,
++		.card_init	= FXO_card_init,
++		.card_remove	= FXO_card_remove,
++		.card_zaptel_registration	= FXO_card_zaptel_registration,
++#ifdef WITH_RBS
++		.card_hooksig	= FXO_card_hooksig,
++#else
++		.card_sethook	= FXO_card_sethook,
++#endif
++		.card_tick	= FXO_card_tick,
++
++		.RING		= XPROTO_CALLER(FXO, RING),
++		.SETHOOK	= XPROTO_CALLER(FXO, SETHOOK),
++		.RELAY_OUT	= XPROTO_CALLER(FXO, RELAY_OUT),
++		.CHAN_ENABLE	= XPROTO_CALLER(FXO, CHAN_ENABLE),
++		.CHAN_CID	= XPROTO_CALLER(FXO, CHAN_CID),
++
++		.SYNC_SOURCE	= XPROTO_CALLER(GLOBAL, SYNC_SOURCE),
++		.PCM_WRITE	= XPROTO_CALLER(GLOBAL, PCM_WRITE),
++	},
++	.packet_is_valid = fxo_packet_is_valid,
++	.packet_dump = fxo_packet_dump,
++};
++
++static bool fxo_packet_is_valid(xpacket_t *pack)
++{
++	const xproto_entry_t	*xe;
++
++	//DBG("\n");
++	xe = xproto_card_entry(&PROTO_TABLE(FXO), pack->content.opcode);
++	return xe != NULL;
++}
++
++static void fxo_packet_dump(xpacket_t *pack)
++{
++	DBG("\n");
++}
++
++/*------------------------- DAA Handling --------------------------*/
++
++static int proc_fxo_info_read(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++	int			len = 0;
++	unsigned long		flags;
++	xpd_t			*xpd = data;
++	struct FXO_priv_data	*priv;
++	int			i;
++
++	BUG_ON(!xpd);
++	spin_lock_irqsave(&xpd->lock, flags);
++	priv = xpd->priv;
++	BUG_ON(!priv);
++	len += sprintf(page + len, "\t%-17s: ", "Channel");
++	for_each_line(xpd, i) {
++		if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
++			len += sprintf(page + len, "%d ", i % 10);
++	}
++	len += sprintf(page + len, "\n\t%-17s: ", "ledstate");
++	for_each_line(xpd, i) {
++		if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
++			len += sprintf(page + len, "%d ", IS_SET(priv->ledstate[LED_GREEN], i));
++	}
++	len += sprintf(page + len, "\n\t%-17s: ", "blinking");
++	for_each_line(xpd, i) {
++		if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
++			len += sprintf(page + len, "%d ", IS_BLINKING(priv,i,LED_GREEN));
++	}
++	len += sprintf(page + len, "\n\t%-17s: ", "battery");
++	for_each_line(xpd, i) {
++		len += sprintf(page + len, "%d ", IS_SET(priv->battery, i));
++	}
++	len += sprintf(page + len, "\n");
++	spin_unlock_irqrestore(&xpd->lock, flags);
++	if (len <= off+count)
++		*eof = 1;
++	*start = page + off;
++	len -= off;
++	if (len > count)
++		len = count;
++	if (len < 0)
++		len = 0;
++	return len;
++}
++
++
++static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++	int			len = 0;
++	unsigned long		flags;
++	xpd_t			*xpd = data;
++	slic_reply_t		*info;
++	struct FXO_priv_data	*priv;
++
++	BUG_ON(!xpd);
++	spin_lock_irqsave(&xpd->lock, flags);
++	priv = xpd->priv;
++	BUG_ON(!priv);
++	info = &priv->last_reply;
++	len += sprintf(page + len, "# Writing bad data into this file may damage your hardware!\n");
++	len += sprintf(page + len, "# Consult firmware docs first\n");
++	len += sprintf(page + len, "DAA_REPLY: %s reg_num=0x%X, dataH=0x%X dataL=0x%X\n",
++			(info->indirect)?"I":"D",
++			info->reg_num, info->data_high, info->data_low);
++	spin_unlock_irqrestore(&xpd->lock, flags);
++	if (len <= off+count)
++		*eof = 1;
++	*start = page + off;
++	len -= off;
++	if (len > count)
++		len = count;
++	if (len < 0)
++		len = 0;
++	return len;
++}
++
++/*
++ *        Direct/Indirect
++ *              v
++ * FF FF FF FF WD 06 1
++ * ^---------^ ^  Reg
++ *      | Write/Read
++ *      |
++ *    DAA #
++ */
++static int parse_slic_cmd(const char *buf, slic_cmd_t *sc, slic_reply_t *requested_reply)
++{
++	char		op;		/* [W]rite, [R]ead */
++	char		reg_type;	/* [D]irect, [I]ndirect */
++	int		s1, s2, s3, s4;
++	int		reg_num;
++	int		data_low, data_high;
++	xpp_line_t	lines;
++	int		ret;
++
++	ret = sscanf(buf, "%x %x %x %x %c%c %x %x %x",
++			&s1, &s2, &s3, &s4, &op, &reg_type, &reg_num, &data_high, &data_low);
++	lines = (s4 << 24) | (s3 << 16) | (s2 << 8) | (s1);
++	switch(op) {
++		case 'R':
++			if(reg_type == 'D' && ret == 7) {
++				// DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num);
++				ret = slic_cmd_direct_read(sc, lines, reg_num);
++				if(requested_reply) {
++					requested_reply->indirect = 0;
++					requested_reply->reg_num = reg_num;
++				}
++			} else if(reg_type == 'I' && ret == 7) {
++				// DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num);
++				ret = slic_cmd_indirect_read(sc, lines, reg_num);
++				if(requested_reply) {
++					requested_reply->indirect = 1;
++					requested_reply->reg_num = reg_num;
++				}
++			} else {
++				NOTICE("%s: Bad read input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type);
++				goto err;
++			}
++			break;
++		case 'W':
++			if(reg_type == 'D' && ret == 8) {
++				// DBG("0x%X 0x%X 0x%X 0x%X %c %x %X\n", s1, s2, s3, s4, reg_type, reg_num, data_high);
++				ret = slic_cmd_direct_write(sc, lines, reg_num, data_high);
++			} else if(reg_type == 'I' && ret == 9) {
++				// DBG("0x%X 0x%X 0x%X 0x%X %c %x %X %X\n", s1, s2, s3, s4, reg_type, reg_num, data_high, data_low);
++				ret = slic_cmd_indirect_write(sc, lines, reg_num, data_low, data_high);
++			} else {
++				NOTICE("%s: Bad write input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type);
++				goto err;
++			}
++			break;
++		default:
++			NOTICE("%s: Bad input: ret=%d buf='%s' op=%c\n", __FUNCTION__, ret, buf, op);
++			goto err;
++	}
++	return ret;
++err:
++	return -EINVAL;
++}
++
++static int process_slic_cmdline(xpd_t *xpd, char *cmdline)
++{
++	xbus_t			*xbus;
++	struct FXO_priv_data	*priv;
++	slic_cmd_t		sc;
++	xpacket_t		*pack;
++	char			*p;
++	int			len = strlen(cmdline);
++
++	BUG_ON(!xpd);
++	xbus = xpd->xbus;
++	priv = xpd->priv;
++	if((p = strchr(cmdline, '#')) != NULL)	/* Truncate comments */
++		*p = '\0';
++	if((p = strchr(cmdline, ';')) != NULL)	/* Truncate comments */
++		*p = '\0';
++	for(p = cmdline; *p && (*p == ' ' || *p == '\t'); p++) /* Trim leading whitespace */
++		;
++	if(*p == '\0')
++		return 0;
++	len = parse_slic_cmd(p, &sc, &priv->requested_reply);
++	if(len < 0)
++		return len;
++	sc.lines &= xpd->enabled_chans;	// Ignore disabled channels
++	if(!sc.lines) {
++		NOTICE("%s: no enabled channels are marked. Skip.\n", __FUNCTION__);
++		return 0;
++	}
++	dump_slic_cmd("WRITE_DAA", &sc);
++	XPACKET_NEW(pack, xbus, FXO, DAA_WRITE, xpd->id);
++	RPACKET_FIELD(pack, FXO, DAA_WRITE, slic_cmd) = sc;
++	pack->datalen = len;
++	packet_send(xbus, pack);
++	return 0;
++}
++
++static int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data)
++{
++	xpd_t		*xpd = data;
++	const int	LINE_LEN = 500;
++	char		buf[LINE_LEN];
++	char		*p;
++	int		i;
++	int		ret;
++
++	BUG_ON(!xpd);
++	for(i = 0; i < count; /* noop */) {
++		for(p = buf; p < buf + LINE_LEN; p++) {	/* read a line */
++			if(i >= count)
++				break;
++			if(get_user(*p, buffer + i))
++				return -EFAULT;
++			i++;
++			if(*p == '\n' || *p == '\r')	/* whatever */
++				break;
++		}
++		if(p >= buf + LINE_LEN)
++			return -E2BIG;
++		*p = '\0';
++		ret = process_slic_cmdline(xpd, buf);
++		if(ret < 0)
++			return ret;
++		mdelay(1);
++	}
++	return count;
++}
++
++
++int __init card_fxo_startup(void)
++{
++	INFO("%s revision %s\n", THIS_MODULE->name, ZAPTEL_VERSION);
++	xproto_register(&PROTO_TABLE(FXO));
++	return 0;
++}
++
++void __exit card_fxo_cleanup(void)
++{
++	xproto_unregister(&PROTO_TABLE(FXO));
++}
++
++MODULE_DESCRIPTION("XPP FXO Card Driver");
++MODULE_AUTHOR("Oron Peled <oron at actcom.co.il>");
++MODULE_LICENSE("GPL");
++MODULE_VERSION(ZAPTEL_VERSION);
++MODULE_ALIAS_XPD(XPD_TYPE_FXO);
++
++module_init(card_fxo_startup);
++module_exit(card_fxo_cleanup);
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/card_fxo.h zaptel-xpp-XJ8JZx_dist/xpp/card_fxo.h
+--- zaptel-1.2.6/xpp/card_fxo.h	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/card_fxo.h	2006-03-30 12:42:46.619481000 +0200
+@@ -0,0 +1,65 @@
++#ifndef	CARD_FXO_H
++#define	CARD_FXO_H
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2006, Xorcom
++ *
++ * 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 "xpd.h"
++#include "slic.h"
++
++enum fxo_opcodes {
++	XPROTO_NAME(FXO, SIG_CHANGED)		= 0x06,
++/**/
++	XPROTO_NAME(FXO, DAA_WRITE)		= 0x0F,	/* Write to DAA */
++	XPROTO_NAME(FXO, CHAN_ENABLE)		= 0x0F,	/* Write to DAA */
++	XPROTO_NAME(FXO, CHAN_CID)		= 0x0F,	/* Write to DAA */
++	XPROTO_NAME(FXO, RING)			= 0x0F,	/* Write to DAA */
++	XPROTO_NAME(FXO, SETHOOK)		= 0x0F,	/* Write to DAA */
++	XPROTO_NAME(FXO, LED)			= 0x0F,	/* Write to DAA */
++	XPROTO_NAME(FXO, RELAY_OUT)		= 0x0F,	/* Write to DAA */
++	XPROTO_NAME(FXO, DAA_INIT)		= 0x0F,	/* Write to DAA */
++	XPROTO_NAME(FXO, DAA_QUERY)		= 0x0F,	/* Write to DAA */
++/**/
++	XPROTO_NAME(FXO, DAA_REPLY)		= 0x10,
++};
++
++
++DEF_RPACKET_DATA(FXO, SIG_CHANGED,
++	byte		type;		/* unused -- we have it from DEV_DESC */
++	xpp_line_t	sig_status;	/* channels: lsb=1, msb=8 */
++	xpp_line_t	sig_toggles;	/* channels: lsb=1, msb=8 */
++	);
++DEF_RPACKET_DATA(FXO, DAA_REPLY,	/* Get status of a single DAA (for debugging) */
++	xpp_line_t	lines;
++	slic_reply_t	info;
++	);
++DEF_RPACKET_DATA(FXO, DAA_INIT,
++	xpp_line_t      lines;
++	slic_data_t     slic_data;
++	);
++DEF_RPACKET_DATA(FXO, DAA_WRITE,
++	slic_cmd_t	slic_cmd;
++	);
++
++#define	DAA_VBAT_REGISTER	29
++#define	BAT_THRESHOLD		3
++
++#endif	/* CARD_FXO_H */
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/card_fxs.c zaptel-xpp-XJ8JZx_dist/xpp/card_fxs.c
+--- zaptel-1.2.6/xpp/card_fxs.c	2006-04-03 10:08:13.000000000 +0300
++++ zaptel-xpp-XJ8JZx_dist/xpp/card_fxs.c	2006-06-26 19:51:05.258875000 +0300
+@@ -1,6 +1,6 @@
+ /*
+  * Written by Oron Peled <oron at actcom.co.il>
+- * Copyright (C) 2004-2005, Xorcom
++ * Copyright (C) 2004-2006, Xorcom
+  *
+  * All rights reserved.
+  *
+@@ -23,13 +23,15 @@
+ #include <linux/init.h>
+ #include <linux/module.h>
+ #include <linux/fs.h>
++#include <linux/delay.h>
++#include <version.h>		/* For zaptel version */
+ #include "xpd.h"
+ #include "xproto.h"
+ #include "xpp_zap.h"
+-#include <linux/delay.h>
++#include "card_fxo.h"
++#include "zap_debug.h"
+ 
+-static const char rcsid[] = "$Id: card_fxs.c 995 2006-04-03 07:08:13Z tzafrir $";
+-static const char revision[] = "$Revision: 995 $";
++static const char rcsid[] = "$Id: card_fxs.c 1457 2006-06-26 16:51:05Z tzafrir $";
+ 
+ DEF_PARM(int, print_dbg, 0, "Print DBG statements");	/* must be before zap_debug.h */
+ 
+@@ -42,20 +44,41 @@
+ #define	MASK_DIGI_OUT	(MASK_BITS(LINES_DIGI_OUT) << LINES_REGULAR)
+ #define	MASK_DIGI_INP	(MASK_BITS(LINES_DIGI_INP) << (LINES_REGULAR + LINES_DIGI_OUT))
+ 
++enum fxs_leds {
++	LED_GREEN,
++	LED_RED,
++	OUTPUT_RELAY,
++};
++
++#define	NUM_LEDS	2
++
++static int SLIC_DIRECT_REQUEST(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, byte reg, byte dL)
++{
++	xpacket_t	*pack;
++	slic_cmd_t	*sc;
++	int		len;
++
++	XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
++	sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
++	len = slic_cmd_direct_write(sc, lines, reg, dL);
++	pack->datalen = len;
++	packet_send(xbus, pack);
++	return 0;
++}
++
+ /*---------------- FXS Protocol Commands ----------------------------------*/
+ 
+-/* 0x0F */ DECLARE_CMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on);
+-/* 0x0F */ DECLARE_CMD(FXS, CHAN_POWER, xpp_line_t lines, bool on);
+-/* 0x0F */ DECLARE_CMD(FXS, CHAN_CID, xpp_line_t lines);
+-/* 0x0F */ DECLARE_CMD(FXS, RING, int pos, bool on);
+-/* 0x0F */ DECLARE_CMD(FXS, SETHOOK, xpp_line_t hook_status);
+-/* 0x0F */ DECLARE_CMD(FXS, LED, xpp_line_t lines, byte which, bool on);
+-/* 0x0F */ DECLARE_CMD(FXS, RELAY_OUT, byte which, bool on);
+-/* 0x0F */ DECLARE_CMD(FXS, SLIC_INIT);
+-/* 0x0F */ DECLARE_CMD(FXS, SLIC_QUERY, int pos, byte reg_num);
++static /* 0x0F */ DECLARE_CMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on);
++static /* 0x0F */ DECLARE_CMD(FXS, CHAN_CID, int pos);
++static /* 0x0F */ DECLARE_CMD(FXS, RING, int pos, bool on);
++static /* 0x0F */ DECLARE_CMD(FXS, SETHOOK, int pos, bool offhook);
++static /* 0x0F */ DECLARE_CMD(FXS, RELAY_OUT, byte which, bool on);
++static /* 0x0F */ DECLARE_CMD(FXS, SLIC_INIT);
++static /* 0x0F */ DECLARE_CMD(FXS, SLIC_QUERY, int pos, byte reg_num);
+ 
+ static bool fxs_packet_is_valid(xpacket_t *pack);
+ static void fxs_packet_dump(xpacket_t *pack);
++static int proc_fxs_info_read(char *page, char **start, off_t off, int count, int *eof, void *data);
+ static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data);
+ static int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data);
+ 
+@@ -71,17 +94,177 @@
+ 	xpp_line_t	lines;
+ 	slic_data_t	slic_data;
+ } slic_init_data[] = {
+-#include "slic_init.inc"
++#ifdef	OLD_CARD
++#include "init_data_3_19.inc"
++#else
++#include "init_data_3_20.inc"
++#endif
+ };
+ #undef	S_
+ 
+ #define	PROC_SLIC_FNAME		"slics"
++#define	PROC_FXS_INFO_FNAME	"fxs_info"
+ 
+ struct FXS_priv_data {
+-	struct proc_dir_entry	*xpd_slic;
+-	slic_reply_t		last_reply;
++	struct proc_dir_entry		*xpd_slic;
++	struct proc_dir_entry		*fxs_info;
++	slic_reply_t			requested_reply;
++	slic_reply_t			last_reply;
++	xpp_line_t			ledstate[NUM_LEDS];	/* 0 - OFF, 1 - ON */
++	xpp_line_t			ledcontrol[NUM_LEDS];	/* 0 - OFF, 1 - ON */
++	int				blinking[NUM_LEDS][CHANNELS_PERXPD];
+ };
+ 
++/*---------------- FXS: Static functions ----------------------------------*/
++static int do_chan_power(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, bool on)
++{
++	int		ret = 0;
++	xpacket_t	*pack;
++	slic_cmd_t	*sc;
++	int		len;
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	lines &= xpd->enabled_chans;	// Ignore disabled channels
++	if(!lines) {
++		return 0;
++	}
++	DBG("%s/%s: 0x%04X %s\n", xbus->busname, xpd->xpdname, lines, (on) ? "up" : "down");
++	XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
++	sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
++	if(on) {
++		// Power up
++		len = slic_cmd_direct_write(sc, lines, 0x42, 0x06);
++	} else {
++		// Power down
++		len = slic_cmd_direct_write(sc, lines, 0x42, 0x00);
++	}
++	pack->datalen = len;
++
++	packet_send(xbus, pack);
++	return ret;
++}
++
++#define	IS_BLINKING(priv,pos,color)	((priv)->blinking[color][pos] != 0)
++#define	DO_BLINK(priv,pos,color,val)	((priv)->blinking[color][pos] = (val))
++#define	DO_LED(priv,pos,color,val)	((val)?BIT_SET((priv)->ledcontrol[color],(pos)):BIT_CLR((priv)->ledcontrol[color],(pos)))
++
++/*
++ * LED and RELAY control is done via SLIC register 0x06:
++ *         7     6     5     4     3     2     1     0
++ * 	+-----+-----+-----+-----+-----+-----+-----+-----+
++ * 	| M2  | M1  | M3  | C2  | O1  | O3  | C1  | C3  |
++ * 	+-----+-----+-----+-----+-----+-----+-----+-----+
++ *
++ * 	Cn	- Control bit (control one digital line)
++ * 	On	- Output bit (program a digital line for output)
++ * 	Mn	- Mask bit (only the matching output control bit is affected)
++ *
++ * 	C3	- OUTPUT RELAY (0 - OFF, 1 - ON)
++ * 	C1	- GREEN LED (0 - OFF, 1 - ON)
++ * 	O3	- Output RELAY (this line is output)
++ * 	O1	- Output GREEN (this line is output)
++ * 	C2	- RED LED (0 - OFF, 1 - ON)
++ * 	M3	- Mask RELAY. (1 - C3 effect the OUTPUT RELAY)
++ * 	M2	- Mask RED. (1 - C2 effect the RED LED)
++ * 	M1	- Mask GREEN. (1 - C1 effect the GREEN LED)
++ *
++ * 	The OUTPUT RELAY (actually a relay out) is connected to line 0 and 4 only.
++ */
++
++//		        		       		GREEN	RED	OUTPUT RELAY
++static const int	led_register_mask[] = { 	BIT(7),	BIT(6),	BIT(5) };
++static const int	led_register_vals[] = { 	BIT(4),	BIT(1),	BIT(0) };
++
++/*
++ * pos can be:
++ * 	- A line number
++ * 	- ALL_LINES
++ */
++static int do_led(xpd_t *xpd, lineno_t pos, byte which, bool on)
++{
++	int			ret = 0;
++	xpacket_t		*pack;
++	slic_cmd_t		*sc;
++	int			len;
++	int			value;
++	struct FXS_priv_data	*priv;
++	xpp_line_t		lines;
++	xbus_t			*xbus;
++
++	BUG_ON(!xpd);
++	xbus = xpd->xbus;
++	priv = xpd->priv;
++	which = which % NUM_LEDS;
++	if(IS_SET(xpd->digital_outputs, pos) || IS_SET(xpd->digital_inputs, pos))
++		goto out;
++	if(pos == ALL_LINES) {
++		lines = ~0;
++		priv->ledstate[which] = (on) ? ~0 : 0;
++	} else {
++		lines = BIT(pos);
++		if(on) {
++			BIT_SET(priv->ledstate[which], pos);
++		} else {
++			BIT_CLR(priv->ledstate[which], pos);
++		}
++	}
++	if(!(lines & xpd->enabled_chans))	// Ignore disabled channels
++		goto out;
++	DBG("%s/%s: LED: lines=0x%04X which=%d -- %s\n", xbus->busname, xpd->xpdname, lines, which, (on) ? "on" : "off");
++	value = BIT(2) | BIT(3);
++	value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_register_mask[which]);
++	if(on)
++		value |= led_register_vals[which];
++	XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
++	sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
++	len = slic_cmd_direct_write(sc, lines, 0x06, value);
++	pack->datalen = len;
++	packet_send(xbus, pack);
++
++out:
++	return ret;
++}
++
++static void handle_fxs_leds(xpd_t *xpd)
++{
++	int			i;
++	unsigned long		flags;
++	const enum fxs_leds	colors[] = { LED_GREEN, LED_RED };
++	int			color;
++	unsigned int		timer_count;
++	struct FXS_priv_data	*priv;
++
++	BUG_ON(!xpd);
++	priv = xpd->priv;
++	spin_lock_irqsave(&xpd->lock, flags);
++	timer_count = xpd->timer_count;
++	for(color = 0; color < ARRAY_SIZE(colors); color++) {
++		for_each_enabled_line(xpd, i) {
++			if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i))
++				continue;
++			if(IS_BLINKING(priv, i, color)) {		// Blinking
++				// led state is toggled
++				if((timer_count % LED_BLINK_PERIOD) == 0) {
++					DBG("%s/%s/%d ledstate=%s\n", xpd->xbus->busname, xpd->xpdname, i,
++							(IS_SET(priv->ledstate[color], i))?"ON":"OFF");
++					if(!IS_SET(priv->ledstate[color], i)) {
++						do_led(xpd, i, color, 1);
++					} else {
++						do_led(xpd, i, color, 0);
++					}
++				}
++			} else if(IS_SET(priv->ledcontrol[color], i) && !IS_SET(priv->ledstate[color], i)) {
++						do_led(xpd, i, color, 1);
++			} else if(!IS_SET(priv->ledcontrol[color], i) && IS_SET(priv->ledstate[color], i)) {
++						do_led(xpd, i, color, 0);
++			}
++
++		}
++	}
++	spin_unlock_irqrestore(&xpd->lock, flags);
++}
++
+ /*---------------- FXS: Methods -------------------------------------------*/
+ 
+ static xpd_t *FXS_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision)
+@@ -100,29 +283,83 @@
+ 		xpd->digital_inputs = MASK_DIGI_INP;
+ 	}
+ 	xpd->direction = TO_PHONE;
++	xpd->revision = revision;
+ 	return xpd;
+ }
+ 
++static void clean_proc(xbus_t *xbus, xpd_t *xpd)
++{
++	struct FXS_priv_data	*priv;
++
++	BUG_ON(!xpd);
++	priv = xpd->priv;
++#ifdef	CONFIG_PROC_FS
++	if(priv->xpd_slic) {
++		DBG("Removing xpd SLIC file %s/%s\n", xbus->busname, xpd->xpdname);
++		remove_proc_entry(PROC_SLIC_FNAME, xpd->proc_xpd_dir);
++	}
++	if(priv->fxs_info) {
++		DBG("Removing xpd FXS_INFO file %s/%s\n", xbus->busname, xpd->xpdname);
++		remove_proc_entry(PROC_FXS_INFO_FNAME, xpd->proc_xpd_dir);
++	}
++#endif
++}
++
+ static int FXS_card_init(xbus_t *xbus, xpd_t *xpd)
+ {
+ 	struct FXS_priv_data	*priv;
++	int			ret = 0;
+ 
+ 	BUG_ON(!xpd);
+ 	priv = xpd->priv;
+-	CALL_PROTO(FXS, SLIC_INIT, xbus, xpd);
+ #ifdef	CONFIG_PROC_FS
++	DBG("Creating FXS_INFO file for %s/%s\n", xbus->busname, xpd->xpdname);
++	priv->fxs_info = create_proc_read_entry(PROC_FXS_INFO_FNAME, 0444, xpd->proc_xpd_dir, proc_fxs_info_read, xpd);
++	if(!priv->fxs_info) {
++		ERR("Failed to create proc '%s' for %s/%s\n", PROC_FXS_INFO_FNAME, xbus->busname, xpd->xpdname);
++		ret = -ENOENT;
++		goto out;
++	}
+ 	DBG("Creating SLICs file for %s/%s\n", xbus->busname, xpd->xpdname);
+ 	priv->xpd_slic = create_proc_entry(PROC_SLIC_FNAME, 0644, xpd->proc_xpd_dir);
+ 	if(!priv->xpd_slic) {
+ 		ERR("Failed to create proc file for SLICs of %s/%s\n", xbus->busname, xpd->xpdname);
++		ret = -ENOENT;
+ 		goto out;
+ 	}
+ 	priv->xpd_slic->write_proc = proc_xpd_slic_write;
+ 	priv->xpd_slic->read_proc = proc_xpd_slic_read;
+ 	priv->xpd_slic->data = xpd;
+-out:
+ #endif
+-	return 0;
++#ifdef HARD_CODED_INIT
++	CALL_PROTO(FXS, SLIC_INIT, xbus, xpd);
++#else
++	ret = run_initialize_registers(xpd);
++#endif
++	if(ret < 0)
++		goto out;
++	/*
++	 * Setup ring timers
++	 */
++#ifdef	WITH_RBS
++	/* Software controled ringing (for CID) */
++	ret = SLIC_DIRECT_REQUEST(xbus, xpd, ALL_LINES, 0x22, 0x00);	/* Ringing Oscilator Control */
++#else
++	/* Hardware controled ringing (no CID) */
++	ret += SLIC_DIRECT_REQUEST(xbus, xpd, ALL_LINES, 0x30, 0x80);	/* Active timer low byte */
++	ret += SLIC_DIRECT_REQUEST(xbus, xpd, ALL_LINES, 0x31, 0x3E);	/* Active timer high byte */
++	ret += SLIC_DIRECT_REQUEST(xbus, xpd, ALL_LINES, 0x32, 0x80);	/* Inactive timer low byte */
++	ret += SLIC_DIRECT_REQUEST(xbus, xpd, ALL_LINES, 0x33, 0x3E);	/* Inactive timer high byte */
++	ret += SLIC_DIRECT_REQUEST(xbus, xpd, ALL_LINES, 0x22, 0x18);	/* Ringing Oscilator Control */
++#endif
++out:
++	if(ret < 0) {
++		clean_proc(xbus, xpd);
++		ERR("%s/%s: Failed initializing registers (%d)\n", xbus->busname, xpd->xpdname, ret);
++	} else {
++		DBG("done: %s/%s\n", xbus->busname, xpd->xpdname);
++	}
++	return ret;
+ }
+ 
+ static int FXS_card_remove(xbus_t *xbus, xpd_t *xpd)
+@@ -132,15 +369,200 @@
+ 	BUG_ON(!xpd);
+ 	priv = xpd->priv;
+ 	DBG("%s/%s\n", xbus->busname, xpd->xpdname);
+-#ifdef	CONFIG_PROC_FS
+-	if(priv->xpd_slic) {
+-		DBG("Removing xpd SLIC file %s/%s\n", xbus->busname, xpd->xpdname);
+-		remove_proc_entry(PROC_SLIC_FNAME, xpd->proc_xpd_dir);
++	clean_proc(xbus, xpd);
++	return 0;
++}
++
++static int FXS_card_zaptel_registration(xpd_t *xpd, bool on)
++{
++	xbus_t			*xbus;
++	struct FXS_priv_data	*priv;
++	unsigned long		flags;
++	int	i;
++
++	BUG_ON(!xpd);
++	xbus = xpd->xbus;
++	priv = xpd->priv;
++	BUG_ON(!xbus);
++	DBG("%s/%s: %s\n", xbus->busname, xpd->xpdname, (on)?"on":"off");
++	if(on) {
++		spin_lock_irqsave(&xpd->lock, flags);
++		do_led(xpd, ALL_LINES, LED_GREEN, LED_OFF);
++		spin_unlock_irqrestore(&xpd->lock, flags);
++		for_each_enabled_line(xpd, i) {
++			DO_LED(priv,i,LED_GREEN,LED_ON);
++			mdelay(50);
++		}
++		for_each_enabled_line(xpd, i) {
++			DO_LED(priv,i,LED_GREEN,LED_OFF);
++			mdelay(50);
++		}
++	} else {
++		spin_lock_irqsave(&xpd->lock, flags);
++		do_led(xpd, ALL_LINES, LED_RED, LED_OFF);
++		spin_unlock_irqrestore(&xpd->lock, flags);
++		for_each_enabled_line(xpd, i) {
++			DO_LED(priv,i,LED_RED,LED_ON);
++			mdelay(50);
++		}
++		for_each_enabled_line(xpd, i) {
++			DO_LED(priv,i,LED_RED,LED_OFF);
++			mdelay(50);
++		}
+ 	}
+-#endif
+ 	return 0;
+ }
+ 
++#ifdef WITH_RBS
++int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
++{
++	int		ret = 0;
++
++	DBG("%s/%s/%d: %s\n", xbus->busname, xpd->xpdname, pos, txsig2str(txsig));
++	BUG_ON(xpd->direction != TO_PHONE);
++	if (IS_SET(xpd->digital_inputs, pos)) {
++		DBG("Ignoring signal sent to digital input line\n");
++		return 0;
++	}
++	if (IS_SET(xpd->digital_outputs, pos)) {
++		DBG("This is an output channel.");
++		switch(txsig) {
++			case ZT_TXSIG_ONHOOK:
++				DBG("%s/%s/%d: digital output OFF\n", xbus->busname, xpd->xpdname, pos);
++				ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 0);
++				return ret;
++			case ZT_TXSIG_START:
++				DBG("%s/%s/%d: digital output ON\n", xbus->busname, xpd->xpdname, pos);
++				ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 1);
++				return ret;
++			default: 
++				DBG("Ignoring signal %s: unhandled for output channel.\n", 
++						txsig2str(txsig));
++		}
++	}
++	switch(txsig) {
++		case ZT_TXSIG_ONHOOK:
++			xpd->ringing[pos] = 0;
++			BIT_CLR(xpd->cid_on, pos);
++			ret = CALL_XMETHOD(RING, xbus, xpd, pos, 0);			// RING off
++#if 0
++			switch(chan->sig) {
++				case ZT_SIG_EM:
++				case ZT_SIG_FXOKS:
++				case ZT_SIG_FXOLS:
++					xpd->lasttxhook[pos] = xpd->idletxhookstate[pos];
++					break;
++				case ZT_SIG_FXOGS:
++					xpd->lasttxhook[pos] = FXS_LINE_TIPOPEN;
++					break;
++			}
++#endif
++			break;
++		case ZT_TXSIG_OFFHOOK:
++			if(xpd->ringing[pos]) {
++				BIT_SET(xpd->cid_on, pos);
++				ret = CALL_XMETHOD(CHAN_CID, xpd->xbus, xpd, pos);		// CALLER ID
++			}
++			xpd->ringing[pos] = 0;
++#if 0
++			switch(chan->sig) {
++				case ZT_SIG_EM:
++					xpd->lasttxhook[pos] = FXS_LINE_REV_ACTIVE;
++					break;
++				default:
++					xpd->lasttxhook[pos] = xpd->idletxhookstate[pos];
++					break;
++			}
++#endif
++			break;
++		case ZT_TXSIG_START:
++			xpd->lasttxhook[pos] = FXS_LINE_RING;
++			xpd->ringing[pos] = 1;
++			BIT_CLR(xpd->cid_on, pos);
++			if(IS_SET(xpd->digital_inputs, pos)) {
++				NOTICE("%s: Trying to RING a digital input channel %d. Ignoring\n", __FUNCTION__, pos);
++				return -EINVAL;
++			}
++			ret = CALL_XMETHOD(RING, xbus, xpd, pos, 1);			// RING on
++			break;
++		case ZT_TXSIG_KEWL:
++			xpd->lasttxhook[pos] = FXS_LINE_DISABLED;
++			break;
++		default:
++			NOTICE("%s: Can't set tx state to %s (%d)\n", __FUNCTION__, txsig2str(txsig), txsig);
++			ret = -EINVAL;
++	}
++	return ret;
++}
++
++#else
++int FXS_card_sethook(xbus_t *xbus, xpd_t *xpd, int pos, int hookstate)
++{
++	int	ret = 0;
++
++	DBG("%s/%s/%d: %s\n", xbus->busname, xpd->xpdname, pos, hookstate2str(hookstate));
++	switch(hookstate) {
++		/* On-hook, off-hook: The PBX is playing a phone on an FXO line. 
++		 * Can be ignored for an FXS line
++		 */
++		case ZT_ONHOOK:
++			if(IS_SET(xpd->digital_inputs, pos)) {
++				NOTICE("%s: Trying to ONHOOK a digital input channel %d. Ignoring\n", __FUNCTION__, pos);
++				ret = -EINVAL;
++				break;
++			}
++			if(IS_SET(xpd->digital_outputs, pos)) {
++				DBG("%s/%s/%d: digital output OFF\n", xbus->busname, xpd->xpdname, pos);
++				ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 0);
++				break;
++			}
++			xpd->ringing[pos] = 0;
++			DBG("%s/%s/%d: stop ringing\n", xbus->busname, xpd->xpdname, pos);
++#if 1	// FIXME: Not needed -- verify
++			ret = CALL_XMETHOD(RING, xbus, xpd, pos, 0);			// RING off
++#endif
++			if(ret) {
++				DBG("ZT_ONHOOK(stop ring) Failed: ret=0x%02X\n", ret);
++				break;
++			}
++			break;
++		case ZT_START:
++			DBG("%s/%s/%d: fall through ZT_OFFHOOK\n", xbus->busname, xpd->xpdname, pos);
++			// Fall through
++		case ZT_OFFHOOK:
++			DBG("%s/%s/%d: ZT_OFFHOOK (ignoring for PHONES)\n", xbus->busname, xpd->xpdname, pos);
++			break;
++		case ZT_WINK:
++			WARN("No code yet\n");
++			break;
++		case ZT_FLASH:
++			WARN("No code yet\n");
++			break;
++		case ZT_RING:
++			DBG("%s/%s/%d: ZT_RING: %d\n", xbus->busname, xpd->xpdname, pos, xpd->ringing[pos]);
++			if(IS_SET(xpd->digital_inputs, pos)) {
++				NOTICE("%s: Trying to RING a digital input channel %d. Ignoring\n", __FUNCTION__, pos);
++				return -EINVAL;
++			}
++			if(IS_SET(xpd->digital_outputs, pos)) {
++				DBG("%s/%s/%d: digital output ON\n", xbus->busname, xpd->xpdname, pos);
++				ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 1);
++				return ret;
++			}
++			xpd->ringing[pos] = 1;
++			ret = CALL_XMETHOD(RING, xbus, xpd, pos, 1);		// RING on
++			if(ret) {
++				DBG("ZT_RING Failed: ret=0x%02X\n", ret);
++			}
++			break;
++		case ZT_RINGOFF:
++			WARN("No code yet\n");
++			break;
++	}
++	return ret;
++}
++#endif
++
+ /*
+  * INPUT polling is done via SLIC register 0x06 (same as LEDS):
+  *         7     6     5     4     3     2     1     0
+@@ -164,22 +586,30 @@
+ 
+ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd)
+ {
+-	static	int	rate_limit = 0;
++	static int		rate_limit = 0;
++	struct FXS_priv_data	*priv;
+ 
++	BUG_ON(!xpd);
++	priv = xpd->priv;
++	BUG_ON(!priv);
+ 	if((rate_limit++ % 1000) == 0) {
+ 		poll_inputs(xbus, xpd);
+ 	}
++	handle_fxs_leds(xpd);
+ 	return 0;
+ }
+ 
+ /*---------------- FXS: HOST COMMANDS -------------------------------------*/
+ 
+-/* 0x0F */ HOSTCMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on)
++static /* 0x0F */ HOSTCMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on)
+ {
+ 	int		ret = 0;
+ 	xpacket_t	*pack;
+ 	slic_cmd_t	*sc;
+ 	int		len;
++	enum fxs_state	value = (on) ? 0x01 : 0x00;
++	unsigned long	flags;
++	int		i;
+ 
+ 	BUG_ON(!xbus);
+ 	BUG_ON(!xpd);
+@@ -188,49 +618,35 @@
+ 		return 0;
+ 	}
+ 	DBG("Channel Activation: 0x%4X %s\n", lines, (on) ? "on" : "off");
++	// Make sure we use normal (low battery) power
++	for_each_line(xpd, i)
++		if (BIT_SET(lines,i))
++			do_chan_power(xbus, xpd, BIT(i), 0);
+ 	XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
+ 	sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
+-	len = slic_cmd_direct_write(sc, lines, 0x40, (on)?0x01:0x00);
++	len = slic_cmd_direct_write(sc, lines, 0x40, value);
+ 	pack->datalen = len;
++	for_each_line(xpd, i)
++		xpd->lasttxhook[i] = value;
+ 
+ 	packet_send(xbus, pack);
+-	return ret;
+-}
+-
+-/* 0x0F */ HOSTCMD(FXS, CHAN_POWER, xpp_line_t lines, bool on)
+-{
+-	int		ret = 0;
+-	xpacket_t	*pack;
+-	slic_cmd_t	*sc;
+-	int		len;
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-	lines &= xpd->enabled_chans;	// Ignore disabled channels
+-	if(!lines) {
+-		return 0;
+-	}
+-	DBG("Channel Power: 0x%04X %s\n", lines, (on) ? "up" : "down");
+-	XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
+-	sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
++	spin_lock_irqsave(&xpd->lock, flags);
+ 	if(on) {
+-		// Power up
+-		len = slic_cmd_direct_write(sc, lines, 0x42, 0x06);
++		do_led(xpd, ALL_LINES, LED_GREEN, LED_ON);
+ 	} else {
+-		// Power down
+-		len = slic_cmd_direct_write(sc, lines, 0x42, 0x00);
++		do_led(xpd, ALL_LINES, LED_GREEN, LED_OFF);
+ 	}
+-	pack->datalen = len;
+-
+-	packet_send(xbus, pack);
++	spin_unlock_irqrestore(&xpd->lock, flags);
+ 	return ret;
+ }
+ 
+-/* 0x0F */ HOSTCMD(FXS, CHAN_CID, xpp_line_t lines)
++static /* 0x0F */ HOSTCMD(FXS, CHAN_CID, int pos)
+ {
+ 	int		ret = 0;
+ 	xpacket_t	*pack;
+ 	slic_cmd_t	*sc;
++	int		i;
++	xpp_line_t	lines = BIT(pos);
+ 
+ 	BUG_ON(!xbus);
+ 	BUG_ON(!xpd);
+@@ -238,103 +654,60 @@
+ 	if(!lines) {
+ 		return 0;
+ 	}
+-	DBG("Channel CID: 0x%04X\n", lines);
++	DBG("%s/%s/%d:\n", xbus->busname, xpd->xpdname, pos);
++	do_chan_power(xbus, xpd, BIT(pos), 0);	// Low battery for normal (non-ring) operation
+ 	XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
+ 	sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
+-	pack->datalen = slic_cmd_direct_write(sc, lines, 0x40, 0x02);
++	pack->datalen = slic_cmd_direct_write(sc, lines, 0x40, FXS_LINE_CID);
+ 	packet_send(xbus, pack);
++	for_each_line(xpd, i)
++		xpd->lasttxhook[i] = FXS_LINE_CID;
+ 	return ret;
+ }
+ 
+ 
+-/* 0x0F */ HOSTCMD(FXS, RING, int pos, bool on)
++static /* 0x0F */ HOSTCMD(FXS, RING, int pos, bool on)
+ {
+ 	int		ret = 0;
++	struct FXS_priv_data	*priv;
+ 	xpacket_t	*pack;
+ 	slic_cmd_t	*sc;
+ 	xpp_line_t	mask = (1 << pos);
+ 	int		len;
++	enum fxs_state	value = (on) ? 0x04 : 0x01;
+ 
+ 	BUG_ON(!xbus);
+ 	BUG_ON(!xpd);
++	priv = xpd->priv;
+ 	mask &= xpd->enabled_chans;	// Ignore disabled channels
+ 	if(!mask) {
+ 		return 0;
+ 	}
+-	DBG("%s pos=%d %s\n", xpd->xpdname, pos, (on) ? "on" : "off");
++	DBG("%s/%s/%d %s\n", xbus->busname, xpd->xpdname, pos, (on) ? "on" : "off");
++	do_chan_power(xbus, xpd, BIT(pos), on);		// Power up (for ring)
+ 	XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
+ 	sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
+-	len = slic_cmd_direct_write(sc, mask, 0x40, (on)?0x04:0x01);
++	len = slic_cmd_direct_write(sc, mask, 0x40, value);
++	xpd->lasttxhook[pos] = value;
+ 	pack->datalen = len;
+ 
+ 	packet_send(xbus, pack);
++	if(on) {
++		DO_BLINK(priv,pos,LED_GREEN,LED_BLINK);
++	} else {
++		if(IS_BLINKING(priv, pos, LED_GREEN))
++			DO_BLINK(priv,pos,LED_GREEN,0);
++	}
+ 	return ret;
+ }
+ 
+-/* 0x0F */ HOSTCMD(FXS, SETHOOK, xpp_line_t hook_status)
++static /* 0x0F */ HOSTCMD(FXS, SETHOOK, int pos, bool offhook)
+ {
+-	DBG("\n");
++	BUG();	// Should never be called
+ 	return 0;
+ }
+ 
+-/*
+- * LED control is done via SLIC register 0x06:
+- *         7     6     5     4     3     2     1     0
+- * 	+-----+-----+-----+-----+-----+-----+-----+-----+
+- * 	| MR  | MG  | MB  |  R  | OG  | OB  |  G  | B   |
+- * 	+-----+-----+-----+-----+-----+-----+-----+-----+
+- *
+- * 	B	- BLUE LED (0 - OFF, 1 - ON)
+- * 	G	- GREEN LED (0 - OFF, 1 - ON)
+- * 	OB	- Output BLUE (this line is output)
+- * 	OG	- Output GREEN (this line is output)
+- * 	R	- RED LED (0 - OFF, 1 - ON)
+- * 	MB	- Mask BLUE. (1 - B effect the BLUE LED)
+- * 	MR	- Mask RED. (1 - R effect the RED LED)
+- * 	MG	- Mask GREEN. (1 - G effect the GREEN LED)
+- *
+- * 	The BLUE LED (actually a relay out) is connected to line 0 and 4 only.
+- */
+-
+-//		                 		GREEN	RED	BLUE
+-static const int	led_mask[NUM_LEDS] = { 	BIT(7),	BIT(6),	BIT(5) };
+-static const int	led_vals[NUM_LEDS] = { 	BIT(4),	BIT(1),	BIT(0) };
+-
+-/* 0x0F */ HOSTCMD(FXS, LED, xpp_line_t lines, byte which, bool on)
+-{
+-	int		ret = 0;
+-	xpacket_t	*pack;
+-	slic_cmd_t	*sc;
+-	int		len;
+-	int		value;
+-	int		i;
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-	lines &= xpd->enabled_chans;	// Ignore disabled channels
+-	if(!lines) {
+-		return 0;
+-	}
+-	DBG("LED: lines=0x%04X which=%d -- %s\n", lines, which, (on) ? "on" : "off");
+-	which = which % NUM_LEDS;
+-	value = BIT(2) | BIT(3);
+-	value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_mask[which]);
+-	if(on)
+-		value |= led_vals[which];
+-	for(i = 0; i < CHANNELS_PERXPD; i++) {
+-		if(!IS_SET(lines, i))
+-				continue;
+-		XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
+-		sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
+-		len = slic_cmd_direct_write(sc, lines, 0x06, value);
+-		DBG("LED pack: line=%d value=0x%04X\n", i, value);
+-		pack->datalen = len;
+-		packet_send(xbus, pack);
+-	}
+-	return ret;
+-}
+-
+-/* 0x0F */ HOSTCMD(FXS, RELAY_OUT, byte which, bool on)
++static /* 0x0F */ HOSTCMD(FXS, RELAY_OUT, byte which, bool on)
+ {
+ 	int		ret = 0;
+ 	xpacket_t	*pack;
+@@ -351,9 +724,9 @@
+ 	which = which % ARRAY_SIZE(relay_channels);
+ 	lines = BIT(relay_channels[which]);
+ 	value = BIT(2) | BIT(3);
+-	value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_mask[LED_BLUE]);
++	value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_register_mask[OUTPUT_RELAY]);
+ 	if(on)
+-		value |= led_vals[LED_BLUE];
++		value |= led_register_vals[OUTPUT_RELAY];
+ 	XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
+ 	sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
+ 	len = slic_cmd_direct_write(sc, lines, 0x06, value);
+@@ -364,7 +737,7 @@
+ 	return ret;
+ }
+ 
+-/* 0x0F */ HOSTCMD(FXS, SLIC_INIT)
++static /* 0x0F */ HOSTCMD(FXS, SLIC_INIT)
+ {
+ 	int	ret = 0;
+ 	xpacket_t		*pack;
+@@ -386,12 +759,12 @@
+ 		pack->datalen = sizeof(xpp_line_t) + slic->len + 1;
+ //		dump_packet("SLIC", pack, print_dbg);
+ 		packet_send(xbus, pack);
+-		mdelay(10);	// FIXME: check with Dima
++		mdelay(1);	// FIXME: check with Dima
+ 	}
+ 	return ret;
+ }
+ 
+-/* 0x0F */ HOSTCMD(FXS, SLIC_QUERY, int pos, byte reg_num)
++static /* 0x0F */ HOSTCMD(FXS, SLIC_QUERY, int pos, byte reg_num)
+ {
+ 	int	ret = 0;
+ 	xpacket_t	*pack;
+@@ -400,7 +773,7 @@
+ 
+ 	BUG_ON(!xbus);
+ 	BUG_ON(!xpd);
+-	DBG("\n");
++	// DBG("\n");
+ 	XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
+ 	sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
+ 	len = slic_cmd_direct_read(sc, BIT(pos), reg_num);
+@@ -416,31 +789,45 @@
+ HANDLER_DEF(FXS, SIG_CHANGED)
+ {
+ 	xpp_line_t	sig_status = RPACKET_FIELD(pack, FXS, SIG_CHANGED, sig_status);
++	xpp_line_t	sig_toggles = RPACKET_FIELD(pack, FXS, SIG_CHANGED, sig_toggles);
++	struct FXS_priv_data	*priv;
++	int		i;
+ 
+-	if(!xpd) {
+-		NOTICE("%s: received %s for non-existing xpd: %d\n",
+-				__FUNCTION__, cmd->name, XPD_NUM(pack->content.addr));
+-		return -EPROTO;
++	BUG_ON(!xpd);
++	BUG_ON(xpd->direction != TO_PHONE);
++	priv = xpd->priv;
++	DBG("%s/%s: (PHONE) sig_toggles=0x%04X sig_status=0x%04X\n", xbus->busname, xpd->xpdname, sig_toggles, sig_status);
++	if(!SPAN_REGISTERED(xpd)) {
++		NOTICE("%s: %s/%s is not registered. Skipping.\n", __FUNCTION__, xbus->busname, xpd->xpdname);
++		return -ENODEV;
++	}
++#if 0
++	Is this needed?
++	for_each_enabled_line(xpd, i) {
++		if(IS_SET(sig_toggles, i))
++			do_chan_power(xpd->xbus, xpd, BIT(i), 0);		// Power down (prevent overheating!!!)
+ 	}
+-	if(xpd->direction == TO_PHONE) {		/* Hook state changes */
+-		DBG("%s (PHONE) sig_status=0x%04X\n", xpd->xpdname, sig_status);
+-		xpp_check_hookstate(xpd, sig_status);
+-	} else {					/* TO_PSTN - line ring changes */
+-		unsigned long	flags;
+-		int		i;
++#endif
++	for_each_line(xpd, i) {
++		if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i))
++			continue;
++		if(IS_SET(sig_toggles, i)) {
++			struct zt_chan *chan = &xpd->span.chans[i];
+ 
+-		DBG("%s (PSTN) sig_status=0x%04X\n", xpd->xpdname, sig_status);
+-		spin_lock_irqsave(&xpd->lock, flags);
+-		for(i = 0; i < xpd->channels; i++) {
++			xpd->ringing[i] = 0;		// No more ringing...
++			DO_BLINK(priv,i,LED_GREEN,0);
+ 			if(IS_SET(sig_status, i)) {
+-				xpd->ringing[i] = RINGS_NUM*2;
+-				zt_hooksig(&xpd->chans[i], ZT_RXSIG_OFFHOOK);
++				DBG("OFFHOOK: channo=%d\n", chan->channo);
++				DO_LED(priv,i,LED_GREEN,LED_ON);
++				BIT_SET(xpd->hookstate, i);
++				zt_hooksig(chan, ZT_RXSIG_OFFHOOK);
+ 			} else {
+-				zt_hooksig(&xpd->chans[i], ZT_RXSIG_ONHOOK);
+-				xpd->ringing[i] = 0;
++				DBG("ONHOOK channo=%d\n", chan->channo);
++				DO_LED(priv,i,LED_GREEN,LED_OFF);
++				BIT_CLR(xpd->hookstate, i);
++				zt_hooksig(chan, ZT_RXSIG_ONHOOK);
+ 			}
+ 		}
+-		spin_unlock_irqrestore(&xpd->lock, flags);
+ 	}
+ 	return 0;
+ }
+@@ -460,52 +847,75 @@
+ 	spin_lock_irqsave(&xpd->lock, flags);
+ 	priv = xpd->priv;
+ 	BUG_ON(!priv);
++#if 0
+ 	DBG("SLIC_REPLY: xpd #%d %s reg_num=0x%X, dataL=0x%X dataH=0x%X\n",
+ 			xpd->id, (info->indirect)?"I":"D",
+ 			info->reg_num, info->data_low, info->data_high);
+-	priv->last_reply = *info;
++#endif
+ 	if(xpd->id == 0 && info->indirect == 0 && info->reg_num == 0x06) {	/* Digital Inputs Poll Result */
+ 		int	i;
+ 		bool	offhook = (info->data_low & 0x1) == 0;
+ 
+ 		/* Map SLIC number into line number */
+ 		for(i = 0; i < ARRAY_SIZE(input_channels); i++) {
+-			int	channo = input_channels[i];
+-			int	newchanno;
++			int		channo = input_channels[i];
++			int		newchanno;
++			struct zt_chan	*chan;
+ 
+ 			if(IS_SET(lines, channo)) {
+ 				newchanno = LINES_REGULAR + LINES_DIGI_OUT + i;
+ 				BIT_CLR(lines, channo);
+ 				BIT_SET(lines, newchanno);
+-				phone_hook(xpd, newchanno, offhook);
++				chan = &xpd->span.chans[newchanno];
++				xpd->ringing[newchanno] = 0;			// Stop ringing. No leds for digital inputs.
++				if(offhook && !IS_SET(xpd->hookstate, newchanno)) {		// OFFHOOK
++					DBG("OFFHOOK: channo=%d\n", chan->channo);
++					BIT_SET(xpd->hookstate, newchanno);
++					zt_hooksig(chan, ZT_RXSIG_OFFHOOK);
++				} else if(!offhook && IS_SET(xpd->hookstate, newchanno)) {	// ONHOOK
++					DBG("ONHOOK channo=%d\n", chan->channo);
++					BIT_CLR(xpd->hookstate, newchanno);
++					zt_hooksig(chan, ZT_RXSIG_ONHOOK);
++				}
+ 			}
+ 		}
+ 	}
++
++	/* Update /proc info only if reply relate to the last slic read request */
++	if(priv->requested_reply.indirect == info->indirect &&
++			priv->requested_reply.reg_num == info->reg_num) {
++		priv->last_reply = *info;
++	}
+ 	spin_unlock_irqrestore(&xpd->lock, flags);
+ 	return 0;
+ }
+ 
+ 
+ xproto_table_t PROTO_TABLE(FXS) = {
++	.owner = THIS_MODULE,
+ 	.entries = {
+ 		/*	Card	Opcode		*/
+ 		XENTRY(	FXS,	SIG_CHANGED	),
+ 		XENTRY(	FXS,	SLIC_REPLY	),
+ 	},
+ 	.name = "FXS",
+-	.type = XPD_TYPE(FXS),
++	.type = XPD_TYPE_FXS,
+ 	.xops = {
+ 		.card_new	= FXS_card_new,
+ 		.card_init	= FXS_card_init,
+ 		.card_remove	= FXS_card_remove,
++		.card_zaptel_registration	= FXS_card_zaptel_registration,
++#ifdef WITH_RBS
++		.card_hooksig	= FXS_card_hooksig,
++#else
++		.card_sethook	= FXS_card_sethook,
++#endif
+ 		.card_tick	= FXS_card_tick,
+ 
+ 		.RING		= XPROTO_CALLER(FXS, RING),
+ 		.SETHOOK	= XPROTO_CALLER(FXS, SETHOOK),
+-		.LED		= XPROTO_CALLER(FXS, LED),
+ 		.RELAY_OUT	= XPROTO_CALLER(FXS, RELAY_OUT),
+ 		.CHAN_ENABLE	= XPROTO_CALLER(FXS, CHAN_ENABLE),
+-		.CHAN_POWER	= XPROTO_CALLER(FXS, CHAN_POWER),
+ 		.CHAN_CID	= XPROTO_CALLER(FXS, CHAN_CID),
+ 
+ 		.SYNC_SOURCE	= XPROTO_CALLER(GLOBAL, SYNC_SOURCE),
+@@ -519,7 +929,7 @@
+ {
+ 	const xproto_entry_t	*xe;
+ 
+-	DBG("\n");
++	// DBG("\n");
+ 	xe = xproto_card_entry(&PROTO_TABLE(FXS), pack->content.opcode);
+ 	return xe != NULL;
+ }
+@@ -531,6 +941,56 @@
+ 
+ /*------------------------- SLIC Handling --------------------------*/
+ 
++static int proc_fxs_info_read(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++	int			len = 0;
++	unsigned long		flags;
++	xpd_t			*xpd = data;
++	struct FXS_priv_data	*priv;
++	int			i;
++	int			led;
++
++	BUG_ON(!xpd);
++	spin_lock_irqsave(&xpd->lock, flags);
++	priv = xpd->priv;
++	BUG_ON(!priv);
++	len += sprintf(page + len, "\t%-17s: ", "Channel");
++	for_each_line(xpd, i) {
++		if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
++			len += sprintf(page + len, "%d ", i % 10);
++	}
++	len += sprintf(page + len, "\n");
++	for(led = 0; led < NUM_LEDS; led++) {
++		len += sprintf(page + len, "LED #%d", led);
++		len += sprintf(page + len, "\n\t%-17s: ", "ledstate");
++		for_each_line(xpd, i) {
++			if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
++				len += sprintf(page + len, "%d ", IS_SET(priv->ledstate[led], i));
++		}
++		len += sprintf(page + len, "\n\t%-17s: ", "ledcontrol");
++		for_each_line(xpd, i) {
++			if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
++				len += sprintf(page + len, "%d ", IS_SET(priv->ledcontrol[led], i));
++		}
++		len += sprintf(page + len, "\n\t%-17s: ", "blinking");
++		for_each_line(xpd, i) {
++			if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
++				len += sprintf(page + len, "%d ", IS_BLINKING(priv,i,led));
++		}
++		len += sprintf(page + len, "\n");
++	}
++	spin_unlock_irqrestore(&xpd->lock, flags);
++	if (len <= off+count)
++		*eof = 1;
++	*start = page + off;
++	len -= off;
++	if (len > count)
++		len = count;
++	if (len < 0)
++		len = 0;
++	return len;
++}
++
+ static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+ {
+ 	int			len = 0;
+@@ -570,7 +1030,7 @@
+  *      |
+  *    SLIC #
+  */
+-static int parse_slic_cmd(const char *buf, slic_cmd_t *sc)
++static int parse_slic_cmd(const char *buf, slic_cmd_t *sc, slic_reply_t *requested_reply)
+ {
+ 	char		op;		/* [W]rite, [R]ead */
+ 	char		reg_type;	/* [D]irect, [I]ndirect */
+@@ -588,9 +1048,17 @@
+ 			if(reg_type == 'D' && ret == 7) {
+ 				// DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num);
+ 				ret = slic_cmd_direct_read(sc, lines, reg_num);
++				if(requested_reply) {
++					requested_reply->indirect = 0;
++					requested_reply->reg_num = reg_num;
++				}
+ 			} else if(reg_type == 'I' && ret == 7) {
+ 				// DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num);
+ 				ret = slic_cmd_indirect_read(sc, lines, reg_num);
++				if(requested_reply) {
++					requested_reply->indirect = 1;
++					requested_reply->reg_num = reg_num;
++				}
+ 			} else {
+ 				NOTICE("%s: Bad read input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type);
+ 				goto err;
+@@ -619,14 +1087,16 @@
+ 
+ static int process_slic_cmdline(xpd_t *xpd, char *cmdline)
+ {
+-	xbus_t		*xbus;
+-	slic_cmd_t	sc;
+-	xpacket_t	*pack;
+-	char		*p;
+-	int		len = strlen(cmdline);
++	xbus_t			*xbus;
++	struct FXS_priv_data	*priv;
++	slic_cmd_t		sc;
++	xpacket_t		*pack;
++	char			*p;
++	int			len = strlen(cmdline);
+ 
+ 	BUG_ON(!xpd);
+ 	xbus = xpd->xbus;
++	priv = xpd->priv;
+ 	if((p = strchr(cmdline, '#')) != NULL)	/* Truncate comments */
+ 		*p = '\0';
+ 	if((p = strchr(cmdline, ';')) != NULL)	/* Truncate comments */
+@@ -635,7 +1105,7 @@
+ 		;
+ 	if(*p == '\0')
+ 		return 0;
+-	len = parse_slic_cmd(p, &sc);
++	len = parse_slic_cmd(p, &sc, &priv->requested_reply);
+ 	if(len < 0)
+ 		return len;
+ 	sc.lines &= xpd->enabled_chans;	// Ignore disabled channels
+@@ -677,6 +1147,7 @@
+ 		ret = process_slic_cmdline(xpd, buf);
+ 		if(ret < 0)
+ 			return ret;
++		mdelay(1);
+ 	}
+ 	return count;
+ }
+@@ -684,7 +1155,7 @@
+ 
+ int __init card_fxs_startup(void)
+ {
+-	INFO("%s revision %s\n", THIS_MODULE->name, revision);
++	INFO("%s revision %s\n", THIS_MODULE->name, ZAPTEL_VERSION);
+ 	xproto_register(&PROTO_TABLE(FXS));
+ 	return 0;
+ }
+@@ -697,7 +1168,8 @@
+ MODULE_DESCRIPTION("XPP FXS Card Driver");
+ MODULE_AUTHOR("Oron Peled <oron at actcom.co.il>");
+ MODULE_LICENSE("GPL");
+-MODULE_VERSION("$Id: card_fxs.c 995 2006-04-03 07:08:13Z tzafrir $");
++MODULE_VERSION(ZAPTEL_VERSION);
++MODULE_ALIAS_XPD(XPD_TYPE_FXS);
+ 
+ module_init(card_fxs_startup);
+ module_exit(card_fxs_cleanup);
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/card_fxs.h zaptel-xpp-XJ8JZx_dist/xpp/card_fxs.h
+--- zaptel-1.2.6/xpp/card_fxs.h	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/card_fxs.h	2006-03-30 12:42:46.619481000 +0200
+@@ -2,7 +2,7 @@
+ #define	CARD_FXS_H
+ /*
+  * Written by Oron Peled <oron at actcom.co.il>
+- * Copyright (C) 2004-2005, Xorcom
++ * Copyright (C) 2004-2006, Xorcom
+  *
+  * All rights reserved.
+  *
+@@ -25,6 +25,24 @@
+ #include "xpd.h"
+ #include "slic.h"
+ 
++enum fxs_opcodes {
++	XPROTO_NAME(FXS, SIG_CHANGED)		= 0x06,
++/**/
++	XPROTO_NAME(FXS, SLIC_WRITE)		= 0x0F,	/* Write to SLIC */
++	XPROTO_NAME(FXS, CHAN_ENABLE)		= 0x0F,	/* Write to SLIC */
++	XPROTO_NAME(FXS, CHAN_POWER)		= 0x0F,	/* Write to SLIC */
++	XPROTO_NAME(FXS, CHAN_CID)		= 0x0F,	/* Write to SLIC */
++	XPROTO_NAME(FXS, RING)			= 0x0F,	/* Write to SLIC */
++	XPROTO_NAME(FXS, SETHOOK)		= 0x0F,	/* Write to SLIC */
++	XPROTO_NAME(FXS, LED)			= 0x0F,	/* Write to SLIC */
++	XPROTO_NAME(FXS, RELAY_OUT)		= 0x0F,	/* Write to SLIC */
++	XPROTO_NAME(FXS, SLIC_INIT)		= 0x0F,	/* Write to SLIC */
++	XPROTO_NAME(FXS, SLIC_QUERY)		= 0x0F,	/* Write to SLIC */
++/**/
++	XPROTO_NAME(FXS, SLIC_REPLY)		= 0x10,
++};
++
++
+ DEF_RPACKET_DATA(FXS, SIG_CHANGED,
+ 	byte		type;		/* unused -- we have it from DEV_DESC */
+ 	xpp_line_t	sig_status;	/* channels: lsb=1, msb=8 */
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/card_global.c zaptel-xpp-XJ8JZx_dist/xpp/card_global.c
+--- zaptel-1.2.6/xpp/card_global.c	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/card_global.c	2006-03-30 12:42:46.619481000 +0200
+@@ -1,6 +1,6 @@
+ /*
+  * Written by Oron Peled <oron at actcom.co.il>
+- * Copyright (C) 2004-2005, Xorcom
++ * Copyright (C) 2004-2006, Xorcom
+  *
+  * All rights reserved.
+  *
+@@ -24,9 +24,10 @@
+ #include "xpd.h"
+ #include "xpp_zap.h"
+ #include "xproto.h"
++#include "zap_debug.h"
+ #include <linux/module.h>
+ 
+-static const char rcsid[] = "$Id: card_global.c 949 2006-02-15 02:24:18Z kpfleming $";
++static const char rcsid[] = "$Id: card_global.c 733 2006-03-30 10:42:46Z oron $";
+ 
+ extern	int print_dbg;
+ static bool pcm_valid(xpd_t *xpd, xpacket_t *pack);
+@@ -65,7 +66,7 @@
+ 
+ 	BUG_ON(!xbus);
+ 	BUG_ON(!xpd);
+-	lines &= xpd->enabled_chans;
++	lines &= (xpd->enabled_chans | ~xpd->no_pcm);
+ 	if(pcm_gen != 0)
+ 		return 0;
+ //	if(lines == 0)
+@@ -81,7 +82,7 @@
+ 	XPACKET_NEW(pack, xbus, GLOBAL, PCM_WRITE, xpd->id);
+ 	RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines) = lines;
+ 	start_pcm = pcm = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, pcm);
+-	for(i = 0; i < CHANNELS_PERXPD; i++) {
++	for_each_line(xpd, i) {
+ 		if(IS_SET(lines, i)) {
+ 			memcpy(pcm, (byte *)buf, ZT_CHUNKSIZE);
+ 			pcm += ZT_CHUNKSIZE;
+@@ -177,7 +178,7 @@
+ 	}
+ 
+ 	/* Copy PCM and put each channel in its index */
+-	for (i = 0; i < CHANNELS_PERXPD; i++) {
++	for_each_line(xpd, i) {
+ 		if(IS_SET(lines, i)) {
+ 			memcpy((u_char *)r, pcm, ZT_CHUNKSIZE);
+ 			//memset((u_char *)r, 0x5A, ZT_CHUNKSIZE);	// DEBUG
+@@ -195,12 +196,18 @@
+ 
+ HANDLER_DEF(GLOBAL, SYNC_REPLY)
+ {
++	byte	mask = RPACKET_FIELD(pack, GLOBAL, SYNC_REPLY, mask);
++	bool	setit = mask & 0x01;
++
+ 	if(!xpd) {
+ 		int xpd_num = XPD_NUM(pack->content.addr);
+ 		NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num);
+ 		return -EPROTO;
+ 	}
+-	DBG("SYNC_REPLY: 0x%X\n", RPACKET_FIELD(pack, GLOBAL, SYNC_REPLY, mask));
++	DBG("%s/%s: SYNC_REPLY: 0x%X %s\n", xpd->xbus->busname, xpd->xpdname,
++			mask, (setit) ? "SET SYNC MASTER" : "");
++	if(setit)
++		sync_master_is(xpd);
+ 	return 0;
+ }
+ 
+@@ -239,7 +246,7 @@
+ 
+ 	BUG_ON(!pack);
+ 	BUG_ON(pack->content.opcode != XPROTO_NAME(GLOBAL, PCM_READ));
+-	for (i = 0; i < CHANNELS_PERXPD; i++)
++	for_each_line(xpd, i)
+ 		if(IS_SET(lines, i))
+ 			count++;
+ 	if(pack->datalen != (sizeof(xpp_line_t) + count * 8)) {
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/card_global.h zaptel-xpp-XJ8JZx_dist/xpp/card_global.h
+--- zaptel-1.2.6/xpp/card_global.h	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/card_global.h	2006-03-30 12:42:46.619481000 +0200
+@@ -2,7 +2,7 @@
+ #define	CARD_GLOBAL_H
+ /*
+  * Written by Oron Peled <oron at actcom.co.il>
+- * Copyright (C) 2004-2005, Xorcom
++ * Copyright (C) 2004-2006, Xorcom
+  *
+  * All rights reserved.
+  *
+@@ -31,11 +31,11 @@
+ 	xpp_line_t	line_status;	/* hook/ring status, depending on unit */
+ 	);
+ DEF_RPACKET_DATA(GLOBAL, PCM_WRITE,
+-	xpp_line_t	lines;	// Must be 0xFF
++	xpp_line_t	lines;
+ 	byte		pcm[PCM_CHUNKSIZE];
+ 	);
+ DEF_RPACKET_DATA(GLOBAL, PCM_READ,
+-	xpp_line_t	lines;	// Must be 0xFF
++	xpp_line_t	lines;
+ 	byte		pcm[PCM_CHUNKSIZE];
+ 	);
+ DEF_RPACKET_DATA(GLOBAL, SYNC_SOURCE,
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/cards.c zaptel-xpp-XJ8JZx_dist/xpp/cards.c
+--- zaptel-1.2.6/xpp/cards.c	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/cards.c	1970-01-01 02:00:00.000000000 +0200
+@@ -1,394 +0,0 @@
+-#include <linux/module.h>
+-#include "xpd.h"
+-#include "xpp_zap.h"
+-#include "xpp_proto.h"
+-#include "cards.h"
+-
+-static char rcsid[] = "$Id: cards.c 949 2006-02-15 02:24:18Z kpfleming $";
+-
+-extern	int print_dbg;
+-#include "zap_debug.h"
+-
+-#define	MAX_SLIC_REGISTERS	100
+-
+-struct FXS_private_data {
+-	slic_reply_t	last_slic_reply;
+-};
+-
+-/*------------------------- FXS Functions --------------------------*/
+-int	FXS_card_new(xpd_t *xpd)
+-{
+-	xpd->direction = TO_PHONE;
+-	xpd->channels = min(8, CHANNELS_PERXPD);
+-	if(xpd->id == 0) {
+-		DBG("First XPD detected. Initialize digital outputs\n");
+-		xpd->channels += 2;
+-		xpd->digital_outputs = BIT(8) | BIT(9);	// Two extra channels
+-	}
+-	return 0;
+-}
+-
+-int	FXS_card_remove(xpd_t *xpd)
+-{
+-	return 0;
+-}
+-
+-static int FXS_card_startup(struct zt_span *span)
+-{
+-	DBG("\n");
+-	return 0;
+-}
+-
+-/*
+- * Called only for 'span' keyword in /etc/zaptel.conf
+- */
+-static int FXS_card_spanconfig(struct zt_span *span, struct zt_lineconfig *lc)
+-{
+-	xpd_t *xpd = span->pvt;
+-
+-	DBG("%s\n", xpd->xpdname);
+-	return 0;
+-}
+-
+-/* Set signalling type (if appropriate) */
+-static int FXS_card_chanconfig(struct zt_chan *chan, int sigtype)
+-{
+-	DBG("channel %d (%s), sigtype %d.\n", chan->channo, chan->name, sigtype);
+-	dump_sigtype(print_dbg, "  ", sigtype);
+-	// FIXME: sanity checks:
+-	// - should be supported (within the sigcap)
+-	// - should not replace fxs <->fxo ??? (covered by previous?)
+-	return 0;
+-}
+-
+-/*
+- * Called only for 'span' keyword in /etc/zaptel.conf
+- */
+-static int FXS_card_shutdown(struct zt_span *span)
+-{
+-	xpd_t *xpd = span->pvt;
+-
+-	DBG("%s\n", xpd->xpdname);
+-	return 0;
+-}
+-
+-static int FXS_card_sethook(struct zt_chan *chan, int hookstate)
+-{
+-	int pos = chan->chanpos - 1;
+-	xpd_t	*xpd = chan->pvt;
+-	xbus_t	*xbus;
+-	int ret = 0;
+-
+-	if(!xpd) {
+-		ERR("%s: channel=%d without an XPD!\n", __FUNCTION__, pos);
+-		return -EINVAL;
+-	}
+-	xbus = xpd->xbus;
+-	// DBG("%s (%d) (old=0x%04X, hook-command=%d)\n", chan->name, pos, xpd->hookstate, hookstate);
+-	switch(hookstate) {
+-		/* On-hook, off-hook: The PBX is playing a phone on an FXO line. 
+-		 * Can be ignored for an FXS line
+-		 */
+-		case ZT_ONHOOK:
+-			if(IS_SET(xpd->digital_outputs, pos)) {
+-				DBG("ZT_ONHOOK %s digital output OFF\n", chan->name);
+-				ret = CALL_PROTO(RELAY_OUT, xpd->xbus, xpd, pos-8, 0);
+-				return ret;
+-			}
+-			DBG("ZT_ONHOOK: %s hookstate=0x%04X (stop ringing pos=%d)\n", chan->name, xpd->hookstate, pos);
+-			xpd->ringing[pos] = 0;
+-#if 1	// FIXME: Not needed -- verify
+-			ret = CALL_PROTO(RING, xbus, xpd, pos, 0);			// RING off
+-#endif
+-			ret = CALL_PROTO(LED, xpd->xbus, xpd, BIT(pos), LED_GREEN, 0);
+-			ret = CALL_PROTO(CHAN_POWER, xbus, xpd, BIT(pos), 0);		// Power down (prevent overheating!!!)
+-			if(ret) {
+-				DBG("ZT_ONHOOK(stop ring) Failed: ret=0x%02X\n", ret);
+-				break;
+-			}
+-			break;
+-		case ZT_START:
+-			DBG("ZT_START: %s hookstate=0x%04X (fall through ZT_OFFHOOK)\n", chan->name, xpd->hookstate);
+-			// Fall through
+-		case ZT_OFFHOOK:
+-			DBG("ZT_OFFHOOK: %s hookstate=0x%04X -- ignoring (FXS)\n", chan->name, xpd->hookstate);
+-			break;
+-		case ZT_WINK:
+-			DBG("ZT_WINK %s\n", chan->name);
+-			break;
+-		case ZT_FLASH:
+-			DBG("ZT_FLASH %s\n", chan->name);
+-			break;
+-		case ZT_RING:
+-			DBG("ZT_RING %s pos=%d (ringing[pos]=%d)\n", chan->name, pos, xpd->ringing[pos]);
+-			if(IS_SET(xpd->digital_outputs, pos)) {
+-				DBG("ZT_ONHOOK %s digital output ON\n", chan->name);
+-				ret = CALL_PROTO(RELAY_OUT, xpd->xbus, xpd, pos-8, 1);
+-				return ret;
+-			}
+-			xpd->ringing[pos] = RINGS_NUM*2;
+-			ret = CALL_PROTO(CHAN_POWER, xbus, xpd, (1 << pos), 1);	// Power up (for ring)
+-			ret = CALL_PROTO(RING, xbus, xpd, pos, 1);			// RING on
+-			if(ret) {
+-				DBG("ZT_RING Failed: ret=0x%02X\n", ret);
+-			}
+-			break;
+-		case ZT_RINGOFF:
+-			DBG("ZT_RINGOFF %s\n", chan->name);
+-			break;
+-		default:
+-			DBG("UNKNOWN hookstate=0x%X\n", hookstate);
+-	}
+-	return ret;
+-}
+-
+-static int FXS_card_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg)
+-{
+-	switch (cmd) {
+-	default:
+-		return xpp_ioctl(chan, cmd, arg);
+-	}
+-	return 0;
+-}
+-
+-#if 0
+-int FXS_zaptel_setup(xpd_t *xpd)
+-{
+-	struct zt_chan	*cur_chan;
+-	struct zt_span	*span;
+-	xbus_t		*xbus;
+-	int		i;
+-	int		cn;
+-
+-	BUG_ON(!xpd);
+-
+-	sigfxs = ! (xpd->direction == TO_PHONE);	/* signaling is opposite */
+-	cn = xpd->channels;
+-	DBG("Initializing span: xpd %d have %d channels.\n", xpd->id, cn);
+-
+-	xpd->chans = kmalloc(sizeof(struct zt_chan)*cn, GFP_ATOMIC);
+-	if (xpd->chans == NULL) {
+-		ERR("xpd: Unable to allocate channels\n");
+-		return -ENOMEM;
+-	}
+-	memset(xpd->chans, 0, sizeof(struct zt_chan)*cn);
+-	memset(&xpd->span, 0, sizeof(struct zt_span));
+-
+-	span = &xpd->span;
+-	xbus = xpd->xbus;
+-	snprintf(span->name, MAX_SPANNAME, "%s/%s",
+-			xbus->busname, xpd->xpdname);
+-	{
+-		char tmp[MAX_SPANNAME];
+-		struct xpd_sim	*sim = &xbus->sim[xpd->id];
+-
+-		if(sim->simulated)
+-			snprintf(tmp, MAX_SPANNAME, " (sim to=%d)", sim->loopto);
+-		else
+-			tmp[0] = '\0';
+-
+-		snprintf(span->desc, MAX_SPANDESC, "Xorcom XPD #%d/%d: %s%s",
+-				xbus->num, xpd->id,
+-				(xpd->direction == TO_PHONE) ? "FXS" : "FXO",
+-				tmp
+-				);
+-	}
+-	for(i = 0; i < cn; i++) {
+-		
+-		cur_chan = &xpd->chans[i];
+-		DBG("setting channel %d\n", i);
+-		snprintf(cur_chan->name, MAX_CHANNAME, "XPP_FXS/%d-%d", xpd->id, i);
+-		cur_chan->chanpos = i + 1;
+-		cur_chan->pvt = xpd;
+-		cur_chan->sigcap =
+-#if 1
+-				ZT_SIG_FXOKS	|
+-				ZT_SIG_FXOLS	|
+-				ZT_SIG_FXOGS	|
+-#else
+-				ZT_SIG_SF	|
+-				ZT_SIG_EM	|
+-#endif
+-				0;
+-	}
+-	span->deflaw = ZT_LAW_MULAW;
+-	init_waitqueue_head(&span->maintq);
+-	span->pvt = xpd;
+-	span->channels = cn;
+-	span->chans = xpd->chans;
+-
+-	span->startup = FXS_xpp_startup;
+-	span->shutdown = FXS_xpp_shutdown;
+-	span->spanconfig = FXS_xpp_spanconfig;
+-	span->chanconfig = FXS_xpp_chanconfig;
+-	span->open = xpp_open;
+-	span->close = xpp_close;
+-#ifdef	WITH_RBS
+-	span->flags = ZT_FLAG_RBS;
+-	span->hooksig = xpp_hooksig;	/* Only with RBS bits */
+-#else
+-	span->sethook = FXS_xpp_sethook;
+-#endif
+-	span->ioctl = FXS_xpp_ioctl;
+-	span->maint = xpp_maint;
+-#ifdef	CONFIG_ZAPTEL_WATCHDOG
+-	span->watchdog = xpp_watchdog;
+-#endif
+-
+-	return 0;
+-}
+-#endif
+-
+-int FXS_zaptel_cleanup(xpd_t *xpd)
+-{
+-	return 0;
+-}
+-
+-/*------------------------- FXO Functions --------------------------*/
+-static int FXO_card_new(xpd_t *xpd)
+-{
+-	xpd->direction = TO_TRUNK;
+-	xpd->channels = min(8, CHANNELS_PERXPD);
+-	return 0;
+-}
+-
+-static int FXO_card_remove(xpd_t *xpd)
+-{
+-	return 0;
+-}
+-
+-static int FXO_card_startup(struct zt_span *span)
+-{
+-	DBG("\n");
+-	return 0;
+-}
+-
+-/*
+- * Called only for 'span' keyword in /etc/zaptel.conf
+- */
+-static int FXO_card_spanconfig(struct zt_span *span, struct zt_lineconfig *lc)
+-{
+-	xpd_t *xpd = span->pvt;
+-
+-	DBG("%s\n", xpd->xpdname);
+-	return 0;
+-}
+-
+-/* Set signalling type (if appropriate) */
+-static int FXO_card_chanconfig(struct zt_chan *chan, int sigtype)
+-{
+-	DBG("channel %d (%s), sigtype %d.\n", chan->channo, chan->name, sigtype);
+-	dump_sigtype(print_dbg, "  ", sigtype);
+-	// FIXME: sanity checks:
+-	// - should be supported (within the sigcap)
+-	// - should not replace fxs <->fxo ??? (covered by previous?)
+-	return 0;
+-}
+-
+-/*
+- * Called only for 'span' keyword in /etc/zaptel.conf
+- */
+-static int FXO_card_shutdown(struct zt_span *span)
+-{
+-	xpd_t *xpd = span->pvt;
+-
+-	DBG("%s\n", xpd->xpdname);
+-	return 0;
+-}
+-
+-
+-static int FXO_card_sethook(struct zt_chan *chan, int hookstate)
+-{
+-	int pos = chan->chanpos - 1;
+-	xpd_t	*xpd = chan->pvt;
+-	xbus_t	*xbus;
+-	int ret = 0;
+-
+-	BUG_ON(!xpd);
+-	xbus = xpd->xbus;
+-	// DBG("%s (%d) (old=0x%04X, hook-command=%d)\n", chan->name, pos, xpd->hookstate, hookstate);
+-	switch(hookstate) {
+-		/* On-hook, off-hook: The PBX is playing a phone on an FXO line. 
+-		 * Can be ignored for an FXS line
+-		 */
+-		case ZT_ONHOOK:
+-			if(IS_SET(xpd->digital_outputs, pos)) {
+-				DBG("ZT_ONHOOK %s digital output OFF\n", chan->name);
+-				ret = CALL_PROTO(RELAY_OUT, xpd->xbus, xpd, pos-8, 0);
+-				return ret;
+-			}
+-			DBG("ZT_ONHOOK: %s hookstate=0x%04X (pos=%d)\n", chan->name, xpd->hookstate, pos);
+-			xpd->ringing[pos] = 0;
+-			BIT_CLR(xpd->hookstate, pos);
+-			ret = CALL_PROTO(SETHOOK, xbus, xpd, xpd->hookstate);
+-			if(ret) {
+-				DBG("ZT_ONHOOK Failed: ret=0x%02X\n", ret);
+-				break;
+-			}
+-			break;
+-		case ZT_START:
+-			DBG("ZT_START: %s hookstate=0x%04X (fall through ZT_OFFHOOK)\n", chan->name, xpd->hookstate);
+-			// Fall through
+-		case ZT_OFFHOOK:
+-			DBG("ZT_OFFHOOK: %s hookstate=0x%04X (pos=%d)\n", chan->name, xpd->hookstate, pos);
+-			BIT_SET(xpd->hookstate, pos);
+-			xpd->ringing[pos] = 0;
+-			ret = CALL_PROTO(SETHOOK, xbus, xpd, xpd->hookstate);
+-			if(ret) {
+-				DBG("ZT_OFFHOOK Failed: ret=0x%02X\n", ret);
+-				break;
+-			}
+-			break;
+-		case ZT_WINK:
+-			DBG("ZT_WINK %s\n", chan->name);
+-			break;
+-		case ZT_FLASH:
+-			DBG("ZT_FLASH %s\n", chan->name);
+-			break;
+-		case ZT_RING:
+-			DBG("ZT_RING %s pos=%d (ringing[pos]=%d)\n", chan->name, pos, xpd->ringing[pos]);
+-			break;
+-		case ZT_RINGOFF:
+-			DBG("ZT_RINGOFF %s\n", chan->name);
+-			break;
+-		default:
+-			DBG("UNKNOWN hookstate=0x%X\n", hookstate);
+-	}
+-	return ret;
+-}
+-
+-static int FXO_card_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg)
+-{
+-	switch (cmd) {
+-	default:
+-		return xpp_ioctl(chan, cmd, arg);
+-	}
+-	return 0;
+-}
+-
+-
+-/*------------------------- Function table -------------------------*/
+-
+-#define	DEF_(t)	\
+-	[XPD_TYPE_ ## t] {					\
+-		.card_new = t ## _card_new,			\
+-		.card_remove = t ## _card_remove,		\
+-		.card_startup = t ## _card_startup,		\
+-		.card_shutdown = t ## _card_shutdown,		\
+-		.card_spanconfig = t ## _card_spanconfig,	\
+-		.card_chanconfig = t ## _card_chanconfig,	\
+-		.card_sethook = t ## _card_sethook,		\
+-		.card_ioctl = t ## _card_ioctl,			\
+-		}
+-
+-xops_t	xpd_card_ops[XPD_TYPE_NOMODULE] = {
+-	DEF_(FXS),
+-	DEF_(FXO)
+-};
+-
+-xops_t	*get_xops(xpd_type_t xpd_type)
+-{
+-	if(xpd_type >= XPD_TYPE_NOMODULE)
+-		return NULL;
+-	return &xpd_card_ops[xpd_type];
+-}
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/cards.h zaptel-xpp-XJ8JZx_dist/xpp/cards.h
+--- zaptel-1.2.6/xpp/cards.h	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/cards.h	1970-01-01 02:00:00.000000000 +0200
+@@ -1,23 +0,0 @@
+-#ifndef	CARDS_H
+-#define	CARDS_H
+-
+-#include "xpd.h"
+-
+-struct xpd_card_ops {
+-	int	(*card_new)(xpd_t *xpd);
+-	int	(*card_remove)(xpd_t *xpd);
+-#if 0
+-	int	(*zaptel_setup)(xpd_t *xpd);
+-	int	(*zaptel_cleanup)(xpd_t *xpd);
+-#endif
+-	int	(*card_startup)(struct zt_span *span);
+-	int	(*card_shutdown)(struct zt_span *span);
+-	int	(*card_spanconfig)(struct zt_span *span, struct zt_lineconfig *lc);
+-	int	(*card_chanconfig)(struct zt_chan *chan, int sigtype);
+-	int	(*card_sethook)(struct zt_chan *chan, int hookstate);
+-	int	(*card_ioctl)(struct zt_chan *chan, unsigned int cmd, unsigned long arg);
+-};
+-
+-xops_t *get_xops(xpd_type_t xpd_type);
+-
+-#endif	/* CARDS_H */
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/cmd2inc zaptel-xpp-XJ8JZx_dist/xpp/cmd2inc
+--- zaptel-1.2.6/xpp/cmd2inc	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/cmd2inc	2006-03-01 14:30:36.514598000 +0200
+@@ -0,0 +1,48 @@
++#! /usr/bin/perl -w
++
++use strict;
++
++my $input;
++my $header;
++my $comment;
++my $len;
++
++ at ARGV == 1 or @ARGV == 2 or die "Usage: $0 <infile> <outfile>\n";
++$input = $ARGV[0];
++$header = $ARGV[1];
++open(IF, "$input") or die "Failed to open '$input': $!\n";
++
++if($header) {
++	open(HF, ">$header") or die "Failed to write '$header': $!\n";
++	select HF;
++}
++
++while(<IF>) {
++	chomp;
++	undef $comment;
++	s/\r//;				# CRLF -> LF
++	if(s/\s*[;#]\s*(.*?)$//) {	# Comments
++		$comment = $1;
++	}
++	if(/^\s*$/) {		# Empty lines
++		next;
++	}
++	my ($slic0, $slic1, $slic2, $slic3, $op, @data) = split;
++	my $slic = "$slic3$slic2$slic1$slic0";
++	die "Bad slic address '$slic'" if length($slic) != 8;
++	die "Bad op (op=%op)\n" if $op !~ /^[WR][DI]$/;
++	if($op =~ /D$/) {	# Direct
++		die "Bad input at line $.: @data=(@data)\n" if @data != 2;
++		#                   len      reg    data
++		printf "S_(0x$slic,\t0x02,\t0x%02s, 0x%02s),\t", $data[0], $data[1];
++	} else {		# Indirect
++		die "Bad input at line $.: @data=(@data)\n" if @data != 3;
++		#                   len          data1         data2          reg
++		printf "S_(0x$slic,\t0x06,\t0x1C, 0x%02s, 0x1D, 0x%02s, 0x1E, 0x%2s),\t", $data[2], $data[1], $data[0];
++	}
++} continue {
++	if(defined($comment)) {
++		print "// $comment";
++	}
++	print "\n";
++}
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/FPGA_XPD.hex zaptel-xpp-XJ8JZx_dist/xpp/FPGA_XPD.hex
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/gen_slic_init zaptel-xpp-XJ8JZx_dist/xpp/gen_slic_init
+--- zaptel-1.2.6/xpp/gen_slic_init	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/gen_slic_init	1970-01-01 02:00:00.000000000 +0200
+@@ -1,37 +0,0 @@
+-#! /usr/bin/perl -w
+-
+-use strict;
+-
+-my $input;
+-my $header;
+-my $comment;
+-
+- at ARGV == 2 or die "Usage: $0 <infile> <outfile>\n";
+-$input = $ARGV[0];
+-$header = $ARGV[1];
+-open(IF, "$input") or die "Failed to write '$input': $!\n";
+-open(HF, ">$header") or die "Failed to write '$header': $!\n";
+-
+-while(<IF>) {
+-	chomp;
+-	undef $comment;
+-	s/\r//;				# CRLF -> LF
+-	if(s/\s*[;#]\s*(.*?)$//) {	# Comments
+-		$comment = $1;
+-	}
+-	if(/^\s*$/) {		# Empty lines
+-		next;
+-	}
+-	my ($slic0, $slic1, $slic2, $slic3, $len, @data) = split;
+-	die "Bad input (len=$len)" if hex($len) != @data;
+-	my $slic = "$slic3$slic2$slic1$slic0";
+-	die "Bad slic address '$slic'" if length($slic) != 8;
+-	grep(s/\w+/0x$&/g, ($slic, $len, @data));
+-	my $str = join(", ", @data);
+-	print HF "S_($slic,\t$len,\t$str),\t";
+-} continue {
+-	if(defined($comment)) {
+-		print HF "// $comment";
+-	}
+-	print HF "\n";
+-}
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/init_data_3_19.cmd zaptel-xpp-XJ8JZx_dist/xpp/init_data_3_19.cmd
+--- zaptel-1.2.6/xpp/init_data_3_19.cmd	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/init_data_3_19.cmd	2006-06-26 11:48:58.007317000 +0300
+@@ -0,0 +1,169 @@
++#
++# Written by Oron Peled <oron at actcom.co.il>
++# Copyright (C) 2006, Xorcom
++#
++# 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.
++#
++# See the file LICENSE in the top level of this tarball.
++#
++
++;
++; $Id: init_data_fxs.cmd 540 2006-03-01 12:30:36Z oron $
++;
++; SLICS		CMD	Reg	High	Low
++
++; ----------------------------------==== 8-channel FXS unit initialization ===-----------------------------------------
++; INTERNAL PS
++; Change SLICs states to "Open state"s  (Off,all transfers tristated to avoid data collision), Voltage sense
++
++FF 00 00 00	WD	40	00
++FF 00 00 00	WD	6C	01
++
++; ------------------------------------- Initialization of indirect registers ------------------------------------------
++
++FF 00 00 00	WI	00	55	C2
++FF 00 00 00	WI	01	51	E6
++FF 00 00 00	WI	02	4B	85
++FF 00 00 00	WI	03	49	37
++
++FF 00 00 00	WI	04	33	33
++FF 00 00 00	WI	05	02	02
++FF 00 00 00	WI	06	02	02
++FF 00 00 00	WI	07	01	98
++
++FF 00 00 00	WI	08	01	98
++FF 00 00 00	WI	09	06	11
++FF 00 00 00	WI	0A	02	02
++FF 00 00 00	WI	0B	00	E5
++
++FF 00 00 00	WI	0C	0A	1C
++FF 00 00 00	WI	0D	7B	30
++FF 00 00 00	WI	0E	00	63
++FF 00 00 00	WI	0F	00	00
++
++FF 00 00 00	WI	10	78	70
++FF 00 00 00	WI	11	00	7D
++FF 00 00 00	WI	12	00	00
++FF 00 00 00	WI	13	00	00
++
++FF 00 00 00	WI	14	7E	F0
++FF 00 00 00	WI	15	01	60
++FF 00 00 00	WI	16	00	00
++FF 00 00 00	WI	17	20	00
++
++FF 00 00 00	WI	18	20	00
++FF 00 00 00	WI	19	00	00
++FF 00 00 00	WI	1A	40	00
++FF 00 00 00	WI	1B	40	00
++
++FF 00 00 00	WI	1C	18	00
++FF 00 00 00	WI	1D	40	00
++FF 00 00 00	WI	1E	10	00
++FF 00 00 00	WI	1F	00	80
++
++FF 00 00 00	WI	20	0F	F4
++FF 00 00 00	WI	21	6E	7E
++FF 00 00 00	WI	22	0F	F4
++FF 00 00 00	WI	23	88	00
++
++FF 00 00 00	WI	24	03	20
++FF 00 00 00	WI	25	00	12
++FF 00 00 00	WI	26	00	12
++FF 00 00 00	WI	27	00	12
++
++FF 00 00 00	WI	28	0C	00
++FF 00 00 00	WI	29	0C	00
++FF 00 00 00	WI	2B	08	00
++
++FF 00 00 00	WI	63	00	DA
++FF 00 00 00	WI	64	6B	60
++FF 00 00 00	WI	65	00	74
++FF 00 00 00	WI	66	79	C0
++
++FF 00 00 00	WI	67	11	20
++FF 00 00 00	WI	68	3B	E0	
++
++; ------------------------------------- Initialization of direct registers --------------------------------------------
++
++; Mode(8-bit,u-Law,1 PCLK ) setting, Loopbacks and Interrupts clear
++
++FF 00 00 00	WD	01 29
++FF 00 00 00	WD	08 00
++FF 00 00 00	WD	09 00
++FF 00 00 00	WD	0E 00  
++
++FF 00 00 00	WD	15 00
++FF 00 00 00	WD	16 03
++FF 00 00 00	WD	17 00
++FF 00 00 00	WD	12 FF
++FF 00 00 00	WD	13 FF
++FF 00 00 00	WD	14 FF 
++
++; Automatic/Manual Control: defaults - Cancel Power Alarm
++FF 00 00 00	WD	43 1E
++
++FF 00 00 00	WD	4A 31
++FF 00 00 00	WD	4B 10
++
++; Battery Feed Control: Battery low (DCSW low)
++FF 00 00 00	WD	42 00
++
++; Slic Calibration
++FF 00 00 00	WD	61 1F
++FF 00 00 00	WD	60 5F
++
++; Loop Closure Debounce Interval
++FF 00 00 00	WD	45 0A
++
++; Ring Detect Debounce Interval
++FF 00 00 00	WD	46 0B
++
++; Loop Current Limit
++FF 00 00 00	WD	47 07
++
++; Setting of SLICs offsets
++
++01 00 00 00	WD	02 01
++01 00 00 00	WD	03 00
++01 00 00 00	WD	04 01
++01 00 00 00	WD	05 00
++
++02 00 00 00	WD	02 09
++02 00 00 00	WD	03 00
++02 00 00 00	WD	04 09
++02 00 00 00	WD	05 00
++
++04 00 00 00	WD	02 11
++04 00 00 00	WD	03 00
++04 00 00 00	WD	04 11
++04 00 00 00	WD	05 00
++
++08 00 00 00	WD	02 19
++08 00 00 00	WD	03 00
++08 00 00 00	WD	04 19
++08 00 00 00	WD	05 00
++
++10 00 00 00	WD	02 21
++10 00 00 00	WD	03 00
++10 00 00 00	WD	04 21
++10 00 00 00	WD	05 00
++
++20 00 00 00	WD	02 29
++20 00 00 00	WD	03 00
++20 00 00 00	WD	04 29
++20 00 00 00	WD	05 00
++
++40 00 00 00	WD	02 31
++40 00 00 00	WD	03 00
++40 00 00 00	WD	04 31
++40 00 00 00	WD	05 00
++
++80 00 00 00	WD	02 39
++80 00 00 00	WD	03 00
++80 00 00 00	WD	04 39
++80 00 00 00	WD	05 00
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/init_data_3_20.cmd zaptel-xpp-XJ8JZx_dist/xpp/init_data_3_20.cmd
+--- zaptel-1.2.6/xpp/init_data_3_20.cmd	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/init_data_3_20.cmd	2006-06-26 11:48:58.007317000 +0300
+@@ -0,0 +1,170 @@
++#
++# Written by Oron Peled <oron at actcom.co.il>
++# Copyright (C) 2006, Xorcom
++#
++# 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.
++#
++# See the file LICENSE in the top level of this tarball.
++#
++
++;
++; $Id: init_data_fxs.cmd 540 2006-03-01 12:30:36Z oron $
++;
++; SLICS		CMD	Reg	High	Low
++
++; ----------------------------------==== 8-channel FXS unit initialization ===-----------------------------------------
++; INTERNAL PS
++; Change SLICs states to "Open state"s  (Off,all transfers tristated to avoid data collision), Voltage sense
++
++FF 00 00 00	WD	40	00
++FF 00 00 00	WD	6C	01
++
++; ------------------------------------- Initialization of indirect registers ------------------------------------------
++
++FF 00 00 00	WI	00	55	C2
++FF 00 00 00	WI	01	51	E6
++FF 00 00 00	WI	02	4B	85
++FF 00 00 00	WI	03	49	37
++
++FF 00 00 00	WI	04	33	33
++FF 00 00 00	WI	05	02	02
++FF 00 00 00	WI	06	02	02
++FF 00 00 00	WI	07	01	98
++
++FF 00 00 00	WI	08	01	98
++FF 00 00 00	WI	09	06	11
++FF 00 00 00	WI	0A	02	02
++FF 00 00 00	WI	0B	00	E5
++
++FF 00 00 00	WI	0C	0A	1C
++FF 00 00 00	WI	0D	7B	30
++FF 00 00 00	WI	0E	00	63
++FF 00 00 00	WI	0F	00	00
++
++FF 00 00 00	WI	10	78	70
++FF 00 00 00	WI	11	00	7D
++FF 00 00 00	WI	12	00	00
++FF 00 00 00	WI	13	00	00
++
++FF 00 00 00	WI	14	7E	F0
++FF 00 00 00	WI	15	01	60
++FF 00 00 00	WI	16	00	00
++FF 00 00 00	WI	17	20	00
++
++FF 00 00 00	WI	18	20	00
++FF 00 00 00	WI	19	00	00
++FF 00 00 00	WI	1A	40	00
++FF 00 00 00	WI	1B	40	00
++
++FF 00 00 00	WI	1C	18	00
++FF 00 00 00	WI	1D	40	00
++FF 00 00 00	WI	1E	10	00
++FF 00 00 00	WI	1F	00	80
++
++FF 00 00 00	WI	20	0F	F4
++FF 00 00 00	WI	21	6E	7E
++FF 00 00 00	WI	22	0F	F4
++FF 00 00 00	WI	23	88	00
++
++FF 00 00 00	WI	24	03	20
++FF 00 00 00	WI	25	00	12
++FF 00 00 00	WI	26	00	12
++FF 00 00 00	WI	27	00	12
++
++FF 00 00 00	WI	28	0C	00
++FF 00 00 00	WI	29	0C	00
++FF 00 00 00	WI	2B	08	00
++
++FF 00 00 00	WI	63	00	DA
++FF 00 00 00	WI	64	6B	60
++FF 00 00 00	WI	65	00	74
++FF 00 00 00	WI	66	79	C0
++
++FF 00 00 00	WI	67	11	20
++FF 00 00 00	WI	68	3B	E0	
++
++; ------------------------------------- Initialization of direct registers --------------------------------------------
++
++; Mode(8-bit,u-Law,1 PCLK ) setting, Loopbacks and Interrupts clear
++
++FF 00 00 00	WD	01 29
++FF 00 00 00	WD	08 00
++FF 00 00 00	WD	09 00
++FF 00 00 00	WD	0E 00  
++
++FF 00 00 00	WD	15 00
++FF 00 00 00	WD	16 03
++FF 00 00 00	WD	17 00
++FF 00 00 00	WD	12 FF
++FF 00 00 00	WD	13 FF
++FF 00 00 00	WD	14 FF 
++
++; Automatic/Manual Control: defaults - Cancel Power Alarm
++FF 00 00 00	WD	43 1E
++
++FF 00 00 00	WD	4A 31
++FF 00 00 00	WD	4B 10
++
++; Battery Feed Control: Battery low (DCSW low)
++FF 00 00 00	WD	42 00
++
++; Slic Calibration
++FF 00 00 00	WD	61 1F
++FF 00 00 00	WD	60 5F
++
++; Loop Closure Debounce Interval
++FF 00 00 00	WD	45 0A
++
++; Ring Detect Debounce Interval
++FF 00 00 00	WD	46 0B
++
++; Loop Current Limit
++FF 00 00 00	WD	47 07
++
++; Setting of SLICs offsets
++
++# New card initialization
++01 00 00 00	WD	02 00
++01 00 00 00	WD	03 00
++01 00 00 00	WD	04 00
++01 00 00 00	WD	05 00
++
++02 00 00 00	WD	02 08
++02 00 00 00	WD	03 00
++02 00 00 00	WD	04 08
++02 00 00 00	WD	05 00
++
++04 00 00 00	WD	02 10
++04 00 00 00	WD	03 00
++04 00 00 00	WD	04 10
++04 00 00 00	WD	05 00
++
++08 00 00 00	WD	02 18
++08 00 00 00	WD	03 00
++08 00 00 00	WD	04 18
++08 00 00 00	WD	05 00
++
++10 00 00 00	WD	02 20
++10 00 00 00	WD	03 00
++10 00 00 00	WD	04 20
++10 00 00 00	WD	05 00
++
++20 00 00 00	WD	02 28
++20 00 00 00	WD	03 00
++20 00 00 00	WD	04 28
++20 00 00 00	WD	05 00
++
++40 00 00 00	WD	02 30
++40 00 00 00	WD	03 00
++40 00 00 00	WD	04 30
++40 00 00 00	WD	05 00
++
++80 00 00 00	WD	02 38
++80 00 00 00	WD	03 00
++80 00 00 00	WD	04 38
++80 00 00 00	WD	05 00
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/init_data_4_19.cmd zaptel-xpp-XJ8JZx_dist/xpp/init_data_4_19.cmd
+--- zaptel-1.2.6/xpp/init_data_4_19.cmd	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/init_data_4_19.cmd	2006-04-27 16:03:25.463235000 +0300
+@@ -0,0 +1,69 @@
++#
++# Written by Oron Peled <oron at actcom.co.il>
++# Copyright (C) 2006, Xorcom
++#
++# 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.
++#
++# See the file LICENSE in the top level of this tarball.
++#
++
++;
++; $Id: init_data_fxo.cmd 540 2006-03-01 12:30:36Z oron $
++;
++; DAA's 	CMD	Reg	High	Low
++
++; ----------------------------------==== 8-channel FXO unit initialization ===-----------------------------------------
++
++FF FF 00 00	WD	21	28
++FF FF 00 00	WD	18	99
++FF FF 00 00	WD	06	00
++
++; ----------- DAA PCM start offset ----------
++
++01 00 00 00	WD	22 01
++01 00 00 00	WD	23 00
++01 00 00 00	WD	24 01
++01 00 00 00	WD	25 00
++
++02 00 00 00	WD	22 09
++02 00 00 00	WD	23 00
++02 00 00 00	WD	24 09
++02 00 00 00	WD	25 00
++
++04 00 00 00	WD	22 11
++04 00 00 00	WD	23 00
++04 00 00 00	WD	24 11
++04 00 00 00	WD	25 00
++
++08 00 00 00	WD	22 19
++08 00 00 00	WD	23 00
++08 00 00 00	WD	24 19
++08 00 00 00	WD	25 00
++
++10 00 00 00	WD	22 21
++10 00 00 00	WD	23 00
++10 00 00 00	WD	24 21
++10 00 00 00	WD	25 00
++
++20 00 00 00	WD	22 29
++20 00 00 00	WD	23 00
++20 00 00 00	WD	24 29
++20 00 00 00	WD	25 00
++
++40 00 00 00	WD	22 31
++40 00 00 00	WD	23 00
++40 00 00 00	WD	24 31
++40 00 00 00	WD	25 00
++
++80 00 00 00	WD	22 39
++80 00 00 00	WD	23 00
++80 00 00 00	WD	24 39
++80 00 00 00	WD	25 00
++
++; ----------- DAA ONHOOK --------------------
++FF FF 00 00	WD	05	00
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/init_data_4_20.cmd zaptel-xpp-XJ8JZx_dist/xpp/init_data_4_20.cmd
+--- zaptel-1.2.6/xpp/init_data_4_20.cmd	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/init_data_4_20.cmd	2006-04-27 16:03:25.463235000 +0300
+@@ -0,0 +1,69 @@
++#
++# Written by Oron Peled <oron at actcom.co.il>
++# Copyright (C) 2006, Xorcom
++#
++# 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.
++#
++# See the file LICENSE in the top level of this tarball.
++#
++
++;
++; $Id: init_data_fxo.cmd 540 2006-03-01 12:30:36Z oron $
++;
++; DAA's 	CMD	Reg	High	Low
++
++; ----------------------------------==== 8-channel FXO unit initialization ===-----------------------------------------
++
++FF FF 00 00	WD	21	28
++FF FF 00 00	WD	18	99
++FF FF 00 00	WD	06	00
++
++; ----------- DAA PCM start offset ----------
++
++01 00 00 00	WD	22 00
++01 00 00 00	WD	23 00
++01 00 00 00	WD	24 00
++01 00 00 00	WD	25 00
++
++02 00 00 00	WD	22 08
++02 00 00 00	WD	23 00
++02 00 00 00	WD	24 08
++02 00 00 00	WD	25 00
++
++04 00 00 00	WD	22 10
++04 00 00 00	WD	23 00
++04 00 00 00	WD	24 10
++04 00 00 00	WD	25 00
++
++08 00 00 00	WD	22 18
++08 00 00 00	WD	23 00
++08 00 00 00	WD	24 18
++08 00 00 00	WD	25 00
++
++10 00 00 00	WD	22 20
++10 00 00 00	WD	23 00
++10 00 00 00	WD	24 20
++10 00 00 00	WD	25 00
++
++20 00 00 00	WD	22 28
++20 00 00 00	WD	23 00
++20 00 00 00	WD	24 28
++20 00 00 00	WD	25 00
++
++40 00 00 00	WD	22 30
++40 00 00 00	WD	23 00
++40 00 00 00	WD	24 30
++40 00 00 00	WD	25 00
++
++80 00 00 00	WD	22 38
++80 00 00 00	WD	23 00
++80 00 00 00	WD	24 38
++80 00 00 00	WD	25 00
++
++; ----------- DAA ONHOOK --------------------
++FF FF 00 00	WD	05	00
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/initialize_registers zaptel-xpp-XJ8JZx_dist/xpp/initialize_registers
+--- zaptel-1.2.6/xpp/initialize_registers	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/initialize_registers	2006-05-01 12:49:12.732258000 +0300
+@@ -0,0 +1,39 @@
++#! /bin/sh
++# XPD_BUS	- bus name
++# XPD_NAME	- xpd name
++# XPD_TYPE	- xpd type number (from protocol reply):
++#			3 - FXS
++#			4 - FXO
++# XPD_REVISION	- xpd revision number
++
++set -e
++
++LOGGER="logger -i -t `basename $0`"
++
++INIT_DIR=`dirname $0`
++BASE=/proc/xpp
++
++SLICS="$BASE/$XPD_BUS/$XPD_NAME/slics"
++FILE="$INIT_DIR/init_data_${XPD_TYPE}_${XPD_REVISION}.cmd"
++
++if [ ! -f "$SLICS" ]; then
++	$LOGGER "missing slics file '$SLICS'"
++	exit 1
++fi
++
++if [ ! -f "$FILE" ]; then
++	$LOGGER "missing register initialization file '$FILE'"
++	exit 1
++fi
++
++case "$XPD_TYPE" in
++3|4)
++	cat "$FILE" > "$SLICS" 
++	;;
++*)
++	$LOGGER "Unknown type '$XPD_TYPE'"
++	exit 2
++esac
++$LOGGER "Wrote '$FILE' into '$SLICS'"
++
++exit 0
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/LICENSE.firmware zaptel-xpp-XJ8JZx_dist/xpp/LICENSE.firmware
+--- zaptel-1.2.6/xpp/LICENSE.firmware	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/LICENSE.firmware	2006-05-01 22:45:43.558912000 +0300
+@@ -0,0 +1,37 @@
++The firmware files (*.hex) in this directory are software for the 
++Astribank itself and not intended to run on the Linux system itself. 
++They are generally freely distriributable (see exact terms below).
++
++/****************************************************************************/
++/*  Copyright (c) 2004-2006 Xorcom Inc. All Rights Reserved.                */
++/*  Redistribution and use of the microcode software ( Firmware ) is        */
++/*  permitted provided that the following conditions are met:               */
++/*                                                                          */
++/*          1. Firmware is redistributed verbatim without any modification; */
++/*          2. Any reproduction of Firmware must contain the above          */
++/*             copyright notice, this list of conditions and the below      */
++/*             disclaimer in the documentation and/or other materials       */
++/*             provided with the distribution; and                          */
++/*          3. The name of Xorcom may not be used to endorse or promote     */
++/*             products derived from this Firmware without specific prior   */
++/*             written consent.                                             */
++/*                                                                          */
++/*  Disclaimer: Xorcom provides this firmware "as is" with no warranties    */
++/*  or indemnities whatsoever. Xorcom expressly disclaims any express,      */
++/*  statutory or implied warranties, including, but not limited to, the     */
++/*  implied warranties of merchantability, fitness for a particular         */
++/*  purpose and non-infringement. In no event shall Xorcom be liable for    */
++/*  any direct, indirect, incidental, special, exemplary, or consequential  */
++/*  damages (including, but not limited to, procurement of substitute       */
++/*  goods or services; loss of use, data, or profits; or business           */
++/*  interruption) however caused and on any theory of liability, whether    */
++/*  in contract, strict liability, or tort (including negligence or         */
++/*  otherwise) arising in any way out of the use of this firmware, even     */
++/*  if advised of the possibility of such damage. User acknowledges and     */
++/*  agrees that the purchase or use of the firmware will not create or      */
++/*  give grounds for a license by implication, estoppel, or otherwise in    */
++/*  any intellectual property rights (patent, copyright, trade secret,      */
++/*  mask work, or other proprietary right) embodied in any other Xorcom     */
++/*  hardware or firmware either solely or in combination with the firmware. */
++/****************************************************************************/
++
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/Makefile zaptel-xpp-XJ8JZx_dist/xpp/Makefile
+--- zaptel-1.2.6/xpp/Makefile	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/Makefile	2006-06-22 11:31:28.427315000 +0300
+@@ -1,5 +1,30 @@
+-EXTRA_CFLAGS	= -I$(src)/.. -DSOFT_SIMULATOR=0
++EXTRA_CFLAGS	= -I$(SUBDIRS) -DDEBUG -DWITH_ECHO_SUPPRESSION -DXPP_EC_CHUNK -DWITH_RBS # -DHARD_CODED_INIT # -DOLD_CARD
+ 
+-obj-m		= xpd_fxs.o xpp.o xpp_usb.o
+-xpp-y		+= xproto.o card_global.o xpp_zap.o zap_debug.o
+-xpd_fxs-y	+= card_fxs.o slic.o
++obj-m			= xpp.o xpd_fxs.o xpd_fxo.o xpp_usb.o
++xpp-y			+= xbus-core.o xpp_zap.o xproto.o card_global.o
++xpd_fxs-y		+= card_fxs.o slic.o
++xpd_fxo-y		+= card_fxo.o slic.o
++
++GENERATE_INIT_DATA	:= 1
++INIT_DATA		= init_data_3_19.inc init_data_3_20.inc init_data_4_19.inc init_data_4_20.inc
++clean-files		:= $(INIT_DATA)
++
++cmd2inc=$(src)/cmd2inc
++
++define run_cmd2inc
++if [ ! -x $(cmd2inc) ]; then chmod +x $(cmd2inc); fi
++$(cmd2inc) $^ $@
++endef
++
++ifeq	($(GENERATE_INIT_DATA),1)
++
++$(obj)/%.inc: $(src)/%.cmd
++	@echo "Generating $@"
++	@$(run_cmd2inc)
++
++init_data: $(INIT_DATA)
++
++$(obj)/card_fxs.o: $(obj)/init_data_3_19.inc $(obj)/init_data_3_20.inc
++$(obj)/card_fxo.o: $(obj)/init_data_4_19.inc $(obj)/init_data_4_20.inc
++
++endif
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/README.Astribank zaptel-xpp-XJ8JZx_dist/xpp/README.Astribank
+--- zaptel-1.2.6/xpp/README.Astribank	2006-03-03 21:11:53.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/README.Astribank	2006-06-19 11:29:10.334411000 +0300
+@@ -5,15 +5,63 @@
+ Building and Installation:
+ """""""""""""""""""""""""
+ Building and installation is basically like the normal procedure of 
+-installing Zaptel. Follow the rest of the documentation here.
++installing Zaptel with some additions.
+ 
+-In addition, the file xpp/xpp_modprobe contains modprobe settings. It
+-should be copied verbatim into /etc/modprobe.conf or (better) copied to
+-/etc/modprobe.d/ . If you fail to do so, xpp_usb.ko will fail to load
+-xpd_fxs.ko and as a result will not detect your Astribank.
+-
+-Loading Firmware
++Building drivers:
+ """"""""""""""""
++From the toplevel zaptel directory run a command similar to (I used line
++continuation to prevent line wrapping):
++
++ $ make \
++        KSRC=/usr/src/kernel-headers-2.6.12-1-386       \
++        KVERS=2.6.12-1-386                              \
++        XPPMOD=xpp/                                     \
++        EC_TYPE=CAN_KB1
++
++ - The KSRC= points to a configured kernel source tree.
++ - The KVERS= should match the relevant tree version.
++ - The XPPMOD= instructs the Zaptel Makefile to descend into the xpp/
++   subdirectory. The slash (/) in the end is mandatory.
++ - The EC_TYPE= select the echo canceler.
++
++Building firmware utilities:
++"""""""""""""""""""""""""""
++Then you should compile the firmware loading utilities. Simply go
++to the zaptel/xpp/utils and run make.
++
++Those who don't use prepackaged drivers should make sure they also
++install the (externally available) fxload utility.
++
++Installation:
++""""""""""""
++
++apart from the standard 'make install' in the zaptel directory, 
++run:
++
++  make -C xpp/utils install
++
++Alternatively, do the following manually:
++
++All firmware files should be copied to a new directory:
++  /usr/share/zaptel/
++
++The xpp_fxloader and xpp_fxloader.usbmap should be copied to:
++ /etc/hotplug/usb/
++
++In addition, the file xpp/xpp_modprobe contains optional modprobe settings.
++It may be copied verbatim into /etc/modprobe.conf or (better) copied to
++/etc/modprobe.d/ .
++
++
++Note that loading through udev is not yet provided. Run 
++
++  /etc/hotplug/usb/xpp_fxloader xppdetect
++
++to load firmware.
++
++
++Loading Firmware Details:
++""""""""""""""""""""""""
+ The Astribank needs a firmware loaded into it. Without the firmware, 
+ the device will appear in lsusb with vendor ID 04b4 and product ID 8613
+ The firmware is provided in the Intel hex format. It can be loaded using 
+@@ -21,12 +69,11 @@
+ 'hotplug-utils' . 
+ 
+ To load the firmware automatically using the standard hotplug script, 
+-place xpp/xpp_fxloader and xpp/xpp_fxloader.usermap in /etc/hotplug/usb
+-and place xpp/FPGA_XPD.hex in /etc/xortel (or edit xpp_fxloader
+-accordingly). 
++place xpp/utils/xpp_fxloader and xpp/utils/xpp_fxloader.usermap in 
++/etc/hotplug/usb and place xpp/utils/*.hex in /usr/share/zaptel .
+ 
+ Alternatively, xpp_fxloader when given the parameter 'xppdetect' will load 
+-the firmware from /etc/xortel/FPGA_XPD.hex . You can use it to load the 
++the firmwares from /usr/share/zaptel/ . You can use it to load the 
+ firmware manually.
+ 
+ You should then get in lsusb the vendor ID e4e4 and device ID 2121
+@@ -128,6 +175,25 @@
+ (There are a bunch of other status files under /proc/xpp/ )
+ 
+ 
++Useful Module Parameters:
++""""""""""""""""""""""""
++zap_autoreg: (xpp)
++Register spans automatically (1) or not (0). Default: 1. 
++Unsetting this could be useful if you have several Astribanks and you 
++want to set their registration order manually using zt_registration in 
++the /proc interface.
++
++initialize_registers (xpd_fxs)
++The script that is run to initilize registers of the device. The default is 
++/usr/share/zaptel/initialize_registers . 
++Setting this value could be useful if that location is inconvient for you.
++
++print_dbg: (all modules)
++It will make the driver print tons of debugging messages. Can be sometime 
++even handy, but overly-verbose in the case of xpp_usb. Can be safely 
++set/unset at run-time using /sys/modules .
++
++
+ 
+ BTW: XPP here does not stand for X Printing Panel, XML Pull Parser, 
+ X-Windows Phase Plane or XML Professional Publisher. It is simply the 
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/slic.c zaptel-xpp-XJ8JZx_dist/xpp/slic.c
+--- zaptel-1.2.6/xpp/slic.c	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/slic.c	2006-05-01 12:37:15.907543000 +0300
+@@ -1,6 +1,6 @@
+ /*
+  * Written by Oron Peled <oron at actcom.co.il>
+- * Copyright (C) 2004-2005, Xorcom
++ * Copyright (C) 2004-2006, Xorcom
+  *
+  * All rights reserved.
+  *
+@@ -23,13 +23,16 @@
+ #include "xproto.h"
+ #include "slic.h"
+ 
+-static const char rcsid[] = "$Id: slic.c 949 2006-02-15 02:24:18Z kpfleming $";
++static const char rcsid[] = "$Id: slic.c 1097 2006-05-01 09:37:15Z oron $";
+ 
+ #ifdef	__KERNEL__
+ #include <linux/module.h>
+ 
+ extern	int print_dbg;
+ #include "zap_debug.h"
++
++DEF_PARM(charp,initialize_registers, "/usr/share/zaptel/initialize_registers", "The script to initialize detected cards slics");
++
+ #else
+ #include <stdio.h>
+ #endif
+@@ -120,6 +123,50 @@
+ 
+ #ifdef	__KERNEL__
+ 
++#define	MAX_ENV_STR	20
++
++int run_initialize_registers(xpd_t *xpd)
++{
++	int	ret;
++	xbus_t	*xbus;
++	char	busstr[MAX_ENV_STR];
++	char	xpdstr[MAX_ENV_STR];
++	char	typestr[MAX_ENV_STR];
++	char	revstr[MAX_ENV_STR];
++	char	*argv[] = {
++		initialize_registers,
++		NULL
++	};
++	char	*envp[] = {
++		busstr,
++		xpdstr,
++		typestr,
++		revstr,
++		NULL
++	};
++
++	BUG_ON(!xpd);
++	xbus = xpd->xbus;
++	if(!initialize_registers || !initialize_registers[0]) {
++		NOTICE("%s/%s: No runtime register initialization\n", xbus->busname, xpd->xpdname);
++		return 0;
++	}
++	DBG("%s/%s: running: '%s'\n", xbus->busname, xpd->xpdname, initialize_registers);
++	snprintf(busstr, MAX_ENV_STR, "XPD_BUS=%s", xbus->busname);
++	snprintf(xpdstr, MAX_ENV_STR, "XPD_NAME=%s", xpd->xpdname);
++	snprintf(typestr, MAX_ENV_STR, "XPD_TYPE=%d", xpd->type);
++	snprintf(revstr, MAX_ENV_STR, "XPD_REVISION=%d", xpd->revision);
++	DBG("%s/%s: type=%d revision=%d\n", xbus->busname, xpd->xpdname, xpd->type, xpd->revision);
++	ret = call_usermodehelper(initialize_registers, argv, envp, 1);
++	if(ret != 0) {
++		ERR("%s/%s: Failed running '%s' (errno %d, sig=%d)\n",
++				xbus->busname, xpd->xpdname, initialize_registers,
++				((unsigned)ret >> 8) & 0xFF, ret & 0xFF);
++		ret = -EFAULT;
++	}
++	return ret;
++}
++
+ EXPORT_SYMBOL(slic_cmd_direct_write);
+ EXPORT_SYMBOL(slic_cmd_direct_read);
+ EXPORT_SYMBOL(slic_cmd_indirect_write);
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/slic.h zaptel-xpp-XJ8JZx_dist/xpp/slic.h
+--- zaptel-1.2.6/xpp/slic.h	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/slic.h	2006-04-02 18:33:25.346374000 +0300
+@@ -1,5 +1,26 @@
+ #ifndef	SLIC_H
+ #define	SLIC_H
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2006, Xorcom
++ *
++ * 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 "xdefs.h"
+ 
+@@ -60,6 +81,7 @@
+ int slic_cmd_indirect_write(slic_cmd_t *sc, xpp_line_t lines, byte reg, byte data_low, byte data_high);
+ int slic_cmd_indirect_read(slic_cmd_t *sc, xpp_line_t lines, byte reg);
+ void dump_slic_cmd(const char msg[], slic_cmd_t *sc);
++int run_initialize_registers(xpd_t *xpd);
+ 
+ 
+ #endif	/* SLIC_H */
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/slic_init.inc zaptel-xpp-XJ8JZx_dist/xpp/slic_init.inc
+--- zaptel-1.2.6/xpp/slic_init.inc	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/slic_init.inc	1970-01-01 02:00:00.000000000 +0200
+@@ -1,65 +0,0 @@
+-// ----------------------------------==== 8-channel FXS unit initialization ===-----------------------------------------
+-
+-// INTERNAL PS
+-// Change SLICs states to "Open state"s  (Off,all transfers tristated to avoid data collision), Voltage sense
+-S_(0x000000FF,	0x04,	0x40, 0x00, 0x6C, 0x01),	
+-
+-
+-// ------------------------------------- Initialization of indirect registers ------------------------------------------
+-S_(0x000000FF,	0x02,	0x40, 0x00),	
+-S_(0x000000FF,	0x18,	0x1C, 0xC2, 0x1D, 0x55, 0x1E, 0x00, 0x1C, 0xE6, 0x1D, 0x51, 0x1E, 0x01, 0x1C, 0x85, 0x1D, 0x4B, 0x1E, 0x02, 0x1C, 0x37, 0x1D, 0x49, 0x1E, 0x03),	
+-S_(0x000000FF,	0x18,	0x1C, 0x33, 0x1D, 0x33, 0x1E, 0x04, 0x1C, 0x02, 0x1D, 0x02, 0x1E, 0x05, 0x1C, 0x02, 0x1D, 0x02, 0x1E, 0x06, 0x1C, 0x98, 0x1D, 0x01, 0x1E, 0x07),	
+-S_(0x000000FF,	0x18,	0x1C, 0x98, 0x1D, 0x01, 0x1E, 0x08, 0x1C, 0x11, 0x1D, 0x06, 0x1E, 0x09, 0x1C, 0x02, 0x1D, 0x02, 0x1E, 0x0A, 0x1C, 0xE5, 0x1D, 0x00, 0x1E, 0x0B),	
+-S_(0x000000FF,	0x18,	0x1C, 0x1C, 0x1D, 0x0A, 0x1E, 0x0C, 0x1C, 0x30, 0x1D, 0x7B, 0x1E, 0x0D, 0x1C, 0x63, 0x1D, 0x00, 0x1E, 0x0E, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x0F),	
+-
+-S_(0x000000FF,	0x18,	0x1C, 0x70, 0x1D, 0x78, 0x1E, 0x10, 0x1C, 0x7D, 0x1D, 0x00, 0x1E, 0x11, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x12, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x13),	
+-S_(0x000000FF,	0x18,	0x1C, 0xF0, 0x1D, 0x7E, 0x1E, 0x14, 0x1C, 0x60, 0x1D, 0x01, 0x1E, 0x15, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x16, 0x1C, 0x00, 0x1D, 0x20, 0x1E, 0x17),	
+-S_(0x000000FF,	0x18,	0x1C, 0x00, 0x1D, 0x20, 0x1E, 0x18, 0x1C, 0x00, 0x1D, 0x00, 0x1E, 0x19, 0x1C, 0x00, 0x1D, 0x40, 0x1E, 0x1A, 0x1C, 0x00, 0x1D, 0x40, 0x1E, 0x1B),	
+-S_(0x000000FF,	0x18,	0x1C, 0x00, 0x1D, 0x18, 0x1E, 0x1C, 0x1C, 0x00, 0x1D, 0x40, 0x1E, 0x1D, 0x1C, 0x00, 0x1D, 0x10, 0x1E, 0x1E, 0x1C, 0x80, 0x1D, 0x00, 0x1E, 0x1F),	
+-
+-S_(0x000000FF,	0x18,	0x1C, 0xF4, 0x1D, 0x0F, 0x1E, 0x20, 0x1C, 0x7E, 0x1D, 0x6E, 0x1E, 0x21, 0x1C, 0xF4, 0x1D, 0x0F, 0x1E, 0x22, 0x1C, 0x00, 0x1D, 0x88, 0x1E, 0x23),	
+-S_(0x000000FF,	0x18,	0x1C, 0x20, 0x1D, 0x03, 0x1E, 0x24, 0x1C, 0x12, 0x1D, 0x00, 0x1E, 0x25, 0x1C, 0x12, 0x1D, 0x00, 0x1E, 0x26, 0x1C, 0x12, 0x1D, 0x00, 0x1E, 0x27),	
+-S_(0x000000FF,	0x12,	0x1C, 0x00, 0x1D, 0x0C, 0x1E, 0x28, 0x1C, 0x00, 0x1D, 0x0C, 0x1E, 0x29, 0x1C, 0x00, 0x1D, 0x08, 0x1E, 0x2B),	
+-
+-S_(0x000000FF,	0x18,	0x1C, 0xDA, 0x1D, 0x00, 0x1E, 0x63, 0x1C, 0x60, 0x1D, 0x6B, 0x1E, 0x64, 0x1C, 0x74, 0x1D, 0x00, 0x1E, 0x65, 0x1C, 0xC0, 0x1D, 0x79, 0x1E, 0x66),	
+-S_(0x000000FF,	0x0C,	0x1C, 0x20, 0x1D, 0x11, 0x1E, 0x67, 0x1C, 0xE0, 0x1D, 0x3B, 0x1E, 0x68),	
+-
+-// ------------------------------------- Initialization of direct registers --------------------------------------------
+-
+-// Mode(8-bit,u-Law,1 PCLK ) setting, Loopbacks and Interrupts clear
+-// --Temporary digital loopback
+-S_(0x000000FF,	0x08,	0x01, 0x29, 0x08, 0x00, 0x09, 0x00, 0x0E, 0x00),	
+-S_(0x000000FF,	0x0C,	0x15, 0x00, 0x16, 0x03, 0x17, 0x00, 0x12, 0xFF, 0x13, 0xFF, 0x14, 0xFF),	
+-
+-// Ring timers settings
+-S_(0x000000FF,	0x06,	0x30, 0x80, 0x31, 0x3E, 0x32, 0x80),	
+-S_(0x000000FF,	0x02,	0x33, 0x3E),	
+-S_(0x000000FF,	0x02,	0x22, 0x18),	
+-
+-// Battery feed control(DCSW), Automatic control of Ring Trip and Loop Closure, VBATH, VBATL
+-S_(0x000000FF,	0x02,	0x42, 0x00),	
+-S_(0x000000FF,	0x02,	0x43, 0x1E),	
+-S_(0x000000FF,	0x04,	0x4A, 0x31, 0x4B, 0x10),	
+-
+-S_(0x000000FF,	0x02,	0x45, 0x0A),	
+-S_(0x000000FF,	0x02,	0x46, 0x0B),	
+-S_(0x000000FF,	0x02,	0x47, 0x07),	
+-
+-
+-// Setting of SLICs offsets
+-S_(0x00000001,	0x08,	0x02, 0x01, 0x03, 0x00, 0x04, 0x01, 0x05, 0x00),	
+-S_(0x00000002,	0x08,	0x02, 0x09, 0x03, 0x00, 0x04, 0x09, 0x05, 0x00),	
+-S_(0x00000004,	0x08,	0x02, 0x11, 0x03, 0x00, 0x04, 0x11, 0x05, 0x00),	
+-S_(0x00000008,	0x08,	0x02, 0x19, 0x03, 0x00, 0x04, 0x19, 0x05, 0x00),	
+-S_(0x00000010,	0x08,	0x02, 0x21, 0x03, 0x00, 0x04, 0x21, 0x05, 0x00),	
+-S_(0x00000020,	0x08,	0x02, 0x29, 0x03, 0x00, 0x04, 0x29, 0x05, 0x00),	
+-S_(0x00000040,	0x08,	0x02, 0x31, 0x03, 0x00, 0x04, 0x31, 0x05, 0x00),	
+-S_(0x00000080,	0x08,	0x02, 0x39, 0x03, 0x00, 0x04, 0x39, 0x05, 0x00),	
+-
+-// Change SLICs states to "Normal state"s (On, after offsets are set already)
+-
+-
+-S_(0x000000FF,	0x02,	0x40, 0x1),	
+-S_(0x000000FF,	0x02,	0x42, 0x06),	
+-// -------------------------------------------------------------
+-
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/supress/ec_xpp.h zaptel-xpp-XJ8JZx_dist/xpp/supress/ec_xpp.h
+--- zaptel-1.2.6/xpp/supress/ec_xpp.h	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/supress/ec_xpp.h	2006-06-25 14:42:21.798100000 +0300
+@@ -0,0 +1,100 @@
++#ifndef __EC_XPP_H__
++#define __EC_XPP_H__
++
++#ifdef __KERNEL__
++#	include <linux/kernel.h>
++#	include <linux/slab.h>
++#	define EC_MALLOC(a) kmalloc((a), GFP_ATOMIC)
++#	define EC_FREE(a) kfree(a)
++#	define printf printk
++#else
++#	include <stdlib.h>
++#	include <unistd.h>
++#	include <stdint.h>
++#	include <string.h>
++#include <math.h>
++#	define EC_MALLOC(a) malloc(a)
++#	define EC_FREE(a) free(a)
++#endif
++
++#include "suppressor.c"
++
++typedef struct {
++#define	XPP_EC_MAGIC	0xfee1dead
++	int	magic;
++	int	objSize;
++	void*	pEchoSuppressionObj;
++} xpp_echo_can_state_t;
++
++static inline echo_can_state_t* xpp_echo_can_create(int len, int adaption_mode)
++{
++	xpp_echo_can_state_t	*state;
++	ES_WINDOW_SIZE_ID	currentWindowId;
++	
++	DBG("EC: len=%d adaption_mode=%d\n", len, adaption_mode);
++	switch (len) {
++		case 32:	currentWindowId = ES_WINDOW_SIZE_ID_32_MSEC;	break;
++		case 64:	currentWindowId = ES_WINDOW_SIZE_ID_64_MSEC;	break;
++		case 128:	currentWindowId = ES_WINDOW_SIZE_ID_128_MSEC;	break;
++		case 256:	currentWindowId = ES_WINDOW_SIZE_ID_256_MSEC;	break;
++		default:
++				NOTICE("%s: Unknown len=%d\n", __FUNCTION__, len);
++				currentWindowId = ES_WINDOW_SIZE_ID_64_MSEC;	break;
++	}
++
++	if ( 0 == (state = EC_MALLOC(sizeof(*state)) ))
++		return NULL;
++	
++	if (0 > (state->objSize = ES_GetObjectSize(currentWindowId))){
++		EC_FREE(state);
++		return NULL;
++	}
++		
++	if (NULL == (state->pEchoSuppressionObj = EC_MALLOC(state->objSize))) {
++		EC_FREE(state);
++		return NULL;
++	}
++		
++	if ( !ES_Initialize(state->pEchoSuppressionObj, currentWindowId, ES_HYBRID_LOSS_LEVEL_6_DB)){
++		EC_FREE(state->pEchoSuppressionObj);
++		EC_FREE(state);
++		return NULL;
++	}
++	state->magic = XPP_EC_MAGIC;
++	return (echo_can_state_t *)state;
++}
++
++static inline void xpp_echo_can_free(echo_can_state_t *ec)
++{
++	xpp_echo_can_state_t	*mystate;
++
++	DBG("EC: %p\n", ec);
++	if (!ec)
++		return;
++	mystate = (xpp_echo_can_state_t *)ec;
++	if(mystate->magic != XPP_EC_MAGIC) {
++		ERR("%s: corrupted EC\n", __FUNCTION__);
++		return;
++	}
++	ES_Reset(mystate->pEchoSuppressionObj);
++	EC_FREE(mystate);
++}
++
++static inline int16_t xpp_echo_can_update(echo_can_state_t *ec, int16_t tx, int16_t rx)
++{
++	xpp_echo_can_state_t	*mystate;
++
++	if(!ec) {
++		ERR("%s: missing EC\n", __FUNCTION__);
++		return -EINVAL;
++	}
++	mystate = (xpp_echo_can_state_t *)ec;
++	if(mystate->magic != XPP_EC_MAGIC) {
++		ERR("%s: corrupted EC\n", __FUNCTION__);
++		return -EINVAL;
++	}
++	return ES_ProcessSamples(mystate->pEchoSuppressionObj, tx, rx);
++}
++
++#endif
++
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/supress/suppressor.c zaptel-xpp-XJ8JZx_dist/xpp/supress/suppressor.c
+--- zaptel-1.2.6/xpp/supress/suppressor.c	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/supress/suppressor.c	2006-06-25 14:42:21.798100000 +0300
+@@ -0,0 +1,321 @@
++ /***********************************************************************
++ * Written by Sergey Serik.
++ * Copyright (C) 2006, Xorcom Ltd.
++ * 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 "suppressor.h"
++
++#define PRINTF(fmt, ...)
++
++static inline ES_BOOL IsFirstMoreThanSecond(
++	ES_LONG_VALUE*			pFirstValue,
++	ES_LONG_VALUE*			pSecondValue)
++{
++    if(   (pFirstValue->integer > pSecondValue->integer)
++       || (   (pFirstValue->integer == pSecondValue->integer)
++		   && (pFirstValue->fractional > pSecondValue->fractional)))
++	{
++       return ES_TRUE;
++	}
++
++    return ES_FALSE;
++}
++
++
++static inline ES_BOOL IsFirstLessThanSecond(
++	ES_LONG_VALUE*			pFirstValue,
++	ES_LONG_VALUE*			pSecondValue)
++{
++    if(   (pFirstValue->integer < pSecondValue->integer)
++       || (   (pFirstValue->integer == pSecondValue->integer)
++		   && (pFirstValue->fractional < pSecondValue->fractional)))
++	{
++       return ES_TRUE;
++	}
++
++    return ES_FALSE;
++}
++
++static inline ES_LONG_VALUE* MultiplyByFactor(
++	ES_LONG_VALUE*		pValue,
++	ES_LONG_VALUE*		pNewValue,
++	int					factor)
++{
++	pNewValue->integer    = pValue->integer * factor + (pValue->fractional * factor) / ES_ALFA_INT;
++	pNewValue->fractional = (pValue->fractional * factor) % ES_ALFA_INT;
++
++	return pNewValue;
++}
++
++static inline int GetWindowSize(
++	ES_WINDOW_SIZE_ID		sizeId)
++{
++	switch(sizeId)
++	{
++	case ES_WINDOW_SIZE_ID_4_MSEC:
++		return ES_SAMPLES_IN_4_MSEC;
++
++	case ES_WINDOW_SIZE_ID_8_MSEC:
++		return ES_SAMPLES_IN_8_MSEC;
++
++	case ES_WINDOW_SIZE_ID_16_MSEC:
++		return ES_SAMPLES_IN_16_MSEC;
++
++	case ES_WINDOW_SIZE_ID_32_MSEC:
++		return ES_SAMPLES_IN_32_MSEC;
++
++	case ES_WINDOW_SIZE_ID_64_MSEC:
++		return ES_SAMPLES_IN_64_MSEC;
++
++	case ES_WINDOW_SIZE_ID_128_MSEC:
++		return ES_SAMPLES_IN_128_MSEC;
++
++	case ES_WINDOW_SIZE_ID_256_MSEC:
++		return ES_SAMPLES_IN_256_MSEC;
++
++	default:
++		PRINTF("GetWindowSize - error: unsupported Search Window Size ID <%d>\n", sizeId);
++		return -1;
++	}
++}
++
++static inline void ObtainFarMaximum(
++	SUPPRESSOR*	pES_Obj)
++{
++    if(pES_Obj->tailIndex == pES_Obj->maximalFar.index)
++    {
++        if(IsFirstMoreThanSecond(&pES_Obj->maximalFar.value, &pES_Obj->previousFar))
++        {
++			int n;
++
++			memset(&pES_Obj->maximalFar, 0, sizeof(pES_Obj->maximalFar));
++
++			for(n = 0; n < pES_Obj->windowSize; n++)
++			{
++				if(IsFirstMoreThanSecond(&pES_Obj->pHistoryOfFar[n], &pES_Obj->maximalFar.value))
++				{
++					pES_Obj->maximalFar.value.integer	 = pES_Obj->pHistoryOfFar[n].integer;
++					pES_Obj->maximalFar.value.fractional = pES_Obj->pHistoryOfFar[n].fractional;
++					pES_Obj->maximalFar.index = n;
++				}
++			}
++        }
++        else
++        {
++            pES_Obj->maximalFar.value.integer	 = pES_Obj->previousFar.integer;
++			pES_Obj->maximalFar.value.fractional = pES_Obj->previousFar.fractional;
++        }
++    }
++    else if(IsFirstLessThanSecond(&pES_Obj->maximalFar.value, &pES_Obj->previousFar))
++    {
++        pES_Obj->maximalFar.value.integer	 = pES_Obj->previousFar.integer;
++		pES_Obj->maximalFar.value.fractional = pES_Obj->previousFar.fractional;
++        pES_Obj->maximalFar.index			 = pES_Obj->tailIndex;
++    }
++}
++
++int ES_GetObjectSize(
++	ES_WINDOW_SIZE_ID		sizeId)
++{
++	int longValueSize = sizeof(ES_LONG_VALUE);
++	switch(sizeId)
++	{
++	case ES_WINDOW_SIZE_ID_4_MSEC:
++		return (sizeof(SUPPRESSOR) + ES_SAMPLES_IN_4_MSEC * longValueSize - longValueSize);
++
++	case ES_WINDOW_SIZE_ID_8_MSEC:
++		return (sizeof(SUPPRESSOR) + ES_SAMPLES_IN_8_MSEC * longValueSize - longValueSize);
++
++	case ES_WINDOW_SIZE_ID_16_MSEC:
++		return (sizeof(SUPPRESSOR) + ES_SAMPLES_IN_16_MSEC * longValueSize - longValueSize);
++
++	case ES_WINDOW_SIZE_ID_32_MSEC:
++		return (sizeof(SUPPRESSOR) + ES_SAMPLES_IN_32_MSEC * longValueSize - longValueSize);
++
++	case ES_WINDOW_SIZE_ID_64_MSEC:
++		return (sizeof(SUPPRESSOR) + ES_SAMPLES_IN_64_MSEC * longValueSize - longValueSize);
++
++	case ES_WINDOW_SIZE_ID_128_MSEC:
++		return (sizeof(SUPPRESSOR) + ES_SAMPLES_IN_128_MSEC * longValueSize - longValueSize);
++
++	case ES_WINDOW_SIZE_ID_256_MSEC:
++		return (sizeof(SUPPRESSOR) + ES_SAMPLES_IN_256_MSEC * longValueSize - longValueSize);
++
++	default:
++		PRINTF("ES_GetObjectSize - error: unsupported Search Window Size ID <%d>\n", sizeId);
++		return -1;
++	}
++}
++
++ES_BOOL ES_Initialize(
++	void*					pObject,
++	ES_WINDOW_SIZE_ID		sizeId,
++	ES_HYBRID_LOSS_LEVEL	level)
++{
++	SUPPRESSOR* pES_Obj = (SUPPRESSOR*) pObject;
++	int				 objectSize;
++
++	if(NULL == pES_Obj)
++	{
++		PRINTF("ES_Initialize - error: wrong pObject value <NULL>\n");
++		return ES_FALSE;
++	}
++
++	if(0 > (objectSize = ES_GetObjectSize(sizeId)))
++	{
++		PRINTF("ES_Initialize - error: on ES_GetObjectSize(%d) call\n", sizeId);
++		pES_Obj->initialized = ES_FALSE;
++		return ES_FALSE;
++	}
++
++	memset(pES_Obj, 0, objectSize);
++
++    switch(level)
++    {
++    case ES_HYBRID_LOSS_LEVEL_0_DB:
++        pES_Obj->comparer.factorNear		 = ES_COMPARE_MULTI_0_DB_NEAR;
++        pES_Obj->comparer.factorFar			 = ES_COMPARE_MULTI_0_DB_FAR;
++        pES_Obj->doubleTalkDetectionCountMax = ES_MAX_DTD_COUNT_0_DB;
++		pES_Obj->echoDetectionCountMax		 = ES_MAX_DTD_COUNT_0_DB;
++        break;
++
++    case ES_HYBRID_LOSS_LEVEL_3_DB:
++        pES_Obj->comparer.factorNear		 = ES_COMPARE_MULTI_3_DB_NEAR;
++        pES_Obj->comparer.factorFar			 = ES_COMPARE_MULTI_3_DB_FAR;
++        pES_Obj->doubleTalkDetectionCountMax = ES_MAX_DTD_COUNT_3_DB;
++		pES_Obj->echoDetectionCountMax		 = ES_MAX_DTD_COUNT_3_DB;
++        break;
++
++    case ES_HYBRID_LOSS_LEVEL_6_DB:
++        pES_Obj->comparer.factorNear		 = ES_COMPARE_MULTI_6_DB_NEAR;
++        pES_Obj->comparer.factorFar			 = ES_COMPARE_MULTI_6_DB_FAR;
++        pES_Obj->doubleTalkDetectionCountMax = ES_MAX_DTD_COUNT_6_DB;
++		pES_Obj->echoDetectionCountMax		 = ES_MAX_DTD_COUNT_6_DB;
++		break;
++
++	default:
++		PRINTF("ES_Initialize - error: unsupported Hybrid Loss Level <%d>\n", level);
++		return ES_FALSE;
++    }
++
++	pES_Obj->windowSize = GetWindowSize(sizeId);
++
++    return (pES_Obj->initialized = ES_TRUE);
++}
++
++
++ES_BOOL ES_Reset(
++	void*					pObject)
++{
++	SUPPRESSOR*	pES_Obj = (SUPPRESSOR*) pObject;
++
++	if(NULL == pES_Obj)
++	{
++		PRINTF("ES_Reset - error: wrong pObject value <NULL>\n");
++		return ES_FALSE;
++	}
++
++	if(!pES_Obj->initialized)
++	{
++		PRINTF("ES_Reset - error: the pObject is not initialized\n");
++		return ES_FALSE;
++	}
++
++	memset(pES_Obj->pHistoryOfFar, 0, pES_Obj->windowSize * sizeof(ES_LONG_VALUE));
++	return ES_TRUE;
++}
++
++
++short ES_NEAR_CLEAN ES_ProcessSamples(
++	void*					pObject,
++	ES_FAR_CLEAN short		farSample,
++	ES_NEAR_MIX  short		nearSample)
++{
++	SUPPRESSOR*	pES_Obj = (SUPPRESSOR*) pObject;
++	unsigned long		temp;
++	ES_LONG_VALUE		newNear, newFar;
++
++	if(NULL == pES_Obj)
++	{
++		PRINTF("ES_ProcessSamples - error: wrong pObject value <NULL>\n");
++		return nearSample;
++	}
++
++    if(!pES_Obj->initialized)
++	{
++		PRINTF("ES_ProcessSamples - error: the pObject is not initialized\n");
++        return nearSample;
++	}
++
++    temp = pES_Obj->previousNear.integer % ES_ALFA_INT + pES_Obj->previousNear.fractional;
++    pES_Obj->previousNear.fractional = temp % ES_ALFA_INT;
++    pES_Obj->previousNear.integer	 =  (ES_ALFA_INT - 1) * pES_Obj->previousNear.integer / ES_ALFA_INT
++									  + temp / ES_ALFA_INT + pES_Obj->moduleOfPreviousNear;
++
++    temp = pES_Obj->previousFar.integer % ES_ALFA_INT + pES_Obj->previousFar.fractional;
++    pES_Obj->previousFar.fractional	= temp % ES_ALFA_INT;
++    pES_Obj->previousFar.integer	=  (ES_ALFA_INT - 1) * pES_Obj->previousFar.integer / ES_ALFA_INT
++									 + temp / ES_ALFA_INT + pES_Obj->moduleOfPreviousFar;
++
++    pES_Obj->pHistoryOfFar[pES_Obj->tailIndex].integer	  = pES_Obj->previousFar.integer;
++	pES_Obj->pHistoryOfFar[pES_Obj->tailIndex].fractional = pES_Obj->previousFar.fractional;
++
++    pES_Obj->moduleOfPreviousNear = nearSample * ((nearSample < 0) ? -1 : 1);
++    pES_Obj->moduleOfPreviousFar = farSample  * ((farSample  < 0) ? -1 : 1);
++
++    ObtainFarMaximum(pES_Obj);
++
++    if(IsFirstLessThanSecond(MultiplyByFactor(&pES_Obj->previousNear, &newNear, pES_Obj->comparer.factorNear),
++							 MultiplyByFactor(&pES_Obj->maximalFar.value, &newFar, pES_Obj->comparer.factorFar)))
++    {
++		if(0 == pES_Obj->echoDetectionCount)
++		{
++			nearSample /= ES_DIVISOR_VALUE;
++			pES_Obj->doubleTalkDetectionCount = pES_Obj->doubleTalkDetectionCountMax;
++		}
++		else
++		{
++			pES_Obj->echoDetectionCount--;
++			pES_Obj->doubleTalkDetectionCount = 0;
++		}
++    }
++    else
++	{
++		pES_Obj->echoDetectionCount = pES_Obj->echoDetectionCountMax;
++
++		if(0 < pES_Obj->doubleTalkDetectionCount)
++		{
++			if(IsFirstLessThanSecond(MultiplyByFactor(&pES_Obj->maximalFar.value, &newFar, pES_Obj->comparer.factorFar + 1),
++									 MultiplyByFactor(&pES_Obj->previousNear, &newNear, pES_Obj->comparer.factorNear)))
++			{
++				pES_Obj->doubleTalkDetectionCount = 0;
++			}
++			else
++			{
++				pES_Obj->doubleTalkDetectionCount--;
++				nearSample /= ES_DIVISOR_VALUE;
++			}
++		}
++	}
++
++    pES_Obj->tailIndex = (pES_Obj->tailIndex + 1) % pES_Obj->windowSize;
++
++    return nearSample;
++}
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/supress/suppressor.h zaptel-xpp-XJ8JZx_dist/xpp/supress/suppressor.h
+--- zaptel-1.2.6/xpp/supress/suppressor.h	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/supress/suppressor.h	2006-06-25 14:42:21.798100000 +0300
+@@ -0,0 +1,160 @@
++#if 0  /* File Header */
++************************************************************************
++ * Copyright (C) 2006, Xorcom Ltd.
++ * 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.
++************************************************************************
++#endif /* File Header */
++
++
++#ifndef __SUPPRESSOR_H__
++#define __SUPPRESSOR_H__
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++
++#ifndef WIN32
++#define FILE void
++#endif
++
++#define ES_NEAR_MIX
++#define ES_NEAR_CLEAN
++#define ES_FAR_CLEAN
++
++#define ES_ALFA_INT					32  
++
++#define ES_DIVISOR_VALUE			64	
++
++#define ES_MAX_DTD_COUNT_6_DB		16  
++#define ES_MAX_DTD_COUNT_3_DB		32  
++#define ES_MAX_DTD_COUNT_0_DB		64  
++
++#define ES_COMPARE_MULTI_6_DB_NEAR	2	
++#define ES_COMPARE_MULTI_6_DB_FAR	1	
++
++#define ES_COMPARE_MULTI_3_DB_NEAR	3
++#define ES_COMPARE_MULTI_3_DB_FAR	2
++
++#define ES_COMPARE_MULTI_0_DB_NEAR	1
++#define ES_COMPARE_MULTI_0_DB_FAR	1
++
++#define ES_SAMPLES_IN_4_MSEC		(4 * 8)
++#define ES_SAMPLES_IN_8_MSEC		(8 * 8)
++#define ES_SAMPLES_IN_16_MSEC		(16 * 8)
++#define ES_SAMPLES_IN_32_MSEC		(32 * 8)
++#define ES_SAMPLES_IN_64_MSEC		(64 * 8)
++#define ES_SAMPLES_IN_128_MSEC		(128 * 8)
++#define ES_SAMPLES_IN_256_MSEC		(256 * 8)
++
++
++typedef enum
++{
++	ES_FALSE						= 0,
++	ES_TRUE							= 1
++}
++ES_BOOL;
++
++
++typedef enum
++{
++    ES_HYBRID_LOSS_LEVEL_6_DB		= 0,
++    ES_HYBRID_LOSS_LEVEL_3_DB		= 1,
++    ES_HYBRID_LOSS_LEVEL_0_DB		= 2
++}
++ES_HYBRID_LOSS_LEVEL;
++
++
++typedef	enum
++{
++	ES_WINDOW_SIZE_ID_4_MSEC		= 0,
++	ES_WINDOW_SIZE_ID_8_MSEC		= 1,
++	ES_WINDOW_SIZE_ID_16_MSEC		= 2,
++	ES_WINDOW_SIZE_ID_32_MSEC		= 3,
++	ES_WINDOW_SIZE_ID_64_MSEC		= 4,
++	ES_WINDOW_SIZE_ID_128_MSEC		= 5,
++	ES_WINDOW_SIZE_ID_256_MSEC		= 6
++}
++ES_WINDOW_SIZE_ID;
++
++
++typedef struct
++{
++    unsigned char					factorNear;
++    unsigned char					factorFar;
++}
++ES_COMPARER;
++
++
++typedef struct
++{
++    unsigned long					integer;
++    unsigned long					fractional;
++}
++ES_LONG_VALUE;
++
++
++typedef struct
++{
++	int								index;
++	ES_LONG_VALUE					value;
++}
++ES_MAXIMAL_FAR;
++
++
++typedef struct
++{
++    ES_BOOL							initialized;
++	int								windowSize;
++	ES_MAXIMAL_FAR					maximalFar;
++    int								moduleOfPreviousFar;
++    ES_LONG_VALUE					previousFar;
++    int								moduleOfPreviousNear;
++    ES_LONG_VALUE					previousNear;
++    int								tailIndex;
++    int								doubleTalkDetectionCount;
++    int								doubleTalkDetectionCountMax;
++	int								echoDetectionCount;
++    int								echoDetectionCountMax;
++    ES_COMPARER						comparer;
++	ES_LONG_VALUE					pHistoryOfFar[1];
++}
++SUPPRESSOR;
++
++
++static inline int ES_GetObjectSize(
++	ES_WINDOW_SIZE_ID				sizeId);
++
++static inline ES_BOOL ES_Initialize(
++	void*							pObject,
++	ES_WINDOW_SIZE_ID				sizeId,
++	ES_HYBRID_LOSS_LEVEL			level);
++
++static inline ES_BOOL ES_Reset(
++	void*							pObject);
++
++static inline short ES_NEAR_CLEAN ES_ProcessSamples(
++	void*							pObject,
++	ES_FAR_CLEAN short				farSample,
++	ES_NEAR_MIX  short				nearSample);
++
++
++#if __cplusplus
++}
++#endif
++
++#endif /* __SUPPRESSOR_H__ */
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/sync.sh zaptel-xpp-XJ8JZx_dist/xpp/sync.sh
+--- zaptel-1.2.6/xpp/sync.sh	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/sync.sh	1970-01-01 02:00:00.000000000 +0200
+@@ -1,31 +0,0 @@
+-#!/bin/sh
+-
+-set -e
+-
+-SVN_ROOT=/home/tzafrir/Proj/Svn
+-XORTEL_DIR=$SVN_ROOT/xpp-zaptel/trunk/xortel
+-XPP_DIR=$SVN_ROOT/xpp-zaptel/trunk/zaptel/xpp
+-TARGET_DIR=xpp
+-FIRMWARE=$SVN_ROOT/fpgafirmware/init.dat
+-
+-curdir=$PWD
+-cd $XORTEL_DIR
+-  cd ..; svn update; 
+-  cd xortel
+-  make FIRMWARE="$FIRMWARE" ../zaptel/xpp/slic_init.inc
+-cd $curdir
+-
+-cp -a $XORTEL_DIR/FPGA_XPD.hex ${TARGET_DIR}/
+-cp -a $XORTEL_DIR/xpd.h ${TARGET_DIR}/
+-cp -a $XORTEL_DIR/xpp_zap.[ch] ${TARGET_DIR}/
+-cp -a $XORTEL_DIR/xproto.[ch] ${TARGET_DIR}/
+-cp -a $XORTEL_DIR/xdefs.h ${TARGET_DIR}/
+-cp -a $XORTEL_DIR/xpp_usb.c ${TARGET_DIR}/
+-cp -a $XORTEL_DIR/zap_debug.[ch] ${TARGET_DIR}/
+-cp -a $XORTEL_DIR/slic.[ch] ${TARGET_DIR}/
+-cp -a $XORTEL_DIR/card_*.[ch] ${TARGET_DIR}/
+-cp -a $XORTEL_DIR/xpp_fxloader{,.usermap} ${TARGET_DIR}/
+-cp -a $XORTEL_DIR/xpp_modprobe ${TARGET_DIR}/
+-cp -a $XORTEL_DIR/gen_slic_init ${TARGET_DIR}/
+-cp -a $XPP_DIR/Makefile ${TARGET_DIR}/
+-cp -a $XPP_DIR/slic_init.inc ${TARGET_DIR}/
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/FPGA_FXS.hex zaptel-xpp-XJ8JZx_dist/xpp/utils/FPGA_FXS.hex
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/fpga_load.8 zaptel-xpp-XJ8JZx_dist/xpp/utils/fpga_load.8
+--- zaptel-1.2.6/xpp/utils/fpga_load.8	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/utils/fpga_load.8	2006-04-16 16:20:04.117881000 +0300
+@@ -0,0 +1,72 @@
++.TH "FPGA_LOAD" "8" "16 April 2006" "" ""
++
++.SH NAME
++ztcfg \- reads and loads zaptel.conf
++.SH SYNOPSIS
++
++.B fpga_load 
++[\fB-g\fR|\fB-d\fR] [\fB-v\fR] \fB-D\fR/proc/bus/usb/\fIBUS/DEV\fR 
++
++.B fpga_load 
++[\fB-g\fR] [\fB-v\fR] \fB-D\fR/proc/bus/usb/\fIBUS/DEV\fR \fB-I \fIfirmware.hex\fR [\fB-b \fIdump.bin\fR]
++
++.B fpga_load -h
++
++.SH DESCRIPTION
++.B fpga_load 
++loads the FPGA firmware to the Xorcom Astribank device.
++The syntax resembles that of fxload(8).
++
++.SH OPTIONS
++.B -d
++.I dump.bin
++.RS
++Before writing firmware, bump the processed binary file to 
++.I dump.bin\fR.
++.RE
++
++.B -d
++.RS
++Print Version number bytes from eeprom (to standard output). Implies -g.
++.RE
++
++.B -D 
++.I DEVICE
++.RS
++Required. The device to read from/write to. This is normally 
++/proc/bus/usb/\fIbus_num\fR/\fIdevice_num\fR , where \fIbus_num\fR and 
++\fIdevice_num\fR are the first two numbers in the output of lsusb(8).
++.RE
++
++.B -g
++.RS
++Dump all eeprom data to standard error.
++.RE
++
++.B -I
++.I fireware_file
++.RS
++The firmware file to write to the device.
++.RE
++
++.B -v
++.RS
++Be verobse.
++.RE
++
++.B -h
++.RS
++Displays usage message.
++.RE
++
++.SH SEE ALSO
++fxload(8), lsusb(8)
++
++.SH AUTHOR
++This manual page was written by Tzafrir Cohen <tzafrir.cohen at xorcom.com> .
++Permission is granted to copy, distribute and/or modify this document under
++the terms of the GNU General Public License, Version 2 any 
++later version published by the Free Software Foundation.
++
++On Debian systems, the complete text of the GNU General Public
++License can be found in /usr/share/common-licenses/GPL.
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/fpga_load.c zaptel-xpp-XJ8JZx_dist/xpp/utils/fpga_load.c
+--- zaptel-1.2.6/xpp/utils/fpga_load.c	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/utils/fpga_load.c	2006-06-01 11:50:28.294045000 +0300
+@@ -0,0 +1,822 @@
++#include <stdio.h>
++#include <assert.h>
++#include <string.h>
++#include <stdint.h>
++#include <errno.h>
++#include <sys/types.h>
++#include <sys/param.h>
++#include <usb.h>
++#include "hexfile.h"
++
++static const char rcsid[] = "$Id: fpga_load.c 1304 2006-06-01 08:50:28Z tzafrir $";
++
++#define	ERR(fmt, arg...)	fprintf(stderr, "%s: ERROR: " fmt, progname, ## arg)
++#define	INFO(fmt, arg...)	fprintf(stderr, "%s: " fmt, progname, ## arg)
++
++static int	verbose = LOG_WARNING;
++static char	*progname;
++
++#define	MAX_HEX_LINES	2000
++#define	PACKET_SIZE	512
++#define	EEPROM_SIZE	16
++#define	SERIAL_SIZE	8
++
++enum fpga_load_packet_types {
++	STATUS_REPLY	= 0x01,
++	DATA_PACKET	= 0x01,
++#ifdef	XORCOM_INTERNAL
++	EEPROM_SET	= 0x04,
++#endif
++	EEPROM_GET	= 0x08,
++	RENUMERATE	= 0x10,
++	BAD_COMMAND	= 0xAA
++};
++
++struct myeeprom {
++	uint8_t		source;
++	uint16_t	vendor;
++	uint16_t	product;
++	uint8_t		release_major;
++	uint8_t		release_minor;
++	uint8_t		reserved;
++	uint8_t		serial[SERIAL_SIZE];
++} PACKED;
++
++struct fpga_packet_header {
++	struct {
++		uint8_t		op;
++	} PACKED header;
++	union {
++		struct {
++			uint16_t	seq;
++			uint8_t		status;
++		} PACKED status_reply;
++		struct {
++			uint16_t	seq;
++			uint8_t		reserved;
++			uint8_t		data[ZERO_SIZE];
++		} PACKED data_packet;
++		struct {
++			struct myeeprom		data;
++		} PACKED eeprom_set;
++		struct {
++			struct myeeprom		data;
++		} PACKED eeprom_get;
++	} d;
++} PACKED;
++
++enum fpga_load_status {
++	FW_FAIL_RESET	= 1,
++	FW_FAIL_TRANS	= 2,
++	FW_TRANS_OK	= 4,
++	FW_CONFIG_DONE	= 8
++};
++
++int my_usb_device(struct usb_device *dev, usb_dev_handle *handle);
++
++const char *load_status2str(enum fpga_load_status s)
++{
++	switch(s) {
++		case FW_FAIL_RESET: return "FW_FAIL_RESET";
++		case FW_FAIL_TRANS: return "FW_FAIL_TRANS";
++		case FW_TRANS_OK: return "FW_TRANS_OK";
++		case FW_CONFIG_DONE: return "FW_CONFIG_DONE";
++		default: return "UNKNOWN";
++	}
++}
++
++int path_of_dev(char buf[], unsigned int buflen, struct usb_device *dev)
++{
++	return snprintf(buf, buflen, "/proc/bus/usb/%s/%s", dev->bus->dirname, dev->filename);
++}
++
++struct usb_device *dev_of_path(const char *path)
++{
++	struct usb_bus		*bus;
++	struct usb_device	*dev;
++	char			dirname[PATH_MAX];
++	char			filename[PATH_MAX];
++	const char		prefix[] = "/proc/bus/usb/";
++	const int		prefix_len = strlen(prefix);
++	const char		*p;
++	int			bnum;
++	int			dnum;
++	int			ret;
++
++	assert(path != NULL);
++	if(strncmp(prefix, path, prefix_len) != 0) {
++		ERR("wrong path: '%s'\n", path);
++		return NULL;
++	}
++	p = path + prefix_len;
++	ret = sscanf(p, "%d/%d", &bnum, &dnum);
++	if(ret != 2) {
++		ERR("wrong path tail: '%s'\n", p);
++		return NULL;
++	}
++	sprintf(dirname, "%03d", bnum);
++	sprintf(filename, "%03d", dnum);
++	for (bus = usb_busses; bus; bus = bus->next) {
++		if(strcmp(bus->dirname, dirname) != 0)
++			continue;
++		for (dev = bus->devices; dev; dev = dev->next) {
++			if(strcmp(dev->filename, filename) == 0)
++				return dev;
++		}
++	}
++	ERR("no usb device match '%s'\n", path);
++	return NULL;
++}
++
++int get_usb_string(char *buf, unsigned int len, uint16_t item, usb_dev_handle *handle)
++{
++	char	tmp[BUFSIZ];
++	int	ret;
++
++	if (!item)
++		return 0;
++	ret = usb_get_string_simple(handle, item, tmp, BUFSIZ);
++	if (ret <= 0)
++		return ret;
++	return snprintf(buf, len, "%s", tmp);
++}
++
++/* My device parameters */
++#define	MY_INTERFACE	0
++#define	MY_CONFIG	1
++#define	MY_ENDPOINTS	4
++
++#define	MY_EP_OUT	0x04
++#define	MY_EP_IN	0x88
++
++#define	TIMEOUT		5000
++
++static const int my_endpoints[MY_ENDPOINTS] = {
++	0x02,
++	0x04,
++	0x86,
++	0x88
++};
++
++void usb_cleanup(usb_dev_handle *handle)
++{
++	if(usb_release_interface(handle, MY_INTERFACE) != 0) {
++		ERR("Releasing interface: usb: %s\n", usb_strerror());
++	}
++	if(usb_close(handle) != 0) {
++		ERR("Closing device: usb: %s\n", usb_strerror());
++	}
++}
++
++void print_bcd_ver(const struct myeeprom *eeprom)
++{
++	/* In this case, print only the version. Also note that this 
++	 * is an output, and sent to stdout
++	 */
++	printf("%d.%03d\n", eeprom->release_major, eeprom->release_minor);
++	return;
++}
++
++void dump_eeprom(const struct myeeprom *eeprom)
++{
++	const uint8_t	*data = eeprom->serial;
++
++	INFO("Source:  0x%02X\n", eeprom->source);
++	INFO("Vendor:  0x%04X\n", eeprom->vendor);
++	INFO("Product: 0x%04X\n", eeprom->product);
++	INFO("Release: %d.%03d\n", eeprom->release_major, eeprom->release_minor);
++	INFO("Data:    0x[%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X]\n",
++			data[0], data[1], data[2], data[3],
++			data[4], data[5], data[6], data[7]); 
++}
++
++void dump_packet(const char *buf, int len)
++{
++	int	i;
++
++	for(i = 0; i < len; i++)
++		INFO("dump: %2d> 0x%02X\n", i, (uint8_t)buf[i]);
++}
++
++#ifdef	XORCOM_INTERNAL
++int eeprom_set(struct usb_dev_handle *handle, const struct myeeprom *eeprom)
++{
++	int				ret;
++	int				len;
++	char				buf[PACKET_SIZE];
++	struct fpga_packet_header	*phead = (struct fpga_packet_header *)buf;
++
++	if(verbose >= LOG_INFO)
++		INFO("%s Start...\n", __FUNCTION__);
++	assert(handle != NULL);
++	phead->header.op = EEPROM_SET;
++	memcpy(&phead->d.eeprom_set.data, eeprom, EEPROM_SIZE);
++	len = sizeof(phead->d.eeprom_set) + sizeof(phead->header.op);
++	if(verbose >= LOG_INFO) {
++		INFO("%s write %d bytes\n", __FUNCTION__, len);
++		dump_packet((char *)phead, len);
++	}
++	ret = usb_bulk_write(handle, MY_EP_OUT, (char *)phead, len, TIMEOUT);
++	if(ret < 0) {
++		ERR("usb: bulk_write failed (%d)\n", ret);
++		return ret;
++	} else if(ret != len) {
++		ERR("usb: bulk_write short write (%d)\n", ret);
++		return -EFAULT;
++	}
++	ret = usb_bulk_read(handle, MY_EP_IN, buf, sizeof(buf), TIMEOUT);
++	if(ret < 0) {
++		ERR("usb: bulk_read failed (%d)\n", ret);
++		return ret;
++	} else if(ret == 0)
++		return 0;
++	phead = (struct fpga_packet_header *)buf;
++	if(phead->header.op == BAD_COMMAND) {
++		ERR("BAD_COMMAND\n");
++		return -EINVAL;
++	} else if(phead->header.op != EEPROM_SET) {
++		ERR("Got unexpected reply op=%d\n", phead->header.op);
++		return -EINVAL;
++	}
++	if(verbose >= LOG_INFO) {
++		INFO("%s read %d bytes\n", __FUNCTION__, ret);
++		dump_packet(buf, ret);
++	}
++	return 0;
++}
++#endif
++
++int eeprom_get(struct usb_dev_handle *handle, struct myeeprom *eeprom)
++{
++	int				ret;
++	int				len;
++	char				buf[PACKET_SIZE];
++	struct fpga_packet_header	*phead = (struct fpga_packet_header *)buf;
++
++	assert(handle != NULL);
++	if(verbose >= LOG_INFO)
++		INFO("%s Start...\n", __FUNCTION__);
++	phead->header.op = EEPROM_GET;
++	len = sizeof(phead->header.op);		/* warning: sending small packet */
++	if(verbose >= LOG_INFO) {
++		INFO("%s write %d bytes\n", __FUNCTION__, len);
++		dump_packet(buf, len);
++	}
++	ret = usb_bulk_write(handle, MY_EP_OUT, (char *)phead, len, TIMEOUT);
++	if(ret < 0) {
++		ERR("usb: bulk_write failed (%d)\n", ret);
++		return ret;
++	} else if(ret != len) {
++		ERR("usb: bulk_write short write (%d)\n", ret);
++		return -EFAULT;
++	}
++	ret = usb_bulk_read(handle, MY_EP_IN, buf, sizeof(buf), TIMEOUT);
++	if(ret < 0) {
++		ERR("usb: bulk_read failed (%d)\n", ret);
++		return ret;
++	} else if(ret == 0)
++		return 0;
++	phead = (struct fpga_packet_header *)buf;
++	if(phead->header.op == BAD_COMMAND) {
++		ERR("BAD_COMMAND\n");
++		return -EINVAL;
++	} else if(phead->header.op != EEPROM_GET) {
++		ERR("Got unexpected reply op=%d\n", phead->header.op);
++		return -EINVAL;
++	}
++	if(verbose >= LOG_INFO) {
++		INFO("%s read %d bytes\n", __FUNCTION__, ret);
++		dump_packet(buf, ret);
++	}
++	memcpy(eeprom, &phead->d.eeprom_get.data, EEPROM_SIZE);
++	return 0;
++}
++
++int send_hexline(struct usb_dev_handle *handle, struct hexline *hexline, int seq)
++{
++	int				ret;
++	int				len;
++	uint8_t				*data;
++	char				buf[PACKET_SIZE];
++	struct fpga_packet_header	*phead = (struct fpga_packet_header *)buf;
++	enum fpga_load_status		status;
++
++	assert(handle != NULL);
++	assert(hexline != NULL);
++	len = hexline->d.content.header.ll;	/* don't send checksum */
++	data = hexline->d.content.tt_data.data;
++	if(hexline->d.content.header.tt != TT_DATA) {
++		ERR("Bad record %d type = %d\n", seq, hexline->d.content.header.tt);
++		return -EINVAL;
++	}
++	phead->header.op = DATA_PACKET;
++	phead->d.data_packet.seq = seq;
++	phead->d.data_packet.reserved = 0x00;
++	memcpy(phead->d.data_packet.data, data, len);
++	len += sizeof(phead);
++	if(verbose >= LOG_INFO)
++		INFO("%04d+\r", seq);
++	ret = usb_bulk_write(handle, MY_EP_OUT, (char *)phead, len, TIMEOUT);
++	if(ret < 0) {
++		ERR("usb: bulk_write failed (%d)\n", ret);
++		return ret;
++	} else if(ret != len) {
++		ERR("usb: bulk_write short write (%d)\n", ret);
++		return -EFAULT;
++	}
++	ret = usb_bulk_read(handle, MY_EP_IN, buf, sizeof(buf), TIMEOUT);
++	if(ret < 0) {
++		ERR("usb: bulk_read failed (%d)\n", ret);
++		return ret;
++	} else if(ret == 0)
++		return 0;
++	if(verbose >= LOG_INFO)
++		INFO("%04d-\r", seq);
++	phead = (struct fpga_packet_header *)buf;
++	if(phead->header.op != STATUS_REPLY) {
++		ERR("Got unexpected reply op=%d\n", phead->header.op);
++		return -EINVAL;
++	}
++	status = (enum fpga_load_status)phead->d.status_reply.status;
++	switch(status) {
++		case FW_TRANS_OK:
++		case FW_CONFIG_DONE:
++			break;
++		case FW_FAIL_RESET:
++		case FW_FAIL_TRANS:
++			ERR("status reply %s (%d)\n", load_status2str(status), status);
++			if(verbose >= LOG_INFO)
++				dump_packet(buf, ret);
++			return -EPROTO;
++		default:
++			ERR("Unknown status reply %d\n", status);
++			if(verbose >= LOG_INFO)
++				dump_packet(buf, ret);
++			return -EPROTO;
++	}
++	return 0;
++}
++
++//. returns > 0 - ok, the number of lines sent
++//. returns < 0 - error number
++int send_splited_hexline(struct usb_dev_handle *handle, struct hexline *hexline, int seq, uint8_t maxwidth)
++{
++	struct hexline *extraline;
++	int linessent = 0;
++	int allocsize;
++	int extra_offset = 0;
++	unsigned int this_line = 0;
++	uint8_t bytesleft = 0;
++	
++	if(!hexline) {
++		ERR("Bad record %d type = %d\n", seq, hexline->d.content.header.tt);
++		return -EINVAL;
++	}
++	bytesleft = hexline->d.content.header.ll;
++	// split the line into several lines
++	while (bytesleft > 0) {
++		int status;
++		this_line = (bytesleft >= maxwidth) ? maxwidth : bytesleft;
++		allocsize = sizeof(struct hexline) + this_line + 1;
++		// generate the new line
++		if((extraline = (struct hexline *)malloc(allocsize)) == NULL) {
++			ERR("Not enough memory for spliting the lines\n" );
++			return -EINVAL;
++		}
++		memset( extraline, 0, allocsize );
++		extraline->d.content.header.ll		= this_line;
++		extraline->d.content.header.offset	= hexline->d.content.header.offset + extra_offset;
++		extraline->d.content.header.tt		= hexline->d.content.header.tt;
++		memcpy( extraline->d.content.tt_data.data, hexline->d.content.tt_data.data+extra_offset, this_line);
++		status = send_hexline( handle, extraline, seq+linessent );
++		// cleanups
++		free(extraline);
++		extra_offset += this_line;
++		bytesleft -= this_line;
++		if (status)
++			return status;
++		linessent++;
++	}
++	return linessent;
++}
++
++int my_usb_device(struct usb_device *dev, usb_dev_handle *handle)
++{
++	struct usb_device_descriptor	*dev_desc;
++	struct usb_config_descriptor	*config_desc;
++	struct usb_interface		*interface;
++	struct usb_interface_descriptor	*iface_desc;
++	struct usb_endpoint_descriptor	*endpoint;
++	char				iManufacturer[BUFSIZ];
++	char				iProduct[BUFSIZ];
++	int				ret;
++	int				i;
++
++	assert(dev != NULL);
++	dev_desc = &dev->descriptor;
++	config_desc = dev->config;
++	interface = config_desc->interface;
++	iface_desc = interface->altsetting;
++	if(verbose >= LOG_INFO)
++		INFO("Vendor:Product=%04X:%04X Class=%d (endpoints=%d)\n",
++				dev_desc->idVendor, dev_desc->idProduct, dev_desc->bDeviceClass, iface_desc->bNumEndpoints);
++	if(iface_desc->bInterfaceClass != 0xFF) {
++		ERR("Wrong Interface class %d\n", iface_desc->bInterfaceClass);
++		return -EINVAL;
++	}
++	if(iface_desc->bInterfaceNumber != MY_INTERFACE) {
++		ERR("Wrong Interface number %d\n", iface_desc->bInterfaceNumber);
++		return -EINVAL;
++	}
++	if(iface_desc->bNumEndpoints != MY_ENDPOINTS) {
++		ERR("Wrong number of endpoints: %d\n", iface_desc->bNumEndpoints);
++		return -EINVAL;
++	}
++	endpoint = iface_desc->endpoint;
++	for(i = 0; i < iface_desc->bNumEndpoints; i++, endpoint++) {
++		if(endpoint->bEndpointAddress != my_endpoints[i]) {
++			ERR("Wrong endpoint %d: address = 0x%X\n", i, endpoint->bEndpointAddress);
++			return -EINVAL;
++		}
++		if(endpoint->bEndpointAddress == MY_EP_OUT || endpoint->bEndpointAddress == MY_EP_IN) {
++			if(endpoint->wMaxPacketSize > PACKET_SIZE) {
++				ERR("Endpoint #%d wMaxPacketSize too large (%d)\n", i, endpoint->wMaxPacketSize);
++				return -EINVAL;
++			}
++		}
++	}
++	if(usb_reset(handle) != 0) {
++		ERR("Reseting device: usb: %s\n", usb_strerror());
++	}
++	if(usb_set_configuration(handle, MY_CONFIG) != 0) {
++		ERR("usb: %s\n", usb_strerror());
++		return -EINVAL;
++	}
++	if(usb_claim_interface(handle, MY_INTERFACE) != 0) {
++		ERR("usb: %s\n", usb_strerror());
++		return -EINVAL;
++	}
++	if(usb_resetep(handle, MY_EP_OUT) != 0) {
++		ERR("usb: %s\n", usb_strerror());
++		return -EINVAL;
++	}
++	if(usb_resetep(handle, MY_EP_IN) != 0) {
++		ERR("usb: %s\n", usb_strerror());
++		return -EINVAL;
++	}
++	ret = get_usb_string(iManufacturer, BUFSIZ, dev_desc->iManufacturer, handle);
++	ret = get_usb_string(iProduct, BUFSIZ, dev_desc->iProduct, handle);
++	if(verbose >= LOG_INFO)
++		INFO("iManufacturer=%s iProduct=%s\n", iManufacturer, iProduct);
++	return 0;
++}
++
++int renumerate_device(struct usb_dev_handle *handle)
++{
++	char				buf[PACKET_SIZE];
++	struct fpga_packet_header	*phead = (struct fpga_packet_header *)buf;
++	int				ret;
++
++	assert(handle != NULL);
++	if(verbose >= LOG_INFO)
++		INFO("Renumerating\n");
++	phead->header.op = RENUMERATE;
++	ret = usb_bulk_write(handle, MY_EP_OUT, (char *)phead, 1, TIMEOUT);
++	if(ret < 0) {
++		ERR("usb: bulk_write failed (%d)\n", ret);
++		return ret;
++	} else if(ret != 1) {
++		ERR("usb: bulk_write short write (%d)\n", ret);
++		return -EFAULT;
++	}
++	return 0;
++}
++
++int fpga_load(struct usb_dev_handle *handle, const struct hexdata *hexdata)
++{
++	unsigned int	i;
++	int		ret;
++	int		finished = 0;
++	
++	assert(handle != NULL);
++	if(verbose >= LOG_INFO)
++		INFO("Start...\n");
++		
++	for(i = 0; i < hexdata->maxlines; i++) {
++		struct hexline	*hexline = hexdata->lines[i];
++
++		if(!hexline)
++			break;
++		if(finished) {
++			ERR("Extra data after End Of Data Record (line %d)\n", i);
++			return 0;
++		}
++		if(hexline->d.content.header.tt == TT_EOF) {
++			INFO("End of data\n");
++			finished = 1;
++			continue;
++		}
++		if((ret = send_hexline(handle, hexline, i)) != 0) {
++			perror("Failed sending hexline");
++			return 0;
++		}
++	}
++	if(verbose >= LOG_INFO)
++		INFO("Finished...\n");
++	return 1;
++}
++
++int fpga_load_usb1(struct usb_dev_handle *handle, const struct hexdata *hexdata)
++{
++	unsigned int	i,j=0;
++	int		ret;
++	int		finished = 0;
++	
++	assert(handle != NULL);
++	if(verbose >= LOG_INFO)
++		INFO("Start...\n");
++		
++	// i - is the line number
++	// j - is the sequence number, on USB 2, i=j, but on
++	//     USB 1 send_splited_hexline may increase the sequence
++	//     number, as it needs 
++	for(i = 0; i < hexdata->maxlines; i++) {
++		struct hexline	*hexline = hexdata->lines[i];
++		
++
++		if(!hexline)
++			break;
++		if(finished) {
++			ERR("Extra data after End Of Data Record (line %d)\n", i);
++			return 0;
++		}
++		if(hexline->d.content.header.tt == TT_EOF) {
++			INFO("End of data\n");
++			finished = 1;
++			continue;
++		}
++		
++		if((ret = send_splited_hexline(handle, hexline, j, 60)) < 0) {
++			perror("Failed sending hexline (splitting did not help)");
++			return 0;
++		}
++		j += ret;
++	}
++	if(verbose >= LOG_INFO)
++		INFO("Finished...\n");
++	return 1;
++}
++
++#include <getopt.h>
++
++void usage()
++{
++	fprintf(stderr, "Usage: %s -D /proc/bus/usb/<bus>/<dev> [options...]\n", progname);
++	fprintf(stderr, "\tOptions:\n");
++	fprintf(stderr, "\t\t[-b <binfile>]	# output to <binfile>\n");
++	fprintf(stderr, "\t\t[-d]		# Get device version from eeprom\n");
++	fprintf(stderr, "\t\t[-I <hexfile>]	# Input from <hexfile>\n");
++	fprintf(stderr, "\t\t[-g]		# Get eeprom from device\n");
++	fprintf(stderr, "\t\t[-C srC byte]	# Set Address sourCe (default: C0)\n");
++	fprintf(stderr, "\t\t[-V vendorid]	# Set Vendor id on device\n");
++	fprintf(stderr, "\t\t[-P productid]	# Set Product id on device\n");
++	fprintf(stderr, "\t\t[-R release]	# Set Release. 2 dot separated decimals\n");
++	fprintf(stderr, "\t\t[-S serial]	# Set Serial. 8 comma separated numbers\n");
++	exit(1);
++}
++
++static void parse_report_func(int level, const char *msg, ...)
++{
++	va_list ap;
++
++	va_start(ap, msg);
++	if(level <= verbose)
++		vfprintf(stderr, msg, ap);
++	va_end(ap);
++}
++
++int hasUSB2( struct usb_device	*dev )
++{
++	if (dev->config->interface->altsetting->endpoint->wMaxPacketSize != 512)
++		return 0;
++	else
++		return 1;
++}
++
++// usb_interface_descriptor->usb_endpoint_descriptor.wMaxPacketSize
++
++int main(int argc, char *argv[])
++{
++	struct usb_device	*dev;
++	usb_dev_handle		*handle;
++	const char		*devpath = NULL;
++	const char		*binfile = NULL;
++	const char		*hexfile = NULL;
++	struct hexdata		*hexdata = NULL;
++	struct myeeprom		eeprom_buf;
++	int			opt_read_eeprom = 0;
++	int			opt_print_bcdver_only = 0;
++#ifdef	XORCOM_INTERNAL
++	int			opt_write_eeprom = 0;
++	char			*vendor = NULL;
++	char			*source = NULL;
++	int			is_source_given = 0;
++	char			*product = NULL;
++	char			*release = NULL;
++	char			*serial = NULL;
++	uint8_t			serial_buf[SERIAL_SIZE];
++	const char		options[] = "b:C:dD:ghI:vV:P:R:S:";
++#else
++	const char		options[] = "b:dD:ghI:v";
++#endif
++	int			ret = 0;
++
++	progname = argv[0];
++	assert(sizeof(struct fpga_packet_header) <= PACKET_SIZE);
++	assert(sizeof(struct myeeprom) == EEPROM_SIZE);
++	while (1) {
++		int	c;
++
++		c = getopt (argc, argv, options);
++		if (c == -1)
++			break;
++
++		switch (c) {
++			case 'D':
++				devpath = optarg;
++				break;
++			case 'b':
++				binfile = optarg;
++				break;
++			case 'd':
++				opt_print_bcdver_only = 1;
++				opt_read_eeprom = 1;
++				break;
++			case 'g':
++				opt_read_eeprom = 1;
++				break;
++			case 'I':
++				hexfile = optarg;
++				break;
++#ifdef	XORCOM_INTERNAL
++			case 'V':
++				vendor = optarg;
++				break;
++			case 'C':
++				source = optarg;
++				is_source_given = 1;
++				break;
++			case 'P':
++				product = optarg;
++				break;
++			case 'R':
++				release = optarg;
++				break;
++			case 'S':
++				serial = optarg;
++				{
++					int		i;
++					char		*p;
++					unsigned long	val;
++
++					p = strtok(serial, ",");
++					for(i = 0; i < SERIAL_SIZE && p; i++) {
++						val = strtoul(p, NULL, 0);
++						if(val > 0xFF) {
++							ERR("Value #%d for -S option is too large (%lu)\n", i+1, val);
++							usage();
++						}
++						serial_buf[i] = val;
++						p = strtok(NULL, ",");
++					}
++					if(i < SERIAL_SIZE) {
++						ERR("got only %d values for -S option. Need %d\n", i, SERIAL_SIZE);
++						usage();
++					}
++				}
++
++				break;
++#endif
++			case 'v':
++				verbose++;
++				break;
++			case 'h':
++			default:
++				ERR("Unknown option '%c'\n", c);
++				usage();
++		}
++	}
++
++	if (optind != argc) {
++		usage();
++	}
++	if(hexfile) {
++		parse_hexfile_set_reporting(parse_report_func);
++		hexdata = parse_hexfile(hexfile, MAX_HEX_LINES);
++		if(!hexdata) {
++			ERR("Bailing out\n");
++			exit(1);
++		}
++		if(binfile) {
++			dump_binary(hexdata, binfile);
++			return 0;
++		}
++	}
++	if(!devpath) {
++		ERR("Missing device path\n");
++		usage();
++	}
++	if(verbose)
++		INFO("Startup %s\n", devpath);
++
++	usb_init();
++	usb_find_busses();
++	usb_find_devices();
++	dev = dev_of_path(devpath);
++	if(!dev) {
++		ERR("Bailing out\n");
++		exit(1);
++	}
++	handle = usb_open(dev);
++	if(!handle) {
++		ERR("Failed to open usb device '%s/%s': %s\n", dev->bus->dirname, dev->filename, usb_strerror());
++		return -ENODEV;
++	}
++	if(my_usb_device(dev, handle)) {
++		ERR("Foreign usb device '%s/%s'\n", dev->bus->dirname, dev->filename);
++		ret = -ENODEV;
++		goto dev_err;
++	}
++	
++	if(hexdata) {
++		int status;
++
++		if (hasUSB2(dev))
++			status = fpga_load(handle, hexdata);
++		else {
++			INFO("Warning: working on a low end USB1 backend\n");
++			status = fpga_load_usb1(handle, hexdata);
++		}
++
++		if(!status) {
++			ERR("FPGA loading failed\n");
++			ret = -ENODEV;
++			goto dev_err;
++		}
++		ret = renumerate_device(handle);
++		if(ret < 0) {
++			ERR("Renumeration failed: errno=%d\n", ret);
++			goto dev_err;
++		}
++	}
++#ifdef	XORCOM_INTERNAL
++	if(vendor || product || release || serial || source )
++		opt_read_eeprom = opt_write_eeprom = 1;
++#endif
++	if(opt_read_eeprom) {
++		ret = eeprom_get(handle, &eeprom_buf);
++		if(ret < 0) {
++			ERR("Failed reading eeprom: %d\n", ret);
++			goto dev_err;
++		}
++		if (opt_print_bcdver_only)
++			print_bcd_ver(&eeprom_buf);
++		else
++			dump_eeprom(&eeprom_buf);
++	}
++#ifdef XORCOM_INTERNAL
++	if(opt_write_eeprom) {
++		// FF: address source is from device. C0: from eeprom
++		if (is_source_given)
++			eeprom_buf.source = strtoul(source, NULL, 0);
++		else
++			eeprom_buf.source = 0xC0;
++		if(vendor)
++			eeprom_buf.vendor = strtoul(vendor, NULL, 0);
++		if(product)
++			eeprom_buf.product = strtoul(product, NULL, 0);
++		if(release) {
++			int	release_major = 0;
++			int	release_minor = 0;
++
++			sscanf(release, "%d.%d", &release_major, &release_minor);
++			eeprom_buf.release_major = release_major;
++			eeprom_buf.release_minor = release_minor;
++		}
++		if(serial) {
++			memcpy(eeprom_buf.serial, serial_buf, SERIAL_SIZE);
++		}
++		dump_eeprom(&eeprom_buf);
++		ret = eeprom_set(handle, &eeprom_buf);
++		if(ret < 0) {
++			ERR("Failed writing eeprom: %d\n", ret);
++			goto dev_err;
++		}
++	}
++#endif
++	if(verbose)
++		INFO("Exiting\n");
++dev_err:
++	usb_cleanup(handle);
++	return ret;
++}
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/genzaptelconf zaptel-xpp-XJ8JZx_dist/xpp/utils/genzaptelconf
+--- zaptel-1.2.6/xpp/utils/genzaptelconf	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/utils/genzaptelconf	2006-06-27 10:06:55.011704000 +0300
+@@ -0,0 +1,953 @@
++#! /bin/bash
++
++# genzaptelconf: generate as smartly as you can:
++#		/etc/zaptel.conf
++#		/etc/asterisk/zapata-channels.conf (to be #include-d into zapata.conf)
++#	update:
++#		With '-M' /etc/modules (list of modules to load)
++#
++# Copyright (C) 2005 by Xorcom <support at xorcom.com>
++#
++# 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++#
++#
++# If you have any technical questions, contact 
++# Tzafrir Cohen <tzafrir.cohen at xorcom.com>
++#	
++
++# The script uses a number of bash-specific features
++# TODO: either ditch them or convert to perl
++# Don't override variables here. Override them in /etc/default/zaptel
++#
++# 0.5.3:
++# * Experimental support for Sangoma analog cards (A20x)
++# * Support OpenVox A1200P (A TDM400P clone refactored)
++# * fixed timeout for Astribank load
++# * Delete unsuccessfully-probe modules
++# 0.5.2:
++# * Now it should detect most PRI cards and even wcusb
++# 0.5.1:
++# * Initial support for ztgsm (Junghanns's PCI GSM card)
++# * Wait for the xpp module to register if just loaded
++# 0.5.0:
++# * Not trying to read from zaptel channels: we can run genzaptelconf even 
++#   with asterisk up.
++# * Don't add ztdummy to the list of detected modules
++# 0.4.4:
++# * remove ztdummy when rewriting modules file
++# * Better ISDN PRI behaviour in Israel (il)
++# 0.4.3:
++# * Added -F: to disable writing about FXS ports in zapata.conf
++# * if we have an astribank: start zaptel rather than simple ztcfg (xpd sync)
++# 0.4.2: 
++# * support for digital input/output ports of Astribank
++# * Different ISDN parameters for the Netherlands (nl)
++# * unload zaptel and its dependencies, not a hard-coded list
++# * hence we can reduce the list of modules
++
++# /etc/default/zaptel may override the following variables
++VERSION=0.5.3
++VERSION_FULL="$VERSION $Id: genzaptelconf 1202 2006-05-16 03:16:41Z tzafrir $"
++lc_country=us
++base_exten=6000
++# If set: no context changes are made in zapata-channels.conf
++#context_manual=yes
++context_lines=from-pstn      # context into which PSTN calls go
++context_phones=from-internal # context for internal phones calls.
++# The two below apply to input and output ports of the Xorcom Astribank:
++context_input=astbank-input
++context_output=astbank-output # useless, but helps marking the channels :-)
++# TODO: what about PRI/BRI?
++# If set: no group changes are made in zapata-channels.conf
++#group_manual=yes
++group_phones=5 # group for phones
++group_lines=0  # group for lines
++# set 'immediate=yes' for Asteribank input channels and 'immediate=no' 
++# for others. Note that if an Astribank is not detected, the script 
++# will set this to "no", so you can safely leave it as "yes".
++set_immediate=yes
++
++ZAPCONF_FILE=/etc/zaptel.conf
++ZAPATA_FILE=/etc/asterisk/zapata-channels.conf
++ZAPTEL_BOOT=/etc/default/zaptel
++MODLIST_FILE=/etc/modules
++exten_base_dir=/etc/asterisk/extensions-phones.d
++exten_defs_file=/etc/asterisk/extensions-defs.conf
++ZTCFG=/sbin/ztcfg
++
++# a temporary directory. Created when the switch -r is parsed on getopts
++# and deleted in the end on update_extensions_defs
++tmp_dir=
++
++# A list of all modules:
++# - the list of modules which will be probed (in this order) if -d is used
++# - The module that will be deleted from /etc/modules , if -d -M is used
++ALL_MODULES="zaphfc qozap ztgsm wctdm wctdm24xxp wcfxo wcfxs pciradio tor2 torisa wct1xxp wct4xxp wcte11xp wanpipe wcusb xpp_usb"
++
++# read default configuration from /etc/default/zaptel
++if [ -r $ZAPTEL_BOOT ]; then . $ZAPTEL_BOOT; fi
++
++# it is safe to use -c twice: the last one will be used.
++ztcfg_cmd="$ZTCFG -c $ZAPCONF_FILE"
++
++# work around a bug (that was already fixed) in the installer:
++if [ "$lc_country" = '' ]; then lc_country=us; fi
++
++force_stop_ast=no
++do_detect=no
++do_unload=no
++do_module_list=no
++verbose=no
++fxsdisable=no
++rapid_extens=''
++# global: current extension number in extensions list. Should only be 
++# changed in print_pattern:
++rapid_cur_exten=1 
++# set the TRUNK in extensidialplan dialplan defs file  rapid_conf_mode=no
++
++die() {
++	echo "$@" >&2
++	exit 1
++}
++
++say() {
++	if [ "$verbose" = no ]; then
++		return
++	fi
++	echo "$@"   >&2
++}
++
++run_ztcfg() {
++	if [ "$verbose" = no ]; then
++		$ztcfg_cmd "$@"
++	else
++		say "Reconfiguring identified channels"
++		$ztcfg_cmd -vv "$@"
++	fi
++}
++
++update_module_list() {
++	del_args=`for i in $ALL_MODULES ztdummy
++	do
++		echo "$i" | sed s:.\*:-e\ '/^&/d':
++	done`
++	add_args=`for i in $*
++	do
++		echo "$i" | sed s:.\*:-e\ '\$a&':
++	done`
++	
++	sed -i.bak $del_args "$MODLIST_FILE"
++	for i in $*
++	do
++		echo "$i"
++	done >> "$MODLIST_FILE"
++}
++
++do_update() {
++	if [ ! -d `dirname ${ZAPTEL_BOOT}` ]
++	then
++		return
++	fi
++	sed -i.bak "s/^$1=.*\$/$1=\"$2\"/" ${ZAPTEL_BOOT}
++	if ! grep -q "^$1=" ${ZAPTEL_BOOT}; then
++		echo "$1=\"$2\"" >> ${ZAPTEL_BOOT}
++	fi
++}
++
++update_extensions_defs() {
++	if [ "$rapid_conf_mode" = 'yes' ]
++	then
++		say "DEBUG: Updating dialplan defs file $exten_defs_file"
++		if [ "`echo $tmp_dir/fxo_* | grep -v '*'`" != '' ]
++		then
++			trunk_nums=`cat $tmp_dir/fxo_* | sort -n | xargs`
++			say "Configuring TRUNK to be [first of] zaptel channels: $trunk_nums"
++			trunk_dev=`echo $trunk_nums| sed -e 's/ /\\\\\\&/g' -e 's/[0-9a-zA-Z]\+/Zap\\\\\\/&/g'`
++			echo >&2 sed -i "s/^TRUNK.*=>.*/TRUNK => $trunk_dev/" $exten_defs_file
++			sed -i "s/^TRUNK.*=>.*/TRUNK => $trunk_dev/" $exten_defs_file
++			if ! grep -q "^TRUNK =>" $exten_defs_file; then
++				trunk_dev=`echo $trunk_nums| sed -e 's/ /&/g' -e 's/[0-9a-zA-Z]*/Zap\\/&/g'`
++				echo "TRUNK => $trunk_dev" >> $exten_defs_file
++			fi
++		else
++			say "Warning: No FXO channel for trunk. Moving on."
++		fi
++		if [ "`echo $tmp_dir/fxs_* | grep -v '*'`" != '' ]
++		then
++			fxs_nums=`cat $tmp_dir/fxs_* | sort -n | xargs`
++			zap_nums=`grep '^[^;].*Zap/\${CHAN_ZAP_' $exten_base_dir/*.conf | \
++				sed -e 's/.*Zap\/\${CHAN_ZAP_\([0-9]*\)}.*/\1/' | sort -u | xargs`
++			say "Configuring channels: $fxs_nums as channel placeholders: $zap_nums"
++			j=1
++			for i in $zap_nums
++			do
++				chan=`echo $fxs_nums | awk "{print \\$$i}"`
++				if [ "$chan" = '' ]
++				then
++					# if the result is empty, we probably got past the last one.
++					# bail out.
++					say "Warning: No FXS channel for CHAN_ZAP_$i. Moving on"
++					break
++				fi
++				say "DEBUG: setting channel $chan to placeholder $i"
++				if grep -q "^CHAN_ZAP_$i " $exten_defs_file
++				then
++					sed -i -e "s/^CHAN_ZAP_$i .*/CHAN_ZAP_$i => Zap\/$chan/" $exten_defs_file
++				else
++					echo "CHAN_ZAP_$i => Zap/$chan" >> $exten_defs_file
++				fi
++			done
++		fi	
++		# cleaning up the temp dir
++	fi
++	if [ -d "$tmp_dir" ]; then rm -rf "$tmp_dir"; fi
++}
++
++check_for_astribank(){
++	if ! grep -q XPP_IN/ /proc/zaptel/* 2>/dev/null
++	then
++		# we only get here is if we find no Astribank input channels
++		# in /proc/zaptel . Hence we can safely disable their special settings:
++		set_immediate=no
++	fi
++}
++
++usage() {
++	program=`basename $0`
++
++	echo >&2 "$program: generate zaptel.conf and zapata.conf"
++	echo >&2 "(version $VERSION_FULL)"
++	echo >&2 "usage:"
++	echo >&2 " $program [-sdv] [-m k|l|g] [-c <country_code>] [-r |-e <base_exten>] "
++	echo >&2 " $program [-sdv] -l"
++	echo >&2 " $program -su"
++	echo >&2 " $program -h (this screen)"
++	echo >&2 ""
++	echo >&2 "Options:"
++	echo >&2 "  -c CODE: set the country code (default: $lc_country)"
++	echo >&2 "  -e NUM: set the base extension number (default: $base_exten)"
++	echo >&2 "  -F: Don't print FXSs in zapata.conf"
++	echo >&2 "  -l: output a list of detected channels instead of zaptel.conf"
++	echo >&2 "  -d: Perform hardware detection"
++	echo >&2 "  -u: Unload zaptel modules"
++	echo >&2 "  -v: verbose"
++	echo >&2 "  -s: Don't fail if asterisk is running. Stop it"
++	echo >&2 "  -r: rapid configuration mode: configure Zaptel FXS channels from "
++	echo >&2 "      existing Rapid extension files. FXOs will all be TRUNK "
++}
++
++# $1: channel number
++print_pattern() {
++	local astbank_type=''
++	OPTIND=1
++	while getopts 'a:' arg
++	do
++		case "$arg" in
++			a) case "$OPTARG" in input|output) astbank_type=$OPTARG;;esac ;;
++		esac
++	done
++	shift $(( $OPTIND-1 ))
++
++
++	local chan=$1
++	local sig=$2 #fxs/fxo
++	local mode=$3
++	local method='ks'
++	if [ "$lc_country" = il ] && [ "$sig" = 'fxs' ]
++	then method=ls
++	fi
++	case "$mode" in
++	zaptel)
++		# sadly, both input ports and output ports go into the same span as 
++		# the FXS ports. Thus we need to separate between them. See also 
++		# the zapata.conf section:
++		if [ "$astbank_type" != '' ]; then echo "# astbanktype: $astbank_type"; fi
++		echo "${sig}$method=$chan" 
++		;;
++	list) echo $chan $sig $astbanktype;;
++	zapata)
++		# zap2amp will rewrite those from zaptel.conf and hints there
++		if [ "$fxsdisable" = 'yes' ] && [ $sig = 'fxo' ]; then return; fi
++			
++		echo "signalling=${sig}_$method"
++		if [ "$sig" = 'fxo' ]
++		then
++			# to preconfigure channel 1's extension to 550, set
++			# chan_1_exten=550
++			# in, e.g, /etc/default/zaptel
++		  var_name=`echo chan_${chan}_exten`
++			cfg_exten=`echo ${!var_name} | tr -d -c 0-9`
++		  var_name=`echo chan_${chan}_vmbox`
++			cfg_vmbox=`echo ${!var_name} | tr -d -c 0-9`
++		  var_name=`echo chan_${chan}_cntxt`
++			cfg_cntxt=`echo ${!var_name} | tr -d -c 0-9`
++			
++			# if option -E was given, get configuration from current extension
++			if [ "$rapid_conf_mode" = 'yes' ]
++			then
++				rap_exten=`echo $rapid_extens |awk "{print \\$$rapid_cur_exten}"`
++				if [ "$rap_exten" != '' ]
++				then
++					rap_cfgfile="$exten_base_dir/$rap_exten.conf"
++					if [ -r "$rap_exten" ]
++					then
++						cfg_exten=$rap_exten
++						# the vmbox is the third parameter to stdexten
++						rap_vmbox=`grep '^[^;].*Macro(stdexten' $rap_exten | cut -d, -f3 \
++							| cut -d')' -f1 | tr -d -c '0-9 at a-zA-Z'`
++						if [ "$rap_vmbox" ]!= '' ; then cfg_vmbox=$rap_vmbox; fi
++					fi
++				fi
++				rapid_cur_exten=$(($rapid_cur_exten + 1))
++			fi
++			
++			if [ "$cfg_exten" = '' ]
++			then # No extension number set for this channel
++				exten=$(($chan+$base_exten))
++			else # use the pre-configured extension number
++				exten=$cfg_exten
++			fi
++			# is there any real need to set 'mailbox=' ?
++			if [ "x$cfg_vmbox" = x ]
++			then # No extension number set for this channel
++				vmbox=$exten
++			else # use the pre-configured extension number
++				vmbox=$cfg_vmbox
++			fi
++			echo "callerid=\"Channel $chan\" <$exten>"
++			echo "mailbox=$exten"
++			if [ "$group_manual" != "yes" ]
++			then 
++				echo "group=$group_phones"
++			fi
++			if [ "$context_manual" != "yes" ]
++			then
++				if [ "$astbank_type" != '' ];
++				then 
++					context_var_name=context_$astbank_type
++					echo context=${!context_var_name}
++				else
++					echo "context=$context_phones"
++				fi
++			fi
++		else # this is an FXO (trunk/phone: FXO signalling)
++		  # we have may have set it. So reset it:
++			echo "callerid=\"\" <0>"
++			echo "mailbox="
++			if [ "$group_manual" != "yes" ]
++			then 
++				echo "group=$group_lines"
++			fi
++			if [ "$context_manual" != "yes" ]
++			then 
++				echo "context=$context_lines"
++			fi
++			if [ "$lc_country" = 'uk' ]
++			then
++			  echo "cidsignalling=v23"
++			  case $line in 
++			  *WCFXO*) echo "cidstart=history";;
++			  *)       echo "cidstart=polarity";; #a TDM400
++			  esac
++			fi
++			echo ";;; line=\"$line\""
++			# if kewlstart is not used, busydetect has to be employed:
++			if [ "$method" = 'ls' ]
++			then echo 'busydetect=yes'
++			else echo 'busydetect=no'
++			fi
++		fi
++
++		if [ "$set_immediate" = 'yes' ]
++		then
++			if [ "$astbank_type" = 'input' ]
++			then echo 'immediate=yes'
++			else echo 'immediate=no'
++			fi
++		fi
++		echo "channel => $chan"
++		echo ""
++
++		# Keep a note of what channels we have identified
++		say "DEBUG: adding to channels list: channel: $chan, sig: $sig"
++		case "$sig" in
++		fxs)
++			echo $chan >$tmp_dir/fxo_$chan
++			say "DEBUG: FXO list now contains: `cat $tmp_dir/fxo_* |xargs`"
++			;;
++		fxo)
++			echo $chan >$tmp_dir/fxs_$chan
++			say "DEBUG: FXS list now contains: `cat $tmp_dir/fxs_* |xargs`"
++			;;
++		esac
++		;;
++	esac
++	
++}
++
++# the number of channels from /proc/zaptel
++# must always print a number as its output.
++count_proc_zap_lines() {
++	# if zaptel is not loaded there are 0 channels:
++	if [ ! -d /proc/zaptel ]; then echo '0'; return; fi
++	
++	(
++		for file in `echo /proc/zaptel/* |grep -v '\*'`
++		do sed -e 1,2d $file # remove the two header lines
++		done
++	) | wc -l # the total number of lines
++}
++
++load_modules() {
++	say "Test Loading modules:"
++	for i in $ALL_MODULES
++	do
++		lines_before=`count_proc_zap_lines`
++		args="${i}_args"
++		eval "args=\$$args"
++		# a module is worth listing if it:
++		# a. loaded successfully, and
++		# b. added channels lines under /proc/zaptel/*
++		if /sbin/modprobe $i $args 2> /dev/null 
++		then
++		  check=0
++		  case "$i" in
++			xpp_usb) check=`grep 'STATUS=connected' 2>/dev/null /proc/xpp/xbuses | wc -l` ;;
++			*) if [ $lines_before -lt `count_proc_zap_lines` ]; then check=1; fi ;;
++			esac
++			if [ "$check" != 0 ]
++			then
++			  probed_modules="$probed_modules $i"
++			  say "	ok	$i	$args"
++		  else
++			  say "	- 	$i	$args"
++				rmmod $i
++		  fi
++		else
++			say "	- 	$i	$args"
++		fi
++	done
++}
++
++# recursively unload a module and its dependencies, if possible.
++# where's modprobe -r when you need it?
++# inputs: module to unload.
++# returns: the result from 
++unload_module() {
++	module="$1"
++	line=`lsmod 2>/dev/null | grep "^$1 "`
++	if [ "$line" = '' ]; then return; fi # module was not loaded
++
++	set -- $line
++	# $1: the original module, $2: size, $3: refcount, $4: deps list
++	mods=`echo $4 | tr , ' '`
++	# old versions of xpd_fxs actually depend on xpp, but forget to tell it.
++	# bug has already been fixed but the code will remain here for a while
++	# just in case
++	case "$module" in xpd_*) mods="xpp_usb $mods";; esac
++	for mod in $mods; do
++		# run in a subshell, so it won't step over our vars:
++		(unload_module $mod) 
++		# TODO: the following is probably the error handling we want:
++		# if [ $? != 0 ]; then return 1; fi
++	done
++	rmmod $module
++}
++
++unload_modules() {
++	if
++		pids="$(pgrep asterisk)"
++		[ "$pids" != '' ]
++	then
++		die "Before unloading -- STOP asterisk (pids=$pids)."
++	fi
++	say "Unloading zaptel modules:"
++	unload_module zaptel
++	say ''
++}
++
++# sleep a while until the xpp modules fully register
++wait_for_xpp() {
++	if [ -d /proc/xpp ] && \
++	   [ "`cat /sys/module/xpp/parameters/zap_autoreg`" = 'Y' ]
++	then
++		# wait for the XPDs to register:
++		success=0
++		for i in `seq 30`; do
++			if ! grep -q 0 /proc/xpp/*/*/zt_registration 2>/dev/null
++			then
++				# There are either no XPDs or all of them are 
++				# registered. Nothing to do
++				success=1
++				break
++			fi
++			sleep 1
++		done
++		if [ "$success" = 0 ]; then
++			echo 1>&2 "$0: WARNING: some XPD's are not registered yet (timeout)"
++		fi
++	fi
++}
++
++detect() {
++	unload_modules
++	load_modules
++	modlist="$probed_modules"
++	#for i in $ALL_MODULES
++	#do
++	#	if lsmod | grep "^$i  *" > /dev/null; then
++	#		modlist="$modlist $i"
++	#	fi
++	#done
++	modlist="$(echo $modlist)"		# clean spaces
++	if [ "$do_module_list" = yes ]
++	then
++		say "Updating '${MODLIST_FILE}'"
++		update_module_list "$modlist"
++	fi
++	if echo $modlist | grep -q xpp_usb; then wait_for_xpp; fi
++}
++
++# TODO: kill this function. It is now unreferenced from anywhere.
++check_tdm_sigtype() {
++	chan_num=$1
++	sig_type=$2
++	mode=$3
++	
++	case "$sig_type" in
++	fxs)chan_sig_type=fxo;;
++	fxo)chan_sig_type=fxs;;
++	esac
++
++#	print_pattern $chan_num $chan_sig_type $mode
++	
++  # if you get syntax error from this line, make sure you use 'bash' 
++  # rather than 'sh'
++	$ztcfg_cmd -c <(print_pattern $chan_num $chan_sig_type zaptel) 2>/dev/null  \
++		|| return 1
++	if head -c1 /dev/zap/$chan_num >/dev/null 2>/dev/null
++	then 
++		print_pattern $chan_num $chan_sig_type $mode
++		return 0
++	else
++		return 1
++	fi	
++}
++
++# output a list of extensions that need a channel
++get_rapid_extens() {
++	if [ "$rapid_conf_mode" = 'yes' ]
++	then
++		rapid_extens=`grep -l '^[^;].*Zap/\${CHAN_ZAP_' $exten_base_dir/*.conf 2>/dev/null | \
++			rev | cut -d/ -f1 | cut -d. -f2- | rev | xargs`
++		say "Need to configure extensions: $rapid_extens"
++	fi
++}
++
++genconf() {
++	local mode=$1
++
++	# reset FXO list (global)
++	#say "DEBUG: resetting channels lists"
++	rm -f $tmp_dir/fx{s,o}_*
++
++	if [ "$mode" = 'zapata' ]
++	then
++		rem_char=';'
++	else
++		rem_char='#'
++	fi
++
++	spanlist=`echo /proc/zaptel/* |  grep -v '\*'`
++
++	#if [ "$spanlist" == "" ]; then
++	#	die "No zapata interfaces in /proc/zaptel"
++	#fi
++
++
++	case "$mode" in 
++		zaptel)
++			cat <<EOF
++# Autogenerated by $0 -- do not hand edit
++# Zaptel Configuration File
++#
++# This file is parsed by the Zaptel Configurator, ztcfg
++#
++
++# It must be in the module loading order
++
++EOF
++			;;
++		zapata)
++			cat <<EOF
++; Autogenerated by $0 -- do not hand edit
++; Zaptel Channels Configurations (zapata.conf)
++;
++; This is not intended to be a complete zapata.conf. Rather, it is intended 
++; to be #include-d by /etc/zapata.conf that will include the global settings
++;
++EOF
++		;;
++	esac
++
++	# For each line in the spanlist: see if it represents a channel.
++	# if it does, test that the channel is usable.
++	# we do that by configuring it (using ztcfg with a 1-line config file)
++	# and then trying to read 1 byte from the device file.
++	#
++	# The '<(command)' syntax creates a temporary file whose content is is the
++	# output of 'command'.
++	#
++	# This approach failed with the T1 card we have: the read operation simply 
++	# hung. 
++	#
++	# Another problem with such an approach is how to include an existing 
++	# configuration file. For instance: how to include some default settings.
++	#
++	# Maybe an 'include' directive should be added to zaptel.conf ?
++	#cat $spanlist | 
++	for procfile in $spanlist
++	do
++		span_num=`basename $procfile`
++		# the first line is the title line. It states the model name
++		# the second line is empty
++		title=`head -n 1 $procfile`
++		echo ""
++		# stuff that needs to be remembered accross lines (for PRI support)
++		echo "$rem_char $title"
++		echo '-1'  >$tmp_dir/span_begin
++		echo '-1'  >$tmp_dir/span_end
++		echo '1'   >$tmp_dir/span_timing
++		echo '1'   >$tmp_dir/span_lbo
++		echo ''    >$tmp_dir/span_framing
++		echo 'ami' >$tmp_dir/span_coding
++		echo ''    >$tmp_dir/span_switchtype
++		echo ''    >$tmp_dir/span_signalling
++
++		if echo $title | egrep -q '((quad|octo)BRI PCI ISDN Card.* \[NT\]\ |octoBRI \[NT\] |HFC-S PCI A ISDN.* \[NT\] )'
++		then
++			echo 'nt' >$tmp_dir/span_termtype
++		else 
++			if echo $title | egrep -q '((quad|octo)BRI PCI ISDN Card.* \[TE\]\ |octoBRI \[TE\] |HFC-S PCI A ISDN.* \[TE\] )'
++			then
++				echo 'te' >$tmp_dir/span_termtype
++			fi
++		fi
++		# The rest of the lines are per-channel lines
++		sed -e 1,2d $procfile | \
++		while read line
++		do 
++			# in case this is a real channel. 
++			chan_num=`echo $line |awk '{print $1}'`
++			case "$line" in
++			*WCTDM/*|*/WRTDM/*|*OPVXA1200/*) 
++				# this can be either FXS or FXO
++				maybe_fxs=0
++				maybe_fxo=0
++				$ztcfg_cmd -c <(print_pattern $chan_num fxo zaptel) &>/dev/null && maybe_fxs=1
++				$ztcfg_cmd -c <(print_pattern $chan_num fxs zaptel) &>/dev/null && maybe_fxo=1
++				if [ $maybe_fxs = 1 ] && [ $maybe_fxo = 1 ]
++				then 
++				  # An installed module won't accept both FXS and FXO signalling types:
++					# this is an empty slot.
++					# TODO: I believe that the Sangoma A20x will reject both and thus 
++					# print nothing here. 
++				  echo "$rem_char channel $chan_num, WCTDM, no module."
++					continue
++				fi
++				
++				if [ $maybe_fxs = 1 ]; then print_pattern $chan_num fxo $mode; fi
++				if [ $maybe_fxo = 1 ]; then print_pattern $chan_num fxs $mode; fi
++				;;
++			*WCFXO/*) 
++				print_pattern $chan_num fxs $mode || \
++				echo "$rem_char channel $chan_num, WCFXO, inactive."
++				;;
++			*WCUSB/*)
++				print_pattern $chan_num fxo $mode
++				;;
++			*XPP_FXO/*)
++				print_pattern $chan_num fxs $mode
++				;;
++			*XPP_FXS/*)
++				print_pattern $chan_num fxo $mode
++				;;
++			*XPP_OUT/*)
++				print_pattern -a output $chan_num fxo $mode
++				;;
++			*XPP_IN/*)
++				print_pattern -a input $chan_num fxo $mode
++				;;
++			*ZTHFC*/*|*ztqoz*/*|*ztgsm/*|*TE4/*|*TE2/*|*WCT1/*|*Tor2/*|*TorISA/*) # should also be used for other PRI channels
++				if [ "`cat $tmp_dir/span_begin`" = "-1" ]
++				then
++					echo $chan_num      >$tmp_dir/span_begin
++					echo $span_num      >$tmp_dir/span_num
++					case "$line" in
++					*ZTHFC*/*|*ztqoz*/*)
++						echo 'ccs'          >$tmp_dir/span_framing
++						echo 'euroisdn'     >$tmp_dir/span_switchtype
++						if [ "`cat $tmp_dir/span_termtype`" = 'nt' 2>/dev/null ]
++						then
++							echo 'bri_net' >$tmp_dir/span_signalling
++						else
++							echo 'bri_cpe' >$tmp_dir/span_signalling
++						fi
++						;;
++					*ztgsm*/*)
++						echo 'ccs'          >$tmp_dir/span_framing
++            # what switch type? Any meaning to it?
++						echo 'gsm'          >$tmp_dir/span_signalling
++						;;
++					*TE4/*|*TE2/*|*WCT1/*|*Tor2/*|*TorISA/*)
++						echo 'esf'       >$tmp_dir/span_framing
++						echo 'b8zs'      >$tmp_dir/span_coding
++						echo 'national'  >$tmp_dir/span_switchtype
++						echo 'pri_cpe'   >$tmp_dir/span_signalling
++						# an example of country-specific setup. This is probably not accurate
++						# Contributions are welcome
++						case "$lc_country" in 
++						nl)
++							# (Just an example for per-country info)
++							echo 'ami'       >$tmp_dir/span_framing
++							echo 'ccs'      >$tmp_dir/span_coding
++							#echo 'crc4'      >$tmp_dir/span_yellow
++							#echo 'euroisdn'  >$tmp_dir/span_switchtype
++							#echo 'pri_cpe'   >$tmp_dir/span_signalling
++							;;
++						il)
++							echo 'hdb3'      >$tmp_dir/span_framing
++							echo 'ccs'       >$tmp_dir/span_coding
++							echo 'crc4'      >$tmp_dir/span_yellow
++							echo 'euroisdn'  >$tmp_dir/span_switchtype
++						esac
++					;;
++					esac
++				fi
++				# span_lastd is always the one before last 
++				# channel. span_bchan is the last:
++				echo $chan_num      >$tmp_dir/span_end
++				;;
++			'') ;;		# Empty line (after span header)
++			*) echo "$rem_char ??: $line";;
++			esac
++		done
++		if [ "`cat $tmp_dir/span_begin`" != -1 ]
++		then # write PRI span ocnfig:
++			# read files to variables:
++			for suffix in num begin end timing lbo framing \
++				coding switchtype signalling yellow termtype
++			do
++				eval span_$suffix=`cat $tmp_dir/span_$suffix 2>/dev/null`
++			done
++			if [ "$span_yellow" != '' ]; then span_yellow=",$span_yellow"; fi
++			# exactly the same logic is used in asterisk's chan_zap.c.
++			# also not that $(( )) is bash-specific
++			case "$((1+ $span_end - $span_begin))" in
++			2|3|24) #ztgsm, BRI or T1
++			  dchan=$span_end
++				bchans="$span_begin-$(($span_end-1))"
++				;;
++			31) #E1
++			  dchan="$(($span_begin+15))"
++				bchans="$span_begin-$(($span_begin+14)),$(($span_begin+16))-$span_end"
++				;;
++			esac
++			case "$mode" in
++			zaptel)
++				echo span=$span_num,$span_timing,$span_lbo,$span_framing,$span_coding$span_yellow
++				if [ "$span_termtype" != '' ]
++				then echo "# termtype: $span_termtype"
++				fi
++				echo bchan=$bchans
++				echo dchan=$dchan
++				;;
++			zapata)
++				if [ "$span_termtype" != '' ]
++				then
++					# an ISDN card's span that we know if it is in NT mode or TE mode.
++					# NT is the same as FXS for us and TE is the same as FXO
++					if [ "$span_termtype" = 'nt' ]
++					then
++						#echo "callerid=\"Channel $chan\" <$exten>"
++						#echo "mailbox=$exten"
++						if [ "$group_manual" != "yes" ]
++						then 
++							echo "group=$group_phones"
++						fi
++						if [ "$context_manual" != "yes" ]
++						then 
++							echo "context=$context_phones"
++						fi
++					else # we have may have set it. So reset it:
++						#echo "callerid=\"\" <0>"
++						#echo "mailbox="
++						if [ "$group_manual" != "yes" ]
++						then 
++							echo "group=$group_lines"
++						fi
++						if [ "$context_manual" != "yes" ]
++						then 
++							echo "context=$context_lines"
++						fi
++					fi
++				fi
++				echo "switchtype = $span_switchtype"
++				echo "signalling = $span_signalling"
++				echo "channel => $bchans"
++				;;
++			list)
++				echo BRI/PRI: chans: $bchans, control: $dchan
++				;;
++			esac
++		fi
++	done
++
++	if [ "$mode" = 'zaptel' ]
++	then
++		cat <<EOF
++
++# Global data
++
++EOF
++		echo "loadzone	= $loadzone"
++		echo "defaultzone	= $defaultzone"
++	fi
++	
++	if [ "$mode" = 'zapata' ] || [ "$mode" = 'list' ]
++	then 
++		update_extensions_defs 
++	fi
++}
++
++while getopts 'c:de:Fhlm:Mrsuv' arg
++do
++	case "$arg" in
++		e) # guarantee that it is a number:
++			new_base_exten=`echo $OPTARG | tr -d -c 0-9`
++			if [ "x$new_base_exten" != x ]; then base_exten=$new_base_exten; fi
++			;;
++		c) lc_country=`echo $OPTARG | tr -d -c a-z` ;;
++		d) do_detect=yes ;;
++		F) fxsdisable=yes;;
++		u) do_unload=yes ;;
++		v) verbose=yes ;;
++		l) mode='list' ;;
++		M) do_module_list=yes; do_detect=yes ;;
++		s) force_stop_ast=yes ;;
++		r) 
++			rapid_conf_mode=yes 
++			;;
++		h) usage; exit 0;;
++		*) echo >&2 "unknown parameter -$arg, Aborting"; usage; exit 1;;
++	esac
++done
++shift $(( $OPTIND-1 ))
++if [ $# != 0 ]; then
++	echo >&2 "$0: too many parameters"
++	usage
++	exit 1
++fi
++
++tmp_dir=`mktemp -d -t` || \
++	die "$0: failed to create temporary directory. Aborting"
++
++
++case "$lc_country" in
++	# the list was generated from the source of zaptel:
++	#grep '{.*[0-9]\+,.*"[a-z][a-z]"' zonedata.c | cut -d'"' -f 2 | xargs |tr ' ' '|'
++	us|au|fr|nl|uk|fi|es|jp|no|at|nz|it|gr|tw|cl|se|be|sg|il|br|hu|lt|pl|za|pt|ee|mx|in|de|ch|dk|cz|cn):;;
++	*) 
++		lc_country=us
++		echo >&2 "unknown country-code $lc_country, defaulting to \"us\""
++		;;
++esac
++# any reason for loadzone and defaultzone to be different? If so, this is
++# the place to make that difference
++loadzone=$lc_country
++defaultzone=$loadzone
++
++# make sure asterisk is not in our way
++if [ "$force_stop_ast" = 'yes' ]
++then
++  /etc/init.d/asterisk stop 1>&2
++else
++  # if asterisk is running and we wanted to detect modules
++	# or simply to unload modules, asterisk needs to go away.
++	if ( [ "$do_unload" = yes ] || [ "$do_detect" = yes ] ) && \
++	  pidof asterisk >/dev/null 
++	then
++	  echo >&2 "Asterisk is already running. Configuration left untouched"
++		echo >&2 "You can use the option -s to shut down Asterisk for the"
++		echo >&2 "duration of the detection."
++		exit 1
++	fi
++fi
++
++if [ "$do_unload" = yes ]
++then
++	unload_modules
++	exit
++fi
++
++if [ "$do_detect" = yes ]
++then
++	detect
++fi
++
++if [ "$mode" = list ]; then
++	genconf list
++else
++	check_for_astribank
++	get_rapid_extens
++	say "Generating '${ZAPCONF_FILE}'"
++	mv "${ZAPCONF_FILE}" "${ZAPCONF_FILE}.bak"
++	genconf zaptel > "${ZAPCONF_FILE}"
++	say "Generating '${ZAPATA_FILE}'"
++	mv "${ZAPATA_FILE}" "${ZAPATA_FILE}.bak"
++	genconf zapata > "${ZAPATA_FILE}"
++	if [ "$set_immediate" = 'yes' ] && [ -x /etc/init.d/zaptel ]
++	then /etc/init.d/zaptel start
++	else run_ztcfg
++	fi
++fi
++
++if [ "$tmp_dir" != '' ]
++then
++  rm -rf "$tmp_dir"
++fi
++
++if [ "$force_stop_ast" = 'yes' ]
++then
++  if [ -x /etc/init.d/asterisk ]
++  then
++    /etc/init.d/asterisk start 1>&2
++  fi
++fi
++
++# if in verbose mode: verify that asterisk is running
++if [ "$verbose" != 'no' ] && [ "$force_stop_ast" = 'yes' ]
++	then
++	say "Checking channels configured in Asterisk:"
++	sleep 1 # give it some time. This is enough on our simple test server
++	if [ -x ast-cmd ]
++	then
++		ast-cmd cmd "zap show channels"
++	else
++		asterisk -rx "zap show channels"
++	fi
++fi
++
++# vim:ts=2:
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/genzaptelconf.8 zaptel-xpp-XJ8JZx_dist/xpp/utils/genzaptelconf.8
+--- zaptel-1.2.6/xpp/utils/genzaptelconf.8	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/utils/genzaptelconf.8	2006-06-17 09:56:07.169240000 +0300
+@@ -0,0 +1,258 @@
++.TH GENZAPTELCONF 8 "July 18th, 2005" "Xorcom Rapid Asterisk" "Linux Programmer's Manual"
++.SH "NAME" 
++.B genzaptelconf 
++-- generates zaptel configuration (TDM adaptors)
++.SH SYNOPSIS
++.PP 
++.B genzaptelconf 
++[-sdv] [-c <country_code>] [-r |-e <base_exten>] [ -F ]
++
++.B genzaptelconf 
++[-sdv] -l -- only list to standard output
++   
++.B genzaptelconf 
++-su -- only unload zaptel modules
++
++.B genzaptelconf 
++-h -- Help screen
++    
++.SH DESCRIPTION
++.B genzaptelconf 
++is a script to detect zaptel devices (currently mostly TDM cards are 
++supported). It generates both 
++.I /etc/zaptel.conf
++and 
++.I /etc/asterisk/zapata-channels.conf
++
++.I PRI
++and 
++.I BRI
++(HFC, with ZapBRI) cards are basically identified as well. However the span
++configiration is a default that I only hope is sane. Looking for feedback
++
++.SH OPTIONS
++.B -c
++.I country_code
++.RS
++A two-letter country code. Sets the country-code for the zonezone 
++entries in 
++.I zaptel.conf
++, The default is the value of
++.I lc_country
++from 
++.I /etc/default/zaptel
++and failing that, "us".
++.RE
++
++.B -d
++.RS
++Also try to detect modules. Unloads all zaptel modules and loads them
++one by one. Considers a module useful if it loaded successfully and if 
++loading it has generated at least one zapata channel.
++
++The list of detected modules is written as the value of 
++.I ZAPTEL_MODS
++in 
++.I /etc/default/zaptel
++.RE
++
++.B -e
++.I base_exten_num
++.RS
++Configure channel 
++.I i 
++as extension 
++.I exten_num
+++
++.I i 
++. This is mostly for the caller-id values. Crude, but may be good enough. 
++See also
++.I -r
++.RE
++
++.B -F
++.RS
++Disable writing FXS extensions in zapata.conf
++.RE
++
++.B -l
++.RS
++Only list deceted channels and their signalling. Don't write 
++configuration files. Note, however that 
++.I -ld
++will still rewrite the modules line in 
++.I /etc/default/zaptel
++(see 
++.I -d
++above).
++.RE
++
++.B -M
++.RS
++Update
++.I /etc/modules
++with a list of our modules, thereby
++triggers their loading via modprobe on the next boot.
++
++This triggers the
++.I -d
++option as well.
++.RE
++
++.B -r
++.RS
++Try to guess a useful
++.I zapata-channels
++configuration for Xorcom Rapid .
++.RE
++
++.B -s
++.RS
++Stop asterisk for the duration of the test. The detection will only
++work if nobody uses the zaptel channels: 
++
++* To allow unloading of modules
++
++* to allow reading configuration files.
++
++By default the script will check if asterisk is running and alert if so.
++This option tells the script to stop asterisk (if it was running) and to 
++try to start it after the end of the test.
++.RE
++
++.B -v
++.RS
++Be verbose. lists the detected modules if 
++.I -d
++is used. Lists detected channls. In the end tries to connect to asterisk
++to get a list of configured zaptel channels.
++.RE
++.SH FILES
++.I /etc/zaptel.conf
++.RS
++The configuration file used by 
++.I ztcfg
++to configure zaptel devices. re-written by 
++.I genzaptelconf
++.RE
++
++.I /etc/zaptel.conf.bak
++.RS
++When 
++.I zaptel.conf
++The original zaptel.conf
++.RE
++
++.I /etc/asterisk/zapata.conf
++.RS
++The configuration file of Asterisk's 
++.I chan_zap.
++Not modified directly by 
++.I genzaptelconf.
++If you want genzaptelconf's setting to take effect, add the following 
++line at the end of 
++.I zapata.conf:
++.RS
++#include "zapata-channels.conf"
++.RE
++.RE
++
++.I /etc/asterisk/zapata-channels.conf
++.RS
++This is the snippet of 
++.I chan_zap
++configuration file that 
++.I genzaptelconf generates.
++.RE
++
++.I /etc/asterisk/zapata-channels.conf.bak
++.RS
++The backup copy of 
++.I zapata-channels.conf
++.RE
++
++.I /etc/default/zaptel
++.RS
++This file holds configuration for both 
++.I genzaptelconf
++and
++.I /etc/init.d/zaptel .
++It is sourced by both scripts and can thus be used to override settings 
++of variables from those scripts.
++Some of the variables that can be set in /etc/default/zaptel and affect 
++genzaptelconf:
++
++.I lc_country
++.RS
++The default country. Can be also overriden by the option -c
++.RE
++
++.I base_exten
++.RS
++The base number used for automatic numbering
++.RE
++
++.I context_manual
++.RS
++If set to 'yes', no context changes are made in zapata-channels.conf
++.RE
++
++.I context_lines
++.RS
++The context into which calls will go from zaptel trunks.
++.RE
++
++.I context_phones
++.RS
++The context into which calls will go from zaptel phones.
++.RE
++
++.I context_manual
++.RS
++If set to 'yes', no group settings are made in zapata-channels.conf
++.RE
++
++.I group_lines
++.RS
++The group number for zaptel trunks.
++.RE
++
++.I group_phones
++.RS
++The group number for zaptel phones.
++.RE
++
++.I ALL_MODULES
++.RS
++modules list. Used for unloading and modules detection. The order of modules
++is the same for both.
++.RE
++.RE
++
++.I /etc/modules
++.RS
++A debian-specific list of kernel modules to be loaded by modprobe at 
++boot time. When the option 
++.I -d
++(detect) is used, genzaptelconf will write in this file zaptel modules
++to be loaded. If you want to use a different file, set 
++.I MOD_FILELIST
++.RE
++
++.I /etc/modules.bak
++.RS
++The backup copy of 
++.I /etc/modules
++.RE
++
++.SH "SEE ALSO" 
++ztcfg(8) asterisk(8). 
++
++.SH "AUTHOR" 
++This manual page was written by Tzafrir Cohen <tzafrir.cohen at xorcom.com> 
++Permission is granted to copy, distribute and/or modify this document under 
++the terms of the GNU General Public License, Version 2 any  
++later version published by the Free Software Foundation. 
++
++On Debian systems, the complete text of the GNU General Public 
++License can be found in /usr/share/common-licenses/GPL. 
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/hexfile.c zaptel-xpp-XJ8JZx_dist/xpp/utils/hexfile.c
+--- zaptel-1.2.6/xpp/utils/hexfile.c	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/utils/hexfile.c	2006-06-18 17:05:04.917972000 +0300
+@@ -0,0 +1,360 @@
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2006, Xorcom
++ *
++ * 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 <stdio.h>
++#include <assert.h>
++#include <string.h>
++#include <stdlib.h>
++#include <errno.h>
++#include "hexfile.h"
++
++static const char rcsid[] = "$Id:$";
++
++static parse_hexfile_report_func_t	report_func = NULL;
++
++parse_hexfile_report_func_t parse_hexfile_set_reporting(parse_hexfile_report_func_t rf)
++{
++	parse_hexfile_report_func_t	old_rf = report_func;
++	report_func = rf;
++	return old_rf;
++}
++
++static void chomp(char buf[])
++{
++	size_t	last = strlen(buf) - 1;
++	while(last >= 0 && (buf[last] == '\n' || buf[last] == '\r'))
++		buf[last--] = '\0';
++}
++
++int checksum(struct hexline *hexline)
++{
++	unsigned int	i;
++	unsigned int	chksm = 0;
++	int		ll = hexline->d.content.header.ll;
++
++	for(i = 0; i <= sizeof(hexline->d.content.header) + ll; i++) {
++		chksm += hexline->d.raw[i];
++	}
++	return chksm & 0xFF;
++}
++
++static int update_hexline(struct hexdata *hexdata, char *buf)
++{
++	int		ret;
++	unsigned int	ll, offset, tt;
++	char		*p;
++	struct hexline	*hexline;
++	unsigned int	i;
++	int		allocsize;
++	unsigned int	last_line = hexdata->last_line;
++
++	if(hexdata->got_eof) {
++		if(report_func)
++			report_func(LOG_ERR, "Extranous data after EOF record\n");
++		return -EINVAL;
++	}
++	if(last_line >= hexdata->maxlines) {
++		if(report_func)
++			report_func(LOG_ERR, "Hexfile too large (maxline %d)\n", hexdata->maxlines);
++		return -ENOMEM;
++	}
++	ret = sscanf(buf, "%02X%04X%02X", &ll, &offset, &tt);
++	if(ret != 3) {
++		if(report_func)
++			report_func(LOG_ERR, "Bad line header (only %d items out of 3 parsed)\n", ret);
++		return -EINVAL;
++	}
++	switch(tt) {
++		case TT_DATA:
++			break;
++		case TT_EOF:
++			if(ll != 0) {
++				if(report_func)
++					report_func(LOG_ERR, "Bad EOF record len = %d\n", ll);
++				return -EINVAL;
++			}
++			if(offset != 0) {
++				if(report_func)
++					report_func(LOG_ERR, "Bad EOF record offset = %d\n", offset);
++				return -EINVAL;
++			}
++			hexdata->got_eof = 1;
++			break;
++		case TT_EXT_SEG:	/* Unimplemented */
++		case TT_START_SEG:	/* Unimplemented */
++		case TT_EXT_LIN:	/* Unimplemented */
++		case TT_START_LIN:	/* Unimplemented */
++			return 1;
++		default:
++			if(report_func)
++				report_func(LOG_ERR, "Unknown record type %d\n", tt);
++			return -EINVAL;
++	}
++	buf += 8;	/* Skip header */
++	ll++;		/* include the checksum for now */
++	allocsize = sizeof(struct hexline) + ll;
++	if((hexline = (struct hexline *)malloc(allocsize)) == NULL) {
++		if(report_func)
++			report_func(LOG_ERR, "No more memory for hexfile lines\n");
++		return -EINVAL;
++	}
++	memset(hexline, 0, allocsize);
++	hexline->d.content.header.ll = ll;
++	hexline->d.content.header.offset = offset;
++	hexline->d.content.header.tt = tt;
++	hexdata->lines[last_line++] = hexline;
++	p = buf;
++	for(i = 0; i < ll; i++) {
++		unsigned int	val;
++
++		if((*p == '\0') || (*(p+1) == '\0')) {
++			if(report_func)
++				report_func(LOG_ERR, "Short data string '%s'\n", buf);
++			return -EINVAL;
++		}
++		ret = sscanf(p, "%02X", &val);
++		if(ret != 1) {
++			if(report_func)
++				report_func(LOG_ERR, "Bad data byte #%d\n", i);
++			return -EINVAL;
++		}
++		hexline->d.content.tt_data.data[i] = val;
++		p += 2;
++	}
++	hexline->d.content.header.ll--;	/* Fix the checksum */
++	if(checksum(hexline) != 0) {
++		if(report_func)
++			report_func(LOG_ERR, "Bad checksum\n");
++		return -EINVAL;
++	}
++	if(hexdata->got_eof)
++		return 0;
++	hexdata->last_line++;
++	return 1;
++}
++
++void free_hexdata(struct hexdata *hexdata)
++{
++	if(hexdata) {
++		unsigned int	i;
++
++		for(i = 0; i < hexdata->maxlines; i++)
++			if(hexdata->lines[i] != NULL)
++				free(hexdata->lines[i]);
++		free(hexdata);
++	}
++}
++
++int dump_hexfile(struct hexdata *hexdata, FILE *outfile)
++{
++	uint8_t		ll;
++	uint16_t	offset;
++	uint8_t		tt;
++	uint8_t		old_chksum;
++	uint8_t		new_chksum;
++	uint8_t		*data;
++	unsigned int	i;
++	unsigned int	j;
++
++	for(i = 0; i <= hexdata->last_line; i++) {
++		struct hexline	*line = hexdata->lines[i];
++		if(!line) {
++			if(report_func)
++				report_func(LOG_ERR, "Missing line at #%d\n", i);
++			return -EINVAL;
++		}
++		ll = line->d.content.header.ll;
++		offset = line->d.content.header.offset;
++		tt = line->d.content.header.tt;
++		fprintf(outfile, ":%02X%04X%02X", ll, offset, tt);
++		data = line->d.content.tt_data.data;
++		for(j = 0; j < ll; j++) {
++			fprintf(outfile, "%02X", data[j]);
++		}
++		old_chksum = data[ll];
++		data[ll] = 0;
++		new_chksum = 0xFF - checksum(line) + 1;
++		data[ll] = old_chksum;
++		assert(new_chksum == old_chksum);
++		fprintf(outfile, "%02X\n", new_chksum);
++	}
++	return 0;
++}
++
++int dump_hexfile2(struct hexdata *hexdata, FILE *outfile, uint8_t maxwidth)
++{
++	uint8_t		ll;
++	uint8_t		tt;
++	uint8_t		new_chksum;
++	uint8_t		*data;
++	unsigned int	i;
++	unsigned int	j;
++
++	if (maxwidth <= sizeof(hexdata->lines[0]->d.content.header) ){
++		if(report_func) 
++			report_func(LOG_ERR, "Line width too small %d\n", maxwidth);
++		return -EINVAL;
++	}
++	
++	for(i = 0; i <= hexdata->last_line; i++) {
++		struct hexline	*line = hexdata->lines[i];
++		struct hexline *extraline;
++		int allocsize;
++		int bytesleft = 0;
++		int extra_offset = 0;
++		unsigned int this_line = 0;
++		
++		if(!line) {
++			if(report_func)
++				report_func(LOG_ERR, "Missing line at #%d\n", i);
++			return -EINVAL;
++		}
++		ll = line->d.content.header.ll;
++		bytesleft = ll;
++		/* split the line into several lines */
++		tt = line->d.content.header.tt;
++		while (bytesleft > 0) {
++			this_line = (bytesleft >= maxwidth) ? maxwidth : bytesleft;
++			allocsize = sizeof(struct hexline) + this_line + 1;
++			/* generate the new line */
++			if((extraline = (struct hexline *)malloc(allocsize)) == NULL) {
++				if(report_func)
++					report_func(LOG_ERR, "No more memory for hexfile lines\n");
++				return -EINVAL;
++			}
++			memset( extraline, 0, allocsize );
++			extraline->d.content.header.ll		= this_line;
++			extraline->d.content.header.offset	= line->d.content.header.offset + extra_offset;
++			extraline->d.content.header.tt		= tt;
++			memcpy( extraline->d.content.tt_data.data, line->d.content.tt_data.data+extra_offset, this_line);
++			new_chksum = 0xFF - checksum(extraline) + 1;
++			/* print it */
++			data = extraline->d.content.tt_data.data;
++			fprintf(outfile, ":%02X%04X%02X", extraline->d.content.header.ll, extraline->d.content.header.offset, tt);
++			for(j = 0; j < this_line; j++) {
++				fprintf(outfile, "%02X", data[j]);
++			}
++			fprintf(outfile, "%02X\n", new_chksum);
++			/* cleanups */
++			free( extraline);
++			extra_offset += this_line;
++			bytesleft -= this_line;
++		}
++	}
++	return 0;
++}
++
++struct hexdata *parse_hexfile(const char *fname, unsigned int maxlines)
++{
++	FILE			*fp;
++	struct hexdata		*hexdata = NULL;
++	int			datasize;
++	char			buf[BUFSIZ];
++	int			line;
++	int			ret;
++
++	assert(fname != NULL);
++	if(report_func)
++		report_func(LOG_INFO, "Parsing %s\n", fname);
++	datasize = sizeof(struct hexdata) + maxlines * sizeof(char *);
++	hexdata = (struct hexdata *)malloc(datasize);
++	if(!hexdata) {
++		if(report_func)
++			report_func(LOG_ERR, "Failed to allocate %d bytes for hexfile contents\n", datasize);
++		goto err;
++	}
++	memset(hexdata, 0, datasize);
++	hexdata->maxlines = maxlines;
++	if((fp = fopen(fname, "r")) == NULL) {
++		if(report_func)
++			report_func(LOG_ERR, "Failed to open hexfile '%s'\n", fname);
++		goto err;
++	}
++	line = 0;
++	while(fgets(buf, BUFSIZ, fp)) {
++		line++;
++		if(buf[0] == '\0') {
++				if(report_func)
++					report_func(LOG_ERR, "Short line at %s:%d\n", fname, line);
++				goto err;
++		}
++		chomp(buf);
++		if(buf[0] == '#') {
++			if(report_func)
++				report_func(LOG_INFO, "Comment '%s'\n", buf + 1);
++			continue;
++		}
++		if(buf[0] != ':') {
++			if(report_func)
++				report_func(LOG_ERR, "Line begins with 0x%X at %s:%d\n", buf[0], fname, line);
++			goto err;
++		}
++		if((ret = update_hexline(hexdata, buf + 1)) < 0) {
++			if(report_func)
++				report_func(LOG_ERR, "Failed parsing %s at line: %d\n", fname, line);
++			goto err;
++		}
++	}
++	fclose(fp);
++	if(report_func)
++		report_func(LOG_INFO, "%s parsed OK\n", fname);
++	return hexdata;
++err:
++	free_hexdata(hexdata);
++	return NULL;
++}
++
++void dump_binary(struct hexdata *hexdata, const char *outfile)
++{
++	FILE		*fp;
++	unsigned int	i;
++	size_t		len;
++
++	if(report_func)
++		report_func(LOG_INFO, "Dumping binary data into '%s'\n", outfile);
++	if((fp = fopen(outfile, "w")) == NULL) {
++		perror(outfile);
++		exit(1);
++	}
++	for(i = 0; i < hexdata->maxlines; i++) {
++		struct hexline	*hexline = hexdata->lines[i];
++
++		if(!hexline)
++			break;
++		if(hexline->d.content.header.tt == TT_EOF) {
++			if(report_func)
++				report_func(LOG_INFO, "\ndump: good EOF record");
++			continue;
++		}
++		if(report_func)
++			report_func(LOG_INFO, "dump: %6d\r", i);
++		len = hexline->d.content.header.ll;
++		if(fwrite(hexline->d.content.tt_data.data, 1, len, fp) != len) {
++			perror("write");
++			exit(1);
++		}
++	}
++	if(report_func)
++		report_func(LOG_INFO, "\nDump finished\n");
++	fclose(fp);
++}
++
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/hexfile.h zaptel-xpp-XJ8JZx_dist/xpp/utils/hexfile.h
+--- zaptel-1.2.6/xpp/utils/hexfile.h	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/utils/hexfile.h	2006-05-15 17:27:26.264723000 +0300
+@@ -0,0 +1,120 @@
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2006, Xorcom
++ *
++ * 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.
++ *
++ */
++
++#ifndef	PARSE_HEXFILE_H
++#define	PARSE_HEXFILE_H
++
++#include <stdarg.h>
++
++/*
++ * Some portability workarounds
++ */
++#ifdef	_WINDOWS
++
++#include <windows.h>	/* for UCHAR USHORT */
++typedef UCHAR	uint8_t;
++typedef USHORT	uint16_t;
++#define	PACKED
++#define	sscanf	sscanf_s
++#define	ZERO_SIZE	1
++
++/* From /usr/include/syslog.h */
++#define	LOG_EMERG	0	/* system is unusable */
++#define	LOG_ALERT	1	/* action must be taken immediately */
++#define	LOG_CRIT	2	/* critical conditions */
++#define	LOG_ERR		3	/* error conditions */
++#define	LOG_WARNING	4	/* warning conditions */
++#define	LOG_NOTICE	5	/* normal but significant condition */
++#define	LOG_INFO	6	/* informational */
++#define	LOG_DEBUG	7	/* debug-level messages */
++
++#ifdef  __cplusplus
++# define __BEGIN_DECLS  extern "C" {
++# define __END_DECLS    }
++#else
++# define __BEGIN_DECLS
++# define __END_DECLS
++#endif
++
++#elif	__GNUC__
++
++#include <stdint.h>
++#include <syslog.h>
++#define	PACKED	__attribute__((packed))
++#define	ZERO_SIZE	1
++
++#else
++
++#error "Cannot compile on this platform"
++
++#endif
++
++/* Record types in hexfile */
++enum {
++	TT_DATA		= 0,
++	TT_EOF		= 1,
++	TT_EXT_SEG	= 2,
++	TT_START_SEG	= 3,
++	TT_EXT_LIN	= 4,
++	TT_START_LIN	= 5,
++	TT_NO_SUCH_TT
++};
++
++#pragma pack(1)
++struct hexline {
++	union {
++		uint8_t		raw[ZERO_SIZE];
++		struct content {
++			struct header {
++				uint8_t		ll;	/* len */
++				uint16_t	offset;	/* offset */
++				uint8_t		tt;	/* type */
++			} PACKED header;
++			struct tt_data {
++				uint8_t		data[ZERO_SIZE];
++			} tt_data;
++		} PACKED content;
++	} d;
++} PACKED;
++#pragma pack()
++
++struct hexdata {
++	unsigned int		maxlines;
++	unsigned int		last_line;
++	int			got_eof;
++	struct hexline		*lines[ZERO_SIZE];
++};
++
++
++__BEGIN_DECLS
++
++typedef void (*parse_hexfile_report_func_t)(int level, const char *msg, ...);
++
++parse_hexfile_report_func_t parse_hexfile_set_reporting(parse_hexfile_report_func_t rf);
++void free_hexdata(struct hexdata *hexdata);
++struct hexdata *parse_hexfile(const char *fname, unsigned int maxlines);
++int dump_hexfile(struct hexdata *hexdata, FILE *outfile);
++int dump_hexfile2(struct hexdata *hexdata, FILE *outfile, uint8_t maxwidth);
++void dump_binary(struct hexdata *hexdata, const char *outfile);
++__END_DECLS
++
++#endif
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/Makefile zaptel-xpp-XJ8JZx_dist/xpp/utils/Makefile
+--- zaptel-1.2.6/xpp/utils/Makefile	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/utils/Makefile	2006-06-27 02:07:26.558841000 +0300
+@@ -0,0 +1,50 @@
++PEDANTIC	= -ansi -pedantic -std=c99
++
++CC		= gcc
++RANLIB		= ranlib
++INSTALL		= install
++
++BINDIR    = /usr/sbin
++DATADIR   = /usr/share/zaptel
++MANDIR    = /usr/share/man/man8
++HOTPLUG_USB_DIR = /etc/hotplug/usb
++
++DATA_FILES = $(wildcard ../init_data_*.cmd *.hex) 
++
++CFLAGS		= -g -Wall $(EXTRA_CFLAGS)
++
++TARGETS	= libhexfile.a fpga_load test_parse
++
++all: $(TARGETS)
++
++# install: sets the executable bit
++install: all
++	$(INSTALL) -d $(DESTDIR)$(BINDIR)
++	$(INSTALL) genzaptelconf fpga_load $(DESTDIR)$(BINDIR)/
++	$(INSTALL) -d $(DESTDIR)$(DATADIR)
++	cp -a $(DATA_FILES) $(DESTDIR)$(DATADIR)/
++	$(INSTALL) ../initialize_registers  $(DESTDIR)$(DATADIR)/
++	$(INSTALL) -d $(DESTDIR)$(MANDIR)
++	cp -a fpga_load.8 genzaptelconf.8 $(DESTDIR)$(MANDIR)/
++	$(INSTALL) -d $(DESTDIR)$(HOTPLUG_USB_DIR)
++	cp -a xpp_fxloader.usermap $(DESTDIR)$(HOTPLUG_USB_DIR)/
++	$(INSTALL) xpp_fxloader $(DESTDIR)$(HOTPLUG_USB_DIR)/
++
++libhexfile.a: hexfile.o
++	$(AR) cru $@ $^
++	$(RANLIB) $@
++
++fpga_load: fpga_load.o libhexfile.a
++	$(CC) -L. -o $@ $@.o $(EXTRA_LIBS) -lhexfile -lusb
++
++hexfile.o: hexfile.c hexfile.h
++	$(CC) $(CFLAGS) $(PEDANTIC) -c $*.c
++
++test_parse.o: test_parse.c hexfile.h
++	$(CC) $(CFLAGS) $(PEDANTIC) -c $*.c
++
++test_parse: test_parse.o libhexfile.a
++	$(CC) -L. -o $@ $@.o $(EXTRA_LIBS) -lhexfile -lusb
++
++clean:
++	$(RM) *.o $(TARGETS)
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/test_parse.c zaptel-xpp-XJ8JZx_dist/xpp/utils/test_parse.c
+--- zaptel-1.2.6/xpp/utils/test_parse.c	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/utils/test_parse.c	2006-05-15 17:27:26.264723000 +0300
+@@ -0,0 +1,34 @@
++#include <stdio.h>
++#include <stdarg.h>
++#include "hexfile.h"
++
++static void default_report_func(int level, const char *msg, ...)
++{
++	va_list ap;
++
++	va_start(ap, msg);
++	vfprintf(stderr, msg, ap);
++	va_end(ap);
++}
++
++int main(int argc, char *argv[])
++{
++	struct hexdata	*hd;
++	int		i;
++
++	if(argc < 2) {
++		fprintf(stderr, "Usage: program hexfile...\n");
++		return 1;
++	}
++	parse_hexfile_set_reporting(default_report_func);
++	for(i = 1; i < argc; i++) {
++		hd = parse_hexfile(argv[i], 2000);
++		if(!hd) {
++			fprintf(stderr, "Parsing failed\n");
++			return 1;
++		}
++		dump_hexfile2(hd, stdout, 60 );
++		free_hexdata(hd);
++	}
++	return 0;
++}
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/USB_1130.hex zaptel-xpp-XJ8JZx_dist/xpp/utils/USB_1130.hex
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/USB_8613.hex zaptel-xpp-XJ8JZx_dist/xpp/utils/USB_8613.hex
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/xpp_fxloader zaptel-xpp-XJ8JZx_dist/xpp/utils/xpp_fxloader
+--- zaptel-1.2.6/xpp/utils/xpp_fxloader	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/utils/xpp_fxloader	2006-06-27 02:07:26.558841000 +0300
+@@ -0,0 +1,158 @@
++#!/bin/sh
++
++# xpp_fxload: load XPP firmware
++#
++# This script can be run manually or from hotplug. 
++#
++# Firmware files should be located in $FIRMWARE_DIR which defaults:
++# 	1. /usr/share/zaptel
++#	2. Can be overidden by setting $FIRMWARE_DIR in the environment
++#	3. Can be overidden by setting $FIRMWARE_DIR in /etc/default/zaptel
++#
++# Manual Run
++# ##########
++#
++#   path/to/xpp_fxloader xppdetect
++#
++# Make sure the firmware files are in $FIRMWARE_DIR
++#
++#
++# Hotplg Run
++# ##########
++#
++# 1. Copy this file and the file xpp_fxloader.usermap to /etc/hotplug/usb/
++# 2. tail -f  /var/log/messages...
++#
++#
++# Written by Tzafrir Cohen <tzafrir.cohen at xorcom.com>
++# Copyright (C) 2006, Xorcom
++#
++# 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.
++
++set -e
++
++me=`basename $0`
++DEFAULTS="/etc/default/zaptel"
++
++if [ -t 2 ]; then
++	LOGGER="logger -i -t '$me' -s"
++else
++	LOGGER="logger -i -t '$me'"
++fi
++
++if [ -r "$DEFAULTS" ]; then
++	. "$DEFAULTS"
++fi
++
++FIRMWARE_DIR="${FIRMWARE_DIR:-/usr/share/zaptel}"
++
++FIRM_FXS=$FIRMWARE_DIR/FPGA_FXS.hex
++REENUM_SLEEP_TIME=3 # only used on manual runs
++
++FPGA_LOAD="/usr/sbin/fpga_load"
++
++find_dev() {
++  v_id=$1
++  p_id=$2
++  
++  lsusb | tr -d : | awk "/ ID $v_id$p_id/{printf \"/proc/bus/usb/%s/%s \",\$2,\$4}"
++}
++
++do_fxload() {
++  ( fxload -t fx2 $* 2>&1 1>/dev/null || exit 1 ) | $LOGGER
++}
++
++load_fw() {
++  v_id=$1
++  p_id=$2
++  fw=$3
++  
++  devices=`find_dev $v_id $p_id`
++  for dev in $devices
++  do
++    do_fxload -D $dev -I $FIRMWARE_DIR/$fw || exit 1
++  done
++
++  # Allow time for reenumeration: This only matters in the manual case.
++  if [ "$devices" != '' ]; then sleep $REENUM_SLEEP_TIME; fi
++}
++
++hexfile_version() {
++	hexfile=$1
++
++	grep '$Id:' "$hexfile" | sed -e 's/^.*$Id: *[^ ]\+ *//' -e 's/ .*$//'
++}
++
++load_fpga() {
++	dev=$1
++
++	card_ver=`$FPGA_LOAD -d -D $dev`
++	firm_ver=`hexfile_version $FIRM_FXS`
++
++	$LOGGER "considering fpga firmware version '$firm_ver' for $dev with version $card_ver."
++	case "$card_ver" in 
++	1.001)
++		$LOGGER "Loading firmware '$FIRM_FXS' (version $firm_ver) into '$dev'"
++		$FPGA_LOAD -D "$dev" -I "$FIRM_FXS" 2>&1 >/dev/null | $LOGGER
++		status=$PIPESTATUS
++		if [ $status != 0 ]; then
++			echo "fpga_load failed with status $status" | $LOGGER
++			exit 77
++		fi
++		;;
++	esac
++}
++
++#########################
++##
++## Manual run
++##
++
++# to run manually, pass the parameter 'xppdetect'
++if [ "$1" = 'xppdetect' ]; then
++	load_fw 04b4 8613 USB_8613.hex 
++	load_fw e4e4 1130 USB_1130.hex
++	for dev in `find_dev e4e4 1131`; do
++		load_fpga $dev
++	done
++	exit $?
++fi
++
++#########################
++##
++## Hotplug run
++##
++
++if [ "$ACTION" = "add" ] && [ -f "$DEVICE" ]
++then
++	$LOGGER "Trying to find what to do for product $PRODUCT, device $DEVICE"
++	case "$PRODUCT" in
++	4b4/8613/*)
++		FIRM_USB=$FIRMWARE_DIR/USB_8613.hex
++		$LOGGER "Loading firmware '$FIRM_USB' into '$DEVICE'"
++		do_fxload -D "$DEVICE" -I "$FIRM_USB"
++		;;
++	e4e4/1130/*)
++		FIRM_USB=$FIRMWARE_DIR/USB_1130.hex
++		$LOGGER "Loading firmware '$FIRM_USB' into '$DEVICE'"
++		do_fxload -D "$DEVICE" -I "$FIRM_USB"
++		;;
++	e4e4/1131/*)
++		load_fpga $DEVICE
++		;;
++	esac	
++fi
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/xpp_fxloader.usermap zaptel-xpp-XJ8JZx_dist/xpp/utils/xpp_fxloader.usermap
+--- zaptel-1.2.6/xpp/utils/xpp_fxloader.usermap	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/utils/xpp_fxloader.usermap	2006-05-01 18:45:04.050100000 +0300
+@@ -0,0 +1,4 @@
++# module	match_flags	idVendor	idProduct	bcdDevice_lo	bcdDevice_hi	bDeviceClass	bDeviceSubClass	bDeviceProtocol	bInterfaceClass	bInterfaceSubClass	bInterfaceProtocol	driver_info
++xpp_fxloader	0x0003		0x04b4		0x8613		0x0000		0x0000		0x00		0x00		0x00		0x00		0x00			0x00			0x0
++xpp_fxloader	0x0003		0xe4e4		0x1130		0x0000		0x0000		0x00		0x00		0x00		0x00		0x00			0x00			0x0
++xpp_fxloader	0x0003		0xe4e4		0x1131		0x0000		0x0000		0x00		0x00		0x00		0x00		0x00			0x00			0x0
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/utils/xpp_modprobe zaptel-xpp-XJ8JZx_dist/xpp/utils/xpp_modprobe
+--- zaptel-1.2.6/xpp/utils/xpp_modprobe	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/utils/xpp_modprobe	2006-05-01 17:08:34.781705000 +0300
+@@ -0,0 +1,10 @@
++# Some debugging options for the brave of heart:
++#options zaptel debug=1
++#options wcfxo debug=1
++#options xpp print_dbg=1 
++#options xpp_usb print_dbg=1 
++#options xpd_fxs print_dbg=1 
++#options xpd_fxo print_dbg=1 
++
++# For pre-loading of card modules (e.g: xpp_fxs)
++#install xpp_usb /sbin/modprobe xpd_fxs && /sbin/modprobe --ignore-install xpp_usb
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xbus-core.c zaptel-xpp-XJ8JZx_dist/xpp/xbus-core.c
+--- zaptel-1.2.6/xpp/xbus-core.c	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/xbus-core.c	2006-04-26 15:44:38.391672000 +0300
+@@ -0,0 +1,677 @@
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2006, Xorcom
++ *
++ * 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/version.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
++#  warning "This module is tested only with 2.6 kernels"
++#endif
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/proc_fs.h>
++#include <linux/device.h>
++#include "xpd.h"
++#include "xpp_zap.h"
++#include "xbus-core.h"
++#include "zap_debug.h"
++
++static const char rcsid[] = "$Id: xbus-core.c 894 2006-04-26 12:44:38Z oron $";
++
++/* Defines */
++#define	PROC_XBUSES		"xbuses"
++#define	PROC_XBUS_SUMMARY	"summary"
++
++/* Command line parameters */
++extern int print_dbg;
++extern int max_queue_len;
++extern int ignore_xpds;
++
++/* Forward declarations */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
++#define	DEVICE_ATTR_FUNC(name,dev,buf)	\
++		ssize_t name(struct device *dev, struct device_attribute *attr, char *buf)
++#else
++#define	DEVICE_ATTR_FUNC(name,dev,buf)	\
++		ssize_t name(struct device *dev, char *buf)
++#endif
++
++static DEVICE_ATTR_FUNC(connector_show, dev, buf);
++static DEVICE_ATTR_FUNC(status_show, dev, buf);
++
++static void xbus_release(struct device *dev);
++static int xbus_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data);
++
++/* Data structures */
++static spinlock_t		xbuses_lock = SPIN_LOCK_UNLOCKED;
++static xbus_t			*xbuses_array[MAX_BUSES] = {};
++static int			bus_count = 0;
++static struct proc_dir_entry	*proc_xbuses = NULL;
++
++static	DEVICE_ATTR(connector, S_IRUGO, connector_show, NULL);
++static	DEVICE_ATTR(status, S_IRUGO, status_show, NULL);
++
++/*------------------------- Packet Handling ------------------------*/
++static kmem_cache_t	*packet_cache = NULL;
++static atomic_t	xpacket_count = ATOMIC_INIT(0);
++
++/**
++ * Allocates a new XPP packet.
++ * @xbus The XPP bus in which the packet will flow (for counters 
++ *       maintenance)
++ * @flags Flags for kernel memory allocation.
++ * @returns A pointer to the new packet, or NULL in case of failure.
++ * 
++ * 
++ * Packet allocation/deallocation:
++ * 	Sent packets:
++ * 	  - Allocated by protocol commands
++ * 	  - Deallocated by xmus_xmitter
++ * 	Receive packets:
++ * 	  - Allocated/deallocated by xbus_xmiter
++ */
++xpacket_t	*xbus_packet_new(xbus_t *xbus, int flags)
++{
++	xpacket_t	*pack;
++
++	/* To avoid races we increament counter in advance and decrement it later 
++	 * in case of failure */
++	atomic_inc(&xbus->packet_counter); 
++	//DBG("Incremented packet_counter of bus %s (new packet) to %d\n", 
++	//		xbus->busname, atomic_read(&xbus->packet_counter));
++	pack = kmem_cache_alloc(packet_cache, flags);
++	if (pack) {
++		memset(pack, 0, sizeof(xpacket_t));
++		atomic_inc(&xpacket_count);
++	} else {
++		atomic_dec(&xbus->packet_counter);
++		//DBG("Decremented packet_counter of bus %s (failed new packet) to %d\n", 
++		//		xbus->busname, atomic_read(&xbus->packet_counter));
++	}
++	return pack;
++}
++
++void xbus_packet_free(xbus_t *xbus, xpacket_t *p)
++{
++	kmem_cache_free(packet_cache, p);
++	atomic_dec(&xpacket_count);
++	atomic_dec(&xbus->packet_counter);
++	//DBG("Decremented packet_counter of bus %s (freed packet) to %d\n", 
++	//		xbus->busname, atomic_read(&xbus->packet_counter));
++}
++
++/*------------------------- Packet Queues --------------------------*/
++void init_xbus_packet_queue(packet_queue_t *q, const char name[])
++{
++	INIT_LIST_HEAD(&q->head);
++	spin_lock_init(&q->lock);
++	q->count = 0;
++	q->worst_count = 0;
++	q->overflows = 0;
++	snprintf(q->qname, XPD_NAMELEN, "%s", name);
++}
++
++#if 0
++/*
++ * Assume the queue is locked
++ */
++void __dump_packet_queue(const char *msg, packet_queue_t *q)
++{
++	xpacket_t *tmp;
++
++	list_for_each_entry(tmp, &q->head, list) {
++		dump_packet(msg, tmp);
++	}
++}
++#endif
++
++void drain_xbus_packet_queue(xbus_t *xbus, packet_queue_t *q)
++{
++	unsigned long	flags;
++	xpacket_t	*pack;
++	xpacket_t	*next;
++
++	spin_lock_irqsave(&q->lock, flags);
++	DBG("queue=%s count=%d\n", q->qname, q->count);
++	DBG("     total packets count=%d\n", atomic_read(&xpacket_count));
++	list_for_each_entry_safe(pack, next, &q->head, list) {
++		list_del(&pack->list);
++		q->count--;
++		xbus->ops->packet_free(xbus, pack);
++	}
++	if(q->count != 0)
++		ERR("drain_xbus_packet_queue: queue %s still has %d packets\n",
++				q->qname, q->count);
++	spin_unlock_irqrestore(&q->lock, flags);
++}
++
++void xbus_enqueue_packet(xbus_t *xbus, packet_queue_t *q, xpacket_t *pack)
++{
++	unsigned long	flags;
++
++	spin_lock_irqsave(&q->lock, flags);
++
++	if(q->count >= max_queue_len) {
++		static unsigned long	last_notice = 0;	// rate limit
++
++		if((jiffies - last_notice) < HZ) {
++			NOTICE("xbus_enqueue_packet: dropping packet (queue len = %d, max=%d)\n",
++				q->count, max_queue_len);
++			last_notice = jiffies;
++		}
++		q->overflows++;
++		xbus->ops->packet_free(xbus, pack);
++		goto out;
++	}
++	list_add_tail(&pack->list, &q->head);
++	q->count++;
++
++	if(q->count > q->worst_count)
++		q->worst_count = q->count;
++
++	if(q->count < max_queue_len/100 && q->worst_count > q->count)	// Decay worst_count
++		q->worst_count--;
++
++	// dump_packet("ENQUEUED", pack, print_dbg);
++out:
++	spin_unlock_irqrestore(&q->lock, flags);
++}
++
++xpacket_t *xbus_dequeue_packet(packet_queue_t *q)
++{
++	unsigned long	flags;
++	struct list_head	*p;
++	xpacket_t	*pack = NULL;
++
++	spin_lock_irqsave(&q->lock, flags);
++
++	if(list_empty(&q->head)) {
++		// DBG("LIST EMPTY (count=%d)\n", q->count);
++		goto out;
++	}
++	p = q->head.next;
++	list_del(p);
++	q->count--;
++	pack = list_entry(p, xpacket_t, list);
++	// dump_packet("DEQUEUED", pack, print_dbg);
++out:
++	spin_unlock_irqrestore(&q->lock, flags);
++	return pack;
++}
++
++
++/*------------------------- Bus Management -------------------------*/
++xbus_t *xbus_of(int xbus_num)
++{
++	if(xbus_num < 0 || xbus_num >= MAX_BUSES)
++		return NULL;
++	return xbuses_array[xbus_num];
++}
++
++xpd_t	*xpd_of(xbus_t *xbus, int xpd_num)
++{
++	if(!VALID_XPD_NUM(xpd_num))
++		return NULL;
++	return xbus->xpds[xpd_num];
++}
++
++static void xbus_poll(xbus_t *xbus)
++{
++	int id;
++	int ret;
++	xpd_t **xpds;
++	xpd_t *xpd;
++
++	DBG("%s\n", xbus->busname);
++	xpds = xbus->xpds;
++	for(id = 0; id < MAX_XPDS; id++) {
++		if(!xbus->hardware_exists)
++			break;
++		xpd = xpd_of(xbus, id);
++		if(IS_SET(ignore_xpds, id)) {		/* skip xpds */
++			DBG("  Ignoring XPD #%d\n", id);
++			continue;
++		}
++		DBG("  Polling slot %d %s\n", id, xbus->busname);
++		ret = CALL_PROTO(GLOBAL, DESC_REQ, xbus, NULL, id);
++		if(ret < 0) {
++			NOTICE("xpp: %s: Failed sending DESC_REQ to XPD #%d\n", __FUNCTION__, id);
++		}
++	}
++}
++
++
++void xbus_activate(xbus_t *xbus)
++{
++	xbus_ops_t *ops;
++
++	BUG_ON(!xbus);
++	ops = xbus->ops;
++	BUG_ON(!ops);
++	BUG_ON(!xbus->priv);
++	/* Sanity checks */
++	BUG_ON(!ops->packet_send);
++	BUG_ON(!ops->packet_new || !ops->packet_free);
++	xbus->hardware_exists = 1;
++	DBG("Activating: %s\n", xbus->busname);
++	/* Poll it */
++	xbus_poll(xbus);
++}
++
++void xbus_disconnect(xbus_t *xbus)
++{
++	int	i;
++
++	BUG_ON(!xbus);
++	DBG("%s\n", xbus->busname);
++	xbus->hardware_exists = 0;
++	for(i = 0; i < MAX_XPDS; i++) {
++		xpd_t *xpd = xpd_of(xbus, i);
++		if(!xpd)
++			continue;
++		if(xpd->id != i) {
++			ERR("%s: BUG: xpd->id=%d != i=%d\n", __FUNCTION__, xpd->id, i);
++			continue;
++		}
++		xpd_disconnect(xpd);
++	}
++	DBG("%s (deactivated)\n", xbus->busname);
++	if(xbus->open_counter == 0) {
++		xbus_remove(xbus);
++	}
++}
++
++static xbus_t *xbus_alloc(void)
++{
++	unsigned long	flags;
++	xbus_t	*xbus;
++	int	i;
++
++	xbus = kmalloc(sizeof(xbus_t), GFP_KERNEL);
++	if(!xbus) {
++		ERR("%s: out of memory\n", __FUNCTION__);
++		return NULL;
++	}
++	memset(xbus, 0, sizeof(xbus_t));
++	spin_lock_irqsave(&xbuses_lock, flags);
++	for(i = 0; i < MAX_BUSES; i++)
++		if(xbuses_array[i] == NULL)
++			break;
++	if(i >= MAX_BUSES) {
++		ERR("%s: No free slot for new bus. i=%d\n", __FUNCTION__, i);
++		kfree(xbus);
++		return NULL;
++	}
++	/* Found empty slot */
++	xbuses_array[i] = xbus;
++	xbus->num = i;
++	bus_count++;
++	spin_unlock_irqrestore(&xbuses_lock, flags);
++	return xbus;
++}
++
++
++static void xbus_free(xbus_t *xbus)
++{
++	unsigned long	flags;
++
++	if(!xbus)
++		return;
++	spin_lock_irqsave(&xbuses_lock, flags);
++	BUG_ON(xbus != xbus_of(xbus->num));
++	xbuses_array[xbus->num] = NULL;
++	bus_count--;
++	spin_unlock_irqrestore(&xbuses_lock, flags);
++#ifdef CONFIG_PROC_FS
++	if(xbus->proc_xbus_dir) {
++		if(xbus->proc_xbus_summary) {
++			DBG("Removing proc '%s' for %s\n", PROC_XBUS_SUMMARY, xbus->busname);
++			remove_proc_entry(PROC_XBUS_SUMMARY, xbus->proc_xbus_dir);
++			xbus->proc_xbus_summary = NULL;
++		}
++		DBG("Removing proc directory %s\n", xbus->busname);
++		remove_proc_entry(xbus->busname, xpp_proc_toplevel);
++		xbus->proc_xbus_dir = NULL;
++	}
++#endif
++	device_remove_file(&xbus->the_bus, &dev_attr_status);
++	device_remove_file(&xbus->the_bus, &dev_attr_connector);
++	device_unregister(&xbus->the_bus);
++	kfree(xbus);
++}
++
++static void xbus_release(struct device *dev)
++{
++	xbus_t	*xbus;
++
++	BUG_ON(!dev);
++	xbus = dev->driver_data;
++	DBG("%s\n", xbus->busname);
++}
++
++
++xbus_t *xbus_new(xbus_ops_t *ops)
++{
++	int		err;
++	xbus_t		*xbus = NULL;
++
++	BUG_ON(!ops);
++	xbus = xbus_alloc();
++	if(!xbus)
++		return NULL;
++
++	/* Init data structures */
++	spin_lock_init(&xbus->lock);
++	snprintf(xbus->busname, XBUS_NAMELEN, "XBUS-%d", xbus->num);
++	INFO("New xbus: %s\n", xbus->busname);
++	init_waitqueue_head(&xbus->packet_cache_empty);
++	atomic_set(&xbus->packet_counter, 0);
++	init_rwsem(&xbus->in_use);
++	xbus->num_xpds = 0;
++	xbus_reset_counters(xbus);
++
++	/* Device-Model */
++	snprintf(xbus->the_bus.bus_id, BUS_ID_SIZE, "xbus-%d", xbus->num);
++	xbus->the_bus.driver_data = xbus;
++	xbus->the_bus.release = xbus_release;
++
++	err = device_register(&xbus->the_bus);
++	if(err) {
++		ERR("%s: device_register failed: %d\n", __FUNCTION__, err);
++		goto nobus;
++	}
++	err = device_create_file(&xbus->the_bus, &dev_attr_connector);
++	if(err) {
++		ERR("%s: device_create_file failed: %d\n", __FUNCTION__, err);
++		goto nobus;
++	}
++	err = device_create_file(&xbus->the_bus, &dev_attr_status);
++	if(err) {
++		ERR("%s: device_create_file failed: %d\n", __FUNCTION__, err);
++		goto nobus;
++	}
++
++#ifdef CONFIG_PROC_FS
++	DBG("Creating xbus proc directory %s.\n",xbus->busname);
++	xbus->proc_xbus_dir = proc_mkdir(xbus->busname, xpp_proc_toplevel);
++	if(!xbus->proc_xbus_dir) {
++		ERR("Failed to create proc directory for xbus %s\n", xbus->busname);
++		err = -EIO;
++		goto nobus;
++	}
++	xbus->proc_xbus_summary = create_proc_read_entry(PROC_XBUS_SUMMARY, 0444, xbus->proc_xbus_dir,
++			xbus_read_proc, xbus);
++	if (!xbus->proc_xbus_summary) {
++		ERR("Failed to create '%s' proc file for xbus %s\n", PROC_XBUS_SUMMARY, xbus->busname);
++		err = -EIO;
++		goto nobus;
++	}
++#endif
++	/* Sanity checks */
++	if(!ops->packet_send) {
++		ERR("%s: missing mandatory handler: packet_send\n", __FUNCTION__);
++		goto nobus;
++	}
++	if(!ops->packet_new || !ops->packet_free) {
++		NOTICE("%s: Using default packet allocators\n", __FUNCTION__);
++		ops->packet_new = xbus_packet_new;
++		ops->packet_free = xbus_packet_free;
++	}
++
++	xbus->ops = ops;
++	return xbus;
++nobus:
++	xbus_free(xbus);
++	return NULL;
++}
++
++void xbus_remove(xbus_t *xbus)
++{
++	int	i;
++	int	ret;
++
++	BUG_ON(!xbus);
++	DBG("%s\n", xbus->busname);
++
++	/* Block until no one use */
++	down_write(&xbus->in_use);
++
++	INFO("Removing xbus(%d) %s\n", xbus->num, xbus->busname);
++	for(i = 0; i < MAX_XPDS; i++) {
++		xpd_t *xpd = xpd_of(xbus, i);
++
++		if(xpd) {
++			if(xpd->id != i) {
++				ERR("%s: BUG: xpd->id=%d != i=%d\n", __FUNCTION__, xpd->id, i);
++				continue;
++			}
++			DBG("  Removing xpd id=%d\n", xpd->id);
++			xpd_remove(xpd);
++		}
++		xbus->xpds[i] = NULL;
++	}
++	ret = wait_event_interruptible(xbus->packet_cache_empty, 
++			atomic_read(&xbus->packet_counter) == 0);
++	if(ret) {
++		ERR("waiting for packet_cache_empty interrupted!!!\n");
++	}
++	xbus_free(xbus);
++}
++
++/*------------------------- Proc handling --------------------------*/
++
++void xbus_reset_counters(xbus_t *xbus)
++{
++	int	i;
++
++	DBG("Reseting counters of %s\n", xbus->busname);
++	for(i = 0; i < XBUS_COUNTER_MAX; i++) {
++		xbus->counters[i] = 0;
++	}
++}
++
++#if CONFIG_PROC_FS
++static int xbus_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++	int len = 0;
++	unsigned long flags;
++	xbus_t	*xbus = data;
++	int i;
++
++	if(!xbus)
++		goto out;
++	spin_lock_irqsave(&xbus->lock, flags);
++
++	len += sprintf(page + len, "%s: CONNECTOR=%s STATUS=%s bus_type=%d\n",
++			xbus->busname,
++			xbus->busdesc,
++			(xbus->hardware_exists) ? "connected" : "missing",
++			xbus->bus_type
++		      );
++	len += sprintf(page + len, "max_packet_size=%d open_counter=%d packet_count=%d\n",
++			xbus->max_packet_size,
++			xbus->open_counter,
++			atomic_read(&xbus->packet_counter)
++		      );
++	len += sprintf(page + len, "COUNTERS:\n");
++	for(i = 0; i < XBUS_COUNTER_MAX; i++) {
++		len += sprintf(page + len, "\t%-15s = %d\n",
++				xbus_counters[i].name, xbus->counters[i]);
++	}
++	len += sprintf(page + len, "<-- len=%d\n", len);
++	spin_unlock_irqrestore(&xbus->lock, flags);
++out:
++	if (len <= off+count)
++		*eof = 1;
++	*start = page + off;
++	len -= off;
++	if (len > count)
++		len = count;
++	if (len < 0)
++		len = 0;
++	return len;
++
++}
++
++static int read_proc_xbuses(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++	int len = 0;
++	unsigned long flags;
++	int i;
++
++	spin_lock_irqsave(&xbuses_lock, flags);
++	for(i = 0; i < MAX_BUSES; i++) {
++		xbus_t *xbus = xbus_of(i);
++
++		if(xbus) {
++			len += sprintf(page + len, "%s: CONNECTOR=%s STATUS=%s bus_type=%d\n",
++					xbus->busname,
++					xbus->busdesc,
++					(xbus->hardware_exists) ? "connected" : "missing",
++					xbus->bus_type
++				      );
++		}
++	}
++#if 0
++	len += sprintf(page + len, "<-- len=%d\n", len);
++#endif
++	spin_unlock_irqrestore(&xbuses_lock, flags);
++	if (len <= off+count)
++		*eof = 1;
++	*start = page + off;
++	len -= off;
++	if (len > count)
++		len = count;
++	if (len < 0)
++		len = 0;
++	return len;
++
++}
++#endif
++
++/*------------------------- Initialization -------------------------*/
++
++static DEVICE_ATTR_FUNC(connector_show, dev, buf)
++{
++	xbus_t	*xbus;
++	int	ret;
++
++	xbus = dev->driver_data;
++	ret = snprintf(buf, PAGE_SIZE, "%s\n", xbus->busdesc);
++	return ret;
++}
++
++static DEVICE_ATTR_FUNC(status_show, dev, buf)
++{
++	xbus_t	*xbus;
++	int	ret;
++
++	xbus = dev->driver_data;
++	ret = snprintf(buf, PAGE_SIZE, "%s\n", (xbus->hardware_exists)?"connected":"missing");
++	return ret;
++}
++
++static int xbus_match(struct device *dev, struct device_driver *driver)
++{
++	DBG("dev->bus_id = %s, driver->name = %s\n", dev->bus_id, driver->name);
++	return strncmp(dev->bus_id, driver->name, strlen(driver->name)) == 0;
++}
++
++#if 0
++/* Hotplug replaced with uevent in 2.6.16 */
++static int xbus_hotplug(struct device *device, char **envp, int envnum, char *buff, int bufsize)
++{
++	envp[0] = buff;
++	if(snprintf(buff, bufsize, "XBUS_VERSION=%s", revision) >= bufsize)
++		return -ENOMEM;
++	envp[1] = NULL;
++	return 0;
++}
++#endif
++
++struct bus_type	xbus_bus_type = {
++	.name = "xbus",
++	.match = xbus_match,
++#if 0
++/* Hotplug replaced with uevent in 2.6.16 */
++	.hotplug = xbus_hotplug,
++#endif
++};
++
++static void xbus_core_cleanup(void)
++{
++#ifdef CONFIG_PROC_FS
++	if(proc_xbuses)
++		remove_proc_entry(PROC_XBUSES, xpp_proc_toplevel);
++#endif
++	if(packet_cache)
++		kmem_cache_destroy(packet_cache);
++}
++
++int __init xbus_core_init(void)
++{
++	int	ret;
++
++	packet_cache = kmem_cache_create("xpp_packets",
++			sizeof(xpacket_t),
++			0, 0,
++			NULL, NULL);
++	if(!packet_cache) {
++		return -ENOMEM;
++	}
++#ifdef CONFIG_PROC_FS
++	proc_xbuses = create_proc_read_entry(PROC_XBUSES, 0444, xpp_proc_toplevel, read_proc_xbuses, 0);
++	if (!proc_xbuses) {
++		xbus_core_cleanup();
++		return -EFAULT;
++	}
++#endif
++	ret = bus_register(&xbus_bus_type);
++	if(ret) {
++		ERR("%s: bus_register failed. Error number %d", __FUNCTION__, ret);
++		xbus_core_cleanup();
++		return ret;
++	}
++	return 0;
++}
++
++
++void __exit xbus_core_shutdown(void)
++{
++	int		i;
++
++	for(i = 0; i < MAX_BUSES; i++) {
++		xbus_t	*xbus = xbus_of(i);
++		if(xbus)
++			xbus_remove(xbus);
++	}
++	BUG_ON(bus_count);
++	bus_unregister(&xbus_bus_type);
++	xbus_core_cleanup();
++}
++
++EXPORT_SYMBOL(xpd_of);
++EXPORT_SYMBOL(xbus_new);
++EXPORT_SYMBOL(xbus_remove);
++EXPORT_SYMBOL(xbus_activate);
++EXPORT_SYMBOL(xbus_disconnect);
++EXPORT_SYMBOL(xbus_reset_counters);
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xbus-core.h zaptel-xpp-XJ8JZx_dist/xpp/xbus-core.h
+--- zaptel-1.2.6/xpp/xbus-core.h	1970-01-01 02:00:00.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/xbus-core.h	2006-03-13 14:24:00.628259000 +0200
+@@ -0,0 +1,53 @@
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2006, Xorcom
++ *
++ * 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.
++ *
++ */
++#ifndef	XBUS_CORE_H
++#define	XBUS_CORE_H
++
++#include "xpd.h"
++
++#define	MAX_BUSES		16
++
++int xbus_core_init(void);		/* Initializer */
++void xbus_core_shutdown(void);		/* Terminator */
++
++/* Packet handling */
++xpacket_t *xbus_packet_new(xbus_t *xbus, int flags);
++void xbus_packet_free(xbus_t *xbus, xpacket_t *p);
++
++/* packet queues */
++void init_xbus_packet_queue(packet_queue_t *q, const char name[]);
++void drain_xbus_packet_queue(xbus_t *xbus, packet_queue_t *q);
++void xbus_enqueue_packet(xbus_t *xbus, packet_queue_t *q, xpacket_t *pack);
++xpacket_t *xbus_dequeue_packet(packet_queue_t *q);
++
++/* XBUS handling */
++xbus_t	*xbus_of(int xbus_num);
++xpd_t	*xpd_of(xbus_t *xbus, int xpd_num);
++xbus_t	*xbus_new(xbus_ops_t *ops);
++void xbus_remove(xbus_t *xbus);
++void xbus_activate(xbus_t *xbus);
++void xbus_disconnect(xbus_t *xbus);
++
++void xbus_reset_counters(xbus_t *xbus);
++
++#endif	/* XBUS_CORE_H */
++
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xdefs.h zaptel-xpp-XJ8JZx_dist/xpp/xdefs.h
+--- zaptel-1.2.6/xpp/xdefs.h	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/xdefs.h	2006-03-30 12:42:46.619481000 +0200
+@@ -2,7 +2,7 @@
+ #define	XDEFS_H
+ /*
+  * Written by Oron Peled <oron at actcom.co.il>
+- * Copyright (C) 2004-2005, Xorcom
++ * Copyright (C) 2004-2006, Xorcom
+  *
+  * All rights reserved.
+  *
+@@ -26,15 +26,10 @@
+ 
+ #include <linux/kernel.h>
+ 
+-#define	DBG(fmt, ...)	\
+-	((print_dbg) && printk(KERN_DEBUG "DBG-%s: %s: " fmt, \
+-		THIS_MODULE->name, __FUNCTION__, ## __VA_ARGS__))
+-#define	INFO(fmt, ...)	printk(KERN_INFO "INFO-%s: " fmt, THIS_MODULE->name, ## __VA_ARGS__)
+-#define	NOTICE(fmt, ...)	printk(KERN_NOTICE "NOTICE-%s: " fmt, THIS_MODULE->name, ## __VA_ARGS__)
+-#define	ERR(fmt, ...)	printk(KERN_ERR "ERR-%s: " fmt, THIS_MODULE->name, ## __VA_ARGS__)
+-
+ #else
+ 
++/* This is to enable user-space programs to include this. */
++
+ #include <stdint.h>
+ typedef uint32_t __u32;
+ 
+@@ -57,26 +52,17 @@
+ typedef	struct xpd		xpd_t;
+ typedef	struct xpacket_raw	xpacket_raw_t;
+ typedef	struct xpacket		xpacket_t;
+-typedef struct xops	xops_t;
+-typedef	__u32 xpp_line_t;	/* at most 31 lines for E1 */
++typedef struct xops		xops_t;
++typedef	__u32			xpp_line_t;	/* at most 31 lines for E1 */
++typedef	int			lineno_t;
+ 
++#define	ALL_LINES		((lineno_t)-1)
+ 
+-#define	BIT_SET(x,i)	((x) |= (1 << (i)))
+-#define	BIT_CLR(x,i)	((x) &= ~(1 << (i)))
+-#define	IS_SET(x,i)	(((x) & (1 << (i))) != 0)
+ #define	BIT(i)		(1 << (i))
++#define	BIT_SET(x,i)	((x) |= BIT(i))
++#define	BIT_CLR(x,i)	((x) &= ~BIT(i))
++#define	IS_SET(x,i)	(((x) & BIT(i)) != 0)
+ 
+-#undef	SUPPORT_USB1
+-
+-#ifdef	SUPPORT_USB1
+-/*
+- * packet size <= 64 bytes:
+- * 	ZT_CHUNKSIZE * 7 channels + header size <= 64
+- */
+-#define	CHANNELS_PERXPD	7	/* 7 * ZT_CHUNKSIZE + header <= 64 bytes */
+-#else
+ #define	CHANNELS_PERXPD	30	/* Depends on xpp_line_t and protocol fields */
+-#endif
+-
+ 
+ #endif	/* XDEFS_H */
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xpd.h zaptel-xpp-XJ8JZx_dist/xpp/xpd.h
+--- zaptel-1.2.6/xpp/xpd.h	2006-03-03 22:14:09.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/xpd.h	2006-06-22 11:31:28.427315000 +0300
+@@ -3,7 +3,7 @@
+ 
+ /*
+  * Written by Oron Peled <oron at actcom.co.il>
+- * Copyright (C) 2004-2005, Xorcom
++ * Copyright (C) 2004-2006, Xorcom
+  *
+  * All rights reserved.
+  *
+@@ -28,6 +28,7 @@
+ 
+ #ifdef	__KERNEL__
+ #include <linux/kernel.h>
++#include <linux/device.h>
+ #include <asm/atomic.h>
+ #include <asm/semaphore.h>
+ #include <linux/moduleparam.h>
+@@ -61,9 +62,9 @@
+ #endif	// __KERNEL__
+ 
+ 
+-#define	MAX_SPANNAME	20
+-#define	MAX_SPANDESC	40
+-#define	MAX_CHANNAME	20
++#define	MAX_SPANNAME	20	/* From zaptel.h */
++#define	MAX_SPANDESC	40	/* From zaptel.h */
++#define	MAX_CHANNAME	40	/* From zaptel.h */
+ 
+ #define	XPD_NAMELEN	10	/* must be <= from maximal workqueue name */
+ #define	XPD_DESCLEN	20
+@@ -100,6 +101,9 @@
+ 	void (*packet_free)(xbus_t *xbus, xpacket_t *p);
+ };
+ 
++/*
++ * XBUS statistics counters
++ */
+ enum {
+ 	XBUS_N_DESC_REQ,
+ 	XBUS_N_DEV_DESC,
+@@ -133,30 +137,22 @@
+ 
+ #define	XBUS_COUNTER_MAX	ARRAY_SIZE(xbus_counters)
+ 
+-struct xpd_sim {
+-	bool		simulated;
+-	bool		softloop_xpd;
+-	int		loopto;
+-	xpd_type_t	xpd_type;
+-	xpp_line_t	hookstate;
+-};
+-
+-
++/*
++ * An xbus is a transport layer for Xorcom Protocol commands
++ */
+ struct xbus {
+ 	char		busname[XBUS_NAMELEN];	/* only xbus_new set this */
+ 	char		busdesc[XBUS_DESCLEN];	/* lowlevel drivers set this */
+ 	int		num;
+ 	xbus_ops_t	*ops;
+ 	struct xpd	*xpds[MAX_XPDS];
++	int		max_packet_size;
++
++	/* Device-Model */
++	struct device	the_bus;
+ 
+ 	/* Simulator data */
+ 	xbus_type_t	bus_type;
+-#if SOFT_SIMULATOR
+-	struct xpd_sim	sim[MAX_XPDS];
+-	struct workqueue_struct *sim_workqueue;
+-	struct work_struct sim_work;		// workqueue job for running simulator
+-	packet_queue_t	sim_packet_queue;
+-#endif
+ 
+ 	spinlock_t	lock;
+ 
+@@ -170,10 +166,6 @@
+ 	int	num_xpds;
+ 	void	*priv;				/* Pointer to transport level data structures */
+ 
+-#ifdef	XPP_PACKET_LOG
+-	struct cyclic_buff *packet_log;
+-#endif
+-
+ #ifdef CONFIG_PROC_FS
+ 	struct proc_dir_entry	*proc_xbus_dir;
+ 	struct proc_dir_entry	*proc_xbus_summary;
+@@ -181,7 +173,6 @@
+ 
+ 	/* statistics */
+ 	int		counters[XBUS_COUNTER_MAX];
+-
+ };
+ #endif
+ 
+@@ -190,15 +181,11 @@
+ 	TO_PSTN = 1,
+ } xpd_direction_t;
+ 
+-#define	LINE_BITS	(sizeof(xpp_line_t)*8)
+-
+-
+ #ifdef	__KERNEL__
+-#define	BIT_SET(x,i)	((x) |= (1 << (i)))
+-#define	BIT_CLR(x,i)	((x) &= ~(1 << (i)))
+-#define	IS_SET(x,i)	(((x) & (1 << (i))) != 0)
+-#define	BIT(i)		(1 << (i))
+ 
++/*
++ * XPD statistics counters
++ */
+ enum {
+ 	XPD_N_PCM_READ,
+ 	XPD_N_PCM_WRITE,
+@@ -222,55 +209,69 @@
+ 
+ #define	XPD_COUNTER_MAX	(sizeof(xpd_counters)/sizeof(xpd_counters[0]))
+ 
+-enum leds {
+-	LED_GREEN,
+-	LED_RED,
+-	LED_BLUE,
+-};
++#define	LED_BLINK_PERIOD	(HZ/8)
+ 
+-#define	NUM_LEDS	3
++#define	LED_ON		1
++#define	LED_OFF		0
++#define	LED_BLINK	(-LED_BLINK_PERIOD)
++
++/* Values of SLIC register 0x40 */
++enum fxs_state {
++	FXS_LINE_DISABLED	= 0x00,
++	FXS_LINE_ENABLED	= 0x01,
++	FXS_LINE_CID		= 0x02,
++	FXS_LINE_TIPOPEN	= 0x03,	/* For GroundStart signalling */
++	FXS_LINE_RING		= 0x04,
++	FXS_LINE_REV_ACTIVE	= 0x05
++};
+ 
++/*
++ * An XPD is a single Xorcom Protocol Device
++ */
+ struct xpd {
+ 	char xpdname[XPD_NAMELEN];
+ 	struct zt_span	span;
+ 	struct zt_chan	*chans;
+ 	int channels;
+ 	xpd_type_t	type;
++	byte		revision;		/* Card revision */
+ 	xpd_direction_t	direction;		/* TO_PHONE, TO_PSTN */
+ 	xpp_line_t	enabled_chans;		/* hardware activation: 0 - off, 1 - on */
+-	xpp_line_t	hookstate;		/* 0 - ONHOOK, 1 - OFHOOK */
+-	xpp_line_t	ledstate[NUM_LEDS];	/* 0 - OFF, 1 - ON */
++	xpp_line_t	no_pcm;			/* Temporary: disable PCM (for USB-1) */
++	xpp_line_t	hookstate;		/* Actual chip state: 0 - ONHOOK, 1 - OFHOOK */
++	xpp_line_t	cid_on;
+ 	xpp_line_t	digital_outputs;	/* 0 - no, 1 - yes */
+ 	xpp_line_t	digital_inputs;		/* 0 - no, 1 - yes */
+ 
+-	int	ringing[CHANNELS_PERXPD];
+-	bool	ringer_on[CHANNELS_PERXPD];	/* For ring toggling */
+-	bool	led_on[CHANNELS_PERXPD];	/* For led toggling */
+-	int	lasttxhook[CHANNELS_PERXPD];
++	int		ringing[CHANNELS_PERXPD];
++	bool		ringer_on[CHANNELS_PERXPD];	/* For ring toggling */
++
++	wait_queue_head_t	txstateq[CHANNELS_PERXPD];	/* waiting on the tx state to change */
++	int		delay_until_dialtone[CHANNELS_PERXPD];
++
++	enum fxs_state	lasttxhook[CHANNELS_PERXPD];
++	int		idletxhookstate[CHANNELS_PERXPD];	/* IDLE changing hook state */
++	int		ohttimer[CHANNELS_PERXPD];
+ 
+-	struct work_struct xpd_post_init;
+-	xbus_t *xbus;
++	xbus_t *xbus;			/* The XBUS we are connected to */
+ 
+ 	spinlock_t	lock;
+-	atomic_t	open_counter;		/* Number of open channels */
++	atomic_t	open_counter;	/* Number of open channels */
+ 
+ 	int		flags;
+ 
+-	unsigned int	board_flags;
+-#define	XPD_BOARD_LOOPBACK	1
+-
+ #ifdef CONFIG_PROC_FS
+ 	struct proc_dir_entry	*proc_xpd_dir;
+ 	struct proc_dir_entry	*proc_xpd_summary;
+ 	struct proc_dir_entry	*proc_xpd_ztregister;
+ #endif
+-	// Bit numbers of board_flags
+ 
+ 	int		counters[XPD_COUNTER_MAX];
+ 
+-	const xops_t	*xops;		/* Card level operations */
+-	void		*priv;		/* Card level private data */
+-	atomic_t	card_present;
++	const xproto_table_t	*xproto;	/* Card level protocol table */
++	const xops_t	*xops;			/* Card level operations */
++	void		*priv;			/* Card level private data */
++	bool		card_present;
+ 
+ 	unsigned int	recv_errors;
+ 	unsigned int	seq_errors;
+@@ -285,6 +286,13 @@
+ 	u_char ec_chunk2[CHANNELS_PERXPD][ZT_CHUNKSIZE];
+ };
+ 
++#define	for_each_line(xpd,i)	\
++		for((i) = 0; (i) < (xpd)->channels; (i)++)
++
++#define	for_each_enabled_line(xpd,i)	\
++		for((i) = 0; (i) < (xpd)->channels; (i)++)	\
++			if(IS_SET((xpd)->enabled_chans,(i)))
++
+ #endif
+ 
+ #endif	/* XPD_H */
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xpp_fxloader zaptel-xpp-XJ8JZx_dist/xpp/xpp_fxloader
+--- zaptel-1.2.6/xpp/xpp_fxloader	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/xpp_fxloader	1970-01-01 02:00:00.000000000 +0200
+@@ -1,24 +0,0 @@
+-#!/bin/sh
+-
+-FIRMWARE="/etc/xortel/FPGA_XPD.hex"
+-me=`basename $0`
+-
+-# to run manually, pass the parameter 'xppdetect'
+-V_ID=04b4
+-P_ID=8613
+-if [ "$1" = 'xppdetect' ]; then
+-	DEVICES=`lsusb | tr -d : | awk "/ ID $V_ID$P_ID /{printf \"/proc/bus/usb/%s/%s \",\\$2,\\$4}"`
+-	echo "Loading firmware for $DEVICES"
+-	for dev in $DEVICES
+-	do
+-		fxload -t fx2 -D $dev -I $FIRMWARE
+-	done
+-	exit 0
+-fi
+-
+-if [ "$ACTION" = "add" ] && [ -f "$DEVICE" ]
+-then
+-	logger -i -t "$me" "Loading firmware '$FIRMWARE' into '$DEVICE'"
+-	fxload -t fx2 -D "$DEVICE" -I "$FIRMWARE" || exit 1
+-fi
+-
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xpp_fxloader.usermap zaptel-xpp-XJ8JZx_dist/xpp/xpp_fxloader.usermap
+--- zaptel-1.2.6/xpp/xpp_fxloader.usermap	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/xpp_fxloader.usermap	1970-01-01 02:00:00.000000000 +0200
+@@ -1,2 +0,0 @@
+-# module	match_flags	idVendor	idProduct	bcdDevice_lo	bcdDevice_hi	bDeviceClass	bDeviceSubClass	bDeviceProtocol	bInterfaceClass	bInterfaceSubClass	bInterfaceProtocol	driver_info
+-xpp_fxloader	0x0003		0x04b4		0x8613		0x0000		0x0000		0x00		0x00		0x00		0x00		0x00			0x00			0x0
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xpp_modprobe zaptel-xpp-XJ8JZx_dist/xpp/xpp_modprobe
+--- zaptel-1.2.6/xpp/xpp_modprobe	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/xpp_modprobe	1970-01-01 02:00:00.000000000 +0200
+@@ -1,10 +0,0 @@
+-# Some debugging options for the brave of heart:
+-#options zaptel debug=1
+-#options wcfxo debug=1
+-#options xpp print_dbg=1 
+-
+-# For pre-loading of card modules (e.g: xpp_fxs)
+-#install xpp_usb /sbin/modprobe xpd_fxs && /sbin/modprobe --ignore-install xpp_usb
+-
+-# For auto loading of card modules
+-alias	xpd-type-3	xpd_fxs
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xpp_proto.c zaptel-xpp-XJ8JZx_dist/xpp/xpp_proto.c
+--- zaptel-1.2.6/xpp/xpp_proto.c	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/xpp_proto.c	1970-01-01 02:00:00.000000000 +0200
+@@ -1,1044 +0,0 @@
+-#include <linux/module.h>
+-#include <linux/delay.h>	/* for udelay */
+-#include "xpd.h"
+-#include "xpp_proto.h"
+-#include "xpp_zap.h"
+-
+-static char rcsid[] = "$Id: xpp_proto.c 949 2006-02-15 02:24:18Z kpfleming $";
+-
+-extern	int print_dbg;
+-#include "zap_debug.h"
+-
+-typedef struct xpp_command xpp_command_t;
+-typedef	int (*xpp_handler_t)(xbus_t *xbus, int id, xpp_command_t *cmd, xpacket_t *packet);
+-
+-struct xpp_command {
+-	xpp_opcode_t	opcode;
+-	unsigned int	header_size;
+-	bool		varsize;
+-	const char	*name;
+-	const char	*desc;
+-	xpp_handler_t	handler;
+-};
+-
+-#define	S_(s,l,...)					\
+-	{						\
+-		.lines = s,				\
+-		{					\
+-			.len = l,			\
+-			.data = { __VA_ARGS__ },	\
+-		}					\
+-	}
+-
+-struct slic_init_data {
+-	xpp_line_t	lines;
+-	slic_data_t	slic_data;
+-} slic_init_data[] = {
+-#include "slic_init.inc"
+-};
+-
+-static int packet_process(xbus_t *xbus, int xpd_num, xpacket_t *pack);
+-static int simulate_xpd(xbus_t *xbus, int xpd_num, xpacket_t *sent_packet);
+-static bool pcm_valid(xpd_t *xpd, xpacket_t *reply);
+-
+-#define	NEW_PACKET(p, xbus, name, to)	\
+-	do {				\
+-		p = xbus->ops->packet_new(xbus, GFP_ATOMIC);	\
+-		if(!p)				\
+-			return -ENOMEM;		\
+-		PACKET_INIT(p, name);		\
+-		XPD_ADDR_SET(p->content.addr, to); \
+-	} while(0);
+-
+-/*------------------------- SLIC Handling --------------------------*/
+-
+-int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+-{
+-	int		len = 0;
+-	unsigned long	flags;
+-	xpd_t		*xpd = data;
+-	//slic_reply_t	*info;
+-
+-	BUG_ON(!xpd);
+-	spin_lock_irqsave(&xpd->lock, flags);
+-#if 0
+-	info = (slic_reply_t *)&xpd->slic_info;
+-	len += sprintf(page + len, "SLIC_REPLY: %s reg_num=0x%X, dataH=0x%X dataL=0x%X\n",
+-			(info->indirect)?"I":"D",
+-			info->reg_num, info->data_high, info->data_low);
+-#endif
+-	spin_unlock_irqrestore(&xpd->lock, flags);
+-	if (len <= off+count)
+-		*eof = 1;
+-	*start = page + off;
+-	len -= off;
+-	if (len > count)
+-		len = count;
+-	if (len < 0)
+-		len = 0;
+-	return len;
+-}
+-
+-static int parse_slic_cmd(const char *buf, slic_cmd_t *sc)
+-{
+-	char		op;		/* [W]rite, [R]ead */
+-	char		reg_type;	/* [D]irect, [I]ndirect */
+-	int		s1, s2, s3, s4;
+-	int		reg_num;
+-	int		data_low, data_high;
+-	xpp_line_t	lines;
+-	int		ret;
+-
+-	ret = sscanf(buf, "%x %x %x %x %c%c %x %x %x",
+-			&s1, &s2, &s3, &s4, &op, &reg_type, &reg_num, &data_high, &data_low);
+-	lines = (s4 << 24) | (s3 << 16) | (s2 << 8) | (s1);
+-	switch(op) {
+-		case 'R':
+-			if(reg_type == 'D' && ret == 7) {
+-				// DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num);
+-				ret = slic_cmd_direct_read(sc, lines, reg_num);
+-			} else if(reg_type == 'I' && ret == 7) {
+-				// DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num);
+-				ret = slic_cmd_indirect_read(sc, lines, reg_num);
+-			} else {
+-				NOTICE("%s: Bad read input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type);
+-				goto err;
+-			}
+-			break;
+-		case 'W':
+-			if(reg_type == 'D' && ret == 8) {
+-				// DBG("0x%X 0x%X 0x%X 0x%X %c %x %X\n", s1, s2, s3, s4, reg_type, reg_num, data_high);
+-				ret = slic_cmd_direct_write(sc, lines, reg_num, data_high);
+-			} else if(reg_type == 'I' && ret == 9) {
+-				// DBG("0x%X 0x%X 0x%X 0x%X %c %x %X %X\n", s1, s2, s3, s4, reg_type, reg_num, data_high, data_low);
+-				ret = slic_cmd_indirect_write(sc, lines, reg_num, data_low, data_high);
+-			} else {
+-				NOTICE("%s: Bad write input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type);
+-				goto err;
+-			}
+-			break;
+-		default:
+-			NOTICE("%s: Bad input: ret=%d buf='%s' op=%c\n", __FUNCTION__, ret, buf, op);
+-			goto err;
+-	}
+-	return ret;
+-err:
+-	return -EINVAL;
+-}
+-
+-static int process_slic_cmdline(xpd_t *xpd, char *cmdline)
+-{
+-	xbus_t		*xbus;
+-	slic_cmd_t	sc;
+-	xpacket_t	*pack_tx;
+-	char		*p;
+-	int		len = strlen(cmdline);
+-
+-	BUG_ON(!xpd);
+-	xbus = xpd->xbus;
+-	if((p = strchr(cmdline, '#')) != NULL)	/* Truncate comments */
+-		*p = '\0';
+-	if((p = strchr(cmdline, ';')) != NULL)	/* Truncate comments */
+-		*p = '\0';
+-	for(p = cmdline; *p && (*p == ' ' || *p == '\t'); p++) /* Trim leading whitespace */
+-		;
+-	if(*p == '\0')
+-		return 0;
+-	len = parse_slic_cmd(p, &sc);
+-	if(len < 0)
+-		return len;
+-	sc.lines &= xpd->enabled_chans;	// Ignore disabled channels
+-	if(!sc.lines) {
+-		NOTICE("%s: no enabled channels are marked. Skip.\n", __FUNCTION__);
+-		return 0;
+-	}
+-	dump_slic_cmd("WRITE_SLIC", &sc);
+-	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
+-	PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd) = sc;
+-	pack_tx->datalen = len;
+-	packet_send(xbus, pack_tx);
+-	return 0;
+-}
+-
+-int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data)
+-{
+-	xpd_t		*xpd = data;
+-	const int	LINE_LEN = 500;
+-	char		buf[LINE_LEN];
+-	char		*p;
+-	int		i;
+-	int		ret;
+-
+-	BUG_ON(!xpd);
+-	for(i = 0; i < count; /* noop */) {
+-		for(p = buf; p < buf + LINE_LEN; p++) {	/* read a line */
+-			if(i >= count)
+-				break;
+-			if(get_user(*p, buffer + i))
+-				return -EFAULT;
+-			i++;
+-			if(*p == '\n' || *p == '\r')	/* whatever */
+-				break;
+-		}
+-		if(p >= buf + LINE_LEN)
+-			return -E2BIG;
+-		*p = '\0';
+-		ret = process_slic_cmdline(xpd, buf);
+-		if(ret < 0)
+-			return ret;
+-	}
+-	return count;
+-}
+-
+-
+-
+-/*------------------------- Protocol Functions ---------------------*/
+-
+-#define	HOSTCMD(name, ...)		\
+-			DECLARE_CMD(name, ## __VA_ARGS__ );	\
+-			EXPORT_SYMBOL(xpp_proto_ ## name);	\
+-			DECLARE_CMD(name, ## __VA_ARGS__ )
+-
+-
+-/* 0x04 */ HOSTCMD(DESC_REQ, int xpd_num)
+-{
+-	int	ret = 0;
+-	xpacket_t	*pack_tx;
+-
+-	DBG("\n");
+-	if(!xbus) {
+-		DBG("NO XBUS\n");
+-		return -EINVAL;
+-	}
+-	NEW_PACKET(pack_tx, xbus, DESC_REQ, xpd_num);
+-	DBG("calling packet_send for a DESC_REQ packet.\n");
+-	ret = packet_send(xbus, pack_tx);
+-	DBG("after packet_send, updating counter (ret=%d)\n", ret);
+-	XBUS_COUNTER(xbus, DESC_REQ)++;
+-	return ret;
+-}
+-
+-/* 0x0F */ HOSTCMD(CHAN_POWER, xpp_line_t lines, bool on)
+-{
+-	int		ret = 0;
+-	xpacket_t	*pack_tx;
+-	slic_cmd_t	*sc;
+-	int		len;
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-	lines &= xpd->enabled_chans;	// Ignore disabled channels
+-	if(!lines) {
+-		return 0;
+-	}
+-	DBG("Channel Power: 0x%04X %s\n", lines, (on) ? "up" : "down");
+-	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
+-	sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd);
+-	if(on) {
+-		// Power up
+-		len = slic_cmd_direct_write(sc, lines, 0x42, 0x06);
+-	} else {
+-		// Power down
+-		len = slic_cmd_direct_write(sc, lines, 0x42, 0x00);
+-	}
+-	pack_tx->datalen = len;
+-
+-	packet_send(xbus, pack_tx);
+-	return ret;
+-}
+-
+-/* 0x0F */ HOSTCMD(CHAN_ENABLE, xpp_line_t lines, bool on)
+-{
+-	int		ret = 0;
+-	xpacket_t	*pack_tx;
+-	slic_cmd_t	*sc;
+-	int		len;
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-	lines &= xpd->enabled_chans;	// Ignore disabled channels
+-	if(!lines) {
+-		return 0;
+-	}
+-	DBG("Channel Activation: 0x%4X %s\n", lines, (on) ? "on" : "off");
+-	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
+-	sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd);
+-	len = slic_cmd_direct_write(sc, lines, 0x40, (on)?0x01:0x00);
+-	pack_tx->datalen = len;
+-
+-	packet_send(xbus, pack_tx);
+-	return ret;
+-}
+-
+-/* 0x0F */ HOSTCMD(RING, int pos, bool on)
+-{
+-	int		ret = 0;
+-	xpacket_t	*pack_tx;
+-	slic_cmd_t	*sc;
+-	xpp_line_t	mask = (1 << pos);
+-	int		len;
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-	mask &= xpd->enabled_chans;	// Ignore disabled channels
+-	if(!mask) {
+-		return 0;
+-	}
+-	DBG("%s pos=%d %s\n", xpd->xpdname, pos, (on) ? "on" : "off");
+-	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
+-	sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd);
+-	len = slic_cmd_direct_write(sc, mask, 0x40, (on)?0x04:0x01);
+-	pack_tx->datalen = len;
+-
+-	packet_send(xbus, pack_tx);
+-	return ret;
+-}
+-
+-/* 0x0F */ HOSTCMD(SETHOOK, xpp_line_t hook_status)
+-{
+-	int		ret = 0;
+-	xpacket_t	*pack_tx;
+-	slic_cmd_t	*sc;
+-	int		len;
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-	hook_status &= xpd->enabled_chans;	// Ignore disabled channels
+-	if(!hook_status) {
+-		return 0;
+-	}
+-	DBG("New hook_status: %d\n", hook_status);
+-	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
+-	sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd);
+-	/* FIXME: This is fake, until Dima implements FXO */
+-	len = slic_cmd_direct_write(sc, hook_status, 0x02, 0x00);
+-	pack_tx->datalen = len;
+-
+-	packet_send(xbus, pack_tx);
+-	return ret;
+-}
+-
+-/*
+- * LED control is done via SLIC register 0x06:
+- *         7     6     5     4     3     2     1     0
+- * 	+-----+-----+-----+-----+-----+-----+-----+-----+
+- * 	| MR  | MG  | MB  |  R  | OG  | OB  |  G  | B   |
+- * 	+-----+-----+-----+-----+-----+-----+-----+-----+
+- *
+- * 	B	- BLUE LED (0 - OFF, 1 - ON)
+- * 	G	- GREEN LED (0 - OFF, 1 - ON)
+- * 	OB	- Output BLUE (this line is output)
+- * 	OG	- Output GREEN (this line is output)
+- * 	R	- RED LED (0 - OFF, 1 - ON)
+- * 	MB	- Mask BLUE. (1 - B effect the BLUE LED)
+- * 	MR	- Mask RED. (1 - R effect the RED LED)
+- * 	MG	- Mask GREEN. (1 - G effect the GREEN LED)
+- *
+- * 	The BLUE LED (actually a relay out) is connected to line 0 and 4 only.
+- */
+-
+-//		                 	GREEN	RED	BLUE
+-static int	led_mask[NUM_LEDS] = { 	BIT(6),	BIT(7),	BIT(5) };
+-static int	led_vals[NUM_LEDS] = { 	BIT(1),	BIT(4),	BIT(0) };
+-
+-/* 0x0F */ HOSTCMD(LED, xpp_line_t lines, byte which, bool on)
+-{
+-	int		ret = 0;
+-	xpacket_t	*pack_tx;
+-	slic_cmd_t	*sc;
+-	int		len;
+-	int		value;
+-	int		i;
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-	lines &= xpd->enabled_chans;	// Ignore disabled channels
+-	if(!lines) {
+-		return 0;
+-	}
+-	DBG("LED: lines=0x%04X which=%d -- %s\n", lines, which, (on) ? "on" : "off");
+-	which = which % NUM_LEDS;
+-	value = BIT(2) | BIT(3);
+-	value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_mask[which]);
+-	if(on)
+-		value |= led_vals[which];
+-	for(i = 0; i < CHANNELS_PERXPD; i++) {
+-		if(!IS_SET(lines, i))
+-				continue;
+-		NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
+-		sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd);
+-		len = slic_cmd_direct_write(sc, lines, 0x06, value);
+-		DBG("LED pack: line=%d value=0x%04X\n", i, value);
+-		pack_tx->datalen = len;
+-		packet_send(xbus, pack_tx);
+-	}
+-	return ret;
+-}
+-
+-/* 0x0F */ HOSTCMD(RELAY_OUT, byte which, bool on)
+-{
+-	int		ret = 0;
+-	xpacket_t	*pack_tx;
+-	slic_cmd_t	*sc;
+-	int		len;
+-	int		value;
+-	xpp_line_t	lines;
+-	int		relay_channels[] = { 0, 4 };
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-
+-	DBG("RELAY_OUT: which=%d -- %s\n", which, (on) ? "on" : "off");
+-	which = which % ARRAY_SIZE(relay_channels);
+-	lines = BIT(relay_channels[which]);
+-	value = BIT(2) | BIT(3);
+-	value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_mask[LED_BLUE]);
+-	if(on)
+-		value |= led_vals[LED_BLUE];
+-	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
+-	sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd);
+-	len = slic_cmd_direct_write(sc, lines, 0x06, value);
+-
+-	DBG("RELAY_OUT pack: line=%d value=0x%04X\n", lines, value);
+-	pack_tx->datalen = len;
+-	packet_send(xbus, pack_tx);
+-	return ret;
+-}
+-
+-/* 0x0F */ HOSTCMD(SLIC_INIT)
+-{
+-	int	ret = 0;
+-	xpacket_t		*pack_tx;
+-	slic_data_t		*slic;
+-	struct slic_init_data	*source;
+-	int			i;
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-	DBG("INITIALIZING SLIC\n");
+-	for(i = 0; i < ARRAY_SIZE(slic_init_data); i++) {
+-		source = &slic_init_data[i];
+-		NEW_PACKET(pack_tx, xbus, SLIC_INIT, xpd->id);
+-		PACKET_FIELD(pack_tx, SLIC_INIT, lines) = source->lines;
+-
+-		slic = &PACKET_FIELD(pack_tx, SLIC_INIT, slic_data);
+-		slic->len = source->slic_data.len;
+-		memcpy(slic->data, source->slic_data.data, source->slic_data.len);
+-		pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1;
+-//		dump_packet("SLIC", pack_tx, print_dbg);
+-		packet_send(xbus, pack_tx);
+-		mdelay(10);	// FIXME: Temporary -- Dima need to fix it
+-	}
+-	return ret;
+-}
+-
+-/* 0x0F */ HOSTCMD(SLIC_QUERY, int pos, byte reg_num)
+-{
+-	int	ret = 0;
+-	xpacket_t	*pack_tx;
+-	slic_cmd_t	*sc;
+-	int		len;
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
+-	sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd);
+-	len = slic_cmd_direct_read(sc, (1<<pos), reg_num);
+-
+-	pack_tx->datalen = len;
+-
+-	packet_send(xbus, pack_tx);
+-	return ret;
+-}
+-
+-/* 0x11 */ HOSTCMD(PCM_WRITE, xpp_line_t lines, volatile byte *buf)
+-{
+-	int	ret = 0;
+-	xpacket_t	*pack_tx;
+-	byte		*pcm;
+-	byte		*start_pcm;
+-	int i;
+-	extern ulong	pcm_gen;
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-	lines &= xpd->enabled_chans;
+-	// DBG("PCM_WRITE\n");
+-	if(pcm_gen != 0)
+-		return 0;
+-//	if(lines == 0)
+-//		return 0;
+-
+-	/*
+-	 * FIXME: Workaround a bug in sync code of the Astribank.
+-	 *        Send dummy PCM for sync.
+-	 */
+-	if(lines == 0)
+-		lines = BIT(0);
+-
+-	NEW_PACKET(pack_tx, xbus, PCM_WRITE, xpd->id);
+-	PACKET_FIELD(pack_tx, PCM_WRITE, lines) = lines;
+-	start_pcm = pcm = PACKET_FIELD(pack_tx, PCM_WRITE, pcm);
+-	for(i = 0; i < CHANNELS_PERXPD; i++) {
+-		if(IS_SET(lines, i)) {
+-			memcpy(pcm, (byte *)buf, ZT_CHUNKSIZE);
+-			pcm += ZT_CHUNKSIZE;
+-		}
+-		buf += ZT_CHUNKSIZE;
+-	}
+-	pack_tx->datalen = sizeof(xpp_line_t) + (pcm - start_pcm);
+-	packet_send(xbus, pack_tx);
+-	XPD_COUNTER(xpd, PCM_WRITE)++;
+-	XBUS_COUNTER(xbus, PCM_WRITE)++;
+-	return ret;
+-}
+-
+-/* 0x13 */ HOSTCMD(PCM_GEN, xpp_line_t lines,  volatile byte *buf)
+-{
+-	xpacket_t	*pack_tx;
+-	bool		gen_seq = ((lines != 0) && (buf != NULL));
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-	lines &= xpd->enabled_chans;	// Ignore disabled channels
+-	if(!lines) {
+-		return 0;
+-	}
+-	DBG("PCM_GEN lines=0x%04X %s\n", lines, (gen_seq) ? "seq" : "off");
+-	NEW_PACKET(pack_tx, xbus, PCM_GEN, xpd->id);
+-	PACKET_FIELD(pack_tx, PCM_GEN, lines) = lines;
+-	if(gen_seq) {
+-		PACKET_FIELD(pack_tx, PCM_GEN, gen) = 0;
+-		memcpy(&PACKET_FIELD(pack_tx, PCM_GEN, pcm_seq), (byte *)buf, ZT_CHUNKSIZE);
+-	} else {
+-		PACKET_FIELD(pack_tx, PCM_GEN, gen) = 2;
+-	}
+-	packet_send(xbus, pack_tx);
+-	return 0;
+-}
+-
+-/*
+- * Sync source is controled by a mask byte to 0x19 command:
+- *         7     6     5     4     3     2     1     0
+- * 	+-----+-----+-----+-----+-----+-----+-----+-----+
+- * 	|     |     |     |     |     |     | RW  | AB  |
+- * 	+-----+-----+-----+-----+-----+-----+-----+-----+
+- *
+- *	RW	- Read or set (0 - Write, 1 - Read)
+- *	AB	- This Astribank provide sync (0 - no, 1 - yes)
+- *
+- */
+-
+-/* 0x19 */ HOSTCMD(SYNC_SOURCE, bool setit, bool is_master)
+-{
+-	xpacket_t	*pack_tx;
+-	byte		mask = 0;
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-	if(is_master)
+-		mask |= BIT(0);
+-	if(!setit)
+-		mask |= BIT(1);
+-	DBG("SYNC_SOURCE %s setit=%s is_master=%s (mask=0x%X)\n",
+-			xpd->xpdname, (setit)?"yes":"no", (is_master)?"yes":"no", mask);
+-	NEW_PACKET(pack_tx, xbus, SYNC_SOURCE, xpd->id);
+-	PACKET_FIELD(pack_tx, SYNC_SOURCE, mask) = mask;
+-	packet_send(xbus, pack_tx);
+-	return 0;
+-}
+-
+-/* 0x31 */ HOSTCMD(LOOPBACK_AX, byte *data, unsigned int size)
+-{
+-	xpacket_t	*pack_tx;
+-
+-	BUG_ON(!xbus);
+-	BUG_ON(!xpd);
+-	DBG("LOOPBACK_AX %d bytes\n", size);
+-	NEW_PACKET(pack_tx, xbus, LOOPBACK_AX, xpd->id);
+-	memcpy(&PACKET_FIELD(pack_tx, LOOPBACK_AX, data), data, size);
+-	packet_send(xbus, pack_tx);
+-	return 0;
+-}
+-
+-/*------------------------- Protocol Simulator ---------------------*/
+-
+-
+-static int simulate_xpd(xbus_t *xbus, int xpd_num, xpacket_t *sent_packet)
+-{
+-	xpacket_t	*pack = sent_packet;
+-	struct xpd_sim	*xpd_sim;
+-	struct xpd_sim	*loopto_sim;
+-	xpp_opcode_t	opcode;
+-	int		dest_xpd_num;
+-	int		ret = 0;
+-
+-	// Sanity checks
+-	BUG_ON(!xbus);
+-	BUG_ON(xpd_num > MAX_XPDS || xpd_num < 0);
+-	BUG_ON(!sent_packet);
+-	BUG_ON(!xbus->sim[xpd_num].simulated);
+-
+-	XBUS_COUNTER(xbus, SIM_PACKETS)++;
+-	xpd_sim = &xbus->sim[xpd_num];
+-	opcode = pack->content.opcode;
+-	dest_xpd_num = xpd_sim->loopto;
+-	loopto_sim = &xbus->sim[dest_xpd_num];
+-//	DBG("before: addr=%d, opcode=0x%X\n", xpd_num, opcode);
+-	switch(opcode) {
+-		case XPP_DESC_REQ:
+-			DBG("SIM DESC_REQ (xpd_num=%d)\n", xpd_num);
+-			PACKET_INIT(pack, DEV_DESC);
+-			PACKET_FIELD(pack, DEV_DESC, type) = xpd_sim->xpd_type;
+-			dest_xpd_num = xpd_num;	// Reply as the original XPD
+-			break;
+-		case XPP_PCM_WRITE:
+-			PACKET_INIT(pack, PCM_READ);
+-			XPD_ADDR_SET(pack->content.addr, dest_xpd_num);
+-			break;
+-		case XPP_SLIC_WRITE:
+-#if FINISHED_DECODING_SLICS
+-			slic_cmd_t	*sc;
+-			int		len;
+-
+-			sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd);
+-			lines = sc->lines;
+-			bool	slic_write = ! (slic->data[0] & 0x80);
+-			int	slic_reg = slic->data[0] & ~0x80;
+-
+-			if(slic->len == 2 && slic_write && slic_reg == 0x40) {				// RING
+-				bool		on = (slic->data[1] == 0x04);
+-				if(on) {
+-					loopto_sim->hookstate |= lines;
+-				} else {
+-					loopto_sim->hookstate &= ~lines;
+-				}
+-
+-				DBG("SIM RING to: xpd=%d (type=%d): (%s) ringing=0x%04X lines=0x%04X\n", dest_xpd_num, loopto_sim->xpd_type,
+-						(on)?"on":"off", loopto_sim->hookstate, lines);
+-				PACKET_INIT(pack, SIG_CHANGED);
+-				PACKET_FIELD(pack, SIG_CHANGED, type) = loopto_sim->xpd_type;
+-				PACKET_FIELD(pack, SIG_CHANGED, sig_status) = loopto_sim->hookstate;
+-				PACKET_FIELD(pack, SIG_CHANGED, sig_toggles) = lines;
+-				dump_packet("SIM RING TO", pack, print_dbg);
+-				break;
+-			} else if(slic->len == 1 && slic_write && slic_reg == 0x02) {			// SETHOOK
+-				DBG("SIM SETHOOK: xpd=%d: hookstate=0x%04X lines=0x%04X\n", dest_xpd_num, loopto_sim->hookstate, lines);
+-				PACKET_INIT(pack, SIG_CHANGED);
+-				PACKET_FIELD(pack, SIG_CHANGED, type) = loopto_sim->xpd_type;
+-				PACKET_FIELD(pack, SIG_CHANGED, sig_status) = lines;
+-				PACKET_FIELD(pack, SIG_CHANGED, sig_toggles) = loopto_sim->hookstate ^ lines;
+-				loopto_sim->hookstate = lines;
+-				break;
+-			} else if(slic->len == 2 && slic_write && slic_reg == 0x06) {			// LED
+-				DBG("SIM LED: xpd=%d: 0x%04X=%s\n", xpd_num, lines, (0x10)? "on" : "off");
+-				ret = 0;
+-				goto junk;
+-			} else if(slic->len == 2 && ! slic_write) {				// SLIC_QUERY
+-				DBG("SIM SLIC_QUERY: xpd=%d: register=0x%02X\n", xpd_num, slic_reg);
+-				ret = 0;
+-				goto junk;
+-			} else if(slic->len >= 4) {						// INITIALIZATION?
+-				DBG("SIM INITIALIZATION? xpd=%d len=%d\n", xpd_num, slic->len);
+-				ret = 0;
+-				goto junk;
+-			}
+-			NOTICE("%s: xpd=%d: SLIC_WRITE: len=%d\n", __FUNCTION__, xpd_num, slic->len);
+-#endif
+-			dump_packet("BAD SLIC_WRITE", pack, print_dbg);
+-			// FALL THROUGH
+-		default:
+-			NOTICE("%s: xpd=%d: CANNOT SIMULATE OPCODE=0x%02X\n", 
+-					__FUNCTION__, xpd_num, opcode);
+-//			dump_packet("BAD OPCODE", pack, print_dbg);
+-			ret = -EINVAL;
+-			goto junk;
+-	}
+-//	DBG("after reversing: addr=%d, opcode=0x%X\n", xpd_num, pack->header.opcode);
+-	return packet_process(xbus, dest_xpd_num, pack);
+-junk:
+-	xbus->ops->packet_free(xbus, pack);
+-	return ret;
+-}
+-
+-#define	VERBOSE_DEBUG			1
+-#define	ERR_REPORT_LIMIT		20
+-
+-void dump_packet(const char *msg, xpacket_t *packet, bool print_dbg)
+-{
+-	xpp_opcode_t	op = (byte)packet->content.opcode;
+-
+-	if(!print_dbg)
+-		return;
+-	DBG("%s: @0x%02X OP=0x%02X flags=0x%02X LEN=%d\n",
+-			msg,
+-			XPD_NUM(packet->content.addr),
+-			op,
+-			(byte)packet->flags,
+-			(byte)packet->datalen);
+-#if VERBOSE_DEBUG
+-	{
+-		int i;
+-		byte	*p = packet->content.raw;
+-
+-		for(i = 0; i < packet->datalen; i++) {
+-			static int limiter = 0;
+-
+-			if(i >= sizeof(xpp_packet_r_t)) {
+-				if(limiter < ERR_REPORT_LIMIT) {
+-					ERR("dump_packet: length overflow i=%d > sizeof(xpp_packet_r_t)=%d\n",
+-							i+1, sizeof(xpp_packet_r_t));
+-				} else if(limiter == ERR_REPORT_LIMIT) {
+-					ERR("dump_packet: error packet #%d... squelsh reports.\n", limiter);
+-				}
+-				limiter++;
+-				break;
+-			}
+-			DBG("        %2d> %02X\n", i+1, p[i]);
+-		}
+-	}
+-#endif
+-}
+-
+-/*------------------------- Reply Handlers -------------------------*/
+-
+-#define	HANDLER_DEF(name)		\
+-	int CALL_PROTO(name, xbus_t *xbus, int xpd_num, xpp_command_t *cmd, xpacket_t *reply)
+-
+-/*
+-static HANDLER_DEF(notimp)
+-{
+-	NOTICE("xpp protocol error: command %s is not implemented yet\n", cmd->name);
+-	return -EPROTO;
+-}
+-*/
+-static HANDLER_DEF(DEV_DESC)
+-{
+-	byte		type = PACKET_FIELD(reply, DEV_DESC, type) & 0x7;	// 3 LSB's
+-	byte		rev = PACKET_FIELD(reply, DEV_DESC, rev);
+-	xpp_line_t	line_status = PACKET_FIELD(reply, DEV_DESC, line_status);
+-	xpd_t		*xpd = xpd_of(xbus, xpd_num);
+-
+-	if(xpd) {
+-		NOTICE("Received DEV_DESC packet for an existing xpd %s of type %d\n", 
+-				xpd->xpdname, type);
+-		return 0;
+-	}
+-	XBUS_COUNTER(xbus, DEV_DESC)++;
+-	DBG("xpd=%d type=%d rev=%d line_status=0x%04X\n", xpd_num, type, rev, line_status);
+-	switch(type) {
+-	case XPD_TYPE_FXS:
+-		break;
+-	case XPD_TYPE_FXO:
+-		break;
+-	case XPD_TYPE_NOMODULE:
+-		DBG("No module at address=%d\n", xpd_num);
+-		return 0;
+-	default:
+-		NOTICE("DEV_DESC: unkown type=%d\n", type);
+-		return -EPROTO;
+-	}
+-	if((xpd = xpd_new(xbus, xpd_num, type, rev)) == NULL) {
+-		NOTICE("xpd_new failed\n");
+-	}
+-	xpp_check_hookstate(xpd, line_status);
+-	return 0;
+-}
+-
+-/**
+- * Handle signalling
+- */
+-static HANDLER_DEF(SIG_CHANGED)
+-{
+-	xpd_t		*xpd = xpd_of(xbus, xpd_num);
+-	xpp_line_t	sig_status = PACKET_FIELD(reply, SIG_CHANGED, sig_status);
+-
+-	if(!xpd) {
+-		NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num);
+-		return -EPROTO;
+-	}
+-	if(xpd->direction == TO_PHONE) {		/* Hook state changes */
+-		DBG("%s (PHONE) sig_status=0x%04X\n", xpd->xpdname, sig_status);
+-		xpp_check_hookstate(xpd, sig_status);
+-	} else {					/* TO_TRUNK - line ring changes */
+-		unsigned long	flags;
+-		int		i;
+-
+-		DBG("%s (TRUNK) sig_status=0x%04X\n", xpd->xpdname, sig_status);
+-		spin_lock_irqsave(&xpd->lock, flags);
+-		for(i = 0; i < xpd->channels; i++) {
+-			if(IS_SET(sig_status, i)) {
+-				xpd->ringing[i] = RINGS_NUM*2;
+-				zt_hooksig(&xpd->chans[i], ZT_RXSIG_OFFHOOK);
+-			} else {
+-				zt_hooksig(&xpd->chans[i], ZT_RXSIG_ONHOOK);
+-				xpd->ringing[i] = 0;
+-			}
+-		}
+-		spin_unlock_irqrestore(&xpd->lock, flags);
+-	}
+-	return 0;
+-}
+-
+-static HANDLER_DEF(SLIC_REPLY)
+-{
+-	slic_reply_t	*info = &PACKET_FIELD(reply, SLIC_REPLY, info);
+-	xpd_t		*xpd = xpd_of(xbus, xpd_num);
+-	unsigned long	flags;
+-
+-	if(!xpd) {
+-		NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num);
+-		return -EPROTO;
+-	}
+-	spin_lock_irqsave(&xpd->lock, flags);
+-	DBG("SLIC_REPLY: xpd #%d %s reg_num=0x%X, dataL=0x%X dataH=0x%X\n",
+-			xpd_num, (info->indirect)?"I":"D",
+-			info->reg_num, info->data_low, info->data_high);
+-	spin_unlock_irqrestore(&xpd->lock, flags);
+-	return 0;
+-}
+-
+-static HANDLER_DEF(PCM_READ)
+-{
+-	/* FIXME: work around temporary hardware bug */
+-	xpd_num = 0;
+-
+-	xpp_line_t	lines = PACKET_FIELD(reply, PCM_READ, lines);
+-	const byte	*pcm = PACKET_FIELD(reply, PCM_READ, pcm);
+-	xpd_t		*xpd = xpd_of(xbus, xpd_num);
+-	volatile u_char	*readchunk;
+-	volatile u_char	*r;
+-	unsigned long	flags;
+-	int		i;
+-
+-	if(!xpd) {
+-		NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num);
+-		return -EPROTO;
+-	}
+-	// DBG("lines=0x%04X\n", lines);
+-
+-	if(!pcm_valid(xpd, reply)) {
+-		return -EPROTO;
+-	}
+-	spin_lock_irqsave(&xpd->lock, flags);
+-	if (xpd->timer_count & 1) {
+-		/* First part */
+-		r = readchunk = xpd->readchunk;
+-	} else {
+-		r = readchunk = xpd->readchunk + ZT_CHUNKSIZE * CHANNELS_PERXPD;
+-	}
+-
+-	/* Copy PCM and put each channel in its index */
+-	for (i = 0; i < CHANNELS_PERXPD; i++) {
+-		if(IS_SET(lines, i)) {
+-			memcpy((u_char *)r, pcm, ZT_CHUNKSIZE);
+-			//memset((u_char *)r, 0x5A, ZT_CHUNKSIZE);	// DEBUG
+-			pcm += ZT_CHUNKSIZE;
+-		}
+-		r += ZT_CHUNKSIZE;
+-	}
+-
+-	XPD_COUNTER(xpd, PCM_READ)++;
+-	XBUS_COUNTER(xpd->xbus, PCM_READ)++;
+-	spin_unlock_irqrestore(&xpd->lock, flags);
+-	if(xpd->id == 0)
+-		xpp_tick(0);
+-	return 0;
+-}
+-
+-static HANDLER_DEF(SYNC_REPLY)
+-{
+-	xpd_t		*xpd = xpd_of(xbus, xpd_num);
+-
+-	if(!xpd) {
+-		NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num);
+-		return -EPROTO;
+-	}
+-	DBG("SYNC_REPLY: 0x%X\n", PACKET_FIELD(reply, SYNC_REPLY, mask));
+-	return 0;
+-}
+-
+-static HANDLER_DEF(LOOPBACK_XA)
+-{
+-	xpd_t		*xpd = xpd_of(xbus, xpd_num);
+-
+-	if(!xpd) {
+-		NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num);
+-		return -EPROTO;
+-	}
+-	dump_packet("LOOPBACK_XA", reply, print_dbg);
+-	CALL_PROTO(LED, xpd->xbus, xpd, xpd->enabled_chans, LED_RED, 0);		// FIXME: Find usage for extra LED
+-	return 0;
+-}
+-
+-static HANDLER_DEF(DIAG_FE)
+-{
+-	dump_packet("DIAG_FE", reply, print_dbg);
+-	return 0;
+-}
+-
+-static const xpp_packet_r_t	FOR_SIZE_CALC;	// Ugly hack, so we have something to sizeof()
+-
+-#define	C_(op,var,txt)	\
+-	   [ XPP_##op ] {	\
+-		   .opcode = XPP_##op, \
+-		   .varsize = var,	\
+-		   .header_size = sizeof(FOR_SIZE_CALC.cmd_##op), \
+-		   .name = #op, \
+-		   .desc = txt, \
+-		   .handler = PROTO_FUNC(op) \
+-	   }
+-
+-static xpp_command_t xpp_commands[] = {
+-  /*	OP		V 	DESCRIPTION	*/
+-  C_(	DEV_DESC,	0,	"Device description reply"),
+-  C_(	SIG_CHANGED,	0,	"Signaling change (hookstate/ringing)"),
+-  C_(	SLIC_REPLY,	0,	"Reply to slic state"),
+-  C_(	PCM_READ,	1,	"Read PCM data"),
+-  C_(	SYNC_REPLY,	0,	"SYNC_REPLY"),
+-  C_(	LOOPBACK_XA,	1,	"LOOPBACK Reply"),
+-  C_(	DIAG_FE,	1,	"DIAG FE Opcode"),
+-};
+-
+-#undef	C_
+-
+-static unsigned int xpp_max_opcode(void)
+-{
+-	return ARRAY_SIZE(xpp_commands);
+-}
+-
+-static bool xpp_valid_opcode(xpp_opcode_t op)
+-{
+-	if(op <= 0 || op >= xpp_max_opcode())
+-		return 0;
+-	return xpp_commands[op].opcode != XPP_NOTIMP;
+-}
+-
+-static xpp_command_t *xpp_command(xpp_opcode_t op)
+-{
+-	if(!xpp_valid_opcode(op))
+-		return 0;
+-	return &xpp_commands[op];
+-}
+-
+-static bool xpp_valid_size(xpp_opcode_t op, xpacket_t *pack)
+-{
+-	xpp_command_t	*cmd = xpp_command(op);
+-	unsigned int	hsize = cmd->header_size;
+-	unsigned int	size = pack->datalen;
+-	int		varsize = cmd->varsize;
+-
+-	// ERR("op=%d hsize=%d size=%d\n", op, hsize, size);
+-	return (hsize == size) ||
+-		(varsize && size <= sizeof(struct xpp_packet_r));
+-}
+-
+-static bool pcm_valid(xpd_t *xpd, xpacket_t *reply)
+-{
+-	xpp_opcode_t	op;
+-	xpp_command_t	*cmd;
+-	xpp_line_t	lines = PACKET_FIELD(reply, PCM_READ, lines);
+-	int		i;
+-	int		count = 0;
+-
+-	BUG_ON(!reply);
+-	op = reply->content.opcode;
+-	cmd = xpp_command(op);
+-	if(!cmd) {
+-		ERR("xpp: %s -- bad command op=0x%02X\n", __FUNCTION__, op);
+-		return 0;
+-	}
+-	for (i = 0; i < CHANNELS_PERXPD; i++)
+-		if(IS_SET(lines, i))
+-			count++;
+-	if(reply->datalen != (sizeof(xpp_line_t) + count * 8)) {
+-		static int rate_limit = 0;
+-
+-		XPD_COUNTER(xpd, RECV_ERRORS)++;
+-		if((rate_limit++ % 1000) <= 10) {
+-			ERR("BAD PCM REPLY: reply->datalen=%d, count=%d\n", reply->datalen, count);
+-		}
+-		return 0;
+-	}
+-	return 1;
+-}
+-
+-int packet_receive(xbus_t *xbus, xpacket_t *pack)
+-{
+-	int	xpd_num = XPD_NUM(pack->content.addr);
+-
+-	if(!VALID_XPD_NUM(xpd_num)) {
+-		dump_packet("martian packet", pack, print_dbg);
+-		xbus->ops->packet_free(xbus, pack);
+-		return -EPROTO;
+-	}
+-	if(xbus->sim[xpd_num].simulated) {
+-		//dump_packet("packet_receive -> simulate", pack, print_dbg);
+-		return simulate_xpd(xbus, xpd_num, pack);
+-	} else {
+-		//dump_packet("packet_receive -> process", pack, print_dbg);
+-		return packet_process(xbus, xpd_num, pack);
+-	}
+-}
+-
+-static int packet_process(xbus_t *xbus, int xpd_num, xpacket_t *pack)
+-{
+-	xpp_opcode_t	op;
+-	xpp_command_t	*cmd;
+-	xpp_handler_t	handler;
+-	int		ret = 0;
+-
+-	BUG_ON(!pack);
+-	op = pack->content.opcode;
+-	cmd = xpp_command(op);
+-	/*-------- Validations -----------*/
+-	if(!cmd) {
+-		ERR("xpp: %s -- bad command op=0x%02X\n", __FUNCTION__, op);
+-		dump_packet("packet_process -- bad command", pack, print_dbg);
+-		ret = -EPROTO;
+-		goto out;
+-	}
+-	if(!xpp_valid_size(op, pack)) {
+-		ERR("xpp: %s: wrong size %d for op=0x%02X\n",
+-					__FUNCTION__, pack->datalen, op);
+-		dump_packet("packet_process -- wrong size", pack, print_dbg);
+-		ret = -EPROTO;
+-		goto out;
+-	}
+-	handler = cmd->handler;
+-	BUG_ON(!handler);
+-	XBUS_COUNTER(xbus, RX_BYTES) += pack->datalen;
+-	handler(xbus, xpd_num, cmd, pack);
+-out:
+-	xbus->ops->packet_free(xbus, pack);
+-	return ret;
+-}
+-
+-
+-void process_sim_queue(void *data)
+-{
+-	xbus_t 		*xbus = data;
+-	xpacket_t	*pack;
+-
+-//	DBG("\n");
+-	BUG_ON(!xbus);
+-	while((pack = xbus_dequeue_packet(&xbus->sim_packet_queue)) != NULL) {
+-//		DBG("pack->addr=0x%X pack->opcode=0x%X\n", XPD_NUM(pack->addr), pack->header.opcode);
+-		packet_receive(xbus, pack);
+-	}
+-}
+-
+-/**
+- * processes a packet recieved from the lower-level.
+- * @xbus the data bus
+- * @pack the handled packet
+- * @returns return status (0 for success).
+- * 
+- * Should not be blocking. 
+- * Has separate handling for PCM packets (direct write) and command packets (queued)
+- */
+-
+-EXPORT_SYMBOL(dump_packet);
+-EXPORT_SYMBOL(packet_receive);
+-EXPORT_SYMBOL(proc_xpd_slic_read);
+-EXPORT_SYMBOL(proc_xpd_slic_write);
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xpp_proto.h zaptel-xpp-XJ8JZx_dist/xpp/xpp_proto.h
+--- zaptel-1.2.6/xpp/xpp_proto.h	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/xpp_proto.h	1970-01-01 02:00:00.000000000 +0200
+@@ -1,188 +0,0 @@
+-#ifndef	XPP_PROTO_H
+-#define	XPP_PROTO_H
+-
+-#include "xpd.h"
+-#include "slic.h"
+-#ifdef	__KERNEL__
+-#include <linux/list.h>
+-#endif
+-
+-#define	PCM_CHUNKSIZE	(CHANNELS_PERXPD * ZT_MAX_CHUNKSIZE)
+-
+-typedef enum xpp_opcode {
+-	XPP_NOTIMP		= 0x00,
+-//
+-	XPP_DESC_REQ		= 0x04,
+-	XPP_DEV_DESC		= 0x05,
+-//
+-	XPP_SIG_CHANGED		= 0x06,
+-//
+-	XPP_SLIC_WRITE		= 0x0F,	// Write to SLIC
+-	XPP_CHAN_ENABLE		= 0x0F,	// Write to SLIC
+-	XPP_CHAN_POWER		= 0x0F,	// Write to SLIC
+-	XPP_RING		= 0x0F,	// Write to SLIC
+-	XPP_SETHOOK		= 0x0F,	// Write to SLIC
+-	XPP_LED			= 0x0F,	// Write to SLIC
+-	XPP_RELAY_OUT		= 0x0F,	// Write to SLIC
+-	XPP_SLIC_INIT		= 0x0F,	// Write to SLIC
+-	XPP_SLIC_QUERY		= 0x0F,	// Write to SLIC
+-//
+-	XPP_SLIC_REPLY		= 0x10,
+-//
+-	XPP_PCM_WRITE		= 0x11,
+-	XPP_PCM_READ		= 0x12,
+-//
+-	XPP_PCM_GEN		= 0x13,
+-//
+-	XPP_SYNC_SOURCE		= 0x19,
+-	XPP_SYNC_REPLY		= 0x1A,
+-//
+-	XPP_LOOPBACK_AX		= 0x31,
+-	XPP_LOOPBACK_XA		= 0x32,
+-	XPP_DIAG_FE		= 0xFE,
+-} xpp_opcode_t;
+-
+-/*------------------------- PROTOCOL COMMANDS ----------------------*/
+-
+-#define	XPP_MAX_DATA		50
+-
+-typedef	struct slic_data {
+-	byte	len;
+-	byte	data[40];
+-} __attribute__((packed)) slic_data_t;
+-
+-#define	PROTO_FUNC(name)	xpp_proto_ ## name
+-#define	CALL_PROTO(name, ...)	PROTO_FUNC(name)( __VA_ARGS__ )
+-#define	DECLARE_CMD(name, ...)	\
+-	int CALL_PROTO(name, xbus_t *xbus, xpd_t *xpd, ## __VA_ARGS__ )
+-
+-/* 0x04 */ DECLARE_CMD(DESC_REQ, int xpd_num);
+-/* 0x0F */ DECLARE_CMD(CHAN_ENABLE, xpp_line_t lines, bool on);
+-/* 0x0F */ DECLARE_CMD(CHAN_POWER, xpp_line_t lines, bool on);
+-/* 0x0F */ DECLARE_CMD(RING, int pos, bool on);
+-/* 0x0F */ DECLARE_CMD(SETHOOK, xpp_line_t hook_status);
+-/* 0x0F */ DECLARE_CMD(LED, xpp_line_t lines, byte which, bool on);
+-/* 0x0F */ DECLARE_CMD(RELAY_OUT, byte which, bool on);
+-/* 0x0F */ DECLARE_CMD(SLIC_INIT);
+-/* 0x0F */ DECLARE_CMD(SLIC_QUERY, int pos, byte reg_num);
+-/* 0x11 */ DECLARE_CMD(PCM_WRITE, xpp_line_t hookstate,  volatile byte *buf);
+-/* 0x13 */ DECLARE_CMD(PCM_GEN, xpp_line_t lines,  volatile byte *buf);
+-/* 0x19 */ DECLARE_CMD(SYNC_SOURCE, bool setit, bool is_master);
+-/* 0x31 */ DECLARE_CMD(LOOPBACK_AX, byte *data, unsigned int size);
+-
+-#define	H_(op, ...)	struct { \
+-				__VA_ARGS__ \
+-	} __attribute__((packed)) cmd_##op
+-
+-/*
+- * This struct must be packed exactly as the wire
+- * representation of the packet header after the
+- * XPD address byte
+- */
+-typedef struct xpp_packet_r {
+-	byte		opcode;
+-	xpp_addr_t	addr;
+-	union {
+-
+-		H_(NOTIMP);
+-		H_(DESC_REQ);
+-		H_(DEV_DESC,
+-			byte		rev;					/* Revision number */
+-			byte		type;
+-			xpp_line_t	line_status;	/* hook/ring status, depending on unit */
+-			);
+-
+-		H_(SIG_CHANGED,
+-			byte		type;		/* unused -- we have it from DEV_DESC */
+-			xpp_line_t	sig_status;	/* channels: lsb=1, msb=8 */
+-			xpp_line_t	sig_toggles;	/* channels: lsb=1, msb=8 */
+-			);
+-
+-		H_(SLIC_INIT,
+-			xpp_line_t      lines;
+-			slic_data_t     slic_data;
+-			);
+-		H_(SLIC_WRITE,
+-			slic_cmd_t	slic_cmd;
+-			);
+-		H_(SLIC_REPLY,					/* Get status of a single SLIC (for debugging) */
+-			xpp_line_t	lines;
+-			slic_reply_t	info;
+-			);
+-
+-		H_(PCM_WRITE,
+-			xpp_line_t	lines;	// Must be 0xFF
+-			byte		pcm[PCM_CHUNKSIZE];
+-			);
+-		H_(PCM_READ,
+-			xpp_line_t	lines;	// Must be 0xFF
+-			byte		pcm[PCM_CHUNKSIZE];
+-			);
+-
+-		H_(PCM_GEN,
+-			xpp_line_t	lines;
+-			byte		gen;
+-			byte		pcm_seq[ZT_CHUNKSIZE];
+-			);
+-
+-		H_(SYNC_SOURCE,
+-			byte		mask;
+-			);
+-		H_(SYNC_REPLY,
+-			byte		mask;
+-			);
+-
+-		H_(LOOPBACK_AX,
+-			byte		data[XPP_MAX_DATA];	// FIXME: what data size?
+-			);
+-		H_(LOOPBACK_XA,
+-			byte		data[XPP_MAX_DATA];	// FIXME: what data size?
+-			);
+-		H_(DIAG_FE);
+-		unsigned char	raw[0];
+-	};
+-} __attribute__((packed)) xpp_packet_r_t;
+-#undef	H_
+-
+-#ifdef	__KERNEL__
+-
+-enum {
+-	XPP_PACKET_FIREANDFORGET	= 0x1,
+-};
+-
+-/**
+- * The packet that will actually be sent on the wire.
+- *
+- * TODO: not a good medium-level abstrction
+- */
+-struct xpp_packet {
+-	struct xpp_packet_r	content;
+-	unsigned int		flags;
+-	size_t				datalen;
+-	struct list_head	list;
+-};
+-
+-#define DATA_LEN(p, name) \
+-	sizeof(p->content.cmd_ ## name)
+-
+-#define	PACKET_LEN(p) \
+-	((p)->datalen + sizeof(xpp_addr_t) + 1)
+-
+-#define	PACKET_INIT(p, name)		\
+-	p->content.opcode = XPP_ ## name;	\
+-	p->datalen = DATA_LEN(p, name)
+-
+-#define	PACKET_FIELD(p, name, field)	\
+-	p->content.cmd_ ## name.field
+-
+-void dump_packet(const char *msg, xpacket_t *packet, bool print_dbg);
+-void enqueue_xmit(xbus_t *xbus, xpacket_t *pack);
+-void process_sim_queue(void *xbus);
+-int validate_reply(xpacket_t *reply);
+-int packet_receive(xbus_t *xbus, xpacket_t *pack);
+-int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data);
+-int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data);
+-
+-#endif
+-
+-#endif	/* XPP_PROTO_H */
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xpp_usb.c zaptel-xpp-XJ8JZx_dist/xpp/xpp_usb.c
+--- zaptel-1.2.6/xpp/xpp_usb.c	2006-04-03 10:08:13.000000000 +0300
++++ zaptel-xpp-XJ8JZx_dist/xpp/xpp_usb.c	2006-05-01 18:45:04.050100000 +0300
+@@ -1,6 +1,6 @@
+ /*
+  * Written by Oron Peled <oron at actcom.co.il>
+- * Copyright (C) 2004-2005, Xorcom
++ * Copyright (C) 2004-2006, Xorcom
+  *
+  * All rights reserved.
+  *
+@@ -30,6 +30,7 @@
+ #include <linux/module.h>
+ #include <linux/init.h>
+ #include <linux/errno.h>
++#include <linux/interrupt.h>
+ #include <linux/delay.h>	/* for udelay */
+ #include <linux/seq_file.h>
+ #include <asm/uaccess.h>
+@@ -37,11 +38,16 @@
+ #include <asm/timex.h>
+ #include <linux/proc_fs.h>
+ #include <linux/usb.h>
++#include <version.h>		/* For zaptel version */
+ #include "xpd.h"
+ #include "xproto.h"
+-#include "xpp_zap.h"
++#include "xbus-core.h"
++#ifdef	DEBUG
++#include "card_fxs.h"
++#include "card_fxo.h"
++#endif
+ 
+-static const char revision[] = "$Revision: 995 $";
++static const char rcsid[] = "$Id: xpp_usb.c 1104 2006-05-01 15:45:04Z oron $";
+ 
+ DEF_PARM(int, print_dbg, 0, "Print DBG statements");	/* must be before zap_debug.h */
+ 
+@@ -52,13 +58,11 @@
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
+ #  define URB_ASYNC_UNLINK 0
+ #endif
+-
+ #define	USBDEV_MAX	10
+ /* Get a minor range for your devices from the usb maintainer */
+ #define USB_SKEL_MINOR_BASE	192
+ 
+ #ifdef CONFIG_PROC_FS
+-#define	PROC_XBUSES	"xpp_usb"
+ #define	PROC_USBXPP_SUMMARY	"xpp_usb"
+ #endif
+ 
+@@ -70,8 +74,9 @@
+ struct xusb_model_info;
+ 
+ struct xusb_endpoint {
+-	int	epnum;
+-	int	max_size;
++	int		ep_addr;
++	int		max_size;
++	usb_complete_t	callback;
+ };
+ 
+ static int xusb_packet_send(xbus_t *xbus, xpacket_t *pack);
+@@ -110,18 +115,22 @@
+ 
+ #define	XUSB_COUNTER_MAX	ARRAY_SIZE(xusb_counters)
+ 
++enum xusb_dir {
++	XUSB_RECV = 0,
++	XUSB_SEND = 1,
++};
++
+ /*
+  * USB XPP Bus (a USB Device)
+  */
+-struct xpp_usb_bus {
++typedef struct xpp_usb_bus {
+ 	xbus_t			*xbus;
+ 	struct usb_device	*udev;			/* save off the usb device pointer */
+ 	struct usb_interface	*interface;		/* the interface for this device */
+ 	unsigned char		minor;			/* the starting minor number for this device */
+ 
+ 	struct xusb_model_info	*model_info;
+-	struct xusb_endpoint	ep_in;
+-	struct xusb_endpoint	ep_out;
++	struct xusb_endpoint	endpoints[2];		/* RECV/SEND endpoints */
+ 
+ 	struct urb		*read_urb;
+ 
+@@ -131,10 +140,10 @@
+ 	int			reading;		/* is the read_urb reading (listening) */
+ 	struct semaphore	sem;			/* locks this structure */
+ 	int		counters[XUSB_COUNTER_MAX];
+-};
++} xusb_t;
+ 
+ static	spinlock_t	xusb_lock = SPIN_LOCK_UNLOCKED;
+-static struct xpp_usb_bus *xusb_array[USBDEV_MAX] = {};
++static xusb_t *xusb_array[USBDEV_MAX] = {};
+ static unsigned bus_count = 0;
+ 
+ 
+@@ -153,7 +162,7 @@
+ static void xusb_write_bulk_callback	(struct urb *urb, struct pt_regs *regs);
+ #endif
+ static void xpp_urb_delete(struct urb *urb);
+-static struct urb *xpp_urb_new(struct xpp_usb_bus *dev, unsigned int ep_addr, size_t size, usb_complete_t urb_cb);
++static struct urb *xpp_urb_new(xusb_t *dev, enum xusb_dir dir, size_t size);
+ static void xpp_send_callback(struct urb *urb, struct pt_regs *regs);
+ static void xpp_receive_callback(struct urb *urb, struct pt_regs *regs);
+ 
+@@ -208,42 +217,34 @@
+ 	//DBG("Decremented packet_counter of bus %s (freed packet) to %d\n", 
+ 	//		xbus->busname, atomic_read(&xbus->packet_counter));
+ }
++
+ #endif
+ 
+-static int xusb_packet_send(xbus_t *xbus, xpacket_t *pack)
+-{
+-	struct xpp_usb_bus *xusb = xbus->priv;
+-	struct urb	*urb;
+-	int		ret = 0;
+-	size_t		size;
++#ifndef DEBUG
+ 
+-	BUG_ON(!pack);
+-	if(!xusb->present) {
+-		NOTICE("tried to send packets to non-exitant USB device. Ignored\n");
+-		goto error;
+-	}
+-#if SOFT_SIMULATOR
+-	{
+-		int		toxpd = XPD_NUM(pack->content.addr);
++#define	packet_debug(m, x, p)
+ 
+-		if (xbus->sim[toxpd].softloop_xpd) {
+-			// "send" through loopback queue
+-			//DBG("%s: ENQUEUE toxpd=%d, opcode=%X\n", xbus->busname, toxpd, pack->content.opcode);
+-			XBUS_COUNTER(xbus, SOFTSIM_PACKETS)++;
+-			xbus_enqueue_packet(xbus, &xbus->sim_packet_queue, pack);
+-			ret = queue_work(xbus->sim_workqueue, &xbus->sim_work);
+-			if(ret < 0) {
+-				ERR("%s: queue_work failed with %d (ignoring)\n", __FUNCTION__, ret);
+-				goto error;
+-			}
+-		}
+-		return 0;
+-	}
+-#endif
+-	size = min(PACKET_LEN(pack), (size_t)xusb->ep_out.max_size);
+-	if(pack->content.opcode == XPROTO_NAME(GLOBAL,PCM_WRITE)) {
+-		XUSB_COUNTER(xusb, PCM_WRITES)++;
++#else
+ 
++static void packet_debug(const char msg[], xusb_t *xusb, xpacket_t *pack)
++{
++	char	title[XBUS_DESCLEN];
++
++	if(pack->content.opcode == XPROTO_NAME(GLOBAL,PCM_READ)) {
++#ifdef	DEBUG_PCM_TIMING
++		/*
++		 * DEBUG: high-res timing of PCM_READ to PCM_WRITE
++		 */
++		stamp_last_pcm_read = get_cycles();
++#endif
++#if 0
++		// fill_beep((u_char *)&PACKET_FIELD(pack, PCM_READS, pcm), 2);	// Debugging BEEP
++		static	int rate_limit;
++		if((rate_limit++ % 1000) < 10)
++			dump_packet("USB RECEIVE PCM", pack, print_dbg);
++#endif
++		return;
++	} else if(pack->content.opcode == XPROTO_NAME(GLOBAL,PCM_WRITE)) {
+ #ifdef	DEBUG_PCM_TIMING
+ 		/*
+ 		 * DEBUG: high-res timing of PCM_READ to PCM_WRITE
+@@ -253,50 +254,88 @@
+ #endif
+ #if 0
+ 		static	int rate_limit;
+-		if((rate_limit++ % 1009) < 3) {
++		if((rate_limit++ % 1000) < 10)
+ 			dump_packet("USB SEND PCM", pack, print_dbg);
+-		}
+ #endif
+-	} else {
+-		dump_packet("USB_PACKET_SEND", pack, print_dbg);
++		return;
++	} else if(pack->content.opcode == XPROTO_NAME(FXS, SLIC_WRITE)) {
++		slic_cmd_t	*sc;
++
++		sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd);
++		if(sc->bytes == 2 && sc->content.direct.reg_num == 0x06 && sc->content.direct.read)	/* ignore SLIC_QUERY */
++			return;
++		if(sc->bytes == 2 && sc->content.direct.reg_num == DAA_VBAT_REGISTER && sc->content.direct.read)	/* ignore DAA_QUERY */
++			return;
++	} else if(pack->content.opcode == XPROTO_NAME(FXS, SLIC_REPLY)) {
++			return;
++	}
++	snprintf(title, XBUS_DESCLEN, "%s: %s", msg, xusb->xbus->busname);
++	dump_packet(title, pack, print_dbg);
++}
++#endif
++
++static int xusb_packet_send(xbus_t *xbus, xpacket_t *pack)
++{
++	xusb_t			*xusb;
++	struct urb		*urb;
++	int			ret = 0;
++	size_t			size;
++	struct xusb_endpoint	*xusb_ep;
++
++	BUG_ON(!pack);
++	BUG_ON(!xbus);
++	xusb = xbus->priv;
++	BUG_ON(!xusb);
++	if(!xusb->present) {
++		NOTICE("tried to send packets to non-exitant USB device. Ignored\n");
++		ret = -ENODEV;
++		goto out;
+ 	}
+-	urb = xpp_urb_new(xusb, xusb->ep_out.epnum, size, xpp_send_callback);
++	size = PACKET_LEN(pack);
++	xusb_ep = &xusb->endpoints[XUSB_SEND];
++	urb = xpp_urb_new(xusb, XUSB_SEND, size);
+ 	if (!urb) {
+ 		ERR("No free urbs available\n");
+ 		ret = -ENOMEM;
+-		goto error;
++		goto out;
+ 	}
++	packet_debug("USB_PACKET_SEND", xusb, pack);
+ 
+ 	/* FIXME: FIXME: FIXME: we use copy+free until low-level drivers allocate memory themselves */
+ 	memcpy(urb->transfer_buffer, &pack->content, size);
+-	xbus->ops->packet_free(xbus, pack);
+ 
+-	ret = usb_submit_urb(urb, GFP_KERNEL);
++	ret = usb_submit_urb(urb, GFP_ATOMIC);
+ 	if(ret < 0) {
+-		ERR("%s: failed submit_urb\n", __FUNCTION__);
+-		XUSB_COUNTER(xusb, TX_ERRORS)++;
++		ERR("%s: failed submit_urb: %d\n", __FUNCTION__, ret);
+ 		xpp_urb_delete(urb);
+-		return -EBADF;
++		ret = -EBADF;
++		goto out;
+ 	}
+-	return 0;
+-error:
++	if(pack->content.opcode == XPROTO_NAME(GLOBAL,PCM_WRITE))
++		XUSB_COUNTER(xusb, PCM_WRITES)++;
++out:
++	if(ret < 0)
++		XUSB_COUNTER(xusb, TX_ERRORS)++;
+ 	xbus->ops->packet_free(xbus, pack);	// FIXME: eventually will be done in the urb callback
+ 	return ret;
+ }
+ 
+ static void xpp_urb_delete(struct urb *urb)
+ {
++	BUG_ON(!urb);
+ 	// DBG("%s: (%d) %p %X", __FUNCTION__, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma);
+ 	usb_buffer_free (urb->dev, urb->transfer_buffer_length,
+ 			urb->transfer_buffer,
+ 			urb->transfer_dma);
+-	usb_free_urb (urb);
++	usb_free_urb(urb);
+ }
+ 
+-static struct urb *xpp_urb_new(struct xpp_usb_bus *dev, unsigned int ep_addr, size_t size, usb_complete_t urb_cb)
+-
++static struct urb *xpp_urb_new(xusb_t *xusb, enum xusb_dir dir, size_t size)
+ {
+-	struct usb_device	*udev = dev->udev;
++	struct usb_device	*udev = xusb->udev;
++	struct xusb_endpoint	*xusb_ep = &xusb->endpoints[dir];
++	unsigned int	ep_addr = xusb_ep->ep_addr;
++	usb_complete_t	urb_cb = xusb_ep->callback;
+ 	struct urb	*urb;
+ 	unsigned char	*buffer;	/* the buffer to send data */
+ 	unsigned int	epnum = ep_addr & USB_ENDPOINT_NUMBER_MASK;
+@@ -304,6 +343,8 @@
+ 					? usb_rcvbulkpipe(udev, epnum)
+ 					: usb_sndbulkpipe(udev, epnum);
+ 		 
++	if(size > xusb_ep->max_size)
++		return NULL;
+ 	urb = usb_alloc_urb(0, GFP_ATOMIC);
+ 	if (!urb) {
+ 		err("No free urbs available");
+@@ -326,30 +367,40 @@
+ 		usb_free_urb(urb);
+ 		return NULL;
+ 	}
+-	usb_fill_bulk_urb(urb, udev, pipe, buffer, size, urb_cb, dev);
++	usb_fill_bulk_urb(urb, udev, pipe, buffer, size, urb_cb, xusb);
+ 	return urb;
+ }
+ 
+ /*------------------------- XPP USB Bus Handling -------------------*/
+ 
+-static	struct xusb_model_info {
++#define	XUSB_MODEL(ep_in,ep_out,type,str)	\
++	{					\
++		.in = { .ep_addr = (ep_in) },	\
++		.out = { .ep_addr = (ep_out) },	\
++		.bus_type = (type),		\
++		.desc = (str)			\
++	}
++
++static const struct xusb_model_info {
+ 	const char		*desc;
+ 	struct xusb_endpoint	in;
+ 	struct xusb_endpoint	out;
+ 	xbus_type_t		bus_type;
+ } model_table[] = {
+-	{ .in = { .epnum = 0x86 }, .out = { .epnum = 0x2 }, .bus_type = FIRMWARE_LOOPBACK, .desc = "bulkloop.hex" },
+-	{ .in = { .epnum = 0x86 }, .out = { .epnum = 0x2 }, .bus_type = FIRMWARE_LOOPBACK, .desc = "FPGA_bulkloop.hex" },
+-	{ .in = { .epnum = 0x86 }, .out = { .epnum = 0x2 }, .bus_type = FIRMWARE_XPP, .desc = "FPGA_XPD.hex" },
++	XUSB_MODEL(0x86, 0x02, FIRMWARE_LOOPBACK,	"bulkloop.hex"),
++	XUSB_MODEL(0x86, 0x02, FIRMWARE_LOOPBACK,	"FPGA_bulkloop.hex"),
++	XUSB_MODEL(0x86, 0x02, FIRMWARE_XPP,		"FPGA_XPD.hex"),
+ };
+ 
+ /* table of devices that work with this driver */
+-static struct usb_device_id xusb_table [] = {
++static const struct usb_device_id xusb_table [] = {
+ //	{ USB_DEVICE(0x04B4, 0x8613) }, // default of cypress
+-	{ USB_DEVICE(0x0547, 0x1002), .driver_info=(int)&model_table[0] }, // bulkloop.hex
+-//	{ USB_DEVICE(0x04B4, 0x1004), .driver_info=(int)&model_table[1] }, // FPGA_bulkloop.hex
+-//	{ USB_DEVICE(0x04B4, 0x1004), .driver_info=(int)&model_table[2] }, // FIXME: temporary test for Dima
+-	{ USB_DEVICE(0xE4E4, 0x2211), .driver_info=(int)&model_table[2] }, // FPGA_XPD.hex
++	{ USB_DEVICE(0x0547, 0x1002), .driver_info=(kernel_ulong_t)&model_table[0] }, // bulkloop.hex
++//	{ USB_DEVICE(0x04B4, 0x1004), .driver_info=(kernel_ulong_t)&model_table[1] }, // FPGA_bulkloop.hex
++//	{ USB_DEVICE(0x04B4, 0x1004), .driver_info=(kernel_ulong_t)&model_table[2] }, // FIXME: temporary test for Dima
++	{ USB_DEVICE(0xE4E4, 0x2211), .driver_info=(kernel_ulong_t)&model_table[2] }, // FPGA_XPD.hex
++	{ USB_DEVICE(0xE4E4, 0x1132), .driver_info=(kernel_ulong_t)&model_table[2] }, // FPGA_XPD.hex
++//	{ USB_DEVICE(0x04B4, 0x1004), .driver_info=(kernel_ulong_t)&model_table[2] }, // TEST ONLY
+ 	//{ USB_DEVICE(0x0548, 0x1) },
+ 	//{ USB_DEVICE(0x062a, 0x0) },
+ 	/* "Gadget Zero" firmware runs under Linux */
+@@ -363,7 +414,7 @@
+ /* usb specific object needed to register this driver with the usb subsystem */
+ static struct usb_driver xusb_driver = {
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
+-        .owner =	THIS_MODULE,
++	.owner =	THIS_MODULE,
+ #endif
+ 	.name =		"xpp_usb",
+ 	.probe =	xusb_probe,
+@@ -412,10 +463,12 @@
+  * check out the endpoints
+  * FIXME: Should be simplified (above 2.6.10) to use usb_dev->ep_in[0..16] and usb_dev->ep_out[0..16]
+  */
+-static int set_endpoints(struct xpp_usb_bus *xusb, struct usb_interface *interface, struct xusb_model_info *model_info)
++static int set_endpoints(xusb_t *xusb, struct usb_interface *interface, struct xusb_model_info *model_info)
+ {
+ 	struct usb_host_interface	*iface_desc;
+ 	struct usb_endpoint_descriptor	*endpoint;
++	struct xusb_endpoint		*xusb_ep;
++	int				ep_addr;
+ 	int i;
+ 
+ 	iface_desc = &interface->altsetting[0];
+@@ -425,37 +478,40 @@
+ 
+ 	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ 		endpoint = &iface_desc->endpoint[i].desc;
+-		int	epnum = endpoint->bEndpointAddress;
++		ep_addr = endpoint->bEndpointAddress;
+ 		
+ 		if(!BULK_ENDPOINT(endpoint)) {
+ 			DBG("endpoint 0x%x is not bulk: mbAttributes=0x%X\n",
+-					epnum, endpoint->bmAttributes);
++					ep_addr, endpoint->bmAttributes);
+ 			continue;
+ 		}
+-		if(epnum & USB_DIR_IN) {	// Input
+-			if(epnum == model_info->in.epnum) {
++		if(usb_pipein(ep_addr)) {	// Input
++			if(ep_addr == model_info->in.ep_addr) {
+ 				if(endpoint->wMaxPacketSize < sizeof(xpacket_raw_t)) {
+-					ERR("USB input endpoint 0x%X support only wMaxPacketSize=%d (need USB-2)\n", epnum, endpoint->wMaxPacketSize);
+-					break;
++					NOTICE("USB input endpoint 0x%X support only wMaxPacketSize=%d (need USB-2)\n", ep_addr, endpoint->wMaxPacketSize);
+ 				}
+-				xusb->ep_in.epnum = epnum;
+-				xusb->ep_in.max_size = endpoint->wMaxPacketSize;
++				xusb_ep = &xusb->endpoints[XUSB_RECV];
++				xusb_ep->ep_addr = ep_addr;
++				xusb_ep->max_size = endpoint->wMaxPacketSize;
++				xusb_ep->callback = xpp_receive_callback;
+ 			}
+-		} else {					// Output
+-			if(epnum == model_info->out.epnum) {
++		} else {			// Output
++			if(ep_addr == model_info->out.ep_addr) {
+ 				if(endpoint->wMaxPacketSize < sizeof(xpacket_raw_t)) {
+-					ERR("USB output endpoint 0x%X support only wMaxPacketSize=%d (need USB-2)\n", epnum, endpoint->wMaxPacketSize);
+-					break;
++					NOTICE("USB output endpoint 0x%X support only wMaxPacketSize=%d (need USB-2)\n", ep_addr, endpoint->wMaxPacketSize);
+ 				}
+-				xusb->ep_out.epnum = epnum;
+-				xusb->ep_out.max_size = endpoint->wMaxPacketSize;
++				xusb_ep = &xusb->endpoints[XUSB_SEND];
++				xusb_ep->ep_addr = ep_addr;
++				xusb_ep->max_size = endpoint->wMaxPacketSize;
++				xusb_ep->callback = xpp_send_callback;
+ 			}
+ 		}
+ 	}
+-	if (!xusb->ep_in.epnum || !xusb->ep_out.epnum) {
++	if (!xusb->endpoints[XUSB_RECV].ep_addr || !xusb->endpoints[XUSB_SEND].ep_addr) {
+ 		ERR("Couldn't find bulk-in or bulk-out endpoints\n");
+ 		return 0;
+ 	}
++	DBG("in=0x%02X out=0x%02X\n", xusb->endpoints[XUSB_RECV].ep_addr, xusb->endpoints[XUSB_SEND].ep_addr);
+ 	return 1;
+ }
+ 
+@@ -468,10 +524,11 @@
+ static int xusb_probe(struct usb_interface *interface, const struct usb_device_id *id)
+ {
+ 	struct usb_device	*udev = interface_to_usbdev(interface);
+-	struct xpp_usb_bus	*xusb = NULL;
++	xusb_t			*xusb = NULL;
+ 	struct xusb_model_info	*model_info = (struct xusb_model_info*)id->driver_info;
+-	struct proc_dir_entry	*procsummary;
+-	xbus_t			*xbus;
++	struct proc_dir_entry	*procsummary = NULL;
++	xbus_t			*xbus = NULL;
++	struct xusb_endpoint	*xusb_ep;
+ 	unsigned long		flags;
+ 	int			retval = -ENOMEM;
+ 	int			i;
+@@ -490,13 +547,13 @@
+ 	}
+ 
+ 	/* allocate memory for our device state and initialize it */
+-	xusb = kmalloc(sizeof(struct xpp_usb_bus), GFP_KERNEL);
++	xusb = kmalloc(sizeof(xusb_t), GFP_KERNEL);
+ 	if (xusb == NULL) {
+ 		ERR("xpp_usb: Unable to allocate new xpp usb bus\n");
+ 		retval = -ENOMEM;
+ 		goto probe_failed;
+ 	}
+-	memset(xusb, 0, sizeof(struct xpp_usb_bus));
++	memset(xusb, 0, sizeof(xusb_t));
+ 
+ 	init_MUTEX (&xusb->sem);
+ 	xusb->udev = udev;
+@@ -507,12 +564,14 @@
+ 		retval = -ENODEV;
+ 		goto probe_failed;
+ 	}
+-	xusb->read_urb = xpp_urb_new(xusb, xusb->ep_in.epnum, xusb->ep_in.max_size, xpp_receive_callback);
++	xusb_ep = &xusb->endpoints[XUSB_RECV];
++	xusb->read_urb = xpp_urb_new(xusb, XUSB_RECV, xusb_ep->max_size);
+ 	if (!xusb->read_urb) {
+ 		ERR("No free urbs available\n");
+ 		retval = -ENOMEM;
+ 		goto probe_failed;
+ 	}
++
+ 	/* allow device read, write and ioctl */
+ 	xusb->present = 1;
+ 
+@@ -521,7 +580,7 @@
+ 	retval = usb_register_dev (interface, &xusb_class);
+ 	if (retval) {
+ 		/* something prevented us from registering this driver */
+-		ERR ("Not able to get a minor for this device.");
++		ERR ("Not able to get a minor for this device.\n");
+ 		goto probe_failed;
+ 	}
+ 
+@@ -531,33 +590,31 @@
+ 	INFO ("USB XPP device now attached to minor %d\n", xusb->minor);
+ 
+ 	/* Allocate high level structures */
+-	xbus = xbus_new((model_info->bus_type == FIRMWARE_LOOPBACK) ? ~0 : 0);
++	xbus = xbus_new(&xusb_ops);
+ 	if(!xbus) {
+ 		retval = -ENOMEM;
+ 		goto probe_failed;
+ 	}
+-	xusb->xbus = xbus;
+-	xbus->priv = xusb;
+ 	xbus->bus_type = model_info->bus_type;
++	xbus->max_packet_size = min(xusb->endpoints[XUSB_SEND].max_size , xusb->endpoints[XUSB_RECV].max_size);
+ 
+ 	spin_lock_irqsave(&xusb_lock, flags);
+ 	for(i = 0; i < USBDEV_MAX; i++) {
+ 		if(xusb_array[i] == NULL)
+ 			break;
+ 	}
++	spin_unlock_irqrestore(&xusb_lock, flags);
+ 	if(i >= USBDEV_MAX) {
+ 		ERR("xpp_usb: Too many XPP USB buses\n");
+ 		retval = -ENOMEM;
+ 		goto probe_failed;
+ 	}
+-	spin_unlock_irqrestore(&xusb_lock, flags);
+ 	{
+ 		char	path[XBUS_DESCLEN];
+ 
+ 		usb_make_path(udev, path, XBUS_DESCLEN);	// May trunacte... ignore
+ 		snprintf(xbus->busdesc, XBUS_DESCLEN, "%s", path);
+ 	}
+-	xbus->ops = &xusb_ops;
+ 
+ 	DBG("GOT XPP USB BUS #%d: %s (type=%d)\n", i, xbus->busdesc, xbus->bus_type);
+ 
+@@ -568,7 +625,6 @@
+ 	DBG("Creating proc entry " PROC_USBXPP_SUMMARY " in bus proc dir.\n");
+ 	procsummary = create_proc_read_entry(PROC_USBXPP_SUMMARY, 0444, xbus->proc_xbus_dir,
+ 			xusb_read_proc, xusb);
+-	//xbus->procsummary = 1; // temporary: not 0, for the condition below
+ 	if (!procsummary) {
+ 		ERR("Failed to create proc read entry for xbus %s\n", xbus->busname);
+ 		// FIXME: better error handling
+@@ -576,12 +632,14 @@
+ 		goto probe_failed;
+ 	}
+ #endif
++	bus_count++;
+ 	retval = usb_submit_urb(xusb->read_urb, GFP_ATOMIC);
+ 	if(retval < 0) {
+ 		ERR("%s: Failed to submit the receive URB errno=%d\n", __FUNCTION__, retval);
+ 	}
+-	bus_count++;
+-	xbus_activate(xusb->xbus);
++	xusb->xbus = xbus;
++	xbus->priv = xusb;
++	xbus_activate(xbus);
+ 	return retval;
+ probe_failed:
+ 	ERR("Failed to initialize xpp usb bus: %d\n", retval);
+@@ -593,6 +651,13 @@
+ 			usb_deregister_dev(interface, &xusb_class);
+ 		kfree(xusb);
+ 	}
++	if(xbus) {
++		if(procsummary) {
++			DBG("Remove proc_entry: " PROC_USBXPP_SUMMARY "\n");
++			remove_proc_entry(PROC_USBXPP_SUMMARY, xbus->proc_xbus_dir);
++		}
++		xbus_disconnect(xbus);		// Blocking until fully deactivated!
++	}
+ 	return retval;
+ }
+ 
+@@ -609,7 +674,7 @@
+  */
+ static void xusb_disconnect(struct usb_interface *interface)
+ {
+-	struct xpp_usb_bus	*xusb;
++	xusb_t			*xusb;
+ 	xbus_t			*xbus;
+ 	int			minor;
+ 	int			i;
+@@ -636,7 +701,7 @@
+ 	}
+ #endif
+ 	xusb->present = 0;
+-	xbus_deactivate(xbus);		// Blocking until fully deactivated!
++	xbus_disconnect(xbus);		// Blocking until fully deactivated!
+ 
+ 	down (&xusb->sem);
+ 
+@@ -645,6 +710,7 @@
+ 	/* give back our minor */
+ 	usb_deregister_dev (interface, &xusb_class);
+ 
++	/* terminate an ongoing read */
+ 	/* terminate an ongoing write */
+ 	// FIXME: Does it really kill pending URB's?
+ 
+@@ -657,12 +723,12 @@
+ 	kfree(xusb);
+ 
+ 	up (&disconnect_sem);
+-	INFO("XUSB #%d now disconnected", minor);
++	INFO("XUSB #%d now disconnected\n", minor);
+ }
+ 
+ static void xpp_send_callback(struct urb *urb, struct pt_regs *regs)
+ {
+-	struct xpp_usb_bus	*xusb = (struct xpp_usb_bus *)urb->context;
++	xusb_t			*xusb = (xusb_t *)urb->context;
+ 	xbus_t			*xbus = xusb->xbus;
+ 
+ 	BUG_ON(!xbus);
+@@ -670,7 +736,7 @@
+ 	if (urb->status && !(urb->status == -ENOENT || urb->status == -ECONNRESET)) {
+ 		static	int rate_limit;
+ 		if((rate_limit++ % 1000) < 10)
+-			DBG("nonzero read bulk status received: %d", urb->status);
++			DBG("nonzero read bulk status received: %d\n", urb->status);
+ 		XUSB_COUNTER(xusb, TX_ERRORS)++;
+ 	}
+ 	if(!xusb->present) {
+@@ -684,32 +750,43 @@
+ 
+ static void xpp_receive_callback(struct urb *urb, struct pt_regs *regs)
+ {
+-	struct xpp_usb_bus	*xusb = (struct xpp_usb_bus *)urb->context;
+-	xbus_t			*xbus = xusb->xbus;
+-
+-	xpacket_t		*pack;
+-	size_t			size;
+-	int			retval;
++	xusb_t		*xusb = (xusb_t *)urb->context;
++	xbus_t		*xbus;
++	xpacket_t	*pack;
++	size_t		size;
++	int		retval;
++	bool		do_resubmit = 1;
++	bool		is_inuse = 0;
+ 
+-	BUG_ON(!xbus);
++	BUG_ON(!xusb);
++	xbus = xusb->xbus;
++	if(!xbus) {
++		NOTICE("spurious URB\n");
++		return;
++	}
+ 	if (urb->status) {
+-		/* sync/async unlink faults aren't errors */
+-		if (!(urb->status == -EOVERFLOW || urb->status == -EMSGSIZE)) {
+-			ERR("Dropped connection due to bad URB status: %d\n", urb->status);
+-			return;
+-		} else {
+-			DBG("nonzero read bulk status received: %d\n", urb->status);
+-			goto end;
++		DBG("nonzero read bulk status received: %d\n", urb->status);
++		XUSB_COUNTER(xusb, RX_ERRORS)++;
++		/* Free old URB, allocate a fresh one */
++		if(xusb->read_urb)
++			xpp_urb_delete(xusb->read_urb);
++		xusb->read_urb = xpp_urb_new(xusb, XUSB_RECV, xusb->endpoints[XUSB_RECV].max_size);
++		if (!xusb->read_urb) {
++			ERR("URB allocation failed\n");
++			do_resubmit = 0;;
+ 		}
++		goto end;
+ 	}
+ 	if(!down_read_trylock(&xbus->in_use)) {
+ 		ERR("%s: xbus is going down\n", __FUNCTION__);
+-		return;
++		do_resubmit = 0;
++		goto end;
+ 	}
++	is_inuse = 1;
+ 	if(!xusb->present) {
+ 		ERR("A packet from non-connected device?\n");
+-		up_read(&xbus->in_use);
+-		return;
++		do_resubmit = 0;
++		goto end;
+ 	}
+ 	pack = xbus->ops->packet_new(xbus, GFP_ATOMIC);
+ 	if(!pack) {
+@@ -723,42 +800,21 @@
+ 	pack->datalen = size - sizeof(xpd_addr_t) - 1; 	// opcode size
+ 	// DBG("datalen of new packet: %d\n", pack->datalen);
+ 
+-	// Send UP
+-	if(pack->content.opcode == XPROTO_NAME(GLOBAL,PCM_READ)) {
++	packet_debug("USB_PACKET_RECEIVE", xusb, pack);
++	XUSB_COUNTER(xusb, RX_PACKETS)++;
++	if(pack->content.opcode == XPROTO_NAME(GLOBAL,PCM_READ))
+ 		XUSB_COUNTER(xusb, PCM_READS)++;
+-
+-#ifdef	DEBUG_PCM_TIMING
+-		/*
+-		 * DEBUG: high-res timing of PCM_READ to PCM_WRITE
+-		 */
+-		stamp_last_pcm_read = get_cycles();
+-#endif
+-		// fill_beep((u_char *)&PACKET_FIELD(pack, PCM_READS, pcm), 2);	// Debugging BEEP
+-#if 0
+-		static	int rate_limit;
+-		if((rate_limit++ % 1000) == 0)
+-			dump_packet("USB RECEIVE PCM", pack, print_dbg);
+-#endif
+-	} else if(pack->content.opcode == XPROTO_NAME(GLOBAL,PCM_WRITE)) {	// FIRMWARE_LOOPBACK
+-#if 0
+-		static	int rate_limit;
+-		if((rate_limit++ % 1000) == 0)
+-			dump_packet("USB RECEIVE (LOOPBACK) PCM", pack, print_dbg);
+-#endif
+-	} else {
+-		char	title[XBUS_DESCLEN];
+-
+-		snprintf(title, XBUS_DESCLEN, "USB_PACKET_RECEIVE callback (%s)", xbus->busname);
+-		dump_packet(title, pack, print_dbg);
+-	}
++	// Send UP
+ 	packet_receive(xbus, pack);
+-	XUSB_COUNTER(xusb, RX_PACKETS)++;
+ end:	
+-	up_read(&xbus->in_use);
+-	retval = usb_submit_urb(urb, GFP_KERNEL);
+-	if (retval < 0) {
+-		ERR("failed re-submitting read urb, error %d\n", retval);
+-		return;
++	if(is_inuse)
++		up_read(&xbus->in_use);
++	if(do_resubmit) {
++		retval = usb_submit_urb(urb, GFP_ATOMIC);
++		if (retval < 0) {
++			ERR("failed re-submitting read urb, error %d\n", retval);
++			return;
++		}
+ 	}
+ }
+ 
+@@ -768,14 +824,14 @@
+ int __init xpp_usb_init(void)
+ {
+ 	int result;
+-	//struct xpp_usb_bus *xusb;
++	//xusb_t *xusb;
+ 
+-	INFO("%s revision %s\n", THIS_MODULE->name, revision);
++	INFO("%s revision %s\n", THIS_MODULE->name, ZAPTEL_VERSION);
+ 
+ 	/* register this driver with the USB subsystem */
+ 	result = usb_register(&xusb_driver);
+ 	if (result) {
+-		ERR("usb_register failed. Error number %d", result);
++		ERR("usb_register failed. Error number %d\n", result);
+ 		return result;
+ 	}
+ 	return 0;
+@@ -784,34 +840,7 @@
+ 
+ void __exit xpp_usb_cleanup(void)
+ {
+-	int	i, j;
+-
+ 	DBG("\n");
+-	for(i = 0; i < USBDEV_MAX; i++) {
+-		xbus_t	*xbus;
+-
+-		if(xusb_array[i] == NULL)
+-			continue;
+-		xbus = xusb_array[i]->xbus;
+-		if(!xbus) {
+-			ERR("%s: missing xbus. Skipping\n", __FUNCTION__);
+-			continue;
+-		}
+-		for(j = 0; j < MAX_XPDS; j++) {
+-			xpd_t *xpd = xpd_of(xbus, j);
+-
+-			if(xpd) {
+-				if(xpd->id != j) {
+-					ERR("%s: BUG: xpd->id=%d != j=%d\n", __FUNCTION__, xpd->id, j);
+-					continue;
+-				}
+-#if 0	// FIXME: retest after new driver start working
+-				CALL_XMETHOD(CHAN_ENABLE, xbus, xpd, 0xFF, 0);	// Disable all hardware channels
+-				CALL_XMETHOD(LED, xbus, xpd, 0xFF, 1, 0);	// FIXME: Show activated channels
+-#endif
+-			}
+-		}
+-	}
+ 	/* deregister this driver with the USB subsystem */
+ 	usb_deregister(&xusb_driver);
+ }
+@@ -822,30 +851,29 @@
+ 
+ static int xusb_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
+ {
+-	int len = 0;
+-	unsigned long flags;
++	int 		len = 0;
++	unsigned long	flags;
++	int		i;
+ 	//unsigned long stamp = jiffies;
+-	struct xpp_usb_bus	*xusb = data;
++	xusb_t		*xusb = data;
+ 
+ 	if(!xusb)
+ 		goto out;
+ 	// TODO: probably needs a per-xusb lock:
+ 	spin_lock_irqsave(&xusb_lock, flags);
+-	int i;
+-
+ 	len += sprintf(page + len, "device: %d, #altsettings: %d, minor: %d\n"
+-		"\tBus Type:%d (Model Info: %s)\n"
+-		"\tIn:  0x%02X  - Size: %d\n"
+-		"\tOut: 0x%02X  - Size: %d\n",
++		"\tModel Info: Bus Type=%d (%s)\n"
++		"\tIn:  0x%02X  - Size: %d)\n"
++		"\tOut: 0x%02X  - Size: %d)\n",
+ 		xusb->udev->devnum,
+ 		xusb->interface->num_altsetting,
+ 		xusb->minor,
+ 		xusb->model_info->bus_type,
+ 		xusb->model_info->desc,
+-		xusb->ep_in.epnum,
+-		xusb->ep_in.max_size,
+-		xusb->ep_out.epnum,
+-		xusb->ep_out.max_size
++		xusb->endpoints[XUSB_RECV].ep_addr,
++		xusb->endpoints[XUSB_RECV].max_size,
++		xusb->endpoints[XUSB_SEND].ep_addr,
++		xusb->endpoints[XUSB_SEND].max_size
+ 	);
+ #ifdef	DEBUG_PCM_TIMING
+ 	len += sprintf(page + len, "\nstamp_last_pcm_read=%lld accumulate_diff=%lld\n", stamp_last_pcm_read, accumulate_diff);
+@@ -878,7 +906,7 @@
+ MODULE_DESCRIPTION("XPP USB Driver");
+ MODULE_AUTHOR("Oron Peled <oron at actcom.co.il>");
+ MODULE_LICENSE("GPL");
+-MODULE_VERSION("$Id: xpp_usb.c 995 2006-04-03 07:08:13Z tzafrir $");
++MODULE_VERSION(ZAPTEL_VERSION);
+ 
+ module_init(xpp_usb_init);
+ module_exit(xpp_usb_cleanup);
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xpp_zap.c zaptel-xpp-XJ8JZx_dist/xpp/xpp_zap.c
+--- zaptel-1.2.6/xpp/xpp_zap.c	2006-04-03 10:08:13.000000000 +0300
++++ zaptel-xpp-XJ8JZx_dist/xpp/xpp_zap.c	2006-06-27 11:46:37.045294178 +0300
+@@ -33,40 +33,34 @@
+ #include <linux/kernel.h>
+ #include <linux/errno.h>
+ #include <linux/module.h>
++#include <linux/device.h>
+ #include <linux/init.h>
+ #include <linux/delay.h>	/* for udelay */
+ #include <linux/workqueue.h>
+ #include <linux/proc_fs.h>
+ #include <zaptel.h>
++#include <version.h>		/* For zaptel version */
++#include "xbus-core.h"
+ #include "xproto.h"
+ #include "xpp_zap.h"
+ 
+-static char revision[] = "$Revision: 995 $";
++static const char rcsid[] = "$Id: xpp_zap.c 1435 2006-06-22 10:07:06Z oron $";
+ 
+ #ifdef CONFIG_PROC_FS
+-struct proc_dir_entry *xpp_procdir = NULL;
++struct proc_dir_entry *xpp_proc_toplevel = NULL;
+ #define	PROC_DIR		"xpp"
+-#define	PROC_XBUSES		"xbuses"
+ #define	PROC_SYNC		"sync"
+-#define	PROC_XBUS_SUMMARY	"summary"
+ #define	PROC_XPD_ZTREGISTER	"zt_registration"
+ #define	PROC_XPD_SUMMARY	"summary"
+ #endif
+ 
+-#undef	WITH_RBS
+-//#define	WITH_RBS
+-
+ #define	XPP_CTL_MAJOR		42
+-#define	MAX_BUSES		16
+ #define	MAX_QUEUE_LEN		10000
+-#define	LED_BLINK_PERIOD	(HZ/8)
+ #define	SAMPLE_TICKS		10000
++#define	DELAY_UNTIL_DIALTONE	3000
+ 
+-static spinlock_t		xbuses_lock = SPIN_LOCK_UNLOCKED;
+-static xbus_t			*xbuses_array[MAX_BUSES] = {};
+-static int			bus_count = 0;
+ static struct timer_list	xpp_timer;
+-xpd_t			*sync_master = NULL;	// Start with host based sync
++static xpd_t			*sync_master = NULL;	// Start with host based sync
+ static unsigned int		xpp_timer_count = 0;
+ static unsigned int		xpp_last_jiffies = 0;
+ struct workqueue_struct		*xpp_worker = NULL;
+@@ -75,203 +69,35 @@
+ 
+ DEF_PARM(int, print_dbg, 0, "Print DBG statements");
+ DEF_PARM(int, max_queue_len, MAX_QUEUE_LEN, "Maximum Queue Length.");
+-DEF_PARM(int, xbus_err_disable_bus, 1000, "Number of errors needed to disable bus");
+ DEF_PARM(int, ignore_xpds, 0, "a bitmask of xpd numbers to ignore");
+-#ifdef SOFT_SIMULATOR
+-DEF_PARM(ulong, softloop_xpds, 0, "a bitmask of software xpd numbers");
+-#endif
++DEF_PARM(int, xbus_err_disable_bus, 1000, "Number of errors needed to disable bus");	// FIXME: unused now.
+ DEF_PARM(ulong, pcm_gen, 0, "a bitmask of line numbers for hardware tone generator");
++DEF_PARM(bool, have_sync_bus, 0, "True if all Astribank(TM) devices are connected via a sync-cable");
++DEF_PARM(bool, zap_autoreg, 1, "Register spans automatically (1) or not (0)");
+ 
+ DEF_ARRAY(ulong, enabled_channels, MAX_XPDS, ~0, "Enabled channels for each xpd");
+ 
+ #include "zap_debug.h"
+-
++#ifdef	XPP_EC_CHUNK
++#include "supress/ec_xpp.h"
++#endif
+ 
+ static int xpd_zaptel_register(xpd_t *xpd);
+ static int xpd_zaptel_unregister(xpd_t *xpd);
+-static void xbus_remove(xbus_t *xbus);
+-static void xpd_blink_leds(xpd_t *xpd);
+ static void xpp_ring_generate(xpd_t *xpd);
+ static void xpp_transmitprep(xpd_t *xpd);
+ static void xpp_receiveprep(xpd_t *xpd);
+ static int xpd_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data);
+ static int proc_xpd_ztregister_read(char *page, char **start, off_t off, int count, int *eof, void *data);
+ static int proc_xpd_ztregister_write(struct file *file, const char __user *buffer, unsigned long count, void *data);
+-xbus_t *xbus_of(int xbus_num);
+-static void xpd_cleanup(xpd_t *xpd);
+-static void xpd_card_disable(xpd_t *xpd);
+-static void update_xpd_status(xpd_t *xpd, int alarm_flag);
+-
+-#define	SPAN_REGISTERED(xpd)	((xpd)->span.flags & ZT_FLAG_REGISTERED)
+-
+-/*------------------------- Packet Handling ------------------------*/
+-static kmem_cache_t	*packet_cache = NULL;
+-static atomic_t	xpacket_count = ATOMIC_INIT(0);
+-
+-void card_detected(void *data)
+-{
+-	struct card_desc_struct	*card_desc = (struct card_desc_struct *)data;
+-	xbus_t			*xbus;
+-	xpd_t			*xpd;
+-	int			xpd_num;
+-	byte			type;
+-	byte			rev;
+-	const xops_t		*xops;
+-	const xproto_table_t	*proto_table;
+-
+-	BUG_ON(!card_desc);
+-	BUG_ON(card_desc->magic != CARD_DESC_MAGIC);
+-	xbus = card_desc->xbus;
+-	xpd_num = card_desc->xpd_num;
+-	type = card_desc->type;
+-	rev = card_desc->rev;
+-	BUG_ON(!xbus);
+-	DBG("%s: xpd_num=%d type=%d rev=%d\n", xbus->busname, xpd_num, type, rev);
+-	xpd = xpd_of(xbus, xpd_num);
+-	if(xpd) {
+-		if(type == XPD_TYPE(NOMODULE)) {
+-			NOTICE("%s: xpd #%d: removed\n", __FUNCTION__, xpd_num);
+-			xpd_card_disable(xpd);
+-			goto out;
+-		}
+-		NOTICE("%s: xpd #%d: already exists\n", __FUNCTION__, xpd_num);
+-		goto out;
+-	}
+-	if(type == XPD_TYPE(NOMODULE)) {
+-		DBG("No module at address=%d\n", xpd_num);
+-		goto out;
+-	}
+-	proto_table = get_xproto_table(type);
+-	if(!proto_table) {
+-		NOTICE("%s: xpd #%d: missing protocol table for type=%d. Ignored.\n", __FUNCTION__, xpd_num, type);
+-		goto out;
+-	}
+-	xops = &proto_table->xops;
+-	BUG_ON(!xops);
+-	xpd = xops->card_new(xbus, xpd_num, proto_table, rev);
+-	if(!xpd) {
+-		NOTICE("card_new(%s,%d,%d,%d) failed. Ignored.\n", xbus->busname, xpd_num, proto_table->type, rev);
+-		goto out;
+-	}
+-#if 0
+-	/*
+-	 * Is it nessessary?
+-	 */
+-	if(xpd->type == XPD_TYPE_FXO) {
+-		int i;
+-
+-		for(i = 0; i < xpd->channels; i++) {
+-			zt_hooksig(&xpd->chans[i], ZT_RXSIG_ONHOOK);
+-		}
+-	}
+-#endif
+-#ifdef	CONFIG_PROC_FS
+-	DBG("Creating xpd proc directory for %s/%s\n", xbus->busname, xpd->xpdname);
+-	xpd->proc_xpd_dir = proc_mkdir(xpd->xpdname, xbus->proc_xbus_dir);
+-	if(!xpd->proc_xpd_dir) {
+-		ERR("Failed to create proc directory for %s/%s\n", xbus->busname, xpd->xpdname);
+-		goto err;
+-	}
+-	xpd->proc_xpd_summary = create_proc_read_entry(PROC_XPD_SUMMARY, 0444, xpd->proc_xpd_dir,
+-			xpd_read_proc, xpd);
+-	if(!xpd->proc_xpd_summary) {
+-		ERR("Failed to create proc '%s' for %s/%s\n", PROC_XPD_SUMMARY, xbus->busname, xpd->xpdname);
+-		goto err;
+-	}
+-	xpd->proc_xpd_ztregister = create_proc_entry(PROC_XPD_ZTREGISTER, 0644, xpd->proc_xpd_dir);
+-	if (!xpd->proc_xpd_ztregister) {
+-		ERR("Failed to create proc '%s' for %s/%s\n", PROC_XPD_ZTREGISTER, xbus->busname, xpd->xpdname);
+-		goto err;
+-	}
+-	xpd->proc_xpd_ztregister->data = xpd;
+-	xpd->proc_xpd_ztregister->read_proc = proc_xpd_ztregister_read;
+-	xpd->proc_xpd_ztregister->write_proc = proc_xpd_ztregister_write;
+-#endif
+-	list_add(&xpd->xpd_list, &xpd_list);
+-	xbus->xpds[xpd->id] = xpd;
+-	xbus->num_xpds++;
+-	CALL_XMETHOD(card_init, xbus, xpd);
+-	// Turn off all channels
+-	CALL_XMETHOD(CHAN_ENABLE, xbus, xpd, 0xFF, 0);
+-//	CALL_XMETHOD(LED, xbus, xpd, 0xFF, LED_RED, 0);				// FIXME: Show activated channels
+-	// Turn on enabled channels
+-	CALL_XMETHOD(CHAN_ENABLE, xbus, xpd, xpd->enabled_chans, 1);
+-	atomic_set(&xpd->card_present, 1);
+-	xpd_zaptel_register(xpd);
+-#if 0
+-	// FIXME: not yet initialized...
+-	xpp_check_hookstate(xpd, line_status);
+-#endif
+-
+-out:
+-	memset(card_desc, 0, sizeof(struct card_desc_struct));
+-	kfree(card_desc);
+-	return;
+-err:
+-	xpd_cleanup(xpd);
+-	goto out;
+-}
+-
+-/**
+- * Allocates a new XPP packet.
+- * @xbus The XPP bus in which the packet will flow (for counters 
+- *       maintenance)
+- * @flags Flags for kernel memory allocation.
+- * @returns A pointer to the new packet, or NULL in case of failure.
+- * 
+- * 
+- * Packet allocation/deallocation:
+- * 	Sent packets:
+- * 	  - Allocated by protocol commands
+- * 	  - Deallocated by xmus_xmitter
+- * 	Receive packets:
+- * 	  - Allocated/deallocated by xbus_xmiter
+- */
+-xpacket_t	*softloop_packet_new(xbus_t *xbus, int flags)
+-{
+-	xpacket_t	*pack;
+-
+-	/* To avoid races we increament counter in advance and decrement it later 
+-	 * in case of failure */
+-	atomic_inc(&xbus->packet_counter); 
+-	//DBG("Incremented packet_counter of bus %s (new packet) to %d\n", 
+-	//		xbus->busname, atomic_read(&xbus->packet_counter));
+-	pack = kmem_cache_alloc(packet_cache, flags);
+-	if (pack) {
+-		memset(pack, 0, sizeof(xpacket_t));
+-		atomic_inc(&xpacket_count);
+-	} else {
+-		atomic_dec(&xbus->packet_counter);
+-		//DBG("Decremented packet_counter of bus %s (failed new packet) to %d\n", 
+-		//		xbus->busname, atomic_read(&xbus->packet_counter));
+-	}
+-	return pack;
+-}
+-
+-void softloop_packet_free(xbus_t *xbus, xpacket_t *p)
+-{
+-	kmem_cache_free(packet_cache, p);
+-	atomic_dec(&xpacket_count);
+-	atomic_dec(&xbus->packet_counter);
+-	//DBG("Decremented packet_counter of bus %s (freed packet) to %d\n", 
+-	//		xbus->busname, atomic_read(&xbus->packet_counter));
+-}
+-
+-int call_proto(xbus_t *xbus, xpacket_t *pack)
+-{
+-	const xproto_entry_t	*xe;
+-	int			toxpd = XPD_NUM(pack->content.addr);
+-	xpd_t			*xpd = xpd_of(xbus, toxpd);
+-
+-	xe = find_xproto_entry(xpd, pack->content.opcode);
+-	return 0;
+-}
++static void xpd_free(xpd_t *xpd);
+ 
+ static void external_sync(xpd_t *the_xpd)
+ {
+ 	int	i, j;
+ 
+-	DBG("SYNC %s\n", (the_xpd) ? "EXTERNAL" : "HOST");
++	DBG("SYNC %s (%s sync cable)\n", (the_xpd)?"Astribanks":"HOST", (have_sync_bus)?"with":"without");
++	// Shut all down
+ 	for(i = 0; i < MAX_BUSES; i++) {
+ 		xbus_t	*xbus = xbus_of(i);
+ 		if(!xbus)
+@@ -279,21 +105,26 @@
+ 		if (!xbus->hardware_exists)
+ 			continue;
+ 		for(j = 0; j < MAX_XPDS; j++) {
+-			xpd_t	*xpd = xbus->xpds[j];
+-			if(xpd)
+-				CALL_XMETHOD(SYNC_SOURCE, xbus, xpd, 1, (the_xpd != NULL));
++			xpd_t	*xpd = xpd_of(xbus, j);
++			if(xpd) {
++				CALL_XMETHOD(SYNC_SOURCE, xbus, xpd, 1, 0);
++			}
+ 		}
+ 	}
++	if(the_xpd)
++		CALL_XMETHOD(SYNC_SOURCE, the_xpd->xbus, the_xpd, 1, 1);
+ }
+ 
+-void set_sync_master(xpd_t *xpd)
++void sync_master_is(xpd_t *xpd)
+ {
+-	DBG("SYNC: %s => %s\n",
++	DBG("SYNC MASTER CHANGED: %s => %s\n",
+ 			(sync_master) ? sync_master->xpdname : "HOST",
+-			(xpd) ? xpd->xpdname : "HOST"
+-			);
++			(xpd) ? xpd->xpdname : "HOST");
+ 	sync_master = xpd;
+-	if(!sync_master) {
++	if(xpd) {		// XPD
++		del_timer_sync(&xpp_timer);
++		xpp_tick((unsigned long)xpd);
++	} else {		// HOST
+ 		external_sync(NULL);
+ 		if(!timer_pending(&xpp_timer)) {
+ 			xpp_timer.function = xpp_tick;
+@@ -301,10 +132,6 @@
+ 			xpp_timer.expires = jiffies + 1;	/* Must be 1KHz rate */
+ 			add_timer(&xpp_timer);
+ 		}
+-	} else {
+-		del_timer_sync(&xpp_timer);
+-		external_sync(xpd);
+-		xpp_tick((unsigned long)xpd);
+ 	}
+ }
+ 
+@@ -337,42 +164,45 @@
+ 			continue;
+ 		if (!xbus->hardware_exists)
+ 			continue;
++		if(!down_read_trylock(&xbus->in_use)) {
++			DBG("Dropped packet. %s is in_use\n", xbus->busname);
++			continue;
++		}
+ #if 0
+ 		if(xbus->open_counter == 0)	
+ 			continue;		// optimize, but zttool loopback won't function
+ #endif
+ 		for(j = 0; j < MAX_XPDS; j++) {
+-			xpd_t	*xpd = xbus->xpds[j];
++			xpd_t	*xpd = xpd_of(xbus, j);
+ 
+ 			if(!xpd)
+ 				continue;
+-			if(!atomic_read(&xpd->card_present))
++			if(!xpd->card_present)
+ 				continue;
+ 			xpd->timer_count++;
+ 			CALL_XMETHOD(card_tick, xbus, xpd);
+ 			if(!SPAN_REGISTERED(xpd))
+ 				continue;
+-			xpd_blink_leds(xpd);
+ 			if(xpd->direction == TO_PSTN)
+ 				xpp_ring_generate(xpd);
+ 			xpp_transmitprep(xpd);
+ 			xpp_receiveprep(xpd);
+ 		}
++		up_read(&xbus->in_use);
+ 	}
+ }
+ 
+ #if HZ != 1000
+-#warning This module will not be usable since the kernel HZ setting is not 1000 ticks per second.
++#warning "xpp_timer must be sampled EXACTLY 1000/per second"
+ #endif
+ 
+-static void xpd_cleanup(xpd_t *xpd)
++static void xpd_free(xpd_t *xpd)
+ {
+ 	xbus_t	*xbus = NULL;
+ 
+ 	if(!xpd)
+ 		return;
+ 	xbus = xpd->xbus;
+-	xpd_card_disable(xpd);
+ 	DBG("%s/%s\n", xbus->busname, xpd->xpdname);
+ #ifdef CONFIG_PROC_FS
+ 	if(xpd->proc_xpd_dir) {
+@@ -391,109 +221,145 @@
+ 		xpd->proc_xpd_dir = NULL;
+ 	}
+ #endif
++	if(xpd->writechunk)
++		kfree((void *)xpd->writechunk);
++	if(xpd->xproto)
++		xproto_put(xpd->xproto);
++	kfree(xpd);
+ }
+ 
+-void init_xbus_packet_queue(packet_queue_t *q, const char name[])
+-{
+-	INIT_LIST_HEAD(&q->head);
+-	spin_lock_init(&q->lock);
+-	q->count = 0;
+-	q->worst_count = 0;
+-	q->overflows = 0;
+-	snprintf(q->qname, XPD_NAMELEN, "%s", name);
+-}
+ 
+-#if 0
+-/*
+- * Assume the queue is locked
+- */
+-void __dump_packet_queue(const char *msg, packet_queue_t *q)
+-{
+-	xpacket_t *tmp;
++/*------------------------- XPD Management -------------------------*/
+ 
+-	list_for_each_entry(tmp, &q->head, list) {
+-		dump_packet(msg, tmp);
+-	}
+-}
+-#endif
++#define	REV(x,y)	(10 * (x) + (y))
++static byte good_revs[] = {
++	REV(1,9),
++	REV(2,0),
++};
++#undef	REV
+ 
+-void drain_xbus_packet_queue(xbus_t *xbus, packet_queue_t *q)
++static bool good_rev(byte rev)
+ {
+-	unsigned long	flags;
+-	xpacket_t	*pack;
+-	xpacket_t	*next;
++	int	i;
+ 
+-	spin_lock_irqsave(&q->lock, flags);
+-	DBG("queue=%s count=%d\n", q->qname, q->count);
+-	DBG("     total packets count=%d\n", atomic_read(&xpacket_count));
+-	list_for_each_entry_safe(pack, next, &q->head, list) {
+-		list_del(&pack->list);
+-		q->count--;
+-		xbus->ops->packet_free(xbus, pack);
+-	}
+-	if(q->count != 0)
+-		ERR("drain_xbus_packet_queue: queue %s still has %d packets\n",
+-				q->qname, q->count);
+-	spin_unlock_irqrestore(&q->lock, flags);
++	for(i = 0; i < ARRAY_SIZE(good_revs); i++) {
++		if(good_revs[i] == rev)
++			return 1;
++	}
++	return 0;
+ }
+ 
+-void xbus_enqueue_packet(xbus_t *xbus, packet_queue_t *q, xpacket_t *pack)
++/*
++ * Synchronous part of XPD detection.
++ * Called from xpp_worker workqueue.
++ */
++void card_detected(void *data)
+ {
+-	unsigned long	flags;
+-
+-	spin_lock_irqsave(&q->lock, flags);
++	struct card_desc_struct	*card_desc = (struct card_desc_struct *)data;
++	xbus_t			*xbus;
++	xpd_t			*xpd = NULL;
++	int			xpd_num;
++	byte			type;
++	byte			rev;
++	const xops_t		*xops;
++	const xproto_table_t	*proto_table;
+ 
+-	if(q->count >= max_queue_len) {
+-		static unsigned long	last_notice = 0;	// rate limit
+ 
+-		if((jiffies - last_notice) < HZ) {
+-			NOTICE("xbus_enqueue_packet: dropping packet (queue len = %d, max=%d)\n",
+-				q->count, max_queue_len);
+-			last_notice = jiffies;
++	BUG_ON(!card_desc);
++	BUG_ON(card_desc->magic != CARD_DESC_MAGIC);
++	xbus = card_desc->xbus;
++	xpd_num = card_desc->xpd_num;
++	type = card_desc->type;
++	rev = card_desc->rev;
++	BUG_ON(!xbus);
++	if(!good_rev(rev)) {
++		NOTICE("%s: New XPD #%d type=%d has bad firmware revision %d.%d\n", xbus->busname,
++			xpd_num, type, rev / 10, rev % 10);
++		goto err;
++	}
++	INFO("%s: New XPD #%d type=%d Revision %d.%d\n", xbus->busname,
++			xpd_num, type, rev / 10, rev % 10);
++	xpd = xpd_of(xbus, xpd_num);
++	if(xpd) {
++		if(type == XPD_TYPE_NOMODULE) {
++			NOTICE("%s: xpd #%d: removed\n", __FUNCTION__, xpd_num);
++			xpd_disconnect(xpd);
++			goto out;
+ 		}
+-		q->overflows++;
+-		xbus->ops->packet_free(xbus, pack);
++		NOTICE("%s: xpd #%d: already exists\n", __FUNCTION__, xpd_num);
+ 		goto out;
+ 	}
+-	list_add_tail(&pack->list, &q->head);
+-	q->count++;
+-
+-	if(q->count > q->worst_count)
+-		q->worst_count = q->count;
+-
+-	if(q->count < max_queue_len/100 && q->worst_count > q->count)	// Decay worst_count
+-		q->worst_count--;
+-
+-	// dump_packet("ENQUEUED", pack, print_dbg);
+-out:
+-	spin_unlock_irqrestore(&q->lock, flags);
+-}
+-
+-xpacket_t *xbus_dequeue_packet(packet_queue_t *q)
+-{
+-	unsigned long	flags;
+-	struct list_head	*p;
+-	xpacket_t	*pack = NULL;
+-
+-	spin_lock_irqsave(&q->lock, flags);
+-
+-	if(list_empty(&q->head)) {
+-		// DBG("LIST EMPTY (count=%d)\n", q->count);
++	if(type == XPD_TYPE_NOMODULE) {
++		DBG("No module at address=%d\n", xpd_num);
++		goto out;
++	}
++	proto_table = xproto_get(type);
++	if(!proto_table) {
++		NOTICE("%s: xpd #%d: missing protocol table for type=%d. Ignored.\n", __FUNCTION__, xpd_num, type);
+ 		goto out;
+ 	}
+-	p = q->head.next;
+-	list_del(p);
+-	q->count--;
+-	pack = list_entry(p, xpacket_t, list);
+-	// dump_packet("DEQUEUED", pack, print_dbg);
++	xops = &proto_table->xops;
++	BUG_ON(!xops);
++	xpd = xops->card_new(xbus, xpd_num, proto_table, rev);
++	if(!xpd) {
++		NOTICE("card_new(%s,%d,%d,%d) failed. Ignored.\n", xbus->busname, xpd_num, proto_table->type, rev);
++		goto err;
++	}
++
++	/* For USB-1 disable some channels */
++	if(xbus->max_packet_size < RPACKET_SIZE(GLOBAL, PCM_WRITE)) {
++		xpp_line_t	no_pcm;
++
++		no_pcm = 0x7F | xpd->digital_outputs | xpd->digital_inputs;
++		xpd->no_pcm = no_pcm & xpd->enabled_chans;
++		NOTICE("%s: max packet size = %d, disabling some PCM channels. no_pcm=0x%04X\n",
++				xbus->busname, xbus->max_packet_size, xpd->no_pcm);
++	}
++#ifdef	CONFIG_PROC_FS
++	DBG("Creating xpd proc directory for %s/%s\n", xbus->busname, xpd->xpdname);
++	xpd->proc_xpd_dir = proc_mkdir(xpd->xpdname, xbus->proc_xbus_dir);
++	if(!xpd->proc_xpd_dir) {
++		ERR("Failed to create proc directory for %s/%s\n", xbus->busname, xpd->xpdname);
++		goto err;
++	}
++	xpd->proc_xpd_summary = create_proc_read_entry(PROC_XPD_SUMMARY, 0444, xpd->proc_xpd_dir,
++			xpd_read_proc, xpd);
++	if(!xpd->proc_xpd_summary) {
++		ERR("Failed to create proc '%s' for %s/%s\n", PROC_XPD_SUMMARY, xbus->busname, xpd->xpdname);
++		goto err;
++	}
++	xpd->proc_xpd_ztregister = create_proc_entry(PROC_XPD_ZTREGISTER, 0644, xpd->proc_xpd_dir);
++	if (!xpd->proc_xpd_ztregister) {
++		ERR("Failed to create proc '%s' for %s/%s\n", PROC_XPD_ZTREGISTER, xbus->busname, xpd->xpdname);
++		goto err;
++	}
++	xpd->proc_xpd_ztregister->data = xpd;
++	xpd->proc_xpd_ztregister->read_proc = proc_xpd_ztregister_read;
++	xpd->proc_xpd_ztregister->write_proc = proc_xpd_ztregister_write;
++#endif
++	if(CALL_XMETHOD(card_init, xbus, xpd) < 0)
++		goto err;
++	list_add(&xpd->xpd_list, &xpd_list);
++	xbus->xpds[xpd->id] = xpd;
++	xbus->num_xpds++;
++	// Turn off all channels
++	CALL_XMETHOD(CHAN_ENABLE, xbus, xpd, ~0, 0);
++	xpd->card_present = 1;
++	// Turn on enabled channels
++	CALL_XMETHOD(CHAN_ENABLE, xbus, xpd, xpd->enabled_chans, 1);
++
++	if(zap_autoreg)
++		xpd_zaptel_register(xpd);
+ out:
+-	spin_unlock_irqrestore(&q->lock, flags);
+-	return pack;
++	memset(card_desc, 0, sizeof(struct card_desc_struct));
++	kfree(card_desc);
++	return;
++err:
++	xpd_free(xpd);
++	goto out;
+ }
+ 
+ 
+-/*------------------------- XPD Management -------------------------*/
+-
+ #ifdef CONFIG_PROC_FS
+ 
+ /**
+@@ -510,83 +376,82 @@
+ 	int		len = 0;
+ 	xpd_t		*xpd = data;
+ 	xbus_t		*xbus;
+-#if SOFT_SIMULATOR
+-	struct xpd_sim	*sim;
+-#endif
+-	int		channels;
+ 	int		i;
+ 
+ 	if(!xpd)
+ 		goto out;
+ 
+ 	xbus = xpd->xbus;
+-#if SOFT_SIMULATOR
+-	sim = &xbus->sim[xpd->id];
+-#endif
+-	channels = xpd->channels;
+-	len += sprintf(page + len, "%s (%s ,card %s, span_registered=%s)%s\n"
++	len += sprintf(page + len, "%s (%s ,card %s, span %s) %s\n"
+ 			"timer_count: %d span->mainttimer=%d\n"
+ 			,
+ 			xpd->xpdname, xproto_name(xpd->type),
+-			(atomic_read(&xpd->card_present))?"present":"missing",
+-			(SPAN_REGISTERED(xpd))?"yes":"no",
+-			(xpd == sync_master) ? " SYNCER" : "",
++			(xpd->card_present) ? "present" : "missing",
++			(SPAN_REGISTERED(xpd)) ? "registered" : "NOT registered",
++			(xpd == sync_master) ? "SYNC MASTER" : "SYNC SLAVE",
+ 			xpd->timer_count, xpd->span.mainttimer
+ 			);
+ 	len += sprintf(page + len, "STATES:");
+ 	len += sprintf(page + len, "\n\t%-17s: ", "enabled");
+-	for(i = 0; i < channels; i++) {
++	for_each_line(xpd, i) {
+ 		len += sprintf(page + len, "%d ", IS_SET(xpd->enabled_chans, i));
+ 	}
+ 	len += sprintf(page + len, "\n\t%-17s: ", "output_relays");
+-	for(i = 0; i < channels; i++) {
++	for_each_line(xpd, i) {
+ 		len += sprintf(page + len, "%d ", IS_SET(xpd->digital_outputs, i));
+ 	}
+ 	len += sprintf(page + len, "\n\t%-17s: ", "input_relays");
+-	for(i = 0; i < channels; i++) {
++	for_each_line(xpd, i) {
+ 		len += sprintf(page + len, "%d ", IS_SET(xpd->digital_inputs, i));
+ 	}
+ 	len += sprintf(page + len, "\n\t%-17s: ", "hookstate");
+-	for(i = 0; i < channels; i++) {
++	for_each_line(xpd, i) {
+ 		len += sprintf(page + len, "%d ", IS_SET(xpd->hookstate, i));
+ 	}
+-	len += sprintf(page + len, "\n\t%-17s: ", "ring-state");
+-	for(i = 0; i < channels; i++) {
+-		len += sprintf(page + len, "%d ", xpd->lasttxhook[i]);
+-	}
+ 	len += sprintf(page + len, "\n\t%-17s: ", "ringing");
+-	for(i = 0; i < channels; i++) {
++	for_each_line(xpd, i) {
+ 		len += sprintf(page + len, "%d ", xpd->ringing[i]);
+ 	}
+ #if 1
+ 	if(SPAN_REGISTERED(xpd)) {
+-		len += sprintf(page + len, "\nreadchunk: ");
+-		for(i = 0; i < channels; i++) {
++		len += sprintf(page + len, "\nPCM:\n            |         [readchunk]       |         [writechunk]      | delay");
++		for_each_line(xpd, i) {
+ 			struct zt_chan	*chans = xpd->span.chans;
+-			byte	chunk[ZT_CHUNKSIZE];
++			byte	rchunk[ZT_CHUNKSIZE];
++			byte	wchunk[ZT_CHUNKSIZE];
++			byte	*rp;
++			byte	*wp;
+ 			int j;
+ 
+-			memcpy(chunk, chans[i].readchunk, ZT_CHUNKSIZE);
+-			len += sprintf(page + len, "\n\tport %2d> ", i);
++			if(IS_SET(xpd->digital_outputs, i))
++				continue;
++			if(IS_SET(xpd->digital_inputs, i))
++				continue;
++#if 1
++			rp = chans[i].readchunk;
++			wp = chans[i].writechunk;
++#else
++			rp = (byte *)xpd->readchunk + (ZT_CHUNKSIZE * i);
++			wp = chans[i].writechunk;
++#endif
++			memcpy(rchunk, rp, ZT_CHUNKSIZE);
++			memcpy(wchunk, wp, ZT_CHUNKSIZE);
++			len += sprintf(page + len, "\n  port %2d>  |  ", i);
+ 			for(j = 0; j < ZT_CHUNKSIZE; j++) {
+-				len += sprintf(page + len, "%02X ", chunk[j]);
++				len += sprintf(page + len, "%02X ", rchunk[j]);
+ 			}
+-		}
+-	}
+-#endif
+-#if SOFT_SIMULATOR
+-	if(sim->simulated) {
+-		len += sprintf(page + len, "\nSIMULATED (xpd_type=%d, loopto=%d):", sim->xpd_type, sim->loopto);
+-		len += sprintf(page + len, "\n\t%-17s: ", "hookstate");
+-		for(i = 0; i < channels; i++) {
+-			len += sprintf(page + len, "%d ", IS_SET(sim->hookstate, i));
++			len += sprintf(page + len, " |  ");
++			for(j = 0; j < ZT_CHUNKSIZE; j++) {
++				len += sprintf(page + len, "%02X ", wchunk[j]);
++			}
++			len += sprintf(page + len, " | %d ", xpd->delay_until_dialtone[i]);
+ 		}
+ 	}
+ #endif
+ #if 0
+ 	if(SPAN_REGISTERED(xpd)) {
+ 		len += sprintf(page + len, "\nSignalling:\n");
+-		for(i = 0; i < channels; i++) {
++		for_each_line(xpd, i) {
+ 			struct zt_chan *chan = &xpd->span.chans[i];
+ 			len += sprintf(page + len, "\t%2d> sigcap=0x%04X sig=0x%04X\n", i, chan->sigcap, chan->sig);
+ 		}
+@@ -620,7 +485,9 @@
+ xpd_t *xpd_alloc(size_t privsize, xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, int channels, byte revision)
+ {
+ 	xpd_t		*xpd = NULL;
++	size_t		pcm_size;
+ 	size_t		alloc_size = sizeof(xpd_t) + privsize;
++	int		i;
+ 
+ 	INFO("New XPD #%d (Revision %d.%d) detected on xbus %s\n",
+ 			xpd_num, revision / 10, revision % 10, xbus->busname);
+@@ -645,14 +512,20 @@
+ 	xpd->id = xpd_num;
+ 	xpd->channels = channels;
+ 	xpd->chans = NULL;
+-	atomic_set(&xpd->card_present, 0);
++	xpd->card_present = 0;
+ 	snprintf(xpd->xpdname, XPD_NAMELEN, "XPD-%d", xpd_num);
+ 	xpd->hookstate = 0x0;	/* ONHOOK */
+ 	xpd->type = proto_table->type;
++	xpd->xproto = proto_table;
+ 	xpd->xops = &proto_table->xops;
+ 	xpd->enabled_chans = enabled_channels[xpd_num];
+ 	xpd->digital_outputs = 0;
+ 	xpd->digital_inputs = 0;
++
++	for_each_line(xpd, i) {
++		xpd->idletxhookstate[i] = FXS_LINE_ENABLED;	/* By default, don't send on hook */
++	}
++
+ 	atomic_set(&xpd->open_counter, 0);
+ 
+ 	xpd->chans = kmalloc(sizeof(struct zt_chan)*xpd->channels, GFP_KERNEL);
+@@ -660,33 +533,47 @@
+ 		ERR("%s: Unable to allocate channels\n", __FUNCTION__);
+ 		goto err;
+ 	}
+-	/* 8 channels, double buffer, Read/Write */
+-	int need = ZT_MAX_CHUNKSIZE * CHANNELS_PERXPD * 2 * 2;
+-	if((xpd->writechunk = kmalloc(need, GFP_KERNEL)) == NULL) {
++	pcm_size = ZT_MAX_CHUNKSIZE * CHANNELS_PERXPD * 2;	/* Double Buffer */
++	alloc_size = pcm_size * 2;				/* Read/Write */
++	if((xpd->writechunk = kmalloc(alloc_size, GFP_KERNEL)) == NULL) {
+ 		ERR("%s: Unable to allocate memory for writechunks\n", __FUNCTION__);
+ 		goto err;
+ 	}
+ 	/* Initialize Write/Buffers to all blank data */
+-	memset((void *)xpd->writechunk, 0x00, need);
+-	xpd->readchunk = xpd->writechunk + ZT_CHUNKSIZE * CHANNELS_PERXPD * 2;
++	memset((void *)xpd->writechunk, 0x00, alloc_size);
++	xpd->readchunk = xpd->writechunk + pcm_size;
+ 
+ 	return xpd;
+ err:
+-	if(xpd->chans)
+-		kfree((void *)xpd->chans);
+-	if(xpd->writechunk)
+-		kfree((void *)xpd->writechunk);
+-	if(xpd)
++	if(xpd) {
++		if(xpd->chans)
++			kfree((void *)xpd->chans);
++		if(xpd->writechunk)
++			kfree((void *)xpd->writechunk);
+ 		kfree(xpd);
++	}
+ 	return NULL;
+ }
+ 
+-static void xpd_card_disable(xpd_t *xpd)
++void xpd_disconnect(xpd_t *xpd)
+ {
++	unsigned long	flags;
++
+ 	BUG_ON(!xpd);
+-	atomic_set(&xpd->card_present, 0);
++
++	// TODO: elect a new sync master
++	if(sync_master == xpd)
++		sync_master_is(NULL);
++
++	spin_lock_irqsave(&xpd->lock, flags);
++	DBG("%s/%s (%p)\n", xpd->xbus->busname, xpd->xpdname, xpd->xproto);
++	if(!xpd->card_present)	/* Multiple reports */
++		goto out;
++	xpd->card_present = 0;
+ 	if(SPAN_REGISTERED(xpd))
+ 		update_xpd_status(xpd, ZT_ALARM_NOTOPEN);
++out:
++	spin_unlock_irqrestore(&xpd->lock, flags);
+ }
+ 
+ void xpd_remove(xpd_t *xpd)
+@@ -696,22 +583,16 @@
+ 	BUG_ON(!xpd);
+ 	xbus = xpd->xbus;
+ 	INFO("Remove XPD #%d from xbus=%s\n", xpd->id, xbus->busname);
+-#if 0
+-	// TODO: elect a new sync master
+-	if(sync_master == xpd)
+-		set_sync_master(NULL);
+-#endif
++
+ 	xpd_zaptel_unregister(xpd);
+ 	xbus->xpds[xpd->id] = NULL;
+ 	list_del(&xpd->xpd_list);
+ 	xbus->num_xpds--;
+ 	CALL_XMETHOD(card_remove, xbus, xpd);
+-	xpd_cleanup(xpd);
+-	kfree((void *)xpd->writechunk);
+-	kfree(xpd);
++	xpd_free(xpd);
+ }
+ 
+-static void update_xpd_status(xpd_t *xpd, int alarm_flag)
++void update_xpd_status(xpd_t *xpd, int alarm_flag)
+ {
+ 	struct zt_span *span = &xpd->span;
+ 
+@@ -734,80 +615,6 @@
+ 	DBG("Update XPD alarms: %s -> %02X\n", xpd->span.name, alarm_flag);
+ }
+ 
+-void phone_hook(xpd_t *xpd, int channo, bool offhook)
+-{
+-	struct zt_chan *chan = &xpd->span.chans[channo];
+-
+-	if(offhook && !IS_SET(xpd->hookstate, channo)) {		// OFFHOOK
+-		DBG("OFFHOOK: channo=%d\n", chan->channo);
+-		xpd->ringing[channo] = 0;
+-		BIT_SET(xpd->hookstate, channo);
+-		zt_hooksig(chan, ZT_RXSIG_OFFHOOK);
+-		if(!IS_SET(xpd->digital_outputs, channo) && !IS_SET(xpd->digital_inputs, channo)) {
+-			CALL_XMETHOD(CHAN_POWER, xpd->xbus, xpd, BIT(channo), 0);		// Power down (prevent overheating!!!)
+-			CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(channo), LED_GREEN, 1);
+-		}
+-	} else if(!offhook && IS_SET(xpd->hookstate, channo)) {	// ONHOOK
+-		DBG("ONHOOK channo=%d\n", chan->channo);
+-		xpd->ringing[channo] = 0;
+-		BIT_CLR(xpd->hookstate, channo);
+-		zt_hooksig(chan, ZT_RXSIG_ONHOOK);
+-		if(!IS_SET(xpd->digital_outputs, channo) && !IS_SET(xpd->digital_inputs, channo)) {
+-			CALL_XMETHOD(CHAN_POWER, xpd->xbus, xpd, BIT(channo), 0);		// Power down (prevent overheating!!!)
+-			CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(channo), LED_GREEN, 0);
+-		}
+-	}
+-}
+-
+-void xpp_check_hookstate(xpd_t *xpd, xpp_line_t fxs_off_hook)
+-{
+-	int	i;
+-	unsigned long	flags;
+-
+-	spin_lock_irqsave(&xpd->lock, flags);
+-	if(xpd->direction != TO_PHONE) {
+-		ERR("%s: %s: Only PHONE can report hookstate changes\n", __FUNCTION__, xpd->xpdname);
+-		goto out;
+-	}
+-	if(!SPAN_REGISTERED(xpd)) {
+-		NOTICE("%s: %s is not registered. Skipping.\n", __FUNCTION__, xpd->xpdname);
+-		goto out;
+-	}
+-	DBG("%s: hookstate=0x%04X fxs_off_hook=0x%04X\n", xpd->xpdname, xpd->hookstate, fxs_off_hook);
+-	for(i = 0; i < xpd->channels; i++) {
+-		phone_hook(xpd, i, IS_SET(fxs_off_hook, i));
+-	}
+-out:
+-	spin_unlock_irqrestore(&xpd->lock, flags);
+-}
+-
+-static void xpd_blink_leds(xpd_t *xpd)
+-{
+-	int		i;
+-	unsigned long	flags;
+-
+-	BUG_ON(!xpd);
+-
+-	spin_lock_irqsave(&xpd->lock, flags);
+-	for(i = 0; i < xpd->channels; i++) {
+-		if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i))
+-			continue;
+-		if(xpd->ringing[i]) {
+-			// led state is toggled
+-			if((xpd->timer_count % LED_BLINK_PERIOD) == 0) {
+-				DBG("%s pos=%d ringing=%d led_on=%d\n", xpd->xpdname, i, xpd->ringing[i], xpd->led_on[i]);
+-				if(xpd->ringing[i] && xpd->led_on[i]) {
+-					CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(i), LED_GREEN, 1);
+-				} else {
+-					CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(i), LED_GREEN, 0);
+-				}
+-				xpd->led_on[i] = !xpd->led_on[i];
+-			}
+-		}
+-	}
+-	spin_unlock_irqrestore(&xpd->lock, flags);
+-}
+-
+ static void xpp_ring_generate(xpd_t *xpd)
+ {
+ 	int		i;
+@@ -829,7 +636,7 @@
+ 	 * Ring detect logic:
+ 	 * 	fxo_power is toggled
+ 	 */
+-	for(i = 0; i < xpd->channels; i++) {
++	for_each_enabled_line(xpd, i) {
+ 		if(xpd->ringing[i] || xpd->ringer_on[i]) {
+ 			// ring state is only changed once per second:
+ 			if((xpd->timer_count % 1000) == 0) {
+@@ -839,10 +646,7 @@
+ 				} else {
+ 					zt_hooksig(&xpd->chans[i], ZT_RXSIG_RING);
+ 				}
+-				xpd->ringing[i]--;
+ 				xpd->ringer_on[i] = !xpd->ringer_on[i];
+-				if (xpd->ringing[i] < 0) 
+-					xpd->ringing[i]=0;
+ 			}
+ 		}
+ 	}
+@@ -850,98 +654,13 @@
+ 	spin_unlock_irqrestore(&xpd->lock, flags);
+ }
+ 
+-/*------------------------- Bus Management -------------------------*/
+-
+-xbus_t *xbus_of(int xbus_num)
+-{
+-	if(xbus_num < 0 || xbus_num >= MAX_BUSES)
+-		return NULL;
+-	return xbuses_array[xbus_num];
+-}
+-
+-xpd_t	*xpd_of(xbus_t *xbus, int xpd_num)
+-{
+-	if(!VALID_XPD_NUM(xpd_num))
+-		return NULL;
+-	return xbus->xpds[xpd_num];
+-}
+-
+-void xbus_reset_counters(xbus_t *xbus)
+-{
+-	int	i;
+-
+-	DBG("Reseting counters of %s\n", xbus->busname);
+-	for(i = 0; i < XBUS_COUNTER_MAX; i++) {
+-		xbus->counters[i] = 0;
+-	}
+-//	xbus->xmit_queue.worst_count = 0;
+-//	xbus->xmit_queue.overflows = 0;
+-}
+-
+ #ifdef CONFIG_PROC_FS
+ 
+-/**
+- * Prints a general procfs entry for the bus, under xpp/BUSNAME/summary
+- * @page TODO: figure out procfs
+- * @start TODO: figure out procfs
+- * @off TODO: figure out procfs
+- * @count TODO: figure out procfs
+- * @eof TODO: figure out procfs
+- * @data an xbus_t pointer with the bus data.
+- */
+-static int xbus_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
+-{
+-	int len = 0;
+-	unsigned long flags;
+-	xbus_t	*xbus = data;
+-	int i;
+-
+-	if(!xbus)
+-		goto out;
+-	spin_lock_irqsave(&xbus->lock, flags);
+-
+-	len += sprintf(page + len, "%s: CONNECTOR=%s STATUS=%s bus_type=%d\n",
+-			xbus->busname,
+-			xbus->busdesc,
+-			(xbus->hardware_exists) ? "connected" : "missing",
+-			xbus->bus_type
+-		      );
+-	len += sprintf(page + len, "open_counter=%d packet_count=%d\n",
+-			xbus->open_counter,
+-			atomic_read(&xbus->packet_counter)
+-		      );
+-#if SOFT_SIMULATOR
+-	len += sprintf(page + len, "XPDS SIM\n");
+-	for(i = 0; i < MAX_XPDS; i++) {
+-		struct xpd_sim	*sim = &xbus->sim[i];
+-		xpd_t		*xpd = xpd_of(xbus, i);
+-		len += sprintf(page + len, "\t%d> ignored=%d simulated=%d softloop_xpd=%d loopto=%d instanciated=%s\n",
+-				i, IS_SET(ignore_xpds, i), sim->simulated, sim->softloop_xpd, sim->loopto, (xpd) ? "yes" : "no");
+-	}
+-#endif
+-	len += sprintf(page + len, "COUNTERS:\n");
+-	for(i = 0; i < XBUS_COUNTER_MAX; i++) {
+-		len += sprintf(page + len, "\t%-15s = %d\n",
+-				xbus_counters[i].name, xbus->counters[i]);
+-	}
+-	len += sprintf(page + len, "<-- len=%d\n", len);
+-	spin_unlock_irqrestore(&xbus->lock, flags);
+-out:
+-	if (len <= off+count)
+-		*eof = 1;
+-	*start = page + off;
+-	len -= off;
+-	if (len > count)
+-		len = count;
+-	if (len < 0)
+-		len = 0;
+-	return len;
+-
+-}
+-
+ int proc_sync_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+ {
+-	int len = 0;
++	int		len = 0;
++	unsigned int	xpp_timer_rate;
++	unsigned int	now;
+ 
+ 	len += sprintf(page + len, "# To modify sync source write into this file:\n");
+ 	len += sprintf(page + len, "#     HOST        - For host based sync\n");
+@@ -952,8 +671,8 @@
+ 	else
+ 		len += sprintf(page + len, "%s/%s\n", sync_master->xbus->busname, sync_master->xpdname);
+ 	len += sprintf(page + len, "tick: #%d\n", xpp_timer_count);
+-	unsigned int xpp_timer_rate = 0;
+-	unsigned int now = jiffies;
++	xpp_timer_rate = 0;
++	now = jiffies;
+ 	if(now - xpp_last_jiffies > 0) {
+ 		xpp_timer_rate = ((xpp_timer_count % SAMPLE_TICKS) * 1000) / (now - xpp_last_jiffies);
+ 		len += sprintf(page + len, "tick rate: %4d/second (average over %d seconds)\n", xpp_timer_rate, SAMPLE_TICKS/HZ);
+@@ -971,7 +690,6 @@
+ 
+ static int proc_sync_write(struct file *file, const char __user *buffer, unsigned long count, void *data)
+ {
+-	DBG("%s: count=%ld\n", __FUNCTION__, count);
+ 	const int	NUM_SIZE = 100;
+ 	char		buf[NUM_SIZE];
+ 	int		xbus_num;
+@@ -979,31 +697,47 @@
+ 	xbus_t		*xbus;
+ 	xpd_t		*xpd;
+ 	int		ret;
++	bool		setit;
+ 
++	// DBG("%s: count=%ld\n", __FUNCTION__, count);
+ 	if(count >= NUM_SIZE)
+ 		return -EINVAL;
+ 	if(copy_from_user(buf, buffer, count))
+ 		return -EFAULT;
+ 	buf[count] = '\0';
+ 	if(strncmp("HOST", buf, 4) == 0) {
+-		set_sync_master(NULL);
++		sync_master_is(NULL);
+ 		goto out;
+ 	}
+-	ret = sscanf(buf, "%d %d", &xbus_num, &xpd_num);
+-	if(ret != 2)
++	ret = sscanf(buf, "%d %d %d", &xbus_num, &xpd_num, &setit);
++	if(ret == 2) {
++		// For backward compatibility: before query was introduced,
++		// only two parameters were possible
++		setit = 1;
++		ret = 3;
++	}
++	if(ret != 3 || (setit != 0 && setit != 1)) {
++		ERR("Bad format for SYNC.\n");
++		ERR("Usage: <bus_num> <xpd_num> <0/1> # 0 - QUERY, 1 - SET\n");
+ 		return -EINVAL;
+-	DBG("%s: %d/%d\n", __FUNCTION__, xbus_num, xpd_num);
+-	if(xbus_num >= MAX_BUSES)
++	}
++	if(xbus_num >= MAX_BUSES) {
++		ERR("Invalid xbus number %d\n", xbus_num);
+ 		return -EINVAL;
++	}
+ 	xbus = xbus_of(xbus_num);
+-	if(!xbus)
++	if(!xbus) {
++		ERR("No bus %d exists\n", xbus_num);
+ 		return -EINVAL;
++	}
+ 	xpd = xpd_of(xbus, xpd_num);
+ 	if(!xpd) {
+ 		ERR("%s: XPD number %d does not exist\n", __FUNCTION__, xpd_num);
+ 		return -ENXIO;
+ 	}
+-	set_sync_master(xpd);
++	DBG("%s: %d/%d %s\n", __FUNCTION__, xbus_num, xpd_num, (setit)?"SET":"QUERY");
++	if(setit)
++		external_sync(xpd);
+ out:
+ 	return count;
+ }
+@@ -1048,348 +782,75 @@
+ 	if(ret != 1)
+ 		return -EINVAL;
+ 	DBG("%s: %s/%s %s\n", __FUNCTION__,
+-			xpd->xbus->busname, xpd->xpdname, (zt_reg) ? "register" : "unregister");
+-	if(zt_reg)
+-		ret = xpd_zaptel_register(xpd);
+-	else
+-		ret = xpd_zaptel_unregister(xpd);
+-	return (ret < 0) ? ret : count;
+-}
+-
+-#endif
+-
+-/**
+- *
+- * Packet is freed:
+- * 	- In case of error, by this function.
+- * 	- Otherwise, by the underlying sending mechanism
+- */
+-int packet_send(xbus_t *xbus, xpacket_t *pack_tx)
+-{
+-	int		ret = -ENODEV;
+-	int		toxpd;
+-
+-	if(!pack_tx) {
+-		DBG("null pack\n");
+-		return -EINVAL;
+-	}
+-	toxpd = XPD_NUM(pack_tx->content.addr);
+-	if(!xbus) {
+-		DBG("null xbus\n");
+-		ret = -EINVAL;
+-		goto error;
+-	}
+-	if (!xbus->hardware_exists) {
+-		DBG("xbus %s Dropped a packet -- NO HARDWARE.", xbus->busname);
+-		ret = -ENODEV;
+-		goto error;
+-	}
+-	if(!VALID_XPD_NUM(toxpd)) {
+-		ERR("%s: toxpd=%d > MAX_XPDS\n", __FUNCTION__, toxpd);
+-		ret = -EINVAL;
+-		goto error;
+-	}
+-#if 0
+-	// DEBUG: For Dima
+-	if(pack_tx->content.opcode == XPP_PCM_WRITE) {
+-		static	int rate_limit;
+-		static	int count;
+-
+-		if(sync_master == NULL)
+-			count = 0;
+-		if(count++ > 5) {
+-			ret = 0;
+-			goto error;
+-		}
+-		if(rate_limit++ % 1000 == 0)
+-			INFO("DEBUG: TRANSMIT (PCM_WRITE)\n");
+-	}
+-#endif
+-	if(down_read_trylock(&xbus->in_use)) {
+-		ret = xbus->ops->packet_send(xbus, pack_tx);
+-		XBUS_COUNTER(xbus, TX_BYTES) += pack_tx->datalen;
+-		up_read(&xbus->in_use);
+-	} else {
+-		DBG("Dropped packet. %s is in_use\n", xbus->busname);
+-	}
+-	return ret;
+-
+-error:	
+-	xbus->ops->packet_free(xbus, pack_tx);
+-	return ret;
+-}
+-
+-static void xbus_poll(xbus_t *xbus, int probe_all)
+-{
+-	int id;
+-	int ret;
+-	xpd_t **xpds;
+-	xpd_t *xpd;
+-
+-	DBG("%s (probe_all=%d)\n", xbus->busname, probe_all);
+-	xpds = xbus->xpds;
+-	for(id = 0; id < MAX_XPDS; id++) {
+-		if(!xbus->hardware_exists)
+-			break;
+-		xpd = xpd_of(xbus, id);
+-		if(!probe_all) {
+-			if(!xpd) {
+-				DBG("  Skipping XPD #%d is MISSING\n", id);
+-				continue;
+-			}
+-			if(!atomic_read(&xpd->card_present)) {
+-				DBG("  Skipping XPD #%d not present\n", id);
+-				continue;
+-			}
+-			if(time_after(xpd->last_response+20, jiffies)) {
+-				DBG("  SKIP DESC_REQ\n");
+-				continue;
+-			}
+-		}
+-		if(IS_SET(ignore_xpds, id)) {		/* skip xpds */
+-			DBG("  Ignoring XPD #%d\n", id);
+-			continue;
+-		}
+-		DBG("  Polling slot %d %s\n", id, xbus->busname);
+-		ret = CALL_PROTO(GLOBAL, DESC_REQ, xbus, NULL, id);
+-		if(ret < 0) {
+-			NOTICE("xpp: %s: Failed sending DESC_REQ to XPD #%d\n", __FUNCTION__, id);
+-		}
+-	}
+-}
+-
+-void process_xbus_poll(void *data) {
+-	xbus_t *xbus = (xbus_t*) data;
+-
+-	ERR("%s: issuing xbus_poll\n", __FUNCTION__);
+-	xbus_poll(xbus, 1);
+-}
+-
+-
+-#if SOFT_SIMULATOR
+-/*
+- * Assume xbus->lock is held
+- */
+-static void simulator_setup(xbus_t *xbus, ulong sim_xpds)
+-{
+-	int	i;
+-	int	last_fxo = 0;
+-	bool	first = 1;
+-
+-	DBG("%s: sim_xpds=0x%lX\n", xbus->busname, sim_xpds);
+-	for(i = 0; i < MAX_XPDS; i++) {
+-		if (!IS_SET(sim_xpds, i) || xbus->sim[i].simulated)
+-			continue;
+-		if (first) {
+-			last_fxo=i;
+-		} else {
+-			// setting simulated twice here: in case of odd number of simulated XPDs
+-			xbus->sim[i].xpd_type = XPD_TYPE_FXO;
+-			xbus->sim[i].loopto = last_fxo;
+-			xbus->sim[i].simulated = 1;
+-			xbus->sim[last_fxo].xpd_type = XPD_TYPE_FXS;
+-			xbus->sim[last_fxo].loopto = i;
+-			xbus->sim[last_fxo].simulated = 1;
+-			
+-		}
+-		if(IS_SET(softloop_xpds, i))
+-			xbus->sim[i].softloop_xpd = 1;
+-		xbus->sim[i].hookstate = 0;
+-		
+-		first = !first;
+-	}
+-}
+-#endif
+-
+-void xbus_activate(xbus_t *xbus)
+-{
+-	xbus_ops_t *ops;
+-
+-	BUG_ON(!xbus);
+-	ops = xbus->ops;
+-	BUG_ON(!ops);
+-	BUG_ON(!xbus->priv);
+-	/* Sanity checks */
+-	if(!ops->packet_send) {
+-		ERR("%s: missing mandatory handler: packet_send=\n", __FUNCTION__);
+-		return;
+-	}
+-	if(!ops->packet_new || !ops->packet_free) {
+-		ops->packet_new = softloop_packet_new;
+-		ops->packet_free = softloop_packet_free;
+-	}
+-
+-	xbus->hardware_exists = 1;
+-	DBG("Activating: %s\n", xbus->busname);
+-	/* Poll it */
+-	xbus_poll(xbus, 1);
+-}
+-
+-void xbus_deactivate(xbus_t *xbus)
+-{
+-	int	i;
+-
+-	BUG_ON(!xbus);
+-	DBG("%s\n", xbus->busname);
+-	xbus->hardware_exists = 0;
+-	for(i = 0; i < MAX_XPDS; i++) {
+-		xpd_t *xpd = xpd_of(xbus, i);
+-		if(!xpd)
+-			continue;
+-		if(xpd->id != i) {
+-			ERR("%s: BUG: xpd->id=%d != i=%d\n", __FUNCTION__, xpd->id, i);
+-			continue;
+-		}
+-		xpd_card_disable(xpd);
+-	}
+-	down_write(&xbus->in_use);
+-	DBG("%s (deactivated)\n", xbus->busname);
+-	if(xbus->open_counter == 0) {
+-		xbus_remove(xbus);
+-	}
+-}
+-
+-
+-static void xbus_cleanup(xbus_t *xbus)
+-{
+-	BUG_ON(!xbus);
+-#ifdef CONFIG_PROC_FS
+-	if(xbus->proc_xbus_dir) {
+-		if(xbus->proc_xbus_summary) {
+-			DBG("Removing proc '%s' for %s\n", PROC_XBUS_SUMMARY, xbus->busname);
+-			remove_proc_entry(PROC_XBUS_SUMMARY, xbus->proc_xbus_dir);
+-			xbus->proc_xbus_summary = NULL;
+-		}
+-		DBG("Removing proc directory %s\n", xbus->busname);
+-		remove_proc_entry(xbus->busname, xpp_procdir);
+-		xbus->proc_xbus_dir = NULL;
+-	}
+-#endif
+-	kfree(xbus);
+-}
+-
+-xbus_t *xbus_new(ulong loopback_xpds)
+-{
+-	unsigned long	flags;
+-	int		xbus_num;
+-	int		err;
+-	xbus_t		*xbus;
+-
+-	xbus = kmalloc(sizeof(xbus_t), GFP_KERNEL);
+-	if(!xbus)
+-		return NULL;
+-	memset(xbus, 0, sizeof(xbus_t));
+-
+-	spin_lock_irqsave(&xbuses_lock, flags);
+-	for(xbus_num = 0; xbus_num < MAX_BUSES; xbus_num++)
+-		if(xbuses_array[xbus_num] == NULL)
+-			break;
+-	if(xbus_num >= MAX_BUSES) {
+-		spin_unlock_irqrestore(&xbuses_lock, flags);
+-		err = -ENOMEM;
+-		goto nobus;
+-	}
+-	/* Found empty slot */
+-	xbuses_array[xbus_num] = xbus;
+-	bus_count++;
+-	spin_unlock_irqrestore(&xbuses_lock, flags);
++			xpd->xbus->busname, xpd->xpdname, (zt_reg) ? "register" : "unregister");
++	if(zt_reg)
++		ret = xpd_zaptel_register(xpd);
++	else
++		ret = xpd_zaptel_unregister(xpd);
++	return (ret < 0) ? ret : count;
++}
+ 
+-	/* Init data structures */
+-	spin_lock_init(&xbus->lock);
+-	snprintf(xbus->busname, XBUS_NAMELEN, "XBUS-%d", xbus_num);
+-	INFO("New xbus: %s\n", xbus->busname);
+-	init_waitqueue_head(&xbus->packet_cache_empty);
+-	atomic_set(&xbus->packet_counter, 0);
+-	init_rwsem(&xbus->in_use);
+-	xbus->num = xbus_num;
+-	xbus->num_xpds = 0;
+-#if SOFT_SIMULATOR
+-	xbus->sim_workqueue = create_singlethread_workqueue(xbus->busname);
+-	if(!xbus->sim_workqueue) {
+-		ERR("Failed to create workqueue for xbus %s\n", xbus->busname);
+-		err = -ENOMEM;
+-		goto nobus;
+-	}
+-	init_xbus_packet_queue(&xbus->sim_packet_queue, "SIM_PACKET_QUEUE");
+-	INIT_WORK(&xbus->sim_work, process_sim_queue, xbus);
+-	simulator_setup(xbus, loopback_xpds);	// Hardware loopback must use simulator
+-	simulator_setup(xbus, softloop_xpds);	// Add the soft loopback to the simulated set
+-#endif
+-	xbus_reset_counters(xbus);
+-#ifdef CONFIG_PROC_FS
+-	DBG("Creating xbus proc directory %s.\n",xbus->busname);
+-	xbus->proc_xbus_dir = proc_mkdir(xbus->busname, xpp_procdir);
+-	if(!xbus->proc_xbus_dir) {
+-		ERR("Failed to create proc directory for xbus %s\n", xbus->busname);
+-		err = -EIO;
+-		goto nobus;
+-	}
+-	xbus->proc_xbus_summary = create_proc_read_entry(PROC_XBUS_SUMMARY, 0444, xbus->proc_xbus_dir,
+-			xbus_read_proc, xbus);
+-	if (!xbus->proc_xbus_summary) {
+-		ERR("Failed to create '%s' proc file for xbus %s\n", PROC_XBUS_SUMMARY, xbus->busname);
+-		err = -EIO;
+-		goto nobus;
+-	}
+ #endif
+-	return xbus;
+-nobus:
+-	spin_lock_irqsave(&xbuses_lock, flags);
+-	xbuses_array[xbus_num] = NULL;
+-	bus_count--;
+-	spin_unlock_irqrestore(&xbuses_lock, flags);
+-	xbus_cleanup(xbus);
+-	return NULL;
+-}
+ 
+-static void xbus_remove(xbus_t *xbus)
++/**
++ *
++ * Packet is freed:
++ * 	- In case of error, by this function.
++ * 	- Otherwise, by the underlying sending mechanism
++ */
++int packet_send(xbus_t *xbus, xpacket_t *pack_tx)
+ {
+-	int i;
+-	unsigned long flags;
++	int		ret = -ENODEV;
++	int		toxpd;
+ 
+-	BUG_ON(!xbus);
+-	DBG("%s\n", xbus->busname);
+-	spin_lock_irqsave(&xbuses_lock, flags);
+-	BUG_ON(xbus != xbus_of(xbus->num));
+-	xbuses_array[xbus->num] = NULL;
+-	bus_count--;
+-	spin_unlock_irqrestore(&xbuses_lock, flags);
+-#if SOFT_SIMULATOR
+-	if(xbus->sim_workqueue) {
+-		cancel_delayed_work(&xbus->sim_work);
++	if(!pack_tx) {
++		DBG("null pack\n");
++		return -EINVAL;
+ 	}
+-#endif
+-	INFO("Removing xbus(%d) %s\n", xbus->num, xbus->busname);
+-	for(i = 0; i < MAX_XPDS; i++) {
+-		xpd_t *xpd = xpd_of(xbus, i);
+-
+-		if(xpd) {
+-			const xops_t	*xops = xpd->xops;
++	toxpd = XPD_NUM(pack_tx->content.addr);
++	if(!xbus) {
++		DBG("null xbus\n");
++		ret = -EINVAL;
++		goto error;
++	}
++	if (!xbus->hardware_exists) {
++		DBG("xbus %s Dropped a packet -- NO HARDWARE.", xbus->busname);
++		ret = -ENODEV;
++		goto error;
++	}
++	if(!VALID_XPD_NUM(toxpd)) {
++		ERR("%s: toxpd=%d > MAX_XPDS\n", __FUNCTION__, toxpd);
++		ret = -EINVAL;
++		goto error;
++	}
++#if 0
++	// DEBUG: For Dima
++	if(pack_tx->content.opcode == XPP_PCM_WRITE) {
++		static	int rate_limit;
++		static	int count;
+ 
+-			if(xpd->id != i) {
+-				ERR("%s: BUG: xpd->id=%d != i=%d\n", __FUNCTION__, xpd->id, i);
+-				continue;
+-			}
+-			BUG_ON(!xops);
+-			DBG("  Removing xpd id=%d\n", xpd->id);
+-			xpd_remove(xpd);
++		if(sync_master == NULL)
++			count = 0;
++		if(count++ > 5) {
++			ret = 0;
++			goto error;
+ 		}
+-		xbus->xpds[i] = NULL;
+-	}
+-#if SOFT_SIMULATOR
+-	if(xbus->sim_workqueue) {
+-		flush_workqueue(xbus->sim_workqueue);
+-		destroy_workqueue(xbus->sim_workqueue);
+-		xbus->sim_workqueue = NULL;
++		if(rate_limit++ % 1000 == 0)
++			INFO("DEBUG: TRANSMIT (PCM_WRITE)\n");
+ 	}
+-	drain_xbus_packet_queue(xbus, &xbus->sim_packet_queue);
+ #endif
+-	int ret = wait_event_interruptible(xbus->packet_cache_empty, 
+-			atomic_read(&xbus->packet_counter) == 0);
+-	if(ret) {
+-		ERR("waiting for packet_cache_empty interrupted!!!\n");
++	if(down_read_trylock(&xbus->in_use)) {
++		ret = xbus->ops->packet_send(xbus, pack_tx);
++		XBUS_COUNTER(xbus, TX_BYTES) += pack_tx->datalen;
++		up_read(&xbus->in_use);
++	} else {
++		DBG("Dropped packet. %s is in_use\n", xbus->busname);
+ 	}
+-	xbus_cleanup(xbus);
++	return ret;
++
++error:	
++	xbus->ops->packet_free(xbus, pack_tx);
++	return ret;
+ }
+ 
+ 
+@@ -1407,12 +868,12 @@
+ 	int	i;
+ 	int	channels = xpd->channels;
+ 	struct zt_chan	*chans = xpd->span.chans;
++	unsigned long	flags;
+ 
++	spin_lock_irqsave(&xpd->lock, flags);
+ //	if((xpd->timer_count % PREP_REPORT_RATE) < 10)
+ //		DBG("%d\n", xpd->timer_count);
+ 
+-//	if(xpd->hookstate == 0)
+-//		return;
+ 	if (xpd->timer_count & 1) {
+ 		/* First part */
+ 		w = writechunk = xpd->writechunk /* + 1 */;
+@@ -1422,18 +883,26 @@
+ 	zt_transmit(&xpd->span);
+ 
+ 	for (i = 0; i < channels; i++) {
+-		if(IS_SET(xpd->hookstate, i)) {
++		if (xpd->delay_until_dialtone[i] > 0) {
++			xpd->delay_until_dialtone[i]--;
++			if (xpd->delay_until_dialtone[i] <= 0) {
++				xpd->delay_until_dialtone[i] = 0;
++				wake_up_interruptible(&xpd->txstateq[i]);
++			}
++		}
++		if(IS_SET(xpd->hookstate, i) || IS_SET(xpd->cid_on, i)) {
+ 			memcpy((u_char *)w, chans[i].writechunk, ZT_CHUNKSIZE);
+ 			// fill_beep((u_char *)w, 5);
+ 		}
+ 		w += ZT_CHUNKSIZE;
+ 	}
+-	if(xpd->hookstate != 0 || sync_master == xpd || !sync_master) {
+-		ret = CALL_XMETHOD(PCM_WRITE, xpd->xbus, xpd, xpd->hookstate, writechunk);
++//	if(xpd->hookstate != 0 || sync_master != xpd) {
++		ret = CALL_XMETHOD(PCM_WRITE, xpd->xbus, xpd, xpd->hookstate | xpd->cid_on, writechunk);
+ 		if(ret < 0) {
+ 			DBG("failed to write PCM %d\n", ret);
+ 		}
+-	}
++//	}
++	spin_unlock_irqrestore(&xpd->lock, flags);
+ }
+ 
+ void fill_beep(u_char *buf, int duration)
+@@ -1446,20 +915,46 @@
+ 	static u_char beep[] = {
+ //		0x7F, 0xBE, 0xD8, 0xBE, 0x80, 0x41, 0x24, 0x41,	/* Dima */
+ //		0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67,	/* silence */
+-		0x67, 0x90, 0x89, 0x90, 0xFF, 0x10, 0x09, 0x10,	/* Izzy */
++//		0x67, 0x90, 0x89, 0x90, 0xFF, 0x10, 0x09, 0x10,	/* Izzy */
+ //		0x67, 0xCD, 0xC5, 0xCD, 0xFF, 0x49, 0x41, 0x49,	/* Dima 2 */
+-//		0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,	/* silence */
++		0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,	/* silence */
+ 	};
+ 	memcpy(buf, &beep[(which*8) % ARRAY_SIZE(beep)], ZT_CHUNKSIZE);
+ }
+ 
++#ifdef	XPP_EC_CHUNK
++/*
++ * Taken from zaptel.c
++ */
++static inline void xpp_ec_chunk(struct zt_chan *chan, unsigned char *rxchunk, const unsigned char *txchunk)
++{
++	short rxlin;
++	int x;
++	unsigned long flags;
++
++	/* Perform echo cancellation on a chunk if necessary */
++	if (!chan->ec)
++		return;
++	spin_lock_irqsave(&chan->lock, flags);
++	for (x=0;x<ZT_CHUNKSIZE;x++) {
++		rxlin = ZT_XLAW(rxchunk[x], chan);
++		rxlin = xpp_echo_can_update(chan->ec, ZT_XLAW(txchunk[x], chan), rxlin);
++		rxchunk[x] = ZT_LIN2X((int)rxlin, chan);
++	}
++	spin_unlock_irqrestore(&chan->lock, flags);
++}
++#endif
++
++
+ static void xpp_receiveprep(xpd_t *xpd)
+ {
+ 	volatile u_char *readchunk;
+ 	int i;
+ 	int	channels = xpd->channels;
+ 	struct zt_chan	*chans = xpd->span.chans;
++	unsigned long	flags;
+ 
++	spin_lock_irqsave(&xpd->lock, flags);
+ //	if((xpd->timer_count % PREP_REPORT_RATE) == 0)
+ //		DBG("%d\n", xpd->timer_count);
+ 
+@@ -1472,20 +967,29 @@
+ 
+ 	for (i = 0; i < channels; i++) {
+ 		if(IS_SET(xpd->hookstate, i)) {
++			// memset((u_char *)readchunk, 0x5A, ZT_CHUNKSIZE);	// DEBUG
++			// fill_beep((u_char *)readchunk, 1);	// DEBUG: BEEP
+ 			memcpy(chans[i].readchunk, (u_char *)readchunk, ZT_CHUNKSIZE);
++		} else {
++			memset(chans[i].readchunk, 0x7F, ZT_CHUNKSIZE);	// SILENCE
+ 		}
+ 		readchunk += ZT_CHUNKSIZE;
+ 	}
+ 
+-#if 0
++#if WITH_ECHO_SUPPRESSION
+ 	/* FIXME: need to Echo cancel double buffered data */
+ 	for (i = 0;i < xpd->span.channels; i++) {
++#ifdef XPP_EC_CHUNK
++		xpp_ec_chunk(&chans[i], chans[i].readchunk, xpd->ec_chunk2[i]);
++#else
+ 		zt_ec_chunk(&chans[i], chans[i].readchunk, xpd->ec_chunk2[i]);
++#endif
+ 		memcpy(xpd->ec_chunk2[i], xpd->ec_chunk1[i], ZT_CHUNKSIZE);
+ 		memcpy(xpd->ec_chunk1[i], chans[i].writechunk, ZT_CHUNKSIZE);
+ 	}
+ #endif
+ 	zt_receive(&xpd->span);
++	spin_unlock_irqrestore(&xpd->lock, flags);
+ }
+ 
+ static int xpp_startup(struct zt_span *span)
+@@ -1540,13 +1044,18 @@
+ 	spin_lock_irqsave(&xbus->lock, flags);
+ 	xbus->open_counter--;
+ 	atomic_dec(&xpd->open_counter);
++	if(xpd->direction == TO_PHONE) {	/* Hangup phone */
++		xpd->idletxhookstate[chan->chanpos - 1] = FXS_LINE_ENABLED;
++	}
+ 	if (!xbus->hardware_exists && xbus->open_counter == 0)
+ 		should_remove = 1;
+ 	spin_unlock_irqrestore(&xbus->lock, flags);
+ 
+ 	DBG("chan=%d (open_counter=%d, should_remove=%d)\n", chan->chanpos, xbus->open_counter, should_remove);
+-	if(should_remove)
++	if(should_remove) {
++		DBG("Going to remove: %s\n", xbus->busname);
+ 		xbus_remove(xbus);
++	}
+ 	return 0;
+ }
+ 
+@@ -1560,10 +1069,11 @@
+ 		case ZT_ONHOOKTRANSFER:
+ 			if (get_user(x, (int *)arg))
+ 				return -EFAULT;
+-			if (xpd->lasttxhook[pos] == 0x1) {
++			xpd->ohttimer[pos] = x << 3;
++			xpd->idletxhookstate[pos] = FXS_LINE_CID;	/* OHT mode when idle */
++			if (xpd->lasttxhook[pos] == FXS_LINE_ENABLED) {
+ 				/* Apply the change if appropriate */
+-				xpd->lasttxhook[pos] = 0x2;
+-				// CALL_XMETHOD(CHAN_CID, xpd->xbus, xpd, BIT(pos));		// CALLER ID
++				CALL_XMETHOD(CHAN_CID, xpd->xbus, xpd, pos);		// CALLER ID
+ 			}
+ 			DBG("xpd=%d: ZT_ONHOOKTRANSFER (%d millis) chan=%d\n", xpd->id, x, pos);
+ 			return -ENOTTY;
+@@ -1590,109 +1100,15 @@
+ 	xpd_t	*xpd = chan->pvt;
+ 	xbus_t	*xbus;
+ 	int pos = chan->chanpos - 1;
+-	int ret = 0;
+-	
+-	if(!xpd) {
+-		ERR("%s: channel=%d without an XPD!\n", __FUNCTION__, pos);
+-		return -EINVAL;
+-	}
+-	xbus = xpd->xbus;
+ 
+-	if (txsig == ZT_TXSIG_START) {
+-		if(xpd->direction == TO_PHONE) {
+-			// A PHONE line: ZT_START will be treated as ZT_RING
+-			DBG("Got ZT_START for PHONE channel %d, treated as ZT_RING\n", pos);
+-			//hookstate = ZT_TXSIG_RING;
+-
+-			if(IS_SET(xpd->digital_inputs, pos)) {
+-				NOTICE("%s: Trying to RING a digital input channel %d. Ignoring\n", __FUNCTION__, pos);
+-				return -EINVAL;
+-			}
+-			if(IS_SET(xpd->digital_outputs, pos)) {
+-				DBG("ZT_RING %s digital output ON\n", chan->name);
+-				ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 1);
+-				return ret;
+-			}
+-			xpd->ringing[pos] = RINGS_NUM*2;
+-			DBG("ZT_RING %s ringing=%d\n", chan->name, xpd->ringing[pos]);
+-			ret = CALL_XMETHOD(RING, xbus, xpd, pos, 1);			// RING on
+-			if(ret) {
+-				DBG("ZT_RING Failed: ret=0x%02X\n", ret);
+-				return ret;
+-			}
+-			return ret;
+-		} else {	/* TO_PSTN */
+-			// An FXO line: ZT_START will be treated as ZT_OFFHOOK
+-			DBG("Got ZT_START for FXO channel %d, treated as ZT_OFFHOOK\n", pos);
+-			txsig = ZT_TXSIG_OFFHOOK;
+-		}
+-	}
+-	switch(txsig) {
+-		case ZT_TXSIG_START:
+-			DBG("ZT_TXSIG_START: (doing OFFHOOK) %s (chan->dialing=%d)\n", chan->name, chan->dialing);
+-			break;
+-			/* Fall through */
+-		case ZT_TXSIG_OFFHOOK:
+-			DBG("ZT_TXSIG_OFFHOOK: %s hookstate=0x%04X\n", chan->name, xpd->hookstate);
+-			BIT_SET(xpd->hookstate, pos);
+-			xpd->ringing[pos] = 0;
+-			if(IS_SET(xpd->digital_inputs, pos)) {
+-				NOTICE("%s: Trying to OFFHOOK a digital input channel %d. Ignoring\n", __FUNCTION__, pos);
+-				return -EINVAL;
+-			}
+-			if(IS_SET(xpd->digital_outputs, pos)) {
+-				DBG("ZT_TXSIG_OFFHOOK %s digital output OFF\n", chan->name);
+-				ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 0);
+-				return ret;
+-			}
+-			ret = CALL_XMETHOD(SETHOOK, xbus, xpd->id, xpd->hookstate);
+-			if(ret) {
+-				DBG("ZT_TXSIG_OFFHOOK Failed: ret=0x%02X\n", ret);
+-				break;
+-			}
+-			CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(i), LED_GREEN, 0);
+-			break;
+-			//DBG("ZT_TXSIG_OFFHOOK %d\n", pos);
+-			//BIT_SET(xpd->hookstate, pos);
+-			//break;
+-		case ZT_TXSIG_KEWL:
+-			DBG("ZT_TXSIG_KEWL (doing ONHOOK): %d.\n", pos);
+-			break;
+-			/* Fall through */
+-		case ZT_TXSIG_ONHOOK:
+-			DBG("ZT_TXSIG_ONHOOK: %s hookstate=0x%04X\n", chan->name, xpd->hookstate);
+-			xpd->ringing[pos] = 0;
+-			if(IS_SET(xpd->digital_inputs, pos)) {
+-				NOTICE("%s: Trying to ONHOOK a digital input channel %d. Ignoring\n", __FUNCTION__, pos);
+-				return -EINVAL;
+-			}
+-			if(IS_SET(xpd->digital_outputs, pos)) {
+-				DBG("ZT_TXSIG_ONHOOK %s digital output OFF\n", chan->name);
+-				ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 0);
+-				return ret;
+-			}
+-			BIT_CLR(xpd->hookstate, pos);
+-			ret = CALL_XMETHOD(SETHOOK, xbus, xpd->id, xpd->hookstate);
+-			if(ret) {
+-				DBG("ZT_ONHOOK Failed: ret=0x%02X\n", ret);
+-				break;
+-			}
+-			CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(i), LED_GREEN, 0);
+-			break;
+-			//DBG("ZT_TXSIG_ONHOOK: %d\n", pos);
+-			//BIT_CLR(xpd->hookstate, pos);
+-			//break;
+-		default:
+-			DBG("hooksig: unkown txsig=%d on channel %d\n", txsig, pos);
+-			return -EINVAL;
+-	}
+-	//ret = CALL_XMETHOD(SETHOOK, xbus, xpd->id, xpd->hookstate);
+-	//if(ret) {
+-	//	DBG("ZT_TXSIG_START Failed: ret=0x%02X\n", ret);
+-	//}
+-	return ret;
++	BUG_ON(!xpd);
++	xbus = xpd->xbus;
++	BUG_ON(!xbus);
++	DBG("Setting %s to %s (%d)\n", chan->name, txsig2str(txsig), txsig);
++	return CALL_XMETHOD(card_hooksig, xbus, xpd, pos, txsig);
+ }
+-#endif
++
++#else
+ 
+ static int xpp_sethook(struct zt_chan *chan, int hookstate)
+ {
+@@ -1701,102 +1117,15 @@
+ 	xbus_t	*xbus;
+ 	int ret = 0;
+ 
+-	if(!xpd) {
+-		ERR("%s: channel=%d without an XPD!\n", __FUNCTION__, pos);
+-		return -EINVAL;
+-	}
++	BUG_ON(!xpd);
+ 	xbus = xpd->xbus;
+ 	DBG("%s (%d) (old=0x%04X, hook-command=%d)\n", chan->name, pos, xpd->hookstate, hookstate);
+-	switch(hookstate) {
+-		/* On-hook, off-hook: The PBX is playing a phone on an FXO line. 
+-		 * Can be ignored for an FXS line
+-		 */
+-		case ZT_ONHOOK:
+-			if(IS_SET(xpd->digital_inputs, pos)) {
+-				NOTICE("%s: Trying to ONHOOK a digital input channel %d. Ignoring\n", __FUNCTION__, pos);
+-				return -EINVAL;
+-			}
+-			if(IS_SET(xpd->digital_outputs, pos)) {
+-				DBG("ZT_ONHOOK %s digital output OFF\n", chan->name);
+-				ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 0);
+-				return ret;
+-			}
+-			if(xpd->direction == TO_PHONE) {	/* Stop ring */
+-				DBG("ZT_ONHOOK: %s hookstate=0x%04X (stop ringing pos=%d)\n", chan->name, xpd->hookstate, pos);
+-				xpd->ringing[pos] = 0;
+-#if 1	// FIXME: Not needed -- verify
+-				ret = CALL_XMETHOD(RING, xbus, xpd, pos, 0);			// RING off
+-#endif
+-				xpd->lasttxhook[pos] = 1;
+-				ret = CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(pos), LED_GREEN, 0);
+-				ret = CALL_XMETHOD(CHAN_POWER, xbus, xpd, BIT(pos), 0);		// Power down (prevent overheating!!!)
+-				if(ret) {
+-					DBG("ZT_ONHOOK(stop ring) Failed: ret=0x%02X\n", ret);
+-					break;
+-				}
+-			} else {
+-				DBG("ZT_ONHOOK: %s hookstate=0x%04X (pos=%d)\n", chan->name, xpd->hookstate, pos);
+-				xpd->ringing[pos] = 0;
+-				BIT_CLR(xpd->hookstate, pos);
+-				ret = CALL_XMETHOD(SETHOOK, xbus, xpd, xpd->hookstate);
+-				if(ret) {
+-					DBG("ZT_ONHOOK Failed: ret=0x%02X\n", ret);
+-					break;
+-				}
+-			}
+-			break;
+-		case ZT_START:
+-			DBG("ZT_START: %s hookstate=0x%04X (fall through ZT_OFFHOOK)\n", chan->name, xpd->hookstate);
+-			// Fall through
+-		case ZT_OFFHOOK:
+-			if(xpd->direction == TO_PHONE) {
+-				DBG("ZT_OFFHOOK: %s hookstate=0x%04X -- ignoring (PHONE)\n", chan->name, xpd->hookstate);
+-				break;
+-			}
+-			DBG("ZT_OFFHOOK: %s hookstate=0x%04X (pos=%d)\n", chan->name, xpd->hookstate, pos);
+-			BIT_SET(xpd->hookstate, pos);
+-			xpd->ringing[pos] = 0;
+-			ret = CALL_XMETHOD(SETHOOK, xbus, xpd, xpd->hookstate);
+-			if(ret) {
+-				DBG("ZT_OFFHOOK Failed: ret=0x%02X\n", ret);
+-				break;
+-			}
+-			break;
+-		case ZT_WINK:
+-			DBG("ZT_WINK %s\n", chan->name);
+-			break;
+-		case ZT_FLASH:
+-			DBG("ZT_FLASH %s\n", chan->name);
+-			break;
+-		case ZT_RING:
+-			DBG("ZT_RING %s pos=%d (ringing[pos]=%d)\n", chan->name, pos, xpd->ringing[pos]);
+-			if(xpd->direction == TO_PHONE) {
+-				if(IS_SET(xpd->digital_inputs, pos)) {
+-					NOTICE("%s: Trying to RING a digital input channel %d. Ignoring\n", __FUNCTION__, pos);
+-					return -EINVAL;
+-				}
+-				if(IS_SET(xpd->digital_outputs, pos)) {
+-					DBG("ZT_ONHOOK %s digital output ON\n", chan->name);
+-					ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 1);
+-					return ret;
+-				}
+-				xpd->ringing[pos] = RINGS_NUM*2;
+-				ret = CALL_XMETHOD(CHAN_POWER, xbus, xpd, BIT(pos), 1);	// Power up (for ring)
+-				ret = CALL_XMETHOD(RING, xbus, xpd, pos, 1);			// RING on
+-				if(ret) {
+-					DBG("ZT_RING Failed: ret=0x%02X\n", ret);
+-				}
+-			}
+-			break;
+-		case ZT_RINGOFF:
+-			DBG("ZT_RINGOFF %s\n", chan->name);
+-			break;
+-		default:
+-			DBG("UNKNOWN hookstate=0x%X\n", hookstate);
+-	}
++	ret = CALL_XMETHOD(card_sethook, xpd->xbus, xpd, pos, hookstate);
+ 	return ret;
+ }
+ 
++#endif
++
+ /* Req: Set the requested chunk size.  This is the unit in which you must
+    report results for conferencing, etc */
+ int xpp_setchunksize(struct zt_span *span, int chunksize);
+@@ -1815,7 +1144,6 @@
+ 	switch(cmd) {
+ 		case ZT_MAINT_NONE:
+ 			printk("XXX Turn off local and remote loops XXX\n");
+-			CALL_XMETHOD(LED, xpd->xbus, xpd, xpd->enabled_chans, LED_RED, 0);		// FIXME: Find usage for extra LED
+ 			break;
+ 		case ZT_MAINT_LOCALLOOP:
+ 			printk("XXX Turn on local loopback XXX\n");
+@@ -1825,12 +1153,10 @@
+ 			break;
+ 		case ZT_MAINT_LOOPUP:
+ 			printk("XXX Send loopup code XXX\n");
+-			CALL_XMETHOD(LED, xpd->xbus, xpd, xpd->enabled_chans, LED_RED, 1);		// FIXME: Find usage for extra LED
+ 			// CALL_XMETHOD(LOOPBACK_AX, xpd->xbus, xpd, loopback_data, ARRAY_SIZE(loopback_data));
+ 			break;
+ 		case ZT_MAINT_LOOPDOWN:
+ 			printk("XXX Send loopdown code XXX\n");
+-			CALL_XMETHOD(LED, xpd->xbus, xpd, xpd->enabled_chans, LED_RED, 0);		// FIXME: Find usage for extra LED
+ 			break;
+ 		case ZT_MAINT_LOOPSTOP:
+ 			printk("XXX Stop sending loop codes XXX\n");
+@@ -1848,8 +1174,7 @@
+ /* Set signalling type (if appropriate) */
+ static int xpp_chanconfig(struct zt_chan *chan, int sigtype)
+ {
+-	DBG("channel %d (%s), sigtype %d.\n", chan->channo, chan->name, sigtype);
+-	dump_sigtype(print_dbg, "  ", sigtype);
++	DBG("channel %d (%s) -> %s\n", chan->channo, chan->name, sig2str(sigtype));
+ 	// FIXME: sanity checks:
+ 	// - should be supported (within the sigcap)
+ 	// - should not replace fxs <->fxo ??? (covered by previous?)
+@@ -1878,6 +1203,29 @@
+ int (*sethook)(struct zt_chan *chan, int hookstate);
+ #endif
+ 
++#ifdef	XPP_EC_CHUNK
++static int xpp_echocan(struct zt_chan *chan, int len)
++{
++	if(len == 0) {	/* shut down */
++		/* zaptel calls this also during channel initialization */
++		if(chan->ec) {
++			xpp_echo_can_free(chan->ec);
++		}
++		return 0;
++	}
++	if(chan->ec) {
++		ERR("%s: Trying to override an existing EC (%p)\n", __FUNCTION__, chan->ec);
++		return -EINVAL;
++	}
++	chan->ec = xpp_echo_can_create(len, 0);
++	if(!chan->ec) {
++		ERR("%s: Failed creating xpp EC (len=%d)\n", __FUNCTION__, len);
++		return -EINVAL;
++	}
++	return 0;
++}
++#endif
++
+ #ifdef	CONFIG_ZAPTEL_WATCHDOG
+ /*
+  * If the watchdog detects no received data, it will call the
+@@ -1893,6 +1241,13 @@
+ }
+ #endif
+ 
++/* FIXME: this should be removed once digium patch their zaptel.h
++ * I simply wish to avoid changing zaptel.h in the xpp patches.
++ */
++#ifndef ZT_EVENT_REMOVED
++#define ZT_EVENT_REMOVED (20)
++#endif
++
+ /**
+  * Unregister an xpd from zaptel and release related resources
+  * @xpd The xpd to be unregistered
+@@ -1906,20 +1261,33 @@
+  */
+ static int xpd_zaptel_unregister(xpd_t *xpd)
+ {
++	unsigned long	flags;
++
+ 	BUG_ON(!xpd);
++	spin_lock_irqsave(&xpd->lock, flags);
+ 
+ 	if(!SPAN_REGISTERED(xpd)) {
+ 		NOTICE("%s: %s is already unregistered\n", __FUNCTION__, xpd->xpdname);
++		spin_unlock_irqrestore(&xpd->lock, flags);
+ 		return -EIDRM;
+ 	}
+ 	if(sync_master == xpd)
+-		set_sync_master(NULL);			// FIXME: it's better to elect a new prince
++		sync_master_is(NULL);			// FIXME: it's better to elect a new prince
+ 	update_xpd_status(xpd, ZT_ALARM_NOTOPEN);
+ 	if(atomic_read(&xpd->open_counter)) {
++		int i;
+ 		NOTICE("%s: %s is busy (open_counter=%d). Skipping.\n", __FUNCTION__, xpd->xpdname, atomic_read(&xpd->open_counter));
++		spin_unlock_irqrestore(&xpd->lock, flags);
++		/* TODO: Should this be done before releasing the spinlock? */
++		DBG("Queuing ZT_EVENT_REMOVED on all channels to ask user to release them\n");
++		for (i=0; i<xpd->span.channels; i++)
++			zt_qevent_lock(&xpd->chans[i],ZT_EVENT_REMOVED);
+ 		return -EBUSY;
+ 	}
+ 	mdelay(2);	// FIXME: This is to give chance for transmit/receiveprep to finish.
++	spin_unlock_irqrestore(&xpd->lock, flags);
++	if(xpd->card_present)
++		xpd->xops->card_zaptel_registration(xpd, 0);
+ 	zt_unregister(&xpd->span);
+ 	return 0;
+ }
+@@ -1952,33 +1320,20 @@
+ 	xbus = xpd->xbus;
+ 	snprintf(span->name, MAX_SPANNAME, "%s/%s",
+ 			xbus->busname, xpd->xpdname);
+-	{
+-		char tmp[MAX_SPANNAME];
+-#if SOFT_SIMULATOR
+-		struct xpd_sim	*sim = &xbus->sim[xpd->id];
+-
+-		if(sim->simulated)
+-			snprintf(tmp, MAX_SPANNAME, " (sim to=%d)", sim->loopto);
+-		else
+-#endif
+-			tmp[0] = '\0';
+-
+-		snprintf(span->desc, MAX_SPANDESC, "Xorcom XPD #%d/%d: %s%s",
++		snprintf(span->desc, MAX_SPANDESC, "Xorcom XPD #%d/%d: %s",
+ 				xbus->num, xpd->id,
+-				(xpd->direction == TO_PHONE) ? "FXS" : "FXO",
+-				tmp
++				(xpd->direction == TO_PHONE) ? "FXS" : "FXO"
+ 				);
+-	}
+ 	for(i = 0; i < cn; i++) {
+ 		
+ 		cur_chan = &xpd->chans[i];
+ 		DBG("setting channel %d (sigfxs=%d)\n", i, sigfxs);
+ 		if(IS_SET(xpd->digital_outputs, i)) {
+-			snprintf(cur_chan->name, MAX_CHANNAME, "XPP_OUT/%d-%d", xpd->id, i);
++			snprintf(cur_chan->name, MAX_CHANNAME, "XPP_OUT/%d/%d/%d", xbus->num, xpd->id, i);
+ 		} else if(IS_SET(xpd->digital_inputs, i)) {
+-			snprintf(cur_chan->name, MAX_CHANNAME, "XPP_IN/%d-%d", xpd->id, i);
++			snprintf(cur_chan->name, MAX_CHANNAME, "XPP_IN/%d/%d/%d", xbus->num, xpd->id, i);
+ 		} else {
+-			snprintf(cur_chan->name, MAX_CHANNAME, "XPP_%s/%d-%d", (sigfxs) ? "FXO" : "FXS", xpd->id, i);
++			snprintf(cur_chan->name, MAX_CHANNAME, "XPP_%s/%d/%d/%d", (sigfxs) ? "FXO" : "FXS", xbus->num, xpd->id, i);
+ 		}
+ 		cur_chan->chanpos = i + 1;
+ 		cur_chan->pvt = xpd;
+@@ -2023,6 +1378,9 @@
+ #endif
+ 	span->ioctl = xpp_ioctl;
+ 	span->maint = xpp_maint;
++#ifdef	XPP_EC_CHUNK
++	span->echocan = xpp_echocan;
++#endif
+ #ifdef	CONFIG_ZAPTEL_WATCHDOG
+ 	span->watchdog = xpp_watchdog;
+ #endif
+@@ -2038,52 +1396,14 @@
+ 		xbus->num_xpds--;
+ 		return -ENODEV;
+ 	}
+-//	if(xpd->id == 0)
+-//		set_sync_master(xpd);
+-
++	xpd->xops->card_zaptel_registration(xpd, 1);
+ 	return 0;
+ }
+ 
+-
+ /*------------------------- Proc debugging interface ---------------*/
+ 
+ #ifdef CONFIG_PROC_FS
+ 
+-static int xpp_zap_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
+-{
+-	int len = 0;
+-	unsigned long flags;
+-	int i;
+-
+-	spin_lock_irqsave(&xbuses_lock, flags);
+-	for(i = 0; i < MAX_BUSES; i++) {
+-		xbus_t *xbus = xbus_of(i);
+-
+-		if(xbus) {
+-			len += sprintf(page + len, "%s: CONNECTOR=%s STATUS=%s bus_type=%d\n",
+-					xbus->busname,
+-					xbus->busdesc,
+-					(xbus->hardware_exists) ? "connected" : "missing",
+-					xbus->bus_type
+-				      );
+-		}
+-	}
+-#if 0
+-	len += sprintf(page + len, "<-- len=%d\n", len);
+-#endif
+-	spin_unlock_irqrestore(&xbuses_lock, flags);
+-	if (len <= off+count)
+-		*eof = 1;
+-	*start = page + off;
+-	len -= off;
+-	if (len > count)
+-		len = count;
+-	if (len < 0)
+-		len = 0;
+-	return len;
+-
+-}
+-
+ #if 0
+ static int xpp_zap_write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data)
+ {
+@@ -2097,6 +1417,7 @@
+ #define	MINOR_XBUS_NUM(m)	((m) >> 4)
+ #define	MINOR_XPD_NUM(m)	((m) & 0xF);
+ 
++#if 0
+ static int xpp_sys_open (struct inode * inode, struct file * file)
+ {
+ 	xbus_t *xbus;
+@@ -2186,6 +1507,7 @@
+ 	.release	= xpp_sys_release,
+ };
+ 
++#endif
+ 
+ /*------------------------- Initialization -------------------------*/
+ 
+@@ -2193,42 +1515,52 @@
+ {
+ 	if(timer_pending(&xpp_timer))
+ 		del_timer_sync(&xpp_timer);
+-	unregister_chrdev(XPP_CTL_MAJOR, THIS_MODULE->name);
+-#ifdef CONFIG_PROC_FS
+-	remove_proc_entry(PROC_SYNC, xpp_procdir);
+-	remove_proc_entry(PROC_XBUSES, xpp_procdir);
+-	if(xpp_procdir) {
+-		remove_proc_entry(PROC_DIR, NULL);
+-	}
+-#endif
+ 	if (xpp_worker) {
+ 		flush_workqueue(xpp_worker);
+ 		destroy_workqueue(xpp_worker);
+ 		xpp_worker = NULL;
+ 	}
+-	kmem_cache_destroy(packet_cache);
++#if 0
++	unregister_chrdev(XPP_CTL_MAJOR, THIS_MODULE->name);
++#endif
++#ifdef CONFIG_PROC_FS
++	remove_proc_entry(PROC_SYNC, xpp_proc_toplevel);
++	if(xpp_proc_toplevel) {
++		remove_proc_entry(PROC_DIR, NULL);
++	}
++#endif
+ }
+ 
+ int __init xpp_zap_init(void)
+ {
+-	INFO("%s revision %s\n", THIS_MODULE->name, revision);
++	int	ret;
++	struct proc_dir_entry *ent;
++
++	INFO("%s revision %s\n", THIS_MODULE->name, ZAPTEL_VERSION);
++#ifdef WITH_RBS
++	INFO("FEATURE: %s (RBS signalling)\n", THIS_MODULE->name);
++#else
++	INFO("FEATURE: %s (NO RBS signalling)\n", THIS_MODULE->name);
++#endif
++#if WITH_ECHO_SUPPRESSION
++	INFO("FEATURE: %s (with ECHO_SUPPRESSION)\n", THIS_MODULE->name);
++#else
++	INFO("FEATURE: %s (without ECHO_SUPPRESSION)\n", THIS_MODULE->name);
++#endif
++#ifdef XPP_EC_CHUNK
++	INFO("FEATURE: %s (with XPP_EC_CHUNK)\n", THIS_MODULE->name);
++#else
++	INFO("FEATURE: %s (without XPP_EC_CHUNK)\n", THIS_MODULE->name);
++#endif
+ 
+-	packet_cache = kmem_cache_create("xpp_packets",
+-			sizeof(xpacket_t),
+-			0, 0,
+-			NULL, NULL);
+-	if(!packet_cache) {
+-		return -ENOMEM;
+-	}
+ #ifdef CONFIG_PROC_FS
+-	xpp_procdir = proc_mkdir(PROC_DIR, NULL);
+-	if(!xpp_procdir) {
++	xpp_proc_toplevel = proc_mkdir(PROC_DIR, NULL);
++	if(!xpp_proc_toplevel) {
+ 		do_cleanup();
+ 		return -EIO;
+ 	}
+-	struct proc_dir_entry *ent;
+ 
+-	ent = create_proc_entry(PROC_SYNC, 0644, xpp_procdir);
++	ent = create_proc_entry(PROC_SYNC, 0644, xpp_proc_toplevel);
+ 	if(!ent) {
+ 		do_cleanup();
+ 		return -EFAULT;
+@@ -2236,11 +1568,6 @@
+ 	ent->read_proc = proc_sync_read;
+ 	ent->write_proc = proc_sync_write;
+ 	ent->data = NULL;
+-	ent = create_proc_read_entry(PROC_XBUSES, 0444, xpp_procdir, xpp_zap_read_proc, 0);
+-	if (!ent) {
+-		do_cleanup();
+-		return -EFAULT;
+-	}
+ #endif
+ 	xpp_worker = create_singlethread_workqueue("xppworker");
+ 	if(!xpp_worker) {
+@@ -2248,55 +1575,40 @@
+ 		do_cleanup();
+ 		return -ENOMEM;
+ 	}
++	ret = xbus_core_init();
++	if(ret) {
++		ERR("xbus_core_init failed (%d)\n", ret);
++		do_cleanup();
++		return ret;
++	}
+ 	
++#if 0
+ 	if (register_chrdev(XPP_CTL_MAJOR, THIS_MODULE->name, &xpp_fops)) {
+ 		printk (KERN_WARNING "%s: unable to get major %d\n", THIS_MODULE->name, XPP_CTL_MAJOR);
+ 		do_cleanup();
+ 		return -EIO;
+ 	}
++#endif
+ 
+ 	/* Only timer init. We add it only *after* zt_register */
+ 	init_timer(&xpp_timer);
+-	set_sync_master(NULL);			/* Internal ticking */
++	sync_master_is(NULL);			/* Internal ticking */
+ 	return 0;
+ }
+ 
+ void __exit xpp_zap_cleanup(void)
+ {
+-//	unsigned long	flags;
+-	int		i;
+-
+-	for(i = 0; i < MAX_BUSES; i++) {
+-		xbus_t	*xbus = xbus_of(i);
+-		if(!xbus)
+-			continue;
+-		xbus_remove(xbus);
+-	}
+-//	spin_lock_irqsave(&xbuses_lock, flags);
+-	if(bus_count) {
+-		ERR("%s: bus_count=%d!\n", __FUNCTION__, bus_count);
+-	}
+-//	spin_unlock_irqrestore(&xbuses_lock, flags);
++	xbus_core_shutdown();
+ 	do_cleanup();
+ }
+ 
+ EXPORT_SYMBOL(print_dbg);
+ EXPORT_SYMBOL(card_detected);
+ EXPORT_SYMBOL(xpd_alloc);
+-EXPORT_SYMBOL(xbus_activate);
+-EXPORT_SYMBOL(xbus_deactivate);
+-EXPORT_SYMBOL(xpd_of);
+-EXPORT_SYMBOL(xbus_new);
+-EXPORT_SYMBOL(xbus_remove);
+-EXPORT_SYMBOL(xbus_reset_counters);
++EXPORT_SYMBOL(xpd_disconnect);
+ EXPORT_SYMBOL(packet_send);
++EXPORT_SYMBOL(update_xpd_status);
+ EXPORT_SYMBOL(fill_beep);
+-EXPORT_SYMBOL(xbus_enqueue_packet);
+-EXPORT_SYMBOL(xbus_dequeue_packet);
+-EXPORT_SYMBOL(init_xbus_packet_queue);
+-EXPORT_SYMBOL(drain_xbus_packet_queue);
+-EXPORT_SYMBOL(phone_hook);
+-EXPORT_SYMBOL(xpp_check_hookstate);
+ EXPORT_SYMBOL(xpp_tick);
+ EXPORT_SYMBOL(xpp_open);
+ EXPORT_SYMBOL(xpp_close);
+@@ -2306,7 +1618,7 @@
+ MODULE_DESCRIPTION("XPP Zaptel Driver");
+ MODULE_AUTHOR("Oron Peled <oron at actcom.co.il>");
+ MODULE_LICENSE("GPL");
+-MODULE_VERSION("$Id: xpp_zap.c 995 2006-04-03 07:08:13Z tzafrir $");
++MODULE_VERSION(ZAPTEL_VERSION);
+ 
+ module_init(xpp_zap_init);
+ module_exit(xpp_zap_cleanup);
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xpp_zap.h zaptel-xpp-XJ8JZx_dist/xpp/xpp_zap.h
+--- zaptel-1.2.6/xpp/xpp_zap.h	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/xpp_zap.h	2006-03-30 12:42:46.619481000 +0200
+@@ -1,37 +1,44 @@
+ #ifndef	XPP_ZAP_H
+ #define	XPP_ZAP_H
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2006, Xorcom
++ *
++ * 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/workqueue.h>
+ #include "xpd.h"
+ #include "xproto.h"
+ 
+-xpacket_t *xpacket_new(xbus_t *xbus, int flags);
+-void xpacket_free(xbus_t *xbus, xpacket_t *p);
+-
+-/* packet queues */
+-void init_xbus_packet_queue(packet_queue_t *q, const char name[]);
+-void drain_xbus_packet_queue(xbus_t *xbus, packet_queue_t *q);
+-void xbus_enqueue_packet(xbus_t *xbus, packet_queue_t *q, xpacket_t *pack);
+-xpacket_t *xbus_dequeue_packet(packet_queue_t *q);
+-
+-xbus_t *xbus_new(ulong loopback_xpds);
+-void xbus_activate(xbus_t *xbus);
+-void xbus_deactivate(xbus_t *xbus);
+-
+-void xbus_reset_counters(xbus_t *xbus);
++void xpd_disconnect(xpd_t *xpd);
+ int packet_send(xbus_t *xbus, xpacket_t *pack_tx);
+-void phone_hook(xpd_t *xpd, int channo, bool offhook);
+-void xpp_check_hookstate(xpd_t *xpd, xpp_line_t fxs_off_hook);
+-xpd_t *xpd_of(xbus_t *xbus, int xpd_num);
+ void card_detected(void *data);
+ xpd_t *xpd_alloc(size_t privsize, xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, int channels, byte revision);
+ void xpd_remove(xpd_t *xpd);
++void update_xpd_status(xpd_t *xpd, int alarm_flag);
+ void fill_beep(u_char *buf, int duration);
+ void xpp_tick(unsigned long param);
+ int xpp_open(struct zt_chan *chan);
+ int xpp_close(struct zt_chan *chan);
+ int xpp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg);
+ int xpp_maint(struct zt_span *span, int cmd);
++void sync_master_is(xpd_t *xpd);
+ 
+ #define	CARD_DESC_MAGIC	0xca9dde5c
+ 
+@@ -48,11 +55,9 @@
+ #ifdef CONFIG_PROC_FS
+ #include <linux/proc_fs.h>
+ 
+-extern struct proc_dir_entry	*xpp_procdir;
++extern struct proc_dir_entry	*xpp_proc_toplevel;
+ #endif
+-extern xpd_t			*sync_master;
+ 
+-// Number of rings our simulated phone will ring:
+-#define RINGS_NUM 3
++#define	SPAN_REGISTERED(xpd)	((xpd)->span.flags & ZT_FLAG_REGISTERED)
+ 
+ #endif	/* XPP_ZAP_H */
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xproto.c zaptel-xpp-XJ8JZx_dist/xpp/xproto.c
+--- zaptel-1.2.6/xpp/xproto.c	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/xproto.c	2006-06-22 11:31:28.427315000 +0300
+@@ -1,6 +1,6 @@
+ /*
+  * Written by Oron Peled <oron at actcom.co.il>
+- * Copyright (C) 2004-2005, Xorcom
++ * Copyright (C) 2004-2006, Xorcom
+  *
+  * All rights reserved.
+  *
+@@ -23,9 +23,11 @@
+ #include "xpd.h"
+ #include "xproto.h"
+ #include "xpp_zap.h"
++#include "xbus-core.h"
++#include "zap_debug.h"
+ #include <linux/module.h>
+ 
+-static const char rcsid[] = "$Id: xproto.c 949 2006-02-15 02:24:18Z kpfleming $";
++static const char rcsid[] = "$Id: xproto.c 1431 2006-06-22 08:31:28Z oron $";
+ 
+ extern	int print_dbg;
+ static int packet_process(xbus_t *xbus, int xpd_num, xpacket_t *pack);
+@@ -75,7 +77,7 @@
+ 	return xe;
+ }
+ 
+-const xproto_handler_t xproto_global_handler(byte opcode)
++xproto_handler_t xproto_global_handler(byte opcode)
+ {
+ 	return xproto_card_handler(&PROTO_TABLE(GLOBAL), opcode);
+ }
+@@ -87,7 +89,7 @@
+ 	return xprotocol_tables[cardtype];
+ }
+ 
+-const xproto_table_t *get_xproto_table(xpd_type_t cardtype)
++const xproto_table_t *xproto_get(xpd_type_t cardtype)
+ {
+ 	const xproto_table_t *xtable;
+ 
+@@ -95,18 +97,34 @@
+ 		return NULL;
+ 	xtable = xprotocol_tables[cardtype];
+ 	if(!xtable) {	/* Try to load the relevant module */
+-		int ret = request_module("xpd-type-%d", cardtype);
++		int ret = request_module(XPD_TYPE_PREFIX "%d", cardtype);
+ 		if(ret != 0) {
+ 			NOTICE("%s: Failed to load module for type=%d. exit status=%d.\n",
+ 					__FUNCTION__, cardtype, ret);
+-			/* Drop through: we may be luck... */
++			/* Drop through: we may be lucky... */
+ 		}
+ 		xtable = xprotocol_tables[cardtype];
+ 	}
++	if(xtable) {
++		BUG_ON(!xtable->owner);
++		DBG("%s refcount was %d\n", xtable->name, module_refcount(xtable->owner));
++		if(!try_module_get(xtable->owner)) {
++			ERR("%s: try_module_get for %s failed.\n", __FUNCTION__, xtable->name);
++			return NULL;
++		}
++	}
+ 	return xtable;
+ }
+ 
+-const xproto_handler_t xproto_card_handler(const xproto_table_t *table, byte opcode)
++void xproto_put(const xproto_table_t *xtable)
++{
++	BUG_ON(!xtable);
++	DBG("%s refcount was %d\n", xtable->name, module_refcount(xtable->owner));
++	BUG_ON(module_refcount(xtable->owner) <= 0);
++	module_put(xtable->owner);
++}
++
++xproto_handler_t xproto_card_handler(const xproto_table_t *table, byte opcode)
+ {
+ 	const xproto_entry_t *xe;
+ 
+@@ -115,7 +133,7 @@
+ 	return xe->handler;
+ }
+ 
+-const xproto_entry_t *find_xproto_entry(xpd_t *xpd, byte opcode)
++static const xproto_entry_t *find_xproto_entry(xpd_t *xpd, byte opcode)
+ {
+ 	const xproto_entry_t *xe;
+ 	
+@@ -136,18 +154,6 @@
+ 	return xe;
+ }
+ 
+-const xops_t *get_xops(xpd_type_t xpd_type)
+-{
+-	const xproto_table_t	*proto_table;
+-
+-	if(xpd_type >= XPD_TYPE_NOMODULE)
+-		return NULL;
+-	proto_table = xprotocol_tables[xpd_type];
+-	if(!proto_table)
+-		return NULL;
+-	return &proto_table->xops;
+-}
+-
+ int packet_receive(xbus_t *xbus, xpacket_t *pack)
+ {
+ 	int	xpd_num;
+@@ -161,7 +167,7 @@
+ 		return -EPROTO;
+ 	}
+ 	xpd_num = XPD_NUM(pack->content.addr);
+-#if SOFT_SIMULATOR
++#ifdef SOFT_SIMULATOR
+ 	if(xbus->sim[xpd_num].simulated) {
+ 		//dump_packet("packet_receive -> simulate", pack, print_dbg);
+ 		return simulate_xpd(xbus, xpd_num, pack);
+@@ -256,7 +262,7 @@
+ {
+ 	const xproto_table_t	*proto_table;
+ 
+-	BUG_ON(xpd_type >= XPD_TYPE(NOMODULE));
++	BUG_ON(xpd_type >= XPD_TYPE_NOMODULE);
+ 	proto_table = xprotocol_tables[xpd_type];
+ 	if(!proto_table)
+ 		return NULL;
+@@ -278,7 +284,7 @@
+ 	BUG_ON(!proto_table);
+ 	type = proto_table->type;
+ 	name = proto_table->name;
+-	if(type >= XPD_TYPE(NOMODULE)) {
++	if(type >= XPD_TYPE_NOMODULE) {
+ 		NOTICE("%s: Bad xproto type %d\n", __FUNCTION__, type);
+ 		return -EINVAL;
+ 	}
+@@ -290,14 +296,18 @@
+ 	CHECK_XOP(card_init);
+ 	CHECK_XOP(card_remove);
+ 	CHECK_XOP(card_tick);
++	CHECK_XOP(card_zaptel_registration);
++#ifdef WITH_RBS
++	CHECK_XOP(card_hooksig);
++#else
++	CHECK_XOP(card_sethook);
++#endif
+ 	CHECK_XOP(SYNC_SOURCE);
+ 	CHECK_XOP(PCM_WRITE);
+ 	CHECK_XOP(CHAN_ENABLE);
+-	CHECK_XOP(CHAN_POWER);
+ 	CHECK_XOP(CHAN_CID);
+ 	CHECK_XOP(RING);
+ 	CHECK_XOP(SETHOOK);
+-	CHECK_XOP(LED);
+ 	CHECK_XOP(RELAY_OUT);
+ 
+ 	xprotocol_tables[type] = proto_table;
+@@ -313,7 +323,7 @@
+ 	type = proto_table->type;
+ 	name = proto_table->name;
+ 	DBG("%s (%d)\n", name, type);
+-	if(type >= XPD_TYPE(NOMODULE)) {
++	if(type >= XPD_TYPE_NOMODULE) {
+ 		NOTICE("%s: Bad xproto type %s (%d)\n", __FUNCTION__, name, type);
+ 		return;
+ 	}
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/xproto.h zaptel-xpp-XJ8JZx_dist/xpp/xproto.h
+--- zaptel-1.2.6/xpp/xproto.h	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/xproto.h	2006-06-22 11:31:28.427315000 +0300
+@@ -2,7 +2,7 @@
+ #define	XPROTO_H
+ /*
+  * Written by Oron Peled <oron at actcom.co.il>
+- * Copyright (C) 2004-2005, Xorcom
++ * Copyright (C) 2004-2006, Xorcom
+  *
+  * All rights reserved.
+  *
+@@ -26,18 +26,22 @@
+ 
+ #ifdef	__KERNEL__
+ #include <linux/list.h>
++#include <zaptel.h>
+ #endif
+ 
+-#define	XPD_TYPE(n)	XPD_TYPE_ ## n
+ #define	PROTO_TABLE(n)	n ## _protocol_table
+ 
+-typedef enum xpd_type {
+-	XPD_TYPE(FXO)		= 0x02,
+-	XPD_TYPE(FXS)		= 0x03,
+-	XPD_TYPE(NOMODULE)	= 0x0F,
+-} xpd_type_t;
++#define	XPD_TYPE_FXS		3
++#define	XPD_TYPE_FXO		4
++#define	XPD_TYPE_NOMODULE	15
++
++typedef	byte	xpd_type_t;
++
++#define	XPD_TYPE_PREFIX	"xpd-type-"
++
++#define	MODULE_ALIAS_XPD(type)	\
++	MODULE_ALIAS(XPD_TYPE_PREFIX __stringify(type))
+ 
+-#define	LINE_BITS	(sizeof(xpp_line_t)*8)
+ #define	PCM_CHUNKSIZE	(CHANNELS_PERXPD * 8)	/* samples of 8 bytes */
+ 
+ typedef struct xpd_addr {
+@@ -124,14 +128,13 @@
+ 		const xproto_entry_t *cmd,
+ 		xpacket_t *pack);
+ 
+-const xproto_entry_t *find_xproto_entry(xpd_t *xpd, byte opcode);
+-
+-const xproto_table_t *get_xproto_table(xpd_type_t cardtype);
++const xproto_table_t *xproto_get(xpd_type_t cardtype);
++void xproto_put(const xproto_table_t *xtable);
+ const xproto_entry_t *xproto_card_entry(const xproto_table_t *table, byte opcode);
+-const xproto_handler_t xproto_card_handler(const xproto_table_t *table, byte opcode);
++xproto_handler_t xproto_card_handler(const xproto_table_t *table, byte opcode);
+ 
+ const xproto_entry_t *xproto_global_entry(byte opcode);
+-const xproto_handler_t xproto_global_handler(byte opcode);
++xproto_handler_t xproto_global_handler(byte opcode);
+ 
+ #define	CALL_XMETHOD(name, xbus, xpd, ...)				\
+ 			(xpd)->xops->name(xbus, xpd, ## __VA_ARGS__ )
+@@ -141,23 +144,23 @@
+ 	int (*card_init)(xbus_t *xbus, xpd_t *xpd);
+ 	int (*card_remove)(xbus_t *xbus, xpd_t *xpd);
+ 	int (*card_tick)(xbus_t *xbus, xpd_t *xpd);
++	int (*card_zaptel_registration)(xpd_t *xpd, bool on);
++#ifdef	WITH_RBS
++	int (*card_hooksig)(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig);
++#else
++	int (*card_sethook)(xbus_t *xbus, xpd_t *xpd, int pos, int hookstate);
++#endif
+ 
+ 	int (*SYNC_SOURCE)(xbus_t *xbus, xpd_t *xpd, bool setit, bool is_master);
+ 	int (*PCM_WRITE)(xbus_t *xbus, xpd_t *xpd, xpp_line_t hookstate,  volatile byte *buf);
+ 
+ 	int (*CHAN_ENABLE)(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, bool on);
+-	int (*CHAN_POWER)(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, bool on);
+-	int (*CHAN_CID)(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines);
++	int (*CHAN_CID)(xbus_t *xbus, xpd_t *xpd, int pos);
+ 	int (*RING)(xbus_t *xbus, xpd_t *xpd, int pos, bool on);
+-	int (*SETHOOK)(xbus_t *xbus, xpd_t *xpd, xpp_line_t hook_status);
+-	int (*LED)(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, byte which, bool on);
++	int (*SETHOOK)(xbus_t *xbus, xpd_t *xpd, int pos, bool offhook);
+ 	int (*RELAY_OUT)(xbus_t *xbus, xpd_t *xpd, byte which, bool on);
+ };
+ 
+-const xops_t *get_xops(xpd_type_t xpd_type);
+-
+-#undef	XMETHOD
+-
+ struct xproto_entry {
+ 	xproto_handler_t	handler;
+ 	int			datalen;
+@@ -166,6 +169,7 @@
+ };
+ 
+ struct xproto_table {
++	struct module	*owner;
+ 	xproto_entry_t	entries[255];	/* Indexed by opcode */
+ 	xops_t		xops;
+ 	xpd_type_t	type;
+@@ -176,6 +180,7 @@
+ 
+ #include "card_global.h"
+ #include "card_fxs.h"
++#include "card_fxo.h"
+ 
+ enum opcodes {
+ 	XPROTO_NAME(GLOBAL, DESC_REQ)		= 0x04,
+@@ -186,21 +191,6 @@
+ /**/
+ 	XPROTO_NAME(GLOBAL, SYNC_SOURCE)	= 0x19,
+ 	XPROTO_NAME(GLOBAL, SYNC_REPLY)		= 0x1A,
+-
+-	XPROTO_NAME(FXS, SIG_CHANGED)		= 0x06,
+-/**/
+-	XPROTO_NAME(FXS, SLIC_WRITE)		= 0x0F,	/* Write to SLIC */
+-	XPROTO_NAME(FXS, CHAN_ENABLE)		= 0x0F,	/* Write to SLIC */
+-	XPROTO_NAME(FXS, CHAN_POWER)		= 0x0F,	/* Write to SLIC */
+-	XPROTO_NAME(FXS, CHAN_CID)		= 0x0F,	/* Write to SLIC */
+-	XPROTO_NAME(FXS, RING)			= 0x0F,	/* Write to SLIC */
+-	XPROTO_NAME(FXS, SETHOOK)		= 0x0F,	/* Write to SLIC */
+-	XPROTO_NAME(FXS, LED)			= 0x0F,	/* Write to SLIC */
+-	XPROTO_NAME(FXS, RELAY_OUT)		= 0x0F,	/* Write to SLIC */
+-	XPROTO_NAME(FXS, SLIC_INIT)		= 0x0F,	/* Write to SLIC */
+-	XPROTO_NAME(FXS, SLIC_QUERY)		= 0x0F,	/* Write to SLIC */
+-/**/
+-	XPROTO_NAME(FXS, SLIC_REPLY)		= 0x10,
+ };
+ 
+ 
+@@ -218,6 +208,13 @@
+ 
+ 		MEMBER(FXS, SIG_CHANGED);
+ 		MEMBER(FXS, SLIC_REPLY);
++		MEMBER(FXS, SLIC_INIT);
++		MEMBER(FXS, SLIC_WRITE);
++
++		MEMBER(FXO, SIG_CHANGED);
++		MEMBER(FXO, DAA_REPLY);
++		MEMBER(FXO, DAA_INIT);
++		MEMBER(FXO, DAA_WRITE);
+ 
+ 		byte	data[0];
+ 	};
+@@ -225,8 +222,9 @@
+ 
+ struct xpacket {
+ 	xpacket_raw_t		content;
+-	size_t			datalen;
++	int			datalen;
+ 	struct list_head	list;
++	void			*packet_priv;
+ };
+ 
+ void dump_packet(const char *msg, xpacket_t *packet, bool print_dbg);
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/zap_debug.c zaptel-xpp-XJ8JZx_dist/xpp/zap_debug.c
+--- zaptel-1.2.6/xpp/zap_debug.c	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/zap_debug.c	2006-03-30 12:42:46.619481000 +0200
+@@ -1,6 +1,6 @@
+ /*
+  * Written by Oron Peled <oron at actcom.co.il>
+- * Copyright (C) 2004-2005, Xorcom
++ * Copyright (C) 2004-2006, Xorcom
+  *
+  * All rights reserved.
+  *
+@@ -31,7 +31,7 @@
+ #include <zaptel.h>
+ #include "zap_debug.h"
+ 
+-static const char rcsid[] = "$Id: zap_debug.c 949 2006-02-15 02:24:18Z kpfleming $";
++static const char rcsid[] = "$Id: zap_debug.c 733 2006-03-30 10:42:46Z oron $";
+ 
+ #define	P_(x)	[ x ] = { .value = x, .name = #x, }
+ static	struct {
+@@ -63,74 +63,5 @@
+ 	}
+ }
+ 
+-#define	E_(x)	[ x ] = { .value = x, .name = #x, }
+-static	struct {
+-	int	value;
+-	char	*name;
+-} zt_event_names[] = {
+-	E_(ZT_EVENT_NONE),
+-	E_(ZT_EVENT_ONHOOK),
+-	E_(ZT_EVENT_RINGOFFHOOK),
+-	E_(ZT_EVENT_WINKFLASH),
+-	E_(ZT_EVENT_ALARM),
+-	E_(ZT_EVENT_NOALARM),
+-	E_(ZT_EVENT_ABORT),
+-	E_(ZT_EVENT_OVERRUN),
+-	E_(ZT_EVENT_BADFCS),
+-	E_(ZT_EVENT_DIALCOMPLETE),
+-	E_(ZT_EVENT_RINGERON),
+-	E_(ZT_EVENT_RINGEROFF),
+-	E_(ZT_EVENT_HOOKCOMPLETE),
+-	E_(ZT_EVENT_BITSCHANGED),
+-	E_(ZT_EVENT_PULSE_START),
+-	E_(ZT_EVENT_TIMER_EXPIRED),
+-	E_(ZT_EVENT_TIMER_PING),
+-	E_(ZT_EVENT_POLARITY)
+-};
+-#undef	E_
+-
+-char *event2str(int event)
+-{
+-	BUG_ON(event > ARRAY_SIZE(zt_event_names));
+-	return zt_event_names[event].name;
+-}
+-
+-#define	S_(x)	[ x ] = { .value = x, .name = #x, }
+-static	struct {
+-	int	value;
+-	char	*name;
+-} zt_sig_types[] = {
+-	S_(ZT_SIG_NONE),
+-	S_(ZT_SIG_FXSLS),
+-	S_(ZT_SIG_FXSGS),
+-	S_(ZT_SIG_FXSKS),
+-	S_(ZT_SIG_FXOLS),
+-	S_(ZT_SIG_FXOGS),
+-	S_(ZT_SIG_FXOKS),
+-	S_(ZT_SIG_EM),
+-	S_(ZT_SIG_CLEAR),
+-	S_(ZT_SIG_HDLCRAW),
+-	S_(ZT_SIG_HDLCFCS),
+-	S_(ZT_SIG_HDLCNET),
+-	S_(ZT_SIG_SLAVE),
+-	S_(ZT_SIG_SF),
+-	S_(ZT_SIG_CAS),
+-	S_(ZT_SIG_DACS),
+-	S_(ZT_SIG_EM_E1),
+-	S_(ZT_SIG_DACS_RBS)
+-};
+-#undef	S_
+-
+-void dump_sigtype(int print_dbg, const char *msg, int sigtype)
+-{
+-	int	i;
+-
+-	for(i = 0; i < ARRAY_SIZE(zt_sig_types); i++) {
+-		if(sigtype == zt_sig_types[i].value)
+-			DBG("%s: %s\n", msg, zt_sig_types[i].name);
+-	}
+-}
+ 
+ EXPORT_SYMBOL(dump_poll);
+-EXPORT_SYMBOL(event2str);
+-EXPORT_SYMBOL(dump_sigtype);
+diff -uNr -x .svn -x debian zaptel-1.2.6/xpp/zap_debug.h zaptel-xpp-XJ8JZx_dist/xpp/zap_debug.h
+--- zaptel-1.2.6/xpp/zap_debug.h	2006-02-15 04:24:18.000000000 +0200
++++ zaptel-xpp-XJ8JZx_dist/xpp/zap_debug.h	2006-06-19 10:36:29.741020000 +0300
+@@ -1,16 +1,125 @@
+ #ifndef	ZAP_DEBUG_H
+ #define	ZAP_DEBUG_H
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2006, Xorcom
++ *
++ * 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.
++ *
++ */
+ 
++/* Debugging Macros */
+ #define	DBG(fmt, ...)	\
+-	((print_dbg) && printk(KERN_DEBUG "DBG-%s: %s: " fmt, \
+-		THIS_MODULE->name, __FUNCTION__, ## __VA_ARGS__))
++	((void)((print_dbg) && printk(KERN_DEBUG "DBG-%s: %s: " fmt, \
++		THIS_MODULE->name, __FUNCTION__, ## __VA_ARGS__)))
+ #define	INFO(fmt, ...)	printk(KERN_INFO "INFO-%s: " fmt, THIS_MODULE->name, ## __VA_ARGS__)
+ #define	NOTICE(fmt, ...)	printk(KERN_NOTICE "NOTICE-%s: " fmt, THIS_MODULE->name, ## __VA_ARGS__)
++#define	WARN(fmt, ...)	printk(KERN_WARNING "WARN-%s: %s: " fmt, THIS_MODULE->name, __FUNCTION__, ## __VA_ARGS__)
+ #define	ERR(fmt, ...)	printk(KERN_ERR "ERR-%s: " fmt, THIS_MODULE->name, ## __VA_ARGS__)
+ 
+-
+ void dump_poll(int print_dbg, const char *msg, int poll);
+-char *event2str(int event);
+-void dump_sigtype(int print_dbg, const char *msg, int sigtype);
++
++static inline char *rxsig2str(zt_rxsig_t sig)
++{
++	switch(sig) {
++		case ZT_RXSIG_ONHOOK:	return "ONHOOK";
++		case ZT_RXSIG_OFFHOOK:	return "OFFHOOK";
++		case ZT_RXSIG_START:	return "START";
++		case ZT_RXSIG_RING:	return "RING";
++		case ZT_RXSIG_INITIAL:	return "INITIAL";
++	}
++	return "Unknown rxsig";
++}
++
++static inline char *txsig2str(zt_txsig_t sig)
++{
++	switch(sig) {
++		case ZT_TXSIG_ONHOOK:	return "TXSIG_ONHOOK";
++		case ZT_TXSIG_OFFHOOK:	return "TXSIG_OFFHOOK";
++		case ZT_TXSIG_START:	return "TXSIG_START";
++		case ZT_TXSIG_KEWL:	return "TXSIG_KEWL";				/* Drop battery if possible */
++	}
++	return "Unknown txsig";
++}
++
++static inline char *event2str(int event)
++{
++	switch(event) {
++		case ZT_EVENT_NONE:		return "NONE";
++		case ZT_EVENT_ONHOOK:		return "ONHOOK";
++		case ZT_EVENT_RINGOFFHOOK:	return "RINGOFFHOOK";
++		case ZT_EVENT_WINKFLASH:	return "WINKFLASH";
++		case ZT_EVENT_ALARM:		return "ALARM";
++		case ZT_EVENT_NOALARM:		return "NOALARM";
++		case ZT_EVENT_ABORT:		return "ABORT";
++		case ZT_EVENT_OVERRUN:		return "OVERRUN";
++		case ZT_EVENT_BADFCS:		return "BADFCS";
++		case ZT_EVENT_DIALCOMPLETE:	return "DIALCOMPLETE";
++		case ZT_EVENT_RINGERON:		return "RINGERON";
++		case ZT_EVENT_RINGEROFF:	return "RINGEROFF";
++		case ZT_EVENT_HOOKCOMPLETE:	return "HOOKCOMPLETE";
++		case ZT_EVENT_BITSCHANGED:	return "BITSCHANGED";
++		case ZT_EVENT_PULSE_START:	return "PULSE_START";
++		case ZT_EVENT_TIMER_EXPIRED:	return "TIMER_EXPIRED";
++		case ZT_EVENT_TIMER_PING:	return "TIMER_PING";
++		case ZT_EVENT_POLARITY:		return "POLARITY";
++	}
++	return "Unknown event";
++}
++
++static inline char *hookstate2str(int hookstate)
++{
++	switch(hookstate) {
++		case ZT_ONHOOK:		return "ZT_ONHOOK";
++		case ZT_START:		return "ZT_START";
++		case ZT_OFFHOOK:	return "ZT_OFFHOOK";
++		case ZT_WINK:		return "ZT_WINK";
++		case ZT_FLASH:		return "ZT_FLASH";
++		case ZT_RING:		return "ZT_RING";
++		case ZT_RINGOFF:	return "ZT_RINGOFF";
++	}
++	return "Unknown hookstate";
++}
++
++/* From zaptel.c */
++static inline char *sig2str(int sig)
++{
++	switch (sig) {
++		case ZT_SIG_FXSLS:	return "FXSLS";
++		case ZT_SIG_FXSKS:	return "FXSKS";
++		case ZT_SIG_FXSGS:	return "FXSGS";
++		case ZT_SIG_FXOLS:	return "FXOLS";
++		case ZT_SIG_FXOKS:	return "FXOKS";
++		case ZT_SIG_FXOGS:	return "FXOGS";
++		case ZT_SIG_EM:		return "E&M";
++		case ZT_SIG_EM_E1:	return "E&M-E1";
++		case ZT_SIG_CLEAR:	return "Clear";
++		case ZT_SIG_HDLCRAW:	return "HDLCRAW";
++		case ZT_SIG_HDLCFCS:	return "HDLCFCS";
++		case ZT_SIG_HDLCNET:	return "HDLCNET";
++		case ZT_SIG_SLAVE:	return "Slave";
++		case ZT_SIG_CAS:	return "CAS";
++		case ZT_SIG_DACS:	return "DACS";
++		case ZT_SIG_DACS_RBS:	return "DACS+RBS";
++		case ZT_SIG_SF:		return "SF (ToneOnly)";
++		case ZT_SIG_NONE:
++					break;
++	}
++	return "Unconfigured";
++}
+ 
+ #endif	/* ZAP_DEBUG_H */


Property changes on: zaptel/trunk/debian/patches/xpp_xorcom_debian.dpatch
___________________________________________________________________
Name: svn:executable
   + *

Modified: zaptel/trunk/debian/rules
===================================================================
--- zaptel/trunk/debian/rules	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/rules	2006-06-29 12:50:04 UTC (rev 1980)
@@ -110,22 +110,14 @@
 	# Add here commands to install the package into debian/tmp
 	
 	$(MAKE) install-programs INSTALL_PREFIX=debian/tmp
+	$(MAKE) -C xpp/utils install DESTDIR=debian/tmp
 	/sbin/ldconfig -n debian/tmp/usr/lib
 	dh_install -a --sourcedir=debian/tmp sbin
 	
-	# make sure genzaptelconf is executable
-	chmod +x debian/genzaptelconf
-	dh_install -a debian/genzaptelconf usr/sbin
+	cp $(CURDIR)/debian/zaptel.permissions.rules \
+	  $(CURDIR)/debian/$(PREFIX)/etc/udev/rules.d/zaptel.perms
 	cp debian/$(PREFIX).lintian \
-	  debian/$(PREFIX)/usr/share/lintian/overrides/$(PREFIX)
-ifeq (1,$(USE_BRISTUFF))
-	set -e; for module in cwain qozap zaphfc ztgsm; do \
-		mkdir -p $(MOD_EXAMPLES_DIR)/$$module; \
-		cp -a $$module/*.conf* $(MOD_EXAMPLES_DIR)/$$module; \
-	done
-else
-	mkdir -p $(MOD_EXAMPLES_DIR)/bristuff_stub
-endif
+	  $(CURDIR)/debian/$(PREFIX)/usr/share/lintian/overrides/$(PREFIX)
 
 TARPARDIR=debian/tmp
 TARDIR=$(TARPARDIR)/modules/$(PREFIX)
@@ -155,6 +147,14 @@
 	
 	tar cjf debian/$(PREFIX)-source/usr/src/$(PREFIX).tar.bz2 \
 	  -C $(TARPARDIR) modules
+ifeq (1,$(USE_BRISTUFF))
+	set -e; for module in cwain qozap zaphfc ztgsm; do \
+		mkdir -p $(MOD_EXAMPLES_DIR)/$$module; \
+		cp -a $$module/*.conf* $(MOD_EXAMPLES_DIR)/$$module; \
+	done
+else
+	mkdir -p $(MOD_EXAMPLES_DIR)/bristuff_stub
+endif
 	
 	#rm -rf modules
 
@@ -170,6 +170,7 @@
 	
 	dh_installdocs -i
 	dh_installchangelogs -i ChangeLog
+	dh_installexamples -i $(BRISTUFF_EXAMPLES)
 	dh_link -i
 	dh_compress -i
 	dh_fixperms -i
@@ -187,11 +188,11 @@
 	
 	dh_installdocs -a
 	# TODO: this installs a man page for torisatool, which we don't build
-	dh_installman -a doc/* debian/genzaptelconf.8
+	dh_installman -a doc/*.8 
 	
 	# should be removed, eventually. Still left for compatibility
 	dh_installinit --update-rcd-params="defaults 15 30"
-	dh_installexamples -a zaptel.conf.sample $(BRISTUFF_EXAMPLES)
+	dh_installexamples -a zaptel.conf.sample 
 	
 	dh_installmodules -a
 	dh_installchangelogs -a ChangeLog

Modified: zaptel/trunk/debian/zaptel.dirs
===================================================================
--- zaptel/trunk/debian/zaptel.dirs	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/zaptel.dirs	2006-06-29 12:50:04 UTC (rev 1980)
@@ -1,2 +1,3 @@
 etc/modprobe.d
+etc/udev/rules.d
 usr/share/lintian/overrides

Modified: zaptel/trunk/debian/zaptel.init
===================================================================
--- zaptel/trunk/debian/zaptel.init	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/zaptel.init	2006-06-29 12:50:04 UTC (rev 1980)
@@ -74,6 +74,25 @@
 	rmmod $module || true
 	set -e
 }
+
+# sleep a while until the xpp modules fully register
+wait_for_xpp() {
+	if [ -d /proc/xpp ] && \
+	   [ "`cat /sys/module/xpp/parameters/zap_autoreg`" = 'Y' ]
+	then
+		# wait for the XPDs to register:
+		for i in `seq 30`; do
+			sleep 1
+			if ! grep -q 0 /proc/xpp/*/*/zt_registration 2>/dev/null
+			then
+				# There are either no XPDs or all of them are 
+				# registered. Nothing to do
+				break
+			fi
+		done
+	fi
+}
+
 case "$1" in
 	start|reload)
 		echo -n "$DESC: "
@@ -81,6 +100,7 @@
 		#do
 		#	modprobe $module
 		#done
+		wait_for_xpp
 		fix_asterisbank_sync
 		
 		# If there is no zaptel timing source, load

Modified: zaptel/trunk/debian/zaptel.install
===================================================================
--- zaptel/trunk/debian/zaptel.install	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/zaptel.install	2006-06-29 12:50:04 UTC (rev 1980)
@@ -0,0 +1,4 @@
+sbin/*
+usr/sbin/*
+usr/share/zaptel/*
+etc/hotplug/usb/*

Added: zaptel/trunk/debian/zaptel.permissions.rules
===================================================================
--- zaptel/trunk/debian/zaptel.permissions.rules	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/debian/zaptel.permissions.rules	2006-06-29 12:50:04 UTC (rev 1980)
@@ -0,0 +1,4 @@
+# Some extra udev rules that are missing fromt he current ones in Sarge.
+# See http://bugs.debian.org/316801
+# Being added to our Sarge backport as it was only resolved in Etch
+SUBSYSTEM=="zaptel",    GROUP="dialout"

Added: zaptel/trunk/opvxa1200.c
===================================================================
--- zaptel/trunk/opvxa1200.c	2006-06-29 07:44:10 UTC (rev 1979)
+++ zaptel/trunk/opvxa1200.c	2006-06-29 12:50:04 UTC (rev 1980)
@@ -0,0 +1,2719 @@
+/*
+ * OpenVox A1200P FXS/FXO Interface Driver for Zapata Telephony interface
+ *
+ * Modify from wctdm.c by MiaoLin<miaolin at openvox.com.cn>
+ *
+ * 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. 
+ *
+ */
+
+/* Rev histroy
+ *
+ * Rev 0.10 initial version	
+ * Rev 0.11 
+ * 	fixed the led light on/off bug.
+ * 	modify some wctdm print to opvxa1200
+ * 	support firmware version 1.2, faster i/o operation, and better LED control.
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ */
+
+#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 <asm/io.h>
+#include "proslic.h"
+#include "wctdm.h"
+  
+//miaolin
+#include <linux/string.h>
+#include <asm/uaccess.h> // get_fs(), set_fs(), KERNEL_DS
+#include <linux/file.h> // fput()
+//miaolin
+  
+
+/*
+ *  Define for audio vs. register based ring detection
+ *  
+ */
+/* #define AUDIO_RINGCHECK  */
+
+/*
+  Experimental max loop current limit for the proslic
+  Loop current limit is from 20 mA to 41 mA in steps of 3
+  (according to datasheet)
+  So set the value below to:
+  0x00 : 20mA (default)
+  0x01 : 23mA
+  0x02 : 26mA
+  0x03 : 29mA
+  0x04 : 32mA
+  0x05 : 35mA
+  0x06 : 37mA
+  0x07 : 41mA
+*/
+static int loopcurrent = 20;
+
+static int reversepolarity = 0;
+
+static alpha  indirect_regs[] =
+{
+{0,255,"DTMF_ROW_0_PEAK",0x55C2},
+{1,255,"DTMF_ROW_1_PEAK",0x51E6},
+{2,255,"DTMF_ROW2_PEAK",0x4B85},
+{3,255,"DTMF_ROW3_PEAK",0x4937},
+{4,255,"DTMF_COL1_PEAK",0x3333},
+{5,255,"DTMF_FWD_TWIST",0x0202},
+{6,255,"DTMF_RVS_TWIST",0x0202},
+{7,255,"DTMF_ROW_RATIO_TRES",0x0198},
+{8,255,"DTMF_COL_RATIO_TRES",0x0198},
+{9,255,"DTMF_ROW_2ND_ARM",0x0611},
+{10,255,"DTMF_COL_2ND_ARM",0x0202},
+{11,255,"DTMF_PWR_MIN_TRES",0x00E5},
+{12,255,"DTMF_OT_LIM_TRES",0x0A1C},
+{13,0,"OSC1_COEF",0x7B30},
+{14,1,"OSC1X",0x0063},
+{15,2,"OSC1Y",0x0000},
+{16,3,"OSC2_COEF",0x7870},
+{17,4,"OSC2X",0x007D},
+{18,5,"OSC2Y",0x0000},
+{19,6,"RING_V_OFF",0x0000},
+{20,7,"RING_OSC",0x7EF0},
+{21,8,"RING_X",0x0160},
+{22,9,"RING_Y",0x0000},
+{23,255,"PULSE_ENVEL",0x2000},
+{24,255,"PULSE_X",0x2000},
+{25,255,"PULSE_Y",0x0000},
+//{26,13,"RECV_DIGITAL_GAIN",0x4000},	// playback volume set lower
+{26,13,"RECV_DIGITAL_GAIN",0x2000},	// playback volume set lower
+{27,14,"XMIT_DIGITAL_GAIN",0x4000},
+//{27,14,"XMIT_DIGITAL_GAIN",0x2000},
+{28,15,"LOOP_CLOSE_TRES",0x1000},
+{29,16,"RING_TRIP_TRES",0x3600},
+{30,17,"COMMON_MIN_TRES",0x1000},
+{31,18,"COMMON_MAX_TRES",0x0200},
+{32,19,"PWR_ALARM_Q1Q2",0x07C0},
+{33,20,"PWR_ALARM_Q3Q4",0x2600},
+{34,21,"PWR_ALARM_Q5Q6",0x1B80},
+{35,22,"LOOP_CLOSURE_FILTER",0x8000},
+{36,23,"RING_TRIP_FILTER",0x0320},
+{37,24,"TERM_LP_POLE_Q1Q2",0x008C},
+{38,25,"TERM_LP_POLE_Q3Q4",0x0100},
+{39,26,"TERM_LP_POLE_Q5Q6",0x0010},
+{40,27,"CM_BIAS_RINGING",0x0C00},
+{41,64,"DCDC_MIN_V",0x0C00},
+{42,255,"DCDC_XTRA",0x1000},
+{43,66,"LOOP_CLOSE_TRES_LOW",0x1000},
+};
+
+static struct fxo_mode {
+	char *name;
+	/* FXO */
+	int ohs;
+	int ohs2;
+	int rz;
+	int rt;
+	int ilim;
+	int dcv;
+	int mini;
+	int acim;
+	int ring_osc;
+	int ring_x;
+} fxo_modes[] =
+{
+	{ "FCC", 0, 0, 0, 1, 0, 0x3, 0, 0, }, 	/* US, Canada */
+	{ "TBR21", 0, 0, 0, 0, 1, 0x3, 0, 0x2, 0x7e6c, 0x023a, },
+										/* Austria, Belgium, Denmark, Finland, France, Germany, 
+										   Greece, Iceland, Ireland, Italy, Luxembourg, Netherlands,
+										   Norway, Portugal, Spain, Sweden, Switzerland, and UK */
+	{ "ARGENTINA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "AUSTRALIA", 1, 0, 0, 0, 0, 0, 0x3, 0x3, },
+	{ "AUSTRIA", 0, 1, 0, 0, 1, 0x3, 0, 0x3, },
+	{ "BAHRAIN", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "BELGIUM", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "BRAZIL", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "BULGARIA", 0, 0, 0, 0, 1, 0x3, 0x0, 0x3, },
+	{ "CANADA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "CHILE", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "CHINA", 0, 0, 0, 0, 0, 0, 0x3, 0xf, },
+	{ "COLUMBIA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "CROATIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "CYRPUS", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "CZECH", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "DENMARK", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "ECUADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "EGYPT", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "ELSALVADOR", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "FINLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "FRANCE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "GERMANY", 0, 1, 0, 0, 1, 0x3, 0, 0x3, },
+	{ "GREECE", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "GUAM", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "HONGKONG", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "HUNGARY", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "ICELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "INDIA", 0, 0, 0, 0, 0, 0x3, 0, 0x4, },
+	{ "INDONESIA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "IRELAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "ISRAEL", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "ITALY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "JAPAN", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "JORDAN", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "KAZAKHSTAN", 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "KUWAIT", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "LATVIA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "LEBANON", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "LUXEMBOURG", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "MACAO", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "MALAYSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, },	/* Current loop >= 20ma */
+	{ "MALTA", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "MEXICO", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "MOROCCO", 0, 0, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "NETHERLANDS", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "NEWZEALAND", 0, 0, 0, 0, 0, 0x3, 0, 0x4, },
+	{ "NIGERIA", 0, 0, 0, 0, 0x1, 0x3, 0, 0x2, },
+	{ "NORWAY", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "OMAN", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "PAKISTAN", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "PERU", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "PHILIPPINES", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "POLAND", 0, 0, 1, 1, 0, 0x3, 0, 0, },
+	{ "PORTUGAL", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "ROMANIA", 0, 0, 0, 0, 0, 3, 0, 0, },
+	{ "RUSSIA", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "SAUDIARABIA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "SINGAPORE", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "SLOVAKIA", 0, 0, 0, 0, 0, 0x3, 0, 0x3, },
+	{ "SLOVENIA", 0, 0, 0, 0, 0, 0x3, 0, 0x2, },
+	{ "SOUTHAFRICA", 1, 0, 1, 0, 0, 0x3, 0, 0x3, },
+	{ "SOUTHKOREA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "SPAIN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "SWEDEN", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "SWITZERLAND", 0, 1, 0, 0, 1, 0x3, 0, 0x2, },
+	{ "SYRIA", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "TAIWAN", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "THAILAND", 0, 0, 0, 0, 0, 0, 0x3, 0, },
+	{ "UAE", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "UK", 0, 1, 0, 0, 1, 0x3, 0, 0x5, },
+	{ "USA", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+	{ "YEMEN", 0, 0, 0, 0, 0, 0x3, 0, 0, },
+};
+
+#ifdef STANDALONE_ZAPATA
+#include "zaptel.h"
+#else
+#include <linux/zaptel.h>
+#endif
+
+#ifdef LINUX26
+#include <linux/moduleparam.h>
+#endif
+
+#define NUM_FXO_REGS 60
+
+#define WC_MAX_IFACES 128
+
+#define DELAY	0x0	/* 30 = 15 cycles, 10 = 8 cycles, 0 = 3 cycles */
+#define WC_OFFSET	4	/* Offset between transmit and receive, in bytes. */
+#define WC_SYNCFLAG	0xca1ef1ac
+
+#define WC_CNTL    	0x00
+#define WC_OPER		0x01
+#define WC_AUXC    	0x02
+#define WC_AUXD    	0x03
+#define WC_MASK0   	0x04
+#define WC_MASK1   	0x05
+#define WC_INTSTAT 	0x06
+#define WC_AUXR		0x07
+
+#define WC_DMAWS	0x08
+#define WC_DMAWI	0x0c
+#define WC_DMAWE	0x10
+#define WC_DMARS	0x18
+#define WC_DMARI	0x1c
+#define WC_DMARE	0x20
+
+#define WC_AUXFUNC	0x2b
+#define WC_SERCTL	0x2d
+#define WC_FSCDELAY	0x2f
+
+#define WC_REGBASE	0xc0
+
+#define WC_VER		0x0
+#define WC_CS		0x1
+#define WC_SPICTRL	0x2
+#define WC_SPIDATA	0x3
+
+#define BIT_SPI_BYHW 	(1 << 0)
+#define BIT_SPI_BUSY    (1 << 1)	// 0=can read/write spi, 1=spi working.
+#define BIT_SPI_START	(1 << 2)
+
+
+#define BIT_LED_CLK     (1 << 0)	// MiaoLin add to control the led. 
+#define BIT_LED_DATA    (1 << 1)	// MiaoLin add to control the led.
+
+#define BIT_CS		(1 << 2)
+#define BIT_SCLK	(1 << 3)
+#define BIT_SDI		(1 << 4)
+#define BIT_SDO		(1 << 5)
+
+#define FLAG_EMPTY	0
+#define FLAG_WRITE	1
+#define FLAG_READ	2
+
+/* the constants below control the 'debounce' periods enforced by the
+   check_hook routines; these routines are called once every 4 interrupts
+   (the interrupt cycles around the four modules), so the periods are
+   specified in _4 millisecond_ increments
+*/
+#define RING_DEBOUNCE		4		/* Ringer Debounce (64 ms) */
+#define DEFAULT_BATT_DEBOUNCE	4		/* Battery debounce (64 ms) */
+#define POLARITY_DEBOUNCE 	4		/* Polarity debounce (64 ms) */
+#define DEFAULT_BATT_THRESH	3		/* Anything under this is "no battery" */
+
+#define OHT_TIMER		6000	/* How long after RING to retain OHT */
+
+#define FLAG_3215	(1 << 0)
+
+//modify by MiaoLin from 4 to 12;
+//#define NUM_CARDS 4
+#define NUM_CARDS 12
+#define NUM_FLAG  4	//number of flag channels.
+
+// if you want to record the last 8 sec voice before the driver unload, uncomment it and rebuild.
+//#define TEST_LOG_INCOME_VOICE
+
+#define MAX_ALARMS 10
+
+#define MOD_TYPE_FXS	0
+#define MOD_TYPE_FXO	1
+
+#define MINPEGTIME	10 * 8		/* 30 ms peak to peak gets us no more than 100 Hz */
+#define PEGTIME		50 * 8		/* 50ms peak to peak gets us rings of 10 Hz or more */
+#define PEGCOUNT	5		/* 5 cycles of pegging means RING */
+
+#define NUM_CAL_REGS 12
+
+struct calregs {
+	unsigned char vals[NUM_CAL_REGS];
+};
+
+enum proslic_power_warn {
+	PROSLIC_POWER_UNKNOWN = 0,
+	PROSLIC_POWER_ON,
+	PROSLIC_POWER_WARNED,
+};
+
+#define voc_buffer_size (8000*8)
+
+struct wctdm {
+	struct pci_dev *dev;
+	char *variety;
+	struct zt_span span;
+	unsigned char ios;
+	int usecount;
+	unsigned int intcount;
+	int dead;
+	int pos;
+	int flags[NUM_CARDS];
+	int freeregion;
+	int alt;
+	int curcard;
+	int cardflag;		/* Bit-map of present cards */
+	enum proslic_power_warn proslic_power;
+	spinlock_t lock;
+
+	union {
+		struct {
+#ifdef AUDIO_RINGCHECK
+			unsigned int pegtimer;
+			int pegcount;
+			int peg;
+			int ring;
+#else			
+			int wasringing;
+#endif			
+			int ringdebounce;
+			int offhook;
+			int battdebounce;
+			int nobatttimer;
+			int battery;
+		        int lastpol;
+		        int polarity;
+		        int polaritydebounce;
+		} fxo;
+		struct {
+			int oldrxhook;
+			int debouncehook;
+			int lastrxhook;
+			int debounce;
+			int ohttimer;
+			int idletxhookstate;		/* IDLE changing hook state */
+			int lasttxhook;
+			int palarms;
+			struct calregs calregs;
+		} fxs;
+	} mod[NUM_CARDS];
+
+	/* Receive hook state and debouncing */
+	int modtype[NUM_CARDS];
+	unsigned char reg0shadow[NUM_CARDS];
+	unsigned char reg1shadow[NUM_CARDS];
+
+	unsigned long ioaddr;
+	unsigned long mem_region;	/* 32 bit Region allocated to tiger320 */
+	unsigned long mem_len;		/* Length of 32 bit region */
+	volatile unsigned long mem32;	/* Virtual representation of 32 bit memory area */
+	
+	dma_addr_t 	readdma;
+	dma_addr_t	writedma;
+	volatile unsigned char *writechunk;					/* Double-word aligned write memory */
+	volatile unsigned char *readchunk;					/* Double-word aligned read memory */
+	struct zt_chan chans[NUM_CARDS];
+
+#ifdef TEST_LOG_INCOME_VOICE	
+	//unsigned char tempo[NUM_CARDS + NUM_FLAG];
+	char * voc_buf[NUM_CARDS + NUM_FLAG];
+	int voc_ptr[NUM_CARDS + NUM_FLAG];
+#endif
+	//int offset;
+	int lastchan;
+	unsigned short ledstate;
+	unsigned char fwversion;
+};
+
+
+struct wctdm_desc {
+	char *name;
+	int flags;
+};
+
+static struct wctdm_desc wctdme = { "OpenVox A1200P", 0 };
+static int acim2tiss[16] = { 0x0, 0x1, 0x4, 0x5, 0x7, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x2, 0x0, 0x3 };
+
+static struct wctdm *ifaces[WC_MAX_IFACES];
+
+static void wctdm_release(struct wctdm *wc);
+
+static int battdebounce = DEFAULT_BATT_DEBOUNCE;
+static int battthresh = DEFAULT_BATT_THRESH;
+static int debug = 0;
+//static int debug = 1;
+static int robust = 0;
+static int timingonly = 0;
+static int lowpower = 0;
+static int boostringer = 0;
+static int _opermode = 0;
+static char *opermode = "FCC";
+static int fxshonormode = 0;
+static int alawoverride = 0;
+static int spibyhw = 1;			// MiaoLin add;
+static int usememio = 1;
+
+static int wctdm_init_proslic(struct wctdm *wc, int card, int fast , int manual, int sane);
+
+static void wctdm_set_led(struct wctdm* wc, int card, int onoff)
+{
+	int i;
+	unsigned char c;
+	
+	wc->ledstate &= ~(0x01<<card);
+	wc->ledstate |= (onoff<<card);
+	c = (inb(wc->ioaddr + WC_AUXD)&~BIT_LED_CLK)|BIT_LED_DATA;
+	outb( c,  wc->ioaddr + WC_AUXD);
+	for(i=NUM_CARDS-1; i>=0; i--)
+	{
+		if(wc->ledstate & (0x0001<<i))
+			if(wc->fwversion == 0x11)
+				c &= ~BIT_LED_DATA;
+			else
+				c |= BIT_LED_DATA;
+		else
+			if(wc->fwversion == 0x11)
+				c |= BIT_LED_DATA;
+			else
+				c &= ~BIT_LED_DATA;
+			
+		outb( c,  wc->ioaddr + WC_AUXD);
+		outb( c|BIT_LED_CLK,  wc->ioaddr + WC_AUXD);
+		outb( (c&~BIT_LED_CLK)|BIT_LED_DATA,  wc->ioaddr + WC_AUXD);
+	}	
+}
+ 
+
+static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char ints)
+{
+	int x, y, chan_offset, pos;
+	volatile unsigned char *txbuf;
+	
+	if (ints & 0x04 /*0x01*/) 
+		/* Write is at interrupt address.  Start writing from normal offset */
+		txbuf = wc->writechunk;
+	else 
+		txbuf = wc->writechunk + ZT_CHUNKSIZE * (NUM_CARDS+NUM_FLAG);
+		
+	/* Calculate Transmission */
+	zt_transmit(&wc->span);
+	
+	if(wc->lastchan == -1)	// not in sync.
+		return;
+	
+	chan_offset = (wc->lastchan*4 + 4 ) % (NUM_CARDS+NUM_FLAG);
+
+	//for (x=0;x<wc->offset;x++)
+	//	txbuf[x] = wc->tempo[x];
+	for (y=0;y<ZT_CHUNKSIZE;y++) {
+#ifdef __BIG_ENDIAN
+	// operation pending...
+#else
+		//for (x=0;x<(NUM_CARDS+NUM_FLAG);x++) {
+		//	pos = y * (NUM_CARDS+NUM_FLAG) + chanmap[(x+chan_offset)&0x0f] + wc->offset;
+		//	/* Put channel number as outgoing data */
+		//	if (pos < (NUM_CARDS+NUM_FLAG) * ZT_CHUNKSIZE)
+		//		txbuf[pos] = wc->chans[x].writechunk[y];
+		//	else
+		//		wc->tempo[pos - (NUM_CARDS+NUM_FLAG) * ZT_CHUNKSIZE] = wc->chans[x].writechunk[y];
+		//}
+		//printk("\n");
+		for (x=0;x<(NUM_CARDS+NUM_FLAG);x++) {
+			pos = y * (NUM_CARDS+NUM_FLAG) + ((x + chan_offset + NUM_CARDS+NUM_FLAG  /*+ wc->offset*/ - WC_OFFSET)&0x0f);
+			if(x<NUM_CARDS)
+				txbuf[pos] = wc->chans[x].writechunk[y]; 
+			else
+				txbuf[pos] = 0; 
+			//if(x==2)
+			//	txbuf[pos] = 0x55;//trans_count;
+			//else
+			//	txbuf[pos] = 0;
+		}
+
+		/*for (x=0;x<(NUM_CARDS+NUM_FLAG);x++) {
+			pos = y * (NUM_CARDS+NUM_FLAG) + x + chan_offset + wc->offset - WC_OFFSET;
+			if ( pos<(NUM_CARDS+NUM_FLAG)*ZT_CHUNKSIZE )
+			{	
+				if(x<NUM_CARDS)
+					txbuf[pos] = wc->chans[x].writechunk[y]; 
+				else
+					txbuf[pos] = 0; 
+			}
+			else
+			{
+				if(x<NUM_CARDS)
+					wc->tempo[pos - (NUM_CARDS+NUM_FLAG) * ZT_CHUNKSIZE] = wc->chans[x].writechunk[y]; 
+				else
+					wc->tempo[pos - (NUM_CARDS+NUM_FLAG) * ZT_CHUNKSIZE] = 0; 
+			}
+		}*/
+#endif
+	}
+
+}
+
+#ifdef AUDIO_RINGCHECK
+static inline void ring_check(struct wctdm *wc, int card)
+{
+	int x;
+	short sample;
+	if (wc->modtype[card] != MOD_TYPE_FXO)
+		return;
+	wc->mod[card].fxo.pegtimer += ZT_CHUNKSIZE;
+	for (x=0;x<ZT_CHUNKSIZE;x++) {
+		/* Look for pegging to indicate ringing */
+		sample = ZT_XLAW(wc->chans[card].readchunk[x], (&(wc->chans[card])));
+		if ((sample > 10000) && (wc->mod[card].fxo.peg != 1)) {
+			if (debug > 1) printk("High peg!\n");
+			if ((wc->mod[card].fxo.pegtimer < PEGTIME) && (wc->mod[card].fxo.pegtimer > MINPEGTIME))
+				wc->mod[card].fxo.pegcount++;
+			wc->mod[card].fxo.pegtimer = 0;
+			wc->mod[card].fxo.peg = 1;
+		} else if ((sample < -10000) && (wc->mod[card].fxo.peg != -1)) {
+			if (debug > 1) printk("Low peg!\n");
+			if ((wc->mod[card].fxo.pegtimer < (PEGTIME >> 2)) && (wc->mod[card].fxo.pegtimer > (MINPEGTIME >> 2)))
+				wc->mod[card].fxo.pegcount++;
+			wc->mod[card].fxo.pegtimer = 0;
+			wc->mod[card].fxo.peg = -1;
+		}
+	}
+	if (wc->mod[card].fxo.pegtimer > PEGTIME) {
+		/* Reset pegcount if our timer expires */
+		wc->mod[card].fxo.pegcount = 0;
+	}
+	/* Decrement debouncer if appropriate */
+	if (wc->mod[card].fxo.ringdebounce)
+		wc->mod[card].fxo.ringdebounce--;
+	if (!wc->mod[card].fxo.offhook && !wc->mod[card].fxo.ringdebounce) {
+		if (!wc->mod[card].fxo.ring && (wc->mod[card].fxo.pegcount > PEGCOUNT)) {
+			/* It's ringing */
+			if (debug)
+				printk("RING on %d/%d!\n", wc->span.spanno, card + 1);
+			if (!wc->mod[card].fxo.offhook)
+				zt_hooksig(&wc->chans[card], ZT_RXSIG_RING);
+			wc->mod[card].fxo.ring = 1;
+		}
+		if (wc->mod[card].fxo.ring && !wc->mod[card].fxo.pegcount) {
+			/* No more ring */
+			if (debug)
+				printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1);
+			zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
+			wc->mod[card].fxo.ring = 0;
+		}
+	}
+}
+#endif
+
+
+static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char ints)
+{
+	volatile unsigned char *rxbuf;
+	int x, y, chan_offset;
+
+
+	if (ints & /*0x08*/0x04)
+		/* Read is at interrupt address.  Valid data is available at normal offset */
+		rxbuf = wc->readchunk;
+	else
+		rxbuf = wc->readchunk + ZT_CHUNKSIZE * (NUM_CARDS+NUM_FLAG);
+
+	//search for the flag channel
+	for(x=0; x<4; x++)
+	{
+		//printk("0x%08x ", *(int*)(rxbuf+x*4));
+		if(  *(int*)(rxbuf+x*4) == WC_SYNCFLAG)
+		{
+			//printk(" found at %d ", x);	
+			break;
+		}
+	}
+	
+	if(x==4)
+	{
+		printk("buffer sync misseed!\n");
+		wc->lastchan = -1;
+		return;
+	}
+	else if(wc->lastchan != x)
+	{
+		
+		printk("buffer re-sync occur from %d to %d\n", wc->lastchan, x);
+		wc->lastchan = x;
+	}
+	chan_offset = (wc->lastchan*4 + 4 ) % (NUM_CARDS+NUM_FLAG);
+
+	for (x=0;x<ZT_CHUNKSIZE;x++) {
+#ifdef __BIG_ENDIAN
+	// operation pending...
+#else
+		for (y=0;y<NUM_CARDS;y++) { 
+			if (wc->cardflag & (1 << y))
+				wc->chans[y].readchunk[x] = rxbuf[(NUM_CARDS+NUM_FLAG) * x + ((y + chan_offset ) & 0x0f)];
+#ifdef TEST_LOG_INCOME_VOICE
+			wc->voc_buf[y][wc->voc_ptr[y]] = rxbuf[(NUM_CARDS+NUM_FLAG) * x + ((y + chan_offset) & 0x0f)];
+			wc->voc_ptr[y]++;
+			if(wc->voc_ptr[y] >= voc_buffer_size)
+				wc->voc_ptr[y] = 0;
+#endif		
+		}
+
+#endif
+	}
+#ifdef AUDIO_RINGCHECK
+	for (x=0;x<wc->cards;x++)
+		ring_check(wc, x);
+#endif		
+	/* XXX We're wasting 8 taps.  We should get closer :( */
+	for (x = 0; x < NUM_CARDS; x++) {
+		if (wc->cardflag & (1 << x))
+			zt_ec_chunk(&wc->chans[x], wc->chans[x].readchunk, wc->chans[x].writechunk);
+	}
+	zt_receive(&wc->span);
+}
+
+static void wctdm_stop_dma(struct wctdm *wc);
+static void wctdm_reset_tdm(struct wctdm *wc);
+static void wctdm_restart_dma(struct wctdm *wc);
+
+
+static unsigned char __wctdm_getcreg(struct wctdm *wc, unsigned char reg);
+static void __wctdm_setcreg(struct wctdm *wc, unsigned char reg, unsigned char val);
+
+
+static inline void __write_8bits(struct wctdm *wc, unsigned char bits)
+{
+	if(spibyhw == 0)
+	{
+		int x;
+		/* Drop chip select */
+		wc->ios |= BIT_SCLK;
+		outb(wc->ios, wc->ioaddr + WC_AUXD);
+		wc->ios &= ~BIT_CS;
+		outb(wc->ios, wc->ioaddr + WC_AUXD);
+		for (x=0;x<8;x++) {
+			/* Send out each bit, MSB first, drop SCLK as we do so */
+			if (bits & 0x80)
+				wc->ios |= BIT_SDI;
+			else
+				wc->ios &= ~BIT_SDI;
+			wc->ios &= ~BIT_SCLK;
+			outb(wc->ios, wc->ioaddr + WC_AUXD);
+			/* Now raise SCLK high again and repeat */
+			wc->ios |= BIT_SCLK;
+			outb(wc->ios, wc->ioaddr + WC_AUXD);
+			bits <<= 1;
+		}
+		/* Finally raise CS back high again */
+		wc->ios |= BIT_CS;
+		outb(wc->ios, wc->ioaddr + WC_AUXD);
+	}
+	else
+	{
+		__wctdm_setcreg(wc, WC_SPIDATA, bits);
+		__wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW | BIT_SPI_START);
+		while ((__wctdm_getcreg(wc, WC_SPICTRL) & BIT_SPI_BUSY) != 0);
+		__wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW);
+	}
+}
+
+
+static inline void __reset_spi(struct wctdm *wc)
+{
+	__wctdm_setcreg(wc, WC_SPICTRL, 0);
+	
+	/* Drop chip select and clock once and raise and clock once */
+	wc->ios |= BIT_SCLK;
+	outb(wc->ios, wc->ioaddr + WC_AUXD);
+	wc->ios &= ~BIT_CS;
+	outb(wc->ios, wc->ioaddr + WC_AUXD);
+	wc->ios |= BIT_SDI;
+	wc->ios &= ~BIT_SCLK;
+	outb(wc->ios, wc->ioaddr + WC_AUXD);
+	/* Now raise SCLK high again and repeat */
+	wc->ios |= BIT_SCLK;
+	outb(wc->ios, wc->ioaddr + WC_AUXD);
+	/* Finally raise CS back high again */
+	wc->ios |= BIT_CS;
+	outb(wc->ios, wc->ioaddr + WC_AUXD);
+	/* Clock again */
+	wc->ios &= ~BIT_SCLK;
+	outb(wc->ios, wc->ioaddr + WC_AUXD);
+	/* Now raise SCLK high again and repeat */
+	wc->ios |= BIT_SCLK;
+	outb(wc->ios, wc->ioaddr + WC_AUXD);
+	
+	__wctdm_setcreg(wc, WC_SPICTRL, spibyhw);
+
+}
+
+static inline unsigned char __read_8bits(struct wctdm *wc)
+{
+	unsigned char res=0, c;
+	int x;
+	if(spibyhw == 0)
+	{
+		wc->ios &= ~BIT_CS;
+		outb(wc->ios, wc->ioaddr + WC_AUXD);
+		/* Drop chip select */
+		wc->ios &= ~BIT_CS;
+		outb(wc->ios, wc->ioaddr + WC_AUXD);
+		for (x=0;x<8;x++) {
+			res <<= 1;
+			/* Get SCLK */
+			wc->ios &= ~BIT_SCLK;
+			outb(wc->ios, wc->ioaddr + WC_AUXD);
+			/* Read back the value */
+			c = inb(wc->ioaddr + WC_AUXR);
+			if (c & BIT_SDO)
+				res |= 1;
+			/* Now raise SCLK high again */
+			wc->ios |= BIT_SCLK;
+			outb(wc->ios, wc->ioaddr + WC_AUXD);
+		}
+		/* Finally raise CS back high again */
+		wc->ios |= BIT_CS;
+		outb(wc->ios, wc->ioaddr + WC_AUXD);
+		wc->ios &= ~BIT_SCLK;
+		outb(wc->ios, wc->ioaddr + WC_AUXD);
+	}
+	else
+	{
+		__wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW | BIT_SPI_START);
+		while ((__wctdm_getcreg(wc, WC_SPICTRL) & BIT_SPI_BUSY) != 0);
+		res = __wctdm_getcreg(wc, WC_SPIDATA);
+		__wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW);
+	}
+	
+	/* And return our result */
+	return res;
+}
+
+static void __wctdm_setcreg_mem(struct wctdm *wc, unsigned char reg, unsigned char val)
+{
+	unsigned int *p = (unsigned int*)(wc->mem32 + WC_REGBASE + ((reg & 0xf) << 2));
+	*p = val;
+}
+
+static unsigned char __wctdm_getcreg_mem(struct wctdm *wc, unsigned char reg)
+{
+	unsigned int *p = (unsigned int*)(wc->mem32 + WC_REGBASE + ((reg & 0xf) << 2));
+	return (*p)&0x00ff;
+}
+
+
+static void __wctdm_setcreg(struct wctdm *wc, unsigned char reg, unsigned char val)
+{
+	if(usememio)
+		__wctdm_setcreg_mem(wc, reg, val);
+	else
+		outb(val, wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2));
+}
+
+static unsigned char __wctdm_getcreg(struct wctdm *wc, unsigned char reg)
+{
+	if(usememio)
+		return __wctdm_getcreg_mem(wc, reg);
+	else
+		return inb(wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2));
+}
+
+static inline void __wctdm_setcard(struct wctdm *wc, int card)
+{
+	if (wc->curcard != card) {
+		__wctdm_setcreg(wc, WC_CS, card);
+		wc->curcard = card;
+		//printk("Select card %d\n", card);
+	}
+}
+
+static void __wctdm_setreg(struct wctdm *wc, int card, unsigned char reg, unsigned char value)
+{
+	__wctdm_setcard(wc, card);
+	if (wc->modtype[card] == MOD_TYPE_FXO) {
+		__write_8bits(wc, 0x20);
+		__write_8bits(wc, reg & 0x7f);
+	} else {
+		__write_8bits(wc, reg & 0x7f);
+	}
+	__write_8bits(wc, value);
+}
+
+static void wctdm_setreg(struct wctdm *wc, int card, unsigned char reg, unsigned char value)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&wc->lock, flags);
+	__wctdm_setreg(wc, card, reg, value);
+	spin_unlock_irqrestore(&wc->lock, flags);
+}
+
+static unsigned char __wctdm_getreg(struct wctdm *wc, int card, unsigned char reg)
+{
+	__wctdm_setcard(wc, card);
+	if (wc->modtype[card] == MOD_TYPE_FXO) {
+		__write_8bits(wc, 0x60);
+		__write_8bits(wc, reg & 0x7f);
+	} else {
+		__write_8bits(wc, reg | 0x80);
+	}
+	return __read_8bits(wc);
+}
+
+static inline void reset_spi(struct wctdm *wc, int card)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&wc->lock, flags);
+	__wctdm_setcard(wc, card);
+	__reset_spi(wc);
+	__reset_spi(wc);
+	spin_unlock_irqrestore(&wc->lock, flags);
+}
+
+static unsigned char wctdm_getreg(struct wctdm *wc, int card, unsigned char reg)
+{
+	unsigned long flags;
+	unsigned char res;
+	spin_lock_irqsave(&wc->lock, flags);
+	res = __wctdm_getreg(wc, card, reg);
+	spin_unlock_irqrestore(&wc->lock, flags);
+	return res;
+}
+
+static int __wait_access(struct wctdm *wc, int card)
+{
+    unsigned char data = 0;
+    long origjiffies;
+    int count = 0;
+
+    #define MAX 6000 /* attempts */
+
+
+    origjiffies = jiffies;
+    /* Wait for indirect access */
+    while (count++ < MAX)
+	 {
+		data = __wctdm_getreg(wc, card, I_STATUS);
+
+		if (!data)
+			return 0;
+
+	 }
+
+    if(count > (MAX-1)) printk(" ##### Loop error (%02x) #####\n", data);
+
+	return 0;
+}
+
+static unsigned char translate_3215(unsigned char address)
+{
+	int x;
+	for (x=0;x<sizeof(indirect_regs)/sizeof(indirect_regs[0]);x++) {
+		if (indirect_regs[x].address == address) {
+			address = indirect_regs[x].altaddr;
+			break;
+		}
+	}
+	return address;
+}
+
+static int wctdm_proslic_setreg_indirect(struct wctdm *wc, int card, unsigned char address, unsigned short data)
+{
+	unsigned long flags;
+	int res = -1;
+	/* Translate 3215 addresses */
+	if (wc->flags[card] & FLAG_3215) {
+		address = translate_3215(address);
+		if (address == 255)
+			return 0;
+	}
+	spin_lock_irqsave(&wc->lock, flags);
+	if(!__wait_access(wc, card)) {
+		__wctdm_setreg(wc, card, IDA_LO,(unsigned char)(data & 0xFF));
+		__wctdm_setreg(wc, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8));
+		__wctdm_setreg(wc, card, IAA,address);
+		res = 0;
+	};
+	spin_unlock_irqrestore(&wc->lock, flags);
+	return res;
+}
+
+static int wctdm_proslic_getreg_indirect(struct wctdm *wc, int card, unsigned char address)
+{ 
+	unsigned long flags;
+	int res = -1;
+	char *p=NULL;
+	/* Translate 3215 addresses */
+	if (wc->flags[card] & FLAG_3215) {
+		address = translate_3215(address);
+		if (address == 255)
+			return 0;
+	}
+	spin_lock_irqsave(&wc->lock, flags);
+	if (!__wait_access(wc, card)) {
+		__wctdm_setreg(wc, card, IAA, address);
+		if (!__wait_access(wc, card)) {
+			unsigned char data1, data2;
+			data1 = __wctdm_getreg(wc, card, IDA_LO);
+			data2 = __wctdm_getreg(wc, card, IDA_HI);
+			res = data1 | (data2 << 8);
+		} else
+			p = "Failed to wait inside\n";
+	} else
+		p = "failed to wait\n";
+	spin_unlock_irqrestore(&wc->lock, flags);
+	if (p)
+		printk(p);
+	return res;
+}
+
+static int wctdm_proslic_init_indirect_regs(struct wctdm *wc, int card)
+{
+	unsigned char i;
+
+	for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++)
+	{
+		if(wctdm_proslic_setreg_indirect(wc, card, indirect_regs[i].address,indirect_regs[i].initial))
+			return -1;
+	}
+
+	return 0;
+}
+
+static int wctdm_proslic_verify_indirect_regs(struct wctdm *wc, int card)
+{ 
+	int passed = 1;
+	unsigned short i, initial;
+	int j;
+
+	for (i=0; i<sizeof(indirect_regs) / sizeof(indirect_regs[0]); i++) 
+	{
+		if((j = wctdm_proslic_getreg_indirect(wc, card, (unsigned char) indirect_regs[i].address)) < 0) {
+			printk("Failed to read indirect register %d\n", i);
+			return -1;
+		}
+		initial= indirect_regs[i].initial;
+
+		if ( j != initial && (!(wc->flags[card] & FLAG_3215) || (indirect_regs[i].altaddr != 255)))
+		{
+			 printk("!!!!!!! %s  iREG %X = %X  should be %X\n",
+				indirect_regs[i].name,indirect_regs[i].address,j,initial );
+			 passed = 0;
+		}	
+	}
+
+    if (passed) {
+		if (debug)
+			printk("Init Indirect Registers completed successfully.\n");
+    } else {
+		printk(" !!!!! Init Indirect Registers UNSUCCESSFULLY.\n");
+		return -1;
+    }
+    return 0;
+}
+
+static inline void wctdm_proslic_recheck_sanity(struct wctdm *wc, int card)
+{
+	int res;
+	/* Check loopback */
+	res = wc->reg1shadow[card];
+	if (!res && (res != wc->mod[card].fxs.lasttxhook)) {
+		res = wctdm_getreg(wc, card, 8);
+		if (res) {
+			printk("Ouch, part reset, quickly restoring reality (%d)\n", card);
+			wctdm_init_proslic(wc, card, 1, 0, 1);
+		} else {
+			if (wc->mod[card].fxs.palarms++ < MAX_ALARMS) {
+				printk("Power alarm on module %d, resetting!\n", card + 1);
+				if (wc->mod[card].fxs.lasttxhook == 4)
+					wc->mod[card].fxs.lasttxhook = 1;
+				wctdm_setreg(wc, card, 64, wc->mod[card].fxs.lasttxhook);
+			} else {
+				if (wc->mod[card].fxs.palarms == MAX_ALARMS)
+					printk("Too many power alarms on card %d, NOT resetting!\n", card + 1);
+			}
+		}
+	}
+}
+
+static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card)
+{
+#ifndef AUDIO_RINGCHECK
+	unsigned char res;
+#endif	
+	signed char b;
+	int poopy = 0;
+	/* Try to track issues that plague slot one FXO's */
+	b = wc->reg0shadow[card];
+	if ((b & 0x2) || !(b & 0x8)) {
+		/* Not good -- don't look at anything else */
+		if (debug)
+			printk("Poopy (%02x) on card %d!\n", b, card + 1); 
+		poopy++;
+	}
+	b &= 0x9b;
+	if (wc->mod[card].fxo.offhook) {
+		if (b != 0x9)
+			wctdm_setreg(wc, card, 5, 0x9);
+	} else {
+		if (b != 0x8)
+			wctdm_setreg(wc, card, 5, 0x8);
+	}
+	if (poopy)
+		return;
+#ifndef AUDIO_RINGCHECK
+	if (!wc->mod[card].fxo.offhook) {
+		res = wc->reg0shadow[card];
+		if ((res & 0x60) && wc->mod[card].fxo.battery) {
+			wc->mod[card].fxo.ringdebounce += (ZT_CHUNKSIZE * 16);
+			if (wc->mod[card].fxo.ringdebounce >= ZT_CHUNKSIZE * 64) {
+				if (!wc->mod[card].fxo.wasringing) {
+					wc->mod[card].fxo.wasringing = 1;
+					zt_hooksig(&wc->chans[card], ZT_RXSIG_RING);
+					if (debug)
+						printk("RING on %d/%d!\n", wc->span.spanno, card + 1);
+				}
+				wc->mod[card].fxo.ringdebounce = ZT_CHUNKSIZE * 64;
+			}
+		} else {
+			wc->mod[card].fxo.ringdebounce -= ZT_CHUNKSIZE * 4;
+			if (wc->mod[card].fxo.ringdebounce <= 0) {
+				if (wc->mod[card].fxo.wasringing) {
+					wc->mod[card].fxo.wasringing = 0;
+					zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
+					if (debug)
+						printk("NO RING on %d/%d!\n", wc->span.spanno, card + 1);
+				}
+				wc->mod[card].fxo.ringdebounce = 0;
+			}
+				
+		}
+	}
+#endif
+	b = wc->reg1shadow[card];
+#if 0 
+	{
+		static int count = 0;
+		if (!(count++ % 100)) {
+			printk("Card %d: Voltage: %d  Debounce %d\n", card + 1, 
+			       b, wc->mod[card].fxo.battdebounce);
+		}
+	}
+#endif	
+	if (abs(b) < battthresh) {
+		wc->mod[card].fxo.nobatttimer++;
+#if 0
+		if (wc->mod[card].fxo.battery)
+			printk("Battery loss: %d (%d debounce)\n", b, wc->mod[card].fxo.battdebounce);
+#endif
+		if (wc->mod[card].fxo.battery && !wc->mod[card].fxo.battdebounce) {
+			if (debug)
+				printk("NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1);
+			wc->mod[card].fxo.battery =  0;
+#ifdef	JAPAN
+			if ((!wc->ohdebounce) && wc->offhook) {
+				zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK);
+				if (debug)
+					printk("Signalled On Hook\n");
+#ifdef	ZERO_BATT_RING
+				wc->onhook++;
+#endif
+			}
+#else
+			zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK);
+#endif
+			wc->mod[card].fxo.battdebounce = battdebounce;
+		} else if (!wc->mod[card].fxo.battery)
+			wc->mod[card].fxo.battdebounce = battdebounce;
+	} else if (abs(b) > battthresh) {
+		if (!wc->mod[card].fxo.battery && !wc->mod[card].fxo.battdebounce) {
+			if (debug)
+				printk("BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1, 
+					(b < 0) ? "-" : "+");			    
+#ifdef	ZERO_BATT_RING
+			if (wc->onhook) {
+				wc->onhook = 0;
+				zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
+				if (debug)
+					printk("Signalled Off Hook\n");
+			}
+#else
+			zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
+#endif
+			wc->mod[card].fxo.battery = 1;
+			wc->mod[card].fxo.nobatttimer = 0;
+			wc->mod[card].fxo.battdebounce = battdebounce;
+		} else if (wc->mod[card].fxo.battery)
+			wc->mod[card].fxo.battdebounce = battdebounce;
+
+		if (wc->mod[card].fxo.lastpol >= 0) {
+		    if (b < 0) {
+			wc->mod[card].fxo.lastpol = -1;
+			wc->mod[card].fxo.polaritydebounce = POLARITY_DEBOUNCE;
+		    }
+		} 
+		if (wc->mod[card].fxo.lastpol <= 0) {
+		    if (b > 0) {
+			wc->mod[card].fxo.lastpol = 1;
+			wc->mod[card].fxo.polaritydebounce = POLARITY_DEBOUNCE;
+		    }
+		}
+	} else {
+		/* It's something else... */
+		wc->mod[card].fxo.battdebounce = battdebounce;
+	}
+	if (wc->mod[card].fxo.battdebounce)
+		wc->mod[card].fxo.battdebounce--;
+	if (wc->mod[card].fxo.polaritydebounce) {
+	        wc->mod[card].fxo.polaritydebounce--;
+		if (wc->mod[card].fxo.polaritydebounce < 1) {
+		    if (wc->mod[card].fxo.lastpol != wc->mod[card].fxo.polarity) {
+				if (debug)
+					printk("%lu Polarity reversed (%d -> %d)\n", jiffies, 
+				       wc->mod[card].fxo.polarity, 
+				       wc->mod[card].fxo.lastpol);
+				if (wc->mod[card].fxo.polarity)
+				    zt_qevent_lock(&wc->chans[card], ZT_EVENT_POLARITY);
+				wc->mod[card].fxo.polarity = wc->mod[card].fxo.lastpol;
+		    }
+		}
+	}
+}
+
+static inline void wctdm_proslic_check_hook(struct wctdm *wc, int card)
+{
+	char res;
+	int hook;
+
+	/* For some reason we have to debounce the
+	   hook detector.  */
+
+	res = wc->reg0shadow[card];
+	hook = (res & 1);
+	if (hook != wc->mod[card].fxs.lastrxhook) {
+		/* Reset the debounce (must be multiple of 4ms) */
+		wc->mod[card].fxs.debounce = 8 * (4 * 8);
+#if 0
+		printk("Resetting debounce card %d hook %d, %d\n", card, hook, wc->mod[card].fxs.debounce);
+#endif
+	} else {
+		if (wc->mod[card].fxs.debounce > 0) {
+			wc->mod[card].fxs.debounce-= 16 * ZT_CHUNKSIZE;
+#if 0
+			printk("Sustaining hook %d, %d\n", hook, wc->mod[card].fxs.debounce);
+#endif
+			if (!wc->mod[card].fxs.debounce) {
+#if 0
+				printk("Counted down debounce, newhook: %d...\n", hook);
+#endif
+				wc->mod[card].fxs.debouncehook = hook;
+			}
+			if (!wc->mod[card].fxs.oldrxhook && wc->mod[card].fxs.debouncehook) {
+				/* Off hook */
+#if 1
+				if (debug)
+#endif				
+					printk("opvxa1200: Card %d Going off hook\n", card);
+				zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
+				if (robust)
+					wctdm_init_proslic(wc, card, 1, 0, 1);
+				wc->mod[card].fxs.oldrxhook = 1;
+			
+			} else if (wc->mod[card].fxs.oldrxhook && !wc->mod[card].fxs.debouncehook) {
+				/* On hook */
+#if 1
+				if (debug)
+#endif				
+					printk("opvxa1200: Card %d Going on hook\n", card);
+				zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK);
+				wc->mod[card].fxs.oldrxhook = 0;
+			}
+		}
+	}
+	wc->mod[card].fxs.lastrxhook = hook;
+}
+
+#ifdef LINUX26
+static irqreturn_t wctdm_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+#else
+static void wctdm_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+#endif
+{
+	struct wctdm *wc = dev_id;
+	unsigned char ints;
+	int x, y, z;
+	int mode;
+
+	ints = inb(wc->ioaddr + WC_INTSTAT);
+	outb(ints, wc->ioaddr + WC_INTSTAT);
+
+	if (!ints)
+#ifdef LINUX26
+		return IRQ_NONE;
+#else
+		return;
+#endif		
+
+	if (ints & 0x10) {
+		/* Stop DMA, wait for watchdog */
+		printk("TDM PCI Master abort\n");
+		wctdm_stop_dma(wc);
+#ifdef LINUX26
+		return IRQ_RETVAL(1);
+#else
+		return;
+#endif		
+	}
+	
+	if (ints & 0x20) {
+		printk("PCI Target abort\n");
+#ifdef LINUX26
+		return IRQ_RETVAL(1);
+#else
+		return;
+#endif		
+	}
+
+	for (x=0;x<4*3;x++) {
+		if (wc->cardflag & (1 << x) &&
+		    (wc->modtype[x] == MOD_TYPE_FXS)) {
+			if (wc->mod[x].fxs.lasttxhook == 0x4) {
+				/* RINGing, prepare for OHT */
+				wc->mod[x].fxs.ohttimer = OHT_TIMER << 3;
+				if (reversepolarity)
+					wc->mod[x].fxs.idletxhookstate = 0x6;	/* OHT mode when idle */
+				else
+					wc->mod[x].fxs.idletxhookstate = 0x2; 
+			} else {
+				if (wc->mod[x].fxs.ohttimer) {
+					wc->mod[x].fxs.ohttimer-= ZT_CHUNKSIZE;
+					if (!wc->mod[x].fxs.ohttimer) {
+						if (reversepolarity)
+							wc->mod[x].fxs.idletxhookstate = 0x5;	/* Switch to active */
+						else
+							wc->mod[x].fxs.idletxhookstate = 0x1;
+						if ((wc->mod[x].fxs.lasttxhook == 0x2) || (wc->mod[x].fxs.lasttxhook = 0x6)) {
+							/* Apply the change if appropriate */
+							if (reversepolarity) 
+								wc->mod[x].fxs.lasttxhook = 0x5;
+							else
+								wc->mod[x].fxs.lasttxhook = 0x1;
+							wctdm_setreg(wc, x, 64, wc->mod[x].fxs.lasttxhook);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	if (ints & 0x0f) {
+		wc->intcount++;
+		z = wc->intcount & 0x3;
+		mode = wc->intcount & 0xc;
+		for(y=0; y<3; y++)
+		{
+			x = z + y*4;
+			if (wc->cardflag & (1 << x ) ) 
+			{
+				switch(mode) 
+				{
+				case 0:
+					/* Rest */
+					break;
+				case 4:
+					/* Read first shadow reg */
+					if (wc->modtype[x] == MOD_TYPE_FXS)
+						wc->reg0shadow[x] = wctdm_getreg(wc, x, 68);
+					else if (wc->modtype[x] == MOD_TYPE_FXO)
+						wc->reg0shadow[x] = wctdm_getreg(wc, x, 5);
+					break;
+				case 8:
+					/* Read second shadow reg */
+					if (wc->modtype[x] == MOD_TYPE_FXS)
+						wc->reg1shadow[x] = wctdm_getreg(wc, x, 64);
+					else if (wc->modtype[x] == MOD_TYPE_FXO)
+						wc->reg1shadow[x] = wctdm_getreg(wc, x, 29);
+					break;
+				case 12:
+					/* Perform processing */
+					if (wc->modtype[x] == MOD_TYPE_FXS) {
+						wctdm_proslic_check_hook(wc, x);
+						if (!(wc->intcount & 0xf0))
+							wctdm_proslic_recheck_sanity(wc, x);
+						} else if (wc->modtype[x] == MOD_TYPE_FXO) {
+						wctdm_voicedaa_check_hook(wc, x);
+					}
+					break;
+				}
+			}
+		}
+		if (!(wc->intcount % 10000)) {
+			/* Accept an alarm once per 10 seconds */
+			for (x=0;x<4*3;x++) 
+				if (wc->modtype[x] == MOD_TYPE_FXS) {
+					if (wc->mod[x].fxs.palarms)
+						wc->mod[x].fxs.palarms--;
+				}
+		}
+		wctdm_receiveprep(wc, ints);
+		wctdm_transmitprep(wc, ints);
+	}
+#ifdef LINUX26
+	return IRQ_RETVAL(1);
+#endif		
+	
+}
+
+static int wctdm_voicedaa_insane(struct wctdm *wc, int card)
+{
+	int blah;
+	blah = wctdm_getreg(wc, card, 2);
+	if (blah != 0x3)
+		return -2;
+	blah = wctdm_getreg(wc, card, 11);
+	if (debug)
+		printk("VoiceDAA System: %02x\n", blah & 0xf);
+	return 0;
+}
+
+static int wctdm_proslic_insane(struct wctdm *wc, int card)
+{
+	int blah,insane_report;
+	insane_report=0;
+
+	blah = wctdm_getreg(wc, card, 0);
+	if (debug) 
+		printk("ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf));
+
+#if 0
+	if ((blah & 0x30) >> 4) {
+		printk("ProSLIC on module %d is not a 3210.\n", card);
+		return -1;
+	}
+#endif
+	if (((blah & 0xf) == 0) || ((blah & 0xf) == 0xf)) {
+		/* SLIC not loaded */
+		return -1;
+	}
+	if ((blah & 0xf) < 2) {
+		printk("ProSLIC 3210 version %d is too old\n", blah & 0xf);
+		return -1;
+	}
+	if ((blah & 0xf) == 2) {
+		/* ProSLIC 3215, not a 3210 */
+		wc->flags[card] |= FLAG_3215;
+	}
+	blah = wctdm_getreg(wc, card, 8);
+	if (blah != 0x2) {
+		printk("ProSLIC on module %d insane (1) %d should be 2\n", card, blah);
+		return -1;
+	} else if ( insane_report)
+		printk("ProSLIC on module %d Reg 8 Reads %d Expected is 0x2\n",card,blah);
+
+	blah = wctdm_getreg(wc, card, 64);
+	if (blah != 0x0) {
+		printk("ProSLIC on module %d insane (2)\n", card);
+		return -1;
+	} else if ( insane_report)
+		printk("ProSLIC on module %d Reg 64 Reads %d Expected is 0x0\n",card,blah);
+
+	blah = wctdm_getreg(wc, card, 11);
+	if (blah != 0x33) {
+		printk("ProSLIC on module %d insane (3)\n", card);
+		return -1;
+	} else if ( insane_report)
+		printk("ProSLIC on module %d Reg 11 Reads %d Expected is 0x33\n",card,blah);
+
+	/* Just be sure it's setup right. */
+	wctdm_setreg(wc, card, 30, 0);
+
+	if (debug) 
+		printk("ProSLIC on module %d seems sane.\n", card);
+	return 0;
+}
+
+static int wctdm_proslic_powerleak_test(struct wctdm *wc, int card)
+{
+	unsigned long origjiffies;
+	unsigned char vbat;
+
+	/* Turn off linefeed */
+	wctdm_setreg(wc, card, 64, 0);
+
+	/* Power down */
+	wctdm_setreg(wc, card, 14, 0x10);
+
+	/* Wait for one second */
+	origjiffies = jiffies;
+
+	while((vbat = wctdm_getreg(wc, card, 82)) > 0x6) {
+		if ((jiffies - origjiffies) >= (HZ/2))
+			break;;
+	}
+
+	if (vbat < 0x06) {
+		printk("Excessive leakage detected on module %d: %d volts (%02x) after %d ms\n", card,
+		       376 * vbat / 1000, vbat, (int)((jiffies - origjiffies) * 1000 / HZ));
+		return -1;
+	} else if (debug) {
+		printk("Post-leakage voltage: %d volts\n", 376 * vbat / 1000);
+	}
+	return 0;
+}
+
+static int wctdm_powerup_proslic(struct wctdm *wc, int card, int fast)
+{
+	unsigned char vbat;
+	unsigned long origjiffies;
+	int lim;
+
+	/* Set period of DC-DC converter to 1/64 khz */
+	wctdm_setreg(wc, card, 92, 0xff /* was 0xff */);
+
+	/* Wait for VBat to powerup */
+	origjiffies = jiffies;
+
+	/* Disable powerdown */
+	wctdm_setreg(wc, card, 14, 0);
+
+	/* If fast, don't bother checking anymore */
+	if (fast)
+		return 0;
+
+	while((vbat = wctdm_getreg(wc, card, 82)) < 0xc0) {
+		/* Wait no more than 500ms */
+		if ((jiffies - origjiffies) > HZ/2) {
+			break;
+		}
+	}
+
+	if (vbat < 0xc0) {
+		if (wc->proslic_power == PROSLIC_POWER_UNKNOWN)
+				 printk("ProSLIC on module %d failed to powerup within %d ms (%d mV only)\n\n -- DID YOU REMEMBER TO PLUG IN THE HD POWER CABLE TO THE A1200P??\n",
+					card, (int)(((jiffies - origjiffies) * 1000 / HZ)),
+					vbat * 375);
+		wc->proslic_power = PROSLIC_POWER_WARNED;
+		return -1;
+	} else if (debug) {
+		printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n",
+		       card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ)));
+	}
+	wc->proslic_power = PROSLIC_POWER_ON;
+
+        /* Proslic max allowed loop current, reg 71 LOOP_I_LIMIT */
+        /* If out of range, just set it to the default value     */
+        lim = (loopcurrent - 20) / 3;
+        if ( loopcurrent > 41 ) {
+                lim = 0;
+                if (debug)
+                        printk("Loop current out of range! Setting to default 20mA!\n");
+        }
+        else if (debug)
+                        printk("Loop current set to %dmA!\n",(lim*3)+20);
+        wctdm_setreg(wc,card,LOOP_I_LIMIT,lim);
+
+	/* Engage DC-DC converter */
+	wctdm_setreg(wc, card, 93, 0x19 /* was 0x19 */);
+#if 0
+	origjiffies = jiffies;
+	while(0x80 & wctdm_getreg(wc, card, 93)) {
+		if ((jiffies - origjiffies) > 2 * HZ) {
+			printk("Timeout waiting for DC-DC calibration on module %d\n", card);
+			return -1;
+		}
+	}
+
+#if 0
+	/* Wait a full two seconds */
+	while((jiffies - origjiffies) < 2 * HZ);
+
+	/* Just check to be sure */
+	vbat = wctdm_getreg(wc, card, 82);
+	printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n",
+		       card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ)));
+#endif
+#endif
+	return 0;
+
+}
+
+static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card){
+	unsigned long origjiffies;
+	unsigned char i;
+
+	wctdm_setreg(wc, card, 21, 0);//(0)  Disable all interupts in DR21
+	wctdm_setreg(wc, card, 22, 0);//(0)Disable all interupts in DR21
+	wctdm_setreg(wc, card, 23, 0);//(0)Disable all interupts in DR21
+	wctdm_setreg(wc, card, 64, 0);//(0)
+
+	wctdm_setreg(wc, card, 97, 0x18); //(0x18)Calibrations without the ADC and DAC offset and without common mode calibration.
+	wctdm_setreg(wc, card, 96, 0x47); //(0x47)	Calibrate common mode and differential DAC mode DAC + ILIM
+
+	origjiffies=jiffies;
+	while( wctdm_getreg(wc,card,96)!=0 ){
+		if((jiffies-origjiffies)>80)
+			return -1;
+	}
+//Initialized DR 98 and 99 to get consistant results.
+// 98 and 99 are the results registers and the search should have same intial conditions.
+
+/*******************************The following is the manual gain mismatch calibration****************************/
+/*******************************This is also available as a function *******************************************/
+	// Delay 10ms
+	origjiffies=jiffies; 
+	while((jiffies-origjiffies)<1);
+	wctdm_proslic_setreg_indirect(wc, card, 88,0);
+	wctdm_proslic_setreg_indirect(wc,card,89,0);
+	wctdm_proslic_setreg_indirect(wc,card,90,0);
+	wctdm_proslic_setreg_indirect(wc,card,91,0);
+	wctdm_proslic_setreg_indirect(wc,card,92,0);
+	wctdm_proslic_setreg_indirect(wc,card,93,0);
+
+	wctdm_setreg(wc, card, 98,0x10); // This is necessary if the calibration occurs other than at reset time
+	wctdm_setreg(wc, card, 99,0x10);
+
+	for ( i=0x1f; i>0; i--)
+	{
+		wctdm_setreg(wc, card, 98,i);
+		origjiffies=jiffies; 
+		while((jiffies-origjiffies)<4);
+		if((wctdm_getreg(wc,card,88)) == 0)
+			break;
+	} // for
+
+	for ( i=0x1f; i>0; i--)
+	{
+		wctdm_setreg(wc, card, 99,i);
+		origjiffies=jiffies; 
+		while((jiffies-origjiffies)<4);
+		if((wctdm_getreg(wc,card,89)) == 0)
+			break;
+	}//for
+
+/*******************************The preceding is the manual gain mismatch calibration****************************/
+/**********************************The following is the longitudinal Balance Cal***********************************/
+	wctdm_setreg(wc,card,64,1);
+	while((jiffies-origjiffies)<10); // Sleep 100?
+
+	wctdm_setreg(wc, card, 64, 0);
+	wctdm_setreg(wc, card, 23, 0x4);  // enable interrupt for the balance Cal
+	wctdm_setreg(wc, card, 97, 0x1); // this is a singular calibration bit for longitudinal calibration
+	wctdm_setreg(wc, card, 96,0x40);
+
+	wctdm_getreg(wc,card,96); /* Read Reg 96 just cause */
+
+	wctdm_setreg(wc, card, 21, 0xFF);
+	wctdm_setreg(wc, card, 22, 0xFF);
+	wctdm_setreg(wc, card, 23, 0xFF);
+
+	/**The preceding is the longitudinal Balance Cal***/
+	return(0);
+
+}
+#if 1
+static int wctdm_proslic_calibrate(struct wctdm *wc, int card)
+{
+	unsigned long origjiffies;
+	int x;
+	/* Perform all calibrations */
+	wctdm_setreg(wc, card, 97, 0x1f);
+	
+	/* Begin, no speedup */
+	wctdm_setreg(wc, card, 96, 0x5f);
+
+	/* Wait for it to finish */
+	origjiffies = jiffies;
+	while(wctdm_getreg(wc, card, 96)) {
+		if ((jiffies - origjiffies) > 2 * HZ) {
+			printk("Timeout waiting for calibration of module %d\n", card);
+			return -1;
+		}
+	}
+	
+	if (debug) {
+		/* Print calibration parameters */
+		printk("Calibration Vector Regs 98 - 107: \n");
+		for (x=98;x<108;x++) {
+			printk("%d: %02x\n", x, wctdm_getreg(wc, card, x));
+		}
+	}
+	return 0;
+}
+#endif
+
+static void wait_just_a_bit(int foo)
+{
+	long newjiffies;
+	newjiffies = jiffies + foo;
+	while(jiffies < newjiffies);
+}
+
+static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, int sane)
+{
+	unsigned char reg16=0, reg26=0, reg30=0, reg31=0;
+	long newjiffies;
+	wc->modtype[card] = MOD_TYPE_FXO;
+	
+	/* Sanity check the ProSLIC */
+	reset_spi(wc, card);
+	if (!sane && wctdm_voicedaa_insane(wc, card))
+		return -2;
+
+	/* Software reset */
+	wctdm_setreg(wc, card, 1, 0x80);
+
+	/* Wait just a bit */
+	wait_just_a_bit(HZ/10);
+
+	/* Enable PCM, ulaw */
+	if (alawoverride)
+		wctdm_setreg(wc, card, 33, 0x20);
+	else
+		wctdm_setreg(wc, card, 33, 0x28);
+
+	/* Set On-hook speed, Ringer impedence, and ringer threshold */
+	reg16 |= (fxo_modes[_opermode].ohs << 6);
+	reg16 |= (fxo_modes[_opermode].rz << 1);
+	reg16 |= (fxo_modes[_opermode].rt);
+	wctdm_setreg(wc, card, 16, reg16);
+	
+	/* Set DC Termination:
+	   Tip/Ring voltage adjust, minimum operational current, current limitation */
+	reg26 |= (fxo_modes[_opermode].dcv << 6);
+	reg26 |= (fxo_modes[_opermode].mini << 4);
+	reg26 |= (fxo_modes[_opermode].ilim << 1);
+	wctdm_setreg(wc, card, 26, reg26);
+
+	/* Set AC Impedence */
+	reg30 = (fxo_modes[_opermode].acim);
+	wctdm_setreg(wc, card, 30, reg30);
+
+	/* Misc. DAA parameters */
+	reg31 = 0xa3;
+	reg31 |= (fxo_modes[_opermode].ohs2 << 3);
+	wctdm_setreg(wc, card, 31, reg31);
+
+	/* Set Transmit/Receive timeslot */
+	//printk("set card %d to %d\n", card, (3-(card%4)) * 8 + (card/4) * 64);
+	wctdm_setreg(wc, card, 34, (3-(card%4)) * 8 + (card/4) * 64);
+	wctdm_setreg(wc, card, 35, 0x00);
+	wctdm_setreg(wc, card, 36, (3-(card%4)) * 8 + (card/4) * 64);
+	wctdm_setreg(wc, card, 37, 0x00);
+
+	/* Enable ISO-Cap */
+	wctdm_setreg(wc, card, 6, 0x00);
+
+	/* Wait 1000ms for ISO-cap to come up */
+	newjiffies = jiffies;
+	newjiffies += 2 * HZ;
+	while((jiffies < newjiffies) && !(wctdm_getreg(wc, card, 11) & 0xf0))
+		wait_just_a_bit(HZ/10);
+
+	if (!(wctdm_getreg(wc, card, 11) & 0xf0)) {
+		printk("VoiceDAA did not bring up ISO link properly!\n");
+		return -1;
+	}
+	if (debug)
+		printk("ISO-Cap is now up, line side: %02x rev %02x\n", 
+		       wctdm_getreg(wc, card, 11) >> 4,
+		       (wctdm_getreg(wc, card, 13) >> 2) & 0xf);
+	/* Enable on-hook line monitor */
+	wctdm_setreg(wc, card, 5, 0x08);
+
+	/* NZ -- crank the tx gain up by 7 dB */
+	if (!strcmp(fxo_modes[_opermode].name, "NEWZEALAND")) {
+		printk("Adjusting gain\n");
+		wctdm_setreg(wc, card, 38, 0x7);
+	}
+
+	return 0;
+		
+}
+
+static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, int sane)
+{
+
+	unsigned short tmp[5];
+	unsigned char r19;
+	int x;
+	int fxsmode=0;
+
+
+	/* By default, don't send on hook */
+	if (reversepolarity)
+		wc->mod[card].fxs.idletxhookstate = 5;
+	else
+		wc->mod[card].fxs.idletxhookstate = 1;
+
+	/* Sanity check the ProSLIC */
+	if (!sane && wctdm_proslic_insane(wc, card))
+		return -2;
+		
+	if (sane) {
+		/* Make sure we turn off the DC->DC converter to prevent anything from blowing up */
+		wctdm_setreg(wc, card, 14, 0x10);
+	}
+
+	if (wctdm_proslic_init_indirect_regs(wc, card)) {
+		printk(KERN_INFO "Indirect Registers failed to initialize on module %d.\n", card);
+		return -1;
+	}
+
+	/* Clear scratch pad area */
+	wctdm_proslic_setreg_indirect(wc, card, 97,0);
+
+	/* Clear digital loopback */
+	wctdm_setreg(wc, card, 8, 0);
+
+	/* Revision C optimization */
+	wctdm_setreg(wc, card, 108, 0xeb);
+
+	/* Disable automatic VBat switching for safety to prevent
+	   Q7 from accidently turning on and burning out. */
+	wctdm_setreg(wc, card, 67, 0x17);
+
+	/* Turn off Q7 */
+	wctdm_setreg(wc, card, 66, 1);
+
+	/* Flush ProSLIC digital filters by setting to clear, while
+	   saving old values */
+	for (x=0;x<5;x++) {
+		tmp[x] = wctdm_proslic_getreg_indirect(wc, card, x + 35);
+		wctdm_proslic_setreg_indirect(wc, card, x + 35, 0x8000);
+	}
+
+	/* Power up the DC-DC converter */
+	if (wctdm_powerup_proslic(wc, card, fast)) {
+		printk("Unable to do INITIAL ProSLIC powerup on module %d\n", card);
+		return -1;
+	}
+
+	if (!fast) {
+
+		/* Check for power leaks */
+		if (wctdm_proslic_powerleak_test(wc, card)) {
+			printk("ProSLIC module %d failed leakage test.  Check for short circuit\n", card);
+		}
+		/* Power up again */
+		if (wctdm_powerup_proslic(wc, card, fast)) {
+			printk("Unable to do FINAL ProSLIC powerup on module %d\n", card);
+			return -1;
+		}
+#ifndef NO_CALIBRATION
+		/* Perform calibration */
+		if(manual) {
+			if (wctdm_proslic_manual_calibrate(wc, card)) {
+				//printk("Proslic failed on Manual Calibration\n");
+				if (wctdm_proslic_manual_calibrate(wc, card)) {
+					printk("Proslic Failed on Second Attempt to Calibrate Manually. (Try -DNO_CALIBRATION in Makefile)\n");
+					return -1;
+				}
+				printk("Proslic Passed Manual Calibration on Second Attempt\n");
+			}
+		}
+		else {
+			if(wctdm_proslic_calibrate(wc, card))  {
+				//printk("ProSlic died on Auto Calibration.\n");
+				if (wctdm_proslic_calibrate(wc, card)) {
+					printk("Proslic Failed on Second Attempt to Auto Calibrate\n");
+					return -1;
+				}
+				printk("Proslic Passed Auto Calibration on Second Attempt\n");
+			}
+		}
+		/* Perform DC-DC calibration */
+		wctdm_setreg(wc, card, 93, 0x99);
+		r19 = wctdm_getreg(wc, card, 107);
+		if ((r19 < 0x2) || (r19 > 0xd)) {
+			printk("DC-DC cal has a surprising direct 107 of 0x%02x!\n", r19);
+			wctdm_setreg(wc, card, 107, 0x8);
+		}
+
+		/* Save calibration vectors */
+		for (x=0;x<NUM_CAL_REGS;x++)
+			wc->mod[card].fxs.calregs.vals[x] = wctdm_getreg(wc, card, 96 + x);
+#endif
+
+	} else {
+		/* Restore calibration registers */
+		for (x=0;x<NUM_CAL_REGS;x++)
+			wctdm_setreg(wc, card, 96 + x, wc->mod[card].fxs.calregs.vals[x]);
+	}
+	/* Calibration complete, restore original values */
+	for (x=0;x<5;x++) {
+		wctdm_proslic_setreg_indirect(wc, card, x + 35, tmp[x]);
+	}
+
+	if (wctdm_proslic_verify_indirect_regs(wc, card)) {
+		printk(KERN_INFO "Indirect Registers failed verification.\n");
+		return -1;
+	}
+
+
+#if 0
+    /* Disable Auto Power Alarm Detect and other "features" */
+    wctdm_setreg(wc, card, 67, 0x0e);
+    blah = wctdm_getreg(wc, card, 67);
+#endif
+
+#if 0
+    if (wctdm_proslic_setreg_indirect(wc, card, 97, 0x0)) { // Stanley: for the bad recording fix
+		 printk(KERN_INFO "ProSlic IndirectReg Died.\n");
+		 return -1;
+	}
+#endif
+
+    if (alawoverride)
+    	wctdm_setreg(wc, card, 1, 0x20);
+    else
+    	wctdm_setreg(wc, card, 1, 0x28);
+ 	// U-Law 8-bit interface
+	
+    //printk("set card %d to %d\n", card, (3-(card%4)) * 8 + (card/4) * 64);
+    wctdm_setreg(wc, card, 2, (3-(card%4)) * 8 + (card/4) * 64);    // Tx Start count low byte  0
+    wctdm_setreg(wc, card, 3, 0);    // Tx Start count high byte 0
+    wctdm_setreg(wc, card, 4, (3-(card%4)) * 8 + (card/4) * 64);    // Rx Start count low byte  0
+    wctdm_setreg(wc, card, 5, 0);    // Rx Start count high byte 0
+    wctdm_setreg(wc, card, 18, 0xff);     // clear all interrupt
+    wctdm_setreg(wc, card, 19, 0xff);
+    wctdm_setreg(wc, card, 20, 0xff);
+    wctdm_setreg(wc, card, 73, 0x04);
+	if (fxshonormode) {
+		fxsmode = acim2tiss[fxo_modes[_opermode].acim];
+		wctdm_setreg(wc, card, 10, 0x08 | fxsmode);
+		if (fxo_modes[_opermode].ring_osc)
+			wctdm_proslic_setreg_indirect(wc, card, 20, fxo_modes[_opermode].ring_osc);
+		if (fxo_modes[_opermode].ring_x)
+			wctdm_proslic_setreg_indirect(wc, card, 21, fxo_modes[_opermode].ring_x);
+	}
+    if (lowpower)
+    	wctdm_setreg(wc, card, 72, 0x10);
+
+#if 0
+    wctdm_setreg(wc, card, 21, 0x00); 	// enable interrupt
+    wctdm_setreg(wc, card, 22, 0x02); 	// Loop detection interrupt
+    wctdm_setreg(wc, card, 23, 0x01); 	// DTMF detection interrupt
+#endif
+
+#if 0
+    /* Enable loopback */
+    wctdm_setreg(wc, card, 8, 0x2);
+    wctdm_setreg(wc, card, 14, 0x0);
+    wctdm_setreg(wc, card, 64, 0x0);
+    wctdm_setreg(wc, card, 1, 0x08);
+#endif
+
+	/* Beef up Ringing voltage to 89V */
+	if (boostringer) {
+		if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x1d1)) 
+			return -1;
+		printk("Boosting ringinger on slot %d (89V peak)\n", card + 1);
+	} else if (lowpower) {
+		if (wctdm_proslic_setreg_indirect(wc, card, 21, 0x108)) 
+			return -1;
+		printk("Reducing ring power on slot %d (50V peak)\n", card + 1);
+	}
+	wctdm_setreg(wc, card, 64, 0x01);
+	return 0;
+}
+
+
+static int wctdm_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data)
+{
+	struct wctdm_stats stats;
+	struct wctdm_regs regs;
+	struct wctdm_regop regop;
+	struct wctdm_echo_coefs echoregs;
+	struct wctdm *wc = chan->pvt;
+	int x;
+	switch (cmd) {
+	case ZT_ONHOOKTRANSFER:
+		if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS)
+			return -EINVAL;
+		if (get_user(x, (int *)data))
+			return -EFAULT;
+		wc->mod[chan->chanpos - 1].fxs.ohttimer = x << 3;
+		if (reversepolarity)
+			wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x6;	/* OHT mode when idle */
+		else
+			wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 0x2;
+		if (wc->mod[chan->chanpos - 1].fxs.lasttxhook == 0x1) {
+				/* Apply the change if appropriate */
+				if (reversepolarity)
+					wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x6;
+				else
+					wc->mod[chan->chanpos - 1].fxs.lasttxhook = 0x2;
+				wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook);
+		}
+		break;
+	case ZT_SETPOLARITY:
+		if (get_user(x, (int *)data))
+			return -EFAULT;
+		if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS)
+			return -EINVAL;
+		/* Can't change polarity while ringing or when open */
+		if ((wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x04) ||
+		    (wc->mod[chan->chanpos -1 ].fxs.lasttxhook == 0x00))
+			return -EINVAL;
+
+		if ((x && !reversepolarity) || (!x && reversepolarity))
+			wc->mod[chan->chanpos - 1].fxs.lasttxhook |= 0x04;
+		else
+			wc->mod[chan->chanpos - 1].fxs.lasttxhook &= ~0x04;
+		wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos - 1].fxs.lasttxhook);
+		break;
+	case WCTDM_GET_STATS:
+		if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) {
+			stats.tipvolt = wctdm_getreg(wc, chan->chanpos - 1, 80) * -376;
+			stats.ringvolt = wctdm_getreg(wc, chan->chanpos - 1, 81) * -376;
+			stats.batvolt = wctdm_getreg(wc, chan->chanpos - 1, 82) * -376;
+		} else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) {
+			stats.tipvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000;
+			stats.ringvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000;
+			stats.batvolt = (signed char)wctdm_getreg(wc, chan->chanpos - 1, 29) * 1000;
+		} else 
+			return -EINVAL;
+		if (copy_to_user((struct wctdm_stats *)data, &stats, sizeof(stats)))
+			return -EFAULT;
+		break;
+	case WCTDM_GET_REGS:
+		if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) {
+			for (x=0;x<NUM_INDIRECT_REGS;x++)
+				regs.indirect[x] = wctdm_proslic_getreg_indirect(wc, chan->chanpos -1, x);
+			for (x=0;x<NUM_REGS;x++)
+				regs.direct[x] = wctdm_getreg(wc, chan->chanpos - 1, x);
+		} else {
+			memset(&regs, 0, sizeof(regs));
+			for (x=0;x<NUM_FXO_REGS;x++)
+				regs.direct[x] = wctdm_getreg(wc, chan->chanpos - 1, x);
+		}
+		if (copy_to_user((struct wctdm_regs *)data, &regs, sizeof(regs)))
+			return -EFAULT;
+		break;
+	case WCTDM_SET_REG:
+		if (copy_from_user(&regop, (struct wctdm_regop *)data, sizeof(regop)))
+			return -EFAULT;
+		if (regop.indirect) {
+			if (wc->modtype[chan->chanpos - 1] != MOD_TYPE_FXS)
+				return -EINVAL;
+			printk("Setting indirect %d to 0x%04x on %d\n", regop.reg, regop.val, chan->chanpos);
+			wctdm_proslic_setreg_indirect(wc, chan->chanpos - 1, regop.reg, regop.val);
+		} else {
+			regop.val &= 0xff;
+			printk("Setting direct %d to %04x on %d\n", regop.reg, regop.val, chan->chanpos);
+			wctdm_setreg(wc, chan->chanpos - 1, regop.reg, regop.val);
+		}
+		break;
+	case WCTDM_SET_ECHOTUNE:
+		printk("-- Setting echo registers: \n");
+		if (copy_from_user(&echoregs, (struct wctdm_echo_coefs*)data, sizeof(echoregs)))
+			return -EFAULT;
+
+		if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) {
+			/* Set the ACIM register */
+			wctdm_setreg(wc, chan->chanpos - 1, 30, echoregs.acim);
+
+			/* Set the digital echo canceller registers */
+			wctdm_setreg(wc, chan->chanpos - 1, 45, echoregs.coef1);
+			wctdm_setreg(wc, chan->chanpos - 1, 46, echoregs.coef2);
+			wctdm_setreg(wc, chan->chanpos - 1, 47, echoregs.coef3);
+			wctdm_setreg(wc, chan->chanpos - 1, 48, echoregs.coef4);
+			wctdm_setreg(wc, chan->chanpos - 1, 49, echoregs.coef5);
+			wctdm_setreg(wc, chan->chanpos - 1, 50, echoregs.coef6);
+			wctdm_setreg(wc, chan->chanpos - 1, 51, echoregs.coef7);
+			wctdm_setreg(wc, chan->chanpos - 1, 52, echoregs.coef8);
+
+			printk("-- Set echo registers successfully\n");
+
+			break;
+		} else {
+			return -EINVAL;
+
+		}
+		break;
+	default:
+		return -ENOTTY;
+	}
+	return 0;
+
+}
+
+static int wctdm_open(struct zt_chan *chan)
+{
+	struct wctdm *wc = chan->pvt;
+	if (!(wc->cardflag & (1 << (chan->chanpos - 1))))
+		return -ENODEV;
+	if (wc->dead)
+		return -ENODEV;
+	wc->usecount++;
+#ifndef LINUX26
+	MOD_INC_USE_COUNT;
+#else
+	try_module_get(THIS_MODULE);
+#endif	
+	return 0;
+}
+
+static int wctdm_watchdog(struct zt_span *span, int event)
+{
+	printk("opvxa1200: Restarting DMA\n");
+	wctdm_restart_dma(span->pvt);
+	return 0;
+}
+
+static int wctdm_close(struct zt_chan *chan)
+{
+	struct wctdm *wc = chan->pvt;
+	wc->usecount--;
+#ifndef LINUX26
+	MOD_DEC_USE_COUNT;
+#else
+	module_put(THIS_MODULE);
+#endif
+	if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) {
+		if (reversepolarity)
+			wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 5;
+		else
+			wc->mod[chan->chanpos - 1].fxs.idletxhookstate = 1;
+	}
+	/* If we're dead, release us now */
+	if (!wc->usecount && wc->dead) 
+		wctdm_release(wc);
+	return 0;
+}
+
+static int wctdm_hooksig(struct zt_chan *chan, zt_txsig_t txsig)
+{
+	struct wctdm *wc = chan->pvt;
+	int reg=0;
+	if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXO) {
+		/* XXX Enable hooksig for FXO XXX */
+		switch(txsig) {
+		case ZT_TXSIG_START:
+		case ZT_TXSIG_OFFHOOK:
+			wc->mod[chan->chanpos - 1].fxo.offhook = 1;
+			wctdm_setreg(wc, chan->chanpos - 1, 5, 0x9);
+			break;
+		case ZT_TXSIG_ONHOOK:
+			wc->mod[chan->chanpos - 1].fxo.offhook = 0;
+			wctdm_setreg(wc, chan->chanpos - 1, 5, 0x8);
+			break;
+		default:
+			printk("wcfxo: Can't set tx state to %d\n", txsig);
+		}
+	} else {
+		switch(txsig) {
+		case ZT_TXSIG_ONHOOK:
+			switch(chan->sig) {
+			case ZT_SIG_EM:
+			case ZT_SIG_FXOKS:
+			case ZT_SIG_FXOLS:
+				wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate;
+				break;
+			case ZT_SIG_FXOGS:
+				wc->mod[chan->chanpos-1].fxs.lasttxhook = 3;
+				break;
+			}
+			break;
+		case ZT_TXSIG_OFFHOOK:
+			switch(chan->sig) {
+			case ZT_SIG_EM:
+				wc->mod[chan->chanpos-1].fxs.lasttxhook = 5;
+				break;
+			default:
+				wc->mod[chan->chanpos-1].fxs.lasttxhook = wc->mod[chan->chanpos-1].fxs.idletxhookstate;
+				break;
+			}
+			break;
+		case ZT_TXSIG_START:
+			wc->mod[chan->chanpos-1].fxs.lasttxhook = 4;
+			break;
+		case ZT_TXSIG_KEWL:
+			wc->mod[chan->chanpos-1].fxs.lasttxhook = 0;
+			break;
+		default:
+			printk("opvxa1200: Can't set tx state to %d\n", txsig);
+		}
+		if (debug)
+			printk("Setting FXS hook state to %d (%02x)\n", txsig, reg);
+
+#if 1
+		wctdm_setreg(wc, chan->chanpos - 1, 64, wc->mod[chan->chanpos-1].fxs.lasttxhook);
+#endif
+	}
+	return 0;
+}
+
+static int wctdm_initialize(struct wctdm *wc)
+{
+	int x;
+
+	/* Zapata stuff */
+	sprintf(wc->span.name, "OPVXA1200/%d", wc->pos);
+	sprintf(wc->span.desc, "%s Board %d", wc->variety, wc->pos + 1);
+	if (alawoverride) {
+		printk("ALAW override parameter detected.  Device will be operating in ALAW\n");
+		wc->span.deflaw = ZT_LAW_ALAW;
+	} else
+		wc->span.deflaw = ZT_LAW_MULAW;
+	for (x = 0; x < NUM_CARDS; x++) {
+		sprintf(wc->chans[x].name, "OPVXA1200/%d/%d", wc->pos, x);
+		wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR;
+		wc->chans[x].sigcap |= ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF | ZT_SIG_CLEAR;
+		wc->chans[x].chanpos = x+1;
+		wc->chans[x].pvt = wc;
+	}
+	wc->span.chans = wc->chans;
+	wc->span.channels = NUM_CARDS;
+	wc->span.hooksig = wctdm_hooksig;
+	wc->span.open = wctdm_open;
+	wc->span.close = wctdm_close;
+	wc->span.flags = ZT_FLAG_RBS;
+	wc->span.ioctl = wctdm_ioctl;
+	wc->span.watchdog = wctdm_watchdog;
+	init_waitqueue_head(&wc->span.maintq);
+
+	wc->span.pvt = wc;
+	if (zt_register(&wc->span, 0)) {
+		printk("Unable to register span with zaptel\n");
+		return -1;
+	}
+	return 0;
+}
+
+static void wctdm_post_initialize(struct wctdm *wc)
+{
+	int x;
+	/* Finalize signalling  */
+	for (x = 0; x < NUM_CARDS; x++) {
+		if (wc->cardflag & (1 << x)) {
+			if (wc->modtype[x] == MOD_TYPE_FXO)
+				wc->chans[x].sigcap = ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF | ZT_SIG_CLEAR;
+			else
+				wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_SF | ZT_SIG_EM | ZT_SIG_CLEAR;
+		}
+	}
+}
+
+static int wctdm_hardware_init(struct wctdm *wc)
+{
+	/* Hardware stuff */
+	unsigned char ver;
+	unsigned char x,y;
+	int failed;
+	//long origjiffies; //ml.
+	
+	/* Signal Reset */
+	//printk("before raise reset\n");
+	outb(0x01, wc->ioaddr + WC_CNTL);
+
+	/* Wait for 2 second */
+	/*
+	origjiffies = jiffies;
+
+	while(1) 
+	{
+		if ((jiffies - origjiffies) >= (HZ*5))
+			break;;
+	}
+
+	printk("after raise reset\n");*/
+
+	/* Check OpenVox chip */
+	x=inb(wc->ioaddr + WC_CNTL);
+	ver = __wctdm_getcreg(wc, WC_VER);
+	wc->fwversion = ver;
+	printk("OpenVox A1200P version: %01x.%01x\n", ver>>4, ver&0x0f);
+	failed = 0;
+	if (ver != 0x00) {
+		for (x=0;x<16;x++) {
+			/* Test registers */
+			__wctdm_setcreg(wc, WC_CS, x);
+			y = __wctdm_getcreg(wc, WC_CS) & 0x0f;
+			if (x != y) {
+				printk("%02x != %02x\n", x, y);
+				failed++;
+			}
+		}
+
+		if (!failed) {
+			printk("OpenVox A1200P passed register test\n");
+		} else {
+			printk("OpenVox A1200P failed register test\n");
+			return -1;
+		}
+	} else {
+		printk("No OpenVox chip %02x\n", ver);
+	}
+
+	if (spibyhw)
+		__wctdm_setcreg(wc, WC_SPICTRL, BIT_SPI_BYHW);	// spi controled by hw MiaoLin;
+	else
+		__wctdm_setcreg(wc, WC_SPICTRL, 0);	
+		
+	/* Reset PCI Interface chip and registers (and serial) */
+	outb(0x06, wc->ioaddr + WC_CNTL);
+	/* Setup our proper outputs for when we switch for our "serial" port */
+	wc->ios = BIT_CS | BIT_SCLK | BIT_SDI;
+
+	outb(wc->ios, wc->ioaddr + WC_AUXD);
+
+	/* Set all to outputs except AUX 5, which is an input */
+	outb(0xdf, wc->ioaddr + WC_AUXC);
+
+	/* Select alternate function for AUX0 *///MiaoLin modify it to normal io line.
+	//outb(0x4, wc->ioaddr + WC_AUXFUNC);
+	
+	/* Wait 1/4 of a sec */
+	wait_just_a_bit(HZ/4);
+
+	/* Back to normal, with automatic DMA wrap around */
+	outb(0x30 | 0x01, wc->ioaddr + WC_CNTL);
+	wc->ledstate = 0;
+	wctdm_set_led(wc, 0, 0);
+	
+	/* Make sure serial port and DMA are out of reset */
+	outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, WC_CNTL);
+	
+	/* Configure serial port for MSB->LSB operation */
+	//outb(0xc1, wc->ioaddr + WC_SERCTL);
+	outb(0xc1, wc->ioaddr + WC_SERCTL);
+
+	/* Delay FSC by 0 so it's properly aligned */
+	//outb(0x0, wc->ioaddr + WC_FSCDELAY);
+	outb(0x01, wc->ioaddr + WC_FSCDELAY);
+
+	/* Setup DMA Addresses */
+	outl(wc->writedma,                    wc->ioaddr + WC_DMAWS);		/* Write start */
+	outl(wc->writedma + ZT_CHUNKSIZE * 4 * 4 - 4, wc->ioaddr + WC_DMAWI);		/* Middle (interrupt) */
+	outl(wc->writedma + ZT_CHUNKSIZE * 8 * 4 - 4, wc->ioaddr + WC_DMAWE);			/* End */
+	
+	outl(wc->readdma,                    	 wc->ioaddr + WC_DMARS);	/* Read start */
+	outl(wc->readdma + ZT_CHUNKSIZE * 4 * 4 - 4, 	 wc->ioaddr + WC_DMARI);	/* Middle (interrupt) */
+	outl(wc->readdma + ZT_CHUNKSIZE * 8 * 4 - 4, wc->ioaddr + WC_DMARE);	/* End */
+	
+	/* Clear interrupts */
+	outb(0xff, wc->ioaddr + WC_INTSTAT);
+
+	/* Wait 1/4 of a second more */
+	wait_just_a_bit(HZ/4);
+
+	for (x = 0; x < NUM_CARDS; x++) {
+		int sane=0,ret=0,readi=0;
+#if 1
+		/* Init with Auto Calibration */
+		if (!(ret=wctdm_init_proslic(wc, x, 0, 0, sane))) {
+			wc->cardflag |= (1 << x);
+                        if (debug) {
+                                readi = wctdm_getreg(wc,x,LOOP_I_LIMIT);
+                                printk("Proslic module %d loop current is %dmA\n",x,
+                                ((readi*3)+20));
+                        }
+			printk("Module %d: Installed -- AUTO FXS/DPO\n",x);
+			wctdm_set_led(wc, (unsigned int)x, 1);
+		} else {
+			if(ret!=-2) {
+				sane=1;
+				
+				printk("Init ProSlic with Manual Calibration \n");
+				/* Init with Manual Calibration */
+				if (!wctdm_init_proslic(wc, x, 0, 1, sane)) {
+					wc->cardflag |= (1 << x);
+                                if (debug) {
+                                        readi = wctdm_getreg(wc,x,LOOP_I_LIMIT);
+                                        printk("Proslic module %d loop current is %dmA\n",x,
+                                        ((readi*3)+20));
+                                }
+					printk("Module %d: Installed -- MANUAL FXS\n",x);
+				} else {
+					printk("Module %d: FAILED FXS (%s)\n", x, fxshonormode ? fxo_modes[_opermode].name : "FCC");
+				} 
+			} else if (!(ret = wctdm_init_voicedaa(wc, x, 0, 0, sane))) {
+				wc->cardflag |= (1 << x);
+				printk("Module %d: Installed -- AUTO FXO (%s mode)\n",x, fxo_modes[_opermode].name);
+				wctdm_set_led(wc, (unsigned int)x, 1);
+			} else
+				printk("Module %d: Not installed\n", x);
+		}
+#endif
+	}
+
+	/* Return error if nothing initialized okay. */
+	if (!wc->cardflag && !timingonly)
+		return -1;
+	//__wctdm_setcreg(wc, WC_SYNC, (wc->cardflag << 1) | 0x1);
+	return 0;
+}
+
+static void wctdm_enable_interrupts(struct wctdm *wc)
+{
+	/* Clear interrupts */
+	outb(0xff, wc->ioaddr + WC_INTSTAT);
+
+	/* Enable interrupts (we care about all of them) */
+	outb(0x3c /*0x3f*/, wc->ioaddr + WC_MASK0);
+	/* No external interrupts */
+	outb(0x00, wc->ioaddr + WC_MASK1);
+}
+
+static void wctdm_restart_dma(struct wctdm *wc)
+{
+	/* Reset Master and TDM */
+	outb(0x01, wc->ioaddr + WC_CNTL);
+	outb(0x01, wc->ioaddr + WC_OPER);
+}
+
+static void wctdm_start_dma(struct wctdm *wc)
+{
+	/* Reset Master and TDM */
+	outb(0x0f, wc->ioaddr + WC_CNTL);
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(1);
+	outb(0x01, wc->ioaddr + WC_CNTL);
+	outb(0x01, wc->ioaddr + WC_OPER);
+}
+
+static void wctdm_stop_dma(struct wctdm *wc)
+{
+	outb(0x00, wc->ioaddr + WC_OPER);
+}
+
+static void wctdm_reset_tdm(struct wctdm *wc)
+{
+	/* Reset TDM */
+	outb(0x0f, wc->ioaddr + WC_CNTL);
+}
+
+static void wctdm_disable_interrupts(struct wctdm *wc)	
+{
+	outb(0x00, wc->ioaddr + WC_MASK0);
+	outb(0x00, wc->ioaddr + WC_MASK1);
+}
+
+static int __devinit wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int res;
+	struct wctdm *wc;
+	struct wctdm_desc *d = (struct wctdm_desc *)ent->driver_data;
+	int x;
+	int y;
+
+	static int initd_ifaces=0;
+	
+	if(initd_ifaces){
+		memset((void *)ifaces,0,(sizeof(struct wctdm *))*WC_MAX_IFACES);
+		initd_ifaces=1;
+	}
+	for (x=0;x<WC_MAX_IFACES;x++)
+		if (!ifaces[x]) break;
+	if (x >= WC_MAX_IFACES) {
+		printk("Too many interfaces\n");
+		return -EIO;
+	}
+	
+	if (pci_enable_device(pdev)) {
+		res = -EIO;
+	} else {
+		wc = kmalloc(sizeof(struct wctdm), GFP_KERNEL);
+		if (wc) {
+			int cardcount = 0;
+			
+			//wc->offset = 12;	// miaolin add.
+			wc->lastchan = -1;	// first channel offset = -1;
+			wc->ledstate = 0;
+			
+			ifaces[x] = wc;
+			memset(wc, 0, sizeof(struct wctdm));
+			spin_lock_init(&wc->lock);
+			wc->curcard = -1;
+			wc->ioaddr = pci_resource_start(pdev, 0);
+			wc->mem_region = pci_resource_start(pdev, 1);
+			wc->mem_len = pci_resource_len(pdev, 1);
+			wc->mem32 = (unsigned long)ioremap(wc->mem_region, wc->mem_len);
+			wc->dev = pdev;
+			wc->pos = x;
+			wc->variety = d->name;
+			for (y=0;y<NUM_CARDS;y++)
+				wc->flags[y] = d->flags;
+			/* Keep track of whether we need to free the region */
+			if (request_region(wc->ioaddr, 0xff, "opvxa1200")) 
+				wc->freeregion = 1;
+			else
+				wc->freeregion = 0;
+			
+			if (request_mem_region(wc->mem_region, wc->mem_len, "opvxa1200"))
+				wc->freeregion |= 0x02;
+
+			/* Allocate enough memory for two zt chunks, receive and transmit.  Each sample uses
+			   8 bits.  */
+			wc->writechunk = pci_alloc_consistent(pdev, ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2 * 2, &wc->writedma);
+			if (!wc->writechunk) {
+				printk("opvxa1200: Unable to allocate DMA-able memory\n");
+				if (wc->freeregion & 0x01)
+					release_region(wc->ioaddr, 0xff);
+				if (wc->freeregion & 0x02);
+				{
+					release_mem_region(wc->mem_region, wc->mem_len);
+					iounmap((void *)wc->mem32);
+				}
+				return -ENOMEM;
+			}
+
+			wc->readchunk = wc->writechunk + ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2;	/* in bytes */
+			wc->readdma = wc->writedma + ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2;	/* in bytes */
+			
+			if (wctdm_initialize(wc)) {
+				printk("opvxa1200: Unable to intialize FXS\n");
+				/* Set Reset Low */
+				x=inb(wc->ioaddr + WC_CNTL);
+				outb((~0x1)&x, wc->ioaddr + WC_CNTL);
+				/* Free Resources */
+				free_irq(pdev->irq, wc);
+				if (wc->freeregion & 0x01)
+					release_region(wc->ioaddr, 0xff);
+				if (wc->freeregion & 0x02);
+				{
+					release_mem_region(wc->mem_region, wc->mem_len);
+					iounmap((void *)wc->mem32);
+				}
+				pci_free_consistent(pdev,  ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma);
+				kfree(wc);
+				return -EIO;
+			}
+
+			/* Enable bus mastering */
+			pci_set_master(pdev);
+
+			/* Keep track of which device we are */
+			pci_set_drvdata(pdev, wc);
+
+			if (request_irq(pdev->irq, wctdm_interrupt, SA_SHIRQ, "opvxa1200", wc)) {
+				printk("opvxa1200: Unable to request IRQ %d\n", pdev->irq);
+				if (wc->freeregion & 0x01)
+					release_region(wc->ioaddr, 0xff);
+				if (wc->freeregion & 0x02);
+				{
+					release_mem_region(wc->mem_region, wc->mem_len);
+					iounmap((void *)wc->mem32);
+				}
+				pci_free_consistent(pdev,  ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma);
+				pci_set_drvdata(pdev, NULL);
+				kfree(wc);
+				return -EIO;
+			}
+
+
+			if (wctdm_hardware_init(wc)) {
+				unsigned char x;
+
+				/* Set Reset Low */
+				x=inb(wc->ioaddr + WC_CNTL);
+				outb((~0x1)&x, wc->ioaddr + WC_CNTL);
+				/* Free Resources */
+				free_irq(pdev->irq, wc);
+				if (wc->freeregion & 0x01)
+					release_region(wc->ioaddr, 0xff);
+				if (wc->freeregion & 0x02);
+				{
+					release_mem_region(wc->mem_region, wc->mem_len);
+					iounmap((void *)wc->mem32);
+				}
+				pci_free_consistent(pdev,  ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma);
+				pci_set_drvdata(pdev, NULL);
+				zt_unregister(&wc->span);
+				kfree(wc);
+				return -EIO;
+
+			}
+
+#ifdef TEST_LOG_INCOME_VOICE
+			for(i=0; i<NUM_CARDS+NUM_FLAG; i++)
+			{
+				wc->voc_buf[i] = kmalloc(voc_buffer_size, GFP_KERNEL);
+				wc->voc_ptr[i] = 0;
+			}
+#endif
+			
+			wctdm_post_initialize(wc);
+
+			/* Enable interrupts */
+			wctdm_enable_interrupts(wc);
+			/* Initialize Write/Buffers to all blank data */
+			memset((void *)wc->writechunk,0, ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2 * 2);
+
+			/* Start DMA */
+			wctdm_start_dma(wc);
+
+			for (x = 0; x < NUM_CARDS; x++) {
+				if (wc->cardflag & (1 << x))
+					cardcount++;
+			}
+
+			printk("Found a OpenVox A1200P: Version %01x.%01x (%d modules)\n", wc->fwversion>>4, wc->fwversion&0x0f, cardcount);
+			
+			res = 0;
+		} else
+			res = -ENOMEM;
+	}
+	return res;
+}
+
+static void wctdm_release(struct wctdm *wc)
+{
+#ifdef TEST_LOG_INCOME_VOICE
+	struct file * f = NULL;
+	mm_segment_t orig_fs;
+	int i;
+	char fname[20];
+#endif
+	
+	zt_unregister(&wc->span);
+	if (wc->freeregion & 0x01)
+		release_region(wc->ioaddr, 0xff);
+	if (wc->freeregion & 0x02);
+	{
+		release_mem_region(wc->mem_region, wc->mem_len);
+		iounmap((void *)wc->mem32);
+	}
+	
+#ifdef TEST_LOG_INCOME_VOICE
+	for(i=0; i<NUM_CARDS + NUM_FLAG; i++)
+	{
+		sprintf(fname, "//usr//%d.pcm", i); 
+		f = filp_open(fname, O_RDWR|O_CREAT, 00);
+	
+		if (!f || !f->f_op || !f->f_op->read)
+		{
+			printk("WARNING: File (read) object is a null pointer!!!\n");
+			continue;
+		}
+	
+		f->f_pos = 0;
+		
+		orig_fs = get_fs();
+		set_fs(KERNEL_DS); 
+		
+		if(wc->voc_buf[i])
+		{
+			f->f_op->write(f, wc->voc_buf[i], voc_buffer_size, &f->f_pos);
+			kfree(wc->voc_buf[i]);
+		}
+		
+		set_fs(orig_fs); 
+		fput(f);
+	}
+#endif
+ 
+	kfree(wc);
+	printk("Freed a OpenVox A1200 card\n");
+}
+
+static void __devexit wctdm_remove_one(struct pci_dev *pdev)
+{
+	struct wctdm *wc = pci_get_drvdata(pdev);
+	if (wc) {
+
+		/* Stop any DMA */
+		wctdm_stop_dma(wc);
+		wctdm_reset_tdm(wc);
+
+		/* In case hardware is still there */
+		wctdm_disable_interrupts(wc);
+		
+		/* Immediately free resources */
+		pci_free_consistent(pdev,  ZT_MAX_CHUNKSIZE * (NUM_CARDS+NUM_FLAG) * 2 * 2, (void *)wc->writechunk, wc->writedma);
+		free_irq(pdev->irq, wc);
+
+		/* Reset PCI chip and registers */
+		if(wc->fwversion > 0x11)
+			outb(0x0e, wc->ioaddr + WC_CNTL);
+		else
+		{
+			wc->ledstate = 0;
+			wctdm_set_led(wc,0,0);	// power off all leds.
+		}
+
+		/* Release span, possibly delayed */
+		if (!wc->usecount)
+			wctdm_release(wc);
+		else
+			wc->dead = 1;
+	}
+}
+
+static struct pci_device_id wctdm_pci_tbl[] = {
+	{ 0xe159, 0x0001, 0x9100, PCI_ANY_ID, 0, 0, (unsigned long) &wctdme },
+	{ 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, wctdm_pci_tbl);
+
+static struct pci_driver wctdm_driver = {
+	name: 	"opvxa1200",
+	probe: 	wctdm_init_one,
+#ifdef LINUX26
+	remove:	__devexit_p(wctdm_remove_one),
+#else
+	remove:	wctdm_remove_one,
+#endif
+	suspend: NULL,
+	resume:	NULL,
+	id_table: wctdm_pci_tbl,
+};
+
+static int __init wctdm_init(void)
+{
+	int res;
+	int x;
+	for (x=0;x<(sizeof(fxo_modes) / sizeof(fxo_modes[0])); x++) {
+		if (!strcmp(fxo_modes[x].name, opermode))
+			break;
+	}
+	if (x < sizeof(fxo_modes) / sizeof(fxo_modes[0])) {
+		_opermode = x;
+	} else {
+		printk("Invalid/unknown operating mode '%s' specified.  Please choose one of:\n", opermode);
+		for (x=0;x<sizeof(fxo_modes) / sizeof(fxo_modes[0]); x++)
+			printk("  %s\n", fxo_modes[x].name);
+		printk("Note this option is CASE SENSITIVE!\n");
+		return -ENODEV;
+	}
+
+	res = zap_pci_module(&wctdm_driver);
+	if (res)
+		return -ENODEV;
+	return 0;
+}
+
+static void __exit wctdm_cleanup(void)
+{
+	pci_unregister_driver(&wctdm_driver);
+}
+
+#ifdef LINUX26
+module_param(debug, int, 0600);
+module_param(loopcurrent, int, 0600);
+module_param(reversepolarity, int, 0600);
+module_param(robust, int, 0600);
+module_param(_opermode, int, 0600);
+module_param(opermode, charp, 0600);
+module_param(timingonly, int, 0600);
+module_param(lowpower, int, 0600);
+module_param(boostringer, int, 0600);
+module_param(fxshonormode, int, 0600);
+module_param(battdebounce, int, 0600);
+module_param(battthresh, int, 0600);
+module_param(alawoverride, int, 0600);
+module_param(spibyhw, int, 0600);
+module_param(usememio, int, 0600);
+#else
+MODULE_PARM(debug, "i");
+MODULE_PARM(loopcurrent, "i");
+MODULE_PARM(reversepolarity, "i");
+MODULE_PARM(robust, "i");
+MODULE_PARM(_opermode, "i");
+MODULE_PARM(opermode, "s");
+MODULE_PARM(timingonly, "i");
+MODULE_PARM(lowpower, "i");
+MODULE_PARM(boostringer, "i");
+MODULE_PARM(fxshonormode, "i");
+MODULE_PARM(battdebounce, "i");
+MODULE_PARM(battthresh, "i");
+MODULE_PARM(alawoverride, "i");
+MODULE_PARM(spibyhw, "i");
+MODULE_PARM(usememio, "i");
+#endif
+MODULE_DESCRIPTION("OpenVox A1200 Zaptel Driver");
+MODULE_AUTHOR("MiaoLin <miaolin at openvox.com.cn>");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+module_init(wctdm_init);
+module_exit(wctdm_cleanup);




More information about the Pkg-voip-commits mailing list