sk driver (yukon based card) and multicast

Keith Mitchell kmitch at guru.org
Wed Dec 17 07:00:31 PST 2003


Hi,

I just got a couple of Linksys EG1032 cards that are based on the Marvell
Yukon Chipset.  I built a kernel with the sk driver in it and the basic
stuff seemed to work fine, but multicast didn't work at all unless I was
also running tcpdump (i.e. promiscuous mode was turned on).

I traced this down to the hash calculation routine used to program
the GMAC on the Yukon controller.  Apparently the Yukon chip uses
a different algorithm than the XMAC (Genesis chipset) uses.

I ported the algorithm used by the linux driver over and now it seems
to work fine.  The diffs are attached.

-- 
Keith Mitchell
Email: kmitch at guru.org				PGP key available upon request
-------------- next part --------------
Index: if_sk.c
===================================================================
RCS file: /pub/cvs/src/sys/pci/if_sk.c,v
retrieving revision 1.19.2.10
diff -c -r1.19.2.10 if_sk.c
*** if_sk.c	14 Oct 2003 18:22:42 -0000	1.19.2.10
--- if_sk.c	17 Dec 2003 14:52:14 -0000
***************
*** 214,220 ****
  						int));
  static void sk_marv_miibus_statchg	__P((struct sk_if_softc *));
  
! static u_int32_t sk_calchash	__P((caddr_t));
  static void sk_setfilt		__P((struct sk_if_softc *, caddr_t, int));
  static void sk_setmulti		__P((struct sk_if_softc *));
  
--- 219,226 ----
  						int));
  static void sk_marv_miibus_statchg	__P((struct sk_if_softc *));
  
! static u_int32_t xmac_calchash	__P((caddr_t));
! static u_int32_t gmac_calchash	__P((caddr_t));
  static void sk_setfilt		__P((struct sk_if_softc *, caddr_t, int));
  static void sk_setmulti		__P((struct sk_if_softc *));
  
***************
*** 668,677 ****
  	return;
  }
  
! #define SK_POLY		0xEDB88320
! #define SK_BITS		6
  
! static u_int32_t sk_calchash(addr)
  	caddr_t			addr;
  {
  	u_int32_t		idx, bit, data, crc;
--- 674,684 ----
  	return;
  }
  
! #define XMAC_POLY		0xEDB88320
! #define GMAC_POLY               0x04C11DB7L
! #define HASH_BITS		6
  
! static u_int32_t xmac_calchash(addr)
  	caddr_t			addr;
  {
  	u_int32_t		idx, bit, data, crc;
***************
*** 681,690 ****
  
  	for (idx = 0; idx < 6; idx++) {
  		for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1)
! 			crc = (crc >> 1) ^ (((crc ^ data) & 1) ? SK_POLY : 0);
  	}
  
! 	return (~crc & ((1 << SK_BITS) - 1));
  }
  
  static void sk_setfilt(sc_if, addr, slot)
--- 688,734 ----
  
  	for (idx = 0; idx < 6; idx++) {
  		for (data = *addr++, bit = 0; bit < 8; bit++, data >>= 1)
! 			crc = (crc >> 1) ^ (((crc ^ data) & 1) ? XMAC_POLY : 0);
  	}
  
! 	return (~crc & ((1 << HASH_BITS) - 1));
! }
! 
! static u_int32_t gmac_calchash(addr)
!     caddr_t			addr;
! {
!     u_int32_t               idx, bit, crc, tmpData, data;
! 
!     /* Compute CRC for the address value. */
!     crc = 0xFFFFFFFF; /* initial value */
! 
!     for (idx = 0; idx < 6; idx++) {
!         data = *addr++;
! 
!         /* Change bit order in byte. */
!         tmpData = data;
!         for (bit = 0; bit < 8; bit++) {
!             if (tmpData & 1) {
!                 data |=  1 << (7 - bit);
!             }
!             else {
!                 data &= ~(1 << (7 - bit));
!             }
! 
!             tmpData >>= 1;
!         }
! 
!         crc ^= (data << 24);
!         for (bit = 0; bit < 8; bit++) {
!             if (crc & 0x80000000) {
!                 crc = (crc << 1) ^ GMAC_POLY;
!             } else {
!                 crc <<= 1;
!             }
!         }
!     }
! 
!     return (crc & ((1 << HASH_BITS) - 1));
  }
  
  static void sk_setfilt(sc_if, addr, slot)
***************
*** 760,771 ****
  				continue;
  			}
  
! 			h = sk_calchash(
! 				LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
! 			if (h < 32)
! 				hashes[0] |= (1 << h);
! 			else
! 				hashes[1] |= (1 << (h - 32));
  		}
  	}
  
--- 804,828 ----
  				continue;
  			}
  
!                         switch(sc->sk_type) {
!                         case SK_GENESIS:
!                             h = xmac_calchash(
!                                 LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
!                             if (h < 32)
!                                 hashes[0] |= (1 << h);
!                             else
!                                 hashes[1] |= (1 << (h - 32));
!                             break;
! 
!                         case SK_YUKON:
!                             h = gmac_calchash(
!                                 LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
!                             if (h < 32)
!                                 hashes[0] |= (1 << h);
!                             else
!                                 hashes[1] |= (1 << (h - 32));
!                             break;
!                         }
  		}
  	}


More information about the freebsd-current mailing list