Patrick McHardy has been working on patches for this feature
for me.  I've hacked on it a small bit too, so he doesn't get all the blame.

I'm attaching a patch that is mostly working:

Tested so far:

120 stations:  Works OK with no encryption
10 stations:  Works OK with WEP
WPA has issues, at least when running one supplicant per interface.

Haven't tested VAPs yet.

Known bugs:
lockdep warning on some race with handler callbacks.
soft-lockup in irq occassionally
WPA doesn't work quite right with multiple supplicants, at least.


To create a virtual station:

modprobe ath5k with nohwaccel=1  (I think that's the name...test system is
currently locked and waiting debugging :P)

iw dev wmaster0 interface add sta0 type station
ip link set sta0 address 00:11:22:33:44:01

To create a vap:
iw dev wmaster0 interface add vap0 type __ap

You probably have to beat udev senseless so that it doesn't rename things for 
you.


I have this patch to iw:

[gree...@fs2 iw]$ git diff
diff --git a/nl80211.h b/nl80211.h
index f6e5637..08c1462 100644
--- a/nl80211.h
+++ b/nl80211.h
@@ -195,8 +195,6 @@ enum nl80211_commands {
        NL80211_CMD_GET_MESH_PARAMS,
        NL80211_CMD_SET_MESH_PARAMS,

-       NL80211_CMD_SET_MGMT_EXTRA_IE,
-
        NL80211_CMD_GET_REG,

        NL80211_CMD_GET_SCAN,


Using top-of tree hostap (from a few weeks ago)


I would welcome inclusion of any/all of this code and/or help
with debugging if anyone wants to give it a try.

Thanks,
Ben

--
Ben Greear <gree...@candelatech.com>
Candela Technologies Inc  http://www.candelatech.com

diff --git a/drivers/net/wireless/ath5k/base.c 
b/drivers/net/wireless/ath5k/base.c
index 6cf69d3..3d53284 100644
--- a/drivers/net/wireless/ath5k/base.c
+++ b/drivers/net/wireless/ath5k/base.c
@@ -241,13 +241,16 @@ static int ath5k_get_tx_stats(struct ieee80211_hw *hw,
 static u64 ath5k_get_tsf(struct ieee80211_hw *hw);
 static void ath5k_reset_tsf(struct ieee80211_hw *hw);
 static int ath5k_beacon_update(struct ath5k_softc *sc,
+               struct ieee80211_vif *vif,
                struct sk_buff *skb);
 static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
                struct ieee80211_vif *vif,
                struct ieee80211_bss_conf *bss_conf,
                u32 changes);
+static void ath5k_update_bssid_mask(struct ath5k_softc *sc);
 
 static struct ieee80211_ops ath5k_hw_ops = {
+       .drvname        = KBUILD_MODNAME,
        .tx             = ath5k_tx,
        .start          = ath5k_start,
        .stop           = ath5k_stop,
@@ -331,6 +334,7 @@ static void         ath5k_tx_processq(struct ath5k_softc 
*sc,
 static void    ath5k_tasklet_tx(unsigned long data);
 /* Beacon handling */
 static int     ath5k_beacon_setup(struct ath5k_softc *sc,
+                                       struct ath5k_vif *avf,
                                        struct ath5k_buf *bf);
 static void    ath5k_beacon_send(struct ath5k_softc *sc);
 static void    ath5k_beacon_config(struct ath5k_softc *sc);
@@ -507,6 +511,7 @@ ath5k_pci_probe(struct pci_dev *pdev,
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_STATION) |
                BIT(NL80211_IFTYPE_ADHOC) |
+               BIT(NL80211_IFTYPE_AP) |
                BIT(NL80211_IFTYPE_MESH_POINT);
 
        hw->extra_tx_headroom = 2;
@@ -554,6 +559,8 @@ ath5k_pci_probe(struct pci_dev *pdev,
                hw->max_rate_tries = 11;
        }
 
+       hw->vif_data_size = sizeof(struct ath5k_vif);
+
        /* Finish private driver data initialization */
        ret = ath5k_attach(pdev, hw);
        if (ret)
@@ -791,9 +798,8 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
        }
 
        SET_IEEE80211_PERM_ADDR(hw, mac);
-       /* All MAC address bits matter for ACKs */
-       memset(sc->bssidmask, 0xff, ETH_ALEN);
-       ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
+       memcpy(&sc->lladdr, mac, ETH_ALEN);
+       ath5k_update_bssid_mask(sc);
 
        ret = ieee80211_register_hw(hw);
        if (ret) {
@@ -1034,11 +1040,11 @@ ath5k_setup_bands(struct ieee80211_hw *hw)
 static int
 ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan)
 {
-       ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "(%u MHz) -> (%u MHz)\n",
-               sc->curchan->center_freq, chan->center_freq);
-
        if (chan->center_freq != sc->curchan->center_freq ||
                chan->hw_value != sc->curchan->hw_value) {
+               printk("%s: (%u MHz) -> (%u MHz)\n", current->comm,
+                      sc->curchan->center_freq, chan->center_freq);
+
 
                sc->curchan = chan;
                sc->curband = &sc->sbands[chan->band];
@@ -1312,10 +1318,13 @@ ath5k_desc_alloc(struct ath5k_softc *sc, struct pci_dev 
*pdev)
                list_add_tail(&bf->list, &sc->txbuf);
        }
 
-       /* beacon buffer */
-       bf->desc = ds;
-       bf->daddr = da;
-       sc->bbuf = bf;
+       /* beacon buffers */
+       INIT_LIST_HEAD(&sc->bcbuf);
+       for (i = 0; i < ATH_BCBUF; i++, bf++, ds++, da += sizeof(*ds)) {
+               bf->desc = ds;
+               bf->daddr = da;
+               list_add_tail(&bf->list, &sc->bcbuf);
+       }
 
        return 0;
 err_free:
@@ -1330,11 +1339,12 @@ ath5k_desc_free(struct ath5k_softc *sc, struct pci_dev 
*pdev)
 {
        struct ath5k_buf *bf;
 
-       ath5k_txbuf_free(sc, sc->bbuf);
        list_for_each_entry(bf, &sc->txbuf, list)
                ath5k_txbuf_free(sc, bf);
        list_for_each_entry(bf, &sc->rxbuf, list)
                ath5k_txbuf_free(sc, bf);
+       list_for_each_entry(bf, &sc->bcbuf, list)
+               ath5k_txbuf_free(sc, bf);
 
        /* Free memory associated with all descriptors */
        pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);
@@ -1958,7 +1968,8 @@ ath5k_tasklet_tx(unsigned long data)
  * Setup the beacon frame for transmit.
  */
 static int
-ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
+ath5k_beacon_setup(struct ath5k_softc *sc, struct ath5k_vif *avf,
+                  struct ath5k_buf *bf)
 {
        struct sk_buff *skb = bf->skb;
        struct  ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -2024,16 +2035,16 @@ err_unmap:
 static void
 ath5k_beacon_send(struct ath5k_softc *sc)
 {
-       struct ath5k_buf *bf = sc->bbuf;
+       struct ath5k_vif *avf;
+       struct ath5k_buf *bf;
        struct ath5k_hw *ah = sc->ah;
+       u64 tsf;
+       u32 tsftu;
+       u16 intval;
+       int slot, if_id;
 
        ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n");
 
-       if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION ||
-                       sc->opmode == NL80211_IFTYPE_MONITOR)) {
-               ATH5K_WARN(sc, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL);
-               return;
-       }
        /*
         * Check if the previous beacon has gone out.  If
         * not don't don't try to post another, skip this
@@ -2060,6 +2071,28 @@ ath5k_beacon_send(struct ath5k_softc *sc)
                sc->bmisscount = 0;
        }
 
+       intval = sc->bintval ? sc->bintval : ATH5K_DEFAULT_BINTVAL;
+
+       tsf = ath5k_hw_get_tsf64(ah);
+       tsftu = TSF_TO_TU(tsf);
+       slot = ((tsftu % intval) * ATH_BCBUF) / intval;
+       if_id = sc->bslot[(slot + 1) % ATH_BCBUF];
+
+       pr_debug("tsf %llx tsftu %x intval %u slot %u if_id %x\n",
+                (unsigned long long)tsf, tsftu, intval, slot, if_id);
+
+       if (if_id != ATH5K_IF_ID_ANY)
+               avf = (void *)sc->vifs[if_id]->drv_priv;
+       else
+               return;
+
+       bf = avf->bbuf;
+       if (unlikely(bf->skb == NULL || sc->opmode == NL80211_IFTYPE_STATION ||
+                       sc->opmode == NL80211_IFTYPE_MONITOR)) {
+               ATH5K_WARN(sc, "bf=%p bf_skb=%p\n", bf, bf ? bf->skb : NULL);
+               return;
+       }
+
        /*
         * Stop any current dma and put the new frame on the queue.
         * This should never fail since we check above that no frames
@@ -2103,6 +2136,12 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 
bc_tsf)
        u64 hw_tsf;
 
        intval = sc->bintval & AR5K_BEACON_PERIOD;
+       if (sc->opmode == NL80211_IFTYPE_AP) {
+               intval /= ATH_BCBUF;            /* staggered multi-bss beacons 
*/
+               if (intval < 15)
+                       printk("ath5k: intval %u is too low, min 15\n", intval);
+       }
+
        if (WARN_ON(!intval))
                return;
 
@@ -2286,6 +2325,9 @@ ath5k_init(struct ath5k_softc *sc, bool is_resume)
        /* Set ack to be sent at low bit-rates */
        ath5k_hw_set_ack_bitrate_high(ah, false);
 
+       for (i = 0; i < ARRAY_SIZE(sc->bslot); i++)
+               sc->bslot[i] = ATH5K_IF_ID_ANY;
+
        mod_timer(&sc->calib_tim, round_jiffies(jiffies +
                        msecs_to_jiffies(ath5k_calinterval * 1000)));
 
@@ -2374,7 +2416,7 @@ ath5k_stop_hw(struct ath5k_softc *sc, bool is_suspend)
                        ath5k_hw_set_power(sc->ah, AR5K_PM_FULL_SLEEP, true, 0);
                }
        }
