Linux wireless drivers development
 help / color / mirror / Atom feed
* Re: [PATCH v12 00/22] wifi: nxpwifi: create nxpwifi to support
From: Johannes Berg @ 2026-06-08  6:39 UTC (permalink / raw)
  To: Jeff Chen, linux-wireless
  Cc: linux-kernel, briannorris, francesco, wyatt.hsu, s.hauer,
	ulf.hansson
In-Reply-To: <20260605161335.2415583-1-jeff.chen_1@nxp.com>

On Sat, 2026-06-06 at 00:13 +0800, Jeff Chen wrote:
> 
> Firmware binaries can be obtained from:
> https://github.com/nxp-imx/imx-firmware/tree/lf-6.12.34_2.1.0/nxp/FwImage_IW612_SD/

Are you going to send this for inclusion in linux-firmware with an
appropriate (redistributable) license? I'm not entirely convinced that
the license (that appears to be attached to) the repository is really
one that could be used there?

johannes

^ permalink raw reply

* [PATCH v2 7/7] arm64: dts: qcom: sm8350-hdk: describe WiFi/BT chip
From: Dmitry Baryshkov @ 2026-06-08  6:59 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Konrad Dybcio, Qiang Yu, Jeff Johnson, Liam Girdwood, Mark Brown,
	Krzysztof Kozlowski, Conor Dooley, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Bjorn Andersson, Konrad Dybcio
  Cc: linux-arm-msm, linux-pci, linux-kernel, linux-wireless, ath11k,
	devicetree, Bartosz Golaszewski, linux-bluetooth,
	Bartosz Golaszewski
In-Reply-To: <20260608-sm8350-wifi-v2-0-efb68f1ff04c@oss.qualcomm.com>

The SM8350 HDK has onboard WiFi/BT chip, WCN6851. It is an earlier
version of well-known WCN6855 WiFI/BT SoC. Describe the PMU, BT and WiFI
parts of the device.

The firmware isn't (yet) available as a part of linux-firmware, so it
was verified with the firmware files from the vendor-supplied package
(wcn prefix was applied to Bluetooth firmware files to make them follow
upstream driver changes, vendor provided hpbtfw10.tlv and hpnv10.b06).

Bluetooth: hci0: QCA Product ID   :0x00000013
Bluetooth: hci0: QCA SOC Version  :0x400c0110
Bluetooth: hci0: QCA ROM Version  :0x00000100
Bluetooth: hci0: QCA Patch Version:0x00001017
Bluetooth: hci0: QCA controller version 0x01100100
Bluetooth: hci0: QCA Downloading qca/wcnhpbtfw10.tlv
Bluetooth: hci0: QCA Downloading qca/wcnhpnv10.b06
Bluetooth: hci0: QCA setup on UART is completed
Bluetooth: hci0: HFP non-HCI data transport is supported

ath11k_pci 0000:01:00.0: BAR 0 [mem 0x60400000-0x605fffff 64bit]: assigned
ath11k_pci 0000:01:00.0: MSI vectors: 32
ath11k_pci 0000:01:00.0: wcn6855 hw1.1
mhi mhi0: Requested to power ON
mhi mhi0: Power on setup success
mhi mhi0: Wait for device to enter SBL or Mission mode
ath11k_pci 0000:01:00.0: chip_id 0x0 chip_family 0xb board_id 0x6 soc_id 0x400c0110
ath11k_pci 0000:01:00.0: fw_version 0x110c80c8 fw_build_timestamp 2021-05-25 21:43 fw_build_id WLAN.HSP.1.1.c3-00200-QCAHSPSWPL_V1_V2_SILICONZ-1
ath11k_pci 0000:01:00.0 wlp1s0: renamed from wlan0

For the reference, the driver looks for the board data for
bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=0108,qmi-chip-id=0,qmi-board-id=6,variant=QC_8350_HDK

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 arch/arm64/boot/dts/qcom/sm8350-hdk.dts | 126 ++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sm8350-hdk.dts b/arch/arm64/boot/dts/qcom/sm8350-hdk.dts
index 4973a3eb11b5..8e35216e4272 100644
--- a/arch/arm64/boot/dts/qcom/sm8350-hdk.dts
+++ b/arch/arm64/boot/dts/qcom/sm8350-hdk.dts
@@ -115,6 +115,70 @@ lt9611_3v3: lt9611-3v3-regulator {
 		regulator-boot-on;
 		regulator-always-on;
 	};
+
+	wcn6855-pmu {
+		compatible = "qcom,wcn6851-pmu", "qcom,wcn6855-pmu";
+
+		pinctrl-0 = <&bt_en>, <&wlan_en>, <&swctrl>;
+		pinctrl-names = "default";
+
+		wlan-enable-gpios = <&tlmm 64 GPIO_ACTIVE_HIGH>;
+		bt-enable-gpios = <&tlmm 65 GPIO_ACTIVE_HIGH>;
+		swctrl-gpios = <&tlmm 153 GPIO_ACTIVE_HIGH>;
+
+		vddio-supply = <&vreg_s10b_1p8>;
+		vddaon-supply = <&vreg_s11b_0p95>;
+		vddpmu-supply = <&vreg_s11b_0p95>;
+		vddpmumx-supply = <&vreg_s2e_0p85>;
+		vddpmucx-supply = <&vreg_s11b_0p95>;
+		vddrfa0p95-supply = <&vreg_s11b_0p95>;
+		vddrfa1p3-supply = <&vreg_s12b_1p25>;
+		vddrfa1p9-supply = <&vreg_s1c_1p86>;
+		vddpcie1p3-supply = <&vreg_s12b_1p25>;
+		vddpcie1p9-supply = <&vreg_s1c_1p86>;
+
+		regulators {
+			vreg_pmu_rfa_cmn_0p8: ldo0 {
+				regulator-name = "vreg_pmu_rfa_cmn_0p8";
+			};
+
+			vreg_pmu_aon_0p8: ldo1 {
+				regulator-name = "vreg_pmu_aon_0p8";
+			};
+
+			vreg_pmu_wlcx_0p8: ldo2 {
+				regulator-name = "vreg_pmu_wlcx_0p8";
+			};
+
+			vreg_pmu_wlmx_0p8: ldo3 {
+				regulator-name = "vreg_pmu_wlmx_0p8";
+			};
+
+			vreg_pmu_btcmx_0p8: ldo4 {
+				regulator-name = "vreg_pmu_btcmx_0p8";
+			};
+
+			vreg_pmu_pcie_1p8: ldo5 {
+				regulator-name = "vreg_pmu_pcie_1p8";
+			};
+
+			vreg_pmu_pcie_0p9: ldo6 {
+				regulator-name = "vreg_pmu_pcie_0p9";
+			};
+
+			vreg_pmu_rfa_0p8: ldo7 {
+				regulator-name = "vreg_pmu_rfa_0p8";
+			};
+
+			vreg_pmu_rfa_1p2: ldo8 {
+				regulator-name = "vreg_pmu_rfa_1p2";
+			};
+
+			vreg_pmu_rfa_1p7: ldo9 {
+				regulator-name = "vreg_pmu_rfa_1p7";
+			};
+		};
+	};
 };
 
 &adsp {
@@ -373,6 +437,13 @@ vreg_l7e_2p8: ldo7 {
 			regulator-name = "vreg_l7e_2p8";
 			regulator-min-microvolt = <2800000>;
 			regulator-max-microvolt = <2800000>;
+
+			/*
+			 * This is used by the RF front-end for which there is
+			 * no way to represent it in DT (yet?).
+			 */
+			regulator-boot-on;
+			regulator-always-on;
 		};
 	};
 };
