diff --git a/.mailmap b/.mailmap index 1f5540bc33f1..dd31d5aab2b3 100644 --- a/.mailmap +++ b/.mailmap @@ -10,6 +10,7 @@ # # Please keep this list dictionary sorted. # +Aaradhana Sahu Aaron Durbin Abel Vesa Abel Vesa @@ -130,7 +131,8 @@ Axel Dyks Axel Lin Balakrishna Godavarthi Banajit Goswami -Baochen Qiang +Baochen Qiang +Baochen Qiang Baolin Wang Baolin Wang Baolin Wang @@ -575,6 +577,7 @@ Maheshwar Ajja Maíra Canal Malathi Gottam Manikanta Pubbisetty +Manish Dharanenthiran Manivannan Sadhasivam Manivannan Sadhasivam Manoj Basapathi @@ -764,6 +767,8 @@ Rakesh Pillai Ralf Baechle Ralf Wildenhues Ram Chandra Jangir +Rameshkumar Sundaram +Rameshkumar Sundaram Randy Dunlap Randy Dunlap Randy Dunlap @@ -862,7 +867,8 @@ Sricharan Ramabadhran Srinivas Kandagatla Srinivas Kandagatla Srinivas Ramana -Sriram R +Sriram R +Sriram R Sriram Yagnaraman Stanislav Fomichev Stanislav Fomichev @@ -946,7 +952,8 @@ Vadim Fedorenko Valdis Kletnieks Vara Reddy Varadarajan Narayanan -Vasanthakumar Thiagarajan +Vasanthakumar Thiagarajan +Vasanthakumar Thiagarajan Vasily Averin Vasily Averin Vasily Averin diff --git a/Documentation/devicetree/bindings/net/wireless/marvell,sd8787.yaml b/Documentation/devicetree/bindings/net/wireless/marvell,sd8787.yaml index 930b700b73d0..9f905ef5c2c0 100644 --- a/Documentation/devicetree/bindings/net/wireless/marvell,sd8787.yaml +++ b/Documentation/devicetree/bindings/net/wireless/marvell,sd8787.yaml @@ -59,6 +59,12 @@ properties: description: Calibration data for sub-band 3 in the 5GHz band. maxItems: 502 + marvell,invalid-reg-hint-in-rom: + $ref: /schemas/types.yaml#/definitions/flag + description: + The regulatory hint in ROM conflicts with platform configuration. + It should be ignored, so the platform regulatory domain can be used. + marvell,wakeup-pin: $ref: /schemas/types.yaml#/definitions/uint32 description: diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath12k-wsi.yaml b/Documentation/devicetree/bindings/net/wireless/qcom,ath12k-wsi.yaml index 589960144fe1..fa64c8aa56f5 100644 --- a/Documentation/devicetree/bindings/net/wireless/qcom,ath12k-wsi.yaml +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath12k-wsi.yaml @@ -58,13 +58,6 @@ properties: String to uniquely identify variant of the calibration data for designs with colliding bus and device ids - qcom,ath12k-calibration-variant: - $ref: /schemas/types.yaml#/definitions/string - deprecated: true - description: - String to uniquely identify variant of the calibration data for designs - with colliding bus and device ids - qcom,wsi-controller: $ref: /schemas/types.yaml#/definitions/flag description: diff --git a/Documentation/devicetree/bindings/net/wireless/silabs,wfx.yaml b/Documentation/devicetree/bindings/net/wireless/silabs,wfx.yaml index 6c0888ae4c4e..60dfe257eadc 100644 --- a/Documentation/devicetree/bindings/net/wireless/silabs,wfx.yaml +++ b/Documentation/devicetree/bindings/net/wireless/silabs,wfx.yaml @@ -50,6 +50,9 @@ properties: used, this property is required. When SDIO is used, the "in-band" interrupt provided by the SDIO bus is used unless an interrupt is defined in the Device Tree. + + The hardware suffers from a limitation when this property is used with + SDIO. OOB IRQ with SDIO should only be used for debug. maxItems: 1 reset-gpios: diff --git a/Documentation/devicetree/bindings/net/wireless/ti,wl1251.txt b/Documentation/devicetree/bindings/net/wireless/ti,wl1251.txt deleted file mode 100644 index 88fd28d15eac..000000000000 --- a/Documentation/devicetree/bindings/net/wireless/ti,wl1251.txt +++ /dev/null @@ -1,64 +0,0 @@ -* Texas Instruments wl1251 wireless lan controller - -The wl1251 chip can be connected via SPI or via SDIO. This -document describes the binding for the SPI connected chip. - -Required properties: -- compatible : Should be "ti,wl1251" -- reg : Chip select address of device -- spi-max-frequency : Maximum SPI clocking speed of device in Hz -- interrupts : Should contain interrupt line -- vio-supply : phandle to regulator providing VIO - -Optional properties: -- ti,wl1251-has-eeprom : boolean, the wl1251 has an eeprom connected, which - provides configuration data (calibration, MAC, ...) -- ti,power-gpio : GPIO connected to chip's PMEN pin if operated in - SPI mode -- Please consult Documentation/devicetree/bindings/spi/spi-bus.txt - for optional SPI connection related properties, - -Examples: - -&spi1 { - wl1251@0 { - compatible = "ti,wl1251"; - - reg = <0>; - spi-max-frequency = <48000000>; - spi-cpol; - spi-cpha; - - interrupt-parent = <&gpio2>; - interrupts = <10 IRQ_TYPE_NONE>; /* gpio line 42 */ - - vio-supply = <&vio>; - ti,power-gpio = <&gpio3 23 GPIO_ACTIVE_HIGH>; /* 87 */ - }; -}; - -&mmc3 { - vmmc-supply = <&wlan_en>; - - bus-width = <4>; - non-removable; - ti,non-removable; - cap-power-off-card; - - pinctrl-names = "default"; - pinctrl-0 = <&mmc3_pins>; - - #address-cells = <1>; - #size-cells = <0>; - - wlan: wifi@1 { - compatible = "ti,wl1251"; - - reg = <1>; - - interrupt-parent = <&gpio1>; - interrupts = <21 IRQ_TYPE_LEVEL_HIGH>; /* GPIO_21 */ - - ti,wl1251-has-eeprom; - }; -}; diff --git a/Documentation/devicetree/bindings/net/wireless/ti,wl1251.yaml b/Documentation/devicetree/bindings/net/wireless/ti,wl1251.yaml new file mode 100644 index 000000000000..6ce82c4a2c0b --- /dev/null +++ b/Documentation/devicetree/bindings/net/wireless/ti,wl1251.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/wireless/ti,wl1251.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments WL1251 Wireless LAN Controller + +maintainers: + - Eduard Bostina + +properties: + compatible: + const: ti,wl1251 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vio-supply: + description: + Phandle to the regulator providing VIO. Required when connected via SPI. + + ti,power-gpio: + maxItems: 1 + description: + GPIO connected to chip's PMEN pin if operated in SPI mode. + + ti,wl1251-has-eeprom: + type: boolean + description: + The wl1251 has an EEPROM connected, which provides configuration + data (calibration, MAC, ...). + + clocks: + maxItems: 1 + + spi-max-frequency: true + spi-cpol: true + spi-cpha: true + +required: + - compatible + - reg + - interrupts + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + wifi@0 { + compatible = "ti,wl1251"; + reg = <0>; + spi-max-frequency = <48000000>; + spi-cpol; + spi-cpha; + interrupt-parent = <&gpio2>; + interrupts = <10 IRQ_TYPE_NONE>; + vio-supply = <&vio>; + ti,power-gpio = <&gpio3 23 GPIO_ACTIVE_HIGH>; + }; + }; + - | + #include + mmc { + #address-cells = <1>; + #size-cells = <0>; + wifi@1 { + compatible = "ti,wl1251"; + reg = <1>; + interrupt-parent = <&gpio1>; + interrupts = <21 IRQ_TYPE_LEVEL_HIGH>; + ti,wl1251-has-eeprom; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 645c85e3a212..e3ce77c839b0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -113,7 +113,7 @@ F: net/6lowpan/ M: Johannes Berg L: linux-wireless@vger.kernel.org S: Maintained -W: https://wireless.wiki.kernel.org/ +W: https://wireless.docs.kernel.org/en/latest/ Q: https://patchwork.kernel.org/project/linux-wireless/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git @@ -4260,13 +4260,13 @@ M: Nick Kossifidis M: Luis Chamberlain L: linux-wireless@vger.kernel.org S: Maintained -W: https://wireless.wiki.kernel.org/en/users/Drivers/ath5k +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/ath5k.html F: drivers/net/wireless/ath/ath5k/ ATHEROS ATH6KL WIRELESS DRIVER L: linux-wireless@vger.kernel.org S: Orphan -W: https://wireless.wiki.kernel.org/en/users/Drivers/ath6kl +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/ath6kl.html F: drivers/net/wireless/ath/ath6kl/ ATI_REMOTE2 DRIVER @@ -4535,14 +4535,14 @@ B43 WIRELESS DRIVER L: linux-wireless@vger.kernel.org L: b43-dev@lists.infradead.org S: Orphan -W: https://wireless.wiki.kernel.org/en/users/Drivers/b43 +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/b43.html F: drivers/net/wireless/broadcom/b43/ B43LEGACY WIRELESS DRIVER L: linux-wireless@vger.kernel.org L: b43-dev@lists.infradead.org S: Orphan -W: https://wireless.wiki.kernel.org/en/users/Drivers/b43 +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/b43.html F: drivers/net/wireless/broadcom/b43legacy/ BACKLIGHT CLASS/SUBSYSTEM @@ -5919,7 +5919,7 @@ CARL9170 LINUX COMMUNITY WIRELESS DRIVER M: Christian Lamparter L: linux-wireless@vger.kernel.org S: Maintained -W: https://wireless.wiki.kernel.org/en/users/Drivers/carl9170 +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/carl9170.html F: drivers/net/wireless/ath/carl9170/ CAVIUM I2C DRIVER @@ -13560,7 +13560,7 @@ INTEL WIRELESS WIFI LINK (iwlwifi) M: Miri Korenblit L: linux-wireless@vger.kernel.org S: Supported -W: https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/iwlwifi.html T: git https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next.git/ F: drivers/net/wireless/intel/iwlwifi/ @@ -15663,7 +15663,7 @@ MAC80211 M: Johannes Berg L: linux-wireless@vger.kernel.org S: Maintained -W: https://wireless.wiki.kernel.org/ +W: https://wireless.docs.kernel.org/en/latest/ Q: https://patchwork.kernel.org/project/linux-wireless/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git @@ -18933,7 +18933,7 @@ NETWORKING DRIVERS (WIRELESS) M: Johannes Berg L: linux-wireless@vger.kernel.org S: Maintained -W: https://wireless.wiki.kernel.org/ +W: https://wireless.docs.kernel.org/en/latest/ Q: https://patchwork.kernel.org/project/linux-wireless/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git @@ -20606,7 +20606,7 @@ P54 WIRELESS DRIVER M: Christian Lamparter L: linux-wireless@vger.kernel.org S: Maintained -W: https://wireless.wiki.kernel.org/en/users/Drivers/p54 +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/p54.html F: Documentation/devicetree/bindings/net/wireless/st,stlc4560.yaml F: drivers/net/wireless/intersil/ @@ -22296,7 +22296,7 @@ M: Jeff Johnson L: linux-wireless@vger.kernel.org L: ath12k@lists.infradead.org S: Supported -W: https://wireless.wiki.kernel.org/en/users/Drivers/ath12k +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/ath12k.html T: git git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath.git F: drivers/net/wireless/ath/ath12k/ N: ath12k @@ -22306,7 +22306,7 @@ M: Jeff Johnson L: linux-wireless@vger.kernel.org L: ath10k@lists.infradead.org S: Supported -W: https://wireless.wiki.kernel.org/en/users/Drivers/ath10k +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/ath10k.html T: git git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath.git F: drivers/net/wireless/ath/ath10k/ N: ath10k @@ -22316,8 +22316,8 @@ M: Jeff Johnson L: linux-wireless@vger.kernel.org L: ath11k@lists.infradead.org S: Supported -W: https://wireless.wiki.kernel.org/en/users/Drivers/ath11k -B: https://wireless.wiki.kernel.org/en/users/Drivers/ath11k/bugreport +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/ath11k.html +B: https://wireless.docs.kernel.org/en/latest/en/users/drivers/ath11k/bugreport.html T: git git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath.git F: drivers/net/wireless/ath/ath11k/ N: ath11k @@ -22326,7 +22326,7 @@ QUALCOMM ATHEROS ATH9K WIRELESS DRIVER M: Toke Høiland-Jørgensen L: linux-wireless@vger.kernel.org S: Maintained -W: https://wireless.wiki.kernel.org/en/users/Drivers/ath9k +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/ath9k.html T: git git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath.git F: Documentation/devicetree/bindings/net/wireless/qca,ath9k.yaml F: drivers/net/wireless/ath/ath9k/ @@ -22668,7 +22668,7 @@ M: Loic Poulain L: wcn36xx@lists.infradead.org L: linux-wireless@vger.kernel.org S: Supported -W: https://wireless.wiki.kernel.org/en/users/Drivers/wcn36xx +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/wcn36xx.html F: drivers/net/wireless/ath/wcn36xx/ QUANTENNA QTNFMAC WIRELESS DRIVER @@ -23427,7 +23427,7 @@ RFKILL M: Johannes Berg L: linux-wireless@vger.kernel.org S: Maintained -W: https://wireless.wiki.kernel.org/ +W: https://wireless.docs.kernel.org/en/latest/ Q: https://patchwork.kernel.org/project/linux-wireless/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git @@ -27522,8 +27522,8 @@ F: drivers/media/platform/ti/vpe/ TI WILINK WIRELESS DRIVERS L: linux-wireless@vger.kernel.org S: Orphan -W: https://wireless.wiki.kernel.org/en/users/Drivers/wl12xx -W: https://wireless.wiki.kernel.org/en/users/Drivers/wl1251 +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/wl12xx.html +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/wl1251.html F: drivers/net/wireless/ti/ TIMEKEEPING, CLOCKSOURCE CORE, NTP, ALARMTIMER @@ -29412,7 +29412,7 @@ F: drivers/hid/hid-wiimote* WILOCITY WIL6210 WIRELESS DRIVER L: linux-wireless@vger.kernel.org S: Orphan -W: https://wireless.wiki.kernel.org/en/users/Drivers/wil6210 +W: https://wireless.docs.kernel.org/en/latest/en/users/drivers/wil6210.html F: drivers/net/wireless/ath/wil6210/ WINBOND CIR DRIVER diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig index 122726f84492..44b520d2e66e 100644 --- a/drivers/net/wireless/ath/ath11k/Kconfig +++ b/drivers/net/wireless/ath/ath11k/Kconfig @@ -14,6 +14,9 @@ config ATH11K_AHB tristate "Atheros ath11k AHB support" depends on ATH11K depends on REMOTEPROC + select RPMSG + select QRTR + select QRTR_SMD help This module adds support for AHB bus diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index ec5bf5c9fd79..27d01411b6e9 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -391,12 +391,17 @@ static void ath11k_ahb_ext_irq_disable(struct ath11k_base *ab) ath11k_ahb_sync_ext_irqs(ab); } -static void ath11k_ahb_stop(struct ath11k_base *ab) +static void ath11k_ahb_ce_irq_disable_sync(struct ath11k_base *ab) { if (!test_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags)) ath11k_ahb_ce_irqs_disable(ab); ath11k_ahb_sync_ce_irqs(ab); ath11k_ahb_kill_tasklets(ab); +} + +static void ath11k_ahb_stop(struct ath11k_base *ab) +{ + ath11k_ahb_ce_irq_disable_sync(ab); timer_delete_sync(&ab->rx_replenish_retry); ath11k_ce_cleanup_pipes(ab); } @@ -804,6 +809,8 @@ static const struct ath11k_hif_ops ath11k_ahb_hif_ops_ipq8074 = { .map_service_to_pipe = ath11k_ahb_map_service_to_pipe, .power_down = ath11k_ahb_power_down, .power_up = ath11k_ahb_power_up, + .ce_irq_enable = ath11k_ahb_ce_irqs_enable, + .ce_irq_disable = ath11k_ahb_ce_irq_disable_sync, }; static const struct ath11k_hif_ops ath11k_ahb_hif_ops_wcn6750 = { @@ -1296,14 +1303,16 @@ static void ath11k_ahb_remove(struct platform_device *pdev) { struct ath11k_base *ab = platform_get_drvdata(pdev); - if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) { + ath11k_ahb_remove_prepare(ab); + + if (!test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags)) { ath11k_ahb_power_down(ab, false); ath11k_debugfs_soc_destroy(ab); ath11k_qmi_deinit_service(ab); + ath11k_core_pm_notifier_unregister(ab); goto qmi_fail; } - ath11k_ahb_remove_prepare(ab); ath11k_core_deinit(ab); qmi_fail: @@ -1343,5 +1352,6 @@ static struct platform_driver ath11k_ahb_driver = { module_platform_driver(ath11k_ahb_driver); +MODULE_SOFTDEP("pre: qrtr_smd"); MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11ax WLAN AHB devices"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c index c56dd8a86760..1e08b0970b14 100644 --- a/drivers/net/wireless/ath/ath11k/ce.c +++ b/drivers/net/wireless/ath/ath11k/ce.c @@ -325,7 +325,7 @@ static int ath11k_ce_rx_post_pipe(struct ath11k_ce_pipe *pipe) dma_addr_t paddr; int ret = 0; - if (!(pipe->dest_ring || pipe->status_ring)) + if (!pipe->dest_ring || !pipe->status_ring) return 0; spin_lock_bh(&ab->ce.ce_lock); diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 8039124e7832..d2ed6a0ea7e3 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -2334,6 +2334,16 @@ static int ath11k_core_reconfigure_on_crash(struct ath11k_base *ab) mutex_lock(&ab->core_lock); ath11k_thermal_unregister(ab); + + /* + * ath11k_core_reset() already disabled the interrupts on the reset + * path; only the firmware crash path reaches here with them live. + */ + if (!ab->is_reset) { + ath11k_hif_irq_disable(ab); + ath11k_hif_ce_irq_disable(ab); + } + ath11k_dp_pdev_free(ab); ath11k_cfr_deinit(ab); ath11k_spectral_deinit(ab); diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index f389b97acbdd..be65d73b904b 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -313,6 +313,7 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, case HAL_RXDMA_DIR_BUF: break; default: + ath11k_dp_srng_cleanup(ab, ring); ath11k_warn(ab, "Not a valid ring type in dp :%d\n", type); return -EINVAL; } @@ -324,6 +325,7 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, ret = ath11k_hal_srng_setup(ab, type, ring_num, mac_id, ¶ms); if (ret < 0) { + ath11k_dp_srng_cleanup(ab, ring); ath11k_warn(ab, "failed to setup srng: %d ring_id %d\n", ret, ring_num); return ret; diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 33425707c084..6d95f7b343a5 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -762,6 +762,9 @@ static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx, struct ath11k_base *ab = dp->ab; struct dp_rx_tid *rx_tid = ctx; struct dp_reo_cache_flush_elem *elem, *tmp; + struct list_head flush_list; + + INIT_LIST_HEAD(&flush_list); if (status == HAL_REO_CMD_DRAIN) { goto free_desc; @@ -783,23 +786,25 @@ static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx, list_add_tail(&elem->list, &dp->reo_cmd_cache_flush_list); dp->reo_cmd_cache_flush_count++; - /* Flush and invalidate aged REO desc from HW cache */ + /* identify aged REO desc that needs removal */ list_for_each_entry_safe(elem, tmp, &dp->reo_cmd_cache_flush_list, list) { if (dp->reo_cmd_cache_flush_count > DP_REO_DESC_FREE_THRESHOLD || time_after(jiffies, elem->ts + msecs_to_jiffies(DP_REO_DESC_FREE_TIMEOUT_MS))) { - list_del(&elem->list); + list_move_tail(&elem->list, &flush_list); dp->reo_cmd_cache_flush_count--; - spin_unlock_bh(&dp->reo_cmd_lock); - - ath11k_dp_reo_cache_flush(ab, &elem->data); - kfree(elem); - spin_lock_bh(&dp->reo_cmd_lock); } } spin_unlock_bh(&dp->reo_cmd_lock); + /* remove aged REO desc from HW */ + list_for_each_entry_safe(elem, tmp, &flush_list, list) { + ath11k_dp_reo_cache_flush(ab, &elem->data); + list_del(&elem->list); + kfree(elem); + } + return; free_desc: dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, @@ -2500,7 +2505,7 @@ static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *nap !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED)) rx_status->flag |= RX_FLAG_8023; - ieee80211_rx_napi(ar->hw, pubsta, msdu, napi); + ieee80211_rx_napi(ar->hw, pubsta ? &pubsta->deflink : NULL, msdu, napi); } static bool ath11k_dp_rx_check_nwifi_hdr_len_valid(struct ath11k_base *ab, diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c index d89a49c6ebb7..85360365aa4b 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.c +++ b/drivers/net/wireless/ath/ath12k/ahb.c @@ -347,35 +347,60 @@ static void ath12k_ahb_stop(struct ath12k_base *ab) ath12k_ce_cleanup_pipes(ab); } +static int ath12k_ahb_get_fw_load_region(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + struct ath12k_ahb_rproc_info *rproc_info = ab_ahb->rproc_info; + struct device *dev = ab->dev; + struct resource res; + int ret; + + if (rproc_info->mem_region) + return 0; + + ret = of_reserved_mem_region_to_resource_byname(dev->of_node, "q6-region", &res); + if (ret) + return ret; + + rproc_info->mem_phys = res.start; + rproc_info->mem_size = resource_size(&res); + rproc_info->mem_region = memremap(rproc_info->mem_phys, rproc_info->mem_size, + MEMREMAP_WC); + if (!rproc_info->mem_region) { + ath12k_err(ab, "unable to map memory region: %pa+%zx\n", + &res.start, rproc_info->mem_size); + rproc_info->mem_phys = 0; + rproc_info->mem_size = 0; + return -ENOMEM; + } + + return 0; +} + +static void ath12k_ahb_put_fw_load_region(struct ath12k_ahb_rproc_info *rproc_info) +{ + memunmap(rproc_info->mem_region); + rproc_info->mem_region = NULL; + rproc_info->mem_phys = 0; + rproc_info->mem_size = 0; +} + static int ath12k_ahb_power_up(struct ath12k_base *ab) { struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + struct ath12k_ahb_rproc_info *rproc_info = ab_ahb->rproc_info; char fw_name[ATH12K_USERPD_FW_NAME_LEN]; char fw2_name[ATH12K_USERPD_FW_NAME_LEN]; struct device *dev = ab->dev; const struct firmware *fw, *fw2; unsigned long time_left; - phys_addr_t mem_phys; - struct resource res; - void *mem_region; - size_t mem_size; u32 pasid; int ret; - ret = of_reserved_mem_region_to_resource_byname(dev->of_node, "q6-region", - &res); + ret = ath12k_ahb_get_fw_load_region(ab); if (ret) return ret; - mem_phys = res.start; - mem_size = resource_size(&res); - mem_region = devm_memremap(dev, mem_phys, mem_size, MEMREMAP_WC); - if (IS_ERR(mem_region)) { - ath12k_err(ab, "unable to map memory region: %pa+%zx\n", - &res.start, mem_size); - return PTR_ERR(mem_region); - } - snprintf(fw_name, sizeof(fw_name), "%s/%s/%s%d%s", ATH12K_FW_DIR, ab->hw_params->fw.dir, ATH12K_AHB_FW_PREFIX, ab_ahb->userpd_id, ATH12K_AHB_FW_SUFFIX); @@ -400,11 +425,13 @@ static int ath12k_ahb_power_up(struct ath12k_base *ab) /* Load FW image to a reserved memory location */ if (ab_ahb->scm_auth_enabled) - ret = qcom_mdt_load(dev, fw, fw_name, pasid, mem_region, - mem_phys, mem_size, &mem_phys); + ret = qcom_mdt_load(dev, fw, fw_name, pasid, rproc_info->mem_region, + rproc_info->mem_phys, rproc_info->mem_size, + NULL); else - ret = qcom_mdt_load_no_init(dev, fw, fw_name, mem_region, - mem_phys, mem_size, &mem_phys); + ret = qcom_mdt_load_no_init(dev, fw, fw_name, rproc_info->mem_region, + rproc_info->mem_phys, rproc_info->mem_size, + NULL); if (ret) { ath12k_err(ab, "Failed to load MDT segments: %d\n", ret); goto err_fw; @@ -428,8 +455,9 @@ static int ath12k_ahb_power_up(struct ath12k_base *ab) goto err_fw2; } - ret = qcom_mdt_load_no_init(dev, fw2, fw2_name, mem_region, mem_phys, - mem_size, &mem_phys); + ret = qcom_mdt_load_no_init(dev, fw2, fw2_name, rproc_info->mem_region, + rproc_info->mem_phys, rproc_info->mem_size, + NULL); if (ret) { ath12k_err(ab, "Failed to load MDT segments: %d\n", ret); goto err_fw2; @@ -877,6 +905,7 @@ static struct ath12k_ahb_rproc_info *ath12k_ahb_rproc_info_alloc(struct ath12k_b rproc_info->rootpd_booted_by_driver = false; rproc_info->userpd[ab_ahb->userpd_id - 1] = ab_ahb; rproc_info->num_userpd = 1; + rproc_info->shared_fw_loaded = false; init_completion(&rproc_info->rootpd_ready); ab_ahb->rproc_info = rproc_info; @@ -961,6 +990,76 @@ static int ath12k_ahb_boot_root_pd(struct ath12k_base *ab) return 0; } +static int ath12k_ahb_load_auth_shared_fw(struct ath12k_base *ab, + struct ath12k_ahb_rproc_info *rproc_info, + const char *fw_name, u32 pasid) +{ + int ret; + + const struct firmware *fw __free(firmware) = NULL; + ret = request_firmware(&fw, fw_name, ab->dev); + if (ret) { + ath12k_err(ab, "failed to request shared firmware %s: %d\n", + fw_name, ret); + return ret; + } + + if (!fw->size) { + ath12k_err(ab, "Invalid firmware size\n"); + return -EINVAL; + } + + ath12k_dbg(ab, ATH12K_DBG_AHB, "loading firmware %s, size %zd\n", fw_name, + fw->size); + + ret = qcom_mdt_load(ab->dev, fw, fw_name, pasid, rproc_info->mem_region, + rproc_info->mem_phys, rproc_info->mem_size, NULL); + if (ret) { + ath12k_err(ab, "failed to load RO firmware %s: %d\n", fw_name, ret); + return ret; + } + + ret = qcom_pas_auth_and_reset(pasid); + if (ret) + ath12k_err(ab, "failed to authenticate and boot shared firmware: %d\n", + ret); + return ret; +} + +static int ath12k_ahb_load_shared_firmware(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + struct ath12k_ahb_rproc_info *rproc_info = ab_ahb->rproc_info; + char fw_name[ATH12K_USERPD_FW_NAME_LEN]; + u32 pasid; + int ret; + + lockdep_assert_held(&ath12k_rproc_info_lock); + + if (!ab_ahb->supports_multipd) + return 0; + + ret = ath12k_ahb_get_fw_load_region(ab); + if (ret) + return ret; + + snprintf(fw_name, sizeof(fw_name), "%s/%s/%s%d%s", ATH12K_FW_DIR, + ab->hw_params->fw.dir, ATH12K_AHB_FW_PREFIX, ATH12K_AHB_RO_ID, + ATH12K_AHB_FW_SUFFIX); + + pasid = u32_encode_bits(ATH12K_AHB_RO_ID, ATH12K_USERPD_ID_MASK) | + ATH12K_AHB_UPD_SWID; + + ret = ath12k_ahb_load_auth_shared_fw(ab, rproc_info, fw_name, pasid); + if (ret) { + ath12k_ahb_put_fw_load_region(rproc_info); + return ret; + } + + rproc_info->shared_fw_loaded = true; + return 0; +} + static int ath12k_ahb_configure_rproc(struct ath12k_base *ab) { int ret; @@ -989,6 +1088,15 @@ static int ath12k_ahb_configure_rproc(struct ath12k_base *ab) g_rproc_info->rootpd_booted_by_driver = true; } + if (!g_rproc_info->shared_fw_loaded) { + ret = ath12k_ahb_load_shared_firmware(ab); + if (ret) { + if (g_rproc_info->rootpd_booted_by_driver) + rproc_shutdown(g_rproc_info->tgt_rproc); + goto err_unreg_notifier; + } + } + mutex_unlock(&ath12k_rproc_info_lock); return 0; @@ -1012,6 +1120,8 @@ static void ath12k_ahb_deconfigure_rproc(struct ath12k_base *ab) { struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); struct ath12k_ahb_rproc_info *rproc_info = ab_ahb->rproc_info; + u32 pasid; + int ret; lockdep_assert_held(&ath12k_rproc_info_lock); @@ -1023,6 +1133,20 @@ static void ath12k_ahb_deconfigure_rproc(struct ath12k_base *ab) if (!g_rproc_info->num_userpd) { ath12k_ahb_unregister_rproc_notifier(); + if (g_rproc_info->shared_fw_loaded) { + pasid = u32_encode_bits(ATH12K_AHB_RO_ID, ATH12K_USERPD_ID_MASK) | + ATH12K_AHB_UPD_SWID; + ret = qcom_pas_shutdown(pasid); + if (ret) + ath12k_err(ab, "pas shutdown failed for shared firmware: %d\n", + ret); + + g_rproc_info->shared_fw_loaded = false; + } + + if (g_rproc_info->mem_region) + ath12k_ahb_put_fw_load_region(g_rproc_info); + if (g_rproc_info->rootpd_booted_by_driver && g_rproc_info->tgt_rproc->state == RPROC_RUNNING) rproc_shutdown(g_rproc_info->tgt_rproc); diff --git a/drivers/net/wireless/ath/ath12k/ahb.h b/drivers/net/wireless/ath/ath12k/ahb.h index cdb58b07338f..46a4420d41bd 100644 --- a/drivers/net/wireless/ath/ath12k/ahb.h +++ b/drivers/net/wireless/ath/ath12k/ahb.h @@ -30,6 +30,9 @@ #define ATH12K_USERPD_ID_MASK GENMASK(10, 8) #define ATH12K_USERPD_FW_NAME_LEN 35 +/* Shared read-only firmware ID, mandated by MultiPD firmware architecture */ +#define ATH12K_AHB_RO_ID 4 + enum ath12k_ahb_userpd_id { ATH12K_AHB_USERPD_ID_0 = 1, ATH12K_AHB_USERPD_ID_1, @@ -46,6 +49,7 @@ struct ath12k_ahb_desc { enum ath12k_hw_rev hw_rev; bool auth_enabled; const struct ath12k_hif_ops *ops; + bool supports_multipd; }; enum ath12k_ahb_smp2p_msg_id { @@ -77,6 +81,10 @@ struct ath12k_ahb_rproc_info { u8 num_userpd; bool rootpd_booted_by_driver; struct ath12k_ahb *userpd[ATH12K_MAX_DEVICES]; + bool shared_fw_loaded; + phys_addr_t mem_phys; + size_t mem_size; + void *mem_region; }; struct ath12k_ahb { @@ -95,6 +103,7 @@ struct ath12k_ahb { const struct ath12k_ahb_device_family_ops *device_family_ops; bool scm_auth_enabled; struct ath12k_ahb_rproc_info *rproc_info; + bool supports_multipd; }; struct ath12k_ahb_driver { diff --git a/drivers/net/wireless/ath/ath12k/ce.c b/drivers/net/wireless/ath/ath12k/ce.c index 67bbc6d8d0c9..c99e915e3885 100644 --- a/drivers/net/wireless/ath/ath12k/ce.c +++ b/drivers/net/wireless/ath/ath12k/ce.c @@ -269,7 +269,7 @@ static void ath12k_ce_srng_msi_ring_params_setup(struct ath12k_base *ab, u32 ce_ ath12k_hif_get_ce_msi_idx(ab, ce_id, &msi_data_idx); ring_params->msi_addr = addr_lo; - ring_params->msi_addr |= (dma_addr_t)(((uint64_t)addr_hi) << 32); + ring_params->msi_addr |= (dma_addr_t)(((u64)addr_hi) << 32); ring_params->msi_data = (msi_data_idx % msi_data_count) + msi_data_start; ring_params->flags |= HAL_SRNG_FLAGS_MSI_INTR; } diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 262a2045309b..0c72948f3f56 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -792,6 +792,29 @@ int ath12k_core_check_smbios(struct ath12k_base *ab) return 0; } +int ath12k_core_check_dt(struct ath12k_base *ab) +{ + size_t max_len = sizeof(ab->qmi.target.bdf_ext); + const char *variant = NULL; + struct device_node *node; + + node = ab->dev->of_node; + if (!node) + return -ENOENT; + + of_property_read_string(node, "qcom,calibration-variant", + &variant); + if (!variant) + return -ENODATA; + + if (strscpy(ab->qmi.target.bdf_ext, variant, max_len) < 0) + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "bdf variant string is longer than the buffer can accommodate (variant: %s)\n", + variant); + + return 0; +} + static int ath12k_core_soc_create(struct ath12k_base *ab) { int ret; diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index f9b37d75956d..ef9601152f81 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -224,7 +224,7 @@ static void ath12k_dp_srng_msi_setup(struct ath12k_base *ab, ath12k_hif_get_msi_address(ab, &addr_lo, &addr_hi); ring_params->msi_addr = addr_lo; - ring_params->msi_addr |= (dma_addr_t)(((uint64_t)addr_hi) << 32); + ring_params->msi_addr |= (dma_addr_t)(((u64)addr_hi) << 32); ring_params->msi_data = (msi_group_number % msi_data_count) + msi_data_start; ring_params->flags |= HAL_SRNG_FLAGS_MSI_INTR; diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 7d5be77b081f..1faf1896d0ab 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -503,8 +503,6 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct ieee80211_rx_status *rx_status; struct ieee80211_radiotap_he *he = NULL; - status->link_valid = 0; - if ((status->encoding == RX_ENC_HE) && !(status->flag & RX_FLAG_RADIOTAP_HE) && !(status->flag & RX_FLAG_SKIP_MONITOR)) { he = skb_push(msdu, sizeof(known)); diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 8fa0e90b4531..41ac0a4072ae 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1375,6 +1375,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc struct ath12k_dp *dp = dp_pdev->dp; struct ieee80211_rx_status *rx_status; struct ieee80211_sta *pubsta; + struct ieee80211_link_sta *link_pubsta = NULL; struct ath12k_dp_peer *peer; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); struct ieee80211_rx_status *status = rx_info->rx_status; @@ -1384,9 +1385,19 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc pubsta = peer ? peer->sta : NULL; - status->link_valid = 0; - if (pubsta && pubsta->valid_links) - ath12k_hw_set_rx_link_id(dp->hw_params, peer, rxcb, status); + if (pubsta) { + if (pubsta->valid_links) { + int link_id = + ath12k_hw_get_rx_link_id(dp->hw_params, peer, + rxcb, status); + + if (link_id >= 0) + link_pubsta = + rcu_dereference(pubsta->link[link_id]); + } else { + link_pubsta = &pubsta->deflink; + } + } ath12k_dbg(dp->ab, ATH12K_DBG_DATA, "rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", @@ -1422,7 +1433,8 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc /* TODO: trace rx packet */ - ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi); + ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), link_pubsta, msdu, + napi); } EXPORT_SYMBOL(ath12k_dp_rx_deliver_msdu); diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index 011bb22e21c7..e790de5737db 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -249,9 +249,9 @@ struct ath12k_hw_ops { bool (*dp_srng_is_tx_comp_ring)(int ring_num); bool (*is_frame_link_agnostic)(struct ath12k_link_vif *arvif, struct ieee80211_mgmt *mgmt); - void (*set_rx_link_id)(struct ath12k_dp_peer *dp_peer, - struct ath12k_skb_rxcb *rxcb, - struct ieee80211_rx_status *status); + int (*get_rx_link_id)(struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status); }; static inline @@ -282,13 +282,15 @@ static inline int ath12k_hw_mac_id_to_srng_id(const struct ath12k_hw_params *hw, return 0; } -static inline void ath12k_hw_set_rx_link_id(const struct ath12k_hw_params *hw, - struct ath12k_dp_peer *dp_peer, - struct ath12k_skb_rxcb *rxcb, - struct ieee80211_rx_status *status) +static inline int ath12k_hw_get_rx_link_id(const struct ath12k_hw_params *hw, + struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status) { - if (hw->hw_ops->set_rx_link_id) - hw->hw_ops->set_rx_link_id(dp_peer, rxcb, status); + if (hw->hw_ops->get_rx_link_id) + return hw->hw_ops->get_rx_link_id(dp_peer, rxcb, status); + + return -1; } struct ath12k_fw_ie { diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 99bf5cf79d10..d4116ba0da08 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -1506,6 +1506,13 @@ static int ath12k_mac_monitor_start(struct ath12k *ar) return ret; } + /* + * A previous monitor session may have stopped mid-PPDU, leaving + * ppdu_continuation set. Clear it so the first status buffer of + * this session is not merged with stale state from before. + */ + ar->dp.mon_data.mon_ppdu_info.ppdu_continuation = false; + ar->monitor_started = true; ar->num_started_vdevs++; @@ -3517,7 +3524,7 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar, IEEE80211_EHT_MCS_NSS_RX)); } - max_nss = min(max_nss, (uint8_t)eht_nss); + max_nss = min(max_nss, (u8)eht_nss); arg->peer_nss = min(link_sta->rx_nss, max_nss); @@ -14294,6 +14301,7 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, sizeof(ath12k_6ghz_channels), GFP_KERNEL); if (!channels) { kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); + ar->mac.sbands[NL80211_BAND_2GHZ].channels = NULL; return -ENOMEM; } @@ -14344,7 +14352,9 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, GFP_KERNEL); if (!channels) { kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); + ar->mac.sbands[NL80211_BAND_2GHZ].channels = NULL; kfree(ar->mac.sbands[NL80211_BAND_6GHZ].channels); + ar->mac.sbands[NL80211_BAND_6GHZ].channels = NULL; return -ENOMEM; } @@ -14384,7 +14394,7 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, kfree(ar->mac.sbands[NL80211_BAND_2GHZ].channels); ar->mac.sbands[NL80211_BAND_2GHZ].channels = NULL; kfree(ar->mac.sbands[NL80211_BAND_6GHZ].channels); - ar->mac.sbands[NL80211_BAND_2GHZ].channels = NULL; + ar->mac.sbands[NL80211_BAND_6GHZ].channels = NULL; kfree(channels); band->channels = NULL; return ret; @@ -15043,28 +15053,49 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MONITOR); for_each_ar(ah, ar, i) { + struct ath12k_base *this_ab = ar->ab; + /* Apply the regd received during initialization */ ret = ath12k_regd_update(ar, true); if (ret) { - ath12k_err(ar->ab, "ath12k regd update failed: %d\n", ret); + ath12k_err(this_ab, "ath12k regd update failed: %d\n", ret); goto err_unregister_hw; } - if (ar->ab->hw_params->current_cc_support && ab->new_alpha2[0]) { + if (this_ab->hw_params->current_cc_support) { struct wmi_set_current_country_arg current_cc = {}; + struct ieee80211_regdomain *default_regd; + bool same_cc = false; - memcpy(¤t_cc.alpha2, ab->new_alpha2, 2); - memcpy(&ar->alpha2, ab->new_alpha2, 2); + spin_lock_bh(&this_ab->base_lock); + memcpy(¤t_cc.alpha2, this_ab->new_alpha2, 2); + spin_unlock_bh(&this_ab->base_lock); + + if (!current_cc.alpha2[0]) + goto fw_stats_init; + + memcpy(&ar->alpha2, current_cc.alpha2, 2); + + spin_lock_bh(&this_ab->base_lock); + default_regd = this_ab->default_regd[ar->pdev_idx]; + if (default_regd) + same_cc = !memcmp(default_regd->alpha2, + current_cc.alpha2, 2); + spin_unlock_bh(&this_ab->base_lock); + + if (same_cc) + goto fw_stats_init; reinit_completion(&ar->regd_update_completed); ret = ath12k_wmi_send_set_current_country_cmd(ar, ¤t_cc); if (ret) - ath12k_warn(ar->ab, + ath12k_warn(this_ab, "failed set cc code for mac register: %d\n", ret); } +fw_stats_init: ath12k_fw_stats_init(ar); ath12k_debugfs_register(ar); } diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index 6441927b5382..af0e882fd0b5 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -313,11 +313,24 @@ static void ath12k_pci_sw_reset(struct ath12k_base *ab, bool power_on) ath12k_mhi_set_mhictrl_reset(ab); } -static void ath12k_pci_free_ext_irq(struct ath12k_base *ab) +static void ath12k_pci_free_ce_irq(struct ath12k_base *ab, int num_ce) +{ + int i, irq_idx; + + for (i = 0; i < num_ce; i++) { + if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + + irq_idx = ATH12K_PCI_IRQ_CE0_OFFSET + i; + free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]); + } +} + +static void ath12k_pci_free_ext_irq(struct ath12k_base *ab, int num_ext_irq_grp) { int i, j; - for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { + for (i = 0; i < num_ext_irq_grp; i++) { struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; for (j = 0; j < irq_grp->num_irq; j++) @@ -330,16 +343,8 @@ static void ath12k_pci_free_ext_irq(struct ath12k_base *ab) static void ath12k_pci_free_irq(struct ath12k_base *ab) { - int i, irq_idx; - - for (i = 0; i < ab->hw_params->ce_count; i++) { - if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) - continue; - irq_idx = ATH12K_PCI_IRQ_CE0_OFFSET + i; - free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]); - } - - ath12k_pci_free_ext_irq(ab); + ath12k_pci_free_ce_irq(ab, ab->hw_params->ce_count); + ath12k_pci_free_ext_irq(ab, ATH12K_EXT_IRQ_GRP_NUM_MAX); } static void ath12k_pci_ce_irq_enable(struct ath12k_base *ab, u16 ce_id) @@ -595,8 +600,6 @@ static int ath12k_pci_ext_irq_config(struct ath12k_base *ab) irq = ath12k_pci_get_msi_irq(ab->dev, vector); - ab->irq_num[irq_idx] = irq; - ath12k_dbg(ab, ATH12K_DBG_PCI, "irq:%d group:%d\n", irq, i); @@ -607,22 +610,24 @@ static int ath12k_pci_ext_irq_config(struct ath12k_base *ab) if (ret) { ath12k_err(ab, "failed request irq %d: %d\n", vector, ret); - goto fail_request; + + for (n = 0; n < j; n++) + free_irq(ab->irq_num[irq_grp->irqs[n]], irq_grp); + + netif_napi_del(&ab->ext_irq_grp[i].napi); + free_netdev(ab->ext_irq_grp[i].napi_ndev); + goto fail_allocate; } + + ab->irq_num[irq_idx] = irq; } ath12k_pci_ext_grp_disable(irq_grp); } return 0; -fail_request: - /* i ->napi_ndev was properly allocated. Free it also */ - i += 1; fail_allocate: - for (n = 0; n < i; n++) { - irq_grp = &ab->ext_irq_grp[n]; - free_netdev(irq_grp->napi_ndev); - } + ath12k_pci_free_ext_irq(ab, i); return ret; } @@ -671,6 +676,8 @@ static int ath12k_pci_config_irq(struct ath12k_base *ab) if (ret) { ath12k_err(ab, "failed to request irq %d: %d\n", irq_idx, ret); + + ath12k_pci_free_ce_irq(ab, i); return ret; } @@ -681,8 +688,10 @@ static int ath12k_pci_config_irq(struct ath12k_base *ab) } ret = ath12k_pci_ext_irq_config(ab); - if (ret) + if (ret) { + ath12k_pci_free_ce_irq(ab, ab->hw_params->ce_count); return ret; + } return 0; } diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 280e50a1f31d..50082505c882 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -2972,6 +2972,10 @@ int ath12k_qmi_request_target_cap(struct ath12k_base *ab) if (r) ath12k_dbg(ab, ATH12K_DBG_QMI, "SMBIOS bdf variant name not set.\n"); + r = ath12k_core_check_dt(ab); + if (r) + ath12k_dbg(ab, ATH12K_DBG_QMI, "DT bdf variant name not set.\n"); + r = ath12k_acpi_start(ab); if (r) /* ACPI is optional so continue in case of an error */ diff --git a/drivers/net/wireless/ath/ath12k/wifi7/ahb.c b/drivers/net/wireless/ath/ath12k/wifi7/ahb.c index 6e9e9034cba1..70372db6abae 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/ahb.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/ahb.c @@ -20,11 +20,13 @@ static const struct ath12k_ahb_desc ath12k_wifi7_ahb_desc[] = { .hw_rev = ATH12K_HW_IPQ5332_HW10, .auth_enabled = true, .ops = &ath12k_ahb_hif_ops, + .supports_multipd = true, }, [ATH12K_HW_IPQ5424_HW10] = { .hw_rev = ATH12K_HW_IPQ5424_HW10, .auth_enabled = false, .ops = &ath12k_ahb_hif_ops, + .supports_multipd = false, }, }; diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c index 016b0c38e51e..ded7d56cd79b 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c @@ -3211,18 +3211,23 @@ __ath12k_wifi7_dp_mon_process_ring(struct ath12k *ar, int mac_id, goto exit; while ((skb = __skb_dequeue(&skb_list))) { - memset(ppdu_info, 0, sizeof(*ppdu_info)); - ppdu_info->peer_id = HAL_INVALID_PEERID; + if (!ppdu_info->ppdu_continuation) + ath12k_wifi7_dp_mon_rx_memset_ppdu_info(ppdu_info); hal_status = ath12k_wifi7_dp_mon_parse_rx_dest(&ar->dp, pmon, skb); - if (ar->monitor_started && - pmon->mon_ppdu_status == DP_PPDU_STATUS_START && - hal_status == HAL_TLV_STATUS_PPDU_DONE) { - rx_mon_stats->status_ppdu_done++; - pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; - ath12k_wifi7_dp_rx_mon_dest_process(ar, mac_id, *budget, napi); - pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + if (hal_status == HAL_TLV_STATUS_PPDU_DONE) { + ppdu_info->ppdu_continuation = false; + if (ar->monitor_started && + pmon->mon_ppdu_status == DP_PPDU_STATUS_START) { + rx_mon_stats->status_ppdu_done++; + pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; + ath12k_wifi7_dp_rx_mon_dest_process(ar, mac_id, + *budget, napi); + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + } + } else { + ppdu_info->ppdu_continuation = true; } dev_kfree_skb_any(skb); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c index 95d87dd67872..8b722ee0ad8f 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c @@ -2317,17 +2317,16 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab, return tlv_tag == HAL_RX_MPDU_START; } -void -ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer, +int +ath12k_wifi7_dp_rx_get_link_id_qcn9274(struct ath12k_dp_peer *dp_peer, struct ath12k_skb_rxcb *rxcb, struct ieee80211_rx_status *status) { - status->link_valid = 1; - status->link_id = dp_peer->hw_links[rxcb->hw_link_id]; + return dp_peer->hw_links[rxcb->hw_link_id]; } -void -ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer, +int +ath12k_wifi7_dp_rx_get_link_id_wcn7850(struct ath12k_dp_peer *dp_peer, struct ath12k_skb_rxcb *rxcb, struct ieee80211_rx_status *status) { @@ -2341,10 +2340,9 @@ ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer, links_map = READ_ONCE(dp_peer->link_peers_map); for_each_set_bit(i, &links_map, ATH12K_NUM_MAX_LINKS) { link_peer = rcu_dereference(dp_peer->link_peers[i]); - if (link_peer && link_peer->peer_id == rxcb->peer_id) { - status->link_valid = 1; - status->link_id = link_peer->link_id; - return; - } + if (link_peer && link_peer->peer_id == rxcb->peer_id) + return link_peer->link_id; } + + return -1; } diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h index 1d3a4788a2dd..d68489ff7b03 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h @@ -57,10 +57,10 @@ ath12k_wifi7_dp_rxdesc_mpdu_valid(struct ath12k_base *ab, struct hal_rx_desc *rx_desc); int ath12k_wifi7_dp_rx_tid_delete_handler(struct ath12k_base *ab, struct ath12k_dp_rx_tid_rxq *rx_tid); -void ath12k_wifi7_dp_rx_set_link_id_qcn9274(struct ath12k_dp_peer *dp_peer, - struct ath12k_skb_rxcb *rxcb, - struct ieee80211_rx_status *status); -void ath12k_wifi7_dp_rx_set_link_id_wcn7850(struct ath12k_dp_peer *dp_peer, - struct ath12k_skb_rxcb *rxcb, - struct ieee80211_rx_status *status); +int ath12k_wifi7_dp_rx_get_link_id_qcn9274(struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status); +int ath12k_wifi7_dp_rx_get_link_id_wcn7850(struct ath12k_dp_peer *dp_peer, + struct ath12k_skb_rxcb *rxcb, + struct ieee80211_rx_status *status); #endif diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c index 587d58eeccfa..7c12dba4cc51 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_tx.c @@ -63,7 +63,7 @@ ath12k_wifi7_hal_tx_cmd_ext_desc_setup(struct ath12k_base *ab, { tcl_ext_cmd->info0 = le32_encode_bits(ti->paddr, HAL_TX_MSDU_EXT_INFO0_BUF_PTR_LO); - tcl_ext_cmd->info1 = le32_encode_bits(0x0, + tcl_ext_cmd->info1 = le32_encode_bits((u64)ti->paddr >> HAL_ADDR_MSB_REG_SHIFT, HAL_TX_MSDU_EXT_INFO1_BUF_PTR_HI) | le32_encode_bits(ti->data_len, HAL_TX_MSDU_EXT_INFO1_BUF_LEN); @@ -313,10 +313,15 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a goto map; } - /* hdr is pointing to a wrong place after alignment, - * so refresh it for later use. + /* + * The payload may have been shifted or even the entire buffer may have + * been reallocated for alignment. In that case, hdr, eth and skb_cb + * are stale pointers. Refresh them now for later dereference. */ hdr = (void *)skb->data; + if (eth) + eth = (struct ethhdr *)skb->data; + skb_cb = ATH12K_SKB_CB(skb); } map: ti.paddr = dma_map_single(dp->dev, skb->data, skb->len, DMA_TO_DEVICE); @@ -449,15 +454,17 @@ int ath12k_wifi7_dp_tx(struct ath12k_pdev_dp *dp_pdev, struct ath12k_link_vif *a return 0; fail_unmap_dma_ext: - if (skb_cb->paddr_ext_desc) + if (skb_cb->paddr_ext_desc) { dma_unmap_single(dp->dev, skb_cb->paddr_ext_desc, skb_ext_desc->len, DMA_TO_DEVICE); + skb_cb->paddr_ext_desc = 0; + } fail_free_ext_skb: kfree_skb(skb_ext_desc); fail_unmap_dma: - dma_unmap_single(dp->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE); + dma_unmap_single(dp->dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); fail_remove_tx_buf: ath12k_dp_tx_release_txbuf(dp, tx_desc, pool_id); diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c index eeabe9db776e..def540529872 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hal_tx.c @@ -36,7 +36,7 @@ void ath12k_wifi7_hal_tx_cmd_desc_setup(struct ath12k_base *ab, tcl_cmd->buf_addr_info.info0 = le32_encode_bits(ti->paddr, BUFFER_ADDR_INFO0_ADDR); tcl_cmd->buf_addr_info.info1 = - le32_encode_bits(((uint64_t)ti->paddr >> HAL_ADDR_MSB_REG_SHIFT), + le32_encode_bits(((u64)ti->paddr >> HAL_ADDR_MSB_REG_SHIFT), BUFFER_ADDR_INFO1_ADDR); tcl_cmd->buf_addr_info.info1 |= le32_encode_bits((ti->rbm_id), BUFFER_ADDR_INFO1_RET_BUF_MGR) | diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c index 7b86f324b2e6..ae8df80928b0 100644 --- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c +++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c @@ -158,7 +158,7 @@ static const struct ath12k_hw_ops qcn9274_ops = { .get_ring_selector = ath12k_wifi7_hw_get_ring_selector_qcn9274, .dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_qcn9274, .is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_qcn9274, - .set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_qcn9274, + .get_rx_link_id = ath12k_wifi7_dp_rx_get_link_id_qcn9274, }; static const struct ath12k_hw_ops wcn7850_ops = { @@ -169,7 +169,7 @@ static const struct ath12k_hw_ops wcn7850_ops = { .get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850, .dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850, .is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850, - .set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850, + .get_rx_link_id = ath12k_wifi7_dp_rx_get_link_id_wcn7850, }; static const struct ath12k_hw_ops qcc2072_ops = { @@ -180,7 +180,7 @@ static const struct ath12k_hw_ops qcc2072_ops = { .get_ring_selector = ath12k_wifi7_hw_get_ring_selector_wcn7850, .dp_srng_is_tx_comp_ring = ath12k_wifi7_dp_srng_is_comp_ring_wcn7850, .is_frame_link_agnostic = ath12k_wifi7_is_frame_link_agnostic_wcn7850, - .set_rx_link_id = ath12k_wifi7_dp_rx_set_link_id_wcn7850, + .get_rx_link_id = ath12k_wifi7_dp_rx_get_link_id_wcn7850, }; #define ATH12K_TX_RING_MASK_0 0x1 @@ -664,7 +664,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = { .interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_MESH_POINT), + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_AP_VLAN), .supports_monitor = true, .supports_cong_ctrl_max_msdus = true, diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index d5160af60e00..3149b6376c94 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -6975,6 +6975,14 @@ static int ath12k_reg_chan_list_event(struct ath12k_base *ab, struct sk_buff *sk goto mem_free; } + /* + * Set the valid pdev_idx before validating so that, even when the + * event is dropped or falls back, the completion the caller in + * ath12k_mac_hw_register() may be waiting on is still signalled at + * the end and it does not time out. + */ + pdev_idx = reg_info->phy_id; + ret = ath12k_reg_validate_reg_info(ab, reg_info); if (ret == ATH12K_REG_STATUS_FALLBACK) { ath12k_warn(ab, "failed to validate reg info %d\n", ret); @@ -6991,7 +6999,6 @@ static int ath12k_reg_chan_list_event(struct ath12k_base *ab, struct sk_buff *sk } /* free old reg_info if it exist */ - pdev_idx = reg_info->phy_id; if (ab->reg_info[pdev_idx]) { ath12k_reg_reset_reg_info(ab->reg_info[pdev_idx]); kfree(ab->reg_info[pdev_idx]); @@ -7030,7 +7037,7 @@ static int ath12k_reg_chan_list_event(struct ath12k_base *ab, struct sk_buff *sk out: /* In some error cases, even a valid pdev_idx might not be available */ - if (pdev_idx != 255) + if (pdev_idx < ab->num_radios) ar = ab->pdevs[pdev_idx].ar; /* During the boot-time update, 'ar' might not be allocated, @@ -7271,7 +7278,6 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) struct ath12k_wmi_mgmt_rx_arg rx_ev = {}; struct ath12k *ar; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - struct ieee80211_sta *pubsta = NULL; struct ath12k_dp_link_peer *peer; struct ieee80211_hdr *hdr; bool is_4addr_null_pkt; @@ -7373,11 +7379,6 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) dev_kfree_skb(skb); goto exit; } - pubsta = peer->sta; - if (pubsta && pubsta->valid_links) { - status->link_valid = 1; - status->link_id = peer->link_id; - } spin_unlock_bh(&dp->dp_lock); goto send_rx; } @@ -9992,11 +9993,6 @@ static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab, u32 event_count; int ret; - if (!skb->data) { - ath12k_warn(ab, "No data present in tpc stats event\n"); - return; - } - if (skb->len < (sizeof(*fixed_param) + TLV_HDR_SIZE)) { ath12k_warn(ab, "TPC stats event size invalid\n"); return; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 89d8b3178784..3413bb4906d4 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -168,7 +168,7 @@ static bool ath_merge_ratetbl(struct ieee80211_sta *sta, struct ath_buf *bf, if (!sta) return false; - ratetbl = rcu_dereference(sta->rates); + ratetbl = rcu_dereference_bh(sta->rates); if (!ratetbl) return false; diff --git a/drivers/net/wireless/broadcom/b43legacy/dma.c b/drivers/net/wireless/broadcom/b43legacy/dma.c index a9557356c9ab..6c3d847242bb 100644 --- a/drivers/net/wireless/broadcom/b43legacy/dma.c +++ b/drivers/net/wireless/broadcom/b43legacy/dma.c @@ -610,25 +610,25 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, int nr_slots; dma_addr_t dma_test; - ring = kzalloc_obj(*ring); + if (for_tx) + nr_slots = B43legacy_TXRING_SLOTS; + else + nr_slots = B43legacy_RXRING_SLOTS; + + ring = kzalloc_flex(*ring, meta, nr_slots); if (!ring) goto out; + + ring->nr_slots = nr_slots; ring->type = type; ring->dev = dev; - nr_slots = B43legacy_RXRING_SLOTS; - if (for_tx) - nr_slots = B43legacy_TXRING_SLOTS; - - ring->meta = kzalloc_objs(struct b43legacy_dmadesc_meta, nr_slots); - if (!ring->meta) - goto err_kfree_ring; if (for_tx) { ring->txhdr_cache = kcalloc(nr_slots, sizeof(struct b43legacy_txhdr_fw3), GFP_KERNEL); if (!ring->txhdr_cache) - goto err_kfree_meta; + goto err_kfree_ring; /* test for ability to dma to txhdr_cache */ dma_test = dma_map_single(dev->dev->dma_dev, ring->txhdr_cache, @@ -643,7 +643,7 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, sizeof(struct b43legacy_txhdr_fw3), GFP_KERNEL | GFP_DMA); if (!ring->txhdr_cache) - goto err_kfree_meta; + goto err_kfree_txhdr_cache; dma_test = dma_map_single(dev->dev->dma_dev, ring->txhdr_cache, @@ -660,7 +660,6 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, DMA_TO_DEVICE); } - ring->nr_slots = nr_slots; ring->mmio_base = b43legacy_dmacontroller_base(type, controller_index); ring->index = controller_index; if (for_tx) { @@ -694,8 +693,6 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, free_ringmemory(ring); err_kfree_txhdr_cache: kfree(ring->txhdr_cache); -err_kfree_meta: - kfree(ring->meta); err_kfree_ring: kfree(ring); ring = NULL; @@ -720,7 +717,6 @@ static void b43legacy_destroy_dmaring(struct b43legacy_dmaring *ring) free_ringmemory(ring); kfree(ring->txhdr_cache); - kfree(ring->meta); kfree(ring); } diff --git a/drivers/net/wireless/broadcom/b43legacy/dma.h b/drivers/net/wireless/broadcom/b43legacy/dma.h index b5c1a51db2a4..edd06225b64f 100644 --- a/drivers/net/wireless/broadcom/b43legacy/dma.h +++ b/drivers/net/wireless/broadcom/b43legacy/dma.h @@ -122,8 +122,6 @@ enum b43legacy_dmatype { struct b43legacy_dmaring { /* Kernel virtual base address of the ring memory. */ void *descbase; - /* Meta data about all descriptors. */ - struct b43legacy_dmadesc_meta *meta; /* Cache of TX headers for each slot. * This is to avoid an allocation on each TX. * This is NULL for an RX ring. @@ -161,6 +159,8 @@ struct b43legacy_dmaring { /* Last time we injected a ring overflow. */ unsigned long last_injected_overflow; #endif /* CONFIG_B43LEGACY_DEBUG*/ + /* Meta data about all descriptors. */ + struct b43legacy_dmadesc_meta meta[] __counted_by(nr_slots); }; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 872c48806d09..9d8ce7bb046e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -2112,9 +2112,11 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) struct brcmf_pub *drvr = ifp->drvr; s32 val; s32 err; + s32 okc_enable; const struct brcmf_tlv *rsn_ie; const u8 *ie; u32 ie_len; + bool fwsup_roam; u32 offset; u16 rsn_cap; u32 mfp; @@ -2122,6 +2124,9 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE; profile->is_ft = false; + profile->is_okc = false; + fwsup_roam = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FBT) || + brcmf_feat_is_enabled(ifp, BRCMF_FEAT_OKC); if (!sme->crypto.n_akm_suites) return 0; @@ -2138,6 +2143,8 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) val = WPA_AUTH_UNSPECIFIED; if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else if (fwsup_roam) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_PSK: val = WPA_AUTH_PSK; @@ -2153,11 +2160,15 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) val = WPA2_AUTH_UNSPECIFIED; if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else if (fwsup_roam) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_8021X_SHA256: val = WPA2_AUTH_1X_SHA256; if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else if (fwsup_roam) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_PSK_SHA256: val = WPA2_AUTH_PSK_SHA256; @@ -2170,10 +2181,16 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) profile->is_ft = true; if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else if (fwsup_roam) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_FT_PSK: val = WPA2_AUTH_PSK | WPA2_AUTH_FT; profile->is_ft = true; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; + else if (fwsup_roam) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_WFA_DPP: val = WFA_AUTH_DPP; @@ -2204,8 +2221,22 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X) brcmf_dbg(INFO, "using 1X offload\n"); + + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X || + profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM) { + err = brcmf_fil_bsscfg_int_get(ifp, "okc_enable", + &okc_enable); + if (err) { + bphy_err(drvr, "get okc_enable failed (%d)\n", err); + } else { + brcmf_dbg(INFO, "okc_enable (%d)\n", okc_enable); + profile->is_okc = okc_enable; + } + } if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) brcmf_dbg(INFO, "using SAE offload\n"); + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM) + brcmf_dbg(INFO, "using roaming offload\n"); if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) goto skip_mfp_config; @@ -2494,13 +2525,14 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, if (sme->crypto.psk && !is_sae_akm && profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) { - if (WARN_ON(profile->use_fwsup != - BRCMF_PROFILE_FWSUP_NONE)) { + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_NONE) { + brcmf_dbg(INFO, "using PSK offload\n"); + profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; + } else if (WARN_ON(profile->use_fwsup != + BRCMF_PROFILE_FWSUP_PSK)) { err = -EINVAL; goto done; } - brcmf_dbg(INFO, "using PSK offload\n"); - profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; } if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { /* enable firmware supplicant for this interface */ @@ -5953,17 +5985,29 @@ static int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev, const struct cfg80211_pmk_conf *conf) { struct brcmf_if *ifp; + struct brcmf_pub *drvr; + int ret; brcmf_dbg(TRACE, "enter\n"); - /* expect using firmware supplicant for 1X */ ifp = netdev_priv(dev); - if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X)) + drvr = ifp->drvr; + if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X && + !ifp->vif->profile.is_ft && + !ifp->vif->profile.is_okc)) return -EINVAL; if (conf->pmk_len > BRCMF_WSEC_MAX_PSK_LEN) return -ERANGE; + if (ifp->vif->profile.is_okc) { + ret = brcmf_fil_iovar_data_set(ifp, "okc_info_pmk", + conf->pmk, conf->pmk_len); + if (ret < 0) + bphy_err(drvr, "okc_info_pmk iovar failed: ret=%d\n", + ret); + } + return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len); } @@ -6422,6 +6466,47 @@ static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg, return err; } +static bool brcmf_has_pmkid(const u8 *parse, u32 len) +{ + const struct brcmf_tlv *rsn_ie; + const u8 *ie; + u32 ie_len; + u32 offset; + u16 count; + + if (!parse) + return false; + + rsn_ie = brcmf_parse_tlvs(parse, len, WLAN_EID_RSN); + if (!rsn_ie) + return false; + + ie = (const u8 *)rsn_ie; + ie_len = rsn_ie->len + TLV_HDR_LEN; + + offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN; + if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) + return false; + + count = ie[offset] + (ie[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN + count * WPA_IE_MIN_OUI_LEN; + if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) + return false; + + count = ie[offset] + (ie[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN + count * WPA_IE_MIN_OUI_LEN; + if (offset + RSN_CAP_LEN >= ie_len) + return false; + + offset += RSN_CAP_LEN; + if (offset + RSN_PMKID_COUNT_LEN > ie_len) + return false; + + count = ie[offset] + (ie[offset + 1] << 8); + + return count > 0; +} + static s32 brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, @@ -6436,6 +6521,7 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, struct brcmf_bss_info_le *bi; struct brcmu_chan ch; struct cfg80211_roam_info roam_info = {}; + bool authorized = false; u32 freq; s32 err = 0; u8 *buf; @@ -6482,13 +6568,17 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, roam_info.resp_ie = conn_info->resp_ie; roam_info.resp_ie_len = conn_info->resp_ie_len; - cfg80211_roamed(ndev, &roam_info, GFP_KERNEL); - brcmf_dbg(CONN, "Report roaming result\n"); + if ((profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X || + profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM) && + (brcmf_has_pmkid(roam_info.req_ie, roam_info.req_ie_len) || + profile->is_ft || profile->is_okc)) + authorized = true; - if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && profile->is_ft) { - cfg80211_port_authorized(ndev, profile->bssid, NULL, 0, GFP_KERNEL); - brcmf_dbg(CONN, "Report port authorized\n"); - } + cfg80211_roamed(ndev, &roam_info, GFP_KERNEL); + if (authorized) + cfg80211_port_authorized(ndev, profile->bssid, NULL, 0, + GFP_KERNEL); + brcmf_dbg(CONN, "Report roaming result\n"); set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); brcmf_dbg(TRACE, "Exit\n"); @@ -6504,6 +6594,7 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); struct cfg80211_connect_resp_params conn_params; + bool authorized; brcmf_dbg(TRACE, "Enter\n"); @@ -6522,13 +6613,23 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &ifp->vif->sme_state); conn_params.status = WLAN_STATUS_AUTH_TIMEOUT; + bphy_err(ifp->drvr, "connect failed: event %u status %u reason %u\n", + e->event_code, e->status, e->reason); } conn_params.links[0].bssid = profile->bssid; conn_params.req_ie = conn_info->req_ie; conn_params.req_ie_len = conn_info->req_ie_len; conn_params.resp_ie = conn_info->resp_ie; conn_params.resp_ie_len = conn_info->resp_ie_len; + authorized = completed && + (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X || + profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM) && + brcmf_has_pmkid(conn_params.req_ie, + conn_params.req_ie_len); cfg80211_connect_done(ndev, &conn_params, GFP_KERNEL); + if (authorized) + cfg80211_port_authorized(ndev, profile->bssid, NULL, 0, + GFP_KERNEL); brcmf_dbg(CONN, "Report connect result - connection %s\n", completed ? "succeeded" : "failed"); } @@ -7731,6 +7832,10 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) } if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE_EXT)) wiphy->features |= NL80211_FEATURE_SAE; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FBT) || + brcmf_feat_is_enabled(ifp, BRCMF_FEAT_OKC)) + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD); wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 63e534523f51..1aa99390a951 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -127,7 +127,8 @@ enum brcmf_profile_fwsup { BRCMF_PROFILE_FWSUP_NONE, BRCMF_PROFILE_FWSUP_PSK, BRCMF_PROFILE_FWSUP_1X, - BRCMF_PROFILE_FWSUP_SAE + BRCMF_PROFILE_FWSUP_SAE, + BRCMF_PROFILE_FWSUP_ROAM }; /** @@ -164,6 +165,7 @@ enum brcmf_mgmt_tx_status { * @bssid: bssid of joined/joining ibss. * @sec: security information. * @key: key information + * @is_okc: OKC is used for current connection. */ struct brcmf_cfg80211_profile { u8 bssid[ETH_ALEN]; @@ -172,6 +174,7 @@ struct brcmf_cfg80211_profile { enum brcmf_profile_fwsup use_fwsup; u16 use_fwauth; bool is_ft; + bool is_okc; }; /** diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index 488364ef8ff2..cc19e5e7ebf5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -45,6 +45,8 @@ static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = { { BRCMF_FEAT_SAE, "sae " }, { BRCMF_FEAT_FWAUTH, "idauth" }, { BRCMF_FEAT_SAE_EXT, "sae_ext" }, + { BRCMF_FEAT_FBT, "fbt " }, + { BRCMF_FEAT_OKC, "okc" }, }; #ifdef DEBUG diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h index 31f8695ca417..8165a286b630 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -59,7 +59,9 @@ BRCMF_FEAT_DEF(SCAN_V2) \ BRCMF_FEAT_DEF(PMKID_V2) \ BRCMF_FEAT_DEF(PMKID_V3) \ - BRCMF_FEAT_DEF(SAE_EXT) + BRCMF_FEAT_DEF(SAE_EXT) \ + BRCMF_FEAT_DEF(FBT) \ + BRCMF_FEAT_DEF(OKC) /* * Quirks: diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c index 96102d2af2cf..c45c47337509 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c @@ -7,7 +7,8 @@ #include "hcmd.h" static void -iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta, +iwl_mld_reorder_release_frames(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta, struct napi_struct *napi, struct iwl_mld_baid_data *baid_data, struct iwl_mld_reorder_buffer *reorder_buf, @@ -32,7 +33,7 @@ iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta, while ((skb = __skb_dequeue(skb_list))) { iwl_mld_pass_packet_to_mac80211(mld, napi, skb, reorder_buf->queue, - sta); + link_sta); reorder_buf->num_stored--; } } @@ -74,7 +75,7 @@ static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld, reorder_buf = &ba_data->reorder_buf[queue]; - iwl_mld_reorder_release_frames(mld, link_sta->sta, napi, ba_data, + iwl_mld_reorder_release_frames(mld, link_sta, napi, ba_data, reorder_buf, nssn); out_unlock: rcu_read_unlock(); @@ -180,7 +181,7 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue, reorder_buf = &ba_data->reorder_buf[queue]; /* release all frames that are in the reorder buffer to the stack */ - iwl_mld_reorder_release_frames(mld, link_sta->sta, NULL, + iwl_mld_reorder_release_frames(mld, link_sta, NULL, ba_data, reorder_buf, ieee80211_sn_add(reorder_buf->head_sn, ba_data->buf_size)); @@ -193,7 +194,7 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue, */ enum iwl_mld_reorder_result iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, - int queue, struct ieee80211_sta *sta, + int queue, struct ieee80211_link_sta *link_sta, struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc) { struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb); @@ -228,12 +229,12 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, return IWL_MLD_PASS_SKB; /* no sta yet */ - if (IWL_FW_CHECK(mld, !sta, + if (IWL_FW_CHECK(mld, !link_sta, "Got valid BAID without a valid station assigned - %d\n", baid)) return IWL_MLD_PASS_SKB; - mld_sta = iwl_mld_sta_from_mac80211(sta); + mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); /* not a data packet */ if (!ieee80211_is_data_qos(hdr->frame_control) || @@ -324,7 +325,7 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, * will be released when the frame release notification arrives. */ if (!amsdu || last_subframe) - iwl_mld_reorder_release_frames(mld, sta, napi, baid_data, + iwl_mld_reorder_release_frames(mld, link_sta, napi, baid_data, buffer, nssn); else if (buffer->num_stored == 1) buffer->head_sn = nssn; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.h b/drivers/net/wireless/intel/iwlwifi/mld/agg.h index 651c80d1c7cd..c6cd5fa219be 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/agg.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024, 2026 Intel Corporation */ #ifndef __iwl_agg_h__ #define __iwl_agg_h__ @@ -106,7 +106,7 @@ int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta, enum iwl_mld_reorder_result iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, - int queue, struct ieee80211_sta *sta, + int queue, struct ieee80211_link_sta *link_sta, struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc); void iwl_mld_handle_frame_release_notif(struct iwl_mld *mld, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c index 269439d789f4..02bdf6ccbcdc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c @@ -47,7 +47,8 @@ iwl_mld_fill_phy_data_from_mpdu(struct iwl_mld *mld, } static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb, - int queue, struct ieee80211_sta *sta) + int queue, + struct ieee80211_link_sta *link_sta) { struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb); struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb); @@ -71,13 +72,13 @@ static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb, return 0; /* if we are here - this for sure is either CCMP or GCMP */ - if (!sta) { + if (!link_sta) { IWL_DEBUG_DROP(mld, "expected hw-decrypted unicast frame for station\n"); return -1; } - mld_sta = iwl_mld_sta_from_mac80211(sta); + mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); extiv = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); keyidx = extiv[3] >> 6; @@ -119,17 +120,17 @@ static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb, void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld, struct napi_struct *napi, struct sk_buff *skb, int queue, - struct ieee80211_sta *sta) + struct ieee80211_link_sta *link_sta) { KUNIT_STATIC_STUB_REDIRECT(iwl_mld_pass_packet_to_mac80211, - mld, napi, skb, queue, sta); + mld, napi, skb, queue, link_sta); - if (unlikely(iwl_mld_check_pn(mld, skb, queue, sta))) { + if (unlikely(iwl_mld_check_pn(mld, skb, queue, link_sta))) { kfree_skb(skb); return; } - ieee80211_rx_napi(mld->hw, sta, skb, napi); + ieee80211_rx_napi(mld->hw, link_sta, skb, napi); } EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_pass_packet_to_mac80211); @@ -1728,7 +1729,7 @@ static void iwl_mld_update_last_rx_timestamp(struct iwl_mld *mld, u8 baid) * Sets *drop to true if the packet should be dropped. * Returns the station if found, or NULL otherwise. */ -static struct ieee80211_sta * +static struct ieee80211_link_sta * iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr, struct sk_buff *skb, const struct iwl_rx_mpdu_desc *mpdu_desc, @@ -1764,11 +1765,6 @@ iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr, rx_status = IEEE80211_SKB_RXCB(skb); - if (link_sta && sta->valid_links) { - rx_status->link_valid = true; - rx_status->link_id = link_sta->link_id; - } - /* fill checksum */ if (ieee80211_is_data(hdr->frame_control) && pkt->len_n_flags & cpu_to_le32(FH_RSCSR_RPA_EN)) { @@ -1803,10 +1799,10 @@ iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr, queue); } - return sta; + return link_sta; } -static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta, +static int iwl_mld_rx_mgmt_prot(struct ieee80211_link_sta *link_sta, struct ieee80211_hdr *hdr, struct ieee80211_rx_status *rx_status, u32 mpdu_status, @@ -1820,7 +1816,6 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta, struct ieee80211_key_conf *key; const u8 *frame = (void *)hdr; const u8 *mmie; - u8 link_id; if ((mpdu_status & IWL_RX_MPDU_STATUS_SEC_MASK) == IWL_RX_MPDU_STATUS_SEC_NONE) @@ -1836,10 +1831,10 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta, if (!ieee80211_is_beacon(hdr->frame_control)) return 0; - if (!sta) + if (!link_sta) return -1; - mld_sta = iwl_mld_sta_from_mac80211(sta); + mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif); /* key mismatch - will also report !MIC_OK but we shouldn't count it */ @@ -1853,8 +1848,7 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta, return 0; } - link_id = rx_status->link_valid ? rx_status->link_id : 0; - link = rcu_dereference(mld_vif->link[link_id]); + link = rcu_dereference(mld_vif->link[link_sta->link_id]); if (WARN_ON_ONCE(!link)) return -1; @@ -1905,7 +1899,7 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta, } static int iwl_mld_rx_crypto(struct iwl_mld *mld, - struct ieee80211_sta *sta, + struct ieee80211_link_sta *link_sta, struct ieee80211_hdr *hdr, struct ieee80211_rx_status *rx_status, struct iwl_rx_mpdu_desc *desc, int queue, @@ -1915,7 +1909,7 @@ static int iwl_mld_rx_crypto(struct iwl_mld *mld, if (unlikely(ieee80211_is_mgmt(hdr->frame_control) && !ieee80211_has_protected(hdr->frame_control))) - return iwl_mld_rx_mgmt_prot(sta, hdr, rx_status, status, + return iwl_mld_rx_mgmt_prot(link_sta, hdr, rx_status, status, le16_to_cpu(desc->mpdu_len)); if (!ieee80211_has_protected(hdr->frame_control) || @@ -2023,7 +2017,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_mld_rx_phy_data phy_data = {}; struct iwl_rx_mpdu_desc *mpdu_desc = (void *)pkt->data; - struct ieee80211_sta *sta; + struct ieee80211_link_sta *link_sta; struct ieee80211_hdr *hdr; struct sk_buff *skb; size_t mpdu_desc_size = sizeof(*mpdu_desc); @@ -2086,7 +2080,8 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, rcu_read_lock(); - sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue, &drop); + link_sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue, + &drop); if (drop) goto drop; @@ -2127,7 +2122,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, iwl_mld_rx_fill_status(mld, link_id, hdr, skb, &phy_data); - if (iwl_mld_rx_crypto(mld, sta, hdr, rx_status, mpdu_desc, queue, + if (iwl_mld_rx_crypto(mld, link_sta, hdr, rx_status, mpdu_desc, queue, le32_to_cpu(pkt->len_n_flags), &crypto_len)) goto drop; @@ -2140,7 +2135,8 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, if (iwl_mld_time_sync_frame(mld, skb, hdr->addr2)) goto out; - reorder_res = iwl_mld_reorder(mld, napi, queue, sta, skb, mpdu_desc); + reorder_res = iwl_mld_reorder(mld, napi, queue, link_sta, skb, + mpdu_desc); switch (reorder_res) { case IWL_MLD_PASS_SKB: break; @@ -2153,7 +2149,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, goto drop; } - iwl_mld_pass_packet_to_mac80211(mld, napi, skb, queue, sta); + iwl_mld_pass_packet_to_mac80211(mld, napi, skb, queue, link_sta); goto out; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.h b/drivers/net/wireless/intel/iwlwifi/mld/rx.h index 573b89c3c9c6..b08c5ade3db8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/rx.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.h @@ -69,7 +69,7 @@ void iwl_mld_handle_rsc_notif(struct iwl_mld *mld, void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld, struct napi_struct *napi, struct sk_buff *skb, int queue, - struct ieee80211_sta *sta); + struct ieee80211_link_sta *link_sta); void iwl_mld_handle_phy_air_sniffer_notif(struct iwl_mld *mld, struct napi_struct *napi, diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c index 29b0248cec3d..e9efe2996f07 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c @@ -2,7 +2,7 @@ /* * KUnit tests for channel helper functions * - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include #include @@ -441,7 +441,7 @@ static void fake_iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld, struct napi_struct *napi, struct sk_buff *skb, int queue, - struct ieee80211_sta *sta) + struct ieee80211_link_sta *link_sta) { __skb_queue_tail(&g_released_skbs, skb); g_num_released_skbs++; @@ -630,7 +630,8 @@ static void test_reorder_buffer(struct kunit *test) mpdu_desc = setup_mpdu_desc(); rcu_read_lock(); - reorder_res = iwl_mld_reorder(mld, NULL, QUEUE, sta, skb, mpdu_desc); + reorder_res = iwl_mld_reorder(mld, NULL, QUEUE, &sta->deflink, skb, + mpdu_desc); rcu_read_unlock(); KUNIT_ASSERT_EQ(test, reorder_res, param->expected.reorder_res); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index ab1eb2eb0c3c..8c197a090c2b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -90,7 +90,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, fraglen, rxb->truesize); } - ieee80211_rx_napi(mvm->hw, sta, skb, napi); + ieee80211_rx_napi(mvm->hw, sta ? &sta->deflink : NULL, skb, napi); } /* diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 7f0b4f5daa21..8c4009d3a00c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -243,7 +243,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, return; } - ieee80211_rx_napi(mvm->hw, sta, skb, napi); + ieee80211_rx_napi(mvm->hw, sta ? &sta->deflink : NULL, skb, napi); } static bool iwl_mvm_used_average_energy(struct iwl_mvm *mvm, @@ -2528,7 +2528,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, } rcu_read_lock(); - ieee80211_rx_napi(mvm->hw, sta, skb, napi); + ieee80211_rx_napi(mvm->hw, sta ? &sta->deflink : NULL, skb, napi); rcu_read_unlock(); } diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c index 5cc0c5cac257..74870c9bd091 100644 --- a/drivers/net/wireless/marvell/libertas/if_usb.c +++ b/drivers/net/wireless/marvell/libertas/if_usb.c @@ -798,10 +798,18 @@ static int check_fwfile_format(const uint8_t *data, uint32_t totlen) exit = len = 0; do { - struct fwheader *fwh = (void *)data; + struct fwheader *fwh; + + if (totlen - len < sizeof(*fwh)) + break; + + fwh = (void *)data; bincmd = le32_to_cpu(fwh->dnldcmd); blksize = le32_to_cpu(fwh->datalength); + if (blksize > totlen - len - sizeof(*fwh)) + break; + switch (bincmd) { case FW_HAS_DATA_TO_RECV: offset = sizeof(struct fwheader) + blksize; diff --git a/drivers/net/wireless/marvell/libertas/mesh.c b/drivers/net/wireless/marvell/libertas/mesh.c index fced7485d015..686b2e18a0e9 100644 --- a/drivers/net/wireless/marvell/libertas/mesh.c +++ b/drivers/net/wireless/marvell/libertas/mesh.c @@ -65,19 +65,6 @@ static int __lbs_mesh_config_send(struct lbs_private *priv, return ret; } -static int lbs_mesh_config_send(struct lbs_private *priv, - struct cmd_ds_mesh_config *cmd, - uint16_t action, uint16_t type) -{ - int ret; - - if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG)) - return -EOPNOTSUPP; - - ret = __lbs_mesh_config_send(priv, cmd, action, type); - return ret; -} - /* This function is the CMD_MESH_CONFIG legacy function. It only handles the * START and STOP actions. The extended actions supported by CMD_MESH_CONFIG * are all handled by preparing a struct cmd_ds_mesh_config and passing it to @@ -333,497 +320,6 @@ static const struct attribute_group lbs_mesh_attr_group = { .attrs = lbs_mesh_sysfs_entries, }; - -/*************************************************************************** - * Persistent configuration support - */ - -static int mesh_get_default_parameters(struct device *dev, - struct mrvl_mesh_defaults *defs) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - int ret; - - memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET, - CMD_TYPE_MESH_GET_DEFAULTS); - - if (ret) - return -EOPNOTSUPP; - - memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults)); - - return 0; -} - -/** - * bootflag_show - Get function for sysfs attribute bootflag - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t bootflag_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return sysfs_emit(buf, "%d\n", le32_to_cpu(defs.bootflag)); -} - -/** - * bootflag_store - Set function for sysfs attribute bootflag - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t bootflag_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - uint32_t datum; - int ret; - - ret = kstrtouint(buf, 10, &datum); - if (ret) - return ret; - if (datum > 1) - return -EINVAL; - - memset(&cmd, 0, sizeof(cmd)); - *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum); - cmd.length = cpu_to_le16(sizeof(uint32_t)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_BOOTFLAG); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * boottime_show - Get function for sysfs attribute boottime - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t boottime_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return sysfs_emit(buf, "%d\n", defs.boottime); -} - -/** - * boottime_store - Set function for sysfs attribute boottime - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t boottime_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - uint32_t datum; - int ret; - - ret = kstrtouint(buf, 10, &datum); - if (ret) - return ret; - if (datum > 255) - return -EINVAL; - - memset(&cmd, 0, sizeof(cmd)); - - /* A too small boot time will result in the device booting into - * standalone (no-host) mode before the host can take control of it, - * so the change will be hard to revert. This may be a desired - * feature (e.g to configure a very fast boot time for devices that - * will not be attached to a host), but dangerous. So I'm enforcing a - * lower limit of 20 seconds: remove and recompile the driver if this - * does not work for you. - */ - datum = (datum < 20) ? 20 : datum; - cmd.data[0] = datum; - cmd.length = cpu_to_le16(sizeof(uint8_t)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_BOOTTIME); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * channel_show - Get function for sysfs attribute channel - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t channel_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return sysfs_emit(buf, "%d\n", le16_to_cpu(defs.channel)); -} - -/** - * channel_store - Set function for sysfs attribute channel - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t channel_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - struct cmd_ds_mesh_config cmd; - uint32_t datum; - int ret; - - ret = kstrtouint(buf, 10, &datum); - if (ret) - return ret; - if (datum < 1 || datum > 11) - return -EINVAL; - - memset(&cmd, 0, sizeof(cmd)); - *((__le16 *)&cmd.data[0]) = cpu_to_le16(datum); - cmd.length = cpu_to_le16(sizeof(uint16_t)); - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_DEF_CHANNEL); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * mesh_id_show - Get function for sysfs attribute mesh_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t mesh_id_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) { - dev_err(dev, "inconsistent mesh ID length\n"); - defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN; - } - - memcpy(buf, defs.meshie.val.mesh_id, defs.meshie.val.mesh_id_len); - buf[defs.meshie.val.mesh_id_len] = '\n'; - buf[defs.meshie.val.mesh_id_len + 1] = '\0'; - - return defs.meshie.val.mesh_id_len + 1; -} - -/** - * mesh_id_store - Set function for sysfs attribute mesh_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t mesh_id_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - int len; - int ret; - - if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1) - return -EINVAL; - - memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config)); - ie = (struct mrvl_meshie *) &cmd.data[0]; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - - len = count - 1; - memcpy(ie->val.mesh_id, buf, len); - /* SSID len */ - ie->val.mesh_id_len = len; - /* IE len */ - ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * protocol_id_show - Get function for sysfs attribute protocol_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t protocol_id_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return sysfs_emit(buf, "%d\n", defs.meshie.val.active_protocol_id); -} - -/** - * protocol_id_store - Set function for sysfs attribute protocol_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t protocol_id_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - uint32_t datum; - int ret; - - ret = kstrtouint(buf, 10, &datum); - if (ret) - return ret; - if (datum > 255) - return -EINVAL; - - memset(&cmd, 0, sizeof(cmd)); - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - ie = (struct mrvl_meshie *) &cmd.data[0]; - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - /* update protocol id */ - ie->val.active_protocol_id = datum; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * metric_id_show - Get function for sysfs attribute metric_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t metric_id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return sysfs_emit(buf, "%d\n", defs.meshie.val.active_metric_id); -} - -/** - * metric_id_store - Set function for sysfs attribute metric_id - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t metric_id_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - ie = (struct mrvl_meshie *) &cmd.data[0]; - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - /* update metric id */ - ie->val.active_metric_id = datum; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - -/** - * capability_show - Get function for sysfs attribute capability - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer where data will be returned - */ -static ssize_t capability_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mrvl_mesh_defaults defs; - int ret; - - ret = mesh_get_default_parameters(dev, &defs); - - if (ret) - return ret; - - return sysfs_emit(buf, "%d\n", defs.meshie.val.mesh_capability); -} - -/** - * capability_store - Set function for sysfs attribute capability - * @dev: the &struct device - * @attr: device attributes - * @buf: buffer that contains new attribute value - * @count: size of buffer - */ -static ssize_t capability_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct cmd_ds_mesh_config cmd; - struct mrvl_mesh_defaults defs; - struct mrvl_meshie *ie; - struct lbs_private *priv = to_net_dev(dev)->ml_priv; - uint32_t datum; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%d", &datum); - if ((ret != 1) || (datum > 255)) - return -EINVAL; - - /* fetch all other Information Element parameters */ - ret = mesh_get_default_parameters(dev, &defs); - - cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie)); - - /* transfer IE elements */ - ie = (struct mrvl_meshie *) &cmd.data[0]; - memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie)); - /* update value */ - ie->val.mesh_capability = datum; - - ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET, - CMD_TYPE_MESH_SET_MESH_IE); - if (ret) - return ret; - - return strlen(buf); -} - - -static DEVICE_ATTR_RW(bootflag); -static DEVICE_ATTR_RW(boottime); -static DEVICE_ATTR_RW(channel); -static DEVICE_ATTR_RW(mesh_id); -static DEVICE_ATTR_RW(protocol_id); -static DEVICE_ATTR_RW(metric_id); -static DEVICE_ATTR_RW(capability); - -static struct attribute *boot_opts_attrs[] = { - &dev_attr_bootflag.attr, - &dev_attr_boottime.attr, - &dev_attr_channel.attr, - NULL -}; - -static const struct attribute_group boot_opts_group = { - .name = "boot_options", - .attrs = boot_opts_attrs, -}; - -static struct attribute *mesh_ie_attrs[] = { - &dev_attr_mesh_id.attr, - &dev_attr_protocol_id.attr, - &dev_attr_metric_id.attr, - &dev_attr_capability.attr, - NULL -}; - -static const struct attribute_group mesh_ie_group = { - .name = "mesh_ie", - .attrs = mesh_ie_attrs, -}; - - /*************************************************************************** * Initializing and starting, stopping mesh */ @@ -1019,8 +515,6 @@ static int lbs_add_mesh(struct lbs_private *priv) mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST; mesh_dev->sysfs_groups[0] = &lbs_mesh_attr_group; - mesh_dev->sysfs_groups[1] = &boot_opts_group; - mesh_dev->sysfs_groups[2] = &mesh_ie_group; /* Register virtual mesh interface */ ret = register_netdev(mesh_dev); diff --git a/drivers/net/wireless/marvell/libertas_tf/if_usb.c b/drivers/net/wireless/marvell/libertas_tf/if_usb.c index b85c6d783bf7..b9d1a33bddfc 100644 --- a/drivers/net/wireless/marvell/libertas_tf/if_usb.c +++ b/drivers/net/wireless/marvell/libertas_tf/if_usb.c @@ -769,10 +769,18 @@ static int check_fwfile_format(const u8 *data, u32 totlen) exit = len = 0; do { - struct fwheader *fwh = (void *) data; + struct fwheader *fwh; + + if (totlen - len < sizeof(*fwh)) + break; + + fwh = (void *)data; bincmd = le32_to_cpu(fwh->dnldcmd); blksize = le32_to_cpu(fwh->datalength); + if (blksize > totlen - len - sizeof(*fwh)) + break; + switch (bincmd) { case FW_HAS_DATA_TO_RECV: offset = sizeof(struct fwheader) + blksize; diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c index 9deb47f22a61..06b005525078 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c @@ -213,7 +213,6 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, list_del(&tbl->list); spin_unlock_bh(&priv->rx_reorder_tbl_lock); - kfree(tbl->rx_reorder_ptr); kfree(tbl); spin_lock_bh(&priv->adapter->rx_proc_lock); @@ -329,7 +328,6 @@ static void mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, int tid, int win_size, int seq_num) { - int i; struct mwifiex_rx_reorder_tbl *tbl, *new_node; u16 last_seq = 0; struct mwifiex_sta_node *node; @@ -344,10 +342,12 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, return; } /* if !tbl then create one */ - new_node = kzalloc_obj(struct mwifiex_rx_reorder_tbl); + new_node = kzalloc_flex(*new_node, rx_reorder_ptr, win_size); if (!new_node) return; + new_node->win_size = win_size; + INIT_LIST_HEAD(&new_node->list); new_node->tid = tid; memcpy(new_node->ta, ta, ETH_ALEN); @@ -381,26 +381,12 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, new_node->flags |= RXREOR_INIT_WINDOW_SHIFT; } - new_node->win_size = win_size; - - new_node->rx_reorder_ptr = kcalloc(win_size, sizeof(void *), - GFP_KERNEL); - if (!new_node->rx_reorder_ptr) { - kfree(new_node); - mwifiex_dbg(priv->adapter, ERROR, - "%s: failed to alloc reorder_ptr\n", __func__); - return; - } - new_node->timer_context.ptr = new_node; new_node->timer_context.priv = priv; new_node->timer_context.timer_is_set = false; timer_setup(&new_node->timer_context.timer, mwifiex_flush_data, 0); - for (i = 0; i < win_size; ++i) - new_node->rx_reorder_ptr[i] = NULL; - spin_lock_bh(&priv->rx_reorder_tbl_lock); list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr); spin_unlock_bh(&priv->rx_reorder_tbl_lock); diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index 936939ea9c47..d7f20ec0bb3e 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -4905,7 +4905,14 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) country_code = mwifiex_11d_code_2_region( adapter->region_code); + + /* If the reg hint in ROM conflicts with platform + * configuration, it should be ignored, so the + * platform regulatory domain can be used. + */ if (country_code && + !device_property_read_bool(adapter->dev, + "marvell,invalid-reg-hint-in-rom") && regulatory_hint(wiphy, country_code)) mwifiex_dbg(priv->adapter, ERROR, "regulatory_hint() failed\n"); diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 27559e2ddc31..8e6238c01aa6 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -708,10 +708,10 @@ struct mwifiex_rx_reorder_tbl { int init_win; int start_win; int win_size; - void **rx_reorder_ptr; struct reorder_tmr_cnxt timer_context; u8 amsdu; u8 flags; + struct sk_buff *rx_reorder_ptr[] __counted_by(win_size); }; struct mwifiex_bss_prio_node { diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index abbe65cbcd89..cda5c5d51aab 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -1255,11 +1255,12 @@ EXPORT_SYMBOL(mt76_rx_signal); static void mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb, struct ieee80211_hw **hw, - struct ieee80211_sta **sta) + struct ieee80211_link_sta **link_sta) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb); struct mt76_rx_status mstat; + struct ieee80211_sta *sta; mstat = *((struct mt76_rx_status *)skb->cb); memset(status, 0, sizeof(*status)); @@ -1301,12 +1302,14 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb, memcpy(status->chain_signal, mstat.chain_signal, sizeof(mstat.chain_signal)); - if (mstat.wcid) { - status->link_valid = mstat.wcid->link_valid; - status->link_id = mstat.wcid->link_id; - } + sta = wcid_to_sta(mstat.wcid); + if (!sta) + *link_sta = NULL; + else if (mstat.wcid->link_valid) + *link_sta = rcu_dereference(sta->link[mstat.wcid->link_id]); + else + *link_sta = &sta->deflink; - *sta = wcid_to_sta(mstat.wcid); *hw = mt76_phy_hw(dev, mstat.phy_idx); } @@ -1530,7 +1533,7 @@ mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb) void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames, struct napi_struct *napi) { - struct ieee80211_sta *sta; + struct ieee80211_link_sta *link_sta; struct ieee80211_hw *hw; struct sk_buff *skb, *tmp; LIST_HEAD(list); @@ -1541,8 +1544,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames, mt76_check_ccmp_pn(skb); skb_shinfo(skb)->frag_list = NULL; - mt76_rx_convert(dev, skb, &hw, &sta); - ieee80211_rx_list(hw, sta, skb, &list); + mt76_rx_convert(dev, skb, &hw, &link_sta); + ieee80211_rx_list(hw, link_sta, skb, &list); /* subsequent amsdu frames */ while (nskb) { @@ -1550,8 +1553,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames, nskb = nskb->next; skb->next = NULL; - mt76_rx_convert(dev, skb, &hw, &sta); - ieee80211_rx_list(hw, sta, skb, &list); + mt76_rx_convert(dev, skb, &hw, &link_sta); + ieee80211_rx_list(hw, link_sta, skb, &list); } } spin_unlock(&dev->rx_lock); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c index cabeef0dde45..d4635b531481 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c @@ -19,84 +19,50 @@ #include "rt2x00.h" #include "rt2x00pci.h" -/* - * PCI driver handlers. - */ -static void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev) -{ - kfree(rt2x00dev->rf); - rt2x00dev->rf = NULL; - - kfree(rt2x00dev->eeprom); - rt2x00dev->eeprom = NULL; - - if (rt2x00dev->csr.base) { - iounmap(rt2x00dev->csr.base); - rt2x00dev->csr.base = NULL; - } -} - -static int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev) -{ - struct pci_dev *pci_dev = to_pci_dev(rt2x00dev->dev); - - rt2x00dev->csr.base = pci_ioremap_bar(pci_dev, 0); - if (!rt2x00dev->csr.base) - goto exit; - - rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); - if (!rt2x00dev->eeprom) - goto exit; - - rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); - if (!rt2x00dev->rf) - goto exit; - - return 0; - -exit: - rt2x00_probe_err("Failed to allocate registers\n"); - - rt2x00pci_free_reg(rt2x00dev); - - return -ENOMEM; -} - int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) { - struct ieee80211_hw *hw; struct rt2x00_dev *rt2x00dev; + struct ieee80211_hw *hw; + void __iomem *base; + __le16 *eeprom; int retval; u16 chip; + u32 *rf; - retval = pci_enable_device(pci_dev); + retval = pcim_enable_device(pci_dev); if (retval) { rt2x00_probe_err("Enable device failed\n"); return retval; } - retval = pci_request_regions(pci_dev, pci_name(pci_dev)); - if (retval) { - rt2x00_probe_err("PCI request regions failed\n"); - goto exit_disable_device; + base = pcim_iomap_region(pci_dev, 0, pci_name(pci_dev)); + if (IS_ERR(base)) { + rt2x00_probe_err("PCI iomap region failed\n"); + return PTR_ERR(base); } pci_set_master(pci_dev); - if (pci_set_mwi(pci_dev)) + if (pcim_set_mwi(pci_dev)) rt2x00_probe_err("MWI not available\n"); if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) { rt2x00_probe_err("PCI DMA not supported\n"); - retval = -EIO; - goto exit_release_regions; + return -EIO; } + eeprom = devm_kzalloc(&pci_dev->dev, ops->eeprom_size, GFP_KERNEL); + if (!eeprom) + return -ENOMEM; + + rf = devm_kzalloc(&pci_dev->dev, ops->rf_size, GFP_KERNEL); + if (!rf) + return -ENOMEM; + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); if (!hw) { rt2x00_probe_err("Failed to allocate hardware\n"); - retval = -ENOMEM; - goto exit_release_regions; + return -ENOMEM; } pci_set_drvdata(pci_dev, hw); @@ -105,18 +71,17 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) rt2x00dev->dev = &pci_dev->dev; rt2x00dev->ops = ops; rt2x00dev->hw = hw; + rt2x00dev->csr.base = base; rt2x00dev->irq = pci_dev->irq; rt2x00dev->name = ops->name; + rt2x00dev->eeprom = eeprom; + rt2x00dev->rf = rf; if (pci_is_pcie(pci_dev)) rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); else rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI); - retval = rt2x00pci_alloc_reg(rt2x00dev); - if (retval) - goto exit_free_device; - /* * Because rt3290 chip use different efuse offset to read efuse data. * So before read efuse it need to indicate it is the @@ -127,23 +92,13 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) retval = rt2x00lib_probe_dev(rt2x00dev); if (retval) - goto exit_free_reg; + goto exit_free_device; return 0; -exit_free_reg: - rt2x00pci_free_reg(rt2x00dev); - exit_free_device: ieee80211_free_hw(hw); -exit_release_regions: - pci_clear_mwi(pci_dev); - pci_release_regions(pci_dev); - -exit_disable_device: - pci_disable_device(pci_dev); - return retval; } EXPORT_SYMBOL_GPL(rt2x00pci_probe); @@ -153,19 +108,8 @@ void rt2x00pci_remove(struct pci_dev *pci_dev) struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); struct rt2x00_dev *rt2x00dev = hw->priv; - /* - * Free all allocated data. - */ rt2x00lib_remove_dev(rt2x00dev); - rt2x00pci_free_reg(rt2x00dev); ieee80211_free_hw(hw); - - /* - * Free the PCI device data. - */ - pci_clear_mwi(pci_dev); - pci_disable_device(pci_dev); - pci_release_regions(pci_dev); } EXPORT_SYMBOL_GPL(rt2x00pci_remove); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c index 47e427ea8622..c9fbc3710fd8 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00usb.c @@ -755,55 +755,31 @@ void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev) } EXPORT_SYMBOL_GPL(rt2x00usb_uninitialize); -/* - * USB driver handlers. - */ -static void rt2x00usb_free_reg(struct rt2x00_dev *rt2x00dev) -{ - kfree(rt2x00dev->rf); - rt2x00dev->rf = NULL; - - kfree(rt2x00dev->eeprom); - rt2x00dev->eeprom = NULL; - - kfree(rt2x00dev->csr.cache); - rt2x00dev->csr.cache = NULL; -} - -static int rt2x00usb_alloc_reg(struct rt2x00_dev *rt2x00dev) -{ - rt2x00dev->csr.cache = kzalloc(CSR_CACHE_SIZE, GFP_KERNEL); - if (!rt2x00dev->csr.cache) - goto exit; - - rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); - if (!rt2x00dev->eeprom) - goto exit; - - rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); - if (!rt2x00dev->rf) - goto exit; - - return 0; - -exit: - rt2x00_probe_err("Failed to allocate registers\n"); - - rt2x00usb_free_reg(rt2x00dev); - - return -ENOMEM; -} - int rt2x00usb_probe(struct usb_interface *usb_intf, const struct rt2x00_ops *ops) { struct usb_device *usb_dev = interface_to_usbdev(usb_intf); - struct ieee80211_hw *hw; struct rt2x00_dev *rt2x00dev; + struct ieee80211_hw *hw; + __le16 *eeprom; + void *cache; int retval; + u32 *rf; usb_reset_device(usb_dev); + cache = devm_kzalloc(&usb_intf->dev, CSR_CACHE_SIZE, GFP_KERNEL); + if (!cache) + return -ENOMEM; + + eeprom = devm_kzalloc(&usb_intf->dev, ops->eeprom_size, GFP_KERNEL); + if (!eeprom) + return -ENOMEM; + + rf = devm_kzalloc(&usb_intf->dev, ops->rf_size, GFP_KERNEL); + if (!rf) + return -ENOMEM; + hw = ieee80211_alloc_hw(struct_size(rt2x00dev, anchor, 1), ops->hw); if (!hw) { rt2x00_probe_err("Failed to allocate hardware\n"); @@ -816,16 +792,15 @@ int rt2x00usb_probe(struct usb_interface *usb_intf, rt2x00dev->dev = &usb_intf->dev; rt2x00dev->ops = ops; rt2x00dev->hw = hw; + rt2x00dev->csr.cache = cache; + rt2x00dev->eeprom = eeprom; + rt2x00dev->rf = rf; rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone); INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone); - retval = rt2x00usb_alloc_reg(rt2x00dev); - if (retval) - goto exit_free_device; - init_usb_anchor(rt2x00dev->anchor); retval = rt2x00lib_probe_dev(rt2x00dev); @@ -836,9 +811,6 @@ int rt2x00usb_probe(struct usb_interface *usb_intf, exit_free_anchor: usb_kill_anchored_urbs(rt2x00dev->anchor); - rt2x00usb_free_reg(rt2x00dev); - -exit_free_device: ieee80211_free_hw(hw); usb_set_intfdata(usb_intf, NULL); @@ -855,7 +827,6 @@ void rt2x00usb_disconnect(struct usb_interface *usb_intf) * Free all allocated data. */ rt2x00lib_remove_dev(rt2x00dev); - rt2x00usb_free_reg(rt2x00dev); ieee80211_free_hw(hw); usb_set_intfdata(usb_intf, NULL); diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 95e14df2f137..dacc2d889a46 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3073,10 +3073,8 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_rx_desc_info *desc_info = iter_data->desc_info; struct sk_buff *skb = iter_data->skb; - struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct rtw89_rx_phy_ppdu *phy_ppdu = iter_data->phy_ppdu; - bool is_mld = ieee80211_vif_is_mld(vif); struct ieee80211_bss_conf *bss_conf; struct rtw89_vif_link *rtwvif_link; const u8 *bssid = iter_data->bssid; @@ -3110,11 +3108,6 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, if (!ether_addr_equal(target_bssid, bssid)) goto out; - if (is_mld) { - rx_status->link_valid = true; - rx_status->link_id = rtwvif_link->link_id; - } - bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); pkt_stat = &bb->cur_pkt_stat; diff --git a/drivers/net/wireless/silabs/wfx/bh.c b/drivers/net/wireless/silabs/wfx/bh.c index 21dfdcf9cc27..0071659bc9c9 100644 --- a/drivers/net/wireless/silabs/wfx/bh.c +++ b/drivers/net/wireless/silabs/wfx/bh.c @@ -284,10 +284,10 @@ void wfx_bh_request_tx(struct wfx_dev *wdev) } /* If IRQ is not available, this function allow to manually poll the control register and simulate - * an IRQ ahen an event happened. + * an IRQ when an event happened. * - * Note that the device has a bug: If an IRQ raise while host read control register, the IRQ is - * lost. So, use this function carefully (only duing device initialisation). + * Note that the device has a bug: If an IRQ raise while host read control register, the device is + * lost. Unfortunately, this is the only way to initialize the SDIO with out-of-band IRQ. */ void wfx_bh_poll_irq(struct wfx_dev *wdev) { diff --git a/drivers/net/wireless/silabs/wfx/bus_sdio.c b/drivers/net/wireless/silabs/wfx/bus_sdio.c index ab0793b9908f..1ca2cc262feb 100644 --- a/drivers/net/wireless/silabs/wfx/bus_sdio.c +++ b/drivers/net/wireless/silabs/wfx/bus_sdio.c @@ -279,6 +279,7 @@ static int wfx_sdio_probe(struct sdio_func *func, const struct sdio_device_id *i goto sdio_release; } + bus->core->poll_irq = !!bus->of_irq; ret = wfx_probe(bus->core); if (ret) goto sdio_release; diff --git a/drivers/net/wireless/silabs/wfx/main.c b/drivers/net/wireless/silabs/wfx/main.c index dda36e41eed1..16d5b51f6f03 100644 --- a/drivers/net/wireless/silabs/wfx/main.c +++ b/drivers/net/wireless/silabs/wfx/main.c @@ -362,7 +362,6 @@ int wfx_probe(struct wfx_dev *wdev) */ gpio_saved = wdev->pdata.gpio_wakeup; wdev->pdata.gpio_wakeup = NULL; - wdev->poll_irq = true; wdev->bh_wq = alloc_workqueue("wfx_bh_wq", WQ_HIGHPRI | WQ_PERCPU, 0); if (!wdev->bh_wq) @@ -370,16 +369,24 @@ int wfx_probe(struct wfx_dev *wdev) wfx_bh_register(wdev); + if (!wdev->poll_irq) { + err = wdev->hwbus_ops->irq_subscribe(wdev->hwbus_priv); + if (err) + goto bh_unregister; + } + err = wfx_init_device(wdev); if (err) - goto bh_unregister; + goto irq_unsubscribe; + + if (wdev->poll_irq) + wfx_bh_poll_irq(wdev); - wfx_bh_poll_irq(wdev); err = wait_for_completion_timeout(&wdev->firmware_ready, 1 * HZ); if (err == 0) { dev_err(wdev->dev, "timeout while waiting for startup indication\n"); err = -ETIMEDOUT; - goto bh_unregister; + goto irq_unsubscribe; } /* FIXME: fill wiphy::hw_version */ @@ -399,12 +406,13 @@ int wfx_probe(struct wfx_dev *wdev) dev_err(wdev->dev, "unsupported firmware API version (expect 1 while firmware returns %d)\n", wdev->hw_caps.api_version_major); err = -EOPNOTSUPP; - goto bh_unregister; + goto irq_unsubscribe; } if (wdev->hw_caps.link_mode == SEC_LINK_ENFORCED) { dev_err(wdev->dev, "chip require secure_link, but can't negotiate it\n"); - goto bh_unregister; + err = -EOPNOTSUPP; + goto irq_unsubscribe; } if (wdev->hw_caps.region_sel_mode) { @@ -420,12 +428,14 @@ int wfx_probe(struct wfx_dev *wdev) dev_dbg(wdev->dev, "sending configuration file %s\n", wdev->pdata.file_pds); err = wfx_send_pdata_pds(wdev); if (err < 0 && err != -ENOENT) - goto bh_unregister; + goto irq_unsubscribe; - wdev->poll_irq = false; - err = wdev->hwbus_ops->irq_subscribe(wdev->hwbus_priv); - if (err) - goto bh_unregister; + if (wdev->poll_irq) { + err = wdev->hwbus_ops->irq_subscribe(wdev->hwbus_priv); + if (err) + goto bh_unregister; + wdev->poll_irq = false; + } err = wfx_hif_use_multi_tx_conf(wdev, true); if (err) @@ -474,9 +484,11 @@ int wfx_probe(struct wfx_dev *wdev) ieee80211_unregister: ieee80211_unregister_hw(wdev->hw); irq_unsubscribe: - wdev->hwbus_ops->irq_unsubscribe(wdev->hwbus_priv); + if (!wdev->poll_irq) + wdev->hwbus_ops->irq_unsubscribe(wdev->hwbus_priv); bh_unregister: wfx_bh_unregister(wdev); + cancel_delayed_work_sync(&wdev->cooling_timeout_work); destroy_workqueue(wdev->bh_wq); return err; } @@ -487,6 +499,7 @@ void wfx_release(struct wfx_dev *wdev) wfx_hif_shutdown(wdev); wdev->hwbus_ops->irq_unsubscribe(wdev->hwbus_priv); wfx_bh_unregister(wdev); + cancel_delayed_work_sync(&wdev->cooling_timeout_work); destroy_workqueue(wdev->bh_wq); } diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.h b/drivers/net/wireless/virtual/mac80211_hwsim.h index a022cd5c0f1c..1d2fd06c585a 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim.h @@ -3,7 +3,7 @@ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211 * Copyright (c) 2008, Jouni Malinen * Copyright (c) 2011, Javier Lopez - * Copyright (C) 2020, 2022-2025 Intel Corporation + * Copyright (C) 2020, 2022-2026 Intel Corporation */ #ifndef __MAC80211_HWSIM_H @@ -162,6 +162,8 @@ enum hwsim_commands { * each radio instead of for the wiphy. * @HWSIM_ATTR_SUPPORT_NAN_DEVICE: support NAN Device virtual interface (flag) * @HWSIM_ATTR_SUPPORT_BACKGROUND_RADAR: background radar/CAC support (flag) + * @HWSIM_ATTR_NO_MONITOR: skip monitor report for this TX status, e.g. in case + * the frame was never reported to any receiver (flag) * @__HWSIM_ATTR_MAX: enum limit */ enum hwsim_attrs { @@ -197,6 +199,7 @@ enum hwsim_attrs { HWSIM_ATTR_MULTI_RADIO, HWSIM_ATTR_SUPPORT_NAN_DEVICE, HWSIM_ATTR_SUPPORT_BACKGROUND_RADAR, + HWSIM_ATTR_NO_MONITOR, __HWSIM_ATTR_MAX, }; #define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index b9446577ff49..d567ed8d45a4 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -895,6 +895,7 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { [HWSIM_ATTR_MULTI_RADIO] = { .type = NLA_FLAG }, [HWSIM_ATTR_SUPPORT_NAN_DEVICE] = { .type = NLA_FLAG }, [HWSIM_ATTR_SUPPORT_BACKGROUND_RADAR] = { .type = NLA_FLAG }, + [HWSIM_ATTR_NO_MONITOR] = { .type = NLA_FLAG }, }; #if IS_REACHABLE(CONFIG_VIRTIO) @@ -1337,7 +1338,7 @@ mac80211_hwsim_get_tx_rate(struct ieee80211_hw *hw, static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, struct sk_buff *tx_skb, - struct ieee80211_channel *chan) + u32 freq) { struct mac80211_hwsim_data *data = hw->priv; struct sk_buff *skb; @@ -1369,7 +1370,7 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, hdr->rt_tsft = __mac80211_hwsim_get_tsf(data); hdr->rt_flags = 0; hdr->rt_rate = bitrate / 5; - hdr->rt_channel = cpu_to_le16(chan->center_freq); + hdr->rt_channel = cpu_to_le16(freq); flags = IEEE80211_CHAN_2GHZ; if (txrate && txrate->flags & IEEE80211_RATE_ERP_G) flags |= IEEE80211_CHAN_OFDM; @@ -1387,8 +1388,7 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, } -static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan, - const u8 *addr) +static void mac80211_hwsim_monitor_ack(u32 freq, const u8 *addr) { struct sk_buff *skb; struct hwsim_radiotap_ack_hdr *hdr; @@ -1410,7 +1410,7 @@ static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan, (1 << IEEE80211_RADIOTAP_CHANNEL)); hdr->rt_flags = 0; hdr->pad = 0; - hdr->rt_channel = cpu_to_le16(chan->center_freq); + hdr->rt_channel = cpu_to_le16(freq); flags = IEEE80211_CHAN_2GHZ; hdr->rt_chbitmask = cpu_to_le16(flags); @@ -1735,6 +1735,9 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, if (nla_put_u64_64bit(skb, HWSIM_ATTR_COOKIE, cookie, HWSIM_ATTR_PAD)) goto nla_put_failure; + /* track the frequency */ + info->rate_driver_data[1] = (void *)(uintptr_t)channel->center_freq; + genlmsg_end(skb, msg_head); if (hwsim_virtio_enabled) { @@ -1880,9 +1883,6 @@ static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data, sp->active_links_rx &= ~BIT(link_id); else sp->active_links_rx |= BIT(link_id); - - rx_status->link_valid = true; - rx_status->link_id = link_id; } rcu_read_unlock(); } @@ -1912,7 +1912,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, mac80211_hwsim_write_tsf(data, skb, sim_tsf); - mac80211_hwsim_monitor_rx(hw, skb, chan); + mac80211_hwsim_monitor_rx(hw, skb, chan->center_freq); memset(&rx_status, 0, sizeof(rx_status)); rx_status.flag |= RX_FLAG_MACTIME_START; @@ -2276,7 +2276,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel); if (ack && skb->len >= 16) - mac80211_hwsim_monitor_ack(channel, hdr->addr2); + mac80211_hwsim_monitor_ack(channel->center_freq, hdr->addr2); ieee80211_tx_info_clear_status(txi); @@ -4401,15 +4401,15 @@ struct hwsim_new_radio_params { bool background_radar; }; -static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb, +static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb, struct net *net, struct genl_info *info) { if (info) genl_notify(&hwsim_genl_family, mcast_skb, info, HWSIM_MCGRP_CONFIG, GFP_KERNEL); else - genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0, - HWSIM_MCGRP_CONFIG, GFP_KERNEL); + genlmsg_multicast_netns(&hwsim_genl_family, net, mcast_skb, 0, + HWSIM_MCGRP_CONFIG, GFP_KERNEL); } static int append_radio_msg(struct sk_buff *skb, int id, @@ -4493,7 +4493,8 @@ static int append_radio_msg(struct sk_buff *skb, int id, return 0; } -static void hwsim_mcast_new_radio(int id, struct genl_info *info, +static void hwsim_mcast_new_radio(int id, struct net *net, + struct genl_info *info, struct hwsim_new_radio_params *param) { struct sk_buff *mcast_skb; @@ -4513,7 +4514,7 @@ static void hwsim_mcast_new_radio(int id, struct genl_info *info, genlmsg_end(mcast_skb, data); - hwsim_mcast_config_msg(mcast_skb, info); + hwsim_mcast_config_msg(mcast_skb, net, info); return; out_err: @@ -6195,7 +6196,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, hwsim_radios_generation++; spin_unlock_bh(&hwsim_radio_lock); - hwsim_mcast_new_radio(idx, info, param); + hwsim_mcast_new_radio(idx, wiphy_net(data->hw->wiphy), info, param); return idx; @@ -6213,7 +6214,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, } static void hwsim_mcast_del_radio(int id, const char *hwname, - struct genl_info *info) + struct net *net, struct genl_info *info) { struct sk_buff *skb; void *data; @@ -6239,7 +6240,7 @@ static void hwsim_mcast_del_radio(int id, const char *hwname, genlmsg_end(skb, data); - hwsim_mcast_config_msg(skb, info); + hwsim_mcast_config_msg(skb, net, info); return; @@ -6251,7 +6252,7 @@ static void mac80211_hwsim_del_radio(struct mac80211_hwsim_data *data, const char *hwname, struct genl_info *info) { - hwsim_mcast_del_radio(data->idx, hwname, info); + hwsim_mcast_del_radio(data->idx, hwname, wiphy_net(data->hw->wiphy), info); debugfs_remove_recursive(data->debugfs); ieee80211_unregister_hw(data->hw); device_release_driver(data->dev); @@ -6396,6 +6397,8 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, int i; unsigned long flags; bool found = false; + bool no_monitor; + u32 freq; if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] || !info->attrs[HWSIM_ATTR_FLAGS] || @@ -6407,6 +6410,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, src = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]); hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]); ret_skb_cookie = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]); + no_monitor = nla_get_flag(info->attrs[HWSIM_ATTR_NO_MONITOR]); data2 = get_hwsim_data_ref_from_addr(src); if (!data2) @@ -6441,7 +6445,11 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, if (!found) goto out; - mac80211_hwsim_monitor_rx(data2->hw, skb, data2->channel); + txi = IEEE80211_SKB_CB(skb); + freq = (uintptr_t)txi->rate_driver_data[1]; + + if (!no_monitor) + mac80211_hwsim_monitor_rx(data2->hw, skb, freq); /* Tx info received because the frame was broadcasted on user space, so we get all the necessary info: tx attempts and skb control buff */ @@ -6450,8 +6458,6 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, info->attrs[HWSIM_ATTR_TX_INFO]); /* now send back TX status */ - txi = IEEE80211_SKB_CB(skb); - ieee80211_tx_info_clear_status(txi); for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { @@ -6471,8 +6477,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, if (!(hwsim_flags & HWSIM_TX_CTL_NO_ACK) && (hwsim_flags & HWSIM_TX_STAT_ACK)) { if (skb->len >= 16) - mac80211_hwsim_monitor_ack(data2->channel, - hdr->addr2); + mac80211_hwsim_monitor_ack(freq, hdr->addr2); txi->flags |= IEEE80211_TX_STAT_ACK; } @@ -6547,6 +6552,51 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, */ mutex_lock(&data2->mutex); + /* look for the skb matching the cookie passed back from user */ + if (info->attrs[HWSIM_ATTR_COOKIE] && + info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]) { + u64 cookie = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]); + struct sk_buff *orig_skb, *found = NULL; + struct mac80211_hwsim_data *txdata; + struct ieee80211_tx_info *txi; + const u8 *transmitter; + unsigned long flags; + + transmitter = nla_data(info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]); + txdata = get_hwsim_data_ref_from_addr(transmitter); + if (!txdata || txdata->netgroup != data2->netgroup) + goto out_unlock; + + spin_lock_irqsave(&txdata->pending.lock, flags); + skb_queue_walk(&txdata->pending, orig_skb) { + uintptr_t skb_cookie; + + txi = IEEE80211_SKB_CB(orig_skb); + skb_cookie = (uintptr_t)txi->rate_driver_data[0]; + + if (skb_cookie == cookie) { + found = orig_skb; + break; + } + } + + /* that's weird */ + if (!found) { + spin_unlock_irqrestore(&txdata->pending.lock, flags); + goto out_unlock; + } + + if (frame_data_len > found->len && + pskb_expand_head(found, 0, frame_data_len - found->len, + GFP_ATOMIC)) { + spin_unlock_irqrestore(&txdata->pending.lock, flags); + goto out_unlock; + } + skb_trim(found, 0); + skb_put_data(found, frame_data, frame_data_len); + spin_unlock_irqrestore(&txdata->pending.lock, flags); + } + /* check if radio is configured properly */ if ((data2->idle && !data2->tmp_chan) || !data2->started) diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h index b62297a978e7..a699d108eee6 100644 --- a/include/linux/ieee80211-eht.h +++ b/include/linux/ieee80211-eht.h @@ -482,6 +482,7 @@ struct ieee80211_multi_link_elem { #define IEEE80211_MLC_BASIC_PRES_MLD_ID 0x0200 #define IEEE80211_MLC_BASIC_PRES_EXT_MLD_CAPA_OP 0x0400 #define IEEE80211_MLC_BASIC_PRES_ENH_CRIT_UPD 0x0800 +#define IEEE80211_MLC_BASIC_PRES_AGE_OF_BSS_LOAD 0x1000 #define IEEE80211_MED_SYNC_DELAY_DURATION 0x00ff #define IEEE80211_MED_SYNC_DELAY_SYNC_OFDM_ED_THRESH 0x0f00 @@ -550,6 +551,8 @@ struct ieee80211_mle_basic_common_info { } __packed; #define IEEE80211_MLC_PREQ_PRES_MLD_ID 0x0010 +#define IEEE80211_MLC_PREQ_PRES_MLD_MAC_ADDR 0x0020 +#define IEEE80211_MLC_PREQ_PRES_NEIGH_AP_MLD_MAC_ADDR 0x0040 struct ieee80211_mle_preq_common_info { u8 len; @@ -560,6 +563,7 @@ struct ieee80211_mle_preq_common_info { #define IEEE80211_MLC_RECONF_PRES_EML_CAPA 0x0020 #define IEEE80211_MLC_RECONF_PRES_MLD_CAPA_OP 0x0040 #define IEEE80211_MLC_RECONF_PRES_EXT_MLD_CAPA_OP 0x0080 +#define IEEE80211_MLC_RECONF_PRES_TARGET_AP_MLD_ADDR 0x0100 /* no fixed fields in RECONF */ @@ -766,7 +770,7 @@ static inline u16 ieee80211_mle_get_mld_capa_op(const u8 *data) #define IEEE80211_EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE 0x0020 #define IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK 0x0040 #define IEEE80211_EHT_ML_EXT_MLD_CAPA_BTM_MLD_RECO_MULTI_AP 0x0080 -/* defined by UHR Draft P802.11bn_D1.3 Figure 9-1147 */ +/* defined by UHR Draft P802.11bn_D1.5 Figure 9-1168 */ #define IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM 0x0100 /** @@ -928,11 +932,17 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) common += 2; if (control & IEEE80211_MLC_BASIC_PRES_ENH_CRIT_UPD) common += 1; + if (control & IEEE80211_MLC_BASIC_PRES_AGE_OF_BSS_LOAD) + common += 1; break; case IEEE80211_ML_CONTROL_TYPE_PREQ: common += sizeof(struct ieee80211_mle_preq_common_info); if (control & IEEE80211_MLC_PREQ_PRES_MLD_ID) common += 1; + if (control & IEEE80211_MLC_PREQ_PRES_MLD_MAC_ADDR) + common += ETH_ALEN; + if (control & IEEE80211_MLC_PREQ_PRES_NEIGH_AP_MLD_MAC_ADDR) + common += ETH_ALEN; break; case IEEE80211_ML_CONTROL_TYPE_RECONF: common += 1; @@ -944,6 +954,8 @@ static inline bool ieee80211_mle_size_ok(const u8 *data, size_t len) common += 2; if (control & IEEE80211_MLC_RECONF_PRES_EXT_MLD_CAPA_OP) common += 2; + if (control & IEEE80211_MLC_RECONF_PRES_TARGET_AP_MLD_ADDR) + common += ETH_ALEN; break; case IEEE80211_ML_CONTROL_TYPE_TDLS: common += sizeof(struct ieee80211_mle_tdls_common_info); @@ -1003,6 +1015,7 @@ enum ieee80211_mle_subelems { #define IEEE80211_MLE_STA_CONTROL_BSS_PARAM_CHANGE_CNT_PRESENT 0x0800 #define IEEE80211_MLE_STA_CONTROL_ENH_CRIT_UPD_PRESENT 0x1000 #define IEEE80211_MLE_STA_CONTROL_AP_CONDUCTED_TX_PWR_PRESENT 0x2000 +#define IEEE80211_MLE_STA_CONTROL_AGE_OF_BSS_LOAD_PRESENT 0x4000 struct ieee80211_mle_per_sta_profile { __le16 control; @@ -1054,6 +1067,9 @@ static inline bool ieee80211_mle_basic_sta_prof_size_ok(const u8 *data, if (control & IEEE80211_MLE_STA_CONTROL_AP_CONDUCTED_TX_PWR_PRESENT) info_len += 1; + if (control & IEEE80211_MLE_STA_CONTROL_AGE_OF_BSS_LOAD_PRESENT) + info_len += 1; + return prof->sta_info_len >= info_len && fixed + prof->sta_info_len - 1 <= len; } diff --git a/include/linux/ieee80211-uhr.h b/include/linux/ieee80211-uhr.h index 665d4b3a5b41..19e9260d0db8 100644 --- a/include/linux/ieee80211-uhr.h +++ b/include/linux/ieee80211-uhr.h @@ -17,32 +17,32 @@ #define IEEE80211_UHR_OPER_PARAMS_PEDCA_ENA 0x0004 #define IEEE80211_UHR_OPER_PARAMS_DBE_ENA 0x0008 #define IEEE80211_UHR_OPER_PARAMS_DBE_BW 0x0070 -#define IEEE80211_UHR_OPER_PARAMS_DUO_PRES 0x0080 -#define IEEE80211_UHR_OPER_PARAMS_DPS_PRES 0x0100 -#define IEEE80211_UHR_OPER_PARAMS_NPCA_PRES 0x0200 -#define IEEE80211_UHR_OPER_PARAMS_PEDCA_PRES 0x0400 -#define IEEE80211_UHR_OPER_PARAMS_DBE_PRES 0x0800 +#define IEEE80211_UHR_OPER_PARAMS_ELR_RX 0x0080 +#define IEEE80211_UHR_OPER_PARAMS_DUO_PRES 0x0100 +#define IEEE80211_UHR_OPER_PARAMS_DPS_PRES 0x0200 +#define IEEE80211_UHR_OPER_PARAMS_NPCA_PRES 0x0400 +#define IEEE80211_UHR_OPER_PARAMS_PEDCA_PRES 0x0800 +#define IEEE80211_UHR_OPER_PARAMS_DBE_PRES 0x1000 struct ieee80211_uhr_operation { __le16 params; - u8 basic_mcs_nss_set[4]; u8 variable[]; } __packed; -#define IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN_OFFS 0x0000000F -#define IEEE80211_UHR_NPCA_PARAMS_MIN_DUR_THRESH 0x000000F0 -#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_DELAY 0x00003F00 -#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_BACK_DELAY 0x000FC000 -#define IEEE80211_UHR_NPCA_PARAMS_INIT_QSRC 0x00300000 -#define IEEE80211_UHR_NPCA_PARAMS_MOPLEN 0x00400000 -#define IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES 0x00800000 +#define IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN 0x000000FF +#define IEEE80211_UHR_NPCA_PARAMS_MIN_DUR_THRESH 0x00000F00 +#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_DELAY 0x0003F000 +#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_BACK_DELAY 0x00FC0000 +#define IEEE80211_UHR_NPCA_PARAMS_INIT_QSRC 0x03000000 +#define IEEE80211_UHR_NPCA_PARAMS_MOPLEN 0x04000000 +#define IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES 0x08000000 /** * struct ieee80211_uhr_npca_info - npca operation information * * This structure is the "NPCA Operation Parameters field format" of "UHR - * Operation Element" fields as described in P802.11bn_D1.3 - * subclause 9.4.2.353. See Figure 9-aa4. + * Operation Element" fields as described in P802.11bn_D1.5 + * subclause 9.4.2.355.2, see Figure 9-aa6. * * Refer to IEEE80211_UHR_NPCA* * @params: @@ -97,7 +97,7 @@ struct ieee80211_uhr_npca_info { * struct ieee80211_uhr_dps_info - DPS operation information * * This structure is the "DPS Operation Parameter field" of "UHR - * Operation Element" fields as described in P802.11bn_D1.3 + * Operation Element" fields as described in P802.11bn_D1.5 * subclause 9.4.1.87. See Figure 9-207u. * * Refer to IEEE80211_UHR_DPS* @@ -211,8 +211,8 @@ static inline int ieee80211_uhr_dbe_bw_mhz(enum ieee80211_uhr_dbe_oper_bw bw) * struct ieee80211_uhr_dbe_info - DBE operation information * * This structure is the "DBE Operation Parameters field" of - * "UHR Operation Element" fields as described in P802.11bn_D1.3 - * subclause 9.4.2.353. See Figure 9-aa6. + * "UHR Operation Element" fields as described in P802.11bn_D1.5 + * subclause 9.4.2.355.4, see Figure 9-aa9. * * Refer to IEEE80211_UHR_DBE_OPER* * @params: @@ -234,25 +234,23 @@ struct ieee80211_uhr_dbe_info { __le16 dis_subch_bmap[]; } __packed; -#define IEEE80211_UHR_P_EDCA_ECWMIN 0x0F -#define IEEE80211_UHR_P_EDCA_ECWMAX 0xF0 -#define IEEE80211_UHR_P_EDCA_AIFSN 0x000F -#define IEEE80211_UHR_P_EDCA_CW_DS 0x0030 -#define IEEE80211_UHR_P_EDCA_PSRC_THRESHOLD 0x01C0 -#define IEEE80211_UHR_P_EDCA_QSRC_THRESHOLD 0x0600 +#define IEEE80211_UHR_P_EDCA_ECWMIN 0x0007 +#define IEEE80211_UHR_P_EDCA_ECWMAX 0x0038 +#define IEEE80211_UHR_P_EDCA_AIFSN 0x01C0 +#define IEEE80211_UHR_P_EDCA_CW_DS 0x0600 +#define IEEE80211_UHR_P_EDCA_PSRC_THRESHOLD 0x3800 +#define IEEE80211_UHR_P_EDCA_QSRC_THRESHOLD 0xC000 /** * struct ieee80211_uhr_p_edca_info - P-EDCA operation information * * This structure is the "P-EDCA Operation Parameters field" of - * "UHR Operation Element" fields as described in P802.11bn_D1.3 - * subclause 9.4.2.353. See Figure 9-aa5. + * "UHR Operation Element" fields as described in P802.11bn_D1.5 + * subclause 9.4.2.355.3, see Figure 9-aa8. * * Refer to IEEE80211_UHR_P_EDCA* - * @p_edca_ec: P-EDCA ECWmin and ECWmax. - * These fields indicate the CWmin and CWmax values used by a - * P-EDCA STA during P-EDCA contention. - * @params: AIFSN, CW DS, PSRC threshold, and QSRC threshold. + * @params: ECWmin, ECWmax, AIFSN, CW DS, PSRC threshold, and QSRC threshold. + * - CWmin/CWmax values used by a P-EDCA STA during P-EDCA contention. * - The AIFSN field indicates the AIFSN value used by a P-EDCA STA * during P-EDCA contention. * - The CW DS field indicates the value used for randomization of the @@ -266,7 +264,6 @@ struct ieee80211_uhr_dbe_info { * value 0 is reserved. */ struct ieee80211_uhr_p_edca_info { - u8 p_edca_ec; __le16 params; } __packed; @@ -302,7 +299,7 @@ static inline bool ieee80211_uhr_oper_size_ok(const u8 *data, u8 len) } } - /* P-EDCA Operation Parameters (fixed 3 bytes) */ + /* P-EDCA Operation Parameters */ if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_PEDCA_PRES)) { needed += sizeof(struct ieee80211_uhr_p_edca_info); if (len < needed) @@ -341,6 +338,9 @@ ieee80211_uhr_npca_info(const struct ieee80211_uhr_operation *oper) if (!(oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA))) return NULL; + if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DUO_PRES)) + pos += 1; + if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DPS_PRES)) pos += sizeof(struct ieee80211_uhr_dps_info); @@ -372,6 +372,9 @@ ieee80211_uhr_oper_dbe_info(const struct ieee80211_uhr_operation *oper) if (!(oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DBE_ENA))) return NULL; + if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DUO_PRES)) + pos += 1; + if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_DPS_PRES)) pos += sizeof(struct ieee80211_uhr_dps_info); @@ -415,28 +418,42 @@ ieee80211_uhr_oper_dbe_info(const struct ieee80211_uhr_operation *oper) #define IEEE80211_UHR_MAC_CAP2_UHR_OM_PU_TO_LOW 0xC0 #define IEEE80211_UHR_MAC_CAP3_UHR_OM_PU_TO_HIGH 0x03 -#define IEEE80211_UHR_MAC_CAP3_PARAM_UPD_ADV_NOTIF_INTV 0x1C -#define IEEE80211_UHR_MAC_CAP3_UPD_IND_TIM_INTV_LOW 0xE0 +#define IEEE80211_UHR_MAC_CAP3_PARAM_UPD_ADV_NOTIF_INTV 0x7C +#define IEEE80211_UHR_MAC_CAP3_UPD_IND_TIM_INTV_LOW 0x80 -#define IEEE80211_UHR_MAC_CAP4_UPD_IND_TIM_INTV_HIGH 0x03 -#define IEEE80211_UHR_MAC_CAP4_BOUNDED_ESS 0x04 -#define IEEE80211_UHR_MAC_CAP4_BTM_ASSURANCE 0x08 -#define IEEE80211_UHR_MAC_CAP4_CO_BF_SUPP 0x10 +#define IEEE80211_UHR_MAC_CAP4_UPD_IND_TIM_INTV_HIGH 0x0F +#define IEEE80211_UHR_MAC_CAP4_BOUNDED_ESS 0x10 +#define IEEE80211_UHR_MAC_CAP4_BTM_ASSURANCE 0x20 +#define IEEE80211_UHR_MAC_CAP4_CO_BF_SUPP 0x40 +#define IEEE80211_UHR_MAC_CAP4_CO_SR_SUPP 0x80 + +#define IEEE80211_UHR_MAC_CAP5_MAPC_ENH_MSRMT_SUPP 0x01 #define IEEE80211_UHR_MAC_CAP_DBE_MAX_BW 0x07 #define IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES 0x08 #define IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES 0x10 +/* struct ieee80211_uhr_cap_dbe::bwcap[].cap */ +#define IEEE80211_UHR_MAC_CAP_DBE_CAP_NUM_SND_DIMS 0x07 +#define IEEE80211_UHR_MAC_CAP_DBE_CAP_NON_OFDMA_UL_MUMIMO 0x08 +#define IEEE80211_UHR_MAC_CAP_DBE_CAP_MU_BEAMFORMER 0x10 +#define IEEE80211_UHR_MAC_CAP_DBE_CAP_BEAMFORMEE_SS 0xe0 + struct ieee80211_uhr_cap_dbe { u8 cap; + u8 max_switch_time_period; + u8 mode_change_intvl; /* present 0, 1 or 2 times depending on _PRES bits */ - struct ieee80211_eht_mcs_nss_supp_bw eht_mcs_map[]; + struct { + struct ieee80211_eht_mcs_nss_supp_bw eht_mcs_map; + u8 cap; + } __packed bwcap[]; } __packed; /** * enum ieee80211_uhr_dbe_max_supported_bw - DBE Maximum Supported Bandwidth * - * As per spec P802.11bn_D1.3 "Table 9-bb5—Encoding of the DBE Maximum + * As per spec P802.11bn_D1.5 Table 9-bb8 "Encoding of the DBE Maximum * Supported Bandwidth field". * * @IEEE80211_UHR_DBE_MAX_BW_40: Indicates 40 MHz DBE max supported bw @@ -520,10 +537,10 @@ static inline bool ieee80211_uhr_capa_size_ok(const u8 *data, u8 len, dbe = (const void *)cap->variable; if (dbe->cap & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES) - needed += sizeof(dbe->eht_mcs_map[0]); + needed += sizeof(dbe->bwcap[0]); if (dbe->cap & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES) - needed += sizeof(dbe->eht_mcs_map[0]); + needed += sizeof(dbe->bwcap[0]); } return len >= needed; diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 26e674038865..7fbc51f873ff 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2421,8 +2421,7 @@ static inline bool ieee80211_is_bufferable_mmpdu(struct sk_buff *skb) /* action frame - additionally check for non-bufferable FTM */ - if (mgmt->u.action.category != WLAN_CATEGORY_PUBLIC && - mgmt->u.action.category != WLAN_CATEGORY_PROTECTED_DUAL_OF_ACTION) + if (mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) return true; if (mgmt->u.action.action_code == WLAN_PUB_ACTION_FTM_REQUEST || diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 97c16d4ff127..9aa7d2d4a184 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -576,7 +576,6 @@ struct ieee80211_sta_s1g_cap { * @vht_cap: VHT capabilities in this band * @s1g_cap: S1G capabilities in this band * @edmg_cap: EDMG capabilities in this band - * @s1g_cap: S1G capabilities in this band (S1G band only, of course) * @n_iftype_data: number of iftype data entries * @iftype_data: interface type data entries. Note that the bits in * @types_mask inside this structure cannot overlap (i.e. only @@ -901,6 +900,7 @@ struct cfg80211_bitrate_mask { enum nl80211_eht_gi eht_gi; enum nl80211_he_ltf he_ltf; enum nl80211_eht_ltf eht_ltf; + bool nonht_dup_6ghz; } control[NUM_NL80211_BANDS]; }; @@ -4009,6 +4009,7 @@ struct cfg80211_update_ft_ies_params { * @link_id: for MLO, the link ID to transmit on, -1 if not given; note * that the link ID isn't validated (much), it's in range but the * link might not exist (or be used by the receiver STA) + * @no_sta: set if the frame should not be transmitted using an existing STA */ struct cfg80211_mgmt_tx_params { struct ieee80211_channel *chan; @@ -4021,6 +4022,7 @@ struct cfg80211_mgmt_tx_params { int n_csa_offsets; const u16 *csa_offsets; int link_id; + bool no_sta; }; /** @@ -9501,6 +9503,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, * @flags: flags, as defined in &enum nl80211_rxmgmt_flags * @rx_tstamp: Hardware timestamp of frame RX in nanoseconds * @ack_tstamp: Hardware timestamp of ack TX in nanoseconds + * @no_sta: set if no station is known for the frame (relevant for MLD) */ struct cfg80211_rx_info { int freq; @@ -9512,6 +9515,7 @@ struct cfg80211_rx_info { u32 flags; u64 rx_tstamp; u64 ack_tstamp; + bool no_sta; }; /** diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index 8bbaf77da7cf..ffdfbade25b8 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -93,6 +93,7 @@ enum ieee80211_radiotap_presence { IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29, IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30, IEEE80211_RADIOTAP_EXT = 31, + IEEE80211_RADIOTAP_S1G = 32, IEEE80211_RADIOTAP_EHT_USIG = 33, IEEE80211_RADIOTAP_EHT = 34, IEEE80211_RADIOTAP_UHR_ELR = 37, @@ -112,6 +113,9 @@ enum ieee80211_radiotap_flags { /* for IEEE80211_RADIOTAP_CHANNEL */ enum ieee80211_radiotap_channel_flags { + IEEE80211_CHAN_700MHZ = 0x0001, + IEEE80211_CHAN_800MHZ = 0x0002, + IEEE80211_CHAN_900MHZ = 0x0004, IEEE80211_CHAN_CCK = 0x0020, IEEE80211_CHAN_OFDM = 0x0040, IEEE80211_CHAN_2GHZ = 0x0080, @@ -592,7 +596,7 @@ enum ieee80211_radiotap_eht_usig_mu { enum ieee80211_radiotap_eht_usig_tb { /* TB-USIG-1 */ - IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD = 0x0000001f, + IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD = 0x0000003f, /* TB-USIG-2 */ IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE = 0x000000c0, @@ -792,6 +796,68 @@ enum ieee80211_radiotap_uhr_user_info { IEEE80211_RADIOTAP_UHR_USER_INFO_2X_LDPC = 0x02000000, }; +/* + * ieee80211_radiotap_s1g - content of S1G TLV (type 32) + * see www.radiotap.org/fields/S1G.html for details + */ +struct ieee80211_radiotap_s1g { + __le16 known; + __le16 data1; + __le16 data2; +} __packed; + +enum ieee80211_radiotap_s1g_known { + IEEE80211_RADIOTAP_S1G_KNOWN_PPDU_FORMAT = 0x0001, + IEEE80211_RADIOTAP_S1G_KNOWN_RESPONSE_IND = 0x0002, + IEEE80211_RADIOTAP_S1G_KNOWN_GI = 0x0004, + IEEE80211_RADIOTAP_S1G_KNOWN_NSS = 0x0008, + IEEE80211_RADIOTAP_S1G_KNOWN_BW = 0x0010, + IEEE80211_RADIOTAP_S1G_KNOWN_MCS = 0x0020, + IEEE80211_RADIOTAP_S1G_KNOWN_COLOR = 0x0040, + IEEE80211_RADIOTAP_S1G_KNOWN_UPLINK_IND = 0x0080, +}; + +enum ieee80211_radiotap_s1g_data1 { + IEEE80211_RADIOTAP_S1G_DATA1_PPDU_FORMAT = 0x0003, + IEEE80211_RADIOTAP_S1G_DATA1_RESPONSE_IND = 0x000c, + IEEE80211_RADIOTAP_S1G_DATA1_GI = 0x0020, + IEEE80211_RADIOTAP_S1G_DATA1_NSS = 0x00c0, + IEEE80211_RADIOTAP_S1G_DATA1_BW = 0x0f00, + IEEE80211_RADIOTAP_S1G_DATA1_MCS = 0xf000, +}; + +enum ieee80211_radiotap_s1g_data2 { + IEEE80211_RADIOTAP_S1G_DATA2_COLOR = 0x0007, + IEEE80211_RADIOTAP_S1G_DATA2_UPLINK_IND = 0x0008, + IEEE80211_RADIOTAP_S1G_DATA2_RSSI = 0xff00, +}; + +enum ieee80211_radiotap_s1g_ppdu_format { + IEEE80211_RADIOTAP_S1G_PPDU_FORMAT_1M = 0, + IEEE80211_RADIOTAP_S1G_PPDU_FORMAT_SHORT = 1, + IEEE80211_RADIOTAP_S1G_PPDU_FORMAT_LONG = 2, +}; + +enum ieee80211_radiotap_s1g_response_ind { + IEEE80211_RADIOTAP_S1G_RESPONSE_IND_NONE = 0, + IEEE80211_RADIOTAP_S1G_RESPONSE_IND_NDP = 1, + IEEE80211_RADIOTAP_S1G_RESPONSE_IND_NORMAL = 2, + IEEE80211_RADIOTAP_S1G_RESPONSE_IND_LONG = 3, +}; + +enum ieee80211_radiotap_s1g_gi { + IEEE80211_RADIOTAP_S1G_GI_LONG = 0, + IEEE80211_RADIOTAP_S1G_GI_SHORT = 1, +}; + +enum ieee80211_radiotap_s1g_bw { + IEEE80211_RADIOTAP_S1G_BW_1MHZ = 0, + IEEE80211_RADIOTAP_S1G_BW_2MHZ = 1, + IEEE80211_RADIOTAP_S1G_BW_4MHZ = 2, + IEEE80211_RADIOTAP_S1G_BW_8MHZ = 3, + IEEE80211_RADIOTAP_S1G_BW_16MHZ = 4, +}; + /** * ieee80211_get_radiotap_len - get radiotap header length * @data: pointer to the header diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ed6a5874ff96..d6f3c5855910 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1770,10 +1770,9 @@ enum mac80211_rx_encoding { * @ampdu_reference: A-MPDU reference number, must be a different value for * each A-MPDU but the same for each subframe within one A-MPDU * @zero_length_psdu_type: radiotap type of the 0-length PSDU - * @link_valid: if the link which is identified by @link_id is valid. This flag - * is set only when connection is MLO. - * @link_id: id of the link used to receive the packet. This is used along with - * @link_valid. + * @link_id: id of the link used to receive the packet. Set and used by + * mac80211 internally, it uses @freq set by the driver to identify the + * correct link per vif. */ struct ieee80211_rx_status { u64 mactime; @@ -1813,7 +1812,7 @@ struct ieee80211_rx_status { u8 chains; s8 chain_signal[IEEE80211_MAX_CHAINS]; u8 zero_length_psdu_type; - u8 link_valid:1, link_id:4; + u8 link_id:4; }; static_assert(sizeof(struct ieee80211_rx_status) <= sizeof_field(struct sk_buff, cb)); @@ -2919,8 +2918,8 @@ struct ieee80211_txq { * autonomously manages the PS status of connected stations. When * this flag is set mac80211 will not trigger PS mode for connected * stations based on the PM bit of incoming frames. - * Use ieee80211_start_ps()/ieee8021_end_ps() to manually configure - * the PS mode of connected stations. + * Use ieee80211_sta_ps_transition() to manually toggle the PS mode + * of connected stations. * * @IEEE80211_HW_TX_AMPDU_SETUP_IN_HW: The device handles TX A-MPDU session * setup strictly in HW. mac80211 should not attempt to do this in @@ -5389,14 +5388,18 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw); * mixed for a single hardware. Must not run concurrently with * ieee80211_tx_status_skb() or ieee80211_tx_status_ni(). * + * For data frames, when hardware has done address translation, a link station + * has to be provided and the frequency information may be skipped. + * * This function must be called with BHs disabled and RCU read lock * * @hw: the hardware this frame came in on - * @sta: the station the frame was received from, or %NULL + * @link_sta: the link station the data frame was received from, or %NULL * @skb: the buffer to receive, owned by mac80211 after this call * @list: the destination list */ -void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta, +void ieee80211_rx_list(struct ieee80211_hw *hw, + struct ieee80211_link_sta *link_sta, struct sk_buff *skb, struct list_head *list); /** @@ -5414,14 +5417,18 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta, * mixed for a single hardware. Must not run concurrently with * ieee80211_tx_status_skb() or ieee80211_tx_status_ni(). * + * For data frames, when hardware has done address translation, a link station + * has to be provided and the frequency information may be skipped. + * * This function must be called with BHs disabled. * * @hw: the hardware this frame came in on - * @sta: the station the frame was received from, or %NULL + * @link_sta: the link station the data frame was received from, or %NULL * @skb: the buffer to receive, owned by mac80211 after this call * @napi: the NAPI context */ -void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *sta, +void ieee80211_rx_napi(struct ieee80211_hw *hw, + struct ieee80211_link_sta *link_sta, struct sk_buff *skb, struct napi_struct *napi); /** @@ -5499,10 +5506,8 @@ static inline void ieee80211_rx_ni(struct ieee80211_hw *hw, * * @sta: currently connected sta * @start: start or stop PS - * - * Return: 0 on success. -EINVAL when the requested PS mode is already set. */ -int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start); +void ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start); /** * ieee80211_sta_ps_transition_ni - PS transition for connected sta @@ -5514,19 +5519,13 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start); * * @sta: currently connected sta * @start: start or stop PS - * - * Return: Like ieee80211_sta_ps_transition(). */ -static inline int ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta, +static inline void ieee80211_sta_ps_transition_ni(struct ieee80211_sta *sta, bool start) { - int ret; - local_bh_disable(); - ret = ieee80211_sta_ps_transition(sta, start); + ieee80211_sta_ps_transition(sta, start); local_bh_enable(); - - return ret; } /** @@ -7640,12 +7639,14 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, * @dev: the &struct device of this 802.11 device * @chandef: the channel definition the frame will be transmitted on, or * %NULL to skip the bandwidth checks + * @trim_fcs: trim the FCS trailer when present; validate it otherwise * * Return: %true if the radiotap header was parsed, %false otherwise */ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, struct net_device *dev, - const struct cfg80211_chan_def *chandef); + const struct cfg80211_chan_def *chandef, + bool trim_fcs); /** * struct ieee80211_noa_data - holds temporary data for tracking P2P NoA state @@ -8061,25 +8062,6 @@ void ieee80211_obss_color_collision_notify(struct ieee80211_vif *vif, u64 color_bitmap, u8 link_id); -/** - * ieee80211_is_tx_data - check if frame is a data frame - * - * The function is used to check if a frame is a data frame. Frames with - * hardware encapsulation enabled are data frames. - * - * @skb: the frame to be transmitted. - * - * Return: %true if @skb is a data frame, %false otherwise - */ -static inline bool ieee80211_is_tx_data(struct sk_buff *skb) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = (void *) skb->data; - - return info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP || - ieee80211_is_data(hdr->frame_control); -} - /** * ieee80211_set_active_links - set active links in client mode * @vif: interface to set active links on diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 020387d76412..9a2ccb8d66b8 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -937,8 +937,8 @@ * OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME * messages. Note that per PHY only one application may register. * - * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap for the individual TIDs whether - * No Acknowledgement Policy should be applied. + * @NL80211_CMD_SET_NOACK_MAP: sets a bitmap (%NL80211_ATTR_TID_BITMAP) + * indicating for which TIDs No Acknowledgment Policy should be applied. * * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels * independently of the userspace SME, send this event indicating @@ -2288,8 +2288,8 @@ enum nl80211_commands { * abides to when initiating radiation on DFS channels. A country maps * to one DFS region. * - * @NL80211_ATTR_NOACK_MAP: This u16 bitmap contains the No Ack Policy of - * up to 16 TIDs. + * @NL80211_ATTR_TID_BITMAP: A TID bitmap (u16) whose meaning depends + * on the command. * * @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be * used by the drivers which has MLME in firmware and does not have support @@ -3185,6 +3185,11 @@ enum nl80211_commands { * The aggregated message always precedes the per-link messages for the * same station within a dump sequence. * + * @NL80211_ATTR_FRAME_NO_STA: Valid for @NL80211_CMD_FRAME to denote that + * the kernel had no station for a received frame or should not use a + * known station to transmit a frame. This is relevant to know whether + * MLD address translation happened or to disable it when sending a frame. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3429,7 +3434,7 @@ enum nl80211_attrs { NL80211_ATTR_DISABLE_HT, NL80211_ATTR_HT_CAPABILITY_MASK, - NL80211_ATTR_NOACK_MAP, + NL80211_ATTR_TID_BITMAP, NL80211_ATTR_INACTIVITY_TIMEOUT, @@ -3785,6 +3790,8 @@ enum nl80211_attrs { NL80211_ATTR_STA_DUMP_LINK_STATS, + NL80211_ATTR_FRAME_NO_STA, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3800,6 +3807,7 @@ enum nl80211_attrs { #define NL80211_ATTR_CSA_C_OFF_BEACON NL80211_ATTR_CNTDWN_OFFS_BEACON #define NL80211_ATTR_CSA_C_OFF_PRESP NL80211_ATTR_CNTDWN_OFFS_PRESP #define NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS NL80211_ATTR_EXT_MLD_CAPA_AND_OPS +#define NL80211_ATTR_NOACK_MAP NL80211_ATTR_TID_BITMAP /* * Allow user space programs to use #ifdef on new attributes by defining them @@ -5900,6 +5908,9 @@ enum nl80211_key_attributes { * see &struct nl80211_txrate_eht * @NL80211_TXRATE_EHT_GI: configure EHT GI, (u8, see &enum nl80211_eht_gi) * @NL80211_TXRATE_EHT_LTF: configure EHT LTF, (u8, see &enum nl80211_eht_ltf) + * @NL80211_TXRATE_6GHZ_NON_HT_DUP: configure 6 GHz non-HT duplicate Beacon + * transmission. This flag is applicable only in Beacon TX rate setting + * and must be accompanied by a non-HT (legacy) Beacon rate. * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ @@ -5915,6 +5926,7 @@ enum nl80211_tx_rate_attributes { NL80211_TXRATE_EHT, NL80211_TXRATE_EHT_GI, NL80211_TXRATE_EHT_LTF, + NL80211_TXRATE_6GHZ_NON_HT_DUP, /* keep last */ __NL80211_TXRATE_AFTER_LAST, @@ -7110,6 +7122,10 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_PROBE_AP: Driver supports probing the associated AP * in STA mode using @NL80211_CMD_PROBE_PEER. * + * @NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD: Driver supports fast roaming + * offload in station mode, including Fast Transition or Opportunistic + * Key Caching. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -7192,6 +7208,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_ROC_ADDR_FILTER, NL80211_EXT_FEATURE_SET_KEY_LTF_SEED, NL80211_EXT_FEATURE_PROBE_AP, + NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 0832213430f4..f3ecdd8601a0 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -98,7 +98,7 @@ static void ieee80211_send_addba_request(struct sta_info *sta, u16 tid, if (sta->sta.deflink.he_cap.has_he) ieee80211_add_addbaext(skb, 0, agg_size); - ieee80211_tx_skb_tid(sdata, skb, tid, -1); + ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1); } void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn) @@ -127,7 +127,7 @@ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn) IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | IEEE80211_TX_CTL_REQ_TX_STATUS; - ieee80211_tx_skb_tid(sdata, skb, tid, -1); + ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1); } EXPORT_SYMBOL(ieee80211_send_bar); @@ -212,10 +212,7 @@ ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable) txqi = to_txq_info(txq); - if (enable) - set_bit(IEEE80211_TXQ_AMPDU, &txqi->flags); - else - clear_bit(IEEE80211_TXQ_AMPDU, &txqi->flags); + assign_bit(IEEE80211_TXQ_AMPDU, &txqi->flags, enable); clear_bit(IEEE80211_TXQ_STOP, &txqi->flags); local_bh_disable(); diff --git a/net/mac80211/ap.c b/net/mac80211/ap.c index e7ac99c5a22e..fe255891c35f 100644 --- a/net/mac80211/ap.c +++ b/net/mac80211/ap.c @@ -82,9 +82,6 @@ ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata, if (!ift_ext_capa) return; - if (!status->link_valid) - return; - sta = sta_info_get_bss(sdata, mgmt->sa); if (!sta) return; diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index e38631d7cfb4..1f914b48e3bb 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -96,8 +96,8 @@ static ssize_t aqm_read(struct file *file, "RW fq_quantum %u\n", fq->flows_cnt, fq->backlog, - fq->overmemory, fq->overlimit, + fq->overmemory, fq->collisions, fq->memory_usage, fq->memory_limit, diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 6aba22493670..81d18ab83543 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -1033,7 +1033,13 @@ void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata) void ieee80211_debugfs_rename_netdev(struct ieee80211_sub_if_data *sdata) { - debugfs_change_name(sdata->vif.debugfs_dir, "netdev:%s", sdata->name); + struct dentry *dir; + + wiphy_lock(sdata->local->hw.wiphy); + dir = sdata->vif.debugfs_dir; + if (dir) + debugfs_change_name(dir, "netdev:%s", sdata->name); + wiphy_unlock(sdata->local->hw.wiphy); } void ieee80211_debugfs_recreate_netdev(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index e1e1b7f82f3f..c5c2113fa4f7 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -545,7 +545,7 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, info->status_data = IEEE80211_STATUS_TYPE_SMPS | u16_encode_bits(status_link_id << 2 | smps, IEEE80211_STATUS_SUBDATA_MASK); - ieee80211_tx_skb_tid(sdata, skb, 7, link_id); + ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id); return 0; } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d05f59467399..10767666c45c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -191,6 +191,15 @@ struct ieee80211_tx_data { unsigned int flags; }; +static inline bool ieee80211_is_tx_data(struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + + return info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP || + ieee80211_is_data(hdr->frame_control); +} + /** * enum ieee80211_packet_rx_flags - packet RX flags * @IEEE80211_RX_AMSDU: a-MSDU packet @@ -1459,6 +1468,7 @@ struct ieee80211_local { spinlock_t active_txq_lock[IEEE80211_NUM_ACS]; struct list_head active_txqs[IEEE80211_NUM_ACS]; u16 schedule_round[IEEE80211_NUM_ACS]; + bool schedule_open[IEEE80211_NUM_ACS]; /* serializes ieee80211_handle_wake_tx_queue */ spinlock_t handle_wake_tx_queue_lock; @@ -2233,8 +2243,7 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, void __ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev, u32 info_flags, - u32 ctrl_flags, - u64 cookie); + u32 ctrl_flags); struct sk_buff * ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, u32 info_flags); @@ -2388,7 +2397,6 @@ ieee80211_he_op_ie_to_bss_conf(struct ieee80211_vif *vif, const struct ieee80211_he_operation *he_op_ie_elem); /* S1G */ -void ieee80211_s1g_sta_rate_init(struct sta_info *sta); bool ieee80211_s1g_is_twt_setup(struct sk_buff *skb); void ieee80211_s1g_rx_twt_action(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); @@ -2463,7 +2471,8 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct sk_buff *skb); void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, int tid, int link_id, + struct sk_buff *skb, struct sta_info *sta, + int tid, int link_id, enum nl80211_band band); static inline bool ieee80211_require_encrypted_assoc(__le16 fc, @@ -2472,29 +2481,26 @@ static inline bool ieee80211_require_encrypted_assoc(__le16 fc, return sta && sta->sta.epp_peer && ieee80211_is_assoc(fc); } -/* sta_out needs to be checked for ERR_PTR() before using */ -int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, - struct sta_info **sta_out); - static inline void ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, int tid, enum nl80211_band band) { rcu_read_lock(); - __ieee80211_tx_skb_tid_band(sdata, skb, tid, -1, band); + __ieee80211_tx_skb_tid_band(sdata, skb, ERR_PTR(-ENOENT), + tid, -1, band); rcu_read_unlock(); } void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, int tid, int link_id); + struct sk_buff *skb, struct sta_info *sta, + int tid, int link_id); static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */ - ieee80211_tx_skb_tid(sdata, skb, 7, -1); + ieee80211_tx_skb_tid(sdata, skb, NULL, 7, -1); } /** diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 889c32fd8de1..8535dd478633 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -632,8 +632,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR; } - ieee80211_adjust_monitor_flags(sdata, -1); } + + ieee80211_adjust_monitor_flags(sdata, -1); break; case NL80211_IFTYPE_NAN: /* Check if any open NAN_DATA interfaces */ @@ -993,7 +994,7 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev, /* reset flags and info before parsing radiotap header */ memset(info, 0, sizeof(*info)); - if (!ieee80211_parse_tx_radiotap(skb, dev, NULL)) + if (!ieee80211_parse_tx_radiotap(skb, dev, NULL, false)) return 0; /* doesn't matter, frame will be dropped */ len_rthdr = ieee80211_get_radiotap_len(skb->data); @@ -1706,11 +1707,8 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local, break; status = IEEE80211_SKB_RXCB(skb); - if (!status->link_valid) - link_sta = &sta->deflink; - else - link_sta = rcu_dereference_protected(sta->link[status->link_id], - lockdep_is_held(&local->hw.wiphy->mtx)); + link_sta = wiphy_dereference(local->hw.wiphy, + sta->link[status->link_id]); if (link_sta) ieee80211_ht_handle_chanwidth_notif(local, sdata, sta, link_sta, chanwidth, diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 6408e8464338..7816395681fa 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1322,12 +1322,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; /* currently no support for HE client where HT has 40 MHz but not HT */ - if (iftd->he_cap.has_he && - iftd->types_mask & (BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_P2P_CLIENT)) && - sband->ht_cap.ht_supported && - sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && - !(iftd->he_cap.he_cap_elem.phy_cap_info[0] & he_40_mhz_cap)) + if (WARN_ON(iftd->he_cap.has_he && + iftd->types_mask & (BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT)) && + sband->ht_cap.ht_supported && + sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && + !(iftd->he_cap.he_cap_elem.phy_cap_info[0] & + he_40_mhz_cap))) return -EINVAL; /* no support for per-band vendor elems with MLO */ diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 03171cf00855..a9c5a3250edb 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -673,7 +673,7 @@ void mesh_fast_tx_flush_addr(struct ieee80211_sub_if_data *sdata, * @sdata: local subif * @dst: destination address of the path (ETH_ALEN length) * - * Returns: 0 on success + * Returns: the new or the existing path on success, an ERR_PTR() on failure * * State: the initial state of the new path is set to 0 */ diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f51167f0fc46..cf3873d8855b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -6347,7 +6347,6 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, } static int ieee80211_mgd_setup_link_sta(struct ieee80211_link_data *link, - struct sta_info *sta, struct link_sta_info *link_sta, struct cfg80211_bss *cbss) { @@ -6362,11 +6361,9 @@ static int ieee80211_mgd_setup_link_sta(struct ieee80211_link_data *link, memcpy(link_sta->addr, cbss->bssid, ETH_ALEN); memcpy(link_sta->pub->addr, cbss->bssid, ETH_ALEN); - /* TODO: S1G Basic Rate Set is expressed elsewhere */ - if (cbss->channel->band == NL80211_BAND_S1GHZ) { - ieee80211_s1g_sta_rate_init(sta); + /* S1G does not use basic rates */ + if (cbss->channel->band == NL80211_BAND_S1GHZ) return 0; - } sband = local->hw.wiphy->bands[cbss->channel->band]; @@ -6516,7 +6513,7 @@ ieee80211_determine_our_sta_mode(struct ieee80211_sub_if_data *sdata, struct ieee80211_sta_ht_cap sta_ht_cap = sband->ht_cap; bool is_5ghz = sband->band == NL80211_BAND_5GHZ; bool is_6ghz = sband->band == NL80211_BAND_6GHZ; - const struct ieee80211_sta_he_cap *he_cap; + const struct ieee80211_sta_he_cap *he_cap = NULL; const struct ieee80211_sta_eht_cap *eht_cap; const struct ieee80211_sta_uhr_cap *uhr_cap; struct ieee80211_sta_vht_cap vht_cap; @@ -6580,7 +6577,15 @@ ieee80211_determine_our_sta_mode(struct ieee80211_sub_if_data *sdata, goto out; } - if (vht_cap.vht_supported && is_5ghz) { + if (req && req->flags & ASSOC_REQ_DISABLE_HE && !is_6ghz) + mlme_link_id_dbg(sdata, link_id, + "HE disabled by flag, limiting to HT/VHT\n"); + else + he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); + + if (vht_cap.vht_supported && is_5ghz && he_cap) { + /* nothing - since HE we can be 20 MHz-only non-AP STA */ + } else if (vht_cap.vht_supported && is_5ghz) { bool have_80mhz = false; unsigned int i; @@ -6626,17 +6631,11 @@ ieee80211_determine_our_sta_mode(struct ieee80211_sub_if_data *sdata, "no VHT 160 MHz capability on 5 GHz, limiting to 80 MHz"); } - if (req && req->flags & ASSOC_REQ_DISABLE_HE) { - mlme_link_id_dbg(sdata, link_id, - "HE disabled by flag, limiting to HT/VHT\n"); - goto out; - } - - he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); if (!he_cap) { WARN_ON(is_6ghz); - mlme_link_id_dbg(sdata, link_id, - "no HE support, limiting to HT/VHT\n"); + if (!req || !(req->flags & ASSOC_REQ_DISABLE_HE)) + mlme_link_id_dbg(sdata, link_id, + "no HE support, limiting to HT/VHT\n"); goto out; } @@ -7107,7 +7106,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, } } - err = ieee80211_mgd_setup_link_sta(link, sta, link_sta, + err = ieee80211_mgd_setup_link_sta(link, link_sta, assoc_data->link[link_id].bss); if (err) goto out_err; @@ -9699,8 +9698,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, goto out_err; } - err = ieee80211_mgd_setup_link_sta(link, new_sta, - link_sta, cbss); + err = ieee80211_mgd_setup_link_sta(link, link_sta, cbss); if (err) { rcu_read_unlock(); sta_info_free(local, new_sta); @@ -11090,7 +11088,7 @@ ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, goto disconnect; } - if (ieee80211_mgd_setup_link_sta(link, sta, link_sta, + if (ieee80211_mgd_setup_link_sta(link, link_sta, add_links_data->link[link_id].bss)) goto disconnect; @@ -11831,12 +11829,10 @@ void ieee80211_sta_rx_queued_frame(struct ieee80211_sub_if_data *sdata, rx_status = (struct ieee80211_rx_status *) skb->cb; fc = le16_to_cpu(mgmt->frame_control); - if (rx_status->link_valid) { - link = sdata_dereference(sdata->link[rx_status->link_id], - sdata); - if (!link) - return; - } + link = sdata_dereference(sdata->link[rx_status->link_id], + sdata); + if (!link) + return; switch (fc & IEEE80211_FCTL_STYPE) { case IEEE80211_STYPE_BEACON: diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index e30767853c36..4b40b461ca78 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -861,8 +861,10 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, need_offchan = true; rcu_read_lock(); - sta = sta_info_get_bss(sdata, mgmt->da); - mlo_sta = sta && sta->sta.mlo; + if (!params->no_sta) { + sta = sta_info_get_bss(sdata, mgmt->da); + mlo_sta = sta && sta->sta.mlo; + } if (!ieee80211_is_action(mgmt->frame_control) || mgmt->u.action.category == WLAN_CATEGORY_PUBLIC || @@ -891,7 +893,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, local->ops->remain_on_channel && memcmp(sdata->vif.cfg.ap_addr, mgmt->bssid, ETH_ALEN))) { need_offchan = true; - } else if (sdata->u.mgd.associated && + } else if (!params->no_sta && + sdata->u.mgd.associated && ether_addr_equal(sdata->vif.cfg.ap_addr, mgmt->da)) { sta = sta_info_get_bss(sdata, mgmt->da); mlo_sta = sta && sta->sta.mlo; @@ -1029,7 +1032,9 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, } if (!need_offchan) { - ieee80211_tx_skb_tid(sdata, skb, 7, link_id); + ieee80211_tx_skb_tid(sdata, skb, + params->no_sta ? ERR_PTR(-ENOENT) : sta, + 7, link_id); ret = 0; goto out_unlock; } diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index e910f03af777..f75257a19064 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -57,7 +57,6 @@ void rate_control_rate_init(struct link_sta_info *link_sta) /* TODO: check for minstrel_s1g ? */ if (sband->band == NL80211_BAND_S1GHZ) { - ieee80211_s1g_sta_rate_init(sta); rcu_read_unlock(); return; } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 5e26be8e27d8..b3990b7a7299 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -222,43 +222,21 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local, } static void __ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata, - int link_id, - struct sta_info *sta, + struct link_sta_info *link_sta, struct sk_buff *skb) { - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - - if (link_id >= 0) { - status->link_valid = 1; - status->link_id = link_id; - } else { - status->link_valid = 0; - } - skb_queue_tail(&sdata->skb_queue, skb); wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work); - if (sta) { - struct link_sta_info *link_sta_info; - - if (link_id >= 0) { - link_sta_info = rcu_dereference(sta->link[link_id]); - if (!link_sta_info) - return; - } else { - link_sta_info = &sta->deflink; - } - - link_sta_info->rx_stats.packets++; - } + if (link_sta) + link_sta->rx_stats.packets++; } static void ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata, - int link_id, - struct sta_info *sta, + struct link_sta_info *link_sta, struct sk_buff *skb) { skb->protocol = 0; - __ieee80211_queue_skb_to_iface(sdata, link_id, sta, skb); + __ieee80211_queue_skb_to_iface(sdata, link_sta, skb); } static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata, @@ -301,7 +279,7 @@ static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata, if (!skb) return; - ieee80211_queue_skb_to_iface(sdata, -1, NULL, skb); + ieee80211_queue_skb_to_iface(sdata, NULL, skb); } /* @@ -452,6 +430,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, if (status->band == NL80211_BAND_5GHZ || status->band == NL80211_BAND_6GHZ) channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ; + else if (status->band == NL80211_BAND_S1GHZ) + channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_900MHZ; else if (status->encoding != RX_ENC_LEGACY) channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; else if (rate && rate->flags & IEEE80211_RATE_ERP_G) @@ -1498,7 +1478,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx, /* if this mpdu is fragmented - terminate rx aggregation session */ sc = le16_to_cpu(hdr->seq_ctrl); if (sc & IEEE80211_SCTL_FRAG) { - ieee80211_queue_skb_to_iface(rx->sdata, rx->link_id, NULL, skb); + ieee80211_queue_skb_to_iface(rx->sdata, NULL, skb); return; } @@ -1712,10 +1692,7 @@ static void sta_ps_start(struct sta_info *sta) list_del_init(&txqi->schedule_order); spin_unlock(&local->active_txq_lock[txq->ac]); - if (txq_has_queue(txq)) - set_bit(tid, &sta->txq_buffered_tids); - else - clear_bit(tid, &sta->txq_buffered_tids); + assign_bit(tid, &sta->txq_buffered_tids, txq_has_queue(txq)); } sta_info_recalc_tim(sta); @@ -1744,7 +1721,7 @@ static void sta_ps_end(struct sta_info *sta) ieee80211_sta_ps_deliver_wakeup(sta); } -int ieee80211_sta_ps_transition(struct ieee80211_sta *pubsta, bool start) +void ieee80211_sta_ps_transition(struct ieee80211_sta *pubsta, bool start) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); bool in_ps; @@ -1753,15 +1730,13 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *pubsta, bool start) /* Don't let the same PS state be set twice */ in_ps = test_sta_flag(sta, WLAN_STA_PS_STA); - if ((start && in_ps) || (!start && !in_ps)) - return -EINVAL; + if (start == in_ps) + return; if (start) sta_ps_start(sta); else sta_ps_end(sta); - - return 0; } EXPORT_SYMBOL(ieee80211_sta_ps_transition); @@ -2524,7 +2499,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) out: ieee80211_led_rx(rx->local); - if (rx->sta) + if (rx->link_sta) rx->link_sta->rx_stats.packets++; return RX_CONTINUE; } @@ -3335,8 +3310,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST || tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) { rx->skb->protocol = cpu_to_be16(ETH_P_TDLS); - __ieee80211_queue_skb_to_iface(sdata, rx->link_id, - rx->sta, rx->skb); + __ieee80211_queue_skb_to_iface(sdata, rx->link_sta, + rx->skb); return RX_QUEUED; } } @@ -3991,7 +3966,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) return RX_QUEUED; queue: - ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb); + ieee80211_queue_skb_to_iface(sdata, rx->link_sta, rx->skb); return RX_QUEUED; } @@ -4005,6 +3980,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) .len = rx->skb->len, .link_id = rx->link_id, .have_link_id = rx->link_id >= 0, + .no_sta = !rx->sta, }; /* skip known-bad action frames and return them in the next handler */ @@ -4127,7 +4103,7 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx) local->hw.offchannel_tx_hw_queue; } - __ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7, -1, + __ieee80211_tx_skb_tid_band(rx->sdata, nskb, rx->sta, 7, -1, status->band); } @@ -4147,7 +4123,7 @@ ieee80211_rx_h_ext(struct ieee80211_rx_data *rx) return RX_DROP_U_UNEXPECTED_EXT_FRAME; /* for now only beacons are ext, so queue them */ - ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb); + ieee80211_queue_skb_to_iface(sdata, rx->link_sta, rx->skb); return RX_QUEUED; } @@ -4204,7 +4180,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) return RX_DROP_U_UNHANDLED_MGMT_STYPE; } - ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb); + ieee80211_queue_skb_to_iface(sdata, rx->link_sta, rx->skb); return RX_QUEUED; } @@ -4248,12 +4224,28 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx, spin_lock_bh(&rx->local->rx_path_lock); while ((skb = __skb_dequeue(frames))) { + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + /* - * all the other fields are valid across frames - * that belong to an aMPDU since they are on the - * same TID from the same station + * Most fields are valid across frames. However, + * in the case of reordering frames may have arrived + * on different links, so adjust the link_id, link + * and link_sta accordingly. */ rx->skb = skb; + if (ieee80211_vif_is_mld(&rx->sdata->vif) && + unlikely(rx->link_id != status->link_id)) { + if (rx->sta) { + rx->link_sta = + rcu_dereference(rx->sta->link[rx->link_id]); + + /* Link got disabled, just use deflink */ + if (!rx->link_sta) + rx->link_sta = &rx->sta->deflink; + } + rx->link_id = status->link_id; + rx->link = rcu_dereference(rx->sdata->link[status->link_id]); + } if (WARN_ON_ONCE(!rx->link)) { res = RX_DROP_U_NO_LINK; @@ -4323,7 +4315,34 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx) static bool ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id) { - return !!(sta->valid_links & BIT(link_id)); + if (sta->mlo) + return sta->valid_links & BIT(link_id); + + return sta->deflink.link_id == link_id; +} + +static bool ieee80211_rx_data_set_link_sta(struct ieee80211_rx_data *rx, + struct link_sta_info *link_sta) +{ + struct sta_info *sta; + + if (WARN_ON_ONCE(!link_sta) || + !ieee80211_rx_is_valid_sta_link_id(&link_sta->sta->sta, + link_sta->link_id)) + return false; + + sta = link_sta->sta; + + rx->sta = sta; + rx->local = sta->sdata->local; + rx->link = rcu_dereference(sta->sdata->link[link_sta->link_id]); + if (!rx->link) + return false; + + rx->link_id = rx->link->link_id; + rx->link_sta = link_sta; + + return true; } static bool ieee80211_rx_data_set_link(struct ieee80211_rx_data *rx, @@ -4360,11 +4379,13 @@ static bool ieee80211_rx_data_set_sta(struct ieee80211_rx_data *rx, if (link_id < 0) { if (ieee80211_vif_is_mld(&rx->sdata->vif) && - sta && !sta->sta.valid_links) + sta && !sta->sta.valid_links) { rx->link = rcu_dereference(rx->sdata->link[sta->deflink.link_id]); - else + rx->link_sta = &sta->deflink; + } else { rx->link = &rx->sdata->deflink; + } } else if (!ieee80211_rx_data_set_link(rx, link_id)) { return false; } @@ -4387,7 +4408,7 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid) struct tid_ampdu_rx *tid_agg_rx; int link_id = -1; - /* FIXME: statistics won't be right with this */ + /* NOTE: the correct link STA for the frame is set later */ if (sta->sta.valid_links) link_id = ffs(sta->sta.valid_links) - 1; @@ -4914,20 +4935,14 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx, { struct ieee80211_sta_rx_stats *stats; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); - struct sta_info *sta = rx->sta; - struct link_sta_info *link_sta; + struct link_sta_info *link_sta = rx->link_sta; struct sk_buff *skb = rx->skb; void *sa = skb->data + ETH_ALEN; void *da = skb->data; - if (rx->link_id >= 0) { - link_sta = rcu_dereference(sta->link[rx->link_id]); - if (WARN_ON_ONCE(!link_sta)) { - dev_kfree_skb(rx->skb); - return; - } - } else { - link_sta = &sta->deflink; + if (WARN_ON_ONCE(!link_sta)) { + dev_kfree_skb(rx->skb); + return; } stats = &link_sta->rx_stats; @@ -5154,6 +5169,9 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx, goto drop; } + status = IEEE80211_SKB_RXCB(skb); + status->link_id = rx->link_id < 0 ? 0 : rx->link_id; + ieee80211_rx_8023(rx, fast_rx, orig_len); return true; @@ -5178,6 +5196,7 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, struct ieee80211_hdr *hdr = (void *)skb->data; struct link_sta_info *link_sta = rx->link_sta; struct ieee80211_link_data *link = rx->link; + struct ieee80211_rx_status *status; rx->skb = skb; @@ -5221,6 +5240,9 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, hdr = (struct ieee80211_hdr *)rx->skb->data; } + status = IEEE80211_SKB_RXCB(rx->skb); + status->link_id = rx->link_id < 0 ? 0 : rx->link_id; + if (ieee80211_is_s1g_beacon(hdr->frame_control)) { ieee80211_invoke_rx_handlers(rx); return true; @@ -5256,22 +5278,19 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx, } static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw, - struct ieee80211_sta *pubsta, + struct ieee80211_link_sta *link_pubsta, struct sk_buff *skb, struct list_head *list) { struct ieee80211_local *local = hw_to_local(hw); - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_fast_rx *fast_rx; struct ieee80211_rx_data rx; struct sta_info *sta; - int link_id = -1; memset(&rx, 0, sizeof(rx)); rx.skb = skb; rx.local = local; rx.list = list; - rx.link_id = -1; I802_DEBUG_INC(local->dot11ReceivedFragmentCount); @@ -5279,23 +5298,15 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw, if (skb->len < sizeof(struct ethhdr)) goto drop; - if (!pubsta) + if (!link_pubsta) goto drop; - if (status->link_valid) - link_id = status->link_id; - - /* - * TODO: Should the frame be dropped if the right link_id is not - * available? Or may be it is fine in the current form to proceed with - * the frame processing because with frame being in 802.3 format, - * link_id is used only for stats purpose and updating the stats on - * the deflink is fine? - */ - sta = container_of(pubsta, struct sta_info, sta); - if (!ieee80211_rx_data_set_sta(&rx, sta, link_id)) + sta = container_of(link_pubsta->sta, struct sta_info, sta); + if (!ieee80211_rx_data_set_sta(&rx, sta, link_pubsta->link_id)) goto drop; + rx.link_id = link_pubsta->link_id; + fast_rx = rcu_dereference(rx.sta->fast_rx); if (!fast_rx) goto drop; @@ -5307,59 +5318,19 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw, dev_kfree_skb(skb); } -static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx, - struct sk_buff *skb, bool consume) +static bool ieee80211_rx_valid_freq(int freq, struct ieee80211_link_data *link) { - struct link_sta_info *link_sta; - struct ieee80211_hdr *hdr = (void *)skb->data; - struct sta_info *sta; - int link_id = -1; + struct ieee80211_chanctx_conf *conf; - if (ieee80211_is_s1g_beacon(hdr->frame_control)) { - if (!ieee80211_rx_data_set_sta(rx, NULL, -1)) - return false; + if (!freq || link->sdata->vif.type == NL80211_IFTYPE_NAN || + link->sdata->vif.type == NL80211_IFTYPE_NAN_DATA) + return true; - return ieee80211_prepare_and_rx_handle(rx, skb, consume); - } - - /* - * Look up link station first, in case there's a - * chance that they might have a link address that - * is identical to the MLD address, that way we'll - * have the link information if needed. - */ - link_sta = link_sta_info_get_bss(rx->sdata, hdr->addr2); - if (link_sta) { - sta = link_sta->sta; - link_id = link_sta->link_id; - } else { - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - - sta = sta_info_get_bss(rx->sdata, hdr->addr2); - if (status->link_valid) { - link_id = status->link_id; - } else if (ieee80211_vif_is_mld(&rx->sdata->vif) && - status->freq) { - struct ieee80211_link_data *link; - struct ieee80211_chanctx_conf *conf; - - for_each_link_data_rcu(rx->sdata, link) { - conf = rcu_dereference(link->conf->chanctx_conf); - if (!conf || !conf->def.chan) - continue; - - if (status->freq == conf->def.chan->center_freq) { - link_id = link->link_id; - break; - } - } - } - } - - if (!ieee80211_rx_data_set_sta(rx, sta, link_id)) + conf = rcu_dereference(link->conf->chanctx_conf); + if (!conf || !conf->def.chan) return false; - return ieee80211_prepare_and_rx_handle(rx, skb, consume); + return freq == conf->def.chan->center_freq; } /* @@ -5367,18 +5338,20 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx, * be called with rcu_read_lock protection. */ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, - struct ieee80211_sta *pubsta, + struct ieee80211_link_sta *link_pubsta, struct sk_buff *skb, struct list_head *list) { struct ieee80211_local *local = hw_to_local(hw); - struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_sub_if_data *sdata; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr; + struct link_sta_info *link_sta; + struct sta_info *sta; __le16 fc; struct ieee80211_rx_data rx; - struct ieee80211_sub_if_data *prev; struct rhlist_head *tmp; + bool rx_data_pending; int err = 0; fc = ((struct ieee80211_hdr *)skb->data)->frame_control; @@ -5386,7 +5359,6 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, rx.skb = skb; rx.local = local; rx.list = list; - rx.link_id = -1; if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc)) I802_DEBUG_INC(local->dot11ReceivedFragmentCount); @@ -5426,97 +5398,102 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, ieee80211_is_s1g_beacon(hdr->frame_control))) ieee80211_scan_rx(local, skb); + /* + * RX of a data frame should only happen to existing stations. + * It is therefore more efficient to use the one provided by the driver + * or search based on the station's address. + * + * Note we will fall through and handle a spurious data frame in the + * same way as a management frame. + */ if (ieee80211_is_data(fc)) { - struct sta_info *sta, *prev_sta; - int link_id = -1; + if (link_pubsta) { + sta = container_of(link_pubsta->sta, struct sta_info, + sta); + link_sta = rcu_dereference(sta->link[link_pubsta->link_id]); - if (status->link_valid) - link_id = status->link_id; - - if (pubsta) { - sta = container_of(pubsta, struct sta_info, sta); - if (!ieee80211_rx_data_set_sta(&rx, sta, link_id)) - goto out; - - /* - * In MLO connection, fetch the link_id using addr2 - * when the driver does not pass link_id in status. - * When the address translation is already performed by - * driver/hw, the valid link_id must be passed in - * status. - */ - - if (!status->link_valid && pubsta->mlo) { - struct link_sta_info *link_sta; - - link_sta = link_sta_info_get_bss(rx.sdata, - hdr->addr2); - if (!link_sta) - goto out; - - if (!ieee80211_rx_data_set_link(&rx, - link_sta->link_id)) - goto out; - } - - if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) + rx.sdata = sta->sdata; + if (ieee80211_rx_data_set_link_sta(&rx, link_sta) && + ieee80211_prepare_and_rx_handle(&rx, skb, true)) return; + goto out; } - prev_sta = NULL; + rx_data_pending = false; + /* Search for stations on non-MLD interfaces */ for_each_sta_info(local, hdr->addr2, sta, tmp) { - if (!prev_sta) { - prev_sta = sta; + struct ieee80211_link_data *link; + + if (ieee80211_vif_is_mld(&sta->sdata->vif)) continue; + + link = &sta->sdata->deflink; + if (!ieee80211_rx_valid_freq(status->freq, link)) + continue; + + if (rx_data_pending) { + ieee80211_prepare_and_rx_handle(&rx, skb, + false); + rx_data_pending = false; } - rx.sdata = prev_sta->sdata; - if (!status->link_valid && prev_sta->sta.mlo) { - struct link_sta_info *link_sta; + rx.sdata = sta->sdata; + if (!ieee80211_rx_data_set_link_sta(&rx, &sta->deflink)) + continue; - link_sta = link_sta_info_get_bss(rx.sdata, - hdr->addr2); - if (!link_sta) - continue; - - link_id = link_sta->link_id; - } - - if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id)) - goto out; - - ieee80211_prepare_and_rx_handle(&rx, skb, false); - - prev_sta = sta; + rx_data_pending = true; } - if (prev_sta) { - rx.sdata = prev_sta->sdata; - if (!status->link_valid && prev_sta->sta.mlo) { - struct link_sta_info *link_sta; + /* And search stations on MLD interfaces */ + for_each_link_sta_info(local, hdr->addr2, link_sta, tmp) { + struct ieee80211_link_data *link; - link_sta = link_sta_info_get_bss(rx.sdata, - hdr->addr2); - if (!link_sta) - goto out; + sta = link_sta->sta; + sdata = sta->sdata; + link = rcu_dereference(sdata->link[link_sta->link_id]); - link_id = link_sta->link_id; + if (!ieee80211_rx_valid_freq(status->freq, link)) + continue; + + if (rx_data_pending) { + ieee80211_prepare_and_rx_handle(&rx, skb, + false); + rx_data_pending = false; } - if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id)) - goto out; + rx.sdata = sta->sdata; + if (!ieee80211_rx_data_set_link_sta(&rx, link_sta)) + continue; + rx_data_pending = true; + } + + if (rx_data_pending) { if (ieee80211_prepare_and_rx_handle(&rx, skb, true)) return; + goto out; } + + /* fall through, e.g. for spurious frame notification */ } - prev = NULL; + /* + * This is a management frame (or a data frame without a station) and + * it will be delivered independent of whether a station exists, + * so iterate the interfaces. + */ + rx_data_pending = false; + + /* We expect the driver to provide frequency information */ + if (WARN_ON_ONCE(!local->emulate_chanctx && !status->freq)) + goto out; list_for_each_entry_rcu(sdata, &local->interfaces, list) { + struct ieee80211_link_data *link = NULL; + if (!ieee80211_sdata_running(sdata)) continue; @@ -5524,29 +5501,84 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, sdata->vif.type == NL80211_IFTYPE_AP_VLAN) continue; - /* - * frame is destined for this interface, but if it's - * not also for the previous one we handle that after - * the loop to avoid copying the SKB once too much - */ + link_sta = NULL; + link = NULL; + + /* Try to resolve the station */ + if (!ieee80211_vif_is_mld(&sdata->vif)) { + sta = sta_info_get_bss(sdata, hdr->addr2); + + if (sta) { + link_sta = &sta->deflink; + link = &sdata->deflink; + } + } else { + link_sta = link_sta_info_get_bss(sdata, hdr->addr2); + + if (link_sta) + link = rcu_dereference(sdata->link[link_sta->link_id]); + } + + /* + * If we found a STA and it has a valid link information, then + * RX using the station. + */ + if (link_sta && link && + ieee80211_rx_valid_freq(status->freq, link)) { + if (rx_data_pending) { + ieee80211_prepare_and_rx_handle(&rx, skb, false); + rx_data_pending = false; + } + + /* No valid_links check as we need to RX beacons */ + + rx.sdata = sdata; + if (ieee80211_rx_data_set_link_sta(&rx, link_sta)) + rx_data_pending = true; - if (!prev) { - prev = sdata; continue; } - rx.sdata = prev; - ieee80211_rx_for_interface(&rx, skb, false); + /* No station, try to resolve the link and RX */ + if (ieee80211_vif_is_mld(&sdata->vif)) { + struct ieee80211_chanctx_conf *conf; + bool found = false; - prev = sdata; + for_each_link_data_rcu(sdata, link) { + conf = rcu_dereference(link->conf->chanctx_conf); + if (!conf || !conf->def.chan) + continue; + + if (status->freq == conf->def.chan->center_freq) { + found = true; + break; + } + } + + if (!found) + link = &sdata->deflink; + } else { + link = &sdata->deflink; + } + + if (rx_data_pending) { + ieee80211_prepare_and_rx_handle(&rx, skb, false); + rx_data_pending = false; + } + + rx.sdata = sdata; + rx.local = sdata->local; + rx.link = link; + rx.link_id = link->link_id; + rx.sta = NULL; + rx.link_sta = NULL; + + rx_data_pending = true; } - if (prev) { - rx.sdata = prev; - - if (ieee80211_rx_for_interface(&rx, skb, true)) - return; - } + if (rx_data_pending && + ieee80211_prepare_and_rx_handle(&rx, skb, true)) + return; out: dev_kfree_skb(skb); @@ -5556,7 +5588,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, * This is the receive path handler. It is called by a low level driver when an * 802.11 MPDU is received from the hardware. */ -void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, +void ieee80211_rx_list(struct ieee80211_hw *hw, + struct ieee80211_link_sta *link_pubsta, struct sk_buff *skb, struct list_head *list) { struct ieee80211_local *local = hw_to_local(hw); @@ -5694,9 +5727,6 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, } } - if (WARN_ON_ONCE(status->link_id >= IEEE80211_LINK_UNSPECIFIED)) - goto drop; - status->rx_flags = 0; kcov_remote_start_common(skb_get_kcov_handle(skb)); @@ -5715,9 +5745,10 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, ieee80211_tpt_led_trig_rx(local, skb->len); if (status->flag & RX_FLAG_8023) - __ieee80211_rx_handle_8023(hw, pubsta, skb, list); + __ieee80211_rx_handle_8023(hw, link_pubsta, skb, list); else - __ieee80211_rx_handle_packet(hw, pubsta, skb, list); + __ieee80211_rx_handle_packet(hw, link_pubsta, skb, + list); } kcov_remote_stop(); @@ -5727,7 +5758,8 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, } EXPORT_SYMBOL(ieee80211_rx_list); -void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, +void ieee80211_rx_napi(struct ieee80211_hw *hw, + struct ieee80211_link_sta *link_pubsta, struct sk_buff *skb, struct napi_struct *napi) { struct sk_buff *tmp; @@ -5740,7 +5772,7 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta, * receive processing */ rcu_read_lock(); - ieee80211_rx_list(hw, pubsta, skb, &list); + ieee80211_rx_list(hw, link_pubsta, skb, &list); rcu_read_unlock(); if (!napi) { diff --git a/net/mac80211/s1g.c b/net/mac80211/s1g.c index bd7baf481836..4b70afa6e6d7 100644 --- a/net/mac80211/s1g.c +++ b/net/mac80211/s1g.c @@ -9,14 +9,6 @@ #include "ieee80211_i.h" #include "driver-ops.h" -void ieee80211_s1g_sta_rate_init(struct sta_info *sta) -{ - /* avoid indicating legacy bitrates for S1G STAs */ - sta->deflink.tx_stats.last_rate.flags |= IEEE80211_TX_RC_S1G_MCS; - sta->deflink.rx_stats.last_rate = - STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_S1G); -} - bool ieee80211_s1g_is_twt_setup(struct sk_buff *skb) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 8e950ef6d1ea..a4bca412e577 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -9,7 +9,7 @@ * Copyright 2007, Michael Wu * Copyright 2013-2015 Intel Mobile Communications GmbH * Copyright 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2025 Intel Corporation + * Copyright (C) 2018-2026 Intel Corporation */ #include @@ -204,12 +204,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local, * an indication on which of the links the frame was received */ if (ieee80211_vif_is_mld(&scan_sdata->vif)) { - if (rx_status->link_valid) { - s8 link_id = rx_status->link_id; + s8 link_id = rx_status->link_id; - link_conf = - rcu_dereference(scan_sdata->vif.link_conf[link_id]); - } + link_conf = + rcu_dereference(scan_sdata->vif.link_conf[link_id]); } else { link_conf = &scan_sdata->vif.bss_conf; } diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index f663d28d9209..68bd64328937 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -1066,7 +1066,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, } if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) { - ieee80211_tx_skb_tid(sdata, skb, 7, link_id); + ieee80211_tx_skb_tid(sdata, skb, sta, 7, link_id); return 0; } @@ -1121,7 +1121,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, /* disable bottom halves when entering the Tx path */ local_bh_disable(); __ieee80211_subif_start_xmit(skb, dev, flags, - IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, 0); + IEEE80211_TX_CTRL_MLO_LINK_UNSPEC); local_bh_enable(); return ret; diff --git a/net/mac80211/tests/chan-mode.c b/net/mac80211/tests/chan-mode.c index ab7d38ef6a3a..85dc87215d23 100644 --- a/net/mac80211/tests/chan-mode.c +++ b/net/mac80211/tests/chan-mode.c @@ -219,10 +219,8 @@ static void test_determine_chan_mode(struct kunit *test) /* To force EHT downgrade to HE on punctured 80 MHz downgraded to 40 MHz */ set_bit(IEEE80211_HW_DISALLOW_PUNCTURING, t_sdata->local.hw.flags); - if (params->strict) - set_bit(IEEE80211_HW_STRICT, t_sdata->local.hw.flags); - else - clear_bit(IEEE80211_HW_STRICT, t_sdata->local.hw.flags); + assign_bit(IEEE80211_HW_STRICT, t_sdata->local.hw.flags, + params->strict); t_sdata->sdata->u.mgd.ht_capa_mask = params->ht_capa_mask; t_sdata->sdata->u.mgd.vht_capa = params->vht_capa; diff --git a/net/mac80211/tests/util.c b/net/mac80211/tests/util.c index 0b01264af577..89ff4f389de3 100644 --- a/net/mac80211/tests/util.c +++ b/net/mac80211/tests/util.c @@ -2,7 +2,7 @@ /* * Utilities for mac80211 unit testing * - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024, 2026 Intel Corporation */ #include #include @@ -226,10 +226,6 @@ int t_sdata_init(struct kunit_resource *resource, void *ctx) sband = t_sdata->wiphy->bands[band]; sband->band = band; - sband->bitrates = - kmemdup(bitrates, sizeof(bitrates), GFP_KERNEL); - sband->n_bitrates = ARRAY_SIZE(bitrates); - /* Initialize channels, feel free to add more channels/bands */ switch (band) { case NL80211_BAND_2GHZ: diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 814399989b5e..83b758724edf 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -269,7 +269,6 @@ ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx) static ieee80211_tx_result debug_noinline ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); bool assoc = false; @@ -277,20 +276,6 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) return TX_CONTINUE; - if (unlikely(test_bit(SCAN_SW_SCANNING, &tx->local->scanning)) && - test_bit(SDATA_STATE_OFFCHANNEL, &tx->sdata->state) && - !ieee80211_is_probe_req(hdr->frame_control) && - !ieee80211_is_any_nullfunc(hdr->frame_control)) - /* - * When software scanning only nullfunc frames (to notify - * the sleep state to the AP) and probe requests (for the - * active scan) are allowed, all other frames should not be - * sent and we should not get here, but if we do - * nonetheless, drop them to avoid sending them - * off-channel. See __ieee80211_start_scan() for more. - */ - return TX_DROP; - if (tx->sdata->vif.type == NL80211_IFTYPE_OCB) return TX_CONTINUE; @@ -589,6 +574,43 @@ ieee80211_select_link_key(struct ieee80211_tx_data *tx) return NULL; } +/* + * An 802.3 frame is always a data frame, so there are no 802.11 addresses to + * look at: with a station the pairwise key is all that can apply, without one + * the frame is group addressed. On an MLD the driver has to pick the group key + * itself, as the same frame goes out on several links that each have their own. + */ +static ieee80211_tx_result +ieee80211_select_key_8023(struct ieee80211_tx_data *tx) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); + + if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) + return TX_CONTINUE; + + if (tx->sta) { + tx->key = rcu_dereference(tx->sta->ptk[tx->sta->ptk_idx]); + if (!tx->key) + tx->key = rcu_dereference(tx->sdata->default_unicast_key); + + if (!tx->key && test_sta_flag(tx->sta, WLAN_STA_USES_ENCRYPTION)) + return TX_DROP; + } else if (!ieee80211_vif_is_mld(&tx->sdata->vif)) { + tx->key = rcu_dereference(tx->sdata->deflink.default_multicast_key); + } + + if (!tx->key) + return TX_CONTINUE; + + if (unlikely(tx->key->flags & KEY_FLAG_TAINTED)) + return TX_DROP; + + if (tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) + info->control.hw_key = &tx->key->conf; + + return TX_CONTINUE; +} + static ieee80211_tx_result debug_noinline ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) { @@ -596,6 +618,9 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; + if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) + return ieee80211_select_key_8023(tx); + if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT)) { tx->key = NULL; return TX_CONTINUE; @@ -612,12 +637,6 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) else tx->key = NULL; - if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) { - if (tx->key && tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) - info->control.hw_key = &tx->key->conf; - return TX_CONTINUE; - } - if (tx->key) { bool skip_hw = false; @@ -807,6 +826,33 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) return TX_CONTINUE; } +static ieee80211_tx_result debug_noinline +ieee80211_tx_h_check_offchannel(struct ieee80211_tx_data *tx) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); + + if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) + return TX_CONTINUE; + + if (unlikely(test_bit(SCAN_SW_SCANNING, &tx->local->scanning)) && + test_bit(SDATA_STATE_OFFCHANNEL, &tx->sdata->state) && + !ieee80211_is_probe_req(hdr->frame_control) && + !ieee80211_is_any_nullfunc(hdr->frame_control)) { + /* + * When software scanning only nullfunc frames (to notify + * the sleep state to the AP) and probe requests (for the + * active scan) are allowed, all other frames should not be + * sent and we should not get here, but if we do + * nonetheless, drop them to avoid sending them + * off-channel. See __ieee80211_start_scan() for more. + */ + return TX_DROP; + } + + return TX_CONTINUE; +} + static __le16 ieee80211_tx_next_seq(struct sta_info *sta, int tid) { u16 *seq = &sta->tid_seq[tid]; @@ -1873,6 +1919,7 @@ static int invoke_tx_handlers_late(struct ieee80211_tx_data *tx) goto txh_done; } + CALL_TXH(ieee80211_tx_h_check_offchannel); CALL_TXH(ieee80211_tx_h_michael_mic_add); CALL_TXH(ieee80211_tx_h_sequence); CALL_TXH(ieee80211_tx_h_fragment); @@ -2127,7 +2174,8 @@ static bool ieee80211_rate_bw_usable(u16 rate_flags, bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, struct net_device *dev, - const struct cfg80211_chan_def *chandef) + const struct cfg80211_chan_def *chandef, + bool trim_fcs) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_radiotap_iterator iterator; @@ -2184,7 +2232,8 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, if (skb->len < (iterator._max_length + FCS_LEN)) return false; - skb_trim(skb, skb->len - FCS_LEN); + if (trim_fcs) + skb_trim(skb, skb->len - FCS_LEN); } if (*iterator.this_arg & IEEE80211_RADIOTAP_F_WEP) info->flags &= ~IEEE80211_TX_INTFL_DONT_ENCRYPT; @@ -2503,7 +2552,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, * selected chandef above to accurately set injection rates and * retransmissions. */ - if (!ieee80211_parse_tx_radiotap(skb, dev, chandef)) + if (!ieee80211_parse_tx_radiotap(skb, dev, chandef, true)) goto fail_rcu; /* remove the injection radiotap header */ @@ -2530,9 +2579,16 @@ static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb) skb->data[14] == WLAN_TDLS_SNAP_RFTYPE; } -int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, - struct sta_info **sta_out) +/* + * Returns an error if the frame should be dropped, otherwise + * the *sta_out pointer is filled: + * - valid sta pointer: frame goes to that station + * - NULL: frame will be unicast to a yet unknown station + * - ERR_PTR(-ENOENT): frame will be group addressed + */ +static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, + struct sta_info **sta_out) { struct sta_info *sta; @@ -2557,6 +2613,10 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: + if (is_multicast_ether_addr(skb->data)) { + *sta_out = ERR_PTR(-ENOENT); + return 0; + } /* determined much later */ *sta_out = NULL; return 0; @@ -2599,17 +2659,15 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, return -EINVAL; } - *sta_out = sta ?: ERR_PTR(-ENOENT); + *sta_out = sta; return 0; } -static u16 ieee80211_store_ack_skb(struct ieee80211_local *local, - struct sk_buff *skb, - u32 *info_flags, - u64 cookie) +static void ieee80211_store_ack_skb(struct ieee80211_local *local, + struct sk_buff *skb, u64 cookie) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct sk_buff *ack_skb; - u16 info_id = 0; if (skb->sk) ack_skb = skb_clone_sk(skb); @@ -2626,58 +2684,41 @@ static u16 ieee80211_store_ack_skb(struct ieee80211_local *local, spin_unlock_irqrestore(&local->ack_status_lock, flags); if (id >= 0) { - info_id = id; - *info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + info->status_data = id; + info->status_data_idr = 1; + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; if (cookie) IEEE80211_SKB_CB(ack_skb)->ack.cookie = cookie; } else { kfree_skb(ack_skb); } } - - return info_id; -} - -static void ieee80211_remove_ack_skb(struct ieee80211_local *local, u16 info_id) -{ - struct sk_buff *ack_skb; - unsigned long flags; - - spin_lock_irqsave(&local->ack_status_lock, flags); - ack_skb = idr_remove(&local->ack_status_frames, info_id); - spin_unlock_irqrestore(&local->ack_status_lock, flags); - - kfree_skb(ack_skb); } /** * ieee80211_build_hdr - build 802.11 header in the given frame * @sdata: virtual interface to build the header for * @skb: the skb to build the header in - * @info_flags: skb flags to set * @sta: the station pointer - * @ctrl_flags: info control flags to set - * @cookie: cookie pointer to fill (if not %NULL) * * This function takes the skb with 802.3 header and reformats the header to * the appropriate IEEE 802.11 header based on which interface the packet is * being transmitted on. * - * Note that this function also takes care of the TX status request and - * potential unsharing of the SKB - this needs to be interleaved with the - * header building. + * Note that this function also takes care of the TX status request. The skb + * must not be shared, and its TX info must already be initialised - this + * function only fills in what it determines itself. * * The function requires the read-side RCU lock held * * Returns: the (possibly reallocated) skb or an ERR_PTR() code */ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, u32 info_flags, - struct sta_info *sta, u32 ctrl_flags, - u64 cookie) + struct sk_buff *skb, + struct sta_info *sta) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_local *local = sdata->local; - struct ieee80211_tx_info *info; int head_need; u16 ethertype, hdrlen, meshhdrlen = 0; __le16 fc; @@ -2689,18 +2730,18 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, bool wme_sta = false, authorized = false; bool tdls_peer; bool multicast; - u16 info_id = 0; struct ieee80211_chanctx_conf *chanctx_conf = NULL; enum nl80211_band band; int ret; - u8 link_id = u32_get_bits(ctrl_flags, IEEE80211_TX_CTRL_MLO_LINK); + u8 link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); if (IS_ERR(sta)) sta = NULL; #ifdef CONFIG_MAC80211_DEBUGFS if (local->force_tx_status) - info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; #endif /* convert Ethernet header to proper 802.11 header (based on @@ -2841,7 +2882,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, /* For injected frames, fill RA right away as nexthop lookup * will be skipped. */ - if ((ctrl_flags & IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP) && + if ((info->control.flags & IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP) && is_zero_ether_addr(hdr.addr1)) memcpy(hdr.addr1, skb->data, ETH_ALEN); break; @@ -2970,34 +3011,6 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, goto free; } - if (unlikely(!multicast && - (sk_requests_wifi_status(skb->sk) || - ctrl_flags & IEEE80211_TX_CTL_REQ_TX_STATUS))) - info_id = ieee80211_store_ack_skb(local, skb, &info_flags, - cookie); - - /* - * If the skb is shared we need to obtain our own copy. - */ - skb = skb_share_check(skb, GFP_ATOMIC); - if (unlikely(!skb)) { - /* skb_share_check() already freed the skb */ - if (info_id) - ieee80211_remove_ack_skb(local, info_id); - return ERR_PTR(-ENOMEM); - } - - /* set this up so failure paths can clean up ack skb */ - info = IEEE80211_SKB_CB(skb); - memset(info, 0, sizeof(*info)); - - info->flags = info_flags; - if (info_id) { - info->status_data = info_id; - info->status_data_idr = 1; - } - info->band = band; - hdr.frame_control = fc; hdr.duration_id = 0; hdr.seq_ctrl = 0; @@ -3037,7 +3050,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, head_need = max_t(int, 0, head_need); if (ieee80211_skb_resize(sdata, skb, head_need, ENCRYPT_DATA)) { ret = -ENOMEM; - goto free_txskb; + goto free; } } @@ -3064,41 +3077,14 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, skb_reset_mac_header(skb); - if (likely(!cookie)) { - ctrl_flags |= u32_encode_bits(link_id, - IEEE80211_TX_CTRL_MLO_LINK); - } else { - unsigned int pre_conf_link_id; - - /* - * ctrl_flags already have been set by - * ieee80211_tx_control_port(), here - * we just sanity check that - */ - - pre_conf_link_id = u32_get_bits(ctrl_flags, - IEEE80211_TX_CTRL_MLO_LINK); - - if (pre_conf_link_id != link_id && - link_id != IEEE80211_LINK_UNSPECIFIED) { -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - net_info_ratelimited("%s: dropped frame to %pM with bad link ID request (%d vs. %d)\n", - sdata->name, hdr.addr1, - pre_conf_link_id, link_id); -#endif - ret = -EINVAL; - goto free_txskb; - } - } - - info->control.flags = ctrl_flags; + info->band = band; + info->control.flags = + u32_replace_bits(info->control.flags, link_id, + IEEE80211_TX_CTRL_MLO_LINK); return skb; - free_txskb: - ieee80211_free_txskb(&local->hw, skb); - return ERR_PTR(ret); free: - kfree_skb(skb); + ieee80211_free_txskb(&local->hw, skb); return ERR_PTR(ret); } @@ -3905,6 +3891,38 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata, return true; } +static bool ieee80211_drop_unauth_port(struct ieee80211_tx_data *tx) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); + struct ieee80211_hdr *hdr; + + if (likely(test_sta_flag(tx->sta, WLAN_STA_AUTHORIZED))) + return false; + + if (info->flags & IEEE80211_TX_CTL_INJECTED) + return false; + + if (ieee80211_vif_is_mesh(&tx->sdata->vif) || + tx->sdata->vif.type == NL80211_IFTYPE_OCB) + return false; + + /* no need to check forwarding here */ + if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) + return !(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO); + + hdr = (struct ieee80211_hdr *)tx->skb->data; + + if (!ieee80211_is_data_present(hdr->frame_control) || + is_multicast_ether_addr(hdr->addr1)) + return false; + + if ((info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) && + ieee80211_is_our_addr(tx->sdata, hdr->addr2, NULL)) + return false; + + return true; +} + struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) { @@ -3971,20 +3989,8 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, if (txq->sta) { tx.sta = container_of(txq->sta, struct sta_info, sta); - /* - * Drop unicast frames to unauthorised stations unless they are - * injected frames or EAPOL frames from the local station. - */ - if (unlikely(!(info->flags & IEEE80211_TX_CTL_INJECTED) && - 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) && - !test_sta_flag(tx.sta, WLAN_STA_AUTHORIZED) && - (!(info->control.flags & - IEEE80211_TX_CTRL_PORT_CTRL_PROTO) || - !ieee80211_is_our_addr(tx.sdata, hdr->addr2, - NULL)))) { + + if (unlikely(ieee80211_drop_unauth_port(&tx))) { I802_DEBUG_INC(local->tx_handlers_drop_unauth_port); ieee80211_free_txskb(&local->hw, skb); goto begin; @@ -4156,7 +4162,7 @@ struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac) spin_lock_bh(&local->active_txq_lock[ac]); - if (!local->schedule_round[ac]) + if (!local->schedule_open[ac]) goto out; begin: @@ -4381,12 +4387,12 @@ void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac) spin_lock_bh(&local->active_txq_lock[ac]); - if (ieee80211_txq_schedule_airtime_check(local, ac)) { + local->schedule_open[ac] = + ieee80211_txq_schedule_airtime_check(local, ac); + if (local->schedule_open[ac]) { local->schedule_round[ac]++; if (!local->schedule_round[ac]) local->schedule_round[ac]++; - } else { - local->schedule_round[ac] = 0; } spin_unlock_bh(&local->active_txq_lock[ac]); @@ -4396,13 +4402,14 @@ EXPORT_SYMBOL(ieee80211_txq_schedule_start); void __ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev, u32 info_flags, - u32 ctrl_flags, - u64 cookie) + u32 ctrl_flags) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; + struct ieee80211_tx_info *info; struct sta_info *sta; struct sk_buff *next; + bool group_addressed; int len = skb->len; if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) { @@ -4422,6 +4429,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) goto out_free; + group_addressed = IS_ERR(sta); if (IS_ERR(sta)) sta = NULL; @@ -4454,8 +4462,22 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, if (skb->protocol == sdata->control_port_protocol) ctrl_flags |= IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP; - skb = ieee80211_build_hdr(sdata, skb, info_flags, - sta, ctrl_flags, cookie); + skb = skb_share_check(skb, GFP_ATOMIC); + if (unlikely(!skb)) { + kfree_skb_list(next); + goto out; + } + + info = IEEE80211_SKB_CB(skb); + memset(info, 0, sizeof(*info)); + info->flags = info_flags; + info->control.flags = ctrl_flags; + + if (unlikely(!group_addressed && + sk_requests_wifi_status(skb->sk))) + ieee80211_store_ack_skb(local, skb, 0); + + skb = ieee80211_build_hdr(sdata, skb, sta); if (IS_ERR(skb)) { kfree_skb_list(next); goto out; @@ -4592,7 +4614,7 @@ static void ieee80211_mlo_multicast_tx_one(struct ieee80211_sub_if_data *sdata, return; ctrl_flags |= u32_encode_bits(link_id, IEEE80211_TX_CTRL_MLO_LINK); - __ieee80211_subif_start_xmit(out, sdata->dev, 0, ctrl_flags, 0); + __ieee80211_subif_start_xmit(out, sdata->dev, 0, ctrl_flags); } static void ieee80211_mlo_multicast_tx(struct net_device *dev, @@ -4607,7 +4629,7 @@ static void ieee80211_mlo_multicast_tx(struct net_device *dev, ctrl_flags |= u32_encode_bits(__ffs(links), IEEE80211_TX_CTRL_MLO_LINK); - __ieee80211_subif_start_xmit(skb, sdata->dev, 0, ctrl_flags, 0); + __ieee80211_subif_start_xmit(skb, sdata->dev, 0, ctrl_flags); return; } @@ -4648,8 +4670,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, ieee80211_convert_to_unicast(skb, dev, &queue); while ((skb = __skb_dequeue(&queue))) __ieee80211_subif_start_xmit(skb, dev, 0, - IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, - 0); + IEEE80211_TX_CTRL_MLO_LINK_UNSPEC); } else if (ieee80211_vif_is_mld(&sdata->vif) && ((sdata->vif.type == NL80211_IFTYPE_AP && !ieee80211_hw_check(&sdata->local->hw, MLO_MCAST_MULTI_LINK_TX)) || @@ -4659,8 +4680,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, } else { normal: __ieee80211_subif_start_xmit(skb, dev, 0, - IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, - 0); + IEEE80211_TX_CTRL_MLO_LINK_UNSPEC); } return NETDEV_TX_OK; @@ -4740,10 +4760,6 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, queue = ieee80211_select_queue(sdata, sta, skb); skb_set_queue_mapping(skb, queue); - if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) && - test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) - goto out_free; - skb = skb_share_check(skb, GFP_ATOMIC); if (unlikely(!skb)) return; @@ -4758,8 +4774,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { /* fall back to non-offload slow path */ __ieee80211_subif_start_xmit(skb, dev, 0, - IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, - 0); + IEEE80211_TX_CTRL_MLO_LINK_UNSPEC); return; } @@ -4793,12 +4808,8 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, memcpy(IEEE80211_SKB_CB(seg), info, sizeof(*info)); } - if (unlikely(sk_requests_wifi_status(skb->sk))) { - info->status_data = ieee80211_store_ack_skb(local, skb, - &info->flags, 0); - if (info->status_data) - info->status_data_idr = 1; - } + if (unlikely(sk_requests_wifi_status(skb->sk))) + ieee80211_store_ack_skb(local, skb, 0); dev_sw_netstats_tx_add(dev, skbs, len); @@ -4810,11 +4821,6 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, ieee80211_tpt_led_trig_tx(local, len); ieee80211_tx_8023(sdata, skb, sta, false); - - return; - -out_free: - kfree_skb(skb); } static bool ieee80211_check_mcast_offload(struct ieee80211_sub_if_data *sdata, @@ -4927,6 +4933,7 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, .local = sdata->local, .sdata = sdata, }; + struct ieee80211_tx_info *info; struct sta_info *sta; rcu_read_lock(); @@ -4937,8 +4944,12 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, goto out; } - skb = ieee80211_build_hdr(sdata, skb, info_flags, sta, - IEEE80211_TX_CTRL_MLO_LINK_UNSPEC, 0); + info = IEEE80211_SKB_CB(skb); + memset(info, 0, sizeof(*info)); + info->flags = info_flags; + info->control.flags = IEEE80211_TX_CTRL_MLO_LINK_UNSPEC; + + skb = ieee80211_build_hdr(sdata, skb, sta); if (IS_ERR(skb)) goto out; @@ -6467,7 +6478,8 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid) EXPORT_SYMBOL(ieee80211_unreserve_tid); void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, int tid, int link_id, + struct sk_buff *skb, struct sta_info *sta, + int tid, int link_id, enum nl80211_band band) { const struct ieee80211_hdr *hdr = (void *)skb->data; @@ -6519,12 +6531,13 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, */ local_bh_disable(); IEEE80211_SKB_CB(skb)->band = band; - ieee80211_xmit(sdata, NULL, skb); + ieee80211_xmit(sdata, sta, skb); local_bh_enable(); } void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, int tid, int link_id) + struct sk_buff *skb, struct sta_info *sta, + int tid, int link_id) { struct ieee80211_chanctx_conf *chanctx_conf; enum nl80211_band band; @@ -6550,7 +6563,7 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, band = 0; } - __ieee80211_tx_skb_tid_band(sdata, skb, tid, link_id, band); + __ieee80211_tx_skb_tid_band(sdata, skb, sta, tid, link_id, band); rcu_read_unlock(); } @@ -6561,6 +6574,8 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; + struct ieee80211_tx_info *info; + bool group_addressed; struct sta_info *sta; struct sk_buff *skb; struct ethhdr *ehdr; @@ -6585,9 +6600,6 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, if (unencrypted) flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; - if (cookie) - ctrl_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; - flags |= IEEE80211_TX_INTFL_NL80211_FRAME_TX; skb = dev_alloc_skb(local->hw.extra_tx_headroom + @@ -6604,15 +6616,11 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, /* we may override the SA for MLO STA later */ if (link_id < 0) { - ctrl_flags |= u32_encode_bits(IEEE80211_LINK_UNSPECIFIED, - IEEE80211_TX_CTRL_MLO_LINK); + link_id = IEEE80211_LINK_UNSPECIFIED; memcpy(ehdr->h_source, sdata->vif.addr, ETH_ALEN); } else { struct ieee80211_bss_conf *link_conf; - ctrl_flags |= u32_encode_bits(link_id, - IEEE80211_TX_CTRL_MLO_LINK); - rcu_read_lock(); link_conf = rcu_dereference(sdata->vif.link_conf[link_id]); if (!link_conf) { @@ -6631,9 +6639,6 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, skb_reset_network_header(skb); skb_reset_mac_header(skb); - if (local->hw.queues < IEEE80211_NUM_ACS) - goto start_xmit; - /* update QoS header to prioritize control port frames if possible, * prioritization also happens for control port frames send over * AF_PACKET @@ -6646,24 +6651,47 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, return err; } - if (!IS_ERR(sta)) { - u16 queue = ieee80211_select_queue(sdata, sta, skb); + group_addressed = IS_ERR(sta); + if (IS_ERR(sta)) + sta = NULL; - skb_set_queue_mapping(skb, queue); + skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, sta, skb)); - /* - * for MLO STA, the SA should be the AP MLD address, but - * the link ID has been selected already - */ - if (sta && sta->sta.mlo) - memcpy(ehdr->h_source, sdata->vif.addr, ETH_ALEN); + /* + * for MLO STA, the SA should be the AP MLD address, but + * the link ID has been selected already + */ + if (sta && sta->sta.mlo) + memcpy(ehdr->h_source, sdata->vif.addr, ETH_ALEN); + + info = IEEE80211_SKB_CB(skb); + memset(info, 0, sizeof(*info)); + info->flags = flags; + ctrl_flags |= u32_encode_bits(link_id, IEEE80211_TX_CTRL_MLO_LINK); + info->control.flags = ctrl_flags; + + if (!group_addressed) + ieee80211_store_ack_skb(local, skb, cookie); + + skb = ieee80211_build_hdr(sdata, skb, sta); + if (IS_ERR(skb)) { + rcu_read_unlock(); + return PTR_ERR(skb); + } + + if (link_id != u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK)) { + ieee80211_free_txskb(&local->hw, skb); + rcu_read_unlock(); + return -EINVAL; } - rcu_read_unlock(); -start_xmit: local_bh_disable(); - __ieee80211_subif_start_xmit(skb, skb->dev, flags, ctrl_flags, cookie); + dev_sw_netstats_tx_add(dev, 1, skb->len); + ieee80211_tpt_led_trig_tx(local, skb->len); + ieee80211_xmit(sdata, sta, skb); local_bh_enable(); + rcu_read_unlock(); return 0; } @@ -6691,8 +6719,7 @@ int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev, local_bh_disable(); __ieee80211_subif_start_xmit(skb, skb->dev, 0, - IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP, - 0); + IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP); local_bh_enable(); return 0; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index a96078a6bfa2..e2b5b2b21c34 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -47,7 +47,7 @@ struct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy) EXPORT_SYMBOL(wiphy_to_ieee80211_hw); const struct ieee80211_conn_settings ieee80211_conn_settings_unlimited = { - .mode = IEEE80211_CONN_MODE_EHT, + .mode = IEEE80211_CONN_MODE_HIGHEST, .bw_limit = IEEE80211_CONN_BW_LIMIT_320, }; diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 7f6af1790736..1071e823108b 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -568,8 +568,8 @@ int cfg80211_chandef_add_npca(struct wiphy *wiphy, const struct ieee80211_uhr_npca_info *npca) { struct cfg80211_chan_def new_chandef = *chandef; - u32 width, npca_freq; - u8 offs; + u32 npca_freq; + u8 chan; if (chandef->npca_chan || chandef->npca_punctured) return -EINVAL; @@ -589,11 +589,10 @@ int cfg80211_chandef_add_npca(struct wiphy *wiphy, return -EINVAL; } - offs = le32_get_bits(npca->params, - IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN_OFFS); + chan = le32_get_bits(npca->params, + IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN); - width = cfg80211_chandef_get_width(chandef); - npca_freq = chandef->center_freq1 - width / 2 + 10 + 20 * offs; + npca_freq = ieee80211_channel_to_frequency(chan, chandef->chan->band); new_chandef.npca_chan = ieee80211_get_channel(wiphy, npca_freq); if (!new_chandef.npca_chan) return -EINVAL; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9fafd7673d85..85c010793c53 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -569,6 +569,7 @@ static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { [NL80211_TXRATE_EHT_LTF] = NLA_POLICY_RANGE(NLA_U8, NL80211_RATE_INFO_EHT_1XLTF, NL80211_RATE_INFO_EHT_8XLTF), + [NL80211_TXRATE_6GHZ_NON_HT_DUP] = { .type = NLA_FLAG }, }; @@ -876,7 +877,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_HT_CAPABILITY_MASK] = { .len = NL80211_HT_CAPABILITY_LEN }, - [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 }, + [NL80211_ATTR_TID_BITMAP] = { .type = NLA_U16 }, [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, [NL80211_ATTR_WDEV] = { .type = NLA_U64 }, @@ -1096,6 +1097,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_NPCA_PUNCT_BITMAP] = NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range), [NL80211_ATTR_STA_DUMP_LINK_STATS] = { .type = NLA_FLAG }, + [NL80211_ATTR_FRAME_NO_STA] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -5280,13 +5282,13 @@ static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info) struct net_device *dev = info->user_ptr[1]; u16 noack_map; - if (!info->attrs[NL80211_ATTR_NOACK_MAP]) + if (!info->attrs[NL80211_ATTR_TID_BITMAP]) return -EINVAL; if (!rdev->ops->set_noack_map) return -EOPNOTSUPP; - noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]); + noack_map = nla_get_u16(info->attrs[NL80211_ATTR_TID_BITMAP]); return rdev_set_noack_map(rdev, dev, noack_map); } @@ -6303,6 +6305,14 @@ static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, mask->control[band].eht_ltf = nla_get_u8(tb[NL80211_TXRATE_EHT_LTF]); + if (tb[NL80211_TXRATE_6GHZ_NON_HT_DUP]) { + if (band != NL80211_BAND_6GHZ || + (wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO)) + return -EINVAL; + mask->control[band].nonht_dup_6ghz = true; + } + if (mask->control[band].legacy == 0) { /* don't allow empty legacy rates if HT, VHT, HE or EHT * are not even supported. @@ -6344,6 +6354,7 @@ static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev, { u32 count_ht, count_vht, count_he, count_eht, i; u32 rate = beacon_rate->control[band].legacy; + bool nonht_dup = beacon_rate->control[band].nonht_dup_6ghz; /* Allow only one rate */ if (hweight32(rate) > 1) @@ -6405,6 +6416,9 @@ static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev, (!rate && !count_ht && !count_vht && !count_he && !count_eht)) return -EINVAL; + if (nonht_dup && !rate) + return -EINVAL; + if (rate && !wiphy_ext_feature_isset(&rdev->wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY)) @@ -14878,6 +14892,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) !(wdev->valid_links & BIT(params.link_id))) return -EINVAL; + params.no_sta = + nla_get_flag(info->attrs[NL80211_ATTR_FRAME_NO_STA]); + params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]); @@ -18449,6 +18466,7 @@ static int nl80211_set_multicast_to_unicast(struct sk_buff *skb, static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wiphy *wiphy = &rdev->wiphy; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_pmk_conf pmk_conf = {}; @@ -18457,7 +18475,9 @@ static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; - if (!wiphy_ext_feature_isset(&rdev->wiphy, + if (!wiphy_ext_feature_isset(wiphy, + NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD) && + !wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) return -EOPNOTSUPP; @@ -18487,6 +18507,7 @@ static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wiphy *wiphy = &rdev->wiphy; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; const u8 *aa; @@ -18495,7 +18516,9 @@ static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info) wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; - if (!wiphy_ext_feature_isset(&rdev->wiphy, + if (!wiphy_ext_feature_isset(wiphy, + NL80211_EXT_FEATURE_FAST_ROAM_OFFLOAD) && + !wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) return -EOPNOTSUPP; @@ -21538,7 +21561,6 @@ void nl80211_mlo_reconf_add_done(struct net_device *dev, nl80211_send_mlme_event(rdev, dev, &event, GFP_KERNEL); } -EXPORT_SYMBOL(nl80211_mlo_reconf_add_done); void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, @@ -22039,7 +22061,9 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, (info->ack_tstamp && nla_put_u64_64bit(msg, NL80211_ATTR_TX_HW_TIMESTAMP, info->ack_tstamp, - NL80211_ATTR_PAD))) + NL80211_ATTR_PAD)) || + (info->no_sta && + nla_put_flag(msg, NL80211_ATTR_FRAME_NO_STA))) goto nla_put_failure; genlmsg_end(msg, hdr);