ath9k: fix BSSID mask calculation

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>
This commit is contained in:
Felix Fietkau 2010-09-14 18:37:19 +02:00 committed by John W. Linville
parent 4a0e8ecca4
commit 31a01645c0
3 changed files with 17 additions and 50 deletions

View 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);

View 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 */

View File

@ -19,45 +19,36 @@
#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;
int i;
nbuf = krealloc(iter_data->addr, (iter_data->count + 1) * ETH_ALEN,
GFP_ATOMIC);
if (nbuf == NULL)
return;
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);
}