2005-04-17 02:20:36 +04:00
/*
* 7990. c - - LANCE ethernet IC generic routines .
* This is an attempt to separate out the bits of various ethernet
* drivers that are common because they all use the AMD 7990 LANCE
* ( Local Area Network Controller for Ethernet ) chip .
*
* Copyright ( C ) 05 / 1998 Peter Maydell < pmaydell @ chiark . greenend . org . uk >
*
* Most of this stuff was obtained by looking at other LANCE drivers ,
* in particular a2065 . [ ch ] . The AMD C - LANCE datasheet was also helpful .
* NB : this was made easy by the fact that Jes Sorensen had cleaned up
* most of a2025 and sunlance with the aim of merging them , so the
* common code was pretty obvious .
*/
# include <linux/crc32.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/fcntl.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
# include <linux/in.h>
# include <linux/route.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/skbuff.h>
2005-12-01 14:44:55 +03:00
# include <asm/irq.h>
2005-04-17 02:20:36 +04:00
/* Used for the temporal inet entries and routing */
# include <linux/socket.h>
# include <linux/bitops.h>
# include <asm/system.h>
# include <asm/io.h>
# include <asm/dma.h>
# include <asm/pgtable.h>
# ifdef CONFIG_HP300
# include <asm/blinken.h>
# endif
# include "7990.h"
# define WRITERAP(lp,x) out_be16(lp->base + LANCE_RAP, (x))
# define WRITERDP(lp,x) out_be16(lp->base + LANCE_RDP, (x))
# define READRDP(lp) in_be16(lp->base + LANCE_RDP)
# if defined(CONFIG_HPLANCE) || defined(CONFIG_HPLANCE_MODULE)
# include "hplance.h"
# undef WRITERAP
# undef WRITERDP
# undef READRDP
# if defined(CONFIG_MVME147_NET) || defined(CONFIG_MVME147_NET_MODULE)
/* Lossage Factor Nine, Mr Sulu. */
# define WRITERAP(lp,x) (lp->writerap(lp,x))
# define WRITERDP(lp,x) (lp->writerdp(lp,x))
# define READRDP(lp) (lp->readrdp(lp))
# else
/* These inlines can be used if only CONFIG_HPLANCE is defined */
static inline void WRITERAP ( struct lance_private * lp , __u16 value )
{
do {
out_be16 ( lp - > base + HPLANCE_REGOFF + LANCE_RAP , value ) ;
} while ( ( in_8 ( lp - > base + HPLANCE_STATUS ) & LE_ACK ) = = 0 ) ;
}
static inline void WRITERDP ( struct lance_private * lp , __u16 value )
{
do {
out_be16 ( lp - > base + HPLANCE_REGOFF + LANCE_RDP , value ) ;
} while ( ( in_8 ( lp - > base + HPLANCE_STATUS ) & LE_ACK ) = = 0 ) ;
}
static inline __u16 READRDP ( struct lance_private * lp )
{
__u16 value ;
do {
value = in_be16 ( lp - > base + HPLANCE_REGOFF + LANCE_RDP ) ;
} while ( ( in_8 ( lp - > base + HPLANCE_STATUS ) & LE_ACK ) = = 0 ) ;
return value ;
}
# endif
# endif /* CONFIG_HPLANCE || CONFIG_HPLANCE_MODULE */
/* debugging output macros, various flavours */
/* #define TEST_HITS */
# ifdef UNDEF
# define PRINT_RINGS() \
do { \
int t ; \
for ( t = 0 ; t < RX_RING_SIZE ; t + + ) { \
printk ( " R%d: @(%02X %04X) len %04X, mblen %04X, bits %02X \n " , \
t , ib - > brx_ring [ t ] . rmd1_hadr , ib - > brx_ring [ t ] . rmd0 , \
ib - > brx_ring [ t ] . length , \
ib - > brx_ring [ t ] . mblength , ib - > brx_ring [ t ] . rmd1_bits ) ; \
} \
for ( t = 0 ; t < TX_RING_SIZE ; t + + ) { \
printk ( " T%d: @(%02X %04X) len %04X, misc %04X, bits %02X \n " , \
t , ib - > btx_ring [ t ] . tmd1_hadr , ib - > btx_ring [ t ] . tmd0 , \
ib - > btx_ring [ t ] . length , \
ib - > btx_ring [ t ] . misc , ib - > btx_ring [ t ] . tmd1_bits ) ; \
} \
} while ( 0 )
# else
# define PRINT_RINGS()
# endif
/* Load the CSR registers. The LANCE has to be STOPped when we do this! */
static void load_csrs ( struct lance_private * lp )
{
volatile struct lance_init_block * aib = lp - > lance_init_block ;
int leptr ;
leptr = LANCE_ADDR ( aib ) ;
WRITERAP ( lp , LE_CSR1 ) ; /* load address of init block */
WRITERDP ( lp , leptr & 0xFFFF ) ;
WRITERAP ( lp , LE_CSR2 ) ;
WRITERDP ( lp , leptr > > 16 ) ;
WRITERAP ( lp , LE_CSR3 ) ;
WRITERDP ( lp , lp - > busmaster_regval ) ; /* set byteswap/ALEctrl/byte ctrl */
/* Point back to csr0 */
WRITERAP ( lp , LE_CSR0 ) ;
}
/* #define to 0 or 1 appropriately */
# define DEBUG_IRING 0
/* Set up the Lance Rx and Tx rings and the init block */
static void lance_init_ring ( struct net_device * dev )
{
struct lance_private * lp = netdev_priv ( dev ) ;
volatile struct lance_init_block * ib = lp - > init_block ;
volatile struct lance_init_block * aib ; /* for LANCE_ADDR computations */
int leptr ;
int i ;
aib = lp - > lance_init_block ;
lp - > rx_new = lp - > tx_new = 0 ;
lp - > rx_old = lp - > tx_old = 0 ;
ib - > mode = LE_MO_PROM ; /* normal, enable Tx & Rx */
/* Copy the ethernet address to the lance init block
* Notice that we do a byteswap if we ' re big endian .
* [ I think this is the right criterion ; at least , sunlance ,
* a2065 and atarilance do the byteswap and lance . c ( PC ) doesn ' t .
* However , the datasheet says that the BSWAP bit doesn ' t affect
* the init block , so surely it should be low byte first for
* everybody ? Um . ]
* We could define the ib - > physaddr as three 16 bit values and
* use ( addr [ 1 ] < < 8 ) | addr [ 0 ] & co , but this is more efficient .
*/
# ifdef __BIG_ENDIAN
ib - > phys_addr [ 0 ] = dev - > dev_addr [ 1 ] ;
ib - > phys_addr [ 1 ] = dev - > dev_addr [ 0 ] ;
ib - > phys_addr [ 2 ] = dev - > dev_addr [ 3 ] ;
ib - > phys_addr [ 3 ] = dev - > dev_addr [ 2 ] ;
ib - > phys_addr [ 4 ] = dev - > dev_addr [ 5 ] ;
ib - > phys_addr [ 5 ] = dev - > dev_addr [ 4 ] ;
# else
for ( i = 0 ; i < 6 ; i + + )
ib - > phys_addr [ i ] = dev - > dev_addr [ i ] ;
# endif
if ( DEBUG_IRING )
printk ( " TX rings: \n " ) ;
lp - > tx_full = 0 ;
/* Setup the Tx ring entries */
for ( i = 0 ; i < ( 1 < < lp - > lance_log_tx_bufs ) ; i + + ) {
leptr = LANCE_ADDR ( & aib - > tx_buf [ i ] [ 0 ] ) ;
ib - > btx_ring [ i ] . tmd0 = leptr ;
ib - > btx_ring [ i ] . tmd1_hadr = leptr > > 16 ;
ib - > btx_ring [ i ] . tmd1_bits = 0 ;
ib - > btx_ring [ i ] . length = 0xf000 ; /* The ones required by tmd2 */
ib - > btx_ring [ i ] . misc = 0 ;
if ( DEBUG_IRING )
printk ( " %d: 0x%8.8x \n " , i , leptr ) ;
}
/* Setup the Rx ring entries */
if ( DEBUG_IRING )
printk ( " RX rings: \n " ) ;
for ( i = 0 ; i < ( 1 < < lp - > lance_log_rx_bufs ) ; i + + ) {
leptr = LANCE_ADDR ( & aib - > rx_buf [ i ] [ 0 ] ) ;
ib - > brx_ring [ i ] . rmd0 = leptr ;
ib - > brx_ring [ i ] . rmd1_hadr = leptr > > 16 ;
ib - > brx_ring [ i ] . rmd1_bits = LE_R1_OWN ;
/* 0xf000 == bits that must be one (reserved, presumably) */
ib - > brx_ring [ i ] . length = - RX_BUFF_SIZE | 0xf000 ;
ib - > brx_ring [ i ] . mblength = 0 ;
if ( DEBUG_IRING )
printk ( " %d: 0x%8.8x \n " , i , leptr ) ;
}
/* Setup the initialization block */
/* Setup rx descriptor pointer */
leptr = LANCE_ADDR ( & aib - > brx_ring ) ;
ib - > rx_len = ( lp - > lance_log_rx_bufs < < 13 ) | ( leptr > > 16 ) ;
ib - > rx_ptr = leptr ;
if ( DEBUG_IRING )
printk ( " RX ptr: %8.8x \n " , leptr ) ;
/* Setup tx descriptor pointer */
leptr = LANCE_ADDR ( & aib - > btx_ring ) ;
ib - > tx_len = ( lp - > lance_log_tx_bufs < < 13 ) | ( leptr > > 16 ) ;
ib - > tx_ptr = leptr ;
if ( DEBUG_IRING )
printk ( " TX ptr: %8.8x \n " , leptr ) ;
/* Clear the multicast filter */
ib - > filter [ 0 ] = 0 ;
ib - > filter [ 1 ] = 0 ;
PRINT_RINGS ( ) ;
}
/* LANCE must be STOPped before we do this, too... */
static int init_restart_lance ( struct lance_private * lp )
{
int i ;
WRITERAP ( lp , LE_CSR0 ) ;
WRITERDP ( lp , LE_C0_INIT ) ;
/* Need a hook here for sunlance ledma stuff */
/* Wait for the lance to complete initialization */
for ( i = 0 ; ( i < 100 ) & & ! ( READRDP ( lp ) & ( LE_C0_ERR | LE_C0_IDON ) ) ; i + + )
barrier ( ) ;
if ( ( i = = 100 ) | | ( READRDP ( lp ) & LE_C0_ERR ) ) {
printk ( " LANCE unopened after %d ticks, csr0=%4.4x. \n " , i , READRDP ( lp ) ) ;
return - 1 ;
}
/* Clear IDON by writing a "1", enable interrupts and start lance */
WRITERDP ( lp , LE_C0_IDON ) ;
WRITERDP ( lp , LE_C0_INEA | LE_C0_STRT ) ;
return 0 ;
}
static int lance_reset ( struct net_device * dev )
{
struct lance_private * lp = netdev_priv ( dev ) ;
int status ;
/* Stop the lance */
WRITERAP ( lp , LE_CSR0 ) ;
WRITERDP ( lp , LE_C0_STOP ) ;
load_csrs ( lp ) ;
lance_init_ring ( dev ) ;
dev - > trans_start = jiffies ;
status = init_restart_lance ( lp ) ;
# ifdef DEBUG_DRIVER
printk ( " Lance restart=%d \n " , status ) ;
# endif
return status ;
}
static int lance_rx ( struct net_device * dev )
{
struct lance_private * lp = netdev_priv ( dev ) ;
volatile struct lance_init_block * ib = lp - > init_block ;
volatile struct lance_rx_desc * rd ;
unsigned char bits ;
int len = 0 ; /* XXX shut up gcc warnings */
struct sk_buff * skb = 0 ; /* XXX shut up gcc warnings */
# ifdef TEST_HITS
int i ;
# endif
# ifdef TEST_HITS
printk ( " [ " ) ;
for ( i = 0 ; i < RX_RING_SIZE ; i + + ) {
if ( i = = lp - > rx_new )
printk ( " %s " ,
ib - > brx_ring [ i ] . rmd1_bits & LE_R1_OWN ? " _ " : " X " ) ;
else
printk ( " %s " ,
ib - > brx_ring [ i ] . rmd1_bits & LE_R1_OWN ? " . " : " 1 " ) ;
}
printk ( " ] " ) ;
# endif
# ifdef CONFIG_HP300
blinken_leds ( 0x40 , 0 ) ;
# endif
WRITERDP ( lp , LE_C0_RINT | LE_C0_INEA ) ; /* ack Rx int, reenable ints */
for ( rd = & ib - > brx_ring [ lp - > rx_new ] ; /* For each Rx ring we own... */
! ( ( bits = rd - > rmd1_bits ) & LE_R1_OWN ) ;
rd = & ib - > brx_ring [ lp - > rx_new ] ) {
/* We got an incomplete frame? */
if ( ( bits & LE_R1_POK ) ! = LE_R1_POK ) {
lp - > stats . rx_over_errors + + ;
lp - > stats . rx_errors + + ;
continue ;
} else if ( bits & LE_R1_ERR ) {
/* Count only the end frame as a rx error,
* not the beginning
*/
if ( bits & LE_R1_BUF ) lp - > stats . rx_fifo_errors + + ;
if ( bits & LE_R1_CRC ) lp - > stats . rx_crc_errors + + ;
if ( bits & LE_R1_OFL ) lp - > stats . rx_over_errors + + ;
if ( bits & LE_R1_FRA ) lp - > stats . rx_frame_errors + + ;
if ( bits & LE_R1_EOP ) lp - > stats . rx_errors + + ;
} else {
len = ( rd - > mblength & 0xfff ) - 4 ;
skb = dev_alloc_skb ( len + 2 ) ;
if ( skb = = 0 ) {
printk ( " %s: Memory squeeze, deferring packet. \n " ,
dev - > name ) ;
lp - > stats . rx_dropped + + ;
rd - > mblength = 0 ;
rd - > rmd1_bits = LE_R1_OWN ;
lp - > rx_new = ( lp - > rx_new + 1 ) & lp - > rx_ring_mod_mask ;
return 0 ;
}
skb - > dev = dev ;
skb_reserve ( skb , 2 ) ; /* 16 byte align */
skb_put ( skb , len ) ; /* make room */
eth_copy_and_sum ( skb ,
( unsigned char * ) & ( ib - > rx_buf [ lp - > rx_new ] [ 0 ] ) ,
len , 0 ) ;
skb - > protocol = eth_type_trans ( skb , dev ) ;
netif_rx ( skb ) ;
dev - > last_rx = jiffies ;
lp - > stats . rx_packets + + ;
lp - > stats . rx_bytes + = len ;
}
/* Return the packet to the pool */
rd - > mblength = 0 ;
rd - > rmd1_bits = LE_R1_OWN ;
lp - > rx_new = ( lp - > rx_new + 1 ) & lp - > rx_ring_mod_mask ;
}
return 0 ;
}
static int lance_tx ( struct net_device * dev )
{
struct lance_private * lp = netdev_priv ( dev ) ;
volatile struct lance_init_block * ib = lp - > init_block ;
volatile struct lance_tx_desc * td ;
int i , j ;
int status ;
# ifdef CONFIG_HP300
blinken_leds ( 0x80 , 0 ) ;
# endif
/* csr0 is 2f3 */
WRITERDP ( lp , LE_C0_TINT | LE_C0_INEA ) ;
/* csr0 is 73 */
j = lp - > tx_old ;
for ( i = j ; i ! = lp - > tx_new ; i = j ) {
td = & ib - > btx_ring [ i ] ;
/* If we hit a packet not owned by us, stop */
if ( td - > tmd1_bits & LE_T1_OWN )
break ;
if ( td - > tmd1_bits & LE_T1_ERR ) {
status = td - > misc ;
lp - > stats . tx_errors + + ;
if ( status & LE_T3_RTY ) lp - > stats . tx_aborted_errors + + ;
if ( status & LE_T3_LCOL ) lp - > stats . tx_window_errors + + ;
if ( status & LE_T3_CLOS ) {
lp - > stats . tx_carrier_errors + + ;
if ( lp - > auto_select ) {
lp - > tpe = 1 - lp - > tpe ;
printk ( " %s: Carrier Lost, trying %s \n " ,
dev - > name , lp - > tpe ? " TPE " : " AUI " ) ;
/* Stop the lance */
WRITERAP ( lp , LE_CSR0 ) ;
WRITERDP ( lp , LE_C0_STOP ) ;
lance_init_ring ( dev ) ;
load_csrs ( lp ) ;
init_restart_lance ( lp ) ;
return 0 ;
}
}
/* buffer errors and underflows turn off the transmitter */
/* Restart the adapter */
if ( status & ( LE_T3_BUF | LE_T3_UFL ) ) {
lp - > stats . tx_fifo_errors + + ;
printk ( " %s: Tx: ERR_BUF|ERR_UFL, restarting \n " ,
dev - > name ) ;
/* Stop the lance */
WRITERAP ( lp , LE_CSR0 ) ;
WRITERDP ( lp , LE_C0_STOP ) ;
lance_init_ring ( dev ) ;
load_csrs ( lp ) ;
init_restart_lance ( lp ) ;
return 0 ;
}
} else if ( ( td - > tmd1_bits & LE_T1_POK ) = = LE_T1_POK ) {
/*
* So we don ' t count the packet more than once .
*/
td - > tmd1_bits & = ~ ( LE_T1_POK ) ;
/* One collision before packet was sent. */
if ( td - > tmd1_bits & LE_T1_EONE )
lp - > stats . collisions + + ;
/* More than one collision, be optimistic. */
if ( td - > tmd1_bits & LE_T1_EMORE )
lp - > stats . collisions + = 2 ;
lp - > stats . tx_packets + + ;
}
j = ( j + 1 ) & lp - > tx_ring_mod_mask ;
}
lp - > tx_old = j ;
WRITERDP ( lp , LE_C0_TINT | LE_C0_INEA ) ;
return 0 ;
}
static irqreturn_t
lance_interrupt ( int irq , void * dev_id , struct pt_regs * regs )
{
struct net_device * dev = ( struct net_device * ) dev_id ;
struct lance_private * lp = netdev_priv ( dev ) ;
int csr0 ;
spin_lock ( & lp - > devlock ) ;
WRITERAP ( lp , LE_CSR0 ) ; /* LANCE Controller Status */
csr0 = READRDP ( lp ) ;
PRINT_RINGS ( ) ;
if ( ! ( csr0 & LE_C0_INTR ) ) { /* Check if any interrupt has */
spin_unlock ( & lp - > devlock ) ;
return IRQ_NONE ; /* been generated by the Lance. */
}
/* Acknowledge all the interrupt sources ASAP */
WRITERDP ( lp , csr0 & ~ ( LE_C0_INEA | LE_C0_TDMD | LE_C0_STOP | LE_C0_STRT | LE_C0_INIT ) ) ;
if ( ( csr0 & LE_C0_ERR ) ) {
/* Clear the error condition */
WRITERDP ( lp , LE_C0_BABL | LE_C0_ERR | LE_C0_MISS | LE_C0_INEA ) ;
}
if ( csr0 & LE_C0_RINT )
lance_rx ( dev ) ;
if ( csr0 & LE_C0_TINT )
lance_tx ( dev ) ;
/* Log misc errors. */
if ( csr0 & LE_C0_BABL )
lp - > stats . tx_errors + + ; /* Tx babble. */
if ( csr0 & LE_C0_MISS )
lp - > stats . rx_errors + + ; /* Missed a Rx frame. */
if ( csr0 & LE_C0_MERR ) {
printk ( " %s: Bus master arbitration failure, status %4.4x. \n " ,
dev - > name , csr0 ) ;
/* Restart the chip. */
WRITERDP ( lp , LE_C0_STRT ) ;
}
if ( lp - > tx_full & & netif_queue_stopped ( dev ) & & ( TX_BUFFS_AVAIL > = 0 ) ) {
lp - > tx_full = 0 ;
netif_wake_queue ( dev ) ;
}
WRITERAP ( lp , LE_CSR0 ) ;
WRITERDP ( lp , LE_C0_BABL | LE_C0_CERR | LE_C0_MISS | LE_C0_MERR | LE_C0_IDON | LE_C0_INEA ) ;
spin_unlock ( & lp - > devlock ) ;
return IRQ_HANDLED ;
}
int lance_open ( struct net_device * dev )
{
struct lance_private * lp = netdev_priv ( dev ) ;
int res ;
/* Install the Interrupt handler. Or we could shunt this out to specific drivers? */
if ( request_irq ( lp - > irq , lance_interrupt , 0 , lp - > name , dev ) )
return - EAGAIN ;
res = lance_reset ( dev ) ;
spin_lock_init ( & lp - > devlock ) ;
netif_start_queue ( dev ) ;
return res ;
}
int lance_close ( struct net_device * dev )
{
struct lance_private * lp = netdev_priv ( dev ) ;
netif_stop_queue ( dev ) ;
/* Stop the LANCE */
WRITERAP ( lp , LE_CSR0 ) ;
WRITERDP ( lp , LE_C0_STOP ) ;
free_irq ( lp - > irq , dev ) ;
return 0 ;
}
void lance_tx_timeout ( struct net_device * dev )
{
printk ( " lance_tx_timeout \n " ) ;
lance_reset ( dev ) ;
dev - > trans_start = jiffies ;
netif_wake_queue ( dev ) ;
}
int lance_start_xmit ( struct sk_buff * skb , struct net_device * dev )
{
struct lance_private * lp = netdev_priv ( dev ) ;
volatile struct lance_init_block * ib = lp - > init_block ;
int entry , skblen , len ;
static int outs ;
unsigned long flags ;
if ( ! TX_BUFFS_AVAIL )
return - 1 ;
netif_stop_queue ( dev ) ;
skblen = skb - > len ;
# ifdef DEBUG_DRIVER
/* dump the packet */
{
int i ;
for ( i = 0 ; i < 64 ; i + + ) {
if ( ( i % 16 ) = = 0 )
printk ( " \n " ) ;
printk ( " %2.2x " , skb - > data [ i ] ) ;
}
}
# endif
len = ( skblen < = ETH_ZLEN ) ? ETH_ZLEN : skblen ;
entry = lp - > tx_new & lp - > tx_ring_mod_mask ;
ib - > btx_ring [ entry ] . length = ( - len ) | 0xf000 ;
ib - > btx_ring [ entry ] . misc = 0 ;
if ( skb - > len < ETH_ZLEN )
memset ( ( char * ) & ib - > tx_buf [ entry ] [ 0 ] , 0 , ETH_ZLEN ) ;
memcpy ( ( char * ) & ib - > tx_buf [ entry ] [ 0 ] , skb - > data , skblen ) ;
/* Now, give the packet to the lance */
ib - > btx_ring [ entry ] . tmd1_bits = ( LE_T1_POK | LE_T1_OWN ) ;
lp - > tx_new = ( lp - > tx_new + 1 ) & lp - > tx_ring_mod_mask ;
outs + + ;
/* Kick the lance: transmit now */
WRITERDP ( lp , LE_C0_INEA | LE_C0_TDMD ) ;
dev - > trans_start = jiffies ;
dev_kfree_skb ( skb ) ;
spin_lock_irqsave ( & lp - > devlock , flags ) ;
if ( TX_BUFFS_AVAIL )
netif_start_queue ( dev ) ;
else
lp - > tx_full = 1 ;
spin_unlock_irqrestore ( & lp - > devlock , flags ) ;
return 0 ;
}
struct net_device_stats * lance_get_stats ( struct net_device * dev )
{
struct lance_private * lp = netdev_priv ( dev ) ;
return & lp - > stats ;
}
/* taken from the depca driver via a2065.c */
static void lance_load_multicast ( struct net_device * dev )
{
struct lance_private * lp = netdev_priv ( dev ) ;
volatile struct lance_init_block * ib = lp - > init_block ;
volatile u16 * mcast_table = ( u16 * ) & ib - > filter ;
struct dev_mc_list * dmi = dev - > mc_list ;
char * addrs ;
int i ;
u32 crc ;
/* set all multicast bits */
if ( dev - > flags & IFF_ALLMULTI ) {
ib - > filter [ 0 ] = 0xffffffff ;
ib - > filter [ 1 ] = 0xffffffff ;
return ;
}
/* clear the multicast filter */
ib - > filter [ 0 ] = 0 ;
ib - > filter [ 1 ] = 0 ;
/* Add addresses */
for ( i = 0 ; i < dev - > mc_count ; i + + ) {
addrs = dmi - > dmi_addr ;
dmi = dmi - > next ;
/* multicast address? */
if ( ! ( * addrs & 1 ) )
continue ;
crc = ether_crc_le ( 6 , addrs ) ;
crc = crc > > 26 ;
mcast_table [ crc > > 4 ] | = 1 < < ( crc & 0xf ) ;
}
return ;
}
void lance_set_multicast ( struct net_device * dev )
{
struct lance_private * lp = netdev_priv ( dev ) ;
volatile struct lance_init_block * ib = lp - > init_block ;
int stopped ;
stopped = netif_queue_stopped ( dev ) ;
if ( ! stopped )
netif_stop_queue ( dev ) ;
while ( lp - > tx_old ! = lp - > tx_new )
schedule ( ) ;
WRITERAP ( lp , LE_CSR0 ) ;
WRITERDP ( lp , LE_C0_STOP ) ;
lance_init_ring ( dev ) ;
if ( dev - > flags & IFF_PROMISC ) {
ib - > mode | = LE_MO_PROM ;
} else {
ib - > mode & = ~ LE_MO_PROM ;
lance_load_multicast ( dev ) ;
}
load_csrs ( lp ) ;
init_restart_lance ( lp ) ;
if ( ! stopped )
netif_start_queue ( dev ) ;
}
# ifdef CONFIG_NET_POLL_CONTROLLER
void lance_poll ( struct net_device * dev )
{
struct lance_private * lp = netdev_priv ( dev ) ;
spin_lock ( & lp - > devlock ) ;
WRITERAP ( lp , LE_CSR0 ) ;
WRITERDP ( lp , LE_C0_STRT ) ;
spin_unlock ( & lp - > devlock ) ;
lance_interrupt ( dev - > irq , dev , NULL ) ;
}
# endif
MODULE_LICENSE ( " GPL " ) ;