wifi: brcmfmac: cyw: support external SAE authentication in station mode
authorArend van Spriel <arend.vanspriel@broadcom.com>
Fri, 25 Apr 2025 08:55:18 +0000 (10:55 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 8 May 2025 22:00:55 +0000 (00:00 +0200)
Firmware has SME functionality but would like the userspace to handle
SAE authentication. This patch adds support for such an external SAE
authentication mechanism in station mode.

Signed-off-by: Chung-Hsien Hsu <chung-hsien.hsu@infineon.com>
Signed-off-by: Chi-hsien Lin <chi-hsien.lin@infineon.com>
[arend: rework patch for per-vendor framework]
Tested-by: James Prestwood <prestwoj@gmail.com>
Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com>
Link: https://patch.msgid.link/20250425085519.492267-4-arend.vanspriel@broadcom.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/fwil_types.h [new file with mode: 0644]
drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h

index b9381154d124f1ef94fc02f4232d4a978a1188d7..dc2383faddd1229bd46b092cc21e074ef47a808c 100644 (file)
@@ -74,7 +74,6 @@
 #define VNDR_IE_HDR_SIZE               12
 #define VNDR_IE_PARSE_LIMIT            5
 
-#define        DOT11_MGMT_HDR_LEN              24      /* d11 management header len */
 #define        DOT11_BCN_PRB_FIXED_LEN         12      /* beacon/probe fixed length */
 
 #define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS   320
@@ -1945,17 +1944,22 @@ static s32 brcmf_set_wpa_version(struct net_device *ndev,
        struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
        struct brcmf_pub *drvr = ifp->drvr;
        struct brcmf_cfg80211_security *sec;
-       s32 val = 0;
-       s32 err = 0;
+       s32 val;
+       s32 err;
 
-       if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+       if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) {
                val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
-       else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
-               val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
-       else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3)
+       } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) {
+               if (drvr->bus_if->fwvid == BRCMF_FWVENDOR_CYW &&
+                   sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_SAE)
+                       val = WPA3_AUTH_SAE_PSK;
+               else
+                       val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
+       } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3) {
                val = WPA3_AUTH_SAE_PSK;
-       else
+       } else {
                val = WPA_AUTH_DISABLED;
+       }
        brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
        err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", val);
        if (err) {
@@ -2163,28 +2167,25 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
                switch (sme->crypto.akm_suites[0]) {
                case WLAN_AKM_SUITE_SAE:
                        val = WPA3_AUTH_SAE_PSK;
-                       if (sme->crypto.sae_pwd) {
-                               brcmf_dbg(INFO, "using SAE offload\n");
-                               profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
-                       }
                        break;
                case WLAN_AKM_SUITE_FT_OVER_SAE:
                        val = WPA3_AUTH_SAE_PSK | WPA2_AUTH_FT;
                        profile->is_ft = true;
-                       if (sme->crypto.sae_pwd) {
-                               brcmf_dbg(INFO, "using SAE offload\n");
-                               profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
-                       }
                        break;
                default:
                        bphy_err(drvr, "invalid akm suite (%d)\n",
                                 sme->crypto.akm_suites[0]);
                        return -EINVAL;
                }
+               if (sme->crypto.sae_pwd) {
+                       profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
+               }
        }
 
        if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X)
                brcmf_dbg(INFO, "using 1X offload\n");
+       if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE)
+               brcmf_dbg(INFO, "using SAE offload\n");
 
        if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
                goto skip_mfp_config;
@@ -2221,7 +2222,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
        brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp);
 
 skip_mfp_config:
-       brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
+       brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
        err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
        if (err) {
                bphy_err(drvr, "could not set wpa_auth (%d)\n", err);
@@ -5509,7 +5510,7 @@ brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
 }
 
 
-static int
+int
 brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                       struct cfg80211_mgmt_tx_params *params, u64 *cookie)
 {
@@ -5616,6 +5617,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 exit:
        return err;
 }