@@ -499,6 +570,23 @@ &pcie0 {
 &pcie0_port0 {
 	reset-gpios = <&tlmm 94 GPIO_ACTIVE_LOW>;
 	wake-gpios = <&tlmm 96 GPIO_ACTIVE_HIGH>;
+
+	wifi@0 {
+		compatible = "pci17cb,1103";
+		reg = <0x10000 0x0 0x0 0x0 0x0>;
+
+		vddrfacmn-supply = <&vreg_pmu_rfa_cmn_0p8>;
+		vddaon-supply = <&vreg_pmu_aon_0p8>;
+		vddwlcx-supply = <&vreg_pmu_wlcx_0p8>;
+		vddwlmx-supply = <&vreg_pmu_wlmx_0p8>;
+		vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>;
+		vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>;
+		vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>;
+		vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>;
+		vddrfa1p8-supply = <&vreg_pmu_rfa_1p7>;
+
+		qcom,calibration-variant = "QC_8350_HDK";
+	};
 };
 
 &pcie0_phy {
@@ -763,6 +851,20 @@ &tlmm {
 		"HST_WLAN_UART_TX",
 		"HST_WLAN_UART_RX";
 
+	wlan_en: wlan-en-state {
+		pins = "gpio64";
+		function = "gpio";
+		drive-strength = <16>;
+		bias-disable;
+	};
+
+	bt_en: bt-en-state {
+		pins = "gpio65";
+		function = "gpio";
+		drive-strength = <16>;
+		bias-disable;
+	};
+
 	pcie0_default_state: pcie0-default-state {
 		perst-pins {
 			pins = "gpio94";
@@ -815,12 +917,36 @@ sdc2_card_det_n: sd-card-det-n-state {
 		drive-strength = <2>;
 		bias-pull-up;
 	};
+
+	swctrl: swctrl-state {
+		pins = "gpio153";
+		function = "gpio";
+		drive-strength = <16>;
+		bias-disable;
+	};
 };
 
 &uart2 {
 	status = "okay";
 };
 
+&uart18 {
+	status = "okay";
+
+	bluetooth {
+		compatible = "qcom,wcn6851-bt", "qcom,wcn6855-bt";
+
+		vddrfacmn-supply = <&vreg_pmu_rfa_cmn_0p8>;
+		vddaon-supply = <&vreg_pmu_aon_0p8>;
+		vddwlcx-supply = <&vreg_pmu_wlcx_0p8>;
+		vddwlmx-supply = <&vreg_pmu_wlmx_0p8>;
+		vddbtcmx-supply = <&vreg_pmu_btcmx_0p8>;
+		vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>;
+		vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>;
+		vddrfa1p7-supply = <&vreg_pmu_rfa_1p7>;
+	};
+};
+
 &ufs_mem_hc {
 	status = "okay";
 

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 6/7] arm64: dts: qcom: sm8350: modernize PCIe entries
From: Dmitry Baryshkov @ 2026-06-08  6:59 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Konrad Dybcio, Qiang Yu, Jeff Johnson, Liam Girdwood, Mark Brown,
	Krzysztof Kozlowski, Conor Dooley, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Bjorn Andersson, Konrad Dybcio
  Cc: linux-arm-msm, linux-pci, linux-kernel, linux-wireless, ath11k,
	devicetree, Bartosz Golaszewski, linux-bluetooth,
	Bartosz Golaszewski
In-Reply-To: <20260608-sm8350-wifi-v2-0-efb68f1ff04c@oss.qualcomm.com>

The recent suggestion is to have PERST# / WAKE pins and PHYs in the PCIe
port rather than RC device. The kernel recently started warning about
the older style of DT. Modernize DT for SM8350 platform by moving the
entries under the root port device node.

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 arch/arm64/boot/dts/qcom/sm8350-hdk.dts | 18 +++++++++++-------
 arch/arm64/boot/dts/qcom/sm8350.dtsi    | 12 ++++--------
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/arch/arm64/boot/dts/qcom/sm8350-hdk.dts b/arch/arm64/boot/dts/qcom/sm8350-hdk.dts
index 5f975d009465..4973a3eb11b5 100644
--- a/arch/arm64/boot/dts/qcom/sm8350-hdk.dts
+++ b/arch/arm64/boot/dts/qcom/sm8350-hdk.dts
@@ -493,12 +493,14 @@ &pcie0 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pcie0_default_state>;
 
-	perst-gpios = <&tlmm 94 GPIO_ACTIVE_LOW>;
-	wake-gpios = <&tlmm 96 GPIO_ACTIVE_HIGH>;
-
 	status = "okay";
 };
 
+&pcie0_port0 {
+	reset-gpios = <&tlmm 94 GPIO_ACTIVE_LOW>;
+	wake-gpios = <&tlmm 96 GPIO_ACTIVE_HIGH>;
+};
+
 &pcie0_phy {
 	vdda-phy-supply = <&vreg_l5b_0p88>;
 	vdda-pll-supply = <&vreg_l6b_1p2>;
@@ -507,15 +509,17 @@ &pcie0_phy {
 };
 
 &pcie1 {
-	perst-gpios = <&tlmm 97 GPIO_ACTIVE_LOW>;
-	wake-gpios = <&tlmm 99 GPIO_ACTIVE_HIGH>;
-
-	pinctrl-names = "default";
 	pinctrl-0 = <&pcie1_default_state>;
+	pinctrl-names = "default";
 
 	status = "okay";
 };
 
+&pcie1_port0 {
+	reset-gpios = <&tlmm 97 GPIO_ACTIVE_LOW>;
+	wake-gpios = <&tlmm 99 GPIO_ACTIVE_HIGH>;
+};
+
 &pcie1_phy {
 	status = "okay";
 	vdda-phy-supply = <&vreg_l5b_0p88>;
diff --git a/arch/arm64/boot/dts/qcom/sm8350.dtsi b/arch/arm64/boot/dts/qcom/sm8350.dtsi
index eb2a795d8edb..136daa444865 100644
--- a/arch/arm64/boot/dts/qcom/sm8350.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8350.dtsi
@@ -1583,12 +1583,9 @@ pcie0: pcie@1c00000 {
 
 			power-domains = <&gcc PCIE_0_GDSC>;
 
-			phys = <&pcie0_phy>;
-			phy-names = "pciephy";
-
 			status = "disabled";
 
-			pcie@0 {
+			pcie0_port0: pcie@0 {
 				device_type = "pci";
 				reg = <0x0 0x0 0x0 0x0 0x0>;
 				bus-range = <0x01 0xff>;
@@ -1596,6 +1593,7 @@ pcie@0 {
 				#address-cells = <3>;
 				#size-cells = <2>;
 				ranges;
+				phys = <&pcie0_phy>;
 			};
 		};
 
@@ -1692,12 +1690,9 @@ pcie1: pcie@1c08000 {
 
 			power-domains = <&gcc PCIE_1_GDSC>;
 
-			phys = <&pcie1_phy>;
-			phy-names = "pciephy";
-
 			status = "disabled";
 
-			pcie@0 {
+			pcie1_port0: pcie@0 {
 				device_type = "pci";
 				reg = <0x0 0x0 0x0 0x0 0x0>;
 				bus-range = <0x01 0xff>;
@@ -1705,6 +1700,7 @@ pcie@0 {
 				#address-cells = <3>;
 				#size-cells = <2>;
 				ranges;
+				phys = <&pcie1_phy>;
 			};
 		};
 

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 5/7] arm64: dts: qcom: sm8350: expand UART18 to 4 pins config
From: Dmitry Baryshkov @ 2026-06-08  6:59 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Konrad Dybcio, Qiang Yu, Jeff Johnson, Liam Girdwood, Mark Brown,
	Krzysztof Kozlowski, Conor Dooley, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Bjorn Andersson, Konrad Dybcio
  Cc: linux-arm-msm, linux-pci, linux-kernel, linux-wireless, ath11k,
	devicetree, Bartosz Golaszewski, linux-bluetooth,
	Bartosz Golaszewski
In-Reply-To: <20260608-sm8350-wifi-v2-0-efb68f1ff04c@oss.qualcomm.com>

On SM8350 platforms the primary use of UART18 is a 4-pin UART (targeting
Bluetooth or other similar applications). Add all 4 pins to the default
pinctrl entry for the UART.

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 arch/arm64/boot/dts/qcom/sm8350.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/qcom/sm8350.dtsi b/arch/arm64/boot/dts/qcom/sm8350.dtsi
index c830953156ec..eb2a795d8edb 100644
--- a/arch/arm64/boot/dts/qcom/sm8350.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8350.dtsi
@@ -3309,7 +3309,7 @@ qup_uart6_default: qup-uart6-default-state {
 			};
 
 			qup_uart18_default: qup-uart18-default-state {
-				pins = "gpio68", "gpio69";
+				pins = "gpio68", "gpio69", "gpio70", "gpio71";
 				function = "qup18";
 				drive-strength = <2>;
 				bias-disable;

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 4/7] dt-bindings: bluetooth: qcom,wcn6855-bt: document WCN6851
From: Dmitry Baryshkov @ 2026-06-08  6:59 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Konrad Dybcio, Qiang Yu, Jeff Johnson, Liam Girdwood, Mark Brown,
	Krzysztof Kozlowski, Conor Dooley, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Bjorn Andersson, Konrad Dybcio
  Cc: linux-arm-msm, linux-pci, linux-kernel, linux-wireless, ath11k,
	devicetree, Bartosz Golaszewski, linux-bluetooth,
	Bartosz Golaszewski
In-Reply-To: <20260608-sm8350-wifi-v2-0-efb68f1ff04c@oss.qualcomm.com>

WCN6851 is an earlier version of WCN6855 WiFi/BT chip, compatible with
it. Add a device-specific compat string with the fallback to WCN6855
one.

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 .../devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml        | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml
index 0beda26ae8bb..ec766f40a042 100644
--- a/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml
+++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,wcn6855-bt.yaml
@@ -13,8 +13,12 @@ maintainers:
 
 properties:
   compatible:
-    enum:
-      - qcom,wcn6855-bt
+    oneOf:
+      - items:
+          - const: qcom,wcn6851-bt
+          - const: qcom,wcn6855-bt
+      - enum:
+          - qcom,wcn6855-bt
 
   enable-gpios:
     maxItems: 1

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 3/7] regulator: dt-bindings: qcom,qca6390-pmu: document WCN6851
From: Dmitry Baryshkov @ 2026-06-08  6:59 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Konrad Dybcio, Qiang Yu, Jeff Johnson, Liam Girdwood, Mark Brown,
	Krzysztof Kozlowski, Conor Dooley, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Bjorn Andersson, Konrad Dybcio
  Cc: linux-arm-msm, linux-pci, linux-kernel, linux-wireless, ath11k,
	devicetree, Bartosz Golaszewski, linux-bluetooth,
	Bartosz Golaszewski
In-Reply-To: <20260608-sm8350-wifi-v2-0-efb68f1ff04c@oss.qualcomm.com>

WCN6851 is an earlier version of WCN6855 WiFi/BT chip, compatible with
it. Add a device-specific compat string with the fallback to WCN6855
one.

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 Documentation/devicetree/bindings/regulator/qcom,qca6390-pmu.yaml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/Documentation/devicetree/bindings/regulator/qcom,qca6390-pmu.yaml b/Documentation/devicetree/bindings/regulator/qcom,qca6390-pmu.yaml
index 105174df7df2..3d3c6fa7ecbc 100644
--- a/Documentation/devicetree/bindings/regulator/qcom,qca6390-pmu.yaml
+++ b/Documentation/devicetree/bindings/regulator/qcom,qca6390-pmu.yaml
@@ -21,6 +21,10 @@ properties:
           - enum:
               - qcom,wcn6755-pmu
           - const: qcom,wcn6750-pmu
+      - items:
+          - enum:
+              - qcom,wcn6851-pmu
+          - const: qcom,wcn6855-pmu
 
       - enum:
           - qcom,qca6390-pmu

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 2/7] wifi: ath11k: enable support for WCN6851
From: Dmitry Baryshkov @ 2026-06-08  6:59 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Konrad Dybcio, Qiang Yu, Jeff Johnson, Liam Girdwood, Mark Brown,
	Krzysztof Kozlowski, Conor Dooley, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Bjorn Andersson, Konrad Dybcio
  Cc: linux-arm-msm, linux-pci, linux-kernel, linux-wireless, ath11k,
	devicetree, Bartosz Golaszewski, linux-bluetooth,
	Bartosz Golaszewski
In-Reply-To: <20260608-sm8350-wifi-v2-0-efb68f1ff04c@oss.qualcomm.com>

The WCN6851, found e.g. on SM8350 platforms, is an earlier version of
WCN6855 platform. It identifies itself as hw1.1. Copy WCN6855 hw 2.0
configuration to support hw1.1 version.

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 drivers/net/wireless/ath/ath11k/core.c | 92 ++++++++++++++++++++++++++++++++++
 drivers/net/wireless/ath/ath11k/core.h |  1 +
 drivers/net/wireless/ath/ath11k/mhi.c  |  1 +
 drivers/net/wireless/ath/ath11k/pci.c  |  9 ++++
 drivers/net/wireless/ath/ath11k/pcic.c | 11 ++++
 5 files changed, 114 insertions(+)

diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index 8dacc878c006..78a226ca081c 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -393,6 +393,98 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
 		.cfr_num_stream_bufs = 0,
 		.cfr_stream_buf_size = 0,
 	},
+	{
+		.name = "wcn6855 hw1.1",
+		.hw_rev = ATH11K_HW_WCN6855_HW11,
+		.fw = {
+			.dir = "WCN6855/hw1.1",
+			.board_size = 256 * 1024,
+			.cal_offset = 128 * 1024,
+		},
+		.max_radios = 3,
+		.bdf_addr = 0x4B0C0000,
+		.hw_ops = &wcn6855_ops,
+		.ring_mask = &ath11k_hw_ring_mask_qca6390,
+		.internal_sleep_clock = true,
+		.regs = &wcn6855_regs,
+		.qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390,
+		.host_ce_config = ath11k_host_ce_config_qca6390,
+		.ce_count = 9,
+		.target_ce_config = ath11k_target_ce_config_wlan_qca6390,
+		.target_ce_count = 9,
+		.svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390,
+		.svc_to_ce_map_len = 14,
+		.ce_ie_addr = &ath11k_ce_ie_addr_ipq8074,
+		.single_pdev_only = true,
+		.rxdma1_enable = false,
+		.num_rxdma_per_pdev = 2,
+		.rx_mac_buf_ring = true,
+		.vdev_start_delay = true,
+		.htt_peer_map_v2 = false,
+
+		.spectral = {
+			.fft_sz = 0,
+			.fft_pad_sz = 0,
+			.summary_pad_sz = 0,
+			.fft_hdr_len = 0,
+			.max_fft_bins = 0,
+			.fragment_160mhz = false,
+		},
+
+		.interface_modes = BIT(NL80211_IFTYPE_STATION) |
+					BIT(NL80211_IFTYPE_AP) |
+					BIT(NL80211_IFTYPE_P2P_DEVICE) |
+					BIT(NL80211_IFTYPE_P2P_CLIENT) |
+					BIT(NL80211_IFTYPE_P2P_GO),
+		.supports_monitor = false,
+		.full_monitor_mode = false,
+		.supports_shadow_regs = true,
+		.idle_ps = true,
+		.supports_sta_ps = true,
+		.coldboot_cal_mm = false,
+		.coldboot_cal_ftm = false,
+		.cbcal_restart_fw = false,
+		.fw_mem_mode = 0,
+		.num_vdevs = 4,
+		.num_peers = 512,
+		.supports_suspend = true,
+		.hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855),
+		.supports_regdb = true,
+		.fix_l1ss = false,
+		.credit_flow = true,
+		.hal_params = &ath11k_hw_hal_params_qca6390,
+		.supports_dynamic_smps_6ghz = false,
+		.alloc_cacheable_memory = false,
+		.supports_rssi_stats = true,
+		.fw_wmi_diag_event = true,
+		.current_cc_support = true,
+		.dbr_debug_support = false,
+		.global_reset = true,
+		.bios_sar_capa = &ath11k_hw_sar_capa_wcn6855,
+		.m3_fw_support = true,
+		.fixed_bdf_addr = false,
+		.fixed_mem_region = false,
+		.static_window_map = false,
+		.hybrid_bus_type = false,
+		.fixed_fw_mem = false,
+		.support_off_channel_tx = true,
+		.supports_multi_bssid = true,
+
+		.sram_dump = {
+			.start = 0x01400000,
+			.end = 0x0177ffff,
+		},
+
+		.tcl_ring_retry = true,
+		.tx_ring_size = DP_TCL_DATA_RING_SIZE,
+		.smp2p_wow_exit = false,
+		.support_fw_mac_sequence = true,
+		.support_dual_stations = true,
+		.pdev_suspend = false,
+		.cfr_support = false,
+		.cfr_num_stream_bufs = 0,
+		.cfr_stream_buf_size = 0,
+	},
 	{
 		.name = "wcn6855 hw2.0",
 		.hw_rev = ATH11K_HW_WCN6855_HW20,
diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index a0d725923ef2..29727ee94bfc 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -147,6 +147,7 @@ enum ath11k_hw_rev {
 	ATH11K_HW_QCA6390_HW20,
 	ATH11K_HW_IPQ6018_HW10,
 	ATH11K_HW_QCN9074_HW10,
+	ATH11K_HW_WCN6855_HW11,
 	ATH11K_HW_WCN6855_HW20,
 	ATH11K_HW_WCN6855_HW21,
 	ATH11K_HW_WCN6750_HW10,
diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c
index a6c9ff112c68..c94546c367a7 100644
--- a/drivers/net/wireless/ath/ath11k/mhi.c
+++ b/drivers/net/wireless/ath/ath11k/mhi.c
@@ -393,6 +393,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci)
 		ath11k_mhi_config = &ath11k_mhi_config_qcn9074;
 		break;
 	case ATH11K_HW_QCA6390_HW20:
+	case ATH11K_HW_WCN6855_HW11:
 	case ATH11K_HW_WCN6855_HW20:
 	case ATH11K_HW_WCN6855_HW21:
 	case ATH11K_HW_QCA2066_HW21:
diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
index 35bb9e7a63a2..dc1dfd219d88 100644
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -1031,6 +1031,15 @@ static int ath11k_pci_probe(struct pci_dev *pdev,
 		ath11k_pci_read_hw_version(ab, &soc_hw_version_major,
 					   &soc_hw_version_minor);
 		switch (soc_hw_version_major) {
+		case 1:
+			switch (soc_hw_version_minor) {
+			case 0x10:
+				ab->hw_rev = ATH11K_HW_WCN6855_HW11;
+				break;
+			default:
+				goto unsupported_wcn6855_soc;
+			}
+			break;
 		case 2:
 			switch (soc_hw_version_minor) {
 			case 0x00:
diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c
index fc6e7da05c60..0f4dc05cc5b5 100644
--- a/drivers/net/wireless/ath/ath11k/pcic.c
+++ b/drivers/net/wireless/ath/ath11k/pcic.c
@@ -86,6 +86,17 @@ static const struct ath11k_msi_config ath11k_msi_config[] = {
 		},
 		.hw_rev = ATH11K_HW_QCN9074_HW10,
 	},
+	{
+		.total_vectors = 32,
+		.total_users = 4,
+		.users = (struct ath11k_msi_user[]) {
+			{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
+			{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
+			{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
+			{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
+		},
+		.hw_rev = ATH11K_HW_WCN6855_HW11,
+	},
 	{
 		.total_vectors = 32,
 		.total_users = 4,

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 1/7] PCI: qcom: fix parsing of PERST# in the legacy case
From: Dmitry Baryshkov @ 2026-06-08  6:59 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Konrad Dybcio, Qiang Yu, Jeff Johnson, Liam Girdwood, Mark Brown,
	Krzysztof Kozlowski, Conor Dooley, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Bjorn Andersson, Konrad Dybcio
  Cc: linux-arm-msm, linux-pci, linux-kernel, linux-wireless, ath11k,
	devicetree, Bartosz Golaszewski, linux-bluetooth
In-Reply-To: <20260608-sm8350-wifi-v2-0-efb68f1ff04c@oss.qualcomm.com>

Commit deed8aec62dc ("PCI: qcom: Handle mixed PERST#/PHY DT
configuration") fixed support for the "mixed" platforms which declare
PERST# pin the RC node and the PHY in the RP node, however it also broke
support for a majority of existing platforms, which declare both PERST#
and PHY in the RC node, because now PERST# is first acquired in
qcom_pcie_parse_ports(), which then returns -ENODEV (as there are no
PHYs in the RP nodes). Later qcom_pcie_parse_legacy_binding() tries to
acquire the PERST# GPIO again and fails with -EBUSY (as the GPIO has
already been requested).

Move parsing of RC's perst-gpios to qcom_pcie_probe(), making it obvious
that it's shared for both cases and skip parsing it in both functions.

Fixes: deed8aec62dc ("PCI: qcom: Handle mixed PERST#/PHY DT configuration")
Closes: https://lore.kernel.org/r/gieaybsg2ckxpctvqj77nlwu7utama2yeyvebkonmexsxrra3v@v3fobqasxnmy/
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
 drivers/pci/controller/dwc/pcie-qcom.c | 25 ++++++++++---------------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index 11fc60489892..7664c7c28c0e 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -1907,15 +1907,6 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
 	struct device *dev = pcie->pci->dev;
 	int ret = -ENODEV;
 
-	if (of_find_property(dev->of_node, "perst-gpios", NULL)) {
-		pcie->reset = devm_gpiod_get_optional(dev, "perst",
-						      GPIOD_OUT_HIGH);
-		if (IS_ERR(pcie->reset))
-			return PTR_ERR(pcie->reset);
-
-		dev_warn(dev, "Reusing PERST# from Root Complex node. DT needs to be fixed!\n");
-	}
-
 	for_each_available_child_of_node_scoped(dev->of_node, of_port) {
 		if (!of_node_is_type(of_port, "pci"))
 			continue;
@@ -1942,7 +1933,6 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
 	struct device *dev = pcie->pci->dev;
 	struct qcom_pcie_perst *perst;
 	struct qcom_pcie_port *port;
-	struct gpio_desc *reset;
 	struct phy *phy;
 	int ret;
 
@@ -1950,10 +1940,6 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
 	if (IS_ERR(phy))
 		return PTR_ERR(phy);
 
-	reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH);
-	if (IS_ERR(reset))
-		return PTR_ERR(reset);
-
 	ret = phy_init(phy);
 	if (ret)
 		return ret;
@@ -1970,7 +1956,7 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
 	INIT_LIST_HEAD(&port->list);
 	list_add_tail(&port->list, &pcie->ports);
 
-	perst->desc = reset;
+	perst->desc = pcie->reset;
 	INIT_LIST_HEAD(&port->perst);
 	INIT_LIST_HEAD(&perst->list);
 	list_add_tail(&perst->list, &port->perst);
@@ -2107,6 +2093,15 @@ static int qcom_pcie_probe(struct platform_device *pdev)
 
 	pp->ops = &qcom_pcie_dw_ops;
 
+	if (of_find_property(dev->of_node, "perst-gpios", NULL)) {
+		pcie->reset = devm_gpiod_get_optional(dev, "perst",
+						      GPIOD_OUT_HIGH);
+		if (IS_ERR(pcie->reset))
+			return PTR_ERR(pcie->reset);
+
+		dev_warn(dev, "Reusing PERST# from Root Complex node. DT needs to be updated!\n");
+	}
+
 	ret = qcom_pcie_parse_ports(pcie);
 	if (ret) {
 		if (ret != -ENODEV) {

-- 
2.47.3


^ permalink raw reply related

* [PATCH v2 0/7] arm64: dts: qcom: enable WiFi/BT on SM8350 HDK
From: Dmitry Baryshkov @ 2026-06-08  6:59 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Konrad Dybcio, Qiang Yu, Jeff Johnson, Liam Girdwood, Mark Brown,
	Krzysztof Kozlowski, Conor Dooley, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Bjorn Andersson, Konrad Dybcio
  Cc: linux-arm-msm, linux-pci, linux-kernel, linux-wireless, ath11k,
	devicetree, Bartosz Golaszewski, linux-bluetooth,
	Bartosz Golaszewski

The SM8350 HDK has an onboard WCN6851 WiFi/BT chip, which for a long
time was not supported. Bring up different pieces required to enable
this SoC.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
Changes in v2:
- Bumped num_vdevs to 4 to follow other similar devices (Jeff)
- Link to v1: https://patch.msgid.link/20260601-sm8350-wifi-v1-0-242917d88031@oss.qualcomm.com

---
Dmitry Baryshkov (7):
      PCI: qcom: fix parsing of PERST# in the legacy case
      wifi: ath11k: enable support for WCN6851
      regulator: dt-bindings: qcom,qca6390-pmu: document WCN6851
      dt-bindings: bluetooth: qcom,wcn6855-bt: document WCN6851
      arm64: dts: qcom: sm8350: expand UART18 to 4 pins config
      arm64: dts: qcom: sm8350: modernize PCIe entries
      arm64: dts: qcom: sm8350-hdk: describe WiFi/BT chip

 .../bindings/net/bluetooth/qcom,wcn6855-bt.yaml    |   8 +-
 .../bindings/regulator/qcom,qca6390-pmu.yaml       |   4 +
 arch/arm64/boot/dts/qcom/sm8350-hdk.dts            | 142 ++++++++++++++++++++-
 arch/arm64/boot/dts/qcom/sm8350.dtsi               |  14 +-
 drivers/net/wireless/ath/ath11k/core.c             |  92 +++++++++++++
 drivers/net/wireless/ath/ath11k/core.h             |   1 +
 drivers/net/wireless/ath/ath11k/mhi.c              |   1 +
 drivers/net/wireless/ath/ath11k/pci.c              |   9 ++
 drivers/net/wireless/ath/ath11k/pcic.c             |  11 ++
 drivers/pci/controller/dwc/pcie-qcom.c             |  25 ++--
 10 files changed, 275 insertions(+), 32 deletions(-)
---
base-commit: 6e845bcb78c95af935094040bd4edc3c2b6dd784
change-id: 20260531-sm8350-wifi-3b0659bc183a

Best regards,
--  
With best wishes
Dmitry


^ permalink raw reply

* Re: [PATCH ath-next 2/6] wifi: ath12k: Add support for 4-address mode
From: Tamizh Raja @ 2026-06-08  6:03 UTC (permalink / raw)
  To: Jeff Johnson; +Cc: ath12k, linux-wireless
In-Reply-To: <a97dac59-a689-4b9d-8419-2b09c1d106ab@oss.qualcomm.com>

On Sun, Jun 7, 2026 at 9:03 PM Jeff Johnson
<jeff.johnson@oss.qualcomm.com> wrote:
>
> On 5/25/2026 4:09 AM, Tamizh Chelvam Raja wrote:
> ...
> > @@ -999,6 +1007,11 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw,
> >                       skb_cb->vif = vif;
> >                       skb_cb->ar = tmp_ar;
> >
> > +                     if (ahsta && ahsta->enable_4addr)
> > +                             arsta = rcu_dereference(ahsta->link[link_id]);
> > +                     else
> > +                             arsta = NULL;
> > +
> Tamizh, please check the smatch warning at:
> https://lore.kernel.org/oe-kbuild/202606051125.XaYVDQZf-lkp@intel.com/
>
> This looks legitimate since there is a path where link_id can be 15, exceeding
> the ahsta->link[] array.
>
The function has the below check at line 925 in the same function,
before the execution reaches line 979
/* handle only for MLO case, use deflink for non MLO case */
        if (ieee80211_vif_is_mld(vif)) {
                link_id = ath12k_mac_get_tx_link(sta, vif, link_id,
skb, info_flags);
                if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) {
                        ieee80211_free_txskb(hw, skb);
                        return;
                }
        }

In this link_id has already been validated
- MLO path: explicitly checked link_id >= IEEE80211_MLD_MAX_NUM_LINKS
and returned early.
- Non-MLO path: link_id is set to 0 or a known-safe constant.

> /jeff

Tamizh.

^ permalink raw reply

* [PATCHv2 wireless-next] wifi: brcm80211: change current_bss to value
From: Rosen Penev @ 2026-06-08  5:28 UTC (permalink / raw)
  To: linux-wireless
  Cc: Arend van Spriel,
	open list:BROADCOM BRCM80211 IEEE802.11 WIRELESS DRIVERS,
	open list:BROADCOM BRCM80211 IEEE802.11 WIRELESS DRIVERS,
	open list

Change to a single allocation and remove some boilerplate.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
 v2: change to value
 .../broadcom/brcm80211/brcmsmac/main.c        | 40 +++----------------
 .../broadcom/brcm80211/brcmsmac/main.h        |  2 +-
 2 files changed, 7 insertions(+), 35 deletions(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c
index c7eaf160e1fa..ec3aeaa7bab9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c
@@ -418,21 +418,12 @@ static int brcms_chspec_bw(u16 chanspec)
 	return BRCMS_10_MHZ;
 }

-static void brcms_c_bsscfg_mfree(struct brcms_bss_cfg *cfg)
-{
-	if (cfg == NULL)
-		return;
-
-	kfree(cfg->current_bss);
-	kfree(cfg);
-}
-
 static void brcms_c_detach_mfree(struct brcms_c_info *wlc)
 {
 	if (wlc == NULL)
 		return;

-	brcms_c_bsscfg_mfree(wlc->bsscfg);
+	kfree(wlc->bsscfg);
 	kfree(wlc->pub);
 	kfree(wlc->modulecb);
 	kfree(wlc->default_bss);
@@ -453,25 +444,6 @@ static void brcms_c_detach_mfree(struct brcms_c_info *wlc)
 	kfree(wlc);
 }

-static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit)
-{
-	struct brcms_bss_cfg *cfg;
-
-	cfg = kzalloc_obj(*cfg, GFP_ATOMIC);
-	if (cfg == NULL)
-		goto fail;
-
-	cfg->current_bss = kzalloc_obj(*cfg->current_bss, GFP_ATOMIC);
-	if (cfg->current_bss == NULL)
-		goto fail;
-
-	return cfg;
-
- fail:
-	brcms_c_bsscfg_mfree(cfg);
-	return NULL;
-}
-
 static struct brcms_c_info *
 brcms_c_attach_malloc(uint unit, uint *err, uint devid)
 {
@@ -527,7 +499,7 @@ brcms_c_attach_malloc(uint unit, uint *err, uint devid)
 		goto fail;
 	}

-	wlc->bsscfg = brcms_c_bsscfg_malloc(unit);
+	wlc->bsscfg = kzalloc_obj(*wlc->bsscfg, GFP_ATOMIC);
 	if (wlc->bsscfg == NULL) {
 		*err = 1011;
 		goto fail;
@@ -3813,7 +3785,7 @@ static void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, u16 chanspec)
 		wlc->home_chanspec = chanspec;

 		if (wlc->pub->associated)
-			wlc->bsscfg->current_bss->chanspec = chanspec;
+			wlc->bsscfg->current_bss.chanspec = chanspec;
 	}
 }

@@ -5423,7 +5395,7 @@ void brcms_c_get_current_rateset(struct brcms_c_info *wlc,
 	struct brcms_c_rateset *rs;

 	if (wlc->pub->associated)
-		rs = &wlc->bsscfg->current_bss->rateset;
+		rs = &wlc->bsscfg->current_bss.rateset;
 	else
 		rs = &wlc->default_bss->rateset;

@@ -5450,7 +5422,7 @@ int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs)
 	if (wlc->pub->_n_enab & SUPPORT_11N) {
 		struct brcms_bss_info *mcsset_bss;
 		if (wlc->pub->associated)
-			mcsset_bss = wlc->bsscfg->current_bss;
+			mcsset_bss = &wlc->bsscfg->current_bss;
 		else
 			mcsset_bss = wlc->default_bss;
 		memcpy(internal_rs.mcs, &mcsset_bss->rateset.mcs[0],
@@ -7809,7 +7781,7 @@ void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx)
 		u32 bi;

 		/* get beacon period and convert to uS */
-		bi = wlc->bsscfg->current_bss->beacon_period << 10;
+		bi = wlc->bsscfg->current_bss.beacon_period << 10;
 		/*
 		 * update since init path would reset
 		 * to default value
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h
index b7ca0d9891c4..2d133264f4a7 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h
@@ -614,7 +614,7 @@ struct brcms_bss_cfg {
 	u8 SSID_len;
 	u8 SSID[IEEE80211_MAX_SSID_LEN];
 	u8 BSSID[ETH_ALEN];
-	struct brcms_bss_info *current_bss;
+	struct brcms_bss_info current_bss;
 };

 int brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p);
--
2.54.0


^ permalink raw reply related

* [mt76][mt7921] Question about host-visible deterministic TX scheduling / TX completion timestamps
From: Zitong Zhao @ 2026-06-08  5:28 UTC (permalink / raw)
  To: linux-wireless, linux-mediatek
  Cc: nbd, lorenzo, ryder.lee, shayne.chen, sean.wang, deren.wu

Hi mt76 maintainers,

Resending as plain text because the previous HTML mail was rejected by
linux-mediatek.

We are working on a deterministic Wi-Fi TDMA research prototype using
MT7921/MT7922 with the mt76/mt7921e driver.

We would like to understand whether the MT7921/MT7922
hardware/firmware exposes any supported host-visible primitive for:

1. TSF-scheduled TX or hardware-timed queue release
2. firmware TX queue admission / queue pause-resume
3. reliable per-packet firmware/PHY TX completion timestamps
4. TWT service-period control usable for deterministic UL/DL scheduling

From the public mt76 driver and our local diagnostics, we currently observe:

- RX MACTIME is available and useful for RX-side timestamping.
- TXS can be forced for diagnostics, but coverage is partial/asymmetric.
- WTBL_QUERY returns only a short status-like response in our setup.
- TWT_AGRT_UPDATE returns ACK/status, but we have not found evidence
that it provides host-controllable deterministic queue admission.
- We did not find an exposed TSF-scheduled TX path in mt7921e.

Could you confirm whether this hardware/firmware stack supports any
host-visible deterministic TX scheduling or reliable per-packet TX
completion timestamp interface?

If this is not available in the public mt76 driver, is it a firmware
limitation, or is there a documented MediaTek interface available
through another channel?

Hardware/firmware in our setup:
- AP side: MT7961/MT7921e path
- STA side: MT7922/RZ616-family, mt7921e path
- Driver base: mt76/mt7921e with local research diagnostics

We do not need confidential details on the public mailing list. A
yes/no answer about whether such primitives exist would already be
very helpful. If this requires MediaTek confidential documentation or
an NDA channel, could you point us to the right contact/process?

Thanks,
Zitong Zhao
Cyber Physical System Lab from Shanghai Jiao Tong University

^ permalink raw reply

* [PATCH wireless-next] wifi: brcmfmac: flowring: simplify flow allocation
From: Rosen Penev @ 2026-06-08  5:11 UTC (permalink / raw)
  To: linux-wireless
  Cc: Arend van Spriel, Kees Cook, Gustavo A. R. Silva,
	open list:BROADCOM BRCM80211 IEEE802.11 WIRELESS DRIVERS,
	open list:BROADCOM BRCM80211 IEEE802.11 WIRELESS DRIVERS,
	open list,
	open list:KERNEL HARDENING (not covered by other areas):Keyword:b__counted_by(_le|_be|_ptr)?b

Use a flexible array member and kzalloc_flex to combine allocations.
Simplifies code slightly.

Add __counted_by for extra runtime analysis.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
 .../wireless/broadcom/brcm80211/brcmfmac/flowring.c    | 10 ++--------
 .../wireless/broadcom/brcm80211/brcmfmac/flowring.h    |  2 +-
 2 files changed, 3 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
index df7e3bee19f2..35cbcea0abc9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c
@@ -360,20 +360,15 @@ struct brcmf_flowring *brcmf_flowring_attach(struct device *dev, u16 nrofrings)
 	struct brcmf_flowring *flow;
 	u32 i;

-	flow = kzalloc_obj(*flow);
+	flow = kzalloc_flex(*flow, rings, nrofrings);
 	if (flow) {
-		flow->dev = dev;
 		flow->nrofrings = nrofrings;
+		flow->dev = dev;
 		spin_lock_init(&flow->block_lock);
 		for (i = 0; i < ARRAY_SIZE(flow->addr_mode); i++)
 			flow->addr_mode[i] = ADDR_INDIRECT;
 		for (i = 0; i < ARRAY_SIZE(flow->hash); i++)
 			flow->hash[i].ifidx = BRCMF_FLOWRING_INVALID_IFIDX;
-		flow->rings = kzalloc_objs(*flow->rings, nrofrings);
-		if (!flow->rings) {
-			kfree(flow);
-			flow = NULL;
-		}
 	}

 	return flow;
@@ -399,7 +394,6 @@ void brcmf_flowring_detach(struct brcmf_flowring *flow)
 		search = search->next;
 		kfree(remove);
 	}
-	kfree(flow->rings);
 	kfree(flow);
 }

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
index 818882b0fd01..f3d511f9a3c9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.h
@@ -38,12 +38,12 @@ struct brcmf_flowring_tdls_entry {
 struct brcmf_flowring {
 	struct device *dev;
 	struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE];
-	struct brcmf_flowring_ring **rings;
 	spinlock_t block_lock;
 	enum proto_addr_mode addr_mode[BRCMF_MAX_IFS];
 	u16 nrofrings;
 	bool tdls_active;
 	struct brcmf_flowring_tdls_entry *tdls_entry;
+	struct brcmf_flowring_ring *rings[] __counted_by(nrofrings);
 };


--
2.54.0


^ permalink raw reply related

* [PATCH] wifi: mt76: mt76u: use GRO on the USB RX path
From: Filip Bakreski @ 2026-06-08  4:41 UTC (permalink / raw)
  To: nbd, lorenzo, ryder.lee; +Cc: shayne.chen, sean.wang, linux-wireless

The USB RX path delivers frames to the stack via mt76_rx_complete() with
a NULL napi pointer, which takes the netif_receive_skb_list() path and
therefore never benefits from GRO. The DMA-based mt76 drivers pass a real
napi and get napi_gro_receive(); the USB path does not. For bulk TCP
traffic this is costly, as every segment traverses the network stack
individually instead of being coalesced.

Add a small container NAPI on a dummy netdev that the RX worker drives
manually: napi_schedule_prep() marks it scheduled, frames are delivered
through napi_gro_receive(), and napi_complete_done() flushes the coalesced
list. The poll handler is never invoked by the core.

On mt7921u at HE-MCS 11 (2x2, 80 MHz) this raises single-stream TCP
download throughput from ~383 to ~475 Mbit/s (~+24%), averaged over six
interleaved A/B measurements. The gain only applies while the link is not
RF-limited, as expected for a host-side optimisation.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Filip Bakreski <phial@phiality.com>
---
 drivers/net/wireless/mediatek/mt76/mt76.h |  4 +++
 drivers/net/wireless/mediatek/mt76/usb.c  | 36 ++++++++++++++++++++++-
 2 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 07955555f..f5e52c1f4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -668,6 +668,10 @@ struct mt76_usb {
 	struct mt76_worker status_worker;
 	struct mt76_worker rx_worker;
 
+	/* container NAPI used only to batch GRO for the RX worker */
+	struct net_device *napi_dev;
+	struct napi_struct napi;
+
 	struct work_struct stat_work;
 
 	u8 out_ep[__MT_EP_OUT_MAX];
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index d9638a9b7..f9c48140c 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -619,12 +619,31 @@ mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
 		mt76u_submit_rx_buf(dev, qid, urb);
 	}
 	if (qid == MT_RXQ_MAIN) {
+		struct napi_struct *napi = &dev->usb.napi;
+
 		local_bh_disable();
-		mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL);
+		/* Drive a container NAPI so the RX path can use
+		 * napi_gro_receive(): napi_schedule_prep() marks it SCHED and
+		 * napi_complete_done() flushes the coalesced GRO list. The poll
+		 * handler is never actually invoked by the core.
+		 */
+		if (dev->usb.napi_dev && napi_schedule_prep(napi)) {
+			mt76_rx_poll_complete(dev, MT_RXQ_MAIN, napi);
+			napi_complete_done(napi, 1);
+		} else {
+			mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL);
+		}
 		local_bh_enable();
 	}
 }
 
+/* Never invoked by the core: the RX worker drives GRO via the napi manually. */
+static int mt76u_napi_poll(struct napi_struct *napi, int budget)
+{
+	napi_complete(napi);
+	return 0;
+}
+
 static void mt76u_rx_worker(struct mt76_worker *w)
 {
 	struct mt76_usb *usb = container_of(w, struct mt76_usb, rx_worker);
@@ -1051,6 +1070,13 @@ void mt76u_queues_deinit(struct mt76_dev *dev)
 	mt76u_stop_rx(dev);
 	mt76u_stop_tx(dev);
 
+	if (dev->usb.napi_dev) {
+		napi_disable(&dev->usb.napi);
+		netif_napi_del(&dev->usb.napi);
+		free_netdev(dev->usb.napi_dev);
+		dev->usb.napi_dev = NULL;
+	}
+
 	mt76u_free_rx(dev);
 	mt76u_free_tx(dev);
 }
@@ -1115,6 +1141,14 @@ int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf,
 	sched_set_fifo_low(usb->rx_worker.task);
 	sched_set_fifo_low(usb->status_worker.task);
 
+	/* container netdev + NAPI used only to enable GRO on the RX path */
+	usb->napi_dev = alloc_netdev_dummy(0);
+	if (!usb->napi_dev)
+		return -ENOMEM;
+	strscpy(usb->napi_dev->name, "mt76u-rx", sizeof(usb->napi_dev->name));
+	netif_napi_add(usb->napi_dev, &usb->napi, mt76u_napi_poll);
+	napi_enable(&usb->napi);
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(__mt76u_init);

base-commit: 5f6099446d1ddb888e36cdf93b6a0551f05c1267
-- 
2.54.0


^ permalink raw reply related

* RE: rtw88: WiFi card is not offloaded properly when suspending the OS
From: Ping-Ke Shih @ 2026-06-08  1:32 UTC (permalink / raw)
  To: Giovanni Santini, stable@vger.kernel.org,
	linux-wireless@vger.kernel.org
  Cc: Linux regressions mailing list
In-Reply-To: <10da4c45-74ed-4deb-8c88-6d0b803465ba@yahoo.it>

Giovanni Santini <giovannisantini93@yahoo.it> wrote:
> 
> Jun 02 17:25:33 archlinux-tug kernel: rtw88_8822ce 0000:03:00.0: failed
> to send h2c command
> Jun 02 17:25:33 archlinux-tug kernel: rtw88_8822ce 0000:03:00.0: failed
> to send h2c command
> Jun 02 17:25:33 archlinux-tug kernel: rtw88_8822ce 0000:03:00.0: failed
> to send h2c command
> Jun 02 17:25:33 archlinux-tug kernel: rtw88_8822ce 0000:03:00.0: failed
> to send h2c command
> Jun 02 17:25:36 archlinux-tug kernel: rtw88_8822ce 0000:03:00.0: failed
> to poll offset=0x5 mask=0x2 value=0x0
> Jun 02 17:25:36 archlinux-tug kernel: ------------[ cut here ]------------
> Jun 02 17:25:36 archlinux-tug kernel: failed to read DBI register,
> addr=0x0719

Add these below to see if it can help. 

sudo nano /etc/modprobe.d/rtw88.conf
options rtw88_core disable_lps_deep=y
options rtw88_pci disable_aspm=y

After cold reboot, check /sys/modules/rtw88_*/paramters/* to see if
the modification takes effect.

> 
> I'm attaching the full boot log, if you need a fresh one I can provide it.

Before "failed to poll ..." log, I also see 

Jun 02 17:25:02 archlinux-tug kernel: rtw88_8822ce 0000:03:00.0: AMD-Vi:
 Event logged [IO_PAGE_FAULT domain=0x000e address=0xae9668bc flags=0x0000]

Please try to turn off IOMMU by editing /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash amd_iommu=off iommu=off"

And then update-grub

> 
> I run ArchLinux with KDE Plasma, for networking I use NetworkManager and
> wpa_supplicant.
> 
> If you would like me to run tests with e.g. just iwd I can do so, just
> let me know what setup you would like me to have.
> 
> I do not have this issue with the LTS kernel 6.18.34. This affects 7.0
> and 7.1.
> I believe this issue was not present in < 7 kernels, but I cannot guarantee.
> I can however downgrade to previous kernels to understand where the
> issue was introduced.

The commits between 6.18.34 ~ 7.0.10 are quite few, and I can't find the 
one that can affect the behavior. Please switch your kernel back to 6.18.34,
and apply rtw88's patches added until 7.0.10.

Since the last commit of 6.18 is fce6fee0817b8899e0ee38ab6b98f0d7e939ceed

Please use below commands to get the (20) patches:
drivers/net/wireless/realtek/rtw88$ git format-patch v7.0.10...fce6fee0817b8899e0ee38ab6b98f0d7e939ceed -- ./

Then you can add them one by one to see which one is the cause.

The git repository you need is:
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git


Ping-Ke



^ permalink raw reply

* [PATCH wireless-next 5/5] wifi: cfg80211: support MAC address filtering in station dump for link stats
From: P Praneesh @ 2026-06-07 17:59 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, ath12k, praneesh.p
In-Reply-To: <20260607175912.2266215-1-praneesh.p@oss.qualcomm.com>

Currently, when userspace requests station information with
link statistics using NL80211_CMD_GET_STATION with the
NL80211_ATTR_STA_DUMP_LINK_STATS flag, the kernel uses the .doit callback
(nl80211_get_station) which sends a single netlink message. For MLO
stations with multiple links, the link statistics can be large and may
exceed the maximum netlink message size, causing the operation to fail
with -EMSGSIZE.

The .dumpit callback (nl80211_dump_station) already supports
fragmentation across multiple netlink messages, making it suitable
for handling large link statistics. However, it currently iterates over
all stations on the interface, which is inefficient when userspace only
wants information about a specific station.

Add support for MAC address filtering in nl80211_dump_station to allow
userspace to request fragmented link statistics for a specific station.
When NL80211_ATTR_MAC is present in a dump request, cache the MAC address
in the dump context and use rdev_get_station() to retrieve information for
only that station, instead of iterating over all stations with
rdev_dump_station().

This allows userspace tools (like iw) to use NL80211_CMD_GET_STATION with
NLM_F_DUMP flag to retrieve complete link statistics for a specific
station across multiple netlink messages, avoiding the message size
limitation.

Signed-off-by: P Praneesh <praneesh.p@oss.qualcomm.com>
---
 net/wireless/nl80211.c | 44 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 41 insertions(+), 3 deletions(-)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 3dba329c7eb2..209df747e53e 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -56,6 +56,8 @@ struct nl80211_dump_station_ctx {
 	enum nl80211_dump_station_phase phase;
 	int link_idx;
 	bool dump_link_stats;
+	bool filter_mac;
+	u8 filter_mac_addr[ETH_ALEN];
 	u8 mac_addr[ETH_ALEN];
 	struct station_info sinfo;
 };
@@ -8618,6 +8620,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
 	struct nl80211_dump_station_ctx *ctx = cb_data->ctx;
 	struct nlattr **attrbuf = NULL;
 	int err, ret;
+	const u8 *mac_addr;
 
 	NL_ASSERT_CTX_FITS(struct nl80211_dump_station_cb);
 
@@ -8650,6 +8653,19 @@ static int nl80211_dump_station(struct sk_buff *skb,
 		ctx->link_idx = 0;
 		ctx->dump_link_stats =
 			!!attrbuf[NL80211_ATTR_STA_DUMP_LINK_STATS];
+		if (attrbuf[NL80211_ATTR_MAC]) {
+			mac_addr = nla_data(attrbuf[NL80211_ATTR_MAC]);
+			if (!is_valid_ether_addr(mac_addr)) {
+				kfree(attrbuf);
+				kfree(ctx);
+				err = -EINVAL;
+				goto out_err;
+			}
+
+			ctx->filter_mac = true;
+			memcpy(ctx->filter_mac_addr, mac_addr, ETH_ALEN);
+		}
+
 		cb_data->ctx = ctx;
 	}
 
@@ -8661,7 +8677,12 @@ static int nl80211_dump_station(struct sk_buff *skb,
 		goto out_err;
 	}
 
-	if (!rdev->ops->dump_station) {
+	if (ctx->filter_mac) {
+		if (!rdev->ops->get_station) {
+			err = -EOPNOTSUPP;
+			goto out_err;
+		}
+	} else if (!rdev->ops->dump_station) {
 		err = -EOPNOTSUPP;
 		goto out_err;
 	}
@@ -8679,8 +8700,25 @@ static int nl80211_dump_station(struct sk_buff *skb,
 				}
 			}
 
-			err = rdev_dump_station(rdev, wdev, ctx->sta_idx,
-						ctx->mac_addr, &ctx->sinfo);
+			if (ctx->filter_mac) {
+				if (ctx->sta_idx > 0) {
+					err = skb->len;
+					goto out_err_release;
+				}
+
+				err = rdev_get_station(rdev, wdev,
+						       ctx->filter_mac_addr,
+						       &ctx->sinfo);
+				if (!err)
+					memcpy(ctx->mac_addr,
+					       ctx->filter_mac_addr, ETH_ALEN);
+			} else {
+				err = rdev_dump_station(rdev, wdev,
+							ctx->sta_idx,
+							ctx->mac_addr,
+							&ctx->sinfo);
+			}
+
 			if (err == -ENOENT) {
 				err = skb->len;
 				goto out_err_release;
-- 
2.43.0


^ permalink raw reply related

* [PATCH wireless-next 4/5] wifi: cfg80211: Fragment per-link station stats in nl80211_dump_station()
From: P Praneesh @ 2026-06-07 17:59 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, ath12k, praneesh.p
In-Reply-To: <20260607175912.2266215-1-praneesh.p@oss.qualcomm.com>

In MLO scenarios, stations may have multiple links, each with distinct
statistics. When userspace tools like iw or hostapd request station dumps,
attempting to pack all per-link stats into a single netlink message can
easily exceed the default 4KB buffer limit, especially when more than two
links are active. This results in -EMSGSIZE errors and incomplete data
delivery.

To address this, fragment per-link station statistics across multiple
netlink messages to ensure reliable delivery of complete MLO station
information. Extend the stateful two-phase dump mechanism: the first
phase sends aggregated station-level statistics and the second phase
sends individual per-link statistics in separate fragments.

Add a new request flag attribute, NL80211_ATTR_STA_DUMP_LINK_STATS
(NLA_FLAG), for NL80211_CMD_GET_STATION dump. Userspace can set this
flag to request per-link station statistics for multi-link (MLO)
stations.

Extract this flag safely during the first dump invocation by passing an
attribute buffer to nl80211_prepare_wdev_dump(), caching the boolean
result in the dump context to avoid repeated parsing overhead.

Each per-link message now includes a nested NL80211_ATTR_STA_INFO
attribute to carry link-specific statistics, along with
NL80211_ATTR_MLO_LINKS for link context. Add a new helper function
nl80211_send_link_station() to construct these messages. The dump loop
iterates through the valid_links bitmask to process active links,
breaking out gracefully on EMSGSIZE while preserving the deeply allocated
station info for the next syscall iteration.

Even when no per-link statistics attributes are added, an empty
NL80211_ATTR_STA_INFO nest is still emitted. This is intentional, as
userspace expects NL80211_ATTR_STA_INFO to be present in
NL80211_CMD_NEW_STATION messages and may otherwise abort parsing or
drop the event if it is missing.

Backward compatibility is seamlessly preserved for non-MLO stations.

Signed-off-by: P Praneesh <praneesh.p@oss.qualcomm.com>
---
 include/uapi/linux/nl80211.h |  32 +++-
 net/wireless/nl80211.c       | 332 +++++++++++++++++++++++++++++++++--
 2 files changed, 351 insertions(+), 13 deletions(-)

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 9998f6c0a665..f00a9cd8ab2c 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1818,6 +1818,10 @@ enum nl80211_commands {
  * @NL80211_ATTR_STA_INFO: information about a station, part of station info
  *	given for %NL80211_CMD_GET_STATION, nested attribute containing
  *	info as possible, see &enum nl80211_sta_info.
+ *	In the per-link dump messages produced when %NL80211_ATTR_STA_DUMP_LINK_STATS
+ *	is requested, the top-level occurrence of this attribute is empty.
+ *	Per-link statistics are carried inside %NL80211_ATTR_MLO_LINKS;
+ *	see %NL80211_ATTR_STA_DUMP_LINK_STATS for the full message layout.
  *
  * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands,
  *	consisting of a nested array.
@@ -2904,7 +2908,11 @@ enum nl80211_commands {
  * @NL80211_ATTR_MLO_LINK_ID: A (u8) link ID for use with MLO, to be used with
  *	various commands that need a link ID to operate.
  * @NL80211_ATTR_MLO_LINKS: A nested array of links, each containing some
- *	per-link information and a link ID.
+ *	per-link information and a link ID. In %NL80211_CMD_NEW_STATION
+ *	responses produced by a %NL80211_ATTR_STA_DUMP_LINK_STATS dump,
+ *	each link entry additionally carries %NL80211_ATTR_STA_INFO with
+ *	per-link station statistics and %NL80211_ATTR_MAC with the
+ *	link-specific MAC address.
  * @NL80211_ATTR_MLD_ADDR: An MLD address, used with various commands such as
  *	authenticate/associate.
  *
@@ -3165,6 +3173,26 @@ enum nl80211_commands {
  * @NL80211_ATTR_NPCA_PRIMARY_FREQ: NPCA primary channel (u32)
  * @NL80211_ATTR_NPCA_PUNCT_BITMAP: NPCA puncturing bitmap (u32)
  *
+ * @NL80211_ATTR_STA_DUMP_LINK_STATS: Request flag for %NL80211_CMD_GET_STATION
+ *	(dump mode only). When set on an MLD station, the dump produces two
+ *	%NL80211_CMD_NEW_STATION messages per station per dump call:
+ *
+ *	1. An aggregated-stats message whose top-level %NL80211_ATTR_STA_INFO
+ *	   contains MLO-combined statistics (same content as a dump without
+ *	   this flag).
+ *
+ *	2. For each active link, a per-link message containing
+ *	   %NL80211_ATTR_MLO_LINKS with a single link entry. Each entry holds
+ *	   %NL80211_ATTR_MLO_LINK_ID, the link-specific %NL80211_ATTR_MAC,
+ *	   and %NL80211_ATTR_STA_INFO with per-link statistics (see
+ *	   &enum nl80211_sta_info). The top-level %NL80211_ATTR_STA_INFO in
+ *	   this message is intentionally empty; it is present solely for ABI
+ *	   compatibility with parsers that require %NL80211_ATTR_STA_INFO to
+ *	   be present in every %NL80211_CMD_NEW_STATION message.
+ *
+ *	The aggregated message always precedes the per-link messages for the
+ *	same station within a dump sequence.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3763,6 +3791,8 @@ enum nl80211_attrs {
 	NL80211_ATTR_NPCA_PRIMARY_FREQ,
 	NL80211_ATTR_NPCA_PUNCT_BITMAP,
 
+	NL80211_ATTR_STA_DUMP_LINK_STATS,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d146d6af6e48..3dba329c7eb2 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -54,6 +54,8 @@ enum nl80211_dump_station_phase {
 struct nl80211_dump_station_ctx {
 	int sta_idx;
 	enum nl80211_dump_station_phase phase;
+	int link_idx;
+	bool dump_link_stats;
 	u8 mac_addr[ETH_ALEN];
 	struct station_info sinfo;
 };
@@ -1126,6 +1128,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
 	[NL80211_ATTR_NPCA_PRIMARY_FREQ] = { .type = NLA_U32 },
 	[NL80211_ATTR_NPCA_PUNCT_BITMAP] =
 		NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range),
+	[NL80211_ATTR_STA_DUMP_LINK_STATS] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -7874,6 +7877,185 @@ static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,
 	return true;
 }
 
+static int nl80211_fill_link_station(struct sk_buff *msg,
+				     struct cfg80211_registered_device *rdev,
+				     struct link_station_info *link_sinfo)
+{
+	struct nlattr *bss_param, *link_sinfoattr;
+
+#define PUT_LINK_SINFO(attr, memb, type) do {				\
+	BUILD_BUG_ON(sizeof(type) == sizeof(u64));			\
+	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&	\
+	    nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr,		\
+			     link_sinfo->memb))				\
+		goto nla_put_failure;					\
+	} while (0)
+#define PUT_LINK_SINFO_U64(attr, memb) do {				\
+	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&	\
+	    nla_put_u64_64bit(msg, NL80211_STA_INFO_ ## attr,		\
+			      link_sinfo->memb, NL80211_STA_INFO_PAD))	\
+		goto nla_put_failure;					\
+	} while (0)
+
+	link_sinfoattr = nla_nest_start_noflag(msg, NL80211_ATTR_STA_INFO);
+	if (!link_sinfoattr)
+		goto nla_put_failure;
+
+	PUT_LINK_SINFO(INACTIVE_TIME, inactive_time, u32);
+
+	if (link_sinfo->filled & (BIT_ULL(NL80211_STA_INFO_RX_BYTES) |
+			     BIT_ULL(NL80211_STA_INFO_RX_BYTES64)) &&
+	    nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,
+			(u32)link_sinfo->rx_bytes))
+		goto nla_put_failure;
+
+	if (link_sinfo->filled & (BIT_ULL(NL80211_STA_INFO_TX_BYTES) |
+			     BIT_ULL(NL80211_STA_INFO_TX_BYTES64)) &&
+	    nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
+			(u32)link_sinfo->tx_bytes))
+		goto nla_put_failure;
+
+	PUT_LINK_SINFO_U64(RX_BYTES64, rx_bytes);
+	PUT_LINK_SINFO_U64(TX_BYTES64, tx_bytes);
+	PUT_LINK_SINFO_U64(RX_DURATION, rx_duration);
+	PUT_LINK_SINFO_U64(TX_DURATION, tx_duration);
+
+	if (wiphy_ext_feature_isset(&rdev->wiphy,
+				    NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
+		PUT_LINK_SINFO(AIRTIME_WEIGHT, airtime_weight, u16);
+
+	switch (rdev->wiphy.signal_type) {
+	case CFG80211_SIGNAL_TYPE_MBM:
+		PUT_LINK_SINFO(SIGNAL, signal, u8);
+		PUT_LINK_SINFO(SIGNAL_AVG, signal_avg, u8);
+		break;
+	default:
+		break;
+	}
+	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) {
+		if (!nl80211_put_signal(msg, link_sinfo->chains,
+					link_sinfo->chain_signal,
+					NL80211_STA_INFO_CHAIN_SIGNAL))
+			goto nla_put_failure;
+	}
+	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) {
+		if (!nl80211_put_signal(msg, link_sinfo->chains,
+					link_sinfo->chain_signal_avg,
+					NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
+			goto nla_put_failure;
+	}
+	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE)) {
+		if (!nl80211_put_sta_rate(msg, &link_sinfo->txrate,
+					  NL80211_STA_INFO_TX_BITRATE))
+			goto nla_put_failure;
+	}
+	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) {
+		if (!nl80211_put_sta_rate(msg, &link_sinfo->rxrate,
+					  NL80211_STA_INFO_RX_BITRATE))
+			goto nla_put_failure;
+	}
+
+	PUT_LINK_SINFO(RX_PACKETS, rx_packets, u32);
+	PUT_LINK_SINFO(TX_PACKETS, tx_packets, u32);
+	PUT_LINK_SINFO(TX_RETRIES, tx_retries, u32);
+	PUT_LINK_SINFO(TX_FAILED, tx_failed, u32);
+	PUT_LINK_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32);
+	PUT_LINK_SINFO(BEACON_LOSS, beacon_loss_count, u32);
+
+	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) {
+		bss_param = nla_nest_start_noflag(msg,
+						  NL80211_STA_INFO_BSS_PARAM);
+		if (!bss_param)
+			goto nla_put_failure;
+
+		if (((link_sinfo->bss_param.flags &
+		      BSS_PARAM_FLAGS_CTS_PROT) &&
+		     nla_put_flag(msg, NL80211_STA_BSS_PARAM_CTS_PROT)) ||
+		    ((link_sinfo->bss_param.flags &
+		      BSS_PARAM_FLAGS_SHORT_PREAMBLE) &&
+		     nla_put_flag(msg,
+				  NL80211_STA_BSS_PARAM_SHORT_PREAMBLE)) ||
+		    ((link_sinfo->bss_param.flags &
+		      BSS_PARAM_FLAGS_SHORT_SLOT_TIME) &&
+		     nla_put_flag(msg,
+				  NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME)) ||
+		    nla_put_u8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
+			       link_sinfo->bss_param.dtim_period) ||
+		    nla_put_u16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
+				link_sinfo->bss_param.beacon_interval))
+			goto nla_put_failure;
+
+		nla_nest_end(msg, bss_param);
+	}
+
+	PUT_LINK_SINFO_U64(RX_DROP_MISC, rx_dropped_misc);
+	PUT_LINK_SINFO_U64(BEACON_RX, rx_beacon);
+	PUT_LINK_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8);
+	PUT_LINK_SINFO(RX_MPDUS, rx_mpdu_count, u32);
+	PUT_LINK_SINFO(FCS_ERROR_COUNT, fcs_err_count, u32);
+	if (wiphy_ext_feature_isset(&rdev->wiphy,
+				    NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT)) {
+		PUT_LINK_SINFO(ACK_SIGNAL, ack_signal, u8);
+		PUT_LINK_SINFO(ACK_SIGNAL_AVG, avg_ack_signal, s8);
+	}
+
+#undef PUT_LINK_SINFO
+#undef PUT_LINK_SINFO_U64
+
+	if (link_sinfo->pertid) {
+		struct nlattr *tidsattr;
+		int tid;
+
+		tidsattr = nla_nest_start_noflag(msg,
+						 NL80211_STA_INFO_TID_STATS);
+		if (!tidsattr)
+			goto nla_put_failure;
+
+		for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) {
+			struct cfg80211_tid_stats *tidstats;
+			struct nlattr *tidattr;
+
+			tidstats = &link_sinfo->pertid[tid];
+
+			if (!tidstats->filled)
+				continue;
+
+			tidattr = nla_nest_start_noflag(msg, tid + 1);
+			if (!tidattr)
+				goto nla_put_failure;
+
+#define PUT_TIDVAL_U64(attr, memb) do {					\
+	if (tidstats->filled & BIT(NL80211_TID_STATS_ ## attr) &&	\
+	    nla_put_u64_64bit(msg, NL80211_TID_STATS_ ## attr,		\
+			      tidstats->memb, NL80211_TID_STATS_PAD))	\
+		goto nla_put_failure;					\
+	} while (0)
+
+			PUT_TIDVAL_U64(RX_MSDU, rx_msdu);
+			PUT_TIDVAL_U64(TX_MSDU, tx_msdu);
+			PUT_TIDVAL_U64(TX_MSDU_RETRIES, tx_msdu_retries);
+			PUT_TIDVAL_U64(TX_MSDU_FAILED, tx_msdu_failed);
+
+#undef PUT_TIDVAL_U64
+			if ((tidstats->filled &
+			     BIT(NL80211_TID_STATS_TXQ_STATS)) &&
+			    !nl80211_put_txq_stats(msg, &tidstats->txq_stats,
+						   NL80211_TID_STATS_TXQ_STATS))
+				goto nla_put_failure;
+
+			nla_nest_end(msg, tidattr);
+		}
+
+		nla_nest_end(msg, tidsattr);
+	}
+
+	nla_nest_end(msg, link_sinfoattr);
+	return 0;
+
+nla_put_failure:
+	return -EMSGSIZE;
+}
+
 static int nl80211_put_sta_info_common(struct sk_buff *msg,
 				       struct cfg80211_registered_device *rdev,
 				       struct station_info *sinfo)
@@ -8347,6 +8529,86 @@ nl80211_send_accumulated_station(struct sk_buff *msg,
 	return -EMSGSIZE;
 }
 
+static int nl80211_send_link_station(struct sk_buff *msg,
+				     struct netlink_callback *cb,
+				     struct cfg80211_registered_device *rdev,
+				     struct wireless_dev *wdev,
+				     const u8 *mac_addr,
+				     struct station_info *sinfo,
+				     int link_idx)
+{
+	void *hdr;
+	struct nlattr *links, *link;
+	struct link_station_info *link_sinfo;
+	struct nlattr *sinfoattr;
+	int err;
+
+	hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid,
+			     cb->nlh->nlmsg_seq, NLM_F_MULTI,
+			     NL80211_CMD_NEW_STATION);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	if ((wdev->netdev &&
+	     nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex)) ||
+	    nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+			      NL80211_ATTR_PAD) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
+	    nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation)) {
+		err = -EMSGSIZE;
+		goto err_cancel;
+	}
+
+	sinfoattr = nla_nest_start_noflag(msg, NL80211_ATTR_STA_INFO);
+	if (!sinfoattr) {
+		err = -EMSGSIZE;
+		goto err_cancel;
+	}
+
+	link_sinfo = sinfo->links[link_idx];
+	if (!link_sinfo) {
+		err = -ENOENT;
+		goto err_cancel;
+	}
+
+	nla_nest_end(msg, sinfoattr);
+	if (!is_valid_ether_addr(link_sinfo->addr)) {
+		err = -EADDRNOTAVAIL;
+		goto err_cancel;
+	}
+
+	links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
+	if (!links) {
+		err = -EMSGSIZE;
+		goto err_cancel;
+	}
+
+	link = nla_nest_start(msg, link_idx + 1);
+	if (!link) {
+		err = -EMSGSIZE;
+		goto err_cancel;
+	}
+
+	if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_idx) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, link_sinfo->addr)) {
+		err = -EMSGSIZE;
+		goto err_cancel;
+	}
+
+	err = nl80211_fill_link_station(msg, rdev, link_sinfo);
+	if (err)
+		goto err_cancel;
+
+	nla_nest_end(msg, link);
+	nla_nest_end(msg, links);
+	genlmsg_end(msg, hdr);
+	return 0;
+
+err_cancel:
+	genlmsg_cancel(msg, hdr);
+	return err;
+}
+
 static int nl80211_dump_station(struct sk_buff *skb,
 				struct netlink_callback *cb)
 {
@@ -8354,13 +8616,22 @@ static int nl80211_dump_station(struct sk_buff *skb,
 	struct wireless_dev *wdev;
 	struct nl80211_dump_station_cb *cb_data = (void *)cb->ctx;
 	struct nl80211_dump_station_ctx *ctx = cb_data->ctx;
+	struct nlattr **attrbuf = NULL;
 	int err, ret;
 
 	NL_ASSERT_CTX_FITS(struct nl80211_dump_station_cb);
 
-	err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, NULL);
-	if (err)
+	if (!ctx) {
+		attrbuf = kzalloc_objs(*attrbuf, NUM_NL80211_ATTR);
+		if (!attrbuf)
+			return -ENOMEM;
+	}
+
+	err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, attrbuf);
+	if (err) {
+		kfree(attrbuf);
 		return err;
+	}
 
 	/* nl80211_prepare_wdev_dump acquired it in the successful case */
 	__acquire(&rdev->wiphy.mtx);
@@ -8369,15 +8640,22 @@ static int nl80211_dump_station(struct sk_buff *skb,
 	if (!ctx) {
 		ctx = kzalloc_obj(*ctx);
 		if (!ctx) {
+			kfree(attrbuf);
 			err = -ENOMEM;
 			goto out_err;
 		}
 
 		ctx->phase = NL80211_DUMP_STA_PHASE_AGGREGATED;
 		ctx->sta_idx = 0;
+		ctx->link_idx = 0;
+		ctx->dump_link_stats =
+			!!attrbuf[NL80211_ATTR_STA_DUMP_LINK_STATS];
 		cb_data->ctx = ctx;
 	}
 
+	kfree(attrbuf);
+	attrbuf = NULL;
+
 	if (!wdev->netdev && wdev->iftype != NL80211_IFTYPE_NAN) {
 		err = -EINVAL;
 		goto out_err;
@@ -8388,9 +8666,9 @@ static int nl80211_dump_station(struct sk_buff *skb,
 		goto out_err;
 	}
 
-	/* Phase 0: dump aggregated station info */
-	if (ctx->phase == NL80211_DUMP_STA_PHASE_AGGREGATED) {
-		while (true) {
+	while (true) {
+		/* Phase 0: dump aggregated station info */
+		if (ctx->phase == NL80211_DUMP_STA_PHASE_AGGREGATED) {
 			memset(&ctx->sinfo, 0, sizeof(ctx->sinfo));
 			for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
 				ctx->sinfo.links[i] =
@@ -8428,15 +8706,45 @@ static int nl80211_dump_station(struct sk_buff *skb,
 				goto out_err_release;
 			}
 
-			/* Reset ctx for next station */
-			cfg80211_sinfo_release_content(&ctx->sinfo);
-			ctx->sta_idx++;
+			ctx->phase = NL80211_DUMP_STA_PHASE_PER_LINK;
 		}
-	}
 
-	ctx->phase = NL80211_DUMP_STA_PHASE_AGGREGATED;
-	err = skb->len;
-	goto out_err;
+		/* Phase 1: dump per-link station info */
+		if (ctx->phase == NL80211_DUMP_STA_PHASE_PER_LINK &&
+		    ctx->dump_link_stats && ctx->sinfo.valid_links) {
+			while (ctx->link_idx < IEEE80211_MLD_MAX_NUM_LINKS) {
+				if (!(ctx->sinfo.valid_links &
+				      BIT(ctx->link_idx))) {
+					ctx->link_idx++;
+					continue;
+				}
+
+				ret = nl80211_send_link_station(skb, cb, rdev,
+								wdev,
+								ctx->mac_addr,
+								&ctx->sinfo,
+								ctx->link_idx);
+				if (ret == -EMSGSIZE) {
+					err = skb->len;
+					goto out_err;
+				}
+
+				if (ret < 0) {
+					err = ret;
+					goto out_err_release;
+				}
+
+				ctx->link_idx++;
+			}
+		}
+
+		/* Reset ctx for next station */
+		cfg80211_sinfo_release_content(&ctx->sinfo);
+		memset(&ctx->sinfo, 0, sizeof(ctx->sinfo));
+		ctx->sta_idx++;
+		ctx->phase = NL80211_DUMP_STA_PHASE_AGGREGATED;
+		ctx->link_idx = 0;
+	}
 
 out_err_release:
 	cfg80211_sinfo_release_content(&ctx->sinfo);
-- 
2.43.0


^ permalink raw reply related

* [PATCH wireless-next 3/5] wifi: cfg80211: Refactor nl80211_dump_station() to prepare for per-link stats
From: P Praneesh @ 2026-06-07 17:59 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, ath12k, praneesh.p
In-Reply-To: <20260607175912.2266215-1-praneesh.p@oss.qualcomm.com>

Currently, nl80211_dump_station() relies on the netlink callback's generic
args array (cb->args[2]) to track the station index during dumps. It also
processes the entire sinfo structure and transmits it to userspace
immediately in a single pass.

This approach creates a bottleneck for MLO. When an MLD station has
multiple active links, the aggregated station information, combined
with the individual per-link statistics, can easily exceed the
maximum netlink message size limits. The current monolithic dump
iteration cannot pause and resume mid-station to fragment these large
per-link statistics across multiple netlink messages.

Introduce a stateful context structure (struct nl80211_dump_station_ctx)
allocated during the dump to track the iteration state. Move the station
index (sta_idx) tracking into this context, and introduce a phase
variable to represent the current fragmentation state (starting with
Phase 0 for the base aggregated MAC-level statistics).

Utilize the existing nl80211_prepare_wdev_dump() to safely parse the wdev
attributes on every dump iteration, ensuring proper RTNL and wiphy locking
without caching dangling device pointers. Furthermore, move the
NL80211_CMD_GET_STATION command definition from genl_small_ops to
genl_ops to natively support the .done callback. Implement
nl80211_dump_station_done() to ensure the newly allocated state context
and its deeply allocated sinfo payload are safely freed when the dump
concludes or is aborted prematurely by userspace.

Note that the previous dump path used nl80211_send_station(), which
included NL80211_ATTR_IE and NL80211_ATTR_RESP_IE. These attributes are
not carried forward in this implementation. As documented, association
response IEs (assoc_resp_ies) are only relevant at station creation time
(e.g. via cfg80211_new_sta()) to notify userspace about association
details, and are not expected to be part of get_station()/dump_station()
callbacks. Aligning with this expectation, these IEs are intentionally
omitted here.

This refactoring maintains the existing netlink batching performance while
laying the stateful foundation required for per-link statistics
fragmentation in subsequent patches.

Signed-off-by: P Praneesh <praneesh.p@oss.qualcomm.com>
---
 net/wireless/nl80211.c | 201 +++++++++++++++++++++++++++++++----------
 1 file changed, 153 insertions(+), 48 deletions(-)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 88232c323d9c..d146d6af6e48 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -39,6 +39,39 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
 
 /* the netlink family */
 static struct genl_family nl80211_fam;
+/**
+ * enum nl80211_dump_station_phase - station dump fragmentation phases
+ * @NL80211_DUMP_STA_PHASE_AGGREGATED: send aggregated (MLO-combined) station
+ *	statistics for the station entry
+ * @NL80211_DUMP_STA_PHASE_PER_LINK: send per-link statistics for each active
+ *	MLO link of the station; only used when dump_link_stats is set
+ */
+enum nl80211_dump_station_phase {
+	NL80211_DUMP_STA_PHASE_AGGREGATED = 0,
+	NL80211_DUMP_STA_PHASE_PER_LINK   = 1,
+};
+
+struct nl80211_dump_station_ctx {
+	int sta_idx;
+	enum nl80211_dump_station_phase phase;
+	u8 mac_addr[ETH_ALEN];
+	struct station_info sinfo;
+};
+
+/**
+ * struct nl80211_dump_station_cb - state stored in netlink_callback::ctx
+ * @wiphy_idx: args[0] - wiphy index from nl80211_prepare_wdev_dump
+ * @wdev_id: args[1] - wdev identifier from nl80211_prepare_wdev_dump
+ * @ctx: args[2] - dump station context pointer
+ *
+ * args[0] and args[1] are reserved by nl80211_prepare_wdev_dump().
+ * The ctx pointer must live at args[2] to avoid corrupting those fields.
+ */
+struct nl80211_dump_station_cb {
+	long wiphy_idx;
+	long wdev_id;
+	struct nl80211_dump_station_ctx *ctx;
+};
 
 /* multicast groups */
 enum nl80211_multicast_groups {
@@ -8279,23 +8312,72 @@ static void cfg80211_sta_set_mld_sinfo(struct station_info *sinfo)
 	sinfo->filled &= ~BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
 }
 
+static int
+nl80211_send_accumulated_station(struct sk_buff *msg,
+				 struct netlink_callback *cb,
+				 struct cfg80211_registered_device *rdev,
+				 struct wireless_dev *wdev,
+				 const u8 *mac_addr,
+				 struct station_info *sinfo)
+{
+	void *hdr;
+
+	hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid,
+			     cb->nlh->nlmsg_seq, NLM_F_MULTI,
+			     NL80211_CMD_NEW_STATION);
+	if (!hdr)
+		return -EMSGSIZE;
+
+	if ((wdev->netdev &&
+	     nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex)) ||
+	    nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+			      NL80211_ATTR_PAD) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
+	    nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation))
+		goto nla_put_failure;
+
+	if (nl80211_put_sta_info_common(msg, rdev, sinfo))
+		goto nla_put_failure;
+
+	genlmsg_end(msg, hdr);
+	return 0;
+
+nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	return -EMSGSIZE;
+}
+
 static int nl80211_dump_station(struct sk_buff *skb,
 				struct netlink_callback *cb)
 {
-	struct station_info sinfo;
 	struct cfg80211_registered_device *rdev;
 	struct wireless_dev *wdev;
-	u8 mac_addr[ETH_ALEN];
-	int sta_idx = cb->args[2];
-	bool sinfo_alloc = false;
-	int err, i;
+	struct nl80211_dump_station_cb *cb_data = (void *)cb->ctx;
+	struct nl80211_dump_station_ctx *ctx = cb_data->ctx;
+	int err, ret;
+
+	NL_ASSERT_CTX_FITS(struct nl80211_dump_station_cb);
 
 	err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, NULL);
 	if (err)
 		return err;
+
 	/* nl80211_prepare_wdev_dump acquired it in the successful case */
 	__acquire(&rdev->wiphy.mtx);
 
+	/* First invocation: allocate ctx */
+	if (!ctx) {
+		ctx = kzalloc_obj(*ctx);
+		if (!ctx) {
+			err = -ENOMEM;
+			goto out_err;
+		}
+
+		ctx->phase = NL80211_DUMP_STA_PHASE_AGGREGATED;
+		ctx->sta_idx = 0;
+		cb_data->ctx = ctx;
+	}
+
 	if (!wdev->netdev && wdev->iftype != NL80211_IFTYPE_NAN) {
 		err = -EINVAL;
 		goto out_err;
@@ -8306,55 +8388,77 @@ static int nl80211_dump_station(struct sk_buff *skb,
 		goto out_err;
 	}
 
-	while (1) {
-		memset(&sinfo, 0, sizeof(sinfo));
-
-		for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
-			sinfo.links[i] =
-				kzalloc_obj(*sinfo.links[0]);
-			if (!sinfo.links[i]) {
-				err = -ENOMEM;
-				goto out_err;
+	/* Phase 0: dump aggregated station info */
+	if (ctx->phase == NL80211_DUMP_STA_PHASE_AGGREGATED) {
+		while (true) {
+			memset(&ctx->sinfo, 0, sizeof(ctx->sinfo));
+			for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
+				ctx->sinfo.links[i] =
+					kzalloc_obj(*ctx->sinfo.links[0]);
+				if (!ctx->sinfo.links[i]) {
+					err = -ENOMEM;
+					goto out_err_release;
+				}
 			}
-			sinfo_alloc = true;
-		}
-
-		err = rdev_dump_station(rdev, wdev, sta_idx,
-					mac_addr, &sinfo);
-		if (err == -ENOENT)
-			break;
-		if (err)
-			goto out_err;
 
-		if (sinfo.valid_links)
-			cfg80211_sta_set_mld_sinfo(&sinfo);
+			err = rdev_dump_station(rdev, wdev, ctx->sta_idx,
+						ctx->mac_addr, &ctx->sinfo);
+			if (err == -ENOENT) {
+				err = skb->len;
+				goto out_err_release;
+			}
 
-		/* reset the sinfo_alloc flag as nl80211_send_station()
-		 * always releases sinfo
-		 */
-		sinfo_alloc = false;
+			if (err)
+				goto out_err_release;
+
+			if (ctx->sinfo.valid_links)
+				cfg80211_sta_set_mld_sinfo(&ctx->sinfo);
+
+			ret = nl80211_send_accumulated_station(skb, cb, rdev,
+							       wdev,
+							       ctx->mac_addr,
+							       &ctx->sinfo);
+			if (ret == -EMSGSIZE) {
+				err = skb->len;
+				goto out_err_release;
+			}
 
-		if (nl80211_send_station(skb, NL80211_CMD_NEW_STATION,
-				NETLINK_CB(cb->skb).portid,
-				cb->nlh->nlmsg_seq, NLM_F_MULTI,
-				rdev, wdev, mac_addr,
-				&sinfo) < 0)
-			goto out;
+			if (ret < 0) {
+				err = ret;
+				goto out_err_release;
+			}
 
-		sta_idx++;
+			/* Reset ctx for next station */
+			cfg80211_sinfo_release_content(&ctx->sinfo);
+			ctx->sta_idx++;
+		}
 	}
 
- out:
-	cb->args[2] = sta_idx;
+	ctx->phase = NL80211_DUMP_STA_PHASE_AGGREGATED;
 	err = skb->len;
- out_err:
-	if (sinfo_alloc)
-		cfg80211_sinfo_release_content(&sinfo);
+	goto out_err;
+
+out_err_release:
+	cfg80211_sinfo_release_content(&ctx->sinfo);
+	memset(&ctx->sinfo, 0, sizeof(ctx->sinfo));
+out_err:
 	wiphy_unlock(&rdev->wiphy);
 
 	return err;
 }
 
+static int nl80211_dump_station_done(struct netlink_callback *cb)
+{
+	struct nl80211_dump_station_cb *cb_data = (void *)cb->ctx;
+	struct nl80211_dump_station_ctx *ctx = cb_data->ctx;
+
+	if (ctx) {
+		cfg80211_sinfo_release_content(&ctx->sinfo);
+		kfree(ctx);
+	}
+	return 0;
+}
+
 static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
 {
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -19319,6 +19423,14 @@ static const struct genl_ops nl80211_ops[] = {
 		/* can be retrieved by unprivileged users */
 		.internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY),
 	},
+	{
+		.cmd = NL80211_CMD_GET_STATION,
+		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+		.doit = nl80211_get_station,
+		.dumpit = nl80211_dump_station,
+		.done = nl80211_dump_station_done,
+		.internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV),
+	},
 };
 
 static const struct genl_small_ops nl80211_small_ops[] = {
@@ -19418,13 +19530,6 @@ static const struct genl_small_ops nl80211_small_ops[] = {
 		.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP |
 					 NL80211_FLAG_MLO_VALID_LINK_ID),
 	},
-	{
-		.cmd = NL80211_CMD_GET_STATION,
-		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
-		.doit = nl80211_get_station,
-		.dumpit = nl80211_dump_station,
-		.internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV),
-	},
 	{
 		.cmd = NL80211_CMD_SET_STATION,
 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
-- 
2.43.0


^ permalink raw reply related

* [PATCH wireless-next 2/5] wifi: cfg80211: Add helper to pack station-level STA_INFO
From: P Praneesh @ 2026-06-07 17:59 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, ath12k, praneesh.p
In-Reply-To: <20260607175912.2266215-1-praneesh.p@oss.qualcomm.com>

Add a helper function nl80211_put_sta_info_common() to pack the
station-level (aggregated) STA information into a netlink message.
This prepares the code for future enhancements such as supporting
fragmented link statistics in nl80211_dump_station.

Signed-off-by: P Praneesh <praneesh.p@oss.qualcomm.com>
---
 net/wireless/nl80211.c | 59 ++++++++++++++++++++++++++----------------
 1 file changed, 37 insertions(+), 22 deletions(-)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 33609a065423..88232c323d9c 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -7841,32 +7841,15 @@ static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,
 	return true;
 }
 
-static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
-				u32 seq, int flags,
-				struct cfg80211_registered_device *rdev,
-				struct wireless_dev *wdev,
-				const u8 *mac_addr, struct station_info *sinfo)
+static int nl80211_put_sta_info_common(struct sk_buff *msg,
+				       struct cfg80211_registered_device *rdev,
+				       struct station_info *sinfo)
 {
-	void *hdr;
 	struct nlattr *sinfoattr, *bss_param;
 
-	hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
-	if (!hdr) {
-		cfg80211_sinfo_release_content(sinfo);
-		return -1;
-	}
-
-	if ((wdev->netdev &&
-	     nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex)) ||
-	    nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
-			      NL80211_ATTR_PAD) ||
-	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
-	    nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation))
-		goto nla_put_failure;
-
 	sinfoattr = nla_nest_start_noflag(msg, NL80211_ATTR_STA_INFO);
 	if (!sinfoattr)
