Netdev List
 help / color / mirror / Atom feed
* [PATCH] napi: use non-interruptible sleep in napi_disable
From: Benjamin Herrenschmidt @ 2007-10-25  3:59 UTC (permalink / raw)
  To: netdev; +Cc: linux-kernel

The current napi_disable() uses msleep_interruptible() but doesn't
(and can't) exit in case there's a signal, thus ending up doing a
hot spin without a cpu_relax. Use uninterruptible sleep instead.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

 include/linux/netdevice.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-work/include/linux/netdevice.h
===================================================================
--- linux-work.orig/include/linux/netdevice.h	2007-10-17 12:39:16.000000000 +1000
+++ linux-work/include/linux/netdevice.h	2007-10-17 12:45:00.000000000 +1000
@@ -390,7 +390,7 @@ static inline void napi_complete(struct 
 static inline void napi_disable(struct napi_struct *n)
 {
 	while (test_and_set_bit(NAPI_STATE_SCHED, &n->state))
-		msleep_interruptible(1);
+		msleep(1);
 }
 
 /**

^ permalink raw reply

* QUESTION: How to fix race between .suspend routine and watchdog timer
From: Maxim Levitsky @ 2007-10-25  4:12 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-pm, netdev

Hi,

Recently, trying to fix saa7134 suspend/resume problems I found that there 
is a race between IRQ handler and .suspend , and that I cant let driver access the device
while its in D3 since it can lock up some systems.

Now I am looking to fix those issues in two drivers that have my .suspend/.resume routines.
the saa7134 capture chip and dmfe, the davicom network driver.

Looking through the dmfe code, I noticed yet another possible race.
A race between the .suspend, and a timer that serves both as a watchdog, and link state detector.
Again I need to prevent it from running during the suspend/resume, but how?

I can use del_timer in .suspend, and mod_timer in .resume, but that doesn't protect against
race with already running timer.
I can use del_timer_sync, but it states that it is useless if timer re-enables itself, and I agree with that.
In dmfe case the timer does re-enable itself.

I can put checks in the timer for ->insuspend, and don't re enable it if set,
but that opens a new can of worms with memory barriers, etc...

So please tell me how properly to do that.

By the way, this problem, together with synchronize_irq it very generic, since most drivers
have and irq handler, and a timeout timer.

Best regards,
	Maxim Levitsky

^ permalink raw reply

* Re: [PATCH] napi: use non-interruptible sleep in napi_disable
From: Jeff Garzik @ 2007-10-25  5:10 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: netdev, linux-kernel, Andrew Morton
In-Reply-To: <20071025035920.C2D39DDECE@ozlabs.org>

Benjamin Herrenschmidt wrote:
> The current napi_disable() uses msleep_interruptible() but doesn't
> (and can't) exit in case there's a signal, thus ending up doing a
> hot spin without a cpu_relax. Use uninterruptible sleep instead.
> 
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
> 
>  include/linux/netdevice.h |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> Index: linux-work/include/linux/netdevice.h
> ===================================================================
> --- linux-work.orig/include/linux/netdevice.h	2007-10-17 12:39:16.000000000 +1000
> +++ linux-work/include/linux/netdevice.h	2007-10-17 12:45:00.000000000 +1000
> @@ -390,7 +390,7 @@ static inline void napi_complete(struct 
>  static inline void napi_disable(struct napi_struct *n)
>  {
>  	while (test_and_set_bit(NAPI_STATE_SCHED, &n->state))
> -		msleep_interruptible(1);
> +		msleep(1);
>  }

ACK



^ permalink raw reply

* [PATCH] wan: new driver retina
From: Matti Linnanvuori @ 2007-10-25  6:49 UTC (permalink / raw)
  To: akpm, jgarzik, info; +Cc: netdev

From: Matti Linnanvuori <mattilinnanvuori@yahoo.com>

Adding Retina G.703 and G.SHDSL driver.

Signed-off-by: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
---

Fixing bugs and style according to netdev mailing list 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-25 09:23:19.933170522 +0300
@@ -494,4 +494,15 @@ 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 or
+	  character device stream.
+
+	  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-25 09:43:59.029038615 +0300
@@ -0,0 +1,3709 @@
+/* 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.7"
+
+/* 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)
+
+static const char fepci_name[] = "retina";
+static const char fepci_alarm_manager_name[] = "retina alarm manager";
+static const char fepci_NAME[] = "RETINA";
+static const char fepci_netdev_name[] = "dcpxx";
+static 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"
+
+/* Time in jiffies before concluding that the transmitter is hung */
+#define TX_TIMEOUT  (5 * 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)
+
+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 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 stream mode: */
+	/* rx-errors in descriptors */
+	unsigned rx_desc_fifo_err_stream;
+	unsigned rx_desc_size_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;
+/* other: */
+	/* tx interrupts since last timer interrupt */
+	unsigned tx_interrupts_since_last_timer;
+};
+
+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): */
+static int major;			/* char device major number */
+static struct fepci_card_private card_privates[MAX_DEVICES];
+static unsigned long stream_pointers;
+static struct proc_dir_entry *proc_root_entry;
+
+static void set_int_mask(int channel, u_char value,
+			 struct fepci_card_private *cp)
+{
+	void *address;
+	unsigned shift, oldvalue;
+	pr_debug("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);
+}
+
+static void clear_int(unsigned channel, unsigned value, void *ioaddr)
+{
+	void *address;
+	unsigned shift, longvalue;
+	pr_debug("clear_int\n");
+	address = ioaddr + reg_first_int_status + (channel / 4) * 4;
+	shift = 8 * (channel % 4);
+	longvalue = value << shift;
+	writel(~longvalue, (void *)address);
+}
+
+static u_char get_int_status(int channel, void *ioaddr)
+{
+	void *address;
+	unsigned shift, oldvalue;
+	pr_debug("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);
+}
+
+static void fillregisterswith_00(void *ioaddr)
+{
+	pr_debug("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);
+
+/* char device operations: */
+
+static ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+			loff_t *f_pos);
+static int fepci_char_open(struct inode *inode, struct file *filp);
+static int fepci_char_release(struct inode *inode, struct file *filp);
+static int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma);
+static 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
+};
+
+static int fepci_char_open(struct inode *inode, struct file *filp)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	pr_debug("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;
+}
+
+static int fepci_char_release(struct inode *inode, struct file *filp)
+{
+	pr_debug("fepci_char_release\n");
+	module_put(THIS_MODULE);
+	return 0;
+}
+
+static void fepci_vma_open(struct vm_area_struct *vma)
+{
+	pr_debug("fepci_vma_open\n");
+}
+
+static void fepci_vma_close(struct vm_area_struct *vma)
+{
+	pr_debug("fepci_vma_close\n");
+	module_put(THIS_MODULE);
+}
+
+static struct vm_operations_struct fepci_vm_ops = {
+       .open	= fepci_vma_open,
+       .close	= fepci_vma_close
+};
+
+static 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:
+			pr_debug("%s: mmap: invalid address 0x%lx\n",
+				  fepci_NAME, virtual_address);
+			return -EINVAL;
+		}
+		if (unlikely(virtual_address == 0))
+			goto INVALID;
+	}
+
+	if (unlikely(!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 */
+
+static void fepci_copy_to_user(unsigned long to, void *from, unsigned long len,
+			int shrink)
+{
+	unsigned int i;
+	pr_debug("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));
+	}
+}
+
+static 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));
+	}
+}
+
+static unsigned get_semafore(struct fepci_real_mailbox *mailbox)
+{
+	unsigned semafore = readb(&mailbox->Semafore_Mail_number);
+	pr_debug("get_semafore = %x\n", semafore);
+	return semafore;
+}
+
+static void set_semafore(struct fepci_real_mailbox *mailbox, unsigned semafore)
+{
+	unsigned number = readl(&mailbox->Semafore_Mail_number);
+	pr_debug("got number %u at %p.\n", number,
+		  &mailbox->Semafore_Mail_number);
+	number = ((number & ~0xFF) | semafore) + (1 << 8);
+	pr_debug
+	    ("increases the mail number to %u at the same time at %p.\n",
+	     number, &mailbox->Semafore_Mail_number);
+	writel(number, &mailbox->Semafore_Mail_number);
+	pr_debug("set_semafore %p, %u returns 0.\n", mailbox, semafore);
+}
+
+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;
+}
+
+static 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;
+	}
+
+	pr_debug("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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug("%s: IOCTL_B_GRAB commanded.\n", fepci_NAME);
+		if ((my_pid != *saved_pid) && (*saved_pid != 0)) {
+			retval = 0x2;
+			break;
+		}
+		pr_debug("%s: IOCTL_B_GRAB getting semaphore.\n", fepci_NAME);
+		if (get_semafore(real_mailbox) == 0x0) {
+			pr_debug("%s: IOCTL_B_GRAB setting semaphore.\n",
+				  fepci_NAME);
+			set_semafore(real_mailbox, 0x40);
+			pr_debug("%s: IOCTL_B_GRAB sleeping.\n", fepci_NAME);
+			msleep(1);	/* delay at least 1 millisecond */
+			pr_debug
+			    ("%s: IOCTL_B_GRAB getting semaphore again.\n",
+			     fepci_NAME);
+			switch (get_semafore(real_mailbox)) {
+			case 0x40:
+				retval = 0x0;
+				pr_debug
+				    ("%s: IOCTL_B_GRAB saving pid to %p.\n",
+				     fepci_NAME, saved_pid);
+				*saved_pid = my_pid;
+				pr_debug
+				    ("%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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %s: IOCTL_ALARM_MANAGER commanded.\n", fepci_NAME);
+		interruptible_sleep_on(&(card_privates[minor].
+					 alarm_manager_wait_q));
+		return retval;
+	default:
+		pr_debug(" %s: Unknown ioctl command 0x%x.\n", fepci_NAME,
+			  cmd);
+		return -ENOTTY;
+	}
+	return retval;
+}
+
+static ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+			loff_t *f_pos)
+{
+	pr_debug("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
+		pr_debug("%s: registered char device, major:0x%x.\n",
+			  fepci_NAME, error);
+	return error;
+}
+
+static 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;
+
+	pr_debug("%s: Bufsize is 0x%x.\n", fepci_NAME, fp->bufsize);
+	pr_debug("%s: Unit_size is 0x%x.\n", fepci_NAME, fp->unit_sz);
+	pr_debug("%s: Number of units is 0x%x.\n", fepci_NAME, fp->units);
+
+	pr_debug("%s: Fake_unit_size is 0x%x.\n", fepci_NAME,
+		  fp->fake_unit_sz);
+	pr_debug("%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);
+	unsigned fifo, dropped;
+	unsigned int temp_rx;
+	unsigned int temp_rx_unit;
+	unsigned int temp_tx;
+	unsigned int temp_tx_unit;
+
+	if (unlikely(!intr_status))
+		return IRQ_NONE;
+	clear_int(fp->channel_number, intr_status, ioaddr);
+
+	/* debugging */
+	fifo = intr_status & IntrRxFifoError;
+	if (fifo)
+		fp->rx_int_fifo_err_stream++;
+	dropped = intr_status & IntrRxFrameDroppedError;
+	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 octet = (desc_b & octet_error) != 0;
+			bool line = (desc_b & line_error) != 0;
+			dma_addr_t bus_address;
+			/* update debug counters */
+			if (fifo)
+				fp->rx_desc_fifo_err_stream++;
+			else if (size)
+				fp->rx_desc_size_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 IRQ_HANDLED;
+}
+
+/* stream operations end */
+
+static int fepci_rebuild_header(struct sk_buff *skb)
+{
+	pr_debug("fepci_rebuild_header\n");
+	return 0;
+}
+
+static inline u16 get_common_reg_word(void *ioaddr, unsigned long offsett)
+{
+	u16 word;
+	pr_debug("get_common_reg_word ioaddr %p, offsett %lu\n", ioaddr,
+		  offsett);
+	__clear_bit(0, &offsett);
+	pr_debug("get_common_reg_word %p\n",
+		  ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1));
+	word = le16_to_cpu(readw
+			   (ioaddr + FEPCI_IDENTIFICATION_OFFSETT +
+			    (offsett << 1)));
+	pr_debug("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 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);
+	ioaddr = ioremap_nocache(real_ioaddr, FEPCI_SIZE);
+	pr_debug("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;
+	pr_debug("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);
+
+	pr_debug("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;
+	}
+	pr_debug("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++) {
+		pr_debug("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);
+		dev_dbg(&dev->dev, "clear_int %u, %x, %p.\n", j,
+			IntrAllInts, ioaddr);
+		clear_int(j, IntrAllInts, ioaddr);
+		dev_dbg(&dev->dev, "ether_setup.\n");
+		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) {
+				dev_dbg(&dev->dev,
+					"jiffies %lu < waituntil %lu.\n",
+					jiffies, waituntil);
+				schedule_timeout(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;
+		dev_dbg(&dev->dev, "alarm pci_set_drvdata %p.\n", pdev);
+		if (j == 0)
+			pci_set_drvdata(pdev, dev);
+
+		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
+		dev_dbg(&dev->dev, "register_netdev.\n");
+		i = register_netdev(dev);
+		if (i) {
+			printk(KERN_WARNING
+			       "%s: register_netdev failed 0x%x.\n",
+			       fepci_NAME, i);
+			continue;
+		}
+
+		for (i = 0; i < 5; i++)
+			dev_dbg(&dev->dev, "%2.2x:", dev->dev_addr[i]);
+		dev_dbg(&dev->dev, "%2.2x, IRQ %d.\n", dev->dev_addr[i],
+			pdev->irq);
+	}
+	up_write(&card_privates[position].semaphore);
+	pr_debug("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;
+
+	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);
+}
+
+/* Just to make it absolutely sure the sending starts again
+ * if the system jams: free already sent skbuffs. */
+
+static void fepci_tx_timeout(struct net_device *dev)
+{
+	struct fepci_ch_private *fp = dev->priv;
+
+	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)
+					continue;
+				desc_b = readl(&fp->tx_desc[i].desc_b);
+				if ((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_skbuff[i] = NULL;
+
+					if (desc_b & fifo_error)
+						fp->stats.tx_fifo_errors++;
+					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;
+}
+
+/* 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))) {
+				/* 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;
+
+	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);
+
+	if (unlikely(!intr_status))
+		return IRQ_NONE;
+	clear_int(fp->channel_number, intr_status, ioaddr);
+
+	if (intr_status &
+		(IntrFrameReceived | IntrRxFifoError | IntrRxFrameDroppedError))
+		fepci_rx(dev);
+	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)
+					continue;
+				desc_b = readl(&fp->tx_desc[i].desc_b);
+				if ((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_skbuff[i] = NULL;
+					if (desc_b & fifo_error)
+						fp->stats.tx_fifo_errors++;
+					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);
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+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);
+			if (fifo) {
+				fp->stats.rx_errors++;
+				fp->stats.rx_frame_errors++;
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			} else if (size) {
+				fp->stats.rx_errors++;
+				fp->stats.rx_over_errors++;
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			} else if (crc) {
+				fp->stats.rx_errors++;
+				fp->stats.rx_crc_errors++;
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			} else if (octet)
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			else if (line)
+				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);
+				/* 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_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_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_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)
+{
+	dev_dbg(&dev->dev, "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;
+
+	dev_dbg(&dev->dev, "netdev_ioctl called (command_nmbr:0x%x).\n",
+		cmd);
+
+	switch (cmd) {
+	case FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE:
+		dev_dbg
+		    (&dev->dev,
+		     " 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:
+		dev_dbg
+		    (&dev->dev,
+		     " 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:
+		dev_dbg(&dev->dev, " ioctl stream open commanded.\n");
+		ret = fepci_stream_open(dev);
+		break;
+	case FEPCI_NETDEV_IOCTL_STREAM_START:
+		dev_dbg(&dev->dev, " ioctl stream start commanded.\n");
+		ret = fepci_stream_start(dev);
+		break;
+	case FEPCI_NETDEV_IOCTL_STREAM_CLOSE:
+		dev_dbg(&dev->dev, " ioctl stream close commanded.\n");
+		ret = fepci_stream_close(dev);
+		break;
+	default:
+		dev_dbg(&dev->dev, " 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;
+		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);
+	pr_debug(" %lx.\n", stream_pointers);
+	if (stream_pointers == 0) {
+		fepci_unregister_char_device();
+		return -ENOMEM;
+	}
+	pr_debug("SetPageReserved %lu.\n", stream_pointers);
+	SetPageReserved(virt_to_page(stream_pointers));
+	pr_debug("fepci_proc_init_driver.\n");
+	fepci_proc_init_driver();
+	pr_debug("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);
+			pr_debug
+			    ("pci_register_driver %p failed with %d.\n",
+			     &fepci_driver, ret);
+			fepci_proc_cleanup_driver();
+			return ret;
+		}
+		pr_debug("fepci_init %d.\n", ret);
+		return ret;
+	}
+}
+
+static void __exit fepci_cleanup(void)
+{
+	pr_debug("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);
+
+static int get_line_data_rate_value(unsigned char line_rate)
+{
+	switch (line_rate) {
+	case 0x00:
+		return 0;
+	case 0x05:
+	case 0x28:
+		return 8;
+	case 0x14:
+		return 256;
+	case 0x15:
+		return 300;
+	case 0x29:
+	case 0x06:
+		return 10;
+	case 0x30:
+		return 32;
+	case 0x34:
+		return 56;
+	case 0x36:
+		return 64;
+	case 0x01:
+	case 0x60:
+	case 0xa0:
+	case 0x20:
+		return 1;
+	default:
+		return -1;
+	}
+}
+
+static char get_line_data_rate_unit(unsigned char line_rate)
+{
+	switch (line_rate) {
+	case 0x00:
+	case 0x01:
+	case 0x05:
+	case 0x06:
+	case 0x14:
+	case 0x15:
+		return 0;
+	case 0x20:
+	case 0x28:
+	case 0x29:
+	case 0x30:
+	case 0x34:
+	case 0x36:
+		return 'k';
+	case 0x60:
+		return 'M';
+	case 0xa0:
+		return 'G';
+	default:
+		return 0;
+	}
+}
+
+static int print_line_type(unsigned char type, char *buf, int pos)
+{
+	pr_debug("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;
+}
+
+static int print_card_type(unsigned char type, char *buf, int pos)
+{
+	pr_debug("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;
+}
+
+static int print_card_model(unsigned char model, char *buf, int pos)
+{
+	pr_debug("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;
+	pr_debug("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);
+	pr_debug("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 +=
+						    sprintf(buf + pos, "n/a\n");
+				}
+			}
+		}
+
+		/* 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;
+	}
+}
+
+static 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);
+	}
+	pr_debug("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");
+	pr_debug("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)
+{
+	pr_debug("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);
+}
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-25 09:22:15.579158599 +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:	reiserfs-devel@vger.kernel.org
 W:	http://www.namesys.com
 S:	Supported
 
+RETINA DRIVER
+P:	Matti Linnanvuori
+M:	mattilinnanvuori@yahoo.com
+L:	netdev@vger.kernel.org
+S:	Supported
+
 ROCKETPORT DRIVER
 P:	Comtrol Corp.
 W:	http://www.comtrol.com




      Machen Sie Yahoo! zu Ihrer Startseite. Los geht's: 
http://de.yahoo.com/set

^ permalink raw reply

* Re: [2.6 patch] make bonding/bond_main.c:bond_deinit() static
From: Jeff Garzik @ 2007-10-25  7:31 UTC (permalink / raw)
  To: Adrian Bunk
  Cc: Moni Shoua, Jay Vosburgh, ctindel, bonding-devel, linux-kernel,
	netdev
In-Reply-To: <20071024162317.GK30533@stusta.de>

applied both bonding patches


^ permalink raw reply

* Re: [2.6 patch] remove Documentation/networking/net-modules.txt
From: Jeff Garzik @ 2007-10-25  7:31 UTC (permalink / raw)
  To: Adrian Bunk; +Cc: netdev, linux-kernel
In-Reply-To: <20071024162503.GT30533@stusta.de>

Adrian Bunk wrote:
> According to git, the only one who touched this file during the last
> 5 years was me when removing drivers...
> 
> modinfo offers less ancient information.
> 
> Signed-off-by: Adrian Bunk <bunk@kernel.org>
> Acked-by: Geert Uytterhoeven <geert@linux-m68k.org>
> Acked-by: Paul Gortmaker <paul.gortmaker@gmail.com>
> 
> ---
> 
> This patch has been sent on:
> - 14 Aug 2007
> - 23 Jul 2007
> 

applied



^ permalink raw reply

* Re: [2.6 patch] drivers/net/ipg.c: cleanups
From: Jeff Garzik @ 2007-10-25  7:31 UTC (permalink / raw)
  To: Adrian Bunk
  Cc: Jesse Huang, Stefan Lippers-Hollmann, Francois Romieu, netdev,
	linux-kernel
In-Reply-To: <20071024162319.GL30533@stusta.de>

Adrian Bunk wrote:
> This patch contains the following cleanups:
> - make ipg_nic_get_stats() static
> - move DefaultPhyParam[] from ipg.h to ipg.c and make it static
> 

applied



^ permalink raw reply

* Re: [PATCH 1/3] cpmac: use print_mac() instead of MAC_FMT
From: Jeff Garzik @ 2007-10-25  7:32 UTC (permalink / raw)
  To: Eugene Konev; +Cc: Matteo Croce, netdev
In-Reply-To: <1193193723-12691-2-git-send-email-ejka@imfi.kspu.ru>

Eugene Konev wrote:
> Switch to using DECLARE_MAC_BUF/print_mac() added by commit
> 0795af5729b18218767fab27c44b1384f72dc9ad [NET]: Introduce and use print_mac()
> and DECLARE_MAC_BUF().
> 
> 
> Signed-off-by: Eugene Konev <ejka@imfi.kspu.ru>

applied 1-3



^ permalink raw reply

* Re: [PATCH 1/2] forcedeth new device ids
From: Jeff Garzik @ 2007-10-25  7:36 UTC (permalink / raw)
  To: Ayaz Abdulla; +Cc: Andrew Morton, Manfred Spraul, nedev, stable
In-Reply-To: <471E62E9.80307@nvidia.com>

applied 1-2, after combining into a single patch



^ permalink raw reply

* Re: [PATCH 1/3] netdev: use ARRAY_SIZE() instead of sizeof(array) / ETH_GSTRING_LEN
From: Jeff Garzik @ 2007-10-25  7:39 UTC (permalink / raw)
  To: Alejandro Martinez Ruiz; +Cc: netdev
In-Reply-To: <20071018094904.GB23058@inspiron>

applied 1-3 to #upstream (2.6.25)



^ permalink raw reply

* [git patches] net driver updates
From: Jeff Garzik @ 2007-10-25  7:49 UTC (permalink / raw)
  To: Andrew Morton, Linus Torvalds; +Cc: netdev, LKML


Please pull from 'upstream-linus' branch of
master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git upstream-linus

to receive the following updates:

 Documentation/networking/00-INDEX        |    2 -
 Documentation/networking/net-modules.txt |  315 ------------------------------
 drivers/net/bonding/bond_main.c          |    5 +-
 drivers/net/bonding/bonding.h            |    1 -
 drivers/net/cpmac.c                      |  145 ++++++++------
 drivers/net/ehea/ehea.h                  |    2 +-
 drivers/net/ehea/ehea_main.c             |    7 +-
 drivers/net/forcedeth.c                  |   16 ++
 drivers/net/ipg.c                        |   22 ++-
 drivers/net/ipg.h                        |   20 --
 drivers/net/natsemi.c                    |    1 +
 drivers/net/usb/rndis_host.c             |   18 ++-
 include/linux/pci_ids.h                  |    4 +
 13 files changed, 149 insertions(+), 409 deletions(-)
 delete mode 100644 Documentation/networking/net-modules.txt

Adrian Bunk (4):
      remove Documentation/networking/net-modules.txt
      drivers/net/ipg.c: cleanups
      make bonding/bond_main.c:bond_deinit() static
      bonding/bond_main.c: fix cut'n'paste error

Ayaz Abdulla (1):
      [netdrvr] forcedeth: add MCP77 device IDs

Eugene Konev (3):
      cpmac: use print_mac() instead of MAC_FMT
      cpmac: convert to napi_struct interface
      cpmac: update to new fixed phy driver interface

Ingo Molnar (1):
      natsemi: fix oops, link back netdevice from private-struct

Jan-Bernd Themann (1):
      ehea: fix port_napi_disable/enable

Thomas Sailer (1):
      rndis_host: reduce MTU instead of refusing to talk to devices with low max packet size

diff --git a/Documentation/networking/00-INDEX b/Documentation/networking/00-INDEX
index 153d84d..f5a5e6d 100644
--- a/Documentation/networking/00-INDEX
+++ b/Documentation/networking/00-INDEX
@@ -80,8 +80,6 @@ multicast.txt
 	- Behaviour of cards under Multicast
 ncsa-telnet
 	- notes on how NCSA telnet (DOS) breaks with MTU discovery enabled.
-net-modules.txt
-	- info and "insmod" parameters for all network driver modules.
 netdevices.txt
 	- info on network device driver functions exported to the kernel.
 olympic.txt
diff --git a/Documentation/networking/net-modules.txt b/Documentation/networking/net-modules.txt
deleted file mode 100644
index 98c4392..0000000
--- a/Documentation/networking/net-modules.txt
+++ /dev/null
@@ -1,315 +0,0 @@
-Wed 2-Aug-95  <matti.aarnio@utu.fi>
-
-		Linux network driver modules
-
-	Do not mistake this for "README.modules" at the top-level
-	directory!  That document tells about modules in general, while
-	this one tells only about network device driver modules.
-
-	This is a potpourri of INSMOD-time(*) configuration options
-	(if such exists) and their default values of various modules
-	in the Linux network drivers collection.
-
-	Some modules have also hidden (= non-documented) tunable values.
-	The choice of not documenting them is based on general belief, that
-	the less the user needs to know, the better.  (There are things that
-	driver developers can use, others should not confuse themselves.)
-
-	In many cases it is highly preferred that insmod:ing is done
-	ONLY with defining an explicit address for the card, AND BY
-	NOT USING AUTO-PROBING!
-
-	Now most cards have some explicitly defined base address that they
-	are compiled with (to avoid auto-probing, among other things).
-	If that compiled value does not match your actual configuration,
-	do use the "io=0xXXX" -parameter for the insmod, and give there
-	a value matching your environment.
-
-	If you are adventurous, you can ask the driver to autoprobe
-	by using the "io=0" parameter, however it is a potentially dangerous
-	thing to do in a live system.  (If you don't know where the
-	card is located, you can try autoprobing, and after possible
-	crash recovery, insmod with proper IO-address..)
-
-	--------------------------
-	(*)	"INSMOD-time" means when you load module with
-		/sbin/insmod  you can feed it optional parameters.
-		See "man insmod".
-	--------------------------
-
-
-	8390 based Network Modules		(Paul Gortmaker, Nov 12, 1995)
-	--------------------------
-
-(Includes: smc-ultra, ne, wd, 3c503, hp, hp-plus, e2100 and ac3200)
-
-The 8390 series of network drivers now support multiple card systems without 
-reloading the same module multiple times (memory efficient!) This is done by 
-specifying multiple comma separated values, such as:
-
-	insmod 3c503.o io=0x280,0x300,0x330,0x350  xcvr=0,1,0,1
-
-The above would have the one module controlling four 3c503 cards, with card 2
-and 4 using external transceivers. The "insmod" manual describes the usage
-of comma separated value lists.
-
-It is *STRONGLY RECOMMENDED* that you supply "io=" instead of autoprobing.
-If an "io=" argument is not supplied, then the ISA drivers will complain
-about autoprobing being not recommended, and begrudgingly autoprobe for
-a *SINGLE CARD ONLY* -- if you want to use multiple cards you *have* to 
-supply an "io=0xNNN,0xQQQ,..." argument.
-
-The ne module is an exception to the above. A NE2000 is essentially an
-8390 chip, some bus glue and some RAM. Because of this, the ne probe is
-more invasive than the rest, and so at boot we make sure the ne probe is 
-done last of all the 8390 cards (so that it won't trip over other 8390 based
-cards) With modules we can't ensure that all other non-ne 8390 cards have
-already been found. Because of this, the ne module REQUIRES an "io=0xNNN" 
-argument passed in via insmod. It will refuse to autoprobe.
-
-It is also worth noting that auto-IRQ probably isn't as reliable during 
-the flurry of interrupt activity on a running machine. Cards such as the 
-ne2000 that can't get the IRQ setting from an EEPROM or configuration
-register are probably best supplied with an "irq=M" argument as well.
-
-
-----------------------------------------------------------------------
-Card/Module List - Configurable Parameters and Default Values
-----------------------------------------------------------------------
-
-3c501.c:
-	io  = 0x280	IO base address
-	irq = 5		IRQ
-	(Probes ports:	0x280, 0x300)
-
-3c503.c:
-	io = 0		(It will complain if you don't supply an "io=0xNNN")
-	irq = 0		(IRQ software selected by driver using autoIRQ)
-	xcvr = 0	(Use xcvr=1 to select external transceiver.)
-	(Probes ports: 0x300, 0x310, 0x330, 0x350, 0x250, 0x280, 0x2A0, 0x2E0)
-
-3c505.c:
-	io = 0
-	irq = 0
-	dma = 6         (not autoprobed)
-	(Probes ports: 0x300, 0x280, 0x310)
-
-3c507.c:
-	io = 0x300
-	irq = 0
-	(Probes ports: 0x300, 0x320, 0x340, 0x280)
-
-3c509.c:
-	io = 0
-	irq = 0
-	( Module load-time probing Works reliably only on EISA, ISA ID-PROBE
-	  IS NOT RELIABLE!  Compile this driver statically into kernel for
-	  now, if you need it auto-probing on an ISA-bus machine. )
-
-8390.c:
-	(No public options, several other modules need this one)
-
-a2065.c:
-	Since this is a Zorro board, it supports full autoprobing, even for
-	multiple boards. (m68k/Amiga)
-
-ac3200.c:
-	io = 0		(Checks 0x1000 to 0x8fff in 0x1000 intervals)
-	irq = 0		(Read from config register)
-	(EISA probing..)
-
-apricot.c:
-	io = 0x300  (Can't be altered!)
-	irq = 10
-
-arcnet.c:
-	io = 0
-	irqnum = 0
-	shmem = 0
-	num = 0
-	DO SET THESE MANUALLY AT INSMOD!
-	(When probing, looks at the following possible addresses:
-	 Suggested ones:
-		0x300, 0x2E0, 0x2F0, 0x2D0
-	 Other ones:
-		0x200, 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x270,
-		0x280, 0x290, 0x2A0, 0x2B0, 0x2C0,
-		       0x310, 0x320, 0x330, 0x340, 0x350, 0x360, 0x370,
-		0x380, 0x390, 0x3A0,			  0x3E0, 0x3F0  )
-
-ariadne.c:
-	Since this is a Zorro board, it supports full autoprobing, even for
-	multiple boards. (m68k/Amiga)
-
-at1700.c:
-	io = 0x260
-	irq = 0
-	(Probes ports: 0x260, 0x280, 0x2A0, 0x240, 0x340, 0x320, 0x380, 0x300)
-
-atarilance.c:
-	Supports full autoprobing. (m68k/Atari)
-
-atp.c: *Not modularized*
-	(Probes ports: 0x378, 0x278, 0x3BC;
-	 fixed IRQs: 5 and 7			)
-
-cops.c:
-	io = 0x240
-	irq = 5
-	nodeid = 0	(AutoSelect = 0, NodeID 1-254 is hand selected.)
-	(Probes ports: 0x240, 0x340, 0x200, 0x210, 0x220, 0x230, 0x260,
-		       0x2A0, 0x300, 0x310, 0x320, 0x330, 0x350, 0x360)	
-
-de4x5.c:
-	io = 0x000b
-	irq = 10
-	is_not_dec = 0  -- For non-DEC card using DEC 21040/21041/21140 chip, set this to 1
-	(EISA, and PCI probing)
-
-de600.c:
-	de600_debug = 0
-	(On port 0x378, irq 7 -- lpt1;  compile time configurable)
-
-de620.c:
-	bnc = 0, utp = 0  <-- Force media by setting either.
-	io = 0x378	(also compile-time configurable)
-	irq = 7
-
-depca.c:
-	io = 0x200
-	irq = 7
-	(Probes ports:	ISA:  0x300, 0x200;
-			EISA: 0x0c00		)
-
-dummy.c:
-	No options
-
-e2100.c:
-	io = 0		(It will complain if you don't supply an "io=0xNNN")
-	irq = 0		(IRQ software selected by driver)
-	mem = 0		(Override default shared memory start of 0xd0000)
-	xcvr = 0	(Use xcvr=1 to select external transceiver.)
-	(Probes ports: 0x300, 0x280, 0x380, 0x220)
-
-eepro.c:
-	io = 0x200
-	irq = 0
-	(Probes ports: 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0x360)
-
-eexpress.c:
-	io = 0x300
-	irq = 0		(IRQ value read from EEPROM)
-	(Probes ports: 0x300, 0x270, 0x320, 0x340)
-
-eql.c:
-	(No parameters)
-
-ewrk3.c:
-	io = 0x300
-	irq = 5
-	(With module no autoprobing!
-	 On EISA-bus does EISA probing.
-	 Static linkage probes ports on ISA bus:
-		0x100, 0x120, 0x140, 0x160, 0x180, 0x1A0, 0x1C0,
-		0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0,
-		0x300,        0x340, 0x360, 0x380, 0x3A0, 0x3C0)
-
-hp-plus.c:
-	io = 0		(It will complain if you don't supply an "io=0xNNN")
-	irq = 0		(IRQ read from configuration register)
-	(Probes ports: 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340)
-
-hp.c:
-	io = 0		(It will complain if you don't supply an "io=0xNNN")
-	irq = 0		(IRQ software selected by driver using autoIRQ)
-	(Probes ports: 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240)
-
-hp100.c:
-	hp100_port = 0 (IO-base address)
-	(Does EISA-probing, if on EISA-slot;
-	 On ISA-bus probes all ports from 0x100 thru to 0x3E0
-	 in increments of 0x020)
-
-hydra.c:
-	Since this is a Zorro board, it supports full autoprobing, even for
-	multiple boards. (m68k/Amiga)
-
-ibmtr.c:
-	io = 0xa20, 0xa24 (autoprobed by default)
-	irq = 0 (driver cannot select irq - read from hardware)
-	mem = 0 (shared memory base set at 0xd0000 and not yet 
-	         able to override thru mem= parameter.)
-
-lance.c: *Not modularized*
-	(PCI, and ISA probing; "CONFIG_PCI" needed for PCI support)
-	(Probes ISA ports: 0x300, 0x320, 0x340, 0x360)
-
-loopback.c: *Static kernel component*
-
-ne.c:
-	io = 0		(Explicitly *requires* an "io=0xNNN" value)
-	irq = 0		(Tries to determine configured IRQ via autoIRQ)
-	(Probes ports: 0x300, 0x280, 0x320, 0x340, 0x360)
-
-net_init.c: *Static kernel component*
-
-ni52.c: *Not modularized*
-	(Probes ports:	0x300, 0x280, 0x360, 0x320, 0x340
-		mems:	0xD0000, 0xD2000, 0xC8000, 0xCA000,
-			0xD4000, 0xD6000, 0xD8000 )
-
-ni65.c: *Not modularized*  **16MB MEMORY BARRIER BUG**
-	(Probes ports:	0x300, 0x320, 0x340, 0x360)
-
-pi2.c:	*Not modularized* (well, NON-STANDARD modularization!)
-	Only one card supported at this time.
-	(Probes ports: 0x380, 0x300, 0x320, 0x340, 0x360, 0x3A0)
-
-plip.c:
-	io = 0
-	irq = 0		(by default, uses IRQ 5 for port at 0x3bc, IRQ 7
-			for port at 0x378, and IRQ 2 for port at 0x278)
-	(Probes ports: 0x278, 0x378, 0x3bc)
-
-ppp.c:
-	No options (ppp-2.2+ has some, this is based on non-dynamic
-	version from ppp-2.1.2d)
-
-seeq8005.c: *Not modularized*
-	(Probes ports: 0x300, 0x320, 0x340, 0x360)
-
-skeleton.c: *Skeleton*
-
-slhc.c:
-	No configuration parameters
-
-slip.c:
-	slip_maxdev = 256 (default value from SL_NRUNIT on slip.h)
-
-
-smc-ultra.c:
-	io = 0		(It will complain if you don't supply an "io=0xNNN")
-	irq = 0		(IRQ val. read from EEPROM)
-	(Probes ports:	0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380)
-
-tulip.c: *Partial modularization*
-	(init-time memory allocation makes problems..)
-
-tunnel.c:
-	No insmod parameters
-
-wavelan.c:
-	io = 0x390	(Settable, but change not recommended)
-	irq = 0		(Not honoured, if changed..)
-
-wd.c:
-	io = 0		(It will complain if you don't supply an "io=0xNNN")
-	irq = 0		(IRQ val. read from EEPROM, ancient cards use autoIRQ)
-	mem = 0		(Force shared-memory on address 0xC8000, or whatever..)
-	mem_end = 0	(Force non-std. mem. size via supplying mem_end val.)
-			(eg. for 32k WD8003EBT, use mem=0xd0000 mem_end=0xd8000)
-	(Probes ports:	0x300, 0x280, 0x380, 0x240)
-
-znet.c: *Not modularized*
-	(Only one device on  Zenith Z-Note (notebook?) systems,
-	 configuration information from (EE)PROM)
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 6909bec..6937ef0 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -188,6 +188,7 @@ struct bond_parm_tbl arp_validate_tbl[] = {
 /*-------------------------- Forward declarations ---------------------------*/
 
 static void bond_send_gratuitous_arp(struct bonding *bond);
+static void bond_deinit(struct net_device *bond_dev);
 
 /*---------------------------- General routines -----------------------------*/
 
@@ -3681,7 +3682,7 @@ static int bond_open(struct net_device *bond_dev)
 	}
 
 	if (bond->params.mode == BOND_MODE_8023AD) {
-		INIT_DELAYED_WORK(&bond->ad_work, bond_alb_monitor);
+		INIT_DELAYED_WORK(&bond->ad_work, bond_3ad_state_machine_handler);
 		queue_delayed_work(bond->wq, &bond->ad_work, 0);
 		/* register to receive LACPDUs */
 		bond_register_lacpdu(bond);
@@ -4449,7 +4450,7 @@ static int bond_init(struct net_device *bond_dev, struct bond_params *params)
 /* De-initialize device specific data.
  * Caller must hold rtnl_lock.
  */
-void bond_deinit(struct net_device *bond_dev)
+static void bond_deinit(struct net_device *bond_dev)
 {
 	struct bonding *bond = bond_dev->priv;
 
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index d1ed14b..61c1b45 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -302,7 +302,6 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_de
 int bond_create(char *name, struct bond_params *params, struct bonding **newbond);
 void bond_destroy(struct bonding *bond);
 int  bond_release_and_destroy(struct net_device *bond_dev, struct net_device *slave_dev);
-void bond_deinit(struct net_device *bond_dev);
 int bond_create_sysfs(void);
 void bond_destroy_sysfs(void);
 void bond_destroy_sysfs_entry(struct bonding *bond);
diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c
index 57541d2..6fd95a2 100644
--- a/drivers/net/cpmac.c
+++ b/drivers/net/cpmac.c
@@ -34,6 +34,7 @@
 #include <linux/skbuff.h>
 #include <linux/mii.h>
 #include <linux/phy.h>
+#include <linux/phy_fixed.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <asm/gpio.h>
@@ -53,12 +54,6 @@ MODULE_PARM_DESC(debug_level, "Number of NETIF_MSG bits to enable");
 MODULE_PARM_DESC(dumb_switch, "Assume switch is not connected to MDIO bus");
 
 #define CPMAC_VERSION "0.5.0"
-/* stolen from net/ieee80211.h */
-#ifndef MAC_FMT
-#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
-#define MAC_ARG(x) ((u8*)(x))[0], ((u8*)(x))[1], ((u8*)(x))[2], \
-		   ((u8*)(x))[3], ((u8*)(x))[4], ((u8*)(x))[5]
-#endif
 /* frame size + 802.1q tag */
 #define CPMAC_SKB_SIZE		(ETH_FRAME_LEN + 4)
 #define CPMAC_QUEUES	8
@@ -211,6 +206,7 @@ struct cpmac_priv {
 	struct net_device *dev;
 	struct work_struct reset_work;
 	struct platform_device *pdev;
+	struct napi_struct napi;
 };
 
 static irqreturn_t cpmac_irq(int, void *);
@@ -362,47 +358,48 @@ static void cpmac_set_multicast_list(struct net_device *dev)
 	}
 }
 
-static struct sk_buff *cpmac_rx_one(struct net_device *dev,
-				    struct cpmac_priv *priv,
+static struct sk_buff *cpmac_rx_one(struct cpmac_priv *priv,
 				    struct cpmac_desc *desc)
 {
 	struct sk_buff *skb, *result = NULL;
 
 	if (unlikely(netif_msg_hw(priv)))
-		cpmac_dump_desc(dev, desc);
+		cpmac_dump_desc(priv->dev, desc);
 	cpmac_write(priv->regs, CPMAC_RX_ACK(0), (u32)desc->mapping);
 	if (unlikely(!desc->datalen)) {
 		if (netif_msg_rx_err(priv) && net_ratelimit())
 			printk(KERN_WARNING "%s: rx: spurious interrupt\n",
-			       dev->name);
+			       priv->dev->name);
 		return NULL;
 	}
 
-	skb = netdev_alloc_skb(dev, CPMAC_SKB_SIZE);
+	skb = netdev_alloc_skb(priv->dev, CPMAC_SKB_SIZE);
 	if (likely(skb)) {
 		skb_reserve(skb, 2);
 		skb_put(desc->skb, desc->datalen);
-		desc->skb->protocol = eth_type_trans(desc->skb, dev);
+		desc->skb->protocol = eth_type_trans(desc->skb, priv->dev);
 		desc->skb->ip_summed = CHECKSUM_NONE;
-		dev->stats.rx_packets++;
-		dev->stats.rx_bytes += desc->datalen;
+		priv->dev->stats.rx_packets++;
+		priv->dev->stats.rx_bytes += desc->datalen;
 		result = desc->skb;
-		dma_unmap_single(&dev->dev, desc->data_mapping, CPMAC_SKB_SIZE,
-				 DMA_FROM_DEVICE);
+		dma_unmap_single(&priv->dev->dev, desc->data_mapping,
+				 CPMAC_SKB_SIZE, DMA_FROM_DEVICE);
 		desc->skb = skb;
-		desc->data_mapping = dma_map_single(&dev->dev, skb->data,
+		desc->data_mapping = dma_map_single(&priv->dev->dev, skb->data,
 						    CPMAC_SKB_SIZE,
 						    DMA_FROM_DEVICE);
 		desc->hw_data = (u32)desc->data_mapping;
 		if (unlikely(netif_msg_pktdata(priv))) {
-			printk(KERN_DEBUG "%s: received packet:\n", dev->name);
-			cpmac_dump_skb(dev, result);
+			printk(KERN_DEBUG "%s: received packet:\n",
+			       priv->dev->name);
+			cpmac_dump_skb(priv->dev, result);
 		}
 	} else {
 		if (netif_msg_rx_err(priv) && net_ratelimit())
 			printk(KERN_WARNING
-			       "%s: low on skbs, dropping packet\n", dev->name);
-		dev->stats.rx_dropped++;
+			       "%s: low on skbs, dropping packet\n",
+			       priv->dev->name);
+		priv->dev->stats.rx_dropped++;
 	}
 
 	desc->buflen = CPMAC_SKB_SIZE;
@@ -411,25 +408,25 @@ static struct sk_buff *cpmac_rx_one(struct net_device *dev,
 	return result;
 }
 
-static int cpmac_poll(struct net_device *dev, int *budget)
+static int cpmac_poll(struct napi_struct *napi, int budget)
 {
 	struct sk_buff *skb;
 	struct cpmac_desc *desc;
-	int received = 0, quota = min(dev->quota, *budget);
-	struct cpmac_priv *priv = netdev_priv(dev);
+	int received = 0;
+	struct cpmac_priv *priv = container_of(napi, struct cpmac_priv, napi);
 
 	spin_lock(&priv->rx_lock);
 	if (unlikely(!priv->rx_head)) {
 		if (netif_msg_rx_err(priv) && net_ratelimit())
 			printk(KERN_WARNING "%s: rx: polling, but no queue\n",
-			       dev->name);
-		netif_rx_complete(dev);
+			       priv->dev->name);
+		netif_rx_complete(priv->dev, napi);
 		return 0;
 	}
 
 	desc = priv->rx_head;
-	while ((received < quota) && ((desc->dataflags & CPMAC_OWN) == 0)) {
-		skb = cpmac_rx_one(dev, priv, desc);
+	while (((desc->dataflags & CPMAC_OWN) == 0) && (received < budget)) {
+		skb = cpmac_rx_one(priv, desc);
 		if (likely(skb)) {
 			netif_receive_skb(skb);
 			received++;
@@ -439,13 +436,11 @@ static int cpmac_poll(struct net_device *dev, int *budget)
 
 	priv->rx_head = desc;
 	spin_unlock(&priv->rx_lock);
-	*budget -= received;
-	dev->quota -= received;
 	if (unlikely(netif_msg_rx_status(priv)))
-		printk(KERN_DEBUG "%s: poll processed %d packets\n", dev->name,
-		       received);
+		printk(KERN_DEBUG "%s: poll processed %d packets\n",
+		       priv->dev->name, received);
 	if (desc->dataflags & CPMAC_OWN) {
-		netif_rx_complete(dev);
+		netif_rx_complete(priv->dev, napi);
 		cpmac_write(priv->regs, CPMAC_RX_PTR(0), (u32)desc->mapping);
 		cpmac_write(priv->regs, CPMAC_RX_INT_ENABLE, 1);
 		return 0;
@@ -655,6 +650,7 @@ static void cpmac_hw_error(struct work_struct *work)
 	spin_unlock(&priv->rx_lock);
 	cpmac_clear_tx(priv->dev);
 	cpmac_hw_start(priv->dev);
+	napi_enable(&priv->napi);
 	netif_start_queue(priv->dev);
 }
 
@@ -681,8 +677,10 @@ static irqreturn_t cpmac_irq(int irq, void *dev_id)
 
 	if (status & MAC_INT_RX) {
 		queue = (status >> 8) & 7;
-		netif_rx_schedule(dev);
-		cpmac_write(priv->regs, CPMAC_RX_INT_CLEAR, 1 << queue);
+		if (netif_rx_schedule_prep(dev, &priv->napi)) {
+			cpmac_write(priv->regs, CPMAC_RX_INT_CLEAR, 1 << queue);
+			__netif_rx_schedule(dev, &priv->napi);
+		}
 	}
 
 	cpmac_write(priv->regs, CPMAC_MAC_EOI_VECTOR, 0);
@@ -692,6 +690,7 @@ static irqreturn_t cpmac_irq(int irq, void *dev_id)
 			printk(KERN_ERR "%s: hw error, resetting...\n",
 			       dev->name);
 		netif_stop_queue(dev);
+		napi_disable(&priv->napi);
 		cpmac_hw_stop(dev);
 		schedule_work(&priv->reset_work);
 		if (unlikely(netif_msg_hw(priv)))
@@ -849,6 +848,15 @@ static void cpmac_adjust_link(struct net_device *dev)
 	spin_unlock(&priv->lock);
 }
 
+static int cpmac_link_update(struct net_device *dev,
+			     struct fixed_phy_status *status)
+{
+	status->link = 1;
+	status->speed = 100;
+	status->duplex = 1;
+	return 0;
+}
+
 static int cpmac_open(struct net_device *dev)
 {
 	int i, size, res;
@@ -857,15 +865,6 @@ static int cpmac_open(struct net_device *dev)
 	struct cpmac_desc *desc;
 	struct sk_buff *skb;
 
-	priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link,
-				0, PHY_INTERFACE_MODE_MII);
-	if (IS_ERR(priv->phy)) {
-		if (netif_msg_drv(priv))
-			printk(KERN_ERR "%s: Could not attach to PHY\n",
-			       dev->name);
-		return PTR_ERR(priv->phy);
-	}
-
 	mem = platform_get_resource_byname(priv->pdev, IORESOURCE_MEM, "regs");
 	if (!request_mem_region(mem->start, mem->end - mem->start, dev->name)) {
 		if (netif_msg_drv(priv))
@@ -927,6 +926,7 @@ static int cpmac_open(struct net_device *dev)
 	INIT_WORK(&priv->reset_work, cpmac_hw_error);
 	cpmac_hw_start(dev);
 
+	napi_enable(&priv->napi);
 	priv->phy->state = PHY_CHANGELINK;
 	phy_start(priv->phy);
 
@@ -951,8 +951,6 @@ fail_remap:
 	release_mem_region(mem->start, mem->end - mem->start);
 
 fail_reserve:
-	phy_disconnect(priv->phy);
-
 	return res;
 }
 
@@ -965,9 +963,8 @@ static int cpmac_stop(struct net_device *dev)
 	netif_stop_queue(dev);
 
 	cancel_work_sync(&priv->reset_work);
+	napi_disable(&priv->napi);
 	phy_stop(priv->phy);
-	phy_disconnect(priv->phy);
-	priv->phy = NULL;
 
 	cpmac_hw_stop(dev);
 
@@ -1001,11 +998,13 @@ static int external_switch;
 
 static int __devinit cpmac_probe(struct platform_device *pdev)
 {
-	int rc, phy_id;
+	int rc, phy_id, i;
 	struct resource *mem;
 	struct cpmac_priv *priv;
 	struct net_device *dev;
 	struct plat_cpmac_data *pdata;
+	struct fixed_info *fixed_phy;
+	DECLARE_MAC_BUF(mac);
 
 	pdata = pdev->dev.platform_data;
 
@@ -1053,21 +1052,51 @@ static int __devinit cpmac_probe(struct platform_device *pdev)
 	dev->set_multicast_list = cpmac_set_multicast_list;
 	dev->tx_timeout         = cpmac_tx_timeout;
 	dev->ethtool_ops        = &cpmac_ethtool_ops;
-	dev->poll = cpmac_poll;
-	dev->weight = 64;
 	dev->features |= NETIF_F_MULTI_QUEUE;
 
+	netif_napi_add(dev, &priv->napi, cpmac_poll, 64);
+
 	spin_lock_init(&priv->lock);
 	spin_lock_init(&priv->rx_lock);
 	priv->dev = dev;
 	priv->ring_size = 64;
 	priv->msg_enable = netif_msg_init(debug_level, 0xff);
 	memcpy(dev->dev_addr, pdata->dev_addr, sizeof(dev->dev_addr));
+
 	if (phy_id == 31) {
-		snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT,
-			 cpmac_mii.id, phy_id);
-	} else
-		snprintf(priv->phy_name, BUS_ID_SIZE, "fixed@%d:%d", 100, 1);
+		snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT, cpmac_mii.id,
+			 phy_id);
+	} else {
+		/* Let's try to get a free fixed phy... */
+		for (i = 0; i < MAX_PHY_AMNT; i++) {
+			fixed_phy = fixed_mdio_get_phydev(i);
+			if (!fixed_phy)
+				continue;
+			if (!fixed_phy->phydev->attached_dev) {
+				strncpy(priv->phy_name,
+					fixed_phy->phydev->dev.bus_id,
+					BUS_ID_SIZE);
+				fixed_mdio_set_link_update(fixed_phy->phydev,
+							   &cpmac_link_update);
+				goto phy_found;
+			}
+		}
+		if (netif_msg_drv(priv))
+			printk(KERN_ERR "%s: Could not find fixed PHY\n",
+			       dev->name);
+		rc = -ENODEV;
+		goto fail;
+	}
+
+phy_found:
+	priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link, 0,
+				PHY_INTERFACE_MODE_MII);
+	if (IS_ERR(priv->phy)) {
+		if (netif_msg_drv(priv))
+			printk(KERN_ERR "%s: Could not attach to PHY\n",
+			       dev->name);
+		return PTR_ERR(priv->phy);
+	}
 
 	if ((rc = register_netdev(dev))) {
 		printk(KERN_ERR "cpmac: error %i registering device %s\n", rc,
@@ -1077,9 +1106,9 @@ static int __devinit cpmac_probe(struct platform_device *pdev)
 
 	if (netif_msg_probe(priv)) {
 		printk(KERN_INFO
-		       "cpmac: device %s (regs: %p, irq: %d, phy: %s, mac: "
-		       MAC_FMT ")\n", dev->name, (void *)mem->start, dev->irq,
-		       priv->phy_name, MAC_ARG(dev->dev_addr));
+		       "cpmac: device %s (regs: %p, irq: %d, phy: %s, "
+		       "mac: %s)\n", dev->name, (void *)mem->start, dev->irq,
+		       priv->phy_name, print_mac(mac, dev->dev_addr));
 	}
 	return 0;
 
diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h
index b557bb4..4b4b74e 100644
--- a/drivers/net/ehea/ehea.h
+++ b/drivers/net/ehea/ehea.h
@@ -40,7 +40,7 @@
 #include <asm/io.h>
 
 #define DRV_NAME	"ehea"
-#define DRV_VERSION	"EHEA_0078"
+#define DRV_VERSION	"EHEA_0079"
 
 /* eHEA capability flags */
 #define DLPAR_PORT_ADD_REM 1
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index 2809c99..0a7e789 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -2329,7 +2329,7 @@ static void port_napi_disable(struct ehea_port *port)
 {
 	int i;
 
-	for (i = 0; i < port->num_def_qps; i++)
+	for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++)
 		napi_disable(&port->port_res[i].napi);
 }
 
@@ -2337,7 +2337,7 @@ static void port_napi_enable(struct ehea_port *port)
 {
 	int i;
 
-	for (i = 0; i < port->num_def_qps; i++)
+	for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++)
 		napi_enable(&port->port_res[i].napi);
 }
 
@@ -2373,8 +2373,6 @@ static int ehea_down(struct net_device *dev)
 	ehea_drop_multicast_list(dev);
 	ehea_free_interrupts(dev);
 
-	port_napi_disable(port);
-
 	port->state = EHEA_PORT_DOWN;
 
 	ret = ehea_clean_all_portres(port);
@@ -2396,6 +2394,7 @@ static int ehea_stop(struct net_device *dev)
 	flush_scheduled_work();
 	down(&port->port_lock);
 	netif_stop_queue(dev);
+	port_napi_disable(port);
 	ret = ehea_down(dev);
 	up(&port->port_lock);
 	return ret;
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 70ddf1a..92ce2e3 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -5597,6 +5597,22 @@ static struct pci_device_id pci_tbl[] = {
 		PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_31),
 		.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR,
 	},
+	{	/* MCP77 Ethernet Controller */
+		PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_32),
+		.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
+	},
+	{	/* MCP77 Ethernet Controller */
+		PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_33),
+		.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
+	},
+	{	/* MCP77 Ethernet Controller */
+		PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_34),
+		.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
+	},
+	{	/* MCP77 Ethernet Controller */
+		PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_35),
+		.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT,
+	},
 	{0,},
 };
 
diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c
index 6888723..dbd23bb 100644
--- a/drivers/net/ipg.c
+++ b/drivers/net/ipg.c
@@ -55,6 +55,26 @@ MODULE_DESCRIPTION("IC Plus IP1000 Gigabit Ethernet Adapter Linux Driver "
 		   DrvVer);
 MODULE_LICENSE("GPL");
 
+//variable record -- index by leading revision/length
+//Revision/Length(=N*4), Address1, Data1, Address2, Data2,...,AddressN,DataN
+static unsigned short DefaultPhyParam[] = {
+	// 11/12/03 IP1000A v1-3 rev=0x40
+	/*--------------------------------------------------------------------------
+	(0x4000|(15*4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 22, 0x85bd, 24, 0xfff2,
+				 27, 0x0c10, 28, 0x0c10, 29, 0x2c10, 31, 0x0003, 23, 0x92f6,
+				 31, 0x0000, 23, 0x003d, 30, 0x00de, 20, 0x20e7,  9, 0x0700,
+	  --------------------------------------------------------------------------*/
+	// 12/17/03 IP1000A v1-4 rev=0x40
+	(0x4000 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
+	    0x0000,
+	30, 0x005e, 9, 0x0700,
+	// 01/09/04 IP1000A v1-5 rev=0x41
+	(0x4100 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
+	    0x0000,
+	30, 0x005e, 9, 0x0700,
+	0x0000
+};
+
 static const char *ipg_brand_name[] = {
 	"IC PLUS IP1000 1000/100/10 based NIC",
 	"Sundance Technology ST2021 based NIC",
@@ -990,7 +1010,7 @@ static void ipg_nic_txcleanup(struct net_device *dev)
 }
 
 /* Provides statistical information about the IPG NIC. */
-struct net_device_stats *ipg_nic_get_stats(struct net_device *dev)
+static struct net_device_stats *ipg_nic_get_stats(struct net_device *dev)
 {
 	struct ipg_nic_private *sp = netdev_priv(dev);
 	void __iomem *ioaddr = sp->ioaddr;
diff --git a/drivers/net/ipg.h b/drivers/net/ipg.h
index e418b90..d5d092c 100644
--- a/drivers/net/ipg.h
+++ b/drivers/net/ipg.h
@@ -833,24 +833,4 @@ struct ipg_nic_private {
 	struct delayed_work task;
 };
 
-//variable record -- index by leading revision/length
-//Revision/Length(=N*4), Address1, Data1, Address2, Data2,...,AddressN,DataN
-unsigned short DefaultPhyParam[] = {
-	// 11/12/03 IP1000A v1-3 rev=0x40
-	/*--------------------------------------------------------------------------
-	(0x4000|(15*4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 22, 0x85bd, 24, 0xfff2,
-				 27, 0x0c10, 28, 0x0c10, 29, 0x2c10, 31, 0x0003, 23, 0x92f6,
-				 31, 0x0000, 23, 0x003d, 30, 0x00de, 20, 0x20e7,  9, 0x0700,
-	  --------------------------------------------------------------------------*/
-	// 12/17/03 IP1000A v1-4 rev=0x40
-	(0x4000 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
-	    0x0000,
-	30, 0x005e, 9, 0x0700,
-	// 01/09/04 IP1000A v1-5 rev=0x41
-	(0x4100 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
-	    0x0000,
-	30, 0x005e, 9, 0x0700,
-	0x0000
-};
-
 #endif				/* __LINUX_IPG_H */
diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c
index 9531171..87cde06 100644
--- a/drivers/net/natsemi.c
+++ b/drivers/net/natsemi.c
@@ -864,6 +864,7 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
 
 	np = netdev_priv(dev);
 	netif_napi_add(dev, &np->napi, natsemi_poll, 64);
+	np->dev = dev;
 
 	np->pci_dev = pdev;
 	pci_set_drvdata(pdev, dev);
diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index cd991a0..1ebe325 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -512,11 +512,19 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf)
 	}
 	tmp = le32_to_cpu(u.init_c->max_transfer_size);
 	if (tmp < dev->hard_mtu) {
-		dev_err(&intf->dev,
-			"dev can't take %u byte packets (max %u)\n",
-			dev->hard_mtu, tmp);
-		retval = -EINVAL;
-		goto fail_and_release;
+		if (tmp <= net->hard_header_len) {
+			dev_err(&intf->dev,
+				"dev can't take %u byte packets (max %u)\n",
+				dev->hard_mtu, tmp);
+			retval = -EINVAL;
+			goto fail_and_release;
+		}
+		dev->hard_mtu = tmp;
+		net->mtu = dev->hard_mtu - net->hard_header_len;
+		dev_warn(&intf->dev,
+			 "dev can't take %u byte packets (max %u), "
+			 "adjusting MTU to %u\n",
+			 dev->hard_mtu, tmp, net->mtu);
 	}
 
 	/* REVISIT:  peripheral "alignment" request is ignored ... */
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 4e10a07..e44aac8 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -1236,6 +1236,10 @@
 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE       0x0560
 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE       0x056C
 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE       0x0759
+#define PCI_DEVICE_ID_NVIDIA_NVENET_32              0x0760
+#define PCI_DEVICE_ID_NVIDIA_NVENET_33              0x0761
+#define PCI_DEVICE_ID_NVIDIA_NVENET_34              0x0762
+#define PCI_DEVICE_ID_NVIDIA_NVENET_35              0x0763
 
 #define PCI_VENDOR_ID_IMS		0x10e0
 #define PCI_DEVICE_ID_IMS_TT128		0x9128

^ permalink raw reply related

* Re: [PATCH net-2.6] [TCP]: fix D-SACK cwnd handling
From: Ilpo Järvinen @ 2007-10-25  8:39 UTC (permalink / raw)
  To: Ryousei Takano, David Miller; +Cc: Netdev, y-kodama
In-Reply-To: <20071025.171147.107738702.takano@axe-inc.co.jp>

[-- Attachment #1: Type: TEXT/PLAIN, Size: 3178 bytes --]

On Thu, 25 Oct 2007, Ryousei Takano wrote:

> In the current net-2.6 kernel, handling FLAG_DSACKING_ACK is broken.
> The flag is cleared to 1 just after FLAG_DSACKING_ACK is set.
> 
>         if (found_dup_sack)
>                 flag |= FLAG_DSACKING_ACK;
> 	:
> 	flag = 1;
> 
> To fix it, this patch introduces a part of the tcp_sacktag_state patch:
> 	http://marc.info/?l=linux-netdev&m=119210560431519&w=2
> 
> Do you plan to apply the tcp_sacktag_state patch?
> Or please apply this patch.

Thanks for noticing this, it seems I've been playing too much already with 
the safer code where the possibility of this kind of annoying trap is 
already removed and wasn't therefore careful enough anymore. Luckily, it 
doesn't have huge impact.

...I'd say that this is better for now (it could have been a separate one
right from the beginning, conforms better to just-one-thing-per-patch). 
State patch and other things that depend on it are not targetted, at 
earliest, to 2.6.25 AFAICT, so no big hurry to have it fully.

Acked-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>

-- 
 i.

> Signed-off-by: David S. Miller <davem@davemloft.net>
> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
> Signed-off-by: Ryousei Takano <takano-ryousei@aist.go.jp>
> ---
>  net/ipv4/tcp_input.c |   12 +++++-------
>  1 files changed, 5 insertions(+), 7 deletions(-)
> 
> diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
> index 3dbbb44..59e3c9a 100644
> --- a/net/ipv4/tcp_input.c
> +++ b/net/ipv4/tcp_input.c
> @@ -1248,6 +1248,7 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
>  	int cached_fack_count;
>  	int i;
>  	int first_sack_index;
> +	int force_one_sack;
>  
>  	if (!tp->sacked_out) {
>  		if (WARN_ON(tp->fackets_out))
> @@ -1272,18 +1273,18 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
>  	 * if the only SACK change is the increase of the end_seq of
>  	 * the first block then only apply that SACK block
>  	 * and use retrans queue hinting otherwise slowpath */
> -	flag = 1;
> +	force_one_sack = 1;
>  	for (i = 0; i < num_sacks; i++) {
>  		__be32 start_seq = sp[i].start_seq;
>  		__be32 end_seq = sp[i].end_seq;
>  
>  		if (i == 0) {
>  			if (tp->recv_sack_cache[i].start_seq != start_seq)
> -				flag = 0;
> +				force_one_sack = 0;
>  		} else {
>  			if ((tp->recv_sack_cache[i].start_seq != start_seq) ||
>  			    (tp->recv_sack_cache[i].end_seq != end_seq))
> -				flag = 0;
> +				force_one_sack = 0;
>  		}
>  		tp->recv_sack_cache[i].start_seq = start_seq;
>  		tp->recv_sack_cache[i].end_seq = end_seq;
> @@ -1295,7 +1296,7 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
>  	}
>  
>  	first_sack_index = 0;
> -	if (flag)
> +	if (force_one_sack)
>  		num_sacks = 1;
>  	else {
>  		int j;
> @@ -1321,9 +1322,6 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
>  		}
>  	}
>  
> -	/* clear flag as used for different purpose in following code */
> -	flag = 0;
> -
>  	/* Use SACK fastpath hint if valid */
>  	cached_skb = tp->fastpath_skb_hint;
>  	cached_fack_count = tp->fastpath_cnt_hint;
> 

^ permalink raw reply

* [PATCH net-2.6] [TCP]: fix D-SACK cwnd handling
From: Ryousei Takano @ 2007-10-25  8:11 UTC (permalink / raw)
  To: netdev; +Cc: ilpo.jarvinen, davem, y-kodama

In the current net-2.6 kernel, handling FLAG_DSACKING_ACK is broken.
The flag is cleared to 1 just after FLAG_DSACKING_ACK is set.

        if (found_dup_sack)
                flag |= FLAG_DSACKING_ACK;
	:
	flag = 1;

To fix it, this patch introduces a part of the tcp_sacktag_state patch:
	http://marc.info/?l=linux-netdev&m=119210560431519&w=2

Do you plan to apply the tcp_sacktag_state patch?
Or please apply this patch.

Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Signed-off-by: Ryousei Takano <takano-ryousei@aist.go.jp>
---
 net/ipv4/tcp_input.c |   12 +++++-------
 1 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 3dbbb44..59e3c9a 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1248,6 +1248,7 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
 	int cached_fack_count;
 	int i;
 	int first_sack_index;
+	int force_one_sack;
 
 	if (!tp->sacked_out) {
 		if (WARN_ON(tp->fackets_out))
@@ -1272,18 +1273,18 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
 	 * if the only SACK change is the increase of the end_seq of
 	 * the first block then only apply that SACK block
 	 * and use retrans queue hinting otherwise slowpath */
-	flag = 1;
+	force_one_sack = 1;
 	for (i = 0; i < num_sacks; i++) {
 		__be32 start_seq = sp[i].start_seq;
 		__be32 end_seq = sp[i].end_seq;
 
 		if (i == 0) {
 			if (tp->recv_sack_cache[i].start_seq != start_seq)
-				flag = 0;
+				force_one_sack = 0;
 		} else {
 			if ((tp->recv_sack_cache[i].start_seq != start_seq) ||
 			    (tp->recv_sack_cache[i].end_seq != end_seq))
-				flag = 0;
+				force_one_sack = 0;
 		}
 		tp->recv_sack_cache[i].start_seq = start_seq;
 		tp->recv_sack_cache[i].end_seq = end_seq;
@@ -1295,7 +1296,7 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
 	}
 
 	first_sack_index = 0;
-	if (flag)
+	if (force_one_sack)
 		num_sacks = 1;
 	else {
 		int j;
@@ -1321,9 +1322,6 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, u32 prior_snd_
 		}
 	}
 
-	/* clear flag as used for different purpose in following code */
-	flag = 0;
-
 	/* Use SACK fastpath hint if valid */
 	cached_skb = tp->fastpath_skb_hint;
 	cached_fack_count = tp->fastpath_cnt_hint;
-- 
1.5.3.4


^ permalink raw reply related

* Re: Regression in current git - Network Manager fails (bisected)
From: Thomas Graf @ 2007-10-25  9:06 UTC (permalink / raw)
  To: Dan Williams
  Cc: Denis V. Lunev, netdev, Denis V. Lunev, David S. Miller,
	Alexey Kuznetsov
In-Reply-To: <1193148618.8648.22.camel@localhost.localdomain>

* Dan Williams <dcbw@redhat.com> 2007-10-23 10:10
> Should I make NM disable ACKs for now until it gets fixed?

The reason libnl enables ACKs by default is to give the
application using it clear synchronisation points. For
change requests that means the interface function won't
return until the change has been commited as it will
call nl_wait_for_ack(). So if you disable it in NM and
run it on "old" kernels still using async netlink you
won't be sure when the change is actually being done so
this might break things if you rely on it.

I think providing a invalid message handler which returns
NL_OK if nlmsg_type is NLMSG_DONE or NLMSG_ERROR && err == 0
would be better if you need some kind of workaround. As
those messages are always last this should never cause
real troubles.

^ permalink raw reply

* Re: [RFC][PATCH 0/3][XFRM]: Support packet processing error statistics.
From: Masahide NAKAMURA @ 2007-10-25  9:06 UTC (permalink / raw)
  To: hadi; +Cc: Herbert Xu, David Miller, netdev
In-Reply-To: <1193228326.4442.12.camel@localhost>

Wednesday 24 October 2007 21:18, jamal wrote:
> On Wed, 2007-24-10 at 12:30 +0900, Masahide NAKAMURA wrote:
> 
> > At IPsec point of view, actually "SPI mismatch" caused by user configuration
> > cannot be identified easily since identify of SAD is consist of SPI, address and
> > protocol(ESP/AH...) and linux SAD uses hash database. It is database identify
> > mismatch. Then, SPI mismatch goes "NoStates" at my patch.
> > OTOH Key mismatch goes "ProtoError" since esp[46]_input returns error.
> 
> Would be useful to just document what you said above so that user doesnt
> have to intepret it.

OK, I write it to commit-log then. If anybody have another place
where such information should be written, tell me.

[snip]
> > > In any case, it seems to me to be more accurate to not call them MIB
> > > stats if they are not. This doesnt qualify using the macros, utilities
> > > etc used for MIBs.
> > 
> 
> BTW, I meant "doesnt disqualify them" above;-> 

OK ;-)

Jamal, thanks for many comments.

-- 
Masahide NAKAMURA

^ permalink raw reply

* Re: [PATCH,RFC] Marvell Orion SoC ethernet driver
From: Jeff Garzik @ 2007-10-25  9:12 UTC (permalink / raw)
  To: Lennert Buytenhek; +Cc: netdev, tzachi, nico
In-Reply-To: <20071016192806.GD19618@xi.wantstofly.org>

Lennert Buytenhek wrote:
> +struct rx_desc {
> +	u32 cmd_sts;
> +	u16 size;
> +	u16 count;
> +	u32 buf;
> +	u32 next;
> +};
> +
> +struct tx_desc {
> +	u32 cmd_sts;
> +	u16 l4i_chk;
> +	u16 count;
> +	u32 buf;
> +	u32 next;
> +};

should use sparse type (__le32, etc.) and make sure this driver passes 
sparse checks

ditto for checkpatch (except for the excessively anal stuff)


> +struct orion_priv {
> +	unsigned long base_addr;
> +
> +	/*
> +	 * RX stuff
> +	 */
> +	u32 rxd_used;
> +	u32 rxd_curr;
> +	u32 rxd_count;
> +	u32 rxd_max_pending;
> +	struct sk_buff *rx_skb[RX_DESC_NR];
> +	struct rx_desc *rxd_base;
> +	dma_addr_t rxd_base_dma;
> +	spinlock_t rx_lock;
> +	struct timer_list rx_fill_timer;
> +
> +	/*
> +	 * TX stuff
> +	 */
> +	u32 txd_used;
> +	u32 txd_curr;
> +	u32 txd_count;
> +	u32 txd_max_pending;
> +	struct sk_buff *tx_skb[TX_DESC_NR];
> +	struct tx_desc *txd_base;
> +	dma_addr_t txd_base_dma;
> +	spinlock_t tx_lock;
> +
> +	/*
> +	 * PHY stuff
> +	 */
> +	struct mii_if_info mii;
> +	spinlock_t mii_lock;
> +
> +	/*
> +	 * Statistics counters
> +	 */
> +	struct net_device_stats stats;
> +};
> +
> +/*****************************************************************************
> + * PHY access
> + ****************************************************************************/
> +static int orion_mii_read(struct net_device *dev, int phy_id, int reg)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	int val, i;
> +
> +	spin_lock(&op->mii_lock);
> +
> +	/*
> +	 * Poll until not busy
> +	 */
> +	for (i = 10000; i && (rdl(op, ETH_SMI) & SMI_BUSY); i--)
> +		rmb();
> +
> +	if (i == 0) {
> +		printk("orion-eth mii read busy timeout\n");
> +		val = -1;
> +		goto out;
> +	}
> +
> +	/*
> +	 * Issue read command
> +	 */
> +	wrl(op, ETH_SMI, (phy_id << SMI_DEV_OFFS) |
> +			 (reg << SMI_REG_OFFS) | SMI_READ);
> +
> +	/*
> +	 * Poll until data is ready
> +	 */
> +	for (i = 10000; i && !(rdl(op, ETH_SMI) & SMI_READ_VALID); i--)
> +		rmb();
> +
> +	if (i == 0) {
> +		printk("orion-eth mii read busy timeout\n");
> +		val = -1;
> +		goto out;
> +	}
> +
> +	/*
> +	 * Read data
> +	 */
> +	val = rdl(op, ETH_SMI) & 0xffff;
> +
> +out:
> +	spin_unlock(&op->mii_lock);
> +	return val;
> +}
> +
> +static void orion_mii_write(struct net_device *dev, int phy_id, int reg, int data)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	int i;
> +
> +	spin_lock(&op->mii_lock);
> +
> +	/*
> +	 * Poll until not busy
> +	 */
> +	for (i = 10000; i && (rdl(op, ETH_SMI) & SMI_BUSY); i--)
> +		rmb();
> +
> +	if (i == 0) {
> +		printk("orion-eth mii write busy timeout\n");
> +		goto out;
> +	}
> +
> +	/*
> +	 * Issue write command
> +	 */
> +	wrl(op, ETH_SMI, (phy_id << 16) | (reg << 21) | data);
> +
> +out:
> +	spin_unlock(&op->mii_lock);
> +}
> +
> +/*
> + * Called from orion_irq in interrupt context.
> + * Not going out to read PHY status, using Orion registers instead.
> + */
> +static inline void orion_phy_link_change(struct net_device *dev)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	u32 stat = rdl(op, PORT_STAT);
> +
> +	if (!(stat & STAT_LINK_UP)) {
> +		netif_carrier_off(dev);
> +		netif_stop_queue(dev);
> +		printk(KERN_NOTICE "%s: link down.\n", dev->name);
> +	} else {
> +		netif_carrier_on(dev);
> +		netif_wake_queue(dev);
> +		netif_poll_enable(dev);
> +		printk(KERN_NOTICE "%s: link up, ", dev->name);
> +		if (stat & STAT_FULL_DUPLEX)
> +			printk("full duplex, ");
> +		else
> +			printk("half duplex, ");
> +		if (stat & STAT_SPEED_1000)
> +			printk("1000Mbps.\n");
> +		else if (stat & STAT_SPEED_100)
> +			printk("100Mbps\n");
> +		else
> +			printk("10Mbps\n");
> +	}
> +}
> +
> +/*****************************************************************************
> + * MAC address filtering
> + ****************************************************************************/
> +static void orion_set_unicast(struct orion_priv *op, u8 *addr)
> +{
> +	int i;
> +
> +	/*
> +	 * Clear unicast table
> +	 */
> +	for (i = 0; i < PORT_UCAST_SIZE; i += 4)
> +		wrl(op, PORT_UCAST_BASE + i, 0);
> +
> +	/*
> +	 * Setup MAC addr registers
> +	 */
> +	wrl(op, PORT_MAC_HI, (addr[0] << 24) | (addr[1] << 16) |
> +			     (addr[2] << 8) | addr[3]);
> +	wrl(op, PORT_MAC_LO, (addr[4] << 8) | addr[5]);
> +
> +	/*
> +	 * Enable our entry in unicat table
> +	 */
> +	wrb(op, PORT_UCAST_BASE + (addr[5] & 0xf), 1);
> +}
> +
> +static void orion_set_promisc(struct orion_priv *op)
> +{
> +	int i;
> +
> +	/*
> +	 * Turn on promiscuous mode
> +	 */
> +	wrl(op, PORT_CONF, rdl(op, PORT_CONF) | 1);
> +
> +	/*
> +	 * Remove our addr from MAC addr registers
> +	 */
> +	wrl(op, PORT_MAC_LO, 0xffff);
> +	wrl(op, PORT_MAC_HI, 0xffffffff);
> +
> +	/*
> +	 * Enable all entries in address filter tables
> +	 */
> +	for (i = 0; i < PORT_SPEC_MCAST_SIZE; i += 4)
> +		wrl(op, PORT_SPEC_MCAST_BASE + i, 0x01010101);
> +	for (i = 0; i < PORT_OTHER_MCAST_SIZE; i += 4)
> +		wrl(op, PORT_OTHER_MCAST_BASE + i, 0x01010101);
> +	for (i = 0; i < PORT_UCAST_SIZE; i += 4)
> +		wrl(op, PORT_UCAST_BASE + i, 0x01010101);
> +}
> +
> +static void orion_set_allmulti(struct orion_priv *op)
> +{
> +	int i;
> +
> +	/*
> +	 * Enable all entries in multicast address tables
> +	 */
> +	for (i = 0; i < PORT_SPEC_MCAST_SIZE; i += 4)
> +		wrl(op, PORT_SPEC_MCAST_BASE + i, 0x01010101);
> +	for (i = 0; i < PORT_OTHER_MCAST_SIZE; i += 4)
> +		wrl(op, PORT_OTHER_MCAST_BASE + i, 0x01010101);
> +}
> +
> +static u8 orion_mcast_hash(u8 *addr)
> +{
> +	/*
> +	 * CRC-8 x^8+x^2+x^1+1
> +	 */
> +#define b(bit)	(((addr[(bit)/8]) >> (7 - ((bit) % 8))) & 1)
> +
> +	return(((b(2)^b(4)^b(7)^b(8)^b(12)^b(13)^b(16)^b(17)^b(19)^
> +		b(24)^b(26)^b(28)^b(29)^b(31)^b(33)^b(35)^b(39)^b(40)^
> +		b(41)^b(47) ) << 0)
> +		|
> +		((b(1)^b(2)^b(3)^b(4)^b(6)^b(8)^b(11)^b(13)^b(15)^
> +		b(17)^b(18)^b(19)^b(23)^b(24)^b(25)^b(26)^b(27)^b(29)^
> +		b(30)^b(31)^b(32)^b(33)^b(34)^b(35)^b(38)^b(41)^b(46)^
> +		b(47)) << 1)
> +		|
> +		((b(0)^b(1)^b(3)^b(4)^b(5)^b(8)^b(10)^b(13)^b(14)^
> +		b(18)^b(19)^b(22)^b(23)^b(25)^b(30)^b(32)^b(34)^b(35)^
> +		b(37)^b(39)^b(41)^b(45)^b(46)^b(47)) << 2)
> +		|
> +		((b(0)^b(2)^b(3)^b(4)^b(7)^b(9)^b(12)^b(13)^b(17)^
> +		b(18)^b(21)^b(22)^b(24)^b(29)^b(31)^b(33)^b(34)^b(36)^
> +		b(38)^b(40)^b(44)^b(45)^b(46)) << 3)
> +		|
> +		((b(1)^b(2)^b(3)^b(6)^b(8)^b(11)^b(12)^b(16)^b(17)^
> +		b(20)^b(21)^b(23)^b(28)^b(30)^b(32)^b(33)^b(35)^b(37)^
> +		b(39)^b(43)^b(44)^b(45)) << 4)
> +		|
> +		((b(0)^b(1)^b(2)^b(5)^b(7)^b(10)^b(11)^b(15)^b(16)^
> +		b(19)^b(20)^b(22)^b(27)^b(29)^b(31)^b(32)^b(34)^b(36)^
> +		b(38)^b(42)^b(43)^b(44)) << 5)
> +		|
> +		((b(0)^b(1)^b(4)^b(6)^b(9)^b(10)^b(14)^b(15)^b(18)^
> +		b(19)^b(21)^b(26)^b(28)^b(30)^b(31)^b(33)^b(35)^b(37)^
> +		b(41)^b(42)^b(43)) << 6)
> +		|
> +		((b(0)^b(3)^b(5)^b(8)^b(9)^b(13)^b(14)^b(17)^b(18)^
> +		b(20)^b(25)^b(27)^b(29)^b(30)^b(32)^b(34)^b(36)^b(40)^
> +		b(41)^b(42)) << 7));
> +}

maybe a lib/ function?


> +static void orion_set_multi_list(struct net_device *dev)
> +{
> +	struct dev_mc_list *addr = dev->mc_list;
> +	struct orion_priv *op = netdev_priv(dev);
> +	int i;
> +	u8 *p;
> +
> +	/*
> +	 * Enable specific entries in multicast filter table
> +	 */
> +	for (i = 0; i < dev->mc_count; i++, addr = addr->next) {
> +		if (!addr)
> +			break;
> + 		p = addr->dmi_addr;
> +		if ((p[0] == 0x01) && (p[1] == 0x00) && (p[2] == 0x5E) &&
> +		    (p[3] == 0x00) && (p[4] == 0x00)) {
> +			wrb(op, PORT_SPEC_MCAST_BASE + p[5], 1);
> +		} else {
> +			u8 entry = orion_mcast_hash(p);
> +			wrb(op, PORT_OTHER_MCAST_BASE + entry, 1);
> +		}
> +	}

what happens if dev->mc_count is a big number?  (answer for most: fall 
back to ALLMULTI behavior)


> +static void orion_clr_allmulti(struct orion_priv *op)
> +{
> +	int i;
> +
> +	/*
> +	 * Clear multicast tables
> +	 */
> +	for (i = 0; i < PORT_SPEC_MCAST_SIZE; i += 4)
> +		wrl(op, PORT_SPEC_MCAST_BASE + i, 0);
> +	for (i = 0; i < PORT_OTHER_MCAST_SIZE; i += 4)
> +		wrl(op, PORT_OTHER_MCAST_BASE + i, 0);
> +}
> +
> +static void orion_multicast(struct net_device *dev)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +
> +	if (dev->flags & IFF_PROMISC) {
> +		orion_set_promisc(op);
> +	} else {
> +		/*
> +		 * If we were in promisc mode, we now must turn it off and
> +		 * setup our MAC addr again in HW registers and unicast table
> +		 */
> +		wrl(op, PORT_CONF, rdl(op, PORT_CONF) & (~1));
> +		orion_set_unicast(op, dev->dev_addr);
> +
> +		if (dev->flags & IFF_ALLMULTI) {
> +			orion_set_allmulti(op);
> +		} else {
> +			/*
> +			 * If we were in promiscuous/allmulti mode, we now
> +			 * must clear the multicast tables first
> +			 */
> +			orion_clr_allmulti(op);
> +
> +			if (dev->mc_count) {
> +				orion_set_multi_list(dev);
> +			}
> +		}
> +	}
> +}
> +
> +static int orion_set_mac_addr(struct net_device *dev, void *p)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	struct sockaddr *addr = p;
> +
> +	if (!is_valid_ether_addr(addr->sa_data))
> +		return -EADDRNOTAVAIL;
> +
> +	/*
> +	 * Setup addr to HW registers and unicast table
> +	 */
> +	orion_set_unicast(op, addr->sa_data);
> +
> +	/*
> +	 * Store new addr in net_dev
> +	 */
> +	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
> +
> +	return 0;
> +}
> +
> +/*****************************************************************************
> + * Data flow RX/TX
> + ****************************************************************************/
> +static u32 orion_tx_done(struct net_device *dev)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	struct tx_desc *txd;
> +	u32 count = 0, cmd_sts;
> +
> +#ifndef ORION_TX_DONE_IN_TX
> +	spin_lock_bh(&op->tx_lock);
> +#endif

ifdef'd spinlocking is a maintenance problem


> +	while ((op->txd_count > 0)) {

why this condition?  its highly unusual, most net drivers use another 
loop ending condition


> +		txd = &op->txd_base[op->txd_used];
> +		cmd_sts = txd->cmd_sts;
> +
> +		if (cmd_sts & TXD_DMA)
> +			break;
> +
> +		dma_unmap_single(NULL, txd->buf, txd->count, DMA_TO_DEVICE);
> +
> +		if (cmd_sts & TXD_LAST) {
> +			/*
> +			 * The skb was stored at the packet's last frag index
> +			 */
> +			dev_kfree_skb_any(op->tx_skb[op->txd_used]);
> +
> +			if (cmd_sts & TXD_ERR)
> +				op->stats.tx_errors++;
> +		}
> +
> +		count++;
> +		op->txd_count--;
> +		op->txd_used = (op->txd_used + 1) % TX_DESC_NR;
> +	}
> +
> +	/*
> +	 * If transmission was previously stopped, now it can be restarted
> +	 */
> +	if (count && netif_queue_stopped(dev) && (dev->flags & IFF_UP))
> +		netif_wake_queue(dev);
> +
> +#ifndef ORION_TX_DONE_IN_TX
> +	spin_unlock_bh(&op->tx_lock);
> +#endif
> +	return count;
> +}
> +
> +static int orion_tx(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	struct tx_desc *txd, *txd_first;
> +	u32 count = 0, txd_flags = 0;
> +	int ret = NETDEV_TX_OK;
> +
> +	spin_lock_bh(&op->tx_lock);
> +
> +	if (unlikely(skb->len > MAX_PKT_SIZE)) {
> +		op->stats.tx_dropped++;
> +		dev_kfree_skb(skb);
> +		goto out;
> +	}
> +
> +	/*
> +	 * Stop TX if there are not enough descriptors available. The next
> +	 * TX-Done will enable TX back after making available descriptors.
> +	 */
> +	if (TX_DESC_NR - op->txd_count < skb_shinfo(skb)->nr_frags + 1) {
> +		netif_stop_queue(dev);
> +		ret = NETDEV_TX_BUSY;
> +		goto out;
> +	}
> +
> +	/*
> +	 * Buffers with a payload <= 8 bytes must be aligned on 8 bytes boundary.
> +	 * If there is such a small unaligned fragment we linearize the skb.
> +	 */
> +	if (skb_is_nonlinear(skb)) {
> +		int i;
> +		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
> +			skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
> +			if (unlikely(frag->size <= 8 && frag->page_offset & 0x7)) {
> +				if (__skb_linearize(skb)) {
> +					op->stats.tx_dropped++;
> +					goto out;
> +				}
> +				break;
> +			}
> +		}
> +	}
> +
> +	/*
> +	 * Need to remember the first desc to handle multiple frags
> +	 */
> +	txd_first = &op->txd_base[op->txd_curr];
> +
> +	do {
> +		u8* buf;
> +		u32 size;
> +
> +		txd = &op->txd_base[op->txd_curr];
> +
> +		if (skb_shinfo(skb)->nr_frags == 0) {
> +			buf = skb->data;
> +			size = skb->len;
> +		} else {
> +			if (count == 0) {
> +				buf = skb->data;
> +				size = skb_headlen(skb);
> +			} else {
> +				skb_frag_t *frag = &skb_shinfo(skb)->frags[count - 1];
> +				buf = page_address(frag->page) + frag->page_offset;
> +				size = frag->size;
> +			}
> +		}
> +
> +		/*
> +		 * Setup the descriptor and only pass ownership to HW for the non-first
> +		 * descriptors. Some cmd_sts flags for the first and last descriptos are
> +		 * being set outside the loop.
> +		 */
> +		txd->buf = dma_map_single(NULL, buf, size, DMA_TO_DEVICE);
> +		txd->count = size;
> +		if (count > 0)
> +			txd->cmd_sts = TXD_DMA;
> +
> +		op->tx_skb[op->txd_curr] = (void *)0xffffffff;
> +
> +		count++;
> +		op->txd_curr = (op->txd_curr + 1) % TX_DESC_NR;
> +
> +	} while (count < skb_shinfo(skb)->nr_frags + 1);
> +
> +#ifdef ORION_TX_CSUM_OFFLOAD
> +	/*
> +	 * Setup checksum offloading flags for the 'first' txd
> +	 */
> +	if (skb->ip_summed == CHECKSUM_COMPLETE ||
> +		skb->ip_summed == CHECKSUM_PARTIAL) {
> +		txd_flags = TXD_IP_CSUM | TXD_IP_NO_FRAG | TXD_L4_CSUM |
> +				(ip_hdr(skb)->ihl << TXD_IP_HDRLEN_OFFS);
> +		if (ip_hdr(skb)->protocol == IPPROTO_UDP)
> +			txd_flags |= TXD_L4_UDP;
> +	} else {
> +		/*
> +		 * Workaround (Errata). Leaving IP hdr len '0' might cause
> +		 * a wrong checksum calc of the next packet.
> +		 */
> +		txd_flags = 5 << TXD_IP_HDRLEN_OFFS;
> +	}
> +#endif

don't ifdef this, control it via ethtool (default:off if necessary)


> +	wmb();
> +
> +	if (count == 1) {
> +		/*
> +		 * Single buffer case - set 'first' & 'last' flags
> +		 */
> +		txd->cmd_sts = txd_flags | TXD_DMA | TXD_CRC | TXD_INT |
> +				TXD_PAD | TXD_FRST | TXD_LAST;
> +	} else {
> +		/*
> +		 * Multiple buffers case - set 'last' flags first,
> +		 * and 'first' flags last.
> +		 */
> +		txd->cmd_sts = TXD_DMA | TXD_INT | TXD_PAD | TXD_LAST;
> +		wmb();
> +		txd_first->cmd_sts = txd_flags | TXD_DMA | TXD_CRC | TXD_FRST;
> +	}
> +
> +	/*
> +	 * Store skb for tx_done in the last frag index
> +	 */
> +	if(op->txd_curr != 0)
> +		op->tx_skb[op->txd_curr - 1] = skb;
> +	else
> +		op->tx_skb[TX_DESC_NR - 1] = skb;
> +
> +	/*
> +	 * Apply send command
> +	 */
> +	wmb();
> +	wrl(op, PORT_TXQ_CMD, PORT_EN_TXQ0);
> +
> +	op->txd_count += count;
> +	if (op->txd_count > op->txd_max_pending)
> +		op->txd_max_pending = op->txd_count;
> +
> +	op->stats.tx_bytes += skb->len;
> +	op->stats.tx_packets++;
> +	dev->trans_start = jiffies;
> +
> +#ifdef ORION_TX_DONE_IN_TX
> +	if(op->txd_count > TX_DONE_THRESH)
> +		orion_tx_done(dev);
> +#endif
> +
> +out:
> +	spin_unlock_bh(&op->tx_lock);
> +	return ret;
> +}
> +
> +static void orion_rx_fill(struct orion_priv *op)
> +{
> +	struct sk_buff *skb;
> +	struct rx_desc *rxd;
> +	int alloc_skb_failed = 0;
> +	u32 unaligned;
> +
> +	spin_lock_bh(&op->rx_lock);
> +
> +	while (op->rxd_count < RX_DESC_NR) {
> +
> +		rxd = &op->rxd_base[op->rxd_used];
> +
> +		if (rxd->cmd_sts & RXD_DMA) {
> +			printk(KERN_ERR "orion_rx_fill error, desc owned by DMA\n");
> +			break;
> +		}
> +
> +		skb = dev_alloc_skb(MAX_PKT_SIZE + dma_get_cache_alignment());
> +		if (!skb) {
> +			alloc_skb_failed = 1;
> +			break;
> +		}
> +
> +		unaligned = (u32)skb->data & (dma_get_cache_alignment() - 1);
> +		if (unaligned)
> +			skb_reserve(skb, dma_get_cache_alignment() - unaligned);
> +
> +		/*
> +		 * HW skips on first 2B to align the IP header
> +		 */
> +		skb_reserve(skb, 2);
> +
> +		op->rx_skb[op->rxd_used] = skb;
> +
> +		rxd->buf = dma_map_single(NULL, skb->data, MAX_PKT_SIZE - 2,
> +						DMA_FROM_DEVICE);
> +		rxd->size = MAX_PKT_SIZE & RXD_SIZE_MASK;
> +		rxd->count = 0;
> +		wmb();
> +		rxd->cmd_sts = RXD_DMA | RXD_INT;
> +
> +		op->rxd_count++;
> +		op->rxd_used = (op->rxd_used + 1) % RX_DESC_NR;
> +	}
> +
> +	/*
> +	 * If skb_alloc failed and the number of rx buffers in the ring is
> +	 * less than half of the ring size, then set a timer to try again
> +	 * later (100ms).
> +	 */
> +	if (alloc_skb_failed && op->rxd_count < RX_DESC_NR / 2) {
> +		printk(KERN_INFO "orion_rx_fill set timer to alloc bufs\n");
> +		if (!timer_pending(&op->rx_fill_timer))
> +			mod_timer(&op->rx_fill_timer, jiffies + (HZ / 10));
> +	}
> +
> +	spin_unlock_bh(&op->rx_lock);
> +}

why spin_lock_bh(rx_lock) ?  RX is traditionally pretty lightweight, 
lock-wise, because it is an independent process.

also, you could just use napi_enable/disable and completely remove the 
lock, controlling the RX process that way


> +static void orion_rx_fill_on_timeout(unsigned long data)
> +{
> +	orion_rx_fill(((struct net_device *)data)->priv);
> +}
> +
> +#ifdef ORION_RX_CSUM_OFFLOAD
> +static inline int orion_rx_is_good_csum(struct rx_desc *rxd)
> +{
> +	if ((rxd->count > 72) &&
> +	    (rxd->cmd_sts & RXD_IP_TYPE) &&
> +	    (rxd->cmd_sts & RXD_IP_HDR_OK) &&
> +	    (!(rxd->size & RXD_IP_FRAG)) &&
> +	    (!(rxd->cmd_sts & RXD_L4_NO_TYPE)) &&
> +	    (rxd->cmd_sts & RXD_L4_CSUM_OK))
> +		return 1;
> +
> +	return 0;
> +}
> +#endif
> +
> +static inline int get_rx_pending(struct orion_priv *op)
> +{
> +	u32 hw_rxd = (rdl(op, PORT_CURR_RXD) - op->rxd_base_dma) / sizeof(struct rx_desc);
> +	u32 sw_rxd = (&op->rxd_base[op->rxd_curr] - op->rxd_base) / sizeof(struct rx_desc);
> +
> +	if (hw_rxd > sw_rxd)
> +		return(hw_rxd - sw_rxd);
> +	else
> +		return(RX_DESC_NR - (sw_rxd - hw_rxd));
> +}
> +
> +static int orion_rx(struct net_device *dev, u32 work_to_do)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	struct rx_desc *rxd;
> +	u32 work_done = 0, cmd_sts;
> +	struct sk_buff *skb;
> +	u32 pending;
> +
> +	spin_lock_bh(&op->rx_lock);
> +
> +	pending = get_rx_pending(op);
> +	if (pending > op->rxd_max_pending)
> +		op->rxd_max_pending = pending;
> +
> +	while (op->rxd_count > 0 && work_done < work_to_do) {
> +
> +		rxd = &op->rxd_base[op->rxd_curr];
> +		cmd_sts = rxd->cmd_sts;
> +
> +		if (cmd_sts & RXD_DMA)
> +			break;
> +
> +		skb = op->rx_skb[op->rxd_curr];
> +		dma_unmap_single(NULL, rxd->buf, rxd->size & RXD_SIZE_MASK, DMA_FROM_DEVICE);
> +
> +		if ((cmd_sts & RXD_FRST) && (cmd_sts & RXD_LAST) &&
> +						!(cmd_sts & RXD_ERR)) {
> +
> +			/*
> +			 * Good RX
> +			 */
> +			op->stats.rx_packets++;
> +			op->stats.rx_bytes += rxd->count;
> +
> +			/*
> +			 * Reduce 4B crc + 2B offset
> +			 */
> +			skb_put(skb, (rxd->count - 4 - 2));
> +
> +#ifdef ORION_RX_CSUM_OFFLOAD
> +			if (orion_rx_is_good_csum(rxd)) {
> +				skb->csum =  htons((rxd->cmd_sts & RXD_L4_CSUM_MASK)
> +							>> RXD_L4_CSUM_OFFS);
> +				skb->ip_summed = CHECKSUM_UNNECESSARY;
> +			} else {
> +				skb->ip_summed = CHECKSUM_NONE;
> +			}
> +#else
> +			skb->ip_summed = CHECKSUM_NONE;
> +#endif
> +
> +			skb->protocol = eth_type_trans(skb, dev);
> +			skb->dev = dev;
> +
> +			netif_receive_skb(skb);
> +			work_done++;
> +
> +		} else {
> +			dev_kfree_skb(skb);
> +			op->stats.rx_errors++;
> +			op->stats.rx_dropped++;
> +		}
> +
> +		dev->last_rx = jiffies;
> +
> +		op->rxd_count--;
> +		op->rxd_curr = (op->rxd_curr + 1) % RX_DESC_NR;
> +	}
> +
> +	spin_unlock_bh(&op->rx_lock);
> +
> +	/*
> +	 * Refill RX buffers when only half of the decriptors left available
> +	 */
> +	if (work_done && (op->rxd_count < RX_DESC_NR / 2))
> +		orion_rx_fill(op);
> +
> +	return work_done;
> +}
> +
> +static int orion_poll(struct net_device *dev, int *budget)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	int rx_work_done = 0, tx_work_done = 0;
> +
> +#ifndef ORION_TX_DONE_IN_TX
> +	/*
> +	 * Release transmitted buffers
> +	 */
> +	tx_work_done = orion_tx_done(dev);
> +#endif
> +
> +	/*
> +	 * Push up receive buffers
> +	 */
> +	rx_work_done = orion_rx(dev, min(*budget, dev->quota));
> +	*budget -= rx_work_done;
> +	dev->quota -= rx_work_done;
> +
> +	/*
> +	 * If no work was done, go down from NAPI list and enable interrupts
> +	 */
> +	if (((tx_work_done == 0) && (rx_work_done == 0)) ||
> +		(!netif_running(dev)) ) {
> +		netif_rx_complete(dev);
> +		wrl(op, PORT_MASK, PIC_MASK);
> +		wrl(op, PORT_MASK_EXT, PICE_MASK);
> +		return 0;
> +	}
> +
> +	return 1;
> +}
> +
> +static irqreturn_t orion_irq(int irq , void *dev_id)
> +{
> +	struct net_device *dev = (struct net_device *)dev_id;

remove pointless cast


> +	struct orion_priv *op = netdev_priv(dev);
> +	u32 pic, pice = 0;
> +
> +	pic = rdl(op, PORT_CAUSE) & rdl(op, PORT_MASK);
> +	if (pic == 0)

generally wise to check for 0xffffffff (hardware fault / unplugged / 
scrogged)


> +		return IRQ_NONE;
> +	wrl(op, PORT_CAUSE, ~pic);
> +
> +	if (pic & PIC_EXT) {
> +		pice = rdl(op, PORT_CAUSE_EXT) & rdl(op, PORT_MASK_EXT);
> +		wrl(op, PORT_CAUSE_EXT, ~pice);
> +
> +		/*
> +		 * Link status change event
> +		 */
> +		if (pice & (PICE_PHY | PICE_LINK)) {
> +			orion_phy_link_change(dev);
> +			pice &= ~(PICE_PHY | PICE_LINK);
> +		}
> +		pic &= ~(PIC_EXT);
> +	}
> +
> +	/*
> +	 * RX/TX events handled outside IRQ context (NAPI) while interrups
> +	 * disabled (PHY Link interrupts left unmask)
> +	 */
> +	if (pic || pice) {
> +		if (netif_rx_schedule_prep(dev)) {
> +			wrl(op, PORT_MASK, PIC_EXT);
> +			wrl(op, PORT_MASK_EXT, PICE_PHY | PICE_LINK);
> +			wrl(op, PORT_CAUSE, 0);
> +			wrl(op, PORT_CAUSE_EXT, 0);
> +
> +			__netif_rx_schedule(dev);
> +		}
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*****************************************************************************
> + * Tools and statistics
> + ****************************************************************************/
> +static struct net_device_stats *orion_get_stats(struct net_device *dev)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	return &(op->stats);

use struct net_device::stats rather than local copy


> +static void orion_get_drvinfo(struct net_device *dev,
> +				struct ethtool_drvinfo *info)
> +{
> +	strcpy(info->driver, DRV_NAME);
> +	strcpy(info->version, DRV_VERSION);
> +	strcpy(info->fw_version, "N/A");
> +}
> +
> +static int orion_get_settings(struct net_device *dev,
> +				struct ethtool_cmd *cmd)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	return mii_ethtool_gset(&op->mii, cmd);
> +}
> +
> +static int orion_set_settings(struct net_device *dev,
> +				struct ethtool_cmd *cmd)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	return mii_ethtool_sset(&op->mii, cmd);
> +}
> +
> +static int orion_nway_reset(struct net_device *dev)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	return mii_nway_restart(&op->mii);
> +}
> +
> +static u32 orion_get_link(struct net_device *dev)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	return mii_link_ok(&op->mii);
> +}
> +
> +static void orion_get_ringparam(struct net_device *dev,
> +				struct ethtool_ringparam *ring)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +
> +	ring->rx_max_pending = op->rxd_max_pending;
> +	ring->tx_max_pending = op->txd_max_pending;
> +	ring->rx_pending = get_rx_pending(op);
> +	ring->tx_pending = op->txd_count;
> +	ring->rx_mini_max_pending = -1;
> +	ring->rx_jumbo_max_pending = -1;
> +	ring->rx_mini_pending = -1;
> +	ring->rx_jumbo_pending = -1;
> +}
> +
> +static u32 orion_get_rx_csum(struct net_device *netdev)
> +{
> +#ifdef ORION_RX_CSUM_OFFLOAD
> +	return 1;
> +#else
> +	return 0;
> +#endif
> +}
> +
> +static u32 orion_get_tx_csum(struct net_device *netdev)
> +{
> +#ifdef ORION_TX_CSUM_OFFLOAD
> +	return 1;
> +#else
> +	return 0;
> +#endif
> +}
> +
> +static struct ethtool_ops orion_ethtool_ops = {
> +	.get_drvinfo		= orion_get_drvinfo,
> +	.get_settings		= orion_get_settings,
> +	.set_settings		= orion_set_settings,
> +	.nway_reset		= orion_nway_reset,
> +	.get_link		= orion_get_link,
> +	.get_ringparam		= orion_get_ringparam,
> +	.get_rx_csum		= orion_get_rx_csum,
> +	.get_tx_csum		= orion_get_tx_csum,
> +};
> +
> +static int orion_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	struct mii_ioctl_data *data = if_mii(ifr);
> +
> +	return generic_mii_ioctl(&op->mii, data, cmd, NULL);
> +}
> +
> +void orion_clr_mib(struct orion_priv *op)
> +{
> +	/*
> +	 * Dummy reads do the work
> +	 */
> +	int i, dummy;
> +	for (i = 0; i < PORT_MIB_SIZE; i += 4)
> +		dummy = rdl(op, (PORT_MIB_BASE + i));
> +}
> +
> +/*****************************************************************************
> + * Start/Stop
> + ****************************************************************************/
> +static void orion_init_hw(struct orion_priv *op)
> +{
> +	int i;
> +
> +	/*
> +	 * Mask and clear Ethernet unit interrupts
> +	 */
> +	wrl(op, ETH_MASK, 0);
> +	wrl(op, ETH_CAUSE, 0);
> +
> +	/*
> +	 * Clear address filter tables
> +	 */
> +	for (i = 0; i < PORT_UCAST_SIZE; i += 4)
> +		wrl(op, PORT_UCAST_BASE + i, 0);
> +	for (i = 0; i < PORT_SPEC_MCAST_SIZE; i += 4)
> +		wrl(op, PORT_SPEC_MCAST_BASE + i, 0);
> +	for (i = 0; i < PORT_OTHER_MCAST_SIZE; i += 4)
> +		wrl(op, PORT_OTHER_MCAST_BASE + i, 0);
> +}
> +
> +static void orion_start_hw(struct orion_priv *op)
> +{
> +	/*
> +	 * Clear and mask interrupts
> +	 */
> +	wrl(op, PORT_CAUSE, 0);
> +	wrl(op, PORT_CAUSE_EXT, 0);
> +	wrl(op, PORT_MASK, 0);
> +	wrl(op, PORT_MASK_EXT, 0);
> +
> +	/*
> +	 * Clear MIB counters
> +	 */
> +	orion_clr_mib(op);
> +
> +	/*
> +	 * Setup HW with TXD/RXD base
> +	 */
> +	wrl(op, PORT_CURR_TXD, op->txd_base_dma);
> +	wrl(op, PORT_CURR_RXD, op->rxd_base_dma);
> +
> +	/*
> +	 * Basic default port config
> +	 */
> +	wrl(op, PORT_CONF, (1 << 25));
> +	wrl(op, PORT_CONF_EXT, 0);
> +	wrl(op, PORT_SERIAL, 0x0240609);
> +	wrl(op, PORT_SDMA, 0x01021038);
> +	wrl(op, PORT_MTU, 0x0);
> +	wrl(op, PORT_TX_THRESH, 0x2100);
> +
> +	/*
> +	 * Enable RX & TX queues (using only queue '0')
> +	 */
> +	wrl(op, PORT_RXQ_CMD, PORT_EN_RXQ0);
> +	wrl(op, PORT_TXQ_CMD, PORT_EN_TXQ0);
> +
> +	/*
> +	 * Unmask interrupts
> +	 */
> +	wrl(op, PORT_MASK, PIC_MASK);
> +	wrl(op, PORT_MASK_EXT, PICE_MASK);
> +}
> +
> +static int orion_open(struct net_device *dev)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +	int err;
> +
> +	setup_timer(&op->rx_fill_timer, orion_rx_fill_on_timeout,
> +					(unsigned long)dev);
> +
> +	err = request_irq(dev->irq, orion_irq, IRQF_SAMPLE_RANDOM, dev->name, dev);
> +	if (err) {
> +		del_timer(&op->rx_fill_timer);
> +		printk(KERN_ERR "Failed to request IRQ %d\n", dev->irq);
> +		return err;
> +	}
> +
> +	/*
> +	 * Fill RX buffers and start the HW
> +	 */
> +	orion_rx_fill(op);
> +	orion_start_hw(op);
> +	orion_phy_link_change(dev);
> +
> +	return 0;
> +}
> +
> +static int orion_close(struct net_device *dev)
> +{
> +	struct orion_priv *op = netdev_priv(dev);
> +
> +	/*
> +	 * Clear and mask interrupts
> +	 */
> +	wrl(op, PORT_MASK, 0);
> +	wrl(op, PORT_MASK_EXT, 0);
> +	wrl(op, PORT_CAUSE, 0);
> +	wrl(op, PORT_CAUSE_EXT, 0);
> +
> +	/*
> +	 * Stop RX, reset descriptors, free buffers and RX timer
> +	 */
> +	spin_lock_bh(&op->rx_lock);
> +
> +	wrl(op, PORT_RXQ_CMD, PORT_DIS_RXQ0);
> +	mdelay(1);

this is a poor and unfriendly synchronization method


> +	while (op->rxd_count > 0) {
> +		struct rx_desc *rxd = &op->rxd_base[op->rxd_curr];
> +		dma_unmap_single(NULL, rxd->buf, rxd->size & RXD_SIZE_MASK, DMA_FROM_DEVICE);
> +		rxd->cmd_sts = rxd->size = rxd->count = rxd->buf = 0;
> +		dev_kfree_skb_any(op->rx_skb[op->rxd_curr]);
> +		op->rxd_count--;
> +		op->rxd_curr = (op->rxd_curr + 1) % RX_DESC_NR;
> +	}
> +	op->rxd_curr = op->rxd_used = op->rxd_max_pending = 0;
> +	wrl(op, PORT_CURR_RXD, op->rxd_base_dma);
> +
> +
> +	spin_unlock_bh(&op->rx_lock);
> +
> +	/*
> +	 * Stop TX, reset descriptors, free buffers
> +	 */
> +	spin_lock_bh(&op->tx_lock);
> +
> +	netif_stop_queue(dev);
> +
> +	wrl(op, PORT_TXQ_CMD, PORT_DIS_TXQ0);
> +	mdelay(1);

ditto


> +	while (op->txd_count > 0) {
> +		struct tx_desc *txd = &op->txd_base[op->txd_curr];
> +		dma_unmap_single(NULL, txd->buf, txd->count, DMA_TO_DEVICE);
> +		if ((txd->cmd_sts & TXD_LAST))
> +			dev_kfree_skb_any(op->tx_skb[op->txd_used]);
> +		txd->cmd_sts = txd->l4i_chk = txd->count = txd->buf = 0;
> +		op->txd_count--;
> +		op->txd_used = (op->txd_used + 1) % TX_DESC_NR;
> +	}
> +	op->txd_curr = op->txd_used = op->txd_max_pending = 0;
> +	wrl(op, PORT_CURR_TXD, op->txd_base_dma);
> +
> +	spin_unlock_bh(&op->tx_lock);
> +
> +	/*
> +	 * Diable serial interface
> +	 */
> +	wrl(op, PORT_SERIAL, rdl(op, PORT_SERIAL) & (~1));
> +	mdelay(1);
> +
> +	free_irq(dev->irq, dev);
> +
> +	/*
> +	 * Stop poll and set Link down state
> +	 */
> +	netif_poll_disable(dev);
> +	netif_carrier_off(dev);
> +
> +	return 0;
> +}
> +
> +/*****************************************************************************
> + * Probe/Remove
> + ****************************************************************************/
> +static int orion_remove(struct platform_device *pdev)
> +{
> +	struct net_device *dev;
> +	struct orion_priv *op;
> +
> +	/*
> +	 * Remove net_device link
> +	 */
> +	dev = platform_get_drvdata(pdev);
> +	if (dev == NULL)
> +		return 0;

test for impossible condition


> +	platform_set_drvdata(pdev, NULL);
> +
> +	/*
> +	 * Close and remove interface
> +	 */
> +	unregister_netdev(dev);
> +
> +	/*
> +	 * Free our private data and net_device
> +	 */
> +	op = netdev_priv(dev);
> +	if (op == NULL)
> +		return 0;

ditto

> +	iounmap((void *)op->base_addr);

pointless void* cast


> +	del_timer(&op->rx_fill_timer);

del_timer_sync()


> +	if (op->rxd_base)
> +		dma_free_coherent(NULL, sizeof(struct rx_desc) * RX_DESC_NR,
> +			op->rxd_base, op->rxd_base_dma);
> +
> +	if (op->txd_base)
> +		dma_free_coherent(NULL, sizeof(struct tx_desc) * TX_DESC_NR,
> +			op->txd_base, op->txd_base_dma);
> +
> +	free_netdev(dev);
> +
> +	return 0;
> +}
> +
> +static int orion_probe(struct platform_device *pdev)
> +{
> +	struct orion_eth_data *data;
> +	struct net_device *dev;
> +	struct orion_priv *op;
> +	struct rx_desc *rxd;
> +	struct tx_desc *txd;
> +	int i, err, irq;
> +	struct resource *res;
> +	u32 base_addr;
> +
> +	if (pdev == NULL)
> +		return -ENODEV;

pointless test


> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (res == NULL)
> +		return -ENODEV;
> +	base_addr = res->start;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (res == NULL)
> +		return -ENODEV;
> +	irq = res->start;
> +
> +	data = pdev->dev.platform_data;
> +
> +	dev = alloc_etherdev(sizeof(struct orion_priv));
> +	if (dev == NULL)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, dev);
> +
> +	op = netdev_priv(dev);
> +	op->base_addr = (u32)ioremap(base_addr, 64 * 1024);
> +	if (!op->base_addr) {
> +		err = -EIO;
> +		goto err_out;
> +	}
> +
> +	/*
> + 	 * Put HW in quite mode
> +	 */
> +	orion_init_hw(op);
> +
> +	/*
> + 	 * Setup our net_device
> +	 */
> +	dev->base_addr = op->base_addr;
> +	dev->irq = irq;
> +	dev->open = orion_open;
> +	dev->stop = orion_close;
> +	dev->hard_start_xmit = orion_tx;
> +	dev->do_ioctl = orion_ioctl;
> +	dev->get_stats = orion_get_stats;
> +	dev->ethtool_ops = &orion_ethtool_ops;
> +	dev->set_mac_address = orion_set_mac_addr;
> +	dev->set_multicast_list = orion_multicast;
> +	dev->poll = orion_poll;
> +	dev->weight = 64;
> +	dev->tx_queue_len = TX_DESC_NR;
> +	SET_ETHTOOL_OPS(dev, &orion_ethtool_ops);
> +#ifdef ORION_TX_CSUM_OFFLOAD
> +	dev->features = NETIF_F_SG | NETIF_F_IP_CSUM;
> +#endif

a tx_timeout method (that resets the NIC, usually) would be nice


> +	/*
> +	 * Use MAC address from (1) board specific data, or (2) current HW
> +	 * settings, or (3) random address.
> +	 */
> +	if (is_valid_ether_addr(data->dev_addr)) {
> +		memcpy(dev->dev_addr, data->dev_addr, ETH_ALEN);
> +		printk(KERN_INFO "Using board specific MAC address\n");
> +	} else {
> +		/*
> +		 * Read from HW (Boot loader settings)
> +		 */
> +		u32 mac_h, mac_l;
> +		mac_h = rdl(op, PORT_MAC_HI);
> +		mac_l = rdl(op, PORT_MAC_LO);
> +
> +		dev->dev_addr[0] = (mac_h >> 24) & 0xff;
> +		dev->dev_addr[1] = (mac_h >> 16) & 0xff;
> +		dev->dev_addr[2] = (mac_h >> 8) & 0xff;
> +		dev->dev_addr[3] = mac_h & 0xff;
> +		dev->dev_addr[4] = (mac_l >> 8) & 0xff;
> +		dev->dev_addr[5] = mac_l & 0xff;
> +
> +		if (!is_valid_ether_addr(dev->dev_addr)) {
> +			printk(KERN_INFO "Invalid MAC address "
> +				"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x, "
> +				"using random address instead\n",
> +				dev->dev_addr[0], dev->dev_addr[1],
> +				dev->dev_addr[2], dev->dev_addr[3],
> +				dev->dev_addr[4], dev->dev_addr[5]);
> +			random_ether_addr(dev->dev_addr);
> +		}
> +	}
> +
> +	orion_set_unicast(op, dev->dev_addr);
> +
> +	/*
> +	 * Setup MII data
> +	 */
> +	op->mii.phy_id = data->phy_id;
> +	op->mii.phy_id_mask = 0x1f;
> +	op->mii.reg_num_mask = 0x1f;
> +	op->mii.dev = dev;
> +	op->mii.supports_gmii = 1;
> +	op->mii.mdio_read = orion_mii_read;
> +	op->mii.mdio_write = orion_mii_write;
> +
> +	/*
> +	 * Enable PHY autoneg
> +	 */
> +	orion_mii_write(dev, op->mii.phy_id, MII_BMCR, orion_mii_read(dev,
> +			op->mii.phy_id, MII_BMCR) | BMCR_ANENABLE);
> +
> +	/*
> +	 * Setup our net_device private date
> +	 */
> +	spin_lock_init(&op->tx_lock);
> +	spin_lock_init(&op->rx_lock);
> +	spin_lock_init(&op->mii_lock);
> +
> +	/*
> +	 * Setup RX descriptors rings
> +	 */
> +	op->rxd_used = op->rxd_curr = op->rxd_count = 0;
> +	op->rxd_base = dma_alloc_coherent(NULL, sizeof(struct rx_desc) *
> +			RX_DESC_NR, &op->rxd_base_dma, GFP_KERNEL | GFP_DMA);
> +	if (op->rxd_base == NULL) {
> +		printk(KERN_ERR "Failed to alloc RX descriptors\n");
> +		err = -ENOMEM;
> +		goto err_out;
> +	}
> +	memset(op->rxd_base, 0, sizeof(struct rx_desc) * RX_DESC_NR);
> +	for (i = 0, rxd = op->rxd_base; i < RX_DESC_NR - 1; i++, rxd++)
> +		rxd->next = op->rxd_base_dma +
> +				((i + 1) * sizeof(struct rx_desc));
> +	rxd->next = op->rxd_base_dma;
> +
> +	/*
> +	 * Setup TX descriptors rings
> +	 */
> +	op->txd_used = op->txd_curr = op->txd_count = 0;
> +	op->txd_base = dma_alloc_coherent(NULL, sizeof(struct tx_desc) *
> +			TX_DESC_NR, &op->txd_base_dma, GFP_KERNEL | GFP_DMA);
> +	if (op->txd_base == NULL) {
> +		dev_err(&pdev->dev, "Failed to alloc TX descriptors\n");
> +		err = -ENOMEM;
> +		goto err_out;
> +	}
> +	memset(op->txd_base, 0, sizeof(struct tx_desc) * TX_DESC_NR);
> +	for (i = 0, txd = op->txd_base; i < TX_DESC_NR - 1; i++, txd++)
> +		txd->next = op->txd_base_dma +
> +				((i + 1) * sizeof(struct tx_desc));
> +	txd->next = op->txd_base_dma;
> +
> +	/*
> +	 * Register our device
> +	 */
> +	err = register_netdev(dev);
> +	if (err) {
> +		dev_err(&pdev->dev, "Failed to register netdev\n");
> +		goto err_out;
> +	}
> +
> +	printk(KERN_INFO "%s: Orion on-chip gigabit ethernet, IRQ %d, "
> +		"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x, PHY ID %d.\n", dev->name,
> +		dev->irq, dev->dev_addr[0], dev->dev_addr[1],
> +		dev->dev_addr[2], dev->dev_addr[3],
> +		dev->dev_addr[4], dev->dev_addr[5], op->mii.phy_id);
> +
> +	return 0;
> +
> +err_out:
> +	orion_remove(pdev);
> +	return err;
> +}
> +
> +int orion_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> +	/* Not implemented yet */
> +	return -ENOSYS;
> +}
> +
> +int orion_resume(struct platform_device *pdev)
> +{
> +	/* Not implemented yet */
> +	return -ENOSYS;
> +}

just delete these until actually implemented


^ permalink raw reply

* Re: [PATCH] wan: new driver retina
From: Jeff Garzik @ 2007-10-25  9:18 UTC (permalink / raw)
  To: Matti Linnanvuori; +Cc: akpm, info, netdev
In-Reply-To: <586461.49400.qm@web52009.mail.re2.yahoo.com>


Not sure what's going on, but your patch looks scrambled, as scripts 
look at it:

> From: Matti Linnanvuori <mattilinnanvuori@yahoo.com>=0A=0AAdding Retina G.7=
> 03 and G.SHDSL driver.=0A=0ASigned-off-by: Matti Linnanvuori <mattilinnanvu=
> ori@yahoo.com>=0A---=0A=0AFixing bugs and style according to netdev mailing=
>  list comments.=0A=0Adiff -Napur linux-2.6.23/drivers/net/wan/Kconfig linux=
> -2.6.24/drivers/net/wan/Kconfig=0A--- linux-2.6.23/drivers/net/wan/Kconfig=
> =092007-10-09 23:31:38.000000000 +0300=0A+++ linux-2.6.24/drivers/net/wan/K=
> config=092007-10-25 09:23:19.933170522 +0300=0A@@ -494,4 +494,15 @@ config =
> SBNI_MULTILINE=0A =0A =09  If unsure, say N.=0A =0A+config RETINA=0A+=09tri=
> state "Retina support"=0A+=09depends on PCI=0A+=09help=0A+=09  Driver for R=

Its been mangled into "Content-Transfer-Encoding: quoted-printable" by 
your mailer.

Also, when you revise a patch, please note somehow that this is an 
updated version.  One convention is changing the subject line:

	[PATCH v3] wan: new driver retina

Otherwise I have an mbox full of seemingly-similar patches, all with the 
subject "wan: new driver retina"

	Jeff



^ permalink raw reply

* Re: [PATCH v4] FEC - fast ethernet controller for mpc52xx
From: Jeff Garzik @ 2007-10-25  9:29 UTC (permalink / raw)
  To: Domen Puncer; +Cc: Grant Likely, galak, linuxppc-dev, tnt, netdev
In-Reply-To: <20071019112700.GD27403@nd47.coderock.org>

Domen Puncer wrote:
> +static int mpc52xx_fec_alloc_rx_buffers(struct bcom_task *rxtsk)
> +{
> +	while (!bcom_queue_full(rxtsk)) {
> +		struct sk_buff *skb;
> +		struct bcom_fec_bd *bd;
> +
> +		skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE);
> +		if (skb == NULL)
> +			return -EAGAIN;
> +
> +		/* zero out the initial receive buffers to aid debugging */
> +		memset(skb->data, 0, FEC_RX_BUFFER_SIZE);
> +
> +		bd = (struct bcom_fec_bd *)bcom_prepare_next_buffer(rxtsk);
> +
> +		bd->status = FEC_RX_BUFFER_SIZE;
> +		bd->skb_pa = virt_to_phys(skb->data);
> +
> +		bcom_submit_next_buffer(rxtsk, skb);

use your platform's dma mapping functions, rather than virt_to_phys()

it might be the exact same implementation, inside the platform 
internals, but drivers should not be using this directly.


> +/* based on generic_adjust_link from fs_enet-main.c */
> +static void mpc52xx_fec_adjust_link(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	struct phy_device *phydev = priv->phydev;
> +	int new_state = 0;
> +
> +	if (phydev->link != PHY_DOWN) {
> +		if (phydev->duplex != priv->duplex) {
> +			struct mpc52xx_fec __iomem *fec = priv->fec;
> +			u32 rcntrl;
> +			u32 tcntrl;
> +
> +			new_state = 1;
> +			priv->duplex = phydev->duplex;
> +
> +			rcntrl = in_be32(&fec->r_cntrl);
> +			tcntrl = in_be32(&fec->x_cntrl);
> +
> +			rcntrl &= ~FEC_RCNTRL_DRT;
> +			tcntrl &= ~FEC_TCNTRL_FDEN;
> +			if (phydev->duplex == DUPLEX_FULL)
> +				tcntrl |= FEC_TCNTRL_FDEN;	/* FD enable */
> +			else
> +				rcntrl |= FEC_RCNTRL_DRT;	/* disable Rx on Tx (HD) */
> +
> +			out_be32(&fec->r_cntrl, rcntrl);
> +			out_be32(&fec->x_cntrl, tcntrl);
> +		}
> +
> +		if (phydev->speed != priv->speed) {
> +			new_state = 1;
> +			priv->speed = phydev->speed;
> +		}
> +
> +		if (priv->link == PHY_DOWN) {
> +			new_state = 1;
> +			priv->link = phydev->link;
> +			netif_schedule(dev);
> +			netif_carrier_on(dev);
> +			netif_start_queue(dev);
> +		}
> +
> +	} else if (priv->link) {
> +		new_state = 1;
> +		priv->link = PHY_DOWN;
> +		priv->speed = 0;
> +		priv->duplex = -1;
> +		netif_stop_queue(dev);
> +		netif_carrier_off(dev);
> +	}
> +
> +	if (new_state && netif_msg_link(priv))
> +		phy_print_status(phydev);
> +}
> +
> +static int mpc52xx_fec_init_phy(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	struct phy_device *phydev;
> +	char phy_id[BUS_ID_SIZE];
> +
> +	snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT,
> +			(unsigned int)dev->base_addr, priv->phy_addr);
> +
> +	priv->link = PHY_DOWN;
> +	priv->speed = 0;
> +	priv->duplex = -1;
> +
> +	phydev = phy_connect(dev, phy_id, &mpc52xx_fec_adjust_link, 0, PHY_INTERFACE_MODE_MII);
> +	if (IS_ERR(phydev)) {
> +		dev_err(&dev->dev, "phy_connect failed\n");
> +		return PTR_ERR(phydev);
> +	}
> +	dev_info(&dev->dev, "attached phy %i to driver %s\n",
> +			phydev->addr, phydev->drv->name);
> +
> +	priv->phydev = phydev;
> +
> +	return 0;
> +}
> +
> +static int mpc52xx_fec_phy_start(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	int err;
> +
> +	if (!priv->has_phy)
> +		return 0;
> +
> +	err = mpc52xx_fec_init_phy(dev);
> +	if (err) {
> +		dev_err(&dev->dev, "mpc52xx_fec_init_phy failed\n");
> +		return err;
> +	}
> +
> +	/* reset phy - this also wakes it from PDOWN */
> +	phy_write(priv->phydev, MII_BMCR, BMCR_RESET);
> +	phy_start(priv->phydev);
> +
> +	return 0;
> +}
> +
> +static void mpc52xx_fec_phy_stop(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +
> +	if (!priv->has_phy)
> +		return;
> +
> +	phy_disconnect(priv->phydev);
> +	/* power down phy */
> +	phy_stop(priv->phydev);
> +	phy_write(priv->phydev, MII_BMCR, BMCR_PDOWN);
> +}
> +
> +static int mpc52xx_fec_phy_mii_ioctl(struct mpc52xx_fec_priv *priv,
> +		struct mii_ioctl_data *mii_data, int cmd)
> +{
> +	if (!priv->has_phy)
> +		return -ENOTSUPP;
> +
> +	return phy_mii_ioctl(priv->phydev, mii_data, cmd);
> +}
> +
> +static void mpc52xx_fec_phy_hw_init(struct mpc52xx_fec_priv *priv)
> +{
> +	struct mpc52xx_fec __iomem *fec = priv->fec;
> +
> +	if (!priv->has_phy)
> +		return;
> +
> +	out_be32(&fec->mii_speed, priv->phy_speed);
> +}
> +
> +static int mpc52xx_fec_open(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	int err = -EBUSY;
> +
> +	if (request_irq(dev->irq, &mpc52xx_fec_interrupt, IRQF_DISABLED | IRQF_SHARED,

why IRQF_DISABLED?  that should not be needed.


> +	                DRIVER_NAME "_ctrl", dev)) {
> +		dev_err(&dev->dev, "ctrl interrupt request failed\n");
> +		goto out;
> +	}
> +	if (request_irq(priv->r_irq, &mpc52xx_fec_rx_interrupt, IRQF_DISABLED,

ditto

> +	                DRIVER_NAME "_rx", dev)) {
> +		dev_err(&dev->dev, "rx interrupt request failed\n");
> +		goto free_ctrl_irq;
> +	}
> +	if (request_irq(priv->t_irq, &mpc52xx_fec_tx_interrupt, IRQF_DISABLED,

ditto


> +	                DRIVER_NAME "_tx", dev)) {
> +		dev_err(&dev->dev, "tx interrupt request failed\n");
> +		goto free_2irqs;
> +	}
> +
> +	bcom_fec_rx_reset(priv->rx_dmatsk);
> +	bcom_fec_tx_reset(priv->tx_dmatsk);
> +
> +	err = mpc52xx_fec_alloc_rx_buffers(priv->rx_dmatsk);
> +	if (err) {
> +		dev_err(&dev->dev, "mpc52xx_fec_alloc_rx_buffers failed\n");
> +		goto free_irqs;
> +	}
> +
> +	err = mpc52xx_fec_phy_start(dev);
> +	if (err)
> +		goto free_skbs;
> +
> +	bcom_enable(priv->rx_dmatsk);
> +	bcom_enable(priv->tx_dmatsk);
> +
> +	mpc52xx_fec_start(dev);
> +
> +	netif_start_queue(dev);
> +
> +	return 0;
> +
> + free_skbs:
> +	mpc52xx_fec_free_rx_buffers(priv->rx_dmatsk);
> +
> + free_irqs:
> +	free_irq(priv->t_irq, dev);
> + free_2irqs:
> +	free_irq(priv->r_irq, dev);
> + free_ctrl_irq:
> +	free_irq(dev->irq, dev);
> + out:
> +
> +	return err;
> +}
> +
> +static int mpc52xx_fec_close(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +
> +	netif_stop_queue(dev);
> +
> +	mpc52xx_fec_stop(dev);
> +
> +	mpc52xx_fec_free_rx_buffers(priv->rx_dmatsk);
> +
> +	free_irq(dev->irq, dev);
> +	free_irq(priv->r_irq, dev);
> +	free_irq(priv->t_irq, dev);
> +
> +	mpc52xx_fec_phy_stop(dev);
> +
> +	return 0;
> +}
> +
> +/* This will only be invoked if your driver is _not_ in XOFF state.
> + * What this means is that you need not check it, and that this
> + * invariant will hold if you make sure that the netif_*_queue()
> + * calls are done at the proper times.
> + */
> +static int mpc52xx_fec_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	struct bcom_fec_bd *bd;
> +
> +	if (bcom_queue_full(priv->tx_dmatsk)) {
> +		if (net_ratelimit())
> +			dev_err(&dev->dev, "transmit queue overrun\n");
> +		return 1;
> +	}
> +
> +	spin_lock_irq(&priv->lock);
> +	dev->trans_start = jiffies;
> +
> +	bd = (struct bcom_fec_bd *)
> +		bcom_prepare_next_buffer(priv->tx_dmatsk);
> +
> +	bd->status = skb->len | BCOM_FEC_TX_BD_TFD | BCOM_FEC_TX_BD_TC;
> +	bd->skb_pa = virt_to_phys(skb->data);

use dma_xxx

> +	bcom_submit_next_buffer(priv->tx_dmatsk, skb);
> +
> +	if (bcom_queue_full(priv->tx_dmatsk)) {
> +		priv->tx_full = 1;

no need for your own tx_full variable


> +		netif_stop_queue(dev);
> +	}
> +
> +	spin_unlock_irq(&priv->lock);
> +
> +	return 0;
> +}
> +
> +/* This handles BestComm transmit task interrupts
> + */
> +static irqreturn_t mpc52xx_fec_tx_interrupt(int irq, void *dev_id)
> +{
> +	struct net_device *dev = dev_id;
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +
> +	spin_lock(&priv->lock);
> +
> +	while (bcom_buffer_done(priv->tx_dmatsk)) {
> +		struct sk_buff *skb;
> +		skb = bcom_retrieve_buffer(priv->tx_dmatsk, NULL, NULL);
> +
> +		priv->tx_full = 0;
> +		dev_kfree_skb_irq(skb);
> +	}
> +
> +	if (netif_queue_stopped(dev) && !priv->tx_full)

no need to test netif_queue_stopped(), netif_wake_queue() does that anyway

no need for tx_full



> +		netif_wake_queue(dev);
> +
> +	spin_unlock(&priv->lock);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mpc52xx_fec_rx_interrupt(int irq, void *dev_id)
> +{
> +	struct net_device *dev = dev_id;
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +
> +	while (bcom_buffer_done(priv->rx_dmatsk)) {
> +		struct sk_buff *skb;
> +		struct sk_buff *rskb;
> +		struct bcom_fec_bd *bd;
> +		u32 status;
> +
> +		rskb = bcom_retrieve_buffer(priv->rx_dmatsk, &status, NULL);
> +
> +		/* Test for errors in received frame */
> +		if (status & BCOM_FEC_RX_BD_ERRORS) {
> +			/* Drop packet and reuse the buffer */
> +			bd = (struct bcom_fec_bd *)
> +				bcom_prepare_next_buffer(priv->rx_dmatsk);
> +
> +			bd->status = FEC_RX_BUFFER_SIZE;
> +			bd->skb_pa = virt_to_phys(rskb->data);
> +
> +			bcom_submit_next_buffer(priv->rx_dmatsk, rskb);
> +
> +			priv->stats.rx_dropped++;
> +
> +			continue;
> +		}
> +
> +		/* skbs are allocated on open, so now we allocate a new one,
> +		 * and remove the old (with the packet) */
> +		skb = dev_alloc_skb(FEC_RX_BUFFER_SIZE);
> +		if (skb) {
> +			/* Process the received skb */
> +			int length = status & BCOM_FEC_RX_BD_LEN_MASK;
> +
> +			skb_put(rskb, length - 4);	/* length without CRC32 */
> +
> +			rskb->dev = dev;
> +			rskb->protocol = eth_type_trans(rskb, dev);
> +
> +			netif_rx(rskb);
> +			dev->last_rx = jiffies;
> +		} else {
> +			/* Can't get a new one : reuse the same & drop pkt */
> +			dev_notice(&dev->dev, "Memory squeeze, dropping packet.\n");
> +			priv->stats.rx_dropped++;
> +
> +			skb = rskb;
> +		}
> +
> +		bd = (struct bcom_fec_bd *)
> +			bcom_prepare_next_buffer(priv->rx_dmatsk);
> +
> +		bd->status = FEC_RX_BUFFER_SIZE;
> +		bd->skb_pa = virt_to_phys(skb->data);
> +
> +		bcom_submit_next_buffer(priv->rx_dmatsk, skb);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t mpc52xx_fec_interrupt(int irq, void *dev_id)
> +{
> +	struct net_device *dev = dev_id;
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	struct mpc52xx_fec __iomem *fec = priv->fec;
> +	u32 ievent;
> +
> +	ievent = in_be32(&fec->ievent);

generally wise to check for 0xffffffff, which often indicates hardware 
fault / device not there / scrogged


> +	ievent &= ~FEC_IEVENT_MII;	/* mii is handled separately */
> +	if (!ievent)



> +		return IRQ_NONE;
> +
> +	out_be32(&fec->ievent, ievent);		/* clear pending events */
> +
> +	if (ievent & ~(FEC_IEVENT_RFIFO_ERROR | FEC_IEVENT_XFIFO_ERROR)) {
> +		if (ievent & ~FEC_IEVENT_TFINT)
> +			dev_dbg(&dev->dev, "ievent: %08x\n", ievent);
> +		return IRQ_HANDLED;
> +	}
> +
> +	if (net_ratelimit() && (ievent & FEC_IEVENT_RFIFO_ERROR))
> +		dev_warn(&dev->dev, "FEC_IEVENT_RFIFO_ERROR\n");
> +	if (net_ratelimit() && (ievent & FEC_IEVENT_XFIFO_ERROR))
> +		dev_warn(&dev->dev, "FEC_IEVENT_XFIFO_ERROR\n");
> +
> +	mpc52xx_fec_reset(dev);
> +
> +	netif_wake_queue(dev);
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * Get the current statistics.
> + * This may be called with the card open or closed.
> + */
> +static struct net_device_stats *mpc52xx_fec_get_stats(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	struct net_device_stats *stats = &priv->stats;
> +	struct mpc52xx_fec __iomem *fec = priv->fec;
> +
> +	stats->rx_bytes = in_be32(&fec->rmon_r_octets);
> +	stats->rx_packets = in_be32(&fec->rmon_r_packets);
> +	stats->rx_errors = in_be32(&fec->rmon_r_crc_align) +
> +		in_be32(&fec->rmon_r_undersize) +
> +		in_be32(&fec->rmon_r_oversize) +
> +		in_be32(&fec->rmon_r_frag) +
> +		in_be32(&fec->rmon_r_jab);
> +
> +	stats->tx_bytes = in_be32(&fec->rmon_t_octets);
> +	stats->tx_packets = in_be32(&fec->rmon_t_packets);
> +	stats->tx_errors = in_be32(&fec->rmon_t_crc_align) +
> +		in_be32(&fec->rmon_t_undersize) +
> +		in_be32(&fec->rmon_t_oversize) +
> +		in_be32(&fec->rmon_t_frag) +
> +		in_be32(&fec->rmon_t_jab);
> +
> +	stats->multicast = in_be32(&fec->rmon_r_mc_pkt);
> +	stats->collisions = in_be32(&fec->rmon_t_col);
> +
> +	/* detailed rx_errors: */
> +	stats->rx_length_errors = in_be32(&fec->rmon_r_undersize)
> +					+ in_be32(&fec->rmon_r_oversize)
> +					+ in_be32(&fec->rmon_r_frag)
> +					+ in_be32(&fec->rmon_r_jab);
> +	stats->rx_over_errors = in_be32(&fec->r_macerr);
> +	stats->rx_crc_errors = in_be32(&fec->ieee_r_crc);
> +	stats->rx_frame_errors = in_be32(&fec->ieee_r_align);
> +	stats->rx_fifo_errors = in_be32(&fec->rmon_r_drop);
> +	stats->rx_missed_errors = in_be32(&fec->rmon_r_drop);
> +
> +	/* detailed tx_errors: */
> +	stats->tx_aborted_errors = 0;
> +	stats->tx_carrier_errors = in_be32(&fec->ieee_t_cserr);
> +	stats->tx_fifo_errors = in_be32(&fec->rmon_t_drop);
> +	stats->tx_heartbeat_errors = in_be32(&fec->ieee_t_sqe);
> +	stats->tx_window_errors = in_be32(&fec->ieee_t_lcol);
> +
> +	return stats;
> +}
> +
> +/*
> + * Read MIB counters in order to reset them,
> + * then zero all the stats fields in memory
> + */
> +static void mpc52xx_fec_reset_stats(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	struct mpc52xx_fec __iomem *fec = priv->fec;
> +
> +	out_be32(&fec->mib_control, FEC_MIB_DISABLE);
> +	memset_io(&fec->rmon_t_drop, 0,	(__force u32)&fec->reserved10 -
> +			(__force u32)&fec->rmon_t_drop);
> +	out_be32(&fec->mib_control, 0);
> +
> +	memset(&priv->stats, 0, sizeof(priv->stats));

don't use your own copy of net_device_stats, it's in net_device now


> +/*
> + * Set or clear the multicast filter for this adaptor.
> + */
> +static void mpc52xx_fec_set_multicast_list(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	struct mpc52xx_fec __iomem *fec = priv->fec;
> +	u32 rx_control;
> +
> +	rx_control = in_be32(&fec->r_cntrl);
> +
> +	if (dev->flags & IFF_PROMISC) {
> +		rx_control |= FEC_RCNTRL_PROM;
> +		out_be32(&fec->r_cntrl, rx_control);
> +	} else {
> +		rx_control &= ~FEC_RCNTRL_PROM;
> +		out_be32(&fec->r_cntrl, rx_control);
> +
> +		if (dev->flags & IFF_ALLMULTI) {
> +			out_be32(&fec->gaddr1, 0xffffffff);
> +			out_be32(&fec->gaddr2, 0xffffffff);
> +		} else {
> +			u32 crc;
> +			int i;
> +			struct dev_mc_list *dmi;
> +			u32 gaddr1 = 0x00000000;
> +			u32 gaddr2 = 0x00000000;
> +
> +			dmi = dev->mc_list;
> +			for (i=0; i<dev->mc_count; i++) {
> +				crc = ether_crc_le(6, dmi->dmi_addr) >> 26;
> +				if (crc >= 32)
> +					gaddr1 |= 1 << (crc-32);
> +				else
> +					gaddr2 |= 1 << crc;
> +				dmi = dmi->next;
> +			}
> +			out_be32(&fec->gaddr1, gaddr1);
> +			out_be32(&fec->gaddr2, gaddr2);

fall back to ALLMULTI behavior if dev->mc_count is too large (for your 
chip's version of "too large")


> +/**
> + * mpc52xx_fec_hw_init
> + * @dev: network device
> + *
> + * Setup various hardware setting, only needed once on start
> + */
> +static void mpc52xx_fec_hw_init(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	struct mpc52xx_fec __iomem *fec = priv->fec;
> +	int i;
> +
> +	/* Whack a reset.  We should wait for this. */
> +	out_be32(&fec->ecntrl, FEC_ECNTRL_RESET);
> +	for (i = 0; i < FEC_RESET_DELAY; ++i) {
> +		if ((in_be32(&fec->ecntrl) & FEC_ECNTRL_RESET) == 0)
> +			break;
> +		udelay(1);
> +	}
> +	if (i == FEC_RESET_DELAY)
> +		dev_err(&dev->dev, "FEC Reset timeout!\n");
> +
> +	/* set pause to 0x20 frames */
> +	out_be32(&fec->op_pause, FEC_OP_PAUSE_OPCODE | 0x20);
> +
> +	/* high service request will be deasserted when there's < 7 bytes in fifo
> +	 * low service request will be deasserted when there's < 4*7 bytes in fifo
> +	 */
> +	out_be32(&fec->rfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7);
> +	out_be32(&fec->tfifo_cntrl, FEC_FIFO_CNTRL_FRAME | FEC_FIFO_CNTRL_LTG_7);
> +
> +	/* alarm when <= x bytes in FIFO */
> +	out_be32(&fec->rfifo_alarm, 0x0000030c);
> +	out_be32(&fec->tfifo_alarm, 0x00000100);
> +
> +	/* begin transmittion when 256 bytes are in FIFO (or EOF or FIFO full) */
> +	out_be32(&fec->x_wmrk, FEC_FIFO_WMRK_256B);
> +
> +	/* enable crc generation */
> +	out_be32(&fec->xmit_fsm, FEC_XMIT_FSM_APPEND_CRC | FEC_XMIT_FSM_ENABLE_CRC);
> +	out_be32(&fec->iaddr1, 0x00000000);	/* No individual filter */
> +	out_be32(&fec->iaddr2, 0x00000000);	/* No individual filter */
> +
> +	/* set phy speed.
> +	 * this can't be done in phy driver, since it needs to be called
> +	 * before fec stuff (even on resume) */
> +	mpc52xx_fec_phy_hw_init(priv);
> +}
> +
> +/**
> + * mpc52xx_fec_start
> + * @dev: network device
> + *
> + * This function is called to start or restart the FEC during a link
> + * change.  This happens on fifo errors or when switching between half
> + * and full duplex.
> + */
> +static void mpc52xx_fec_start(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	struct mpc52xx_fec __iomem *fec = priv->fec;
> +	u32 rcntrl;
> +	u32 tcntrl;
> +	u32 tmp;
> +
> +	/* clear sticky error bits */
> +	tmp = FEC_FIFO_STATUS_ERR | FEC_FIFO_STATUS_UF | FEC_FIFO_STATUS_OF;
> +	out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status) & tmp);
> +	out_be32(&fec->tfifo_status, in_be32(&fec->tfifo_status) & tmp);
> +
> +	/* FIFOs will reset on mpc52xx_fec_enable */
> +	out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_ENABLE_IS_RESET);
> +
> +	/* Set station address. */
> +	mpc52xx_fec_set_paddr(dev, dev->dev_addr);
> +
> +	mpc52xx_fec_set_multicast_list(dev);
> +
> +	/* set max frame len, enable flow control, select mii mode */
> +	rcntrl = FEC_RX_BUFFER_SIZE << 16;	/* max frame length */
> +	rcntrl |= FEC_RCNTRL_FCE;
> +
> +	if (priv->has_phy)
> +		rcntrl |= FEC_RCNTRL_MII_MODE;
> +
> +	if (priv->duplex == DUPLEX_FULL)
> +		tcntrl = FEC_TCNTRL_FDEN;	/* FD enable */
> +	else {
> +		rcntrl |= FEC_RCNTRL_DRT;	/* disable Rx on Tx (HD) */
> +		tcntrl = 0;
> +	}
> +	out_be32(&fec->r_cntrl, rcntrl);
> +	out_be32(&fec->x_cntrl, tcntrl);
> +
> +	/* Clear any outstanding interrupt. */
> +	out_be32(&fec->ievent, 0xffffffff);
> +
> +	/* Enable interrupts we wish to service. */
> +	out_be32(&fec->imask, FEC_IMASK_ENABLE);
> +
> +	/* And last, enable the transmit and receive processing. */
> +	out_be32(&fec->ecntrl, FEC_ECNTRL_ETHER_EN);
> +	out_be32(&fec->r_des_active, 0x01000000);
> +
> +	priv->tx_full = 0;
> +}
> +
> +/**
> + * mpc52xx_fec_stop
> + * @dev: network device
> + *
> + * stop all activity on fec and empty dma buffers
> + */
> +static void mpc52xx_fec_stop(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	struct mpc52xx_fec __iomem *fec = priv->fec;
> +	unsigned long timeout;
> +
> +	/* disable all interrupts */
> +	out_be32(&fec->imask, 0);
> +
> +	/* Disable the rx task. */
> +	bcom_disable(priv->rx_dmatsk);
> +
> +	/* Wait for tx queue to drain, but only if we're in process context */
> +	if (!in_interrupt()) {
> +		timeout = jiffies + msecs_to_jiffies(2000);
> +		while (time_before(jiffies, timeout) &&
> +				!bcom_queue_empty(priv->tx_dmatsk))
> +			msleep(100);
> +
> +		if (time_after_eq(jiffies, timeout))
> +			dev_err(&dev->dev, "queues didn't drain\n");
> +#if 1
> +		if (time_after_eq(jiffies, timeout)) {
> +			dev_err(&dev->dev, "  tx: index: %i, outdex: %i\n",
> +					priv->tx_dmatsk->index,
> +					priv->tx_dmatsk->outdex);
> +			dev_err(&dev->dev, "  rx: index: %i, outdex: %i\n",
> +					priv->rx_dmatsk->index,
> +					priv->rx_dmatsk->outdex);
> +		}
> +#endif
> +	}
> +
> +	bcom_disable(priv->tx_dmatsk);
> +
> +	/* Stop FEC */
> +	out_be32(&fec->ecntrl, in_be32(&fec->ecntrl) & ~FEC_ECNTRL_ETHER_EN);
> +
> +	return;
> +}
> +
> +/* reset fec and bestcomm tasks */
> +static void mpc52xx_fec_reset(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	struct mpc52xx_fec __iomem *fec = priv->fec;
> +
> +	mpc52xx_fec_stop(dev);
> +
> +	out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status));
> +	out_be32(&fec->reset_cntrl, FEC_RESET_CNTRL_RESET_FIFO);
> +
> +	mpc52xx_fec_free_rx_buffers(priv->rx_dmatsk);
> +
> +	mpc52xx_fec_hw_init(dev);
> +
> +	phy_stop(priv->phydev);
> +	phy_write(priv->phydev, MII_BMCR, BMCR_RESET);
> +	phy_start(priv->phydev);
> +
> +	bcom_fec_rx_reset(priv->rx_dmatsk);
> +	bcom_fec_tx_reset(priv->tx_dmatsk);
> +
> +	mpc52xx_fec_alloc_rx_buffers(priv->rx_dmatsk);
> +
> +	bcom_enable(priv->rx_dmatsk);
> +	bcom_enable(priv->tx_dmatsk);
> +
> +	mpc52xx_fec_start(dev);
> +}
> +
> +
> +/* ethtool interface */
> +static void mpc52xx_fec_get_drvinfo(struct net_device *dev,
> +		struct ethtool_drvinfo *info)
> +{
> +	strcpy(info->driver, DRIVER_NAME);

version?  anything else?


> +static int mpc52xx_fec_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	return phy_ethtool_gset(priv->phydev, cmd);
> +}
> +
> +static int mpc52xx_fec_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	return phy_ethtool_sset(priv->phydev, cmd);
> +}
> +
> +static u32 mpc52xx_fec_get_msglevel(struct net_device *dev)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	return priv->msg_enable;
> +}
> +
> +static void mpc52xx_fec_set_msglevel(struct net_device *dev, u32 level)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +	priv->msg_enable = level;
> +}
> +
> +static const struct ethtool_ops mpc52xx_fec_ethtool_ops = {
> +	.get_drvinfo = mpc52xx_fec_get_drvinfo,
> +	.get_settings = mpc52xx_fec_get_settings,
> +	.set_settings = mpc52xx_fec_set_settings,
> +	.get_link = ethtool_op_get_link,
> +	.get_msglevel = mpc52xx_fec_get_msglevel,
> +	.set_msglevel = mpc52xx_fec_set_msglevel,
> +};
> +
> +
> +static int mpc52xx_fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
> +{
> +	struct mpc52xx_fec_priv *priv = netdev_priv(dev);
> +
> +	return mpc52xx_fec_phy_mii_ioctl(priv, if_mii(rq), cmd);
> +}
> +
> +/* ======================================================================== */
> +/* OF Driver                                                                */
> +/* ======================================================================== */
> +
> +static int __devinit
> +mpc52xx_fec_probe(struct of_device *op, const struct of_device_id *match)
> +{
> +	int rv;
> +	struct net_device *ndev;
> +	struct mpc52xx_fec_priv *priv = NULL;
> +	struct resource mem;
> +	const phandle *ph;
> +
> +	phys_addr_t rx_fifo;
> +	phys_addr_t tx_fifo;
> +
> +	/* Get the ether ndev & it's private zone */
> +	ndev = alloc_etherdev(sizeof(struct mpc52xx_fec_priv));
> +	if (!ndev)
> +		return -ENOMEM;
> +
> +	priv = netdev_priv(ndev);
> +
> +	/* Reserve FEC control zone */
> +	rv = of_address_to_resource(op->node, 0, &mem);
> +	if (rv) {
> +		printk(KERN_ERR DRIVER_NAME ": "
> +				"Error while parsing device node resource\n" );
> +		return rv;
> +	}
> +	if ((mem.end - mem.start + 1) != sizeof(struct mpc52xx_fec)) {
> +		printk(KERN_ERR DRIVER_NAME
> +			" - invalid resource size (%lx != %x), check mpc52xx_devices.c\n",
> +			(unsigned long)(mem.end - mem.start + 1), sizeof(struct mpc52xx_fec));
> +		return -EINVAL;
> +	}
> +
> +	if (!request_mem_region(mem.start, sizeof(struct mpc52xx_fec), DRIVER_NAME))
> +		return -EBUSY;
> +
> +	/* Init ether ndev with what we have */
> +	ndev->open		= mpc52xx_fec_open;
> +	ndev->stop		= mpc52xx_fec_close;
> +	ndev->hard_start_xmit	= mpc52xx_fec_hard_start_xmit;
> +	ndev->do_ioctl		= mpc52xx_fec_ioctl;
> +	ndev->ethtool_ops	= &mpc52xx_fec_ethtool_ops;
> +	ndev->get_stats		= mpc52xx_fec_get_stats;
> +	ndev->set_mac_address	= mpc52xx_fec_set_mac_address;
> +	ndev->set_multicast_list = mpc52xx_fec_set_multicast_list;
> +	ndev->tx_timeout	= mpc52xx_fec_tx_timeout;
> +	ndev->watchdog_timeo	= FEC_WATCHDOG_TIMEOUT;
> +	ndev->flags &= ~IFF_RUNNING;

delete this, no reason to ever touch IFF_RUNNING yourself

> +	ndev->base_addr		= mem.start;

> +	priv->t_irq = priv->r_irq = ndev->irq = NO_IRQ; /* IRQ are free for now */
> +
> +	spin_lock_init(&priv->lock);
> +
> +	/* ioremap the zones */
> +	priv->fec = ioremap(mem.start, sizeof(struct mpc52xx_fec));
> +
> +	if (!priv->fec) {
> +		rv = -ENOMEM;
> +		goto probe_error;
> +	}
> +
> +	/* Bestcomm init */
> +	rx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, rfifo_data);
> +	tx_fifo = ndev->base_addr + offsetof(struct mpc52xx_fec, tfifo_data);
> +
> +	priv->rx_dmatsk = bcom_fec_rx_init(FEC_RX_NUM_BD, rx_fifo, FEC_RX_BUFFER_SIZE);
> +	priv->tx_dmatsk = bcom_fec_tx_init(FEC_TX_NUM_BD, tx_fifo);
> +
> +	if (!priv->rx_dmatsk || !priv->tx_dmatsk) {
> +		printk(KERN_ERR DRIVER_NAME ": Can not init SDMA tasks\n" );
> +		rv = -ENOMEM;
> +		goto probe_error;
> +	}
> +
> +	/* Get the IRQ we need one by one */
> +		/* Control */
> +	ndev->irq = irq_of_parse_and_map(op->node, 0);
> +
> +		/* RX */
> +	priv->r_irq = bcom_get_task_irq(priv->rx_dmatsk);
> +
> +		/* TX */
> +	priv->t_irq = bcom_get_task_irq(priv->tx_dmatsk);
> +
> +	/* MAC address init */
> +	if (!is_zero_ether_addr(mpc52xx_fec_mac_addr))
> +		memcpy(ndev->dev_addr, mpc52xx_fec_mac_addr, 6);
> +	else
> +		mpc52xx_fec_get_paddr(ndev, ndev->dev_addr);
> +
> +	priv->msg_enable = (NETIF_MSG_IFUP << 1) - 1;

netif_msg_init


> +	priv->duplex = DUPLEX_FULL;
> +
> +	/* is the phy present in device tree? */
> +	ph = of_get_property(op->node, "phy-handle", NULL);
> +	if (ph) {
> +		const unsigned int *prop;
> +		struct device_node *phy_dn;
> +		priv->has_phy = 1;
> +
> +		phy_dn = of_find_node_by_phandle(*ph);
> +		prop = of_get_property(phy_dn, "reg", NULL);
> +		priv->phy_addr = *prop;
> +
> +		of_node_put(phy_dn);
> +
> +		/* Phy speed */
> +		priv->phy_speed = ((mpc52xx_find_ipb_freq(op->node) >> 20) / 5) << 1;
> +	} else {
> +		dev_info(&ndev->dev, "can't find \"phy-handle\" in device"
> +				" tree, using 7-wire mode\n");
> +	}
> +
> +	/* Hardware init */
> +	mpc52xx_fec_hw_init(ndev);
> +
> +	mpc52xx_fec_reset_stats(ndev);
> +
> +	/* Register the new network device */
> +	rv = register_netdev(ndev);
> +	if (rv < 0)
> +		goto probe_error;
> +
> +	/* We're done ! */
> +	dev_set_drvdata(&op->dev, ndev);
> +
> +	return 0;
> +
> +
> +	/* Error handling - free everything that might be allocated */
> +probe_error:
> +
> +	irq_dispose_mapping(ndev->irq);
> +
> +	if (priv->rx_dmatsk)
> +		bcom_fec_rx_release(priv->rx_dmatsk);
> +	if (priv->tx_dmatsk)
> +		bcom_fec_tx_release(priv->tx_dmatsk);
> +
> +	if (priv->fec)
> +		iounmap(priv->fec);
> +
> +	release_mem_region(mem.start, sizeof(struct mpc52xx_fec));
> +
> +	free_netdev(ndev);
> +
> +	return rv;
> +}
> +
> +static int
> +mpc52xx_fec_remove(struct of_device *op)
> +{
> +	struct net_device *ndev;
> +	struct mpc52xx_fec_priv *priv;
> +
> +	ndev = dev_get_drvdata(&op->dev);
> +	if (!ndev)
> +		return 0;

testing for impossible condition


> +	priv = netdev_priv(ndev);
> +
> +	unregister_netdev(ndev);
> +
> +	irq_dispose_mapping(ndev->irq);
> +
> +	bcom_fec_rx_release(priv->rx_dmatsk);
> +	bcom_fec_tx_release(priv->tx_dmatsk);
> +
> +	iounmap(priv->fec);
> +
> +	release_mem_region(ndev->base_addr, sizeof(struct mpc52xx_fec));
> +
> +	free_netdev(ndev);
> +
> +	dev_set_drvdata(&op->dev, NULL);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int mpc52xx_fec_of_suspend(struct of_device *op, pm_message_t state)
> +{
> +	struct net_device *dev = dev_get_drvdata(&op->dev);
> +
> +	if (netif_running(dev))
> +		mpc52xx_fec_close(dev);
> +
> +	return 0;
> +}
> +
> +static int mpc52xx_fec_of_resume(struct of_device *op)
> +{
> +	struct net_device *dev = dev_get_drvdata(&op->dev);
> +
> +	mpc52xx_fec_hw_init(dev);
> +	mpc52xx_fec_reset_stats(dev);
> +
> +	if (netif_running(dev))
> +		mpc52xx_fec_open(dev);
> +
> +	return 0;
> +}
> +#endif
> +
> +static struct of_device_id mpc52xx_fec_match[] = {
> +	{
> +		.type		= "network",
> +		.compatible	= "mpc5200-fec",
> +	},
> +	{ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, mpc52xx_fec_match);
> +
> +static struct of_platform_driver mpc52xx_fec_driver = {
> +	.owner		= THIS_MODULE,
> +	.name		= DRIVER_NAME,
> +	.match_table	= mpc52xx_fec_match,
> +	.probe		= mpc52xx_fec_probe,
> +	.remove		= mpc52xx_fec_remove,
> +#ifdef CONFIG_PM
> +	.suspend	= mpc52xx_fec_of_suspend,
> +	.resume		= mpc52xx_fec_of_resume,
> +#endif
> +};
> +
> +
> +/* ======================================================================== */
> +/* Module                                                                   */
> +/* ======================================================================== */
> +
> +static int __init
> +mpc52xx_fec_init(void)
> +{
> +#ifdef CONFIG_FEC_MPC52xx_MDIO
> +	int ret;
> +	ret = of_register_platform_driver(&mpc52xx_fec_mdio_driver);
> +	if (ret) {
> +		printk(KERN_ERR DRIVER_NAME ": failed to register mdio driver\n");
> +		return ret;
> +	}
> +#endif
> +	return of_register_platform_driver(&mpc52xx_fec_driver);
> +}
> +
> +static void __exit
> +mpc52xx_fec_exit(void)
> +{
> +	of_unregister_platform_driver(&mpc52xx_fec_driver);
> +#ifdef CONFIG_FEC_MPC52xx_MDIO
> +	of_unregister_platform_driver(&mpc52xx_fec_mdio_driver);
> +#endif
> +}
> +
> +
> +module_init(mpc52xx_fec_init);
> +module_exit(mpc52xx_fec_exit);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Dale Farnsworth");
> +MODULE_DESCRIPTION("Ethernet driver for the Freescale MPC52xx FEC");
> Index: linux.git/drivers/net/fec_mpc52xx.h
> ===================================================================
> --- /dev/null
> +++ linux.git/drivers/net/fec_mpc52xx.h
> @@ -0,0 +1,315 @@
> +/*
> + * drivers/drivers/net/fec_mpc52xx/fec.h
> + *
> + * Driver for the MPC5200 Fast Ethernet Controller
> + *
> + * Author: Dale Farnsworth <dfarnsworth@mvista.com>
> + *
> + * 2003-2004 (c) MontaVista, Software, Inc.  This file is licensed under
> + * the terms of the GNU General Public License version 2.  This program
> + * is licensed "as is" without any warranty of any kind, whether express
> + * or implied.
> + */
> +
> +#ifndef __DRIVERS_NET_MPC52XX_FEC_H__
> +#define __DRIVERS_NET_MPC52XX_FEC_H__
> +
> +#include <linux/phy.h>
> +
> +/* Tunable constant */
> +/* FEC_RX_BUFFER_SIZE includes 4 bytes for CRC32 */
> +#define FEC_RX_BUFFER_SIZE	1522	/* max receive packet size */
> +#define FEC_RX_NUM_BD		256
> +#define FEC_TX_NUM_BD		64
> +
> +#define FEC_RESET_DELAY		50 	/* uS */
> +
> +#define FEC_WATCHDOG_TIMEOUT	((400*HZ)/1000)
> +
> +struct mpc52xx_fec_priv {
> +	int duplex;
> +	int tx_full;
> +	int r_irq;
> +	int t_irq;
> +	struct mpc52xx_fec __iomem *fec;
> +	struct bcom_task *rx_dmatsk;
> +	struct bcom_task *tx_dmatsk;
> +	spinlock_t lock;
> +	struct net_device_stats stats;
> +	int msg_enable;
> +
> +	int has_phy;
> +	unsigned int phy_speed;
> +	unsigned int phy_addr;
> +	struct phy_device *phydev;
> +	enum phy_state link;
> +	int speed;
> +};
> +
> +
> +/* ======================================================================== */
> +/* Hardware register sets & bits                                            */
> +/* ======================================================================== */
> +
> +struct mpc52xx_fec {
> +	u32 fec_id;			/* FEC + 0x000 */
> +	u32 ievent;			/* FEC + 0x004 */
> +	u32 imask;			/* FEC + 0x008 */
> +
> +	u32 reserved0[1];		/* FEC + 0x00C */
> +	u32 r_des_active;		/* FEC + 0x010 */
> +	u32 x_des_active;		/* FEC + 0x014 */
> +	u32 r_des_active_cl;		/* FEC + 0x018 */
> +	u32 x_des_active_cl;		/* FEC + 0x01C */
> +	u32 ivent_set;			/* FEC + 0x020 */
> +	u32 ecntrl;			/* FEC + 0x024 */
> +
> +	u32 reserved1[6];		/* FEC + 0x028-03C */
> +	u32 mii_data;			/* FEC + 0x040 */
> +	u32 mii_speed;			/* FEC + 0x044 */
> +	u32 mii_status;			/* FEC + 0x048 */
> +
> +	u32 reserved2[5];		/* FEC + 0x04C-05C */
> +	u32 mib_data;			/* FEC + 0x060 */
> +	u32 mib_control;		/* FEC + 0x064 */
> +
> +	u32 reserved3[6];		/* FEC + 0x068-7C */
> +	u32 r_activate;			/* FEC + 0x080 */
> +	u32 r_cntrl;			/* FEC + 0x084 */
> +	u32 r_hash;			/* FEC + 0x088 */
> +	u32 r_data;			/* FEC + 0x08C */
> +	u32 ar_done;			/* FEC + 0x090 */
> +	u32 r_test;			/* FEC + 0x094 */
> +	u32 r_mib;			/* FEC + 0x098 */
> +	u32 r_da_low;			/* FEC + 0x09C */
> +	u32 r_da_high;			/* FEC + 0x0A0 */
> +
> +	u32 reserved4[7];		/* FEC + 0x0A4-0BC */
> +	u32 x_activate;			/* FEC + 0x0C0 */
> +	u32 x_cntrl;			/* FEC + 0x0C4 */
> +	u32 backoff;			/* FEC + 0x0C8 */
> +	u32 x_data;			/* FEC + 0x0CC */
> +	u32 x_status;			/* FEC + 0x0D0 */
> +	u32 x_mib;			/* FEC + 0x0D4 */
> +	u32 x_test;			/* FEC + 0x0D8 */
> +	u32 fdxfc_da1;			/* FEC + 0x0DC */
> +	u32 fdxfc_da2;			/* FEC + 0x0E0 */
> +	u32 paddr1;			/* FEC + 0x0E4 */
> +	u32 paddr2;			/* FEC + 0x0E8 */
> +	u32 op_pause;			/* FEC + 0x0EC */
> +
> +	u32 reserved5[4];		/* FEC + 0x0F0-0FC */
> +	u32 instr_reg;			/* FEC + 0x100 */
> +	u32 context_reg;		/* FEC + 0x104 */
> +	u32 test_cntrl;			/* FEC + 0x108 */
> +	u32 acc_reg;			/* FEC + 0x10C */
> +	u32 ones;			/* FEC + 0x110 */
> +	u32 zeros;			/* FEC + 0x114 */
> +	u32 iaddr1;			/* FEC + 0x118 */
> +	u32 iaddr2;			/* FEC + 0x11C */
> +	u32 gaddr1;			/* FEC + 0x120 */
> +	u32 gaddr2;			/* FEC + 0x124 */
> +	u32 random;			/* FEC + 0x128 */
> +	u32 rand1;			/* FEC + 0x12C */
> +	u32 tmp;			/* FEC + 0x130 */
> +
> +	u32 reserved6[3];		/* FEC + 0x134-13C */
> +	u32 fifo_id;			/* FEC + 0x140 */
> +	u32 x_wmrk;			/* FEC + 0x144 */
> +	u32 fcntrl;			/* FEC + 0x148 */
> +	u32 r_bound;			/* FEC + 0x14C */
> +	u32 r_fstart;			/* FEC + 0x150 */
> +	u32 r_count;			/* FEC + 0x154 */
> +	u32 r_lag;			/* FEC + 0x158 */
> +	u32 r_read;			/* FEC + 0x15C */
> +	u32 r_write;			/* FEC + 0x160 */
> +	u32 x_count;			/* FEC + 0x164 */
> +	u32 x_lag;			/* FEC + 0x168 */
> +	u32 x_retry;			/* FEC + 0x16C */
> +	u32 x_write;			/* FEC + 0x170 */
> +	u32 x_read;			/* FEC + 0x174 */
> +
> +	u32 reserved7[2];		/* FEC + 0x178-17C */
> +	u32 fm_cntrl;			/* FEC + 0x180 */
> +	u32 rfifo_data;			/* FEC + 0x184 */
> +	u32 rfifo_status;		/* FEC + 0x188 */
> +	u32 rfifo_cntrl;		/* FEC + 0x18C */
> +	u32 rfifo_lrf_ptr;		/* FEC + 0x190 */
> +	u32 rfifo_lwf_ptr;		/* FEC + 0x194 */
> +	u32 rfifo_alarm;		/* FEC + 0x198 */
> +	u32 rfifo_rdptr;		/* FEC + 0x19C */
> +	u32 rfifo_wrptr;		/* FEC + 0x1A0 */
> +	u32 tfifo_data;			/* FEC + 0x1A4 */
> +	u32 tfifo_status;		/* FEC + 0x1A8 */
> +	u32 tfifo_cntrl;		/* FEC + 0x1AC */
> +	u32 tfifo_lrf_ptr;		/* FEC + 0x1B0 */
> +	u32 tfifo_lwf_ptr;		/* FEC + 0x1B4 */
> +	u32 tfifo_alarm;		/* FEC + 0x1B8 */
> +	u32 tfifo_rdptr;		/* FEC + 0x1BC */
> +	u32 tfifo_wrptr;		/* FEC + 0x1C0 */
> +
> +	u32 reset_cntrl;		/* FEC + 0x1C4 */
> +	u32 xmit_fsm;			/* FEC + 0x1C8 */
> +
> +	u32 reserved8[3];		/* FEC + 0x1CC-1D4 */
> +	u32 rdes_data0;			/* FEC + 0x1D8 */
> +	u32 rdes_data1;			/* FEC + 0x1DC */
> +	u32 r_length;			/* FEC + 0x1E0 */
> +	u32 x_length;			/* FEC + 0x1E4 */
> +	u32 x_addr;			/* FEC + 0x1E8 */
> +	u32 cdes_data;			/* FEC + 0x1EC */
> +	u32 status;			/* FEC + 0x1F0 */
> +	u32 dma_control;		/* FEC + 0x1F4 */
> +	u32 des_cmnd;			/* FEC + 0x1F8 */
> +	u32 data;			/* FEC + 0x1FC */
> +
> +	u32 rmon_t_drop;		/* FEC + 0x200 */
> +	u32 rmon_t_packets;		/* FEC + 0x204 */
> +	u32 rmon_t_bc_pkt;		/* FEC + 0x208 */
> +	u32 rmon_t_mc_pkt;		/* FEC + 0x20C */
> +	u32 rmon_t_crc_align;		/* FEC + 0x210 */
> +	u32 rmon_t_undersize;		/* FEC + 0x214 */
> +	u32 rmon_t_oversize;		/* FEC + 0x218 */
> +	u32 rmon_t_frag;		/* FEC + 0x21C */
> +	u32 rmon_t_jab;			/* FEC + 0x220 */
> +	u32 rmon_t_col;			/* FEC + 0x224 */
> +	u32 rmon_t_p64;			/* FEC + 0x228 */
> +	u32 rmon_t_p65to127;		/* FEC + 0x22C */
> +	u32 rmon_t_p128to255;		/* FEC + 0x230 */
> +	u32 rmon_t_p256to511;		/* FEC + 0x234 */
> +	u32 rmon_t_p512to1023;		/* FEC + 0x238 */
> +	u32 rmon_t_p1024to2047;		/* FEC + 0x23C */
> +	u32 rmon_t_p_gte2048;		/* FEC + 0x240 */
> +	u32 rmon_t_octets;		/* FEC + 0x244 */
> +	u32 ieee_t_drop;		/* FEC + 0x248 */
> +	u32 ieee_t_frame_ok;		/* FEC + 0x24C */
> +	u32 ieee_t_1col;		/* FEC + 0x250 */
> +	u32 ieee_t_mcol;		/* FEC + 0x254 */
> +	u32 ieee_t_def;			/* FEC + 0x258 */
> +	u32 ieee_t_lcol;		/* FEC + 0x25C */
> +	u32 ieee_t_excol;		/* FEC + 0x260 */
> +	u32 ieee_t_macerr;		/* FEC + 0x264 */
> +	u32 ieee_t_cserr;		/* FEC + 0x268 */
> +	u32 ieee_t_sqe;			/* FEC + 0x26C */
> +	u32 t_fdxfc;			/* FEC + 0x270 */
> +	u32 ieee_t_octets_ok;		/* FEC + 0x274 */
> +
> +	u32 reserved9[2];		/* FEC + 0x278-27C */
> +	u32 rmon_r_drop;		/* FEC + 0x280 */
> +	u32 rmon_r_packets;		/* FEC + 0x284 */
> +	u32 rmon_r_bc_pkt;		/* FEC + 0x288 */
> +	u32 rmon_r_mc_pkt;		/* FEC + 0x28C */
> +	u32 rmon_r_crc_align;		/* FEC + 0x290 */
> +	u32 rmon_r_undersize;		/* FEC + 0x294 */
> +	u32 rmon_r_oversize;		/* FEC + 0x298 */
> +	u32 rmon_r_frag;		/* FEC + 0x29C */
> +	u32 rmon_r_jab;			/* FEC + 0x2A0 */
> +
> +	u32 rmon_r_resvd_0;		/* FEC + 0x2A4 */
> +
> +	u32 rmon_r_p64;			/* FEC + 0x2A8 */
> +	u32 rmon_r_p65to127;		/* FEC + 0x2AC */
> +	u32 rmon_r_p128to255;		/* FEC + 0x2B0 */
> +	u32 rmon_r_p256to511;		/* FEC + 0x2B4 */
> +	u32 rmon_r_p512to1023;		/* FEC + 0x2B8 */
> +	u32 rmon_r_p1024to2047;		/* FEC + 0x2BC */
> +	u32 rmon_r_p_gte2048;		/* FEC + 0x2C0 */
> +	u32 rmon_r_octets;		/* FEC + 0x2C4 */
> +	u32 ieee_r_drop;		/* FEC + 0x2C8 */
> +	u32 ieee_r_frame_ok;		/* FEC + 0x2CC */
> +	u32 ieee_r_crc;			/* FEC + 0x2D0 */
> +	u32 ieee_r_align;		/* FEC + 0x2D4 */
> +	u32 r_macerr;			/* FEC + 0x2D8 */
> +	u32 r_fdxfc;			/* FEC + 0x2DC */
> +	u32 ieee_r_octets_ok;		/* FEC + 0x2E0 */
> +
> +	u32 reserved10[7];		/* FEC + 0x2E4-2FC */
> +
> +	u32 reserved11[64];		/* FEC + 0x300-3FF */
> +};
> +
> +#define	FEC_MIB_DISABLE			0x80000000
> +
> +#define	FEC_IEVENT_HBERR		0x80000000
> +#define	FEC_IEVENT_BABR			0x40000000
> +#define	FEC_IEVENT_BABT			0x20000000
> +#define	FEC_IEVENT_GRA			0x10000000
> +#define	FEC_IEVENT_TFINT		0x08000000
> +#define	FEC_IEVENT_MII			0x00800000
> +#define	FEC_IEVENT_LATE_COL		0x00200000
> +#define	FEC_IEVENT_COL_RETRY_LIM	0x00100000
> +#define	FEC_IEVENT_XFIFO_UN		0x00080000
> +#define	FEC_IEVENT_XFIFO_ERROR		0x00040000
> +#define	FEC_IEVENT_RFIFO_ERROR		0x00020000
> +
> +#define	FEC_IMASK_HBERR			0x80000000
> +#define	FEC_IMASK_BABR			0x40000000
> +#define	FEC_IMASK_BABT			0x20000000
> +#define	FEC_IMASK_GRA			0x10000000
> +#define	FEC_IMASK_MII			0x00800000
> +#define	FEC_IMASK_LATE_COL		0x00200000
> +#define	FEC_IMASK_COL_RETRY_LIM		0x00100000
> +#define	FEC_IMASK_XFIFO_UN		0x00080000
> +#define	FEC_IMASK_XFIFO_ERROR		0x00040000
> +#define	FEC_IMASK_RFIFO_ERROR		0x00020000
> +
> +/* all but MII, which is enabled separately */
> +#define FEC_IMASK_ENABLE	(FEC_IMASK_HBERR | FEC_IMASK_BABR | \
> +		FEC_IMASK_BABT | FEC_IMASK_GRA | FEC_IMASK_LATE_COL | \
> +		FEC_IMASK_COL_RETRY_LIM | FEC_IMASK_XFIFO_UN | \
> +		FEC_IMASK_XFIFO_ERROR | FEC_IMASK_RFIFO_ERROR)
> +
> +#define	FEC_RCNTRL_MAX_FL_SHIFT		16
> +#define	FEC_RCNTRL_LOOP			0x01
> +#define	FEC_RCNTRL_DRT			0x02
> +#define	FEC_RCNTRL_MII_MODE		0x04
> +#define	FEC_RCNTRL_PROM			0x08
> +#define	FEC_RCNTRL_BC_REJ		0x10
> +#define	FEC_RCNTRL_FCE			0x20
> +
> +#define	FEC_TCNTRL_GTS			0x00000001
> +#define	FEC_TCNTRL_HBC			0x00000002
> +#define	FEC_TCNTRL_FDEN			0x00000004
> +#define	FEC_TCNTRL_TFC_PAUSE		0x00000008
> +#define	FEC_TCNTRL_RFC_PAUSE		0x00000010
> +
> +#define	FEC_ECNTRL_RESET		0x00000001
> +#define	FEC_ECNTRL_ETHER_EN		0x00000002
> +
> +#define FEC_MII_DATA_ST			0x40000000	/* Start frame */
> +#define FEC_MII_DATA_OP_RD		0x20000000	/* Perform read */
> +#define FEC_MII_DATA_OP_WR		0x10000000	/* Perform write */
> +#define FEC_MII_DATA_PA_MSK		0x0f800000	/* PHY Address mask */
> +#define FEC_MII_DATA_RA_MSK		0x007c0000	/* PHY Register mask */
> +#define FEC_MII_DATA_TA			0x00020000	/* Turnaround */
> +#define FEC_MII_DATA_DATAMSK		0x0000ffff	/* PHY data mask */
> +
> +#define FEC_MII_READ_FRAME	(FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA)
> +#define FEC_MII_WRITE_FRAME	(FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | FEC_MII_DATA_TA)
> +
> +#define FEC_MII_DATA_RA_SHIFT		0x12		/* MII reg addr bits */
> +#define FEC_MII_DATA_PA_SHIFT		0x17		/* MII PHY addr bits */
> +
> +#define FEC_PADDR2_TYPE			0x8808
> +
> +#define FEC_OP_PAUSE_OPCODE		0x00010000
> +
> +#define FEC_FIFO_WMRK_256B		0x3
> +
> +#define FEC_FIFO_STATUS_ERR		0x00400000
> +#define FEC_FIFO_STATUS_UF		0x00200000
> +#define FEC_FIFO_STATUS_OF		0x00100000
> +
> +#define FEC_FIFO_CNTRL_FRAME		0x08000000
> +#define FEC_FIFO_CNTRL_LTG_7		0x07000000
> +
> +#define FEC_RESET_CNTRL_RESET_FIFO	0x02000000
> +#define FEC_RESET_CNTRL_ENABLE_IS_RESET	0x01000000
> +
> +#define FEC_XMIT_FSM_APPEND_CRC		0x02000000
> +#define FEC_XMIT_FSM_ENABLE_CRC		0x01000000
> +
> +
> +extern struct of_platform_driver mpc52xx_fec_mdio_driver;
> +
> +#endif	/* __DRIVERS_NET_MPC52XX_FEC_H__ */
> Index: linux.git/drivers/net/fec_mpc52xx_phy.c
> ===================================================================
> --- /dev/null
> +++ linux.git/drivers/net/fec_mpc52xx_phy.c
> @@ -0,0 +1,198 @@
> +/*
> + * Driver for the MPC5200 Fast Ethernet Controller - MDIO bus driver
> + *
> + * Copyright (C) 2007  Domen Puncer, Telargo, Inc.
> + *
> + * This file is licensed under the terms of the GNU General Public License
> + * version 2. This program is licensed "as is" without any warranty of any
> + * kind, whether express or implied.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/phy.h>
> +#include <linux/of_platform.h>
> +#include <asm/io.h>
> +#include <asm/mpc52xx.h>
> +#include "fec_mpc52xx.h"
> +
> +struct mpc52xx_fec_mdio_priv {
> +	struct mpc52xx_fec __iomem *regs;
> +};
> +
> +static int mpc52xx_fec_mdio_read(struct mii_bus *bus, int phy_id, int reg)
> +{
> +	struct mpc52xx_fec_mdio_priv *priv = bus->priv;
> +	struct mpc52xx_fec __iomem *fec;
> +	int tries = 100;
> +	u32 request = FEC_MII_READ_FRAME;
> +
> +	fec = priv->regs;
> +	out_be32(&fec->ievent, FEC_IEVENT_MII);
> +
> +	request |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK;
> +	request |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK;
> +
> +	out_be32(&priv->regs->mii_data, request);
> +
> +	/* wait for it to finish, this takes about 23 us on lite5200b */
> +	while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries)
> +		udelay(5);
> +
> +	if (tries == 0)
> +		return -ETIMEDOUT;
> +
> +	return in_be32(&priv->regs->mii_data) & FEC_MII_DATA_DATAMSK;
> +}
> +
> +static int mpc52xx_fec_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 data)
> +{
> +	struct mpc52xx_fec_mdio_priv *priv = bus->priv;
> +	struct mpc52xx_fec __iomem *fec;
> +	u32 value = data;
> +	int tries = 100;
> +
> +	fec = priv->regs;
> +	out_be32(&fec->ievent, FEC_IEVENT_MII);
> +
> +	value |= FEC_MII_WRITE_FRAME;
> +	value |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK;
> +	value |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK;
> +
> +	out_be32(&priv->regs->mii_data, value);
> +
> +	/* wait for request to finish */
> +	while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries)
> +		udelay(5);
> +
> +	if (tries == 0)
> +		return -ETIMEDOUT;
> +
> +	return 0;
> +}
> +
> +static int mpc52xx_fec_mdio_probe(struct of_device *of, const struct of_device_id *match)
> +{
> +	struct device *dev = &of->dev;
> +	struct device_node *np = of->node;
> +	struct device_node *child = NULL;
> +	struct mii_bus *bus;
> +	struct mpc52xx_fec_mdio_priv *priv;
> +	struct resource res = {};
> +	int err;
> +	int i;
> +
> +	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
> +	if (bus == NULL)
> +		return -ENOMEM;
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (priv == NULL) {
> +		err = -ENOMEM;
> +		goto out_free;
> +	}
> +
> +	bus->name = "mpc52xx MII bus";
> +	bus->read = mpc52xx_fec_mdio_read;
> +	bus->write = mpc52xx_fec_mdio_write;
> +
> +	/* setup irqs */
> +	bus->irq = kmalloc(sizeof(bus->irq[0]) * PHY_MAX_ADDR, GFP_KERNEL);
> +	if (bus->irq == NULL) {
> +		err = -ENOMEM;
> +		goto out_free;
> +	}
> +	for (i=0; i<PHY_MAX_ADDR; i++)
> +		bus->irq[i] = PHY_POLL;
> +
> +	while ((child = of_get_next_child(np, child)) != NULL) {
> +		int irq = irq_of_parse_and_map(child, 0);
> +		if (irq != NO_IRQ) {
> +			const u32 *id = of_get_property(child, "reg", NULL);
> +			bus->irq[*id] = irq;
> +		}
> +	}
> +
> +	/* setup registers */
> +	err = of_address_to_resource(np, 0, &res);
> +	if (err)
> +		goto out_free;
> +	priv->regs = ioremap(res.start, res.end - res.start + 1);
> +	if (priv->regs == NULL) {
> +		err = -ENOMEM;
> +		goto out_free;
> +	}
> +
> +	bus->id = res.start;
> +	bus->priv = priv;
> +
> +	bus->dev = dev;
> +	dev_set_drvdata(dev, bus);
> +
> +	/* set MII speed */
> +	out_be32(&priv->regs->mii_speed, ((mpc52xx_find_ipb_freq(of->node) >> 20) / 5) << 1);
> +
> +	/* enable MII interrupt */
> +	out_be32(&priv->regs->imask, in_be32(&priv->regs->imask) | FEC_IMASK_MII);
> +
> +	err = mdiobus_register(bus);
> +	if (err)
> +		goto out_unmap;
> +
> +	return 0;
> +
> + out_unmap:
> +	iounmap(priv->regs);
> + out_free:
> +	for (i=0; i<PHY_MAX_ADDR; i++)
> +		if (bus->irq[i] != PHY_POLL)
> +			irq_dispose_mapping(bus->irq[i]);
> +	kfree(bus->irq);
> +	kfree(priv);
> +	kfree(bus);
> +
> +	return err;
> +}
> +
> +static int mpc52xx_fec_mdio_remove(struct of_device *of)
> +{
> +	struct device *dev = &of->dev;
> +	struct mii_bus *bus = dev_get_drvdata(dev);
> +	struct mpc52xx_fec_mdio_priv *priv = bus->priv;
> +	int i;
> +
> +	mdiobus_unregister(bus);
> +	dev_set_drvdata(dev, NULL);
> +
> +	iounmap(priv->regs);
> +	for (i=0; i<PHY_MAX_ADDR; i++)
> +		if (bus->irq[i])
> +			irq_dispose_mapping(bus->irq[i]);
> +	kfree(priv);
> +	kfree(bus->irq);
> +	kfree(bus);
> +
> +	return 0;
> +}
> +
> +
> +static struct of_device_id mpc52xx_fec_mdio_match[] = {
> +	{
> +		.type = "mdio",
> +		.compatible = "mpc5200b-fec-phy",
> +	},
> +	{},
> +};
> +
> +struct of_platform_driver mpc52xx_fec_mdio_driver = {
> +	.name = "mpc5200b-fec-phy",
> +	.probe = mpc52xx_fec_mdio_probe,
> +	.remove = mpc52xx_fec_mdio_remove,
> +	.match_table = mpc52xx_fec_mdio_match,
> +};
> +
> +/* let fec driver call it, since this has to be registered before it */
> +EXPORT_SYMBOL_GPL(mpc52xx_fec_mdio_driver);
> +
> +
> +MODULE_LICENSE("Dual BSD/GPL");
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


