More changes, including drivers:

- brcmfmac: FT/OKC roaming offload
  - ath12k: IPQ5332 platform infrastructure
  - cfg80211/mac80211: non-STA TX/RX infrastructure
    for MLO to fix address translation bugs
  - the rest is mostly minor cleanups etc.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEpeA8sTs3M8SN2hR410qiO8sPaAAFAmqxHzUACgkQ10qiO8sP
 aACS2g/9HLt0g35/O74zeEyD1+J37Xu7oXNdbG7CWGPnTqxupvSeNxrLJN8ToSRj
 Gm80BGEuFxF+OhR4ZuvwGd2sUjmprZ6l27CIrqiEIHWrI3+kFyqkOBHKu2DuQ6/m
 QyvXn6tAKwQ9YPOyfpmyVYszujT/TyEmYkPvgX8xeNeTStwUhDCT/G3/NIN2/eyn
 jR5po2eeg3M12V93mTb9usHRkaWN5NIfsUTAuN5noHv4o8v8kbwbTuQiVMjlMIxJ
 5rEen2BidPJuxfSXeYK1JVgEzs1j+JKvHF+Wd1VCE011iZaF3lp8d9CF+Wn15v0Z
 UfvXzrmU3zoQb2bKyCSQPBskHI8r3zDpkxzZoifGBucKsf0lm3pqH2EVGiBpTPga
 OvUp2/4Z75RGKtC9Hao7a6rofPDyZTyiaG+hHtsOs8qTDBSQZGt6sAbv1b4iVziw
 VpZoyocBUyIKqX8SST14kLbqG8crEso08Zy4xE4JbSV7VBl96Qwck4hqmiDL0cAC
 Vuq5Pg/OCN6A2TWvJElnWJiWE96sZ0wF9T00WmMEGPBuyZWVk4RkfCGzQJ1SK/1K
 s8NrFdqqcIsICLNQTO7ofBeaky7/55m0It7OfcvHIQwo1DWUZ//WeMptSKWP2BBb
 rVBgUtPlQhxvuNVNysvkgUnjS7Fh8/18JvE81yX8sIN6DGgduxE=
 =Nzgx
 -----END PGP SIGNATURE-----

Merge tag 'wireless-next-2026-09-21' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next

Johannes Berg says:

====================
More changes, including drivers:
 - brcmfmac: FT/OKC roaming offload
 - ath12k: IPQ5332 platform infrastructure
 - cfg80211/mac80211: non-STA TX/RX infrastructure
   for MLO to fix address translation bugs
 - the rest is mostly minor cleanups etc.

* tag 'wireless-next-2026-09-21' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next: (101 commits)
  dt-bindings: net: wireless: Convert WL1251 to DT schema
  libertas: mesh: remove unused and undocumented sysfs attribute groups
  wifi: mac80211: fix multi-link UHR association
  wifi: nl80211: rename no-ACK bitmap to TID bitmap
  wifi: radiotap: correct EHT TB U-SIG 1 B20:25
  wifi: ieee80211: fix FTM bufferable check
  wifi: mac80211: allow advertising 20 MHz-only non-AP STA
  wifi: mac80211: WARN on 40 MHz HT/HE mismatch
  wifi: mac80211: tests: fix memory leak
  wifi: mac80211: use assign_bit() where applicable
  wifi: mac80211: pass error station if non-STA transmit was requested
  wifi: mac80211: pass station to ieee80211_tx_skb_tid
  wifi: mac80211: report to cfg80211 when no STA is known for a frame
  wifi: cfg80211: add attribute for TX/RX denoting there is no station
  wifi: mac80211: rework RX packet handling
  wifi: mac80211: refactor RX link_id and station handling
  wifi: mac80211: change public RX API to use link stations
  wifi: iwlwifi: use link_sta internally to the driver
  MAINTAINERS: Replace wireless.wiki.kernel.org links
  wifi: brcmfmac: log the firmware status when a connect fails
  ...
====================

