[Pkg-voip-commits] r1911 - in zaptel/trunk: . debian vzaphfc

Kilian Krause kilian at costa.debian.org
Sat Jun 17 14:06:39 UTC 2006


Author: kilian
Date: 2006-06-17 14:06:37 +0000 (Sat, 17 Jun 2006)
New Revision: 1911

Added:
   zaptel/trunk/vzaphfc/
   zaptel/trunk/vzaphfc/ChangeLog
   zaptel/trunk/vzaphfc/Makefile
   zaptel/trunk/vzaphfc/README
   zaptel/trunk/vzaphfc/fifo.c
   zaptel/trunk/vzaphfc/fifo.h
   zaptel/trunk/vzaphfc/lapd.c
   zaptel/trunk/vzaphfc/lapd.h
   zaptel/trunk/vzaphfc/vzaphfc.h
   zaptel/trunk/vzaphfc/vzaphfc_main.c
Modified:
   zaptel/trunk/debian/changelog
   zaptel/trunk/debian/copyright
   zaptel/trunk/debian/rules
Log:
import vzaphfc by Jens Wilke.


Modified: zaptel/trunk/debian/changelog
===================================================================
--- zaptel/trunk/debian/changelog	2006-06-17 13:14:33 UTC (rev 1910)
+++ zaptel/trunk/debian/changelog	2006-06-17 14:06:37 UTC (rev 1911)
@@ -2,8 +2,11 @@
 
   * NOT RELEASED YET
 
- -- Tzafrir Cohen <tzafrir.cohen at xorcom.com>  Fri, 16 Jun 2006 11:38:55 +0300
+  [ Kilian Krause ]
+  * Add vzaphfc driver (enhanced zaphfc) by Jens Wilke.
 
+ -- Kilian Krause <kilian at debian.org>  Sat, 17 Jun 2006 16:05:32 +0200
+
 zaptel (1:1.2.6-2) unstable; urgency=high
 
   * Urgency high as this is blocking a security fix for asterisk [CVE-2006-2898]

Modified: zaptel/trunk/debian/copyright
===================================================================
--- zaptel/trunk/debian/copyright	2006-06-17 13:14:33 UTC (rev 1910)
+++ zaptel/trunk/debian/copyright	2006-06-17 14:06:37 UTC (rev 1911)
@@ -32,3 +32,10 @@
 sethdlc.c: * Copyright (C) 2000 Krzysztof Halasa <khc at pm.waw.pl>
 ztd-loc.c: * Copyright (C) 2004, Axialys Interactive
 ztdummy.c: * Copyright (C) 2002, Hermes Softlab
+
+bristuff driver:
+ * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH
+
+vzaphfc is derived from bristuff and since enhanced by:
+ * Copyright (C) 2004-2006, Daniele "Vihai" Orlandi <daniele at orlandi.com>
+ * Copyright (C) 2006, headissue GmbH; Jens Wilke <jw_vzaphfc at headissue.com>

Modified: zaptel/trunk/debian/rules
===================================================================
--- zaptel/trunk/debian/rules	2006-06-17 13:14:33 UTC (rev 1910)
+++ zaptel/trunk/debian/rules	2006-06-17 14:06:37 UTC (rev 1911)
@@ -130,7 +130,7 @@
 	# driver source code
 	mkdir -p $(TARDIR)/debian
 	cp Makefile .version *.c *.h *.rbt $(TARDIR)/
-	for dir in build_tools cwain qozap xpp zaphfc ztgsm; do \
+	for dir in build_tools cwain qozap vzaphfc xpp zaphfc ztgsm; do \
 	  if [ -d $$dir ]; then cp -r $$dir $(TARDIR); fi; \
 	done
 	dh_install -i zaptel.h torisa.h usr/include/linux/

Added: zaptel/trunk/vzaphfc/ChangeLog
===================================================================
--- zaptel/trunk/vzaphfc/ChangeLog	2006-06-17 13:14:33 UTC (rev 1910)
+++ zaptel/trunk/vzaphfc/ChangeLog	2006-06-17 14:06:37 UTC (rev 1911)
@@ -0,0 +1,4 @@
+1.42 - 2006jun06 - Jens Wilke <jw_vzaphfc at headissue.com>
+- fixed fifo timing issue
+- added statistics for fifo underruns (RXU&TXU)
+- maintanance now done by jw

Added: zaptel/trunk/vzaphfc/Makefile
===================================================================
--- zaptel/trunk/vzaphfc/Makefile	2006-06-17 13:14:33 UTC (rev 1910)
+++ zaptel/trunk/vzaphfc/Makefile	2006-06-17 14:06:37 UTC (rev 1911)
@@ -0,0 +1,38 @@
+#
+#  Copyright (C) 2006 headissue GmbH; Jens Wilke
+#
+#  This program is free software and may be modified and
+#  distributed under the terms of the GNU Public License.
+#
+
+KSOURCE ?= /lib/modules/`uname -r`/build
+
+ifndef src
+	src=$(shell pwd)
+endif
+
+ZAPTEL_SOURCE = $(src)/../../bristuff12/zaptel/
+
+EXTRA_CFLAGS += -I $(ZAPTEL_SOURCE)
+
+obj-m	:= vzaphfc.o
+vzaphfc-objs := vzaphfc_main.o fifo.o lapd.o
+
+all:
+	$(MAKE) -C $(KSOURCE) SUBDIRS=`pwd` modules
+
+clean:
+	$(MAKE) -C $(KSOURCE) SUBDIRS=`pwd` clean
+
+debug:
+	$(MAKE) -C $(KSOURCE) modules SUBDIRS=`pwd` EXTRA_CFLAGS="$(EXTRA_CFLAGS) -DDEBUG" CONFIG_DEBUG_INFO=1
+
+# 2.4 stuff ####
+modules: $(obj-m)
+	sync
+
+vzaphfc.o: vzaphfc_main.c fifo.c vzaphfc.h fifo.h lapd.h
+	$(CC) -c vzaphfc_main.c fifo.c $(CFLAGS) $(EXTRA_CFLAGS)
+
+install:
+	install -D -m 644 vzaphfc.ko $(INSTALL_PREFIX)/lib/modules/`uname -r`/misc/vzaphfc.ko

Added: zaptel/trunk/vzaphfc/README
===================================================================
--- zaptel/trunk/vzaphfc/README	2006-06-17 13:14:33 UTC (rev 1910)
+++ zaptel/trunk/vzaphfc/README	2006-06-17 14:06:37 UTC (rev 1911)
@@ -0,0 +1,45 @@
+== General Info ==
+
+This is a Zaptel driver for HFC-S PCI A based ISDN BRI cards (Cologne
+Chipdesign).
+
+== Driver Build ==
+
+You need the kernel source (2.6) and the Zaptel driver sources
+to build the module.
+
+== Module Parameters ==
+
+  nt_modes:
+  comma seperated list 
+
+  sniff_zaptel_d_channel:
+  Make frames transmitted from Zaptel appear as received by the board
+  Set this to 1 if you want to debug D channel signaling with Etherreal
+
+  force_l1_up:
+  Keep ISDN bus activated. Usually set by PtP TE interfaces.
+
+  nt_modes:
+  Comma-separated list of card IDs to configure in NT mode (array of int)
+
+== Information in the proc file system ==
+
+  You will find valuable data about the current state of your ISDN
+  interface in /proc/drivers/vzaphfc/<cardnum>. The following files
+  are present for each card:
+
+  info:
+  Information about the bus activation, transmit errors (CRC), and 
+  overall transfer statistics
+
+  bufs:
+  Except from the data that is currently transmitted
+
+  fifo:
+  sizes and counter values from the FIFOs, mainly for debugging
+  purposes
+
+
+Latest Change: 6 Jun 2006 Jens Wilke <jw_vzaphfc at headissue.com>
+

Added: zaptel/trunk/vzaphfc/fifo.c
===================================================================
--- zaptel/trunk/vzaphfc/fifo.c	2006-06-17 13:14:33 UTC (rev 1910)
+++ zaptel/trunk/vzaphfc/fifo.c	2006-06-17 14:06:37 UTC (rev 1911)
@@ -0,0 +1,366 @@
+/*
+ * fifo.c - HFC FIFO management routines
+ *
+ * Copyright (C) 2006 headissue GmbH; Jens Wilke
+ * Copyright (C) 2004 Daniele Orlandi
+ * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH
+ *
+ * Original author of this code is
+ * Daniele "Vihai" Orlandi <daniele at orlandi.com>
+ *
+ * This program is free software and may be modified and
+ * distributed under the terms of the GNU Public License.
+ *
+ */
+
+#include <linux/kernel.h>
+
+#include <zaptel.h>
+
+#include "fifo.h"
+
+static void hfc_fifo_mem_read(struct hfc_chan_simplex *chan,
+	int z_start,
+	void *data, int size)
+{
+	int bytes_to_boundary = chan->z_max - z_start + 1;
+	if (bytes_to_boundary >= size) {
+		memcpy(data,
+			chan->z_base + z_start,
+			size);
+	} else {
+		// Buffer wrap
+		memcpy(data,
+			chan->z_base + z_start,
+			bytes_to_boundary);
+
+		memcpy(data + bytes_to_boundary,
+			chan->fifo_base,
+			size - bytes_to_boundary);
+	}
+}
+
+static void hfc_fifo_mem_write(struct hfc_chan_simplex *chan,
+	void *data, int size)
+{
+	int bytes_to_boundary = chan->z_max - *Z1_F1(chan) + 1;
+	if (bytes_to_boundary >= size) {
+		memcpy(chan->z_base + *Z1_F1(chan),
+			data,
+			size);
+	} else {
+		// FIFO wrap
+
+		memcpy(chan->z_base + *Z1_F1(chan),
+			data,
+			bytes_to_boundary);
+
+		memcpy(chan->fifo_base,
+			data + bytes_to_boundary,
+			size - bytes_to_boundary);
+	}
+}
+
+int hfc_fifo_get(struct hfc_chan_simplex *chan,
+		void *data, int size)
+{
+	int available_bytes;
+
+	// Some useless statistic
+	chan->bytes += size;
+
+	available_bytes = hfc_fifo_used_rx(chan);
+
+	if (available_bytes < size && !chan->fifo_underrun++) {
+		// print the warning only once
+		printk(KERN_WARNING hfc_DRIVER_PREFIX
+			"card %d: "
+			"chan %s: "
+			"RX FIFO not enough (%d) bytes to receive!\n",
+			chan->chan->card->cardnum,
+			chan->chan->name,
+			available_bytes);
+		return -1;
+	}
+
+	hfc_fifo_mem_read(chan, *Z2_F2(chan), data, size);
+	*Z2_F2(chan) = Z_inc(chan, *Z2_F2(chan), size);
+	return available_bytes - size;
+}
+
+/*
+static void hfc_fifo_drop(struct hfc_chan_simplex *chan, int size)
+{
+	int available_bytes = hfc_fifo_used_rx(chan);
+	if (available_bytes + 1 < size) {
+		printk(KERN_WARNING hfc_DRIVER_PREFIX
+			"card %d: "
+			"chan %s: "
+			"RX FIFO not enough (%d) bytes to drop!\n",
+			chan->chan->card->cardnum,
+			chan->chan->name,
+			available_bytes);
+
+		return;
+	}
+
+	*Z2_F2(chan) = Z_inc(chan, *Z2_F2(chan), size);
+}
+*/
+
+void hfc_fifo_put(struct hfc_chan_simplex *chan,
+			void *data, int size)
+{
+	struct hfc_card *card = chan->chan->card;
+	int used_bytes = hfc_fifo_used_tx(chan);
+	int free_bytes = hfc_fifo_free_tx(chan);
+
+	if (!used_bytes && !chan->fifo_underrun++) {
+		// print warning only once, to make timing not worse
+		printk(KERN_WARNING hfc_DRIVER_PREFIX
+			"card %d: "
+			"chan %s: "
+			"TX FIFO has become empty\n",
+			card->cardnum,
+			chan->chan->name);
+	}
+	if (free_bytes < size) {
+		printk(KERN_CRIT hfc_DRIVER_PREFIX
+			"card %d: "
+			"chan %s: "
+			"TX FIFO full!\n",
+			chan->chan->card->cardnum,
+			chan->chan->name);
+		chan->fifo_full++;
+		hfc_clear_fifo_tx(chan);
+	}
+
+	hfc_fifo_mem_write(chan, data, size);
+	chan->bytes += size;
+	*Z1_F1(chan) = Z_inc(chan, *Z1_F1(chan), size);
+}
+
+int hfc_fifo_get_frame(struct hfc_chan_simplex *chan, void *data, int max_size)
+{
+	int frame_size;
+	u16 newz2 ;
+	
+	if (*chan->f1 == *chan->f2) {
+		// nothing received, strange uh?
+		printk(KERN_WARNING hfc_DRIVER_PREFIX
+			"card %d: "
+			"chan %s: "
+			"get_frame called with no frame in FIFO.\n",
+			chan->chan->card->cardnum,
+			chan->chan->name);
+
+		return -1;
+	}
+
+	// frame_size includes CRC+CRC+STAT
+	frame_size = hfc_fifo_get_frame_size(chan);
+
+#ifdef DEBUG
+	if(debug_level == 3) {
+		printk(KERN_DEBUG hfc_DRIVER_PREFIX
+			"card %d: "
+			"chan %s: "
+			"RX len %2d: ",
+			chan->chan->card->cardnum,
+			chan->chan->name,
+			frame_size);
+	} else if(debug_level >= 4) {
+		printk(KERN_DEBUG hfc_DRIVER_PREFIX
+			"card %d: "
+			"chan %s: "
+			"RX (f1=%02x, f2=%02x, z1=%04x, z2=%04x) len %2d: ",
+			chan->chan->card->cardnum,
+			chan->chan->name,
+			*chan->f1, *chan->f2, *Z1_F2(chan), *Z2_F2(chan),
+			frame_size);
+	}
+
+	if(debug_level >= 3) {
+		int i;
+		for (i=0; i < frame_size; i++) {
+			printk("%02x", hfc_fifo_u8(chan,
+				Z_inc(chan, *Z2_F2(chan), i)));
+		}
+
+		printk("\n"); 
+	}
+#endif
+
+	if (frame_size <= 0) {
+#ifdef DEBUG
+		if (debug_level >= 2) {
+			printk(KERN_DEBUG hfc_DRIVER_PREFIX
+				"card %d: "
+				"chan %s: "
+				"invalid (empty) frame received.\n",
+				chan->chan->card->cardnum,
+				chan->chan->name);
+		}
+#endif
+
+		hfc_fifo_drop_frame(chan);
+		return -1;
+	}
+
+	// STAT is not really received
+	chan->bytes += frame_size - 1;
+
+	// Calculate beginning of the next frame
+	newz2 = Z_inc(chan, *Z2_F2(chan), frame_size);
+
+	// We cannot use hfc_fifo_get because of different semantic of
+	// "available bytes" and to avoid useless increment of Z2
+	hfc_fifo_mem_read(chan, *Z2_F2(chan), data,
+		frame_size < max_size ? frame_size : max_size);
+
+	if (hfc_fifo_u8(chan, Z_inc(chan, *Z2_F2(chan),
+		frame_size - 1)) != 0x00) {
+		// CRC not ok, frame broken, skipping
+#ifdef DEBUG
+		if(debug_level >= 2) {
+			printk(KERN_WARNING hfc_DRIVER_PREFIX
+				"card %d: "
+				"chan %s: "
+				"Received frame with wrong CRC\n",
+				chan->chan->card->cardnum,
+				chan->chan->name);
+		}
+#endif
+
+		chan->crc++;
+		chan->chan->net_device_stats.rx_errors++;
+
+		hfc_fifo_drop_frame(chan);
+		return -1;
+	}
+
+	chan->frames++;
+
+	*chan->f2 = F_inc(chan, *chan->f2, 1);
+
+	// Set Z2 for the next frame we're going to receive
+	*Z2_F2(chan) = newz2;
+
+	return frame_size;
+}
+
+void hfc_fifo_drop_frame(struct hfc_chan_simplex *chan)
+{
+	int available_bytes;
+	u16 newz2;
+
+	if (*chan->f1 == *chan->f2) {
+		// nothing received, strange eh?
+		printk(KERN_WARNING hfc_DRIVER_PREFIX
+			"card %d: "
+			"chan %s: "
+			"skip_frame called with no frame in FIFO.\n",
+			chan->chan->card->cardnum,
+			chan->chan->name);
+
+		return;
+	}
+
+//	chan->drops++;
+
+	available_bytes = hfc_fifo_used_rx(chan) + 1;
+
+	// Calculate beginning of the next frame
+	newz2 = Z_inc(chan, *Z2_F2(chan), available_bytes);
+
+	*chan->f2 = F_inc(chan, *chan->f2, 1);
+
+	// Set Z2 for the next frame we're going to receive
+	*Z2_F2(chan) = newz2;
+}
+
+void hfc_fifo_put_frame(struct hfc_chan_simplex *chan,
+		 void *data, int size)
+{
+	u16 newz1;
+	int available_frames;
+
+#ifdef DEBUG
+	if (debug_level == 3) {
+		printk(KERN_DEBUG hfc_DRIVER_PREFIX
+			"card %d: "
+			"chan %s: "
+			"TX len %2d: ",
+			chan->chan->card->cardnum,
+			chan->chan->name,
+			size);
+	} else if (debug_level >= 4) {
+		printk(KERN_DEBUG hfc_DRIVER_PREFIX
+			"card %d: "
+			"chan %s: "
+			"TX (f1=%02x, f2=%02x, z1=%04x, z2=%04x) len %2d: ",
+			chan->chan->card->cardnum,
+			chan->chan->name,
+			*chan->f1, *chan->f2, *Z1_F1(chan), *Z2_F1(chan),
+			size);
+	}
+
+	if (debug_level >= 3) {
+		int i;
+		for (i=0; i<size; i++)
+			printk("%02x",((u8 *)data)[i]);
+
+		printk("\n");
+	}
+#endif
+
+	available_frames = hfc_fifo_free_frames(chan);
+
+	if (available_frames >= chan->f_num) {
+		printk(KERN_CRIT hfc_DRIVER_PREFIX
+			"card %d: "
+			"chan %s: "
+			"TX FIFO total number of frames exceeded!\n",
+			chan->chan->card->cardnum,
+			chan->chan->name);
+
+		chan->fifo_full++;
+
+		return;
+	}
+
+	hfc_fifo_put(chan, data, size);
+
+	newz1 = *Z1_F1(chan);
+
+	*chan->f1 = F_inc(chan, *chan->f1, 1);
+
+	*Z1_F1(chan) = newz1;
+
+	chan->frames++;
+}
+
+void hfc_clear_fifo_rx(struct hfc_chan_simplex *chan)
+{
+	*chan->f2 = *chan->f1;
+	*Z2_F2(chan) = *Z1_F2(chan);
+}
+
+void hfc_clear_fifo_tx(struct hfc_chan_simplex *chan)
+{
+	*chan->f1 = *chan->f2;
+	*Z1_F1(chan) = *Z2_F1(chan);
+
+	if (chan->chan->status == open_voice) {
+		// Make sure that at least hfc_TX_FIFO_PRELOAD bytes are
+		// present in the TX FIFOs
+
+		// Create hfc_TX_FIFO_PRELOAD bytes of empty data
+		// (0x7f is mute audio)
+		u8 empty_fifo[hfc_TX_FIFO_PRELOAD + ZT_CHUNKSIZE + hfc_RX_FIFO_PRELOAD];
+		memset(empty_fifo, 0x7f, sizeof(empty_fifo));
+
+		hfc_fifo_put(chan, empty_fifo, sizeof(empty_fifo));
+	}
+}
+

Added: zaptel/trunk/vzaphfc/fifo.h
===================================================================
--- zaptel/trunk/vzaphfc/fifo.h	2006-06-17 13:14:33 UTC (rev 1910)
+++ zaptel/trunk/vzaphfc/fifo.h	2006-06-17 14:06:37 UTC (rev 1911)
@@ -0,0 +1,131 @@
+/*
+ * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards
+ *
+ * Copyright (C) 2004 Daniele Orlandi
+ * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH
+ *
+ * Daniele "Vihai" Orlandi <daniele at orlandi.com> 
+ *
+ * Major rewrite of the driver made by
+ * Klaus-Peter Junghanns <kpj at junghanns.net>
+ *
+ * This program is free software and may be modified and
+ * distributed under the terms of the GNU Public License.
+ *
+ */
+
+#ifndef _HFC_FIFO_H
+#define _HFC_FIFO_H
+
+#include "vzaphfc.h"
+
+static inline u16 *Z1_F1(struct hfc_chan_simplex *chan)
+{
+	return chan->z1_base + (*chan->f1 * 4);
+}
+
+static inline u16 *Z2_F1(struct hfc_chan_simplex *chan)
+{
+	return chan->z2_base + (*chan->f1 * 4);
+}
+
+static inline u16 *Z1_F2(struct hfc_chan_simplex *chan)
+{
+	return chan->z1_base + (*chan->f2 * 4);
+}
+
+static inline u16 *Z2_F2(struct hfc_chan_simplex *chan)
+{
+	return chan->z2_base + (*chan->f2 * 4);
+}
+
+static inline u16 Z_inc(struct hfc_chan_simplex *chan, u16 z, u16 inc)
+{
+	// declared as u32 in order to manage overflows
+	u32 newz = z + inc;
+	if (newz > chan->z_max)
+		newz -= chan->fifo_size;
+
+	return newz;
+}
+
+static inline u8 F_inc(struct hfc_chan_simplex *chan, u8 f, u8 inc)
+{
+	// declared as u16 in order to manage overflows
+	u16 newf = f + inc;
+	if (newf > chan->f_max)
+		newf -= chan->f_num;
+
+	return newf;
+}
+
+static inline u16 hfc_fifo_used_rx(struct hfc_chan_simplex *chan)
+{
+	return (*Z1_F2(chan) - *Z2_F2(chan) + chan->fifo_size) % chan->fifo_size;
+}
+
+static inline u16 hfc_fifo_get_frame_size(struct hfc_chan_simplex *chan)
+{
+ // This +1 is needed because in frame mode the available bytes are Z2-Z1+1
+ // while in transparent mode I wouldn't consider the byte pointed by Z2 to
+ // be available, otherwise, the FIFO would always contain one byte, even
+ // when Z1==Z2
+
+	return hfc_fifo_used_rx(chan) + 1;
+}
+
+static inline u8 hfc_fifo_u8(struct hfc_chan_simplex *chan, u16 z)
+{
+	return *((u8 *)(chan->z_base + z));
+}
+
+static inline u16 hfc_fifo_used_tx(struct hfc_chan_simplex *chan)
+{
+	return (*Z1_F1(chan) - *Z2_F1(chan) + chan->fifo_size) % chan->fifo_size;
+}
+
+static inline u16 hfc_fifo_free_rx(struct hfc_chan_simplex *chan)
+{
+	u16 free_bytes=*Z2_F1(chan) - *Z1_F1(chan);
+
+	if (free_bytes > 0)
+		return free_bytes;
+	else
+		return free_bytes + chan->fifo_size;
+}
+
+static inline u16 hfc_fifo_free_tx(struct hfc_chan_simplex *chan)
+{
+	u16 free_bytes=*Z2_F1(chan) - *Z1_F1(chan);
+
+	if (free_bytes > 0)
+		return free_bytes;
+	else
+		return free_bytes + chan->fifo_size;
+}
+
+static inline int hfc_fifo_has_frames(struct hfc_chan_simplex *chan)
+{
+	return *chan->f1 != *chan->f2;
+}
+
+static inline u8 hfc_fifo_used_frames(struct hfc_chan_simplex *chan)
+{
+	return (*chan->f1 - *chan->f2 + chan->f_num) % chan->f_num;
+}
+
+static inline u8 hfc_fifo_free_frames(struct hfc_chan_simplex *chan)
+{
+	return (*chan->f2 - *chan->f1 + chan->f_num) % chan->f_num;
+}
+
+int hfc_fifo_get(struct hfc_chan_simplex *chan, void *data, int size);
+void hfc_fifo_put(struct hfc_chan_simplex *chan, void *data, int size);
+void hfc_fifo_drop(struct hfc_chan_simplex *chan, int size);
+int hfc_fifo_get_frame(struct hfc_chan_simplex *chan, void *data, int max_size);
+void hfc_fifo_drop_frame(struct hfc_chan_simplex *chan);
+void hfc_fifo_put_frame(struct hfc_chan_simplex *chan, void *data, int size);
+void hfc_clear_fifo_rx(struct hfc_chan_simplex *chan);
+void hfc_clear_fifo_tx(struct hfc_chan_simplex *chan);
+
+#endif

Added: zaptel/trunk/vzaphfc/lapd.c
===================================================================
--- zaptel/trunk/vzaphfc/lapd.c	2006-06-17 13:14:33 UTC (rev 1910)
+++ zaptel/trunk/vzaphfc/lapd.c	2006-06-17 14:06:37 UTC (rev 1911)
@@ -0,0 +1,45 @@
+/*
+ * lapd.c
+ *
+ * This program is free software and may be modified and
+ * distributed under the terms of the GNU Public License.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+
+#include "lapd.h"
+
+static int lapd_change_mtu(struct net_device *dev, int mtu)
+{
+	return -EINVAL;
+}
+
+static int lapd_mac_addr(struct net_device *dev, void *addr)
+{
+	return -EINVAL;
+}
+
+void setup_lapd(struct net_device *netdev)
+{
+
+	netdev->change_mtu         = lapd_change_mtu;
+	netdev->hard_header        = NULL;
+	netdev->rebuild_header     = NULL;
+	netdev->set_mac_address    = lapd_mac_addr;
+	netdev->hard_header_cache  = NULL;
+	netdev->header_cache_update= NULL;
+
+	netdev->type               = ARPHRD_LAPD;
+	netdev->hard_header_len    = 0;
+	netdev->mtu                = 512;
+	netdev->addr_len           = 0;
+	netdev->tx_queue_len       = 10;
+
+	memset(netdev->broadcast, 0x00, sizeof(netdev->broadcast));
+
+	netdev->flags              = IFF_NOARP;
+}
+
+

Added: zaptel/trunk/vzaphfc/lapd.h
===================================================================
--- zaptel/trunk/vzaphfc/lapd.h	2006-06-17 13:14:33 UTC (rev 1910)
+++ zaptel/trunk/vzaphfc/lapd.h	2006-06-17 14:06:37 UTC (rev 1911)
@@ -0,0 +1,26 @@
+/*
+ * lapd.c - net_device LAPD link layer support functionss
+ *
+ * Copyright (C) 2004 Daniele Orlandi
+ *
+ * Daniele "Vihai" Orlandi <daniele at orlandi.com> 
+ *
+ * This program is free software and may be modified and
+ * distributed under the terms of the GNU Public License.
+ *
+ */
+
+#ifndef _HFC_LAPD_H
+#define _HFC_LAPD_H
+
+#ifndef ARPHRD_LAPD
+#define ARPHRD_LAPD 1000	/* LAPD pseudo type */
+#endif
+
+#ifndef ETH_P_LAPD
+#define ETH_P_LAPD 0x0030	/* LAPD pseudo type */
+#endif
+
+void setup_lapd(struct net_device *netdev);
+
+#endif

Added: zaptel/trunk/vzaphfc/vzaphfc.h
===================================================================
--- zaptel/trunk/vzaphfc/vzaphfc.h	2006-06-17 13:14:33 UTC (rev 1910)
+++ zaptel/trunk/vzaphfc/vzaphfc.h	2006-06-17 14:06:37 UTC (rev 1911)
@@ -0,0 +1,387 @@
+/*
+ * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards
+ *
+ * Copyright (C) 2006 headissue GmbH; Jens Wilke
+ * Copyright (C) 2004 Daniele Orlandi
+ * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH
+ *
+ * Jens Wilke <jw_vzaphfc at headissue.com>
+ * 
+ * Orginal author of this code is
+ * Daniele "Vihai" Orlandi <daniele at orlandi.com> 
+ *
+ * Major rewrite of the driver made by
+ * Klaus-Peter Junghanns <kpj at junghanns.net>
+ *
+ * This program is free software and may be modified and
+ * distributed under the terms of the GNU Public License.
+ *
+ */
+
+#ifndef _HFC_ZAPHFC_H
+#define _HFC_ZAPHFC_H
+
+#include <asm/io.h>
+#include <linux/netdevice.h>
+
+#include "zaptel.h"
+
+#define hfc_DRIVER_NAME "vzaphfc"
+#define hfc_DRIVER_PREFIX hfc_DRIVER_NAME ": "
+#define hfc_DRIVER_DESCR "HFC-S PCI A Zaptel Driver"
+#define hfc_DRIVER_VERSION "1.42"
+#define hfc_DRIVER_STRING hfc_DRIVER_DESCR " (V" hfc_DRIVER_VERSION ")"
+
+#define hfc_MAX_BOARDS 32
+
+#ifndef LINUX26
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define irqreturn_t void
+#endif
+
+#ifndef PCI_DMA_32BIT
+#define PCI_DMA_32BIT	0x00000000ffffffffULL
+#endif
+
+#ifndef PCI_VENDOR_ID_SITECOM
+#define PCI_VENDOR_ID_SITECOM 0x182D
+#endif
+
+#ifndef PCI_DEVICE_ID_SITECOM_3069
+#define PCI_DEVICE_ID_SITECOM_3069 0x3069
+#endif
+
+#define hfc_RESET_DELAY 20
+
+#define hfc_CLKDEL_TE	0x0f	/* CLKDEL in TE mode */
+#define hfc_CLKDEL_NT	0x6c	/* CLKDEL in NT mode */
+
+/* PCI memory mapped I/O */
+
+#define hfc_PCI_MEM_SIZE	0x0100
+#define hfc_PCI_MWBA		0x80
+
+/* GCI/IOM bus monitor registers */
+
+#define hfc_C_I       0x08
+#define hfc_TRxR      0x0C
+#define hfc_MON1_D    0x28
+#define hfc_MON2_D    0x2C
+
+
+/* GCI/IOM bus timeslot registers */
+
+#define hfc_B1_SSL    0x80
+#define hfc_B2_SSL    0x84
+#define hfc_AUX1_SSL  0x88
+#define hfc_AUX2_SSL  0x8C
+#define hfc_B1_RSL    0x90
+#define hfc_B2_RSL    0x94
+#define hfc_AUX1_RSL  0x98
+#define hfc_AUX2_RSL  0x9C
+
+/* GCI/IOM bus data registers */
+
+#define hfc_B1_D      0xA0
+#define hfc_B2_D      0xA4
+#define hfc_AUX1_D    0xA8
+#define hfc_AUX2_D    0xAC
+
+/* GCI/IOM bus configuration registers */
+
+#define hfc_MST_EMOD  0xB4
+#define hfc_MST_MODE	 0xB8
+#define hfc_CONNECT 	 0xBC
+
+
+/* Interrupt and status registers */
+
+#define hfc_FIFO_EN   0x44
+#define hfc_TRM       0x48
+#define hfc_B_MODE    0x4C
+#define hfc_CHIP_ID   0x58
+#define hfc_CIRM  	 0x60
+#define hfc_CTMT	 0x64
+#define hfc_INT_M1  	 0x68
+#define hfc_INT_M2  	 0x6C
+#define hfc_INT_S1  	 0x78
+#define hfc_INT_S2  	 0x7C
+#define hfc_STATUS  	 0x70
+
+/* S/T section registers */
+
+#define hfc_STATES  	 0xC0
+#define hfc_SCTRL  	 0xC4
+#define hfc_SCTRL_E   0xC8
+#define hfc_SCTRL_R   0xCC
+#define hfc_SQ  	 0xD0
+#define hfc_CLKDEL  	 0xDC
+#define hfc_B1_REC    0xF0
+#define hfc_B1_SEND   0xF0
+#define hfc_B2_REC    0xF4
+#define hfc_B2_SEND   0xF4
+#define hfc_D_REC     0xF8
+#define hfc_D_SEND    0xF8
+#define hfc_E_REC     0xFC
+
+/* Bits and values in various HFC PCI registers */
+
+/* bits in status register (READ) */
+#define hfc_STATUS_PCI_PROC   0x02
+#define hfc_STATUS_NBUSY	  0x04 
+#define hfc_STATUS_TIMER_ELAP 0x10
+#define hfc_STATUS_STATINT	  0x20
+#define hfc_STATUS_FRAMEINT	  0x40
+#define hfc_STATUS_ANYINT	  0x80
+
+/* bits in CTMT (Write) */
+#define hfc_CTMT_TRANSB1	0x01
+#define hfc_CTMT_TRANSB2	0x02
+#define hfc_CTMT_TIMER_CLEAR	0x80
+#define hfc_CTMT_TIMER_MASK	0x1C
+#define hfc_CTMT_TIMER_3_125	(0x01 << 2)
+#define hfc_CTMT_TIMER_6_25	(0x02 << 2)
+#define hfc_CTMT_TIMER_12_5	(0x03 << 2)
+#define hfc_CTMT_TIMER_25	(0x04 << 2)
+#define hfc_CTMT_TIMER_50	(0x05 << 2)
+#define hfc_CTMT_TIMER_400	(0x06 << 2)
+#define hfc_CTMT_TIMER_800	(0x07 << 2)
+#define hfc_CTMT_AUTO_TIMER	0x20
+
+/* bits in CIRM (Write) */
+#define hfc_CIRM_AUX_MSK    0x07
+#define hfc_CIRM_RESET  	  0x08
+#define hfc_CIRM_B1_REV     0x40
+#define hfc_CIRM_B2_REV     0x80
+
+/* bits in INT_M1 and INT_S1 */
+#define hfc_INTS_B1TRANS  0x01
+#define hfc_INTS_B2TRANS  0x02
+#define hfc_INTS_DTRANS   0x04
+#define hfc_INTS_B1REC    0x08
+#define hfc_INTS_B2REC    0x10
+#define hfc_INTS_DREC     0x20
+#define hfc_INTS_L1STATE  0x40
+#define hfc_INTS_TIMER    0x80
+
+/* bits in INT_M2 */
+#define hfc_M2_PROC_TRANS    0x01
+#define hfc_M2_GCI_I_CHG     0x02
+#define hfc_M2_GCI_MON_REC   0x04
+#define hfc_M2_IRQ_ENABLE    0x08
+#define hfc_M2_PMESEL        0x80
+
+/* bits in STATES */
+#define hfc_STATES_STATE_MASK     0x0F
+#define hfc_STATES_LOAD_STATE    0x10
+#define hfc_STATES_ACTIVATE	     0x20
+#define hfc_STATES_DO_ACTION     0x40
+#define hfc_STATES_NT_G2_G3      0x80
+
+/* bits in HFCD_MST_MODE */
+#define hfc_MST_MODE_MASTER	     0x01
+#define hfc_MST_MODE_SLAVE         0x00
+/* remaining bits are for codecs control */
+
+/* bits in HFCD_SCTRL */
+#define hfc_SCTRL_B1_ENA	     0x01
+#define hfc_SCTRL_B2_ENA	     0x02
+#define hfc_SCTRL_MODE_TE        0x00
+#define hfc_SCTRL_MODE_NT        0x04
+#define hfc_SCTRL_LOW_PRIO	     0x08
+#define hfc_SCTRL_SQ_ENA	     0x10
+#define hfc_SCTRL_TEST	     0x20
+#define hfc_SCTRL_NONE_CAP	     0x40
+#define hfc_SCTRL_PWR_DOWN	     0x80
+
+/* bits in SCTRL_E  */
+#define hfc_SCTRL_E_AUTO_AWAKE    0x01
+#define hfc_SCTRL_E_DBIT_1        0x04
+#define hfc_SCTRL_E_IGNORE_COL    0x08
+#define hfc_SCTRL_E_CHG_B1_B2     0x80
+
+/* bits in SCTRL_R  */
+#define hfc_SCTRL_R_B1_ENA	     0x01
+#define hfc_SCTRL_R_B2_ENA	     0x02
+
+/* bits in FIFO_EN register */
+#define hfc_FIFOEN_B1TX   0x01
+#define hfc_FIFOEN_B1RX   0x02
+#define hfc_FIFOEN_B2TX   0x04
+#define hfc_FIFOEN_B2RX   0x08
+#define hfc_FIFOEN_DTX    0x10
+#define hfc_FIFOEN_DRX    0x20
+
+#define hfc_FIFOEN_B1     (hfc_FIFOEN_B1TX|hfc_FIFOEN_B1RX)
+#define hfc_FIFOEN_B2     (hfc_FIFOEN_B2TX|hfc_FIFOEN_B2RX)
+#define hfc_FIFOEN_D      (hfc_FIFOEN_DTX|hfc_FIFOEN_DRX)
+
+/* bits in the CONNECT register */
+#define	hfc_CONNECT_B1_HFC_from_ST		0x00
+#define	hfc_CONNECT_B1_HFC_from_GCI		0x01
+#define hfc_CONNECT_B1_ST_from_HFC		0x00
+#define hfc_CONNECT_B1_ST_from_GCI		0x02
+#define hfc_CONNECT_B1_GCI_from_HFC		0x00
+#define hfc_CONNECT_B1_GCI_from_ST		0x04
+
+#define	hfc_CONNECT_B2_HFC_from_ST		0x00
+#define	hfc_CONNECT_B2_HFC_from_GCI		0x08
+#define hfc_CONNECT_B2_ST_from_HFC		0x00
+#define hfc_CONNECT_B2_ST_from_GCI		0x10
+#define hfc_CONNECT_B2_GCI_from_HFC		0x00
+#define hfc_CONNECT_B2_GCI_from_ST		0x20
+
+/* bits in the TRM register */
+#define hfc_TRM_TRANS_INT_00	0x00
+#define hfc_TRM_TRANS_INT_01	0x01
+#define hfc_TRM_TRANS_INT_10	0x02
+#define hfc_TRM_TRANS_INT_11	0x04
+#define hfc_TRM_ECHO		0x20
+#define hfc_TRM_B1_PLUS_B2	0x40
+#define hfc_TRM_IOM_TEST_LOOP	0x80
+
+/* bits in the __SSL and __RSL registers */
+#define	hfc_SRSL_STIO		0x40
+#define hfc_SRSL_ENABLE		0x80
+#define hfc_SRCL_SLOT_MASK	0x1f
+
+/* FIFO memory definitions */
+
+#define hfc_FIFO_SIZE   0x8000
+
+#define hfc_UGLY_FRAMEBUF 0x2000
+
+#define hfc_TX_FIFO_PRELOAD ZT_CHUNKSIZE + 2
+#define hfc_RX_FIFO_PRELOAD 4
+
+/* NOTE: FIFO pointers are not declared volatile because accesses to the
+ *       FIFOs are inherently safe.
+ */
+
+#ifdef DEBUG
+extern int debug_level;
+#endif
+
+struct hfc_chan;
+
+struct hfc_chan_simplex {
+	struct hfc_chan_duplex *chan;
+
+	u8 zaptel_buffer[ZT_CHUNKSIZE];
+
+	u8 ugly_framebuf[hfc_UGLY_FRAMEBUF];
+	int ugly_framebuf_size;
+	u16 ugly_framebuf_off;
+
+	void *z1_base,*z2_base;
+	void *fifo_base;
+	void *z_base;
+	u16 z_min;
+	u16 z_max;
+	u16 fifo_size;
+
+	u8 *f1,*f2;
+	u8 f_min;
+	u8 f_max;
+	u8 f_num;
+	
+	unsigned long long frames;
+	unsigned long long bytes;
+	unsigned long long fifo_full;
+	unsigned long long crc;
+	unsigned long long fifo_underrun;
+};
+
+enum hfc_chan_status {
+	free,
+	open_framed,
+	open_voice,
+	sniff_aux,
+	loopback,
+};
+
+struct hfc_chan_duplex {
+	struct hfc_card *card;
+
+	char *name;
+	int number;
+
+	enum hfc_chan_status status;
+	int open_by_netdev;
+	int open_by_zaptel;
+
+	unsigned short protocol;
+
+	spinlock_t lock;
+
+	struct hfc_chan_simplex rx;
+	struct hfc_chan_simplex tx;
+
+	struct net_device *netdev;
+	struct net_device_stats net_device_stats;
+};
+
+typedef struct hfc_card {
+	int cardnum;
+	spinlock_t lock;
+
+	int ticks;
+
+	struct pci_dev *pcidev;
+
+	struct proc_dir_entry *proc_dir;
+	char proc_dir_name[32];
+
+	struct proc_dir_entry *proc_info;
+	struct proc_dir_entry *proc_fifos;
+	struct proc_dir_entry *proc_bufs;
+
+	unsigned long io_bus_mem;
+	void *io_mem;
+
+	dma_addr_t fifo_bus_mem;
+	void *fifo_mem;
+	void *fifos;
+
+	int nt_mode;
+	int sync_loss_reported;
+	int late_irqs;
+
+	u8 l1_state;
+	int fifo_suspended;
+	int ignore_first_timer_interrupt;
+
+	struct {
+		u8 m1;
+		u8 m2;
+		u8 fifo_en;
+		u8 trm;
+		u8 connect;
+		u8 sctrl;
+		u8 sctrl_r;
+		u8 sctrl_e;
+		u8 ctmt;
+		u8 cirm;
+	} regs;
+
+	struct hfc_chan_duplex chans[3];
+	int echo_enabled;
+
+	struct zt_span zt_span;
+	struct zt_chan zt_chans[3];
+
+	int debug_event;
+} hfc_card;
+
+static inline u8 hfc_inb(struct hfc_card *card, int offset)
+{
+ return readb(card->io_mem + offset);
+}
+
+static inline void hfc_outb(struct hfc_card *card, int offset, u8 value)
+{
+ writeb(value, card->io_mem + offset);
+}
+
+#endif

