| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* sbni.c:  Granch SBNI12 leased line adapters driver for linux | 
 | 2 |  * | 
 | 3 |  *	Written 2001 by Denis I.Timofeev (timofeev@granch.ru) | 
 | 4 |  * | 
 | 5 |  *	Previous versions were written by Yaroslav Polyakov, | 
 | 6 |  *	Alexey Zverev and Max Khon. | 
 | 7 |  * | 
 | 8 |  *	Driver supports SBNI12-02,-04,-05,-10,-11 cards, single and | 
 | 9 |  *	double-channel, PCI and ISA modifications. | 
 | 10 |  *	More info and useful utilities to work with SBNI12 cards you can find | 
 | 11 |  *	at http://www.granch.com (English) or http://www.granch.ru (Russian) | 
 | 12 |  * | 
 | 13 |  *	This software may be used and distributed according to the terms | 
 | 14 |  *	of the GNU General Public License. | 
 | 15 |  * | 
 | 16 |  * | 
 | 17 |  *  5.0.1	Jun 22 2001 | 
 | 18 |  *	  - Fixed bug in probe | 
 | 19 |  *  5.0.0	Jun 06 2001 | 
 | 20 |  *	  - Driver was completely redesigned by Denis I.Timofeev, | 
 | 21 |  *	  - now PCI/Dual, ISA/Dual (with single interrupt line) models are | 
 | 22 |  *	  - supported | 
 | 23 |  *  3.3.0	Thu Feb 24 21:30:28 NOVT 2000  | 
 | 24 |  *        - PCI cards support | 
 | 25 |  *  3.2.0	Mon Dec 13 22:26:53 NOVT 1999 | 
 | 26 |  * 	  - Completely rebuilt all the packet storage system | 
 | 27 |  * 	  -    to work in Ethernet-like style. | 
 | 28 |  *  3.1.1	just fixed some bugs (5 aug 1999) | 
 | 29 |  *  3.1.0	added balancing feature	(26 apr 1999) | 
 | 30 |  *  3.0.1	just fixed some bugs (14 apr 1999). | 
 | 31 |  *  3.0.0	Initial Revision, Yaroslav Polyakov (24 Feb 1999) | 
 | 32 |  *        - added pre-calculation for CRC, fixed bug with "len-2" frames,  | 
 | 33 |  *        - removed outbound fragmentation (MTU=1000), written CRC-calculation  | 
 | 34 |  *        - on asm, added work with hard_headers and now we have our own cache  | 
 | 35 |  *        - for them, optionally supported word-interchange on some chipsets, | 
 | 36 |  *  | 
 | 37 |  *	Known problem: this driver wasn't tested on multiprocessor machine. | 
 | 38 |  */ | 
 | 39 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | #include <linux/module.h> | 
 | 41 | #include <linux/kernel.h> | 
 | 42 | #include <linux/ptrace.h> | 
 | 43 | #include <linux/fcntl.h> | 
 | 44 | #include <linux/ioport.h> | 
 | 45 | #include <linux/interrupt.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 46 | #include <linux/string.h> | 
 | 47 | #include <linux/errno.h> | 
 | 48 | #include <linux/netdevice.h> | 
 | 49 | #include <linux/etherdevice.h> | 
 | 50 | #include <linux/pci.h> | 
 | 51 | #include <linux/skbuff.h> | 
 | 52 | #include <linux/timer.h> | 
 | 53 | #include <linux/init.h> | 
 | 54 | #include <linux/delay.h> | 
 | 55 |  | 
| Eric W. Biederman | 881d966 | 2007-09-17 11:56:21 -0700 | [diff] [blame] | 56 | #include <net/net_namespace.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 57 | #include <net/arp.h> | 
 | 58 |  | 
 | 59 | #include <asm/io.h> | 
 | 60 | #include <asm/types.h> | 
 | 61 | #include <asm/byteorder.h> | 
 | 62 | #include <asm/irq.h> | 
 | 63 | #include <asm/uaccess.h> | 
 | 64 |  | 
 | 65 | #include "sbni.h" | 
 | 66 |  | 
 | 67 | /* device private data */ | 
 | 68 |  | 
 | 69 | struct net_local { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 70 | 	struct timer_list	watchdog; | 
 | 71 |  | 
 | 72 | 	spinlock_t	lock; | 
 | 73 | 	struct sk_buff  *rx_buf_p;		/* receive buffer ptr */ | 
 | 74 | 	struct sk_buff  *tx_buf_p;		/* transmit buffer ptr */ | 
 | 75 | 	 | 
 | 76 | 	unsigned int	framelen;		/* current frame length */ | 
 | 77 | 	unsigned int	maxframe;		/* maximum valid frame length */ | 
 | 78 | 	unsigned int	state; | 
 | 79 | 	unsigned int	inppos, outpos;		/* positions in rx/tx buffers */ | 
 | 80 |  | 
 | 81 | 	/* transmitting frame number - from frames qty to 1 */ | 
 | 82 | 	unsigned int	tx_frameno; | 
 | 83 |  | 
 | 84 | 	/* expected number of next receiving frame */ | 
 | 85 | 	unsigned int	wait_frameno; | 
 | 86 |  | 
 | 87 | 	/* count of failed attempts to frame send - 32 attempts do before | 
 | 88 | 	   error - while receiver tunes on opposite side of wire */ | 
 | 89 | 	unsigned int	trans_errors; | 
 | 90 |  | 
 | 91 | 	/* idle time; send pong when limit exceeded */ | 
 | 92 | 	unsigned int	timer_ticks; | 
 | 93 |  | 
 | 94 | 	/* fields used for receive level autoselection */ | 
 | 95 | 	int	delta_rxl; | 
 | 96 | 	unsigned int	cur_rxl_index, timeout_rxl; | 
 | 97 | 	unsigned long	cur_rxl_rcvd, prev_rxl_rcvd; | 
 | 98 |  | 
 | 99 | 	struct sbni_csr1	csr1;		/* current value of CSR1 */ | 
 | 100 | 	struct sbni_in_stats	in_stats; 	/* internal statistics */  | 
 | 101 |  | 
 | 102 | 	struct net_device		*second;	/* for ISA/dual cards */ | 
 | 103 |  | 
 | 104 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 105 | 	struct net_device		*master; | 
 | 106 | 	struct net_device		*link; | 
 | 107 | #endif | 
 | 108 | }; | 
 | 109 |  | 
 | 110 |  | 
 | 111 | static int  sbni_card_probe( unsigned long ); | 
 | 112 | static int  sbni_pci_probe( struct net_device  * ); | 
 | 113 | static struct net_device  *sbni_probe1(struct net_device *, unsigned long, int); | 
 | 114 | static int  sbni_open( struct net_device * ); | 
 | 115 | static int  sbni_close( struct net_device * ); | 
| Stephen Hemminger | d71a674 | 2009-08-31 19:50:47 +0000 | [diff] [blame] | 116 | static netdev_tx_t sbni_start_xmit(struct sk_buff *, | 
 | 117 | 					 struct net_device * ); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 118 | static int  sbni_ioctl( struct net_device *, struct ifreq *, int ); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 119 | static void  set_multicast_list( struct net_device * ); | 
 | 120 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 121 | static irqreturn_t sbni_interrupt( int, void * ); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 122 | static void  handle_channel( struct net_device * ); | 
 | 123 | static int   recv_frame( struct net_device * ); | 
 | 124 | static void  send_frame( struct net_device * ); | 
 | 125 | static int   upload_data( struct net_device *, | 
 | 126 | 			  unsigned, unsigned, unsigned, u32 ); | 
 | 127 | static void  download_data( struct net_device *, u32 * ); | 
 | 128 | static void  sbni_watchdog( unsigned long ); | 
 | 129 | static void  interpret_ack( struct net_device *, unsigned ); | 
 | 130 | static int   append_frame_to_pkt( struct net_device *, unsigned, u32 ); | 
 | 131 | static void  indicate_pkt( struct net_device * ); | 
 | 132 | static void  card_start( struct net_device * ); | 
 | 133 | static void  prepare_to_send( struct sk_buff *, struct net_device * ); | 
 | 134 | static void  drop_xmit_queue( struct net_device * ); | 
 | 135 | static void  send_frame_header( struct net_device *, u32 * ); | 
 | 136 | static int   skip_tail( unsigned int, unsigned int, u32 ); | 
 | 137 | static int   check_fhdr( u32, u32 *, u32 *, u32 *, u32 *, u32 * ); | 
 | 138 | static void  change_level( struct net_device * ); | 
 | 139 | static void  timeout_change_level( struct net_device * ); | 
 | 140 | static u32   calc_crc32( u32, u8 *, u32 ); | 
 | 141 | static struct sk_buff *  get_rx_buf( struct net_device * ); | 
 | 142 | static int  sbni_init( struct net_device * ); | 
 | 143 |  | 
 | 144 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 145 | static int  enslave( struct net_device *, struct net_device * ); | 
 | 146 | static int  emancipate( struct net_device * ); | 
 | 147 | #endif | 
 | 148 |  | 
 | 149 | #ifdef __i386__ | 
 | 150 | #define ASM_CRC 1 | 
 | 151 | #endif | 
 | 152 |  | 
 | 153 | static const char  version[] = | 
 | 154 | 	"Granch SBNI12 driver ver 5.0.1  Jun 22 2001  Denis I.Timofeev.\n"; | 
 | 155 |  | 
 | 156 | static int  skip_pci_probe	__initdata = 0; | 
 | 157 | static int  scandone	__initdata = 0; | 
 | 158 | static int  num		__initdata = 0; | 
 | 159 |  | 
 | 160 | static unsigned char  rxl_tab[]; | 
 | 161 | static u32  crc32tab[]; | 
 | 162 |  | 
 | 163 | /* A list of all installed devices, for removing the driver module. */ | 
 | 164 | static struct net_device  *sbni_cards[ SBNI_MAX_NUM_CARDS ]; | 
 | 165 |  | 
 | 166 | /* Lists of device's parameters */ | 
 | 167 | static u32	io[   SBNI_MAX_NUM_CARDS ] __initdata = | 
 | 168 | 	{ [0 ... SBNI_MAX_NUM_CARDS-1] = -1 }; | 
 | 169 | static u32	irq[  SBNI_MAX_NUM_CARDS ] __initdata; | 
 | 170 | static u32	baud[ SBNI_MAX_NUM_CARDS ] __initdata; | 
 | 171 | static u32	rxl[  SBNI_MAX_NUM_CARDS ] __initdata = | 
 | 172 | 	{ [0 ... SBNI_MAX_NUM_CARDS-1] = -1 }; | 
 | 173 | static u32	mac[  SBNI_MAX_NUM_CARDS ] __initdata; | 
 | 174 |  | 
 | 175 | #ifndef MODULE | 
 | 176 | typedef u32  iarr[]; | 
 | 177 | static iarr __initdata *dest[5] = { &io, &irq, &baud, &rxl, &mac }; | 
 | 178 | #endif | 
 | 179 |  | 
 | 180 | /* A zero-terminated list of I/O addresses to be probed on ISA bus */ | 
 | 181 | static unsigned int  netcard_portlist[ ] __initdata = {  | 
 | 182 | 	0x210, 0x214, 0x220, 0x224, 0x230, 0x234, 0x240, 0x244, 0x250, 0x254, | 
 | 183 | 	0x260, 0x264, 0x270, 0x274, 0x280, 0x284, 0x290, 0x294, 0x2a0, 0x2a4, | 
 | 184 | 	0x2b0, 0x2b4, 0x2c0, 0x2c4, 0x2d0, 0x2d4, 0x2e0, 0x2e4, 0x2f0, 0x2f4, | 
 | 185 | 	0 }; | 
 | 186 |  | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 187 | #define NET_LOCAL_LOCK(dev) (((struct net_local *)netdev_priv(dev))->lock) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 188 |  | 
 | 189 | /* | 
 | 190 |  * Look for SBNI card which addr stored in dev->base_addr, if nonzero. | 
 | 191 |  * Otherwise, look through PCI bus. If none PCI-card was found, scan ISA. | 
 | 192 |  */ | 
 | 193 |  | 
 | 194 | static inline int __init | 
 | 195 | sbni_isa_probe( struct net_device  *dev ) | 
 | 196 | { | 
| Joe Perches | 8e95a20 | 2009-12-03 07:58:21 +0000 | [diff] [blame] | 197 | 	if( dev->base_addr > 0x1ff && | 
 | 198 | 	    request_region( dev->base_addr, SBNI_IO_EXTENT, dev->name ) && | 
 | 199 | 	    sbni_probe1( dev, dev->base_addr, dev->irq ) ) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 200 |  | 
 | 201 | 		return  0; | 
 | 202 | 	else { | 
 | 203 | 		printk( KERN_ERR "sbni: base address 0x%lx is busy, or adapter " | 
 | 204 | 			"is malfunctional!\n", dev->base_addr ); | 
 | 205 | 		return  -ENODEV; | 
 | 206 | 	} | 
 | 207 | } | 
 | 208 |  | 
| Stephen Hemminger | 7dd0b6e | 2009-03-20 19:36:20 +0000 | [diff] [blame] | 209 | static const struct net_device_ops sbni_netdev_ops = { | 
 | 210 | 	.ndo_open		= sbni_open, | 
 | 211 | 	.ndo_stop		= sbni_close, | 
 | 212 | 	.ndo_start_xmit		= sbni_start_xmit, | 
 | 213 | 	.ndo_set_multicast_list	= set_multicast_list, | 
 | 214 | 	.ndo_do_ioctl		= sbni_ioctl, | 
 | 215 | 	.ndo_change_mtu		= eth_change_mtu, | 
 | 216 | 	.ndo_set_mac_address 	= eth_mac_addr, | 
 | 217 | 	.ndo_validate_addr	= eth_validate_addr, | 
 | 218 | }; | 
 | 219 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 220 | static void __init sbni_devsetup(struct net_device *dev) | 
 | 221 | { | 
 | 222 | 	ether_setup( dev ); | 
| Stephen Hemminger | 7dd0b6e | 2009-03-20 19:36:20 +0000 | [diff] [blame] | 223 | 	dev->netdev_ops = &sbni_netdev_ops; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 224 | } | 
 | 225 |  | 
 | 226 | int __init sbni_probe(int unit) | 
 | 227 | { | 
 | 228 | 	struct net_device *dev; | 
 | 229 | 	static unsigned  version_printed __initdata = 0; | 
 | 230 | 	int err; | 
 | 231 |  | 
 | 232 | 	dev = alloc_netdev(sizeof(struct net_local), "sbni", sbni_devsetup); | 
 | 233 | 	if (!dev) | 
 | 234 | 		return -ENOMEM; | 
 | 235 |  | 
| Stephen Hemminger | 7dd0b6e | 2009-03-20 19:36:20 +0000 | [diff] [blame] | 236 | 	dev->netdev_ops = &sbni_netdev_ops; | 
 | 237 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 238 | 	sprintf(dev->name, "sbni%d", unit); | 
 | 239 | 	netdev_boot_setup_check(dev); | 
 | 240 |  | 
 | 241 | 	err = sbni_init(dev); | 
 | 242 | 	if (err) { | 
 | 243 | 		free_netdev(dev); | 
 | 244 | 		return err; | 
 | 245 | 	} | 
 | 246 |  | 
 | 247 | 	err = register_netdev(dev); | 
 | 248 | 	if (err) { | 
 | 249 | 		release_region( dev->base_addr, SBNI_IO_EXTENT ); | 
 | 250 | 		free_netdev(dev); | 
 | 251 | 		return err; | 
 | 252 | 	} | 
 | 253 | 	if( version_printed++ == 0 ) | 
 | 254 | 		printk( KERN_INFO "%s", version ); | 
 | 255 | 	return 0; | 
 | 256 | } | 
 | 257 |  | 
 | 258 | static int __init sbni_init(struct net_device *dev) | 
 | 259 | { | 
 | 260 | 	int  i; | 
 | 261 | 	if( dev->base_addr ) | 
 | 262 | 		return  sbni_isa_probe( dev ); | 
 | 263 | 	/* otherwise we have to perform search our adapter */ | 
 | 264 |  | 
 | 265 | 	if( io[ num ] != -1 ) | 
 | 266 | 		dev->base_addr	= io[ num ], | 
 | 267 | 		dev->irq	= irq[ num ]; | 
 | 268 | 	else if( scandone  ||  io[ 0 ] != -1 ) | 
 | 269 | 		return  -ENODEV; | 
 | 270 |  | 
 | 271 | 	/* if io[ num ] contains non-zero address, then that is on ISA bus */ | 
 | 272 | 	if( dev->base_addr ) | 
 | 273 | 		return  sbni_isa_probe( dev ); | 
 | 274 |  | 
 | 275 | 	/* ...otherwise - scan PCI first */ | 
 | 276 | 	if( !skip_pci_probe  &&  !sbni_pci_probe( dev ) ) | 
 | 277 | 		return  0; | 
 | 278 |  | 
 | 279 | 	if( io[ num ] == -1 ) { | 
 | 280 | 		/* Auto-scan will be stopped when first ISA card were found */ | 
 | 281 | 		scandone = 1; | 
 | 282 | 		if( num > 0 ) | 
 | 283 | 			return  -ENODEV; | 
 | 284 | 	} | 
 | 285 |  | 
 | 286 | 	for( i = 0;  netcard_portlist[ i ];  ++i ) { | 
 | 287 | 		int  ioaddr = netcard_portlist[ i ]; | 
| Joe Perches | 8e95a20 | 2009-12-03 07:58:21 +0000 | [diff] [blame] | 288 | 		if( request_region( ioaddr, SBNI_IO_EXTENT, dev->name ) && | 
 | 289 | 		    sbni_probe1( dev, ioaddr, 0 )) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 290 | 			return 0; | 
 | 291 | 	} | 
 | 292 |  | 
 | 293 | 	return  -ENODEV; | 
 | 294 | } | 
 | 295 |  | 
 | 296 |  | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 297 | static int __init | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 298 | sbni_pci_probe( struct net_device  *dev ) | 
 | 299 | { | 
 | 300 | 	struct pci_dev  *pdev = NULL; | 
 | 301 |  | 
 | 302 | 	while( (pdev = pci_get_class( PCI_CLASS_NETWORK_OTHER << 8, pdev )) | 
 | 303 | 	       != NULL ) { | 
 | 304 | 		int  pci_irq_line; | 
 | 305 | 		unsigned long  pci_ioaddr; | 
 | 306 | 		u16  subsys; | 
 | 307 |  | 
| Joe Perches | 8e95a20 | 2009-12-03 07:58:21 +0000 | [diff] [blame] | 308 | 		if( pdev->vendor != SBNI_PCI_VENDOR && | 
 | 309 | 		    pdev->device != SBNI_PCI_DEVICE ) | 
 | 310 | 			continue; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 311 |  | 
 | 312 | 		pci_ioaddr = pci_resource_start( pdev, 0 ); | 
 | 313 | 		pci_irq_line = pdev->irq; | 
 | 314 |  | 
 | 315 | 		/* Avoid already found cards from previous calls */ | 
 | 316 | 		if( !request_region( pci_ioaddr, SBNI_IO_EXTENT, dev->name ) ) { | 
 | 317 | 			pci_read_config_word( pdev, PCI_SUBSYSTEM_ID, &subsys ); | 
 | 318 |  | 
 | 319 | 			if (subsys != 2) | 
 | 320 | 				continue; | 
 | 321 |  | 
 | 322 | 			/* Dual adapter is present */ | 
 | 323 | 			if (!request_region(pci_ioaddr += 4, SBNI_IO_EXTENT, | 
 | 324 | 							dev->name ) ) | 
 | 325 | 				continue; | 
 | 326 | 		} | 
 | 327 |  | 
| Yinghai Lu | 60e4ad7 | 2008-08-19 20:49:50 -0700 | [diff] [blame] | 328 | 		if (pci_irq_line <= 0 || pci_irq_line >= nr_irqs) | 
| Joe Perches | ad361c9 | 2009-07-06 13:05:40 -0700 | [diff] [blame] | 329 | 			printk( KERN_WARNING | 
 | 330 | 	"  WARNING: The PCI BIOS assigned this PCI card to IRQ %d, which is unlikely to work!.\n" | 
 | 331 | 	" You should use the PCI BIOS setup to assign a valid IRQ line.\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 332 | 				pci_irq_line ); | 
 | 333 |  | 
 | 334 | 		/* avoiding re-enable dual adapters */ | 
 | 335 | 		if( (pci_ioaddr & 7) == 0  &&  pci_enable_device( pdev ) ) { | 
 | 336 | 			release_region( pci_ioaddr, SBNI_IO_EXTENT ); | 
 | 337 | 			pci_dev_put( pdev ); | 
 | 338 | 			return  -EIO; | 
 | 339 | 		} | 
 | 340 | 		if( sbni_probe1( dev, pci_ioaddr, pci_irq_line ) ) { | 
 | 341 | 			SET_NETDEV_DEV(dev, &pdev->dev); | 
 | 342 | 			/* not the best thing to do, but this is all messed up  | 
 | 343 | 			   for hotplug systems anyway... */ | 
 | 344 | 			pci_dev_put( pdev ); | 
 | 345 | 			return  0; | 
 | 346 | 		} | 
 | 347 | 	} | 
 | 348 | 	return  -ENODEV; | 
 | 349 | } | 
 | 350 |  | 
 | 351 |  | 
 | 352 | static struct net_device * __init | 
 | 353 | sbni_probe1( struct net_device  *dev,  unsigned long  ioaddr,  int  irq ) | 
 | 354 | { | 
 | 355 | 	struct net_local  *nl; | 
 | 356 |  | 
 | 357 | 	if( sbni_card_probe( ioaddr ) ) { | 
 | 358 | 		release_region( ioaddr, SBNI_IO_EXTENT ); | 
 | 359 | 		return NULL; | 
 | 360 | 	} | 
 | 361 |  | 
 | 362 | 	outb( 0, ioaddr + CSR0 ); | 
 | 363 |  | 
 | 364 | 	if( irq < 2 ) { | 
 | 365 | 		unsigned long irq_mask; | 
 | 366 |  | 
 | 367 | 		irq_mask = probe_irq_on(); | 
 | 368 | 		outb( EN_INT | TR_REQ, ioaddr + CSR0 ); | 
 | 369 | 		outb( PR_RES, ioaddr + CSR1 ); | 
 | 370 | 		mdelay(50); | 
 | 371 | 		irq = probe_irq_off(irq_mask); | 
 | 372 | 		outb( 0, ioaddr + CSR0 ); | 
 | 373 |  | 
 | 374 | 		if( !irq ) { | 
 | 375 | 			printk( KERN_ERR "%s: can't detect device irq!\n", | 
 | 376 | 				dev->name ); | 
 | 377 | 			release_region( ioaddr, SBNI_IO_EXTENT ); | 
 | 378 | 			return NULL; | 
 | 379 | 		} | 
 | 380 | 	} else if( irq == 2 ) | 
 | 381 | 		irq = 9; | 
 | 382 |  | 
 | 383 | 	dev->irq = irq; | 
 | 384 | 	dev->base_addr = ioaddr; | 
 | 385 |  | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 386 | 	/* Fill in sbni-specific dev fields. */ | 
 | 387 | 	nl = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 388 | 	if( !nl ) { | 
 | 389 | 		printk( KERN_ERR "%s: unable to get memory!\n", dev->name ); | 
 | 390 | 		release_region( ioaddr, SBNI_IO_EXTENT ); | 
 | 391 | 		return NULL; | 
 | 392 | 	} | 
 | 393 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 394 | 	memset( nl, 0, sizeof(struct net_local) ); | 
 | 395 | 	spin_lock_init( &nl->lock ); | 
 | 396 |  | 
 | 397 | 	/* store MAC address (generate if that isn't known) */ | 
| Al Viro | 9045840 | 2007-12-22 17:52:52 +0000 | [diff] [blame] | 398 | 	*(__be16 *)dev->dev_addr = htons( 0x00ff ); | 
 | 399 | 	*(__be32 *)(dev->dev_addr + 2) = htonl( 0x01000000 | | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 400 | 		((mac[num] ? | 
 | 401 | 		mac[num] : | 
 | 402 | 		(u32)((long)netdev_priv(dev))) & 0x00ffffff)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 403 |  | 
 | 404 | 	/* store link settings (speed, receive level ) */ | 
 | 405 | 	nl->maxframe  = DEFAULT_FRAME_LEN; | 
 | 406 | 	nl->csr1.rate = baud[ num ]; | 
 | 407 |  | 
 | 408 | 	if( (nl->cur_rxl_index = rxl[ num ]) == -1 ) | 
 | 409 | 		/* autotune rxl */ | 
 | 410 | 		nl->cur_rxl_index = DEF_RXL, | 
 | 411 | 		nl->delta_rxl = DEF_RXL_DELTA; | 
 | 412 | 	else | 
 | 413 | 		nl->delta_rxl = 0; | 
 | 414 | 	nl->csr1.rxl  = rxl_tab[ nl->cur_rxl_index ]; | 
 | 415 | 	if( inb( ioaddr + CSR0 ) & 0x01 ) | 
 | 416 | 		nl->state |= FL_SLOW_MODE; | 
 | 417 |  | 
 | 418 | 	printk( KERN_NOTICE "%s: ioaddr %#lx, irq %d, " | 
 | 419 | 		"MAC: 00:ff:01:%02x:%02x:%02x\n",  | 
 | 420 | 		dev->name, dev->base_addr, dev->irq, | 
 | 421 | 		((u8 *) dev->dev_addr) [3], | 
 | 422 | 		((u8 *) dev->dev_addr) [4], | 
 | 423 | 		((u8 *) dev->dev_addr) [5] ); | 
 | 424 |  | 
 | 425 | 	printk( KERN_NOTICE "%s: speed %d, receive level ", dev->name, | 
 | 426 | 		( (nl->state & FL_SLOW_MODE)  ?  500000 : 2000000) | 
 | 427 | 		/ (1 << nl->csr1.rate) ); | 
 | 428 |  | 
 | 429 | 	if( nl->delta_rxl == 0 ) | 
 | 430 | 		printk( "0x%x (fixed)\n", nl->cur_rxl_index );  | 
 | 431 | 	else | 
 | 432 | 		printk( "(auto)\n"); | 
 | 433 |  | 
 | 434 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 435 | 	nl->master = dev; | 
 | 436 | 	nl->link   = NULL; | 
 | 437 | #endif | 
 | 438 |     | 
 | 439 | 	sbni_cards[ num++ ] = dev; | 
 | 440 | 	return  dev; | 
 | 441 | } | 
 | 442 |  | 
 | 443 | /* -------------------------------------------------------------------------- */ | 
 | 444 |  | 
 | 445 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 446 |  | 
| Stephen Hemminger | d71a674 | 2009-08-31 19:50:47 +0000 | [diff] [blame] | 447 | static netdev_tx_t | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 448 | sbni_start_xmit( struct sk_buff  *skb,  struct net_device  *dev ) | 
 | 449 | { | 
 | 450 | 	struct net_device  *p; | 
 | 451 |  | 
 | 452 | 	netif_stop_queue( dev ); | 
 | 453 |  | 
 | 454 | 	/* Looking for idle device in the list */ | 
 | 455 | 	for( p = dev;  p; ) { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 456 | 		struct net_local  *nl = netdev_priv(p); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 457 | 		spin_lock( &nl->lock ); | 
 | 458 | 		if( nl->tx_buf_p  ||  (nl->state & FL_LINE_DOWN) ) { | 
 | 459 | 			p = nl->link; | 
 | 460 | 			spin_unlock( &nl->lock ); | 
 | 461 | 		} else { | 
 | 462 | 			/* Idle dev is found */ | 
 | 463 | 			prepare_to_send( skb, p ); | 
 | 464 | 			spin_unlock( &nl->lock ); | 
 | 465 | 			netif_start_queue( dev ); | 
| Patrick McHardy | 6ed1065 | 2009-06-23 06:03:08 +0000 | [diff] [blame] | 466 | 			return NETDEV_TX_OK; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 467 | 		} | 
 | 468 | 	} | 
 | 469 |  | 
| Patrick McHardy | 5b54814 | 2009-06-12 06:22:29 +0000 | [diff] [blame] | 470 | 	return NETDEV_TX_BUSY; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 471 | } | 
 | 472 |  | 
 | 473 | #else	/* CONFIG_SBNI_MULTILINE */ | 
 | 474 |  | 
| Stephen Hemminger | d71a674 | 2009-08-31 19:50:47 +0000 | [diff] [blame] | 475 | static netdev_tx_t | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 476 | sbni_start_xmit( struct sk_buff  *skb,  struct net_device  *dev ) | 
 | 477 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 478 | 	struct net_local  *nl  = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 479 |  | 
 | 480 | 	netif_stop_queue( dev ); | 
 | 481 | 	spin_lock( &nl->lock ); | 
 | 482 |  | 
 | 483 | 	prepare_to_send( skb, dev ); | 
 | 484 |  | 
 | 485 | 	spin_unlock( &nl->lock ); | 
| Patrick McHardy | 6ed1065 | 2009-06-23 06:03:08 +0000 | [diff] [blame] | 486 | 	return NETDEV_TX_OK; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 487 | } | 
 | 488 |  | 
 | 489 | #endif	/* CONFIG_SBNI_MULTILINE */ | 
 | 490 |  | 
 | 491 | /* -------------------------------------------------------------------------- */ | 
 | 492 |  | 
 | 493 | /* interrupt handler */ | 
 | 494 |  | 
 | 495 | /* | 
 | 496 |  * 	SBNI12D-10, -11/ISA boards within "common interrupt" mode could not | 
 | 497 |  * be looked as two independent single-channel devices. Every channel seems | 
 | 498 |  * as Ethernet interface but interrupt handler must be common. Really, first | 
 | 499 |  * channel ("master") driver only registers the handler. In its struct net_local | 
 | 500 |  * it has got pointer to "slave" channel's struct net_local and handles that's | 
 | 501 |  * interrupts too. | 
 | 502 |  *	dev of successfully attached ISA SBNI boards is linked to list. | 
 | 503 |  * While next board driver is initialized, it scans this list. If one | 
 | 504 |  * has found dev with same irq and ioaddr different by 4 then it assumes | 
 | 505 |  * this board to be "master". | 
 | 506 |  */  | 
 | 507 |  | 
 | 508 | static irqreturn_t | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 509 | sbni_interrupt( int  irq,  void  *dev_id ) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 510 | { | 
| Jeff Garzik | 06efcad | 2007-10-19 03:10:11 -0400 | [diff] [blame] | 511 | 	struct net_device	  *dev = dev_id; | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 512 | 	struct net_local  *nl  = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 513 | 	int	repeat; | 
 | 514 |  | 
 | 515 | 	spin_lock( &nl->lock ); | 
 | 516 | 	if( nl->second ) | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 517 | 		spin_lock(&NET_LOCAL_LOCK(nl->second)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 518 |  | 
 | 519 | 	do { | 
 | 520 | 		repeat = 0; | 
 | 521 | 		if( inb( dev->base_addr + CSR0 ) & (RC_RDY | TR_RDY) ) | 
 | 522 | 			handle_channel( dev ), | 
 | 523 | 			repeat = 1; | 
 | 524 | 		if( nl->second  && 	/* second channel present */ | 
 | 525 | 		    (inb( nl->second->base_addr+CSR0 ) & (RC_RDY | TR_RDY)) ) | 
 | 526 | 			handle_channel( nl->second ), | 
 | 527 | 			repeat = 1; | 
 | 528 | 	} while( repeat ); | 
 | 529 |  | 
 | 530 | 	if( nl->second ) | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 531 | 		spin_unlock(&NET_LOCAL_LOCK(nl->second)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 532 | 	spin_unlock( &nl->lock ); | 
 | 533 | 	return IRQ_HANDLED; | 
 | 534 | } | 
 | 535 |  | 
 | 536 |  | 
 | 537 | static void | 
 | 538 | handle_channel( struct net_device  *dev ) | 
 | 539 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 540 | 	struct net_local	*nl    = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 541 | 	unsigned long		ioaddr = dev->base_addr; | 
 | 542 |  | 
 | 543 | 	int  req_ans; | 
 | 544 | 	unsigned char  csr0; | 
 | 545 |  | 
 | 546 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 547 | 	/* Lock the master device because we going to change its local data */ | 
 | 548 | 	if( nl->state & FL_SLAVE ) | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 549 | 		spin_lock(&NET_LOCAL_LOCK(nl->master)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 550 | #endif | 
 | 551 |  | 
 | 552 | 	outb( (inb( ioaddr + CSR0 ) & ~EN_INT) | TR_REQ, ioaddr + CSR0 ); | 
 | 553 |  | 
 | 554 | 	nl->timer_ticks = CHANGE_LEVEL_START_TICKS; | 
 | 555 | 	for(;;) { | 
 | 556 | 		csr0 = inb( ioaddr + CSR0 ); | 
 | 557 | 		if( ( csr0 & (RC_RDY | TR_RDY) ) == 0 ) | 
 | 558 | 			break; | 
 | 559 |  | 
 | 560 | 		req_ans = !(nl->state & FL_PREV_OK); | 
 | 561 |  | 
 | 562 | 		if( csr0 & RC_RDY ) | 
 | 563 | 			req_ans = recv_frame( dev ); | 
 | 564 |  | 
 | 565 | 		/* | 
 | 566 | 		 * TR_RDY always equals 1 here because we have owned the marker, | 
 | 567 | 		 * and we set TR_REQ when disabled interrupts | 
 | 568 | 		 */ | 
 | 569 | 		csr0 = inb( ioaddr + CSR0 ); | 
 | 570 | 		if( !(csr0 & TR_RDY)  ||  (csr0 & RC_RDY) ) | 
 | 571 | 			printk( KERN_ERR "%s: internal error!\n", dev->name ); | 
 | 572 |  | 
 | 573 | 		/* if state & FL_NEED_RESEND != 0 then tx_frameno != 0 */ | 
 | 574 | 		if( req_ans  ||  nl->tx_frameno != 0 ) | 
 | 575 | 			send_frame( dev ); | 
 | 576 | 		else | 
 | 577 | 			/* send marker without any data */ | 
 | 578 | 			outb( inb( ioaddr + CSR0 ) & ~TR_REQ, ioaddr + CSR0 ); | 
 | 579 | 	} | 
 | 580 |  | 
 | 581 | 	outb( inb( ioaddr + CSR0 ) | EN_INT, ioaddr + CSR0 ); | 
 | 582 |  | 
 | 583 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 584 | 	if( nl->state & FL_SLAVE ) | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 585 | 		spin_unlock(&NET_LOCAL_LOCK(nl->master)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 586 | #endif | 
 | 587 | } | 
 | 588 |  | 
 | 589 |  | 
 | 590 | /* | 
 | 591 |  * Routine returns 1 if it need to acknoweledge received frame. | 
 | 592 |  * Empty frame received without errors won't be acknoweledged. | 
 | 593 |  */ | 
 | 594 |  | 
 | 595 | static int | 
 | 596 | recv_frame( struct net_device  *dev ) | 
 | 597 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 598 | 	struct net_local  *nl   = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 599 | 	unsigned long  ioaddr	= dev->base_addr; | 
 | 600 |  | 
 | 601 | 	u32  crc = CRC32_INITIAL; | 
 | 602 |  | 
| Jeff Garzik | e5fb4f4 | 2007-07-17 01:56:32 -0400 | [diff] [blame] | 603 | 	unsigned  framelen = 0, frameno, ack; | 
 | 604 | 	unsigned  is_first, frame_ok = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 605 |  | 
 | 606 | 	if( check_fhdr( ioaddr, &framelen, &frameno, &ack, &is_first, &crc ) ) { | 
 | 607 | 		frame_ok = framelen > 4 | 
 | 608 | 			?  upload_data( dev, framelen, frameno, is_first, crc ) | 
 | 609 | 			:  skip_tail( ioaddr, framelen, crc ); | 
 | 610 | 		if( frame_ok ) | 
 | 611 | 			interpret_ack( dev, ack ); | 
| Jeff Garzik | e5fb4f4 | 2007-07-17 01:56:32 -0400 | [diff] [blame] | 612 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 613 |  | 
 | 614 | 	outb( inb( ioaddr + CSR0 ) ^ CT_ZER, ioaddr + CSR0 ); | 
 | 615 | 	if( frame_ok ) { | 
 | 616 | 		nl->state |= FL_PREV_OK; | 
 | 617 | 		if( framelen > 4 ) | 
 | 618 | 			nl->in_stats.all_rx_number++; | 
 | 619 | 	} else | 
 | 620 | 		nl->state &= ~FL_PREV_OK, | 
 | 621 | 		change_level( dev ), | 
 | 622 | 		nl->in_stats.all_rx_number++, | 
 | 623 | 		nl->in_stats.bad_rx_number++; | 
 | 624 |  | 
 | 625 | 	return  !frame_ok  ||  framelen > 4; | 
 | 626 | } | 
 | 627 |  | 
 | 628 |  | 
 | 629 | static void | 
 | 630 | send_frame( struct net_device  *dev ) | 
 | 631 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 632 | 	struct net_local  *nl    = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 633 |  | 
 | 634 | 	u32  crc = CRC32_INITIAL; | 
 | 635 |  | 
 | 636 | 	if( nl->state & FL_NEED_RESEND ) { | 
 | 637 |  | 
 | 638 | 		/* if frame was sended but not ACK'ed - resend it */ | 
 | 639 | 		if( nl->trans_errors ) { | 
 | 640 | 			--nl->trans_errors; | 
 | 641 | 			if( nl->framelen != 0 ) | 
 | 642 | 				nl->in_stats.resend_tx_number++; | 
 | 643 | 		} else { | 
 | 644 | 			/* cannot xmit with many attempts */ | 
 | 645 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 646 | 			if( (nl->state & FL_SLAVE)  ||  nl->link ) | 
 | 647 | #endif | 
 | 648 | 			nl->state |= FL_LINE_DOWN; | 
 | 649 | 			drop_xmit_queue( dev ); | 
 | 650 | 			goto  do_send; | 
 | 651 | 		} | 
 | 652 | 	} else | 
 | 653 | 		nl->trans_errors = TR_ERROR_COUNT; | 
 | 654 |  | 
 | 655 | 	send_frame_header( dev, &crc ); | 
 | 656 | 	nl->state |= FL_NEED_RESEND; | 
 | 657 | 	/* | 
 | 658 | 	 * FL_NEED_RESEND will be cleared after ACK, but if empty | 
 | 659 | 	 * frame sended then in prepare_to_send next frame | 
 | 660 | 	 */ | 
 | 661 |  | 
 | 662 |  | 
 | 663 | 	if( nl->framelen ) { | 
 | 664 | 		download_data( dev, &crc ); | 
 | 665 | 		nl->in_stats.all_tx_number++; | 
 | 666 | 		nl->state |= FL_WAIT_ACK; | 
 | 667 | 	} | 
 | 668 |  | 
 | 669 | 	outsb( dev->base_addr + DAT, (u8 *)&crc, sizeof crc ); | 
 | 670 |  | 
 | 671 | do_send: | 
 | 672 | 	outb( inb( dev->base_addr + CSR0 ) & ~TR_REQ, dev->base_addr + CSR0 ); | 
 | 673 |  | 
 | 674 | 	if( nl->tx_frameno ) | 
 | 675 | 		/* next frame exists - we request card to send it */ | 
 | 676 | 		outb( inb( dev->base_addr + CSR0 ) | TR_REQ, | 
 | 677 | 		      dev->base_addr + CSR0 ); | 
 | 678 | } | 
 | 679 |  | 
 | 680 |  | 
 | 681 | /* | 
 | 682 |  * Write the frame data into adapter's buffer memory, and calculate CRC. | 
 | 683 |  * Do padding if necessary. | 
 | 684 |  */ | 
 | 685 |  | 
 | 686 | static void | 
 | 687 | download_data( struct net_device  *dev,  u32  *crc_p ) | 
 | 688 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 689 | 	struct net_local  *nl    = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 690 | 	struct sk_buff    *skb	 = nl->tx_buf_p; | 
 | 691 |  | 
 | 692 | 	unsigned  len = min_t(unsigned int, skb->len - nl->outpos, nl->framelen); | 
 | 693 |  | 
 | 694 | 	outsb( dev->base_addr + DAT, skb->data + nl->outpos, len ); | 
 | 695 | 	*crc_p = calc_crc32( *crc_p, skb->data + nl->outpos, len ); | 
 | 696 |  | 
 | 697 | 	/* if packet too short we should write some more bytes to pad */ | 
 | 698 | 	for( len = nl->framelen - len;  len--; ) | 
 | 699 | 		outb( 0, dev->base_addr + DAT ), | 
 | 700 | 		*crc_p = CRC32( 0, *crc_p ); | 
 | 701 | } | 
 | 702 |  | 
 | 703 |  | 
 | 704 | static int | 
 | 705 | upload_data( struct net_device  *dev,  unsigned  framelen,  unsigned  frameno, | 
 | 706 | 	     unsigned  is_first,  u32  crc ) | 
 | 707 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 708 | 	struct net_local  *nl = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 709 |  | 
 | 710 | 	int  frame_ok; | 
 | 711 |  | 
 | 712 | 	if( is_first ) | 
 | 713 | 		nl->wait_frameno = frameno, | 
 | 714 | 		nl->inppos = 0; | 
 | 715 |  | 
 | 716 | 	if( nl->wait_frameno == frameno ) { | 
 | 717 |  | 
 | 718 | 		if( nl->inppos + framelen  <=  ETHER_MAX_LEN ) | 
 | 719 | 			frame_ok = append_frame_to_pkt( dev, framelen, crc ); | 
 | 720 |  | 
 | 721 | 		/* | 
 | 722 | 		 * if CRC is right but framelen incorrect then transmitter | 
 | 723 | 		 * error was occurred... drop entire packet | 
 | 724 | 		 */ | 
 | 725 | 		else if( (frame_ok = skip_tail( dev->base_addr, framelen, crc )) | 
 | 726 | 			 != 0 ) | 
 | 727 | 			nl->wait_frameno = 0, | 
 | 728 | 			nl->inppos = 0, | 
 | 729 | #ifdef CONFIG_SBNI_MULTILINE | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 730 | 			nl->master->stats.rx_errors++, | 
 | 731 | 			nl->master->stats.rx_missed_errors++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 732 | #else | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 733 | 		        dev->stats.rx_errors++, | 
 | 734 | 			dev->stats.rx_missed_errors++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 735 | #endif | 
 | 736 | 			/* now skip all frames until is_first != 0 */ | 
 | 737 | 	} else | 
 | 738 | 		frame_ok = skip_tail( dev->base_addr, framelen, crc ); | 
 | 739 |  | 
 | 740 | 	if( is_first  &&  !frame_ok ) | 
 | 741 | 		/* | 
 | 742 | 		 * Frame has been broken, but we had already stored | 
 | 743 | 		 * is_first... Drop entire packet. | 
 | 744 | 		 */ | 
 | 745 | 		nl->wait_frameno = 0, | 
 | 746 | #ifdef CONFIG_SBNI_MULTILINE | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 747 | 		nl->master->stats.rx_errors++, | 
 | 748 | 		nl->master->stats.rx_crc_errors++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 749 | #else | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 750 | 		dev->stats.rx_errors++, | 
 | 751 | 		dev->stats.rx_crc_errors++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 752 | #endif | 
 | 753 |  | 
 | 754 | 	return  frame_ok; | 
 | 755 | } | 
 | 756 |  | 
 | 757 |  | 
| Harvey Harrison | dfec722 | 2008-03-05 18:36:28 -0800 | [diff] [blame] | 758 | static inline void | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 759 | send_complete( struct net_device *dev ) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 760 | { | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 761 | 	struct net_local  *nl = netdev_priv(dev); | 
 | 762 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 763 | #ifdef CONFIG_SBNI_MULTILINE | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 764 | 	nl->master->stats.tx_packets++; | 
 | 765 | 	nl->master->stats.tx_bytes += nl->tx_buf_p->len; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 766 | #else | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 767 | 	dev->stats.tx_packets++; | 
 | 768 | 	dev->stats.tx_bytes += nl->tx_buf_p->len; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 769 | #endif | 
 | 770 | 	dev_kfree_skb_irq( nl->tx_buf_p ); | 
 | 771 |  | 
 | 772 | 	nl->tx_buf_p = NULL; | 
 | 773 |  | 
 | 774 | 	nl->outpos = 0; | 
 | 775 | 	nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); | 
 | 776 | 	nl->framelen   = 0; | 
 | 777 | } | 
 | 778 |  | 
 | 779 |  | 
 | 780 | static void | 
 | 781 | interpret_ack( struct net_device  *dev,  unsigned  ack ) | 
 | 782 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 783 | 	struct net_local  *nl = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 784 |  | 
 | 785 | 	if( ack == FRAME_SENT_OK ) { | 
 | 786 | 		nl->state &= ~FL_NEED_RESEND; | 
 | 787 |  | 
 | 788 | 		if( nl->state & FL_WAIT_ACK ) { | 
 | 789 | 			nl->outpos += nl->framelen; | 
 | 790 |  | 
 | 791 | 			if( --nl->tx_frameno ) | 
 | 792 | 				nl->framelen = min_t(unsigned int, | 
 | 793 | 						   nl->maxframe, | 
 | 794 | 						   nl->tx_buf_p->len - nl->outpos); | 
 | 795 | 			else | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 796 | 				send_complete( dev ), | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 797 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 798 | 				netif_wake_queue( nl->master ); | 
 | 799 | #else | 
 | 800 | 				netif_wake_queue( dev ); | 
 | 801 | #endif | 
 | 802 | 		} | 
 | 803 | 	} | 
 | 804 |  | 
 | 805 | 	nl->state &= ~FL_WAIT_ACK; | 
 | 806 | } | 
 | 807 |  | 
 | 808 |  | 
 | 809 | /* | 
 | 810 |  * Glue received frame with previous fragments of packet. | 
 | 811 |  * Indicate packet when last frame would be accepted. | 
 | 812 |  */ | 
 | 813 |  | 
 | 814 | static int | 
 | 815 | append_frame_to_pkt( struct net_device  *dev,  unsigned  framelen,  u32  crc ) | 
 | 816 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 817 | 	struct net_local  *nl = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 818 |  | 
 | 819 | 	u8  *p; | 
 | 820 |  | 
 | 821 | 	if( nl->inppos + framelen  >  ETHER_MAX_LEN ) | 
 | 822 | 		return  0; | 
 | 823 |  | 
 | 824 | 	if( !nl->rx_buf_p  &&  !(nl->rx_buf_p = get_rx_buf( dev )) ) | 
 | 825 | 		return  0; | 
 | 826 |  | 
 | 827 | 	p = nl->rx_buf_p->data + nl->inppos; | 
 | 828 | 	insb( dev->base_addr + DAT, p, framelen ); | 
 | 829 | 	if( calc_crc32( crc, p, framelen ) != CRC32_REMAINDER ) | 
 | 830 | 		return  0; | 
 | 831 |  | 
 | 832 | 	nl->inppos += framelen - 4; | 
 | 833 | 	if( --nl->wait_frameno == 0 )		/* last frame received */ | 
 | 834 | 		indicate_pkt( dev ); | 
 | 835 |  | 
 | 836 | 	return  1; | 
 | 837 | } | 
 | 838 |  | 
 | 839 |  | 
 | 840 | /* | 
 | 841 |  * Prepare to start output on adapter. | 
 | 842 |  * Transmitter will be actually activated when marker is accepted. | 
 | 843 |  */ | 
 | 844 |  | 
 | 845 | static void | 
 | 846 | prepare_to_send( struct sk_buff  *skb,  struct net_device  *dev ) | 
 | 847 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 848 | 	struct net_local  *nl = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 849 |  | 
 | 850 | 	unsigned int  len; | 
 | 851 |  | 
 | 852 | 	/* nl->tx_buf_p == NULL here! */ | 
 | 853 | 	if( nl->tx_buf_p ) | 
 | 854 | 		printk( KERN_ERR "%s: memory leak!\n", dev->name ); | 
 | 855 |  | 
 | 856 | 	nl->outpos = 0; | 
 | 857 | 	nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); | 
 | 858 |  | 
 | 859 | 	len = skb->len; | 
 | 860 | 	if( len < SBNI_MIN_LEN ) | 
 | 861 | 		len = SBNI_MIN_LEN; | 
 | 862 |  | 
 | 863 | 	nl->tx_buf_p	= skb; | 
| Julia Lawall | bb55b32 | 2008-09-22 19:23:48 -0700 | [diff] [blame] | 864 | 	nl->tx_frameno	= DIV_ROUND_UP(len, nl->maxframe); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 865 | 	nl->framelen	= len < nl->maxframe  ?  len  :  nl->maxframe; | 
 | 866 |  | 
 | 867 | 	outb( inb( dev->base_addr + CSR0 ) | TR_REQ,  dev->base_addr + CSR0 ); | 
 | 868 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 869 | 	nl->master->trans_start = jiffies; | 
 | 870 | #else | 
 | 871 | 	dev->trans_start = jiffies; | 
 | 872 | #endif | 
 | 873 | } | 
 | 874 |  | 
 | 875 |  | 
 | 876 | static void | 
 | 877 | drop_xmit_queue( struct net_device  *dev ) | 
 | 878 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 879 | 	struct net_local  *nl = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 880 |  | 
 | 881 | 	if( nl->tx_buf_p ) | 
 | 882 | 		dev_kfree_skb_any( nl->tx_buf_p ), | 
 | 883 | 		nl->tx_buf_p = NULL, | 
 | 884 | #ifdef CONFIG_SBNI_MULTILINE | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 885 | 		nl->master->stats.tx_errors++, | 
 | 886 | 		nl->master->stats.tx_carrier_errors++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 887 | #else | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 888 | 		dev->stats.tx_errors++, | 
 | 889 | 		dev->stats.tx_carrier_errors++; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 890 | #endif | 
 | 891 |  | 
 | 892 | 	nl->tx_frameno	= 0; | 
 | 893 | 	nl->framelen	= 0; | 
 | 894 | 	nl->outpos	= 0; | 
 | 895 | 	nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); | 
 | 896 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 897 | 	netif_start_queue( nl->master ); | 
 | 898 | 	nl->master->trans_start = jiffies; | 
 | 899 | #else | 
 | 900 | 	netif_start_queue( dev ); | 
 | 901 | 	dev->trans_start = jiffies; | 
 | 902 | #endif | 
 | 903 | } | 
 | 904 |  | 
 | 905 |  | 
 | 906 | static void | 
 | 907 | send_frame_header( struct net_device  *dev,  u32  *crc_p ) | 
 | 908 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 909 | 	struct net_local  *nl  = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 910 |  | 
 | 911 | 	u32  crc = *crc_p; | 
 | 912 | 	u32  len_field = nl->framelen + 6;	/* CRC + frameno + reserved */ | 
 | 913 | 	u8   value; | 
 | 914 |  | 
 | 915 | 	if( nl->state & FL_NEED_RESEND ) | 
 | 916 | 		len_field |= FRAME_RETRY;	/* non-first attempt... */ | 
 | 917 |  | 
 | 918 | 	if( nl->outpos == 0 ) | 
 | 919 | 		len_field |= FRAME_FIRST; | 
 | 920 |  | 
 | 921 | 	len_field |= (nl->state & FL_PREV_OK) ? FRAME_SENT_OK : FRAME_SENT_BAD; | 
 | 922 | 	outb( SBNI_SIG, dev->base_addr + DAT ); | 
 | 923 |  | 
 | 924 | 	value = (u8) len_field; | 
 | 925 | 	outb( value, dev->base_addr + DAT ); | 
 | 926 | 	crc = CRC32( value, crc ); | 
 | 927 | 	value = (u8) (len_field >> 8); | 
 | 928 | 	outb( value, dev->base_addr + DAT ); | 
 | 929 | 	crc = CRC32( value, crc ); | 
 | 930 |  | 
 | 931 | 	outb( nl->tx_frameno, dev->base_addr + DAT ); | 
 | 932 | 	crc = CRC32( nl->tx_frameno, crc ); | 
 | 933 | 	outb( 0, dev->base_addr + DAT ); | 
 | 934 | 	crc = CRC32( 0, crc ); | 
 | 935 | 	*crc_p = crc; | 
 | 936 | } | 
 | 937 |  | 
 | 938 |  | 
 | 939 | /* | 
 | 940 |  * if frame tail not needed (incorrect number or received twice), | 
 | 941 |  * it won't store, but CRC will be calculated | 
 | 942 |  */ | 
 | 943 |  | 
 | 944 | static int | 
 | 945 | skip_tail( unsigned int  ioaddr,  unsigned int  tail_len,  u32 crc ) | 
 | 946 | { | 
 | 947 | 	while( tail_len-- ) | 
 | 948 | 		crc = CRC32( inb( ioaddr + DAT ), crc ); | 
 | 949 |  | 
 | 950 | 	return  crc == CRC32_REMAINDER; | 
 | 951 | } | 
 | 952 |  | 
 | 953 |  | 
 | 954 | /* | 
 | 955 |  * Preliminary checks if frame header is correct, calculates its CRC | 
 | 956 |  * and split it to simple fields | 
 | 957 |  */ | 
 | 958 |  | 
 | 959 | static int | 
 | 960 | check_fhdr( u32  ioaddr,  u32  *framelen,  u32  *frameno,  u32  *ack, | 
 | 961 | 	    u32  *is_first,  u32  *crc_p ) | 
 | 962 | { | 
 | 963 | 	u32  crc = *crc_p; | 
 | 964 | 	u8   value; | 
 | 965 |  | 
 | 966 | 	if( inb( ioaddr + DAT ) != SBNI_SIG ) | 
 | 967 | 		return  0; | 
 | 968 |  | 
 | 969 | 	value = inb( ioaddr + DAT ); | 
 | 970 | 	*framelen = (u32)value; | 
 | 971 | 	crc = CRC32( value, crc ); | 
 | 972 | 	value = inb( ioaddr + DAT ); | 
 | 973 | 	*framelen |= ((u32)value) << 8; | 
 | 974 | 	crc = CRC32( value, crc ); | 
 | 975 |  | 
 | 976 | 	*ack = *framelen & FRAME_ACK_MASK; | 
 | 977 | 	*is_first = (*framelen & FRAME_FIRST) != 0; | 
 | 978 |  | 
| Joe Perches | 8e95a20 | 2009-12-03 07:58:21 +0000 | [diff] [blame] | 979 | 	if( (*framelen &= FRAME_LEN_MASK) < 6 || | 
 | 980 | 	    *framelen > SBNI_MAX_FRAME - 3 ) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 981 | 		return  0; | 
 | 982 |  | 
 | 983 | 	value = inb( ioaddr + DAT ); | 
 | 984 | 	*frameno = (u32)value; | 
 | 985 | 	crc = CRC32( value, crc ); | 
 | 986 |  | 
 | 987 | 	crc = CRC32( inb( ioaddr + DAT ), crc );	/* reserved byte */ | 
 | 988 | 	*framelen -= 2; | 
 | 989 |  | 
 | 990 | 	*crc_p = crc; | 
 | 991 | 	return  1; | 
 | 992 | } | 
 | 993 |  | 
 | 994 |  | 
 | 995 | static struct sk_buff * | 
 | 996 | get_rx_buf( struct net_device  *dev ) | 
 | 997 | { | 
 | 998 | 	/* +2 is to compensate for the alignment fixup below */ | 
 | 999 | 	struct sk_buff  *skb = dev_alloc_skb( ETHER_MAX_LEN + 2 ); | 
 | 1000 | 	if( !skb ) | 
 | 1001 | 		return  NULL; | 
 | 1002 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1003 | 	skb_reserve( skb, 2 );		/* Align IP on longword boundaries */ | 
 | 1004 | 	return  skb; | 
 | 1005 | } | 
 | 1006 |  | 
 | 1007 |  | 
 | 1008 | static void | 
 | 1009 | indicate_pkt( struct net_device  *dev ) | 
 | 1010 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 1011 | 	struct net_local  *nl  = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1012 | 	struct sk_buff    *skb = nl->rx_buf_p; | 
 | 1013 |  | 
 | 1014 | 	skb_put( skb, nl->inppos ); | 
 | 1015 |  | 
 | 1016 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 1017 | 	skb->protocol = eth_type_trans( skb, nl->master ); | 
 | 1018 | 	netif_rx( skb ); | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 1019 | 	++nl->master->stats.rx_packets; | 
 | 1020 | 	nl->master->stats.rx_bytes += nl->inppos; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1021 | #else | 
 | 1022 | 	skb->protocol = eth_type_trans( skb, dev ); | 
 | 1023 | 	netif_rx( skb ); | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 1024 | 	++dev->stats.rx_packets; | 
 | 1025 | 	dev->stats.rx_bytes += nl->inppos; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1026 | #endif | 
 | 1027 | 	nl->rx_buf_p = NULL;	/* protocol driver will clear this sk_buff */ | 
 | 1028 | } | 
 | 1029 |  | 
 | 1030 |  | 
 | 1031 | /* -------------------------------------------------------------------------- */ | 
 | 1032 |  | 
 | 1033 | /* | 
 | 1034 |  * Routine checks periodically wire activity and regenerates marker if | 
 | 1035 |  * connect was inactive for a long time. | 
 | 1036 |  */ | 
 | 1037 |  | 
 | 1038 | static void | 
 | 1039 | sbni_watchdog( unsigned long  arg ) | 
 | 1040 | { | 
 | 1041 | 	struct net_device  *dev = (struct net_device *) arg; | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 1042 | 	struct net_local   *nl  = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1043 | 	struct timer_list  *w   = &nl->watchdog;  | 
 | 1044 | 	unsigned long	   flags; | 
 | 1045 | 	unsigned char	   csr0; | 
 | 1046 |  | 
 | 1047 | 	spin_lock_irqsave( &nl->lock, flags ); | 
 | 1048 |  | 
 | 1049 | 	csr0 = inb( dev->base_addr + CSR0 ); | 
 | 1050 | 	if( csr0 & RC_CHK ) { | 
 | 1051 |  | 
 | 1052 | 		if( nl->timer_ticks ) { | 
 | 1053 | 			if( csr0 & (RC_RDY | BU_EMP) ) | 
 | 1054 | 				/* receiving not active */ | 
 | 1055 | 				nl->timer_ticks--; | 
 | 1056 | 		} else { | 
 | 1057 | 			nl->in_stats.timeout_number++; | 
 | 1058 | 			if( nl->delta_rxl ) | 
 | 1059 | 				timeout_change_level( dev ); | 
 | 1060 |  | 
 | 1061 | 			outb( *(u_char *)&nl->csr1 | PR_RES, | 
 | 1062 | 			      dev->base_addr + CSR1 ); | 
 | 1063 | 			csr0 = inb( dev->base_addr + CSR0 ); | 
 | 1064 | 		} | 
 | 1065 | 	} else | 
 | 1066 | 		nl->state &= ~FL_LINE_DOWN; | 
 | 1067 |  | 
 | 1068 | 	outb( csr0 | RC_CHK, dev->base_addr + CSR0 );  | 
 | 1069 |  | 
 | 1070 | 	init_timer( w ); | 
 | 1071 | 	w->expires	= jiffies + SBNI_TIMEOUT; | 
 | 1072 | 	w->data		= arg; | 
 | 1073 | 	w->function	= sbni_watchdog; | 
 | 1074 | 	add_timer( w ); | 
 | 1075 |  | 
 | 1076 | 	spin_unlock_irqrestore( &nl->lock, flags ); | 
 | 1077 | } | 
 | 1078 |  | 
 | 1079 |  | 
 | 1080 | static unsigned char  rxl_tab[] = { | 
 | 1081 | 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, | 
 | 1082 | 	0x0a, 0x0c, 0x0f, 0x16, 0x18, 0x1a, 0x1c, 0x1f | 
 | 1083 | }; | 
 | 1084 |  | 
 | 1085 | #define SIZE_OF_TIMEOUT_RXL_TAB 4 | 
 | 1086 | static unsigned char  timeout_rxl_tab[] = { | 
 | 1087 | 	0x03, 0x05, 0x08, 0x0b | 
 | 1088 | }; | 
 | 1089 |  | 
 | 1090 | /* -------------------------------------------------------------------------- */ | 
 | 1091 |  | 
 | 1092 | static void | 
 | 1093 | card_start( struct net_device  *dev ) | 
 | 1094 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 1095 | 	struct net_local  *nl = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1096 |  | 
 | 1097 | 	nl->timer_ticks = CHANGE_LEVEL_START_TICKS; | 
 | 1098 | 	nl->state &= ~(FL_WAIT_ACK | FL_NEED_RESEND); | 
 | 1099 | 	nl->state |= FL_PREV_OK; | 
 | 1100 |  | 
 | 1101 | 	nl->inppos = nl->outpos = 0; | 
 | 1102 | 	nl->wait_frameno = 0; | 
 | 1103 | 	nl->tx_frameno	 = 0; | 
 | 1104 | 	nl->framelen	 = 0; | 
 | 1105 |  | 
 | 1106 | 	outb( *(u_char *)&nl->csr1 | PR_RES, dev->base_addr + CSR1 ); | 
 | 1107 | 	outb( EN_INT, dev->base_addr + CSR0 ); | 
 | 1108 | } | 
 | 1109 |  | 
 | 1110 | /* -------------------------------------------------------------------------- */ | 
 | 1111 |  | 
 | 1112 | /* Receive level auto-selection */ | 
 | 1113 |  | 
 | 1114 | static void | 
 | 1115 | change_level( struct net_device  *dev ) | 
 | 1116 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 1117 | 	struct net_local  *nl = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1118 |  | 
 | 1119 | 	if( nl->delta_rxl == 0 )	/* do not auto-negotiate RxL */ | 
 | 1120 | 		return; | 
 | 1121 |  | 
 | 1122 | 	if( nl->cur_rxl_index == 0 ) | 
 | 1123 | 		nl->delta_rxl = 1; | 
 | 1124 | 	else if( nl->cur_rxl_index == 15 ) | 
 | 1125 | 		nl->delta_rxl = -1; | 
 | 1126 | 	else if( nl->cur_rxl_rcvd < nl->prev_rxl_rcvd ) | 
 | 1127 | 		nl->delta_rxl = -nl->delta_rxl; | 
 | 1128 |  | 
 | 1129 | 	nl->csr1.rxl = rxl_tab[ nl->cur_rxl_index += nl->delta_rxl ]; | 
 | 1130 | 	inb( dev->base_addr + CSR0 );	/* needs for PCI cards */ | 
 | 1131 | 	outb( *(u8 *)&nl->csr1, dev->base_addr + CSR1 ); | 
 | 1132 |  | 
 | 1133 | 	nl->prev_rxl_rcvd = nl->cur_rxl_rcvd; | 
 | 1134 | 	nl->cur_rxl_rcvd  = 0; | 
 | 1135 | } | 
 | 1136 |  | 
 | 1137 |  | 
 | 1138 | static void | 
 | 1139 | timeout_change_level( struct net_device  *dev ) | 
 | 1140 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 1141 | 	struct net_local  *nl = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1142 |  | 
 | 1143 | 	nl->cur_rxl_index = timeout_rxl_tab[ nl->timeout_rxl ]; | 
 | 1144 | 	if( ++nl->timeout_rxl >= 4 ) | 
 | 1145 | 		nl->timeout_rxl = 0; | 
 | 1146 |  | 
 | 1147 | 	nl->csr1.rxl = rxl_tab[ nl->cur_rxl_index ]; | 
 | 1148 | 	inb( dev->base_addr + CSR0 ); | 
 | 1149 | 	outb( *(unsigned char *)&nl->csr1, dev->base_addr + CSR1 ); | 
 | 1150 |  | 
 | 1151 | 	nl->prev_rxl_rcvd = nl->cur_rxl_rcvd; | 
 | 1152 | 	nl->cur_rxl_rcvd  = 0; | 
 | 1153 | } | 
 | 1154 |  | 
 | 1155 | /* -------------------------------------------------------------------------- */ | 
 | 1156 |  | 
 | 1157 | /* | 
 | 1158 |  *	Open/initialize the board.  | 
 | 1159 |  */ | 
 | 1160 |  | 
 | 1161 | static int | 
 | 1162 | sbni_open( struct net_device  *dev ) | 
 | 1163 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 1164 | 	struct net_local	*nl = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1165 | 	struct timer_list	*w  = &nl->watchdog; | 
 | 1166 |  | 
 | 1167 | 	/* | 
 | 1168 | 	 * For double ISA adapters within "common irq" mode, we have to | 
 | 1169 | 	 * determine whether primary or secondary channel is initialized, | 
 | 1170 | 	 * and set the irq handler only in first case. | 
 | 1171 | 	 */ | 
 | 1172 | 	if( dev->base_addr < 0x400 ) {		/* ISA only */ | 
 | 1173 | 		struct net_device  **p = sbni_cards; | 
 | 1174 | 		for( ;  *p  &&  p < sbni_cards + SBNI_MAX_NUM_CARDS;  ++p ) | 
| Joe Perches | 8e95a20 | 2009-12-03 07:58:21 +0000 | [diff] [blame] | 1175 | 			if( (*p)->irq == dev->irq && | 
 | 1176 | 			    ((*p)->base_addr == dev->base_addr + 4 || | 
 | 1177 | 			     (*p)->base_addr == dev->base_addr - 4) && | 
 | 1178 | 			    (*p)->flags & IFF_UP ) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1179 |  | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 1180 | 				((struct net_local *) (netdev_priv(*p))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1181 | 					->second = dev; | 
 | 1182 | 				printk( KERN_NOTICE "%s: using shared irq " | 
 | 1183 | 					"with %s\n", dev->name, (*p)->name ); | 
 | 1184 | 				nl->state |= FL_SECONDARY; | 
 | 1185 | 				goto  handler_attached; | 
 | 1186 | 			} | 
 | 1187 | 	} | 
 | 1188 |  | 
| Thomas Gleixner | 1fb9df5 | 2006-07-01 19:29:39 -0700 | [diff] [blame] | 1189 | 	if( request_irq(dev->irq, sbni_interrupt, IRQF_SHARED, dev->name, dev) ) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1190 | 		printk( KERN_ERR "%s: unable to get IRQ %d.\n", | 
 | 1191 | 			dev->name, dev->irq ); | 
 | 1192 | 		return  -EAGAIN; | 
 | 1193 | 	} | 
 | 1194 |  | 
 | 1195 | handler_attached: | 
 | 1196 |  | 
 | 1197 | 	spin_lock( &nl->lock ); | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 1198 | 	memset( &dev->stats, 0, sizeof(struct net_device_stats) ); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1199 | 	memset( &nl->in_stats, 0, sizeof(struct sbni_in_stats) ); | 
 | 1200 |  | 
 | 1201 | 	card_start( dev ); | 
 | 1202 |  | 
 | 1203 | 	netif_start_queue( dev ); | 
 | 1204 |  | 
 | 1205 | 	/* set timer watchdog */ | 
 | 1206 | 	init_timer( w ); | 
 | 1207 | 	w->expires	= jiffies + SBNI_TIMEOUT; | 
 | 1208 | 	w->data		= (unsigned long) dev; | 
 | 1209 | 	w->function	= sbni_watchdog; | 
 | 1210 | 	add_timer( w ); | 
 | 1211 |     | 
 | 1212 | 	spin_unlock( &nl->lock ); | 
 | 1213 | 	return 0; | 
 | 1214 | } | 
 | 1215 |  | 
 | 1216 |  | 
 | 1217 | static int | 
 | 1218 | sbni_close( struct net_device  *dev ) | 
 | 1219 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 1220 | 	struct net_local  *nl = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1221 |  | 
 | 1222 | 	if( nl->second  &&  nl->second->flags & IFF_UP ) { | 
 | 1223 | 		printk( KERN_NOTICE "Secondary channel (%s) is active!\n", | 
 | 1224 | 			nl->second->name ); | 
 | 1225 | 		return  -EBUSY; | 
 | 1226 | 	} | 
 | 1227 |  | 
 | 1228 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 1229 | 	if( nl->state & FL_SLAVE ) | 
 | 1230 | 		emancipate( dev ); | 
 | 1231 | 	else | 
 | 1232 | 		while( nl->link )	/* it's master device! */ | 
 | 1233 | 			emancipate( nl->link ); | 
 | 1234 | #endif | 
 | 1235 |  | 
 | 1236 | 	spin_lock( &nl->lock ); | 
 | 1237 |  | 
 | 1238 | 	nl->second = NULL; | 
 | 1239 | 	drop_xmit_queue( dev );	 | 
 | 1240 | 	netif_stop_queue( dev ); | 
 | 1241 |     | 
 | 1242 | 	del_timer( &nl->watchdog ); | 
 | 1243 |  | 
 | 1244 | 	outb( 0, dev->base_addr + CSR0 ); | 
 | 1245 |  | 
 | 1246 | 	if( !(nl->state & FL_SECONDARY) ) | 
 | 1247 | 		free_irq( dev->irq, dev ); | 
 | 1248 | 	nl->state &= FL_SECONDARY; | 
 | 1249 |  | 
 | 1250 | 	spin_unlock( &nl->lock ); | 
 | 1251 | 	return 0; | 
 | 1252 | } | 
 | 1253 |  | 
 | 1254 |  | 
 | 1255 | /* | 
 | 1256 | 	Valid combinations in CSR0 (for probing): | 
 | 1257 |  | 
 | 1258 | 	VALID_DECODER	0000,0011,1011,1010 | 
 | 1259 |  | 
 | 1260 | 				    	; 0   ; - | 
 | 1261 | 				TR_REQ	; 1   ; + | 
 | 1262 | 			TR_RDY	    	; 2   ; - | 
 | 1263 | 			TR_RDY	TR_REQ	; 3   ; + | 
 | 1264 | 		BU_EMP		    	; 4   ; + | 
 | 1265 | 		BU_EMP	     	TR_REQ	; 5   ; + | 
 | 1266 | 		BU_EMP	TR_RDY	    	; 6   ; - | 
 | 1267 | 		BU_EMP	TR_RDY	TR_REQ	; 7   ; + | 
 | 1268 | 	RC_RDY 		     		; 8   ; + | 
 | 1269 | 	RC_RDY			TR_REQ	; 9   ; + | 
 | 1270 | 	RC_RDY		TR_RDY		; 10  ; - | 
 | 1271 | 	RC_RDY		TR_RDY	TR_REQ	; 11  ; - | 
 | 1272 | 	RC_RDY	BU_EMP			; 12  ; - | 
 | 1273 | 	RC_RDY	BU_EMP		TR_REQ	; 13  ; - | 
 | 1274 | 	RC_RDY	BU_EMP	TR_RDY		; 14  ; - | 
 | 1275 | 	RC_RDY	BU_EMP	TR_RDY	TR_REQ	; 15  ; - | 
 | 1276 | */ | 
 | 1277 |  | 
 | 1278 | #define VALID_DECODER (2 + 8 + 0x10 + 0x20 + 0x80 + 0x100 + 0x200) | 
 | 1279 |  | 
 | 1280 |  | 
 | 1281 | static int | 
 | 1282 | sbni_card_probe( unsigned long  ioaddr ) | 
 | 1283 | { | 
 | 1284 | 	unsigned char  csr0; | 
 | 1285 |  | 
 | 1286 | 	csr0 = inb( ioaddr + CSR0 ); | 
 | 1287 | 	if( csr0 != 0xff  &&  csr0 != 0x00 ) { | 
 | 1288 | 		csr0 &= ~EN_INT; | 
 | 1289 | 		if( csr0 & BU_EMP ) | 
 | 1290 | 			csr0 |= EN_INT; | 
 | 1291 |        | 
 | 1292 | 		if( VALID_DECODER & (1 << (csr0 >> 4)) ) | 
 | 1293 | 			return  0; | 
 | 1294 | 	} | 
 | 1295 |     | 
 | 1296 | 	return  -ENODEV; | 
 | 1297 | } | 
 | 1298 |  | 
 | 1299 | /* -------------------------------------------------------------------------- */ | 
 | 1300 |  | 
 | 1301 | static int | 
 | 1302 | sbni_ioctl( struct net_device  *dev,  struct ifreq  *ifr,  int  cmd ) | 
 | 1303 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 1304 | 	struct net_local  *nl = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1305 | 	struct sbni_flags  flags; | 
 | 1306 | 	int  error = 0; | 
 | 1307 |  | 
 | 1308 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 1309 | 	struct net_device  *slave_dev; | 
 | 1310 | 	char  slave_name[ 8 ]; | 
 | 1311 | #endif | 
 | 1312 |    | 
 | 1313 | 	switch( cmd ) { | 
 | 1314 | 	case  SIOCDEVGETINSTATS : | 
 | 1315 | 		if (copy_to_user( ifr->ifr_data, &nl->in_stats, | 
 | 1316 | 					sizeof(struct sbni_in_stats) )) | 
 | 1317 | 			error = -EFAULT; | 
 | 1318 | 		break; | 
 | 1319 |  | 
 | 1320 | 	case  SIOCDEVRESINSTATS : | 
| Eugene Teo | f2455eb | 2008-08-27 04:50:30 -0700 | [diff] [blame] | 1321 | 		if (!capable(CAP_NET_ADMIN)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1322 | 			return  -EPERM; | 
 | 1323 | 		memset( &nl->in_stats, 0, sizeof(struct sbni_in_stats) ); | 
 | 1324 | 		break; | 
 | 1325 |  | 
 | 1326 | 	case  SIOCDEVGHWSTATE : | 
 | 1327 | 		flags.mac_addr	= *(u32 *)(dev->dev_addr + 3); | 
 | 1328 | 		flags.rate	= nl->csr1.rate; | 
 | 1329 | 		flags.slow_mode	= (nl->state & FL_SLOW_MODE) != 0; | 
 | 1330 | 		flags.rxl	= nl->cur_rxl_index; | 
 | 1331 | 		flags.fixed_rxl	= nl->delta_rxl == 0; | 
 | 1332 |  | 
 | 1333 | 		if (copy_to_user( ifr->ifr_data, &flags, sizeof flags )) | 
 | 1334 | 			error = -EFAULT; | 
 | 1335 | 		break; | 
 | 1336 |  | 
 | 1337 | 	case  SIOCDEVSHWSTATE : | 
| Eugene Teo | f2455eb | 2008-08-27 04:50:30 -0700 | [diff] [blame] | 1338 | 		if (!capable(CAP_NET_ADMIN)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1339 | 			return  -EPERM; | 
 | 1340 |  | 
 | 1341 | 		spin_lock( &nl->lock ); | 
 | 1342 | 		flags = *(struct sbni_flags*) &ifr->ifr_ifru; | 
 | 1343 | 		if( flags.fixed_rxl ) | 
 | 1344 | 			nl->delta_rxl = 0, | 
 | 1345 | 			nl->cur_rxl_index = flags.rxl; | 
 | 1346 | 		else | 
 | 1347 | 			nl->delta_rxl = DEF_RXL_DELTA, | 
 | 1348 | 			nl->cur_rxl_index = DEF_RXL; | 
 | 1349 |  | 
 | 1350 | 		nl->csr1.rxl = rxl_tab[ nl->cur_rxl_index ]; | 
 | 1351 | 		nl->csr1.rate = flags.rate; | 
 | 1352 | 		outb( *(u8 *)&nl->csr1 | PR_RES, dev->base_addr + CSR1 ); | 
 | 1353 | 		spin_unlock( &nl->lock ); | 
 | 1354 | 		break; | 
 | 1355 |  | 
 | 1356 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 1357 |  | 
 | 1358 | 	case  SIOCDEVENSLAVE : | 
| Eugene Teo | f2455eb | 2008-08-27 04:50:30 -0700 | [diff] [blame] | 1359 | 		if (!capable(CAP_NET_ADMIN)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1360 | 			return  -EPERM; | 
 | 1361 |  | 
 | 1362 | 		if (copy_from_user( slave_name, ifr->ifr_data, sizeof slave_name )) | 
 | 1363 | 			return -EFAULT; | 
| Eric W. Biederman | 881d966 | 2007-09-17 11:56:21 -0700 | [diff] [blame] | 1364 | 		slave_dev = dev_get_by_name(&init_net, slave_name ); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1365 | 		if( !slave_dev  ||  !(slave_dev->flags & IFF_UP) ) { | 
 | 1366 | 			printk( KERN_ERR "%s: trying to enslave non-active " | 
 | 1367 | 				"device %s\n", dev->name, slave_name ); | 
 | 1368 | 			return  -EPERM; | 
 | 1369 | 		} | 
 | 1370 |  | 
 | 1371 | 		return  enslave( dev, slave_dev ); | 
 | 1372 |  | 
 | 1373 | 	case  SIOCDEVEMANSIPATE : | 
| Eugene Teo | f2455eb | 2008-08-27 04:50:30 -0700 | [diff] [blame] | 1374 | 		if (!capable(CAP_NET_ADMIN)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1375 | 			return  -EPERM; | 
 | 1376 |  | 
 | 1377 | 		return  emancipate( dev ); | 
 | 1378 |  | 
 | 1379 | #endif	/* CONFIG_SBNI_MULTILINE */ | 
 | 1380 |  | 
 | 1381 | 	default : | 
 | 1382 | 		return  -EOPNOTSUPP; | 
 | 1383 | 	} | 
 | 1384 |  | 
 | 1385 | 	return  error; | 
 | 1386 | } | 
 | 1387 |  | 
 | 1388 |  | 
 | 1389 | #ifdef CONFIG_SBNI_MULTILINE | 
 | 1390 |  | 
 | 1391 | static int | 
 | 1392 | enslave( struct net_device  *dev,  struct net_device  *slave_dev ) | 
 | 1393 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 1394 | 	struct net_local  *nl  = netdev_priv(dev); | 
 | 1395 | 	struct net_local  *snl = netdev_priv(slave_dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1396 |  | 
 | 1397 | 	if( nl->state & FL_SLAVE )	/* This isn't master or free device */ | 
 | 1398 | 		return  -EBUSY; | 
 | 1399 |  | 
 | 1400 | 	if( snl->state & FL_SLAVE )	/* That was already enslaved */ | 
 | 1401 | 		return  -EBUSY; | 
 | 1402 |  | 
 | 1403 | 	spin_lock( &nl->lock ); | 
 | 1404 | 	spin_lock( &snl->lock ); | 
 | 1405 |  | 
 | 1406 | 	/* append to list */ | 
 | 1407 | 	snl->link = nl->link; | 
 | 1408 | 	nl->link  = slave_dev; | 
 | 1409 | 	snl->master = dev; | 
 | 1410 | 	snl->state |= FL_SLAVE; | 
 | 1411 |  | 
 | 1412 | 	/* Summary statistics of MultiLine operation will be stored | 
 | 1413 | 	   in master's counters */ | 
| Stephen Hemminger | fe6c6fb | 2009-03-20 19:36:19 +0000 | [diff] [blame] | 1414 | 	memset( &slave_dev->stats, 0, sizeof(struct net_device_stats) ); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1415 | 	netif_stop_queue( slave_dev ); | 
 | 1416 | 	netif_wake_queue( dev );	/* Now we are able to transmit */ | 
 | 1417 |  | 
 | 1418 | 	spin_unlock( &snl->lock ); | 
 | 1419 | 	spin_unlock( &nl->lock ); | 
 | 1420 | 	printk( KERN_NOTICE "%s: slave device (%s) attached.\n", | 
 | 1421 | 		dev->name, slave_dev->name ); | 
 | 1422 | 	return  0; | 
 | 1423 | } | 
 | 1424 |  | 
 | 1425 |  | 
 | 1426 | static int | 
 | 1427 | emancipate( struct net_device  *dev ) | 
 | 1428 | { | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 1429 | 	struct net_local   *snl = netdev_priv(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1430 | 	struct net_device  *p   = snl->master; | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 1431 | 	struct net_local   *nl  = netdev_priv(p); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1432 |  | 
 | 1433 | 	if( !(snl->state & FL_SLAVE) ) | 
 | 1434 | 		return  -EINVAL; | 
 | 1435 |  | 
 | 1436 | 	spin_lock( &nl->lock ); | 
 | 1437 | 	spin_lock( &snl->lock ); | 
 | 1438 | 	drop_xmit_queue( dev ); | 
 | 1439 |  | 
 | 1440 | 	/* exclude from list */ | 
 | 1441 | 	for(;;) {	/* must be in list */ | 
| Wang Chen | 486bf8d | 2008-11-24 14:52:16 -0800 | [diff] [blame] | 1442 | 		struct net_local  *t = netdev_priv(p); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1443 | 		if( t->link == dev ) { | 
 | 1444 | 			t->link = snl->link; | 
 | 1445 | 			break; | 
 | 1446 | 		} | 
 | 1447 | 		p = t->link; | 
 | 1448 | 	} | 
 | 1449 |  | 
 | 1450 | 	snl->link = NULL; | 
 | 1451 | 	snl->master = dev; | 
 | 1452 | 	snl->state &= ~FL_SLAVE; | 
 | 1453 |  | 
 | 1454 | 	netif_start_queue( dev ); | 
 | 1455 |  | 
 | 1456 | 	spin_unlock( &snl->lock ); | 
 | 1457 | 	spin_unlock( &nl->lock ); | 
 | 1458 |  | 
 | 1459 | 	dev_put( dev ); | 
 | 1460 | 	return  0; | 
 | 1461 | } | 
 | 1462 |  | 
 | 1463 | #endif | 
 | 1464 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1465 | static void | 
 | 1466 | set_multicast_list( struct net_device  *dev ) | 
 | 1467 | { | 
 | 1468 | 	return;		/* sbni always operate in promiscuos mode */ | 
 | 1469 | } | 
 | 1470 |  | 
 | 1471 |  | 
 | 1472 | #ifdef MODULE | 
 | 1473 | module_param_array(io, int, NULL, 0); | 
 | 1474 | module_param_array(irq, int, NULL, 0); | 
 | 1475 | module_param_array(baud, int, NULL, 0); | 
 | 1476 | module_param_array(rxl, int, NULL, 0); | 
 | 1477 | module_param_array(mac, int, NULL, 0); | 
 | 1478 | module_param(skip_pci_probe, bool, 0); | 
 | 1479 |  | 
 | 1480 | MODULE_LICENSE("GPL"); | 
 | 1481 |  | 
 | 1482 |  | 
| Sam Ravnborg | 471a24c | 2006-03-20 22:34:52 -0800 | [diff] [blame] | 1483 | int __init init_module( void ) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1484 | { | 
 | 1485 | 	struct net_device  *dev; | 
 | 1486 | 	int err; | 
 | 1487 |  | 
 | 1488 | 	while( num < SBNI_MAX_NUM_CARDS ) { | 
 | 1489 | 		dev = alloc_netdev(sizeof(struct net_local),  | 
 | 1490 | 				   "sbni%d", sbni_devsetup); | 
 | 1491 | 		if( !dev) | 
 | 1492 | 			break; | 
 | 1493 |  | 
 | 1494 | 		sprintf( dev->name, "sbni%d", num ); | 
 | 1495 |  | 
 | 1496 | 		err = sbni_init(dev); | 
 | 1497 | 		if (err) { | 
 | 1498 | 			free_netdev(dev); | 
 | 1499 | 			break; | 
 | 1500 | 		} | 
 | 1501 |  | 
 | 1502 | 		if( register_netdev( dev ) ) { | 
 | 1503 | 			release_region( dev->base_addr, SBNI_IO_EXTENT ); | 
 | 1504 | 			free_netdev( dev ); | 
 | 1505 | 			break; | 
 | 1506 | 		} | 
 | 1507 | 	} | 
 | 1508 |  | 
 | 1509 | 	return  *sbni_cards  ?  0  :  -ENODEV; | 
 | 1510 | } | 
 | 1511 |  | 
 | 1512 | void | 
| Hannes Eder | 5ed1e98 | 2009-02-14 11:48:25 +0000 | [diff] [blame] | 1513 | cleanup_module(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1514 | { | 
| Hannes Eder | 5ed1e98 | 2009-02-14 11:48:25 +0000 | [diff] [blame] | 1515 | 	int i; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1516 |  | 
| Hannes Eder | 5ed1e98 | 2009-02-14 11:48:25 +0000 | [diff] [blame] | 1517 | 	for (i = 0;  i < SBNI_MAX_NUM_CARDS;  ++i) { | 
 | 1518 | 		struct net_device *dev = sbni_cards[i]; | 
 | 1519 | 		if (dev != NULL) { | 
 | 1520 | 			unregister_netdev(dev); | 
 | 1521 | 			release_region(dev->base_addr, SBNI_IO_EXTENT); | 
 | 1522 | 			free_netdev(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1523 | 		} | 
| Hannes Eder | 5ed1e98 | 2009-02-14 11:48:25 +0000 | [diff] [blame] | 1524 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1525 | } | 
 | 1526 |  | 
 | 1527 | #else	/* MODULE */ | 
 | 1528 |  | 
 | 1529 | static int __init | 
 | 1530 | sbni_setup( char  *p ) | 
 | 1531 | { | 
 | 1532 | 	int  n, parm; | 
 | 1533 |  | 
 | 1534 | 	if( *p++ != '(' ) | 
 | 1535 | 		goto  bad_param; | 
 | 1536 |  | 
 | 1537 | 	for( n = 0, parm = 0;  *p  &&  n < 8; ) { | 
 | 1538 | 		(*dest[ parm ])[ n ] = simple_strtol( p, &p, 0 ); | 
 | 1539 | 		if( !*p  ||  *p == ')' ) | 
 | 1540 | 			return 1; | 
 | 1541 | 		if( *p == ';' ) | 
 | 1542 | 			++p, ++n, parm = 0; | 
 | 1543 | 		else if( *p++ != ',' ) | 
 | 1544 | 			break; | 
 | 1545 | 		else | 
 | 1546 | 			if( ++parm >= 5 ) | 
 | 1547 | 				break; | 
 | 1548 | 	} | 
 | 1549 | bad_param: | 
 | 1550 | 	printk( KERN_ERR "Error in sbni kernel parameter!\n" ); | 
 | 1551 | 	return 0; | 
 | 1552 | } | 
 | 1553 |  | 
 | 1554 | __setup( "sbni=", sbni_setup ); | 
 | 1555 |  | 
 | 1556 | #endif	/* MODULE */ | 
 | 1557 |  | 
 | 1558 | /* -------------------------------------------------------------------------- */ | 
 | 1559 |  | 
 | 1560 | #ifdef ASM_CRC | 
 | 1561 |  | 
 | 1562 | static u32 | 
 | 1563 | calc_crc32( u32  crc,  u8  *p,  u32  len ) | 
 | 1564 | { | 
 | 1565 | 	register u32  _crc; | 
 | 1566 | 	_crc = crc; | 
 | 1567 | 	 | 
 | 1568 | 	__asm__ __volatile__ ( | 
 | 1569 | 		"xorl	%%ebx, %%ebx\n" | 
 | 1570 | 		"movl	%2, %%esi\n"  | 
 | 1571 | 		"movl	%3, %%ecx\n"  | 
 | 1572 | 		"movl	$crc32tab, %%edi\n" | 
 | 1573 | 		"shrl	$2, %%ecx\n" | 
 | 1574 | 		"jz	1f\n" | 
 | 1575 |  | 
 | 1576 | 		".align 4\n" | 
 | 1577 | 	"0:\n" | 
 | 1578 | 		"movb	%%al, %%bl\n" | 
 | 1579 | 		"movl	(%%esi), %%edx\n" | 
 | 1580 | 		"shrl	$8, %%eax\n" | 
 | 1581 | 		"xorb	%%dl, %%bl\n" | 
 | 1582 | 		"shrl	$8, %%edx\n" | 
 | 1583 | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
 | 1584 |  | 
 | 1585 | 		"movb	%%al, %%bl\n" | 
 | 1586 | 		"shrl	$8, %%eax\n" | 
 | 1587 | 		"xorb	%%dl, %%bl\n" | 
 | 1588 | 		"shrl	$8, %%edx\n" | 
 | 1589 | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
 | 1590 |  | 
 | 1591 | 		"movb	%%al, %%bl\n" | 
 | 1592 | 		"shrl	$8, %%eax\n" | 
 | 1593 | 		"xorb	%%dl, %%bl\n" | 
 | 1594 | 		"movb	%%dh, %%dl\n"  | 
 | 1595 | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
 | 1596 |  | 
 | 1597 | 		"movb	%%al, %%bl\n" | 
 | 1598 | 		"shrl	$8, %%eax\n" | 
 | 1599 | 		"xorb	%%dl, %%bl\n" | 
 | 1600 | 		"addl	$4, %%esi\n" | 
 | 1601 | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
 | 1602 |  | 
 | 1603 | 		"decl	%%ecx\n" | 
 | 1604 | 		"jnz	0b\n" | 
 | 1605 |  | 
 | 1606 | 	"1:\n" | 
 | 1607 | 		"movl	%3, %%ecx\n" | 
 | 1608 | 		"andl	$3, %%ecx\n" | 
 | 1609 | 		"jz	2f\n" | 
 | 1610 |  | 
 | 1611 | 		"movb	%%al, %%bl\n" | 
 | 1612 | 		"shrl	$8, %%eax\n" | 
 | 1613 | 		"xorb	(%%esi), %%bl\n" | 
 | 1614 | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
 | 1615 |  | 
 | 1616 | 		"decl	%%ecx\n" | 
 | 1617 | 		"jz	2f\n" | 
 | 1618 |  | 
 | 1619 | 		"movb	%%al, %%bl\n" | 
 | 1620 | 		"shrl	$8, %%eax\n" | 
 | 1621 | 		"xorb	1(%%esi), %%bl\n" | 
 | 1622 | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
 | 1623 |  | 
 | 1624 | 		"decl	%%ecx\n" | 
 | 1625 | 		"jz	2f\n" | 
 | 1626 |  | 
 | 1627 | 		"movb	%%al, %%bl\n" | 
 | 1628 | 		"shrl	$8, %%eax\n" | 
 | 1629 | 		"xorb	2(%%esi), %%bl\n" | 
 | 1630 | 		"xorl	(%%edi,%%ebx,4), %%eax\n" | 
 | 1631 | 	"2:\n" | 
 | 1632 | 		: "=a" (_crc) | 
 | 1633 | 		: "0" (_crc), "g" (p), "g" (len) | 
 | 1634 | 		: "bx", "cx", "dx", "si", "di" | 
 | 1635 | 	); | 
 | 1636 |  | 
 | 1637 | 	return  _crc; | 
 | 1638 | } | 
 | 1639 |  | 
 | 1640 | #else	/* ASM_CRC */ | 
 | 1641 |  | 
 | 1642 | static u32 | 
 | 1643 | calc_crc32( u32  crc,  u8  *p,  u32  len ) | 
 | 1644 | { | 
 | 1645 | 	while( len-- ) | 
 | 1646 | 		crc = CRC32( *p++, crc ); | 
 | 1647 |  | 
 | 1648 | 	return  crc; | 
 | 1649 | } | 
 | 1650 |  | 
 | 1651 | #endif	/* ASM_CRC */ | 
 | 1652 |  | 
 | 1653 |  | 
 | 1654 | static u32  crc32tab[] __attribute__ ((aligned(8))) = { | 
 | 1655 | 	0xD202EF8D,  0xA505DF1B,  0x3C0C8EA1,  0x4B0BBE37, | 
 | 1656 | 	0xD56F2B94,  0xA2681B02,  0x3B614AB8,  0x4C667A2E, | 
 | 1657 | 	0xDCD967BF,  0xABDE5729,  0x32D70693,  0x45D03605, | 
 | 1658 | 	0xDBB4A3A6,  0xACB39330,  0x35BAC28A,  0x42BDF21C, | 
 | 1659 | 	0xCFB5FFE9,  0xB8B2CF7F,  0x21BB9EC5,  0x56BCAE53, | 
 | 1660 | 	0xC8D83BF0,  0xBFDF0B66,  0x26D65ADC,  0x51D16A4A, | 
 | 1661 | 	0xC16E77DB,  0xB669474D,  0x2F6016F7,  0x58672661, | 
 | 1662 | 	0xC603B3C2,  0xB1048354,  0x280DD2EE,  0x5F0AE278, | 
 | 1663 | 	0xE96CCF45,  0x9E6BFFD3,  0x0762AE69,  0x70659EFF, | 
 | 1664 | 	0xEE010B5C,  0x99063BCA,  0x000F6A70,  0x77085AE6, | 
 | 1665 | 	0xE7B74777,  0x90B077E1,  0x09B9265B,  0x7EBE16CD, | 
 | 1666 | 	0xE0DA836E,  0x97DDB3F8,  0x0ED4E242,  0x79D3D2D4, | 
 | 1667 | 	0xF4DBDF21,  0x83DCEFB7,  0x1AD5BE0D,  0x6DD28E9B, | 
 | 1668 | 	0xF3B61B38,  0x84B12BAE,  0x1DB87A14,  0x6ABF4A82, | 
 | 1669 | 	0xFA005713,  0x8D076785,  0x140E363F,  0x630906A9, | 
 | 1670 | 	0xFD6D930A,  0x8A6AA39C,  0x1363F226,  0x6464C2B0, | 
 | 1671 | 	0xA4DEAE1D,  0xD3D99E8B,  0x4AD0CF31,  0x3DD7FFA7, | 
 | 1672 | 	0xA3B36A04,  0xD4B45A92,  0x4DBD0B28,  0x3ABA3BBE, | 
 | 1673 | 	0xAA05262F,  0xDD0216B9,  0x440B4703,  0x330C7795, | 
 | 1674 | 	0xAD68E236,  0xDA6FD2A0,  0x4366831A,  0x3461B38C, | 
 | 1675 | 	0xB969BE79,  0xCE6E8EEF,  0x5767DF55,  0x2060EFC3, | 
 | 1676 | 	0xBE047A60,  0xC9034AF6,  0x500A1B4C,  0x270D2BDA, | 
 | 1677 | 	0xB7B2364B,  0xC0B506DD,  0x59BC5767,  0x2EBB67F1, | 
 | 1678 | 	0xB0DFF252,  0xC7D8C2C4,  0x5ED1937E,  0x29D6A3E8, | 
 | 1679 | 	0x9FB08ED5,  0xE8B7BE43,  0x71BEEFF9,  0x06B9DF6F, | 
 | 1680 | 	0x98DD4ACC,  0xEFDA7A5A,  0x76D32BE0,  0x01D41B76, | 
 | 1681 | 	0x916B06E7,  0xE66C3671,  0x7F6567CB,  0x0862575D, | 
 | 1682 | 	0x9606C2FE,  0xE101F268,  0x7808A3D2,  0x0F0F9344, | 
 | 1683 | 	0x82079EB1,  0xF500AE27,  0x6C09FF9D,  0x1B0ECF0B, | 
 | 1684 | 	0x856A5AA8,  0xF26D6A3E,  0x6B643B84,  0x1C630B12, | 
 | 1685 | 	0x8CDC1683,  0xFBDB2615,  0x62D277AF,  0x15D54739, | 
 | 1686 | 	0x8BB1D29A,  0xFCB6E20C,  0x65BFB3B6,  0x12B88320, | 
 | 1687 | 	0x3FBA6CAD,  0x48BD5C3B,  0xD1B40D81,  0xA6B33D17, | 
 | 1688 | 	0x38D7A8B4,  0x4FD09822,  0xD6D9C998,  0xA1DEF90E, | 
 | 1689 | 	0x3161E49F,  0x4666D409,  0xDF6F85B3,  0xA868B525, | 
 | 1690 | 	0x360C2086,  0x410B1010,  0xD80241AA,  0xAF05713C, | 
 | 1691 | 	0x220D7CC9,  0x550A4C5F,  0xCC031DE5,  0xBB042D73, | 
 | 1692 | 	0x2560B8D0,  0x52678846,  0xCB6ED9FC,  0xBC69E96A, | 
 | 1693 | 	0x2CD6F4FB,  0x5BD1C46D,  0xC2D895D7,  0xB5DFA541, | 
 | 1694 | 	0x2BBB30E2,  0x5CBC0074,  0xC5B551CE,  0xB2B26158, | 
 | 1695 | 	0x04D44C65,  0x73D37CF3,  0xEADA2D49,  0x9DDD1DDF, | 
 | 1696 | 	0x03B9887C,  0x74BEB8EA,  0xEDB7E950,  0x9AB0D9C6, | 
 | 1697 | 	0x0A0FC457,  0x7D08F4C1,  0xE401A57B,  0x930695ED, | 
 | 1698 | 	0x0D62004E,  0x7A6530D8,  0xE36C6162,  0x946B51F4, | 
 | 1699 | 	0x19635C01,  0x6E646C97,  0xF76D3D2D,  0x806A0DBB, | 
 | 1700 | 	0x1E0E9818,  0x6909A88E,  0xF000F934,  0x8707C9A2, | 
 | 1701 | 	0x17B8D433,  0x60BFE4A5,  0xF9B6B51F,  0x8EB18589, | 
 | 1702 | 	0x10D5102A,  0x67D220BC,  0xFEDB7106,  0x89DC4190, | 
 | 1703 | 	0x49662D3D,  0x3E611DAB,  0xA7684C11,  0xD06F7C87, | 
 | 1704 | 	0x4E0BE924,  0x390CD9B2,  0xA0058808,  0xD702B89E, | 
 | 1705 | 	0x47BDA50F,  0x30BA9599,  0xA9B3C423,  0xDEB4F4B5, | 
 | 1706 | 	0x40D06116,  0x37D75180,  0xAEDE003A,  0xD9D930AC, | 
 | 1707 | 	0x54D13D59,  0x23D60DCF,  0xBADF5C75,  0xCDD86CE3, | 
 | 1708 | 	0x53BCF940,  0x24BBC9D6,  0xBDB2986C,  0xCAB5A8FA, | 
 | 1709 | 	0x5A0AB56B,  0x2D0D85FD,  0xB404D447,  0xC303E4D1, | 
 | 1710 | 	0x5D677172,  0x2A6041E4,  0xB369105E,  0xC46E20C8, | 
 | 1711 | 	0x72080DF5,  0x050F3D63,  0x9C066CD9,  0xEB015C4F, | 
 | 1712 | 	0x7565C9EC,  0x0262F97A,  0x9B6BA8C0,  0xEC6C9856, | 
 | 1713 | 	0x7CD385C7,  0x0BD4B551,  0x92DDE4EB,  0xE5DAD47D, | 
 | 1714 | 	0x7BBE41DE,  0x0CB97148,  0x95B020F2,  0xE2B71064, | 
 | 1715 | 	0x6FBF1D91,  0x18B82D07,  0x81B17CBD,  0xF6B64C2B, | 
 | 1716 | 	0x68D2D988,  0x1FD5E91E,  0x86DCB8A4,  0xF1DB8832, | 
 | 1717 | 	0x616495A3,  0x1663A535,  0x8F6AF48F,  0xF86DC419, | 
 | 1718 | 	0x660951BA,  0x110E612C,  0x88073096,  0xFF000000 | 
 | 1719 | }; | 
 | 1720 |  |