diff options
author | John W. Linville <linville@tuxdriver.com> | 2013-11-05 15:49:02 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-11-05 15:49:02 -0500 |
commit | 353c78152c10027b8da5de446bad3472f977fcdc (patch) | |
tree | 8663ca32b912e8b3df428f570b7fd376aa3aa0cb /net/mac80211 | |
parent | 01925efdf7e03b4b803b5c9f985163d687f7f017 (diff) | |
parent | d0a361a5b3f5aa28778a0c336de5a911fc0cd678 (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/mac80211')
-rw-r--r-- | net/mac80211/Kconfig | 12 | ||||
-rw-r--r-- | net/mac80211/aes_ccm.c | 169 | ||||
-rw-r--r-- | net/mac80211/aes_ccm.h | 14 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 142 | ||||
-rw-r--r-- | net/mac80211/debug.h | 10 | ||||
-rw-r--r-- | net/mac80211/debugfs_netdev.c | 25 | ||||
-rw-r--r-- | net/mac80211/ht.c | 41 | ||||
-rw-r--r-- | net/mac80211/ibss.c | 116 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 50 | ||||
-rw-r--r-- | net/mac80211/iface.c | 3 | ||||
-rw-r--r-- | net/mac80211/key.h | 2 | ||||
-rw-r--r-- | net/mac80211/mesh.c | 302 | ||||
-rw-r--r-- | net/mac80211/mesh_plink.c | 3 | ||||
-rw-r--r-- | net/mac80211/mesh_ps.c | 33 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 32 | ||||
-rw-r--r-- | net/mac80211/rx.c | 5 | ||||
-rw-r--r-- | net/mac80211/spectmgmt.c | 33 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 72 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 5 | ||||
-rw-r--r-- | net/mac80211/status.c | 31 | ||||
-rw-r--r-- | net/mac80211/tx.c | 45 | ||||
-rw-r--r-- | net/mac80211/util.c | 124 | ||||
-rw-r--r-- | net/mac80211/wpa.c | 44 |
23 files changed, 1004 insertions, 309 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 | |||
4 | select CRYPTO | 4 | select CRYPTO |
5 | select CRYPTO_ARC4 | 5 | select CRYPTO_ARC4 |
6 | select CRYPTO_AES | 6 | select CRYPTO_AES |
7 | select CRYPTO_CCM | ||
7 | select CRC32 | 8 | select CRC32 |
8 | select AVERAGE | 9 | select AVERAGE |
9 | ---help--- | 10 | ---help--- |
@@ -258,6 +259,17 @@ config MAC80211_MESH_SYNC_DEBUG | |||
258 | 259 | ||
259 | Do not select this option. | 260 | Do not select this option. |
260 | 261 | ||
262 | config MAC80211_MESH_CSA_DEBUG | ||
263 | bool "Verbose mesh channel switch debugging" | ||
264 | depends on MAC80211_DEBUG_MENU | ||
265 | depends on MAC80211_MESH | ||
266 | ---help--- | ||
267 | Selecting this option causes mac80211 to print out very verbose mesh | ||
268 | channel switch debugging messages (when mac80211 is taking part in a | ||
269 | mesh network). | ||
270 | |||
271 | Do not select this option. | ||
272 | |||
261 | config MAC80211_MESH_PS_DEBUG | 273 | config MAC80211_MESH_PS_DEBUG |
262 | bool "Verbose mesh powersave debugging" | 274 | bool "Verbose mesh powersave debugging" |
263 | depends on MAC80211_DEBUG_MENU | 275 | 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 @@ | |||
2 | * Copyright 2003-2004, Instant802 Networks, Inc. | 2 | * Copyright 2003-2004, Instant802 Networks, Inc. |
3 | * Copyright 2005-2006, Devicescape Software, Inc. | 3 | * Copyright 2005-2006, Devicescape Software, Inc. |
4 | * | 4 | * |
5 | * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org> | ||
6 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
7 | * published by the Free Software Foundation. | 9 | * published by the Free Software Foundation. |
@@ -17,134 +19,75 @@ | |||
17 | #include "key.h" | 19 | #include "key.h" |
18 | #include "aes_ccm.h" | 20 | #include "aes_ccm.h" |
19 | 21 | ||
20 | static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a) | 22 | void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, |
23 | u8 *data, size_t data_len, u8 *mic) | ||
21 | { | 24 | { |
22 | int i; | 25 | struct scatterlist assoc, pt, ct[2]; |
23 | u8 *b_0, *aad, *b, *s_0; | 26 | struct { |
24 | 27 | struct aead_request req; | |
25 | b_0 = scratch + 3 * AES_BLOCK_SIZE; | 28 | u8 priv[crypto_aead_reqsize(tfm)]; |
26 | aad = scratch + 4 * AES_BLOCK_SIZE; | 29 | } aead_req; |
27 | b = scratch; | ||
28 | s_0 = scratch + AES_BLOCK_SIZE; | ||
29 | |||
30 | crypto_cipher_encrypt_one(tfm, b, b_0); | ||
31 | 30 | ||
32 | /* Extra Authenticate-only data (always two AES blocks) */ | 31 | memset(&aead_req, 0, sizeof(aead_req)); |
33 | for (i = 0; i < AES_BLOCK_SIZE; i++) | ||
34 | aad[i] ^= b[i]; | ||
35 | crypto_cipher_encrypt_one(tfm, b, aad); | ||
36 | 32 | ||
37 | aad += AES_BLOCK_SIZE; | 33 | sg_init_one(&pt, data, data_len); |
34 | sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); | ||
35 | sg_init_table(ct, 2); | ||
36 | sg_set_buf(&ct[0], data, data_len); | ||
37 | sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN); | ||
38 | 38 | ||
39 | for (i = 0; i < AES_BLOCK_SIZE; i++) | 39 | aead_request_set_tfm(&aead_req.req, tfm); |
40 | aad[i] ^= b[i]; | 40 | aead_request_set_assoc(&aead_req.req, &assoc, assoc.length); |
41 | crypto_cipher_encrypt_one(tfm, a, aad); | 41 | aead_request_set_crypt(&aead_req.req, &pt, ct, data_len, b_0); |
42 | 42 | ||
43 | /* Mask out bits from auth-only-b_0 */ | 43 | crypto_aead_encrypt(&aead_req.req); |
44 | b_0[0] &= 0x07; | ||
45 | |||
46 | /* S_0 is used to encrypt T (= MIC) */ | ||
47 | b_0[14] = 0; | ||
48 | b_0[15] = 0; | ||
49 | crypto_cipher_encrypt_one(tfm, s_0, b_0); | ||
50 | } | 44 | } |
51 | 45 | ||
52 | 46 | int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, | |
53 | void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, | 47 | u8 *data, size_t data_len, u8 *mic) |
54 | u8 *data, size_t data_len, | ||
55 | u8 *cdata, u8 *mic) | ||
56 | { | 48 | { |
57 | int i, j, last_len, num_blocks; | 49 | struct scatterlist assoc, pt, ct[2]; |
58 | u8 *pos, *cpos, *b, *s_0, *e, *b_0; | 50 | struct { |
59 | 51 | struct aead_request req; | |
60 | b = scratch; | 52 | u8 priv[crypto_aead_reqsize(tfm)]; |
61 | s_0 = scratch + AES_BLOCK_SIZE; | 53 | } aead_req; |
62 | e = scratch + 2 * AES_BLOCK_SIZE; | 54 | |
63 | b_0 = scratch + 3 * AES_BLOCK_SIZE; | 55 | memset(&aead_req, 0, sizeof(aead_req)); |
64 | 56 | ||
65 | num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); | 57 | sg_init_one(&pt, data, data_len); |
66 | last_len = data_len % AES_BLOCK_SIZE; | 58 | sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); |
67 | aes_ccm_prepare(tfm, scratch, b); | 59 | sg_init_table(ct, 2); |
68 | 60 | sg_set_buf(&ct[0], data, data_len); | |
69 | /* Process payload blocks */ | 61 | sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN); |
70 | pos = data; | 62 | |
71 | cpos = cdata; | 63 | aead_request_set_tfm(&aead_req.req, tfm); |
72 | for (j = 1; j <= num_blocks; j++) { | 64 | aead_request_set_assoc(&aead_req.req, &assoc, assoc.length); |
73 | int blen = (j == num_blocks && last_len) ? | 65 | aead_request_set_crypt(&aead_req.req, ct, &pt, |
74 | last_len : AES_BLOCK_SIZE; | 66 | data_len + IEEE80211_CCMP_MIC_LEN, b_0); |
75 | 67 | ||
76 | /* Authentication followed by encryption */ | 68 | return crypto_aead_decrypt(&aead_req.req); |
77 | for (i = 0; i < blen; i++) | ||
78 | b[i] ^= pos[i]; | ||
79 | crypto_cipher_encrypt_one(tfm, b, b); | ||
80 | |||
81 | b_0[14] = (j >> 8) & 0xff; | ||
82 | b_0[15] = j & 0xff; | ||
83 | crypto_cipher_encrypt_one(tfm, e, b_0); | ||
84 | for (i = 0; i < blen; i++) | ||
85 | *cpos++ = *pos++ ^ e[i]; | ||
86 | } | ||
87 | |||
88 | for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) | ||
89 | mic[i] = b[i] ^ s_0[i]; | ||
90 | } | 69 | } |
91 | 70 | ||
92 | 71 | struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]) | |
93 | int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, | ||
94 | u8 *cdata, size_t data_len, u8 *mic, u8 *data) | ||
95 | { | 72 | { |
96 | int i, j, last_len, num_blocks; | 73 | struct crypto_aead *tfm; |
97 | u8 *pos, *cpos, *b, *s_0, *a, *b_0; | 74 | int err; |
98 | |||
99 | b = scratch; | ||
100 | s_0 = scratch + AES_BLOCK_SIZE; | ||
101 | a = scratch + 2 * AES_BLOCK_SIZE; | ||
102 | b_0 = scratch + 3 * AES_BLOCK_SIZE; | ||
103 | |||
104 | num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); | ||
105 | last_len = data_len % AES_BLOCK_SIZE; | ||
106 | aes_ccm_prepare(tfm, scratch, a); | ||
107 | |||
108 | /* Process payload blocks */ | ||
109 | cpos = cdata; | ||
110 | pos = data; | ||
111 | for (j = 1; j <= num_blocks; j++) { | ||
112 | int blen = (j == num_blocks && last_len) ? | ||
113 | last_len : AES_BLOCK_SIZE; | ||
114 | |||
115 | /* Decryption followed by authentication */ | ||
116 | b_0[14] = (j >> 8) & 0xff; | ||
117 | b_0[15] = j & 0xff; | ||
118 | crypto_cipher_encrypt_one(tfm, b, b_0); | ||
119 | for (i = 0; i < blen; i++) { | ||
120 | *pos = *cpos++ ^ b[i]; | ||
121 | a[i] ^= *pos++; | ||
122 | } | ||
123 | crypto_cipher_encrypt_one(tfm, a, a); | ||
124 | } | ||
125 | |||
126 | for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) { | ||
127 | if ((mic[i] ^ s_0[i]) != a[i]) | ||
128 | return -1; | ||
129 | } | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | 75 | ||
76 | tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC); | ||
77 | if (IS_ERR(tfm)) | ||
78 | return tfm; | ||
134 | 79 | ||
135 | struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]) | 80 | err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP); |
136 | { | 81 | if (!err) |
137 | struct crypto_cipher *tfm; | 82 | err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN); |
83 | if (!err) | ||
84 | return tfm; | ||
138 | 85 | ||
139 | tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); | 86 | crypto_free_aead(tfm); |
140 | if (!IS_ERR(tfm)) | 87 | return ERR_PTR(err); |
141 | crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP); | ||
142 | |||
143 | return tfm; | ||
144 | } | 88 | } |
145 | 89 | ||
146 | 90 | void ieee80211_aes_key_free(struct crypto_aead *tfm) | |
147 | void ieee80211_aes_key_free(struct crypto_cipher *tfm) | ||
148 | { | 91 | { |
149 | crypto_free_cipher(tfm); | 92 | crypto_free_aead(tfm); |
150 | } | 93 | } |
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 @@ | |||
12 | 12 | ||
13 | #include <linux/crypto.h> | 13 | #include <linux/crypto.h> |
14 | 14 | ||
15 | struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]); | 15 | struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]); |
16 | void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, | 16 | void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, |
17 | u8 *data, size_t data_len, | 17 | u8 *data, size_t data_len, u8 *mic); |
18 | u8 *cdata, u8 *mic); | 18 | int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, |
19 | int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, | 19 | u8 *data, size_t data_len, u8 *mic); |
20 | u8 *cdata, size_t data_len, | 20 | void ieee80211_aes_key_free(struct crypto_aead *tfm); |
21 | u8 *mic, u8 *data); | ||
22 | void ieee80211_aes_key_free(struct crypto_cipher *tfm); | ||
23 | 21 | ||
24 | #endif /* AES_CCM_H */ | 22 | #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) | |||
1059 | /* abort any running channel switch */ | 1059 | /* abort any running channel switch */ |
1060 | sdata->vif.csa_active = false; | 1060 | sdata->vif.csa_active = false; |
1061 | cancel_work_sync(&sdata->csa_finalize_work); | 1061 | cancel_work_sync(&sdata->csa_finalize_work); |
1062 | cancel_work_sync(&sdata->u.ap.request_smps_work); | ||
1062 | 1063 | ||
1063 | /* turn off carrier for this interface and dependent VLANs */ | 1064 | /* turn off carrier for this interface and dependent VLANs */ |
1064 | list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) | 1065 | 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, | |||
1342 | sta->plink_state = params->plink_state; | 1343 | sta->plink_state = params->plink_state; |
1343 | 1344 | ||
1344 | ieee80211_mps_sta_status_update(sta); | 1345 | ieee80211_mps_sta_status_update(sta); |
1345 | changed |= | 1346 | changed |= ieee80211_mps_set_sta_local_pm(sta, |
1346 | ieee80211_mps_local_status_update(sdata); | 1347 | NL80211_MESH_POWER_UNKNOWN); |
1347 | break; | 1348 | break; |
1348 | default: | 1349 | default: |
1349 | /* nothing */ | 1350 | /* nothing */ |
@@ -1553,6 +1554,20 @@ static int ieee80211_change_station(struct wiphy *wiphy, | |||
1553 | 1554 | ||
1554 | mutex_unlock(&local->sta_mtx); | 1555 | mutex_unlock(&local->sta_mtx); |
1555 | 1556 | ||
1557 | if ((sdata->vif.type == NL80211_IFTYPE_AP || | ||
1558 | sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && | ||
1559 | sta->known_smps_mode != sta->sdata->bss->req_smps && | ||
1560 | test_sta_flag(sta, WLAN_STA_AUTHORIZED) && | ||
1561 | sta_info_tx_streams(sta) != 1) { | ||
1562 | ht_dbg(sta->sdata, | ||
1563 | "%pM just authorized and MIMO capable - update SMPS\n", | ||
1564 | sta->sta.addr); | ||
1565 | ieee80211_send_smps_action(sta->sdata, | ||
1566 | sta->sdata->bss->req_smps, | ||
1567 | sta->sta.addr, | ||
1568 | sta->sdata->vif.bss_conf.bssid); | ||
1569 | } | ||
1570 | |||
1556 | if (sdata->vif.type == NL80211_IFTYPE_STATION && | 1571 | if (sdata->vif.type == NL80211_IFTYPE_STATION && |
1557 | params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { | 1572 | params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { |
1558 | ieee80211_recalc_ps(local, -1); | 1573 | ieee80211_recalc_ps(local, -1); |
@@ -2337,8 +2352,92 @@ static int ieee80211_testmode_dump(struct wiphy *wiphy, | |||
2337 | } | 2352 | } |
2338 | #endif | 2353 | #endif |
2339 | 2354 | ||
2340 | int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | 2355 | int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, |
2341 | enum ieee80211_smps_mode smps_mode) | 2356 | enum ieee80211_smps_mode smps_mode) |
2357 | { | ||
2358 | struct sta_info *sta; | ||
2359 | enum ieee80211_smps_mode old_req; | ||
2360 | int i; | ||
2361 | |||
2362 | if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP)) | ||
2363 | return -EINVAL; | ||
2364 | |||
2365 | if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) | ||
2366 | return 0; | ||
2367 | |||
2368 | old_req = sdata->u.ap.req_smps; | ||
2369 | sdata->u.ap.req_smps = smps_mode; | ||
2370 | |||
2371 | /* AUTOMATIC doesn't mean much for AP - don't allow it */ | ||
2372 | if (old_req == smps_mode || | ||
2373 | smps_mode == IEEE80211_SMPS_AUTOMATIC) | ||
2374 | return 0; | ||
2375 | |||
2376 | /* If no associated stations, there's no need to do anything */ | ||
2377 | if (!atomic_read(&sdata->u.ap.num_mcast_sta)) { | ||
2378 | sdata->smps_mode = smps_mode; | ||
2379 | ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps); | ||
2380 | return 0; | ||
2381 | } | ||
2382 | |||
2383 | ht_dbg(sdata, | ||
2384 | "SMSP %d requested in AP mode, sending Action frame to %d stations\n", | ||
2385 | smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta)); | ||
2386 | |||
2387 | mutex_lock(&sdata->local->sta_mtx); | ||
2388 | for (i = 0; i < STA_HASH_SIZE; i++) { | ||
2389 | for (sta = rcu_dereference_protected(sdata->local->sta_hash[i], | ||
2390 | lockdep_is_held(&sdata->local->sta_mtx)); | ||
2391 | sta; | ||
2392 | sta = rcu_dereference_protected(sta->hnext, | ||
2393 | lockdep_is_held(&sdata->local->sta_mtx))) { | ||
2394 | /* | ||
2395 | * Only stations associated to our AP and | ||
2396 | * associated VLANs | ||
2397 | */ | ||
2398 | if (sta->sdata->bss != &sdata->u.ap) | ||
2399 | continue; | ||
2400 | |||
2401 | /* This station doesn't support MIMO - skip it */ | ||
2402 | if (sta_info_tx_streams(sta) == 1) | ||
2403 | continue; | ||
2404 | |||
2405 | /* | ||
2406 | * Don't wake up a STA just to send the action frame | ||
2407 | * unless we are getting more restrictive. | ||
2408 | */ | ||
2409 | if (test_sta_flag(sta, WLAN_STA_PS_STA) && | ||
2410 | !ieee80211_smps_is_restrictive(sta->known_smps_mode, | ||
2411 | smps_mode)) { | ||
2412 | ht_dbg(sdata, | ||
2413 | "Won't send SMPS to sleeping STA %pM\n", | ||
2414 | sta->sta.addr); | ||
2415 | continue; | ||
2416 | } | ||
2417 | |||
2418 | /* | ||
2419 | * If the STA is not authorized, wait until it gets | ||
2420 | * authorized and the action frame will be sent then. | ||
2421 | */ | ||
2422 | if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED)) | ||
2423 | continue; | ||
2424 | |||
2425 | ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr); | ||
2426 | ieee80211_send_smps_action(sdata, smps_mode, | ||
2427 | sta->sta.addr, | ||
2428 | sdata->vif.bss_conf.bssid); | ||
2429 | } | ||
2430 | } | ||
2431 | mutex_unlock(&sdata->local->sta_mtx); | ||
2432 | |||
2433 | sdata->smps_mode = smps_mode; | ||
2434 | ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps); | ||
2435 | |||
2436 | return 0; | ||
2437 | } | ||
2438 | |||
2439 | int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, | ||
2440 | enum ieee80211_smps_mode smps_mode) | ||
2342 | { | 2441 | { |
2343 | const u8 *ap; | 2442 | const u8 *ap; |
2344 | enum ieee80211_smps_mode old_req; | 2443 | enum ieee80211_smps_mode old_req; |
@@ -2346,6 +2445,9 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | |||
2346 | 2445 | ||
2347 | lockdep_assert_held(&sdata->wdev.mtx); | 2446 | lockdep_assert_held(&sdata->wdev.mtx); |
2348 | 2447 | ||
2448 | if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) | ||
2449 | return -EINVAL; | ||
2450 | |||
2349 | old_req = sdata->u.mgd.req_smps; | 2451 | old_req = sdata->u.mgd.req_smps; |
2350 | sdata->u.mgd.req_smps = smps_mode; | 2452 | sdata->u.mgd.req_smps = smps_mode; |
2351 | 2453 | ||
@@ -2402,7 +2504,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, | |||
2402 | 2504 | ||
2403 | /* no change, but if automatic follow powersave */ | 2505 | /* no change, but if automatic follow powersave */ |
2404 | sdata_lock(sdata); | 2506 | sdata_lock(sdata); |
2405 | __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps); | 2507 | __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps); |
2406 | sdata_unlock(sdata); | 2508 | sdata_unlock(sdata); |
2407 | 2509 | ||
2408 | if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) | 2510 | if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) |
@@ -2860,7 +2962,7 @@ void ieee80211_csa_finalize_work(struct work_struct *work) | |||
2860 | container_of(work, struct ieee80211_sub_if_data, | 2962 | container_of(work, struct ieee80211_sub_if_data, |
2861 | csa_finalize_work); | 2963 | csa_finalize_work); |
2862 | struct ieee80211_local *local = sdata->local; | 2964 | struct ieee80211_local *local = sdata->local; |
2863 | int err, changed; | 2965 | int err, changed = 0; |
2864 | 2966 | ||
2865 | if (!ieee80211_sdata_running(sdata)) | 2967 | if (!ieee80211_sdata_running(sdata)) |
2866 | return; | 2968 | return; |
@@ -2892,6 +2994,13 @@ void ieee80211_csa_finalize_work(struct work_struct *work) | |||
2892 | case NL80211_IFTYPE_ADHOC: | 2994 | case NL80211_IFTYPE_ADHOC: |
2893 | ieee80211_ibss_finish_csa(sdata); | 2995 | ieee80211_ibss_finish_csa(sdata); |
2894 | break; | 2996 | break; |
2997 | #ifdef CONFIG_MAC80211_MESH | ||
2998 | case NL80211_IFTYPE_MESH_POINT: | ||
2999 | err = ieee80211_mesh_finish_csa(sdata); | ||
3000 | if (err < 0) | ||
3001 | return; | ||
3002 | break; | ||
3003 | #endif | ||
2895 | default: | 3004 | default: |
2896 | WARN_ON(1); | 3005 | WARN_ON(1); |
2897 | return; | 3006 | return; |
@@ -2912,6 +3021,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, | |||
2912 | struct ieee80211_local *local = sdata->local; | 3021 | struct ieee80211_local *local = sdata->local; |
2913 | struct ieee80211_chanctx_conf *chanctx_conf; | 3022 | struct ieee80211_chanctx_conf *chanctx_conf; |
2914 | struct ieee80211_chanctx *chanctx; | 3023 | struct ieee80211_chanctx *chanctx; |
3024 | struct ieee80211_if_mesh __maybe_unused *ifmsh; | ||
2915 | int err, num_chanctx; | 3025 | int err, num_chanctx; |
2916 | 3026 | ||
2917 | if (!list_empty(&local->roc_list) || local->scanning) | 3027 | if (!list_empty(&local->roc_list) || local->scanning) |
@@ -2995,6 +3105,26 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, | |||
2995 | if (err < 0) | 3105 | if (err < 0) |
2996 | return err; | 3106 | return err; |
2997 | break; | 3107 | break; |
3108 | #ifdef CONFIG_MAC80211_MESH | ||
3109 | case NL80211_IFTYPE_MESH_POINT: | ||
3110 | ifmsh = &sdata->u.mesh; | ||
3111 | |||
3112 | if (!ifmsh->mesh_id) | ||
3113 | return -EINVAL; | ||
3114 | |||
3115 | if (params->chandef.width != sdata->vif.bss_conf.chandef.width) | ||
3116 | return -EINVAL; | ||
3117 | |||
3118 | /* changes into another band are not supported */ | ||
3119 | if (sdata->vif.bss_conf.chandef.chan->band != | ||
3120 | params->chandef.chan->band) | ||
3121 | return -EINVAL; | ||
3122 | |||
3123 | err = ieee80211_mesh_csa_beacon(sdata, params, true); | ||
3124 | if (err < 0) | ||
3125 | return err; | ||
3126 | break; | ||
3127 | #endif | ||
2998 | default: | 3128 | default: |
2999 | return -EOPNOTSUPP; | 3129 | return -EOPNOTSUPP; |
3000 | } | 3130 | } |
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 @@ | |||
44 | #define MAC80211_MESH_SYNC_DEBUG 0 | 44 | #define MAC80211_MESH_SYNC_DEBUG 0 |
45 | #endif | 45 | #endif |
46 | 46 | ||
47 | #ifdef CONFIG_MAC80211_MESH_CSA_DEBUG | ||
48 | #define MAC80211_MESH_CSA_DEBUG 1 | ||
49 | #else | ||
50 | #define MAC80211_MESH_CSA_DEBUG 0 | ||
51 | #endif | ||
52 | |||
47 | #ifdef CONFIG_MAC80211_MESH_PS_DEBUG | 53 | #ifdef CONFIG_MAC80211_MESH_PS_DEBUG |
48 | #define MAC80211_MESH_PS_DEBUG 1 | 54 | #define MAC80211_MESH_PS_DEBUG 1 |
49 | #else | 55 | #else |
@@ -157,6 +163,10 @@ do { \ | |||
157 | _sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \ | 163 | _sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \ |
158 | sdata, fmt, ##__VA_ARGS__) | 164 | sdata, fmt, ##__VA_ARGS__) |
159 | 165 | ||
166 | #define mcsa_dbg(sdata, fmt, ...) \ | ||
167 | _sdata_dbg(MAC80211_MESH_CSA_DEBUG, \ | ||
168 | sdata, fmt, ##__VA_ARGS__) | ||
169 | |||
160 | #define mps_dbg(sdata, fmt, ...) \ | 170 | #define mps_dbg(sdata, fmt, ...) \ |
161 | _sdata_dbg(MAC80211_MESH_PS_DEBUG, \ | 171 | _sdata_dbg(MAC80211_MESH_PS_DEBUG, \ |
162 | sdata, fmt, ##__VA_ARGS__) | 172 | 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, | |||
224 | smps_mode == IEEE80211_SMPS_AUTOMATIC)) | 224 | smps_mode == IEEE80211_SMPS_AUTOMATIC)) |
225 | return -EINVAL; | 225 | return -EINVAL; |
226 | 226 | ||
227 | /* supported only on managed interfaces for now */ | 227 | if (sdata->vif.type != NL80211_IFTYPE_STATION && |
228 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | 228 | sdata->vif.type != NL80211_IFTYPE_AP) |
229 | return -EOPNOTSUPP; | 229 | return -EOPNOTSUPP; |
230 | 230 | ||
231 | sdata_lock(sdata); | 231 | sdata_lock(sdata); |
232 | err = __ieee80211_request_smps(sdata, smps_mode); | 232 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
233 | err = __ieee80211_request_smps_mgd(sdata, smps_mode); | ||
234 | else | ||
235 | err = __ieee80211_request_smps_ap(sdata, smps_mode); | ||
233 | sdata_unlock(sdata); | 236 | sdata_unlock(sdata); |
234 | 237 | ||
235 | return err; | 238 | return err; |
@@ -245,12 +248,15 @@ static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = { | |||
245 | static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata, | 248 | static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata, |
246 | char *buf, int buflen) | 249 | char *buf, int buflen) |
247 | { | 250 | { |
248 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | 251 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
249 | return -EOPNOTSUPP; | 252 | return snprintf(buf, buflen, "request: %s\nused: %s\n", |
250 | 253 | smps_modes[sdata->u.mgd.req_smps], | |
251 | return snprintf(buf, buflen, "request: %s\nused: %s\n", | 254 | smps_modes[sdata->smps_mode]); |
252 | smps_modes[sdata->u.mgd.req_smps], | 255 | if (sdata->vif.type == NL80211_IFTYPE_AP) |
253 | smps_modes[sdata->smps_mode]); | 256 | return snprintf(buf, buflen, "request: %s\nused: %s\n", |
257 | smps_modes[sdata->u.ap.req_smps], | ||
258 | smps_modes[sdata->smps_mode]); | ||
259 | return -EINVAL; | ||
254 | } | 260 | } |
255 | 261 | ||
256 | static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, | 262 | 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) | |||
563 | static void add_ap_files(struct ieee80211_sub_if_data *sdata) | 569 | static void add_ap_files(struct ieee80211_sub_if_data *sdata) |
564 | { | 570 | { |
565 | DEBUGFS_ADD(num_mcast_sta); | 571 | DEBUGFS_ADD(num_mcast_sta); |
572 | DEBUGFS_ADD_MODE(smps, 0600); | ||
566 | DEBUGFS_ADD(num_sta_ps); | 573 | DEBUGFS_ADD(num_sta_ps); |
567 | DEBUGFS_ADD(dtim_count); | 574 | DEBUGFS_ADD(dtim_count); |
568 | DEBUGFS_ADD(num_buffered_multicast); | 575 | 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, | |||
448 | return 0; | 448 | return 0; |
449 | } | 449 | } |
450 | 450 | ||
451 | void ieee80211_request_smps_work(struct work_struct *work) | 451 | void ieee80211_request_smps_mgd_work(struct work_struct *work) |
452 | { | 452 | { |
453 | struct ieee80211_sub_if_data *sdata = | 453 | struct ieee80211_sub_if_data *sdata = |
454 | container_of(work, struct ieee80211_sub_if_data, | 454 | container_of(work, struct ieee80211_sub_if_data, |
455 | u.mgd.request_smps_work); | 455 | u.mgd.request_smps_work); |
456 | 456 | ||
457 | sdata_lock(sdata); | 457 | sdata_lock(sdata); |
458 | __ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode); | 458 | __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode); |
459 | sdata_unlock(sdata); | ||
460 | } | ||
461 | |||
462 | void ieee80211_request_smps_ap_work(struct work_struct *work) | ||
463 | { | ||
464 | struct ieee80211_sub_if_data *sdata = | ||
465 | container_of(work, struct ieee80211_sub_if_data, | ||
466 | u.ap.request_smps_work); | ||
467 | |||
468 | sdata_lock(sdata); | ||
469 | __ieee80211_request_smps_ap(sdata, sdata->u.ap.driver_smps_mode); | ||
459 | sdata_unlock(sdata); | 470 | sdata_unlock(sdata); |
460 | } | 471 | } |
461 | 472 | ||
@@ -464,19 +475,29 @@ void ieee80211_request_smps(struct ieee80211_vif *vif, | |||
464 | { | 475 | { |
465 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | 476 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); |
466 | 477 | ||
467 | if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) | 478 | if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION && |
479 | vif->type != NL80211_IFTYPE_AP)) | ||
468 | return; | 480 | return; |
469 | 481 | ||
470 | if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) | 482 | if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) |
471 | smps_mode = IEEE80211_SMPS_AUTOMATIC; | 483 | smps_mode = IEEE80211_SMPS_AUTOMATIC; |
472 | 484 | ||
473 | if (sdata->u.mgd.driver_smps_mode == smps_mode) | 485 | if (vif->type == NL80211_IFTYPE_STATION) { |
474 | return; | 486 | if (sdata->u.mgd.driver_smps_mode == smps_mode) |
475 | 487 | return; | |
476 | sdata->u.mgd.driver_smps_mode = smps_mode; | 488 | sdata->u.mgd.driver_smps_mode = smps_mode; |
477 | 489 | ieee80211_queue_work(&sdata->local->hw, | |
478 | ieee80211_queue_work(&sdata->local->hw, | 490 | &sdata->u.mgd.request_smps_work); |
479 | &sdata->u.mgd.request_smps_work); | 491 | } else { |
492 | /* AUTOMATIC is meaningless in AP mode */ | ||
493 | if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC)) | ||
494 | return; | ||
495 | if (sdata->u.ap.driver_smps_mode == smps_mode) | ||
496 | return; | ||
497 | sdata->u.ap.driver_smps_mode = smps_mode; | ||
498 | ieee80211_queue_work(&sdata->local->hw, | ||
499 | &sdata->u.ap.request_smps_work); | ||
500 | } | ||
480 | } | 501 | } |
481 | /* this might change ... don't want non-open drivers using it */ | 502 | /* this might change ... don't want non-open drivers using it */ |
482 | EXPORT_SYMBOL_GPL(ieee80211_request_smps); | 503 | 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, | |||
229 | struct beacon_data *presp; | 229 | struct beacon_data *presp; |
230 | enum nl80211_bss_scan_width scan_width; | 230 | enum nl80211_bss_scan_width scan_width; |
231 | bool have_higher_than_11mbit; | 231 | bool have_higher_than_11mbit; |
232 | bool radar_required = false; | ||
232 | int err; | 233 | int err; |
233 | 234 | ||
234 | sdata_assert_lock(sdata); | 235 | sdata_assert_lock(sdata); |
@@ -273,6 +274,23 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
273 | } | 274 | } |
274 | chandef.width = NL80211_CHAN_WIDTH_20; | 275 | chandef.width = NL80211_CHAN_WIDTH_20; |
275 | chandef.center_freq1 = chan->center_freq; | 276 | chandef.center_freq1 = chan->center_freq; |
277 | /* check again for downgraded chandef */ | ||
278 | if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) { | ||
279 | sdata_info(sdata, | ||
280 | "Failed to join IBSS, beacons forbidden\n"); | ||
281 | return; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, | ||
286 | &chandef); | ||
287 | if (err > 0) { | ||
288 | if (!ifibss->userspace_handles_dfs) { | ||
289 | sdata_info(sdata, | ||
290 | "Failed to join IBSS, DFS channel without control program\n"); | ||
291 | return; | ||
292 | } | ||
293 | radar_required = true; | ||
276 | } | 294 | } |
277 | 295 | ||
278 | ieee80211_vif_release_channel(sdata); | 296 | ieee80211_vif_release_channel(sdata); |
@@ -297,6 +315,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
297 | rcu_assign_pointer(ifibss->presp, presp); | 315 | rcu_assign_pointer(ifibss->presp, presp); |
298 | mgmt = (void *)presp->head; | 316 | mgmt = (void *)presp->head; |
299 | 317 | ||
318 | sdata->radar_required = radar_required; | ||
300 | sdata->vif.bss_conf.enable_beacon = true; | 319 | sdata->vif.bss_conf.enable_beacon = true; |
301 | sdata->vif.bss_conf.beacon_int = beacon_int; | 320 | sdata->vif.bss_conf.beacon_int = beacon_int; |
302 | sdata->vif.bss_conf.basic_rates = basic_rates; | 321 | sdata->vif.bss_conf.basic_rates = basic_rates; |
@@ -445,60 +464,6 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
445 | tsf, false); | 464 | tsf, false); |
446 | } | 465 | } |
447 | 466 | ||
448 | static int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, | ||
449 | struct cfg80211_csa_settings *csa_settings) | ||
450 | { | ||
451 | struct sk_buff *skb; | ||
452 | struct ieee80211_mgmt *mgmt; | ||
453 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | ||
454 | struct ieee80211_local *local = sdata->local; | ||
455 | int freq; | ||
456 | int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) + | ||
457 | sizeof(mgmt->u.action.u.chan_switch); | ||
458 | u8 *pos; | ||
459 | |||
460 | skb = dev_alloc_skb(local->tx_headroom + hdr_len + | ||
461 | 5 + /* channel switch announcement element */ | ||
462 | 3); /* secondary channel offset element */ | ||
463 | if (!skb) | ||
464 | return -1; | ||
465 | |||
466 | skb_reserve(skb, local->tx_headroom); | ||
467 | mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len); | ||
468 | memset(mgmt, 0, hdr_len); | ||
469 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | ||
470 | IEEE80211_STYPE_ACTION); | ||
471 | |||
472 | eth_broadcast_addr(mgmt->da); | ||
473 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | ||
474 | memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); | ||
475 | mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; | ||
476 | mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH; | ||
477 | pos = skb_put(skb, 5); | ||
478 | *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */ | ||
479 | *pos++ = 3; /* IE length */ | ||
480 | *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */ | ||
481 | freq = csa_settings->chandef.chan->center_freq; | ||
482 | *pos++ = ieee80211_frequency_to_channel(freq); /* channel */ | ||
483 | *pos++ = csa_settings->count; /* count */ | ||
484 | |||
485 | if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) { | ||
486 | enum nl80211_channel_type ch_type; | ||
487 | |||
488 | skb_put(skb, 3); | ||
489 | *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */ | ||
490 | *pos++ = 1; /* IE length */ | ||
491 | ch_type = cfg80211_get_chandef_type(&csa_settings->chandef); | ||
492 | if (ch_type == NL80211_CHAN_HT40PLUS) | ||
493 | *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; | ||
494 | else | ||
495 | *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW; | ||
496 | } | ||
497 | |||
498 | ieee80211_tx_skb(sdata, skb); | ||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, | 467 | int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, |
503 | struct cfg80211_csa_settings *csa_settings) | 468 | struct cfg80211_csa_settings *csa_settings) |
504 | { | 469 | { |
@@ -796,19 +761,34 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work) | |||
796 | ieee80211_queue_work(&sdata->local->hw, &sdata->work); | 761 | ieee80211_queue_work(&sdata->local->hw, &sdata->work); |
797 | } | 762 | } |
798 | 763 | ||
764 | static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata) | ||
765 | { | ||
766 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | ||
767 | int err; | ||
768 | |||
769 | /* if the current channel is a DFS channel, mark the channel as | ||
770 | * unavailable. | ||
771 | */ | ||
772 | err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, | ||
773 | &ifibss->chandef); | ||
774 | if (err > 0) | ||
775 | cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef, | ||
776 | GFP_ATOMIC); | ||
777 | } | ||
778 | |||
799 | static bool | 779 | static bool |
800 | ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, | 780 | ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, |
801 | struct ieee802_11_elems *elems, | 781 | struct ieee802_11_elems *elems, |
802 | bool beacon) | 782 | bool beacon) |
803 | { | 783 | { |
804 | struct cfg80211_csa_settings params; | 784 | struct cfg80211_csa_settings params; |
785 | struct ieee80211_csa_ie csa_ie; | ||
805 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | 786 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; |
806 | struct ieee80211_chanctx_conf *chanctx_conf; | 787 | struct ieee80211_chanctx_conf *chanctx_conf; |
807 | struct ieee80211_chanctx *chanctx; | 788 | struct ieee80211_chanctx *chanctx; |
808 | enum nl80211_channel_type ch_type; | 789 | enum nl80211_channel_type ch_type; |
809 | int err, num_chanctx; | 790 | int err, num_chanctx; |
810 | u32 sta_flags; | 791 | u32 sta_flags; |
811 | u8 mode; | ||
812 | 792 | ||
813 | if (sdata->vif.csa_active) | 793 | if (sdata->vif.csa_active) |
814 | return true; | 794 | return true; |
@@ -831,12 +811,10 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
831 | } | 811 | } |
832 | 812 | ||
833 | memset(¶ms, 0, sizeof(params)); | 813 | memset(¶ms, 0, sizeof(params)); |
814 | memset(&csa_ie, 0, sizeof(csa_ie)); | ||
834 | err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, | 815 | err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, |
835 | ifibss->chandef.chan->band, | 816 | ifibss->chandef.chan->band, |
836 | sta_flags, ifibss->bssid, | 817 | sta_flags, ifibss->bssid, &csa_ie); |
837 | ¶ms.count, &mode, | ||
838 | ¶ms.chandef); | ||
839 | |||
840 | /* can't switch to destination channel, fail */ | 818 | /* can't switch to destination channel, fail */ |
841 | if (err < 0) | 819 | if (err < 0) |
842 | goto disconnect; | 820 | goto disconnect; |
@@ -845,6 +823,9 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
845 | if (err) | 823 | if (err) |
846 | return false; | 824 | return false; |
847 | 825 | ||
826 | params.count = csa_ie.count; | ||
827 | params.chandef = csa_ie.chandef; | ||
828 | |||
848 | if (ifibss->chandef.chan->band != params.chandef.chan->band) | 829 | if (ifibss->chandef.chan->band != params.chandef.chan->band) |
849 | goto disconnect; | 830 | goto disconnect; |
850 | 831 | ||
@@ -880,8 +861,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
880 | goto disconnect; | 861 | goto disconnect; |
881 | } | 862 | } |
882 | 863 | ||
883 | if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef, | 864 | if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, ¶ms.chandef)) { |
884 | IEEE80211_CHAN_DISABLED)) { | ||
885 | sdata_info(sdata, | 865 | sdata_info(sdata, |
886 | "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", | 866 | "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", |
887 | ifibss->bssid, | 867 | ifibss->bssid, |
@@ -897,10 +877,11 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
897 | if (err < 0) | 877 | if (err < 0) |
898 | goto disconnect; | 878 | goto disconnect; |
899 | if (err) { | 879 | if (err) { |
900 | params.radar_required = true; | 880 | /* IBSS-DFS only allowed with a control program */ |
881 | if (!ifibss->userspace_handles_dfs) | ||
882 | goto disconnect; | ||
901 | 883 | ||
902 | /* TODO: IBSS-DFS not (yet) supported, disconnect. */ | 884 | params.radar_required = true; |
903 | goto disconnect; | ||
904 | } | 885 | } |
905 | 886 | ||
906 | rcu_read_lock(); | 887 | rcu_read_lock(); |
@@ -931,7 +912,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
931 | "received channel switch announcement to go to channel %d MHz\n", | 912 | "received channel switch announcement to go to channel %d MHz\n", |
932 | params.chandef.chan->center_freq); | 913 | params.chandef.chan->center_freq); |
933 | 914 | ||
934 | params.block_tx = !!mode; | 915 | params.block_tx = !!csa_ie.mode; |
935 | 916 | ||
936 | ieee80211_ibss_csa_beacon(sdata, ¶ms); | 917 | ieee80211_ibss_csa_beacon(sdata, ¶ms); |
937 | sdata->csa_radar_required = params.radar_required; | 918 | sdata->csa_radar_required = params.radar_required; |
@@ -947,12 +928,16 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
947 | ieee80211_bss_info_change_notify(sdata, err); | 928 | ieee80211_bss_info_change_notify(sdata, err); |
948 | drv_channel_switch_beacon(sdata, ¶ms.chandef); | 929 | drv_channel_switch_beacon(sdata, ¶ms.chandef); |
949 | 930 | ||
931 | ieee80211_ibss_csa_mark_radar(sdata); | ||
932 | |||
950 | return true; | 933 | return true; |
951 | disconnect: | 934 | disconnect: |
952 | ibss_dbg(sdata, "Can't handle channel switch, disconnect\n"); | 935 | ibss_dbg(sdata, "Can't handle channel switch, disconnect\n"); |
953 | ieee80211_queue_work(&sdata->local->hw, | 936 | ieee80211_queue_work(&sdata->local->hw, |
954 | &ifibss->csa_connection_drop_work); | 937 | &ifibss->csa_connection_drop_work); |
955 | 938 | ||
939 | ieee80211_ibss_csa_mark_radar(sdata); | ||
940 | |||
956 | return true; | 941 | return true; |
957 | } | 942 | } |
958 | 943 | ||
@@ -1688,6 +1673,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, | |||
1688 | 1673 | ||
1689 | sdata->u.ibss.privacy = params->privacy; | 1674 | sdata->u.ibss.privacy = params->privacy; |
1690 | sdata->u.ibss.control_port = params->control_port; | 1675 | sdata->u.ibss.control_port = params->control_port; |
1676 | sdata->u.ibss.userspace_handles_dfs = params->userspace_handles_dfs; | ||
1691 | sdata->u.ibss.basic_rates = params->basic_rates; | 1677 | sdata->u.ibss.basic_rates = params->basic_rates; |
1692 | 1678 | ||
1693 | /* fix basic_rates if channel does not support these rates */ | 1679 | /* fix basic_rates if channel does not support these rates */ |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index fe48b093d4dc..29dc505be125 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -262,6 +262,10 @@ struct ieee80211_if_ap { | |||
262 | 262 | ||
263 | struct ps_data ps; | 263 | struct ps_data ps; |
264 | atomic_t num_mcast_sta; /* number of stations receiving multicast */ | 264 | atomic_t num_mcast_sta; /* number of stations receiving multicast */ |
265 | enum ieee80211_smps_mode req_smps, /* requested smps mode */ | ||
266 | driver_smps_mode; /* smps mode request */ | ||
267 | |||
268 | struct work_struct request_smps_work; | ||
265 | }; | 269 | }; |
266 | 270 | ||
267 | struct ieee80211_if_wds { | 271 | struct ieee80211_if_wds { |
@@ -498,6 +502,7 @@ struct ieee80211_if_ibss { | |||
498 | bool privacy; | 502 | bool privacy; |
499 | 503 | ||
500 | bool control_port; | 504 | bool control_port; |
505 | bool userspace_handles_dfs; | ||
501 | 506 | ||
502 | u8 bssid[ETH_ALEN] __aligned(2); | 507 | u8 bssid[ETH_ALEN] __aligned(2); |
503 | u8 ssid[IEEE80211_MAX_SSID_LEN]; | 508 | u8 ssid[IEEE80211_MAX_SSID_LEN]; |
@@ -539,6 +544,11 @@ struct ieee80211_mesh_sync_ops { | |||
539 | /* add other framework functions here */ | 544 | /* add other framework functions here */ |
540 | }; | 545 | }; |
541 | 546 | ||
547 | struct mesh_csa_settings { | ||
548 | struct rcu_head rcu_head; | ||
549 | struct cfg80211_csa_settings settings; | ||
550 | }; | ||
551 | |||
542 | struct ieee80211_if_mesh { | 552 | struct ieee80211_if_mesh { |
543 | struct timer_list housekeeping_timer; | 553 | struct timer_list housekeeping_timer; |
544 | struct timer_list mesh_path_timer; | 554 | struct timer_list mesh_path_timer; |
@@ -599,6 +609,11 @@ struct ieee80211_if_mesh { | |||
599 | int ps_peers_light_sleep; | 609 | int ps_peers_light_sleep; |
600 | int ps_peers_deep_sleep; | 610 | int ps_peers_deep_sleep; |
601 | struct ps_data ps; | 611 | struct ps_data ps; |
612 | /* Channel Switching Support */ | ||
613 | struct mesh_csa_settings __rcu *csa; | ||
614 | bool chsw_init; | ||
615 | u8 chsw_ttl; | ||
616 | u16 pre_value; | ||
602 | }; | 617 | }; |
603 | 618 | ||
604 | #ifdef CONFIG_MAC80211_MESH | 619 | #ifdef CONFIG_MAC80211_MESH |
@@ -1207,6 +1222,14 @@ struct ieee80211_ra_tid { | |||
1207 | u16 tid; | 1222 | u16 tid; |
1208 | }; | 1223 | }; |
1209 | 1224 | ||
1225 | /* this struct holds the value parsing from channel switch IE */ | ||
1226 | struct ieee80211_csa_ie { | ||
1227 | struct cfg80211_chan_def chandef; | ||
1228 | u8 mode; | ||
1229 | u8 count; | ||
1230 | u8 ttl; | ||
1231 | }; | ||
1232 | |||
1210 | /* Parsed Information Elements */ | 1233 | /* Parsed Information Elements */ |
1211 | struct ieee802_11_elems { | 1234 | struct ieee802_11_elems { |
1212 | const u8 *ie_start; | 1235 | const u8 *ie_start; |
@@ -1243,6 +1266,7 @@ struct ieee802_11_elems { | |||
1243 | const struct ieee80211_timeout_interval_ie *timeout_int; | 1266 | const struct ieee80211_timeout_interval_ie *timeout_int; |
1244 | const u8 *opmode_notif; | 1267 | const u8 *opmode_notif; |
1245 | const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; | 1268 | const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; |
1269 | const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie; | ||
1246 | 1270 | ||
1247 | /* length of them, respectively */ | 1271 | /* length of them, respectively */ |
1248 | u8 ssid_len; | 1272 | u8 ssid_len; |
@@ -1343,6 +1367,10 @@ void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata); | |||
1343 | void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata); | 1367 | void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata); |
1344 | void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | 1368 | void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, |
1345 | struct sk_buff *skb); | 1369 | struct sk_buff *skb); |
1370 | int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, | ||
1371 | struct cfg80211_csa_settings *csa_settings, | ||
1372 | bool csa_action); | ||
1373 | int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata); | ||
1346 | 1374 | ||
1347 | /* scan/BSS handling */ | 1375 | /* scan/BSS handling */ |
1348 | void ieee80211_scan_work(struct work_struct *work); | 1376 | void ieee80211_scan_work(struct work_struct *work); |
@@ -1439,7 +1467,10 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, | |||
1439 | int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, | 1467 | int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, |
1440 | enum ieee80211_smps_mode smps, const u8 *da, | 1468 | enum ieee80211_smps_mode smps, const u8 *da, |
1441 | const u8 *bssid); | 1469 | const u8 *bssid); |
1442 | void ieee80211_request_smps_work(struct work_struct *work); | 1470 | void ieee80211_request_smps_ap_work(struct work_struct *work); |
1471 | void ieee80211_request_smps_mgd_work(struct work_struct *work); | ||
1472 | bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, | ||
1473 | enum ieee80211_smps_mode smps_mode_new); | ||
1443 | 1474 | ||
1444 | void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, | 1475 | void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, |
1445 | u16 initiator, u16 reason, bool stop); | 1476 | u16 initiator, u16 reason, bool stop); |
@@ -1501,17 +1532,16 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, | |||
1501 | * %IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT, | 1532 | * %IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT, |
1502 | * %IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ, | 1533 | * %IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ, |
1503 | * %IEEE80211_STA_DISABLE_160MHZ. | 1534 | * %IEEE80211_STA_DISABLE_160MHZ. |
1504 | * @count: to be filled with the counter until the switch (on success only) | ||
1505 | * @bssid: the currently connected bssid (for reporting) | 1535 | * @bssid: the currently connected bssid (for reporting) |
1506 | * @mode: to be filled with CSA mode (on success only) | 1536 | * @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl. |
1507 | * @new_chandef: to be filled with destination chandef (on success only) | 1537 | All of them will be filled with if success only. |
1508 | * Return: 0 on success, <0 on error and >0 if there is nothing to parse. | 1538 | * Return: 0 on success, <0 on error and >0 if there is nothing to parse. |
1509 | */ | 1539 | */ |
1510 | int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, | 1540 | int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, |
1511 | struct ieee802_11_elems *elems, bool beacon, | 1541 | struct ieee802_11_elems *elems, bool beacon, |
1512 | enum ieee80211_band current_band, | 1542 | enum ieee80211_band current_band, |
1513 | u32 sta_flags, u8 *bssid, u8 *count, u8 *mode, | 1543 | u32 sta_flags, u8 *bssid, |
1514 | struct cfg80211_chan_def *new_chandef); | 1544 | struct ieee80211_csa_ie *csa_ie); |
1515 | 1545 | ||
1516 | /* Suspend/resume and hw reconfiguration */ | 1546 | /* Suspend/resume and hw reconfiguration */ |
1517 | int ieee80211_reconfig(struct ieee80211_local *local); | 1547 | int ieee80211_reconfig(struct ieee80211_local *local); |
@@ -1657,8 +1687,10 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, | |||
1657 | u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, | 1687 | u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, |
1658 | struct ieee802_11_elems *elems, | 1688 | struct ieee802_11_elems *elems, |
1659 | enum ieee80211_band band, u32 *basic_rates); | 1689 | enum ieee80211_band band, u32 *basic_rates); |
1660 | int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, | 1690 | int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, |
1661 | enum ieee80211_smps_mode smps_mode); | 1691 | enum ieee80211_smps_mode smps_mode); |
1692 | int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, | ||
1693 | enum ieee80211_smps_mode smps_mode); | ||
1662 | void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); | 1694 | void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); |
1663 | 1695 | ||
1664 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, | 1696 | size_t ieee80211_ie_split(const u8 *ies, size_t ielen, |
@@ -1714,6 +1746,8 @@ void ieee80211_dfs_cac_timer(unsigned long data); | |||
1714 | void ieee80211_dfs_cac_timer_work(struct work_struct *work); | 1746 | void ieee80211_dfs_cac_timer_work(struct work_struct *work); |
1715 | void ieee80211_dfs_cac_cancel(struct ieee80211_local *local); | 1747 | void ieee80211_dfs_cac_cancel(struct ieee80211_local *local); |
1716 | void ieee80211_dfs_radar_detected_work(struct work_struct *work); | 1748 | void ieee80211_dfs_radar_detected_work(struct work_struct *work); |
1749 | int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, | ||
1750 | struct cfg80211_csa_settings *csa_settings); | ||
1717 | 1751 | ||
1718 | #ifdef CONFIG_MAC80211_NOINLINE | 1752 | #ifdef CONFIG_MAC80211_NOINLINE |
1719 | #define debug_noinline noinline | 1753 | #define debug_noinline noinline |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index e48f103b9ade..ff101ea1d9ae 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -1293,7 +1293,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, | |||
1293 | case NL80211_IFTYPE_AP: | 1293 | case NL80211_IFTYPE_AP: |
1294 | skb_queue_head_init(&sdata->u.ap.ps.bc_buf); | 1294 | skb_queue_head_init(&sdata->u.ap.ps.bc_buf); |
1295 | INIT_LIST_HEAD(&sdata->u.ap.vlans); | 1295 | INIT_LIST_HEAD(&sdata->u.ap.vlans); |
1296 | INIT_WORK(&sdata->u.ap.request_smps_work, | ||
1297 | ieee80211_request_smps_ap_work); | ||
1296 | sdata->vif.bss_conf.bssid = sdata->vif.addr; | 1298 | sdata->vif.bss_conf.bssid = sdata->vif.addr; |
1299 | sdata->u.ap.req_smps = IEEE80211_SMPS_OFF; | ||
1297 | break; | 1300 | break; |
1298 | case NL80211_IFTYPE_P2P_CLIENT: | 1301 | case NL80211_IFTYPE_P2P_CLIENT: |
1299 | type = NL80211_IFTYPE_STATION; | 1302 | type = NL80211_IFTYPE_STATION; |
diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 036d57e76a5e..aaae0ed37004 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h | |||
@@ -83,7 +83,7 @@ struct ieee80211_key { | |||
83 | * Management frames. | 83 | * Management frames. |
84 | */ | 84 | */ |
85 | u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; | 85 | u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; |
86 | struct crypto_cipher *tfm; | 86 | struct crypto_aead *tfm; |
87 | u32 replays; /* dot11RSNAStatsCCMPReplays */ | 87 | u32 replays; /* dot11RSNAStatsCCMPReplays */ |
88 | } ccmp; | 88 | } ccmp; |
89 | struct { | 89 | struct { |
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 707ac61d63e5..896fe3bd599e 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <asm/unaligned.h> | 12 | #include <asm/unaligned.h> |
13 | #include "ieee80211_i.h" | 13 | #include "ieee80211_i.h" |
14 | #include "mesh.h" | 14 | #include "mesh.h" |
15 | #include "driver-ops.h" | ||
15 | 16 | ||
16 | static int mesh_allocated; | 17 | static int mesh_allocated; |
17 | static struct kmem_cache *rm_cache; | 18 | static struct kmem_cache *rm_cache; |
@@ -610,6 +611,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) | |||
610 | struct sk_buff *skb; | 611 | struct sk_buff *skb; |
611 | struct ieee80211_mgmt *mgmt; | 612 | struct ieee80211_mgmt *mgmt; |
612 | struct ieee80211_chanctx_conf *chanctx_conf; | 613 | struct ieee80211_chanctx_conf *chanctx_conf; |
614 | struct mesh_csa_settings *csa; | ||
613 | enum ieee80211_band band; | 615 | enum ieee80211_band band; |
614 | u8 *pos; | 616 | u8 *pos; |
615 | struct ieee80211_sub_if_data *sdata; | 617 | struct ieee80211_sub_if_data *sdata; |
@@ -624,6 +626,10 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) | |||
624 | 626 | ||
625 | head_len = hdr_len + | 627 | head_len = hdr_len + |
626 | 2 + /* NULL SSID */ | 628 | 2 + /* NULL SSID */ |
629 | /* Channel Switch Announcement */ | ||
630 | 2 + sizeof(struct ieee80211_channel_sw_ie) + | ||
631 | /* Mesh Channel Swith Parameters */ | ||
632 | 2 + sizeof(struct ieee80211_mesh_chansw_params_ie) + | ||
627 | 2 + 8 + /* supported rates */ | 633 | 2 + 8 + /* supported rates */ |
628 | 2 + 3; /* DS params */ | 634 | 2 + 3; /* DS params */ |
629 | tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) + | 635 | tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) + |
@@ -665,6 +671,38 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) | |||
665 | *pos++ = WLAN_EID_SSID; | 671 | *pos++ = WLAN_EID_SSID; |
666 | *pos++ = 0x0; | 672 | *pos++ = 0x0; |
667 | 673 | ||
674 | rcu_read_lock(); | ||
675 | csa = rcu_dereference(ifmsh->csa); | ||
676 | if (csa) { | ||
677 | __le16 pre_value; | ||
678 | |||
679 | pos = skb_put(skb, 13); | ||
680 | memset(pos, 0, 13); | ||
681 | *pos++ = WLAN_EID_CHANNEL_SWITCH; | ||
682 | *pos++ = 3; | ||
683 | *pos++ = 0x0; | ||
684 | *pos++ = ieee80211_frequency_to_channel( | ||
685 | csa->settings.chandef.chan->center_freq); | ||
686 | sdata->csa_counter_offset_beacon = hdr_len + 6; | ||
687 | *pos++ = csa->settings.count; | ||
688 | *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; | ||
689 | *pos++ = 6; | ||
690 | if (ifmsh->chsw_init) { | ||
691 | *pos++ = ifmsh->mshcfg.dot11MeshTTL; | ||
692 | *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; | ||
693 | } else { | ||
694 | *pos++ = ifmsh->chsw_ttl; | ||
695 | } | ||
696 | *pos++ |= csa->settings.block_tx ? | ||
697 | WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; | ||
698 | put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); | ||
699 | pos += 2; | ||
700 | pre_value = cpu_to_le16(ifmsh->pre_value); | ||
701 | memcpy(pos, &pre_value, 2); | ||
702 | pos += 2; | ||
703 | } | ||
704 | rcu_read_unlock(); | ||
705 | |||
668 | if (ieee80211_add_srates_ie(sdata, skb, true, band) || | 706 | if (ieee80211_add_srates_ie(sdata, skb, true, band) || |
669 | mesh_add_ds_params_ie(sdata, skb)) | 707 | mesh_add_ds_params_ie(sdata, skb)) |
670 | goto out_free; | 708 | goto out_free; |
@@ -812,6 +850,127 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) | |||
812 | ieee80211_configure_filter(local); | 850 | ieee80211_configure_filter(local); |
813 | } | 851 | } |
814 | 852 | ||
853 | static bool | ||
854 | ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, | ||
855 | struct ieee802_11_elems *elems, bool beacon) | ||
856 | { | ||
857 | struct cfg80211_csa_settings params; | ||
858 | struct ieee80211_csa_ie csa_ie; | ||
859 | struct ieee80211_chanctx_conf *chanctx_conf; | ||
860 | struct ieee80211_chanctx *chanctx; | ||
861 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
862 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); | ||
863 | int err, num_chanctx; | ||
864 | u32 sta_flags; | ||
865 | |||
866 | if (sdata->vif.csa_active) | ||
867 | return true; | ||
868 | |||
869 | if (!ifmsh->mesh_id) | ||
870 | return false; | ||
871 | |||
872 | sta_flags = IEEE80211_STA_DISABLE_VHT; | ||
873 | switch (sdata->vif.bss_conf.chandef.width) { | ||
874 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
875 | sta_flags |= IEEE80211_STA_DISABLE_HT; | ||
876 | case NL80211_CHAN_WIDTH_20: | ||
877 | sta_flags |= IEEE80211_STA_DISABLE_40MHZ; | ||
878 | break; | ||
879 | default: | ||
880 | break; | ||
881 | } | ||
882 | |||
883 | memset(¶ms, 0, sizeof(params)); | ||
884 | memset(&csa_ie, 0, sizeof(csa_ie)); | ||
885 | err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band, | ||
886 | sta_flags, sdata->vif.addr, | ||
887 | &csa_ie); | ||
888 | if (err < 0) | ||
889 | return false; | ||
890 | if (err) | ||
891 | return false; | ||
892 | |||
893 | params.chandef = csa_ie.chandef; | ||
894 | params.count = csa_ie.count; | ||
895 | |||
896 | if (sdata->vif.bss_conf.chandef.chan->band != | ||
897 | params.chandef.chan->band) | ||
898 | return false; | ||
899 | |||
900 | if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef, | ||
901 | IEEE80211_CHAN_DISABLED)) { | ||
902 | sdata_info(sdata, | ||
903 | "mesh STA %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), aborting\n", | ||
904 | sdata->vif.addr, | ||
905 | params.chandef.chan->center_freq, | ||
906 | params.chandef.width, | ||
907 | params.chandef.center_freq1, | ||
908 | params.chandef.center_freq2); | ||
909 | return false; | ||
910 | } | ||
911 | |||
912 | err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, | ||
913 | ¶ms.chandef); | ||
914 | if (err < 0) | ||
915 | return false; | ||
916 | if (err) { | ||
917 | params.radar_required = true; | ||
918 | /* TODO: DFS not (yet) supported */ | ||
919 | return false; | ||
920 | } | ||
921 | |||
922 | rcu_read_lock(); | ||
923 | chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); | ||
924 | if (!chanctx_conf) | ||
925 | goto failed_chswitch; | ||
926 | |||
927 | /* don't handle for multi-VIF cases */ | ||
928 | chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); | ||
929 | if (chanctx->refcount > 1) | ||
930 | goto failed_chswitch; | ||
931 | |||
932 | num_chanctx = 0; | ||
933 | list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list) | ||
934 | num_chanctx++; | ||
935 | |||
936 | if (num_chanctx > 1) | ||
937 | goto failed_chswitch; | ||
938 | |||
939 | rcu_read_unlock(); | ||
940 | |||
941 | mcsa_dbg(sdata, | ||
942 | "received channel switch announcement to go to channel %d MHz\n", | ||
943 | params.chandef.chan->center_freq); | ||
944 | |||
945 | params.block_tx = csa_ie.mode & WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT; | ||
946 | if (beacon) | ||
947 | ifmsh->chsw_ttl = csa_ie.ttl - 1; | ||
948 | else | ||
949 | ifmsh->chsw_ttl = 0; | ||
950 | |||
951 | if (ifmsh->chsw_ttl > 0) | ||
952 | if (ieee80211_mesh_csa_beacon(sdata, ¶ms, false) < 0) | ||
953 | return false; | ||
954 | |||
955 | sdata->csa_radar_required = params.radar_required; | ||
956 | |||
957 | if (params.block_tx) | ||
958 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | ||
959 | IEEE80211_MAX_QUEUE_MAP, | ||
960 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
961 | |||
962 | sdata->local->csa_chandef = params.chandef; | ||
963 | sdata->vif.csa_active = true; | ||
964 | |||
965 | ieee80211_bss_info_change_notify(sdata, err); | ||
966 | drv_channel_switch_beacon(sdata, ¶ms.chandef); | ||
967 | |||
968 | return true; | ||
969 | failed_chswitch: | ||
970 | rcu_read_unlock(); | ||
971 | return false; | ||
972 | } | ||
973 | |||
815 | static void | 974 | static void |
816 | ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata, | 975 | ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata, |
817 | struct ieee80211_mgmt *mgmt, size_t len) | 976 | struct ieee80211_mgmt *mgmt, size_t len) |
@@ -918,6 +1077,142 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, | |||
918 | if (ifmsh->sync_ops) | 1077 | if (ifmsh->sync_ops) |
919 | ifmsh->sync_ops->rx_bcn_presp(sdata, | 1078 | ifmsh->sync_ops->rx_bcn_presp(sdata, |
920 | stype, mgmt, &elems, rx_status); | 1079 | stype, mgmt, &elems, rx_status); |
1080 | |||
1081 | if (!ifmsh->chsw_init) | ||
1082 | ieee80211_mesh_process_chnswitch(sdata, &elems, true); | ||
1083 | } | ||
1084 | |||
1085 | int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata) | ||
1086 | { | ||
1087 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
1088 | struct mesh_csa_settings *tmp_csa_settings; | ||
1089 | int ret = 0; | ||
1090 | |||
1091 | /* Reset the TTL value and Initiator flag */ | ||
1092 | ifmsh->chsw_init = false; | ||
1093 | ifmsh->chsw_ttl = 0; | ||
1094 | |||
1095 | /* Remove the CSA and MCSP elements from the beacon */ | ||
1096 | tmp_csa_settings = rcu_dereference(ifmsh->csa); | ||
1097 | rcu_assign_pointer(ifmsh->csa, NULL); | ||
1098 | kfree_rcu(tmp_csa_settings, rcu_head); | ||
1099 | ret = ieee80211_mesh_rebuild_beacon(sdata); | ||
1100 | if (ret) | ||
1101 | return -EINVAL; | ||
1102 | |||
1103 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); | ||
1104 | |||
1105 | mcsa_dbg(sdata, "complete switching to center freq %d MHz", | ||
1106 | sdata->vif.bss_conf.chandef.chan->center_freq); | ||
1107 | return 0; | ||
1108 | } | ||
1109 | |||
1110 | int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, | ||
1111 | struct cfg80211_csa_settings *csa_settings, | ||
1112 | bool csa_action) | ||
1113 | { | ||
1114 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
1115 | struct mesh_csa_settings *tmp_csa_settings; | ||
1116 | int ret = 0; | ||
1117 | |||
1118 | tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings), | ||
1119 | GFP_ATOMIC); | ||
1120 | if (!tmp_csa_settings) | ||
1121 | return -ENOMEM; | ||
1122 | |||
1123 | memcpy(&tmp_csa_settings->settings, csa_settings, | ||
1124 | sizeof(struct cfg80211_csa_settings)); | ||
1125 | |||
1126 | rcu_assign_pointer(ifmsh->csa, tmp_csa_settings); | ||
1127 | |||
1128 | ret = ieee80211_mesh_rebuild_beacon(sdata); | ||
1129 | if (ret) { | ||
1130 | tmp_csa_settings = rcu_dereference(ifmsh->csa); | ||
1131 | rcu_assign_pointer(ifmsh->csa, NULL); | ||
1132 | kfree_rcu(tmp_csa_settings, rcu_head); | ||
1133 | return ret; | ||
1134 | } | ||
1135 | |||
1136 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); | ||
1137 | |||
1138 | if (csa_action) | ||
1139 | ieee80211_send_action_csa(sdata, csa_settings); | ||
1140 | |||
1141 | return 0; | ||
1142 | } | ||
1143 | |||
1144 | static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata, | ||
1145 | struct ieee80211_mgmt *mgmt, size_t len) | ||
1146 | { | ||
1147 | struct ieee80211_mgmt *mgmt_fwd; | ||
1148 | struct sk_buff *skb; | ||
1149 | struct ieee80211_local *local = sdata->local; | ||
1150 | u8 *pos = mgmt->u.action.u.chan_switch.variable; | ||
1151 | size_t offset_ttl; | ||
1152 | |||
1153 | skb = dev_alloc_skb(local->tx_headroom + len); | ||
1154 | if (!skb) | ||
1155 | return -ENOMEM; | ||
1156 | skb_reserve(skb, local->tx_headroom); | ||
1157 | mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len); | ||
1158 | |||
1159 | /* offset_ttl is based on whether the secondary channel | ||
1160 | * offset is available or not. Substract 1 from the mesh TTL | ||
1161 | * and disable the initiator flag before forwarding. | ||
1162 | */ | ||
1163 | offset_ttl = (len < 42) ? 7 : 10; | ||
1164 | *(pos + offset_ttl) -= 1; | ||
1165 | *(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; | ||
1166 | sdata->u.mesh.chsw_ttl = *(pos + offset_ttl); | ||
1167 | |||
1168 | memcpy(mgmt_fwd, mgmt, len); | ||
1169 | eth_broadcast_addr(mgmt_fwd->da); | ||
1170 | memcpy(mgmt_fwd->sa, sdata->vif.addr, ETH_ALEN); | ||
1171 | memcpy(mgmt_fwd->bssid, sdata->vif.addr, ETH_ALEN); | ||
1172 | |||
1173 | ieee80211_tx_skb(sdata, skb); | ||
1174 | return 0; | ||
1175 | } | ||
1176 | |||
1177 | static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata, | ||
1178 | struct ieee80211_mgmt *mgmt, size_t len) | ||
1179 | { | ||
1180 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
1181 | struct ieee802_11_elems elems; | ||
1182 | u16 pre_value; | ||
1183 | bool fwd_csa = true; | ||
1184 | size_t baselen; | ||
1185 | u8 *pos, ttl; | ||
1186 | |||
1187 | if (mgmt->u.action.u.measurement.action_code != | ||
1188 | WLAN_ACTION_SPCT_CHL_SWITCH) | ||
1189 | return; | ||
1190 | |||
1191 | pos = mgmt->u.action.u.chan_switch.variable; | ||
1192 | baselen = offsetof(struct ieee80211_mgmt, | ||
1193 | u.action.u.chan_switch.variable); | ||
1194 | ieee802_11_parse_elems(pos, len - baselen, false, &elems); | ||
1195 | |||
1196 | ttl = elems.mesh_chansw_params_ie->mesh_ttl; | ||
1197 | if (!--ttl) | ||
1198 | fwd_csa = false; | ||
1199 | |||
1200 | pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value); | ||
1201 | if (ifmsh->pre_value >= pre_value) | ||
1202 | return; | ||
1203 | |||
1204 | ifmsh->pre_value = pre_value; | ||
1205 | |||
1206 | if (!ieee80211_mesh_process_chnswitch(sdata, &elems, false)) { | ||
1207 | mcsa_dbg(sdata, "Failed to process CSA action frame"); | ||
1208 | return; | ||
1209 | } | ||
1210 | |||
1211 | /* forward or re-broadcast the CSA frame */ | ||
1212 | if (fwd_csa) { | ||
1213 | if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0) | ||
1214 | mcsa_dbg(sdata, "Failed to forward the CSA frame"); | ||
1215 | } | ||
921 | } | 1216 | } |
922 | 1217 | ||
923 | static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, | 1218 | 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, | |||
939 | if (mesh_action_is_path_sel(mgmt)) | 1234 | if (mesh_action_is_path_sel(mgmt)) |
940 | mesh_rx_path_sel_frame(sdata, mgmt, len); | 1235 | mesh_rx_path_sel_frame(sdata, mgmt, len); |
941 | break; | 1236 | break; |
1237 | case WLAN_CATEGORY_SPECTRUM_MGMT: | ||
1238 | mesh_rx_csa_frame(sdata, mgmt, len); | ||
1239 | break; | ||
942 | } | 1240 | } |
943 | } | 1241 | } |
944 | 1242 | ||
@@ -1056,13 +1354,11 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) | |||
1056 | (unsigned long) sdata); | 1354 | (unsigned long) sdata); |
1057 | 1355 | ||
1058 | ifmsh->accepting_plinks = true; | 1356 | ifmsh->accepting_plinks = true; |
1059 | ifmsh->preq_id = 0; | ||
1060 | ifmsh->sn = 0; | ||
1061 | ifmsh->num_gates = 0; | ||
1062 | atomic_set(&ifmsh->mpaths, 0); | 1357 | atomic_set(&ifmsh->mpaths, 0); |
1063 | mesh_rmc_init(sdata); | 1358 | mesh_rmc_init(sdata); |
1064 | ifmsh->last_preq = jiffies; | 1359 | ifmsh->last_preq = jiffies; |
1065 | ifmsh->next_perr = jiffies; | 1360 | ifmsh->next_perr = jiffies; |
1361 | ifmsh->chsw_init = false; | ||
1066 | /* Allocate all mesh structures when creating the first mesh interface. */ | 1362 | /* Allocate all mesh structures when creating the first mesh interface. */ |
1067 | if (!mesh_allocated) | 1363 | if (!mesh_allocated) |
1068 | ieee80211s_init(); | 1364 | ieee80211s_init(); |
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 6b65d5055f5b..4301aa5aa227 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c | |||
@@ -222,7 +222,8 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta) | |||
222 | mesh_path_flush_by_nexthop(sta); | 222 | mesh_path_flush_by_nexthop(sta); |
223 | 223 | ||
224 | ieee80211_mps_sta_status_update(sta); | 224 | ieee80211_mps_sta_status_update(sta); |
225 | changed |= ieee80211_mps_local_status_update(sdata); | 225 | changed |= ieee80211_mps_set_sta_local_pm(sta, |
226 | NL80211_MESH_POWER_UNKNOWN); | ||
226 | 227 | ||
227 | return changed; | 228 | return changed; |
228 | } | 229 | } |
diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c index 22290a929b94..0f79b78b5e86 100644 --- a/net/mac80211/mesh_ps.c +++ b/net/mac80211/mesh_ps.c | |||
@@ -152,6 +152,9 @@ u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta, | |||
152 | { | 152 | { |
153 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 153 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
154 | 154 | ||
155 | if (sta->local_pm == pm) | ||
156 | return 0; | ||
157 | |||
155 | mps_dbg(sdata, "local STA operates in mode %d with %pM\n", | 158 | mps_dbg(sdata, "local STA operates in mode %d with %pM\n", |
156 | pm, sta->sta.addr); | 159 | pm, sta->sta.addr); |
157 | 160 | ||
@@ -245,6 +248,14 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta) | |||
245 | 248 | ||
246 | do_buffer = (pm != NL80211_MESH_POWER_ACTIVE); | 249 | do_buffer = (pm != NL80211_MESH_POWER_ACTIVE); |
247 | 250 | ||
251 | /* clear the MPSP flags for non-peers or active STA */ | ||
252 | if (sta->plink_state != NL80211_PLINK_ESTAB) { | ||
253 | clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); | ||
254 | clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); | ||
255 | } else if (!do_buffer) { | ||
256 | clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); | ||
257 | } | ||
258 | |||
248 | /* Don't let the same PS state be set twice */ | 259 | /* Don't let the same PS state be set twice */ |
249 | if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer) | 260 | if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer) |
250 | return; | 261 | return; |
@@ -257,14 +268,6 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta) | |||
257 | } else { | 268 | } else { |
258 | ieee80211_sta_ps_deliver_wakeup(sta); | 269 | ieee80211_sta_ps_deliver_wakeup(sta); |
259 | } | 270 | } |
260 | |||
261 | /* clear the MPSP flags for non-peers or active STA */ | ||
262 | if (sta->plink_state != NL80211_PLINK_ESTAB) { | ||
263 | clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); | ||
264 | clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); | ||
265 | } else if (!do_buffer) { | ||
266 | clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); | ||
267 | } | ||
268 | } | 271 | } |
269 | 272 | ||
270 | static void mps_set_sta_peer_pm(struct sta_info *sta, | 273 | 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, | |||
444 | */ | 447 | */ |
445 | static void mps_frame_deliver(struct sta_info *sta, int n_frames) | 448 | static void mps_frame_deliver(struct sta_info *sta, int n_frames) |
446 | { | 449 | { |
447 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 450 | struct ieee80211_local *local = sta->sdata->local; |
448 | struct ieee80211_local *local = sdata->local; | ||
449 | int ac; | 451 | int ac; |
450 | struct sk_buff_head frames; | 452 | struct sk_buff_head frames; |
451 | struct sk_buff *skb; | 453 | struct sk_buff *skb; |
@@ -558,10 +560,10 @@ void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta, | |||
558 | } | 560 | } |
559 | 561 | ||
560 | /** | 562 | /** |
561 | * ieee80211_mps_frame_release - release buffered frames in response to beacon | 563 | * ieee80211_mps_frame_release - release frames buffered due to mesh power save |
562 | * | 564 | * |
563 | * @sta: mesh STA | 565 | * @sta: mesh STA |
564 | * @elems: beacon IEs | 566 | * @elems: IEs of beacon or probe response |
565 | * | 567 | * |
566 | * For peers if we have individually-addressed frames buffered or the peer | 568 | * For peers if we have individually-addressed frames buffered or the peer |
567 | * indicates buffered frames, send a corresponding MPSP trigger frame. Since | 569 | * indicates buffered frames, send a corresponding MPSP trigger frame. Since |
@@ -588,9 +590,10 @@ void ieee80211_mps_frame_release(struct sta_info *sta, | |||
588 | (!elems->awake_window || !le16_to_cpu(*elems->awake_window))) | 590 | (!elems->awake_window || !le16_to_cpu(*elems->awake_window))) |
589 | return; | 591 | return; |
590 | 592 | ||
591 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) | 593 | if (!test_sta_flag(sta, WLAN_STA_MPSP_OWNER)) |
592 | buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) + | 594 | for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) |
593 | skb_queue_len(&sta->tx_filtered[ac]); | 595 | buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) + |
596 | skb_queue_len(&sta->tx_filtered[ac]); | ||
594 | 597 | ||
595 | if (!has_buffered && !buffer_local) | 598 | if (!has_buffered && !buffer_local) |
596 | return; | 599 | return; |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d7bdc4b97dde..d7504ab61a34 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -958,9 +958,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
958 | struct cfg80211_bss *cbss = ifmgd->associated; | 958 | struct cfg80211_bss *cbss = ifmgd->associated; |
959 | struct ieee80211_chanctx *chanctx; | 959 | struct ieee80211_chanctx *chanctx; |
960 | enum ieee80211_band current_band; | 960 | enum ieee80211_band current_band; |
961 | u8 count; | 961 | struct ieee80211_csa_ie csa_ie; |
962 | u8 mode; | ||
963 | struct cfg80211_chan_def new_chandef = {}; | ||
964 | int res; | 962 | int res; |
965 | 963 | ||
966 | sdata_assert_lock(sdata); | 964 | sdata_assert_lock(sdata); |
@@ -976,24 +974,24 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
976 | return; | 974 | return; |
977 | 975 | ||
978 | current_band = cbss->channel->band; | 976 | current_band = cbss->channel->band; |
977 | memset(&csa_ie, 0, sizeof(csa_ie)); | ||
979 | res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band, | 978 | res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band, |
980 | ifmgd->flags, | 979 | ifmgd->flags, |
981 | ifmgd->associated->bssid, &count, | 980 | ifmgd->associated->bssid, &csa_ie); |
982 | &mode, &new_chandef); | ||
983 | if (res < 0) | 981 | if (res < 0) |
984 | ieee80211_queue_work(&local->hw, | 982 | ieee80211_queue_work(&local->hw, |
985 | &ifmgd->csa_connection_drop_work); | 983 | &ifmgd->csa_connection_drop_work); |
986 | if (res) | 984 | if (res) |
987 | return; | 985 | return; |
988 | 986 | ||
989 | if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef, | 987 | if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef, |
990 | IEEE80211_CHAN_DISABLED)) { | 988 | IEEE80211_CHAN_DISABLED)) { |
991 | sdata_info(sdata, | 989 | sdata_info(sdata, |
992 | "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", | 990 | "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", |
993 | ifmgd->associated->bssid, | 991 | ifmgd->associated->bssid, |
994 | new_chandef.chan->center_freq, | 992 | csa_ie.chandef.chan->center_freq, |
995 | new_chandef.width, new_chandef.center_freq1, | 993 | csa_ie.chandef.width, csa_ie.chandef.center_freq1, |
996 | new_chandef.center_freq2); | 994 | csa_ie.chandef.center_freq2); |
997 | ieee80211_queue_work(&local->hw, | 995 | ieee80211_queue_work(&local->hw, |
998 | &ifmgd->csa_connection_drop_work); | 996 | &ifmgd->csa_connection_drop_work); |
999 | return; | 997 | return; |
@@ -1037,9 +1035,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
1037 | } | 1035 | } |
1038 | mutex_unlock(&local->chanctx_mtx); | 1036 | mutex_unlock(&local->chanctx_mtx); |
1039 | 1037 | ||
1040 | local->csa_chandef = new_chandef; | 1038 | local->csa_chandef = csa_ie.chandef; |
1041 | 1039 | ||
1042 | if (mode) | 1040 | if (csa_ie.mode) |
1043 | ieee80211_stop_queues_by_reason(&local->hw, | 1041 | ieee80211_stop_queues_by_reason(&local->hw, |
1044 | IEEE80211_MAX_QUEUE_MAP, | 1042 | IEEE80211_MAX_QUEUE_MAP, |
1045 | IEEE80211_QUEUE_STOP_REASON_CSA); | 1043 | IEEE80211_QUEUE_STOP_REASON_CSA); |
@@ -1048,9 +1046,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
1048 | /* use driver's channel switch callback */ | 1046 | /* use driver's channel switch callback */ |
1049 | struct ieee80211_channel_switch ch_switch = { | 1047 | struct ieee80211_channel_switch ch_switch = { |
1050 | .timestamp = timestamp, | 1048 | .timestamp = timestamp, |
1051 | .block_tx = mode, | 1049 | .block_tx = csa_ie.mode, |
1052 | .chandef = new_chandef, | 1050 | .chandef = csa_ie.chandef, |
1053 | .count = count, | 1051 | .count = csa_ie.count, |
1054 | }; | 1052 | }; |
1055 | 1053 | ||
1056 | drv_channel_switch(local, &ch_switch); | 1054 | drv_channel_switch(local, &ch_switch); |
@@ -1058,11 +1056,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
1058 | } | 1056 | } |
1059 | 1057 | ||
1060 | /* channel switch handled in software */ | 1058 | /* channel switch handled in software */ |
1061 | if (count <= 1) | 1059 | if (csa_ie.count <= 1) |
1062 | ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work); | 1060 | ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work); |
1063 | else | 1061 | else |
1064 | mod_timer(&ifmgd->chswitch_timer, | 1062 | mod_timer(&ifmgd->chswitch_timer, |
1065 | TU_TO_EXP_TIME(count * cbss->beacon_interval)); | 1063 | TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval)); |
1066 | } | 1064 | } |
1067 | 1065 | ||
1068 | static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, | 1066 | 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) | |||
3500 | ieee80211_beacon_connection_loss_work); | 3498 | ieee80211_beacon_connection_loss_work); |
3501 | INIT_WORK(&ifmgd->csa_connection_drop_work, | 3499 | INIT_WORK(&ifmgd->csa_connection_drop_work, |
3502 | ieee80211_csa_connection_drop_work); | 3500 | ieee80211_csa_connection_drop_work); |
3503 | INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work); | 3501 | INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work); |
3504 | setup_timer(&ifmgd->timer, ieee80211_sta_timer, | 3502 | setup_timer(&ifmgd->timer, ieee80211_sta_timer, |
3505 | (unsigned long) sdata); | 3503 | (unsigned long) sdata); |
3506 | setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, | 3504 | setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, |
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 0011ac815097..caecef870c0e 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -2593,13 +2593,16 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) | |||
2593 | break; | 2593 | break; |
2594 | 2594 | ||
2595 | if (sdata->vif.type != NL80211_IFTYPE_STATION && | 2595 | if (sdata->vif.type != NL80211_IFTYPE_STATION && |
2596 | sdata->vif.type != NL80211_IFTYPE_ADHOC) | 2596 | sdata->vif.type != NL80211_IFTYPE_ADHOC && |
2597 | sdata->vif.type != NL80211_IFTYPE_MESH_POINT) | ||
2597 | break; | 2598 | break; |
2598 | 2599 | ||
2599 | if (sdata->vif.type == NL80211_IFTYPE_STATION) | 2600 | if (sdata->vif.type == NL80211_IFTYPE_STATION) |
2600 | bssid = sdata->u.mgd.bssid; | 2601 | bssid = sdata->u.mgd.bssid; |
2601 | else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) | 2602 | else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) |
2602 | bssid = sdata->u.ibss.bssid; | 2603 | bssid = sdata->u.ibss.bssid; |
2604 | else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) | ||
2605 | bssid = mgmt->sa; | ||
2603 | else | 2606 | else |
2604 | break; | 2607 | break; |
2605 | 2608 | ||
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 921597e279a3..a40da20b32e0 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c | |||
@@ -24,8 +24,8 @@ | |||
24 | int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, | 24 | int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, |
25 | struct ieee802_11_elems *elems, bool beacon, | 25 | struct ieee802_11_elems *elems, bool beacon, |
26 | enum ieee80211_band current_band, | 26 | enum ieee80211_band current_band, |
27 | u32 sta_flags, u8 *bssid, u8 *count, u8 *mode, | 27 | u32 sta_flags, u8 *bssid, |
28 | struct cfg80211_chan_def *new_chandef) | 28 | struct ieee80211_csa_ie *csa_ie) |
29 | { | 29 | { |
30 | enum ieee80211_band new_band; | 30 | enum ieee80211_band new_band; |
31 | int new_freq; | 31 | int new_freq; |
@@ -62,18 +62,24 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, | |||
62 | return -EINVAL; | 62 | return -EINVAL; |
63 | } | 63 | } |
64 | new_chan_no = elems->ext_chansw_ie->new_ch_num; | 64 | new_chan_no = elems->ext_chansw_ie->new_ch_num; |
65 | *count = elems->ext_chansw_ie->count; | 65 | csa_ie->count = elems->ext_chansw_ie->count; |
66 | *mode = elems->ext_chansw_ie->mode; | 66 | csa_ie->mode = elems->ext_chansw_ie->mode; |
67 | } else if (elems->ch_switch_ie) { | 67 | } else if (elems->ch_switch_ie) { |
68 | new_band = current_band; | 68 | new_band = current_band; |
69 | new_chan_no = elems->ch_switch_ie->new_ch_num; | 69 | new_chan_no = elems->ch_switch_ie->new_ch_num; |
70 | *count = elems->ch_switch_ie->count; | 70 | csa_ie->count = elems->ch_switch_ie->count; |
71 | *mode = elems->ch_switch_ie->mode; | 71 | csa_ie->mode = elems->ch_switch_ie->mode; |
72 | } else { | 72 | } else { |
73 | /* nothing here we understand */ | 73 | /* nothing here we understand */ |
74 | return 1; | 74 | return 1; |
75 | } | 75 | } |
76 | 76 | ||
77 | /* Mesh Channel Switch Parameters Element */ | ||
78 | if (elems->mesh_chansw_params_ie) { | ||
79 | csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl; | ||
80 | csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags; | ||
81 | } | ||
82 | |||
77 | new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); | 83 | new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); |
78 | new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); | 84 | new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); |
79 | if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) { | 85 | 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, | |||
103 | default: | 109 | default: |
104 | /* secondary_channel_offset was present but is invalid */ | 110 | /* secondary_channel_offset was present but is invalid */ |
105 | case IEEE80211_HT_PARAM_CHA_SEC_NONE: | 111 | case IEEE80211_HT_PARAM_CHA_SEC_NONE: |
106 | cfg80211_chandef_create(new_chandef, new_chan, | 112 | cfg80211_chandef_create(&csa_ie->chandef, new_chan, |
107 | NL80211_CHAN_HT20); | 113 | NL80211_CHAN_HT20); |
108 | break; | 114 | break; |
109 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: | 115 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: |
110 | cfg80211_chandef_create(new_chandef, new_chan, | 116 | cfg80211_chandef_create(&csa_ie->chandef, new_chan, |
111 | NL80211_CHAN_HT40PLUS); | 117 | NL80211_CHAN_HT40PLUS); |
112 | break; | 118 | break; |
113 | case IEEE80211_HT_PARAM_CHA_SEC_BELOW: | 119 | case IEEE80211_HT_PARAM_CHA_SEC_BELOW: |
114 | cfg80211_chandef_create(new_chandef, new_chan, | 120 | cfg80211_chandef_create(&csa_ie->chandef, new_chan, |
115 | NL80211_CHAN_HT40MINUS); | 121 | NL80211_CHAN_HT40MINUS); |
116 | break; | 122 | break; |
117 | case -1: | 123 | case -1: |
118 | cfg80211_chandef_create(new_chandef, new_chan, | 124 | cfg80211_chandef_create(&csa_ie->chandef, new_chan, |
119 | NL80211_CHAN_NO_HT); | 125 | NL80211_CHAN_NO_HT); |
120 | /* keep width for 5/10 MHz channels */ | 126 | /* keep width for 5/10 MHz channels */ |
121 | switch (sdata->vif.bss_conf.chandef.width) { | 127 | switch (sdata->vif.bss_conf.chandef.width) { |
122 | case NL80211_CHAN_WIDTH_5: | 128 | case NL80211_CHAN_WIDTH_5: |
123 | case NL80211_CHAN_WIDTH_10: | 129 | case NL80211_CHAN_WIDTH_10: |
124 | new_chandef->width = sdata->vif.bss_conf.chandef.width; | 130 | csa_ie->chandef.width = |
131 | sdata->vif.bss_conf.chandef.width; | ||
125 | break; | 132 | break; |
126 | default: | 133 | default: |
127 | break; | 134 | break; |
@@ -171,13 +178,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, | |||
171 | /* if VHT data is there validate & use it */ | 178 | /* if VHT data is there validate & use it */ |
172 | if (new_vht_chandef.chan) { | 179 | if (new_vht_chandef.chan) { |
173 | if (!cfg80211_chandef_compatible(&new_vht_chandef, | 180 | if (!cfg80211_chandef_compatible(&new_vht_chandef, |
174 | new_chandef)) { | 181 | &csa_ie->chandef)) { |
175 | sdata_info(sdata, | 182 | sdata_info(sdata, |
176 | "BSS %pM: CSA has inconsistent channel data, disconnecting\n", | 183 | "BSS %pM: CSA has inconsistent channel data, disconnecting\n", |
177 | bssid); | 184 | bssid); |
178 | return -EINVAL; | 185 | return -EINVAL; |
179 | } | 186 | } |
180 | *new_chandef = new_vht_chandef; | 187 | csa_ie->chandef = new_vht_chandef; |
181 | } | 188 | } |
182 | 189 | ||
183 | return 0; | 190 | return 0; |
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index aeb967a0aeed..1eb66e26e49d 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c | |||
@@ -385,6 +385,30 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, | |||
385 | sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); | 385 | sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); |
386 | 386 | ||
387 | sta->sta.smps_mode = IEEE80211_SMPS_OFF; | 387 | sta->sta.smps_mode = IEEE80211_SMPS_OFF; |
388 | if (sdata->vif.type == NL80211_IFTYPE_AP || | ||
389 | sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { | ||
390 | struct ieee80211_supported_band *sband = | ||
391 | local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; | ||
392 | u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> | ||
393 | IEEE80211_HT_CAP_SM_PS_SHIFT; | ||
394 | /* | ||
395 | * Assume that hostapd advertises our caps in the beacon and | ||
396 | * this is the known_smps_mode for a station that just assciated | ||
397 | */ | ||
398 | switch (smps) { | ||
399 | case WLAN_HT_SMPS_CONTROL_DISABLED: | ||
400 | sta->known_smps_mode = IEEE80211_SMPS_OFF; | ||
401 | break; | ||
402 | case WLAN_HT_SMPS_CONTROL_STATIC: | ||
403 | sta->known_smps_mode = IEEE80211_SMPS_STATIC; | ||
404 | break; | ||
405 | case WLAN_HT_SMPS_CONTROL_DYNAMIC: | ||
406 | sta->known_smps_mode = IEEE80211_SMPS_DYNAMIC; | ||
407 | break; | ||
408 | default: | ||
409 | WARN_ON(1); | ||
410 | } | ||
411 | } | ||
388 | 412 | ||
389 | sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); | 413 | sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); |
390 | 414 | ||
@@ -1069,6 +1093,19 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) | |||
1069 | 1093 | ||
1070 | ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta); | 1094 | ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta); |
1071 | 1095 | ||
1096 | /* This station just woke up and isn't aware of our SMPS state */ | ||
1097 | if (!ieee80211_smps_is_restrictive(sta->known_smps_mode, | ||
1098 | sdata->smps_mode) && | ||
1099 | sta->known_smps_mode != sdata->bss->req_smps && | ||
1100 | sta_info_tx_streams(sta) != 1) { | ||
1101 | ht_dbg(sdata, | ||
1102 | "%pM just woke up and MIMO capable - update SMPS\n", | ||
1103 | sta->sta.addr); | ||
1104 | ieee80211_send_smps_action(sdata, sdata->bss->req_smps, | ||
1105 | sta->sta.addr, | ||
1106 | sdata->vif.bss_conf.bssid); | ||
1107 | } | ||
1108 | |||
1072 | local->total_ps_buffered -= buffered; | 1109 | local->total_ps_buffered -= buffered; |
1073 | 1110 | ||
1074 | sta_info_recalc_tim(sta); | 1111 | sta_info_recalc_tim(sta); |
@@ -1520,3 +1557,38 @@ int sta_info_move_state(struct sta_info *sta, | |||
1520 | 1557 | ||
1521 | return 0; | 1558 | return 0; |
1522 | } | 1559 | } |
1560 | |||
1561 | u8 sta_info_tx_streams(struct sta_info *sta) | ||
1562 | { | ||
1563 | struct ieee80211_sta_ht_cap *ht_cap = &sta->sta.ht_cap; | ||
1564 | u8 rx_streams; | ||
1565 | |||
1566 | if (!sta->sta.ht_cap.ht_supported) | ||
1567 | return 1; | ||
1568 | |||
1569 | if (sta->sta.vht_cap.vht_supported) { | ||
1570 | int i; | ||
1571 | u16 tx_mcs_map = | ||
1572 | le16_to_cpu(sta->sta.vht_cap.vht_mcs.tx_mcs_map); | ||
1573 | |||
1574 | for (i = 7; i >= 0; i--) | ||
1575 | if ((tx_mcs_map & (0x3 << (i * 2))) != | ||
1576 | IEEE80211_VHT_MCS_NOT_SUPPORTED) | ||
1577 | return i + 1; | ||
1578 | } | ||
1579 | |||
1580 | if (ht_cap->mcs.rx_mask[3]) | ||
1581 | rx_streams = 4; | ||
1582 | else if (ht_cap->mcs.rx_mask[2]) | ||
1583 | rx_streams = 3; | ||
1584 | else if (ht_cap->mcs.rx_mask[1]) | ||
1585 | rx_streams = 2; | ||
1586 | else | ||
1587 | rx_streams = 1; | ||
1588 | |||
1589 | if (!(ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF)) | ||
1590 | return rx_streams; | ||
1591 | |||
1592 | return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) | ||
1593 | >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; | ||
1594 | } | ||
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 4208dbd5861f..3ef06a26b9cb 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h | |||
@@ -301,6 +301,8 @@ struct sta_ampdu_mlme { | |||
301 | * @chains: chains ever used for RX from this station | 301 | * @chains: chains ever used for RX from this station |
302 | * @chain_signal_last: last signal (per chain) | 302 | * @chain_signal_last: last signal (per chain) |
303 | * @chain_signal_avg: signal average (per chain) | 303 | * @chain_signal_avg: signal average (per chain) |
304 | * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for | ||
305 | * AP only. | ||
304 | */ | 306 | */ |
305 | struct sta_info { | 307 | struct sta_info { |
306 | /* General information, mostly static */ | 308 | /* General information, mostly static */ |
@@ -411,6 +413,8 @@ struct sta_info { | |||
411 | unsigned int lost_packets; | 413 | unsigned int lost_packets; |
412 | unsigned int beacon_loss_count; | 414 | unsigned int beacon_loss_count; |
413 | 415 | ||
416 | enum ieee80211_smps_mode known_smps_mode; | ||
417 | |||
414 | /* keep last! */ | 418 | /* keep last! */ |
415 | struct ieee80211_sta sta; | 419 | struct ieee80211_sta sta; |
416 | }; | 420 | }; |
@@ -613,6 +617,7 @@ void sta_set_rate_info_rx(struct sta_info *sta, | |||
613 | struct rate_info *rinfo); | 617 | struct rate_info *rinfo); |
614 | void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, | 618 | void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, |
615 | unsigned long exp_time); | 619 | unsigned long exp_time); |
620 | u8 sta_info_tx_streams(struct sta_info *sta); | ||
616 | 621 | ||
617 | void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); | 622 | void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); |
618 | void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); | 623 | void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 78dc2e99027e..52a152b01b06 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -194,29 +194,36 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) | |||
194 | if (ieee80211_is_action(mgmt->frame_control) && | 194 | if (ieee80211_is_action(mgmt->frame_control) && |
195 | mgmt->u.action.category == WLAN_CATEGORY_HT && | 195 | mgmt->u.action.category == WLAN_CATEGORY_HT && |
196 | mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS && | 196 | mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS && |
197 | sdata->vif.type == NL80211_IFTYPE_STATION && | ||
198 | ieee80211_sdata_running(sdata)) { | 197 | ieee80211_sdata_running(sdata)) { |
199 | /* | 198 | enum ieee80211_smps_mode smps_mode; |
200 | * This update looks racy, but isn't -- if we come | 199 | |
201 | * here we've definitely got a station that we're | ||
202 | * talking to, and on a managed interface that can | ||
203 | * only be the AP. And the only other place updating | ||
204 | * this variable in managed mode is before association. | ||
205 | */ | ||
206 | switch (mgmt->u.action.u.ht_smps.smps_control) { | 200 | switch (mgmt->u.action.u.ht_smps.smps_control) { |
207 | case WLAN_HT_SMPS_CONTROL_DYNAMIC: | 201 | case WLAN_HT_SMPS_CONTROL_DYNAMIC: |
208 | sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; | 202 | smps_mode = IEEE80211_SMPS_DYNAMIC; |
209 | break; | 203 | break; |
210 | case WLAN_HT_SMPS_CONTROL_STATIC: | 204 | case WLAN_HT_SMPS_CONTROL_STATIC: |
211 | sdata->smps_mode = IEEE80211_SMPS_STATIC; | 205 | smps_mode = IEEE80211_SMPS_STATIC; |
212 | break; | 206 | break; |
213 | case WLAN_HT_SMPS_CONTROL_DISABLED: | 207 | case WLAN_HT_SMPS_CONTROL_DISABLED: |
214 | default: /* shouldn't happen since we don't send that */ | 208 | default: /* shouldn't happen since we don't send that */ |
215 | sdata->smps_mode = IEEE80211_SMPS_OFF; | 209 | smps_mode = IEEE80211_SMPS_OFF; |
216 | break; | 210 | break; |
217 | } | 211 | } |
218 | 212 | ||
219 | ieee80211_queue_work(&local->hw, &sdata->recalc_smps); | 213 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
214 | /* | ||
215 | * This update looks racy, but isn't -- if we come | ||
216 | * here we've definitely got a station that we're | ||
217 | * talking to, and on a managed interface that can | ||
218 | * only be the AP. And the only other place updating | ||
219 | * this variable in managed mode is before association. | ||
220 | */ | ||
221 | sdata->smps_mode = smps_mode; | ||
222 | ieee80211_queue_work(&local->hw, &sdata->recalc_smps); | ||
223 | } else if (sdata->vif.type == NL80211_IFTYPE_AP || | ||
224 | sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { | ||
225 | sta->known_smps_mode = smps_mode; | ||
226 | } | ||
220 | } | 227 | } |
221 | } | 228 | } |
222 | 229 | ||
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 9993fcb19ecd..c558b246ef00 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -1367,6 +1367,35 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) | |||
1367 | return 0; | 1367 | return 0; |
1368 | } | 1368 | } |
1369 | 1369 | ||
1370 | bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, | ||
1371 | struct ieee80211_vif *vif, struct sk_buff *skb, | ||
1372 | int band, struct ieee80211_sta **sta) | ||
1373 | { | ||
1374 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | ||
1375 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||
1376 | struct ieee80211_tx_data tx; | ||
1377 | |||
1378 | if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP) | ||
1379 | return false; | ||
1380 | |||
1381 | info->band = band; | ||
1382 | info->control.vif = vif; | ||
1383 | info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)]; | ||
1384 | |||
1385 | if (invoke_tx_handlers(&tx)) | ||
1386 | return false; | ||
1387 | |||
1388 | if (sta) { | ||
1389 | if (tx.sta) | ||
1390 | *sta = &tx.sta->sta; | ||
1391 | else | ||
1392 | *sta = NULL; | ||
1393 | } | ||
1394 | |||
1395 | return true; | ||
1396 | } | ||
1397 | EXPORT_SYMBOL(ieee80211_tx_prepare_skb); | ||
1398 | |||
1370 | /* | 1399 | /* |
1371 | * Returns false if the frame couldn't be transmitted but was queued instead. | 1400 | * Returns false if the frame couldn't be transmitted but was queued instead. |
1372 | */ | 1401 | */ |
@@ -2370,6 +2399,10 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata, | |||
2370 | beacon_data = beacon->head; | 2399 | beacon_data = beacon->head; |
2371 | beacon_data_len = beacon->head_len; | 2400 | beacon_data_len = beacon->head_len; |
2372 | break; | 2401 | break; |
2402 | case NL80211_IFTYPE_MESH_POINT: | ||
2403 | beacon_data = beacon->head; | ||
2404 | beacon_data_len = beacon->head_len; | ||
2405 | break; | ||
2373 | default: | 2406 | default: |
2374 | return; | 2407 | return; |
2375 | } | 2408 | } |
@@ -2426,6 +2459,15 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) | |||
2426 | 2459 | ||
2427 | beacon_data = beacon->head; | 2460 | beacon_data = beacon->head; |
2428 | beacon_data_len = beacon->head_len; | 2461 | beacon_data_len = beacon->head_len; |
2462 | } else if (vif->type == NL80211_IFTYPE_MESH_POINT) { | ||
2463 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
2464 | |||
2465 | beacon = rcu_dereference(ifmsh->beacon); | ||
2466 | if (!beacon) | ||
2467 | goto out; | ||
2468 | |||
2469 | beacon_data = beacon->head; | ||
2470 | beacon_data_len = beacon->head_len; | ||
2429 | } else { | 2471 | } else { |
2430 | WARN_ON(1); | 2472 | WARN_ON(1); |
2431 | goto out; | 2473 | goto out; |
@@ -2531,6 +2573,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, | |||
2531 | if (!bcn) | 2573 | if (!bcn) |
2532 | goto out; | 2574 | goto out; |
2533 | 2575 | ||
2576 | if (sdata->vif.csa_active) | ||
2577 | ieee80211_update_csa(sdata, bcn); | ||
2578 | |||
2534 | if (ifmsh->sync_ops) | 2579 | if (ifmsh->sync_ops) |
2535 | ifmsh->sync_ops->adjust_tbtt( | 2580 | ifmsh->sync_ops->adjust_tbtt( |
2536 | sdata); | 2581 | sdata); |
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index aefb9d5b9620..592a18171f95 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -300,9 +300,6 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) | |||
300 | if (!sdata->dev) | 300 | if (!sdata->dev) |
301 | continue; | 301 | continue; |
302 | 302 | ||
303 | if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) | ||
304 | continue; | ||
305 | |||
306 | if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE && | 303 | if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE && |
307 | local->queue_stop_reasons[sdata->vif.cab_queue] != 0) | 304 | local->queue_stop_reasons[sdata->vif.cab_queue] != 0) |
308 | continue; | 305 | continue; |
@@ -743,6 +740,7 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, | |||
743 | case WLAN_EID_TIMEOUT_INTERVAL: | 740 | case WLAN_EID_TIMEOUT_INTERVAL: |
744 | case WLAN_EID_SECONDARY_CHANNEL_OFFSET: | 741 | case WLAN_EID_SECONDARY_CHANNEL_OFFSET: |
745 | case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: | 742 | case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: |
743 | case WLAN_EID_CHAN_SWITCH_PARAM: | ||
746 | /* | 744 | /* |
747 | * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible | 745 | * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible |
748 | * that if the content gets bigger it might be needed more than once | 746 | * 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, | |||
908 | } | 906 | } |
909 | elems->sec_chan_offs = (void *)pos; | 907 | elems->sec_chan_offs = (void *)pos; |
910 | break; | 908 | break; |
909 | case WLAN_EID_CHAN_SWITCH_PARAM: | ||
910 | if (elen != | ||
911 | sizeof(*elems->mesh_chansw_params_ie)) { | ||
912 | elem_parse_failed = true; | ||
913 | break; | ||
914 | } | ||
915 | elems->mesh_chansw_params_ie = (void *)pos; | ||
916 | break; | ||
911 | case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: | 917 | case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: |
912 | if (!action || | 918 | if (!action || |
913 | elen != sizeof(*elems->wide_bw_chansw_ie)) { | 919 | elen != sizeof(*elems->wide_bw_chansw_ie)) { |
@@ -2354,3 +2360,115 @@ u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) | |||
2354 | 2360 | ||
2355 | return ret; | 2361 | return ret; |
2356 | } | 2362 | } |
2363 | |||
2364 | /* | ||
2365 | * Returns true if smps_mode_new is strictly more restrictive than | ||
2366 | * smps_mode_old. | ||
2367 | */ | ||
2368 | bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, | ||
2369 | enum ieee80211_smps_mode smps_mode_new) | ||
2370 | { | ||
2371 | if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC || | ||
2372 | smps_mode_new == IEEE80211_SMPS_AUTOMATIC)) | ||
2373 | return false; | ||
2374 | |||
2375 | switch (smps_mode_old) { | ||
2376 | case IEEE80211_SMPS_STATIC: | ||
2377 | return false; | ||
2378 | case IEEE80211_SMPS_DYNAMIC: | ||
2379 | return smps_mode_new == IEEE80211_SMPS_STATIC; | ||
2380 | case IEEE80211_SMPS_OFF: | ||
2381 | return smps_mode_new != IEEE80211_SMPS_OFF; | ||
2382 | default: | ||
2383 | WARN_ON(1); | ||
2384 | } | ||
2385 | |||
2386 | return false; | ||
2387 | } | ||
2388 | |||
2389 | int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, | ||
2390 | struct cfg80211_csa_settings *csa_settings) | ||
2391 | { | ||
2392 | struct sk_buff *skb; | ||
2393 | struct ieee80211_mgmt *mgmt; | ||
2394 | struct ieee80211_local *local = sdata->local; | ||
2395 | int freq; | ||
2396 | int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) + | ||
2397 | sizeof(mgmt->u.action.u.chan_switch); | ||
2398 | u8 *pos; | ||
2399 | |||
2400 | if (sdata->vif.type != NL80211_IFTYPE_ADHOC && | ||
2401 | sdata->vif.type != NL80211_IFTYPE_MESH_POINT) | ||
2402 | return -EOPNOTSUPP; | ||
2403 | |||
2404 | skb = dev_alloc_skb(local->tx_headroom + hdr_len + | ||
2405 | 5 + /* channel switch announcement element */ | ||
2406 | 3 + /* secondary channel offset element */ | ||
2407 | 8); /* mesh channel switch parameters element */ | ||
2408 | if (!skb) | ||
2409 | return -ENOMEM; | ||
2410 | |||
2411 | skb_reserve(skb, local->tx_headroom); | ||
2412 | mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len); | ||
2413 | memset(mgmt, 0, hdr_len); | ||
2414 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | ||
2415 | IEEE80211_STYPE_ACTION); | ||
2416 | |||
2417 | eth_broadcast_addr(mgmt->da); | ||
2418 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | ||
2419 | if (ieee80211_vif_is_mesh(&sdata->vif)) { | ||
2420 | memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); | ||
2421 | } else { | ||
2422 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | ||
2423 | memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); | ||
2424 | } | ||
2425 | mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; | ||
2426 | mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH; | ||
2427 | pos = skb_put(skb, 5); | ||
2428 | *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */ | ||
2429 | *pos++ = 3; /* IE length */ | ||
2430 | *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */ | ||
2431 | freq = csa_settings->chandef.chan->center_freq; | ||
2432 | *pos++ = ieee80211_frequency_to_channel(freq); /* channel */ | ||
2433 | *pos++ = csa_settings->count; /* count */ | ||
2434 | |||
2435 | if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) { | ||
2436 | enum nl80211_channel_type ch_type; | ||
2437 | |||
2438 | skb_put(skb, 3); | ||
2439 | *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */ | ||
2440 | *pos++ = 1; /* IE length */ | ||
2441 | ch_type = cfg80211_get_chandef_type(&csa_settings->chandef); | ||
2442 | if (ch_type == NL80211_CHAN_HT40PLUS) | ||
2443 | *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; | ||
2444 | else | ||
2445 | *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW; | ||
2446 | } | ||
2447 | |||
2448 | if (ieee80211_vif_is_mesh(&sdata->vif)) { | ||
2449 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | ||
2450 | __le16 pre_value; | ||
2451 | |||
2452 | skb_put(skb, 8); | ||
2453 | *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */ | ||
2454 | *pos++ = 6; /* IE length */ | ||
2455 | *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL; /* Mesh TTL */ | ||
2456 | *pos = 0x00; /* Mesh Flag: Tx Restrict, Initiator, Reason */ | ||
2457 | *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; | ||
2458 | *pos++ |= csa_settings->block_tx ? | ||
2459 | WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; | ||
2460 | put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */ | ||
2461 | pos += 2; | ||
2462 | if (!ifmsh->pre_value) | ||
2463 | ifmsh->pre_value = 1; | ||
2464 | else | ||
2465 | ifmsh->pre_value++; | ||
2466 | pre_value = cpu_to_le16(ifmsh->pre_value); | ||
2467 | memcpy(pos, &pre_value, 2); /* Precedence Value */ | ||
2468 | pos += 2; | ||
2469 | ifmsh->chsw_init = true; | ||
2470 | } | ||
2471 | |||
2472 | ieee80211_tx_skb(sdata, skb); | ||
2473 | return 0; | ||
2474 | } | ||
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index c9edfcb7a13b..d65728220763 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c | |||
@@ -301,22 +301,16 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) | |||
301 | } | 301 | } |
302 | 302 | ||
303 | 303 | ||
304 | static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, | 304 | static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad, |
305 | int encrypted) | 305 | int encrypted) |
306 | { | 306 | { |
307 | __le16 mask_fc; | 307 | __le16 mask_fc; |
308 | int a4_included, mgmt; | 308 | int a4_included, mgmt; |
309 | u8 qos_tid; | 309 | u8 qos_tid; |
310 | u8 *b_0, *aad; | 310 | u16 len_a; |
311 | u16 data_len, len_a; | ||
312 | unsigned int hdrlen; | 311 | unsigned int hdrlen; |
313 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | 312 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; |
314 | 313 | ||
315 | memset(scratch, 0, 6 * AES_BLOCK_SIZE); | ||
316 | |||
317 | b_0 = scratch + 3 * AES_BLOCK_SIZE; | ||
318 | aad = scratch + 4 * AES_BLOCK_SIZE; | ||
319 | |||
320 | /* | 314 | /* |
321 | * Mask FC: zero subtype b4 b5 b6 (if not mgmt) | 315 | * Mask FC: zero subtype b4 b5 b6 (if not mgmt) |
322 | * Retry, PwrMgt, MoreData; set Protected | 316 | * Retry, PwrMgt, MoreData; set Protected |
@@ -338,20 +332,21 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, | |||
338 | else | 332 | else |
339 | qos_tid = 0; | 333 | qos_tid = 0; |
340 | 334 | ||
341 | data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN; | 335 | /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC |
342 | if (encrypted) | 336 | * mode authentication are not allowed to collide, yet both are derived |
343 | data_len -= IEEE80211_CCMP_MIC_LEN; | 337 | * from this vector b_0. We only set L := 1 here to indicate that the |
338 | * data size can be represented in (L+1) bytes. The CCM layer will take | ||
339 | * care of storing the data length in the top (L+1) bytes and setting | ||
340 | * and clearing the other bits as is required to derive the two IVs. | ||
341 | */ | ||
342 | b_0[0] = 0x1; | ||
344 | 343 | ||
345 | /* First block, b_0 */ | ||
346 | b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */ | ||
347 | /* Nonce: Nonce Flags | A2 | PN | 344 | /* Nonce: Nonce Flags | A2 | PN |
348 | * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7) | 345 | * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7) |
349 | */ | 346 | */ |
350 | b_0[1] = qos_tid | (mgmt << 4); | 347 | b_0[1] = qos_tid | (mgmt << 4); |
351 | memcpy(&b_0[2], hdr->addr2, ETH_ALEN); | 348 | memcpy(&b_0[2], hdr->addr2, ETH_ALEN); |
352 | memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN); | 349 | memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN); |
353 | /* l(m) */ | ||
354 | put_unaligned_be16(data_len, &b_0[14]); | ||
355 | 350 | ||
356 | /* AAD (extra authenticate-only data) / masked 802.11 header | 351 | /* AAD (extra authenticate-only data) / masked 802.11 header |
357 | * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ | 352 | * 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) | |||
407 | u8 *pos; | 402 | u8 *pos; |
408 | u8 pn[6]; | 403 | u8 pn[6]; |
409 | u64 pn64; | 404 | u64 pn64; |
410 | u8 scratch[6 * AES_BLOCK_SIZE]; | 405 | u8 aad[2 * AES_BLOCK_SIZE]; |
406 | u8 b_0[AES_BLOCK_SIZE]; | ||
411 | 407 | ||
412 | if (info->control.hw_key && | 408 | if (info->control.hw_key && |
413 | !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && | 409 | !(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) | |||
460 | return 0; | 456 | return 0; |
461 | 457 | ||
462 | pos += IEEE80211_CCMP_HDR_LEN; | 458 | pos += IEEE80211_CCMP_HDR_LEN; |
463 | ccmp_special_blocks(skb, pn, scratch, 0); | 459 | ccmp_special_blocks(skb, pn, b_0, aad, 0); |
464 | ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len, | 460 | ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, |
465 | pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN)); | 461 | skb_put(skb, IEEE80211_CCMP_MIC_LEN)); |
466 | 462 | ||
467 | return 0; | 463 | return 0; |
468 | } | 464 | } |
@@ -525,16 +521,16 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) | |||
525 | } | 521 | } |
526 | 522 | ||
527 | if (!(status->flag & RX_FLAG_DECRYPTED)) { | 523 | if (!(status->flag & RX_FLAG_DECRYPTED)) { |
528 | u8 scratch[6 * AES_BLOCK_SIZE]; | 524 | u8 aad[2 * AES_BLOCK_SIZE]; |
525 | u8 b_0[AES_BLOCK_SIZE]; | ||
529 | /* hardware didn't decrypt/verify MIC */ | 526 | /* hardware didn't decrypt/verify MIC */ |
530 | ccmp_special_blocks(skb, pn, scratch, 1); | 527 | ccmp_special_blocks(skb, pn, b_0, aad, 1); |
531 | 528 | ||
532 | if (ieee80211_aes_ccm_decrypt( | 529 | if (ieee80211_aes_ccm_decrypt( |
533 | key->u.ccmp.tfm, scratch, | 530 | key->u.ccmp.tfm, b_0, aad, |
534 | skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN, | 531 | skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN, |
535 | data_len, | 532 | data_len, |
536 | skb->data + skb->len - IEEE80211_CCMP_MIC_LEN, | 533 | skb->data + skb->len - IEEE80211_CCMP_MIC_LEN)) |
537 | skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN)) | ||
538 | return RX_DROP_UNUSABLE; | 534 | return RX_DROP_UNUSABLE; |
539 | } | 535 | } |
540 | 536 | ||