summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath9k/virtual.c
diff options
context:
space:
mode:
authorJouni Malinen <jouni.malinen@atheros.com>2009-03-03 19:23:29 +0200
committerJohn W. Linville <linville@tuxdriver.com>2009-03-05 14:39:45 -0500
commitc52f33d05e5f8d59f02722fbc308f5f391575ca5 (patch)
tree19c6d56f3594e8b43d725b5d919bb775921fde38 /drivers/net/wireless/ath9k/virtual.c
parentbce048d77dff3dcfd75d54dc38580c81baa95853 (diff)
ath9k: Add support for multiple secondary virtual wiphys
The new struct ath_softc::sec_wiphy array is used to store information about virtual wiphys and select which wiphy is used in calls to mac80211. Each virtual wiphy will be assigned a different MAC address based on the virtual wiphy index. Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath9k/virtual.c')
-rw-r--r--drivers/net/wireless/ath9k/virtual.c90
1 files changed, 89 insertions, 1 deletions
diff --git a/drivers/net/wireless/ath9k/virtual.c b/drivers/net/wireless/ath9k/virtual.c
index a91f2f1c911..67bcb9343ca 100644
--- a/drivers/net/wireless/ath9k/virtual.c
+++ b/drivers/net/wireless/ath9k/virtual.c
@@ -57,8 +57,16 @@ void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
iter_data.count = 0;
/* Get list of all active MAC addresses */
- ieee80211_iterate_active_interfaces_atomic(hw, ath9k_vif_iter,
+ spin_lock_bh(&sc->wiphy_lock);
+ ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter,
&iter_data);
+ for (i = 0; i < sc->num_sec_wiphy; i++) {
+ if (sc->sec_wiphy[i] == NULL)
+ continue;
+ ieee80211_iterate_active_interfaces_atomic(
+ sc->sec_wiphy[i]->hw, ath9k_vif_iter, &iter_data);
+ }
+ spin_unlock_bh(&sc->wiphy_lock);
/* Generate an address mask to cover all active addresses */
memset(mask, 0, ETH_ALEN);
@@ -87,3 +95,83 @@ void ath9k_set_bssid_mask(struct ieee80211_hw *hw)
ath9k_hw_setbssidmask(sc);
}
+
+int ath9k_wiphy_add(struct ath_softc *sc)
+{
+ int i, error;
+ struct ath_wiphy *aphy;
+ struct ieee80211_hw *hw;
+ u8 addr[ETH_ALEN];
+
+ hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy), &ath9k_ops);
+ if (hw == NULL)
+ return -ENOMEM;
+
+ spin_lock_bh(&sc->wiphy_lock);
+ for (i = 0; i < sc->num_sec_wiphy; i++) {
+ if (sc->sec_wiphy[i] == NULL)
+ break;
+ }
+
+ if (i == sc->num_sec_wiphy) {
+ /* No empty slot available; increase array length */
+ struct ath_wiphy **n;
+ n = krealloc(sc->sec_wiphy,
+ (sc->num_sec_wiphy + 1) *
+ sizeof(struct ath_wiphy *),
+ GFP_ATOMIC);
+ if (n == NULL) {
+ spin_unlock_bh(&sc->wiphy_lock);
+ ieee80211_free_hw(hw);
+ return -ENOMEM;
+ }
+ n[i] = NULL;
+ sc->sec_wiphy = n;
+ sc->num_sec_wiphy++;
+ }
+
+ SET_IEEE80211_DEV(hw, sc->dev);
+
+ aphy = hw->priv;
+ aphy->sc = sc;
+ aphy->hw = hw;
+ sc->sec_wiphy[i] = aphy;
+ spin_unlock_bh(&sc->wiphy_lock);
+
+ memcpy(addr, sc->sc_ah->macaddr, ETH_ALEN);
+ addr[0] |= 0x02; /* Locally managed address */
+ /*
+ * XOR virtual wiphy index into the least significant bits to generate
+ * a different MAC address for each virtual wiphy.
+ */
+ addr[5] ^= i & 0xff;
+ addr[4] ^= (i & 0xff00) >> 8;
+ addr[3] ^= (i & 0xff0000) >> 16;
+
+ SET_IEEE80211_PERM_ADDR(hw, addr);
+
+ ath_set_hw_capab(sc, hw);
+
+ error = ieee80211_register_hw(hw);
+
+ return error;
+}
+
+int ath9k_wiphy_del(struct ath_wiphy *aphy)
+{
+ struct ath_softc *sc = aphy->sc;
+ int i;
+
+ spin_lock_bh(&sc->wiphy_lock);
+ for (i = 0; i < sc->num_sec_wiphy; i++) {
+ if (aphy == sc->sec_wiphy[i]) {
+ sc->sec_wiphy[i] = NULL;
+ spin_unlock_bh(&sc->wiphy_lock);
+ ieee80211_unregister_hw(aphy->hw);
+ ieee80211_free_hw(aphy->hw);
+ return 0;
+ }
+ }
+ spin_unlock_bh(&sc->wiphy_lock);
+ return -ENOENT;
+}