patch-2.1.15 linux/drivers/net/8390.c

Next file: linux/drivers/net/8390.h
Previous file: linux/drivers/net/3c523.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.14/linux/drivers/net/8390.c linux/drivers/net/8390.c
@@ -30,7 +30,8 @@
   Paul Gortmaker	: exchange static int ei_pingpong for a #define,
 			  also add better Tx error handling.
   Paul Gortmaker	: rewrite Rx overrun handling as per NS specs.
-  Alexey Kuznetsov	: use the software multicast filter.
+  Alexey Kuznetsov	: use the 8390's six bit hash multicast filter.
+  Paul Gortmaker	: tweak ANK's above multicast changes a bit.
 
 
   Sources:
@@ -679,73 +680,90 @@
 }
 
 /*
- *	Set or clear the multicast filter for this adaptor.
- *	(Don't assume 8bit char..)
+ * Update the given Autodin II CRC value with another data byte.
  */
- 
-extern inline __u32 upd_8390_crc(__u8 b, __u32 x)
+static inline u32 update_crc(u8 byte, u32 current_crc)
 {
-	int i;
-	__u8 ah=0;
-	for(i=0;i<8;i++)
-	{
-		__u8 carry = (x>>31);
-		x<<=1;
-		ah = ((ah<<1)|carry)^b;
-		
-		if(ah&1)
-			x^=0x04C11DB7;
-		ah>>=1;
-		b>>=1;
+	int bit;
+	u8 ah = 0;
+
+	for (bit=0; bit<8; bit++) {
+		u8 carry = (current_crc>>31);
+		current_crc <<= 1;
+		ah = ((ah<<1) | carry) ^ byte;
+		if (ah&1)
+			current_crc ^= 0x04C11DB7;	/* CRC polynomial */
+		ah >>= 1;
+		byte >>= 1;
 	}
-	return x;
+	return current_crc;
 }
 
-extern __inline void make_8390_mc_bits(__u8 *bits, struct device *dev)
+/*
+ * Form the 64 bit 8390 multicast table from the linked list of addresses
+ * associated with this dev structure.
+ */
+static inline void make_mc_bits(u8 *bits, struct device *dev)
 {
 	struct dev_mc_list *dmi;
-	memset(bits,0,8);
-	
-	for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
-	{
+
+	for (dmi=dev->mc_list; dmi; dmi=dmi->next) {
 		int i;
-		__u32 x;
-		if(dmi->dmi_addrlen!=6)
-			continue;	/* !! */
-		x=0xFFFFFFFFUL;
-		for(i=0;i<6;i++)
-			x = upd_8390_crc(dmi->dmi_addr[i],x);
-		bits[x>>29] |= (1<<((x>>26)&7));
+		u32 crc;
+		if (dmi->dmi_addrlen != ETH_ALEN) {
+			printk(KERN_INFO "%s: invalid multicast address length given.\n", dev->name);
+			continue;
+		}
+		crc = 0xffffffff;	/* initial CRC value */
+		for (i=0; i<ETH_ALEN; i++)
+			crc = update_crc(dmi->dmi_addr[i], crc);
+		/* 
+		 * The 8390 uses the 6 most significant bits of the
+		 * CRC to index the multicast table.
+		 */
+		bits[crc>>29] |= (1<<((crc>>26)&7));
 	}
 }
 
+/*
+ *	Set or clear the multicast filter for this adaptor.
+ */
+ 
 static void set_multicast_list(struct device *dev)
 {
 	short ioaddr = dev->base_addr;
-    
+	int i;
+	unsigned long flags;
+	struct ei_device *ei = (struct ei_device*)dev->priv;
+
+	if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) {
+		memset(ei->mcfilter, 0, 8);
+		if (dev->mc_list)
+			make_mc_bits(ei->mcfilter, dev);
+	} else
+		memset(ei->mcfilter, 0xFF, 8);	/* mcast set to accept-all */
+
+	/* 
+	 * DP8390 manuals don't specify any magic sequence for altering
+	 * the multicast regs on an already running card. To be safe, we
+	 * ensure multicast mode is off prior to loading up the new hash
+	 * table. If this proves to be not enough, we can always resort
+	 * to stopping the NIC, loading the table and then restarting.
+	 */
+	if (dev->start)
+		outb_p(E8390_RXCONFIG, ioaddr + EN0_RXCR);
+	save_flags(flags);
+	cli();
+	outb_p(E8390_NODMA + E8390_PAGE1, ioaddr + E8390_CMD);
+	for(i = 0; i < 8; i++)
+		outb_p(ei->mcfilter[i], ioaddr + EN1_MULT + i);
+	outb_p(E8390_NODMA + E8390_PAGE0, ioaddr + E8390_CMD);
+	restore_flags(flags);
+
 	if(dev->flags&IFF_PROMISC)
-	{
 		outb_p(E8390_RXCONFIG | 0x18, ioaddr + EN0_RXCR);
-	}
-	else if((dev->flags&IFF_ALLMULTI)||dev->mc_list)
-	{
-		unsigned long flags;
-		__u8 mc_bits[8];
-		int i;
-		
-		if(dev->flags&IFF_ALLMULTI)
-			memset(mc_bits,0xFF,8);
-		else
-			make_8390_mc_bits(mc_bits,dev);
-		save_flags(flags);
-		cli();
-		outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, ioaddr);
-		for(i = 0; i < 8; i++)
-				outb_p(mc_bits[i], ioaddr + EN1_MULT + i);
-		outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, ioaddr);
+	else if(dev->flags&IFF_ALLMULTI || dev->mc_list)
 		outb_p(E8390_RXCONFIG | 0x08, ioaddr + EN0_RXCR);
-		restore_flags(flags);
-	} 
 	else
 		outb_p(E8390_RXCONFIG, ioaddr + EN0_RXCR);
 }
@@ -806,20 +824,15 @@
     outb_p(0xFF, e8390_base + EN0_ISR);
     outb_p(0x00,  e8390_base + EN0_IMR);
     
-    /* Copy the station address into the DS8390 registers,
-       and set the multicast hash bitmap to receive all multicasts. */
+    /* Copy the station address into the DS8390 registers. */
     save_flags(flags);
     cli();
     outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
     for(i = 0; i < 6; i++) {
 		outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS + i);
     }
-    /* Initialize the multicast list to accept-all.  If we enable multicast
-       the higher levels can do the filtering. */
-    for(i = 0; i < 8; i++)
-		outb_p(0xff, e8390_base + EN1_MULT + i);
     
-    outb_p(ei_local->rx_start_page,	 e8390_base + EN1_CURPAG);
+    outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
     outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
     restore_flags(flags);
     dev->tbusy = 0;
@@ -833,8 +846,7 @@
 		outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
 		/* 3c503 TechMan says rxconfig only after the NIC is started. */
 		outb_p(E8390_RXCONFIG,	e8390_base + EN0_RXCR); /* rx on,  */
-		dev->set_multicast_list(dev);		/* Get the multicast status right if this
-							   was a reset. */
+		set_multicast_list(dev);	/* (re)load the mcast table */
     }
     return;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov