* [PATCH wireless-next 0/4] wifi: brcmfmac: Add support for 6 GHz operation
@ 2026-11-13 6:31 Michael Reeves via B4 Relay
2026-11-13 6:31 ` [PATCH wireless-next 1/4] wifi: brcmfmac: Add support for 6 GHz chanspecs Michael Reeves via B4 Relay
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-11-13 6:31 UTC (permalink / raw)
To: Arend van Spriel
Cc: asahi, linux-wireless, brcm80211, brcm80211-dev-list.pdl,
linux-kernel, Michael Reeves, Daniel Berlin, Yureka Lilian
Hi Arend, all,
This series adds support for 6 GHz (Wi-Fi 6E) operation to brcmfmac.
It includes HE capability advertisement, 6 GHz chanspec and band
handling and PNO version 3 support. The PNO support is required to avoid
channel-number collisions with 2.4/5 GHz during scheduled scans.
It is adapted from our downstream work in the Asahi kernel by Daniel
Berlin, but reworked to make the code cleaner and more maintainable.
As I don't have a 6 GHz AP to test with, Yureka Lilian tested this
series on my behalf, on her Apple Silicon M3 Mac (BCM4388). Connection
and use of a 6 GHz network was successful, without regressions on 2.4
GHz or 5 GHz operation.
Thank you for your time,
Michael
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
Daniel Berlin (2):
wifi: brcmfmac: Add support for 6 GHz chanspecs
wifi: brcmfmac: Add support for the 6 GHz band
Michael Reeves (2):
wifi: brcmfmac: Advertise HE capabilities
wifi: brcmfmac: Add support for PNO version 3
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 477 +++++++++++++++------
.../broadcom/brcm80211/brcmfmac/cfg80211.h | 2 +
.../broadcom/brcm80211/brcmfmac/fwil_types.h | 68 +++
.../net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 6 +-
.../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 147 ++++++-
.../net/wireless/broadcom/brcm80211/brcmfmac/pno.h | 41 +-
.../net/wireless/broadcom/brcm80211/brcmutil/d11.c | 7 +-
.../broadcom/brcm80211/include/brcmu_d11.h | 3 +-
.../broadcom/brcm80211/include/brcmu_wifi.h | 1 +
9 files changed, 585 insertions(+), 167 deletions(-)
---
base-commit: 3c311bf321442d4c4f452a1a08bc9efce242a1f9
change-id: 20261113-6ghz-a8d587ab3cce
Best regards,
--
Michael Reeves <michael.reeves077@gmail.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH wireless-next 1/4] wifi: brcmfmac: Add support for 6 GHz chanspecs
2026-11-13 6:31 [PATCH wireless-next 0/4] wifi: brcmfmac: Add support for 6 GHz operation Michael Reeves via B4 Relay
@ 2026-11-13 6:31 ` Michael Reeves via B4 Relay
2026-11-13 6:31 ` [PATCH wireless-next 2/4] wifi: brcmfmac: Advertise HE capabilities Michael Reeves via B4 Relay
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-11-13 6:31 UTC (permalink / raw)
To: Arend van Spriel
Cc: asahi, linux-wireless, brcm80211, brcm80211-dev-list.pdl,
linux-kernel, Michael Reeves, Daniel Berlin, Yureka Lilian
From: Daniel Berlin <dberlin@dberlin.org>
Although the AC chanspec format defines a band value for 6 GHz, the
driver currently neither encodes nor decodes it.
Extend the chanspec encoder and decoder to support the 6 GHz band,
replacing the obsolete and unused 3 GHz definition. Populate the band
when converting cfg80211 channels to chanspecs.
Additionally, initialise channel structures prior to encoding in the P2P
path, as the encoder now requires a valid band field.
Tested-by: Yureka Lilian <yureka@cyberchaos.dev>
Signed-off-by: Daniel Berlin <dberlin@dberlin.org>
Co-developed-by: Michael Reeves <michael.reeves077@gmail.com>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 28 +++++++++++++---------
.../net/wireless/broadcom/brcm80211/brcmfmac/p2p.c | 6 ++---
.../net/wireless/broadcom/brcm80211/brcmutil/d11.c | 7 +++++-
.../broadcom/brcm80211/include/brcmu_d11.h | 3 ++-
4 files changed, 28 insertions(+), 16 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index b0eaf96413b7..c4ef4f6d3a06 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -326,6 +326,21 @@ static u8 nl80211_band_to_fwil(enum nl80211_band band)
return 0;
}
+static u8 nl80211_band_to_chanspec_band(enum nl80211_band band)
+{
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ return BRCMU_CHAN_BAND_2G;
+ case NL80211_BAND_5GHZ:
+ return BRCMU_CHAN_BAND_5G;
+ case NL80211_BAND_6GHZ:
+ return BRCMU_CHAN_BAND_6G;
+ default:
+ WARN_ON_ONCE(1);
+ return BRCMU_CHAN_BAND_2G;
+ }
+}
+
static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
struct cfg80211_chan_def *ch)
{
@@ -385,17 +400,7 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
default:
WARN_ON_ONCE(1);
}
- switch (ch->chan->band) {
- case NL80211_BAND_2GHZ:
- ch_inf.band = BRCMU_CHAN_BAND_2G;
- break;
- case NL80211_BAND_5GHZ:
- ch_inf.band = BRCMU_CHAN_BAND_5G;
- break;
- case NL80211_BAND_60GHZ:
- default:
- WARN_ON_ONCE(1);
- }
+ ch_inf.band = nl80211_band_to_chanspec_band(ch->chan->band);
d11inf->encchspec(&ch_inf);
brcmf_dbg(TRACE, "chanspec: 0x%x\n", ch_inf.chspec);
@@ -409,6 +414,7 @@ u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
ch_inf.bw = BRCMU_CHAN_BW_20;
+ ch_inf.band = nl80211_band_to_chanspec_band(ch->band);
d11inf->encchspec(&ch_inf);
return ch_inf.chspec;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
index 813cc15d8b87..06e0d90b194a 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -958,7 +958,7 @@ brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration)
{
struct brcmf_pub *drvr = p2p->cfg->pub;
struct brcmf_cfg80211_vif *vif;
- struct brcmu_chan ch;
+ struct brcmu_chan ch = {};
s32 err = 0;
vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
@@ -1092,7 +1092,7 @@ static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel)
u32 channel_cnt;
u16 *default_chan_list;
u32 i;
- struct brcmu_chan ch;
+ struct brcmu_chan ch = {};
brcmf_dbg(TRACE, "Enter\n");
@@ -2031,7 +2031,7 @@ static void brcmf_p2p_get_current_chanspec(struct brcmf_p2p_info *p2p,
{
struct brcmf_if *ifp;
u8 mac_addr[ETH_ALEN];
- struct brcmu_chan ch;
+ struct brcmu_chan ch = {};
struct brcmf_bss_info_le *bi;
u8 *buf;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c
index 1e2b1e487eb7..6cd43920fe67 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c
@@ -87,7 +87,9 @@ static void brcmu_d11ac_encchspec(struct brcmu_chan *ch)
0, d11ac_bw(ch->bw));
ch->chspec &= ~BRCMU_CHSPEC_D11AC_BND_MASK;
- if (ch->chnum <= CH_MAX_2G_CHANNEL)
+ if (ch->band == BRCMU_CHAN_BAND_6G)
+ ch->chspec |= BRCMU_CHSPEC_D11AC_BND_6G;
+ else if (ch->chnum <= CH_MAX_2G_CHANNEL)
ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G;
else
ch->chspec |= BRCMU_CHSPEC_D11AC_BND_5G;
@@ -222,6 +224,9 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
}
switch (ch->chspec & BRCMU_CHSPEC_D11AC_BND_MASK) {
+ case BRCMU_CHSPEC_D11AC_BND_6G:
+ ch->band = BRCMU_CHAN_BAND_6G;
+ break;
case BRCMU_CHSPEC_D11AC_BND_5G:
ch->band = BRCMU_CHAN_BAND_5G;
break;
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h
index f6344023855c..aae75329e266 100644
--- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h
@@ -81,12 +81,13 @@
#define BRCMU_CHSPEC_D11AC_BND_MASK 0xc000
#define BRCMU_CHSPEC_D11AC_BND_SHIFT 14
#define BRCMU_CHSPEC_D11AC_BND_2G 0x0000
-#define BRCMU_CHSPEC_D11AC_BND_3G 0x4000
+#define BRCMU_CHSPEC_D11AC_BND_6G 0x4000
#define BRCMU_CHSPEC_D11AC_BND_4G 0x8000
#define BRCMU_CHSPEC_D11AC_BND_5G 0xc000
#define BRCMU_CHAN_BAND_2G 0
#define BRCMU_CHAN_BAND_5G 1
+#define BRCMU_CHAN_BAND_6G 2
enum brcmu_chan_bw {
BRCMU_CHAN_BW_20,
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH wireless-next 2/4] wifi: brcmfmac: Advertise HE capabilities
2026-11-13 6:31 [PATCH wireless-next 0/4] wifi: brcmfmac: Add support for 6 GHz operation Michael Reeves via B4 Relay
2026-11-13 6:31 ` [PATCH wireless-next 1/4] wifi: brcmfmac: Add support for 6 GHz chanspecs Michael Reeves via B4 Relay
@ 2026-11-13 6:31 ` Michael Reeves via B4 Relay
2026-11-13 6:31 ` [PATCH wireless-next 3/4] wifi: brcmfmac: Add support for the 6 GHz band Michael Reeves via B4 Relay
2026-11-13 6:31 ` [PATCH wireless-next 4/4] wifi: brcmfmac: Add support for PNO version 3 Michael Reeves via B4 Relay
3 siblings, 0 replies; 5+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-11-13 6:31 UTC (permalink / raw)
To: Arend van Spriel
Cc: asahi, linux-wireless, brcm80211, brcm80211-dev-list.pdl,
linux-kernel, Michael Reeves, Yureka Lilian, Daniel Berlin
From: Michael Reeves <michael.reeves077@gmail.com>
The driver currently does not report HE (802.11ax) capabilities, which
are mandatory in cfg80211 for 6 GHz band operation.
Advertise HE capabilities for the station interface type when the
firmware indicates HE support. Determine the 160 MHz capability bits
from the firmware-reported bandwidth limits, and generate the supported
MCS map based on the number of spatial chains.
Move band setup prior to wiphy registration so cfg80211 validates HE
capabilities at registration time.
Tested-by: Yureka Lilian <yureka@cyberchaos.dev>
Co-developed-by: Daniel Berlin <dberlin@dberlin.org>
Signed-off-by: Daniel Berlin <dberlin@dberlin.org>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 126 +++++++++++++++++++--
.../broadcom/brcm80211/brcmfmac/cfg80211.h | 2 +
.../broadcom/brcm80211/brcmfmac/fwil_types.h | 3 +
3 files changed, 124 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index c4ef4f6d3a06..12545ae15e9e 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -7451,7 +7451,7 @@ static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
}
-static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
+static __le16 brcmf_get_mcs_map(u32 nchain, int supp)
{
u16 mcs_map;
int i;
@@ -7504,6 +7504,112 @@ static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
}
}
+static void brcmf_update_he_cap(struct ieee80211_supported_band *band,
+ struct ieee80211_sband_iftype_data *data,
+ u32 bw_cap[], u32 nchain)
+{
+ struct ieee80211_sta_he_cap *he_cap = &data->he_cap;
+ struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem;
+ struct ieee80211_he_mcs_nss_supp *mcs = &he_cap->he_mcs_nss_supp;
+ __le16 mcs_map = brcmf_get_mcs_map(nchain,
+ IEEE80211_HE_MCS_SUPPORT_0_11);
+ bool bw160 = bw_cap[band->band] & WLC_BW_160MHZ_BIT;
+
+ memset(data, 0, sizeof(*data));
+ data->types_mask = BIT(NL80211_IFTYPE_STATION);
+ he_cap->has_he = true;
+
+ elem->mac_cap_info[0] = IEEE80211_HE_MAC_CAP0_HTC_HE |
+ IEEE80211_HE_MAC_CAP0_TWT_REQ |
+ IEEE80211_HE_MAC_CAP0_TWT_RES;
+ elem->mac_cap_info[1] = IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_8US |
+ IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US;
+ elem->mac_cap_info[2] = IEEE80211_HE_MAC_CAP2_BSR |
+ IEEE80211_HE_MAC_CAP2_BCAST_TWT;
+ elem->mac_cap_info[3] = IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
+ IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_1 |
+ IEEE80211_HE_MAC_CAP3_FLEX_TWT_SCHED;
+ elem->mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU;
+
+ if (band->band == NL80211_BAND_2GHZ) {
+ if (band->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
+ elem->phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+ elem->phy_cap_info[8] |=
+ IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G;
+ } else {
+ if (bw_cap[band->band] & WLC_BW_80MHZ_BIT)
+ elem->phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+ if (bw160) {
+ elem->phy_cap_info[0] |=
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+ elem->phy_cap_info[8] |=
+ IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU |
+ IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU;
+ }
+ }
+
+ elem->phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
+ elem->phy_cap_info[2] = IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+ IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
+ IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO;
+ elem->phy_cap_info[3] = IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK |
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_2 |
+ IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+ IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER;
+ elem->phy_cap_info[4] =
+ IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE |
+ IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4 |
+ IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8;
+ elem->phy_cap_info[5] =
+ IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+ IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK |
+ IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2;
+ elem->phy_cap_info[6] =
+ IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+ IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+ IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB |
+ IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB |
+ IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB |
+ IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE |
+ IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT;
+ elem->phy_cap_info[7] =
+ IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI |
+ IEEE80211_HE_PHY_CAP7_MAX_NC_1;
+ elem->phy_cap_info[8] |=
+ IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI;
+ elem->phy_cap_info[9] =
+ IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU |
+ IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU |
+ IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+ IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB;
+
+ mcs->rx_mcs_80 = mcs_map;
+ mcs->tx_mcs_80 = mcs_map;
+ if (bw160) {
+ mcs->rx_mcs_160 = mcs_map;
+ mcs->tx_mcs_160 = mcs_map;
+ } else {
+ mcs->rx_mcs_160 = cpu_to_le16(0xffff);
+ mcs->tx_mcs_160 = cpu_to_le16(0xffff);
+ }
+
+ band->n_iftype_data = 1;
+ band->iftype_data = data;
+}
+
+static bool brcmf_he_enabled(struct brcmf_if *ifp)
+{
+ u8 enable = 0;
+
+ if (brcmf_fil_xtlv_data_get(ifp, "he", BRCMF_HE_CMD_ENABLE,
+ &enable, sizeof(enable)))
+ return false;
+
+ return enable;
+}
+
static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg)
{
struct brcmf_pub *drvr = cfg->pub;
@@ -7520,8 +7626,10 @@ static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg)
u32 txstreams = 0;
u32 txbf_bfe_cap = 0;
u32 txbf_bfr_cap = 0;
+ bool he_enable;
(void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
+ he_enable = brcmf_he_enabled(ifp);
err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
if (err) {
bphy_err(drvr, "nmode error (%d)\n", err);
@@ -7571,6 +7679,9 @@ static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg)
if (vhtmode)
brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
txbf_bfe_cap, txbf_bfr_cap);
+ if (he_enable)
+ brcmf_update_he_cap(band, &cfg->he_iftype_data[i],
+ bw_cap, nchain);
}
return 0;
@@ -8598,18 +8709,19 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DUMP_OBSS))
ops->dump_survey = brcmf_cfg80211_dump_survey;
+ /* cfg80211 validates the band capabilities when registering */
+ err = brcmf_setup_wiphybands(cfg);
+ if (err) {
+ bphy_err(drvr, "Setting wiphy bands failed (%d)\n", err);
+ goto priv_out;
+ }
+
err = wiphy_register(wiphy);
if (err < 0) {
bphy_err(drvr, "Could not register wiphy device (%d)\n", err);
goto priv_out;
}
- err = brcmf_setup_wiphybands(cfg);
- if (err) {
- bphy_err(drvr, "Setting wiphy bands failed (%d)\n", err);
- goto wiphy_unreg_out;
- }
-
/* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
* setup 40MHz in 2GHz band and enable OBSS scanning.
*/
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
index 1aa99390a951..3168a262109e 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -368,6 +368,7 @@ struct brcmf_cfg80211_wowl {
* @vif_event: vif event signalling.
* @wowl: wowl related information.
* @pno: information of pno module.
+ * @he_iftype_data: per band HE capabilities registered with the wiphy.
*/
struct brcmf_cfg80211_info {
struct wiphy *wiphy;
@@ -401,6 +402,7 @@ struct brcmf_cfg80211_info {
struct brcmf_cfg80211_wowl wowl;
struct brcmf_pno_info *pno;
u8 ac_priority[MAX_8021D_PRIO];
+ struct ieee80211_sband_iftype_data he_iftype_data[NUM_NL80211_BANDS];
};
/**
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index 37d9414546d5..f4e8eeeffa08 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -1375,4 +1375,7 @@ struct brcmf_eventmsgs_ext_le {
u8 mask[] __counted_by(len);
};
+/* "he" iovar sub-command ids */
+#define BRCMF_HE_CMD_ENABLE 0
+
#endif /* FWIL_TYPES_H_ */
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH wireless-next 3/4] wifi: brcmfmac: Add support for the 6 GHz band
2026-11-13 6:31 [PATCH wireless-next 0/4] wifi: brcmfmac: Add support for 6 GHz operation Michael Reeves via B4 Relay
2026-11-13 6:31 ` [PATCH wireless-next 1/4] wifi: brcmfmac: Add support for 6 GHz chanspecs Michael Reeves via B4 Relay
2026-11-13 6:31 ` [PATCH wireless-next 2/4] wifi: brcmfmac: Advertise HE capabilities Michael Reeves via B4 Relay
@ 2026-11-13 6:31 ` Michael Reeves via B4 Relay
2026-11-13 6:31 ` [PATCH wireless-next 4/4] wifi: brcmfmac: Add support for PNO version 3 Michael Reeves via B4 Relay
3 siblings, 0 replies; 5+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-11-13 6:31 UTC (permalink / raw)
To: Arend van Spriel
Cc: asahi, linux-wireless, brcm80211, brcm80211-dev-list.pdl,
linux-kernel, Michael Reeves, Daniel Berlin, Yureka Lilian
From: Daniel Berlin <dberlin@dberlin.org>
While firmware may report 6 GHz support in its band list, the driver
currently ignores the capability.
Enable 6 GHz band support when reported by the firmware and HE is
enabled. This includes registering 6 GHz channels, populating band
bandwidth capabilities, adding regulatory rules, and parsing 6 GHz
chanspecs in channel lists and scan results.
Exclude 6 GHz channels from PNO lists for now, as legacy PNO identifies
channels strictly by channel number, which collides with 2.4 GHz and 5
GHz channel numbering.
Tested-by: Yureka Lilian <yureka@cyberchaos.dev>
Signed-off-by: Daniel Berlin <dberlin@dberlin.org>
Co-developed-by: Michael Reeves <michael.reeves077@gmail.com>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 229 +++++++++++++++------
.../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 3 +
.../broadcom/brcm80211/include/brcmu_wifi.h | 1 +
3 files changed, 166 insertions(+), 67 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 12545ae15e9e..90db55c0ab7a 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -5,6 +5,7 @@
/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
+#include <linux/bitfield.h>
#include <linux/kernel.h>
#include <linux/etherdevice.h>
#include <linux/module.h>
@@ -181,6 +182,14 @@ static struct ieee80211_rate __wl_rates[] = {
.max_power = 30, \
}
+#define CHAN6G(_channel, _freq) {\
+ .band = NL80211_BAND_6GHZ,\
+ .center_freq = (_freq),\
+ .hw_value = (_channel),\
+ .max_antenna_gain = 0,\
+ .max_power = 30,\
+}
+
static struct ieee80211_channel __wl_2ghz_channels[] = {
CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
@@ -197,6 +206,29 @@ static struct ieee80211_channel __wl_5ghz_channels[] = {
CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
};
+static struct ieee80211_channel __wl_6ghz_channels[] = {
+ CHAN6G(2, 5935), CHAN6G(1, 5955), CHAN6G(5, 5975),
+ CHAN6G(9, 5995), CHAN6G(13, 6015), CHAN6G(17, 6035),
+ CHAN6G(21, 6055), CHAN6G(25, 6075), CHAN6G(29, 6095),
+ CHAN6G(33, 6115), CHAN6G(37, 6135), CHAN6G(41, 6155),
+ CHAN6G(45, 6175), CHAN6G(49, 6195), CHAN6G(53, 6215),
+ CHAN6G(57, 6235), CHAN6G(61, 6255), CHAN6G(65, 6275),
+ CHAN6G(69, 6295), CHAN6G(73, 6315), CHAN6G(77, 6335),
+ CHAN6G(81, 6355), CHAN6G(85, 6375), CHAN6G(89, 6395),
+ CHAN6G(93, 6415), CHAN6G(97, 6435), CHAN6G(101, 6455),
+ CHAN6G(105, 6475), CHAN6G(109, 6495), CHAN6G(113, 6515),
+ CHAN6G(117, 6535), CHAN6G(121, 6555), CHAN6G(125, 6575),
+ CHAN6G(129, 6595), CHAN6G(133, 6615), CHAN6G(137, 6635),
+ CHAN6G(141, 6655), CHAN6G(145, 6675), CHAN6G(149, 6695),
+ CHAN6G(153, 6715), CHAN6G(157, 6735), CHAN6G(161, 6755),
+ CHAN6G(165, 6775), CHAN6G(169, 6795), CHAN6G(173, 6815),
+ CHAN6G(177, 6835), CHAN6G(181, 6855), CHAN6G(185, 6875),
+ CHAN6G(189, 6895), CHAN6G(193, 6915), CHAN6G(197, 6935),
+ CHAN6G(201, 6955), CHAN6G(205, 6975), CHAN6G(209, 6995),
+ CHAN6G(213, 7015), CHAN6G(217, 7035), CHAN6G(221, 7055),
+ CHAN6G(225, 7075), CHAN6G(229, 7095), CHAN6G(233, 7115)
+};
+
/* Band templates duplicated per wiphy. The channel info
* above is added to the band during setup.
*/
@@ -212,6 +244,12 @@ static const struct ieee80211_supported_band __wl_band_5ghz = {
.n_bitrates = wl_a_rates_size,
};
+static const struct ieee80211_supported_band __wl_band_6ghz = {
+ .band = NL80211_BAND_6GHZ,
+ .bitrates = wl_a_rates,
+ .n_bitrates = wl_a_rates_size,
+};
+
/* This is to override regulatory domains defined in cfg80211 module (reg.c)
* By default world regulatory domain defined in reg.c puts the flags
* NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
@@ -220,7 +258,7 @@ static const struct ieee80211_supported_band __wl_band_5ghz = {
* domain are to be done here.
*/
static const struct ieee80211_regdomain brcmf_regdom = {
- .n_reg_rules = 4,
+ .n_reg_rules = 5,
.alpha2 = "99",
.reg_rules = {
/* IEEE 802.11b/g, channels 1..11 */
@@ -233,7 +271,9 @@ static const struct ieee80211_regdomain brcmf_regdom = {
/* IEEE 802.11a, channel 36..64 */
REG_RULE(5150-10, 5350+10, 160, 6, 20, 0),
/* IEEE 802.11a, channel 100..165 */
- REG_RULE(5470-10, 5850+10, 160, 6, 20, 0), }
+ REG_RULE(5470 - 10, 5850 + 10, 160, 6, 20, 0),
+ /* IEEE 802.11ax, 6 GHz */
+ REG_RULE(5935 - 10, 7115 + 10, 160, 6, 20, 0), }
};
/* Note: brcmf_cipher_suites is an array of int defining which cipher suites
@@ -319,6 +359,8 @@ static u8 nl80211_band_to_fwil(enum nl80211_band band)
return WLC_BAND_2G;
case NL80211_BAND_5GHZ:
return WLC_BAND_5G;
+ case NL80211_BAND_6GHZ:
+ return WLC_BAND_6G;
default:
WARN_ON(1);
break;
@@ -341,6 +383,18 @@ static u8 nl80211_band_to_chanspec_band(enum nl80211_band band)
}
}
+static enum nl80211_band chanspec_band_to_nl80211(u8 band)
+{
+ switch (band) {
+ case BRCMU_CHAN_BAND_6G:
+ return NL80211_BAND_6GHZ;
+ case BRCMU_CHAN_BAND_5G:
+ return NL80211_BAND_5GHZ;
+ default:
+ return NL80211_BAND_2GHZ;
+ }
+}
+
static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
struct cfg80211_chan_def *ch)
{
@@ -3347,7 +3401,9 @@ static bool brcmf_chanspec_supported(struct brcmf_cfg80211_info *cfg, u16 chansp
return true;
return band == BRCMU_CHSPEC_D11AC_BND_2G ||
- band == BRCMU_CHSPEC_D11AC_BND_5G;
+ band == BRCMU_CHSPEC_D11AC_BND_5G ||
+ (band == BRCMU_CHSPEC_D11AC_BND_6G &&
+ cfg_to_wiphy(cfg)->bands[NL80211_BAND_6GHZ]);
}
static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
@@ -3374,20 +3430,17 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
if (!brcmf_chanspec_supported(cfg, le16_to_cpu(bi->chanspec)))
return 0;
- if (!bi->ctl_ch) {
- ch.chspec = le16_to_cpu(bi->chanspec);
- cfg->d11inf.decchspec(&ch);
+ ch.chspec = le16_to_cpu(bi->chanspec);
+ cfg->d11inf.decchspec(&ch);
+ if (!bi->ctl_ch)
bi->ctl_ch = ch.control_ch_num;
- }
channel = bi->ctl_ch;
-
- if (channel <= CH_MAX_2G_CHANNEL)
- band = NL80211_BAND_2GHZ;
- else
- band = NL80211_BAND_5GHZ;
+ band = chanspec_band_to_nl80211(ch.band);
freq = ieee80211_channel_to_frequency(channel, band);
bss_data.chan = ieee80211_get_channel(wiphy, freq);
+ if (!bss_data.chan)
+ return 0;
bss_data.boottime_ns = ktime_to_ns(ktime_get_boottime());
notify_capability = le16_to_cpu(bi->capability);
@@ -3459,7 +3512,7 @@ static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
struct brcmf_pub *drvr = cfg->pub;
struct ieee80211_channel *notify_channel;
struct brcmf_bss_info_le *bi = NULL;
- struct ieee80211_supported_band *band;
+ enum nl80211_band band;
struct cfg80211_bss *bss;
struct brcmu_chan ch;
u8 *buf = NULL;
@@ -3493,12 +3546,9 @@ static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
ch.chspec = le16_to_cpu(bi->chanspec);
cfg->d11inf.decchspec(&ch);
- if (ch.band == BRCMU_CHAN_BAND_2G)
- band = wiphy->bands[NL80211_BAND_2GHZ];
- else
- band = wiphy->bands[NL80211_BAND_5GHZ];
+ band = chanspec_band_to_nl80211(ch.band);
- freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
+ freq = ieee80211_channel_to_frequency(ch.control_ch_num, band);
cfg->channel = freq;
notify_channel = ieee80211_get_channel(wiphy, freq);
@@ -5843,6 +5893,9 @@ static int brcmf_cfg80211_get_channel(struct wiphy *wiphy,
case BRCMU_CHAN_BAND_5G:
band = NL80211_BAND_5GHZ;
break;
+ case BRCMU_CHAN_BAND_6G:
+ band = NL80211_BAND_6GHZ;
+ break;
}
switch (ch.bw) {
@@ -6563,7 +6616,7 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
struct wiphy *wiphy = cfg_to_wiphy(cfg);
struct ieee80211_channel *notify_channel = NULL;
- struct ieee80211_supported_band *band;
+ enum nl80211_band band;
struct brcmf_bss_info_le *bi;
struct brcmu_chan ch;
struct cfg80211_roam_info roam_info = {};
@@ -6596,12 +6649,9 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
ch.chspec = le16_to_cpu(bi->chanspec);
cfg->d11inf.decchspec(&ch);
- if (ch.band == BRCMU_CHAN_BAND_2G)
- band = wiphy->bands[NL80211_BAND_2GHZ];
- else
- band = wiphy->bands[NL80211_BAND_5GHZ];
+ band = chanspec_band_to_nl80211(ch.band);
- freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
+ freq = ieee80211_channel_to_frequency(ch.control_ch_num, band);
notify_channel = ieee80211_get_channel(wiphy, freq);
done:
@@ -7207,6 +7257,10 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
for (i = 0; i < band->n_channels; i++)
band->channels[i].flags = IEEE80211_CHAN_DISABLED;
band = wiphy->bands[NL80211_BAND_5GHZ];
+ if (band)
+ for (i = 0; i < band->n_channels; i++)
+ band->channels[i].flags = IEEE80211_CHAN_DISABLED;
+ band = wiphy->bands[NL80211_BAND_6GHZ];
if (band)
for (i = 0; i < band->n_channels; i++)
band->channels[i].flags = IEEE80211_CHAN_DISABLED;
@@ -7229,6 +7283,8 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
band = wiphy->bands[NL80211_BAND_2GHZ];
} else if (ch.band == BRCMU_CHAN_BAND_5G) {
band = wiphy->bands[NL80211_BAND_5GHZ];
+ } else if (ch.band == BRCMU_CHAN_BAND_6G) {
+ band = wiphy->bands[NL80211_BAND_6GHZ];
} else {
bphy_err(drvr, "Invalid channel Spec. 0x%x.\n",
ch.chspec);
@@ -7406,11 +7462,17 @@ static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
bw_cap[NL80211_BAND_2GHZ] = band;
band = WLC_BAND_5G;
err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &band);
- if (!err) {
- bw_cap[NL80211_BAND_5GHZ] = band;
+ if (err) {
+ WARN_ON(1);
return;
}
- WARN_ON(1);
+ bw_cap[NL80211_BAND_5GHZ] = band;
+ if (!ifp->drvr->wiphy->bands[NL80211_BAND_6GHZ])
+ return;
+ band = WLC_BAND_6G;
+ err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &band);
+ if (!err)
+ bw_cap[NL80211_BAND_6GHZ] = band;
return;
}
brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
@@ -7436,7 +7498,7 @@ static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
}
static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
- u32 bw_cap[2], u32 nchain)
+ u32 bw_cap[], u32 nchain)
{
band->ht_cap.ht_supported = true;
if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
@@ -7463,7 +7525,7 @@ static __le16 brcmf_get_mcs_map(u32 nchain, int supp)
}
static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
- u32 bw_cap[2], u32 nchain, u32 txstreams,
+ u32 bw_cap[], u32 nchain, u32 txstreams,
u32 txbf_bfe_cap, u32 txbf_bfr_cap)
{
__le16 mcs_map;
@@ -7595,6 +7657,18 @@ static void brcmf_update_he_cap(struct ieee80211_supported_band *band,
mcs->tx_mcs_160 = cpu_to_le16(0xffff);
}
+ if (band->band == NL80211_BAND_6GHZ) {
+ u16 capa;
+
+ capa = FIELD_PREP(IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START,
+ IEEE80211_HT_MPDU_DENSITY_8) |
+ FIELD_PREP(IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP,
+ IEEE80211_VHT_MAX_AMPDU_1024K) |
+ FIELD_PREP(IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN,
+ IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454);
+ data->he_6ghz_capa.capa = cpu_to_le16(capa);
+ }
+
band->n_iftype_data = 1;
band->iftype_data = data;
}
@@ -7617,7 +7691,11 @@ static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg)
struct wiphy *wiphy = cfg_to_wiphy(cfg);
u32 nmode;
u32 vhtmode = 0;
- u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
+ u32 bw_cap[NUM_NL80211_BANDS] = {
+ [NL80211_BAND_2GHZ] = WLC_BW_20MHZ_BIT,
+ [NL80211_BAND_5GHZ] = WLC_BW_20MHZ_BIT,
+ [NL80211_BAND_6GHZ] = WLC_BW_20MHZ_BIT,
+ };
u32 rxchain;
u32 nchain;
int err;
@@ -7636,9 +7714,9 @@ static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg)
} else {
brcmf_get_bwcap(ifp, bw_cap);
}
- brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
+ brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d, %d)\n",
nmode, vhtmode, bw_cap[NL80211_BAND_2GHZ],
- bw_cap[NL80211_BAND_5GHZ]);
+ bw_cap[NL80211_BAND_5GHZ], bw_cap[NL80211_BAND_6GHZ]);
err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
if (err) {
@@ -7674,11 +7752,14 @@ static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg)
if (band == NULL)
continue;
- if (nmode)
- brcmf_update_ht_cap(band, bw_cap, nchain);
- if (vhtmode)
- brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
- txbf_bfe_cap, txbf_bfr_cap);
+ if (band->band != NL80211_BAND_6GHZ) {
+ if (nmode)
+ brcmf_update_ht_cap(band, bw_cap, nchain);
+ if (vhtmode)
+ brcmf_update_vht_cap(band, bw_cap, nchain,
+ txstreams, txbf_bfe_cap,
+ txbf_bfr_cap);
+ }
if (he_enable)
brcmf_update_he_cap(band, &cfg->he_iftype_data[i],
bw_cap, nchain);
@@ -7928,6 +8009,27 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
#endif
}
+static struct ieee80211_supported_band *
+brcmf_alloc_band(const struct ieee80211_supported_band *tmpl,
+ const struct ieee80211_channel *channels, size_t n_channels)
+{
+ struct ieee80211_supported_band *band;
+
+ band = kmemdup(tmpl, sizeof(*tmpl), GFP_KERNEL);
+ if (!band)
+ return NULL;
+
+ band->channels = kmemdup(channels, n_channels * sizeof(*channels),
+ GFP_KERNEL);
+ if (!band->channels) {
+ kfree(band);
+ return NULL;
+ }
+ band->n_channels = n_channels;
+
+ return band;
+}
+
static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
{
struct brcmf_pub *drvr = ifp->drvr;
@@ -8041,41 +8143,36 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
if (wiphy->bands[NL80211_BAND_2GHZ])
break;
- band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
- GFP_KERNEL);
+ band = brcmf_alloc_band(&__wl_band_2ghz,
+ __wl_2ghz_channels,
+ ARRAY_SIZE(__wl_2ghz_channels));
if (!band)
return -ENOMEM;
-
- band->channels = kmemdup(&__wl_2ghz_channels,
- sizeof(__wl_2ghz_channels),
- GFP_KERNEL);
- if (!band->channels) {
- kfree(band);
- return -ENOMEM;
- }
-
- band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
wiphy->bands[NL80211_BAND_2GHZ] = band;
break;
case WLC_BAND_5G:
if (wiphy->bands[NL80211_BAND_5GHZ])
break;
- band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
- GFP_KERNEL);
+ band = brcmf_alloc_band(&__wl_band_5ghz,
+ __wl_5ghz_channels,
+ ARRAY_SIZE(__wl_5ghz_channels));
if (!band)
return -ENOMEM;
+ wiphy->bands[NL80211_BAND_5GHZ] = band;
+ break;
+ case WLC_BAND_6G:
+ /* cfg80211 requires HE on the 6 GHz band */
+ if (wiphy->bands[NL80211_BAND_6GHZ] ||
+ !brcmf_he_enabled(ifp))
+ break;
- band->channels = kmemdup(&__wl_5ghz_channels,
- sizeof(__wl_5ghz_channels),
- GFP_KERNEL);
- if (!band->channels) {
- kfree(band);
+ band = brcmf_alloc_band(&__wl_band_6ghz,
+ __wl_6ghz_channels,
+ ARRAY_SIZE(__wl_6ghz_channels));
+ if (!band)
return -ENOMEM;
- }
-
- band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
- wiphy->bands[NL80211_BAND_5GHZ] = band;
+ wiphy->bands[NL80211_BAND_6GHZ] = band;
break;
default:
brcmf_dbg(INFO, "ignoring unsupported band %u\n",
@@ -8610,13 +8707,11 @@ static void brcmf_free_wiphy(struct wiphy *wiphy)
kfree(wiphy->iface_combinations[i].limits);
}
kfree(wiphy->iface_combinations);
- if (wiphy->bands[NL80211_BAND_2GHZ]) {
- kfree(wiphy->bands[NL80211_BAND_2GHZ]->channels);
- kfree(wiphy->bands[NL80211_BAND_2GHZ]);
- }
- if (wiphy->bands[NL80211_BAND_5GHZ]) {
- kfree(wiphy->bands[NL80211_BAND_5GHZ]->channels);
- kfree(wiphy->bands[NL80211_BAND_5GHZ]);
+ for (i = 0; i < NUM_NL80211_BANDS; i++) {
+ if (!wiphy->bands[i])
+ continue;
+ kfree(wiphy->bands[i]->channels);
+ kfree(wiphy->bands[i]);
}
#if IS_ENABLED(CONFIG_PM)
if (wiphy->wowlan != &brcmf_wowlan_support)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
index d9fc94076791..ba62ab41bae1 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
@@ -278,6 +278,9 @@ static int brcmf_pno_get_bucket_channels(struct cfg80211_sched_scan_request *r,
int i, err = 0;
for (i = 0; i < r->n_channels; i++) {
+ /* bare channel numbers cannot identify 6 GHz channels */
+ if (r->channels[i]->band == NL80211_BAND_6GHZ)
+ continue;
if (n_chan >= BRCMF_NUMCHANNELS) {
err = -ENOSPC;
goto done;
diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
index c465208c4331..aa692dcc00d6 100644
--- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
+++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h
@@ -93,6 +93,7 @@
#define WLC_BAND_5G 1 /* 5 Ghz */
#define WLC_BAND_2G 2 /* 2.4 Ghz */
#define WLC_BAND_ALL 3 /* all bands */
+#define WLC_BAND_6G 4 /* 6 Ghz */
#define CHSPEC_CHANNEL(chspec) ((u8)((chspec) & WL_CHANSPEC_CHAN_MASK))
#define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK)
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH wireless-next 4/4] wifi: brcmfmac: Add support for PNO version 3
2026-11-13 6:31 [PATCH wireless-next 0/4] wifi: brcmfmac: Add support for 6 GHz operation Michael Reeves via B4 Relay
` (2 preceding siblings ...)
2026-11-13 6:31 ` [PATCH wireless-next 3/4] wifi: brcmfmac: Add support for the 6 GHz band Michael Reeves via B4 Relay
@ 2026-11-13 6:31 ` Michael Reeves via B4 Relay
3 siblings, 0 replies; 5+ messages in thread
From: Michael Reeves via B4 Relay @ 2026-11-13 6:31 UTC (permalink / raw)
To: Arend van Spriel
Cc: asahi, linux-wireless, brcm80211, brcm80211-dev-list.pdl,
linux-kernel, Michael Reeves, Yureka Lilian, Daniel Berlin
From: Michael Reeves <michael.reeves077@gmail.com>
Recent firmware versions introduce version 3 of PNO settings and result
events. Unlike prior versions that only provide raw channel numbers,
version 3 provides complete chanspecs, resolving band ambiguity for
overlapping channel numbers (e.g. 6 GHz).
Query the firmware's PNO version during driver attach. Use version 3
configuration structures if supported by the firmware, and parse
incoming PNO scan results based on the event version.
Tested-by: Yureka Lilian <yureka@cyberchaos.dev>
Co-developed-by: Daniel Berlin <dberlin@dberlin.org>
Signed-off-by: Daniel Berlin <dberlin@dberlin.org>
Signed-off-by: Michael Reeves <michael.reeves077@gmail.com>
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 94 ++++++--------
.../broadcom/brcm80211/brcmfmac/fwil_types.h | 65 ++++++++++
.../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 144 ++++++++++++++++++---
.../net/wireless/broadcom/brcm80211/brcmfmac/pno.h | 41 +++++-
4 files changed, 267 insertions(+), 77 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 90db55c0ab7a..2450dd0c1e99 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -3828,17 +3828,12 @@ brcmf_alloc_internal_escan_request(struct wiphy *wiphy, u32 n_netinfo) {
}
static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req,
- u8 *ssid, u8 ssid_len, u8 channel)
+ u8 *ssid, u8 ssid_len, u8 channel,
+ enum nl80211_band band)
{
struct ieee80211_channel *chan;
- enum nl80211_band band;
int freq, i;
- if (channel <= CH_MAX_2G_CHANNEL)
- band = NL80211_BAND_2GHZ;
- else
- band = NL80211_BAND_5GHZ;
-
freq = ieee80211_channel_to_frequency(channel, band);
if (!freq)
return -EINVAL;
@@ -3894,28 +3889,6 @@ static int brcmf_start_internal_escan(struct brcmf_if *ifp, u32 fwmap,
return 0;
}
-static struct brcmf_pno_net_info_le *
-brcmf_get_netinfo_array(struct brcmf_pno_scanresults_le *pfn_v1)
-{
- struct brcmf_pno_scanresults_v2_le *pfn_v2;
- struct brcmf_pno_net_info_le *netinfo;
-
- switch (pfn_v1->version) {
- default:
- WARN_ON(1);
- fallthrough;
- case cpu_to_le32(1):
- netinfo = (struct brcmf_pno_net_info_le *)(pfn_v1 + 1);
- break;
- case cpu_to_le32(2):
- pfn_v2 = (struct brcmf_pno_scanresults_v2_le *)pfn_v1;
- netinfo = (struct brcmf_pno_net_info_le *)(pfn_v2 + 1);
- break;
- }
-
- return netinfo;
-}
-
/* PFN result doesn't have all the info which are required by the supplicant
* (For e.g IEs) Do a target Escan so that sched scan results are reported
* via wl_inform_single_bss in the required format. Escan does require the
@@ -3928,19 +3901,19 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
{
struct brcmf_pub *drvr = ifp->drvr;
struct brcmf_cfg80211_info *cfg = drvr->config;
- struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
struct cfg80211_scan_request *request = NULL;
struct wiphy *wiphy = cfg_to_wiphy(cfg);
- int i, err = 0;
struct brcmf_pno_scanresults_le *pfn_result;
+ struct brcmf_pno_netinfo netinfo;
+ int i, err = 0;
u32 bucket_map;
u32 result_count;
u32 status;
- u32 datalen;
brcmf_dbg(SCAN, "Enter\n");
- if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
+ if (e->datalen < (sizeof(*pfn_result) +
+ sizeof(struct brcmf_pno_net_info_le))) {
brcmf_dbg(SCAN, "Event data too small. Ignore\n");
return 0;
}
@@ -3964,9 +3937,8 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
goto out_err;
}
- netinfo_start = brcmf_get_netinfo_array(pfn_result);
- datalen = e->datalen - ((void *)netinfo_start - (void *)pfn_result);
- if (datalen < result_count * sizeof(*netinfo)) {
+ err = brcmf_pno_check_results(data, e->datalen, result_count);
+ if (err) {
bphy_err(drvr, "insufficient event data\n");
goto out_err;
}
@@ -3980,17 +3952,20 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
bucket_map = 0;
for (i = 0; i < result_count; i++) {
- netinfo = &netinfo_start[i];
-
- if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
- netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
- brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n",
- netinfo->SSID, netinfo->channel);
- bucket_map |= brcmf_pno_get_bucket_map(cfg->pno, netinfo);
- err = brcmf_internal_escan_add_info(request,
- netinfo->SSID,
- netinfo->SSID_len,
- netinfo->channel);
+ err = brcmf_pno_get_netinfo(&cfg->d11inf, data, e->datalen, i,
+ &netinfo);
+ if (err) {
+ bphy_err(drvr, "insufficient event data\n");
+ goto out_err;
+ }
+
+ brcmf_dbg(SCAN, "SSID:%.*s Channel:%d\n",
+ netinfo.ssid_len, netinfo.ssid, netinfo.channel);
+ bucket_map |= brcmf_pno_get_bucket_map(cfg->pno, &netinfo);
+ err = brcmf_internal_escan_add_info(request, netinfo.ssid,
+ netinfo.ssid_len,
+ netinfo.channel,
+ chanspec_band_to_nl80211(netinfo.band));
if (err)
goto out_err;
}
@@ -4104,11 +4079,13 @@ brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
struct brcmf_pub *drvr = ifp->drvr;
struct brcmf_cfg80211_info *cfg = drvr->config;
struct brcmf_pno_scanresults_le *pfn_result;
- struct brcmf_pno_net_info_le *netinfo;
+ struct brcmf_pno_netinfo netinfo;
+ int err;
brcmf_dbg(SCAN, "Enter\n");
- if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
+ if (e->datalen < (sizeof(*pfn_result) +
+ sizeof(struct brcmf_pno_net_info_le))) {
brcmf_dbg(SCAN, "Event data too small. Ignore\n");
return 0;
}
@@ -4126,16 +4103,19 @@ brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
return -EINVAL;
}
- netinfo = brcmf_get_netinfo_array(pfn_result);
- if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
- netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
- memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len);
- cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len;
+ err = brcmf_pno_get_netinfo(&cfg->d11inf, data, e->datalen, 0,
+ &netinfo);
+ if (err) {
+ bphy_err(drvr, "insufficient event data\n");
+ return err;
+ }
+
+ memcpy(cfg->wowl.nd->ssid.ssid, netinfo.ssid, netinfo.ssid_len);
+ cfg->wowl.nd->ssid.ssid_len = netinfo.ssid_len;
cfg->wowl.nd->n_channels = 1;
cfg->wowl.nd->channels[0] =
- ieee80211_channel_to_frequency(netinfo->channel,
- netinfo->channel <= CH_MAX_2G_CHANNEL ?
- NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);
+ ieee80211_channel_to_frequency(netinfo.channel,
+ chanspec_band_to_nl80211(netinfo.band));
cfg->wowl.nd_info->n_matches = 1;
cfg->wowl.nd_info->matches[0] = cfg->wowl.nd;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index f4e8eeeffa08..9dd2de048275 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -1107,6 +1107,46 @@ struct brcmf_pno_param_le {
__le32 slow_freq;
};
+/**
+ * struct brcmf_pno_param_v3_le - PNO scan configuration parameters
+ *
+ * @version: PNO parameters version.
+ * @length: size of this structure.
+ * @scan_freq: scan frequency.
+ * @lost_network_timeout: #sec. to declare discovered network as lost.
+ * @flags: Bit field to control features of PFN such as sort criteria auto
+ * enable switch and background scan.
+ * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort
+ * criteria.
+ * @bestn: number of best networks in each bucket.
+ * @mscan: number of scans recorded which is equal to or less than bestn.
+ * @repeat: minimum number of scan intervals before scan frequency changes
+ * in adaptive scan.
+ * @exp: exponent of 2 for maximum scan interval.
+ * @slow_freq: slow scan period.
+ * @min_bound: minimum bound for PFN backoff.
+ * @max_bound: maximum bound for PFN backoff.
+ * @pfn_lp_scan_disable: disable low power scan interleaving.
+ * @pfn_lp_scan_cnt: number of low power scans before a high power scan.
+ */
+struct brcmf_pno_param_v3_le {
+ __le16 version;
+ __le16 length;
+ __le32 scan_freq;
+ __le32 lost_network_timeout;
+ __le16 flags;
+ __le16 rssi_margin;
+ u8 bestn;
+ u8 mscan;
+ u8 repeat;
+ u8 exp;
+ __le32 slow_freq;
+ u8 min_bound;
+ u8 max_bound;
+ u8 pfn_lp_scan_disable;
+ u8 pfn_lp_scan_cnt;
+};
+
/**
* struct brcmf_pno_config_le - PNO channel configuration.
*
@@ -1160,6 +1200,31 @@ struct brcmf_pno_net_info_le {
__le16 timestamp;
};
+/**
+ * struct brcmf_pno_net_info_v3_le - information per found network.
+ *
+ * @bssid: BSS network identifier.
+ * @chanspec: channel spec.
+ * @SSID_len: length of ssid.
+ * @pad: padding.
+ * @flags: flags.
+ * @SSID: ssid characters.
+ * @RSSI: receive signal strength (in dBm).
+ * @timestamp: age in seconds.
+ *
+ * Version 3 results use the header of &struct brcmf_pno_scanresults_v2_le.
+ */
+struct brcmf_pno_net_info_v3_le {
+ u8 bssid[ETH_ALEN];
+ __le16 chanspec;
+ u8 SSID_len;
+ u8 pad;
+ __le16 flags;
+ u8 SSID[32];
+ __le16 RSSI;
+ __le16 timestamp;
+};
+
/**
* struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event.
*
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
index ba62ab41bae1..ec8cd81de9cf 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c
@@ -5,6 +5,7 @@
#include <linux/netdevice.h>
#include <linux/gcd.h>
#include <net/cfg80211.h>
+#include <brcmu_wifi.h>
#include "core.h"
#include "debug.h"
@@ -13,7 +14,8 @@
#include "cfg80211.h"
#include "pno.h"
-#define BRCMF_PNO_VERSION 2
+#define BRCMF_PNO_VERSION_2 2
+#define BRCMF_PNO_VERSION_3 3
#define BRCMF_PNO_REPEAT 4
#define BRCMF_PNO_FREQ_EXPO_MAX 3
#define BRCMF_PNO_IMMEDIATE_SCAN_BIT 3
@@ -30,6 +32,7 @@
#define GSCAN_RETRY_THRESHOLD 3
struct brcmf_pno_info {
+ u8 version;
int n_reqs;
struct cfg80211_sched_scan_request *reqs[BRCMF_PNO_MAX_BUCKETS];
struct mutex req_lock;
@@ -102,23 +105,16 @@ static int brcmf_pno_channel_config(struct brcmf_if *ifp,
static int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq,
u32 mscan, u32 bestn)
{
+ struct brcmf_pno_info *pi = ifp_to_pno(ifp);
struct brcmf_pub *drvr = ifp->drvr;
- struct brcmf_pno_param_le pfn_param;
+ u8 pfn_mscan = 0;
+ u8 pfn_bestn = 0;
u16 flags;
u32 pfnmem;
s32 err;
- memset(&pfn_param, 0, sizeof(pfn_param));
- pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
-
- /* set extra pno params */
flags = BIT(BRCMF_PNO_IMMEDIATE_SCAN_BIT) |
BIT(BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
- pfn_param.repeat = BRCMF_PNO_REPEAT;
- pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
-
- /* set up pno scan fr */
- pfn_param.scan_freq = cpu_to_le32(scan_freq);
if (mscan) {
pfnmem = bestn;
@@ -136,15 +132,40 @@ static int brcmf_pno_config(struct brcmf_if *ifp, u32 scan_freq,
goto exit;
}
mscan = min_t(u32, mscan, pfnmem);
- pfn_param.mscan = mscan;
- pfn_param.bestn = bestn;
+ pfn_mscan = mscan;
+ pfn_bestn = bestn;
flags |= BIT(BRCMF_PNO_ENABLE_BD_SCAN_BIT);
brcmf_dbg(INFO, "mscan=%d, bestn=%d\n", mscan, bestn);
}
- pfn_param.flags = cpu_to_le16(flags);
- err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
- sizeof(pfn_param));
+ if (pi->version == BRCMF_PNO_VERSION_3) {
+ struct brcmf_pno_param_v3_le pfn_param = {
+ .version = cpu_to_le16(BRCMF_PNO_VERSION_3),
+ .length = cpu_to_le16(sizeof(pfn_param)),
+ .scan_freq = cpu_to_le32(scan_freq),
+ .flags = cpu_to_le16(flags),
+ .bestn = pfn_bestn,
+ .mscan = pfn_mscan,
+ .repeat = BRCMF_PNO_REPEAT,
+ .exp = BRCMF_PNO_FREQ_EXPO_MAX,
+ };
+
+ err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
+ sizeof(pfn_param));
+ } else {
+ struct brcmf_pno_param_le pfn_param = {
+ .version = cpu_to_le32(BRCMF_PNO_VERSION_2),
+ .scan_freq = cpu_to_le32(scan_freq),
+ .flags = cpu_to_le16(flags),
+ .bestn = pfn_bestn,
+ .mscan = pfn_mscan,
+ .repeat = BRCMF_PNO_REPEAT,
+ .exp = BRCMF_PNO_FREQ_EXPO_MAX,
+ };
+
+ err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
+ sizeof(pfn_param));
+ }
if (err)
bphy_err(drvr, "pfn_set failed, err=%d\n", err);
@@ -517,6 +538,8 @@ int brcmf_pno_stop_sched_scan(struct brcmf_if *ifp, u64 reqid)
int brcmf_pno_attach(struct brcmf_cfg80211_info *cfg)
{
+ struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
+ struct brcmf_pno_param_v3_le param = {};
struct brcmf_pno_info *pi;
brcmf_dbg(TRACE, "enter\n");
@@ -524,6 +547,11 @@ int brcmf_pno_attach(struct brcmf_cfg80211_info *cfg)
if (!pi)
return -ENOMEM;
+ pi->version = BRCMF_PNO_VERSION_2;
+ if (!brcmf_fil_iovar_data_get(ifp, "pfn_set", ¶m, sizeof(param)) &&
+ le16_to_cpu(param.version) == BRCMF_PNO_VERSION_3)
+ pi->version = BRCMF_PNO_VERSION_3;
+
cfg->pno = pi;
mutex_init(&pi->req_lock);
return 0;
@@ -565,8 +593,86 @@ u64 brcmf_pno_find_reqid_by_bucket(struct brcmf_pno_info *pi, u32 bucket)
return reqid;
}
+static bool brcmf_pno_get_layout(const struct brcmf_pno_scanresults_le *res,
+ size_t *hdr_len, size_t *size)
+{
+ switch (le32_to_cpu(res->version)) {
+ case 3:
+ *hdr_len = sizeof(struct brcmf_pno_scanresults_v2_le);
+ *size = sizeof(struct brcmf_pno_net_info_v3_le);
+ return true;
+ case 2:
+ *hdr_len = sizeof(struct brcmf_pno_scanresults_v2_le);
+ *size = sizeof(struct brcmf_pno_net_info_le);
+ return false;
+ default:
+ WARN_ON_ONCE(1);
+ fallthrough;
+ case 1:
+ *hdr_len = sizeof(*res);
+ *size = sizeof(struct brcmf_pno_net_info_le);
+ return false;
+ }
+}
+
+int brcmf_pno_check_results(const void *data, u32 datalen, u32 count)
+{
+ const struct brcmf_pno_scanresults_le *res = data;
+ size_t hdr_len, size;
+
+ if (datalen < sizeof(*res))
+ return -EINVAL;
+
+ brcmf_pno_get_layout(res, &hdr_len, &size);
+ if (datalen < hdr_len || (datalen - hdr_len) / size < count)
+ return -EINVAL;
+
+ return 0;
+}
+
+int brcmf_pno_get_netinfo(struct brcmu_d11inf *d11inf, const void *data,
+ u32 datalen, u32 idx, struct brcmf_pno_netinfo *ni)
+{
+ const struct brcmf_pno_scanresults_le *res = data;
+ const struct brcmf_pno_net_info_v3_le *v3;
+ const struct brcmf_pno_net_info_le *v1;
+ struct brcmu_chan ch = {};
+ size_t hdr_len, size;
+ bool is_v3;
+
+ memset(ni, 0, sizeof(*ni));
+
+ if (datalen < sizeof(*res))
+ return -EINVAL;
+
+ is_v3 = brcmf_pno_get_layout(res, &hdr_len, &size);
+ if (datalen < hdr_len || (datalen - hdr_len) / size <= idx)
+ return -EINVAL;
+
+ if (is_v3) {
+ v3 = data + hdr_len + idx * size;
+ ni->ssid_len = min_t(u8, v3->SSID_len, IEEE80211_MAX_SSID_LEN);
+ memcpy(ni->ssid, v3->SSID, ni->ssid_len);
+ memcpy(ni->bssid, v3->bssid, ETH_ALEN);
+ ch.chspec = le16_to_cpu(v3->chanspec);
+ d11inf->decchspec(&ch);
+ ni->channel = ch.control_ch_num;
+ ni->band = ch.band;
+ } else {
+ v1 = data + hdr_len + idx * size;
+ ni->ssid_len = min_t(u8, v1->SSID_len, IEEE80211_MAX_SSID_LEN);
+ memcpy(ni->ssid, v1->SSID, ni->ssid_len);
+ memcpy(ni->bssid, v1->bssid, ETH_ALEN);
+ ni->channel = v1->channel;
+ ni->band = v1->channel <= CH_MAX_2G_CHANNEL ?
+ BRCMU_CHAN_BAND_2G : BRCMU_CHAN_BAND_5G;
+ }
+
+ return 0;
+}
+
u32 brcmf_pno_get_bucket_map(struct brcmf_pno_info *pi,
- struct brcmf_pno_net_info_le *ni)
+ const struct brcmf_pno_netinfo *ni)
{
struct cfg80211_sched_scan_request *req;
struct cfg80211_match_set *ms;
@@ -581,8 +687,8 @@ u32 brcmf_pno_get_bucket_map(struct brcmf_pno_info *pi,
continue;
for (j = 0; j < req->n_match_sets; j++) {
ms = &req->match_sets[j];
- if (ms->ssid.ssid_len == ni->SSID_len &&
- !memcmp(ms->ssid.ssid, ni->SSID, ni->SSID_len)) {
+ if (ms->ssid.ssid_len == ni->ssid_len &&
+ !memcmp(ms->ssid.ssid, ni->ssid, ni->ssid_len)) {
bucket_map |= BIT(i);
break;
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h
index 25d406019ac3..a47918244e1a 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.h
@@ -12,6 +12,24 @@
/* forward declaration */
struct brcmf_pno_info;
+struct brcmu_d11inf;
+
+/**
+ * struct brcmf_pno_netinfo - network found by a PNO scan.
+ *
+ * @bssid: BSS network identifier.
+ * @ssid: ssid characters.
+ * @ssid_len: length of ssid.
+ * @channel: control channel number.
+ * @band: band of the channel, one of the BRCMU_CHAN_BAND_* values.
+ */
+struct brcmf_pno_netinfo {
+ u8 bssid[ETH_ALEN];
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 ssid_len;
+ u8 channel;
+ u8 band;
+};
/**
* brcmf_pno_start_sched_scan - initiate scheduled scan on device.
@@ -60,6 +78,27 @@ void brcmf_pno_detach(struct brcmf_cfg80211_info *cfg);
*/
u64 brcmf_pno_find_reqid_by_bucket(struct brcmf_pno_info *pi, u32 bucket);
+/**
+ * brcmf_pno_check_results - validate PNO scan results buffer length.
+ *
+ * @data: PNO scan results event data.
+ * @datalen: length of @data.
+ * @count: number of network entries reported in the results header.
+ */
+int brcmf_pno_check_results(const void *data, u32 datalen, u32 count);
+
+/**
+ * brcmf_pno_get_netinfo - extract a network from PNO scan results.
+ *
+ * @d11inf: chanspec conversion functions.
+ * @data: PNO scan results event data.
+ * @datalen: length of @data.
+ * @idx: index of the network in the results.
+ * @ni: network information to fill in.
+ */
+int brcmf_pno_get_netinfo(struct brcmu_d11inf *d11inf, const void *data,
+ u32 datalen, u32 idx, struct brcmf_pno_netinfo *ni);
+
/**
* brcmf_pno_get_bucket_map - determine bucket map for given netinfo.
*
@@ -67,6 +106,6 @@ u64 brcmf_pno_find_reqid_by_bucket(struct brcmf_pno_info *pi, u32 bucket);
* @netinfo: netinfo to compare with bucket configuration.
*/
u32 brcmf_pno_get_bucket_map(struct brcmf_pno_info *pi,
- struct brcmf_pno_net_info_le *netinfo);
+ const struct brcmf_pno_netinfo *netinfo);
#endif /* _BRCMF_PNO_H */
--
2.56.0
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-13 6:32 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-13 6:31 [PATCH wireless-next 0/4] wifi: brcmfmac: Add support for 6 GHz operation Michael Reeves via B4 Relay
2026-11-13 6:31 ` [PATCH wireless-next 1/4] wifi: brcmfmac: Add support for 6 GHz chanspecs Michael Reeves via B4 Relay
2026-11-13 6:31 ` [PATCH wireless-next 2/4] wifi: brcmfmac: Advertise HE capabilities Michael Reeves via B4 Relay
2026-11-13 6:31 ` [PATCH wireless-next 3/4] wifi: brcmfmac: Add support for the 6 GHz band Michael Reeves via B4 Relay
2026-11-13 6:31 ` [PATCH wireless-next 4/4] wifi: brcmfmac: Add support for PNO version 3 Michael Reeves via B4 Relay
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®