ath9k: Set BSSID mask based on configured interfaces
authorJouni Malinen <jouni.malinen@atheros.com>
Tue, 3 Mar 2009 17:23:27 +0000 (19:23 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 5 Mar 2009 19:39:44 +0000 (14:39 -0500)
Instead of using a hardcoded BSSID mask (mask for own addresses),
iterate through all active interfaces and determine the minimal mask
that covers all local addresses.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath9k/Makefile
drivers/net/wireless/ath9k/ath9k.h
drivers/net/wireless/ath9k/main.c
drivers/net/wireless/ath9k/virtual.c [new file with mode: 0644]

index 00629587b790f8cc037ea643ad52ba7a66d6a1f9..1a4d4eab6fe8575c73e6835ed21005bd59e51c18 100644 (file)
@@ -9,6 +9,7 @@ ath9k-y +=      hw.o \
                main.o \
                recv.o \
                xmit.o \
+               virtual.o \
                rc.o
 
 ath9k-$(CONFIG_PCI) += pci.o
index 09b2b008feba23e3238b3a03ae579965d86d073b..1598bac9242d2af6d188f6f2dff46adfac2ec352 100644 (file)
@@ -387,17 +387,6 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid
 /* VIFs */
 /********/
 
-/*
- * Define the scheme that we select MAC address for multiple
- * BSS on the same radio. The very first VIF will just use the MAC
- * address from the EEPROM. For the next 3 VIFs, we set the
- * U/L bit (bit 1) in MAC address, and use the next two bits as the
- * index of the VIF.
- */
-
-#define ATH_SET_VIF_BSSID_MASK(bssid_mask) \
-       ((bssid_mask)[0] &= ~(((ATH_BCBUF-1)<<2)|0x02))
-
 struct ath_vif {
        int av_bslot;
        enum nl80211_iftype av_opmode;
@@ -676,4 +665,7 @@ static inline void ath9k_ps_restore(struct ath_softc *sc)
                                          sc->sc_ah->restore_mode);
 }
 
+
+void ath9k_set_bssid_mask(struct ieee80211_hw *hw);
+
 #endif /* ATH9K_H */
index e43cee7907b21516b35c5cdcd50234bf5720d94b..599218def79900450690c32e6232b8158a8b3d8b 100644 (file)
@@ -1514,11 +1514,8 @@ static int ath_init(u16 devid, struct ath_softc *sc)
        ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL);
        sc->rx.defant = ath9k_hw_getdefantenna(ah);
 
-       if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) {
+       if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
                memcpy(sc->bssidmask, ath_bcast_mac, ETH_ALEN);
-               ATH_SET_VIF_BSSID_MASK(sc->bssidmask);
-               ath9k_hw_setbssidmask(sc);
-       }
 
        sc->beacon.slottime = ATH9K_SLOT_TIME_9;        /* default to short slot time */
 
@@ -2128,6 +2125,12 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
 
        mutex_lock(&sc->mutex);
 
+       if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) &&
+           sc->nvifs > 0) {
+               ret = -ENOBUFS;
+               goto out;
+       }
+
        switch (conf->type) {
        case NL80211_IFTYPE_STATION:
                ic_opmode = NL80211_IFTYPE_STATION;
@@ -2160,6 +2163,10 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
        avp->av_bslot = -1;
 
        sc->nvifs++;
+
+       if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
+               ath9k_set_bssid_mask(hw);
+
        if (sc->nvifs > 1)
                goto out; /* skip global settings for secondary vif */
 
diff --git a/drivers/net/wireless/ath9k/virtual.c b/drivers/net/wireless/ath9k/virtual.c
new file mode 100644 (file)
index 0000000..52d5021
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ath9k.h"
+
+struct ath9k_vif_iter_data {
+       int count;
+       u8 *addr;
+};
+
+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;
+
+       memcpy(nbuf + iter_data->count * ETH_ALEN, mac, ETH_ALEN);
+       iter_data->addr = nbuf;
+       iter_data->count++;
+}
+
+void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
+{
+       struct ath_softc *sc = hw->priv;
+       struct ath9k_vif_iter_data iter_data;
+       int i, j;
+       u8 mask[ETH_ALEN];
+
+       /*
+        * 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.
+        */
+       iter_data.addr = kmalloc(ETH_ALEN, GFP_ATOMIC);
+       if (iter_data.addr) {
+               memcpy(iter_data.addr, sc->sc_ah->macaddr, ETH_ALEN);
+               iter_data.count = 1;
+       } else
+               iter_data.count = 0;
+
+       /* Get list of all active MAC addresses */
+       ieee80211_iterate_active_interfaces_atomic(hw, ath9k_vif_iter,
+                                                  &iter_data);
+
+       /* 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 */
+       sc->bssidmask[0] = ~mask[0];
+       sc->bssidmask[1] = ~mask[1];
+       sc->bssidmask[2] = ~mask[2];
+       sc->bssidmask[3] = ~mask[3];
+       sc->bssidmask[4] = ~mask[4];
+       sc->bssidmask[5] = ~mask[5];
+
+       ath9k_hw_setbssidmask(sc);
+}