+BRCMF_EXPORT_SYMBOL_GPL(brcmf_cfg80211_mgmt_tx);
 
 static int brcmf_cfg80211_set_cqm_rssi_range_config(struct wiphy *wiphy,
                                                    struct net_device *ndev,
@@ -6009,6 +6011,7 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
 
        vif->wdev.wiphy = cfg->wiphy;
        vif->wdev.iftype = type;
+       init_completion(&vif->mgmt_tx);
 
        brcmf_init_prof(&vif->profile);
 
@@ -7348,6 +7351,7 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
        [NL80211_IFTYPE_STATION] = {
                .tx = 0xffff,
                .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+                     BIT(IEEE80211_STYPE_AUTH >> 4) |
                      BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
        },
        [NL80211_IFTYPE_P2P_CLIENT] = {
@@ -7655,6 +7659,8 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
                        wiphy_ext_feature_set(wiphy,
                                              NL80211_EXT_FEATURE_SAE_OFFLOAD_AP);
        }
+       if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE_EXT))
+               wiphy->features |= NL80211_FEATURE_SAE;
        wiphy->mgmt_stypes = brcmf_txrx_stypes;
        wiphy->max_remain_on_channel_duration = 5000;
        if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
index 2abae8894614b6c8d5f91be7628aed054d38cdb0..b83485ec7b8774d673bdeef2e9d8dbd1a6a93845 100644 (file)
@@ -13,6 +13,8 @@
 #include "fwil_types.h"
 #include "p2p.h"
 
+#define        DOT11_MGMT_HDR_LEN              24      /* d11 management header len */
+
 #define BRCMF_SCAN_IE_LEN_MAX          2048
 
 #define WL_NUM_SCAN_MAX                        10
@@ -141,6 +143,21 @@ enum brcmf_profile_fwauth {
        BRCMF_PROFILE_FWAUTH_SAE
 };
 
+/**
+ * enum brcmf_mgmt_tx_status - mgmt frame tx status
+ *
+ * @BRCMF_MGMT_TX_ACK: mgmt frame acked
+ * @BRCMF_MGMT_TX_NOACK: mgmt frame not acked
+ * @BRCMF_MGMT_TX_OFF_CHAN_COMPLETED: off-channel complete
+ * @BRCMF_MGMT_TX_SEND_FRAME: mgmt frame tx is in progres
+ */
+enum brcmf_mgmt_tx_status {
+       BRCMF_MGMT_TX_ACK,
+       BRCMF_MGMT_TX_NOACK,
+       BRCMF_MGMT_TX_OFF_CHAN_COMPLETED,
+       BRCMF_MGMT_TX_SEND_FRAME
+};
+
 /**
  * struct brcmf_cfg80211_profile - profile information.
  *
@@ -211,6 +228,9 @@ struct vif_saved_ie {
  * @profile: profile information.
  * @sme_state: SME state using enum brcmf_vif_status bits.
  * @list: linked list.
+ * @mgmt_tx: completion for management frame transmit.
+ * @mgmt_tx_status: status of last management frame sent to firmware.
+ * @mgmt_tx_id:
  * @mgmt_rx_reg: registered rx mgmt frame types.
  * @mbss: Multiple BSS type, set if not first AP (not relevant for P2P).
  * @cqm_rssi_low: Lower RSSI limit for CQM monitoring
@@ -224,6 +244,9 @@ struct brcmf_cfg80211_vif {
        unsigned long sme_state;
        struct vif_saved_ie saved_ie;
        struct list_head list;
+       struct completion mgmt_tx;
+       unsigned long mgmt_tx_status;
+       u32 mgmt_tx_id;
        u16 mgmt_rx_reg;
        bool mbss;
        int is_11d;
@@ -468,5 +491,7 @@ void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg);
 void brcmf_cfg80211_free_netdev(struct net_device *ndev);
 
 int brcmf_set_wsec(struct brcmf_if *ifp, const u8 *key, u16 key_len, u16 flags);
+int brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+                          struct cfg80211_mgmt_tx_params *params, u64 *cookie);
 
 #endif /* BRCMFMAC_CFG80211_H */
index 9a48378814866781820936c19ea147989d888122..c9537fb597ce850842ca6cb8bd1f6856de04c3d3 100644 (file)
@@ -8,11 +8,21 @@
 #include <bus.h>
 #include <fwvid.h>
 #include <fwil.h>
+#include <fweh.h>
 
 #include "vops.h"
+#include "fwil_types.h"
 
+/* event definitions */
+#define BRCMF_CYW_E_EXT_AUTH_REQ       187
+#define BRCMF_CYW_E_EXT_AUTH_FRAME_RX  188
+#define BRCMF_CYW_E_MGMT_FRAME_TXS     189
+#define BRCMF_CYW_E_MGMT_FRAME_TXS_OC  190
 #define BRCMF_CYW_E_LAST               197
 
+#define MGMT_AUTH_FRAME_DWELL_TIME     4000
+#define MGMT_AUTH_FRAME_WAIT_TIME      (MGMT_AUTH_FRAME_DWELL_TIME + 100)
+
 static int brcmf_cyw_set_sae_pwd(struct brcmf_if *ifp,
                                 struct cfg80211_crypto_settings *crypto)
 {
@@ -39,6 +49,19 @@ static int brcmf_cyw_set_sae_pwd(struct brcmf_if *ifp,
        return err;
 }
 
+static const struct brcmf_fweh_event_map brcmf_cyw_event_map = {
+       .items = {
+               { BRCMF_E_EXT_AUTH_REQ, BRCMF_CYW_E_EXT_AUTH_REQ },
+               { BRCMF_E_EXT_AUTH_FRAME_RX, BRCMF_CYW_E_EXT_AUTH_FRAME_RX },
+               { BRCMF_E_MGMT_FRAME_TXSTATUS, BRCMF_CYW_E_MGMT_FRAME_TXS },
+               {
+                       BRCMF_E_MGMT_FRAME_OFFCHAN_DONE,
+                       BRCMF_CYW_E_MGMT_FRAME_TXS_OC
+               },
+       },
+       .n_items = 4
+};
+
 static int brcmf_cyw_alloc_fweh_info(struct brcmf_pub *drvr)
 {
        struct brcmf_fweh_info *fweh;
@@ -49,11 +72,296 @@ static int brcmf_cyw_alloc_fweh_info(struct brcmf_pub *drvr)
                return -ENOMEM;
 
        fweh->num_event_codes = BRCMF_CYW_E_LAST;
+       fweh->event_map = &brcmf_cyw_event_map;
        drvr->fweh = fweh;
        return 0;
 }
 
+static int brcmf_cyw_activate_events(struct brcmf_if *ifp)
+{
+       struct brcmf_fweh_info *fweh = ifp->drvr->fweh;
+       struct brcmf_eventmsgs_ext *eventmask_msg;
+       u32 msglen;
+       int err;
+
+       msglen = sizeof(*eventmask_msg) + fweh->event_mask_len;
+       eventmask_msg = kzalloc(msglen, GFP_KERNEL);
+       if (!eventmask_msg)
+               return -ENOMEM;
+       eventmask_msg->ver = EVENTMSGS_VER;
+       eventmask_msg->command = CYW_EVENTMSGS_SET_MASK;
+       eventmask_msg->len = fweh->event_mask_len;
+       memcpy(eventmask_msg->mask, fweh->event_mask, fweh->event_mask_len);
+
+       err = brcmf_fil_iovar_data_set(ifp, "event_msgs_ext", eventmask_msg,
+                                      msglen);
+       kfree(eventmask_msg);
+       return err;
+}
+
+static
+int brcmf_cyw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+                     struct cfg80211_mgmt_tx_params *params, u64 *cookie)
+{
+       struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+       struct ieee80211_channel *chan = params->chan;
+       struct brcmf_pub *drvr = cfg->pub;
+       const u8 *buf = params->buf;
+       size_t len = params->len;
+       const struct ieee80211_mgmt *mgmt;
+       struct brcmf_cfg80211_vif *vif;
+       s32 err = 0;
+       bool ack = false;
+       s32 chan_nr;
+       u32 freq;
+       struct brcmf_mf_params_le *mf_params;
+       u32 mf_params_len;
+       s32 ready;
+
+       brcmf_dbg(TRACE, "Enter\n");
+
+       mgmt = (const struct ieee80211_mgmt *)buf;
+
+       if (!ieee80211_is_auth(mgmt->frame_control))
+               return brcmf_cfg80211_mgmt_tx(wiphy, wdev, params, cookie);
+
+       *cookie = 0;
+       vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+
+       reinit_completion(&vif->mgmt_tx);
+       clear_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status);
+       clear_bit(BRCMF_MGMT_TX_NOACK, &vif->mgmt_tx_status);
+       clear_bit(BRCMF_MGMT_TX_OFF_CHAN_COMPLETED,
+                 &vif->mgmt_tx_status);
+       mf_params_len = offsetof(struct brcmf_mf_params_le, data) +
+                       (len - DOT11_MGMT_HDR_LEN);
+       mf_params = kzalloc(mf_params_len, GFP_KERNEL);
+       if (!mf_params)
+               return -ENOMEM;
+
+       mf_params->dwell_time = cpu_to_le32(MGMT_AUTH_FRAME_DWELL_TIME);
+       mf_params->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
+       mf_params->frame_control = mgmt->frame_control;
+
+       if (chan)
+               freq = chan->center_freq;
+       else
+               brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
+                                     &freq);
+       chan_nr = ieee80211_frequency_to_channel(freq);
+       mf_params->channel = cpu_to_le16(chan_nr);
+       memcpy(&mf_params->da[0], &mgmt->da[0], ETH_ALEN);
+       memcpy(&mf_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
+       mf_params->packet_id = cpu_to_le32(*cookie);
+       memcpy(mf_params->data, &buf[DOT11_MGMT_HDR_LEN],
+              le16_to_cpu(mf_params->len));
+
+       brcmf_dbg(TRACE, "Auth frame, cookie=%d, fc=%04x, len=%d, channel=%d\n",
+                 le32_to_cpu(mf_params->packet_id),
+                 le16_to_cpu(mf_params->frame_control),
+                 le16_to_cpu(mf_params->len), chan_nr);
+
+       vif->mgmt_tx_id = le32_to_cpu(mf_params->packet_id);
+       set_bit(BRCMF_MGMT_TX_SEND_FRAME, &vif->mgmt_tx_status);
+
+       err = brcmf_fil_bsscfg_data_set(vif->ifp, "mgmt_frame",
+                                       mf_params, mf_params_len);
+       if (err) {
+               bphy_err(drvr, "Failed to send Auth frame: err=%d\n",
+                        err);
+               goto tx_status;
+       }
+
+       ready = wait_for_completion_timeout(&vif->mgmt_tx,
+                                           MGMT_AUTH_FRAME_WAIT_TIME);
+       if (test_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status)) {
+               brcmf_dbg(TRACE, "TX Auth frame operation is success\n");
+               ack = true;
+       } else {
+               bphy_err(drvr, "TX Auth frame operation is %s: status=%ld)\n",
+                        ready ? "failed" : "timedout", vif->mgmt_tx_status);
+       }
+
+tx_status:
+       cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
+                               GFP_KERNEL);
+       kfree(mf_params);
+       return err;
+}
+
+static int
+brcmf_cyw_external_auth(struct wiphy *wiphy, struct net_device *dev,
+                       struct cfg80211_external_auth_params *params)
+{
+       struct brcmf_if *ifp;
+       struct brcmf_pub *drvr;
+       struct brcmf_auth_req_status_le auth_status;
+       int ret = 0;
+
+       brcmf_dbg(TRACE, "Enter\n");
+
+       ifp = netdev_priv(dev);
+       drvr = ifp->drvr;
+       if (params->status == WLAN_STATUS_SUCCESS) {
+               auth_status.flags = cpu_to_le16(BRCMF_EXTAUTH_SUCCESS);
+       } else {
+               bphy_err(drvr, "External authentication failed: status=%d\n",
+                        params->status);
+               auth_status.flags = cpu_to_le16(BRCMF_EXTAUTH_FAIL);
+       }
+
+       memcpy(auth_status.peer_mac, params->bssid, ETH_ALEN);
+       params->ssid.ssid_len = min_t(u8, params->ssid.ssid_len,
+                                     IEEE80211_MAX_SSID_LEN);
+       auth_status.ssid_len = cpu_to_le32(params->ssid.ssid_len);
+       memcpy(auth_status.ssid, params->ssid.ssid, params->ssid.ssid_len);
+
+       ret = brcmf_fil_iovar_data_set(ifp, "auth_status", &auth_status,
+                                      sizeof(auth_status));
+       if (ret < 0)
+               bphy_err(drvr, "auth_status iovar failed: ret=%d\n", ret);
+
+       return ret;
+}
+
+static void brcmf_cyw_get_cfg80211_ops(struct brcmf_pub *drvr)
+{
+       drvr->ops->mgmt_tx = brcmf_cyw_mgmt_tx;
+       drvr->ops->external_auth = brcmf_cyw_external_auth;
+}
+
+static s32
+brcmf_cyw_notify_ext_auth_req(struct brcmf_if *ifp,
+                             const struct brcmf_event_msg *e, void *data)
+{
+       struct brcmf_pub *drvr = ifp->drvr;
+       struct cfg80211_external_auth_params params;
+       struct brcmf_auth_req_status_le *auth_req =
+               (struct brcmf_auth_req_status_le *)data;
+       s32 err = 0;
+
+       brcmf_dbg(INFO, "Enter: event %s (%d) received\n",
+                 brcmf_fweh_event_name(e->event_code), e->event_code);
+
+       if (e->datalen < sizeof(*auth_req)) {
+               bphy_err(drvr, "Event %s (%d) data too small. Ignore\n",
+                        brcmf_fweh_event_name(e->event_code), e->event_code);
+               return -EINVAL;
+       }
+
+       memset(&params, 0, sizeof(params));
+       params.action = NL80211_EXTERNAL_AUTH_START;
+       params.key_mgmt_suite = WLAN_AKM_SUITE_SAE;
+       params.status = WLAN_STATUS_SUCCESS;
+       params.ssid.ssid_len = min_t(u32, 32, le32_to_cpu(auth_req->ssid_len));
+       memcpy(params.ssid.ssid, auth_req->ssid, params.ssid.ssid_len);
+       memcpy(params.bssid, auth_req->peer_mac, ETH_ALEN);
+
+       err = cfg80211_external_auth_request(ifp->ndev, &params, GFP_KERNEL);
+       if (err)
+               bphy_err(drvr, "Ext Auth request to supplicant failed (%d)\n",
+                        err);
+
+       return err;
+}
+
+static s32
+brcmf_notify_auth_frame_rx(struct brcmf_if *ifp,
+                          const struct brcmf_event_msg *e, void *data)
+{
+       struct brcmf_pub *drvr = ifp->drvr;
+       struct brcmf_cfg80211_info *cfg = drvr->config;
+       struct wireless_dev *wdev;
+       u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data);
+       struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data;
+       u8 *frame = (u8 *)(rxframe + 1);
+       struct brcmu_chan ch;
+       struct ieee80211_mgmt *mgmt_frame;
+       s32 freq;
+
+       brcmf_dbg(INFO, "Enter: event %s (%d) received\n",
+                 brcmf_fweh_event_name(e->event_code), e->event_code);
+
+       if (e->datalen < sizeof(*rxframe)) {
+               bphy_err(drvr, "Event %s (%d) data too small. Ignore\n",
+                        brcmf_fweh_event_name(e->event_code), e->event_code);
+               return -EINVAL;
+       }
+
+       wdev = &ifp->vif->wdev;
+       WARN_ON(!wdev);
+
+       ch.chspec = be16_to_cpu(rxframe->chanspec);
+       cfg->d11inf.decchspec(&ch);
+
+       mgmt_frame = kzalloc(mgmt_frame_len, GFP_KERNEL);
+       if (!mgmt_frame)
+               return -ENOMEM;
+
+       mgmt_frame->frame_control = cpu_to_le16(IEEE80211_STYPE_AUTH);
+       memcpy(mgmt_frame->da, ifp->mac_addr, ETH_ALEN);
+       memcpy(mgmt_frame->sa, e->addr, ETH_ALEN);
+       brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mgmt_frame->bssid,
+                              ETH_ALEN);
+       frame += offsetof(struct ieee80211_mgmt, u);
+       memcpy(&mgmt_frame->u, frame,
+              mgmt_frame_len - offsetof(struct ieee80211_mgmt, u));
+
+       freq = ieee80211_channel_to_frequency(ch.control_ch_num,
+                                             ch.band == BRCMU_CHAN_BAND_2G ?
+                                             NL80211_BAND_2GHZ :
+                                             NL80211_BAND_5GHZ);
+
+       cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len,
+                        NL80211_RXMGMT_FLAG_EXTERNAL_AUTH);
+       kfree(mgmt_frame);
+       return 0;
+}
+
+static s32
+brcmf_notify_mgmt_tx_status(struct brcmf_if *ifp,
+                           const struct brcmf_event_msg *e, void *data)
+{
+       struct brcmf_cfg80211_vif *vif = ifp->vif;
+       u32 *packet_id = (u32 *)data;
+
+       brcmf_dbg(INFO, "Enter: event %s (%d), status=%d\n",
+                 brcmf_fweh_event_name(e->event_code), e->event_code,
+                 e->status);
+
+       if (!test_bit(BRCMF_MGMT_TX_SEND_FRAME, &vif->mgmt_tx_status) ||
+           (*packet_id != vif->mgmt_tx_id))
+               return 0;
+
+       if (e->event_code == BRCMF_E_MGMT_FRAME_TXSTATUS) {
+               if (e->status == BRCMF_E_STATUS_SUCCESS)
+                       set_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status);
+               else
+                       set_bit(BRCMF_MGMT_TX_NOACK, &vif->mgmt_tx_status);
+       } else {
+               set_bit(BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, &vif->mgmt_tx_status);
+       }
+
+       complete(&vif->mgmt_tx);
+       return 0;
+}
+
+static void brcmf_cyw_register_event_handlers(struct brcmf_pub *drvr)
+{
+       brcmf_fweh_register(drvr, BRCMF_E_EXT_AUTH_REQ,
+                           brcmf_cyw_notify_ext_auth_req);
+       brcmf_fweh_register(drvr, BRCMF_E_EXT_AUTH_FRAME_RX,
+                           brcmf_notify_auth_frame_rx);
+       brcmf_fweh_register(drvr, BRCMF_E_MGMT_FRAME_TXSTATUS,
+                           brcmf_notify_mgmt_tx_status);
+       brcmf_fweh_register(drvr, BRCMF_E_MGMT_FRAME_OFFCHAN_DONE,
+                           brcmf_notify_mgmt_tx_status);
+}
+
 const struct brcmf_fwvid_ops brcmf_cyw_ops = {
        .set_sae_password = brcmf_cyw_set_sae_pwd,
        .alloc_fweh_info = brcmf_cyw_alloc_fweh_info,
+       .activate_events = brcmf_cyw_activate_events,
+       .get_cfg80211_ops = brcmf_cyw_get_cfg80211_ops,
+       .register_event_handlers = brcmf_cyw_register_event_handlers,
 };
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/fwil_types.h
new file mode 100644 (file)
index 0000000..a3243b9
--- /dev/null
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ */
+
+#ifndef CYW_FWIL_TYPES_H_
+#define CYW_FWIL_TYPES_H_
+
+#include <fwil_types.h>
+
+enum brcmf_event_msgs_ext_command {
+       CYW_EVENTMSGS_NONE      = 0,
+       CYW_EVENTMSGS_SET_BIT   = 1,
+       CYW_EVENTMSGS_RESET_BIT = 2,
+       CYW_EVENTMSGS_SET_MASK  = 3,
+};
+
+#define EVENTMSGS_VER 1
+#define EVENTMSGS_EXT_STRUCT_SIZE      offsetof(struct eventmsgs_ext, mask[0])
+
+/**
+ * struct brcmf_eventmsgs_ext - structure used with "eventmsgs_ext" iovar.
+ *
+ * @ver: version.
+ * @command: requested operation (see &enum event_msgs_ext_command).
+ * @len: length of the @mask array.
+ * @maxgetsize: indicates maximum mask size that may be returned by firmware
+ *     upon iovar GET.
+ * @mask: array where each bit represents firmware event.
+ */
+struct brcmf_eventmsgs_ext {
+       u8      ver;
+       u8      command;
+       u8      len;
+       u8      maxgetsize;
+       u8      mask[] __counted_by(len);
+};
+
+#define BRCMF_EXTAUTH_START            1
+#define BRCMF_EXTAUTH_ABORT            2
+#define BRCMF_EXTAUTH_FAIL             3
+#define BRCMF_EXTAUTH_SUCCESS          4
+
+/**
+ * struct brcmf_auth_req_status_le - external auth request and status update
+ *
+ * @flags: flags for external auth status
+ * @peer_mac: peer MAC address
+ * @ssid_len: length of ssid
+ * @ssid: ssid characters
+ */
+struct brcmf_auth_req_status_le {
+       __le16 flags;
+       u8 peer_mac[ETH_ALEN];
+       __le32 ssid_len;
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+};
+
+/**
+ * struct brcmf_mf_params_le - management frame parameters for mgmt_frame iovar
+ *
+ * @version: version of the iovar
+ * @dwell_time: dwell duration in ms
+ * @len: length of frame data
+ * @frame_control: frame control
+ * @channel: channel
+ * @da: peer MAC address
+ * @bssid: BSS network identifier
+ * @packet_id: packet identifier
+ * @data: frame data
+ */
+struct brcmf_mf_params_le {
+       __le32 version;
+       __le32 dwell_time;
+       __le16 len;
+       __le16 frame_control;
+       __le16 channel;
+       u8 da[ETH_ALEN];
+       u8 bssid[ETH_ALEN];
+       __le32 packet_id;
+       u8 data[] __counted_by(len);
+};
+
+#endif /* CYW_FWIL_TYPES_H_ */
+
index 0d9ae197fa1ec341a05844b1546634f5eab3ab36..488364ef8ff2a1f2dfd92386cec9f50b4f90c5cd 100644 (file)
@@ -42,8 +42,9 @@ static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = {
        { BRCMF_FEAT_MONITOR_FLAG, "rtap" },
        { BRCMF_FEAT_MONITOR_FMT_RADIOTAP, "rtap" },
        { BRCMF_FEAT_DOT11H, "802.11h" },
-       { BRCMF_FEAT_SAE, "sae" },
+       { BRCMF_FEAT_SAE, "sae " },
        { BRCMF_FEAT_FWAUTH, "idauth" },
+       { BRCMF_FEAT_SAE_EXT, "sae_ext" },
 };
 
 #ifdef DEBUG
index 7f4f0b3e4a7b4a39e06f7e183d3504d1879c072f..31f8695ca41765befad013a9f1653b97e088e1ff 100644 (file)
@@ -31,6 +31,7 @@
  * FWAUTH: Firmware authenticator
  * DUMP_OBSS: Firmware has capable to dump obss info to support ACS
  * SCAN_V2: Version 2 scan params
+ * SAE_EXT: SAE authentication handled by user-space supplicant
  */
 #define BRCMF_FEAT_LIST \
        BRCMF_FEAT_DEF(MBSS) \
@@ -57,7 +58,8 @@
        BRCMF_FEAT_DEF(DUMP_OBSS) \
        BRCMF_FEAT_DEF(SCAN_V2) \
        BRCMF_FEAT_DEF(PMKID_V2) \
-       BRCMF_FEAT_DEF(PMKID_V3)
+       BRCMF_FEAT_DEF(PMKID_V3) \
+       BRCMF_FEAT_DEF(SAE_EXT)
 
 /*
  * Quirks:
index 4f76e812a860a9f23c61fa4e95a42e85f4eceb29..c2d98ee6652f3a2d5d1e4172893990c17e85d06e 100644 (file)
@@ -450,11 +450,14 @@ int brcmf_fweh_activate_events(struct brcmf_if *ifp)
        brcmf_dbg(EVENT, "enable event IF\n");
        setbit(fweh->event_mask, BRCMF_E_IF);
 
+       /* allow per-vendor method to activate firmware events */
+       if (!brcmf_fwvid_activate_events(ifp))
+               return 0;
+
        err = brcmf_fil_iovar_data_set(ifp, "event_msgs", fweh->event_mask,
                                       fweh->event_mask_len);
        if (err)
                bphy_err(fweh->drvr, "Set event_msgs error (%d)\n", err);
-
        return err;
 }
 
index 38a9138f1fd31364215caa12a394dd5acd9f70f2..e327dd58d29c950675ffa3c92759970879da18ea 100644 (file)
@@ -94,7 +94,11 @@ struct brcmf_cfg80211_info;
        BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \
        BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \
        BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \
-       BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127)
+       BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \
+       BRCMF_ABSTRACT_ENUM_DEF(EXT_AUTH_REQ, 0) \
+       BRCMF_ABSTRACT_ENUM_DEF(EXT_AUTH_FRAME_RX, 1) \
+       BRCMF_ABSTRACT_ENUM_DEF(MGMT_FRAME_TXSTATUS, 2) \
+       BRCMF_ABSTRACT_ENUM_DEF(MGMT_FRAME_OFFCHAN_DONE, 3)
 
 #define BRCMF_ENUM_DEF(id, val) \
        BRCMF_E_##id = (val),
index 7fe78d41f29162dd03201356859224352c1af5ef..f3e011d090f29a93eb9fe5c1c2d802a9a5a0403a 100644 (file)
@@ -15,6 +15,7 @@ struct brcmf_fwvid_ops {
        void (*feat_attach)(struct brcmf_if *ifp);
        int (*set_sae_password)(struct brcmf_if *ifp, struct cfg80211_crypto_settings *crypto);
        int (*alloc_fweh_info)(struct brcmf_pub *drvr);
+       int (*activate_events)(struct brcmf_if *ifp);
        void (*get_cfg80211_ops)(struct brcmf_pub *drvr);
        void (*register_event_handlers)(struct brcmf_pub *drvr);
 };
@@ -58,6 +59,16 @@ static inline int brcmf_fwvid_alloc_fweh_info(struct brcmf_pub *drvr)
        return drvr->vops->alloc_fweh_info(drvr);
 }
 
+static inline int brcmf_fwvid_activate_events(struct brcmf_if *ifp)
+{
+       const struct brcmf_fwvid_ops *vops = ifp->drvr->vops;
+
+       if (!vops || !vops->activate_events)
+               return -EOPNOTSUPP;
+
+       return vops->activate_events(ifp);
+}
+
 static inline void brcmf_fwvid_get_cfg80211_ops(struct brcmf_pub *drvr)
 {
        if (!drvr->vops || !drvr->vops->get_cfg80211_ops)