-		goto nla_put_failure;
+		return -EMSGSIZE;
 
 #define PUT_SINFO(attr, memb, type) do {				\
 	BUILD_BUG_ON(sizeof(type) == sizeof(u64));			\
@@ -8044,6 +8027,38 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
 	}
 
 	nla_nest_end(msg, sinfoattr);
+	return 0;
+
+nla_put_failure:
+	nla_nest_cancel(msg, sinfoattr);
+	return -EMSGSIZE;
+}
+
+static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
+				u32 seq, int flags,
+				struct cfg80211_registered_device *rdev,
+				struct wireless_dev *wdev,
+				const u8 *mac_addr, struct station_info *sinfo)
+{
+	void *hdr;
+
+	hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
+	if (!hdr) {
+		cfg80211_sinfo_release_content(sinfo);
+		return -1;
+	}
+
+	if ((wdev->netdev &&
+	     nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex)) ||
+	    nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+			      NL80211_ATTR_PAD) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
+	    nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation))
+		goto nla_put_failure;
+
+	/* Use common helper for aggregated STA_INFO */
+	if (nl80211_put_sta_info_common(msg, rdev, sinfo))
+		goto nla_put_failure;
 
 	if (sinfo->assoc_req_ies_len &&
 	    nla_put(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
@@ -8070,7 +8085,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
 	genlmsg_end(msg, hdr);
 	return 0;
 
- nla_put_failure:
+nla_put_failure:
 	cfg80211_sinfo_release_content(sinfo);
 	genlmsg_cancel(msg, hdr);
 	return -EMSGSIZE;
-- 
2.43.0


^ permalink raw reply related

* [PATCH wireless-next 1/5] wifi: cfg80211: Drop unused link stats handling in nl80211_send_station()
From: P Praneesh @ 2026-06-07 17:59 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, ath12k, praneesh.p
In-Reply-To: <20260607175912.2266215-1-praneesh.p@oss.qualcomm.com>

Remove the link level statistics handling from
nl80211_send_station() and drop the unused link_stats parameter
from its signature and callers. The removed code iterated over
each MLO link and attempted to send link specific station data
through NL80211_ATTR_MLO_LINKS, but this logic was never used
because link_stats was always false.

This logic was introduced during early work on link level station
statistics with the intention of reporting information for each
link. Due to message size concerns when a station has multiple
links, the feature was disabled behind the link_stats flag and
remained unused.

The link level reporting block in nl80211_send_station() is dead
code and cannot support larger messages, so remove it. This
cleanup also prepares for proper link level statistics reporting
in nl80211_dump_station() in a later patch, where fragmentation
allows safe transmission of multi link data.

Signed-off-by: P Praneesh <praneesh.p@oss.qualcomm.com>
---
 net/wireless/nl80211.c | 227 +----------------------------------------
 1 file changed, 5 insertions(+), 222 deletions(-)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 85057bd4d565..33609a065423 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -7841,197 +7841,14 @@ static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,
 	return true;
 }
 
-static int nl80211_fill_link_station(struct sk_buff *msg,
-				     struct cfg80211_registered_device *rdev,
-				     struct link_station_info *link_sinfo)
-{
-	struct nlattr *bss_param, *link_sinfoattr;
-
-#define PUT_LINK_SINFO(attr, memb, type) do {				\
-	BUILD_BUG_ON(sizeof(type) == sizeof(u64));			\
-	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&	\
-	    nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr,		\
-			     link_sinfo->memb))				\
-		goto nla_put_failure;					\
-	} while (0)
-#define PUT_LINK_SINFO_U64(attr, memb) do {				\
-	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&	\
-	    nla_put_u64_64bit(msg, NL80211_STA_INFO_ ## attr,		\
-			      link_sinfo->memb, NL80211_STA_INFO_PAD))	\
-		goto nla_put_failure;					\
-	} while (0)
-
-	link_sinfoattr = nla_nest_start_noflag(msg, NL80211_ATTR_STA_INFO);
-	if (!link_sinfoattr)
-		goto nla_put_failure;
-
-	PUT_LINK_SINFO(INACTIVE_TIME, inactive_time, u32);
-
-	if (link_sinfo->filled & (BIT_ULL(NL80211_STA_INFO_RX_BYTES) |
-			     BIT_ULL(NL80211_STA_INFO_RX_BYTES64)) &&
-	    nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,
-			(u32)link_sinfo->rx_bytes))
-		goto nla_put_failure;
-
-	if (link_sinfo->filled & (BIT_ULL(NL80211_STA_INFO_TX_BYTES) |
-			     BIT_ULL(NL80211_STA_INFO_TX_BYTES64)) &&
-	    nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
-			(u32)link_sinfo->tx_bytes))
-		goto nla_put_failure;
-
-	PUT_LINK_SINFO_U64(RX_BYTES64, rx_bytes);
-	PUT_LINK_SINFO_U64(TX_BYTES64, tx_bytes);
-	PUT_LINK_SINFO_U64(RX_DURATION, rx_duration);
-	PUT_LINK_SINFO_U64(TX_DURATION, tx_duration);
-
-	if (wiphy_ext_feature_isset(&rdev->wiphy,
-				    NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
-		PUT_LINK_SINFO(AIRTIME_WEIGHT, airtime_weight, u16);
-
-	switch (rdev->wiphy.signal_type) {
-	case CFG80211_SIGNAL_TYPE_MBM:
-		PUT_LINK_SINFO(SIGNAL, signal, u8);
-		PUT_LINK_SINFO(SIGNAL_AVG, signal_avg, u8);
-		break;
-	default:
-		break;
-	}
-	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) {
-		if (!nl80211_put_signal(msg, link_sinfo->chains,
-					link_sinfo->chain_signal,
-					NL80211_STA_INFO_CHAIN_SIGNAL))
-			goto nla_put_failure;
-	}
-	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) {
-		if (!nl80211_put_signal(msg, link_sinfo->chains,
-					link_sinfo->chain_signal_avg,
-					NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
-			goto nla_put_failure;
-	}
-	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE)) {
-		if (!nl80211_put_sta_rate(msg, &link_sinfo->txrate,
-					  NL80211_STA_INFO_TX_BITRATE))
-			goto nla_put_failure;
-	}
-	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) {
-		if (!nl80211_put_sta_rate(msg, &link_sinfo->rxrate,
-					  NL80211_STA_INFO_RX_BITRATE))
-			goto nla_put_failure;
-	}
-
-	PUT_LINK_SINFO(RX_PACKETS, rx_packets, u32);
-	PUT_LINK_SINFO(TX_PACKETS, tx_packets, u32);
-	PUT_LINK_SINFO(TX_RETRIES, tx_retries, u32);
-	PUT_LINK_SINFO(TX_FAILED, tx_failed, u32);
-	PUT_LINK_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32);
-	PUT_LINK_SINFO(BEACON_LOSS, beacon_loss_count, u32);
-
-	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) {
-		bss_param = nla_nest_start_noflag(msg,
-						  NL80211_STA_INFO_BSS_PARAM);
-		if (!bss_param)
-			goto nla_put_failure;
-
-		if (((link_sinfo->bss_param.flags &
-		      BSS_PARAM_FLAGS_CTS_PROT) &&
-		     nla_put_flag(msg, NL80211_STA_BSS_PARAM_CTS_PROT)) ||
-		    ((link_sinfo->bss_param.flags &
-		      BSS_PARAM_FLAGS_SHORT_PREAMBLE) &&
-		     nla_put_flag(msg,
-				  NL80211_STA_BSS_PARAM_SHORT_PREAMBLE)) ||
-		    ((link_sinfo->bss_param.flags &
-		      BSS_PARAM_FLAGS_SHORT_SLOT_TIME) &&
-		     nla_put_flag(msg,
-				  NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME)) ||
-		    nla_put_u8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
-			       link_sinfo->bss_param.dtim_period) ||
-		    nla_put_u16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
-				link_sinfo->bss_param.beacon_interval))
-			goto nla_put_failure;
-
-		nla_nest_end(msg, bss_param);
-	}
-
-	PUT_LINK_SINFO_U64(RX_DROP_MISC, rx_dropped_misc);
-	PUT_LINK_SINFO_U64(BEACON_RX, rx_beacon);
-	PUT_LINK_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8);
-	PUT_LINK_SINFO(RX_MPDUS, rx_mpdu_count, u32);
-	PUT_LINK_SINFO(FCS_ERROR_COUNT, fcs_err_count, u32);
-	if (wiphy_ext_feature_isset(&rdev->wiphy,
-				    NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT)) {
-		PUT_LINK_SINFO(ACK_SIGNAL, ack_signal, u8);
-		PUT_LINK_SINFO(ACK_SIGNAL_AVG, avg_ack_signal, s8);
-	}
-
-#undef PUT_LINK_SINFO
-#undef PUT_LINK_SINFO_U64
-
-	if (link_sinfo->pertid) {
-		struct nlattr *tidsattr;
-		int tid;
-
-		tidsattr = nla_nest_start_noflag(msg,
-						 NL80211_STA_INFO_TID_STATS);
-		if (!tidsattr)
-			goto nla_put_failure;
-
-		for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) {
-			struct cfg80211_tid_stats *tidstats;
-			struct nlattr *tidattr;
-
-			tidstats = &link_sinfo->pertid[tid];
-
-			if (!tidstats->filled)
-				continue;
-
-			tidattr = nla_nest_start_noflag(msg, tid + 1);
-			if (!tidattr)
-				goto nla_put_failure;
-
-#define PUT_TIDVAL_U64(attr, memb) do {					\
-	if (tidstats->filled & BIT(NL80211_TID_STATS_ ## attr) &&	\
-	    nla_put_u64_64bit(msg, NL80211_TID_STATS_ ## attr,		\
-			      tidstats->memb, NL80211_TID_STATS_PAD))	\
-		goto nla_put_failure;					\
-	} while (0)
-
-			PUT_TIDVAL_U64(RX_MSDU, rx_msdu);
-			PUT_TIDVAL_U64(TX_MSDU, tx_msdu);
-			PUT_TIDVAL_U64(TX_MSDU_RETRIES, tx_msdu_retries);
-			PUT_TIDVAL_U64(TX_MSDU_FAILED, tx_msdu_failed);
-
-#undef PUT_TIDVAL_U64
-			if ((tidstats->filled &
-			     BIT(NL80211_TID_STATS_TXQ_STATS)) &&
-			    !nl80211_put_txq_stats(msg, &tidstats->txq_stats,
-						   NL80211_TID_STATS_TXQ_STATS))
-				goto nla_put_failure;
-
-			nla_nest_end(msg, tidattr);
-		}
-
-		nla_nest_end(msg, tidsattr);
-	}
-
-	nla_nest_end(msg, link_sinfoattr);
-	return 0;
-
-nla_put_failure:
-	return -EMSGSIZE;
-}
-
 static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
 				u32 seq, int flags,
 				struct cfg80211_registered_device *rdev,
 				struct wireless_dev *wdev,
-				const u8 *mac_addr, struct station_info *sinfo,
-				bool link_stats)
+				const u8 *mac_addr, struct station_info *sinfo)
 {
 	void *hdr;
 	struct nlattr *sinfoattr, *bss_param;
-	struct link_station_info *link_sinfo;
-	struct nlattr *links, *link;
-	int link_id;
 
 	hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
 	if (!hdr) {
@@ -8249,40 +8066,6 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
 			goto nla_put_failure;
 	}
 
-	if (link_stats && sinfo->valid_links) {
-		links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
-		if (!links)
-			goto nla_put_failure;
-
-		for_each_valid_link(sinfo, link_id) {
-			link_sinfo = sinfo->links[link_id];
-
-			if (WARN_ON_ONCE(!link_sinfo))
-				continue;
-
-			if (!is_valid_ether_addr(link_sinfo->addr))
-				continue;
-
-			link = nla_nest_start(msg, link_id + 1);
-			if (!link)
-				goto nla_put_failure;
-
-			if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
-				       link_id))
-				goto nla_put_failure;
-
-			if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
-				    link_sinfo->addr))
-				goto nla_put_failure;
-
-			if (nl80211_fill_link_station(msg, rdev, link_sinfo))
-				goto nla_put_failure;
-
-			nla_nest_end(msg, link);
-		}
-		nla_nest_end(msg, links);
-	}
-
 	cfg80211_sinfo_release_content(sinfo);
 	genlmsg_end(msg, hdr);
 	return 0;
@@ -8540,7 +8323,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
 				NETLINK_CB(cb->skb).portid,
 				cb->nlh->nlmsg_seq, NLM_F_MULTI,
 				rdev, wdev, mac_addr,
-				&sinfo, false) < 0)
+				&sinfo) < 0)
 			goto out;
 
 		sta_idx++;
@@ -8604,7 +8387,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
 
 	if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION,
 				 info->snd_portid, info->snd_seq, 0,
-				 rdev, wdev, mac_addr, &sinfo, false) < 0) {
+				 rdev, wdev, mac_addr, &sinfo) < 0) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
 	}
@@ -21626,7 +21409,7 @@ void cfg80211_new_sta(struct wireless_dev *wdev, const u8 *mac_addr,
 		return;
 
 	if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, 0, 0, 0,
-				 rdev, wdev, mac_addr, sinfo, false) < 0) {
+				 rdev, wdev, mac_addr, sinfo) < 0) {
 		nlmsg_free(msg);
 		return;
 	}
@@ -21656,7 +21439,7 @@ void cfg80211_del_sta_sinfo(struct wireless_dev *wdev, const u8 *mac_addr,
 	}
 
 	if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0,
-				 rdev, wdev, mac_addr, sinfo, false) < 0) {
+				 rdev, wdev, mac_addr, sinfo) < 0) {
 		nlmsg_free(msg);
 		return;
 	}
