mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 22:02:28 +00:00
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Conflicts: net/wireless/reg.c
This commit is contained in:
commit
353c78152c
@ -152,8 +152,8 @@
|
||||
!Finclude/net/cfg80211.h cfg80211_scan_request
|
||||
!Finclude/net/cfg80211.h cfg80211_scan_done
|
||||
!Finclude/net/cfg80211.h cfg80211_bss
|
||||
!Finclude/net/cfg80211.h cfg80211_inform_bss_frame
|
||||
!Finclude/net/cfg80211.h cfg80211_inform_bss
|
||||
!Finclude/net/cfg80211.h cfg80211_inform_bss_width_frame
|
||||
!Finclude/net/cfg80211.h cfg80211_inform_bss_width
|
||||
!Finclude/net/cfg80211.h cfg80211_unlink_bss
|
||||
!Finclude/net/cfg80211.h cfg80211_find_ie
|
||||
!Finclude/net/cfg80211.h ieee80211_bss_get_ie
|
||||
|
@ -167,6 +167,7 @@ struct hwsim_vif_priv {
|
||||
u32 magic;
|
||||
u8 bssid[ETH_ALEN];
|
||||
bool assoc;
|
||||
bool bcn_en;
|
||||
u16 aid;
|
||||
};
|
||||
|
||||
@ -1170,6 +1171,16 @@ static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw,
|
||||
*total_flags = data->rx_filter;
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_bcn_en_iter(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
unsigned int *count = data;
|
||||
struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
|
||||
|
||||
if (vp->bcn_en)
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *info,
|
||||
@ -1180,7 +1191,8 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
|
||||
|
||||
hwsim_check_magic(vif);
|
||||
|
||||
wiphy_debug(hw->wiphy, "%s(changed=0x%x)\n", __func__, changed);
|
||||
wiphy_debug(hw->wiphy, "%s(changed=0x%x vif->addr=%pM)\n",
|
||||
__func__, changed, vif->addr);
|
||||
|
||||
if (changed & BSS_CHANGED_BSSID) {
|
||||
wiphy_debug(hw->wiphy, "%s: BSSID changed: %pM\n",
|
||||
@ -1202,6 +1214,7 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
|
||||
|
||||
if (changed & BSS_CHANGED_BEACON_ENABLED) {
|
||||
wiphy_debug(hw->wiphy, " BCN EN: %d\n", info->enable_beacon);
|
||||
vp->bcn_en = info->enable_beacon;
|
||||
if (data->started &&
|
||||
!hrtimer_is_queued(&data->beacon_timer.timer) &&
|
||||
info->enable_beacon) {
|
||||
@ -1215,8 +1228,16 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw,
|
||||
tasklet_hrtimer_start(&data->beacon_timer,
|
||||
ns_to_ktime(until_tbtt * 1000),
|
||||
HRTIMER_MODE_REL);
|
||||
} else if (!info->enable_beacon)
|
||||
tasklet_hrtimer_cancel(&data->beacon_timer);
|
||||
} else if (!info->enable_beacon) {
|
||||
unsigned int count = 0;
|
||||
ieee80211_iterate_active_interfaces(
|
||||
data->hw, IEEE80211_IFACE_ITER_NORMAL,
|
||||
mac80211_hwsim_bcn_en_iter, &count);
|
||||
wiphy_debug(hw->wiphy, " beaconing vifs remaining: %u",
|
||||
count);
|
||||
if (count == 0)
|
||||
tasklet_hrtimer_cancel(&data->beacon_timer);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed & BSS_CHANGED_ERP_CTS_PROT) {
|
||||
|
@ -696,6 +696,18 @@ struct ieee80211_sec_chan_offs_ie {
|
||||
u8 sec_chan_offs;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ieee80211_mesh_chansw_params_ie - mesh channel switch parameters IE
|
||||
*
|
||||
* This structure represents the "Mesh Channel Switch Paramters element"
|
||||
*/
|
||||
struct ieee80211_mesh_chansw_params_ie {
|
||||
u8 mesh_ttl;
|
||||
u8 mesh_flags;
|
||||
__le16 mesh_reason;
|
||||
__le16 mesh_pre_value;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ieee80211_wide_bw_chansw_ie - wide bandwidth channel switch IE
|
||||
*/
|
||||
@ -750,6 +762,14 @@ enum mesh_config_capab_flags {
|
||||
IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL = 0x40,
|
||||
};
|
||||
|
||||
/**
|
||||
* mesh channel switch parameters element's flag indicator
|
||||
*
|
||||
*/
|
||||
#define WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT BIT(0)
|
||||
#define WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR BIT(1)
|
||||
#define WLAN_EID_CHAN_SWITCH_PARAM_REASON BIT(2)
|
||||
|
||||
/**
|
||||
* struct ieee80211_rann_ie
|
||||
*
|
||||
|
@ -744,6 +744,10 @@ enum station_parameters_apply_mask {
|
||||
* @capability: station capability
|
||||
* @ext_capab: extended capabilities of the station
|
||||
* @ext_capab_len: number of extended capabilities
|
||||
* @supported_channels: supported channels in IEEE 802.11 format
|
||||
* @supported_channels_len: number of supported channels
|
||||
* @supported_oper_classes: supported oper classes in IEEE 802.11 format
|
||||
* @supported_oper_classes_len: number of supported operating classes
|
||||
*/
|
||||
struct station_parameters {
|
||||
const u8 *supported_rates;
|
||||
@ -763,6 +767,10 @@ struct station_parameters {
|
||||
u16 capability;
|
||||
const u8 *ext_capab;
|
||||
u8 ext_capab_len;
|
||||
const u8 *supported_channels;
|
||||
u8 supported_channels_len;
|
||||
const u8 *supported_oper_classes;
|
||||
u8 supported_oper_classes_len;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1656,6 +1664,9 @@ struct cfg80211_disassoc_request {
|
||||
* sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is
|
||||
* required to assume that the port is unauthorized until authorized by
|
||||
* user space. Otherwise, port is marked authorized by default.
|
||||
* @userspace_handles_dfs: whether user space controls DFS operation, i.e.
|
||||
* changes the channel when a radar is detected. This is required
|
||||
* to operate on DFS channels.
|
||||
* @basic_rates: bitmap of basic rates to use when creating the IBSS
|
||||
* @mcast_rate: per-band multicast rate index + 1 (0: disabled)
|
||||
* @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
|
||||
@ -1673,6 +1684,7 @@ struct cfg80211_ibss_params {
|
||||
bool channel_fixed;
|
||||
bool privacy;
|
||||
bool control_port;
|
||||
bool userspace_handles_dfs;
|
||||
int mcast_rate[IEEE80211_NUM_BANDS];
|
||||
struct ieee80211_ht_cap ht_capa;
|
||||
struct ieee80211_ht_cap ht_capa_mask;
|
||||
@ -3053,6 +3065,7 @@ struct cfg80211_cached_keys;
|
||||
* @conn: (private) cfg80211 software SME connection state machine data
|
||||
* @connect_keys: (private) keys to set after connection is established
|
||||
* @ibss_fixed: (private) IBSS is using fixed BSSID
|
||||
* @ibss_dfs_possible: (private) IBSS may change to a DFS channel
|
||||
* @event_list: (private) list for internal event processing
|
||||
* @event_lock: (private) lock for event list
|
||||
*/
|
||||
@ -3091,6 +3104,7 @@ struct wireless_dev {
|
||||
struct ieee80211_channel *channel;
|
||||
|
||||
bool ibss_fixed;
|
||||
bool ibss_dfs_possible;
|
||||
|
||||
bool ps;
|
||||
int ps_timeout;
|
||||
|
@ -1503,6 +1503,10 @@ struct ieee80211_tx_control {
|
||||
* @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames
|
||||
* only, to allow getting TBTT of a DTIM beacon.
|
||||
*
|
||||
* @IEEE80211_HW_SUPPORTS_HT_CCK_RATES: Hardware supports mixing HT/CCK rates
|
||||
* and can cope with CCK rates in an aggregation session (e.g. by not
|
||||
* using aggregation for such frames.)
|
||||
*
|
||||
* @IEEE80211_HW_CHANCTX_STA_CSA: Support 802.11h based channel-switch (CSA)
|
||||
* for a single active channel while using channel contexts. When support
|
||||
* is not enabled the default action is to disconnect when getting the
|
||||
@ -4567,4 +4571,18 @@ void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
|
||||
struct cfg80211_wowlan_wakeup *wakeup,
|
||||
gfp_t gfp);
|
||||
|
||||
/**
|
||||
* ieee80211_tx_prepare_skb - prepare an 802.11 skb for transmission
|
||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
* @vif: virtual interface
|
||||
* @skb: frame to be sent from within the driver
|
||||
* @band: the band to transmit on
|
||||
* @sta: optional pointer to get the station to send the frame to
|
||||
*
|
||||
* Note: must be called under RCU lock
|
||||
*/
|
||||
bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif, struct sk_buff *skb,
|
||||
int band, struct ieee80211_sta **sta);
|
||||
|
||||
#endif /* MAC80211_H */
|
||||
|
@ -988,7 +988,7 @@ enum nl80211_commands {
|
||||
* to query the CRDA to retrieve one regulatory domain. This attribute can
|
||||
* also be used by userspace to query the kernel for the currently set
|
||||
* regulatory domain. We chose an alpha2 as that is also used by the
|
||||
* IEEE-802.11d country information element to identify a country.
|
||||
* IEEE-802.11 country information element to identify a country.
|
||||
* Users can also simply ask the wireless core to set regulatory domain
|
||||
* to a specific alpha2.
|
||||
* @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
|
||||
@ -1496,6 +1496,18 @@ enum nl80211_commands {
|
||||
* @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
|
||||
* As specified in the &enum nl80211_rxmgmt_flags.
|
||||
*
|
||||
* @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
|
||||
*
|
||||
* @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
|
||||
* supported operating classes.
|
||||
*
|
||||
* @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space
|
||||
* controls DFS operation in IBSS mode. If the flag is included in
|
||||
* %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS
|
||||
* channels and reports radar events to userspace. Userspace is required
|
||||
* to react to radar events, e.g. initiate a channel switch or leave the
|
||||
* IBSS network.
|
||||
*
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
*/
|
||||
@ -1806,6 +1818,12 @@ enum nl80211_attrs {
|
||||
|
||||
NL80211_ATTR_RXMGMT_FLAGS,
|
||||
|
||||
NL80211_ATTR_STA_SUPPORTED_CHANNELS,
|
||||
|
||||
NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
|
||||
|
||||
NL80211_ATTR_HANDLE_DFS,
|
||||
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
@ -3860,13 +3878,12 @@ enum nl80211_radar_event {
|
||||
*
|
||||
* Channel states used by the DFS code.
|
||||
*
|
||||
* @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
|
||||
* @NL80211_DFS_USABLE: The channel can be used, but channel availability
|
||||
* check (CAC) must be performed before using it for AP or IBSS.
|
||||
* @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
|
||||
* @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
|
||||
* is therefore marked as not available.
|
||||
* @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
|
||||
* @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
|
||||
*/
|
||||
|
||||
enum nl80211_dfs_state {
|
||||
NL80211_DFS_USABLE,
|
||||
NL80211_DFS_UNAVAILABLE,
|
||||
|
@ -4,6 +4,7 @@ config MAC80211
|
||||
select CRYPTO
|
||||
select CRYPTO_ARC4
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_CCM
|
||||
select CRC32
|
||||
select AVERAGE
|
||||
---help---
|
||||
@ -258,6 +259,17 @@ config MAC80211_MESH_SYNC_DEBUG
|
||||
|
||||
Do not select this option.
|
||||
|
||||
config MAC80211_MESH_CSA_DEBUG
|
||||
bool "Verbose mesh channel switch debugging"
|
||||
depends on MAC80211_DEBUG_MENU
|
||||
depends on MAC80211_MESH
|
||||
---help---
|
||||
Selecting this option causes mac80211 to print out very verbose mesh
|
||||
channel switch debugging messages (when mac80211 is taking part in a
|
||||
mesh network).
|
||||
|
||||
Do not select this option.
|
||||
|
||||
config MAC80211_MESH_PS_DEBUG
|
||||
bool "Verbose mesh powersave debugging"
|
||||
depends on MAC80211_DEBUG_MENU
|
||||
|
@ -2,6 +2,8 @@
|
||||
* Copyright 2003-2004, Instant802 Networks, Inc.
|
||||
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||
*
|
||||
* Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
@ -17,134 +19,75 @@
|
||||
#include "key.h"
|
||||
#include "aes_ccm.h"
|
||||
|
||||
static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a)
|
||||
void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
|
||||
u8 *data, size_t data_len, u8 *mic)
|
||||
{
|
||||
int i;
|
||||
u8 *b_0, *aad, *b, *s_0;
|
||||
struct scatterlist assoc, pt, ct[2];
|
||||
struct {
|
||||
struct aead_request req;
|
||||
u8 priv[crypto_aead_reqsize(tfm)];
|
||||
} aead_req;
|
||||
|
||||
b_0 = scratch + 3 * AES_BLOCK_SIZE;
|
||||
aad = scratch + 4 * AES_BLOCK_SIZE;
|
||||
b = scratch;
|
||||
s_0 = scratch + AES_BLOCK_SIZE;
|
||||
memset(&aead_req, 0, sizeof(aead_req));
|
||||
|
||||
crypto_cipher_encrypt_one(tfm, b, b_0);
|
||||
sg_init_one(&pt, data, data_len);
|
||||
sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
|
||||
sg_init_table(ct, 2);
|
||||
sg_set_buf(&ct[0], data, data_len);
|
||||
sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
|
||||
|
||||
/* Extra Authenticate-only data (always two AES blocks) */
|
||||
for (i = 0; i < AES_BLOCK_SIZE; i++)
|
||||
aad[i] ^= b[i];
|
||||
crypto_cipher_encrypt_one(tfm, b, aad);
|
||||
aead_request_set_tfm(&aead_req.req, tfm);
|
||||
aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
|
||||
aead_request_set_crypt(&aead_req.req, &pt, ct, data_len, b_0);
|
||||
|
||||
aad += AES_BLOCK_SIZE;
|
||||
|
||||
for (i = 0; i < AES_BLOCK_SIZE; i++)
|
||||
aad[i] ^= b[i];
|
||||
crypto_cipher_encrypt_one(tfm, a, aad);
|
||||
|
||||
/* Mask out bits from auth-only-b_0 */
|
||||
b_0[0] &= 0x07;
|
||||
|
||||
/* S_0 is used to encrypt T (= MIC) */
|
||||
b_0[14] = 0;
|
||||
b_0[15] = 0;
|
||||
crypto_cipher_encrypt_one(tfm, s_0, b_0);
|
||||
crypto_aead_encrypt(&aead_req.req);
|
||||
}
|
||||
|
||||
|
||||
void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
|
||||
u8 *data, size_t data_len,
|
||||
u8 *cdata, u8 *mic)
|
||||
int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
|
||||
u8 *data, size_t data_len, u8 *mic)
|
||||
{
|
||||
int i, j, last_len, num_blocks;
|
||||
u8 *pos, *cpos, *b, *s_0, *e, *b_0;
|
||||
struct scatterlist assoc, pt, ct[2];
|
||||
struct {
|
||||
struct aead_request req;
|
||||
u8 priv[crypto_aead_reqsize(tfm)];
|
||||
} aead_req;
|
||||
|
||||
b = scratch;
|
||||
s_0 = scratch + AES_BLOCK_SIZE;
|
||||
e = scratch + 2 * AES_BLOCK_SIZE;
|
||||
b_0 = scratch + 3 * AES_BLOCK_SIZE;
|
||||
memset(&aead_req, 0, sizeof(aead_req));
|
||||
|
||||
num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
|
||||
last_len = data_len % AES_BLOCK_SIZE;
|
||||
aes_ccm_prepare(tfm, scratch, b);
|
||||
sg_init_one(&pt, data, data_len);
|
||||
sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
|
||||
sg_init_table(ct, 2);
|
||||
sg_set_buf(&ct[0], data, data_len);
|
||||
sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
|
||||
|
||||
/* Process payload blocks */
|
||||
pos = data;
|
||||
cpos = cdata;
|
||||
for (j = 1; j <= num_blocks; j++) {
|
||||
int blen = (j == num_blocks && last_len) ?
|
||||
last_len : AES_BLOCK_SIZE;
|
||||
aead_request_set_tfm(&aead_req.req, tfm);
|
||||
aead_request_set_assoc(&aead_req.req, &assoc, assoc.length);
|
||||
aead_request_set_crypt(&aead_req.req, ct, &pt,
|
||||
data_len + IEEE80211_CCMP_MIC_LEN, b_0);
|
||||
|
||||
/* Authentication followed by encryption */
|
||||
for (i = 0; i < blen; i++)
|
||||
b[i] ^= pos[i];
|
||||
crypto_cipher_encrypt_one(tfm, b, b);
|
||||
|
||||
b_0[14] = (j >> 8) & 0xff;
|
||||
b_0[15] = j & 0xff;
|
||||
crypto_cipher_encrypt_one(tfm, e, b_0);
|
||||
for (i = 0; i < blen; i++)
|
||||
*cpos++ = *pos++ ^ e[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++)
|
||||
mic[i] = b[i] ^ s_0[i];
|
||||
return crypto_aead_decrypt(&aead_req.req);
|
||||
}
|
||||
|
||||
|
||||
int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
|
||||
u8 *cdata, size_t data_len, u8 *mic, u8 *data)
|
||||
struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])
|
||||
{
|
||||
int i, j, last_len, num_blocks;
|
||||
u8 *pos, *cpos, *b, *s_0, *a, *b_0;
|
||||
struct crypto_aead *tfm;
|
||||
int err;
|
||||
|
||||
b = scratch;
|
||||
s_0 = scratch + AES_BLOCK_SIZE;
|
||||
a = scratch + 2 * AES_BLOCK_SIZE;
|
||||
b_0 = scratch + 3 * AES_BLOCK_SIZE;
|
||||
tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(tfm))
|
||||
return tfm;
|
||||
|
||||
num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
|
||||
last_len = data_len % AES_BLOCK_SIZE;
|
||||
aes_ccm_prepare(tfm, scratch, a);
|
||||
err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
|
||||
if (!err)
|
||||
err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN);
|
||||
if (!err)
|
||||
return tfm;
|
||||
|
||||
/* Process payload blocks */
|
||||
cpos = cdata;
|
||||
pos = data;
|
||||
for (j = 1; j <= num_blocks; j++) {
|
||||
int blen = (j == num_blocks && last_len) ?
|
||||
last_len : AES_BLOCK_SIZE;
|
||||
|
||||
/* Decryption followed by authentication */
|
||||
b_0[14] = (j >> 8) & 0xff;
|
||||
b_0[15] = j & 0xff;
|
||||
crypto_cipher_encrypt_one(tfm, b, b_0);
|
||||
for (i = 0; i < blen; i++) {
|
||||
*pos = *cpos++ ^ b[i];
|
||||
a[i] ^= *pos++;
|
||||
}
|
||||
crypto_cipher_encrypt_one(tfm, a, a);
|
||||
}
|
||||
|
||||
for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) {
|
||||
if ((mic[i] ^ s_0[i]) != a[i])
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
crypto_free_aead(tfm);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
|
||||
struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[])
|
||||
void ieee80211_aes_key_free(struct crypto_aead *tfm)
|
||||
{
|
||||
struct crypto_cipher *tfm;
|
||||
|
||||
tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
|
||||
if (!IS_ERR(tfm))
|
||||
crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
|
||||
|
||||
return tfm;
|
||||
}
|
||||
|
||||
|
||||
void ieee80211_aes_key_free(struct crypto_cipher *tfm)
|
||||
{
|
||||
crypto_free_cipher(tfm);
|
||||
crypto_free_aead(tfm);
|
||||
}
|
||||
|
@ -12,13 +12,11 @@
|
||||
|
||||
#include <linux/crypto.h>
|
||||
|
||||
struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]);
|
||||
void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch,
|
||||
u8 *data, size_t data_len,
|
||||
u8 *cdata, u8 *mic);
|
||||
int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch,
|
||||
u8 *cdata, size_t data_len,
|
||||
u8 *mic, u8 *data);
|
||||
void ieee80211_aes_key_free(struct crypto_cipher *tfm);
|
||||
struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]);
|
||||
void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
|
||||
u8 *data, size_t data_len, u8 *mic);
|
||||
int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
|
||||
u8 *data, size_t data_len, u8 *mic);
|
||||
void ieee80211_aes_key_free(struct crypto_aead *tfm);
|
||||
|
||||
#endif /* AES_CCM_H */
|
||||
|
@ -1059,6 +1059,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
|
||||
/* abort any running channel switch */
|
||||
sdata->vif.csa_active = false;
|
||||
cancel_work_sync(&sdata->csa_finalize_work);
|
||||
cancel_work_sync(&sdata->u.ap.request_smps_work);
|
||||
|
||||
/* turn off carrier for this interface and dependent VLANs */
|
||||
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
|
||||
@ -1342,8 +1343,8 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
||||
sta->plink_state = params->plink_state;
|
||||
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
changed |=
|
||||
ieee80211_mps_local_status_update(sdata);
|
||||
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
||||
NL80211_MESH_POWER_UNKNOWN);
|
||||
break;
|
||||
default:
|
||||
/* nothing */
|
||||
@ -1553,6 +1554,20 @@ static int ieee80211_change_station(struct wiphy *wiphy,
|
||||
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
|
||||
if ((sdata->vif.type == NL80211_IFTYPE_AP ||
|
||||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
|
||||
sta->known_smps_mode != sta->sdata->bss->req_smps &&
|
||||
test_sta_flag(sta, WLAN_STA_AUTHORIZED) &&
|
||||
sta_info_tx_streams(sta) != 1) {
|
||||
ht_dbg(sta->sdata,
|
||||
"%pM just authorized and MIMO capable - update SMPS\n",
|
||||
sta->sta.addr);
|
||||
ieee80211_send_smps_action(sta->sdata,
|
||||
sta->sdata->bss->req_smps,
|
||||
sta->sta.addr,
|
||||
sta->sdata->vif.bss_conf.bssid);
|
||||
}
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
|
||||
params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
|
||||
ieee80211_recalc_ps(local, -1);
|
||||
@ -2337,8 +2352,92 @@ static int ieee80211_testmode_dump(struct wiphy *wiphy,
|
||||
}
|
||||
#endif
|
||||
|
||||
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
|
||||
enum ieee80211_smps_mode smps_mode)
|
||||
int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
|
||||
enum ieee80211_smps_mode smps_mode)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
enum ieee80211_smps_mode old_req;
|
||||
int i;
|
||||
|
||||
if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP))
|
||||
return -EINVAL;
|
||||
|
||||
if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
|
||||
return 0;
|
||||
|
||||
old_req = sdata->u.ap.req_smps;
|
||||
sdata->u.ap.req_smps = smps_mode;
|
||||
|
||||
/* AUTOMATIC doesn't mean much for AP - don't allow it */
|
||||
if (old_req == smps_mode ||
|
||||
smps_mode == IEEE80211_SMPS_AUTOMATIC)
|
||||
return 0;
|
||||
|
||||
/* If no associated stations, there's no need to do anything */
|
||||
if (!atomic_read(&sdata->u.ap.num_mcast_sta)) {
|
||||
sdata->smps_mode = smps_mode;
|
||||
ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ht_dbg(sdata,
|
||||
"SMSP %d requested in AP mode, sending Action frame to %d stations\n",
|
||||
smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta));
|
||||
|
||||
mutex_lock(&sdata->local->sta_mtx);
|
||||
for (i = 0; i < STA_HASH_SIZE; i++) {
|
||||
for (sta = rcu_dereference_protected(sdata->local->sta_hash[i],
|
||||
lockdep_is_held(&sdata->local->sta_mtx));
|
||||
sta;
|
||||
sta = rcu_dereference_protected(sta->hnext,
|
||||
lockdep_is_held(&sdata->local->sta_mtx))) {
|
||||
/*
|
||||
* Only stations associated to our AP and
|
||||
* associated VLANs
|
||||
*/
|
||||
if (sta->sdata->bss != &sdata->u.ap)
|
||||
continue;
|
||||
|
||||
/* This station doesn't support MIMO - skip it */
|
||||
if (sta_info_tx_streams(sta) == 1)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Don't wake up a STA just to send the action frame
|
||||
* unless we are getting more restrictive.
|
||||
*/
|
||||
if (test_sta_flag(sta, WLAN_STA_PS_STA) &&
|
||||
!ieee80211_smps_is_restrictive(sta->known_smps_mode,
|
||||
smps_mode)) {
|
||||
ht_dbg(sdata,
|
||||
"Won't send SMPS to sleeping STA %pM\n",
|
||||
sta->sta.addr);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the STA is not authorized, wait until it gets
|
||||
* authorized and the action frame will be sent then.
|
||||
*/
|
||||
if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED))
|
||||
continue;
|
||||
|
||||
ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr);
|
||||
ieee80211_send_smps_action(sdata, smps_mode,
|
||||
sta->sta.addr,
|
||||
sdata->vif.bss_conf.bssid);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&sdata->local->sta_mtx);
|
||||
|
||||
sdata->smps_mode = smps_mode;
|
||||
ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
|
||||
enum ieee80211_smps_mode smps_mode)
|
||||
{
|
||||
const u8 *ap;
|
||||
enum ieee80211_smps_mode old_req;
|
||||
@ -2346,6 +2445,9 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
lockdep_assert_held(&sdata->wdev.mtx);
|
||||
|
||||
if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
|
||||
return -EINVAL;
|
||||
|
||||
old_req = sdata->u.mgd.req_smps;
|
||||
sdata->u.mgd.req_smps = smps_mode;
|
||||
|
||||
@ -2402,7 +2504,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
||||
|
||||
/* no change, but if automatic follow powersave */
|
||||
sdata_lock(sdata);
|
||||
__ieee80211_request_smps(sdata, sdata->u.mgd.req_smps);
|
||||
__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps);
|
||||
sdata_unlock(sdata);
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
|
||||
@ -2860,7 +2962,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
|
||||
container_of(work, struct ieee80211_sub_if_data,
|
||||
csa_finalize_work);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int err, changed;
|
||||
int err, changed = 0;
|
||||
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
return;
|
||||
@ -2892,6 +2994,13 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
ieee80211_ibss_finish_csa(sdata);
|
||||
break;
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
err = ieee80211_mesh_finish_csa(sdata);
|
||||
if (err < 0)
|
||||
return;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return;
|
||||
@ -2912,6 +3021,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
struct ieee80211_if_mesh __maybe_unused *ifmsh;
|
||||
int err, num_chanctx;
|
||||
|
||||
if (!list_empty(&local->roc_list) || local->scanning)
|
||||
@ -2995,6 +3105,26 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
ifmsh = &sdata->u.mesh;
|
||||
|
||||
if (!ifmsh->mesh_id)
|
||||
return -EINVAL;
|
||||
|
||||
if (params->chandef.width != sdata->vif.bss_conf.chandef.width)
|
||||
return -EINVAL;
|
||||
|
||||
/* changes into another band are not supported */
|
||||
if (sdata->vif.bss_conf.chandef.chan->band !=
|
||||
params->chandef.chan->band)
|
||||
return -EINVAL;
|
||||
|
||||
err = ieee80211_mesh_csa_beacon(sdata, params, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -44,6 +44,12 @@
|
||||
#define MAC80211_MESH_SYNC_DEBUG 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH_CSA_DEBUG
|
||||
#define MAC80211_MESH_CSA_DEBUG 1
|
||||
#else
|
||||
#define MAC80211_MESH_CSA_DEBUG 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH_PS_DEBUG
|
||||
#define MAC80211_MESH_PS_DEBUG 1
|
||||
#else
|
||||
@ -157,6 +163,10 @@ do { \
|
||||
_sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \
|
||||
sdata, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define mcsa_dbg(sdata, fmt, ...) \
|
||||
_sdata_dbg(MAC80211_MESH_CSA_DEBUG, \
|
||||
sdata, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define mps_dbg(sdata, fmt, ...) \
|
||||
_sdata_dbg(MAC80211_MESH_PS_DEBUG, \
|
||||
sdata, fmt, ##__VA_ARGS__)
|
||||
|
@ -224,12 +224,15 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
|
||||
smps_mode == IEEE80211_SMPS_AUTOMATIC))
|
||||
return -EINVAL;
|
||||
|
||||
/* supported only on managed interfaces for now */
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
|
||||
sdata->vif.type != NL80211_IFTYPE_AP)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
sdata_lock(sdata);
|
||||
err = __ieee80211_request_smps(sdata, smps_mode);
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||
err = __ieee80211_request_smps_mgd(sdata, smps_mode);
|
||||
else
|
||||
err = __ieee80211_request_smps_ap(sdata, smps_mode);
|
||||
sdata_unlock(sdata);
|
||||
|
||||
return err;
|
||||
@ -245,12 +248,15 @@ static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = {
|
||||
static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,
|
||||
char *buf, int buflen)
|
||||
{
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return snprintf(buf, buflen, "request: %s\nused: %s\n",
|
||||
smps_modes[sdata->u.mgd.req_smps],
|
||||
smps_modes[sdata->smps_mode]);
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||
return snprintf(buf, buflen, "request: %s\nused: %s\n",
|
||||
smps_modes[sdata->u.mgd.req_smps],
|
||||
smps_modes[sdata->smps_mode]);
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
return snprintf(buf, buflen, "request: %s\nused: %s\n",
|
||||
smps_modes[sdata->u.ap.req_smps],
|
||||
smps_modes[sdata->smps_mode]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,
|
||||
@ -563,6 +569,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
|
||||
static void add_ap_files(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
DEBUGFS_ADD(num_mcast_sta);
|
||||
DEBUGFS_ADD_MODE(smps, 0600);
|
||||
DEBUGFS_ADD(num_sta_ps);
|
||||
DEBUGFS_ADD(dtim_count);
|
||||
DEBUGFS_ADD(num_buffered_multicast);
|
||||
|
@ -448,14 +448,25 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ieee80211_request_smps_work(struct work_struct *work)
|
||||
void ieee80211_request_smps_mgd_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata =
|
||||
container_of(work, struct ieee80211_sub_if_data,
|
||||
u.mgd.request_smps_work);
|
||||
|
||||
sdata_lock(sdata);
|
||||
__ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode);
|
||||
__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode);
|
||||
sdata_unlock(sdata);
|
||||
}
|
||||
|
||||
void ieee80211_request_smps_ap_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata =
|
||||
container_of(work, struct ieee80211_sub_if_data,
|
||||
u.ap.request_smps_work);
|
||||
|
||||
sdata_lock(sdata);
|
||||
__ieee80211_request_smps_ap(sdata, sdata->u.ap.driver_smps_mode);
|
||||
sdata_unlock(sdata);
|
||||
}
|
||||
|
||||
@ -464,19 +475,29 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
|
||||
if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
|
||||
if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION &&
|
||||
vif->type != NL80211_IFTYPE_AP))
|
||||
return;
|
||||
|
||||
if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
|
||||
smps_mode = IEEE80211_SMPS_AUTOMATIC;
|
||||
|
||||
if (sdata->u.mgd.driver_smps_mode == smps_mode)
|
||||
return;
|
||||
|
||||
sdata->u.mgd.driver_smps_mode = smps_mode;
|
||||
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&sdata->u.mgd.request_smps_work);
|
||||
if (vif->type == NL80211_IFTYPE_STATION) {
|
||||
if (sdata->u.mgd.driver_smps_mode == smps_mode)
|
||||
return;
|
||||
sdata->u.mgd.driver_smps_mode = smps_mode;
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&sdata->u.mgd.request_smps_work);
|
||||
} else {
|
||||
/* AUTOMATIC is meaningless in AP mode */
|
||||
if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC))
|
||||
return;
|
||||
if (sdata->u.ap.driver_smps_mode == smps_mode)
|
||||
return;
|
||||
sdata->u.ap.driver_smps_mode = smps_mode;
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&sdata->u.ap.request_smps_work);
|
||||
}
|
||||
}
|
||||
/* this might change ... don't want non-open drivers using it */
|
||||
EXPORT_SYMBOL_GPL(ieee80211_request_smps);
|
||||
|
@ -229,6 +229,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
struct beacon_data *presp;
|
||||
enum nl80211_bss_scan_width scan_width;
|
||||
bool have_higher_than_11mbit;
|
||||
bool radar_required = false;
|
||||
int err;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
@ -273,6 +274,23 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
chandef.width = NL80211_CHAN_WIDTH_20;
|
||||
chandef.center_freq1 = chan->center_freq;
|
||||
/* check again for downgraded chandef */
|
||||
if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
|
||||
sdata_info(sdata,
|
||||
"Failed to join IBSS, beacons forbidden\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
|
||||
&chandef);
|
||||
if (err > 0) {
|
||||
if (!ifibss->userspace_handles_dfs) {
|
||||
sdata_info(sdata,
|
||||
"Failed to join IBSS, DFS channel without control program\n");
|
||||
return;
|
||||
}
|
||||
radar_required = true;
|
||||
}
|
||||
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
@ -297,6 +315,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
rcu_assign_pointer(ifibss->presp, presp);
|
||||
mgmt = (void *)presp->head;
|
||||
|
||||
sdata->radar_required = radar_required;
|
||||
sdata->vif.bss_conf.enable_beacon = true;
|
||||
sdata->vif.bss_conf.beacon_int = beacon_int;
|
||||
sdata->vif.bss_conf.basic_rates = basic_rates;
|
||||
@ -445,60 +464,6 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
tsf, false);
|
||||
}
|
||||
|
||||
static int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_csa_settings *csa_settings)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int freq;
|
||||
int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
|
||||
sizeof(mgmt->u.action.u.chan_switch);
|
||||
u8 *pos;
|
||||
|
||||
skb = dev_alloc_skb(local->tx_headroom + hdr_len +
|
||||
5 + /* channel switch announcement element */
|
||||
3); /* secondary channel offset element */
|
||||
if (!skb)
|
||||
return -1;
|
||||
|
||||
skb_reserve(skb, local->tx_headroom);
|
||||
mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
|
||||
memset(mgmt, 0, hdr_len);
|
||||
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
||||
IEEE80211_STYPE_ACTION);
|
||||
|
||||
eth_broadcast_addr(mgmt->da);
|
||||
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
||||
memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
|
||||
mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
|
||||
mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
|
||||
pos = skb_put(skb, 5);
|
||||
*pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */
|
||||
*pos++ = 3; /* IE length */
|
||||
*pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */
|
||||
freq = csa_settings->chandef.chan->center_freq;
|
||||
*pos++ = ieee80211_frequency_to_channel(freq); /* channel */
|
||||
*pos++ = csa_settings->count; /* count */
|
||||
|
||||
if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
|
||||
enum nl80211_channel_type ch_type;
|
||||
|
||||
skb_put(skb, 3);
|
||||
*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */
|
||||
*pos++ = 1; /* IE length */
|
||||
ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
|
||||
if (ch_type == NL80211_CHAN_HT40PLUS)
|
||||
*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
|
||||
else
|
||||
*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
|
||||
}
|
||||
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_csa_settings *csa_settings)
|
||||
{
|
||||
@ -796,19 +761,34 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work)
|
||||
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
|
||||
}
|
||||
|
||||
static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
int err;
|
||||
|
||||
/* if the current channel is a DFS channel, mark the channel as
|
||||
* unavailable.
|
||||
*/
|
||||
err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
|
||||
&ifibss->chandef);
|
||||
if (err > 0)
|
||||
cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef,
|
||||
GFP_ATOMIC);
|
||||
}
|
||||
|
||||
static bool
|
||||
ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems,
|
||||
bool beacon)
|
||||
{
|
||||
struct cfg80211_csa_settings params;
|
||||
struct ieee80211_csa_ie csa_ie;
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
enum nl80211_channel_type ch_type;
|
||||
int err, num_chanctx;
|
||||
u32 sta_flags;
|
||||
u8 mode;
|
||||
|
||||
if (sdata->vif.csa_active)
|
||||
return true;
|
||||
@ -831,12 +811,10 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
memset(&csa_ie, 0, sizeof(csa_ie));
|
||||
err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
|
||||
ifibss->chandef.chan->band,
|
||||
sta_flags, ifibss->bssid,
|
||||
¶ms.count, &mode,
|
||||
¶ms.chandef);
|
||||
|
||||
sta_flags, ifibss->bssid, &csa_ie);
|
||||
/* can't switch to destination channel, fail */
|
||||
if (err < 0)
|
||||
goto disconnect;
|
||||
@ -845,6 +823,9 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
if (err)
|
||||
return false;
|
||||
|
||||
params.count = csa_ie.count;
|
||||
params.chandef = csa_ie.chandef;
|
||||
|
||||
if (ifibss->chandef.chan->band != params.chandef.chan->band)
|
||||
goto disconnect;
|
||||
|
||||
@ -880,8 +861,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef,
|
||||
IEEE80211_CHAN_DISABLED)) {
|
||||
if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, ¶ms.chandef)) {
|
||||
sdata_info(sdata,
|
||||
"IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
|
||||
ifibss->bssid,
|
||||
@ -897,10 +877,11 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
if (err < 0)
|
||||
goto disconnect;
|
||||
if (err) {
|
||||
params.radar_required = true;
|
||||
/* IBSS-DFS only allowed with a control program */
|
||||
if (!ifibss->userspace_handles_dfs)
|
||||
goto disconnect;
|
||||
|
||||
/* TODO: IBSS-DFS not (yet) supported, disconnect. */
|
||||
goto disconnect;
|
||||
params.radar_required = true;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
@ -931,7 +912,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
"received channel switch announcement to go to channel %d MHz\n",
|
||||
params.chandef.chan->center_freq);
|
||||
|
||||
params.block_tx = !!mode;
|
||||
params.block_tx = !!csa_ie.mode;
|
||||
|
||||
ieee80211_ibss_csa_beacon(sdata, ¶ms);
|
||||
sdata->csa_radar_required = params.radar_required;
|
||||
@ -947,12 +928,16 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
ieee80211_bss_info_change_notify(sdata, err);
|
||||
drv_channel_switch_beacon(sdata, ¶ms.chandef);
|
||||
|
||||
ieee80211_ibss_csa_mark_radar(sdata);
|
||||
|
||||
return true;
|
||||
disconnect:
|
||||
ibss_dbg(sdata, "Can't handle channel switch, disconnect\n");
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&ifibss->csa_connection_drop_work);
|
||||
|
||||
ieee80211_ibss_csa_mark_radar(sdata);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1688,6 +1673,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
sdata->u.ibss.privacy = params->privacy;
|
||||
sdata->u.ibss.control_port = params->control_port;
|
||||
sdata->u.ibss.userspace_handles_dfs = params->userspace_handles_dfs;
|
||||
sdata->u.ibss.basic_rates = params->basic_rates;
|
||||
|
||||
/* fix basic_rates if channel does not support these rates */
|
||||
|
@ -262,6 +262,10 @@ struct ieee80211_if_ap {
|
||||
|
||||
struct ps_data ps;
|
||||
atomic_t num_mcast_sta; /* number of stations receiving multicast */
|
||||
enum ieee80211_smps_mode req_smps, /* requested smps mode */
|
||||
driver_smps_mode; /* smps mode request */
|
||||
|
||||
struct work_struct request_smps_work;
|
||||
};
|
||||
|
||||
struct ieee80211_if_wds {
|
||||
@ -498,6 +502,7 @@ struct ieee80211_if_ibss {
|
||||
bool privacy;
|
||||
|
||||
bool control_port;
|
||||
bool userspace_handles_dfs;
|
||||
|
||||
u8 bssid[ETH_ALEN] __aligned(2);
|
||||
u8 ssid[IEEE80211_MAX_SSID_LEN];
|
||||
@ -539,6 +544,11 @@ struct ieee80211_mesh_sync_ops {
|
||||
/* add other framework functions here */
|
||||
};
|
||||
|
||||
struct mesh_csa_settings {
|
||||
struct rcu_head rcu_head;
|
||||
struct cfg80211_csa_settings settings;
|
||||
};
|
||||
|
||||
struct ieee80211_if_mesh {
|
||||
struct timer_list housekeeping_timer;
|
||||
struct timer_list mesh_path_timer;
|
||||
@ -599,6 +609,11 @@ struct ieee80211_if_mesh {
|
||||
int ps_peers_light_sleep;
|
||||
int ps_peers_deep_sleep;
|
||||
struct ps_data ps;
|
||||
/* Channel Switching Support */
|
||||
struct mesh_csa_settings __rcu *csa;
|
||||
bool chsw_init;
|
||||
u8 chsw_ttl;
|
||||
u16 pre_value;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
@ -1207,6 +1222,14 @@ struct ieee80211_ra_tid {
|
||||
u16 tid;
|
||||
};
|
||||
|
||||
/* this struct holds the value parsing from channel switch IE */
|
||||
struct ieee80211_csa_ie {
|
||||
struct cfg80211_chan_def chandef;
|
||||
u8 mode;
|
||||
u8 count;
|
||||
u8 ttl;
|
||||
};
|
||||
|
||||
/* Parsed Information Elements */
|
||||
struct ieee802_11_elems {
|
||||
const u8 *ie_start;
|
||||
@ -1243,6 +1266,7 @@ struct ieee802_11_elems {
|
||||
const struct ieee80211_timeout_interval_ie *timeout_int;
|
||||
const u8 *opmode_notif;
|
||||
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
|
||||
const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;
|
||||
|
||||
/* length of them, respectively */
|
||||
u8 ssid_len;
|
||||
@ -1343,6 +1367,10 @@ void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb);
|
||||
int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_csa_settings *csa_settings,
|
||||
bool csa_action);
|
||||
int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
/* scan/BSS handling */
|
||||
void ieee80211_scan_work(struct work_struct *work);
|
||||
@ -1439,7 +1467,10 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
|
||||
int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
|
||||
enum ieee80211_smps_mode smps, const u8 *da,
|
||||
const u8 *bssid);
|
||||
void ieee80211_request_smps_work(struct work_struct *work);
|
||||
void ieee80211_request_smps_ap_work(struct work_struct *work);
|
||||
void ieee80211_request_smps_mgd_work(struct work_struct *work);
|
||||
bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
|
||||
enum ieee80211_smps_mode smps_mode_new);
|
||||
|
||||
void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
|
||||
u16 initiator, u16 reason, bool stop);
|
||||
@ -1501,17 +1532,16 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
||||
* %IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT,
|
||||
* %IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ,
|
||||
* %IEEE80211_STA_DISABLE_160MHZ.
|
||||
* @count: to be filled with the counter until the switch (on success only)
|
||||
* @bssid: the currently connected bssid (for reporting)
|
||||
* @mode: to be filled with CSA mode (on success only)
|
||||
* @new_chandef: to be filled with destination chandef (on success only)
|
||||
* @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl.
|
||||
All of them will be filled with if success only.
|
||||
* Return: 0 on success, <0 on error and >0 if there is nothing to parse.
|
||||
*/
|
||||
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems, bool beacon,
|
||||
enum ieee80211_band current_band,
|
||||
u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
|
||||
struct cfg80211_chan_def *new_chandef);
|
||||
u32 sta_flags, u8 *bssid,
|
||||
struct ieee80211_csa_ie *csa_ie);
|
||||
|
||||
/* Suspend/resume and hw reconfiguration */
|
||||
int ieee80211_reconfig(struct ieee80211_local *local);
|
||||
@ -1657,8 +1687,10 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
|
||||
u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems,
|
||||
enum ieee80211_band band, u32 *basic_rates);
|
||||
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
|
||||
enum ieee80211_smps_mode smps_mode);
|
||||
int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
|
||||
enum ieee80211_smps_mode smps_mode);
|
||||
int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
|
||||
enum ieee80211_smps_mode smps_mode);
|
||||
void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
|
||||
@ -1714,6 +1746,8 @@ void ieee80211_dfs_cac_timer(unsigned long data);
|
||||
void ieee80211_dfs_cac_timer_work(struct work_struct *work);
|
||||
void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
|
||||
void ieee80211_dfs_radar_detected_work(struct work_struct *work);
|
||||
int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_csa_settings *csa_settings);
|
||||
|
||||
#ifdef CONFIG_MAC80211_NOINLINE
|
||||
#define debug_noinline noinline
|
||||
|
@ -1293,7 +1293,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
|
||||
case NL80211_IFTYPE_AP:
|
||||
skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
|
||||
INIT_LIST_HEAD(&sdata->u.ap.vlans);
|
||||
INIT_WORK(&sdata->u.ap.request_smps_work,
|
||||
ieee80211_request_smps_ap_work);
|
||||
sdata->vif.bss_conf.bssid = sdata->vif.addr;
|
||||
sdata->u.ap.req_smps = IEEE80211_SMPS_OFF;
|
||||
break;
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
type = NL80211_IFTYPE_STATION;
|
||||
|
@ -83,7 +83,7 @@ struct ieee80211_key {
|
||||
* Management frames.
|
||||
*/
|
||||
u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN];
|
||||
struct crypto_cipher *tfm;
|
||||
struct crypto_aead *tfm;
|
||||
u32 replays; /* dot11RSNAStatsCCMPReplays */
|
||||
} ccmp;
|
||||
struct {
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <asm/unaligned.h>
|
||||
#include "ieee80211_i.h"
|
||||
#include "mesh.h"
|
||||
#include "driver-ops.h"
|
||||
|
||||
static int mesh_allocated;
|
||||
static struct kmem_cache *rm_cache;
|
||||
@ -610,6 +611,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct mesh_csa_settings *csa;
|
||||
enum ieee80211_band band;
|
||||
u8 *pos;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
@ -624,6 +626,10 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
|
||||
|
||||
head_len = hdr_len +
|
||||
2 + /* NULL SSID */
|
||||
/* Channel Switch Announcement */
|
||||
2 + sizeof(struct ieee80211_channel_sw_ie) +
|
||||
/* Mesh Channel Swith Parameters */
|
||||
2 + sizeof(struct ieee80211_mesh_chansw_params_ie) +
|
||||
2 + 8 + /* supported rates */
|
||||
2 + 3; /* DS params */
|
||||
tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
|
||||
@ -665,6 +671,38 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
|
||||
*pos++ = WLAN_EID_SSID;
|
||||
*pos++ = 0x0;
|
||||
|
||||
rcu_read_lock();
|
||||
csa = rcu_dereference(ifmsh->csa);
|
||||
if (csa) {
|
||||
__le16 pre_value;
|
||||
|
||||
pos = skb_put(skb, 13);
|
||||
memset(pos, 0, 13);
|
||||
*pos++ = WLAN_EID_CHANNEL_SWITCH;
|
||||
*pos++ = 3;
|
||||
*pos++ = 0x0;
|
||||
*pos++ = ieee80211_frequency_to_channel(
|
||||
csa->settings.chandef.chan->center_freq);
|
||||
sdata->csa_counter_offset_beacon = hdr_len + 6;
|
||||
*pos++ = csa->settings.count;
|
||||
*pos++ = WLAN_EID_CHAN_SWITCH_PARAM;
|
||||
*pos++ = 6;
|
||||
if (ifmsh->chsw_init) {
|
||||
*pos++ = ifmsh->mshcfg.dot11MeshTTL;
|
||||
*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
|
||||
} else {
|
||||
*pos++ = ifmsh->chsw_ttl;
|
||||
}
|
||||
*pos++ |= csa->settings.block_tx ?
|
||||
WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
|
||||
put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos);
|
||||
pos += 2;
|
||||
pre_value = cpu_to_le16(ifmsh->pre_value);
|
||||
memcpy(pos, &pre_value, 2);
|
||||
pos += 2;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
|
||||
mesh_add_ds_params_ie(sdata, skb))
|
||||
goto out_free;
|
||||
@ -812,6 +850,127 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
|
||||
ieee80211_configure_filter(local);
|
||||
}
|
||||
|
||||
static bool
|
||||
ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems, bool beacon)
|
||||
{
|
||||
struct cfg80211_csa_settings params;
|
||||
struct ieee80211_csa_ie csa_ie;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
|
||||
int err, num_chanctx;
|
||||
u32 sta_flags;
|
||||
|
||||
if (sdata->vif.csa_active)
|
||||
return true;
|
||||
|
||||
if (!ifmsh->mesh_id)
|
||||
return false;
|
||||
|
||||
sta_flags = IEEE80211_STA_DISABLE_VHT;
|
||||
switch (sdata->vif.bss_conf.chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
sta_flags |= IEEE80211_STA_DISABLE_HT;
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
sta_flags |= IEEE80211_STA_DISABLE_40MHZ;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
memset(&csa_ie, 0, sizeof(csa_ie));
|
||||
err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
|
||||
sta_flags, sdata->vif.addr,
|
||||
&csa_ie);
|
||||
if (err < 0)
|
||||
return false;
|
||||
if (err)
|
||||
return false;
|
||||
|
||||
params.chandef = csa_ie.chandef;
|
||||
params.count = csa_ie.count;
|
||||
|
||||
if (sdata->vif.bss_conf.chandef.chan->band !=
|
||||
params.chandef.chan->band)
|
||||
return false;
|
||||
|
||||
if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef,
|
||||
IEEE80211_CHAN_DISABLED)) {
|
||||
sdata_info(sdata,
|
||||
"mesh STA %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), aborting\n",
|
||||
sdata->vif.addr,
|
||||
params.chandef.chan->center_freq,
|
||||
params.chandef.width,
|
||||
params.chandef.center_freq1,
|
||||
params.chandef.center_freq2);
|
||||
return false;
|
||||
}
|
||||
|
||||
err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
|
||||
¶ms.chandef);
|
||||
if (err < 0)
|
||||
return false;
|
||||
if (err) {
|
||||
params.radar_required = true;
|
||||
/* TODO: DFS not (yet) supported */
|
||||
return false;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (!chanctx_conf)
|
||||
goto failed_chswitch;
|
||||
|
||||
/* don't handle for multi-VIF cases */
|
||||
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
|
||||
if (chanctx->refcount > 1)
|
||||
goto failed_chswitch;
|
||||
|
||||
num_chanctx = 0;
|
||||
list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list)
|
||||
num_chanctx++;
|
||||
|
||||
if (num_chanctx > 1)
|
||||
goto failed_chswitch;
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
mcsa_dbg(sdata,
|
||||
"received channel switch announcement to go to channel %d MHz\n",
|
||||
params.chandef.chan->center_freq);
|
||||
|
||||
params.block_tx = csa_ie.mode & WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT;
|
||||
if (beacon)
|
||||
ifmsh->chsw_ttl = csa_ie.ttl - 1;
|
||||
else
|
||||
ifmsh->chsw_ttl = 0;
|
||||
|
||||
if (ifmsh->chsw_ttl > 0)
|
||||
if (ieee80211_mesh_csa_beacon(sdata, ¶ms, false) < 0)
|
||||
return false;
|
||||
|
||||
sdata->csa_radar_required = params.radar_required;
|
||||
|
||||
if (params.block_tx)
|
||||
ieee80211_stop_queues_by_reason(&sdata->local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
|
||||
sdata->local->csa_chandef = params.chandef;
|
||||
sdata->vif.csa_active = true;
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, err);
|
||||
drv_channel_switch_beacon(sdata, ¶ms.chandef);
|
||||
|
||||
return true;
|
||||
failed_chswitch:
|
||||
rcu_read_unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt, size_t len)
|
||||
@ -918,6 +1077,142 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
||||
if (ifmsh->sync_ops)
|
||||
ifmsh->sync_ops->rx_bcn_presp(sdata,
|
||||
stype, mgmt, &elems, rx_status);
|
||||
|
||||
if (!ifmsh->chsw_init)
|
||||
ieee80211_mesh_process_chnswitch(sdata, &elems, true);
|
||||
}
|
||||
|
||||
int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct mesh_csa_settings *tmp_csa_settings;
|
||||
int ret = 0;
|
||||
|
||||
/* Reset the TTL value and Initiator flag */
|
||||
ifmsh->chsw_init = false;
|
||||
ifmsh->chsw_ttl = 0;
|
||||
|
||||
/* Remove the CSA and MCSP elements from the beacon */
|
||||
tmp_csa_settings = rcu_dereference(ifmsh->csa);
|
||||
rcu_assign_pointer(ifmsh->csa, NULL);
|
||||
kfree_rcu(tmp_csa_settings, rcu_head);
|
||||
ret = ieee80211_mesh_rebuild_beacon(sdata);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
|
||||
|
||||
mcsa_dbg(sdata, "complete switching to center freq %d MHz",
|
||||
sdata->vif.bss_conf.chandef.chan->center_freq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_csa_settings *csa_settings,
|
||||
bool csa_action)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct mesh_csa_settings *tmp_csa_settings;
|
||||
int ret = 0;
|
||||
|
||||
tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings),
|
||||
GFP_ATOMIC);
|
||||
if (!tmp_csa_settings)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(&tmp_csa_settings->settings, csa_settings,
|
||||
sizeof(struct cfg80211_csa_settings));
|
||||
|
||||
rcu_assign_pointer(ifmsh->csa, tmp_csa_settings);
|
||||
|
||||
ret = ieee80211_mesh_rebuild_beacon(sdata);
|
||||
if (ret) {
|
||||
tmp_csa_settings = rcu_dereference(ifmsh->csa);
|
||||
rcu_assign_pointer(ifmsh->csa, NULL);
|
||||
kfree_rcu(tmp_csa_settings, rcu_head);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
|
||||
|
||||
if (csa_action)
|
||||
ieee80211_send_action_csa(sdata, csa_settings);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt, size_t len)
|
||||
{
|
||||
struct ieee80211_mgmt *mgmt_fwd;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
u8 *pos = mgmt->u.action.u.chan_switch.variable;
|
||||
size_t offset_ttl;
|
||||
|
||||
skb = dev_alloc_skb(local->tx_headroom + len);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
skb_reserve(skb, local->tx_headroom);
|
||||
mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len);
|
||||
|
||||
/* offset_ttl is based on whether the secondary channel
|
||||
* offset is available or not. Substract 1 from the mesh TTL
|
||||
* and disable the initiator flag before forwarding.
|
||||
*/
|
||||
offset_ttl = (len < 42) ? 7 : 10;
|
||||
*(pos + offset_ttl) -= 1;
|
||||
*(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
|
||||
sdata->u.mesh.chsw_ttl = *(pos + offset_ttl);
|
||||
|
||||
memcpy(mgmt_fwd, mgmt, len);
|
||||
eth_broadcast_addr(mgmt_fwd->da);
|
||||
memcpy(mgmt_fwd->sa, sdata->vif.addr, ETH_ALEN);
|
||||
memcpy(mgmt_fwd->bssid, sdata->vif.addr, ETH_ALEN);
|
||||
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt, size_t len)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct ieee802_11_elems elems;
|
||||
u16 pre_value;
|
||||
bool fwd_csa = true;
|
||||
size_t baselen;
|
||||
u8 *pos, ttl;
|
||||
|
||||
if (mgmt->u.action.u.measurement.action_code !=
|
||||
WLAN_ACTION_SPCT_CHL_SWITCH)
|
||||
return;
|
||||
|
||||
pos = mgmt->u.action.u.chan_switch.variable;
|
||||
baselen = offsetof(struct ieee80211_mgmt,
|
||||
u.action.u.chan_switch.variable);
|
||||
ieee802_11_parse_elems(pos, len - baselen, false, &elems);
|
||||
|
||||
ttl = elems.mesh_chansw_params_ie->mesh_ttl;
|
||||
if (!--ttl)
|
||||
fwd_csa = false;
|
||||
|
||||
pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value);
|
||||
if (ifmsh->pre_value >= pre_value)
|
||||
return;
|
||||
|
||||
ifmsh->pre_value = pre_value;
|
||||
|
||||
if (!ieee80211_mesh_process_chnswitch(sdata, &elems, false)) {
|
||||
mcsa_dbg(sdata, "Failed to process CSA action frame");
|
||||
return;
|
||||
}
|
||||
|
||||
/* forward or re-broadcast the CSA frame */
|
||||
if (fwd_csa) {
|
||||
if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0)
|
||||
mcsa_dbg(sdata, "Failed to forward the CSA frame");
|
||||
}
|
||||
}
|
||||
|
||||
static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
|
||||
@ -939,6 +1234,9 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
|
||||
if (mesh_action_is_path_sel(mgmt))
|
||||
mesh_rx_path_sel_frame(sdata, mgmt, len);
|
||||
break;
|
||||
case WLAN_CATEGORY_SPECTRUM_MGMT:
|
||||
mesh_rx_csa_frame(sdata, mgmt, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1056,13 +1354,11 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
(unsigned long) sdata);
|
||||
|
||||
ifmsh->accepting_plinks = true;
|
||||
ifmsh->preq_id = 0;
|
||||
ifmsh->sn = 0;
|
||||
ifmsh->num_gates = 0;
|
||||
atomic_set(&ifmsh->mpaths, 0);
|
||||
mesh_rmc_init(sdata);
|
||||
ifmsh->last_preq = jiffies;
|
||||
ifmsh->next_perr = jiffies;
|
||||
ifmsh->chsw_init = false;
|
||||
/* Allocate all mesh structures when creating the first mesh interface. */
|
||||
if (!mesh_allocated)
|
||||
ieee80211s_init();
|
||||
|
@ -222,7 +222,8 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta)
|
||||
mesh_path_flush_by_nexthop(sta);
|
||||
|
||||
ieee80211_mps_sta_status_update(sta);
|
||||
changed |= ieee80211_mps_local_status_update(sdata);
|
||||
changed |= ieee80211_mps_set_sta_local_pm(sta,
|
||||
NL80211_MESH_POWER_UNKNOWN);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
@ -152,6 +152,9 @@ u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta,
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
|
||||
if (sta->local_pm == pm)
|
||||
return 0;
|
||||
|
||||
mps_dbg(sdata, "local STA operates in mode %d with %pM\n",
|
||||
pm, sta->sta.addr);
|
||||
|
||||
@ -245,6 +248,14 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)
|
||||
|
||||
do_buffer = (pm != NL80211_MESH_POWER_ACTIVE);
|
||||
|
||||
/* clear the MPSP flags for non-peers or active STA */
|
||||
if (sta->plink_state != NL80211_PLINK_ESTAB) {
|
||||
clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
|
||||
clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
|
||||
} else if (!do_buffer) {
|
||||
clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
|
||||
}
|
||||
|
||||
/* Don't let the same PS state be set twice */
|
||||
if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer)
|
||||
return;
|
||||
@ -257,14 +268,6 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta)
|
||||
} else {
|
||||
ieee80211_sta_ps_deliver_wakeup(sta);
|
||||
}
|
||||
|
||||
/* clear the MPSP flags for non-peers or active STA */
|
||||
if (sta->plink_state != NL80211_PLINK_ESTAB) {
|
||||
clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
|
||||
clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT);
|
||||
} else if (!do_buffer) {
|
||||
clear_sta_flag(sta, WLAN_STA_MPSP_OWNER);
|
||||
}
|
||||
}
|
||||
|
||||
static void mps_set_sta_peer_pm(struct sta_info *sta,
|
||||
@ -444,8 +447,7 @@ static void mpsp_qos_null_append(struct sta_info *sta,
|
||||
*/
|
||||
static void mps_frame_deliver(struct sta_info *sta, int n_frames)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_local *local = sta->sdata->local;
|
||||
int ac;
|
||||
struct sk_buff_head frames;
|
||||
struct sk_buff *skb;
|
||||
@ -558,10 +560,10 @@ void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta,
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_mps_frame_release - release buffered frames in response to beacon
|
||||
* ieee80211_mps_frame_release - release frames buffered due to mesh power save
|
||||
*
|
||||
* @sta: mesh STA
|
||||
* @elems: beacon IEs
|
||||
* @elems: IEs of beacon or probe response
|
||||
*
|
||||
* For peers if we have individually-addressed frames buffered or the peer
|
||||
* indicates buffered frames, send a corresponding MPSP trigger frame. Since
|
||||
@ -588,9 +590,10 @@ void ieee80211_mps_frame_release(struct sta_info *sta,
|
||||
(!elems->awake_window || !le16_to_cpu(*elems->awake_window)))
|
||||
return;
|
||||
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
|
||||
buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
|
||||
skb_queue_len(&sta->tx_filtered[ac]);
|
||||
if (!test_sta_flag(sta, WLAN_STA_MPSP_OWNER))
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
|
||||
buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) +
|
||||
skb_queue_len(&sta->tx_filtered[ac]);
|
||||
|
||||
if (!has_buffered && !buffer_local)
|
||||
return;
|
||||
|
@ -958,9 +958,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_bss *cbss = ifmgd->associated;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
enum ieee80211_band current_band;
|
||||
u8 count;
|
||||
u8 mode;
|
||||
struct cfg80211_chan_def new_chandef = {};
|
||||
struct ieee80211_csa_ie csa_ie;
|
||||
int res;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
@ -976,24 +974,24 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
return;
|
||||
|
||||
current_band = cbss->channel->band;
|
||||
memset(&csa_ie, 0, sizeof(csa_ie));
|
||||
res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
|
||||
ifmgd->flags,
|
||||
ifmgd->associated->bssid, &count,
|
||||
&mode, &new_chandef);
|
||||
ifmgd->associated->bssid, &csa_ie);
|
||||
if (res < 0)
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
if (res)
|
||||
return;
|
||||
|
||||
if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
|
||||
if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef,
|
||||
IEEE80211_CHAN_DISABLED)) {
|
||||
sdata_info(sdata,
|
||||
"AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
|
||||
ifmgd->associated->bssid,
|
||||
new_chandef.chan->center_freq,
|
||||
new_chandef.width, new_chandef.center_freq1,
|
||||
new_chandef.center_freq2);
|
||||
csa_ie.chandef.chan->center_freq,
|
||||
csa_ie.chandef.width, csa_ie.chandef.center_freq1,
|
||||
csa_ie.chandef.center_freq2);
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
return;
|
||||
@ -1037,9 +1035,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
|
||||
local->csa_chandef = new_chandef;
|
||||
local->csa_chandef = csa_ie.chandef;
|
||||
|
||||
if (mode)
|
||||
if (csa_ie.mode)
|
||||
ieee80211_stop_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
@ -1048,9 +1046,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
/* use driver's channel switch callback */
|
||||
struct ieee80211_channel_switch ch_switch = {
|
||||
.timestamp = timestamp,
|
||||
.block_tx = mode,
|
||||
.chandef = new_chandef,
|
||||
.count = count,
|
||||
.block_tx = csa_ie.mode,
|
||||
.chandef = csa_ie.chandef,
|
||||
.count = csa_ie.count,
|
||||
};
|
||||
|
||||
drv_channel_switch(local, &ch_switch);
|
||||
@ -1058,11 +1056,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
|
||||
/* channel switch handled in software */
|
||||
if (count <= 1)
|
||||
if (csa_ie.count <= 1)
|
||||
ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
|
||||
else
|
||||
mod_timer(&ifmgd->chswitch_timer,
|
||||
TU_TO_EXP_TIME(count * cbss->beacon_interval));
|
||||
TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
|
||||
}
|
||||
|
||||
static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
||||
@ -3500,7 +3498,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
ieee80211_beacon_connection_loss_work);
|
||||
INIT_WORK(&ifmgd->csa_connection_drop_work,
|
||||
ieee80211_csa_connection_drop_work);
|
||||
INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
|
||||
INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
|
||||
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
|
||||
(unsigned long) sdata);
|
||||
setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
|
||||
|
@ -2593,13 +2593,16 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
|
||||
break;
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
|
||||
sdata->vif.type != NL80211_IFTYPE_ADHOC)
|
||||
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||
sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
|
||||
break;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||
bssid = sdata->u.mgd.bssid;
|
||||
else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
bssid = sdata->u.ibss.bssid;
|
||||
else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
|
||||
bssid = mgmt->sa;
|
||||
else
|
||||
break;
|
||||
|
||||
|
@ -24,8 +24,8 @@
|
||||
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems, bool beacon,
|
||||
enum ieee80211_band current_band,
|
||||
u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
|
||||
struct cfg80211_chan_def *new_chandef)
|
||||
u32 sta_flags, u8 *bssid,
|
||||
struct ieee80211_csa_ie *csa_ie)
|
||||
{
|
||||
enum ieee80211_band new_band;
|
||||
int new_freq;
|
||||
@ -62,18 +62,24 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
return -EINVAL;
|
||||
}
|
||||
new_chan_no = elems->ext_chansw_ie->new_ch_num;
|
||||
*count = elems->ext_chansw_ie->count;
|
||||
*mode = elems->ext_chansw_ie->mode;
|
||||
csa_ie->count = elems->ext_chansw_ie->count;
|
||||
csa_ie->mode = elems->ext_chansw_ie->mode;
|
||||
} else if (elems->ch_switch_ie) {
|
||||
new_band = current_band;
|
||||
new_chan_no = elems->ch_switch_ie->new_ch_num;
|
||||
*count = elems->ch_switch_ie->count;
|
||||
*mode = elems->ch_switch_ie->mode;
|
||||
csa_ie->count = elems->ch_switch_ie->count;
|
||||
csa_ie->mode = elems->ch_switch_ie->mode;
|
||||
} else {
|
||||
/* nothing here we understand */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Mesh Channel Switch Parameters Element */
|
||||
if (elems->mesh_chansw_params_ie) {
|
||||
csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl;
|
||||
csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags;
|
||||
}
|
||||
|
||||
new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
|
||||
new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
|
||||
if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
|
||||
@ -103,25 +109,26 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
default:
|
||||
/* secondary_channel_offset was present but is invalid */
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
|
||||
cfg80211_chandef_create(new_chandef, new_chan,
|
||||
cfg80211_chandef_create(&csa_ie->chandef, new_chan,
|
||||
NL80211_CHAN_HT20);
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
cfg80211_chandef_create(new_chandef, new_chan,
|
||||
cfg80211_chandef_create(&csa_ie->chandef, new_chan,
|
||||
NL80211_CHAN_HT40PLUS);
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
cfg80211_chandef_create(new_chandef, new_chan,
|
||||
cfg80211_chandef_create(&csa_ie->chandef, new_chan,
|
||||
NL80211_CHAN_HT40MINUS);
|
||||
break;
|
||||
case -1:
|
||||
cfg80211_chandef_create(new_chandef, new_chan,
|
||||
cfg80211_chandef_create(&csa_ie->chandef, new_chan,
|
||||
NL80211_CHAN_NO_HT);
|
||||
/* keep width for 5/10 MHz channels */
|
||||
switch (sdata->vif.bss_conf.chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
new_chandef->width = sdata->vif.bss_conf.chandef.width;
|
||||
csa_ie->chandef.width =
|
||||
sdata->vif.bss_conf.chandef.width;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -171,13 +178,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
/* if VHT data is there validate & use it */
|
||||
if (new_vht_chandef.chan) {
|
||||
if (!cfg80211_chandef_compatible(&new_vht_chandef,
|
||||
new_chandef)) {
|
||||
&csa_ie->chandef)) {
|
||||
sdata_info(sdata,
|
||||
"BSS %pM: CSA has inconsistent channel data, disconnecting\n",
|
||||
bssid);
|
||||
return -EINVAL;
|
||||
}
|
||||
*new_chandef = new_vht_chandef;
|
||||
csa_ie->chandef = new_vht_chandef;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -385,6 +385,30 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
||||
sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
|
||||
|
||||
sta->sta.smps_mode = IEEE80211_SMPS_OFF;
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP ||
|
||||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
|
||||
struct ieee80211_supported_band *sband =
|
||||
local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
|
||||
u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
|
||||
IEEE80211_HT_CAP_SM_PS_SHIFT;
|
||||
/*
|
||||
* Assume that hostapd advertises our caps in the beacon and
|
||||
* this is the known_smps_mode for a station that just assciated
|
||||
*/
|
||||
switch (smps) {
|
||||
case WLAN_HT_SMPS_CONTROL_DISABLED:
|
||||
sta->known_smps_mode = IEEE80211_SMPS_OFF;
|
||||
break;
|
||||
case WLAN_HT_SMPS_CONTROL_STATIC:
|
||||
sta->known_smps_mode = IEEE80211_SMPS_STATIC;
|
||||
break;
|
||||
case WLAN_HT_SMPS_CONTROL_DYNAMIC:
|
||||
sta->known_smps_mode = IEEE80211_SMPS_DYNAMIC;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
}
|
||||
|
||||
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
|
||||
|
||||
@ -1069,6 +1093,19 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
|
||||
|
||||
ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta);
|
||||
|
||||
/* This station just woke up and isn't aware of our SMPS state */
|
||||
if (!ieee80211_smps_is_restrictive(sta->known_smps_mode,
|
||||
sdata->smps_mode) &&
|
||||
sta->known_smps_mode != sdata->bss->req_smps &&
|
||||
sta_info_tx_streams(sta) != 1) {
|
||||
ht_dbg(sdata,
|
||||
"%pM just woke up and MIMO capable - update SMPS\n",
|
||||
sta->sta.addr);
|
||||
ieee80211_send_smps_action(sdata, sdata->bss->req_smps,
|
||||
sta->sta.addr,
|
||||
sdata->vif.bss_conf.bssid);
|
||||
}
|
||||
|
||||
local->total_ps_buffered -= buffered;
|
||||
|
||||
sta_info_recalc_tim(sta);
|
||||
@ -1520,3 +1557,38 @@ int sta_info_move_state(struct sta_info *sta,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 sta_info_tx_streams(struct sta_info *sta)
|
||||
{
|
||||
struct ieee80211_sta_ht_cap *ht_cap = &sta->sta.ht_cap;
|
||||
u8 rx_streams;
|
||||
|
||||
if (!sta->sta.ht_cap.ht_supported)
|
||||
return 1;
|
||||
|
||||
if (sta->sta.vht_cap.vht_supported) {
|
||||
int i;
|
||||
u16 tx_mcs_map =
|
||||
le16_to_cpu(sta->sta.vht_cap.vht_mcs.tx_mcs_map);
|
||||
|
||||
for (i = 7; i >= 0; i--)
|
||||
if ((tx_mcs_map & (0x3 << (i * 2))) !=
|
||||
IEEE80211_VHT_MCS_NOT_SUPPORTED)
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
if (ht_cap->mcs.rx_mask[3])
|
||||
rx_streams = 4;
|
||||
else if (ht_cap->mcs.rx_mask[2])
|
||||
rx_streams = 3;
|
||||
else if (ht_cap->mcs.rx_mask[1])
|
||||
rx_streams = 2;
|
||||
else
|
||||
rx_streams = 1;
|
||||
|
||||
if (!(ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF))
|
||||
return rx_streams;
|
||||
|
||||
return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
|
||||
>> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
|
||||
}
|
||||
|
@ -301,6 +301,8 @@ struct sta_ampdu_mlme {
|
||||
* @chains: chains ever used for RX from this station
|
||||
* @chain_signal_last: last signal (per chain)
|
||||
* @chain_signal_avg: signal average (per chain)
|
||||
* @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
|
||||
* AP only.
|
||||
*/
|
||||
struct sta_info {
|
||||
/* General information, mostly static */
|
||||
@ -411,6 +413,8 @@ struct sta_info {
|
||||
unsigned int lost_packets;
|
||||
unsigned int beacon_loss_count;
|
||||
|
||||
enum ieee80211_smps_mode known_smps_mode;
|
||||
|
||||
/* keep last! */
|
||||
struct ieee80211_sta sta;
|
||||
};
|
||||
@ -613,6 +617,7 @@ void sta_set_rate_info_rx(struct sta_info *sta,
|
||||
struct rate_info *rinfo);
|
||||
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
|
||||
unsigned long exp_time);
|
||||
u8 sta_info_tx_streams(struct sta_info *sta);
|
||||
|
||||
void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
|
||||
void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
|
||||
|
@ -194,29 +194,36 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
|
||||
if (ieee80211_is_action(mgmt->frame_control) &&
|
||||
mgmt->u.action.category == WLAN_CATEGORY_HT &&
|
||||
mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS &&
|
||||
sdata->vif.type == NL80211_IFTYPE_STATION &&
|
||||
ieee80211_sdata_running(sdata)) {
|
||||
/*
|
||||
* This update looks racy, but isn't -- if we come
|
||||
* here we've definitely got a station that we're
|
||||
* talking to, and on a managed interface that can
|
||||
* only be the AP. And the only other place updating
|
||||
* this variable in managed mode is before association.
|
||||
*/
|
||||
enum ieee80211_smps_mode smps_mode;
|
||||
|
||||
switch (mgmt->u.action.u.ht_smps.smps_control) {
|
||||
case WLAN_HT_SMPS_CONTROL_DYNAMIC:
|
||||
sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
|
||||
smps_mode = IEEE80211_SMPS_DYNAMIC;
|
||||
break;
|
||||
case WLAN_HT_SMPS_CONTROL_STATIC:
|
||||
sdata->smps_mode = IEEE80211_SMPS_STATIC;
|
||||
smps_mode = IEEE80211_SMPS_STATIC;
|
||||
break;
|
||||
case WLAN_HT_SMPS_CONTROL_DISABLED:
|
||||
default: /* shouldn't happen since we don't send that */
|
||||
sdata->smps_mode = IEEE80211_SMPS_OFF;
|
||||
smps_mode = IEEE80211_SMPS_OFF;
|
||||
break;
|
||||
}
|
||||
|
||||
ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
||||
/*
|
||||
* This update looks racy, but isn't -- if we come
|
||||
* here we've definitely got a station that we're
|
||||
* talking to, and on a managed interface that can
|
||||
* only be the AP. And the only other place updating
|
||||
* this variable in managed mode is before association.
|
||||
*/
|
||||
sdata->smps_mode = smps_mode;
|
||||
ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
|
||||
} else if (sdata->vif.type == NL80211_IFTYPE_AP ||
|
||||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
|
||||
sta->known_smps_mode = smps_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1367,6 +1367,35 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif, struct sk_buff *skb,
|
||||
int band, struct ieee80211_sta **sta)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_tx_data tx;
|
||||
|
||||
if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP)
|
||||
return false;
|
||||
|
||||
info->band = band;
|
||||
info->control.vif = vif;
|
||||
info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)];
|
||||
|
||||
if (invoke_tx_handlers(&tx))
|
||||
return false;
|
||||
|
||||
if (sta) {
|
||||
if (tx.sta)
|
||||
*sta = &tx.sta->sta;
|
||||
else
|
||||
*sta = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
|
||||
|
||||
/*
|
||||
* Returns false if the frame couldn't be transmitted but was queued instead.
|
||||
*/
|
||||
@ -2370,6 +2399,10 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
|
||||
beacon_data = beacon->head;
|
||||
beacon_data_len = beacon->head_len;
|
||||
break;
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
beacon_data = beacon->head;
|
||||
beacon_data_len = beacon->head_len;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
@ -2424,6 +2457,15 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
|
||||
if (!beacon)
|
||||
goto out;
|
||||
|
||||
beacon_data = beacon->head;
|
||||
beacon_data_len = beacon->head_len;
|
||||
} else if (vif->type == NL80211_IFTYPE_MESH_POINT) {
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
|
||||
beacon = rcu_dereference(ifmsh->beacon);
|
||||
if (!beacon)
|
||||
goto out;
|
||||
|
||||
beacon_data = beacon->head;
|
||||
beacon_data_len = beacon->head_len;
|
||||
} else {
|
||||
@ -2531,6 +2573,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
|
||||
if (!bcn)
|
||||
goto out;
|
||||
|
||||
if (sdata->vif.csa_active)
|
||||
ieee80211_update_csa(sdata, bcn);
|
||||
|
||||
if (ifmsh->sync_ops)
|
||||
ifmsh->sync_ops->adjust_tbtt(
|
||||
sdata);
|
||||
|
@ -300,9 +300,6 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
|
||||
if (!sdata->dev)
|
||||
continue;
|
||||
|
||||
if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state))
|
||||
continue;
|
||||
|
||||
if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE &&
|
||||
local->queue_stop_reasons[sdata->vif.cab_queue] != 0)
|
||||
continue;
|
||||
@ -743,6 +740,7 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
||||
case WLAN_EID_TIMEOUT_INTERVAL:
|
||||
case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
|
||||
case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
|
||||
case WLAN_EID_CHAN_SWITCH_PARAM:
|
||||
/*
|
||||
* not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
|
||||
* that if the content gets bigger it might be needed more than once
|
||||
@ -908,6 +906,14 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
||||
}
|
||||
elems->sec_chan_offs = (void *)pos;
|
||||
break;
|
||||
case WLAN_EID_CHAN_SWITCH_PARAM:
|
||||
if (elen !=
|
||||
sizeof(*elems->mesh_chansw_params_ie)) {
|
||||
elem_parse_failed = true;
|
||||
break;
|
||||
}
|
||||
elems->mesh_chansw_params_ie = (void *)pos;
|
||||
break;
|
||||
case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
|
||||
if (!action ||
|
||||
elen != sizeof(*elems->wide_bw_chansw_ie)) {
|
||||
@ -2354,3 +2360,115 @@ u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if smps_mode_new is strictly more restrictive than
|
||||
* smps_mode_old.
|
||||
*/
|
||||
bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old,
|
||||
enum ieee80211_smps_mode smps_mode_new)
|
||||
{
|
||||
if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC ||
|
||||
smps_mode_new == IEEE80211_SMPS_AUTOMATIC))
|
||||
return false;
|
||||
|
||||
switch (smps_mode_old) {
|
||||
case IEEE80211_SMPS_STATIC:
|
||||
return false;
|
||||
case IEEE80211_SMPS_DYNAMIC:
|
||||
return smps_mode_new == IEEE80211_SMPS_STATIC;
|
||||
case IEEE80211_SMPS_OFF:
|
||||
return smps_mode_new != IEEE80211_SMPS_OFF;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_csa_settings *csa_settings)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int freq;
|
||||
int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
|
||||
sizeof(mgmt->u.action.u.chan_switch);
|
||||
u8 *pos;
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||
sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
skb = dev_alloc_skb(local->tx_headroom + hdr_len +
|
||||
5 + /* channel switch announcement element */
|
||||
3 + /* secondary channel offset element */
|
||||
8); /* mesh channel switch parameters element */
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
skb_reserve(skb, local->tx_headroom);
|
||||
mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
|
||||
memset(mgmt, 0, hdr_len);
|
||||
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
||||
IEEE80211_STYPE_ACTION);
|
||||
|
||||
eth_broadcast_addr(mgmt->da);
|
||||
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
|
||||
} else {
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
|
||||
}
|
||||
mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
|
||||
mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
|
||||
pos = skb_put(skb, 5);
|
||||
*pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */
|
||||
*pos++ = 3; /* IE length */
|
||||
*pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */
|
||||
freq = csa_settings->chandef.chan->center_freq;
|
||||
*pos++ = ieee80211_frequency_to_channel(freq); /* channel */
|
||||
*pos++ = csa_settings->count; /* count */
|
||||
|
||||
if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
|
||||
enum nl80211_channel_type ch_type;
|
||||
|
||||
skb_put(skb, 3);
|
||||
*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */
|
||||
*pos++ = 1; /* IE length */
|
||||
ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
|
||||
if (ch_type == NL80211_CHAN_HT40PLUS)
|
||||
*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
|
||||
else
|
||||
*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
|
||||
}
|
||||
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
__le16 pre_value;
|
||||
|
||||
skb_put(skb, 8);
|
||||
*pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */
|
||||
*pos++ = 6; /* IE length */
|
||||
*pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL; /* Mesh TTL */
|
||||
*pos = 0x00; /* Mesh Flag: Tx Restrict, Initiator, Reason */
|
||||
*pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
|
||||
*pos++ |= csa_settings->block_tx ?
|
||||
WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
|
||||
put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */
|
||||
pos += 2;
|
||||
if (!ifmsh->pre_value)
|
||||
ifmsh->pre_value = 1;
|
||||
else
|
||||
ifmsh->pre_value++;
|
||||
pre_value = cpu_to_le16(ifmsh->pre_value);
|
||||
memcpy(pos, &pre_value, 2); /* Precedence Value */
|
||||
pos += 2;
|
||||
ifmsh->chsw_init = true;
|
||||
}
|
||||
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
return 0;
|
||||
}
|
||||
|
@ -301,22 +301,16 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
|
||||
}
|
||||
|
||||
|
||||
static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
|
||||
static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
|
||||
int encrypted)
|
||||
{
|
||||
__le16 mask_fc;
|
||||
int a4_included, mgmt;
|
||||
u8 qos_tid;
|
||||
u8 *b_0, *aad;
|
||||
u16 data_len, len_a;
|
||||
u16 len_a;
|
||||
unsigned int hdrlen;
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||
|
||||
memset(scratch, 0, 6 * AES_BLOCK_SIZE);
|
||||
|
||||
b_0 = scratch + 3 * AES_BLOCK_SIZE;
|
||||
aad = scratch + 4 * AES_BLOCK_SIZE;
|
||||
|
||||
/*
|
||||
* Mask FC: zero subtype b4 b5 b6 (if not mgmt)
|
||||
* Retry, PwrMgt, MoreData; set Protected
|
||||
@ -338,20 +332,21 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch,
|
||||
else
|
||||
qos_tid = 0;
|
||||
|
||||
data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN;
|
||||
if (encrypted)
|
||||
data_len -= IEEE80211_CCMP_MIC_LEN;
|
||||
/* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
|
||||
* mode authentication are not allowed to collide, yet both are derived
|
||||
* from this vector b_0. We only set L := 1 here to indicate that the
|
||||
* data size can be represented in (L+1) bytes. The CCM layer will take
|
||||
* care of storing the data length in the top (L+1) bytes and setting
|
||||
* and clearing the other bits as is required to derive the two IVs.
|
||||
*/
|
||||
b_0[0] = 0x1;
|
||||
|
||||
/* First block, b_0 */
|
||||
b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */
|
||||
/* Nonce: Nonce Flags | A2 | PN
|
||||
* Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)
|
||||
*/
|
||||
b_0[1] = qos_tid | (mgmt << 4);
|
||||
memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
|
||||
memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
|
||||
/* l(m) */
|
||||
put_unaligned_be16(data_len, &b_0[14]);
|
||||
|
||||
/* AAD (extra authenticate-only data) / masked 802.11 header
|
||||
* FC | A1 | A2 | A3 | SC | [A4] | [QC] */
|
||||
@ -407,7 +402,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
|
||||
u8 *pos;
|
||||
u8 pn[6];
|
||||
u64 pn64;
|
||||
u8 scratch[6 * AES_BLOCK_SIZE];
|
||||
u8 aad[2 * AES_BLOCK_SIZE];
|
||||
u8 b_0[AES_BLOCK_SIZE];
|
||||
|
||||
if (info->control.hw_key &&
|
||||
!(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
|
||||
@ -460,9 +456,9 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
|
||||
return 0;
|
||||
|
||||
pos += IEEE80211_CCMP_HDR_LEN;
|
||||
ccmp_special_blocks(skb, pn, scratch, 0);
|
||||
ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len,
|
||||
pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN));
|
||||
ccmp_special_blocks(skb, pn, b_0, aad, 0);
|
||||
ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
|
||||
skb_put(skb, IEEE80211_CCMP_MIC_LEN));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -525,16 +521,16 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
|
||||
}
|
||||
|
||||
if (!(status->flag & RX_FLAG_DECRYPTED)) {
|
||||
u8 scratch[6 * AES_BLOCK_SIZE];
|
||||
u8 aad[2 * AES_BLOCK_SIZE];
|
||||
u8 b_0[AES_BLOCK_SIZE];
|
||||
/* hardware didn't decrypt/verify MIC */
|
||||
ccmp_special_blocks(skb, pn, scratch, 1);
|
||||
ccmp_special_blocks(skb, pn, b_0, aad, 1);
|
||||
|
||||
if (ieee80211_aes_ccm_decrypt(
|
||||
key->u.ccmp.tfm, scratch,
|
||||
key->u.ccmp.tfm, b_0, aad,
|
||||
skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
|
||||
data_len,
|
||||
skb->data + skb->len - IEEE80211_CCMP_MIC_LEN,
|
||||
skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN))
|
||||
skb->data + skb->len - IEEE80211_CCMP_MIC_LEN))
|
||||
return RX_DROP_UNUSABLE;
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ config RFKILL_REGULATOR
|
||||
|
||||
config RFKILL_GPIO
|
||||
tristate "GPIO RFKILL driver"
|
||||
depends on RFKILL && GPIOLIB && HAVE_CLK
|
||||
depends on RFKILL && GPIOLIB
|
||||
default n
|
||||
help
|
||||
If you say yes here you get support of a generic gpio RFKILL
|
||||
|
@ -24,27 +24,23 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/acpi_gpio.h>
|
||||
|
||||
#include <linux/rfkill-gpio.h>
|
||||
|
||||
enum rfkill_gpio_clk_state {
|
||||
UNSPECIFIED = 0,
|
||||
PWR_ENABLED,
|
||||
PWR_DISABLED
|
||||
};
|
||||
|
||||
#define PWR_CLK_SET(_RF, _EN) \
|
||||
((_RF)->pwr_clk_enabled = (!(_EN) ? PWR_ENABLED : PWR_DISABLED))
|
||||
#define PWR_CLK_ENABLED(_RF) ((_RF)->pwr_clk_enabled == PWR_ENABLED)
|
||||
#define PWR_CLK_DISABLED(_RF) ((_RF)->pwr_clk_enabled != PWR_ENABLED)
|
||||
|
||||
struct rfkill_gpio_data {
|
||||
struct rfkill_gpio_platform_data *pdata;
|
||||
struct rfkill *rfkill_dev;
|
||||
char *reset_name;
|
||||
char *shutdown_name;
|
||||
enum rfkill_gpio_clk_state pwr_clk_enabled;
|
||||
struct clk *pwr_clk;
|
||||
const char *name;
|
||||
enum rfkill_type type;
|
||||
int reset_gpio;
|
||||
int shutdown_gpio;
|
||||
|
||||
struct rfkill *rfkill_dev;
|
||||
char *reset_name;
|
||||
char *shutdown_name;
|
||||
struct clk *clk;
|
||||
|
||||
bool clk_enabled;
|
||||
};
|
||||
|
||||
static int rfkill_gpio_set_power(void *data, bool blocked)
|
||||
@ -52,23 +48,22 @@ static int rfkill_gpio_set_power(void *data, bool blocked)
|
||||
struct rfkill_gpio_data *rfkill = data;
|
||||
|
||||
if (blocked) {
|
||||
if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
|
||||
gpio_direction_output(rfkill->pdata->shutdown_gpio, 0);
|
||||
if (gpio_is_valid(rfkill->pdata->reset_gpio))
|
||||
gpio_direction_output(rfkill->pdata->reset_gpio, 0);
|
||||
if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill))
|
||||
clk_disable(rfkill->pwr_clk);
|
||||
if (gpio_is_valid(rfkill->shutdown_gpio))
|
||||
gpio_set_value(rfkill->shutdown_gpio, 0);
|
||||
if (gpio_is_valid(rfkill->reset_gpio))
|
||||
gpio_set_value(rfkill->reset_gpio, 0);
|
||||
if (!IS_ERR(rfkill->clk) && rfkill->clk_enabled)
|
||||
clk_disable(rfkill->clk);
|
||||
} else {
|
||||
if (rfkill->pwr_clk && PWR_CLK_DISABLED(rfkill))
|
||||
clk_enable(rfkill->pwr_clk);
|
||||
if (gpio_is_valid(rfkill->pdata->reset_gpio))
|
||||
gpio_direction_output(rfkill->pdata->reset_gpio, 1);
|
||||
if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
|
||||
gpio_direction_output(rfkill->pdata->shutdown_gpio, 1);
|
||||
if (!IS_ERR(rfkill->clk) && !rfkill->clk_enabled)
|
||||
clk_enable(rfkill->clk);
|
||||
if (gpio_is_valid(rfkill->reset_gpio))
|
||||
gpio_set_value(rfkill->reset_gpio, 1);
|
||||
if (gpio_is_valid(rfkill->shutdown_gpio))
|
||||
gpio_set_value(rfkill->shutdown_gpio, 1);
|
||||
}
|
||||
|
||||
if (rfkill->pwr_clk)
|
||||
PWR_CLK_SET(rfkill, blocked);
|
||||
rfkill->clk_enabled = blocked;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -77,117 +72,112 @@ static const struct rfkill_ops rfkill_gpio_ops = {
|
||||
.set_block = rfkill_gpio_set_power,
|
||||
};
|
||||
|
||||
static int rfkill_gpio_acpi_probe(struct device *dev,
|
||||
struct rfkill_gpio_data *rfkill)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
|
||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
rfkill->name = dev_name(dev);
|
||||
rfkill->type = (unsigned)id->driver_data;
|
||||
rfkill->reset_gpio = acpi_get_gpio_by_index(dev, 0, NULL);
|
||||
rfkill->shutdown_gpio = acpi_get_gpio_by_index(dev, 1, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rfkill_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rfkill_gpio_data *rfkill;
|
||||
struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct rfkill_gpio_data *rfkill;
|
||||
const char *clk_name = NULL;
|
||||
int ret = 0;
|
||||
int len = 0;
|
||||
|
||||
if (!pdata) {
|
||||
pr_warn("%s: No platform data specified\n", __func__);
|
||||
return -EINVAL;
|
||||
rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL);
|
||||
if (!rfkill)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ACPI_HANDLE(&pdev->dev)) {
|
||||
ret = rfkill_gpio_acpi_probe(&pdev->dev, rfkill);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (pdata) {
|
||||
clk_name = pdata->power_clk_name;
|
||||
rfkill->name = pdata->name;
|
||||
rfkill->type = pdata->type;
|
||||
rfkill->reset_gpio = pdata->reset_gpio;
|
||||
rfkill->shutdown_gpio = pdata->shutdown_gpio;
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* make sure at-least one of the GPIO is defined and that
|
||||
* a name is specified for this instance */
|
||||
if (!pdata->name || (!gpio_is_valid(pdata->reset_gpio) &&
|
||||
!gpio_is_valid(pdata->shutdown_gpio))) {
|
||||
if ((!gpio_is_valid(rfkill->reset_gpio) &&
|
||||
!gpio_is_valid(rfkill->shutdown_gpio)) || !rfkill->name) {
|
||||
pr_warn("%s: invalid platform data\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL);
|
||||
if (!rfkill)
|
||||
return -ENOMEM;
|
||||
|
||||
if (pdata->gpio_runtime_setup) {
|
||||
if (pdata && pdata->gpio_runtime_setup) {
|
||||
ret = pdata->gpio_runtime_setup(pdev);
|
||||
if (ret) {
|
||||
pr_warn("%s: can't set up gpio\n", __func__);
|
||||
goto fail_alloc;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
rfkill->pdata = pdata;
|
||||
len = strlen(rfkill->name);
|
||||
rfkill->reset_name = devm_kzalloc(&pdev->dev, len + 7, GFP_KERNEL);
|
||||
if (!rfkill->reset_name)
|
||||
return -ENOMEM;
|
||||
|
||||
len = strlen(pdata->name);
|
||||
rfkill->reset_name = kzalloc(len + 7, GFP_KERNEL);
|
||||
if (!rfkill->reset_name) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_alloc;
|
||||
}
|
||||
rfkill->shutdown_name = devm_kzalloc(&pdev->dev, len + 10, GFP_KERNEL);
|
||||
if (!rfkill->shutdown_name)
|
||||
return -ENOMEM;
|
||||
|
||||
rfkill->shutdown_name = kzalloc(len + 10, GFP_KERNEL);
|
||||
if (!rfkill->shutdown_name) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_reset_name;
|
||||
}
|
||||
snprintf(rfkill->reset_name, len + 6 , "%s_reset", rfkill->name);
|
||||
snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", rfkill->name);
|
||||
|
||||
snprintf(rfkill->reset_name, len + 6 , "%s_reset", pdata->name);
|
||||
snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", pdata->name);
|
||||
rfkill->clk = devm_clk_get(&pdev->dev, clk_name);
|
||||
|
||||
if (pdata->power_clk_name) {
|
||||
rfkill->pwr_clk = clk_get(&pdev->dev, pdata->power_clk_name);
|
||||
if (IS_ERR(rfkill->pwr_clk)) {
|
||||
pr_warn("%s: can't find pwr_clk.\n", __func__);
|
||||
ret = PTR_ERR(rfkill->pwr_clk);
|
||||
goto fail_shutdown_name;
|
||||
}
|
||||
}
|
||||
|
||||
if (gpio_is_valid(pdata->reset_gpio)) {
|
||||
ret = gpio_request(pdata->reset_gpio, rfkill->reset_name);
|
||||
if (gpio_is_valid(rfkill->reset_gpio)) {
|
||||
ret = devm_gpio_request_one(&pdev->dev, rfkill->reset_gpio,
|
||||
0, rfkill->reset_name);
|
||||
if (ret) {
|
||||
pr_warn("%s: failed to get reset gpio.\n", __func__);
|
||||
goto fail_clock;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (gpio_is_valid(pdata->shutdown_gpio)) {
|
||||
ret = gpio_request(pdata->shutdown_gpio, rfkill->shutdown_name);
|
||||
if (gpio_is_valid(rfkill->shutdown_gpio)) {
|
||||
ret = devm_gpio_request_one(&pdev->dev, rfkill->shutdown_gpio,
|
||||
0, rfkill->shutdown_name);
|
||||
if (ret) {
|
||||
pr_warn("%s: failed to get shutdown gpio.\n", __func__);
|
||||
goto fail_reset;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type,
|
||||
&rfkill_gpio_ops, rfkill);
|
||||
if (!rfkill->rfkill_dev) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_shutdown;
|
||||
}
|
||||
rfkill->rfkill_dev = rfkill_alloc(rfkill->name, &pdev->dev,
|
||||
rfkill->type, &rfkill_gpio_ops,
|
||||
rfkill);
|
||||
if (!rfkill->rfkill_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = rfkill_register(rfkill->rfkill_dev);
|
||||
if (ret < 0)
|
||||
goto fail_rfkill;
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, rfkill);
|
||||
|
||||
dev_info(&pdev->dev, "%s device registered.\n", pdata->name);
|
||||
dev_info(&pdev->dev, "%s device registered.\n", rfkill->name);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_rfkill:
|
||||
rfkill_destroy(rfkill->rfkill_dev);
|
||||
fail_shutdown:
|
||||
if (gpio_is_valid(pdata->shutdown_gpio))
|
||||
gpio_free(pdata->shutdown_gpio);
|
||||
fail_reset:
|
||||
if (gpio_is_valid(pdata->reset_gpio))
|
||||
gpio_free(pdata->reset_gpio);
|
||||
fail_clock:
|
||||
if (rfkill->pwr_clk)
|
||||
clk_put(rfkill->pwr_clk);
|
||||
fail_shutdown_name:
|
||||
kfree(rfkill->shutdown_name);
|
||||
fail_reset_name:
|
||||
kfree(rfkill->reset_name);
|
||||
fail_alloc:
|
||||
kfree(rfkill);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rfkill_gpio_remove(struct platform_device *pdev)
|
||||
@ -195,31 +185,26 @@ static int rfkill_gpio_remove(struct platform_device *pdev)
|
||||
struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev);
|
||||
struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
if (pdata->gpio_runtime_close)
|
||||
if (pdata && pdata->gpio_runtime_close)
|
||||
pdata->gpio_runtime_close(pdev);
|
||||
rfkill_unregister(rfkill->rfkill_dev);
|
||||
rfkill_destroy(rfkill->rfkill_dev);
|
||||
if (gpio_is_valid(rfkill->pdata->shutdown_gpio))
|
||||
gpio_free(rfkill->pdata->shutdown_gpio);
|
||||
if (gpio_is_valid(rfkill->pdata->reset_gpio))
|
||||
gpio_free(rfkill->pdata->reset_gpio);
|
||||
if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill))
|
||||
clk_disable(rfkill->pwr_clk);
|
||||
if (rfkill->pwr_clk)
|
||||
clk_put(rfkill->pwr_clk);
|
||||
kfree(rfkill->shutdown_name);
|
||||
kfree(rfkill->reset_name);
|
||||
kfree(rfkill);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id rfkill_acpi_match[] = {
|
||||
{ "BCM4752", RFKILL_TYPE_GPS },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver rfkill_gpio_driver = {
|
||||
.probe = rfkill_gpio_probe,
|
||||
.remove = rfkill_gpio_remove,
|
||||
.driver = {
|
||||
.name = "rfkill_gpio",
|
||||
.owner = THIS_MODULE,
|
||||
.name = "rfkill_gpio",
|
||||
.owner = THIS_MODULE,
|
||||
.acpi_match_table = ACPI_PTR(rfkill_acpi_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -504,7 +504,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
if (wdev->current_bss) {
|
||||
*chan = wdev->current_bss->pub.channel;
|
||||
*chanmode = wdev->ibss_fixed
|
||||
*chanmode = (wdev->ibss_fixed &&
|
||||
!wdev->ibss_dfs_possible)
|
||||
? CHAN_MODE_SHARED
|
||||
: CHAN_MODE_EXCLUSIVE;
|
||||
return;
|
||||
|
@ -83,6 +83,8 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
|
||||
struct cfg80211_cached_keys *connkeys)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct ieee80211_channel *check_chan;
|
||||
u8 radar_detect_width = 0;
|
||||
int err;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
@ -114,14 +116,28 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
|
||||
wdev->connect_keys = connkeys;
|
||||
|
||||
wdev->ibss_fixed = params->channel_fixed;
|
||||
wdev->ibss_dfs_possible = params->userspace_handles_dfs;
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
wdev->wext.ibss.chandef = params->chandef;
|
||||
#endif
|
||||
check_chan = params->chandef.chan;
|
||||
if (params->userspace_handles_dfs) {
|
||||
/* use channel NULL to check for radar even if the current
|
||||
* channel is not a radar channel - it might decide to change
|
||||
* to DFS channel later.
|
||||
*/
|
||||
radar_detect_width = BIT(params->chandef.width);
|
||||
check_chan = NULL;
|
||||
}
|
||||
|
||||
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
|
||||
check_chan,
|
||||
(params->channel_fixed &&
|
||||
!radar_detect_width)
|
||||
? CHAN_MODE_SHARED
|
||||
: CHAN_MODE_EXCLUSIVE,
|
||||
radar_detect_width);
|
||||
|
||||
err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
|
||||
params->channel_fixed
|
||||
? CHAN_MODE_SHARED
|
||||
: CHAN_MODE_EXCLUSIVE);
|
||||
if (err) {
|
||||
wdev->connect_keys = NULL;
|
||||
return err;
|
||||
|
@ -354,6 +354,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
|
||||
[NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
|
||||
[NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
|
||||
[NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
|
||||
[NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
|
||||
[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
|
||||
[NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
@ -3896,9 +3899,45 @@ static int nl80211_parse_sta_wme(struct genl_info *info,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_parse_sta_channel_info(struct genl_info *info,
|
||||
struct station_parameters *params)
|
||||
{
|
||||
if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) {
|
||||
params->supported_channels =
|
||||
nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
|
||||
params->supported_channels_len =
|
||||
nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
|
||||
/*
|
||||
* Need to include at least one (first channel, number of
|
||||
* channels) tuple for each subband, and must have proper
|
||||
* tuples for the rest of the data as well.
|
||||
*/
|
||||
if (params->supported_channels_len < 2)
|
||||
return -EINVAL;
|
||||
if (params->supported_channels_len % 2)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) {
|
||||
params->supported_oper_classes =
|
||||
nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
|
||||
params->supported_oper_classes_len =
|
||||
nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
|
||||
/*
|
||||
* The value of the Length field of the Supported Operating
|
||||
* Classes element is between 2 and 253.
|
||||
*/
|
||||
if (params->supported_oper_classes_len < 2 ||
|
||||
params->supported_oper_classes_len > 253)
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_set_station_tdls(struct genl_info *info,
|
||||
struct station_parameters *params)
|
||||
{
|
||||
int err;
|
||||
/* Dummy STA entry gets updated once the peer capabilities are known */
|
||||
if (info->attrs[NL80211_ATTR_PEER_AID])
|
||||
params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
|
||||
@ -3909,6 +3948,10 @@ static int nl80211_set_station_tdls(struct genl_info *info,
|
||||
params->vht_capa =
|
||||
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
|
||||
|
||||
err = nl80211_parse_sta_channel_info(info, params);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return nl80211_parse_sta_wme(info, params);
|
||||
}
|
||||
|
||||
@ -4089,6 +4132,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = nl80211_parse_sta_channel_info(info, ¶ms);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = nl80211_parse_sta_wme(info, ¶ms);
|
||||
if (err)
|
||||
return err;
|
||||
@ -5653,6 +5700,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
@ -5665,9 +5713,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
return -EINVAL;
|
||||
|
||||
/* only important for AP, IBSS and mesh create IEs internally */
|
||||
if (need_new_beacon &&
|
||||
(!info->attrs[NL80211_ATTR_CSA_IES] ||
|
||||
!info->attrs[NL80211_ATTR_CSA_C_OFF_BEACON]))
|
||||
if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
|
||||
return -EINVAL;
|
||||
|
||||
params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
|
||||
@ -5722,9 +5768,9 @@ skip_beacons:
|
||||
if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef))
|
||||
return -EINVAL;
|
||||
|
||||
/* DFS channels are only supported for AP/P2P GO ... for now. */
|
||||
if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
|
||||
dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
|
||||
dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO ||
|
||||
dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC) {
|
||||
err = cfg80211_chandef_dfs_required(wdev->wiphy,
|
||||
¶ms.chandef);
|
||||
if (err < 0) {
|
||||
@ -6556,6 +6602,9 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
|
||||
ibss.control_port =
|
||||
nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
|
||||
|
||||
ibss.userspace_handles_dfs =
|
||||
nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
|
||||
|
||||
err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
|
||||
if (err)
|
||||
kfree(connkeys);
|
||||
@ -10762,7 +10811,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
|
||||
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
|
||||
wdev->iftype != NL80211_IFTYPE_P2P_GO &&
|
||||
wdev->iftype != NL80211_IFTYPE_ADHOC))
|
||||
wdev->iftype != NL80211_IFTYPE_ADHOC &&
|
||||
wdev->iftype != NL80211_IFTYPE_MESH_POINT))
|
||||
goto out;
|
||||
|
||||
wdev->channel = chandef->chan;
|
||||
|
@ -787,7 +787,6 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
|
||||
EXPORT_SYMBOL(reg_initiator_name);
|
||||
|
||||
#ifdef CONFIG_CFG80211_REG_DEBUG
|
||||
|
||||
static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
|
||||
const struct ieee80211_reg_rule *reg_rule)
|
||||
{
|
||||
@ -974,6 +973,13 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy)
|
||||
{
|
||||
if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY &&
|
||||
!(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ignore_reg_update(struct wiphy *wiphy,
|
||||
enum nl80211_reg_initiator initiator)
|
||||
@ -1000,7 +1006,7 @@ static bool ignore_reg_update(struct wiphy *wiphy,
|
||||
* wiphy->regd will be set once the device has its own
|
||||
* desired regulatory domain set
|
||||
*/
|
||||
if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd &&
|
||||
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 "
|
||||
@ -1706,8 +1712,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
|
||||
}
|
||||
EXPORT_SYMBOL(regulatory_hint);
|
||||
|
||||
void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band,
|
||||
const u8 *country_ie, u8 country_ie_len)
|
||||
void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band,
|
||||
const u8 *country_ie, u8 country_ie_len)
|
||||
{
|
||||
char alpha2[2];
|
||||
enum environment_cap env = ENVIRON_ANY;
|
||||
|
@ -58,7 +58,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
|
||||
gfp_t gfp);
|
||||
|
||||
/**
|
||||
* regulatory_hint_11d - hints a country IE as a regulatory domain
|
||||
* regulatory_hint_country_ie - hints a country IE as a regulatory domain
|
||||
* @wiphy: the wireless device giving the hint (used only for reporting
|
||||
* conflicts)
|
||||
* @band: the band on which the country IE was received on. This determines
|
||||
@ -78,7 +78,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
|
||||
* not observed. For this reason if a triplet is seen with channel
|
||||
* information for a band the BSS is not present in it will be ignored.
|
||||
*/
|
||||
void regulatory_hint_11d(struct wiphy *wiphy,
|
||||
void regulatory_hint_country_ie(struct wiphy *wiphy,
|
||||
enum ieee80211_band band,
|
||||
const u8 *country_ie,
|
||||
u8 country_ie_len);
|
||||
|
@ -682,8 +682,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
|
||||
* - country_ie + 2, the start of the country ie data, and
|
||||
* - and country_ie[1] which is the IE length
|
||||
*/
|
||||
regulatory_hint_11d(wdev->wiphy, bss->channel->band,
|
||||
country_ie + 2, country_ie[1]);
|
||||
regulatory_hint_country_ie(wdev->wiphy, bss->channel->band,
|
||||
country_ie + 2, country_ie[1]);
|
||||
kfree(country_ie);
|
||||
}
|
||||
|
||||
|
@ -1249,7 +1249,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
|
||||
enum cfg80211_chan_mode chmode;
|
||||
int num_different_channels = 0;
|
||||
int total = 1;
|
||||
bool radar_required;
|
||||
bool radar_required = false;
|
||||
int i, j;
|
||||
|
||||
ASSERT_RTNL();
|
||||
@ -1264,14 +1264,20 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
case NL80211_IFTYPE_WDS:
|
||||
radar_required = !!(chan &&
|
||||
(chan->flags & IEEE80211_CHAN_RADAR));
|
||||
/* if the interface could potentially choose a DFS channel,
|
||||
* then mark DFS as required.
|
||||
*/
|
||||
if (!chan) {
|
||||
if (chanmode != CHAN_MODE_UNDEFINED && radar_detect)
|
||||
radar_required = true;
|
||||
break;
|
||||
}
|
||||
radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR);
|
||||
break;
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
case NL80211_IFTYPE_STATION:
|
||||
case NL80211_IFTYPE_P2P_DEVICE:
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
radar_required = false;
|
||||
break;
|
||||
case NUM_NL80211_IFTYPES:
|
||||
case NL80211_IFTYPE_UNSPECIFIED:
|
||||
|
Loading…
Reference in New Issue
Block a user