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

Tzafrir Cohen tzafrir-guest at costa.debian.org
Tue Jan 3 21:05:49 UTC 2006


Author: tzafrir-guest
Date: 2006-01-03 21:05:47 +0000 (Tue, 03 Jan 2006)
New Revision: 1127

Modified:
   zaptel/trunk/debian/genzaptelconf
   zaptel/trunk/debian/genzaptelconf.8
   zaptel/trunk/debian/patches/xpp.dpatch
Log:
* genzaptelconf: version 0.41. Bugfixes.
* xpp.dpatch: newer version


Modified: zaptel/trunk/debian/genzaptelconf
===================================================================
--- zaptel/trunk/debian/genzaptelconf	2006-01-03 21:00:14 UTC (rev 1126)
+++ zaptel/trunk/debian/genzaptelconf	2006-01-03 21:05:47 UTC (rev 1127)
@@ -30,7 +30,6 @@
 #
 # If you have any technical questions, contact 
 # Tzafrir Cohen <tzafrir.cohen at xorcom.com>
-# $Id:$
 #	
 
 # The script uses a number of bash-specific features
@@ -38,10 +37,9 @@
 # Don't override variables here. Override them in /etc/default/zaptel
 
 # /etc/default/zaptel may override the following variables
-VERSION=0.4.0
+VERSION=0.4.1
+VERSION_FULL="$VERSION $Id: genzaptelconf 84 2006-01-01 18:52:28Z tzafrir $"
 lc_country=us
-#method=ls
-#method=ks
 base_exten=6000
 # If set: no context changes are made in zapata-channels.conf
 #context_manual=yes
@@ -191,7 +189,7 @@
 usage() {
 	program=`basename $0`
 
-	echo >&2 "$program: generate zaptel.conf (version $VERSION)"
+	echo >&2 "$program: generate zaptel.conf (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"
@@ -201,7 +199,6 @@
 	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 "  -m: set signalling method (gs/ks/ls, default: $method)"
 	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"
@@ -216,6 +213,10 @@
 	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)	echo "${sig}$method=$chan" ;;
 	list) echo $chan $sig;;
@@ -275,7 +276,8 @@
 			then 
 				echo "context=$context_phones"
 			fi
-		else # we have may have set it. So reset it:
+		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" ]
@@ -286,12 +288,20 @@
 			then 
 				echo "context=$context_lines"
 			fi
-			echo "cidsignalling=v23"
-			case $line in 
-			*WCFXO*) echo "cidstart=history";;
-			*)       echo "cidstart=polarity";; #a TDM400
-			esac
+			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
 
 		echo "channel => $chan"
@@ -681,12 +691,6 @@
 		u) do_unload=yes ;;
 		v) verbose=yes ;;
 		l) mode='list' ;;
-		m) 
-		  case "$OPTARG" in 
-			k|l|g)method=${OPTARG}s ;;
-			*) echo >&2 "unknown signalling method ${OPTARG}s, defaulting to \"$method\"";;
-			esac
-			;;
 		M) do_module_list=yes; do_detect=yes ;;
 		s) force_stop_ast=yes ;;
 		r) 
@@ -711,7 +715,6 @@
 	# 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):;;
-	de)lc_country=us;; # used in earlier versions that did not have 'de'
 	*) 
 		lc_country=us
 		echo >&2 "unknown country-code $lc_country, defaulting to \"us\""
@@ -722,14 +725,6 @@
 loadzone=$lc_country
 defaultzone=$loadzone
 
-# Choose reasonable default for signaling method (by country)
-if [ "" = "$method" ]; then
-	case "$lc_country" in
-	il) method=ls	;;
-	*) method=ks	;;
-	esac
-fi
-
 # make sure asterisk is not in our way
 if [ "$force_stop_ast" = 'yes' ]
 then

Modified: zaptel/trunk/debian/genzaptelconf.8
===================================================================
--- zaptel/trunk/debian/genzaptelconf.8	2006-01-03 21:00:14 UTC (rev 1126)
+++ zaptel/trunk/debian/genzaptelconf.8	2006-01-03 21:05:47 UTC (rev 1127)
@@ -5,7 +5,7 @@
 .SH SYNOPSIS
 .PP 
 .B genzaptelconf 
-[-sdv] [-m k|l] [-c <country_code>] [-r |-e <base_exten>] 
+[-sdv] [-c <country_code>] [-r |-e <base_exten>] 
 
 .B genzaptelconf 
 [-sdv] -l -- only list to standard output
@@ -27,7 +27,7 @@
 .I PRI
 and 
 .I BRI
-(HFC, with ZapHFC) cards are basically identified as well. However the span
+(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
@@ -93,19 +93,6 @@
 option as well.
 .RE
 
-
-.B -m k|l
-.RS
-Sets the signalling mode: Kewlstart or Loopstart. By default the 
-signalling mode is ks for any country other than Israel (il), where it is 
-ls .
-
-Alternatively set the value of
-.I method
-in 
-.I /etc/default/zaptel
-.RE
-
 .B -r
 .RS
 Try to guess a useful
@@ -184,8 +171,58 @@
 .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 

Modified: zaptel/trunk/debian/patches/xpp.dpatch
===================================================================
--- zaptel/trunk/debian/patches/xpp.dpatch	2006-01-03 21:00:14 UTC (rev 1126)
+++ zaptel/trunk/debian/patches/xpp.dpatch	2006-01-03 21:05:47 UTC (rev 1127)
@@ -2,12 +2,1395 @@
 ## xpp.dpatch by Tzafrir Cohen <tzafrir.cohen at xorcom.com>
 ##
 ## All lines beginning with `## DP:' are a description of the patch.
-## DP: No description.
+## DP: The zaptel drivers for the Xorcom Astribank and friends.
+## DP: Revision 168.
 
 @DPATCH@
-diff -urNad trunk/xpp/FPGA_bulkloop.hex /tmp/dpep.oAj2YR/trunk/xpp/FPGA_bulkloop.hex
---- trunk/xpp/FPGA_bulkloop.hex	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/FPGA_bulkloop.hex	2005-09-22 18:16:26.000000000 +0300
+diff -urNad zaptel-1.0.10/xpp/card_fxs.c /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/card_fxs.c
+--- zaptel-1.0.10/xpp/card_fxs.c	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/card_fxs.c	2006-01-03 15:08:38.089954000 +0200
+@@ -0,0 +1,548 @@
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2005, 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/module.h>
++#include "xpd.h"
++#include "xproto.h"
++#include "xpp_zap.h"
++#include <linux/delay.h>
++
++static const char rcsid[] = "$Id: card_fxs.c 168 2006-01-03 12:51:06Z oron $";
++
++extern	int print_dbg;
++
++/*---------------- FXS Protocol Commands ----------------------------------*/
++static bool fxs_packet_is_valid(xpacket_t *pack);
++static void fxs_packet_dump(xpacket_t *pack);
++
++#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"
++};
++#undef	S_
++
++/*---------------- FXS: Methods -------------------------------------------*/
++
++static int FXS_card_new(xbus_t *xbus, xpd_t *xpd, byte rev)
++{
++	int	channels = min(8, CHANNELS_PERXPD);
++
++	xpd->direction = TO_PHONE;
++	if(xpd->id == 0) {
++		DBG("First XPD detected. Initialize digital outputs\n");
++		channels += 2;
++		xpd->digital_outputs = BIT(8) | BIT(9);	// Two extra channels
++	}
++	xpd->channels = channels;
++	return 0;
++}
++
++static int FXS_card_init(xbus_t *xbus, xpd_t *xpd)
++{
++	CALL_PROTO(FXS, SLIC_INIT, xbus, xpd);
++	return 0;
++}
++
++static int FXS_card_remove(xbus_t *xbus, xpd_t *xpd)
++{
++	return 0;
++}
++
++xops_t FXS_xops = {
++	.card_new	= FXS_card_new,
++	.card_init	= FXS_card_init,
++	.card_remove	= FXS_card_remove,
++
++	.RING		= XPROTO_CALLER(FXS, RING),
++	.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),
++	.PCM_WRITE	= XPROTO_CALLER(GLOBAL, PCM_WRITE),
++};
++
++/*---------------- FXS: HOST COMMANDS -------------------------------------*/
++
++/* 0x0F */ HOSTCMD(FXS, CHAN_ENABLE, 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 Activation: 0x%4X %s\n", lines, (on) ? "on" : "off");
++	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);
++	pack->datalen = len;
++
++	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);
++	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;
++}
++
++/* 0x0F */ HOSTCMD(FXS, CHAN_CID, xpp_line_t lines)
++{
++	int		ret = 0;
++	xpacket_t	*pack;
++	slic_cmd_t	*sc;
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	lines &= xpd->enabled_chans;	// Ignore disabled channels
++	if(!lines) {
++		return 0;
++	}
++	DBG("Channel CID: 0x%04X\n", lines);
++	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);
++	packet_send(xbus, pack);
++	return ret;
++}
++
++
++/* 0x0F */ HOSTCMD(FXS, 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 pos=%d %s\n", xpd->xpdname, pos, (on) ? "on" : "off");
++	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);
++	pack->datalen = len;
++
++	packet_send(xbus, pack);
++	return ret;
++}
++
++/* 0x0F */ HOSTCMD(FXS, SETHOOK, xpp_line_t hook_status)
++{
++	DBG("\n");
++	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)
++{
++	int		ret = 0;
++	xpacket_t	*pack;
++	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];
++	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("RELAY_OUT pack: line=%d value=0x%04X\n", lines, value);
++	pack->datalen = len;
++	packet_send(xbus, pack);
++	return ret;
++}
++
++/* 0x0F */ HOSTCMD(FXS, SLIC_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 SLIC\n");
++	for(i = 0; i < ARRAY_SIZE(slic_init_data); i++) {
++		source = &slic_init_data[i];
++		XPACKET_NEW(pack, xbus, FXS, SLIC_INIT, xpd->id);
++		RPACKET_FIELD(pack, FXS, SLIC_INIT, lines) = source->lines;
++
++		slic = &RPACKET_FIELD(pack, FXS, SLIC_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("SLIC", pack, print_dbg);
++		packet_send(xbus, pack);
++		mdelay(10);	// FIXME: check with Dima
++	}
++	return ret;
++}
++
++/* 0x0F */ HOSTCMD(FXS, SLIC_QUERY, int pos, byte reg_num)
++{
++	DBG("\n");
++	return 0;
++}
++
++/*---------------- FXS: Astribank Reply Handlers --------------------------*/
++
++HANDLER_DEF(FXS, SIG_CHANGED)
++{
++	xpp_line_t	sig_status = RPACKET_FIELD(pack, FXS, SIG_CHANGED, sig_status);
++
++	if(!xpd) {
++		NOTICE("%s: received %s for non-existing xpd: %d\n",
++				__FUNCTION__, cmd->name, XPD_NUM(pack->content.addr));
++		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;
++}
++
++HANDLER_DEF(FXS, SLIC_REPLY)
++{
++	slic_reply_t	*info = &RPACKET_FIELD(pack, FXS, SLIC_REPLY, info);
++	unsigned long	flags;
++
++	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);
++	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);
++	spin_unlock_irqrestore(&xpd->lock, flags);
++	return 0;
++}
++
++
++xproto_table_t PROTO_TABLE(FXS) = {
++	.entries = {
++		/*	Card	Opcode		*/
++		XENTRY(	FXS,	SIG_CHANGED	),
++		XENTRY(	FXS,	SLIC_REPLY	),
++	},
++	.name = "FXS",
++	.packet_is_valid = fxs_packet_is_valid,
++	.packet_dump = fxs_packet_dump,
++};
++
++static bool fxs_packet_is_valid(xpacket_t *pack)
++{
++	const xproto_entry_t	*xe;
++
++	DBG("\n");
++	xe = xproto_card_entry(&PROTO_TABLE(FXS), pack->content.opcode);
++	return xe != NULL;
++}
++
++static void fxs_packet_dump(xpacket_t *pack)
++{
++	DBG("\n");
++}
++
++/*------------------------- 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;
++}
++
++/*
++ *        Direct/Indirect
++ *              v
++ * FF FF FF FF WD 06 1
++ * ^---------^ ^  Reg
++ *      | Write/Read
++ *      |
++ *    SLIC #
++ */
++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;
++	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);
++	XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
++	RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd) = sc;
++	pack->datalen = len;
++	packet_send(xbus, pack);
++	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;
++}
++
++
+diff -urNad zaptel-1.0.10/xpp/card_fxs.h /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/card_fxs.h
+--- zaptel-1.0.10/xpp/card_fxs.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/card_fxs.h	2006-01-03 13:22:53.823218000 +0200
+@@ -0,0 +1,67 @@
++#ifndef	CARD_FXS_H
++#define	CARD_FXS_H
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2005, 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"
++#include "xproto.h"
++
++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 */
++	xpp_line_t	sig_toggles;	/* channels: lsb=1, msb=8 */
++	);
++DEF_RPACKET_DATA(FXS, SLIC_REPLY,	/* Get status of a single SLIC (for debugging) */
++	xpp_line_t	lines;
++	slic_reply_t	info;
++	);
++DEF_RPACKET_DATA(FXS, SLIC_INIT,
++	xpp_line_t      lines;
++	slic_data_t     slic_data;
++	);
++DEF_RPACKET_DATA(FXS, SLIC_WRITE,
++	slic_cmd_t	slic_cmd;
++	);
++
++#ifdef	__KERNEL__
++
++#include <linux/fs.h>
++
++/* 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);
++
++extern xproto_table_t PROTO_TABLE(FXS);
++extern xops_t FXS_xops;
++int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data);
++int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data);
++
++#endif
++
++#endif	/* CARD_FXS_H */
+diff -urNad zaptel-1.0.10/xpp/card_global.c /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/card_global.c
+--- zaptel-1.0.10/xpp/card_global.c	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/card_global.c	2006-01-03 15:08:38.089954000 +0200
+@@ -0,0 +1,270 @@
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2005, 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"
++#include "xpd.h"
++#include "xpp_zap.h"
++#include "xproto.h"
++#include <linux/module.h>
++
++static const char rcsid[] = "$Id: card_global.c 165 2006-01-03 12:36:57Z oron $";
++
++extern	int print_dbg;
++static bool pcm_valid(xpd_t *xpd, xpacket_t *pack);
++
++/*---------------- GLOBAL Protocol Commands -------------------------------*/
++
++static bool global_packet_is_valid(xpacket_t *pack);
++static void global_packet_dump(xpacket_t *pack);
++
++/*---------------- GLOBAL: HOST COMMANDS ----------------------------------*/
++
++/* 0x04 */ HOSTCMD(GLOBAL, DESC_REQ, int xpd_num)
++{
++	int		ret = 0;
++	xpacket_t	*pack;
++
++	if(!xbus) {
++		DBG("NO XBUS\n");
++		return -EINVAL;
++	}
++	XPACKET_NEW(pack, xbus, GLOBAL, DESC_REQ, xpd_num);
++	DBG("on %s #%d\n", xbus->busname, xpd_num);
++	ret = packet_send(xbus, pack);
++	XBUS_COUNTER(xbus, DESC_REQ)++;
++	return ret;
++}
++
++/* 0x11 */ HOSTCMD(GLOBAL, PCM_WRITE, xpp_line_t lines, volatile byte *buf)
++{
++	int	ret = 0;
++	xpacket_t	*pack;
++	byte		*pcm;
++	byte		*start_pcm;
++	int i;
++	extern ulong	pcm_gen;
++
++	BUG_ON(!xbus);
++	BUG_ON(!xpd);
++	lines &= xpd->enabled_chans;
++	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);
++
++	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++) {
++		if(IS_SET(lines, i)) {
++			memcpy(pcm, (byte *)buf, ZT_CHUNKSIZE);
++			pcm += ZT_CHUNKSIZE;
++		}
++		buf += ZT_CHUNKSIZE;
++	}
++	pack->datalen = sizeof(xpp_line_t) + (pcm - start_pcm);
++	packet_send(xbus, pack);
++	XPD_COUNTER(xpd, PCM_WRITE)++;
++	XBUS_COUNTER(xbus, PCM_WRITE)++;
++	return ret;
++}
++
++/* 0x19 */ HOSTCMD(GLOBAL, SYNC_SOURCE, bool setit, bool is_master)
++{
++	xpacket_t	*pack;
++	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);
++	XPACKET_NEW(pack, xbus, GLOBAL, SYNC_SOURCE, xpd->id);
++	RPACKET_FIELD(pack, GLOBAL, SYNC_SOURCE, mask) = mask;
++	packet_send(xbus, pack);
++	return 0;
++}
++
++xops_t GLOBAL_xops = {
++	.DESC_REQ = XPROTO_CALLER(GLOBAL, DESC_REQ),
++	.PCM_WRITE = XPROTO_CALLER(GLOBAL, PCM_WRITE),
++	.SYNC_SOURCE = XPROTO_CALLER(GLOBAL, SYNC_SOURCE),
++};
++
++/*---------------- GLOBAL: Astribank Reply Handlers -----------------------*/
++
++HANDLER_DEF(GLOBAL, DEV_DESC)
++{
++	byte		rev = RPACKET_FIELD(pack, GLOBAL, DEV_DESC, rev);
++	byte		type = RPACKET_FIELD(pack, GLOBAL, DEV_DESC, type);
++	xpp_line_t	line_status = RPACKET_FIELD(pack, GLOBAL, DEV_DESC, line_status);
++	int		xpd_num = XPD_NUM(pack->content.addr);
++	xops_t		*xops;
++
++	DBG("xpd=%d type=%d rev=%d line_status=0x%04X\n",
++			xpd_num, type, rev, line_status);
++	if(xpd) {
++		if(type == XPD_TYPE(NOMODULE)) {
++			NOTICE("DEV_DESC: xpd #%d: removed\n", xpd_num);
++			xpd_remove(xpd);
++			return 0;
++		}
++		NOTICE("DEV_DESC: xpd #%d: already exists\n", xpd_num);
++		return 0;
++	}
++	if(type == XPD_TYPE_NOMODULE) {
++		DBG("No module at address=%d\n", xpd_num);
++		return 0;
++	}
++	xops = get_xops(type);
++	if(!xops) {
++		NOTICE("DEV_DESC: xpd #%d: missing xops for type=%d\n", xpd_num, type);
++		return -EINVAL;
++	}
++	xpd = xpd_new(xbus, xpd_num, type, rev);
++	if(!xpd) {
++		NOTICE("xpd_new failed\n");
++		return -EINVAL;
++	}
++	xpp_check_hookstate(xpd, line_status);
++	return 0;
++}
++
++HANDLER_DEF(GLOBAL, PCM_READ)
++{
++	/* FIXME: work around temporary hardware bug */
++	xpp_line_t	lines = RPACKET_FIELD(pack, GLOBAL, PCM_READ, lines);
++	const byte	*pcm = RPACKET_FIELD(pack, GLOBAL, PCM_READ, pcm);
++	volatile u_char	*readchunk;
++	volatile u_char	*r;
++	unsigned long	flags;
++	int		i;
++
++	if(!xpd) {
++#if 0
++		int xpd_num = XPD_NUM(pack->content.addr);
++		NOTICE("%s: received %s for non-existing xpd: %d\n",
++				__FUNCTION__, cmd->name, xpd_num);
++#endif
++		return -EPROTO;
++	}
++	// DBG("lines=0x%04X\n", lines);
++
++	if(!pcm_valid(xpd, pack)) {
++		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);
++	xpp_tick((unsigned long)xpd);
++	return 0;
++}
++
++HANDLER_DEF(GLOBAL, SYNC_REPLY)
++{
++	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));
++	return 0;
++}
++
++
++xproto_table_t PROTO_TABLE(GLOBAL) = {
++	.entries = {
++		/*	Card	Opcode		*/
++		XENTRY(	GLOBAL, DEV_DESC	),
++		XENTRY(	GLOBAL,	PCM_READ	),
++		XENTRY(	GLOBAL,	SYNC_REPLY	),
++	},
++	.name = "GLOBAL",
++	.packet_is_valid = global_packet_is_valid,
++	.packet_dump = global_packet_dump,
++};
++
++static bool global_packet_is_valid(xpacket_t *pack)
++{
++	const xproto_entry_t	*xe;
++
++	//DBG("\n");
++	xe = xproto_global_entry(pack->content.opcode);
++	return xe != NULL;
++}
++
++static void global_packet_dump(xpacket_t *pack)
++{
++	DBG("\n");
++}
++
++static bool pcm_valid(xpd_t *xpd, xpacket_t *pack)
++{
++	xpp_line_t	lines = RPACKET_FIELD(pack, GLOBAL, PCM_READ, lines);
++	int		i;
++	int		count = 0;
++
++	BUG_ON(!pack);
++	BUG_ON(pack->content.opcode != XPROTO_NAME(GLOBAL, PCM_READ));
++	for (i = 0; i < CHANNELS_PERXPD; i++)
++		if(IS_SET(lines, i))
++			count++;
++	if(pack->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: pack->datalen=%d, count=%d\n", pack->datalen, count);
++		}
++		return 0;
++	}
++	return 1;
++}
++
+diff -urNad zaptel-1.0.10/xpp/card_global.h /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/card_global.h
+--- zaptel-1.0.10/xpp/card_global.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/card_global.h	2006-01-03 13:22:53.823218000 +0200
+@@ -0,0 +1,56 @@
++#ifndef	CARD_GLOBAL_H
++#define	CARD_GLOBAL_H
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2005, 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"
++#include "xproto.h"
++
++DEF_RPACKET_DATA(GLOBAL, DESC_REQ);
++DEF_RPACKET_DATA(GLOBAL, DEV_DESC,
++	byte		rev;		/* Revision number */
++	byte		type;		/* LSB: 1 - to_phone, 0 - to_line */
++	xpp_line_t	line_status;	/* hook/ring status, depending on unit */
++	);
++DEF_RPACKET_DATA(GLOBAL, PCM_WRITE,
++	xpp_line_t	lines;	// Must be 0xFF
++	byte		pcm[PCM_CHUNKSIZE];
++	);
++DEF_RPACKET_DATA(GLOBAL, PCM_READ,
++	xpp_line_t	lines;	// Must be 0xFF
++	byte		pcm[PCM_CHUNKSIZE];
++	);
++DEF_RPACKET_DATA(GLOBAL, SYNC_SOURCE,
++	byte		mask;
++	);
++DEF_RPACKET_DATA(GLOBAL, SYNC_REPLY,
++	byte		mask;
++	);
++
++
++/* 0x04 */ DECLARE_CMD(GLOBAL, DESC_REQ, int xpd_num);
++/* 0x11 */ DECLARE_CMD(GLOBAL, PCM_WRITE, xpp_line_t hookstate,  volatile byte *buf);
++/* 0x19 */ DECLARE_CMD(GLOBAL, SYNC_SOURCE, bool setit, bool is_master);
++
++extern xproto_table_t PROTO_TABLE(GLOBAL);
++
++#endif	/* CARD_GLOBAL_H */
+diff -urNad zaptel-1.0.10/xpp/cards.c /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/cards.c
+--- zaptel-1.0.10/xpp/cards.c	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/cards.c	2006-01-03 14:01:24.370308595 +0200
+@@ -0,0 +1,394 @@
++#include <linux/module.h>
++#include "xpd.h"
++#include "xpp_zap.h"
++#include "xpp_proto.h"
++#include "cards.h"
++
++static char rcsid[] = "$Id: cards.c 150 2005-12-12 09:27:57Z oron $";
++
++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 -urNad zaptel-1.0.10/xpp/cards.h /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/cards.h
+--- zaptel-1.0.10/xpp/cards.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/cards.h	2006-01-03 14:01:24.370308595 +0200
+@@ -0,0 +1,23 @@
++#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 -urNad zaptel-1.0.10/xpp/FPGA_XPD.hex /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/FPGA_XPD.hex
+--- zaptel-1.0.10/xpp/FPGA_XPD.hex	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/FPGA_XPD.hex	2006-01-03 13:22:53.823218000 +0200
 @@ -0,0 +1,243 @@
 +:0A0B3C000001020203030404050592
 +:100546005010C0C0F9A4B0999282F880988883C6EA
@@ -33,253 +1416,6 @@
 +:1003D700D2AF90E680E020E105D2041206D090E685
 +:1003E70080E054F7F0538EF8C20390E6C2E054FB66
 +:1003F700F000000000000090E6187410F000000004
-+:1004070090E61A7408F000000090E6017413F000FB
-+:100417000000300105120080C2013003F5120B5AAB
-+:1004270050F0C203120A7B20001690E682E030E704
-+:1004370004E020E1EF90E682E030E604E020E0E42B
-+:080447001209FC120B5C80CAD3
-+:0B0B310090E50DE030E402C322D32267
-+:1000800090E6B9E0700302013F1470030201BC2442
-+:10009000FE700302023F24FB700302013914700357
-+:1000A00002013314700302012714700302012D248E
-+:1000B0000560030202A3120B5E40030202AF90E64A
-+:1000C000BBE024FE602714603824FD601114602713
-+:1000D00024067050E52390E6B3F0E524803C120B33
-+:1000E00031503EE52B90E6B3F0E52C802DE52590D0
-+:1000F000E6B3F0E5268023E52790E6B3F0E5288017
-+:100100001990E6BAE0FF120A28AA06A9077B01EABD
-+:10011000494B600DEE90E6B3F0EF90E6B4F00202CA
-+:10012000AF02029E02029E120B0E0202AF120B4E93
-+:100130000202AF120B460202AF120AFC0202AF1219
-+:100140000B6040030202AF90E6B8E0247F60151414
-+:10015000601924027063A200E43325E0FFA202E4E8
-+:10016000334F8041E490E740F0803F90E6BCE0549C
-+:100170007EFF7E00E0D394807C0040047D018002FD
-+:100180007D00EC4EFEED4F243CF582740B3EF58372
-+:10019000E493FF3395E0FEEF24A1FFEE34E68F8277
-+:1001A000F583E0540190E740F0E4A3F090E68AF094
-+:1001B00090E68B7402F00202AF02029E120B6240C4
-+:1001C000030202AF90E6B8E024FE6016240260034A
-+:1001D0000202AF90E6BAE0B40105C2000202AF022B
-+:1001E000029E90E6BAE0705590E6BCE0547EFF7E39
-+:1001F00000E0D394807C0040047D0180027D00EC0F
-+:100200004EFEED4F243CF582740B3EF583E493FFE4
-+:100210003395E0FEEF24A1FFEE34E68F82F583E014
-+:1002200054FEF090E6BCE05480131313541FFFE01B
-+:10023000540F2F90E683F0E04420F08072805F122C
-+:100240000B64506B90E6B8E024FE60192402704EF7
-+:1002500090E6BAE0B40104D200805490E6BAE064BB
-+:1002600002604C803990E6BCE0547EFF7E00E0D313
-+:1002700094807C0040047D0180027D00EC4EFEED08
-+:100280004F243CF582740B3EF583E493FF3395E0F5
-+:10029000FEEF24A1FFEE34E68F82F583800D90E619
-+:1002A000A08008120A53500790E6A0E04401F090A5
-+:0602B000E6A0E04480F02E
-+:0102B6002225
-+:03003300020B5667
-+:040B560053D8EF324F
-+:100700001201000200000040B404041000000102C5
-+:1007100000010A06000200000040010009022E004C
-+:1007200001010080320904000004FF0000000705F9
-+:10073000020200020007050402000200070586020B
-+:100740000002000705880200020009022E000101D4
-+:100750000080320904000004FF00000007050202C7
-+:100760004000000705040240000007058602400023
-+:100770000007058802400000040309041C0341002F
-+:100780007300740072006900620061006E006B000B
-+:10079000320030003000360028035800500044007A
-+:1007A00028004200610073006500640020006F00B3
-+:1007B0006E002000410058005500500050002900F4
-+:0207C000000037
-+:100559005010B0C0F9A4B0999282F880988883C6E7
-+:10056900A1868E4110ABFF4110AD004110AEFF4195
-+:0705790010AC004110AF00BF
-+:1006370090E600E054E74410F000000090E60474F0
-+:1006470080F00000007406F0000000E4F0000000F5
-+:1006570090E610F090E611F000000090E613F0002D
-+:10066700000090E615F090E61074A0F090E611F007
-+:1006770000000000000090E61274AAF0000000904D
-+:10068700E61474EAF000000090E6047480F00000BD
-+:10069700007404F0000000E4F000000090E64974E4
-+:1006A70082F0000000F000000090E6187410F000DF
-+:1006B700000090E61A7408F090E6917480F000004C
-+:0906C70000F000000043AF012225
-+:020B5A00D322A4
-+:020B5C00D322A2
-+:020B5E00D322A0
-+:080B460090E6BAE0F51BD32292
-+:100AFC0090E740E51BF0E490E68AF090E68B04F07A
-+:020B0C00D322F2
-+:080B4E0090E6BAE0F51AD3228B
-+:100B0E0090E740E51AF0E490E68AF090E68B04F068
-+:020B1E00D322E0
-+:020B6000D3229E
-+:020B6200D3229C
-+:020B6400D3229A
-+:100A530090E6B9E0242F600D04701990E604E0FFDE
-+:100A6300430780800890E604E0FF53077F000000FF
-+:070A7300EFF08002D322C363
-+:010A7A002259
-+:100AA000C0E0C083C082D2015391EF90E65D740133
-+:080AB000F0D082D083D0E032C7
-+:100AD000C0E0C083C0825391EF90E65D7404F0D013
-+:060AE00082D083D0E03259
-+:100AE600C0E0C083C0825391EF90E65D7402F0D0FF
-+:060AF60082D083D0E03243
-+:1009C600C0E0C083C082852925852A2685268285A2
-+:1009D6002583A37402F08521278522288528828510
-+:1009E6002783A37407F05391EF90E65D7410F0D05F
-+:0609F60082D083D0E03244
-+:100AB800C0E0C083C082D2035391EF90E65D740812
-+:080AC800F0D082D083D0E032AF
-+:1007C200C0E0C083C08290E680E030E7208521252A
-+:1007D200852226852682852583A37402F085292712
-+:1007E200852A28852882852783A37407F05391EFF1
-+:0D07F20090E65D7420F0D082D083D0E0321C
-+:0106FF0032C8
-+:0107FF0032C7
-+:010B6600325C
-+:010B6700325B
-+:010B6800325A
-+:010B69003259
-+:010B6A003258
-+:010B6B003257
-+:010B6C003256
-+:010B6D003255
-+:010B6E003254
-+:010B6F003253
-+:010B70003252
-+:010B71003251
-+:010B72003250
-+:010B7300324F
-+:010B7400324E
-+:010B7500324D
-+:010B7600324C
-+:010B7700324B
-+:010B7800324A
-+:010B79003249
-+:010B7A003248
-+:010B7B003247
-+:010B7C003246
-+:010B7D003245
-+:010B7E003244
-+:010B7F003243
-+:010B80003242
-+:010B81003241
-+:010B82003240
-+:010B8300323F
-+:010B8400323E
-+:010B8500323D
-+:10098A00C0E0C083C08290E6D1E09010ACF090E65F
-+:10099A00D0E4F000000090E6D17402F000000053A9
-+:1009AA0091BF00000090E66104F000000090E69913
-+:0C09BA0004F075BB06D082D083D0E03280
-+:010B8600323C
-+:03004300020800B0
-+:03005300020800A0
-+:10080000020AA000020AE600020AD000020AB800AA
-+:100810000209C6000207C2000206FF000207FF002D
-+:10082000020B6600020B6700020B6800020B6900F6
-+:10083000020B6A00020B6B00020B6C00020B6D00D6
-+:10084000020B6E000207FF00020B6F00020B70002C
-+:10085000020B7100020B7200020B7300020B74009A
-+:10086000020B75000207FF000207FF000207FF00EE
-+:10087000020B7600020B7700020B7800020B790066
-+:10088000020B7A00020B7B00020B7C00020B7D0046
-+:10089000020B7E00020B7F00020B8000020B810026
-+:1008A000020B8200020B8300020B8400020B850006
-+:0808B00002098A00020B860018
-+:1009FC0090E682E030E004E020E60B90E682E03006
-+:100A0C00E119E030E71590E680E04401F07F147EB8
-+:0C0A1C000012094490E680E054FEF02235
-+:1006D00030040990E680E0440AF0800790E680E06C
-+:1006E0004408F07FDC7E0512094490E65D74FFF05B
-+:0F06F00090E65FF05391EF90E680E054F7F02230
-+:020A2800A9071C
-+:100A2A00AE2DAF2E8F828E83A3E064037017AD01C3
-+:100A3A0019ED7001228F828E83E07C002FFDEC3E3F
-+:080A4A00FEAF0580DFE4FEFFB2
-+:010A52002281
-+:100A7B0090E682E044C0F090E681F04387010000ED
-+:040A8B000000002245
-+:100944008E188F1990E600E054187012E5192401EE
-+:10095400FFE43518C313F518EF13F519801590E665
-+:1009640000E05418FFBF100BE51925E0F519E51850
-+:1009740033F518E5191519AE18700215184E6005EF
-+:06098400120A8F80EE2232
-+:100A8F007400F58690FDA57C05A3E582458370F97A
-+:010A9F002234
-+:1005800060801000011113213C01010703030305E2
-+:10059000010000000C0D0C0C0E0E0E0E410000367A
-+:1005A0000900003F010A013B0101010701010201AD
-+:1005B0000000000002020000020202024049004066
-+:1005C0000000003F010101010101010700000000DE
-+:1005D000000000000E0E0E0E0E0E0E0E00000000AB
-+:1005E0000000003F0139010101010107000302027F
-+:1005F000020202020E0E0E0E0E0E0E0E002D000056
-+:100600000000003F60241087000000000000000090
-+:1006100000000000000000000000833611170004F5
-+:10062000030201813616170004030201471080E01F
-+:0606300000000ECE4E009A
-+:10044F0090E60174CEF090E6F574FFF0901080E026
-+:10045F0090E6F3F0901081E090E6C3F0901082E008
-+:10046F0090E6C1F0901083E090E6C2F0901085E026
-+:10047F0090E6C0F0901086E090E6F4F075AF077448
-+:10048F0010F59A7400F59B759DE4E4F59EFF90E6D8
-+:10049F007BE090E67CF00FBF80F490E67174FFF084
-+:1004AF00F5B490E672E04480F043B680000000E4BB
-+:1004BF0090E6C4F000000090E6C5F0901087E09041
-+:1004CF00E6C6F0901088E090E6C7F0901089E090B3
-+:1004DF00E6C8F090108AE090E6C9F090108BE0909B
-+:1004EF00E6CAF090108CE090E6CBF090108DE09083
-+:1004FF00E6CCF090108EE090E6CDF000000090E694
-+:10050F00D27401F000000090E6E204F00000000059
-+:10051F00000090E6EB14F000000000000000000067
-+:10052F0090E6C0F090E6C1F090E6247408F0000069
-+:06053F0000E490E625F047
-+:010545002293
-+:030000000208B83B
-+:0C08B800787FE4F6D8FD75812E0208FF61
-+:100B2000EB9FF5F0EA9E42F0E99D42F0E89C45F02B
-+:010B300022A2
-+:1008C4000202B7E493A3F8E493A34003F68001F291
-+:1008D40008DFF48029E493A3F85407240CC8C33335
-+:1008E400C4540F4420C8834004F456800146F6DF04
-+:1008F400E4800B0102040810204080900546E47E49
-+:10090400019360BCA3FF543F30E509541FFEE493F8
-+:10091400A360010ECF54C025E060A840B8E493A3BF
-+:10092400FAE493A3F8E493A3C8C582C8CAC583CAEA
-+:10093400F0A3C8C582C8CAC583CADFE9DEE780BEA2
-+:0106360000C3
-+:00000001FF
-diff -urNad trunk/xpp/FPGA_XPD.hex /tmp/dpep.oAj2YR/trunk/xpp/FPGA_XPD.hex
---- trunk/xpp/FPGA_XPD.hex	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/FPGA_XPD.hex	2005-09-25 15:31:51.000000000 +0300
-@@ -0,0 +1,243 @@
-+:0A0B3C000001020203030404050592
-+:100546005010C0C0F9A4B0999282F880988883C6EA
-+:03055600A1868EED
-+:1002B700E4F513F512F511F510C203C200C202C22C
-+:1002C700011206377E077F008E238F24752B077553
-+:1002D7002C1275210775221C752907752A4A752D59
-+:1002E70007752E78EE54C070030203B875140075B5
-+:1002F70015808E168F17C374C09FFF74079ECF2477
-+:1003070002CF3400FEE48F0F8E0EF50DF50CF50BC2
-+:10031700F50AF509F508AF0FAE0EAD0DAC0CAB0B3A
-+:10032700AA0AA909A808C3120B205033E517250B01
-+:10033700F582E516350AF583E0FFE515250BF5820D
-+:10034700E514350AF583EFF0E50B2401F50BE435E9
-+:100357000AF50AE43509F509E43508F50880B78593
-+:10036700142385152474002480FF740734FFFEC30B
-+:10037700E52C9FF52CE52B9EF52BC3E5269FF5264F
-+:10038700E5259EF525C3E5289FF528E5279EF52752
-+:10039700C3E5229FF522E5219EF521C3E52A9FF5B6
-+:1003A7002AE5299EF529C3E52E9FF52EE52D9EF515
-+:1003B7002DD2E843D82090E668E0440BF090E65C45
-+:1003C700E0443DF0000000000000E4F5A20000005A
-+:1003D700D2AF90E680E020E105D2041206D090E685
-+:1003E70080E054F7F0538EF8C20390E6C2E054FB66
-+:1003F700F000000000000090E6187410F000000004
 +:1004070090E61A7408F000000090E6017403F0000B
 +:100417000000300105120080C2013003F5120B5AAB
 +:1004270050F0C203120A7B20001690E682E030E704
@@ -499,22 +1635,25 @@
 +:10093400F0A3C8C582C8CAC583CADFE9DEE780BEA2
 +:0106360000C3
 +:00000001FF
-diff -urNad trunk/xpp/gen_slic_init /tmp/dpep.oAj2YR/trunk/xpp/gen_slic_init
---- trunk/xpp/gen_slic_init	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/gen_slic_init	2005-11-06 18:00:07.000000000 +0200
-@@ -0,0 +1,34 @@
+diff -urNad zaptel-1.0.10/xpp/gen_slic_init /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/gen_slic_init
+--- zaptel-1.0.10/xpp/gen_slic_init	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/gen_slic_init	2006-01-03 15:08:38.089954000 +0200
+@@ -0,0 +1,37 @@
 +#! /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(<>) {
++while(<IF>) {
 +	chomp;
 +	undef $comment;
 +	s/\r//;				# CRLF -> LF
@@ -537,26 +1676,229 @@
 +	}
 +	print HF "\n";
 +}
-diff -urNad trunk/xpp/Makefile /tmp/dpep.oAj2YR/trunk/xpp/Makefile
---- trunk/xpp/Makefile	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/Makefile	2005-11-10 04:05:17.000000000 +0200
-@@ -0,0 +1,5 @@
+diff -urNad zaptel-1.0.10/xpp/Makefile /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/Makefile
+--- zaptel-1.0.10/xpp/Makefile	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/Makefile	2006-01-03 13:22:53.565151000 +0200
+@@ -0,0 +1,4 @@
 +EXTRA_CFLAGS	= -I$(src)/.. 
 +
 +obj-m		= xpp.o xpp_usb.o
-+xpp-y		+= xpp_proto.o xpp_zap.o zap_debug.o
++xpp-y		+= xproto.o card_global.o card_fxs.o slic.o xpp_zap.o zap_debug.o
+diff -urNad zaptel-1.0.10/xpp/slic.c /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/slic.c
+--- zaptel-1.0.10/xpp/slic.c	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/slic.c	2006-01-03 13:22:53.823218000 +0200
+@@ -0,0 +1,129 @@
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2005, 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.
++ *
++ */
 +
-diff -urNad trunk/xpp/slic_init.inc /tmp/dpep.oAj2YR/trunk/xpp/slic_init.inc
---- trunk/xpp/slic_init.inc	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/slic_init.inc	2005-11-06 18:04:13.000000000 +0200
-@@ -0,0 +1,51 @@
++#include "xproto.h"
++#include "slic.h"
++
++static const char rcsid[] = "$Id: slic.c 161 2006-01-03 09:13:40Z oron $";
++
++#ifdef	__KERNEL__
++#include <linux/module.h>
++
++extern	int print_dbg;
++#include "zap_debug.h"
++#else
++#include <stdio.h>
++#endif
++
++int slic_cmd_direct_write(slic_cmd_t *sc, xpp_line_t lines, byte reg, byte data)
++{
++	struct slic_reg_d	*p = (struct slic_reg_d *)&sc->content;
++
++	sc->lines = lines;
++	sc->bytes = sizeof(struct slic_reg_d);
++	SLIC_REG_INIT(p, 0, reg, data);
++	return sizeof(xpp_line_t) + 1 + sc->bytes;
++}
++
++int slic_cmd_direct_read(slic_cmd_t *sc, xpp_line_t lines, byte reg)
++{
++	struct slic_reg_d	*p = (struct slic_reg_d *)&sc->content;
++
++	sc->lines = lines;
++	sc->bytes = sizeof(struct slic_reg_d);
++	SLIC_REG_INIT(p, 1, reg, 0);
++	return sizeof(xpp_line_t) + 1 + sc->bytes;
++}
++
++int slic_cmd_indirect_write(slic_cmd_t *sc, xpp_line_t lines, byte reg, byte data_low, byte data_high)
++{
++	struct slic_reg_iw	*p = (struct slic_reg_iw *)&sc->content;
++
++	sc->lines = lines;
++	sc->bytes = sizeof(struct slic_reg_iw);
++	SLIC_REG_INIT(&p->iw_data_low, 0, 0x1C, data_low);
++	SLIC_REG_INIT(&p->iw_data_high, 0, 0x1D, data_high);
++	SLIC_REG_INIT(&p->iw_reg, 0, 0x1E, reg);
++	return sizeof(xpp_line_t) + 1 + sc->bytes;
++}
++
++int slic_cmd_indirect_read(slic_cmd_t *sc, xpp_line_t lines, byte reg)
++{
++	struct slic_reg_ir	*p = (struct slic_reg_ir *)&sc->content;
++
++	sc->lines = lines;
++	sc->bytes = sizeof(struct slic_reg_ir);
++	SLIC_REG_INIT(&p->ir_reg, 0, 0x1E, reg);
++	return sizeof(xpp_line_t) + 1 + sc->bytes;
++}
++
++void dump_slic_cmd(const char msg[], slic_cmd_t *sc)
++{
++	int	i;
++	struct slic_reg_d	*sr;
++	int	last_data_low = -1;
++	int	last_data_high = -1;
++
++	sr = (struct slic_reg_d *)&sc->content;
++	if(sc->bytes > sizeof(sc->content)) {
++		NOTICE("%s: Bug: sc->bytes = %d\n", __FUNCTION__, sc->bytes);
++		return;
++	}
++	if(sc->bytes % 2) {
++		NOTICE("%s: Bug: ODD sc->bytes = %d\n", __FUNCTION__, sc->bytes);
++		return;
++	}
++	for(i = 0; i < sc->bytes/2; i++, sr++) {
++		if(sr->reg_num == 0x1C) {
++			last_data_low = sr->reg_data;
++			continue;
++		}
++		if(sr->reg_num == 0x1D) {
++			last_data_high = sr->reg_data;
++			continue;
++		}
++		if(sr->reg_num == 0x1E) {
++			if(last_data_low == -1 && last_data_high == -1)		// Indirect Read
++				DBG("%s: LINES=0x%08X bytes=%d INDIRECT READ: register=0x%02X\n", msg, sc->lines, sc->bytes, sr->reg_data);
++			else if(last_data_low == -1 || last_data_high == -1) {
++				NOTICE("%s: BUG: PARTIAL INDIRECT: register=%d last_data_low=0x%X last_data_high=0x%X\n",
++						msg, sr->reg_data, last_data_low, last_data_high);
++			} else
++				DBG("%s: LINES=0x%08X bytes=%d INDIRECT WRITE: register=%d data_low=0x%02x data_high=0x%02X\n",
++						msg, sc->lines, sc->bytes, sr->reg_data, (byte)last_data_low, (byte)last_data_high);
++			last_data_low = last_data_high = -1;
++		} else {
++			DBG("%s: LINES=0x%08X bytes=%d DIRECT %s: register=%d data=0x%02X\n",
++				msg, sc->lines, sc->bytes, (sr->read) ? "READ" : "WRITE", sr->reg_num, sr->reg_data);
++		}
++	}
++}
++
++#ifdef	__KERNEL__
++
++EXPORT_SYMBOL(slic_cmd_direct_write);
++EXPORT_SYMBOL(slic_cmd_direct_read);
++EXPORT_SYMBOL(slic_cmd_indirect_write);
++EXPORT_SYMBOL(slic_cmd_indirect_read);
++EXPORT_SYMBOL(dump_slic_cmd);
++
++#endif
+diff -urNad zaptel-1.0.10/xpp/slic.h /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/slic.h
+--- zaptel-1.0.10/xpp/slic.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/slic.h	2006-01-03 13:22:53.823218000 +0200
+@@ -0,0 +1,65 @@
++#ifndef	SLIC_H
++#define	SLIC_H
++
++#include "xdefs.h"
++
++/*------------------------------ SLIC Data Structures ----------------------*/
++
++struct slic_reg_d {		/* SLIC Register Direct Read/Write */
++	byte	reg_num:7;
++	byte	read:1;
++	byte	reg_data;
++} __attribute__((packed));
++
++struct slic_reg_iw {		/* SLIC Register Indirect-Write */
++	struct slic_reg_d	iw_data_low;
++	struct slic_reg_d	iw_data_high;
++	struct slic_reg_d	iw_reg;
++} __attribute__((packed));
++
++struct slic_reg_ir {		/* SLIC Register Indirect-Read */
++	struct slic_reg_d	ir_reg;
++} __attribute__((packed));
++
++typedef struct slic_cmd {
++	xpp_line_t	lines;
++	byte		bytes;
++	union {
++		struct slic_reg_d	direct;
++		struct slic_reg_iw	indirect_write;
++		struct slic_reg_ir	indirect_read;
++	} content;
++} __attribute__((packed)) slic_cmd_t;
++
++typedef	struct slic_reply {
++	byte	size;
++	byte	reg_num:7;
++	byte	indirect:1;
++	byte	data_low;
++	byte	data_high;
++} __attribute__((packed)) slic_reply_t;
++
++#define	SLIC_REG_INIT(slic_reg, reading, num, data)	\
++				do {	\
++					(slic_reg)->read = reading;	\
++					(slic_reg)->reg_num = num;	\
++					(slic_reg)->reg_data = data;	\
++				} while(0);
++
++/* OLD SLIC_INIT data */
++typedef	struct slic_data {
++	byte	len;
++	byte	data[40];
++} __attribute__((packed)) slic_data_t;
++
++
++/*------------------------------ SLIC Initializers -------------------------*/
++
++int slic_cmd_direct_write(slic_cmd_t *sc, xpp_line_t lines, byte reg, byte data);
++int slic_cmd_direct_read(slic_cmd_t *sc, xpp_line_t lines, byte reg);
++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);
++
++
++#endif	/* SLIC_H */
+diff -urNad zaptel-1.0.10/xpp/slic_init.inc /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/slic_init.inc
+--- zaptel-1.0.10/xpp/slic_init.inc	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/slic_init.inc	2006-01-03 15:08:39.122220000 +0200
+@@ -0,0 +1,65 @@
 +// ----------------------------------==== 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),	
@@ -582,15 +1924,20 @@
 +S_(0x000000FF,	0x0C,	0x15, 0x00, 0x16, 0x03, 0x17, 0x00, 0x12, 0xFF, 0x13, 0xFF, 0x14, 0xFF),	
 +
 +// Ring timers settings
-+S_(0x000000FF,	0x0A,	0x30, 0x80, 0x31, 0x3E, 0x32, 0x80, 0x33, 0x3E, 0x22, 0x18),	
++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, Manual VBATH, VBATL
-+S_(0x000000FF,	0x02,	0x43, 0x16),	
++// 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),	
 +
-+// SLICs calibration
-+S_(0x000000FF,	0x0C,	0x61, 0x1F, 0x60, 0x5F, 0x45, 0x20, 0x46, 0x02, 0x42, 0x04, 0x47, 0x04),	
++S_(0x000000FF,	0x02,	0x45, 0x0A),	
++S_(0x000000FF,	0x02,	0x46, 0x0B),	
++S_(0x000000FF,	0x02,	0x47, 0x00),	
 +
++
 +// 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),	
@@ -601,10 +1948,17 @@
 +S_(0x00000040,	0x08,	0x02, 0x31, 0x03, 0x00, 0x04, 0x31, 0x05, 0x00),	
 +S_(0x00000080,	0x08,	0x02, 0x39, 0x03, 0x00, 0x04, 0x39, 0x05, 0x00),	
 +
-diff -urNad trunk/xpp/sync.sh /tmp/dpep.oAj2YR/trunk/xpp/sync.sh
---- trunk/xpp/sync.sh	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/sync.sh	2005-11-14 16:43:00.829030244 +0200
-@@ -0,0 +1,21 @@
++// Change SLICs states to "Normal state"s (On, after offsets are set already)
++
++
++S_(0x000000FF,	0x02,	0x40, 0x1),	
++S_(0x000000FF,	0x02,	0x42, 0x06),	
++// -------------------------------------------------------------
++
+diff -urNad zaptel-1.0.10/xpp/sync.sh /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/sync.sh
+--- zaptel-1.0.10/xpp/sync.sh	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/sync.sh	2006-01-03 15:35:29.050560735 +0200
+@@ -0,0 +1,30 @@
 +#!/bin/sh
 +
 +set -e
@@ -614,22 +1968,117 @@
 +XPP_DIR=$SVN_ROOT/xpp-zaptel/zaptel/xpp
 +TARGET_DIR=xpp
 +
-+(cd $SVN_ROOT/xpp-zaptel; svn update)
++curdir=$PWD
++cd $SVN_ROOT/xpp-zaptel
++  svn update
++  cd xortel
++  make FIRMWARE="$SVN_ROOT/fpgafirmware/init.dat" ../zaptel/xpp/slic_init.inc
++cd $curdir
 +
-+cp -a $XORTEL_DIR/FPGA_*.hex ${TARGET_DIR}/
++cp -a $XORTEL_DIR/FPGA_XPD.hex ${TARGET_DIR}/
 +cp -a $XORTEL_DIR/xpd.h ${TARGET_DIR}/
-+cp -a $XORTEL_DIR/xpp_{proto,zap}.{c,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 -urNad trunk/xpp/xpd.h /tmp/dpep.oAj2YR/trunk/xpp/xpd.h
---- trunk/xpp/xpd.h	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/xpd.h	2005-11-06 18:00:07.000000000 +0200
-@@ -0,0 +1,318 @@
+diff -urNad zaptel-1.0.10/xpp/xdefs.h /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xdefs.h
+--- zaptel-1.0.10/xpp/xdefs.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xdefs.h	2006-01-03 13:22:53.823218000 +0200
+@@ -0,0 +1,82 @@
++#ifndef	XDEFS_H
++#define	XDEFS_H
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2005, 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.
++ *
++ */
++
++#ifdef	__KERNEL__
++
++#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
++
++#include <stdint.h>
++typedef uint32_t __u32;
++
++#include <stdio.h>
++
++#define	DBG(fmt, ...)		printf("DBG: %s: " fmt, __FUNCTION__, ## __VA_ARGS__)
++#define	INFO(fmt, ...)		printf("INFO: " fmt, ## __VA_ARGS__)
++#define	NOTICE(fmt, ...)	printf("NOTICE: " fmt, ## __VA_ARGS__)
++#define	ERR(fmt, ...)		printf("ERR: " fmt, ## __VA_ARGS__)
++#define	__user
++
++struct list_head { struct list_head *next; struct list_head *prev; };
++
++#endif
++
++typedef char			*charp;
++typedef unsigned char		byte;
++typedef int			bool;
++typedef struct xbus		xbus_t;
++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 */
++
++
++#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))
++
++#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 -urNad zaptel-1.0.10/xpp/xpd.h /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpd.h
+--- zaptel-1.0.10/xpp/xpd.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpd.h	2006-01-03 13:22:53.823218000 +0200
+@@ -0,0 +1,294 @@
 +#ifndef	XPD_H
 +#define	XPD_H
 +
@@ -655,25 +2104,18 @@
 + *
 + */
 +
-+/*
-+ * Undef to disable packet logging
-+#define	XPP_PACKET_LOG
-+ */
++#include "xdefs.h"
++#include "xproto.h"
 +
 +#ifdef	__KERNEL__
 +#include <linux/kernel.h>
 +#include <asm/atomic.h>
 +#include <asm/semaphore.h>
 +#include <linux/moduleparam.h>
-+#else
-+#include <stdint.h>
-+typedef uint32_t __u32;
 +#endif
 +
 +#include <zaptel.h>
 +
-+typedef	char	*charp;
-+
 +#ifdef __KERNEL__
 +#define	DEF_PARM(type,name,init,desc)	\
 +	type name = init;	\
@@ -708,51 +2150,19 @@
 +#define	XPD_DESCLEN	20
 +#define	XBUS_NAMELEN	20	/* must be <= from maximal workqueue name */
 +#define	XBUS_DESCLEN	40
-+#define	MAX_XPDS	8	// 1 FXS + 2 E1/T1 + 1 (Quad * E1/T1)
 +
-+#undef	SUPPORT_USB1
++/* Hardware does not check bank_num yet. So only 4 cards can be used */
++#define	MAX_XPDS	4	// 1 FXS + 2 E1/T1 + 1 (Quad * E1/T1)
 +
-+#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
-+
-+#define	XPD_NUM(x)		((x).id)
-+#define	XPD_ADDR_SET(x,val)	do { (x).id = val; (x).reserved = 0; } while(0);
 +#define	VALID_XPD_NUM(x)	((x) < MAX_XPDS && (x) >= 0)
 +
-+typedef	unsigned char byte;
-+typedef	int bool;
-+typedef	__u32 xpp_line_t;	/* at most 31 lines for E1 */
++typedef	struct xbus_ops		xbus_ops_t;
 +
-+typedef struct xpp_addr {
-+	byte	id:4;
-+	byte	reserved:4;
-+} __attribute__((packed)) xpp_addr_t;
-+
-+
-+typedef struct xbus xbus_t;
-+typedef	struct xbus_ops xbus_ops_t;
-+typedef	struct xpd xpd_t;
-+typedef struct xpp_packet xpacket_t;
-+
 +typedef enum xbus_type {
 +		FIRMWARE_LOOPBACK = 1,
 +		FIRMWARE_XPP = 2,
 +} xbus_type_t;
 +
-+typedef enum xpd_type {
-+	XPD_TYPE_FXO = 0x2,
-+	XPD_TYPE_FXS = 0x3,
-+	//XPD_TYPE_E1 = ,
-+	XPD_TYPE_NOMODULE = 0x7,
-+} xpd_type_t;
-+
 +#ifdef	__KERNEL__
 +
 +
@@ -822,11 +2232,12 @@
 +
 +	/* 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 zt_register
-+	struct work_struct poll_work; // workqueue job for issuing poll
++	struct work_struct sim_work;		// workqueue job for running simulator
 +	packet_queue_t	sim_packet_queue;
++#endif
 +
 +	spinlock_t	lock;
 +
@@ -838,7 +2249,7 @@
 +	struct timer_list poll_timer;
 +	struct	rw_semaphore in_use;
 +	int	num_xpds;
-+	void	*priv;
++	void	*priv;				/* Pointer to transport level data structures */
 +
 +#ifdef	XPP_PACKET_LOG
 +	struct cyclic_buff *packet_log;
@@ -847,6 +2258,7 @@
 +#ifdef CONFIG_PROC_FS
 +	struct proc_dir_entry	*procdir;
 +	struct proc_dir_entry	*procsummary;
++	struct proc_dir_entry	*proc_ztregister;
 +#endif
 +
 +	/* statistics */
@@ -867,6 +2279,7 @@
 +#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))
 +
 +enum {
 +	XPD_N_PCM_READ,
@@ -891,8 +2304,14 @@
 +
 +#define	XPD_COUNTER_MAX	(sizeof(xpd_counters)/sizeof(xpd_counters[0]))
 +
-+#define	NUM_LEDS	2
++enum leds {
++	LED_GREEN,
++	LED_RED,
++	LED_BLUE,
++};
 +
++#define	NUM_LEDS	3
++
 +struct xpd {
 +	char xpdname[XPD_NAMELEN];
 +	struct zt_span	span;
@@ -903,16 +2322,18 @@
 +	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	digital_outputs;	/* 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];
 +
-+	atomic_t span_registered; /* is span zt_registered ? */
 +	struct work_struct xpd_post_init;
 +	xbus_t *xbus;
 +
 +	spinlock_t	lock;
++	atomic_t	open_counter;		/* Number of open channels */
 +
 +	int flags;
 +	enum {
@@ -927,11 +2348,15 @@
 +
 +#ifdef CONFIG_PROC_FS
 +	struct proc_dir_entry	*xpd_proc;
++	struct proc_dir_entry	*xpd_slic;
 +#endif
 +	// Bit numbers of board_flags
 +
 +	int		counters[XPD_COUNTER_MAX];
 +
++	xops_t		*xops;		/* Card level operations */
++	void		*card;		/* Card level private data */
++
 +	unsigned int	recv_errors;
 +	unsigned int	seq_errors;
 +	unsigned long	last_response;	/* in jiffies */
@@ -948,9 +2373,9 @@
 +#endif
 +
 +#endif	/* XPD_H */
-diff -urNad trunk/xpp/xpp_fxloader /tmp/dpep.oAj2YR/trunk/xpp/xpp_fxloader
---- trunk/xpp/xpp_fxloader	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_fxloader	2005-10-09 12:51:54.000000000 +0200
+diff -urNad zaptel-1.0.10/xpp/xpp_fxloader /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_fxloader
+--- zaptel-1.0.10/xpp/xpp_fxloader	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_fxloader	2006-01-03 13:22:53.823218000 +0200
 @@ -0,0 +1,11 @@
 +#!/bin/bash
 +
@@ -963,31 +2388,31 @@
 +	fxload -t fx2 -D "$DEVICE" -I "$FIRMWARE" || exit 1
 +fi
 +
-diff -urNad trunk/xpp/xpp_fxloader.usermap /tmp/dpep.oAj2YR/trunk/xpp/xpp_fxloader.usermap
---- trunk/xpp/xpp_fxloader.usermap	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_fxloader.usermap	2005-10-09 12:51:54.000000000 +0200
+diff -urNad zaptel-1.0.10/xpp/xpp_fxloader.usermap /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_fxloader.usermap
+--- zaptel-1.0.10/xpp/xpp_fxloader.usermap	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_fxloader.usermap	2006-01-03 13:22:53.823218000 +0200
 @@ -0,0 +1,2 @@
 +# 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 -urNad trunk/xpp/xpp_modprobe /tmp/dpep.oAj2YR/trunk/xpp/xpp_modprobe
---- trunk/xpp/xpp_modprobe	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_modprobe	2005-11-03 09:45:31.000000000 +0200
+diff -urNad zaptel-1.0.10/xpp/xpp_modprobe /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_modprobe
+--- zaptel-1.0.10/xpp/xpp_modprobe	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_modprobe	2006-01-03 13:22:53.823218000 +0200
 @@ -0,0 +1,4 @@
 +options zaptel debug=1
 +options wcfxo debug=1
 +options xpp print_dbg=1 ignore_xpds=0x00 softloop_xpds=0x00 enabled_channels=0x000000FF pcm_gen=0x00000000 max_queue_len=20000
 +options usb_test vendor=0x4B4 product=0x8613 altsetting=1 innum=2 outnum=4
-diff -urNad trunk/xpp/xpp_proto.c /tmp/dpep.oAj2YR/trunk/xpp/xpp_proto.c
---- trunk/xpp/xpp_proto.c	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_proto.c	2005-11-10 04:05:18.000000000 +0200
-@@ -0,0 +1,823 @@
+diff -urNad zaptel-1.0.10/xpp/xpp_proto.c /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_proto.c
+--- zaptel-1.0.10/xpp/xpp_proto.c	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_proto.c	2006-01-03 14:01:24.370308595 +0200
+@@ -0,0 +1,1044 @@
 +#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 123 2005-11-09 13:21:20Z oron $";
++static char rcsid[] = "$Id: xpp_proto.c 136 2005-12-12 07:10:40Z oron $";
 +
 +extern	int print_dbg;
 +#include "zap_debug.h"
@@ -1024,13 +2449,6 @@
 +static int simulate_xpd(xbus_t *xbus, int xpd_num, xpacket_t *sent_packet);
 +static bool pcm_valid(xpd_t *xpd, xpacket_t *reply);
 +
-+/*------------------------- Protocol Functions ---------------------*/
-+
-+#define	HOSTCMD(name, ...)		\
-+			DECLARE_CMD(name, ## __VA_ARGS__ );	\
-+			EXPORT_SYMBOL(xpp_proto_ ## name);	\
-+			DECLARE_CMD(name, ## __VA_ARGS__ )
-+
 +#define	NEW_PACKET(p, xbus, name, to)	\
 +	do {				\
 +		p = xbus->ops->packet_new(xbus, GFP_ATOMIC);	\
@@ -1040,7 +2458,156 @@
 +		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;
@@ -1063,29 +2630,26 @@
 +{
 +	int		ret = 0;
 +	xpacket_t	*pack_tx;
-+	slic_data_t	*slic;
-+	int		seq = 0;
++	slic_cmd_t	*sc;
++	int		len;
 +
 +	BUG_ON(!xbus);
 +	BUG_ON(!xpd);
-+	if(!(xpd->enabled_chans & lines)) {	// Ignore disabled channels
++	lines &= xpd->enabled_chans;	// Ignore disabled channels
++	if(!lines) {
 +		return 0;
 +	}
-+	DBG("Channel Power: 0x%4X %s\n", lines, (on) ? "up" : "down");
++	DBG("Channel Power: 0x%04X %s\n", lines, (on) ? "up" : "down");
 +	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
-+	PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = lines;
-+	slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_data);
++	sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd);
 +	if(on) {
 +		// Power up
-+		slic->data[seq++] = 0x42;
-+		slic->data[seq++] = 0x06;
++		len = slic_cmd_direct_write(sc, lines, 0x42, 0x06);
 +	} else {
 +		// Power down
-+		slic->data[seq++] = 0x42;
-+		slic->data[seq++] = 0x00;
++		len = slic_cmd_direct_write(sc, lines, 0x42, 0x00);
 +	}
-+	slic->len = seq;
-+	pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1;
++	pack_tx->datalen = len;
 +
 +	packet_send(xbus, pack_tx);
 +	return ret;
@@ -1095,21 +2659,20 @@
 +{
 +	int		ret = 0;
 +	xpacket_t	*pack_tx;
-+	slic_data_t	*slic;
++	slic_cmd_t	*sc;
++	int		len;
 +
 +	BUG_ON(!xbus);
 +	BUG_ON(!xpd);
-+	if(!(xpd->enabled_chans & lines)) {	// Ignore disabled channels
++	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);
-+	PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = lines;
-+	slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_data);
-+	slic->len = 2;
-+	slic->data[0] = 0x40;
-+	slic->data[1] = (on)? 0x01 : 0x00;
-+	pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1;
++	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;
@@ -1119,23 +2682,21 @@
 +{
 +	int		ret = 0;
 +	xpacket_t	*pack_tx;
-+	slic_data_t	*slic;
++	slic_cmd_t	*sc;
 +	xpp_line_t	mask = (1 << pos);
-+	int		seq = 0;
++	int		len;
 +
 +	BUG_ON(!xbus);
 +	BUG_ON(!xpd);
-+	if(!(mask & xpd->enabled_chans)) {	// Ignore disabled channels
++	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);
-+	PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = mask;
-+	slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_data);
-+	slic->data[seq++] = 0x40;
-+	slic->data[seq++] = (on)? 0x04 : 0x01;
-+	slic->len = seq;
-+	pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1;
++	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;
@@ -1145,21 +2706,21 @@
 +{
 +	int		ret = 0;
 +	xpacket_t	*pack_tx;
-+	slic_data_t	*slic;
++	slic_cmd_t	*sc;
++	int		len;
 +
 +	BUG_ON(!xbus);
 +	BUG_ON(!xpd);
-+	if(!(xpd->enabled_chans & hook_status)) {	// Ignore disabled channels
++	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);
-+	PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = hook_status;
-+	slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_data);
++	sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd);
 +	/* FIXME: This is fake, until Dima implements FXO */
-+	slic->len = 1;
-+	slic->data[0] = 0x02;
-+	pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1;
++	len = slic_cmd_direct_write(sc, hook_status, 0x02, 0x00);
++	pack_tx->datalen = len;
 +
 +	packet_send(xbus, pack_tx);
 +	return ret;
@@ -1167,28 +2728,35 @@
 +
 +/*
 + * LED control is done via SLIC register 0x06:
-+ *         8     7     6     5     4     3     2     1     0
-+ * 	+-----+-----+-----+-----+-----+-----+-----+-----+-----+
-+ * 	| MR  | MG  |     |     |  R  |  W  |     |  G  |     |
-+ * 	+-----+-----+-----+-----+-----+-----+-----+-----+-----+
++ *         7     6     5     4     3     2     1     0
++ * 	+-----+-----+-----+-----+-----+-----+-----+-----+
++ * 	| MR  | MG  | MB  |  R  | OG  | OB  |  G  | B   |
++ * 	+-----+-----+-----+-----+-----+-----+-----+-----+
 + *
-+ * 	MR	- Mask Red. (1 - R effect the Red LED)
-+ * 	MG	- Mask Green. (1 - G effect the Green LED)
-+ * 	R	- Red LED (0 - OFF, 1 - ON)
-+ * 	G	- Green LED (0 - OFF, 1 - ON)
-+ * 	W	- Write (actually change the LED state)
++ * 	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_data_t	*slic;
++	slic_cmd_t	*sc;
++	int		len;
 +	int		value;
 +	int		i;
-+			//                 GREEN	RED
-+	int		mask[NUM_LEDS] = { 0x40,	0x80 };
-+	int		leds[NUM_LEDS] = { 0x02,	0x10 };
 +
 +	BUG_ON(!xbus);
 +	BUG_ON(!xpd);
@@ -1198,27 +2766,53 @@
 +	}
 +	DBG("LED: lines=0x%04X which=%d -- %s\n", lines, which, (on) ? "on" : "off");
 +	which = which % NUM_LEDS;
-+	value = 0x08;
-+	value |= (0xC0 & ~mask[which]);
++	value = BIT(2) | BIT(3);
++	value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_mask[which]);
 +	if(on)
-+		value |= leds[which];
++		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);
-+		PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = (1 << i);
-+
++		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);
-+		slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_data);
-+		slic->len = 2;
-+		slic->data[0] = 0x06;
-+		slic->data[1] = value;
-+		pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1;
++		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;
@@ -1232,10 +2826,10 @@
 +	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_WRITE, xpd->id);
-+		PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = source->lines;
++		NEW_PACKET(pack_tx, xbus, SLIC_INIT, xpd->id);
++		PACKET_FIELD(pack_tx, SLIC_INIT, lines) = source->lines;
 +
-+		slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_data);
++		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;
@@ -1250,18 +2844,16 @@
 +{
 +	int	ret = 0;
 +	xpacket_t	*pack_tx;
-+	slic_data_t	*slic;
++	slic_cmd_t	*sc;
++	int		len;
 +
 +	BUG_ON(!xbus);
 +	BUG_ON(!xpd);
 +	NEW_PACKET(pack_tx, xbus, SLIC_WRITE, xpd->id);
-+	PACKET_FIELD(pack_tx, SLIC_WRITE, lines) = (1 << pos);
++	sc = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_cmd);
++	len = slic_cmd_direct_read(sc, (1<<pos), reg_num);
 +
-+	slic = &PACKET_FIELD(pack_tx, SLIC_WRITE, slic_data);
-+	slic->len = 2;
-+	slic->data[0] = 0x80 | (reg_num & ~0x80);	// MSB: 1 -- read, 0 -- write
-+	slic->data[1] = 0x00;
-+	pack_tx->datalen = sizeof(xpp_line_t) + slic->len + 1;
++	pack_tx->datalen = len;
 +
 +	packet_send(xbus, pack_tx);
 +	return ret;
@@ -1278,14 +2870,20 @@
 +
 +	BUG_ON(!xbus);
 +	BUG_ON(!xpd);
-+	if(!(xpd->enabled_chans & lines)) {	// Ignore disabled channels
-+		return 0;
-+	}
++	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)
-+		return 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);
@@ -1310,7 +2908,8 @@
 +
 +	BUG_ON(!xbus);
 +	BUG_ON(!xpd);
-+	if(!(xpd->enabled_chans & lines)) {	// Ignore disabled channels
++	lines &= xpd->enabled_chans;	// Ignore disabled channels
++	if(!lines) {
 +		return 0;
 +	}
 +	DBG("PCM_GEN lines=0x%04X %s\n", lines, (gen_seq) ? "seq" : "off");
@@ -1326,10 +2925,40 @@
 +	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);
@@ -1346,10 +2975,8 @@
 +static int simulate_xpd(xbus_t *xbus, int xpd_num, xpacket_t *sent_packet)
 +{
 +	xpacket_t	*pack = sent_packet;
-+	xpp_line_t	lines;
 +	struct xpd_sim	*xpd_sim;
 +	struct xpd_sim	*loopto_sim;
-+	slic_data_t	*slic;
 +	xpp_opcode_t	opcode;
 +	int		dest_xpd_num;
 +	int		ret = 0;
@@ -1378,8 +3005,12 @@
 +			XPD_ADDR_SET(pack->content.addr, dest_xpd_num);
 +			break;
 +		case XPP_SLIC_WRITE:
-+			lines = PACKET_FIELD(pack, SLIC_WRITE, lines);
-+			slic = &PACKET_FIELD(pack, SLIC_WRITE, slic_data);
++#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;
 +
@@ -1421,6 +3052,7 @@
 +				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:
@@ -1514,7 +3146,7 @@
 +		NOTICE("DEV_DESC: unkown type=%d\n", type);
 +		return -EPROTO;
 +	}
-+	if((xpd = xpd_new(xbus, xpd_num, type)) == NULL) {
++	if((xpd = xpd_new(xbus, xpd_num, type, rev)) == NULL) {
 +		NOTICE("xpd_new failed\n");
 +	}
 +	xpp_check_hookstate(xpd, line_status);
@@ -1558,30 +3190,27 @@
 +
 +static HANDLER_DEF(SLIC_REPLY)
 +{
-+	byte		dataL = PACKET_FIELD(reply, SLIC_REPLY, dataL);
-+	byte		dataH = PACKET_FIELD(reply, SLIC_REPLY, dataH);
-+	const char	*str = "";
++	slic_reply_t	*info = &PACKET_FIELD(reply, SLIC_REPLY, info);
++	xpd_t		*xpd = xpd_of(xbus, xpd_num);
++	unsigned long	flags;
 +
-+#if 0
-+	switch (dataL) {
-+	case 0x04:
-+		str = "RINGING";
-+		break;
-+	case 0x11:
-+		str = "NORMAL";
-+		break;
-+	default:
-+		str = "?????";
-+		break;
++	if(!xpd) {
++		NOTICE("%s: received %s for non-existing xpd: %d\n", __FUNCTION__, cmd->name, xpd_num);
++		return -EPROTO;
 +	}
-+#endif
-+	DBG("SLIC_REPLY: xpd #%d dataH=0x%X dataL=0x%X [%s]\n",
-+			xpd_num, dataH, dataL, str);
++	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);
@@ -1620,9 +3249,23 @@
 +	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);
@@ -1632,7 +3275,7 @@
 +		return -EPROTO;
 +	}
 +	dump_packet("LOOPBACK_XA", reply, print_dbg);
-+	CALL_PROTO(LED, xpd->xbus, xpd, xpd->enabled_chans, 1, 0);		// FIXME: Find usage for extra LED
++	CALL_PROTO(LED, xpd->xbus, xpd, xpd->enabled_chans, LED_RED, 0);		// FIXME: Find usage for extra LED
 +	return 0;
 +}
 +
@@ -1660,8 +3303,9 @@
 +  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"),
-+  C_(	LOOPBACK_XA,	1,	"LOOPBACK Reply"),
 +};
 +
 +#undef	C_
@@ -1692,7 +3336,7 @@
 +	unsigned int	size = pack->datalen;
 +	int		varsize = cmd->varsize;
 +
-+//	ERR("op=%d hsize=%d size=%d\n", op, hsize, size);
++	// ERR("op=%d hsize=%d size=%d\n", op, hsize, size);
 +	return (hsize == size) ||
 +		(varsize && size <= sizeof(struct xpp_packet_r));
 +}
@@ -1804,14 +3448,17 @@
 +
 +EXPORT_SYMBOL(dump_packet);
 +EXPORT_SYMBOL(packet_receive);
-diff -urNad trunk/xpp/xpp_proto.h /tmp/dpep.oAj2YR/trunk/xpp/xpp_proto.h
---- trunk/xpp/xpp_proto.h	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_proto.h	2005-11-03 15:01:15.000000000 +0200
-@@ -0,0 +1,167 @@
++EXPORT_SYMBOL(proc_xpd_slic_read);
++EXPORT_SYMBOL(proc_xpd_slic_write);
+diff -urNad zaptel-1.0.10/xpp/xpp_proto.h /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_proto.h
+--- zaptel-1.0.10/xpp/xpp_proto.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_proto.h	2006-01-03 14:01:24.370308595 +0200
+@@ -0,0 +1,188 @@
 +#ifndef	XPP_PROTO_H
 +#define	XPP_PROTO_H
 +
 +#include "xpd.h"
++#include "slic.h"
 +#ifdef	__KERNEL__
 +#include <linux/list.h>
 +#endif
@@ -1832,6 +3479,7 @@
 +	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
 +//
@@ -1841,6 +3489,10 @@
 +	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,
@@ -1866,10 +3518,12 @@
 +/* 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 { \
@@ -1900,13 +3554,16 @@
 +			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,
-+			xpp_line_t	lines;
-+			slic_data_t	slic_data;
++			slic_cmd_t	slic_cmd;
 +			);
 +		H_(SLIC_REPLY,					/* Get status of a single SLIC (for debugging) */
-+			byte		dataL;
-+			byte		dataH;
++			xpp_line_t	lines;
++			slic_reply_t	info;
 +			);
 +
 +		H_(PCM_WRITE,
@@ -1923,6 +3580,14 @@
 +			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?
 +			);
@@ -1971,14 +3636,16 @@
 +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 -urNad trunk/xpp/xpp_usb.c /tmp/dpep.oAj2YR/trunk/xpp/xpp_usb.c
---- trunk/xpp/xpp_usb.c	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_usb.c	2005-11-14 10:37:50.000000000 +0200
-@@ -0,0 +1,838 @@
+diff -urNad zaptel-1.0.10/xpp/xpp_usb.c /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_usb.c
+--- zaptel-1.0.10/xpp/xpp_usb.c	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_usb.c	2006-01-03 13:22:53.823218000 +0200
+@@ -0,0 +1,869 @@
 +/*
 + * Written by Oron Peled <oron at actcom.co.il>
 + * Copyright (C) 2004-2005, Xorcom
@@ -2015,13 +3682,14 @@
 +#include <linux/seq_file.h>
 +#include <asm/uaccess.h>
 +#include <asm/atomic.h>
++#include <asm/timex.h>
 +#include <linux/proc_fs.h>
 +#include <linux/usb.h>
-+#include "xpp_proto.h"
 +#include "xpd.h"
++#include "xproto.h"
 +#include "xpp_zap.h"
 +
-+static char revision[] = "$Revision: 125 $";
++static char revision[] = "$Revision: 159 $";
 +
 +DEF_PARM(int, print_dbg, 1, "Print DBG statements");	/* must be before zap_debug.h */
 +
@@ -2036,6 +3704,9 @@
 +#define	PROC_USBXPP_SUMMARY	"xpp_usb"
 +#endif
 +
++static cycles_t	stamp_last_pcm_read;
++static cycles_t accumulate_diff;
++
 +struct xusb_model_info;
 +
 +struct xusb_endpoint {
@@ -2182,55 +3853,70 @@
 +static int xusb_packet_send(xbus_t *xbus, xpacket_t *pack)
 +{
 +	struct xpp_usb_bus *xusb = xbus->priv;
-+	int		toxpd = XPD_NUM(pack->content.addr);
 +	struct urb	*urb;
 +	int		ret = 0;
-+	
++	size_t		size;
++
 +	BUG_ON(!pack);
 +	if(!xusb->present) {
 +		NOTICE("tried to send packets to non-exitant USB device. Ignored\n");
 +		goto error;
 +	}
-+	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;
++#if SOFT_SIMULATOR
++	{
++		int		toxpd = XPD_NUM(pack->content.addr);
++
++		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;
++			}
 +		}
-+	} else {
-+		size_t	size = min(PACKET_LEN(pack), (size_t)xusb->ep_out.max_size);
-+		if(pack->content.opcode == XPP_PCM_WRITE) {
-+			XUSB_COUNTER(xusb, PCM_WRITES)++;
++		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)++;
++
++#if 1
++		/*
++		 * DEBUG: high-res timing of PCM_READ to PCM_WRITE
++		 */
++		cycles_t	diff = get_cycles() - stamp_last_pcm_read;
++		accumulate_diff += diff;
++#endif
 +#if 0
-+			static	int rate_limit;
-+			if((rate_limit++ % 1000) == 0)
-+				dump_packet("USB SEND PCM", pack, print_dbg);
++		static	int rate_limit;
++		if((rate_limit++ % 1009) < 3) {
++			dump_packet("USB SEND PCM", pack, print_dbg);
++		}
 +#endif
-+		} else {
-+			dump_packet("USB_PACKET_SEND", pack, print_dbg);
-+		}
-+		urb = xpp_urb_new(xusb, xusb->ep_out.epnum, size, xpp_send_callback);
-+		if (!urb) {
-+			ERR("No free urbs available\n");
-+			ret = -ENOMEM;
-+			goto error;
-+		}
++	} else {
++		dump_packet("USB_PACKET_SEND", pack, print_dbg);
++	}
++	urb = xpp_urb_new(xusb, xusb->ep_out.epnum, size, xpp_send_callback);
++	if (!urb) {
++		ERR("No free urbs available\n");
++		ret = -ENOMEM;
++		goto error;
++	}
 +
-+		/* 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);
++	/* 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);
-+		if(ret < 0) {
-+			ERR("%s: failed submit_urb\n", __FUNCTION__);
-+			XUSB_COUNTER(xusb, TX_ERRORS)++;
-+			xpp_urb_delete(urb);
-+			return -EBADF;
-+		}
++	ret = usb_submit_urb(urb, GFP_KERNEL);
++	if(ret < 0) {
++		ERR("%s: failed submit_urb\n", __FUNCTION__);
++		XUSB_COUNTER(xusb, TX_ERRORS)++;
++		xpp_urb_delete(urb);
++		return -EBADF;
 +	}
 +	return 0;
 +error:
@@ -2383,7 +4069,7 @@
 +		}
 +		if(epnum & USB_DIR_IN) {	// Input
 +			if(epnum == model_info->in.epnum) {
-+				if(endpoint->wMaxPacketSize < sizeof(xpp_packet_r_t)) {
++				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;
 +				}
@@ -2392,7 +4078,7 @@
 +			}
 +		} else {					// Output
 +			if(epnum == model_info->out.epnum) {
-+				if(endpoint->wMaxPacketSize < sizeof(xpp_packet_r_t)) {
++				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;
 +				}
@@ -2611,15 +4297,17 @@
 +
 +static void xpp_send_callback(struct urb *urb, struct pt_regs *regs)
 +{
-+        struct xpp_usb_bus	*xusb = (struct xpp_usb_bus *)urb->context;
++	struct xpp_usb_bus	*xusb = (struct xpp_usb_bus *)urb->context;
 +	xbus_t			*xbus = xusb->xbus;
 +
 +	BUG_ON(!xbus);
-+        /* sync/async unlink faults aren't errors */
-+        if (urb->status && !(urb->status == -ENOENT || urb->status == -ECONNRESET)) {
-+                DBG("nonzero read bulk status received: %d", urb->status);
++	/* sync/async unlink faults aren't errors */
++	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);
 +		XUSB_COUNTER(xusb, TX_ERRORS)++;
-+        }
++	}
 +	if(!xusb->present) {
 +		ERR("A packet from non-connected device?\n");
 +		return;
@@ -2631,7 +4319,7 @@
 +
 +static void xpp_receive_callback(struct urb *urb, struct pt_regs *regs)
 +{
-+        struct xpp_usb_bus	*xusb = (struct xpp_usb_bus *)urb->context;
++	struct xpp_usb_bus	*xusb = (struct xpp_usb_bus *)urb->context;
 +	xbus_t			*xbus = xusb->xbus;
 +
 +	xpacket_t		*pack;
@@ -2639,7 +4327,7 @@
 +	int			retval;
 +
 +	BUG_ON(!xbus);
-+        if (urb->status) {
++	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);
@@ -2648,7 +4336,7 @@
 +			DBG("nonzero read bulk status received: %d\n", urb->status);
 +			goto end;
 +		}
-+        }
++	}
 +	if(!down_read_trylock(&xbus->in_use)) {
 +		ERR("%s: xbus is going down\n", __FUNCTION__);
 +		return;
@@ -2663,23 +4351,30 @@
 +		ERR("%s: Not enough memory for packets. Dropping\n", __FUNCTION__);
 +		goto end;
 +	}
-+	
++
 +	size = urb->actual_length;
 +	memcpy(&pack->content, urb->transfer_buffer, size);
-+	
-+	pack->datalen = size - sizeof(xpp_addr_t) - 1; 	// opcode size
++
++	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 == XPP_PCM_READ) {
++	if(pack->content.opcode == XPROTO_NAME(GLOBAL,PCM_READ)) {
 +		XUSB_COUNTER(xusb, PCM_READS)++;
++
++#if 1
++		/*
++		 * 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 == XPP_PCM_WRITE) {	// FIRMWARE_LOOPBACK
++	} else if(pack->content.opcode == XPROTO_NAME(GLOBAL,PCM_WRITE)) {	// FIRMWARE_LOOPBACK
 +#if 0
 +		static	int rate_limit;
 +		if((rate_limit++ % 1000) == 0)
@@ -2689,7 +4384,7 @@
 +		char	title[XBUS_DESCLEN];
 +
 +		snprintf(title, XBUS_DESCLEN, "USB_PACKET_RECEIVE callback (%s)", xbus->busname);
-+		// dump_packet(title, pack, print_dbg);
++		dump_packet(title, pack, print_dbg);
 +	}
 +	packet_receive(xbus, pack);
 +	XUSB_COUNTER(xusb, RX_PACKETS)++;
@@ -2745,8 +4440,10 @@
 +					ERR("%s: BUG: xpd->id=%d != j=%d\n", __FUNCTION__, xpd->id, j);
 +					continue;
 +				}
-+				CALL_PROTO(CHAN_ENABLE, xbus, xpd, 0xFF, 0);		// Disable all hardware channels
-+				CALL_PROTO(LED, xbus, xpd, 0xFF, 1, 0);			// FIXME: Show activated channels
++#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
 +			}
 +		}
 +	}
@@ -2787,6 +4484,7 @@
 +		xusb->ep_out.epnum,
 +		xusb->ep_out.max_size
 +	);
++	len += sprintf(page + len, "\nstamp_last_pcm_read=%lld accumulate_diff=%lld\n", stamp_last_pcm_read, accumulate_diff);
 +	len += sprintf(page + len, "\nCOUNTERS:\n");
 +	for(i = 0; i < XUSB_COUNTER_MAX; i++) {
 +		len += sprintf(page + len, "\t%-15s = %d\n", xusb_counters[i].name, xusb->counters[i]);
@@ -2813,14 +4511,14 @@
 +MODULE_DESCRIPTION("XPP USB Driver");
 +MODULE_AUTHOR("Oron Peled <oron at actcom.co.il>");
 +MODULE_LICENSE("GPL");
-+MODULE_VERSION("$Id: xpp_usb.c 125 2005-11-10 15:42:41Z oron $");
++MODULE_VERSION("$Id: xpp_usb.c 159 2006-01-03 08:49:05Z oron $");
 +
 +module_init(xpp_usb_init);
 +module_exit(xpp_usb_cleanup);
-diff -urNad trunk/xpp/xpp_zap.c /tmp/dpep.oAj2YR/trunk/xpp/xpp_zap.c
---- trunk/xpp/xpp_zap.c	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_zap.c	2005-11-10 04:05:18.000000000 +0200
-@@ -0,0 +1,1881 @@
+diff -urNad zaptel-1.0.10/xpp/xpp_zap.c /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_zap.c
+--- zaptel-1.0.10/xpp/xpp_zap.c	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_zap.c	2006-01-03 15:08:38.089954000 +0200
+@@ -0,0 +1,2273 @@
 +/*
 + * Written by Oron Peled <oron at actcom.co.il>
 + * Copyright (C) 2004, Xorcom
@@ -2861,17 +4559,19 @@
 +#include <linux/workqueue.h>
 +#include <linux/proc_fs.h>
 +#include <zaptel.h>
-+#include "xpp_proto.h"
++#include "xproto.h"
 +#include "xpp_zap.h"
-+#include "xpd.h"
 +
-+static char revision[] = "$Revision: 124 $";
++static char revision[] = "$Revision: 167 $";
 +
 +#ifdef CONFIG_PROC_FS
 +struct proc_dir_entry *xpp_procdir = NULL;
 +#define	PROC_DIR		"xpp"
 +#define	PROC_XBUSES		"xbuses"
++#define	PROC_SYNC		"sync"
 +#define	PROC_XBUS_SUMMARY	"summary"
++#define	PROC_XBUS_ZTREGISTER	"zt_registration"
++#define	PROC_SLIC_FNAME		"%s.slic"
 +#endif
 +
 +#undef	WITH_RBS
@@ -2881,6 +4581,7 @@
 +#define	MAX_BUSES		16
 +#define	MAX_QUEUE_LEN		10000
 +#define	LED_BLINK_PERIOD	(HZ/8)
++#define	SAMPLE_TICKS		10000
 +
 +#define	N_(x)	[ x] = #x
 +static const char *xpd_type_names[] = {
@@ -2890,11 +4591,13 @@
 +};
 +#undef N_
 +
-+static	spinlock_t		xbuses_lock = SPIN_LOCK_UNLOCKED;
-+static	xbus_t			*xbuses_array[MAX_BUSES] = {};
-+static	int			bus_count = 0;
-+static	bool			pcm_write_enable = 1;	// DEBUG
++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;
++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;
 +
 +static	LIST_HEAD(xpd_list);
 +
@@ -2902,20 +4605,28 @@
 +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(ulong, pcm_gen, 0, "a bitmask of line numbers for hardware tone generator");
 +
 +DEF_ARRAY(ulong, enabled_channels, MAX_XPDS, ~0, "Enabled channels for each xpd");
 +
 +#include "zap_debug.h"
 +
++
++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);
++xbus_t *xbus_of(int xbus_num);
 +
++#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);
@@ -2967,16 +4678,82 @@
 +	//		xbus->busname, atomic_read(&xbus->packet_counter));
 +}
 +
-+static void xpp_timer_tick(unsigned long param)
++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 external_sync(xpd_t *the_xpd)
++{
++	int	i, j;
++
++	DBG("SYNC %s\n", (the_xpd) ? "EXTERNAL" : "HOST");
++	for(i = 0; i < MAX_BUSES; i++) {
++		xbus_t	*xbus = xbus_of(i);
++		if(!xbus)
++			continue;
++		if (!xbus->hardware_exists)
++			continue;
++		for(j = 0; j < MAX_XPDS; j++) {
++			xpd_t	*xpd = xbus->xpds[j];
++			if(xpd)
++				CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, xpd, 1, (the_xpd != NULL));
++		}
++	}
++}
++
++void set_sync_master(xpd_t *xpd)
++{
++	DBG("SYNC: %s => %s\n",
++			(sync_master) ? sync_master->xpdname : "HOST",
++			(xpd) ? xpd->xpdname : "HOST"
++			);
++	sync_master = xpd;
++	if(!sync_master) {
++		external_sync(NULL);
++		if(!timer_pending(&xpp_timer)) {
++			xpp_timer.function = xpp_tick;
++			xpp_timer.data = 0;
++			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);
++	}
++}
++
++void xpp_tick(unsigned long param)
++{
 +	xbus_t	*xbus;
-+	xpd_t	*xpd;
++	xpd_t	*the_xpd = (xpd_t *)param;
 +	int	i;
 +	int	j;
 +
-+	mod_timer(&xpp_timer, jiffies + 1);	/* Must be 1KHz rate */
++	if(!the_xpd) {		/* Called from timer */
++#if 0
++		static int rate_limit = 0;
++		if(rate_limit++ % 1000 == 0)
++			DBG("FROM_TIMER\n");
++#endif
++		mod_timer(&xpp_timer, jiffies + 1);	/* Must be 1KHz rate */
++	}
++	else if(the_xpd != sync_master)
++		return;
++	/* Statistics */
++	if((xpp_timer_count % SAMPLE_TICKS) == 0) {
++		xpp_last_jiffies = jiffies;
++	}
++	xpp_timer_count++;
++
 +	for(i = 0; i < MAX_BUSES; i++) {
-+		xbus = xbuses_array[i];
++		xbus = xbus_of(i);
 +		if(!xbus)
 +			continue;
 +		if (!xbus->hardware_exists)
@@ -2986,13 +4763,16 @@
 +			continue;		// optimize, but zttool loopback won't function
 +#endif
 +		for(j = 0; j < MAX_XPDS; j++) {
-+			xpd = xbus->xpds[j];
++			xpd_t	*xpd = xbus->xpds[j];
++
 +			if(!xpd)
 +				continue;
 +			xpd->timer_count++;
-+			xpd_blink_leds(xpd);
 +			if(xpd->state != XPD_STATE_ACTIVE)
 +				continue;
++			if(!SPAN_REGISTERED(xpd))
++				continue;
++			xpd_blink_leds(xpd);
 +			if(xpd->direction == TO_TRUNK)
 +				xpp_ring_generate(xpd);
 +			xpp_transmitprep(xpd);
@@ -3016,7 +4796,8 @@
 + * registration to zaptel is done "off-line" by posting a registration to the
 + * "reg" workqueue that calls this function.
 + */
-+void xpd_initialize(void *data) {
++void xpd_initialize(void *data)
++{
 +	xpd_t		*xpd = (xpd_t *)data;
 +	xbus_t		*xbus;
 +	
@@ -3027,43 +4808,37 @@
 +	xbus = xpd->xbus;
 +#ifdef	CONFIG_PROC_FS
 +	DBG("Creating xpd proc file %s.\n", xpd->xpdname);
-+	xpd->xpd_proc = create_proc_read_entry(xpd->xpdname, 0644, xpd->xbus->procdir,
++	xpd->xpd_proc = create_proc_read_entry(xpd->xpdname, 0644, xbus->procdir,
 +			xpd_read_proc, xpd);
 +	if(!xpd->xpd_proc) {
 +		ERR("Failed to create proc file for xpd %s\n", xpd->xpdname);
 +		return;
 +	}
++	{
++		char fname[NAME_MAX];
++
++		snprintf(fname, NAME_MAX, PROC_SLIC_FNAME, xpd->xpdname);
++		DBG("Creating xpd slic file %s.\n", fname);
++		xpd->xpd_slic = create_proc_entry(fname, 0644, xbus->procdir);
++		if(!xpd->xpd_slic) {
++			ERR("Failed to create proc file for SLIC xpd %s\n", fname);
++			return;
++		}
++		xpd->xpd_slic->write_proc = proc_xpd_slic_write;
++		xpd->xpd_slic->read_proc = proc_xpd_slic_read;
++		xpd->xpd_slic->data = xpd;
++	}
 +#endif
 +	list_add(&xpd->xpd_list, &xpd_list);
 +	xbus->xpds[xpd->id] = xpd;
 +	xbus->num_xpds++;
-+	CALL_PROTO(SLIC_INIT, xbus, xpd);
++	CALL_XMETHOD(card_init, xbus, xpd);
 +	// Turn off all channels
-+	CALL_PROTO(CHAN_ENABLE, xbus, xpd, 0xFF, 0);
-+//	CALL_PROTO(LED, xbus, xpd, 0xFF, 1, 0);				// FIXME: Show activated 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_PROTO(CHAN_ENABLE, xbus, xpd, xpd->enabled_chans, 1);
-+	DBG("Registering span of %s.\n", xpd->xpdname);
-+	if(zt_register(&xpd->span, 1)) {
-+		ERR("Failed to zt_register of span of xpd %s.\n", 
-+				xpd->xpdname);
-+		xbus->xpds[xpd->id] = NULL;
-+		list_del(&xpd->xpd_list);
-+		xbus->num_xpds--;
-+		xpd->state = XPD_STATE_ZAPTEL_ERR;
-+		return;
-+	}
-+#if 0
-+	{
-+		static char buf[ZT_CHUNKSIZE] = { 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67 };	/* silence */
-+		// static char buf[ZT_CHUNKSIZE] = { 0x7F, 0xBE, 0xD8, 0xBE, 0x80, 0x41, 0x24, 0x41 };	/* Dima */
-+		CALL_PROTO(PCM_GEN, xbus, xpd->id, pcm_gen, buf);
-+	}
-+#endif
-+
-+	atomic_inc(&xpd->span_registered);
-+
-+	xpd->state = XPD_STATE_ACTIVE;
++	CALL_XMETHOD(CHAN_ENABLE, xbus, xpd, xpd->enabled_chans, 1);
++	xpd_zaptel_register(xpd);
 +}
 +
 +void init_xbus_packet_queue(packet_queue_t *q, const char name[])
@@ -3183,7 +4958,9 @@
 +	int		len = 0;
 +	xpd_t		*xpd = data;
 +	xbus_t		*xbus;
++#if SOFT_SIMULATOR
 +	struct xpd_sim	*sim;
++#endif
 +	int		channels;
 +	int		i;
 +
@@ -3191,12 +4968,15 @@
 +		goto out;
 +
 +	xbus = xpd->xbus;
++#if SOFT_SIMULATOR
 +	sim = &xbus->sim[xpd->id];
++#endif
 +	channels = xpd->channels;
-+	len += sprintf(page + len, "%s (%s ,state=%d, span_registered=%d)\n"
++	len += sprintf(page + len, "%s (%s ,state=%d, span_registered=%s)%s\n"
 +			"timer_count: %d span->mainttimer=%d\n"
 +			,
-+			xpd->xpdname, xpd_type_names[xpd->type], xpd->state, atomic_read(&xpd->span_registered),
++			xpd->xpdname, xpd_type_names[xpd->type], xpd->state, (SPAN_REGISTERED(xpd))?"yes":"no",
++			(xpd == sync_master) ? " SYNCER" : "",
 +			xpd->timer_count, xpd->span.mainttimer
 +			);
 +	len += sprintf(page + len, "STATES:");
@@ -3204,26 +4984,39 @@
 +	for(i = 0; i < channels; 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++) {
++		len += sprintf(page + len, "%d ", IS_SET(xpd->digital_outputs, i));
++	}
 +	len += sprintf(page + len, "\n\t%-17s: ", "hookstate");
 +	for(i = 0; i < channels; 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++) {
 +		len += sprintf(page + len, "%d ", xpd->ringing[i]);
 +	}
 +#if 1
-+	len += sprintf(page + len, "\nreadchunk: ");
-+	for(i = 0; i < channels; i++) {
-+		struct zt_chan	*chans = xpd->span.chans;
-+		int j;
++	if(SPAN_REGISTERED(xpd)) {
++		len += sprintf(page + len, "\nreadchunk: ");
++		for(i = 0; i < channels; i++) {
++			struct zt_chan	*chans = xpd->span.chans;
++			byte	chunk[ZT_CHUNKSIZE];
++			int j;
 +
-+		len += sprintf(page + len, "\n\tport %2d> ", i);
-+		for(j = 0; j < ZT_CHUNKSIZE; j++) {
-+			len += sprintf(page + len, "%02X ", chans[i].readchunk[j]);
++			memcpy(chunk, chans[i].readchunk, ZT_CHUNKSIZE);
++			len += sprintf(page + len, "\n\tport %2d> ", i);
++			for(j = 0; j < ZT_CHUNKSIZE; j++) {
++				len += sprintf(page + len, "%02X ", chunk[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");
@@ -3231,11 +5024,14 @@
 +			len += sprintf(page + len, "%d ", IS_SET(sim->hookstate, i));
 +		}
 +	}
++#endif
 +#if 0
-+	len += sprintf(page + len, "\nSignalling:\n");
-+	for(i = 0; i < channels; 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);
++	if(SPAN_REGISTERED(xpd)) {
++		len += sprintf(page + len, "\nSignalling:\n");
++		for(i = 0; i < channels; 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);
++		}
 +	}
 +#endif
 +	len += sprintf(page + len, "\nCOUNTERS:\n");
@@ -3259,28 +5055,31 @@
 +
 +#endif
 +
-+static int span_init(xpd_t *xpd);
-+
-+xpd_t *xpd_new(xbus_t *xbus, int xpd_num, xpd_type_t type)
++xpd_t *xpd_new(xbus_t *xbus, int xpd_num, xpd_type_t type, byte revision)
 +{
-+	int ret;
 +	unsigned long	flags;
-+	int channels;
-+	xpd_t *xpd = NULL;
++	xpd_t		*xpd = NULL;
++	xops_t		*xops;
 +
 +	spin_lock_irqsave(&xbus->lock, flags);
-+	INFO("New XPD #%d detected on xbus %s\n", xpd_num, xbus->busname);
++	INFO("New XPD #%d (Revision %d.%d) detected on xbus %s\n",
++			xpd_num, revision / 10, revision % 10, xbus->busname);
 +	if(!VALID_XPD_NUM(xpd_num)) {
-+		ERR("xpd_new: illegal xpd id = %d\n", xpd_num);
++		ERR("%s: illegal xpd id = %d\n", __FUNCTION__, xpd_num);
 +		goto err;
 +	}
++	if((xops = get_xops(type)) == NULL) {
++		ERR("%s: no xops for type=%d\n", __FUNCTION__, type);
++		goto err;
++	}
 +	if((xpd = kmalloc(sizeof(xpd_t), GFP_ATOMIC)) == NULL) {
-+		ERR("xpd_new: Unable to allocate memory for xpd\n");
++		ERR("%s: Unable to allocate memory for xpd\n", __FUNCTION__);
 +		goto err;
 +	}
 +	memset(xpd, 0, sizeof(xpd_t));
 +
 +	spin_lock_init(&xpd->lock);
++	xpd->xops = xops;
 +	xpd->xbus = xbus;
 +	xpd->id = xpd_num;
 +	xpd->chans = NULL;
@@ -3289,50 +5088,47 @@
 +	xpd->hookstate = 0x0;	/* ONHOOK */
 +	xpd->type = type;
 +	xpd->enabled_chans = enabled_channels[xpd_num];
++	xpd->digital_outputs = 0;
++	atomic_set(&xpd->open_counter, 0);
 +
-+	switch(xpd->type) {
-+		case XPD_TYPE_FXS:
-+			channels = xpd->channels = min(8, CHANNELS_PERXPD);
-+			xpd->direction = TO_PHONE;
-+			break;
-+		case XPD_TYPE_FXO:
-+			channels = xpd->channels = min(8, CHANNELS_PERXPD);
-+			xpd->direction = TO_TRUNK;
-+			break;
-+#if 0
-+		case XPD_TYPE_E1:
-+			channels = xpd->channels = CHANNELS_PERXPD;
-+			break;
-+#endif
-+		case XPD_TYPE_NOMODULE:
-+		default:
-+			ERR("%s: bad xpd->type=0x%X\n", __FUNCTION__, xpd->type);
-+			goto err;
-+	}
-+
 +	INIT_WORK(&xpd->xpd_post_init, xpd_initialize, xpd);
 +
++	CALL_XMETHOD(card_new, xbus, xpd, revision);
++	if(xpd_setup(xpd) != 0)
++		goto err;
++
++	spin_unlock_irqrestore(&xbus->lock, flags);
++	return xpd;
++err:
++	spin_unlock_irqrestore(&xbus->lock, flags);
++	if(xpd->chans)
++		kfree((void *)xpd->chans);
++	if(xpd->writechunk)
++		kfree((void *)xpd->writechunk);
++	if(xpd)
++		kfree(xpd);
++	return NULL;
++}
++
++int xpd_setup(xpd_t *xpd)
++{
++	xpd->chans = kmalloc(sizeof(struct zt_chan)*xpd->channels, GFP_ATOMIC);
++	if (xpd->chans == NULL) {
++		ERR("%s: Unable to allocate channels\n", __FUNCTION__);
++		return -ENOMEM;
++	}
 +	/* 8 channels, double buffer, Read/Write */
 +	int need = ZT_MAX_CHUNKSIZE * CHANNELS_PERXPD * 2 * 2;
 +	if((xpd->writechunk = kmalloc(need, GFP_ATOMIC)) == NULL) {
-+		ERR("xpd_new: Unable to allocate memory for writechunks\n");
-+		goto err;
++		ERR("%s: Unable to allocate memory for writechunks\n", __FUNCTION__);
++		kfree(xpd->chans);
++		xpd->chans = NULL;
++		return -ENOMEM;
 +	}
 +	/* Initialize Write/Buffers to all blank data */
 +	memset((void *)xpd->writechunk, 0x00, need);
 +	xpd->readchunk = xpd->writechunk + ZT_CHUNKSIZE * CHANNELS_PERXPD * 2;
 +
-+	if((ret = span_init(xpd)) < 0) {
-+		ERR("Unable to initialize new span\n");
-+		goto err;
-+	}
-+	DBG("Queueing xpd_post_init for xpd %d\n", xpd->id);
-+	if(!queue_work(xpd_init_workqueue, &xpd->xpd_post_init)) {
-+		ERR("Failed to queue xpd_post_init work\n");
-+		goto err;
-+	}
-+
-+
 +#if 0
 +	/*
 +	 * Is it nessessary?
@@ -3345,41 +5141,42 @@
 +		}
 +	}
 +#endif
-+	spin_unlock_irqrestore(&xbus->lock, flags);
-+	return xpd;
-+err:
-+	spin_unlock_irqrestore(&xbus->lock, flags);
-+	if(xpd->writechunk)
-+		kfree((void *)xpd->writechunk);
-+	if(xpd)
-+		kfree(xpd);
-+	return NULL;
++
++	DBG("Queueing xpd_post_init for xpd %d\n", xpd->id);
++	if(!queue_work(xpd_init_workqueue, &xpd->xpd_post_init)) {
++		ERR("Failed to queue xpd_post_init work\n");
++		return -EINVAL;
++	}
++	return 0;
 +}
 +
-+static void xpd_remove(xbus_t *xbus, xpd_t *xpd)
++void xpd_remove(xpd_t *xpd)
 +{
-+	BUG_ON(xpd == NULL);
-+	BUG_ON(xbus == NULL);
-+	BUG_ON(xpd->xbus != xbus);	// xpd belong to another xbus
++	xbus_t	*xbus;
++
++	BUG_ON(!xpd);
++	xbus = xpd->xbus;
 +	INFO("Remove XPD #%d from xbus=%s\n", xpd->id, xbus->busname);
 +	xpd->state = XPD_STATE_OFF;
-+	if (atomic_read(&xpd->span_registered)) {
-+		zt_unregister(&xpd->span);
-+		atomic_dec(&xpd->span_registered);
-+	}
-+	if(xpd->chans) {	/* Already registered */
-+		kfree(xpd->chans);
-+	}
++	xpd_zaptel_unregister(xpd);
 +#ifdef CONFIG_PROC_FS
 +	if(xpd->xpd_proc) {
 +		DBG("Removing proc entry %s\n", xpd->xpdname);
 +		remove_proc_entry(xpd->xpdname, xbus->procdir);
 +	}
++	if(xpd->xpd_slic) {
++		char fname[NAME_MAX];
++
++		snprintf(fname, NAME_MAX, PROC_SLIC_FNAME, xpd->xpdname);
++		DBG("Removing proc entry %s\n", fname);
++		remove_proc_entry(fname, xbus->procdir);
++	}
 +#endif
 +	xbus->xpds[xpd->id] = NULL;
 +	list_del(&xpd->xpd_list);
 +	xbus->num_xpds--;
 +	cancel_delayed_work(&xpd->xpd_post_init);
++	CALL_XMETHOD(card_remove, xbus, xpd);
 +	kfree((void *)xpd->writechunk);
 +	kfree(xpd);
 +}
@@ -3388,6 +5185,10 @@
 +{
 +	struct zt_span *span = &xpd->span;
 +
++	if(!SPAN_REGISTERED(xpd)) {
++		NOTICE("%s: %s is not registered. Skipping.\n", __FUNCTION__, xpd->xpdname);
++		return;
++	}
 +	switch (alarm_flag) {
 +		case ZT_ALARM_NONE:
 +			xpd->state = XPD_STATE_ACTIVE;
@@ -3412,11 +5213,15 @@
 +	int	i;
 +	unsigned long	flags;
 +
++	spin_lock_irqsave(&xpd->lock, flags);
 +	if(xpd->direction != TO_PHONE) {
 +		ERR("%s: %s: Only FXS can report hookstate changes\n", __FUNCTION__, xpd->xpdname);
-+		return;
++		goto out;
 +	}
-+	spin_lock_irqsave(&xpd->lock, flags);
++	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++) {
 +		struct zt_chan *chan = &xpd->span.chans[i];
@@ -3425,17 +5230,18 @@
 +			xpd->ringing[i] = 0;
 +			BIT_SET(xpd->hookstate, i);
 +			zt_hooksig(chan, ZT_RXSIG_OFFHOOK);
-+			CALL_PROTO(CHAN_POWER, xpd->xbus, xpd, (1 << i), 0);		// Power down (prevent overheating!!!)
-+			CALL_PROTO(LED, xpd->xbus, xpd, (1 << i), 0, 1);
++			CALL_XMETHOD(CHAN_POWER, xpd->xbus, xpd, BIT(i), 0);		// Power down (prevent overheating!!!)
++			CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(i), LED_GREEN, 1);
 +		} else if(!IS_SET(fxs_off_hook, i) && IS_SET(xpd->hookstate, i)) {	// ONHOOK
 +			DBG("ONHOOK channo=%d\n", chan->channo);
 +			xpd->ringing[i] = 0;
 +			BIT_CLR(xpd->hookstate, i);
 +			zt_hooksig(chan, ZT_RXSIG_ONHOOK);
-+			CALL_PROTO(CHAN_POWER, xpd->xbus, xpd, (1 << i), 0);		// Power down (prevent overheating!!!)
-+			CALL_PROTO(LED, xpd->xbus, xpd, (1 << i), 0, 0);
++			CALL_XMETHOD(CHAN_POWER, xpd->xbus, xpd, BIT(i), 0);		// Power down (prevent overheating!!!)
++			CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(i), LED_GREEN, 0);
 +		}
 +	}
++out:
 +	spin_unlock_irqrestore(&xpd->lock, flags);
 +}
 +
@@ -3446,22 +5252,24 @@
 +
 +	BUG_ON(!xpd);
 +
++	spin_lock_irqsave(&xpd->lock, flags);
 +	for(i = 0; i < xpd->channels; i++) {
-+		spin_lock_irqsave(&xpd->lock, flags);
++		if(IS_SET(xpd->digital_outputs, i))
++			continue;
 +		if(xpd->ringing[i]) {
 +			// led state is toggled
 +			if((xpd->timer_count % LED_BLINK_PERIOD) == 0) {
-+				DBG("%s pos=%d =%d led_on=%d\n", xpd->xpdname, i, xpd->ringing[i], xpd->led_on[i]);
++				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_PROTO(LED, xpd->xbus, xpd, (1 << i), 0, 1);
++					CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(i), LED_GREEN, 1);
 +				} else {
-+					CALL_PROTO(LED, xpd->xbus, xpd, (1 << i), 0, 0);
++					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);
 +	}
++	spin_unlock_irqrestore(&xpd->lock, flags);
 +}
 +
 +static void xpp_ring_generate(xpd_t *xpd)
@@ -3472,17 +5280,20 @@
 +
 +	BUG_ON(!xpd);
 +
++	spin_lock_irqsave(&xpd->lock, flags);
 +	if(xpd->direction != TO_TRUNK && ((bug_counter++ % 1000) == 0)) {
 +		ERR("%s: %s: Only FXO can report ring changes\n", __FUNCTION__, xpd->xpdname);
-+		return;
++		goto out;
 +	}
-+
++	if(!SPAN_REGISTERED(xpd)) {
++		NOTICE("%s: %s is not registered. Skipping.\n", __FUNCTION__, xpd->xpdname);
++		goto out;
++	}
 +	/*
 +	 * Ring detect logic:
 +	 * 	fxo_power is toggled
 +	 */
 +	for(i = 0; i < xpd->channels; i++) {
-+		spin_lock_irqsave(&xpd->lock, flags);
 +		if(xpd->ringing[i] || xpd->ringer_on[i]) {
 +			// ring state is only changed once per second:
 +			if((xpd->timer_count % 1000) == 0) {
@@ -3498,8 +5309,9 @@
 +					xpd->ringing[i]=0;
 +			}
 +		}
-+		spin_unlock_irqrestore(&xpd->lock, flags);
 +	}
++out:
++	spin_unlock_irqrestore(&xpd->lock, flags);
 +}
 +
 +/*------------------------- Bus Management -------------------------*/
@@ -3562,6 +5374,7 @@
 +			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];
@@ -3569,6 +5382,7 @@
 +		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",
@@ -3589,6 +5403,142 @@
 +
 +}
 +
++int proc_sync_read(char *page, char **start, off_t off, int count, int *eof, void *data)
++{
++	int len = 0;
++
++	if(!sync_master)
++		len += sprintf(page + len, "HOST\n");
++	else
++		len += sprintf(page + len, "%s/%s\n", sync_master->xbus->busname, sync_master->xpdname);
++	len += sprintf(page + len, "xpp_timer_count=%d\n", xpp_timer_count);
++	unsigned int xpp_timer_rate = 0;
++	unsigned int 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, "xpp_timer_rate=%d\n", xpp_timer_rate);
++	}
++	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_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;
++	int		xpd_num;
++	xbus_t		*xbus;
++	xpd_t		*xpd;
++	int		ret;
++
++	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);
++		goto out;
++	}
++	ret = sscanf(buf, "%d %d", &xbus_num, &xpd_num);
++	if(ret != 2)
++		return -EINVAL;
++	DBG("%s: %d/%d\n", __FUNCTION__, xbus_num, xpd_num);
++	if(xbus_num >= MAX_BUSES)
++		return -EINVAL;
++	xbus = xbus_of(xbus_num);
++	if(!xbus)
++		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);
++out:
++	return count;
++}
++
++int proc_xbus_ztregister_read(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, "# To (un)register an XPD write into this file: <ID> <1/0>\n");
++	len += sprintf(page + len, "# ID\tExists\tRegistered\n");
++	for(i = 0; i < MAX_XPDS; i++) {
++		xpd_t	*xpd = xbus->xpds[i];
++		bool	registered = xpd && SPAN_REGISTERED(xpd);
++		len += sprintf(page + len, "%d\t%s\t%s\n", i, (xpd)?"yes":"no", (registered)?"yes":"no");
++	}
++	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 proc_xbus_ztregister_write(struct file *file, const char __user *buffer, unsigned long count, void *data)
++{
++	xbus_t		*xbus = data;
++	xpd_t		*xpd;
++	int		xpd_num;
++	const int	NUM_SIZE = 100;
++	char		buf[NUM_SIZE];
++	bool		zt_reg;
++	int		ret;
++
++	BUG_ON(!xbus);
++	if(count >= NUM_SIZE)
++		return -EINVAL;
++	if(copy_from_user(buf, buffer, count))
++		return -EFAULT;
++	buf[count] = '\0';
++	ret = sscanf(buf, "%d %d", &xpd_num, &zt_reg);
++	if(ret != 2)
++		return -EINVAL;
++	xpd = xpd_of(xbus, xpd_num);
++	if(!xpd) {
++		ERR("%s: XPD number %d does not exist\n", __FUNCTION__, xpd_num);
++		return -ENXIO;
++	}
++	DBG("%s: %s: xpd #%d %s\n", __FUNCTION__, xbus->busname, xpd_num, (zt_reg) ? "register" : "unregister");
++	if(zt_reg)
++		ret = xpd_zaptel_register(xpd);
++	else
++		ret = xpd_zaptel_unregister(xpd);
++	return (ret < 0) ? ret : count;
++}
++
++int xpd_slic_write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data)
++{
++	xpd_t		*xpd = data;
++
++	BUG_ON(!xpd);
++	return count;
++}
++
 +#endif
 +
 +/**
@@ -3622,6 +5572,22 @@
 +		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;
@@ -3669,7 +5635,7 @@
 +			continue;
 +		}
 +		DBG("  Polling slot %d %s\n", id, xbus->busname);
-+		ret = CALL_PROTO(DESC_REQ, xbus, NULL, id);
++		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);
 +		}
@@ -3684,6 +5650,7 @@
 +}
 +
 +
++#if SOFT_SIMULATOR
 +/*
 + * Assume xbus->lock is held
 + */