^ permalink raw reply

* [PATCH v1.2.8] wan: new driver retina
From: Matti Linnanvuori @ 2007-10-25 10:36 UTC (permalink / raw)
  To: jgarzik, akpm, info, netdev

From: Matti Linnanvuori <mattilinnanvuori@yahoo.com>

Adding Retina G.703 and G.SHDSL driver.

Signed-off-by: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
---

Fixing a schedule_timeout bug. Formatting a comment.

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-25 09:23:19.933170522 +0300
@@ -494,4 +494,15 @@ 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 or
+	  character device stream.
+
+	  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-25 13:10:05.004606703 +0300
@@ -0,0 +1,3708 @@
+/* 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.8"
+
+/* 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)
+
+static const char fepci_name[] = "retina";
+static const char fepci_alarm_manager_name[] = "retina alarm manager";
+static const char fepci_NAME[] = "RETINA";
+static const char fepci_netdev_name[] = "dcpxx";
+static 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"
+
+/* Time in jiffies before concluding that the transmitter is hung */
+#define TX_TIMEOUT  (5 * 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)
+
+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 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 stream mode: */
+	/* rx-errors in descriptors */
+	unsigned rx_desc_fifo_err_stream;
+	unsigned rx_desc_size_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;
+/* other: */
+	/* tx interrupts since last timer interrupt */
+	unsigned tx_interrupts_since_last_timer;
+};
+
+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): */
+static int major;			/* char device major number */
+static struct fepci_card_private card_privates[MAX_DEVICES];
+static unsigned long stream_pointers;
+static struct proc_dir_entry *proc_root_entry;
+
+static void set_int_mask(int channel, u_char value,
+			 struct fepci_card_private *cp)
+{
+	void *address;
+	unsigned shift, oldvalue;
+	pr_debug("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);
+}
+
+static void clear_int(unsigned channel, unsigned value, void *ioaddr)
+{
+	void *address;
+	unsigned shift, longvalue;
+	pr_debug("clear_int\n");
+	address = ioaddr + reg_first_int_status + (channel / 4) * 4;
+	shift = 8 * (channel % 4);
+	longvalue = value << shift;
+	writel(~longvalue, (void *)address);
+}
+
+static u_char get_int_status(int channel, void *ioaddr)
+{
+	void *address;
+	unsigned shift, oldvalue;
+	pr_debug("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);
+}
+
+static void fillregisterswith_00(void *ioaddr)
+{
+	pr_debug("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);
+
+/* char device operations: */
+
+static ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+			loff_t *f_pos);
+static int fepci_char_open(struct inode *inode, struct file *filp);
+static int fepci_char_release(struct inode *inode, struct file *filp);
+static int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma);
+static 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
+};
+
+static int fepci_char_open(struct inode *inode, struct file *filp)
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+	pr_debug("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;
+}
+
+static int fepci_char_release(struct inode *inode, struct file *filp)
+{
+	pr_debug("fepci_char_release\n");
+	module_put(THIS_MODULE);
+	return 0;
+}
+
+static void fepci_vma_open(struct vm_area_struct *vma)
+{
+	pr_debug("fepci_vma_open\n");
+}
+
+static void fepci_vma_close(struct vm_area_struct *vma)
+{
+	pr_debug("fepci_vma_close\n");
+	module_put(THIS_MODULE);
+}
+
+static struct vm_operations_struct fepci_vm_ops = {
+       .open	= fepci_vma_open,
+       .close	= fepci_vma_close
+};
+
+static 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:
+			pr_debug("%s: mmap: invalid address 0x%lx\n",
+				  fepci_NAME, virtual_address);
+			return -EINVAL;
+		}
+		if (unlikely(virtual_address == 0))
+			goto INVALID;
+	}
+
+	if (unlikely(!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 */
+
+static void fepci_copy_to_user(unsigned long to, void *from, unsigned long len,
+			int shrink)
+{
+	unsigned int i;
+	pr_debug("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));
+	}
+}
+
+static 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));
+	}
+}
+
+static unsigned get_semafore(struct fepci_real_mailbox *mailbox)
+{
+	unsigned semafore = readb(&mailbox->Semafore_Mail_number);
+	pr_debug("get_semafore = %x\n", semafore);
+	return semafore;
+}
+
+static void set_semafore(struct fepci_real_mailbox *mailbox, unsigned semafore)
+{
+	unsigned number = readl(&mailbox->Semafore_Mail_number);
+	pr_debug("got number %u at %p.\n", number,
+		  &mailbox->Semafore_Mail_number);
+	number = ((number & ~0xFF) | semafore) + (1 << 8);
+	pr_debug
+	    ("increases the mail number to %u at the same time at %p.\n",
+	     number, &mailbox->Semafore_Mail_number);
+	writel(number, &mailbox->Semafore_Mail_number);
+	pr_debug("set_semafore %p, %u returns 0.\n", mailbox, semafore);
+}
+
+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;
+}
+
+static 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;
+	}
+
+	pr_debug("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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug("%s: IOCTL_B_GRAB commanded.\n", fepci_NAME);
+		if ((my_pid != *saved_pid) && (*saved_pid != 0)) {
+			retval = 0x2;
+			break;
+		}
+		pr_debug("%s: IOCTL_B_GRAB getting semaphore.\n", fepci_NAME);
+		if (get_semafore(real_mailbox) == 0x0) {
+			pr_debug("%s: IOCTL_B_GRAB setting semaphore.\n",
+				  fepci_NAME);
+			set_semafore(real_mailbox, 0x40);
+			pr_debug("%s: IOCTL_B_GRAB sleeping.\n", fepci_NAME);
+			msleep(1);	/* delay at least 1 millisecond */
+			pr_debug
+			    ("%s: IOCTL_B_GRAB getting semaphore again.\n",
+			     fepci_NAME);
+			switch (get_semafore(real_mailbox)) {
+			case 0x40:
+				retval = 0x0;
+				pr_debug
+				    ("%s: IOCTL_B_GRAB saving pid to %p.\n",
+				     fepci_NAME, saved_pid);
+				*saved_pid = my_pid;
+				pr_debug
+				    ("%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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %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:
+		pr_debug(" %s: IOCTL_ALARM_MANAGER commanded.\n", fepci_NAME);
+		interruptible_sleep_on(&(card_privates[minor].
+					 alarm_manager_wait_q));
+		return retval;
+	default:
+		pr_debug(" %s: Unknown ioctl command 0x%x.\n", fepci_NAME,
+			  cmd);
+		return -ENOTTY;
+	}
+	return retval;
+}
+
+static ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
+			loff_t *f_pos)
+{
+	pr_debug("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
+		pr_debug("%s: registered char device, major:0x%x.\n",
+			  fepci_NAME, error);
+	return error;
+}
+
+static 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;
+
+	pr_debug("%s: Bufsize is 0x%x.\n", fepci_NAME, fp->bufsize);
+	pr_debug("%s: Unit_size is 0x%x.\n", fepci_NAME, fp->unit_sz);
+	pr_debug("%s: Number of units is 0x%x.\n", fepci_NAME, fp->units);
+
+	pr_debug("%s: Fake_unit_size is 0x%x.\n", fepci_NAME,
+		  fp->fake_unit_sz);
+	pr_debug("%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);
+	unsigned fifo, dropped;
+	unsigned int temp_rx;
+	unsigned int temp_rx_unit;
+	unsigned int temp_tx;
+	unsigned int temp_tx_unit;
+
+	if (unlikely(!intr_status))
+		return IRQ_NONE;
+	clear_int(fp->channel_number, intr_status, ioaddr);
+
+	/* debugging */
+	fifo = intr_status & IntrRxFifoError;
+	if (fifo)
+		fp->rx_int_fifo_err_stream++;
+	dropped = intr_status & IntrRxFrameDroppedError;
+	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 octet = (desc_b & octet_error) != 0;
+			bool line = (desc_b & line_error) != 0;
+			dma_addr_t bus_address;
+			/* update debug counters */
+			if (fifo)
+				fp->rx_desc_fifo_err_stream++;
+			else if (size)
+				fp->rx_desc_size_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 IRQ_HANDLED;
+}
+
+/* stream operations end */
+
+static int fepci_rebuild_header(struct sk_buff *skb)
+{
+	pr_debug("fepci_rebuild_header\n");
+	return 0;
+}
+
+static inline u16 get_common_reg_word(void *ioaddr, unsigned long offsett)
+{
+	u16 word;
+	pr_debug("get_common_reg_word ioaddr %p, offsett %lu\n", ioaddr,
+		  offsett);
+	__clear_bit(0, &offsett);
+	pr_debug("get_common_reg_word %p\n",
+		  ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1));
+	word = le16_to_cpu(readw
+			   (ioaddr + FEPCI_IDENTIFICATION_OFFSETT +
+			    (offsett << 1)));
+	pr_debug("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 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);
+	ioaddr = ioremap_nocache(real_ioaddr, FEPCI_SIZE);
+	pr_debug("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;
+	pr_debug("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);
+
+	pr_debug("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;
+	}
+	pr_debug("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++) {
+		pr_debug("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);
+		dev_dbg(&dev->dev, "clear_int %u, %x, %p.\n", j,
+			IntrAllInts, ioaddr);
+		clear_int(j, IntrAllInts, ioaddr);
+		dev_dbg(&dev->dev, "ether_setup.\n");
+		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) {
+				dev_dbg(&dev->dev,
+					"jiffies %lu < waituntil %lu.\n",
+					jiffies, waituntil);
+				schedule_timeout_uninterruptible(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;
+		dev_dbg(&dev->dev, "alarm pci_set_drvdata %p.\n", pdev);
+		if (j == 0)
+			pci_set_drvdata(pdev, dev);
+
+		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
+		dev_dbg(&dev->dev, "register_netdev.\n");
+		i = register_netdev(dev);
+		if (i) {
+			printk(KERN_WARNING
+			       "%s: register_netdev failed 0x%x.\n",
+			       fepci_NAME, i);
+			continue;
+		}
+
+		for (i = 0; i < 5; i++)
+			dev_dbg(&dev->dev, "%2.2x:", dev->dev_addr[i]);
+		dev_dbg(&dev->dev, "%2.2x, IRQ %d.\n", dev->dev_addr[i],
+			pdev->irq);
+	}
+	up_write(&card_privates[position].semaphore);
+	pr_debug("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;
+
+	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);
+}
+
+/* Just to make it absolutely sure the sending starts again
+ * if the system jams: free already sent skbuffs. */
+
+static void fepci_tx_timeout(struct net_device *dev)
+{
+	struct fepci_ch_private *fp = dev->priv;
+
+	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)
+					continue;
+				desc_b = readl(&fp->tx_desc[i].desc_b);
+				if ((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_skbuff[i] = NULL;
+
+					if (desc_b & fifo_error)
+						fp->stats.tx_fifo_errors++;
+					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;
+}
+
+/* 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))) {
+				/* 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;
+
+	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);
+
+	if (unlikely(!intr_status))
+		return IRQ_NONE;
+	clear_int(fp->channel_number, intr_status, ioaddr);
+
+	if (intr_status &
+		(IntrFrameReceived | IntrRxFifoError | IntrRxFrameDroppedError))
+		fepci_rx(dev);
+	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)
+					continue;
+				desc_b = readl(&fp->tx_desc[i].desc_b);
+				if ((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_skbuff[i] = NULL;
+					if (desc_b & fifo_error)
+						fp->stats.tx_fifo_errors++;
+					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);
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+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);
+			if (fifo) {
+				fp->stats.rx_errors++;
+				fp->stats.rx_frame_errors++;
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			} else if (size) {
+				fp->stats.rx_errors++;
+				fp->stats.rx_over_errors++;
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			} else if (crc) {
+				fp->stats.rx_errors++;
+				fp->stats.rx_crc_errors++;
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			} else if (octet)
+				writel(enable_transfer, &fp->rx_desc[i].desc_b);
+			else if (line)
+				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);
+				/* 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_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_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_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)
+{
+	dev_dbg(&dev->dev, "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;
+
+	dev_dbg(&dev->dev, "netdev_ioctl called (command_nmbr:0x%x).\n",
+		cmd);
+
+	switch (cmd) {
+	case FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE:
+		dev_dbg
+		    (&dev->dev,
+		     " 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:
+		dev_dbg
+		    (&dev->dev,
+		     " 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:
+		dev_dbg(&dev->dev, " ioctl stream open commanded.\n");
+		ret = fepci_stream_open(dev);
+		break;
+	case FEPCI_NETDEV_IOCTL_STREAM_START:
+		dev_dbg(&dev->dev, " ioctl stream start commanded.\n");
+		ret = fepci_stream_start(dev);
+		break;
+	case FEPCI_NETDEV_IOCTL_STREAM_CLOSE:
+		dev_dbg(&dev->dev, " ioctl stream close commanded.\n");
+		ret = fepci_stream_close(dev);
+		break;
+	default:
+		dev_dbg(&dev->dev, " 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;
+		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);
+	pr_debug(" %lx.\n", stream_pointers);
+	if (stream_pointers == 0) {
+		fepci_unregister_char_device();
+		return -ENOMEM;
+	}
+	pr_debug("SetPageReserved %lu.\n", stream_pointers);
+	SetPageReserved(virt_to_page(stream_pointers));
+	pr_debug("fepci_proc_init_driver.\n");
+	fepci_proc_init_driver();
+	pr_debug("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);
+			pr_debug
+			    ("pci_register_driver %p failed with %d.\n",
+			     &fepci_driver, ret);
+			fepci_proc_cleanup_driver();
+			return ret;
+		}
+		pr_debug("fepci_init %d.\n", ret);
+		return ret;
+	}
+}
+
+static void __exit fepci_cleanup(void)
+{
+	pr_debug("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);
+
+static int get_line_data_rate_value(unsigned char line_rate)
+{
+	switch (line_rate) {
+	case 0x00:
+		return 0;
+	case 0x05:
+	case 0x28:
+		return 8;
+	case 0x14:
+		return 256;
+	case 0x15:
+		return 300;
+	case 0x29:
+	case 0x06:
+		return 10;
+	case 0x30:
+		return 32;
+	case 0x34:
+		return 56;
+	case 0x36:
+		return 64;
+	case 0x01:
+	case 0x60:
+	case 0xa0:
+	case 0x20:
+		return 1;
+	default:
+		return -1;
+	}
+}
+
+static char get_line_data_rate_unit(unsigned char line_rate)
+{
+	switch (line_rate) {
+	case 0x00:
+	case 0x01:
+	case 0x05:
+	case 0x06:
+	case 0x14:
+	case 0x15:
+		return 0;
+	case 0x20:
+	case 0x28:
+	case 0x29:
+	case 0x30:
+	case 0x34:
+	case 0x36:
+		return 'k';
+	case 0x60:
+		return 'M';
+	case 0xa0:
+		return 'G';
+	default:
+		return 0;
+	}
+}
+
+static int print_line_type(unsigned char type, char *buf, int pos)
+{
+	pr_debug("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;
+}
+
+static int print_card_type(unsigned char type, char *buf, int pos)
+{
+	pr_debug("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;
+}
+
+static int print_card_model(unsigned char model, char *buf, int pos)
+{
+	pr_debug("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;
+	pr_debug("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);
+	pr_debug("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 +=
+						    sprintf(buf + pos, "n/a\n");
+				}
+			}
+		}
+
+		/* 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;
+	}
+}
+
+static 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);
+	}
+	pr_debug("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");
+	pr_debug("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)
+{
+	pr_debug("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);
+}
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-25 13:10:05.004606703 +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:	reiserfs-devel@vger.kernel.org
 W:	http://www.namesys.com
 S:	Supported
 
+RETINA DRIVER
+P:	Matti Linnanvuori
+M:	mattilinnanvuori@yahoo.com
+L:	netdev@vger.kernel.org
+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

^ permalink raw reply

* Re: [PATCH v1.2.8] wan: new driver retina
From: Jeff Garzik @ 2007-10-25 10:59 UTC (permalink / raw)
  To: Matti Linnanvuori; +Cc: akpm, info, netdev
In-Reply-To: <816507.21090.qm@web52011.mail.re2.yahoo.com>

Matti Linnanvuori wrote:
> From: Matti Linnanvuori <mattilinnanvuori@yahoo.com>
> 
> Adding Retina G.703 and G.SHDSL driver.
> 
> Signed-off-by: Matti Linnanvuori <mattilinnanvuori@yahoo.com>

Overall comment:  very nice and clean, well done.



> 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-25 09:23:19.933170522 +0300
> @@ -494,4 +494,15 @@ 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 or
> +	  character device stream.
> +
> +	  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-25 13:10:05.004606703 +0300
> @@ -0,0 +1,3708 @@
> +/* 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.8"
> +
> +/* 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)
> +
> +static const char fepci_name[] = "retina";
> +static const char fepci_alarm_manager_name[] = "retina alarm manager";
> +static const char fepci_NAME[] = "RETINA";
> +static const char fepci_netdev_name[] = "dcpxx";
> +static 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"
> +
> +/* Time in jiffies before concluding that the transmitter is hung */
> +#define TX_TIMEOUT  (5 * 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

consider making an enum like your other constants


> +#define PCI_IOTYPE (PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY)

delete, never used


> +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},

some prefer

	{ PCI_VDEVICE(VENDOR_NAME, 0x0300) },
	{ PCI_VDEVICE(VENDOR_NAME, 0x0301) },
	{ }	/* terminate list */

but it's up to you.


> +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 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 stream mode: */
> +	/* rx-errors in descriptors */
> +	unsigned rx_desc_fifo_err_stream;
> +	unsigned rx_desc_size_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;
> +/* other: */
> +	/* tx interrupts since last timer interrupt */
> +	unsigned tx_interrupts_since_last_timer;

if these are purely informational statistics, recommend a separate 
structure that is in turn embedded within struct fepci_ch_private


> +};
> +
> +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,

I recommend making these enums more readable by tabbing the values into 
alignment:

	fifo_error		= 0x10000,
	transfer_not_done	= 0x80000000,
	etc.


> +/* global variables (common to whole driver, all the cards): */
> +static int major;			/* char device major number */
> +static struct fepci_card_private card_privates[MAX_DEVICES];
> +static unsigned long stream_pointers;
> +static struct proc_dir_entry *proc_root_entry;

generally new procfs nodes are discouraged, in favor of sysfs attributes


> +static void set_int_mask(int channel, u_char value,
> +			 struct fepci_card_private *cp)
> +{
> +	void *address;
> +	unsigned shift, oldvalue;
> +	pr_debug("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);

all addresses passed to writel() and readl() should be marked with 'void 
__iomem *'

then run sparse to validate (Documentation/sparse.txt)


> +static void clear_int(unsigned channel, unsigned value, void *ioaddr)
> +{
> +	void *address;
> +	unsigned shift, longvalue;
> +	pr_debug("clear_int\n");
> +	address = ioaddr + reg_first_int_status + (channel / 4) * 4;
> +	shift = 8 * (channel % 4);
> +	longvalue = value << shift;
> +	writel(~longvalue, (void *)address);

1) delete cast

2) void __iomem *address

> +static u_char get_int_status(int channel, void *ioaddr)
> +{
> +	void *address;
> +	unsigned shift, oldvalue;
> +	pr_debug("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);
> +}
> +
> +static void fillregisterswith_00(void *ioaddr)
> +{
> +	pr_debug("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));

ditto

> +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);
> +
> +/* char device operations: */
> +
> +static ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
> +			loff_t *f_pos);
> +static int fepci_char_open(struct inode *inode, struct file *filp);
> +static int fepci_char_release(struct inode *inode, struct file *filp);
> +static int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma);
> +static 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
> +};
> +
> +static int fepci_char_open(struct inode *inode, struct file *filp)
> +{
> +	unsigned int minor = MINOR(inode->i_rdev);
> +	pr_debug("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;
> +}
> +
> +static int fepci_char_release(struct inode *inode, struct file *filp)
> +{
> +	pr_debug("fepci_char_release\n");
> +	module_put(THIS_MODULE);
> +	return 0;
> +}
> +
> +static void fepci_vma_open(struct vm_area_struct *vma)
> +{
> +	pr_debug("fepci_vma_open\n");
> +}
> +
> +static void fepci_vma_close(struct vm_area_struct *vma)
> +{
> +	pr_debug("fepci_vma_close\n");
> +	module_put(THIS_MODULE);
> +}
> +
> +static struct vm_operations_struct fepci_vm_ops = {
> +       .open	= fepci_vma_open,
> +       .close	= fepci_vma_close
> +};
> +
> +static 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:
> +			pr_debug("%s: mmap: invalid address 0x%lx\n",
> +				  fepci_NAME, virtual_address);
> +			return -EINVAL;
> +		}
> +		if (unlikely(virtual_address == 0))
> +			goto INVALID;
> +	}
> +
> +	if (unlikely(!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 */
> +
> +static void fepci_copy_to_user(unsigned long to, void *from, unsigned long len,
> +			int shrink)
> +{
> +	unsigned int i;
> +	pr_debug("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));
> +	}
> +}
> +
> +static 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));
> +	}
> +}

please kindly explain this char device in detail (URL to documentation?)...

a WAN driver providing mmap(2) is quite unusual.  what is the interface 
description?  what security restrictions exist?

Stopped reviewing here.  That should give you a bit to do :)

	Jeff



^ permalink raw reply

* Re: [PATCH,RFC] Marvell Orion SoC ethernet driver
From: Lennert Buytenhek @ 2007-10-25 11:51 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev, tzachi, nico
In-Reply-To: <47205DE4.7040001@garzik.org>

On Thu, Oct 25, 2007 at 05:12:04AM -0400, Jeff Garzik wrote:

> >+struct rx_desc {
> >+	u32 cmd_sts;
> >+	u16 size;
> >+	u16 count;
> >+	u32 buf;
> >+	u32 next;
> >+};
> >+
> >+struct tx_desc {
> >+	u32 cmd_sts;
> >+	u16 l4i_chk;
> >+	u16 count;
> >+	u32 buf;
> >+	u32 next;
> >+};
> 
> should use sparse type (__le32, etc.) and make sure this driver passes 
> sparse checks
> 
> ditto for checkpatch (except for the excessively anal stuff)

Sorry if it wasn't clear from the thread -- the mainline mv643xx_eth
driver turns out to support the same silicon block (but as part of a
different chip), so we've dropped orion_eth and submitted patches to
make mv643xx_eth work on both the Discovery (what it was originally
written for) and the Orion, and these patches are in -rc1 already.

^ permalink raw reply

* [NETNS] Oops in register_pernet_operations() with CONFIG_NET_NS=n
From: Benjamin Thery @ 2007-10-25 12:59 UTC (permalink / raw)
  To: Pavel Emelianov
  Cc: Eric W. Biederman, David Miller, Linux Netdev List,
	Cedric Le Goater, Linux Containers, Daniel Lezcano

Hello Pavel,

I've found a problem with one of your patch related to netns:

* [NETNS] Move some code into __init section when CONFIG_NET_NS=n (v2)
   http://www.spinics.net/lists/netdev/msg43310.html

This patch introduces the __net_init/__net_exit/__net_initdata
defines to save some memory when CONFIG_NET_NS is not set.

Cedric Le Goater reported he had a *non-fatal* oops when booting
a 2.6.23-mm1-lxc1 kernel with CONFIG_NET_NS=n. (2.6.23-mm1-lxc1 
contains the NETNS49 patchset). The oops occured when modules 
related to iptables were loaded after the boot completes.

The problem is the following:

- Your patch adds the __net_initdata attribute to pernet_operations 
  structures.

- pernet_operations are registered via register_pernet_subsys() and 
  linked in the pernet_list during boot.

- At the end of boot, pernet_operations are freed (because of the
  __net_initdata attribute), and the pernet_list (or first_device list) 
  points to freed memory.

- After boot, network modules which are netns-aware try to register
  themselves with register_pernet_subsys() and ...KABOOM... page
  fault when accessing pernet_list (or first_device list).
  (I reproduce Cedric's oops with the command: iptables --list)

This is not a problem right now in 2.6.23-mm1 or net-2.6 because 
there are very few netns-aware network subsystems merged and they
are all initialized during boot. But it will be problematic when 
we will merge netns code for subsystems which can be built as 
modules (eg. iptables, ...). I'm not sure we can use 
__net_init_data for pernet_operations then. 
Maybe we can add some checks in register_pernet_operations
when CONFIG_NET_NS=n.

I haven't found a fix yet.

Regards,
Benjamin

-- 
B e n j a m i n   T h e r y  - BULL/DT/Open Software R&D

   http://www.bull.com

^ permalink raw reply

* [PATCH] NET: Mark snmp4_icmp_list[] as being unused
From: David Howells @ 2007-10-25 13:36 UTC (permalink / raw)
  To: netdev; +Cc: dhowells

Mark snmp4_icmp_list[] as being unused to avoid the warning it would otherwise
incur.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 net/ipv4/proc.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index fd16cb8..1c0854d 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -121,7 +121,8 @@ static const struct snmp_mib snmp4_ipextstats_list[] = {
 	SNMP_MIB_SENTINEL
 };
 
-static const struct snmp_mib snmp4_icmp_list[] = {
+static __attribute__((unused))
+const struct snmp_mib snmp4_icmp_list[] = {
 	SNMP_MIB_ITEM("InMsgs", ICMP_MIB_INMSGS),
 	SNMP_MIB_ITEM("InErrors", ICMP_MIB_INERRORS),
 	SNMP_MIB_ITEM("OutMsgs", ICMP_MIB_OUTMSGS),


^ permalink raw reply related

* [PATCH] NetLabel fixes against 2.6.24-rc1
From: Paul Moore @ 2007-10-25 13:52 UTC (permalink / raw)
  To: netdev

There have been some good discussions on the list about RCU lately that made
me realize NetLabel was doing some rather stupid things in regards to how it
makes use of RCU.  This is a small patch again against 2.6.24-rc1 which should
fix the RCU problems.  You can grab the patch from email or from here:

 * git://git.infradead.org/users/pcmoore/lblnet-2.6

I tested last patch last week using some stress tests for a few days and
didn't nothing anything out of the oridinary.  Please consider for 2.6.24.

Thanks.

-- 
paul moore
linux security @ hp


^ permalink raw reply

* [PATCH] NetLabel: correct usage of RCU locking
From: Paul Moore @ 2007-10-25 13:52 UTC (permalink / raw)
  To: netdev
In-Reply-To: <20071025134727.6382.79273.stgit@flek.americas.hpqcorp.net>

This fixes some awkward, and perhaps even problematic, RCU lock usage in the
NetLabel code as well as some other related trivial cleanups found when
looking through the RCU locking.  Most of the changes involve removing the
redundant RCU read locks wrapping spinlocks in the case of a RCU writer.

Signed-off-by: Paul Moore <paul.moore@hp.com>
---

 net/ipv4/cipso_ipv4.c              |   39 +++++++++---------------------------
 net/netlabel/netlabel_domainhash.c |   37 ++++++++++------------------------
 net/netlabel/netlabel_mgmt.c       |    4 ----
 net/netlabel/netlabel_unlabeled.c  |    4 +---
 4 files changed, 22 insertions(+), 62 deletions(-)

diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
index 805a78e..f18e88b 100644
--- a/net/ipv4/cipso_ipv4.c
+++ b/net/ipv4/cipso_ipv4.c
@@ -504,22 +504,16 @@ int cipso_v4_doi_add(struct cipso_v4_doi *doi_def)
 	INIT_RCU_HEAD(&doi_def->rcu);
 	INIT_LIST_HEAD(&doi_def->dom_list);
 
-	rcu_read_lock();
-	if (cipso_v4_doi_search(doi_def->doi) != NULL)
-		goto doi_add_failure_rlock;
 	spin_lock(&cipso_v4_doi_list_lock);
 	if (cipso_v4_doi_search(doi_def->doi) != NULL)
-		goto doi_add_failure_slock;
+		goto doi_add_failure;
 	list_add_tail_rcu(&doi_def->list, &cipso_v4_doi_list);
 	spin_unlock(&cipso_v4_doi_list_lock);
-	rcu_read_unlock();
 
 	return 0;
 
-doi_add_failure_slock:
+doi_add_failure:
 	spin_unlock(&cipso_v4_doi_list_lock);
-doi_add_failure_rlock:
-	rcu_read_unlock();
 	return -EEXIST;
 }
 
@@ -543,29 +537,23 @@ int cipso_v4_doi_remove(u32 doi,
 	struct cipso_v4_doi *doi_def;
 	struct cipso_v4_domhsh_entry *dom_iter;
 
-	rcu_read_lock();
-	if (cipso_v4_doi_search(doi) != NULL) {
-		spin_lock(&cipso_v4_doi_list_lock);
-		doi_def = cipso_v4_doi_search(doi);
-		if (doi_def == NULL) {
-			spin_unlock(&cipso_v4_doi_list_lock);
-			rcu_read_unlock();
-			return -ENOENT;
-		}
+	spin_lock(&cipso_v4_doi_list_lock);
+	doi_def = cipso_v4_doi_search(doi);
+	if (doi_def != NULL) {
 		doi_def->valid = 0;
 		list_del_rcu(&doi_def->list);
 		spin_unlock(&cipso_v4_doi_list_lock);
+		rcu_read_lock();
 		list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list)
 			if (dom_iter->valid)
 				netlbl_domhsh_remove(dom_iter->domain,
 						     audit_info);
-		cipso_v4_cache_invalidate();
 		rcu_read_unlock();
-
+		cipso_v4_cache_invalidate();
 		call_rcu(&doi_def->rcu, callback);
 		return 0;
 	}
-	rcu_read_unlock();
+	spin_unlock(&cipso_v4_doi_list_lock);
 
 	return -ENOENT;
 }
@@ -653,22 +641,19 @@ int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain)
 	new_dom->valid = 1;
 	INIT_RCU_HEAD(&new_dom->rcu);
 
-	rcu_read_lock();
 	spin_lock(&cipso_v4_doi_list_lock);
-	list_for_each_entry_rcu(iter, &doi_def->dom_list, list)
+	list_for_each_entry(iter, &doi_def->dom_list, list)
 		if (iter->valid &&
 		    ((domain != NULL && iter->domain != NULL &&
 		      strcmp(iter->domain, domain) == 0) ||
 		     (domain == NULL && iter->domain == NULL))) {
 			spin_unlock(&cipso_v4_doi_list_lock);
-			rcu_read_unlock();
 			kfree(new_dom->domain);
 			kfree(new_dom);
 			return -EEXIST;
 		}
 	list_add_tail_rcu(&new_dom->list, &doi_def->dom_list);
 	spin_unlock(&cipso_v4_doi_list_lock);
-	rcu_read_unlock();
 
 	return 0;
 }
@@ -689,9 +674,8 @@ int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
 {
 	struct cipso_v4_domhsh_entry *iter;
 
-	rcu_read_lock();
 	spin_lock(&cipso_v4_doi_list_lock);
-	list_for_each_entry_rcu(iter, &doi_def->dom_list, list)
+	list_for_each_entry(iter, &doi_def->dom_list, list)
 		if (iter->valid &&
 		    ((domain != NULL && iter->domain != NULL &&
 		      strcmp(iter->domain, domain) == 0) ||
@@ -699,13 +683,10 @@ int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def,
 			iter->valid = 0;
 			list_del_rcu(&iter->list);
 			spin_unlock(&cipso_v4_doi_list_lock);
-			rcu_read_unlock();
 			call_rcu(&iter->rcu, cipso_v4_doi_domhsh_free);
-
 			return 0;
 		}
 	spin_unlock(&cipso_v4_doi_list_lock);
-	rcu_read_unlock();
 
 	return -ENOENT;
 }
diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c
index b6c844b..b3675bd 100644
--- a/net/netlabel/netlabel_domainhash.c
+++ b/net/netlabel/netlabel_domainhash.c
@@ -178,11 +178,9 @@ int netlbl_domhsh_init(u32 size)
 	for (iter = 0; iter < hsh_tbl->size; iter++)
 		INIT_LIST_HEAD(&hsh_tbl->tbl[iter]);
 
-	rcu_read_lock();
 	spin_lock(&netlbl_domhsh_lock);
 	rcu_assign_pointer(netlbl_domhsh, hsh_tbl);
 	spin_unlock(&netlbl_domhsh_lock);
-	rcu_read_unlock();
 
 	return 0;
 }
@@ -222,7 +220,6 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
 	entry->valid = 1;
 	INIT_RCU_HEAD(&entry->rcu);
 
-	ret_val = 0;
 	rcu_read_lock();
 	if (entry->domain != NULL) {
 		bkt = netlbl_domhsh_hash(entry->domain);
@@ -233,7 +230,7 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
 		else
 			ret_val = -EEXIST;
 		spin_unlock(&netlbl_domhsh_lock);
-	} else if (entry->domain == NULL) {
+	} else {
 		INIT_LIST_HEAD(&entry->list);
 		spin_lock(&netlbl_domhsh_def_lock);
 		if (rcu_dereference(netlbl_domhsh_def) == NULL)
@@ -241,9 +238,7 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
 		else
 			ret_val = -EEXIST;
 		spin_unlock(&netlbl_domhsh_def_lock);
-	} else
-		ret_val = -EINVAL;
-
+	}
 	audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info);
 	if (audit_buf != NULL) {
 		audit_log_format(audit_buf,
@@ -262,7 +257,6 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry,
 		audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0);
 		audit_log_end(audit_buf);
 	}
-
 	rcu_read_unlock();
 
 	if (ret_val != 0) {
@@ -313,38 +307,30 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
 	struct audit_buffer *audit_buf;
 
 	rcu_read_lock();
-	if (domain != NULL)
-		entry = netlbl_domhsh_search(domain, 0);
-	else
-		entry = netlbl_domhsh_search(domain, 1);
+	entry = netlbl_domhsh_search(domain, (domain != NULL ? 0 : 1));
 	if (entry == NULL)
 		goto remove_return;
 	switch (entry->type) {
-	case NETLBL_NLTYPE_UNLABELED:
-		break;
 	case NETLBL_NLTYPE_CIPSOV4:
-		ret_val = cipso_v4_doi_domhsh_remove(entry->type_def.cipsov4,
-						     entry->domain);
-		if (ret_val != 0)
-			goto remove_return;
+		cipso_v4_doi_domhsh_remove(entry->type_def.cipsov4,
+					   entry->domain);
 		break;
 	}
-	ret_val = 0;
 	if (entry != rcu_dereference(netlbl_domhsh_def)) {
 		spin_lock(&netlbl_domhsh_lock);
 		if (entry->valid) {
 			entry->valid = 0;
 			list_del_rcu(&entry->list);
-		} else
-			ret_val = -ENOENT;
+			ret_val = 0;
+		}
 		spin_unlock(&netlbl_domhsh_lock);
 	} else {
 		spin_lock(&netlbl_domhsh_def_lock);
 		if (entry->valid) {
 			entry->valid = 0;
 			rcu_assign_pointer(netlbl_domhsh_def, NULL);
-		} else
-			ret_val = -ENOENT;
+			ret_val = 0;
+		}
 		spin_unlock(&netlbl_domhsh_def_lock);
 	}
 
@@ -357,11 +343,10 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info)
 		audit_log_end(audit_buf);
 	}
 
-	if (ret_val == 0)
-		call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
-
 remove_return:
 	rcu_read_unlock();
+	if (ret_val == 0)
+		call_rcu(&entry->rcu, netlbl_domhsh_free_entry);
 	return ret_val;
 }
 
diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c
index 5315dac..5648337 100644
--- a/net/netlabel/netlabel_mgmt.c
+++ b/net/netlabel/netlabel_mgmt.c
@@ -85,11 +85,9 @@ static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = {
  */
 void netlbl_mgmt_protocount_inc(void)
 {
-	rcu_read_lock();
 	spin_lock(&netlabel_mgmt_protocount_lock);
 	netlabel_mgmt_protocount++;
 	spin_unlock(&netlabel_mgmt_protocount_lock);
-	rcu_read_unlock();
 }
 
 /**
@@ -103,12 +101,10 @@ void netlbl_mgmt_protocount_inc(void)
  */
 void netlbl_mgmt_protocount_dec(void)
 {
-	rcu_read_lock();
 	spin_lock(&netlabel_mgmt_protocount_lock);
 	if (netlabel_mgmt_protocount > 0)
 		netlabel_mgmt_protocount--;
 	spin_unlock(&netlabel_mgmt_protocount_lock);
-	rcu_read_unlock();
 }
 
 /**
diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c
index 5c303c6..3482924 100644
--- a/net/netlabel/netlabel_unlabeled.c
+++ b/net/netlabel/netlabel_unlabeled.c
@@ -84,12 +84,10 @@ static void netlbl_unlabel_acceptflg_set(u8 value,
 	struct audit_buffer *audit_buf;
 	u8 old_val;
 
-	rcu_read_lock();
-	old_val = netlabel_unlabel_acceptflg;
 	spin_lock(&netlabel_unlabel_acceptflg_lock);
+	old_val = netlabel_unlabel_acceptflg;
 	netlabel_unlabel_acceptflg = value;
 	spin_unlock(&netlabel_unlabel_acceptflg_lock);
-	rcu_read_unlock();
 
 	audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_ALLOW,
 					      audit_info);


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox