ath9k: fix BSSID mask calculation
authorFelix Fietkau <nbd@openwrt.org>
Tue, 14 Sep 2010 16:37:19 +0000 (18:37 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 14 Sep 2010 20:14:26 +0000 (16:14 -0400)
At the time the .add_interface driver op is called, the interface has not
been marked as running yet, so ieee80211_iterate_active_interfaces will
not pass it to the iterator function.
Because of this, the calculated BSSID mask is wrong, which breaks multi-BSS
operation.

Additionally, the current way of comparing all addresses against each other
is pointless, as the hardware only uses the hardware MAC address and the BSSID
mask for matching the destination address, so all the address array
reallocation is completely unnecessary.

This patch simplifies the logic by setting the initial mask bytes to 0xff
and removing all bits in the iterator call that don't match the hardware MAC
address. It also calls the iterator for the vif that was passed to
add_interface()

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Tested-by: Ben Greear <greearb@candelatech.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/virtual.c

index 5fe570bb5c4c2d01d32f4d72d0296dd5c7d077b4..c8ff417f6c1066b8c672bda666d239f870c414d4 100644 (file)
@@ -719,7 +719,7 @@ static inline void ath_ahb_exit(void) {};
 void ath9k_ps_wakeup(struct ath_softc *sc);
 void ath9k_ps_restore(struct ath_softc *sc);
 
-void ath9k_set_bssid_mask(struct ieee80211_hw *hw);
+void ath9k_set_bssid_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
 int ath9k_wiphy_add(struct ath_softc *sc);
 int ath9k_wiphy_del(struct ath_wiphy *aphy);
 void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb);
index 1165f909ef04605edc283a50cf48cc58cd611781..d3f96e4520f19953815ee60b12da3bddb7d15f80 100644 (file)
@@ -1415,7 +1415,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
        sc->nvifs++;
 
        if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
-               ath9k_set_bssid_mask(hw);
+               ath9k_set_bssid_mask(hw, vif);
 
        if (sc->nvifs > 1)
                goto out; /* skip global settings for secondary vif */
index fd20241f57d8ef8239aa3cdc62bec57198e2ec46..ec7cf5ee56bc3ac9a850f7ee56fab95251b88d6d 100644 (file)
 #include "ath9k.h"
 
 struct ath9k_vif_iter_data {
-       int count;
-       u8 *addr;
+       const u8 *hw_macaddr;
+       u8 mask[ETH_ALEN];
 };
 
 static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
 {
        struct ath9k_vif_iter_data *iter_data = data;
-       u8 *nbuf;
-
-       nbuf = krealloc(iter_data->addr, (iter_data->count + 1) * ETH_ALEN,
-                       GFP_ATOMIC);
-       if (nbuf == NULL)
-               return;
+       int i;
 
-       memcpy(nbuf + iter_data->count * ETH_ALEN, mac, ETH_ALEN);
-       iter_data->addr = nbuf;
-       iter_data->count++;
+       for (i = 0; i < ETH_ALEN; i++)
+               iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
 }
 
-void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
+void ath9k_set_bssid_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
        struct ath_wiphy *aphy = hw->priv;
        struct ath_softc *sc = aphy->sc;
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath9k_vif_iter_data iter_data;
-       int i, j;
-       u8 mask[ETH_ALEN];
+       int i;
 
        /*
-        * Add primary MAC address even if it is not in active use since it
-        * will be configured to the hardware as the starting point and the
-        * BSSID mask will need to be changed if another address is active.
+        * Use the hardware MAC address as reference, the hardware uses it
+        * together with the BSSID mask when matching addresses.
         */
-       iter_data.addr = kmalloc(ETH_ALEN, GFP_ATOMIC);
-       if (iter_data.addr) {
-               memcpy(iter_data.addr, common->macaddr, ETH_ALEN);
-               iter_data.count = 1;
-       } else
-               iter_data.count = 0;
+       iter_data.hw_macaddr = common->macaddr;
+       memset(&iter_data.mask, 0xff, ETH_ALEN);
+
+       if (vif)
+               ath9k_vif_iter(&iter_data, vif->addr, vif);
 
        /* Get list of all active MAC addresses */
        spin_lock_bh(&sc->wiphy_lock);
@@ -71,31 +62,7 @@ void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
        }
        spin_unlock_bh(&sc->wiphy_lock);
 
-       /* Generate an address mask to cover all active addresses */
-       memset(mask, 0, ETH_ALEN);
-       for (i = 0; i < iter_data.count; i++) {
-               u8 *a1 = iter_data.addr + i * ETH_ALEN;
-               for (j = i + 1; j < iter_data.count; j++) {
-                       u8 *a2 = iter_data.addr + j * ETH_ALEN;
-                       mask[0] |= a1[0] ^ a2[0];
-                       mask[1] |= a1[1] ^ a2[1];
-                       mask[2] |= a1[2] ^ a2[2];
-                       mask[3] |= a1[3] ^ a2[3];
-                       mask[4] |= a1[4] ^ a2[4];
-                       mask[5] |= a1[5] ^ a2[5];
-               }
-       }
-
-       kfree(iter_data.addr);
-
-       /* Invert the mask and configure hardware */
-       common->bssidmask[0] = ~mask[0];
-       common->bssidmask[1] = ~mask[1];
-       common->bssidmask[2] = ~mask[2];
-       common->bssidmask[3] = ~mask[3];
-       common->bssidmask[4] = ~mask[4];
-       common->bssidmask[5] = ~mask[5];
-
+       memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
        ath_hw_setbssidmask(common);
 }