Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem

Conflicts:
	drivers/net/wireless/iwlwifi/dvm/tx.c
	drivers/net/wireless/ti/wlcore/sdio.c
	drivers/net/wireless/ti/wlcore/spi.c
This commit is contained in:
John W. Linville
2013-02-18 13:47:13 -05:00
189 changed files with 12404 additions and 3253 deletions

View File

@@ -147,6 +147,32 @@ static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
}
}
static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
{
int width;
switch (c->width) {
case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_20_NOHT:
width = 20;
break;
case NL80211_CHAN_WIDTH_40:
width = 40;
break;
case NL80211_CHAN_WIDTH_80P80:
case NL80211_CHAN_WIDTH_80:
width = 80;
break;
case NL80211_CHAN_WIDTH_160:
width = 160;
break;
default:
WARN_ON_ONCE(1);
return -1;
}
return width;
}
const struct cfg80211_chan_def *
cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
const struct cfg80211_chan_def *c2)
@@ -192,6 +218,93 @@ cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
}
EXPORT_SYMBOL(cfg80211_chandef_compatible);
static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
u32 bandwidth,
enum nl80211_dfs_state dfs_state)
{
struct ieee80211_channel *c;
u32 freq;
for (freq = center_freq - bandwidth/2 + 10;
freq <= center_freq + bandwidth/2 - 10;
freq += 20) {
c = ieee80211_get_channel(wiphy, freq);
if (!c || !(c->flags & IEEE80211_CHAN_RADAR))
continue;
c->dfs_state = dfs_state;
c->dfs_state_entered = jiffies;
}
}
void cfg80211_set_dfs_state(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
enum nl80211_dfs_state dfs_state)
{
int width;
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
return;
width = cfg80211_chandef_get_width(chandef);
if (width < 0)
return;
cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1,
width, dfs_state);
if (!chandef->center_freq2)
return;
cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2,
width, dfs_state);
}
static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
u32 center_freq,
u32 bandwidth)
{
struct ieee80211_channel *c;
u32 freq;
for (freq = center_freq - bandwidth/2 + 10;
freq <= center_freq + bandwidth/2 - 10;
freq += 20) {
c = ieee80211_get_channel(wiphy, freq);
if (!c)
return -EINVAL;
if (c->flags & IEEE80211_CHAN_RADAR)
return 1;
}
return 0;
}
int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef)
{
int width;
int r;
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
return -EINVAL;
width = cfg80211_chandef_get_width(chandef);
if (width < 0)
return -EINVAL;
r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1,
width);
if (r)
return r;
if (!chandef->center_freq2)
return 0;
return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
width);
}
static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
u32 center_freq, u32 bandwidth,
u32 prohibited_flags)
@@ -203,7 +316,16 @@ static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
freq <= center_freq + bandwidth/2 - 10;
freq += 20) {
c = ieee80211_get_channel(wiphy, freq);
if (!c || c->flags & prohibited_flags)
if (!c)
return false;
/* check for radar flags */
if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
(c->dfs_state != NL80211_DFS_AVAILABLE))
return false;
/* check for the other flags */
if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
return false;
}
@@ -253,6 +375,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
case NL80211_CHAN_WIDTH_80:
if (!vht_cap->vht_supported)
return false;
prohibited_flags |= IEEE80211_CHAN_NO_80MHZ;
width = 80;
break;
case NL80211_CHAN_WIDTH_160:
@@ -260,6 +383,7 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
return false;
if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
return false;
prohibited_flags |= IEEE80211_CHAN_NO_160MHZ;
width = 160;
break;
default:
@@ -267,7 +391,16 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
return false;
}
/* TODO: missing regulatory check on 80/160 bandwidth */
/*
* TODO: What if there are only certain 80/160/80+80 MHz channels
* allowed by the driver, or only certain combinations?
* For 40 MHz the driver can set the NO_HT40 flags, but for
* 80/160 MHz and in particular 80+80 MHz this isn't really
* feasible and we only have NO_80MHZ/NO_160MHZ so far but
* no way to cover 80+80 MHz or more complex restrictions.
* Note that such restrictions also need to be advertised to
* userspace, for example for P2P channel selection.
*/
if (width > 20)
prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
@@ -344,7 +477,10 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
if (wdev->beacon_interval) {
if (wdev->cac_started) {
*chan = wdev->channel;
*chanmode = CHAN_MODE_SHARED;
} else if (wdev->beacon_interval) {
*chan = wdev->channel;
*chanmode = CHAN_MODE_SHARED;
}

View File

@@ -324,6 +324,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
INIT_LIST_HEAD(&rdev->bss_list);
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk,
cfg80211_dfs_channels_update_work);
#ifdef CONFIG_CFG80211_WEXT
rdev->wiphy.wext = &cfg80211_wext_handler;
#endif
@@ -365,7 +367,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
rdev->wiphy.rts_threshold = (u32) -1;
rdev->wiphy.coverage_class = 0;
rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH;
rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH |
NL80211_FEATURE_ADVERTISE_CHAN_LIMITS;
return &rdev->wiphy;
}
@@ -695,6 +698,7 @@ void wiphy_unregister(struct wiphy *wiphy)
flush_work(&rdev->scan_done_wk);
cancel_work_sync(&rdev->conn_work);
flush_work(&rdev->event_work);
cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
if (rdev->wowlan && rdev->ops->set_wakeup)
rdev_set_wakeup(rdev, false);
@@ -715,7 +719,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
kfree(reg);
}
list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
cfg80211_put_bss(&scan->pub);
cfg80211_put_bss(&rdev->wiphy, &scan->pub);
kfree(rdev);
}

View File

