mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
Some new content already, notably:
- mac80211: major rework of station bandwidth handling,
fixing issues with lower capability than AP
- general: cleanups for EMLSR spec issues (drafts differed)
- ath9k: GPIO interface improvements
- ath12k: replace dynamic memory allocation in WMI RX path
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEpeA8sTs3M8SN2hR410qiO8sPaAAFAmnzRDMACgkQ10qiO8sP
aADD4A//QO8xam/2bs/xUs2jg/1UT3m2i9RWfJT7FNi4J1eCOsSbvc8ioqieNkUy
OA+TQnyeKziQAGB9k5R9LLbMvpRD3Z1LgSYwJdh+wjPFCiHWpr3pTtUh8q+/6vQ9
z/2ksPnrV4xlRSPmqq1zbELSb1nJgJ8CeEZiOD4DQUqN+bsALid9BflK+0LmzbTl
BgB0nuYUy49daIQe1yqg19qiHSStie/vwWzdpRU19rs27CSxxgGBeipa18a18dU8
TgG0x90YdNw2DibyeRi3cuD4VK3vUfGOBAJ59p8I+Ib8qODTVK9+m+DYGEXA62Zo
ky4KUu6EnBHTpDAftgXNqWVs/Yvv2ioNoVu1aIqoeAdCUF4IsOeViAjuntxdmFUo
g0eqpFF550xvvDqQIC1iAX/SL7MC8xKJocY7jqIy7aseVOwxVSuzbZ4lSp3QjS+6
ljtiVIWbwv9llOVhv5VIUWRloK8Ostycu2iQEAOLi5Hq8RDEk17aC4yu08ecDXGF
PpECs2gWCNkAJORSwh9vks/msJVuGqCrO946lCMGdMeL/9GJyzHDIdgEFRfd8tkB
dRQlY+gn9viyC+WVeVoIXxCWUKHFpmQvUUz3nF3NdxPBL1kljcpEJUO1cdkUmw6o
HkJuJ1gOlAqgI6n6XImIY4/17GPeNmOey3rz7njXzkn/FS/pjNs=
=cEhV
-----END PGP SIGNATURE-----
Merge tag 'wireless-next-2026-04-30' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next
Johannes Berg says:
====================
Some new content already, notably:
- mac80211: major rework of station bandwidth handling,
fixing issues with lower capability than AP
- general: cleanups for EMLSR spec issues (drafts differed)
- ath9k: GPIO interface improvements
- ath12k: replace dynamic memory allocation in WMI RX path
* tag 'wireless-next-2026-04-30' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next: (39 commits)
wifi: brcmsmac: phy_lcn: Remove dead code in wlc_lcnphy_radio_2064_channel_tune_4313()
wifi: mac80211: always allow transmitting null-data on TXQs
wifi: mac80211: use kstrtobool_from_user() in debugfs callbacks
wifi: cfg80211: validate cipher suite for NAN Data keys
wifi: nl80211: check link is beaconing for color change
wifi: mac80211: clarify an 802.11 VHT spec reference
wifi: mac80211: fix per-station PHY capability bandwidth
wifi: mac80211: clarify per-STA bandwidth handling
wifi: nl80211: always validate AP operation/PHY regulatory
wifi: cfg80211: provide HT/VHT operation for AP beacon
wifi: nl80211: reject too short HT/VHT/HE/EHT capability/operation
wifi: cfg80211: move AP HT/VHT/... operation to beacon info
wifi: nl80211: reject beacons with bad HE operation
wifi: cfg80211: remove HE/SAE H2E required fields
wifi: mac80211: remove ieee80211_sta_cur_vht_bw()
wifi: mac80211: clean up ieee80211_sta_cap_rx_bw()
wifi: mac80211: clean up initial STA NSS/bandwidth handling
wifi: mac80211: clean up STA NSS handling
wifi: mac80211: simplify ieee80211_sta_rx_bw_to_chan_width()
wifi: nl80211: document channel opmode change channel width
...
====================
Link: https://patch.msgid.link/20260430120304.249081-3-johannes@sipsolutions.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
6855a52318
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio/generic.h>
|
||||
#include <linux/gpio/machine.h> /* For WLAN GPIOs */
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
|
@ -214,6 +215,56 @@ static const struct of_device_id ath79_gpio_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, ath79_gpio_of_match);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ATH9K_AHB)
|
||||
/*
|
||||
* This registers all of the ath79k GPIOs as descriptors to be picked
|
||||
* directly from the ATH79K wifi driver if the two are jitted together
|
||||
* in the same SoC.
|
||||
*/
|
||||
#define ATH79K_WIFI_DESCS 32
|
||||
static int ath79_gpio_register_wifi_descriptors(struct device *dev,
|
||||
const char *label)
|
||||
{
|
||||
struct gpiod_lookup_table *lookup;
|
||||
int i;
|
||||
|
||||
/* Create a gpiod lookup using gpiochip-local offsets + 1 for NULL */
|
||||
lookup = devm_kzalloc(dev,
|
||||
struct_size(lookup, table, ATH79K_WIFI_DESCS + 1),
|
||||
GFP_KERNEL);
|
||||
if (!lookup)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Ugly system-wide lookup for the NULL device: we know this
|
||||
* is already NULL but explicitly assign it here for people to
|
||||
* know what is going on. (Yes this is an ugly legacy hack, live
|
||||
* with it.)
|
||||
*/
|
||||
lookup->dev_id = NULL;
|
||||
|
||||
for (i = 0; i < ATH79K_WIFI_DESCS; i++) {
|
||||
lookup->table[i] =
|
||||
/*
|
||||
* Set the HW offset on the chip and the lookup
|
||||
* index to the same value, so looking up index 0
|
||||
* will get HW offset 0, index 1 HW offset 1 etc.
|
||||
*/
|
||||
GPIO_LOOKUP_IDX(label, i, "ath9k", i, GPIO_ACTIVE_HIGH);
|
||||
}
|
||||
|
||||
gpiod_add_lookup_table(lookup);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int ath79_gpio_register_wifi_descriptors(struct device *dev,
|
||||
const char *label)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ath79_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_generic_chip_config config;
|
||||
|
|
@ -276,7 +327,11 @@ static int ath79_gpio_probe(struct platform_device *pdev)
|
|||
girq->handler = handle_simple_irq;
|
||||
}
|
||||
|
||||
return devm_gpiochip_add_data(dev, &ctrl->chip.gc, ctrl);
|
||||
err = devm_gpiochip_add_data(dev, &ctrl->chip.gc, ctrl);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return ath79_gpio_register_wifi_descriptors(dev, ctrl->chip.gc.label);
|
||||
}
|
||||
|
||||
static struct platform_driver ath79_gpio_driver = {
|
||||
|
|
|
|||
|
|
@ -583,31 +583,36 @@ static int ath12k_ahb_config_ext_irq(struct ath12k_base *ab)
|
|||
netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi,
|
||||
ath12k_ahb_ext_grp_napi_poll);
|
||||
|
||||
for (j = 0; j < ATH12K_EXT_IRQ_NUM_MAX; j++) {
|
||||
/* For TX ring, ensure that the ring mask and the
|
||||
* tcl_to_wbm_rbm_map point to the same ring number.
|
||||
*/
|
||||
for (j = 0; j < DP_TCL_NUM_RING_MAX; j++) {
|
||||
if (ring_mask->tx[i] &
|
||||
BIT(ab->hal.tcl_to_wbm_rbm_map[j].wbm_ring_num)) {
|
||||
BIT(ab->hal.tcl_to_wbm_rbm_map[j].wbm_ring_num) &&
|
||||
num_irq < ATH12K_EXT_IRQ_NUM_MAX) {
|
||||
irq_grp->irqs[num_irq++] =
|
||||
wbm2host_tx_completions_ring1 - j;
|
||||
}
|
||||
}
|
||||
|
||||
if (ring_mask->rx[i] & BIT(j)) {
|
||||
for (j = 0; j < ATH12K_EXT_IRQ_NUM_MAX; j++) {
|
||||
if (ring_mask->rx[i] & BIT(j) &&
|
||||
num_irq < ATH12K_EXT_IRQ_NUM_MAX) {
|
||||
irq_grp->irqs[num_irq++] =
|
||||
reo2host_destination_ring1 - j;
|
||||
}
|
||||
|
||||
if (ring_mask->rx_err[i] & BIT(j))
|
||||
if (ring_mask->rx_err[i] & BIT(j) &&
|
||||
num_irq < ATH12K_EXT_IRQ_NUM_MAX)
|
||||
irq_grp->irqs[num_irq++] = reo2host_exception;
|
||||
|
||||
if (ring_mask->rx_wbm_rel[i] & BIT(j))
|
||||
if (ring_mask->rx_wbm_rel[i] & BIT(j) &&
|
||||
num_irq < ATH12K_EXT_IRQ_NUM_MAX)
|
||||
irq_grp->irqs[num_irq++] = wbm2host_rx_release;
|
||||
|
||||
if (ring_mask->reo_status[i] & BIT(j))
|
||||
if (ring_mask->reo_status[i] & BIT(j) &&
|
||||
num_irq < ATH12K_EXT_IRQ_NUM_MAX)
|
||||
irq_grp->irqs[num_irq++] = reo2host_status;
|
||||
|
||||
if (ring_mask->rx_mon_dest[i] & BIT(j))
|
||||
if (ring_mask->rx_mon_dest[i] & BIT(j) &&
|
||||
num_irq < ATH12K_EXT_IRQ_NUM_MAX)
|
||||
irq_grp->irqs[num_irq++] =
|
||||
rxdma2host_monitor_destination_mac1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2256,6 +2256,7 @@ void ath12k_core_deinit(struct ath12k_base *ab)
|
|||
void ath12k_core_free(struct ath12k_base *ab)
|
||||
{
|
||||
timer_delete_sync(&ab->rx_replenish_retry);
|
||||
ath12k_wmi_free();
|
||||
destroy_workqueue(ab->workqueue_aux);
|
||||
destroy_workqueue(ab->workqueue);
|
||||
kfree(ab);
|
||||
|
|
@ -2280,6 +2281,9 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
|
|||
if (!ab->workqueue_aux)
|
||||
goto err_free_wq;
|
||||
|
||||
if (ath12k_wmi_alloc() < 0)
|
||||
goto err_free_wq_aux;
|
||||
|
||||
mutex_init(&ab->core_lock);
|
||||
spin_lock_init(&ab->base_lock);
|
||||
init_completion(&ab->reset_complete);
|
||||
|
|
@ -2314,6 +2318,8 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
|
|||
|
||||
return ab;
|
||||
|
||||
err_free_wq_aux:
|
||||
destroy_workqueue(ab->workqueue_aux);
|
||||
err_free_wq:
|
||||
destroy_workqueue(ab->workqueue);
|
||||
err_sc_free:
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ static int ath12k_htt_tlv_ppdu_stats_parse(struct ath12k_base *ab,
|
|||
}
|
||||
|
||||
int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len,
|
||||
int (*iter)(struct ath12k_base *ar, u16 tag, u16 len,
|
||||
int (*iter)(struct ath12k_base *ab, u16 tag, u16 len,
|
||||
const void *ptr, void *data),
|
||||
void *data)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1523,7 +1523,7 @@ int ath12k_dp_tx_htt_srng_setup(struct ath12k_base *ab, u32 ring_id,
|
|||
void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
|
||||
struct sk_buff *skb);
|
||||
int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len,
|
||||
int (*iter)(struct ath12k_base *ar, u16 tag, u16 len,
|
||||
int (*iter)(struct ath12k_base *ab, u16 tag, u16 len,
|
||||
const void *ptr, void *data),
|
||||
void *data);
|
||||
int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
|
||||
/*
|
||||
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#ifndef ATH12K_HTC_H
|
||||
|
|
@ -301,7 +301,7 @@ struct ath12k_htc {
|
|||
u8 wmi_ep_count;
|
||||
};
|
||||
|
||||
int ath12k_htc_init(struct ath12k_base *ar);
|
||||
int ath12k_htc_init(struct ath12k_base *ab);
|
||||
int ath12k_htc_wait_target(struct ath12k_htc *htc);
|
||||
int ath12k_htc_start(struct ath12k_htc *htc);
|
||||
int ath12k_htc_connect_service(struct ath12k_htc *htc,
|
||||
|
|
@ -309,8 +309,8 @@ int ath12k_htc_connect_service(struct ath12k_htc *htc,
|
|||
struct ath12k_htc_svc_conn_resp *conn_resp);
|
||||
int ath12k_htc_send(struct ath12k_htc *htc, enum ath12k_htc_ep_id eid,
|
||||
struct sk_buff *packet);
|
||||
struct sk_buff *ath12k_htc_alloc_skb(struct ath12k_base *ar, int size);
|
||||
void ath12k_htc_rx_completion_handler(struct ath12k_base *ar,
|
||||
struct sk_buff *ath12k_htc_alloc_skb(struct ath12k_base *ab, int size);
|
||||
void ath12k_htc_rx_completion_handler(struct ath12k_base *ab,
|
||||
struct sk_buff *skb);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -559,7 +559,6 @@ struct hal_eht_sig_ofdma_cmn_eb1 {
|
|||
#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_4 GENMASK_ULL(35, 27)
|
||||
#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_5 GENMASK_ULL(44, 36)
|
||||
#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_6 GENMASK_ULL(53, 45)
|
||||
#define HAL_RX_EHT_SIG_OFDMA_EB2_MCS GNEMASK_ULL(57, 54)
|
||||
|
||||
struct hal_eht_sig_ofdma_cmn_eb2 {
|
||||
__le64 info0;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
#include <linux/time.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/refcount.h>
|
||||
#include "core.h"
|
||||
#include "debugfs.h"
|
||||
#include "debug.h"
|
||||
|
|
@ -134,6 +136,10 @@ struct wmi_pdev_set_obss_bitmap_arg {
|
|||
const char *label;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(ath12k_wmi_mutex);
|
||||
static refcount_t ath12k_wmi_refcount;
|
||||
static void __percpu *ath12k_wmi_tb;
|
||||
|
||||
static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = {
|
||||
[WMI_TAG_ARRAY_BYTE] = { .min_len = 0 },
|
||||
[WMI_TAG_ARRAY_UINT32] = { .min_len = 0 },
|
||||
|
|
@ -289,29 +295,19 @@ static int ath12k_wmi_tlv_iter_parse(struct ath12k_base *ab, u16 tag, u16 len,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ath12k_wmi_tlv_parse(struct ath12k_base *ar, const void **tb,
|
||||
const void *ptr, size_t len)
|
||||
{
|
||||
return ath12k_wmi_tlv_iter(ar, ptr, len, ath12k_wmi_tlv_iter_parse,
|
||||
(void *)tb);
|
||||
}
|
||||
|
||||
static const void **
|
||||
ath12k_wmi_tlv_parse_alloc(struct ath12k_base *ab,
|
||||
struct sk_buff *skb, gfp_t gfp)
|
||||
ath12k_wmi_tlv_parse(struct ath12k_base *ab, struct sk_buff *skb)
|
||||
{
|
||||
const void **tb;
|
||||
int ret;
|
||||
|
||||
tb = kzalloc_objs(*tb, WMI_TAG_MAX, gfp);
|
||||
if (!tb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
tb = this_cpu_ptr(ath12k_wmi_tb);
|
||||
memset(tb, 0, WMI_TAG_MAX * sizeof(*tb));
|
||||
|
||||
ret = ath12k_wmi_tlv_parse(ab, tb, skb->data, skb->len);
|
||||
if (ret) {
|
||||
kfree(tb);
|
||||
ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len,
|
||||
ath12k_wmi_tlv_iter_parse, (void *)tb);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return tb;
|
||||
}
|
||||
|
|
@ -3911,9 +3907,10 @@ ath12k_wmi_obss_color_collision_event(struct ath12k_base *ab, struct sk_buff *sk
|
|||
const struct wmi_obss_color_collision_event *ev;
|
||||
struct ath12k_link_vif *arvif;
|
||||
u32 vdev_id, evt_type;
|
||||
const void **tb;
|
||||
u64 bitmap;
|
||||
|
||||
const void **tb __free(kfree) = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ath12k_warn(ab, "failed to parse OBSS color collision tlv %ld\n",
|
||||
PTR_ERR(tb));
|
||||
|
|
@ -5714,7 +5711,7 @@ static int ath12k_pull_vdev_start_resp_tlv(struct ath12k_base *ab, struct sk_buf
|
|||
const struct wmi_vdev_start_resp_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -5724,13 +5721,11 @@ static int ath12k_pull_vdev_start_resp_tlv(struct ath12k_base *ab, struct sk_buf
|
|||
ev = tb[WMI_TAG_VDEV_START_RESPONSE_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch vdev start resp ev");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
*vdev_rsp = *ev;
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -5809,7 +5804,7 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
|
|||
|
||||
ath12k_dbg(ab, ATH12K_DBG_WMI, "processing regulatory ext channel list\n");
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -5819,7 +5814,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
|
|||
ev = tb[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch reg chan list ext update ev\n");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
|
|
@ -5849,7 +5843,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
|
|||
if (num_2g_reg_rules > MAX_REG_RULES || num_5g_reg_rules > MAX_REG_RULES) {
|
||||
ath12k_warn(ab, "Num reg rules for 2G/5G exceeds max limit (num_2g_reg_rules: %d num_5g_reg_rules: %d max_rules: %d)\n",
|
||||
num_2g_reg_rules, num_5g_reg_rules, MAX_REG_RULES);
|
||||
kfree(tb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -5859,7 +5852,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
|
|||
if (num_6g_reg_rules_ap[i] > MAX_6GHZ_REG_RULES) {
|
||||
ath12k_warn(ab, "Num 6G reg rules for AP mode(%d) exceeds max limit (num_6g_reg_rules_ap: %d, max_rules: %d)\n",
|
||||
i, num_6g_reg_rules_ap[i], MAX_6GHZ_REG_RULES);
|
||||
kfree(tb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -5884,14 +5876,12 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
|
|||
num_6g_reg_rules_cl[WMI_REG_VLP_AP][i] > MAX_6GHZ_REG_RULES) {
|
||||
ath12k_warn(ab, "Num 6g client reg rules exceeds max limit, for client(type: %d)\n",
|
||||
i);
|
||||
kfree(tb);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!total_reg_rules) {
|
||||
ath12k_warn(ab, "No reg rules available\n");
|
||||
kfree(tb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -5993,7 +5983,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
|
|||
ext_wmi_reg_rule);
|
||||
|
||||
if (!reg_info->reg_rules_2g_ptr) {
|
||||
kfree(tb);
|
||||
ath12k_warn(ab, "Unable to Allocate memory for 2g rules\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -6027,7 +6016,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
|
|||
ext_wmi_reg_rule);
|
||||
|
||||
if (!reg_info->reg_rules_5g_ptr) {
|
||||
kfree(tb);
|
||||
ath12k_warn(ab, "Unable to Allocate memory for 5g rules\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -6046,7 +6034,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
|
|||
ext_wmi_reg_rule);
|
||||
|
||||
if (!reg_info->reg_rules_6g_ap_ptr[i]) {
|
||||
kfree(tb);
|
||||
ath12k_warn(ab, "Unable to Allocate memory for 6g ap rules\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -6061,7 +6048,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
|
|||
ext_wmi_reg_rule);
|
||||
|
||||
if (!reg_info->reg_rules_6g_client_ptr[j][i]) {
|
||||
kfree(tb);
|
||||
ath12k_warn(ab, "Unable to Allocate memory for 6g client rules\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -6096,7 +6082,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
|
|||
|
||||
ath12k_dbg(ab, ATH12K_DBG_WMI, "processed regulatory ext channel list\n");
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6107,7 +6092,7 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff *
|
|||
const struct wmi_peer_delete_resp_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -6117,7 +6102,6 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff *
|
|||
ev = tb[WMI_TAG_PEER_DELETE_RESP_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch peer delete resp ev");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
|
|
@ -6127,7 +6111,6 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff *
|
|||
ether_addr_copy(peer_del_resp->peer_macaddr.addr,
|
||||
ev->peer_macaddr.addr);
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6139,7 +6122,7 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab,
|
|||
const struct wmi_vdev_delete_resp_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -6149,13 +6132,11 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab,
|
|||
ev = tb[WMI_TAG_VDEV_DELETE_RESP_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch vdev delete resp ev");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
*vdev_id = le32_to_cpu(ev->vdev_id);
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6167,7 +6148,7 @@ static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab,
|
|||
const struct wmi_bcn_tx_status_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -6177,14 +6158,12 @@ static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab,
|
|||
ev = tb[WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch bcn tx status ev");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
*vdev_id = le32_to_cpu(ev->vdev_id);
|
||||
*tx_status = le32_to_cpu(ev->tx_status);
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6195,7 +6174,7 @@ static int ath12k_pull_vdev_stopped_param_tlv(struct ath12k_base *ab, struct sk_
|
|||
const struct wmi_vdev_stopped_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -6205,13 +6184,11 @@ static int ath12k_pull_vdev_stopped_param_tlv(struct ath12k_base *ab, struct sk_
|
|||
ev = tb[WMI_TAG_VDEV_STOPPED_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch vdev stop ev");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
*vdev_id = le32_to_cpu(ev->vdev_id);
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6350,7 +6327,7 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab,
|
|||
const struct wmi_mgmt_tx_compl_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -6360,7 +6337,6 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab,
|
|||
ev = tb[WMI_TAG_MGMT_TX_COMPL_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch mgmt tx compl ev");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
|
|
@ -6370,7 +6346,6 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab,
|
|||
param->ppdu_id = ev->ppdu_id;
|
||||
param->ack_rssi = ev->ack_rssi;
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6533,7 +6508,7 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb,
|
|||
const struct wmi_scan_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -6543,7 +6518,6 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb,
|
|||
ev = tb[WMI_TAG_SCAN_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch scan ev");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
|
|
@ -6555,7 +6529,6 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb,
|
|||
scan_evt_param->vdev_id = ev->vdev_id;
|
||||
scan_evt_param->tsf_timestamp = ev->tsf_timestamp;
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6566,7 +6539,7 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf
|
|||
const struct wmi_peer_sta_kickout_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -6576,7 +6549,6 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf
|
|||
ev = tb[WMI_TAG_PEER_STA_KICKOUT_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch peer sta kickout ev");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
|
|
@ -6584,7 +6556,6 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf
|
|||
arg->reason = le32_to_cpu(ev->reason);
|
||||
arg->rssi = le32_to_cpu(ev->rssi);
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6595,7 +6566,7 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb,
|
|||
const struct wmi_roam_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -6605,7 +6576,6 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb,
|
|||
ev = tb[WMI_TAG_ROAM_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch roam ev");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
|
|
@ -6613,7 +6583,6 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb,
|
|||
roam_ev->reason = ev->reason;
|
||||
roam_ev->rssi = ev->rssi;
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6647,7 +6616,7 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
|
|||
const struct wmi_chan_info_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -6657,7 +6626,6 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
|
|||
ev = tb[WMI_TAG_CHAN_INFO_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch chan info ev");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
|
|
@ -6674,7 +6642,6 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
|
|||
ch_info_ev->mac_clk_mhz = ev->mac_clk_mhz;
|
||||
ch_info_ev->vdev_id = ev->vdev_id;
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6686,7 +6653,7 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
|
|||
const struct wmi_pdev_bss_chan_info_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -6696,7 +6663,6 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
|
|||
ev = tb[WMI_TAG_PDEV_BSS_CHAN_INFO_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch pdev bss chan info ev");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
|
|
@ -6714,7 +6680,6 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
|
|||
bss_ch_info_ev->rx_bss_cycle_count_low = ev->rx_bss_cycle_count_low;
|
||||
bss_ch_info_ev->rx_bss_cycle_count_high = ev->rx_bss_cycle_count_high;
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6726,7 +6691,7 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk
|
|||
const struct wmi_vdev_install_key_compl_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -6736,7 +6701,6 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk
|
|||
ev = tb[WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch vdev install key compl ev");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
|
|
@ -6746,7 +6710,6 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk
|
|||
arg->key_flags = le32_to_cpu(ev->key_flags);
|
||||
arg->status = le32_to_cpu(ev->status);
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6757,7 +6720,7 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff
|
|||
const struct wmi_peer_assoc_conf_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -6767,14 +6730,12 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff
|
|||
ev = tb[WMI_TAG_PEER_ASSOC_CONF_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch peer assoc conf ev");
|
||||
kfree(tb);
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
peer_assoc_conf->vdev_id = le32_to_cpu(ev->vdev_id);
|
||||
peer_assoc_conf->macaddr = ev->peer_macaddr.addr;
|
||||
|
||||
kfree(tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6792,7 +6753,7 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s
|
|||
const void **tb;
|
||||
int ret, i;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -6801,7 +6762,6 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s
|
|||
|
||||
ev = tb[WMI_TAG_11D_NEW_COUNTRY_EVENT];
|
||||
if (!ev) {
|
||||
kfree(tb);
|
||||
ath12k_warn(ab, "failed to fetch 11d new cc ev");
|
||||
return -EPROTO;
|
||||
}
|
||||
|
|
@ -6814,8 +6774,6 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s
|
|||
ab->new_alpha2[0],
|
||||
ab->new_alpha2[1]);
|
||||
|
||||
kfree(tb);
|
||||
|
||||
for (i = 0; i < ab->num_radios; i++) {
|
||||
pdev = &ab->pdevs[i];
|
||||
ar = pdev->ar;
|
||||
|
|
@ -8557,7 +8515,7 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab,
|
|||
const struct wmi_pdev_ctl_failsafe_chk_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -8567,7 +8525,6 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab,
|
|||
ev = tb[WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch pdev ctl failsafe check ev");
|
||||
kfree(tb);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -8581,8 +8538,6 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab,
|
|||
if (ev->ctl_failsafe_status != 0)
|
||||
ath12k_warn(ab, "pdev ctl failsafe failure status %d",
|
||||
ev->ctl_failsafe_status);
|
||||
|
||||
kfree(tb);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -8654,7 +8609,7 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab,
|
|||
const u32 *vdev_ids;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -8666,7 +8621,6 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab,
|
|||
|
||||
if (!ev || !vdev_ids) {
|
||||
ath12k_warn(ab, "failed to fetch pdev csa switch count ev");
|
||||
kfree(tb);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -8676,8 +8630,6 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab,
|
|||
ev->num_vdevs);
|
||||
|
||||
ath12k_wmi_process_csa_switch_count_event(ab, ev, vdev_ids);
|
||||
|
||||
kfree(tb);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -8689,7 +8641,7 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff
|
|||
struct ath12k *ar;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -8700,7 +8652,6 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff
|
|||
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch pdev dfs radar detected ev");
|
||||
kfree(tb);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -8739,8 +8690,6 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff
|
|||
|
||||
exit:
|
||||
rcu_read_unlock();
|
||||
|
||||
kfree(tb);
|
||||
}
|
||||
|
||||
static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id,
|
||||
|
|
@ -8751,7 +8700,7 @@ static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id,
|
|||
int ret;
|
||||
u16 length;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
|
|
@ -8762,14 +8711,11 @@ static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id,
|
|||
ev = tb[WMI_TAG_ARRAY_BYTE];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch ftm msg\n");
|
||||
kfree(tb);
|
||||
return;
|
||||
}
|
||||
|
||||
length = skb->len - TLV_HDR_SIZE;
|
||||
ath12k_tm_process_event(ab, cmd_id, ev, length);
|
||||
kfree(tb);
|
||||
tb = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -8782,7 +8728,7 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
|
|||
int temp;
|
||||
u32 pdev_id;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ath12k_warn(ab, "failed to parse tlv: %ld\n", PTR_ERR(tb));
|
||||
return;
|
||||
|
|
@ -8791,15 +8737,12 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
|
|||
ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch pdev temp ev\n");
|
||||
kfree(tb);
|
||||
return;
|
||||
}
|
||||
|
||||
temp = a_sle32_to_cpu(ev->temp);
|
||||
pdev_id = le32_to_cpu(ev->pdev_id);
|
||||
|
||||
kfree(tb);
|
||||
|
||||
ath12k_dbg(ab, ATH12K_DBG_WMI,
|
||||
"pdev temperature ev temp %d pdev_id %u\n",
|
||||
temp, pdev_id);
|
||||
|
|
@ -8826,7 +8769,7 @@ static void ath12k_fils_discovery_event(struct ath12k_base *ab,
|
|||
const struct wmi_fils_discovery_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab,
|
||||
|
|
@ -8838,15 +8781,12 @@ static void ath12k_fils_discovery_event(struct ath12k_base *ab,
|
|||
ev = tb[WMI_TAG_HOST_SWFDA_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch FILS discovery event\n");
|
||||
kfree(tb);
|
||||
return;
|
||||
}
|
||||
|
||||
ath12k_warn(ab,
|
||||
"FILS discovery frame expected from host for vdev_id: %u, transmission scheduled at %u, next TBTT: %u\n",
|
||||
ev->vdev_id, ev->fils_tt, ev->tbtt);
|
||||
|
||||
kfree(tb);
|
||||
}
|
||||
|
||||
static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab,
|
||||
|
|
@ -8856,7 +8796,7 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab,
|
|||
const struct wmi_probe_resp_tx_status_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab,
|
||||
|
|
@ -8869,7 +8809,6 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab,
|
|||
if (!ev) {
|
||||
ath12k_warn(ab,
|
||||
"failed to fetch probe response transmission status event");
|
||||
kfree(tb);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -8877,8 +8816,6 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab,
|
|||
ath12k_warn(ab,
|
||||
"Probe response transmission failed for vdev_id %u, status %u\n",
|
||||
ev->vdev_id, ev->tx_status);
|
||||
|
||||
kfree(tb);
|
||||
}
|
||||
|
||||
static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab,
|
||||
|
|
@ -8890,7 +8827,7 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab,
|
|||
struct ath12k *ar;
|
||||
int ret, vdev_id;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse P2P NoA TLV: %d\n", ret);
|
||||
|
|
@ -8900,10 +8837,8 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab,
|
|||
ev = tb[WMI_TAG_P2P_NOA_EVENT];
|
||||
noa = tb[WMI_TAG_P2P_NOA_INFO];
|
||||
|
||||
if (!ev || !noa) {
|
||||
ret = -EPROTO;
|
||||
goto out;
|
||||
}
|
||||
if (!ev || !noa)
|
||||
return -EPROTO;
|
||||
|
||||
vdev_id = __le32_to_cpu(ev->vdev_id);
|
||||
|
||||
|
|
@ -8926,8 +8861,6 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab,
|
|||
|
||||
unlock:
|
||||
rcu_read_unlock();
|
||||
out:
|
||||
kfree(tb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -8938,7 +8871,7 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab,
|
|||
const void **tb;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -8946,10 +8879,8 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab,
|
|||
}
|
||||
|
||||
ev = tb[WMI_TAG_RFKILL_EVENT];
|
||||
if (!ev) {
|
||||
kfree(tb);
|
||||
if (!ev)
|
||||
return;
|
||||
}
|
||||
|
||||
ath12k_dbg(ab, ATH12K_DBG_MAC,
|
||||
"wmi tlv rfkill state change gpio %d type %d radio_state %d\n",
|
||||
|
|
@ -8962,7 +8893,6 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab,
|
|||
spin_unlock_bh(&ab->base_lock);
|
||||
|
||||
queue_work(ab->workqueue, &ab->rfkill_work);
|
||||
kfree(tb);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -8978,7 +8908,7 @@ static void ath12k_wmi_twt_enable_event(struct ath12k_base *ab,
|
|||
const struct wmi_twt_enable_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse wmi twt enable status event tlv: %d\n",
|
||||
|
|
@ -8989,15 +8919,12 @@ static void ath12k_wmi_twt_enable_event(struct ath12k_base *ab,
|
|||
ev = tb[WMI_TAG_TWT_ENABLE_COMPLETE_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch twt enable wmi event\n");
|
||||
goto exit;
|
||||
return;
|
||||
}
|
||||
|
||||
ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi twt enable event pdev id %u status %u\n",
|
||||
le32_to_cpu(ev->pdev_id),
|
||||
le32_to_cpu(ev->status));
|
||||
|
||||
exit:
|
||||
kfree(tb);
|
||||
}
|
||||
|
||||
static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab,
|
||||
|
|
@ -9007,7 +8934,7 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab,
|
|||
const struct wmi_twt_disable_event *ev;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse wmi twt disable status event tlv: %d\n",
|
||||
|
|
@ -9018,15 +8945,12 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab,
|
|||
ev = tb[WMI_TAG_TWT_DISABLE_COMPLETE_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch twt disable wmi event\n");
|
||||
goto exit;
|
||||
return;
|
||||
}
|
||||
|
||||
ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi twt disable event pdev id %d status %u\n",
|
||||
le32_to_cpu(ev->pdev_id),
|
||||
le32_to_cpu(ev->status));
|
||||
|
||||
exit:
|
||||
kfree(tb);
|
||||
}
|
||||
|
||||
static int ath12k_wmi_wow_wakeup_host_parse(struct ath12k_base *ab,
|
||||
|
|
@ -9099,7 +9023,7 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab,
|
|||
const void **tb;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
|
||||
|
|
@ -9109,7 +9033,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab,
|
|||
ev = tb[WMI_TAG_GTK_OFFLOAD_STATUS_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch gtk offload status ev");
|
||||
kfree(tb);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -9119,7 +9042,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab,
|
|||
rcu_read_unlock();
|
||||
ath12k_warn(ab, "failed to get arvif for vdev_id:%d\n",
|
||||
le32_to_cpu(ev->vdev_id));
|
||||
kfree(tb);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -9135,8 +9057,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab,
|
|||
(void *)&replay_ctr_be, GFP_ATOMIC);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
kfree(tb);
|
||||
}
|
||||
|
||||
static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab,
|
||||
|
|
@ -9148,7 +9068,7 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab,
|
|||
const void **tb;
|
||||
int ret, i;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse mlo setup complete event tlv: %d\n",
|
||||
|
|
@ -9159,7 +9079,6 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab,
|
|||
ev = tb[WMI_TAG_MLO_SETUP_COMPLETE_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_warn(ab, "failed to fetch mlo setup complete event\n");
|
||||
kfree(tb);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -9178,14 +9097,11 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab,
|
|||
if (!ar) {
|
||||
ath12k_warn(ab, "invalid pdev_id %d status %u in setup complete event\n",
|
||||
ev->pdev_id, ev->status);
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
ar->mlo_setup_status = le32_to_cpu(ev->status);
|
||||
complete(&ar->mlo_setup_done);
|
||||
|
||||
out:
|
||||
kfree(tb);
|
||||
}
|
||||
|
||||
static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab,
|
||||
|
|
@ -9195,7 +9111,7 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab,
|
|||
const void **tb;
|
||||
int ret;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ret = PTR_ERR(tb);
|
||||
ath12k_warn(ab, "failed to parse teardown complete event tlv: %d\n", ret);
|
||||
|
|
@ -9203,13 +9119,8 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab,
|
|||
}
|
||||
|
||||
ev = tb[WMI_TAG_MLO_TEARDOWN_COMPLETE];
|
||||
if (!ev) {
|
||||
if (!ev)
|
||||
ath12k_warn(ab, "failed to fetch teardown complete event\n");
|
||||
kfree(tb);
|
||||
return;
|
||||
}
|
||||
|
||||
kfree(tb);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ATH12K_DEBUGFS
|
||||
|
|
@ -11239,3 +11150,31 @@ int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab,
|
|||
dev_kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ath12k_wmi_alloc(void)
|
||||
{
|
||||
guard(mutex)(&ath12k_wmi_mutex);
|
||||
|
||||
if (!ath12k_wmi_tb) {
|
||||
ath12k_wmi_tb = __alloc_percpu(WMI_TAG_MAX * sizeof(void *),
|
||||
__alignof__(void *));
|
||||
if (!ath12k_wmi_tb)
|
||||
return -ENOMEM;
|
||||
|
||||
refcount_set(&ath12k_wmi_refcount, 1);
|
||||
} else {
|
||||
refcount_inc(&ath12k_wmi_refcount);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ath12k_wmi_free(void)
|
||||
{
|
||||
guard(mutex)(&ath12k_wmi_mutex);
|
||||
|
||||
if (refcount_dec_and_test(&ath12k_wmi_refcount)) {
|
||||
free_percpu(ath12k_wmi_tb);
|
||||
ath12k_wmi_tb = NULL;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6576,4 +6576,7 @@ int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar,
|
|||
struct ath12k_reg_tpc_power_info *param);
|
||||
int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab,
|
||||
struct wmi_mlo_link_set_active_arg *param);
|
||||
int ath12k_wmi_alloc(void);
|
||||
void ath12k_wmi_free(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#include <linux/time.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/unaligned.h>
|
||||
|
||||
#include "hw.h"
|
||||
|
|
@ -2719,19 +2719,28 @@ static void ath9k_hw_gpio_cfg_output_mux(struct ath_hw *ah, u32 gpio, u32 type)
|
|||
static void ath9k_hw_gpio_cfg_soc(struct ath_hw *ah, u32 gpio, bool out,
|
||||
const char *label)
|
||||
{
|
||||
enum gpiod_flags flags = out ? GPIOD_OUT_LOW : GPIOD_IN;
|
||||
struct gpio_desc *gpiod;
|
||||
int err;
|
||||
|
||||
if (ah->caps.gpio_requested & BIT(gpio))
|
||||
if (ah->gpiods[gpio])
|
||||
return;
|
||||
|
||||
err = devm_gpio_request_one(ah->dev, gpio, out ? GPIOF_OUT_INIT_LOW : GPIOF_IN, label);
|
||||
/*
|
||||
* Obtains a system specific GPIO descriptor from another GPIO controller.
|
||||
* Ideally this should come from the device tree, this is a legacy code
|
||||
* path.
|
||||
*/
|
||||
gpiod = gpiod_get_index(NULL, "ath9k", gpio, flags);
|
||||
err = PTR_ERR_OR_ZERO(gpiod);
|
||||
if (err) {
|
||||
ath_err(ath9k_hw_common(ah), "request GPIO%d failed:%d\n",
|
||||
gpio, err);
|
||||
return;
|
||||
}
|
||||
|
||||
ah->caps.gpio_requested |= BIT(gpio);
|
||||
gpiod_set_consumer_name(gpiod, label);
|
||||
ah->gpiods[gpio] = gpiod;
|
||||
}
|
||||
|
||||
static void ath9k_hw_gpio_cfg_wmac(struct ath_hw *ah, u32 gpio, bool out,
|
||||
|
|
@ -2791,10 +2800,12 @@ void ath9k_hw_gpio_free(struct ath_hw *ah, u32 gpio)
|
|||
if (!AR_SREV_SOC(ah))
|
||||
return;
|
||||
|
||||
WARN_ON(gpio >= ah->caps.num_gpio_pins);
|
||||
if (ah->gpiods[gpio]) {
|
||||
gpiod_put(ah->gpiods[gpio]);
|
||||
ah->gpiods[gpio] = NULL;
|
||||
}
|
||||
|
||||
if (ah->caps.gpio_requested & BIT(gpio))
|
||||
ah->caps.gpio_requested &= ~BIT(gpio);
|
||||
WARN_ON(gpio >= ah->caps.num_gpio_pins);
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_hw_gpio_free);
|
||||
|
||||
|
|
@ -2822,8 +2833,8 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio)
|
|||
val = REG_READ(ah, AR_GPIO_IN(ah)) & BIT(gpio);
|
||||
else
|
||||
val = MS_REG_READ(AR, gpio);
|
||||
} else if (BIT(gpio) & ah->caps.gpio_requested) {
|
||||
val = gpio_get_value(gpio) & BIT(gpio);
|
||||
} else if (ah->gpiods[gpio]) {
|
||||
val = gpiod_get_value(ah->gpiods[gpio]);
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
|
@ -2846,8 +2857,8 @@ void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val)
|
|||
AR7010_GPIO_OUT : AR_GPIO_IN_OUT(ah);
|
||||
|
||||
REG_RMW(ah, out_addr, val << gpio, BIT(gpio));
|
||||
} else if (BIT(gpio) & ah->caps.gpio_requested) {
|
||||
gpio_set_value(gpio, val);
|
||||
} else if (ah->gpiods[gpio]) {
|
||||
gpiod_set_value(ah->gpiods[gpio], val);
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
|
|
@ -302,7 +303,6 @@ struct ath9k_hw_capabilities {
|
|||
u8 max_rxchains;
|
||||
u8 num_gpio_pins;
|
||||
u32 gpio_mask;
|
||||
u32 gpio_requested;
|
||||
u8 rx_hp_qdepth;
|
||||
u8 rx_lp_qdepth;
|
||||
u8 rx_status_len;
|
||||
|
|
@ -783,6 +783,7 @@ struct ath_hw {
|
|||
struct ath9k_hw_capabilities caps;
|
||||
struct ath9k_channel channels[ATH9K_NUM_CHANNELS];
|
||||
struct ath9k_channel *curchan;
|
||||
struct gpio_desc *gpiods[32];
|
||||
|
||||
union {
|
||||
struct ar5416_eeprom_def def;
|
||||
|
|
|
|||
|
|
@ -1607,7 +1607,6 @@ wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel)
|
|||
{
|
||||
uint i;
|
||||
const struct chan_info_2064_lcnphy *ci;
|
||||
u8 rfpll_doubler = 0;
|
||||
u8 pll_pwrup, pll_pwrup_ovr;
|
||||
s32 qFcal;
|
||||
u8 d15, d16, f16, e44, e45;
|
||||
|
|
@ -1618,18 +1617,12 @@ wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel)
|
|||
u16 g30, d28;
|
||||
|
||||
ci = &chan_info_2064_lcnphy[0];
|
||||
rfpll_doubler = 1;
|
||||
|
||||
mod_radio_reg(pi, RADIO_2064_REG09D, 0x4, 0x1 << 2);
|
||||
|
||||
write_radio_reg(pi, RADIO_2064_REG09E, 0xf);
|
||||
if (!rfpll_doubler) {
|
||||
loop_bw = PLL_2064_LOOP_BW;
|
||||
d30 = PLL_2064_D30;
|
||||
} else {
|
||||
loop_bw = PLL_2064_LOOP_BW_DOUBLER;
|
||||
d30 = PLL_2064_D30_DOUBLER;
|
||||
}
|
||||
loop_bw = PLL_2064_LOOP_BW_DOUBLER;
|
||||
d30 = PLL_2064_D30_DOUBLER;
|
||||
|
||||
if (CHSPEC_IS2G(pi->radio_chanspec)) {
|
||||
for (i = 0; i < ARRAY_SIZE(chan_info_2064_lcnphy); i++)
|
||||
|
|
@ -1669,7 +1662,7 @@ wlc_lcnphy_radio_2064_channel_tune_4313(struct brcms_phy *pi, u8 channel)
|
|||
e44 = 0;
|
||||
e45 = 0;
|
||||
|
||||
fpfd = rfpll_doubler ? (pi->xtalfreq << 1) : (pi->xtalfreq);
|
||||
fpfd = pi->xtalfreq << 1;
|
||||
if (pi->xtalfreq > 26000000)
|
||||
e44 = 1;
|
||||
if (pi->xtalfreq > 52000000)
|
||||
|
|
|
|||
|
|
@ -257,7 +257,7 @@ static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld,
|
|||
IEEE80211_EML_CAP_TRANSITION_TIMEOUT);
|
||||
|
||||
cmd->client.esr_transition_timeout =
|
||||
min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU,
|
||||
min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_64TU,
|
||||
esr_transition_timeout);
|
||||
cmd->client.medium_sync_delay =
|
||||
cpu_to_le16(vif->cfg.eml_med_sync_delay);
|
||||
|
|
|
|||
|
|
@ -114,10 +114,10 @@ static const u8 ext_capa_base[IWL_MLD_STA_EXT_CAPA_SIZE] = {
|
|||
};
|
||||
|
||||
#define IWL_MLD_EMLSR_CAPA (IEEE80211_EML_CAP_EMLSR_SUPP | \
|
||||
IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US << \
|
||||
__bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \
|
||||
IEEE80211_EML_CAP_EML_PADDING_DELAY_32US << \
|
||||
__bf_shf(IEEE80211_EML_CAP_EML_PADDING_DELAY) | \
|
||||
IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \
|
||||
__bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY))
|
||||
__bf_shf(IEEE80211_EML_CAP_EML_TRANSITION_DELAY))
|
||||
#define IWL_MLD_CAPA_OPS (FIELD_PREP_CONST( \
|
||||
IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \
|
||||
IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \
|
||||
|
|
|
|||
|
|
@ -263,10 +263,10 @@ static const u8 tm_if_types_ext_capa_sta[] = {
|
|||
*/
|
||||
|
||||
#define IWL_MVM_EMLSR_CAPA (IEEE80211_EML_CAP_EMLSR_SUPP | \
|
||||
IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US << \
|
||||
__bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \
|
||||
IEEE80211_EML_CAP_EML_PADDING_DELAY_32US << \
|
||||
__bf_shf(IEEE80211_EML_CAP_EML_PADDING_DELAY) | \
|
||||
IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \
|
||||
__bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY))
|
||||
__bf_shf(IEEE80211_EML_CAP_EML_TRANSITION_DELAY))
|
||||
#define IWL_MVM_MLD_CAPA_OPS (FIELD_PREP_CONST( \
|
||||
IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \
|
||||
IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \
|
||||
|
|
|
|||
|
|
@ -1880,8 +1880,8 @@ mt7925_mcu_sta_eht_mld_tlv(struct sk_buff *skb,
|
|||
|
||||
eml_cap = (vif->cfg.eml_cap & (IEEE80211_EML_CAP_EMLSR_SUPP |
|
||||
IEEE80211_EML_CAP_TRANSITION_TIMEOUT)) |
|
||||
(ext_capa->eml_capabilities & (IEEE80211_EML_CAP_EMLSR_PADDING_DELAY |
|
||||
IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY));
|
||||
(ext_capa->eml_capabilities & (IEEE80211_EML_CAP_EML_PADDING_DELAY |
|
||||
IEEE80211_EML_CAP_EML_TRANSITION_DELAY));
|
||||
|
||||
if (eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP) {
|
||||
eht_mld->eml_cap[0] = u16_get_bits(eml_cap, GENMASK(7, 0));
|
||||
|
|
|
|||
|
|
@ -261,8 +261,8 @@ int qtnf_cmd_send_start_ap(struct qtnf_vif *vif,
|
|||
cmd->p2p_ctwindow = s->p2p_ctwindow;
|
||||
cmd->p2p_opp_ps = s->p2p_opp_ps;
|
||||
cmd->pbss = s->pbss;
|
||||
cmd->ht_required = s->ht_required;
|
||||
cmd->vht_required = s->vht_required;
|
||||
cmd->ht_required = s->beacon.ht_required;
|
||||
cmd->vht_required = s->beacon.vht_required;
|
||||
cmd->twt_responder = s->twt_responder;
|
||||
if (s->he_obss_pd.enable) {
|
||||
cmd->sr_params.sr_control |= QLINK_SR_SRG_INFORMATION_PRESENT;
|
||||
|
|
|
|||
|
|
@ -5050,7 +5050,7 @@ int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv
|
|||
le32_encode_bits(0, RTW89_H2C_JOININFO_W1_EMLSR_CAB) |
|
||||
le32_encode_bits(0, RTW89_H2C_JOININFO_W1_NSTR_EN) |
|
||||
le32_encode_bits(init_ps, RTW89_H2C_JOININFO_W1_INIT_PWR_STATE) |
|
||||
le32_encode_bits(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US,
|
||||
le32_encode_bits(IEEE80211_EML_CAP_EML_PADDING_DELAY_256US,
|
||||
RTW89_H2C_JOININFO_W1_EMLSR_PADDING) |
|
||||
le32_encode_bits(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US,
|
||||
RTW89_H2C_JOININFO_W1_EMLSR_TRANS_DELAY) |
|
||||
|
|
|
|||
|
|
@ -485,26 +485,28 @@ struct ieee80211_multi_link_elem {
|
|||
#define IEEE80211_MED_SYNC_DELAY_DEFAULT 0x10ac
|
||||
|
||||
#define IEEE80211_EML_CAP_EMLSR_SUPP 0x0001
|
||||
#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY 0x000e
|
||||
#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_0US 0
|
||||
#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US 1
|
||||
#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_64US 2
|
||||
#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_128US 3
|
||||
#define IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US 4
|
||||
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY 0x0070
|
||||
#define IEEE80211_EML_CAP_EML_PADDING_DELAY 0x000e
|
||||
/* Described Tables 9-417i & 9-417k in 802.11be-2024, which have the same values */
|
||||
#define IEEE80211_EML_CAP_EML_PADDING_DELAY_0US 0
|
||||
#define IEEE80211_EML_CAP_EML_PADDING_DELAY_32US 1
|
||||
#define IEEE80211_EML_CAP_EML_PADDING_DELAY_64US 2
|
||||
#define IEEE80211_EML_CAP_EML_PADDING_DELAY_128US 3
|
||||
#define IEEE80211_EML_CAP_EML_PADDING_DELAY_256US 4
|
||||
#define IEEE80211_EML_CAP_EML_TRANSITION_DELAY 0x0070
|
||||
/* Described in Table 9-417j in 802.11be-2024 */
|
||||
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_0US 0
|
||||
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_16US 1
|
||||
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_32US 2
|
||||
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US 3
|
||||
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_128US 4
|
||||
#define IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US 5
|
||||
/* Described in Table 9-417l in 802.11be-2024 */
|
||||
#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_0US 0
|
||||
#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_32US 1
|
||||
#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_64US 2
|
||||
#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_128US 3
|
||||
#define IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_256US 4
|
||||
#define IEEE80211_EML_CAP_EMLMR_SUPPORT 0x0080
|
||||
#define IEEE80211_EML_CAP_EMLMR_DELAY 0x0700
|
||||
#define IEEE80211_EML_CAP_EMLMR_DELAY_0US 0
|
||||
#define IEEE80211_EML_CAP_EMLMR_DELAY_32US 1
|
||||
#define IEEE80211_EML_CAP_EMLMR_DELAY_64US 2
|
||||
#define IEEE80211_EML_CAP_EMLMR_DELAY_128US 3
|
||||
#define IEEE80211_EML_CAP_EMLMR_DELAY_256US 4
|
||||
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT 0x7800
|
||||
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_0 0
|
||||
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128US 1
|
||||
|
|
@ -517,7 +519,6 @@ struct ieee80211_multi_link_elem {
|
|||
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_16TU 8
|
||||
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_32TU 9
|
||||
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_64TU 10
|
||||
#define IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU 11
|
||||
|
||||
#define IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS 0x000f
|
||||
#define IEEE80211_MLD_CAP_OP_SRS_SUPPORT 0x0010
|
||||
|
|
@ -1121,14 +1122,20 @@ static inline bool ieee80211_tid_to_link_map_size_ok(const u8 *data, size_t len)
|
|||
|
||||
static inline u32 ieee80211_emlsr_pad_delay_in_us(u16 eml_cap)
|
||||
{
|
||||
u32 emlsr_supp =
|
||||
u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLSR_SUPP);
|
||||
|
||||
if (!emlsr_supp)
|
||||
return 0;
|
||||
|
||||
/* IEEE Std 802.11be-2024 Table 9-417i—Encoding of the EMLSR
|
||||
* Padding Delay subfield.
|
||||
*/
|
||||
u32 pad_delay = u16_get_bits(eml_cap,
|
||||
IEEE80211_EML_CAP_EMLSR_PADDING_DELAY);
|
||||
IEEE80211_EML_CAP_EML_PADDING_DELAY);
|
||||
|
||||
if (!pad_delay ||
|
||||
pad_delay > IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US)
|
||||
pad_delay > IEEE80211_EML_CAP_EML_PADDING_DELAY_256US)
|
||||
return 0;
|
||||
|
||||
return 32 * (1 << (pad_delay - 1));
|
||||
|
|
@ -1145,12 +1152,18 @@ static inline u32 ieee80211_emlsr_pad_delay_in_us(u16 eml_cap)
|
|||
|
||||
static inline u32 ieee80211_emlsr_trans_delay_in_us(u16 eml_cap)
|
||||
{
|
||||
u32 emlsr_supp =
|
||||
u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLSR_SUPP);
|
||||
|
||||
if (!emlsr_supp)
|
||||
return 0;
|
||||
|
||||
/* IEEE Std 802.11be-2024 Table 9-417j—Encoding of the EMLSR
|
||||
* Transition Delay subfield.
|
||||
*/
|
||||
u32 trans_delay =
|
||||
u16_get_bits(eml_cap,
|
||||
IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY);
|
||||
IEEE80211_EML_CAP_EML_TRANSITION_DELAY);
|
||||
|
||||
/* invalid values also just use 0 */
|
||||
if (!trans_delay ||
|
||||
|
|
@ -1161,11 +1174,73 @@ static inline u32 ieee80211_emlsr_trans_delay_in_us(u16 eml_cap)
|
|||
}
|
||||
|
||||
/**
|
||||
* ieee80211_eml_trans_timeout_in_us - Fetch the EMLSR Transition
|
||||
* ieee80211_emlmr_pad_delay_in_us - Fetch the EMLMR Padding delay
|
||||
* in microseconds
|
||||
* @eml_cap: EML capabilities field value from common info field of
|
||||
* the Multi-link element
|
||||
* Return: the EMLMR Padding delay (in microseconds) encoded in the
|
||||
* EML Capabilities field
|
||||
*/
|
||||
|
||||
static inline u32 ieee80211_emlmr_pad_delay_in_us(u16 eml_cap)
|
||||
{
|
||||
u32 emlmr_supp =
|
||||
u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLMR_SUPPORT);
|
||||
|
||||
if (!emlmr_supp)
|
||||
return 0;
|
||||
|
||||
/* IEEE Std 802.11be-2024 Table 9-417k—Encoding of the EMLMR
|
||||
* Padding Delay subfield.
|
||||
*/
|
||||
u32 pad_delay = u16_get_bits(eml_cap,
|
||||
IEEE80211_EML_CAP_EML_PADDING_DELAY);
|
||||
|
||||
if (!pad_delay ||
|
||||
pad_delay > IEEE80211_EML_CAP_EML_PADDING_DELAY_256US)
|
||||
return 0;
|
||||
|
||||
return 32 * (1 << (pad_delay - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_emlmr_trans_delay_in_us - Fetch the EMLMR Transition
|
||||
* delay in microseconds
|
||||
* @eml_cap: EML capabilities field value from common info field of
|
||||
* the Multi-link element
|
||||
* Return: the EMLMR Transition delay (in microseconds) encoded in the
|
||||
* EML Capabilities field
|
||||
*/
|
||||
|
||||
static inline u32 ieee80211_emlmr_trans_delay_in_us(u16 eml_cap)
|
||||
{
|
||||
u32 emlmr_supp =
|
||||
u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLMR_SUPPORT);
|
||||
|
||||
if (!emlmr_supp)
|
||||
return 0;
|
||||
|
||||
/* IEEE Std 802.11be-2024 Table 9-417l—Encoding of the EMLMR
|
||||
* Transition Delay subfield.
|
||||
*/
|
||||
u32 trans_delay =
|
||||
u16_get_bits(eml_cap,
|
||||
IEEE80211_EML_CAP_EML_TRANSITION_DELAY);
|
||||
|
||||
/* invalid values also just use 0 */
|
||||
if (!trans_delay ||
|
||||
trans_delay > IEEE80211_EML_CAP_EMLMR_TRANSITION_DELAY_256US)
|
||||
return 0;
|
||||
|
||||
return 32 * (1 << (trans_delay - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_eml_trans_timeout_in_us - Fetch the EML Transition
|
||||
* timeout value in microseconds
|
||||
* @eml_cap: EML capabilities field value from common info field of
|
||||
* the Multi-link element
|
||||
* Return: the EMLSR Transition timeout (in microseconds) encoded in
|
||||
* Return: the EML Transition timeout (in microseconds) encoded in
|
||||
* the EML Capabilities field
|
||||
*/
|
||||
|
||||
|
|
@ -1178,7 +1253,7 @@ static inline u32 ieee80211_eml_trans_timeout_in_us(u16 eml_cap)
|
|||
IEEE80211_EML_CAP_TRANSITION_TIMEOUT);
|
||||
|
||||
/* invalid values also just use 0 */
|
||||
if (!timeout || timeout > IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU)
|
||||
if (!timeout || timeout > IEEE80211_EML_CAP_TRANSITION_TIMEOUT_64TU)
|
||||
return 0;
|
||||
|
||||
return 128 * (1 << (timeout - 1));
|
||||
|
|
|
|||
|
|
@ -1396,6 +1396,13 @@ struct cfg80211_rnr_elems {
|
|||
* @he_bss_color: BSS Color settings
|
||||
* @he_bss_color_valid: indicates whether bss color
|
||||
* attribute is present in beacon data or not.
|
||||
* @ht_required: stations must support HT
|
||||
* @vht_required: stations must support VHT
|
||||
* @ht_oper: HT operation element (or %NULL if HT isn't enabled)
|
||||
* @vht_oper: VHT operation element (or %NULL if VHT isn't enabled)
|
||||
* @he_oper: HE operation IE (or %NULL if HE isn't enabled)
|
||||
* @eht_oper: EHT operation IE (or %NULL if EHT isn't enabled)
|
||||
* @uhr_oper: UHR operation (or %NULL if UHR isn't enabled)
|
||||
*/
|
||||
struct cfg80211_beacon_data {
|
||||
unsigned int link_id;
|
||||
|
|
@ -1420,6 +1427,13 @@ struct cfg80211_beacon_data {
|
|||
size_t civicloc_len;
|
||||
struct cfg80211_he_bss_color he_bss_color;
|
||||
bool he_bss_color_valid;
|
||||
|
||||
bool ht_required, vht_required;
|
||||
const struct ieee80211_ht_operation *ht_oper;
|
||||
const struct ieee80211_vht_operation *vht_oper;
|
||||
const struct ieee80211_he_operation *he_oper;
|
||||
const struct ieee80211_eht_operation *eht_oper;
|
||||
const struct ieee80211_uhr_operation *uhr_oper;
|
||||
};
|
||||
|
||||
struct mac_address {
|
||||
|
|
@ -1524,16 +1538,9 @@ struct cfg80211_s1g_short_beacon {
|
|||
* @vht_cap: VHT capabilities (or %NULL if VHT isn't enabled)
|
||||
* @he_cap: HE capabilities (or %NULL if HE isn't enabled)
|
||||
* @eht_cap: EHT capabilities (or %NULL if EHT isn't enabled)
|
||||
* @eht_oper: EHT operation IE (or %NULL if EHT isn't enabled)
|
||||
* @uhr_oper: UHR operation (or %NULL if UHR isn't enabled)
|
||||
* @ht_required: stations must support HT
|
||||
* @vht_required: stations must support VHT
|
||||
* @twt_responder: Enable Target Wait Time
|
||||
* @he_required: stations must support HE
|
||||
* @sae_h2e_required: stations must support direct H2E technique in SAE
|
||||
* @flags: flags, as defined in &enum nl80211_ap_settings_flags
|
||||
* @he_obss_pd: OBSS Packet Detection settings
|
||||
* @he_oper: HE operation IE (or %NULL if HE isn't enabled)
|
||||
* @fils_discovery: FILS discovery transmission parameters
|
||||
* @unsol_bcast_probe_resp: Unsolicited broadcast probe response parameters
|
||||
* @mbssid_config: AP settings for multiple bssid
|
||||
|
|
@ -1562,11 +1569,7 @@ struct cfg80211_ap_settings {
|
|||
const struct ieee80211_ht_cap *ht_cap;
|
||||
const struct ieee80211_vht_cap *vht_cap;
|
||||
const struct ieee80211_he_cap_elem *he_cap;
|
||||
const struct ieee80211_he_operation *he_oper;
|
||||
const struct ieee80211_eht_cap_elem *eht_cap;
|
||||
const struct ieee80211_eht_operation *eht_oper;
|
||||
const struct ieee80211_uhr_operation *uhr_oper;
|
||||
bool ht_required, vht_required, he_required, sae_h2e_required;
|
||||
bool twt_responder;
|
||||
u32 flags;
|
||||
struct ieee80211_he_obss_pd he_obss_pd;
|
||||
|
|
|
|||
|
|
@ -1747,18 +1747,18 @@ struct ieee80211_rx_status {
|
|||
u8 he_ru:3;
|
||||
u8 he_gi:2;
|
||||
u8 he_dcm:1;
|
||||
};
|
||||
} __packed;
|
||||
struct {
|
||||
u8 ru:4;
|
||||
u8 gi:2;
|
||||
} eht;
|
||||
} __packed eht;
|
||||
struct {
|
||||
u8 ru:4;
|
||||
u8 gi:2;
|
||||
u8 elr:1;
|
||||
u8 im:1;
|
||||
} uhr;
|
||||
};
|
||||
} __packed uhr;
|
||||
} __packed;
|
||||
u8 rate_idx;
|
||||
u8 nss;
|
||||
u8 rx_flags;
|
||||
|
|
@ -1771,6 +1771,8 @@ struct ieee80211_rx_status {
|
|||
u8 link_valid:1, link_id:4;
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct ieee80211_rx_status) <= sizeof_field(struct sk_buff, cb));
|
||||
|
||||
static inline u32
|
||||
ieee80211_rx_status_to_khz(struct ieee80211_rx_status *rx_status)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1204,10 +1204,12 @@
|
|||
* user space through the connect result as the user space would have
|
||||
* initiated the connection through the connect request.
|
||||
*
|
||||
* @NL80211_CMD_STA_OPMODE_CHANGED: An event that notify station's
|
||||
* ht opmode or vht opmode changes using any of %NL80211_ATTR_SMPS_MODE,
|
||||
* %NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its
|
||||
* address(specified in %NL80211_ATTR_MAC).
|
||||
* @NL80211_CMD_STA_OPMODE_CHANGED: An event that notifies that a station's
|
||||
* HT opmode or VHT opmode changed using any of %NL80211_ATTR_SMPS_MODE,
|
||||
* %NL80211_ATTR_CHANNEL_WIDTH, %NL80211_ATTR_NSS attributes with its
|
||||
* address (specified in %NL80211_ATTR_MAC).
|
||||
* Note that 80+80 and 160 MHz might not be differentiated, i.e. may
|
||||
* report %NL80211_CHAN_WIDTH_160 instead of %NL80211_CHAN_WIDTH_80P80.
|
||||
*
|
||||
* @NL80211_CMD_GET_FTM_RESPONDER_STATS: Retrieve FTM responder statistics, in
|
||||
* the %NL80211_ATTR_FTM_RESPONDER_STATS attribute.
|
||||
|
|
|
|||
|
|
@ -1312,6 +1312,120 @@ ieee80211_copy_rnr_beacon(u8 *pos, struct cfg80211_rnr_elems *dst,
|
|||
return offset;
|
||||
}
|
||||
|
||||
static enum ieee80211_sta_rx_bandwidth
|
||||
ieee80211_calc_ap_he_and_lower(struct cfg80211_beacon_data *params)
|
||||
{
|
||||
const struct ieee80211_vht_operation *vht_oper = params->vht_oper;
|
||||
int ccfs0, ccfs1;
|
||||
|
||||
if (params->he_oper) {
|
||||
const struct ieee80211_he_6ghz_oper *he_6ghz_oper;
|
||||
|
||||
if (params->he_oper->he_oper_params &
|
||||
cpu_to_le32(IEEE80211_HE_OPERATION_VHT_OPER_INFO))
|
||||
vht_oper = (void *)params->he_oper->optional;
|
||||
|
||||
he_6ghz_oper = ieee80211_he_6ghz_oper(params->he_oper);
|
||||
if (he_6ghz_oper) {
|
||||
switch (u8_get_bits(he_6ghz_oper->control,
|
||||
IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH)) {
|
||||
case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ:
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ:
|
||||
return IEEE80211_STA_RX_BW_40;
|
||||
case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ:
|
||||
return IEEE80211_STA_RX_BW_80;
|
||||
case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ:
|
||||
return IEEE80211_STA_RX_BW_160;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vht_oper) {
|
||||
switch (vht_oper->chan_width) {
|
||||
case IEEE80211_VHT_CHANWIDTH_USE_HT:
|
||||
/* check for HT (or fall down to 20) below */
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
||||
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
||||
/* deprecated encodings */
|
||||
return IEEE80211_STA_RX_BW_160;
|
||||
case IEEE80211_VHT_CHANWIDTH_80MHZ:
|
||||
/*
|
||||
* See IEEE 802.11-2020 Table 9-352-BSS bandwidth
|
||||
* when the VHT Operation Information field Channel
|
||||
* Width subfield is 1
|
||||
*
|
||||
* (IEEE80211_VHT_CHANWIDTH_80MHZ == 1)
|
||||
*/
|
||||
ccfs0 = vht_oper->center_freq_seg0_idx;
|
||||
ccfs1 = vht_oper->center_freq_seg1_idx;
|
||||
if (!ccfs0)
|
||||
return IEEE80211_STA_RX_BW_80;
|
||||
if (ccfs1 && abs(ccfs1 - ccfs0) == 8)
|
||||
return IEEE80211_STA_RX_BW_160;
|
||||
/* 80+80 - RX BW doesn't cover that / uses 160 */
|
||||
if (ccfs1 && abs(ccfs1 - ccfs0) > 16)
|
||||
return IEEE80211_STA_RX_BW_160;
|
||||
fallthrough;
|
||||
default:
|
||||
/* reserved encoding - assume 80 */
|
||||
return IEEE80211_STA_RX_BW_80;
|
||||
}
|
||||
}
|
||||
|
||||
if (params->ht_oper) {
|
||||
switch (u8_get_bits(params->ht_oper->ht_param,
|
||||
IEEE80211_HT_PARAM_CHA_SEC_OFFSET)) {
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
|
||||
default: /* invalid values */
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
return IEEE80211_STA_RX_BW_40;
|
||||
}
|
||||
}
|
||||
|
||||
/* nothing found, must be 20 MHz */
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
}
|
||||
|
||||
static void ieee80211_update_ap_bandwidth(struct ieee80211_link_data *link,
|
||||
struct cfg80211_beacon_data *params)
|
||||
{
|
||||
struct ieee80211_local *local = link->sdata->local;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
|
||||
/*
|
||||
* Updating the beacon might, without even changing the channel, cause
|
||||
* the usable bandwidth for some stations to be changed, for example
|
||||
* if the beacon configuration is EHT with 160 MHz, HE could change
|
||||
* between 20, 40, 80 and 160 MHz, and HE (or lower) clients need to
|
||||
* be handled accordingly.
|
||||
* Calculate the HE and lower bandwidth and apply that to all stations.
|
||||
*
|
||||
* In the future, this also needs to calculate EHT bandwidth and apply
|
||||
* it to all stations not using UHR DBE, since the chandef would then
|
||||
* include DBE.
|
||||
*/
|
||||
|
||||
if (link->conf->chanreq.oper.chan->band == NL80211_BAND_S1GHZ)
|
||||
return;
|
||||
|
||||
link->bss_bw.he_and_lower = ieee80211_calc_ap_he_and_lower(params);
|
||||
|
||||
chanctx_conf = sdata_dereference(link->conf->chanctx_conf, link->sdata);
|
||||
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
|
||||
|
||||
/*
|
||||
* Note: this relies on ieee80211_recalc_chanctx_min_def() having
|
||||
* the side effect of updating all stations, if they changed; that
|
||||
* was normally for when the chandef changed but is used here too.
|
||||
*/
|
||||
ieee80211_recalc_chanctx_min_def(local, chanctx);
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_link_data *link,
|
||||
|
|
@ -1450,6 +1564,8 @@ ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
|
|||
if (old)
|
||||
kfree_rcu(old, rcu_head);
|
||||
|
||||
ieee80211_update_ap_bandwidth(link, params);
|
||||
|
||||
*changed |= _changed;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1541,13 +1657,13 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
|||
cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
|
||||
}
|
||||
|
||||
if (params->he_cap && params->he_oper) {
|
||||
if (params->he_cap && params->beacon.he_oper) {
|
||||
link_conf->he_support = true;
|
||||
link_conf->htc_trig_based_pkt_ext =
|
||||
le32_get_bits(params->he_oper->he_oper_params,
|
||||
le32_get_bits(params->beacon.he_oper->he_oper_params,
|
||||
IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK);
|
||||
link_conf->frame_time_rts_th =
|
||||
le32_get_bits(params->he_oper->he_oper_params,
|
||||
le32_get_bits(params->beacon.he_oper->he_oper_params,
|
||||
IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK);
|
||||
changed |= BSS_CHANGED_HE_OBSS_PD;
|
||||
|
||||
|
|
@ -1596,7 +1712,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
|||
IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ |
|
||||
IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ);
|
||||
link_conf->eht_disable_mcs15 =
|
||||
u8_get_bits(params->eht_oper->params,
|
||||
u8_get_bits(params->beacon.eht_oper->params,
|
||||
IEEE80211_EHT_OPER_MCS15_DISABLE);
|
||||
} else {
|
||||
link_conf->eht_su_beamformer = false;
|
||||
|
|
@ -1604,7 +1720,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
|
|||
link_conf->eht_mu_beamformer = false;
|
||||
}
|
||||
|
||||
if (params->uhr_oper) {
|
||||
if (params->beacon.uhr_oper) {
|
||||
if (!link_conf->eht_support)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
|
|
@ -2221,7 +2337,15 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
|
|||
ieee80211_s1g_cap_to_sta_s1g_cap(sdata, params->s1g_capa,
|
||||
link_sta);
|
||||
|
||||
ieee80211_sta_init_nss(link_sta);
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_NAN:
|
||||
case NL80211_IFTYPE_NAN_DATA:
|
||||
/* not applicable - they don't use NSS/BW as capability */
|
||||
break;
|
||||
default:
|
||||
ieee80211_sta_init_nss_bw_capa(link_sta, &link->conf->chanreq.oper);
|
||||
break;
|
||||
}
|
||||
|
||||
if (params->opmode_notif_used) {
|
||||
enum nl80211_chan_width width = link->conf->chanreq.oper.width;
|
||||
|
|
|
|||
|
|
@ -438,11 +438,12 @@ ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static enum nl80211_chan_width ieee80211_get_sta_bw(struct sta_info *sta,
|
||||
unsigned int link_id)
|
||||
static enum nl80211_chan_width
|
||||
ieee80211_get_sta_bw(struct sta_info *sta, struct ieee80211_link_data *link)
|
||||
{
|
||||
enum ieee80211_sta_rx_bandwidth width;
|
||||
struct link_sta_info *link_sta;
|
||||
int link_id = link->link_id;
|
||||
|
||||
link_sta = wiphy_dereference(sta->local->hw.wiphy, sta->link[link_id]);
|
||||
|
||||
|
|
@ -454,45 +455,28 @@ static enum nl80211_chan_width ieee80211_get_sta_bw(struct sta_info *sta,
|
|||
* We assume that TX/RX might be asymmetric (so e.g. VHT operating
|
||||
* mode notification changes what a STA wants to receive, but not
|
||||
* necessarily what it will transmit to us), and therefore use the
|
||||
* capabilities here. Calling it RX bandwidth capability is a bit
|
||||
* wrong though, since capabilities are in fact symmetric.
|
||||
* "from station" bandwidth here.
|
||||
*/
|
||||
width = ieee80211_sta_cap_rx_bw(link_sta);
|
||||
width = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper,
|
||||
IEEE80211_STA_BW_RX_FROM_STA);
|
||||
|
||||
switch (width) {
|
||||
case IEEE80211_STA_RX_BW_20:
|
||||
if (link_sta->pub->ht_cap.ht_supported)
|
||||
return NL80211_CHAN_WIDTH_20;
|
||||
else
|
||||
return NL80211_CHAN_WIDTH_20_NOHT;
|
||||
case IEEE80211_STA_RX_BW_40:
|
||||
return NL80211_CHAN_WIDTH_40;
|
||||
case IEEE80211_STA_RX_BW_80:
|
||||
return NL80211_CHAN_WIDTH_80;
|
||||
case IEEE80211_STA_RX_BW_160:
|
||||
/*
|
||||
* This applied for both 160 and 80+80. since we use
|
||||
* the returned value to consider degradation of
|
||||
* ctx->conf.min_def, we have to make sure to take
|
||||
* the bigger one (NL80211_CHAN_WIDTH_160).
|
||||
* Otherwise we might try degrading even when not
|
||||
* needed, as the max required sta_bw returned (80+80)
|
||||
* might be smaller than the configured bw (160).
|
||||
*/
|
||||
return NL80211_CHAN_WIDTH_160;
|
||||
case IEEE80211_STA_RX_BW_320:
|
||||
return NL80211_CHAN_WIDTH_320;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return NL80211_CHAN_WIDTH_20;
|
||||
}
|
||||
if (width == IEEE80211_STA_RX_BW_20 &&
|
||||
!link_sta->pub->ht_cap.ht_supported &&
|
||||
!link_sta->pub->he_cap.has_he)
|
||||
return NL80211_CHAN_WIDTH_20_NOHT;
|
||||
|
||||
/*
|
||||
* This returns 160 for both 160 and 80+80. Since we use
|
||||
* the returned value to consider narrowing for
|
||||
* ctx->conf.min_def, that's correct and necessary.
|
||||
*/
|
||||
return ieee80211_sta_rx_bw_to_chan_width(width);
|
||||
}
|
||||
|
||||
static enum nl80211_chan_width
|
||||
ieee80211_get_max_required_bw(struct ieee80211_link_data *link)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = link->sdata;
|
||||
unsigned int link_id = link->link_id;
|
||||
enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
struct sta_info *sta;
|
||||
|
||||
|
|
@ -503,7 +487,7 @@ ieee80211_get_max_required_bw(struct ieee80211_link_data *link)
|
|||
!(sta->sdata->bss && sta->sdata->bss == sdata->bss))
|
||||
continue;
|
||||
|
||||
max_bw = max(max_bw, ieee80211_get_sta_bw(sta, link_id));
|
||||
max_bw = max(max_bw, ieee80211_get_sta_bw(sta, link));
|
||||
}
|
||||
|
||||
return max_bw;
|
||||
|
|
@ -709,8 +693,9 @@ static void ieee80211_chan_bw_change(struct ieee80211_local *local,
|
|||
else
|
||||
new_chandef = &link_conf->chanreq.oper;
|
||||
|
||||
new_sta_bw = _ieee80211_sta_cur_vht_bw(link_sta,
|
||||
new_chandef);
|
||||
new_sta_bw = ieee80211_sta_current_bw(link_sta,
|
||||
new_chandef,
|
||||
IEEE80211_STA_BW_TX_TO_STA);
|
||||
|
||||
/* nothing change */
|
||||
if (new_sta_bw == link_sta->pub->bandwidth)
|
||||
|
|
@ -733,6 +718,9 @@ static void ieee80211_chan_bw_change(struct ieee80211_local *local,
|
|||
* recalc the min required chan width of the channel context, which is
|
||||
* the max of min required widths of all the interfaces bound to this
|
||||
* channel context.
|
||||
*
|
||||
* Note: ieee80211_update_ap_bandwidth() relies on this iterating all
|
||||
* affected stations, even if min_def didn't change.
|
||||
*/
|
||||
static void
|
||||
_ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
|
||||
|
|
@ -743,13 +731,11 @@ _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
|
|||
u32 changed = __ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for,
|
||||
check_reserved);
|
||||
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
/* check is BW narrowed */
|
||||
ieee80211_chan_bw_change(local, ctx, false, true);
|
||||
|
||||
drv_change_chanctx(local, ctx, changed);
|
||||
if (changed)
|
||||
drv_change_chanctx(local, ctx, changed);
|
||||
|
||||
/* check is BW wider */
|
||||
ieee80211_chan_bw_change(local, ctx, false, false);
|
||||
|
|
|
|||
|
|
@ -320,26 +320,17 @@ static ssize_t aql_enable_read(struct file *file, char __user *user_buf,
|
|||
static ssize_t aql_enable_write(struct file *file, const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char buf[3];
|
||||
size_t len;
|
||||
bool val;
|
||||
int ret;
|
||||
|
||||
if (count > sizeof(buf))
|
||||
return -EINVAL;
|
||||
ret = kstrtobool_from_user(user_buf, count, &val);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
if (copy_from_user(buf, user_buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
len = strlen(buf);
|
||||
if (len > 0 && buf[len - 1] == '\n')
|
||||
buf[len - 1] = 0;
|
||||
|
||||
if (buf[0] == '0' && buf[1] == '\0')
|
||||
static_branch_enable(&aql_disable);
|
||||
else if (buf[0] == '1' && buf[1] == '\0')
|
||||
if (val)
|
||||
static_branch_disable(&aql_disable);
|
||||
else
|
||||
return -EINVAL;
|
||||
static_branch_enable(&aql_disable);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
|
@ -371,26 +362,14 @@ static ssize_t force_tx_status_write(struct file *file,
|
|||
loff_t *ppos)
|
||||
{
|
||||
struct ieee80211_local *local = file->private_data;
|
||||
char buf[3];
|
||||
bool val;
|
||||
int ret;
|
||||
|
||||
if (count >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(buf, user_buf, count))
|
||||
return -EFAULT;
|
||||
|
||||
if (count && buf[count - 1] == '\n')
|
||||
buf[count - 1] = '\0';
|
||||
else
|
||||
buf[count] = '\0';
|
||||
|
||||
if (buf[0] == '0' && buf[1] == '\0')
|
||||
local->force_tx_status = 0;
|
||||
else if (buf[0] == '1' && buf[1] == '\0')
|
||||
local->force_tx_status = 1;
|
||||
else
|
||||
return -EINVAL;
|
||||
ret = kstrtobool_from_user(user_buf, count, &val);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
local->force_tx_status = val;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* EHT handling
|
||||
*
|
||||
* Copyright(c) 2021-2025 Intel Corporation
|
||||
* Copyright(c) 2021-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include "driver-ops.h"
|
||||
|
|
@ -74,9 +74,6 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
eht_cap->has_eht = true;
|
||||
|
||||
link_sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(link_sta);
|
||||
link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta);
|
||||
|
||||
/*
|
||||
* The MPDU length bits are reserved on all but 2.4 GHz and get set via
|
||||
* VHT (5 GHz) or HE (6 GHz) capabilities.
|
||||
|
|
@ -204,7 +201,7 @@ void ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
|
|||
pad_delay = u8_get_bits(ptr[2],
|
||||
IEEE80211_EML_EMLSR_PAD_DELAY);
|
||||
if (pad_delay >
|
||||
IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US)
|
||||
IEEE80211_EML_CAP_EML_PADDING_DELAY_256US)
|
||||
return;
|
||||
|
||||
trans_delay = u8_get_bits(ptr[2],
|
||||
|
|
@ -217,11 +214,11 @@ void ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
|
|||
sta->sta.eml_cap =
|
||||
u8_replace_bits(sta->sta.eml_cap,
|
||||
pad_delay,
|
||||
IEEE80211_EML_CAP_EMLSR_PADDING_DELAY);
|
||||
IEEE80211_EML_CAP_EML_PADDING_DELAY);
|
||||
sta->sta.eml_cap =
|
||||
u8_replace_bits(sta->sta.eml_cap,
|
||||
trans_delay,
|
||||
IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY);
|
||||
IEEE80211_EML_CAP_EML_TRANSITION_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* HE handling
|
||||
*
|
||||
* Copyright(c) 2017 Intel Deutschland GmbH
|
||||
* Copyright(c) 2019-2025 Intel Corporation
|
||||
* Copyright(c) 2019-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include "ieee80211_i.h"
|
||||
|
|
@ -159,10 +159,6 @@ _ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
he_cap->has_he = true;
|
||||
|
||||
link_sta->cur_max_bandwidth = ieee80211_sta_cap_rx_bw(link_sta);
|
||||
if (sdata->vif.type != NL80211_IFTYPE_NAN)
|
||||
link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta);
|
||||
|
||||
if (he_6ghz_capa)
|
||||
ieee80211_update_from_he_6ghz_capa(he_6ghz_capa, link_sta);
|
||||
|
||||
|
|
@ -277,7 +273,8 @@ static void ieee80211_link_sta_rc_update_omi(struct ieee80211_link_data *link,
|
|||
band = link->conf->chanreq.oper.chan->band;
|
||||
sband = sdata->local->hw.wiphy->bands[band];
|
||||
|
||||
new_bw = ieee80211_sta_cur_vht_bw(link_sta);
|
||||
new_bw = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper,
|
||||
IEEE80211_STA_BW_TX_TO_STA);
|
||||
if (link_sta->pub->bandwidth == new_bw)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -140,14 +140,11 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
|||
const struct ieee80211_ht_cap *ht_cap_ie,
|
||||
struct link_sta_info *link_sta)
|
||||
{
|
||||
struct ieee80211_bss_conf *link_conf;
|
||||
struct sta_info *sta = link_sta->sta;
|
||||
struct ieee80211_sta_ht_cap ht_cap, own_cap;
|
||||
u8 ampdu_info, tx_mcs_set_cap;
|
||||
int i, max_tx_streams;
|
||||
bool changed;
|
||||
enum ieee80211_sta_rx_bandwidth bw;
|
||||
enum nl80211_chan_width width;
|
||||
|
||||
memset(&ht_cap, 0, sizeof(ht_cap));
|
||||
|
||||
|
|
@ -256,45 +253,6 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
memcpy(&link_sta->pub->ht_cap, &ht_cap, sizeof(ht_cap));
|
||||
|
||||
rcu_read_lock();
|
||||
link_conf = rcu_dereference(sdata->vif.link_conf[link_sta->link_id]);
|
||||
if (WARN_ON(!link_conf)) {
|
||||
width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
} else if (sdata->vif.type == NL80211_IFTYPE_NAN ||
|
||||
sdata->vif.type == NL80211_IFTYPE_NAN_DATA) {
|
||||
/* In NAN, link_sta->bandwidth is invalid since NAN operates on
|
||||
* multiple channels. Just take the maximum.
|
||||
*/
|
||||
width = NL80211_CHAN_WIDTH_320;
|
||||
} else {
|
||||
width = link_conf->chanreq.oper.width;
|
||||
}
|
||||
|
||||
switch (width) {
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
fallthrough;
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
bw = IEEE80211_STA_RX_BW_20;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
case NL80211_CHAN_WIDTH_320:
|
||||
bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
|
||||
break;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
link_sta->pub->bandwidth = bw;
|
||||
|
||||
link_sta->cur_max_bandwidth =
|
||||
ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
|
||||
|
||||
if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
|
||||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
|
||||
sta->sdata->vif.type == NL80211_IFTYPE_NAN ||
|
||||
|
|
@ -629,25 +587,30 @@ void ieee80211_ht_handle_chanwidth_notif(struct ieee80211_local *local,
|
|||
enum ieee80211_sta_rx_bandwidth max_bw, new_bw;
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct sta_opmode_info sta_opmode = {};
|
||||
struct ieee80211_link_data *link;
|
||||
|
||||
lockdep_assert_wiphy(local->hw.wiphy);
|
||||
|
||||
link = sdata_dereference(sdata->link[link_sta->link_id], sdata);
|
||||
if (WARN_ON(!link))
|
||||
return;
|
||||
|
||||
if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ)
|
||||
max_bw = IEEE80211_STA_RX_BW_20;
|
||||
else
|
||||
max_bw = ieee80211_sta_cap_rx_bw(link_sta);
|
||||
max_bw = IEEE80211_STA_RX_BW_MAX;
|
||||
|
||||
/* set cur_max_bandwidth and recalc sta bw */
|
||||
link_sta->cur_max_bandwidth = max_bw;
|
||||
new_bw = ieee80211_sta_cur_vht_bw(link_sta);
|
||||
/* set op_mode_bw and recalc sta bw */
|
||||
link_sta->op_mode_bw = max_bw;
|
||||
new_bw = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper,
|
||||
IEEE80211_STA_BW_TX_TO_STA);
|
||||
|
||||
if (link_sta->pub->bandwidth == new_bw)
|
||||
return;
|
||||
|
||||
link_sta->pub->bandwidth = new_bw;
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
sta_opmode.bw =
|
||||
ieee80211_sta_rx_bw_to_chan_width(link_sta);
|
||||
sta_opmode.bw = ieee80211_sta_rx_bw_to_chan_width(new_bw);
|
||||
sta_opmode.changed = STA_OPMODE_MAX_BW_CHANGED;
|
||||
|
||||
rate_control_rate_update(local, sband, link_sta,
|
||||
|
|
|
|||
|
|
@ -553,6 +553,9 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
|
|||
|
||||
memcpy(addr, sta->sta.addr, ETH_ALEN);
|
||||
|
||||
ieee80211_sta_init_nss_bw_capa(&sta->deflink,
|
||||
&sdata->deflink.conf->chanreq.oper);
|
||||
|
||||
ibss_dbg(sdata, "Adding new IBSS station %pM\n", addr);
|
||||
|
||||
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
|
||||
|
|
|
|||
|
|
@ -1133,6 +1133,10 @@ struct ieee80211_link_data {
|
|||
|
||||
struct ieee80211_bss_conf *conf;
|
||||
|
||||
struct {
|
||||
enum ieee80211_sta_rx_bandwidth he_and_lower;
|
||||
} bss_bw;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
struct dentry *debugfs_dir;
|
||||
#endif
|
||||
|
|
@ -2299,25 +2303,6 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
|||
const struct ieee80211_vht_cap *vht_cap_ie,
|
||||
const struct ieee80211_vht_cap *vht_cap_ie2,
|
||||
struct link_sta_info *link_sta);
|
||||
enum ieee80211_sta_rx_bandwidth
|
||||
_ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
static inline enum ieee80211_sta_rx_bandwidth
|
||||
ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta)
|
||||
{
|
||||
return _ieee80211_sta_cap_rx_bw(link_sta, NULL);
|
||||
}
|
||||
enum ieee80211_sta_rx_bandwidth
|
||||
_ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
static inline enum ieee80211_sta_rx_bandwidth
|
||||
ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta)
|
||||
{
|
||||
return _ieee80211_sta_cur_vht_bw(link_sta, NULL);
|
||||
}
|
||||
void ieee80211_sta_init_nss(struct link_sta_info *link_sta);
|
||||
enum nl80211_chan_width
|
||||
ieee80211_sta_cap_chan_bw(struct link_sta_info *link_sta);
|
||||
void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_link_data *link,
|
||||
struct ieee80211_mgmt *mgmt);
|
||||
|
|
@ -2331,8 +2316,6 @@ void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
|
|||
struct ieee80211_sta_vht_cap *vht_cap);
|
||||
void ieee80211_get_vht_mask_from_cap(__le16 vht_cap,
|
||||
u16 vht_mask[NL80211_VHT_NSS_MAX]);
|
||||
enum nl80211_chan_width
|
||||
ieee80211_sta_rx_bw_to_chan_width(struct link_sta_info *sta);
|
||||
|
||||
/* HE */
|
||||
void
|
||||
|
|
@ -2718,6 +2701,9 @@ void ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata,
|
|||
void ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb);
|
||||
|
||||
enum nl80211_chan_width
|
||||
ieee80211_sta_rx_bw_to_chan_width(enum ieee80211_sta_rx_bandwidth bw);
|
||||
|
||||
/* element building in SKBs */
|
||||
int ieee80211_put_srates_elem(struct sk_buff *skb,
|
||||
const struct ieee80211_supported_band *sband,
|
||||
|
|
|
|||
|
|
@ -470,6 +470,9 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
|
|||
elems->eht_cap, elems->eht_cap_len,
|
||||
&sta->deflink);
|
||||
|
||||
ieee80211_sta_init_nss_bw_capa(&sta->deflink,
|
||||
&sdata->deflink.conf->chanreq.oper);
|
||||
|
||||
if (bw != sta->sta.deflink.bandwidth)
|
||||
changed |= IEEE80211_RC_BW_CHANGED;
|
||||
|
||||
|
|
|
|||
|
|
@ -2571,8 +2571,9 @@ static void ieee80211_csa_switch_work(struct wiphy *wiphy,
|
|||
return;
|
||||
|
||||
link_sta->pub->bandwidth =
|
||||
_ieee80211_sta_cur_vht_bw(link_sta,
|
||||
&link->csa.chanreq.oper);
|
||||
ieee80211_sta_current_bw(link_sta,
|
||||
&link->csa.chanreq.oper,
|
||||
IEEE80211_STA_BW_TX_TO_STA);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -5748,20 +5749,13 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
|
|||
* next beacon and update then.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If an operating mode notification IE is present, override the
|
||||
* NSS calculation (that would be done in rate_control_rate_init())
|
||||
* and use the # of streams from that element.
|
||||
*/
|
||||
if (elems->opmode_notif &&
|
||||
!(*elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)) {
|
||||
u8 nss;
|
||||
ieee80211_sta_init_nss_bw_capa(link_sta, &bss_conf->chanreq.oper);
|
||||
|
||||
nss = *elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
|
||||
nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
|
||||
nss += 1;
|
||||
link_sta->pub->rx_nss = nss;
|
||||
}
|
||||
/* If an operating mode notification element is present, use it. */
|
||||
if (elems->opmode_notif)
|
||||
__ieee80211_vht_handle_opmode(sdata, link_sta,
|
||||
*elems->opmode_notif,
|
||||
sband->band);
|
||||
|
||||
/*
|
||||
* Always handle WMM once after association regardless
|
||||
|
|
@ -10617,7 +10611,6 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata,
|
|||
if (add_links_data->link[link_id].status != WLAN_STATUS_SUCCESS)
|
||||
goto disconnect;
|
||||
|
||||
ieee80211_sta_init_nss(link_sta);
|
||||
if (ieee80211_sta_activate_link(sta, link_id))
|
||||
goto disconnect;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* Copyright: (c) 2014 Czech Technical University in Prague
|
||||
* (c) 2014 Volkswagen Group Research
|
||||
* Copyright (C) 2022 - 2024 Intel Corporation
|
||||
* Copyright (C) 2022 - 2024, 2026 Intel Corporation
|
||||
* Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz>
|
||||
* Funded by: Volkswagen Group Research
|
||||
*/
|
||||
|
|
@ -92,6 +92,9 @@ static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta)
|
|||
|
||||
memcpy(addr, sta->sta.addr, ETH_ALEN);
|
||||
|
||||
ieee80211_sta_init_nss_bw_capa(&sta->deflink,
|
||||
&sdata->deflink.conf->chanreq.oper);
|
||||
|
||||
ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n",
|
||||
addr, sdata->name);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright 2005-2006, Devicescape Software, Inc.
|
||||
* Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
|
||||
* Copyright 2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2019, 2022-2025 Intel Corporation
|
||||
* Copyright (C) 2019, 2022-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
|
@ -38,8 +38,6 @@ void rate_control_rate_init(struct link_sta_info *link_sta)
|
|||
struct ieee80211_supported_band *sband;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
|
||||
ieee80211_sta_init_nss(link_sta);
|
||||
|
||||
if (!ref)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -572,11 +572,13 @@ static int sta_info_alloc_link(struct ieee80211_local *local,
|
|||
link_info->rx_omi_bw_tx = IEEE80211_STA_RX_BW_MAX;
|
||||
link_info->rx_omi_bw_staging = IEEE80211_STA_RX_BW_MAX;
|
||||
|
||||
link_info->op_mode_bw = IEEE80211_STA_RX_BW_MAX;
|
||||
|
||||
/*
|
||||
* Cause (a) warning(s) if IEEE80211_STA_RX_BW_MAX != 320
|
||||
* or if new values are added to the enum.
|
||||
*/
|
||||
switch (link_info->cur_max_bandwidth) {
|
||||
switch (link_info->op_mode_bw) {
|
||||
case IEEE80211_STA_RX_BW_20:
|
||||
case IEEE80211_STA_RX_BW_40:
|
||||
case IEEE80211_STA_RX_BW_80:
|
||||
|
|
@ -3424,6 +3426,104 @@ void ieee80211_sta_remove_link(struct sta_info *sta, unsigned int link_id)
|
|||
sta_remove_link(sta, link_id, true);
|
||||
}
|
||||
|
||||
static u8 ieee80211_sta_nss_capability(struct link_sta_info *link_sta)
|
||||
{
|
||||
u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, eht_rx_nss = 0, rx_nss;
|
||||
bool support_160;
|
||||
|
||||
if (link_sta->pub->eht_cap.has_eht) {
|
||||
int i;
|
||||
const u8 *rx_nss_mcs = (void *)&link_sta->pub->eht_cap.eht_mcs_nss_supp;
|
||||
|
||||
/* get the max nss for EHT over all possible bandwidths and mcs */
|
||||
for (i = 0; i < sizeof(struct ieee80211_eht_mcs_nss_supp); i++)
|
||||
eht_rx_nss = max_t(u8, eht_rx_nss,
|
||||
u8_get_bits(rx_nss_mcs[i],
|
||||
IEEE80211_EHT_MCS_NSS_RX));
|
||||
}
|
||||
|
||||
if (link_sta->pub->he_cap.has_he) {
|
||||
int i;
|
||||
u8 rx_mcs_80 = 0, rx_mcs_160 = 0;
|
||||
const struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
|
||||
u16 mcs_160_map =
|
||||
le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
|
||||
u16 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
|
||||
|
||||
for (i = 7; i >= 0; i--) {
|
||||
u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3;
|
||||
|
||||
if (mcs_160 != IEEE80211_HE_MCS_NOT_SUPPORTED) {
|
||||
rx_mcs_160 = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 7; i >= 0; i--) {
|
||||
u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3;
|
||||
|
||||
if (mcs_80 != IEEE80211_HE_MCS_NOT_SUPPORTED) {
|
||||
rx_mcs_80 = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
support_160 = he_cap->he_cap_elem.phy_cap_info[0] &
|
||||
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
|
||||
|
||||
if (support_160)
|
||||
he_rx_nss = min(rx_mcs_80, rx_mcs_160);
|
||||
else
|
||||
he_rx_nss = rx_mcs_80;
|
||||
}
|
||||
|
||||
if (link_sta->pub->ht_cap.ht_supported) {
|
||||
if (link_sta->pub->ht_cap.mcs.rx_mask[0])
|
||||
ht_rx_nss++;
|
||||
if (link_sta->pub->ht_cap.mcs.rx_mask[1])
|
||||
ht_rx_nss++;
|
||||
if (link_sta->pub->ht_cap.mcs.rx_mask[2])
|
||||
ht_rx_nss++;
|
||||
if (link_sta->pub->ht_cap.mcs.rx_mask[3])
|
||||
ht_rx_nss++;
|
||||
/* FIXME: consider rx_highest? */
|
||||
}
|
||||
|
||||
if (link_sta->pub->vht_cap.vht_supported) {
|
||||
int i;
|
||||
u16 rx_mcs_map;
|
||||
|
||||
rx_mcs_map = le16_to_cpu(link_sta->pub->vht_cap.vht_mcs.rx_mcs_map);
|
||||
|
||||
for (i = 7; i >= 0; i--) {
|
||||
u8 mcs = (rx_mcs_map >> (2 * i)) & 3;
|
||||
|
||||
if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
|
||||
vht_rx_nss = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* FIXME: consider rx_highest? */
|
||||
}
|
||||
|
||||
rx_nss = max(vht_rx_nss, ht_rx_nss);
|
||||
rx_nss = max(he_rx_nss, rx_nss);
|
||||
rx_nss = max(eht_rx_nss, rx_nss);
|
||||
rx_nss = max_t(u8, 1, rx_nss);
|
||||
|
||||
return rx_nss;
|
||||
}
|
||||
|
||||
void ieee80211_sta_init_nss_bw_capa(struct link_sta_info *link_sta,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
link_sta->capa_nss = ieee80211_sta_nss_capability(link_sta);
|
||||
link_sta->pub->rx_nss = link_sta->capa_nss;
|
||||
|
||||
link_sta->pub->bandwidth =
|
||||
ieee80211_sta_current_bw(link_sta, chandef,
|
||||
IEEE80211_STA_BW_TX_TO_STA);
|
||||
}
|
||||
|
||||
void ieee80211_sta_set_max_amsdu_subframes(struct sta_info *sta,
|
||||
const u8 *ext_capab,
|
||||
unsigned int ext_capab_len)
|
||||
|
|
@ -3456,3 +3556,195 @@ bool lockdep_sta_mutex_held(struct ieee80211_sta *pubsta)
|
|||
}
|
||||
EXPORT_SYMBOL(lockdep_sta_mutex_held);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* ieee80211_sta_bw_capability - get STA's bandwidth capability
|
||||
* @link_sta: the (link) STA to get the capability for
|
||||
* @band: the band to get the capability on
|
||||
*
|
||||
* Return: the maximum bandwidth supported by the STA
|
||||
*/
|
||||
static enum ieee80211_sta_rx_bandwidth
|
||||
ieee80211_sta_bw_capability(struct link_sta_info *link_sta,
|
||||
enum nl80211_band band)
|
||||
{
|
||||
struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap;
|
||||
struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
|
||||
struct ieee80211_sta_eht_cap *eht_cap = &link_sta->pub->eht_cap;
|
||||
u32 cap_width;
|
||||
|
||||
if (he_cap->has_he) {
|
||||
u8 info;
|
||||
|
||||
if (eht_cap->has_eht && band == NL80211_BAND_6GHZ) {
|
||||
info = eht_cap->eht_cap_elem.phy_cap_info[0];
|
||||
|
||||
if (info & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ)
|
||||
return IEEE80211_STA_RX_BW_320;
|
||||
}
|
||||
|
||||
info = he_cap->he_cap_elem.phy_cap_info[0];
|
||||
|
||||
if (band == NL80211_BAND_2GHZ) {
|
||||
if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G)
|
||||
return IEEE80211_STA_RX_BW_40;
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
}
|
||||
|
||||
if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G ||
|
||||
info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
|
||||
return IEEE80211_STA_RX_BW_160;
|
||||
|
||||
if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)
|
||||
return IEEE80211_STA_RX_BW_80;
|
||||
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
}
|
||||
|
||||
if (!vht_cap->vht_supported)
|
||||
return link_sta->pub->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
IEEE80211_STA_RX_BW_40 :
|
||||
IEEE80211_STA_RX_BW_20;
|
||||
|
||||
cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
||||
|
||||
if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ||
|
||||
cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
|
||||
return IEEE80211_STA_RX_BW_160;
|
||||
|
||||
/*
|
||||
* If this is non-zero, then it does support 160 MHz after all,
|
||||
* in one form or the other. We don't distinguish here (or even
|
||||
* above) between 160 and 80+80 yet.
|
||||
*/
|
||||
if (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)
|
||||
return IEEE80211_STA_RX_BW_160;
|
||||
|
||||
return IEEE80211_STA_RX_BW_80;
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_sta_usable_bw - get STA's usable bandwidth capability
|
||||
* @link_sta: the (link) STA to get the capability for
|
||||
* @band: the band to get the capability on
|
||||
*
|
||||
* If the STA is on an AP interface, take into account the AP's
|
||||
* bandwidth corresponding to this station's PHY capability
|
||||
*
|
||||
* Return: the maximum bandwidth supported by the STA on the
|
||||
* connection to the interface it's connected to
|
||||
*/
|
||||
static enum ieee80211_sta_rx_bandwidth
|
||||
ieee80211_sta_usable_bw(struct link_sta_info *link_sta,
|
||||
enum nl80211_band band)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = link_sta->sta->sdata;
|
||||
enum ieee80211_sta_rx_bandwidth bw;
|
||||
struct ieee80211_link_data *link;
|
||||
|
||||
bw = ieee80211_sta_bw_capability(link_sta, band);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
|
||||
sdata = get_bss_sdata(sdata);
|
||||
|
||||
/* for a STA to exist on VLAN, it must have AP */
|
||||
if (WARN_ON(!sdata))
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
}
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_AP)
|
||||
return bw;
|
||||
|
||||
/* for a link STA to exist, vif must have the link */
|
||||
link = sdata_dereference(sdata->link[link_sta->link_id], sdata);
|
||||
if (WARN_ON(!link))
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
|
||||
if (link_sta->pub->eht_cap.has_eht)
|
||||
return bw;
|
||||
|
||||
return min(bw, link->bss_bw.he_and_lower);
|
||||
}
|
||||
|
||||
static enum ieee80211_sta_rx_bandwidth
|
||||
ieee80211_sta_current_bw_rx_from_sta(struct link_sta_info *link_sta,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
/*
|
||||
* Take RX OMI into account. The value "rx_omi_bw_rx" is what
|
||||
* we've indicated to the STA we can currently receive.
|
||||
*
|
||||
* This is needed since the RX OMI is done by us to save power,
|
||||
* requiring changing both our TX (rate control) and RX (chanctx),
|
||||
* which in turn needs to be done in the right order (stop TX
|
||||
* at a higher bandwidth first while reducing bandwidth, and
|
||||
* change the chanctx only after the peer accepts, etc.)
|
||||
*/
|
||||
return min(ieee80211_sta_usable_bw(link_sta, chandef->chan->band),
|
||||
link_sta->rx_omi_bw_rx);
|
||||
}
|
||||
|
||||
static enum ieee80211_sta_rx_bandwidth
|
||||
ieee80211_sta_current_bw_tx_to_sta(struct link_sta_info *link_sta,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct sta_info *sta = link_sta->sta;
|
||||
enum nl80211_chan_width bss_width;
|
||||
enum ieee80211_sta_rx_bandwidth bw;
|
||||
enum nl80211_band band;
|
||||
|
||||
bss_width = chandef->width;
|
||||
band = chandef->chan->band;
|
||||
|
||||
bw = ieee80211_sta_usable_bw(link_sta, band);
|
||||
bw = min(bw, link_sta->op_mode_bw);
|
||||
/* also limit to RX OMI bandwidth we TX to the STA */
|
||||
bw = min(bw, link_sta->rx_omi_bw_tx);
|
||||
|
||||
/* Don't consider AP's bandwidth for TDLS peers, section 11.23.1 of
|
||||
* IEEE80211-2016 specification makes higher bandwidth operation
|
||||
* possible on the TDLS link if the peers have wider bandwidth
|
||||
* capability.
|
||||
*
|
||||
* However, in this case, and only if the TDLS peer is authorized,
|
||||
* limit to the tdls_chandef so that the configuration here isn't
|
||||
* wider than what's actually requested on the channel context.
|
||||
*/
|
||||
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
|
||||
test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) &&
|
||||
test_sta_flag(sta, WLAN_STA_AUTHORIZED) &&
|
||||
sta->tdls_chandef.chan)
|
||||
bw = min(bw, ieee80211_chan_width_to_rx_bw(sta->tdls_chandef.width));
|
||||
else
|
||||
bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width));
|
||||
|
||||
return bw;
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_sta_current_bw - get STA's current usable bandwidth
|
||||
* @link_sta: the (link) STA to get the bandwidth for
|
||||
* @chandef: the chandef for the channel the STA is on
|
||||
* @direction: the direction (to or from STA)
|
||||
*
|
||||
* Return: the maximum bandwidth that the station can/may
|
||||
* (currently) use in the given direction
|
||||
*/
|
||||
enum ieee80211_sta_rx_bandwidth
|
||||
ieee80211_sta_current_bw(struct link_sta_info *link_sta,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
enum ieee80211_sta_bw_direction direction)
|
||||
{
|
||||
if (WARN_ON(!chandef))
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
|
||||
switch (direction) {
|
||||
case IEEE80211_STA_BW_RX_FROM_STA:
|
||||
return ieee80211_sta_current_bw_rx_from_sta(link_sta, chandef);
|
||||
case IEEE80211_STA_BW_TX_TO_STA:
|
||||
return ieee80211_sta_current_bw_tx_to_sta(link_sta, chandef);
|
||||
}
|
||||
|
||||
/* unreachable */
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -504,7 +504,7 @@ struct ieee80211_fragment_cache {
|
|||
* @status_stats.last_ack_signal: last ACK signal
|
||||
* @status_stats.ack_signal_filled: last ACK signal validity
|
||||
* @status_stats.avg_ack_signal: average ACK signal
|
||||
* @cur_max_bandwidth: maximum bandwidth to use for TX to the station,
|
||||
* @op_mode_bw: dynamic bandwidth limit to transmit to the STA,
|
||||
* taken from HT/VHT capabilities or VHT operating mode notification.
|
||||
* Invalid for NAN since that is operating on multiple bands.
|
||||
* @rx_omi_bw_rx: RX OMI bandwidth restriction to apply for RX
|
||||
|
|
@ -558,7 +558,7 @@ struct link_sta_info {
|
|||
u64 msdu[IEEE80211_NUM_TIDS + 1];
|
||||
} tx_stats;
|
||||
|
||||
enum ieee80211_sta_rx_bandwidth cur_max_bandwidth;
|
||||
enum ieee80211_sta_rx_bandwidth op_mode_bw;
|
||||
enum ieee80211_sta_rx_bandwidth rx_omi_bw_rx,
|
||||
rx_omi_bw_tx,
|
||||
rx_omi_bw_staging;
|
||||
|
|
@ -997,12 +997,24 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);
|
|||
|
||||
unsigned long ieee80211_sta_last_active(struct sta_info *sta, int link_id);
|
||||
|
||||
void ieee80211_sta_init_nss_bw_capa(struct link_sta_info *link_sta,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
void ieee80211_sta_set_max_amsdu_subframes(struct sta_info *sta,
|
||||
const u8 *ext_capab,
|
||||
unsigned int ext_capab_len);
|
||||
|
||||
void __ieee80211_sta_recalc_aggregates(struct sta_info *sta, u16 active_links);
|
||||
|
||||
enum ieee80211_sta_bw_direction {
|
||||
IEEE80211_STA_BW_RX_FROM_STA,
|
||||
IEEE80211_STA_BW_TX_TO_STA,
|
||||
};
|
||||
|
||||
enum ieee80211_sta_rx_bandwidth
|
||||
ieee80211_sta_current_bw(struct link_sta_info *link_sta,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
enum ieee80211_sta_bw_direction direction);
|
||||
|
||||
enum sta_stats_type {
|
||||
STA_STATS_RATE_TYPE_INVALID = 0,
|
||||
STA_STATS_RATE_TYPE_LEGACY,
|
||||
|
|
|
|||
|
|
@ -311,17 +311,21 @@ ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata,
|
|||
/* IEEE802.11ac-2013 Table E-4 */
|
||||
static const u16 centers_80mhz[] = { 5210, 5290, 5530, 5610, 5690, 5775 };
|
||||
struct cfg80211_chan_def uc = sta->tdls_chandef;
|
||||
enum nl80211_chan_width max_width =
|
||||
ieee80211_sta_cap_chan_bw(&sta->deflink);
|
||||
enum nl80211_chan_width max_width;
|
||||
int i;
|
||||
|
||||
/* only support upgrading non-narrow channels up to 80Mhz */
|
||||
if (max_width == NL80211_CHAN_WIDTH_5 ||
|
||||
max_width == NL80211_CHAN_WIDTH_10)
|
||||
return;
|
||||
|
||||
if (max_width > NL80211_CHAN_WIDTH_80)
|
||||
switch (ieee80211_sta_current_bw(&sta->deflink, &uc,
|
||||
IEEE80211_STA_BW_RX_FROM_STA)) {
|
||||
case IEEE80211_STA_RX_BW_20:
|
||||
max_width = NL80211_CHAN_WIDTH_20;
|
||||
break;
|
||||
case IEEE80211_STA_RX_BW_40:
|
||||
max_width = NL80211_CHAN_WIDTH_40;
|
||||
break;
|
||||
default: /* 80 or higher, only support upgrade to 80 */
|
||||
max_width = NL80211_CHAN_WIDTH_80;
|
||||
break;
|
||||
}
|
||||
|
||||
if (uc.width >= max_width)
|
||||
return;
|
||||
|
|
@ -1334,7 +1338,9 @@ static void iee80211_tdls_recalc_chanctx(struct ieee80211_sub_if_data *sdata,
|
|||
enum ieee80211_sta_rx_bandwidth bw;
|
||||
|
||||
bw = ieee80211_chan_width_to_rx_bw(conf->def.width);
|
||||
bw = min(bw, ieee80211_sta_cap_rx_bw(&sta->deflink));
|
||||
bw = min(bw, ieee80211_sta_current_bw(&sta->deflink,
|
||||
&conf->def,
|
||||
IEEE80211_STA_BW_RX_FROM_STA));
|
||||
if (bw != sta->sta.deflink.bandwidth) {
|
||||
sta->sta.deflink.bandwidth = bw;
|
||||
rate_control_rate_update(local, sband,
|
||||
|
|
|
|||
|
|
@ -3916,7 +3916,7 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
|
|||
* injected frames or EAPOL frames from the local station.
|
||||
*/
|
||||
if (unlikely(!(info->flags & IEEE80211_TX_CTL_INJECTED) &&
|
||||
ieee80211_is_data(hdr->frame_control) &&
|
||||
ieee80211_is_data_present(hdr->frame_control) &&
|
||||
!ieee80211_vif_is_mesh(&tx.sdata->vif) &&
|
||||
tx.sdata->vif.type != NL80211_IFTYPE_OCB &&
|
||||
!is_multicast_ether_addr(hdr->addr1) &&
|
||||
|
|
|
|||
|
|
@ -3149,7 +3149,9 @@ bool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info,
|
|||
ext_nss_bw_supp = 0;
|
||||
|
||||
/*
|
||||
* Cf. IEEE 802.11 Table 9-250
|
||||
* Cf. IEEE 802.11-2020 Table 9-272 - Setting of the Supported Channel
|
||||
* Width Set subfield and Extended NSS BW Support subfield at a STA
|
||||
* transmitting the VHT Capabilities Information field
|
||||
*
|
||||
* We really just consider that because it's inefficient to connect
|
||||
* at a higher bandwidth than we'll actually be able to use.
|
||||
|
|
@ -3840,6 +3842,25 @@ void ieee80211_chandef_downgrade(struct cfg80211_chan_def *c,
|
|||
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
|
||||
}
|
||||
|
||||
enum nl80211_chan_width
|
||||
ieee80211_sta_rx_bw_to_chan_width(enum ieee80211_sta_rx_bandwidth bw)
|
||||
{
|
||||
switch (bw) {
|
||||
case IEEE80211_STA_RX_BW_20:
|
||||
return NL80211_CHAN_WIDTH_20;
|
||||
case IEEE80211_STA_RX_BW_40:
|
||||
return NL80211_CHAN_WIDTH_40;
|
||||
case IEEE80211_STA_RX_BW_80:
|
||||
return NL80211_CHAN_WIDTH_80;
|
||||
case IEEE80211_STA_RX_BW_160:
|
||||
return NL80211_CHAN_WIDTH_160;
|
||||
case IEEE80211_STA_RX_BW_320:
|
||||
return NL80211_CHAN_WIDTH_320;
|
||||
default:
|
||||
return NL80211_CHAN_WIDTH_20;
|
||||
}
|
||||
}
|
||||
|
||||
int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_csa_settings *csa_settings)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -301,32 +301,6 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
|||
return;
|
||||
}
|
||||
|
||||
/* finally set up the bandwidth */
|
||||
switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
|
||||
case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
|
||||
case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
|
||||
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
|
||||
break;
|
||||
default:
|
||||
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
|
||||
|
||||
if (!(vht_cap->vht_mcs.tx_highest &
|
||||
cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE)))
|
||||
break;
|
||||
|
||||
/*
|
||||
* If this is non-zero, then it does support 160 MHz after all,
|
||||
* in one form or the other. We don't distinguish here (or even
|
||||
* above) between 160 and 80+80 yet.
|
||||
*/
|
||||
if (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)
|
||||
link_sta->cur_max_bandwidth =
|
||||
IEEE80211_STA_RX_BW_160;
|
||||
}
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_NAN)
|
||||
link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta);
|
||||
|
||||
/*
|
||||
* Work around the Cisco 9115 FW 17.3 bug by taking the min of
|
||||
* both reported MPDU lengths.
|
||||
|
|
@ -357,319 +331,20 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
|||
ieee80211_sta_recalc_aggregates(&link_sta->sta->sta);
|
||||
}
|
||||
|
||||
/* FIXME: move this to some better location - parses HE/EHT now */
|
||||
static enum ieee80211_sta_rx_bandwidth
|
||||
__ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
unsigned int link_id = link_sta->link_id;
|
||||
struct ieee80211_sub_if_data *sdata = link_sta->sta->sdata;
|
||||
struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap;
|
||||
struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
|
||||
struct ieee80211_sta_eht_cap *eht_cap = &link_sta->pub->eht_cap;
|
||||
u32 cap_width;
|
||||
|
||||
if (he_cap->has_he) {
|
||||
enum nl80211_band band;
|
||||
u8 info;
|
||||
|
||||
if (chandef) {
|
||||
band = chandef->chan->band;
|
||||
} else {
|
||||
struct ieee80211_bss_conf *link_conf;
|
||||
|
||||
if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_NAN_DATA ||
|
||||
sdata->vif.type == NL80211_IFTYPE_NAN))
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
|
||||
rcu_read_lock();
|
||||
link_conf = rcu_dereference(sdata->vif.link_conf[link_id]);
|
||||
band = link_conf->chanreq.oper.chan->band;
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
if (eht_cap->has_eht && band == NL80211_BAND_6GHZ) {
|
||||
info = eht_cap->eht_cap_elem.phy_cap_info[0];
|
||||
|
||||
if (info & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ)
|
||||
return IEEE80211_STA_RX_BW_320;
|
||||
}
|
||||
|
||||
info = he_cap->he_cap_elem.phy_cap_info[0];
|
||||
|
||||
if (band == NL80211_BAND_2GHZ) {
|
||||
if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G)
|
||||
return IEEE80211_STA_RX_BW_40;
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
}
|
||||
|
||||
if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G ||
|
||||
info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
|
||||
return IEEE80211_STA_RX_BW_160;
|
||||
|
||||
if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)
|
||||
return IEEE80211_STA_RX_BW_80;
|
||||
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
}
|
||||
|
||||
if (!vht_cap->vht_supported)
|
||||
return link_sta->pub->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
IEEE80211_STA_RX_BW_40 :
|
||||
IEEE80211_STA_RX_BW_20;
|
||||
|
||||
cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
||||
|
||||
if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ||
|
||||
cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
|
||||
return IEEE80211_STA_RX_BW_160;
|
||||
|
||||
/*
|
||||
* If this is non-zero, then it does support 160 MHz after all,
|
||||
* in one form or the other. We don't distinguish here (or even
|
||||
* above) between 160 and 80+80 yet.
|
||||
*/
|
||||
if (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)
|
||||
return IEEE80211_STA_RX_BW_160;
|
||||
|
||||
return IEEE80211_STA_RX_BW_80;
|
||||
}
|
||||
|
||||
enum ieee80211_sta_rx_bandwidth
|
||||
_ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
/*
|
||||
* With RX OMI, also pretend that the STA's capability changed.
|
||||
* Of course this isn't really true, it didn't change, only our
|
||||
* RX capability was changed by notifying RX OMI to the STA.
|
||||
* The purpose, however, is to save power, and that requires
|
||||
* changing also transmissions to the AP and the chanctx. The
|
||||
* transmissions depend on link_sta->bandwidth which is set in
|
||||
* _ieee80211_sta_cur_vht_bw() below, but the chanctx depends
|
||||
* on the result of this function which is also called by
|
||||
* _ieee80211_sta_cur_vht_bw(), so we need to do that here as
|
||||
* well. This is sufficient for the steady state, but during
|
||||
* the transition we already need to change TX/RX separately,
|
||||
* so _ieee80211_sta_cur_vht_bw() below applies the _tx one.
|
||||
*/
|
||||
return min(__ieee80211_sta_cap_rx_bw(link_sta, chandef),
|
||||
link_sta->rx_omi_bw_rx);
|
||||
}
|
||||
|
||||
enum nl80211_chan_width
|
||||
ieee80211_sta_cap_chan_bw(struct link_sta_info *link_sta)
|
||||
{
|
||||
struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap;
|
||||
u32 cap_width;
|
||||
|
||||
if (!vht_cap->vht_supported) {
|
||||
if (!link_sta->pub->ht_cap.ht_supported)
|
||||
return NL80211_CHAN_WIDTH_20_NOHT;
|
||||
|
||||
return link_sta->pub->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
NL80211_CHAN_WIDTH_40 : NL80211_CHAN_WIDTH_20;
|
||||
}
|
||||
|
||||
cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
||||
|
||||
if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)
|
||||
return NL80211_CHAN_WIDTH_160;
|
||||
else if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
|
||||
return NL80211_CHAN_WIDTH_80P80;
|
||||
|
||||
return NL80211_CHAN_WIDTH_80;
|
||||
}
|
||||
|
||||
enum nl80211_chan_width
|
||||
ieee80211_sta_rx_bw_to_chan_width(struct link_sta_info *link_sta)
|
||||
{
|
||||
enum ieee80211_sta_rx_bandwidth cur_bw =
|
||||
link_sta->pub->bandwidth;
|
||||
struct ieee80211_sta_vht_cap *vht_cap =
|
||||
&link_sta->pub->vht_cap;
|
||||
u32 cap_width;
|
||||
|
||||
switch (cur_bw) {
|
||||
case IEEE80211_STA_RX_BW_20:
|
||||
if (!link_sta->pub->ht_cap.ht_supported)
|
||||
return NL80211_CHAN_WIDTH_20_NOHT;
|
||||
else
|
||||
return NL80211_CHAN_WIDTH_20;
|
||||
case IEEE80211_STA_RX_BW_40:
|
||||
return NL80211_CHAN_WIDTH_40;
|
||||
case IEEE80211_STA_RX_BW_80:
|
||||
return NL80211_CHAN_WIDTH_80;
|
||||
case IEEE80211_STA_RX_BW_160:
|
||||
cap_width =
|
||||
vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
||||
|
||||
if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)
|
||||
return NL80211_CHAN_WIDTH_160;
|
||||
|
||||
return NL80211_CHAN_WIDTH_80P80;
|
||||
default:
|
||||
return NL80211_CHAN_WIDTH_20;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: rename/move - this deals with everything not just VHT */
|
||||
enum ieee80211_sta_rx_bandwidth
|
||||
_ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct sta_info *sta = link_sta->sta;
|
||||
enum nl80211_chan_width bss_width;
|
||||
enum ieee80211_sta_rx_bandwidth bw;
|
||||
|
||||
if (chandef) {
|
||||
bss_width = chandef->width;
|
||||
} else {
|
||||
struct ieee80211_bss_conf *link_conf;
|
||||
|
||||
/* NAN operates on multiple channels so a chandef must be given */
|
||||
if (WARN_ON_ONCE(sta->sdata->vif.type == NL80211_IFTYPE_NAN ||
|
||||
sta->sdata->vif.type == NL80211_IFTYPE_NAN_DATA))
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
|
||||
rcu_read_lock();
|
||||
link_conf = rcu_dereference(sta->sdata->vif.link_conf[link_sta->link_id]);
|
||||
if (WARN_ON_ONCE(!link_conf)) {
|
||||
rcu_read_unlock();
|
||||
return IEEE80211_STA_RX_BW_20;
|
||||
}
|
||||
bss_width = link_conf->chanreq.oper.width;
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/* intentionally do not take rx_bw_omi_rx into account */
|
||||
bw = __ieee80211_sta_cap_rx_bw(link_sta, chandef);
|
||||
bw = min(bw, link_sta->cur_max_bandwidth);
|
||||
/* but do apply rx_omi_bw_tx */
|
||||
bw = min(bw, link_sta->rx_omi_bw_tx);
|
||||
|
||||
/* Don't consider AP's bandwidth for TDLS peers, section 11.23.1 of
|
||||
* IEEE80211-2016 specification makes higher bandwidth operation
|
||||
* possible on the TDLS link if the peers have wider bandwidth
|
||||
* capability.
|
||||
*
|
||||
* However, in this case, and only if the TDLS peer is authorized,
|
||||
* limit to the tdls_chandef so that the configuration here isn't
|
||||
* wider than what's actually requested on the channel context.
|
||||
*/
|
||||
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
|
||||
test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) &&
|
||||
test_sta_flag(sta, WLAN_STA_AUTHORIZED) &&
|
||||
sta->tdls_chandef.chan)
|
||||
bw = min(bw, ieee80211_chan_width_to_rx_bw(sta->tdls_chandef.width));
|
||||
else
|
||||
bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width));
|
||||
|
||||
return bw;
|
||||
}
|
||||
|
||||
void ieee80211_sta_init_nss(struct link_sta_info *link_sta)
|
||||
{
|
||||
u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, eht_rx_nss = 0, rx_nss;
|
||||
bool support_160;
|
||||
|
||||
if (link_sta->pub->eht_cap.has_eht) {
|
||||
int i;
|
||||
const u8 *rx_nss_mcs = (void *)&link_sta->pub->eht_cap.eht_mcs_nss_supp;
|
||||
|
||||
/* get the max nss for EHT over all possible bandwidths and mcs */
|
||||
for (i = 0; i < sizeof(struct ieee80211_eht_mcs_nss_supp); i++)
|
||||
eht_rx_nss = max_t(u8, eht_rx_nss,
|
||||
u8_get_bits(rx_nss_mcs[i],
|
||||
IEEE80211_EHT_MCS_NSS_RX));
|
||||
}
|
||||
|
||||
if (link_sta->pub->he_cap.has_he) {
|
||||
int i;
|
||||
u8 rx_mcs_80 = 0, rx_mcs_160 = 0;
|
||||
const struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
|
||||
u16 mcs_160_map =
|
||||
le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
|
||||
u16 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
|
||||
|
||||
for (i = 7; i >= 0; i--) {
|
||||
u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3;
|
||||
|
||||
if (mcs_160 != IEEE80211_HE_MCS_NOT_SUPPORTED) {
|
||||
rx_mcs_160 = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 7; i >= 0; i--) {
|
||||
u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3;
|
||||
|
||||
if (mcs_80 != IEEE80211_HE_MCS_NOT_SUPPORTED) {
|
||||
rx_mcs_80 = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
support_160 = he_cap->he_cap_elem.phy_cap_info[0] &
|
||||
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
|
||||
|
||||
if (support_160)
|
||||
he_rx_nss = min(rx_mcs_80, rx_mcs_160);
|
||||
else
|
||||
he_rx_nss = rx_mcs_80;
|
||||
}
|
||||
|
||||
if (link_sta->pub->ht_cap.ht_supported) {
|
||||
if (link_sta->pub->ht_cap.mcs.rx_mask[0])
|
||||
ht_rx_nss++;
|
||||
if (link_sta->pub->ht_cap.mcs.rx_mask[1])
|
||||
ht_rx_nss++;
|
||||
if (link_sta->pub->ht_cap.mcs.rx_mask[2])
|
||||
ht_rx_nss++;
|
||||
if (link_sta->pub->ht_cap.mcs.rx_mask[3])
|
||||
ht_rx_nss++;
|
||||
/* FIXME: consider rx_highest? */
|
||||
}
|
||||
|
||||
if (link_sta->pub->vht_cap.vht_supported) {
|
||||
int i;
|
||||
u16 rx_mcs_map;
|
||||
|
||||
rx_mcs_map = le16_to_cpu(link_sta->pub->vht_cap.vht_mcs.rx_mcs_map);
|
||||
|
||||
for (i = 7; i >= 0; i--) {
|
||||
u8 mcs = (rx_mcs_map >> (2 * i)) & 3;
|
||||
|
||||
if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
|
||||
vht_rx_nss = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* FIXME: consider rx_highest? */
|
||||
}
|
||||
|
||||
rx_nss = max(vht_rx_nss, ht_rx_nss);
|
||||
rx_nss = max(he_rx_nss, rx_nss);
|
||||
rx_nss = max(eht_rx_nss, rx_nss);
|
||||
rx_nss = max_t(u8, 1, rx_nss);
|
||||
link_sta->capa_nss = rx_nss;
|
||||
|
||||
/* that shouldn't be set yet, but we can handle it anyway */
|
||||
if (link_sta->op_mode_nss)
|
||||
link_sta->pub->rx_nss =
|
||||
min_t(u8, rx_nss, link_sta->op_mode_nss);
|
||||
else
|
||||
link_sta->pub->rx_nss = rx_nss;
|
||||
}
|
||||
|
||||
u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
|
||||
struct link_sta_info *link_sta,
|
||||
u8 opmode, enum nl80211_band band)
|
||||
{
|
||||
enum ieee80211_sta_rx_bandwidth new_bw;
|
||||
struct sta_opmode_info sta_opmode = {};
|
||||
struct ieee80211_link_data *link;
|
||||
u32 changed = 0;
|
||||
u8 nss;
|
||||
|
||||
link = sdata_dereference(sdata->link[link_sta->link_id], sdata);
|
||||
if (WARN_ON(!link))
|
||||
return 0;
|
||||
|
||||
/* ignore - no support for BF yet */
|
||||
if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)
|
||||
return 0;
|
||||
|
|
@ -698,28 +373,29 @@ u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
|
|||
switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) {
|
||||
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ:
|
||||
/* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */
|
||||
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20;
|
||||
link_sta->op_mode_bw = IEEE80211_STA_RX_BW_20;
|
||||
break;
|
||||
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ:
|
||||
/* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */
|
||||
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40;
|
||||
link_sta->op_mode_bw = IEEE80211_STA_RX_BW_40;
|
||||
break;
|
||||
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ:
|
||||
if (opmode & IEEE80211_OPMODE_NOTIF_BW_160_80P80)
|
||||
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
|
||||
link_sta->op_mode_bw = IEEE80211_STA_RX_BW_160;
|
||||
else
|
||||
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
|
||||
link_sta->op_mode_bw = IEEE80211_STA_RX_BW_80;
|
||||
break;
|
||||
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ:
|
||||
/* legacy only, no longer used by newer spec */
|
||||
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
|
||||
link_sta->op_mode_bw = IEEE80211_STA_RX_BW_160;
|
||||
break;
|
||||
}
|
||||
|
||||
new_bw = ieee80211_sta_cur_vht_bw(link_sta);
|
||||
new_bw = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper,
|
||||
IEEE80211_STA_BW_TX_TO_STA);
|
||||
if (new_bw != link_sta->pub->bandwidth) {
|
||||
link_sta->pub->bandwidth = new_bw;
|
||||
sta_opmode.bw = ieee80211_sta_rx_bw_to_chan_width(link_sta);
|
||||
sta_opmode.bw = ieee80211_sta_rx_bw_to_chan_width(new_bw);
|
||||
changed |= IEEE80211_RC_BW_CHANGED;
|
||||
sta_opmode.changed |= STA_OPMODE_MAX_BW_CHANGED;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -804,6 +804,24 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool wiphy_cipher_suites_valid(const struct wiphy *wiphy)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if (wiphy->n_cipher_suites && !wiphy->cipher_suites)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < wiphy->n_cipher_suites; i++) {
|
||||
for (j = 0; j < i; j++) {
|
||||
if (wiphy->cipher_suites[i] ==
|
||||
wiphy->cipher_suites[j])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int wiphy_register(struct wiphy *wiphy)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
|
|
@ -940,6 +958,9 @@ int wiphy_register(struct wiphy *wiphy)
|
|||
if (res)
|
||||
return res;
|
||||
|
||||
if (!wiphy_cipher_suites_valid(wiphy))
|
||||
return -EINVAL;
|
||||
|
||||
/* sanity check supported bands/channels */
|
||||
for (band = 0; band < NUM_NL80211_BANDS; band++) {
|
||||
const struct ieee80211_sband_iftype_data *iftd;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Wireless configuration interface internals.
|
||||
*
|
||||
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __NET_WIRELESS_CORE_H
|
||||
#define __NET_WIRELESS_CORE_H
|
||||
|
|
@ -446,6 +446,7 @@ bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher);
|
|||
bool cfg80211_valid_key_idx(struct cfg80211_registered_device *rdev,
|
||||
int key_idx, bool pairwise);
|
||||
int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
struct key_params *params, int key_idx,
|
||||
bool pairwise, const u8 *mac_addr);
|
||||
void __cfg80211_scan_done(struct wiphy *wiphy, struct wiphy_work *wk);
|
||||
|
|
|
|||
|
|
@ -1733,6 +1733,7 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
|
|||
|
||||
static struct cfg80211_cached_keys *
|
||||
nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
struct genl_info *info, bool *no_ht)
|
||||
{
|
||||
struct nlattr *keys = info->attrs[NL80211_ATTR_KEYS];
|
||||
|
|
@ -1782,7 +1783,7 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
|
|||
goto error;
|
||||
} else if (parse.defmgmt)
|
||||
goto error;
|
||||
err = cfg80211_validate_key_settings(rdev, &parse.p,
|
||||
err = cfg80211_validate_key_settings(rdev, wdev, &parse.p,
|
||||
parse.idx, false, NULL);
|
||||
if (err)
|
||||
goto error;
|
||||
|
|
@ -5100,7 +5101,7 @@ static int nl80211_validate_key_link_id(struct genl_info *info,
|
|||
if (wdev->valid_links) {
|
||||
if (link_id == -1) {
|
||||
GENL_SET_ERR_MSG(info,
|
||||
"link ID must for MLO group key");
|
||||
"link ID must be set for MLO group key");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!(wdev->valid_links & BIT(link_id))) {
|
||||
|
|
@ -5407,7 +5408,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
|
|||
if (!rdev->ops->add_key)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
|
||||
if (cfg80211_validate_key_settings(rdev, wdev, &key.p, key.idx,
|
||||
key.type == NL80211_KEYTYPE_PAIRWISE,
|
||||
mac_addr)) {
|
||||
GENL_SET_ERR_MSG(info, "key setting validation failed");
|
||||
|
|
@ -6394,9 +6395,104 @@ static int nl80211_parse_he_bss_color(struct nlattr *attrs,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void nl80211_check_ap_rate_selectors(struct cfg80211_beacon_data *bcn,
|
||||
const struct element *rates)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!rates)
|
||||
return;
|
||||
|
||||
for (i = 0; i < rates->datalen; i++) {
|
||||
if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
|
||||
bcn->ht_required = true;
|
||||
if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_VHT_PHY)
|
||||
bcn->vht_required = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Since the nl80211 API didn't include, from the beginning, attributes about
|
||||
* HT/VHT/... operation, we parse them out of the elements and check for
|
||||
* validity for use by drivers/mac80211.
|
||||
*/
|
||||
static int nl80211_calculate_ap_operation(struct nlattr *attrs[],
|
||||
struct cfg80211_beacon_data *bcn,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
size_t ies_len = bcn->tail_len;
|
||||
const u8 *ies = bcn->tail;
|
||||
const struct element *rates;
|
||||
const struct element *op;
|
||||
|
||||
rates = cfg80211_find_elem(WLAN_EID_SUPP_RATES, ies, ies_len);
|
||||
nl80211_check_ap_rate_selectors(bcn, rates);
|
||||
|
||||
rates = cfg80211_find_elem(WLAN_EID_EXT_SUPP_RATES, ies, ies_len);
|
||||
nl80211_check_ap_rate_selectors(bcn, rates);
|
||||
|
||||
op = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ies, ies_len);
|
||||
if (op) {
|
||||
if (op->datalen < sizeof(*bcn->he_oper) + 1) {
|
||||
NL_SET_ERR_MSG(extack, "bad HE operation in beacon");
|
||||
return -EINVAL;
|
||||
}
|
||||
bcn->he_oper = (void *)(op->data + 1);
|
||||
/* takes extension ID into account */
|
||||
if (op->datalen < ieee80211_he_oper_size((void *)bcn->he_oper)) {
|
||||
NL_SET_ERR_MSG(extack, "bad HE operation in beacon");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
op = cfg80211_find_elem(WLAN_EID_HT_OPERATION, ies, ies_len);
|
||||
if (op) {
|
||||
if (op->datalen < sizeof(*bcn->ht_oper)) {
|
||||
NL_SET_ERR_MSG(extack, "bad HT operation in beacon");
|
||||
return -EINVAL;
|
||||
}
|
||||
bcn->ht_oper = (void *)op->data;
|
||||
}
|
||||
|
||||
op = cfg80211_find_elem(WLAN_EID_VHT_OPERATION, ies, ies_len);
|
||||
if (op) {
|
||||
if (op->datalen < sizeof(*bcn->vht_oper)) {
|
||||
NL_SET_ERR_MSG(extack, "bad VHT operation in beacon");
|
||||
return -EINVAL;
|
||||
}
|
||||
bcn->vht_oper = (void *)op->data;
|
||||
}
|
||||
|
||||
op = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, ies, ies_len);
|
||||
if (op) {
|
||||
if (!ieee80211_eht_oper_size_ok(op->data + 1,
|
||||
op->datalen - 1)) {
|
||||
NL_SET_ERR_MSG(extack, "bad EHT operation in beacon");
|
||||
return -EINVAL;
|
||||
}
|
||||
bcn->eht_oper = (void *)(op->data + 1);
|
||||
}
|
||||
|
||||
op = cfg80211_find_ext_elem(WLAN_EID_EXT_UHR_OPER, ies, ies_len);
|
||||
if (op) {
|
||||
/* need full UHR operation separately */
|
||||
if (!attrs[NL80211_ATTR_UHR_OPERATION]) {
|
||||
NL_SET_ERR_MSG(extack, "missing UHR operation");
|
||||
return -EINVAL;
|
||||
}
|
||||
bcn->uhr_oper = nla_data(attrs[NL80211_ATTR_UHR_OPERATION]);
|
||||
} else if (attrs[NL80211_ATTR_UHR_OPERATION]) {
|
||||
NL_SET_ERR_MSG(extack, "unexpected UHR operation");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
|
||||
struct nlattr *attrs[],
|
||||
struct cfg80211_beacon_data *bcn,
|
||||
struct ieee80211_channel *chan,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
bool haveinfo = false;
|
||||
|
|
@ -6511,6 +6607,19 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
|
|||
}
|
||||
}
|
||||
|
||||
err = nl80211_calculate_ap_operation(attrs, bcn, extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (bcn->he_oper && (chan->flags & IEEE80211_CHAN_NO_HE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (bcn->eht_oper && (chan->flags & IEEE80211_CHAN_NO_EHT))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (bcn->uhr_oper && (chan->flags & IEEE80211_CHAN_NO_UHR))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6628,75 +6737,54 @@ nl80211_parse_unsol_bcast_probe_resp(struct cfg80211_registered_device *rdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void nl80211_check_ap_rate_selectors(struct cfg80211_ap_settings *params,
|
||||
const struct element *rates)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!rates)
|
||||
return;
|
||||
|
||||
for (i = 0; i < rates->datalen; i++) {
|
||||
if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
|
||||
params->ht_required = true;
|
||||
if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_VHT_PHY)
|
||||
params->vht_required = true;
|
||||
if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HE_PHY)
|
||||
params->he_required = true;
|
||||
if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_SAE_H2E)
|
||||
params->sae_h2e_required = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Since the nl80211 API didn't include, from the beginning, attributes about
|
||||
* HT/VHT requirements/capabilities, we parse them out of the IEs for the
|
||||
* benefit of drivers that rebuild IEs in the firmware.
|
||||
* HT/VHT/... capabilities, we parse them out of the elements and check for
|
||||
* validity for use by drivers/mac80211.
|
||||
*/
|
||||
static int nl80211_calculate_ap_params(struct cfg80211_ap_settings *params)
|
||||
static int nl80211_calculate_ap_capabilities(struct genl_info *info,
|
||||
struct cfg80211_ap_settings *params)
|
||||
{
|
||||
const struct cfg80211_beacon_data *bcn = ¶ms->beacon;
|
||||
size_t ies_len = bcn->tail_len;
|
||||
const u8 *ies = bcn->tail;
|
||||
const struct element *rates;
|
||||
size_t ies_len = params->beacon.tail_len;
|
||||
const u8 *ies = params->beacon.tail;
|
||||
const struct element *cap;
|
||||
|
||||
rates = cfg80211_find_elem(WLAN_EID_SUPP_RATES, ies, ies_len);
|
||||
nl80211_check_ap_rate_selectors(params, rates);
|
||||
|
||||
rates = cfg80211_find_elem(WLAN_EID_EXT_SUPP_RATES, ies, ies_len);
|
||||
nl80211_check_ap_rate_selectors(params, rates);
|
||||
|
||||
cap = cfg80211_find_elem(WLAN_EID_HT_CAPABILITY, ies, ies_len);
|
||||
if (cap && cap->datalen >= sizeof(*params->ht_cap))
|
||||
if (cap) {
|
||||
if (cap->datalen < sizeof(*params->ht_cap)) {
|
||||
GENL_SET_ERR_MSG(info, "bad HT capability in beacon");
|
||||
return -EINVAL;
|
||||
}
|
||||
params->ht_cap = (void *)cap->data;
|
||||
}
|
||||
|
||||
cap = cfg80211_find_elem(WLAN_EID_VHT_CAPABILITY, ies, ies_len);
|
||||
if (cap && cap->datalen >= sizeof(*params->vht_cap))
|
||||
if (cap) {
|
||||
if (cap->datalen < sizeof(*params->vht_cap)) {
|
||||
GENL_SET_ERR_MSG(info, "bad VHT capability in beacon");
|
||||
return -EINVAL;
|
||||
}
|
||||
params->vht_cap = (void *)cap->data;
|
||||
}
|
||||
|
||||
cap = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY, ies, ies_len);
|
||||
if (cap && cap->datalen >= sizeof(*params->he_cap) + 1)
|
||||
if (cap) {
|
||||
if (cap->datalen < sizeof(*params->he_cap) + 1) {
|
||||
GENL_SET_ERR_MSG(info, "bad HE capability in beacon");
|
||||
return -EINVAL;
|
||||
}
|
||||
params->he_cap = (void *)(cap->data + 1);
|
||||
cap = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ies, ies_len);
|
||||
if (cap && cap->datalen >= sizeof(*params->he_oper) + 1)
|
||||
params->he_oper = (void *)(cap->data + 1);
|
||||
}
|
||||
|
||||
cap = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_CAPABILITY, ies, ies_len);
|
||||
if (cap) {
|
||||
if (!cap->datalen)
|
||||
return -EINVAL;
|
||||
params->eht_cap = (void *)(cap->data + 1);
|
||||
if (!ieee80211_eht_capa_size_ok((const u8 *)params->he_cap,
|
||||
(const u8 *)params->eht_cap,
|
||||
cap->datalen - 1, true))
|
||||
return -EINVAL;
|
||||
}
|
||||
cap = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, ies, ies_len);
|
||||
if (cap) {
|
||||
if (!cap->datalen)
|
||||
return -EINVAL;
|
||||
params->eht_oper = (void *)(cap->data + 1);
|
||||
if (!ieee80211_eht_oper_size_ok((const u8 *)params->eht_oper,
|
||||
cap->datalen - 1))
|
||||
cap->datalen - 1, true)) {
|
||||
GENL_SET_ERR_MSG(info, "bad EHT capability in beacon");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -6826,24 +6914,6 @@ static void nl80211_send_ap_started(struct wireless_dev *wdev,
|
|||
nlmsg_free(msg);
|
||||
}
|
||||
|
||||
static int nl80211_validate_ap_phy_operation(struct cfg80211_ap_settings *params)
|
||||
{
|
||||
struct ieee80211_channel *channel = params->chandef.chan;
|
||||
|
||||
if ((params->he_cap || params->he_oper) &&
|
||||
(channel->flags & IEEE80211_CHAN_NO_HE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if ((params->eht_cap || params->eht_oper) &&
|
||||
(channel->flags & IEEE80211_CHAN_NO_EHT))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (params->uhr_oper && (channel->flags & IEEE80211_CHAN_NO_UHR))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nl80211_parse_s1g_short_beacon(struct cfg80211_registered_device *rdev,
|
||||
struct nlattr *attrs,
|
||||
|
|
@ -6916,11 +6986,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
|||
if (!params)
|
||||
return -ENOMEM;
|
||||
|
||||
err = nl80211_parse_beacon(rdev, info->attrs, ¶ms->beacon,
|
||||
info->extack);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
params->beacon_interval =
|
||||
nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
|
||||
params->dtim_period =
|
||||
|
|
@ -7037,6 +7102,11 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
|||
goto out;
|
||||
}
|
||||
|
||||
err = nl80211_parse_beacon(rdev, info->attrs, ¶ms->beacon,
|
||||
params->chandef.chan, info->extack);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
beacon_check.iftype = wdev->iftype;
|
||||
beacon_check.relax = true;
|
||||
beacon_check.reg_power =
|
||||
|
|
@ -7136,14 +7206,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
|||
goto out;
|
||||
}
|
||||
|
||||
err = nl80211_calculate_ap_params(params);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (info->attrs[NL80211_ATTR_UHR_OPERATION])
|
||||
params->uhr_oper = nla_data(info->attrs[NL80211_ATTR_UHR_OPERATION]);
|
||||
|
||||
err = nl80211_validate_ap_phy_operation(params);
|
||||
err = nl80211_calculate_ap_capabilities(info, params);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
|
@ -7214,6 +7277,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
|
|||
return -ENOMEM;
|
||||
|
||||
err = nl80211_parse_beacon(rdev, info->attrs, ¶ms->beacon,
|
||||
wdev->links[link_id].ap.chandef.chan,
|
||||
info->extack);
|
||||
if (err)
|
||||
goto out;
|
||||
|
|
@ -11818,11 +11882,16 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
|||
|
||||
params.count = cs_count;
|
||||
|
||||
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
|
||||
¶ms.chandef);
|
||||
if (err)
|
||||
goto free;
|
||||
|
||||
if (!need_new_beacon)
|
||||
goto skip_beacons;
|
||||
|
||||
err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon_after,
|
||||
info->extack);
|
||||
params.chandef.chan, info->extack);
|
||||
if (err)
|
||||
goto free;
|
||||
|
||||
|
|
@ -11839,6 +11908,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
|||
goto free;
|
||||
|
||||
err = nl80211_parse_beacon(rdev, csa_attrs, ¶ms.beacon_csa,
|
||||
wdev->links[link_id].ap.chandef.chan,
|
||||
info->extack);
|
||||
if (err)
|
||||
goto free;
|
||||
|
|
@ -11867,11 +11937,6 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
|||
goto free;
|
||||
|
||||
skip_beacons:
|
||||
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
|
||||
¶ms.chandef);
|
||||
if (err)
|
||||
goto free;
|
||||
|
||||
if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, ¶ms.chandef,
|
||||
wdev->iftype)) {
|
||||
err = -EINVAL;
|
||||
|
|
@ -13163,7 +13228,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
|
|||
if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
|
||||
bool no_ht = false;
|
||||
|
||||
connkeys = nl80211_parse_connkeys(rdev, info, &no_ht);
|
||||
connkeys = nl80211_parse_connkeys(rdev, dev->ieee80211_ptr,
|
||||
info, &no_ht);
|
||||
if (IS_ERR(connkeys))
|
||||
return PTR_ERR(connkeys);
|
||||
|
||||
|
|
@ -13605,7 +13671,8 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
|
|||
}
|
||||
|
||||
if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
|
||||
connkeys = nl80211_parse_connkeys(rdev, info, NULL);
|
||||
connkeys = nl80211_parse_connkeys(rdev, dev->ieee80211_ptr,
|
||||
info, NULL);
|
||||
if (IS_ERR(connkeys))
|
||||
return PTR_ERR(connkeys);
|
||||
}
|
||||
|
|
@ -18324,7 +18391,12 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info)
|
|||
params.count = nla_get_u8(info->attrs[NL80211_ATTR_COLOR_CHANGE_COUNT]);
|
||||
params.color = nla_get_u8(info->attrs[NL80211_ATTR_COLOR_CHANGE_COLOR]);
|
||||
|
||||
params.link_id = nl80211_link_id(info->attrs);
|
||||
if (!wdev->links[params.link_id].ap.beacon_interval)
|
||||
return -EINVAL;
|
||||
|
||||
err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon_next,
|
||||
wdev->links[params.link_id].ap.chandef.chan,
|
||||
info->extack);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
@ -18340,6 +18412,7 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info)
|
|||
goto out;
|
||||
|
||||
err = nl80211_parse_beacon(rdev, tb, ¶ms.beacon_color_change,
|
||||
wdev->links[params.link_id].ap.chandef.chan,
|
||||
info->extack);
|
||||
if (err)
|
||||
goto out;
|
||||
|
|
@ -18397,7 +18470,6 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info)
|
|||
goto out;
|
||||
}
|
||||
|
||||
params.link_id = nl80211_link_id(info->attrs);
|
||||
err = rdev_color_change(rdev, dev, ¶ms);
|
||||
|
||||
out:
|
||||
|
|
|
|||
|
|
@ -284,6 +284,7 @@ bool cfg80211_valid_key_idx(struct cfg80211_registered_device *rdev,
|
|||
}
|
||||
|
||||
int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
struct key_params *params, int key_idx,
|
||||
bool pairwise, const u8 *mac_addr)
|
||||
{
|
||||
|
|
@ -344,6 +345,15 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
|
|||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Per Wi-Fi Aware v4.0 section 7.1.2, NAN Data interfaces
|
||||
* shall only use CCMP-128 or GCMP-256.
|
||||
*/
|
||||
if (wdev->iftype == NL80211_IFTYPE_NAN_DATA &&
|
||||
params->cipher != WLAN_CIPHER_SUITE_CCMP &&
|
||||
params->cipher != WLAN_CIPHER_SUITE_GCMP_256)
|
||||
return -EINVAL;
|
||||
|
||||
switch (params->cipher) {
|
||||
case WLAN_CIPHER_SUITE_WEP40:
|
||||
if (params->key_len != WLAN_KEY_LEN_WEP40)
|
||||
|
|
|
|||
|
|
@ -489,7 +489,8 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
|
|||
if (addr)
|
||||
tx_key = false;
|
||||
|
||||
if (cfg80211_validate_key_settings(rdev, params, idx, pairwise, addr))
|
||||
if (cfg80211_validate_key_settings(rdev, wdev, params, idx,
|
||||
pairwise, addr))
|
||||
return -EINVAL;
|
||||
|
||||
err = 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user