@@ -3716,6 +5683,7 @@
 +		first = !first;
 +	}
 +}
++#endif
 +
 +void xbus_activate(xbus_t *xbus)
 +{
@@ -3750,13 +5718,15 @@
 +	xbus->hardware_exists = 0;
 +	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;
-+			}
-+			update_xpd_status(xpd, ZT_ALARM_NOTOPEN);
++		if(!xpd)
++			continue;
++		if(xpd->id != i) {
++			ERR("%s: BUG: xpd->id=%d != i=%d\n", __FUNCTION__, xpd->id, i);
++			continue;
 +		}
++		if (!SPAN_REGISTERED(xpd))
++			continue;
++		update_xpd_status(xpd, ZT_ALARM_NOTOPEN);
 +	}
 +	down_write(&xbus->in_use);
 +	DBG("%s (deactivated)\n", xbus->busname);
@@ -3776,6 +5746,11 @@
 +			remove_proc_entry(PROC_XBUS_SUMMARY, xbus->procdir);
 +			xbus->procsummary = NULL;
 +		}
++		if(xbus->proc_ztregister) {
++			DBG("Removing proc entry " PROC_XBUS_ZTREGISTER ".\n");
++			remove_proc_entry(PROC_XBUS_ZTREGISTER, xbus->procdir);
++			xbus->proc_ztregister = NULL;
++		}
 +		DBG("Removing proc entry %s.\n",xbus->busname);
 +		remove_proc_entry(xbus->busname, xpp_procdir);
 +		xbus->procdir = NULL;
