lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Date:	Tue, 23 Oct 2007 09:31:19 -0700
From:	Stephen Hemminger <shemminger@...ux-foundation.org>
To:	Matti Linnanvuori <mattilinnanvuori@...oo.com>
Cc:	akpm@...ux-foundation.org, jgarzik@...ox.com,
	netdev@...r.kernel.org
Subject: Re: [PATCH] wan: new driver retina

On Tue, 23 Oct 2007 02:58:29 -0700 (PDT)
Matti Linnanvuori <mattilinnanvuori@...oo.com> wrote:

> From: Matti Linnanvuori <mattilinnanvuori@...oo.com>
> 
> Retina G.703 and G.SHDSL driver.
> 
> Signed-off-by: Matti Linnanvuori <mattilinnanvuori@...oo.com>
> ---
> 
> Fixed bugs according to linux-netdev comments.
> 
> diff -Napur linux-2.6.23/drivers/net/wan/Kconfig linux-2.6.24/drivers/net/wan/Kconfig
> --- linux-2.6.23/drivers/net/wan/Kconfig	2007-10-09 23:31:38.000000000 +0300
> +++ linux-2.6.24/drivers/net/wan/Kconfig	2007-10-23 12:30:45.853384514 +0300
> @@ -494,4 +494,14 @@ config SBNI_MULTILINE
>  
>  	  If unsure, say N.
>  
> +config RETINA
> +	tristate "Retina support"
> +	depends on PCI
> +	help
> +	  Driver for Retina C5400 and E2200 network PCI cards, which
> +	  support G.703, G.SHDSL with Ethernet encapsulation.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called retina.
> +
>  endif # WAN
> diff -Napur linux-2.6.23/drivers/net/wan/Makefile linux-2.6.24/drivers/net/wan/Makefile
> --- linux-2.6.23/drivers/net/wan/Makefile	2007-10-09 23:31:38.000000000 +0300
> +++ linux-2.6.24/drivers/net/wan/Makefile	2007-10-23 12:31:17.598640178 +0300
> @@ -42,6 +42,7 @@ obj-$(CONFIG_C101)		+= c101.o
>  obj-$(CONFIG_WANXL)		+= wanxl.o
>  obj-$(CONFIG_PCI200SYN)		+= pci200syn.o
>  obj-$(CONFIG_PC300TOO)		+= pc300too.o
> +obj-$(CONFIG_RETINA)		+= retina.o
>  
>  clean-files := wanxlfw.inc
>  $(obj)/wanxl.o:	$(obj)/wanxlfw.inc
> diff -Napur linux-2.6.23/drivers/net/wan/retina.c linux-2.6.24/drivers/net/wan/retina.c
> --- linux-2.6.23/drivers/net/wan/retina.c	1970-01-01 02:00:00.000000000 +0200
> +++ linux-2.6.24/drivers/net/wan/retina.c	2007-10-23 12:52:17.122802714 +0300
> @@ -0,0 +1,4428 @@
> +/* retina.c: */
> +
> +/*
> +	This driver is based on:
> +
> +	/drivers/net/fepci.c
> +	FEPCI (Frame Engine for PCI) driver for Linux operating system
> +
> +	Copyright (C) 2002-2003 Jouni Kujala, Flexibilis Oy.
> +
> +	This program is free software; you can redistribute it and/or
> +	modify it under the terms of the GNU General Public License
> +	as published by the Free Software Foundation; either version 2
> +	of the License, or (at your option) any later version.
> +
> +	This program is distributed in the hope that it will be useful,
> +	but WITHOUT ANY WARRANTY; without even the implied warranty of
> +	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +	GNU General Public License for more details.
> +
> +	All the drivers derived from or based on this code fall under the
> +	GPL and must retain the copyright and license notice.
> +*/
> +
> +#define DRV_NAME	"retina"
> +#define DRV_VERSION	"1.2.6"
> +
> +#define DBG_PRINT(xyz...)	/* printk(KERN_DEBUG xyz) */

pr_debug()

> +/* uncomment this if you want to have debug files in /proc
> + * #define DEBUG_PROC_FILES */
> +
> +/* Keep this if you want to have point-to-point links
> + ! Only interfaces listed in retina_ptp_interfaces will
> +   be created in PtP mode
> +  see retina_ptp_interfaces */
> +#define FEPCI_POINT_TO_POINT
> +
> +/* need to update MODULE_PARM also */
> +#define MAX_DEVICES 32u
> +
> +#define MAX_TX_UNITS  256u
> +#define MAX_RX_UNITS  256u
> +
> +#define MAX_UNIT_SZ_ORDER  10u
> +
> +#define TX_RING_SIZE	8u
> +#define RX_RING_SIZE	8u
> +
> +/* need to update MODULE_PARM also */
> +#define CHANNELS        4u
> +
> +#define RX_FIFO_THRESHOLD_PACKET_MODE 0x4
> +#define TX_FIFO_THRESHOLD_PACKET_MODE 0x4
> +#define TX_DESC_THRESHOLD_PACKET_MODE 0x4
> +
> +#define RX_FIFO_THRESHOLD_STREAM_MODE 0x4
> +#define TX_FIFO_THRESHOLD_STREAM_MODE 0x7
> +#define TX_DESC_THRESHOLD_STREAM_MODE 0x1
> +
> +/* need to update MODULE_PARM also */
> +#define MAX_INTERFACES (CHANNELS*MAX_DEVICES)
> +
> +const char fepci_name[] = "retina";
> +const char fepci_alarm_manager_name[] = "retina alarm manager";
> +const char fepci_NAME[] = "RETINA";
> +const char fepci_netdev_name[] = "dcpxx";
> +const char fepci_proc_entry_name[] = "driver/retina";
> +
> +static unsigned int find_cnt;
> +
> +#ifdef FEPCI_POINT_TO_POINT
> +static char *retina_ptp_interfaces[MAX_INTERFACES];
> +static int retina_noarp_with_ptp = 1;
> +#endif /* FEPCI_POINT_TO_POINT */
> +
> +#define fepci_features_proc_entry_name "driver/retina/%02x:%02x.%02x/features"
> +#define fepci_settings_proc_entry_name "driver/retina/%02x:%02x.%02x/settings"
> +#define fepci_status_proc_entry_name "driver/retina/%02x:%02x.%02x/status"
> +
> +#ifdef DEBUG_PROC_FILES
> +#define fepci_counters_proc_entry_name \
> +"driver/retina/%02x:%02x.%02x/counters_ch%d"
> +#define fepci_descriptors_proc_entry_name \
> +"driver/retina/%02x:%02x.%02x/descriptors_ch%d"
> +#define fepci_stream_counters_proc_entry_name \
> +"driver/retina/%02x:%02x.%02x/stream_counters_ch%d"
> +#define fepci_registers_proc_entry_name \
> +"driver/retina/%02x:%02x.%02x/registers_ch%d"
> +#endif /* DEBUG_PROC_FILES */

You can't use /proc for this in new drivers. Either:
1. Remove the code since it doesn't do anything useful
2. Convert it to use debugfs (see skge and sky2)
3. Convert it to use sysfs attributes.

> +/* Time in jiffies before concluding that the transmitter is hung */
> +#define TX_TIMEOUT  (20*HZ)

Pretty long.

> +#include "retina.h"
> +#include <linux/mm.h>
> +#include <linux/random.h>
> +#include <linux/proc_fs.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/string.h>
> +#include <linux/timer.h>
> +#include <linux/errno.h>
> +#include <linux/ioport.h>
> +#include <linux/slab.h>
> +#include <linux/interrupt.h>
> +#include <linux/pci.h>
> +#include <linux/init.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/ethtool.h>
> +#include <linux/delay.h>
> +#include <linux/bitops.h>
> +#include <linux/version.h>
> +#include <linux/pfn.h>
> +#include <linux/uaccess.h>
> +#include <linux/io.h>
> +
> +#include <asm/unaligned.h>
> +#include <asm/pgtable.h>
> +
> +MODULE_VERSION(DRV_VERSION);
> +
> +/* PCI I/O space extent */
> +#define FEPCI_SIZE 0x20000
> +#define PCI_IOTYPE (PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY)
> +
> +struct pci_id_info {
> +	const char *name;
> +	struct match_info {
> +		int pci, pci_mask, subsystem, subsystem_mask;
> +		int revision, revision_mask;	/* Only 8 bits. */
> +	} id;
> +	unsigned pci_flags;
> +	int io_size;		/* Needed for I/O region check or ioremap */
> +	int drv_flags;		/* Driver use, intended as capability flags */
> +};
> +
> +static struct pci_id_info pci_id_tbl[] = {
> +	{"Frame Engine for PCI (FEPCI)",
> +	 {0x1FC00300, 0x1FC00301, 0xffffffff},
> +	 PCI_IOTYPE, FEPCI_SIZE},
> +	{0,},
> +};

You shouldn't need this if you use current pci device discovery
methods.

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

You might want to add PCI_VENDOR_ID_FEPCI to pci_ids.h

> +MODULE_DESCRIPTION("Frame Engine for PCI (FEPCI)");
> +MODULE_AUTHOR("Jouni Kujala");
> +MODULE_LICENSE("GPL");
> +MODULE_DEVICE_TABLE(pci, fepci_pci_tbl);
> +
> +/* Linux appears to drop POINTOPOINT,BROADCAST and NOARP flags in SIOCSFLAGS
> + * This workaround allows load time per interface ptp mode configuration.
> + * Runtime ptp mode changes would either require changes to Linux or
> + * use of proprietary ioctls, which ifconfig knows nothing about anyway
> + */
> +
> +static unsigned interfaces = MAX_INTERFACES;
> +#ifdef FEPCI_POINT_TO_POINT
> +module_param_array(retina_ptp_interfaces, charp, &interfaces, S_IRUGO);
> +module_param(retina_noarp_with_ptp, bool, S_IRUGO);
> +MODULE_PARM_DESC(retina_noarp_with_ptp,
> +		 "0 to disable NOARP, "
> +		 "1 to enable NOARP on pointopoint interfaces");
> +#endif
> +
> +struct fepci_ch_private {
> +	unsigned int channel_number;
> +	struct net_device *this_dev;
> +
> +	struct fepci_card_private *this_card_priv;
> +
> +	unsigned int reg_rxctrl;
> +	unsigned int reg_txctrl;
> +
> +	struct fepci_desc *rx_desc;	/* rx_ring start */
> +	struct fepci_desc *tx_desc;	/* tx_ring start */
> +	struct sk_buff *rx_skbuff[RX_RING_SIZE];
> +	struct sk_buff *tx_skbuff[TX_RING_SIZE];
> +
> +	struct timer_list timer;
> +	struct net_device_stats stats;
> +
> +	unsigned drv_flags;
> +
> +	unsigned int rx_buf_sz;	/*  MTU+slack */
> +	unsigned int cur_tx;	/* the next filled tx_descriptor */
> +	/* in stream mode the desc which is being transmitted */
> +	/* rx_descriptor where next packet transferred */
> +	unsigned int cur_rx;
> +	/* in stream mode the desc which is being received */
> +
> +/* stream mode: */
> +	bool in_eth_mode;
> +	bool in_stream_mode;
> +	bool stream_on;
> +	u32 *rx_buffer;
> +	u32 *tx_buffer;
> +	unsigned bufsize_order;	/* 10=1kB,11=2kB,12=4kB...16=64kB */
> +	unsigned bufsize;
> +	unsigned unit_sz_order;	/* 8=256B...14=16kB */
> +	unsigned unit_sz;
> +	unsigned units;		/* 2,4,8,16,...,256 */
> +	/* fake units (and pointers) are for faking larger unit sizes to
> +	 * the user than what is the maximum internal unit size in FEPCI */
> +	unsigned fake_unit_sz_order;
> +	unsigned fake_unit_sz;
> +	unsigned fake_units;
> +	u32 *tx_unit[MAX_TX_UNITS];
> +	u32 *rx_unit[MAX_RX_UNITS];
> +	unsigned cur_tx_unit;	/* last sent tx_unit */
> +	/* rx_unit where to next packet is transferred */
> +	unsigned cur_rx_unit;
> +/* char device: */
> +	unsigned minor;		/* currently the same as card_nuber */
> +/* debugging stuff for packet mode: */
> +	/* rx-errors in descriptors */
> +	unsigned int rx_desc_fifo_err;
> +	unsigned int rx_desc_size_err;
> +	unsigned int rx_desc_crc_err;
> +	unsigned int rx_desc_octet_err;
> +	unsigned rx_desc_line_err;
> +	/* tx-errors in descriptors */
> +	unsigned tx_desc_fifo_err;
> +	/* rx-errors in interrupts */
> +	unsigned rx_int_fifo_err;
> +	unsigned rx_int_frame_dropped_err;
> +	/* tx-errors in interrupts */
> +	unsigned tx_int_fifo_err;
> +	/* ints */
> +	unsigned interrupts;
> +	unsigned rx_interrupts;
> +	unsigned tx_interrupts;
> +	/* error combination tables */
> +	/* fifo,size,crc,octet,line */
> +	unsigned rx_desc_err_table[2][2][2][2][2];
> +	unsigned int_err_table[2][2];	/* fifo,frame_dropped */
> +	/* skbuff counters */
> +	unsigned rx_skbuffs_in;
> +	unsigned rx_skbuffs_out;
> +	unsigned tx_skbuffs_in;
> +	unsigned tx_skbuffs_out;
> +/* debugging stuff for stream mode: */
> +	/* rx-errors in descriptors */
> +	unsigned rx_desc_fifo_err_stream;
> +	unsigned rx_desc_size_err_stream;
> +	unsigned rx_desc_crc_err_stream;
> +	unsigned rx_desc_octet_err_stream;
> +	unsigned rx_desc_line_err_stream;
> +	/* tx-errors in descriptors */
> +	unsigned tx_desc_fifo_err_stream;
> +	/* rx-errors in interrupts */
> +	unsigned rx_int_fifo_err_stream;
> +	unsigned rx_int_frame_dropped_err_stream;
> +	/* tx-errors in interrupts */
> +	unsigned tx_int_fifo_err_stream;
> +	/* ints */
> +	unsigned interrupts_stream;
> +	unsigned rx_interrupts_stream;
> +	unsigned tx_interrupts_stream;
> +	/* error combination tables */
> +	/* fifo,size,crc,octet,line */
> +	unsigned rx_desc_err_table_stream[2][2][2][2][2];
> +	unsigned int_err_table_stream[2][2];	/* fifo,frame_dropped */
> +/* other: */
> +	/* tx interrupts since last timer interrupt */
> +	unsigned tx_interrupts_since_last_timer;
> +/* small packet counters: */
> +	unsigned rx_packets_of_size_0;
> +	unsigned rx_packets_of_size_1;
> +	unsigned rx_packets_of_size_2;
> +	unsigned rx_packets_of_size_3;
> +	unsigned rx_packets_of_size_4_7;
> +	unsigned rx_packets_of_size_8_15;
> +	unsigned rx_packets_of_size_16_31;
> +/* small packet counters for stream: */
> +	unsigned rx_packets_of_size_0_stream;
> +	unsigned rx_packets_of_size_1_stream;
> +	unsigned rx_packets_of_size_2_stream;
> +	unsigned rx_packets_of_size_3_stream;
> +	unsigned rx_packets_of_size_4_7_stream;
> +	unsigned rx_packets_of_size_8_15_stream;
> +	unsigned rx_packets_of_size_16_31_stream;
> +};
> +
> +struct fepci_card_private {
> +	unsigned int card_number;
> +	u8 *ioaddr;
> +	/* Process ID of the current mailbox user
> +	 * (for whom it is reserved for) */
> +	unsigned int ioctl_saved_pid;
> +	struct pci_dev *pci_dev;
> +	struct fepci_ch_private *ch_privates[CHANNELS];
> +
> +	wait_queue_head_t alarm_manager_wait_q;
> +	struct timer_list mailbox_timer;
> +
> +	wait_queue_head_t stream_receive_q;
> +	wait_queue_head_t stream_transmit_q;
> +	wait_queue_head_t stream_both_q;
> +
> +	struct rw_semaphore semaphore;
> +};
> +
> +/* Offsets to the FEPCI registers */
> +enum fepci_offsets {
> +	reg_custom = 0x40,
> +
> +	reg_first_int_mask = 0x80,
> +	reg_first_int_status = 0xc0,
> +
> +	reg_first_rxctrl = 0x4000,
> +	to_next_rxctrl = 0x80,
> +
> +	reg_first_txctrl = 0x6000,
> +	to_next_txctrl = 0x80,
> +
> +	first_rx_desc = 0x10000,
> +	to_next_ch_rx_desc = 0x200,
> +
> +	first_tx_desc = 0x18000,
> +	to_next_ch_tx_desc = 0x200,
> +};
> +
> +enum reg_custom_bits {
> +	AM_interrupt_mask = 0x1,
> +	AM_interrupt_status = 0x100,
> +};
> +
> +enum reg_receive_control {
> +	Rx_fifo_threshold = 0x7,
> +	Receive_enable = 0x80000000,
> +};
> +
> +enum reg_transmit_control {
> +	Tx_fifo_threshold = 0x7,
> +	Tx_desc_threshold = 0x700,
> +	Transmit_enable = 0x80000000,
> +};
> +
> +enum int_bits {
> +	MaskFrameReceived = 0x01, MaskRxFifoError =
> +	    0x02, MaskRxFrameDroppedError = 0x04,
> +	MaskFrameTransmitted = 0x40, MaskTxFifoError = 0x80,
> +	MaskAllInts = 0xc7,
> +	IntrFrameReceived = 0x01, IntrRxFifoError =
> +	    0x02, IntrRxFrameDroppedError = 0x04,
> +	IntrFrameTransmitted = 0x40, IntrTxFifoError = 0x80,
> +	IntrAllInts = 0xc7,
> +};
> +
> +/* The FEPCI Rx and Tx buffer descriptors
> + * Elements are written as 32 bit for endian portability */
> +
> +struct fepci_desc {
> +	u32 desc_a;
> +	u32 desc_b;
> +};
> +
> +enum desc_b_bits {
> +	frame_length = 0xFFF,
> +	fifo_error = 0x10000,
> +	size_error = 0x20000,
> +	crc_error = 0x40000,
> +	octet_error = 0x80000,
> +	line_error = 0x100000,
> +	enable_transfer = 0x80000000,
> +	transfer_not_done = 0x80000000,
> +};
> +
> +/* global variables (common to whole driver, all the cards): */
> +int major;			/* char device major number */

Why are you using regular major/minor for a network device?
Any local variable like this has to be declared static so
as not to pollute the namespace of the whole kernel.



> +struct fepci_card_private card_privates[MAX_DEVICES];
> +unsigned long stream_pointers;
> +struct proc_dir_entry *proc_root_entry;

More name space pollution

> +void set_int_mask(int channel, u_char value, struct fepci_card_private *cp)
> +{
> +	void *address;
> +	unsigned shift, oldvalue;
> +	DBG_PRINT("set_int_mask\n");
> +	address = (cp->ioaddr) + reg_first_int_mask + (channel / 4L) * 4L;
> +	shift = 8L * (channel % 4L);
> +	oldvalue = readl((void *)address);
> +	oldvalue &= ~(0xff << shift);	/* clear bits */
> +	oldvalue |= value << shift;	/* set bits */
> +	writel(oldvalue, (void *)address);
> +}

All these have to be declared static.

> +u_char get_int_mask(int channel, void *ioaddr)
> +{
> +	void *address;
> +	unsigned shift, oldvalue;
> +	DBG_PRINT("get_int_mask\n");
> +	address = ioaddr + reg_first_int_mask + (channel / 4L) * 4L;
> +	shift = 8L * (channel % 4L);
> +	oldvalue = readl((void *)address);
> +	oldvalue &= (0xff << shift);	/* clear other bits */
> +	return (oldvalue >> shift);
> +}
> +
> +void clear_int(int channel, u_char value, void *ioaddr)
> +{
> +	void *address;
> +	unsigned shift, longvalue;
> +	DBG_PRINT("clear_int\n");
> +	address = ioaddr + reg_first_int_status + (channel / 4L) * 4L;
> +	shift = 8L * (channel % 4L);
> +	longvalue = value << shift;
> +	writel(~longvalue, (void *)address);
> +}
> +
> +u_char get_int_status(int channel, void *ioaddr)
> +{
> +	void *address;
> +	unsigned shift, oldvalue;
> +	DBG_PRINT("get_int_status\n");
> +	address = ioaddr + reg_first_int_status + (channel / 4L) * 4L;
> +	shift = 8L * (channel % 4L);
> +	oldvalue = readl((void *)address);
> +	oldvalue &= (0xff << shift);	/* clear other bits */
> +	return (oldvalue >> shift);
> +}
> +
> +void fillregisterswith_00(void *ioaddr)
> +{
> +	DBG_PRINT("fillregisterswith_00\n");
> +	writel(0x0, (void *)(ioaddr + reg_first_rxctrl));
> +	writel(0x0, (void *)(ioaddr + reg_first_txctrl));
> +	writel(0x0, (void *)(ioaddr + reg_first_int_mask));
> +	writel(0x0, (void *)(ioaddr + reg_first_int_status));
> +	writel(0x0, (void *)(ioaddr + first_rx_desc));
> +	writel(0x0, (void *)(ioaddr + first_tx_desc));
> +}
> +
> +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
> +static int fepci_open(struct net_device *dev);
> +static void fepci_timer(unsigned long data);
> +static void fepci_tx_timeout(struct net_device *dev);
> +static void fepci_init_ring(struct net_device *dev);
> +static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev);
> +static irqreturn_t fepci_interrupt(int irq, void *dev_instance);
> +static int fepci_rx(struct net_device *dev);
> +static int fepci_close(struct net_device *dev);
> +static struct net_device_stats *fepci_get_stats(struct net_device *dev);
> +static void set_rx_mode(struct net_device *dev);
> +static void fepci_remove_one(struct pci_dev *pdev);
> +
> +/* proc filesystem functions introduced: */
> +
> +static int fepci_proc_init_driver(void);
> +static void fepci_proc_cleanup_driver(void);
> +static void fepci_proc_init_card(int card_number, void *card_data);
> +static void fepci_proc_cleanup_card(int card_number);
> +#ifdef DEBUG_PROC_FILES
> +static void fepci_proc_init_channel(int card_number, int channel_number,
> +				    void *channel_data);
> +static void fepci_proc_cleanup_channel(int card_number, int channel_number);
> +#endif /* DEBUG_PROC_FILES */
> +
> +/* char device operations: */
> +
> +ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
> +			loff_t *f_pos);
> +int fepci_char_open(struct inode *inode, struct file *filp);
> +int fepci_char_release(struct inode *inode, struct file *filp);
> +int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma);
> +int fepci_char_ioctl(struct inode *inode, struct file *filp,
> +		     unsigned int cmd, unsigned long arg);
> +
> +struct file_operations fepci_char_fops = {
> +read:		fepci_char_read,
> +ioctl:		fepci_char_ioctl,
> +open:		fepci_char_open,
> +release:	fepci_char_release,
> +mmap:		fepci_char_mmap,
> +};

Please use C99 style initializers not old Gcc style.

> +int fepci_char_open(struct inode *inode, struct file *filp)
> +{
> +	unsigned int minor = MINOR(inode->i_rdev);
> +	DBG_PRINT("fepci_char_open\n");
> +	if (unlikely(minor >= find_cnt || card_privates[minor].pci_dev == NULL))
> +		return -ENXIO;
> +	filp->f_op = &fepci_char_fops;
> +	if (unlikely(!try_module_get(THIS_MODULE)))
> +		return -EBUSY;
> +	return 0;
> +}

If you use owner properly on proc files, you won't need to be
dorking with module ref counts.

> +
> +int fepci_char_release(struct inode *inode, struct file *filp)
> +{
> +	DBG_PRINT("fepci_char_release\n");
> +	module_put(THIS_MODULE);
> +	return 0;
> +}
> +
> +void fepci_vma_open(struct vm_area_struct *vma)
> +{
> +	DBG_PRINT("fepci_vma_open\n");
> +}
> +
> +void fepci_vma_close(struct vm_area_struct *vma)
> +{
> +	DBG_PRINT("fepci_vma_close\n");
> +	module_put(THIS_MODULE);
> +}
> +
> +static struct vm_operations_struct fepci_vm_ops = {
> +open:	fepci_vma_open,
> +close:	fepci_vma_close,
> +};
> +
> +int fepci_char_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> +	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
> +	unsigned long size = vma->vm_end - vma->vm_start;
> +
> +	unsigned long virtual_address = 0;
> +
> +	vma->vm_flags |= VM_IO | VM_RESERVED;
> +	vma->vm_ops = &fepci_vm_ops;
> +	vma->vm_file = filp;
> +
> +	if (offset == STREAM_BUFFER_POINTER_AREA) {
> +		virtual_address = stream_pointers;
> +		if (virtual_address == 0) {
> +			printk(KERN_WARNING "%s: mmap: internal error.\n",
> +			       fepci_name);
> +			return -ENOMEM;
> +		}
> +		if (size > (1 << PAGE_SHIFT)) {
> +			printk(KERN_WARNING
> +			       "%s: mmap: area size over range.\n", fepci_name);
> +			return -EINVAL;
> +		}
> +	} else {
> +		unsigned int page;
> +
> +		unsigned int card;
> +		unsigned int channel;
> +		unsigned int area;	/* 0=rx, 1=tx */
> +
> +		card = (offset >> CARD_ADDRESS_SHIFT) & 0xf;
> +		channel = (offset >> CHANNEL_ADDRESS_SHIFT) & 0xf;
> +		area = (offset >> AREA_ADDRESS_SHIFT) & 0xf;
> +		page = (offset & 0xffff);	/* >> PAGE_SHIFT; */
> +
> +		if (area == 0) {
> +			/* if there really is such card */
> +			if (card < find_cnt && card_privates[card].pci_dev)
> +				virtual_address =
> +				    (unsigned long)card_privates[card].
> +				    ch_privates[channel]->rx_buffer;
> +			else
> +				goto INVALID;
> +		} else if (area == 1) {
> +			/* if there really is such card */
> +			if (card < find_cnt && card_privates[card].pci_dev)
> +				virtual_address =
> +				    (unsigned long)card_privates[card].
> +				    ch_privates[channel]->tx_buffer;
> +			else
> +				goto INVALID;
> +		} else {
> +INVALID:
> +			DBG_PRINT("%s: mmap: invalid address 0x%lx\n",
> +				  fepci_NAME, virtual_address);
> +			return -EINVAL;
> +		}
> +		if (virtual_address == 0)
> +			goto INVALID;
> +	}
> +
> +	if (!try_module_get(THIS_MODULE))
> +		return -EBUSY;
> +
> +	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> +	{
> +		unsigned pfn = PFN_DOWN(virt_to_phys((void *)virtual_address));
> +		int error = io_remap_pfn_range(vma, vma->vm_start, pfn,
> +					       size, vma->vm_page_prot);
> +		if (unlikely(error))
> +			return error;
> +	}
> +	fepci_vma_open(vma);
> +	return 0;
> +}
> +
> +/* mmap operations end */
> +
> +/* char operations start */
> +
> +void fepci_copy_to_user(unsigned long to, void *from, unsigned long len,
> +			int shrink)
> +{
> +	unsigned int i;
> +	DBG_PRINT("fepci_copy_to_user\n");
> +	if (shrink) {
> +		for (i = 0; i < len; i += 2) {
> +			put_user((((unsigned long *)from)[i / 2]) & 0xff,
> +				 (unsigned char *)(to + i));
> +			put_user((((unsigned long *)from)[i / 2]) >> 8,
> +				 (unsigned char *)(to + i + 1));
> +		}
> +	} else {
> +		for (i = 0; i < len; i += 4)
> +			put_user(((unsigned long *)from)[i / 4],
> +				 (unsigned long *)(to + i));
> +	}
> +}
> +
> +void fepci_copy_from_user(void *to, unsigned long from, unsigned long len,
> +			  int enlarge)
> +{
> +	unsigned int i;
> +	if (enlarge) {
> +		for (i = 0; i < len; i += 2) {
> +			unsigned char temp1;
> +			unsigned char temp2;
> +			get_user(temp1, (unsigned char *)(from + i));
> +			get_user(temp2, (unsigned char *)(from + i + 1));
> +			*((unsigned long *)(to + i * 2)) = temp1 + (temp2 << 8);
> +		}
> +	} else {
> +		for (i = 0; i < len; i += 4)
> +			get_user(((unsigned long *)to)[i / 4],
> +				 (unsigned long *)(from + i));
> +	}
> +}
> +
> +unsigned get_semafore(struct fepci_real_mailbox *mailbox)
> +{
> +	unsigned semafore = readb(&mailbox->Semafore_Mail_number);
> +	DBG_PRINT("get_semafore = %x\n", semafore);
> +	return semafore;
> +}
> +
> +int set_semafore(struct fepci_real_mailbox *mailbox, unsigned semafore)
> +{
> +	unsigned number = readl(&mailbox->Semafore_Mail_number);
> +	DBG_PRINT("got number %u at %p.\n", number,
> +		  &mailbox->Semafore_Mail_number);
> +	number = ((number & ~0xFF) | semafore) + (1 << 8);
> +	DBG_PRINT
> +	    ("increases the mail number to %u at the same time at %p.\n",
> +	     number, &mailbox->Semafore_Mail_number);
> +	writel(number, &mailbox->Semafore_Mail_number);
> +	DBG_PRINT("set_semafore %p, %u returns 0.\n", mailbox, semafore);
> +	return 0;
> +}
> +
> +static void fepci_mailbox_timer(unsigned long data)
> +{
> +	int card_number = data;
> +	unsigned int *saved_pid = &card_privates[card_number].ioctl_saved_pid;
> +	void *ioaddr = card_privates[card_number].ioaddr;
> +	struct fepci_real_mailbox *real_mailbox =
> +	    (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
> +
> +	set_semafore(real_mailbox, 0x0);
> +	*saved_pid = 0;
> +}
> +
> +int fepci_char_ioctl(struct inode *inode, struct file *filp,
> +		     unsigned int cmd, unsigned long arg)
> +{
> +	unsigned int minor = MINOR(inode->i_rdev);
> +	void *ioaddr;
> +	struct fepci_real_mailbox *real_mailbox;
> +	int retval = 0;
> +	unsigned int *saved_pid;
> +	unsigned int my_pid;
> +
> +	if (minor >= find_cnt || card_privates[minor].pci_dev == NULL) {
> +		printk(KERN_WARNING
> +		       "%s: trying to access a card that does not exist.\n",
> +		       fepci_NAME);
> +		/* if trying to access a card that does not exist */
> +		return -ENXIO;
> +	}
> +
> +	DBG_PRINT("fepci_char_ioctl minor %u.\n", minor);
> +
> +	if (_IOC_DIR(cmd) & _IOC_READ)
> +		if (!access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)))
> +			return -EFAULT;
> +	if (_IOC_DIR(cmd) & _IOC_WRITE)
> +		if (!access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)))
> +			return -EFAULT;
> +
> +	ioaddr = card_privates[minor].ioaddr;
> +	real_mailbox =
> +	    (struct fepci_real_mailbox *)(ioaddr + FEPCI_MAILBOX_OFFSETT);
> +	saved_pid = &card_privates[minor].ioctl_saved_pid;
> +	my_pid = current->pid;
> +
> +	switch (cmd) {
> +	case FEPCI_IOCTL_STREAM_TRANSMIT_POLL:
> +		/* here: arg == channel number */
> +		if (arg < 0 || arg >= CHANNELS
> +		    || !(card_privates[minor].ch_privates[arg]->stream_on))
> +			return 0x2;
> +		{
> +			u32 pointer = *USER_TX_S_FAKE_POINTER(minor, arg,
> +							      stream_pointers);
> +			wait_event_interruptible((card_privates[minor].
> +						  stream_transmit_q)
> +						 ,
> +						 (pointer !=
> +						  *USER_TX_S_FAKE_POINTER
> +						  (minor, arg,
> +						   stream_pointers)));
> +			return 0x1;
> +		}
> +		return retval;
> +	case FEPCI_IOCTL_STREAM_RECEIVE_POLL:
> +		/* here: arg == channel number */
> +		if (arg < 0 || arg >= CHANNELS
> +		    || !(card_privates[minor].ch_privates[arg]->stream_on))
> +			return 0x2;
> +		{
> +			u32 pointer = *USER_RX_S_FAKE_POINTER(minor, arg,
> +							      stream_pointers);
> +			wait_event_interruptible((card_privates[minor].
> +						  stream_receive_q)
> +						 ,
> +						 (pointer !=
> +						  *USER_RX_S_FAKE_POINTER
> +						  (minor, arg,
> +						   stream_pointers)));
> +			retval = 0x1;
> +		}
> +		return retval;
> +	case FEPCI_IOCTL_STREAM_BOTH_POLL:
> +		/* here: arg == channel number */
> +		if (arg < 0 || arg >= CHANNELS
> +		    || !(card_privates[minor].ch_privates[arg]->stream_on))
> +			return 0x2;
> +		{
> +			u32 temp_tx_pointer =
> +			    *USER_TX_S_FAKE_POINTER(minor, arg,
> +						    stream_pointers);
> +			u32 temp_rx_pointer =
> +			    *USER_RX_S_FAKE_POINTER(minor, arg,
> +						    stream_pointers);
> +
> +			wait_event_interruptible((card_privates[minor].
> +						  stream_both_q)
> +						 ,
> +						 (temp_tx_pointer !=
> +						  *USER_TX_S_FAKE_POINTER
> +						  (minor, arg, stream_pointers))
> +						 || (temp_rx_pointer !=
> +						     *USER_RX_S_FAKE_POINTER
> +						     (minor, arg,
> +						      stream_pointers)));
> +			retval = 0x1;
> +		}
> +		return retval;
> +	case FEPCI_IOCTL_R_SHARED_MEM:
> +		DBG_PRINT(" %s: ioctl read shared mem commanded.\n",
> +			  fepci_NAME);
> +		fepci_copy_to_user(arg, ioaddr + FEPCI_SHARED_MEM_OFFSETT,
> +				   _IOC_SIZE(cmd), 0);
> +		break;
> +	case FEPCI_IOCTL_W_SHARED_MEM:
> +		DBG_PRINT(" %s: ioctl write shared mem commanded.\n",
> +			  fepci_NAME);
> +		fepci_copy_from_user(ioaddr + FEPCI_SHARED_MEM_OFFSETT,
> +				     arg, _IOC_SIZE(cmd), 0);
> +		break;
> +	case FEPCI_IOCTL_G_IDENTIFICATION:
> +		DBG_PRINT(" %s: IOCTL_G_IDENTIFICATION commanded.\n",
> +			  fepci_NAME);
> +		fepci_copy_to_user(arg,
> +				   ioaddr + FEPCI_IDENTIFICATION_OFFSETT,
> +				   _IOC_SIZE(cmd), 1);
> +		break;
> +	case FEPCI_IOCTL_G_FEATURES:
> +		DBG_PRINT(" %s: IOCTL_G_FEATURES commanded.\n", fepci_NAME);
> +		fepci_copy_to_user(arg, ioaddr + FEPCI_FEATURES_OFFSETT,
> +				   _IOC_SIZE(cmd), 1);
> +		break;
> +	case FEPCI_IOCTL_G_SETTINGS:
> +		DBG_PRINT(" %s: IOCTL_G_SETTINGS commanded.\n", fepci_NAME);
> +		fepci_copy_to_user(arg, ioaddr + FEPCI_SETTINGS_OFFSETT,
> +				   _IOC_SIZE(cmd), 1);
> +		break;
> +	case FEPCI_IOCTL_G_STATUS:
> +		DBG_PRINT(" %s: IOCTL_G_STATUS commanded.\n", fepci_NAME);
> +		fepci_copy_to_user(arg, ioaddr + FEPCI_STATUS_OFFSETT,
> +				   _IOC_SIZE(cmd), 1);
> +		break;
> +	case FEPCI_IOCTL_B_POLL:
> +		DBG_PRINT(" %s: IOCTL_B_POLL commanded.\n", fepci_NAME);
> +		retval = get_semafore(real_mailbox);
> +		if ((retval == 0x20 || retval == 0x21 || retval == 0x40)
> +		    && *saved_pid != my_pid)
> +			retval = 0x7;
> +		del_timer_sync(&card_privates[minor].mailbox_timer);
> +		card_privates[minor].mailbox_timer.expires = jiffies + 20 * HZ;
> +		card_privates[minor].mailbox_timer.data = minor;
> +		card_privates[minor].mailbox_timer.function =
> +		    &fepci_mailbox_timer;
> +		add_timer(&card_privates[minor].mailbox_timer);
> +		break;
> +	case FEPCI_IOCTL_B_GRAB:
> +		DBG_PRINT("%s: IOCTL_B_GRAB commanded.\n", fepci_NAME);
> +		if ((my_pid != *saved_pid) && (*saved_pid != 0)) {
> +			retval = 0x2;
> +			break;
> +		}
> +		DBG_PRINT("%s: IOCTL_B_GRAB getting semaphore.\n", fepci_NAME);
> +		if (get_semafore(real_mailbox) == 0x0) {
> +			DBG_PRINT("%s: IOCTL_B_GRAB setting semaphore.\n",
> +				  fepci_NAME);
> +			set_semafore(real_mailbox, 0x40);
> +			DBG_PRINT("%s: IOCTL_B_GRAB sleeping.\n", fepci_NAME);
> +			msleep(1);	/* delay at least 1 millisecond */
> +			DBG_PRINT
> +			    ("%s: IOCTL_B_GRAB getting semaphore again.\n",
> +			     fepci_NAME);
> +			switch (get_semafore(real_mailbox)) {
> +			case 0x40:
> +				retval = 0x0;
> +				DBG_PRINT
> +				    ("%s: IOCTL_B_GRAB saving pid to %p.\n",
> +				     fepci_NAME, saved_pid);
> +				*saved_pid = my_pid;
> +				DBG_PRINT
> +				    ("%s: IOCTL_B_GRAB deleting timer at %p.\n",
> +				     fepci_NAME,
> +				     &card_privates[minor].mailbox_timer);
> +				del_timer_sync(&card_privates[minor].
> +					       mailbox_timer);
> +				card_privates[minor].mailbox_timer.
> +				    expires = jiffies + 20 * HZ;
> +				card_privates[minor].mailbox_timer.data = minor;
> +				card_privates[minor].mailbox_timer.
> +				    function = &fepci_mailbox_timer;
> +				add_timer(&card_privates[minor].mailbox_timer);
> +				break;
> +			case 0x10:
> +			case 0x11:
> +			case 0x80:
> +				retval = 0x1;
> +				break;
> +			default:
> +				retval = 0xff;
> +			}
> +		} else {
> +			switch (get_semafore(real_mailbox)) {
> +			case 0x10:
> +			case 0x11:
> +			case 0x80:
> +				retval = 0x1;
> +				break;
> +			default:
> +				retval = 0xff;
> +			}
> +		}
> +		break;
> +	case FEPCI_IOCTL_B_RELEASE:
> +		DBG_PRINT(" %s: IOCTL_B_RELEASE commanded.\n", fepci_NAME);
> +		if (my_pid != *saved_pid) {
> +			retval = 0x2;
> +			break;
> +		}
> +		switch (get_semafore(real_mailbox)) {
> +		case 0x40:
> +		case 0x20:
> +			retval = 0x0;
> +			set_semafore(real_mailbox, 0x0);
> +			*saved_pid = 0;
> +			del_timer(&card_privates[minor].mailbox_timer);
> +			break;
> +		case 0x21:
> +			retval = 0x04;
> +			break;
> +		case 0x10:
> +		case 0x11:
> +		case 0x80:
> +			retval = 0x1;
> +			break;
> +		default:
> +			retval = 0xff;
> +		}
> +		break;
> +	case FEPCI_IOCTL_B_S_CMAIL:
> +		DBG_PRINT(" %s: IOCTL_B_S_CMAIL commanded.\n", fepci_NAME);
> +		if (my_pid != *saved_pid) {
> +			retval = 0x2;
> +			break;
> +		}
> +		switch (get_semafore(real_mailbox)) {
> +		case 0x40:
> +		case 0x20:
> +		case 0x21:
> +			/* copy the mailbox */
> +			fepci_copy_from_user(ioaddr +
> +					     FEPCI_MAILBOX_OFFSETT + 4,
> +					     arg + 2, _IOC_SIZE(cmd) - 2, 1);
> +			/* semafore -> 10 */
> +			set_semafore(real_mailbox, 0x10);
> +			retval = 0x0;
> +
> +			del_timer_sync(&card_privates[minor].mailbox_timer);
> +			card_privates[minor].mailbox_timer.expires =
> +			    jiffies + 20 * HZ;
> +			card_privates[minor].mailbox_timer.data = minor;
> +			card_privates[minor].mailbox_timer.function =
> +			    &fepci_mailbox_timer;
> +			add_timer(&card_privates[minor].mailbox_timer);
> +
> +			break;
> +
> +		case 0x10:
> +		case 0x11:
> +		case 0x80:
> +			retval = 0x1;
> +			break;
> +		case 0x0:
> +			retval = 0x3;
> +			break;
> +		default:
> +			retval = 0xff;
> +		}
> +		break;
> +	case FEPCI_IOCTL_B_S_QMAIL:
> +		DBG_PRINT(" %s: IOCTL_B_S_QMAIL commanded.\n", fepci_NAME);
> +		if (my_pid != *saved_pid) {
> +			retval = 0x2;
> +			break;
> +		}
> +		switch (get_semafore(real_mailbox)) {
> +		case 0x40:
> +		case 0x20:
> +		case 0x21:
> +			/* copy the mailbox; */
> +			fepci_copy_from_user(ioaddr +
> +					     FEPCI_MAILBOX_OFFSETT + 4,
> +					     arg + 2, _IOC_SIZE(cmd) - 2, 1);
> +			/* semafore -> 11 */
> +			set_semafore(real_mailbox, 0x11);
> +			retval = 0x0;
> +
> +			del_timer_sync(&card_privates[minor].mailbox_timer);
> +			card_privates[minor].mailbox_timer.expires =
> +			    jiffies + 20 * HZ;
> +			card_privates[minor].mailbox_timer.data = minor;
> +			card_privates[minor].mailbox_timer.function =
> +			    &fepci_mailbox_timer;
> +			add_timer(&card_privates[minor].mailbox_timer);
> +
> +			break;
> +
> +		case 0x10:
> +		case 0x11:
> +		case 0x80:
> +			retval = 0x1;
> +			break;
> +		case 0x0:
> +			retval = 0x3;
> +			break;
> +		default:
> +			retval = 0xff;
> +		}
> +		break;
> +	case FEPCI_IOCTL_B_G_MAIL:
> +		DBG_PRINT(" %s: IOCTL_B_G_MAIL commanded.\n", fepci_NAME);
> +		if (my_pid != *saved_pid) {
> +			retval = 0x2;
> +		} else {
> +			switch (get_semafore(real_mailbox)) {
> +			case 0x10:
> +			case 0x11:
> +			case 0x80:
> +				retval = 0x1;
> +				break;
> +			case 0x40:
> +			case 0x20:
> +			case 0x21:
> +				retval = 0x0;
> +				fepci_copy_to_user(arg,
> +						   ioaddr +
> +						   FEPCI_MAILBOX_OFFSETT,
> +						   _IOC_SIZE(cmd), 1);
> +
> +				del_timer_sync(&card_privates[minor].
> +					       mailbox_timer);
> +				card_privates[minor].mailbox_timer.
> +				    expires = jiffies + 20 * HZ;
> +				card_privates[minor].mailbox_timer.data = minor;
> +				card_privates[minor].mailbox_timer.
> +				    function = &fepci_mailbox_timer;
> +				add_timer(&card_privates[minor].mailbox_timer);
> +
> +				break;
> +			case 0x0:
> +				retval = 0x3;
> +				break;
> +			default:
> +				retval = 0xff;
> +			}
> +		}
> +		if (retval != 0) {
> +			static unsigned char seven = 7;
> +			/* copy four lowest bytes from the mailbox */
> +			fepci_copy_to_user(arg,
> +					   ioaddr + FEPCI_MAILBOX_OFFSETT,
> +					   4, 1);
> +			/* lowest byte = 0x7 */
> +			__put_user(arg, &seven);
> +		}
> +		break;
> +	case FEPCI_IOCTL_ALARM_MANAGER:
> +		DBG_PRINT(" %s: IOCTL_ALARM_MANAGER commanded.\n", fepci_NAME);
> +		interruptible_sleep_on(&(card_privates[minor].
> +					 alarm_manager_wait_q));
> +		return retval;
> +	default:
> +		DBG_PRINT(" %s: Unknown ioctl command 0x%x.\n", fepci_NAME,
> +			  cmd);
> +		return -ENOTTY;
> +	}
> +	return retval;
> +}
> +
> +ssize_t fepci_char_read(struct file *filp, char *buf, size_t count,
> +			loff_t *f_pos)
> +{
> +	DBG_PRINT("fepci_char_read\n");
> +	if (count > 1)
> +		count = 1;
> +	if (unlikely(copy_to_user(buf, "\n", count)))
> +		return -EFAULT;
> +	return count;
> +}
> +
> +static int fepci_register_char_device(void)
> +{
> +	int error =
> +	    register_chrdev(0 /* dynamic */ , fepci_name, &fepci_char_fops);
> +	if (unlikely(error < 0))
> +		printk(KERN_WARNING
> +		       "%s: unable to register char device.\n", fepci_NAME);
> +	else
> +		DBG_PRINT("%s: registered char device, major:0x%x.\n",
> +			  fepci_NAME, error);
> +	return error;
> +}
> +
> +void fepci_unregister_char_device(void)
> +{
> +	unregister_chrdev(major, fepci_name);
> +}
> +
> +/* char operations end */
> +
> +/* stream operations start */
> +
> +static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance);
> +static int fepci_stream_close(struct net_device *dev);
> +
> +static int fepci_stream_open(struct net_device *dev)
> +{
> +	struct fepci_ch_private *fp = dev->priv;
> +	unsigned tx_pages, rx_pages, tx_order, rx_order;
> +	unsigned page_number;
> +	unsigned int i;
> +
> +	down_write(&fp->this_card_priv->semaphore);
> +	if (fp->in_eth_mode) {
> +		up_write(&fp->this_card_priv->semaphore);
> +		printk(KERN_WARNING
> +		       "%s: Interface is in Ethernet mode, "
> +		       "cannot open stream interface.\n", fepci_NAME);
> +BUSY:
> +		return -EBUSY;
> +	}
> +	if (fp->in_stream_mode) {
> +		up_write(&fp->this_card_priv->semaphore);
> +		printk(KERN_WARNING
> +		       "%s: Interface is already in stream mode, "
> +		       "cannot open stream interface.\n", fepci_NAME);
> +		goto BUSY;
> +	}
> +
> +	if (unlikely(fp->this_card_priv->pci_dev == NULL)) {
> +		up_write(&fp->this_card_priv->semaphore);
> +		return -ENXIO;
> +	}
> +
> +	fp->bufsize = 1 << fp->bufsize_order;
> +
> +	if (fp->fake_unit_sz_order < 5) {
> +		up_write(&fp->this_card_priv->semaphore);
> +		printk(KERN_WARNING
> +		       "%s: Unit size has to be at least 32 Bytes.\n",
> +		       fepci_NAME);
> +INVALID:
> +		return (-EINVAL);
> +	}
> +
> +	if (fp->fake_unit_sz_order >= fp->bufsize_order) {
> +		up_write(&fp->this_card_priv->semaphore);
> +		printk(KERN_WARNING
> +		       "%s: Bufsize has to be greater than unit size.\n",
> +		       fepci_NAME);
> +		goto INVALID;
> +	}
> +
> +	if (fp->fake_unit_sz_order >= MAX_UNIT_SZ_ORDER) {
> +		fp->unit_sz_order = MAX_UNIT_SZ_ORDER;
> +	} else {
> +		fp->unit_sz_order = fp->fake_unit_sz_order;
> +	}
> +
> +	fp->fake_unit_sz = 1 << fp->fake_unit_sz_order;
> +	fp->unit_sz = 1 << fp->unit_sz_order;
> +	fp->units = 1 << (fp->bufsize_order - fp->unit_sz_order);
> +	fp->fake_units = 1 << (fp->bufsize_order - fp->fake_unit_sz_order);
> +
> +	/* reserve memory */
> +	if (fp->bufsize_order < PAGE_SHIFT) {
> +		rx_order = 0;
> +		tx_order = 0;
> +		rx_pages = 1;
> +		tx_pages = 1;
> +	} else {
> +		tx_order = fp->bufsize_order - PAGE_SHIFT;
> +		tx_pages = 1 << tx_order;
> +		rx_order = tx_order + 1;
> +		rx_pages = 1 << rx_order;
> +	}
> +	fp->in_stream_mode = 1;
> +	fp->tx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, tx_order);
> +	if (!fp->tx_buffer)
> +		goto NO_MEMORY;
> +	fp->rx_buffer = (u32 *) __get_dma_pages(GFP_KERNEL, rx_order);
> +	if (!fp->rx_buffer) {
> +NO_MEMORY:
> +		up_write(&fp->this_card_priv->semaphore);
> +		printk(KERN_WARNING
> +		       "%s: unable to allocate memory for buffers.\n",
> +		       fepci_NAME);
> +		fepci_stream_close(dev);
> +		return (-ENOMEM);
> +	}
> +
> +	for (page_number = 0; page_number < rx_pages; page_number++)
> +		/* make pages reserved to allow remappping pages
> +		   with io_remap_pfn_range */
> +		SetPageReserved(virt_to_page
> +				((unsigned long)fp->rx_buffer +
> +				 (page_number << PAGE_SHIFT)));
> +	for (page_number = 0; page_number < tx_pages; page_number++)
> +		/* make pages reserved to allow remappping pages
> +		   with io_remap_pfn_range */
> +		SetPageReserved(virt_to_page
> +				((unsigned long)fp->tx_buffer +
> +				 (page_number << PAGE_SHIFT)));
> +
> +	for (i = 0; i < (fp->bufsize) / 4; i++)
> +		fp->tx_buffer[i] = 0xffffffff;
> +
> +	/* + fp->channel_number; */
> +	*USER_RX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
> +			   stream_pointers) = 0;
> +	/* + fp->channel_number; */
> +	*USER_TX_S_POINTER(fp->this_card_priv->card_number, fp->channel_number,
> +			   stream_pointers) = 0;
> +	/* + fp->channel_number; */
> +	*USER_RX_S_FAKE_POINTER(fp->this_card_priv->card_number,
> +				fp->channel_number, stream_pointers) = 0;
> +	/* + fp->channel_number; */
> +	*USER_TX_S_FAKE_POINTER(fp->this_card_priv->card_number,
> +				fp->channel_number, stream_pointers) = 0;
> +
> +	DBG_PRINT("%s: Bufsize is 0x%x.\n", fepci_NAME, fp->bufsize);
> +	DBG_PRINT("%s: Unit_size is 0x%x.\n", fepci_NAME, fp->unit_sz);
> +	DBG_PRINT("%s: Number of units is 0x%x.\n", fepci_NAME, fp->units);
> +
> +	DBG_PRINT("%s: Fake_unit_size is 0x%x.\n", fepci_NAME,
> +		  fp->fake_unit_sz);
> +	DBG_PRINT("%s: Number of fake units is 0x%x.\n", fepci_NAME,
> +		  fp->fake_units);
> +
> +	/* init ring buffers */
> +	for (i = 0; i < MAX_RX_UNITS; i++)
> +		fp->rx_unit[i] =
> +		    (u32 *) ((u32) (fp->rx_buffer) + (fp->unit_sz * i));
> +	for (i = 0; i < MAX_TX_UNITS; i++)
> +		fp->tx_unit[i] =
> +		    (u32 *) ((u32) (fp->tx_buffer) + (fp->unit_sz * i));
> +
> +	for (i = 0; i < RX_RING_SIZE; i++) {
> +		writel(0, &fp->rx_desc[i].desc_a);
> +		writel(0, &fp->rx_desc[i].desc_b);
> +	}
> +	for (i = 0; i < TX_RING_SIZE; i++) {
> +		writel(0, &fp->tx_desc[i].desc_a);
> +		writel(0, &fp->tx_desc[i].desc_b);
> +	}
> +
> +	up_write(&fp->this_card_priv->semaphore);
> +	return 0;
> +}
> +
> +static int fepci_stream_start(struct net_device *dev)
> +{
> +	struct fepci_ch_private *fp = dev->priv;
> +	unsigned i;
> +	down_write(&fp->this_card_priv->semaphore);
> +
> +	if (fp->in_stream_mode == 0) {
> +		up_write(&fp->this_card_priv->semaphore);
> +		printk(KERN_WARNING
> +		       "%s: Interface is not in stream mode, "
> +		       "streaming cannot be started.\n", fepci_NAME);
> +		return (-EBUSY);
> +	}
> +	if (fp->stream_on) {
> +		up_write(&fp->this_card_priv->semaphore);
> +		printk(KERN_WARNING
> +		       "%s: Streaming is already on, "
> +		       "streaming cannot be started.\n", fepci_NAME);
> +		return (-EBUSY);
> +	}
> +
> +	{
> +		/* reserve irq */
> +		int error = request_irq(dev->irq, &fepci_stream_interrupt,
> +					IRQF_SHARED, dev->name, dev);
> +		if (error) {
> +			up_write(&fp->this_card_priv->semaphore);
> +			printk(KERN_WARNING
> +			       "%s: unable to allocate IRQ %d, error 0x%x\n",
> +			       fepci_NAME, dev->irq, error);
> +			return -ENOMEM;
> +		}
> +	}
> +
> +	fp->stream_on = 1;
> +
> +	/* sending &receiving on, start from the beginning of the buffer */
> +	fp->cur_tx_unit = 0;
> +	fp->cur_rx_unit = 0;
> +	fp->cur_tx = 0;
> +	fp->cur_rx = 0;
> +
> +	/* all the descriptors ready to go: */
> +	for (i = 0; i < min(RX_RING_SIZE, TX_RING_SIZE); i++) {
> +		dma_addr_t address = pci_map_single(fp->this_card_priv->pci_dev,
> +						    fp->
> +						    rx_unit[(fp->cur_rx_unit +
> +							     i) % fp->units],
> +						    fp->unit_sz,
> +						    PCI_DMA_FROMDEVICE);
> +		if (unlikely(pci_dma_mapping_error(address)))
> +			printk(KERN_WARNING
> +			       "%s: failed to map DMA buffer.\n", fepci_NAME);
> +		else {
> +			writel(address,
> +			       &fp->rx_desc[(fp->cur_rx + i) &
> +					    (RX_RING_SIZE - 1)].desc_a);
> +			if (!
> +			    (readl
> +			     (&fp->
> +			      rx_desc[(fp->cur_rx + i) & (RX_RING_SIZE -
> +							  1)].
> +			      desc_b) & enable_transfer))
> +				writel(enable_transfer,
> +				       &fp->rx_desc[(fp->cur_rx + i) %
> +						    RX_RING_SIZE].desc_b);
> +		}
> +		address =
> +		    pci_map_single(fp->this_card_priv->pci_dev,
> +				   fp->tx_unit[(fp->cur_tx_unit + i) %
> +					       fp->units], fp->unit_sz,
> +				   PCI_DMA_TODEVICE);
> +		writel(address,
> +		       &fp->
> +		       tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE - 1)].desc_a);
> +		if (unlikely(pci_dma_mapping_error(address)))
> +			printk(KERN_WARNING
> +			       "%s: failed to map DMA buffer.\n", fepci_NAME);
> +		else {
> +			if (!
> +			    (readl
> +			     (&fp->
> +			      tx_desc[(fp->cur_tx + i) & (TX_RING_SIZE -
> +							  1)].
> +			      desc_b) & enable_transfer))
> +				writel(enable_transfer |
> +				       (fp->unit_sz & frame_length),
> +				       &fp->
> +				       tx_desc[(fp->cur_tx +
> +						i) & (TX_RING_SIZE -
> +						      1)].desc_b);
> +		}
> +	}
> +
> +	/* irq on */
> +	set_int_mask(fp->channel_number,
> +		     MaskFrameReceived | MaskFrameTransmitted |
> +		     MaskRxFifoError | MaskRxFrameDroppedError |
> +		     MaskTxFifoError, fp->this_card_priv);
> +	{
> +		void *ioaddr = (void *)dev->base_addr;
> +		/* Start Rx and Tx channels */
> +		writel(Receive_enable |
> +		       (Rx_fifo_threshold & RX_FIFO_THRESHOLD_STREAM_MODE),
> +		       (void *)(ioaddr + fp->reg_rxctrl));
> +		writel((Transmit_enable |
> +			(Tx_desc_threshold &
> +			 (TX_DESC_THRESHOLD_STREAM_MODE << 8)) |
> +			(Tx_fifo_threshold & TX_FIFO_THRESHOLD_STREAM_MODE)),
> +		       (void *)(ioaddr + fp->reg_txctrl));
> +	}
> +	up_write(&fp->this_card_priv->semaphore);
> +
> +	return 0;
> +}
> +
> +static int fepci_stream_stop(struct net_device *dev)
> +{
> +	struct fepci_ch_private *fp = dev->priv;
> +	void *ioaddr = (void *)dev->base_addr;
> +	down_write(&fp->this_card_priv->semaphore);
> +	if (fp->in_stream_mode == 0) {
> +		up_write(&fp->this_card_priv->semaphore);
> +		return (1);
> +	}
> +	fp->stream_on = 0;
> +	/* Stop Rx and Tx channels. */
> +	writel(0x0, (void *)(ioaddr + fp->reg_rxctrl));
> +	writel(0x0, (void *)(ioaddr + fp->reg_txctrl));
> +
> +	/* Disable interrupts by clearing the interrupt mask. */
> +	set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
> +
> +	/* unregister irq */
> +	free_irq(dev->irq, dev);
> +
> +	{
> +		unsigned i = min(RX_RING_SIZE, TX_RING_SIZE) - 1;
> +		do {
> +			dma_addr_t bus_address = readl(&fp->rx_desc[i].desc_a);
> +			if (likely(!pci_dma_mapping_error(bus_address)))
> +				pci_unmap_single(fp->this_card_priv->
> +						 pci_dev, bus_address,
> +						 fp->unit_sz,
> +						 PCI_DMA_FROMDEVICE);
> +			bus_address = readl(&fp->tx_desc[i].desc_a);
> +			if (likely(!pci_dma_mapping_error(bus_address)))
> +				pci_unmap_single(fp->this_card_priv->
> +						 pci_dev, bus_address,
> +						 fp->unit_sz, PCI_DMA_TODEVICE);
> +		}
> +		while (i--);
> +	}
> +
> +	up_write(&fp->this_card_priv->semaphore);
> +	return 0;
> +}
> +
> +static int fepci_stream_close(struct net_device *dev)
> +{
> +	struct fepci_ch_private *fp = dev->priv;
> +	unsigned rx_pages, tx_pages, rx_order, tx_order;
> +
> +	if (fepci_stream_stop(dev))
> +		return -ENODEV;
> +	down_write(&fp->this_card_priv->semaphore);
> +	if (!(fp->in_stream_mode)) {
> +		up_write(&fp->this_card_priv->semaphore);
> +		return -ENODEV;
> +	}
> +	/* release memory */
> +	if (fp->bufsize_order < PAGE_SHIFT) {
> +		rx_order = 0;
> +		tx_order = 0;
> +		rx_pages = 1;
> +		tx_pages = 1;
> +	} else {
> +		rx_order = (int)((fp->bufsize_order) - PAGE_SHIFT + 1);
> +		rx_pages = 1 << rx_order;
> +		tx_order = (int)((fp->bufsize_order) - PAGE_SHIFT);
> +		tx_pages = 1 << tx_order;
> +	}
> +	if (fp->rx_buffer) {
> +		unsigned page_number;
> +		for (page_number = 0; page_number < rx_pages; page_number++)
> +			/* turn pages back to non-reserved */
> +			ClearPageReserved(virt_to_page
> +					  ((unsigned long)fp->rx_buffer +
> +					   (page_number << PAGE_SHIFT)));
> +		free_pages((unsigned long)fp->rx_buffer, rx_order);
> +		fp->rx_buffer = NULL;
> +	}
> +	if (fp->tx_buffer) {
> +		unsigned page_number;
> +		for (page_number = 0; page_number < tx_pages; page_number++)
> +			/* turn pages back to non-reserved */
> +			ClearPageReserved(virt_to_page
> +					  ((unsigned long)fp->tx_buffer +
> +					   (page_number << PAGE_SHIFT)));
> +		free_pages((unsigned long)fp->tx_buffer, tx_order);
> +		fp->tx_buffer = NULL;
> +	}
> +
> +	fp->in_stream_mode = 0;
> +	up_write(&fp->this_card_priv->semaphore);
> +	return 0;
> +}
> +
> +static irqreturn_t fepci_stream_interrupt(int irq, void *dev_instance)
> +{
> +	struct net_device *dev = dev_instance;
> +	struct fepci_ch_private *fp = dev->priv;
> +	void *ioaddr = (void *)dev->base_addr;
> +	unsigned intr_status = get_int_status(fp->channel_number, ioaddr);
> +	bool fifo, dropped;
> +	unsigned int temp_rx;
> +	unsigned int temp_rx_unit;
> +	unsigned int temp_tx;
> +	unsigned int temp_tx_unit;
> +
> +	clear_int(fp->channel_number, intr_status, ioaddr);
> +
> +	/* debugging */
> +	fp->interrupts_stream++;
> +	fifo = (intr_status & IntrRxFifoError) != 0;
> +	dropped = (intr_status & IntrRxFrameDroppedError) != 0;
> +	fp->int_err_table_stream[fifo][dropped]++;
> +	if (intr_status & IntrFrameReceived)
> +		fp->rx_interrupts_stream++;
> +	if (intr_status & IntrFrameTransmitted)
> +		fp->tx_interrupts_stream++;
> +	if (fifo)
> +		fp->rx_int_fifo_err_stream++;
> +	if (dropped)
> +		fp->rx_int_frame_dropped_err_stream++;
> +	if (intr_status & IntrTxFifoError)
> +		fp->tx_int_fifo_err_stream++;
> +	/* first update cur_rx, and do stuff if it has moved
> +	   (+ packets have been received) */
> +	{
> +		temp_rx = fp->cur_rx;
> +		/* has been received */
> +		while ((readl(&fp->rx_desc[fp->cur_rx].desc_b) &
> +			transfer_not_done) == 0
> +		       /* stop if made one round */
> +		       && temp_rx != ((fp->cur_rx + 1) & (RX_RING_SIZE - 1))) {
> +			dma_addr_t bus_address = readl(&fp->rx_desc[fp->cur_rx].
> +						       desc_a);
> +			if (likely(!pci_dma_mapping_error(bus_address)))
> +				pci_unmap_single(fp->this_card_priv->
> +						 pci_dev, bus_address,
> +						 fp->unit_sz,
> +						 PCI_DMA_FROMDEVICE);
> +			fp->cur_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
> +			fp->cur_rx_unit = (fp->cur_rx_unit + 1);
> +			fp->cur_rx_unit *= fp->cur_rx_unit < fp->units;
> +
> +			*USER_RX_S_POINTER(fp->this_card_priv->
> +					   card_number,
> +					   fp->channel_number,
> +					   stream_pointers) = fp->cur_rx_unit;
> +			*USER_RX_S_FAKE_POINTER(fp->this_card_priv->
> +						card_number,
> +						fp->channel_number,
> +						stream_pointers) =
> +			    fp->cur_rx_unit * fp->unit_sz / fp->fake_unit_sz;
> +			wake_up_interruptible(&(fp->this_card_priv->
> +						stream_receive_q));
> +			wake_up_interruptible(&(fp->this_card_priv->
> +						stream_both_q));
> +		}
> +	}
> +	/* from the first uninitialized descriptor to cur_rx */
> +	temp_rx = (fp->cur_rx + 1) & (RX_RING_SIZE - 1);
> +	temp_rx_unit = (fp->cur_rx_unit + 1);
> +	temp_rx_unit *= temp_rx_unit < fp->units;
> +
> +	while (temp_rx != fp->cur_rx) {
> +		unsigned desc_b = readl(&fp->rx_desc[temp_rx].desc_b);
> +		if ((desc_b & transfer_not_done) == 0) {
> +			bool fifo = (desc_b & fifo_error) != 0;
> +			bool size = (desc_b & size_error) != 0;
> +			bool crc = (desc_b & crc_error) != 0;
> +			bool octet = (desc_b & octet_error) != 0;
> +			bool line = (desc_b & line_error) != 0;
> +			unsigned length = desc_b & frame_length;
> +			dma_addr_t bus_address;
> +			/* update debug counters */
> +			fp->rx_desc_err_table_stream[fifo]
> +			    [size]
> +			    [crc]
> +			    [octet]
> +			    [line]++;
> +			if (length == 0)
> +				fp->rx_packets_of_size_0_stream++;
> +			else if (length == 1)
> +				fp->rx_packets_of_size_1_stream++;
> +			else if (length == 2)
> +				fp->rx_packets_of_size_2_stream++;
> +			else if (length == 3)
> +				fp->rx_packets_of_size_3_stream++;
> +			else if (length < 8)
> +				fp->rx_packets_of_size_4_7_stream++;
> +			else if (length < 16)
> +				fp->rx_packets_of_size_8_15_stream++;
> +			else if (length < 32)
> +				fp->rx_packets_of_size_16_31_stream++;
> +			if (fifo)
> +				fp->rx_desc_fifo_err_stream++;
> +			else if (size)
> +				fp->rx_desc_size_err_stream++;
> +			else if (crc)
> +				fp->rx_desc_crc_err_stream++;
> +			else if (octet)
> +				fp->rx_desc_octet_err_stream++;
> +			else if (line)
> +				fp->rx_desc_line_err_stream++;
> +			/* initialize the descriptor for transfer */
> +			bus_address =
> +			    pci_map_single(fp->this_card_priv->pci_dev,
> +					   fp->rx_unit[temp_rx_unit],
> +					   fp->unit_sz, PCI_DMA_FROMDEVICE);
> +			if (likely(!pci_dma_mapping_error(bus_address))) {
> +				writel(bus_address,
> +				       &fp->rx_desc[temp_rx].desc_a);
> +				writel(enable_transfer,
> +				       &fp->rx_desc[temp_rx].desc_b);
> +			} else
> +				printk(KERN_WARNING
> +				       "%s: failed to map DMA for reception.\n",
> +				       fepci_NAME);
> +		}
> +		temp_rx = (temp_rx + 1) & (RX_RING_SIZE - 1);
> +		temp_rx_unit = (temp_rx_unit + 1);
> +		temp_rx_unit *= temp_rx_unit < fp->units;
> +	}
> +
> +	/* first update cur_tx, and do stuff if it has moved
> +	   (+ packets have been transmitted) */
> +	{
> +		temp_tx = fp->cur_tx;
> +		/* has been transmitted */
> +		while ((readl(&fp->tx_desc[fp->cur_tx].desc_b) &
> +			transfer_not_done) == 0
> +		       /* stop if made one round */
> +		       && temp_tx != ((fp->cur_tx + 1) & (TX_RING_SIZE - 1))) {
> +			dma_addr_t bus_address = readl(&fp->tx_desc[fp->cur_tx].
> +						       desc_a);
> +			if (likely(!pci_dma_mapping_error(bus_address)))
> +				pci_unmap_single(fp->this_card_priv->
> +						 pci_dev, bus_address,
> +						 fp->unit_sz, PCI_DMA_TODEVICE);
> +			fp->cur_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
> +			fp->cur_tx_unit = (fp->cur_tx_unit + 1);
> +			fp->cur_tx_unit *= fp->cur_tx_unit < fp->units;
> +
> +			*USER_TX_S_POINTER(fp->this_card_priv->
> +					   card_number,
> +					   fp->channel_number,
> +					   stream_pointers) = fp->cur_tx_unit;
> +			*USER_TX_S_FAKE_POINTER(fp->this_card_priv->
> +						card_number,
> +						fp->channel_number,
> +						stream_pointers) =
> +			    fp->cur_tx_unit * fp->unit_sz / fp->fake_unit_sz;
> +			wake_up_interruptible(&(fp->this_card_priv->
> +						stream_transmit_q));
> +			wake_up_interruptible(&(fp->this_card_priv->
> +						stream_both_q));
> +		}
> +	}
> +	/* from the first uninitialized descriptor to cur_tx */
> +	temp_tx = (fp->cur_tx + 1) & (TX_RING_SIZE - 1);
> +	temp_tx_unit = (fp->cur_tx_unit + 1);
> +	temp_tx_unit *= temp_tx_unit < fp->units;
> +
> +	while (temp_tx != fp->cur_tx) {
> +		unsigned desc_b = readl(&fp->tx_desc[temp_tx].desc_b);
> +		if ((desc_b & transfer_not_done) == 0) {
> +			dma_addr_t bus_address;
> +			/* update debug counters */
> +			if (desc_b & fifo_error)
> +				fp->tx_desc_fifo_err_stream++;
> +			/* initialize the desctiptor for transfer */
> +			bus_address =
> +			    pci_map_single(fp->this_card_priv->pci_dev,
> +					   fp->tx_unit[temp_tx_unit],
> +					   fp->unit_sz, PCI_DMA_TODEVICE);
> +			writel(bus_address, &fp->tx_desc[temp_tx].desc_a);
> +			if (likely(!pci_dma_mapping_error(bus_address)))
> +				writel(enable_transfer |
> +				       (fp->unit_sz & frame_length),
> +				       &fp->tx_desc[temp_tx].desc_b);
> +			else
> +				printk(KERN_WARNING
> +				       "%s: failed to map tranmission DMA.\n",
> +				       fepci_NAME);
> +		}
> +		temp_tx = (temp_tx + 1) & (TX_RING_SIZE - 1);
> +		temp_tx_unit = (temp_tx_unit + 1);
> +		temp_tx_unit *= temp_tx_unit < fp->units;
> +	}
> +
> +	return intr_status ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +/* stream operations end */
> +
> +int fepci_rebuild_header(struct sk_buff *skb)
> +{
> +	DBG_PRINT("fepci_rebuild_header\n");
> +	return 0;
> +}
> +
> +static inline u16 get_common_reg_word(void *ioaddr, unsigned long offsett)
> +{
> +	u16 word;
> +	DBG_PRINT("get_common_reg_word ioaddr %p, offsett %lu\n", ioaddr,
> +		  offsett);
> +	__clear_bit(0, &offsett);
> +	DBG_PRINT("get_common_reg_word %p\n",
> +		  ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1));
> +	word = le16_to_cpu(readw
> +			   (ioaddr + FEPCI_IDENTIFICATION_OFFSETT +
> +			    (offsett << 1)));
> +	DBG_PRINT("get_common_reg_word %p: %hu\n",
> +		  ioaddr + FEPCI_IDENTIFICATION_OFFSETT + (offsett << 1), word);
> +	return word;
> +}
> +
> +static irqreturn_t alarm_manager_interrupt(int irq, void *pointer)
> +{
> +	struct fepci_card_private *card_private = pointer;
> +	void *ioaddr = card_private->ioaddr;
> +	/* check int status */
> +	if (readl((void *)(ioaddr + reg_custom)) & AM_interrupt_status) {
> +		/* clear int (zero everything, but the mask bit) */
> +		writel(readl((void *)(ioaddr + reg_custom)) &
> +		       AM_interrupt_mask, (void *)(ioaddr + reg_custom));
> +		/* wake queue */
> +		wake_up(&(card_private->alarm_manager_wait_q));
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +#ifdef FEPCI_POINT_TO_POINT
> +static int is_ptp_interface(struct net_device *dev)
> +{
> +	char **p_ptp_if_name = retina_ptp_interfaces;
> +	unsigned int i = interfaces;
> +	while (i > 0 && *p_ptp_if_name != NULL) {
> +		if (!strncmp(dev->name, *p_ptp_if_name, sizeof(dev->name)))
> +			return 1;
> +		p_ptp_if_name++;
> +		i--;
> +	}
> +	return 0;
> +}
> +#endif /* FEPCI_POINT_TO_POINT */
> +
> +static int __devinit fepci_init_one(struct pci_dev *pdev,
> +				    const struct pci_device_id *ent)
> +{
> +	struct net_device *dev = NULL;
> +	struct fepci_ch_private *fp = NULL;
> +	int chip_idx = ent->driver_data;
> +	int drv_flags = pci_id_tbl[chip_idx].drv_flags;
> +	int i;
> +	unsigned j;
> +	resource_size_t real_ioaddr;
> +	void *ioaddr;
> +	unsigned position;
> +
> +	i = pci_enable_device(pdev);
> +	if (i) {
> +		printk(KERN_WARNING "%s: pci_enable_device returned %x.\n",
> +		       fepci_NAME, i);
> +		return i;
> +	}
> +
> +	pci_set_master(pdev);
> +
> +	i = pci_request_regions(pdev, (char *)fepci_name);
> +	if (i) {
> +		printk(KERN_WARNING
> +		       "%s: pci_request_regions returned %x.\n", fepci_NAME, i);
> +		pci_disable_device(pdev);
> +		return i;
> +	}
> +
> +	/* make sure above region is MMIO */
> +	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
> +		printk(KERN_WARNING "%s: region not MMIO region\n", fepci_NAME);
> +		goto ERR_1;
> +	}
> +
> +	j = pci_resource_len(pdev, 0);
> +	if (j < FEPCI_SIZE) {
> +		printk(KERN_WARNING
> +		       "%s: resource length %u less than required %u.\n",
> +		       fepci_NAME, j, FEPCI_SIZE);
> +		goto ERR_1;
> +	}
> +
> +	if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
> +		printk(KERN_WARNING "%s: no suitable DMA available.\n",
> +		       fepci_NAME);
> +		goto ERR_1;
> +	}
> +
> +	real_ioaddr = pci_resource_start(pdev, 0);
> +	DBG_PRINT("pci_resource_start %lu.\n", real_ioaddr);
> +	ioaddr = ioremap_nocache(real_ioaddr, FEPCI_SIZE);
> +	DBG_PRINT("ioremap_nocache = %p.\n", ioaddr);
> +	if (!ioaddr) {
> +		printk(KERN_WARNING "%s: remapping failed.\n", fepci_NAME);
> +		goto ERR_1_5;
> +	}
> +	position = 0;
> +	for (; position < MAX_DEVICES; position++) {
> +		down_write(&card_privates[position].semaphore);
> +		if (card_privates[position].pci_dev == NULL) {
> +			card_privates[position].pci_dev = pdev;
> +			if (position == find_cnt)
> +				find_cnt++;
> +			goto FOUND;
> +		}
> +		up_write(&card_privates[position].semaphore);
> +	}
> +	printk(KERN_WARNING
> +	       "%s: no space to inialize device #%u.\n",
> +	       fepci_NAME, MAX_DEVICES + 1);
> +	goto ERR_2;
> +FOUND:
> +	card_privates[position].card_number = position;
> +	card_privates[position].ioaddr = ioaddr;
> +	card_privates[position].pci_dev = pdev;
> +	DBG_PRINT("fillregisterswith_00 %p.\n", ioaddr);
> +	fillregisterswith_00(ioaddr);
> +
> +	fepci_proc_init_card(position, (void *)&(card_privates[position]));
> +
> +	init_waitqueue_head(&(card_privates[position].alarm_manager_wait_q));
> +	init_waitqueue_head(&(card_privates[position].stream_transmit_q));
> +	init_waitqueue_head(&(card_privates[position].stream_receive_q));
> +	init_waitqueue_head(&(card_privates[position].stream_both_q));
> +
> +	init_timer(&card_privates[position].mailbox_timer);
> +
> +	DBG_PRINT("request_irq %d, %s.\n", pdev->irq, fepci_alarm_manager_name);
> +	i = request_irq(pdev->irq, &alarm_manager_interrupt,
> +			IRQF_SHARED,
> +			fepci_alarm_manager_name, &card_privates[position]);
> +	if (i) {
> +		up_write(&card_privates[position].semaphore);
> +		printk(KERN_WARNING
> +		       "%s: unable to allocate IRQ %d alarm manager: 0x%x\n",
> +		       fepci_NAME, pdev->irq, i);
> +		goto ERR_2;
> +	}
> +	DBG_PRINT("alarm manager int on %p.\n", (void *)(ioaddr + reg_custom));
> +	writel(AM_interrupt_mask, (void *)(ioaddr + reg_custom));
> +	/* alarm manager int on */
> +
> +	for (j = 0; j < CHANNELS; j++) {
> +		DBG_PRINT("alloc_etherdev %u.\n", j);
> +		dev = alloc_etherdev(sizeof(struct fepci_ch_private));
> +		if (!dev) {
> +			printk(KERN_WARNING
> +			       "%s: cannot allocate ethernet device\n",
> +			       fepci_NAME);
> +			continue;
> +		}
> +
> +		fp = dev->priv;
> +		fp->minor = position;	/* * CHANNELS + j; */
> +		/* name := xxx01..xxxnn */
> +		memcpy(dev->name, fepci_netdev_name, 6);
> +		/* dev->name[3]= j+'0';    channel number -> ascii */
> +		/* minor number -> ascii */
> +		dev->name[4] = ((fp->minor * CHANNELS + j) % 10) + '0';
> +		/* minor number -> ascii */
> +		dev->name[3] = ((fp->minor * CHANNELS + j) / 10) + '0';
> +
> +		SET_MODULE_OWNER(dev);
> +		DBG_PRINT("clear_int %u, %x, %p.\n", j, IntrAllInts, ioaddr);
> +		clear_int(j, IntrAllInts, ioaddr);
> +		DBG_PRINT("ether_setup %p.\n", dev);
> +		ether_setup(dev);
> +
> +		random_ether_addr(dev->dev_addr);
> +		/* HW_ADDR is got using the mailbox: */
> +		{
> +			struct fepci_real_mailbox *real_mailbox =
> +			    (struct fepci_real_mailbox *)
> +			    (ioaddr + FEPCI_MAILBOX_OFFSETT);
> +			unsigned long waituntil;
> +
> +			set_semafore(real_mailbox, 0x40);
> +			writel(0x1 /*size */  +
> +			       (0x8 << 8) /* get mac command */ ,
> +			       &real_mailbox->Size_Command);
> +			set_semafore(real_mailbox, 0x11);
> +
> +			waituntil = jiffies + HZ;
> +			while (time_before(jiffies, waituntil) &&
> +			       get_semafore(real_mailbox) != 0x20) {
> +				DBG_PRINT("jiffies %lu < waituntil %lu.\n",
> +					  jiffies, waituntil);
> +				msleep(0);
> +			}
> +
> +			/* 14.5.2004 JT: Made this safer. */
> +			if (get_semafore(real_mailbox) == 0x20) {
> +				dev->dev_addr[5] =
> +				    readb(&real_mailbox->Data[0 + 3 * j]);
> +				dev->dev_addr[4] =
> +				    readb(((u8 *) & real_mailbox->
> +					   Data[0 + 3 * j]) + 1);
> +				dev->dev_addr[3] =
> +				    readb(&real_mailbox->Data[1 + 3 * j]);
> +				dev->dev_addr[2] =
> +				    readb(((u8 *) & real_mailbox->
> +					   Data[1 + 3 * j]) + 1);
> +				dev->dev_addr[1] =
> +				    readb(&real_mailbox->Data[2 + 3 * j]);
> +				dev->dev_addr[0] =
> +				    readb(((u8 *) & real_mailbox->
> +					   Data[2 + 3 * j]) + 1);
> +			}
> +
> +			set_semafore(real_mailbox, 0x0);
> +		}
> +		dev->addr_len = 6;
> +
> +		dev->base_addr = (unsigned long)ioaddr;
> +		dev->irq = pdev->irq;
> +		DBG_PRINT("alarm pci_set_drvdata %p, %p.\n", pdev, dev);
> +		if (j == 0)
> +			pci_set_drvdata(pdev, dev);
> +
> +		fp->drv_flags = drv_flags;
> +
> +		fp->rx_desc =
> +		    (struct fepci_desc *)(dev->base_addr + first_rx_desc +
> +					  j * to_next_ch_rx_desc);
> +		fp->tx_desc =
> +		    (struct fepci_desc *)(dev->base_addr + first_tx_desc +
> +					  j * to_next_ch_tx_desc);
> +
> +		fp->channel_number = j;	/*channel in this device */
> +		fp->this_dev = dev;
> +
> +		fp->this_card_priv = &card_privates[position];
> +		fp->this_card_priv->ch_privates[j] = fp;
> +
> +		fp->cur_tx = 0;
> +
> +		fp->in_stream_mode = 0;
> +		fp->in_eth_mode = 0;
> +
> +		fp->reg_rxctrl = reg_first_rxctrl + j * to_next_rxctrl;
> +		fp->reg_txctrl = reg_first_txctrl + j * to_next_txctrl;
> +
> +		/* The FEPCI specific entries in the device structure */
> +		dev->open = &fepci_open;
> +		dev->hard_start_xmit = &fepci_start_xmit;
> +		dev->stop = &fepci_close;
> +		dev->get_stats = &fepci_get_stats;
> +		dev->set_multicast_list = &set_rx_mode;
> +		dev->do_ioctl = &netdev_ioctl;
> +		dev->tx_timeout = fepci_tx_timeout;
> +		dev->watchdog_timeo = TX_TIMEOUT;
> +
> +#ifdef FEPCI_POINT_TO_POINT
> +		if (is_ptp_interface(dev)) {
> +			dev->flags |= (IFF_POINTOPOINT);
> +			dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
> +			if (retina_noarp_with_ptp) {
> +				dev->rebuild_header = fepci_rebuild_header;
> +				dev->flags |= (IFF_NOARP);
> +			}
> +		}
> +#endif
> +		DBG_PRINT("register_netdev %p.\n", dev);
> +		i = register_netdev(dev);
> +		if (i) {
> +			printk(KERN_WARNING
> +			       "%s: register_netdev failed 0x%x.\n",
> +			       fepci_NAME, i);
> +			continue;
> +		}
> +
> +		printk("%s: %s type %x at %p, ",
> +		       dev->name, pci_id_tbl[chip_idx].name, 0x0, ioaddr);
> +		for (i = 0; i < 5; i++)
> +			printk("%2.2x:", dev->dev_addr[i]);
> +		printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], pdev->irq);
> +#ifdef DEBUG_PROC_FILES
> +		fepci_proc_init_channel(position, j, fp);
> +#endif /* DEBUG_PROC_FILES */
> +	}
> +	up_write(&card_privates[position].semaphore);
> +	DBG_PRINT("fepci_init_one %u.\n", position);
> +	return 0;
> +
> +ERR_2:
> +	iounmap(ioaddr);
> +ERR_1_5:
> +	pci_set_drvdata(pdev, NULL);
> +ERR_1:
> +	pci_disable_device(pdev);
> +	pci_release_regions(pdev);
> +	return -ENODEV;
> +}
> +
> +static int fepci_open(struct net_device *dev)
> +{
> +	struct fepci_ch_private *fp = dev->priv;
> +
> +	down_write(&fp->this_card_priv->semaphore);
> +
> +	if (fp->this_card_priv->pci_dev == NULL) {
> +		up_write(&fp->this_card_priv->semaphore);
> +		fepci_close(dev);
> +		return -ENXIO;
> +	}
> +
> +	while (fp->in_stream_mode) {
> +		up_write(&fp->this_card_priv->semaphore);
> +		fepci_stream_close(dev);
> +		down_write(&fp->this_card_priv->semaphore);
> +	}
> +
> +	{
> +		int i = request_irq(dev->irq, &fepci_interrupt,
> +				    IRQF_SHARED, dev->name, dev);
> +		if (i) {
> +			up_write(&fp->this_card_priv->semaphore);
> +			printk(KERN_WARNING
> +			       "%s: unable to allocate IRQ %d, error 0x%x",
> +			       fepci_NAME, dev->irq, i);
> +			return i;
> +		}
> +	}
> +
> +	fp->in_eth_mode = 1;
> +
> +	fepci_init_ring(dev);
> +	set_rx_mode(dev);
> +
> +	fp->cur_rx = 0;
> +	fp->cur_tx = 0;
> +
> +	netif_carrier_off(dev);
> +
> +	/* Enable interrupts by setting the interrupt mask. */
> +
> +	set_int_mask(fp->channel_number,
> +		     MaskFrameReceived | MaskFrameTransmitted |
> +		     MaskRxFifoError | MaskRxFrameDroppedError |
> +		     MaskTxFifoError, fp->this_card_priv);
> +
> +	{
> +		void *ioaddr = (void *)dev->base_addr;
> +
> +		/* Start Rx and Tx channels. */
> +		writel(Receive_enable |
> +		       (Rx_fifo_threshold & RX_FIFO_THRESHOLD_PACKET_MODE),
> +		       (void *)(ioaddr + fp->reg_rxctrl));
> +
> +		writel((Transmit_enable |
> +			(Tx_desc_threshold &
> +			 (TX_DESC_THRESHOLD_PACKET_MODE << 8)) |
> +			(Tx_fifo_threshold & TX_FIFO_THRESHOLD_PACKET_MODE)),
> +		       (void *)(ioaddr + fp->reg_txctrl));
> +	}
> +
> +	netif_start_queue(dev);
> +
> +	/* Set timer */
> +	init_timer(&fp->timer);
> +	fp->timer.expires = jiffies + HZ;
> +	fp->timer.data = (unsigned long)dev;
> +	fp->timer.function = &fepci_timer;	/* timer handler */
> +	add_timer(&fp->timer);
> +
> +	up_write(&fp->this_card_priv->semaphore);
> +
> +	return 0;
> +}
> +
> +static void fepci_timer(unsigned long data)
> +{
> +	struct net_device *dev = (struct net_device *)data;
> +	struct fepci_ch_private *fp = dev->priv;
> +
> +	/* just to make it absolutely sure the sending starts again
> +	 * if the system jams: free already sent skbuffs */
> +
> +	if (netif_tx_trylock(dev)) {
> +		if (!(fp->tx_interrupts_since_last_timer)) {
> +			unsigned i = TX_RING_SIZE - 1;
> +			do {
> +				unsigned desc_b;
> +				if ((fp->tx_skbuff[i] != NULL)
> +				    &&
> +				    (((desc_b =
> +				       readl(&fp->tx_desc[i].
> +					     desc_b)) & transfer_not_done) ==
> +				     0)) {
> +					/* has been sent */
> +					pci_unmap_single(fp->
> +							 this_card_priv->
> +							 pci_dev,
> +							 readl(&fp->
> +							       tx_desc[i].
> +							       desc_a),
> +							 desc_b &
> +							 frame_length,
> +							 PCI_DMA_TODEVICE);
> +					dev_kfree_skb(fp->tx_skbuff[i]);
> +					fp->tx_skbuffs_out++;
> +
> +					fp->tx_skbuff[i] = NULL;
> +
> +					if (desc_b & fifo_error) {
> +						fp->stats.tx_fifo_errors++;
> +						fp->tx_desc_fifo_err++;
> +					} else
> +						fp->stats.tx_packets++;
> +				}
> +			}
> +			while (i--);
> +		}
> +		/* if the next descriptor is free, continue taking new ones */
> +		if (!
> +		    (readl(&fp->tx_desc[fp->cur_tx].desc_b) &
> +		     transfer_not_done))
> +			netif_wake_queue(dev);
> +		netif_tx_unlock(dev);
> +	}
> +
> +	fp->tx_interrupts_since_last_timer = 0;
> +
> +	if ((get_common_reg_word(fp->this_card_priv->ioaddr, 0x72) >> fp->
> +	     channel_number) & 1) {
> +		netif_carrier_off(dev);
> +	} else {
> +		netif_carrier_on(dev);
> +	}
> +
> +	if (fp->in_eth_mode)
> +		mod_timer(&fp->timer, jiffies + 5 * HZ);
> +}
> +
> +static void fepci_tx_timeout(struct net_device *dev)
> +{
> +	DBG_PRINT("%s: transmit timed out!\n", dev->name);
> +}
> +
> +/* Initialize the rx and tx rings */
> +static void fepci_init_ring(struct net_device *dev)
> +{
> +	struct fepci_ch_private *fp = dev->priv;
> +	unsigned i;
> +
> +	fp->rx_buf_sz = 2000;
> +
> +	for (i = 0; i < RX_RING_SIZE; i++) {
> +		struct sk_buff *skb =
> +		    __dev_alloc_skb(fp->rx_buf_sz, GFP_KERNEL);
> +
> +		if (unlikely(skb == NULL)) {
> +ZERO:
> +			writel(0, &fp->rx_desc[i].desc_a);
> +			writel(0, &fp->rx_desc[i].desc_b);
> +			continue;
> +		} else {
> +			dma_addr_t bus_address =
> +			    pci_map_single(fp->this_card_priv->pci_dev,
> +					   skb->data, fp->rx_buf_sz,
> +					   PCI_DMA_FROMDEVICE);
> +			if (likely(!pci_dma_mapping_error(bus_address))) {
> +				fp->rx_skbuffs_in++;
> +				/* Mark as being used by this device */
> +				skb->dev = dev;
> +				skb->ip_summed = CHECKSUM_UNNECESSARY;
> +				fp->rx_skbuff[i] = skb;
> +				writel(bus_address, &fp->rx_desc[i].desc_a);
> +				writel(enable_transfer, &fp->rx_desc[i].desc_b);
> +			} else {
> +				dev_kfree_skb(skb);
> +				goto ZERO;
> +			}
> +		}
> +	}
> +
> +	for (i = 0; i < TX_RING_SIZE; i++) {
> +		fp->tx_skbuff[i] = NULL;
> +		writel(0, &fp->tx_desc[i].desc_a);	/* no skbuff */
> +		/* no transfer enable, no int enable */
> +		writel(0, &fp->tx_desc[i].desc_b);
> +	}
> +
> +	return;
> +}
> +
> +static int fepci_start_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct fepci_ch_private *fp = dev->priv;
> +	const unsigned cur_tx = fp->cur_tx;
> +	unsigned next;
> +	unsigned tx_length = skb->len;
> +	dma_addr_t bus_address;
> +	struct sk_buff *old;
> +
> +	fp->tx_skbuffs_in++;
> +
> +	if (unlikely(tx_length < ETH_ZLEN)) {
> +		struct sk_buff *bigger =
> +		    skb_copy_expand(skb, 0, ETH_ZLEN - tx_length,
> +				    GFP_ATOMIC);
> +		if (unlikely(!bigger))
> +			return NET_XMIT_CN;
> +		tx_length = ETH_ZLEN;
> +		old = skb;
> +		skb = bigger;
> +	} else
> +		old = NULL;
> +	bus_address =
> +	    pci_map_single(fp->this_card_priv->pci_dev, skb->data,
> +			   tx_length, PCI_DMA_TODEVICE);
> +	if (likely(!pci_dma_mapping_error(bus_address))) {
> +		struct fepci_desc *descriptor;
> +		if (old)
> +			dev_kfree_skb(old);
> +		descriptor = &fp->tx_desc[cur_tx];
> +		writel(bus_address, &descriptor->desc_a);
> +		writel((tx_length & frame_length) | enable_transfer,
> +		       &descriptor->desc_b);
> +	} else {
> +		if (old)
> +			dev_kfree_skb(skb);
> +		return NET_XMIT_CN;
> +	}
> +	fp->stats.tx_bytes += tx_length;
> +
> +	fp->tx_skbuff[cur_tx] = skb;
> +
> +	/* Calculate the next Tx descriptor entry */
> +	next = (cur_tx + 1) & (TX_RING_SIZE - 1);
> +	fp->cur_tx = next;
> +	/* if the next descriptor is busy, discontinue taking new ones */
> +	if (fp->tx_skbuff[next] != NULL)
> +		netif_stop_queue(dev);
> +	dev->trans_start = jiffies;
> +
> +	return NET_XMIT_SUCCESS;
> +}
> +
> +static irqreturn_t fepci_interrupt(int irq, void *dev_instance)
> +{
> +	struct net_device *dev = dev_instance;
> +	void *ioaddr = (void *)dev->base_addr;
> +	struct fepci_ch_private *fp = dev->priv;
> +	unsigned intr_status = get_int_status(fp->channel_number, ioaddr);
> +	bool RxFifoError;
> +	bool RxFrameDroppedError;
> +
> +	clear_int(fp->channel_number, intr_status, ioaddr);
> +
> +	RxFifoError = (intr_status & IntrRxFifoError) != 0;
> +	RxFrameDroppedError = (intr_status & IntrRxFrameDroppedError) != 0;
> +	/* first update interrupt error table */
> +	fp->int_err_table[RxFifoError][RxFrameDroppedError]++;
> +
> +	fp->interrupts++;
> +
> +	if (intr_status & IntrFrameReceived) {
> +		fepci_rx(dev);
> +		fp->rx_interrupts++;
> +	}
> +	if (intr_status & IntrFrameTransmitted) {
> +		fp->tx_interrupts_since_last_timer++;
> +		if (netif_tx_trylock(dev)) {
> +			unsigned i = TX_RING_SIZE - 1;
> +			unsigned next;
> +			do {
> +				unsigned desc_b;
> +				if ((fp->tx_skbuff[i] != NULL)
> +				    &&
> +				    (((desc_b =
> +				       readl(&fp->tx_desc[i].
> +					     desc_b)) & transfer_not_done) ==
> +				     0)) {
> +					/* has been sent */
> +					pci_unmap_single(fp->
> +							 this_card_priv->
> +							 pci_dev,
> +							 readl(&fp->
> +							       tx_desc[i].
> +							       desc_a),
> +							 desc_b &
> +							 frame_length,
> +							 PCI_DMA_TODEVICE);
> +					dev_kfree_skb_irq(fp->tx_skbuff[i]);
> +					fp->tx_skbuffs_out++;
> +					fp->tx_skbuff[i] = NULL;
> +					if (desc_b & fifo_error) {
> +						fp->stats.tx_fifo_errors++;
> +						fp->tx_desc_fifo_err++;
> +					} else
> +						fp->stats.tx_packets++;
> +				}
> +			}
> +			while (i--);
> +			next = fp->cur_tx;
> +			/* if next tx descriptor is free,
> +			 * continue taking new ones */
> +			if (!(readl(&fp->tx_desc[next].desc_b) &
> +			      transfer_not_done))
> +				netif_wake_queue(dev);
> +			netif_tx_unlock(dev);
> +		}
> +		fp->tx_interrupts++;
> +	}
> +	if (RxFifoError) {
> +		fp->rx_int_fifo_err++;
> +		fepci_rx(dev);
> +	}
> +	if (RxFrameDroppedError) {
> +		fp->rx_int_frame_dropped_err++;
> +		fepci_rx(dev);
> +	}
> +	if (intr_status & IntrTxFifoError)
> +		fp->tx_int_fifo_err++;
> +	return intr_status ? IRQ_HANDLED : IRQ_NONE;
> +}
> +
> +static int fepci_rx(struct net_device *dev)
> +{
> +	struct fepci_ch_private *fp = dev->priv;
> +
> +	unsigned int i, old_cur_rx = fp->cur_rx;
> +	for (i = old_cur_rx;
> +	     i != ((old_cur_rx + RX_RING_SIZE - 1) & (RX_RING_SIZE - 1));
> +	     i = (i + 1) & (RX_RING_SIZE - 1)) {
> +		unsigned desc_b;
> +		struct sk_buff *skb;
> +		/* transfer done */
> +		bool condition = (skb = fp->rx_skbuff[i]) &&
> +		    ((desc_b =
> +		      readl(&fp->rx_desc[i].desc_b)) & transfer_not_done) == 0;
> +		if (condition) {
> +			bool fifo = (desc_b & fifo_error) != 0;
> +			bool size = (desc_b & size_error) != 0;
> +			bool crc = (desc_b & crc_error) != 0;
> +			bool octet = (desc_b & octet_error) != 0;
> +			bool line = (desc_b & line_error) != 0;
> +			unsigned length = desc_b & frame_length;
> +			pci_unmap_single(fp->this_card_priv->pci_dev,
> +					 readl(&fp->rx_desc[i].desc_a),
> +					 fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
> +			fp->cur_rx = (i + 1) & (RX_RING_SIZE - 1);
> +			/* first update error table */
> +			fp->rx_desc_err_table[fifo]
> +			    [size]
> +			    [crc]
> +			    [octet]
> +			    [line]++;
> +			/* small packet counters */
> +			if (length == 0)
> +				fp->rx_packets_of_size_0++;
> +			else if (length == 1)
> +				fp->rx_packets_of_size_1++;
> +			else if (length == 2)
> +				fp->rx_packets_of_size_2++;
> +			else if (length == 3)
> +				fp->rx_packets_of_size_3++;
> +			else if (length < 8)
> +				fp->rx_packets_of_size_4_7++;
> +			else if (length < 16)
> +				fp->rx_packets_of_size_8_15++;
> +			else if (length < 32)
> +				fp->rx_packets_of_size_16_31++;
> +
> +			if (fifo) {
> +				fp->stats.rx_errors++;
> +				fp->stats.rx_frame_errors++;
> +				fp->rx_desc_fifo_err++;
> +				writel(enable_transfer, &fp->rx_desc[i].desc_b);
> +			} else if (size) {
> +				fp->stats.rx_errors++;
> +				fp->stats.rx_over_errors++;
> +				fp->rx_desc_size_err++;
> +				writel(enable_transfer, &fp->rx_desc[i].desc_b);
> +			} else if (crc) {
> +				fp->stats.rx_errors++;
> +				fp->stats.rx_crc_errors++;
> +				fp->rx_desc_crc_err++;
> +				writel(enable_transfer, &fp->rx_desc[i].desc_b);
> +			} else if (octet) {
> +				fp->rx_desc_octet_err++;
> +				writel(enable_transfer, &fp->rx_desc[i].desc_b);
> +			} else if (line) {
> +				fp->rx_desc_line_err++;
> +				writel(enable_transfer, &fp->rx_desc[i].desc_b);
> +			} else {
> +				skb_put(skb, length - 4);
> +
> +				skb->protocol = eth_type_trans(skb, dev);
> +#ifdef FEPCI_POINT_TO_POINT
> +				if (dev->flags & IFF_POINTOPOINT) {
> +					/* everything received is for us. */
> +
> +					if (dev->flags & IFF_NOARP) {
> +						/* NOARP applied ->
> +						 * destination MAC addresses
> +						 * are bogus */
> +						if (skb->
> +						    pkt_type ==
> +						    PACKET_OTHERHOST)
> +							skb->
> +							    pkt_type =
> +							    PACKET_HOST;
> +					} else {
> +						/* NOARP not applied ->
> +						 * destination MAC addresses are
> +						 * broadcast */
> +						if (skb->
> +						    pkt_type ==
> +						    PACKET_BROADCAST)
> +							skb->
> +							    pkt_type =
> +							    PACKET_HOST;
> +
> +					}	/* IFF_NOARP */
> +				}	/* IFF_POINTOPOINT */
> +#endif
> +				skb_reset_mac_header(skb);
> +				netif_rx(skb);
> +				fp->rx_skbuffs_out++;
> +				/* statistics -4==crc */
> +				fp->stats.rx_bytes += length - 4;
> +				fp->stats.rx_packets++;
> +
> +				fp->rx_skbuff[i] = NULL;
> +				dev->last_rx = jiffies;
> +			}
> +		}
> +		/* reserve a new one */
> +		if (fp->rx_skbuff[i] == NULL) {
> +			struct sk_buff *skb = dev_alloc_skb(fp->rx_buf_sz);
> +
> +			if (skb == NULL)
> +				continue;	/* Better luck next round. */
> +			else {
> +				dma_addr_t address =
> +				    pci_map_single(fp->this_card_priv->pci_dev,
> +						   skb->data,
> +						   fp->rx_buf_sz,
> +						   PCI_DMA_FROMDEVICE);
> +				if (likely(!pci_dma_mapping_error(address))) {
> +					struct fepci_desc *descriptor;
> +					fp->rx_skbuffs_in++;
> +					fp->rx_skbuff[i] = skb;
> +					/* Mark as being used by this device. */
> +					skb->dev = dev;
> +					skb->ip_summed = CHECKSUM_UNNECESSARY;
> +					descriptor = &fp->rx_desc[i];
> +					writel(address, &descriptor->desc_a);
> +					writel(enable_transfer,
> +					       &descriptor->desc_b);
> +				} else {
> +					dev_kfree_skb(skb);
> +					printk(KERN_WARNING
> +					       "%s: failed to map DMA.\n",
> +					       dev->name);
> +				}
> +			}
> +		}
> +	}
> +	return 0;
> +}
> +
> +static int fepci_close(struct net_device *dev)
> +{
> +	struct fepci_ch_private *fp = dev->priv;
> +	unsigned i;
> +	void *ioaddr = (void *)dev->base_addr;
> +
> +	down_write(&fp->this_card_priv->semaphore);
> +
> +	netif_stop_queue(dev);
> +
> +	/* Disable interrupts by clearing the interrupt mask */
> +	set_int_mask(fp->channel_number, 0x0, fp->this_card_priv);
> +
> +	/* Stop the Tx and Rx processes */
> +	writel(0x0, ioaddr + fp->reg_rxctrl);
> +	writel(0x0, ioaddr + fp->reg_txctrl);
> +	fp->in_eth_mode = 0;
> +
> +	del_timer_sync(&fp->timer);
> +
> +	free_irq(dev->irq, dev);
> +
> +	/* Free all the rx skbuffs */
> +	for (i = 0; i < RX_RING_SIZE; i++) {
> +		if (fp->rx_skbuff[i] != NULL) {
> +			pci_unmap_single(fp->this_card_priv->
> +					 pci_dev,
> +					 readl(&fp->rx_desc[i].
> +					       desc_a),
> +					 fp->rx_buf_sz, PCI_DMA_FROMDEVICE);
> +			dev_kfree_skb(fp->rx_skbuff[i]);
> +			fp->rx_skbuffs_out++;
> +			fp->rx_skbuff[i] = NULL;
> +		}
> +	}
> +	/* and tx */
> +	for (i = 0; i < TX_RING_SIZE; i++) {
> +		if (fp->tx_skbuff[i] != NULL) {
> +			pci_unmap_single(fp->this_card_priv->
> +					 pci_dev,
> +					 readl(&fp->tx_desc[i].
> +					       desc_a),
> +					 readl(&fp->tx_desc[i].
> +					       desc_b) & frame_length,
> +					 PCI_DMA_TODEVICE);
> +			dev_kfree_skb(fp->tx_skbuff[i]);
> +			fp->tx_skbuffs_out++;
> +			fp->tx_skbuff[i] = NULL;
> +		}
> +	}
> +	up_write(&fp->this_card_priv->semaphore);
> +	return 0;
> +}
> +
> +static struct net_device_stats *fepci_get_stats(struct net_device *dev)
> +{
> +	struct fepci_ch_private *fp = dev->priv;
> +	return &fp->stats;
> +}
> +
> +static void set_rx_mode(struct net_device *dev)
> +{
> +	DBG_PRINT("set_rx_mode\n");
> +}
> +
> +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
> +{
> +	struct fepci_ch_private *fp = dev->priv;
> +	char data = ((unsigned)rq->ifr_data) & 0xff;
> +	int ret = 0;
> +
> +	DBG_PRINT("%s: netdev_ioctl called (command_nmbr:0x%x).\n",
> +		  dev->name, cmd);
> +
> +	switch (cmd) {
> +	case FEPCI_NETDEV_IOCTL_STREAM_BUFSIZE:
> +		DBG_PRINT
> +		    (" ioctl stream bufsize commanded. (bufsize:0x%x)\n", data);
> +		down_write(&fp->this_card_priv->semaphore);
> +		if (fp->in_stream_mode) {
> +			up_write(&fp->this_card_priv->semaphore);
> +			return -EBUSY;
> +		}
> +		fp->bufsize_order = data;
> +		up_write(&fp->this_card_priv->semaphore);
> +		break;
> +	case FEPCI_NETDEV_IOCTL_STREAM_UNITSIZE:
> +		DBG_PRINT
> +		    (" ioctl stream unitsize commanded. (unitsize:0x%x)\n",
> +		     data);
> +		down_write(&fp->this_card_priv->semaphore);
> +		if (fp->in_stream_mode) {
> +			up_write(&fp->this_card_priv->semaphore);
> +			return -EBUSY;
> +		}
> +		fp->fake_unit_sz_order = data;
> +		up_write(&fp->this_card_priv->semaphore);
> +		break;
> +	case FEPCI_NETDEV_IOCTL_STREAM_OPEN:
> +		DBG_PRINT(" ioctl stream open commanded.\n");
> +		ret = fepci_stream_open(dev);
> +		break;
> +	case FEPCI_NETDEV_IOCTL_STREAM_START:
> +		DBG_PRINT(" ioctl stream start commanded.\n");
> +		ret = fepci_stream_start(dev);
> +		break;
> +	case FEPCI_NETDEV_IOCTL_STREAM_CLOSE:
> +		DBG_PRINT(" ioctl stream close commanded.\n");
> +		ret = fepci_stream_close(dev);
> +		break;
> +	default:
> +		DBG_PRINT(" unknown ioctl command 0x%x.\n", cmd);
> +		return -ENOTTY;
> +	}
> +	return ret;
> +}
> +
> +static void fepci_remove_one(struct pci_dev *pdev)
> +{
> +	/* from first dev, same in all */
> +	struct net_device *dev = pci_get_drvdata(pdev);
> +	struct fepci_ch_private *fp = dev->priv;
> +	struct fepci_card_private *cardp = fp->this_card_priv;
> +	unsigned int i;
> +
> +	writel(0, (void *)(cardp->ioaddr + reg_custom));
> +	/* alarm manager int off */
> +
> +	for (i = 0; i < CHANNELS; i++) {
> +		dev = cardp->ch_privates[i]->this_dev;
> +		fp = dev->priv;
> +#ifdef DEBUG_PROC_FILES
> +		fepci_proc_cleanup_channel(cardp->card_number,
> +					   fp->channel_number);
> +#endif /* DEBUG_PROC_FILES */
> +		unregister_netdev(dev);
> +		fepci_stream_close(dev);
> +		free_netdev(dev);
> +	}
> +
> +	pdev = cardp->pci_dev;
> +	free_irq(pdev->irq, (void *)cardp);
> +
> +	down_write(&cardp->semaphore);
> +
> +	fepci_proc_cleanup_card(cardp->card_number);
> +
> +	pci_set_drvdata(pdev, NULL);
> +
> +	if (cardp->card_number + 1 == find_cnt)
> +		find_cnt--;
> +	cardp->pci_dev = NULL;
> +
> +	iounmap(cardp->ioaddr);
> +
> +	up_write(&cardp->semaphore);
> +
> +	pci_disable_device(pdev);
> +	pci_release_regions(pdev);
> +}
> +
> +static struct pci_driver fepci_driver = {
> +name:		DRV_NAME,
> +id_table:	fepci_pci_tbl,
> +probe:		fepci_init_one,
> +remove:		fepci_remove_one,
> +};
> +
> +static int __init fepci_init(void)
> +{
> +	unsigned card = MAX_DEVICES - 1;
> +	do
> +		init_rwsem(&card_privates[card].semaphore);
> +	while (card--);
> +	major = fepci_register_char_device();
> +	if (major < 0)
> +		return major;
> +	stream_pointers =
> +	    (unsigned long)get_zeroed_page(GFP_KERNEL | __GFP_DMA);
> +	DBG_PRINT(" %x.\n", stream_pointers);
> +	if (stream_pointers == 0) {
> +		fepci_unregister_char_device();
> +		return -ENOMEM;
> +	}
> +	DBG_PRINT("SetPageReserved %u.\n", stream_pointers);
> +	SetPageReserved(virt_to_page(stream_pointers));
> +	DBG_PRINT("fepci_proc_init_driver.\n");
> +	fepci_proc_init_driver();
> +	DBG_PRINT("pci_register_driver %p.\n", &fepci_driver);
> +	{
> +		int ret = pci_register_driver(&fepci_driver);
> +		if (ret) {
> +			fepci_unregister_char_device();
> +			ClearPageReserved(virt_to_page(stream_pointers));
> +			free_page(stream_pointers);
> +			DBG_PRINT
> +			    ("pci_register_driver %p failed with %d.\n",
> +			     &fepci_driver, ret);
> +			fepci_proc_cleanup_driver();
> +			return ret;
> +		}
> +		DBG_PRINT("fepci_init %d.\n", ret);
> +		return ret;
> +	}
> +}
> +
> +static void __exit fepci_cleanup(void)
> +{
> +	DBG_PRINT("fepci_cleanup\n");
> +	pci_unregister_driver(&fepci_driver);
> +	fepci_proc_cleanup_driver();
> +	fepci_unregister_char_device();
> +	ClearPageReserved(virt_to_page(stream_pointers));
> +	free_page(stream_pointers);
> +}
> +
> +module_init(fepci_init);
> +module_exit(fepci_cleanup);
> +
>

-- 
Stephen Hemminger <shemminger@...ux-foundation.org>
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists