[PATCH v3 3/5] wifi: brcmfmac: add PMK programming for firmware roaming offload
From: Jason Huang
Date: Mon Aug 10 2026 - 07:43:26 EST
From: Darren Li <hsin-hung.li@xxxxxxxxxxx>
FT and OKC roaming offload need PMK material in firmware even when
the initial 802.1X exchange is handled by userspace. Add a roaming
firmware-supplicant profile state for those connections and allow PMK
configuration while firmware FT or OKC state is active.
Program the OKC PMK through the okc_info_pmk iovar before setting the
regular PMK when firmware reports OKC state. Only select the roaming
firmware-supplicant state when firmware advertises FBT or OKC support;
devices without those capabilities keep the profile in the userspace
supplicant state and do not issue roaming-offload specific iovars on
every connect.
For FT-PSK with firmware supplicant support, keep the profile in the
PSK firmware-supplicant state.
Assisted-by: GitHub Copilot CLI:gpt-5.5
Signed-off-by: Darren Li <hsin-hung.li@xxxxxxxxxxx>
Signed-off-by: Chung-Hsien Hsu <Chung-Hsien.Hsu@xxxxxxxxxxxx>
Signed-off-by: Chi-hsien Lin <chi-hsien.lin@xxxxxxxxxxx>
Co-developed-by: Carella Chen <carella.chen@xxxxxxxxxxxx>
Signed-off-by: Carella Chen <carella.chen@xxxxxxxxxxxx>
Co-developed-by: Shelley Yang <shelley.yang@xxxxxxxxxxxx>
Signed-off-by: Shelley Yang <shelley.yang@xxxxxxxxxxxx>
Signed-off-by: Jason Huang <jason.huang2@xxxxxxxxxxxx>
---
.../broadcom/brcm80211/brcmfmac/cfg80211.c | 56 +++++++++++++++++++---
.../broadcom/brcm80211/brcmfmac/cfg80211.h | 4 +-
2 files changed, 53 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 872c48806d09..f2916cc9e5fc 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -2112,9 +2112,11 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
struct brcmf_pub *drvr = ifp->drvr;
s32 val;
s32 err;
+ s32 okc_enable;
const struct brcmf_tlv *rsn_ie;
const u8 *ie;
u32 ie_len;
+ bool fwsup_roam;
u32 offset;
u16 rsn_cap;
u32 mfp;
@@ -2122,6 +2124,9 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE;
profile->is_ft = false;
+ profile->is_okc = false;
+ fwsup_roam = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FBT) ||
+ brcmf_feat_is_enabled(ifp, BRCMF_FEAT_OKC);
if (!sme->crypto.n_akm_suites)
return 0;
@@ -2138,6 +2143,8 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
val = WPA_AUTH_UNSPECIFIED;
if (sme->want_1x)
profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
+ else if (fwsup_roam)
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM;
break;
case WLAN_AKM_SUITE_PSK:
val = WPA_AUTH_PSK;
@@ -2153,11 +2160,15 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
val = WPA2_AUTH_UNSPECIFIED;
if (sme->want_1x)
profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
+ else if (fwsup_roam)
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM;
break;
case WLAN_AKM_SUITE_8021X_SHA256:
val = WPA2_AUTH_1X_SHA256;
if (sme->want_1x)
profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
+ else if (fwsup_roam)
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM;
break;
case WLAN_AKM_SUITE_PSK_SHA256:
val = WPA2_AUTH_PSK_SHA256;
@@ -2170,10 +2181,16 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
profile->is_ft = true;
if (sme->want_1x)
profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
+ else if (fwsup_roam)
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM;
break;
case WLAN_AKM_SUITE_FT_PSK:
val = WPA2_AUTH_PSK | WPA2_AUTH_FT;
profile->is_ft = true;
+ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP))
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK;
+ else if (fwsup_roam)
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM;
break;
case WLAN_AKM_SUITE_WFA_DPP:
val = WFA_AUTH_DPP;
@@ -2204,8 +2221,22 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X)
brcmf_dbg(INFO, "using 1X offload\n");
+
+ if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X ||
+ profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM) {
+ err = brcmf_fil_bsscfg_int_get(ifp, "okc_enable",
+ &okc_enable);
+ if (err) {
+ bphy_err(drvr, "get okc_enable failed (%d)\n", err);
+ } else {
+ brcmf_dbg(INFO, "okc_enable (%d)\n", okc_enable);
+ profile->is_okc = okc_enable;
+ }
+ }
if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE)
brcmf_dbg(INFO, "using SAE offload\n");
+ if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM)
+ brcmf_dbg(INFO, "using roaming offload\n");
if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
goto skip_mfp_config;
@@ -2494,13 +2525,14 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
if (sme->crypto.psk && !is_sae_akm &&
profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) {
- if (WARN_ON(profile->use_fwsup !=
- BRCMF_PROFILE_FWSUP_NONE)) {
+ if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_NONE) {
+ brcmf_dbg(INFO, "using PSK offload\n");
+ profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK;
+ } else if (WARN_ON(profile->use_fwsup !=
+ BRCMF_PROFILE_FWSUP_PSK)) {
err = -EINVAL;
goto done;
}
- brcmf_dbg(INFO, "using PSK offload\n");
- profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK;
}
if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {
/* enable firmware supplicant for this interface */
@@ -5953,17 +5985,29 @@ static int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev,
const struct cfg80211_pmk_conf *conf)
{
struct brcmf_if *ifp;
+ struct brcmf_pub *drvr;
+ int ret;
brcmf_dbg(TRACE, "enter\n");
- /* expect using firmware supplicant for 1X */
ifp = netdev_priv(dev);
- if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))
+ drvr = ifp->drvr;
+ if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X &&
+ !ifp->vif->profile.is_ft &&
+ !ifp->vif->profile.is_okc))
return -EINVAL;
if (conf->pmk_len > BRCMF_WSEC_MAX_PSK_LEN)
return -ERANGE;
+ if (ifp->vif->profile.is_okc) {
+ ret = brcmf_fil_iovar_data_set(ifp, "okc_info_pmk",
+ conf->pmk, conf->pmk_len);
+ if (ret < 0)
+ bphy_err(drvr, "okc_info_pmk iovar failed: ret=%d\n",
+ ret);
+ }
+
return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len);
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
index 63e534523f51..58f229489836 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -127,7 +127,8 @@ enum brcmf_profile_fwsup {
BRCMF_PROFILE_FWSUP_NONE,
BRCMF_PROFILE_FWSUP_PSK,
BRCMF_PROFILE_FWSUP_1X,
- BRCMF_PROFILE_FWSUP_SAE
+ BRCMF_PROFILE_FWSUP_SAE,
+ BRCMF_PROFILE_FWSUP_ROAM
};
/**
@@ -172,6 +173,7 @@ struct brcmf_cfg80211_profile {
enum brcmf_profile_fwsup use_fwsup;
u16 use_fwauth;
bool is_ft;
+ bool is_okc;
};
/**
--
2.25.1