Benoit,

I appreciate, that you have actually provided a patch. I will probably not 
use the code at is, I don't like particularly the kevent hacks here. It's simpler
and not less prone to race conditions to handle each event in its own
work function. So everything related to kevent needs to be removed. 
Given the structure of our driver we would certainly implement
a zd_chip function, which gets the list of muliticast addresses and writes
the group hash table.

Your patch doesn't work however, because you didn't implement loopback
handling. The easy way would be to remove all inbound packets with a source
address, that is equal to our own. It would be perfect only to remove it, if we
actually send out a multicast packet some time before.

Cheers,

Uli


Am 23.11.2006 um 21:38 wrote Benoit PAPILLAULT:

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

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..3698ed9 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;
@@ -778,8 +863,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
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

--
Uli Kunitz