Benoit PAPILLAULT wrote:
Hello,

I noticed that the zd1211rw driver does not work with ipv6 since the autoconfiguration process requires multicast to be implemented. This was working with the vendor driver. I successfully ported the needed part of the source code to zd1211rw to add multicast support (in fact, the cards needs to be instruct with the proper MAC multicast addr in order to receive the packets intended to those multicast addr). It works (patch attached, against git repository from dsd).

However, ipv6 is still not working and i got several messages "wlan0: duplicate address detected!". I'm not sure yet but i think that multicast packets sent from STA to AP are then retransmitted by the AP to all STA, including the STA which has sent it!

How to make ipv6 working?

Best regards,
Benoit

I found it. Broadcast/multicast packets sent by the AP are retransmitted by the AP and as such, received by the STA. Reading the vendor driver and the 802.11 specs, I added a filter against broadcast/multicast packets whose source address (SA) is ours.

IPv6 is now working!

Patch (replacing the previous) is attached.

Best regards,
Benoit

diff --git a/zd_chip.c b/zd_chip.c
diff --git a/zd_def.h b/zd_def.h
diff --git a/zd_mac.c b/zd_mac.c
index 8acd168..fac9d65 100644
--- a/zd_mac.c
+++ b/zd_mac.c
@@ -36,6 +36,9 @@ static void softmac_init(struct ieee8021
 static void housekeeping_init(struct zd_mac *mac);
 static void housekeeping_enable(struct zd_mac *mac);
 static void housekeeping_disable(struct zd_mac *mac);
+static void zd_mac_kevent(void *);
+
+#define KEVENT_SET_MULTICAST 1
 
 int zd_mac_init(struct zd_mac *mac,
 	        struct net_device *netdev,
@@ -51,6 +54,9 @@ int zd_mac_init(struct zd_mac *mac,
 	softmac_init(ieee80211_priv(netdev));
 	zd_chip_init(&mac->chip, netdev, intf);
 	housekeeping_init(mac);
+
+        INIT_WORK(&mac->kevent, zd_mac_kevent, netdev);
+        mac->kevent_flags = 0;
 	return 0;
 }
 
@@ -220,6 +226,85 @@ int zd_mac_stop(struct net_device *netde
 	return 0;
 }
 
+/*
+  Set the proper registers from the list of multicast addr. to listen to.
+  Promiscuous and allmulti modes are handled here as well. This function cannot
+  be called in atomic context since zd_io... function needs to sleep.
+*/
+
+void zd_mac_set_multicast_list_real(struct net_device * dev)
+{
+    struct zd_mac * mac = zd_netdev_mac(dev);
+    struct zd_chip * chip = &mac->chip;
+    struct dev_mc_list * mc_list;
+    unsigned int i;
+    u32 tmp;
+
+    /* check that the interface is UP */
+    if (!(dev->flags & IFF_UP))
+        return;
+
+    zd_iowrite32(chip, CR_GROUP_HASH_P1, 0);
+    zd_iowrite32(chip, CR_GROUP_HASH_P2, 0x80000000);
+
+    for (i=0, mc_list=dev->mc_list;
+         i<dev->mc_count; i++, mc_list = mc_list->next) {
+
+        u8 val;
+
+        printk(__FILE__ " : multicast " MAC_FMT "\n",
+               MAC_ARG(mc_list->dmi_addr));
+
+        val = mc_list->dmi_addr[5] >> 2;
+        if (val < 32) {
+            zd_ioread32(chip, CR_GROUP_HASH_P1, &tmp);
+            tmp |= 1 << val;
+            zd_iowrite32(chip, CR_GROUP_HASH_P1, tmp);
+        } else {
+            val -= 32;
+            zd_ioread32(chip, CR_GROUP_HASH_P2, &tmp);
+            tmp |= 1 << val;
+            zd_iowrite32(chip, CR_GROUP_HASH_P2, tmp);
+        }
+    }
+
+    if ((dev->flags & IFF_PROMISC) ||
+        (dev->flags & IFF_ALLMULTI)) {
+        zd_iowrite32(chip, CR_GROUP_HASH_P1, 0xffffffff);
+        zd_iowrite32(chip, CR_GROUP_HASH_P2, 0xffffffff);
+    }
+
+    zd_ioread32(chip, CR_GROUP_HASH_P1, &tmp);
+    printk(__FILE__ " : GroupHashP1 = %x\n", tmp);
+    zd_ioread32(chip, CR_GROUP_HASH_P2, &tmp);
+    printk(__FILE__ " : GroupHashP2 = %x\n", tmp);
+}
+
+/*
+  This is the function called by the netdev layer. This function is in an
+  atomic context and such registers cannot be set directly. As such, the work
+  is defered to the default workqueue, which will call zd_mac_keven().
+*/
+
+void zd_mac_set_multicast_list(struct net_device * dev)
+{
+    struct zd_mac * mac = zd_netdev_mac(dev);
+
+    set_bit(KEVENT_SET_MULTICAST, &mac->kevent_flags);
+    schedule_work(&mac->kevent);
+}
+
+void zd_mac_kevent(void * data)
+{
+    struct net_device * dev = (struct net_device *) data;
+    struct zd_mac * mac = zd_netdev_mac(dev);
+
+    if (test_bit(KEVENT_SET_MULTICAST, &mac->kevent_flags)) {
+        zd_mac_set_multicast_list_real(dev);
+        clear_bit(KEVENT_SET_MULTICAST, &mac->kevent_flags);
+    }
+}
+
 int zd_mac_set_mac_address(struct net_device *netdev, void *p)
 {
 	int r;
@@ -771,6 +856,14 @@ static int is_data_packet_for_us(struct
 		    IEEE80211_FCTL_FROMDS ||
 		    memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) != 0)
 			return 0;
+
+                /* broadcast or multicast packets are resent by the AP, so we
+                   ignore packets we sent */
+
+                if (is_multicast_ether_addr(hdr->addr1) && 
+                    (memcmp(hdr->addr3, netdev->dev_addr, ETH_ALEN) == 0)) {
+                    return 0;
+                }
 		break;
 	default:
 		ZD_ASSERT(ieee->iw_mode != IW_MODE_MONITOR);
@@ -778,8 +871,8 @@ static int is_data_packet_for_us(struct
 	}
 
 	return memcmp(hdr->addr1, netdev->dev_addr, ETH_ALEN) == 0 ||
-	       is_multicast_ether_addr(hdr->addr1) ||
-	       (netdev->flags & IFF_PROMISC);
+              is_multicast_ether_addr(hdr->addr1) ||
+              (netdev->flags & IFF_PROMISC);
 }
 
 /* Filters received packets. The function returns 1 if the packet should be
@@ -817,6 +910,34 @@ static int filter_rx(struct ieee80211_de
 		/* Ignore invalid short buffers */
 		if (length < sizeof(struct ieee80211_hdr_3addr))
 			return -EINVAL;
+
+                switch (fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) {
+                case 0: /* ToDS=0, FromDS=0 */
+                    printk(__FILE__ " : filter_rx DA=" MAC_FMT " SA=" MAC_FMT
+                           " BSSID=" MAC_FMT "\n",
+                           MAC_ARG(hdr->addr1), MAC_ARG(hdr->addr2),
+                           MAC_ARG(hdr->addr3));
+                    break;
+                case IEEE80211_FCTL_FROMDS: /* ToDS=0, FromDS=1 */
+                    printk(__FILE__ " : filter_rx DA=" MAC_FMT " BSSID=" MAC_FMT
+                           " SA=" MAC_FMT "\n",
+                           MAC_ARG(hdr->addr1), MAC_ARG(hdr->addr2),
+                           MAC_ARG(hdr->addr3));
+                    break;
+                case IEEE80211_FCTL_TODS: /* ToDS=1, FromDS=0 */
+                    printk(__FILE__ " : filter_rx BSSID=" MAC_FMT " SA=" MAC_FMT
+                           " DA=" MAC_FMT "\n",
+                           MAC_ARG(hdr->addr1), MAC_ARG(hdr->addr2),
+                           MAC_ARG(hdr->addr3));
+                    break;
+                case IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS : /* ToDS=1, FromDS=1 */
+                    printk(__FILE__ " : filter_rx RA=" MAC_FMT " TA=" MAC_FMT
+                           " DA=" MAC_FMT " SA=" MAC_FMT "\n",
+                           MAC_ARG(hdr->addr1), MAC_ARG(hdr->addr2),
+                           MAC_ARG(hdr->addr3), MAC_ARG(hdr->addr4));
+                    break;
+                }
+
 		return is_data_packet_for_us(ieee, hdr);
 	}
 
