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 = ®_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 ath5k-devel@lists.ath5k.org https://lists.ath5k.org/mailman/listinfo/ath5k-devel