Link: https://patch.msgid.link/20260921121635.195723-3-johannes@sipsolutions.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-09-21 18:16:26 -07:00
commit 10cfa109c8
90 changed files with 1672 additions and 1632 deletions

View File

@ -10,6 +10,7 @@
#
# Please keep this list dictionary sorted.
#
Aaradhana Sahu <aaradhana.sahu@oss.qualcomm.com> <quic_aarasahu@quicinc.com>
Aaron Durbin <adurbin@google.com>
Abel Vesa <abelvesa@kernel.org> <abel.vesa@nxp.com>
Abel Vesa <abelvesa@kernel.org> <abel.vesa@linaro.org>
@ -130,7 +131,8 @@ Axel Dyks <xl@xlsigned.net>
Axel Lin <axel.lin@gmail.com>
Balakrishna Godavarthi <quic_bgodavar@quicinc.com> <bgodavar@codeaurora.org>
Banajit Goswami <quic_bgoswami@quicinc.com> <bgoswami@codeaurora.org>
Baochen Qiang <quic_bqiang@quicinc.com> <bqiang@codeaurora.org>
Baochen Qiang <baochen.qiang@oss.qualcomm.com> <bqiang@codeaurora.org>
Baochen Qiang <baochen.qiang@oss.qualcomm.com> <quic_bqiang@quicinc.com>
Baolin Wang <baolin.wang@linux.alibaba.com> <baolin.wang@linaro.org>
Baolin Wang <baolin.wang@linux.alibaba.com> <baolin.wang@spreadtrum.com>
Baolin Wang <baolin.wang@linux.alibaba.com> <baolin.wang@unisoc.com>
@ -575,6 +577,7 @@ Maheshwar Ajja <quic_majja@quicinc.com> <majja@codeaurora.org>
Maíra Canal <mairacanal@riseup.net> <maira.canal@usp.br>
Malathi Gottam <quic_mgottam@quicinc.com> <mgottam@codeaurora.org>
Manikanta Pubbisetty <quic_mpubbise@quicinc.com> <mpubbise@codeaurora.org>
Manish Dharanenthiran <manish.dharanenthiran@oss.qualcomm.com> <quic_mdharane@quicinc.com>
Manivannan Sadhasivam <mani@kernel.org> <manivannanece23@gmail.com>
Manivannan Sadhasivam <mani@kernel.org> <manivannan.sadhasivam@linaro.org>
Manoj Basapathi <quic_manojbm@quicinc.com> <manojbm@codeaurora.org>
@ -764,6 +767,8 @@ Rakesh Pillai <quic_pillair@quicinc.com> <pillair@codeaurora.org>
Ralf Baechle <ralf@linux-mips.org>
Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Ram Chandra Jangir <quic_rjangir@quicinc.com> <rjangir@codeaurora.org>
Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com> <quic_ramess@quicinc.com>
Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com> <ramess@codeaurora.org>
Randy Dunlap <rdunlap@infradead.org> <rdunlap@xenotime.net>
Randy Dunlap <rdunlap@infradead.org> <randy.dunlap@oracle.com>
Randy Dunlap <rdunlap@infradead.org> <rddunlap@osdl.org>
@ -862,7 +867,8 @@ Sricharan Ramabadhran <quic_srichara@quicinc.com> <sricharan@codeaurora.org>
Srinivas Kandagatla <srini@kernel.org> <srinivas.kandagatla@st.com>
Srinivas Kandagatla <srini@kernel.org> <srinivas.kandagatla@linaro.org>
Srinivas Ramana <quic_sramana@quicinc.com> <sramana@codeaurora.org>
Sriram R <quic_srirrama@quicinc.com> <srirrama@codeaurora.org>
Sriram R <sriram.ramachandran@oss.qualcomm.com> <quic_srirrama@quicinc.com>
Sriram R <sriram.ramachandran@oss.qualcomm.com> <srirrama@codeaurora.org>
Sriram Yagnaraman <sriram.yagnaraman@ericsson.com> <sriram.yagnaraman@est.tech>
Stanislav Fomichev <sdf@fomichev.me> <sdf@google.com>
Stanislav Fomichev <sdf@fomichev.me> <stfomichev@gmail.com>
@ -946,7 +952,8 @@ Vadim Fedorenko <vadim.fedorenko@linux.dev> <vfedorenko@novek.ru>
Valdis Kletnieks <Valdis.Kletnieks@vt.edu>
Vara Reddy <quic_varar@quicinc.com> <varar@codeaurora.org>
Varadarajan Narayanan <quic_varada@quicinc.com> <varada@codeaurora.org>
Vasanthakumar Thiagarajan <quic_vthiagar@quicinc.com> <vthiagar@codeaurora.org>
Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com> <quic_vthiagar@quicinc.com>
Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com> <vthiagar@codeaurora.org>
Vasily Averin <vasily.averin@linux.dev> <vvs@virtuozzo.com>
Vasily Averin <vasily.averin@linux.dev> <vvs@openvz.org>
Vasily Averin <vasily.averin@linux.dev> <vvs@parallels.com>

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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;
};
};

View File

@ -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 <egbostina@gmail.com>
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 <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
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 <dt-bindings/interrupt-controller/irq.h>
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;
};
};

View File

@ -113,7 +113,7 @@ F: net/6lowpan/
M: Johannes Berg <johannes@sipsolutions.net>
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 <mickflemm@gmail.com>
M: Luis Chamberlain <mcgrof@kernel.org>
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 <chunkeey@googlemail.com>
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 <miriam.rachel.korenblit@intel.com>
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 <johannes@sipsolutions.net>
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 <johannes@sipsolutions.net>
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 <chunkeey@googlemail.com>
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 <jjohnson@kernel.org>
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 <jjohnson@kernel.org>
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 <jjohnson@kernel.org>
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 <toke@toke.dk>
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 <loic.poulain@oss.qualcomm.com>
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 <johannes@sipsolutions.net>
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

View File

@ -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

View File

@ -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");

View File

@ -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);

View File

@ -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);

View File

@ -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, &params);
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;

View File

@ -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,

View File

@ -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);

View File

@ -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 {

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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));

View File

@ -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);

View File

@ -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 {

View File

@ -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(&current_cc.alpha2, ab->new_alpha2, 2);
memcpy(&ar->alpha2, ab->new_alpha2, 2);
spin_lock_bh(&this_ab->base_lock);
memcpy(&current_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, &current_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);
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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,
},
};

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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) |

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
};

View File

@ -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)) {

View File

@ -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;
};
/**

View File

@ -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

View File

@ -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:

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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,

View File

@ -2,7 +2,7 @@
/*
* KUnit tests for channel helper functions
*
* Copyright (C) 2024-2025 Intel Corporation
* Copyright (C) 2024-2026 Intel Corporation
*/
#include <kunit/test.h>
#include <kunit/static_stub.h>
@ -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);

View File

@ -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);
}
/*

View File

@ -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();
}

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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");

View File

@ -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 {

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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)
{

View File

@ -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;

View File

@ -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);
}

View File

@ -3,7 +3,7 @@
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
* 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)

View File

@ -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)

View File

@ -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;
}

View File

@ -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;

View File

@ -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 ||

View File

@ -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;
};
/**

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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();

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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;
}

View File

@ -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);
}
/**

View File

@ -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,

View File

@ -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 */

View File

@ -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
*/

View File

@ -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:

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;

View File

@ -9,7 +9,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* 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 <linux/if_arp.h>
@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -2,7 +2,7 @@
/*
* Utilities for mac80211 unit testing
*
* Copyright (C) 2024 Intel Corporation
* Copyright (C) 2024, 2026 Intel Corporation
*/
#include <linux/ieee80211.h>
#include <net/mac80211.h>
@ -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:

View File

@ -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;

View File

@ -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,
};

View File

@ -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;

View File

@ -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);