diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c index 2ad9f6ac6636..85bd994d15d4 100644 --- a/drivers/gpio/gpio-ath79.c +++ b/drivers/gpio/gpio-ath79.c @@ -11,6 +11,7 @@ #include #include #include +#include /* For WLAN GPIOs */ #include #include #include @@ -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 = { diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c index 2dcf0a52e4c1..30733a244454 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.c +++ b/drivers/net/wireless/ath/ath12k/ahb.c @@ -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; } diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 2519e2400d58..e8137144fd1f 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -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: diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.c b/drivers/net/wireless/ath/ath12k/dp_htt.c index 9c19d9707abf..52e10059c6d5 100644 --- a/drivers/net/wireless/ath/ath12k/dp_htt.c +++ b/drivers/net/wireless/ath/ath12k/dp_htt.c @@ -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) { diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.h b/drivers/net/wireless/ath/ath12k/dp_htt.h index 6020e632f74e..987689f11cda 100644 --- a/drivers/net/wireless/ath/ath12k/dp_htt.h +++ b/drivers/net/wireless/ath/ath12k/dp_htt.h @@ -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); diff --git a/drivers/net/wireless/ath/ath12k/htc.h b/drivers/net/wireless/ath/ath12k/htc.h index 7e3dccc7cc14..05b3e593542d 100644 --- a/drivers/net/wireless/ath/ath12k/htc.h +++ b/drivers/net/wireless/ath/ath12k/htc.h @@ -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 diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.h b/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.h index ac2a8ac03288..dd60626b3240 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_rx.h @@ -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; diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 65a05a9520ff..484fdd3b1b7f 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #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; + } +} diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 5ba9b7d3a888..4a34b2ca99ea 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -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 diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index a45351afcf6e..05c95e67a853 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #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); } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index eaa07d6dbde0..d9d2f64c5570 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -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; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c index 8cec5ad79fda..d65a68045ca8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_lcn.c @@ -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) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c index 52b2ae496811..1e85a9168d2b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c @@ -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); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index da6fd7471568..b48ebec18dd5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -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) | \ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 1ec9807e4827..214e6d10081b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -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) | \ diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 37cdf3e8a067..22bad3cba8df 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -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)); diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c index d9c4dd6beaec..77929ed9ef78 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/commands.c +++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c @@ -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; diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 17704f054727..ffa04315a62b 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -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) | diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h index a97b1d01f3ac..335e78bd4b5d 100644 --- a/include/linux/ieee80211-eht.h +++ b/include/linux/ieee80211-eht.h @@ -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)); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9d3639ff9c28..a40ab36b8edb 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -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; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 40cb20d9309c..02318a4be0e1 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -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) { diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 3d55bf4be36f..072b383d7d3c 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -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. diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7b77d57c9f96..00261bd6674b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -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; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index fda692316f08..248531051a4e 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -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); diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 5a1831b08677..a4d5461f6480 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -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; } diff --git a/net/mac80211/eht.c b/net/mac80211/eht.c index 768bfc4e737d..3fb469ef27b7 100644 --- a/net/mac80211/eht.c +++ b/net/mac80211/eht.c @@ -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); } } diff --git a/net/mac80211/he.c b/net/mac80211/he.c index a3e16a5bec22..b7d9e4cb6ba6 100644 --- a/net/mac80211/he.c +++ b/net/mac80211/he.c @@ -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; diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 97719298e038..6285ac15c16c 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -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, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 97292ff51475..08690342cfaa 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -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); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2a693406294b..e23e347f870d 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -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, diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 7cbab90c8784..37adb053213e 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -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; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 160ae65a5c64..00b4beff0e43 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -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; diff --git a/net/mac80211/ocb.c b/net/mac80211/ocb.c index ebb4f4d88c23..e9bf4ba3e60b 100644 --- a/net/mac80211/ocb.c +++ b/net/mac80211/ocb.c @@ -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 * 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); diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 31af7dd6aedc..ba1a3aa3f5d4 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -4,7 +4,7 @@ * Copyright 2005-2006, Devicescape Software, Inc. * Copyright (c) 2006 Jiri Benc * Copyright 2017 Intel Deutschland GmbH - * Copyright (C) 2019, 2022-2025 Intel Corporation + * Copyright (C) 2019, 2022-2026 Intel Corporation */ #include @@ -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; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 4c31ef8817ce..aba2fabfe0db 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -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; +} diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 3e5d003bd31f..39608a0abbb5 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -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, diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 44bba7c3e603..ffd575a8d188 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -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, diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index b487d2330f25..8e4876d1c544 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -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) && diff --git a/net/mac80211/util.c b/net/mac80211/util.c index b093bc203c81..89e82d34ae48 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -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) { diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index f3bb5a561a38..92a4e0450593 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -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; } diff --git a/net/wireless/core.c b/net/wireless/core.c index 6783e0672dcb..345a83fe428f 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -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; diff --git a/net/wireless/core.h b/net/wireless/core.h index ae2d56d3ad90..0e6f9bd4ba1b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -3,7 +3,7 @@ * Wireless configuration interface internals. * * Copyright 2006-2010 Johannes Berg - * 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); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f334cdef8958..cf236307cca9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -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: diff --git a/net/wireless/util.c b/net/wireless/util.c index cff5a1bd95cc..95ae06e94f10 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -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) diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 22d9d9bae8f5..28bc996c1437 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -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;