linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* mv643xx_eth.c: Multicast and IPv6 support
@ 2006-01-12 12:29 Wolfram Joost
  2006-01-12 19:09 ` Dale Farnsworth
  0 siblings, 1 reply; 4+ messages in thread
From: Wolfram Joost @ 2006-01-12 12:29 UTC (permalink / raw)
  To: linuxppc-dev

[-- Attachment #1: Type: text/plain, Size: 567 bytes --]

Hello,

currently, the driver mv643xx_eth.c doesn't support receiving of multicast and 
sending of IPv6 packets.
I found a piece of code in the u-boot bootloader which supports multicast 
receiving and did some cut and paste. Now receiving multicast packets works 
for me.
To be able to send IPv6 packets I had to remove the NETIF_F_HW_CSUM flag. 
Hardware checksumming for IPv4 should continue to work because of the 
NETIF_F_IP_CSUM flag.

I attach a diff against 2.6.15.

Does anyone know how to get documentation for the marvell mv643xx-family?

Regards
 Wolfram

[-- Attachment #2: mv643xx_eth.c.diff --]
[-- Type: text/x-diff, Size: 9318 bytes --]

diff -Naurp linux-2.6.15/drivers/net/mv643xx_eth.c linux-2.6.15.patched/drivers/net/mv643xx_eth.c
--- linux-2.6.15/drivers/net/mv643xx_eth.c	2006-01-03 04:21:10.000000000 +0100
+++ linux-2.6.15.patched/drivers/net/mv643xx_eth.c	2006-01-11 19:57:42.000000000 +0100
@@ -89,6 +89,7 @@ static int mv643xx_poll(struct net_devic
 static void ethernet_phy_set(unsigned int eth_port_num, int phy_addr);
 static int ethernet_phy_detect(unsigned int eth_port_num);
 static struct ethtool_ops mv643xx_ethtool_ops;
+static u8 mv643xx_eth_calc_mc_hash(u8 *addr);
 
 static char mv643xx_driver_name[] = "mv643xx_eth";
 static char mv643xx_driver_version[] = "1.0";
@@ -250,9 +251,115 @@ static void mv643xx_eth_update_mac_addre
 }
 
 /*
+ * mv643xx_eth_calc_mc_hash
+ *
+ * calculate multicast hash
+ *
+ * Input : pointer to mac address
+ * Output : hash value
+ */
+static u8 mv643xx_eth_calc_mc_hash(u8 *p_addr)
+{
+	unsigned mac_h;
+	unsigned mac_l;
+	u8 crc_result = 0;
+	unsigned mac_array[48];
+	unsigned crc[8];
+	unsigned i;
+
+	/* Calculate CRC-8 out of the given address */
+	mac_h = (p_addr[0] << 8) | (p_addr[1]);
+	mac_l = (p_addr[2] << 24) | (p_addr[3] << 16) |
+		(p_addr[4] << 8) | (p_addr[5] << 0);
+
+	for (i = 0; i < 32; i++)
+		mac_array[i] = (mac_l >> i) & 0x1;
+	for (i = 32; i < 48; i++)
+		mac_array[i] = (mac_h >> (i - 32)) & 0x1;
+
+
+	crc[0] = mac_array[45] ^ mac_array[43] ^ mac_array[40] ^
+		mac_array[39] ^ mac_array[35] ^ mac_array[34] ^
+		mac_array[31] ^ mac_array[30] ^ mac_array[28] ^
+		mac_array[23] ^ mac_array[21] ^ mac_array[19] ^
+		mac_array[18] ^ mac_array[16] ^ mac_array[14] ^
+		mac_array[12] ^ mac_array[8] ^ mac_array[7] ^
+		mac_array[6] ^ mac_array[0];
+
+	crc[1] = mac_array[46] ^ mac_array[45] ^ mac_array[44] ^
+		mac_array[43] ^ mac_array[41] ^ mac_array[39] ^
+		mac_array[36] ^ mac_array[34] ^ mac_array[32] ^
+		mac_array[30] ^ mac_array[29] ^ mac_array[28] ^
+		mac_array[24] ^ mac_array[23] ^ mac_array[22] ^
+		mac_array[21] ^ mac_array[20] ^ mac_array[18] ^
+		mac_array[17] ^ mac_array[16] ^ mac_array[15] ^
+		mac_array[14] ^ mac_array[13] ^ mac_array[12] ^
+		mac_array[9] ^ mac_array[6] ^ mac_array[1] ^
+		mac_array[0];
+
+	crc[2] = mac_array[47] ^ mac_array[46] ^ mac_array[44] ^
+		mac_array[43] ^ mac_array[42] ^ mac_array[39] ^
+		mac_array[37] ^ mac_array[34] ^ mac_array[33] ^
+		mac_array[29] ^ mac_array[28] ^ mac_array[25] ^
+		mac_array[24] ^ mac_array[22] ^ mac_array[17] ^
+		mac_array[15] ^ mac_array[13] ^ mac_array[12] ^
+		mac_array[10] ^ mac_array[8] ^ mac_array[6] ^
+		mac_array[2] ^ mac_array[1] ^ mac_array[0];
+
+	crc[3] = mac_array[47] ^ mac_array[45] ^ mac_array[44] ^
+		mac_array[43] ^ mac_array[40] ^ mac_array[38] ^
+		mac_array[35] ^ mac_array[34] ^ mac_array[30] ^
+		mac_array[29] ^ mac_array[26] ^ mac_array[25] ^
+		mac_array[23] ^ mac_array[18] ^ mac_array[16] ^
+		mac_array[14] ^ mac_array[13] ^ mac_array[11] ^
+		mac_array[9] ^ mac_array[7] ^ mac_array[3] ^
+		mac_array[2] ^ mac_array[1];
+
+	crc[4] = mac_array[46] ^ mac_array[45] ^ mac_array[44] ^
+		mac_array[41] ^ mac_array[39] ^ mac_array[36] ^
+		mac_array[35] ^ mac_array[31] ^ mac_array[30] ^
+		mac_array[27] ^ mac_array[26] ^ mac_array[24] ^
+		mac_array[19] ^ mac_array[17] ^ mac_array[15] ^
+		mac_array[14] ^ mac_array[12] ^ mac_array[10] ^
+		mac_array[8] ^ mac_array[4] ^ mac_array[3] ^
+		mac_array[2];
+
+	crc[5] = mac_array[47] ^ mac_array[46] ^ mac_array[45] ^
+		mac_array[42] ^ mac_array[40] ^ mac_array[37] ^
+		mac_array[36] ^ mac_array[32] ^ mac_array[31] ^
+		mac_array[28] ^ mac_array[27] ^ mac_array[25] ^
+		mac_array[20] ^ mac_array[18] ^ mac_array[16] ^
+		mac_array[15] ^ mac_array[13] ^ mac_array[11] ^
+		mac_array[9] ^ mac_array[5] ^ mac_array[4] ^
+		mac_array[3];
+
+	crc[6] = mac_array[47] ^ mac_array[46] ^ mac_array[43] ^
+		mac_array[41] ^ mac_array[38] ^ mac_array[37] ^
+		mac_array[33] ^ mac_array[32] ^ mac_array[29] ^
+		mac_array[28] ^ mac_array[26] ^ mac_array[21] ^
+		mac_array[19] ^ mac_array[17] ^ mac_array[16] ^
+		mac_array[14] ^ mac_array[12] ^ mac_array[10] ^
+		mac_array[6] ^ mac_array[5] ^ mac_array[4];
+
+	crc[7] = mac_array[47] ^ mac_array[44] ^ mac_array[42] ^
+		mac_array[39] ^ mac_array[38] ^ mac_array[34] ^
+		mac_array[33] ^ mac_array[30] ^ mac_array[29] ^
+		mac_array[27] ^ mac_array[22] ^ mac_array[20] ^
+		mac_array[18] ^ mac_array[17] ^ mac_array[15] ^
+		mac_array[13] ^ mac_array[11] ^ mac_array[7] ^
+		mac_array[6] ^ mac_array[5];
+
+	for (i = 0; i < 8; i++)
+		crc_result = crc_result | (crc[i] << i);
+
+	return crc_result;
+}
+
+/*
  * mv643xx_eth_set_rx_mode
  *
  * Change from promiscuos to regular rx mode
+ * maintain multicast tables
  *
  * Input :	pointer to ethernet interface network device structure
  * Output :	N/A
@@ -260,13 +367,64 @@ static void mv643xx_eth_update_mac_addre
 static void mv643xx_eth_set_rx_mode(struct net_device *dev)
 {
 	struct mv643xx_private *mp = netdev_priv(dev);
+	struct dev_mc_list *mc_list = dev->mc_list;
+	u32 multicast_table[64];
+	unsigned table_index;
+	unsigned reg_index;
 
+	/* promiscuous mode */
 	if (dev->flags & IFF_PROMISC)
 		mp->port_config |= (u32) MV643XX_ETH_UNICAST_PROMISCUOUS_MODE;
 	else
 		mp->port_config &= ~(u32) MV643XX_ETH_UNICAST_PROMISCUOUS_MODE;
 
 	mv_write(MV643XX_ETH_PORT_CONFIG_REG(mp->port_num), mp->port_config);
+
+	/* multicast */
+	if (dev->flags & IFF_ALLMULTI) {
+		for (table_index = 0; table_index <= 0xFC; table_index += 4) {
+			printk(KERN_INFO "IFF_ALLMULTI!\n");
+			mv_write((MV643XX_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE(mp->port_num) +
+				  table_index), 0x01010101);
+			mv_write((MV643XX_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE(mp->port_num) +
+				  table_index), 0x01010101);
+		}
+	} else {
+		/* special multicast table */
+		memset(multicast_table,0,sizeof(multicast_table));
+		for (mc_list = dev->mc_list; mc_list; mc_list = mc_list->next) {
+			if ( (mc_list->dmi_addr[0] == 0x01) &&
+				 (mc_list->dmi_addr[1] == 0x00) &&
+				 (mc_list->dmi_addr[2] == 0x5E) &&
+				 (mc_list->dmi_addr[3] == 0x00) &&
+				 (mc_list->dmi_addr[4] == 0x00) ) {
+
+				table_index = mc_list->dmi_addr[5] / 4;
+				reg_index = 8 * ( mc_list->dmi_addr[5] & 0x03 );
+				multicast_table[table_index] |= 0x01 << reg_index;
+			}
+		}
+		for (table_index = 0; table_index <=0xFC; table_index +=4)
+				mv_write((MV643XX_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE(mp->port_num) +
+						  table_index), multicast_table[table_index / 4]);
+		/* other multicast table */
+		memset(multicast_table,0,sizeof(multicast_table));
+		for (mc_list = dev->mc_list; mc_list; mc_list = mc_list->next) {
+			if ( (mc_list->dmi_addr[0] != 0x01) ||
+				 (mc_list->dmi_addr[1] != 0x00) ||
+				 (mc_list->dmi_addr[2] != 0x5E) ||
+				 (mc_list->dmi_addr[3] != 0x00) ||
+				 (mc_list->dmi_addr[4] != 0x00) ) {
+
+				table_index = mv643xx_eth_calc_mc_hash(mc_list->dmi_addr);
+				reg_index = 8 * (table_index & 0x03);
+				multicast_table[table_index / 4] |= 0x01 << reg_index;
+			}
+		}
+		for (table_index = 0; table_index <=0xFC; table_index +=4)
+				mv_write((MV643XX_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE(mp->port_num) +
+						  table_index), multicast_table[table_index / 4]);
+	}
 }
 
 /*
@@ -1150,7 +1308,6 @@ linear:
 					   5 << ETH_TX_IHL_SHIFT;
 			pkt_info.l4i_chk = 0;
 		} else {
-
 			pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT |
 					   ETH_TX_FIRST_DESC |
 					   ETH_TX_LAST_DESC |
@@ -1158,14 +1315,16 @@ linear:
 					   ETH_GEN_IP_V_4_CHECKSUM |
 					   skb->nh.iph->ihl << ETH_TX_IHL_SHIFT;
 			/* CPU already calculated pseudo header checksum. */
-			if (skb->nh.iph->protocol == IPPROTO_UDP) {
+			if ( (skb->protocol == ETH_P_IP) &&
+				 (skb->nh.iph->protocol == IPPROTO_UDP) ) {
 				pkt_info.cmd_sts |= ETH_UDP_FRAME;
 				pkt_info.l4i_chk = skb->h.uh->check;
-			} else if (skb->nh.iph->protocol == IPPROTO_TCP)
+			} else if ( (skb->protocol == ETH_P_IP) &&
+				 (skb->nh.iph->protocol == IPPROTO_TCP) )
 				pkt_info.l4i_chk = skb->h.th->check;
 			else {
 				printk(KERN_ERR
-					"%s: chksum proto != TCP or UDP\n",
+					"%s: chksum proto != IPv4 TCP or UDP\n",
 					dev->name);
 				spin_unlock_irqrestore(&mp->lock, flags);
 				return 1;
@@ -1221,14 +1380,19 @@ linear:
 					   ETH_GEN_IP_V_4_CHECKSUM |
 					   skb->nh.iph->ihl << ETH_TX_IHL_SHIFT;
 			/* CPU already calculated pseudo header checksum. */
-			if (skb->nh.iph->protocol == IPPROTO_UDP) {
+
+
+
+			if ( (skb->protocol == ETH_P_IP) &&
+				 (skb->nh.iph->protocol == IPPROTO_UDP) ) {
 				pkt_info.cmd_sts |= ETH_UDP_FRAME;
 				pkt_info.l4i_chk = skb->h.uh->check;
-			} else if (skb->nh.iph->protocol == IPPROTO_TCP)
+			} else if ( (skb->protocol == ETH_P_IP) &&
+				 (skb->nh.iph->protocol == IPPROTO_TCP) ) 
 				pkt_info.l4i_chk = skb->h.th->check;
 			else {
 				printk(KERN_ERR
-					"%s: chksum proto != TCP or UDP\n",
+					"%s: chksum proto != IPv4 TCP or UDP\n",
 					dev->name);
 				spin_unlock_irqrestore(&mp->lock, flags);
 				return 1;
@@ -1441,7 +1605,7 @@ static int mv643xx_eth_probe(struct plat
 	 * Zero copy can only work if we use Discovery II memory. Else, we will
 	 * have to map the buffers to ISA memory which is only 16 MB
 	 */
-	dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HW_CSUM;
+	dev->features = NETIF_F_SG | NETIF_F_IP_CSUM;
 #endif
 #endif

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: mv643xx_eth.c: Multicast and IPv6 support
  2006-01-12 12:29 mv643xx_eth.c: Multicast and IPv6 support Wolfram Joost
@ 2006-01-12 19:09 ` Dale Farnsworth
  2006-01-12 21:16   ` Wolfram Joost
  0 siblings, 1 reply; 4+ messages in thread
From: Dale Farnsworth @ 2006-01-12 19:09 UTC (permalink / raw)
  To: pegasos, linuxppc-dev

In article <200601121329.11693@mail.frokaschwei.de>
pegasos@frokaschwei.de writes:
> currently, the driver mv643xx_eth.c doesn't support receiving of multicast and 
> sending of IPv6 packets.
> I found a piece of code in the u-boot bootloader which supports multicast 
> receiving and did some cut and paste. Now receiving multicast packets works 
> for me.
> To be able to send IPv6 packets I had to remove the NETIF_F_HW_CSUM flag. 
> Hardware checksumming for IPv4 should continue to work because of the 
> NETIF_F_IP_CSUM flag.
> 
> I attach a diff against 2.6.15.

Thanks.  On December 28th, I posted a patch to netdev@vger.kernel.org that
adds multicast support to the mv643xx driver.  I'll ping jgarzik
about it.

The IPv6 changes look good.  Would you please re-submit these without
the multicast changes and with a Signed-off-by line?

-Dale

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: mv643xx_eth.c: Multicast and IPv6 support
  2006-01-12 19:09 ` Dale Farnsworth
@ 2006-01-12 21:16   ` Wolfram Joost
  2006-01-12 21:28     ` Dale Farnsworth
  0 siblings, 1 reply; 4+ messages in thread
From: Wolfram Joost @ 2006-01-12 21:16 UTC (permalink / raw)
  To: Dale Farnsworth, linuxppc-dev

Hello,

> > currently, the driver mv643xx_eth.c doesn't support receiving of

> Thanks.  On December 28th, I posted a patch to netdev@vger.kernel.org that
> adds multicast support to the mv643xx driver.  I'll ping jgarzik
> about it.

Oh, didn't find that patch yesterday.

> The IPv6 changes look good.  Would you please re-submit these without
> the multicast changes and with a Signed-off-by line?

Here it comes. However, IPv6 uses multicasting for address resolution
(replacement for ARP), so IPv6 doesn't work until the multicast patch is applied, too.

Wolfram.

---
This patch removes the NETIF_F_HW_CSUM flag to be able to use other protocols
than IPv4. Hardware checksums for IPv4 should continue to work.
The sanity-check has been enhanced to check the used protocol and to not
access skb->iph for non-ipv4-packets.

Signed-off-by: Wolfram Joost <pegasos@frokaschwei.de>
---
diff -up linux-2.6.15/drivers/net/mv643xx_eth.c linux-2.6.15.patched/drivers/net/mv643xx_eth.c
--- linux-2.6.15/drivers/net/mv643xx_eth.c	2006-01-03 04:21:10.000000000 +0100
+++ linux-2.6.15.patched/drivers/net/mv643xx_eth.c	2006-01-12 21:52:32.000000000 +0100
@@ -1151,25 +1151,28 @@ linear:
 			pkt_info.l4i_chk = 0;
 		} else {
 
-			pkt_info.cmd_sts = ETH_TX_ENABLE_INTERRUPT |
-					   ETH_TX_FIRST_DESC |
-					   ETH_TX_LAST_DESC |
-					   ETH_GEN_TCP_UDP_CHECKSUM |
-					   ETH_GEN_IP_V_4_CHECKSUM |
-					   skb->nh.iph->ihl << ETH_TX_IHL_SHIFT;
 			/* CPU already calculated pseudo header checksum. */
-			if (skb->nh.iph->protocol == IPPROTO_UDP) {
-				pkt_info.cmd_sts |= ETH_UDP_FRAME;
+			if ( (skb->protocol == ETH_P_IP) &&
+			     (skb->nh.iph->protocol == IPPROTO_UDP) ) {
+				pkt_info.cmd_sts = ETH_UDP_FRAME;
 				pkt_info.l4i_chk = skb->h.uh->check;
-			} else if (skb->nh.iph->protocol == IPPROTO_TCP)
+			} else if ( (skb->protocol == ETH_P_IP) &&
+				    (skb->nh.iph->protocol == IPPROTO_TCP) ) {
+				pkt_info.cmd_sts = 0;
 				pkt_info.l4i_chk = skb->h.th->check;
-			else {
+			} else {
 				printk(KERN_ERR
-					"%s: chksum proto != TCP or UDP\n",
+					"%s: chksum proto != IPv4 TCP or UDP\n",
 					dev->name);
 				spin_unlock_irqrestore(&mp->lock, flags);
 				return 1;
 			}
+			pkt_info.cmd_sts |= ETH_TX_ENABLE_INTERRUPT |
+					    ETH_TX_FIRST_DESC |
+					    ETH_TX_LAST_DESC |
+					    ETH_GEN_TCP_UDP_CHECKSUM |
+					    ETH_GEN_IP_V_4_CHECKSUM |
+					    skb->nh.iph->ihl << ETH_TX_IHL_SHIFT;
 		}
 		pkt_info.byte_cnt = skb->len;
 		pkt_info.buf_ptr = dma_map_single(NULL, skb->data, skb->len,
@@ -1216,23 +1219,26 @@ linear:
 			pkt_info.cmd_sts = ETH_TX_FIRST_DESC |
 					   5 << ETH_TX_IHL_SHIFT;
 		else {
-			pkt_info.cmd_sts = ETH_TX_FIRST_DESC |
-					   ETH_GEN_TCP_UDP_CHECKSUM |
-					   ETH_GEN_IP_V_4_CHECKSUM |
-					   skb->nh.iph->ihl << ETH_TX_IHL_SHIFT;
 			/* CPU already calculated pseudo header checksum. */
-			if (skb->nh.iph->protocol == IPPROTO_UDP) {
-				pkt_info.cmd_sts |= ETH_UDP_FRAME;
+			if ( (skb->protocol == ETH_P_IP) &&
+			     (skb->nh.iph->protocol == IPPROTO_UDP) ) {
+				pkt_info.cmd_sts = ETH_UDP_FRAME;
 				pkt_info.l4i_chk = skb->h.uh->check;
-			} else if (skb->nh.iph->protocol == IPPROTO_TCP)
+			} else if ( (skb->protocol == ETH_P_IP) &&
+				    (skb->nh.iph->protocol == IPPROTO_TCP) ) {
+				pkt_info.cmd_sts = 0;
 				pkt_info.l4i_chk = skb->h.th->check;
-			else {
+			} else {
 				printk(KERN_ERR
-					"%s: chksum proto != TCP or UDP\n",
+					"%s: chksum proto != IPv4 TCP or UDP\n",
 					dev->name);
 				spin_unlock_irqrestore(&mp->lock, flags);
 				return 1;
 			}
+			pkt_info.cmd_sts |= ETH_TX_FIRST_DESC |
+					    ETH_GEN_TCP_UDP_CHECKSUM |
+					    ETH_GEN_IP_V_4_CHECKSUM |
+					    skb->nh.iph->ihl << ETH_TX_IHL_SHIFT;
 		}
 
 		status = eth_port_send(mp, &pkt_info);
@@ -1441,7 +1447,7 @@ static int mv643xx_eth_probe(struct plat
 	 * Zero copy can only work if we use Discovery II memory. Else, we will
 	 * have to map the buffers to ISA memory which is only 16 MB
 	 */
-	dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HW_CSUM;
+	dev->features = NETIF_F_SG | NETIF_F_IP_CSUM;
 #endif
 #endif
 

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: mv643xx_eth.c: Multicast and IPv6 support
  2006-01-12 21:16   ` Wolfram Joost
@ 2006-01-12 21:28     ` Dale Farnsworth
  0 siblings, 0 replies; 4+ messages in thread
From: Dale Farnsworth @ 2006-01-12 21:28 UTC (permalink / raw)
  To: Wolfram Joost; +Cc: linuxppc-dev

On Thu, Jan 12, 2006 at 10:16:18PM +0100, Wolfram Joost wrote:
> > > currently, the driver mv643xx_eth.c doesn't support receiving of
> 
> > Thanks.  On December 28th, I posted a patch to netdev@vger.kernel.org that
> > adds multicast support to the mv643xx driver.  I'll ping jgarzik
> > about it.
> 
> Oh, didn't find that patch yesterday.
> 
> > The IPv6 changes look good.  Would you please re-submit these without
> > the multicast changes and with a Signed-off-by line?
> 
> Here it comes. However, IPv6 uses multicasting for address resolution
> (replacement for ARP), so IPv6 doesn't work until the multicast patch is applied, too.

Thanks Wolfram.  I'll put it in my queue for testing and if all goes well,
I'll forward it to Jeff Garzik this weekend.

-Dale

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2006-01-12 21:28 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-01-12 12:29 mv643xx_eth.c: Multicast and IPv6 support Wolfram Joost
2006-01-12 19:09 ` Dale Farnsworth
2006-01-12 21:16   ` Wolfram Joost
2006-01-12 21:28     ` Dale Farnsworth

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).