summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2013-11-05 15:49:02 -0500
committerJohn W. Linville <linville@tuxdriver.com>2013-11-05 15:49:02 -0500
commit353c78152c10027b8da5de446bad3472f977fcdc (patch)
tree8663ca32b912e8b3df428f570b7fd376aa3aa0cb /net
parent01925efdf7e03b4b803b5c9f985163d687f7f017 (diff)
parentd0a361a5b3f5aa28778a0c336de5a911fc0cd678 (diff)
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Conflicts: net/wireless/reg.c
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/Kconfig12
-rw-r--r--net/mac80211/aes_ccm.c169
-rw-r--r--net/mac80211/aes_ccm.h14
-rw-r--r--net/mac80211/cfg.c142
-rw-r--r--net/mac80211/debug.h10
-rw-r--r--net/mac80211/debugfs_netdev.c25
-rw-r--r--net/mac80211/ht.c41
-rw-r--r--net/mac80211/ibss.c116
-rw-r--r--net/mac80211/ieee80211_i.h50
-rw-r--r--net/mac80211/iface.c3
-rw-r--r--net/mac80211/key.h2
-rw-r--r--net/mac80211/mesh.c302
-rw-r--r--net/mac80211/mesh_plink.c3
-rw-r--r--net/mac80211/mesh_ps.c33
-rw-r--r--net/mac80211/mlme.c32
-rw-r--r--net/mac80211/rx.c5
-rw-r--r--net/mac80211/spectmgmt.c33
-rw-r--r--net/mac80211/sta_info.c72
-rw-r--r--net/mac80211/sta_info.h5
-rw-r--r--net/mac80211/status.c31
-rw-r--r--net/mac80211/tx.c45
-rw-r--r--net/mac80211/util.c124
-rw-r--r--net/mac80211/wpa.c44
-rw-r--r--net/rfkill/Kconfig2
-rw-r--r--net/rfkill/rfkill-gpio.c211
-rw-r--r--net/wireless/chan.c3
-rw-r--r--net/wireless/ibss.c24
-rw-r--r--net/wireless/nl80211.c62
-rw-r--r--net/wireless/reg.c14
-rw-r--r--net/wireless/reg.h4
-rw-r--r--net/wireless/sme.c4
-rw-r--r--net/wireless/util.c14
32 files changed, 1205 insertions, 446 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 62535fe9f570..97b5dcad5025 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -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
diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c
index be7614b9ed27..7c7df475a401 100644
--- a/net/mac80211/aes_ccm.c
+++ b/net/mac80211/aes_ccm.c
@@ -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;
-
- b_0 = scratch + 3 * AES_BLOCK_SIZE;
- aad = scratch + 4 * AES_BLOCK_SIZE;
- b = scratch;
- s_0 = scratch + AES_BLOCK_SIZE;
-
- crypto_cipher_encrypt_one(tfm, b, b_0);
+ struct scatterlist assoc, pt, ct[2];
+ struct {
+ struct aead_request req;
+ u8 priv[crypto_aead_reqsize(tfm)];
+ } aead_req;
- /* 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);
+ memset(&aead_req, 0, sizeof(aead_req));
- aad += AES_BLOCK_SIZE;
+ 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);
- for (i = 0; i < AES_BLOCK_SIZE; i++)
- aad[i] ^= b[i];
- crypto_cipher_encrypt_one(tfm, a, 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);
- /* 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;
-
- b = scratch;
- s_0 = scratch + AES_BLOCK_SIZE;
- e = scratch + 2 * AES_BLOCK_SIZE;
- b_0 = scratch + 3 * AES_BLOCK_SIZE;
-
- num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
- last_len = data_len % AES_BLOCK_SIZE;
- aes_ccm_prepare(tfm, scratch, b);
-
- /* 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;
-
- /* 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];
+ struct scatterlist assoc, pt, ct[2];
+ struct {
+ struct aead_request req;
+ u8 priv[crypto_aead_reqsize(tfm)];
+ } aead_req;
+
+ memset(&aead_req, 0, sizeof(aead_req));
+
+ 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);
+
+ 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);
+
+ 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;
-
- b = scratch;
- s_0 = scratch + AES_BLOCK_SIZE;
- a = scratch + 2 * AES_BLOCK_SIZE;
- b_0 = scratch + 3 * AES_BLOCK_SIZE;
-
- num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
- last_len = data_len % AES_BLOCK_SIZE;
- aes_ccm_prepare(tfm, scratch, a);
-
- /* 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;
-}
+ struct crypto_aead *tfm;
+ int err;
+ tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm))
+ return tfm;
-struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[])
-{
- struct crypto_cipher *tfm;
+ 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;
- tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
- if (!IS_ERR(tfm))
- crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
-
- return tfm;
+ crypto_free_aead(tfm);
+ return ERR_PTR(err);
}
-
-void ieee80211_aes_key_free(struct crypto_cipher *tfm)
+void ieee80211_aes_key_free(struct crypto_aead *tfm)
{
- crypto_free_cipher(tfm);
+ crypto_free_aead(tfm);
}
diff --git a/net/mac80211/aes_ccm.h b/net/mac80211/aes_ccm.h
index 5b7d744e2370..2c7ab1948a2e 100644
--- a/net/mac80211/aes_ccm.h
+++ b/net/mac80211/aes_ccm.h
@@ -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 */
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index b0a651cc389f..95667b088c5b 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -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;
}
diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h
index 4ccc5ed6237d..493d68061f0c 100644
--- a/net/mac80211/debug.h
+++ b/net/mac80211/debug.h
@@ -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__)
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index cafe614ef93d..04b5a14c8a05 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -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);
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 529bf58bc145..9a8be8f69224 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -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);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 21a0b8835cb3..531be040b9ae 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -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(&params, 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,
- &params.count, &mode,
- &params.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, &params.chandef,
- IEEE80211_CHAN_DISABLED)) {
+ if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, &params.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, &params);
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, &params.chandef);