⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sbni.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* 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/config.h>#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/slab.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>#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 net_device_stats	stats;	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 * );static int  sbni_start_xmit( struct sk_buff *, struct net_device * );static int  sbni_ioctl( struct net_device *, struct ifreq *, int );static struct net_device_stats  *sbni_get_stats( struct net_device * );static void  set_multicast_list( struct net_device * );static irqreturn_t sbni_interrupt( int, void *, struct pt_regs * );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_MULTILINEstatic int  enslave( struct net_device *, struct net_device * );static int  emancipate( struct net_device * );#endif#ifdef __i386__#define ASM_CRC 1#endifstatic 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 MODULEtypedef 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 };/* * 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 __initsbni_isa_probe( struct net_device  *dev ){	if( dev->base_addr > 0x1ff	    &&  request_region( dev->base_addr, SBNI_IO_EXTENT, dev->name )	    &&  sbni_probe1( dev, dev->base_addr, dev->irq ) )		return  0;	else {		printk( KERN_ERR "sbni: base address 0x%lx is busy, or adapter "			"is malfunctional!\n", dev->base_addr );		return  -ENODEV;	}}static void __init sbni_devsetup(struct net_device *dev){	ether_setup( dev );	dev->open		= &sbni_open;	dev->stop		= &sbni_close;	dev->hard_start_xmit	= &sbni_start_xmit;	dev->get_stats		= &sbni_get_stats;	dev->set_multicast_list	= &set_multicast_list;	dev->do_ioctl		= &sbni_ioctl;	SET_MODULE_OWNER( dev );}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;	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 ];		if( request_region( ioaddr, SBNI_IO_EXTENT, dev->name )		    &&  sbni_probe1( dev, ioaddr, 0 ))			return 0;	}	return  -ENODEV;}int __initsbni_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;		if( pdev->vendor != SBNI_PCI_VENDOR		    &&  pdev->device != SBNI_PCI_DEVICE )				continue;		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;		}		if( pci_irq_line <= 0  ||  pci_irq_line >= NR_IRQS )			printk( KERN_WARNING "  WARNING: The PCI BIOS assigned "				"this PCI card to IRQ %d, which is unlikely "				"to work!.\n"				KERN_WARNING " You should use the PCI BIOS "				"setup to assign a valid IRQ line.\n",				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 * __initsbni_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;	/* Allocate dev->priv and fill in sbni-specific dev fields. */	nl = dev->priv;	if( !nl ) {		printk( KERN_ERR "%s: unable to get memory!\n", dev->name );		release_region( ioaddr, SBNI_IO_EXTENT );		return NULL;	}	dev->priv = nl;	memset( nl, 0, sizeof(struct net_local) );	spin_lock_init( &nl->lock );	/* store MAC address (generate if that isn't known) */	*(u16 *)dev->dev_addr = htons( 0x00ff );	*(u32 *)(dev->dev_addr + 2) = htonl( 0x01000000 |		( (mac[num]  ?  mac[num]  :  (u32)((long)dev->priv)) & 0x00ffffff) );	/* 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_MULTILINEstatic intsbni_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; ) {		struct net_local  *nl = (struct net_local *) p->priv;		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 );			return  0;		}	}	return  1;}#else	/* CONFIG_SBNI_MULTILINE */static intsbni_start_xmit( struct sk_buff  *skb,  struct net_device  *dev ){	struct net_local  *nl  = (struct net_local *) dev->priv;	netif_stop_queue( dev );	spin_lock( &nl->lock );	prepare_to_send( skb, dev );	spin_unlock( &nl->lock );	return  0;}#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_tsbni_interrupt( int  irq,  void  *dev_id,  struct pt_regs  *regs ){	struct net_device	  *dev = (struct net_device *) dev_id;	struct net_local  *nl  = (struct net_local *) dev->priv;	int	repeat;	spin_lock( &nl->lock );	if( nl->second )		spin_lock( &((struct net_local *) nl->second->priv)->lock );	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 )		spin_unlock( &((struct net_local *)nl->second->priv)->lock );	spin_unlock( &nl->lock );	return IRQ_HANDLED;}static voidhandle_channel( struct net_device  *dev ){	struct net_local	*nl    = (struct net_local *) dev->priv;	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 )		spin_lock( &((struct net_local *) nl->master->priv)->lock );#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

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -