Merge tag 'mac80211-next-for-davem-2016-02-26' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Johannes Berg says: ==================== Here's another round of updates for -next: * big A-MSDU RX performance improvement (avoid linearize of paged RX) * rfkill changes: cleanups, documentation, platform properties * basic PBSS support in cfg80211 * MU-MIMO action frame processing support * BlockAck reordering & duplicate detection offload support * various cleanups & little fixes ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -50,8 +50,8 @@ config CFG80211_DEVELOPER_WARNINGS
|
||||
default n
|
||||
help
|
||||
This option enables some additional warnings that help
|
||||
cfg80211 developers and driver developers, but that can
|
||||
trigger due to races with userspace.
|
||||
cfg80211 developers and driver developers, but beware that
|
||||
they can also trigger due to races with userspace.
|
||||
|
||||
For example, when a driver reports that it was disconnected
|
||||
from the AP, but the user disconnects manually at the same
|
||||
@@ -61,19 +61,6 @@ config CFG80211_DEVELOPER_WARNINGS
|
||||
on it (or mac80211).
|
||||
|
||||
|
||||
config CFG80211_REG_DEBUG
|
||||
bool "cfg80211 regulatory debugging"
|
||||
depends on CFG80211
|
||||
default n
|
||||
---help---
|
||||
You can enable this if you want to debug regulatory changes.
|
||||
For more information on cfg80211 regulatory refer to the wireless
|
||||
wiki:
|
||||
|
||||
http://wireless.kernel.org/en/developers/Regulatory
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config CFG80211_CERTIFICATION_ONUS
|
||||
bool "cfg80211 certification onus"
|
||||
depends on CFG80211 && EXPERT
|
||||
@@ -123,7 +110,7 @@ config CFG80211_REG_RELAX_NO_IR
|
||||
interface which associated to an AP which userspace assumes or confirms
|
||||
to be an authorized master, i.e., with radar detection support and DFS
|
||||
capabilities. However, note that in order to not create daisy chain
|
||||
scenarios, this relaxation is not allowed in cases that the BSS client
|
||||
scenarios, this relaxation is not allowed in cases where the BSS client
|
||||
is associated to P2P GO and in addition the P2P GO instantiated on
|
||||
a channel due to this relaxation should not allow connection from
|
||||
non P2P clients.
|
||||
@@ -148,7 +135,7 @@ config CFG80211_DEBUGFS
|
||||
depends on CFG80211
|
||||
depends on DEBUG_FS
|
||||
---help---
|
||||
You can enable this if you want to debugfs entries for cfg80211.
|
||||
You can enable this if you want debugfs entries for cfg80211.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
@@ -159,7 +146,7 @@ config CFG80211_INTERNAL_REGDB
|
||||
---help---
|
||||
This option generates an internal data structure representing
|
||||
the wireless regulatory rules described in net/wireless/db.txt
|
||||
and includes code to query that database. This is an alternative
|
||||
and includes code to query that database. This is an alternative
|
||||
to using CRDA for defining regulatory rules for the kernel.
|
||||
|
||||
Using this option requires some parsing of the db.txt at build time,
|
||||
@@ -172,7 +159,7 @@ config CFG80211_INTERNAL_REGDB
|
||||
|
||||
http://wireless.kernel.org/en/developers/Regulatory
|
||||
|
||||
Most distributions have a CRDA package. So if unsure, say N.
|
||||
Most distributions have a CRDA package. So if unsure, say N.
|
||||
|
||||
config CFG80211_CRDA_SUPPORT
|
||||
bool "support CRDA" if CFG80211_INTERNAL_REGDB
|
||||
|
@@ -352,6 +352,16 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
|
||||
WARN_ON(ops->add_station && !ops->del_station);
|
||||
WARN_ON(ops->add_mpath && !ops->del_mpath);
|
||||
WARN_ON(ops->join_mesh && !ops->leave_mesh);
|
||||
WARN_ON(ops->start_p2p_device && !ops->stop_p2p_device);
|
||||
WARN_ON(ops->start_ap && !ops->stop_ap);
|
||||
WARN_ON(ops->join_ocb && !ops->leave_ocb);
|
||||
WARN_ON(ops->suspend && !ops->resume);
|
||||
WARN_ON(ops->sched_scan_start && !ops->sched_scan_stop);
|
||||
WARN_ON(ops->remain_on_channel && !ops->cancel_remain_on_channel);
|
||||
WARN_ON(ops->tdls_channel_switch && !ops->tdls_cancel_channel_switch);
|
||||
WARN_ON(ops->add_tx_ts && !ops->del_tx_ts);
|
||||
WARN_ON(ops->set_tx_power && !ops->get_tx_power);
|
||||
WARN_ON(ops->set_antenna && !ops->get_antenna);
|
||||
|
||||
alloc_size = sizeof(*rdev) + sizeof_priv;
|
||||
|
||||
|
@@ -711,7 +711,7 @@ EXPORT_SYMBOL(cfg80211_rx_mgmt);
|
||||
|
||||
void cfg80211_dfs_channels_update_work(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *delayed_work;
|
||||
struct delayed_work *delayed_work = to_delayed_work(work);
|
||||
struct cfg80211_registered_device *rdev;
|
||||
struct cfg80211_chan_def chandef;
|
||||
struct ieee80211_supported_band *sband;
|
||||
@@ -721,7 +721,6 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
|
||||
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;
|
||||
|
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright 2015 Intel Deutschland GmbH
|
||||
* Copyright 2015-2016 Intel Deutschland GmbH
|
||||
*/
|
||||
|
||||
#include <linux/if.h>
|
||||
@@ -401,6 +401,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
||||
[NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_PBSS] = { .type = NLA_FLAG },
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
@@ -3461,6 +3462,10 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
||||
return PTR_ERR(params.acl);
|
||||
}
|
||||
|
||||
params.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]);
|
||||
if (params.pbss && !rdev->wiphy.bands[IEEE80211_BAND_60GHZ])
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = rdev_start_ap(rdev, dev, ¶ms);
|
||||
if (!err) {
|
||||
@@ -7281,9 +7286,11 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
|
||||
}
|
||||
|
||||
if (nla_get_flag(info->attrs[NL80211_ATTR_USE_RRM])) {
|
||||
if (!(rdev->wiphy.features &
|
||||
NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) ||
|
||||
!(rdev->wiphy.features & NL80211_FEATURE_QUIET))
|
||||
if (!((rdev->wiphy.features &
|
||||
NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) &&
|
||||
(rdev->wiphy.features & NL80211_FEATURE_QUIET)) &&
|
||||
!wiphy_ext_feature_isset(&rdev->wiphy,
|
||||
NL80211_EXT_FEATURE_RRM))
|
||||
return -EINVAL;
|
||||
req.flags |= ASSOC_REQ_USE_RRM;
|
||||
}
|
||||
@@ -7971,15 +7978,23 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
|
||||
}
|
||||
|
||||
if (nla_get_flag(info->attrs[NL80211_ATTR_USE_RRM])) {
|
||||
if (!(rdev->wiphy.features &
|
||||
NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) ||
|
||||
!(rdev->wiphy.features & NL80211_FEATURE_QUIET)) {
|
||||
if (!((rdev->wiphy.features &
|
||||
NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) &&
|
||||
(rdev->wiphy.features & NL80211_FEATURE_QUIET)) &&
|
||||
!wiphy_ext_feature_isset(&rdev->wiphy,
|
||||
NL80211_EXT_FEATURE_RRM)) {
|
||||
kzfree(connkeys);
|
||||
return -EINVAL;
|
||||
}
|
||||
connect.flags |= ASSOC_REQ_USE_RRM;
|
||||
}
|
||||
|
||||
connect.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]);
|
||||
if (connect.pbss && !rdev->wiphy.bands[IEEE80211_BAND_60GHZ]) {
|
||||
kzfree(connkeys);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
wdev_lock(dev->ieee80211_ptr);
|
||||
err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL);
|
||||
wdev_unlock(dev->ieee80211_ptr);
|
||||
|
@@ -43,6 +43,7 @@ static const struct radiotap_align_size rtap_namespace_sizes[] = {
|
||||
[IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, },
|
||||
[IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, },
|
||||
[IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, },
|
||||
[IEEE80211_RADIOTAP_VHT] = { .align = 2, .size = 12, },
|
||||
/*
|
||||
* add more here as they are defined in radiotap.h
|
||||
*/
|
||||
|
@@ -60,13 +60,6 @@
|
||||
#include "regdb.h"
|
||||
#include "nl80211.h"
|
||||
|
||||
#ifdef CONFIG_CFG80211_REG_DEBUG
|
||||
#define REG_DBG_PRINT(format, args...) \
|
||||
printk(KERN_DEBUG pr_fmt(format), ##args)
|
||||
#else
|
||||
#define REG_DBG_PRINT(args...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Grace period we give before making sure all current interfaces reside on
|
||||
* channels allowed by the current regulatory domain.
|
||||
@@ -178,12 +171,10 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy)
|
||||
if (wiphy_regd->dfs_region == regd->dfs_region)
|
||||
goto out;
|
||||
|
||||
REG_DBG_PRINT("%s: device specific dfs_region "
|
||||
"(%s) disagrees with cfg80211's "
|
||||
"central dfs_region (%s)\n",
|
||||
dev_name(&wiphy->dev),
|
||||
reg_dfs_region_str(wiphy_regd->dfs_region),
|
||||
reg_dfs_region_str(regd->dfs_region));
|
||||
pr_debug("%s: device specific dfs_region (%s) disagrees with cfg80211's central dfs_region (%s)\n",
|
||||
dev_name(&wiphy->dev),
|
||||
reg_dfs_region_str(wiphy_regd->dfs_region),
|
||||
reg_dfs_region_str(regd->dfs_region));
|
||||
|
||||
out:
|
||||
return regd->dfs_region;
|
||||
@@ -543,7 +534,7 @@ static DECLARE_DELAYED_WORK(crda_timeout, crda_timeout_work);
|
||||
|
||||
static void crda_timeout_work(struct work_struct *work)
|
||||
{
|
||||
REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
|
||||
pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
|
||||
rtnl_lock();
|
||||
reg_crda_timeouts++;
|
||||
restore_regulatory_settings(true);
|
||||
@@ -585,7 +576,7 @@ static int call_crda(const char *alpha2)
|
||||
|
||||
if (!is_world_regdom((char *) alpha2))
|
||||
pr_debug("Calling CRDA for country: %c%c\n",
|
||||
alpha2[0], alpha2[1]);
|
||||
alpha2[0], alpha2[1]);
|
||||
else
|
||||
pr_debug("Calling CRDA to update world regulatory domain\n");
|
||||
|
||||
@@ -1132,42 +1123,6 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
|
||||
}
|
||||
EXPORT_SYMBOL(reg_initiator_name);
|
||||
|
||||
static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
|
||||
struct ieee80211_channel *chan,
|
||||
const struct ieee80211_reg_rule *reg_rule)
|
||||
{
|
||||
#ifdef CONFIG_CFG80211_REG_DEBUG
|
||||
const struct ieee80211_power_rule *power_rule;
|
||||
const struct ieee80211_freq_range *freq_range;
|
||||
char max_antenna_gain[32], bw[32];
|
||||
|
||||
power_rule = ®_rule->power_rule;
|
||||
freq_range = ®_rule->freq_range;
|
||||
|
||||
if (!power_rule->max_antenna_gain)
|
||||
snprintf(max_antenna_gain, sizeof(max_antenna_gain), "N/A");
|
||||
else
|
||||
snprintf(max_antenna_gain, sizeof(max_antenna_gain), "%d mBi",
|
||||
power_rule->max_antenna_gain);
|
||||
|
||||
if (reg_rule->flags & NL80211_RRF_AUTO_BW)
|
||||
snprintf(bw, sizeof(bw), "%d KHz, %d KHz AUTO",
|
||||
freq_range->max_bandwidth_khz,
|
||||
reg_get_max_bandwidth(regd, reg_rule));
|
||||
else
|
||||
snprintf(bw, sizeof(bw), "%d KHz",
|
||||
freq_range->max_bandwidth_khz);
|
||||
|
||||
REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n",
|
||||
chan->center_freq);
|
||||
|
||||
REG_DBG_PRINT("(%d KHz - %d KHz @ %s), (%s, %d mBm)\n",
|
||||
freq_range->start_freq_khz, freq_range->end_freq_khz,
|
||||
bw, max_antenna_gain,
|
||||
power_rule->max_eirp);
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint32_t reg_rule_to_chan_bw_flags(const struct ieee80211_regdomain *regd,
|
||||
const struct ieee80211_reg_rule *reg_rule,
|
||||
const struct ieee80211_channel *chan)
|
||||
@@ -1242,20 +1197,19 @@ static void handle_channel(struct wiphy *wiphy,
|
||||
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
|
||||
request_wiphy && request_wiphy == wiphy &&
|
||||
request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
|
||||
REG_DBG_PRINT("Disabling freq %d MHz for good\n",
|
||||
chan->center_freq);
|
||||
pr_debug("Disabling freq %d MHz for good\n",
|
||||
chan->center_freq);
|
||||
chan->orig_flags |= IEEE80211_CHAN_DISABLED;
|
||||
chan->flags = chan->orig_flags;
|
||||
} else {
|
||||
REG_DBG_PRINT("Disabling freq %d MHz\n",
|
||||
chan->center_freq);
|
||||
pr_debug("Disabling freq %d MHz\n",
|
||||
chan->center_freq);
|
||||
chan->flags |= IEEE80211_CHAN_DISABLED;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
regd = reg_get_regdomain(wiphy);
|
||||
chan_reg_rule_print_dbg(regd, chan, reg_rule);
|
||||
|
||||
power_rule = ®_rule->power_rule;
|
||||
bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
|
||||
@@ -1393,18 +1347,15 @@ static bool ignore_reg_update(struct wiphy *wiphy,
|
||||
return true;
|
||||
|
||||
if (!lr) {
|
||||
REG_DBG_PRINT("Ignoring regulatory request set by %s "
|
||||
"since last_request is not set\n",
|
||||
reg_initiator_name(initiator));
|
||||
pr_debug("Ignoring regulatory request set by %s since last_request is not set\n",
|
||||
reg_initiator_name(initiator));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (initiator == NL80211_REGDOM_SET_BY_CORE &&
|
||||
wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
|
||||
REG_DBG_PRINT("Ignoring regulatory request set by %s "
|
||||
"since the driver uses its own custom "
|
||||
"regulatory domain\n",
|
||||
reg_initiator_name(initiator));
|
||||
pr_debug("Ignoring regulatory request set by %s since the driver uses its own custom regulatory domain\n",
|
||||
reg_initiator_name(initiator));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1415,10 +1366,8 @@ static bool ignore_reg_update(struct wiphy *wiphy,
|
||||
if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd &&
|
||||
initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
|
||||
!is_world_regdom(lr->alpha2)) {
|
||||
REG_DBG_PRINT("Ignoring regulatory request set by %s "
|
||||
"since the driver requires its own regulatory "
|
||||
"domain to be set first\n",
|
||||
reg_initiator_name(initiator));
|
||||
pr_debug("Ignoring regulatory request set by %s since the driver requires its own regulatory domain to be set first\n",
|
||||
reg_initiator_name(initiator));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1699,7 +1648,7 @@ static void reg_check_chans_work(struct work_struct *work)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev;
|
||||
|
||||
REG_DBG_PRINT("Verifying active interfaces after reg change\n");
|
||||
pr_debug("Verifying active interfaces after reg change\n");
|
||||
rtnl_lock();
|
||||
|
||||
list_for_each_entry(rdev, &cfg80211_rdev_list, list)
|
||||
@@ -1781,8 +1730,8 @@ static void handle_channel_custom(struct wiphy *wiphy,
|
||||
}
|
||||
|
||||
if (IS_ERR(reg_rule)) {
|
||||
REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
|
||||
chan->center_freq);
|
||||
pr_debug("Disabling freq %d MHz as custom regd has no rule that fits it\n",
|
||||
chan->center_freq);
|
||||
if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {
|
||||
chan->flags |= IEEE80211_CHAN_DISABLED;
|
||||
} else {
|
||||
@@ -1792,8 +1741,6 @@ static void handle_channel_custom(struct wiphy *wiphy,
|
||||
return;
|
||||
}
|
||||
|
||||
chan_reg_rule_print_dbg(regd, chan, reg_rule);
|
||||
|
||||
power_rule = ®_rule->power_rule;
|
||||
bw_flags = reg_rule_to_chan_bw_flags(regd, reg_rule, chan);
|
||||
|
||||
@@ -2524,7 +2471,7 @@ static void restore_alpha2(char *alpha2, bool reset_user)
|
||||
if (is_user_regdom_saved()) {
|
||||
/* Unless we're asked to ignore it and reset it */
|
||||
if (reset_user) {
|
||||
REG_DBG_PRINT("Restoring regulatory settings including user preference\n");
|
||||
pr_debug("Restoring regulatory settings including user preference\n");
|
||||
user_alpha2[0] = '9';
|
||||
user_alpha2[1] = '7';
|
||||
|
||||
@@ -2534,24 +2481,24 @@ static void restore_alpha2(char *alpha2, bool reset_user)
|
||||
* back as they were for a full restore.
|
||||
*/
|
||||
if (!is_world_regdom(ieee80211_regdom)) {
|
||||
REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
|
||||
ieee80211_regdom[0], ieee80211_regdom[1]);
|
||||
pr_debug("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
|
||||
ieee80211_regdom[0], ieee80211_regdom[1]);
|
||||
alpha2[0] = ieee80211_regdom[0];
|
||||
alpha2[1] = ieee80211_regdom[1];
|
||||
}
|
||||
} else {
|
||||
REG_DBG_PRINT("Restoring regulatory settings while preserving user preference for: %c%c\n",
|
||||
user_alpha2[0], user_alpha2[1]);
|
||||
pr_debug("Restoring regulatory settings while preserving user preference for: %c%c\n",
|
||||
user_alpha2[0], user_alpha2[1]);
|
||||
alpha2[0] = user_alpha2[0];
|
||||
alpha2[1] = user_alpha2[1];
|
||||
}
|
||||
} else if (!is_world_regdom(ieee80211_regdom)) {
|
||||
REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
|
||||
ieee80211_regdom[0], ieee80211_regdom[1]);
|
||||
pr_debug("Keeping preference on module parameter ieee80211_regdom: %c%c\n",
|
||||
ieee80211_regdom[0], ieee80211_regdom[1]);
|
||||
alpha2[0] = ieee80211_regdom[0];
|
||||
alpha2[1] = ieee80211_regdom[1];
|
||||
} else
|
||||
REG_DBG_PRINT("Restoring regulatory settings\n");
|
||||
pr_debug("Restoring regulatory settings\n");
|
||||
}
|
||||
|
||||
static void restore_custom_reg_settings(struct wiphy *wiphy)
|
||||
@@ -2663,14 +2610,14 @@ static void restore_regulatory_settings(bool reset_user)
|
||||
list_splice_tail_init(&tmp_reg_req_list, ®_requests_list);
|
||||
spin_unlock(®_requests_lock);
|
||||
|
||||
REG_DBG_PRINT("Kicking the queue\n");
|
||||
pr_debug("Kicking the queue\n");
|
||||
|
||||
schedule_work(®_work);
|
||||
}
|
||||
|
||||
void regulatory_hint_disconnect(void)
|
||||
{
|
||||
REG_DBG_PRINT("All devices are disconnected, going to restore regulatory settings\n");
|
||||
pr_debug("All devices are disconnected, going to restore regulatory settings\n");
|
||||
restore_regulatory_settings(false);
|
||||
}
|
||||
|
||||
@@ -2718,10 +2665,10 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
|
||||
if (!reg_beacon)
|
||||
return -ENOMEM;
|
||||
|
||||
REG_DBG_PRINT("Found new beacon on frequency: %d MHz (Ch %d) on %s\n",
|
||||
beacon_chan->center_freq,
|
||||
ieee80211_frequency_to_channel(beacon_chan->center_freq),
|
||||
wiphy_name(wiphy));
|
||||
pr_debug("Found new beacon on frequency: %d MHz (Ch %d) on %s\n",
|
||||
beacon_chan->center_freq,
|
||||
ieee80211_frequency_to_channel(beacon_chan->center_freq),
|
||||
wiphy_name(wiphy));
|
||||
|
||||
memcpy(®_beacon->chan, beacon_chan,
|
||||
sizeof(struct ieee80211_channel));
|
||||
@@ -2800,8 +2747,7 @@ bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region)
|
||||
case NL80211_DFS_JP:
|
||||
return true;
|
||||
default:
|
||||
REG_DBG_PRINT("Ignoring uknown DFS master region: %d\n",
|
||||
dfs_region);
|
||||
pr_debug("Ignoring uknown DFS master region: %d\n", dfs_region);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -264,7 +264,7 @@ static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
|
||||
wdev->conn->params.bssid,
|
||||
wdev->conn->params.ssid,
|
||||
wdev->conn->params.ssid_len,
|
||||
IEEE80211_BSS_TYPE_ESS,
|
||||
wdev->conn_bss_type,
|
||||
IEEE80211_PRIVACY(wdev->conn->params.privacy));
|
||||
if (!bss)
|
||||
return NULL;
|
||||
@@ -687,7 +687,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
|
||||
WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect);
|
||||
bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
|
||||
wdev->ssid, wdev->ssid_len,
|
||||
IEEE80211_BSS_TYPE_ESS,
|
||||
wdev->conn_bss_type,
|
||||
IEEE80211_PRIVACY_ANY);
|
||||
if (bss)
|
||||
cfg80211_hold_bss(bss_from_pub(bss));
|
||||
@@ -846,7 +846,7 @@ void cfg80211_roamed(struct net_device *dev,
|
||||
|
||||
bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid,
|
||||
wdev->ssid_len,
|
||||
IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY);
|
||||
wdev->conn_bss_type, IEEE80211_PRIVACY_ANY);
|
||||
if (WARN_ON(!bss))
|
||||
return;
|
||||
|
||||
@@ -1017,6 +1017,9 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
|
||||
memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
|
||||
wdev->ssid_len = connect->ssid_len;
|
||||
|
||||
wdev->conn_bss_type = connect->pbss ? IEEE80211_BSS_TYPE_PBSS :
|
||||
IEEE80211_BSS_TYPE_ESS;
|
||||
|
||||
if (!rdev->ops->connect)
|
||||
err = cfg80211_sme_connect(wdev, connect, prev_bssid);
|
||||
else
|
||||
|
@@ -393,9 +393,9 @@ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
|
||||
|
||||
unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
|
||||
static unsigned int __ieee80211_get_mesh_hdrlen(u8 flags)
|
||||
{
|
||||
int ae = meshhdr->flags & MESH_FLAGS_AE;
|
||||
int ae = flags & MESH_FLAGS_AE;
|
||||
/* 802.11-2012, 8.2.4.7.3 */
|
||||
switch (ae) {
|
||||
default:
|
||||
@@ -407,21 +407,31 @@ unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
|
||||
return 18;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
|
||||
{
|
||||
return __ieee80211_get_mesh_hdrlen(meshhdr->flags);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
|
||||
|
||||
int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
|
||||
enum nl80211_iftype iftype)
|
||||
static int __ieee80211_data_to_8023(struct sk_buff *skb, struct ethhdr *ehdr,
|
||||
const u8 *addr, enum nl80211_iftype iftype)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
u16 hdrlen, ethertype;
|
||||
u8 *payload;
|
||||
u8 dst[ETH_ALEN];
|
||||
u8 src[ETH_ALEN] __aligned(2);
|
||||
struct {
|
||||
u8 hdr[ETH_ALEN] __aligned(2);
|
||||
__be16 proto;
|
||||
} payload;
|
||||
struct ethhdr tmp;
|
||||
u16 hdrlen;
|
||||
u8 mesh_flags = 0;
|
||||
|
||||
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
|
||||
return -1;
|
||||
|
||||
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
if (skb->len < hdrlen + 8)
|
||||
return -1;
|
||||
|
||||
/* convert IEEE 802.11 header + possible LLC headers into Ethernet
|
||||
* header
|
||||
@@ -432,8 +442,11 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
|
||||
* 1 0 BSSID SA DA n/a
|
||||
* 1 1 RA TA DA SA
|
||||
*/
|
||||
memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN);
|
||||
memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN);
|
||||
memcpy(tmp.h_dest, ieee80211_get_DA(hdr), ETH_ALEN);
|
||||
memcpy(tmp.h_source, ieee80211_get_SA(hdr), ETH_ALEN);
|
||||
|
||||
if (iftype == NL80211_IFTYPE_MESH_POINT)
|
||||
skb_copy_bits(skb, hdrlen, &mesh_flags, 1);
|
||||
|
||||
switch (hdr->frame_control &
|
||||
cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
|
||||
@@ -450,44 +463,31 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
|
||||
iftype != NL80211_IFTYPE_STATION))
|
||||
return -1;
|
||||
if (iftype == NL80211_IFTYPE_MESH_POINT) {
|
||||
struct ieee80211s_hdr *meshdr =
|
||||
(struct ieee80211s_hdr *) (skb->data + hdrlen);
|
||||
/* make sure meshdr->flags is on the linear part */
|
||||
if (!pskb_may_pull(skb, hdrlen + 1))
|
||||
if (mesh_flags & MESH_FLAGS_AE_A4)
|
||||
return -1;
|
||||
if (meshdr->flags & MESH_FLAGS_AE_A4)
|
||||
return -1;
|
||||
if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
|
||||
if (mesh_flags & MESH_FLAGS_AE_A5_A6) {
|
||||
skb_copy_bits(skb, hdrlen +
|
||||
offsetof(struct ieee80211s_hdr, eaddr1),
|
||||
dst, ETH_ALEN);
|
||||
skb_copy_bits(skb, hdrlen +
|
||||
offsetof(struct ieee80211s_hdr, eaddr2),
|
||||
src, ETH_ALEN);
|
||||
tmp.h_dest, 2 * ETH_ALEN);
|
||||
}
|
||||
hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
|
||||
hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
|
||||
}
|
||||
break;
|
||||
case cpu_to_le16(IEEE80211_FCTL_FROMDS):
|
||||
if ((iftype != NL80211_IFTYPE_STATION &&
|
||||
iftype != NL80211_IFTYPE_P2P_CLIENT &&
|
||||
iftype != NL80211_IFTYPE_MESH_POINT) ||
|
||||
(is_multicast_ether_addr(dst) &&
|
||||
ether_addr_equal(src, addr)))
|
||||
(is_multicast_ether_addr(tmp.h_dest) &&
|
||||
ether_addr_equal(tmp.h_source, addr)))
|
||||
return -1;
|
||||
if (iftype == NL80211_IFTYPE_MESH_POINT) {
|
||||
struct ieee80211s_hdr *meshdr =
|
||||
(struct ieee80211s_hdr *) (skb->data + hdrlen);
|
||||
/* make sure meshdr->flags is on the linear part */
|
||||
if (!pskb_may_pull(skb, hdrlen + 1))
|
||||
if (mesh_flags & MESH_FLAGS_AE_A5_A6)
|
||||
return -1;
|
||||
if (meshdr->flags & MESH_FLAGS_AE_A5_A6)
|
||||
return -1;
|
||||
if (meshdr->flags & MESH_FLAGS_AE_A4)
|
||||
if (mesh_flags & MESH_FLAGS_AE_A4)
|
||||
skb_copy_bits(skb, hdrlen +
|
||||
offsetof(struct ieee80211s_hdr, eaddr1),
|
||||
src, ETH_ALEN);
|
||||
hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
|
||||
tmp.h_source, ETH_ALEN);
|
||||
hdrlen += __ieee80211_get_mesh_hdrlen(mesh_flags);
|
||||
}
|
||||
break;
|
||||
case cpu_to_le16(0):
|
||||
@@ -498,33 +498,33 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
|
||||
break;
|
||||
}
|
||||
|
||||
if (!pskb_may_pull(skb, hdrlen + 8))
|
||||
return -1;
|
||||
skb_copy_bits(skb, hdrlen, &payload, sizeof(payload));
|
||||
tmp.h_proto = payload.proto;
|
||||
|
||||
payload = skb->data + hdrlen;
|
||||
ethertype = (payload[6] << 8) | payload[7];
|
||||
|
||||
if (likely((ether_addr_equal(payload, rfc1042_header) &&
|
||||
ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
|
||||
ether_addr_equal(payload, bridge_tunnel_header))) {
|
||||
if (likely((ether_addr_equal(payload.hdr, rfc1042_header) &&
|
||||
tmp.h_proto != htons(ETH_P_AARP) &&
|
||||
tmp.h_proto != htons(ETH_P_IPX)) ||
|
||||
ether_addr_equal(payload.hdr, bridge_tunnel_header)))
|
||||
/* remove RFC1042 or Bridge-Tunnel encapsulation and
|
||||
* replace EtherType */
|
||||
skb_pull(skb, hdrlen + 6);
|
||||
memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
|
||||
memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
|
||||
} else {
|
||||
struct ethhdr *ehdr;
|
||||
__be16 len;
|
||||
hdrlen += ETH_ALEN + 2;
|
||||
else
|
||||
tmp.h_proto = htons(skb->len);
|
||||
|
||||
skb_pull(skb, hdrlen);
|
||||
len = htons(skb->len);
|
||||
pskb_pull(skb, hdrlen);
|
||||
|
||||
if (!ehdr)
|
||||
ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
|
||||
memcpy(ehdr->h_dest, dst, ETH_ALEN);
|
||||
memcpy(ehdr->h_source, src, ETH_ALEN);
|
||||
ehdr->h_proto = len;
|
||||
}
|
||||
memcpy(ehdr, &tmp, sizeof(tmp));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
|
||||
enum nl80211_iftype iftype)
|
||||
{
|
||||
return __ieee80211_data_to_8023(skb, NULL, addr, iftype);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_data_to_8023);
|
||||
|
||||
int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
|
||||
@@ -644,70 +644,147 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_data_from_8023);
|
||||
|
||||
static void
|
||||
__frame_add_frag(struct sk_buff *skb, struct page *page,
|
||||
void *ptr, int len, int size)
|
||||
{
|
||||
struct skb_shared_info *sh = skb_shinfo(skb);
|
||||
int page_offset;
|
||||
|
||||
atomic_inc(&page->_count);
|
||||
page_offset = ptr - page_address(page);
|
||||
skb_add_rx_frag(skb, sh->nr_frags, page, page_offset, len, size);
|
||||
}
|
||||
|
||||
static void
|
||||
__ieee80211_amsdu_copy_frag(struct sk_buff *skb, struct sk_buff *frame,
|
||||
int offset, int len)
|
||||
{
|
||||
struct skb_shared_info *sh = skb_shinfo(skb);
|
||||
const skb_frag_t *frag = &sh->frags[-1];
|
||||
struct page *frag_page;
|
||||
void *frag_ptr;
|
||||
int frag_len, frag_size;
|
||||
int head_size = skb->len - skb->data_len;
|
||||
int cur_len;
|
||||
|
||||
frag_page = virt_to_head_page(skb->head);
|
||||
frag_ptr = skb->data;
|
||||
frag_size = head_size;
|
||||
|
||||
while (offset >= frag_size) {
|
||||
offset -= frag_size;
|
||||
frag++;
|
||||
frag_page = skb_frag_page(frag);
|
||||
frag_ptr = skb_frag_address(frag);
|
||||
frag_size = skb_frag_size(frag);
|
||||
}
|
||||
|
||||
frag_ptr += offset;
|
||||
frag_len = frag_size - offset;
|
||||
|
||||
cur_len = min(len, frag_len);
|
||||
|
||||
__frame_add_frag(frame, frag_page, frag_ptr, cur_len, frag_size);
|
||||
len -= cur_len;
|
||||
|
||||
while (len > 0) {
|
||||
frag++;
|
||||
frag_len = skb_frag_size(frag);
|
||||
cur_len = min(len, frag_len);
|
||||
__frame_add_frag(frame, skb_frag_page(frag),
|
||||
skb_frag_address(frag), cur_len, frag_len);
|
||||
len -= cur_len;
|
||||
}
|
||||
}
|
||||
|
||||
static struct sk_buff *
|
||||
__ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
|
||||
int offset, int len, bool reuse_frag)
|
||||
{
|
||||
struct sk_buff *frame;
|
||||
int cur_len = len;
|
||||
|
||||
if (skb->len - offset < len)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* When reusing framents, copy some data to the head to simplify
|
||||
* ethernet header handling and speed up protocol header processing
|
||||
* in the stack later.
|
||||
*/
|
||||
if (reuse_frag)
|
||||
cur_len = min_t(int, len, 32);
|
||||
|
||||
/*
|
||||
* Allocate and reserve two bytes more for payload
|
||||
* alignment since sizeof(struct ethhdr) is 14.
|
||||
*/
|
||||
frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + cur_len);
|
||||
|
||||
skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
|
||||
skb_copy_bits(skb, offset, skb_put(frame, cur_len), cur_len);
|
||||
|
||||
len -= cur_len;
|
||||
if (!len)
|
||||
return frame;
|
||||
|
||||
offset += cur_len;
|
||||
__ieee80211_amsdu_copy_frag(skb, frame, offset, len);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
|
||||
const u8 *addr, enum nl80211_iftype iftype,
|
||||
const unsigned int extra_headroom,
|
||||
bool has_80211_header)
|
||||
{
|
||||
unsigned int hlen = ALIGN(extra_headroom, 4);
|
||||
struct sk_buff *frame = NULL;
|
||||
u16 ethertype;
|
||||
u8 *payload;
|
||||
const struct ethhdr *eth;
|
||||
int remaining, err;
|
||||
u8 dst[ETH_ALEN], src[ETH_ALEN];
|
||||
int offset = 0, remaining, err;
|
||||
struct ethhdr eth;
|
||||
bool reuse_frag = skb->head_frag && !skb_has_frag_list(skb);
|
||||
bool reuse_skb = false;
|
||||
bool last = false;
|
||||
|
||||
if (has_80211_header) {
|
||||
err = ieee80211_data_to_8023(skb, addr, iftype);
|
||||
err = __ieee80211_data_to_8023(skb, ð, addr, iftype);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* skip the wrapping header */
|
||||
eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr));
|
||||
if (!eth)
|
||||
goto out;
|
||||
} else {
|
||||
eth = (struct ethhdr *) skb->data;
|
||||
}
|
||||
|
||||
while (skb != frame) {
|
||||
while (!last) {
|
||||
unsigned int subframe_len;
|
||||
int len;
|
||||
u8 padding;
|
||||
__be16 len = eth->h_proto;
|
||||
unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len);
|
||||
|
||||
remaining = skb->len;
|
||||
memcpy(dst, eth->h_dest, ETH_ALEN);
|
||||
memcpy(src, eth->h_source, ETH_ALEN);
|
||||
|
||||
skb_copy_bits(skb, offset, ð, sizeof(eth));
|
||||
len = ntohs(eth.h_proto);
|
||||
subframe_len = sizeof(struct ethhdr) + len;
|
||||
padding = (4 - subframe_len) & 0x3;
|
||||
|
||||
/* the last MSDU has no padding */
|
||||
remaining = skb->len - offset;
|
||||
if (subframe_len > remaining)
|
||||
goto purge;
|
||||
|
||||
skb_pull(skb, sizeof(struct ethhdr));
|
||||
offset += sizeof(struct ethhdr);
|
||||
/* reuse skb for the last subframe */
|
||||
if (remaining <= subframe_len + padding)
|
||||
last = remaining <= subframe_len + padding;
|
||||
if (!skb_is_nonlinear(skb) && !reuse_frag && last) {
|
||||
skb_pull(skb, offset);
|
||||
frame = skb;
|
||||
else {
|
||||
unsigned int hlen = ALIGN(extra_headroom, 4);
|
||||
/*
|
||||
* Allocate and reserve two bytes more for payload
|
||||
* alignment since sizeof(struct ethhdr) is 14.
|
||||
*/
|
||||
frame = dev_alloc_skb(hlen + subframe_len + 2);
|
||||
reuse_skb = true;
|
||||
} else {
|
||||
frame = __ieee80211_amsdu_copy(skb, hlen, offset, len,
|
||||
reuse_frag);
|
||||
if (!frame)
|
||||
goto purge;
|
||||
|
||||
skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
|
||||
memcpy(skb_put(frame, ntohs(len)), skb->data,
|
||||
ntohs(len));
|
||||
|
||||
eth = (struct ethhdr *)skb_pull(skb, ntohs(len) +
|
||||
padding);
|
||||
if (!eth) {
|
||||
dev_kfree_skb(frame);
|
||||
goto purge;
|
||||
}
|
||||
offset += len + padding;
|
||||
}
|
||||
|
||||
skb_reset_network_header(frame);
|
||||
@@ -716,24 +793,20 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
|
||||
|
||||
payload = frame->data;
|
||||
ethertype = (payload[6] << 8) | payload[7];
|
||||
|
||||
if (likely((ether_addr_equal(payload, rfc1042_header) &&
|
||||
ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
|
||||
ether_addr_equal(payload, bridge_tunnel_header))) {
|
||||
/* remove RFC1042 or Bridge-Tunnel
|
||||
* encapsulation and replace EtherType */
|
||||
skb_pull(frame, 6);
|
||||
memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
|
||||
memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
|
||||
} else {
|
||||
memcpy(skb_push(frame, sizeof(__be16)), &len,
|
||||
sizeof(__be16));
|
||||
memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN);
|
||||
memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN);
|
||||
eth.h_proto = htons(ethertype);
|
||||
skb_pull(frame, ETH_ALEN + 2);
|
||||
}
|
||||
|
||||
memcpy(skb_push(frame, sizeof(eth)), ð, sizeof(eth));
|
||||
__skb_queue_tail(list, frame);
|
||||
}
|
||||
|
||||
if (!reuse_skb)
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return;
|
||||
|
||||
purge:
|
||||
|
Reference in New Issue
Block a user