Added: zaptel/trunk/vzaphfc/vzaphfc_main.c
===================================================================
--- zaptel/trunk/vzaphfc/vzaphfc_main.c	2006-06-17 13:14:33 UTC (rev 1910)
+++ zaptel/trunk/vzaphfc/vzaphfc_main.c	2006-06-17 14:06:37 UTC (rev 1911)
@@ -0,0 +1,2024 @@
+/*
+ * zaphfc.c - Zaptel driver for HFC-S PCI A based ISDN BRI cards
+ *
+ * Copyright (C) 2006, headiisue GmbH; Jens Wilke
+ * Copyright (C) 2004 Daniele Orlandi
+ * Copyright (C) 2002, 2003, 2004, Junghanns.NET GmbH
+ *
+ * Jens Wilke <jw_vzaphfc at headissue.com>
+ *
+ * Original author of this code is
+ * Daniele "Vihai" Orlandi <daniele at orlandi.com> 
+ *
+ * Major rewrite of the driver made by
+ * Klaus-Peter Junghanns <kpj at junghanns.net>
+ *
+ * This program is free software and may be modified and
+ * distributed under the terms of the GNU Public License.
+ *
+ * Please read the README file for important infos.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+
+#include <zaptel.h>
+
+#include "vzaphfc.h"
+#include "fifo.h"
+#include "lapd.h"
+
+#if CONFIG_PCI
+
+#define ZT_B1 0
+#define ZT_B2 1
+#define ZT_D 2
+
+#define D 0
+#define B1 1
+#define B2 2
+
+static int modes = 0; // all TE
+static int nt_modes[hfc_MAX_BOARDS];
+static int nt_modes_count;
+static int force_l1_up = 0;
+static int sniff_zaptel_d_channel = 0;
+static struct proc_dir_entry *hfc_proc_zaphfc_dir;
+
+#ifdef DEBUG
+int debug_level = 0;
+#endif
+
+static struct pci_device_id hfc_pci_ids[] = {
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_3069,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, hfc_pci_ids);
+
+static int __devinit hfc_probe(struct pci_dev *dev
+			, const struct pci_device_id *ent);
+static void __devexit hfc_remove(struct pci_dev *dev);
+
+struct pci_driver hfc_driver = {
+	.name     = hfc_DRIVER_NAME,
+	.id_table = hfc_pci_ids,
+	.probe    = hfc_probe,
+	.remove   = hfc_remove,
+};
+
+/******************************************
+ * HW routines
+ ******************************************/
+
+static void hfc_softreset(struct hfc_card *card)
+{
+	printk(KERN_INFO hfc_DRIVER_PREFIX
+		"card %d: "
+		"resetting\n",
+		card->cardnum);
+
+	hfc_outb(card, hfc_CIRM, hfc_CIRM_RESET);	// softreset on
+	udelay(6);	  // wait at least 5.21us
+	hfc_outb(card, hfc_CIRM, 0);	// softreset off
+
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout((hfc_RESET_DELAY * HZ) / 1000);	// wait 20 ms
+}
+
+void hfc_resetCard(struct hfc_card *card)
+{
+	card->regs.m1 = 0;
+	hfc_outb(card, hfc_INT_M1, card->regs.m1);	// no ints
+
+	card->regs.m2 = 0;
+	hfc_outb(card, hfc_INT_M2, card->regs.m2);	// not at all
+
+	hfc_softreset(card);
+
+	card->regs.trm = 0;
+	hfc_outb(card, hfc_TRM, card->regs.trm);
+
+	// Select the non-capacitive line mode for the S/T interface */
+	card->regs.sctrl = hfc_SCTRL_NONE_CAP;
+
+	if (card->nt_mode) {
+		// ST-Bit delay for NT-Mode
+		hfc_outb(card, hfc_CLKDEL, hfc_CLKDEL_NT);
+
+		card->regs.sctrl |= hfc_SCTRL_MODE_NT;
+	} else {
+		// ST-Bit delay for TE-Mode
+		hfc_outb(card, hfc_CLKDEL, hfc_CLKDEL_TE);
+
+		card->regs.sctrl |= hfc_SCTRL_MODE_TE;
+	}
+
+	hfc_outb(card, hfc_SCTRL, card->regs.sctrl);
+
+	// S/T Auto awake
+	card->regs.sctrl_e = hfc_SCTRL_E_AUTO_AWAKE;
+	hfc_outb(card, hfc_SCTRL_E, card->regs.sctrl_e);
+
+	// No B-channel enabled at startup
+	card->regs.sctrl_r = 0;
+	hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r);
+
+	// HFC Master Mode
+	hfc_outb(card, hfc_MST_MODE, hfc_MST_MODE_MASTER);
+
+	// Connect internal blocks
+	card->regs.connect =
+		hfc_CONNECT_B1_HFC_from_ST |
+		hfc_CONNECT_B1_ST_from_HFC |
+		hfc_CONNECT_B1_GCI_from_HFC |
+		hfc_CONNECT_B2_HFC_from_ST |
+		hfc_CONNECT_B2_ST_from_HFC |
+		hfc_CONNECT_B2_GCI_from_HFC;
+	hfc_outb(card, hfc_CONNECT, card->regs.connect);
+
+	// All bchans are HDLC by default, not useful, actually
+	// since mode is set during open()
+	hfc_outb(card, hfc_CTMT, 0);
+
+	// bit order
+	hfc_outb(card, hfc_CIRM, 0);
+
+	// Enable D-rx FIFO. At least one FIFO must be enabled (by specs)
+	card->regs.fifo_en = hfc_FIFOEN_DRX;
+	hfc_outb(card, hfc_FIFO_EN, card->regs.fifo_en);
+
+	card->late_irqs=0;
+
+	// Clear already pending ints
+	hfc_inb(card, hfc_INT_S1);
+	hfc_inb(card, hfc_INT_S2);
+
+	// Enable IRQ output
+	card->regs.m1 = hfc_INTS_DREC | hfc_INTS_L1STATE | hfc_INTS_TIMER;
+	hfc_outb(card, hfc_INT_M1, card->regs.m1);
+
+	card->regs.m2 = hfc_M2_IRQ_ENABLE;
+	hfc_outb(card, hfc_INT_M2, card->regs.m2);
+
+	// Unlocks the states machine
+	hfc_outb(card, hfc_STATES, 0);
+
+	// There's no need to explicitly activate L1 now.
+	// Activation is managed inside the interrupt routine.
+}
+
+static void hfc_update_fifo_state(struct hfc_card *card)
+{
+	// I'm not sure if irqsave is needed but there could be a race
+	// condition since hfc_update_fifo_state could be called from
+	// both the IRQ handler and the *_(open|close) functions
+
+	unsigned long flags;
+	spin_lock_irqsave(&card->chans[B1].lock, flags);
+	if (!card->fifo_suspended &&
+		(card->chans[B1].status == open_framed ||
+		card->chans[B1].status == open_voice)) {
+
+ 	 	if(!(card->regs.fifo_en & hfc_FIFOEN_B1RX)) {
+			card->regs.fifo_en |= hfc_FIFOEN_B1RX;
+			hfc_clear_fifo_rx(&card->chans[B1].rx);
+		}
+
+ 	 	if(!(card->regs.fifo_en & hfc_FIFOEN_B1TX)) {
+			card->regs.fifo_en |= hfc_FIFOEN_B1TX;
+			hfc_clear_fifo_tx(&card->chans[B1].tx);
+		}
+	} else {
+ 	 	if(card->regs.fifo_en & hfc_FIFOEN_B1RX)
+			card->regs.fifo_en &= ~hfc_FIFOEN_B1RX;
+ 	 	if(card->regs.fifo_en & hfc_FIFOEN_B1TX)
+			card->regs.fifo_en &= ~hfc_FIFOEN_B1TX;
+	}
+	spin_unlock_irqrestore(&card->chans[B1].lock, flags);
+
+	spin_lock_irqsave(&card->chans[B2].lock, flags);
+	if (!card->fifo_suspended &&
+		(card->chans[B2].status == open_framed ||
+		card->chans[B2].status == open_voice ||
+		card->chans[B2].status == sniff_aux)) {
+
+ 	 	if(!(card->regs.fifo_en & hfc_FIFOEN_B2RX)) {
+			card->regs.fifo_en |= hfc_FIFOEN_B2RX;
+			hfc_clear_fifo_rx(&card->chans[B2].rx);
+		}
+
+ 	 	if(!(card->regs.fifo_en & hfc_FIFOEN_B2TX)) {
+			card->regs.fifo_en |= hfc_FIFOEN_B2TX;
+			hfc_clear_fifo_tx(&card->chans[B2].tx);
+		}
+	} else {
+ 	 	if(card->regs.fifo_en & hfc_FIFOEN_B2RX)
+			card->regs.fifo_en &= ~hfc_FIFOEN_B2RX;
+ 	 	if(card->regs.fifo_en & hfc_FIFOEN_B2TX)
+			card->regs.fifo_en &= ~hfc_FIFOEN_B2TX;
+	}
+	spin_unlock_irqrestore(&card->chans[B2].lock, flags);
+
+	spin_lock_irqsave(&card->chans[D].lock, flags);
+	if (!card->fifo_suspended &&
+		card->chans[D].status == open_framed) {
+
+// 	 	if(!(card->regs.fifo_en & hfc_FIFOEN_DRX)) {
+//			hfc_clear_fifo_rx(&card->chans[B2].rx);
+//
+//			card->chans[B2].rx.ugly_framebuf_size = 0;
+//			card->chans[B2].rx.ugly_framebuf_off = 0;
+//		}
+//
+ 	 	if(!(card->regs.fifo_en & hfc_FIFOEN_DTX)) {
+			card->regs.fifo_en |= hfc_FIFOEN_DTX;
+
+			card->chans[D].tx.ugly_framebuf_size = 0;
+			card->chans[D].tx.ugly_framebuf_off = 0;
+		}
+	} else {
+// 	 	if(card->regs.fifo_en & hfc_FIFOEN_DRX)
+//			card->regs.fifo_en &= ~hfc_FIFOEN_DRX;
+ 	 	if(card->regs.fifo_en & hfc_FIFOEN_DTX)
+			card->regs.fifo_en &= ~hfc_FIFOEN_DTX;
+	}
+	spin_unlock_irqrestore(&card->chans[D].lock, flags);
+
+	hfc_outb(card, hfc_FIFO_EN, card->regs.fifo_en);
+}
+
+static inline void hfc_suspend_fifo(struct hfc_card *card)
+{
+	card->fifo_suspended = TRUE;
+
+	hfc_update_fifo_state(card);
+
+	// When L1 goes down D rx receives garbage; it is nice to
+	// clear it to avoid a CRC error on reactivation
+	// udelay is needed because the FIFO deactivation happens
+	// in 250us
+	udelay(250);
+	hfc_clear_fifo_rx(&card->chans[D].rx);
+
+#ifdef DEBUG
+	if (debug_level >= 3) {
+		printk(KERN_DEBUG hfc_DRIVER_PREFIX
+			"card %d: "
+			"FIFOs suspended\n",
+			card->cardnum);
+	}
+#endif
+}
+
+static inline void hfc_resume_fifo(struct hfc_card *card)
+{
+	card->fifo_suspended = FALSE;
+
+	hfc_update_fifo_state(card);
+
+#ifdef DEBUG
+	if (debug_level >= 3) {
+		printk(KERN_DEBUG hfc_DRIVER_PREFIX
+			"card %d: "
+			"FIFOs resumed\n",
+			card->cardnum);
+	}
+#endif
+}
+
+static void hfc_check_l1_up(struct hfc_card *card)
+{
+	if ((!card->nt_mode && card->l1_state != 7) ||
+		(card->nt_mode && card->l1_state != 3)) {
+// 0 because this is quite verbose when an inferface is unconnected, jaw		
+#if 0
+		if(debug_level >= 1) {
+			printk(KERN_DEBUG hfc_DRIVER_PREFIX
+				"card %d: "
+				"L1 is down, bringing up L1.\n",
+				card->cardnum);
+		}
+#endif
+
+       		hfc_outb(card, hfc_STATES, hfc_STATES_DO_ACTION |
+       					hfc_STATES_ACTIVATE|
+					hfc_STATES_NT_G2_G3);
+       	}
+}
+
+/******************************************
+ * /proc interface functions
+ ******************************************/
+
+struct hfc_status_to_name_names {
+	enum hfc_chan_status status;
+	char *name;
+};
+
+static char *hfc_status_to_name(int status)
+{
+	struct hfc_status_to_name_names names[] = {
+		{ free, "free" },
+		{ open_framed, "framed" },
+		{ open_voice, "voice" },
+		{ sniff_aux, "sniff aux" },
+		{ loopback, "loopback" },
+		};
+
+	int i;
+
+	for (i=0; i<sizeof(names); i++) {
+		if (names[i].status == status)
+			return names[i].name;
+	}
+
+	return "*UNK*";
+}
+
+static void hfc_hexdump(char *src,char *dst, int size)
+{
+	static char hexchars[] = "0123456789ABCDEF";
+
+	int i;
+	for(i=0;i<size;i++) {
+		dst[i*2]  = hexchars[(src[i] & 0xf0) >> 4];
+		dst[i*2+1]= hexchars[src[i] & 0xf];
+	}
+
+	dst[size*2]='\0';
+}
+
+static int hfc_proc_read_info(char *page, char **start,
+		off_t off, int count, 
+		int *eof, void *data)
+{
+	struct hfc_card *card = data;
+	int len;
+
+	u8 chip_id;
+	chip_id = hfc_inb(card, hfc_CHIP_ID);
+
+	len = snprintf(page, PAGE_SIZE,
+		"Driver    : %s\n"
+		"Cardnum   : %d\n"
+		"IRQ       : %d\n"
+		"PCI Mem   : %#08lx (0x%p)\n"
+		"FIFO Mem  : %#08lx (0x%p)\n"
+		"Mode      : %s\n"
+		"CHIP_ID   : %#02x\n"
+		"L1 State  : %c%d\n"
+		"Sync Lost : %s\n"
+		"Late IRQs : %d\n"
+		"FIFO susp : %s\n"
+		"\nChannel     %12s %12s %12s %12s %4s %4s %4s %4s %4s\n"
+		"D         : %12llu %12llu %12llu %12llu %4llu %4llu %4llu %4llu %4llu %c%c %s\n"
+		"B1        : %12llu %12llu %12llu %12llu %4llu %4llu %4llu %4llu %4llu %c%c %s\n"
+		"B2        : %12llu %12llu %12llu %12llu %4llu %4llu %4llu %4llu %4llu %c%c %s\n"
+		,hfc_DRIVER_STRING
+		,card->cardnum
+		,card->pcidev->irq
+		,card->io_bus_mem, card->io_mem
+		,(ulong)card->fifo_bus_mem, card->fifo_mem
+		,card->nt_mode?"NT":"TE"
+		,chip_id
+		,card->nt_mode?'G':'F'
+		,card->l1_state
+		,card->sync_loss_reported?"YES":"NO"
+		,card->late_irqs
+		,card->fifo_suspended?"YES":"NO"
+
+		,"RX Frames","TX Frames","RX Bytes","TX Bytes","RXFF","TXFF","RXU","TXU","CRC"
+		,card->chans[D].rx.frames
+		,card->chans[D].tx.frames
+		,card->chans[D].rx.bytes
+		,card->chans[D].tx.bytes
+		,card->chans[D].rx.fifo_full
+		,card->chans[D].tx.fifo_full
+		,card->chans[D].rx.fifo_underrun
+		,card->chans[D].tx.fifo_underrun
+		,card->chans[D].rx.crc
+		,card->chans[D].open_by_netdev ? 'N' : ' '
+		,card->chans[D].open_by_zaptel ? 'Z' : ' '
+		,hfc_status_to_name(card->chans[D].status)
+
+		,card->chans[B1].rx.frames
+		,card->chans[B1].tx.frames
+		,card->chans[B1].rx.bytes
+		,card->chans[B1].tx.bytes
+		,card->chans[B1].rx.fifo_full
+		,card->chans[B1].tx.fifo_full
+		,card->chans[B1].rx.fifo_underrun
+		,card->chans[B1].tx.fifo_underrun
+		,card->chans[B1].rx.crc
+		,card->chans[B1].open_by_netdev ? 'N' : ' '
+		,card->chans[B1].open_by_zaptel ? 'Z' : ' '
+		,hfc_status_to_name(card->chans[B1].status)
+
+		,card->chans[B2].rx.frames
+		,card->chans[B2].tx.frames
+		,card->chans[B2].rx.bytes
+		,card->chans[B2].tx.bytes
+		,card->chans[B2].rx.fifo_full
+		,card->chans[B2].tx.fifo_full
+		,card->chans[B2].rx.fifo_underrun
+		,card->chans[B2].tx.fifo_underrun
+		,card->chans[B2].rx.crc
+		,card->chans[B2].open_by_netdev ? 'N' : ' '
+		,card->chans[B2].open_by_zaptel ? 'Z' : ' '
+		,hfc_status_to_name(card->chans[B2].status)
+		);
+
+	return len;
+}
+
+static int hfc_proc_read_fifos(char *page, char **start,
+		off_t off, int count, 
+		int *eof, void *data)
+{
+	struct hfc_card *card = data;
+
+	int len;
+	len = snprintf(page, PAGE_SIZE,
+		"            Receive                Transmit\n"
+		"Channel     F1 F2   Z1   Z2 Used   F1 F2   Z1   Z2 Used\n"
+		"D         : %02x %02x %04x %04x %4d   %02x %02x %04x %04x %4d\n"
+		"B1        : %02x %02x %04x %04x %4d   %02x %02x %04x %04x %4d\n"
+		"B2        : %02x %02x %04x %04x %4d   %02x %02x %04x %04x %4d\n"
+		,*card->chans[D].rx.f1
+		,*card->chans[D].rx.f2
+		,*Z1_F2(&card->chans[D].rx)
+		,*Z2_F2(&card->chans[D].rx)
+		,hfc_fifo_used_rx(&card->chans[D].rx)
+		,*card->chans[D].tx.f1
+		,*card->chans[D].tx.f2
+		,*Z1_F1(&card->chans[D].tx)
+		,*Z2_F1(&card->chans[D].tx)
+		,hfc_fifo_used_tx(&card->chans[D].tx)
+
+		,*card->chans[B1].rx.f1
+		,*card->chans[B1].rx.f2
+		,*Z1_F2(&card->chans[B1].rx)
+		,*Z2_F2(&card->chans[B1].rx)
+		,hfc_fifo_used_rx(&card->chans[B1].rx)
+		,*card->chans[B1].tx.f1
+		,*card->chans[B1].tx.f2
+		,*Z1_F1(&card->chans[B1].tx)
+		,*Z2_F1(&card->chans[B1].tx)
+		,hfc_fifo_used_tx(&card->chans[B1].tx)
+
+		,*card->chans[B2].rx.f1
+		,*card->chans[B2].rx.f2
+		,*Z1_F2(&card->chans[B2].rx)
+		,*Z2_F2(&card->chans[B2].rx)
+		,hfc_fifo_used_rx(&card->chans[B2].rx)
+		,*card->chans[B2].tx.f1
+		,*card->chans[B2].tx.f2
+		,*Z1_F1(&card->chans[B2].tx)
+		,*Z2_F1(&card->chans[B2].tx)
+		,hfc_fifo_used_tx(&card->chans[B2].tx)
+		);
+
+	return len;
+}
+
+static int hfc_proc_read_bufs(char *page, char **start,
+		off_t off, int count, 
+		int *eof, void *data)
+{
+	struct hfc_card *card = data;
+	int len;
+
+	char hex_rchunk[3][ZT_CHUNKSIZE * 2 + 1];
+	char hex_wchunk[3][ZT_CHUNKSIZE * 2 + 1];
+
+	hfc_hexdump(card->chans[D].rx.zaptel_buffer, hex_rchunk[0], ZT_CHUNKSIZE);
+	hfc_hexdump(card->chans[D].tx.zaptel_buffer, hex_wchunk[0], ZT_CHUNKSIZE);
+	hfc_hexdump(card->chans[B1].rx.zaptel_buffer, hex_rchunk[1], ZT_CHUNKSIZE);
+	hfc_hexdump(card->chans[B1].tx.zaptel_buffer, hex_wchunk[1], ZT_CHUNKSIZE);
+	hfc_hexdump(card->chans[B2].rx.zaptel_buffer, hex_rchunk[2], ZT_CHUNKSIZE);
+	hfc_hexdump(card->chans[B2].tx.zaptel_buffer, hex_wchunk[2], ZT_CHUNKSIZE);
+
+	len = snprintf(page, PAGE_SIZE,
+		"Channel    Read Chunk       Write Chunk\n"
+		"D         : %16s %16s\n"
+		"B1        : %16s %16s\n"
+		"B2        : %16s %16s\n"
+		,hex_rchunk[0],hex_wchunk[0]
+		,hex_rchunk[1],hex_wchunk[1]
+		,hex_rchunk[2],hex_wchunk[2]
+		);
+
+	return len;
+}
+
+/******************************************
+ * net_device interface functions
+ ******************************************/
+
+static int hfc_open(struct net_device *netdev)
+{
+	struct hfc_chan_duplex *chan = netdev->priv;
+	struct hfc_card *card = chan->card;
+
+	spin_lock(&chan->lock);
+
+	if (chan->status != free &&
+		(chan->number != D || chan->status != open_framed)) {
+		spin_unlock(&chan->lock);
+		return -EBUSY;
+	}
+
+	chan->status = open_framed;
+	chan->open_by_netdev = TRUE;
+
+	spin_unlock(&chan->lock);
+
+	printk(KERN_INFO hfc_DRIVER_PREFIX
+		"card %d: "
+		"chan %s opened.\n",
+		card->cardnum,
+		chan->name);
+
+	return 0;
+}
+
+static int hfc_close(struct net_device *netdev)
+{
+	struct hfc_chan_duplex *chan = netdev->priv;
+	struct hfc_card *card = chan->card;
+
+	spin_lock(&chan->lock);
+
+	if (chan->status != open_framed) {
+		spin_unlock(&chan->lock);
+		return -EINVAL;
+	}
+
+	chan->status = free;
+	chan->open_by_netdev = FALSE;
+
+	spin_unlock(&chan->lock);
+
+	printk(KERN_INFO hfc_DRIVER_PREFIX
+		"card %d: "
+		"chan %s closed.\n",
+		card->cardnum,
+		chan->name);
+
+	return 0;
+}
+
+static int hfc_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct hfc_chan_duplex *chan = netdev->priv;
+	struct hfc_card *card = chan->card;
+
+	netdev->trans_start = jiffies;
+
+	hfc_check_l1_up(card);
+
+	hfc_fifo_put_frame(&chan->tx, skb->data, skb->len);
+
+	// We're not called from IRQ handler, otherwise we'd need
+	// dev_kfree_skb
+	dev_kfree_skb(skb);
+
+	return 0;
+}
+
+static struct net_device_stats *hfc_get_stats(struct net_device *netdev)
+{
+	struct hfc_chan_duplex *chan = netdev->priv;
+//	struct hfc_card *card = chan->card;
+
+	return &chan->net_device_stats;
+}
+
+static void hfc_set_multicast_list(struct net_device *netdev)
+{
+	struct hfc_chan_duplex *chan = netdev->priv;
+	struct hfc_card *card = chan->card;
+
+	spin_lock(&card->lock);
+
+        if(netdev->flags & IFF_PROMISC && !card->echo_enabled) {
+		if (card->nt_mode) {
+			printk(KERN_INFO hfc_DRIVER_PREFIX
+				"card %d "
+				"is in NT mode, not going promiscuous\n",
+				card->cardnum);
+
+			spin_unlock(&card->lock);
+			return;
+		}
+
+		if (card->chans[B2].status != free) {
+			printk(KERN_INFO hfc_DRIVER_PREFIX
+				"card %d: "
+				"chan %s: is busy, not going promiscuous\n",
+				card->cardnum,
+				card->chans[B2].name);
+
+			spin_unlock(&card->lock);
+			return;
+		}
+
+		card->regs.trm |= hfc_TRM_ECHO;
+		card->regs.m1 |= hfc_INTS_B2REC;
+		card->regs.cirm &= ~hfc_CIRM_B2_REV;
+		card->regs.sctrl &= ~hfc_SCTRL_B2_ENA;
+		card->regs.sctrl_r &= ~hfc_SCTRL_R_B2_ENA;
+		card->regs.connect |= hfc_CONNECT_B2_ST_from_GCI;
+		card->regs.ctmt &= ~hfc_CTMT_TRANSB2;
+
+		card->chans[B2].status = sniff_aux;
+
+		card->echo_enabled = TRUE;
+
+		printk(KERN_INFO hfc_DRIVER_PREFIX
+			"card %d: "
+			"chan %s entered echo mode on channel %s\n",
+			card->cardnum,
+			chan->name,
+			card->chans[B2].name);
+
+        } else if(!(netdev->flags & IFF_PROMISC) && card->echo_enabled) {
+		if (!card->echo_enabled) {
+			spin_unlock(&card->lock);
+			return;
+		}
+
+		card->regs.trm &= ~hfc_TRM_ECHO;
+		card->regs.m1 &= ~hfc_INTS_B2REC;
+		card->regs.cirm |= hfc_CIRM_B2_REV;
+		card->regs.sctrl &= ~hfc_SCTRL_B2_ENA;
+		card->regs.sctrl_r &= ~hfc_SCTRL_R_B2_ENA;
+		card->regs.connect =
+			hfc_CONNECT_B1_HFC_from_ST |
+			hfc_CONNECT_B1_ST_from_HFC |
+			hfc_CONNECT_B1_GCI_from_HFC |
+			hfc_CONNECT_B2_HFC_from_ST |
+			hfc_CONNECT_B2_ST_from_HFC |
+			hfc_CONNECT_B2_GCI_from_HFC;
+
+		card->chans[B2].status = free;
+
+		card->echo_enabled = FALSE;
+
+		printk(KERN_INFO hfc_DRIVER_PREFIX
+			"card %d: "
+			"chan %s left promiscuous mode.\n",
+			card->cardnum,
+			chan->name);
+	}
+
+	spin_unlock(&card->lock);
+
+	hfc_outb(card, hfc_TRM, card->regs.trm);
+	hfc_outb(card, hfc_CIRM, card->regs.cirm);
+	hfc_outb(card, hfc_SCTRL, card->regs.sctrl);
+	hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r);
+	hfc_outb(card, hfc_CONNECT, card->regs.connect);
+	hfc_outb(card, hfc_CTMT, card->regs.ctmt);
+
+	// Enable appropriate B receive interrupt
+	hfc_outb(card, hfc_INT_M1, card->regs.m1);
+
+	hfc_update_fifo_state(card);
+}
+
+/******************************************
+ * Zaptel interface (zaptel sucks)
+ ******************************************/
+
+static int hfc_zap_open(struct zt_chan *zaptel_chan)
+{
+	struct hfc_chan_duplex *chan = zaptel_chan->pvt;
+	struct hfc_card *card = chan->card;
+
+	spin_lock(&chan->lock);
+
+	switch (chan->number) {
+		case D:
+			if (chan->status != free &&
+				chan->status != open_framed) {
+				spin_unlock(&chan->lock);
+				return -EBUSY;
+			}
+
+			chan->status = open_framed;
+		break;
+
+		case B1:
+		case B2:
+			if (chan->status != free) {
+				spin_unlock(&chan->lock);
+				return -EBUSY;
+			}
+
+			chan->status = open_voice;
+		break;
+	}
+
+	chan->open_by_zaptel = TRUE;
+
+#ifndef LINUX26
+	MOD_INC_USE_COUNT;
+#else
+	// Zaptel is buggy
+	try_module_get(THIS_MODULE);
+#endif
+
+	spin_unlock(&chan->lock);
+
+	switch (chan->number) {
+		case D:
+		break;
+
+		case B1:
+			// B1
+			card->regs.m2 |= hfc_M2_PROC_TRANS;
+			card->regs.ctmt |= hfc_CTMT_TRANSB1; // Enable transparent mode
+			card->regs.cirm |= hfc_CIRM_B1_REV; // Reversed bit order
+			card->regs.sctrl |= hfc_SCTRL_B1_ENA; // Enable transmission
+			card->regs.sctrl_r |= hfc_SCTRL_R_B1_ENA; // Enable reception
+		break;
+
+		case B2:
+			// B2
+			card->regs.m2 |= hfc_M2_PROC_TRANS;
+			card->regs.ctmt |= hfc_CTMT_TRANSB2; // Enable transparent mode
+			card->regs.cirm |= hfc_CIRM_B2_REV; // Reversed bit order
+			card->regs.sctrl |= hfc_SCTRL_B2_ENA; // Enable transmission
+			card->regs.sctrl_r |= hfc_SCTRL_R_B2_ENA; // Enable reception
+		break;
+
+	}
+
+	// If not already enabled, enable processing transition (8KHz)
+	// interrupt
+	hfc_outb(card, hfc_INT_M2, card->regs.m2);
+	hfc_outb(card, hfc_CTMT, card->regs.ctmt);
+	hfc_outb(card, hfc_CIRM, card->regs.cirm);
+	hfc_outb(card, hfc_SCTRL, card->regs.sctrl);
+	hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r);
+
+	hfc_update_fifo_state(card);
+
+	printk(KERN_INFO hfc_DRIVER_PREFIX
+		"card %d: "
+		"chan %s opened as %s.\n",
+		card->cardnum,
+		chan->name,
+		zaptel_chan->name);
+
+	return 0;
+}
+
+static int hfc_zap_close(struct zt_chan *zaptel_chan)
+{
+	struct hfc_chan_duplex *chan = zaptel_chan->pvt;
+	struct hfc_card *card = chan->card;
+
+	if (!card) {
+		printk(KERN_CRIT hfc_DRIVER_PREFIX
+			"hfc_zap_close called with NULL card\n");
+		return -1;
+	}
+
+	spin_lock(&chan->lock);
+
+	if (chan->status == free) {
+		spin_unlock(&chan->lock);
+		return -EINVAL;
+	}
+
+	chan->status = free;
+	chan->open_by_zaptel = FALSE;
+
+	spin_unlock(&chan->lock);
+
+	switch (chan->number) {
+		case D:
+		break;
+
+		case B1:
+			// B1
+			card->regs.ctmt &= ~hfc_CTMT_TRANSB1;
+			card->regs.cirm &= ~hfc_CIRM_B1_REV;
+			card->regs.sctrl &= ~hfc_SCTRL_B1_ENA;
+			card->regs.sctrl_r &= ~hfc_SCTRL_R_B1_ENA;
+		break;
+
+		case B2:
+			// B2
+			card->regs.ctmt &= ~hfc_CTMT_TRANSB2;
+			card->regs.cirm &= ~hfc_CIRM_B2_REV;
+			card->regs.sctrl &= ~hfc_SCTRL_B2_ENA;
+			card->regs.sctrl_r &= ~hfc_SCTRL_R_B2_ENA;
+		break;
+	}
+
+	if (card->chans[B1].status == free &&
+		card->chans[B2].status == free)
+		card->regs.m2 &= ~hfc_M2_PROC_TRANS;
+
+	hfc_outb(card, hfc_INT_M2, card->regs.m2);
+	hfc_outb(card, hfc_CTMT, card->regs.ctmt);
+	hfc_outb(card, hfc_CIRM, card->regs.cirm);
+	hfc_outb(card, hfc_SCTRL, card->regs.sctrl);
+	hfc_outb(card, hfc_SCTRL_R, card->regs.sctrl_r);
+
+	hfc_update_fifo_state(card);
+
+#ifndef LINUX26
+	MOD_DEC_USE_COUNT;
+#else
+	// Zaptel is buggy
+	module_put(THIS_MODULE);
+#endif
+
+	printk(KERN_INFO hfc_DRIVER_PREFIX
+		"card %d: "
+		"chan %s closed as %s.\n",
+		card->cardnum,
+		chan->name,
+		zaptel_chan->name);
+
+	return 0;
+}
+
+static int hfc_zap_rbsbits(struct zt_chan *chan, int bits)
+{
+	return 0;
+}
+
+static int hfc_zap_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data)
+{
+	switch(cmd) {
+		default:
+			return -ENOTTY;
+	}
+
+	return 0;
+}
+
+static int hfc_zap_startup(struct zt_span *span)
+{
+	struct hfc_card *card = span->pvt;
+	int alreadyrunning;
+
+	if (!card) {
+		printk(KERN_INFO hfc_DRIVER_PREFIX
+			"card %d: "
+			"no card for span at startup!\n",
+			card->cardnum);
+	}
+
+	alreadyrunning = span->flags & ZT_FLAG_RUNNING;
+
+	if (!alreadyrunning) {
+		span->chans[ZT_D].flags &= ~ZT_FLAG_HDLC;
+		span->chans[ZT_D].flags |= ZT_FLAG_BRIDCHAN;
+		span->flags |= ZT_FLAG_RUNNING;
+	}
+
+	return 0;
+}
+
+static int hfc_zap_shutdown(struct zt_span *span)
+{
+	return 0;
+}
+
+static int hfc_zap_maint(struct zt_span *span, int cmd)
+{
+	return 0;
+}
+
+static int hfc_zap_chanconfig(struct zt_chan *chan, int sigtype)
+{
+	return 0;
+}
+
+static int hfc_zap_spanconfig(struct zt_span *span, struct zt_lineconfig *lc)
+{
+	span->lineconfig = lc->lineconfig;
+
+	return 0;
+}
+
+static int hfc_zap_initialize(struct hfc_card *card)
+{
+	int i;
+	memset(&card->zt_span, 0x0, sizeof(struct zt_span));
+
+	snprintf(card->zt_span.name, sizeof(card->zt_span.name),
+		 "ZTHFC%d", card->cardnum + 1);
+
+	snprintf(card->zt_span.desc, sizeof(card->zt_span.desc),
+			hfc_DRIVER_DESCR " card %d [%s]",
+			card->cardnum,
+			card->nt_mode?"NT":"TE");
+
+	card->zt_span.spanconfig = hfc_zap_spanconfig;
+	card->zt_span.chanconfig = hfc_zap_chanconfig;
+	card->zt_span.startup = hfc_zap_startup;
+	card->zt_span.shutdown = hfc_zap_shutdown;
+	card->zt_span.maint = hfc_zap_maint;
+	card->zt_span.rbsbits = hfc_zap_rbsbits;
+	card->zt_span.open = hfc_zap_open;
+	card->zt_span.close = hfc_zap_close;
+	card->zt_span.ioctl = hfc_zap_ioctl;
+
+	card->zt_span.chans = card->zt_chans;
+	card->zt_span.channels = 3;
+	card->zt_span.deflaw = ZT_LAW_ALAW;
+	card->zt_span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_CCS; // <--- this is really BS
+	card->zt_span.offset = 0;
+	init_waitqueue_head(&card->zt_span.maintq);
+	card->zt_span.pvt = card;
+
+	for (i = 0; i < card->zt_span.channels; i++) {
+		memset(&card->zt_chans[i], 0x0, sizeof(struct zt_chan));
+
+		snprintf(card->zt_chans[i].name, sizeof(card->zt_chans[i].name),
+				"ZTHFC%d/%d/%d",
+				card->cardnum + 1, 0, i + 1);
+
+		printk(KERN_INFO hfc_DRIVER_PREFIX
+			"card %d: "
+			"registered %s\n",
+			card->cardnum,
+			card->zt_chans[i].name);
+
+		card->zt_chans[i].sigcap =  ZT_SIG_EM | ZT_SIG_CLEAR |
+			ZT_SIG_FXSLS | ZT_SIG_FXSGS |
+			ZT_SIG_FXSKS | ZT_SIG_FXOLS |
+			ZT_SIG_FXOGS | ZT_SIG_FXOKS |
+			ZT_SIG_CAS | ZT_SIG_SF;
+
+		card->zt_chans[i].chanpos = i + 1; 
+	}
+
+	card->zt_chans[ZT_D].readchunk  = card->chans[D].rx.zaptel_buffer;
+	card->zt_chans[ZT_D].writechunk = card->chans[D].tx.zaptel_buffer;
+	card->zt_chans[ZT_D].pvt = &card->chans[D];
+
+	card->zt_chans[ZT_B1].readchunk  = card->chans[B1].rx.zaptel_buffer;
+	card->zt_chans[ZT_B1].writechunk = card->chans[B1].tx.zaptel_buffer;
+	card->zt_chans[ZT_B1].pvt = &card->chans[B1];
+
+	card->zt_chans[ZT_B2].readchunk  = card->chans[B2].rx.zaptel_buffer;
+	card->zt_chans[ZT_B2].writechunk = card->chans[B2].tx.zaptel_buffer;
+	card->zt_chans[ZT_B2].pvt = &card->chans[B2];
+
+	if (zt_register(&card->zt_span,0)) {
+		printk(KERN_CRIT "unable to register zaptel device!\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void hfc_zap_transmit(struct hfc_chan_simplex *chan)
+{
+	hfc_fifo_put(chan, chan->zaptel_buffer, ZT_CHUNKSIZE);
+}
+
+static void hfc_zap_receive(struct hfc_chan_simplex *chan)
+{
+	hfc_fifo_get(chan, chan->zaptel_buffer, ZT_CHUNKSIZE);
+}
+
+/******************************************
+ * Interrupt Handler
+ ******************************************/
+
+static void hfc_handle_timer_interrupt(struct hfc_card *card);
+static void hfc_handle_state_interrupt(struct hfc_card *card);
+static void hfc_handle_processing_interrupt(struct hfc_card *card);
+static void hfc_frame_arrived(struct hfc_chan_duplex *chan);
+static void hfc_handle_voice(struct hfc_card *card);
+
+static irqreturn_t hfc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct hfc_card *card = dev_id;
+	unsigned long flags;
+	u8 status,s1,s2;
+
+	if (!card) {
+		printk(KERN_CRIT hfc_DRIVER_PREFIX
+			"spurious interrupt (IRQ %d)\n",
+			irq);
+		return IRQ_NONE;
+	}
+
+	spin_lock_irqsave(&card->lock, flags);
+	status = hfc_inb(card, hfc_STATUS);
+	if (!(status & hfc_STATUS_ANYINT)) {
+		// maybe we are sharing the irq
+		spin_unlock_irqrestore(&card->lock,flags);
+		return IRQ_NONE;
+	}
+
+	/* We used to ingore the IRQ when the card was in processing
+	 * state but apparently there is no restriction to access the
+	 * card in such state:
+	 *
+	 * Joerg Ciesielski wrote:
+	 * > There is no restriction for the IRQ handler to access
+	 * > HFC-S PCI during processing phase. A IRQ latency of 375 us
+	 * > is also no problem since there are no interrupt sources in
+	 * > HFC-S PCI which must be handled very fast.
+	 * > Due to its deep fifos the IRQ latency can be several ms with
+	 * > out the risk of loosing data. Even the S/T state interrupts
+	 * > must not be handled with a latency less than <5ms.
+	 * >
+	 * > The processing phase only indicates that HFC-S PCI is
+	 * > processing the Fifos as PCI master so that data is read and
+	 * > written in the 32k memory window. But there is no restriction
+	 * > to access data in the memory window during this time.
+	 *
+	 * // if (status & hfc_STATUS_PCI_PROC) {
+	 * // return IRQ_HANDLED;
+	 * // }
+	 */
+
+	s1 = hfc_inb(card, hfc_INT_S1);
+	s2 = hfc_inb(card, hfc_INT_S2);
+
+	if (s1 != 0) {
+		if (s1 & hfc_INTS_TIMER) {
+			// timer (bit 7)
+			hfc_handle_timer_interrupt(card);
+		}
+
+		if (s1 & hfc_INTS_L1STATE) {
+			// state machine (bit 6)
+			hfc_handle_state_interrupt(card);
+		}
+
+		if (s1 & hfc_INTS_DREC) {
+			// D chan RX (bit 5)
+			hfc_frame_arrived(&card->chans[D]);
+		}
+
+		if (s1 & hfc_INTS_B1REC) {
+			// B1 chan RX (bit 3)
+			hfc_frame_arrived(&card->chans[B1]);
+		}
+
+		if (s1 & hfc_INTS_B2REC) {
+			// B2 chan RX (bit 4)
+			hfc_frame_arrived(&card->chans[B2]);
+		}
+
+		if (s1 & hfc_INTS_DTRANS) {
+			// D chan TX (bit 2)
+		}
+
+		if (s1 & hfc_INTS_B1TRANS) {
+			// B1 chan TX (bit 0)
+		}
+
+		if (s1 & hfc_INTS_B2TRANS) {
+			// B2 chan TX (bit 1)
+		}
+
+	}
+
+	if (s2 != 0) {
+		if (s2 & hfc_M2_PMESEL) {
+			// kaboom irq (bit 7)
+
+			/* CologneChip says:
+			 *
+			 * the meaning of this fatal error bit is that HFC-S PCI as PCI
+			 * master could not access the PCI bus within 125us to finish its
+			 * data processing. If this happens only very seldom it does not
+			 * cause big problems but of course some B-channel or D-channel
+			 * data will be corrupted due to this event.
+			 *
+			 * Unfortunately this bit is only set once after the problem occurs
+			 * and can only be reseted by a software reset. That means it is not
+			 * easily possible to check how often this fatal error happens.
+			 */
+
+			if(!card->sync_loss_reported) {
+				printk(KERN_CRIT hfc_DRIVER_PREFIX
+					"card %d: "
+					"sync lost, pci performance too low!\n",
+					card->cardnum);
+
+				card->sync_loss_reported = TRUE;
+			}
+		}
+
+		if (s2 & hfc_M2_GCI_MON_REC) {
+			// RxR monitor channel (bit 2)
+		}
+
+		if (s2 & hfc_M2_GCI_I_CHG) {
+			// GCI I-change  (bit 1)
+		}
+
+		if (s2 & hfc_M2_PROC_TRANS){
+			// processing/non-processing transition  (bit 0)
+			hfc_handle_processing_interrupt(card);
+		}
+
+	}
+
+	spin_unlock_irqrestore(&card->lock,flags);
+
+	return IRQ_HANDLED;
+}
+
+static void hfc_handle_timer_interrupt(struct hfc_card *card)
+{
+	if(card->ignore_first_timer_interrupt) {
+		card->ignore_first_timer_interrupt = FALSE;
+		return;
+	}
+
+	if ((card->nt_mode && card->l1_state == 3) ||
+		(!card->nt_mode && card->l1_state == 7)) {
+
+		card->regs.ctmt &= ~hfc_CTMT_TIMER_MASK;
+		hfc_outb(card, hfc_CTMT, card->regs.ctmt);
+
+		hfc_resume_fifo(card);
+	}
+}
+
+static void hfc_handle_state_interrupt(struct hfc_card *card)
+{
+	u8 new_state = hfc_inb(card,hfc_STATES)  & hfc_STATES_STATE_MASK;
+
+#ifdef DEBUG
+	if (debug_level >= 1) {
+		printk(KERN_DEBUG hfc_DRIVER_PREFIX
+			"card %d: "
+			"layer 1 state = %c%d\n",
+			card->cardnum,
+			card->nt_mode?'G':'F',
+			new_state);
+	}
+#endif
+
+	if (card->nt_mode) {
+		// NT mode
+
+		if (new_state == 3) {
+			// fix to G3 state (see specs)
+			hfc_outb(card, hfc_STATES, hfc_STATES_LOAD_STATE | 3);
+		}
+
+		if (new_state == 3 && card->l1_state != 3) {
+			hfc_resume_fifo(card);
+		}
+
+		if (new_state != 3 && card->l1_state == 3) {
+			hfc_suspend_fifo(card);
+		}
+	} else {
+		if (new_state == 3) {
+			// Keep L1 up... zaptel & libpri expects a always up L1...
+			// Enable only  when using an unpatched libpri
+
+			if (force_l1_up) {
+				hfc_outb(card, hfc_STATES,
+					hfc_STATES_DO_ACTION |
+					hfc_STATES_ACTIVATE|
+					hfc_STATES_NT_G2_G3);
+			}
+		}
+
+		if (new_state == 7 && card->l1_state != 7) {
+			// TE is now active, schedule FIFO activation after
+			// some time, otherwise the first frames are lost
+
+			card->regs.ctmt |= hfc_CTMT_TIMER_50 | hfc_CTMT_TIMER_CLEAR;
+			hfc_outb(card, hfc_CTMT, card->regs.ctmt);
+
+			// Activating the timer firest an interrupt immediately, we
+			// obviously need to ignore it
+			card->ignore_first_timer_interrupt = TRUE;
+		}
+
+		if (new_state != 7 && card->l1_state == 7) {
+			// TE has become inactive, disable FIFO
+			hfc_suspend_fifo(card);
+		}
+	}
+
+	card->l1_state = new_state;
+}
+
+static void hfc_handle_processing_interrupt(struct hfc_card *card)
+{
+	int available_bytes=0;
+
+	// Synchronize with the first enabled channel
+	if(card->regs.fifo_en & hfc_FIFOEN_B1RX)
+		available_bytes = hfc_fifo_used_rx(&card->chans[B1].rx);
+	if(card->regs.fifo_en & hfc_FIFOEN_B2RX)
+		available_bytes = hfc_fifo_used_rx(&card->chans[B2].rx);
+	else
+		available_bytes = -1;
+
+	if ((available_bytes == -1 && card->ticks == 8) ||
+		available_bytes >= ZT_CHUNKSIZE + hfc_RX_FIFO_PRELOAD) {
+		card->ticks = 0;
+
+		if (available_bytes > ZT_CHUNKSIZE*2 + hfc_RX_FIFO_PRELOAD) {
+			card->late_irqs++;
+			// we are out of sync, clear fifos, jaw
+			hfc_clear_fifo_rx(&card->chans[B1].rx);
+			hfc_clear_fifo_tx(&card->chans[B1].tx);
+			hfc_clear_fifo_rx(&card->chans[B2].rx);
+			hfc_clear_fifo_tx(&card->chans[B2].tx);
+
+#ifdef DEBUG
+			if (debug_level >= 4) {
+				printk(KERN_DEBUG hfc_DRIVER_PREFIX
+					"card %d: "
+					"late IRQ, %d bytes late\n",
+					card->cardnum,
+					available_bytes -
+						(ZT_CHUNKSIZE +
+						 hfc_RX_FIFO_PRELOAD));
+			}
+#endif
+		} else {
+			hfc_handle_voice(card);
+		}
+	}
+
+	card->ticks++;
+}
+
+static void hfc_sniff_zaptel_d_channel(struct hfc_card *card)
+{
+	struct sk_buff *skb =
+		dev_alloc_skb(card->chans[D].tx.ugly_framebuf_size);
+
+	if (!skb) {
+		printk(KERN_ERR hfc_DRIVER_PREFIX
+			"card %d: "
+			"cannot allocate skb: sniffed frame dropped\n",
+			card->cardnum);
+		return;
+	}
+
+	skb->dev = card->chans[D].netdev;
+	skb->protocol = htons(card->chans[D].protocol);
+	skb->ip_summed = CHECKSUM_HW;
+	skb->pkt_type = PACKET_OUTGOING;
+
+	memcpy(skb_put(skb, card->chans[D].tx.ugly_framebuf_size),
+		card->chans[D].tx.ugly_framebuf,
+		card->chans[D].tx.ugly_framebuf_size);
+
+	netif_rx(skb);
+}
+
+static void hfc_handle_voice(struct hfc_card *card)
+{
+	if (card->chans[B1].status != open_voice &&
+		card->chans[B2].status != open_voice)
+		return;
+
+	card->zt_chans[ZT_D].bytes2transmit = 0;
+	card->zt_chans[ZT_D].maxbytes2transmit = ZT_CHUNKSIZE;
+
+	zt_transmit(&card->zt_span);
+
+	if (card->regs.fifo_en & hfc_FIFOEN_B1TX)
+		hfc_zap_transmit(&card->chans[B1].tx);
+	if (card->regs.fifo_en & hfc_FIFOEN_B2TX)
+		hfc_zap_transmit(&card->chans[B2].tx);
+
+       	if (card->zt_chans[ZT_D].bytes2transmit) {
+		hfc_check_l1_up(card);
+
+       		memcpy(card->chans[D].tx.ugly_framebuf +
+       			card->chans[D].tx.ugly_framebuf_size,
+       			card->chans[D].tx.zaptel_buffer,
+       			card->zt_chans[ZT_D].bytes2transmit);
+
+       		card->chans[D].tx.ugly_framebuf_size +=
+       			card->zt_chans[ZT_D].bytes2transmit;
+
+       		if (card->zt_chans[ZT_D].eoftx) {
+       			hfc_fifo_put_frame(&card->chans[D].tx,
+       				 card->chans[D].tx.ugly_framebuf,
+       				 card->chans[D].tx.ugly_framebuf_size);
+
+			if (sniff_zaptel_d_channel)
+				hfc_sniff_zaptel_d_channel(card);
+
+			card->chans[D].tx.ugly_framebuf_size = 0;
+			card->zt_chans[ZT_D].eoftx = FALSE;
+		}
+	}
+
+	if (card->regs.fifo_en & hfc_FIFOEN_B1RX)
+		hfc_zap_receive(&card->chans[B1].rx);
+	else
+		memset(&card->chans[B1].rx.zaptel_buffer, 0x7f,
+			sizeof(card->chans[B1].rx.zaptel_buffer));
+
+	if (card->regs.fifo_en & hfc_FIFOEN_B2RX)
+		hfc_zap_receive(&card->chans[B2].rx);
+	else
+		memset(&card->chans[B2].rx.zaptel_buffer, 0x7f,
+			sizeof(card->chans[B1].rx.zaptel_buffer));
+
+	// Echo cancellation
+	zt_ec_chunk(&card->zt_chans[ZT_B1],
+			card->chans[B1].rx.zaptel_buffer,
+			card->chans[B1].tx.zaptel_buffer);
+	zt_ec_chunk(&card->zt_chans[ZT_B2],
+			card->chans[B2].rx.zaptel_buffer,
+			card->chans[B2].tx.zaptel_buffer);
+
+	// If there's a frame in the FIFO, read it all and make it
+	// available to zaptel
+	if (hfc_fifo_has_frames(&card->chans[D].rx)) {
+		hfc_frame_arrived(&card->chans[D]);
+	}
+
+	// Stupid zaptel frame handling
+	if (!card->chans[D].rx.ugly_framebuf_size) {
+		// hmm....ok, let zaptel receive nothing
+		card->zt_chans[ZT_D].bytes2receive = 0;
+		card->zt_chans[ZT_D].eofrx = FALSE;
+	}
+	else if (card->chans[D].rx.ugly_framebuf_size -
+		card->chans[D].rx.ugly_framebuf_off > ZT_CHUNKSIZE) {
+
+		// the frame is longer than ZT_CHUNKSIZE
+		memcpy(card->chans[D].rx.zaptel_buffer,
+			card->chans[D].rx.ugly_framebuf+
+			card->chans[D].rx.ugly_framebuf_off,
+			ZT_CHUNKSIZE);
+
+		card->zt_chans[ZT_D].bytes2receive = ZT_CHUNKSIZE;
+		card->zt_chans[ZT_D].eofrx = FALSE;
+
+		card->chans[D].rx.ugly_framebuf_off += ZT_CHUNKSIZE;
+	} else {
+		// we can read it all
+		memcpy(card->chans[D].rx.zaptel_buffer,
+			card->chans[D].rx.ugly_framebuf+
+			card->chans[D].rx.ugly_framebuf_off,
+			card->chans[D].rx.ugly_framebuf_size);
+
+		card->zt_chans[ZT_D].bytes2receive = card->chans[D].rx.ugly_framebuf_size-
+			card->chans[D].rx.ugly_framebuf_off;
+		card->zt_chans[ZT_D].eofrx = TRUE;
+
+		card->chans[D].rx.ugly_framebuf_size = 0;
+		card->chans[D].rx.ugly_framebuf_off = 0;
+	}
+
+	if (card->zt_span.flags & ZT_FLAG_RUNNING) {
+		zt_receive(&card->zt_span);
+	}
+
+}
+
+static void hfc_frame_arrived(struct hfc_chan_duplex *chan)
+{
+	struct hfc_card *card = chan->card;
+	int antiloop = 16;
+	struct sk_buff *skb;
+	
+	while(hfc_fifo_has_frames(&chan->rx) && --antiloop) {
+		int frame_size = hfc_fifo_get_frame_size(&chan->rx);
+
+		if (frame_size < 3) {
+#ifdef DEBUG
+			if (debug_level>=2)
+				printk(KERN_DEBUG hfc_DRIVER_PREFIX
+					"card %d: "
+					"chan %s: "
+					"invalid frame received, just %d bytes\n",
+					card->cardnum,
+					chan->name,
+					frame_size);
+#endif
+
+			hfc_fifo_drop_frame(&chan->rx);
+
+			chan->net_device_stats.rx_dropped++;
+
+			continue;
+		} else if(frame_size == 3) {
+#ifdef DEBUG
+			if (debug_level>=2)
+				printk(KERN_DEBUG hfc_DRIVER_PREFIX
+					"card %d: "
+					"chan %s: "
+					"empty frame received\n",
+					card->cardnum,
+					chan->name);
+#endif
+
+			hfc_fifo_drop_frame(&chan->rx);
+
+			chan->net_device_stats.rx_dropped++;
+
+			continue;
+		}
+
+		if (chan->open_by_zaptel &&
+			card->chans[D].rx.ugly_framebuf_size) {
+				// We have to wait for zaptel to transmit the
+				// frame... wait for next time
+
+				break;
+		}
+
+		skb = dev_alloc_skb(frame_size - 3);
+
+		if (!skb) {
+			printk(KERN_ERR hfc_DRIVER_PREFIX
+				"card %d: "
+				"chan %s: "
+				"cannot allocate skb: frame dropped\n",
+				card->cardnum,
+				chan->name);
+
+			hfc_fifo_drop_frame(&chan->rx);
+
+			chan->net_device_stats.rx_dropped++;
+
+			continue;
+		}
+
+		// Oh... this is the echo channel... redirect to D
+		// channel's netdev
+		if (card->echo_enabled && chan->number == B2) {
+			skb->protocol = htons(card->chans[D].protocol);
+			skb->dev = card->chans[D].netdev;
+			skb->pkt_type = PACKET_OTHERHOST;
+		} else {
+			skb->protocol = htons(chan->protocol);
+			skb->dev = chan->netdev;
+			skb->pkt_type = PACKET_HOST;
+		}
+
+		skb->ip_summed = CHECKSUM_HW; // HFC does the checksum
+
+		if (chan->open_by_zaptel) {
+			card->chans[D].rx.ugly_framebuf_size = frame_size - 1;
+
+			if (hfc_fifo_get_frame(&card->chans[D].rx,
+				card->chans[D].rx.ugly_framebuf,
+				frame_size - 1) == -1) {
+				dev_kfree_skb(skb);
+				continue;
+			}
+
+			memcpy(skb_put(skb, frame_size - 3),
+				card->chans[D].rx.ugly_framebuf,
+				frame_size - 3);
+		} else {
+			if (hfc_fifo_get_frame(&chan->rx,
+				skb_put(skb, frame_size - 3),
+				frame_size - 3) == -1) {
+				dev_kfree_skb(skb);
+				continue;
+			}
+		}
+
+		chan->net_device_stats.rx_packets++;
+		chan->net_device_stats.rx_bytes += frame_size - 1;
+
+		netif_rx(skb);
+	}
+
+	if (!antiloop) 
+		printk(KERN_CRIT hfc_DRIVER_PREFIX
+			"card %d: "
+			"Infinite loop detected\n",
+			card->cardnum);
+}
+
+/******************************************
+ * Module initialization and cleanup
+ ******************************************/
+
+static void hfc_setup_lapd(struct hfc_chan_duplex *chan)
+{
+	chan->netdev->priv = chan;
+	chan->netdev->open = hfc_open;
+	chan->netdev->stop = hfc_close;
+	chan->netdev->hard_start_xmit = hfc_xmit_frame;
+	chan->netdev->get_stats = hfc_get_stats;
+	chan->netdev->set_multicast_list = hfc_set_multicast_list;
+
+	memset(chan->netdev->dev_addr, 0x00, sizeof(chan->netdev->dev_addr));
+
+	SET_MODULE_OWNER(chan->netdev);
+}
+
+static int __devinit hfc_probe(struct pci_dev *pci_dev,
+	const struct pci_device_id *ent)
+{
+	static int cardnum=0;
+	int err;
+	int i;
+
+	struct hfc_card *card = NULL;
+	card = kmalloc(sizeof(struct hfc_card), GFP_KERNEL);
+	if (!card) {
+		printk(KERN_CRIT hfc_DRIVER_PREFIX
+			"unable to kmalloc!\n");
+		err = -ENOMEM;
+		goto err_alloc_hfccard;
+	}
+
+	memset(card, 0x00, sizeof(struct hfc_card));
+	card->cardnum = cardnum;
+	card->pcidev = pci_dev;
+	spin_lock_init(&card->lock);
+
+	pci_set_drvdata(pci_dev, card);
+
+	if ((err = pci_enable_device(pci_dev))) {
+		goto err_pci_enable_device;
+	}
+
+	if ((err = pci_set_dma_mask(pci_dev, PCI_DMA_32BIT))) {
+		printk(KERN_ERR hfc_DRIVER_PREFIX
+			"card %d: "
+			"No suitable DMA configuration available.\n",
+			card->cardnum);
+		goto err_pci_set_dma_mask;
+	}
+
+	pci_write_config_word(pci_dev, PCI_COMMAND, PCI_COMMAND_MEMORY);
+
+	if((err = pci_request_regions(pci_dev, hfc_DRIVER_NAME))) {
+		printk(KERN_CRIT hfc_DRIVER_PREFIX
+			"card %d: "
+			"cannot request I/O memory region\n",
+			card->cardnum);
+		goto err_pci_request_regions;
+	}
+
+	pci_set_master(pci_dev);
+
+	if (!pci_dev->irq) {
+		printk(KERN_CRIT hfc_DRIVER_PREFIX
+			"card %d: "
+			"no irq!\n",
+			card->cardnum);
+		err = -ENODEV;
+		goto err_noirq;
+	}
+
+	card->io_bus_mem = pci_resource_start(pci_dev,1);
+	if (!card->io_bus_mem) {
+		printk(KERN_CRIT hfc_DRIVER_PREFIX
+			"card %d: "
+			"no iomem!\n",
+			card->cardnum);
+		err = -ENODEV;
+		goto err_noiobase;
+	}
+
+	if(!(card->io_mem = ioremap(card->io_bus_mem, hfc_PCI_MEM_SIZE))) {
+		printk(KERN_CRIT hfc_DRIVER_PREFIX
+			"card %d: "
+			"cannot ioremap I/O memory\n",
+			card->cardnum);
+		err = -ENODEV;
+		goto err_ioremap;
+	}
+
+	// pci_alloc_consistent guarantees alignment (Documentation/DMA-mapping.txt)
+	card->fifo_mem = pci_alloc_consistent(pci_dev, hfc_FIFO_SIZE, &card->fifo_bus_mem);
+	if (!card->fifo_mem) {
+		printk(KERN_CRIT hfc_DRIVER_PREFIX
+			"card %d: "
+			"unable to allocate FIFO DMA memory!\n",
+			card->cardnum);
+		err = -ENOMEM;
+		goto err_alloc_fifo;
+	}
+
+	memset(card->fifo_mem, 0x00, hfc_FIFO_SIZE);
+
+	card->fifos = card->fifo_mem;
+
+	pci_write_config_dword(card->pcidev, hfc_PCI_MWBA, card->fifo_bus_mem);
+
+	if ((err = request_irq(card->pcidev->irq, &hfc_interrupt,
+		SA_SHIRQ, hfc_DRIVER_NAME, card))) {
+		printk(KERN_CRIT hfc_DRIVER_PREFIX
+			"card %d: "
+			"unable to register irq\n",
+			card->cardnum);
+		goto err_request_irq;
+	}
+
+	card->nt_mode = FALSE;
+
+	if (modes & (1 << card->cardnum))
+		card->nt_mode = TRUE;
+
+	for (i=0; i<nt_modes_count; i++) {
+		if (nt_modes[i] == card->cardnum)
+			card->nt_mode=TRUE;
+	}
+
+//---------------------------------- D
+	card->chans[D].card = card;
+	card->chans[D].name = "D";
+	card->chans[D].status = free;
+	card->chans[D].number = D;
+	card->chans[D].protocol = ETH_P_LAPD;
+	spin_lock_init(&card->chans[D].lock);
+
+	card->chans[D].rx.chan      = &card->chans[D];
+	card->chans[D].rx.fifo_base = card->fifos + 0x4000;
+	card->chans[D].rx.z_base    = card->fifos + 0x4000;
+	card->chans[D].rx.z1_base   = card->fifos + 0x6080;
+	card->chans[D].rx.z2_base   = card->fifos + 0x6082;
+	card->chans[D].rx.z_min     = 0x0000;
+	card->chans[D].rx.z_max     = 0x01FF;
+	card->chans[D].rx.f_min     = 0x10;
+	card->chans[D].rx.f_max     = 0x1F;
+	card->chans[D].rx.f1        = card->fifos + 0x60a0;
+	card->chans[D].rx.f2        = card->fifos + 0x60a1;
+	card->chans[D].rx.fifo_size = card->chans[D].rx.z_max - card->chans[D].rx.z_min + 1;
+	card->chans[D].rx.f_num     = card->chans[D].rx.f_max - card->chans[D].rx.f_min + 1;
+
+	card->chans[D].tx.chan      = &card->chans[D];
+	card->chans[D].tx.fifo_base = card->fifos + 0x0000;
+	card->chans[D].tx.z_base    = card->fifos + 0x0000;
+	card->chans[D].tx.z1_base   = card->fifos + 0x2080;
+	card->chans[D].tx.z2_base   = card->fifos + 0x2082;
+	card->chans[D].tx.z_min     = 0x0000;
+	card->chans[D].tx.z_max     = 0x01FF;
+	card->chans[D].tx.f_min     = 0x10;
+	card->chans[D].tx.f_max     = 0x1F;
+	card->chans[D].tx.f1        = card->fifos + 0x20a0;
+	card->chans[D].tx.f2        = card->fifos + 0x20a1;
+	card->chans[D].tx.fifo_size = card->chans[D].tx.z_max - card->chans[D].tx.z_min + 1;
+	card->chans[D].tx.f_num     = card->chans[D].tx.f_max - card->chans[D].tx.f_min + 1;
+
+	if(!(card->chans[D].netdev = alloc_netdev(0, "isdn%dd", setup_lapd))) {
+		printk(KERN_ERR hfc_DRIVER_PREFIX
+			"net_device alloc failed, abort.\n");
+		err = -ENOMEM;
+		goto err_alloc_netdev_d;
+	}
+
+	hfc_setup_lapd(&card->chans[D]);
+
+	card->chans[D].netdev->irq = card->pcidev->irq;
+	card->chans[D].netdev->base_addr = card->io_bus_mem;
+/*	card->chans[D].netdev->rmem_start = card->fifo_bus_mem + 0x4000;
+	card->chans[D].netdev->rmem_end = card->fifo_bus_mem + 0x4000 +
+		card->chans[D].rx.fifo_size - 1;*/
+	card->chans[D].netdev->mem_start = card->fifo_bus_mem + 0x0000;
+	card->chans[D].netdev->mem_end = card->fifo_bus_mem + 0x0000 +
+		card->chans[D].tx.fifo_size - 1;
+
+	if((err = register_netdev(card->chans[D].netdev))) {
+		printk(KERN_INFO hfc_DRIVER_PREFIX
+			"card %d: "
+			"Cannot register net device, aborting.\n",
+			card->cardnum);
+		goto err_register_netdev_d;
+	}
+
+//---------------------------------- B1
+	card->chans[B1].card = card;
+	card->chans[B1].name = "B1";
+	card->chans[B1].status = free;
+	card->chans[B1].number = B1;
+	card->chans[B1].protocol = 0;
+	spin_lock_init(&card->chans[B1].lock);
+
+	card->chans[B1].rx.chan      = &card->chans[B1];
+	card->chans[B1].rx.fifo_base = card->fifos + 0x4200;
+	card->chans[B1].rx.z_base    = card->fifos + 0x4000;
+	card->chans[B1].rx.z1_base   = card->fifos + 0x6000;
+	card->chans[B1].rx.z2_base   = card->fifos + 0x6002;
+	card->chans[B1].rx.z_min     = 0x0200;
+	card->chans[B1].rx.z_max     = 0x1FFF;
+	card->chans[B1].rx.f_min     = 0x00;
+	card->chans[B1].rx.f_max     = 0x1F;
+	card->chans[B1].rx.f1        = card->fifos + 0x6080;
+	card->chans[B1].rx.f2        = card->fifos + 0x6081;
+	card->chans[B1].rx.fifo_size = card->chans[B1].rx.z_max - card->chans[B1].rx.z_min + 1;
+	card->chans[B1].rx.f_num     = card->chans[B1].rx.f_max - card->chans[B1].rx.f_min + 1;
+
+	card->chans[B1].tx.chan      = &card->chans[B1];
+	card->chans[B1].tx.fifo_base = card->fifos + 0x0200;
+	card->chans[B1].tx.z_base    = card->fifos + 0x0000;
+	card->chans[B1].tx.z1_base   = card->fifos + 0x2000;
+	card->chans[B1].tx.z2_base   = card->fifos + 0x2002;
+	card->chans[B1].tx.z_min     = 0x0200;
+	card->chans[B1].tx.z_max     = 0x1FFF;
+	card->chans[B1].tx.f_min     = 0x00;
+	card->chans[B1].tx.f_max     = 0x1F;
+	card->chans[B1].tx.f1        = card->fifos + 0x2080;
+	card->chans[B1].tx.f2        = card->fifos + 0x2081;
+	card->chans[B1].tx.fifo_size = card->chans[B1].tx.z_max - card->chans[B1].tx.z_min + 1;
+	card->chans[B1].tx.f_num     = card->chans[B1].tx.f_max - card->chans[B1].tx.f_min + 1;
+
+//	card->chans[B1].netdev->irq = card->pcidev->irq;
+//	card->chans[B1].netdev->base_addr = card->io_bus_mem;
+/*	card->chans[B1].netdev->rmem_start = card->fifo_bus_mem + 0x4200;
+	card->chans[B1].netdev->rmem_end = card->fifo_bus_mem + 0x4200 +
+		card->chans[B1].rx.fifo_size - 1;*/
+//	card->chans[B1].netdev->mem_start = card->fifo_bus_mem + 0x0200;
+//	card->chans[B1].netdev->mem_end = card->fifo_bus_mem + 0x0200 +
+//		card->chans[B1].tx.fifo_size - 1;
+
+//---------------------------------- B2
+	card->chans[B2].card = card;
+	card->chans[B2].name = "B2";
+	card->chans[B2].status = free;
+	card->chans[B2].number = B2;
+	card->chans[B2].protocol = 0;
+	spin_lock_init(&card->chans[B2].lock);
+
+	card->chans[B2].rx.chan      = &card->chans[B2];
+	card->chans[B2].rx.fifo_base = card->fifos + 0x6200,
+	card->chans[B2].rx.z_base    = card->fifos + 0x6000;
+	card->chans[B2].rx.z1_base   = card->fifos + 0x6100;
+	card->chans[B2].rx.z2_base   = card->fifos + 0x6102;
+	card->chans[B2].rx.z_min     = 0x0200;
+	card->chans[B2].rx.z_max     = 0x1FFF;
+	card->chans[B2].rx.f_min     = 0x00;
+	card->chans[B2].rx.f_max     = 0x1F;
+	card->chans[B2].rx.f1        = card->fifos + 0x6180;
+	card->chans[B2].rx.f2        = card->fifos + 0x6181;
+	card->chans[B2].rx.fifo_size = card->chans[B2].rx.z_max - card->chans[B2].rx.z_min + 1;
+	card->chans[B2].rx.f_num     = card->chans[B2].rx.f_max - card->chans[B2].rx.f_min + 1;
+
+	card->chans[B2].tx.chan      = &card->chans[B2];
+	card->chans[B2].tx.fifo_base = card->fifos + 0x2200;
+	card->chans[B2].tx.z_base    = card->fifos + 0x2000;
+	card->chans[B2].tx.z1_base   = card->fifos + 0x2100;
+	card->chans[B2].tx.z2_base   = card->fifos + 0x2102;
+	card->chans[B2].tx.z_min     = 0x0200;
+	card->chans[B2].tx.z_max     = 0x1FFF;
+	card->chans[B2].tx.f_min     = 0x00;
+	card->chans[B2].tx.f_max     = 0x1F;
+	card->chans[B2].tx.f1        = card->fifos + 0x2180;
+	card->chans[B2].tx.f2        = card->fifos + 0x2181;
+	card->chans[B2].tx.fifo_size = card->chans[B2].tx.z_max - card->chans[B2].tx.z_min + 1;
+	card->chans[B2].tx.f_num     = card->chans[B2].tx.f_max - card->chans[B2].tx.f_min + 1;
+
+//	card->chans[B2].netdev->irq = card->pcidev->irq;
+//	card->chans[B2].netdev->base_addr = card->io_bus_mem;
+/*	card->chans[B2].netdev->rmem_start = card->fifo_bus_mem + 0x6200;
+	card->chans[B2].netdev->rmem_end = card->fifo_bus_mem + 0x6200 +
+		card->chans[B2].rx.fifo_size - 1;*/
+//	card->chans[B2].netdev->mem_start = card->fifo_bus_mem + 0x2200;
+//	card->chans[B2].netdev->mem_end = card->fifo_bus_mem + 0x2200 +
+//		card->chans[B2].tx.fifo_size - 1;
+
+// -------------------------------------------------------
+
+	hfc_zap_initialize(card);
+
+	snprintf(card->proc_dir_name,
+			sizeof(card->proc_dir_name),
+			"%d", card->cardnum);
+	card->proc_dir = proc_mkdir(card->proc_dir_name, hfc_proc_zaphfc_dir);
+	card->proc_dir->owner = THIS_MODULE;
+
+	card->proc_info = create_proc_read_entry(
+			"info", 0444, card->proc_dir,
+			hfc_proc_read_info, card);
+	card->proc_info->owner = THIS_MODULE;
+
+	card->proc_fifos = create_proc_read_entry(
+			"fifos", 0400, card->proc_dir,
+			hfc_proc_read_fifos, card);
+	card->proc_fifos->owner = THIS_MODULE;
+
+	card->proc_bufs = create_proc_read_entry(
+			"bufs", 0400, card->proc_dir,
+			hfc_proc_read_bufs, card);
+	card->proc_bufs->owner = THIS_MODULE;
+
+	hfc_resetCard(card);
+
+	printk(KERN_INFO hfc_DRIVER_PREFIX
+		"card %d configured for %s mode at mem %#lx (0x%p) IRQ %u\n",
+		card->cardnum,
+		card->nt_mode?"NT":"TE",
+		card->io_bus_mem,
+		card->io_mem,
+		card->pcidev->irq); 
+
+	cardnum++;
+
+	return 0;
+
+//	unregister_netdev(card->chans[D].netdev);
+err_register_netdev_d:
+	free_netdev(card->chans[D].netdev);
+err_alloc_netdev_d:
+	free_irq(pci_dev->irq, card);
+err_request_irq:
+	pci_free_consistent(pci_dev, hfc_FIFO_SIZE,
+		card->fifo_mem, card->fifo_bus_mem);
+err_alloc_fifo:
+	iounmap(card->io_mem);
+err_ioremap:
+err_noiobase:
+err_noirq:
+	pci_release_regions(pci_dev);
+err_pci_request_regions:
+err_pci_set_dma_mask:
+err_pci_enable_device:
+	kfree(card);
+err_alloc_hfccard:
+	return err;
+}
+
+static void __devexit hfc_remove(struct pci_dev *pci_dev)
+{
+	struct hfc_card *card = pci_get_drvdata(pci_dev);
+
+	unregister_netdev(card->chans[D].netdev);
+
+//	unsigned long flags;
+//	spin_lock_irqsave(&card->lock,flags);
+
+	printk(KERN_INFO hfc_DRIVER_PREFIX
+		"card %d: "
+		"shutting down card at %p.\n",
+		card->cardnum,
+		card->io_mem);
+
+	hfc_softreset(card);
+
+	zt_unregister(&card->zt_span);
+
+
+	// disable memio and bustmaster
+	pci_write_config_word(pci_dev, PCI_COMMAND, 0);
+
+//	spin_unlock_irqrestore(&card->lock,flags);
+
+	remove_proc_entry("bufs", card->proc_dir);
+	remove_proc_entry("fifos", card->proc_dir);
+	remove_proc_entry("info", card->proc_dir);
+	remove_proc_entry(card->proc_dir_name, hfc_proc_zaphfc_dir);
+
+	free_irq(pci_dev->irq, card);
+
+	pci_free_consistent(pci_dev, hfc_FIFO_SIZE,
+		card->fifo_mem, card->fifo_bus_mem);
+
+	iounmap(card->io_mem);
+
+	pci_release_regions(pci_dev);
+
+	pci_disable_device(pci_dev);
+
+	free_netdev(card->chans[D].netdev);
+	kfree(card);
+}
+
+/******************************************
+ * Module stuff
+ ******************************************/
+
+static int __init hfc_init_module(void)
+{
+	int ret;
+
+	printk(KERN_INFO hfc_DRIVER_PREFIX
+		hfc_DRIVER_STRING " loading\n");
+
+	hfc_proc_zaphfc_dir = proc_mkdir(hfc_DRIVER_NAME, proc_root_driver);
+
+	ret = pci_module_init(&hfc_driver);
+	return ret;
+}
+
+module_init(hfc_init_module);
+
+static void __exit hfc_module_exit(void)
+{
+	pci_unregister_driver(&hfc_driver);
+
+	remove_proc_entry(hfc_DRIVER_NAME, proc_root_driver);
+
+	printk(KERN_INFO hfc_DRIVER_PREFIX
+		hfc_DRIVER_STRING " unloaded\n");
+}
+
+module_exit(hfc_module_exit);
+
+#endif
+
+MODULE_DESCRIPTION(hfc_DRIVER_DESCR);
+MODULE_AUTHOR("Jens Wilke <jw_vzaphfc at headissue.com>, Daniele (Vihai) Orlandi <daniele at orlandi.com>");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+#ifdef LINUX26
+
+module_param(modes, int, 0444);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+module_param_array(nt_modes, int, &nt_modes_count, 0444);
+#else
+module_param_array(nt_modes, int, nt_modes_count, 0444);
+#endif
+
+module_param(force_l1_up, int, 0444);
+module_param(sniff_zaptel_d_channel, int, 0444);
+#ifdef DEBUG
+module_param(debug_level, int, 0444);
+#endif
+
+#else
+
+MODULE_PARM(modes,"i");
+MODULE_PARM(force_l1_up,"i");
+MODULE_PARM(sniff_zaptel_d_channel,"i");
+#ifdef DEBUG
+MODULE_PARM(debug_level,"i");
+#endif
+
+#endif // LINUX26
+
+MODULE_PARM_DESC(modes, "[Deprecated] bit-mask to configure NT mode");
+MODULE_PARM_DESC(nt_modes, "Comma-separated list of card IDs to configure in NT mode");
+MODULE_PARM_DESC(force_l1_up, "Don't allow L1 to go down");
+MODULE_PARM_DESC(sniff_zaptel_d_channel, "Make frames transmitted from zaptel"
+					" appear as received by the board");
+#ifdef DEBUG
+MODULE_PARM_DESC(debug_level, "Debug verbosity level");
+#endif




More information about the Pkg-voip-commits mailing list