-- 
2.43.0


^ permalink raw reply related

* [PATCH wireless-next 0/5] wifi: cfg80211: Add fragmented per-link station stats in MLO
From: P Praneesh @ 2026-06-07 17:59 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, ath12k, praneesh.p

This series introduces fragmentation support for Multi-Link Operation
(MLO) station statistics in nl80211.

The current nl80211_dump_station() implementation works well for legacy
(single-link) stations, but it does not scale for MLO peers. With
multiple links per station, dumping all per-link information in a single
netlink message can exceed size limits, resulting in -EMSGSIZE errors
and incomplete dumps.

With 802.11be (Wi‑Fi 7), a single station may have multiple links, each
with its own statistics. To address this, this series introduces a
stateful dump mechanism that splits station information into multiple
messages when required.

The dump is performed in two phases:
  - Phase 0: Aggregated (station-level) statistics
  - Phase 1: Per-link statistics sent in separate messages

This ensures:
  - Reliable delivery of complete station information for MLO peers
  - Backward compatibility for legacy stations
  - A scalable design for future extensions

Summary of changes:
  - Drop unused per-link stats handling from nl80211_send_station()
  - Introduce a helper to pack common station-level STA_INFO attributes
  - Refactor nl80211_dump_station() to use a stateful dump context
  - Implement per-link stats fragmentation using a two-phase approach
  - Add optional MAC address filtering for per-link station dumps

