From: Jeff Chen <jeff.chen_1@nxp.com>
To: linux-wireless@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org,
johannes@sipsolutions.net, francesco@dolcini.it,
tsung-hsien.hsieh@nxp.com, s.hauer@pengutronix.de,
Jeff Chen <jeff.chen_1@nxp.com>
Subject: [PATCH v7 07/22] wifi: nxpwifi: add join and association support
Date: Mon, 17 Nov 2025 19:00:31 +0800 [thread overview]
Message-ID: <20251117110046.2810811-8-jeff.chen_1@nxp.com> (raw)
In-Reply-To: <20251117110046.2810811-1-jeff.chen_1@nxp.com>
Introduce join.c to implement infrastructure mode association and
deauthentication logic for the nxpwifi driver.
- Implements association command construction, including:
- SSID, channel, rates, authentication, WPA/WPA2/RSN IEs
- 802.11n/ac/ax capability TLVs
- WMM and vendor-specific IEs
- TSF synchronization and scan result integration
- Handles association response parsing and connection state updates
- Adds support for deauthentication (infra, AP, adhoc)
- Updates current BSS parameters and enables data path upon success
This patch enables full STA-mode association flow with cfg80211
integration.
Signed-off-by: Jeff Chen <jeff.chen_1@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/join.c | 910 ++++++++++++++++++++++++
1 file changed, 910 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/join.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/join.c b/drivers/net/wireless/nxp/nxpwifi/join.c
new file mode 100644
index 000000000000..ba58df07b84c
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/join.c
@@ -0,0 +1,910 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: association and ad-hoc start/join
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "cfg.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cmdevt.h"
+#include "wmm.h"
+#include "11n.h"
+#include "11ac.h"
+#include "11ax.h"
+
+#define CAPINFO_MASK (~(BIT(15) | BIT(14) | BIT(12) | BIT(11) | BIT(9)))
+
+/* Append a generic element as a pass through TLV to a TLV buffer.
+ *
+ * This function is called from the network join command preparation routine.
+ *
+ * If the element buffer has been setup by the application, this routine appends
+ * the buffer as a pass through TLV type to the request.
+ */
+static int
+nxpwifi_cmd_append_generic_ie(struct nxpwifi_private *priv, u8 **buffer)
+{
+ int ret_len = 0;
+ struct nxpwifi_ie_types_header ie_header;
+
+ /* Null Checks */
+ if (!buffer)
+ return 0;
+ if (!(*buffer))
+ return 0;
+
+ /* If there is a generic element buffer setup, append it to the return
+ * parameter buffer pointer.
+ */
+ if (priv->gen_ie_buf_len) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: %s: append generic element len %d to %p\n",
+ __func__, priv->gen_ie_buf_len, *buffer);
+
+ /* Wrap the generic element buffer with a pass through TLV type */
+ ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
+ ie_header.len = cpu_to_le16(priv->gen_ie_buf_len);
+ memcpy(*buffer, &ie_header, sizeof(ie_header));
+
+ /* Increment the return size and the return buffer pointer
+ * param
+ */
+ *buffer += sizeof(ie_header);
+ ret_len += sizeof(ie_header);
+
+ /* Copy the generic element buffer to the output buffer, advance
+ * pointer
+ */
+ memcpy(*buffer, priv->gen_ie_buf, priv->gen_ie_buf_len);
+
+ /* Increment the return size and the return buffer pointer
+ * param
+ */
+ *buffer += priv->gen_ie_buf_len;
+ ret_len += priv->gen_ie_buf_len;
+
+ /* Reset the generic element buffer */
+ priv->gen_ie_buf_len = 0;
+ }
+
+ /* return the length appended to the buffer */
+ return ret_len;
+}
+
+/* Append TSF tracking info from the scan table for the target AP.
+ *
+ * This function is called from the network join command preparation routine.
+ *
+ * The TSF table TSF sent to the firmware contains two TSF values:
+ * - The TSF of the target AP from its previous beacon/probe response
+ * - The TSF timestamp of our local MAC at the time we observed the
+ * beacon/probe response.
+ *
+ * The firmware uses the timestamp values to set an initial TSF value
+ * in the MAC for the new association after a reassociation attempt.
+ */
+static int
+nxpwifi_cmd_append_tsf_tlv(struct nxpwifi_private *priv, u8 **buffer,
+ struct nxpwifi_bssdescriptor *bss_desc)
+{
+ struct nxpwifi_ie_types_tsf_timestamp tsf_tlv;
+ __le64 tsf_val;
+
+ /* Null Checks */
+ if (!buffer)
+ return 0;
+ if (!*buffer)
+ return 0;
+
+ memset(&tsf_tlv, 0x00, sizeof(struct nxpwifi_ie_types_tsf_timestamp));
+
+ tsf_tlv.header.type = cpu_to_le16(TLV_TYPE_TSFTIMESTAMP);
+ tsf_tlv.header.len = cpu_to_le16(2 * sizeof(tsf_val));
+
+ memcpy(*buffer, &tsf_tlv, sizeof(tsf_tlv.header));
+ *buffer += sizeof(tsf_tlv.header);
+
+ /* TSF at the time when beacon/probe_response was received */
+ tsf_val = cpu_to_le64(bss_desc->fw_tsf);
+ memcpy(*buffer, &tsf_val, sizeof(tsf_val));
+ *buffer += sizeof(tsf_val);
+
+ tsf_val = cpu_to_le64(bss_desc->timestamp);
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: %s: TSF offset calc: %016llx - %016llx\n",
+ __func__, bss_desc->timestamp, bss_desc->fw_tsf);
+
+ memcpy(*buffer, &tsf_val, sizeof(tsf_val));
+ *buffer += sizeof(tsf_val);
+
+ return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val));
+}
+
+/* This function finds out the common rates between rate1 and rate2.
+ *
+ * It will fill common rates in rate1 as output if found.
+ *
+ * NOTE: Setting the MSB of the basic rates needs to be taken
+ * care of, either before or after calling this function.
+ */
+static int nxpwifi_get_common_rates(struct nxpwifi_private *priv, u8 *rate1,
+ u32 rate1_size, u8 *rate2, u32 rate2_size)
+{
+ int ret;
+ u8 *ptr = rate1, *tmp;
+ u32 i, j;
+
+ tmp = kmemdup(rate1, rate1_size, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ memset(rate1, 0, rate1_size);
+
+ for (i = 0; i < rate2_size && rate2[i]; i++) {
+ for (j = 0; j < rate1_size && tmp[j]; j++) {
+ /* Check common rate, excluding the bit for
+ * basic rate
+ */
+ if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) {
+ *rate1++ = tmp[j];
+ break;
+ }
+ }
+ }
+
+ nxpwifi_dbg(priv->adapter, INFO, "info: Tx data rate set to %#x\n",
+ priv->data_rate);
+
+ if (!priv->is_data_rate_auto) {
+ while (*ptr) {
+ if ((*ptr & 0x7f) == priv->data_rate) {
+ ret = 0;
+ goto done;
+ }
+ ptr++;
+ }
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "previously set fixed data rate %#x\t"
+ "is not compatible with the network\n",
+ priv->data_rate);
+
+ ret = -EPERM;
+ goto done;
+ }
+
+ ret = 0;
+done:
+ kfree(tmp);
+ return ret;
+}
+
+/* This function creates the intersection of the rates supported by a
+ * target BSS and our adapter settings for use in an assoc/join command.
+ */
+static int
+nxpwifi_setup_rates_from_bssdesc(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc,
+ u8 *out_rates, u32 *out_rates_size)
+{
+ u8 card_rates[NXPWIFI_SUPPORTED_RATES];
+ u32 card_rates_size;
+ int ret;
+
+ /* Copy AP supported rates */
+ memcpy(out_rates, bss_desc->supported_rates, NXPWIFI_SUPPORTED_RATES);
+ /* Get the STA supported rates */
+ card_rates_size = nxpwifi_get_active_data_rates(priv, card_rates);
+ /* Get the common rates between AP and STA supported rates */
+ ret = nxpwifi_get_common_rates(priv, out_rates, NXPWIFI_SUPPORTED_RATES,
+ card_rates, card_rates_size);
+ if (ret) {
+ *out_rates_size = 0;
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "%s: cannot get common rates\n",
+ __func__);
+ } else {
+ *out_rates_size =
+ min_t(size_t, strlen(out_rates), NXPWIFI_SUPPORTED_RATES);
+ }
+
+ return ret;
+}
+
+/* This function appends a WPS element. It is called from the network join command
+ * preparation routine.
+ *
+ * If the element buffer has been setup by the application, this routine appends
+ * the buffer as a WPS TLV type to the request.
+ */
+static int
+nxpwifi_cmd_append_wps_ie(struct nxpwifi_private *priv, u8 **buffer)
+{
+ int ret_len = 0;
+ struct nxpwifi_ie_types_header ie_header;
+
+ if (!buffer || !*buffer)
+ return 0;
+
+ /* If there is a wps element buffer setup, append it to the return
+ * parameter buffer pointer.
+ */
+ if (priv->wps_ie_len) {
+ nxpwifi_dbg(priv->adapter, CMD,
+ "cmd: append wps element %d to %p\n",
+ priv->wps_ie_len, *buffer);
+
+ /* Wrap the generic element buffer with a pass through TLV type */
+ ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
+ ie_header.len = cpu_to_le16(priv->wps_ie_len);
+ memcpy(*buffer, &ie_header, sizeof(ie_header));
+ *buffer += sizeof(ie_header);
+ ret_len += sizeof(ie_header);
+
+ memcpy(*buffer, priv->wps_ie, priv->wps_ie_len);
+ *buffer += priv->wps_ie_len;
+ ret_len += priv->wps_ie_len;
+ }
+
+ kfree(priv->wps_ie);
+ priv->wps_ie_len = 0;
+ return ret_len;
+}
+
+/* This function appends rsn element tlv for wpa/wpa2 security modes.
+ * It is called from the network join command preparation routine.
+ */
+static int nxpwifi_append_rsn_ie_wpa_wpa2(struct nxpwifi_private *priv,
+ u8 **buffer)
+{
+ struct nxpwifi_ie_types_rsn_param_set *rsn_ie_tlv;
+ int rsn_ie_len;
+
+ if (!buffer || !(*buffer))
+ return 0;
+
+ rsn_ie_tlv = (struct nxpwifi_ie_types_rsn_param_set *)(*buffer);
+ rsn_ie_tlv->header.type = cpu_to_le16((u16)priv->wpa_ie[0]);
+ rsn_ie_tlv->header.type =
+ cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF);
+ rsn_ie_tlv->header.len = cpu_to_le16((u16)priv->wpa_ie[1]);
+ rsn_ie_tlv->header.len = cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len)
+ & 0x00FF);
+ if (le16_to_cpu(rsn_ie_tlv->header.len) <= (sizeof(priv->wpa_ie) - 2))
+ memcpy(rsn_ie_tlv->rsn_ie, &priv->wpa_ie[2],
+ le16_to_cpu(rsn_ie_tlv->header.len));
+ else
+ return -ENOMEM;
+
+ rsn_ie_len = sizeof(rsn_ie_tlv->header) +
+ le16_to_cpu(rsn_ie_tlv->header.len);
+ *buffer += rsn_ie_len;
+
+ return rsn_ie_len;
+}
+
+/* This function prepares command for association.
+ *
+ * This sets the following parameters -
+ * - Peer MAC address
+ * - Listen interval
+ * - Beacon interval
+ * - Capability information
+ *
+ * ...and the following TLVs, as required -
+ * - SSID TLV
+ * - PHY TLV
+ * - SS TLV
+ * - Rates TLV
+ * - Authentication TLV
+ * - Channel TLV
+ * - WPA/WPA2 element
+ * - 11n TLV
+ * - Vendor specific TLV
+ * - WMM TLV
+ * - Generic element
+ * - TSF TLV
+ *
+ * Preparation also includes -
+ * - Setting command ID and proper size
+ * - Ensuring correct endian-ness
+ */
+int nxpwifi_cmd_802_11_associate(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct nxpwifi_bssdescriptor *bss_desc)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate;
+ struct nxpwifi_ie_types_host_mlme *host_mlme_tlv;
+ struct nxpwifi_ie_types_ssid_param_set *ssid_tlv;
+ struct nxpwifi_ie_types_phy_param_set *phy_tlv;
+ struct nxpwifi_ie_types_ss_param_set *ss_tlv;
+ struct nxpwifi_ie_types_rates_param_set *rates_tlv;
+ struct nxpwifi_ie_types_auth_type *auth_tlv;
+ struct nxpwifi_ie_types_sae_pwe_mode *sae_pwe_tlv;
+ struct nxpwifi_ie_types_chan_list_param_set *chan_tlv;
+ u8 rates[NXPWIFI_SUPPORTED_RATES];
+ u32 rates_size;
+ u16 tmp_cap;
+ u8 *pos;
+ int rsn_ie_len = 0;
+ int ret;
+
+ pos = (u8 *)assoc;
+
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_ASSOCIATE);
+
+ /* Save so we know which BSS Desc to use in the response handler */
+ priv->attempted_bss_desc = bss_desc;
+
+ memcpy(assoc->peer_sta_addr,
+ bss_desc->mac_address, sizeof(assoc->peer_sta_addr));
+ pos += sizeof(assoc->peer_sta_addr);
+
+ /* Set the listen interval */
+ assoc->listen_interval = cpu_to_le16(priv->listen_interval);
+ /* Set the beacon period */
+ assoc->beacon_period = cpu_to_le16(bss_desc->beacon_period);
+
+ pos += sizeof(assoc->cap_info_bitmap);
+ pos += sizeof(assoc->listen_interval);
+ pos += sizeof(assoc->beacon_period);
+ pos += sizeof(assoc->dtim_period);
+
+ host_mlme_tlv = (struct nxpwifi_ie_types_host_mlme *)pos;
+ host_mlme_tlv->header.type = cpu_to_le16(TLV_TYPE_HOST_MLME);
+ host_mlme_tlv->header.len = cpu_to_le16(sizeof(host_mlme_tlv->host_mlme));
+ host_mlme_tlv->host_mlme = 1;
+ pos += sizeof(host_mlme_tlv->header) + sizeof(host_mlme_tlv->host_mlme);
+
+ ssid_tlv = (struct nxpwifi_ie_types_ssid_param_set *)pos;
+ ssid_tlv->header.type = cpu_to_le16(WLAN_EID_SSID);
+ ssid_tlv->header.len = cpu_to_le16((u16)bss_desc->ssid.ssid_len);
+ memcpy(ssid_tlv->ssid, bss_desc->ssid.ssid,
+ le16_to_cpu(ssid_tlv->header.len));
+ pos += sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len);
+
+ phy_tlv = (struct nxpwifi_ie_types_phy_param_set *)pos;
+ phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS);
+ phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set));
+ memcpy(&phy_tlv->fh_ds.ds_param_set,
+ &bss_desc->phy_param_set.ds_param_set.current_chan,
+ sizeof(phy_tlv->fh_ds.ds_param_set));
+ pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len);
+
+ ss_tlv = (struct nxpwifi_ie_types_ss_param_set *)pos;
+ ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS);
+ ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set));
+ pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len);
+
+ /* Get the common rates supported between the driver and the BSS Desc */
+ ret = nxpwifi_setup_rates_from_bssdesc(priv, bss_desc,
+ rates, &rates_size);
+ if (ret)
+ return ret;
+
+ /* Save the data rates into Current BSS state structure */
+ priv->curr_bss_params.num_of_rates = rates_size;
+ memcpy(&priv->curr_bss_params.data_rates, rates, rates_size);
+
+ /* Setup the Rates TLV in the association command */
+ rates_tlv = (struct nxpwifi_ie_types_rates_param_set *)pos;
+ rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
+ rates_tlv->header.len = cpu_to_le16((u16)rates_size);
+ memcpy(rates_tlv->rates, rates, rates_size);
+ pos += sizeof(rates_tlv->header) + rates_size;
+ nxpwifi_dbg(adapter, INFO, "info: ASSOC_CMD: rates size = %d\n",
+ rates_size);
+
+ /* Add the Authentication type */
+ auth_tlv = (struct nxpwifi_ie_types_auth_type *)pos;
+ auth_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
+ auth_tlv->header.len = cpu_to_le16(sizeof(auth_tlv->auth_type));
+ if (priv->sec_info.wep_enabled)
+ auth_tlv->auth_type =
+ cpu_to_le16((u16)priv->sec_info.authentication_mode);
+ else
+ auth_tlv->auth_type = cpu_to_le16(NL80211_AUTHTYPE_OPEN_SYSTEM);
+
+ pos += sizeof(auth_tlv->header) + le16_to_cpu(auth_tlv->header.len);
+
+ if (priv->sec_info.authentication_mode == WLAN_AUTH_SAE) {
+ auth_tlv->auth_type = cpu_to_le16(NXPWIFI_AUTHTYPE_SAE);
+ if (bss_desc->bcn_rsnx_ie &&
+ bss_desc->bcn_rsnx_ie->datalen &&
+ (bss_desc->bcn_rsnx_ie->data[0] &
+ WLAN_RSNX_CAPA_SAE_H2E)) {
+ sae_pwe_tlv =
+ (struct nxpwifi_ie_types_sae_pwe_mode *)pos;
+ sae_pwe_tlv->header.type =
+ cpu_to_le16(TLV_TYPE_SAE_PWE_MODE);
+ sae_pwe_tlv->header.len =
+ cpu_to_le16(sizeof(sae_pwe_tlv->pwe[0]));
+ sae_pwe_tlv->pwe[0] = bss_desc->bcn_rsnx_ie->data[0];
+ pos += sizeof(sae_pwe_tlv->header) +
+ sizeof(sae_pwe_tlv->pwe[0]);
+ }
+ }
+
+ if (IS_SUPPORT_MULTI_BANDS(adapter) &&
+ !(ISSUPP_11NENABLED(adapter->fw_cap_info) &&
+ !bss_desc->disable_11n &&
+ (priv->config_bands & BAND_GN ||
+ priv->config_bands & BAND_AN) &&
+ bss_desc->bcn_ht_cap)) {
+ /* Append a channel TLV for the channel the attempted AP was
+ * found on
+ */
+ chan_tlv = (struct nxpwifi_ie_types_chan_list_param_set *)pos;
+ chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
+ chan_tlv->header.len =
+ cpu_to_le16(sizeof(struct nxpwifi_chan_scan_param_set));
+
+ memset(chan_tlv->chan_scan_param, 0x00,
+ sizeof(struct nxpwifi_chan_scan_param_set));
+ chan_tlv->chan_scan_param[0].chan_number =
+ (bss_desc->phy_param_set.ds_param_set.current_chan);
+ nxpwifi_dbg(adapter, INFO, "info: Assoc: TLV Chan = %d\n",
+ chan_tlv->chan_scan_param[0].chan_number);
+
+ chan_tlv->chan_scan_param[0].band_cfg =
+ nxpwifi_band_to_radio_type((u8)bss_desc->bss_band);
+
+ nxpwifi_dbg(adapter, INFO, "info: Assoc: TLV Band = %d\n",
+ chan_tlv->chan_scan_param[0].band_cfg);
+ pos += sizeof(chan_tlv->header) +
+ sizeof(struct nxpwifi_chan_scan_param_set);
+ }
+
+ if (!priv->wps.session_enable) {
+ if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
+ rsn_ie_len = nxpwifi_append_rsn_ie_wpa_wpa2(priv, &pos);
+
+ if (rsn_ie_len == -ENOMEM)
+ return -ENOMEM;
+ }
+
+ if (ISSUPP_11NENABLED(adapter->fw_cap_info) &&
+ !bss_desc->disable_11n &&
+ (priv->config_bands & BAND_GN ||
+ priv->config_bands & BAND_AN))
+ nxpwifi_cmd_append_11n_tlv(priv, bss_desc, &pos);
+
+ if (ISSUPP_11ACENABLED(adapter->fw_cap_info) &&
+ !bss_desc->disable_11n && !bss_desc->disable_11ac &&
+ (priv->config_bands & BAND_GAC ||
+ priv->config_bands & BAND_AAC))
+ nxpwifi_cmd_append_11ac_tlv(priv, bss_desc, &pos);
+
+ if (ISSUPP_11AXENABLED(adapter->fw_cap_ext) &&
+ nxpwifi_11ax_bandconfig_allowed(priv, bss_desc))
+ nxpwifi_cmd_append_11ax_tlv(priv, bss_desc, &pos);
+
+ /* Append vendor specific element TLV */
+ nxpwifi_cmd_append_vsie_tlv(priv, NXPWIFI_VSIE_MASK_ASSOC, &pos);
+
+ nxpwifi_wmm_process_association_req(priv, &pos, &bss_desc->wmm_ie,
+ bss_desc->bcn_ht_cap);
+
+ if (priv->wps.session_enable && priv->wps_ie_len)
+ nxpwifi_cmd_append_wps_ie(priv, &pos);
+
+ nxpwifi_cmd_append_generic_ie(priv, &pos);
+
+ nxpwifi_cmd_append_tsf_tlv(priv, &pos, bss_desc);
+
+ nxpwifi_11h_process_join(priv, &pos, bss_desc);
+
+ cmd->size = cpu_to_le16((u16)(pos - (u8 *)assoc) + S_DS_GEN);
+
+ /* Set the Capability info at last */
+ tmp_cap = bss_desc->cap_info_bitmap;
+
+ if (priv->config_bands == BAND_B)
+ tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
+
+ tmp_cap &= CAPINFO_MASK;
+ nxpwifi_dbg(adapter, INFO,
+ "info: ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n",
+ tmp_cap, CAPINFO_MASK);
+ assoc->cap_info_bitmap = cpu_to_le16(tmp_cap);
+
+ return ret;
+}
+
+static const char *assoc_failure_reason_to_str(u16 cap_info)
+{
+ switch (cap_info) {
+ case CONNECT_ERR_AUTH_ERR_STA_FAILURE:
+ return "CONNECT_ERR_AUTH_ERR_STA_FAILURE";
+ case CONNECT_ERR_AUTH_MSG_UNHANDLED:
+ return "CONNECT_ERR_AUTH_MSG_UNHANDLED";
+ case CONNECT_ERR_ASSOC_ERR_TIMEOUT:
+ return "CONNECT_ERR_ASSOC_ERR_TIMEOUT";
+ case CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED:
+ return "CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED";
+ case CONNECT_ERR_STA_FAILURE:
+ return "CONNECT_ERR_STA_FAILURE";
+ }
+
+ return "Unknown connect failure";
+}
+
+/* Association firmware command response handler
+ *
+ * The response buffer for the association command has the following
+ * memory layout.
+ *
+ * For cases where an association response was not received (indicated
+ * by the CapInfo and AId field):
+ *
+ * .------------------------------------------------------------.
+ * | Header(4 * sizeof(t_u16)): Standard command response hdr |
+ * .------------------------------------------------------------.
+ * | cap_info/Error Return(t_u16): |
+ * | 0xFFFF(-1): Internal error |
+ * | 0xFFFE(-2): Authentication unhandled message |
+ * | 0xFFFD(-3): Authentication refused |
+ * | 0xFFFC(-4): Timeout waiting for AP response |
+ * .------------------------------------------------------------.
+ * | status_code(t_u16): |
+ * | If cap_info is -1: |
+ * | An internal firmware failure prevented the |
+ * | command from being processed. The status_code |
+ * | will be set to 1. |
+ * | |
+ * | If cap_info is -2: |
+ * | An authentication frame was received but was |
+ * | not handled by the firmware. IEEE Status |
+ * | code for the failure is returned. |
+ * | |
+ * | If cap_info is -3: |
+ * | An authentication frame was received and the |
+ * | status_code is the IEEE Status reported in the |
+ * | response. |
+ * | |
+ * | If cap_info is -4: |
+ * | (1) Association response timeout |
+ * | (2) Authentication response timeout |
+ * .------------------------------------------------------------.
+ * | a_id(t_u16): 0xFFFF |
+ * .------------------------------------------------------------.
+ *
+ *
+ * For cases where an association response was received, the IEEE
+ * standard association response frame is returned:
+ *
+ * .------------------------------------------------------------.
+ * | Header(4 * sizeof(t_u16)): Standard command response hdr |
+ * .------------------------------------------------------------.
+ * | cap_info(t_u16): IEEE Capability |
+ * .------------------------------------------------------------.
+ * | status_code(t_u16): IEEE Status Code |
+ * .------------------------------------------------------------.
+ * | a_id(t_u16): IEEE Association ID |
+ * .------------------------------------------------------------.
+ * | IEEE IEs(variable): Any received IEs comprising the |
+ * | remaining portion of a received |
+ * | association response frame. |
+ * .------------------------------------------------------------.
+ *
+ * For simplistic handling, the status_code field can be used to determine
+ * an association success (0) or failure (non-zero).
+ */
+int nxpwifi_ret_802_11_associate(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int ret = 0;
+ struct ieee_types_assoc_rsp *assoc_rsp;
+ struct nxpwifi_bssdescriptor *bss_desc;
+ bool enable_data = true;
+ u16 cap_info, status_code, aid;
+ const u8 *ie_ptr;
+ struct ieee80211_ht_operation *assoc_resp_ht_oper;
+ struct ieee80211_mgmt *hdr;
+
+ if (!priv->attempted_bss_desc) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: failed, association terminated by host\n",
+ __func__);
+ goto done;
+ }
+
+ hdr = (struct ieee80211_mgmt *)&resp->params;
+ if (!memcmp(hdr->bssid, priv->attempted_bss_desc->mac_address,
+ ETH_ALEN))
+ assoc_rsp = (struct ieee_types_assoc_rsp *)&hdr->u.assoc_resp;
+ else
+ assoc_rsp = (struct ieee_types_assoc_rsp *)&resp->params;
+
+ cap_info = le16_to_cpu(assoc_rsp->cap_info_bitmap);
+ status_code = le16_to_cpu(assoc_rsp->status_code);
+ aid = le16_to_cpu(assoc_rsp->a_id);
+
+ if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
+ nxpwifi_dbg(adapter, ERROR,
+ "invalid AID value 0x%x; bits 15:14 not set\n", aid);
+
+ aid &= ~(BIT(15) | BIT(14));
+
+ priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN,
+ sizeof(priv->assoc_rsp_buf));
+
+ assoc_rsp->a_id = cpu_to_le16(aid);
+ memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size);
+
+ if (status_code) {
+ adapter->dbg.num_cmd_assoc_failure++;
+ nxpwifi_dbg(adapter, ERROR,
+ "ASSOC_RESP: failed,\t"
+ "status code=%d err=%#x a_id=%#x\n",
+ status_code, cap_info,
+ le16_to_cpu(assoc_rsp->a_id));
+
+ nxpwifi_dbg(adapter, ERROR, "assoc failure: reason %s\n",
+ assoc_failure_reason_to_str(cap_info));
+ if (cap_info == CONNECT_ERR_ASSOC_ERR_TIMEOUT) {
+ if (status_code == NXPWIFI_ASSOC_CMD_FAILURE_AUTH) {
+ ret = WLAN_STATUS_AUTH_TIMEOUT;
+ nxpwifi_dbg(adapter, ERROR,
+ "ASSOC_RESP: AUTH timeout\n");
+ } else {
+ ret = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ nxpwifi_dbg(adapter, ERROR,
+ "ASSOC_RESP: UNSPECIFIED failure\n");
+ }
+
+ priv->assoc_rsp_size = 0;
+ } else {
+ ret = status_code;
+ }
+
+ goto done;
+ }
+
+ /* Send a Media Connected event, according to the Spec */
+ priv->media_connected = true;
+
+ adapter->ps_state = PS_STATE_AWAKE;
+ adapter->pps_uapsd_mode = false;
+ adapter->tx_lock_flag = false;
+
+ /* Set the attempted BSSID Index to current */
+ bss_desc = priv->attempted_bss_desc;
+
+ nxpwifi_dbg(adapter, INFO, "info: ASSOC_RESP: %s\n",
+ bss_desc->ssid.ssid);
+
+ /* Make a copy of current BSSID descriptor */
+ memcpy(&priv->curr_bss_params.bss_descriptor,
+ bss_desc, sizeof(struct nxpwifi_bssdescriptor));
+
+ /* Update curr_bss_params */
+ priv->curr_bss_params.bss_descriptor.channel =
+ bss_desc->phy_param_set.ds_param_set.current_chan;
+
+ priv->curr_bss_params.band = (u8)bss_desc->bss_band;
+
+ if (bss_desc->wmm_ie.element_id == WLAN_EID_VENDOR_SPECIFIC)
+ priv->curr_bss_params.wmm_enabled = true;
+ else
+ priv->curr_bss_params.wmm_enabled = false;
+
+ if ((priv->wmm_required || bss_desc->bcn_ht_cap) &&
+ priv->curr_bss_params.wmm_enabled)
+ priv->wmm_enabled = true;
+ else
+ priv->wmm_enabled = false;
+
+ priv->curr_bss_params.wmm_uapsd_enabled = false;
+
+ if (priv->wmm_enabled)
+ priv->curr_bss_params.wmm_uapsd_enabled =
+ ((bss_desc->wmm_ie.qos_info &
+ IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) ? 1 : 0);
+
+ /* Store the bandwidth information from assoc response */
+ ie_ptr = cfg80211_find_ie(WLAN_EID_HT_OPERATION, assoc_rsp->ie_buffer,
+ priv->assoc_rsp_size
+ - sizeof(struct ieee_types_assoc_rsp));
+ if (ie_ptr) {
+ assoc_resp_ht_oper = (struct ieee80211_ht_operation *)(ie_ptr
+ + sizeof(struct element));
+ priv->assoc_resp_ht_param = assoc_resp_ht_oper->ht_param;
+ priv->ht_param_present = true;
+ } else {
+ priv->ht_param_present = false;
+ }
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: ASSOC_RESP: curr_pkt_filter is %#x\n",
+ priv->curr_pkt_filter);
+ if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
+ priv->wpa_is_gtk_set = false;
+
+ if (priv->wmm_enabled) {
+ /* Don't re-enable carrier until we get the WMM_GET_STATUS
+ * event
+ */
+ enable_data = false;
+ } else {
+ /* Since WMM is not enabled, setup the queues with the
+ * defaults
+ */
+ nxpwifi_wmm_setup_queue_priorities(priv, NULL);
+ nxpwifi_wmm_setup_ac_downgrade(priv);
+ }
+
+ if (enable_data)
+ nxpwifi_dbg(adapter, INFO,
+ "info: post association, re-enabling data flow\n");
+
+ /* Reset SNR/NF/RSSI values */
+ priv->data_rssi_last = 0;
+ priv->data_nf_last = 0;
+ priv->data_rssi_avg = 0;
+ priv->data_nf_avg = 0;
+ priv->bcn_rssi_last = 0;
+ priv->bcn_nf_last = 0;
+ priv->bcn_rssi_avg = 0;
+ priv->bcn_nf_avg = 0;
+ priv->rxpd_rate = 0;
+ priv->rxpd_htinfo = 0;
+
+ nxpwifi_save_curr_bcn(priv);
+
+ adapter->dbg.num_cmd_assoc_success++;
+
+ nxpwifi_dbg(adapter, MSG, "assoc: associated with %pM\n",
+ priv->attempted_bss_desc->mac_address);
+
+ /* Add the ra_list here for infra mode as there will be only 1 ra
+ * always
+ */
+ nxpwifi_ralist_add(priv,
+ priv->curr_bss_params.bss_descriptor.mac_address);
+
+ netif_carrier_on(priv->netdev);
+ nxpwifi_wake_up_net_dev_queue(priv->netdev, adapter);
+
+ if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
+ priv->scan_block = true;
+ else
+ priv->port_open = true;
+
+done:
+ /* Need to indicate IOCTL complete */
+ if (adapter->curr_cmd->wait_q_enabled) {
+ if (ret)
+ adapter->cmd_wait_q.status = -1;
+ else
+ adapter->cmd_wait_q.status = 0;
+ }
+
+ return ret;
+}
+
+/* This function associates to a specific BSS discovered in a scan.
+ *
+ * It clears any past association response stored for application
+ * retrieval and calls the command preparation routine to send the
+ * command to firmware.
+ */
+int nxpwifi_associate(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc)
+{
+ /* Return error if the adapter is not STA role or table entry
+ * is not marked as infra.
+ */
+ if ((GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_STA) ||
+ bss_desc->bss_mode != NL80211_IFTYPE_STATION)
+ return -EINVAL;
+
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
+ !bss_desc->disable_11n && !bss_desc->disable_11ac &&
+ priv->config_bands & BAND_AAC)
+ nxpwifi_set_11ac_ba_params(priv);
+ else
+ nxpwifi_set_ba_params(priv);
+
+ /* Clear any past association response stored for application
+ * retrieval
+ */
+ priv->assoc_rsp_size = 0;
+
+ return nxpwifi_send_cmd(priv, HOST_CMD_802_11_ASSOCIATE,
+ HOST_ACT_GEN_SET, 0, bss_desc, true);
+}
+
+/* This function deauthenticates/disconnects from infra network by sending
+ * deauthentication request.
+ */
+static int nxpwifi_deauthenticate_infra(struct nxpwifi_private *priv, u8 *mac)
+{
+ u8 mac_address[ETH_ALEN];
+ int ret;
+
+ if (!mac || is_zero_ether_addr(mac))
+ memcpy(mac_address,
+ priv->curr_bss_params.bss_descriptor.mac_address,
+ ETH_ALEN);
+ else
+ memcpy(mac_address, mac, ETH_ALEN);
+
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_DEAUTHENTICATE,
+ HOST_ACT_GEN_SET, 0, mac_address, true);
+
+ return ret;
+}
+
+/* This function deauthenticates/disconnects from a BSS.
+ *
+ * In case of infra made, it sends deauthentication request, and
+ * in case of ad-hoc mode, a stop network request is sent to the firmware.
+ * In AP mode, a command to stop bss is sent to firmware.
+ */
+int nxpwifi_deauthenticate(struct nxpwifi_private *priv, u8 *mac)
+{
+ int ret = 0;
+
+ if (!priv->media_connected)
+ return 0;
+
+ priv->auth_flag = 0;
+ priv->auth_alg = WLAN_AUTH_NONE;
+ priv->host_mlme_reg = false;
+ priv->mgmt_frame_mask = 0;
+
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_MGMT_FRAME_REG,
+ HOST_ACT_GEN_SET, 0,
+ &priv->mgmt_frame_mask, false);
+ if (ret) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "could not unregister mgmt frame rx\n");
+ return ret;
+ }
+
+ switch (priv->bss_mode) {
+ case NL80211_IFTYPE_STATION:
+ ret = nxpwifi_deauthenticate_infra(priv, mac);
+ if (ret)
+ cfg80211_disconnected(priv->netdev, 0, NULL, 0,
+ true, GFP_KERNEL);
+ break;
+ case NL80211_IFTYPE_AP:
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_STOP,
+ HOST_ACT_GEN_SET, 0, NULL, true);
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* This function deauthenticates/disconnects from all BSS. */
+void nxpwifi_deauthenticate_all(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_private *priv;
+ int i;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ nxpwifi_deauthenticate(priv, NULL);
+ }
+}
+EXPORT_SYMBOL_GPL(nxpwifi_deauthenticate_all);
+
+/* This function converts band to radio type used in channel TLV.
+ */
+u8 nxpwifi_band_to_radio_type(u16 config_bands)
+{
+ if (config_bands & BAND_A || config_bands & BAND_AN ||
+ config_bands & BAND_AAC || config_bands & BAND_AAX)
+ return HOST_SCAN_RADIO_TYPE_A;
+
+ return HOST_SCAN_RADIO_TYPE_BG;
+}
--
2.34.1
next prev parent reply other threads:[~2025-11-17 11:02 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-17 11:00 [PATCH v7 00/22] wifi: nxpwifi: create nxpwifi to support iw61x Jeff Chen
2025-11-17 11:00 ` [PATCH v7 01/22] wifi: nxpwifi: Add 802.11n support for client and AP modes Jeff Chen
2025-11-17 11:00 ` [PATCH v7 02/22] wifi: nxpwifi: add initial support for 802.11ac Jeff Chen
2025-11-17 11:00 ` [PATCH v7 03/22] wifi: nxpwifi: add initial support for 802.11ax Jeff Chen
2025-11-17 11:00 ` [PATCH v7 04/22] wifi: nxpwifi: add support for 802.11h (DFS and TPC) Jeff Chen
2025-11-17 11:00 ` [PATCH v7 05/22] wifi: nxpwifi: add support for WMM Jeff Chen
2025-11-17 11:00 ` [PATCH v7 06/22] wifi: nxpwifi: add scan support Jeff Chen
2025-11-17 11:00 ` Jeff Chen [this message]
2025-11-17 11:00 ` [PATCH v7 08/22] wifi: nxpwifi: add channel/frequency/power (cfp) support Jeff Chen
2025-11-17 11:00 ` [PATCH v7 09/22] wifi: nxpwifi: add configuration support Jeff Chen
2025-11-17 11:00 ` [PATCH v7 10/22] wifi: nxpwifi: implement cfg80211 ops for STA and AP modes Jeff Chen
2025-11-17 11:00 ` [PATCH v7 11/22] wifi: nxpwifi: add firmware command and TLV definitions Jeff Chen
2025-11-17 11:00 ` [PATCH v7 12/22] wifi: nxpwifi: introduce command and event handling infrastructure Jeff Chen
2025-11-25 15:04 ` [PATCH v7 12/22] wifi: nxpwifi: introduce command and event handling infrastructure# Sascha Hauer
2025-11-17 11:00 ` [PATCH v7 13/22] wifi: nxpwifi: add data path support for STA and AP modes Jeff Chen
2025-11-17 11:00 ` [PATCH v7 14/22] wifi: nxpwifi: add debugfs support for diagnostics and testing Jeff Chen
2025-11-17 11:00 ` [PATCH v7 15/22] wifi: nxpwifi: add ethtool support for Wake-on-LAN Jeff Chen
2025-11-17 11:00 ` [PATCH v7 16/22] wifi: nxpwifi: add utility and IE handling support Jeff Chen
2025-11-17 11:00 ` [PATCH v7 17/22] wifi: nxpwifi: add driver initialization and shutdown support Jeff Chen
2025-11-17 11:00 ` [PATCH v7 18/22] wifi: nxpwifi: add core driver implementation Jeff Chen
2025-11-20 15:47 ` Abdun Nihaal
2025-12-05 8:33 ` jeff.chen_1
2025-11-17 11:00 ` [PATCH v7 19/22] wifi: nxpwifi: add initial SDIO bus driver support Jeff Chen
2025-11-17 11:00 ` [PATCH v7 20/22] wifi: nxpwifi: add NXP vendor and IW61x device IDs to sdio_ids.h Jeff Chen
2025-11-17 11:00 ` [PATCH v7 21/22] wifi: nxpwifi: add Kconfig and Makefile for kernel integration Jeff Chen
2025-11-17 11:00 ` [PATCH v7 22/22] wifi: nxpwifi: add MAINTAINERS entry for nxpwifi driver Jeff Chen
2025-11-17 19:10 ` [PATCH v7 00/22] wifi: nxpwifi: create nxpwifi to support iw61x Jeff Johnson
2025-11-20 9:01 ` Johannes Berg
2025-12-05 8:23 ` Jeff Chen
2025-12-05 8:12 ` Jeff Chen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251117110046.2810811-8-jeff.chen_1@nxp.com \
--to=jeff.chen_1@nxp.com \
--cc=briannorris@chromium.org \
--cc=francesco@dolcini.it \
--cc=johannes@sipsolutions.net \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-wireless@vger.kernel.org \
--cc=s.hauer@pengutronix.de \
--cc=tsung-hsien.hsieh@nxp.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.