Subject: [PATCH] wan: new driver retina

From: Matti Linnanvuori <[email-masked]>
Date: Tue, 23 Oct 2007 02:58:29 -0700 (PDT)

From: Matti Linnanvuori <[email-masked]>

Retina G.703 and G.SHDSL driver.

Signed-off-by: Matti Linnanvuori <[email-masked]>
---

Fixed bugs according to linux-netdev comments.

diff -Napur linux-2.6.23/drivers/net/wan/Kconfig linux-2.6.24/drivers/net/wan/Kconfig
--- linux-2.6.23/drivers/net/wan/Kconfig	2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/drivers/net/wan/Kconfig	2007-10-23 12:30:45.853384514 +0300
@@ -494,4 +494,14 @@ config SBNI_MULTILINE
 
 	  If unsure, say N.
 
+config RETINA
+	tristate "Retina support"
+	depends on PCI
+	help
+	  Driver for Retina C5400 and E2200 network PCI cards, which
+	  support G.703, G.SHDSL with Ethernet encapsulation.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called retina.
+
 endif # WAN
diff -Napur linux-2.6.23/drivers/net/wan/Makefile linux-2.6.24/drivers/net/wan/Makefile
--- linux-2.6.23/drivers/net/wan/Makefile	2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/drivers/net/wan/Makefile	2007-10-23 12:31:17.598640178 +0300
@@ -42,6 +42,7 @@ obj-$(CONFIG_C101)		+= c101.o
 obj-$(CONFIG_WANXL)		+= wanxl.o
 obj-$(CONFIG_PCI200SYN)		+= pci200syn.o
 obj-$(CONFIG_PC300TOO)		+= pc300too.o
+obj-$(CONFIG_RETINA)		+= retina.o
 
 clean-files := wanxlfw.inc
 $(obj)/wanxl.o:	$(obj)/wanxlfw.inc
diff -Napur linux-2.6.23/drivers/net/wan/retina.c linux-2.6.24/drivers/net/wan/retina.c
--- linux-2.6.23/drivers/net/wan/retina.c	1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.24/drivers/net/wan/retina.c	2007-10-23 12:52:17.122802714 +0300
@@ -0,0 +1,4428 @@
+/* retina.c: */
+
+/*
+	This driver is based on:
+
+	/drivers/net/fepci.c
+	FEPCI (Frame Engine for PCI) driver for Linux operating system
+
+	Copyright (C) 2002-2003 Jouni Kujala, Flexibilis Oy.
+
+	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.
+
+	All the drivers derived from or based on this code fall under the
+	GPL and must retain the copyright and license notice.
+*/
+
+#define DRV_NAME	"retina"
+#define DRV_VERSION	"1.2.6"
+
+#define DBG_PRINT(xyz...)	/* printk(KERN_DEBUG xyz) */
+
+/* uncomment this if you want to have debug files in /proc
+ * #define DEBUG_PROC_FILES */
+
+/* Keep this if you want to have point-to-point links
+ ! Only interfaces listed in retina_ptp_interfaces will
+   be created in PtP mode
+  see retina_ptp_interfaces */
+#define FEPCI_POINT_TO_POINT
+
+/* need to update MODULE_PARM also */
+#define MAX_DEVICES 32u
+
+#define MAX_TX_UNITS  256u
+#define MAX_RX_UNITS  256u
+
+#define MAX_UNIT_SZ_ORDER  10u
+
+#define TX_RING_SIZE	8u
+#define RX_RING_SIZE	8u
+
+/* need to update MODULE_PARM also */
+#define CHANNELS        4u
+
+#define RX_FIFO_THRESHOLD_PACKET_MODE 0x4
+#define TX_FIFO_THRESHOLD_PACKET_MODE 0x4
+#define TX_DESC_THRESHOLD_PACKET_MODE 0x4
+
+#define RX_FIFO_THRESHOLD_STREAM_MODE 0x4
+#define TX_FIFO_THRESHOLD_STREAM_MODE 0x7
+#define TX_DESC_THRESHOLD_STREAM_MODE 0x1
+
+/* need to update MODULE_PARM also */
+#define MAX_INTERFACES (CHANNELS*MAX_DEVICES)
+
+const char fepci_name[] = "retina";
+const char fepci_alarm_manager_name[] = "retina alarm manager";
+const char fepci_NAME[] = "RETINA";
+const char fepci_netdev_name[] = "dcpxx";
+const char fepci_proc_entry_name[] = "driver/retina";
+
+static unsigned int find_cnt;
+
+#ifdef FEPCI_POINT_TO_POINT
+static char *retina_ptp_interfaces[MAX_INTERFACES];
+static int retina_noarp_with_ptp = 1;
+#endif /* FEPCI_POINT_TO_POINT */
+
+#define fepci_features_proc_entry_name "driver/retina/%02x:%02x.%02x/features"
+#define fepci_settings_proc_entry_name "driver/retina/%02x:%02x.%02x/settings"
+#define fepci_status_proc_entry_name "driver/retina/%02x:%02x.%02x/status"
+
+#ifdef DEBUG_PROC_FILES
+#define fepci_counters_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/counters_ch%d"
+#define fepci_descriptors_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/descriptors_ch%d"
+#define fepci_stream_counters_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/stream_counters_ch%d"
+#define fepci_registers_proc_entry_name \
+"driver/retina/%02x:%02x.%02x/registers_ch%d"
+#endif /* DEBUG_PROC_FILES */
+
+/* Time in jiffies before concluding that the transmitter is hung */
+#define TX_TIMEOUT  (20*HZ)
+
+#include "retina.h"
+#include <linux/mm.h>
+#include <linux/random.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/version.h>
+#include <linux/pfn.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/unaligned.h>
+#include <asm/pgtable.h>
+
+MODULE_VERSION(DRV_VERSION);
+
+/* PCI I/O space extent */
+#define FEPCI_SIZE 0x20000
+#define PCI_IOTYPE (PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY)
+
+struct pci_id_info {
+	const char *name;
+	struct match_info {
+		int pci, pci_mask, subsystem, subsystem_mask;
+		int revision, revision_mask;	/* Only 8 bits. */
+	} id;
+	unsigned pci_flags;
+	int io_size;		/* Needed for I/O region check or ioremap */
+	int drv_flags;		/* Driver use, intended as capability flags */
+};
+
+static struct pci_id_info pci_id_tbl[] = {
+	{"Frame Engine for PCI (FEPCI)",
+	 {0x1FC00300, 0x1FC00301, 0xffffffff},
+	 PCI_IOTYPE, FEPCI_SIZE},
+	{0,},
+};
+
+static struct pci_device_id fepci_pci_tbl[] __devinitdata = {
+	{0x1FC0, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0x1FC0, 0x0301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{0,}
+};
+
+MODULE_DESCRIPTION("Frame Engine for PCI (FEPCI)");
+MODULE_AUTHOR("Jouni Kujala");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, fepci_pci_tbl);
+
+/* Linux appears to drop POINTOPOINT,BROADCAST and NOARP flags in SIOCSFLAGS
+ * This workaround allows load time per interface ptp mode configuration.
+ * Runtime ptp mode changes would either require changes to Linux or
+ * use of proprietary ioctls, which ifconfig knows nothing about anyway
+ */
+
+static unsigned interfaces = MAX_INTERFACES;
+#ifdef FEPCI_POINT_TO_POINT
+module_param_array(retina_ptp_interfaces, charp, &interfaces, S_IRUGO);
+module_param(retina_noarp_with_ptp, bool, S_IRUGO);
+MODULE_PARM_DESC(retina_noarp_with_ptp,
+		 "0 to disable NOARP, "
+		 "1 to enable NOARP on pointopoint interfaces");
+#endif
+
+struct fepci_ch_private {
+	unsigned int channel_number;
+	struct net_device *this_dev;
+
+	struct fepci_card_private *this_card_priv;
+
+	unsigned int reg_rxctrl;
+	unsigned int reg_txctrl;
+
+	struct fepci_desc *rx_desc;	/* rx_ring start */
+	struct fepci_desc *tx_desc;	/* tx_ring start */
+	struct sk_buff *rx_skbuff[RX_RING_SIZE];
+	struct sk_buff *tx_skbuff[TX_RING_SIZE];
+
+	struct timer_list timer;
+	struct net_device_stats stats;
+
+	unsigned drv_flags;
+
+	unsigned int rx_buf_sz;	/*  MTU+slack */
+	unsigned int cur_tx;	/* the next filled tx_descriptor */
+	/* in stream mode the desc which is being transmitted */
+	/* rx_descriptor where next packet transferred */
+	unsigned int cur_rx;
+	/* in stream mode the desc which is being received */
+
+/* stream mode: */
+	bool in_eth_mode;
+	bool in_stream_mode;
+	bool stream_on;
+	u32 *rx_buffer;
+	u32 *tx_buffer;
+	unsigned bufsize_order;	/* 10=1kB,11=2kB,12=4kB...16=64kB */
+	unsigned bufsize;
+	unsigned unit_sz_order;	/* 8=256B...14=16kB */
+	unsigned unit_sz;
+	unsigned units;		/* 2,4,8,16,...,256 */
+	/* fake units (and pointers) are for faking larger unit sizes to
+	 * the user than what is the maximum internal unit size in FEPCI */
+	unsigned fake_unit_sz_order;
+	unsigned fake_unit_sz;
+	unsigned fake_units;
+	u32 *tx_unit[MAX_TX_UNITS];
+	u32 *rx_unit[MAX_RX_UNITS];
+	unsigned cur_tx_unit;	/* last sent tx_unit */
+	/* rx_unit where to next packet is transferred */
+	unsigned cur_rx_unit;
+/* char device: */
+	unsigned minor;		/* currently the same as card_nuber */
+/* debugging stuff for packet mode: */
+	/* rx-errors in descriptors */
+	unsigned int rx_desc_fifo_err;
+	unsigned int rx_desc_size_err;
+	unsigned int rx_desc_crc_err;
+	unsigned int rx_desc_octet_err;
+	unsigned rx_desc_line_err;
+	/* tx-errors in descriptors */
+	unsigned tx_desc_fifo_err;
+	/* rx-errors in interrupts */
+	unsigned rx_int_fifo_err;
+	unsigned rx_int_frame_dropped_err;
+	/* tx-errors in interrupts */
+	unsigned tx_int_fifo_err;
+	/* ints */
+	unsigned interrupts;
+	unsigned rx_interrupts;
+	unsigned tx_interrupts;
+	/* error combination tables */
+	/* fifo,size,crc,octet,line */
+	unsigned rx_desc_err_table[2][2][2][2][2];
+	unsigned int_err_table[2][2];	/* fifo,frame_dropped */
+	/* skbuff counters */
+	unsigned rx_skbuffs_in;
+	unsigned rx_skbuffs_out;
+	unsigned tx_skbuffs_in;
+	unsigned tx_skbuffs_out;
+/* debugging stuff for stream mode: */
+	/* rx-errors in descriptors */
+	unsigned rx_desc_fifo_err_stream;
+	unsigned rx_desc_size_err_stream;
+	unsigned rx_desc_crc_err_stream;
+	unsigned rx_desc_octet_err_stream;
+	unsigned rx_desc_line_err_stream;
+	/* tx-errors in descriptors */
+	unsigned tx_desc_fifo_err_stream;
+	/* rx-errors in interrupts */
+	unsigned rx_int_fifo_err_stream;
+	unsigned rx_int_frame_dropped_err_stream;
+	/* tx-errors in interrupts */
+	unsigned tx_int_fifo_err_stream;
+	/* ints */
+	unsigned interrupts_stream;
+	unsigned rx_interrupts_stream;
+	unsigned tx_interrupts_stream;
+	/* error combination tables */
+	/* fifo,size,crc,octet,line */
+	unsigned rx_desc_err_table_stream[2][2][2][2][2];
+	unsigned int_err_table_stream[2][2];	/* fifo,frame_dropped */
+/* other: */
+	/* tx interrupts since last timer interrupt */
+	unsigned tx_interrupts_since_last_timer;
+/* small packet counters: */
+	unsigned rx_packets_of_size_0;
+	unsigned rx_packets_of_size_1;
+	unsigned rx_packets_of_size_2;
+	unsigned rx_packets_of_size_3;
+	unsigned rx_packets_of_size_4_7;
+	unsigned rx_packets_of_size_8_15;
+	unsigned rx_packets_of_size_16_31;
+/* small packet counters for stream: */
+	unsigned rx_packets_of_size_0_stream;
+	unsigned rx_packets_of_size_1_stream;
+	unsigned rx_packets_of_size_2_stream;
+	unsigned rx_packets_of_size_3_stream;
+	unsigned rx_packets_of_size_4_7_stream;
+	unsigned rx_packets_of_size_8_15_stream;
+	unsigned rx_packets_of_size_16_31_stream;
+};
+
+struct fepci_card_private {
+	unsigned int card_number;
+	u8 *ioaddr;
+	/* Process ID of the current mailbox user
+	 * (for whom it is reserved for) */
+	unsigned int ioctl_saved_pid;
+	struct pci_dev *pci_dev;
+	struct fepci_ch_private *ch_privates[CHANNELS];
+
+	wait_queue_head_t alarm_manager_wait_q;
+	struct timer_list mailbox_timer;
+
+	wait_queue_head_t stream_receive_q;
+	wait_queue_head_t stream_transmit_q;
+	wait_queue_head_t stream_both_q;
+
+	struct rw_semaphore semaphore;
+};
+
+/* Offsets to the FEPCI registers */
+enum fepci_offsets {
+	reg_custom = 0x40,
+
+	reg_first_int_mask = 0x80,
+	reg_first_int_status = 0xc0,
+
+	reg_first_rxctrl = 0x4000,
+	to_next_rxctrl = 0x80,
+
+	reg_first_txctrl = 0x6000,
+	to_next_txctrl = 0x80,
+
+	first_rx_desc = 0x10000,
+	to_next_ch_rx_desc = 0x200,
+
+	first_tx_desc = 0x18000,
+	to_next_ch_tx_desc = 0x200,
+};
+
+enum reg_custom_bits {
+	AM_interrupt_mask = 0x1,
+	AM_interrupt_status = 0x100,
+};
+
+enum reg_receive_control {
+	Rx_fifo_threshold = 0x7,
+	Receive_enable = 0x80000000,
+};
+
+enum reg_transmit_control {
+	Tx_fifo_threshold = 0x7,
+	Tx_desc_threshold = 0x700,
+	Transmit_enable = 0x80000000,
+};
+
+enum int_bits {
+	MaskFrameReceived = 0x01, MaskRxFifoError =
+	    0x02, MaskRxFrameDroppedError = 0x04,
+	MaskFrameTransmitted = 0x40, MaskTxFifoError = 0x80,
+	MaskAllInts = 0xc7,
+	IntrFrameReceived = 0x01, IntrRxFifoError =
+	    0x02, IntrRxFrameDroppedError = 0x04,
+	IntrFrameTransmitted = 0x40, IntrTxFifoError = 0x80,
+	IntrAllInts = 0xc7,
+};
+
+/* The FEPCI Rx and Tx buffer descriptors
+ * Elements are written as 32 bit for endian portability */
+
+struct fepci_desc {
+	u32 desc_a;
+	u32 desc_b;
+};
+
+enum desc_b_bits {
+	frame_length = 0xFFF,
+	fifo_error = 0x10000,
+	size_error = 0x20000,
+	crc_error = 0x40000,
+	octet_error = 0x80000,
+	line_error = 0x100000,
+	enable_transfer = 0x80000000,
+	transfer_not_done = 0x80000000,
+};
+
+/* global variables (common to whole driver, all the cards): */
+int major;			/* char device major number */
+struct fepci_card_private card_privates[MAX_DEVICES];
+unsigned long stream_pointers;
+struct proc_dir_entry *proc_root_entry;
+
+void set_int_mask(int channel, u_char value, struct fepci_card_private *cp)
+{
+	void *address;
+	unsigned shift, oldvalue;
+	DBG_PRINT("set_int_mask\n");
+	address = (cp->ioaddr) + reg_first_int_mask + (channel / 4L) * 4L;
+	shift = 8L * (channel % 4L);
+	oldvalue = readl((void *)address);
+	oldvalue &= ~(0xff << shift);	/* clear bits */
+	oldvalue |= value << shift;	/* set bits */
+	writel(oldvalue, (void *)address);
+}
+
+u_char get_int_mask(int channel, void *ioaddr)
+{
+	void *address;
+	unsigned shift, oldvalue;
+	DBG_PRINT("get_int_mask\n");
+	address = ioaddr + reg_first_int_mask + (channel / 4L) * 4L;
+	shift = 8L * (channel % 4L);
+	oldvalue = readl((void *)address);
+	oldvalue &= (0xff << shift);	/* clear other bits */
+	return (oldvalue >> shift);
+}
+
+void clear_int(int channel, u_char value, void *ioaddr)
+{
+	void *address;
+	unsigned shift, longvalue;
+	DBG_PRINT("clear_int\n");
+	address = ioaddr + reg_first_int_status + (channel / 4L) * 4L;
+	shift = 8L * (channel % 4L);
+	longvalue = value << shift;
+	writel(~longvalue, (void *)address);
+}
+
+u_char get_int_status(int channel, void *ioaddr)
+{
+	void *address;
+	unsigned shift, oldvalue;
+	DBG_PRINT("get_int_status\n");
+	address = ioaddr + reg_first_int_status + (channel / 4L) * 4L;
+	shift = 8L * (channel % 4L);
+	oldvalue = readl((void *)address);
+	oldvalue &= (0xff << shift);	/* clear other bits */
+	return (oldvalue >> shift);
+}
+
+void fillregisterswith_00(void *ioaddr)
+{
+	DBG_PRINT("fillregisterswith_00\n");
+	writel(0x0, (void *)(ioaddr + reg_first_rxctrl));
+	writel(0x0, (void *)(ioaddr + reg_first_txctrl));
+	writel(0x0, (void *)(ioaddr + reg_first_int_mask));
+	writel(0x0, (void *)(ioaddr + reg_first_int_status));
+	writel(0x0, (void *)(ioaddr + first_rx_desc));
+	writel(0x0, (void *)(ioaddr + first_tx_desc));
+}
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int fepci_open(struct net_device *dev);
+static void fepci_timer(unsigned long data);
+static void fepci_tx_timeout(struct net_device *dev);
+static void fepci_init_ring(struct net_device *dev);
+static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t fepci_interrupt(int irq, void *dev_instance);
+static int fepci_rx(struct net_device *dev);
+static int fepci_close(struct net_device *dev);
+static struct net_device_stats *fepci_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static void fepci_remove_one(struct pci_dev *pdev);
+
+/* proc filesystem functions introduced: */
+
+static int fepci_proc_init_driver(void);
+static void fepci_proc_cleanup_driver(void);
+static void fepci_proc_init_card(int card_number, void *card_data);
+static void fepci_proc_cleanup_card(int card_number);
+#ifdef DEBUG_PROC_FILES
+static void fepci_proc_init_channel(int card_number, int channel_number,
+				    void *channel_data);
+static void fepci_proc_cleanup_channel(int card_number, int channel_number);
+#endif /* DEBUG_PROC_FILES */
+
+/* char device operations: */
+
+ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+			loff_t *f_pos);
+int fepci_char_open(struct inode *inode, struct file *filp);
+int fepci_char_release(struct inode *inode, struct file *filp);
+int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma);
+int fepci_char_ioctl(struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg);
+
+struct file_operations fepci_char_fops = {
+read:		fepci_char_read,
+ioctl:		fepci_char_ioctl,
+open:		fepci_char_open,
+release:	fepci_char_release,
+mmap:		fepci_char_mmap,
+};
+
+int fepci_char_open(struct inode *inode, struct file *filp)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	DBG_PRINT("fepci_char_open\n");
+	if (unlikely(minor >= find_cnt || card_privates[minor].pci_dev == NULL))
+		return -ENXIO;
+	filp->f_op = &fepci_char_fops;
+	if (unlikely(!try_module_get(THIS_MODULE)))
+		return -EBUSY;
+	return 0;
+}
+
+int fepci_char_release(struct inode *inode, struct file *filp)
+{
+	DBG_PRINT("fepci_char_release\n");
+	module_put(THIS_MODULE);
+	return 0;
+}
+
+void fepci_vma_open(struct vm_area_struct *vma)
+{
+	DBG_PRINT("fepci_vma_open\n");
+}
+
+void fepci_vma_close(struct vm_area_struct *vma)
+{
+	DBG_PRINT("fepci_vma_close\n");
+	module_put(THIS_MODULE);
+}
+
+static struct vm_operations_struct fepci_vm_ops = {
+open:	fepci_vma_open,
+close:	fepci_vma_close,
+};
+
+int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+	unsigned long size = vma->vm_end - vma->vm_start;
+
+	unsigned long virtual_address = 0;
+
+	vma->vm_flags |= VM_IO | VM_RESERVED;
+	vma->vm_ops = &fepci_vm_ops;
+	vma->vm_file = filp;
+
+	if (offset == STREAM_BUFFER_POINTER_AREA) {
+		virtual_address = stream_pointers;
+		if (virtual_address == 0) {
+			printk(KERN_WARNING "%s: mmap: internal error.\n",
+			       fepci_name);
+			return -ENOMEM;
+		}
+		if (size > (1 << PAGE_SHIFT)) {
+			printk(KERN_WARNING
+			       "%s: mmap: area size over range.\n", fepci_name);
+			return -EINVAL;
+		}
+	} else {
+		unsigned int page;
+
+		unsigned int card;
+		unsigned int channel;
+		unsigned int area;	/* 0=rx, 1=tx */
+
+		card = (offset >> CARD_ADDRESS_SHIFT) & 0xf;
+		channel = (offset >> CHANNEL_ADDRESS_SHIFT) & 0xf;
+		area = (offset >> AREA_ADDRESS_SHIFT) & 0xf;
+		page = (offset & 0xffff);	/* >> PAGE_SHIFT; */
+
+		if (area == 0) {
+			/* if there really is such card */
+			if (card < find_cnt && card_privates[card].pci_dev)
+				virtual_address =
+				    (unsigned long)card_privates[card].
+				    ch_privates[channel]->rx_buffer;
+			else
+				goto INVALID;
+		} else if (area == 1) {
+			/* if there really is such card */
+			if (card < find_cnt && card_privates[card].pci_dev)
+				virtual_address =
+				    (unsigned long)card_privates[card].
+				    ch_privates[channel]->tx_buffer;
+			else
+				goto INVALID;
+		} else {
+INVALID:
+			DBG_PRINT("%s: mmap: invalid address 0x%lx\n",
+				  fepci_NAME, virtual_address);
+			return -EINVAL;
+		}
+		if (virtual_address == 0)
+			goto INVALID;
+	}
+
+	if (!try_module_get(THIS_MODULE))
+		return -EBUSY;
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	{
+		unsigned pfn = PFN_DOWN(virt_to_phys((void *)virtual_address));
+		int error = io_remap_pfn_range(vma, vma->vm_start, pfn,
+					       size, vma->vm_page_prot);
+		if (unlikely(error))
+			return error;
+	}
+	fepci_vma_open(vma);
+	return 0;
+}
+
+/* mmap operations end */
+
+/* char operations start */
+
+void fepci_copy_to_user(unsigned long to, void *from, unsigned long len,
+			int shrink)
+{
+	unsigned int i;
+	DBG_PRINT("fepci_copy_to_user\n");
+	if (shrink) {
+		for (i = 0; i < len; i += 2) {
+			put_user((((unsigned long *)from)[i / 2]) & 0xff,
+				 (unsigned char *)(to + i));
+			put_user((((unsigned long *)from)[i / 2]) >> 8,
+				 (unsigned char *)(to + i + 1));
+		}
+	} else {
+		for (i = 0; i < len; i += 4)
+			put_user(((unsigned long *)from)[i / 4],
+				 (unsigned long *)(to + i));
+	}
+}
+
+void fepci_copy_from_user(void *to, unsigned long from, unsigned long len,
+			  int enlarge)
+{
+	unsigned int i;
+	if (enlarge) {
+		for (i = 0; i < len; i += 2) {
+			unsigned char temp1;
+			unsigned char temp2;
+			get_user(temp1, (unsigned char *)(from + i));
+			get_user(temp2, (unsigned char *)(from + i + 1));
+			*((unsigned long *)(to + i * 2)) = temp1 + (temp2 << 8);
+		}
+	} else {
+		for (i = 0; i < len; i += 4)
+			get_user(((unsigned long *)to)[i / 4],
+				 (unsigned long *)(from + i));
+	}
+}
+
+unsigned get_semafore(struct fepci_real_mailbox *mailbox)
+{
+	unsigned semafore = readb(&mailbox->Semafore_Mail_number);
+	DBG_PRINT("get_semafore = %x\n", semafore);
+	return semafore;
+}
+
+int set_semafore(struct fepci_real_mailbox *mailbox, unsigned semafore)
+{
+	unsigned number = readl(&mailbox->Semafore_Mail_number);
+	DBG_PRINT("got number %u at %p.\n", number,
+		  &mailbox->Semafore_Mail_number);
+	number = ((number & ~0xFF) | semafore) + (1 << 8);
+	DBG_PRINT
+	    ("increases the mail number to %u at the same time at %p.\n",
+	     number, &mailbox->Semafore_Mail_number);
+	writel(number, &mailbox->Semafore_Mail_number);
+	DBG_PRINT("set_semafore %p, %u returns 0.\n", mailbox, semafore);
+	return 0;
+}
+
+static void fepci_mailbox_timer(unsigned long data)
+{
+	int card_number = data;
+	unsigned int *saved_pid = &card_privates[card_number].ioctl_saved_pid;
+	void *ioaddr = card_privates[card_number].ioaddr;
+	struct fepci_real_mailbox *real_mailbox =
+	    (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
+
+	set_semafore(real_mailbox, 0x0);
+	*saved_pid = 0;
+}
+
+int fepci_char_ioctl(struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	void *ioaddr;
+	struct fepci_real_mailbox *real_mailbox;
+	int retval = 0;
+	unsigned int *saved_pid;
+	unsigned int my_pid;
+
+	if (minor >= find_cnt || card_privates[minor].pci_dev == NULL) {
+		printk(KERN_WARNING
+		       "%s: trying to access a card that does not exist.\n",
+		       fepci_NAME);
+		/* if trying to access a card that does not exist */
+		return -ENXIO;
+	}
+
+	DBG_PRINT("fepci_char_ioctl minor %u.\n", minor);
+
+	if (_IOC_DIR(cmd) & _IOC_READ)
+		if (!access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)))
+			return -EFAULT;
+	if (_IOC_DIR(cmd) & _IOC_WRITE)
+		if (!access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)))
+			return -EFAULT;
+
+	ioaddr = card_privates[minor].ioaddr;
+	real_mailbox =
+	    (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
+	saved_pid = &card_privates[minor].ioctl_saved_pid;
+	my_pid = current->pid;
+
+	switch (cmd) {
+	case FEPCI_IOCTL_STREAM_TRANSMIT_POLL:
+		/* here: arg == channel number */
+		if (arg < 0 || arg >= CHANNELS
+		    || !(card_privates[minor].ch_privates[arg]->stream_on))
+			return 0x2;
+		{
+			u32 pointer = *USER_TX_S_FAKE_POINTER(minor, arg,
+							      stream_pointers);
+			wait_event_interruptible((card_privates[minor].
+						  stream_transmit_q)
+						 ,
+						 (pointer !=
+						  *USER_TX_S_FAKE_POINTER
+						  (minor, arg,
+						   stream_pointers)));
+			return 0x1;
+		}
+		return retval;
+	case FEPCI_IOCTL_STREAM_RECEIVE_POLL:
+		/* here: arg == channel number */
+		if (arg < 0 || arg >= CHANNELS
+		    || !(card_privates[minor].ch_privates[arg]->stream_on))
+			return 0x2;
+		{
+			u32 pointer = *USER_RX_S_FAKE_POINTER(minor, arg,
+							      stream_pointers);
+			wait_event_interruptible((card_privates[minor].
+						  stream_receive_q)
+						 ,
+						 (pointer !=
+						  *USER_RX_S_FAKE_POINTER
+						  (minor, arg,
+						   stream_pointers)));
+			retval = 0x1;
+		}
+		return retval;
+	case FEPCI_IOCTL_STREAM_BOTH_POLL:
+		/* here: arg == channel number */
+		if (arg < 0 || arg >= CHANNELS
+		    || !(card_privates[minor].ch_privates[arg]->stream_on))
+			return 0x2;
+		{
+			u32 temp_tx_pointer =
+			    *USER_TX_S_FAKE_POINTER(minor, arg,
+						    stream_pointers);
+			u32 temp_rx_pointer =
+			    *USER_RX_S_FAKE_POINTER(minor, arg,
+						    stream_pointers);
+
+			wait_event_interruptible((card_privates[minor].
+						  stream_both_q)
+						 ,
+						 (temp_tx_pointer !=
+						  *USER_TX_S_FAKE_POINTER
+						  (minor, arg, stream_pointers))
+						 || (temp_rx_pointer !=
+						     *USER_RX_S_FAKE_POINTER
+						     (minor, arg,
+						      stream_pointers)));
+			retval = 0x1;
+		}
+		return retval;
+	case FEPCI_IOCTL_R_SHARED_MEM:
+		DBG_PRINT(" %s: ioctl read shared mem commanded.\n",
+			  fepci_NAME);
+		fepci_copy_to_user(arg, ioaddr + FEPCI_SHARED_MEM_OFFSETT,
+				   _IOC_SIZE(cmd), 0);
+		break;
+	case FEPCI_IOCTL_W_SHARED_MEM:
+		DBG_PRINT(" %s: ioctl write shared mem commanded.\n",
+			  fepci_NAME);
+		fepci_copy_from_user(ioaddr + FEPCI_SHARED_MEM_OFFSETT,
+				     arg, _IOC_SIZE(cmd), 0);
+		break;
+	case FEPCI_IOCTL_G_IDENTIFICATION:
+		DBG_PRINT(" %s: IOCTL_G_IDENTIFICATION commanded.\n",
+			  fepci_NAME);
+		fepci_copy_to_user(arg,
+				   ioaddr + FEPCI_IDENTIFICATION_OFFSETT,
+				   _IOC_SIZE(cmd), 1);
+		break;
+	case FEPCI_IOCTL_G_FEATURES:
+		DBG_PRINT(" %s: IOCTL_G_FEATURES commanded.\n", fepci_NAME);
+		fepci_copy_to_user(arg, ioaddr + FEPCI_FEATURES_OFFSETT,
+				   _IOC_SIZE(cmd), 1);
+		break;
+	case FEPCI_IOCTL_G_SETTINGS:
+		DBG_PRINT(" %s: IOCTL_G_SETTINGS commanded.\n", fepci_NAME);
+		fepci_copy_to_user(arg, ioaddr + FEPCI_SETTINGS_OFFSETT,
+				   _IOC_SIZE(cmd), 1);
+		break;
+	case FEPCI_IOCTL_G_STATUS:
+		DBG_PRINT(" %s: IOCTL_G_STATUS commanded.\n", fepci_NAME);
+		fepci_copy_to_user(arg, ioaddr + FEPCI_STATUS_OFFSETT,
+				   _IOC_SIZE(cmd), 1);
+		break;
+	case FEPCI_IOCTL_B_POLL:
+		DBG_PRINT(" %s: IOCTL_B_POLL commanded.\n", fepci_NAME);
+		retval = get_semafore(real_mailbox);
+		if ((retval == 0x20 || retval == 0x21 || retval == 0x40)
+		    && *saved_pid != my_pid)
+			retval = 0x7;
+		del_timer_sync(&card_privates[minor].mailbox_timer);
+		card_privates[minor].mailbox_timer.expires = jiffies + 20 * HZ;
+		card_privates[minor].mailbox_timer.data = minor;
+		card_privates[minor].mailbox_timer.function =
+		    &fepci_mailbox_timer;
+		add_timer(&card_privates[minor].mailbox_timer);
+		break;
+	case FEPCI_IOCTL_B_GRAB:
+		DBG_PRINT("%s: IOCTL_B_GRAB commanded.\n", fepci_NAME);
+		if ((my_pid != *saved_pid) && (*saved_pid != 0)) {
+			retval = 0x2;
+			break;
+		}
+		DBG_PRINT("%s: IOCTL_B_GRAB getting semaphore.\n", fepci_NAME);
+		if (get_semafore(real_mailbox) == 0x0) {
+			DBG_PRINT("%s: IOCTL_B_GRAB setting semaphore.\n",
+				  fepci_NAME);
+			set_semafore(real_mailbox, 0x40);
+			DBG_PRINT("%s: IOCTL_B_GRAB sleeping.\n", fepci_NAME);
+			msleep(1);	/* delay at least 1 millisecond */
+			DBG_PRINT
+			    ("%s: IOCTL_B_GRAB getting semaphore again.\n",
+			     fepci_NAME);
+			switch (get_semafore(real_mailbox)) {
+			case 0x40:
+				retval = 0x0;
+				DBG_PRINT
+				    ("%s: IOCTL_B_GRAB saving pid to %p.\n",
+				     fepci_NAME, saved_pid);
+				*saved_pid = my_pid;
+				DBG_PRINT
+				    ("%s: IOCTL_B_GRAB deleting timer at %p.\n",
+				     fepci_NAME,
+				     &card_privates[minor].mailbox_timer);
+				del_timer_sync(&card_privates[minor].
+					       mailbox_timer);
+				card_privates[minor].mailbox_timer.
+				    expires = jiffies + 20 * HZ;
+				card_privates[minor].mailbox_timer.data = minor;
+				card_privates[minor].mailbox_timer.
+				    function = &fepci_mailbox_timer;
+				add_timer(&card_privates[minor].mailbox_timer);
+				break;
+			case 0x10:
+			case 0x11:
+			case 0x80:
+				retval = 0x1;
+				break;
+			default:
+				retval = 0xff;
+			}
+		} else {
+			switch (get_semafore(real_mailbox)) {
+			case 0x10:
+			case 0x11:
+			case 0x80:
+				retval = 0x1;
+				break;
+			default:
+				retval = 0xff;
+			}
+		}
+		break;
+	case FEPCI_IOCTL_B_RELEASE:
+		DBG_PRINT(" %s: IOCTL_B_RELEASE commanded.\n", fepci_NAME);
+		if (my_pid != *saved_pid) {
+			retval = 0x2;
+			break;
+		}
+		switch (get_semafore(real_mailbox)) {
+		case 0x40:
+		case 0x20:
+			retval = 0x0;
+			set_semafore(real_mailbox, 0x0);
+			*saved_pid = 0;
+			del_timer(&card_privates[minor].mailbox_timer);
+			break;
+		case 0x21:
+			retval = 0x04;
+			break;
+		case 0x10:
+		case 0x11:
+		case 0x80:
+			retval = 0x1;
+			break;
+		default:
+			retval = 0xff;
+		}
+		break;
+	case FEPCI_IOCTL_B_S_CMAIL:
+		DBG_PRINT(" %s: IOCTL_B_S_CMAIL commanded.\n", fepci_NAME);
+		if (my_pid != *saved_pid) {
+			retval = 0x2;
+			break;
+		}
+		switch (get_semafore(real_mailbox)) {
+		case 0x40:
+		case 0x20:
+		case 0x21:
+			/* copy the mailbox */
+			fepci_copy_from_user(ioaddr +
+					     FEPCI_MAILBOX_OFFSETT + 4,
+					     arg + 2, _IOC_SIZE(cmd) - 2, 1);
+			/* semafore -> 10 */
+			set_semafore(real_mailbox, 0x10);
+			retval = 0x0;
+
+			del_timer_sync(&card_privates[minor].mailbox_timer);
+			card_privates[minor].mailbox_timer.expires =
+			    jiffies + 20 * HZ;
+			card_privates[minor].mailbox_timer.data = minor;
+			card_privates[minor].mailbox_timer.function =
+			    &fepci_mailbox_timer;
+			add_timer(&card_privates[minor].mailbox_timer);
+
+			break;
+
+		case 0x10:
+		case 0x11:
+		case 0x80:
+			retval = 0x1;
+			break;
+		case 0x0:
+			retval = 0x3;
+			break;
+		default:
+			retval = 0xff;
+		}
+		break;
+	case FEPCI_IOCTL_B_S_QMAIL:
+		DBG_PRINT(" %s: IOCTL_B_S_QMAIL commanded.\n", fepci_NAME);
+		if (my_pid != *saved_pid) {
+			retval = 0x2;
+			break;
+		}
+		switch (get_semafore(real_mailbox)) {
+		case 0x40:
+		case 0x20:
+		case 0x21:
+			/* copy the mailbox; */
+			fepci_copy_from_user(ioaddr +
+					     FEPCI_MAILBOX_OFFSETT + 4,
+					     arg + 2, _IOC_SIZE(cmd) - 2, 1);
+			/* semafore -> 11 */
+			set_semafore(real_mailbox, 0x11);
+			retval = 0x0;
+
+			del_timer_sync(&card_privates[minor].mailbox_timer);
+			card_privates[minor].mailbox_timer.expires =
+			    jiffies + 20 * HZ;
+			card_privates[minor].mailbox_timer.data = minor;
+			card_privates[minor].mailbox_timer.function =
+			    &fepci_mailbox_timer;
+			add_timer(&card_privates[minor].mailbox_timer);
+
+			break;
+
+		case 0x10:
+		case 0x11:
+		case 0x80:
+			retval = 0x1;
+			break;
+		case 0x0:
+			retval = 0x3;
+			break;
+		default:
+			retval = 0xff;
+		}
+		break;
+	case FEPCI_IOCTL_B_G_MAIL:
+		DBG_PRINT(" %s: IOCTL_B_G_MAIL commanded.\n", fepci_NAME);
+		if (my_pid != *saved_pid) {
+			retval = 0x2;
+		} else {
+			switch (get_semafore(real_mailbox)) {
+			case 0x10:
+			case 0x11:
+			case 0x80:
+				retval = 0x1;
+				break;
+			case 0x40:
+			case 0x20:
+			case 0x21:
+				retval = 0x0;
+				fepci_copy_to_user(arg,
+						   ioaddr +
+						   FEPCI_MAILBOX_OFFSETT,
+						   _IOC_SIZE(cmd), 1);
+
+				del_timer_sync(&card_privates[minor].
+					       mailbox_timer);
+				card_privates[minor].mailbox_timer.
+				    expires = jiffies + 20 * HZ;
+				card_privates[minor].mailbox_timer.data = minor;
+				card_privates[minor].mailbox_timer.
+				    function = &fepci_mailbox_timer;
+				add_timer(&card_privates[minor].mailbox_timer);
+
+				break;
+			case 0x0:
+				retval = 0x3;
+				break;
+			default:
+				retval = 0xff;
+			}
+		}
+		if (retval != 0) {
+			static unsigned char seven = 7;
+			/* copy four lowest bytes from the mailbox */
+			fepci_copy_to_user(arg,
+					   ioaddr + FEPCI_MAILBOX_OFFSETT,
+					   4, 1);
+			/* lowest byte = 0x7 */
+			__put_user(arg, &seven);
+		}
+		break;
+	case FEPCI_IOCTL_ALARM_MANAGER:
+		DBG_PRINT(" %s: IOCTL_ALARM_MANAGER commanded.\n", fepci_NAME);
+		interruptible_sleep_on(&(card_privates[minor].
+					 alarm_manager_wait_q));
+		return retval;
+	default:
+		DBG_PRINT(" %s: Unknown ioctl command 0x%x.\n", fepci_NAME,
+			  cmd);
+		return -ENOTTY;
+	}
+	return retval;
+}
+
+ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+			loff_t *f_pos)
+{
+	DBG_PRINT("fepci_char_read\n");
+	if (count > 1)
+		count = 1;
+	if (unlikely(copy_to_user(buf, "\n", count)))
+		return -EFAULT;
+	return count;
+}
+
+static int fepci_register_char_device(void)
+{
+	int error =
+	    register_chrdev(0 /* dynamic */ , fepci_name, &fepci_char_fops);
+	if (unlikely(error < 0))
+		printk(KERN_WARNING
+		       "%s: unable to register char device.\n", fepci_NAME);
+	else
+		DBG_PRINT("%s: registered char device, major:0x%x.\n",
+			  fepci_NAME, error);
+	return error;
+}
+
+void fepci_unregister_char_device(void)
+{
+	unregister_chrdev(major, fepci_name);
+}
+
+/* char operations end */
+
+/* stream operations start */
+
+static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance);
+static int fepci_stream_close(struct net_device *dev);
+
+static int fepci_stream_open(struct net_device *dev)
+{
+	struct fepci_ch_private *fp = dev->priv;
+	unsigned tx_pages, rx_pages, tx_order, rx_order;
+	unsigned page_number;
+	unsigned int i;
+
+	down_write(&fp->this_card_priv->semaphore);
+	if (fp->in_eth_mode) {
+		up_write(&fp->this_card_priv->semaphore);
+		printk(KERN_WARNING
+		       "%s: Interface is in Ethernet mode, "
+		       "cannot open stream interface.\n", fepci_NAME);
+BUSY:
+		return -EBUSY;
+	}
+	if (fp->in_stream_mode) {
+		up_write(&fp->this_card_priv->semaphore);
+		printk(KERN_WARNING
+		       "%s: Interface is already in stream mode, "
+		       "cannot open stream interface.\n", fepci_NAME);
+		goto BUSY;
+	}
+
+	if (unlikely(fp->this_card_priv->pci_dev == NULL)) {
+		up_write(&fp->this_card_priv->semaphore);
+		return -ENXIO;
+	}
+
+	fp->bufsize = 1 << fp->bufsize_order;
+
+	if (fp->fake_unit_sz_order < 5) {
+		up_write(&fp->this_card_priv->semaphore);
+		printk(KERN_WARNING
+		       "%s: Unit size has to be at least 32 Bytes.\n",
+		       fepci_NAME);
+INVALID:
+		return (-EINVAL);
+	}
+
+	if (fp->fake_unit_sz_order >= fp->bufsize_order) {
+		up_write(&fp->this_card_priv->semaphore);
+		printk(KERN_WARNING
+		       "%s: Bufsize has to be greater than unit size.\n",
+		       fepci_NAME);
+		goto INVALID;
+	}
+
+	if (fp->fake_unit_sz_order >= MAX_UNIT_SZ_ORDER) {
+		fp->unit_sz_order = MAX_UNIT_SZ_ORDER;
+	} else {
+		fp->unit_sz_order = fp->fake_unit_sz_order;
+	}
+
+	fp->fake_unit_sz = 1 << fp->fake_unit_sz_order;
+	fp->unit_sz = 1 << fp->unit_sz_order;
+	fp->units = 1 << (fp->bufsize_order - fp->unit_sz_order);
+	fp->fake_units = 1 << (fp->bufsize_order - fp->fake_unit_sz_order);
+
+	/* reserve memory */
+	if (fp->bufsize_order < PAGE_SHIFT) {
+		rx_order = 0;
+		tx_order = 0;
+		rx_pages = 1;
+		tx_pages = 1;
+	} else {
+		tx_order = fp->bufsize_order - PAGE_SHIFT;
+		tx_pages = 1 << tx_order;
+		rx_order = tx_order + 1;
+		rx_pages = 1 << rx_order;
+	}
+	fp->in_stream_mode = 1;
+	fp->tx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, tx_order);
+	if (!fp->tx_buffer)
+		goto NO_MEMORY;
+	fp->rx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, rx_order);
+	if (!fp->rx_buffer) {
+NO_MEMORY:
+		up_write(&fp->this_card_priv->semaphore);
+		printk(KERN_WARNING
+		       "%s: unable to allocate memory for buffers.\n",
+		       fepci_NAME);
+		fepci_stream_close(dev);
+		return (-ENOMEM);
+	}
+
+	for (page_number = 0; page_number < rx_pages; page_number++)
+		/* make pages reserved to allow remappping pages
+		   with io_remap_pfn_range */
+		SetPageReserved(virt_to_page
+				((unsigned long)fp->rx_buffer +
+				 (page_number << PAGE_SHIFT)));
+	for (page_number = 0; page_number < tx_pages; page_number++)
+		/* make pages reserved to allow remappping pages
+		   with io_remap_pfn_range */
+		SetPageReserved(virt_to_page
+				((unsigned long)fp->tx_buffer +
+				 (page_number << PAGE_SHIFT)));
+
+	for (i = 0; i < (fp->bufsize) / 4; i++)
+		fp->tx_buffer[i] = 0xffffffff;
+
+	/* + fp->channel_number; */
+	*USER_RX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
+			   stream_pointers) = 0;
+	/* + fp->channel_number; */
+	*USER_TX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
+			   stream_pointers) = 0;
+	/* + fp->channel_number; */
+	*USER_RX_S_FAKE_POINTER(fp->this_card_priv->card_number,
+				fp->channel_number, stream_pointers) = 0;
+	/* + fp->channel_number; */
+	*USER_TX_S_FAKE_POINTER(fp->this_card_priv->card_number,
+				fp->channel_number, stream_pointers) = 0;
+
+	DBG_PRINT("%s: Bufsize is 0x%x.\n", fepci_NAME, fp->bufsize);
+	DBG_PRINT("%s: Unit_size is 0x%x.\n", fepci_NAME, fp->unit_sz);
+	DBG_PRINT("%s: Number of units is 0x%x.\n", fepci_NAME, fp->units);
+
+	DBG_PRINT("%s: Fake_unit_size is 0x%x.\n", fepci_NAME,
+		  fp->fake_unit_sz);
+	DBG_PRINT("%s: Number of fake units is 0x%x.\n", fepci_NAME,
+		  fp->fake_units);
+
+	/* init ring buffers */
+	for (i = 0; i < MAX_RX_UNITS; i++)
+		fp->rx_unit[i] =
+		    (u32 *) ((u32) (fp->rx_buffer) + (fp->unit_sz * i));
+	for (i = 0; i < MAX_TX_UNITS; i++)
+		fp->tx_unit[i] =
+		    (u32 *) ((u32) (fp->tx_buffer) + (fp->unit_sz * i));
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		writel(0, &fp->rx_desc[i].desc_a);
+		writel(0, &fp->rx_desc[i].desc_b);
+	}
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		writel(0, &fp->tx_desc[i].desc_a);
+		writel(0, &fp->tx_desc[i].desc_b);
+	}
+
+	up_write(&fp->this_card_priv->semaphore);
+	return 0;
+}
+
+static int fepci_stream_start(struct net_device *dev)
+{
+	struct fepci_ch_private *fp = dev->priv;
+	unsigned i;
+	down_write(&fp->this_card_priv->semaphore);
+
+	if (fp->in_stream_mode == 0) {
+		up_write(&fp->this_card_priv->semaphore);
+		printk(KERN_WARNING
+		       "%s: Interface is not in stream mode, "
+		       "streaming cannot be started.\n", fepci_NAME);
+		return (-EBUSY);
+	}
+	if (fp->stream_on) {
+		up_write(&fp->this_card_priv->semaphore);
+		printk(KERN_WARNING
+		       "%s: Streaming is already on, "
+		       "streaming cannot be started.\n", fepci_NAME);
+		return (-EBUSY);
+	}
+
+	{
+		/* reserve irq */
+		int error = request_irq(dev->irq, &fepci_stream_interrupt,
+					IRQF_SHARED, dev->name, dev);
+		if (error) {
+			up_write(&fp->this_card_priv->semaphore);
+			printk(KERN_WARNING
+			       "%s: unable to allocate IRQ %d, error 0x%x\n",
+			       fepci_NAME, dev->irq, error);
+			return -ENOMEM;
+		}
+	}
+
+	fp->stream_on = 1;
+
+	/* sending &receiving on, start from the beginning of the buffer */
+	fp->cur_tx_unit = 0;
+	fp->cur_rx_unit = 0;
+	fp->cur_tx = 0;
+	fp->cur_rx = 0;
+
+	/* all the descriptors ready to go: */
+	for (i = 0; i < min(RX_RING_SIZE, TX_RING_SIZE); i++) {
+		dma_addr_t address = pci_map_single(fp->this_card_priv->pci_dev,
+						    fp->
+						    rx_unit[(fp->cur_rx_unit +
+							     i) % fp->units],
+						    fp->unit_sz,
+						    PCI_DMA_FROMDEVICE);
+		if (unlikely(pci_dma_mapping_error(address)))
+			printk(KERN_WARNING
+			       "%s: failed to map DMA buffer.\n", fepci_NAME);
+		else {
+			writel(address,
+			       &fp->rx_desc[(fp->cur_rx + i) &
+					    (RX_RING_SIZE - 1)].desc_a);
+			if (!
+			    (readl
+			     (&fp->
+			      rx_desc[(fp->cur_rx + i) & (RX_RING_SIZE -
+							  1)].
+			      desc_b) & enable_transfer))
+				writel(enable_transfer,
+				       &fp->rx_desc[(fp->cur_rx + i) %
+						    RX_RING_SIZE].desc_b);
+		}
+		address =
+		    pci_map_single(fp->this_card_priv->pci_dev,
+				   fp->tx_unit[(fp->cur_tx_unit + i) %
+					       fp->units], fp->unit_sz,
+				   PCI_DMA_TODEVICE);
+		writel(address,
+		       &fp->
+		       tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE - 1)].desc_a);
+		if (unlikely(pci_dma_mapping_error(address)))
+			printk(KERN_WARNING
+			       "%s: failed to map DMA buffer.\n", fepci_NAME);
+		else {
+			if (!
+			    (readl
+			     (&fp->
+			      tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE -
+							  1)].
+			      desc_b) & enable_transfer))
+				writel(enable_transfer |
+				       (fp->unit_sz & frame_length),
+				       &fp->
+				       tx_desc[(fp->cur_tx +
+						i) & (TX_RING_SIZE -
+						      1)].desc_b);
+		}
+	}
+
+	/* irq on */
+	set_int_mask(fp->channel_number,
+		     MaskFrameReceived | MaskFrameTransmitted |
+		     MaskRxFifoError | MaskRxFrameDroppedError |
+		     MaskTxFifoError, fp->this_card_priv);
+	{
+		void *ioaddr = (void *)dev->base_addr;
+		/* Start Rx and Tx channels */
+		writel(Receive_enable |
+		       (Rx_fifo_threshold & RX_FIFO_THRESHOLD_STREAM_MODE),
+		       (void *)(ioaddr + fp->reg_rxctrl));
+		writel((Transmit_enable |
+			(Tx_desc_threshold &
+			 (TX_DESC_THRESHOLD_STREAM_MODE << 8)) |
+			(Tx_fifo_threshold & TX_FIFO_THRESHOLD_STREAM_MODE)),
+		       (void *)(ioaddr + fp->reg_txctrl));
+	}
+	up_write(&fp->this_card_priv->semaphore);
+
+	return 0;
+}
+
+static int fepci_stream_stop(struct net_device *dev)
+{
+	struct fepci_ch_private *fp = dev->priv;
+	void *ioaddr = (void *)dev->base_addr;
+	down_write(&fp->this_card_priv->semaphore);
+	if (fp->in_stream_mode == 0) {
+		up_write(&fp->this_card_priv->semaphore);
+		return (1);
+	}
+	fp->stream_on = 0;
+	/* Stop Rx and Tx channels. */
+	writel(0x0, (void *)(ioaddr + fp->reg_rxctrl));
+	writel(0x0, (void *)(ioaddr + fp->reg_txctrl));
+
+	/* Disable interrupts by clearing the interrupt mask. */
+	set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
+
+	/* unregister irq */
+	free_irq(dev->irq, dev);
+
+	{
+		unsigned i = min(RX_RING_SIZE, TX_RING_SIZE) - 1;
+		do {
+			dma_addr_t bus_address = readl(&fp->rx_desc[i].desc_a);
+			if (likely(!pci_dma_mapping_error(bus_address)))
+				pci_unmap_single(fp->this_card_priv->
+						 pci_dev, bus_address,
+						 fp->unit_sz,
+						 PCI_DMA_FROMDEVICE);
+			bus_address = readl(&fp->tx_desc[i].desc_a);
+			if (likely(!pci_dma_mapping_error(bus_address)))
+				pci_unmap_single(fp->this_card_priv->
+						 pci_dev, bus_address,
+						 fp->unit_sz, PCI_DMA_TODEVICE);
+		}
+		while (i--);
+	}
+
+	up_write(&fp->this_card_priv->semaphore);
+	return 0;
+}
+
+static int fepci_stream_close(struct net_device *dev)
+{
+	struct fepci_ch_private *fp = dev->priv;
+	unsigned rx_pages, tx_pages, rx_order, tx_order;
+
+	if (fepci_stream_stop(dev))
+		return -ENODEV;
+	down_write(&fp->this_card_priv->semaphore);
+	if (!(fp->in_stream_mode)) {
+		up_write(&fp->this_card_priv->semaphore);
+		return -ENODEV;
+	}
+	/* release memory */
+	if (fp->bufsize_order < PAGE_SHIFT) {
+		rx_order = 0;
+		tx_order = 0;
+		rx_pages = 1;
+		tx_pages = 1;
+	} else {
+		rx_order = (int)((fp->bufsize_order) - PAGE_SHIFT + 1);
+		rx_pages = 1 << rx_order;
+		tx_order = (int)((fp->bufsize_order) - PAGE_SHIFT);
+		tx_pages = 1 << tx_order;
+	}
+	if (fp->rx_buffer) {
+		unsigned page_number;
+		for (page_number = 0; page_number < rx_pages; page_number++)
+			/* turn pages back to non-reserved */
+			ClearPageReserved(virt_to_page
+					  ((unsigned long)fp->rx_buffer +
+					   (page_number << PAGE_SHIFT)));
+		free_pages((unsigned long)fp->rx_buffer, rx_order);
+		fp->rx_buffer = NULL;
+	}
+	if (fp->tx_buffer) {
+		unsigned page_number;
+		for (page_number = 0; page_number < tx_pages; page_number++)
+			/* turn pages back to non-reserved */
+			ClearPageReserved(virt_to_page
+					  ((unsigned long)fp->tx_buffer +
+					   (page_number << PAGE_SHIFT)));
+		free_pages((unsigned long)fp->tx_buffer, tx_order);
+		fp->tx_buffer = NULL;
+	}
+
+	fp->in_stream_mode = 0;
+	up_write(&fp->this_card_priv->semaphore);
+	return 0;
+}
+
+static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance)
+{
+	struct net_device *dev = dev_instance;
+	struct fepci_ch_private *fp = dev->priv;
+	void *ioaddr = (void *)dev->base_addr;
+	unsigned intr_status = get_int_status(fp->channel_number, ioaddr);
+	bool fifo, dropped;
+	unsigned int temp_rx;
+	unsigned int temp_rx_unit;
+	unsigned int temp_tx;
+	unsigned int temp_tx_unit;
+
+	clear_int(fp->channel_number, intr_status, ioaddr);
+
+	/* debugging */
+	fp->interrupts_stream++;
+	fifo = (intr_status & IntrRxFifoError) != 0;
+	dropped = (intr_status & IntrRxFrameDroppedError) != 0;
+	fp->int_err_table_stream[fifo][dropped]++;
+	if (intr_status & IntrFrameReceived)
+		fp->rx_interrupts_stream++;
+	if (intr_status & IntrFrameTransmitted)
+		fp->tx_interrupts_stream++;
+	if (fifo)
+		fp->rx_int_fifo_err_stream++;
+	if (dropped)
+		fp->rx_int_frame_dropped_err_stream++;
+	if (intr_status & IntrTxFifoError)
+		fp->tx_int_fifo_err_stream++;
+	/* first update cur_rx, and do stuff if it has moved
+	   (+ packets have been received) */
+	{
+		temp_rx = fp->cur_rx;
+		/* has been received */
+		while ((readl(&fp->rx_desc[fp->cur_rx].desc_b) &
+			transfer_not_done) == 0
+		       /* stop if made one round */
+		       && temp_rx != ((fp->cur_rx + 1) & (RX_RING_SIZE - 1))) {
+			dma_addr_t bus_address = readl(&fp->rx_desc[fp->cur_rx].
+						       desc_a);
+			if (likely(!pci_dma_mapping_error(bus_address)))
+				pci_unmap_single(fp->this_card_priv->
+						 pci_dev, bus_address,
+						 fp->unit_sz,
+						 PCI_DMA_FROMDEVICE);
+			fp->cur_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
+			fp->cur_rx_unit = (fp->cur_rx_unit + 1);
+			fp->cur_rx_unit *= fp->cur_rx_unit < fp->units;
+
+			*USER_RX_S_POINTER(fp->this_card_priv->
+					   card_number,
+					   fp->channel_number,
+					   stream_pointers) = fp->cur_rx_unit;
+			*USER_RX_S_FAKE_POINTER(fp->this_card_priv->
+						card_number,
+						fp->channel_number,
+						stream_pointers) =
+			    fp->cur_rx_unit * fp->unit_sz / fp->fake_unit_sz;
+			wake_up_interruptible(&(fp->this_card_priv->
+						stream_receive_q));
+			wake_up_interruptible(&(fp->this_card_priv->
+						stream_both_q));
+		}
+	}
+	/* from the first uninitialized descriptor to cur_rx */
+	temp_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
+	temp_rx_unit = (fp->cur_rx_unit + 1);
+	temp_rx_unit *= temp_rx_unit < fp->units;
+
+	while (temp_rx != fp->cur_rx) {
+		unsigned desc_b = readl(&fp->rx_desc[temp_rx].desc_b);
+		if ((desc_b & transfer_not_done) == 0) {
+			bool fifo = (desc_b & fifo_error) != 0;
+			bool size = (desc_b & size_error) != 0;
+			bool crc = (desc_b & crc_error) != 0;
+			bool octet = (desc_b & octet_error) != 0;
+			bool line = (desc_b & line_error) != 0;
+			unsigned length = desc_b & frame_length;
+			dma_addr_t bus_address;
+			/* update debug counters */
+			fp->rx_desc_err_table_stream[fifo]
+			    [size]
+			    [crc]
+			    [octet]
+			    [line]++;
+			if (length == 0)
+				fp->rx_packets_of_size_0_stream++;
+			else if (length == 1)
+				fp->rx_packets_of_size_1_stream++;
+			else if (length == 2)
+				fp->rx_packets_of_size_2_stream++;
+			else if (length == 3)
+				fp->rx_packets_of_size_3_stream++;
+			else if (length < 8)
+				fp->rx_packets_of_size_4_7_stream++;
+			else if (length < 16)
+				fp->rx_packets_of_size_8_15_stream++;
+			else if (length < 32)
+				fp->rx_packets_of_size_16_31_stream++;
+			if (fifo)
+				fp->rx_desc_fifo_err_stream++;
+			else if (size)
+				fp->rx_desc_size_err_stream++;
+			else if (crc)
+				fp->rx_desc_crc_err_stream++;
+			else if (octet)
+				fp->rx_desc_octet_err_stream++;
+			else if (line)
+				fp->rx_desc_line_err_stream++;
+			/* initialize the descriptor for transfer */
+			bus_address =
+			    pci_map_single(fp->this_card_priv->pci_dev,
+					   fp->rx_unit[temp_rx_unit],
+					   fp->unit_sz, PCI_DMA_FROMDEVICE);
+			if (likely(!pci_dma_mapping_error(bus_address))) {
+				writel(bus_address,
+				       &fp->rx_desc[temp_rx].desc_a);
+				writel(enable_transfer,
+				       &fp->rx_desc[temp_rx].desc_b);
+			} else
+				printk(KERN_WARNING
+				       "%s: failed to map DMA for reception.\n",
+				       fepci_NAME);
+		}
+		temp_rx = (temp_rx + 1) & (RX_RING_SIZE - 1);
+		temp_rx_unit = (temp_rx_unit + 1);
+		temp_rx_unit *= temp_rx_unit < fp->units;
+	}
+
+	/* first update cur_tx, and do stuff if it has moved
+	   (+ packets have been transmitted) */
+	{
+		temp_tx = fp->cur_tx;
+		/* has been transmitted */
+		while ((readl(&fp->tx_desc[fp->cur_tx].desc_b) &
+			transfer_not_done) == 0
+		       /* stop if made one round */
+		       && temp_tx != ((fp->cur_tx + 1) & (TX_RING_SIZE - 1))) {
+			dma_addr_t bus_address = readl(&fp->tx_desc[fp->cur_tx].
+						       desc_a);
+			if (likely(!pci_dma_mapping_error(bus_address)))
+				pci_unmap_single(fp->this_card_priv->
+						 pci_dev, bus_address,
+						 fp->unit_sz, PCI_DMA_TODEVICE);
+			fp->cur_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
+			fp->cur_tx_unit = (fp->cur_tx_unit + 1);
+			fp->cur_tx_unit *= fp->cur_tx_unit < fp->units;
+
+			*USER_TX_S_POINTER(fp->this_card_priv->
+					   card_number,
+					   fp->channel_number,
+					   stream_pointers) = fp->cur_tx_unit;
+			*USER_TX_S_FAKE_POINTER(fp->this_card_priv->
+						card_number,
+						fp->channel_number,
+						stream_pointers) =
+			    fp->cur_tx_unit * fp->unit_sz / fp->fake_unit_sz;
+			wake_up_interruptible(&(fp->this_card_priv->
+						stream_transmit_q));
+			wake_up_interruptible(&(fp->this_card_priv->
+						stream_both_q));
+		}
+	}
+	/* from the first uninitialized descriptor to cur_tx */
+	temp_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
+	temp_tx_unit = (fp->cur_tx_unit + 1);
+	temp_tx_unit *= temp_tx_unit < fp->units;
+
+	while (temp_tx != fp->cur_tx) {
+		unsigned desc_b = readl(&fp->tx_desc[temp_tx].desc_b);
+		if ((desc_b & transfer_not_done) == 0) {
+			dma_addr_t bus_address;
+			/* update debug counters */
+			if (desc_b & fifo_error)
+				fp->tx_desc_fifo_err_stream++;
+			/* initialize the desctiptor for transfer */
+			bus_address =
+			    pci_map_single(fp->this_card_priv->pci_dev,
+					   fp->tx_unit[temp_tx_unit],
+					   fp->unit_sz, PCI_DMA_TODEVICE);
+			writel(bus_address, &fp->tx_desc[temp_tx].desc_a);
+			if (likely(!pci_dma_mapping_error(bus_address)))
+				writel(enable_transfer |
+				       (fp->unit_sz & frame_length),
+				       &fp->tx_desc[temp_tx].desc_b);
+			else
+				printk(KERN_WARNING
+				       "%s: failed to map tranmission DMA.\n",
+				       fepci_NAME);
+		}
+		temp_tx = (temp_tx + 1) & (TX_RING_SIZE - 1);
+		temp_tx_unit = (temp_tx_unit + 1);
+		temp_tx_unit *= temp_tx_unit < fp->units;
+	}
+
+	return intr_status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/* stream operations end */
+
+int fepci_rebuild_header(struct sk_buff *skb)
+{
+	DBG_PRINT("fepci_rebuild_header\n");
+	return 0;
+}
+
+static inline u16 get_common_reg_word(void *ioaddr, unsigned long offsett)
+{
+	u16 word;
+	DBG_PRINT("get_common_reg_word ioaddr %p, offsett %lu\n", ioaddr,
+		  offsett);
+	__clear_bit(0, &offsett);
+	DBG_PRINT("get_common_reg_word %p\n",
+		  ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1));
+	word = le16_to_cpu(readw
+			   (ioaddr + FEPCI_IDENTIFICATION_OFFSETT +
+			    (offsett << 1)));
+	DBG_PRINT("get_common_reg_word %p: %hu\n",
+		  ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1), word);
+	return word;
+}
+
+static irqreturn_t alarm_manager_interrupt(int irq, void *pointer)
+{
+	struct fepci_card_private *card_private = pointer;
+	void *ioaddr = card_private->ioaddr;
+	/* check int status */
+	if (readl((void *)(ioaddr + reg_custom)) & AM_interrupt_status) {
+		/* clear int (zero everything, but the mask bit) */
+		writel(readl((void *)(ioaddr + reg_custom)) &
+		       AM_interrupt_mask, (void *)(ioaddr + reg_custom));
+		/* wake queue */
+		wake_up(&(card_private->alarm_manager_wait_q));
+	}
+
+	return IRQ_HANDLED;
+}
+
+#ifdef FEPCI_POINT_TO_POINT
+static int is_ptp_interface(struct net_device *dev)
+{
+	char **p_ptp_if_name = retina_ptp_interfaces;
+	unsigned int i = interfaces;
+	while (i > 0 && *p_ptp_if_name != NULL) {
+		if (!strncmp(dev->name, *p_ptp_if_name, sizeof(dev->name)))
+			return 1;
+		p_ptp_if_name++;
+		i--;
+	}
+	return 0;
+}
+#endif /* FEPCI_POINT_TO_POINT */
+
+static int __devinit fepci_init_one(struct pci_dev *pdev,
+				    const struct pci_device_id *ent)
+{
+	struct net_device *dev = NULL;
+	struct fepci_ch_private *fp = NULL;
+	int chip_idx = ent->driver_data;
+	int drv_flags = pci_id_tbl[chip_idx].drv_flags;
+	int i;
+	unsigned j;
+	resource_size_t real_ioaddr;
+	void *ioaddr;
+	unsigned position;
+
+	i = pci_enable_device(pdev);
+	if (i) {
+		printk(KERN_WARNING "%s: pci_enable_device returned %x.\n",
+		       fepci_NAME, i);
+		return i;
+	}
+
+	pci_set_master(pdev);
+
+	i = pci_request_regions(pdev, (char *)fepci_name);
+	if (i) {
+		printk(KERN_WARNING
+		       "%s: pci_request_regions returned %x.\n", fepci_NAME, i);
+		pci_disable_device(pdev);
+		return i;
+	}
+
+	/* make sure above region is MMIO */
+	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+		printk(KERN_WARNING "%s: region not MMIO region\n", fepci_NAME);
+		goto ERR_1;
+	}
+
+	j = pci_resource_len(pdev, 0);
+	if (j < FEPCI_SIZE) {
+		printk(KERN_WARNING
+		       "%s: resource length %u less than required %u.\n",
+		       fepci_NAME, j, FEPCI_SIZE);
+		goto ERR_1;
+	}
+
+	if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+		printk(KERN_WARNING "%s: no suitable DMA available.\n",
+		       fepci_NAME);
+		goto ERR_1;
+	}
+
+	real_ioaddr = pci_resource_start(pdev, 0);
+	DBG_PRINT("pci_resource_start %lu.\n", real_ioaddr);
+	ioaddr = ioremap_nocache(real_ioaddr, FEPCI_SIZE);
+	DBG_PRINT("ioremap_nocache = %p.\n", ioaddr);
+	if (!ioaddr) {
+		printk(KERN_WARNING "%s: remapping failed.\n", fepci_NAME);
+		goto ERR_1_5;
+	}
+	position = 0;
+	for (; position < MAX_DEVICES; position++) {
+		down_write(&card_privates[position].semaphore);
+		if (card_privates[position].pci_dev == NULL) {
+			card_privates[position].pci_dev = pdev;
+			if (position == find_cnt)
+				find_cnt++;
+			goto FOUND;
+		}
+		up_write(&card_privates[position].semaphore);
+	}
+	printk(KERN_WARNING
+	       "%s: no space to inialize device #%u.\n",
+	       fepci_NAME, MAX_DEVICES + 1);
+	goto ERR_2;
+FOUND:
+	card_privates[position].card_number = position;
+	card_privates[position].ioaddr = ioaddr;
+	card_privates[position].pci_dev = pdev;
+	DBG_PRINT("fillregisterswith_00 %p.\n", ioaddr);
+	fillregisterswith_00(ioaddr);
+
+	fepci_proc_init_card(position, (void *)&(card_privates[position]));
+
+	init_waitqueue_head(&(card_privates[position].alarm_manager_wait_q));
+	init_waitqueue_head(&(card_privates[position].stream_transmit_q));
+	init_waitqueue_head(&(card_privates[position].stream_receive_q));
+	init_waitqueue_head(&(card_privates[position].stream_both_q));
+
+	init_timer(&card_privates[position].mailbox_timer);
+
+	DBG_PRINT("request_irq %d, %s.\n", pdev->irq, fepci_alarm_manager_name);
+	i = request_irq(pdev->irq, &alarm_manager_interrupt,
+			IRQF_SHARED,
+			fepci_alarm_manager_name, &card_privates[position]);
+	if (i) {
+		up_write(&card_privates[position].semaphore);
+		printk(KERN_WARNING
+		       "%s: unable to allocate IRQ %d alarm manager: 0x%x\n",
+		       fepci_NAME, pdev->irq, i);
+		goto ERR_2;
+	}
+	DBG_PRINT("alarm manager int on %p.\n", (void *)(ioaddr + reg_custom));
+	writel(AM_interrupt_mask, (void *)(ioaddr + reg_custom));
+	/* alarm manager int on */
+
+	for (j = 0; j < CHANNELS; j++) {
+		DBG_PRINT("alloc_etherdev %u.\n", j);
+		dev = alloc_etherdev(sizeof(struct fepci_ch_private));
+		if (!dev) {
+			printk(KERN_WARNING
+			       "%s: cannot allocate ethernet device\n",
+			       fepci_NAME);
+			continue;
+		}
+
+		fp = dev->priv;
+		fp->minor = position;	/* * CHANNELS + j; */
+		/* name := xxx01..xxxnn */
+		memcpy(dev->name, fepci_netdev_name, 6);
+		/* dev->name[3]= j+'0';    channel number -> ascii */
+		/* minor number -> ascii */
+		dev->name[4] = ((fp->minor * CHANNELS + j) % 10) + '0';
+		/* minor number -> ascii */
+		dev->name[3] = ((fp->minor * CHANNELS + j) / 10) + '0';
+
+		SET_MODULE_OWNER(dev);
+		DBG_PRINT("clear_int %u, %x, %p.\n", j, IntrAllInts, ioaddr);
+		clear_int(j, IntrAllInts, ioaddr);
+		DBG_PRINT("ether_setup %p.\n", dev);
+		ether_setup(dev);
+
+		random_ether_addr(dev->dev_addr);
+		/* HW_ADDR is got using the mailbox: */
+		{
+			struct fepci_real_mailbox *real_mailbox =
+			    (struct fepci_real_mailbox *)
+			    (ioaddr + FEPCI_MAILBOX_OFFSETT);
+			unsigned long waituntil;
+
+			set_semafore(real_mailbox, 0x40);
+			writel(0x1 /*size */  +
+			       (0x8 << 8) /* get mac command */ ,
+			       &real_mailbox->Size_Command);
+			set_semafore(real_mailbox, 0x11);
+
+			waituntil = jiffies + HZ;
+			while (time_before(jiffies, waituntil) &&
+			       get_semafore(real_mailbox) != 0x20) {
+				DBG_PRINT("jiffies %lu < waituntil %lu.\n",
+					  jiffies, waituntil);
+				msleep(0);
+			}
+
+			/* 14.5.2004 JT: Made this safer. */
+			if (get_semafore(real_mailbox) == 0x20) {
+				dev->dev_addr[5] =
+				    readb(&real_mailbox->Data[0 + 3 * j]);
+				dev->dev_addr[4] =
+				    readb(((u8 *) & real_mailbox->
+					   Data[0 + 3 * j]) + 1);
+				dev->dev_addr[3] =
+				    readb(&real_mailbox->Data[1 + 3 * j]);
+				dev->dev_addr[2] =
+				    readb(((u8 *) & real_mailbox->
+					   Data[1 + 3 * j]) + 1);
+				dev->dev_addr[1] =
+				    readb(&real_mailbox->Data[2 + 3 * j]);
+				dev->dev_addr[0] =
+				    readb(((u8 *) & real_mailbox->
+					   Data[2 + 3 * j]) + 1);
+			}
+
+			set_semafore(real_mailbox, 0x0);
+		}
+		dev->addr_len = 6;
+
+		dev->base_addr = (unsigned long)ioaddr;
+		dev->irq = pdev->irq;
+		DBG_PRINT("alarm pci_set_drvdata %p, %p.\n", pdev, dev);
+		if (j == 0)
+			pci_set_drvdata(pdev, dev);
+
+		fp->drv_flags = drv_flags;
+
+		fp->rx_desc =
+		    (struct fepci_desc *)(dev->base_addr + first_rx_desc +
+					  j * to_next_ch_rx_desc);
+		fp->tx_desc =
+		    (struct fepci_desc *)(dev->base_addr + first_tx_desc +
+					  j * to_next_ch_tx_desc);
+
+		fp->channel_number = j;	/*channel in this device */
+		fp->this_dev = dev;
+
+		fp->this_card_priv = &card_privates[position];
+		fp->this_card_priv->ch_privates[j] = fp;
+
+		fp->cur_tx = 0;
+
+		fp->in_stream_mode = 0;
+		fp->in_eth_mode = 0;
+
+		fp->reg_rxctrl = reg_first_rxctrl + j * to_next_rxctrl;
+		fp->reg_txctrl = reg_first_txctrl + j * to_next_txctrl;
+
+		/* The FEPCI specific entries in the device structure */
+		dev->open = &fepci_open;
+		dev->hard_start_xmit = &fepci_start_xmit;
+		dev->stop = &fepci_close;
+		dev->get_stats = &fepci_get_stats;
+		dev->set_multicast_list = &set_rx_mode;
+		dev->do_ioctl = &netdev_ioctl;
+		dev->tx_timeout = fepci_tx_timeout;
+		dev->watchdog_timeo = TX_TIMEOUT;
+
+#ifdef FEPCI_POINT_TO_POINT
+		if (is_ptp_interface(dev)) {
+			dev->flags |= (IFF_POINTOPOINT);
+			dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
+			if (retina_noarp_with_ptp) {
+				dev->rebuild_header = fepci_rebuild_header;
+				dev->flags |= (IFF_NOARP);
+			}
+		}
+#endif
+		DBG_PRINT("register_netdev %p.\n", dev);
+		i = register_netdev(dev);
+		if (i) {
+			printk(KERN_WARNING
+			       "%s: register_netdev failed 0x%x.\n",
+			       fepci_NAME, i);
+			continue;
+		}
+
+		printk("%s: %s type %x at %p, ",
+		       dev->name, pci_id_tbl[chip_idx].name, 0x0, ioaddr);
+		for (i = 0; i < 5; i++)
+			printk("%2.2x:", dev->dev_addr[i]);
+		printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], pdev->irq);
+#ifdef DEBUG_PROC_FILES
+		fepci_proc_init_channel(position, j, fp);
+#endif /* DEBUG_PROC_FILES */
+	}
+	up_write(&card_privates[position].semaphore);
+	DBG_PRINT("fepci_init_one %u.\n", position);
+	return 0;
+
+ERR_2:
+	iounmap(ioaddr);
+ERR_1_5:
+	pci_set_drvdata(pdev, NULL);
+ERR_1:
+	pci_disable_device(pdev);
+	pci_release_regions(pdev);
+	return -ENODEV;
+}
+
+static int fepci_open(struct net_device *dev)
+{
+	struct fepci_ch_private *fp = dev->priv;
+
+	down_write(&fp->this_card_priv->semaphore);
+
+	if (fp->this_card_priv->pci_dev == NULL) {
+		up_write(&fp->this_card_priv->semaphore);
+		fepci_close(dev);
+		return -ENXIO;
+	}
+
+	while (fp->in_stream_mode) {
+		up_write(&fp->this_card_priv->semaphore);
+		fepci_stream_close(dev);
+		down_write(&fp->this_card_priv->semaphore);
+	}
+
+	{
+		int i = request_irq(dev->irq, &fepci_interrupt,
+				    IRQF_SHARED, dev->name, dev);
+		if (i) {
+			up_write(&fp->this_card_priv->semaphore);
+			printk(KERN_WARNING
+			       "%s: unable to allocate IRQ %d, error 0x%x",
+			       fepci_NAME, dev->irq, i);
+			return i;
+		}
+	}
+
+	fp->in_eth_mode = 1;
+
+	fepci_init_ring(dev);
+	set_rx_mode(dev);
+
+	fp->cur_rx = 0;
+	fp->cur_tx = 0;
+
+	netif_carrier_off(dev);
+
+	/* Enable interrupts by setting the interrupt mask. */
+
+	set_int_mask(fp->channel_number,
+		     MaskFrameReceived | MaskFrameTransmitted |
+		     MaskRxFifoError | MaskRxFrameDroppedError |
+		     MaskTxFifoError, fp->this_card_priv);
+
+	{
+		void *ioaddr = (void *)dev->base_addr;
+
+		/* Start Rx and Tx channels. */
+		writel(Receive_enable |
+		       (Rx_fifo_threshold & RX_FIFO_THRESHOLD_PACKET_MODE),
+		       (void *)(ioaddr + fp->reg_rxctrl));
+
+		writel((Transmit_enable |
+			(Tx_desc_threshold &
+			 (TX_DESC_THRESHOLD_PACKET_MODE << 8)) |
+			(Tx_fifo_threshold & TX_FIFO_THRESHOLD_PACKET_MODE)),
+		       (void *)(ioaddr + fp->reg_txctrl));
+	}
+
+	netif_start_queue(dev);
+
+	/* Set timer */
+	init_timer(&fp->timer);
+	fp->timer.expires = jiffies + HZ;
+	fp->timer.data = (unsigned long)dev;
+	fp->timer.function = &fepci_timer;	/* timer handler */
+	add_timer(&fp->timer);
+
+	up_write(&fp->this_card_priv->semaphore);
+
+	return 0;
+}
+
+static void fepci_timer(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct fepci_ch_private *fp = dev->priv;
+
+	/* just to make it absolutely sure the sending starts again
+	 * if the system jams: free already sent skbuffs */
+
+	if (netif_tx_trylock(dev)) {
+		if (!(fp->tx_interrupts_since_last_timer)) {
+			unsigned i = TX_RING_SIZE - 1;
+			do {
+				unsigned desc_b;
+				if ((fp->tx_skbuff[i] != NULL)
+				    &&
+				    (((desc_b =
+				       readl(&fp->tx_desc[i].
+					     desc_b)) & transfer_not_done) ==
+				     0)) {
+					/* has been sent */
+					pci_unmap_single(fp->
+							 this_card_priv->
+							 pci_dev,
+							 readl(&fp->
+							       tx_desc[i].
+							       desc_a),
+							 desc_b &
+							 frame_length,
+							 PCI_DMA_TODEVICE);
+					dev_kfree_skb(fp->tx_skbuff[i]);
+					fp->tx_skbuffs_out++;
+
+					fp->tx_skbuff[i] = NULL;
+
+					if (desc_b & fifo_error) {
+						fp->stats.tx_fifo_errors++;
+						fp->tx_desc_fifo_err++;
+					} else
+						fp->stats.tx_packets++;
+				}
+			}
+			while (i--);
+		}
+		/* if the next descriptor is free, continue taking new ones */
+		if (!
+		    (readl(&fp->tx_desc[fp->cur_tx].desc_b) &
+		     transfer_not_done))
+			netif_wake_queue(dev);
+		netif_tx_unlock(dev);
+	}
+
+	fp->tx_interrupts_since_last_timer = 0;
+
+	if ((get_common_reg_word(fp->this_card_priv->ioaddr, 0x72) >> fp->
+	     channel_number) & 1) {
+		netif_carrier_off(dev);
+	} else {
+		netif_carrier_on(dev);
+	}
+
+	if (fp->in_eth_mode)
+		mod_timer(&fp->timer, jiffies + 5 * HZ);
+}
+
+static void fepci_tx_timeout(struct net_device *dev)
+{
+	DBG_PRINT("%s: transmit timed out!\n", dev->name);
+}
+
+/* Initialize the rx and tx rings */
+static void fepci_init_ring(struct net_device *dev)
+{
+	struct fepci_ch_private *fp = dev->priv;
+	unsigned i;
+
+	fp->rx_buf_sz = 2000;
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb =
+		    __dev_alloc_skb(fp->rx_buf_sz, GFP_KERNEL);
+
+		if (unlikely(skb == NULL)) {
+ZERO:
+			writel(0, &fp->rx_desc[i].desc_a);
+			writel(0, &fp->rx_desc[i].desc_b);
+			continue;
+		} else {
+			dma_addr_t bus_address =
+			    pci_map_single(fp->this_card_priv->pci_dev,
+					   skb->data, fp->rx_buf_sz,
+					   PCI_DMA_FROMDEVICE);
+			if (likely(!pci_dma_mapping_error(bus_address))) {
+				fp->rx_skbuffs_in++;
+				/* Mark as being used by this device */
+				skb->dev = dev;
+				skb->ip_summed = CHECKSUM_UNNECESSARY;
+				fp->rx_skbuff[i] = skb;
+				writel(bus_address, &fp->rx_desc[i].desc_a);
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			} else {
+				dev_kfree_skb(skb);
+				goto ZERO;
+			}
+		}
+	}
+
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		fp->tx_skbuff[i] = NULL;
+		writel(0, &fp->tx_desc[i].desc_a);	/* no skbuff */
+		/* no transfer enable, no int enable */
+		writel(0, &fp->tx_desc[i].desc_b);
+	}
+
+	return;
+}
+
+static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct fepci_ch_private *fp = dev->priv;
+	const unsigned cur_tx = fp->cur_tx;
+	unsigned next;
+	unsigned tx_length = skb->len;
+	dma_addr_t bus_address;
+	struct sk_buff *old;
+
+	fp->tx_skbuffs_in++;
+
+	if (unlikely(tx_length < ETH_ZLEN)) {
+		struct sk_buff *bigger =
+		    skb_copy_expand(skb, 0, ETH_ZLEN - tx_length,
+				    GFP_ATOMIC);
+		if (unlikely(!bigger))
+			return NET_XMIT_CN;
+		tx_length = ETH_ZLEN;
+		old = skb;
+		skb = bigger;
+	} else
+		old = NULL;
+	bus_address =
+	    pci_map_single(fp->this_card_priv->pci_dev, skb->data,
+			   tx_length, PCI_DMA_TODEVICE);
+	if (likely(!pci_dma_mapping_error(bus_address))) {
+		struct fepci_desc *descriptor;
+		if (old)
+			dev_kfree_skb(old);
+		descriptor = &fp->tx_desc[cur_tx];
+		writel(bus_address, &descriptor->desc_a);
+		writel((tx_length & frame_length) | enable_transfer,
+		       &descriptor->desc_b);
+	} else {
+		if (old)
+			dev_kfree_skb(skb);
+		return NET_XMIT_CN;
+	}
+	fp->stats.tx_bytes += tx_length;
+
+	fp->tx_skbuff[cur_tx] = skb;
+
+	/* Calculate the next Tx descriptor entry */
+	next = (cur_tx + 1) & (TX_RING_SIZE - 1);
+	fp->cur_tx = next;
+	/* if the next descriptor is busy, discontinue taking new ones */
+	if (fp->tx_skbuff[next] != NULL)
+		netif_stop_queue(dev);
+	dev->trans_start = jiffies;
+
+	return NET_XMIT_SUCCESS;
+}
+
+static irqreturn_t fepci_interrupt(int irq, void *dev_instance)
+{
+	struct net_device *dev = dev_instance;
+	void *ioaddr = (void *)dev->base_addr;
+	struct fepci_ch_private *fp = dev->priv;
+	unsigned intr_status = get_int_status(fp->channel_number, ioaddr);
+	bool RxFifoError;
+	bool RxFrameDroppedError;
+
+	clear_int(fp->channel_number, intr_status, ioaddr);
+
+	RxFifoError = (intr_status & IntrRxFifoError) != 0;
+	RxFrameDroppedError = (intr_status & IntrRxFrameDroppedError) != 0;
+	/* first update interrupt error table */
+	fp->int_err_table[RxFifoError][RxFrameDroppedError]++;
+
+	fp->interrupts++;
+
+	if (intr_status & IntrFrameReceived) {
+		fepci_rx(dev);
+		fp->rx_interrupts++;
+	}
+	if (intr_status & IntrFrameTransmitted) {
+		fp->tx_interrupts_since_last_timer++;
+		if (netif_tx_trylock(dev)) {
+			unsigned i = TX_RING_SIZE - 1;
+			unsigned next;
+			do {
+				unsigned desc_b;
+				if ((fp->tx_skbuff[i] != NULL)
+				    &&
+				    (((desc_b =
+				       readl(&fp->tx_desc[i].
+					     desc_b)) & transfer_not_done) ==
+				     0)) {
+					/* has been sent */
+					pci_unmap_single(fp->
+							 this_card_priv->
+							 pci_dev,
+							 readl(&fp->
+							       tx_desc[i].
+							       desc_a),
+							 desc_b &
+							 frame_length,
+							 PCI_DMA_TODEVICE);
+					dev_kfree_skb_irq(fp->tx_skbuff[i]);
+					fp->tx_skbuffs_out++;
+					fp->tx_skbuff[i] = NULL;
+					if (desc_b & fifo_error) {
+						fp->stats.tx_fifo_errors++;
+						fp->tx_desc_fifo_err++;
+					} else
+						fp->stats.tx_packets++;
+				}
+			}
+			while (i--);
+			next = fp->cur_tx;
+			/* if next tx descriptor is free,
+			 * continue taking new ones */
+			if (!(readl(&fp->tx_desc[next].desc_b) &
+			      transfer_not_done))
+				netif_wake_queue(dev);
+			netif_tx_unlock(dev);
+		}
+		fp->tx_interrupts++;
+	}
+	if (RxFifoError) {
+		fp->rx_int_fifo_err++;
+		fepci_rx(dev);
+	}
+	if (RxFrameDroppedError) {
+		fp->rx_int_frame_dropped_err++;
+		fepci_rx(dev);
+	}
+	if (intr_status & IntrTxFifoError)
+		fp->tx_int_fifo_err++;
+	return intr_status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int fepci_rx(struct net_device *dev)
+{
+	struct fepci_ch_private *fp = dev->priv;
+
+	unsigned int i, old_cur_rx = fp->cur_rx;
+	for (i = old_cur_rx;
+	     i != ((old_cur_rx + RX_RING_SIZE - 1) & (RX_RING_SIZE - 1));
+	     i = (i + 1) & (RX_RING_SIZE - 1)) {
+		unsigned desc_b;
+		struct sk_buff *skb;
+		/* transfer done */
+		bool condition = (skb = fp->rx_skbuff[i]) &&
+		    ((desc_b =
+		      readl(&fp->rx_desc[i].desc_b)) & transfer_not_done) == 0;
+		if (condition) {
+			bool fifo = (desc_b & fifo_error) != 0;
+			bool size = (desc_b & size_error) != 0;
+			bool crc = (desc_b & crc_error) != 0;
+			bool octet = (desc_b & octet_error) != 0;
+			bool line = (desc_b & line_error) != 0;
+			unsigned length = desc_b & frame_length;
+			pci_unmap_single(fp->this_card_priv->pci_dev,
+					 readl(&fp->rx_desc[i].desc_a),
+					 fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+			fp->cur_rx = (i + 1) & (RX_RING_SIZE - 1);
+			/* first update error table */
+			fp->rx_desc_err_table[fifo]
+			    [size]
+			    [crc]
+			    [octet]
+			    [line]++;
+			/* small packet counters */
+			if (length == 0)
+				fp->rx_packets_of_size_0++;
+			else if (length == 1)
+				fp->rx_packets_of_size_1++;
+			else if (length == 2)
+				fp->rx_packets_of_size_2++;
+			else if (length == 3)
+				fp->rx_packets_of_size_3++;
+			else if (length < 8)
+				fp->rx_packets_of_size_4_7++;
+			else if (length < 16)
+				fp->rx_packets_of_size_8_15++;
+			else if (length < 32)
+				fp->rx_packets_of_size_16_31++;
+
+			if (fifo) {
+				fp->stats.rx_errors++;
+				fp->stats.rx_frame_errors++;
+				fp->rx_desc_fifo_err++;
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			} else if (size) {
+				fp->stats.rx_errors++;
+				fp->stats.rx_over_errors++;
+				fp->rx_desc_size_err++;
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			} else if (crc) {
+				fp->stats.rx_errors++;
+				fp->stats.rx_crc_errors++;
+				fp->rx_desc_crc_err++;
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			} else if (octet) {
+				fp->rx_desc_octet_err++;
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			} else if (line) {
+				fp->rx_desc_line_err++;
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			} else {
+				skb_put(skb, length - 4);
+
+				skb->protocol = eth_type_trans(skb, dev);
+#ifdef FEPCI_POINT_TO_POINT
+				if (dev->flags & IFF_POINTOPOINT) {
+					/* everything received is for us. */
+
+					if (dev->flags & IFF_NOARP) {
+						/* NOARP applied ->
+						 * destination MAC addresses
+						 * are bogus */
+						if (skb->
+						    pkt_type ==
+						    PACKET_OTHERHOST)
+							skb->
+							    pkt_type =
+							    PACKET_HOST;
+					} else {
+						/* NOARP not applied ->
+						 * destination MAC addresses are
+						 * broadcast */
+						if (skb->
+						    pkt_type ==
+						    PACKET_BROADCAST)
+							skb->
+							    pkt_type =
+							    PACKET_HOST;
+
+					}	/* IFF_NOARP */
+				}	/* IFF_POINTOPOINT */
+#endif
+				skb_reset_mac_header(skb);
+				netif_rx(skb);
+				fp->rx_skbuffs_out++;
+				/* statistics -4==crc */
+				fp->stats.rx_bytes += length - 4;
+				fp->stats.rx_packets++;
+
+				fp->rx_skbuff[i] = NULL;
+				dev->last_rx = jiffies;
+			}
+		}
+		/* reserve a new one */
+		if (fp->rx_skbuff[i] == NULL) {
+			struct sk_buff *skb = dev_alloc_skb(fp->rx_buf_sz);
+
+			if (skb == NULL)
+				continue;	/* Better luck next round. */
+			else {
+				dma_addr_t address =
+				    pci_map_single(fp->this_card_priv->pci_dev,
+						   skb->data,
+						   fp->rx_buf_sz,
+						   PCI_DMA_FROMDEVICE);
+				if (likely(!pci_dma_mapping_error(address))) {
+					struct fepci_desc *descriptor;
+					fp->rx_skbuffs_in++;
+					fp->rx_skbuff[i] = skb;
+					/* Mark as being used by this device. */
+					skb->dev = dev;
+					skb->ip_summed = CHECKSUM_UNNECESSARY;
+					descriptor = &fp->rx_desc[i];
+					writel(address, &descriptor->desc_a);
+					writel(enable_transfer,
+					       &descriptor->desc_b);
+				} else {
+					dev_kfree_skb(skb);
+					printk(KERN_WARNING
+					       "%s: failed to map DMA.\n",
+					       dev->name);
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static int fepci_close(struct net_device *dev)
+{
+	struct fepci_ch_private *fp = dev->priv;
+	unsigned i;
+	void *ioaddr = (void *)dev->base_addr;
+
+	down_write(&fp->this_card_priv->semaphore);
+
+	netif_stop_queue(dev);
+
+	/* Disable interrupts by clearing the interrupt mask */
+	set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
+
+	/* Stop the Tx and Rx processes */
+	writel(0x0, ioaddr + fp->reg_rxctrl);
+	writel(0x0, ioaddr + fp->reg_txctrl);
+	fp->in_eth_mode = 0;
+
+	del_timer_sync(&fp->timer);
+
+	free_irq(dev->irq, dev);
+
+	/* Free all the rx skbuffs */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		if (fp->rx_skbuff[i] != NULL) {
+			pci_unmap_single(fp->this_card_priv->
+					 pci_dev,
+					 readl(&fp->rx_desc[i].
+					       desc_a),
+					 fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+			dev_kfree_skb(fp->rx_skbuff[i]);
+			fp->rx_skbuffs_out++;
+			fp->rx_skbuff[i] = NULL;
+		}
+	}
+	/* and tx */
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		if (fp->tx_skbuff[i] != NULL) {
+			pci_unmap_single(fp->this_card_priv->
+					 pci_dev,
+					 readl(&fp->tx_desc[i].
+					       desc_a),
+					 readl(&fp->tx_desc[i].
+					       desc_b) & frame_length,
+					 PCI_DMA_TODEVICE);
+			dev_kfree_skb(fp->tx_skbuff[i]);
+			fp->tx_skbuffs_out++;
+			fp->tx_skbuff[i] = NULL;
+		}
+	}
+	up_write(&fp->this_card_priv->semaphore);
+	return 0;
+}
+
+static struct net_device_stats *fepci_get_stats(struct net_device *dev)
+{
+	struct fepci_ch_private *fp = dev->priv;
+	return &fp->stats;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+	DBG_PRINT("set_rx_mode\n");
+}
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct fepci_ch_private *fp = dev->priv;
+	char data = ((unsigned)rq->ifr_data) & 0xff;
+	int ret = 0;
+
+	DBG_PRINT("%s: netdev_ioctl called (command_nmbr:0x%x).\n",
+		  dev->name, cmd);
+
+	switch (cmd) {
+	case FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE:
+		DBG_PRINT
+		    (" ioctl stream bufsize commanded. (bufsize:0x%x)\n", data);
+		down_write(&fp->this_card_priv->semaphore);
+		if (fp->in_stream_mode) {
+			up_write(&fp->this_card_priv->semaphore);
+			return -EBUSY;
+		}
+		fp->bufsize_order = data;
+		up_write(&fp->this_card_priv->semaphore);
+		break;
+	case FEPCI_NETDEV_IOCTL_STREAM_UNITSIZE:
+		DBG_PRINT
+		    (" ioctl stream unitsize commanded. (unitsize:0x%x)\n",
+		     data);
+		down_write(&fp->this_card_priv->semaphore);
+		if (fp->in_stream_mode) {
+			up_write(&fp->this_card_priv->semaphore);
+			return -EBUSY;
+		}
+		fp->fake_unit_sz_order = data;
+		up_write(&fp->this_card_priv->semaphore);
+		break;
+	case FEPCI_NETDEV_IOCTL_STREAM_OPEN:
+		DBG_PRINT(" ioctl stream open commanded.\n");
+		ret = fepci_stream_open(dev);
+		break;
+	case FEPCI_NETDEV_IOCTL_STREAM_START:
+		DBG_PRINT(" ioctl stream start commanded.\n");
+		ret = fepci_stream_start(dev);
+		break;
+	case FEPCI_NETDEV_IOCTL_STREAM_CLOSE:
+		DBG_PRINT(" ioctl stream close commanded.\n");
+		ret = fepci_stream_close(dev);
+		break;
+	default:
+		DBG_PRINT(" unknown ioctl command 0x%x.\n", cmd);
+		return -ENOTTY;
+	}
+	return ret;
+}
+
+static void fepci_remove_one(struct pci_dev *pdev)
+{
+	/* from first dev, same in all */
+	struct net_device *dev = pci_get_drvdata(pdev);
+	struct fepci_ch_private *fp = dev->priv;
+	struct fepci_card_private *cardp = fp->this_card_priv;
+	unsigned int i;
+
+	writel(0, (void *)(cardp->ioaddr + reg_custom));
+	/* alarm manager int off */
+
+	for (i = 0; i < CHANNELS; i++) {
+		dev = cardp->ch_privates[i]->this_dev;
+		fp = dev->priv;
+#ifdef DEBUG_PROC_FILES
+		fepci_proc_cleanup_channel(cardp->card_number,
+					   fp->channel_number);
+#endif /* DEBUG_PROC_FILES */
+		unregister_netdev(dev);
+		fepci_stream_close(dev);
+		free_netdev(dev);
+	}
+
+	pdev = cardp->pci_dev;
+	free_irq(pdev->irq, (void *)cardp);
+
+	down_write(&cardp->semaphore);
+
+	fepci_proc_cleanup_card(cardp->card_number);
+
+	pci_set_drvdata(pdev, NULL);
+
+	if (cardp->card_number + 1 == find_cnt)
+		find_cnt--;
+	cardp->pci_dev = NULL;
+
+	iounmap(cardp->ioaddr);
+
+	up_write(&cardp->semaphore);
+
+	pci_disable_device(pdev);
+	pci_release_regions(pdev);
+}
+
+static struct pci_driver fepci_driver = {
+name:		DRV_NAME,
+id_table:	fepci_pci_tbl,
+probe:		fepci_init_one,
+remove:		fepci_remove_one,
+};
+
+static int __init fepci_init(void)
+{
+	unsigned card = MAX_DEVICES - 1;
+	do
+		init_rwsem(&card_privates[card].semaphore);
+	while (card--);
+	major = fepci_register_char_device();
+	if (major < 0)
+		return major;
+	stream_pointers =
+	    (unsigned long)get_zeroed_page(GFP_KERNEL | __GFP_DMA);
+	DBG_PRINT(" %x.\n", stream_pointers);
+	if (stream_pointers == 0) {
+		fepci_unregister_char_device();
+		return -ENOMEM;
+	}
+	DBG_PRINT("SetPageReserved %u.\n", stream_pointers);
+	SetPageReserved(virt_to_page(stream_pointers));
+	DBG_PRINT("fepci_proc_init_driver.\n");
+	fepci_proc_init_driver();
+	DBG_PRINT("pci_register_driver %p.\n", &fepci_driver);
+	{
+		int ret = pci_register_driver(&fepci_driver);
+		if (ret) {
+			fepci_unregister_char_device();
+			ClearPageReserved(virt_to_page(stream_pointers));
+			free_page(stream_pointers);
+			DBG_PRINT
+			    ("pci_register_driver %p failed with %d.\n",
+			     &fepci_driver, ret);
+			fepci_proc_cleanup_driver();
+			return ret;
+		}
+		DBG_PRINT("fepci_init %d.\n", ret);
+		return ret;
+	}
+}
+
+static void __exit fepci_cleanup(void)
+{
+	DBG_PRINT("fepci_cleanup\n");
+	pci_unregister_driver(&fepci_driver);
+	fepci_proc_cleanup_driver();
+	fepci_unregister_char_device();
+	ClearPageReserved(virt_to_page(stream_pointers));
+	free_page(stream_pointers);
+}
+
+module_init(fepci_init);
+module_exit(fepci_cleanup);
+
+/* proc-filesystem specific stuff: */
+
+#ifdef DEBUG_PROC_FILES
+
+static char string_true[] = "<true> ";
+static char string_false[] = "   -   ";
+
+/* convert boolean value to more clarifying string format */
+static char *b(int b)
+{
+	if (b)
+		return string_true;
+	return string_false;
+}
+
+static int fepci_proc_read_descriptors(char *buf, char **start,
+				       off_t offset, int len, int *eof,
+				       void *data)
+{
+	int pos = 0;
+
+	int d;			/* descriptor */
+
+	struct fepci_ch_private *fp;
+	struct fepci_card_private *cardp;
+	fp = (struct fepci_ch_private *)data;
+	cardp = (struct fepci_card_private *)fp->this_card_priv;
+
+	pos +=
+	    sprintf(buf + pos,
+		    "Rx descriptors of channel %u of card %u: \n",
+		    fp->channel_number, cardp->card_number);
+	pos += sprintf(buf + pos, "\n");
+
+	for (d = 0; d < RX_RING_SIZE; d++) {
+		pos += sprintf(buf + pos, " rx descriptor number %u : \n", d);
+		pos +=
+		    sprintf(buf + pos, "  desc_a value : 0x%8x  \n",
+			    readl(&fp->rx_desc[d].desc_a));
+		pos +=
+		    sprintf(buf + pos, "  desc_b value : 0x%8x  \n",
+			    readl(&fp->rx_desc[d].desc_b));
+	}
+
+	pos += sprintf(buf + pos, "\n");
+	pos +=
+	    sprintf(buf + pos,
+		    "Tx descriptors of channel %u of card %u: \n",
+		    fp->channel_number, cardp->card_number);
+	pos += sprintf(buf + pos, "\n");
+
+	for (d = 0; d < TX_RING_SIZE; d++) {
+		pos += sprintf(buf + pos, " tx descriptor number %u : \n", d);
+		pos +=
+		    sprintf(buf + pos, "  desc_a value : 0x%8x  \n",
+			    readl(&fp->tx_desc[d].desc_a));
+		pos +=
+		    sprintf(buf + pos, "  desc_b value : 0x%8x  \n",
+			    readl(&fp->tx_desc[d].desc_b));
+	}
+
+	pos += sprintf(buf + pos, "\n");
+	pos += sprintf(buf + pos, "last line\n");
+
+	*eof = 1;
+	return pos;
+}
+
+static int fepci_proc_read_stream_counters(char *buf, char **start,
+					   off_t offset, int len, int *eof,
+					   void *data)
+{
+	int pos = 0;
+
+	int fifo;
+	int size;
+	int crc;
+	int octet;
+	int line;
+	int frame_dropped;
+	unsigned *rx_desc_err_table[2][2][2][2] = fp->rx_desc_err_table_stream;
+	struct fepci_ch_private *fp = (struct fepci_ch_private *)data;
+	struct fepci_card_private *cardp =
+	    (struct fepci_card_private *)fp->this_card_priv;
+
+	pos +=
+	    sprintf(buf + pos,
+		    "Stream status for channel %u of card %u : \n",
+		    fp->channel_number, cardp->card_number);
+	if (fp->in_stream_mode) {
+		pos += sprintf(buf + pos, " Channel is in stream mode \n");
+		if (fp->stream_on)
+			pos += sprintf(buf + pos, " Stream is on (started) \n");
+		else
+			pos +=
+			    sprintf(buf + pos,
+				    " Stream is off (not started)\n");
+	} else
+		pos += sprintf(buf + pos, " Channel is NOT in stream mode  \n");
+	pos += sprintf(buf + pos, "\n");
+
+	pos +=
+	    sprintf(buf + pos,
+		    "Error counters for channel %u of card %u "
+		    "(in stream mode) : \n",
+		    fp->channel_number, cardp->card_number);
+
+	pos +=
+	    sprintf(buf + pos, " rx_desc_fifo_err_stream : %10u  \n",
+		    fp->rx_desc_fifo_err_stream);
+
+	pos +=
+	    sprintf(buf + pos, " rx_desc_size_err_stream : %10u  \n",
+		    fp->rx_desc_size_err_stream);
+	pos +=
+	    sprintf(buf + pos, " rx_desc_crc_err_stream : %11u  \n",
+		    fp->rx_desc_crc_err_stream);
+	pos +=
+	    sprintf(buf + pos, " rx_desc_octet_err_stream : %9u  \n",
+		    fp->rx_desc_octet_err_stream);
+	pos +=
+	    sprintf(buf + pos, " rx_desc_line_err_stream : %10u  \n",
+		    fp->rx_desc_line_err_stream);
+	pos +=
+	    sprintf(buf + pos, " tx_desc_fifo_err_stream : %10u  \n",
+		    fp->tx_desc_fifo_err_stream);
+	pos +=
+	    sprintf(buf + pos, " rx_int_fifo_err_stream : %11u  \n",
+		    fp->rx_int_fifo_err_stream);
+	pos +=
+	    sprintf(buf + pos,
+		    " rx_int_frame_dropped_err_stream : %2u  \n",
+		    fp->rx_int_frame_dropped_err_stream);
+	pos +=
+	    sprintf(buf + pos, " tx_int_fifo_err_stream : %11u  \n",
+		    fp->tx_int_fifo_err_stream);
+
+	pos += sprintf(buf + pos, "\n");
+	pos +=
+	    sprintf(buf + pos,
+		    "Other counters for channel %u of card %u "
+		    "(in stream mode) : \n",
+		    fp->channel_number, cardp->card_number);
+	pos +=
+	    sprintf(buf + pos, " interrupts :    %10u  \n",
+		    fp->interrupts_stream);
+	pos +=
+	    sprintf(buf + pos, " rx_interrupts : %10u  \n",
+		    fp->rx_interrupts_stream);
+	pos +=
+	    sprintf(buf + pos, " tx_interrupts : %10u  \n",
+		    fp->tx_interrupts_stream);
+	pos += sprintf(buf + pos, "\n");
+
+	pos +=
+	    sprintf(buf + pos,
+		    "Error counter tables for channel %u of card %u: \n",
+		    fp->channel_number, cardp->card_number);
+	pos += sprintf(buf + pos, " rx_desc_error_table_stream:\n");
+	pos +=
+	    sprintf(buf + pos,
+		    "  [fifo]  [size]  [crc]   [octet]  [line]  : [count]\n");
+
+	for (fifo = 0; fifo < 2; fifo++)
+		for (size = 0; size < 2; size++)
+			for (crc = 0; crc < 2; crc++)
+				for (octet = 0; octet < 2; octet++)
+					for (line = 0; line < 2; line++)
+						pos +=
+						    sprintf(buf + pos,
+							    "  %s %s %s %s  %s "
+							    ": %7u \n",
+							    b(fifo),
+							    b(size),
+							    b(crc),
+							    b(octet),
+							    b(line),
+							    rx_desc_err_table
+							    [fifo][size]
+							    [crc][octet]
+							    [line]);
+
+	pos += sprintf(buf + pos, "\n");
+	pos += sprintf(buf + pos, " int_error_table:\n");
+	pos += sprintf(buf + pos, "  [rx_fifo]  [frame_dropped] : [count]\n");
+	for (fifo = 0; fifo < 2; fifo++)
+		for (frame_dropped = 0; frame_dropped < 2; frame_dropped++)
+			pos +=
+			    sprintf(buf + pos,
+				    "  %s    %s         : %7u\n", b(fifo),
+				    b(frame_dropped),
+				    fp->int_err_table_stream[fifo]
+				    [frame_dropped]);
+
+	pos += sprintf(buf + pos, "\n");
+
+	pos +=
+	    sprintf(buf + pos,
+		    "Small packet counters for channel %u of card %u "
+		    "(stream mode): \n",
+		    fp->channel_number, cardp->card_number);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 0      : %10u  \n",
+		    fp->rx_packets_of_size_0_stream);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 1      : %10u  \n",
+		    fp->rx_packets_of_size_1_stream);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 2      : %10u  \n",
+		    fp->rx_packets_of_size_2_stream);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 3      : %10u  \n",
+		    fp->rx_packets_of_size_3_stream);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 4..7   : %10u  \n",
+		    fp->rx_packets_of_size_4_7_stream);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 8..15  : %10u  \n",
+		    fp->rx_packets_of_size_8_15_stream);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 16..31 : %10u  \n",
+		    fp->rx_packets_of_size_16_31_stream);
+
+	pos += sprintf(buf + pos, "\n");
+	pos += sprintf(buf + pos, "last line\n");
+
+	*eof = 1;
+	return pos;
+}
+
+static int fepci_proc_read_counters(char *buf, char **start, off_t offset,
+				    int len, int *eof, void *data)
+{
+	int pos = 0;
+
+	int fifo;
+	int size;
+	int crc;
+	int octet;
+	int line;
+	int frame_dropped;
+
+	struct fepci_ch_private *fp;
+	struct fepci_card_private *cardp;
+
+	fp = (struct fepci_ch_private *)data;
+	cardp = (struct fepci_card_private *)fp->this_card_priv;
+
+	pos +=
+	    sprintf(buf + pos,
+		    "Error counters for channel %u of card %u: \n",
+		    fp->channel_number, cardp->card_number);
+
+	pos +=
+	    sprintf(buf + pos, " rx_desc_fifo_err : %10u  \n",
+		    fp->rx_desc_fifo_err);
+	pos +=
+	    sprintf(buf + pos, " rx_desc_size_err : %10u  \n",
+		    fp->rx_desc_size_err);
+	pos +=
+	    sprintf(buf + pos, " rx_desc_crc_err : %11u  \n",
+		    fp->rx_desc_crc_err);
+	pos +=
+	    sprintf(buf + pos, " rx_desc_octet_err : %9u  \n",
+		    fp->rx_desc_octet_err);
+	pos +=
+	    sprintf(buf + pos, " rx_desc_line_err : %10u  \n",
+		    fp->rx_desc_line_err);
+	pos +=
+	    sprintf(buf + pos, " tx_desc_fifo_err : %10u  \n",
+		    fp->tx_desc_fifo_err);
+	pos +=
+	    sprintf(buf + pos, " rx_int_fifo_err : %11u  \n",
+		    fp->rx_int_fifo_err);
+	pos +=
+	    sprintf(buf + pos, " rx_int_frame_dropped_err : %2u  \n",
+		    fp->rx_int_frame_dropped_err);
+	pos +=
+	    sprintf(buf + pos, " tx_int_fifo_err : %11u  \n",
+		    fp->tx_int_fifo_err);
+
+	pos += sprintf(buf + pos, "\n");
+	pos +=
+	    sprintf(buf + pos,
+		    "Other counters for channel %u of card %u: \n",
+		    fp->channel_number, cardp->card_number);
+	pos += sprintf(buf + pos, " interrupts :    %10u  \n", fp->interrupts);
+	pos +=
+	    sprintf(buf + pos, " rx_interrupts : %10u  \n", fp->rx_interrupts);
+	pos +=
+	    sprintf(buf + pos, " tx_interrupts : %10u  \n", fp->tx_interrupts);
+	pos += sprintf(buf + pos, "\n");
+
+	pos +=
+	    sprintf(buf + pos, " rx_skbuffs_in : %10u  \n", fp->rx_skbuffs_in);
+	pos +=
+	    sprintf(buf + pos, " rx_skbuffs_out : %9u  \n", fp->rx_skbuffs_out);
+	pos +=
+	    sprintf(buf + pos, " difference : %13i  \n",
+		    fp->rx_skbuffs_in - fp->rx_skbuffs_out);
+	pos +=
+	    sprintf(buf + pos, " tx_skbuffs_in : %10u  \n", fp->tx_skbuffs_in);
+	pos +=
+	    sprintf(buf + pos, " tx_skbuffs_out : %9u  \n", fp->tx_skbuffs_out);
+	pos +=
+	    sprintf(buf + pos, " difference : %13i  \n",
+		    fp->tx_skbuffs_in - fp->tx_skbuffs_out);
+
+	pos += sprintf(buf + pos, "\n");
+	pos +=
+	    sprintf(buf + pos,
+		    "Error counter tables for channel %u of card %u: \n",
+		    fp->channel_number, cardp->card_number);
+	pos += sprintf(buf + pos, " rx_desc_error_table:\n");
+	pos +=
+	    sprintf(buf + pos,
+		    "  [fifo]  [size]  [crc]   [octet]  [line]  : [count]\n");
+
+	for (fifo = 0; fifo < 2; fifo++)
+		for (size = 0; size < 2; size++)
+			for (crc = 0; crc < 2; crc++)
+				for (octet = 0; octet < 2; octet++)
+					for (line = 0; line < 2; line++)
+						pos +=
+						    sprintf(buf + pos,
+							    "  %s %s %s %s  %s "
+							    ": %7u \n",
+							    b(fifo),
+							    b(size),
+							    b(crc),
+							    b(octet),
+							    b(line),
+							    fp->
+							    rx_desc_err_table
+							    [fifo][size]
+							    [crc][octet]
+							    [line]);
+
+	pos += sprintf(buf + pos, "\n");
+	pos += sprintf(buf + pos, " int_error_table:\n");
+	pos += sprintf(buf + pos, "  [rx_fifo]  [frame_dropped] : [count]\n");
+	for (fifo = 0; fifo < 2; fifo++)
+		for (frame_dropped = 0; frame_dropped < 2; frame_dropped++)
+			pos +=
+			    sprintf(buf + pos,
+				    "  %s    %s         : %7u\n", b(fifo),
+				    b(frame_dropped),
+				    fp->int_err_table[fifo][frame_dropped]);
+
+	pos += sprintf(buf + pos, "\n");
+
+	pos +=
+	    sprintf(buf + pos,
+		    "Small packet counters for channel %u of card %u: \n",
+		    fp->channel_number, cardp->card_number);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 0      : %10u  \n",
+		    fp->rx_packets_of_size_0);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 1      : %10u  \n",
+		    fp->rx_packets_of_size_1);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 2      : %10u  \n",
+		    fp->rx_packets_of_size_2);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 3      : %10u  \n",
+		    fp->rx_packets_of_size_3);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 4..7   : %10u  \n",
+		    fp->rx_packets_of_size_4_7);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 8..15  : %10u  \n",
+		    fp->rx_packets_of_size_8_15);
+	pos +=
+	    sprintf(buf + pos, " rx_packets of size 16..31 : %10u  \n",
+		    fp->rx_packets_of_size_16_31);
+
+	pos += sprintf(buf + pos, "\n");
+	pos += sprintf(buf + pos, "last line\n");
+
+	*eof = 1;
+	return pos;
+}
+
+static int fepci_proc_read_registers(char *buf, char **start, off_t offset,
+				     int len, int *eof, void *data)
+{
+	int pos = 0;
+
+	struct fepci_ch_private *fp;
+	struct fepci_card_private *cardp;
+	void *ioaddr;
+
+	fp = (struct fepci_ch_private *)data;
+	cardp = (struct fepci_card_private *)fp->this_card_priv;
+	ioaddr = cardp->ioaddr;
+
+	pos +=
+	    sprintf(buf + pos, "Registers for channel %u of card %u: \n",
+		    fp->channel_number, cardp->card_number);
+
+	pos +=
+	    sprintf(buf + pos, " interrupt status : 0x%8x  \n",
+		    get_int_status(fp->channel_number, ioaddr));
+	pos +=
+	    sprintf(buf + pos, " interrupt mask   : 0x%8x  \n",
+		    get_int_mask(fp->channel_number, ioaddr));
+	pos +=
+	    sprintf(buf + pos, " rxctrl           : 0x%8x  \n",
+		    readl(ioaddr + fp->reg_rxctrl));
+	pos +=
+	    sprintf(buf + pos, " txctrl           : 0x%8x  \n",
+		    readl(ioaddr + fp->reg_txctrl));
+
+	pos += sprintf(buf + pos, "\n");
+
+	pos += sprintf(buf + pos, "last line\n");
+
+	*eof = 1;
+	return pos;
+}
+
+#endif /* DEBUG_PROC_FILES */
+
+int get_line_data_rate_value(unsigned char line_rate)
+{
+	switch (line_rate) {
+	case 0x00:
+		return 0;
+	case 0x01:
+		return 1;
+	case 0x05:
+		return 8;
+	case 0x06:
+		return 10;
+	case 0x14:
+		return 256;
+	case 0x15:
+		return 300;
+	case 0x20:
+		return 1;
+	case 0x28:
+		return 8;
+	case 0x29:
+		return 10;
+	case 0x30:
+		return 32;
+	case 0x34:
+		return 56;
+	case 0x36:
+		return 64;
+	case 0x60:
+		return 1;
+	case 0xa0:
+		return 1;
+	default:
+		return -1;
+	}
+}
+
+char get_line_data_rate_unit(unsigned char line_rate)
+{
+	switch (line_rate) {
+	case 0x00:
+		return 0;
+	case 0x01:
+		return 0;
+	case 0x05:
+		return 0;
+	case 0x06:
+		return 0;
+	case 0x14:
+		return 0;
+	case 0x15:
+		return 0;
+	case 0x20:
+		return 'k';
+	case 0x28:
+		return 'k';
+	case 0x29:
+		return 'k';
+	case 0x30:
+		return 'k';
+	case 0x34:
+		return 'k';
+	case 0x36:
+		return 'k';
+	case 0x60:
+		return 'M';
+	case 0xa0:
+		return 'G';
+	default:
+		return 0;
+	}
+}
+
+int print_line_type(unsigned char type, char *buf, int pos)
+{
+	DBG_PRINT("print_line_type %c\n", type);
+	switch (type) {
+	case 0:
+		pos += sprintf(buf + pos, "NONE");
+		break;
+	case 1:
+		pos += sprintf(buf + pos, "DCombus management bus");
+		break;
+	case 2:
+		pos += sprintf(buf + pos, "V.24");
+		break;
+	case 3:
+		pos += sprintf(buf + pos, "X.21");
+		break;
+	case 4:
+		pos += sprintf(buf + pos, "V.35");
+		break;
+	case 5:
+		pos += sprintf(buf + pos, "V.11");
+		break;
+	case 6:
+		pos += sprintf(buf + pos, "IDSL (ISDN Basic Rate");
+		break;
+	case 7:
+		pos += sprintf(buf + pos, "E1 nonframed/framed");
+		break;
+	case 8:
+		pos += sprintf(buf + pos, "E2 nonframed/framed");
+		break;
+	case 9:
+		pos += sprintf(buf + pos, "E3 nonframed/framed");
+		break;
+	case 10:
+		pos += sprintf(buf + pos, "T1 nonframed/framed");
+		break;
+	case 11:
+		pos += sprintf(buf + pos, "T2 nonframed/framed");
+		break;
+	case 12:
+		pos += sprintf(buf + pos, "T3 nonframed/framed");
+		break;
+	case 13:
+		pos += sprintf(buf + pos, "HDSL");
+		break;
+	case 14:
+		pos += sprintf(buf + pos, "ADSL");
+		break;
+	case 15:
+		pos += sprintf(buf + pos, "SDSL");
+		break;
+	case 16:
+		pos += sprintf(buf + pos, "HDSL2");
+		break;
+	case 17:
+		pos += sprintf(buf + pos, "VDSL");
+		break;
+	case 18:
+		pos += sprintf(buf + pos, "G.shdsl");
+		break;
+	case 19:
+		pos += sprintf(buf + pos, "SDH");
+		break;
+	case 255:
+		pos +=
+		    sprintf(buf + pos, "COMBINATION, get list using mailbox");
+		break;
+	default:
+		pos += sprintf(buf + pos, "reserved");
+	}
+	return pos;
+}
+
+int print_card_type(unsigned char type, char *buf, int pos)
+{
+	DBG_PRINT("print_card_type\n");
+	switch (type) {
+	case 0x00:
+		pos += sprintf(buf + pos, "illegal  ");
+		break;
+	case 0x01:
+	case 0x02:
+		pos += sprintf(buf + pos, "Retina   ");
+		break;
+	default:
+		pos += sprintf(buf + pos, "reserved ");
+	}
+
+	return pos;
+}
+
+int print_card_model(unsigned char model, char *buf, int pos)
+{
+	DBG_PRINT("print_card_model\n");
+	switch (model) {
+	case 0x00:
+		pos += sprintf(buf + pos, "illegal  ");
+		break;
+	case 0x01:
+		pos += sprintf(buf + pos, "E2200    ");
+		break;
+	case 0x02:
+		pos += sprintf(buf + pos, "C5400    ");
+		break;
+	default:
+		pos += sprintf(buf + pos, "reserved ");
+	}
+
+	return pos;
+}
+
+static inline u8 get_common_reg(void *ioaddr, unsigned long offsett)
+{
+	unsigned long byte = __test_and_clear_bit(0, &offsett);
+	u8 reg;
+	DBG_PRINT("get_common_reg %p, old %p\n",
+		  ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+		  byte,
+		  &((u32 *) (ioaddr +
+			     FEPCI_IDENTIFICATION_OFFSETT))[offsett / 2]);
+	reg =
+	    readb(ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+		  byte);
+	DBG_PRINT("get_common_reg %p: %u\n",
+		  ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1) +
+		  byte, reg);
+	return reg;
+}
+
+static int fepci_proc_read_features(char *buf, char **start, off_t offset,
+				    int len, int *eof, void *data)
+{
+	int pos = 0;
+	int tab = 0;
+	struct fepci_card_private *cardp;
+	void *ioaddr;
+
+	cardp = (struct fepci_card_private *)data;
+
+	down_read(&cardp->semaphore);
+
+	if (unlikely(cardp->pci_dev == NULL))
+		goto EOF;
+
+	ioaddr = cardp->ioaddr;
+
+	switch (offset) {
+	case 0:
+		pos += sprintf(buf + pos, "\n");
+
+		pos += sprintf(buf + pos, "card type:          ");
+		pos = print_card_type(get_common_reg(ioaddr, 0x00), buf, pos);
+		pos += sprintf(buf + pos, "\n");
+
+		pos += sprintf(buf + pos, "model:              ");
+		pos = print_card_model(get_common_reg(ioaddr, 0x00), buf, pos);
+		pos += sprintf(buf + pos, "\n");
+
+		pos += sprintf(buf + pos, "serial number:      ");
+		pos +=
+		    sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x07));
+		pos +=
+		    sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x06));
+		pos +=
+		    sprintf(buf + pos, "%2.2x", get_common_reg(ioaddr, 0x05));
+		pos +=
+		    sprintf(buf + pos, "%2.2x\n", get_common_reg(ioaddr, 0x04));
+
+		pos += sprintf(buf + pos, "version:            ");
+		pos +=
+		    sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x08));
+		pos +=
+		    sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x09));
+		pos +=
+		    sprintf(buf + pos, "%2.2x.", get_common_reg(ioaddr, 0x0a));
+		pos +=
+		    sprintf(buf + pos, "%2.2x\n", get_common_reg(ioaddr, 0x0b));
+
+		pos += sprintf(buf + pos, "\n");
+
+		pos += sprintf(buf + pos, "max. pipe count:    ");
+		pos += sprintf(buf + pos, "%d\n", get_common_reg(ioaddr, 0x03));
+
+		up_read(&cardp->semaphore);
+		*start = (char *)1;
+		*eof = 1;
+		return min(pos, len);
+	case 1:
+		pos += sprintf(buf + pos, "line count:         ");
+		pos += sprintf(buf + pos, "%d\n", get_common_reg(ioaddr, 0x02));
+
+		pos += sprintf(buf + pos, "line type:          ");
+		pos = print_line_type(get_common_reg(ioaddr, 0x0c), buf, pos);
+
+		/* JT: Secondary interface is also printed also,
+		 * if there is one. */
+		if (get_common_reg(ioaddr, 0x10) > 0) {
+			pos += sprintf(buf + pos, " / ");
+			pos =
+			    print_line_type(get_common_reg(ioaddr, 0x10),
+					    buf, pos);
+		}
+
+		pos += sprintf(buf + pos, "\n");
+
+		/* JT: Line modes are printed only if
+		 * the card has NO secondary interface. */
+		if (get_common_reg(ioaddr, 0x10) == 0) {
+			pos += sprintf(buf + pos, "line modes:         ");
+			tab = 0;
+			if (get_common_reg(ioaddr, 0x21) & 0x80) {
+				tab = 1;
+				pos +=
+				    sprintf(buf + pos,
+					    "framed mode supported\n");
+			}
+			if (get_common_reg(ioaddr, 0x21) & 0x40) {
+				if (tab)
+					pos +=
+					    sprintf(buf + pos,
+						    "                    ");
+				else
+					tab = 1;
+				pos +=
+				    sprintf(buf + pos,
+					    "unframed mode supported\n");
+			}
+			if (get_common_reg(ioaddr, 0x21) & 0x20) {
+				if (tab)
+					pos +=
+					    sprintf(buf + pos,
+						    "                    ");
+				else
+					tab = 1;
+				pos +=
+				    sprintf(buf + pos,
+					    "line code setting available\n");
+			}
+			if (get_common_reg(ioaddr, 0x21) & 0x10) {
+				if (tab)
+					pos +=
+					    sprintf(buf + pos,
+						    "                    ");
+				else
+					tab = 1;
+				pos +=
+				    sprintf(buf + pos,
+					    "line attenuation available\n");
+			}
+			if (get_common_reg(ioaddr, 0x21) & 0x08) {
+				if (tab)
+					pos +=
+					    sprintf(buf + pos,
+						    "                    ");
+				else
+					tab = 1;
+				pos +=
+				    sprintf(buf + pos,
+					    "line quality available\n");
+			}
+			if (get_common_reg(ioaddr, 0x21) & 0x04) {
+				if (tab)
+					pos +=
+					    sprintf(buf + pos,
+						    "                    ");
+				else
+					tab = 1;
+				pos +=
+				    sprintf(buf + pos,
+					    "timing setting available\n");
+			}
+			if (get_common_reg(ioaddr, 0x21) & 0x02) {
+				if (tab)
+					pos +=
+					    sprintf(buf + pos,
+						    "                    ");
+				else
+					tab = 1;
+				pos += sprintf(buf + pos, "reserved\n");
+			}
+			if (get_common_reg(ioaddr, 0x21) & 0x01) {
+				if (tab)
+					pos +=
+					    sprintf(buf + pos,
+						    "                    ");
+				else
+					tab = 1;
+				pos +=
+				    sprintf(buf + pos,
+					    "line shutdown setting "
+					    "available\n");
+			}
+			if (get_common_reg(ioaddr, 0x21) == 0) {
+				if (tab)
+					pos +=
+					    sprintf(buf + pos,
+						    "                    ");
+				else
+					tab = 1;
+				pos += sprintf(buf + pos, "n/a\n");
+			}
+		}
+		up_read(&cardp->semaphore);
+		*start = (char *)1;
+		*eof = 1;
+		return min(pos, len);
+	case 2:
+		pos += sprintf(buf + pos, "line rate:          ");
+		if (get_common_reg(ioaddr, 0x28) ==
+		    get_common_reg(ioaddr, 0x2a)) {
+			if (get_line_data_rate_value
+			    (get_common_reg(ioaddr, 0x26)) < 0)
+				pos += sprintf(buf + pos, "Invalid value");
+			else if (get_line_data_rate_value
+				 (get_common_reg(ioaddr, 0x26))) {
+				pos += sprintf(buf + pos, "fixed ");
+				pos +=
+				    sprintf(buf + pos, "%d ",
+					    get_line_data_rate_value
+					    (get_common_reg(ioaddr, 0x26))
+					    * get_common_reg_word(ioaddr,
+								  0x28));
+				if (get_line_data_rate_unit
+				    (get_common_reg(ioaddr, 0x26)))
+					pos +=
+					    sprintf(buf + pos, "%c",
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x26)));
+				pos += sprintf(buf + pos, "bps");
+			} else
+				pos +=
+				    sprintf(buf + pos,
+					    "No multiplier, "
+					    "get value or list using mailbox");
+		} else {
+			if (get_line_data_rate_value
+			    (get_common_reg(ioaddr, 0x26)) < 0)
+				pos += sprintf(buf + pos, "Invalid value");
+			else if (get_line_data_rate_value
+				 (get_common_reg(ioaddr, 0x26))) {
+				pos +=
+				    sprintf(buf + pos, "%d ",
+					    get_line_data_rate_value
+					    (get_common_reg(ioaddr, 0x26))
+					    * get_common_reg_word(ioaddr,
+								  0x28));
+				if (get_line_data_rate_unit
+				    (get_common_reg(ioaddr, 0x26)))
+					pos +=
+					    sprintf(buf + pos, "%c",
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x26)));
+				pos += sprintf(buf + pos, "bps - ");
+				pos +=
+				    sprintf(buf + pos, "%d ",
+					    get_line_data_rate_value
+					    (get_common_reg(ioaddr, 0x26))
+					    * get_common_reg_word(ioaddr,
+								  0x2a));
+				if (get_line_data_rate_unit
+				    (get_common_reg(ioaddr, 0x26)))
+					pos +=
+					    sprintf(buf + pos, "%c",
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x26)));
+				pos += sprintf(buf + pos, "bps");
+			} else
+				pos +=
+				    sprintf(buf + pos,
+					    "No multiplier, "
+					    "get value or list using mailbox");
+		}
+
+		/* JT: Line rate of a secondary interface is printed,
+		 * if there is one. */
+		if (get_common_reg(ioaddr, 0x10) > 0) {
+			pos += sprintf(buf + pos, " / ");
+
+			if (get_common_reg(ioaddr, 0x34) ==
+			    get_common_reg(ioaddr, 0x36)) {
+				if (get_line_data_rate_value
+				    (get_common_reg(ioaddr, 0x26)) < 0)
+					pos +=
+					    sprintf(buf + pos, "Invalid value");
+				else if (get_line_data_rate_value
+					 (get_common_reg(ioaddr, 0x26))) {
+					pos += sprintf(buf + pos, "fixed ");
+					pos +=
+					    sprintf(buf + pos, "%d ",
+						    get_line_data_rate_value
+						    (get_common_reg
+						     (ioaddr,
+						      0x26)) *
+						    get_common_reg_word
+						    (ioaddr, 0x34));
+					if (get_line_data_rate_unit
+					    (get_common_reg(ioaddr, 0x26))) {
+						char unit =
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x26));
+						pos +=
+						    sprintf(buf + pos,
+							    "%c", unit);
+					}
+					pos += sprintf(buf + pos, "bps");
+				} else
+					pos +=
+					    sprintf(buf + pos,
+						    "No multiplier, "
+						    "get value or "
+						    "list using mailbox");
+			} else {
+				if (get_line_data_rate_value
+				    (get_common_reg(ioaddr, 0x26)) < 0)
+					pos +=
+					    sprintf(buf + pos, "Invalid value");
+				else if (get_line_data_rate_value
+					 (get_common_reg(ioaddr, 0x26))) {
+					pos +=
+					    sprintf(buf + pos, "%d ",
+						    get_line_data_rate_value
+						    (get_common_reg
+						     (ioaddr,
+						      0x26)) *
+						    get_common_reg_word
+						    (ioaddr, 0x34));
+					if (get_line_data_rate_unit
+					    (get_common_reg(ioaddr, 0x26))) {
+						char unit =
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x26));
+						pos +=
+						    sprintf(buf + pos,
+							    "%c", unit);
+					}
+					pos += sprintf(buf + pos, "bps - ");
+					pos +=
+					    sprintf(buf + pos, "%d ",
+						    get_line_data_rate_value
+						    (get_common_reg
+						     (ioaddr,
+						      0x26)) *
+						    get_common_reg_word
+						    (ioaddr, 0x36));
+					if (get_line_data_rate_unit
+					    (get_common_reg(ioaddr, 0x26))) {
+						char unit =
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x26));
+						pos +=
+						    sprintf(buf + pos,
+							    "%c", unit);
+					}
+					pos += sprintf(buf + pos, "bps");
+				} else
+					pos +=
+					    sprintf(buf + pos,
+						    "No multiplier, "
+						    "get value or list "
+						    "using mailbox");
+			}
+		}
+
+		pos += sprintf(buf + pos, "\n");
+
+		/* JT: Primary line special info is printed only
+		 * if the card has NO secondary interface. */
+		if (get_common_reg(ioaddr, 0x10) == 0) {
+			pos += sprintf(buf + pos, "rate sel. methods:  ");
+			if (get_common_reg(ioaddr, 0x28) ==
+			    get_common_reg(ioaddr, 0x2a))
+				pos += sprintf(buf + pos, "n/a\n");
+			else if ((get_common_reg(ioaddr, 0x22) & 4)
+				 && (get_common_reg(ioaddr, 0x23) & 4))
+				pos += sprintf(buf + pos, "adjust\n");
+			else if (((!get_common_reg(ioaddr, 0x22)) & 4)
+				 && (get_common_reg(ioaddr, 0x23) & 4))
+				pos += sprintf(buf + pos, "adjust(framed)\n");
+			else if ((get_common_reg(ioaddr, 0x22) & 4)
+				 && ((!get_common_reg(ioaddr, 0x23)) & 4))
+				pos += sprintf(buf + pos, "adjust(unframed)\n");
+			else
+				pos += sprintf(buf + pos, "\n");
+
+			pos += sprintf(buf + pos, "\n");
+
+			pos += sprintf(buf + pos, "unframed data rate: ");
+			if (get_common_reg(ioaddr, 0x30) ==
+			    get_common_reg(ioaddr, 0x32)) {
+				if (get_line_data_rate_value
+				    (get_common_reg(ioaddr, 0x27)) < 0)
+					pos +=
+					    sprintf(buf + pos,
+						    "Invalid value\n");
+				else if (get_line_data_rate_value
+					 (get_common_reg(ioaddr, 0x27))) {
+					pos += sprintf(buf + pos, "fixed ");
+					pos +=
+					    sprintf(buf + pos, "%d ",
+						    get_line_data_rate_value
+						    (get_common_reg
+						     (ioaddr,
+						      0x27)) *
+						    get_common_reg_word
+						    (ioaddr, 0x30));
+					if (get_line_data_rate_unit
+					    (get_common_reg(ioaddr, 0x27))) {
+						char unit =
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x27));
+						pos +=
+						    sprintf(buf + pos,
+							    "%c", unit);
+					}
+					pos += sprintf(buf + pos, "bps\n");
+				} else
+					pos +=
+					    sprintf(buf + pos,
+						    "No multiplier, "
+						    "get value or list using "
+						    "mailbox\n");
+			} else {
+				if (get_line_data_rate_value
+				    (get_common_reg(ioaddr, 0x27)) < 0)
+					pos +=
+					    sprintf(buf + pos,
+						    "Invalid value\n");
+				else if (get_line_data_rate_value
+					 (get_common_reg(ioaddr, 0x27))) {
+					pos +=
+					    sprintf(buf + pos, "%d ",
+						    get_line_data_rate_value
+						    (get_common_reg
+						     (ioaddr,
+						      0x27)) *
+						    get_common_reg_word
+						    (ioaddr, 0x30));
+					if (get_line_data_rate_unit
+					    (get_common_reg(ioaddr, 0x27))) {
+						char unit =
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x27));
+						pos +=
+						    sprintf(buf + pos,
+							    "%c", unit);
+					}
+					pos += sprintf(buf + pos, "bps - ");
+					pos +=
+					    sprintf(buf + pos, "%d ",
+						    get_line_data_rate_value
+						    (get_common_reg
+						     (ioaddr,
+						      0x27)) *
+						    get_common_reg_word
+						    (ioaddr, 0x32));
+					if (get_line_data_rate_unit
+					    (get_common_reg(ioaddr, 0x27))) {
+						char unit =
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x27));
+						pos +=
+						    sprintf(buf + pos,
+							    "%c", unit);
+					}
+					pos += sprintf(buf + pos, "bps\n");
+				} else
+					pos +=
+					    sprintf(buf + pos,
+						    "No multiplier, get value "
+						    "or list using mailbox\n");
+			}
+		}
+		up_read(&cardp->semaphore);
+		*start = (char *)1;
+		*eof = 1;
+		return min(pos, len);
+
+	case 3:
+		/* JT: Line modes are printed only
+		 * if the card has NO secondary interface. */
+		if (get_common_reg(ioaddr, 0x10) == 0) {
+			pos += sprintf(buf + pos, "rate sel. methods:  ");
+			if (get_common_reg(ioaddr, 0x30) ==
+			    get_common_reg(ioaddr, 0x32))
+				pos += sprintf(buf + pos, "n/a\n");
+			else {
+				if (get_common_reg(ioaddr, 0x23) & 1)
+					pos +=
+					    sprintf(buf + pos,
+						    "timeslot map\n");
+				if (get_common_reg(ioaddr, 0x23) & 2)
+					pos += sprintf(buf + pos, "adjust\n");
+			}
+
+			pos += sprintf(buf + pos, "framed data rate:   ");
+			if (get_common_reg(ioaddr, 0x2c) ==
+			    get_common_reg(ioaddr, 0x2e)) {
+				if (get_line_data_rate_value
+				    (get_common_reg(ioaddr, 0x27)) < 0)
+					pos +=
+					    sprintf(buf + pos,
+						    "Invalid value\n");
+				else if (get_line_data_rate_value
+					 (get_common_reg(ioaddr, 0x27))) {
+					pos += sprintf(buf + pos, "fixed ");
+					pos +=
+					    sprintf(buf + pos, "%d ",
+						    get_line_data_rate_value
+						    (get_common_reg
+						     (ioaddr,
+						      0x27)) *
+						    get_common_reg_word
+						    (ioaddr, 0x2c));
+					if (get_line_data_rate_unit
+					    (get_common_reg(ioaddr, 0x27))) {
+						char unit =
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x27));
+						pos +=
+						    sprintf(buf + pos,
+							    "%c", unit);
+					}
+					pos += sprintf(buf + pos, "bps\n");
+				} else
+					pos +=
+					    sprintf(buf + pos,
+						    "No multiplier, get value "
+						    "or list using mailbox\n");
+			} else {
+				if (get_line_data_rate_value
+				    (get_common_reg(ioaddr, 0x27)) < 0)
+					pos +=
+					    sprintf(buf + pos,
+						    "Invalid value\n");
+				else if (get_line_data_rate_value
+					 (get_common_reg(ioaddr, 0x27))) {
+					pos +=
+					    sprintf(buf + pos, "%d ",
+						    get_line_data_rate_value
+						    (get_common_reg
+						     (ioaddr,
+						      0x27)) *
+						    get_common_reg_word
+						    (ioaddr, 0x2c));
+					if (get_line_data_rate_unit
+					    (get_common_reg(ioaddr, 0x27))) {
+						char unit =
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x27));
+						pos +=
+						    sprintf(buf + pos,
+							    "%c", unit);
+					}
+					pos += sprintf(buf + pos, "bps - ");
+					pos +=
+					    sprintf(buf + pos, "%d ",
+						    get_line_data_rate_value
+						    (get_common_reg
+						     (ioaddr,
+						      0x27)) *
+						    get_common_reg_word
+						    (ioaddr, 0x2e));
+					if (get_line_data_rate_unit
+					    (get_common_reg(ioaddr, 0x27))) {
+						char unit =
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x27));
+						pos +=
+						    sprintf(buf + pos,
+							    "%c", unit);
+					}
+					pos += sprintf(buf + pos, "bps\n");
+				} else
+					pos +=
+					    sprintf(buf + pos,
+						    "No multiplier, get value "
+						    "or list using mailbox\n");
+			}
+
+			pos += sprintf(buf + pos, "rate sel. methods:  ");
+			if (get_common_reg_word(ioaddr, 0x2c) ==
+			    get_common_reg_word(ioaddr, 0x2e))
+				pos += sprintf(buf + pos, "n/a\n");
+			else {
+				if (get_common_reg(ioaddr, 0x22) & 1)
+					pos +=
+					    sprintf(buf + pos,
+						    "timeslot map\n");
+				if (get_common_reg(ioaddr, 0x22) & 2)
+					pos += sprintf(buf + pos, "adjust\n");
+			}
+		}
+		up_read(&cardp->semaphore);
+		*start = (char *)1;
+		*eof = 1;
+		return min(pos, len);
+	default:
+EOF:
+		up_read(&cardp->semaphore);
+		*start = (char *)0;
+		*eof = 1;
+		return 0;
+	}
+}
+
+static int fepci_proc_read_settings(char *buf, char **start, off_t offset,
+				    int len, int *eof, void *data)
+{
+	int pos = 0;
+	int i, j;
+
+	struct fepci_card_private *cardp;
+	void *ioaddr;
+
+	cardp = (struct fepci_card_private *)data;
+
+	down_read(&cardp->semaphore);
+
+	if (unlikely(cardp->pci_dev == NULL))
+		goto EOF;
+
+	ioaddr = cardp->ioaddr;
+
+	switch (offset) {
+	case 0:
+		j = get_common_reg(ioaddr, 0x02);
+
+		for (i = 0; i < j; i++) {
+			if ((get_common_reg_word(ioaddr, 0x44) >> i) & 1) {
+				pos += sprintf(buf + pos, "\nline %2d\n", i);
+				pos += sprintf(buf + pos, "-------\n");
+				pos +=
+				    sprintf(buf + pos, "mode:               ");
+				if ((get_common_reg_word(ioaddr, 0x46) >>
+				     i) & 1)
+					pos += sprintf(buf + pos, "framed\n");
+				else
+					pos += sprintf(buf + pos, "unframed\n");
+
+				pos +=
+				    sprintf(buf + pos, "scrambler:          ");
+				if ((get_common_reg_word(ioaddr, 0x48) >>
+				     i) & 1)
+					pos += sprintf(buf + pos, "enabled\n");
+				else if ((get_common_reg_word(ioaddr, 0x46)
+					  >> i) & 1)
+					pos += sprintf(buf + pos, "n/a\n");
+				else
+					pos += sprintf(buf + pos, "disabled\n");
+
+				pos +=
+				    sprintf(buf + pos, "line crc:           ");
+				if ((get_common_reg_word(ioaddr, 0x46) >>
+				     i) & 1) {
+					if ((get_common_reg_word
+					     (ioaddr, 0x4a) >> i) & 1)
+						pos +=
+						    sprintf(buf + pos,
+							    "enabled\n");
+					else if (get_common_reg
+						 (ioaddr, 0x22) & 0x20)
+						pos +=
+						    sprintf(buf + pos,
+							    "disabled\n");
+					else
+						pos +=
+						    sprintf(buf + pos, "n/a\n");
+				} else {
+					if ((get_common_reg_word
+					     (ioaddr, 0x4a) >> i) & 1)
+						pos +=
+						    sprintf(buf + pos,
+							    "enabled\n");
+					else if (get_common_reg
+						 (ioaddr, 0x23) & 0x20)
+						pos +=
+						    sprintf(buf + pos,
+							    "disabled\n");
+					else
+						pos +=
+			ÀGø+				}
+			}
+		}
+
+		/* If there are no enabled lines, "No enabled lines!" printed.
+		 * The settings file would remain empty otherwise. */
+		if ((get_common_reg_word(ioaddr, 0x44)) == 0)
+			pos += sprintf(buf + pos, "No enabled lines!\n");
+		up_read(&cardp->semaphore);
+		*start = (char *)1;
+		*eof = 1;
+		return min(pos, len);
+	case 1:
+		j = get_common_reg(ioaddr, 0x03);
+
+		if ((get_common_reg_word(ioaddr, 0x44)) != 0) {
+			for (i = 0; i < j; i++) {
+				pos += sprintf(buf + pos, "\npipe %2d\n", i);
+				pos += sprintf(buf + pos, "-------\n");
+
+				pos +=
+				    sprintf(buf + pos, "access mode:        ");
+				if ((get_common_reg_word(ioaddr, 0x4e) >>
+				     i) & 1)
+					pos += sprintf(buf + pos, "packet\n");
+				else
+					pos += sprintf(buf + pos, "stream\n");
+
+				pos +=
+				    sprintf(buf + pos, "data rate:          ");
+				if (get_line_data_rate_value
+				    (get_common_reg(ioaddr, 0x42)) < 0)
+					pos +=
+					    sprintf(buf + pos,
+						    "Invalid value\n");
+				else if (get_line_data_rate_value
+					 (get_common_reg(ioaddr, 0x42))) {
+					pos +=
+					    sprintf(buf + pos, "%d ",
+						    get_line_data_rate_value
+						    (get_common_reg
+						     (ioaddr,
+						      0x42)) *
+						    get_common_reg_word
+						    (ioaddr, 0x50 + 2 * i));
+					if (get_line_data_rate_unit
+					    (get_common_reg(ioaddr, 0x42))) {
+						char unit =
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x42));
+						pos +=
+						    sprintf(buf + pos,
+							    "%c", unit);
+					}
+					pos += sprintf(buf + pos, "bps\n");
+				} else
+					pos +=
+					    sprintf(buf + pos,
+						    "No multiplier, get value "
+						    "or list using mailbox\n");
+			}
+		}
+		up_read(&cardp->semaphore);
+		*start = (char *)1;
+		*eof = 1;
+		return min(pos, len);
+	default:
+EOF:
+		up_read(&cardp->semaphore);
+		*start = (char *)0;
+		*eof = 1;
+		return 0;
+	}
+}
+
+void proc_cpy(char *buf_from, char *buf_to, int len)
+{
+	int i;
+	for (i = 0; i < len; i++)
+		*(buf_to + i) = *(buf_from + i);
+}
+
+static int fepci_proc_read_status(char *buf, char **start, off_t offset,
+				  int len, int *eof, void *data)
+{
+	unsigned int pos = 0;
+	unsigned int i, j;
+
+	struct fepci_card_private *cardp;
+
+	void *ioaddr;
+
+	struct fepci_ch_private *fp;
+
+	cardp = (struct fepci_card_private *)data;
+
+	down_read(&cardp->semaphore);
+
+	if (unlikely(cardp->pci_dev == NULL))
+		goto EOF;
+
+	ioaddr = cardp->ioaddr;
+
+	if ((offset & 0xff000) == 0) {
+		j = get_common_reg(ioaddr, 0x02);
+		for (i = 0; i < j; i++) {
+			pos += sprintf(buf + pos, "\nline %2d\n", i);
+			pos += sprintf(buf + pos, "-------\n");
+			pos += sprintf(buf + pos, "status:             ");
+			if ((get_common_reg_word(ioaddr, 0x72) >> i) & 1) {
+				pos += sprintf(buf + pos, "DOWN\n");
+				netif_carrier_off(cardp->ch_privates[i]->
+						  this_dev);
+			} else {
+				netif_carrier_on(cardp->ch_privates[i]->
+						 this_dev);
+				if ((get_common_reg_word(ioaddr, 0x74) >>
+				     i) & 1)
+					pos += sprintf(buf + pos, "UP/Alarm\n");
+				else
+					pos += sprintf(buf + pos, "UP\n");
+			}
+			pos += sprintf(buf + pos, "primary clock:      ");
+			if ((get_common_reg_word(ioaddr, 0x76) >> i) & 1)
+				pos += sprintf(buf + pos, "OK\n");
+			else
+				pos += sprintf(buf + pos, "FAIL\n");
+		}
+		up_read(&cardp->semaphore);
+		if (pos - (0xfff & offset) > (unsigned)len)
+			*start = (char *)len;
+		else
+			*start = (char *)(0x1000 - (offset & 0xfff));
+		proc_cpy(buf + (offset & 0xfff), buf, len);
+		return min((int)(pos - (0xfff & offset)), len);
+	}
+
+	j = get_common_reg(ioaddr, 0x03);
+	if ((j <= 0) || (j > 16))
+		j = 16;
+	if ((((((unsigned)offset) & 0xff000) >> 12) > 0) &&
+	    (((((unsigned)offset) & 0xff000) >> 12) <= j)) {
+		i = ((int)((offset & 0xff000) >> 12)) - 1;
+		if (i < CHANNELS) {
+			pos += sprintf(buf + pos, "\npipe %2d\n", i);
+			pos += sprintf(buf + pos, "-------\n");
+			pos += sprintf(buf + pos, "status:             ");
+
+			if (!((get_common_reg_word(ioaddr, 0x78) >> i) & 1))
+				pos += sprintf(buf + pos, "DOWN\n");
+			else if ((get_common_reg_word(ioaddr, 0x7a) >> i) & 1)
+				pos += sprintf(buf + pos, "UP/Degraded\n");
+			else
+				pos += sprintf(buf + pos, "UP\n");
+			if ((get_common_reg_word(ioaddr, 0x4e) >> i) & 1) {
+				pos +=
+				    sprintf(buf + pos, "packet counters:\n\n");
+				pos +=
+				    sprintf(buf + pos, "rx count: %lu \n",
+					    cardp->ch_privates[i]->stats.
+					    rx_packets);
+				pos +=
+				    sprintf(buf + pos, "rx data:  %lu \n",
+					    cardp->ch_privates[i]->stats.
+					    rx_bytes);
+				pos +=
+				    sprintf(buf + pos, "rx errors:%lu \n",
+					    cardp->ch_privates[i]->stats.
+					    rx_errors);
+				pos +=
+				    sprintf(buf + pos, "tx count: %lu \n",
+					    cardp->ch_privates[i]->stats.
+					    tx_packets);
+				pos +=
+				    sprintf(buf + pos, "tx data:  %lu \n",
+					    cardp->ch_privates[i]->stats.
+					    tx_bytes);
+				pos +=
+				    sprintf(buf + pos, "tx errors:%lu \n",
+					    cardp->ch_privates[i]->stats.
+					    tx_errors);
+			} else {
+				pos +=
+				    sprintf(buf + pos, "stream counters:\n\n");
+				fp = cardp->ch_privates[i];
+				pos += sprintf(buf + pos,
+					       "rx_desc_fifo_err_stream : %10u"
+					       "  \n",
+					       fp->rx_desc_fifo_err_stream);
+				pos +=
+				    sprintf(buf + pos,
+					    "rx_desc_size_err_stream : %10u"
+					    "  \n",
+					    fp->rx_desc_size_err_stream);
+				pos +=
+				    sprintf(buf + pos,
+					    "rx_desc_octet_err_stream : %9u"
+					    "  \n",
+					    fp->rx_desc_octet_err_stream);
+				pos +=
+				    sprintf(buf + pos,
+					    "rx_desc_line_err_stream : %10u"
+					    "  \n",
+					    fp->rx_desc_line_err_stream);
+				pos +=
+				    sprintf(buf + pos,
+					    "tx_desc_fifo_err_stream : %10u"
+					    "  \n",
+					    fp->tx_desc_fifo_err_stream);
+				pos +=
+				    sprintf(buf + pos,
+					    "rx_int_fifo_err_stream : %11u  \n",
+					    fp->rx_int_fifo_err_stream);
+				pos +=
+				    sprintf(buf + pos,
+					    "rx_int_frame_dropped_err_stream :"
+					    " %2u  \n",
+					    fp->
+					    rx_int_frame_dropped_err_stream);
+				pos +=
+				    sprintf(buf + pos,
+					    "tx_int_fifo_err_stream : %11u  \n",
+					    fp->tx_int_fifo_err_stream);
+			}
+			if (pos - (0xfff & offset) > (unsigned)len)
+				*start = (char *)len;
+			else
+				*start = (char *)(0x1000 - (offset & 0xfff));
+			proc_cpy(buf + (offset & 0xfff), buf, len);
+		}
+		up_read(&cardp->semaphore);
+		return min((int)(pos - (0xfff & offset)), len);
+	} else {
+EOF:
+		up_read(&cardp->semaphore);
+		*start = (char *)0;
+		*eof = 1;
+		return 0;
+	}
+}
+
+static int fepci_proc_read_devices(char *buf, char **start, off_t offset,
+				   int len, int *eof, void *data)
+{
+	void *ioaddr;
+	unsigned int pos = 0;
+	unsigned int i;
+
+	pos +=
+	    sprintf(buf + pos,
+		    "bus:card.function  cardtype model    lines, max rate\n");
+	for (i = 0; i < find_cnt; i++) {
+		down_read(&card_privates[i].semaphore);
+		if (likely(card_privates[i].pci_dev)) {	/* the card exists */
+			pos +=
+			    sprintf(buf + pos,
+				    "%02x:%s            ",
+				    card_privates[i].pci_dev->bus->
+				    number,
+				    card_privates[i].pci_dev->procent->name);
+			ioaddr = card_privates[i].ioaddr;
+			pos =
+			    print_card_type(get_common_reg
+					    (ioaddr, 0x00), buf, pos);
+			pos =
+			    print_card_model(get_common_reg
+					     (ioaddr, 0x00), buf, pos);
+			pos +=
+			    sprintf(buf + pos, "%d",
+				    get_common_reg(ioaddr, 0x02));
+			pos += sprintf(buf + pos, " x ");
+			pos =
+			    print_line_type(get_common_reg
+					    (ioaddr, 0x0c), buf, pos);
+			if (get_line_data_rate_value
+			    (get_common_reg(ioaddr, 0x26)) < 0)
+				pos += sprintf(buf + pos, "Invalid value\n");
+			else if (get_line_data_rate_value
+				 (get_common_reg(ioaddr, 0x26))) {
+				pos += sprintf(buf + pos, ", total ");
+				pos +=
+				    sprintf(buf + pos, "%d ",
+					    get_line_data_rate_value
+					    (get_common_reg
+					     (ioaddr,
+					      0x26)) *
+					    get_common_reg_word
+					    (ioaddr,
+					     0x2a) *
+					    get_common_reg(ioaddr, 0x02));
+				if (get_line_data_rate_unit
+				    (get_common_reg(ioaddr, 0x26)))
+					pos +=
+					    sprintf(buf + pos,
+						    "%c",
+						    get_line_data_rate_unit
+						    (get_common_reg
+						     (ioaddr, 0x26)));
+				pos += sprintf(buf + pos, "bps\n");
+			} else
+				pos +=
+				    sprintf(buf + pos,
+					    "No multiplier, get value or list "
+					    "using mailbox\n");
+
+		}
+		up_read(&card_privates[i].semaphore);
+	}
+	DBG_PRINT("fepci_proc_read_devices pos %u < %lu?\n", pos, PAGE_SIZE);
+	*eof = 1;
+	return pos;
+}
+
+static int fepci_proc_read_id_list(char *buf, char **start, off_t offset,
+				   int len, int *eof, void *data)
+{
+	unsigned int pos = 0;
+	unsigned int i;
+
+	pos += sprintf(buf + pos, "bus:card.function major minor interfaces\n");
+	for (i = 0; i < find_cnt; i++) {
+		down_read(&card_privates[i].semaphore);
+		if (likely(card_privates[i].pci_dev))	/* the card exists */
+			pos +=
+			    sprintf(buf + pos,
+				    "%02x:%s           %03i   %03i   %s..%s\n",
+				    card_privates[i].pci_dev->bus->
+				    number,
+				    card_privates[i].pci_dev->
+				    procent->name, major,
+				    card_privates[i].
+				    ch_privates[0]->minor,
+				    card_privates[i].
+				    ch_privates[0]->this_dev->name,
+				    card_privates[i].
+				    ch_privates[CHANNELS - 1]->this_dev->name);
+		up_read(&card_privates[i].semaphore);
+	}
+
+	pos += sprintf(buf + pos, "\n");
+	DBG_PRINT("fepci_proc_read_id_list pos %u < %lu?\n", pos, PAGE_SIZE);
+	*eof = 1;
+	return pos;
+}
+
+const static char DEVICES[] = "devices";
+const static char ID_LIST[] = "id_list";
+
+static int fepci_proc_init_driver(void)
+{
+	DBG_PRINT("fepci_proc_init_driver\n");
+	proc_root_entry = create_proc_entry(fepci_proc_entry_name, S_IFDIR, 0);
+	if (!proc_root_entry)
+		printk(KERN_WARNING
+		       "%s: creating proc root dir entry failed.\n",
+		       fepci_NAME);
+
+	create_proc_read_entry(DEVICES, 0, proc_root_entry,
+			       fepci_proc_read_devices, NULL);
+	create_proc_read_entry(ID_LIST, 0, proc_root_entry,
+			       fepci_proc_read_id_list, NULL);
+
+	return 0;
+}
+
+static void fepci_proc_cleanup_driver(void)
+{
+	char name[sizeof(fepci_proc_entry_name) + sizeof(DEVICES)];
+	strcpy(name, fepci_proc_entry_name);
+	name[sizeof(fepci_proc_entry_name) - 1] = '/';
+	strcpy(name + sizeof(fepci_proc_entry_name), DEVICES);
+	remove_proc_entry(name, 0);
+	strcpy(name + sizeof(fepci_proc_entry_name), ID_LIST);
+	remove_proc_entry(name, 0);
+	remove_proc_entry(fepci_proc_entry_name, 0);
+}
+
+static void fepci_proc_init_card(int card_number, void *card_data)
+{
+	char buf[50];
+	struct proc_dir_entry *ent;
+	sprintf(buf, "%02x:%02x.%02x",
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+	ent = create_proc_entry(buf, S_IFDIR, proc_root_entry);
+	if (!ent)
+		printk("%s: creating proc dir entry failed.\n", fepci_NAME);
+
+	sprintf(buf, fepci_features_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+	create_proc_read_entry(buf, 0, NULL, fepci_proc_read_features,
+			       card_data);
+
+	sprintf(buf, fepci_settings_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+	create_proc_read_entry(buf, 0, NULL, fepci_proc_read_settings,
+			       card_data);
+
+	sprintf(buf, fepci_status_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+	create_proc_read_entry(buf, 0, NULL, fepci_proc_read_status, card_data);
+}
+
+static void fepci_proc_cleanup_card(int card_number)
+{
+	char buf[50];
+
+	sprintf(buf, fepci_features_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+	remove_proc_entry(buf, 0);
+	sprintf(buf, fepci_settings_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+	remove_proc_entry(buf, 0);
+	sprintf(buf, fepci_status_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+	remove_proc_entry(buf, 0);
+
+	sprintf(buf, "%02x:%02x.%02x",
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn));
+
+	remove_proc_entry(buf, proc_root_entry);
+}
+
+#ifdef DEBUG_PROC_FILES
+static void fepci_proc_init_channel(int card_number, int channel_number,
+				    void *channel_data)
+{
+	DBG_PRINT("fepci_proc_init_channel\n");
+	char buf[50];
+
+	sprintf(buf, fepci_counters_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+		channel_number);
+
+	create_proc_read_entry(buf, 0, NULL, fepci_proc_read_counters,
+			       channel_data);
+
+	sprintf(buf, fepci_descriptors_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+		channel_number);
+
+	create_proc_read_entry(buf, 0, NULL, fepci_proc_read_descriptors,
+			       channel_data);
+
+	sprintf(buf, fepci_stream_counters_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+		channel_number);
+
+	create_proc_read_entry(buf, 0, NULL,
+			       fepci_proc_read_stream_counters, channel_data);
+
+	sprintf(buf, fepci_registers_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+		channel_number);
+	create_proc_read_entry(buf, 0, NULL, fepci_proc_read_registers,
+			       channel_data);
+}
+
+static void fepci_proc_cleanup_channel(int card_number, int channel_number)
+{
+	char buf[50];
+	sprintf(buf, fepci_counters_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+		channel_number);
+	remove_proc_entry(buf, 0);
+
+	sprintf(buf, fepci_descriptors_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+		channel_number);
+	remove_proc_entry(buf, 0);
+
+	sprintf(buf, fepci_stream_counters_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+		channel_number);
+	remove_proc_entry(buf, 0);
+
+	sprintf(buf, fepci_registers_proc_entry_name,
+		card_privates[card_number].pci_dev->bus->number,
+		PCI_SLOT(card_privates[card_number].pci_dev->devfn),
+		PCI_FUNC(card_privates[card_number].pci_dev->devfn),
+		channel_number);
+	remove_proc_entry(buf, 0);
+}
+#endif /* DEBUG_PROC_FILES */
diff -Napur linux-2.6.23/drivers/net/wan/retina.h linux-2.6.24/drivers/net/wan/retina.h
--- linux-2.6.23/drivers/net/wan/retina.h	1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.24/drivers/net/wan/retina.h	2007-10-23 12:31:44.539117387 +0300
@@ -0,0 +1,164 @@
+/* V1.0.0 */
+
+/*
+	Copyright (C) 2002-2003 Jouni Kujala, Flexibilis Oy.
+
+	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.
+
+	All the drivers derived from or based on this code fall under the
+	GPL and must retain the copyright and license notice.
+*/
+
+#ifndef RETINA_H
+#define RETINA_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* net device related stuff: */
+#define FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE 0x89F1
+#define FEPCI_NETDEV_IOCTL_STREAM_UNITSIZE 0x89F2
+#define FEPCI_NETDEV_IOCTL_STREAM_OPEN 0x89F3
+#define FEPCI_NETDEV_IOCTL_STREAM_START 0x89F4
+#define FEPCI_NETDEV_IOCTL_STREAM_CLOSE 0x89F6
+
+/* char device related stuff: */
+
+#define FEPCI_SHARED_MEM_OFFSETT 0x8000
+#define FEPCI_IDENTIFICATION_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x0)
+#define FEPCI_FEATURES_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x40)
+#define FEPCI_SETTINGS_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x80)
+#define FEPCI_STATUS_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0xE0)
+#define FEPCI_MAILBOX_OFFSETT (FEPCI_SHARED_MEM_OFFSETT+0x100)
+
+/* structures for ioctl calls: */
+struct fepci_ioctl_identification {
+	unsigned char data[0x20];
+};
+struct fepci_real_identification {
+	unsigned long int data[0x10];
+};
+
+struct fepci_ioctl_features {
+	unsigned char data[0x20];
+};
+struct fepci_real_features {
+	unsigned long int data[0x10];
+};
+
+struct fepci_ioctl_settings {
+	unsigned char data[0x30];
+};
+struct fepci_real_settings {
+	unsigned long int data[0x15];
+};
+
+struct fepci_ioctl_status {
+	unsigned char data[0x10];
+};
+struct fepci_real_status {
+	unsigned long int data[0x5];
+};
+
+struct fepci_ioctl_shared_mem {
+	unsigned long int data[0x80];
+};
+
+#define FEPCI_IOCTL_MAGIC 0xAA
+
+#define FEPCI_IOCTL_R_SHARED_MEM _IOR(FEPCI_IOCTL_MAGIC, 1, \
+struct fepci_ioctl_shared_mem)
+#define FEPCI_IOCTL_W_SHARED_MEM _IOW(FEPCI_IOCTL_MAGIC, 2, \
+struct fepci_ioctl_shared_mem)
+
+#define FEPCI_IOCTL_G_IDENTIFICATION _IOR(FEPCI_IOCTL_MAGIC, 0x81, \
+struct fepci_ioctl_identification)
+#define FEPCI_IOCTL_G_FEATURES _IOR(FEPCI_IOCTL_MAGIC, 0x82, \
+struct fepci_ioctl_features)
+#define FEPCI_IOCTL_G_SETTINGS _IOR(FEPCI_IOCTL_MAGIC, 0x83, \
+struct fepci_ioctl_settings)
+#define FEPCI_IOCTL_G_STATUS _IOR(FEPCI_IOCTL_MAGIC, 0x84, \
+struct fepci_ioctl_status)
+
+/* mailbox: */
+
+struct fepci_ioctl_mailbox {
+	unsigned char Semafore;
+	unsigned char Mail_number;
+	unsigned char Size;
+	unsigned char Command;
+	unsigned char Data[112];
+};
+
+struct fepci_real_mailbox {
+	__u32 Semafore_Mail_number;
+	__u32 Size_Command;
+	__u32 Data[112 / 2];
+};
+
+#define FEPCI_IOCTL_B_POLL _IO(FEPCI_IOCTL_MAGIC, 0x85)
+#define FEPCI_IOCTL_B_GRAB _IO(FEPCI_IOCTL_MAGIC, 0x86)
+#define FEPCI_IOCTL_B_RELEASE _IO(FEPCI_IOCTL_MAGIC, 0x87)
+#define FEPCI_IOCTL_B_S_CMAIL _IOW(FEPCI_IOCTL_MAGIC, 0x88, \
+struct fepci_ioctl_mailbox)
+#define FEPCI_IOCTL_B_S_QMAIL _IOW(FEPCI_IOCTL_MAGIC, 0x89, \
+struct fepci_ioctl_mailbox)
+#define FEPCI_IOCTL_B_G_MAIL _IOR(FEPCI_IOCTL_MAGIC, 0x90, \
+struct fepci_ioctl_mailbox)
+
+#define FEPCI_IOCTL_ALARM_MANAGER _IO(FEPCI_IOCTL_MAGIC, 0x91)
+#define FEPCI_IOCTL_STREAM_TRANSMIT_POLL _IO(FEPCI_IOCTL_MAGIC, 0x92)
+#define FEPCI_IOCTL_STREAM_RECEIVE_POLL _IO(FEPCI_IOCTL_MAGIC, 0x93)
+#define FEPCI_IOCTL_STREAM_BOTH_POLL _IO(FEPCI_IOCTL_MAGIC, 0x94)
+
+/* stream related stuff: */
+
+/* stream buffer address space:
+ * address: 0x 7 6 5 4   3 2 1 0
+ *              ^ ^ ^
+ *              | | |
+ *           card | area(rx/tx,0==rx,1==tx)
+ *             channel     */
+
+#define CARD_ADDRESS_SHIFT 24u
+#define CHANNEL_ADDRESS_SHIFT 20u
+#define AREA_ADDRESS_SHIFT 16u
+
+#define STREAM_BUFFER_POINTER_AREA 0x7fff0000	/* one page reserved */
+
+/* stream buffer pointers (at pointer area):
+ * address: 0x 7 6 5 4   3 2 1 0
+ *                        ^ ^ ^
+ *                        | | |
+ *                     card | area(rx/tx,0==rx,4==tx)
+ *                       channel     */
+
+#define CARD_POINTER_SHIFT 8u
+#define CHANNEL_POINTER_SHIFT 4u
+#define AREA_POINTER_SHIFT 2u
+
+/* fake pointers are for faking larger unit sizes to the user than
+ * what is the maximum internal unit size in FEPCI */
+#define USER_RX_S_FAKE_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x0) + __offset))
+#define USER_TX_S_FAKE_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x4) + __offset))
+
+#define USER_RX_S_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0x8) + __offset))
+#define USER_TX_S_POINTER(__card, __channel, __offset) \
+((u32 *)(((__card << CARD_POINTER_SHIFT) | \
+(__channel << CHANNEL_POINTER_SHIFT) | 0xC) + __offset))
+
+#endif
diff -Napur linux-2.6.23/MAINTAINERS linux-2.6.24/MAINTAINERS
--- linux-2.6.23/MAINTAINERS	2007-10-09 23:31:38.000000000 +0300
+++ linux-2.6.24/MAINTAINERS	2007-10-23 12:27:47.056221363 +0300
@@ -3149,6 +3149,12 @@ L:	[email-masked]
 W:	http://www.namesys.com
 S:	Supported
 
+RETINA DRIVER
+P:	Matti Linnanvuori
+M:	[email-masked]
+L:	[email-masked]
+S:	Supported
+
 ROCKETPORT DRIVER
 P:	Comtrol Corp.
 W:	http://www.comtrol.com

      Heute schon einen Blick in die Zukunft von E-Mails wagen? Versuchen Sie´s mit dem neuen Yahoo! Mail. www.yahoo.de/mail