diff options
author | Ulrich Kunitz <kune@deine-taler.de> | 2006-11-30 19:58:07 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2006-12-05 19:31:32 -0500 |
commit | 9cdac9657fda58ae39c2bbc8be396f5530ed8398 (patch) | |
tree | f7a85659f05bf26e9012540f4f001c1e5b04fa59 /drivers/net | |
parent | ff9b99bcccee8449afd23ddc28f8b4b7aec996ea (diff) |
[PATCH] zd1211rw: Support for multicast addresses
Support for multicast adresses is implemented by supporting the
set_multicast_list() function of the network device. Address
filtering is supported by a group hash table in the device.
This is based on earlier work by Benoit Papillaut. Fixes multicast packet
reception and ipv6 connectivity:
http://bugzilla.kernel.org/show_bug.cgi?id=7424
http://bugzilla.kernel.org/show_bug.cgi?id=7425
Signed-off-by: Ulrich Kunitz <kune@deine-taler.de>
Signed-off-by: Daniel Drake <dsd@gentoo.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_chip.c | 13 | ||||
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_chip.h | 43 | ||||
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_mac.c | 44 | ||||
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_mac.h | 3 | ||||
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_netdev.c | 2 |
5 files changed, 102 insertions, 3 deletions
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c index 8be99ebbe1cd..77e11ddad836 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.c +++ b/drivers/net/wireless/zd1211rw/zd_chip.c | |||
@@ -1673,3 +1673,16 @@ int zd_rfwritev_cr_locked(struct zd_chip *chip, | |||
1673 | 1673 | ||
1674 | return 0; | 1674 | return 0; |
1675 | } | 1675 | } |
1676 | |||
1677 | int zd_chip_set_multicast_hash(struct zd_chip *chip, | ||
1678 | struct zd_mc_hash *hash) | ||
1679 | { | ||
1680 | struct zd_ioreq32 ioreqs[] = { | ||
1681 | { CR_GROUP_HASH_P1, hash->low }, | ||
1682 | { CR_GROUP_HASH_P2, hash->high }, | ||
1683 | }; | ||
1684 | |||
1685 | dev_dbg_f(zd_chip_dev(chip), "hash l 0x%08x h 0x%08x\n", | ||
1686 | ioreqs[0].value, ioreqs[1].value); | ||
1687 | return zd_iowrite32a(chip, ioreqs, ARRAY_SIZE(ioreqs)); | ||
1688 | } | ||
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h index ca892b9a6448..a4e3cee9b59d 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.h +++ b/drivers/net/wireless/zd1211rw/zd_chip.h | |||
@@ -390,10 +390,19 @@ | |||
390 | #define CR_BSSID_P1 CTL_REG(0x0618) | 390 | #define CR_BSSID_P1 CTL_REG(0x0618) |
391 | #define CR_BSSID_P2 CTL_REG(0x061C) | 391 | #define CR_BSSID_P2 CTL_REG(0x061C) |
392 | #define CR_BCN_PLCP_CFG CTL_REG(0x0620) | 392 | #define CR_BCN_PLCP_CFG CTL_REG(0x0620) |
393 | |||
394 | /* Group hash table for filtering incoming packets. | ||
395 | * | ||
396 | * The group hash table is 64 bit large and split over two parts. The first | ||
397 | * part is the lower part. The upper 6 bits of the last byte of the target | ||
398 | * address are used as index. Packets are received if the hash table bit is | ||
399 | * set. This is used for multicast handling, but for broadcasts (address | ||
400 | * ff:ff:ff:ff:ff:ff) the highest bit in the second table must also be set. | ||
401 | */ | ||
393 | #define CR_GROUP_HASH_P1 CTL_REG(0x0624) | 402 | #define CR_GROUP_HASH_P1 CTL_REG(0x0624) |
394 | #define CR_GROUP_HASH_P2 CTL_REG(0x0628) | 403 | #define CR_GROUP_HASH_P2 CTL_REG(0x0628) |
395 | #define CR_RX_TIMEOUT CTL_REG(0x062C) | ||
396 | 404 | ||
405 | #define CR_RX_TIMEOUT CTL_REG(0x062C) | ||
397 | /* Basic rates supported by the BSS. When producing ACK or CTS messages, the | 406 | /* Basic rates supported by the BSS. When producing ACK or CTS messages, the |
398 | * device will use a rate in this table that is less than or equal to the rate | 407 | * device will use a rate in this table that is less than or equal to the rate |
399 | * of the incoming frame which prompted the response */ | 408 | * of the incoming frame which prompted the response */ |
@@ -864,4 +873,36 @@ u8 zd_rx_strength_percent(u8 rssi); | |||
864 | 873 | ||
865 | u16 zd_rx_rate(const void *rx_frame, const struct rx_status *status); | 874 | u16 zd_rx_rate(const void *rx_frame, const struct rx_status *status); |
866 | 875 | ||
876 | struct zd_mc_hash { | ||
877 | u32 low; | ||
878 | u32 high; | ||
879 | }; | ||
880 | |||
881 | static inline void zd_mc_clear(struct zd_mc_hash *hash) | ||
882 | { | ||
883 | hash->low = 0; | ||
884 | /* The interfaces must always received broadcasts. | ||
885 | * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63. | ||
886 | */ | ||
887 | hash->high = 0x80000000; | ||
888 | } | ||
889 | |||
890 | static inline void zd_mc_add_all(struct zd_mc_hash *hash) | ||
891 | { | ||
892 | hash->low = hash->high = 0xffffffff; | ||
893 | } | ||
894 | |||
895 | static inline void zd_mc_add_addr(struct zd_mc_hash *hash, u8 *addr) | ||
896 | { | ||
897 | unsigned int i = addr[5] >> 2; | ||
898 | if (i < 32) { | ||
899 | hash->low |= 1 << i; | ||
900 | } else { | ||
901 | hash->high |= 1 << (i-32); | ||
902 | } | ||
903 | } | ||
904 | |||
905 | int zd_chip_set_multicast_hash(struct zd_chip *chip, | ||
906 | struct zd_mc_hash *hash); | ||
907 | |||
867 | #endif /* _ZD_CHIP_H */ | 908 | #endif /* _ZD_CHIP_H */ |
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index bd1593e13f8f..1dd3f766ff49 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c | |||
@@ -39,6 +39,8 @@ static void housekeeping_init(struct zd_mac *mac); | |||
39 | static void housekeeping_enable(struct zd_mac *mac); | 39 | static void housekeeping_enable(struct zd_mac *mac); |
40 | static void housekeeping_disable(struct zd_mac *mac); | 40 | static void housekeeping_disable(struct zd_mac *mac); |
41 | 41 | ||
42 | static void set_multicast_hash_handler(void *mac_ptr); | ||
43 | |||
42 | int zd_mac_init(struct zd_mac *mac, | 44 | int zd_mac_init(struct zd_mac *mac, |
43 | struct net_device *netdev, | 45 | struct net_device *netdev, |
44 | struct usb_interface *intf) | 46 | struct usb_interface *intf) |
@@ -55,6 +57,8 @@ int zd_mac_init(struct zd_mac *mac, | |||
55 | softmac_init(ieee80211_priv(netdev)); | 57 | softmac_init(ieee80211_priv(netdev)); |
56 | zd_chip_init(&mac->chip, netdev, intf); | 58 | zd_chip_init(&mac->chip, netdev, intf); |
57 | housekeeping_init(mac); | 59 | housekeeping_init(mac); |
60 | INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler, | ||
61 | mac); | ||
58 | return 0; | 62 | return 0; |
59 | } | 63 | } |
60 | 64 | ||
@@ -136,6 +140,7 @@ out: | |||
136 | 140 | ||
137 | void zd_mac_clear(struct zd_mac *mac) | 141 | void zd_mac_clear(struct zd_mac *mac) |
138 | { | 142 | { |
143 | flush_workqueue(zd_workqueue); | ||
139 | zd_chip_clear(&mac->chip); | 144 | zd_chip_clear(&mac->chip); |
140 | ZD_ASSERT(!spin_is_locked(&mac->lock)); | 145 | ZD_ASSERT(!spin_is_locked(&mac->lock)); |
141 | ZD_MEMCLEAR(mac, sizeof(struct zd_mac)); | 146 | ZD_MEMCLEAR(mac, sizeof(struct zd_mac)); |
@@ -256,6 +261,42 @@ int zd_mac_set_mac_address(struct net_device *netdev, void *p) | |||
256 | return 0; | 261 | return 0; |
257 | } | 262 | } |
258 | 263 | ||
264 | static void set_multicast_hash_handler(void *mac_ptr) | ||
265 | { | ||
266 | struct zd_mac *mac = mac_ptr; | ||
267 | struct zd_mc_hash hash; | ||
268 | |||
269 | spin_lock_irq(&mac->lock); | ||
270 | hash = mac->multicast_hash; | ||
271 | spin_unlock_irq(&mac->lock); | ||
272 | |||
273 | zd_chip_set_multicast_hash(&mac->chip, &hash); | ||
274 | } | ||
275 | |||
276 | void zd_mac_set_multicast_list(struct net_device *dev) | ||
277 | { | ||
278 | struct zd_mc_hash hash; | ||
279 | struct zd_mac *mac = zd_netdev_mac(dev); | ||
280 | struct dev_mc_list *mc; | ||
281 | unsigned long flags; | ||
282 | |||
283 | if (dev->flags & (IFF_PROMISC|IFF_ALLMULTI)) { | ||
284 | zd_mc_add_all(&hash); | ||
285 | } else { | ||
286 | zd_mc_clear(&hash); | ||
287 | for (mc = dev->mc_list; mc; mc = mc->next) { | ||
288 | dev_dbg_f(zd_mac_dev(mac), "mc addr " MAC_FMT "\n", | ||
289 | MAC_ARG(mc->dmi_addr)); | ||
290 | zd_mc_add_addr(&hash, mc->dmi_addr); | ||
291 | } | ||
292 | } | ||
293 | |||
294 | spin_lock_irqsave(&mac->lock, flags); | ||
295 | mac->multicast_hash = hash; | ||
296 | spin_unlock_irqrestore(&mac->lock, flags); | ||
297 | queue_work(zd_workqueue, &mac->set_multicast_hash_work); | ||
298 | } | ||
299 | |||
259 | int zd_mac_set_regdomain(struct zd_mac *mac, u8 regdomain) | 300 | int zd_mac_set_regdomain(struct zd_mac *mac, u8 regdomain) |
260 | { | 301 | { |
261 | int r; | 302 | int r; |
@@ -930,7 +971,8 @@ static int is_data_packet_for_us(struct ieee80211_device *ieee, | |||
930 | } | 971 | } |
931 | 972 | ||
932 | return memcmp(hdr->addr1, netdev->dev_addr, ETH_ALEN) == 0 || | 973 | return memcmp(hdr->addr1, netdev->dev_addr, ETH_ALEN) == 0 || |
933 | is_multicast_ether_addr(hdr->addr1) || | 974 | (is_multicast_ether_addr(hdr->addr1) && |
975 | memcmp(hdr->addr3, netdev->dev_addr, ETH_ALEN) != 0) || | ||
934 | (netdev->flags & IFF_PROMISC); | 976 | (netdev->flags & IFF_PROMISC); |
935 | } | 977 | } |
936 | 978 | ||
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h index 5dcfb251f02e..77f1268080ed 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.h +++ b/drivers/net/wireless/zd1211rw/zd_mac.h | |||
@@ -133,6 +133,8 @@ struct zd_mac { | |||
133 | struct iw_statistics iw_stats; | 133 | struct iw_statistics iw_stats; |
134 | 134 | ||
135 | struct housekeeping housekeeping; | 135 | struct housekeeping housekeeping; |
136 | struct work_struct set_multicast_hash_work; | ||
137 | struct zd_mc_hash multicast_hash; | ||
136 | struct work_struct set_rts_cts_work; | 138 | struct work_struct set_rts_cts_work; |
137 | struct work_struct set_basic_rates_work; | 139 | struct work_struct set_basic_rates_work; |
138 | 140 | ||
@@ -189,6 +191,7 @@ int zd_mac_init_hw(struct zd_mac *mac, u8 device_type); | |||
189 | int zd_mac_open(struct net_device *netdev); | 191 | int zd_mac_open(struct net_device *netdev); |
190 | int zd_mac_stop(struct net_device *netdev); | 192 | int zd_mac_stop(struct net_device *netdev); |
191 | int zd_mac_set_mac_address(struct net_device *dev, void *p); | 193 | int zd_mac_set_mac_address(struct net_device *dev, void *p); |
194 | void zd_mac_set_multicast_list(struct net_device *netdev); | ||
192 | 195 | ||
193 | int zd_mac_rx(struct zd_mac *mac, const u8 *buffer, unsigned int length); | 196 | int zd_mac_rx(struct zd_mac *mac, const u8 *buffer, unsigned int length); |
194 | 197 | ||
diff --git a/drivers/net/wireless/zd1211rw/zd_netdev.c b/drivers/net/wireless/zd1211rw/zd_netdev.c index 60f1b0f6d45b..8bda48de31ef 100644 --- a/drivers/net/wireless/zd1211rw/zd_netdev.c +++ b/drivers/net/wireless/zd1211rw/zd_netdev.c | |||
@@ -242,7 +242,7 @@ struct net_device *zd_netdev_alloc(struct usb_interface *intf) | |||
242 | netdev->open = zd_mac_open; | 242 | netdev->open = zd_mac_open; |
243 | netdev->stop = zd_mac_stop; | 243 | netdev->stop = zd_mac_stop; |
244 | /* netdev->get_stats = */ | 244 | /* netdev->get_stats = */ |
245 | /* netdev->set_multicast_list = */ | 245 | netdev->set_multicast_list = zd_mac_set_multicast_list; |
246 | netdev->set_mac_address = zd_mac_set_mac_address; | 246 | netdev->set_mac_address = zd_mac_set_mac_address; |
247 | netdev->wireless_handlers = &iw_handler_def; | 247 | netdev->wireless_handlers = &iw_handler_def; |
248 | /* netdev->ethtool_ops = */ | 248 | /* netdev->ethtool_ops = */ |