[zaptel-commits] branch 1.2 r1212 - in /branches/1.2/xpp: ./ utils/

zaptel-commits at lists.digium.com zaptel-commits at lists.digium.com
Fri Jul 7 17:43:32 MST 2006


Author: tzafrir
Date: Fri Jul  7 19:43:31 2006
New Revision: 1212

URL: http://svn.digium.com/view/zaptel?rev=1212&view=rev
Log:
xpp Release 1.1.0 :
* FPGA firmware now loaded from PC (for newer models)
* Driver for the FXO module (xpd_fxo.ko)
* Moved most userspace files to the subdirectory utils (see also next commit)
* Explicit license for firmware files
* Optionally avoid auto-registration
* Registers initializations code is done by a userspace script.
* Remove obsolete .inc initialization files (we use user-space init)
* Added an install target to the utils dir.
* Updated README.Astribank accordingly.
* Using RBS signalling, as caller ID did not work well otherwise.
* Better handling of USB protocol errors.
* Fixed some procfs-related races.
* per-card-module ioctls.
* fxotune support.
* opermode support (set through /etc/default/zaptel for now)
* Userspace initialization script can also read registers.
* Power calibration works (and implemented in perl)
* some fine-tuning to the regster initialization parameters.
* Leds turn on before registration and turn off after it.

Added:
    branches/1.2/xpp/LICENSE.firmware   (with props)
    branches/1.2/xpp/card_fxo.c   (with props)
    branches/1.2/xpp/card_fxo.h   (with props)
    branches/1.2/xpp/init_data_3_19.cmd   (with props)
    branches/1.2/xpp/init_data_3_20.cmd   (with props)
    branches/1.2/xpp/init_data_4_19.cmd   (with props)
    branches/1.2/xpp/init_data_4_20.cmd   (with props)
    branches/1.2/xpp/initialize_registers   (with props)
    branches/1.2/xpp/utils/
    branches/1.2/xpp/utils/FPGA_FXS.hex   (with props)
    branches/1.2/xpp/utils/Makefile   (with props)
    branches/1.2/xpp/utils/USB_1130.hex   (with props)
    branches/1.2/xpp/utils/USB_8613.hex   (with props)
    branches/1.2/xpp/utils/fpga_load.8   (with props)
    branches/1.2/xpp/utils/fpga_load.c   (with props)
    branches/1.2/xpp/utils/genzaptelconf   (with props)
    branches/1.2/xpp/utils/genzaptelconf.8   (with props)
    branches/1.2/xpp/utils/hexfile.c   (with props)
    branches/1.2/xpp/utils/hexfile.h   (with props)
    branches/1.2/xpp/utils/test_parse.c   (with props)
    branches/1.2/xpp/utils/xpp_fxloader   (with props)
    branches/1.2/xpp/utils/xpp_fxloader.usermap   (with props)
    branches/1.2/xpp/utils/xpp_modprobe   (with props)
    branches/1.2/xpp/xbus-core.c   (with props)
    branches/1.2/xpp/xbus-core.h   (with props)
Removed:
    branches/1.2/xpp/FPGA_XPD.hex
    branches/1.2/xpp/cards.c
    branches/1.2/xpp/cards.h
    branches/1.2/xpp/gen_slic_init
    branches/1.2/xpp/slic_init.inc
    branches/1.2/xpp/sync.sh
    branches/1.2/xpp/xpp_fxloader
    branches/1.2/xpp/xpp_fxloader.usermap
    branches/1.2/xpp/xpp_modprobe
    branches/1.2/xpp/xpp_proto.c
    branches/1.2/xpp/xpp_proto.h
Modified:
    branches/1.2/xpp/Makefile
    branches/1.2/xpp/README.Astribank
    branches/1.2/xpp/card_fxs.c
    branches/1.2/xpp/card_fxs.h
    branches/1.2/xpp/card_global.c
    branches/1.2/xpp/card_global.h
    branches/1.2/xpp/slic.c
    branches/1.2/xpp/slic.h
    branches/1.2/xpp/xdefs.h
    branches/1.2/xpp/xpd.h
    branches/1.2/xpp/xpp_usb.c
    branches/1.2/xpp/xpp_zap.c
    branches/1.2/xpp/xpp_zap.h
    branches/1.2/xpp/xproto.c
    branches/1.2/xpp/xproto.h
    branches/1.2/xpp/zap_debug.c
    branches/1.2/xpp/zap_debug.h

Added: branches/1.2/xpp/LICENSE.firmware
URL: http://svn.digium.com/view/zaptel/branches/1.2/xpp/LICENSE.firmware?rev=1212&view=auto
==============================================================================
--- branches/1.2/xpp/LICENSE.firmware (added)
+++ branches/1.2/xpp/LICENSE.firmware Fri Jul  7 19:43:31 2006
@@ -1,0 +1,37 @@
+The firmware files (*.hex) in this directory are software for the 
+Astribank itself and not intended to run on the Linux system itself. 
+They are generally freely distriributable (see exact terms below).
+
+/****************************************************************************/
+/*  Copyright (c) 2004-2006 Xorcom Inc. All Rights Reserved.                */
+/*  Redistribution and use of the microcode software ( Firmware ) is        */
+/*  permitted provided that the following conditions are met:               */
+/*                                                                          */
+/*          1. Firmware is redistributed verbatim without any modification; */
+/*          2. Any reproduction of Firmware must contain the above          */
+/*             copyright notice, this list of conditions and the below      */
+/*             disclaimer in the documentation and/or other materials       */
+/*             provided with the distribution; and                          */
+/*          3. The name of Xorcom may not be used to endorse or promote     */
+/*             products derived from this Firmware without specific prior   */
+/*             written consent.                                             */
+/*                                                                          */
+/*  Disclaimer: Xorcom provides this firmware "as is" with no warranties    */
+/*  or indemnities whatsoever. Xorcom expressly disclaims any express,      */
+/*  statutory or implied warranties, including, but not limited to, the     */
+/*  implied warranties of merchantability, fitness for a particular         */
+/*  purpose and non-infringement. In no event shall Xorcom be liable for    */
+/*  any direct, indirect, incidental, special, exemplary, or consequential  */
+/*  damages (including, but not limited to, procurement of substitute       */
+/*  goods or services; loss of use, data, or profits; or business           */
+/*  interruption) however caused and on any theory of liability, whether    */
+/*  in contract, strict liability, or tort (including negligence or         */
+/*  otherwise) arising in any way out of the use of this firmware, even     */
+/*  if advised of the possibility of such damage. User acknowledges and     */
+/*  agrees that the purchase or use of the firmware will not create or      */
+/*  give grounds for a license by implication, estoppel, or otherwise in    */
+/*  any intellectual property rights (patent, copyright, trade secret,      */
+/*  mask work, or other proprietary right) embodied in any other Xorcom     */
+/*  hardware or firmware either solely or in combination with the firmware. */
+/****************************************************************************/
+

Propchange: branches/1.2/xpp/LICENSE.firmware
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: branches/1.2/xpp/LICENSE.firmware
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: branches/1.2/xpp/LICENSE.firmware
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: branches/1.2/xpp/Makefile
URL: http://svn.digium.com/view/zaptel/branches/1.2/xpp/Makefile?rev=1212&r1=1211&r2=1212&view=diff
==============================================================================
--- branches/1.2/xpp/Makefile (original)
+++ branches/1.2/xpp/Makefile Fri Jul  7 19:43:31 2006
@@ -1,5 +1,9 @@
-EXTRA_CFLAGS	= -I$(src)/.. -DSOFT_SIMULATOR=0
+EXTRA_CFLAGS	= -I$(SUBDIRS) -DDEBUG -DPOLL_DIGITAL_INPUTS -DWITH_ECHO_SUPPRESSION -DWITH_RBS
 
-obj-m		= xpd_fxs.o xpp.o xpp_usb.o
-xpp-y		+= xproto.o card_global.o xpp_zap.o zap_debug.o
-xpd_fxs-y	+= card_fxs.o slic.o
+obj-m			= xpp.o xpp_usb.o xpd_fxs.o xpd_fxo.o
+xpp-y			+= xbus-core.o xpp_zap.o xproto.o card_global.o
+xpd_fxs-y		+= card_fxs.o slic.o
+xpd_fxo-y		+= card_fxo.o slic.o
+
+ctags:
+	ctags *.[ch]

Modified: branches/1.2/xpp/README.Astribank
URL: http://svn.digium.com/view/zaptel/branches/1.2/xpp/README.Astribank?rev=1212&r1=1211&r2=1212&view=diff
==============================================================================
--- branches/1.2/xpp/README.Astribank (original)
+++ branches/1.2/xpp/README.Astribank Fri Jul  7 19:43:31 2006
@@ -5,15 +5,63 @@
 Building and Installation:
 """""""""""""""""""""""""
 Building and installation is basically like the normal procedure of 
-installing Zaptel. Follow the rest of the documentation here.
-
-In addition, the file xpp/xpp_modprobe contains modprobe settings. It
-should be copied verbatim into /etc/modprobe.conf or (better) copied to
-/etc/modprobe.d/ . If you fail to do so, xpp_usb.ko will fail to load
-xpd_fxs.ko and as a result will not detect your Astribank.
-
-Loading Firmware
+installing Zaptel with some additions.
+
+Building drivers:
 """"""""""""""""
+From the toplevel zaptel directory run a command similar to (I used line
+continuation to prevent line wrapping):
+
+ $ make \
+        KSRC=/usr/src/kernel-headers-2.6.12-1-386       \
+        KVERS=2.6.12-1-386                              \
+        XPPMOD=xpp/                                     \
+        EC_TYPE=CAN_KB1
+
+ - The KSRC= points to a configured kernel source tree.
+ - The KVERS= should match the relevant tree version.
+ - The XPPMOD= instructs the Zaptel Makefile to descend into the xpp/
+   subdirectory. The slash (/) in the end is mandatory.
+ - The EC_TYPE= select the echo canceler.
+
+Building firmware utilities:
+"""""""""""""""""""""""""""
+Then you should compile the firmware loading utilities. Simply go
+to the zaptel/xpp/utils and run make.
+
+Those who don't use prepackaged drivers should make sure they also
+install the (externally available) fxload utility.
+
+Installation:
+""""""""""""
+
+apart from the standard 'make install' in the zaptel directory, 
+run:
+
+  make -C xpp/utils install
+
+Alternatively, do the following manually:
+
+All firmware files should be copied to a new directory:
+  /usr/share/zaptel/
+
+The xpp_fxloader and xpp_fxloader.usbmap should be copied to:
+ /etc/hotplug/usb/
+
+In addition, the file xpp/xpp_modprobe contains optional modprobe settings.
+It may be copied verbatim into /etc/modprobe.conf or (better) copied to
+/etc/modprobe.d/ .
+
+
+Note that loading through udev is not yet provided. Run 
+
+  /etc/hotplug/usb/xpp_fxloader xppdetect
+
+to load firmware.
+
+
+Loading Firmware Details:
+""""""""""""""""""""""""
 The Astribank needs a firmware loaded into it. Without the firmware, 
 the device will appear in lsusb with vendor ID 04b4 and product ID 8613
 The firmware is provided in the Intel hex format. It can be loaded using 
@@ -21,12 +69,11 @@
 'hotplug-utils' . 
 
 To load the firmware automatically using the standard hotplug script, 
-place xpp/xpp_fxloader and xpp/xpp_fxloader.usermap in /etc/hotplug/usb
-and place xpp/FPGA_XPD.hex in /etc/xortel (or edit xpp_fxloader
-accordingly). 
+place xpp/utils/xpp_fxloader and xpp/utils/xpp_fxloader.usermap in 
+/etc/hotplug/usb and place xpp/utils/*.hex in /usr/share/zaptel .
 
 Alternatively, xpp_fxloader when given the parameter 'xppdetect' will load 
-the firmware from /etc/xortel/FPGA_XPD.hex . You can use it to load the 
+the firmwares from /usr/share/zaptel/ . You can use it to load the 
 firmware manually.
 
 You should then get in lsusb the vendor ID e4e4 and device ID 2121
@@ -128,6 +175,25 @@
 (There are a bunch of other status files under /proc/xpp/ )
 
 
+Useful Module Parameters:
+""""""""""""""""""""""""
+zap_autoreg: (xpp)
+Register spans automatically (1) or not (0). Default: 1. 
+Unsetting this could be useful if you have several Astribanks and you 
+want to set their registration order manually using zt_registration in 
+the /proc interface.
+
+initialize_registers (xpd_fxs)
+The script that is run to initilize registers of the device. The default is 
+/usr/share/zaptel/initialize_registers . 
+Setting this value could be useful if that location is inconvient for you.
+
+print_dbg: (all modules)
+It will make the driver print tons of debugging messages. Can be sometime 
+even handy, but overly-verbose in the case of xpp_usb. Can be safely 
+set/unset at run-time using /sys/modules .
+
+
 
 BTW: XPP here does not stand for X Printing Panel, XML Pull Parser, 
 X-Windows Phase Plane or XML Professional Publisher. It is simply the 

Added: branches/1.2/xpp/card_fxo.c
URL: http://svn.digium.com/view/zaptel/branches/1.2/xpp/card_fxo.c?rev=1212&view=auto
==============================================================================
--- branches/1.2/xpp/card_fxo.c (added)
+++ branches/1.2/xpp/card_fxo.c Fri Jul  7 19:43:31 2006
@@ -1,0 +1,946 @@
+/*
+ * Written by Oron Peled <oron at actcom.co.il>
+ * Copyright (C) 2004-2006, Xorcom
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <version.h>		/* For zaptel version */
+#include "xpd.h"
+#include "xproto.h"
+#include "xpp_zap.h"
+#include "card_fxo.h"
+#include "zap_debug.h"
+
+static const char rcsid[] = "$Id$";
+
+DEF_PARM(int, print_dbg, 0, "Print DBG statements");	/* must be before zap_debug.h */
+DEF_PARM(uint, poll_battery_interval, 100, "Poll battery interval in milliseconds");
+DEF_PARM(bool, report_battery, 0, "Report battery status to zaptel");
+
+/* Signaling is opposite (fxs signalling for fxo card) */
+#if 1
+#define	FXO_DEFAULT_SIGCAP	(ZT_SIG_FXSKS | ZT_SIG_FXSLS)
+#else
+#define	FXO_DEFAULT_SIGCAP	(ZT_SIG_SF)
+#endif
+
+enum fxo_leds {
+	LED_GREEN,
+};
+
+#define	NUM_LEDS		1
+#define	DELAY_UNTIL_DIALTONE	3000
+
+/*---------------- FXO Protocol Commands ----------------------------------*/
+
+static /* 0x0F */ DECLARE_CMD(FXO, CHAN_ENABLE, xpp_line_t lines, bool on);
+static /* 0x0F */ DECLARE_CMD(FXO, CHAN_CID, int pos);
+static /* 0x0F */ DECLARE_CMD(FXO, RING, int pos, bool on);
+static /* 0x0F */ DECLARE_CMD(FXO, SETHOOK, int pos, bool offhook);
+static /* 0x0F */ DECLARE_CMD(FXO, RELAY_OUT, byte which, bool on);
+static /* 0x0F */ DECLARE_CMD(FXO, DAA_QUERY, int pos, byte reg_num);
+
+static bool fxo_packet_is_valid(xpacket_t *pack);
+static void fxo_packet_dump(xpacket_t *pack);
+static int proc_fxo_info_read(char *page, char **start, off_t off, int count, int *eof, void *data);
+static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data);
+static int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data);
+static int process_slic_cmdline(xpd_t *xpd, char *cmdline);
+
+#define	PROC_DAA_FNAME		"slics"
+#define	PROC_FXO_INFO_FNAME	"fxo_info"
+
+struct FXO_priv_data {
+	struct proc_dir_entry		*xpd_slic;
+	struct proc_dir_entry		*fxo_info;
+	slic_reply_t			requested_reply;
+	slic_reply_t			last_reply;
+	xpp_line_t			battery;
+	xpp_line_t			ledstate[NUM_LEDS];	/* 0 - OFF, 1 - ON */
+	int				blinking[NUM_LEDS][CHANNELS_PERXPD];
+};
+
+/*---------------- FXO: Static functions ----------------------------------*/
+
+#define	IS_BLINKING(priv,pos,color)	((priv)->blinking[color][pos] != 0)
+#define	DO_BLINK(priv,pos,color,val)	((priv)->blinking[color][pos] = (val))
+
+/*
+ * LED control is done via DAA register 0x20
+ */
+static int do_led(xpd_t *xpd, lineno_t pos, byte which, bool on)
+{
+	int			ret = 0;
+	xpacket_t		*pack;
+	slic_cmd_t		*sc;
+	int			len;
+	struct FXO_priv_data	*priv;
+	xpp_line_t		lines;
+	xbus_t			*xbus;
+
+	BUG_ON(!xpd);
+	xbus = xpd->xbus;
+	priv = xpd->priv;
+	which = which % NUM_LEDS;
+	if(IS_SET(xpd->digital_outputs, pos) || IS_SET(xpd->digital_inputs, pos))
+		goto out;
+	if(pos == ALL_LINES) {
+		lines = ~0;
+		priv->ledstate[which] = (on) ? ~0 : 0;
+	} else {
+		lines = BIT(pos);
+		if(on) {
+			BIT_SET(priv->ledstate[which], pos);
+		} else {
+			BIT_CLR(priv->ledstate[which], pos);
+		}
+	}
+	if(!lines)	// Nothing to do
+		goto out;
+	DBG("%s/%s: LED: lines=0x%04X which=%d -- %s\n", xbus->busname, xpd->xpdname, lines, which, (on) ? "on" : "off");
+	XPACKET_NEW(pack, xbus, FXO, DAA_WRITE, xpd->id);
+	sc = &RPACKET_FIELD(pack, FXO, DAA_WRITE, slic_cmd);
+	len = slic_cmd_direct_write(sc, lines, 0x20, on);
+	// DBG("LED pack: line=%d %s\n", i, (on)?"on":"off");
+	pack->datalen = len;
+	packet_send(xbus, pack);
+out:
+	return ret;
+}
+
+static void handle_fxo_leds(xpd_t *xpd)
+{
+	int		i;
+	unsigned long	flags;
+	const enum fxo_leds	color = LED_GREEN;
+	unsigned int	timer_count;
+	struct FXO_priv_data	*priv;
+
+	BUG_ON(!xpd);
+	spin_lock_irqsave(&xpd->lock, flags);
+	priv = xpd->priv;
+	timer_count = xpd->timer_count;
+	for_each_line(xpd, i) {
+		if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i))
+			continue;
+		if(IS_BLINKING(priv,i,color)) {
+			// led state is toggled
+			if((timer_count % LED_BLINK_PERIOD) == 0) {
+				DBG("%s/%s/%d: led_state=%s\n", xpd->xbus->busname, xpd->xpdname, i,
+						(IS_SET(priv->ledstate[color], i))?"ON":"OFF");
+				if(!IS_SET(priv->ledstate[color], i)) {
+					do_led(xpd, i, color, 1);
+				} else {
+					do_led(xpd, i, color, 0);
+				}
+			}
+		}
+	}
+	spin_unlock_irqrestore(&xpd->lock, flags);
+}
+
+static void do_sethook(xpd_t *xpd, int pos, bool offhook)
+{
+	unsigned long		flags;
+	struct FXO_priv_data	*priv;
+
+	BUG_ON(!xpd);
+	BUG_ON(xpd->direction == TO_PHONE);		// We can SETHOOK state only on PSTN
+	priv = xpd->priv;
+	BUG_ON(!priv);
+	if(!IS_SET(priv->battery, pos)) {
+		DBG("%s/%s/%d: WARNING: called while battery is off\n", xpd->xbus->busname, xpd->xpdname, pos);
+	}
+	spin_lock_irqsave(&xpd->lock, flags);
+	xpd->ringing[pos] = 0;				// No more rings
+	CALL_XMETHOD(SETHOOK, xpd->xbus, xpd, pos, offhook);
+	if(offhook) {
+		BIT_SET(xpd->hookstate, pos);
+	} else {
+		BIT_CLR(xpd->hookstate, pos);
+		xpd->delay_until_dialtone[pos] = 0;
+	}
+	spin_unlock_irqrestore(&xpd->lock, flags);
+	if(offhook)
+		wake_up_interruptible(&xpd->txstateq[pos]);
+}
+
+/*---------------- FXO: Methods -------------------------------------------*/
+
+static xpd_t *FXO_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision)
+{
+	xpd_t		*xpd = NULL;
+	int		channels = min(8, CHANNELS_PERXPD);
+
+	xpd = xpd_alloc(sizeof(struct FXO_priv_data), xbus, xpd_num, proto_table, channels, revision);
+	if(!xpd)
+		return NULL;
+	xpd->direction = TO_PSTN;
+	xpd->revision = revision;
+	return xpd;
+}
+
+static void clean_proc(xbus_t *xbus, xpd_t *xpd)
+{
+	struct FXO_priv_data	*priv;
+
+	BUG_ON(!xpd);
+	priv = xpd->priv;
+	DBG("%s/%s\n", xbus->busname, xpd->xpdname);
+#ifdef	CONFIG_PROC_FS
+	if(priv->xpd_slic) {
+		DBG("Removing xpd DAA file %s/%s\n", xbus->busname, xpd->xpdname);
+		remove_proc_entry(PROC_DAA_FNAME, xpd->proc_xpd_dir);
+	}
+	if(priv->fxo_info) {
+		DBG("Removing xpd FXO_INFO file %s/%s\n", xbus->busname, xpd->xpdname);
+		remove_proc_entry(PROC_FXO_INFO_FNAME, xpd->proc_xpd_dir);
+	}
+#endif
+}
+
+static int FXO_card_init(xbus_t *xbus, xpd_t *xpd)
+{
+	struct FXO_priv_data	*priv;
+	int			ret = 0;
+	int			i;
+
+	BUG_ON(!xpd);
+	priv = xpd->priv;
+#ifdef	CONFIG_PROC_FS
+	DBG("Creating FXO_INFO file for %s/%s\n", xbus->busname, xpd->xpdname);
+	priv->fxo_info = create_proc_read_entry(PROC_FXO_INFO_FNAME, 0444, xpd->proc_xpd_dir, proc_fxo_info_read, xpd);
+	if(!priv->fxo_info) {
+		ERR("Failed to create proc '%s' for %s/%s\n", PROC_FXO_INFO_FNAME, xbus->busname, xpd->xpdname);
+		ret = -ENOENT;
+		goto err;
+	}
+	priv->fxo_info->owner = THIS_MODULE;
+	DBG("Creating DAAs file for %s/%s\n", xbus->busname, xpd->xpdname);
+	priv->xpd_slic = create_proc_entry(PROC_DAA_FNAME, 0644, xpd->proc_xpd_dir);
+	if(!priv->xpd_slic) {
+		ERR("Failed to create proc file for DAAs of %s/%s\n", xbus->busname, xpd->xpdname);
+		ret = -ENOENT;
+		goto err;
+	}
+	priv->xpd_slic->owner = THIS_MODULE;
+	priv->xpd_slic->write_proc = proc_xpd_slic_write;
+	priv->xpd_slic->read_proc = proc_xpd_slic_read;
+	priv->xpd_slic->data = xpd;
+#endif
+	ret = run_initialize_registers(xpd);
+	if(ret < 0)
+		goto err;
+	// Hanghup all lines
+	for_each_line(xpd, i) {
+		init_waitqueue_head(&xpd->txstateq[i]);
+		do_sethook(xpd, i, 0);
+	}
+	DBG("done: %s/%s\n", xbus->busname, xpd->xpdname);
+	return 0;
+err:
+	clean_proc(xbus, xpd);
+	ERR("%s/%s: Failed initializing registers (%d)\n", xbus->busname, xpd->xpdname, ret);
+	return ret;
+}
+
+static int FXO_card_remove(xbus_t *xbus, xpd_t *xpd)
+{
+	struct FXO_priv_data	*priv;
+
+	BUG_ON(!xpd);
+	priv = xpd->priv;
+	DBG("%s/%s\n", xbus->busname, xpd->xpdname);
+	clean_proc(xbus, xpd);
+	return 0;
+}
+
+static int FXO_card_zaptel_preregistration(xpd_t *xpd, bool on)
+{
+	xbus_t			*xbus;
+	struct FXO_priv_data	*priv;
+	int			i;
+	unsigned long		flags;
+
+	BUG_ON(!xpd);
+	xbus = xpd->xbus;
+	BUG_ON(!xbus);
+	priv = xpd->priv;
+	BUG_ON(!priv);
+	DBG("%s/%s (%d)\n", xbus->busname, xpd->xpdname, on);
+	snprintf(xpd->span.desc, MAX_SPANDESC, "Xorcom XPD #%d/%d: FXO", xbus->num, xpd->id);
+	for_each_line(xpd, i) {
+		struct zt_chan	*cur_chan = &xpd->chans[i];
+
+		DBG("setting FXO channel %d\n", i);
+		snprintf(cur_chan->name, MAX_CHANNAME, "XPP_FXO/%d/%d/%d", xbus->num, xpd->id, i);
+		cur_chan->chanpos = i + 1;
+		cur_chan->pvt = xpd;
+		cur_chan->sigcap = FXO_DEFAULT_SIGCAP;
+	}
+	spin_lock_irqsave(&xpd->lock, flags);
+	do_led(xpd, ALL_LINES, LED_GREEN, LED_OFF);
+	spin_unlock_irqrestore(&xpd->lock, flags);
+	for_each_line(xpd, i) {
+		do_led(xpd, i, LED_GREEN, LED_ON);
+		mdelay(50);
+	}
+	return 0;
+}
+
+static int FXO_card_zaptel_postregistration(xpd_t *xpd, bool on)
+{
+	xbus_t			*xbus;
+	struct FXO_priv_data	*priv;
+	int			i;
+
+	BUG_ON(!xpd);
+	xbus = xpd->xbus;
+	BUG_ON(!xbus);
+	priv = xpd->priv;
+	BUG_ON(!priv);
+	DBG("%s/%s (%d)\n", xbus->busname, xpd->xpdname, on);
+	for_each_line(xpd, i) {
+		do_led(xpd, i, LED_GREEN, LED_OFF);
+		mdelay(50);
+	}
+	return 0;
+}
+
+#ifdef WITH_RBS
+int FXO_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
+{
+	struct FXO_priv_data	*priv;
+
+	priv = xpd->priv;
+	BUG_ON(!priv);
+	DBG("%s/%s/%d: %s\n", xbus->busname, xpd->xpdname, pos, txsig2str(txsig));
+	BUG_ON(xpd->direction != TO_PSTN);
+	/* XXX Enable hooksig for FXO XXX */
+	switch(txsig) {
+		case ZT_TXSIG_START:
+			break;
+		case ZT_TXSIG_OFFHOOK:
+			do_sethook(xpd, pos, 1);
+			break;
+		case ZT_TXSIG_ONHOOK:
+			do_sethook(xpd, pos, 0);
+			break;
+		default:
+			NOTICE("Can't set tx state to %s (%d)\n", txsig2str(txsig), txsig);
+			return -EINVAL;
+	}
+	return 0;
+}
+
+#else
+int FXO_card_sethook(xbus_t *xbus, xpd_t *xpd, int pos, int hookstate)
+{
+	int			ret = 0;
+	struct FXO_priv_data	*priv;
+
+	DBG("%s/%s/%d: %s\n", xbus->busname, xpd->xpdname, pos, hookstate2str(hookstate));
+	switch(hookstate) {
+		/* On-hook, off-hook: The PBX is playing a phone on an FXO line.  */
+		case ZT_ONHOOK:
+			do_sethook(xpd, pos, 0);
+			break;
+		case ZT_START:
+			DBG("%s/%s/%d: fall through ZT_OFFHOOK\n", xbus->busname, xpd->xpdname, pos);
+			xpd->delay_until_dialtone[pos] = DELAY_UNTIL_DIALTONE;
+			// Fall through
+		case ZT_OFFHOOK:
+			do_sethook(xpd, pos, 1);
+			wait_event_interruptible(xpd->txstateq[pos], xpd->delay_until_dialtone[pos] <= 0);
+			break;
+		case ZT_WINK:
+			WARN("No code yet\n");
+			break;
+		case ZT_FLASH:
+			WARN("No code yet\n");
+			break;
+		case ZT_RING:
+			DBG("%s/%s/%d: ZT_RING: %d\n", xbus->busname, xpd->xpdname, pos, xpd->ringing[pos]);
+			break;
+		case ZT_RINGOFF:
+			WARN("No code yet\n");
+			break;
+	}
+	return ret;
+}
+#endif
+
+static void poll_battery(xbus_t *xbus, xpd_t *xpd)
+{
+	int	i;
+
+	for_each_line(xpd, i) {
+		CALL_PROTO(FXO, DAA_QUERY, xbus, xpd, i, DAA_VBAT_REGISTER);
+	}
+}
+
+
+static int FXO_card_tick(xbus_t *xbus, xpd_t *xpd)
+{
+	static unsigned		rate_limit = 0;
+	struct FXO_priv_data	*priv;
+
+	BUG_ON(!xpd);
+	priv = xpd->priv;
+	BUG_ON(!priv);
+	rate_limit++;
+	if(poll_battery_interval != 0 && (rate_limit % poll_battery_interval) == 0) {
+		poll_battery(xbus, xpd);
+	}
+	handle_fxo_leds(xpd);
+	return 0;
+}
+
+/* FIXME: based on data from from wctdm.h */
+#include <wctdm.h>
+static const int echotune_reg[] = {30,45,46,47,58,49,50,51,52};
+union echotune {
+	/* "coeff 0" is acim */
+	unsigned char		coeff[sizeof(echotune_reg)];
+	struct wctdm_echo_coefs	wctdm_struct;
+};
+
+static int FXO_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long arg)
+{
+	union echotune	echoregs;
+	int 		i,ret;
+
+	BUG_ON(!xpd);
+	DBG("cmd: 0x%x, expecting: 0x%x, pos=%d.\n", cmd, WCTDM_SET_ECHOTUNE, pos);
+	switch (cmd) {
+		case WCTDM_SET_ECHOTUNE:
+			DBG("-- Setting echo registers: \n");
+			/* first off: check if this span is fxs. If not: -EINVALID */
+			if (copy_from_user(&echoregs.wctdm_struct, 
+				(struct wctdm_echo_coefs*)arg, sizeof(echoregs.wctdm_struct)))
+				return -EFAULT;
+
+			/* Set the ACIM register */
+			/* quick and dirty registers writing: */
+			for (i=0; i<sizeof(echotune_reg); i++) {
+				char buf[22];
+				xpp_line_t	lines = BIT(pos);
+				sprintf(buf, "%02X %02X %02X %02X WD %2X %2X",
+					(lines & 0xFF),
+					((lines >> 8) & 0xFF),
+					((lines >> 16) & 0xFF),
+					((lines >> 24) & 0xFF),
+					echotune_reg[i],echoregs.coeff[i]
+				);
+				/* FIXME: code duplicated from proc_xpd_register_write */
+				ret = process_slic_cmdline(xpd, buf);
+				if(ret < 0)
+					return ret;
+				mdelay(1);
+			}
+
+			DBG("-- Set echo registers successfully\n");
+
+			break;
+		default:
+			return -ENOTTY;
+	}
+	return 0;
+}
+
+/*---------------- FXO: HOST COMMANDS -------------------------------------*/
+
+static /* 0x0F */ HOSTCMD(FXO, CHAN_ENABLE, xpp_line_t lines, bool on)
+{
+	unsigned long		flags;
+	int	ret = 0;
+	int	i;
+
+	BUG_ON(!xbus);
+	BUG_ON(!xpd);
+	if(!lines) {
+		return 0;
+	}
+	DBG("Channel Activation: 0x%4X %s\n", lines, (on) ? "on" : "off");
+	if(on) {
+		for_each_line(xpd, i) {
+			spin_lock_irqsave(&xpd->lock, flags);
+			do_led(xpd, i, LED_GREEN, LED_ON);
+			spin_unlock_irqrestore(&xpd->lock, flags);
+			mdelay(20);
+		}
+		for_each_line(xpd, i) {
+			spin_lock_irqsave(&xpd->lock, flags);
+			do_led(xpd, i, LED_GREEN, LED_OFF);
+			spin_unlock_irqrestore(&xpd->lock, flags);
+			mdelay(20);
+		}
+	}
+	return ret;
+}
+
+static /* 0x0F */ HOSTCMD(FXO, CHAN_CID, int pos)
+{
+	int		ret = 0;
+	xpp_line_t	lines = BIT(pos);
+
+	BUG_ON(!xbus);
+	BUG_ON(!xpd);
+	if(!lines) {
+		return 0;
+	}
+	DBG("%s/%s/%d:\n", xbus->busname, xpd->xpdname, pos);
+	return ret;
+}
+
+
+static /* 0x0F */ HOSTCMD(FXO, RING, int pos, bool on)
+{
+	int		ret = 0;
+	xpacket_t	*pack;
+	slic_cmd_t	*sc;
+	xpp_line_t	mask = BIT(pos);
+	int		len;
+
+	BUG_ON(!xbus);
+	BUG_ON(!xpd);
+	if(!mask) {
+		return 0;
+	}
+	DBG("%s/%s/%d %s\n", xpd->xbus->busname, xpd->xpdname, pos, (on) ? "on" : "off");
+	XPACKET_NEW(pack, xbus, FXO, DAA_WRITE, xpd->id);
+	sc = &RPACKET_FIELD(pack, FXO, DAA_WRITE, slic_cmd);
+	len = slic_cmd_direct_write(sc, mask, 0x40, (on)?0x04:0x01);
+	pack->datalen = len;
+
+	packet_send(xbus, pack);
+	return ret;
+}
+
+static /* 0x0F */ HOSTCMD(FXO, SETHOOK, int pos, bool offhook)
+{
+	int		ret = 0;
+	xpacket_t	*pack;
+	slic_cmd_t	*sc;
+	int		len;
+	unsigned long	flags;
+	bool		value;
+
+	BUG_ON(!xbus);
+	BUG_ON(!xpd);
+	value = (offhook) ? 0x09 : 0x08;
+	// value |= BIT(3);	/* Bit 3 is for CID */
+	DBG("%s/%s/%d: SETHOOK: value=0x%02X %s\n", xbus->busname, xpd->xpdname, pos, value, (offhook)?"OFFHOOK":"ONHOOK");
+	spin_lock_irqsave(&xpd->lock, flags);
+	XPACKET_NEW(pack, xbus, FXO, DAA_WRITE, xpd->id);
+	sc = &RPACKET_FIELD(pack, FXO, DAA_WRITE, slic_cmd);
+	len = slic_cmd_direct_write(sc, BIT(pos), 0x05, value);
+	pack->datalen = len;
+	packet_send(xbus, pack);
+	do_led(xpd, pos, LED_GREEN, (offhook)?LED_ON:LED_OFF);
+	spin_unlock_irqrestore(&xpd->lock, flags);
+	return ret;
+}
+
+static /* 0x0F */ HOSTCMD(FXO, RELAY_OUT, byte which, bool on)
+{
+	return -ENOSYS;
+}
+
+static /* 0x0F */ HOSTCMD(FXO, DAA_QUERY, int pos, byte reg_num)
+{
+	int	ret = 0;
+	xpacket_t	*pack;
+	slic_cmd_t	*sc;
+	int		len;
+
+	BUG_ON(!xbus);
+	BUG_ON(!xpd);
+	// DBG("\n");
+	XPACKET_NEW(pack, xbus, FXO, DAA_WRITE, xpd->id);
+	sc = &RPACKET_FIELD(pack, FXO, DAA_WRITE, slic_cmd);
+	len = slic_cmd_direct_read(sc, BIT(pos), reg_num);
+
+	pack->datalen = len;
+
+	packet_send(xbus, pack);
+	return ret;
+}
+
+/*---------------- FXO: Astribank Reply Handlers --------------------------*/
+
+HANDLER_DEF(FXO, SIG_CHANGED)
+{
+	xpp_line_t	sig_status = RPACKET_FIELD(pack, FXO, SIG_CHANGED, sig_status);
+	xpp_line_t	sig_toggles = RPACKET_FIELD(pack, FXO, SIG_CHANGED, sig_toggles);
+	unsigned long	flags;
+	int		i;
+
+	if(!xpd) {
+		NOTICE("%s: received %s for non-existing xpd: %d\n",
+				__FUNCTION__, cmd->name, XPD_NUM(pack->content.addr));
+		return -EPROTO;
+	}
+	DBG("%s/%s: (PSTN) sig_toggles=0x%04X sig_status=0x%04X\n", xpd->xbus->busname, xpd->xpdname, sig_toggles, sig_status);
+	spin_lock_irqsave(&xpd->lock, flags);
+	for_each_line(xpd, i) {
+		if(IS_SET(sig_status, i)) {
+			xpd->ringing[i] = 1;
+		} else {
+			xpd->ringing[i] = 0;
+		}
+	}
+	spin_unlock_irqrestore(&xpd->lock, flags);
+	return 0;
+}
+
+HANDLER_DEF(FXO, DAA_REPLY)
+{
+	slic_reply_t		*info = &RPACKET_FIELD(pack, FXO, DAA_REPLY, info);
+	xpp_line_t		lines = RPACKET_FIELD(pack, FXO, DAA_REPLY, lines);
+	unsigned long		flags;
+	struct FXO_priv_data	*priv;
+
+	if(!xpd) {
+		NOTICE("%s: received %s for non-existing xpd: %d\n",
+				__FUNCTION__, cmd->name, XPD_NUM(pack->content.addr));
+		return -EPROTO;
+	}
+	spin_lock_irqsave(&xpd->lock, flags);
+	priv = xpd->priv;
+	BUG_ON(!priv);
+	if(!info->indirect && info->reg_num == DAA_VBAT_REGISTER) {
+		xpp_line_t	last_batt_on = priv->battery;
+		xpp_line_t	changed_lines;
+		int		i;
+
+		if(abs(info->data_low) < BAT_THRESHOLD) {
+			priv->battery &= ~lines;
+			// DBG("%s/%s: BATTERY OFF (%04X) = %d\n", xpd->xbus->busname, xpd->xpdname, lines, info->data_low);
+		} else {
+			priv->battery |= lines;
+			// DBG("%s/%s: BATTERY ON (%04X) = %d\n", xpd->xbus->busname, xpd->xpdname, lines, info->data_low);
+		}
+		changed_lines = last_batt_on ^ priv->battery;
+		for_each_line(xpd, i) {
+			if(IS_SET(changed_lines, i)) {
+				update_line_status(xpd, i, IS_SET(priv->battery, i));
+			}
+		}
+	}
+#if 0
+	DBG("DAA_REPLY: xpd #%d %s reg_num=0x%X, dataL=0x%X dataH=0x%X\n",
+			xpd->id, (info->indirect)?"I":"D",
+			info->reg_num, info->data_low, info->data_high);
+#endif
+
+	/* Update /proc info only if reply relate to the last slic read request */
+	if(priv->requested_reply.indirect == info->indirect &&
+			priv->requested_reply.reg_num == info->reg_num) {
+		priv->last_reply = *info;
+	}
+	spin_unlock_irqrestore(&xpd->lock, flags);
+	return 0;
+}
+
+
+xproto_table_t PROTO_TABLE(FXO) = {
+	.owner = THIS_MODULE,
+	.entries = {
+		/*	Card	Opcode		*/
+		XENTRY(	FXO,	SIG_CHANGED	),
+		XENTRY(	FXO,	DAA_REPLY	),
+	},
+	.name = "FXO",
+	.type = XPD_TYPE_FXO,
+	.xops = {
+		.card_new	= FXO_card_new,
+		.card_init	= FXO_card_init,
+		.card_remove	= FXO_card_remove,
+		.card_zaptel_preregistration	= FXO_card_zaptel_preregistration,
+		.card_zaptel_postregistration	= FXO_card_zaptel_postregistration,
+#ifdef WITH_RBS
+		.card_hooksig	= FXO_card_hooksig,
+#else
+		.card_sethook	= FXO_card_sethook,
+#endif
+		.card_tick	= FXO_card_tick,
+		.card_ioctl	= FXO_card_ioctl,
+
+		.RING		= XPROTO_CALLER(FXO, RING),
+		.SETHOOK	= XPROTO_CALLER(FXO, SETHOOK),
+		.RELAY_OUT	= XPROTO_CALLER(FXO, RELAY_OUT),
+		.CHAN_ENABLE	= XPROTO_CALLER(FXO, CHAN_ENABLE),
+		.CHAN_CID	= XPROTO_CALLER(FXO, CHAN_CID),
+
+		.SYNC_SOURCE	= XPROTO_CALLER(GLOBAL, SYNC_SOURCE),
+		.PCM_WRITE	= XPROTO_CALLER(GLOBAL, PCM_WRITE),
+	},
+	.packet_is_valid = fxo_packet_is_valid,
+	.packet_dump = fxo_packet_dump,
+};
+
+static bool fxo_packet_is_valid(xpacket_t *pack)
+{
+	const xproto_entry_t	*xe;
+
+	//DBG("\n");
+	xe = xproto_card_entry(&PROTO_TABLE(FXO), pack->content.opcode);
+	return xe != NULL;
+}
+
+static void fxo_packet_dump(xpacket_t *pack)
+{
+	DBG("\n");
+}
+
+/*------------------------- DAA Handling --------------------------*/
+
+static int proc_fxo_info_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	int			len = 0;
+	unsigned long		flags;
+	xpd_t			*xpd = data;
+	struct FXO_priv_data	*priv;
+	int			i;
+
+	if(!xpd)
+		return -ENODEV;
+	spin_lock_irqsave(&xpd->lock, flags);
+	priv = xpd->priv;
+	BUG_ON(!priv);
+	len += sprintf(page + len, "\t%-17s: ", "Channel");
+	for_each_line(xpd, i) {
+		if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
+			len += sprintf(page + len, "%d ", i % 10);
+	}
+	len += sprintf(page + len, "\n\t%-17s: ", "ledstate");
+	for_each_line(xpd, i) {
+		if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
+			len += sprintf(page + len, "%d ", IS_SET(priv->ledstate[LED_GREEN], i));
+	}
+	len += sprintf(page + len, "\n\t%-17s: ", "blinking");
+	for_each_line(xpd, i) {
+		if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i))
+			len += sprintf(page + len, "%d ", IS_BLINKING(priv,i,LED_GREEN));
+	}
+	len += sprintf(page + len, "\n\t%-17s: ", "battery");
+	for_each_line(xpd, i) {
+		len += sprintf(page + len, "%d ", IS_SET(priv->battery, i));
+	}
+	len += sprintf(page + len, "\n");
+	spin_unlock_irqrestore(&xpd->lock, flags);
+	if (len <= off+count)
+		*eof = 1;
+	*start = page + off;
+	len -= off;
+	if (len > count)
+		len = count;
+	if (len < 0)
+		len = 0;
+	return len;
+}
+
+
+static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	int			len = 0;
+	unsigned long		flags;
+	xpd_t			*xpd = data;
+	slic_reply_t		*info;
+	struct FXO_priv_data	*priv;
+
+	BUG_ON(!xpd);
+	spin_lock_irqsave(&xpd->lock, flags);
+	priv = xpd->priv;
+	BUG_ON(!priv);
+	info = &priv->last_reply;
+	len += sprintf(page + len, "# Writing bad data into this file may damage your hardware!\n");
+	len += sprintf(page + len, "# Consult firmware docs first\n");
+	len += sprintf(page + len, "DAA_REPLY: %s reg_num=0x%X, dataH=0x%X dataL=0x%X\n",
+			(info->indirect)?"I":"D",
+			info->reg_num, info->data_high, info->data_low);
+	spin_unlock_irqrestore(&xpd->lock, flags);
+	if (len <= off+count)
+		*eof = 1;
+	*start = page + off;
+	len -= off;
+	if (len > count)
+		len = count;
+	if (len < 0)
+		len = 0;
+	return len;
+}
+
+/*
+ *        Direct/Indirect
+ *              v
+ * FF FF FF FF WD 06 1
+ * ^---------^ ^  Reg
+ *      | Write/Read
+ *      |
+ *    DAA #
+ */
+static int parse_slic_cmd(const char *buf, slic_cmd_t *sc, slic_reply_t *requested_reply)
+{
+	char		op;		/* [W]rite, [R]ead */
+	char		reg_type;	/* [D]irect, [I]ndirect */
+	int		s1, s2, s3, s4;
+	int		reg_num;
+	int		data_low, data_high;
+	xpp_line_t	lines;
+	int		ret;
+
+	ret = sscanf(buf, "%x %x %x %x %c%c %x %x %x",
+			&s1, &s2, &s3, &s4, &op, &reg_type, &reg_num, &data_high, &data_low);
+	lines = (s4 << 24) | (s3 << 16) | (s2 << 8) | (s1);
+	switch(op) {
+		case 'R':
+			if(reg_type == 'D' && ret == 7) {
+				// DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num);
+				ret = slic_cmd_direct_read(sc, lines, reg_num);
+				if(requested_reply) {
+					requested_reply->indirect = 0;
+					requested_reply->reg_num = reg_num;
+				}
+			} else if(reg_type == 'I' && ret == 7) {
+				// DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num);
+				ret = slic_cmd_indirect_read(sc, lines, reg_num);
+				if(requested_reply) {
+					requested_reply->indirect = 1;
+					requested_reply->reg_num = reg_num;
+				}
+			} else {
+				NOTICE("%s: Bad read input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type);
+				goto err;
+			}
+			break;
+		case 'W':
+			if(reg_type == 'D' && ret == 8) {
+				// DBG("0x%X 0x%X 0x%X 0x%X %c %x %X\n", s1, s2, s3, s4, reg_type, reg_num, data_high);
+				ret = slic_cmd_direct_write(sc, lines, reg_num, data_high);
+			} else if(reg_type == 'I' && ret == 9) {
+				// DBG("0x%X 0x%X 0x%X 0x%X %c %x %X %X\n", s1, s2, s3, s4, reg_type, reg_num, data_high, data_low);
+				ret = slic_cmd_indirect_write(sc, lines, reg_num, data_low, data_high);
+			} else {
+				NOTICE("%s: Bad write input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type);
+				goto err;
+			}
+			break;
+		default:
+			NOTICE("%s: Bad input: ret=%d buf='%s' op=%c\n", __FUNCTION__, ret, buf, op);
+			goto err;
+	}
+	return ret;
+err:
+	return -EINVAL;
+}
+
+static int process_slic_cmdline(xpd_t *xpd, char *cmdline)
+{
+	xbus_t			*xbus;
+	struct FXO_priv_data	*priv;
+	slic_cmd_t		sc;
+	xpacket_t		*pack;
+	char			*p;
+	int			len = strlen(cmdline);
+
+	BUG_ON(!xpd);
+	xbus = xpd->xbus;
+	priv = xpd->priv;
+	if((p = strchr(cmdline, '#')) != NULL)	/* Truncate comments */
+		*p = '\0';
+	if((p = strchr(cmdline, ';')) != NULL)	/* Truncate comments */
+		*p = '\0';
+	for(p = cmdline; *p && (*p == ' ' || *p == '\t'); p++) /* Trim leading whitespace */
+		;
+	if(*p == '\0')
+		return 0;
+	len = parse_slic_cmd(p, &sc, &priv->requested_reply);
+	if(len < 0)
+		return len;
+	if(!sc.lines) {
+		NOTICE("%s: no channels are marked. Skip.\n", __FUNCTION__);
+		return 0;
+	}
+	dump_slic_cmd("WRITE_DAA", &sc);
+	XPACKET_NEW(pack, xbus, FXO, DAA_WRITE, xpd->id);
+	RPACKET_FIELD(pack, FXO, DAA_WRITE, slic_cmd) = sc;
+	pack->datalen = len;
+	packet_send(xbus, pack);
+	return 0;
+}
+
+static int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data)
+{
+	xpd_t		*xpd = data;
+	const int	LINE_LEN = 500;
+	char		buf[LINE_LEN];
+	char		*p;
+	int		i;
+	int		ret;
+
+	if(!xpd)
+		return -ENODEV;
+	for(i = 0; i < count; /* noop */) {
+		for(p = buf; p < buf + LINE_LEN; p++) {	/* read a line */
+			if(i >= count)
+				break;
+			if(get_user(*p, buffer + i))
+				return -EFAULT;
+			i++;
+			if(*p == '\n' || *p == '\r')	/* whatever */
+				break;
+		}
+		if(p >= buf + LINE_LEN)
+			return -E2BIG;
+		*p = '\0';
+		ret = process_slic_cmdline(xpd, buf);
+		if(ret < 0)
+			return ret;
+		mdelay(1);
+	}
+	return count;
+}
+
+
+int __init card_fxo_startup(void)
+{
+	INFO("%s revision %s\n", THIS_MODULE->name, ZAPTEL_VERSION);
+	xproto_register(&PROTO_TABLE(FXO));
+	return 0;
+}
+
+void __exit card_fxo_cleanup(void)
+{
+	xproto_unregister(&PROTO_TABLE(FXO));
+}
+
+MODULE_DESCRIPTION("XPP FXO Card Driver");
+MODULE_AUTHOR("Oron Peled <oron at actcom.co.il>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ZAPTEL_VERSION);
+MODULE_ALIAS_XPD(XPD_TYPE_FXO);
+
+module_init(card_fxo_startup);
+module_exit(card_fxo_cleanup);

Propchange: branches/1.2/xpp/card_fxo.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: branches/1.2/xpp/card_fxo.c
------------------------------------------------------------------------------
    svn:keywords = Id

Propchange: branches/1.2/xpp/card_fxo.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: branches/1.2/xpp/card_fxo.h
URL: http://svn.digium.com/view/zaptel/branches/1.2/xpp/card_fxo.h?rev=1212&view=auto
==============================================================================
--- branches/1.2/xpp/card_fxo.h (added)
+++ branches/1.2/xpp/card_fxo.h Fri Jul  7 19:43:31 2006
@@ -1,0 +1,61 @@
+#ifndef	CARD_FXO_H
+#define	CARD_FXO_H
+/*
+ * Written by Oron Peled <oron at actcom.co.il>
+ * Copyright (C) 2004-2006, Xorcom
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by

[... 11943 lines stripped ...]


More information about the zaptel-commits mailing list