@@ -3818,6 +5793,8 @@
 +	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);
@@ -3825,12 +5802,11 @@
 +		goto nobus;
 +	}
 +	init_xbus_packet_queue(&xbus->sim_packet_queue, "SIM_PACKET_QUEUE");
-+	xbus->num_xpds = 0;
 +	INIT_WORK(&xbus->sim_work, process_sim_queue, xbus);
-+	INIT_WORK(&xbus->poll_work, process_xbus_poll, xbus);
-+	xbus_reset_counters(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->procdir = proc_mkdir(xbus->busname, xpp_procdir);
@@ -3842,10 +5818,19 @@
 +	xbus->procsummary = create_proc_read_entry(PROC_XBUS_SUMMARY, 0644, xbus->procdir,
 +			xbus_read_proc, xbus);
 +	if (!xbus->procsummary) {
-+		ERR("Failed to create proc read entry for xbus %s\n", xbus->busname);
++		ERR("Failed to create '%s' proc file for xbus %s\n", PROC_XBUS_SUMMARY, xbus->busname);
 +		err = -EIO;
 +		goto nobus;
 +	}
++	xbus->proc_ztregister = create_proc_entry(PROC_XBUS_ZTREGISTER, 0644, xbus->procdir);
++	if (!xbus->proc_ztregister) {
++		ERR("Failed to create '%s' proc file for xbus %s\n", PROC_XBUS_ZTREGISTER, xbus->busname);
++		err = -EIO;
++		goto nobus;
++	}
++	xbus->proc_ztregister->data = xbus;
++	xbus->proc_ztregister->read_proc = proc_xbus_ztregister_read;
++	xbus->proc_ztregister->write_proc = proc_xbus_ztregister_write;
 +#endif
 +	return xbus;
 +nobus:
@@ -3862,36 +5847,44 @@
 +	int i;
 +	unsigned long flags;
 +
-+	DBG("\n");
 +	BUG_ON(!xbus);
++	DBG("%s\n", xbus->busname);
 +	spin_lock_irqsave(&xbuses_lock, flags);
-+	BUG_ON(xbus != xbuses_array[xbus->num]);
++	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);
-+		cancel_delayed_work(&xbus->poll_work);
 +	}
++#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) {
++			xops_t	*xops = xpd->xops;
++
 +			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(xbus, xpd);
++			BUG_ON(!xops->card_remove);
++			xpd_remove(xpd);
 +		}
 +		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;
 +	}
 +	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) {
@@ -3916,7 +5909,7 @@
 +	int	channels = xpd->channels;
 +	struct zt_chan	*chans = xpd->span.chans;
 +
-+//	if((xpd->timer_count % PREP_REPORT_RATE) == 0)
++//	if((xpd->timer_count % PREP_REPORT_RATE) < 10)
 +//		DBG("%d\n", xpd->timer_count);
 +
 +//	if(xpd->hookstate == 0)
@@ -3932,13 +5925,15 @@
 +	for (i = 0; i < channels; i++) {
 +		if(IS_SET(xpd->hookstate, i)) {
 +			memcpy((u_char *)w, chans[i].writechunk, ZT_CHUNKSIZE);
-+			// fill_beep((u_char *)w, 2);
++			// fill_beep((u_char *)w, 5);
 +		}
 +		w += ZT_CHUNKSIZE;
 +	}
-+	ret = CALL_PROTO(PCM_WRITE, xpd->xbus, xpd, xpd->hookstate, writechunk);
-+	if(ret < 0) {
-+		DBG("failed to write PCM %d\n", ret);
++	if(xpd->hookstate != 0 || sync_master == xpd) {
++		ret = CALL_XMETHOD(PCM_WRITE, xpd->xbus, xpd, xpd->hookstate, writechunk);
++		if(ret < 0) {
++			DBG("failed to write PCM %d\n", ret);
++		}
 +	}
 +}
 +
@@ -3952,9 +5947,9 @@
 +	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, 0xCD, 0xC5, 0xCD, 0xFF, 0x49, 0x41, 0x49,	/* Dima 2 */
-+		0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67, 0x67,	/* silence */
++		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 */
 +	};
 +	memcpy(buf, &beep[(which*8) % ARRAY_SIZE(beep)], ZT_CHUNKSIZE);
 +}
@@ -3983,7 +5978,7 @@
 +		readchunk += ZT_CHUNKSIZE;
 +	}
 +
-+#if 1
++#if 0
 +	/* FIXME: need to Echo cancel double buffered data */
 +	for (i = 0;i < xpd->span.channels; i++) {
 +		zt_ec_chunk(&chans[i], chans[i].readchunk, xpd->ec_chunk2[i]);
@@ -4022,7 +6017,7 @@
 +	return 0;
 +}
 +
-+static int xpp_open(struct zt_chan *chan)
++int xpp_open(struct zt_chan *chan)
 +{
 +	xpd_t		*xpd = chan->pvt;
 +	xbus_t		*xbus = xpd->xbus;
@@ -4030,12 +6025,13 @@
 +
 +	spin_lock_irqsave(&xbus->lock, flags);
 +	xbus->open_counter++;
++	atomic_inc(&xpd->open_counter);
 +	DBG("chan=%d (open_counter=%d)\n", chan->chanpos, xbus->open_counter);
 +	spin_unlock_irqrestore(&xbus->lock, flags);
 +	return 0;
 +}
 +
-+static int xpp_close(struct zt_chan *chan)
++int xpp_close(struct zt_chan *chan)
 +{
 +	xpd_t		*xpd = chan->pvt;
 +	xbus_t		*xbus = xpd->xbus;
@@ -4044,6 +6040,7 @@
 +
 +	spin_lock_irqsave(&xbus->lock, flags);
 +	xbus->open_counter--;
++	atomic_dec(&xpd->open_counter);
 +	if (!xbus->hardware_exists && xbus->open_counter == 0)
 +		should_remove = 1;
 +	spin_unlock_irqrestore(&xbus->lock, flags);
@@ -4054,25 +6051,36 @@
 +	return 0;
 +}
 +
-+static int xpp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg)
++int xpp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg)
 +{
 +	xpd_t	*xpd = chan->pvt;
 +	int pos = chan->chanpos - 1;
 +	int x;
 +
 +	switch (cmd) {
-+	case ZT_ONHOOKTRANSFER:
-+		if (get_user(x, (int *)arg))
-+			return -EFAULT;
-+		DBG("xpd=%d: ZT_ONHOOKTRANSFER (%d millis) chan=%d\n", xpd->id, x, pos);
-+		break;
-+	default:
-+		DBG("ENOTTY: chan=%d cmd=0x%x\n", pos, cmd);
-+		DBG("        IOC_TYPE=0x%02X\n", _IOC_TYPE(cmd));
-+		DBG("        IOC_DIR=0x%02X\n", _IOC_DIR(cmd));
-+		DBG("        IOC_NR=0x%02X\n", _IOC_NR(cmd));
-+		DBG("        IOC_SIZE=0x%02X\n", _IOC_SIZE(cmd));
-+		return -ENOTTY;
++		case ZT_ONHOOKTRANSFER:
++			if (get_user(x, (int *)arg))
++				return -EFAULT;
++			if (xpd->lasttxhook[pos] == 0x1) {
++				/* Apply the change if appropriate */
++				xpd->lasttxhook[pos] = 0x2;
++				// CALL_XMETHOD(CHAN_CID, xpd->xbus, xpd, BIT(pos));		// CALLER ID
++			}
++			DBG("xpd=%d: ZT_ONHOOKTRANSFER (%d millis) chan=%d\n", xpd->id, x, pos);
++			return -ENOTTY;
++		case ZT_TONEDETECT:
++			if (get_user(x, (int *)arg))
++				return -EFAULT;
++			DBG("xpd=%d: ZT_TONEDETECT chan=%d: TONEDETECT_ON=%d TONEDETECT_MUTE=%d\n",
++				xpd->id, pos, (x & ZT_TONEDETECT_ON), (x & ZT_TONEDETECT_MUTE));
++			return -ENOTTY;
++		default:
++			DBG("ENOTTY: chan=%d cmd=0x%x\n", pos, cmd);
++			DBG("        IOC_TYPE=0x%02X\n", _IOC_TYPE(cmd));
++			DBG("        IOC_DIR=0x%02X\n", _IOC_DIR(cmd));
++			DBG("        IOC_NR=0x%02X\n", _IOC_NR(cmd));
++			DBG("        IOC_SIZE=0x%02X\n", _IOC_SIZE(cmd));
++			return -ENOTTY;
 +	}
 +	return 0;
 +}
@@ -4097,9 +6105,14 @@
 +			DBG("Got ZT_START for FXS channel %d, treated as ZT_RING\n", pos);
 +			//hookstate = ZT_TXSIG_RING;
 +
++			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_PROTO(RING, xbus, xpd->id, (1 << pos));
++			ret = CALL_XMETHOD(RING, xbus, xpd, pos, 1);			// RING on
 +			if(ret) {
 +				DBG("ZT_RING Failed: ret=0x%02X\n", ret);
 +				return ret;
@@ -4120,12 +6133,17 @@
 +			DBG("ZT_TXSIG_OFFHOOK: %s hookstate=0x%04X\n", chan->name, xpd->hookstate);
 +			BIT_SET(xpd->hookstate, pos);
 +			xpd->ringing[pos] = 0;
-+			ret = CALL_PROTO(SETHOOK, xbus, xpd->id, xpd->hookstate);
++			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_PROTO(LED, xpd->xbus, xpd, (1 << i), 0, 0);
++			CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(i), LED_GREEN, 0);
 +			break;
 +			//DBG("ZT_TXSIG_OFFHOOK %d\n", pos);
 +			//BIT_SET(xpd->hookstate, pos);
@@ -4137,13 +6155,18 @@
 +		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_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_PROTO(SETHOOK, xbus, xpd->id, xpd->hookstate);
++			ret = CALL_XMETHOD(SETHOOK, xbus, xpd->id, xpd->hookstate);
 +			if(ret) {
 +				DBG("ZT_ONHOOK Failed: ret=0x%02X\n", ret);
 +				break;
 +			}
-+			CALL_PROTO(LED, xpd->xbus, xpd, (1 << i), 0, 0);
++			CALL_XMETHOD(LED, xpd->xbus, xpd, BIT(i), LED_GREEN, 0);
 +			break;
 +			//DBG("ZT_TXSIG_ONHOOK: %d\n", pos);
 +			//BIT_CLR(xpd->hookstate, pos);
@@ -4152,7 +6175,7 @@
 +			DBG("hooksig: unkown txsig=%d on channel %d\n", txsig, pos);
 +			return -EINVAL;
 +	}
-+	//ret = CALL_PROTO(SETHOOK, xbus, xpd->id, xpd->hookstate);
++	//ret = CALL_XMETHOD(SETHOOK, xbus, xpd->id, xpd->hookstate);
 +	//if(ret) {
 +	//	DBG("ZT_TXSIG_START Failed: ret=0x%02X\n", ret);
 +	//}
@@ -4160,7 +6183,7 @@
 +}
 +#endif
 +
-+int xpp_sethook(struct zt_chan *chan, int hookstate)
++static int xpp_sethook(struct zt_chan *chan, int hookstate)
 +{
 +	int pos = chan->chanpos - 1;
 +	xpd_t	*xpd = chan->pvt;
@@ -4172,19 +6195,26 @@
 +		return -EINVAL;
 +	}
 +	xbus = xpd->xbus;
-+	// DBG("%s (%d) (old=0x%04X, hook-command=%d)\n", chan->name, pos, xpd->hookstate, hookstate);
++	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_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_PROTO(RING, xbus, xpd, pos, 0);			// RING off
++				ret = CALL_XMETHOD(RING, xbus, xpd, pos, 0);			// RING off
 +#endif
-+				ret = CALL_PROTO(CHAN_POWER, xbus, xpd, (1 << pos), 0);		// Power down (prevent overheating!!!)
++				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;
@@ -4193,7 +6223,7 @@
 +				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);
++				ret = CALL_XMETHOD(SETHOOK, xbus, xpd, xpd->hookstate);
 +				if(ret) {
 +					DBG("ZT_ONHOOK Failed: ret=0x%02X\n", ret);
 +					break;
@@ -4211,7 +6241,7 @@
 +			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);
++			ret = CALL_XMETHOD(SETHOOK, xbus, xpd, xpd->hookstate);
 +			if(ret) {
 +				DBG("ZT_OFFHOOK Failed: ret=0x%02X\n", ret);
 +				break;
@@ -4226,9 +6256,14 @@
 +		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_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_PROTO(CHAN_POWER, xbus, xpd, (1 << pos), 1);	// Power up (for ring)
-+				ret = CALL_PROTO(RING, xbus, xpd, pos, 1);			// RING on
++				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);
 +				}
@@ -4248,18 +6283,20 @@
 +int xpp_setchunksize(struct zt_span *span, int chunksize);
 +
 +/* Enable maintenance modes */
-+static int xpp_maint(struct zt_span *span, int cmd)
++int xpp_maint(struct zt_span *span, int cmd)
 +{
 +	xpd_t		*xpd = span->pvt;
 +	int		ret = 0;
++#if 0
 +	char		loopback_data[] = "THE-QUICK-BROWN-FOX-JUMPED-OVER-THE-LAZY-DOG";
++#endif
 +
 +	BUG_ON(!xpd);
 +	DBG("%s: span->mainttimer=%d\n", __FUNCTION__, span->mainttimer);
 +	switch(cmd) {
 +		case ZT_MAINT_NONE:
 +			printk("XXX Turn off local and remote loops XXX\n");
-+			CALL_PROTO(LED, xpd->xbus, xpd, xpd->enabled_chans, 1, 0);		// FIXME: Find usage for extra LED
++			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");
@@ -4269,12 +6306,12 @@
 +			break;
 +		case ZT_MAINT_LOOPUP:
 +			printk("XXX Send loopup code XXX\n");
-+			CALL_PROTO(LED, xpd->xbus, xpd, xpd->enabled_chans, 1, 1);		// FIXME: Find usage for extra LED
-+			CALL_PROTO(LOOPBACK_AX, xpd->xbus, xpd, loopback_data, ARRAY_SIZE(loopback_data));
++			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_PROTO(LED, xpd->xbus, xpd, xpd->enabled_chans, 1, 0);		// FIXME: Find usage for extra LED
++			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");
@@ -4289,7 +6326,6 @@
 +	return ret;
 +}
 +
-+
 +/* Set signalling type (if appropriate) */
 +static int xpp_chanconfig(struct zt_chan *chan, int sigtype)
 +{
@@ -4338,26 +6374,39 @@
 +}
 +#endif
 +
-+#if 0
-+static void dump_xpp(xpd_t *xpd, const char *msg)
++/**
++ * Unregister an xpd from zaptel and release related resources
++ * @xpd The xpd to be unregistered
++ * @returns 0 on success, errno otherwise
++ * 
++ * Checks that nobody holds an open channel.
++ *
++ * Called by:
++ * 	- User action through /proc
++ * 	- During xpd_remove()
++ */
++static int xpd_zaptel_unregister(xpd_t *xpd)
 +{
-+	int i;
-+	struct zt_chan *cur_chan;
-+	struct zt_span *span = &xpd->span;
++	BUG_ON(!xpd);
 +
-+	DBG("xpd=%p: %s\n", xpd, msg);
-+	DBG("    Span name is: %s\n", span->name);
-+	DBG("    pvt: %p\n", span->pvt);
-+	for(i = 0; i < span->channels; i++) {
-+		cur_chan = &(span->chans[i]);
-+		DBG("    Channel %d (%p) name ='%s'\n",
-+				cur_chan->channo, cur_chan, cur_chan->name);
++	if (!SPAN_REGISTERED(xpd)) {
++		NOTICE("%s: %s is already unregistered\n", __FUNCTION__, xpd->xpdname);
++		return -EIDRM;
 +	}
-+	DBG("    Parallel device: %p\n", xpd->pdev);
++	if(atomic_read(&xpd->open_counter)) {
++		NOTICE("%s: %s is busy (open_counter=%d). Skipping.\n", __FUNCTION__, xpd->xpdname, atomic_read(&xpd->open_counter));
++		return -EBUSY;
++	}
++	if(sync_master == xpd)
++		set_sync_master(NULL);			// FIXME: it's better to elect a new prince
++	update_xpd_status(xpd, ZT_ALARM_NOTOPEN);
++	xpd->state = XPD_STATE_OFF;
++	mdelay(2);	// FIXME: This is to give chance for transmit/receiveprep to finish.
++	zt_unregister(&xpd->span);
++	return 0;
 +}
-+#endif
 +
-+static int span_init(xpd_t *xpd)
++static int xpd_zaptel_register(xpd_t *xpd)
 +{
 +	struct zt_chan	*cur_chan;
 +	struct zt_span	*span;
@@ -4365,19 +6414,21 @@
 +	int		sigfxs;
 +	int		i;
 +	int		cn;
++	xops_t		*xops;
 +
-+	if(!xpd)
-+		return -EINVAL;
++	BUG_ON(!xpd);
++	xops = xpd->xops;
++
++	if (SPAN_REGISTERED(xpd)) {
++		ERR("xpd %s already registered\n", xpd->xpdname);
++		return -EEXIST;
++	}
 +	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;
@@ -4385,11 +6436,13 @@
 +			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",
@@ -4452,6 +6505,22 @@
 +#endif
 +
 +	DBG("Finished span_load: ZT_FLAG_RUNNING=%d\n", span->flags & ZT_FLAG_RUNNING);
++
++	DBG("Registering span of %s.\n", xpd->xpdname);
++	if(zt_register(&xpd->span, 1)) {
++		xbus_t	*xbus = xpd->xbus;
++		ERR("Failed to zt_register of span of xpd %s.\n", xpd->xpdname);
++		xbus->xpds[xpd->id] = NULL;
++		list_del(&xpd->xpd_list);
++		xbus->num_xpds--;
++		xpd->state = XPD_STATE_ZAPTEL_ERR;
++		return -ENODEV;
++	}
++	xpd->state = XPD_STATE_ACTIVE;
++	CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, xpd, 0, 0);	// Query SYNC_SOURCE
++//	if(xpd->id == 0)
++//		set_sync_master(xpd);
++
 +	return 0;
 +}
 +
@@ -4473,7 +6542,7 @@
 +	len += sprintf(page + len, "\txpacket_count=%d\n", atomic_read(&xpacket_count));
 +	len += sprintf(page + len, "\tbus_count=%d\n", bus_count);
 +	for(i = 0; i < MAX_BUSES; i++) {
-+		xbus_t *xbus = xbuses_array[i];
++		xbus_t *xbus = xbus_of(i);
 +
 +		if(xbus) {
 +			len += sprintf(page + len, "%s: HW=%s bus_type=%d connected=%s\n",
@@ -4518,10 +6587,8 @@
 +	unsigned int busnum = MINOR_XBUS_NUM(minor);
 +	unsigned long flags;
 +
-+	if(busnum >= MAX_BUSES)
-+		return -EINVAL;
 +	spin_lock_irqsave(&xbuses_lock, flags);
-+	xbus = xbuses_array[busnum];
++	xbus = xbus_of(busnum);
 +	spin_unlock_irqrestore(&xbuses_lock, flags);
 +	if(xbus == NULL)
 +		return -ENODEV;
@@ -4552,6 +6619,7 @@
 +	return 0;
 +}
 +
++#if 0
 +static ssize_t xpp_sys_write (struct file * file, const char __user * buf,
 +			 size_t count, loff_t * ppos)
 +{
@@ -4568,9 +6636,9 @@
 +	}
 +	if(count == 0)
 +		return 0;
-+	if(count >= sizeof(xpp_packet_r_t)) {
++	if(count >= sizeof(xpacket_raw_t)) {
 +		DBG("count=%d, partial write...\n", count);
-+		count = sizeof(xpp_packet_r_t);
++		count = sizeof(xpacket_raw_t);
 +	}
 +	pack_tx = xbus->ops->packet_new(xbus, GFP_KERNEL);
 +	if (!pack_tx) {
@@ -4588,11 +6656,14 @@
 +	packet_send(xbus, pack_tx);
 +	return count;
 +}
++#endif
 +
 +static struct file_operations xpp_fops = {
 +	.owner		= THIS_MODULE,
 +	.llseek		= no_llseek,
++#if 0
 +	.write		= xpp_sys_write,
++#endif
 +	.ioctl		= xpp_sys_ioctl,
 +	.open		= xpp_sys_open,
 +	.release	= xpp_sys_release,
@@ -4607,6 +6678,7 @@
 +		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);
@@ -4639,6 +6711,14 @@
 +	}
 +	struct proc_dir_entry *ent;
 +
++	ent = create_proc_entry(PROC_SYNC, 0644, xpp_procdir);
++	if(!ent) {
++		do_cleanup();
++		return -EFAULT;
++	}
++	ent->read_proc = proc_sync_read;
++	ent->write_proc = proc_sync_write;
++	ent->data = NULL;
 +	ent = create_proc_read_entry(PROC_XBUSES, 0644, xpp_procdir, xpp_zap_read_proc, 0);
 +	if (!ent) {
 +		do_cleanup();
@@ -4660,22 +6740,26 @@
 +
 +	/* Only timer init. We add it only *after* zt_register */
 +	init_timer(&xpp_timer);
-+	xpp_timer.function = xpp_timer_tick;
-+	xpp_timer.data = 0;
-+	xpp_timer.expires = jiffies + 1;	/* Must be 1KHz rate */
-+	add_timer(&xpp_timer);
++	set_sync_master(NULL);			/* Internal ticking */
 +	return 0;
 +}
 +
 +void __exit xpp_zap_cleanup(void)
 +{
-+	unsigned long flags;
++//	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)
++			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);
++//	spin_unlock_irqrestore(&xbuses_lock, flags);
 +	do_cleanup();
 +}
 +
@@ -4685,6 +6769,7 @@
 +EXPORT_SYMBOL(xbus_deactivate);
 +EXPORT_SYMBOL(xpd_of);
 +EXPORT_SYMBOL(xbus_new);
++EXPORT_SYMBOL(xbus_remove);
 +EXPORT_SYMBOL(xbus_reset_counters);
 +EXPORT_SYMBOL(packet_send);
 +EXPORT_SYMBOL(fill_beep);
@@ -4694,18 +6779,23 @@
 +EXPORT_SYMBOL(drain_xbus_packet_queue);
 +EXPORT_SYMBOL(update_xpd_status);
 +EXPORT_SYMBOL(xpp_check_hookstate);
++EXPORT_SYMBOL(xpp_tick);
++EXPORT_SYMBOL(xpp_open);
++EXPORT_SYMBOL(xpp_close);
++EXPORT_SYMBOL(xpp_ioctl);
++EXPORT_SYMBOL(xpp_maint);
 +
 +MODULE_DESCRIPTION("XPP Zaptel Driver");
 +MODULE_AUTHOR("Oron Peled <oron at actcom.co.il>");
 +MODULE_LICENSE("GPL");
-+MODULE_VERSION("$Id: xpp_zap.c 124 2005-11-09 18:42:02Z oron $");
++MODULE_VERSION("$Id: xpp_zap.c 167 2006-01-03 12:39:36Z oron $");
 +
 +module_init(xpp_zap_init);
 +module_exit(xpp_zap_cleanup);
-diff -urNad trunk/xpp/xpp_zap.h /tmp/dpep.oAj2YR/trunk/xpp/xpp_zap.h
---- trunk/xpp/xpp_zap.h	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/xpp_zap.h	2005-11-03 15:01:15.000000000 +0200
-@@ -0,0 +1,36 @@
+diff -urNad zaptel-1.0.10/xpp/xpp_zap.h /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_zap.h
+--- zaptel-1.0.10/xpp/xpp_zap.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xpp_zap.h	2006-01-03 13:22:53.823218000 +0200
+@@ -0,0 +1,44 @@
 +#ifndef	XPP_ZAP_H
 +#define	XPP_ZAP_H
 +
@@ -4729,22 +6819,529 @@
 +void update_xpd_status(xpd_t *xpd, int alarm_flag);
 +void xpp_check_hookstate(xpd_t *xpd, xpp_line_t fxs_off_hook);
 +xpd_t *xpd_of(xbus_t *xbus, int xpd_num);
-+xpd_t *xpd_new(xbus_t *xbus, int xpd_num, xpd_type_t type);
++xpd_t *xpd_new(xbus_t *xbus, int xpd_num, xpd_type_t type, byte revision);
++int xpd_setup(xpd_t *xpd);
++void xpd_remove(xpd_t *xpd);
 +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);
 +
 +#include <linux/proc_fs.h>
 +
 +#ifdef CONFIG_PROC_FS
-+extern struct proc_dir_entry *xpp_procdir;
++extern struct proc_dir_entry	*xpp_procdir;
 +#endif
++extern xpd_t			*sync_master;
 +
 +// Number of rings our simulated phone will ring:
 +#define RINGS_NUM 3
 +
 +#endif	/* XPP_ZAP_H */
-diff -urNad trunk/xpp/zap_debug.c /tmp/dpep.oAj2YR/trunk/xpp/zap_debug.c
---- trunk/xpp/zap_debug.c	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/zap_debug.c	2005-11-10 04:05:18.000000000 +0200
+diff -urNad zaptel-1.0.10/xpp/xproto.c /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xproto.c
+--- zaptel-1.0.10/xpp/xproto.c	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xproto.c	2006-01-03 13:22:53.823218000 +0200
+@@ -0,0 +1,244 @@
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2005, 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 "xproto.h"
++#include "xpp_zap.h"
++#include <linux/module.h>
++
++static const char rcsid[] = "$Id: xproto.c 164 2006-01-03 11:14:34Z oron $";
++
++extern	int print_dbg;
++static int packet_process(xbus_t *xbus, int xpd_num, xpacket_t *pack);
++
++bool valid_xpd_addr(const xpd_addr_t *addr)
++{
++	return ((addr->bank_num & ~0x1) == 0) && ((addr->card_id & ~0x3) == 0);
++}
++
++int xpd_addr2num(const xpd_addr_t *addr)
++{
++	BUG_ON(!valid_xpd_addr(addr));
++	return addr->bank_num * 4 + addr->card_id;
++}
++
++void xpd_set_addr(xpd_addr_t *addr, int xpd_num)
++{
++	if(xpd_num < 4) {
++		addr->card_id = xpd_num;
++		addr->bank_num = 0;
++	} else {
++		addr->card_id = xpd_num % 4;
++		addr->bank_num = xpd_num / 4;
++	}
++}
++
++
++/*---------------- General Protocol Management ----------------------------*/
++
++#define	XTABLE_ENTRY(name) [ XPD_TYPE(name) ] &name ## _protocol_table
++
++xproto_table_t	*xprotocol_tables[] = {
++	XTABLE_ENTRY(FXS),
++};
++
++const xproto_entry_t *xproto_global_entry(byte opcode)
++{
++	const xproto_entry_t *xe;
++
++	xe = xproto_card_entry(&PROTO_TABLE(GLOBAL), opcode);
++	//DBG("opcode=0x%X xe=%p\n", opcode, xe);
++	return xe;
++}
++
++const xproto_handler_t xproto_global_handler(byte opcode)
++{
++	return xproto_card_handler(&PROTO_TABLE(GLOBAL), opcode);
++}
++
++const xproto_table_t *get_xproto_table(xpd_type_t cardtype)
++{
++	return xprotocol_tables[cardtype];
++}
++
++const xproto_entry_t *xproto_card_entry(const xproto_table_t *table, byte opcode)
++{
++	const xproto_entry_t *xe;
++
++	//DBG("\n");
++	xe = &table->entries[opcode];
++	return (xe->handler != NULL) ? xe : NULL;
++}
++
++const xproto_handler_t xproto_card_handler(const xproto_table_t *table, byte opcode)
++{
++	const xproto_entry_t *xe;
++
++	//DBG("\n");
++	xe = xproto_card_entry(table, opcode);
++	return xe->handler;
++}
++
++const xproto_entry_t *find_xproto_entry(xpd_t *xpd, byte opcode)
++{
++	const xproto_entry_t *xe;
++	
++	xe = xproto_global_entry(opcode);
++	// DBG("opcode=0x%X xe=%p\n", opcode, xe);
++	if(!xe) {
++		const xproto_table_t *xtable;
++		
++		if(!xpd)
++			return NULL;
++		xtable = get_xproto_table(xpd->type);
++		if(!xtable)
++			return NULL;
++		xe = xproto_card_entry(xtable, opcode);
++		if(!xe)
++			return NULL;
++	}
++	return xe;
++}
++
++#define	DEF_(t)	\
++	[ XPD_TYPE_ ## t ]  = & t ## _xops
++
++xops_t	*xpd_card_ops[XPD_TYPE_NOMODULE] = {
++	DEF_(FXS),
++};
++
++xops_t	*get_xops(xpd_type_t xpd_type)
++{
++	if(xpd_type >= XPD_TYPE_NOMODULE)
++		return NULL;
++	return xpd_card_ops[xpd_type];
++}
++
++int packet_receive(xbus_t *xbus, xpacket_t *pack)
++{
++	int	xpd_num;
++
++	if(!valid_xpd_addr(&pack->content.addr)) {
++		static int rate_limit = 0;
++
++		if((rate_limit++ % 5003) < 3)
++			dump_packet("bad address", pack, print_dbg);
++		xbus->ops->packet_free(xbus, pack);
++		return -EPROTO;
++	}
++	xpd_num = XPD_NUM(pack->content.addr);
++#if SOFT_SIMULATOR
++	if(xbus->sim[xpd_num].simulated) {
++		//dump_packet("packet_receive -> simulate", pack, print_dbg);
++		return simulate_xpd(xbus, xpd_num, pack);
++	} else
++#endif
++	{
++		//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)
++{
++	byte			op;
++	const xproto_entry_t	*xe;
++	xproto_handler_t	handler;
++	xproto_table_t		*table;
++	xpd_t			*xpd;
++	int			ret = 0;
++
++	BUG_ON(!pack);
++	op = pack->content.opcode;
++	xpd_num = XPD_NUM(pack->content.addr);
++	xpd = xpd_of(xbus, xpd_num);
++	xe = find_xproto_entry(xpd, op);
++	/*-------- Validations -----------*/
++	if(!xe) {
++		ERR("xpp: %s -- bad command op=0x%02X\n", __FUNCTION__, op);
++		dump_packet("packet_process -- bad command", pack, print_dbg);
++		ret = -EPROTO;
++		goto out;
++	}
++	table = xe->table;
++	BUG_ON(!table);
++	if(!table->packet_is_valid(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 = xe->handler;
++	BUG_ON(!handler);
++	XBUS_COUNTER(xbus, RX_BYTES) += pack->datalen;
++	handler(xbus, xpd, xe, pack);
++out:
++	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)
++{
++	byte	op = packet->content.opcode;
++
++	if(!print_dbg)
++		return;
++	DBG("%s: @0x%1X%1X OP=0x%02X LEN=%d\n",
++			msg,
++			packet->content.addr.bank_num,
++			packet->content.addr.card_id,
++			op,
++			(byte)packet->datalen);
++#if VERBOSE_DEBUG
++	{
++		int i;
++		byte	*p = packet->content.data;
++
++		for(i = 0; i < packet->datalen; i++) {
++			static int limiter = 0;
++
++			if(i >= sizeof(xpacket_raw_t)) {
++				if(limiter < ERR_REPORT_LIMIT) {
++					ERR("%s: length overflow i=%d > sizeof(xpacket_raw_t)=%d\n",
++							__FUNCTION__, i+1, sizeof(xpacket_raw_t));
++				} else if(limiter == ERR_REPORT_LIMIT) {
++					ERR("%s: error packet #%d... squelsh reports.\n",
++							__FUNCTION__, limiter);
++				}
++				limiter++;
++				break;
++			}
++			DBG("        %2d> %02X\n", i+1, p[i]);
++		}
++	}
++#endif
++}
++
++EXPORT_SYMBOL(dump_packet);
++EXPORT_SYMBOL(packet_receive);
++EXPORT_SYMBOL(valid_xpd_addr);
++EXPORT_SYMBOL(xpd_addr2num);
++EXPORT_SYMBOL(xpd_set_addr);
+diff -urNad zaptel-1.0.10/xpp/xproto.h /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xproto.h
+--- zaptel-1.0.10/xpp/xproto.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/xproto.h	2006-01-03 13:22:53.823218000 +0200
+@@ -0,0 +1,247 @@
++#ifndef	XPROTO_H
++#define	XPROTO_H
++/*
++ * Written by Oron Peled <oron at actcom.co.il>
++ * Copyright (C) 2004-2005, 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"
++
++#ifdef	__KERNEL__
++#include <linux/list.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	LINE_BITS	(sizeof(xpp_line_t)*8)
++#define	PCM_CHUNKSIZE	(CHANNELS_PERXPD * 8)	/* samples of 8 bytes */
++
++typedef struct xpd_addr {
++	byte	card_id:4;
++	byte	bank_num:4;
++} __attribute__((packed)) xpd_addr_t;
++
++bool valid_xpd_addr(const xpd_addr_t *addr);
++int xpd_addr2num(const xpd_addr_t *addr);
++void xpd_set_addr(xpd_addr_t *addr, int xpd_num);
++
++#define	XPD_NUM(x)		xpd_addr2num(&x)
++#define	XPD_ADDR_SET(x,val)	xpd_set_addr(&x, val)
++#define	MAX_XPACKET_DATALEN	100
++
++#define	XPROTO_NAME(card,op)	card ## _ ## op
++#define	XPROTO_HANDLER(card,op)	XPROTO_NAME(card,op ## _handler)
++#define	XPROTO_CALLER(card,op)	XPROTO_NAME(card,op ## _send)
++
++#define	HANDLER_DEF(card,op)	\
++	int XPROTO_HANDLER(card,op) (		\
++		xbus_t *xbus,			\
++		xpd_t *xpd,			\
++		const xproto_entry_t *cmd,	\
++		xpacket_t *pack)
++
++#define	CALL_PROTO(card,op, ...)	XPROTO_CALLER(card,op)( __VA_ARGS__ )
++
++#define	DECLARE_CMD(card,op, ...)	\
++	int CALL_PROTO(card, op, xbus_t *xbus, xpd_t *xpd, ## __VA_ARGS__ )
++
++#define	HOSTCMD(card, op, ...)					\
++			DECLARE_CMD(card, op, ## __VA_ARGS__ );	\
++			EXPORT_SYMBOL(XPROTO_CALLER(card, op));	\
++			DECLARE_CMD(card, op, ## __VA_ARGS__ )
++
++#define	RPACKET_NAME(card,op)	XPROTO_NAME(RPACKET_ ## card, op)
++#define	RPACKET_TYPE(card,op)	struct RPACKET_NAME(card, op)
++
++#define	DEF_RPACKET_DATA(card,op, ...)	\
++	struct RPACKET_NAME(card,op) {	\
++		byte		opcode;	\
++		xpd_addr_t	addr;	\
++		__VA_ARGS__		\
++	} __attribute__((packed))
++
++#define	RPACKET_CAST(p,card,op)		((RPACKET_TYPE(card,op) *)p)
++#define	RPACKET_FIELD(p,card,op,field)	(RPACKET_CAST(p,card,op)->field)
++#define	RPACKET_SIZE(card,op)		sizeof(RPACKET_TYPE(card,op))
++
++#define	PACKET_LEN(p) \
++	((p)->datalen + sizeof(xpd_addr_t) + 1)
++
++#define	XENTRY(card,op)					\
++	[ XPROTO_NAME(card,op) ] {			\
++		.handler = XPROTO_HANDLER(card,op),	\
++		.datalen = RPACKET_SIZE(card,op),	\
++		.name = #op,				\
++		.table = &PROTO_TABLE(card)		\
++	}
++
++
++#define	XPACKET_INIT(p, card, op)					\
++		do {							\
++			p->content.opcode = XPROTO_NAME(card,op);	\
++			p->datalen = RPACKET_SIZE(card,op);		\
++		} while(0)
++
++#define	XPACKET_NEW(p, xbus, card, op, to)			\
++	do {							\
++		p = xbus->ops->packet_new(xbus, GFP_ATOMIC);	\
++		if(!p)						\
++			return -ENOMEM;				\
++		XPACKET_INIT(p, card, op);			\
++		XPD_ADDR_SET(p->content.addr, to);		\
++	} while(0);
++
++typedef struct xproto_entry	xproto_entry_t;
++typedef struct xproto_table	xproto_table_t;
++
++typedef int (*xproto_handler_t)(
++		xbus_t *xbus,
++		xpd_t *xpd,
++		const xproto_entry_t *cmd,
++		xpacket_t *pack);
++
++struct xproto_entry {
++	xproto_handler_t	handler;
++	int			datalen;
++	const char		*name;
++	xproto_table_t		*table;
++};
++
++struct xproto_table {
++	xproto_entry_t	entries[255];	/* Indexed by opcode */
++	const char	*name;
++	bool (*packet_is_valid)(xpacket_t *pack);
++	void (*packet_dump)(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_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);
++
++const xproto_entry_t *xproto_global_entry(byte opcode);
++const xproto_handler_t xproto_global_handler(byte opcode);
++
++#define	XMETHOD(name, ...)		\
++	int	(*name)(xbus_t *xbus, xpd_t *xpd, ## __VA_ARGS__ );
++
++#if 0
++#define	CALL_XMETHOD(name, xbus, xpd, ...)				\
++		(							\
++		 	(xpd) && (xpd)->xops && (xpd)->xops->name &&	\
++			(xpd)->xops->name(xbus, xpd, ## __VA_ARGS__ )	\
++		)
++#else
++#define	CALL_XMETHOD(name, xbus, xpd, ...)				\
++			(xpd)->xops->name(xbus, xpd, ## __VA_ARGS__ )
++#endif
++
++struct xops {
++	XMETHOD(card_new, byte rev);
++	XMETHOD(card_init);
++	XMETHOD(card_remove);
++
++	XMETHOD(DESC_REQ, int xpd_num);
++	XMETHOD(SYNC_SOURCE, bool setit, bool is_master);
++	XMETHOD(PCM_WRITE, xpp_line_t hookstate,  volatile byte *buf);
++
++	XMETHOD(CHAN_ENABLE, xpp_line_t lines, bool on);
++	XMETHOD(CHAN_POWER, xpp_line_t lines, bool on);
++	XMETHOD(CHAN_CID, xpp_line_t lines);
++	XMETHOD(RING, int pos, bool on);
++	XMETHOD(SETHOOK, xpp_line_t hook_status);
++	XMETHOD(LED, xpp_line_t lines, byte which, bool on);
++	XMETHOD(RELAY_OUT, byte which, bool on);
++	XMETHOD(SLIC_INIT);
++	XMETHOD(SLIC_QUERY, int pos, byte reg_num);
++};
++
++xops_t	*get_xops(xpd_type_t xpd_type);
++
++#undef	XMETHOD
++
++#include "card_global.h"
++#include "card_fxs.h"
++
++enum opcodes {
++	XPROTO_NAME(GLOBAL, DESC_REQ)		= 0x04,
++	XPROTO_NAME(GLOBAL, DEV_DESC)		= 0x05,
++/**/
++	XPROTO_NAME(GLOBAL, PCM_WRITE)		= 0x11,
++	XPROTO_NAME(GLOBAL, PCM_READ)		= 0x12,
++/**/
++	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,
++};
++
++
++#define	MEMBER(card,op)	RPACKET_TYPE(card,op)	RPACKET_NAME(card,op)
++
++struct xpacket_raw {
++	byte		opcode;
++	xpd_addr_t	addr;
++	union {
++		MEMBER(GLOBAL, DESC_REQ);
++		MEMBER(GLOBAL, DEV_DESC);
++		MEMBER(GLOBAL, PCM_WRITE);
++		MEMBER(GLOBAL, PCM_READ);
++		MEMBER(GLOBAL, SYNC_REPLY);
++
++		MEMBER(FXS, SIG_CHANGED);
++		MEMBER(FXS, SLIC_REPLY);
++
++		byte	data[0];
++	};
++} __attribute__((packed));
++
++struct xpacket {
++	xpacket_raw_t		content;
++	size_t			datalen;
++	struct list_head	list;
++};
++
++
++void dump_packet(const char *msg, xpacket_t *packet, bool print_dbg);
++int packet_receive(xbus_t *xbus, xpacket_t *pack);
++
++#endif	/* XPROTO_H */
+diff -urNad zaptel-1.0.10/xpp/zap_debug.c /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/zap_debug.c
+--- zaptel-1.0.10/xpp/zap_debug.c	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/zap_debug.c	2006-01-03 13:22:53.823218000 +0200
 @@ -0,0 +1,136 @@
 +/*
 + * Written by Oron Peled <oron at actcom.co.il>
@@ -4779,7 +7376,7 @@
 +#include <zaptel.h>
 +#include "zap_debug.h"
 +
-+static char rcsid[] = "$Id: zap_debug.c 124 2005-11-09 18:42:02Z oron $";
++static const char rcsid[] = "$Id: zap_debug.c 161 2006-01-03 09:13:40Z oron $";
 +
 +#define	P_(x)	[ x ] = { .value = x, .name = #x, }
 +static	struct {
@@ -4882,9 +7479,9 @@
 +EXPORT_SYMBOL(dump_poll);
 +EXPORT_SYMBOL(event2str);
 +EXPORT_SYMBOL(dump_sigtype);
-diff -urNad trunk/xpp/zap_debug.h /tmp/dpep.oAj2YR/trunk/xpp/zap_debug.h
---- trunk/xpp/zap_debug.h	1970-01-01 02:00:00.000000000 +0200
-+++ /tmp/dpep.oAj2YR/trunk/xpp/zap_debug.h	2005-11-10 04:05:18.000000000 +0200
+diff -urNad zaptel-1.0.10/xpp/zap_debug.h /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/zap_debug.h
+--- zaptel-1.0.10/xpp/zap_debug.h	1970-01-01 02:00:00.000000000 +0200
++++ /tmp/dpep.lpTITx/zaptel-1.0.10/xpp/zap_debug.h	2006-01-03 13:22:53.823218000 +0200
 @@ -0,0 +1,16 @@
 +#ifndef	ZAP_DEBUG_H
 +#define	ZAP_DEBUG_H




More information about the Pkg-voip-commits mailing list