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:
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
|
@@ -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
@@ -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,
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
|
@@ -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
|
||||
|
@@ -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:
|
||||
|
Reference in New Issue
Block a user