diff --git a/zd_mac.h b/zd_mac.h
index fa4e61f..c53d1a3 100644
--- a/zd_mac.h
+++ b/zd_mac.h
@@ -144,6 +144,10 @@ struct zd_mac {
 	u8 regdomain;
 	u8 default_regdomain;
 	u8 requested_channel;
+    
+    /* for workqueue */
+    struct work_struct kevent;
+    int kevent_flags;
 };
 
 static inline struct ieee80211_device *zd_mac_to_ieee80211(struct zd_mac *mac)
@@ -177,6 +181,8 @@ int zd_mac_init_hw(struct zd_mac *mac, u
 
 int zd_mac_open(struct net_device *netdev);
 int zd_mac_stop(struct net_device *netdev);
+
+void zd_mac_set_multicast_list(struct net_device *dev);
 int zd_mac_set_mac_address(struct net_device *dev, void *p);
 
 int zd_mac_rx(struct zd_mac *mac, const u8 *buffer, unsigned int length);
diff --git a/zd_netdev.c b/zd_netdev.c
index 440ef24..1c71c1f 100644
--- a/zd_netdev.c
+++ b/zd_netdev.c
@@ -253,7 +253,7 @@ struct net_device *zd_netdev_alloc(struc
 	netdev->open = zd_mac_open;
 	netdev->stop = zd_mac_stop;
 	/* netdev->get_stats = */
-	/* netdev->set_multicast_list = */
+	netdev->set_multicast_list = zd_mac_set_multicast_list;
 	netdev->set_mac_address = zd_mac_set_mac_address;
 	netdev->wireless_handlers = &iw_handler_def;
 	/* netdev->ethtool_ops = */
diff --git a/zd_usb.c b/zd_usb.c
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Zd1211-devs mailing list - http://zd1211.ath.cx/
Unsubscribe: https://lists.sourceforge.net/lists/listinfo/zd1211-devs

Reply via email to