| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | /* sbni.c:  Granch SBNI12 leased line adapters driver for linux
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *	Written 2001 by Denis I.Timofeev (timofeev@granch.ru) | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *	Previous versions were written by Yaroslav Polyakov, | 
					
						
							|  |  |  |  *	Alexey Zverev and Max Khon. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *	Driver supports SBNI12-02,-04,-05,-10,-11 cards, single and | 
					
						
							|  |  |  |  *	double-channel, PCI and ISA modifications. | 
					
						
							|  |  |  |  *	More info and useful utilities to work with SBNI12 cards you can find | 
					
						
							|  |  |  |  *	at http://www.granch.com (English) or http://www.granch.ru (Russian)
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *	This software may be used and distributed according to the terms | 
					
						
							|  |  |  |  *	of the GNU General Public License. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  5.0.1	Jun 22 2001 | 
					
						
							|  |  |  |  *	  - Fixed bug in probe | 
					
						
							|  |  |  |  *  5.0.0	Jun 06 2001 | 
					
						
							|  |  |  |  *	  - Driver was completely redesigned by Denis I.Timofeev, | 
					
						
							|  |  |  |  *	  - now PCI/Dual, ISA/Dual (with single interrupt line) models are | 
					
						
							|  |  |  |  *	  - supported | 
					
						
							|  |  |  |  *  3.3.0	Thu Feb 24 21:30:28 NOVT 2000  | 
					
						
							|  |  |  |  *        - PCI cards support | 
					
						
							|  |  |  |  *  3.2.0	Mon Dec 13 22:26:53 NOVT 1999 | 
					
						
							|  |  |  |  * 	  - Completely rebuilt all the packet storage system | 
					
						
							|  |  |  |  * 	  -    to work in Ethernet-like style. | 
					
						
							|  |  |  |  *  3.1.1	just fixed some bugs (5 aug 1999) | 
					
						
							|  |  |  |  *  3.1.0	added balancing feature	(26 apr 1999) | 
					
						
							|  |  |  |  *  3.0.1	just fixed some bugs (14 apr 1999). | 
					
						
							|  |  |  |  *  3.0.0	Initial Revision, Yaroslav Polyakov (24 Feb 1999) | 
					
						
							|  |  |  |  *        - added pre-calculation for CRC, fixed bug with "len-2" frames,  | 
					
						
							|  |  |  |  *        - removed outbound fragmentation (MTU=1000), written CRC-calculation  | 
					
						
							|  |  |  |  *        - on asm, added work with hard_headers and now we have our own cache  | 
					
						
							|  |  |  |  *        - for them, optionally supported word-interchange on some chipsets, | 
					
						
							|  |  |  |  *  | 
					
						
							|  |  |  |  *	Known problem: this driver wasn't tested on multiprocessor machine. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/ptrace.h>
 | 
					
						
							|  |  |  | #include <linux/fcntl.h>
 | 
					
						
							|  |  |  | #include <linux/ioport.h>
 | 
					
						
							|  |  |  | #include <linux/interrupt.h>
 | 
					
						
							|  |  |  | #include <linux/string.h>
 | 
					
						
							|  |  |  | #include <linux/errno.h>
 | 
					
						
							|  |  |  | #include <linux/netdevice.h>
 | 
					
						
							|  |  |  | #include <linux/etherdevice.h>
 | 
					
						
							|  |  |  | #include <linux/pci.h>
 | 
					
						
							|  |  |  | #include <linux/skbuff.h>
 | 
					
						
							|  |  |  | #include <linux/timer.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/delay.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-17 11:56:21 -07:00
										 |  |  | #include <net/net_namespace.h>
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #include <net/arp.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/io.h>
 | 
					
						
							|  |  |  | #include <asm/types.h>
 | 
					
						
							|  |  |  | #include <asm/byteorder.h>
 | 
					
						
							|  |  |  | #include <asm/irq.h>
 | 
					
						
							|  |  |  | #include <asm/uaccess.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "sbni.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* device private data */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct net_local { | 
					
						
							|  |  |  | 	struct timer_list	watchdog; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spinlock_t	lock; | 
					
						
							|  |  |  | 	struct sk_buff  *rx_buf_p;		/* receive buffer ptr */ | 
					
						
							|  |  |  | 	struct sk_buff  *tx_buf_p;		/* transmit buffer ptr */ | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	unsigned int	framelen;		/* current frame length */ | 
					
						
							|  |  |  | 	unsigned int	maxframe;		/* maximum valid frame length */ | 
					
						
							|  |  |  | 	unsigned int	state; | 
					
						
							|  |  |  | 	unsigned int	inppos, outpos;		/* positions in rx/tx buffers */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* transmitting frame number - from frames qty to 1 */ | 
					
						
							|  |  |  | 	unsigned int	tx_frameno; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* expected number of next receiving frame */ | 
					
						
							|  |  |  | 	unsigned int	wait_frameno; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* count of failed attempts to frame send - 32 attempts do before
 | 
					
						
							|  |  |  | 	   error - while receiver tunes on opposite side of wire */ | 
					
						
							|  |  |  | 	unsigned int	trans_errors; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* idle time; send pong when limit exceeded */ | 
					
						
							|  |  |  | 	unsigned int	timer_ticks; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* fields used for receive level autoselection */ | 
					
						
							|  |  |  | 	int	delta_rxl; | 
					
						
							|  |  |  | 	unsigned int	cur_rxl_index, timeout_rxl; | 
					
						
							|  |  |  | 	unsigned long	cur_rxl_rcvd, prev_rxl_rcvd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	struct sbni_csr1	csr1;		/* current value of CSR1 */ | 
					
						
							|  |  |  | 	struct sbni_in_stats	in_stats; 	/* internal statistics */  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	struct net_device		*second;	/* for ISA/dual cards */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 	struct net_device		*master; | 
					
						
							|  |  |  | 	struct net_device		*link; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int  sbni_card_probe( unsigned long ); | 
					
						
							|  |  |  | static int  sbni_pci_probe( struct net_device  * ); | 
					
						
							|  |  |  | static struct net_device  *sbni_probe1(struct net_device *, unsigned long, int); | 
					
						
							|  |  |  | static int  sbni_open( struct net_device * ); | 
					
						
							|  |  |  | static int  sbni_close( struct net_device * ); | 
					
						
							| 
									
										
										
										
											2009-08-31 19:50:47 +00:00
										 |  |  | static netdev_tx_t sbni_start_xmit(struct sk_buff *, | 
					
						
							|  |  |  | 					 struct net_device * ); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static int  sbni_ioctl( struct net_device *, struct ifreq *, int ); | 
					
						
							|  |  |  | static void  set_multicast_list( struct net_device * ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around.  On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable.  On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions.  Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller.  A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386.  I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs.  Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
	struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
	set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
	-	update_process_times(user_mode(regs));
	-	profile_tick(CPU_PROFILING, regs);
	+	update_process_times(user_mode(get_irq_regs()));
	+	profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
 (*) input_dev() is now gone entirely.  The regs pointer is no longer stored in
     the input_dev struct.
 (*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking.  It does
     something different depending on whether it's been supplied with a regs
     pointer or not.
 (*) Various IRQ handler function pointers have been moved to type
     irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
											
										 
											2006-10-05 14:55:46 +01:00
										 |  |  | static irqreturn_t sbni_interrupt( int, void * ); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static void  handle_channel( struct net_device * ); | 
					
						
							|  |  |  | static int   recv_frame( struct net_device * ); | 
					
						
							|  |  |  | static void  send_frame( struct net_device * ); | 
					
						
							|  |  |  | static int   upload_data( struct net_device *, | 
					
						
							|  |  |  | 			  unsigned, unsigned, unsigned, u32 ); | 
					
						
							|  |  |  | static void  download_data( struct net_device *, u32 * ); | 
					
						
							|  |  |  | static void  sbni_watchdog( unsigned long ); | 
					
						
							|  |  |  | static void  interpret_ack( struct net_device *, unsigned ); | 
					
						
							|  |  |  | static int   append_frame_to_pkt( struct net_device *, unsigned, u32 ); | 
					
						
							|  |  |  | static void  indicate_pkt( struct net_device * ); | 
					
						
							|  |  |  | static void  card_start( struct net_device * ); | 
					
						
							|  |  |  | static void  prepare_to_send( struct sk_buff *, struct net_device * ); | 
					
						
							|  |  |  | static void  drop_xmit_queue( struct net_device * ); | 
					
						
							|  |  |  | static void  send_frame_header( struct net_device *, u32 * ); | 
					
						
							|  |  |  | static int   skip_tail( unsigned int, unsigned int, u32 ); | 
					
						
							|  |  |  | static int   check_fhdr( u32, u32 *, u32 *, u32 *, u32 *, u32 * ); | 
					
						
							|  |  |  | static void  change_level( struct net_device * ); | 
					
						
							|  |  |  | static void  timeout_change_level( struct net_device * ); | 
					
						
							|  |  |  | static u32   calc_crc32( u32, u8 *, u32 ); | 
					
						
							|  |  |  | static struct sk_buff *  get_rx_buf( struct net_device * ); | 
					
						
							|  |  |  | static int  sbni_init( struct net_device * ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | static int  enslave( struct net_device *, struct net_device * ); | 
					
						
							|  |  |  | static int  emancipate( struct net_device * ); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef __i386__
 | 
					
						
							|  |  |  | #define ASM_CRC 1
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char  version[] = | 
					
						
							|  |  |  | 	"Granch SBNI12 driver ver 5.0.1  Jun 22 2001  Denis I.Timofeev.\n"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int  skip_pci_probe	__initdata = 0; | 
					
						
							|  |  |  | static int  scandone	__initdata = 0; | 
					
						
							|  |  |  | static int  num		__initdata = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static unsigned char  rxl_tab[]; | 
					
						
							|  |  |  | static u32  crc32tab[]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* A list of all installed devices, for removing the driver module. */ | 
					
						
							|  |  |  | static struct net_device  *sbni_cards[ SBNI_MAX_NUM_CARDS ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Lists of device's parameters */ | 
					
						
							|  |  |  | static u32	io[   SBNI_MAX_NUM_CARDS ] __initdata = | 
					
						
							|  |  |  | 	{ [0 ... SBNI_MAX_NUM_CARDS-1] = -1 }; | 
					
						
							|  |  |  | static u32	irq[  SBNI_MAX_NUM_CARDS ] __initdata; | 
					
						
							|  |  |  | static u32	baud[ SBNI_MAX_NUM_CARDS ] __initdata; | 
					
						
							|  |  |  | static u32	rxl[  SBNI_MAX_NUM_CARDS ] __initdata = | 
					
						
							|  |  |  | 	{ [0 ... SBNI_MAX_NUM_CARDS-1] = -1 }; | 
					
						
							|  |  |  | static u32	mac[  SBNI_MAX_NUM_CARDS ] __initdata; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef MODULE
 | 
					
						
							|  |  |  | typedef u32  iarr[]; | 
					
						
							|  |  |  | static iarr __initdata *dest[5] = { &io, &irq, &baud, &rxl, &mac }; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* A zero-terminated list of I/O addresses to be probed on ISA bus */ | 
					
						
							|  |  |  | static unsigned int  netcard_portlist[ ] __initdata = {  | 
					
						
							|  |  |  | 	0x210, 0x214, 0x220, 0x224, 0x230, 0x234, 0x240, 0x244, 0x250, 0x254, | 
					
						
							|  |  |  | 	0x260, 0x264, 0x270, 0x274, 0x280, 0x284, 0x290, 0x294, 0x2a0, 0x2a4, | 
					
						
							|  |  |  | 	0x2b0, 0x2b4, 0x2c0, 0x2c4, 0x2d0, 0x2d4, 0x2e0, 0x2e4, 0x2f0, 0x2f4, | 
					
						
							|  |  |  | 	0 }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | #define NET_LOCAL_LOCK(dev) (((struct net_local *)netdev_priv(dev))->lock)
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Look for SBNI card which addr stored in dev->base_addr, if nonzero. | 
					
						
							|  |  |  |  * Otherwise, look through PCI bus. If none PCI-card was found, scan ISA. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline int __init | 
					
						
							|  |  |  | sbni_isa_probe( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2009-12-03 07:58:21 +00:00
										 |  |  | 	if( dev->base_addr > 0x1ff && | 
					
						
							|  |  |  | 	    request_region( dev->base_addr, SBNI_IO_EXTENT, dev->name ) && | 
					
						
							|  |  |  | 	    sbni_probe1( dev, dev->base_addr, dev->irq ) ) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return  0; | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		printk( KERN_ERR "sbni: base address 0x%lx is busy, or adapter " | 
					
						
							|  |  |  | 			"is malfunctional!\n", dev->base_addr ); | 
					
						
							|  |  |  | 		return  -ENODEV; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:20 +00:00
										 |  |  | static const struct net_device_ops sbni_netdev_ops = { | 
					
						
							|  |  |  | 	.ndo_open		= sbni_open, | 
					
						
							|  |  |  | 	.ndo_stop		= sbni_close, | 
					
						
							|  |  |  | 	.ndo_start_xmit		= sbni_start_xmit, | 
					
						
							|  |  |  | 	.ndo_set_multicast_list	= set_multicast_list, | 
					
						
							|  |  |  | 	.ndo_do_ioctl		= sbni_ioctl, | 
					
						
							|  |  |  | 	.ndo_change_mtu		= eth_change_mtu, | 
					
						
							|  |  |  | 	.ndo_set_mac_address 	= eth_mac_addr, | 
					
						
							|  |  |  | 	.ndo_validate_addr	= eth_validate_addr, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | static void __init sbni_devsetup(struct net_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ether_setup( dev ); | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:20 +00:00
										 |  |  | 	dev->netdev_ops = &sbni_netdev_ops; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int __init sbni_probe(int unit) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct net_device *dev; | 
					
						
							|  |  |  | 	static unsigned  version_printed __initdata = 0; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev = alloc_netdev(sizeof(struct net_local), "sbni", sbni_devsetup); | 
					
						
							|  |  |  | 	if (!dev) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:20 +00:00
										 |  |  | 	dev->netdev_ops = &sbni_netdev_ops; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	sprintf(dev->name, "sbni%d", unit); | 
					
						
							|  |  |  | 	netdev_boot_setup_check(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = sbni_init(dev); | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							|  |  |  | 		free_netdev(dev); | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = register_netdev(dev); | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							|  |  |  | 		release_region( dev->base_addr, SBNI_IO_EXTENT ); | 
					
						
							|  |  |  | 		free_netdev(dev); | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if( version_printed++ == 0 ) | 
					
						
							|  |  |  | 		printk( KERN_INFO "%s", version ); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init sbni_init(struct net_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int  i; | 
					
						
							|  |  |  | 	if( dev->base_addr ) | 
					
						
							|  |  |  | 		return  sbni_isa_probe( dev ); | 
					
						
							|  |  |  | 	/* otherwise we have to perform search our adapter */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( io[ num ] != -1 ) | 
					
						
							|  |  |  | 		dev->base_addr	= io[ num ], | 
					
						
							|  |  |  | 		dev->irq	= irq[ num ]; | 
					
						
							|  |  |  | 	else if( scandone  ||  io[ 0 ] != -1 ) | 
					
						
							|  |  |  | 		return  -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* if io[ num ] contains non-zero address, then that is on ISA bus */ | 
					
						
							|  |  |  | 	if( dev->base_addr ) | 
					
						
							|  |  |  | 		return  sbni_isa_probe( dev ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* ...otherwise - scan PCI first */ | 
					
						
							|  |  |  | 	if( !skip_pci_probe  &&  !sbni_pci_probe( dev ) ) | 
					
						
							|  |  |  | 		return  0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( io[ num ] == -1 ) { | 
					
						
							|  |  |  | 		/* Auto-scan will be stopped when first ISA card were found */ | 
					
						
							|  |  |  | 		scandone = 1; | 
					
						
							|  |  |  | 		if( num > 0 ) | 
					
						
							|  |  |  | 			return  -ENODEV; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for( i = 0;  netcard_portlist[ i ];  ++i ) { | 
					
						
							|  |  |  | 		int  ioaddr = netcard_portlist[ i ]; | 
					
						
							| 
									
										
										
										
											2009-12-03 07:58:21 +00:00
										 |  |  | 		if( request_region( ioaddr, SBNI_IO_EXTENT, dev->name ) && | 
					
						
							|  |  |  | 		    sbni_probe1( dev, ioaddr, 0 )) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return  -ENODEV; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | static int __init | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | sbni_pci_probe( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct pci_dev  *pdev = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while( (pdev = pci_get_class( PCI_CLASS_NETWORK_OTHER << 8, pdev )) | 
					
						
							|  |  |  | 	       != NULL ) { | 
					
						
							|  |  |  | 		int  pci_irq_line; | 
					
						
							|  |  |  | 		unsigned long  pci_ioaddr; | 
					
						
							|  |  |  | 		u16  subsys; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-03 07:58:21 +00:00
										 |  |  | 		if( pdev->vendor != SBNI_PCI_VENDOR && | 
					
						
							|  |  |  | 		    pdev->device != SBNI_PCI_DEVICE ) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		pci_ioaddr = pci_resource_start( pdev, 0 ); | 
					
						
							|  |  |  | 		pci_irq_line = pdev->irq; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Avoid already found cards from previous calls */ | 
					
						
							|  |  |  | 		if( !request_region( pci_ioaddr, SBNI_IO_EXTENT, dev->name ) ) { | 
					
						
							|  |  |  | 			pci_read_config_word( pdev, PCI_SUBSYSTEM_ID, &subsys ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (subsys != 2) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* Dual adapter is present */ | 
					
						
							|  |  |  | 			if (!request_region(pci_ioaddr += 4, SBNI_IO_EXTENT, | 
					
						
							|  |  |  | 							dev->name ) ) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-08-19 20:49:50 -07:00
										 |  |  | 		if (pci_irq_line <= 0 || pci_irq_line >= nr_irqs) | 
					
						
							| 
									
										
										
										
											2009-07-06 13:05:40 -07:00
										 |  |  | 			printk( KERN_WARNING | 
					
						
							|  |  |  | 	"  WARNING: The PCI BIOS assigned this PCI card to IRQ %d, which is unlikely to work!.\n" | 
					
						
							|  |  |  | 	" You should use the PCI BIOS setup to assign a valid IRQ line.\n", | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 				pci_irq_line ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* avoiding re-enable dual adapters */ | 
					
						
							|  |  |  | 		if( (pci_ioaddr & 7) == 0  &&  pci_enable_device( pdev ) ) { | 
					
						
							|  |  |  | 			release_region( pci_ioaddr, SBNI_IO_EXTENT ); | 
					
						
							|  |  |  | 			pci_dev_put( pdev ); | 
					
						
							|  |  |  | 			return  -EIO; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if( sbni_probe1( dev, pci_ioaddr, pci_irq_line ) ) { | 
					
						
							|  |  |  | 			SET_NETDEV_DEV(dev, &pdev->dev); | 
					
						
							|  |  |  | 			/* not the best thing to do, but this is all messed up 
 | 
					
						
							|  |  |  | 			   for hotplug systems anyway... */ | 
					
						
							|  |  |  | 			pci_dev_put( pdev ); | 
					
						
							|  |  |  | 			return  0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return  -ENODEV; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct net_device * __init | 
					
						
							|  |  |  | sbni_probe1( struct net_device  *dev,  unsigned long  ioaddr,  int  irq ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct net_local  *nl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( sbni_card_probe( ioaddr ) ) { | 
					
						
							|  |  |  | 		release_region( ioaddr, SBNI_IO_EXTENT ); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outb( 0, ioaddr + CSR0 ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( irq < 2 ) { | 
					
						
							|  |  |  | 		unsigned long irq_mask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		irq_mask = probe_irq_on(); | 
					
						
							|  |  |  | 		outb( EN_INT | TR_REQ, ioaddr + CSR0 ); | 
					
						
							|  |  |  | 		outb( PR_RES, ioaddr + CSR1 ); | 
					
						
							|  |  |  | 		mdelay(50); | 
					
						
							|  |  |  | 		irq = probe_irq_off(irq_mask); | 
					
						
							|  |  |  | 		outb( 0, ioaddr + CSR0 ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if( !irq ) { | 
					
						
							|  |  |  | 			printk( KERN_ERR "%s: can't detect device irq!\n", | 
					
						
							|  |  |  | 				dev->name ); | 
					
						
							|  |  |  | 			release_region( ioaddr, SBNI_IO_EXTENT ); | 
					
						
							|  |  |  | 			return NULL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if( irq == 2 ) | 
					
						
							|  |  |  | 		irq = 9; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev->irq = irq; | 
					
						
							|  |  |  | 	dev->base_addr = ioaddr; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	/* Fill in sbni-specific dev fields. */ | 
					
						
							|  |  |  | 	nl = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	if( !nl ) { | 
					
						
							|  |  |  | 		printk( KERN_ERR "%s: unable to get memory!\n", dev->name ); | 
					
						
							|  |  |  | 		release_region( ioaddr, SBNI_IO_EXTENT ); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memset( nl, 0, sizeof(struct net_local) ); | 
					
						
							|  |  |  | 	spin_lock_init( &nl->lock ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* store MAC address (generate if that isn't known) */ | 
					
						
							| 
									
										
										
										
											2007-12-22 17:52:52 +00:00
										 |  |  | 	*(__be16 *)dev->dev_addr = htons( 0x00ff ); | 
					
						
							|  |  |  | 	*(__be32 *)(dev->dev_addr + 2) = htonl( 0x01000000 | | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 		((mac[num] ? | 
					
						
							|  |  |  | 		mac[num] : | 
					
						
							|  |  |  | 		(u32)((long)netdev_priv(dev))) & 0x00ffffff)); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* store link settings (speed, receive level ) */ | 
					
						
							|  |  |  | 	nl->maxframe  = DEFAULT_FRAME_LEN; | 
					
						
							|  |  |  | 	nl->csr1.rate = baud[ num ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( (nl->cur_rxl_index = rxl[ num ]) == -1 ) | 
					
						
							|  |  |  | 		/* autotune rxl */ | 
					
						
							|  |  |  | 		nl->cur_rxl_index = DEF_RXL, | 
					
						
							|  |  |  | 		nl->delta_rxl = DEF_RXL_DELTA; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		nl->delta_rxl = 0; | 
					
						
							|  |  |  | 	nl->csr1.rxl  = rxl_tab[ nl->cur_rxl_index ]; | 
					
						
							|  |  |  | 	if( inb( ioaddr + CSR0 ) & 0x01 ) | 
					
						
							|  |  |  | 		nl->state |= FL_SLOW_MODE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	printk( KERN_NOTICE "%s: ioaddr %#lx, irq %d, " | 
					
						
							|  |  |  | 		"MAC: 00:ff:01:%02x:%02x:%02x\n",  | 
					
						
							|  |  |  | 		dev->name, dev->base_addr, dev->irq, | 
					
						
							|  |  |  | 		((u8 *) dev->dev_addr) [3], | 
					
						
							|  |  |  | 		((u8 *) dev->dev_addr) [4], | 
					
						
							|  |  |  | 		((u8 *) dev->dev_addr) [5] ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	printk( KERN_NOTICE "%s: speed %d, receive level ", dev->name, | 
					
						
							|  |  |  | 		( (nl->state & FL_SLOW_MODE)  ?  500000 : 2000000) | 
					
						
							|  |  |  | 		/ (1 << nl->csr1.rate) ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->delta_rxl == 0 ) | 
					
						
							|  |  |  | 		printk( "0x%x (fixed)\n", nl->cur_rxl_index );  | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		printk( "(auto)\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 	nl->master = dev; | 
					
						
							|  |  |  | 	nl->link   = NULL; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     | 
					
						
							|  |  |  | 	sbni_cards[ num++ ] = dev; | 
					
						
							|  |  |  | 	return  dev; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------------- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-31 19:50:47 +00:00
										 |  |  | static netdev_tx_t | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | sbni_start_xmit( struct sk_buff  *skb,  struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct net_device  *p; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	netif_stop_queue( dev ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Looking for idle device in the list */ | 
					
						
							|  |  |  | 	for( p = dev;  p; ) { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 		struct net_local  *nl = netdev_priv(p); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		spin_lock( &nl->lock ); | 
					
						
							|  |  |  | 		if( nl->tx_buf_p  ||  (nl->state & FL_LINE_DOWN) ) { | 
					
						
							|  |  |  | 			p = nl->link; | 
					
						
							|  |  |  | 			spin_unlock( &nl->lock ); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			/* Idle dev is found */ | 
					
						
							|  |  |  | 			prepare_to_send( skb, p ); | 
					
						
							|  |  |  | 			spin_unlock( &nl->lock ); | 
					
						
							|  |  |  | 			netif_start_queue( dev ); | 
					
						
							| 
									
										
										
										
											2009-06-23 06:03:08 +00:00
										 |  |  | 			return NETDEV_TX_OK; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-06-12 06:22:29 +00:00
										 |  |  | 	return NETDEV_TX_BUSY; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #else	/* CONFIG_SBNI_MULTILINE */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-31 19:50:47 +00:00
										 |  |  | static netdev_tx_t | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | sbni_start_xmit( struct sk_buff  *skb,  struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl  = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	netif_stop_queue( dev ); | 
					
						
							|  |  |  | 	spin_lock( &nl->lock ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	prepare_to_send( skb, dev ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock( &nl->lock ); | 
					
						
							| 
									
										
										
										
											2009-06-23 06:03:08 +00:00
										 |  |  | 	return NETDEV_TX_OK; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif	/* CONFIG_SBNI_MULTILINE */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------------- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* interrupt handler */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * 	SBNI12D-10, -11/ISA boards within "common interrupt" mode could not | 
					
						
							|  |  |  |  * be looked as two independent single-channel devices. Every channel seems | 
					
						
							|  |  |  |  * as Ethernet interface but interrupt handler must be common. Really, first | 
					
						
							|  |  |  |  * channel ("master") driver only registers the handler. In its struct net_local | 
					
						
							|  |  |  |  * it has got pointer to "slave" channel's struct net_local and handles that's | 
					
						
							|  |  |  |  * interrupts too. | 
					
						
							|  |  |  |  *	dev of successfully attached ISA SBNI boards is linked to list. | 
					
						
							|  |  |  |  * While next board driver is initialized, it scans this list. If one | 
					
						
							|  |  |  |  * has found dev with same irq and ioaddr different by 4 then it assumes | 
					
						
							|  |  |  |  * this board to be "master". | 
					
						
							|  |  |  |  */  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static irqreturn_t | 
					
						
							| 
									
										
											  
											
												IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around.  On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable.  On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions.  Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller.  A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386.  I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs.  Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
	struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
	set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
	-	update_process_times(user_mode(regs));
	-	profile_tick(CPU_PROFILING, regs);
	+	update_process_times(user_mode(get_irq_regs()));
	+	profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
 (*) input_dev() is now gone entirely.  The regs pointer is no longer stored in
     the input_dev struct.
 (*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking.  It does
     something different depending on whether it's been supplied with a regs
     pointer or not.
 (*) Various IRQ handler function pointers have been moved to type
     irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
											
										 
											2006-10-05 14:55:46 +01:00
										 |  |  | sbni_interrupt( int  irq,  void  *dev_id ) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2007-10-19 03:10:11 -04:00
										 |  |  | 	struct net_device	  *dev = dev_id; | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl  = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	int	repeat; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock( &nl->lock ); | 
					
						
							|  |  |  | 	if( nl->second ) | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 		spin_lock(&NET_LOCAL_LOCK(nl->second)); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	do { | 
					
						
							|  |  |  | 		repeat = 0; | 
					
						
							|  |  |  | 		if( inb( dev->base_addr + CSR0 ) & (RC_RDY | TR_RDY) ) | 
					
						
							|  |  |  | 			handle_channel( dev ), | 
					
						
							|  |  |  | 			repeat = 1; | 
					
						
							|  |  |  | 		if( nl->second  && 	/* second channel present */ | 
					
						
							|  |  |  | 		    (inb( nl->second->base_addr+CSR0 ) & (RC_RDY | TR_RDY)) ) | 
					
						
							|  |  |  | 			handle_channel( nl->second ), | 
					
						
							|  |  |  | 			repeat = 1; | 
					
						
							|  |  |  | 	} while( repeat ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->second ) | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 		spin_unlock(&NET_LOCAL_LOCK(nl->second)); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	spin_unlock( &nl->lock ); | 
					
						
							|  |  |  | 	return IRQ_HANDLED; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | handle_channel( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local	*nl    = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	unsigned long		ioaddr = dev->base_addr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int  req_ans; | 
					
						
							|  |  |  | 	unsigned char  csr0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 	/* Lock the master device because we going to change its local data */ | 
					
						
							|  |  |  | 	if( nl->state & FL_SLAVE ) | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 		spin_lock(&NET_LOCAL_LOCK(nl->master)); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outb( (inb( ioaddr + CSR0 ) & ~EN_INT) | TR_REQ, ioaddr + CSR0 ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->timer_ticks = CHANGE_LEVEL_START_TICKS; | 
					
						
							|  |  |  | 	for(;;) { | 
					
						
							|  |  |  | 		csr0 = inb( ioaddr + CSR0 ); | 
					
						
							|  |  |  | 		if( ( csr0 & (RC_RDY | TR_RDY) ) == 0 ) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		req_ans = !(nl->state & FL_PREV_OK); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if( csr0 & RC_RDY ) | 
					
						
							|  |  |  | 			req_ans = recv_frame( dev ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * TR_RDY always equals 1 here because we have owned the marker, | 
					
						
							|  |  |  | 		 * and we set TR_REQ when disabled interrupts | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		csr0 = inb( ioaddr + CSR0 ); | 
					
						
							|  |  |  | 		if( !(csr0 & TR_RDY)  ||  (csr0 & RC_RDY) ) | 
					
						
							|  |  |  | 			printk( KERN_ERR "%s: internal error!\n", dev->name ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* if state & FL_NEED_RESEND != 0 then tx_frameno != 0 */ | 
					
						
							|  |  |  | 		if( req_ans  ||  nl->tx_frameno != 0 ) | 
					
						
							|  |  |  | 			send_frame( dev ); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			/* send marker without any data */ | 
					
						
							|  |  |  | 			outb( inb( ioaddr + CSR0 ) & ~TR_REQ, ioaddr + CSR0 ); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outb( inb( ioaddr + CSR0 ) | EN_INT, ioaddr + CSR0 ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 	if( nl->state & FL_SLAVE ) | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 		spin_unlock(&NET_LOCAL_LOCK(nl->master)); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Routine returns 1 if it need to acknoweledge received frame. | 
					
						
							|  |  |  |  * Empty frame received without errors won't be acknoweledged. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | recv_frame( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl   = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	unsigned long  ioaddr	= dev->base_addr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	u32  crc = CRC32_INITIAL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-07-17 01:56:32 -04:00
										 |  |  | 	unsigned  framelen = 0, frameno, ack; | 
					
						
							|  |  |  | 	unsigned  is_first, frame_ok = 0; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if( check_fhdr( ioaddr, &framelen, &frameno, &ack, &is_first, &crc ) ) { | 
					
						
							|  |  |  | 		frame_ok = framelen > 4 | 
					
						
							|  |  |  | 			?  upload_data( dev, framelen, frameno, is_first, crc ) | 
					
						
							|  |  |  | 			:  skip_tail( ioaddr, framelen, crc ); | 
					
						
							|  |  |  | 		if( frame_ok ) | 
					
						
							|  |  |  | 			interpret_ack( dev, ack ); | 
					
						
							| 
									
										
										
										
											2007-07-17 01:56:32 -04:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	outb( inb( ioaddr + CSR0 ) ^ CT_ZER, ioaddr + CSR0 ); | 
					
						
							|  |  |  | 	if( frame_ok ) { | 
					
						
							|  |  |  | 		nl->state |= FL_PREV_OK; | 
					
						
							|  |  |  | 		if( framelen > 4 ) | 
					
						
							|  |  |  | 			nl->in_stats.all_rx_number++; | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | 		nl->state &= ~FL_PREV_OK, | 
					
						
							|  |  |  | 		change_level( dev ), | 
					
						
							|  |  |  | 		nl->in_stats.all_rx_number++, | 
					
						
							|  |  |  | 		nl->in_stats.bad_rx_number++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return  !frame_ok  ||  framelen > 4; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | send_frame( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl    = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	u32  crc = CRC32_INITIAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->state & FL_NEED_RESEND ) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* if frame was sended but not ACK'ed - resend it */ | 
					
						
							|  |  |  | 		if( nl->trans_errors ) { | 
					
						
							|  |  |  | 			--nl->trans_errors; | 
					
						
							|  |  |  | 			if( nl->framelen != 0 ) | 
					
						
							|  |  |  | 				nl->in_stats.resend_tx_number++; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			/* cannot xmit with many attempts */ | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 			if( (nl->state & FL_SLAVE)  ||  nl->link ) | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 			nl->state |= FL_LINE_DOWN; | 
					
						
							|  |  |  | 			drop_xmit_queue( dev ); | 
					
						
							|  |  |  | 			goto  do_send; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | 		nl->trans_errors = TR_ERROR_COUNT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	send_frame_header( dev, &crc ); | 
					
						
							|  |  |  | 	nl->state |= FL_NEED_RESEND; | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * FL_NEED_RESEND will be cleared after ACK, but if empty | 
					
						
							|  |  |  | 	 * frame sended then in prepare_to_send next frame | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->framelen ) { | 
					
						
							|  |  |  | 		download_data( dev, &crc ); | 
					
						
							|  |  |  | 		nl->in_stats.all_tx_number++; | 
					
						
							|  |  |  | 		nl->state |= FL_WAIT_ACK; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outsb( dev->base_addr + DAT, (u8 *)&crc, sizeof crc ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | do_send: | 
					
						
							|  |  |  | 	outb( inb( dev->base_addr + CSR0 ) & ~TR_REQ, dev->base_addr + CSR0 ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->tx_frameno ) | 
					
						
							|  |  |  | 		/* next frame exists - we request card to send it */ | 
					
						
							|  |  |  | 		outb( inb( dev->base_addr + CSR0 ) | TR_REQ, | 
					
						
							|  |  |  | 		      dev->base_addr + CSR0 ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Write the frame data into adapter's buffer memory, and calculate CRC. | 
					
						
							|  |  |  |  * Do padding if necessary. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | download_data( struct net_device  *dev,  u32  *crc_p ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl    = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	struct sk_buff    *skb	 = nl->tx_buf_p; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	unsigned  len = min_t(unsigned int, skb->len - nl->outpos, nl->framelen); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outsb( dev->base_addr + DAT, skb->data + nl->outpos, len ); | 
					
						
							|  |  |  | 	*crc_p = calc_crc32( *crc_p, skb->data + nl->outpos, len ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* if packet too short we should write some more bytes to pad */ | 
					
						
							|  |  |  | 	for( len = nl->framelen - len;  len--; ) | 
					
						
							|  |  |  | 		outb( 0, dev->base_addr + DAT ), | 
					
						
							|  |  |  | 		*crc_p = CRC32( 0, *crc_p ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | upload_data( struct net_device  *dev,  unsigned  framelen,  unsigned  frameno, | 
					
						
							|  |  |  | 	     unsigned  is_first,  u32  crc ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	int  frame_ok; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( is_first ) | 
					
						
							|  |  |  | 		nl->wait_frameno = frameno, | 
					
						
							|  |  |  | 		nl->inppos = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->wait_frameno == frameno ) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if( nl->inppos + framelen  <=  ETHER_MAX_LEN ) | 
					
						
							|  |  |  | 			frame_ok = append_frame_to_pkt( dev, framelen, crc ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * if CRC is right but framelen incorrect then transmitter | 
					
						
							|  |  |  | 		 * error was occurred... drop entire packet | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		else if( (frame_ok = skip_tail( dev->base_addr, framelen, crc )) | 
					
						
							|  |  |  | 			 != 0 ) | 
					
						
							|  |  |  | 			nl->wait_frameno = 0, | 
					
						
							|  |  |  | 			nl->inppos = 0, | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 			nl->master->stats.rx_errors++, | 
					
						
							|  |  |  | 			nl->master->stats.rx_missed_errors++; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 		        dev->stats.rx_errors++, | 
					
						
							|  |  |  | 			dev->stats.rx_missed_errors++; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 			/* now skip all frames until is_first != 0 */ | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | 		frame_ok = skip_tail( dev->base_addr, framelen, crc ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( is_first  &&  !frame_ok ) | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Frame has been broken, but we had already stored | 
					
						
							|  |  |  | 		 * is_first... Drop entire packet. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		nl->wait_frameno = 0, | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 		nl->master->stats.rx_errors++, | 
					
						
							|  |  |  | 		nl->master->stats.rx_crc_errors++; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 		dev->stats.rx_errors++, | 
					
						
							|  |  |  | 		dev->stats.rx_crc_errors++; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return  frame_ok; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-03-05 18:36:28 -08:00
										 |  |  | static inline void | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | send_complete( struct net_device *dev ) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 	struct net_local  *nl = netdev_priv(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 	nl->master->stats.tx_packets++; | 
					
						
							|  |  |  | 	nl->master->stats.tx_bytes += nl->tx_buf_p->len; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 	dev->stats.tx_packets++; | 
					
						
							|  |  |  | 	dev->stats.tx_bytes += nl->tx_buf_p->len; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 	dev_kfree_skb_irq( nl->tx_buf_p ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->tx_buf_p = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->outpos = 0; | 
					
						
							|  |  |  | 	nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); | 
					
						
							|  |  |  | 	nl->framelen   = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | interpret_ack( struct net_device  *dev,  unsigned  ack ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if( ack == FRAME_SENT_OK ) { | 
					
						
							|  |  |  | 		nl->state &= ~FL_NEED_RESEND; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if( nl->state & FL_WAIT_ACK ) { | 
					
						
							|  |  |  | 			nl->outpos += nl->framelen; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if( --nl->tx_frameno ) | 
					
						
							|  |  |  | 				nl->framelen = min_t(unsigned int, | 
					
						
							|  |  |  | 						   nl->maxframe, | 
					
						
							|  |  |  | 						   nl->tx_buf_p->len - nl->outpos); | 
					
						
							|  |  |  | 			else | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 				send_complete( dev ), | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 				netif_wake_queue( nl->master ); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 				netif_wake_queue( dev ); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->state &= ~FL_WAIT_ACK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Glue received frame with previous fragments of packet. | 
					
						
							|  |  |  |  * Indicate packet when last frame would be accepted. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | append_frame_to_pkt( struct net_device  *dev,  unsigned  framelen,  u32  crc ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	u8  *p; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->inppos + framelen  >  ETHER_MAX_LEN ) | 
					
						
							|  |  |  | 		return  0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( !nl->rx_buf_p  &&  !(nl->rx_buf_p = get_rx_buf( dev )) ) | 
					
						
							|  |  |  | 		return  0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	p = nl->rx_buf_p->data + nl->inppos; | 
					
						
							|  |  |  | 	insb( dev->base_addr + DAT, p, framelen ); | 
					
						
							|  |  |  | 	if( calc_crc32( crc, p, framelen ) != CRC32_REMAINDER ) | 
					
						
							|  |  |  | 		return  0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->inppos += framelen - 4; | 
					
						
							|  |  |  | 	if( --nl->wait_frameno == 0 )		/* last frame received */ | 
					
						
							|  |  |  | 		indicate_pkt( dev ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return  1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Prepare to start output on adapter. | 
					
						
							|  |  |  |  * Transmitter will be actually activated when marker is accepted. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | prepare_to_send( struct sk_buff  *skb,  struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	unsigned int  len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* nl->tx_buf_p == NULL here! */ | 
					
						
							|  |  |  | 	if( nl->tx_buf_p ) | 
					
						
							|  |  |  | 		printk( KERN_ERR "%s: memory leak!\n", dev->name ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->outpos = 0; | 
					
						
							|  |  |  | 	nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	len = skb->len; | 
					
						
							|  |  |  | 	if( len < SBNI_MIN_LEN ) | 
					
						
							|  |  |  | 		len = SBNI_MIN_LEN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->tx_buf_p	= skb; | 
					
						
							| 
									
										
										
										
											2008-09-22 19:23:48 -07:00
										 |  |  | 	nl->tx_frameno	= DIV_ROUND_UP(len, nl->maxframe); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	nl->framelen	= len < nl->maxframe  ?  len  :  nl->maxframe; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outb( inb( dev->base_addr + CSR0 ) | TR_REQ,  dev->base_addr + CSR0 ); | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 	nl->master->trans_start = jiffies; | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 	dev->trans_start = jiffies; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | drop_xmit_queue( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->tx_buf_p ) | 
					
						
							|  |  |  | 		dev_kfree_skb_any( nl->tx_buf_p ), | 
					
						
							|  |  |  | 		nl->tx_buf_p = NULL, | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 		nl->master->stats.tx_errors++, | 
					
						
							|  |  |  | 		nl->master->stats.tx_carrier_errors++; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 		dev->stats.tx_errors++, | 
					
						
							|  |  |  | 		dev->stats.tx_carrier_errors++; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->tx_frameno	= 0; | 
					
						
							|  |  |  | 	nl->framelen	= 0; | 
					
						
							|  |  |  | 	nl->outpos	= 0; | 
					
						
							|  |  |  | 	nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 	netif_start_queue( nl->master ); | 
					
						
							|  |  |  | 	nl->master->trans_start = jiffies; | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 	netif_start_queue( dev ); | 
					
						
							|  |  |  | 	dev->trans_start = jiffies; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | send_frame_header( struct net_device  *dev,  u32  *crc_p ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl  = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	u32  crc = *crc_p; | 
					
						
							|  |  |  | 	u32  len_field = nl->framelen + 6;	/* CRC + frameno + reserved */ | 
					
						
							|  |  |  | 	u8   value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->state & FL_NEED_RESEND ) | 
					
						
							|  |  |  | 		len_field |= FRAME_RETRY;	/* non-first attempt... */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->outpos == 0 ) | 
					
						
							|  |  |  | 		len_field |= FRAME_FIRST; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	len_field |= (nl->state & FL_PREV_OK) ? FRAME_SENT_OK : FRAME_SENT_BAD; | 
					
						
							|  |  |  | 	outb( SBNI_SIG, dev->base_addr + DAT ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	value = (u8) len_field; | 
					
						
							|  |  |  | 	outb( value, dev->base_addr + DAT ); | 
					
						
							|  |  |  | 	crc = CRC32( value, crc ); | 
					
						
							|  |  |  | 	value = (u8) (len_field >> 8); | 
					
						
							|  |  |  | 	outb( value, dev->base_addr + DAT ); | 
					
						
							|  |  |  | 	crc = CRC32( value, crc ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outb( nl->tx_frameno, dev->base_addr + DAT ); | 
					
						
							|  |  |  | 	crc = CRC32( nl->tx_frameno, crc ); | 
					
						
							|  |  |  | 	outb( 0, dev->base_addr + DAT ); | 
					
						
							|  |  |  | 	crc = CRC32( 0, crc ); | 
					
						
							|  |  |  | 	*crc_p = crc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * if frame tail not needed (incorrect number or received twice), | 
					
						
							|  |  |  |  * it won't store, but CRC will be calculated | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | skip_tail( unsigned int  ioaddr,  unsigned int  tail_len,  u32 crc ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	while( tail_len-- ) | 
					
						
							|  |  |  | 		crc = CRC32( inb( ioaddr + DAT ), crc ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return  crc == CRC32_REMAINDER; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Preliminary checks if frame header is correct, calculates its CRC | 
					
						
							|  |  |  |  * and split it to simple fields | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | check_fhdr( u32  ioaddr,  u32  *framelen,  u32  *frameno,  u32  *ack, | 
					
						
							|  |  |  | 	    u32  *is_first,  u32  *crc_p ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32  crc = *crc_p; | 
					
						
							|  |  |  | 	u8   value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( inb( ioaddr + DAT ) != SBNI_SIG ) | 
					
						
							|  |  |  | 		return  0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	value = inb( ioaddr + DAT ); | 
					
						
							|  |  |  | 	*framelen = (u32)value; | 
					
						
							|  |  |  | 	crc = CRC32( value, crc ); | 
					
						
							|  |  |  | 	value = inb( ioaddr + DAT ); | 
					
						
							|  |  |  | 	*framelen |= ((u32)value) << 8; | 
					
						
							|  |  |  | 	crc = CRC32( value, crc ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*ack = *framelen & FRAME_ACK_MASK; | 
					
						
							|  |  |  | 	*is_first = (*framelen & FRAME_FIRST) != 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-03 07:58:21 +00:00
										 |  |  | 	if( (*framelen &= FRAME_LEN_MASK) < 6 || | 
					
						
							|  |  |  | 	    *framelen > SBNI_MAX_FRAME - 3 ) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		return  0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	value = inb( ioaddr + DAT ); | 
					
						
							|  |  |  | 	*frameno = (u32)value; | 
					
						
							|  |  |  | 	crc = CRC32( value, crc ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	crc = CRC32( inb( ioaddr + DAT ), crc );	/* reserved byte */ | 
					
						
							|  |  |  | 	*framelen -= 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*crc_p = crc; | 
					
						
							|  |  |  | 	return  1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct sk_buff * | 
					
						
							|  |  |  | get_rx_buf( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* +2 is to compensate for the alignment fixup below */ | 
					
						
							|  |  |  | 	struct sk_buff  *skb = dev_alloc_skb( ETHER_MAX_LEN + 2 ); | 
					
						
							|  |  |  | 	if( !skb ) | 
					
						
							|  |  |  | 		return  NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	skb_reserve( skb, 2 );		/* Align IP on longword boundaries */ | 
					
						
							|  |  |  | 	return  skb; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | indicate_pkt( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl  = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	struct sk_buff    *skb = nl->rx_buf_p; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	skb_put( skb, nl->inppos ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 	skb->protocol = eth_type_trans( skb, nl->master ); | 
					
						
							|  |  |  | 	netif_rx( skb ); | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 	++nl->master->stats.rx_packets; | 
					
						
							|  |  |  | 	nl->master->stats.rx_bytes += nl->inppos; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #else
 | 
					
						
							|  |  |  | 	skb->protocol = eth_type_trans( skb, dev ); | 
					
						
							|  |  |  | 	netif_rx( skb ); | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 	++dev->stats.rx_packets; | 
					
						
							|  |  |  | 	dev->stats.rx_bytes += nl->inppos; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 	nl->rx_buf_p = NULL;	/* protocol driver will clear this sk_buff */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------------- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Routine checks periodically wire activity and regenerates marker if | 
					
						
							|  |  |  |  * connect was inactive for a long time. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | sbni_watchdog( unsigned long  arg ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct net_device  *dev = (struct net_device *) arg; | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local   *nl  = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	struct timer_list  *w   = &nl->watchdog;  | 
					
						
							|  |  |  | 	unsigned long	   flags; | 
					
						
							|  |  |  | 	unsigned char	   csr0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave( &nl->lock, flags ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	csr0 = inb( dev->base_addr + CSR0 ); | 
					
						
							|  |  |  | 	if( csr0 & RC_CHK ) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if( nl->timer_ticks ) { | 
					
						
							|  |  |  | 			if( csr0 & (RC_RDY | BU_EMP) ) | 
					
						
							|  |  |  | 				/* receiving not active */ | 
					
						
							|  |  |  | 				nl->timer_ticks--; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			nl->in_stats.timeout_number++; | 
					
						
							|  |  |  | 			if( nl->delta_rxl ) | 
					
						
							|  |  |  | 				timeout_change_level( dev ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			outb( *(u_char *)&nl->csr1 | PR_RES, | 
					
						
							|  |  |  | 			      dev->base_addr + CSR1 ); | 
					
						
							|  |  |  | 			csr0 = inb( dev->base_addr + CSR0 ); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | 		nl->state &= ~FL_LINE_DOWN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outb( csr0 | RC_CHK, dev->base_addr + CSR0 );  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	init_timer( w ); | 
					
						
							|  |  |  | 	w->expires	= jiffies + SBNI_TIMEOUT; | 
					
						
							|  |  |  | 	w->data		= arg; | 
					
						
							|  |  |  | 	w->function	= sbni_watchdog; | 
					
						
							|  |  |  | 	add_timer( w ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_irqrestore( &nl->lock, flags ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static unsigned char  rxl_tab[] = { | 
					
						
							|  |  |  | 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, | 
					
						
							|  |  |  | 	0x0a, 0x0c, 0x0f, 0x16, 0x18, 0x1a, 0x1c, 0x1f | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define SIZE_OF_TIMEOUT_RXL_TAB 4
 | 
					
						
							|  |  |  | static unsigned char  timeout_rxl_tab[] = { | 
					
						
							|  |  |  | 	0x03, 0x05, 0x08, 0x0b | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------------- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | card_start( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	nl->timer_ticks = CHANGE_LEVEL_START_TICKS; | 
					
						
							|  |  |  | 	nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); | 
					
						
							|  |  |  | 	nl->state |= FL_PREV_OK; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->inppos = nl->outpos = 0; | 
					
						
							|  |  |  | 	nl->wait_frameno = 0; | 
					
						
							|  |  |  | 	nl->tx_frameno	 = 0; | 
					
						
							|  |  |  | 	nl->framelen	 = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outb( *(u_char *)&nl->csr1 | PR_RES, dev->base_addr + CSR1 ); | 
					
						
							|  |  |  | 	outb( EN_INT, dev->base_addr + CSR0 ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------------- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Receive level auto-selection */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | change_level( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->delta_rxl == 0 )	/* do not auto-negotiate RxL */ | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->cur_rxl_index == 0 ) | 
					
						
							|  |  |  | 		nl->delta_rxl = 1; | 
					
						
							|  |  |  | 	else if( nl->cur_rxl_index == 15 ) | 
					
						
							|  |  |  | 		nl->delta_rxl = -1; | 
					
						
							|  |  |  | 	else if( nl->cur_rxl_rcvd < nl->prev_rxl_rcvd ) | 
					
						
							|  |  |  | 		nl->delta_rxl = -nl->delta_rxl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->csr1.rxl = rxl_tab[ nl->cur_rxl_index += nl->delta_rxl ]; | 
					
						
							|  |  |  | 	inb( dev->base_addr + CSR0 );	/* needs for PCI cards */ | 
					
						
							|  |  |  | 	outb( *(u8 *)&nl->csr1, dev->base_addr + CSR1 ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->prev_rxl_rcvd = nl->cur_rxl_rcvd; | 
					
						
							|  |  |  | 	nl->cur_rxl_rcvd  = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | timeout_change_level( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	nl->cur_rxl_index = timeout_rxl_tab[ nl->timeout_rxl ]; | 
					
						
							|  |  |  | 	if( ++nl->timeout_rxl >= 4 ) | 
					
						
							|  |  |  | 		nl->timeout_rxl = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->csr1.rxl = rxl_tab[ nl->cur_rxl_index ]; | 
					
						
							|  |  |  | 	inb( dev->base_addr + CSR0 ); | 
					
						
							|  |  |  | 	outb( *(unsigned char *)&nl->csr1, dev->base_addr + CSR1 ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->prev_rxl_rcvd = nl->cur_rxl_rcvd; | 
					
						
							|  |  |  | 	nl->cur_rxl_rcvd  = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------------- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  *	Open/initialize the board.  | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | sbni_open( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local	*nl = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	struct timer_list	*w  = &nl->watchdog; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * For double ISA adapters within "common irq" mode, we have to | 
					
						
							|  |  |  | 	 * determine whether primary or secondary channel is initialized, | 
					
						
							|  |  |  | 	 * and set the irq handler only in first case. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if( dev->base_addr < 0x400 ) {		/* ISA only */ | 
					
						
							|  |  |  | 		struct net_device  **p = sbni_cards; | 
					
						
							|  |  |  | 		for( ;  *p  &&  p < sbni_cards + SBNI_MAX_NUM_CARDS;  ++p ) | 
					
						
							| 
									
										
										
										
											2009-12-03 07:58:21 +00:00
										 |  |  | 			if( (*p)->irq == dev->irq && | 
					
						
							|  |  |  | 			    ((*p)->base_addr == dev->base_addr + 4 || | 
					
						
							|  |  |  | 			     (*p)->base_addr == dev->base_addr - 4) && | 
					
						
							|  |  |  | 			    (*p)->flags & IFF_UP ) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 				((struct net_local *) (netdev_priv(*p))) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 					->second = dev; | 
					
						
							|  |  |  | 				printk( KERN_NOTICE "%s: using shared irq " | 
					
						
							|  |  |  | 					"with %s\n", dev->name, (*p)->name ); | 
					
						
							|  |  |  | 				nl->state |= FL_SECONDARY; | 
					
						
							|  |  |  | 				goto  handler_attached; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-07-01 19:29:39 -07:00
										 |  |  | 	if( request_irq(dev->irq, sbni_interrupt, IRQF_SHARED, dev->name, dev) ) { | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		printk( KERN_ERR "%s: unable to get IRQ %d.\n", | 
					
						
							|  |  |  | 			dev->name, dev->irq ); | 
					
						
							|  |  |  | 		return  -EAGAIN; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | handler_attached: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock( &nl->lock ); | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 	memset( &dev->stats, 0, sizeof(struct net_device_stats) ); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	memset( &nl->in_stats, 0, sizeof(struct sbni_in_stats) ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	card_start( dev ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	netif_start_queue( dev ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* set timer watchdog */ | 
					
						
							|  |  |  | 	init_timer( w ); | 
					
						
							|  |  |  | 	w->expires	= jiffies + SBNI_TIMEOUT; | 
					
						
							|  |  |  | 	w->data		= (unsigned long) dev; | 
					
						
							|  |  |  | 	w->function	= sbni_watchdog; | 
					
						
							|  |  |  | 	add_timer( w ); | 
					
						
							|  |  |  |     | 
					
						
							|  |  |  | 	spin_unlock( &nl->lock ); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | sbni_close( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->second  &&  nl->second->flags & IFF_UP ) { | 
					
						
							|  |  |  | 		printk( KERN_NOTICE "Secondary channel (%s) is active!\n", | 
					
						
							|  |  |  | 			nl->second->name ); | 
					
						
							|  |  |  | 		return  -EBUSY; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 	if( nl->state & FL_SLAVE ) | 
					
						
							|  |  |  | 		emancipate( dev ); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		while( nl->link )	/* it's master device! */ | 
					
						
							|  |  |  | 			emancipate( nl->link ); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock( &nl->lock ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nl->second = NULL; | 
					
						
							|  |  |  | 	drop_xmit_queue( dev );	 | 
					
						
							|  |  |  | 	netif_stop_queue( dev ); | 
					
						
							|  |  |  |     | 
					
						
							|  |  |  | 	del_timer( &nl->watchdog ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outb( 0, dev->base_addr + CSR0 ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( !(nl->state & FL_SECONDARY) ) | 
					
						
							|  |  |  | 		free_irq( dev->irq, dev ); | 
					
						
							|  |  |  | 	nl->state &= FL_SECONDARY; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock( &nl->lock ); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  | 	Valid combinations in CSR0 (for probing): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	VALID_DECODER	0000,0011,1011,1010 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				    	; 0   ; - | 
					
						
							|  |  |  | 				TR_REQ	; 1   ; + | 
					
						
							|  |  |  | 			TR_RDY	    	; 2   ; - | 
					
						
							|  |  |  | 			TR_RDY	TR_REQ	; 3   ; + | 
					
						
							|  |  |  | 		BU_EMP		    	; 4   ; + | 
					
						
							|  |  |  | 		BU_EMP	     	TR_REQ	; 5   ; + | 
					
						
							|  |  |  | 		BU_EMP	TR_RDY	    	; 6   ; - | 
					
						
							|  |  |  | 		BU_EMP	TR_RDY	TR_REQ	; 7   ; + | 
					
						
							|  |  |  | 	RC_RDY 		     		; 8   ; + | 
					
						
							|  |  |  | 	RC_RDY			TR_REQ	; 9   ; + | 
					
						
							|  |  |  | 	RC_RDY		TR_RDY		; 10  ; - | 
					
						
							|  |  |  | 	RC_RDY		TR_RDY	TR_REQ	; 11  ; - | 
					
						
							|  |  |  | 	RC_RDY	BU_EMP			; 12  ; - | 
					
						
							|  |  |  | 	RC_RDY	BU_EMP		TR_REQ	; 13  ; - | 
					
						
							|  |  |  | 	RC_RDY	BU_EMP	TR_RDY		; 14  ; - | 
					
						
							|  |  |  | 	RC_RDY	BU_EMP	TR_RDY	TR_REQ	; 15  ; - | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define VALID_DECODER (2 + 8 + 0x10 + 0x20 + 0x80 + 0x100 + 0x200)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | sbni_card_probe( unsigned long  ioaddr ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned char  csr0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	csr0 = inb( ioaddr + CSR0 ); | 
					
						
							|  |  |  | 	if( csr0 != 0xff  &&  csr0 != 0x00 ) { | 
					
						
							|  |  |  | 		csr0 &= ~EN_INT; | 
					
						
							|  |  |  | 		if( csr0 & BU_EMP ) | 
					
						
							|  |  |  | 			csr0 |= EN_INT; | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  | 		if( VALID_DECODER & (1 << (csr0 >> 4)) ) | 
					
						
							|  |  |  | 			return  0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  |     | 
					
						
							|  |  |  | 	return  -ENODEV; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------------- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | sbni_ioctl( struct net_device  *dev,  struct ifreq  *ifr,  int  cmd ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	struct sbni_flags  flags; | 
					
						
							|  |  |  | 	int  error = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 	struct net_device  *slave_dev; | 
					
						
							|  |  |  | 	char  slave_name[ 8 ]; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | 	switch( cmd ) { | 
					
						
							|  |  |  | 	case  SIOCDEVGETINSTATS : | 
					
						
							|  |  |  | 		if (copy_to_user( ifr->ifr_data, &nl->in_stats, | 
					
						
							|  |  |  | 					sizeof(struct sbni_in_stats) )) | 
					
						
							|  |  |  | 			error = -EFAULT; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case  SIOCDEVRESINSTATS : | 
					
						
							| 
									
										
										
										
											2008-08-27 04:50:30 -07:00
										 |  |  | 		if (!capable(CAP_NET_ADMIN)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return  -EPERM; | 
					
						
							|  |  |  | 		memset( &nl->in_stats, 0, sizeof(struct sbni_in_stats) ); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case  SIOCDEVGHWSTATE : | 
					
						
							|  |  |  | 		flags.mac_addr	= *(u32 *)(dev->dev_addr + 3); | 
					
						
							|  |  |  | 		flags.rate	= nl->csr1.rate; | 
					
						
							|  |  |  | 		flags.slow_mode	= (nl->state & FL_SLOW_MODE) != 0; | 
					
						
							|  |  |  | 		flags.rxl	= nl->cur_rxl_index; | 
					
						
							|  |  |  | 		flags.fixed_rxl	= nl->delta_rxl == 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (copy_to_user( ifr->ifr_data, &flags, sizeof flags )) | 
					
						
							|  |  |  | 			error = -EFAULT; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case  SIOCDEVSHWSTATE : | 
					
						
							| 
									
										
										
										
											2008-08-27 04:50:30 -07:00
										 |  |  | 		if (!capable(CAP_NET_ADMIN)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return  -EPERM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		spin_lock( &nl->lock ); | 
					
						
							|  |  |  | 		flags = *(struct sbni_flags*) &ifr->ifr_ifru; | 
					
						
							|  |  |  | 		if( flags.fixed_rxl ) | 
					
						
							|  |  |  | 			nl->delta_rxl = 0, | 
					
						
							|  |  |  | 			nl->cur_rxl_index = flags.rxl; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			nl->delta_rxl = DEF_RXL_DELTA, | 
					
						
							|  |  |  | 			nl->cur_rxl_index = DEF_RXL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		nl->csr1.rxl = rxl_tab[ nl->cur_rxl_index ]; | 
					
						
							|  |  |  | 		nl->csr1.rate = flags.rate; | 
					
						
							|  |  |  | 		outb( *(u8 *)&nl->csr1 | PR_RES, dev->base_addr + CSR1 ); | 
					
						
							|  |  |  | 		spin_unlock( &nl->lock ); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case  SIOCDEVENSLAVE : | 
					
						
							| 
									
										
										
										
											2008-08-27 04:50:30 -07:00
										 |  |  | 		if (!capable(CAP_NET_ADMIN)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return  -EPERM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (copy_from_user( slave_name, ifr->ifr_data, sizeof slave_name )) | 
					
						
							|  |  |  | 			return -EFAULT; | 
					
						
							| 
									
										
										
										
											2007-09-17 11:56:21 -07:00
										 |  |  | 		slave_dev = dev_get_by_name(&init_net, slave_name ); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if( !slave_dev  ||  !(slave_dev->flags & IFF_UP) ) { | 
					
						
							|  |  |  | 			printk( KERN_ERR "%s: trying to enslave non-active " | 
					
						
							|  |  |  | 				"device %s\n", dev->name, slave_name ); | 
					
						
							|  |  |  | 			return  -EPERM; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return  enslave( dev, slave_dev ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case  SIOCDEVEMANSIPATE : | 
					
						
							| 
									
										
										
										
											2008-08-27 04:50:30 -07:00
										 |  |  | 		if (!capable(CAP_NET_ADMIN)) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 			return  -EPERM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return  emancipate( dev ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif	/* CONFIG_SBNI_MULTILINE */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default : | 
					
						
							|  |  |  | 		return  -EOPNOTSUPP; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return  error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_SBNI_MULTILINE
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | enslave( struct net_device  *dev,  struct net_device  *slave_dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local  *nl  = netdev_priv(dev); | 
					
						
							|  |  |  | 	struct net_local  *snl = netdev_priv(slave_dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if( nl->state & FL_SLAVE )	/* This isn't master or free device */ | 
					
						
							|  |  |  | 		return  -EBUSY; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( snl->state & FL_SLAVE )	/* That was already enslaved */ | 
					
						
							|  |  |  | 		return  -EBUSY; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock( &nl->lock ); | 
					
						
							|  |  |  | 	spin_lock( &snl->lock ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* append to list */ | 
					
						
							|  |  |  | 	snl->link = nl->link; | 
					
						
							|  |  |  | 	nl->link  = slave_dev; | 
					
						
							|  |  |  | 	snl->master = dev; | 
					
						
							|  |  |  | 	snl->state |= FL_SLAVE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Summary statistics of MultiLine operation will be stored
 | 
					
						
							|  |  |  | 	   in master's counters */ | 
					
						
							| 
									
										
										
										
											2009-03-20 19:36:19 +00:00
										 |  |  | 	memset( &slave_dev->stats, 0, sizeof(struct net_device_stats) ); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	netif_stop_queue( slave_dev ); | 
					
						
							|  |  |  | 	netif_wake_queue( dev );	/* Now we are able to transmit */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock( &snl->lock ); | 
					
						
							|  |  |  | 	spin_unlock( &nl->lock ); | 
					
						
							|  |  |  | 	printk( KERN_NOTICE "%s: slave device (%s) attached.\n", | 
					
						
							|  |  |  | 		dev->name, slave_dev->name ); | 
					
						
							|  |  |  | 	return  0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | emancipate( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local   *snl = netdev_priv(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 	struct net_device  *p   = snl->master; | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 	struct net_local   *nl  = netdev_priv(p); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if( !(snl->state & FL_SLAVE) ) | 
					
						
							|  |  |  | 		return  -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock( &nl->lock ); | 
					
						
							|  |  |  | 	spin_lock( &snl->lock ); | 
					
						
							|  |  |  | 	drop_xmit_queue( dev ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* exclude from list */ | 
					
						
							|  |  |  | 	for(;;) {	/* must be in list */ | 
					
						
							| 
									
										
										
										
											2008-11-24 14:52:16 -08:00
										 |  |  | 		struct net_local  *t = netdev_priv(p); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		if( t->link == dev ) { | 
					
						
							|  |  |  | 			t->link = snl->link; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		p = t->link; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	snl->link = NULL; | 
					
						
							|  |  |  | 	snl->master = dev; | 
					
						
							|  |  |  | 	snl->state &= ~FL_SLAVE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	netif_start_queue( dev ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock( &snl->lock ); | 
					
						
							|  |  |  | 	spin_unlock( &nl->lock ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev_put( dev ); | 
					
						
							|  |  |  | 	return  0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | set_multicast_list( struct net_device  *dev ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return;		/* sbni always operate in promiscuos mode */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef MODULE
 | 
					
						
							|  |  |  | module_param_array(io, int, NULL, 0); | 
					
						
							|  |  |  | module_param_array(irq, int, NULL, 0); | 
					
						
							|  |  |  | module_param_array(baud, int, NULL, 0); | 
					
						
							|  |  |  | module_param_array(rxl, int, NULL, 0); | 
					
						
							|  |  |  | module_param_array(mac, int, NULL, 0); | 
					
						
							|  |  |  | module_param(skip_pci_probe, bool, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2006-03-20 22:34:52 -08:00
										 |  |  | int __init init_module( void ) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct net_device  *dev; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while( num < SBNI_MAX_NUM_CARDS ) { | 
					
						
							|  |  |  | 		dev = alloc_netdev(sizeof(struct net_local),  | 
					
						
							|  |  |  | 				   "sbni%d", sbni_devsetup); | 
					
						
							|  |  |  | 		if( !dev) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		sprintf( dev->name, "sbni%d", num ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		err = sbni_init(dev); | 
					
						
							|  |  |  | 		if (err) { | 
					
						
							|  |  |  | 			free_netdev(dev); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if( register_netdev( dev ) ) { | 
					
						
							|  |  |  | 			release_region( dev->base_addr, SBNI_IO_EXTENT ); | 
					
						
							|  |  |  | 			free_netdev( dev ); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return  *sbni_cards  ?  0  :  -ENODEV; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							| 
									
										
										
										
											2009-02-14 11:48:25 +00:00
										 |  |  | cleanup_module(void) | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2009-02-14 11:48:25 +00:00
										 |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-02-14 11:48:25 +00:00
										 |  |  | 	for (i = 0;  i < SBNI_MAX_NUM_CARDS;  ++i) { | 
					
						
							|  |  |  | 		struct net_device *dev = sbni_cards[i]; | 
					
						
							|  |  |  | 		if (dev != NULL) { | 
					
						
							|  |  |  | 			unregister_netdev(dev); | 
					
						
							|  |  |  | 			release_region(dev->base_addr, SBNI_IO_EXTENT); | 
					
						
							|  |  |  | 			free_netdev(dev); | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2009-02-14 11:48:25 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2005-04-16 15:20:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #else	/* MODULE */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init | 
					
						
							|  |  |  | sbni_setup( char  *p ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int  n, parm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if( *p++ != '(' ) | 
					
						
							|  |  |  | 		goto  bad_param; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for( n = 0, parm = 0;  *p  &&  n < 8; ) { | 
					
						
							|  |  |  | 		(*dest[ parm ])[ n ] = simple_strtol( p, &p, 0 ); | 
					
						
							|  |  |  | 		if( !*p  ||  *p == ')' ) | 
					
						
							|  |  |  | 			return 1; | 
					
						
							|  |  |  | 		if( *p == ';' ) | 
					
						
							|  |  |  | 			++p, ++n, parm = 0; | 
					
						
							|  |  |  | 		else if( *p++ != ',' ) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			if( ++parm >= 5 ) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | bad_param: | 
					
						
							|  |  |  | 	printk( KERN_ERR "Error in sbni kernel parameter!\n" ); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __setup( "sbni=", sbni_setup ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif	/* MODULE */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------------- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef ASM_CRC
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static u32 | 
					
						
							|  |  |  | calc_crc32( u32  crc,  u8  *p,  u32  len ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	register u32  _crc; | 
					
						
							|  |  |  | 	_crc = crc; | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	__asm__ __volatile__ ( | 
					
						
							|  |  |  | 		"xorl	%%ebx, %%ebx\n" | 
					
						
							|  |  |  | 		"movl	%2, %%esi\n"  | 
					
						
							|  |  |  | 		"movl	%3, %%ecx\n"  | 
					
						
							|  |  |  | 		"movl	$crc32tab, %%edi\n" | 
					
						
							|  |  |  | 		"shrl	$2, %%ecx\n" | 
					
						
							|  |  |  | 		"jz	1f\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		".align 4\n" | 
					
						
							|  |  |  | 	"0:\n" | 
					
						
							|  |  |  | 		"movb	%%al, %%bl\n" | 
					
						
							|  |  |  | 		"movl	(%%esi), %%edx\n" | 
					
						
							|  |  |  | 		"shrl	$8, %%eax\n" | 
					
						
							|  |  |  | 		"xorb	%%dl, %%bl\n" | 
					
						
							|  |  |  | 		"shrl	$8, %%edx\n" | 
					
						
							|  |  |  | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		"movb	%%al, %%bl\n" | 
					
						
							|  |  |  | 		"shrl	$8, %%eax\n" | 
					
						
							|  |  |  | 		"xorb	%%dl, %%bl\n" | 
					
						
							|  |  |  | 		"shrl	$8, %%edx\n" | 
					
						
							|  |  |  | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		"movb	%%al, %%bl\n" | 
					
						
							|  |  |  | 		"shrl	$8, %%eax\n" | 
					
						
							|  |  |  | 		"xorb	%%dl, %%bl\n" | 
					
						
							|  |  |  | 		"movb	%%dh, %%dl\n"  | 
					
						
							|  |  |  | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		"movb	%%al, %%bl\n" | 
					
						
							|  |  |  | 		"shrl	$8, %%eax\n" | 
					
						
							|  |  |  | 		"xorb	%%dl, %%bl\n" | 
					
						
							|  |  |  | 		"addl	$4, %%esi\n" | 
					
						
							|  |  |  | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		"decl	%%ecx\n" | 
					
						
							|  |  |  | 		"jnz	0b\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"1:\n" | 
					
						
							|  |  |  | 		"movl	%3, %%ecx\n" | 
					
						
							|  |  |  | 		"andl	$3, %%ecx\n" | 
					
						
							|  |  |  | 		"jz	2f\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		"movb	%%al, %%bl\n" | 
					
						
							|  |  |  | 		"shrl	$8, %%eax\n" | 
					
						
							|  |  |  | 		"xorb	(%%esi), %%bl\n" | 
					
						
							|  |  |  | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		"decl	%%ecx\n" | 
					
						
							|  |  |  | 		"jz	2f\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		"movb	%%al, %%bl\n" | 
					
						
							|  |  |  | 		"shrl	$8, %%eax\n" | 
					
						
							|  |  |  | 		"xorb	1(%%esi), %%bl\n" | 
					
						
							|  |  |  | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		"decl	%%ecx\n" | 
					
						
							|  |  |  | 		"jz	2f\n" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		"movb	%%al, %%bl\n" | 
					
						
							|  |  |  | 		"shrl	$8, %%eax\n" | 
					
						
							|  |  |  | 		"xorb	2(%%esi), %%bl\n" | 
					
						
							|  |  |  | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
					
						
							|  |  |  | 	"2:\n" | 
					
						
							|  |  |  | 		: "=a" (_crc) | 
					
						
							|  |  |  | 		: "0" (_crc), "g" (p), "g" (len) | 
					
						
							|  |  |  | 		: "bx", "cx", "dx", "si", "di" | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return  _crc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #else	/* ASM_CRC */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static u32 | 
					
						
							|  |  |  | calc_crc32( u32  crc,  u8  *p,  u32  len ) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	while( len-- ) | 
					
						
							|  |  |  | 		crc = CRC32( *p++, crc ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return  crc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif	/* ASM_CRC */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static u32  crc32tab[] __attribute__ ((aligned(8))) = { | 
					
						
							|  |  |  | 	0xD202EF8D,  0xA505DF1B,  0x3C0C8EA1,  0x4B0BBE37, | 
					
						
							|  |  |  | 	0xD56F2B94,  0xA2681B02,  0x3B614AB8,  0x4C667A2E, | 
					
						
							|  |  |  | 	0xDCD967BF,  0xABDE5729,  0x32D70693,  0x45D03605, | 
					
						
							|  |  |  | 	0xDBB4A3A6,  0xACB39330,  0x35BAC28A,  0x42BDF21C, | 
					
						
							|  |  |  | 	0xCFB5FFE9,  0xB8B2CF7F,  0x21BB9EC5,  0x56BCAE53, | 
					
						
							|  |  |  | 	0xC8D83BF0,  0xBFDF0B66,  0x26D65ADC,  0x51D16A4A, | 
					
						
							|  |  |  | 	0xC16E77DB,  0xB669474D,  0x2F6016F7,  0x58672661, | 
					
						
							|  |  |  | 	0xC603B3C2,  0xB1048354,  0x280DD2EE,  0x5F0AE278, | 
					
						
							|  |  |  | 	0xE96CCF45,  0x9E6BFFD3,  0x0762AE69,  0x70659EFF, | 
					
						
							|  |  |  | 	0xEE010B5C,  0x99063BCA,  0x000F6A70,  0x77085AE6, | 
					
						
							|  |  |  | 	0xE7B74777,  0x90B077E1,  0x09B9265B,  0x7EBE16CD, | 
					
						
							|  |  |  | 	0xE0DA836E,  0x97DDB3F8,  0x0ED4E242,  0x79D3D2D4, | 
					
						
							|  |  |  | 	0xF4DBDF21,  0x83DCEFB7,  0x1AD5BE0D,  0x6DD28E9B, | 
					
						
							|  |  |  | 	0xF3B61B38,  0x84B12BAE,  0x1DB87A14,  0x6ABF4A82, | 
					
						
							|  |  |  | 	0xFA005713,  0x8D076785,  0x140E363F,  0x630906A9, | 
					
						
							|  |  |  | 	0xFD6D930A,  0x8A6AA39C,  0x1363F226,  0x6464C2B0, | 
					
						
							|  |  |  | 	0xA4DEAE1D,  0xD3D99E8B,  0x4AD0CF31,  0x3DD7FFA7, | 
					
						
							|  |  |  | 	0xA3B36A04,  0xD4B45A92,  0x4DBD0B28,  0x3ABA3BBE, | 
					
						
							|  |  |  | 	0xAA05262F,  0xDD0216B9,  0x440B4703,  0x330C7795, | 
					
						
							|  |  |  | 	0xAD68E236,  0xDA6FD2A0,  0x4366831A,  0x3461B38C, | 
					
						
							|  |  |  | 	0xB969BE79,  0xCE6E8EEF,  0x5767DF55,  0x2060EFC3, | 
					
						
							|  |  |  | 	0xBE047A60,  0xC9034AF6,  0x500A1B4C,  0x270D2BDA, | 
					
						
							|  |  |  | 	0xB7B2364B,  0xC0B506DD,  0x59BC5767,  0x2EBB67F1, | 
					
						
							|  |  |  | 	0xB0DFF252,  0xC7D8C2C4,  0x5ED1937E,  0x29D6A3E8, | 
					
						
							|  |  |  | 	0x9FB08ED5,  0xE8B7BE43,  0x71BEEFF9,  0x06B9DF6F, | 
					
						
							|  |  |  | 	0x98DD4ACC,  0xEFDA7A5A,  0x76D32BE0,  0x01D41B76, | 
					
						
							|  |  |  | 	0x916B06E7,  0xE66C3671,  0x7F6567CB,  0x0862575D, | 
					
						
							|  |  |  | 	0x9606C2FE,  0xE101F268,  0x7808A3D2,  0x0F0F9344, | 
					
						
							|  |  |  | 	0x82079EB1,  0xF500AE27,  0x6C09FF9D,  0x1B0ECF0B, | 
					
						
							|  |  |  | 	0x856A5AA8,  0xF26D6A3E,  0x6B643B84,  0x1C630B12, | 
					
						
							|  |  |  | 	0x8CDC1683,  0xFBDB2615,  0x62D277AF,  0x15D54739, | 
					
						
							|  |  |  | 	0x8BB1D29A,  0xFCB6E20C,  0x65BFB3B6,  0x12B88320, | 
					
						
							|  |  |  | 	0x3FBA6CAD,  0x48BD5C3B,  0xD1B40D81,  0xA6B33D17, | 
					
						
							|  |  |  | 	0x38D7A8B4,  0x4FD09822,  0xD6D9C998,  0xA1DEF90E, | 
					
						
							|  |  |  | 	0x3161E49F,  0x4666D409,  0xDF6F85B3,  0xA868B525, | 
					
						
							|  |  |  | 	0x360C2086,  0x410B1010,  0xD80241AA,  0xAF05713C, | 
					
						
							|  |  |  | 	0x220D7CC9,  0x550A4C5F,  0xCC031DE5,  0xBB042D73, | 
					
						
							|  |  |  | 	0x2560B8D0,  0x52678846,  0xCB6ED9FC,  0xBC69E96A, | 
					
						
							|  |  |  | 	0x2CD6F4FB,  0x5BD1C46D,  0xC2D895D7,  0xB5DFA541, | 
					
						
							|  |  |  | 	0x2BBB30E2,  0x5CBC0074,  0xC5B551CE,  0xB2B26158, | 
					
						
							|  |  |  | 	0x04D44C65,  0x73D37CF3,  0xEADA2D49,  0x9DDD1DDF, | 
					
						
							|  |  |  | 	0x03B9887C,  0x74BEB8EA,  0xEDB7E950,  0x9AB0D9C6, | 
					
						
							|  |  |  | 	0x0A0FC457,  0x7D08F4C1,  0xE401A57B,  0x930695ED, | 
					
						
							|  |  |  | 	0x0D62004E,  0x7A6530D8,  0xE36C6162,  0x946B51F4, | 
					
						
							|  |  |  | 	0x19635C01,  0x6E646C97,  0xF76D3D2D,  0x806A0DBB, | 
					
						
							|  |  |  | 	0x1E0E9818,  0x6909A88E,  0xF000F934,  0x8707C9A2, | 
					
						
							|  |  |  | 	0x17B8D433,  0x60BFE4A5,  0xF9B6B51F,  0x8EB18589, | 
					
						
							|  |  |  | 	0x10D5102A,  0x67D220BC,  0xFEDB7106,  0x89DC4190, | 
					
						
							|  |  |  | 	0x49662D3D,  0x3E611DAB,  0xA7684C11,  0xD06F7C87, | 
					
						
							|  |  |  | 	0x4E0BE924,  0x390CD9B2,  0xA0058808,  0xD702B89E, | 
					
						
							|  |  |  | 	0x47BDA50F,  0x30BA9599,  0xA9B3C423,  0xDEB4F4B5, | 
					
						
							|  |  |  | 	0x40D06116,  0x37D75180,  0xAEDE003A,  0xD9D930AC, | 
					
						
							|  |  |  | 	0x54D13D59,  0x23D60DCF,  0xBADF5C75,  0xCDD86CE3, | 
					
						
							|  |  |  | 	0x53BCF940,  0x24BBC9D6,  0xBDB2986C,  0xCAB5A8FA, | 
					
						
							|  |  |  | 	0x5A0AB56B,  0x2D0D85FD,  0xB404D447,  0xC303E4D1, | 
					
						
							|  |  |  | 	0x5D677172,  0x2A6041E4,  0xB369105E,  0xC46E20C8, | 
					
						
							|  |  |  | 	0x72080DF5,  0x050F3D63,  0x9C066CD9,  0xEB015C4F, | 
					
						
							|  |  |  | 	0x7565C9EC,  0x0262F97A,  0x9B6BA8C0,  0xEC6C9856, | 
					
						
							|  |  |  | 	0x7CD385C7,  0x0BD4B551,  0x92DDE4EB,  0xE5DAD47D, | 
					
						
							|  |  |  | 	0x7BBE41DE,  0x0CB97148,  0x95B020F2,  0xE2B71064, | 
					
						
							|  |  |  | 	0x6FBF1D91,  0x18B82D07,  0x81B17CBD,  0xF6B64C2B, | 
					
						
							|  |  |  | 	0x68D2D988,  0x1FD5E91E,  0x86DCB8A4,  0xF1DB8832, | 
					
						
							|  |  |  | 	0x616495A3,  0x1663A535,  0x8F6AF48F,  0xF86DC419, | 
					
						
							|  |  |  | 	0x660951BA,  0x110E612C,  0x88073096,  0xFF000000 | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 |