2005-04-17 02:20:36 +04:00
/*
* Digi RightSwitch SE - X loadable device driver for Linux
*
* The RightSwitch is a 4 ( EISA ) or 6 ( PCI ) port etherswitch and
* a NIC on an internal board .
*
* Author : Rick Richardson , rick @ remotepoint . com
* Derived from the SVR4 .2 ( UnixWare ) driver for the same card .
*
* Copyright 1995 - 1996 Digi International Inc .
*
* This software may be used and distributed according to the terms
* of the GNU General Public License , incorporated herein by reference .
*
* For information on purchasing a RightSwitch SE - 4 or SE - 6
* board , please contact Digi ' s sales department at 1 - 612 - 912 - 3444
* or 1 - 800 - DIGIBRD . Outside the U . S . , please check our Web page
* at http : //www.dgii.com for sales offices worldwide.
*
* OPERATION :
* When compiled as a loadable module , this driver can operate
* the board as either a 4 / 6 port switch with a 5 th or 7 th port
* that is a conventional NIC interface as far as the host is
* concerned , OR as 4 / 6 independent NICs . To select multi - NIC
* mode , add " nicmode=1 " on the insmod load line for the driver .
*
* This driver uses the " dev " common ethernet device structure
* and a private " priv " ( dev - > priv ) structure that contains
* mostly DGRS - specific information and statistics . To keep
* the code for both the switch mode and the multi - NIC mode
* as similar as possible , I have introduced the concept of
* " dev0 " / " priv0 " and " devN " / " privN " pointer pairs in subroutines
* where needed . The first pair of pointers points to the
* " dev " and " priv " structures of the zeroth ( 0 th ) device
* interface associated with a board . The second pair of
* pointers points to the current ( Nth ) device interface
* for the board : the one for which we are processing data .
*
* In switch mode , the pairs of pointers are always the same ,
* that is , dev0 = = devN and priv0 = = privN . This is just
* like previous releases of this driver which did not support
* NIC mode .
*
* In multi - NIC mode , the pairs of pointers may be different .
* We use the devN and privN pointers to reference just the
* name , port number , and statistics for the current interface .
* We use the dev0 and priv0 pointers to access the variables
* that control access to the board , such as board address
* and simulated 82596 variables . This is because there is
* only one " fake " 82596 that serves as the interface to
* the board . We do not want to try to keep the variables
* associated with this 82596 in sync across all devices .
*
* This scheme works well . As you will see , except for
* initialization , there is very little difference between
* the two modes as far as this driver is concerned . On the
* receive side in NIC mode , the interrupt * always * comes in on
* the 0 th interface ( dev0 / priv0 ) . We then figure out which
* real 82596 port it came in on from looking at the " chan "
* member that the board firmware adds at the end of each
* RBD ( a . k . a . TBD ) . We get the channel number like this :
* int chan = ( ( I596_RBD * ) S2H ( cbp - > xmit . tbdp ) ) - > chan ;
*
* On the transmit side in multi - NIC mode , we specify the
* output 82596 port by setting the new " dstchan " structure
* member that is at the end of the RFD , like this :
* priv0 - > rfdp - > dstchan = privN - > chan ;
*
* TODO :
* - Multi - NIC mode is not yet supported when the driver is linked
* into the kernel .
* - Better handling of multicast addresses .
*
* Fixes :
* Arnaldo Carvalho de Melo < acme @ conectiva . com . br > - 11 / 01 / 2001
* - fix dgrs_found_device wrt checking kmalloc return and
* rollbacking the partial steps of the whole process when
* one of the devices can ' t be allocated . Fix SET_MODULE_OWNER
* on the loop to use devN instead of repeated calls to dev .
*
* davej < davej @ suse . de > - 9 / 2 / 2001
* - Enable PCI device before reading ioaddr / irq
*
*/
# include <linux/module.h>
# include <linux/eisa.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/ioport.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <linux/bitops.h>
# include <asm/io.h>
# include <asm/byteorder.h>
# include <asm/uaccess.h>
static char version [ ] __initdata =
" $Id: dgrs.c,v 1.13 2000/06/06 04:07:00 rick Exp $ " ;
/*
* DGRS include files
*/
typedef unsigned char uchar ;
typedef unsigned int bool ;
# define vol volatile
# include "dgrs.h"
# include "dgrs_es4h.h"
# include "dgrs_plx9060.h"
# include "dgrs_i82596.h"
# include "dgrs_ether.h"
# include "dgrs_asstruct.h"
# include "dgrs_bcomm.h"
# ifdef CONFIG_PCI
static struct pci_device_id dgrs_pci_tbl [ ] = {
{ SE6_PCI_VENDOR_ID , SE6_PCI_DEVICE_ID , PCI_ANY_ID , PCI_ANY_ID , } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( pci , dgrs_pci_tbl ) ;
# endif
# ifdef CONFIG_EISA
static struct eisa_device_id dgrs_eisa_tbl [ ] = {
{ " DBI0A01 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( eisa , dgrs_eisa_tbl ) ;
# endif
MODULE_LICENSE ( " GPL " ) ;
/*
* Firmware . Compiled separately for local compilation ,
* but # included for Linux distribution .
*/
# ifndef NOFW
# include "dgrs_firmware.c"
# else
extern int dgrs_firmnum ;
extern char dgrs_firmver [ ] ;
extern char dgrs_firmdate [ ] ;
extern uchar dgrs_code [ ] ;
extern int dgrs_ncode ;
# endif
/*
* Linux out * ( ) is backwards from all other operating systems
*/
# define OUTB(ADDR, VAL) outb(VAL, ADDR)
# define OUTW(ADDR, VAL) outw(VAL, ADDR)
# define OUTL(ADDR, VAL) outl(VAL, ADDR)
/*
* Macros to convert switch to host and host to switch addresses
* ( assumes a local variable priv points to board dependent struct )
*/
# define S2H(A) ( ((unsigned long)(A)&0x00ffffff) + priv0->vmem )
# define S2HN(A) ( ((unsigned long)(A)&0x00ffffff) + privN->vmem )
# define H2S(A) ( ((char *) (A) - priv0->vmem) + 0xA3000000 )
/*
* Convert a switch address to a " safe " address for use with the
* PLX 9060 DMA registers and the associated HW kludge that allows
* for host access of the DMA registers .
*/
# define S2DMA(A) ( (unsigned long)(A) & 0x00ffffff)
/*
* " Space.c " variables , now settable from module interface
* Use the name below , minus the " dgrs_ " prefix . See init_module ( ) .
*/
static int dgrs_debug = 1 ;
static int dgrs_dma = 1 ;
static int dgrs_spantree = - 1 ;
static int dgrs_hashexpire = - 1 ;
static uchar dgrs_ipaddr [ 4 ] = { 0xff , 0xff , 0xff , 0xff } ;
static uchar dgrs_iptrap [ 4 ] = { 0xff , 0xff , 0xff , 0xff } ;
static __u32 dgrs_ipxnet = - 1 ;
static int dgrs_nicmode ;
/*
* Private per - board data structure ( dev - > priv )
*/
typedef struct
{
/*
* Stuff for generic ethercard I / F
*/
struct net_device_stats stats ;
/*
* DGRS specific data
*/
char * vmem ;
struct bios_comm * bcomm ; /* Firmware BIOS comm structure */
PORT * port ; /* Ptr to PORT[0] struct in VM */
I596_SCB * scbp ; /* Ptr to SCB struct in VM */
I596_RFD * rfdp ; /* Current RFD list */
I596_RBD * rbdp ; /* Current RBD list */
volatile int intrcnt ; /* Count of interrupts */
/*
* SE - 4 ( EISA ) board variables
*/
uchar is_reg ; /* EISA: Value for ES4H_IS reg */
/*
* SE - 6 ( PCI ) board variables
*
* The PLX " expansion rom " space is used for DMA register
* access from the host on the SE - 6. These are the physical
* and virtual addresses of that space .
*/
ulong plxreg ; /* Phys address of PLX chip */
char * vplxreg ; /* Virtual address of PLX chip */
ulong plxdma ; /* Phys addr of PLX "expansion rom" */
ulong volatile * vplxdma ; /* Virtual addr of "expansion rom" */
int use_dma ; /* Flag: use DMA */
DMACHAIN * dmadesc_s ; /* area for DMA chains (SW addr.) */
DMACHAIN * dmadesc_h ; /* area for DMA chains (Host Virtual) */
/*
* Multi - NIC mode variables
*
* All entries of the devtbl [ ] array are valid for the 0 th
* device ( i . e . eth0 , but not eth1 . . . eth5 ) . devtbl [ 0 ] is
* valid for all devices ( i . e . eth0 , eth1 , . . . , eth5 ) .
*/
int nports ; /* Number of physical ports (4 or 6) */
int chan ; /* Channel # (1-6) for this device */
struct net_device * devtbl [ 6 ] ; /* Ptrs to N device structs */
} DGRS_PRIV ;
/*
* reset or un - reset the IDT processor
*/
static void
proc_reset ( struct net_device * dev0 , int reset )
{
DGRS_PRIV * priv0 = ( DGRS_PRIV * ) dev0 - > priv ;
if ( priv0 - > plxreg )
{
ulong val ;
val = inl ( dev0 - > base_addr + PLX_MISC_CSR ) ;
if ( reset )
val | = SE6_RESET ;
else
val & = ~ SE6_RESET ;
OUTL ( dev0 - > base_addr + PLX_MISC_CSR , val ) ;
}
else
{
OUTB ( dev0 - > base_addr + ES4H_PC , reset ? ES4H_PC_RESET : 0 ) ;
}
}
/*
* See if the board supports bus master DMA
*/
static int
check_board_dma ( struct net_device * dev0 )
{
DGRS_PRIV * priv0 = ( DGRS_PRIV * ) dev0 - > priv ;
ulong x ;
/*
* If Space . c says not to use DMA , or if it ' s not a PLX based
* PCI board , or if the expansion ROM space is not PCI
* configured , then return false .
*/
if ( ! dgrs_dma | | ! priv0 - > plxreg | | ! priv0 - > plxdma )
return ( 0 ) ;
/*
* Set the local address remap register of the " expansion rom "
* area to 0x80000000 so that we can use it to access the DMA
* registers from the host side .
*/
OUTL ( dev0 - > base_addr + PLX_ROM_BASE_ADDR , 0x80000000 ) ;
/*
* Set the PCI region descriptor to :
* Space 0 :
* disable read - prefetch
* enable READY
* enable BURST
* 0 internal wait states
* Expansion ROM : ( used for host DMA register access )
* disable read - prefetch
* enable READY
* disable BURST
* 0 internal wait states
*/
OUTL ( dev0 - > base_addr + PLX_BUS_REGION , 0x49430343 ) ;
/*
* Now map the DMA registers into our virtual space
*/
priv0 - > vplxdma = ( ulong * ) ioremap ( priv0 - > plxdma , 256 ) ;
if ( ! priv0 - > vplxdma )
{
printk ( " %s: can't *remap() the DMA regs \n " , dev0 - > name ) ;
return ( 0 ) ;
}
/*
* Now test to see if we can access the DMA registers
* If we write - 1 and get back 1FF F , then we accessed the
* DMA register . Otherwise , we probably have an old board
* and wrote into regular RAM .
*/
priv0 - > vplxdma [ PLX_DMA0_MODE / 4 ] = 0xFFFFFFFF ;
x = priv0 - > vplxdma [ PLX_DMA0_MODE / 4 ] ;
if ( x ! = 0x00001FFF ) {
iounmap ( ( void * ) priv0 - > vplxdma ) ;
return ( 0 ) ;
}
return ( 1 ) ;
}
/*
* Initiate DMA using PLX part on PCI board . Spin the
* processor until completed . All addresses are physical !
*
* If pciaddr is NULL , then it ' s a chaining DMA , and lcladdr is
* the address of the first DMA descriptor in the chain .
*
* If pciaddr is not NULL , then it ' s a single DMA .
*
* In either case , " lcladdr " must have been fixed up to make
* sure the MSB isn ' t set using the S2DMA macro before passing
* the address to this routine .
*/
static int
do_plx_dma (
struct net_device * dev ,
ulong pciaddr ,
ulong lcladdr ,
int len ,
int to_host
)
{
int i ;
ulong csr = 0 ;
DGRS_PRIV * priv = ( DGRS_PRIV * ) dev - > priv ;
if ( pciaddr )
{
/*
* Do a single , non - chain DMA
*/
priv - > vplxdma [ PLX_DMA0_PCI_ADDR / 4 ] = pciaddr ;
priv - > vplxdma [ PLX_DMA0_LCL_ADDR / 4 ] = lcladdr ;
priv - > vplxdma [ PLX_DMA0_SIZE / 4 ] = len ;
priv - > vplxdma [ PLX_DMA0_DESCRIPTOR / 4 ] = to_host
? PLX_DMA_DESC_TO_HOST
: PLX_DMA_DESC_TO_BOARD ;
priv - > vplxdma [ PLX_DMA0_MODE / 4 ] =
PLX_DMA_MODE_WIDTH32
| PLX_DMA_MODE_WAITSTATES ( 0 )
| PLX_DMA_MODE_READY
| PLX_DMA_MODE_NOBTERM
| PLX_DMA_MODE_BURST
| PLX_DMA_MODE_NOCHAIN ;
}
else
{
/*
* Do a chaining DMA
*/
priv - > vplxdma [ PLX_DMA0_MODE / 4 ] =
PLX_DMA_MODE_WIDTH32
| PLX_DMA_MODE_WAITSTATES ( 0 )
| PLX_DMA_MODE_READY
| PLX_DMA_MODE_NOBTERM
| PLX_DMA_MODE_BURST
| PLX_DMA_MODE_CHAIN ;
priv - > vplxdma [ PLX_DMA0_DESCRIPTOR / 4 ] = lcladdr ;
}
priv - > vplxdma [ PLX_DMA_CSR / 4 ] =
PLX_DMA_CSR_0_ENABLE | PLX_DMA_CSR_0_START ;
/*
* Wait for DMA to complete
*/
for ( i = 0 ; i < 1000000 ; + + i )
{
/*
* Spin the host CPU for 1 usec , so we don ' t thrash
* the PCI bus while the PLX 9060 is doing DMA .
*/
udelay ( 1 ) ;
csr = ( volatile unsigned long ) priv - > vplxdma [ PLX_DMA_CSR / 4 ] ;
if ( csr & PLX_DMA_CSR_0_DONE )
break ;
}
if ( ! ( csr & PLX_DMA_CSR_0_DONE ) )
{
printk ( " %s: DMA done never occurred. DMA disabled. \n " ,
dev - > name ) ;
priv - > use_dma = 0 ;
return 1 ;
}
return 0 ;
}
/*
* dgrs_rcv_frame ( )
*
* Process a received frame . This is called from the interrupt
* routine , and works for both switch mode and multi - NIC mode .
*
* Note that when in multi - NIC mode , we want to always access the
* hardware using the dev and priv structures of the first port ,
* so that we are using only one set of variables to maintain
* the board interface status , but we want to use the Nth port
* dev and priv structures to maintain statistics and to pass
* the packet up .
*
* Only the first device structure is attached to the interrupt .
* We use the special " chan " variable at the end of the first RBD
* to select the Nth device in multi - NIC mode .
*
* We currently do chained DMA on a per - packet basis when the
* packet is " long " , and we spin the CPU a short time polling
* for DMA completion . This avoids a second interrupt overhead ,
* and gives the best performance for light traffic to the host .
*
* However , a better scheme that could be implemented would be
* to see how many packets are outstanding for the host , and if
* the number is " large " , create a long chain to DMA several
* packets into the host in one go . In this case , we would set
* up some state variables to let the host CPU continue doing
* other things until a DMA completion interrupt comes along .
*/
static void
dgrs_rcv_frame (
struct net_device * dev0 ,
DGRS_PRIV * priv0 ,
I596_CB * cbp
)
{
int len ;
I596_TBD * tbdp ;
struct sk_buff * skb ;
uchar * putp ;
uchar * p ;
struct net_device * devN ;
DGRS_PRIV * privN ;
/*
* Determine Nth priv and dev structure pointers
*/
if ( dgrs_nicmode )
{ /* Multi-NIC mode */
int chan = ( ( I596_RBD * ) S2H ( cbp - > xmit . tbdp ) ) - > chan ;
devN = priv0 - > devtbl [ chan - 1 ] ;
/*
* If devN is null , we got an interrupt before the I / F
* has been initialized . Pitch the packet .
*/
if ( devN = = NULL )
goto out ;
privN = ( DGRS_PRIV * ) devN - > priv ;
}
else
{ /* Switch mode */
devN = dev0 ;
privN = priv0 ;
}
if ( 0 ) printk ( " %s: rcv len=%ld \n " , devN - > name , cbp - > xmit . count ) ;
/*
* Allocate a message block big enough to hold the whole frame
*/
len = cbp - > xmit . count ;
if ( ( skb = dev_alloc_skb ( len + 5 ) ) = = NULL )
{
printk ( " %s: dev_alloc_skb failed for rcv buffer \n " , devN - > name ) ;
+ + privN - > stats . rx_dropped ;
/* discarding the frame */
goto out ;
}
skb - > dev = devN ;
skb_reserve ( skb , 2 ) ; /* Align IP header */
again :
putp = p = skb_put ( skb , len ) ;
/*
* There are three modes here for doing the packet copy .
* If we have DMA , and the packet is " long " , we use the
* chaining mode of DMA . If it ' s shorter , we use single
* DMA ' s . Otherwise , we use memcpy ( ) .
*/
if ( priv0 - > use_dma & & priv0 - > dmadesc_h & & len > 64 )
{
/*
* If we can use DMA and it ' s a long frame , copy it using
* DMA chaining .
*/
DMACHAIN * ddp_h ; /* Host virtual DMA desc. pointer */
DMACHAIN * ddp_s ; /* Switch physical DMA desc. pointer */
uchar * phys_p ;
/*
* Get the physical address of the STREAMS buffer .
* NOTE : allocb ( ) guarantees that the whole buffer
* is in a single page if the length < 4096.
*/
phys_p = ( uchar * ) virt_to_phys ( putp ) ;
ddp_h = priv0 - > dmadesc_h ;
ddp_s = priv0 - > dmadesc_s ;
tbdp = ( I596_TBD * ) S2H ( cbp - > xmit . tbdp ) ;
for ( ; ; )
{
int count ;
int amt ;
count = tbdp - > count ;
amt = count & 0x3fff ;
if ( amt = = 0 )
break ; /* For safety */
if ( ( p - putp ) > = len )
{
printk ( " %s: cbp = %lx \n " , devN - > name , ( long ) H2S ( cbp ) ) ;
proc_reset ( dev0 , 1 ) ; /* Freeze IDT */
break ; /* For Safety */
}
ddp_h - > pciaddr = ( ulong ) phys_p ;
ddp_h - > lcladdr = S2DMA ( tbdp - > buf ) ;
ddp_h - > len = amt ;
phys_p + = amt ;
p + = amt ;
if ( count & I596_TBD_EOF )
{
ddp_h - > next = PLX_DMA_DESC_TO_HOST
| PLX_DMA_DESC_EOC ;
+ + ddp_h ;
break ;
}
else
{
+ + ddp_s ;
ddp_h - > next = PLX_DMA_DESC_TO_HOST
| ( ulong ) ddp_s ;
tbdp = ( I596_TBD * ) S2H ( tbdp - > next ) ;
+ + ddp_h ;
}
}
if ( ddp_h - priv0 - > dmadesc_h )
{
int rc ;
rc = do_plx_dma ( dev0 ,
0 , ( ulong ) priv0 - > dmadesc_s , len , 0 ) ;
if ( rc )
{
printk ( " %s: Chained DMA failure \n " , devN - > name ) ;
goto again ;
}
}
}
else if ( priv0 - > use_dma )
{
/*
* If we can use DMA and it ' s a shorter frame , copy it
* using single DMA transfers .
*/
uchar * phys_p ;
/*
* Get the physical address of the STREAMS buffer .
* NOTE : allocb ( ) guarantees that the whole buffer
* is in a single page if the length < 4096.
*/
phys_p = ( uchar * ) virt_to_phys ( putp ) ;
tbdp = ( I596_TBD * ) S2H ( cbp - > xmit . tbdp ) ;
for ( ; ; )
{
int count ;
int amt ;
int rc ;
count = tbdp - > count ;
amt = count & 0x3fff ;
if ( amt = = 0 )
break ; /* For safety */
if ( ( p - putp ) > = len )
{
printk ( " %s: cbp = %lx \n " , devN - > name , ( long ) H2S ( cbp ) ) ;
proc_reset ( dev0 , 1 ) ; /* Freeze IDT */
break ; /* For Safety */
}
rc = do_plx_dma ( dev0 , ( ulong ) phys_p ,
S2DMA ( tbdp - > buf ) , amt , 1 ) ;
if ( rc )
{
memcpy ( p , S2H ( tbdp - > buf ) , amt ) ;
printk ( " %s: Single DMA failed \n " , devN - > name ) ;
}
phys_p + = amt ;
p + = amt ;
if ( count & I596_TBD_EOF )
break ;
tbdp = ( I596_TBD * ) S2H ( tbdp - > next ) ;
}
}
else
{
/*
* Otherwise , copy it piece by piece using memcpy ( )
*/
tbdp = ( I596_TBD * ) S2H ( cbp - > xmit . tbdp ) ;
for ( ; ; )
{
int count ;
int amt ;
count = tbdp - > count ;
amt = count & 0x3fff ;
if ( amt = = 0 )
break ; /* For safety */
if ( ( p - putp ) > = len )
{
printk ( " %s: cbp = %lx \n " , devN - > name , ( long ) H2S ( cbp ) ) ;
proc_reset ( dev0 , 1 ) ; /* Freeze IDT */
break ; /* For Safety */
}
memcpy ( p , S2H ( tbdp - > buf ) , amt ) ;
p + = amt ;
if ( count & I596_TBD_EOF )
break ;
tbdp = ( I596_TBD * ) S2H ( tbdp - > next ) ;
}
}
/*
* Pass the frame to upper half
*/
skb - > protocol = eth_type_trans ( skb , devN ) ;
netif_rx ( skb ) ;
devN - > last_rx = jiffies ;
+ + privN - > stats . rx_packets ;
privN - > stats . rx_bytes + = len ;
out :
cbp - > xmit . status = I596_CB_STATUS_C | I596_CB_STATUS_OK ;
}
/*
* Start transmission of a frame
*
* The interface to the board is simple : we pretend that we are
* a fifth 82596 ethernet controller ' receiving ' data , and copy the
* data into the same structures that a real 82596 would . This way ,
* the board firmware handles the host ' port ' the same as any other .
*
* NOTE : we do not use Bus master DMA for this routine . Turns out
* that it is not needed . Slave writes over the PCI bus are about
* as fast as DMA , due to the fact that the PLX part can do burst
* writes . The same is not true for data being read from the board .
*
* For multi - NIC mode , we tell the firmware the desired 82596
* output port by setting the special " dstchan " member at the
* end of the traditional 82596 RFD structure .
*/
static int dgrs_start_xmit ( struct sk_buff * skb , struct net_device * devN )
{
DGRS_PRIV * privN = ( DGRS_PRIV * ) devN - > priv ;
struct net_device * dev0 ;
DGRS_PRIV * priv0 ;
I596_RBD * rbdp ;
int count ;
int i , len , amt ;
/*
* Determine 0 th priv and dev structure pointers
*/
if ( dgrs_nicmode )
{
dev0 = privN - > devtbl [ 0 ] ;
priv0 = ( DGRS_PRIV * ) dev0 - > priv ;
}
else
{
dev0 = devN ;
priv0 = privN ;
}
if ( dgrs_debug > 1 )
printk ( " %s: xmit len=%d \n " , devN - > name , ( int ) skb - > len ) ;
devN - > trans_start = jiffies ;
netif_start_queue ( devN ) ;
if ( priv0 - > rfdp - > cmd & I596_RFD_EL )
{ /* Out of RFD's */
if ( 0 ) printk ( " %s: NO RFD's \n " , devN - > name ) ;
goto no_resources ;
}
rbdp = priv0 - > rbdp ;
count = 0 ;
priv0 - > rfdp - > rbdp = ( I596_RBD * ) H2S ( rbdp ) ;
i = 0 ; len = skb - > len ;
for ( ; ; )
{
if ( rbdp - > size & I596_RBD_EL )
{ /* Out of RBD's */
if ( 0 ) printk ( " %s: NO RBD's \n " , devN - > name ) ;
goto no_resources ;
}
amt = min_t ( unsigned int , len , rbdp - > size - count ) ;
memcpy ( ( char * ) S2H ( rbdp - > buf ) + count , skb - > data + i , amt ) ;
i + = amt ;
count + = amt ;
len - = amt ;
if ( len = = 0 )
{
if ( skb - > len < 60 )
rbdp - > count = 60 | I596_RBD_EOF ;
else
rbdp - > count = count | I596_RBD_EOF ;
rbdp = ( I596_RBD * ) S2H ( rbdp - > next ) ;
goto frame_done ;
}
else if ( count < 32 )
{
/* More data to come, but we used less than 32
* bytes of this RBD . Keep filling this RBD .
*/
{ } /* Yes, we do nothing here */
}
else
{
rbdp - > count = count ;
rbdp = ( I596_RBD * ) S2H ( rbdp - > next ) ;
count = 0 ;
}
}
frame_done :
priv0 - > rbdp = rbdp ;
if ( dgrs_nicmode )
priv0 - > rfdp - > dstchan = privN - > chan ;
priv0 - > rfdp - > status = I596_RFD_C | I596_RFD_OK ;
priv0 - > rfdp = ( I596_RFD * ) S2H ( priv0 - > rfdp - > next ) ;
+ + privN - > stats . tx_packets ;
dev_kfree_skb ( skb ) ;
return ( 0 ) ;
no_resources :
priv0 - > scbp - > status | = I596_SCB_RNR ; /* simulate I82596 */
return ( - EAGAIN ) ;
}
/*
* Open the interface
*/
static int
dgrs_open ( struct net_device * dev )
{
netif_start_queue ( dev ) ;
return ( 0 ) ;
}
/*
* Close the interface
*/
static int dgrs_close ( struct net_device * dev )
{
netif_stop_queue ( dev ) ;
return ( 0 ) ;
}
/*
* Get statistics
*/
static struct net_device_stats * dgrs_get_stats ( struct net_device * dev )
{
DGRS_PRIV * priv = ( DGRS_PRIV * ) dev - > priv ;
return ( & priv - > stats ) ;
}
/*
* Set multicast list and / or promiscuous mode
*/
static void dgrs_set_multicast_list ( struct net_device * dev )
{
DGRS_PRIV * priv = ( DGRS_PRIV * ) dev - > priv ;
priv - > port - > is_promisc = ( dev - > flags & IFF_PROMISC ) ? 1 : 0 ;
}
/*
* Unique ioctl ' s
*/
static int dgrs_ioctl ( struct net_device * devN , struct ifreq * ifr , int cmd )
{
DGRS_PRIV * privN = ( DGRS_PRIV * ) devN - > priv ;
DGRS_IOCTL ioc ;
int i ;
if ( cmd ! = DGRSIOCTL )
return - EINVAL ;
if ( copy_from_user ( & ioc , ifr - > ifr_data , sizeof ( DGRS_IOCTL ) ) )
return - EFAULT ;
switch ( ioc . cmd )
{
case DGRS_GETMEM :
if ( ioc . len ! = sizeof ( ulong ) )
return - EINVAL ;
if ( copy_to_user ( ioc . data , & devN - > mem_start , ioc . len ) )
return - EFAULT ;
return ( 0 ) ;
case DGRS_SETFILTER :
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
if ( ioc . port > privN - > bcomm - > bc_nports )
return - EINVAL ;
if ( ioc . filter > = NFILTERS )
return - EINVAL ;
if ( ioc . len > privN - > bcomm - > bc_filter_area_len )
return - EINVAL ;
/* Wait for old command to finish */
for ( i = 0 ; i < 1000 ; + + i )
{
if ( ( volatile long ) privN - > bcomm - > bc_filter_cmd < = 0 )
break ;
udelay ( 1 ) ;
}
if ( i > = 1000 )
return - EIO ;
privN - > bcomm - > bc_filter_port = ioc . port ;
privN - > bcomm - > bc_filter_num = ioc . filter ;
privN - > bcomm - > bc_filter_len = ioc . len ;
if ( ioc . len )
{
if ( copy_from_user ( S2HN ( privN - > bcomm - > bc_filter_area ) ,
ioc . data , ioc . len ) )
return - EFAULT ;
privN - > bcomm - > bc_filter_cmd = BC_FILTER_SET ;
}
else
privN - > bcomm - > bc_filter_cmd = BC_FILTER_CLR ;
return ( 0 ) ;
default :
return - EOPNOTSUPP ;
}
}
/*
* Process interrupts
*
* dev , priv will always refer to the 0 th device in Multi - NIC mode .
*/
static irqreturn_t dgrs_intr ( int irq , void * dev_id , struct pt_regs * regs )
{
struct net_device * dev0 = ( struct net_device * ) dev_id ;
DGRS_PRIV * priv0 = ( DGRS_PRIV * ) dev0 - > priv ;
I596_CB * cbp ;
int cmd ;
int i ;
+ + priv0 - > intrcnt ;
if ( 1 ) + + priv0 - > bcomm - > bc_cnt [ 4 ] ;
if ( 0 )
{
static int cnt = 100 ;
if ( - - cnt > 0 )
printk ( " %s: interrupt: irq %d \n " , dev0 - > name , irq ) ;
}
/*
* Get 596 command
*/
cmd = priv0 - > scbp - > cmd ;
/*
* See if RU has been restarted
*/
if ( ( cmd & I596_SCB_RUC ) = = I596_SCB_RUC_START )
{
if ( 0 ) printk ( " %s: RUC start \n " , dev0 - > name ) ;
priv0 - > rfdp = ( I596_RFD * ) S2H ( priv0 - > scbp - > rfdp ) ;
priv0 - > rbdp = ( I596_RBD * ) S2H ( priv0 - > rfdp - > rbdp ) ;
priv0 - > scbp - > status & = ~ ( I596_SCB_RNR | I596_SCB_RUS ) ;
/*
* Tell upper half ( halves )
*/
if ( dgrs_nicmode )
{
for ( i = 0 ; i < priv0 - > nports ; + + i )
netif_wake_queue ( priv0 - > devtbl [ i ] ) ;
}
else
netif_wake_queue ( dev0 ) ;
/* if (bd->flags & TX_QUEUED)
DL_sched ( bd , bdd ) ; */
}
/*
* See if any CU commands to process
*/
if ( ( cmd & I596_SCB_CUC ) ! = I596_SCB_CUC_START )
{
priv0 - > scbp - > cmd = 0 ; /* Ignore all other commands */
goto ack_intr ;
}
priv0 - > scbp - > status & = ~ ( I596_SCB_CNA | I596_SCB_CUS ) ;
/*
* Process a command
*/
cbp = ( I596_CB * ) S2H ( priv0 - > scbp - > cbp ) ;
priv0 - > scbp - > cmd = 0 ; /* Safe to clear the command */
for ( ; ; )
{
switch ( cbp - > nop . cmd & I596_CB_CMD )
{
case I596_CB_CMD_XMIT :
dgrs_rcv_frame ( dev0 , priv0 , cbp ) ;
break ;
default :
cbp - > nop . status = I596_CB_STATUS_C | I596_CB_STATUS_OK ;
break ;
}
if ( cbp - > nop . cmd & I596_CB_CMD_EL )
break ;
cbp = ( I596_CB * ) S2H ( cbp - > nop . next ) ;
}
priv0 - > scbp - > status | = I596_SCB_CNA ;
/*
* Ack the interrupt
*/
ack_intr :
if ( priv0 - > plxreg )
OUTL ( dev0 - > base_addr + PLX_LCL2PCI_DOORBELL , 1 ) ;
return IRQ_HANDLED ;
}
/*
* Download the board firmware
*/
static int __init
dgrs_download ( struct net_device * dev0 )
{
DGRS_PRIV * priv0 = ( DGRS_PRIV * ) dev0 - > priv ;
int is ;
unsigned long i ;
2006-03-04 05:33:57 +03:00
static const int iv2is [ 16 ] = {
2005-04-17 02:20:36 +04:00
0 , 0 , 0 , ES4H_IS_INT3 ,
0 , ES4H_IS_INT5 , 0 , ES4H_IS_INT7 ,
0 , 0 , ES4H_IS_INT10 , ES4H_IS_INT11 ,
ES4H_IS_INT12 , 0 , 0 , ES4H_IS_INT15 } ;
/*
* Map in the dual port memory
*/
priv0 - > vmem = ioremap ( dev0 - > mem_start , 2048 * 1024 ) ;
if ( ! priv0 - > vmem )
{
printk ( " %s: cannot map in board memory \n " , dev0 - > name ) ;
return - ENXIO ;
}
/*
* Hold the processor and configure the board addresses
*/
if ( priv0 - > plxreg )
{ /* PCI bus */
proc_reset ( dev0 , 1 ) ;
}
else
{ /* EISA bus */
is = iv2is [ dev0 - > irq & 0x0f ] ;
if ( ! is )
{
printk ( " %s: Illegal IRQ %d \n " , dev0 - > name , dev0 - > irq ) ;
iounmap ( priv0 - > vmem ) ;
priv0 - > vmem = NULL ;
return - ENXIO ;
}
OUTB ( dev0 - > base_addr + ES4H_AS_31_24 ,
( uchar ) ( dev0 - > mem_start > > 24 ) ) ;
OUTB ( dev0 - > base_addr + ES4H_AS_23_16 ,
( uchar ) ( dev0 - > mem_start > > 16 ) ) ;
priv0 - > is_reg = ES4H_IS_LINEAR | is |
( ( uchar ) ( dev0 - > mem_start > > 8 ) & ES4H_IS_AS15 ) ;
OUTB ( dev0 - > base_addr + ES4H_IS , priv0 - > is_reg ) ;
OUTB ( dev0 - > base_addr + ES4H_EC , ES4H_EC_ENABLE ) ;
OUTB ( dev0 - > base_addr + ES4H_PC , ES4H_PC_RESET ) ;
OUTB ( dev0 - > base_addr + ES4H_MW , ES4H_MW_ENABLE | 0x00 ) ;
}
/*
* See if we can do DMA on the SE - 6
*/
priv0 - > use_dma = check_board_dma ( dev0 ) ;
if ( priv0 - > use_dma )
printk ( " %s: Bus Master DMA is enabled. \n " , dev0 - > name ) ;
/*
* Load and verify the code at the desired address
*/
memcpy ( priv0 - > vmem , dgrs_code , dgrs_ncode ) ; /* Load code */
if ( memcmp ( priv0 - > vmem , dgrs_code , dgrs_ncode ) )
{
iounmap ( priv0 - > vmem ) ;
priv0 - > vmem = NULL ;
printk ( " %s: download compare failed \n " , dev0 - > name ) ;
return - ENXIO ;
}
/*
* Configurables
*/
priv0 - > bcomm = ( struct bios_comm * ) ( priv0 - > vmem + 0x0100 ) ;
priv0 - > bcomm - > bc_nowait = 1 ; /* Tell board to make printf not wait */
priv0 - > bcomm - > bc_squelch = 0 ; /* Flag from Space.c */
priv0 - > bcomm - > bc_150ohm = 0 ; /* Flag from Space.c */
priv0 - > bcomm - > bc_spew = 0 ; /* Debug flag from Space.c */
priv0 - > bcomm - > bc_maxrfd = 0 ; /* Debug flag from Space.c */
priv0 - > bcomm - > bc_maxrbd = 0 ; /* Debug flag from Space.c */
/*
* Tell board we are operating in switch mode ( 1 ) or in
* multi - NIC mode ( 2 ) .
*/
priv0 - > bcomm - > bc_host = dgrs_nicmode ? BC_MULTINIC : BC_SWITCH ;
/*
* Request memory space on board for DMA chains
*/
if ( priv0 - > use_dma )
priv0 - > bcomm - > bc_hostarea_len = ( 2048 / 64 ) * 16 ;
/*
* NVRAM configurables from Space . c
*/
priv0 - > bcomm - > bc_spantree = dgrs_spantree ;
priv0 - > bcomm - > bc_hashexpire = dgrs_hashexpire ;
memcpy ( priv0 - > bcomm - > bc_ipaddr , dgrs_ipaddr , 4 ) ;
memcpy ( priv0 - > bcomm - > bc_iptrap , dgrs_iptrap , 4 ) ;
memcpy ( priv0 - > bcomm - > bc_ipxnet , & dgrs_ipxnet , 4 ) ;
/*
* Release processor , wait 8 seconds for board to initialize
*/
proc_reset ( dev0 , 0 ) ;
for ( i = jiffies + 8 * HZ ; time_after ( i , jiffies ) ; )
{
barrier ( ) ; /* Gcc 2.95 needs this */
if ( priv0 - > bcomm - > bc_status > = BC_RUN )
break ;
}
if ( priv0 - > bcomm - > bc_status < BC_RUN )
{
printk ( " %s: board not operating \n " , dev0 - > name ) ;
iounmap ( priv0 - > vmem ) ;
priv0 - > vmem = NULL ;
return - ENXIO ;
}
priv0 - > port = ( PORT * ) S2H ( priv0 - > bcomm - > bc_port ) ;
priv0 - > scbp = ( I596_SCB * ) S2H ( priv0 - > port - > scbp ) ;
priv0 - > rfdp = ( I596_RFD * ) S2H ( priv0 - > scbp - > rfdp ) ;
priv0 - > rbdp = ( I596_RBD * ) S2H ( priv0 - > rfdp - > rbdp ) ;
priv0 - > scbp - > status = I596_SCB_CNA ; /* CU is idle */
/*
* Get switch physical and host virtual pointers to DMA
* chaining area . NOTE : the MSB of the switch physical
* address * must * be turned off . Otherwise , the HW kludge
* that allows host access of the PLX DMA registers will
* erroneously select the PLX registers .
*/
priv0 - > dmadesc_s = ( DMACHAIN * ) S2DMA ( priv0 - > bcomm - > bc_hostarea ) ;
if ( priv0 - > dmadesc_s )
priv0 - > dmadesc_h = ( DMACHAIN * ) S2H ( priv0 - > dmadesc_s ) ;
else
priv0 - > dmadesc_h = NULL ;
/*
* Enable board interrupts
*/
if ( priv0 - > plxreg )
{ /* PCI bus */
OUTL ( dev0 - > base_addr + PLX_INT_CSR ,
inl ( dev0 - > base_addr + PLX_INT_CSR )
| PLX_PCI_DOORBELL_IE ) ; /* Enable intr to host */
OUTL ( dev0 - > base_addr + PLX_LCL2PCI_DOORBELL , 1 ) ;
}
else
{ /* EISA bus */
}
return ( 0 ) ;
}
/*
* Probe ( init ) a board
*/
static int __init
dgrs_probe1 ( struct net_device * dev )
{
DGRS_PRIV * priv = ( DGRS_PRIV * ) dev - > priv ;
unsigned long i ;
int rc ;
printk ( " %s: Digi RightSwitch io=%lx mem=%lx irq=%d plx=%lx dma=%lx \n " ,
dev - > name , dev - > base_addr , dev - > mem_start , dev - > irq ,
priv - > plxreg , priv - > plxdma ) ;
/*
* Download the firmware and light the processor
*/
rc = dgrs_download ( dev ) ;
if ( rc )
goto err_out ;
/*
* Get ether address of board
*/
printk ( " %s: Ethernet address " , dev - > name ) ;
memcpy ( dev - > dev_addr , priv - > port - > ethaddr , 6 ) ;
for ( i = 0 ; i < 6 ; + + i )
printk ( " %c%2.2x " , i ? ' : ' : ' ' , dev - > dev_addr [ i ] ) ;
printk ( " \n " ) ;
if ( dev - > dev_addr [ 0 ] & 1 )
{
printk ( " %s: Illegal Ethernet Address \n " , dev - > name ) ;
rc = - ENXIO ;
goto err_out ;
}
/*
* ACK outstanding interrupts , hook the interrupt ,
* and verify that we are getting interrupts from the board .
*/
if ( priv - > plxreg )
OUTL ( dev - > base_addr + PLX_LCL2PCI_DOORBELL , 1 ) ;
rc = request_irq ( dev - > irq , & dgrs_intr , SA_SHIRQ , " RightSwitch " , dev ) ;
if ( rc )
goto err_out ;
priv - > intrcnt = 0 ;
for ( i = jiffies + 2 * HZ + HZ / 2 ; time_after ( i , jiffies ) ; )
{
cpu_relax ( ) ;
if ( priv - > intrcnt > = 2 )
break ;
}
if ( priv - > intrcnt < 2 )
{
printk ( KERN_ERR " %s: Not interrupting on IRQ %d (%d) \n " ,
dev - > name , dev - > irq , priv - > intrcnt ) ;
rc = - ENXIO ;
goto err_free_irq ;
}
/*
* Entry points . . .
*/
dev - > open = & dgrs_open ;
dev - > stop = & dgrs_close ;
dev - > get_stats = & dgrs_get_stats ;
dev - > hard_start_xmit = & dgrs_start_xmit ;
dev - > set_multicast_list = & dgrs_set_multicast_list ;
dev - > do_ioctl = & dgrs_ioctl ;
return rc ;
err_free_irq :
free_irq ( dev - > irq , dev ) ;
err_out :
return rc ;
}
static int __init
dgrs_initclone ( struct net_device * dev )
{
DGRS_PRIV * priv = ( DGRS_PRIV * ) dev - > priv ;
int i ;
printk ( " %s: Digi RightSwitch port %d " ,
dev - > name , priv - > chan ) ;
for ( i = 0 ; i < 6 ; + + i )
printk ( " %c%2.2x " , i ? ' : ' : ' ' , dev - > dev_addr [ i ] ) ;
printk ( " \n " ) ;
return ( 0 ) ;
}
static struct net_device * __init
dgrs_found_device (
int io ,
ulong mem ,
int irq ,
ulong plxreg ,
ulong plxdma ,
struct device * pdev
)
{
DGRS_PRIV * priv ;
struct net_device * dev ;
int i , ret = - ENOMEM ;
dev = alloc_etherdev ( sizeof ( DGRS_PRIV ) ) ;
if ( ! dev )
goto err0 ;
priv = ( DGRS_PRIV * ) dev - > priv ;
dev - > base_addr = io ;
dev - > mem_start = mem ;
dev - > mem_end = mem + 2048 * 1024 - 1 ;
dev - > irq = irq ;
priv - > plxreg = plxreg ;
priv - > plxdma = plxdma ;
priv - > vplxdma = NULL ;
priv - > chan = 1 ;
priv - > devtbl [ 0 ] = dev ;
SET_MODULE_OWNER ( dev ) ;
SET_NETDEV_DEV ( dev , pdev ) ;
ret = dgrs_probe1 ( dev ) ;
if ( ret )
goto err1 ;
ret = register_netdev ( dev ) ;
if ( ret )
goto err2 ;
if ( ! dgrs_nicmode )
return dev ; /* Switch mode, we are done */
/*
* Operating card as N separate NICs
*/
priv - > nports = priv - > bcomm - > bc_nports ;
for ( i = 1 ; i < priv - > nports ; + + i )
{
struct net_device * devN ;
DGRS_PRIV * privN ;
/* Allocate new dev and priv structures */
devN = alloc_etherdev ( sizeof ( DGRS_PRIV ) ) ;
ret = - ENOMEM ;
if ( ! devN )
goto fail ;
/* Don't copy the network device structure! */
/* copy the priv structure of dev[0] */
privN = ( DGRS_PRIV * ) devN - > priv ;
* privN = * priv ;
/* ... and zero out VM areas */
privN - > vmem = NULL ;
privN - > vplxdma = NULL ;
/* ... and zero out IRQ */
devN - > irq = 0 ;
/* ... and base MAC address off address of 1st port */
devN - > dev_addr [ 5 ] + = i ;
ret = dgrs_initclone ( devN ) ;
if ( ret )
goto fail ;
SET_MODULE_OWNER ( devN ) ;
SET_NETDEV_DEV ( dev , pdev ) ;
ret = register_netdev ( devN ) ;
if ( ret ) {
free_netdev ( devN ) ;
goto fail ;
}
privN - > chan = i + 1 ;
priv - > devtbl [ i ] = devN ;
}
return dev ;
fail :
while ( i > = 0 ) {
struct net_device * d = priv - > devtbl [ i - - ] ;
unregister_netdev ( d ) ;
free_netdev ( d ) ;
}
err2 :
free_irq ( dev - > irq , dev ) ;
err1 :
free_netdev ( dev ) ;
err0 :
return ERR_PTR ( ret ) ;
}
static void __devexit dgrs_remove ( struct net_device * dev )
{
DGRS_PRIV * priv = dev - > priv ;
int i ;
unregister_netdev ( dev ) ;
for ( i = 1 ; i < priv - > nports ; + + i ) {
struct net_device * d = priv - > devtbl [ i ] ;
if ( d ) {
unregister_netdev ( d ) ;
free_netdev ( d ) ;
}
}
proc_reset ( priv - > devtbl [ 0 ] , 1 ) ;
if ( priv - > vmem )
iounmap ( priv - > vmem ) ;
if ( priv - > vplxdma )
iounmap ( ( uchar * ) priv - > vplxdma ) ;
if ( dev - > irq )
free_irq ( dev - > irq , dev ) ;
for ( i = 1 ; i < priv - > nports ; + + i ) {
if ( priv - > devtbl [ i ] )
unregister_netdev ( priv - > devtbl [ i ] ) ;
}
}
# ifdef CONFIG_PCI
static int __init dgrs_pci_probe ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
{
struct net_device * dev ;
int err ;
uint io ;
uint mem ;
uint irq ;
uint plxreg ;
uint plxdma ;
/*
* Get and check the bus - master and latency values .
* Some PCI BIOSes fail to set the master - enable bit ,
* and the latency timer must be set to the maximum
* value to avoid data corruption that occurs when the
* timer expires during a transfer . Yes , it ' s a bug .
*/
err = pci_enable_device ( pdev ) ;
if ( err )
return err ;
err = pci_request_regions ( pdev , " RightSwitch " ) ;
if ( err )
return err ;
pci_set_master ( pdev ) ;
plxreg = pci_resource_start ( pdev , 0 ) ;
io = pci_resource_start ( pdev , 1 ) ;
mem = pci_resource_start ( pdev , 2 ) ;
pci_read_config_dword ( pdev , 0x30 , & plxdma ) ;
irq = pdev - > irq ;
plxdma & = ~ 15 ;
/*
* On some BIOSES , the PLX " expansion rom " ( used for DMA )
* address comes up as " 0 " . This is probably because
* the BIOS doesn ' t see a valid 55 AA ROM signature at
* the " ROM " start and zeroes the address . To get
* around this problem the SE - 6 is configured to ask
* for 4 MB of space for the dual port memory . We then
* must set its range back to 2 MB , and use the upper
* half for DMA register access
*/
OUTL ( io + PLX_SPACE0_RANGE , 0xFFE00000L ) ;
if ( plxdma = = 0 )
plxdma = mem + ( 2048L * 1024L ) ;
pci_write_config_dword ( pdev , 0x30 , plxdma + 1 ) ;
pci_read_config_dword ( pdev , 0x30 , & plxdma ) ;
plxdma & = ~ 15 ;
dev = dgrs_found_device ( io , mem , irq , plxreg , plxdma , & pdev - > dev ) ;
if ( IS_ERR ( dev ) ) {
pci_release_regions ( pdev ) ;
return PTR_ERR ( dev ) ;
}
pci_set_drvdata ( pdev , dev ) ;
return 0 ;
}
static void __devexit dgrs_pci_remove ( struct pci_dev * pdev )
{
struct net_device * dev = pci_get_drvdata ( pdev ) ;
dgrs_remove ( dev ) ;
pci_release_regions ( pdev ) ;
free_netdev ( dev ) ;
}
static struct pci_driver dgrs_pci_driver = {
. name = " dgrs " ,
. id_table = dgrs_pci_tbl ,
. probe = dgrs_pci_probe ,
. remove = __devexit_p ( dgrs_pci_remove ) ,
} ;
2005-11-22 08:32:03 +03:00
# else
static struct pci_driver dgrs_pci_driver = { } ;
2005-04-17 02:20:36 +04:00
# endif
# ifdef CONFIG_EISA
static int is2iv [ 8 ] __initdata = { 0 , 3 , 5 , 7 , 10 , 11 , 12 , 15 } ;
static int __init dgrs_eisa_probe ( struct device * gendev )
{
struct net_device * dev ;
struct eisa_device * edev = to_eisa_device ( gendev ) ;
uint io = edev - > base_addr ;
uint mem ;
uint irq ;
int rc = - ENODEV ; /* Not EISA configured */
if ( ! request_region ( io , 256 , " RightSwitch " ) ) {
printk ( KERN_ERR " dgrs: eisa io 0x%x, which is busy. \n " , io ) ;
return - EBUSY ;
}
if ( ! ( inb ( io + ES4H_EC ) & ES4H_EC_ENABLE ) )
goto err_out ;
mem = ( inb ( io + ES4H_AS_31_24 ) < < 24 )
+ ( inb ( io + ES4H_AS_23_16 ) < < 16 ) ;
irq = is2iv [ inb ( io + ES4H_IS ) & ES4H_IS_INTMASK ] ;
dev = dgrs_found_device ( io , mem , irq , 0L , 0L , gendev ) ;
if ( IS_ERR ( dev ) ) {
rc = PTR_ERR ( dev ) ;
goto err_out ;
}
gendev - > driver_data = dev ;
return 0 ;
err_out :
release_region ( io , 256 ) ;
return rc ;
}
static int __devexit dgrs_eisa_remove ( struct device * gendev )
{
struct net_device * dev = gendev - > driver_data ;
dgrs_remove ( dev ) ;
release_region ( dev - > base_addr , 256 ) ;
free_netdev ( dev ) ;
return 0 ;
}
static struct eisa_driver dgrs_eisa_driver = {
. id_table = dgrs_eisa_tbl ,
. driver = {
. name = " dgrs " ,
. probe = dgrs_eisa_probe ,
. remove = __devexit_p ( dgrs_eisa_remove ) ,
}
} ;
# endif
/*
* Variables that can be overriden from module command line
*/
static int debug = - 1 ;
static int dma = - 1 ;
static int hashexpire = - 1 ;
static int spantree = - 1 ;
static int ipaddr [ 4 ] = { - 1 } ;
static int iptrap [ 4 ] = { - 1 } ;
static __u32 ipxnet = - 1 ;
static int nicmode = - 1 ;
module_param ( debug , int , 0 ) ;
module_param ( dma , int , 0 ) ;
module_param ( hashexpire , int , 0 ) ;
module_param ( spantree , int , 0 ) ;
module_param_array ( ipaddr , int , NULL , 0 ) ;
module_param_array ( iptrap , int , NULL , 0 ) ;
module_param ( ipxnet , int , 0 ) ;
module_param ( nicmode , int , 0 ) ;
MODULE_PARM_DESC ( debug , " Digi RightSwitch enable debugging (0-1) " ) ;
MODULE_PARM_DESC ( dma , " Digi RightSwitch enable BM DMA (0-1) " ) ;
MODULE_PARM_DESC ( nicmode , " Digi RightSwitch operating mode (1: switch, 2: multi-NIC) " ) ;
static int __init dgrs_init_module ( void )
{
int i ;
2006-03-25 14:07:19 +03:00
int err ;
2005-04-17 02:20:36 +04:00
/*
* Command line variable overrides
* debug = NNN
* dma = 0 / 1
* spantree = 0 / 1
* hashexpire = NNN
* ipaddr = A , B , C , D
* iptrap = A , B , C , D
* ipxnet = NNN
* nicmode = NNN
*/
if ( debug > = 0 )
dgrs_debug = debug ;
if ( dma > = 0 )
dgrs_dma = dma ;
if ( nicmode > = 0 )
dgrs_nicmode = nicmode ;
if ( hashexpire > = 0 )
dgrs_hashexpire = hashexpire ;
if ( spantree > = 0 )
dgrs_spantree = spantree ;
if ( ipaddr [ 0 ] ! = - 1 )
for ( i = 0 ; i < 4 ; + + i )
dgrs_ipaddr [ i ] = ipaddr [ i ] ;
if ( iptrap [ 0 ] ! = - 1 )
for ( i = 0 ; i < 4 ; + + i )
dgrs_iptrap [ i ] = iptrap [ i ] ;
if ( ipxnet ! = - 1 )
dgrs_ipxnet = htonl ( ipxnet ) ;
if ( dgrs_debug )
{
printk ( KERN_INFO " dgrs: SW=%s FW=Build %d %s \n FW Version=%s \n " ,
version , dgrs_firmnum , dgrs_firmdate , dgrs_firmver ) ;
}
/*
* Find and configure all the cards
*/
# ifdef CONFIG_EISA
2006-03-25 14:07:19 +03:00
err = eisa_driver_register ( & dgrs_eisa_driver ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
# endif
2006-03-25 14:07:19 +03:00
err = pci_register_driver ( & dgrs_pci_driver ) ;
if ( err )
return err ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void __exit dgrs_cleanup_module ( void )
{
# ifdef CONFIG_EISA
eisa_driver_unregister ( & dgrs_eisa_driver ) ;
# endif
# ifdef CONFIG_PCI
pci_unregister_driver ( & dgrs_pci_driver ) ;
# endif
}
module_init ( dgrs_init_module ) ;
module_exit ( dgrs_cleanup_module ) ;