new file mode 100644
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: management IE handling- setting and
+ * deleting IE.
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "main.h"
+#include "cmdevt.h"
+
+/* This function checks if current IE index is used by any on other interface.
+ * Return: true: yes, current IE index is used by someone else.
+ * false: no, current IE index is NOT used by other interface.
+ */
+static bool
+nxpwifi_ie_index_used_by_other_intf(struct nxpwifi_private *priv, u16 idx)
+{
+ int i;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_ie *ie;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i] != priv) {
+ ie = &adapter->priv[i]->mgmt_ie[idx];
+ if (ie->mgmt_subtype_mask && ie->ie_length)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Get unused IE index. This index will be used for setting new IE */
+static int
+nxpwifi_ie_get_autoidx(struct nxpwifi_private *priv, u16 subtype_mask,
+ struct nxpwifi_ie *ie, u16 *index)
+{
+ u16 mask, len, i;
+
+ for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) {
+ mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask);
+ len = le16_to_cpu(ie->ie_length);
+
+ if (mask == NXPWIFI_AUTO_IDX_MASK)
+ continue;
+
+ if (mask == subtype_mask) {
+ if (len > IEEE_MAX_IE_SIZE)
+ continue;
+
+ *index = i;
+ return 0;
+ }
+
+ if (!priv->mgmt_ie[i].ie_length) {
+ if (nxpwifi_ie_index_used_by_other_intf(priv, i))
+ continue;
+
+ *index = i;
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+/* This function prepares IE data buffer for command to be sent to FW */
+static int
+nxpwifi_update_autoindex_ies(struct nxpwifi_private *priv,
+ struct nxpwifi_ie_list *ie_list)
+{
+ u16 travel_len, index, mask;
+ s16 input_len, tlv_len;
+ struct nxpwifi_ie *ie;
+ u8 *tmp;
+
+ input_len = le16_to_cpu(ie_list->len);
+ travel_len = sizeof(struct nxpwifi_ie_types_header);
+
+ ie_list->len = 0;
+
+ while (input_len >= sizeof(struct nxpwifi_ie_types_header)) {
+ ie = (struct nxpwifi_ie *)(((u8 *)ie_list) + travel_len);
+ tlv_len = le16_to_cpu(ie->ie_length);
+ travel_len += tlv_len + NXPWIFI_IE_HDR_SIZE;
+
+ if (input_len < tlv_len + NXPWIFI_IE_HDR_SIZE)
+ return -EINVAL;
+ index = le16_to_cpu(ie->ie_index);
+ mask = le16_to_cpu(ie->mgmt_subtype_mask);
+
+ if (index == NXPWIFI_AUTO_IDX_MASK) {
+ /* automatic addition */
+ if (nxpwifi_ie_get_autoidx(priv, mask, ie, &index))
+ return -ENOENT;
+ if (index == NXPWIFI_AUTO_IDX_MASK)
+ return -EINVAL;
+
+ tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer;
+ memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length));
+ priv->mgmt_ie[index].ie_length = ie->ie_length;
+ priv->mgmt_ie[index].ie_index = cpu_to_le16(index);
+ priv->mgmt_ie[index].mgmt_subtype_mask =
+ cpu_to_le16(mask);
+
+ ie->ie_index = cpu_to_le16(index);
+ } else {
+ if (mask != NXPWIFI_DELETE_MASK)
+ return -EINVAL;
+ /* Check if this index is being used on any
+ * other interface.
+ */
+ if (nxpwifi_ie_index_used_by_other_intf(priv, index))
+ return -EPERM;
+
+ ie->ie_length = 0;
+ memcpy(&priv->mgmt_ie[index], ie,
+ sizeof(struct nxpwifi_ie));
+ }
+
+ le16_unaligned_add_cpu
+ (&ie_list->len,
+ le16_to_cpu(priv->mgmt_ie[index].ie_length) +
+ NXPWIFI_IE_HDR_SIZE);
+ input_len -= tlv_len + NXPWIFI_IE_HDR_SIZE;
+ }
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP)
+ return nxpwifi_send_cmd(priv, HOST_CMD_UAP_SYS_CONFIG,
+ HOST_ACT_GEN_SET,
+ UAP_CUSTOM_IE_I, ie_list, true);
+
+ return 0;
+}
+
+/* Copy individual custom IEs for beacon, probe response and assoc response
+ * and prepare single structure for IE setting.
+ * This function also updates allocated IE indices from driver.
+ */
+static int
+nxpwifi_update_uap_custom_ie(struct nxpwifi_private *priv,
+ struct nxpwifi_ie *beacon_ie, u16 *beacon_idx,
+ struct nxpwifi_ie *pr_ie, u16 *probe_idx,
+ struct nxpwifi_ie *ar_ie, u16 *assoc_idx)
+{
+ struct nxpwifi_ie_list *ap_custom_ie;
+ u8 *pos;
+ u16 len;
+ int ret;
+
+ ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL);
+ if (!ap_custom_ie)
+ return -ENOMEM;
+
+ ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
+ pos = (u8 *)ap_custom_ie->ie_list;
+
+ if (beacon_ie) {
+ len = sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE +
+ le16_to_cpu(beacon_ie->ie_length);
+ memcpy(pos, beacon_ie, len);
+ pos += len;
+ le16_unaligned_add_cpu(&ap_custom_ie->len, len);
+ }
+ if (pr_ie) {
+ len = sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE +
+ le16_to_cpu(pr_ie->ie_length);
+ memcpy(pos, pr_ie, len);
+ pos += len;
+ le16_unaligned_add_cpu(&ap_custom_ie->len, len);
+ }
+ if (ar_ie) {
+ len = sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE +
+ le16_to_cpu(ar_ie->ie_length);
+ memcpy(pos, ar_ie, len);
+ pos += len;
+ le16_unaligned_add_cpu(&ap_custom_ie->len, len);
+ }
+
+ ret = nxpwifi_update_autoindex_ies(priv, ap_custom_ie);
+
+ pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index);
+ if (beacon_ie && *beacon_idx == NXPWIFI_AUTO_IDX_MASK) {
+ /* save beacon ie index after auto-indexing */
+ *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index);
+ len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE +
+ le16_to_cpu(beacon_ie->ie_length);
+ pos += len;
+ }
+ if (pr_ie && le16_to_cpu(pr_ie->ie_index) == NXPWIFI_AUTO_IDX_MASK) {
+ /* save probe resp ie index after auto-indexing */
+ *probe_idx = *((u16 *)pos);
+ len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE +
+ le16_to_cpu(pr_ie->ie_length);
+ pos += len;
+ }
+ if (ar_ie && le16_to_cpu(ar_ie->ie_index) == NXPWIFI_AUTO_IDX_MASK)
+ /* save assoc resp ie index after auto-indexing */
+ *assoc_idx = *((u16 *)pos);
+
+ kfree(ap_custom_ie);
+ return ret;
+}
+
+/* This function checks if the vendor specified IE is present in passed buffer
+ * and copies it to nxpwifi_ie structure.
+ * Function takes pointer to struct nxpwifi_ie pointer as argument.
+ * If the vendor specified IE is present then memory is allocated for
+ * nxpwifi_ie pointer and filled in with IE. Caller should take care of freeing
+ * this memory.
+ */
+static int nxpwifi_update_vs_ie(const u8 *ies, int ies_len,
+ struct nxpwifi_ie **ie_ptr, u16 mask,
+ unsigned int oui, u8 oui_type)
+{
+ struct element *vs_ie;
+ struct nxpwifi_ie *ie = *ie_ptr;
+ const u8 *vendor_ie;
+
+ vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len);
+ if (vendor_ie) {
+ if (!*ie_ptr) {
+ *ie_ptr = kzalloc(sizeof(*ie_ptr), GFP_KERNEL);
+ if (!*ie_ptr)
+ return -ENOMEM;
+ ie = *ie_ptr;
+ }
+
+ vs_ie = (struct element *)vendor_ie;
+ if (le16_to_cpu(ie->ie_length) + vs_ie->datalen + 2 >
+ IEEE_MAX_IE_SIZE)
+ return -EINVAL;
+ memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length),
+ vs_ie, vs_ie->datalen + 2);
+ le16_unaligned_add_cpu(&ie->ie_length, vs_ie->datalen + 2);
+ ie->mgmt_subtype_mask = cpu_to_le16(mask);
+ ie->ie_index = cpu_to_le16(NXPWIFI_AUTO_IDX_MASK);
+ }
+
+ *ie_ptr = ie;
+ return 0;
+}
+
+/* This function parses beacon IEs, probe response IEs, association response IEs
+ * from cfg80211_ap_settings->beacon and sets these IE to FW.
+ */
+static int nxpwifi_set_mgmt_beacon_data_ies(struct nxpwifi_private *priv,
+ struct cfg80211_beacon_data *data)
+{
+ struct nxpwifi_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL;
+ u16 beacon_idx = NXPWIFI_AUTO_IDX_MASK, pr_idx = NXPWIFI_AUTO_IDX_MASK;
+ u16 ar_idx = NXPWIFI_AUTO_IDX_MASK;
+ int ret = 0;
+
+ if (data->beacon_ies && data->beacon_ies_len) {
+ nxpwifi_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
+ &beacon_ie, MGMT_MASK_BEACON,
+ WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS);
+ nxpwifi_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
+ &beacon_ie, MGMT_MASK_BEACON,
+ WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
+ }
+
+ if (data->proberesp_ies && data->proberesp_ies_len) {
+ nxpwifi_update_vs_ie(data->proberesp_ies,
+ data->proberesp_ies_len, &pr_ie,
+ MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS);
+ nxpwifi_update_vs_ie(data->proberesp_ies,
+ data->proberesp_ies_len, &pr_ie,
+ MGMT_MASK_PROBE_RESP,
+ WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
+ }
+
+ if (data->assocresp_ies && data->assocresp_ies_len) {
+ nxpwifi_update_vs_ie(data->assocresp_ies,
+ data->assocresp_ies_len, &ar_ie,
+ MGMT_MASK_ASSOC_RESP |
+ MGMT_MASK_REASSOC_RESP,
+ WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPS);
+ nxpwifi_update_vs_ie(data->assocresp_ies,
+ data->assocresp_ies_len, &ar_ie,
+ MGMT_MASK_ASSOC_RESP |
+ MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA,
+ WLAN_OUI_TYPE_WFA_P2P);
+ }
+
+ if (beacon_ie || pr_ie || ar_ie) {
+ ret = nxpwifi_update_uap_custom_ie(priv, beacon_ie,
+ &beacon_idx, pr_ie,
+ &pr_idx, ar_ie, &ar_idx);
+ if (ret)
+ goto done;
+ }
+
+ priv->beacon_idx = beacon_idx;
+ priv->proberesp_idx = pr_idx;
+ priv->assocresp_idx = ar_idx;
+
+done:
+ kfree(beacon_ie);
+ kfree(pr_ie);
+ kfree(ar_ie);
+
+ return ret;
+}
+
+/* This function parses head and tail IEs, from cfg80211_beacon_data and sets
+ * these IE to FW.
+ */
+static int nxpwifi_uap_parse_tail_ies(struct nxpwifi_private *priv,
+ struct cfg80211_beacon_data *info)
+{
+ struct nxpwifi_ie *gen_ie;
+ struct element *hdr;
+ struct ieee80211_vendor_ie *vendorhdr;
+ u16 gen_idx = NXPWIFI_AUTO_IDX_MASK, ie_len = 0;
+ int left_len, parsed_len = 0;
+ unsigned int token_len;
+ int ret = 0;
+
+ if (!info->tail || !info->tail_len)
+ return 0;
+
+ gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL);
+ if (!gen_ie)
+ return -ENOMEM;
+
+ left_len = info->tail_len;
+
+ /* Many IEs are generated in FW by parsing bss configuration.
+ * Let's not add them here; else we may end up duplicating these IEs
+ */
+ while (left_len > sizeof(struct element)) {
+ hdr = (void *)(info->tail + parsed_len);
+ token_len = hdr->datalen + sizeof(struct element);
+ if (token_len > left_len) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ switch (hdr->id) {
+ case WLAN_EID_SSID:
+ case WLAN_EID_SUPP_RATES:
+ case WLAN_EID_COUNTRY:
+ case WLAN_EID_PWR_CONSTRAINT:
+ case WLAN_EID_ERP_INFO:
+ case WLAN_EID_EXT_SUPP_RATES:
+ case WLAN_EID_HT_CAPABILITY:
+ case WLAN_EID_HT_OPERATION:
+ case WLAN_EID_VHT_CAPABILITY:
+ case WLAN_EID_VHT_OPERATION:
+ break;
+ case WLAN_EID_VENDOR_SPECIFIC:
+ /* Skip only Microsoft WMM IE */
+ if (cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WMM,
+ (const u8 *)hdr,
+ token_len))
+ break;
+ fallthrough;
+ default:
+ if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
+ ret = -EINVAL;
+ goto done;
+ }
+ memcpy(gen_ie->ie_buffer + ie_len, hdr, token_len);
+ ie_len += token_len;
+ break;
+ }
+ left_len -= token_len;
+ parsed_len += token_len;
+ }
+
+ /* parse only WPA vendor IE from tail, WMM IE is configured by
+ * bss_config command
+ */
+ vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WPA,
+ info->tail, info->tail_len);
+ if (vendorhdr) {
+ token_len = vendorhdr->len + sizeof(struct element);
+ if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
+ ret = -EINVAL;
+ goto done;
+ }
+ memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, token_len);
+ ie_len += token_len;
+ }
+
+ if (!ie_len)
+ goto done;
+
+ gen_ie->ie_index = cpu_to_le16(gen_idx);
+ gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
+ MGMT_MASK_PROBE_RESP |
+ MGMT_MASK_ASSOC_RESP);
+ gen_ie->ie_length = cpu_to_le16(ie_len);
+
+ ret = nxpwifi_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL,
+ NULL, NULL, NULL);
+
+ if (ret)
+ goto done;
+
+ priv->gen_idx = gen_idx;
+
+ done:
+ kfree(gen_ie);
+ return ret;
+}
+
+/* This function parses different IEs-head & tail IEs, beacon IEs,
+ * probe response IEs, association response IEs from cfg80211_ap_settings
+ * function and sets these IE to FW.
+ */
+int nxpwifi_set_mgmt_ies(struct nxpwifi_private *priv,
+ struct cfg80211_beacon_data *info)
+{
+ int ret;
+
+ ret = nxpwifi_uap_parse_tail_ies(priv, info);
+
+ if (ret)
+ return ret;
+
+ return nxpwifi_set_mgmt_beacon_data_ies(priv, info);
+}
+
+/* This function removes management IE set */
+int nxpwifi_del_mgmt_ies(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_ie *beacon_ie = NULL, *pr_ie = NULL;
+ struct nxpwifi_ie *ar_ie = NULL, *gen_ie = NULL;
+ int ret = 0;
+
+ if (priv->gen_idx != NXPWIFI_AUTO_IDX_MASK) {
+ gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL);
+ if (!gen_ie)
+ return -ENOMEM;
+
+ gen_ie->ie_index = cpu_to_le16(priv->gen_idx);
+ gen_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK);
+ gen_ie->ie_length = 0;
+ ret = nxpwifi_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx,
+ NULL, &priv->proberesp_idx,
+ NULL, &priv->assocresp_idx);
+ if (ret)
+ goto done;
+
+ priv->gen_idx = NXPWIFI_AUTO_IDX_MASK;
+ }
+
+ if (priv->beacon_idx != NXPWIFI_AUTO_IDX_MASK) {
+ beacon_ie = kmalloc(sizeof(*beacon_ie), GFP_KERNEL);
+ if (!beacon_ie) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx);
+ beacon_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK);
+ beacon_ie->ie_length = 0;
+ }
+ if (priv->proberesp_idx != NXPWIFI_AUTO_IDX_MASK) {
+ pr_ie = kmalloc(sizeof(*pr_ie), GFP_KERNEL);
+ if (!pr_ie) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx);
+ pr_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK);
+ pr_ie->ie_length = 0;
+ }
+ if (priv->assocresp_idx != NXPWIFI_AUTO_IDX_MASK) {
+ ar_ie = kmalloc(sizeof(*ar_ie), GFP_KERNEL);
+ if (!ar_ie) {
+ ret = -ENOMEM;
+ goto done;
+ }
+ ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx);
+ ar_ie->mgmt_subtype_mask = cpu_to_le16(NXPWIFI_DELETE_MASK);
+ ar_ie->ie_length = 0;
+ }
+
+ if (beacon_ie || pr_ie || ar_ie)
+ ret = nxpwifi_update_uap_custom_ie(priv,
+ beacon_ie, &priv->beacon_idx,
+ pr_ie, &priv->proberesp_idx,
+ ar_ie, &priv->assocresp_idx);
+
+done:
+ kfree(gen_ie);
+ kfree(beacon_ie);
+ kfree(pr_ie);
+ kfree(ar_ie);
+
+ return ret;
+}
new file mode 100644
@@ -0,0 +1,999 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: utility functions
+ *
+ * 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"
+
+static struct nxpwifi_debug_data items[] = {
+ {"debug_mask", item_size(debug_mask),
+ item_addr(debug_mask), 1},
+ {"int_counter", item_size(int_counter),
+ item_addr(int_counter), 1},
+ {"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]),
+ item_addr(packets_out[WMM_AC_VO]), 1},
+ {"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]),
+ item_addr(packets_out[WMM_AC_VI]), 1},
+ {"wmm_ac_be", item_size(packets_out[WMM_AC_BE]),
+ item_addr(packets_out[WMM_AC_BE]), 1},
+ {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]),
+ item_addr(packets_out[WMM_AC_BK]), 1},
+ {"tx_buf_size", item_size(tx_buf_size),
+ item_addr(tx_buf_size), 1},
+ {"curr_tx_buf_size", item_size(curr_tx_buf_size),
+ item_addr(curr_tx_buf_size), 1},
+ {"ps_mode", item_size(ps_mode),
+ item_addr(ps_mode), 1},
+ {"ps_state", item_size(ps_state),
+ item_addr(ps_state), 1},
+ {"is_deep_sleep", item_size(is_deep_sleep),
+ item_addr(is_deep_sleep), 1},
+ {"wakeup_dev_req", item_size(pm_wakeup_card_req),
+ item_addr(pm_wakeup_card_req), 1},
+ {"wakeup_tries", item_size(pm_wakeup_fw_try),
+ item_addr(pm_wakeup_fw_try), 1},
+ {"hs_configured", item_size(is_hs_configured),
+ item_addr(is_hs_configured), 1},
+ {"hs_activated", item_size(hs_activated),
+ item_addr(hs_activated), 1},
+ {"num_tx_timeout", item_size(num_tx_timeout),
+ item_addr(num_tx_timeout), 1},
+ {"is_cmd_timedout", item_size(is_cmd_timedout),
+ item_addr(is_cmd_timedout), 1},
+ {"timeout_cmd_id", item_size(timeout_cmd_id),
+ item_addr(timeout_cmd_id), 1},
+ {"timeout_cmd_act", item_size(timeout_cmd_act),
+ item_addr(timeout_cmd_act), 1},
+ {"last_cmd_id", item_size(last_cmd_id),
+ item_addr(last_cmd_id), DBG_CMD_NUM},
+ {"last_cmd_act", item_size(last_cmd_act),
+ item_addr(last_cmd_act), DBG_CMD_NUM},
+ {"last_cmd_index", item_size(last_cmd_index),
+ item_addr(last_cmd_index), 1},
+ {"last_cmd_resp_id", item_size(last_cmd_resp_id),
+ item_addr(last_cmd_resp_id), DBG_CMD_NUM},
+ {"last_cmd_resp_index", item_size(last_cmd_resp_index),
+ item_addr(last_cmd_resp_index), 1},
+ {"last_event", item_size(last_event),
+ item_addr(last_event), DBG_CMD_NUM},
+ {"last_event_index", item_size(last_event_index),
+ item_addr(last_event_index), 1},
+ {"last_mp_wr_bitmap", item_size(last_mp_wr_bitmap),
+ item_addr(last_mp_wr_bitmap), NXPWIFI_DBG_SDIO_MP_NUM},
+ {"last_mp_wr_ports", item_size(last_mp_wr_ports),
+ item_addr(last_mp_wr_ports), NXPWIFI_DBG_SDIO_MP_NUM},
+ {"last_mp_wr_len", item_size(last_mp_wr_len),
+ item_addr(last_mp_wr_len), NXPWIFI_DBG_SDIO_MP_NUM},
+ {"last_mp_curr_wr_port", item_size(last_mp_curr_wr_port),
+ item_addr(last_mp_curr_wr_port), NXPWIFI_DBG_SDIO_MP_NUM},
+ {"last_sdio_mp_index", item_size(last_sdio_mp_index),
+ item_addr(last_sdio_mp_index), 1},
+ {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure),
+ item_addr(num_cmd_host_to_card_failure), 1},
+ {"num_cmd_sleep_cfm_fail",
+ item_size(num_cmd_sleep_cfm_host_to_card_failure),
+ item_addr(num_cmd_sleep_cfm_host_to_card_failure), 1},
+ {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure),
+ item_addr(num_tx_host_to_card_failure), 1},
+ {"num_evt_deauth", item_size(num_event_deauth),
+ item_addr(num_event_deauth), 1},
+ {"num_evt_disassoc", item_size(num_event_disassoc),
+ item_addr(num_event_disassoc), 1},
+ {"num_evt_link_lost", item_size(num_event_link_lost),
+ item_addr(num_event_link_lost), 1},
+ {"num_cmd_deauth", item_size(num_cmd_deauth),
+ item_addr(num_cmd_deauth), 1},
+ {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success),
+ item_addr(num_cmd_assoc_success), 1},
+ {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure),
+ item_addr(num_cmd_assoc_failure), 1},
+ {"cmd_sent", item_size(cmd_sent),
+ item_addr(cmd_sent), 1},
+ {"data_sent", item_size(data_sent),
+ item_addr(data_sent), 1},
+ {"cmd_resp_received", item_size(cmd_resp_received),
+ item_addr(cmd_resp_received), 1},
+ {"event_received", item_size(event_received),
+ item_addr(event_received), 1},
+
+ /* variables defined in struct nxpwifi_adapter */
+ {"cmd_pending", adapter_item_size(cmd_pending),
+ adapter_item_addr(cmd_pending), 1},
+ {"tx_pending", adapter_item_size(tx_pending),
+ adapter_item_addr(tx_pending), 1},
+};
+
+static int num_of_items = ARRAY_SIZE(items);
+
+/* Firmware initialization complete callback handler.
+ *
+ * This function wakes up the function waiting on the init
+ * wait queue for the firmware initialization to complete.
+ */
+void nxpwifi_init_fw_complete(struct nxpwifi_adapter *adapter)
+{
+ adapter->init_wait_q_woken = true;
+ wake_up_interruptible(&adapter->init_wait_q);
+}
+
+/* This function sends init/shutdown command
+ * to firmware.
+ */
+int nxpwifi_init_shutdown_fw(struct nxpwifi_private *priv,
+ u32 func_init_shutdown)
+{
+ u16 cmd;
+
+ if (func_init_shutdown == NXPWIFI_FUNC_INIT) {
+ cmd = HOST_CMD_FUNC_INIT;
+ } else if (func_init_shutdown == NXPWIFI_FUNC_SHUTDOWN) {
+ cmd = HOST_CMD_FUNC_SHUTDOWN;
+ } else {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "unsupported parameter\n");
+ return -EINVAL;
+ }
+
+ return nxpwifi_send_cmd(priv, cmd, HOST_ACT_GEN_SET, 0, NULL, true);
+}
+EXPORT_SYMBOL_GPL(nxpwifi_init_shutdown_fw);
+
+/* IOCTL request handler to set/get debug information.
+ *
+ * This function collates/sets the information from/to different driver
+ * structures.
+ */
+int nxpwifi_get_debug_info(struct nxpwifi_private *priv,
+ struct nxpwifi_debug_info *info)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (info) {
+ info->debug_mask = adapter->debug_mask;
+ memcpy(info->packets_out,
+ priv->wmm.packets_out,
+ sizeof(priv->wmm.packets_out));
+ info->curr_tx_buf_size = (u32)adapter->curr_tx_buf_size;
+ info->tx_buf_size = (u32)adapter->tx_buf_size;
+ info->rx_tbl_num = nxpwifi_get_rx_reorder_tbl(priv,
+ info->rx_tbl);
+ info->tx_tbl_num = nxpwifi_get_tx_ba_stream_tbl(priv,
+ info->tx_tbl);
+ info->ps_mode = adapter->ps_mode;
+ info->ps_state = adapter->ps_state;
+ info->is_deep_sleep = adapter->is_deep_sleep;
+ info->pm_wakeup_card_req = adapter->pm_wakeup_card_req;
+ info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try;
+ info->is_hs_configured = test_bit(NXPWIFI_IS_HS_CONFIGURED,
+ &adapter->work_flags);
+ info->hs_activated = adapter->hs_activated;
+ info->is_cmd_timedout = test_bit(NXPWIFI_IS_CMD_TIMEDOUT,
+ &adapter->work_flags);
+ info->num_cmd_host_to_card_failure =
+ adapter->dbg.num_cmd_host_to_card_failure;
+ info->num_cmd_sleep_cfm_host_to_card_failure =
+ adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure;
+ info->num_tx_host_to_card_failure =
+ adapter->dbg.num_tx_host_to_card_failure;
+ info->num_event_deauth = adapter->dbg.num_event_deauth;
+ info->num_event_disassoc = adapter->dbg.num_event_disassoc;
+ info->num_event_link_lost = adapter->dbg.num_event_link_lost;
+ info->num_cmd_deauth = adapter->dbg.num_cmd_deauth;
+ info->num_cmd_assoc_success =
+ adapter->dbg.num_cmd_assoc_success;
+ info->num_cmd_assoc_failure =
+ adapter->dbg.num_cmd_assoc_failure;
+ info->num_tx_timeout = adapter->dbg.num_tx_timeout;
+ info->timeout_cmd_id = adapter->dbg.timeout_cmd_id;
+ info->timeout_cmd_act = adapter->dbg.timeout_cmd_act;
+ memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id,
+ sizeof(adapter->dbg.last_cmd_id));
+ memcpy(info->last_cmd_act, adapter->dbg.last_cmd_act,
+ sizeof(adapter->dbg.last_cmd_act));
+ info->last_cmd_index = adapter->dbg.last_cmd_index;
+ memcpy(info->last_cmd_resp_id, adapter->dbg.last_cmd_resp_id,
+ sizeof(adapter->dbg.last_cmd_resp_id));
+ info->last_cmd_resp_index = adapter->dbg.last_cmd_resp_index;
+ memcpy(info->last_event, adapter->dbg.last_event,
+ sizeof(adapter->dbg.last_event));
+ info->last_event_index = adapter->dbg.last_event_index;
+ memcpy(info->last_mp_wr_bitmap, adapter->dbg.last_mp_wr_bitmap,
+ sizeof(adapter->dbg.last_mp_wr_bitmap));
+ memcpy(info->last_mp_wr_ports, adapter->dbg.last_mp_wr_ports,
+ sizeof(adapter->dbg.last_mp_wr_ports));
+ memcpy(info->last_mp_curr_wr_port,
+ adapter->dbg.last_mp_curr_wr_port,
+ sizeof(adapter->dbg.last_mp_curr_wr_port));
+ memcpy(info->last_mp_wr_len, adapter->dbg.last_mp_wr_len,
+ sizeof(adapter->dbg.last_mp_wr_len));
+ info->last_sdio_mp_index = adapter->dbg.last_sdio_mp_index;
+ info->data_sent = adapter->data_sent;
+ info->cmd_sent = adapter->cmd_sent;
+ info->cmd_resp_received = adapter->cmd_resp_received;
+ }
+
+ return 0;
+}
+
+int nxpwifi_debug_info_to_buffer(struct nxpwifi_private *priv, char *buf,
+ struct nxpwifi_debug_info *info)
+{
+ char *p = buf;
+ struct nxpwifi_debug_data *d = &items[0];
+ size_t size, addr;
+ long val;
+ int i, j;
+
+ if (!info)
+ return 0;
+
+ for (i = 0; i < num_of_items; i++) {
+ p += sprintf(p, "%s=", d[i].name);
+
+ size = d[i].size / d[i].num;
+
+ if (i < (num_of_items - 3))
+ addr = d[i].addr + (size_t)info;
+ else /* The last 3 items are struct nxpwifi_adapter variables */
+ addr = d[i].addr + (size_t)priv->adapter;
+
+ for (j = 0; j < d[i].num; j++) {
+ switch (size) {
+ case 1:
+ val = *((u8 *)addr);
+ break;
+ case 2:
+ val = get_unaligned((u16 *)addr);
+ break;
+ case 4:
+ val = get_unaligned((u32 *)addr);
+ break;
+ case 8:
+ val = get_unaligned((long long *)addr);
+ break;
+ default:
+ val = -1;
+ break;
+ }
+
+ p += sprintf(p, "%#lx ", val);
+ addr += size;
+ }
+
+ p += sprintf(p, "\n");
+ }
+
+ if (info->tx_tbl_num) {
+ p += sprintf(p, "Tx BA stream table:\n");
+ for (i = 0; i < info->tx_tbl_num; i++)
+ p += sprintf(p, "tid = %d, ra = %pM\n",
+ info->tx_tbl[i].tid, info->tx_tbl[i].ra);
+ }
+
+ if (info->rx_tbl_num) {
+ p += sprintf(p, "Rx reorder table:\n");
+ for (i = 0; i < info->rx_tbl_num; i++) {
+ p += sprintf(p, "tid = %d, ta = %pM, ",
+ info->rx_tbl[i].tid,
+ info->rx_tbl[i].ta);
+ p += sprintf(p, "start_win = %d, ",
+ info->rx_tbl[i].start_win);
+ p += sprintf(p, "win_size = %d, buffer: ",
+ info->rx_tbl[i].win_size);
+
+ for (j = 0; j < info->rx_tbl[i].win_size; j++)
+ p += sprintf(p, "%c ",
+ info->rx_tbl[i].buffer[j] ?
+ '1' : '0');
+
+ p += sprintf(p, "\n");
+ }
+ }
+
+ return p - buf;
+}
+
+bool nxpwifi_is_channel_setting_allowable(struct nxpwifi_private *priv,
+ struct ieee80211_channel *check_chan)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int i;
+ struct nxpwifi_private *tmp_priv;
+ u8 bss_role = GET_BSS_ROLE(priv);
+ struct ieee80211_channel *set_chan;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ tmp_priv = adapter->priv[i];
+ if (tmp_priv == priv)
+ continue;
+
+ set_chan = NULL;
+ if (bss_role == NXPWIFI_BSS_ROLE_STA) {
+ if (GET_BSS_ROLE(tmp_priv) == NXPWIFI_BSS_ROLE_UAP &&
+ netif_carrier_ok(tmp_priv->netdev) &&
+ cfg80211_chandef_valid(&tmp_priv->bss_chandef))
+ set_chan = tmp_priv->bss_chandef.chan;
+ } else if (bss_role == NXPWIFI_BSS_ROLE_UAP) {
+ struct nxpwifi_current_bss_params *bss_params =
+ &tmp_priv->curr_bss_params;
+ int channel = bss_params->bss_descriptor.channel;
+ enum nl80211_band band =
+ nxpwifi_band_to_radio_type(bss_params->band);
+ int freq =
+ ieee80211_channel_to_frequency(channel, band);
+
+ if (GET_BSS_ROLE(tmp_priv) == NXPWIFI_BSS_ROLE_STA &&
+ tmp_priv->media_connected)
+ set_chan = ieee80211_get_channel(adapter->wiphy, freq);
+ }
+
+ if (set_chan && !ieee80211_channel_equal(check_chan, set_chan)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "AP/STA must run on the same channel\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int
+nxpwifi_parse_mgmt_packet(struct nxpwifi_private *priv, u8 *payload, u16 len,
+ struct rxpd *rx_pd)
+{
+ u16 stype;
+ u8 category;
+ struct ieee80211_hdr *ieee_hdr = (void *)payload;
+
+ stype = (le16_to_cpu(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE);
+
+ switch (stype) {
+ case IEEE80211_STYPE_ACTION:
+ category = *(payload + sizeof(struct ieee80211_hdr));
+ switch (category) {
+ case WLAN_CATEGORY_BACK:
+ /*we dont indicate BACK action frames to cfg80211*/
+ nxpwifi_dbg(priv->adapter, INFO,
+ "drop BACK action frames");
+ return -EINVAL;
+ default:
+ nxpwifi_dbg(priv->adapter, INFO,
+ "unknown public action frame category %d\n",
+ category);
+ }
+ break;
+ default:
+ nxpwifi_dbg(priv->adapter, INFO,
+ "unknown mgmt frame subtype %#x\n", stype);
+ return 0;
+ }
+
+ return 0;
+}
+
+/* This function sends deauth packet to the kernel. */
+void nxpwifi_host_mlme_disconnect(struct nxpwifi_private *priv,
+ u16 reason_code, u8 *sa)
+{
+ u8 frame_buf[100];
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)frame_buf;
+
+ memset(frame_buf, 0, sizeof(frame_buf));
+ mgmt->frame_control = cpu_to_le16(IEEE80211_STYPE_DEAUTH);
+ mgmt->duration = 0;
+ mgmt->seq_ctrl = 0;
+ mgmt->u.deauth.reason_code = cpu_to_le16(reason_code);
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) {
+ eth_broadcast_addr(mgmt->da);
+ memcpy(mgmt->sa,
+ priv->curr_bss_params.bss_descriptor.mac_address,
+ ETH_ALEN);
+ memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN);
+ priv->auth_flag = 0;
+ priv->auth_alg = WLAN_AUTH_NONE;
+ } else {
+ memcpy(mgmt->da, priv->curr_addr, ETH_ALEN);
+ memcpy(mgmt->sa, sa, ETH_ALEN);
+ memcpy(mgmt->bssid, priv->curr_addr, ETH_ALEN);
+ }
+
+ if (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_UAP) {
+ wiphy_lock(priv->wdev.wiphy);
+ cfg80211_rx_mlme_mgmt(priv->netdev, frame_buf, 26);
+ wiphy_unlock(priv->wdev.wiphy);
+ } else {
+ cfg80211_rx_mgmt(&priv->wdev,
+ priv->bss_chandef.chan->center_freq,
+ 0, frame_buf, 26, 0);
+ }
+}
+
+/* This function processes the received management packet and send it
+ * to the kernel.
+ */
+int
+nxpwifi_process_mgmt_packet(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct rxpd *rx_pd;
+ u16 pkt_len;
+ struct ieee80211_hdr *ieee_hdr;
+ int ret;
+
+ if (!skb)
+ return -ENOMEM;
+
+ if (!priv->mgmt_frame_mask ||
+ priv->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) {
+ nxpwifi_dbg(adapter, ERROR,
+ "do not receive mgmt frames on uninitialized intf");
+ return -EINVAL;
+ }
+
+ rx_pd = (struct rxpd *)skb->data;
+ pkt_len = le16_to_cpu(rx_pd->rx_pkt_length);
+ if (pkt_len < sizeof(struct ieee80211_hdr) + sizeof(pkt_len)) {
+ nxpwifi_dbg(adapter, ERROR, "invalid rx_pkt_length");
+ return -EINVAL;
+ }
+
+ skb_pull(skb, le16_to_cpu(rx_pd->rx_pkt_offset));
+ skb_pull(skb, sizeof(pkt_len));
+ pkt_len -= sizeof(pkt_len);
+
+ ieee_hdr = (void *)skb->data;
+ if (ieee80211_is_mgmt(ieee_hdr->frame_control)) {
+ ret = nxpwifi_parse_mgmt_packet(priv, (u8 *)ieee_hdr,
+ pkt_len, rx_pd);
+ if (ret)
+ return ret;
+ }
+ /* Remove address4 */
+ memmove(skb->data + sizeof(struct ieee80211_hdr_3addr),
+ skb->data + sizeof(struct ieee80211_hdr),
+ pkt_len - sizeof(struct ieee80211_hdr));
+
+ pkt_len -= ETH_ALEN;
+ rx_pd->rx_pkt_length = cpu_to_le16(pkt_len);
+
+ if (priv->host_mlme_reg &&
+ (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_UAP) &&
+ (ieee80211_is_auth(ieee_hdr->frame_control) ||
+ ieee80211_is_deauth(ieee_hdr->frame_control) ||
+ ieee80211_is_disassoc(ieee_hdr->frame_control))) {
+ struct nxpwifi_rxinfo *rx_info;
+
+ if (ieee80211_is_auth(ieee_hdr->frame_control)) {
+ if (priv->auth_flag & HOST_MLME_AUTH_PENDING) {
+ if (priv->auth_alg != WLAN_AUTH_SAE) {
+ priv->auth_flag &=
+ ~HOST_MLME_AUTH_PENDING;
+ priv->auth_flag |=
+ HOST_MLME_AUTH_DONE;
+ }
+ } else {
+ return 0;
+ }
+
+ nxpwifi_dbg(adapter, MSG,
+ "auth: receive authentication from %pM\n",
+ ieee_hdr->addr3);
+ } else {
+ if (!priv->wdev.connected)
+ return 0;
+
+ if (ieee80211_is_deauth(ieee_hdr->frame_control)) {
+ nxpwifi_dbg(adapter, MSG,
+ "auth: receive deauth from %pM\n",
+ ieee_hdr->addr3);
+ priv->auth_flag = 0;
+ priv->auth_alg = WLAN_AUTH_NONE;
+ } else {
+ nxpwifi_dbg(adapter, MSG,
+ "assoc: receive disassoc from %pM\n",
+ ieee_hdr->addr3);
+ }
+ }
+
+ rx_info = NXPWIFI_SKB_RXCB(skb);
+ rx_info->pkt_len = pkt_len;
+ skb_queue_tail(&adapter->rx_mlme_q, skb);
+ nxpwifi_queue_work(adapter, &adapter->host_mlme_work);
+ return -EINPROGRESS;
+ }
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
+ if (ieee80211_is_auth(ieee_hdr->frame_control))
+ nxpwifi_dbg(adapter, MSG,
+ "auth: receive auth from %pM\n",
+ ieee_hdr->addr2);
+ if (ieee80211_is_deauth(ieee_hdr->frame_control))
+ nxpwifi_dbg(adapter, MSG,
+ "auth: receive deauth from %pM\n",
+ ieee_hdr->addr2);
+ if (ieee80211_is_disassoc(ieee_hdr->frame_control))
+ nxpwifi_dbg(adapter, MSG,
+ "assoc: receive disassoc from %pM\n",
+ ieee_hdr->addr2);
+ if (ieee80211_is_assoc_req(ieee_hdr->frame_control))
+ nxpwifi_dbg(adapter, MSG,
+ "assoc: receive assoc req from %pM\n",
+ ieee_hdr->addr2);
+ if (ieee80211_is_reassoc_req(ieee_hdr->frame_control))
+ nxpwifi_dbg(adapter, MSG,
+ "assoc: receive reassoc req from %pM\n",
+ ieee_hdr->addr2);
+ }
+
+ cfg80211_rx_mgmt(&priv->wdev, priv->roc_cfg.chan.center_freq,
+ CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len,
+ 0);
+
+ return 0;
+}
+
+/* This function processes the received packet before sending it to the
+ * kernel.
+ *
+ * It extracts the SKB from the received buffer and sends it to kernel.
+ * In case the received buffer does not contain the data in SKB format,
+ * the function creates a blank SKB, fills it with the data from the
+ * received buffer and then sends this new SKB to the kernel.
+ */
+int nxpwifi_recv_packet(struct nxpwifi_private *priv, struct sk_buff *skb)
+{
+ struct nxpwifi_sta_node *src_node;
+ struct ethhdr *p_ethhdr;
+
+ if (!skb)
+ return -ENOMEM;
+
+ priv->stats.rx_bytes += skb->len;
+ priv->stats.rx_packets++;
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
+ p_ethhdr = (void *)skb->data;
+ src_node = nxpwifi_get_sta_entry(priv, p_ethhdr->h_source);
+ if (src_node) {
+ src_node->stats.last_rx = jiffies;
+ src_node->stats.rx_bytes += skb->len;
+ src_node->stats.rx_packets++;
+ }
+ }
+
+ skb->dev = priv->netdev;
+ skb->protocol = eth_type_trans(skb, priv->netdev);
+ skb->ip_summed = CHECKSUM_NONE;
+
+ netif_rx(skb);
+ return 0;
+}
+
+/* IOCTL completion callback handler.
+ *
+ * This function is called when a pending IOCTL is completed.
+ *
+ * If work queue support is enabled, the function wakes up the
+ * corresponding waiting function. Otherwise, it processes the
+ * IOCTL response and frees the response buffer.
+ */
+int nxpwifi_complete_cmd(struct nxpwifi_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node)
+{
+ WARN_ON(!cmd_node->wait_q_enabled);
+ nxpwifi_dbg(adapter, CMD, "cmd completed: status=%d\n",
+ adapter->cmd_wait_q.status);
+
+ *cmd_node->condition = true;
+ wake_up_interruptible(&adapter->cmd_wait_q.wait);
+
+ return 0;
+}
+
+/* This function will return the pointer to station entry in station list
+ * table which matches specified mac address.
+ * This function should be called after acquiring RA list spinlock.
+ * NULL is returned if station entry is not found in associated STA list.
+ */
+struct nxpwifi_sta_node *
+nxpwifi_get_sta_entry(struct nxpwifi_private *priv, const u8 *mac)
+{
+ struct nxpwifi_sta_node *node;
+
+ if (!mac)
+ return NULL;
+
+ list_for_each_entry(node, &priv->sta_list, list) {
+ if (!memcmp(node->mac_addr, mac, ETH_ALEN))
+ return node;
+ }
+
+ return NULL;
+}
+
+/* This function will add a sta_node entry to associated station list
+ * table with the given mac address.
+ * If entry exist already, existing entry is returned.
+ * If received mac address is NULL, NULL is returned.
+ */
+struct nxpwifi_sta_node *
+nxpwifi_add_sta_entry(struct nxpwifi_private *priv, const u8 *mac)
+{
+ struct nxpwifi_sta_node *node;
+
+ if (!mac)
+ return NULL;
+
+ spin_lock_bh(&priv->sta_list_spinlock);
+ node = nxpwifi_get_sta_entry(priv, mac);
+ if (node)
+ goto done;
+
+ node = kzalloc(sizeof(*node), GFP_ATOMIC);
+ if (!node)
+ goto done;
+
+ memcpy(node->mac_addr, mac, ETH_ALEN);
+ list_add_tail(&node->list, &priv->sta_list);
+
+done:
+ spin_unlock_bh(&priv->sta_list_spinlock);
+ return node;
+}
+
+/* This function will search for HT IE in association request IEs
+ * and set station HT parameters accordingly.
+ */
+void
+nxpwifi_set_sta_ht_cap(struct nxpwifi_private *priv, const u8 *ies,
+ int ies_len, struct nxpwifi_sta_node *node)
+{
+ struct element *ht_cap_ie;
+ const struct ieee80211_ht_cap *ht_cap;
+
+ if (!ies)
+ return;
+
+ ht_cap_ie = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies,
+ ies_len);
+ if (ht_cap_ie) {
+ ht_cap = (void *)(ht_cap_ie + 1);
+ node->is_11n_enabled = 1;
+ node->max_amsdu = le16_to_cpu(ht_cap->cap_info) &
+ IEEE80211_HT_CAP_MAX_AMSDU ?
+ NXPWIFI_TX_DATA_BUF_SIZE_8K :
+ NXPWIFI_TX_DATA_BUF_SIZE_4K;
+ } else {
+ node->is_11n_enabled = 0;
+ }
+}
+
+/* This function will delete a station entry from station list */
+void nxpwifi_del_sta_entry(struct nxpwifi_private *priv, const u8 *mac)
+{
+ struct nxpwifi_sta_node *node;
+
+ spin_lock_bh(&priv->sta_list_spinlock);
+
+ node = nxpwifi_get_sta_entry(priv, mac);
+ if (node) {
+ list_del(&node->list);
+ kfree(node);
+ }
+
+ spin_unlock_bh(&priv->sta_list_spinlock);
+}
+
+/* This function will delete all stations from associated station list. */
+void nxpwifi_del_all_sta_list(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_sta_node *node, *tmp;
+
+ spin_lock_bh(&priv->sta_list_spinlock);
+
+ list_for_each_entry_safe(node, tmp, &priv->sta_list, list) {
+ list_del(&node->list);
+ kfree(node);
+ }
+
+ INIT_LIST_HEAD(&priv->sta_list);
+ spin_unlock_bh(&priv->sta_list_spinlock);
+}
+
+/* This function adds histogram data to histogram array*/
+void nxpwifi_hist_data_add(struct nxpwifi_private *priv,
+ u8 rx_rate, s8 snr, s8 nflr)
+{
+ struct nxpwifi_histogram_data *phist_data = priv->hist_data;
+
+ if (atomic_read(&phist_data->num_samples) > NXPWIFI_HIST_MAX_SAMPLES)
+ nxpwifi_hist_data_reset(priv);
+ nxpwifi_hist_data_set(priv, rx_rate, snr, nflr);
+}
+
+/* function to add histogram record */
+void nxpwifi_hist_data_set(struct nxpwifi_private *priv, u8 rx_rate, s8 snr,
+ s8 nflr)
+{
+ struct nxpwifi_histogram_data *phist_data = priv->hist_data;
+ s8 nf = -nflr;
+ s8 rssi = snr - nflr;
+
+ atomic_inc(&phist_data->num_samples);
+ atomic_inc(&phist_data->rx_rate[rx_rate]);
+ atomic_inc(&phist_data->snr[snr + 128]);
+ atomic_inc(&phist_data->noise_flr[nf + 128]);
+ atomic_inc(&phist_data->sig_str[rssi + 128]);
+}
+
+/* function to reset histogram data during init/reset */
+void nxpwifi_hist_data_reset(struct nxpwifi_private *priv)
+{
+ int ix;
+ struct nxpwifi_histogram_data *phist_data = priv->hist_data;
+
+ atomic_set(&phist_data->num_samples, 0);
+ for (ix = 0; ix < NXPWIFI_MAX_AC_RX_RATES; ix++)
+ atomic_set(&phist_data->rx_rate[ix], 0);
+ for (ix = 0; ix < NXPWIFI_MAX_SNR; ix++)
+ atomic_set(&phist_data->snr[ix], 0);
+ for (ix = 0; ix < NXPWIFI_MAX_NOISE_FLR; ix++)
+ atomic_set(&phist_data->noise_flr[ix], 0);
+ for (ix = 0; ix < NXPWIFI_MAX_SIG_STRENGTH; ix++)
+ atomic_set(&phist_data->sig_str[ix], 0);
+}
+
+void *nxpwifi_alloc_dma_align_buf(int rx_len, gfp_t flags)
+{
+ struct sk_buff *skb;
+ int buf_len, pad;
+
+ buf_len = rx_len + NXPWIFI_RX_HEADROOM + NXPWIFI_DMA_ALIGN_SZ;
+
+ skb = __dev_alloc_skb(buf_len, flags);
+
+ if (!skb)
+ return NULL;
+
+ skb_reserve(skb, NXPWIFI_RX_HEADROOM);
+
+ pad = NXPWIFI_ALIGN_ADDR(skb->data, NXPWIFI_DMA_ALIGN_SZ) -
+ (long)skb->data;
+
+ skb_reserve(skb, pad);
+
+ return skb;
+}
+EXPORT_SYMBOL_GPL(nxpwifi_alloc_dma_align_buf);
+
+void nxpwifi_fw_dump_event(struct nxpwifi_private *priv)
+{
+ nxpwifi_send_cmd(priv, HOST_CMD_FW_DUMP_EVENT, HOST_ACT_GEN_SET,
+ 0, NULL, true);
+}
+EXPORT_SYMBOL_GPL(nxpwifi_fw_dump_event);
+
+int nxpwifi_append_data_tlv(u16 id, u8 *data, int len, u8 *pos, u8 *cmd_end)
+{
+ struct nxpwifi_ie_types_data *tlv;
+ u16 header_len = sizeof(struct nxpwifi_ie_types_header);
+
+ tlv = (struct nxpwifi_ie_types_data *)pos;
+ tlv->header.len = cpu_to_le16(len);
+
+ if (id == WLAN_EID_EXT_HE_CAPABILITY) {
+ if ((pos + header_len + len + 1) > cmd_end)
+ return 0;
+
+ tlv->header.type = cpu_to_le16(WLAN_EID_EXTENSION);
+ tlv->data[0] = WLAN_EID_EXT_HE_CAPABILITY;
+ memcpy(tlv->data + 1, data, len);
+ } else {
+ if ((pos + header_len + len) > cmd_end)
+ return 0;
+
+ tlv->header.type = cpu_to_le16(id);
+ memcpy(tlv->data, data, len);
+ }
+
+ return (header_len + len);
+}
+
+static int nxpwifi_get_vdll_image(struct nxpwifi_adapter *adapter, u32 vdll_len)
+{
+ struct vdll_dnld_ctrl *ctrl = &adapter->vdll_ctrl;
+ bool req_fw = false;
+ u32 offset;
+
+ if (ctrl->vdll_mem) {
+ nxpwifi_dbg(adapter, EVENT,
+ "VDLL mem is not empty: %p old_len=%d new_len=%d\n",
+ ctrl->vdll_mem, ctrl->vdll_len, vdll_len);
+ vfree(ctrl->vdll_mem);
+ ctrl->vdll_mem = NULL;
+ ctrl->vdll_len = 0;
+ }
+
+ ctrl->vdll_mem = vmalloc(vdll_len);
+ if (!ctrl->vdll_mem)
+ return -ENOMEM;
+
+ if (!adapter->firmware) {
+ req_fw = true;
+ if (request_firmware(&adapter->firmware, adapter->fw_name,
+ adapter->dev))
+ return -ENOENT;
+ }
+
+ if (adapter->firmware) {
+ if (vdll_len < adapter->firmware->size) {
+ offset = adapter->firmware->size - vdll_len;
+ memcpy(ctrl->vdll_mem, adapter->firmware->data + offset,
+ vdll_len);
+ } else {
+ nxpwifi_dbg(adapter, ERROR,
+ "Invalid VDLL length = %d, fw_len=%d\n",
+ vdll_len, (int)adapter->firmware->size);
+ return -EINVAL;
+ }
+ if (req_fw) {
+ release_firmware(adapter->firmware);
+ adapter->firmware = NULL;
+ }
+ }
+
+ ctrl->vdll_len = vdll_len;
+ nxpwifi_dbg(adapter, MSG, "VDLL image: len=%d\n", ctrl->vdll_len);
+
+ return 0;
+}
+
+int nxpwifi_download_vdll_block(struct nxpwifi_adapter *adapter,
+ u8 *block, u16 block_len)
+{
+ struct vdll_dnld_ctrl *ctrl = &adapter->vdll_ctrl;
+ struct host_cmd_ds_command *host_cmd;
+ u16 msg_len = block_len + S_DS_GEN;
+ int ret = 0;
+
+ skb_trim(ctrl->skb, 0);
+ skb_put_zero(ctrl->skb, msg_len);
+
+ host_cmd = (struct host_cmd_ds_command *)(ctrl->skb->data);
+
+ host_cmd->command = cpu_to_le16(HOST_CMD_VDLL);
+ host_cmd->seq_num = cpu_to_le16(0xFF00);
+ host_cmd->size = cpu_to_le16(msg_len);
+ memcpy(ctrl->skb->data + S_DS_GEN, block, block_len);
+
+ skb_push(ctrl->skb, adapter->intf_hdr_len);
+ ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_VDLL,
+ ctrl->skb, NULL);
+ skb_pull(ctrl->skb, adapter->intf_hdr_len);
+
+ if (ret)
+ nxpwifi_dbg(adapter, ERROR,
+ "Fail to download VDLL: block: %p, len: %d\n",
+ block, block_len);
+
+ return ret;
+}
+
+int nxpwifi_process_vdll_event(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct vdll_ind_event *vdll_evt =
+ (struct vdll_ind_event *)(skb->data + sizeof(u32));
+ u16 type = le16_to_cpu(vdll_evt->type);
+ u16 vdll_id = le16_to_cpu(vdll_evt->vdll_id);
+ u32 offset = le32_to_cpu(vdll_evt->offset);
+ u16 block_len = le16_to_cpu(vdll_evt->block_len);
+ struct vdll_dnld_ctrl *ctrl = &adapter->vdll_ctrl;
+ int ret = 0;
+
+ switch (type) {
+ case VDLL_IND_TYPE_REQ:
+ nxpwifi_dbg(adapter, EVENT,
+ "VDLL IND (REG): ID: %d, offset: %#x, len: %d\n",
+ vdll_id, offset, block_len);
+ if (offset <= ctrl->vdll_len) {
+ block_len =
+ min((u32)block_len, ctrl->vdll_len - offset);
+ if (!adapter->cmd_sent) {
+ ret = nxpwifi_download_vdll_block(adapter,
+ ctrl->vdll_mem
+ + offset,
+ block_len);
+ if (ret)
+ nxpwifi_dbg(adapter, ERROR,
+ "Download VDLL failed\n");
+ } else {
+ nxpwifi_dbg(adapter, EVENT,
+ "Delay download VDLL block\n");
+ ctrl->pending_block_len = block_len;
+ ctrl->pending_block = ctrl->vdll_mem + offset;
+ }
+ } else {
+ nxpwifi_dbg(adapter, ERROR,
+ "Err Req: offset=%#x, len=%d, vdll_len=%d\n",
+ offset, block_len, ctrl->vdll_len);
+ ret = -EINVAL;
+ }
+ break;
+ case VDLL_IND_TYPE_OFFSET:
+ nxpwifi_dbg(adapter, EVENT,
+ "VDLL IND (OFFSET): offset: %#x\n", offset);
+ ret = nxpwifi_get_vdll_image(adapter, offset);
+ break;
+ case VDLL_IND_TYPE_ERR_SIG:
+ case VDLL_IND_TYPE_ERR_ID:
+ case VDLL_IND_TYPE_SEC_ERR_ID:
+ nxpwifi_dbg(adapter, ERROR, "VDLL IND: error: %d\n", type);
+ break;
+ case VDLL_IND_TYPE_INTF_RESET:
+ nxpwifi_dbg(adapter, EVENT, "VDLL IND: interface reset\n");
+ break;
+ default:
+ nxpwifi_dbg(adapter, ERROR, "VDLL IND: unknown type: %d", type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+u64 nxpwifi_roc_cookie(struct nxpwifi_adapter *adapter)
+{
+ adapter->roc_cookie_counter++;
+
+ /* wow, you wrapped 64 bits ... more likely a bug */
+ if (WARN_ON(adapter->roc_cookie_counter == 0))
+ adapter->roc_cookie_counter++;
+
+ return adapter->roc_cookie_counter;
+}
+
+static bool nxpwifi_can_queue_work(struct nxpwifi_adapter *adapter)
+{
+ if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags) ||
+ test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags) ||
+ test_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags)) {
+ nxpwifi_dbg(adapter, WARN,
+ "queueing nxpwifi work while going to suspend\n");
+ return false;
+ }
+
+ return true;
+}
+
+void nxpwifi_queue_work(struct nxpwifi_adapter *adapter,
+ struct work_struct *work)
+{
+ if (!nxpwifi_can_queue_work(adapter))
+ return;
+
+ queue_work(adapter->workqueue, work);
+}
+EXPORT_SYMBOL(nxpwifi_queue_work);
+
+void nxpwifi_queue_delayed_work(struct nxpwifi_adapter *adapter,
+ struct delayed_work *dwork,
+ unsigned long delay)
+{
+ if (!nxpwifi_can_queue_work(adapter))
+ return;
+
+ queue_delayed_work(adapter->workqueue, dwork, delay);
+}
+EXPORT_SYMBOL(nxpwifi_queue_delayed_work);
new file mode 100644
@@ -0,0 +1,121 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NXP Wireless LAN device driver: utility functions
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#ifndef _NXPWIFI_UTIL_H_
+#define _NXPWIFI_UTIL_H_
+
+struct nxpwifi_adapter;
+
+struct nxpwifi_private;
+
+struct nxpwifi_dma_mapping {
+ dma_addr_t addr;
+ size_t len;
+};
+
+struct nxpwifi_cb {
+ struct nxpwifi_dma_mapping dma_mapping;
+ union {
+ struct nxpwifi_rxinfo rx_info;
+ struct nxpwifi_txinfo tx_info;
+ };
+};
+
+/* size/addr for nxpwifi_debug_info */
+#define item_size(n) (sizeof_field(struct nxpwifi_debug_info, n))
+#define item_addr(n) (offsetof(struct nxpwifi_debug_info, n))
+
+/* size/addr for struct nxpwifi_adapter */
+#define adapter_item_size(n) (sizeof_field(struct nxpwifi_adapter, n))
+#define adapter_item_addr(n) (offsetof(struct nxpwifi_adapter, n))
+
+struct nxpwifi_debug_data {
+ char name[32]; /* variable/array name */
+ u32 size; /* size of the variable/array */
+ size_t addr; /* address of the variable/array */
+ int num; /* number of variables in an array */
+};
+
+static inline struct nxpwifi_rxinfo *NXPWIFI_SKB_RXCB(struct sk_buff *skb)
+{
+ struct nxpwifi_cb *cb = (struct nxpwifi_cb *)skb->cb;
+
+ BUILD_BUG_ON(sizeof(struct nxpwifi_cb) > sizeof(skb->cb));
+ return &cb->rx_info;
+}
+
+static inline struct nxpwifi_txinfo *NXPWIFI_SKB_TXCB(struct sk_buff *skb)
+{
+ struct nxpwifi_cb *cb = (struct nxpwifi_cb *)skb->cb;
+
+ return &cb->tx_info;
+}
+
+static inline void nxpwifi_store_mapping(struct sk_buff *skb,
+ struct nxpwifi_dma_mapping *mapping)
+{
+ struct nxpwifi_cb *cb = (struct nxpwifi_cb *)skb->cb;
+
+ memcpy(&cb->dma_mapping, mapping, sizeof(*mapping));
+}
+
+static inline void nxpwifi_get_mapping(struct sk_buff *skb,
+ struct nxpwifi_dma_mapping *mapping)
+{
+ struct nxpwifi_cb *cb = (struct nxpwifi_cb *)skb->cb;
+
+ memcpy(mapping, &cb->dma_mapping, sizeof(*mapping));
+}
+
+static inline dma_addr_t NXPWIFI_SKB_DMA_ADDR(struct sk_buff *skb)
+{
+ struct nxpwifi_dma_mapping mapping;
+
+ nxpwifi_get_mapping(skb, &mapping);
+
+ return mapping.addr;
+}
+
+int nxpwifi_debug_info_to_buffer(struct nxpwifi_private *priv, char *buf,
+ struct nxpwifi_debug_info *info);
+
+static inline void le16_unaligned_add_cpu(__le16 *var, u16 val)
+{
+ put_unaligned_le16(get_unaligned_le16(var) + val, var);
+}
+
+int nxpwifi_append_data_tlv(u16 id, u8 *data, int len, u8 *pos, u8 *cmd_end);
+
+int nxpwifi_download_vdll_block(struct nxpwifi_adapter *adapter,
+ u8 *block, u16 block_len);
+
+int nxpwifi_process_vdll_event(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+
+u64 nxpwifi_roc_cookie(struct nxpwifi_adapter *adapter);
+
+void nxpwifi_queue_work(struct nxpwifi_adapter *adapter,
+ struct work_struct *work);
+
+void nxpwifi_queue_delayed_work(struct nxpwifi_adapter *adapter,
+ struct delayed_work *dwork,
+ unsigned long delay);
+
+/* Current firmware doesn't support AP and STA running on different
+ * channels simultaneously in normal mode.
+ * FW crash would occur in such case.
+ * This function is used to check if check_chan can be set to FW or not.
+ *
+ * Return:
+ * %true if check_chan can be set to FW without issues.
+ * %false there is already other channel is set to FW, setting of
+ * check_chan is not allowable.
+ */
+bool nxpwifi_is_channel_setting_allowable(struct nxpwifi_private *priv,
+ struct ieee80211_channel *check_chan);
+
+#endif /* !_NXPWIFI_UTIL_H_ */
Implement useful functions to help IE operations and some common utilities. Signed-off-by: David Lin <yu-hao.lin@nxp.com> --- drivers/net/wireless/nxp/nxpwifi/ie.c | 501 ++++++++++++ drivers/net/wireless/nxp/nxpwifi/util.c | 999 ++++++++++++++++++++++++ drivers/net/wireless/nxp/nxpwifi/util.h | 121 +++ 3 files changed, 1621 insertions(+) create mode 100644 drivers/net/wireless/nxp/nxpwifi/ie.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/util.c create mode 100644 drivers/net/wireless/nxp/nxpwifi/util.h