-       ath5k_txbuf_free(sc, sc->bbuf);
+
        if (!is_suspend)
                __clear_bit(ATH_STAT_STARTED, sc->status);
 
@@ -2784,19 +2826,57 @@ static void ath5k_stop(struct ieee80211_hw *hw)
        ath5k_stop_hw(hw->priv, false);
 }
 
+static void ath5k_update_bssid_mask(struct ath5k_softc *sc)
+{
+       struct ath5k_vif *avf;
+       unsigned int i, j;
+
+       /*
+        * This doesn't include the address of the default STA device in case
+        * it is reconfigured since for some reason it is not created through
+        * ->add_interface().
+        */
+       memset(sc->bssidmask, 0xff, ETH_ALEN);
+       for (i = 0; i < ATH5K_VIF_MAX; i++) {
+               if (sc->vifs[i] == NULL)
+                       continue;
+               avf = (void *)sc->vifs[i]->drv_priv;
+               for (j = 0; j < ETH_ALEN; j++) {
+                       sc->bssidmask[j] &= ~(sc->lladdr[j] ^ avf->lladdr[j]);
+                       sc->bssidmask[j] &= ~(sc->lladdr[j] ^ avf->bssid[j]);
+               }
+       }
+       ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask);
+}
+
 static int ath5k_add_interface(struct ieee80211_hw *hw,
                struct ieee80211_if_init_conf *conf)
 {
        struct ath5k_softc *sc = hw->priv;
+       struct ath5k_hw *ah = sc->ah;
+       struct ath5k_vif *avf = (void *)conf->vif->drv_priv;
+       unsigned int i;
        int ret;
 
        mutex_lock(&sc->lock);
-       if (sc->vif) {
-               ret = 0;
+       if (sc->nvifs > 1 && !modparam_nohwcrypt) {
+               pr_err("ath5k: can not add multiple virtual interfaces with 
hardware encryption\n");
+               ret = -EOPNOTSUPP;
                goto end;
        }
-
-       sc->vif = conf->vif;
+       if (sc->nvifs >= ATH5K_VIF_MAX ||
+           (conf->type == NL80211_IFTYPE_AP && sc->nbcnvifs >= ATH_BCBUF)) {
+               ret = -ELNRNG;
+               goto end;
+       }
+       for (i = 0; i < ATH5K_VIF_MAX; i++) {
+               if (sc->vifs[i] != NULL)
+                       continue;
+               sc->vifs[i] = conf->vif;
+               avf->if_id = i;
+               break;
+       }
+       sc->nvifs++;
 
        switch (conf->type) {
        case NL80211_IFTYPE_AP:
@@ -2804,17 +2884,38 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
        case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_MONITOR:
-               sc->opmode = conf->type;
+               avf->opmode = conf->type;
                break;
        default:
                ret = -EOPNOTSUPP;
                goto end;
        }
 
+       /* Set combined mode - when APs are configured, operate in AP mode.
+        * Otherwise use the mode of the new interface. This can currently
+        * only deal with combinations of APs and STAs I think ...
+        */
+       if (sc->nbcnvifs)
+               sc->opmode = NL80211_IFTYPE_AP;
+       else
+               sc->opmode = conf->type;
+
+       ah->ah_op_mode = sc->opmode;
+       ath5k_hw_set_opmode(ah);
+
        /* Set to a reasonable value. Note that this will
         * be set to mac80211's value at ath5k_config(). */
-       sc->bintval = 1000;
-       ath5k_hw_set_lladdr(sc->ah, conf->mac_addr);
+       sc->bintval = ATH5K_DEFAULT_BINTVAL;
+
+       /* Any MAC address is finee, all others are included through the
+        * filter.
+        */
+       memcpy(&sc->lladdr, conf->mac_addr, ETH_ALEN);
+       ath5k_hw_set_lladdr(sc->ah, sc->lladdr);
+
+       memcpy(&avf->lladdr, conf->mac_addr, ETH_ALEN);
+       memcpy(&avf->bssid, conf->mac_addr, ETH_ALEN);
+       ath5k_update_bssid_mask(sc);
 
        ret = 0;
 end:
@@ -2827,15 +2928,32 @@ ath5k_remove_interface(struct ieee80211_hw *hw,
                        struct ieee80211_if_init_conf *conf)
 {
        struct ath5k_softc *sc = hw->priv;
-       u8 mac[ETH_ALEN] = {};
+       struct ath5k_vif *avf = (void *)conf->vif->drv_priv, *avf2;
+       u8 null_mac[ETH_ALEN] = {}, *mac = null_mac;
+       unsigned int i;
 
        mutex_lock(&sc->lock);
-       if (sc->vif != conf->vif)
-               goto end;
+       sc->vifs[avf->if_id] = NULL;
+       sc->nvifs--;
+
+       for (i = 0; i < ATH5K_VIF_MAX; i++) {
+               if (sc->vifs[i] == NULL)
+                       continue;
+               avf2 = (void *)sc->vifs[i]->drv_priv;
+               mac = avf2->lladdr;
+               break;
+       }
+
+       if (avf->bbuf) {
+               ath5k_txbuf_free(sc, avf->bbuf);
+               list_add_tail(&avf->bbuf->list, &sc->bcbuf);
+               sc->bslot[avf->bslot] = ATH5K_IF_ID_ANY;
+               sc->nbcnvifs--;
+               avf->bbuf = NULL;
+       }
 
        ath5k_hw_set_lladdr(sc->ah, mac);
-       sc->vif = NULL;
-end:
+       ath5k_update_bssid_mask(sc);
        mutex_unlock(&sc->lock);
 }
 
@@ -2866,16 +2984,17 @@ ath5k_config_interface(struct ieee80211_hw *hw, struct 
ieee80211_vif *vif,
 {
        struct ath5k_softc *sc = hw->priv;
        struct ath5k_hw *ah = sc->ah;
+       struct ath5k_vif *avf = (void *)vif->drv_priv;
        int ret;
 
        mutex_lock(&sc->lock);
-       if (sc->vif != vif) {
-               ret = -EIO;
-               goto unlock;
-       }
        if (conf->changed & IEEE80211_IFCC_BSSID && conf->bssid) {
                /* Cache for later use during resets */
                memcpy(ah->ah_bssid, conf->bssid, ETH_ALEN);
+
+               memcpy(avf->bssid, conf->bssid, ETH_ALEN);
+               ath5k_update_bssid_mask(sc);
+
                /* XXX: assoc id is set to 0 for now, mac80211 doesn't have
                 * a clean way of letting us retrieve this yet. */
                ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
@@ -2890,7 +3009,7 @@ ath5k_config_interface(struct ieee80211_hw *hw, struct 
ieee80211_vif *vif,
                        ret = -ENOMEM;
                        goto unlock;
                }
-               ath5k_beacon_update(sc, beacon);
+               ath5k_beacon_update(sc, vif, beacon);
        }
        mutex_unlock(&sc->lock);
 
@@ -3129,19 +3248,43 @@ ath5k_reset_tsf(struct ieee80211_hw *hw)
 }
 
 static int
-ath5k_beacon_update(struct ath5k_softc *sc, struct sk_buff *skb)
+ath5k_beacon_update(struct ath5k_softc *sc, struct ieee80211_vif *vif,
+                   struct sk_buff *skb)
 {
+       struct ath5k_vif *avf = (void *)vif->drv_priv;
        unsigned long flags;
        int ret;
 
        ath5k_debug_dump_skb(sc, skb, "BC  ", 1);
 
        spin_lock_irqsave(&sc->block, flags);
-       ath5k_txbuf_free(sc, sc->bbuf);
-       sc->bbuf->skb = skb;
-       ret = ath5k_beacon_setup(sc, sc->bbuf);
+       if (!avf->bbuf) {
+               WARN_ON(list_empty(&sc->bcbuf));
+               avf->bbuf = list_first_entry(&sc->bcbuf, struct ath5k_buf, 
list);
+               list_del(&avf->bbuf->list);
+
+               /* Assign the vap to a beacon xmit slot. */
+               if (avf->opmode == NL80211_IFTYPE_AP) {
+                       int slot;
+
+                       avf->bslot = 0;
+                       for (slot = 0; slot < ATH_BCBUF; slot++) {
+                               if (sc->bslot[slot] == ATH5K_IF_ID_ANY) {
+                                       avf->bslot = slot;
+                                       break;
+                               }
+                       }
+                       BUG_ON(sc->bslot[avf->bslot] != ATH5K_IF_ID_ANY);
+                       sc->bslot[avf->bslot] = avf->if_id;
+                       sc->nbcnvifs++;
+               }
+       }
+
+       ath5k_txbuf_free(sc, avf->bbuf);
+       avf->bbuf->skb = skb;
+       ret = ath5k_beacon_setup(sc, avf, avf->bbuf);
        if (ret)
-               sc->bbuf->skb = NULL;
+               avf->bbuf->skb = NULL;
        spin_unlock_irqrestore(&sc->block, flags);
        if (!ret) {
                ath5k_beacon_config(sc);
diff --git a/drivers/net/wireless/ath5k/base.h 
b/drivers/net/wireless/ath5k/base.h
index d86ab39..3af9d1b 100644
--- a/drivers/net/wireless/ath5k/base.h
+++ b/drivers/net/wireless/ath5k/base.h
@@ -52,7 +52,9 @@
 
 #define        ATH_RXBUF       40              /* number of RX buffers */
 #define        ATH_TXBUF       200             /* number of TX buffers */
-#define ATH_BCBUF      1               /* number of beacon buffers */
+#define ATH_BCBUF      16              /* number of beacon buffers */
+
+#define ATH5K_DEFAULT_BINTVAL  1000
 
 struct ath5k_buf {
        struct list_head        list;
@@ -92,6 +94,17 @@ struct ath5k_led
        struct led_classdev led_dev;            /* led classdev */
 };
 
+#define ATH5K_VIF_MAX  2048
+#define ATH5K_IF_ID_ANY        -1
+
+struct ath5k_vif {
+       int                     if_id;
+       enum nl80211_iftype     opmode;
+       int                     bslot;
+       struct ath5k_buf        *bbuf;          /* beacon buffer */
+       u8                      lladdr[ETH_ALEN];
+       u8                      bssid[ETH_ALEN];
+};
 
 #if CHAN_DEBUG
 #define ATH_CHAN_MAX   (26+26+26+200+200)
@@ -139,12 +152,14 @@ struct ath5k_softc {
        unsigned int            curmode;        /* current phy mode */
        struct ieee80211_channel *curchan;      /* current h/w channel */
 
-       struct ieee80211_vif *vif;
+       u16                     nvifs;
+       struct ieee80211_vif    *vifs[ATH5K_VIF_MAX];
 
        enum ath5k_int          imask;          /* interrupt mask copy */
 
        DECLARE_BITMAP(keymap, AR5K_KEYCACHE_SIZE); /* key use bit map */
 
+       u8                      lladdr[ETH_ALEN];
        u8                      bssidmask[ETH_ALEN];
 
        unsigned int            led_pin,        /* GPIO pin for driving LED */
@@ -170,7 +185,9 @@ struct ath5k_softc {
        struct ath5k_led        tx_led;         /* tx led */
 
        spinlock_t              block;          /* protects beacon */
-       struct ath5k_buf        *bbuf;          /* beacon buffer */
+       struct list_head        bcbuf;          /* beacon buffer */
+       int                     bslot[ATH_BCBUF];
+       u16                     nbcnvifs;
        unsigned int            bhalq,          /* SW q for outgoing beacons */
                                bmisscount,     /* missed beacon transmits */
                                bintval,        /* beacon interval in TU */
diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c
index 75eb9f4..aa4b6c2 100644
--- a/drivers/net/wireless/ath5k/pcu.c
+++ b/drivers/net/wireless/ath5k/pcu.c
@@ -365,7 +365,7 @@ void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 
*bssid, u16 assoc_id)
  * assuming only 4 bits for a mac address and for BSSIDs you can then have:
  *
  *                  \
- * MAC:                0001 |
+ * MAC:        0001 |
  * BSSID-01:   0100 | --> Belongs to us
  * BSSID-02:   1001 |
  *                  /
diff --git a/drivers/net/wireless/ath5k/reset.c 
b/drivers/net/wireless/ath5k/reset.c
index dc2d7d8..47607f8 100644
--- a/drivers/net/wireless/ath5k/reset.c
+++ b/drivers/net/wireless/ath5k/reset.c
@@ -125,7 +125,7 @@ static inline void ath5k_hw_write_rate_duration(struct 
ath5k_hw *ah,
                 * ieee80211_duration() for a brief description of
                 * what rate we should choose to TX ACKs. */
                tx_time = le16_to_cpu(ieee80211_generic_frame_duration(sc->hw,
-                                                       sc->vif, 10, rate));
+                                                       NULL, 10, rate));
 
                ath5k_hw_reg_write(ah, tx_time, reg);
 
@@ -597,7 +597,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype 
op_mode,
                 * XXX: rethink this after new mode changes to
                 * mac80211 are integrated */
                if (ah->ah_version == AR5K_AR5212 &&
-                       ah->ah_sc->vif != NULL)
+                       ah->ah_sc->nvifs)
                        ath5k_hw_write_rate_duration(ah, mode);
 
                /*
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index e86ed59..8c6a6bd 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -113,6 +113,8 @@
  * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by
  *     %NL80211_ATTR_IFINDEX.
  *
+ * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
+ *     regulatory domain.
  * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command
  *     after being queried by the kernel. CRDA replies by sending a regulatory
  *     domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
@@ -178,6 +180,8 @@ enum nl80211_commands {
        NL80211_CMD_GET_MESH_PARAMS,
        NL80211_CMD_SET_MESH_PARAMS,
 
+       NL80211_CMD_GET_REG,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 559422f..7eddf0d 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1333,6 +1333,7 @@ struct ieee80211_ops {
        int (*ampdu_action)(struct ieee80211_hw *hw,
                            enum ieee80211_ampdu_mlme_action action,
                            struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+       const char *drvname;
 };
 
 /**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 9d4e4d8..2d80329 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -144,7 +144,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct 
net_device *dev,
        rcu_read_lock();
 
        if (mac_addr) {
-               sta = sta_info_get(sdata->local, mac_addr);
+               sta = sta_info_get(sdata->local, mac_addr, 
sdata->dev->dev_addr);
                if (!sta) {
                        ieee80211_key_free(key);
                        err = -ENOENT;
@@ -175,7 +175,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct 
net_device *dev,
        if (mac_addr) {
                ret = -ENOENT;
 
-               sta = sta_info_get(sdata->local, mac_addr);
+               sta = sta_info_get(sdata->local, mac_addr, 
sdata->dev->dev_addr);
                if (!sta)
                        goto out_unlock;
 
@@ -222,7 +222,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct 
net_device *dev,
        rcu_read_lock();
 
        if (mac_addr) {
-               sta = sta_info_get(sdata->local, mac_addr);
+               sta = sta_info_get(sdata->local, mac_addr, 
sdata->dev->dev_addr);
                if (!sta)
                        goto out;
 
@@ -385,7 +385,7 @@ static int ieee80211_get_station(struct wiphy *wiphy, 
struct net_device *dev,
 
        /* XXX: verify sta->dev == dev */
 
-       sta = sta_info_get(local, mac);
+       sta = sta_info_get(local, mac, dev->dev_addr);
        if (sta) {
                ret = 0;
                sta_set_sinfo(sta, sinfo);
@@ -727,7 +727,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, 
struct net_device *dev,
                /* STA has been freed */
                if (err == -EEXIST && layer2_update) {
                        /* Need to update layer 2 devices on reassociation */
-                       sta = sta_info_get(local, mac);
+                       sta = sta_info_get(local, mac, dev->dev_addr);
                        if (sta)
                                ieee80211_send_layer2_update(sta);
                }
@@ -756,7 +756,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, 
struct net_device *dev,
                rcu_read_lock();
 
                /* XXX: get sta belonging to dev */
-               sta = sta_info_get(local, mac);
+               sta = sta_info_get(local, mac, dev->dev_addr);
                if (!sta) {
                        rcu_read_unlock();
                        return -ENOENT;
@@ -784,7 +784,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
        rcu_read_lock();
 
        /* XXX: get sta belonging to dev */
-       sta = sta_info_get(local, mac);
+       sta = sta_info_get(local, mac, dev->dev_addr);
        if (!sta) {
                rcu_read_unlock();
                return -ENOENT;
@@ -829,7 +829,7 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct 
net_device *dev,
                return -ENOTSUPP;
 
        rcu_read_lock();
-       sta = sta_info_get(local, next_hop);
+       sta = sta_info_get(local, next_hop, dev->dev_addr);
        if (!sta) {
                rcu_read_unlock();
                return -ENOENT;
@@ -883,7 +883,7 @@ static int ieee80211_change_mpath(struct wiphy *wiphy,
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, next_hop);
+       sta = sta_info_get(local, next_hop, dev->dev_addr);
        if (!sta) {
                rcu_read_unlock();
                return -ENOENT;
@@ -1138,6 +1138,7 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
        local->oper_channel = chan;
        local->oper_channel_type = channel_type;
 
+       /*printk("%s: change channel phy %s\n", current->comm, 
dev_name(&wiphy->dev));*/
        return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 }
 
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index c542193..810675e 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -442,7 +442,7 @@ static int notif_registered;
 
 void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata)
 {
-       char buf[10+IFNAMSIZ];
+       char buf[sizeof("phy4294967296/")+10+IFNAMSIZ];
 
        if (!notif_registered)
                return;
@@ -450,6 +450,12 @@ void ieee80211_debugfs_add_netdev(struct 
ieee80211_sub_if_data *sdata)
        sprintf(buf, "netdev:%s", sdata->dev->name);
        sdata->debugfsdir = debugfs_create_dir(buf,
                sdata->local->hw.wiphy->debugfsdir);
+
+       sprintf(buf, "%s/netdev:%s", dev_name(&sdata->local->hw.wiphy->dev),
+               sdata->dev->name);
+       sdata->debugfslink = debugfs_create_symlink(sdata->dev->name,
+               sdata->local->hw.wiphy->debugfsdir->d_parent, buf);
+
        add_files(sdata);
 }
 
@@ -457,6 +463,7 @@ void ieee80211_debugfs_remove_netdev(struct 
ieee80211_sub_if_data *sdata)
 {
        del_files(sdata);
        debugfs_remove(sdata->debugfsdir);
+       debugfs_remove(sdata->debugfslink);
        sdata->debugfsdir = NULL;
 }
 
@@ -467,7 +474,7 @@ static int netdev_notify(struct notifier_block *nb,
        struct net_device *dev = ndev;
        struct dentry *dir;
        struct ieee80211_sub_if_data *sdata;
-       char buf[10+IFNAMSIZ];
+       char buf[sizeof("phy4294967296/")+10+IFNAMSIZ];
 
        if (state != NETDEV_CHANGENAME)
                return 0;
@@ -490,6 +497,12 @@ static int netdev_notify(struct notifier_block *nb,
                printk(KERN_ERR "mac80211: debugfs: failed to rename debugfs "
                       "dir to %s\n", buf);
 
+       sprintf(buf, "%s/netdev:%s", dev_name(&sdata->local->hw.wiphy->dev),
+               sdata->dev->name);
+       debugfs_remove(sdata->debugfslink);
+       sdata->debugfslink = debugfs_create_symlink(sdata->dev->name,
+               sdata->local->hw.wiphy->debugfsdir->d_parent, buf);
+
        return 0;
 }
 
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index c5c0c52..cde4c3f 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -332,7 +332,7 @@ void ieee80211_sta_stop_rx_ba_session(struct 
ieee80211_sub_if_data *sdata, u8 *r
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, ra);
+       sta = sta_info_get(local, ra, sdata->dev->dev_addr);
        if (!sta) {
                rcu_read_unlock();
                return;
@@ -415,7 +415,7 @@ static void sta_addba_resp_timer_expired(unsigned long data)
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, temp_sta->sta.addr);
+       sta = sta_info_get(local, temp_sta->sta.addr, NULL);
        if (!sta) {
                rcu_read_unlock();
                return;
@@ -481,7 +481,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, 
u8 *ra, u16 tid)
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, ra);
+       sta = sta_info_get(local, ra, NULL);
        if (!sta) {
 #ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "Could not find the station\n");
@@ -619,7 +619,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
                return -EINVAL;
 
        rcu_read_lock();
-       sta = sta_info_get(local, ra);
+       sta = sta_info_get(local, ra, NULL);
        if (!sta) {
                rcu_read_unlock();
                return -ENOENT;
@@ -680,7 +680,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 
*ra, u16 tid)
        }
 
        rcu_read_lock();
-       sta = sta_info_get(local, ra);
+       sta = sta_info_get(local, ra, NULL);
        if (!sta) {
                rcu_read_unlock();
 #ifdef CONFIG_MAC80211_HT_DEBUG
@@ -739,7 +739,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 
*ra, u8 tid)
 #endif /* CONFIG_MAC80211_HT_DEBUG */
 
        rcu_read_lock();
-       sta = sta_info_get(local, ra);
+       sta = sta_info_get(local, ra, NULL);
        if (!sta) {
 #ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "Could not find station: %pM\n", ra);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index f3eec98..b7bb201 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -432,6 +432,7 @@ struct ieee80211_sub_if_data {
 
 #ifdef CONFIG_MAC80211_DEBUGFS
        struct dentry *debugfsdir;
+       struct dentry *debugfslink;
        union {
                struct {
                        struct dentry *drop_unencrypted;
@@ -548,6 +549,7 @@ enum queue_stop_reason {
 
 struct ieee80211_master_priv {
        struct ieee80211_local *local;
+       struct wireless_dev wdev;
 };
 
 struct ieee80211_local {
@@ -627,6 +629,7 @@ struct ieee80211_local {
        enum ieee80211_band scan_band;
 
        enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
+       bool scan_probe_once;
        unsigned long last_scan_completed;
        struct delayed_work scan_work;
        struct ieee80211_sub_if_data *scan_sdata;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index b907482..a4177fe 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -13,6 +13,7 @@
 #include <linux/kernel.h>
 #include <linux/if_arp.h>
 #include <linux/netdevice.h>
+#include <linux/ethtool.h>
 #include <linux/rtnetlink.h>
 #include <net/mac80211.h>
 #include "ieee80211_i.h"
@@ -56,6 +57,14 @@ static inline int identical_mac_addr_allowed(int type1, int 
type2)
                         type2 == NL80211_IFTYPE_AP_VLAN));
 }
 
+static int ieee80211_init(struct net_device *dev)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+       dev->iflink = sdata->local->mdev->ifindex;
+       return 0;
+}
+
 static int ieee80211_open(struct net_device *dev)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -569,6 +578,22 @@ static void ieee80211_set_multicast_list(struct net_device 
*dev)
        dev_mc_sync(local->mdev, dev);
 }
 
+static void ieee80211_get_drvinfo(struct net_device *dev,
+                                 struct ethtool_drvinfo *drvinfo)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+
+       if (local->ops->drvname != NULL)
+               snprintf(drvinfo->driver, sizeof(drvinfo->driver),
+                        local->ops->drvname);
+}
+
+static const struct ethtool_ops ieee80211_ethtool_ops = {
+       .get_link       = ethtool_op_get_link,
+       .get_drvinfo    = ieee80211_get_drvinfo,
+};
+
 static void ieee80211_if_setup(struct net_device *dev)
 {
        ether_setup(dev);
@@ -576,12 +601,15 @@ static void ieee80211_if_setup(struct net_device *dev)
        dev->wireless_handlers = &ieee80211_iw_handler_def;
        dev->set_multicast_list = ieee80211_set_multicast_list;
        dev->change_mtu = ieee80211_change_mtu;
+       dev->init = ieee80211_init,
        dev->open = ieee80211_open;
        dev->stop = ieee80211_stop;
        dev->destructor = free_netdev;
        /* we will validate the address ourselves in ->open */
        dev->validate_addr = NULL;
+       dev->ethtool_ops = &ieee80211_ethtool_ops;
 }
+
 /*
  * Called when the netdev is removed or, by the code below, before
  * the interface type changes.
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 999f7aa..10b323c 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -352,7 +352,8 @@ void ieee80211_key_link(struct ieee80211_key *key,
                         */
 
                        /* same here, the AP could be using QoS */
-                       ap = sta_info_get(key->local, key->sdata->u.sta.bssid);
+                       ap = sta_info_get(key->local, key->sdata->u.sta.bssid,
+                                         sdata->dev->dev_addr);
                        if (ap) {
                                if (test_sta_flags(ap, WLAN_STA_WME))
                                        key->conf.flags |=
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 24b1436..9a9ee05 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/netdevice.h>
+#include <linux/ethtool.h>
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/skbuff.h>
@@ -151,6 +152,23 @@ static void ieee80211_master_set_multicast_list(struct 
net_device *dev)
        ieee80211_configure_filter(local);
 }
 
+
+static void ieee80211_get_drvinfo(struct net_device *dev,
+                                 struct ethtool_drvinfo *drvinfo)
+{
+       struct ieee80211_master_priv *mpriv = netdev_priv(dev);
+       struct ieee80211_local *local = mpriv->local;
+
+       if (local->ops->drvname != NULL)
+               snprintf(drvinfo->driver, sizeof(drvinfo->driver),
+                        local->ops->drvname);
+}
+
+static const struct ethtool_ops ieee80211_ethtool_ops = {
+       .get_link       = ethtool_op_get_link,
+       .get_drvinfo    = ieee80211_get_drvinfo,
+};
+
 /* everything else */
 
 int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
@@ -499,7 +517,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct 
sk_buff *skb)
 
        sband = local->hw.wiphy->bands[info->band];
 
-       sta = sta_info_get(local, hdr->addr1);
+       sta = sta_info_get(local, hdr->addr1, hdr->addr2);
 
        if (sta) {
                if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
@@ -812,6 +830,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        mpriv->local = local;
        local->mdev = mdev;
 
+       mpriv->wdev.wiphy = local->hw.wiphy;
+       mpriv->wdev.iftype = -1; /* invalid */
+       mpriv->wdev.netdev = mdev;
+       mdev->ieee80211_ptr = &mpriv->wdev;
+
        ieee80211_rx_bss_list_init(local);
 
        mdev->hard_start_xmit = ieee80211_master_start_xmit;
@@ -819,6 +842,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        mdev->stop = ieee80211_master_stop;
        mdev->type = ARPHRD_IEEE80211;
        mdev->header_ops = &ieee80211_header_ops;
+       mdev->ethtool_ops = &ieee80211_ethtool_ops;
        mdev->set_multicast_list = ieee80211_master_set_multicast_list;
 
        local->hw.workqueue =
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 71fe609..319ec17 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -266,7 +266,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data 
*sdata,
        u8 action = mgmt->u.action.u.mesh_action.action_code;
 
        rcu_read_lock();
-       sta = sta_info_get(local, mgmt->sa);
+       sta = sta_info_get(local, mgmt->sa, sdata->dev->dev_addr);
        if (!sta) {
                rcu_read_unlock();
                return 0;
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 1159bdb..81c407f 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -230,7 +230,7 @@ void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct 
ieee80211_sub_if_data
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, hw_addr);
+       sta = sta_info_get(local, hw_addr, sdata->dev->dev_addr);
        if (!sta) {
                sta = mesh_plink_alloc(sdata, hw_addr, rates);
                if (!sta) {
@@ -448,7 +448,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data 
*sdata, struct ieee80211_m
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, mgmt->sa);
+       sta = sta_info_get(local, mgmt->sa, sdata->dev->dev_addr);
        if (!sta && ftype != PLINK_OPEN) {
                mpl_dbg("Mesh plink: cls or cnf from unknown peer\n");
                rcu_read_unlock();
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 2b890af..460b083 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -827,7 +827,7 @@ static void ieee80211_set_disassoc(struct 
ieee80211_sub_if_data *sdata,
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, ifsta->bssid);
+       sta = sta_info_get(local, ifsta->bssid, sdata->dev->dev_addr);
        if (!sta) {
                rcu_read_unlock();
                return;
@@ -885,7 +885,7 @@ static void ieee80211_set_disassoc(struct 
ieee80211_sub_if_data *sdata,
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, ifsta->bssid);
+       sta = sta_info_get(local, ifsta->bssid, sdata->dev->dev_addr);
        if (!sta) {
                rcu_read_unlock();
                return;
@@ -939,6 +939,8 @@ static int ieee80211_privacy_mismatch(struct 
ieee80211_sub_if_data *sdata,
 static void ieee80211_associate(struct ieee80211_sub_if_data *sdata,
                                struct ieee80211_if_sta *ifsta)
 {
+       unsigned int skew;
+
        ifsta->assoc_tries++;
        if (ifsta->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
                printk(KERN_DEBUG "%s: association with AP %pM"
@@ -961,7 +963,8 @@ static void ieee80211_associate(struct 
ieee80211_sub_if_data *sdata,
 
        ieee80211_send_assoc(sdata, ifsta);
 
-       mod_timer(&ifsta->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
+       skew = ((u64)net_random() * (HZ / 10)) >> 32;
+       mod_timer(&ifsta->timer, jiffies + IEEE80211_ASSOC_TIMEOUT + skew);
 }
 
 
@@ -981,7 +984,7 @@ static void ieee80211_associated(struct 
ieee80211_sub_if_data *sdata,
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, ifsta->bssid);
+       sta = sta_info_get(local, ifsta->bssid, sdata->dev->dev_addr);
        if (!sta) {
                printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n",
                       sdata->dev->name, ifsta->bssid);
@@ -989,7 +992,7 @@ static void ieee80211_associated(struct 
ieee80211_sub_if_data *sdata,
        } else {
                disassoc = 0;
                if (time_after(jiffies,
-                              sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
+                              sta->last_rx + 2 * 
IEEE80211_MONITORING_INTERVAL)) {
                        if (ifsta->flags & IEEE80211_STA_PROBEREQ_POLL) {
                                printk(KERN_DEBUG "%s: No ProbeResp from "
                                       "current AP %pM - assume out of "
@@ -1018,9 +1021,13 @@ static void ieee80211_associated(struct 
ieee80211_sub_if_data *sdata,
        if (disassoc)
                ieee80211_set_disassoc(sdata, ifsta, true, true,
                                        WLAN_REASON_PREV_AUTH_NOT_VALID);
-       else
+       else {
+               unsigned int skew;
+
+               skew = ((u64)net_random() * HZ) >> 32;
                mod_timer(&ifsta->timer, jiffies +
-                                     IEEE80211_MONITORING_INTERVAL);
+                         IEEE80211_MONITORING_INTERVAL + skew);
+       }
 }
 
 
@@ -1279,7 +1286,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct 
ieee80211_sub_if_data *sdata,
        rcu_read_lock();
 
        /* Add STA entry for the AP */
-       sta = sta_info_get(local, ifsta->bssid);
+       sta = sta_info_get(local, ifsta->bssid, sdata->dev->dev_addr);
        if (!sta) {
                struct ieee80211_bss *bss;
 
@@ -1565,7 +1572,7 @@ static void ieee80211_rx_bss_info(struct 
ieee80211_sub_if_data *sdata,
 
                rcu_read_lock();
 
-               sta = sta_info_get(local, mgmt->sa);
+               sta = sta_info_get(local, mgmt->sa, sdata->dev->dev_addr);
                if (sta) {
                        u64 prev_rates;
 
@@ -1754,7 +1761,7 @@ static void ieee80211_rx_mgmt_beacon(struct 
ieee80211_sub_if_data *sdata,
 
                rcu_read_lock();
 
-               sta = sta_info_get(local, ifsta->bssid);
+               sta = sta_info_get(local, ifsta->bssid, sdata->dev->dev_addr);
                if (!sta) {
                        rcu_read_unlock();
                        return;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 7175ae8..a7a0301 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1252,7 +1252,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
                                printk(KERN_DEBUG "%s: failed to clone "
                                       "multicast frame\n", dev->name);
                } else {
-                       dsta = sta_info_get(local, skb->data);
+                       dsta = sta_info_get(local, skb->data, dev->dev_addr);
                        if (dsta && dsta->sdata->dev == dev) {
                                /*
                                 * The destination station is associated to
@@ -1928,7 +1928,7 @@ static void __ieee80211_rx_handle_packet(struct 
ieee80211_hw *hw,
        int prepares;
        struct ieee80211_sub_if_data *prev = NULL;
        struct sk_buff *skb_new;
-       u8 *bssid;
+       u8 *bssid, *laddr;
 
        hdr = (struct ieee80211_hdr *)skb->data;
        memset(&rx, 0, sizeof(rx));
@@ -1941,12 +1941,6 @@ static void __ieee80211_rx_handle_packet(struct 
ieee80211_hw *hw,
        if (ieee80211_is_data(hdr->frame_control) || 
ieee80211_is_mgmt(hdr->frame_control))
                local->dot11ReceivedFragmentCount++;
 
-       rx.sta = sta_info_get(local, hdr->addr2);
-       if (rx.sta) {
-               rx.sdata = rx.sta->sdata;
-               rx.dev = rx.sta->sdata->dev;
-       }
-
        if ((status->flag & RX_FLAG_MMIC_ERROR)) {
                ieee80211_rx_michael_mic_report(local->mdev, hdr, &rx);
                return;
@@ -1967,6 +1961,19 @@ static void __ieee80211_rx_handle_packet(struct 
ieee80211_hw *hw,
                if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
                        continue;
 
+               laddr = hdr->addr1;
+               if (is_multicast_ether_addr(laddr))
+                       laddr = sdata->dev->dev_addr;
+
+               rx.sta = sta_info_get(local, hdr->addr2, laddr);
+               if (rx.sta) {
+                       rx.sdata = rx.sta->sdata;
+                       rx.dev = rx.sta->sdata->dev;
+               } else {
+                       rx.sdata = NULL;
+                       rx.dev = NULL;
+               }
+
                bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type);
                rx.flags |= IEEE80211_RX_RA_MATCH;
                prepares = prepare_for_handlers(sdata, bssid, &rx, hdr);
@@ -2151,7 +2158,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct 
ieee80211_local *local,
        u8 ret = 0;
        int tid;
 
-       sta = sta_info_get(local, hdr->addr2);
+       sta = sta_info_get(local, hdr->addr2, hdr->addr1);
        if (!sta)
                return ret;
 
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index f5c7c33..cae19c7 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -434,6 +434,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
        if (WARN_ON(!local->hw_scanning && !local->sw_scanning))
                return;
 
+       printk("scan completed\n");
        local->last_scan_completed = jiffies;
        memset(&wrqu, 0, sizeof(wrqu));
 
@@ -458,7 +459,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
        }
 
        local->sw_scanning = false;
-       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+       if (!local->scan_probe_once)
+               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
 
        netif_tx_lock_bh(local->mdev);
        netif_addr_lock(local->mdev);
@@ -531,7 +533,8 @@ void ieee80211_scan_work(struct work_struct *work)
                }
 
                /* if no more bands/channels left, complete scan */
-               if (!sband || local->scan_channel_idx >= sband->n_channels) {
+               if (!sband || local->scan_channel_idx >= sband->n_channels ||
+                   local->scan_probe_once) {
                        ieee80211_scan_completed(local_to_hw(local));
                        return;
                }
@@ -591,6 +594,8 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data 
*scan_sdata,
 {
        struct ieee80211_local *local = scan_sdata->local;
        struct ieee80211_sub_if_data *sdata;
+       unsigned int svifs; /* station vifs */
+       unsigned int avifs; /* associated stations vifs and any other type of 
vif */
 
        if (ssid_len > IEEE80211_MAX_SSID_LEN)
                return -EINVAL;
@@ -634,7 +639,17 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data 
*scan_sdata,
        local->sw_scanning = true;
 
        rcu_read_lock();
+       svifs = 0;
+       avifs = 0;
        list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+               if (sdata->vif.type != NL80211_IFTYPE_STATION ||
+                   (sdata->u.sta.state == IEEE80211_STA_MLME_ASSOCIATE ||
+                    sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED))
+                       avifs++;
+               
+               if (sdata->vif.type == NL80211_IFTYPE_STATION)
+                       svifs++;
+
                if (sdata->vif.type == NL80211_IFTYPE_STATION) {
                        if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
                                netif_tx_stop_all_queues(sdata->dev);
@@ -650,11 +665,25 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data 
*scan_sdata,
                memcpy(local->scan_ssid, ssid, ssid_len);
        } else
                local->scan_ssid_len = 0;
-       local->scan_state = SCAN_SET_CHANNEL;
+
+       /* If one sta is associated, we don't want another to start scanning, 
as that
+        * will un-associate the first.
+        * TODO:  This still leaves a race when a thundering herd of WPA 
supplicants
+        *  are all coming up at once.
+        */
+       if ((avifs > 1) || ((avifs == 1) && (svifs > 1)))
+               local->scan_probe_once = true;
+       else
+               local->scan_probe_once = false;
+       
+       local->scan_state = local->scan_probe_once ? SCAN_SEND_PROBE : 
SCAN_SET_CHANNEL;
        local->scan_channel_idx = 0;
        local->scan_band = IEEE80211_BAND_2GHZ;
        local->scan_sdata = scan_sdata;
 
+       printk("%s: start scan: active avifs=%u svifs=%u hop=%d\n", 
current->comm,
+              avifs, svifs, !local->scan_probe_once);
+
        netif_addr_lock_bh(local->mdev);
        local->filter_flags |= FIF_BCN_PRBRESP_PROMISC;
        local->ops->configure_filter(local_to_hw(local),
@@ -699,6 +728,7 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data 
*sdata,
        if (ssid_len)
                memcpy(ifsta->scan_ssid, ssid, ssid_len);
        set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request);
+       printk("%s: request scan\n", current->comm);
        queue_work(local->hw.workqueue, &ifsta->work);
 
        return 0;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 10c5539..743fc3f 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -93,13 +93,16 @@ static int sta_info_hash_del(struct ieee80211_local *local,
 }
 
 /* protected by RCU */
-struct sta_info *sta_info_get(struct ieee80211_local *local, const u8 *addr)
+struct sta_info *sta_info_get(struct ieee80211_local *local, const u8 *addr,
+                             const u8 *laddr)
 {
        struct sta_info *sta;
 
        sta = rcu_dereference(local->sta_hash[STA_HASH(addr)]);
        while (sta) {
-               if (memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
+               if ((laddr == NULL||
+                    memcmp(sta->sdata->dev->dev_addr, laddr, ETH_ALEN) == 0) &&
+                   memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
                        break;
                sta = rcu_dereference(sta->hnext);
        }
@@ -297,7 +300,7 @@ int sta_info_insert(struct sta_info *sta)
 
        spin_lock_irqsave(&local->sta_lock, flags);
        /* check if STA exists already */
-       if (sta_info_get(local, sta->sta.addr)) {
+       if (sta_info_get(local, sta->sta.addr, sdata->dev->dev_addr)) {
                spin_unlock_irqrestore(&local->sta_lock, flags);
                err = -EEXIST;
                goto out_free;
@@ -823,7 +826,7 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data 
*sdata,
 struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw,
                                         const u8 *addr)
 {
-       struct sta_info *sta = sta_info_get(hw_to_local(hw), addr);
+       struct sta_info *sta = sta_info_get(hw_to_local(hw), addr, NULL);
 
        if (!sta)
                return NULL;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index e49a5b9..d181f29 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -403,7 +403,8 @@ static inline u32 get_sta_flags(struct sta_info *sta)
 /*
  * Get a STA info, must have be under RCU read lock.
  */
-struct sta_info *sta_info_get(struct ieee80211_local *local, const u8 *addr);
+struct sta_info *sta_info_get(struct ieee80211_local *local, const u8 *addr,
+                             const u8 *laddr);
 /*
  * Get STA info by index, BROKEN!
  */
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 37e3d5e..2e338cb 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -981,7 +981,7 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
 
        hdr = (struct ieee80211_hdr *) skb->data;
 
-       tx->sta = sta_info_get(local, hdr->addr1);
+       tx->sta = sta_info_get(local, hdr->addr1, dev->dev_addr);
 
        if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) {
                qc = ieee80211_get_qos_ctl(hdr);
@@ -1596,7 +1596,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
         */
        if (!is_multicast_ether_addr(hdr.addr1)) {
                rcu_read_lock();
-               sta = sta_info_get(local, hdr.addr1);
+               sta = sta_info_get(local, hdr.addr1, dev->dev_addr);
                if (sta)
                        sta_flags = get_sta_flags(sta);
                rcu_read_unlock();
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index fb89e1d..1ec8ecf 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -719,9 +719,12 @@ int ieee80211_set_freq(struct ieee80211_sub_if_data 
*sdata, int freqMHz)
 
                if (local->sw_scanning || local->hw_scanning)
                        ret = 0;
-               else
+               else {
+                       printk("%s: change channel phy %s (%i)\n", 
current->comm,
+                              dev_name(&local->hw.wiphy->dev), freqMHz);
                        ret = ieee80211_hw_config(
                                local, IEEE80211_CONF_CHANGE_CHANNEL);
+               }
        }
 
        return ret;
diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c
index 7162d58..3c10851 100644
--- a/net/mac80211/wext.c
+++ b/net/mac80211/wext.c
@@ -51,7 +51,7 @@ static int ieee80211_set_encryption(struct 
ieee80211_sub_if_data *sdata, u8 *sta
                if (is_broadcast_ether_addr(sta_addr)) {
                        key = sdata->keys[idx];
                } else {
-                       sta = sta_info_get(local, sta_addr);
+                       sta = sta_info_get(local, sta_addr, 
sdata->dev->dev_addr);
                        if (!sta) {
                                err = -ENOENT;
                                goto out_unlock;
@@ -84,7 +84,7 @@ static int ieee80211_set_encryption(struct 
ieee80211_sub_if_data *sdata, u8 *sta
                                goto out_unlock;
                        }
 
-                       sta = sta_info_get(local, sta_addr);
+                       sta = sta_info_get(local, sta_addr, 
sdata->dev->dev_addr);
                        if (!sta) {
                                ieee80211_key_free(key);
                                err = -ENOENT;
@@ -511,7 +511,7 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev,
 
        rcu_read_lock();
 
-       sta = sta_info_get(local, sdata->u.sta.bssid);
+       sta = sta_info_get(local, sdata->u.sta.bssid, dev->dev_addr);
 
        if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS))
                rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate;
@@ -953,7 +953,7 @@ static struct iw_statistics 
*ieee80211_get_wireless_stats(struct net_device *dev
 
        if (sdata->vif.type == NL80211_IFTYPE_STATION ||
            sdata->vif.type == NL80211_IFTYPE_ADHOC)
-               sta = sta_info_get(local, sdata->u.sta.bssid);
+               sta = sta_info_get(local, sdata->u.sta.bssid, dev->dev_addr);
        if (!sta) {
                wstats->discard.fragment = 0;
                wstats->discard.misc = 0;
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index ac71b38..889376f 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -129,7 +129,7 @@ u16 ieee80211_select_queue(struct net_device *dev, struct 
sk_buff *skb)
                        return queue;
 
                rcu_read_lock();
-               sta = sta_info_get(local, hdr->addr1);
+               sta = sta_info_get(local, hdr->addr1, dev->dev_addr);
                tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
                if (sta) {
                        int ampdu_queue = sta->tid_to_tx_q[tid];
@@ -162,7 +162,7 @@ u16 ieee80211_select_queue(struct net_device *dev, struct 
sk_buff *skb)
 
                rcu_read_lock();
 
-               sta = sta_info_get(local, hdr->addr1);
+               sta = sta_info_get(local, hdr->addr1, dev->dev_addr);
                if (sta) {
                        int ampdu_queue = sta->tid_to_tx_q[tid];
 
diff --git a/net/wireless/core.c b/net/wireless/core.c
index b96fc0c..9ac947d 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -118,18 +118,18 @@ cfg80211_get_dev_from_ifindex(int ifindex)
        struct cfg80211_registered_device *drv = ERR_PTR(-ENODEV);
        struct net_device *dev;
 
-       mutex_lock(&cfg80211_drv_mutex);
        dev = dev_get_by_index(&init_net, ifindex);
        if (!dev)
                goto out;
+       mutex_lock(&cfg80211_drv_mutex);
        if (dev->ieee80211_ptr) {
                drv = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
                mutex_lock(&drv->mtx);
        } else
                drv = ERR_PTR(-ENODEV);
        dev_put(dev);
- out:
        mutex_unlock(&cfg80211_drv_mutex);
+ out:
        return drv;
 }
 
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 31b807a..fc2ac5c 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -2080,6 +2080,81 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, 
struct genl_info *info)
 
 #undef FILL_IN_MESH_PARAM_IF_SET
 
+static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
+{
+       struct sk_buff *msg;
+       void *hdr = NULL;
+       struct nlattr *nl_reg_rules;
+       unsigned int i;
+       int err = -EINVAL;
+
+       mutex_lock(&cfg80211_drv_mutex);
+
+       if (!cfg80211_regdomain)
+               goto out;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg) {
+               err = -ENOBUFS;
+               goto out;
+       }
+
+       hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+                            NL80211_CMD_GET_REG);
+       if (!hdr)
+               goto nla_put_failure;
+
+       NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
+               cfg80211_regdomain->alpha2);
+
+       nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
+       if (!nl_reg_rules)
+               goto nla_put_failure;
+
+       for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
+               struct nlattr *nl_reg_rule;
+               const struct ieee80211_reg_rule *reg_rule;
+               const struct ieee80211_freq_range *freq_range;
+               const struct ieee80211_power_rule *power_rule;
+
+               reg_rule = &cfg80211_regdomain->reg_rules[i];
+               freq_range = &reg_rule->freq_range;
+               power_rule = &reg_rule->power_rule;
+
+               nl_reg_rule = nla_nest_start(msg, i);
+               if (!nl_reg_rule)
+                       goto nla_put_failure;
+
+               NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
+                       reg_rule->flags);
+               NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
+                       freq_range->start_freq_khz);
+               NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
+                       freq_range->end_freq_khz);
+               NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
+                       freq_range->max_bandwidth_khz);
+               NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
+                       power_rule->max_antenna_gain);
+               NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
+                       power_rule->max_eirp);
+
+               nla_nest_end(msg, nl_reg_rule);
+       }
+
+       nla_nest_end(msg, nl_reg_rules);
+
+       genlmsg_end(msg, hdr);
+       err = genlmsg_unicast(msg, info->snd_pid);
+       goto out;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       err = -EMSGSIZE;
+out:
+       mutex_unlock(&cfg80211_drv_mutex);
+       return err;
+}
+
 static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
@@ -2283,6 +2358,12 @@ static struct genl_ops nl80211_ops[] = {
                .flags = GENL_ADMIN_PERM,
        },
        {
+               .cmd = NL80211_CMD_GET_REG,
+               .doit = nl80211_get_reg,
+               .policy = nl80211_policy,
+               /* can be retrieved by unprivileged users */
+       },
+       {
                .cmd = NL80211_CMD_SET_REG,
                .doit = nl80211_set_reg,
                .policy = nl80211_policy,
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 4f9ff2a..f2bc808 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -89,7 +89,7 @@ static u32 supported_bandwidths[] = {
 /* Central wireless core regulatory domains, we only need two,
  * the current one and a world regulatory domain in case we have no
  * information to give us an alpha2 */
-static const struct ieee80211_regdomain *cfg80211_regdomain;
+const struct ieee80211_regdomain *cfg80211_regdomain;
 
 /* We use this as a place for the rd structure built from the
  * last parsed country IE to rest until CRDA gets back to us with
@@ -1381,8 +1381,9 @@ static int __set_regdom(const struct ieee80211_regdomain 
*rd)
         * Country IE requests are handled a bit differently, we intersect
         * the country IE rd with what CRDA believes that country should have
         */
-
-       BUG_ON(!country_ie_regdomain);
+       if (!country_ie_regdomain)
+               return -EINVAL;
+       /* BUG_ON(!country_ie_regdomain);  Too harsh, races with virtual 
stations it seems. --Ben */
 
        if (rd != country_ie_regdomain) {
                /* Intersect what CRDA returned and our what we
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index a76ea3f..3ccee49 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -1,5 +1,7 @@
 #ifndef __NET_WIRELESS_REG_H
 #define __NET_WIRELESS_REG_H
 
+extern const struct ieee80211_regdomain *cfg80211_regdomain;
+
 bool is_world_regdom(const char *alpha2);
 bool reg_is_valid_request(const char *alpha2);
_______________________________________________
ath5k-devel mailing list
ath5k-devel@lists.ath5k.org
https://lists.ath5k.org/mailman/listinfo/ath5k-devel

Reply via email to