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 <[email protected]>
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 = ®_rule->freq_range;
+ power_rule = ®_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
[email protected]
https://lists.ath5k.org/mailman/listinfo/ath5k-devel