Message Layout Examples:

Aggregated Stats (Phase 0)

NL80211_CMD_NEW_STATION
 ├─ NL80211_ATTR_IFINDEX
 ├─ NL80211_ATTR_MAC
 ├─ NL80211_ATTR_GENERATION
 └─ NL80211_ATTR_STA_INFO
      ├─ <aggregated station attributes>
      └─ ...

Note: No NL80211_ATTR_MLO_LINKS is included in this phase.
Per‑link stats are sent in Phase 1.

Per-Link Stats (Phase 1)

NL80211_CMD_NEW_STATION
  ├─ NL80211_ATTR_IFINDEX
  ├─ NL80211_ATTR_MAC                 (MLO MAC)
  ├─ NL80211_ATTR_GENERATION
  └─ NL80211_ATTR_STA_INFO
       └─ NL80211_ATTR_MLO_LINKS
            ├─ [link0]
            │    ├─ NL80211_ATTR_MLO_LINK_ID = 0
            │    ├─ NL80211_ATTR_MAC = <link0 MAC>
            │    ├─ NL80211_STA_INFO_RX_BYTES
            │    ├─ NL80211_STA_INFO_TX_BYTES
            │    └─ ... more link0 stats ...
            │
            ├─ [link1]
            │    ├─ NL80211_ATTR_MLO_LINK_ID = 1
            │    ├─ NL80211_ATTR_MAC = <link1 MAC>
            │    ├─ NL80211_STA_INFO_RX_BYTES
            │    ├─ NL80211_STA_INFO_TX_BYTES
            │    └─ ... more link1 stats ...
            │
            └─ [link2]
                 ├─ NL80211_ATTR_MLO_LINK_ID = 2
                 ├─ NL80211_ATTR_MAC = <link2 MAC>
                 ├─ NL80211_STA_INFO_RX_BYTES
                 ├─ NL80211_STA_INFO_TX_BYTES
                 └─ ... more link2 stats ...


Testing:
  - Verified using "iw station dump" on a 3-link MLO station
  - iw requires some updates to parse fragmented per-link stats
    (e.g., NL80211_ATTR_MLO_LINKS handling). Those changes will be
    submitted separately.

Sample station dump output:

Single link EHT STA:
~~~~~~~~~~~~~~~~~~~

Station ff:ff:ff:0f:f8:64 (on wlan0)
        authorized:     yes
        authenticated:  yes
        associated:     yes
        preamble:       long
        WMM/WME:        yes
        MFP:            yes
        TDLS peer:      no
        inactive time:  182068 ms
        rx bytes:       1567
        rx packets:     15
        tx bytes:       1083
        tx packets:     6
        tx retries:     0
        tx failed:      0
        rx drop misc:   0
        signal:         -30 dBm
        signal avg:     -27 dBm
        tx bitrate:     6.0 MBit/s
        tx duration:    3136 us
        rx bitrate:     1441.3 MBit/s 80MHz EHT-MCS 7 EHT-NSS 4 EHT-GI 0
        rx duration:    0 us
        last ack signal:-31 dBm
        avg ack signal: -31 dBm
        DTIM period:    2
        beacon interval:100
        connected time: 185 seconds
        associated at [boottime]:       157.326s
        associated at:  157326 ms
        current time:   342408 ms

        Link 0:
                address: ff:ff:ff:0f:f8:64
                inactive time:  182068 ms
                rx bytes:       1567
                rx packets:     15
                tx bytes:       1083
                tx packets:     6
                tx retries:     0
                tx failed:      0
                rx drop misc:   0
                signal:         -30 dBm
                signal avg:     -29 dBm
                tx bitrate:     6.0 MBit/s
                tx duration:    1568 us
                rx duration:    0 us
                last ack signal:-31 dBm
                avg ack signal: -31 dBm
                DTIM period:    2
                beacon interval:100
				
3 link MLO EHT STA:
~~~~~~~~~~~~~~~~~~

Station ff:ff:ff:ff:5f:23 (on wlan0)
        authorized:     yes
        authenticated:  yes
        associated:     yes
        preamble:       long
        WMM/WME:        yes
        MFP:            yes
        TDLS peer:      no
        inactive time:  173476 ms
        rx bytes:       1036
        rx packets:     9
        tx bytes:       1694
        tx packets:     6
        tx retries:     1
        tx failed:      1
        rx drop misc:   0
        signal:         -20 dBm
        signal avg:     -22 dBm
        tx bitrate:     6.0 MBit/s
        tx duration:    2476 us
        rx duration:    0 us
        last ack signal:-22 dBm
        avg ack signal: -22 dBm
        DTIM period:    2
        beacon interval:100
        connected time: 174 seconds
        associated at [boottime]:       168.236s
        associated at:  168236 ms
        current time:   342412 ms

        Link 0:
                address: ff:ff:ff:ff:5f:23
                inactive time:  173476 ms
                rx bytes:       138
                rx packets:     3
                tx bytes:       0
                tx packets:     0
                tx retries:     0
                tx failed:      0
                rx drop misc:   0
                signal:         -27 dBm
                signal avg:     -28 dBm
                tx duration:    0 us
                rx duration:    0 us
                DTIM period:    2
                beacon interval:100
        Link 1:
                address: ff:ff:ff:ff:5f:24
                inactive time:  173976 ms
                rx bytes:       898
                rx packets:     6
                tx bytes:       1694
                tx packets:     6
                tx retries:     0
                tx failed:      0
                rx drop misc:   0
                signal:         -20 dBm
                signal avg:     -22 dBm
                tx bitrate:     6.0 MBit/s
                tx duration:    2476 us
                rx bitrate:     6.0 MBit/s
                rx duration:    0 us
                last ack signal:-22 dBm
                avg ack signal: -22 dBm
                DTIM period:    2
                beacon interval:100
        Link 2:
                address: ff:ff:ff:ff:5f:25
                inactive time:  174188 ms
                rx bytes:       0
                rx packets:     0
                tx bytes:       0
                tx packets:     0
                tx retries:     1
                tx failed:      1
                rx drop misc:   0
                signal:         -30 dBm
                signal avg:     -31 dBm
                tx bitrate:     12.0 MBit/s
                tx duration:    1920 us
                rx duration:    0 us
                DTIM period:    2
                beacon interval:100
				
With HE AP and STA:
~~~~~~~~~~~~~~~~~~

Station ff:ff:ff:f1:f8:f4 (on wlan0)
        authorized:     yes
        authenticated:  yes
        associated:     yes
        preamble:       long
        WMM/WME:        yes
        MFP:            yes
        TDLS peer:      no
        inactive time:  10176 ms
        rx bytes:       968
        rx packets:     10
        tx bytes:       856
        tx packets:     6
        tx retries:     0
        tx failed:      0
        rx drop misc:   0
        signal:         -24 dBm
        signal avg:     -27 dBm
        tx bitrate:     6.0 MBit/s
        tx duration:    1300 us
        rx bitrate:     1297.1 MBit/s 80MHz HE-MCS 6 HE-NSS 4
        rx duration:    0 us
        last ack signal:-30 dBm
        avg ack signal: -30 dBm
        DTIM period:    2
        beacon interval:100
        short slot time:yes
        connected time: 11 seconds
        associated at [boottime]:       69.552s
        associated at:  69530 ms
        current time:   80780 ms 

P Praneesh (5):
  wifi: cfg80211: Drop unused link stats handling in
    nl80211_send_station()
  wifi: cfg80211: Add helper to pack station-level STA_INFO
  wifi: cfg80211: Refactor nl80211_dump_station() to prepare for
    per-link stats
  wifi: cfg80211: Fragment per-link station stats in
    nl80211_dump_station()
  wifi: cfg80211: support MAC address filtering in station dump for link
    stats

 include/uapi/linux/nl80211.h |  32 ++-
 net/wireless/nl80211.c       | 473 ++++++++++++++++++++++++++---------
 2 files changed, 392 insertions(+), 113 deletions(-)


base-commit: 4cc0cc0b297c17c2b106d6892bd13d9c32fe66ce
-- 
2.43.0


^ permalink raw reply

* Re: [PATCH ath-next 2/6] wifi: ath12k: Add support for 4-address mode
From: Jeff Johnson @ 2026-06-07 15:33 UTC (permalink / raw)
  To: Tamizh Chelvam Raja, ath12k; +Cc: linux-wireless
In-Reply-To: <20260525110942.2890212-3-tamizh.raja@oss.qualcomm.com>

On 5/25/2026 4:09 AM, Tamizh Chelvam Raja wrote:
...
> @@ -999,6 +1007,11 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw,
>  			skb_cb->vif = vif;
>  			skb_cb->ar = tmp_ar;
>  
> +			if (ahsta && ahsta->enable_4addr)
> +				arsta = rcu_dereference(ahsta->link[link_id]);
> +			else
> +				arsta = NULL;
> +
Tamizh, please check the smatch warning at:
https://lore.kernel.org/oe-kbuild/202606051125.XaYVDQZf-lkp@intel.com/

This looks legitimate since there is a path where link_id can be 15, exceeding
the ahsta->link[] array.

/jeff

^ permalink raw reply

* [PATCHv3 wireless-next] wifi: mac80211: fold tid_ampdu_rx allocations into a flexible array
From: Rosen Penev @ 2026-06-07  2:12 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg, open list

Convert the separately-allocated reorder_buf pointer to a C99 flexible
array member at the end of struct tid_ampdu_rx, with both the
sk_buff_head and the jiffies timestamp in each array element. This
collapses three allocations into one and removes the corresponding
kfree() pairs from the error and free paths.

Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
 v3: Fix kernel-doc
 v2: use anonymous struct to combine allocations
 net/mac80211/agg-rx.c   | 22 +++-------------------
 net/mac80211/rx.c       | 14 +++++++-------
 net/mac80211/sta_info.h | 13 ++++++++-----
 3 files changed, 18 insertions(+), 31 deletions(-)

diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 0140ac826b23..9629e00069a1 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -49,9 +49,7 @@ static void ieee80211_free_tid_rx(struct rcu_head *h)
 	int i;

 	for (i = 0; i < tid_rx->buf_size; i++)
-		__skb_queue_purge(&tid_rx->reorder_buf[i]);
-	kfree(tid_rx->reorder_buf);
-	kfree(tid_rx->reorder_time);
+		__skb_queue_purge(&tid_rx->reorder[i].buf);
 	kfree(tid_rx);
 }

@@ -412,7 +410,7 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
 	}

 	/* prepare A-MPDU MLME for Rx aggregation */
-	tid_agg_rx = kzalloc_obj(*tid_agg_rx);
+	tid_agg_rx = kzalloc_flex(*tid_agg_rx, reorder, buf_size);
 	if (!tid_agg_rx)
 		goto end;

@@ -426,27 +424,13 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta,
 	timer_setup(&tid_agg_rx->reorder_timer,
 		    sta_rx_agg_reorder_timer_expired, 0);

-	/* prepare reordering buffer */
-	tid_agg_rx->reorder_buf =
-		kzalloc_objs(struct sk_buff_head, buf_size);
-	tid_agg_rx->reorder_time =
-		kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL);
-	if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) {
-		kfree(tid_agg_rx->reorder_buf);
-		kfree(tid_agg_rx->reorder_time);
-		kfree(tid_agg_rx);
-		goto end;
-	}
-
 	for (i = 0; i < buf_size; i++)
-		__skb_queue_head_init(&tid_agg_rx->reorder_buf[i]);
+		__skb_queue_head_init(&tid_agg_rx->reorder[i].buf);

 	ret = drv_ampdu_action(local, sta->sdata, &params);
 	ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n",
 	       sta->sta.addr, tid, ret);
 	if (ret) {
-		kfree(tid_agg_rx->reorder_buf);
-		kfree(tid_agg_rx->reorder_time);
 		kfree(tid_agg_rx);
 		goto end;
 	}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index db421edfd54c..fb9a3574afe9 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1188,7 +1188,7 @@ static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
 static inline bool ieee80211_rx_reorder_ready(struct tid_ampdu_rx *tid_agg_rx,
 					      int index)
 {
-	struct sk_buff_head *frames = &tid_agg_rx->reorder_buf[index];
+	struct sk_buff_head *frames = &tid_agg_rx->reorder[index].buf;
 	struct sk_buff *tail = skb_peek_tail(frames);
 	struct ieee80211_rx_status *status;

@@ -1211,7 +1211,7 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
 					    int index,
 					    struct sk_buff_head *frames)
 {
-	struct sk_buff_head *skb_list = &tid_agg_rx->reorder_buf[index];
+	struct sk_buff_head *skb_list = &tid_agg_rx->reorder[index].buf;
 	struct sk_buff *skb;
 	struct ieee80211_rx_status *status;

@@ -1290,14 +1290,14 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
 				continue;
 			}
 			if (skipped &&
-			    !time_after(jiffies, tid_agg_rx->reorder_time[j] +
+			    !time_after(jiffies, tid_agg_rx->reorder[j].time +
 					HT_RX_REORDER_BUF_TIMEOUT))
 				goto set_release_timer;

 			/* don't leave incomplete A-MSDUs around */
 			for (i = (index + 1) % tid_agg_rx->buf_size; i != j;
 			     i = (i + 1) % tid_agg_rx->buf_size)
-				__skb_queue_purge(&tid_agg_rx->reorder_buf[i]);
+				__skb_queue_purge(&tid_agg_rx->reorder[i].buf);

 			ht_dbg_ratelimited(sdata,
 					   "release an RX reorder frame due to timeout on earlier frames\n");
@@ -1331,7 +1331,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,

 		if (!tid_agg_rx->removed)
 			mod_timer(&tid_agg_rx->reorder_timer,
-				  tid_agg_rx->reorder_time[j] + 1 +
+				  tid_agg_rx->reorder[j].time + 1 +
 				  HT_RX_REORDER_BUF_TIMEOUT);
 	} else {
 		timer_delete(&tid_agg_rx->reorder_timer);
@@ -1426,9 +1426,9 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
 	}

 	/* put the frame in the reordering buffer */
-	__skb_queue_tail(&tid_agg_rx->reorder_buf[index], skb);
+	__skb_queue_tail(&tid_agg_rx->reorder[index].buf, skb);
 	if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
-		tid_agg_rx->reorder_time[index] = jiffies;
+		tid_agg_rx->reorder[index].time = jiffies;
 		tid_agg_rx->stored_mpdu_num++;
 		ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames);
 	}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 26138934b72e..b5f1310d2cd6 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -207,11 +207,8 @@ struct tid_ampdu_tx {
 /**
  * struct tid_ampdu_rx - TID aggregation information (Rx).
  *
- * @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an
- *	A-MSDU with individually reported subframes.
  * @reorder_buf_filtered: bitmap indicating where there are filtered frames in
  *	the reorder buffer that should be ignored when releasing frames
- * @reorder_time: jiffies when skb was added
  * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
  * @reorder_timer: releases expired frames from the reorder buffer.
  * @sta: station we are attached to
@@ -228,6 +225,10 @@ struct tid_ampdu_tx {
  *	and ssn.
  * @removed: this session is removed (but might have been found due to RCU)
  * @started: this session has started (head ssn or higher was received)
+ * @reorder: array containing a buffer with a jiffies component
+ * @reorder.buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an
+ *     A-MSDU with individually reported subframes.
+ * @reorder.time: jiffies when skb was added
  *
  * This structure's lifetime is managed by RCU, assignments to
  * the array holding it must hold the aggregation mutex.
@@ -241,8 +242,6 @@ struct tid_ampdu_rx {
 	struct rcu_head rcu_head;
 	spinlock_t reorder_lock;
 	u64 reorder_buf_filtered;
-	struct sk_buff_head *reorder_buf;
-	unsigned long *reorder_time;
 	struct sta_info *sta;
 	struct timer_list session_timer;
 	struct timer_list reorder_timer;
@@ -256,6 +255,10 @@ struct tid_ampdu_rx {
 	u8 auto_seq:1,
 	   removed:1,
 	   started:1;
+	struct {
+		struct sk_buff_head buf;
+		unsigned long time;
+	} reorder[];
 };

 /**
--
2.54.0


^ permalink raw reply related

* [wireless-next:for-next] BUILD SUCCESS 4cc0cc0b297c17c2b106d6892bd13d9c32fe66ce
From: kernel test robot @ 2026-06-06 23:52 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless

tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git for-next
branch HEAD: 4cc0cc0b297c17c2b106d6892bd13d9c32fe66ce  wifi: mac80211: Add 802.3 multicast encapsulation offload support

elapsed time: 1597m

configs tested: 175
configs skipped: 15

The following configs have been built successfully.
More configs may be tested in the coming days.

tested configs:
alpha                             allnoconfig    gcc-15.2.0
alpha                            allyesconfig    gcc-15.2.0
alpha                               defconfig    gcc-16.1.0
arc                              allmodconfig    gcc-15.2.0
arc                               allnoconfig    gcc-15.2.0
arc                              allyesconfig    gcc-15.2.0
arc                                 defconfig    gcc-16.1.0
arc                   randconfig-001-20260606    gcc-8.5.0
arc                   randconfig-002-20260606    gcc-8.5.0
arm                               allnoconfig    clang-23
arm                              allyesconfig    gcc-15.2.0
arm                                 defconfig    clang-23
arm                   randconfig-001-20260606    clang-22
arm                   randconfig-002-20260606    gcc-16.1.0
arm                   randconfig-003-20260606    clang-23
arm                   randconfig-004-20260606    clang-23
arm64                            allmodconfig    clang-19
arm64                             allnoconfig    gcc-15.2.0
arm64                               defconfig    gcc-16.1.0
arm64                          randconfig-001    gcc-8.5.0
arm64                 randconfig-001-20260606    gcc-10.5.0
arm64                          randconfig-002    gcc-14.3.0
arm64                 randconfig-002-20260606    gcc-8.5.0
arm64                          randconfig-003    clang-23
arm64                 randconfig-003-20260606    clang-23
arm64                          randconfig-004    clang-23
arm64                 randconfig-004-20260606    gcc-14.3.0
csky                             allmodconfig    gcc-15.2.0
csky                              allnoconfig    gcc-15.2.0
csky                                defconfig    gcc-16.1.0
csky                           randconfig-001    gcc-10.5.0
csky                  randconfig-001-20260606    gcc-10.5.0
csky                           randconfig-002    gcc-10.5.0
csky                  randconfig-002-20260606    gcc-9.5.0
hexagon                          allmodconfig    clang-17
hexagon                           allnoconfig    clang-23
hexagon                             defconfig    clang-23
hexagon                        randconfig-001    clang-23
hexagon               randconfig-001-20260606    clang-23
hexagon                        randconfig-002    clang-23
hexagon               randconfig-002-20260606    clang-23
i386                             allmodconfig    gcc-14
i386                              allnoconfig    gcc-14
i386                             allyesconfig    gcc-14
i386        buildonly-randconfig-001-20260606    clang-20
i386        buildonly-randconfig-002-20260606    clang-20
i386        buildonly-randconfig-003-20260606    clang-20
i386        buildonly-randconfig-004-20260606    gcc-14
i386        buildonly-randconfig-005-20260606    clang-20
i386        buildonly-randconfig-006-20260606    gcc-13
i386                                defconfig    clang-22
i386                           randconfig-001    clang-20
i386                  randconfig-001-20260606    clang-20
i386                           randconfig-002    gcc-14
i386                  randconfig-002-20260606    gcc-14
i386                           randconfig-003    gcc-14
i386                  randconfig-003-20260606    clang-20
i386                           randconfig-004    clang-20
i386                  randconfig-004-20260606    clang-20
i386                           randconfig-005    gcc-14
i386                  randconfig-005-20260606    gcc-14
i386                           randconfig-006    gcc-14
i386                  randconfig-006-20260606    clang-20
i386                           randconfig-007    gcc-14
i386                  randconfig-007-20260606    clang-20
i386                  randconfig-011-20260606    clang-22
i386                  randconfig-012-20260606    gcc-14
i386                  randconfig-013-20260606    gcc-12
i386                  randconfig-014-20260606    clang-22
i386                  randconfig-015-20260606    clang-22
i386                  randconfig-016-20260606    gcc-14
i386                  randconfig-017-20260606    gcc-14
loongarch                        allmodconfig    clang-19
loongarch                         allnoconfig    clang-23
loongarch                      randconfig-001    clang-23
loongarch             randconfig-001-20260606    clang-23
loongarch                      randconfig-002    gcc-16.1.0
loongarch             randconfig-002-20260606    clang-23
m68k                             allmodconfig    gcc-15.2.0
m68k                              allnoconfig    gcc-15.2.0
m68k                             allyesconfig    gcc-15.2.0
microblaze                        allnoconfig    gcc-15.2.0
microblaze                       allyesconfig    gcc-15.2.0
mips                             allmodconfig    gcc-15.2.0
mips                              allnoconfig    gcc-15.2.0
mips                             allyesconfig    gcc-15.2.0
nios2                            allmodconfig    gcc-11.5.0
nios2                             allnoconfig    gcc-11.5.0
nios2                          randconfig-001    gcc-11.5.0
nios2                 randconfig-001-20260606    gcc-11.5.0
nios2                          randconfig-002    gcc-8.5.0
nios2                 randconfig-002-20260606    gcc-8.5.0
openrisc                         allmodconfig    gcc-15.2.0
openrisc                          allnoconfig    gcc-15.2.0
openrisc                            defconfig    gcc-16.1.0
parisc                           allmodconfig    gcc-15.2.0
parisc                            allnoconfig    gcc-15.2.0
parisc                              defconfig    gcc-16.1.0
parisc                         randconfig-001    gcc-13.4.0
parisc                randconfig-001-20260606    gcc-8.5.0
parisc                         randconfig-002    gcc-8.5.0
parisc                randconfig-002-20260606    gcc-10.5.0
powerpc                          allmodconfig    gcc-15.2.0
powerpc                           allnoconfig    gcc-15.2.0
powerpc                        randconfig-001    gcc-13.4.0
powerpc               randconfig-001-20260606    clang-23
powerpc                        randconfig-002    gcc-10.5.0
powerpc               randconfig-002-20260606    clang-23
powerpc64                      randconfig-001    clang-17
powerpc64             randconfig-001-20260606    gcc-15.2.0
powerpc64                      randconfig-002    clang-23
powerpc64             randconfig-002-20260606    gcc-8.5.0
riscv                             allnoconfig    gcc-15.2.0
riscv                            allyesconfig    clang-16
riscv                               defconfig    clang-23
riscv                 randconfig-001-20260606    gcc-12.5.0
riscv                 randconfig-002-20260606    gcc-8.5.0
s390                              allnoconfig    clang-23
s390                             allyesconfig    gcc-15.2.0
s390                                defconfig    clang-18
s390                  randconfig-001-20260606    gcc-13.4.0
s390                  randconfig-002-20260606    gcc-8.5.0
sh                               allmodconfig    gcc-15.2.0
sh                                allnoconfig    gcc-15.2.0
sh                                  defconfig    gcc-16.1.0
sh                    randconfig-001-20260606    gcc-10.5.0
sh                    randconfig-002-20260606    gcc-16.1.0
sh                        sh7763rdp_defconfig    gcc-16.1.0
sparc                             allnoconfig    gcc-15.2.0
sparc                               defconfig    gcc-16.1.0
sparc                 randconfig-001-20260606    gcc-13.4.0
sparc                 randconfig-002-20260606    gcc-16.1.0
sparc64                          allmodconfig    clang-23
sparc64                             defconfig    clang-23
sparc64               randconfig-001-20260606    gcc-12.5.0
sparc64               randconfig-002-20260606    gcc-8.5.0
um                                allnoconfig    clang-23
um                               allyesconfig    gcc-14
um                                  defconfig    clang-23
um                             i386_defconfig    gcc-14
um                    randconfig-001-20260606    clang-23
um                    randconfig-002-20260606    gcc-14
um                           x86_64_defconfig    clang-23
x86_64                           allmodconfig    clang-20
x86_64                            allnoconfig    clang-20
x86_64                           allyesconfig    clang-20
x86_64                              defconfig    gcc-14
x86_64                         randconfig-001    gcc-14
x86_64                randconfig-001-20260606    gcc-14
x86_64                         randconfig-002    gcc-14
x86_64                randconfig-002-20260606    gcc-14
x86_64                         randconfig-003    clang-20
x86_64                randconfig-003-20260606    gcc-14
x86_64                         randconfig-004    clang-20
x86_64                randconfig-004-20260606    gcc-14
x86_64                         randconfig-005    gcc-14
x86_64                randconfig-005-20260606    gcc-14
x86_64                         randconfig-006    clang-20
x86_64                randconfig-006-20260606    gcc-14
x86_64                randconfig-011-20260606    clang-22
x86_64                randconfig-012-20260606    clang-22
x86_64                randconfig-013-20260606    gcc-14
x86_64                randconfig-014-20260606    clang-22
x86_64                randconfig-015-20260606    clang-22
x86_64                randconfig-016-20260606    clang-22
x86_64                randconfig-071-20260606    clang-20
x86_64                randconfig-072-20260606    gcc-14
x86_64                randconfig-073-20260606    clang-20
x86_64                randconfig-074-20260606    clang-20
x86_64                randconfig-075-20260606    clang-20
x86_64                randconfig-076-20260606    gcc-13
x86_64                          rhel-9.4-rust    clang-20
xtensa                            allnoconfig    gcc-15.2.0
xtensa                randconfig-001-20260606    gcc-8.5.0
xtensa                randconfig-002-20260606    gcc-11.5.0

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* [PATCH next] rfkill: Replace strcpy() with memcpy()
From: david.laight.linux @ 2026-06-06 20:26 UTC (permalink / raw)
  To: Kees Cook, linux-hardening, Arnd Bergmann, linux-kernel,
	linux-wireless
  Cc: Johannes Berg, David Laight

From: David Laight <david.laight.linux@gmail.com>

The length of the string is calculated in order to allocate the correct
sized memory block, use the same length to copy the string.

Signed-off-by: David Laight <david.laight.linux@gmail.com>
---
This is one of a group of patches that remove potentially unbounded
strcpy() calls.

They are mostly replaced by strscpy() or, when strlen() has just been
called, with memcpy() (usually including the '\0').

Calls with copy string literals into arrays are left unchanged.
They are safe and easily detected as such.

The changes were made by getting the compiler to detect the calls and
then fixing the code by hand.

Note that all the changes are only compile tested.

Some Makefiles were changed to allow files to contain strcpy().
As well as 'difficult to fix' files, this included 'show' functions
as they really need to use sysfs_emit() or seq_printf().

All the patches are being sent individually to avoid very long cc lists.
Apologies for the terse commit messages and likely unexpected tags.
(There are about 100 patches in total.)

 net/rfkill/core.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index 4827e1fb8804..9e143c4bfe6a 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -1000,6 +1000,7 @@ struct rfkill * __must_check rfkill_alloc(const char *name,
 {
 	struct rfkill *rfkill;
 	struct device *dev;
+	size_t name_len;
 
 	if (WARN_ON(!ops))
 		return NULL;
@@ -1013,14 +1014,15 @@ struct rfkill * __must_check rfkill_alloc(const char *name,
 	if (WARN_ON(type == RFKILL_TYPE_ALL || type >= NUM_RFKILL_TYPES))
 		return NULL;
 
-	rfkill = kzalloc(sizeof(*rfkill) + strlen(name) + 1, GFP_KERNEL);
+	name_len = strlen(name);
+	rfkill = kzalloc(sizeof(*rfkill) + name_len + 1, GFP_KERNEL);
 	if (!rfkill)
 		return NULL;
 
 	spin_lock_init(&rfkill->lock);
 	INIT_LIST_HEAD(&rfkill->node);
 	rfkill->type = type;
-	strcpy(rfkill->name, name);
+	memcpy(rfkill->name, name, name_len);
 	rfkill->ops = ops;
 	rfkill->data = ops_data;
 
-- 
2.39.5


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox