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