@@ -8,7 +8,6 @@
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/kref.h>
#include <linux/rbtree.h>
#include <linux/debugfs.h>
#include <linux/rfkill.h>
@@ -87,6 +86,8 @@ struct cfg80211_registered_device {
struct cfg80211_wowlan *wowlan;
struct delayed_work dfs_update_channels_wk;
/* must be last because of the way we do wiphy_priv(),
* and it should at least be aligned to NETDEV_ALIGN */
struct wiphy wiphy __aligned(NETDEV_ALIGN);
@@ -109,6 +110,9 @@ cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
for (i = 0; i < rdev->wowlan->n_patterns; i++)
kfree(rdev->wowlan->patterns[i].mask);
kfree(rdev->wowlan->patterns);
if (rdev->wowlan->tcp && rdev->wowlan->tcp->sock)
sock_release(rdev->wowlan->tcp->sock);
kfree(rdev->wowlan->tcp);
kfree(rdev->wowlan);
}
@@ -124,9 +128,10 @@ static inline void assert_cfg80211_lock(void)
struct cfg80211_internal_bss {
struct list_head list;
struct list_head hidden_list;
struct rb_node rbn;
unsigned long ts;
struct kref ref;
unsigned long refcount;
atomic_t hold;
/* must be last because of priv member */
@@ -428,6 +433,22 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
enum cfg80211_chan_mode chanmode,
u8 radar_detect);
/**
* cfg80211_chandef_dfs_required - checks if radar detection is required
* @wiphy: the wiphy to validate against
* @chandef: the channel definition to check
* Return: 1 if radar detection is required, 0 if it is not, < 0 on error
*/
int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
const struct cfg80211_chan_def *c);
void cfg80211_set_dfs_state(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
enum nl80211_dfs_state dfs_state);
void cfg80211_dfs_channels_update_work(struct work_struct *work);
static inline int
cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
@@ -454,6 +475,16 @@ cfg80211_can_use_chan(struct cfg80211_registered_device *rdev,
chan, chanmode, 0);
}
static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
{
unsigned long end = jiffies;
if (end >= start)
return jiffies_to_msecs(end - start);
return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
}
void
cfg80211_get_chan_state(struct wireless_dev *wdev,
struct ieee80211_channel **chan,

View File

@@ -37,7 +37,7 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
if (wdev->current_bss) {
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(&wdev->current_bss->pub);
cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
}
cfg80211_hold_bss(bss_from_pub(bss));
@@ -182,7 +182,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
if (wdev->current_bss) {
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(&wdev->current_bss->pub);
cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
}
wdev->current_bss = NULL;

View File

@@ -58,7 +58,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
*/
if (status_code != WLAN_STATUS_SUCCESS && wdev->conn &&
cfg80211_sme_failed_reassoc(wdev)) {
cfg80211_put_bss(bss);
cfg80211_put_bss(wiphy, bss);
goto out;
}
@@ -70,7 +70,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
* do not call connect_result() now because the
* sme will schedule work that does it later.
*/
cfg80211_put_bss(bss);
cfg80211_put_bss(wiphy, bss);
goto out;
}
@@ -108,7 +108,7 @@ void __cfg80211_send_deauth(struct net_device *dev,
if (wdev->current_bss &&
ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(&wdev->current_bss->pub);
cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
wdev->current_bss = NULL;
was_current = true;
}
@@ -164,7 +164,7 @@ void __cfg80211_send_disassoc(struct net_device *dev,
ether_addr_equal(wdev->current_bss->pub.bssid, bssid)) {
cfg80211_sme_disassoc(dev, wdev->current_bss);
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(&wdev->current_bss->pub);
cfg80211_put_bss(wiphy, &wdev->current_bss->pub);
wdev->current_bss = NULL;
} else
WARN_ON(1);
@@ -324,7 +324,7 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
err = rdev_auth(rdev, dev, &req);
out:
cfg80211_put_bss(req.bss);
cfg80211_put_bss(&rdev->wiphy, req.bss);
return err;
}
@@ -432,7 +432,7 @@ out:
if (err) {
if (was_connected)
wdev->sme_state = CFG80211_SME_CONNECTED;
cfg80211_put_bss(req.bss);
cfg80211_put_bss(&rdev->wiphy, req.bss);
}
return err;
@@ -514,7 +514,7 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
if (wdev->sme_state != CFG80211_SME_CONNECTED)
return -ENOTCONN;
if (WARN_ON(!wdev->current_bss))
if (WARN(!wdev->current_bss, "sme_state=%d\n", wdev->sme_state))
return -ENOTCONN;
memset(&req, 0, sizeof(req));
@@ -572,7 +572,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
if (wdev->current_bss) {
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(&wdev->current_bss->pub);
cfg80211_put_bss(&rdev->wiphy, &wdev->current_bss->pub);
wdev->current_bss = NULL;
}
}
@@ -987,3 +987,123 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
}
EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
void cfg80211_dfs_channels_update_work(struct work_struct *work)
{
struct delayed_work *delayed_work;
struct cfg80211_registered_device *rdev;
struct cfg80211_chan_def chandef;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *c;
struct wiphy *wiphy;
bool check_again = false;
unsigned long timeout, next_time = 0;
int bandid, i;
delayed_work = container_of(work, struct delayed_work, work);
rdev = container_of(delayed_work, struct cfg80211_registered_device,
dfs_update_channels_wk);
wiphy = &rdev->wiphy;
mutex_lock(&cfg80211_mutex);
for (bandid = 0; bandid < IEEE80211_NUM_BANDS; bandid++) {
sband = wiphy->bands[bandid];
if (!sband)
continue;
for (i = 0; i < sband->n_channels; i++) {
c = &sband->channels[i];
if (c->dfs_state != NL80211_DFS_UNAVAILABLE)
continue;
timeout = c->dfs_state_entered +
IEEE80211_DFS_MIN_NOP_TIME_MS;
if (time_after_eq(jiffies, timeout)) {
c->dfs_state = NL80211_DFS_USABLE;
cfg80211_chandef_create(&chandef, c,
NL80211_CHAN_NO_HT);
nl80211_radar_notify(rdev, &chandef,
NL80211_RADAR_NOP_FINISHED,
NULL, GFP_ATOMIC);
continue;
}
if (!check_again)
next_time = timeout - jiffies;
else
next_time = min(next_time, timeout - jiffies);
check_again = true;
}
}
mutex_unlock(&cfg80211_mutex);
/* reschedule if there are other channels waiting to be cleared again */
if (check_again)
queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
next_time);
}
void cfg80211_radar_event(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef,
gfp_t gfp)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
unsigned long timeout;
trace_cfg80211_radar_event(wiphy, chandef);
/* only set the chandef supplied channel to unavailable, in
* case the radar is detected on only one of multiple channels
* spanned by the chandef.
*/
cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS);
queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
timeout);
nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
}
EXPORT_SYMBOL(cfg80211_radar_event);
void cfg80211_cac_event(struct net_device *netdev,
enum nl80211_radar_event event, gfp_t gfp)
{
struct wireless_dev *wdev = netdev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct cfg80211_chan_def chandef;
unsigned long timeout;
trace_cfg80211_cac_event(netdev, event);
if (WARN_ON(!wdev->cac_started))
return;
if (WARN_ON(!wdev->channel))
return;
cfg80211_chandef_create(&chandef, wdev->channel, NL80211_CHAN_NO_HT);
switch (event) {
case NL80211_RADAR_CAC_FINISHED:
timeout = wdev->cac_start_time +
msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
WARN_ON(!time_after_eq(jiffies, timeout));
cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_AVAILABLE);
break;
case NL80211_RADAR_CAC_ABORTED:
break;
default:
WARN_ON(1);
return;
}
wdev->cac_started = false;
nl80211_radar_notify(rdev, &chandef, event, netdev, gfp);
}
EXPORT_SYMBOL(cfg80211_cac_event);

File diff suppressed because it is too large Load Diff

View File

@@ -108,6 +108,13 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
enum nl80211_cqm_rssi_threshold_event rssi_event,
gfp_t gfp);
void
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
struct cfg80211_chan_def *chandef,
enum nl80211_radar_event event,
struct net_device *netdev, gfp_t gfp);
void
nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer,

View File

@@ -866,6 +866,10 @@ static void handle_channel(struct wiphy *wiphy,
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
bw_flags = IEEE80211_CHAN_NO_HT40;
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80))
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160))
bw_flags |= IEEE80211_CHAN_NO_160MHZ;
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
request_wiphy && request_wiphy == wiphy &&
@@ -884,6 +888,9 @@ static void handle_channel(struct wiphy *wiphy,
return;
}
chan->dfs_state = NL80211_DFS_USABLE;
chan->dfs_state_entered = jiffies;
chan->beacon_found = false;
chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
chan->max_antenna_gain =
@@ -1261,6 +1268,10 @@ static void handle_channel_custom(struct wiphy *wiphy,
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40))
bw_flags = IEEE80211_CHAN_NO_HT40;
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(80))
bw_flags |= IEEE80211_CHAN_NO_80MHZ;
if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(160))
bw_flags |= IEEE80211_CHAN_NO_160MHZ;
chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
@@ -2189,10 +2200,15 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
* However if a driver requested this specific regulatory
* domain we keep it for its private use
*/
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER)
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) {
const struct ieee80211_regdomain *tmp;
tmp = get_wiphy_regdom(request_wiphy);
rcu_assign_pointer(request_wiphy->regd, rd);
else
rcu_free_regdom(tmp);
} else {
kfree(rd);
}
rd = NULL;

View File

@@ -19,55 +19,142 @@
#include "wext-compat.h"
#include "rdev-ops.h"
/**
* DOC: BSS tree/list structure
*
* At the top level, the BSS list is kept in both a list in each
* registered device (@bss_list) as well as an RB-tree for faster
* lookup. In the RB-tree, entries can be looked up using their
* channel, MESHID, MESHCONF (for MBSSes) or channel, BSSID, SSID
* for other BSSes.
*
* Due to the possibility of hidden SSIDs, there's a second level
* structure, the "hidden_list" and "hidden_beacon_bss" pointer.
* The hidden_list connects all BSSes belonging to a single AP
* that has a hidden SSID, and connects beacon and probe response
* entries. For a probe response entry for a hidden SSID, the
* hidden_beacon_bss pointer points to the BSS struct holding the
* beacon's information.
*
* Reference counting is done for all these references except for
* the hidden_list, so that a beacon BSS struct that is otherwise
* not referenced has one reference for being on the bss_list and
* one for each probe response entry that points to it using the
* hidden_beacon_bss pointer. When a BSS struct that has such a
* pointer is get/put, the refcount update is also propagated to
* the referenced struct, this ensure that it cannot get removed
* while somebody is using the probe response version.
*
* Note that the hidden_beacon_bss pointer never changes, due to
* the reference counting. Therefore, no locking is needed for
* it.
*
* Also note that the hidden_beacon_bss pointer is only relevant
* if the driver uses something other than the IEs, e.g. private
* data stored stored in the BSS struct, since the beacon IEs are
* also linked into the probe response struct.
*/
#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ)
static void bss_release(struct kref *ref)
static void bss_free(struct cfg80211_internal_bss *bss)
{
struct cfg80211_bss_ies *ies;
struct cfg80211_internal_bss *bss;
bss = container_of(ref, struct cfg80211_internal_bss, ref);
if (WARN_ON(atomic_read(&bss->hold)))
return;
if (bss->pub.free_priv)
bss->pub.free_priv(&bss->pub);
ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
if (ies)
if (ies && !bss->pub.hidden_beacon_bss)
kfree_rcu(ies, rcu_head);
ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies);
if (ies)
kfree_rcu(ies, rcu_head);
/*
* This happens when the module is removed, it doesn't
* really matter any more save for completeness
*/
if (!list_empty(&bss->hidden_list))
list_del(&bss->hidden_list);
kfree(bss);
}
/* must hold dev->bss_lock! */
static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *bss)
static inline void bss_ref_get(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *bss)
{
list_del_init(&bss->list);
rb_erase(&bss->rbn, &dev->bss_tree);
kref_put(&bss->ref, bss_release);
lockdep_assert_held(&dev->bss_lock);
bss->refcount++;
if (bss->pub.hidden_beacon_bss) {
bss = container_of(bss->pub.hidden_beacon_bss,
struct cfg80211_internal_bss,
pub);
bss->refcount++;
}
}
static inline void bss_ref_put(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *bss)
{
lockdep_assert_held(&dev->bss_lock);
if (bss->pub.hidden_beacon_bss) {
struct cfg80211_internal_bss *hbss;
hbss = container_of(bss->pub.hidden_beacon_bss,
struct cfg80211_internal_bss,
pub);
hbss->refcount--;
if (hbss->refcount == 0)
bss_free(hbss);
}
bss->refcount--;
if (bss->refcount == 0)
bss_free(bss);
}
static bool __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *bss)
{
lockdep_assert_held(&dev->bss_lock);
if (!list_empty(&bss->hidden_list)) {
/*
* don't remove the beacon entry if it has
* probe responses associated with it
*/
if (!bss->pub.hidden_beacon_bss)
return false;
/*
* if it's a probe response entry break its
* link to the other entries in the group
*/
list_del_init(&bss->hidden_list);
}
list_del_init(&bss->list);
rb_erase(&bss->rbn, &dev->bss_tree);
bss_ref_put(dev, bss);
return true;
}
/* must hold dev->bss_lock! */
static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev,
unsigned long expire_time)
{
struct cfg80211_internal_bss *bss, *tmp;
bool expired = false;
lockdep_assert_held(&dev->bss_lock);
list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
if (atomic_read(&bss->hold))
continue;
if (!time_after(expire_time, bss->ts))
continue;
__cfg80211_unlink_bss(dev, bss);
expired = true;
if (__cfg80211_unlink_bss(dev, bss))
expired = true;
}
if (expired)
@@ -234,15 +321,16 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
return 0;
}
/* must hold dev->bss_lock! */
void cfg80211_bss_age(struct cfg80211_registered_device *dev,
unsigned long age_secs)
{
struct cfg80211_internal_bss *bss;
unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
spin_lock_bh(&dev->bss_lock);
list_for_each_entry(bss, &dev->bss_list, list)
bss->ts -= age_jiffies;
spin_unlock_bh(&dev->bss_lock);
}
void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
@@ -277,40 +365,24 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,
if (!pos)
return NULL;
if (end - pos < sizeof(*ie))
return NULL;
ie = (struct ieee80211_vendor_ie *)pos;
/* make sure we can access ie->len */
BUILD_BUG_ON(offsetof(struct ieee80211_vendor_ie, len) != 1);
if (ie->len < sizeof(*ie))
goto cont;
ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2];
if (ie_oui == oui && ie->oui_type == oui_type)
return pos;
cont:
pos += 2 + ie->len;
}
return NULL;
}
EXPORT_SYMBOL(cfg80211_find_vendor_ie);
static int cmp_ies(u8 num, const u8 *ies1, int len1, const u8 *ies2, int len2)
{
const u8 *ie1 = cfg80211_find_ie(num, ies1, len1);
const u8 *ie2 = cfg80211_find_ie(num, ies2, len2);
/* equal if both missing */
if (!ie1 && !ie2)
return 0;
/* sort missing IE before (left of) present IE */
if (!ie1)
return -1;
if (!ie2)
return 1;
/* sort by length first, then by contents */
if (ie1[1] != ie2[1])
return ie2[1] - ie1[1];
return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
}
static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
const u8 *ssid, size_t ssid_len)
{
@@ -334,152 +406,82 @@ static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
return memcmp(ssidie + 2, ssid, ssid_len) == 0;
}
static bool is_mesh_bss(struct cfg80211_bss *a)
{
const struct cfg80211_bss_ies *ies;
const u8 *ie;
/**
* enum bss_compare_mode - BSS compare mode
* @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find)
* @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode
* @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode
*/
enum bss_compare_mode {
BSS_CMP_REGULAR,
BSS_CMP_HIDE_ZLEN,
BSS_CMP_HIDE_NUL,
};
if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
return false;
ies = rcu_access_pointer(a->ies);
if (!ies)
return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
if (!ie)
return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
if (!ie)
return false;
return true;
}
static bool is_mesh(struct cfg80211_bss *a,
const u8 *meshid, size_t meshidlen,
const u8 *meshcfg)
{
const struct cfg80211_bss_ies *ies;
const u8 *ie;
if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
return false;
ies = rcu_access_pointer(a->ies);
if (!ies)
return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
if (!ie)
return false;
if (ie[1] != meshidlen)
return false;
if (memcmp(ie + 2, meshid, meshidlen))
return false;
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
if (!ie)
return false;
if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
return false;
/*
* Ignore mesh capability (last two bytes of the IE) when
* comparing since that may differ between stations taking
* part in the same mesh.
*/
return memcmp(ie + 2, meshcfg,
sizeof(struct ieee80211_meshconf_ie) - 2) == 0;
}
static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b)
static int cmp_bss(struct cfg80211_bss *a,
struct cfg80211_bss *b,
enum bss_compare_mode mode)
{
const struct cfg80211_bss_ies *a_ies, *b_ies;
int r;
const u8 *ie1 = NULL;
const u8 *ie2 = NULL;
int i, r;
if (a->channel != b->channel)
return b->channel->center_freq - a->channel->center_freq;
if (is_mesh_bss(a) && is_mesh_bss(b)) {
a_ies = rcu_access_pointer(a->ies);
if (!a_ies)
return -1;
b_ies = rcu_access_pointer(b->ies);
if (!b_ies)
return 1;
a_ies = rcu_access_pointer(a->ies);
if (!a_ies)
return -1;
b_ies = rcu_access_pointer(b->ies);
if (!b_ies)
return 1;
r = cmp_ies(WLAN_EID_MESH_ID,
a_ies->data, a_ies->len,
b_ies->data, b_ies->len);
if (r)
return r;
return cmp_ies(WLAN_EID_MESH_CONFIG,
a_ies->data, a_ies->len,
b_ies->data, b_ies->len);
if (WLAN_CAPABILITY_IS_STA_BSS(a->capability))
ie1 = cfg80211_find_ie(WLAN_EID_MESH_ID,
a_ies->data, a_ies->len);
if (WLAN_CAPABILITY_IS_STA_BSS(b->capability))
ie2 = cfg80211_find_ie(WLAN_EID_MESH_ID,
b_ies->data, b_ies->len);
if (ie1 && ie2) {
int mesh_id_cmp;
if (ie1[1] == ie2[1])
mesh_id_cmp = memcmp(ie1 + 2, ie2 + 2, ie1[1]);
else
mesh_id_cmp = ie2[1] - ie1[1];
ie1 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
a_ies->data, a_ies->len);
ie2 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
b_ies->data, b_ies->len);
if (ie1 && ie2) {
if (mesh_id_cmp)
return mesh_id_cmp;
if (ie1[1] != ie2[1])
return ie2[1] - ie1[1];
return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
}
}
/*
* we can't use compare_ether_addr here since we need a < > operator.
* The binary return value of compare_ether_addr isn't enough
*/
return memcmp(a->bssid, b->bssid, sizeof(a->bssid));
}
static int cmp_bss(struct cfg80211_bss *a,
struct cfg80211_bss *b)
{
const struct cfg80211_bss_ies *a_ies, *b_ies;
int r;
r = cmp_bss_core(a, b);
r = memcmp(a->bssid, b->bssid, sizeof(a->bssid));
if (r)
return r;
a_ies = rcu_access_pointer(a->ies);
if (!a_ies)
return -1;
b_ies = rcu_access_pointer(b->ies);
if (!b_ies)
return 1;
return cmp_ies(WLAN_EID_SSID,
a_ies->data, a_ies->len,
b_ies->data, b_ies->len);
}
static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b)
{
const struct cfg80211_bss_ies *a_ies, *b_ies;
const u8 *ie1;
const u8 *ie2;
int i;
int r;
r = cmp_bss_core(a, b);
if (r)
return r;
a_ies = rcu_access_pointer(a->ies);
if (!a_ies)
return -1;
b_ies = rcu_access_pointer(b->ies);
if (!b_ies)
return 1;
ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len);
ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len);
if (!ie1 && !ie2)
return 0;
/*
* Key comparator must use same algorithm in any rb-tree
* search function (order is important), otherwise ordering
* of items in the tree is broken and search gives incorrect
* results. This code uses same order as cmp_ies() does.
*
* Note that due to the differring behaviour with hidden SSIDs
* this function only works when "b" is the tree element and
* "a" is the key we're looking for.
* Note that with "hide_ssid", the function returns a match if
* the already-present BSS ("b") is a hidden SSID beacon for
* the new BSS ("a").
*/
/* sort missing IE before (left of) present IE */
@@ -488,24 +490,36 @@ static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b)
if (!ie2)
return 1;
/* zero-size SSID is used as an indication of the hidden bss */
if (!ie2[1])
switch (mode) {
case BSS_CMP_HIDE_ZLEN:
/*
* In ZLEN mode we assume the BSS entry we're
* looking for has a zero-length SSID. So if
* the one we're looking at right now has that,
* return 0. Otherwise, return the difference
* in length, but since we're looking for the
* 0-length it's really equivalent to returning
* the length of the one we're looking at.
*
* No content comparison is needed as we assume
* the content length is zero.
*/
return ie2[1];
case BSS_CMP_REGULAR:
default:
/* sort by length first, then by contents */
if (ie1[1] != ie2[1])
return ie2[1] - ie1[1];
return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
case BSS_CMP_HIDE_NUL:
if (ie1[1] != ie2[1])
return ie2[1] - ie1[1];
/* this is equivalent to memcmp(zeroes, ie2 + 2, len) */
for (i = 0; i < ie2[1]; i++)
if (ie2[i + 2])
return -1;
return 0;
/* sort by length first, then by contents */
if (ie1[1] != ie2[1])
return ie2[1] - ie1[1];
/*
* zeroed SSID ie is another indication of a hidden bss;
* if it isn't zeroed just return the regular sort value
* to find the next candidate
*/
for (i = 0; i < ie2[1]; i++)
if (ie2[i + 2])
return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
return 0;
}
}
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
@@ -534,7 +548,7 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
continue;
if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
res = bss;
kref_get(&res->ref);
bss_ref_get(dev, res);
break;
}
}
@@ -547,34 +561,6 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
}
EXPORT_SYMBOL(cfg80211_get_bss);
struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy,
struct ieee80211_channel *channel,
const u8 *meshid, size_t meshidlen,
const u8 *meshcfg)
{
struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
struct cfg80211_internal_bss *bss, *res = NULL;
spin_lock_bh(&dev->bss_lock);
list_for_each_entry(bss, &dev->bss_list, list) {
if (channel && bss->pub.channel != channel)
continue;
if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) {
res = bss;
kref_get(&res->ref);
break;
}
}
spin_unlock_bh(&dev->bss_lock);
if (!res)
return NULL;
return &res->pub;
}
EXPORT_SYMBOL(cfg80211_get_mesh);
static void rb_insert_bss(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *bss)
{
@@ -587,7 +573,7 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev,
parent = *p;
tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
cmp = cmp_bss(&bss->pub, &tbss->pub);
cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR);
if (WARN_ON(!cmp)) {
/* will sort of leak this BSS */
@@ -606,7 +592,8 @@ static void rb_insert_bss(struct cfg80211_registered_device *dev,
static struct cfg80211_internal_bss *
rb_find_bss(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *res)
struct cfg80211_internal_bss *res,
enum bss_compare_mode mode)
{
struct rb_node *n = dev->bss_tree.rb_node;
struct cfg80211_internal_bss *bss;
@@ -614,7 +601,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
while (n) {
bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
r = cmp_bss(&res->pub, &bss->pub);
r = cmp_bss(&res->pub, &bss->pub, mode);
if (r == 0)
return bss;
@@ -627,46 +614,67 @@ rb_find_bss(struct cfg80211_registered_device *dev,
return NULL;
}
static struct cfg80211_internal_bss *
rb_find_hidden_bss(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *res)
{
struct rb_node *n = dev->bss_tree.rb_node;
struct cfg80211_internal_bss *bss;
int r;
while (n) {
bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
r = cmp_hidden_bss(&res->pub, &bss->pub);
if (r == 0)
return bss;
else if (r < 0)
n = n->rb_left;
else
n = n->rb_right;
}
return NULL;
}
static void
copy_hidden_ies(struct cfg80211_internal_bss *res,
struct cfg80211_internal_bss *hidden)
static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *new)
{
const struct cfg80211_bss_ies *ies;
struct cfg80211_internal_bss *bss;
const u8 *ie;
int i, ssidlen;
u8 fold = 0;
if (rcu_access_pointer(res->pub.beacon_ies))
return;
ies = rcu_access_pointer(hidden->pub.beacon_ies);
ies = rcu_access_pointer(new->pub.beacon_ies);
if (WARN_ON(!ies))
return;
return false;
ies = kmemdup(ies, sizeof(*ies) + ies->len, GFP_ATOMIC);
if (unlikely(!ies))
return;
rcu_assign_pointer(res->pub.beacon_ies, ies);
ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
if (!ie) {
/* nothing to do */
return true;
}
ssidlen = ie[1];
for (i = 0; i < ssidlen; i++)
fold |= ie[2 + i];
if (fold) {
/* not a hidden SSID */
return true;
}
/* This is the bad part ... */
list_for_each_entry(bss, &dev->bss_list, list) {
if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid))
continue;
if (bss->pub.channel != new->pub.channel)
continue;
if (rcu_access_pointer(bss->pub.beacon_ies))
continue;
ies = rcu_access_pointer(bss->pub.ies);
if (!ies)
continue;
ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
if (!ie)
continue;
if (ssidlen && ie[1] != ssidlen)
continue;
/* that would be odd ... */
if (bss->pub.beacon_ies)
continue;
if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss))
continue;
if (WARN_ON_ONCE(!list_empty(&bss->hidden_list)))
list_del(&bss->hidden_list);
/* combine them */
list_add(&bss->hidden_list, &new->hidden_list);
bss->pub.hidden_beacon_bss = &new->pub;
new->refcount += bss->refcount;
rcu_assign_pointer(bss->pub.beacon_ies,
new->pub.beacon_ies);
}
return true;
}
static struct cfg80211_internal_bss *
@@ -687,11 +695,10 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
return NULL;
}
found = rb_find_bss(dev, tmp);
found = rb_find_bss(dev, tmp, BSS_CMP_REGULAR);
if (found) {
found->pub.beacon_interval = tmp->pub.beacon_interval;
found->pub.tsf = tmp->pub.tsf;
found->pub.signal = tmp->pub.signal;
found->pub.capability = tmp->pub.capability;
found->ts = tmp->ts;
@@ -711,19 +718,45 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
kfree_rcu((struct cfg80211_bss_ies *)old,
rcu_head);
} else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
const struct cfg80211_bss_ies *old, *ies;
const struct cfg80211_bss_ies *old;
struct cfg80211_internal_bss *bss;
if (found->pub.hidden_beacon_bss &&
!list_empty(&found->hidden_list)) {
/*
* The found BSS struct is one of the probe
* response members of a group, but we're
* receiving a beacon (beacon_ies in the tmp
* bss is used). This can only mean that the
* AP changed its beacon from not having an
* SSID to showing it, which is confusing so
* drop this information.
*/
goto drop;
}
old = rcu_access_pointer(found->pub.beacon_ies);
ies = rcu_access_pointer(found->pub.ies);
rcu_assign_pointer(found->pub.beacon_ies,
tmp->pub.beacon_ies);
/* Override IEs if they were from a beacon before */
if (old == ies)
if (old == rcu_access_pointer(found->pub.ies))
rcu_assign_pointer(found->pub.ies,
tmp->pub.beacon_ies);
/* Assign beacon IEs to all sub entries */
list_for_each_entry(bss, &found->hidden_list,
hidden_list) {
const struct cfg80211_bss_ies *ies;
ies = rcu_access_pointer(bss->pub.beacon_ies);
WARN_ON(ies != old);
rcu_assign_pointer(bss->pub.beacon_ies,
tmp->pub.beacon_ies);
}
if (old)
kfree_rcu((struct cfg80211_bss_ies *)old,
rcu_head);
@@ -733,19 +766,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *hidden;
struct cfg80211_bss_ies *ies;
/* First check if the beacon is a probe response from
* a hidden bss. If so, copy beacon ies (with nullified
* ssid) into the probe response bss entry (with real ssid).
* It is required basically for PSM implementation
* (probe responses do not contain tim ie) */
/* TODO: The code is not trying to update existing probe
* response bss entries when beacon ies are
* getting changed. */
hidden = rb_find_hidden_bss(dev, tmp);
if (hidden)
copy_hidden_ies(tmp, hidden);
/*
* create a copy -- the "res" variable that is passed in
* is allocated on the stack since it's not needed in the
@@ -760,21 +780,51 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
if (ies)
kfree_rcu(ies, rcu_head);
spin_unlock_bh(&dev->bss_lock);
return NULL;
goto drop;
}
memcpy(new, tmp, sizeof(*new));
kref_init(&new->ref);
new->refcount = 1;
INIT_LIST_HEAD(&new->hidden_list);
if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
hidden = rb_find_bss(dev, tmp, BSS_CMP_HIDE_ZLEN);
if (!hidden)
hidden = rb_find_bss(dev, tmp,
BSS_CMP_HIDE_NUL);
if (hidden) {
new->pub.hidden_beacon_bss = &hidden->pub;
list_add(&new->hidden_list,
&hidden->hidden_list);
hidden->refcount++;
rcu_assign_pointer(new->pub.beacon_ies,
hidden->pub.beacon_ies);
}
} else {
/*
* Ok so we found a beacon, and don't have an entry. If
* it's a beacon with hidden SSID, we might be in for an
* expensive search for any probe responses that should
* be grouped with this beacon for updates ...
*/
if (!cfg80211_combine_bsses(dev, new)) {
kfree(new);
goto drop;
}
}
list_add_tail(&new->list, &dev->bss_list);
rb_insert_bss(dev, new);
found = new;
}
dev->bss_generation++;
bss_ref_get(dev, found);
spin_unlock_bh(&dev->bss_lock);
kref_get(&found->ref);
return found;
drop:
spin_unlock_bh(&dev->bss_lock);
return NULL;
}
static struct ieee80211_channel *
@@ -833,7 +883,6 @@ cfg80211_inform_bss(struct wiphy *wiphy,
memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
tmp.pub.channel = channel;
tmp.pub.signal = signal;
tmp.pub.tsf = tsf;
tmp.pub.beacon_interval = beacon_interval;
tmp.pub.capability = capability;
/*
@@ -841,16 +890,14 @@ cfg80211_inform_bss(struct wiphy *wiphy,
* Response frame, we need to pick one of the options and only use it
* with the driver that does not provide the full Beacon/Probe Response
* frame. Use Beacon frame pointer to avoid indicating that this should
* override the iies pointer should we have received an earlier
* override the IEs pointer should we have received an earlier
* indication of Probe Response data.
*
* The initial buffer for the IEs is allocated with the BSS entry and
* is located after the private area.
*/
ies = kmalloc(sizeof(*ies) + ielen, gfp);
if (!ies)
return NULL;
ies->len = ielen;
ies->tsf = tsf;
memcpy(ies->data, ie, ielen);
rcu_assign_pointer(tmp.pub.beacon_ies, ies);
@@ -907,6 +954,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
if (!ies)
return NULL;
ies->len = ielen;
ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
if (ieee80211_is_probe_resp(mgmt->frame_control))
@@ -918,7 +966,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
tmp.pub.channel = channel;
tmp.pub.signal = signal;
tmp.pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
@@ -935,27 +982,35 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
}
EXPORT_SYMBOL(cfg80211_inform_bss_frame);
void cfg80211_ref_bss(struct cfg80211_bss *pub)
void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
{
struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
struct cfg80211_internal_bss *bss;
if (!pub)
return;
bss = container_of(pub, struct cfg80211_internal_bss, pub);
kref_get(&bss->ref);
spin_lock_bh(&dev->bss_lock);
bss_ref_get(dev, bss);
spin_unlock_bh(&dev->bss_lock);
}
EXPORT_SYMBOL(cfg80211_ref_bss);
void cfg80211_put_bss(struct cfg80211_bss *pub)
void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
{
struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
struct cfg80211_internal_bss *bss;
if (!pub)
return;
bss = container_of(pub, struct cfg80211_internal_bss, pub);
kref_put(&bss->ref, bss_release);
spin_lock_bh(&dev->bss_lock);
bss_ref_put(dev, bss);
spin_unlock_bh(&dev->bss_lock);
}
EXPORT_SYMBOL(cfg80211_put_bss);
@@ -971,8 +1026,8 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
spin_lock_bh(&dev->bss_lock);
if (!list_empty(&bss->list)) {
__cfg80211_unlink_bss(dev, bss);
dev->bss_generation++;
if (__cfg80211_unlink_bss(dev, bss))
dev->bss_generation++;
}
spin_unlock_bh(&dev->bss_lock);
}
@@ -1155,16 +1210,6 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
}
}
static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
{
unsigned long end = jiffies;
if (end >= start)
return jiffies_to_msecs(end - start);
return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
}
static char *
ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
struct cfg80211_internal_bss *bss, char *current_ev,
@@ -1241,15 +1286,10 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
rcu_read_lock();
ies = rcu_dereference(bss->pub.ies);
if (ies) {
rem = ies->len;
ie = ies->data;
} else {
rem = 0;
ie = NULL;
}
rem = ies->len;
ie = ies->data;
while (ies && rem >= 2) {
while (rem >= 2) {
/* invalid data */
if (ie[1] > rem - 2)
break;
@@ -1362,7 +1402,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
if (buf) {
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVCUSTOM;
sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf));
sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
&iwe, buf);

View File

@@ -301,7 +301,7 @@ static void __cfg80211_sme_scan_done(struct net_device *dev)
bss = cfg80211_get_conn_bss(wdev);
if (bss) {
cfg80211_put_bss(bss);
cfg80211_put_bss(&rdev->wiphy, bss);
} else {
/* not found */
if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
@@ -464,7 +464,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
if (wdev->current_bss) {
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(&wdev->current_bss->pub);
cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
wdev->current_bss = NULL;
}
@@ -480,7 +480,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
kfree(wdev->connect_keys);
wdev->connect_keys = NULL;
wdev->ssid_len = 0;
cfg80211_put_bss(bss);
cfg80211_put_bss(wdev->wiphy, bss);
return;
}
@@ -586,7 +586,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
}
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(&wdev->current_bss->pub);
cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
wdev->current_bss = NULL;
cfg80211_hold_bss(bss_from_pub(bss));
@@ -621,7 +621,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev,
return;
out:
cfg80211_put_bss(bss);
cfg80211_put_bss(wdev->wiphy, bss);
}
void cfg80211_roamed(struct net_device *dev,
@@ -663,7 +663,7 @@ void cfg80211_roamed_bss(struct net_device *dev,
ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
if (!ev) {
cfg80211_put_bss(bss);
cfg80211_put_bss(wdev->wiphy, bss);
return;
}
@@ -704,7 +704,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
if (wdev->current_bss) {
cfg80211_unhold_bss(wdev->current_bss);
cfg80211_put_bss(&wdev->current_bss->pub);
cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
}
wdev->current_bss = NULL;
@@ -875,7 +875,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
if (bss) {
wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
err = cfg80211_conn_do_work(wdev);
cfg80211_put_bss(bss);
cfg80211_put_bss(wdev->wiphy, bss);
} else {
/* otherwise we'll need to scan for the AP first */
err = cfg80211_conn_scan(wdev);

View File

@@ -106,9 +106,7 @@ static int wiphy_resume(struct device *dev)
int ret = 0;
/* Age scan results with time spent in suspend */
spin_lock_bh(&rdev->bss_lock);
cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at);
spin_unlock_bh(&rdev->bss_lock);
if (rdev->ops->resume) {
rtnl_lock();

View File

@@ -2051,6 +2051,21 @@ TRACE_EVENT(cfg80211_reg_can_beacon,
WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
);
TRACE_EVENT(cfg80211_chandef_dfs_required,
TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
TP_ARGS(wiphy, chandef),
TP_STRUCT__entry(
WIPHY_ENTRY
CHAN_DEF_ENTRY
),
TP_fast_assign(
WIPHY_ASSIGN;
CHAN_DEF_ASSIGN(chandef);
),
TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
);
TRACE_EVENT(cfg80211_ch_switch_notify,
TP_PROTO(struct net_device *netdev,
struct cfg80211_chan_def *chandef),
@@ -2067,6 +2082,36 @@ TRACE_EVENT(cfg80211_ch_switch_notify,
NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
);
TRACE_EVENT(cfg80211_radar_event,
TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
TP_ARGS(wiphy, chandef),
TP_STRUCT__entry(
WIPHY_ENTRY
CHAN_DEF_ENTRY
),
TP_fast_assign(
WIPHY_ASSIGN;
CHAN_DEF_ASSIGN(chandef);
),
TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
);
TRACE_EVENT(cfg80211_cac_event,
TP_PROTO(struct net_device *netdev, enum nl80211_radar_event evt),
TP_ARGS(netdev, evt),
TP_STRUCT__entry(
NETDEV_ENTRY
__field(enum nl80211_radar_event, evt)
),
TP_fast_assign(
NETDEV_ASSIGN;
__entry->evt = evt;
),
TP_printk(NETDEV_PR_FMT ", event: %d",
NETDEV_PR_ARG, __entry->evt)
);
DECLARE_EVENT_CLASS(cfg80211_rx_evt,
TP_PROTO(struct net_device *netdev, const u8 *addr),
TP_ARGS(netdev, addr),
@@ -2333,6 +2378,41 @@ TRACE_EVENT(cfg80211_return_u32,
TP_printk("ret: %u", __entry->ret)
);
TRACE_EVENT(cfg80211_report_wowlan_wakeup,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
struct cfg80211_wowlan_wakeup *wakeup),
TP_ARGS(wiphy, wdev, wakeup),
TP_STRUCT__entry(
WIPHY_ENTRY
WDEV_ENTRY
__field(bool, disconnect)
__field(bool, magic_pkt)
__field(bool, gtk_rekey_failure)
__field(bool, eap_identity_req)
__field(bool, four_way_handshake)
__field(bool, rfkill_release)
__field(s32, pattern_idx)
__field(u32, packet_len)
__dynamic_array(u8, packet, wakeup->packet_present_len)
),
TP_fast_assign(
WIPHY_ASSIGN;
WDEV_ASSIGN;
__entry->disconnect = wakeup->disconnect;
__entry->magic_pkt = wakeup->magic_pkt;
__entry->gtk_rekey_failure = wakeup->gtk_rekey_failure;
__entry->eap_identity_req = wakeup->eap_identity_req;
__entry->four_way_handshake = wakeup->four_way_handshake;
__entry->rfkill_release = wakeup->rfkill_release;
__entry->pattern_idx = wakeup->pattern_idx;
__entry->packet_len = wakeup->packet_len;
if (wakeup->packet && wakeup->packet_present_len)
memcpy(__get_dynamic_array(packet), wakeup->packet,
wakeup->packet_present_len);
),
TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
);
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH

View File

@@ -1217,10 +1217,10 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
break;
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_MONITOR:
radar_required = false;
break;
case NL80211_IFTYPE_P2P_DEVICE:
case NUM_NL80211_IFTYPES:
case NL80211_IFTYPE_UNSPECIFIED:
default: