* [PATCH 01/43] wifi: nxpwifi: add 11ac.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-28 8:22 ` Abel Vesa
2024-06-21 7:51 ` [PATCH 02/43] wifi: nxpwifi: add 11ac.h David Lin
` (43 subsequent siblings)
44 siblings, 1 reply; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/11ac.c | 366 ++++++++++++++++++++++++
1 file changed, 366 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/11ac.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/11ac.c b/drivers/net/wireless/nxp/nxpwifi/11ac.c
new file mode 100644
index 000000000000..3e14ee602cdc
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/11ac.c
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: 802.11ac
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "fw.h"
+#include "main.h"
+#include "11ac.h"
+
+/* Tables of the MCS map to the highest data rate (in Mbps) supported
+ * for long GI.
+ */
+static const u16 max_rate_lgi_80MHZ[8][3] = {
+ {0x124, 0x15F, 0x186}, /* NSS = 1 */
+ {0x249, 0x2BE, 0x30C}, /* NSS = 2 */
+ {0x36D, 0x41D, 0x492}, /* NSS = 3 */
+ {0x492, 0x57C, 0x618}, /* NSS = 4 */
+ {0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */
+ {0x6DB, 0x83A, 0x0}, /* NSS = 6 */
+ {0x7FF, 0x999, 0xAAA}, /* NSS = 7 */
+ {0x924, 0xAF8, 0xC30} /* NSS = 8 */
+};
+
+static const u16 max_rate_lgi_160MHZ[8][3] = {
+ {0x249, 0x2BE, 0x30C}, /* NSS = 1 */
+ {0x492, 0x57C, 0x618}, /* NSS = 2 */
+ {0x6DB, 0x83A, 0x0}, /* NSS = 3 */
+ {0x924, 0xAF8, 0xC30}, /* NSS = 4 */
+ {0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */
+ {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */
+ {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */
+ {0x1248, 0x15F0, 0x1860} /* NSS = 8 */
+};
+
+/* This function converts the 2-bit MCS map to the highest long GI
+ * VHT data rate.
+ */
+static u16
+nxpwifi_convert_mcsmap_to_maxrate(struct nxpwifi_private *priv,
+ u8 bands, u16 mcs_map)
+{
+ u8 i, nss, mcs;
+ u16 max_rate = 0;
+ u32 usr_vht_cap_info = 0;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (bands & BAND_AAC)
+ usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a;
+ else
+ usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
+
+ /* find the max NSS supported */
+ nss = 1;
+ for (i = 1; i <= 8; i++) {
+ mcs = GET_VHTNSSMCS(mcs_map, i);
+ if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ nss = i;
+ }
+ mcs = GET_VHTNSSMCS(mcs_map, nss);
+
+ /* if mcs is 3, nss must be 1 (NSS = 1). Default mcs to MCS 0~9 */
+ if (mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ mcs = IEEE80211_VHT_MCS_SUPPORT_0_9;
+
+ if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) {
+ /* support 160 MHz */
+ max_rate = max_rate_lgi_160MHZ[nss - 1][mcs];
+ if (!max_rate)
+ /* MCS9 is not supported in NSS6 */
+ max_rate = max_rate_lgi_160MHZ[nss - 1][mcs - 1];
+ } else {
+ max_rate = max_rate_lgi_80MHZ[nss - 1][mcs];
+ if (!max_rate)
+ /* MCS9 is not supported in NSS3 */
+ max_rate = max_rate_lgi_80MHZ[nss - 1][mcs - 1];
+ }
+
+ return max_rate;
+}
+
+static void
+nxpwifi_fill_vht_cap_info(struct nxpwifi_private *priv,
+ struct ieee80211_vht_cap *vht_cap, u8 bands)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (bands & BAND_A)
+ vht_cap->vht_cap_info =
+ cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a);
+ else
+ vht_cap->vht_cap_info =
+ cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg);
+}
+
+void nxpwifi_fill_vht_cap_tlv(struct nxpwifi_private *priv,
+ struct ieee80211_vht_cap *vht_cap, u8 bands)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u16 mcs_map_user, mcs_map_resp, mcs_map_result;
+ u16 mcs_user, mcs_resp, nss, tmp;
+
+ /* Fill VHT cap info */
+ nxpwifi_fill_vht_cap_info(priv, vht_cap, bands);
+
+ /* rx MCS Set: find the minimum of the user rx mcs and ap rx mcs */
+ mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support);
+ mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map);
+ mcs_map_result = 0;
+
+ for (nss = 1; nss <= 8; nss++) {
+ mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
+ mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
+
+ if (mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED ||
+ mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ SET_VHTNSSMCS(mcs_map_result, nss,
+ IEEE80211_VHT_MCS_NOT_SUPPORTED);
+ else
+ SET_VHTNSSMCS(mcs_map_result, nss,
+ min(mcs_user, mcs_resp));
+ }
+
+ vht_cap->supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result);
+
+ tmp = nxpwifi_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
+ vht_cap->supp_mcs.rx_highest = cpu_to_le16(tmp);
+
+ /* tx MCS Set: find the minimum of the user tx mcs and ap tx mcs */
+ mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support);
+ mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
+ mcs_map_result = 0;
+
+ for (nss = 1; nss <= 8; nss++) {
+ mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
+ mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
+ if (mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED ||
+ mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ SET_VHTNSSMCS(mcs_map_result, nss,
+ IEEE80211_VHT_MCS_NOT_SUPPORTED);
+ else
+ SET_VHTNSSMCS(mcs_map_result, nss,
+ min(mcs_user, mcs_resp));
+ }
+
+ vht_cap->supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result);
+
+ tmp = nxpwifi_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
+ vht_cap->supp_mcs.tx_highest = cpu_to_le16(tmp);
+}
+
+int nxpwifi_cmd_append_11ac_tlv(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc,
+ u8 **buffer)
+{
+ struct nxpwifi_ie_types_vhtcap *vht_cap;
+ struct nxpwifi_ie_types_oper_mode_ntf *oper_ntf;
+ struct ieee_types_oper_mode_ntf *ieee_oper_ntf;
+ struct nxpwifi_ie_types_vht_oper *vht_op;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u8 supp_chwd_set;
+ u32 usr_vht_cap_info;
+ int ret_len = 0;
+
+ if (bss_desc->bss_band & BAND_A)
+ usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a;
+ else
+ usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
+
+ /* VHT Capabilities IE */
+ if (bss_desc->bcn_vht_cap) {
+ vht_cap = (struct nxpwifi_ie_types_vhtcap *)*buffer;
+ memset(vht_cap, 0, sizeof(*vht_cap));
+ vht_cap->header.type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY);
+ vht_cap->header.len =
+ cpu_to_le16(sizeof(struct ieee80211_vht_cap));
+ memcpy((u8 *)vht_cap + sizeof(struct nxpwifi_ie_types_header),
+ (u8 *)bss_desc->bcn_vht_cap,
+ le16_to_cpu(vht_cap->header.len));
+
+ nxpwifi_fill_vht_cap_tlv(priv, &vht_cap->vht_cap,
+ bss_desc->bss_band);
+ *buffer += sizeof(*vht_cap);
+ ret_len += sizeof(*vht_cap);
+ }
+
+ /* VHT Operation IE */
+ if (bss_desc->bcn_vht_oper) {
+ if (priv->bss_mode == NL80211_IFTYPE_STATION) {
+ vht_op = (struct nxpwifi_ie_types_vht_oper *)*buffer;
+ memset(vht_op, 0, sizeof(*vht_op));
+ vht_op->header.type =
+ cpu_to_le16(WLAN_EID_VHT_OPERATION);
+ vht_op->header.len = cpu_to_le16(sizeof(*vht_op) -
+ sizeof(struct nxpwifi_ie_types_header));
+ memcpy((u8 *)vht_op +
+ sizeof(struct nxpwifi_ie_types_header),
+ (u8 *)bss_desc->bcn_vht_oper,
+ le16_to_cpu(vht_op->header.len));
+
+ /* negotiate the channel width and central freq
+ * and keep the central freq as the peer suggests
+ */
+ supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info);
+
+ switch (supp_chwd_set) {
+ case 0:
+ vht_op->chan_width =
+ min_t(u8, IEEE80211_VHT_CHANWIDTH_80MHZ,
+ bss_desc->bcn_vht_oper->chan_width);
+ break;
+ case 1:
+ vht_op->chan_width =
+ min_t(u8, IEEE80211_VHT_CHANWIDTH_160MHZ,
+ bss_desc->bcn_vht_oper->chan_width);
+ break;
+ case 2:
+ vht_op->chan_width =
+ min_t(u8, IEEE80211_VHT_CHANWIDTH_80P80MHZ,
+ bss_desc->bcn_vht_oper->chan_width);
+ break;
+ default:
+ vht_op->chan_width =
+ IEEE80211_VHT_CHANWIDTH_USE_HT;
+ break;
+ }
+
+ *buffer += sizeof(*vht_op);
+ ret_len += sizeof(*vht_op);
+ }
+ }
+
+ /* Operating Mode Notification IE */
+ if (bss_desc->oper_mode) {
+ ieee_oper_ntf = bss_desc->oper_mode;
+ oper_ntf = (void *)*buffer;
+ memset(oper_ntf, 0, sizeof(*oper_ntf));
+ oper_ntf->header.type = cpu_to_le16(WLAN_EID_OPMODE_NOTIF);
+ oper_ntf->header.len = cpu_to_le16(sizeof(u8));
+ oper_ntf->oper_mode = ieee_oper_ntf->oper_mode;
+ *buffer += sizeof(*oper_ntf);
+ ret_len += sizeof(*oper_ntf);
+ }
+
+ return ret_len;
+}
+
+int nxpwifi_cmd_11ac_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd, u16 cmd_action,
+ struct nxpwifi_11ac_vht_cfg *cfg)
+{
+ struct host_cmd_11ac_vht_cfg *vhtcfg = &cmd->params.vht_cfg;
+
+ cmd->command = cpu_to_le16(HOST_CMD_11AC_CFG);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_11ac_vht_cfg) +
+ S_DS_GEN);
+ vhtcfg->action = cpu_to_le16(cmd_action);
+ vhtcfg->band_config = cfg->band_config;
+ vhtcfg->misc_config = cfg->misc_config;
+ vhtcfg->cap_info = cpu_to_le32(cfg->cap_info);
+ vhtcfg->mcs_tx_set = cpu_to_le32(cfg->mcs_tx_set);
+ vhtcfg->mcs_rx_set = cpu_to_le32(cfg->mcs_rx_set);
+
+ return 0;
+}
+
+/* This function initializes the BlockACK setup information for given
+ * nxpwifi_private structure for 11ac enabled networks.
+ */
+void nxpwifi_set_11ac_ba_params(struct nxpwifi_private *priv)
+{
+ priv->add_ba_param.timeout = NXPWIFI_DEFAULT_BLOCK_ACK_TIMEOUT;
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
+ priv->add_ba_param.tx_win_size =
+ NXPWIFI_11AC_UAP_AMPDU_DEF_TXWINSIZE;
+ priv->add_ba_param.rx_win_size =
+ NXPWIFI_11AC_UAP_AMPDU_DEF_RXWINSIZE;
+ } else {
+ priv->add_ba_param.tx_win_size =
+ NXPWIFI_11AC_STA_AMPDU_DEF_TXWINSIZE;
+ priv->add_ba_param.rx_win_size =
+ NXPWIFI_11AC_STA_AMPDU_DEF_RXWINSIZE;
+ }
+}
+
+bool nxpwifi_is_bss_in_11ac_mode(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_bssdescriptor *bss_desc;
+ struct ieee80211_vht_operation *vht_oper;
+
+ bss_desc = &priv->curr_bss_params.bss_descriptor;
+ vht_oper = bss_desc->bcn_vht_oper;
+
+ if (!bss_desc->bcn_vht_cap || !vht_oper)
+ return false;
+
+ if (vht_oper->chan_width == IEEE80211_VHT_CHANWIDTH_USE_HT)
+ return false;
+
+ return true;
+}
+
+u8 nxpwifi_get_center_freq_index(struct nxpwifi_private *priv, u8 band,
+ u32 pri_chan, u8 chan_bw)
+{
+ u8 center_freq_idx = 0;
+
+ if (band & BAND_AAC) {
+ switch (pri_chan) {
+ case 36:
+ case 40:
+ case 44:
+ case 48:
+ if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+ center_freq_idx = 42;
+ break;
+ case 52:
+ case 56:
+ case 60:
+ case 64:
+ if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+ center_freq_idx = 58;
+ else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ)
+ center_freq_idx = 50;
+ break;
+ case 100:
+ case 104:
+ case 108:
+ case 112:
+ if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+ center_freq_idx = 106;
+ break;
+ case 116:
+ case 120:
+ case 124:
+ case 128:
+ if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+ center_freq_idx = 122;
+ else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ)
+ center_freq_idx = 114;
+ break;
+ case 132:
+ case 136:
+ case 140:
+ case 144:
+ if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+ center_freq_idx = 138;
+ break;
+ case 149:
+ case 153:
+ case 157:
+ case 161:
+ if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
+ center_freq_idx = 155;
+ break;
+ default:
+ center_freq_idx = 42;
+ }
+ }
+
+ return center_freq_idx;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* Re: [PATCH 01/43] wifi: nxpwifi: add 11ac.c
2024-06-21 7:51 ` [PATCH 01/43] wifi: nxpwifi: add 11ac.c David Lin
@ 2024-06-28 8:22 ` Abel Vesa
2024-07-01 0:46 ` [EXT] " David Lin
0 siblings, 1 reply; 56+ messages in thread
From: Abel Vesa @ 2024-06-28 8:22 UTC (permalink / raw)
To: David Lin
Cc: linux-wireless, linux-kernel, briannorris, kvalo, francesco,
tsung-hsien.hsieh
On 24-06-21 15:51:26, David Lin wrote:
> Signed-off-by: David Lin <yu-hao.lin@nxp.com>
Hi David,
Please read the ./Documentation/process/submitting-patches.rst
Just a hint. There is no commit message.
> ---
> drivers/net/wireless/nxp/nxpwifi/11ac.c | 366 ++++++++++++++++++++++++
> 1 file changed, 366 insertions(+)
> create mode 100644 drivers/net/wireless/nxp/nxpwifi/11ac.c
>
> diff --git a/drivers/net/wireless/nxp/nxpwifi/11ac.c b/drivers/net/wireless/nxp/nxpwifi/11ac.c
> new file mode 100644
> index 000000000000..3e14ee602cdc
> --- /dev/null
> +++ b/drivers/net/wireless/nxp/nxpwifi/11ac.c
> @@ -0,0 +1,366 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * NXP Wireless LAN device driver: 802.11ac
> + *
> + * Copyright 2011-2024 NXP
> + */
[...]
^ permalink raw reply [flat|nested] 56+ messages in thread
* RE: [EXT] Re: [PATCH 01/43] wifi: nxpwifi: add 11ac.c
2024-06-28 8:22 ` Abel Vesa
@ 2024-07-01 0:46 ` David Lin
0 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-07-01 0:46 UTC (permalink / raw)
To: Abel Vesa
Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org,
briannorris@chromium.org, kvalo@kernel.org, francesco@dolcini.it,
Pete Hsieh
Hi Abel,
> From: Abel Vesa <abel.vesa@linaro.org>
> Sent: Friday, June 28, 2024 4:22 PM
> To: David Lin <yu-hao.lin@nxp.com>
> Cc: linux-wireless@vger.kernel.org; linux-kernel@vger.kernel.org;
> briannorris@chromium.org; kvalo@kernel.org; francesco@dolcini.it; Pete
> Hsieh <tsung-hsien.hsieh@nxp.com>
> Subject: [EXT] Re: [PATCH 01/43] wifi: nxpwifi: add 11ac.c
>
> Caution: This is an external email. Please take care when clicking links or
> opening attachments. When in doubt, report the message using the 'Report
> this email' button
>
>
> On 24-06-21 15:51:26, David Lin wrote:
> > Signed-off-by: David Lin <yu-hao.lin@nxp.com>
>
> Hi David,
>
> Please read the ./Documentation/process/submitting-patches.rst
>
> Just a hint. There is no commit message.
>
> > ---
> > drivers/net/wireless/nxp/nxpwifi/11ac.c | 366
> ++++++++++++++++++++++++
> > 1 file changed, 366 insertions(+)
> > create mode 100644 drivers/net/wireless/nxp/nxpwifi/11ac.c
> >
> > diff --git a/drivers/net/wireless/nxp/nxpwifi/11ac.c
> b/drivers/net/wireless/nxp/nxpwifi/11ac.c
> > new file mode 100644
> > index 000000000000..3e14ee602cdc
> > --- /dev/null
> > +++ b/drivers/net/wireless/nxp/nxpwifi/11ac.c
> > @@ -0,0 +1,366 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * NXP Wireless LAN device driver: 802.11ac
> > + *
> > + * Copyright 2011-2024 NXP
> > + */
>
> [...]
Thanks for your information. For new driver, it should be submitted one file per patch first.
Once if accepted, it will become one single patch. That is the reason, I won't give commit
message for the patch of a single file.
David
^ permalink raw reply [flat|nested] 56+ messages in thread
* [PATCH 02/43] wifi: nxpwifi: add 11ac.h
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
2024-06-21 7:51 ` [PATCH 01/43] wifi: nxpwifi: add 11ac.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 03/43] wifi: nxpwifi: add 11h.c David Lin
` (42 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/11ac.h | 33 +++++++++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/11ac.h
diff --git a/drivers/net/wireless/nxp/nxpwifi/11ac.h b/drivers/net/wireless/nxp/nxpwifi/11ac.h
new file mode 100644
index 000000000000..b060de17a18e
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/11ac.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NXP Wireless LAN device driver: 802.11ac
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#ifndef _NXPWIFI_11AC_H_
+#define _NXPWIFI_11AC_H_
+
+#define VHT_CFG_2GHZ BIT(0)
+#define VHT_CFG_5GHZ BIT(1)
+
+enum vht_cfg_misc_config {
+ VHT_CAP_TX_OPERATION = 1,
+ VHT_CAP_ASSOCIATION,
+ VHT_CAP_UAP_ONLY
+};
+
+#define DEFAULT_VHT_MCS_SET 0xfffe
+#define DISABLE_VHT_MCS_SET 0xffff
+
+#define VHT_BW_80_160_80P80 BIT(2)
+
+int nxpwifi_cmd_append_11ac_tlv(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc,
+ u8 **buffer);
+int nxpwifi_cmd_11ac_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd, u16 cmd_action,
+ struct nxpwifi_11ac_vht_cfg *cfg);
+void nxpwifi_fill_vht_cap_tlv(struct nxpwifi_private *priv,
+ struct ieee80211_vht_cap *vht_cap, u8 bands);
+#endif /* _NXPWIFI_11AC_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 03/43] wifi: nxpwifi: add 11h.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
2024-06-21 7:51 ` [PATCH 01/43] wifi: nxpwifi: add 11ac.c David Lin
2024-06-21 7:51 ` [PATCH 02/43] wifi: nxpwifi: add 11ac.h David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 04/43] wifi: nxpwifi: add 11n.c David Lin
` (41 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/11h.c | 432 +++++++++++++++++++++++++
1 file changed, 432 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/11h.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/11h.c b/drivers/net/wireless/nxp/nxpwifi/11h.c
new file mode 100644
index 000000000000..4bd21e1686e6
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/11h.c
@@ -0,0 +1,432 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: 802.11h
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "main.h"
+#include "cmdevt.h"
+#include "fw.h"
+#include "cfg80211.h"
+
+void nxpwifi_init_11h_params(struct nxpwifi_private *priv)
+{
+ priv->state_11h.is_11h_enabled = true;
+ priv->state_11h.is_11h_active = false;
+}
+
+inline int nxpwifi_is_11h_active(struct nxpwifi_private *priv)
+{
+ return priv->state_11h.is_11h_active;
+}
+
+/* This function appends 11h info to a buffer while joining an
+ * infrastructure BSS
+ */
+static void
+nxpwifi_11h_process_infra_join(struct nxpwifi_private *priv, u8 **buffer,
+ struct nxpwifi_bssdescriptor *bss_desc)
+{
+ struct nxpwifi_ie_types_header *ie_header;
+ struct nxpwifi_ie_types_pwr_capability *cap;
+ struct nxpwifi_ie_types_local_pwr_constraint *constraint;
+ struct ieee80211_supported_band *sband;
+ u8 radio_type;
+ int i;
+
+ if (!buffer || !(*buffer))
+ return;
+
+ radio_type = nxpwifi_band_to_radio_type((u8)bss_desc->bss_band);
+ sband = priv->wdev.wiphy->bands[radio_type];
+
+ cap = (struct nxpwifi_ie_types_pwr_capability *)*buffer;
+ cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY);
+ cap->header.len = cpu_to_le16(2);
+ cap->min_pwr = 0;
+ cap->max_pwr = 0;
+ *buffer += sizeof(*cap);
+
+ constraint = (struct nxpwifi_ie_types_local_pwr_constraint *)*buffer;
+ constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT);
+ constraint->header.len = cpu_to_le16(2);
+ constraint->chan = bss_desc->channel;
+ constraint->constraint = bss_desc->local_constraint;
+ *buffer += sizeof(*constraint);
+
+ ie_header = (struct nxpwifi_ie_types_header *)*buffer;
+ ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
+ ie_header->len = cpu_to_le16(2 * sband->n_channels + 2);
+ *buffer += sizeof(*ie_header);
+ *(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS;
+ *(*buffer)++ = 2 * sband->n_channels;
+ for (i = 0; i < sband->n_channels; i++) {
+ u32 center_freq;
+
+ center_freq = sband->channels[i].center_freq;
+ *(*buffer)++ = ieee80211_frequency_to_channel(center_freq);
+ *(*buffer)++ = 1; /* one channel in the subband */
+ }
+}
+
+/* Enable or disable the 11h extensions in the firmware */
+int nxpwifi_11h_activate(struct nxpwifi_private *priv, bool flag)
+{
+ u32 enable = flag;
+
+ /* enable master mode radar detection on AP interface */
+ if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) && enable)
+ enable |= NXPWIFI_MASTER_RADAR_DET_MASK;
+
+ return nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB,
+ HOST_ACT_GEN_SET, DOT11H_I, &enable, true);
+}
+
+/* This functions processes TLV buffer for a pending BSS Join command.
+ *
+ * Activate 11h functionality in the firmware if the spectrum management
+ * capability bit is found in the network we are joining. Also, necessary
+ * TLVs are set based on requested network's 11h capability.
+ */
+void nxpwifi_11h_process_join(struct nxpwifi_private *priv, u8 **buffer,
+ struct nxpwifi_bssdescriptor *bss_desc)
+{
+ if (bss_desc->sensed_11h) {
+ /* Activate 11h functions in firmware, turns on capability
+ * bit
+ */
+ nxpwifi_11h_activate(priv, true);
+ priv->state_11h.is_11h_active = true;
+ bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+ nxpwifi_11h_process_infra_join(priv, buffer, bss_desc);
+ } else {
+ /* Deactivate 11h functions in the firmware */
+ nxpwifi_11h_activate(priv, false);
+ priv->state_11h.is_11h_active = false;
+ bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT;
+ }
+}
+
+/* This is DFS CAC work queue function.
+ * This delayed work emits CAC finished event for cfg80211 if
+ * CAC was started earlier.
+ */
+void nxpwifi_dfs_cac_work_queue(struct work_struct *work)
+{
+ struct cfg80211_chan_def chandef;
+ struct delayed_work *delayed_work = to_delayed_work(work);
+ struct nxpwifi_private *priv =
+ container_of(delayed_work, struct nxpwifi_private,
+ dfs_cac_work);
+
+ chandef = priv->dfs_chandef;
+ if (priv->wdev.cac_started) {
+ nxpwifi_dbg(priv->adapter, MSG,
+ "CAC timer finished; No radar detected\n");
+ cfg80211_cac_event(priv->netdev, &chandef,
+ NL80211_RADAR_CAC_FINISHED,
+ GFP_KERNEL);
+ }
+}
+
+static u8 nxpwifi_get_channel_2_offset(int chan)
+{
+ u8 chan2_offset = SEC_CHAN_NONE;
+
+ switch (chan) {
+ case 36:
+ case 44:
+ case 52:
+ case 60:
+ case 100:
+ case 108:
+ case 116:
+ case 124:
+ case 132:
+ case 140:
+ case 149:
+ case 157:
+ case 165:
+ case 173:
+ chan2_offset = SEC_CHAN_ABOVE;
+ break;
+ case 40:
+ case 48:
+ case 56:
+ case 64:
+ case 104:
+ case 112:
+ case 120:
+ case 128:
+ case 136:
+ case 144:
+ case 153:
+ case 161:
+ case 169:
+ case 177:
+ chan2_offset = SEC_CHAN_BELOW;
+ break;
+ }
+
+ return chan2_offset;
+}
+
+static void nxpwifi_convert_chan_to_band_cfg(u8 *band_cfg,
+ struct cfg80211_chan_def *chan_def)
+{
+ u8 chan_band, chan_width, chan2_offset;
+
+ switch (chan_def->chan->band) {
+ case NL80211_BAND_2GHZ:
+ chan_band = BAND_2GHZ;
+ break;
+ case NL80211_BAND_5GHZ:
+ chan_band = BAND_5GHZ;
+ break;
+ default:
+ break;
+ }
+
+ switch (chan_def->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ chan_width = CHAN_BW_20MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ chan_width = CHAN_BW_40MHZ;
+ if (chan_def->center_freq1 > chan_def->chan->center_freq)
+ chan2_offset = SEC_CHAN_ABOVE;
+ else
+ chan2_offset = SEC_CHAN_BELOW;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ chan2_offset =
+ nxpwifi_get_channel_2_offset(chan_def->chan->hw_value);
+ chan_width = CHAN_BW_80MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_160:
+ default:
+ break;
+ }
+
+ *band_cfg = ((chan2_offset << BAND_CFG_CHAN2_SHIFT_BIT) &
+ BAND_CFG_CHAN2_OFFSET_MASK) |
+ ((chan_width << BAND_CFG_CHAN_WIDTH_SHIFT_BIT) &
+ BAND_CFG_CHAN_WIDTH_MASK) |
+ ((chan_band << BAND_CFG_CHAN_BAND_SHIFT_BIT) &
+ BAND_CFG_CHAN_BAND_MASK);
+}
+
+/* This function prepares channel report request command to FW for
+ * starting radar detection.
+ */
+int nxpwifi_cmd_issue_chan_report_request(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ void *data_buf)
+{
+ struct host_cmd_ds_chan_rpt_req *cr_req = &cmd->params.chan_rpt_req;
+ struct nxpwifi_radar_params *radar_params = (void *)data_buf;
+ u16 size;
+
+ cmd->command = cpu_to_le16(HOST_CMD_CHAN_REPORT_REQUEST);
+ size = S_DS_GEN;
+
+ cr_req->chan_desc.start_freq = cpu_to_le16(NXPWIFI_A_BAND_START_FREQ);
+ nxpwifi_convert_chan_to_band_cfg(&cr_req->chan_desc.band_cfg,
+ radar_params->chandef);
+ cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value;
+ cr_req->msec_dwell_time = cpu_to_le32(radar_params->cac_time_ms);
+ size += sizeof(*cr_req);
+
+ if (radar_params->cac_time_ms) {
+ struct nxpwifi_ie_types_chan_rpt_data *rpt;
+
+ rpt = (struct nxpwifi_ie_types_chan_rpt_data *)((u8 *)cmd + size);
+ rpt->header.type = cpu_to_le16(TLV_TYPE_CHANRPT_11H_BASIC);
+ rpt->header.len = cpu_to_le16(sizeof(u8));
+ rpt->meas_rpt_map = 1 << MEAS_RPT_MAP_RADAR_SHIFT_BIT;
+ size += sizeof(*rpt);
+
+ nxpwifi_dbg(priv->adapter, MSG,
+ "11h: issuing DFS Radar check for channel=%d\n",
+ radar_params->chandef->chan->hw_value);
+ } else {
+ nxpwifi_dbg(priv->adapter, MSG, "cancelling CAC\n");
+ }
+
+ cmd->size = cpu_to_le16(size);
+
+ return 0;
+}
+
+int nxpwifi_stop_radar_detection(struct nxpwifi_private *priv,
+ struct cfg80211_chan_def *chandef)
+{
+ struct nxpwifi_radar_params radar_params;
+
+ memset(&radar_params, 0, sizeof(struct nxpwifi_radar_params));
+ radar_params.chandef = chandef;
+ radar_params.cac_time_ms = 0;
+
+ return nxpwifi_send_cmd(priv, HOST_CMD_CHAN_REPORT_REQUEST,
+ HOST_ACT_GEN_SET, 0, &radar_params, true);
+}
+
+/* This function is to abort ongoing CAC upon stopping AP operations
+ * or during unload.
+ */
+void nxpwifi_abort_cac(struct nxpwifi_private *priv)
+{
+ if (priv->wdev.cac_started) {
+ if (nxpwifi_stop_radar_detection(priv, &priv->dfs_chandef))
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "failed to stop CAC in FW\n");
+ nxpwifi_dbg(priv->adapter, MSG,
+ "Aborting delayed work for CAC.\n");
+ cancel_delayed_work_sync(&priv->dfs_cac_work);
+ cfg80211_cac_event(priv->netdev, &priv->dfs_chandef,
+ NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
+ }
+}
+
+/* This function handles channel report event from FW during CAC period.
+ * If radar is detected during CAC, driver indicates the same to cfg80211
+ * and also cancels ongoing delayed work.
+ */
+int nxpwifi_11h_handle_chanrpt_ready(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct host_cmd_ds_chan_rpt_event *rpt_event;
+ struct nxpwifi_ie_types_chan_rpt_data *rpt;
+ u16 event_len, tlv_len;
+
+ rpt_event = (void *)(skb->data + sizeof(u32));
+ event_len = skb->len - (sizeof(struct host_cmd_ds_chan_rpt_event) +
+ sizeof(u32));
+
+ if (le32_to_cpu(rpt_event->result) != HOST_RESULT_OK) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Error in channel report event\n");
+ return -1;
+ }
+
+ while (event_len >= sizeof(struct nxpwifi_ie_types_header)) {
+ rpt = (void *)&rpt_event->tlvbuf;
+ tlv_len = le16_to_cpu(rpt->header.len);
+
+ switch (le16_to_cpu(rpt->header.type)) {
+ case TLV_TYPE_CHANRPT_11H_BASIC:
+ if (rpt->meas_rpt_map & MEAS_RPT_MAP_RADAR_MASK) {
+ nxpwifi_dbg(priv->adapter, MSG,
+ "RADAR Detected on channel %d!\n",
+ priv->dfs_chandef.chan->hw_value);
+ cancel_delayed_work_sync(&priv->dfs_cac_work);
+ cfg80211_cac_event(priv->netdev,
+ &priv->dfs_chandef,
+ NL80211_RADAR_CAC_ABORTED,
+ GFP_KERNEL);
+ cfg80211_radar_event(priv->adapter->wiphy,
+ &priv->dfs_chandef,
+ GFP_KERNEL);
+ }
+ break;
+ default:
+ break;
+ }
+
+ event_len -= (tlv_len + sizeof(rpt->header));
+ }
+
+ return 0;
+}
+
+/* Handler for radar detected event from FW.*/
+int nxpwifi_11h_handle_radar_detected(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct nxpwifi_radar_det_event *rdr_event;
+
+ rdr_event = (void *)(skb->data + sizeof(u32));
+
+ nxpwifi_dbg(priv->adapter, MSG,
+ "radar detected; indicating kernel\n");
+ if (priv->wdev.cac_started) {
+ if (nxpwifi_stop_radar_detection(priv, &priv->dfs_chandef))
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Failed to stop CAC in FW\n");
+ cancel_delayed_work_sync(&priv->dfs_cac_work);
+ cfg80211_cac_event(priv->netdev, &priv->dfs_chandef,
+ NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
+ }
+ cfg80211_radar_event(priv->adapter->wiphy, &priv->dfs_chandef,
+ GFP_KERNEL);
+ nxpwifi_dbg(priv->adapter, MSG, "regdomain: %d\n",
+ rdr_event->reg_domain);
+ nxpwifi_dbg(priv->adapter, MSG, "radar detection type: %d\n",
+ rdr_event->det_type);
+
+ return 0;
+}
+
+/* This is work queue function for channel switch handling.
+ * This function takes care of updating new channel definitin to
+ * bss config structure, restart AP and indicate channel switch success
+ * to cfg80211.
+ */
+void nxpwifi_dfs_chan_sw_work_queue(struct work_struct *work)
+{
+ struct nxpwifi_uap_bss_param *bss_cfg;
+ struct delayed_work *delayed_work = to_delayed_work(work);
+ struct nxpwifi_private *priv = container_of(delayed_work,
+ struct nxpwifi_private,
+ dfs_chan_sw_work);
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (nxpwifi_del_mgmt_ies(priv))
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Failed to delete mgmt IEs!\n");
+
+ bss_cfg = &priv->bss_cfg;
+ if (!bss_cfg->beacon_period) {
+ nxpwifi_dbg(adapter, ERROR,
+ "channel switch: AP already stopped\n");
+ return;
+ }
+
+ if (nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_STOP,
+ HOST_ACT_GEN_SET, 0, NULL, true)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "channel switch: Failed to stop the BSS\n");
+ return;
+ }
+
+ if (nxpwifi_cfg80211_change_beacon_data(adapter->wiphy, priv->netdev,
+ &priv->beacon_after)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "channel switch: Failed to set beacon\n");
+ return;
+ }
+
+ nxpwifi_uap_set_channel(priv, bss_cfg, priv->dfs_chandef);
+
+ if (nxpwifi_config_start_uap(priv, bss_cfg)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "Failed to start AP after channel switch\n");
+ return;
+ }
+
+ nxpwifi_dbg(adapter, MSG,
+ "indicating channel switch completion to kernel\n");
+ wiphy_lock(priv->wdev.wiphy);
+ cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef, 0);
+ wiphy_unlock(priv->wdev.wiphy);
+
+ if (priv->uap_stop_tx) {
+ if (!netif_carrier_ok(priv->netdev))
+ netif_carrier_on(priv->netdev);
+ nxpwifi_wake_up_net_dev_queue(priv->netdev, adapter);
+ priv->uap_stop_tx = false;
+ }
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 04/43] wifi: nxpwifi: add 11n.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (2 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 03/43] wifi: nxpwifi: add 11h.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 05/43] wifi: nxpwifi: add 11n.h David Lin
` (40 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/11n.c | 851 +++++++++++++++++++++++++
1 file changed, 851 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/11n.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/11n.c b/drivers/net/wireless/nxp/nxpwifi/11n.c
new file mode 100644
index 000000000000..76f01fa6387f
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/11n.c
@@ -0,0 +1,851 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: 802.11n
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cmdevt.h"
+#include "wmm.h"
+#include "11n.h"
+
+/* Fills HT capability information field, AMPDU Parameters field, HT extended
+ * capability field, and supported MCS set fields.
+ *
+ * HT capability information field, AMPDU Parameters field, supported MCS set
+ * fields are retrieved from cfg80211 stack
+ *
+ * RD responder bit to set to clear in the extended capability header.
+ */
+int nxpwifi_fill_cap_info(struct nxpwifi_private *priv, u8 radio_type,
+ struct ieee80211_ht_cap *ht_cap)
+{
+ u16 ht_cap_info;
+ u16 bcn_ht_cap = le16_to_cpu(ht_cap->cap_info);
+ u16 ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info);
+ struct ieee80211_supported_band *sband =
+ priv->wdev.wiphy->bands[radio_type];
+
+ if (WARN_ON_ONCE(!sband)) {
+ nxpwifi_dbg(priv->adapter, ERROR, "Invalid radio type!\n");
+ return -EINVAL;
+ }
+
+ ht_cap->ampdu_params_info =
+ (sband->ht_cap.ampdu_factor &
+ IEEE80211_HT_AMPDU_PARM_FACTOR) |
+ ((sband->ht_cap.ampdu_density <<
+ IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) &
+ IEEE80211_HT_AMPDU_PARM_DENSITY);
+
+ memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs,
+ sizeof(sband->ht_cap.mcs));
+
+ if (priv->bss_mode == NL80211_IFTYPE_STATION ||
+ (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
+ priv->adapter->sec_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_NONE))
+ /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
+ SETHT_MCS32(ht_cap->mcs.rx_mask);
+
+ /* Clear RD responder bit */
+ ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER;
+
+ ht_cap_info = sband->ht_cap.cap;
+ if (bcn_ht_cap) {
+ if (!(bcn_ht_cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
+ ht_cap_info &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ if (!(bcn_ht_cap & IEEE80211_HT_CAP_SGI_40))
+ ht_cap_info &= ~IEEE80211_HT_CAP_SGI_40;
+ if (!(bcn_ht_cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
+ ht_cap_info &= ~IEEE80211_HT_CAP_40MHZ_INTOLERANT;
+ }
+ ht_cap->cap_info = cpu_to_le16(ht_cap_info);
+ ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap);
+
+ if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap))
+ ht_cap->tx_BF_cap_info = cpu_to_le32(NXPWIFI_DEF_11N_TX_BF_CAP);
+
+ return 0;
+}
+
+/* This function returns the pointer to an entry in BA Stream
+ * table which matches the requested BA status.
+ */
+static struct nxpwifi_tx_ba_stream_tbl *
+nxpwifi_get_ba_status(struct nxpwifi_private *priv,
+ enum nxpwifi_ba_status ba_status)
+{
+ struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl;
+
+ spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
+ list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
+ if (tx_ba_tsr_tbl->ba_status == ba_status) {
+ spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
+ return tx_ba_tsr_tbl;
+ }
+ }
+ spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
+ return NULL;
+}
+
+/* This function handles the command response of delete a block
+ * ack request.
+ *
+ * The function checks the response success status and takes action
+ * accordingly (send an add BA request in case of success, or recreate
+ * the deleted stream in case of failure, if the add BA was also
+ * initiated by us).
+ */
+int nxpwifi_ret_11n_delba(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ int tid;
+ struct nxpwifi_tx_ba_stream_tbl *tx_ba_tbl;
+ struct host_cmd_ds_11n_delba *del_ba = &resp->params.del_ba;
+ u16 del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set);
+
+ tid = del_ba_param_set >> DELBA_TID_POS;
+ if (del_ba->del_result == BA_RESULT_SUCCESS) {
+ nxpwifi_del_ba_tbl(priv, tid, del_ba->peer_mac_addr,
+ TYPE_DELBA_SENT,
+ INITIATOR_BIT(del_ba_param_set));
+
+ tx_ba_tbl = nxpwifi_get_ba_status(priv, BA_SETUP_INPROGRESS);
+ if (tx_ba_tbl)
+ nxpwifi_send_addba(priv, tx_ba_tbl->tid,
+ tx_ba_tbl->ra);
+ } else { /*
+ * In case of failure, recreate the deleted stream in case
+ * we initiated the DELBA
+ */
+ if (!INITIATOR_BIT(del_ba_param_set))
+ return 0;
+
+ nxpwifi_create_ba_tbl(priv, del_ba->peer_mac_addr, tid,
+ BA_SETUP_INPROGRESS);
+
+ tx_ba_tbl = nxpwifi_get_ba_status(priv, BA_SETUP_INPROGRESS);
+
+ if (tx_ba_tbl)
+ nxpwifi_del_ba_tbl(priv, tx_ba_tbl->tid, tx_ba_tbl->ra,
+ TYPE_DELBA_SENT, true);
+ }
+
+ return 0;
+}
+
+/* This function handles the command response of add a block
+ * ack request.
+ *
+ * Handling includes changing the header fields to CPU formats, checking
+ * the response success status and taking actions accordingly (delete the
+ * BA stream table in case of failure).
+ */
+int nxpwifi_ret_11n_addba_req(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ int tid, tid_down;
+ struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
+ struct nxpwifi_tx_ba_stream_tbl *tx_ba_tbl;
+ struct nxpwifi_ra_list_tbl *ra_list;
+ u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
+
+ add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn))
+ & SSN_MASK);
+
+ tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
+ >> BLOCKACKPARAM_TID_POS;
+
+ tid_down = nxpwifi_wmm_downgrade_tid(priv, tid);
+ ra_list = nxpwifi_wmm_get_ralist_node(priv, tid_down,
+ add_ba_rsp->peer_mac_addr);
+ if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
+ if (ra_list) {
+ ra_list->ba_status = BA_SETUP_NONE;
+ ra_list->amsdu_in_ampdu = false;
+ }
+ nxpwifi_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr,
+ TYPE_DELBA_SENT, true);
+ if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT)
+ priv->aggr_prio_tbl[tid].ampdu_ap =
+ BA_STREAM_NOT_ALLOWED;
+ return 0;
+ }
+
+ tx_ba_tbl = nxpwifi_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr);
+ if (tx_ba_tbl) {
+ nxpwifi_dbg(priv->adapter, EVENT, "info: BA stream complete\n");
+ tx_ba_tbl->ba_status = BA_SETUP_COMPLETE;
+ if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) &&
+ priv->add_ba_param.tx_amsdu &&
+ priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)
+ tx_ba_tbl->amsdu = true;
+ else
+ tx_ba_tbl->amsdu = false;
+ if (ra_list) {
+ ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu;
+ ra_list->ba_status = BA_SETUP_COMPLETE;
+ }
+ } else {
+ nxpwifi_dbg(priv->adapter, ERROR, "BA stream not created\n");
+ }
+
+ return 0;
+}
+
+/* This function prepares command of reconfigure Tx buffer.
+ *
+ * Preparation includes -
+ * - Setting command ID, action and proper size
+ * - Setting Tx buffer size (for SET only)
+ * - Ensuring correct endian-ness
+ */
+int nxpwifi_cmd_recfg_tx_buf(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd, int cmd_action,
+ u16 *buf_size)
+{
+ struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf;
+ u16 action = (u16)cmd_action;
+
+ cmd->command = cpu_to_le16(HOST_CMD_RECONFIGURE_TX_BUFF);
+ cmd->size =
+ cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN);
+ tx_buf->action = cpu_to_le16(action);
+ switch (action) {
+ case HOST_ACT_GEN_SET:
+ nxpwifi_dbg(priv->adapter, CMD,
+ "cmd: set tx_buf=%d\n", *buf_size);
+ tx_buf->buff_size = cpu_to_le16(*buf_size);
+ break;
+ case HOST_ACT_GEN_GET:
+ default:
+ tx_buf->buff_size = 0;
+ break;
+ }
+ return 0;
+}
+
+/* This function prepares command of AMSDU aggregation control.
+ *
+ * Preparation includes -
+ * - Setting command ID, action and proper size
+ * - Setting AMSDU control parameters (for SET only)
+ * - Ensuring correct endian-ness
+ */
+int nxpwifi_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
+ int cmd_action,
+ struct nxpwifi_ds_11n_amsdu_aggr_ctrl *aa_ctrl)
+{
+ struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl =
+ &cmd->params.amsdu_aggr_ctrl;
+ u16 action = (u16)cmd_action;
+
+ cmd->command = cpu_to_le16(HOST_CMD_AMSDU_AGGR_CTRL);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl)
+ + S_DS_GEN);
+ amsdu_ctrl->action = cpu_to_le16(action);
+ switch (action) {
+ case HOST_ACT_GEN_SET:
+ amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable);
+ amsdu_ctrl->curr_buf_size = 0;
+ break;
+ case HOST_ACT_GEN_GET:
+ default:
+ amsdu_ctrl->curr_buf_size = 0;
+ break;
+ }
+ return 0;
+}
+
+/* This function prepares 11n configuration command.
+ *
+ * Preparation includes -
+ * - Setting command ID, action and proper size
+ * - Setting HT Tx capability and HT Tx information fields
+ * - Ensuring correct endian-ness
+ */
+int nxpwifi_cmd_11n_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd, u16 cmd_action,
+ struct nxpwifi_ds_11n_tx_cfg *txcfg)
+{
+ struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg;
+
+ cmd->command = cpu_to_le16(HOST_CMD_11N_CFG);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN);
+ htcfg->action = cpu_to_le16(cmd_action);
+ htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap);
+ htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo);
+
+ if (priv->adapter->is_hw_11ac_capable)
+ htcfg->misc_config = cpu_to_le16(txcfg->misc_config);
+
+ return 0;
+}
+
+/* This function appends an 11n TLV to a buffer.
+ *
+ * Buffer allocation is responsibility of the calling
+ * function. No size validation is made here.
+ *
+ * The function fills up the following sections, if applicable -
+ * - HT capability IE
+ * - HT information IE (with channel list)
+ * - 20/40 BSS Coexistence IE
+ * - HT Extended Capabilities IE
+ */
+int
+nxpwifi_cmd_append_11n_tlv(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc,
+ u8 **buffer)
+{
+ struct nxpwifi_ie_types_htcap *ht_cap;
+ struct nxpwifi_ie_types_chan_list_param_set *chan_list;
+ struct nxpwifi_ie_types_2040bssco *bss_co_2040;
+ struct nxpwifi_ie_types_extcap *ext_cap;
+ int ret_len = 0;
+ struct ieee80211_supported_band *sband;
+ struct ieee_types_header *hdr;
+ u8 radio_type;
+
+ if (!buffer || !*buffer)
+ return ret_len;
+
+ radio_type = nxpwifi_band_to_radio_type((u8)bss_desc->bss_band);
+ sband = priv->wdev.wiphy->bands[radio_type];
+
+ if (bss_desc->bcn_ht_cap) {
+ ht_cap = (struct nxpwifi_ie_types_htcap *)*buffer;
+ memset(ht_cap, 0, sizeof(struct nxpwifi_ie_types_htcap));
+ ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
+ ht_cap->header.len =
+ cpu_to_le16(sizeof(struct ieee80211_ht_cap));
+ memcpy((u8 *)ht_cap + sizeof(struct nxpwifi_ie_types_header),
+ (u8 *)bss_desc->bcn_ht_cap,
+ le16_to_cpu(ht_cap->header.len));
+
+ nxpwifi_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
+ /* Update HT40 capability from current channel information */
+ if (bss_desc->bcn_ht_oper) {
+ u8 ht_param = bss_desc->bcn_ht_oper->ht_param;
+ u8 radio =
+ nxpwifi_band_to_radio_type(bss_desc->bss_band);
+ int freq =
+ ieee80211_channel_to_frequency(bss_desc->channel,
+ radio);
+ struct ieee80211_channel *chan =
+ ieee80211_get_channel(priv->adapter->wiphy, freq);
+
+ switch (ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) {
+ ht_cap->ht_cap.cap_info &=
+ cpu_to_le16
+ (~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+ ht_cap->ht_cap.cap_info &=
+ cpu_to_le16(~IEEE80211_HT_CAP_SGI_40);
+ }
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) {
+ ht_cap->ht_cap.cap_info &=
+ cpu_to_le16
+ (~IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+ ht_cap->ht_cap.cap_info &=
+ cpu_to_le16(~IEEE80211_HT_CAP_SGI_40);
+ }
+ break;
+ }
+ }
+
+ *buffer += sizeof(struct nxpwifi_ie_types_htcap);
+ ret_len += sizeof(struct nxpwifi_ie_types_htcap);
+ }
+
+ if (bss_desc->bcn_ht_oper) {
+ chan_list =
+ (struct nxpwifi_ie_types_chan_list_param_set *)*buffer;
+ memset(chan_list, 0, struct_size(chan_list, chan_scan_param, 1));
+ chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
+ chan_list->header.len =
+ cpu_to_le16(sizeof(struct nxpwifi_chan_scan_param_set));
+ chan_list->chan_scan_param[0].chan_number =
+ bss_desc->bcn_ht_oper->primary_chan;
+ chan_list->chan_scan_param[0].radio_type =
+ nxpwifi_band_to_radio_type((u8)bss_desc->bss_band);
+
+ if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
+ bss_desc->bcn_ht_oper->ht_param &
+ IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)
+ SET_SECONDARYCHAN
+ (chan_list->chan_scan_param[0].radio_type,
+ (bss_desc->bcn_ht_oper->ht_param &
+ IEEE80211_HT_PARAM_CHA_SEC_OFFSET));
+
+ *buffer += struct_size(chan_list, chan_scan_param, 1);
+ ret_len += struct_size(chan_list, chan_scan_param, 1);
+ }
+
+ if (bss_desc->bcn_bss_co_2040) {
+ bss_co_2040 = (struct nxpwifi_ie_types_2040bssco *)*buffer;
+ memset(bss_co_2040, 0,
+ sizeof(struct nxpwifi_ie_types_2040bssco));
+ bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040);
+ bss_co_2040->header.len =
+ cpu_to_le16(sizeof(bss_co_2040->bss_co_2040));
+
+ memcpy((u8 *)bss_co_2040 +
+ sizeof(struct nxpwifi_ie_types_header),
+ bss_desc->bcn_bss_co_2040 +
+ sizeof(struct ieee_types_header),
+ le16_to_cpu(bss_co_2040->header.len));
+
+ *buffer += sizeof(struct nxpwifi_ie_types_2040bssco);
+ ret_len += sizeof(struct nxpwifi_ie_types_2040bssco);
+ }
+
+ if (bss_desc->bcn_ext_cap) {
+ hdr = (void *)bss_desc->bcn_ext_cap;
+ ext_cap = (struct nxpwifi_ie_types_extcap *)*buffer;
+ memset(ext_cap, 0, sizeof(struct nxpwifi_ie_types_extcap));
+ ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY);
+ ext_cap->header.len = cpu_to_le16(hdr->len);
+
+ memcpy((u8 *)ext_cap->ext_capab,
+ bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header),
+ le16_to_cpu(ext_cap->header.len));
+
+ if (hdr->len > 3 &&
+ ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED)
+ priv->hs2_enabled = true;
+ else
+ priv->hs2_enabled = false;
+
+ *buffer += sizeof(struct nxpwifi_ie_types_extcap) + hdr->len;
+ ret_len += sizeof(struct nxpwifi_ie_types_extcap) + hdr->len;
+ }
+
+ return ret_len;
+}
+
+/* This function checks if the given pointer is valid entry of
+ * Tx BA Stream table.
+ */
+static int
+nxpwifi_is_tx_ba_stream_ptr_valid(struct nxpwifi_private *priv,
+ struct nxpwifi_tx_ba_stream_tbl *tx_tbl_ptr)
+{
+ struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl;
+
+ list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
+ if (tx_ba_tsr_tbl == tx_tbl_ptr)
+ return true;
+ }
+
+ return false;
+}
+
+/* This function deletes the given entry in Tx BA Stream table.
+ *
+ * The function also performs a validity check on the supplied
+ * pointer before trying to delete.
+ */
+void
+nxpwifi_11n_delete_tx_ba_stream_tbl_entry(struct nxpwifi_private *priv,
+ struct nxpwifi_tx_ba_stream_tbl *tbl)
+{
+ if (!tbl && nxpwifi_is_tx_ba_stream_ptr_valid(priv, tbl))
+ return;
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: tx_ba_tsr_tbl %p\n", tbl);
+
+ list_del(&tbl->list);
+
+ kfree(tbl);
+}
+
+/* This function deletes all the entries in Tx BA Stream table.
+ */
+void nxpwifi_11n_delete_all_tx_ba_stream_tbl(struct nxpwifi_private *priv)
+{
+ int i;
+ struct nxpwifi_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node;
+
+ spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
+ list_for_each_entry_safe(del_tbl_ptr, tmp_node,
+ &priv->tx_ba_stream_tbl_ptr, list)
+ nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr);
+ spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
+
+ INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
+
+ for (i = 0; i < MAX_NUM_TID; ++i)
+ priv->aggr_prio_tbl[i].ampdu_ap =
+ priv->aggr_prio_tbl[i].ampdu_user;
+}
+
+/* This function returns the pointer to an entry in BA Stream
+ * table which matches the given RA/TID pair.
+ */
+struct nxpwifi_tx_ba_stream_tbl *
+nxpwifi_get_ba_tbl(struct nxpwifi_private *priv, int tid, u8 *ra)
+{
+ struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl;
+
+ spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
+ list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
+ if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) &&
+ tx_ba_tsr_tbl->tid == tid) {
+ spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
+ return tx_ba_tsr_tbl;
+ }
+ }
+ spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
+ return NULL;
+}
+
+/* This function creates an entry in Tx BA stream table for the
+ * given RA/TID pair.
+ */
+void nxpwifi_create_ba_tbl(struct nxpwifi_private *priv, u8 *ra, int tid,
+ enum nxpwifi_ba_status ba_status)
+{
+ struct nxpwifi_tx_ba_stream_tbl *new_node;
+ struct nxpwifi_ra_list_tbl *ra_list;
+ int tid_down;
+
+ if (!nxpwifi_get_ba_tbl(priv, tid, ra)) {
+ new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC);
+ if (!new_node)
+ return;
+
+ tid_down = nxpwifi_wmm_downgrade_tid(priv, tid);
+ ra_list = nxpwifi_wmm_get_ralist_node(priv, tid_down, ra);
+ if (ra_list) {
+ ra_list->ba_status = ba_status;
+ ra_list->amsdu_in_ampdu = false;
+ }
+ INIT_LIST_HEAD(&new_node->list);
+
+ new_node->tid = tid;
+ new_node->ba_status = ba_status;
+ memcpy(new_node->ra, ra, ETH_ALEN);
+
+ spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
+ list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr);
+ spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
+ }
+}
+
+/* This function sends an add BA request to the given TID/RA pair.
+ */
+int nxpwifi_send_addba(struct nxpwifi_private *priv, int tid, u8 *peer_mac)
+{
+ struct host_cmd_ds_11n_addba_req add_ba_req;
+ u32 tx_win_size = priv->add_ba_param.tx_win_size;
+ static u8 dialog_tok;
+ int ret;
+ u16 block_ack_param_set;
+
+ nxpwifi_dbg(priv->adapter, CMD, "cmd: %s: tid %d\n", __func__, tid);
+
+ memset(&add_ba_req, 0, sizeof(add_ba_req));
+
+ block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) |
+ tx_win_size << BLOCKACKPARAM_WINSIZE_POS |
+ IMMEDIATE_BLOCK_ACK);
+
+ /* enable AMSDU inside AMPDU */
+ if (priv->add_ba_param.tx_amsdu &&
+ priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)
+ block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK;
+
+ add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set);
+ add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout);
+
+ ++dialog_tok;
+
+ if (dialog_tok == 0)
+ dialog_tok = 1;
+
+ add_ba_req.dialog_token = dialog_tok;
+ memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN);
+
+ /* We don't wait for the response of this command */
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_11N_ADDBA_REQ,
+ 0, 0, &add_ba_req, false);
+
+ return ret;
+}
+
+/* This function sends a delete BA request to the given TID/RA pair.
+ */
+int nxpwifi_send_delba(struct nxpwifi_private *priv, int tid, u8 *peer_mac,
+ int initiator)
+{
+ struct host_cmd_ds_11n_delba delba;
+ int ret;
+ u16 del_ba_param_set;
+
+ memset(&delba, 0, sizeof(delba));
+
+ del_ba_param_set = tid << DELBA_TID_POS;
+
+ if (initiator)
+ del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK;
+ else
+ del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK;
+
+ delba.del_ba_param_set = cpu_to_le16(del_ba_param_set);
+ memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN);
+
+ /* We don't wait for the response of this command */
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_11N_DELBA,
+ HOST_ACT_GEN_SET, 0, &delba, false);
+
+ return ret;
+}
+
+/* This function sends delba to specific tid
+ */
+void nxpwifi_11n_delba(struct nxpwifi_private *priv, int tid)
+{
+ struct nxpwifi_rx_reorder_tbl *rx_reor_tbl_ptr;
+
+ spin_lock_bh(&priv->rx_reorder_tbl_lock);
+ list_for_each_entry(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) {
+ if (rx_reor_tbl_ptr->tid == tid) {
+ dev_dbg(priv->adapter->dev,
+ "Send delba to tid=%d, %pM\n",
+ tid, rx_reor_tbl_ptr->ta);
+ nxpwifi_send_delba(priv, tid, rx_reor_tbl_ptr->ta, 0);
+ goto exit;
+ }
+ }
+exit:
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+}
+
+/* This function handles the command response of a delete BA request.
+ */
+void nxpwifi_11n_delete_ba_stream(struct nxpwifi_private *priv, u8 *del_ba)
+{
+ struct host_cmd_ds_11n_delba *cmd_del_ba =
+ (struct host_cmd_ds_11n_delba *)del_ba;
+ u16 del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set);
+ int tid;
+
+ tid = del_ba_param_set >> DELBA_TID_POS;
+
+ nxpwifi_del_ba_tbl(priv, tid, cmd_del_ba->peer_mac_addr,
+ TYPE_DELBA_RECEIVE, INITIATOR_BIT(del_ba_param_set));
+}
+
+/* This function retrieves the Rx reordering table.
+ */
+int nxpwifi_get_rx_reorder_tbl(struct nxpwifi_private *priv,
+ struct nxpwifi_ds_rx_reorder_tbl *buf)
+{
+ int i;
+ struct nxpwifi_ds_rx_reorder_tbl *rx_reo_tbl = buf;
+ struct nxpwifi_rx_reorder_tbl *rx_reorder_tbl_ptr;
+ int count = 0;
+
+ spin_lock_bh(&priv->rx_reorder_tbl_lock);
+ list_for_each_entry(rx_reorder_tbl_ptr, &priv->rx_reorder_tbl_ptr,
+ list) {
+ rx_reo_tbl->tid = (u16)rx_reorder_tbl_ptr->tid;
+ memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN);
+ rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win;
+ rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size;
+ for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) {
+ if (rx_reorder_tbl_ptr->rx_reorder_ptr[i])
+ rx_reo_tbl->buffer[i] = true;
+ else
+ rx_reo_tbl->buffer[i] = false;
+ }
+ rx_reo_tbl++;
+ count++;
+
+ if (count >= NXPWIFI_MAX_RX_BASTREAM_SUPPORTED)
+ break;
+ }
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+
+ return count;
+}
+
+/* This function retrieves the Tx BA stream table.
+ */
+int nxpwifi_get_tx_ba_stream_tbl(struct nxpwifi_private *priv,
+ struct nxpwifi_ds_tx_ba_stream_tbl *buf)
+{
+ struct nxpwifi_tx_ba_stream_tbl *tx_ba_tsr_tbl;
+ struct nxpwifi_ds_tx_ba_stream_tbl *rx_reo_tbl = buf;
+ int count = 0;
+
+ spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
+ list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
+ rx_reo_tbl->tid = (u16)tx_ba_tsr_tbl->tid;
+ nxpwifi_dbg(priv->adapter, DATA, "data: %s tid=%d\n",
+ __func__, rx_reo_tbl->tid);
+ memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN);
+ rx_reo_tbl->amsdu = tx_ba_tsr_tbl->amsdu;
+ rx_reo_tbl++;
+ count++;
+ if (count >= NXPWIFI_MAX_TX_BASTREAM_SUPPORTED)
+ break;
+ }
+ spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
+
+ return count;
+}
+
+/* This function retrieves the entry for specific tx BA stream table by RA and
+ * deletes it.
+ */
+void nxpwifi_del_tx_ba_stream_tbl_by_ra(struct nxpwifi_private *priv, u8 *ra)
+{
+ struct nxpwifi_tx_ba_stream_tbl *tbl, *tmp;
+
+ if (!ra)
+ return;
+
+ spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
+ list_for_each_entry_safe(tbl, tmp, &priv->tx_ba_stream_tbl_ptr, list)
+ if (!memcmp(tbl->ra, ra, ETH_ALEN))
+ nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, tbl);
+ spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
+}
+
+/* This function initializes the BlockACK setup information for given
+ * nxpwifi_private structure.
+ */
+void nxpwifi_set_ba_params(struct nxpwifi_private *priv)
+{
+ priv->add_ba_param.timeout = NXPWIFI_DEFAULT_BLOCK_ACK_TIMEOUT;
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
+ priv->add_ba_param.tx_win_size =
+ NXPWIFI_UAP_AMPDU_DEF_TXWINSIZE;
+ priv->add_ba_param.rx_win_size =
+ NXPWIFI_UAP_AMPDU_DEF_RXWINSIZE;
+ } else {
+ priv->add_ba_param.tx_win_size =
+ NXPWIFI_STA_AMPDU_DEF_TXWINSIZE;
+ priv->add_ba_param.rx_win_size =
+ NXPWIFI_STA_AMPDU_DEF_RXWINSIZE;
+ }
+
+ priv->add_ba_param.tx_amsdu = true;
+ priv->add_ba_param.rx_amsdu = true;
+}
+
+u8 nxpwifi_get_sec_chan_offset(int chan)
+{
+ u8 sec_offset;
+
+ switch (chan) {
+ case 36:
+ case 44:
+ case 52:
+ case 60:
+ case 100:
+ case 108:
+ case 116:
+ case 124:
+ case 132:
+ case 140:
+ case 149:
+ case 157:
+ sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ break;
+ case 40:
+ case 48:
+ case 56:
+ case 64:
+ case 104:
+ case 112:
+ case 120:
+ case 128:
+ case 136:
+ case 144:
+ case 153:
+ case 161:
+ sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ break;
+ case 165:
+ default:
+ sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ break;
+ }
+
+ return sec_offset;
+}
+
+/* This function will send DELBA to entries in the priv's
+ * Tx BA stream table
+ */
+static void
+nxpwifi_send_delba_txbastream_tbl(struct nxpwifi_private *priv, u8 tid)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_tx_ba_stream_tbl *tx_ba_stream_tbl_ptr;
+
+ list_for_each_entry(tx_ba_stream_tbl_ptr,
+ &priv->tx_ba_stream_tbl_ptr, list) {
+ if (tx_ba_stream_tbl_ptr->ba_status == BA_SETUP_COMPLETE) {
+ if (tid == tx_ba_stream_tbl_ptr->tid) {
+ dev_dbg(adapter->dev,
+ "Tx:Send delba to tid=%d, %pM\n", tid,
+ tx_ba_stream_tbl_ptr->ra);
+ nxpwifi_send_delba(priv,
+ tx_ba_stream_tbl_ptr->tid,
+ tx_ba_stream_tbl_ptr->ra, 1);
+ return;
+ }
+ }
+ }
+}
+
+/* This function updates all the tx_win_size
+ */
+void nxpwifi_update_ampdu_txwinsize(struct nxpwifi_adapter *adapter)
+{
+ u8 i, j;
+ u32 tx_win_size;
+ struct nxpwifi_private *priv;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (!adapter->priv[i])
+ continue;
+ priv = adapter->priv[i];
+ tx_win_size = priv->add_ba_param.tx_win_size;
+
+ if (priv->bss_type == NXPWIFI_BSS_TYPE_STA)
+ priv->add_ba_param.tx_win_size =
+ NXPWIFI_STA_AMPDU_DEF_TXWINSIZE;
+
+ if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP)
+ priv->add_ba_param.tx_win_size =
+ NXPWIFI_UAP_AMPDU_DEF_TXWINSIZE;
+
+ if (adapter->coex_win_size) {
+ if (adapter->coex_tx_win_size)
+ priv->add_ba_param.tx_win_size =
+ adapter->coex_tx_win_size;
+ }
+
+ if (tx_win_size != priv->add_ba_param.tx_win_size) {
+ if (!priv->media_connected)
+ continue;
+ for (j = 0; j < MAX_NUM_TID; j++)
+ nxpwifi_send_delba_txbastream_tbl(priv, j);
+ }
+ }
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 05/43] wifi: nxpwifi: add 11n.h
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (3 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 04/43] wifi: nxpwifi: add 11n.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 06/43] wifi: nxpwifi: add 11n_aggr.c David Lin
` (39 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/11n.h | 163 +++++++++++++++++++++++++
1 file changed, 163 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/11n.h
diff --git a/drivers/net/wireless/nxp/nxpwifi/11n.h b/drivers/net/wireless/nxp/nxpwifi/11n.h
new file mode 100644
index 000000000000..9d983aea3a39
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/11n.h
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NXP Wireless LAN device driver: 802.11n
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#ifndef _NXPWIFI_11N_H_
+#define _NXPWIFI_11N_H_
+
+#include "11n_aggr.h"
+#include "11n_rxreorder.h"
+#include "wmm.h"
+
+int nxpwifi_ret_11n_delba(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp);
+int nxpwifi_ret_11n_addba_req(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp);
+int nxpwifi_cmd_11n_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd, u16 cmd_action,
+ struct nxpwifi_ds_11n_tx_cfg *txcfg);
+int nxpwifi_cmd_append_11n_tlv(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc,
+ u8 **buffer);
+int nxpwifi_fill_cap_info(struct nxpwifi_private *priv, u8 radio_type,
+ struct ieee80211_ht_cap *ht_cap);
+int nxpwifi_set_get_11n_htcap_cfg(struct nxpwifi_private *priv,
+ u16 action, int *htcap_cfg);
+void nxpwifi_11n_delete_tx_ba_stream_tbl_entry(struct nxpwifi_private *priv,
+ struct nxpwifi_tx_ba_stream_tbl
+ *tx_tbl);
+void nxpwifi_11n_delete_all_tx_ba_stream_tbl(struct nxpwifi_private *priv);
+struct nxpwifi_tx_ba_stream_tbl *nxpwifi_get_ba_tbl(struct nxpwifi_private
+ *priv, int tid, u8 *ra);
+void nxpwifi_create_ba_tbl(struct nxpwifi_private *priv, u8 *ra, int tid,
+ enum nxpwifi_ba_status ba_status);
+int nxpwifi_send_addba(struct nxpwifi_private *priv, int tid, u8 *peer_mac);
+int nxpwifi_send_delba(struct nxpwifi_private *priv, int tid, u8 *peer_mac,
+ int initiator);
+void nxpwifi_11n_delete_ba_stream(struct nxpwifi_private *priv, u8 *del_ba);
+int nxpwifi_get_rx_reorder_tbl(struct nxpwifi_private *priv,
+ struct nxpwifi_ds_rx_reorder_tbl *buf);
+int nxpwifi_get_tx_ba_stream_tbl(struct nxpwifi_private *priv,
+ struct nxpwifi_ds_tx_ba_stream_tbl *buf);
+int nxpwifi_cmd_recfg_tx_buf(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ int cmd_action, u16 *buf_size);
+int nxpwifi_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
+ int cmd_action,
+ struct nxpwifi_ds_11n_amsdu_aggr_ctrl *aa_ctrl);
+void nxpwifi_del_tx_ba_stream_tbl_by_ra(struct nxpwifi_private *priv, u8 *ra);
+u8 nxpwifi_get_sec_chan_offset(int chan);
+
+static inline u8
+nxpwifi_is_station_ampdu_allowed(struct nxpwifi_private *priv,
+ struct nxpwifi_ra_list_tbl *ptr, int tid)
+{
+ struct nxpwifi_sta_node *node = nxpwifi_get_sta_entry(priv, ptr->ra);
+
+ if (unlikely(!node))
+ return false;
+
+ return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false;
+}
+
+/* This function checks whether AMPDU is allowed or not for a particular TID. */
+static inline u8
+nxpwifi_is_ampdu_allowed(struct nxpwifi_private *priv,
+ struct nxpwifi_ra_list_tbl *ptr, int tid)
+{
+ u8 ret;
+
+ if (is_broadcast_ether_addr(ptr->ra))
+ return false;
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP)
+ ret = nxpwifi_is_station_ampdu_allowed(priv, ptr, tid);
+ else
+ ret = (priv->aggr_prio_tbl[tid].ampdu_ap !=
+ BA_STREAM_NOT_ALLOWED) ? true : false;
+
+ return ret;
+}
+
+/* This function checks whether AMSDU is allowed or not for a particular TID.
+ */
+static inline u8
+nxpwifi_is_amsdu_allowed(struct nxpwifi_private *priv, int tid)
+{
+ return (((priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) &&
+ (priv->is_data_rate_auto || !(priv->bitmap_rates[2] & 0x03)))
+ ? true : false);
+}
+
+/* This function checks whether a space is available for new BA stream or not.
+ */
+static inline u8
+nxpwifi_space_avail_for_new_ba_stream(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_private *priv;
+ u8 i;
+ size_t ba_stream_num = 0, ba_stream_max;
+
+ ba_stream_max = NXPWIFI_MAX_TX_BASTREAM_SUPPORTED;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (priv)
+ ba_stream_num +=
+ list_count_nodes(&priv->tx_ba_stream_tbl_ptr);
+ }
+
+ if (adapter->fw_api_ver == NXPWIFI_FW_V15) {
+ ba_stream_max =
+ GETSUPP_TXBASTREAMS(adapter->hw_dot_11n_dev_cap);
+ if (!ba_stream_max)
+ ba_stream_max = NXPWIFI_MAX_TX_BASTREAM_SUPPORTED;
+ }
+
+ return ((ba_stream_num < ba_stream_max) ? true : false);
+}
+
+/* This function finds the correct Tx BA stream to delete.
+ *
+ * Upon successfully locating, both the TID and the RA are returned.
+ */
+static inline u8
+nxpwifi_find_stream_to_delete(struct nxpwifi_private *priv, int ptr_tid,
+ int *ptid, u8 *ra)
+{
+ int tid;
+ u8 ret = false;
+ struct nxpwifi_tx_ba_stream_tbl *tx_tbl;
+
+ tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user;
+
+ spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
+ list_for_each_entry(tx_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
+ if (tid > priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user) {
+ tid = priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user;
+ *ptid = tx_tbl->tid;
+ memcpy(ra, tx_tbl->ra, ETH_ALEN);
+ ret = true;
+ }
+ }
+ spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
+
+ return ret;
+}
+
+/* This function checks whether associated station is 11n enabled
+ */
+static inline int nxpwifi_is_sta_11n_enabled(struct nxpwifi_private *priv,
+ struct nxpwifi_sta_node *node)
+{
+ if (!node || (priv->bss_role == NXPWIFI_BSS_ROLE_UAP &&
+ !priv->ap_11n_enabled))
+ return 0;
+
+ return node->is_11n_enabled;
+}
+
+#endif /* !_NXPWIFI_11N_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 06/43] wifi: nxpwifi: add 11n_aggr.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (4 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 05/43] wifi: nxpwifi: add 11n.h David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 07/43] wifi: nxpwifi: add 11n_aggr.h David Lin
` (38 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/11n_aggr.c | 278 ++++++++++++++++++++
1 file changed, 278 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/11n_aggr.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/11n_aggr.c b/drivers/net/wireless/nxp/nxpwifi/11n_aggr.c
new file mode 100644
index 000000000000..0169c8d910a4
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/11n_aggr.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: 802.11n Aggregation
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+#include "11n_aggr.h"
+
+/* Creates an AMSDU subframe for aggregation into one AMSDU packet.
+ *
+ * The resultant AMSDU subframe format is -
+ *
+ * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+
+ * | DA | SA | Length | SNAP header | MSDU |
+ * | data[0..5] | data[6..11] | | | data[14..] |
+ * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+
+ * <--6-bytes--> <--6-bytes--> <--2-bytes--><--8-bytes--> <--n-bytes-->
+ *
+ * This function also computes the amount of padding required to make the
+ * buffer length multiple of 4 bytes.
+ *
+ * Data => |DA|SA|SNAP-TYPE|........ .|
+ * MSDU => |DA|SA|Length|SNAP|...... ..|
+ */
+static int
+nxpwifi_11n_form_amsdu_pkt(struct sk_buff *skb_aggr,
+ struct sk_buff *skb_src, int *pad)
+
+{
+ int dt_offset;
+ struct rfc_1042_hdr snap = {
+ 0xaa, /* LLC DSAP */
+ 0xaa, /* LLC SSAP */
+ 0x03, /* LLC CTRL */
+ {0x00, 0x00, 0x00}, /* SNAP OUI */
+ 0x0000 /* SNAP type */
+ /* This field will be overwritten
+ * later with ethertype
+ */
+ };
+ struct tx_packet_hdr *tx_header;
+
+ tx_header = skb_put(skb_aggr, sizeof(*tx_header));
+
+ /* Copy DA and SA */
+ dt_offset = 2 * ETH_ALEN;
+ memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset);
+
+ /* Copy SNAP header */
+ snap.snap_type = ((struct ethhdr *)skb_src->data)->h_proto;
+
+ dt_offset += sizeof(__be16);
+
+ memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr));
+
+ skb_pull(skb_src, dt_offset);
+
+ /* Update Length field */
+ tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN);
+
+ /* Add payload */
+ skb_put_data(skb_aggr, skb_src->data, skb_src->len);
+
+ /* Add padding for new MSDU to start from 4 byte boundary */
+ *pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4;
+
+ return skb_aggr->len + *pad;
+}
+
+/* Adds TxPD to AMSDU header.
+ *
+ * Each AMSDU packet will contain one TxPD at the beginning,
+ * followed by multiple AMSDU subframes.
+ */
+static void
+nxpwifi_11n_form_amsdu_txpd(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct txpd *local_tx_pd;
+
+ skb_push(skb, sizeof(*local_tx_pd));
+
+ local_tx_pd = (struct txpd *)skb->data;
+ memset(local_tx_pd, 0, sizeof(struct txpd));
+
+ /* Original priority has been overwritten */
+ local_tx_pd->priority = (u8)skb->priority;
+ local_tx_pd->pkt_delay_2ms =
+ nxpwifi_wmm_compute_drv_pkt_delay(priv, skb);
+ local_tx_pd->bss_num = priv->bss_num;
+ local_tx_pd->bss_type = priv->bss_type;
+ /* Always zero as the data is followed by struct txpd */
+ local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd));
+ local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU);
+ local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len -
+ sizeof(*local_tx_pd));
+
+ if (local_tx_pd->tx_control == 0)
+ /* TxCtrl set by user or default */
+ local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA &&
+ priv->adapter->pps_uapsd_mode) {
+ if (nxpwifi_check_last_packet_indication(priv)) {
+ priv->adapter->tx_lock_flag = true;
+ local_tx_pd->flags =
+ NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET;
+ }
+ }
+}
+
+/* Create aggregated packet.
+ *
+ * This function creates an aggregated MSDU packet, by combining buffers
+ * from the RA list. Each individual buffer is encapsulated as an AMSDU
+ * subframe and all such subframes are concatenated together to form the
+ * AMSDU packet.
+ *
+ * A TxPD is also added to the front of the resultant AMSDU packets for
+ * transmission. The resultant packets format is -
+ *
+ * +---- ~ ----+------ ~ ------+------ ~ ------+-..-+------ ~ ------+
+ * | TxPD |AMSDU sub-frame|AMSDU sub-frame| .. |AMSDU sub-frame|
+ * | | 1 | 2 | .. | n |
+ * +---- ~ ----+------ ~ ------+------ ~ ------+ .. +------ ~ ------+
+ */
+int
+nxpwifi_11n_aggregate_pkt(struct nxpwifi_private *priv,
+ struct nxpwifi_ra_list_tbl *pra_list,
+ int ptrindex)
+ __releases(&priv->wmm.ra_list_spinlock)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct sk_buff *skb_aggr, *skb_src;
+ struct nxpwifi_txinfo *tx_info_aggr, *tx_info_src;
+ int pad = 0, aggr_num = 0, ret;
+ struct nxpwifi_tx_param tx_param;
+ struct txpd *ptx_pd = NULL;
+ int headroom = adapter->intf_hdr_len;
+
+ skb_src = skb_peek(&pra_list->skb_head);
+ if (!skb_src) {
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ return 0;
+ }
+
+ tx_info_src = NXPWIFI_SKB_TXCB(skb_src);
+ skb_aggr = nxpwifi_alloc_dma_align_buf(adapter->tx_buf_size,
+ GFP_ATOMIC);
+ if (!skb_aggr) {
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ return -1;
+ }
+
+ /* skb_aggr->data already 64 byte align, just reserve bus interface
+ * header and txpd.
+ */
+ skb_reserve(skb_aggr, headroom + sizeof(struct txpd));
+ tx_info_aggr = NXPWIFI_SKB_TXCB(skb_aggr);
+
+ memset(tx_info_aggr, 0, sizeof(*tx_info_aggr));
+ tx_info_aggr->bss_type = tx_info_src->bss_type;
+ tx_info_aggr->bss_num = tx_info_src->bss_num;
+
+ tx_info_aggr->flags |= NXPWIFI_BUF_FLAG_AGGR_PKT;
+ skb_aggr->priority = skb_src->priority;
+ skb_aggr->tstamp = skb_src->tstamp;
+
+ do {
+ /* Check if AMSDU can accommodate this MSDU */
+ if ((skb_aggr->len + skb_src->len + LLC_SNAP_LEN) >
+ adapter->tx_buf_size)
+ break;
+
+ skb_src = skb_dequeue(&pra_list->skb_head);
+ pra_list->total_pkt_count--;
+ atomic_dec(&priv->wmm.tx_pkts_queued);
+ aggr_num++;
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ nxpwifi_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad);
+
+ nxpwifi_write_data_complete(adapter, skb_src, 0, 0);
+
+ spin_lock_bh(&priv->wmm.ra_list_spinlock);
+
+ if (!nxpwifi_is_ralist_valid(priv, pra_list, ptrindex)) {
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ return -1;
+ }
+
+ if (skb_tailroom(skb_aggr) < pad) {
+ pad = 0;
+ break;
+ }
+ skb_put(skb_aggr, pad);
+
+ skb_src = skb_peek(&pra_list->skb_head);
+
+ } while (skb_src);
+
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+
+ /* Last AMSDU packet does not need padding */
+ skb_trim(skb_aggr, skb_aggr->len - pad);
+
+ /* Form AMSDU */
+ nxpwifi_11n_form_amsdu_txpd(priv, skb_aggr);
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA)
+ ptx_pd = (struct txpd *)skb_aggr->data;
+
+ skb_push(skb_aggr, headroom);
+ tx_info_aggr->aggr_num = aggr_num * 2;
+ if (adapter->data_sent || adapter->tx_lock_flag) {
+ atomic_add(aggr_num * 2, &adapter->tx_queued);
+ skb_queue_tail(&adapter->tx_data_q, skb_aggr);
+ return 0;
+ }
+
+ if (skb_src)
+ tx_param.next_pkt_len = skb_src->len + sizeof(struct txpd);
+ else
+ tx_param.next_pkt_len = 0;
+
+ ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_DATA,
+ skb_aggr, &tx_param);
+
+ switch (ret) {
+ case -EBUSY:
+ spin_lock_bh(&priv->wmm.ra_list_spinlock);
+ if (!nxpwifi_is_ralist_valid(priv, pra_list, ptrindex)) {
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ nxpwifi_write_data_complete(adapter, skb_aggr, 1, -1);
+ return -1;
+ }
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA &&
+ adapter->pps_uapsd_mode && adapter->tx_lock_flag) {
+ priv->adapter->tx_lock_flag = false;
+ if (ptx_pd)
+ ptx_pd->flags = 0;
+ }
+
+ skb_queue_tail(&pra_list->skb_head, skb_aggr);
+
+ pra_list->total_pkt_count++;
+
+ atomic_inc(&priv->wmm.tx_pkts_queued);
+
+ tx_info_aggr->flags |= NXPWIFI_BUF_FLAG_REQUEUED_PKT;
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ nxpwifi_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
+ break;
+ case -1:
+ nxpwifi_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n",
+ __func__, ret);
+ adapter->dbg.num_tx_host_to_card_failure++;
+ nxpwifi_write_data_complete(adapter, skb_aggr, 1, ret);
+ return 0;
+ case -EINPROGRESS:
+ break;
+ case 0:
+ nxpwifi_write_data_complete(adapter, skb_aggr, 1, ret);
+ break;
+ default:
+ break;
+ }
+ if (ret != -EBUSY)
+ nxpwifi_rotate_priolists(priv, pra_list, ptrindex);
+
+ return 0;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 07/43] wifi: nxpwifi: add 11n_aggr.h
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (5 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 06/43] wifi: nxpwifi: add 11n_aggr.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 08/43] wifi: nxpwifi: add 11n_rxreorder.c David Lin
` (37 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/11n_aggr.h | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/11n_aggr.h
diff --git a/drivers/net/wireless/nxp/nxpwifi/11n_aggr.h b/drivers/net/wireless/nxp/nxpwifi/11n_aggr.h
new file mode 100644
index 000000000000..be9f0f8f4e48
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/11n_aggr.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NXP Wireless LAN device driver: 802.11n Aggregation
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#ifndef _NXPWIFI_11N_AGGR_H_
+#define _NXPWIFI_11N_AGGR_H_
+
+#define PKT_TYPE_AMSDU 0xE6
+#define MIN_NUM_AMSDU 2
+
+int nxpwifi_11n_deaggregate_pkt(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+int nxpwifi_11n_aggregate_pkt(struct nxpwifi_private *priv,
+ struct nxpwifi_ra_list_tbl *ptr,
+ int ptr_index)
+ __releases(&priv->wmm.ra_list_spinlock);
+
+#endif /* !_NXPWIFI_11N_AGGR_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 08/43] wifi: nxpwifi: add 11n_rxreorder.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (6 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 07/43] wifi: nxpwifi: add 11n_aggr.h David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 09/43] wifi: nxpwifi: add 11n_rxreorder.h David Lin
` (36 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
.../net/wireless/nxp/nxpwifi/11n_rxreorder.c | 928 ++++++++++++++++++
1 file changed, 928 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c b/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c
new file mode 100644
index 000000000000..20faab3d6994
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c
@@ -0,0 +1,928 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: 802.11n RX Re-ordering
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cmdevt.h"
+#include "wmm.h"
+#include "11n.h"
+#include "11n_rxreorder.h"
+
+/* This function will dispatch amsdu packet and forward it to kernel/upper
+ * layer.
+ */
+static int nxpwifi_11n_dispatch_amsdu_pkt(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct rxpd *local_rx_pd = (struct rxpd *)(skb->data);
+ int ret;
+
+ if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) {
+ struct sk_buff_head list;
+ struct sk_buff *rx_skb;
+
+ __skb_queue_head_init(&list);
+
+ skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset));
+ skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
+
+ ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
+ priv->wdev.iftype, 0, NULL, NULL, false);
+
+ while (!skb_queue_empty(&list)) {
+ struct rx_packet_hdr *rx_hdr;
+
+ rx_skb = __skb_dequeue(&list);
+ rx_hdr = (struct rx_packet_hdr *)rx_skb->data;
+
+ if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP)
+ ret = nxpwifi_uap_recv_packet(priv, rx_skb);
+ else
+ ret = nxpwifi_recv_packet(priv, rx_skb);
+ if (ret == -1)
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Rx of A-MSDU failed");
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+/* This function will process the rx packet and forward it to kernel/upper
+ * layer.
+ */
+static int nxpwifi_11n_dispatch_pkt(struct nxpwifi_private *priv,
+ struct sk_buff *payload)
+{
+ int ret;
+
+ if (!payload) {
+ nxpwifi_dbg(priv->adapter, INFO, "info: fw drop data\n");
+ return 0;
+ }
+
+ ret = nxpwifi_11n_dispatch_amsdu_pkt(priv, payload);
+ if (!ret)
+ return 0;
+
+ if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP)
+ return nxpwifi_handle_uap_rx_forward(priv, payload);
+
+ return nxpwifi_process_rx_packet(priv, payload);
+}
+
+/* This function dispatches all packets in the Rx reorder table until the
+ * start window.
+ *
+ * There could be holes in the buffer, which are skipped by the function.
+ * Since the buffer is linear, the function uses rotation to simulate
+ * circular buffer.
+ */
+static void
+nxpwifi_11n_dispatch_pkt_until_start_win(struct nxpwifi_private *priv,
+ struct nxpwifi_rx_reorder_tbl *tbl,
+ int start_win)
+{
+ struct sk_buff_head list;
+ struct sk_buff *skb;
+ int pkt_to_send, i;
+
+ __skb_queue_head_init(&list);
+ spin_lock_bh(&priv->rx_reorder_tbl_lock);
+
+ pkt_to_send = (start_win > tbl->start_win) ?
+ min((start_win - tbl->start_win), tbl->win_size) :
+ tbl->win_size;
+
+ for (i = 0; i < pkt_to_send; ++i) {
+ if (tbl->rx_reorder_ptr[i]) {
+ skb = tbl->rx_reorder_ptr[i];
+ __skb_queue_tail(&list, skb);
+ tbl->rx_reorder_ptr[i] = NULL;
+ }
+ }
+
+ /* We don't have a circular buffer, hence use rotation to simulate
+ * circular buffer
+ */
+ for (i = 0; i < tbl->win_size - pkt_to_send; ++i) {
+ tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i];
+ tbl->rx_reorder_ptr[pkt_to_send + i] = NULL;
+ }
+
+ tbl->start_win = start_win;
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+
+ while ((skb = __skb_dequeue(&list)))
+ nxpwifi_11n_dispatch_pkt(priv, skb);
+}
+
+/* This function dispatches all packets in the Rx reorder table until
+ * a hole is found.
+ *
+ * The start window is adjusted automatically when a hole is located.
+ * Since the buffer is linear, the function uses rotation to simulate
+ * circular buffer.
+ */
+static void
+nxpwifi_11n_scan_and_dispatch(struct nxpwifi_private *priv,
+ struct nxpwifi_rx_reorder_tbl *tbl)
+{
+ struct sk_buff_head list;
+ struct sk_buff *skb;
+ int i, j, xchg;
+
+ __skb_queue_head_init(&list);
+ spin_lock_bh(&priv->rx_reorder_tbl_lock);
+
+ for (i = 0; i < tbl->win_size; ++i) {
+ if (!tbl->rx_reorder_ptr[i])
+ break;
+ skb = tbl->rx_reorder_ptr[i];
+ __skb_queue_tail(&list, skb);
+ tbl->rx_reorder_ptr[i] = NULL;
+ }
+
+ /* We don't have a circular buffer, hence use rotation to simulate
+ * circular buffer
+ */
+ if (i > 0) {
+ xchg = tbl->win_size - i;
+ for (j = 0; j < xchg; ++j) {
+ tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j];
+ tbl->rx_reorder_ptr[i + j] = NULL;
+ }
+ }
+ tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1);
+
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+
+ while ((skb = __skb_dequeue(&list)))
+ nxpwifi_11n_dispatch_pkt(priv, skb);
+}
+
+/* This function deletes the Rx reorder table and frees the memory.
+ *
+ * The function stops the associated timer and dispatches all the
+ * pending packets in the Rx reorder table before deletion.
+ */
+static void
+nxpwifi_del_rx_reorder_entry(struct nxpwifi_private *priv,
+ struct nxpwifi_rx_reorder_tbl *tbl)
+{
+ int start_win;
+
+ if (!tbl)
+ return;
+
+ spin_lock_bh(&priv->adapter->rx_proc_lock);
+ priv->adapter->rx_locked = true;
+ if (priv->adapter->rx_processing) {
+ spin_unlock_bh(&priv->adapter->rx_proc_lock);
+ flush_workqueue(priv->adapter->rx_workqueue);
+ } else {
+ spin_unlock_bh(&priv->adapter->rx_proc_lock);
+ }
+
+ start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1);
+ nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
+
+ del_timer_sync(&tbl->timer_context.timer);
+ tbl->timer_context.timer_is_set = false;
+
+ spin_lock_bh(&priv->rx_reorder_tbl_lock);
+ list_del(&tbl->list);
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+
+ kfree(tbl->rx_reorder_ptr);
+ kfree(tbl);
+
+ spin_lock_bh(&priv->adapter->rx_proc_lock);
+ priv->adapter->rx_locked = false;
+ spin_unlock_bh(&priv->adapter->rx_proc_lock);
+}
+
+/* This function returns the pointer to an entry in Rx reordering
+ * table which matches the given TA/TID pair.
+ */
+struct nxpwifi_rx_reorder_tbl *
+nxpwifi_11n_get_rx_reorder_tbl(struct nxpwifi_private *priv, int tid, u8 *ta)
+{
+ struct nxpwifi_rx_reorder_tbl *tbl;
+
+ spin_lock_bh(&priv->rx_reorder_tbl_lock);
+ list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) {
+ if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) {
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+ return tbl;
+ }
+ }
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+
+ return NULL;
+}
+
+/* This function retrieves the pointer to an entry in Rx reordering
+ * table which matches the given TA and deletes it.
+ */
+void nxpwifi_11n_del_rx_reorder_tbl_by_ta(struct nxpwifi_private *priv, u8 *ta)
+{
+ struct nxpwifi_rx_reorder_tbl *tbl, *tmp;
+
+ if (!ta)
+ return;
+
+ spin_lock_bh(&priv->rx_reorder_tbl_lock);
+ list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) {
+ if (!memcmp(tbl->ta, ta, ETH_ALEN)) {
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+ nxpwifi_del_rx_reorder_entry(priv, tbl);
+ spin_lock_bh(&priv->rx_reorder_tbl_lock);
+ }
+ }
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+}
+
+/* This function finds the last sequence number used in the packets
+ * buffered in Rx reordering table.
+ */
+static int
+nxpwifi_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx)
+{
+ struct nxpwifi_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr;
+ struct nxpwifi_private *priv = ctx->priv;
+ int i;
+
+ spin_lock_bh(&priv->rx_reorder_tbl_lock);
+ for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) {
+ if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) {
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+ return i;
+ }
+ }
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+
+ return -1;
+}
+
+/* This function flushes all the packets in Rx reordering table.
+ *
+ * The function checks if any packets are currently buffered in the
+ * table or not. In case there are packets available, it dispatches
+ * them and then dumps the Rx reordering table.
+ */
+static void
+nxpwifi_flush_data(struct timer_list *t)
+{
+ struct reorder_tmr_cnxt *ctx =
+ from_timer(ctx, t, timer);
+ int start_win, seq_num;
+
+ ctx->timer_is_set = false;
+ seq_num = nxpwifi_11n_find_last_seq_num(ctx);
+
+ if (seq_num < 0)
+ return;
+
+ nxpwifi_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num);
+ start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1);
+ nxpwifi_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr,
+ start_win);
+}
+
+/* This function creates an entry in Rx reordering table for the
+ * given TA/TID.
+ *
+ * The function also initializes the entry with sequence number, window
+ * size as well as initializes the timer.
+ *
+ * If the received TA/TID pair is already present, all the packets are
+ * dispatched and the window size is moved until the SSN.
+ */
+static void
+nxpwifi_11n_create_rx_reorder_tbl(struct nxpwifi_private *priv, u8 *ta,
+ int tid, int win_size, int seq_num)
+{
+ int i;
+ struct nxpwifi_rx_reorder_tbl *tbl, *new_node;
+ u16 last_seq = 0;
+ struct nxpwifi_sta_node *node;
+
+ /* If we get a TID, ta pair which is already present dispatch all
+ * the packets and move the window size until the ssn
+ */
+ tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, ta);
+ if (tbl) {
+ nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num);
+ return;
+ }
+ /* if !tbl then create one */
+ new_node = kzalloc(sizeof(*new_node), GFP_KERNEL);
+ if (!new_node)
+ return;
+
+ INIT_LIST_HEAD(&new_node->list);
+ new_node->tid = tid;
+ memcpy(new_node->ta, ta, ETH_ALEN);
+ new_node->start_win = seq_num;
+ new_node->init_win = seq_num;
+ new_node->flags = 0;
+
+ spin_lock_bh(&priv->sta_list_spinlock);
+ if (nxpwifi_queuing_ra_based(priv)) {
+ if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP) {
+ node = nxpwifi_get_sta_entry(priv, ta);
+ if (node)
+ last_seq = node->rx_seq[tid];
+ }
+ } else {
+ node = nxpwifi_get_sta_entry(priv, ta);
+ if (node)
+ last_seq = node->rx_seq[tid];
+ else
+ last_seq = priv->rx_seq[tid];
+ }
+ spin_unlock_bh(&priv->sta_list_spinlock);
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: last_seq=%d start_win=%d\n",
+ last_seq, new_node->start_win);
+
+ if (last_seq != NXPWIFI_DEF_11N_RX_SEQ_NUM &&
+ last_seq >= new_node->start_win) {
+ new_node->start_win = last_seq + 1;
+ new_node->flags |= RXREOR_INIT_WINDOW_SHIFT;
+ }
+
+ new_node->win_size = win_size;
+
+ new_node->rx_reorder_ptr = kcalloc(win_size, sizeof(void *),
+ GFP_KERNEL);
+ if (!new_node->rx_reorder_ptr) {
+ kfree(new_node);
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "%s: failed to alloc reorder_ptr\n", __func__);
+ return;
+ }
+
+ new_node->timer_context.ptr = new_node;
+ new_node->timer_context.priv = priv;
+ new_node->timer_context.timer_is_set = false;
+
+ timer_setup(&new_node->timer_context.timer, nxpwifi_flush_data, 0);
+
+ for (i = 0; i < win_size; ++i)
+ new_node->rx_reorder_ptr[i] = NULL;
+
+ spin_lock_bh(&priv->rx_reorder_tbl_lock);
+ list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr);
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+}
+
+static void
+nxpwifi_11n_rxreorder_timer_restart(struct nxpwifi_rx_reorder_tbl *tbl)
+{
+ u32 min_flush_time;
+
+ if (tbl->win_size >= NXPWIFI_BA_WIN_SIZE_32)
+ min_flush_time = MIN_FLUSH_TIMER_15_MS;
+ else
+ min_flush_time = MIN_FLUSH_TIMER_MS;
+
+ mod_timer(&tbl->timer_context.timer,
+ jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size));
+
+ tbl->timer_context.timer_is_set = true;
+}
+
+/* This function prepares command for adding a BA request.
+ *
+ * Preparation includes -
+ * - Setting command ID and proper size
+ * - Setting add BA request buffer
+ * - Ensuring correct endian-ness
+ */
+int nxpwifi_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf)
+{
+ struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req;
+
+ cmd->command = cpu_to_le16(HOST_CMD_11N_ADDBA_REQ);
+ cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN);
+ memcpy(add_ba_req, data_buf, sizeof(*add_ba_req));
+
+ return 0;
+}
+
+/* This function prepares command for adding a BA response.
+ *
+ * Preparation includes -
+ * - Setting command ID and proper size
+ * - Setting add BA response buffer
+ * - Ensuring correct endian-ness
+ */
+int nxpwifi_cmd_11n_addba_rsp_gen(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct host_cmd_ds_11n_addba_req
+ *cmd_addba_req)
+{
+ struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp;
+ u32 rx_win_size = priv->add_ba_param.rx_win_size;
+ u8 tid;
+ int win_size;
+ u16 block_ack_param_set;
+
+ cmd->command = cpu_to_le16(HOST_CMD_11N_ADDBA_RSP);
+ cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN);
+
+ memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr,
+ ETH_ALEN);
+ add_ba_rsp->dialog_token = cmd_addba_req->dialog_token;
+ add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo;
+ add_ba_rsp->ssn = cmd_addba_req->ssn;
+
+ block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set);
+ tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
+ >> BLOCKACKPARAM_TID_POS;
+ add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT);
+ block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK;
+
+ /* If we don't support AMSDU inside AMPDU, reset the bit */
+ if (!priv->add_ba_param.rx_amsdu ||
+ priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED)
+ block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK;
+ block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS;
+ add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set);
+ win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set)
+ & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
+ >> BLOCKACKPARAM_WINSIZE_POS;
+ cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set);
+
+ nxpwifi_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr,
+ tid, win_size,
+ le16_to_cpu(cmd_addba_req->ssn));
+ return 0;
+}
+
+/* This function prepares command for deleting a BA request.
+ *
+ * Preparation includes -
+ * - Setting command ID and proper size
+ * - Setting del BA request buffer
+ * - Ensuring correct endian-ness
+ */
+int nxpwifi_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf)
+{
+ struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba;
+
+ cmd->command = cpu_to_le16(HOST_CMD_11N_DELBA);
+ cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN);
+ memcpy(del_ba, data_buf, sizeof(*del_ba));
+
+ return 0;
+}
+
+/* This function identifies if Rx reordering is needed for a received packet.
+ *
+ * In case reordering is required, the function will do the reordering
+ * before sending it to kernel.
+ *
+ * The Rx reorder table is checked first with the received TID/TA pair. If
+ * not found, the received packet is dispatched immediately. But if found,
+ * the packet is reordered and all the packets in the updated Rx reordering
+ * table is dispatched until a hole is found.
+ *
+ * For sequence number less than the starting window, the packet is dropped.
+ */
+int nxpwifi_11n_rx_reorder_pkt(struct nxpwifi_private *priv,
+ u16 seq_num, u16 tid,
+ u8 *ta, u8 pkt_type, void *payload)
+{
+ struct nxpwifi_rx_reorder_tbl *tbl;
+ int prev_start_win, start_win, end_win, win_size;
+ u16 pkt_index;
+ bool init_window_shift = false;
+ int ret = 0;
+
+ tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, ta);
+ if (!tbl) {
+ if (pkt_type != PKT_TYPE_BAR)
+ nxpwifi_11n_dispatch_pkt(priv, payload);
+ return ret;
+ }
+
+ if (pkt_type == PKT_TYPE_AMSDU && !tbl->amsdu) {
+ nxpwifi_11n_dispatch_pkt(priv, payload);
+ return ret;
+ }
+
+ start_win = tbl->start_win;
+ prev_start_win = start_win;
+ win_size = tbl->win_size;
+ end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
+ if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) {
+ init_window_shift = true;
+ tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT;
+ }
+
+ if (tbl->flags & RXREOR_FORCE_NO_DROP) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "RXREOR_FORCE_NO_DROP when HS is activated\n");
+ tbl->flags &= ~RXREOR_FORCE_NO_DROP;
+ } else if (init_window_shift && seq_num < start_win &&
+ seq_num >= tbl->init_win) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "Sender TID sequence number reset %d->%d for SSN %d\n",
+ start_win, seq_num, tbl->init_win);
+ start_win = seq_num;
+ tbl->start_win = start_win;
+ end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
+ } else {
+ /* If seq_num is less then starting win then ignore and drop
+ * the packet
+ */
+ if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {
+ if (seq_num >= ((start_win + TWOPOW11) &
+ (MAX_TID_VALUE - 1)) &&
+ seq_num < start_win) {
+ ret = -1;
+ goto done;
+ }
+ } else if ((seq_num < start_win) ||
+ (seq_num >= (start_win + TWOPOW11))) {
+ ret = -1;
+ goto done;
+ }
+ }
+
+ /* If this packet is a BAR we adjust seq_num as
+ * WinStart = seq_num
+ */
+ if (pkt_type == PKT_TYPE_BAR)
+ seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1);
+
+ if ((end_win < start_win &&
+ seq_num < start_win && seq_num > end_win) ||
+ (end_win > start_win && (seq_num > end_win ||
+ seq_num < start_win))) {
+ end_win = seq_num;
+ if (((end_win - win_size) + 1) >= 0)
+ start_win = (end_win - win_size) + 1;
+ else
+ start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1;
+ nxpwifi_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
+ }
+
+ if (pkt_type != PKT_TYPE_BAR) {
+ if (seq_num >= start_win)
+ pkt_index = seq_num - start_win;
+ else
+ pkt_index = (seq_num + MAX_TID_VALUE) - start_win;
+
+ if (tbl->rx_reorder_ptr[pkt_index]) {
+ ret = -1;
+ goto done;
+ }
+
+ tbl->rx_reorder_ptr[pkt_index] = payload;
+ }
+
+ /* Dispatch all packets sequentially from start_win until a
+ * hole is found and adjust the start_win appropriately
+ */
+ nxpwifi_11n_scan_and_dispatch(priv, tbl);
+
+done:
+ if (!tbl->timer_context.timer_is_set ||
+ prev_start_win != tbl->start_win)
+ nxpwifi_11n_rxreorder_timer_restart(tbl);
+ return ret;
+}
+
+/* This function deletes an entry for a given TID/TA pair.
+ *
+ * The TID/TA are taken from del BA event body.
+ */
+void
+nxpwifi_del_ba_tbl(struct nxpwifi_private *priv, int tid, u8 *peer_mac,
+ u8 type, int initiator)
+{
+ struct nxpwifi_rx_reorder_tbl *tbl;
+ struct nxpwifi_tx_ba_stream_tbl *ptx_tbl;
+ struct nxpwifi_ra_list_tbl *ra_list;
+ u8 cleanup_rx_reorder_tbl;
+ int tid_down;
+
+ if (type == TYPE_DELBA_RECEIVE)
+ cleanup_rx_reorder_tbl = (initiator) ? true : false;
+ else
+ cleanup_rx_reorder_tbl = (initiator) ? false : true;
+
+ nxpwifi_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=%d initiator=%d\n",
+ peer_mac, tid, initiator);
+
+ if (cleanup_rx_reorder_tbl) {
+ tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid, peer_mac);
+ if (!tbl) {
+ nxpwifi_dbg(priv->adapter, EVENT,
+ "event: TID, TA not found in table\n");
+ return;
+ }
+ nxpwifi_del_rx_reorder_entry(priv, tbl);
+ } else {
+ ptx_tbl = nxpwifi_get_ba_tbl(priv, tid, peer_mac);
+ if (!ptx_tbl) {
+ nxpwifi_dbg(priv->adapter, EVENT,
+ "event: TID, RA not found in table\n");
+ return;
+ }
+
+ tid_down = nxpwifi_wmm_downgrade_tid(priv, tid);
+ ra_list = nxpwifi_wmm_get_ralist_node(priv, tid_down, peer_mac);
+ if (ra_list) {
+ ra_list->amsdu_in_ampdu = false;
+ ra_list->ba_status = BA_SETUP_NONE;
+ }
+ spin_lock_bh(&priv->tx_ba_stream_tbl_lock);
+ nxpwifi_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl);
+ spin_unlock_bh(&priv->tx_ba_stream_tbl_lock);
+ }
+}
+
+/* This function handles the command response of an add BA response.
+ *
+ * Handling includes changing the header fields into CPU format and
+ * creating the stream, provided the add BA is accepted.
+ */
+int nxpwifi_ret_11n_addba_resp(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp;
+ int tid, win_size;
+ struct nxpwifi_rx_reorder_tbl *tbl;
+ u16 block_ack_param_set;
+
+ block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
+
+ tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
+ >> BLOCKACKPARAM_TID_POS;
+ /* Check if we had rejected the ADDBA, if yes then do not create
+ * the stream
+ */
+ if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
+ nxpwifi_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n",
+ add_ba_rsp->peer_mac_addr, tid);
+
+ tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid,
+ add_ba_rsp->peer_mac_addr);
+ if (tbl)
+ nxpwifi_del_rx_reorder_entry(priv, tbl);
+
+ return 0;
+ }
+
+ win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
+ >> BLOCKACKPARAM_WINSIZE_POS;
+
+ tbl = nxpwifi_11n_get_rx_reorder_tbl(priv, tid,
+ add_ba_rsp->peer_mac_addr);
+ if (tbl) {
+ if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) &&
+ priv->add_ba_param.rx_amsdu &&
+ priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)
+ tbl->amsdu = true;
+ else
+ tbl->amsdu = false;
+ }
+
+ nxpwifi_dbg(priv->adapter, CMD,
+ "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n",
+ add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size);
+
+ return 0;
+}
+
+/* This function handles BA stream timeout event by preparing and sending
+ * a command to the firmware.
+ */
+void nxpwifi_11n_ba_stream_timeout(struct nxpwifi_private *priv,
+ struct host_cmd_ds_11n_batimeout *event)
+{
+ struct host_cmd_ds_11n_delba delba;
+
+ memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba));
+ memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN);
+
+ delba.del_ba_param_set |=
+ cpu_to_le16((u16)event->tid << DELBA_TID_POS);
+ delba.del_ba_param_set |=
+ cpu_to_le16((u16)event->origninator << DELBA_INITIATOR_POS);
+ delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT);
+ nxpwifi_send_cmd(priv, HOST_CMD_11N_DELBA, 0, 0, &delba, false);
+}
+
+/* This function cleans up the Rx reorder table by deleting all the entries
+ * and re-initializing.
+ */
+void nxpwifi_11n_cleanup_reorder_tbl(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_rx_reorder_tbl *del_tbl_ptr, *tmp_node;
+
+ spin_lock_bh(&priv->rx_reorder_tbl_lock);
+ list_for_each_entry_safe(del_tbl_ptr, tmp_node,
+ &priv->rx_reorder_tbl_ptr, list) {
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+ nxpwifi_del_rx_reorder_entry(priv, del_tbl_ptr);
+ spin_lock_bh(&priv->rx_reorder_tbl_lock);
+ }
+ INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+
+ nxpwifi_reset_11n_rx_seq_num(priv);
+}
+
+/* This function updates all rx_reorder_tbl's flags.
+ */
+void nxpwifi_update_rxreor_flags(struct nxpwifi_adapter *adapter, u8 flags)
+{
+ struct nxpwifi_private *priv;
+ struct nxpwifi_rx_reorder_tbl *tbl;
+ int i;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (!priv)
+ continue;
+
+ spin_lock_bh(&priv->rx_reorder_tbl_lock);
+ list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list)
+ tbl->flags = flags;
+ spin_unlock_bh(&priv->rx_reorder_tbl_lock);
+ }
+}
+
+/* This function update all the rx_win_size based on coex flag
+ */
+static void nxpwifi_update_ampdu_rxwinsize(struct nxpwifi_adapter *adapter,
+ bool coex_flag)
+{
+ u8 i;
+ u32 rx_win_size;
+ struct nxpwifi_private *priv;
+
+ dev_dbg(adapter->dev, "Update rxwinsize %d\n", coex_flag);
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (!adapter->priv[i])
+ continue;
+ priv = adapter->priv[i];
+ rx_win_size = priv->add_ba_param.rx_win_size;
+ if (coex_flag) {
+ if (priv->bss_type == NXPWIFI_BSS_TYPE_STA)
+ priv->add_ba_param.rx_win_size =
+ NXPWIFI_STA_COEX_AMPDU_DEF_RXWINSIZE;
+ if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP)
+ priv->add_ba_param.rx_win_size =
+ NXPWIFI_UAP_COEX_AMPDU_DEF_RXWINSIZE;
+ } else {
+ if (priv->bss_type == NXPWIFI_BSS_TYPE_STA)
+ priv->add_ba_param.rx_win_size =
+ NXPWIFI_STA_AMPDU_DEF_RXWINSIZE;
+ if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP)
+ priv->add_ba_param.rx_win_size =
+ NXPWIFI_UAP_AMPDU_DEF_RXWINSIZE;
+ }
+
+ if (adapter->coex_win_size && adapter->coex_rx_win_size)
+ priv->add_ba_param.rx_win_size =
+ adapter->coex_rx_win_size;
+
+ if (rx_win_size != priv->add_ba_param.rx_win_size) {
+ if (!priv->media_connected)
+ continue;
+ for (i = 0; i < MAX_NUM_TID; i++)
+ nxpwifi_11n_delba(priv, i);
+ }
+ }
+}
+
+/* This function check coex for RX BA
+ */
+void nxpwifi_coex_ampdu_rxwinsize(struct nxpwifi_adapter *adapter)
+{
+ u8 i;
+ struct nxpwifi_private *priv;
+ u8 count = 0;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ priv = adapter->priv[i];
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) {
+ if (priv->media_connected)
+ count++;
+ }
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
+ if (priv->bss_started)
+ count++;
+ }
+ }
+ if (count >= NXPWIFI_BSS_COEX_COUNT)
+ break;
+ }
+ if (count >= NXPWIFI_BSS_COEX_COUNT)
+ nxpwifi_update_ampdu_rxwinsize(adapter, true);
+ else
+ nxpwifi_update_ampdu_rxwinsize(adapter, false);
+}
+
+/* This function handles rxba_sync event
+ */
+void nxpwifi_11n_rxba_sync_event(struct nxpwifi_private *priv,
+ u8 *event_buf, u16 len)
+{
+ struct nxpwifi_ie_types_rxba_sync *tlv_rxba = (void *)event_buf;
+ u16 tlv_type, tlv_len;
+ struct nxpwifi_rx_reorder_tbl *rx_reor_tbl_ptr;
+ u8 i, j;
+ u16 seq_num, tlv_seq_num, tlv_bitmap_len;
+ int tlv_buf_left = len;
+ int ret;
+ u8 *tmp;
+
+ nxpwifi_dbg_dump(priv->adapter, EVT_D, "RXBA_SYNC event:",
+ event_buf, len);
+ while (tlv_buf_left > sizeof(*tlv_rxba)) {
+ tlv_type = le16_to_cpu(tlv_rxba->header.type);
+ tlv_len = le16_to_cpu(tlv_rxba->header.len);
+ if (size_add(sizeof(tlv_rxba->header), tlv_len) > tlv_buf_left) {
+ nxpwifi_dbg(priv->adapter, WARN,
+ "TLV size (%zu) overflows event_buf buf_left=%d\n",
+ size_add(sizeof(tlv_rxba->header), tlv_len),
+ tlv_buf_left);
+ return;
+ }
+
+ if (tlv_type != TLV_TYPE_RXBA_SYNC) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Wrong TLV id=0x%x\n", tlv_type);
+ return;
+ }
+
+ tlv_seq_num = le16_to_cpu(tlv_rxba->seq_num);
+ tlv_bitmap_len = le16_to_cpu(tlv_rxba->bitmap_len);
+ if (size_add(sizeof(*tlv_rxba), tlv_bitmap_len) > tlv_buf_left) {
+ nxpwifi_dbg(priv->adapter, WARN,
+ "TLV size (%zu) overflows event_buf buf_left=%d\n",
+ size_add(sizeof(*tlv_rxba), tlv_bitmap_len),
+ tlv_buf_left);
+ return;
+ }
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "%pM tid=%d seq_num=%d bitmap_len=%d\n",
+ tlv_rxba->mac, tlv_rxba->tid, tlv_seq_num,
+ tlv_bitmap_len);
+
+ rx_reor_tbl_ptr =
+ nxpwifi_11n_get_rx_reorder_tbl(priv, tlv_rxba->tid,
+ tlv_rxba->mac);
+ if (!rx_reor_tbl_ptr) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Can not find rx_reorder_tbl!");
+ return;
+ }
+
+ for (i = 0; i < tlv_bitmap_len; i++) {
+ for (j = 0 ; j < 8; j++) {
+ if (tlv_rxba->bitmap[i] & (1 << j)) {
+ seq_num = (MAX_TID_VALUE - 1) &
+ (tlv_seq_num + i * 8 + j);
+
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "drop packet,seq=%d\n",
+ seq_num);
+
+ ret = nxpwifi_11n_rx_reorder_pkt
+ (priv, seq_num, tlv_rxba->tid,
+ tlv_rxba->mac, 0, NULL);
+
+ if (ret)
+ nxpwifi_dbg(priv->adapter,
+ ERROR,
+ "Fail to drop packet");
+ }
+ }
+ }
+
+ tlv_buf_left -= (sizeof(tlv_rxba->header) + tlv_len);
+ tmp = (u8 *)tlv_rxba + sizeof(tlv_rxba->header) + tlv_len;
+ tlv_rxba = (struct nxpwifi_ie_types_rxba_sync *)tmp;
+ }
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 09/43] wifi: nxpwifi: add 11n_rxreorder.h
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (7 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 08/43] wifi: nxpwifi: add 11n_rxreorder.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 10/43] wifi: nxpwifi: add cfg80211.c David Lin
` (35 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
.../net/wireless/nxp/nxpwifi/11n_rxreorder.h | 72 +++++++++++++++++++
1 file changed, 72 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.h
diff --git a/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.h b/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.h
new file mode 100644
index 000000000000..9b5dd4899f0e
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NXP Wireless LAN device driver: 802.11n RX Re-ordering
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#ifndef _NXPWIFI_11N_RXREORDER_H_
+#define _NXPWIFI_11N_RXREORDER_H_
+
+#define MIN_FLUSH_TIMER_MS 50
+#define MIN_FLUSH_TIMER_15_MS 15
+#define NXPWIFI_BA_WIN_SIZE_32 32
+
+#define PKT_TYPE_BAR 0xE7
+#define MAX_TID_VALUE (2 << 11)
+#define TWOPOW11 (2 << 10)
+
+#define BLOCKACKPARAM_TID_POS 2
+#define BLOCKACKPARAM_AMSDU_SUPP_MASK 0x1
+#define BLOCKACKPARAM_WINSIZE_POS 6
+#define DELBA_TID_POS 12
+#define DELBA_INITIATOR_POS 11
+#define TYPE_DELBA_SENT 1
+#define TYPE_DELBA_RECEIVE 2
+#define IMMEDIATE_BLOCK_ACK 0x2
+
+#define ADDBA_RSP_STATUS_ACCEPT 0
+
+#define NXPWIFI_DEF_11N_RX_SEQ_NUM 0xffff
+#define BA_SETUP_MAX_PACKET_THRESHOLD 16
+#define BA_SETUP_PACKET_OFFSET 16
+
+enum nxpwifi_rxreor_flags {
+ RXREOR_FORCE_NO_DROP = 1 << 0,
+ RXREOR_INIT_WINDOW_SHIFT = 1 << 1,
+};
+
+static inline void nxpwifi_reset_11n_rx_seq_num(struct nxpwifi_private *priv)
+{
+ memset(priv->rx_seq, 0xff, sizeof(priv->rx_seq));
+}
+
+int nxpwifi_11n_rx_reorder_pkt(struct nxpwifi_private *,
+ u16 seqNum,
+ u16 tid, u8 *ta,
+ u8 pkttype, void *payload);
+void nxpwifi_del_ba_tbl(struct nxpwifi_private *priv, int tid,
+ u8 *peer_mac, u8 type, int initiator);
+void nxpwifi_11n_ba_stream_timeout(struct nxpwifi_private *priv,
+ struct host_cmd_ds_11n_batimeout *event);
+int nxpwifi_ret_11n_addba_resp(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command
+ *resp);
+int nxpwifi_cmd_11n_delba(struct host_cmd_ds_command *cmd,
+ void *data_buf);
+int nxpwifi_cmd_11n_addba_rsp_gen(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct host_cmd_ds_11n_addba_req
+ *cmd_addba_req);
+int nxpwifi_cmd_11n_addba_req(struct host_cmd_ds_command *cmd,
+ void *data_buf);
+void nxpwifi_11n_cleanup_reorder_tbl(struct nxpwifi_private *priv);
+struct nxpwifi_rx_reorder_tbl *
+nxpwifi_11n_get_rxreorder_tbl(struct nxpwifi_private *priv, int tid, u8 *ta);
+struct nxpwifi_rx_reorder_tbl *
+nxpwifi_11n_get_rx_reorder_tbl(struct nxpwifi_private *priv, int tid, u8 *ta);
+void nxpwifi_11n_del_rx_reorder_tbl_by_ta(struct nxpwifi_private *priv, u8 *ta);
+void nxpwifi_update_rxreor_flags(struct nxpwifi_adapter *adapter, u8 flags);
+void nxpwifi_11n_rxba_sync_event(struct nxpwifi_private *priv,
+ u8 *event_buf, u16 len);
+#endif /* _NXPWIFI_11N_RXREORDER_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 10/43] wifi: nxpwifi: add cfg80211.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (8 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 09/43] wifi: nxpwifi: add 11n_rxreorder.h David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 11/43] wifi: nxpwifi: add cfg80211.h David Lin
` (34 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/cfg80211.c | 3773 +++++++++++++++++++
1 file changed, 3773 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/cfg80211.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
new file mode 100644
index 000000000000..7a2b72484923
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
@@ -0,0 +1,3773 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: CFG80211
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "cfg80211.h"
+#include "main.h"
+#include "cmdevt.h"
+#include "11n.h"
+#include "wmm.h"
+
+static const struct ieee80211_iface_limit nxpwifi_ap_sta_limits[] = {
+ {
+ .max = NXPWIFI_MAX_BSS_NUM,
+ .types = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP),
+ },
+};
+
+static const struct ieee80211_iface_combination
+nxpwifi_iface_comb_ap_sta = {
+ .limits = nxpwifi_ap_sta_limits,
+ .num_different_channels = 1,
+ .n_limits = ARRAY_SIZE(nxpwifi_ap_sta_limits),
+ .max_interfaces = NXPWIFI_MAX_BSS_NUM,
+ .beacon_int_infra_match = true,
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40),
+};
+
+static const struct ieee80211_iface_combination
+nxpwifi_iface_comb_ap_sta_vht = {
+ .limits = nxpwifi_ap_sta_limits,
+ .num_different_channels = 1,
+ .n_limits = ARRAY_SIZE(nxpwifi_ap_sta_limits),
+ .max_interfaces = NXPWIFI_MAX_BSS_NUM,
+ .beacon_int_infra_match = true,
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40) |
+ BIT(NL80211_CHAN_WIDTH_80),
+};
+
+/* This function maps the nl802.11 channel type into driver channel type.
+ *
+ * The mapping is as follows -
+ * NL80211_CHAN_NO_HT -> IEEE80211_HT_PARAM_CHA_SEC_NONE
+ * NL80211_CHAN_HT20 -> IEEE80211_HT_PARAM_CHA_SEC_NONE
+ * NL80211_CHAN_HT40PLUS -> IEEE80211_HT_PARAM_CHA_SEC_ABOVE
+ * NL80211_CHAN_HT40MINUS -> IEEE80211_HT_PARAM_CHA_SEC_BELOW
+ * Others -> IEEE80211_HT_PARAM_CHA_SEC_NONE
+ */
+u8 nxpwifi_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type)
+{
+ switch (chan_type) {
+ case NL80211_CHAN_NO_HT:
+ case NL80211_CHAN_HT20:
+ return IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ case NL80211_CHAN_HT40PLUS:
+ return IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ case NL80211_CHAN_HT40MINUS:
+ return IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ default:
+ return IEEE80211_HT_PARAM_CHA_SEC_NONE;
+ }
+}
+
+/* This function maps IEEE HT secondary channel type to NL80211 channel type
+ */
+u8 nxpwifi_get_chan_type(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_channel_band channel_band;
+ int ret;
+
+ ret = nxpwifi_get_chan_info(priv, &channel_band);
+
+ if (!ret) {
+ switch (channel_band.band_config.chan_width) {
+ case CHAN_BW_20MHZ:
+ if (IS_11N_ENABLED(priv))
+ return NL80211_CHAN_HT20;
+ else
+ return NL80211_CHAN_NO_HT;
+ case CHAN_BW_40MHZ:
+ if (channel_band.band_config.chan2_offset ==
+ SEC_CHAN_ABOVE)
+ return NL80211_CHAN_HT40PLUS;
+ else
+ return NL80211_CHAN_HT40MINUS;
+ default:
+ return NL80211_CHAN_HT20;
+ }
+ }
+
+ return NL80211_CHAN_HT20;
+}
+
+/* This function retrieves the private structure from kernel wiphy structure.
+ */
+static void *nxpwifi_cfg80211_get_adapter(struct wiphy *wiphy)
+{
+ return (void *)(*(unsigned long *)wiphy_priv(wiphy));
+}
+
+/* CFG802.11 operation handler to delete a network key.
+ */
+static int
+nxpwifi_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev,
+ int link_id, u8 key_index, bool pairwise,
+ const u8 *mac_addr)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(netdev);
+ static const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ const u8 *peer_mac = pairwise ? mac_addr : bc_mac;
+
+ if (nxpwifi_set_encode(priv, NULL, NULL, 0, key_index, peer_mac, 1)) {
+ nxpwifi_dbg(priv->adapter, ERROR, "deleting the crypto keys\n");
+ return -EFAULT;
+ }
+
+ nxpwifi_dbg(priv->adapter, INFO, "info: crypto keys deleted\n");
+ return 0;
+}
+
+/* This function forms an skb for management frame.
+ */
+static int
+nxpwifi_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len)
+{
+ u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+ u16 pkt_len;
+ u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT;
+
+ pkt_len = len + ETH_ALEN;
+
+ skb_reserve(skb, NXPWIFI_MIN_DATA_HEADER_LEN +
+ NXPWIFI_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len));
+ memcpy(skb_push(skb, sizeof(pkt_len)), &pkt_len, sizeof(pkt_len));
+
+ memcpy(skb_push(skb, sizeof(tx_control)),
+ &tx_control, sizeof(tx_control));
+
+ memcpy(skb_push(skb, sizeof(pkt_type)), &pkt_type, sizeof(pkt_type));
+
+ /* Add packet data and address4 */
+ skb_put_data(skb, buf, sizeof(struct ieee80211_hdr_3addr));
+ skb_put_data(skb, addr, ETH_ALEN);
+ skb_put_data(skb, buf + sizeof(struct ieee80211_hdr_3addr),
+ len - sizeof(struct ieee80211_hdr_3addr));
+
+ skb->priority = LOW_PRIO_TID;
+ __net_timestamp(skb);
+
+ return 0;
+}
+
+/* CFG802.11 operation handler to transmit a management frame.
+ */
+static int
+nxpwifi_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie)
+{
+ const u8 *buf = params->buf;
+ size_t len = params->len;
+ struct sk_buff *skb;
+ u16 pkt_len;
+ const struct ieee80211_mgmt *mgmt;
+ struct nxpwifi_txinfo *tx_info;
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev);
+
+ if (!buf || !len) {
+ nxpwifi_dbg(priv->adapter, ERROR, "invalid buffer and length\n");
+ return -EFAULT;
+ }
+
+ mgmt = (const struct ieee80211_mgmt *)buf;
+ if (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_STA &&
+ ieee80211_is_probe_resp(mgmt->frame_control)) {
+ /* Since we support offload probe resp, we need to skip probe
+ * resp in AP or GO mode
+ */
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: skip to send probe resp in AP or GO mode\n");
+ return 0;
+ }
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
+ if (ieee80211_is_auth(mgmt->frame_control))
+ nxpwifi_dbg(priv->adapter, MSG,
+ "auth: send auth to %pM\n", mgmt->da);
+ if (ieee80211_is_deauth(mgmt->frame_control))
+ nxpwifi_dbg(priv->adapter, MSG,
+ "auth: send deauth to %pM\n", mgmt->da);
+ if (ieee80211_is_disassoc(mgmt->frame_control))
+ nxpwifi_dbg(priv->adapter, MSG,
+ "assoc: send disassoc to %pM\n", mgmt->da);
+ if (ieee80211_is_assoc_resp(mgmt->frame_control))
+ nxpwifi_dbg(priv->adapter, MSG,
+ "assoc: send assoc resp to %pM\n",
+ mgmt->da);
+ if (ieee80211_is_reassoc_resp(mgmt->frame_control))
+ nxpwifi_dbg(priv->adapter, MSG,
+ "assoc: send reassoc resp to %pM\n",
+ mgmt->da);
+ }
+
+ pkt_len = len + ETH_ALEN;
+ skb = dev_alloc_skb(NXPWIFI_MIN_DATA_HEADER_LEN +
+ NXPWIFI_MGMT_FRAME_HEADER_SIZE +
+ pkt_len + sizeof(pkt_len));
+
+ if (!skb) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "allocate skb failed for management frame\n");
+ return -ENOMEM;
+ }
+
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+ memset(tx_info, 0, sizeof(*tx_info));
+ tx_info->bss_num = priv->bss_num;
+ tx_info->bss_type = priv->bss_type;
+ tx_info->pkt_len = pkt_len;
+
+ nxpwifi_form_mgmt_frame(skb, buf, len);
+ *cookie = get_random_u32() | 1;
+
+ if (ieee80211_is_action(mgmt->frame_control))
+ skb = nxpwifi_clone_skb_for_tx_status(priv,
+ skb,
+ NXPWIFI_BUF_FLAG_ACTION_TX_STATUS, cookie);
+ else
+ cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
+ GFP_ATOMIC);
+
+ nxpwifi_queue_tx_pkt(priv, skb);
+
+ nxpwifi_dbg(priv->adapter, INFO, "info: management frame transmitted\n");
+ return 0;
+}
+
+/* CFG802.11 operation handler to register a mgmt frame.
+ */
+static void
+nxpwifi_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct mgmt_frame_regs *upd)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev);
+ u32 mask = upd->interface_stypes;
+
+ if (mask != priv->mgmt_frame_mask) {
+ priv->mgmt_frame_mask = mask;
+ if (priv->host_mlme_reg &&
+ GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_UAP)
+ priv->mgmt_frame_mask |= HOST_MLME_MGMT_MASK;
+ nxpwifi_send_cmd(priv, HOST_CMD_MGMT_FRAME_REG,
+ HOST_ACT_GEN_SET, 0,
+ &priv->mgmt_frame_mask, false);
+ nxpwifi_dbg(priv->adapter, INFO, "info: mgmt frame registered\n");
+ }
+}
+
+/* CFG802.11 operation handler to remain on channel.
+ */
+static int
+nxpwifi_cfg80211_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration, u64 *cookie)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev);
+ int ret;
+
+ if (!chan || !cookie) {
+ nxpwifi_dbg(priv->adapter, ERROR, "Invalid parameter for ROC\n");
+ return -EINVAL;
+ }
+
+ if (priv->roc_cfg.cookie) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: ongoing ROC, cookie = 0x%llx\n",
+ priv->roc_cfg.cookie);
+ return -EBUSY;
+ }
+
+ ret = nxpwifi_remain_on_chan_cfg(priv, HOST_ACT_GEN_SET, chan,
+ duration);
+
+ if (!ret) {
+ *cookie = get_random_u32() | 1;
+ priv->roc_cfg.cookie = *cookie;
+ priv->roc_cfg.chan = *chan;
+
+ cfg80211_ready_on_channel(wdev, *cookie, chan,
+ duration, GFP_ATOMIC);
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: ROC, cookie = 0x%llx\n", *cookie);
+ }
+
+ return ret;
+}
+
+/* CFG802.11 operation handler to cancel remain on channel.
+ */
+static int
+nxpwifi_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev, u64 cookie)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev);
+ int ret;
+
+ if (cookie != priv->roc_cfg.cookie)
+ return -ENOENT;
+
+ ret = nxpwifi_remain_on_chan_cfg(priv, HOST_ACT_GEN_REMOVE,
+ &priv->roc_cfg.chan, 0);
+
+ if (!ret) {
+ cfg80211_remain_on_channel_expired(wdev, cookie,
+ &priv->roc_cfg.chan,
+ GFP_ATOMIC);
+
+ memset(&priv->roc_cfg, 0, sizeof(struct nxpwifi_roc_cfg));
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: cancel ROC, cookie = 0x%llx\n", cookie);
+ }
+
+ return ret;
+}
+
+/* CFG802.11 operation handler to set Tx power.
+ */
+static int
+nxpwifi_cfg80211_set_tx_power(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type,
+ int mbm)
+{
+ struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy);
+ struct nxpwifi_private *priv;
+ struct nxpwifi_power_cfg power_cfg;
+ int dbm = MBM_TO_DBM(mbm);
+
+ switch (type) {
+ case NL80211_TX_POWER_FIXED:
+ power_cfg.is_power_auto = 0;
+ power_cfg.is_power_fixed = 1;
+ power_cfg.power_level = dbm;
+ break;
+ case NL80211_TX_POWER_LIMITED:
+ power_cfg.is_power_auto = 0;
+ power_cfg.is_power_fixed = 0;
+ power_cfg.power_level = dbm;
+ break;
+ case NL80211_TX_POWER_AUTOMATIC:
+ power_cfg.is_power_auto = 1;
+ break;
+ }
+
+ priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
+
+ return nxpwifi_set_tx_power(priv, &power_cfg);
+}
+
+/* CFG802.11 operation handler to get Tx power.
+ */
+static int
+nxpwifi_cfg80211_get_tx_power(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ int *dbm)
+{
+ struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy);
+ struct nxpwifi_private *priv = nxpwifi_get_priv(adapter,
+ NXPWIFI_BSS_ROLE_ANY);
+ int ret = nxpwifi_send_cmd(priv, HOST_CMD_RF_TX_PWR,
+ HOST_ACT_GEN_GET, 0, NULL, true);
+
+ if (ret < 0)
+ return ret;
+
+ /* tx_power_level is set in HOST_CMD_RF_TX_PWR command handler */
+ *dbm = priv->tx_power_level;
+
+ return 0;
+}
+
+/* CFG802.11 operation handler to set Power Save option.
+ *
+ * The timeout value, if provided, is currently ignored.
+ */
+static int
+nxpwifi_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+ struct net_device *dev,
+ bool enabled, int timeout)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ u32 ps_mode;
+
+ if (timeout)
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: ignore timeout value for IEEE Power Save\n");
+
+ ps_mode = enabled;
+
+ return nxpwifi_drv_set_power(priv, &ps_mode);
+}
+
+/* CFG802.11 operation handler to set the default network key.
+ */
+static int
+nxpwifi_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
+ int link_id, u8 key_index, bool unicast,
+ bool multicast)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(netdev);
+
+ /* Return if WEP key not configured */
+ if (!priv->sec_info.wep_enabled)
+ return 0;
+
+ if (priv->bss_type == NXPWIFI_BSS_TYPE_UAP) {
+ priv->wep_key_curr_index = key_index;
+ } else if (nxpwifi_set_encode(priv, NULL, NULL, 0, key_index,
+ NULL, 0)) {
+ nxpwifi_dbg(priv->adapter, ERROR, "set default Tx key index\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/* CFG802.11 operation handler to add a network key.
+ */
+static int
+nxpwifi_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev,
+ int link_id, u8 key_index, bool pairwise,
+ const u8 *mac_addr, struct key_params *params)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(netdev);
+ struct nxpwifi_wep_key *wep_key;
+ static const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ const u8 *peer_mac = pairwise ? mac_addr : bc_mac;
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP &&
+ (params->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ params->cipher == WLAN_CIPHER_SUITE_WEP104)) {
+ if (params->key && params->key_len) {
+ wep_key = &priv->wep_key[key_index];
+ memset(wep_key, 0, sizeof(struct nxpwifi_wep_key));
+ memcpy(wep_key->key_material, params->key,
+ params->key_len);
+ wep_key->key_index = key_index;
+ wep_key->key_length = params->key_len;
+ priv->sec_info.wep_enabled = 1;
+ }
+ return 0;
+ }
+
+ if (nxpwifi_set_encode(priv, params, params->key, params->key_len,
+ key_index, peer_mac, 0)) {
+ nxpwifi_dbg(priv->adapter, ERROR, "crypto keys added\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/* CFG802.11 operation handler to set default mgmt key.
+ */
+static int
+nxpwifi_cfg80211_set_default_mgmt_key(struct wiphy *wiphy,
+ struct net_device *netdev,
+ int link_id,
+ u8 key_index)
+{
+ return 0;
+}
+
+/* This function sends domain information to the firmware.
+ *
+ * The following information are passed to the firmware -
+ * - Country codes
+ * - Sub bands (first channel, number of channels, maximum Tx power)
+ */
+int nxpwifi_send_domain_info_cmd_fw(struct wiphy *wiphy)
+{
+ u8 no_of_triplet = 0;
+ struct ieee80211_country_ie_triplet *t;
+ u8 no_of_parsed_chan = 0;
+ u8 first_chan = 0, next_chan = 0, max_pwr = 0;
+ u8 i, flag = 0;
+ enum nl80211_band band;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *ch;
+ struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy);
+ struct nxpwifi_private *priv;
+ struct nxpwifi_802_11d_domain_reg *domain_info = &adapter->domain_reg;
+
+ /* Set country code */
+ domain_info->country_code[0] = adapter->country_code[0];
+ domain_info->country_code[1] = adapter->country_code[1];
+ domain_info->country_code[2] = ' ';
+
+ band = nxpwifi_band_to_radio_type(adapter->config_bands);
+ if (!wiphy->bands[band]) {
+ nxpwifi_dbg(adapter, ERROR,
+ "11D: setting domain info in FW\n");
+ return -1;
+ }
+
+ sband = wiphy->bands[band];
+
+ for (i = 0; i < sband->n_channels ; i++) {
+ ch = &sband->channels[i];
+ if (ch->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ if (!flag) {
+ flag = 1;
+ first_chan = (u32)ch->hw_value;
+ next_chan = first_chan;
+ max_pwr = ch->max_power;
+ no_of_parsed_chan = 1;
+ continue;
+ }
+
+ if (ch->hw_value == next_chan + 1 &&
+ ch->max_power == max_pwr) {
+ next_chan++;
+ no_of_parsed_chan++;
+ } else {
+ t = &domain_info->triplet[no_of_triplet];
+ t->chans.first_channel = first_chan;
+ t->chans.num_channels = no_of_parsed_chan;
+ t->chans.max_power = max_pwr;
+ no_of_triplet++;
+ first_chan = (u32)ch->hw_value;
+ next_chan = first_chan;
+ max_pwr = ch->max_power;
+ no_of_parsed_chan = 1;
+ }
+ }
+
+ if (flag) {
+ t = &domain_info->triplet[no_of_triplet];
+ t->chans.first_channel = first_chan;
+ t->chans.num_channels = no_of_parsed_chan;
+ t->chans.max_power = max_pwr;
+ no_of_triplet++;
+ }
+
+ domain_info->no_of_triplet = no_of_triplet;
+
+ priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
+
+ if (nxpwifi_send_cmd(priv, HOST_CMD_802_11D_DOMAIN_INFO,
+ HOST_ACT_GEN_SET, 0, NULL, false)) {
+ nxpwifi_dbg(adapter, INFO,
+ "11D: setting domain info in FW\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void nxpwifi_reg_apply_radar_flags(struct wiphy *wiphy)
+{
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+ unsigned int i;
+
+ if (!wiphy->bands[NL80211_BAND_5GHZ])
+ return;
+ sband = wiphy->bands[NL80211_BAND_5GHZ];
+
+ for (i = 0; i < sband->n_channels; i++) {
+ chan = &sband->channels[i];
+ if ((!(chan->flags & IEEE80211_CHAN_DISABLED)) &&
+ (chan->flags & IEEE80211_CHAN_RADAR))
+ chan->flags |= IEEE80211_CHAN_NO_IR;
+ }
+}
+
+/* CFG802.11 regulatory domain callback function.
+ *
+ * This function is called when the regulatory domain is changed due to the
+ * following reasons -
+ * - Set by driver
+ * - Set by system core
+ * - Set by user
+ * - Set bt Country IE
+ */
+static void nxpwifi_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy);
+ struct nxpwifi_private *priv = nxpwifi_get_priv(adapter,
+ NXPWIFI_BSS_ROLE_ANY);
+ nxpwifi_dbg(adapter, INFO,
+ "info: cfg80211 regulatory domain callback for %c%c\n",
+ request->alpha2[0], request->alpha2[1]);
+ nxpwifi_reg_apply_radar_flags(wiphy);
+
+ switch (request->initiator) {
+ case NL80211_REGDOM_SET_BY_DRIVER:
+ case NL80211_REGDOM_SET_BY_CORE:
+ case NL80211_REGDOM_SET_BY_USER:
+ case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ break;
+ default:
+ nxpwifi_dbg(adapter, ERROR,
+ "unknown regdom initiator: %d\n",
+ request->initiator);
+ return;
+ }
+
+ /* Don't send world or same regdom info to firmware */
+ if (strncmp(request->alpha2, "00", 2) &&
+ strncmp(request->alpha2, adapter->country_code,
+ sizeof(request->alpha2))) {
+ memcpy(adapter->country_code, request->alpha2,
+ sizeof(request->alpha2));
+ nxpwifi_send_domain_info_cmd_fw(wiphy);
+ nxpwifi_dnld_txpwr_table(priv);
+ }
+}
+
+/* This function sets the fragmentation threshold.
+ *
+ * The fragmentation threshold value must lie between NXPWIFI_FRAG_MIN_VALUE
+ * and NXPWIFI_FRAG_MAX_VALUE.
+ */
+static int
+nxpwifi_set_frag(struct nxpwifi_private *priv, u32 frag_thr)
+{
+ if (frag_thr < NXPWIFI_FRAG_MIN_VALUE ||
+ frag_thr > NXPWIFI_FRAG_MAX_VALUE)
+ frag_thr = NXPWIFI_FRAG_MAX_VALUE;
+
+ return nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB,
+ HOST_ACT_GEN_SET, FRAG_THRESH_I,
+ &frag_thr, true);
+}
+
+/* This function sets the RTS threshold.
+ *
+ * The rts value must lie between NXPWIFI_RTS_MIN_VALUE
+ * and NXPWIFI_RTS_MAX_VALUE.
+ */
+static int
+nxpwifi_set_rts(struct nxpwifi_private *priv, u32 rts_thr)
+{
+ if (rts_thr < NXPWIFI_RTS_MIN_VALUE || rts_thr > NXPWIFI_RTS_MAX_VALUE)
+ rts_thr = NXPWIFI_RTS_MAX_VALUE;
+
+ return nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB,
+ HOST_ACT_GEN_SET, RTS_THRESH_I,
+ &rts_thr, true);
+}
+
+/* CFG802.11 operation handler to set wiphy parameters.
+ *
+ * This function can be used to set the RTS threshold and the
+ * Fragmentation threshold of the driver.
+ */
+static int
+nxpwifi_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+ struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy);
+ struct nxpwifi_private *priv;
+ struct nxpwifi_uap_bss_param *bss_cfg;
+ int ret;
+
+ priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
+
+ switch (priv->bss_role) {
+ case NXPWIFI_BSS_ROLE_UAP:
+ if (priv->bss_started)
+ return 0;
+
+ bss_cfg = kzalloc(sizeof(*bss_cfg), GFP_KERNEL);
+ if (!bss_cfg)
+ return -ENOMEM;
+
+ nxpwifi_set_sys_config_invalid_data(bss_cfg);
+
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD)
+ bss_cfg->rts_threshold = wiphy->rts_threshold;
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
+ bss_cfg->frag_threshold = wiphy->frag_threshold;
+ if (changed & WIPHY_PARAM_RETRY_LONG)
+ bss_cfg->retry_limit = wiphy->retry_long;
+
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_UAP_SYS_CONFIG,
+ HOST_ACT_GEN_SET,
+ UAP_BSS_PARAMS_I, bss_cfg,
+ false);
+
+ kfree(bss_cfg);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "Failed to set wiphy phy params\n");
+ return ret;
+ }
+ break;
+
+ case NXPWIFI_BSS_ROLE_STA:
+ if (priv->media_connected)
+ return 0;
+
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
+ ret = nxpwifi_set_rts(priv,
+ wiphy->rts_threshold);
+ if (ret)
+ return ret;
+ }
+ if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
+ ret = nxpwifi_set_frag(priv,
+ wiphy->frag_threshold);
+ if (ret)
+ return ret;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int nxpwifi_deinit_priv_params(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ unsigned long flags;
+
+ priv->host_mlme_reg = false;
+ priv->mgmt_frame_mask = 0;
+ if (nxpwifi_send_cmd(priv, HOST_CMD_MGMT_FRAME_REG,
+ HOST_ACT_GEN_SET, 0,
+ &priv->mgmt_frame_mask, false)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "could not unregister mgmt frame rx\n");
+ return -1;
+ }
+
+ nxpwifi_deauthenticate(priv, NULL);
+
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+ adapter->main_locked = true;
+ if (adapter->nxpwifi_processing) {
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ flush_workqueue(adapter->workqueue);
+ } else {
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ }
+
+ spin_lock_bh(&adapter->rx_proc_lock);
+ adapter->rx_locked = true;
+ if (adapter->rx_processing) {
+ spin_unlock_bh(&adapter->rx_proc_lock);
+ flush_workqueue(adapter->rx_workqueue);
+ } else {
+ spin_unlock_bh(&adapter->rx_proc_lock);
+ }
+
+ nxpwifi_free_priv(priv);
+ priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;
+
+ return 0;
+}
+
+static int
+nxpwifi_init_new_priv_params(struct nxpwifi_private *priv,
+ struct net_device *dev,
+ enum nl80211_iftype type)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ unsigned long flags;
+
+ nxpwifi_init_priv(priv);
+
+ priv->bss_mode = type;
+ priv->wdev.iftype = type;
+
+ nxpwifi_init_priv_params(priv, priv->netdev);
+ priv->bss_started = 0;
+
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ priv->bss_role = NXPWIFI_BSS_ROLE_STA;
+ priv->bss_type = NXPWIFI_BSS_TYPE_STA;
+ break;
+ case NL80211_IFTYPE_AP:
+ priv->bss_role = NXPWIFI_BSS_ROLE_UAP;
+ priv->bss_type = NXPWIFI_BSS_TYPE_UAP;
+ break;
+ default:
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: changing to %d not supported\n",
+ dev->name, type);
+ return -EOPNOTSUPP;
+ }
+
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+ adapter->main_locked = false;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+
+ spin_lock_bh(&adapter->rx_proc_lock);
+ adapter->rx_locked = false;
+ spin_unlock_bh(&adapter->rx_proc_lock);
+
+ nxpwifi_set_mac_address(priv, dev, false, NULL);
+
+ return 0;
+}
+
+static bool
+is_vif_type_change_allowed(struct nxpwifi_adapter *adapter,
+ enum nl80211_iftype old_iftype,
+ enum nl80211_iftype new_iftype)
+{
+ switch (old_iftype) {
+ case NL80211_IFTYPE_STATION:
+ switch (new_iftype) {
+ case NL80211_IFTYPE_AP:
+ return adapter->curr_iface_comb.uap_intf !=
+ adapter->iface_limit.uap_intf;
+ default:
+ return false;
+ }
+
+ case NL80211_IFTYPE_AP:
+ switch (new_iftype) {
+ case NL80211_IFTYPE_STATION:
+ return adapter->curr_iface_comb.sta_intf !=
+ adapter->iface_limit.sta_intf;
+ default:
+ return false;
+ }
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static void
+update_vif_type_counter(struct nxpwifi_adapter *adapter,
+ enum nl80211_iftype iftype,
+ int change)
+{
+ switch (iftype) {
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_STATION:
+ adapter->curr_iface_comb.sta_intf += change;
+ break;
+ case NL80211_IFTYPE_AP:
+ adapter->curr_iface_comb.uap_intf += change;
+ break;
+ default:
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: Unsupported iftype passed: %d\n",
+ __func__, iftype);
+ break;
+ }
+}
+
+static int
+nxpwifi_change_vif_to_sta(struct net_device *dev,
+ enum nl80211_iftype curr_iftype,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct nxpwifi_private *priv;
+ struct nxpwifi_adapter *adapter;
+
+ priv = nxpwifi_netdev_get_priv(dev);
+
+ if (!priv)
+ return -1;
+
+ adapter = priv->adapter;
+
+ nxpwifi_dbg(adapter, INFO,
+ "%s: changing role to station\n", dev->name);
+
+ if (nxpwifi_deinit_priv_params(priv))
+ return -1;
+ if (nxpwifi_init_new_priv_params(priv, dev, type))
+ return -1;
+
+ update_vif_type_counter(adapter, curr_iftype, -1);
+ update_vif_type_counter(adapter, type, 1);
+ dev->ieee80211_ptr->iftype = type;
+
+ if (nxpwifi_send_cmd(priv, HOST_CMD_SET_BSS_MODE,
+ HOST_ACT_GEN_SET, 0, NULL, true))
+ return -1;
+ if (nxpwifi_sta_init_cmd(priv, false, false))
+ return -1;
+
+ return 0;
+}
+
+static int
+nxpwifi_change_vif_to_ap(struct net_device *dev,
+ enum nl80211_iftype curr_iftype,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct nxpwifi_private *priv;
+ struct nxpwifi_adapter *adapter;
+
+ priv = nxpwifi_netdev_get_priv(dev);
+
+ if (!priv)
+ return -1;
+
+ adapter = priv->adapter;
+
+ nxpwifi_dbg(adapter, INFO,
+ "%s: changing role to AP\n", dev->name);
+
+ if (nxpwifi_deinit_priv_params(priv))
+ return -1;
+ if (nxpwifi_init_new_priv_params(priv, dev, type))
+ return -1;
+
+ update_vif_type_counter(adapter, curr_iftype, -1);
+ update_vif_type_counter(adapter, type, 1);
+ dev->ieee80211_ptr->iftype = type;
+
+ if (nxpwifi_send_cmd(priv, HOST_CMD_SET_BSS_MODE,
+ HOST_ACT_GEN_SET, 0, NULL, true))
+ return -1;
+ if (nxpwifi_sta_init_cmd(priv, false, false))
+ return -1;
+
+ return 0;
+}
+
+/* CFG802.11 operation handler to change interface type.
+ */
+static int
+nxpwifi_cfg80211_change_virtual_intf(struct wiphy *wiphy,
+ struct net_device *dev,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ enum nl80211_iftype curr_iftype = dev->ieee80211_ptr->iftype;
+
+ if (priv->scan_request) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "change virtual interface: scan in process\n");
+ return -EBUSY;
+ }
+
+ if (type == NL80211_IFTYPE_UNSPECIFIED) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "%s: no new type specified, keeping old type %d\n",
+ dev->name, curr_iftype);
+ return 0;
+ }
+
+ if (curr_iftype == type) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "%s: interface already is of type %d\n",
+ dev->name, curr_iftype);
+ return 0;
+ }
+
+ if (!is_vif_type_change_allowed(priv->adapter, curr_iftype, type)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "%s: change from type %d to %d is not allowed\n",
+ dev->name, curr_iftype, type);
+ return -EOPNOTSUPP;
+ }
+
+ switch (curr_iftype) {
+ case NL80211_IFTYPE_STATION:
+ switch (type) {
+ case NL80211_IFTYPE_AP:
+ return nxpwifi_change_vif_to_ap(dev, curr_iftype, type,
+ params);
+ default:
+ goto errnotsupp;
+ }
+
+ case NL80211_IFTYPE_AP:
+ switch (type) {
+ case NL80211_IFTYPE_STATION:
+ return nxpwifi_change_vif_to_sta(dev, curr_iftype,
+ type, params);
+ break;
+ default:
+ goto errnotsupp;
+ }
+
+ default:
+ goto errnotsupp;
+ }
+
+ return 0;
+
+errnotsupp:
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "unsupported interface type transition: %d to %d\n",
+ curr_iftype, type);
+ return -EOPNOTSUPP;
+}
+
+static void
+nxpwifi_parse_htinfo(struct nxpwifi_private *priv, u8 rateinfo, u8 htinfo,
+ struct rate_info *rate)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (adapter->is_hw_11ac_capable) {
+ /* bit[1-0]: 00=LG 01=HT 10=VHT */
+ if (htinfo & BIT(0)) {
+ /* HT */
+ rate->mcs = rateinfo;
+ rate->flags |= RATE_INFO_FLAGS_MCS;
+ }
+ if (htinfo & BIT(1)) {
+ /* VHT */
+ rate->mcs = rateinfo & 0x0F;
+ rate->flags |= RATE_INFO_FLAGS_VHT_MCS;
+ }
+
+ if (htinfo & (BIT(1) | BIT(0))) {
+ /* HT or VHT */
+ switch (htinfo & (BIT(3) | BIT(2))) {
+ case 0:
+ rate->bw = RATE_INFO_BW_20;
+ break;
+ case (BIT(2)):
+ rate->bw = RATE_INFO_BW_40;
+ break;
+ case (BIT(3)):
+ rate->bw = RATE_INFO_BW_80;
+ break;
+ case (BIT(3) | BIT(2)):
+ rate->bw = RATE_INFO_BW_160;
+ break;
+ }
+
+ if (htinfo & BIT(4))
+ rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
+
+ if ((rateinfo >> 4) == 1)
+ rate->nss = 2;
+ else
+ rate->nss = 1;
+ }
+ } else {
+ /* Bit 0 in htinfo indicates that current rate is 11n. Valid
+ * MCS index values for us are 0 to 15.
+ */
+ if ((htinfo & BIT(0)) && rateinfo < 16) {
+ rate->mcs = rateinfo;
+ rate->flags |= RATE_INFO_FLAGS_MCS;
+ rate->bw = RATE_INFO_BW_20;
+ if (htinfo & BIT(1))
+ rate->bw = RATE_INFO_BW_40;
+ if (htinfo & BIT(2))
+ rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
+ }
+ }
+
+ /* Decode legacy rates for non-HT. */
+ if (!(htinfo & (BIT(0) | BIT(1)))) {
+ /* Bitrates in multiples of 100kb/s. */
+ static const int legacy_rates[] = {
+ [0] = 10,
+ [1] = 20,
+ [2] = 55,
+ [3] = 110,
+ [4] = 60, /* NXPWIFI_RATE_INDEX_OFDM0 */
+ [5] = 60,
+ [6] = 90,
+ [7] = 120,
+ [8] = 180,
+ [9] = 240,
+ [10] = 360,
+ [11] = 480,
+ [12] = 540,
+ };
+ if (rateinfo < ARRAY_SIZE(legacy_rates))
+ rate->legacy = legacy_rates[rateinfo];
+ }
+}
+
+/* This function dumps the station information on a buffer.
+ *
+ * The following information are shown -
+ * - Total bytes transmitted
+ * - Total bytes received
+ * - Total packets transmitted
+ * - Total packets received
+ * - Signal quality level
+ * - Transmission rate
+ */
+static int
+nxpwifi_dump_station_info(struct nxpwifi_private *priv,
+ struct nxpwifi_sta_node *node,
+ struct station_info *sinfo)
+{
+ u32 rate;
+
+ sinfo->filled = BIT_ULL(NL80211_STA_INFO_RX_BYTES) |
+ BIT_ULL(NL80211_STA_INFO_TX_BYTES) |
+ BIT_ULL(NL80211_STA_INFO_RX_PACKETS) |
+ BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
+ BIT_ULL(NL80211_STA_INFO_TX_BITRATE) |
+ BIT_ULL(NL80211_STA_INFO_SIGNAL) |
+ BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
+ if (!node)
+ return -ENOENT;
+
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME) |
+ BIT_ULL(NL80211_STA_INFO_TX_FAILED);
+ sinfo->inactive_time =
+ jiffies_to_msecs(jiffies - node->stats.last_rx);
+
+ sinfo->signal = node->stats.rssi;
+ sinfo->signal_avg = node->stats.rssi;
+ sinfo->rx_bytes = node->stats.rx_bytes;
+ sinfo->tx_bytes = node->stats.tx_bytes;
+ sinfo->rx_packets = node->stats.rx_packets;
+ sinfo->tx_packets = node->stats.tx_packets;
+ sinfo->tx_failed = node->stats.tx_failed;
+
+ nxpwifi_parse_htinfo(priv, priv->tx_rate,
+ node->stats.last_tx_htinfo,
+ &sinfo->txrate);
+ sinfo->txrate.legacy = node->stats.last_tx_rate * 5;
+
+ return 0;
+ }
+
+ /* Get signal information from the firmware */
+ if (nxpwifi_send_cmd(priv, HOST_CMD_RSSI_INFO,
+ HOST_ACT_GEN_GET, 0, NULL, true)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "failed to get signal information\n");
+ return -EFAULT;
+ }
+
+ if (nxpwifi_drv_get_data_rate(priv, &rate)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "getting data rate error\n");
+ return -EFAULT;
+ }
+
+ /* Get DTIM period information from firmware */
+ nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB,
+ HOST_ACT_GEN_GET, DTIM_PERIOD_I,
+ &priv->dtim_period, true);
+
+ nxpwifi_parse_htinfo(priv, priv->tx_rate, priv->tx_htinfo,
+ &sinfo->txrate);
+
+ sinfo->signal_avg = priv->bcn_rssi_avg;
+ sinfo->rx_bytes = priv->stats.rx_bytes;
+ sinfo->tx_bytes = priv->stats.tx_bytes;
+ sinfo->rx_packets = priv->stats.rx_packets;
+ sinfo->tx_packets = priv->stats.tx_packets;
+ sinfo->signal = priv->bcn_rssi_avg;
+ /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */
+ sinfo->txrate.legacy = rate * 5;
+
+ sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
+ nxpwifi_parse_htinfo(priv, priv->rxpd_rate, priv->rxpd_htinfo,
+ &sinfo->rxrate);
+
+ if (priv->bss_mode == NL80211_IFTYPE_STATION) {
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BSS_PARAM);
+ sinfo->bss_param.flags = 0;
+ if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap &
+ WLAN_CAPABILITY_SHORT_PREAMBLE)
+ sinfo->bss_param.flags |=
+ BSS_PARAM_FLAGS_SHORT_PREAMBLE;
+ if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap &
+ WLAN_CAPABILITY_SHORT_SLOT_TIME)
+ sinfo->bss_param.flags |=
+ BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
+ sinfo->bss_param.dtim_period = priv->dtim_period;
+ sinfo->bss_param.beacon_interval =
+ priv->curr_bss_params.bss_descriptor.beacon_period;
+ }
+
+ return 0;
+}
+
+/* CFG802.11 operation handler to get station information.
+ *
+ * This function only works in connected mode, and dumps the
+ * requested station information, if available.
+ */
+static int
+nxpwifi_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_info *sinfo)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+
+ if (!priv->media_connected)
+ return -ENOENT;
+ if (memcmp(mac, priv->cfg_bssid, ETH_ALEN))
+ return -ENOENT;
+
+ return nxpwifi_dump_station_info(priv, NULL, sinfo);
+}
+
+/* CFG802.11 operation handler to dump station information.
+ */
+static int
+nxpwifi_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *mac, struct station_info *sinfo)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ struct nxpwifi_sta_node *node;
+ int i;
+
+ if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) &&
+ priv->media_connected && idx == 0) {
+ ether_addr_copy(mac, priv->cfg_bssid);
+ return nxpwifi_dump_station_info(priv, NULL, sinfo);
+ } else if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
+ nxpwifi_send_cmd(priv, HOST_CMD_APCMD_STA_LIST,
+ HOST_ACT_GEN_GET, 0, NULL, true);
+
+ i = 0;
+ list_for_each_entry(node, &priv->sta_list, list) {
+ if (i++ != idx)
+ continue;
+ ether_addr_copy(mac, node->mac_addr);
+ return nxpwifi_dump_station_info(priv, node, sinfo);
+ }
+ }
+
+ return -ENOENT;
+}
+
+static int
+nxpwifi_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,
+ int idx, struct survey_info *survey)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ struct nxpwifi_chan_stats *pchan_stats = priv->adapter->chan_stats;
+ enum nl80211_band band;
+ u8 chan_num;
+
+ nxpwifi_dbg(priv->adapter, DUMP, "dump_survey idx=%d\n", idx);
+
+ memset(survey, 0, sizeof(struct survey_info));
+
+ if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) &&
+ priv->media_connected && idx == 0) {
+ u8 curr_bss_band = priv->curr_bss_params.band;
+ u32 chan = priv->curr_bss_params.bss_descriptor.channel;
+
+ band = nxpwifi_band_to_radio_type(curr_bss_band);
+ survey->channel = ieee80211_get_channel
+ (wiphy,
+ ieee80211_channel_to_frequency(chan, band));
+
+ if (priv->bcn_nf_last) {
+ survey->filled = SURVEY_INFO_NOISE_DBM;
+ survey->noise = priv->bcn_nf_last;
+ }
+ return 0;
+ }
+
+ if (idx >= priv->adapter->num_in_chan_stats)
+ return -ENOENT;
+
+ if (!pchan_stats[idx].cca_scan_dur)
+ return 0;
+
+ band = pchan_stats[idx].bandcfg;
+ chan_num = pchan_stats[idx].chan_num;
+ survey->channel = ieee80211_get_channel
+ (wiphy,
+ ieee80211_channel_to_frequency(chan_num, band));
+ survey->filled = SURVEY_INFO_NOISE_DBM |
+ SURVEY_INFO_TIME |
+ SURVEY_INFO_TIME_BUSY;
+ survey->noise = pchan_stats[idx].noise;
+ survey->time = pchan_stats[idx].cca_scan_dur;
+ survey->time_busy = pchan_stats[idx].cca_busy_dur;
+
+ return 0;
+}
+
+/* Supported rates to be advertised to the cfg80211 */
+static struct ieee80211_rate nxpwifi_rates[] = {
+ {.bitrate = 10, .hw_value = 2, },
+ {.bitrate = 20, .hw_value = 4, },
+ {.bitrate = 55, .hw_value = 11, },
+ {.bitrate = 110, .hw_value = 22, },
+ {.bitrate = 60, .hw_value = 12, },
+ {.bitrate = 90, .hw_value = 18, },
+ {.bitrate = 120, .hw_value = 24, },
+ {.bitrate = 180, .hw_value = 36, },
+ {.bitrate = 240, .hw_value = 48, },
+ {.bitrate = 360, .hw_value = 72, },
+ {.bitrate = 480, .hw_value = 96, },
+ {.bitrate = 540, .hw_value = 108, },
+};
+
+/* Channel definitions to be advertised to cfg80211 */
+static struct ieee80211_channel nxpwifi_channels_2ghz[] = {
+ {.center_freq = 2412, .hw_value = 1, },
+ {.center_freq = 2417, .hw_value = 2, },
+ {.center_freq = 2422, .hw_value = 3, },
+ {.center_freq = 2427, .hw_value = 4, },
+ {.center_freq = 2432, .hw_value = 5, },
+ {.center_freq = 2437, .hw_value = 6, },
+ {.center_freq = 2442, .hw_value = 7, },
+ {.center_freq = 2447, .hw_value = 8, },
+ {.center_freq = 2452, .hw_value = 9, },
+ {.center_freq = 2457, .hw_value = 10, },
+ {.center_freq = 2462, .hw_value = 11, },
+ {.center_freq = 2467, .hw_value = 12, },
+ {.center_freq = 2472, .hw_value = 13, },
+ {.center_freq = 2484, .hw_value = 14, },
+};
+
+static struct ieee80211_supported_band nxpwifi_band_2ghz = {
+ .channels = nxpwifi_channels_2ghz,
+ .n_channels = ARRAY_SIZE(nxpwifi_channels_2ghz),
+ .bitrates = nxpwifi_rates,
+ .n_bitrates = ARRAY_SIZE(nxpwifi_rates),
+};
+
+static struct ieee80211_channel nxpwifi_channels_5ghz[] = {
+ {.center_freq = 5040, .hw_value = 8, },
+ {.center_freq = 5060, .hw_value = 12, },
+ {.center_freq = 5080, .hw_value = 16, },
+ {.center_freq = 5170, .hw_value = 34, },
+ {.center_freq = 5190, .hw_value = 38, },
+ {.center_freq = 5210, .hw_value = 42, },
+ {.center_freq = 5230, .hw_value = 46, },
+ {.center_freq = 5180, .hw_value = 36, },
+ {.center_freq = 5200, .hw_value = 40, },
+ {.center_freq = 5220, .hw_value = 44, },
+ {.center_freq = 5240, .hw_value = 48, },
+ {.center_freq = 5260, .hw_value = 52, },
+ {.center_freq = 5280, .hw_value = 56, },
+ {.center_freq = 5300, .hw_value = 60, },
+ {.center_freq = 5320, .hw_value = 64, },
+ {.center_freq = 5500, .hw_value = 100, },
+ {.center_freq = 5520, .hw_value = 104, },
+ {.center_freq = 5540, .hw_value = 108, },
+ {.center_freq = 5560, .hw_value = 112, },
+ {.center_freq = 5580, .hw_value = 116, },
+ {.center_freq = 5600, .hw_value = 120, },
+ {.center_freq = 5620, .hw_value = 124, },
+ {.center_freq = 5640, .hw_value = 128, },
+ {.center_freq = 5660, .hw_value = 132, },
+ {.center_freq = 5680, .hw_value = 136, },
+ {.center_freq = 5700, .hw_value = 140, },
+ {.center_freq = 5745, .hw_value = 149, },
+ {.center_freq = 5765, .hw_value = 153, },
+ {.center_freq = 5785, .hw_value = 157, },
+ {.center_freq = 5805, .hw_value = 161, },
+ {.center_freq = 5825, .hw_value = 165, },
+};
+
+static struct ieee80211_supported_band nxpwifi_band_5ghz = {
+ .channels = nxpwifi_channels_5ghz,
+ .n_channels = ARRAY_SIZE(nxpwifi_channels_5ghz),
+ .bitrates = nxpwifi_rates + 4,
+ .n_bitrates = ARRAY_SIZE(nxpwifi_rates) - 4,
+};
+
+/* Supported crypto cipher suits to be advertised to cfg80211 */
+static const u32 nxpwifi_cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+ WLAN_CIPHER_SUITE_SMS4,
+ WLAN_CIPHER_SUITE_AES_CMAC,
+};
+
+/* Supported mgmt frame types to be advertised to cfg80211 */
+static const struct ieee80211_txrx_stypes
+nxpwifi_mgmt_stypes[NUM_NL80211_IFTYPES] = {
+ [NL80211_IFTYPE_STATION] = {
+ .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
+ },
+ [NL80211_IFTYPE_AP] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4) |
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_ACTION >> 4),
+ },
+};
+
+/* CFG802.11 operation handler for setting bit rates.
+ *
+ * Function configures data rates to firmware using bitrate mask
+ * provided by cfg80211.
+ */
+static int
+nxpwifi_cfg80211_set_bitrate_mask(struct wiphy *wiphy,
+ struct net_device *dev,
+ unsigned int link_id,
+ const u8 *peer,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ u16 bitmap_rates[MAX_BITMAP_RATES_SIZE];
+ enum nl80211_band band;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (!priv->media_connected) {
+ nxpwifi_dbg(adapter, ERROR,
+ "Can not set Tx data rate in disconnected state\n");
+ return -EINVAL;
+ }
+
+ band = nxpwifi_band_to_radio_type(priv->curr_bss_params.band);
+
+ memset(bitmap_rates, 0, sizeof(bitmap_rates));
+
+ /* Fill HR/DSSS rates. */
+ if (band == NL80211_BAND_2GHZ)
+ bitmap_rates[0] = mask->control[band].legacy & 0x000f;
+
+ /* Fill OFDM rates */
+ if (band == NL80211_BAND_2GHZ)
+ bitmap_rates[1] = (mask->control[band].legacy & 0x0ff0) >> 4;
+ else
+ bitmap_rates[1] = mask->control[band].legacy;
+
+ /* Fill HT MCS rates */
+ bitmap_rates[2] = mask->control[band].ht_mcs[0];
+ if (adapter->hw_dev_mcs_support == HT_STREAM_2X2)
+ bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8;
+
+ /* Fill VHT MCS rates */
+ if (adapter->fw_api_ver == NXPWIFI_FW_V15) {
+ bitmap_rates[10] = mask->control[band].vht_mcs[0];
+ if (adapter->hw_dev_mcs_support == HT_STREAM_2X2)
+ bitmap_rates[11] = mask->control[band].vht_mcs[1];
+ }
+
+ return nxpwifi_send_cmd(priv, HOST_CMD_TX_RATE_CFG,
+ HOST_ACT_GEN_SET, 0, bitmap_rates, true);
+}
+
+/* CFG802.11 operation handler for connection quality monitoring.
+ *
+ * This function subscribes/unsubscribes HIGH_RSSI and LOW_RSSI
+ * events to FW.
+ */
+static int nxpwifi_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ s32 rssi_thold, u32 rssi_hyst)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ struct nxpwifi_ds_misc_subsc_evt subsc_evt;
+
+ priv->cqm_rssi_thold = rssi_thold;
+ priv->cqm_rssi_hyst = rssi_hyst;
+
+ memset(&subsc_evt, 0x00, sizeof(struct nxpwifi_ds_misc_subsc_evt));
+ subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH;
+
+ /* Subscribe/unsubscribe low and high rssi events */
+ if (rssi_thold && rssi_hyst) {
+ subsc_evt.action = HOST_ACT_BITWISE_SET;
+ subsc_evt.bcn_l_rssi_cfg.abs_value = abs(rssi_thold);
+ subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold);
+ subsc_evt.bcn_l_rssi_cfg.evt_freq = 1;
+ subsc_evt.bcn_h_rssi_cfg.evt_freq = 1;
+ return nxpwifi_send_cmd(priv,
+ HOST_CMD_802_11_SUBSCRIBE_EVENT,
+ 0, 0, &subsc_evt, true);
+ } else {
+ subsc_evt.action = HOST_ACT_BITWISE_CLR;
+ return nxpwifi_send_cmd(priv,
+ HOST_CMD_802_11_SUBSCRIBE_EVENT,
+ 0, 0, &subsc_evt, true);
+ }
+
+ return 0;
+}
+
+int nxpwifi_cfg80211_change_beacon_data(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_beacon_data *data)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ nxpwifi_cancel_scan(adapter);
+
+ if (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_UAP) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "%s: bss_type mismatched\n", __func__);
+ return -EINVAL;
+ }
+
+ if (nxpwifi_set_mgmt_ies(priv, data)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "%s: setting mgmt ies failed\n", __func__);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/* cfg80211 operation handler for change_beacon.
+ * Function retrieves and sets modified management IEs to FW.
+ */
+static int nxpwifi_cfg80211_change_beacon(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_ap_update *params)
+{
+ int ret;
+
+ ret = nxpwifi_cfg80211_change_beacon_data(wiphy, dev, ¶ms->beacon);
+
+ return ret;
+}
+
+/* cfg80211 operation handler for del_station.
+ * Function deauthenticates station which value is provided in mac parameter.
+ * If mac is NULL/broadcast, all stations in associated station list are
+ * deauthenticated. If bss is not started or there are no stations in
+ * associated stations list, no action is taken.
+ */
+static int
+nxpwifi_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
+ struct station_del_parameters *params)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ struct nxpwifi_sta_node *sta_node;
+ u8 deauth_mac[ETH_ALEN];
+
+ if (!priv->bss_started && priv->wdev.cac_started) {
+ nxpwifi_dbg(priv->adapter, INFO, "%s: abort CAC!\n", __func__);
+ nxpwifi_abort_cac(priv);
+ }
+
+ if (list_empty(&priv->sta_list) || !priv->bss_started)
+ return 0;
+
+ if (!params->mac || is_broadcast_ether_addr(params->mac))
+ return 0;
+
+ nxpwifi_dbg(priv->adapter, INFO, "%s: mac address %pM\n",
+ __func__, params->mac);
+
+ eth_zero_addr(deauth_mac);
+
+ spin_lock_bh(&priv->sta_list_spinlock);
+ sta_node = nxpwifi_get_sta_entry(priv, params->mac);
+ if (sta_node)
+ ether_addr_copy(deauth_mac, params->mac);
+ spin_unlock_bh(&priv->sta_list_spinlock);
+
+ if (is_valid_ether_addr(deauth_mac)) {
+ if (nxpwifi_send_cmd(priv, HOST_CMD_UAP_STA_DEAUTH,
+ HOST_ACT_GEN_SET, 0,
+ deauth_mac, true))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
+{
+ struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy);
+ struct nxpwifi_private *priv = nxpwifi_get_priv(adapter,
+ NXPWIFI_BSS_ROLE_ANY);
+ struct nxpwifi_ds_ant_cfg ant_cfg;
+
+ if (!tx_ant || !rx_ant)
+ return -EOPNOTSUPP;
+
+ if (adapter->hw_dev_mcs_support != HT_STREAM_2X2) {
+ /* Not a MIMO chip. User should provide specific antenna number
+ * for Tx/Rx path or enable all antennas for diversity
+ */
+ if (tx_ant != rx_ant)
+ return -EOPNOTSUPP;
+
+ if ((tx_ant & (tx_ant - 1)) &&
+ (tx_ant != BIT(adapter->number_of_antenna) - 1))
+ return -EOPNOTSUPP;
+
+ if ((tx_ant == BIT(adapter->number_of_antenna) - 1) &&
+ priv->adapter->number_of_antenna > 1) {
+ tx_ant = RF_ANTENNA_AUTO;
+ rx_ant = RF_ANTENNA_AUTO;
+ }
+ } else {
+ struct ieee80211_sta_ht_cap *ht_info;
+ int rx_mcs_supp;
+ enum nl80211_band band;
+
+ if ((tx_ant == 0x1 && rx_ant == 0x1)) {
+ adapter->user_dev_mcs_support = HT_STREAM_1X1;
+ if (adapter->is_hw_11ac_capable)
+ adapter->usr_dot_11ac_mcs_support =
+ NXPWIFI_11AC_MCS_MAP_1X1;
+ } else {
+ adapter->user_dev_mcs_support = HT_STREAM_2X2;
+ if (adapter->is_hw_11ac_capable)
+ adapter->usr_dot_11ac_mcs_support =
+ NXPWIFI_11AC_MCS_MAP_2X2;
+ }
+
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ if (!adapter->wiphy->bands[band])
+ continue;
+
+ ht_info = &adapter->wiphy->bands[band]->ht_cap;
+ rx_mcs_supp =
+ GET_RXMCSSUPP(adapter->user_dev_mcs_support);
+ memset(&ht_info->mcs, 0, adapter->number_of_antenna);
+ memset(&ht_info->mcs, 0xff, rx_mcs_supp);
+ }
+ }
+
+ ant_cfg.tx_ant = tx_ant;
+ ant_cfg.rx_ant = rx_ant;
+
+ return nxpwifi_send_cmd(priv, HOST_CMD_RF_ANTENNA,
+ HOST_ACT_GEN_SET, 0, &ant_cfg, true);
+}
+
+static int
+nxpwifi_cfg80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)
+{
+ struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy);
+ struct nxpwifi_private *priv = nxpwifi_get_priv(adapter,
+ NXPWIFI_BSS_ROLE_ANY);
+ nxpwifi_send_cmd(priv, HOST_CMD_RF_ANTENNA,
+ HOST_ACT_GEN_GET, 0, NULL, true);
+
+ *tx_ant = priv->tx_ant;
+ *rx_ant = priv->rx_ant;
+
+ return 0;
+}
+
+/* cfg80211 operation handler for stop ap.
+ * Function stops BSS running at uAP interface.
+ */
+static int nxpwifi_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
+ unsigned int link_id)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+
+ nxpwifi_abort_cac(priv);
+
+ if (nxpwifi_del_mgmt_ies(priv))
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Failed to delete mgmt IEs!\n");
+
+ priv->ap_11n_enabled = 0;
+ memset(&priv->bss_cfg, 0, sizeof(priv->bss_cfg));
+
+ if (nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_STOP,
+ HOST_ACT_GEN_SET, 0, NULL, true)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Failed to stop the BSS\n");
+ return -1;
+ }
+
+ if (nxpwifi_send_cmd(priv, HOST_CMD_APCMD_SYS_RESET,
+ HOST_ACT_GEN_SET, 0, NULL, true)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Failed to reset BSS\n");
+ return -1;
+ }
+
+ if (netif_carrier_ok(priv->netdev))
+ netif_carrier_off(priv->netdev);
+ nxpwifi_stop_net_dev_queue(priv->netdev, priv->adapter);
+
+ return 0;
+}
+
+/* cfg80211 operation handler for start_ap.
+ * Function sets beacon period, DTIM period, SSID and security into
+ * AP config structure.
+ * AP is configured with these settings and BSS is started.
+ */
+static int nxpwifi_cfg80211_start_ap(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_ap_settings *params)
+{
+ struct nxpwifi_uap_bss_param *bss_cfg;
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_private *tmp_priv;
+ int i;
+ struct nxpwifi_current_bss_params *bss_params;
+ enum nl80211_band band;
+ int freq;
+
+ if (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_UAP)
+ return -1;
+
+ for (i = 0; i < NXPWIFI_MAX_BSS_NUM; i++) {
+ tmp_priv = adapter->priv[i];
+ if (tmp_priv == priv)
+ continue;
+ if (GET_BSS_ROLE(tmp_priv) == NXPWIFI_BSS_ROLE_STA &&
+ tmp_priv->media_connected) {
+ bss_params = &tmp_priv->curr_bss_params;
+ band = nxpwifi_band_to_radio_type(bss_params->band);
+ freq = ieee80211_channel_to_frequency
+ (bss_params->bss_descriptor.channel, band);
+ if (!ieee80211_channel_equal
+ (params->chandef.chan,
+ ieee80211_get_channel(wiphy, freq))) {
+ nxpwifi_dbg
+ (priv->adapter, MSG,
+ "AP and STA must operate on same channel\n");
+ return -EOPNOTSUPP;
+ }
+ }
+ }
+
+ bss_cfg = kzalloc(sizeof(*bss_cfg), GFP_KERNEL);
+ if (!bss_cfg)
+ return -ENOMEM;
+
+ nxpwifi_set_sys_config_invalid_data(bss_cfg);
+
+ memcpy(bss_cfg->mac_addr, priv->curr_addr, ETH_ALEN);
+
+ if (params->beacon_interval)
+ bss_cfg->beacon_period = params->beacon_interval;
+ if (params->dtim_period)
+ bss_cfg->dtim_period = params->dtim_period;
+
+ if (params->ssid && params->ssid_len) {
+ memcpy(bss_cfg->ssid.ssid, params->ssid, params->ssid_len);
+ bss_cfg->ssid.ssid_len = params->ssid_len;
+ }
+ if (params->inactivity_timeout > 0) {
+ /* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */
+ bss_cfg->sta_ao_timer = 10 * params->inactivity_timeout;
+ bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout;
+ }
+
+ switch (params->hidden_ssid) {
+ case NL80211_HIDDEN_SSID_NOT_IN_USE:
+ bss_cfg->bcast_ssid_ctl = 1;
+ break;
+ case NL80211_HIDDEN_SSID_ZERO_LEN:
+ bss_cfg->bcast_ssid_ctl = 0;
+ break;
+ case NL80211_HIDDEN_SSID_ZERO_CONTENTS:
+ bss_cfg->bcast_ssid_ctl = 2;
+ break;
+ default:
+ kfree(bss_cfg);
+ return -EINVAL;
+ }
+
+ nxpwifi_uap_set_channel(priv, bss_cfg, params->chandef);
+ nxpwifi_set_uap_rates(bss_cfg, params);
+
+ if (nxpwifi_set_secure_params(priv, bss_cfg, params)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Failed to parse security parameters!\n");
+ goto out;
+ }
+
+ nxpwifi_set_ht_params(priv, bss_cfg, params);
+
+ if (priv->adapter->is_hw_11ac_capable) {
+ nxpwifi_set_vht_params(priv, bss_cfg, params);
+ nxpwifi_set_vht_width(priv, params->chandef.width,
+ priv->ap_11ac_enabled);
+ }
+
+ if (priv->ap_11ac_enabled)
+ nxpwifi_set_11ac_ba_params(priv);
+ else
+ nxpwifi_set_ba_params(priv);
+
+ nxpwifi_set_wmm_params(priv, bss_cfg, params);
+
+ if (nxpwifi_is_11h_active(priv))
+ nxpwifi_set_tpc_params(priv, bss_cfg, params);
+
+ if (nxpwifi_is_11h_active(priv) &&
+ !cfg80211_chandef_dfs_required(wiphy, ¶ms->chandef,
+ priv->bss_mode)) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "Disable 11h extensions in FW\n");
+ if (nxpwifi_11h_activate(priv, false)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Failed to disable 11h extensions!!");
+ goto out;
+ }
+ priv->state_11h.is_11h_active = false;
+ }
+
+ nxpwifi_config_uap_11d(priv, ¶ms->beacon);
+
+ if (nxpwifi_config_start_uap(priv, bss_cfg)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Failed to start AP\n");
+ goto out;
+ }
+
+ if (nxpwifi_set_mgmt_ies(priv, ¶ms->beacon))
+ goto out;
+
+ if (!netif_carrier_ok(priv->netdev))
+ netif_carrier_on(priv->netdev);
+ nxpwifi_wake_up_net_dev_queue(priv->netdev, priv->adapter);
+
+ memcpy(&priv->bss_cfg, bss_cfg, sizeof(priv->bss_cfg));
+ kfree(bss_cfg);
+ return 0;
+
+out:
+ kfree(bss_cfg);
+ return -1;
+}
+
+/* CFG802.11 operation handler for scan request.
+ *
+ * This function issues a scan request to the firmware based upon
+ * the user specified scan configuration. On successful completion,
+ * it also informs the results.
+ */
+static int
+nxpwifi_cfg80211_scan(struct wiphy *wiphy,
+ struct cfg80211_scan_request *request)
+{
+ struct net_device *dev = request->wdev->netdev;
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ int i, offset, ret;
+ struct ieee80211_channel *chan;
+ struct ieee_types_header *ie;
+ struct nxpwifi_user_scan_cfg *user_scan_cfg;
+ u8 mac_addr[ETH_ALEN];
+
+ nxpwifi_dbg(priv->adapter, CMD,
+ "info: received scan request on %s\n", dev->name);
+
+ /* Block scan request if scan operation or scan cleanup when interface
+ * is disabled is in process
+ */
+ if (priv->scan_request || priv->scan_aborting) {
+ nxpwifi_dbg(priv->adapter, WARN,
+ "cmd: Scan already in process..\n");
+ return -EBUSY;
+ }
+
+ if (!priv->wdev.connected && priv->scan_block)
+ priv->scan_block = false;
+
+ if (!nxpwifi_stop_bg_scan(priv))
+ cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0);
+
+ user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL);
+ if (!user_scan_cfg)
+ return -ENOMEM;
+
+ priv->scan_request = request;
+
+ if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ get_random_mask_addr(mac_addr, request->mac_addr,
+ request->mac_addr_mask);
+ ether_addr_copy(request->mac_addr, mac_addr);
+ ether_addr_copy(user_scan_cfg->random_mac, mac_addr);
+ }
+
+ user_scan_cfg->num_ssids = request->n_ssids;
+ user_scan_cfg->ssid_list = request->ssids;
+
+ if (request->ie && request->ie_len) {
+ offset = 0;
+ for (i = 0; i < NXPWIFI_MAX_VSIE_NUM; i++) {
+ if (priv->vs_ie[i].mask != NXPWIFI_VSIE_MASK_CLEAR)
+ continue;
+ priv->vs_ie[i].mask = NXPWIFI_VSIE_MASK_SCAN;
+ ie = (struct ieee_types_header *)(request->ie + offset);
+ memcpy(&priv->vs_ie[i].ie, ie, sizeof(*ie) + ie->len);
+ offset += sizeof(*ie) + ie->len;
+
+ if (offset >= request->ie_len)
+ break;
+ }
+ }
+
+ for (i = 0; i < min_t(u32, request->n_channels,
+ NXPWIFI_USER_SCAN_CHAN_MAX); i++) {
+ chan = request->channels[i];
+ user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
+ user_scan_cfg->chan_list[i].radio_type = chan->band;
+
+ if ((chan->flags & IEEE80211_CHAN_NO_IR) || !request->n_ssids)
+ user_scan_cfg->chan_list[i].scan_type =
+ NXPWIFI_SCAN_TYPE_PASSIVE;
+ else
+ user_scan_cfg->chan_list[i].scan_type =
+ NXPWIFI_SCAN_TYPE_ACTIVE;
+
+ user_scan_cfg->chan_list[i].scan_time = 0;
+ }
+
+ if (priv->adapter->scan_chan_gap_enabled &&
+ nxpwifi_is_any_intf_active(priv))
+ user_scan_cfg->scan_chan_gap =
+ priv->adapter->scan_chan_gap_time;
+
+ ret = nxpwifi_scan_networks(priv, user_scan_cfg);
+ kfree(user_scan_cfg);
+ if (ret) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "scan failed: %d\n", ret);
+ priv->scan_aborting = false;
+ priv->scan_request = NULL;
+ return ret;
+ }
+
+ if (request->ie && request->ie_len) {
+ for (i = 0; i < NXPWIFI_MAX_VSIE_NUM; i++) {
+ if (priv->vs_ie[i].mask == NXPWIFI_VSIE_MASK_SCAN) {
+ priv->vs_ie[i].mask = NXPWIFI_VSIE_MASK_CLEAR;
+ memset(&priv->vs_ie[i].ie, 0,
+ NXPWIFI_MAX_VSIE_LEN);
+ }
+ }
+ }
+ return 0;
+}
+
+/* CFG802.11 operation handler for sched_scan_start.
+ *
+ * This function issues a bgscan config request to the firmware based upon
+ * the user specified sched_scan configuration. On successful completion,
+ * firmware will generate BGSCAN_REPORT event, driver should issue bgscan
+ * query command to get sched_scan results from firmware.
+ */
+static int
+nxpwifi_cfg80211_sched_scan_start(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_sched_scan_request *request)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ int i, offset;
+ struct ieee80211_channel *chan;
+ struct nxpwifi_bg_scan_cfg *bgscan_cfg;
+ struct ieee_types_header *ie;
+
+ if (!request || (!request->n_ssids && !request->n_match_sets)) {
+ wiphy_err(wiphy, "%s : Invalid Sched_scan parameters",
+ __func__);
+ return -EINVAL;
+ }
+
+ wiphy_info(wiphy, "sched_scan start : n_ssids=%d n_match_sets=%d ",
+ request->n_ssids, request->n_match_sets);
+ wiphy_info(wiphy, "n_channels=%d interval=%d ie_len=%d\n",
+ request->n_channels, request->scan_plans->interval,
+ (int)request->ie_len);
+
+ bgscan_cfg = kzalloc(sizeof(*bgscan_cfg), GFP_KERNEL);
+ if (!bgscan_cfg)
+ return -ENOMEM;
+
+ if (priv->scan_request || priv->scan_aborting)
+ bgscan_cfg->start_later = true;
+
+ bgscan_cfg->num_ssids = request->n_match_sets;
+ bgscan_cfg->ssid_list = request->match_sets;
+
+ if (request->ie && request->ie_len) {
+ offset = 0;
+ for (i = 0; i < NXPWIFI_MAX_VSIE_NUM; i++) {
+ if (priv->vs_ie[i].mask != NXPWIFI_VSIE_MASK_CLEAR)
+ continue;
+ priv->vs_ie[i].mask = NXPWIFI_VSIE_MASK_BGSCAN;
+ ie = (struct ieee_types_header *)(request->ie + offset);
+ memcpy(&priv->vs_ie[i].ie, ie, sizeof(*ie) + ie->len);
+ offset += sizeof(*ie) + ie->len;
+
+ if (offset >= request->ie_len)
+ break;
+ }
+ }
+
+ for (i = 0; i < min_t(u32, request->n_channels,
+ NXPWIFI_BG_SCAN_CHAN_MAX); i++) {
+ chan = request->channels[i];
+ bgscan_cfg->chan_list[i].chan_number = chan->hw_value;
+ bgscan_cfg->chan_list[i].radio_type = chan->band;
+
+ if ((chan->flags & IEEE80211_CHAN_NO_IR) || !request->n_ssids)
+ bgscan_cfg->chan_list[i].scan_type =
+ NXPWIFI_SCAN_TYPE_PASSIVE;
+ else
+ bgscan_cfg->chan_list[i].scan_type =
+ NXPWIFI_SCAN_TYPE_ACTIVE;
+
+ bgscan_cfg->chan_list[i].scan_time = 0;
+ }
+
+ bgscan_cfg->chan_per_scan = min_t(u32, request->n_channels,
+ NXPWIFI_BG_SCAN_CHAN_MAX);
+
+ /* Use at least 15 second for per scan cycle */
+ bgscan_cfg->scan_interval = (request->scan_plans->interval >
+ NXPWIFI_BGSCAN_INTERVAL) ?
+ request->scan_plans->interval :
+ NXPWIFI_BGSCAN_INTERVAL;
+
+ bgscan_cfg->repeat_count = NXPWIFI_BGSCAN_REPEAT_COUNT;
+ bgscan_cfg->report_condition = NXPWIFI_BGSCAN_SSID_MATCH |
+ NXPWIFI_BGSCAN_WAIT_ALL_CHAN_DONE;
+ bgscan_cfg->bss_type = NXPWIFI_BSS_MODE_INFRA;
+ bgscan_cfg->action = NXPWIFI_BGSCAN_ACT_SET;
+ bgscan_cfg->enable = true;
+ if (request->min_rssi_thold != NL80211_SCAN_RSSI_THOLD_OFF) {
+ bgscan_cfg->report_condition |= NXPWIFI_BGSCAN_SSID_RSSI_MATCH;
+ bgscan_cfg->rssi_threshold = request->min_rssi_thold;
+ }
+
+ if (nxpwifi_send_cmd(priv, HOST_CMD_802_11_BG_SCAN_CONFIG,
+ HOST_ACT_GEN_SET, 0, bgscan_cfg, true)) {
+ kfree(bgscan_cfg);
+ return -EFAULT;
+ }
+
+ priv->sched_scanning = true;
+
+ kfree(bgscan_cfg);
+ return 0;
+}
+
+/* CFG802.11 operation handler for sched_scan_stop.
+ *
+ * This function issues a bgscan config command to disable
+ * previous bgscan configuration in the firmware
+ */
+static int nxpwifi_cfg80211_sched_scan_stop(struct wiphy *wiphy,
+ struct net_device *dev, u64 reqid)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+
+ wiphy_info(wiphy, "sched scan stop!");
+ nxpwifi_stop_bg_scan(priv);
+
+ return 0;
+}
+
+static void nxpwifi_setup_vht_caps(struct ieee80211_sta_vht_cap *vht_info,
+ struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ vht_info->vht_supported = true;
+
+ vht_info->cap = adapter->hw_dot_11ac_dev_cap;
+ /* Update MCS support for VHT */
+ vht_info->vht_mcs.rx_mcs_map =
+ cpu_to_le16(adapter->hw_dot_11ac_mcs_support & 0xFFFF);
+ vht_info->vht_mcs.rx_highest = 0;
+ vht_info->vht_mcs.tx_mcs_map =
+ cpu_to_le16(adapter->hw_dot_11ac_mcs_support >> 16);
+ vht_info->vht_mcs.tx_highest = 0;
+}
+
+/* This function sets up the CFG802.11 specific HT capability fields
+ * with default values.
+ *
+ * The following default values are set -
+ * - HT Supported = True
+ * - Maximum AMPDU length factor = IEEE80211_HT_MAX_AMPDU_64K
+ * - Minimum AMPDU spacing = IEEE80211_HT_MPDU_DENSITY_NONE
+ * - HT Capabilities supported by firmware
+ * - MCS information, Rx mask = 0xff
+ * - MCD information, Tx parameters = IEEE80211_HT_MCS_TX_DEFINED (0x01)
+ */
+static void
+nxpwifi_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info,
+ struct nxpwifi_private *priv)
+{
+ int rx_mcs_supp;
+ struct ieee80211_mcs_info mcs_set;
+ u8 *mcs = (u8 *)&mcs_set;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ ht_info->ht_supported = true;
+ ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+ ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
+
+ memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
+
+ /* Fill HT capability information */
+ if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+ if (ISSUPP_SHORTGI20(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_20;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_SGI_20;
+
+ if (ISSUPP_SHORTGI40(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_SGI_40;
+
+ if (adapter->user_dev_mcs_support == HT_STREAM_2X2)
+ ht_info->cap |= 2 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
+ else
+ ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
+
+ if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_TX_STBC;
+
+ if (ISSUPP_GREENFIELD(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_GRN_FLD;
+
+ if (ISENABLED_40MHZ_INTOLERANT(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_40MHZ_INTOLERANT;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_40MHZ_INTOLERANT;
+
+ if (ISSUPP_RXLDPC(adapter->hw_dot_11n_dev_cap))
+ ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING;
+ else
+ ht_info->cap &= ~IEEE80211_HT_CAP_LDPC_CODING;
+
+ ht_info->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU;
+ ht_info->cap |= IEEE80211_HT_CAP_SM_PS;
+
+ rx_mcs_supp = GET_RXMCSSUPP(adapter->user_dev_mcs_support);
+ /* Set MCS for 1x1/2x2 */
+ memset(mcs, 0xff, rx_mcs_supp);
+ /* Clear all the other values */
+ memset(&mcs[rx_mcs_supp], 0,
+ sizeof(struct ieee80211_mcs_info) - rx_mcs_supp);
+ if (priv->bss_mode == NL80211_IFTYPE_STATION ||
+ ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap))
+ /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
+ SETHT_MCS32(mcs_set.rx_mask);
+
+ memcpy((u8 *)&ht_info->mcs, mcs, sizeof(struct ieee80211_mcs_info));
+
+ ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+}
+
+/* create a new virtual interface with the given name and name assign type
+ */
+struct wireless_dev *nxpwifi_add_virtual_intf(struct wiphy *wiphy,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy);
+ struct nxpwifi_private *priv;
+ struct net_device *dev;
+ void *mdev_priv;
+ int ret;
+
+ if (!adapter)
+ return ERR_PTR(-EFAULT);
+
+ switch (type) {
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_STATION:
+ if (adapter->curr_iface_comb.sta_intf ==
+ adapter->iface_limit.sta_intf) {
+ nxpwifi_dbg(adapter, ERROR,
+ "cannot create multiple sta ifaces\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ priv = nxpwifi_get_unused_priv_by_bss_type
+ (adapter, NXPWIFI_BSS_TYPE_STA);
+ if (!priv) {
+ nxpwifi_dbg(adapter, ERROR,
+ "could not get free private struct\n");
+ return ERR_PTR(-EFAULT);
+ }
+
+ priv->wdev.wiphy = wiphy;
+ priv->wdev.iftype = NL80211_IFTYPE_STATION;
+
+ if (type == NL80211_IFTYPE_UNSPECIFIED)
+ priv->bss_mode = NL80211_IFTYPE_STATION;
+ else
+ priv->bss_mode = type;
+
+ priv->bss_type = NXPWIFI_BSS_TYPE_STA;
+ priv->frame_type = NXPWIFI_DATA_FRAME_TYPE_ETH_II;
+ priv->bss_priority = 0;
+ priv->bss_role = NXPWIFI_BSS_ROLE_STA;
+
+ break;
+ case NL80211_IFTYPE_AP:
+ if (adapter->curr_iface_comb.uap_intf ==
+ adapter->iface_limit.uap_intf) {
+ nxpwifi_dbg(adapter, ERROR,
+ "cannot create multiple AP ifaces\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ priv = nxpwifi_get_unused_priv_by_bss_type
+ (adapter, NXPWIFI_BSS_TYPE_UAP);
+ if (!priv) {
+ nxpwifi_dbg(adapter, ERROR,
+ "could not get free private struct\n");
+ return ERR_PTR(-EFAULT);
+ }
+
+ priv->wdev.wiphy = wiphy;
+ priv->wdev.iftype = NL80211_IFTYPE_AP;
+
+ priv->bss_type = NXPWIFI_BSS_TYPE_UAP;
+ priv->frame_type = NXPWIFI_DATA_FRAME_TYPE_ETH_II;
+ priv->bss_priority = 0;
+ priv->bss_role = NXPWIFI_BSS_ROLE_UAP;
+ priv->bss_started = 0;
+ priv->bss_mode = type;
+
+ break;
+ default:
+ nxpwifi_dbg(adapter, ERROR, "type not supported\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ dev = alloc_netdev_mqs(sizeof(struct nxpwifi_private *), name,
+ name_assign_type, ether_setup,
+ IEEE80211_NUM_ACS, 1);
+ if (!dev) {
+ nxpwifi_dbg(adapter, ERROR,
+ "no memory available for netdevice\n");
+ ret = -ENOMEM;
+ goto err_alloc_netdev;
+ }
+
+ nxpwifi_init_priv_params(priv, dev);
+
+ priv->netdev = dev;
+
+ nxpwifi_set_mac_address(priv, dev, false, NULL);
+
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_SET_BSS_MODE,
+ HOST_ACT_GEN_SET, 0, NULL, true);
+ if (ret)
+ goto err_set_bss_mode;
+
+ ret = nxpwifi_sta_init_cmd(priv, false, false);
+ if (ret)
+ goto err_sta_init;
+
+ nxpwifi_setup_ht_caps(&wiphy->bands[NL80211_BAND_2GHZ]->ht_cap, priv);
+ if (adapter->is_hw_11ac_capable)
+ nxpwifi_setup_vht_caps
+ (&wiphy->bands[NL80211_BAND_2GHZ]->vht_cap, priv);
+
+ if (adapter->config_bands & BAND_A)
+ nxpwifi_setup_ht_caps
+ (&wiphy->bands[NL80211_BAND_5GHZ]->ht_cap, priv);
+
+ if ((adapter->config_bands & BAND_A) && adapter->is_hw_11ac_capable)
+ nxpwifi_setup_vht_caps
+ (&wiphy->bands[NL80211_BAND_5GHZ]->vht_cap, priv);
+
+ dev_net_set(dev, wiphy_net(wiphy));
+ dev->ieee80211_ptr = &priv->wdev;
+ dev->ieee80211_ptr->iftype = priv->bss_mode;
+ SET_NETDEV_DEV(dev, wiphy_dev(wiphy));
+
+ dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
+ dev->watchdog_timeo = NXPWIFI_DEFAULT_WATCHDOG_TIMEOUT;
+ dev->needed_headroom = NXPWIFI_MIN_DATA_HEADER_LEN;
+ dev->ethtool_ops = &nxpwifi_ethtool_ops;
+
+ mdev_priv = netdev_priv(dev);
+ *((unsigned long *)mdev_priv) = (unsigned long)priv;
+
+ SET_NETDEV_DEV(dev, adapter->dev);
+
+ priv->dfs_cac_workqueue = alloc_workqueue("NXPWIFI_DFS_CAC%s",
+ WQ_HIGHPRI |
+ WQ_MEM_RECLAIM |
+ WQ_UNBOUND, 0, name);
+ if (!priv->dfs_cac_workqueue) {
+ nxpwifi_dbg(adapter, ERROR, "cannot alloc DFS CAC queue\n");
+ ret = -ENOMEM;
+ goto err_alloc_cac;
+ }
+
+ INIT_DELAYED_WORK(&priv->dfs_cac_work, nxpwifi_dfs_cac_work_queue);
+
+ priv->dfs_chan_sw_workqueue = alloc_workqueue("NXPWIFI_DFS_CHSW%s",
+ WQ_HIGHPRI | WQ_UNBOUND |
+ WQ_MEM_RECLAIM, 0, name);
+ if (!priv->dfs_chan_sw_workqueue) {
+ nxpwifi_dbg(adapter, ERROR, "cannot alloc DFS channel sw queue\n");
+ ret = -ENOMEM;
+ goto err_alloc_chsw;
+ }
+
+ INIT_DELAYED_WORK(&priv->dfs_chan_sw_work,
+ nxpwifi_dfs_chan_sw_work_queue);
+
+ mutex_init(&priv->async_mutex);
+
+ /* Register network device */
+ if (cfg80211_register_netdevice(dev)) {
+ nxpwifi_dbg(adapter, ERROR, "cannot register network device\n");
+ ret = -EFAULT;
+ goto err_reg_netdev;
+ }
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: %s: NXP 802.11 Adapter\n", dev->name);
+
+#ifdef CONFIG_DEBUG_FS
+ nxpwifi_dev_debugfs_init(priv);
+#endif
+
+ update_vif_type_counter(adapter, type, 1);
+
+ return &priv->wdev;
+
+err_reg_netdev:
+ destroy_workqueue(priv->dfs_chan_sw_workqueue);
+ priv->dfs_chan_sw_workqueue = NULL;
+err_alloc_chsw:
+ destroy_workqueue(priv->dfs_cac_workqueue);
+ priv->dfs_cac_workqueue = NULL;
+err_alloc_cac:
+ free_netdev(dev);
+ priv->netdev = NULL;
+err_sta_init:
+err_set_bss_mode:
+err_alloc_netdev:
+ memset(&priv->wdev, 0, sizeof(priv->wdev));
+ priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(nxpwifi_add_virtual_intf);
+
+/* del_virtual_intf: remove the virtual interface determined by dev
+ */
+int nxpwifi_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev);
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct sk_buff *skb, *tmp;
+
+#ifdef CONFIG_DEBUG_FS
+ nxpwifi_dev_debugfs_remove(priv);
+#endif
+
+ if (priv->sched_scanning)
+ priv->sched_scanning = false;
+
+ nxpwifi_stop_net_dev_queue(priv->netdev, adapter);
+
+ skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) {
+ skb_unlink(skb, &priv->bypass_txq);
+ nxpwifi_write_data_complete(priv->adapter, skb, 0, -1);
+ }
+
+ if (netif_carrier_ok(priv->netdev))
+ netif_carrier_off(priv->netdev);
+
+ if (wdev->netdev->reg_state == NETREG_REGISTERED)
+ cfg80211_unregister_netdevice(wdev->netdev);
+
+ if (priv->dfs_cac_workqueue) {
+ destroy_workqueue(priv->dfs_cac_workqueue);
+ priv->dfs_cac_workqueue = NULL;
+ }
+
+ if (priv->dfs_chan_sw_workqueue) {
+ destroy_workqueue(priv->dfs_chan_sw_workqueue);
+ priv->dfs_chan_sw_workqueue = NULL;
+ }
+ /* Clear the priv in adapter */
+ priv->netdev = NULL;
+
+ update_vif_type_counter(adapter, priv->bss_mode, -1);
+
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA ||
+ GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP)
+ kfree(priv->hist_data);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nxpwifi_del_virtual_intf);
+
+static bool
+nxpwifi_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq,
+ u8 max_byte_seq)
+{
+ int j, k, valid_byte_cnt = 0;
+ bool dont_care_byte = false;
+
+ for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) {
+ for (k = 0; k < 8; k++) {
+ if (pat->mask[j] & 1 << k) {
+ memcpy(byte_seq + valid_byte_cnt,
+ &pat->pattern[j * 8 + k], 1);
+ valid_byte_cnt++;
+ if (dont_care_byte)
+ return false;
+ } else {
+ if (valid_byte_cnt)
+ dont_care_byte = true;
+ }
+
+ /* wildcard bytes record as the offset
+ * before the valid byte
+ */
+ if (!valid_byte_cnt && !dont_care_byte)
+ pat->pkt_offset++;
+
+ if (valid_byte_cnt > max_byte_seq)
+ return false;
+ }
+ }
+
+ byte_seq[max_byte_seq] = valid_byte_cnt;
+
+ return true;
+}
+
+#ifdef CONFIG_PM
+static void nxpwifi_set_auto_arp_mef_entry(struct nxpwifi_private *priv,
+ struct nxpwifi_mef_entry *mef_entry)
+{
+ int i, filt_num = 0, num_ipv4 = 0;
+ struct in_device *in_dev;
+ struct in_ifaddr *ifa;
+ __be32 ips[NXPWIFI_MAX_SUPPORTED_IPADDR];
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ mef_entry->mode = MEF_MODE_HOST_SLEEP;
+ mef_entry->action = MEF_ACTION_AUTO_ARP;
+
+ /* Enable ARP offload feature */
+ memset(ips, 0, sizeof(ips));
+ for (i = 0; i < NXPWIFI_MAX_BSS_NUM; i++) {
+ if (adapter->priv[i]->netdev) {
+ in_dev = __in_dev_get_rtnl(adapter->priv[i]->netdev);
+ if (!in_dev)
+ continue;
+ ifa = rtnl_dereference(in_dev->ifa_list);
+ if (!ifa || !ifa->ifa_local)
+ continue;
+ ips[i] = ifa->ifa_local;
+ num_ipv4++;
+ }
+ }
+
+ for (i = 0; i < num_ipv4; i++) {
+ if (!ips[i])
+ continue;
+ mef_entry->filter[filt_num].repeat = 1;
+ memcpy(mef_entry->filter[filt_num].byte_seq,
+ (u8 *)&ips[i], sizeof(ips[i]));
+ mef_entry->filter[filt_num].byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] =
+ sizeof(ips[i]);
+ mef_entry->filter[filt_num].offset = 46;
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+ if (filt_num) {
+ mef_entry->filter[filt_num].filt_action =
+ TYPE_OR;
+ }
+ filt_num++;
+ }
+
+ mef_entry->filter[filt_num].repeat = 1;
+ mef_entry->filter[filt_num].byte_seq[0] = 0x08;
+ mef_entry->filter[filt_num].byte_seq[1] = 0x06;
+ mef_entry->filter[filt_num].byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] = 2;
+ mef_entry->filter[filt_num].offset = 20;
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+ mef_entry->filter[filt_num].filt_action = TYPE_AND;
+}
+
+static int nxpwifi_set_wowlan_mef_entry(struct nxpwifi_private *priv,
+ struct nxpwifi_ds_mef_cfg *mef_cfg,
+ struct nxpwifi_mef_entry *mef_entry,
+ struct cfg80211_wowlan *wowlan)
+{
+ int i, filt_num = 0, ret = 0;
+ bool first_pat = true;
+ u8 byte_seq[NXPWIFI_MEF_MAX_BYTESEQ + 1];
+ static const u8 ipv4_mc_mac[] = {0x33, 0x33};
+ static const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
+
+ mef_entry->mode = MEF_MODE_HOST_SLEEP;
+ mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST;
+
+ for (i = 0; i < wowlan->n_patterns; i++) {
+ memset(byte_seq, 0, sizeof(byte_seq));
+ if (!nxpwifi_is_pattern_supported
+ (&wowlan->patterns[i], byte_seq,
+ NXPWIFI_MEF_MAX_BYTESEQ)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Pattern not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!wowlan->patterns[i].pkt_offset) {
+ if (is_unicast_ether_addr(byte_seq) &&
+ byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] == 1) {
+ mef_cfg->criteria |= NXPWIFI_CRITERIA_UNICAST;
+ continue;
+ } else if (is_broadcast_ether_addr(byte_seq)) {
+ mef_cfg->criteria |= NXPWIFI_CRITERIA_BROADCAST;
+ continue;
+ } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
+ (byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] == 2)) ||
+ (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
+ (byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] == 3))) {
+ mef_cfg->criteria |= NXPWIFI_CRITERIA_MULTICAST;
+ continue;
+ }
+ }
+ mef_entry->filter[filt_num].repeat = 1;
+ mef_entry->filter[filt_num].offset =
+ wowlan->patterns[i].pkt_offset;
+ memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq,
+ sizeof(byte_seq));
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+
+ if (first_pat) {
+ first_pat = false;
+ nxpwifi_dbg(priv->adapter, INFO, "Wake on patterns\n");
+ } else {
+ mef_entry->filter[filt_num].filt_action = TYPE_AND;
+ }
+
+ filt_num++;
+ }
+
+ if (wowlan->magic_pkt) {
+ mef_cfg->criteria |= NXPWIFI_CRITERIA_UNICAST;
+ mef_entry->filter[filt_num].repeat = 16;
+ memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
+ ETH_ALEN);
+ mef_entry->filter[filt_num].byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] =
+ ETH_ALEN;
+ mef_entry->filter[filt_num].offset = 28;
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+ if (filt_num)
+ mef_entry->filter[filt_num].filt_action = TYPE_OR;
+
+ filt_num++;
+ mef_entry->filter[filt_num].repeat = 16;
+ memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
+ ETH_ALEN);
+ mef_entry->filter[filt_num].byte_seq[NXPWIFI_MEF_MAX_BYTESEQ] =
+ ETH_ALEN;
+ mef_entry->filter[filt_num].offset = 56;
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+ mef_entry->filter[filt_num].filt_action = TYPE_OR;
+ nxpwifi_dbg(priv->adapter, INFO, "Wake on magic packet\n");
+ }
+ return ret;
+}
+
+static int nxpwifi_set_mef_filter(struct nxpwifi_private *priv,
+ struct cfg80211_wowlan *wowlan)
+{
+ int ret = 0, num_entries = 1;
+ struct nxpwifi_ds_mef_cfg mef_cfg;
+ struct nxpwifi_mef_entry *mef_entry;
+
+ if (wowlan->n_patterns || wowlan->magic_pkt)
+ num_entries++;
+
+ mef_entry = kcalloc(num_entries, sizeof(*mef_entry), GFP_KERNEL);
+ if (!mef_entry)
+ return -ENOMEM;
+
+ memset(&mef_cfg, 0, sizeof(mef_cfg));
+ mef_cfg.criteria |= NXPWIFI_CRITERIA_BROADCAST |
+ NXPWIFI_CRITERIA_UNICAST;
+ mef_cfg.num_entries = num_entries;
+ mef_cfg.mef_entry = mef_entry;
+
+ nxpwifi_set_auto_arp_mef_entry(priv, &mef_entry[0]);
+
+ if (wowlan->n_patterns || wowlan->magic_pkt) {
+ ret = nxpwifi_set_wowlan_mef_entry(priv, &mef_cfg,
+ &mef_entry[1], wowlan);
+ if (ret)
+ goto err;
+ }
+
+ if (!mef_cfg.criteria)
+ mef_cfg.criteria = NXPWIFI_CRITERIA_BROADCAST |
+ NXPWIFI_CRITERIA_UNICAST |
+ NXPWIFI_CRITERIA_MULTICAST;
+
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_MEF_CFG, HOST_ACT_GEN_SET, 0,
+ &mef_cfg, true);
+
+err:
+ kfree(mef_entry);
+ return ret;
+}
+
+static int nxpwifi_cfg80211_suspend(struct wiphy *wiphy,
+ struct cfg80211_wowlan *wowlan)
+{
+ struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy);
+ struct nxpwifi_ds_hs_cfg hs_cfg;
+ int i, ret = 0, retry_num = 10;
+ struct nxpwifi_private *priv;
+ struct nxpwifi_private *sta_priv =
+ nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA);
+
+ sta_priv->scan_aborting = true;
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ nxpwifi_abort_cac(priv);
+ }
+
+ nxpwifi_cancel_all_pending_cmd(adapter);
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (priv && priv->netdev)
+ netif_device_detach(priv->netdev);
+ }
+
+ for (i = 0; i < retry_num; i++) {
+ if (!nxpwifi_wmm_lists_empty(adapter) ||
+ !nxpwifi_bypass_txlist_empty(adapter) ||
+ !skb_queue_empty(&adapter->tx_data_q))
+ usleep_range(10000, 15000);
+ else
+ break;
+ }
+
+ if (!wowlan) {
+ nxpwifi_dbg(adapter, INFO,
+ "None of the WOWLAN triggers enabled\n");
+ ret = 0;
+ goto done;
+ }
+
+ if (!sta_priv->media_connected && !wowlan->nd_config) {
+ nxpwifi_dbg(adapter, ERROR,
+ "Can not configure WOWLAN in disconnected state\n");
+ ret = 0;
+ goto done;
+ }
+
+ ret = nxpwifi_set_mef_filter(sta_priv, wowlan);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR, "Failed to set MEF filter\n");
+ goto done;
+ }
+
+ memset(&hs_cfg, 0, sizeof(hs_cfg));
+ hs_cfg.conditions = le32_to_cpu(adapter->hs_cfg.conditions);
+
+ if (wowlan->nd_config) {
+ nxpwifi_dbg(adapter, INFO, "Wake on net detect\n");
+ hs_cfg.conditions |= HS_CFG_COND_MAC_EVENT;
+ nxpwifi_cfg80211_sched_scan_start(wiphy, sta_priv->netdev,
+ wowlan->nd_config);
+ }
+
+ if (wowlan->disconnect) {
+ hs_cfg.conditions |= HS_CFG_COND_MAC_EVENT;
+ nxpwifi_dbg(sta_priv->adapter, INFO, "Wake on device disconnect\n");
+ }
+
+ hs_cfg.is_invoke_hostcmd = false;
+ hs_cfg.gpio = adapter->hs_cfg.gpio;
+ hs_cfg.gap = adapter->hs_cfg.gap;
+ ret = nxpwifi_set_hs_params(sta_priv, HOST_ACT_GEN_SET,
+ NXPWIFI_SYNC_CMD, &hs_cfg);
+ if (ret)
+ nxpwifi_dbg(adapter, ERROR, "Failed to set HS params\n");
+
+done:
+ sta_priv->scan_aborting = false;
+ return ret;
+}
+
+static int nxpwifi_cfg80211_resume(struct wiphy *wiphy)
+{
+ struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy);
+ struct nxpwifi_private *priv;
+ struct nxpwifi_ds_wakeup_reason wakeup_reason;
+ struct cfg80211_wowlan_wakeup wakeup_report;
+ int i;
+ bool report_wakeup_reason = true;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (priv && priv->netdev)
+ netif_device_attach(priv->netdev);
+ }
+
+ if (!wiphy->wowlan_config)
+ goto done;
+
+ priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA);
+ nxpwifi_get_wakeup_reason(priv, HOST_ACT_GEN_GET, NXPWIFI_SYNC_CMD,
+ &wakeup_reason);
+ memset(&wakeup_report, 0, sizeof(struct cfg80211_wowlan_wakeup));
+
+ wakeup_report.pattern_idx = -1;
+
+ switch (wakeup_reason.hs_wakeup_reason) {
+ case NO_HSWAKEUP_REASON:
+ break;
+ case BCAST_DATA_MATCHED:
+ break;
+ case MCAST_DATA_MATCHED:
+ break;
+ case UCAST_DATA_MATCHED:
+ break;
+ case MASKTABLE_EVENT_MATCHED:
+ break;
+ case NON_MASKABLE_EVENT_MATCHED:
+ if (wiphy->wowlan_config->disconnect)
+ wakeup_report.disconnect = true;
+ if (wiphy->wowlan_config->nd_config)
+ wakeup_report.net_detect = adapter->nd_info;
+ break;
+ case NON_MASKABLE_CONDITION_MATCHED:
+ break;
+ case MAGIC_PATTERN_MATCHED:
+ if (wiphy->wowlan_config->magic_pkt)
+ wakeup_report.magic_pkt = true;
+ if (wiphy->wowlan_config->n_patterns)
+ wakeup_report.pattern_idx = 1;
+ break;
+ case GTK_REKEY_FAILURE:
+ if (wiphy->wowlan_config->gtk_rekey_failure)
+ wakeup_report.gtk_rekey_failure = true;
+ break;
+ default:
+ report_wakeup_reason = false;
+ break;
+ }
+
+ if (report_wakeup_reason)
+ cfg80211_report_wowlan_wakeup(&priv->wdev, &wakeup_report,
+ GFP_KERNEL);
+
+done:
+ if (adapter->nd_info) {
+ for (i = 0 ; i < adapter->nd_info->n_matches ; i++)
+ kfree(adapter->nd_info->matches[i]);
+ kfree(adapter->nd_info);
+ adapter->nd_info = NULL;
+ }
+
+ return 0;
+}
+
+static void nxpwifi_cfg80211_set_wakeup(struct wiphy *wiphy,
+ bool enabled)
+{
+ struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy);
+
+ device_set_wakeup_enable(adapter->dev, enabled);
+}
+#endif
+
+static int nxpwifi_get_coalesce_pkt_type(u8 *byte_seq)
+{
+ static const u8 ipv4_mc_mac[] = {0x33, 0x33};
+ static const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
+ static const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff};
+
+ if ((byte_seq[0] & 0x01) &&
+ byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ] == 1)
+ return PACKET_TYPE_UNICAST;
+ else if (!memcmp(byte_seq, bc_mac, 4))
+ return PACKET_TYPE_BROADCAST;
+ else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
+ byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ] == 2) ||
+ (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
+ byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ] == 3))
+ return PACKET_TYPE_MULTICAST;
+
+ return 0;
+}
+
+static int
+nxpwifi_fill_coalesce_rule_info(struct nxpwifi_private *priv,
+ struct cfg80211_coalesce_rules *crule,
+ struct nxpwifi_coalesce_rule *mrule)
+{
+ u8 byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ + 1];
+ struct filt_field_param *param;
+ int i;
+
+ mrule->max_coalescing_delay = crule->delay;
+
+ param = mrule->params;
+
+ for (i = 0; i < crule->n_patterns; i++) {
+ memset(byte_seq, 0, sizeof(byte_seq));
+ if (!nxpwifi_is_pattern_supported(&crule->patterns[i],
+ byte_seq,
+ NXPWIFI_COALESCE_MAX_BYTESEQ)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Pattern not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!crule->patterns[i].pkt_offset) {
+ u8 pkt_type;
+
+ pkt_type = nxpwifi_get_coalesce_pkt_type(byte_seq);
+ if (pkt_type && mrule->pkt_type) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Multiple packet types not allowed\n");
+ return -EOPNOTSUPP;
+ } else if (pkt_type) {
+ mrule->pkt_type = pkt_type;
+ continue;
+ }
+ }
+
+ if (crule->condition == NL80211_COALESCE_CONDITION_MATCH)
+ param->operation = RECV_FILTER_MATCH_TYPE_EQ;
+ else
+ param->operation = RECV_FILTER_MATCH_TYPE_NE;
+
+ param->operand_len = byte_seq[NXPWIFI_COALESCE_MAX_BYTESEQ];
+ memcpy(param->operand_byte_stream, byte_seq,
+ param->operand_len);
+ param->offset = crule->patterns[i].pkt_offset;
+ param++;
+
+ mrule->num_of_fields++;
+ }
+
+ if (!mrule->pkt_type) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Packet type can not be determined\n");
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int nxpwifi_cfg80211_set_coalesce(struct wiphy *wiphy,
+ struct cfg80211_coalesce *coalesce)
+{
+ struct nxpwifi_adapter *adapter = nxpwifi_cfg80211_get_adapter(wiphy);
+ int i, ret;
+ struct nxpwifi_ds_coalesce_cfg coalesce_cfg;
+ struct nxpwifi_private *priv =
+ nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA);
+
+ memset(&coalesce_cfg, 0, sizeof(coalesce_cfg));
+ if (!coalesce) {
+ nxpwifi_dbg(adapter, WARN,
+ "Disable coalesce and reset all previous rules\n");
+ return nxpwifi_send_cmd(priv, HOST_CMD_COALESCE_CFG,
+ HOST_ACT_GEN_SET, 0,
+ &coalesce_cfg, true);
+ }
+
+ coalesce_cfg.num_of_rules = coalesce->n_rules;
+ for (i = 0; i < coalesce->n_rules; i++) {
+ ret = nxpwifi_fill_coalesce_rule_info(priv, &coalesce->rules[i],
+ &coalesce_cfg.rule[i]);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "Recheck the patterns provided for rule %d\n",
+ i + 1);
+ return ret;
+ }
+ }
+
+ return nxpwifi_send_cmd(priv, HOST_CMD_COALESCE_CFG,
+ HOST_ACT_GEN_SET, 0, &coalesce_cfg, true);
+}
+
+static int
+nxpwifi_cfg80211_uap_add_station(struct nxpwifi_private *priv, const u8 *mac,
+ struct station_parameters *params)
+{
+ struct nxpwifi_sta_info add_sta;
+ int ret;
+
+ memcpy(add_sta.peer_mac, mac, ETH_ALEN);
+ add_sta.params = params;
+
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_ADD_NEW_STATION,
+ HOST_ACT_ADD_STA, 0, (void *)&add_sta, true);
+
+ if (!ret) {
+ struct station_info *sinfo = NULL;
+
+ sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
+
+ if (sinfo) {
+ cfg80211_new_sta(priv->netdev, mac, sinfo, GFP_KERNEL);
+ kfree(sinfo);
+ }
+ }
+
+ return ret;
+}
+
+static int
+nxpwifi_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac, struct station_parameters *params)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ int ret = -EOPNOTSUPP;
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP)
+ ret = nxpwifi_cfg80211_uap_add_station(priv, mac, params);
+
+ return ret;
+}
+
+static int
+nxpwifi_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_csa_settings *params)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ int chsw_msec;
+
+ if (priv->adapter->scan_processing) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "radar detection: scan in process...\n");
+ return -EBUSY;
+ }
+
+ if (priv->wdev.cac_started)
+ return -EBUSY;
+
+ if (cfg80211_chandef_identical(¶ms->chandef,
+ &priv->dfs_chandef))
+ return -EINVAL;
+
+ if (params->block_tx) {
+ if (netif_carrier_ok(priv->netdev))
+ netif_carrier_off(priv->netdev);
+ nxpwifi_stop_net_dev_queue(priv->netdev, priv->adapter);
+ priv->uap_stop_tx = true;
+ }
+
+ if (nxpwifi_del_mgmt_ies(priv))
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Failed to delete mgmt IEs!\n");
+
+ if (nxpwifi_set_mgmt_ies(priv, ¶ms->beacon_csa)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "%s: setting mgmt ies failed\n", __func__);
+ return -EFAULT;
+ }
+
+ memcpy(&priv->dfs_chandef, ¶ms->chandef, sizeof(priv->dfs_chandef));
+ memcpy(&priv->beacon_after, ¶ms->beacon_after,
+ sizeof(priv->beacon_after));
+
+ chsw_msec = max(params->count * priv->bss_cfg.beacon_period, 100);
+ queue_delayed_work(priv->dfs_chan_sw_workqueue, &priv->dfs_chan_sw_work,
+ msecs_to_jiffies(chsw_msec));
+ return 0;
+}
+
+static int nxpwifi_cfg80211_get_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ unsigned int link_id,
+ struct cfg80211_chan_def *chandef)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev);
+ struct nxpwifi_bssdescriptor *curr_bss;
+ struct ieee80211_channel *chan;
+ enum nl80211_channel_type chan_type;
+ enum nl80211_band band;
+ int freq;
+ int ret = -ENODATA;
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP &&
+ cfg80211_chandef_valid(&priv->bss_chandef)) {
+ *chandef = priv->bss_chandef;
+ ret = 0;
+ } else if (priv->media_connected) {
+ curr_bss = &priv->curr_bss_params.bss_descriptor;
+ band = nxpwifi_band_to_radio_type(priv->curr_bss_params.band);
+ freq = ieee80211_channel_to_frequency(curr_bss->channel, band);
+ chan = ieee80211_get_channel(wiphy, freq);
+
+ if (priv->ht_param_present) {
+ chan_type = nxpwifi_get_chan_type(priv);
+ cfg80211_chandef_create(chandef, chan, chan_type);
+ } else {
+ cfg80211_chandef_create(chandef, chan,
+ NL80211_CHAN_NO_HT);
+ }
+ ret = 0;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_NL80211_TESTMODE
+
+enum nxpwifi_tm_attr {
+ __NXPWIFI_TM_ATTR_INVALID = 0,
+ NXPWIFI_TM_ATTR_CMD = 1,
+ NXPWIFI_TM_ATTR_DATA = 2,
+
+ /* keep last */
+ __NXPWIFI_TM_ATTR_AFTER_LAST,
+ NXPWIFI_TM_ATTR_MAX = __NXPWIFI_TM_ATTR_AFTER_LAST - 1,
+};
+
+static const struct nla_policy nxpwifi_tm_policy[NXPWIFI_TM_ATTR_MAX + 1] = {
+ [NXPWIFI_TM_ATTR_CMD] = { .type = NLA_U32 },
+ [NXPWIFI_TM_ATTR_DATA] = { .type = NLA_BINARY,
+ .len = NXPWIFI_SIZE_OF_CMD_BUFFER },
+};
+
+enum nxpwifi_tm_command {
+ NXPWIFI_TM_CMD_HOSTCMD = 0,
+};
+
+static int nxpwifi_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev,
+ void *data, int len)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(wdev->netdev);
+ struct nxpwifi_ds_misc_cmd *hostcmd;
+ struct nlattr *tb[NXPWIFI_TM_ATTR_MAX + 1];
+ struct sk_buff *skb;
+ int err;
+
+ if (!priv)
+ return -EINVAL;
+
+ err = nla_parse_deprecated(tb, NXPWIFI_TM_ATTR_MAX, data, len,
+ nxpwifi_tm_policy, NULL);
+ if (err)
+ return err;
+
+ if (!tb[NXPWIFI_TM_ATTR_CMD])
+ return -EINVAL;
+
+ switch (nla_get_u32(tb[NXPWIFI_TM_ATTR_CMD])) {
+ case NXPWIFI_TM_CMD_HOSTCMD:
+ if (!tb[NXPWIFI_TM_ATTR_DATA])
+ return -EINVAL;
+
+ hostcmd = kzalloc(sizeof(*hostcmd), GFP_KERNEL);
+ if (!hostcmd)
+ return -ENOMEM;
+
+ hostcmd->len = nla_len(tb[NXPWIFI_TM_ATTR_DATA]);
+ memcpy(hostcmd->cmd, nla_data(tb[NXPWIFI_TM_ATTR_DATA]),
+ hostcmd->len);
+
+ if (nxpwifi_send_cmd(priv, 0, 0, 0, hostcmd, true)) {
+ dev_err(priv->adapter->dev, "Failed to process hostcmd\n");
+ kfree(hostcmd);
+ return -EFAULT;
+ }
+
+ /* process hostcmd response*/
+ skb = cfg80211_testmode_alloc_reply_skb(wiphy, hostcmd->len);
+ if (!skb) {
+ kfree(hostcmd);
+ return -ENOMEM;
+ }
+ err = nla_put(skb, NXPWIFI_TM_ATTR_DATA,
+ hostcmd->len, hostcmd->cmd);
+ if (err) {
+ kfree(hostcmd);
+ kfree_skb(skb);
+ return -EMSGSIZE;
+ }
+
+ err = cfg80211_testmode_reply(skb);
+ kfree(hostcmd);
+ return err;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+#endif
+
+static int
+nxpwifi_cfg80211_start_radar_detection(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ struct nxpwifi_radar_params radar_params;
+
+ if (priv->adapter->scan_processing) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "radar detection: scan already in process...\n");
+ return -EBUSY;
+ }
+
+ if (!nxpwifi_is_11h_active(priv)) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "Enable 11h extensions in FW\n");
+ if (nxpwifi_11h_activate(priv, true)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Failed to activate 11h extensions!!");
+ return -1;
+ }
+ priv->state_11h.is_11h_active = true;
+ }
+
+ memset(&radar_params, 0, sizeof(struct nxpwifi_radar_params));
+ radar_params.chandef = chandef;
+ radar_params.cac_time_ms = cac_time_ms;
+
+ memcpy(&priv->dfs_chandef, chandef, sizeof(priv->dfs_chandef));
+
+ if (nxpwifi_send_cmd(priv, HOST_CMD_CHAN_REPORT_REQUEST,
+ HOST_ACT_GEN_SET, 0, &radar_params, true))
+ return -1;
+
+ queue_delayed_work(priv->dfs_cac_workqueue, &priv->dfs_cac_work,
+ msecs_to_jiffies(cac_time_ms));
+ return 0;
+}
+
+static int
+nxpwifi_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *mac,
+ struct station_parameters *params)
+{
+ return 0;
+}
+
+static int
+nxpwifi_cfg80211_authenticate(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_auth_request *req)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_private *tmp_priv;
+ int i;
+ struct sk_buff *skb;
+ u16 pkt_len, auth_alg;
+ int ret;
+ struct nxpwifi_ieee80211_mgmt *mgmt;
+ struct nxpwifi_txinfo *tx_info;
+ u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT;
+ u8 trans = 1, status_code = 0;
+ u8 *varptr;
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
+ nxpwifi_dbg(priv->adapter, ERROR, "Interface role is AP\n");
+ return -EFAULT;
+ }
+
+ if (priv->wdev.iftype != NL80211_IFTYPE_STATION) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Interface type is not correct (type %d)\n",
+ priv->wdev.iftype);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < NXPWIFI_MAX_BSS_NUM; i++) {
+ tmp_priv = adapter->priv[i];
+ if (tmp_priv == priv)
+ continue;
+ if (GET_BSS_ROLE(tmp_priv) == NXPWIFI_BSS_ROLE_UAP &&
+ netif_carrier_ok(tmp_priv->netdev) &&
+ cfg80211_chandef_valid(&tmp_priv->bss_chandef)) {
+ if (!ieee80211_channel_equal
+ (req->bss->channel, tmp_priv->bss_chandef.chan)) {
+ nxpwifi_dbg
+ (priv->adapter, MSG,
+ "STA and AP must operate on same channel\n");
+ return -EOPNOTSUPP;
+ }
+ }
+ }
+
+ if (priv->auth_alg != WLAN_AUTH_SAE &&
+ (priv->auth_flag & HOST_MLME_AUTH_PENDING)) {
+ nxpwifi_dbg(priv->adapter, ERROR, "Pending auth on going\n");
+ return -EBUSY;
+ }
+
+ if (!priv->host_mlme_reg) {
+ priv->host_mlme_reg = true;
+ priv->mgmt_frame_mask |= HOST_MLME_MGMT_MASK;
+ nxpwifi_send_cmd(priv, HOST_CMD_MGMT_FRAME_REG,
+ HOST_ACT_GEN_SET, 0,
+ &priv->mgmt_frame_mask, false);
+ }
+
+ switch (req->auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ auth_alg = WLAN_AUTH_OPEN;
+ break;
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ auth_alg = WLAN_AUTH_SHARED_KEY;
+ break;
+ case NL80211_AUTHTYPE_FT:
+ auth_alg = WLAN_AUTH_FT;
+ break;
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ auth_alg = WLAN_AUTH_LEAP;
+ break;
+ case NL80211_AUTHTYPE_SAE:
+ auth_alg = WLAN_AUTH_SAE;
+ break;
+ default:
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "unsupported auth type=%d\n", req->auth_type);
+ return -EOPNOTSUPP;
+ }
+
+ if (!priv->auth_flag) {
+ ret = nxpwifi_remain_on_chan_cfg(priv, HOST_ACT_GEN_SET,
+ req->bss->channel,
+ AUTH_TX_DEFAULT_WAIT_TIME);
+
+ if (!ret) {
+ priv->roc_cfg.cookie = get_random_u32() | 1;
+ priv->roc_cfg.chan = *req->bss->channel;
+ } else {
+ return -EFAULT;
+ }
+ }
+
+ priv->sec_info.authentication_mode = auth_alg;
+
+ nxpwifi_cancel_scan(adapter);
+
+ pkt_len = (u16)req->ie_len + req->auth_data_len +
+ NXPWIFI_MGMT_HEADER_LEN + NXPWIFI_AUTH_BODY_LEN;
+ if (req->auth_data_len >= 4)
+ pkt_len -= 4;
+
+ skb = dev_alloc_skb(NXPWIFI_MIN_DATA_HEADER_LEN +
+ NXPWIFI_MGMT_FRAME_HEADER_SIZE +
+ pkt_len + sizeof(pkt_len));
+ if (!skb) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "allocate skb failed for management frame\n");
+ return -ENOMEM;
+ }
+
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+ memset(tx_info, 0, sizeof(*tx_info));
+ tx_info->bss_num = priv->bss_num;
+ tx_info->bss_type = priv->bss_type;
+ tx_info->pkt_len = pkt_len;
+
+ skb_reserve(skb, NXPWIFI_MIN_DATA_HEADER_LEN +
+ NXPWIFI_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len));
+ memcpy(skb_push(skb, sizeof(pkt_len)), &pkt_len, sizeof(pkt_len));
+ memcpy(skb_push(skb, sizeof(tx_control)),
+ &tx_control, sizeof(tx_control));
+ memcpy(skb_push(skb, sizeof(pkt_type)), &pkt_type, sizeof(pkt_type));
+
+ mgmt = (struct nxpwifi_ieee80211_mgmt *)skb_put(skb, pkt_len);
+ memset(mgmt, 0, pkt_len);
+ mgmt->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
+ memcpy(mgmt->da, req->bss->bssid, ETH_ALEN);
+ memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, req->bss->bssid, ETH_ALEN);
+ eth_broadcast_addr(mgmt->addr4);
+
+ if (req->auth_data_len >= 4) {
+ if (req->auth_type == NL80211_AUTHTYPE_SAE) {
+ __le16 *pos = (__le16 *)req->auth_data;
+
+ trans = le16_to_cpu(pos[0]);
+ status_code = le16_to_cpu(pos[1]);
+ }
+ memcpy((u8 *)(&mgmt->auth.variable), req->auth_data + 4,
+ req->auth_data_len - 4);
+ varptr = (u8 *)&mgmt->auth.variable +
+ (req->auth_data_len - 4);
+ }
+
+ mgmt->auth.auth_alg = cpu_to_le16(auth_alg);
+ mgmt->auth.auth_transaction = cpu_to_le16(trans);
+ mgmt->auth.status_code = cpu_to_le16(status_code);
+
+ if (req->ie && req->ie_len) {
+ if (!varptr)
+ varptr = (u8 *)&mgmt->auth.variable;
+ memcpy((u8 *)varptr, req->ie, req->ie_len);
+ }
+
+ priv->auth_flag = HOST_MLME_AUTH_PENDING;
+ priv->auth_alg = auth_alg;
+
+ skb->priority = WMM_HIGHEST_PRIORITY;
+ __net_timestamp(skb);
+
+ nxpwifi_dbg(priv->adapter, MSG,
+ "auth: send authentication to %pM\n", req->bss->bssid);
+
+ nxpwifi_queue_tx_pkt(priv, skb);
+
+ return 0;
+}
+
+static int
+nxpwifi_cfg80211_associate(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_assoc_request *req)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int ret;
+ struct cfg80211_ssid req_ssid;
+ const u8 *ssid_ie;
+
+ if (GET_BSS_ROLE(priv) != NXPWIFI_BSS_ROLE_STA) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: reject infra assoc request in non-STA role\n",
+ dev->name);
+ return -EINVAL;
+ }
+
+ if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags) ||
+ test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: Ignore association.\t"
+ "Card removed or FW in bad state\n",
+ dev->name);
+ return -EFAULT;
+ }
+
+ if (priv->auth_alg == WLAN_AUTH_SAE)
+ priv->auth_flag = HOST_MLME_AUTH_DONE;
+
+ if (priv->auth_flag && !(priv->auth_flag & HOST_MLME_AUTH_DONE))
+ return -EBUSY;
+
+ if (priv->roc_cfg.cookie) {
+ ret = nxpwifi_remain_on_chan_cfg(priv, HOST_ACT_GEN_REMOVE,
+ &priv->roc_cfg.chan, 0);
+ if (!ret)
+ memset(&priv->roc_cfg, 0,
+ sizeof(struct nxpwifi_roc_cfg));
+ else
+ return -EFAULT;
+ }
+
+ if (!nxpwifi_stop_bg_scan(priv))
+ cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0);
+
+ memset(&req_ssid, 0, sizeof(struct cfg80211_ssid));
+ rcu_read_lock();
+ ssid_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
+
+ if (!ssid_ie)
+ goto ssid_err;
+
+ req_ssid.ssid_len = ssid_ie[1];
+ if (req_ssid.ssid_len > IEEE80211_MAX_SSID_LEN) {
+ nxpwifi_dbg(adapter, ERROR, "invalid SSID - aborting\n");
+ goto ssid_err;
+ }
+
+ memcpy(req_ssid.ssid, ssid_ie + 2, req_ssid.ssid_len);
+ if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) {
+ nxpwifi_dbg(adapter, ERROR, "invalid SSID - aborting\n");
+ goto ssid_err;
+ }
+ rcu_read_unlock();
+
+ /* As this is new association, clear locally stored
+ * keys and security related flags
+ */
+ priv->sec_info.wpa_enabled = false;
+ priv->sec_info.wpa2_enabled = false;
+ priv->wep_key_curr_index = 0;
+ priv->sec_info.encryption_mode = 0;
+ priv->sec_info.is_authtype_auto = 0;
+ ret = nxpwifi_set_encode(priv, NULL, NULL, 0, 0, NULL, 1);
+
+ if (req->crypto.n_ciphers_pairwise)
+ priv->sec_info.encryption_mode =
+ req->crypto.ciphers_pairwise[0];
+
+ if (req->crypto.cipher_group)
+ priv->sec_info.encryption_mode = req->crypto.cipher_group;
+
+ if (req->ie)
+ ret = nxpwifi_set_gen_ie(priv, req->ie, req->ie_len);
+
+ memcpy(priv->cfg_bssid, req->bss->bssid, ETH_ALEN);
+
+ nxpwifi_dbg(adapter, MSG,
+ "assoc: send association to %pM\n", req->bss->bssid);
+
+ cfg80211_ref_bss(adapter->wiphy, req->bss);
+
+ ret = nxpwifi_bss_start(priv, req->bss, &req_ssid);
+
+ if (ret) {
+ priv->auth_flag = 0;
+ priv->auth_alg = WLAN_AUTH_NONE;
+ eth_zero_addr(priv->cfg_bssid);
+ }
+
+ if (priv->assoc_rsp_size) {
+ priv->req_bss = req->bss;
+ adapter->assoc_resp_received = true;
+ queue_work(adapter->host_mlme_workqueue,
+ &adapter->host_mlme_work);
+ }
+
+ cfg80211_put_bss(priv->adapter->wiphy, req->bss);
+
+ return 0;
+
+ssid_err:
+
+ rcu_read_unlock();
+ return -EFAULT;
+}
+
+static int
+nxpwifi_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
+ u16 reason_code)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+
+ if (!nxpwifi_stop_bg_scan(priv))
+ cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0);
+
+ if (nxpwifi_deauthenticate(priv, NULL))
+ return -EFAULT;
+
+ eth_zero_addr(priv->cfg_bssid);
+ priv->hs2_enabled = false;
+
+ return 0;
+}
+
+static int
+nxpwifi_cfg80211_deauthenticate(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_deauth_request *req)
+{
+ return nxpwifi_cfg80211_disconnect(wiphy, dev, req->reason_code);
+}
+
+static int
+nxpwifi_cfg80211_disassociate(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_disassoc_request *req)
+{
+ return nxpwifi_cfg80211_disconnect(wiphy, dev, req->reason_code);
+}
+
+static int
+nxpwifi_cfg80211_probe_client(struct wiphy *wiphy,
+ struct net_device *dev, const u8 *peer,
+ u64 *cookie)
+{
+ return -EOPNOTSUPP;
+}
+
+/* station cfg80211 operations */
+static struct cfg80211_ops nxpwifi_cfg80211_ops = {
+ .add_virtual_intf = nxpwifi_add_virtual_intf,
+ .del_virtual_intf = nxpwifi_del_virtual_intf,
+ .change_virtual_intf = nxpwifi_cfg80211_change_virtual_intf,
+ .scan = nxpwifi_cfg80211_scan,
+ .auth = nxpwifi_cfg80211_authenticate,
+ .assoc = nxpwifi_cfg80211_associate,
+ .deauth = nxpwifi_cfg80211_deauthenticate,
+ .disassoc = nxpwifi_cfg80211_disassociate,
+ .probe_client = nxpwifi_cfg80211_probe_client,
+ .get_station = nxpwifi_cfg80211_get_station,
+ .dump_station = nxpwifi_cfg80211_dump_station,
+ .dump_survey = nxpwifi_cfg80211_dump_survey,
+ .set_wiphy_params = nxpwifi_cfg80211_set_wiphy_params,
+ .add_key = nxpwifi_cfg80211_add_key,
+ .del_key = nxpwifi_cfg80211_del_key,
+ .set_default_mgmt_key = nxpwifi_cfg80211_set_default_mgmt_key,
+ .mgmt_tx = nxpwifi_cfg80211_mgmt_tx,
+ .update_mgmt_frame_registrations =
+ nxpwifi_cfg80211_update_mgmt_frame_registrations,
+ .remain_on_channel = nxpwifi_cfg80211_remain_on_channel,
+ .cancel_remain_on_channel = nxpwifi_cfg80211_cancel_remain_on_channel,
+ .set_default_key = nxpwifi_cfg80211_set_default_key,
+ .set_power_mgmt = nxpwifi_cfg80211_set_power_mgmt,
+ .set_tx_power = nxpwifi_cfg80211_set_tx_power,
+ .get_tx_power = nxpwifi_cfg80211_get_tx_power,
+ .set_bitrate_mask = nxpwifi_cfg80211_set_bitrate_mask,
+ .start_ap = nxpwifi_cfg80211_start_ap,
+ .stop_ap = nxpwifi_cfg80211_stop_ap,
+ .change_beacon = nxpwifi_cfg80211_change_beacon,
+ .set_cqm_rssi_config = nxpwifi_cfg80211_set_cqm_rssi_config,
+ .set_antenna = nxpwifi_cfg80211_set_antenna,
+ .get_antenna = nxpwifi_cfg80211_get_antenna,
+ .del_station = nxpwifi_cfg80211_del_station,
+ .sched_scan_start = nxpwifi_cfg80211_sched_scan_start,
+ .sched_scan_stop = nxpwifi_cfg80211_sched_scan_stop,
+ .change_station = nxpwifi_cfg80211_change_station,
+#ifdef CONFIG_PM
+ .suspend = nxpwifi_cfg80211_suspend,
+ .resume = nxpwifi_cfg80211_resume,
+ .set_wakeup = nxpwifi_cfg80211_set_wakeup,
+#endif
+ .set_coalesce = nxpwifi_cfg80211_set_coalesce,
+ .add_station = nxpwifi_cfg80211_add_station,
+ CFG80211_TESTMODE_CMD(nxpwifi_tm_cmd)
+ .get_channel = nxpwifi_cfg80211_get_channel,
+ .start_radar_detection = nxpwifi_cfg80211_start_radar_detection,
+ .channel_switch = nxpwifi_cfg80211_channel_switch,
+};
+
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support nxpwifi_wowlan_support = {
+ .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT |
+ WIPHY_WOWLAN_NET_DETECT | WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+ WIPHY_WOWLAN_GTK_REKEY_FAILURE,
+ .n_patterns = NXPWIFI_MEF_MAX_FILTERS,
+ .pattern_min_len = 1,
+ .pattern_max_len = NXPWIFI_MAX_PATTERN_LEN,
+ .max_pkt_offset = NXPWIFI_MAX_OFFSET_LEN,
+ .max_nd_match_sets = NXPWIFI_MAX_ND_MATCH_SETS,
+};
+
+static const struct wiphy_wowlan_support nxpwifi_wowlan_support_no_gtk = {
+ .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT |
+ WIPHY_WOWLAN_NET_DETECT,
+ .n_patterns = NXPWIFI_MEF_MAX_FILTERS,
+ .pattern_min_len = 1,
+ .pattern_max_len = NXPWIFI_MAX_PATTERN_LEN,
+ .max_pkt_offset = NXPWIFI_MAX_OFFSET_LEN,
+ .max_nd_match_sets = NXPWIFI_MAX_ND_MATCH_SETS,
+};
+#endif
+
+static const struct wiphy_coalesce_support nxpwifi_coalesce_support = {
+ .n_rules = NXPWIFI_COALESCE_MAX_RULES,
+ .max_delay = NXPWIFI_MAX_COALESCING_DELAY,
+ .n_patterns = NXPWIFI_COALESCE_MAX_FILTERS,
+ .pattern_min_len = 1,
+ .pattern_max_len = NXPWIFI_MAX_PATTERN_LEN,
+ .max_pkt_offset = NXPWIFI_MAX_OFFSET_LEN,
+};
+
+int nxpwifi_init_channel_scan_gap(struct nxpwifi_adapter *adapter)
+{
+ u32 n_channels_bg, n_channels_a = 0;
+
+ n_channels_bg = nxpwifi_band_2ghz.n_channels;
+
+ if (adapter->config_bands & BAND_A)
+ n_channels_a = nxpwifi_band_5ghz.n_channels;
+
+ /* allocate twice the number total channels, since the driver issues an
+ * additional active scan request for hidden SSIDs on passive channels.
+ */
+ adapter->num_in_chan_stats = 2 * (n_channels_bg + n_channels_a);
+ adapter->chan_stats = vmalloc(array_size(sizeof(*adapter->chan_stats),
+ adapter->num_in_chan_stats));
+
+ if (!adapter->chan_stats)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/* This function registers the device with CFG802.11 subsystem.
+ *
+ * The function creates the wireless device/wiphy, populates it with
+ * default parameters and handler function pointers, and finally
+ * registers the device.
+ */
+
+int nxpwifi_register_cfg80211(struct nxpwifi_adapter *adapter)
+{
+ int ret;
+ void *wdev_priv;
+ struct wiphy *wiphy;
+ struct nxpwifi_private *priv = adapter->priv[NXPWIFI_BSS_TYPE_STA];
+ u8 *country_code;
+ u32 thr, retry;
+
+ /* create a new wiphy for use with cfg80211 */
+ wiphy = wiphy_new(&nxpwifi_cfg80211_ops,
+ sizeof(struct nxpwifi_adapter *));
+ if (!wiphy) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: creating new wiphy\n", __func__);
+ return -ENOMEM;
+ }
+
+ wiphy->max_scan_ssids = NXPWIFI_MAX_SSID_LIST_LENGTH;
+ wiphy->max_scan_ie_len = NXPWIFI_MAX_VSIE_LEN;
+
+ wiphy->mgmt_stypes = nxpwifi_mgmt_stypes;
+ wiphy->max_remain_on_channel_duration = 5000;
+ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_AP);
+
+ wiphy->bands[NL80211_BAND_2GHZ] = &nxpwifi_band_2ghz;
+ if (adapter->config_bands & BAND_A)
+ wiphy->bands[NL80211_BAND_5GHZ] = &nxpwifi_band_5ghz;
+ else
+ wiphy->bands[NL80211_BAND_5GHZ] = NULL;
+
+ if (adapter->is_hw_11ac_capable)
+ wiphy->iface_combinations = &nxpwifi_iface_comb_ap_sta_vht;
+ else
+ wiphy->iface_combinations = &nxpwifi_iface_comb_ap_sta;
+ wiphy->n_iface_combinations = 1;
+
+ wiphy->max_ap_assoc_sta = adapter->max_sta_conn;
+
+ /* Initialize cipher suits */
+ wiphy->cipher_suites = nxpwifi_cipher_suites;
+ wiphy->n_cipher_suites = ARRAY_SIZE(nxpwifi_cipher_suites);
+
+ if (adapter->regd) {
+ wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
+ REGULATORY_DISABLE_BEACON_HINTS |
+ REGULATORY_COUNTRY_IE_IGNORE;
+ wiphy_apply_custom_regulatory(wiphy, adapter->regd);
+ }
+
+ ether_addr_copy(wiphy->perm_addr, adapter->perm_addr);
+ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
+ WIPHY_FLAG_AP_UAPSD |
+ WIPHY_FLAG_REPORTS_OBSS |
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+ WIPHY_FLAG_HAS_CHANNEL_SWITCH |
+ WIPHY_FLAG_NETNS_OK |
+ WIPHY_FLAG_PS_ON_BY_DEFAULT;
+ wiphy->max_num_csa_counters = NXPWIFI_MAX_CSA_COUNTERS;
+
+#ifdef CONFIG_PM
+ if (ISSUPP_FIRMWARE_SUPPLICANT(priv->adapter->fw_cap_info))
+ wiphy->wowlan = &nxpwifi_wowlan_support;
+ else
+ wiphy->wowlan = &nxpwifi_wowlan_support_no_gtk;
+#endif
+
+ wiphy->coalesce = &nxpwifi_coalesce_support;
+
+ wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2;
+
+ wiphy->max_sched_scan_reqs = 1;
+ wiphy->max_sched_scan_ssids = NXPWIFI_MAX_SSID_LIST_LENGTH;
+ wiphy->max_sched_scan_ie_len = NXPWIFI_MAX_VSIE_LEN;
+ wiphy->max_match_sets = NXPWIFI_MAX_SSID_LIST_LENGTH;
+
+ wiphy->available_antennas_tx = BIT(adapter->number_of_antenna) - 1;
+ wiphy->available_antennas_rx = BIT(adapter->number_of_antenna) - 1;
+
+ wiphy->features |= NL80211_FEATURE_SAE |
+ NL80211_FEATURE_INACTIVITY_TIMER |
+ NL80211_FEATURE_LOW_PRIORITY_SCAN |
+ NL80211_FEATURE_NEED_OBSS_SCAN;
+
+ if (ISSUPP_RANDOM_MAC(adapter->fw_cap_info))
+ wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR |
+ NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
+ NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
+
+ if (adapter->fw_api_ver == NXPWIFI_FW_V15)
+ wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
+
+ /* Reserve space for nxpwifi specific private data for BSS */
+ wiphy->bss_priv_size = sizeof(struct nxpwifi_bss_priv);
+
+ wiphy->reg_notifier = nxpwifi_reg_notifier;
+
+ /* Set struct nxpwifi_adapter pointer in wiphy_priv */
+ wdev_priv = wiphy_priv(wiphy);
+ *(unsigned long *)wdev_priv = (unsigned long)adapter;
+
+ set_wiphy_dev(wiphy, priv->adapter->dev);
+
+ ret = wiphy_register(wiphy);
+ if (ret < 0) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: wiphy_register failed: %d\n", __func__, ret);
+ wiphy_free(wiphy);
+ return ret;
+ }
+
+ if (!adapter->regd) {
+ if (adapter->region_code == 0x00) {
+ nxpwifi_dbg(adapter, WARN,
+ "Ignore world regulatory domain\n");
+ } else {
+ wiphy->regulatory_flags |=
+ REGULATORY_DISABLE_BEACON_HINTS |
+ REGULATORY_COUNTRY_IE_IGNORE;
+ country_code =
+ nxpwifi_11d_code_2_region(adapter->region_code);
+ if (country_code &&
+ regulatory_hint(wiphy, country_code))
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "regulatory_hint() failed\n");
+ }
+ }
+
+ nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB,
+ HOST_ACT_GEN_GET, FRAG_THRESH_I, &thr, true);
+ wiphy->frag_threshold = thr;
+ nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB,
+ HOST_ACT_GEN_GET, RTS_THRESH_I, &thr, true);
+ wiphy->rts_threshold = thr;
+ nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB,
+ HOST_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry, true);
+ wiphy->retry_short = (u8)retry;
+ nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB,
+ HOST_ACT_GEN_GET, LONG_RETRY_LIM_I, &retry, true);
+ wiphy->retry_long = (u8)retry;
+
+ adapter->wiphy = wiphy;
+ return ret;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 11/43] wifi: nxpwifi: add cfg80211.h
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (9 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 10/43] wifi: nxpwifi: add cfg80211.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 12/43] wifi: nxpwifi: add cfp.c David Lin
` (33 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/cfg80211.h | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/cfg80211.h
diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.h b/drivers/net/wireless/nxp/nxpwifi/cfg80211.h
new file mode 100644
index 000000000000..086ec9ca4cc9
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NXP Wireless LAN device driver: CFG80211
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#ifndef __NXPWIFI_CFG80211__
+#define __NXPWIFI_CFG80211__
+
+#include "main.h"
+
+int nxpwifi_register_cfg80211(struct nxpwifi_adapter *adapter);
+
+int nxpwifi_cfg80211_change_beacon_data(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_beacon_data *data);
+
+#endif
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 12/43] wifi: nxpwifi: add cfp.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (10 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 11/43] wifi: nxpwifi: add cfg80211.h David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 13/43] wifi: nxpwifi: add cmdevt.c David Lin
` (32 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/cfp.c | 484 +++++++++++++++++++++++++
1 file changed, 484 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/cfp.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/cfp.c b/drivers/net/wireless/nxp/nxpwifi/cfp.c
new file mode 100644
index 000000000000..4fcd2add9b66
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/cfp.c
@@ -0,0 +1,484 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: Channel, Frequency and Power
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cfg80211.h"
+
+/* 100mW */
+#define NXPWIFI_TX_PWR_DEFAULT 20
+/* 100mW */
+#define NXPWIFI_TX_PWR_US_DEFAULT 20
+/* 50mW */
+#define NXPWIFI_TX_PWR_JP_DEFAULT 16
+/* 100mW */
+#define NXPWIFI_TX_PWR_FR_100MW 20
+/* 10mW */
+#define NXPWIFI_TX_PWR_FR_10MW 10
+/* 100mW */
+#define NXPWIFI_TX_PWR_EMEA_DEFAULT 20
+
+static u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
+ 0xb0, 0x48, 0x60, 0x6c, 0 };
+static u16 nxpwifi_data_rates[NXPWIFI_SUPPORTED_RATES_EXT] = { 0x02, 0x04,
+ 0x0B, 0x16, 0x00, 0x0C, 0x12, 0x18,
+ 0x24, 0x30, 0x48, 0x60, 0x6C, 0x90,
+ 0x0D, 0x1A, 0x27, 0x34, 0x4E, 0x68,
+ 0x75, 0x82, 0x0C, 0x1B, 0x36, 0x51,
+ 0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x00 };
+
+static u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 };
+
+static u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
+ 0x30, 0x48, 0x60, 0x6c, 0 };
+
+static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c,
+ 0x12, 0x16, 0x18, 0x24, 0x30, 0x48,
+ 0x60, 0x6c, 0 };
+
+u16 region_code_index[NXPWIFI_MAX_REGION_CODE] = { 0x00, 0x10, 0x20, 0x30,
+ 0x31, 0x32, 0x40, 0x41, 0x50 };
+
+static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 };
+
+/* For every mcs_rate line, the first 8 bytes are for stream 1x1,
+ * and all 16 bytes are for stream 2x2.
+ */
+static const u16 mcs_rate[4][16] = {
+ /* LGI 40M */
+ { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e,
+ 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c },
+
+ /* SGI 40M */
+ { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c,
+ 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 },
+
+ /* LGI 20M */
+ { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82,
+ 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 },
+
+ /* SGI 20M */
+ { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90,
+ 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 }
+};
+
+/* AC rates */
+static const u16 ac_mcs_rate_nss1[8][10] = {
+ /* LG 160M */
+ { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
+ 0x492, 0x57C, 0x618 },
+
+ /* SG 160M */
+ { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
+ 0x514, 0x618, 0x6C6 },
+
+ /* LG 80M */
+ { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F,
+ 0x249, 0x2BE, 0x30C },
+
+ /* SG 80M */
+ { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249,
+ 0x28A, 0x30C, 0x363 },
+
+ /* LG 40M */
+ { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3,
+ 0x10E, 0x144, 0x168 },
+
+ /* SG 40M */
+ { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E,
+ 0x12C, 0x168, 0x190 },
+
+ /* LG 20M */
+ { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 },
+
+ /* SG 20M */
+ { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 },
+};
+
+/* NSS2 note: the value in the table is 2 multiplier of the actual rate */
+static const u16 ac_mcs_rate_nss2[8][10] = {
+ /* LG 160M */
+ { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A,
+ 0x924, 0xAF8, 0xC30 },
+
+ /* SG 160M */
+ { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924,
+ 0xA28, 0xC30, 0xD8B },
+
+ /* LG 80M */
+ { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
+ 0x492, 0x57C, 0x618 },
+
+ /* SG 80M */
+ { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
+ 0x514, 0x618, 0x6C6 },
+
+ /* LG 40M */
+ { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6,
+ 0x21C, 0x288, 0x2D0 },
+
+ /* SG 40M */
+ { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C,
+ 0x258, 0x2D0, 0x320 },
+
+ /* LG 20M */
+ { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104,
+ 0x138, 0x00 },
+
+ /* SG 20M */
+ { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121,
+ 0x15B, 0x00 },
+};
+
+struct region_code_mapping {
+ u8 code;
+ u8 region[IEEE80211_COUNTRY_STRING_LEN];
+};
+
+static struct region_code_mapping region_code_mapping_t[] = {
+ { 0x10, "US " }, /* US FCC */
+ { 0x20, "CA " }, /* IC Canada */
+ { 0x30, "FR " }, /* France */
+ { 0x31, "ES " }, /* Spain */
+ { 0x32, "FR " }, /* France */
+ { 0x40, "JP " }, /* Japan */
+ { 0x41, "JP " }, /* Japan */
+ { 0x50, "CN " }, /* China */
+};
+
+/* This function converts integer code to region string */
+u8 *nxpwifi_11d_code_2_region(u8 code)
+{
+ u8 i;
+
+ /* Look for code in mapping table */
+ for (i = 0; i < ARRAY_SIZE(region_code_mapping_t); i++)
+ if (region_code_mapping_t[i].code == code)
+ return region_code_mapping_t[i].region;
+
+ return NULL;
+}
+
+/* This function maps an index in supported rates table into
+ * the corresponding data rate.
+ */
+u32 nxpwifi_index_to_acs_data_rate(struct nxpwifi_private *priv,
+ u8 index, u8 ht_info)
+{
+ u32 rate = 0;
+ u8 mcs_index = 0;
+ u8 bw = 0;
+ u8 gi = 0;
+
+ if ((ht_info & 0x3) == NXPWIFI_RATE_FORMAT_VHT) {
+ mcs_index = min(index & 0xF, 9);
+
+ /* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */
+ bw = (ht_info & 0xC) >> 2;
+
+ /* LGI: gi =0, SGI: gi = 1 */
+ gi = (ht_info & 0x10) >> 4;
+
+ if ((index >> 4) == 1) /* NSS = 2 */
+ rate = ac_mcs_rate_nss2[2 * (3 - bw) + gi][mcs_index];
+ else /* NSS = 1 */
+ rate = ac_mcs_rate_nss1[2 * (3 - bw) + gi][mcs_index];
+ } else if ((ht_info & 0x3) == NXPWIFI_RATE_FORMAT_HT) {
+ /* 20M: bw=0, 40M: bw=1 */
+ bw = (ht_info & 0xC) >> 2;
+
+ /* LGI: gi =0, SGI: gi = 1 */
+ gi = (ht_info & 0x10) >> 4;
+
+ if (index == NXPWIFI_RATE_BITMAP_MCS0) {
+ if (gi == 1)
+ rate = 0x0D; /* MCS 32 SGI rate */
+ else
+ rate = 0x0C; /* MCS 32 LGI rate */
+ } else if (index < 16) {
+ if (bw == 1 || bw == 0)
+ rate = mcs_rate[2 * (1 - bw) + gi][index];
+ else
+ rate = nxpwifi_data_rates[0];
+ } else {
+ rate = nxpwifi_data_rates[0];
+ }
+ } else {
+ /* 11n non-HT rates */
+ if (index >= NXPWIFI_SUPPORTED_RATES_EXT)
+ index = 0;
+ rate = nxpwifi_data_rates[index];
+ }
+
+ return rate;
+}
+
+/* This function maps an index in supported rates table into
+ * the corresponding data rate.
+ */
+u32 nxpwifi_index_to_data_rate(struct nxpwifi_private *priv,
+ u8 index, u8 ht_info)
+{
+ u32 mcs_num_supp =
+ (priv->adapter->user_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8;
+ u32 rate;
+
+ if (priv->adapter->is_hw_11ac_capable)
+ return nxpwifi_index_to_acs_data_rate(priv, index, ht_info);
+
+ if (ht_info & BIT(0)) {
+ if (index == NXPWIFI_RATE_BITMAP_MCS0) {
+ if (ht_info & BIT(2))
+ rate = 0x0D; /* MCS 32 SGI rate */
+ else
+ rate = 0x0C; /* MCS 32 LGI rate */
+ } else if (index < mcs_num_supp) {
+ if (ht_info & BIT(1)) {
+ if (ht_info & BIT(2))
+ /* SGI, 40M */
+ rate = mcs_rate[1][index];
+ else
+ /* LGI, 40M */
+ rate = mcs_rate[0][index];
+ } else {
+ if (ht_info & BIT(2))
+ /* SGI, 20M */
+ rate = mcs_rate[3][index];
+ else
+ /* LGI, 20M */
+ rate = mcs_rate[2][index];
+ }
+ } else {
+ rate = nxpwifi_data_rates[0];
+ }
+ } else {
+ if (index >= NXPWIFI_SUPPORTED_RATES_EXT)
+ index = 0;
+ rate = nxpwifi_data_rates[index];
+ }
+ return rate;
+}
+
+/* This function returns the current active data rates.
+ *
+ * The result may vary depending upon connection status.
+ */
+u32 nxpwifi_get_active_data_rates(struct nxpwifi_private *priv, u8 *rates)
+{
+ if (!priv->media_connected)
+ return nxpwifi_get_supported_rates(priv, rates);
+ else
+ return nxpwifi_copy_rates(rates, 0,
+ priv->curr_bss_params.data_rates,
+ priv->curr_bss_params.num_of_rates);
+}
+
+/* This function locates the Channel-Frequency-Power triplet based upon
+ * band and channel/frequency parameters.
+ */
+struct nxpwifi_chan_freq_power *
+nxpwifi_get_cfp(struct nxpwifi_private *priv, u8 band, u16 channel, u32 freq)
+{
+ struct nxpwifi_chan_freq_power *cfp = NULL;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *ch = NULL;
+ int i;
+
+ if (!channel && !freq)
+ return cfp;
+
+ if (nxpwifi_band_to_radio_type(band) == HOST_SCAN_RADIO_TYPE_BG)
+ sband = priv->wdev.wiphy->bands[NL80211_BAND_2GHZ];
+ else
+ sband = priv->wdev.wiphy->bands[NL80211_BAND_5GHZ];
+
+ if (!sband) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "%s: cannot find cfp by band %d\n",
+ __func__, band);
+ return cfp;
+ }
+
+ for (i = 0; i < sband->n_channels; i++) {
+ ch = &sband->channels[i];
+
+ if (ch->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ if (freq) {
+ if (ch->center_freq == freq)
+ break;
+ } else {
+ /* find by valid channel*/
+ if (ch->hw_value == channel ||
+ channel == FIRST_VALID_CHANNEL)
+ break;
+ }
+ }
+ if (i == sband->n_channels) {
+ nxpwifi_dbg(priv->adapter, WARN,
+ "%s: cannot find cfp by band %d\t"
+ "& channel=%d freq=%d\n",
+ __func__, band, channel, freq);
+ } else {
+ if (!ch)
+ return cfp;
+
+ priv->cfp.channel = ch->hw_value;
+ priv->cfp.freq = ch->center_freq;
+ priv->cfp.max_tx_power = ch->max_power;
+ cfp = &priv->cfp;
+ }
+
+ return cfp;
+}
+
+/* This function checks if the data rate is set to auto.
+ */
+u8
+nxpwifi_is_rate_auto(struct nxpwifi_private *priv)
+{
+ u32 i;
+ int rate_num = 0;
+
+ for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates); i++)
+ if (priv->bitmap_rates[i])
+ rate_num++;
+
+ if (rate_num > 1)
+ return true;
+ else
+ return false;
+}
+
+/* This function gets the supported data rates from bitmask inside
+ * cfg80211_scan_request.
+ */
+u32 nxpwifi_get_rates_from_cfg80211(struct nxpwifi_private *priv,
+ u8 *rates, u8 radio_type)
+{
+ struct wiphy *wiphy = priv->adapter->wiphy;
+ struct cfg80211_scan_request *request = priv->scan_request;
+ u32 num_rates, rate_mask;
+ struct ieee80211_supported_band *sband;
+ int i;
+
+ if (radio_type) {
+ sband = wiphy->bands[NL80211_BAND_5GHZ];
+ if (WARN_ON_ONCE(!sband))
+ return 0;
+ rate_mask = request->rates[NL80211_BAND_5GHZ];
+ } else {
+ sband = wiphy->bands[NL80211_BAND_2GHZ];
+ if (WARN_ON_ONCE(!sband))
+ return 0;
+ rate_mask = request->rates[NL80211_BAND_2GHZ];
+ }
+
+ num_rates = 0;
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if ((BIT(i) & rate_mask) == 0)
+ continue; /* skip rate */
+ rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5);
+ }
+
+ return num_rates;
+}
+
+/* This function gets the supported data rates. The function works in
+ * both Ad-Hoc and infra mode by printing the band and returning the
+ * data rates.
+ */
+u32 nxpwifi_get_supported_rates(struct nxpwifi_private *priv, u8 *rates)
+{
+ u32 k = 0;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (priv->bss_mode == NL80211_IFTYPE_STATION) {
+ switch (adapter->config_bands) {
+ case BAND_B:
+ nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t"
+ "supported_rates_b\n",
+ adapter->config_bands);
+ k = nxpwifi_copy_rates(rates, k, supported_rates_b,
+ sizeof(supported_rates_b));
+ break;
+ case BAND_G:
+ case BAND_G | BAND_GN:
+ nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t"
+ "supported_rates_g\n",
+ adapter->config_bands);
+ k = nxpwifi_copy_rates(rates, k, supported_rates_g,
+ sizeof(supported_rates_g));
+ break;
+ case BAND_B | BAND_G:
+ case BAND_A | BAND_B | BAND_G:
+ case BAND_A | BAND_B:
+ case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN:
+ case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC:
+ case BAND_B | BAND_G | BAND_GN:
+ nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t"
+ "supported_rates_bg\n",
+ adapter->config_bands);
+ k = nxpwifi_copy_rates(rates, k, supported_rates_bg,
+ sizeof(supported_rates_bg));
+ break;
+ case BAND_A:
+ case BAND_A | BAND_G:
+ nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t"
+ "supported_rates_a\n",
+ adapter->config_bands);
+ k = nxpwifi_copy_rates(rates, k, supported_rates_a,
+ sizeof(supported_rates_a));
+ break;
+ case BAND_AN:
+ case BAND_A | BAND_AN:
+ case BAND_A | BAND_AN | BAND_AAC:
+ case BAND_A | BAND_G | BAND_AN | BAND_GN:
+ case BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC:
+ nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t"
+ "supported_rates_a\n",
+ adapter->config_bands);
+ k = nxpwifi_copy_rates(rates, k, supported_rates_a,
+ sizeof(supported_rates_a));
+ break;
+ case BAND_GN:
+ nxpwifi_dbg(adapter, INFO, "info: infra band=%d\t"
+ "supported_rates_n\n",
+ adapter->config_bands);
+ k = nxpwifi_copy_rates(rates, k, supported_rates_n,
+ sizeof(supported_rates_n));
+ break;
+ }
+ }
+
+ return k;
+}
+
+u8 nxpwifi_adjust_data_rate(struct nxpwifi_private *priv,
+ u8 rx_rate, u8 rate_info)
+{
+ u8 rate_index = 0;
+
+ /* HT40 */
+ if ((rate_info & BIT(0)) && (rate_info & BIT(1)))
+ rate_index = NXPWIFI_RATE_INDEX_MCS0 +
+ NXPWIFI_BW20_MCS_NUM + rx_rate;
+ else if (rate_info & BIT(0)) /* HT20 */
+ rate_index = NXPWIFI_RATE_INDEX_MCS0 + rx_rate;
+ else
+ rate_index = (rx_rate > NXPWIFI_RATE_INDEX_OFDM0) ?
+ rx_rate - 1 : rx_rate;
+
+ if (rate_index >= NXPWIFI_MAX_AC_RX_RATES)
+ rate_index = NXPWIFI_MAX_AC_RX_RATES - 1;
+
+ return rate_index;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 13/43] wifi: nxpwifi: add cmdevt.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (11 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 12/43] wifi: nxpwifi: add cfp.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 14/43] wifi: nxpwifi: add cmdevt.h David Lin
` (31 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/cmdevt.c | 1285 +++++++++++++++++++++
1 file changed, 1285 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/cmdevt.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/cmdevt.c b/drivers/net/wireless/nxp/nxpwifi/cmdevt.c
new file mode 100644
index 000000000000..fd05a1ecd127
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/cmdevt.c
@@ -0,0 +1,1285 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: commands and events
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include <asm/unaligned.h>
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cmdevt.h"
+#include "wmm.h"
+#include "11n.h"
+
+static void nxpwifi_cancel_pending_ioctl(struct nxpwifi_adapter *adapter);
+
+/* This function initializes a command node.
+ *
+ * The actual allocation of the node is not done by this function. It only
+ * initiates a node by filling it with default parameters. Similarly,
+ * allocation of the different buffers used (IOCTL buffer, data buffer) are
+ * not done by this function either.
+ */
+static void
+nxpwifi_init_cmd_node(struct nxpwifi_private *priv,
+ struct cmd_ctrl_node *cmd_node,
+ u32 cmd_no, void *data_buf, bool sync)
+{
+ cmd_node->priv = priv;
+ cmd_node->cmd_no = cmd_no;
+
+ if (sync) {
+ cmd_node->wait_q_enabled = true;
+ cmd_node->cmd_wait_q_woken = false;
+ cmd_node->condition = &cmd_node->cmd_wait_q_woken;
+ }
+ cmd_node->data_buf = data_buf;
+ cmd_node->cmd_skb = cmd_node->skb;
+ cmd_node->cmd_resp = NULL;
+}
+
+/* This function returns a command node from the free queue depending upon
+ * availability.
+ */
+static struct cmd_ctrl_node *
+nxpwifi_get_cmd_node(struct nxpwifi_adapter *adapter)
+{
+ struct cmd_ctrl_node *cmd_node;
+
+ spin_lock_bh(&adapter->cmd_free_q_lock);
+ if (list_empty(&adapter->cmd_free_q)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "GET_CMD_NODE: cmd node not available\n");
+ spin_unlock_bh(&adapter->cmd_free_q_lock);
+ return NULL;
+ }
+ cmd_node = list_first_entry(&adapter->cmd_free_q,
+ struct cmd_ctrl_node, list);
+ list_del(&cmd_node->list);
+ spin_unlock_bh(&adapter->cmd_free_q_lock);
+
+ return cmd_node;
+}
+
+/* This function cleans up a command node.
+ *
+ * The function resets the fields including the buffer pointers.
+ * This function does not try to free the buffers. They must be
+ * freed before calling this function.
+ *
+ * This function will however call the receive completion callback
+ * in case a response buffer is still available before resetting
+ * the pointer.
+ */
+static void
+nxpwifi_clean_cmd_node(struct nxpwifi_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node)
+{
+ cmd_node->cmd_no = 0;
+ cmd_node->cmd_flag = 0;
+ cmd_node->data_buf = NULL;
+ cmd_node->wait_q_enabled = false;
+
+ if (cmd_node->cmd_skb)
+ skb_trim(cmd_node->cmd_skb, 0);
+
+ if (cmd_node->resp_skb) {
+ adapter->if_ops.cmdrsp_complete(adapter, cmd_node->resp_skb);
+ cmd_node->resp_skb = NULL;
+ }
+}
+
+/* This function returns a command to the command free queue.
+ *
+ * The function also calls the completion callback if required, before
+ * cleaning the command node and re-inserting it into the free queue.
+ */
+static void
+nxpwifi_insert_cmd_to_free_q(struct nxpwifi_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node)
+{
+ if (!cmd_node)
+ return;
+
+ if (cmd_node->wait_q_enabled)
+ nxpwifi_complete_cmd(adapter, cmd_node);
+ /* Clean the node */
+ nxpwifi_clean_cmd_node(adapter, cmd_node);
+
+ /* Insert node into cmd_free_q */
+ spin_lock_bh(&adapter->cmd_free_q_lock);
+ list_add_tail(&cmd_node->list, &adapter->cmd_free_q);
+ spin_unlock_bh(&adapter->cmd_free_q_lock);
+}
+
+/* This function reuses a command node. */
+void nxpwifi_recycle_cmd_node(struct nxpwifi_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node)
+{
+ struct host_cmd_ds_command *host_cmd = (void *)cmd_node->cmd_skb->data;
+
+ nxpwifi_insert_cmd_to_free_q(adapter, cmd_node);
+
+ atomic_dec(&adapter->cmd_pending);
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: FREE_CMD: cmd=%#x, cmd_pending=%d\n",
+ le16_to_cpu(host_cmd->command),
+ atomic_read(&adapter->cmd_pending));
+}
+
+/* This function sends a host command to the firmware.
+ *
+ * The function copies the host command into the driver command
+ * buffer, which will be transferred to the firmware later by the
+ * main thread.
+ */
+static int nxpwifi_cmd_host_cmd(struct nxpwifi_private *priv,
+ struct cmd_ctrl_node *cmd_node)
+{
+ struct host_cmd_ds_command *cmd;
+ struct nxpwifi_ds_misc_cmd *pcmd_ptr;
+
+ cmd = (struct host_cmd_ds_command *)cmd_node->skb->data;
+ pcmd_ptr = (struct nxpwifi_ds_misc_cmd *)cmd_node->data_buf;
+
+ /* Copy the HOST command to command buffer */
+ memcpy(cmd, pcmd_ptr->cmd, pcmd_ptr->len);
+ nxpwifi_dbg(priv->adapter, CMD,
+ "cmd: host cmd size = %d\n", pcmd_ptr->len);
+ return 0;
+}
+
+/* This function downloads a command to the firmware.
+ *
+ * The function performs sanity tests, sets the command sequence
+ * number and size, converts the header fields to CPU format before
+ * sending. Afterwards, it logs the command ID and action for debugging
+ * and sets up the command timeout timer.
+ */
+static int nxpwifi_dnld_cmd_to_fw(struct nxpwifi_private *priv,
+ struct cmd_ctrl_node *cmd_node)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int ret;
+ struct host_cmd_ds_command *host_cmd;
+ u16 cmd_code;
+ u16 cmd_size;
+
+ if (!adapter || !cmd_node)
+ return -1;
+
+ host_cmd = (struct host_cmd_ds_command *)(cmd_node->cmd_skb->data);
+
+ /* Sanity test */
+ if (host_cmd->size == 0) {
+ nxpwifi_dbg(adapter, ERROR,
+ "DNLD_CMD: host_cmd is null\t"
+ "or cmd size is 0, not sending\n");
+ if (cmd_node->wait_q_enabled)
+ adapter->cmd_wait_q.status = -1;
+ nxpwifi_recycle_cmd_node(adapter, cmd_node);
+ return -1;
+ }
+
+ cmd_code = le16_to_cpu(host_cmd->command);
+ cmd_node->cmd_no = cmd_code;
+ cmd_size = le16_to_cpu(host_cmd->size);
+
+ if (adapter->hw_status == NXPWIFI_HW_STATUS_RESET &&
+ cmd_code != HOST_CMD_FUNC_SHUTDOWN &&
+ cmd_code != HOST_CMD_FUNC_INIT) {
+ nxpwifi_dbg(adapter, ERROR,
+ "DNLD_CMD: FW in reset state, ignore cmd %#x\n",
+ cmd_code);
+ nxpwifi_recycle_cmd_node(adapter, cmd_node);
+ queue_work(adapter->workqueue, &adapter->main_work);
+ return -1;
+ }
+
+ /* Set command sequence number */
+ adapter->seq_num++;
+ host_cmd->seq_num = cpu_to_le16(HOST_SET_SEQ_NO_BSS_INFO
+ (adapter->seq_num,
+ cmd_node->priv->bss_num,
+ cmd_node->priv->bss_type));
+
+ spin_lock_bh(&adapter->nxpwifi_cmd_lock);
+ adapter->curr_cmd = cmd_node;
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+
+ /* Adjust skb length */
+ if (cmd_node->cmd_skb->len > cmd_size)
+ /* cmd_size is less than sizeof(struct host_cmd_ds_command).
+ * Trim off the unused portion.
+ */
+ skb_trim(cmd_node->cmd_skb, cmd_size);
+ else if (cmd_node->cmd_skb->len < cmd_size)
+ /* cmd_size is larger than sizeof(struct host_cmd_ds_command)
+ * because we have appended custom IE TLV. Increase skb length
+ * accordingly.
+ */
+ skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len);
+
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n",
+ cmd_code,
+ get_unaligned_le16((u8 *)host_cmd + S_DS_GEN),
+ cmd_size, le16_to_cpu(host_cmd->seq_num));
+ nxpwifi_dbg_dump(adapter, CMD_D, "cmd buffer:", host_cmd, cmd_size);
+
+ skb_push(cmd_node->cmd_skb, adapter->intf_hdr_len);
+ ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_CMD,
+ cmd_node->cmd_skb, NULL);
+ skb_pull(cmd_node->cmd_skb, adapter->intf_hdr_len);
+
+ if (ret == -1) {
+ nxpwifi_dbg(adapter, ERROR,
+ "DNLD_CMD: host to card failed\n");
+ if (cmd_node->wait_q_enabled)
+ adapter->cmd_wait_q.status = -1;
+ nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd);
+
+ spin_lock_bh(&adapter->nxpwifi_cmd_lock);
+ adapter->curr_cmd = NULL;
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+
+ adapter->dbg.num_cmd_host_to_card_failure++;
+ return -1;
+ }
+
+ /* Save the last command id and action to debug log */
+ adapter->dbg.last_cmd_index =
+ (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM;
+ adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code;
+ adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] =
+ get_unaligned_le16((u8 *)host_cmd + S_DS_GEN);
+
+ /* Setup the timer after transmit command, except that specific
+ * command might not have command response.
+ */
+ if (cmd_code != HOST_CMD_FW_DUMP_EVENT)
+ mod_timer(&adapter->cmd_timer,
+ jiffies + msecs_to_jiffies(NXPWIFI_TIMER_10S));
+
+ /* Clear BSS_NO_BITS from HOST */
+ cmd_code &= HOST_CMD_ID_MASK;
+
+ return 0;
+}
+
+/* This function downloads a sleep confirm command to the firmware.
+ *
+ * The function performs sanity tests, sets the command sequence
+ * number and size, converts the header fields to CPU format before
+ * sending.
+ *
+ * No responses are needed for sleep confirm command.
+ */
+static int nxpwifi_dnld_sleep_confirm_cmd(struct nxpwifi_adapter *adapter)
+{
+ int ret;
+ struct nxpwifi_private *priv;
+ struct nxpwifi_opt_sleep_confirm *sleep_cfm_buf =
+ (struct nxpwifi_opt_sleep_confirm *)
+ adapter->sleep_cfm->data;
+
+ priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
+
+ adapter->seq_num++;
+ sleep_cfm_buf->seq_num =
+ cpu_to_le16(HOST_SET_SEQ_NO_BSS_INFO
+ (adapter->seq_num, priv->bss_num,
+ priv->bss_type));
+
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n",
+ le16_to_cpu(sleep_cfm_buf->command),
+ le16_to_cpu(sleep_cfm_buf->action),
+ le16_to_cpu(sleep_cfm_buf->size),
+ le16_to_cpu(sleep_cfm_buf->seq_num));
+ nxpwifi_dbg_dump(adapter, CMD_D, "SLEEP_CFM buffer: ", sleep_cfm_buf,
+ le16_to_cpu(sleep_cfm_buf->size));
+
+ skb_push(adapter->sleep_cfm, adapter->intf_hdr_len);
+ ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_CMD,
+ adapter->sleep_cfm, NULL);
+ skb_pull(adapter->sleep_cfm, adapter->intf_hdr_len);
+
+ if (ret == -1) {
+ nxpwifi_dbg(adapter, ERROR, "SLEEP_CFM: failed\n");
+ adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++;
+ return -1;
+ }
+
+ if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl))
+ /* Response is not needed for sleep confirm command */
+ adapter->ps_state = PS_STATE_SLEEP;
+ else
+ adapter->ps_state = PS_STATE_SLEEP_CFM;
+
+ if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) &&
+ (test_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags) &&
+ !adapter->sleep_period.period)) {
+ adapter->pm_wakeup_card_req = true;
+ nxpwifi_hs_activated_event(nxpwifi_get_priv
+ (adapter, NXPWIFI_BSS_ROLE_ANY), true);
+ }
+
+ return ret;
+}
+
+/* This function allocates the command buffers and links them to
+ * the command free queue.
+ *
+ * The driver uses a pre allocated number of command buffers, which
+ * are created at driver initializations and freed at driver cleanup.
+ * Every command needs to obtain a command buffer from this pool before
+ * it can be issued. The command free queue lists the command buffers
+ * currently free to use, while the command pending queue lists the
+ * command buffers already in use and awaiting handling. Command buffers
+ * are returned to the free queue after use.
+ */
+int nxpwifi_alloc_cmd_buffer(struct nxpwifi_adapter *adapter)
+{
+ struct cmd_ctrl_node *cmd_array;
+ u32 i;
+
+ /* Allocate and initialize struct cmd_ctrl_node */
+ cmd_array = kcalloc(NXPWIFI_NUM_OF_CMD_BUFFER,
+ sizeof(struct cmd_ctrl_node), GFP_KERNEL);
+ if (!cmd_array)
+ return -ENOMEM;
+
+ adapter->cmd_pool = cmd_array;
+
+ /* Allocate and initialize command buffers */
+ for (i = 0; i < NXPWIFI_NUM_OF_CMD_BUFFER; i++) {
+ cmd_array[i].skb = dev_alloc_skb(NXPWIFI_SIZE_OF_CMD_BUFFER);
+ if (!cmd_array[i].skb)
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < NXPWIFI_NUM_OF_CMD_BUFFER; i++)
+ nxpwifi_insert_cmd_to_free_q(adapter, &cmd_array[i]);
+
+ return 0;
+}
+
+/* This function frees the command buffers.
+ *
+ * The function calls the completion callback for all the command
+ * buffers that still have response buffers associated with them.
+ */
+void nxpwifi_free_cmd_buffer(struct nxpwifi_adapter *adapter)
+{
+ struct cmd_ctrl_node *cmd_array;
+ u32 i;
+
+ /* Need to check if cmd pool is allocated or not */
+ if (!adapter->cmd_pool) {
+ nxpwifi_dbg(adapter, FATAL,
+ "info: FREE_CMD_BUF: cmd_pool is null\n");
+ return;
+ }
+
+ cmd_array = adapter->cmd_pool;
+
+ /* Release shared memory buffers */
+ for (i = 0; i < NXPWIFI_NUM_OF_CMD_BUFFER; i++) {
+ if (cmd_array[i].skb) {
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: free cmd buffer %d\n", i);
+ dev_kfree_skb_any(cmd_array[i].skb);
+ }
+ if (!cmd_array[i].resp_skb)
+ continue;
+
+ dev_kfree_skb_any(cmd_array[i].resp_skb);
+ }
+ /* Release struct cmd_ctrl_node */
+ if (adapter->cmd_pool) {
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: free cmd pool\n");
+ kfree(adapter->cmd_pool);
+ adapter->cmd_pool = NULL;
+ }
+}
+
+/* This function handles events generated by firmware.
+ *
+ * Event body of events received from firmware are not used (though they are
+ * saved), only the event ID is used. Some events are re-invoked by
+ * the driver, with a new event body.
+ *
+ * After processing, the function calls the completion callback
+ * for cleanup.
+ */
+int nxpwifi_process_event(struct nxpwifi_adapter *adapter)
+{
+ int ret, i;
+ struct nxpwifi_private *priv =
+ nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
+ struct sk_buff *skb = adapter->event_skb;
+ u32 eventcause;
+ struct nxpwifi_rxinfo *rx_info;
+
+ if ((adapter->event_cause & EVENT_ID_MASK) == EVENT_RADAR_DETECTED) {
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (priv && nxpwifi_is_11h_active(priv)) {
+ adapter->event_cause |=
+ ((priv->bss_num & 0xff) << 16) |
+ ((priv->bss_type & 0xff) << 24);
+ break;
+ }
+ }
+ }
+
+ eventcause = adapter->event_cause;
+
+ /* Save the last event to debug log */
+ adapter->dbg.last_event_index =
+ (adapter->dbg.last_event_index + 1) % DBG_CMD_NUM;
+ adapter->dbg.last_event[adapter->dbg.last_event_index] =
+ (u16)eventcause;
+
+ /* Get BSS number and corresponding priv */
+ priv = nxpwifi_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause),
+ EVENT_GET_BSS_TYPE(eventcause));
+ if (!priv)
+ priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
+
+ /* Clear BSS_NO_BITS from event */
+ eventcause &= EVENT_ID_MASK;
+ adapter->event_cause = eventcause;
+
+ if (skb) {
+ rx_info = NXPWIFI_SKB_RXCB(skb);
+ memset(rx_info, 0, sizeof(*rx_info));
+ rx_info->bss_num = priv->bss_num;
+ rx_info->bss_type = priv->bss_type;
+ nxpwifi_dbg_dump(adapter, EVT_D, "Event Buf:",
+ skb->data, skb->len);
+ }
+
+ nxpwifi_dbg(adapter, EVENT, "EVENT: cause: %#x\n", eventcause);
+
+ if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP)
+ ret = nxpwifi_process_uap_event(priv);
+ else
+ ret = nxpwifi_process_sta_event(priv);
+
+ adapter->event_cause = 0;
+ adapter->event_skb = NULL;
+ adapter->if_ops.event_complete(adapter, skb);
+
+ return ret;
+}
+
+/* This function prepares a command and send it to the firmware.
+ *
+ * Preparation includes -
+ * - Sanity tests to make sure the card is still present or the FW
+ * is not reset
+ * - Getting a new command node from the command free queue
+ * - Initializing the command node for default parameters
+ * - Fill up the non-default parameters and buffer pointers
+ * - Add the command to pending queue
+ */
+int nxpwifi_send_cmd(struct nxpwifi_private *priv, u16 cmd_no,
+ u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync)
+{
+ int ret;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct cmd_ctrl_node *cmd_node;
+
+ if (!adapter) {
+ pr_err("PREP_CMD: adapter is NULL\n");
+ return -1;
+ }
+
+ if (test_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "PREP_CMD: device in suspended state\n");
+ return -1;
+ }
+
+ if (test_bit(NXPWIFI_IS_HS_ENABLING, &adapter->work_flags) &&
+ cmd_no != HOST_CMD_802_11_HS_CFG_ENH) {
+ nxpwifi_dbg(adapter, ERROR,
+ "PREP_CMD: host entering sleep state\n");
+ return -1;
+ }
+
+ if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "PREP_CMD: card is removed\n");
+ return -1;
+ }
+
+ if (test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "PREP_CMD: FW is in bad state\n");
+ return -1;
+ }
+
+ if (adapter->hw_status == NXPWIFI_HW_STATUS_RESET) {
+ if (cmd_no != HOST_CMD_FUNC_INIT) {
+ nxpwifi_dbg(adapter, ERROR,
+ "PREP_CMD: FW in reset state\n");
+ return -1;
+ }
+ }
+
+ if (priv->adapter->hs_activated_manually &&
+ cmd_no != HOST_CMD_802_11_HS_CFG_ENH) {
+ nxpwifi_cancel_hs(priv, NXPWIFI_ASYNC_CMD);
+ priv->adapter->hs_activated_manually = false;
+ }
+
+ /* Get a new command node */
+ cmd_node = nxpwifi_get_cmd_node(adapter);
+
+ if (!cmd_node) {
+ nxpwifi_dbg(adapter, ERROR,
+ "PREP_CMD: no free cmd node\n");
+ return -1;
+ }
+
+ /* Initialize the command node */
+ nxpwifi_init_cmd_node(priv, cmd_node, cmd_no, data_buf, sync);
+
+ if (!cmd_node->cmd_skb) {
+ nxpwifi_dbg(adapter, ERROR,
+ "PREP_CMD: no free cmd buf\n");
+ return -1;
+ }
+
+ skb_put_zero(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command));
+
+ /* Prepare command */
+ if (cmd_no) {
+ switch (cmd_no) {
+ case HOST_CMD_UAP_SYS_CONFIG:
+ case HOST_CMD_UAP_BSS_START:
+ case HOST_CMD_UAP_BSS_STOP:
+ case HOST_CMD_UAP_STA_DEAUTH:
+ case HOST_CMD_APCMD_SYS_RESET:
+ case HOST_CMD_APCMD_STA_LIST:
+ case HOST_CMD_CHAN_REPORT_REQUEST:
+ case HOST_CMD_ADD_NEW_STATION:
+ ret = nxpwifi_uap_prepare_cmd(priv, cmd_node,
+ cmd_action, cmd_oid);
+ break;
+ default:
+ ret = nxpwifi_sta_prepare_cmd(priv, cmd_node,
+ cmd_action, cmd_oid);
+ break;
+ }
+ } else {
+ ret = nxpwifi_cmd_host_cmd(priv, cmd_node);
+ cmd_node->cmd_flag |= CMD_F_HOSTCMD;
+ }
+
+ /* Return error, since the command preparation failed */
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "PREP_CMD: cmd %#x preparation failed\n",
+ cmd_no);
+ nxpwifi_insert_cmd_to_free_q(adapter, cmd_node);
+ return -1;
+ }
+
+ /* Send command */
+ if (cmd_no == HOST_CMD_802_11_SCAN ||
+ cmd_no == HOST_CMD_802_11_SCAN_EXT) {
+ nxpwifi_queue_scan_cmd(priv, cmd_node);
+ } else {
+ nxpwifi_insert_cmd_to_pending_q(adapter, cmd_node);
+ queue_work(adapter->workqueue, &adapter->main_work);
+ if (cmd_node->wait_q_enabled)
+ ret = nxpwifi_wait_queue_complete(adapter, cmd_node);
+ }
+
+ return ret;
+}
+
+/* This function queues a command to the command pending queue.
+ *
+ * This in effect adds the command to the command list to be executed.
+ * Exit PS command is handled specially, by placing it always to the
+ * front of the command queue.
+ */
+void
+nxpwifi_insert_cmd_to_pending_q(struct nxpwifi_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node)
+{
+ struct host_cmd_ds_command *host_cmd = NULL;
+ u16 command;
+ bool add_tail = true;
+
+ host_cmd = (struct host_cmd_ds_command *)(cmd_node->cmd_skb->data);
+ if (!host_cmd) {
+ nxpwifi_dbg(adapter, ERROR, "QUEUE_CMD: host_cmd is NULL\n");
+ return;
+ }
+
+ command = le16_to_cpu(host_cmd->command);
+
+ /* Exit_PS command needs to be queued in the header always. */
+ if (command == HOST_CMD_802_11_PS_MODE_ENH) {
+ struct host_cmd_ds_802_11_ps_mode_enh *pm =
+ &host_cmd->params.psmode_enh;
+ if ((le16_to_cpu(pm->action) == DIS_PS) ||
+ (le16_to_cpu(pm->action) == DIS_AUTO_PS)) {
+ if (adapter->ps_state != PS_STATE_AWAKE)
+ add_tail = false;
+ }
+ }
+
+ /* Same with exit host sleep cmd, luckily that can't happen at the same time as EXIT_PS */
+ if (command == HOST_CMD_802_11_HS_CFG_ENH) {
+ struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg =
+ &host_cmd->params.opt_hs_cfg;
+
+ if (le16_to_cpu(hs_cfg->action) == HS_ACTIVATE)
+ add_tail = false;
+ }
+
+ spin_lock_bh(&adapter->cmd_pending_q_lock);
+ if (add_tail)
+ list_add_tail(&cmd_node->list, &adapter->cmd_pending_q);
+ else
+ list_add(&cmd_node->list, &adapter->cmd_pending_q);
+ spin_unlock_bh(&adapter->cmd_pending_q_lock);
+
+ atomic_inc(&adapter->cmd_pending);
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: QUEUE_CMD: cmd=%#x, cmd_pending=%d\n",
+ command, atomic_read(&adapter->cmd_pending));
+}
+
+/* This function executes the next command in command pending queue.
+ *
+ * This function will fail if a command is already in processing stage,
+ * otherwise it will dequeue the first command from the command pending
+ * queue and send to the firmware.
+ *
+ * If the device is currently in host sleep mode, any commands, except the
+ * host sleep configuration command will de-activate the host sleep. For PS
+ * mode, the function will put the firmware back to sleep if applicable.
+ */
+int nxpwifi_exec_next_cmd(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_private *priv;
+ struct cmd_ctrl_node *cmd_node;
+ int ret = 0;
+ struct host_cmd_ds_command *host_cmd;
+
+ /* Check if already in processing */
+ if (adapter->curr_cmd) {
+ nxpwifi_dbg(adapter, FATAL,
+ "EXEC_NEXT_CMD: cmd in processing\n");
+ return -1;
+ }
+
+ spin_lock_bh(&adapter->nxpwifi_cmd_lock);
+ /* Check if any command is pending */
+ spin_lock_bh(&adapter->cmd_pending_q_lock);
+ if (list_empty(&adapter->cmd_pending_q)) {
+ spin_unlock_bh(&adapter->cmd_pending_q_lock);
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+ return 0;
+ }
+ cmd_node = list_first_entry(&adapter->cmd_pending_q,
+ struct cmd_ctrl_node, list);
+
+ host_cmd = (struct host_cmd_ds_command *)(cmd_node->cmd_skb->data);
+ priv = cmd_node->priv;
+
+ if (adapter->ps_state != PS_STATE_AWAKE) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: cannot send cmd in sleep state,\t"
+ "this should not happen\n", __func__);
+ spin_unlock_bh(&adapter->cmd_pending_q_lock);
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+ return ret;
+ }
+
+ list_del(&cmd_node->list);
+ spin_unlock_bh(&adapter->cmd_pending_q_lock);
+
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+ ret = nxpwifi_dnld_cmd_to_fw(priv, cmd_node);
+ priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
+ /* Any command sent to the firmware when host is in sleep
+ * mode should de-configure host sleep. We should skip the
+ * host sleep configuration command itself though
+ */
+ if (priv && host_cmd->command !=
+ cpu_to_le16(HOST_CMD_802_11_HS_CFG_ENH)) {
+ if (adapter->hs_activated) {
+ clear_bit(NXPWIFI_IS_HS_CONFIGURED,
+ &adapter->work_flags);
+ nxpwifi_hs_activated_event(priv, false);
+ }
+ }
+
+ return ret;
+}
+
+static void
+nxpwifi_process_cmdresp_error(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_802_11_ps_mode_enh *pm;
+
+ nxpwifi_dbg(adapter, ERROR,
+ "CMD_RESP: cmd %#x error, result=%#x\n",
+ resp->command, resp->result);
+
+ if (adapter->curr_cmd->wait_q_enabled)
+ adapter->cmd_wait_q.status = -1;
+
+ switch (le16_to_cpu(resp->command)) {
+ case HOST_CMD_802_11_PS_MODE_ENH:
+ pm = &resp->params.psmode_enh;
+ nxpwifi_dbg(adapter, ERROR,
+ "PS_MODE_ENH cmd failed: result=0x%x action=0x%X\n",
+ resp->result, le16_to_cpu(pm->action));
+ break;
+ case HOST_CMD_802_11_SCAN:
+ case HOST_CMD_802_11_SCAN_EXT:
+ nxpwifi_cancel_scan(adapter);
+ break;
+
+ case HOST_CMD_MAC_CONTROL:
+ break;
+
+ case HOST_CMD_SDIO_SP_RX_AGGR_CFG:
+ nxpwifi_dbg(adapter, MSG,
+ "SDIO RX single-port aggregation Not support\n");
+ break;
+
+ default:
+ break;
+ }
+ /* Handling errors here */
+ nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd);
+
+ spin_lock_bh(&adapter->nxpwifi_cmd_lock);
+ adapter->curr_cmd = NULL;
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+}
+
+/* This function handles the command response.
+ *
+ * After processing, the function cleans the command node and puts
+ * it back to the command free queue.
+ */
+int nxpwifi_process_cmdresp(struct nxpwifi_adapter *adapter)
+{
+ struct host_cmd_ds_command *resp;
+ struct nxpwifi_private *priv =
+ nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
+ int ret = 0;
+ u16 orig_cmdresp_no;
+ u16 cmdresp_no;
+ u16 cmdresp_result;
+
+ if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) {
+ resp = (struct host_cmd_ds_command *)adapter->upld_buf;
+ nxpwifi_dbg(adapter, ERROR,
+ "CMD_RESP: NULL curr_cmd, %#x\n",
+ le16_to_cpu(resp->command));
+ return -1;
+ }
+
+ resp = (struct host_cmd_ds_command *)adapter->curr_cmd->resp_skb->data;
+ orig_cmdresp_no = le16_to_cpu(resp->command);
+ cmdresp_no = (orig_cmdresp_no & HOST_CMD_ID_MASK);
+
+ if (adapter->curr_cmd->cmd_no != cmdresp_no) {
+ nxpwifi_dbg(adapter, ERROR,
+ "cmdresp error: cmd=0x%x cmd_resp=0x%x\n",
+ adapter->curr_cmd->cmd_no, cmdresp_no);
+ return -1;
+ }
+ /* Now we got response from FW, cancel the command timer */
+ del_timer_sync(&adapter->cmd_timer);
+ clear_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags);
+
+ if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {
+ /* Copy original response back to response buffer */
+ struct nxpwifi_ds_misc_cmd *hostcmd;
+ u16 size = le16_to_cpu(resp->size);
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: host cmd resp size = %d\n", size);
+ size = min_t(u16, size, NXPWIFI_SIZE_OF_CMD_BUFFER);
+ if (adapter->curr_cmd->data_buf) {
+ hostcmd = adapter->curr_cmd->data_buf;
+ hostcmd->len = size;
+ memcpy(hostcmd->cmd, resp, size);
+ }
+ }
+
+ /* Get BSS number and corresponding priv */
+ priv = nxpwifi_get_priv_by_id
+ (adapter, HOST_GET_BSS_NO(le16_to_cpu(resp->seq_num)),
+ HOST_GET_BSS_TYPE(le16_to_cpu(resp->seq_num)));
+ if (!priv)
+ priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
+ /* Clear RET_BIT from HOST */
+ resp->command = cpu_to_le16(orig_cmdresp_no & HOST_CMD_ID_MASK);
+
+ cmdresp_no = le16_to_cpu(resp->command);
+ cmdresp_result = le16_to_cpu(resp->result);
+
+ /* Save the last command response to debug log */
+ adapter->dbg.last_cmd_resp_index =
+ (adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM;
+ adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] =
+ orig_cmdresp_no;
+
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",
+ orig_cmdresp_no, cmdresp_result,
+ le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num));
+ nxpwifi_dbg_dump(adapter, CMD_D, "CMD_RESP buffer:", resp,
+ le16_to_cpu(resp->size));
+
+ if (!(orig_cmdresp_no & HOST_RET_BIT)) {
+ nxpwifi_dbg(adapter, ERROR, "CMD_RESP: invalid cmd resp\n");
+ if (adapter->curr_cmd->wait_q_enabled)
+ adapter->cmd_wait_q.status = -1;
+
+ nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd);
+ spin_lock_bh(&adapter->nxpwifi_cmd_lock);
+ adapter->curr_cmd = NULL;
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+ return -1;
+ }
+
+ if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {
+ adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD;
+ if (cmdresp_result == HOST_RESULT_OK &&
+ cmdresp_no == HOST_CMD_802_11_HS_CFG_ENH)
+ ret = nxpwifi_ret_802_11_hs_cfg(priv, resp);
+ } else {
+ if (resp->result != HOST_RESULT_OK) {
+ nxpwifi_process_cmdresp_error(priv, resp);
+ return -1;
+ }
+ if (adapter->curr_cmd->cmd_resp) {
+ void *data_buf = adapter->curr_cmd->data_buf;
+
+ ret = adapter->curr_cmd->cmd_resp(priv, resp,
+ cmdresp_no,
+ data_buf);
+ }
+ }
+
+ /* Check init command response */
+ if (adapter->hw_status == NXPWIFI_HW_STATUS_INITIALIZING) {
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: cmd %#x failed during\t"
+ "initialization\n", __func__, cmdresp_no);
+ nxpwifi_init_fw_complete(adapter);
+ return -1;
+ } else if (adapter->last_init_cmd == cmdresp_no) {
+ adapter->hw_status = NXPWIFI_HW_STATUS_INIT_DONE;
+ }
+ }
+
+ if (adapter->curr_cmd) {
+ if (adapter->curr_cmd->wait_q_enabled)
+ adapter->cmd_wait_q.status = ret;
+
+ nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd);
+
+ spin_lock_bh(&adapter->nxpwifi_cmd_lock);
+ adapter->curr_cmd = NULL;
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+ }
+
+ return ret;
+}
+
+void nxpwifi_process_assoc_resp(struct nxpwifi_adapter *adapter)
+{
+ struct cfg80211_rx_assoc_resp_data assoc_resp = {
+ .uapsd_queues = -1,
+ };
+ struct nxpwifi_private *priv =
+ nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA);
+
+ if (priv->assoc_rsp_size) {
+ assoc_resp.links[0].bss = priv->req_bss;
+ assoc_resp.buf = priv->assoc_rsp_buf;
+ assoc_resp.len = priv->assoc_rsp_size;
+ cfg80211_rx_assoc_resp(priv->netdev,
+ &assoc_resp);
+ priv->assoc_rsp_size = 0;
+ }
+}
+
+/* This function handles the timeout of command sending.
+ *
+ * It will re-send the same command again.
+ */
+void
+nxpwifi_cmd_timeout_func(struct timer_list *t)
+{
+ struct nxpwifi_adapter *adapter = from_timer(adapter, t, cmd_timer);
+ struct cmd_ctrl_node *cmd_node;
+
+ set_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags);
+ if (!adapter->curr_cmd) {
+ nxpwifi_dbg(adapter, ERROR,
+ "cmd: empty curr_cmd\n");
+ return;
+ }
+ cmd_node = adapter->curr_cmd;
+ if (cmd_node) {
+ adapter->dbg.timeout_cmd_id =
+ adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index];
+ adapter->dbg.timeout_cmd_act =
+ adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index];
+ nxpwifi_dbg(adapter, MSG,
+ "%s: Timeout cmd id = %#x, act = %#x\n", __func__,
+ adapter->dbg.timeout_cmd_id,
+ adapter->dbg.timeout_cmd_act);
+
+ nxpwifi_dbg(adapter, MSG,
+ "num_data_h2c_failure = %d\n",
+ adapter->dbg.num_tx_host_to_card_failure);
+ nxpwifi_dbg(adapter, MSG,
+ "num_cmd_h2c_failure = %d\n",
+ adapter->dbg.num_cmd_host_to_card_failure);
+
+ nxpwifi_dbg(adapter, MSG,
+ "is_cmd_timedout = %d\n",
+ test_bit(NXPWIFI_IS_CMD_TIMEDOUT,
+ &adapter->work_flags));
+ nxpwifi_dbg(adapter, MSG,
+ "num_tx_timeout = %d\n",
+ adapter->dbg.num_tx_timeout);
+
+ nxpwifi_dbg(adapter, MSG,
+ "last_cmd_index = %d\n",
+ adapter->dbg.last_cmd_index);
+ nxpwifi_dbg(adapter, MSG,
+ "last_cmd_id: %*ph\n",
+ (int)sizeof(adapter->dbg.last_cmd_id),
+ adapter->dbg.last_cmd_id);
+ nxpwifi_dbg(adapter, MSG,
+ "last_cmd_act: %*ph\n",
+ (int)sizeof(adapter->dbg.last_cmd_act),
+ adapter->dbg.last_cmd_act);
+
+ nxpwifi_dbg(adapter, MSG,
+ "last_cmd_resp_index = %d\n",
+ adapter->dbg.last_cmd_resp_index);
+ nxpwifi_dbg(adapter, MSG,
+ "last_cmd_resp_id: %*ph\n",
+ (int)sizeof(adapter->dbg.last_cmd_resp_id),
+ adapter->dbg.last_cmd_resp_id);
+
+ nxpwifi_dbg(adapter, MSG,
+ "last_event_index = %d\n",
+ adapter->dbg.last_event_index);
+ nxpwifi_dbg(adapter, MSG,
+ "last_event: %*ph\n",
+ (int)sizeof(adapter->dbg.last_event),
+ adapter->dbg.last_event);
+
+ nxpwifi_dbg(adapter, MSG,
+ "data_sent=%d cmd_sent=%d\n",
+ adapter->data_sent, adapter->cmd_sent);
+
+ nxpwifi_dbg(adapter, MSG,
+ "ps_mode=%d ps_state=%d\n",
+ adapter->ps_mode, adapter->ps_state);
+
+ if (cmd_node->wait_q_enabled) {
+ adapter->cmd_wait_q.status = -ETIMEDOUT;
+ nxpwifi_cancel_pending_ioctl(adapter);
+ }
+ }
+ if (adapter->hw_status == NXPWIFI_HW_STATUS_INITIALIZING) {
+ nxpwifi_init_fw_complete(adapter);
+ return;
+ }
+
+ if (adapter->if_ops.device_dump)
+ adapter->if_ops.device_dump(adapter);
+
+ if (adapter->if_ops.card_reset)
+ adapter->if_ops.card_reset(adapter);
+}
+
+void
+nxpwifi_cancel_pending_scan_cmd(struct nxpwifi_adapter *adapter)
+{
+ struct cmd_ctrl_node *cmd_node = NULL, *tmp_node;
+
+ /* Cancel all pending scan command */
+ spin_lock_bh(&adapter->scan_pending_q_lock);
+ list_for_each_entry_safe(cmd_node, tmp_node,
+ &adapter->scan_pending_q, list) {
+ list_del(&cmd_node->list);
+ cmd_node->wait_q_enabled = false;
+ nxpwifi_insert_cmd_to_free_q(adapter, cmd_node);
+ }
+ spin_unlock_bh(&adapter->scan_pending_q_lock);
+}
+
+/* This function cancels all the pending commands.
+ *
+ * The current command, all commands in command pending queue and all scan
+ * commands in scan pending queue are cancelled. All the completion callbacks
+ * are called with failure status to ensure cleanup.
+ */
+void
+nxpwifi_cancel_all_pending_cmd(struct nxpwifi_adapter *adapter)
+{
+ struct cmd_ctrl_node *cmd_node = NULL, *tmp_node;
+
+ spin_lock_bh(&adapter->nxpwifi_cmd_lock);
+ /* Cancel current cmd */
+ if (adapter->curr_cmd && adapter->curr_cmd->wait_q_enabled) {
+ adapter->cmd_wait_q.status = -1;
+ nxpwifi_complete_cmd(adapter, adapter->curr_cmd);
+ adapter->curr_cmd->wait_q_enabled = false;
+ /* no recycle probably wait for response */
+ }
+ /* Cancel all pending command */
+ spin_lock_bh(&adapter->cmd_pending_q_lock);
+ list_for_each_entry_safe(cmd_node, tmp_node,
+ &adapter->cmd_pending_q, list) {
+ list_del(&cmd_node->list);
+
+ if (cmd_node->wait_q_enabled)
+ adapter->cmd_wait_q.status = -1;
+ nxpwifi_recycle_cmd_node(adapter, cmd_node);
+ }
+ spin_unlock_bh(&adapter->cmd_pending_q_lock);
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+
+ nxpwifi_cancel_scan(adapter);
+}
+
+/* This function cancels all pending commands that matches with
+ * the given IOCTL request.
+ *
+ * Both the current command buffer and the pending command queue are
+ * searched for matching IOCTL request. The completion callback of
+ * the matched command is called with failure status to ensure cleanup.
+ * In case of scan commands, all pending commands in scan pending queue
+ * are cancelled.
+ */
+static void
+nxpwifi_cancel_pending_ioctl(struct nxpwifi_adapter *adapter)
+{
+ struct cmd_ctrl_node *cmd_node = NULL;
+
+ if (adapter->curr_cmd &&
+ adapter->curr_cmd->wait_q_enabled) {
+ spin_lock_bh(&adapter->nxpwifi_cmd_lock);
+ cmd_node = adapter->curr_cmd;
+ /* setting curr_cmd to NULL is quite dangerous, because
+ * nxpwifi_process_cmdresp checks curr_cmd to be != NULL
+ * at the beginning then relies on it and dereferences
+ * it at will
+ * this probably works since nxpwifi_cmd_timeout_func
+ * is the only caller of this function and responses
+ * at that point
+ */
+ adapter->curr_cmd = NULL;
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+
+ nxpwifi_recycle_cmd_node(adapter, cmd_node);
+ }
+
+ nxpwifi_cancel_scan(adapter);
+}
+
+/* This function sends the sleep confirm command to firmware, if
+ * possible.
+ *
+ * The sleep confirm command cannot be issued if command response,
+ * data response or event response is awaiting handling, or if we
+ * are in the middle of sending a command, or expecting a command
+ * response.
+ */
+void
+nxpwifi_check_ps_cond(struct nxpwifi_adapter *adapter)
+{
+ if (!adapter->cmd_sent && !atomic_read(&adapter->tx_hw_pending) &&
+ !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter))
+ nxpwifi_dnld_sleep_confirm_cmd(adapter);
+ else
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: Delay Sleep Confirm (%s%s%s%s)\n",
+ (adapter->cmd_sent) ? "D" : "",
+ atomic_read(&adapter->tx_hw_pending) ? "T" : "",
+ (adapter->curr_cmd) ? "C" : "",
+ (IS_CARD_RX_RCVD(adapter)) ? "R" : "");
+}
+
+/* This function sends a Host Sleep activated event to applications.
+ *
+ * This event is generated by the driver, with a blank event body.
+ */
+void
+nxpwifi_hs_activated_event(struct nxpwifi_private *priv, u8 activated)
+{
+ if (activated) {
+ if (test_bit(NXPWIFI_IS_HS_CONFIGURED,
+ &priv->adapter->work_flags)) {
+ priv->adapter->hs_activated = true;
+ nxpwifi_update_rxreor_flags(priv->adapter,
+ RXREOR_FORCE_NO_DROP);
+ nxpwifi_dbg(priv->adapter, EVENT,
+ "event: hs_activated\n");
+ priv->adapter->hs_activate_wait_q_woken = true;
+ wake_up_interruptible(&priv->adapter->hs_activate_wait_q);
+ } else {
+ nxpwifi_dbg(priv->adapter, EVENT,
+ "event: HS not configured\n");
+ }
+ } else {
+ nxpwifi_dbg(priv->adapter, EVENT,
+ "event: hs_deactivated\n");
+ priv->adapter->hs_activated = false;
+ }
+}
+
+/* This function handles the command response of a Host Sleep configuration
+ * command.
+ *
+ * Handling includes changing the header fields into CPU format
+ * and setting the current host sleep activation status in driver.
+ *
+ * In case host sleep status change, the function generates an event to
+ * notify the applications.
+ */
+int nxpwifi_ret_802_11_hs_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg =
+ &resp->params.opt_hs_cfg;
+ u32 conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions);
+
+ if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE)) {
+ nxpwifi_hs_activated_event(priv, true);
+ goto done;
+ } else {
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: CMD_RESP: HS_CFG cmd reply\t"
+ " result=%#x, conditions=0x%x gpio=0x%x gap=0x%x\n",
+ resp->result, conditions,
+ phs_cfg->params.hs_config.gpio,
+ phs_cfg->params.hs_config.gap);
+ }
+ if (conditions != HS_CFG_CANCEL) {
+ set_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags);
+ } else {
+ clear_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags);
+ if (adapter->hs_activated)
+ nxpwifi_hs_activated_event(priv, false);
+ }
+
+done:
+ return 0;
+}
+
+/* This function wakes up the adapter and generates a Host Sleep
+ * cancel event on receiving the power up interrupt.
+ */
+void
+nxpwifi_process_hs_config(struct nxpwifi_adapter *adapter)
+{
+ nxpwifi_dbg(adapter, INFO,
+ "info: %s: auto cancelling host sleep\t"
+ "since there is interrupt from the firmware\n",
+ __func__);
+
+ adapter->if_ops.wakeup(adapter);
+
+ if (adapter->hs_activated_manually) {
+ nxpwifi_cancel_hs(nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY),
+ NXPWIFI_ASYNC_CMD);
+ adapter->hs_activated_manually = false;
+ }
+
+ adapter->hs_activated = false;
+ clear_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags);
+ clear_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags);
+ nxpwifi_hs_activated_event(nxpwifi_get_priv(adapter,
+ NXPWIFI_BSS_ROLE_ANY),
+ false);
+}
+EXPORT_SYMBOL_GPL(nxpwifi_process_hs_config);
+
+/* This function handles the command response of a sleep confirm command.
+ *
+ * The function sets the card state to SLEEP if the response indicates success.
+ */
+void
+nxpwifi_process_sleep_confirm_resp(struct nxpwifi_adapter *adapter,
+ u8 *pbuf, u32 upld_len)
+{
+ struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *)pbuf;
+ u16 result = le16_to_cpu(cmd->result);
+ u16 command = le16_to_cpu(cmd->command);
+ u16 seq_num = le16_to_cpu(cmd->seq_num);
+
+ if (!upld_len) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: cmd size is 0\n", __func__);
+ return;
+ }
+
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n",
+ command, result, le16_to_cpu(cmd->size), seq_num);
+
+ /* Update sequence number */
+ seq_num = HOST_GET_SEQ_NO(seq_num);
+ /* Clear RET_BIT from HOST */
+ command &= HOST_CMD_ID_MASK;
+
+ if (command != HOST_CMD_802_11_PS_MODE_ENH) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: rcvd unexpected resp for cmd %#x, result = %x\n",
+ __func__, command, result);
+ return;
+ }
+
+ if (result) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: sleep confirm cmd failed\n",
+ __func__);
+ adapter->pm_wakeup_card_req = false;
+ adapter->ps_state = PS_STATE_AWAKE;
+ return;
+ }
+ adapter->pm_wakeup_card_req = true;
+ if (test_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags))
+ nxpwifi_hs_activated_event(nxpwifi_get_priv
+ (adapter, NXPWIFI_BSS_ROLE_ANY),
+ true);
+ adapter->ps_state = PS_STATE_SLEEP;
+ cmd->command = cpu_to_le16(command);
+ cmd->seq_num = cpu_to_le16(seq_num);
+}
+EXPORT_SYMBOL_GPL(nxpwifi_process_sleep_confirm_resp);
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 14/43] wifi: nxpwifi: add cmdevt.h
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (12 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 13/43] wifi: nxpwifi: add cmdevt.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 15/43] wifi: nxpwifi: add debugfs.c David Lin
` (30 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/cmdevt.h | 92 +++++++++++++++++++++++
1 file changed, 92 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/cmdevt.h
diff --git a/drivers/net/wireless/nxp/nxpwifi/cmdevt.h b/drivers/net/wireless/nxp/nxpwifi/cmdevt.h
new file mode 100644
index 000000000000..a7774151fa5d
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/cmdevt.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NXP Wireless LAN device driver: commands and events
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#ifndef _NXPWIFI_CMD_EVT_H_
+#define _NXPWIFI_CMD_EVT_H_
+
+struct nxpwifi_cmd_entry {
+ u16 cmd_no;
+ int (*prepare_cmd)(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type);
+ int (*cmd_resp)(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf);
+};
+
+struct nxpwifi_evt_entry {
+ u32 event_cause;
+ int (*event_handler)(struct nxpwifi_private *priv);
+};
+
+static inline int
+nxpwifi_cmd_fill_head_only(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ cmd->command = cpu_to_le16(cmd_no);
+ cmd->size = cpu_to_le16(S_DS_GEN);
+
+ return 0;
+}
+
+int nxpwifi_send_cmd(struct nxpwifi_private *priv, u16 cmd_no,
+ u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync);
+int nxpwifi_sta_prepare_cmd(struct nxpwifi_private *priv,
+ struct cmd_ctrl_node *cmd_node,
+ u16 cmd_action, u32 cmd_oid);
+int nxpwifi_dnld_dt_cfgdata(struct nxpwifi_private *priv,
+ struct device_node *node, const char *prefix);
+int nxpwifi_sta_init_cmd(struct nxpwifi_private *priv, u8 first_sta, bool init);
+int nxpwifi_uap_prepare_cmd(struct nxpwifi_private *priv,
+ struct cmd_ctrl_node *cmd_node,
+ u16 cmd_action, u32 type);
+int nxpwifi_set_secure_params(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_config,
+ struct cfg80211_ap_settings *params);
+void nxpwifi_set_ht_params(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params);
+void nxpwifi_set_vht_params(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params);
+void nxpwifi_set_tpc_params(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params);
+void nxpwifi_set_uap_rates(struct nxpwifi_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params);
+void nxpwifi_set_vht_width(struct nxpwifi_private *priv,
+ enum nl80211_chan_width width,
+ bool ap_11ac_disable);
+void nxpwifi_set_sys_config_invalid_data(struct nxpwifi_uap_bss_param *config);
+void nxpwifi_set_wmm_params(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params);
+void nxpwifi_config_uap_11d(struct nxpwifi_private *priv,
+ struct cfg80211_beacon_data *beacon_data);
+void nxpwifi_uap_set_channel(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_cfg,
+ struct cfg80211_chan_def chandef);
+int nxpwifi_config_start_uap(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_cfg);
+
+int nxpwifi_process_event(struct nxpwifi_adapter *adapter);
+int nxpwifi_process_sta_event(struct nxpwifi_private *priv);
+int nxpwifi_process_uap_event(struct nxpwifi_private *priv);
+void nxpwifi_reset_connect_state(struct nxpwifi_private *priv, u16 reason,
+ bool from_ap);
+void nxpwifi_process_multi_chan_event(struct nxpwifi_private *priv,
+ struct sk_buff *event_skb);
+void nxpwifi_process_tx_pause_event(struct nxpwifi_private *priv,
+ struct sk_buff *event);
+void nxpwifi_bt_coex_wlan_param_update_event(struct nxpwifi_private *priv,
+ struct sk_buff *event_skb);
+
+#endif /* !_NXPWIFI_CMD_EVT_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 15/43] wifi: nxpwifi: add debugfs.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (13 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 14/43] wifi: nxpwifi: add cmdevt.h David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 16/43] wifi: nxpwifi: add decl.h David Lin
` (29 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/debugfs.c | 1042 ++++++++++++++++++++
1 file changed, 1042 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/debugfs.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/debugfs.c b/drivers/net/wireless/nxp/nxpwifi/debugfs.c
new file mode 100644
index 000000000000..f2c3e5159ff5
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/debugfs.c
@@ -0,0 +1,1042 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: debugfs
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include <linux/debugfs.h>
+
+#include "main.h"
+#include "cmdevt.h"
+#include "11n.h"
+
+static struct dentry *nxpwifi_dfs_dir;
+
+static char *bss_modes[] = {
+ "UNSPECIFIED",
+ "ADHOC",
+ "STATION",
+ "AP",
+ "AP_VLAN",
+ "WDS",
+ "MONITOR",
+ "MESH_POINT",
+ "P2P_CLIENT",
+ "P2P_GO",
+ "P2P_DEVICE",
+};
+
+/* Proc info file read handler.
+ *
+ * This function is called when the 'info' file is opened for reading.
+ * It prints the following driver related information -
+ * - Driver name
+ * - Driver version
+ * - Driver extended version
+ * - Interface name
+ * - BSS mode
+ * - Media state (connected or disconnected)
+ * - MAC address
+ * - Total number of Tx bytes
+ * - Total number of Rx bytes
+ * - Total number of Tx packets
+ * - Total number of Rx packets
+ * - Total number of dropped Tx packets
+ * - Total number of dropped Rx packets
+ * - Total number of corrupted Tx packets
+ * - Total number of corrupted Rx packets
+ * - Carrier status (on or off)
+ * - Tx queue status (started or stopped)
+ *
+ * For STA mode drivers, it also prints the following extra -
+ * - ESSID
+ * - BSSID
+ * - Channel
+ * - Region code
+ * - Multicast count
+ * - Multicast addresses
+ */
+static ssize_t
+nxpwifi_info_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv =
+ (struct nxpwifi_private *)file->private_data;
+ struct net_device *netdev = priv->netdev;
+ struct netdev_hw_addr *ha;
+ struct netdev_queue *txq;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page, fmt[64];
+ struct nxpwifi_bss_info info;
+ ssize_t ret;
+ int i = 0;
+
+ if (!p)
+ return -ENOMEM;
+
+ memset(&info, 0, sizeof(info));
+ ret = nxpwifi_get_bss_info(priv, &info);
+ if (ret)
+ goto free_and_exit;
+
+ nxpwifi_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1);
+
+ nxpwifi_get_ver_ext(priv, 0);
+
+ p += sprintf(p, "driver_name = ");
+ p += sprintf(p, "\"nxpwifi\"\n");
+ p += sprintf(p, "driver_version = %s", fmt);
+ p += sprintf(p, "\nverext = %s", priv->version_str);
+ p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name);
+
+ if (info.bss_mode >= ARRAY_SIZE(bss_modes))
+ p += sprintf(p, "bss_mode=\"%d\"\n", info.bss_mode);
+ else
+ p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]);
+
+ p += sprintf(p, "media_state=\"%s\"\n",
+ (!priv->media_connected ? "Disconnected" : "Connected"));
+ p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr);
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) {
+ p += sprintf(p, "multicast_count=\"%d\"\n",
+ netdev_mc_count(netdev));
+ p += sprintf(p, "essid=\"%.*s\"\n", info.ssid.ssid_len,
+ info.ssid.ssid);
+ p += sprintf(p, "bssid=\"%pM\"\n", info.bssid);
+ p += sprintf(p, "channel=\"%d\"\n", (int)info.bss_chan);
+ p += sprintf(p, "country_code = \"%s\"\n", info.country_code);
+ p += sprintf(p, "region_code=\"0x%x\"\n",
+ priv->adapter->region_code);
+
+ netdev_for_each_mc_addr(ha, netdev)
+ p += sprintf(p, "multicast_address[%d]=\"%pM\"\n",
+ i++, ha->addr);
+ }
+
+ p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes);
+ p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes);
+ p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets);
+ p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets);
+ p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped);
+ p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped);
+ p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors);
+ p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors);
+ p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev))
+ ? "on" : "off"));
+ p += sprintf(p, "tx queue");
+ for (i = 0; i < netdev->num_tx_queues; i++) {
+ txq = netdev_get_tx_queue(netdev, i);
+ p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ?
+ "stopped" : "started");
+ }
+ p += sprintf(p, "\n");
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
+ (unsigned long)p - page);
+
+free_and_exit:
+ free_page(page);
+ return ret;
+}
+
+/* Proc getlog file read handler.
+ *
+ * This function is called when the 'getlog' file is opened for reading
+ * It prints the following log information -
+ * - Number of multicast Tx frames
+ * - Number of failed packets
+ * - Number of Tx retries
+ * - Number of multicast Tx retries
+ * - Number of duplicate frames
+ * - Number of RTS successes
+ * - Number of RTS failures
+ * - Number of ACK failures
+ * - Number of fragmented Rx frames
+ * - Number of multicast Rx frames
+ * - Number of FCS errors
+ * - Number of Tx frames
+ * - WEP ICV error counts
+ * - Number of received beacons
+ * - Number of missed beacons
+ */
+static ssize_t
+nxpwifi_getlog_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv =
+ (struct nxpwifi_private *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ ssize_t ret;
+ struct nxpwifi_ds_get_stats stats;
+
+ if (!p)
+ return -ENOMEM;
+
+ memset(&stats, 0, sizeof(stats));
+ ret = nxpwifi_get_stats_info(priv, &stats);
+ if (ret)
+ goto free_and_exit;
+
+ p += sprintf(p, "\n"
+ "mcasttxframe %u\n"
+ "failed %u\n"
+ "retry %u\n"
+ "multiretry %u\n"
+ "framedup %u\n"
+ "rtssuccess %u\n"
+ "rtsfailure %u\n"
+ "ackfailure %u\n"
+ "rxfrag %u\n"
+ "mcastrxframe %u\n"
+ "fcserror %u\n"
+ "txframe %u\n"
+ "wepicverrcnt-1 %u\n"
+ "wepicverrcnt-2 %u\n"
+ "wepicverrcnt-3 %u\n"
+ "wepicverrcnt-4 %u\n"
+ "bcn_rcv_cnt %u\n"
+ "bcn_miss_cnt %u\n",
+ stats.mcast_tx_frame,
+ stats.failed,
+ stats.retry,
+ stats.multi_retry,
+ stats.frame_dup,
+ stats.rts_success,
+ stats.rts_failure,
+ stats.ack_failure,
+ stats.rx_frag,
+ stats.mcast_rx_frame,
+ stats.fcs_error,
+ stats.tx_frame,
+ stats.wep_icv_error[0],
+ stats.wep_icv_error[1],
+ stats.wep_icv_error[2],
+ stats.wep_icv_error[3],
+ stats.bcn_rcv_cnt,
+ stats.bcn_miss_cnt);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
+ (unsigned long)p - page);
+
+free_and_exit:
+ free_page(page);
+ return ret;
+}
+
+/* Sysfs histogram file read handler.
+ *
+ * This function is called when the 'histogram' file is opened for reading
+ * It prints the following histogram information -
+ * - Number of histogram samples
+ * - Receive packet number of each rx_rate
+ * - Receive packet number of each snr
+ * - Receive packet number of each nosie_flr
+ * - Receive packet number of each signal streath
+ */
+static ssize_t
+nxpwifi_histogram_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv =
+ (struct nxpwifi_private *)file->private_data;
+ ssize_t ret;
+ struct nxpwifi_histogram_data *phist_data;
+ int i, value;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+
+ if (!p)
+ return -ENOMEM;
+
+ if (!priv || !priv->hist_data) {
+ ret = -EFAULT;
+ goto free_and_exit;
+ }
+
+ phist_data = priv->hist_data;
+
+ p += sprintf(p, "\n"
+ "total samples = %d\n",
+ atomic_read(&phist_data->num_samples));
+
+ p += sprintf(p,
+ "rx rates (in Mbps): 0=1M 1=2M 2=5.5M 3=11M 4=6M 5=9M 6=12M\n"
+ "7=18M 8=24M 9=36M 10=48M 11=54M 12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n");
+
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
+ p += sprintf(p,
+ "44-53=MCS0-9(VHT:BW20) 54-63=MCS0-9(VHT:BW40) 64-73=MCS0-9(VHT:BW80)\n\n");
+ } else {
+ p += sprintf(p, "\n");
+ }
+
+ for (i = 0; i < NXPWIFI_MAX_RX_RATES; i++) {
+ value = atomic_read(&phist_data->rx_rate[i]);
+ if (value)
+ p += sprintf(p, "rx_rate[%02d] = %d\n", i, value);
+ }
+
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) {
+ for (i = NXPWIFI_MAX_RX_RATES; i < NXPWIFI_MAX_AC_RX_RATES;
+ i++) {
+ value = atomic_read(&phist_data->rx_rate[i]);
+ if (value)
+ p += sprintf(p, "rx_rate[%02d] = %d\n",
+ i, value);
+ }
+ }
+
+ for (i = 0; i < NXPWIFI_MAX_SNR; i++) {
+ value = atomic_read(&phist_data->snr[i]);
+ if (value)
+ p += sprintf(p, "snr[%02ddB] = %d\n", i, value);
+ }
+ for (i = 0; i < NXPWIFI_MAX_NOISE_FLR; i++) {
+ value = atomic_read(&phist_data->noise_flr[i]);
+ if (value)
+ p += sprintf(p, "noise_flr[%02ddBm] = %d\n",
+ (int)(i - 128), value);
+ }
+ for (i = 0; i < NXPWIFI_MAX_SIG_STRENGTH; i++) {
+ value = atomic_read(&phist_data->sig_str[i]);
+ if (value)
+ p += sprintf(p, "sig_strength[-%02ddBm] = %d\n",
+ i, value);
+ }
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
+ (unsigned long)p - page);
+
+free_and_exit:
+ free_page(page);
+ return ret;
+}
+
+static ssize_t
+nxpwifi_histogram_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv = (void *)file->private_data;
+
+ if (priv && priv->hist_data)
+ nxpwifi_hist_data_reset(priv);
+ return 0;
+}
+
+static struct nxpwifi_debug_info info;
+
+/* Proc debug file read handler.
+ *
+ * This function is called when the 'debug' file is opened for reading
+ * It prints the following log information -
+ * - Interrupt count
+ * - WMM AC VO packets count
+ * - WMM AC VI packets count
+ * - WMM AC BE packets count
+ * - WMM AC BK packets count
+ * - Maximum Tx buffer size
+ * - Tx buffer size
+ * - Current Tx buffer size
+ * - Power Save mode
+ * - Power Save state
+ * - Deep Sleep status
+ * - Device wakeup required status
+ * - Number of wakeup tries
+ * - Host Sleep configured status
+ * - Host Sleep activated status
+ * - Number of Tx timeouts
+ * - Number of command timeouts
+ * - Last timed out command ID
+ * - Last timed out command action
+ * - Last command ID
+ * - Last command action
+ * - Last command index
+ * - Last command response ID
+ * - Last command response index
+ * - Last event
+ * - Last event index
+ * - Number of host to card command failures
+ * - Number of sleep confirm command failures
+ * - Number of host to card data failure
+ * - Number of deauthentication events
+ * - Number of disassociation events
+ * - Number of link lost events
+ * - Number of deauthentication commands
+ * - Number of association success commands
+ * - Number of association failure commands
+ * - Number of commands sent
+ * - Number of data packets sent
+ * - Number of command responses received
+ * - Number of events received
+ * - Tx BA stream table (TID, RA)
+ * - Rx reorder table (TID, TA, Start window, Window size, Buffer)
+ */
+static ssize_t
+nxpwifi_debug_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv =
+ (struct nxpwifi_private *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *p = (char *)page;
+ ssize_t ret;
+
+ if (!p)
+ return -ENOMEM;
+
+ ret = nxpwifi_get_debug_info(priv, &info);
+ if (ret)
+ goto free_and_exit;
+
+ p += nxpwifi_debug_info_to_buffer(priv, p, &info);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
+ (unsigned long)p - page);
+
+free_and_exit:
+ free_page(page);
+ return ret;
+}
+
+static u32 saved_reg_type, saved_reg_offset, saved_reg_value;
+
+/* Proc regrdwr file write handler.
+ *
+ * This function is called when the 'regrdwr' file is opened for writing
+ *
+ * This function can be used to write to a register.
+ */
+static ssize_t
+nxpwifi_regrdwr_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ char *buf;
+ int ret;
+ u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX;
+ int rv;
+
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ rv = sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value);
+
+ if (rv != 3) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (reg_type == 0 || reg_offset == 0) {
+ ret = -EINVAL;
+ goto done;
+ } else {
+ saved_reg_type = reg_type;
+ saved_reg_offset = reg_offset;
+ saved_reg_value = reg_value;
+ ret = count;
+ }
+done:
+ kfree(buf);
+ return ret;
+}
+
+/* Proc regrdwr file read handler.
+ *
+ * This function is called when the 'regrdwr' file is opened for reading
+ *
+ * This function can be used to read from a register.
+ */
+static ssize_t
+nxpwifi_regrdwr_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv =
+ (struct nxpwifi_private *)file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ int pos = 0, ret = 0;
+ u32 reg_value;
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (!saved_reg_type) {
+ /* No command has been given */
+ pos += snprintf(buf, PAGE_SIZE, "0");
+ goto done;
+ }
+ /* Set command has been given */
+ if (saved_reg_value != UINT_MAX) {
+ ret = nxpwifi_reg_write(priv, saved_reg_type, saved_reg_offset,
+ saved_reg_value);
+
+ pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n",
+ saved_reg_type, saved_reg_offset,
+ saved_reg_value);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+ goto done;
+ }
+ /* Get command has been given */
+ ret = nxpwifi_reg_read(priv, saved_reg_type,
+ saved_reg_offset, ®_value);
+ if (ret) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type,
+ saved_reg_offset, reg_value);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+done:
+ free_page(addr);
+ return ret;
+}
+
+/* Proc debug_mask file read handler.
+ * This function is called when the 'debug_mask' file is opened for reading
+ * This function can be used read driver debugging mask value.
+ */
+static ssize_t
+nxpwifi_debug_mask_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv =
+ (struct nxpwifi_private *)file->private_data;
+ unsigned long page = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)page;
+ size_t ret = 0;
+ int pos = 0;
+
+ if (!buf)
+ return -ENOMEM;
+
+ pos += snprintf(buf, PAGE_SIZE, "debug mask=0x%08x\n",
+ priv->adapter->debug_mask);
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+ free_page(page);
+ return ret;
+}
+
+/* Proc debug_mask file read handler.
+ * This function is called when the 'debug_mask' file is opened for reading
+ * This function can be used read driver debugging mask value.
+ */
+static ssize_t
+nxpwifi_debug_mask_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+ unsigned long debug_mask;
+ struct nxpwifi_private *priv = (void *)file->private_data;
+ char *buf;
+
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ if (kstrtoul(buf, 0, &debug_mask)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ priv->adapter->debug_mask = debug_mask;
+ ret = count;
+done:
+ kfree(buf);
+ return ret;
+}
+
+/* debugfs verext file write handler.
+ * This function is called when the 'verext' file is opened for write
+ */
+static ssize_t
+nxpwifi_verext_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ int ret;
+ u32 versionstrsel;
+ struct nxpwifi_private *priv = (void *)file->private_data;
+
+ ret = kstrtou32_from_user(ubuf, count, 10, &versionstrsel);
+ if (ret)
+ return ret;
+
+ priv->versionstrsel = versionstrsel;
+
+ return count;
+}
+
+/* Proc verext file read handler.
+ * This function is called when the 'verext' file is opened for reading
+ * This function can be used read driver exteneed verion string.
+ */
+static ssize_t
+nxpwifi_verext_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv =
+ (struct nxpwifi_private *)file->private_data;
+ char buf[256];
+ int ret;
+
+ nxpwifi_get_ver_ext(priv, priv->versionstrsel);
+ ret = snprintf(buf, sizeof(buf), "version string: %s\n",
+ priv->version_str);
+
+ return simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+}
+
+/* Proc memrw file write handler.
+ * This function is called when the 'memrw' file is opened for writing
+ * This function can be used to write to a memory location.
+ */
+static ssize_t
+nxpwifi_memrw_write(struct file *file, const char __user *ubuf, size_t count,
+ loff_t *ppos)
+{
+ int ret;
+ char cmd;
+ struct nxpwifi_ds_mem_rw mem_rw;
+ u16 cmd_action;
+ struct nxpwifi_private *priv = (void *)file->private_data;
+ char *buf;
+
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value);
+ if (ret != 3) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((cmd == 'r') || (cmd == 'R')) {
+ cmd_action = HOST_ACT_GEN_GET;
+ mem_rw.value = 0;
+ } else if ((cmd == 'w') || (cmd == 'W')) {
+ cmd_action = HOST_ACT_GEN_SET;
+ } else {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw));
+ if (nxpwifi_send_cmd(priv, HOST_CMD_MEM_ACCESS, cmd_action, 0,
+ &mem_rw, true))
+ ret = -1;
+ else
+ ret = count;
+
+done:
+ kfree(buf);
+ return ret;
+}
+
+/* Proc memrw file read handler.
+ * This function is called when the 'memrw' file is opened for reading
+ * This function can be used to read from a memory location.
+ */
+static ssize_t
+nxpwifi_memrw_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv = (void *)file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ int ret, pos = 0;
+
+ if (!buf)
+ return -ENOMEM;
+
+ pos += snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr,
+ priv->mem_rw.value);
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+ free_page(addr);
+ return ret;
+}
+
+static u32 saved_offset = -1, saved_bytes = -1;
+
+/* Proc rdeeprom file write handler.
+ *
+ * This function is called when the 'rdeeprom' file is opened for writing
+ *
+ * This function can be used to write to a RDEEPROM location.
+ */
+static ssize_t
+nxpwifi_rdeeprom_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ char *buf;
+ int ret = 0;
+ int offset = -1, bytes = -1;
+ int rv;
+
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ rv = sscanf(buf, "%d %d", &offset, &bytes);
+
+ if (rv != 2) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (offset == -1 || bytes == -1) {
+ ret = -EINVAL;
+ goto done;
+ } else {
+ saved_offset = offset;
+ saved_bytes = bytes;
+ ret = count;
+ }
+done:
+ kfree(buf);
+ return ret;
+}
+
+/* Proc rdeeprom read write handler.
+ *
+ * This function is called when the 'rdeeprom' file is opened for reading
+ *
+ * This function can be used to read from a RDEEPROM location.
+ */
+static ssize_t
+nxpwifi_rdeeprom_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv =
+ (struct nxpwifi_private *)file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ int pos, ret, i;
+ u8 value[MAX_EEPROM_DATA];
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (saved_offset == -1) {
+ /* No command has been given */
+ pos = snprintf(buf, PAGE_SIZE, "0");
+ goto done;
+ }
+
+ /* Get command has been given */
+ ret = nxpwifi_eeprom_read(priv, (u16)saved_offset,
+ (u16)saved_bytes, value);
+ if (ret) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes);
+
+ for (i = 0; i < saved_bytes; i++)
+ pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]);
+
+done:
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+out_free:
+ free_page(addr);
+ return ret;
+}
+
+/* Proc hscfg file write handler
+ * This function can be used to configure the host sleep parameters.
+ */
+static ssize_t
+nxpwifi_hscfg_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv = (void *)file->private_data;
+ char *buf;
+ int ret, arg_num;
+ struct nxpwifi_ds_hs_cfg hscfg;
+ int conditions = HS_CFG_COND_DEF;
+ u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF;
+
+ buf = memdup_user_nul(ubuf, min(count, (size_t)(PAGE_SIZE - 1)));
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap);
+
+ memset(&hscfg, 0, sizeof(struct nxpwifi_ds_hs_cfg));
+
+ if (arg_num > 3) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Too many arguments\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (arg_num >= 1 && arg_num < 3)
+ nxpwifi_set_hs_params(priv, HOST_ACT_GEN_GET,
+ NXPWIFI_SYNC_CMD, &hscfg);
+
+ if (arg_num) {
+ if (conditions == HS_CFG_CANCEL) {
+ nxpwifi_cancel_hs(priv, NXPWIFI_ASYNC_CMD);
+ ret = count;
+ goto done;
+ }
+ hscfg.conditions = conditions;
+ }
+ if (arg_num >= 2)
+ hscfg.gpio = gpio;
+ if (arg_num == 3)
+ hscfg.gap = gap;
+
+ hscfg.is_invoke_hostcmd = false;
+ nxpwifi_set_hs_params(priv, HOST_ACT_GEN_SET,
+ NXPWIFI_SYNC_CMD, &hscfg);
+
+ nxpwifi_enable_hs(priv->adapter);
+ clear_bit(NXPWIFI_IS_HS_ENABLING, &priv->adapter->work_flags);
+ ret = count;
+done:
+ kfree(buf);
+ return ret;
+}
+
+/* Proc hscfg file read handler
+ * This function can be used to read host sleep configuration
+ * parameters from driver.
+ */
+static ssize_t
+nxpwifi_hscfg_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv = (void *)file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ int pos, ret;
+ struct nxpwifi_ds_hs_cfg hscfg;
+
+ if (!buf)
+ return -ENOMEM;
+
+ nxpwifi_set_hs_params(priv, HOST_ACT_GEN_GET,
+ NXPWIFI_SYNC_CMD, &hscfg);
+
+ pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions,
+ hscfg.gpio, hscfg.gap);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+ free_page(addr);
+ return ret;
+}
+
+static ssize_t
+nxpwifi_timeshare_coex_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv = file->private_data;
+ char buf[3];
+ bool timeshare_coex;
+ int ret;
+ unsigned int len;
+
+ if (priv->adapter->fw_api_ver != NXPWIFI_FW_V15)
+ return -EOPNOTSUPP;
+
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_ROBUST_COEX,
+ HOST_ACT_GEN_GET, 0, ×hare_coex, true);
+ if (ret)
+ return ret;
+
+ len = sprintf(buf, "%d\n", timeshare_coex);
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static ssize_t
+nxpwifi_timeshare_coex_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ bool timeshare_coex;
+ struct nxpwifi_private *priv = file->private_data;
+ int ret;
+
+ if (priv->adapter->fw_api_ver != NXPWIFI_FW_V15)
+ return -EOPNOTSUPP;
+
+ ret = kstrtobool_from_user(ubuf, count, ×hare_coex);
+ if (ret)
+ return ret;
+
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_ROBUST_COEX,
+ HOST_ACT_GEN_SET, 0, ×hare_coex, true);
+ if (ret)
+ return ret;
+ else
+ return count;
+}
+
+static ssize_t
+nxpwifi_reset_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv = file->private_data;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ bool result;
+ int rc;
+
+ rc = kstrtobool_from_user(ubuf, count, &result);
+ if (rc)
+ return rc;
+
+ if (!result)
+ return -EINVAL;
+
+ if (adapter->if_ops.card_reset) {
+ dev_info(adapter->dev, "Resetting per request\n");
+ adapter->if_ops.card_reset(adapter);
+ }
+
+ return count;
+}
+
+static ssize_t
+nxpwifi_fake_radar_detect_write(struct file *file,
+ const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct nxpwifi_private *priv = file->private_data;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ bool result;
+ int rc;
+
+ rc = kstrtobool_from_user(ubuf, count, &result);
+ if (rc)
+ return rc;
+
+ if (!result)
+ return -EINVAL;
+
+ if (priv->wdev.cac_started) {
+ nxpwifi_dbg(adapter, MSG,
+ "Generate fake radar detected during CAC\n");
+ if (nxpwifi_stop_radar_detection(priv, &priv->dfs_chandef))
+ nxpwifi_dbg(adapter, ERROR,
+ "Failed to stop CAC in FW\n");
+ cancel_delayed_work_sync(&priv->dfs_cac_work);
+ cfg80211_cac_event(priv->netdev, &priv->dfs_chandef,
+ NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
+ cfg80211_radar_event(adapter->wiphy, &priv->dfs_chandef,
+ GFP_KERNEL);
+ } else {
+ if (priv->bss_chandef.chan->dfs_cac_ms) {
+ nxpwifi_dbg(adapter, MSG,
+ "Generate fake radar detected\n");
+ cfg80211_radar_event(adapter->wiphy,
+ &priv->dfs_chandef,
+ GFP_KERNEL);
+ }
+ }
+
+ return count;
+}
+
+#define NXPWIFI_DFS_ADD_FILE(name) debugfs_create_file(#name, 0644, \
+ priv->dfs_dev_dir, priv, \
+ &nxpwifi_dfs_##name##_fops)
+
+#define NXPWIFI_DFS_FILE_OPS(name) \
+static const struct file_operations nxpwifi_dfs_##name##_fops = { \
+ .read = nxpwifi_##name##_read, \
+ .write = nxpwifi_##name##_write, \
+ .open = simple_open, \
+}
+
+#define NXPWIFI_DFS_FILE_READ_OPS(name) \
+static const struct file_operations nxpwifi_dfs_##name##_fops = { \
+ .read = nxpwifi_##name##_read, \
+ .open = simple_open, \
+}
+
+#define NXPWIFI_DFS_FILE_WRITE_OPS(name) \
+static const struct file_operations nxpwifi_dfs_##name##_fops = { \
+ .write = nxpwifi_##name##_write, \
+ .open = simple_open, \
+}
+
+NXPWIFI_DFS_FILE_READ_OPS(info);
+NXPWIFI_DFS_FILE_READ_OPS(debug);
+NXPWIFI_DFS_FILE_READ_OPS(getlog);
+NXPWIFI_DFS_FILE_OPS(regrdwr);
+NXPWIFI_DFS_FILE_OPS(rdeeprom);
+NXPWIFI_DFS_FILE_OPS(memrw);
+NXPWIFI_DFS_FILE_OPS(hscfg);
+NXPWIFI_DFS_FILE_OPS(histogram);
+NXPWIFI_DFS_FILE_OPS(debug_mask);
+NXPWIFI_DFS_FILE_OPS(timeshare_coex);
+NXPWIFI_DFS_FILE_WRITE_OPS(reset);
+NXPWIFI_DFS_FILE_WRITE_OPS(fake_radar_detect);
+NXPWIFI_DFS_FILE_OPS(verext);
+
+/* This function creates the debug FS directory structure and the files.
+ */
+void
+nxpwifi_dev_debugfs_init(struct nxpwifi_private *priv)
+{
+ if (!nxpwifi_dfs_dir || !priv)
+ return;
+
+ priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name,
+ nxpwifi_dfs_dir);
+
+ NXPWIFI_DFS_ADD_FILE(info);
+ NXPWIFI_DFS_ADD_FILE(debug);
+ NXPWIFI_DFS_ADD_FILE(getlog);
+ NXPWIFI_DFS_ADD_FILE(regrdwr);
+ NXPWIFI_DFS_ADD_FILE(rdeeprom);
+
+ NXPWIFI_DFS_ADD_FILE(memrw);
+ NXPWIFI_DFS_ADD_FILE(hscfg);
+ NXPWIFI_DFS_ADD_FILE(histogram);
+ NXPWIFI_DFS_ADD_FILE(debug_mask);
+ NXPWIFI_DFS_ADD_FILE(timeshare_coex);
+ NXPWIFI_DFS_ADD_FILE(reset);
+ NXPWIFI_DFS_ADD_FILE(fake_radar_detect);
+ NXPWIFI_DFS_ADD_FILE(verext);
+}
+
+/* This function removes the debug FS directory structure and the files.
+ */
+void
+nxpwifi_dev_debugfs_remove(struct nxpwifi_private *priv)
+{
+ if (!priv)
+ return;
+
+ debugfs_remove_recursive(priv->dfs_dev_dir);
+}
+
+/* This function creates the top level proc directory.
+ */
+void
+nxpwifi_debugfs_init(void)
+{
+ if (!nxpwifi_dfs_dir)
+ nxpwifi_dfs_dir = debugfs_create_dir("nxpwifi", NULL);
+}
+
+/* This function removes the top level proc directory.
+ */
+void
+nxpwifi_debugfs_remove(void)
+{
+ debugfs_remove(nxpwifi_dfs_dir);
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 16/43] wifi: nxpwifi: add decl.h
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (14 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 15/43] wifi: nxpwifi: add debugfs.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 17/43] wifi: nxpwifi: add ethtool.c David Lin
` (28 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/decl.h | 299 ++++++++++++++++++++++++
1 file changed, 299 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/decl.h
diff --git a/drivers/net/wireless/nxp/nxpwifi/decl.h b/drivers/net/wireless/nxp/nxpwifi/decl.h
new file mode 100644
index 000000000000..1d4dee4a5f48
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/decl.h
@@ -0,0 +1,299 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NXP Wireless LAN device driver: generic data structures and APIs
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#ifndef _NXPWIFI_DECL_H_
+#define _NXPWIFI_DECL_H_
+
+#undef pr_fmt
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/wait.h>
+#include <linux/timer.h>
+#include <linux/ieee80211.h>
+#include <uapi/linux/if_arp.h>
+#include <net/cfg80211.h>
+
+#define NXPWIFI_BSS_COEX_COUNT 2
+#define NXPWIFI_MAX_BSS_NUM (2)
+
+#define NXPWIFI_MAX_CSA_COUNTERS 5
+
+#define NXPWIFI_DMA_ALIGN_SZ 64
+#define NXPWIFI_RX_HEADROOM 64
+#define MAX_TXPD_SZ 32
+#define INTF_HDR_ALIGN 4
+/* special FW 4 address management header */
+#define NXPWIFI_MIN_DATA_HEADER_LEN (NXPWIFI_DMA_ALIGN_SZ + INTF_HDR_ALIGN + \
+ MAX_TXPD_SZ)
+
+#define NXPWIFI_MGMT_FRAME_HEADER_SIZE 8 /* sizeof(pkt_type)
+ * + sizeof(tx_control)
+ */
+
+#define FRMCTL_LEN 2
+#define DURATION_LEN 2
+#define SEQCTL_LEN 2
+#define NXPWIFI_MGMT_HEADER_LEN (FRMCTL_LEN + FRMCTL_LEN + ETH_ALEN + \
+ ETH_ALEN + ETH_ALEN + SEQCTL_LEN + ETH_ALEN)
+
+#define AUTH_ALG_LEN 2
+#define AUTH_TRANSACTION_LEN 2
+#define AUTH_STATUS_LEN 2
+#define NXPWIFI_AUTH_BODY_LEN (AUTH_ALG_LEN + AUTH_TRANSACTION_LEN + \
+ AUTH_STATUS_LEN)
+
+#define HOST_MLME_AUTH_PENDING BIT(0)
+#define HOST_MLME_AUTH_DONE BIT(1)
+
+#define HOST_MLME_MGMT_MASK (BIT(IEEE80211_STYPE_AUTH >> 4) | \
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) | \
+ BIT(IEEE80211_STYPE_DISASSOC >> 4))
+
+#define AUTH_TX_DEFAULT_WAIT_TIME 2400
+
+#define WLAN_AUTH_NONE 0xFFFF
+
+#define NXPWIFI_MAX_TX_BASTREAM_SUPPORTED 2
+#define NXPWIFI_MAX_RX_BASTREAM_SUPPORTED 16
+
+#define NXPWIFI_STA_AMPDU_DEF_TXWINSIZE 64
+#define NXPWIFI_STA_AMPDU_DEF_RXWINSIZE 64
+#define NXPWIFI_STA_COEX_AMPDU_DEF_RXWINSIZE 16
+
+#define NXPWIFI_UAP_AMPDU_DEF_TXWINSIZE 32
+
+#define NXPWIFI_UAP_COEX_AMPDU_DEF_RXWINSIZE 16
+
+#define NXPWIFI_UAP_AMPDU_DEF_RXWINSIZE 16
+#define NXPWIFI_11AC_STA_AMPDU_DEF_TXWINSIZE 64
+#define NXPWIFI_11AC_STA_AMPDU_DEF_RXWINSIZE 64
+#define NXPWIFI_11AC_UAP_AMPDU_DEF_TXWINSIZE 64
+#define NXPWIFI_11AC_UAP_AMPDU_DEF_RXWINSIZE 64
+
+#define NXPWIFI_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff
+
+#define NXPWIFI_RATE_BITMAP_MCS0 32
+
+#define NXPWIFI_RX_DATA_BUF_SIZE (4 * 1024)
+#define NXPWIFI_RX_CMD_BUF_SIZE (2 * 1024)
+
+#define MAX_BEACON_PERIOD (4000)
+#define MIN_BEACON_PERIOD (50)
+#define MAX_DTIM_PERIOD (100)
+#define MIN_DTIM_PERIOD (1)
+
+#define NXPWIFI_RTS_MIN_VALUE (0)
+#define NXPWIFI_RTS_MAX_VALUE (2347)
+#define NXPWIFI_FRAG_MIN_VALUE (256)
+#define NXPWIFI_FRAG_MAX_VALUE (2346)
+#define NXPWIFI_WMM_VERSION 0x01
+#define NXPWIFI_WMM_SUBTYPE 0x01
+
+#define NXPWIFI_RETRY_LIMIT 14
+#define NXPWIFI_SDIO_BLOCK_SIZE 256
+
+#define NXPWIFI_BUF_FLAG_REQUEUED_PKT BIT(0)
+#define NXPWIFI_BUF_FLAG_BRIDGED_PKT BIT(1)
+#define NXPWIFI_BUF_FLAG_EAPOL_TX_STATUS BIT(3)
+#define NXPWIFI_BUF_FLAG_ACTION_TX_STATUS BIT(4)
+#define NXPWIFI_BUF_FLAG_AGGR_PKT BIT(5)
+
+#define NXPWIFI_BRIDGED_PKTS_THR_HIGH 1024
+#define NXPWIFI_BRIDGED_PKTS_THR_LOW 128
+
+/* 54M rates, index from 0 to 11 */
+#define NXPWIFI_RATE_INDEX_MCS0 12
+/* 12-27=MCS0-15(BW20) */
+#define NXPWIFI_BW20_MCS_NUM 15
+
+/* Rate index for OFDM 0 */
+#define NXPWIFI_RATE_INDEX_OFDM0 4
+
+#define NXPWIFI_MAX_STA_NUM 3
+#define NXPWIFI_MAX_UAP_NUM 3
+
+#define NXPWIFI_A_BAND_START_FREQ 5000
+
+/* SDIO Aggr data packet special info */
+#define SDIO_MAX_AGGR_BUF_SIZE (256 * 255)
+#define BLOCK_NUMBER_OFFSET 15
+#define SDIO_HEADER_OFFSET 28
+
+#define NXPWIFI_SIZE_4K 0x4000
+
+enum nxpwifi_bss_type {
+ NXPWIFI_BSS_TYPE_STA = 0,
+ NXPWIFI_BSS_TYPE_UAP = 1,
+ NXPWIFI_BSS_TYPE_ANY = 0xff,
+};
+
+enum nxpwifi_bss_role {
+ NXPWIFI_BSS_ROLE_STA = 0,
+ NXPWIFI_BSS_ROLE_UAP = 1,
+ NXPWIFI_BSS_ROLE_ANY = 0xff,
+};
+
+#define BSS_ROLE_BIT_MASK BIT(0)
+
+#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK)
+
+enum nxpwifi_data_frame_type {
+ NXPWIFI_DATA_FRAME_TYPE_ETH_II = 0,
+ NXPWIFI_DATA_FRAME_TYPE_802_11,
+};
+
+struct nxpwifi_fw_image {
+ u8 *helper_buf;
+ u32 helper_len;
+ u8 *fw_buf;
+ u32 fw_len;
+};
+
+struct nxpwifi_802_11_ssid {
+ u32 ssid_len;
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+};
+
+struct nxpwifi_wait_queue {
+ wait_queue_head_t wait;
+ int status;
+};
+
+struct nxpwifi_rxinfo {
+ struct sk_buff *parent;
+ u8 bss_num;
+ u8 bss_type;
+ u8 use_count;
+ u8 buf_type;
+};
+
+struct nxpwifi_txinfo {
+ u8 flags;
+ u8 bss_num;
+ u8 bss_type;
+ u8 aggr_num;
+ u32 pkt_len;
+ u8 ack_frame_id;
+ u64 cookie;
+};
+
+enum nxpwifi_wmm_ac_e {
+ WMM_AC_BK,
+ WMM_AC_BE,
+ WMM_AC_VI,
+ WMM_AC_VO
+} __packed;
+
+struct ieee_types_wmm_ac_parameters {
+ u8 aci_aifsn_bitmap;
+ u8 ecw_bitmap;
+ __le16 tx_op_limit;
+} __packed;
+
+struct nxpwifi_types_wmm_info {
+ u8 oui[4];
+ u8 subtype;
+ u8 version;
+ u8 qos_info;
+ u8 reserved;
+ struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS];
+} __packed;
+
+struct nxpwifi_arp_eth_header {
+ struct arphdr hdr;
+ u8 ar_sha[ETH_ALEN];
+ u8 ar_sip[4];
+ u8 ar_tha[ETH_ALEN];
+ u8 ar_tip[4];
+} __packed;
+
+struct nxpwifi_chan_stats {
+ u8 chan_num;
+ u8 bandcfg;
+ u8 flags;
+ s8 noise;
+ u16 total_bss;
+ u16 cca_scan_dur;
+ u16 cca_busy_dur;
+} __packed;
+
+#define NXPWIFI_HIST_MAX_SAMPLES 1048576
+#define NXPWIFI_MAX_RX_RATES 44
+#define NXPWIFI_MAX_AC_RX_RATES 74
+#define NXPWIFI_MAX_SNR 256
+#define NXPWIFI_MAX_NOISE_FLR 256
+#define NXPWIFI_MAX_SIG_STRENGTH 256
+
+struct nxpwifi_histogram_data {
+ atomic_t rx_rate[NXPWIFI_MAX_AC_RX_RATES];
+ atomic_t snr[NXPWIFI_MAX_SNR];
+ atomic_t noise_flr[NXPWIFI_MAX_NOISE_FLR];
+ atomic_t sig_str[NXPWIFI_MAX_SIG_STRENGTH];
+ atomic_t num_samples;
+};
+
+struct nxpwifi_iface_comb {
+ u8 sta_intf;
+ u8 uap_intf;
+};
+
+struct nxpwifi_radar_params {
+ struct cfg80211_chan_def *chandef;
+ u32 cac_time_ms;
+} __packed;
+
+struct nxpwifi_11h_intf_state {
+ bool is_11h_enabled;
+ bool is_11h_active;
+} __packed;
+
+#define NXPWIFI_FW_DUMP_IDX 0xff
+#define NXPWIFI_FW_DUMP_MAX_MEMSIZE 0x160000
+#define NXPWIFI_DRV_INFO_IDX 20
+#define FW_DUMP_MAX_NAME_LEN 8
+#define FW_DUMP_HOST_READY 0xEE
+#define FW_DUMP_DONE 0xFF
+#define FW_DUMP_READ_DONE 0xFE
+
+struct memory_type_mapping {
+ u8 mem_name[FW_DUMP_MAX_NAME_LEN];
+ u8 *mem_ptr;
+ u32 mem_size;
+ u8 done_flag;
+};
+
+enum rdwr_status {
+ RDWR_STATUS_SUCCESS = 0,
+ RDWR_STATUS_FAILURE = 1,
+ RDWR_STATUS_DONE = 2
+};
+
+enum nxpwifi_chan_band {
+ BAND_2GHZ = 0,
+ BAND_5GHZ,
+ BAND_6GHZ,
+ BAND_4GHZ,
+};
+
+enum nxpwifi_chan_width {
+ CHAN_BW_20MHZ = 0,
+ CHAN_BW_10MHZ,
+ CHAN_BW_40MHZ,
+ CHAN_BW_80MHZ,
+ CHAN_BW_8080MHZ,
+ CHAN_BW_160MHZ,
+ CHAN_BW_5MHZ,
+};
+
+enum nxpwifi_chan_offset {
+ SEC_CHAN_NONE = 0,
+ SEC_CHAN_ABOVE = 1,
+ SEC_CHAN_5MHZ = 2,
+ SEC_CHAN_BELOW = 3
+};
+
+#endif /* !_NXPWIFI_DECL_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 17/43] wifi: nxpwifi: add ethtool.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (15 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 16/43] wifi: nxpwifi: add decl.h David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 18/43] wifi: nxpwifi: add fw.h David Lin
` (27 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/ethtool.c | 58 ++++++++++++++++++++++
1 file changed, 58 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/ethtool.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/ethtool.c b/drivers/net/wireless/nxp/nxpwifi/ethtool.c
new file mode 100644
index 000000000000..6cefa6e6f5b3
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/ethtool.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: ethtool
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "main.h"
+
+static void nxpwifi_ethtool_get_wol(struct net_device *dev,
+ struct ethtool_wolinfo *wol)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ u32 conditions = le32_to_cpu(priv->adapter->hs_cfg.conditions);
+
+ wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_PHY;
+
+ if (conditions == HS_CFG_COND_DEF)
+ return;
+
+ if (conditions & HS_CFG_COND_UNICAST_DATA)
+ wol->wolopts |= WAKE_UCAST;
+ if (conditions & HS_CFG_COND_MULTICAST_DATA)
+ wol->wolopts |= WAKE_MCAST;
+ if (conditions & HS_CFG_COND_BROADCAST_DATA)
+ wol->wolopts |= WAKE_BCAST;
+ if (conditions & HS_CFG_COND_MAC_EVENT)
+ wol->wolopts |= WAKE_PHY;
+}
+
+static int nxpwifi_ethtool_set_wol(struct net_device *dev,
+ struct ethtool_wolinfo *wol)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ u32 conditions = 0;
+
+ if (wol->wolopts & ~(WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_PHY))
+ return -EOPNOTSUPP;
+
+ if (wol->wolopts & WAKE_UCAST)
+ conditions |= HS_CFG_COND_UNICAST_DATA;
+ if (wol->wolopts & WAKE_MCAST)
+ conditions |= HS_CFG_COND_MULTICAST_DATA;
+ if (wol->wolopts & WAKE_BCAST)
+ conditions |= HS_CFG_COND_BROADCAST_DATA;
+ if (wol->wolopts & WAKE_PHY)
+ conditions |= HS_CFG_COND_MAC_EVENT;
+ if (wol->wolopts == 0)
+ conditions |= HS_CFG_COND_DEF;
+ priv->adapter->hs_cfg.conditions = cpu_to_le32(conditions);
+
+ return 0;
+}
+
+const struct ethtool_ops nxpwifi_ethtool_ops = {
+ .get_wol = nxpwifi_ethtool_get_wol,
+ .set_wol = nxpwifi_ethtool_set_wol,
+};
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 18/43] wifi: nxpwifi: add fw.h
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (16 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 17/43] wifi: nxpwifi: add ethtool.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 19/43] wifi: nxpwifi: add ie.c David Lin
` (26 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/fw.h | 2262 +++++++++++++++++++++++++
1 file changed, 2262 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/fw.h
diff --git a/drivers/net/wireless/nxp/nxpwifi/fw.h b/drivers/net/wireless/nxp/nxpwifi/fw.h
new file mode 100644
index 000000000000..e38ca7d99d17
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/fw.h
@@ -0,0 +1,2262 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NXP Wireless LAN device driver: Firmware specific macros & structures
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#ifndef _NXPWIFI_FW_H_
+#define _NXPWIFI_FW_H_
+
+#include <linux/if_ether.h>
+
+#define INTF_HEADER_LEN 4
+
+struct rfc_1042_hdr {
+ u8 llc_dsap;
+ u8 llc_ssap;
+ u8 llc_ctrl;
+ u8 snap_oui[3];
+ __be16 snap_type;
+} __packed;
+
+struct rx_packet_hdr {
+ struct ethhdr eth803_hdr;
+ struct rfc_1042_hdr rfc1042_hdr;
+} __packed;
+
+struct tx_packet_hdr {
+ struct ethhdr eth803_hdr;
+ struct rfc_1042_hdr rfc1042_hdr;
+} __packed;
+
+struct nxpwifi_fw_header {
+ __le32 dnld_cmd;
+ __le32 base_addr;
+ __le32 data_length;
+ __le32 crc;
+} __packed;
+
+struct nxpwifi_fw_data {
+ struct nxpwifi_fw_header header;
+ __le32 seq_num;
+ u8 data[];
+} __packed;
+
+struct nxpwifi_fw_dump_header {
+ __le16 seq_num;
+ __le16 reserved;
+ __le16 type;
+ __le16 len;
+} __packed;
+
+#define FW_DUMP_INFO_ENDED 0x0002
+
+#define NXPWIFI_FW_DNLD_CMD_1 0x1
+#define NXPWIFI_FW_DNLD_CMD_5 0x5
+#define NXPWIFI_FW_DNLD_CMD_6 0x6
+#define NXPWIFI_FW_DNLD_CMD_7 0x7
+
+#define B_SUPPORTED_RATES 5
+#define G_SUPPORTED_RATES 9
+#define BG_SUPPORTED_RATES 13
+#define A_SUPPORTED_RATES 9
+#define HOSTCMD_SUPPORTED_RATES 14
+#define N_SUPPORTED_RATES 3
+#define ALL_802_11_BANDS (BAND_A | BAND_B | BAND_G | BAND_GN | \
+ BAND_AN | BAND_AAC)
+
+#define FW_MULTI_BANDS_SUPPORT (BIT(8) | BIT(9) | BIT(10) | BIT(11) | \
+ BIT(13))
+#define IS_SUPPORT_MULTI_BANDS(adapter) \
+ ((adapter)->fw_cap_info & FW_MULTI_BANDS_SUPPORT)
+
+/* bit 13: 11ac BAND_AAC
+ * bit 12: reserved for lab testing, will be reused for BAND_AN
+ * bit 11: 11n BAND_GN
+ * bit 10: 11a BAND_A
+ * bit 9: 11g BAND_G
+ * bit 8: 11b BAND_B
+ * Map these bits to band capability by right shifting 8 bits.
+ */
+#define GET_FW_DEFAULT_BANDS(adapter) \
+ ((((adapter)->fw_cap_info & 0x2f00) >> 8) & \
+ ALL_802_11_BANDS)
+
+#define HOST_WEP_KEY_INDEX_MASK 0x3fff
+
+#define KEY_INFO_ENABLED 0x01
+enum KEY_TYPE_ID {
+ KEY_TYPE_ID_WEP = 0,
+ KEY_TYPE_ID_TKIP,
+ KEY_TYPE_ID_AES,
+ KEY_TYPE_ID_WAPI,
+ KEY_TYPE_ID_AES_CMAC,
+ KEY_TYPE_ID_AES_CMAC_DEF,
+};
+
+#define WPA_PN_SIZE 8
+#define KEY_PARAMS_FIXED_LEN 10
+#define KEY_INDEX_MASK 0xf
+#define KEY_API_VER_MAJOR_V2 2
+
+#define KEY_MCAST BIT(0)
+#define KEY_UNICAST BIT(1)
+#define KEY_ENABLED BIT(2)
+#define KEY_DEFAULT BIT(3)
+#define KEY_TX_KEY BIT(4)
+#define KEY_RX_KEY BIT(5)
+#define KEY_IGTK BIT(10)
+
+#define MAX_POLL_TRIES 10000
+#define MAX_FIRMWARE_POLL_TRIES 300
+
+#define FIRMWARE_READY_SDIO 0xfedc
+#define FIRMWARE_READY_PCIE 0xfedcba00
+
+#define NXPWIFI_COEX_MODE_TIMESHARE 0x01
+#define NXPWIFI_COEX_MODE_SPATIAL 0x82
+
+enum nxpwifi_usb_ep {
+ NXPWIFI_USB_EP_CMD_EVENT = 1,
+ NXPWIFI_USB_EP_DATA = 2,
+ NXPWIFI_USB_EP_DATA_CH2 = 3,
+};
+
+enum NXPWIFI_802_11_PRIVACY_FILTER {
+ NXPWIFI_802_11_PRIV_FILTER_ACCEPT_ALL,
+ NXPWIFI_802_11_PRIV_FILTER_8021X_WEP
+};
+
+#define CAL_SNR(RSSI, NF) ((s16)((s16)(RSSI) - (s16)(NF)))
+#define CAL_RSSI(SNR, NF) ((s16)((s16)(SNR) + (s16)(NF)))
+
+#define UAP_BSS_PARAMS_I 0
+#define UAP_CUSTOM_IE_I 1
+#define NXPWIFI_AUTO_IDX_MASK 0xffff
+#define NXPWIFI_DELETE_MASK 0x0000
+#define MGMT_MASK_ASSOC_REQ 0x01
+#define MGMT_MASK_REASSOC_REQ 0x04
+#define MGMT_MASK_ASSOC_RESP 0x02
+#define MGMT_MASK_REASSOC_RESP 0x08
+#define MGMT_MASK_PROBE_REQ 0x10
+#define MGMT_MASK_PROBE_RESP 0x20
+#define MGMT_MASK_BEACON 0x100
+
+#define TLV_TYPE_UAP_SSID 0x0000
+#define TLV_TYPE_UAP_RATES 0x0001
+#define TLV_TYPE_PWR_CONSTRAINT 0x0020
+
+#define PROPRIETARY_TLV_BASE_ID 0x0100
+#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0)
+#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1)
+#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2)
+#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4)
+#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10)
+#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16)
+#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18)
+#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19)
+#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22)
+#define TLV_TYPE_BGSCAN_START_LATER (PROPRIETARY_TLV_BASE_ID + 30)
+#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31)
+#define TLV_TYPE_STA_MAC_ADDR (PROPRIETARY_TLV_BASE_ID + 32)
+#define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 35)
+#define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42)
+#define TLV_TYPE_UAP_MAC_ADDRESS (PROPRIETARY_TLV_BASE_ID + 43)
+#define TLV_TYPE_UAP_BEACON_PERIOD (PROPRIETARY_TLV_BASE_ID + 44)
+#define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45)
+#define TLV_TYPE_UAP_BCAST_SSID (PROPRIETARY_TLV_BASE_ID + 48)
+#define TLV_TYPE_UAP_PREAMBLE_CTL (PROPRIETARY_TLV_BASE_ID + 49)
+#define TLV_TYPE_UAP_RTS_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 51)
+#define TLV_TYPE_UAP_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 57)
+#define TLV_TYPE_UAP_WEP_KEY (PROPRIETARY_TLV_BASE_ID + 59)
+#define TLV_TYPE_UAP_WPA_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 60)
+#define TLV_TYPE_UAP_ENCRY_PROTOCOL (PROPRIETARY_TLV_BASE_ID + 64)
+#define TLV_TYPE_UAP_AKMP (PROPRIETARY_TLV_BASE_ID + 65)
+#define TLV_TYPE_UAP_FRAG_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 70)
+#define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82)
+#define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83)
+#define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84)
+#define TLV_TYPE_BSS_SCAN_RSP (PROPRIETARY_TLV_BASE_ID + 86)
+#define TLV_TYPE_BSS_SCAN_INFO (PROPRIETARY_TLV_BASE_ID + 87)
+#define TLV_TYPE_CHANRPT_11H_BASIC (PROPRIETARY_TLV_BASE_ID + 91)
+#define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 93)
+#define TLV_TYPE_ROBUST_COEX (PROPRIETARY_TLV_BASE_ID + 96)
+#define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 104)
+#define TLV_TYPE_MGMT_IE (PROPRIETARY_TLV_BASE_ID + 105)
+#define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 113)
+#define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 114)
+#define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123)
+#define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145)
+#define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146)
+#define TLV_TYPE_TX_PAUSE (PROPRIETARY_TLV_BASE_ID + 148)
+#define TLV_TYPE_RXBA_SYNC (PROPRIETARY_TLV_BASE_ID + 153)
+#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154)
+#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156)
+#define TLV_TYPE_REPEAT_COUNT (PROPRIETARY_TLV_BASE_ID + 176)
+#define TLV_TYPE_PS_PARAMS_IN_HS (PROPRIETARY_TLV_BASE_ID + 181)
+#define TLV_TYPE_MULTI_CHAN_INFO (PROPRIETARY_TLV_BASE_ID + 183)
+#define TLV_TYPE_MC_GROUP_INFO (PROPRIETARY_TLV_BASE_ID + 184)
+#define TLV_TYPE_SCAN_CHANNEL_GAP (PROPRIETARY_TLV_BASE_ID + 197)
+#define TLV_TYPE_API_REV (PROPRIETARY_TLV_BASE_ID + 199)
+#define TLV_TYPE_CHANNEL_STATS (PROPRIETARY_TLV_BASE_ID + 198)
+#define TLV_BTCOEX_WL_AGGR_WINSIZE (PROPRIETARY_TLV_BASE_ID + 202)
+#define TLV_BTCOEX_WL_SCANTIME (PROPRIETARY_TLV_BASE_ID + 203)
+#define TLV_TYPE_BSS_MODE (PROPRIETARY_TLV_BASE_ID + 206)
+#define TLV_TYPE_RANDOM_MAC (PROPRIETARY_TLV_BASE_ID + 236)
+#define TLV_TYPE_CHAN_ATTR_CFG (PROPRIETARY_TLV_BASE_ID + 237)
+#define TLV_TYPE_MAX_CONN (PROPRIETARY_TLV_BASE_ID + 279)
+#define TLV_TYPE_HOST_MLME (PROPRIETARY_TLV_BASE_ID + 307)
+#define TLV_TYPE_UAP_STA_FLAGS (PROPRIETARY_TLV_BASE_ID + 313)
+#define TLV_TYPE_SAE_PWE_MODE (PROPRIETARY_TLV_BASE_ID + 339)
+
+#define NXPWIFI_TX_DATA_BUF_SIZE_2K 2048
+
+#define SSN_MASK 0xfff0
+
+#define BA_RESULT_SUCCESS 0x0
+#define BA_RESULT_TIMEOUT 0x2
+
+#define IS_BASTREAM_SETUP(ptr) ((ptr)->ba_status)
+
+#define BA_STREAM_NOT_ALLOWED 0xff
+
+#define IS_11N_ENABLED(priv) ({ \
+ typeof(priv) (_priv) = priv; \
+ (((_priv)->adapter->config_bands & BAND_GN || \
+ (_priv)->adapter->config_bands & BAND_AN) && \
+ (_priv)->curr_bss_params.bss_descriptor.bcn_ht_cap && \
+ !(_priv)->curr_bss_params.bss_descriptor.disable_11n); \
+ })
+#define INITIATOR_BIT(del_ba_param_set) (((del_ba_param_set) &\
+ BIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS)
+
+#define NXPWIFI_TX_DATA_BUF_SIZE_4K 4096
+#define NXPWIFI_TX_DATA_BUF_SIZE_8K 8192
+#define NXPWIFI_TX_DATA_BUF_SIZE_12K 12288
+
+#define ISSUPP_11NENABLED(fw_cap_info) ((fw_cap_info) & BIT(11))
+#define ISSUPP_DRCS_ENABLED(fw_cap_info) ((fw_cap_info) & BIT(15))
+#define ISSUPP_SDIO_SPA_ENABLED(fw_cap_info) ((fw_cap_info) & BIT(16))
+#define ISSUPP_RANDOM_MAC(fw_cap_info) ((fw_cap_info) & BIT(27))
+#define ISSUPP_FIRMWARE_SUPPLICANT(fw_cap_info) ((fw_cap_info) & BIT(21))
+
+#define NXPWIFI_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \
+ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \
+ IEEE80211_HT_CAP_SM_PS)
+
+#define NXPWIFI_DEF_11N_TX_BF_CAP 0x09E1E008
+
+#define NXPWIFI_DEF_AMPDU IEEE80211_HT_AMPDU_PARM_FACTOR
+
+/* dev_cap bitmap
+ * BIT
+ * 0-16 reserved
+ * 17 IEEE80211_HT_CAP_SUP_WIDTH_20_40
+ * 18-22 reserved
+ * 23 IEEE80211_HT_CAP_SGI_20
+ * 24 IEEE80211_HT_CAP_SGI_40
+ * 25 IEEE80211_HT_CAP_TX_STBC
+ * 26 IEEE80211_HT_CAP_RX_STBC
+ * 27-28 reserved
+ * 29 IEEE80211_HT_CAP_GRN_FLD
+ * 30-31 reserved
+ */
+#define ISSUPP_CHANWIDTH40(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(17))
+#define ISSUPP_SHORTGI20(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(23))
+#define ISSUPP_SHORTGI40(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(24))
+#define ISSUPP_TXSTBC(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(25))
+#define ISSUPP_RXSTBC(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(26))
+#define ISSUPP_GREENFIELD(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(29))
+#define ISENABLED_40MHZ_INTOLERANT(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(8))
+#define ISSUPP_RXLDPC(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(22))
+#define ISSUPP_BEAMFORMING(dot_11n_dev_cap) ((dot_11n_dev_cap) & BIT(30))
+#define ISALLOWED_CHANWIDTH40(ht_param) ((ht_param) & BIT(2))
+#define GETSUPP_TXBASTREAMS(dot_11n_dev_cap) (((dot_11n_dev_cap) >> 18) & 0xF)
+
+/* httxcfg bitmap
+ * 0 reserved
+ * 1 20/40 Mhz enable(1)/disable(0)
+ * 2-3 reserved
+ * 4 green field enable(1)/disable(0)
+ * 5 short GI in 20 Mhz enable(1)/disable(0)
+ * 6 short GI in 40 Mhz enable(1)/disable(0)
+ * 7-15 reserved
+ */
+#define NXPWIFI_FW_DEF_HTTXCFG (BIT(1) | BIT(4) | BIT(5) | BIT(6))
+
+/* 11AC Tx and Rx MCS map for 1x1 mode:
+ * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1
+ * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 7 streams
+ */
+#define NXPWIFI_11AC_MCS_MAP_1X1 0xfffefffe
+
+/* 11AC Tx and Rx MCS map for 2x2 mode:
+ * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 and 2
+ * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 6 streams
+ */
+#define NXPWIFI_11AC_MCS_MAP_2X2 0xfffafffa
+
+#define GET_RXMCSSUPP(dev_mcs_supported) ((dev_mcs_supported) & 0x0f)
+#define SETHT_MCS32(x) (x[4] |= 1)
+#define HT_STREAM_1X1 0x11
+#define HT_STREAM_2X2 0x22
+
+#define SET_SECONDARYCHAN(radio_type, sec_chan) \
+ ((radio_type) |= ((sec_chan) << 4))
+
+#define LLC_SNAP_LEN 8
+
+/* HW_SPEC fw_cap_info */
+
+#define ISSUPP_11ACENABLED(fw_cap_info) ((fw_cap_info) & BIT(13))
+
+#define GET_VHTCAP_CHWDSET(vht_cap_info) (((vht_cap_info) >> 2) & 0x3)
+#define GET_VHTNSSMCS(mcs_mapset, nss) \
+ (((mcs_mapset) >> (2 * ((nss) - 1))) & 0x3)
+#define SET_VHTNSSMCS(mcs_mapset, nss, value) \
+ ((mcs_mapset) |= ((value) & 0x3) << (2 * ((nss) - 1)))
+#define GET_DEVTXMCSMAP(dev_mcs_map) ((dev_mcs_map) >> 16)
+#define GET_DEVRXMCSMAP(dev_mcs_map) ((dev_mcs_map) & 0xFFFF)
+
+/* Clear SU Beanformer, MU beanformer, MU beanformee and
+ * sounding dimensions bits
+ */
+#define NXPWIFI_DEF_11AC_CAP_BF_RESET_MASK \
+ (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | \
+ IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | \
+ IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | \
+ IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK)
+
+#define MOD_CLASS_HR_DSSS 0x03
+#define MOD_CLASS_OFDM 0x07
+#define MOD_CLASS_HT 0x08
+#define HT_BW_20 0
+#define HT_BW_40 1
+
+#define DFS_CHAN_MOVE_TIME 10000
+
+#define HOST_CMD_GET_HW_SPEC 0x0003
+#define HOST_CMD_802_11_SCAN 0x0006
+#define HOST_CMD_802_11_GET_LOG 0x000b
+#define HOST_CMD_MAC_MULTICAST_ADR 0x0010
+#define HOST_CMD_802_11_ASSOCIATE 0x0012
+#define HOST_CMD_802_11_SNMP_MIB 0x0016
+#define HOST_CMD_MAC_REG_ACCESS 0x0019
+#define HOST_CMD_BBP_REG_ACCESS 0x001a
+#define HOST_CMD_RF_REG_ACCESS 0x001b
+#define HOST_CMD_RF_TX_PWR 0x001e
+#define HOST_CMD_RF_ANTENNA 0x0020
+#define HOST_CMD_802_11_DEAUTHENTICATE 0x0024
+#define HOST_CMD_MAC_CONTROL 0x0028
+#define HOST_CMD_802_11_MAC_ADDRESS 0x004D
+#define HOST_CMD_802_11_EEPROM_ACCESS 0x0059
+#define HOST_CMD_802_11D_DOMAIN_INFO 0x005b
+#define HOST_CMD_802_11_KEY_MATERIAL 0x005e
+#define HOST_CMD_802_11_BG_SCAN_CONFIG 0x006b
+#define HOST_CMD_802_11_BG_SCAN_QUERY 0x006c
+#define HOST_CMD_WMM_GET_STATUS 0x0071
+#define HOST_CMD_802_11_SUBSCRIBE_EVENT 0x0075
+#define HOST_CMD_802_11_TX_RATE_QUERY 0x007f
+#define HOST_CMD_MEM_ACCESS 0x0086
+#define HOST_CMD_CFG_DATA 0x008f
+#define HOST_CMD_VERSION_EXT 0x0097
+#define HOST_CMD_MEF_CFG 0x009a
+#define HOST_CMD_RSSI_INFO 0x00a4
+#define HOST_CMD_FUNC_INIT 0x00a9
+#define HOST_CMD_FUNC_SHUTDOWN 0x00aa
+#define HOST_CMD_PMIC_REG_ACCESS 0x00ad
+#define HOST_CMD_APCMD_SYS_RESET 0x00af
+#define HOST_CMD_UAP_SYS_CONFIG 0x00b0
+#define HOST_CMD_UAP_BSS_START 0x00b1
+#define HOST_CMD_UAP_BSS_STOP 0x00b2
+#define HOST_CMD_APCMD_STA_LIST 0x00b3
+#define HOST_CMD_UAP_STA_DEAUTH 0x00b5
+#define HOST_CMD_11N_CFG 0x00cd
+#define HOST_CMD_11N_ADDBA_REQ 0x00ce
+#define HOST_CMD_11N_ADDBA_RSP 0x00cf
+#define HOST_CMD_11N_DELBA 0x00d0
+#define HOST_CMD_TXPWR_CFG 0x00d1
+#define HOST_CMD_TX_RATE_CFG 0x00d6
+#define HOST_CMD_RECONFIGURE_TX_BUFF 0x00d9
+#define HOST_CMD_CHAN_REPORT_REQUEST 0x00dd
+#define HOST_CMD_AMSDU_AGGR_CTRL 0x00df
+#define HOST_CMD_ROBUST_COEX 0x00e0
+#define HOST_CMD_802_11_PS_MODE_ENH 0x00e4
+#define HOST_CMD_802_11_HS_CFG_ENH 0x00e5
+#define HOST_CMD_CAU_REG_ACCESS 0x00ed
+#define HOST_CMD_SET_BSS_MODE 0x00f7
+#define HOST_CMD_PCIE_DESC_DETAILS 0x00fa
+#define HOST_CMD_802_11_SCAN_EXT 0x0107
+#define HOST_CMD_COALESCE_CFG 0x010a
+#define HOST_CMD_MGMT_FRAME_REG 0x010c
+#define HOST_CMD_REMAIN_ON_CHAN 0x010d
+#define HOST_CMD_GTK_REKEY_OFFLOAD_CFG 0x010f
+#define HOST_CMD_11AC_CFG 0x0112
+#define HOST_CMD_HS_WAKEUP_REASON 0x0116
+#define HOST_CMD_MC_POLICY 0x0121
+#define HOST_CMD_FW_DUMP_EVENT 0x0125
+#define HOST_CMD_SDIO_SP_RX_AGGR_CFG 0x0223
+#define HOST_CMD_STA_CONFIGURE 0x023f
+#define HOST_CMD_CHAN_REGION_CFG 0x0242
+#define HOST_CMD_PACKET_AGGR_CTRL 0x0251
+#define HOST_CMD_ADD_NEW_STATION 0x025f
+
+#define PROTOCOL_NO_SECURITY 0x01
+#define PROTOCOL_STATIC_WEP 0x02
+#define PROTOCOL_WPA 0x08
+#define PROTOCOL_WPA2 0x20
+#define PROTOCOL_WPA2_MIXED 0x28
+#define PROTOCOL_EAP 0x40
+#define KEY_MGMT_EAP 0x01
+#define KEY_MGMT_PSK 0x02
+#define KEY_MGMT_NONE 0x04
+#define KEY_MGMT_OWE 0x200
+#define KEY_MGMT_SAE 0x400
+#define CIPHER_TKIP 0x04
+#define CIPHER_AES_CCMP 0x08
+#define VALID_CIPHER_BITMAP 0x0c
+
+enum ENH_PS_MODES {
+ EN_PS = 1,
+ DIS_PS = 2,
+ EN_AUTO_DS = 3,
+ DIS_AUTO_DS = 4,
+ SLEEP_CONFIRM = 5,
+ GET_PS = 0,
+ EN_AUTO_PS = 0xff,
+ DIS_AUTO_PS = 0xfe,
+};
+
+enum nxpwifi_channel_flags {
+ NXPWIFI_CHANNEL_PASSIVE = BIT(0),
+ NXPWIFI_CHANNEL_DFS = BIT(1),
+ NXPWIFI_CHANNEL_NOHT40 = BIT(2),
+ NXPWIFI_CHANNEL_NOHT80 = BIT(3),
+ NXPWIFI_CHANNEL_DISABLED = BIT(7),
+};
+
+#define HOST_RET_BIT 0x8000
+#define HOST_ACT_GEN_GET 0x0000
+#define HOST_ACT_GEN_SET 0x0001
+#define HOST_ACT_GEN_REMOVE 0x0004
+#define HOST_ACT_BITWISE_SET 0x0002
+#define HOST_ACT_BITWISE_CLR 0x0003
+#define HOST_RESULT_OK 0x0000
+#define HOST_ACT_MAC_RX_ON BIT(0)
+#define HOST_ACT_MAC_TX_ON BIT(1)
+#define HOST_ACT_MAC_WEP_ENABLE BIT(3)
+#define HOST_ACT_MAC_ETHERNETII_ENABLE BIT(4)
+#define HOST_ACT_MAC_PROMISCUOUS_ENABLE BIT(7)
+#define HOST_ACT_MAC_ALL_MULTICAST_ENABLE BIT(8)
+#define HOST_ACT_MAC_DYNAMIC_BW_ENABLE BIT(16)
+
+#define HOST_BSS_MODE_IBSS 0x0002
+#define HOST_BSS_MODE_ANY 0x0003
+
+#define HOST_SCAN_RADIO_TYPE_BG 0
+#define HOST_SCAN_RADIO_TYPE_A 1
+
+#define HS_CFG_CANCEL 0xffffffff
+#define HS_CFG_COND_DEF 0x00000000
+#define HS_CFG_GPIO_DEF 0xff
+#define HS_CFG_GAP_DEF 0xff
+#define HS_CFG_COND_BROADCAST_DATA 0x00000001
+#define HS_CFG_COND_UNICAST_DATA 0x00000002
+#define HS_CFG_COND_MAC_EVENT 0x00000004
+#define HS_CFG_COND_MULTICAST_DATA 0x00000008
+
+#define CONNECT_ERR_AUTH_ERR_STA_FAILURE 0xFFFB
+#define CONNECT_ERR_ASSOC_ERR_TIMEOUT 0xFFFC
+#define CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED 0xFFFD
+#define CONNECT_ERR_AUTH_MSG_UNHANDLED 0xFFFE
+#define CONNECT_ERR_STA_FAILURE 0xFFFF
+
+#define CMD_F_HOSTCMD BIT(0)
+
+#define HOST_CMD_ID_MASK 0x0fff
+
+#define HOST_SEQ_NUM_MASK 0x00ff
+
+#define HOST_BSS_NUM_MASK 0x0f00
+
+#define HOST_BSS_TYPE_MASK 0xf000
+
+#define HOST_ACT_SET_RX 0x0001
+#define HOST_ACT_SET_TX 0x0002
+#define HOST_ACT_SET_BOTH 0x0003
+#define HOST_ACT_GET_RX 0x0004
+#define HOST_ACT_GET_TX 0x0008
+#define HOST_ACT_GET_BOTH 0x000c
+
+#define HOST_ACT_REMOVE_STA 0x0
+#define HOST_ACT_ADD_STA 0x1
+
+#define RF_ANTENNA_AUTO 0xFFFF
+
+#define HOST_SET_SEQ_NO_BSS_INFO(seq, num, type) \
+ ((((seq) & 0x00ff) | \
+ (((num) & 0x000f) << 8)) | \
+ (((type) & 0x000f) << 12))
+
+#define HOST_GET_SEQ_NO(seq) \
+ ((seq) & HOST_SEQ_NUM_MASK)
+
+#define HOST_GET_BSS_NO(seq) \
+ (((seq) & HOST_BSS_NUM_MASK) >> 8)
+
+#define HOST_GET_BSS_TYPE(seq) \
+ (((seq) & HOST_BSS_TYPE_MASK) >> 12)
+
+#define EVENT_DUMMY_HOST_WAKEUP_SIGNAL 0x00000001
+#define EVENT_LINK_LOST 0x00000003
+#define EVENT_LINK_SENSED 0x00000004
+#define EVENT_MIB_CHANGED 0x00000006
+#define EVENT_INIT_DONE 0x00000007
+#define EVENT_DEAUTHENTICATED 0x00000008
+#define EVENT_DISASSOCIATED 0x00000009
+#define EVENT_PS_AWAKE 0x0000000a
+#define EVENT_PS_SLEEP 0x0000000b
+#define EVENT_MIC_ERR_MULTICAST 0x0000000d
+#define EVENT_MIC_ERR_UNICAST 0x0000000e
+#define EVENT_DEEP_SLEEP_AWAKE 0x00000010
+#define EVENT_WMM_STATUS_CHANGE 0x00000017
+#define EVENT_BG_SCAN_REPORT 0x00000018
+#define EVENT_RSSI_LOW 0x00000019
+#define EVENT_SNR_LOW 0x0000001a
+#define EVENT_MAX_FAIL 0x0000001b
+#define EVENT_RSSI_HIGH 0x0000001c
+#define EVENT_SNR_HIGH 0x0000001d
+#define EVENT_DATA_RSSI_LOW 0x00000024
+#define EVENT_DATA_SNR_LOW 0x00000025
+#define EVENT_DATA_RSSI_HIGH 0x00000026
+#define EVENT_DATA_SNR_HIGH 0x00000027
+#define EVENT_LINK_QUALITY 0x00000028
+#define EVENT_PORT_RELEASE 0x0000002b
+#define EVENT_UAP_STA_DEAUTH 0x0000002c
+#define EVENT_UAP_STA_ASSOC 0x0000002d
+#define EVENT_UAP_BSS_START 0x0000002e
+#define EVENT_PRE_BEACON_LOST 0x00000031
+#define EVENT_ADDBA 0x00000033
+#define EVENT_DELBA 0x00000034
+#define EVENT_BA_STREAM_TIEMOUT 0x00000037
+#define EVENT_AMSDU_AGGR_CTRL 0x00000042
+#define EVENT_UAP_BSS_IDLE 0x00000043
+#define EVENT_UAP_BSS_ACTIVE 0x00000044
+#define EVENT_WEP_ICV_ERR 0x00000046
+#define EVENT_HS_ACT_REQ 0x00000047
+#define EVENT_BW_CHANGE 0x00000048
+#define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c
+#define EVENT_HOSTWAKE_STAIE 0x0000004d
+#define EVENT_CHANNEL_SWITCH_ANN 0x00000050
+#define EVENT_RADAR_DETECTED 0x00000053
+#define EVENT_CHANNEL_REPORT_RDY 0x00000054
+#define EVENT_TX_DATA_PAUSE 0x00000055
+#define EVENT_EXT_SCAN_REPORT 0x00000058
+#define EVENT_RXBA_SYNC 0x00000059
+#define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f
+#define EVENT_UNKNOWN_DEBUG 0x00000063
+#define EVENT_BG_SCAN_STOPPED 0x00000065
+#define EVENT_MULTI_CHAN_INFO 0x0000006a
+#define EVENT_FW_DUMP_INFO 0x00000073
+#define EVENT_TX_STATUS_REPORT 0x00000074
+#define EVENT_BT_COEX_WLAN_PARA_CHANGE 0X00000076
+
+#define EVENT_ID_MASK 0xffff
+#define BSS_NUM_MASK 0xf
+
+#define EVENT_GET_BSS_NUM(event_cause) \
+ (((event_cause) >> 16) & BSS_NUM_MASK)
+
+#define EVENT_GET_BSS_TYPE(event_cause) \
+ (((event_cause) >> 24) & 0x00ff)
+
+#define NXPWIFI_MAX_PATTERN_LEN 40
+#define NXPWIFI_MAX_OFFSET_LEN 100
+#define NXPWIFI_MAX_ND_MATCH_SETS 10
+
+#define STACK_NBYTES 100
+#define TYPE_DNUM 1
+#define TYPE_BYTESEQ 2
+#define MAX_OPERAND 0x40
+#define TYPE_EQ (MAX_OPERAND + 1)
+#define TYPE_EQ_DNUM (MAX_OPERAND + 2)
+#define TYPE_EQ_BIT (MAX_OPERAND + 3)
+#define TYPE_AND (MAX_OPERAND + 4)
+#define TYPE_OR (MAX_OPERAND + 5)
+#define MEF_MODE_HOST_SLEEP 1
+#define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3
+#define MEF_ACTION_AUTO_ARP 0x10
+#define NXPWIFI_CRITERIA_BROADCAST BIT(0)
+#define NXPWIFI_CRITERIA_UNICAST BIT(1)
+#define NXPWIFI_CRITERIA_MULTICAST BIT(3)
+#define NXPWIFI_MAX_SUPPORTED_IPADDR 4
+
+#define NXPWIFI_DEF_CS_UNIT_TIME 2
+#define NXPWIFI_DEF_CS_THR_OTHERLINK 10
+#define NXPWIFI_DEF_THR_DIRECTLINK 0
+#define NXPWIFI_DEF_CS_TIME 10
+#define NXPWIFI_DEF_CS_TIMEOUT 16
+#define NXPWIFI_DEF_CS_REG_CLASS 12
+#define NXPWIFI_DEF_CS_PERIODICITY 1
+
+#define NXPWIFI_FW_V15 15
+
+#define NXPWIFI_MASTER_RADAR_DET_MASK BIT(1)
+
+struct nxpwifi_ie_types_header {
+ __le16 type;
+ __le16 len;
+} __packed;
+
+struct nxpwifi_ie_types_data {
+ struct nxpwifi_ie_types_header header;
+ u8 data[];
+} __packed;
+
+#define NXPWIFI_TxPD_POWER_MGMT_NULL_PACKET 0x01
+#define NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET 0x08
+#define NXPWIFI_TXPD_FLAGS_REQ_TX_STATUS 0x20
+
+enum HS_WAKEUP_REASON {
+ NO_HSWAKEUP_REASON = 0,
+ BCAST_DATA_MATCHED,
+ MCAST_DATA_MATCHED,
+ UCAST_DATA_MATCHED,
+ MASKTABLE_EVENT_MATCHED,
+ NON_MASKABLE_EVENT_MATCHED,
+ NON_MASKABLE_CONDITION_MATCHED,
+ MAGIC_PATTERN_MATCHED,
+ CONTROL_FRAME_MATCHED,
+ MANAGEMENT_FRAME_MATCHED,
+ GTK_REKEY_FAILURE,
+ RESERVED
+};
+
+struct txpd {
+ u8 bss_type;
+ u8 bss_num;
+ __le16 tx_pkt_length;
+ __le16 tx_pkt_offset;
+ __le16 tx_pkt_type;
+ __le32 tx_control;
+ u8 priority;
+ u8 flags;
+ u8 pkt_delay_2ms;
+ u8 reserved1[2];
+ u8 tx_token_id;
+ u8 reserved[2];
+} __packed;
+
+struct rxpd {
+ u8 bss_type;
+ u8 bss_num;
+ __le16 rx_pkt_length;
+ __le16 rx_pkt_offset;
+ __le16 rx_pkt_type;
+ __le16 seq_num;
+ u8 priority;
+ u8 rx_rate;
+ s8 snr;
+ s8 nf;
+
+ /* For: Non-802.11 AC cards
+ *
+ * Ht Info [Bit 0] RxRate format: LG=0, HT=1
+ * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1
+ * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1
+ *
+ * For: 802.11 AC cards
+ * [Bit 1] [Bit 0] RxRate format: legacy rate = 00 HT = 01 VHT = 10
+ * [Bit 3] [Bit 2] HT/VHT Bandwidth BW20 = 00 BW40 = 01
+ * BW80 = 10 BW160 = 11
+ * [Bit 4] HT/VHT Guard interval LGI = 0 SGI = 1
+ * [Bit 5] STBC support Enabled = 1
+ * [Bit 6] LDPC support Enabled = 1
+ * [Bit 7] Reserved
+ */
+ u8 ht_info;
+ u8 reserved[3];
+ u8 flags;
+} __packed;
+
+struct uap_txpd {
+ u8 bss_type;
+ u8 bss_num;
+ __le16 tx_pkt_length;
+ __le16 tx_pkt_offset;
+ __le16 tx_pkt_type;
+ __le32 tx_control;
+ u8 priority;
+ u8 flags;
+ u8 pkt_delay_2ms;
+ u8 reserved1[2];
+ u8 tx_token_id;
+ u8 reserved[2];
+} __packed;
+
+struct uap_rxpd {
+ u8 bss_type;
+ u8 bss_num;
+ __le16 rx_pkt_length;
+ __le16 rx_pkt_offset;
+ __le16 rx_pkt_type;
+ __le16 seq_num;
+ u8 priority;
+ u8 rx_rate;
+ s8 snr;
+ s8 nf;
+ u8 ht_info;
+ u8 reserved[3];
+ u8 flags;
+} __packed;
+
+struct nxpwifi_auth {
+ __le16 auth_alg;
+ __le16 auth_transaction;
+ __le16 status_code;
+ /* possibly followed by Challenge text */
+ u8 variable[];
+} __packed;
+
+struct nxpwifi_ieee80211_mgmt {
+ __le16 frame_control;
+ __le16 duration;
+ u8 da[ETH_ALEN];
+ u8 sa[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ __le16 seq_ctrl;
+ u8 addr4[ETH_ALEN];
+ struct nxpwifi_auth auth;
+} __packed;
+
+struct nxpwifi_fw_chan_stats {
+ u8 chan_num;
+ u8 bandcfg;
+ u8 flags;
+ s8 noise;
+ __le16 total_bss;
+ __le16 cca_scan_dur;
+ __le16 cca_busy_dur;
+} __packed;
+
+enum nxpwifi_chan_scan_mode_bitmasks {
+ NXPWIFI_PASSIVE_SCAN = BIT(0),
+ NXPWIFI_DISABLE_CHAN_FILT = BIT(1),
+ NXPWIFI_HIDDEN_SSID_REPORT = BIT(4),
+};
+
+struct nxpwifi_chan_scan_param_set {
+ u8 radio_type;
+ u8 chan_number;
+ u8 chan_scan_mode_bmap;
+ __le16 min_scan_time;
+ __le16 max_scan_time;
+} __packed;
+
+struct nxpwifi_ie_types_chan_list_param_set {
+ struct nxpwifi_ie_types_header header;
+ struct nxpwifi_chan_scan_param_set chan_scan_param[];
+} __packed;
+
+struct nxpwifi_ie_types_rxba_sync {
+ struct nxpwifi_ie_types_header header;
+ u8 mac[ETH_ALEN];
+ u8 tid;
+ u8 reserved;
+ __le16 seq_num;
+ __le16 bitmap_len;
+ u8 bitmap[];
+} __packed;
+
+struct chan_band_param_set {
+ u8 radio_type;
+ u8 chan_number;
+};
+
+struct nxpwifi_ie_types_chan_band_list_param_set {
+ struct nxpwifi_ie_types_header header;
+ struct chan_band_param_set chan_band_param[];
+} __packed;
+
+struct nxpwifi_ie_types_rates_param_set {
+ struct nxpwifi_ie_types_header header;
+ u8 rates[];
+} __packed;
+
+struct nxpwifi_ie_types_ssid_param_set {
+ struct nxpwifi_ie_types_header header;
+ u8 ssid[];
+} __packed;
+
+struct nxpwifi_ie_types_host_mlme {
+ struct nxpwifi_ie_types_header header;
+ u8 host_mlme;
+} __packed;
+
+struct nxpwifi_ie_types_num_probes {
+ struct nxpwifi_ie_types_header header;
+ __le16 num_probes;
+} __packed;
+
+struct nxpwifi_ie_types_repeat_count {
+ struct nxpwifi_ie_types_header header;
+ __le16 repeat_count;
+} __packed;
+
+struct nxpwifi_ie_types_min_rssi_threshold {
+ struct nxpwifi_ie_types_header header;
+ __le16 rssi_threshold;
+} __packed;
+
+struct nxpwifi_ie_types_bgscan_start_later {
+ struct nxpwifi_ie_types_header header;
+ __le16 start_later;
+} __packed;
+
+struct nxpwifi_ie_types_scan_chan_gap {
+ struct nxpwifi_ie_types_header header;
+ /* time gap in TUs to be used between two consecutive channels scan */
+ __le16 chan_gap;
+} __packed;
+
+struct nxpwifi_ie_types_random_mac {
+ struct nxpwifi_ie_types_header header;
+ u8 mac[ETH_ALEN];
+} __packed;
+
+struct nxpwifi_ietypes_chanstats {
+ struct nxpwifi_ie_types_header header;
+ struct nxpwifi_fw_chan_stats chanstats[];
+} __packed;
+
+struct nxpwifi_ie_types_wildcard_ssid_params {
+ struct nxpwifi_ie_types_header header;
+ u8 max_ssid_length;
+ u8 ssid[];
+} __packed;
+
+#define TSF_DATA_SIZE 8
+struct nxpwifi_ie_types_tsf_timestamp {
+ struct nxpwifi_ie_types_header header;
+ u8 tsf_data[];
+} __packed;
+
+struct nxpwifi_cf_param_set {
+ u8 cfp_cnt;
+ u8 cfp_period;
+ __le16 cfp_max_duration;
+ __le16 cfp_duration_remaining;
+} __packed;
+
+struct nxpwifi_ibss_param_set {
+ __le16 atim_window;
+} __packed;
+
+struct nxpwifi_ie_types_ss_param_set {
+ struct nxpwifi_ie_types_header header;
+ union {
+ struct nxpwifi_cf_param_set cf_param_set[1];
+ struct nxpwifi_ibss_param_set ibss_param_set[1];
+ } cf_ibss;
+} __packed;
+
+struct nxpwifi_fh_param_set {
+ __le16 dwell_time;
+ u8 hop_set;
+ u8 hop_pattern;
+ u8 hop_index;
+} __packed;
+
+struct nxpwifi_ds_param_set {
+ u8 current_chan;
+} __packed;
+
+struct nxpwifi_ie_types_phy_param_set {
+ struct nxpwifi_ie_types_header header;
+ union {
+ struct nxpwifi_fh_param_set fh_param_set[1];
+ struct nxpwifi_ds_param_set ds_param_set[1];
+ } fh_ds;
+} __packed;
+
+struct nxpwifi_ie_types_auth_type {
+ struct nxpwifi_ie_types_header header;
+ __le16 auth_type;
+} __packed;
+
+struct nxpwifi_ie_types_vendor_param_set {
+ struct nxpwifi_ie_types_header header;
+ u8 ie[NXPWIFI_MAX_VSIE_LEN];
+};
+
+#define NXPWIFI_AUTHTYPE_SAE 6
+
+struct nxpwifi_ie_types_sae_pwe_mode {
+ struct nxpwifi_ie_types_header header;
+ u8 pwe[];
+} __packed;
+
+struct nxpwifi_ie_types_rsn_param_set {
+ struct nxpwifi_ie_types_header header;
+ u8 rsn_ie[];
+} __packed;
+
+#define KEYPARAMSET_FIXED_LEN 6
+
+#define IGTK_PN_LEN 8
+
+struct nxpwifi_cmac_param {
+ u8 ipn[IGTK_PN_LEN];
+ u8 key[WLAN_KEY_LEN_AES_CMAC];
+} __packed;
+
+struct nxpwifi_wep_param {
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_WEP104];
+} __packed;
+
+struct nxpwifi_tkip_param {
+ u8 pn[WPA_PN_SIZE];
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_TKIP];
+} __packed;
+
+struct nxpwifi_aes_param {
+ u8 pn[WPA_PN_SIZE];
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_CCMP_256];
+} __packed;
+
+struct nxpwifi_cmac_aes_param {
+ u8 ipn[IGTK_PN_LEN];
+ __le16 key_len;
+ u8 key[WLAN_KEY_LEN_AES_CMAC];
+} __packed;
+
+struct nxpwifi_ie_type_key_param_set {
+ __le16 type;
+ __le16 len;
+ u8 mac_addr[ETH_ALEN];
+ u8 key_idx;
+ u8 key_type;
+ __le16 key_info;
+ union {
+ struct nxpwifi_wep_param wep;
+ struct nxpwifi_tkip_param tkip;
+ struct nxpwifi_aes_param aes;
+ struct nxpwifi_cmac_aes_param cmac_aes;
+ } key_params;
+} __packed;
+
+struct host_cmd_ds_802_11_key_material {
+ __le16 action;
+ struct nxpwifi_ie_type_key_param_set key_param_set;
+} __packed;
+
+struct host_cmd_ds_gen {
+ __le16 command;
+ __le16 size;
+ __le16 seq_num;
+ __le16 result;
+};
+
+#define S_DS_GEN sizeof(struct host_cmd_ds_gen)
+
+enum sleep_resp_ctrl {
+ RESP_NOT_NEEDED = 0,
+ RESP_NEEDED,
+};
+
+struct nxpwifi_ps_param {
+ __le16 null_pkt_interval;
+ __le16 multiple_dtims;
+ __le16 bcn_miss_timeout;
+ __le16 local_listen_interval;
+ __le16 reserved;
+ __le16 mode;
+ __le16 delay_to_ps;
+} __packed;
+
+#define HS_DEF_WAKE_INTERVAL 100
+#define HS_DEF_INACTIVITY_TIMEOUT 50
+
+struct nxpwifi_ps_param_in_hs {
+ struct nxpwifi_ie_types_header header;
+ __le32 hs_wake_int;
+ __le32 hs_inact_timeout;
+} __packed;
+
+#define BITMAP_AUTO_DS 0x01
+#define BITMAP_STA_PS 0x10
+
+struct nxpwifi_ie_types_auto_ds_param {
+ struct nxpwifi_ie_types_header header;
+ __le16 deep_sleep_timeout;
+} __packed;
+
+struct nxpwifi_ie_types_ps_param {
+ struct nxpwifi_ie_types_header header;
+ struct nxpwifi_ps_param param;
+} __packed;
+
+struct host_cmd_ds_802_11_ps_mode_enh {
+ __le16 action;
+
+ union {
+ struct nxpwifi_ps_param opt_ps;
+ __le16 ps_bitmap;
+ } params;
+} __packed;
+
+enum API_VER_ID {
+ KEY_API_VER_ID = 1,
+ FW_API_VER_ID = 2,
+ UAP_FW_API_VER_ID = 3,
+ CHANRPT_API_VER_ID = 4,
+ FW_HOTFIX_VER_ID = 5,
+};
+
+struct hw_spec_api_rev {
+ struct nxpwifi_ie_types_header header;
+ __le16 api_id;
+ u8 major_ver;
+ u8 minor_ver;
+} __packed;
+
+struct host_cmd_ds_get_hw_spec {
+ __le16 hw_if_version;
+ __le16 version;
+ __le16 reserved;
+ __le16 num_of_mcast_adr;
+ u8 permanent_addr[ETH_ALEN];
+ __le16 region_code;
+ __le16 number_of_antenna;
+ __le32 fw_release_number;
+ __le32 reserved_1;
+ __le32 reserved_2;
+ __le32 reserved_3;
+ __le32 fw_cap_info;
+ __le32 dot_11n_dev_cap;
+ u8 dev_mcs_support;
+ __le16 mp_end_port; /* SDIO only, reserved for other interfacces */
+ __le16 mgmt_buf_count; /* mgmt IE buffer count */
+ __le32 reserved_4;
+ __le32 reserved_5;
+ __le32 dot_11ac_dev_cap;
+ __le32 dot_11ac_mcs_support;
+ u8 tlvs[];
+} __packed;
+
+struct host_cmd_ds_802_11_rssi_info {
+ __le16 action;
+ __le16 ndata;
+ __le16 nbcn;
+ __le16 reserved[9];
+ long long reserved_1;
+} __packed;
+
+struct host_cmd_ds_802_11_rssi_info_rsp {
+ __le16 action;
+ __le16 ndata;
+ __le16 nbcn;
+ __le16 data_rssi_last;
+ __le16 data_nf_last;
+ __le16 data_rssi_avg;
+ __le16 data_nf_avg;
+ __le16 bcn_rssi_last;
+ __le16 bcn_nf_last;
+ __le16 bcn_rssi_avg;
+ __le16 bcn_nf_avg;
+ long long tsf_bcn;
+} __packed;
+
+struct host_cmd_ds_802_11_mac_address {
+ __le16 action;
+ u8 mac_addr[ETH_ALEN];
+} __packed;
+
+struct host_cmd_ds_mac_control {
+ __le32 action;
+};
+
+struct host_cmd_ds_mac_multicast_adr {
+ __le16 action;
+ __le16 num_of_adrs;
+ u8 mac_list[NXPWIFI_MAX_MULTICAST_LIST_SIZE][ETH_ALEN];
+} __packed;
+
+struct host_cmd_ds_802_11_deauthenticate {
+ u8 mac_addr[ETH_ALEN];
+ __le16 reason_code;
+} __packed;
+
+struct host_cmd_ds_802_11_associate {
+ u8 peer_sta_addr[ETH_ALEN];
+ __le16 cap_info_bitmap;
+ __le16 listen_interval;
+ __le16 beacon_period;
+ u8 dtim_period;
+} __packed;
+
+struct ieee_types_assoc_rsp {
+ __le16 cap_info_bitmap;
+ __le16 status_code;
+ __le16 a_id;
+ u8 ie_buffer[];
+} __packed;
+
+struct host_cmd_ds_802_11_associate_rsp {
+ struct ieee_types_assoc_rsp assoc_rsp;
+} __packed;
+
+struct ieee_types_cf_param_set {
+ u8 element_id;
+ u8 len;
+ u8 cfp_cnt;
+ u8 cfp_period;
+ __le16 cfp_max_duration;
+ __le16 cfp_duration_remaining;
+} __packed;
+
+struct ieee_types_ibss_param_set {
+ u8 element_id;
+ u8 len;
+ __le16 atim_window;
+} __packed;
+
+union ieee_types_ss_param_set {
+ struct ieee_types_cf_param_set cf_param_set;
+ struct ieee_types_ibss_param_set ibss_param_set;
+} __packed;
+
+struct ieee_types_fh_param_set {
+ u8 element_id;
+ u8 len;
+ __le16 dwell_time;
+ u8 hop_set;
+ u8 hop_pattern;
+ u8 hop_index;
+} __packed;
+
+struct ieee_types_ds_param_set {
+ u8 element_id;
+ u8 len;
+ u8 current_chan;
+} __packed;
+
+union ieee_types_phy_param_set {
+ struct ieee_types_fh_param_set fh_param_set;
+ struct ieee_types_ds_param_set ds_param_set;
+} __packed;
+
+struct ieee_types_oper_mode_ntf {
+ u8 element_id;
+ u8 len;
+ u8 oper_mode;
+} __packed;
+
+struct host_cmd_ds_802_11_get_log {
+ __le32 mcast_tx_frame;
+ __le32 failed;
+ __le32 retry;
+ __le32 multi_retry;
+ __le32 frame_dup;
+ __le32 rts_success;
+ __le32 rts_failure;
+ __le32 ack_failure;
+ __le32 rx_frag;
+ __le32 mcast_rx_frame;
+ __le32 fcs_error;
+ __le32 tx_frame;
+ __le32 reserved;
+ __le32 wep_icv_err_cnt[4];
+ __le32 bcn_rcv_cnt;
+ __le32 bcn_miss_cnt;
+} __packed;
+
+/* Enumeration for rate format */
+enum nxpwifi_rate_format {
+ NXPWIFI_RATE_FORMAT_LG = 0,
+ NXPWIFI_RATE_FORMAT_HT,
+ NXPWIFI_RATE_FORMAT_VHT,
+ NXPWIFI_RATE_FORMAT_AUTO = 0xFF,
+};
+
+struct host_cmd_ds_tx_rate_query {
+ u8 tx_rate;
+ /* Tx Rate Info: For 802.11 AC cards
+ *
+ * [Bit 0-1] tx rate formate: LG = 0, HT = 1, VHT = 2
+ * [Bit 2-3] HT/VHT Bandwidth: BW20 = 0, BW40 = 1, BW80 = 2, BW160 = 3
+ * [Bit 4] HT/VHT Guard Interval: LGI = 0, SGI = 1
+ *
+ * For non-802.11 AC cards
+ * Ht Info [Bit 0] RxRate format: LG=0, HT=1
+ * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1
+ * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1
+ */
+ u8 ht_info;
+} __packed;
+
+struct nxpwifi_tx_pause_tlv {
+ struct nxpwifi_ie_types_header header;
+ u8 peermac[ETH_ALEN];
+ u8 tx_pause;
+ u8 pkt_cnt;
+} __packed;
+
+enum host_sleep_action {
+ HS_CONFIGURE = 0x0001,
+ HS_ACTIVATE = 0x0002,
+};
+
+struct nxpwifi_hs_config_param {
+ __le32 conditions;
+ u8 gpio;
+ u8 gap;
+} __packed;
+
+struct hs_activate_param {
+ __le16 resp_ctrl;
+} __packed;
+
+struct host_cmd_ds_802_11_hs_cfg_enh {
+ __le16 action;
+
+ union {
+ struct nxpwifi_hs_config_param hs_config;
+ struct hs_activate_param hs_activate;
+ } params;
+} __packed;
+
+enum SNMP_MIB_INDEX {
+ OP_RATE_SET_I = 1,
+ DTIM_PERIOD_I = 3,
+ RTS_THRESH_I = 5,
+ SHORT_RETRY_LIM_I = 6,
+ LONG_RETRY_LIM_I = 7,
+ FRAG_THRESH_I = 8,
+ DOT11D_I = 9,
+ DOT11H_I = 10,
+};
+
+enum nxpwifi_assocmd_failurepoint {
+ NXPWIFI_ASSOC_CMD_SUCCESS = 0,
+ NXPWIFI_ASSOC_CMD_FAILURE_ASSOC,
+ NXPWIFI_ASSOC_CMD_FAILURE_AUTH,
+ NXPWIFI_ASSOC_CMD_FAILURE_JOIN
+};
+
+#define MAX_SNMP_BUF_SIZE 128
+
+struct host_cmd_ds_802_11_snmp_mib {
+ __le16 query_type;
+ __le16 oid;
+ __le16 buf_size;
+ u8 value[];
+} __packed;
+
+struct nxpwifi_rate_scope {
+ __le16 type;
+ __le16 length;
+ __le16 hr_dsss_rate_bitmap;
+ __le16 ofdm_rate_bitmap;
+ __le16 ht_mcs_rate_bitmap[8];
+ __le16 vht_mcs_rate_bitmap[8];
+} __packed;
+
+struct nxpwifi_rate_drop_pattern {
+ __le16 type;
+ __le16 length;
+ __le32 rate_drop_mode;
+} __packed;
+
+struct host_cmd_ds_tx_rate_cfg {
+ __le16 action;
+ __le16 cfg_index;
+} __packed;
+
+struct nxpwifi_power_group {
+ u8 modulation_class;
+ u8 first_rate_code;
+ u8 last_rate_code;
+ s8 power_step;
+ s8 power_min;
+ s8 power_max;
+ u8 ht_bandwidth;
+ u8 reserved;
+} __packed;
+
+struct nxpwifi_types_power_group {
+ __le16 type;
+ __le16 length;
+} __packed;
+
+struct host_cmd_ds_txpwr_cfg {
+ __le16 action;
+ __le16 cfg_index;
+ __le32 mode;
+} __packed;
+
+struct host_cmd_ds_rf_tx_pwr {
+ __le16 action;
+ __le16 cur_level;
+ u8 max_power;
+ u8 min_power;
+} __packed;
+
+struct host_cmd_ds_rf_ant_mimo {
+ __le16 action_tx;
+ __le16 tx_ant_mode;
+ __le16 action_rx;
+ __le16 rx_ant_mode;
+} __packed;
+
+struct host_cmd_ds_rf_ant_siso {
+ __le16 action;
+ __le16 ant_mode;
+} __packed;
+
+#define BAND_CFG_CHAN_BAND_MASK 0x03
+#define BAND_CFG_CHAN_BAND_SHIFT_BIT 0
+#define BAND_CFG_CHAN_WIDTH_MASK 0x0C
+#define BAND_CFG_CHAN_WIDTH_SHIFT_BIT 2
+#define BAND_CFG_CHAN2_OFFSET_MASK 0x30
+#define BAND_CFG_CHAN2_SHIFT_BIT 4
+
+struct nxpwifi_chan_desc {
+ __le16 start_freq;
+ u8 band_cfg;
+ u8 chan_num;
+} __packed;
+
+struct host_cmd_ds_chan_rpt_req {
+ struct nxpwifi_chan_desc chan_desc;
+ __le32 msec_dwell_time;
+} __packed;
+
+struct host_cmd_ds_chan_rpt_event {
+ __le32 result;
+ __le64 start_tsf;
+ __le32 duration;
+ u8 tlvbuf[];
+} __packed;
+
+struct host_cmd_sdio_sp_rx_aggr_cfg {
+ u8 action;
+ u8 enable;
+ __le16 block_size;
+} __packed;
+
+struct nxpwifi_fixed_bcn_param {
+ __le64 timestamp;
+ __le16 beacon_period;
+ __le16 cap_info_bitmap;
+} __packed;
+
+struct nxpwifi_event_scan_result {
+ __le16 event_id;
+ u8 bss_index;
+ u8 bss_type;
+ u8 more_event;
+ u8 reserved[3];
+ __le16 buf_size;
+ u8 num_of_set;
+} __packed;
+
+struct tx_status_event {
+ u8 packet_type;
+ u8 tx_token_id;
+ u8 status;
+} __packed;
+
+#define NXPWIFI_USER_SCAN_CHAN_MAX 50
+
+#define NXPWIFI_MAX_SSID_LIST_LENGTH 10
+
+struct nxpwifi_scan_cmd_config {
+ /* BSS mode to be sent in the firmware command
+ */
+ u8 bss_mode;
+
+ /* Specific BSSID used to filter scan results in the firmware */
+ u8 specific_bssid[ETH_ALEN];
+
+ /* Length of TLVs sent in command starting at tlvBuffer */
+ u32 tlv_buf_len;
+
+ /* SSID TLV(s) and ChanList TLVs to be sent in the firmware command
+ *
+ * TLV_TYPE_CHANLIST, nxpwifi_ie_types_chan_list_param_set
+ * WLAN_EID_SSID, nxpwifi_ie_types_ssid_param_set
+ */
+ u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored here */
+} __packed;
+
+struct nxpwifi_user_scan_chan {
+ u8 chan_number;
+ u8 radio_type;
+ u8 scan_type;
+ u8 reserved;
+ u32 scan_time;
+} __packed;
+
+struct nxpwifi_user_scan_cfg {
+ /* BSS mode to be sent in the firmware command
+ */
+ u8 bss_mode;
+ /* Configure the number of probe requests for active chan scans */
+ u8 num_probes;
+ u8 reserved;
+ /* BSSID filter sent in the firmware command to limit the results */
+ u8 specific_bssid[ETH_ALEN];
+ /* SSID filter list used in the firmware to limit the scan results */
+ struct cfg80211_ssid *ssid_list;
+ u8 num_ssids;
+ /* Variable number (fixed maximum) of channels to scan up */
+ struct nxpwifi_user_scan_chan chan_list[NXPWIFI_USER_SCAN_CHAN_MAX];
+ u16 scan_chan_gap;
+ u8 random_mac[ETH_ALEN];
+} __packed;
+
+#define NXPWIFI_BG_SCAN_CHAN_MAX 38
+#define NXPWIFI_BSS_MODE_INFRA 1
+#define NXPWIFI_BGSCAN_ACT_GET 0x0000
+#define NXPWIFI_BGSCAN_ACT_SET 0x0001
+#define NXPWIFI_BGSCAN_ACT_SET_ALL 0xff01
+/** ssid match */
+#define NXPWIFI_BGSCAN_SSID_MATCH 0x0001
+/** ssid match and RSSI exceeded */
+#define NXPWIFI_BGSCAN_SSID_RSSI_MATCH 0x0004
+/**wait for all channel scan to complete to report scan result*/
+#define NXPWIFI_BGSCAN_WAIT_ALL_CHAN_DONE 0x80000000
+
+struct nxpwifi_bg_scan_cfg {
+ u16 action;
+ u8 enable;
+ u8 bss_type;
+ u8 chan_per_scan;
+ u32 scan_interval;
+ u32 report_condition;
+ u8 num_probes;
+ u8 rssi_threshold;
+ u8 snr_threshold;
+ u16 repeat_count;
+ u16 start_later;
+ struct cfg80211_match_set *ssid_list;
+ u8 num_ssids;
+ struct nxpwifi_user_scan_chan chan_list[NXPWIFI_BG_SCAN_CHAN_MAX];
+ u16 scan_chan_gap;
+} __packed;
+
+struct ie_body {
+ u8 grp_key_oui[4];
+ u8 ptk_cnt[2];
+ u8 ptk_body[4];
+} __packed;
+
+struct host_cmd_ds_802_11_scan {
+ u8 bss_mode;
+ u8 bssid[ETH_ALEN];
+ u8 tlv_buffer[];
+} __packed;
+
+struct host_cmd_ds_802_11_scan_rsp {
+ __le16 bss_descript_size;
+ u8 number_of_sets;
+ u8 bss_desc_and_tlv_buffer[];
+} __packed;
+
+struct host_cmd_ds_802_11_scan_ext {
+ u32 reserved;
+ u8 tlv_buffer[];
+} __packed;
+
+struct nxpwifi_ie_types_bss_mode {
+ struct nxpwifi_ie_types_header header;
+ u8 bss_mode;
+} __packed;
+
+struct nxpwifi_ie_types_bss_scan_rsp {
+ struct nxpwifi_ie_types_header header;
+ u8 bssid[ETH_ALEN];
+ u8 frame_body[];
+} __packed;
+
+struct nxpwifi_ie_types_bss_scan_info {
+ struct nxpwifi_ie_types_header header;
+ __le16 rssi;
+ __le16 anpi;
+ u8 cca_busy_fraction;
+ u8 radio_type;
+ u8 channel;
+ u8 reserved;
+ __le64 tsf;
+} __packed;
+
+struct host_cmd_ds_802_11_bg_scan_config {
+ __le16 action;
+ u8 enable;
+ u8 bss_type;
+ u8 chan_per_scan;
+ u8 reserved;
+ __le16 reserved1;
+ __le32 scan_interval;
+ __le32 reserved2;
+ __le32 report_condition;
+ __le16 reserved3;
+ u8 tlv[];
+} __packed;
+
+struct host_cmd_ds_802_11_bg_scan_query {
+ u8 flush;
+} __packed;
+
+struct host_cmd_ds_802_11_bg_scan_query_rsp {
+ __le32 report_condition;
+ struct host_cmd_ds_802_11_scan_rsp scan_resp;
+} __packed;
+
+struct nxpwifi_ietypes_domain_param_set {
+ struct nxpwifi_ie_types_header header;
+ u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
+ struct ieee80211_country_ie_triplet triplet[];
+} __packed;
+
+struct host_cmd_ds_802_11d_domain_info {
+ __le16 action;
+ struct nxpwifi_ietypes_domain_param_set domain;
+} __packed;
+
+struct host_cmd_ds_802_11d_domain_info_rsp {
+ __le16 action;
+ struct nxpwifi_ietypes_domain_param_set domain;
+} __packed;
+
+struct host_cmd_ds_11n_addba_req {
+ u8 add_req_result;
+ u8 peer_mac_addr[ETH_ALEN];
+ u8 dialog_token;
+ __le16 block_ack_param_set;
+ __le16 block_ack_tmo;
+ __le16 ssn;
+} __packed;
+
+struct host_cmd_ds_11n_addba_rsp {
+ u8 add_rsp_result;
+ u8 peer_mac_addr[ETH_ALEN];
+ u8 dialog_token;
+ __le16 status_code;
+ __le16 block_ack_param_set;
+ __le16 block_ack_tmo;
+ __le16 ssn;
+} __packed;
+
+struct host_cmd_ds_11n_delba {
+ u8 del_result;
+ u8 peer_mac_addr[ETH_ALEN];
+ __le16 del_ba_param_set;
+ __le16 reason_code;
+ u8 reserved;
+} __packed;
+
+struct host_cmd_ds_11n_batimeout {
+ u8 tid;
+ u8 peer_mac_addr[ETH_ALEN];
+ u8 origninator;
+} __packed;
+
+struct host_cmd_ds_11n_cfg {
+ __le16 action;
+ __le16 ht_tx_cap;
+ __le16 ht_tx_info;
+ __le16 misc_config; /* Needed for 802.11AC cards only */
+} __packed;
+
+struct host_cmd_ds_txbuf_cfg {
+ __le16 action;
+ __le16 buff_size;
+ __le16 mp_end_port; /* SDIO only, reserved for other interfacces */
+ __le16 reserved3;
+} __packed;
+
+struct host_cmd_ds_amsdu_aggr_ctrl {
+ __le16 action;
+ __le16 enable;
+ __le16 curr_buf_size;
+} __packed;
+
+struct host_cmd_ds_sta_deauth {
+ u8 mac[ETH_ALEN];
+ __le16 reason;
+} __packed;
+
+struct nxpwifi_ie_types_sta_info {
+ struct nxpwifi_ie_types_header header;
+ u8 mac[ETH_ALEN];
+ u8 power_mfg_status;
+ s8 rssi;
+};
+
+struct host_cmd_ds_sta_list {
+ __le16 sta_count;
+ u8 tlv[];
+} __packed;
+
+struct nxpwifi_ie_types_pwr_capability {
+ struct nxpwifi_ie_types_header header;
+ s8 min_pwr;
+ s8 max_pwr;
+};
+
+struct nxpwifi_ie_types_local_pwr_constraint {
+ struct nxpwifi_ie_types_header header;
+ u8 chan;
+ u8 constraint;
+};
+
+struct nxpwifi_ie_types_wmm_param_set {
+ struct nxpwifi_ie_types_header header;
+ u8 wmm_ie[];
+} __packed;
+
+struct nxpwifi_ie_types_mgmt_frame {
+ struct nxpwifi_ie_types_header header;
+ __le16 frame_control;
+ u8 frame_contents[];
+};
+
+struct nxpwifi_ie_types_wmm_queue_status {
+ struct nxpwifi_ie_types_header header;
+ u8 queue_index;
+ u8 disabled;
+ __le16 medium_time;
+ u8 flow_required;
+ u8 flow_created;
+ u32 reserved;
+};
+
+struct ieee_types_vendor_header {
+ u8 element_id;
+ u8 len;
+ struct {
+ u8 oui[3];
+ u8 oui_type;
+ } __packed oui;
+} __packed;
+
+struct ieee_types_wmm_parameter {
+ /* WMM Parameter IE - Vendor Specific Header:
+ * element_id [221/0xdd]
+ * Len [24]
+ * Oui [00:50:f2]
+ * OuiType [2]
+ * OuiSubType [1]
+ * Version [1]
+ */
+ struct ieee_types_vendor_header vend_hdr;
+ u8 oui_subtype;
+ u8 version;
+
+ u8 qos_info_bitmap;
+ u8 reserved;
+ struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS];
+} __packed;
+
+struct ieee_types_wmm_info {
+ /* WMM Info IE - Vendor Specific Header:
+ * element_id [221/0xdd]
+ * Len [7]
+ * Oui [00:50:f2]
+ * OuiType [2]
+ * OuiSubType [0]
+ * Version [1]
+ */
+ struct ieee_types_vendor_header vend_hdr;
+ u8 oui_subtype;
+ u8 version;
+
+ u8 qos_info_bitmap;
+} __packed;
+
+struct host_cmd_ds_wmm_get_status {
+ u8 queue_status_tlv[sizeof(struct nxpwifi_ie_types_wmm_queue_status) *
+ IEEE80211_NUM_ACS];
+ u8 wmm_param_tlv[sizeof(struct ieee_types_wmm_parameter) + 2];
+} __packed;
+
+struct nxpwifi_wmm_ac_status {
+ u8 disabled;
+ u8 flow_required;
+ u8 flow_created;
+};
+
+struct nxpwifi_ie_types_htcap {
+ struct nxpwifi_ie_types_header header;
+ struct ieee80211_ht_cap ht_cap;
+} __packed;
+
+struct nxpwifi_ie_types_vhtcap {
+ struct nxpwifi_ie_types_header header;
+ struct ieee80211_vht_cap vht_cap;
+} __packed;
+
+struct nxpwifi_ie_types_aid {
+ struct nxpwifi_ie_types_header header;
+ __le16 aid;
+} __packed;
+
+struct nxpwifi_ie_types_oper_mode_ntf {
+ struct nxpwifi_ie_types_header header;
+ u8 oper_mode;
+} __packed;
+
+/* VHT Operations IE */
+struct nxpwifi_ie_types_vht_oper {
+ struct nxpwifi_ie_types_header header;
+ u8 chan_width;
+ u8 chan_center_freq_1;
+ u8 chan_center_freq_2;
+ /* Basic MCS set map, each 2 bits stands for a NSS */
+ __le16 basic_mcs_map;
+} __packed;
+
+struct nxpwifi_ie_types_wmmcap {
+ struct nxpwifi_ie_types_header header;
+ struct nxpwifi_types_wmm_info wmm_info;
+} __packed;
+
+struct nxpwifi_ie_types_htinfo {
+ struct nxpwifi_ie_types_header header;
+ struct ieee80211_ht_operation ht_oper;
+} __packed;
+
+struct nxpwifi_ie_types_2040bssco {
+ struct nxpwifi_ie_types_header header;
+ u8 bss_co_2040;
+} __packed;
+
+struct nxpwifi_ie_types_extcap {
+ struct nxpwifi_ie_types_header header;
+ u8 ext_capab[];
+} __packed;
+
+struct host_cmd_ds_mem_access {
+ __le16 action;
+ __le16 reserved;
+ __le32 addr;
+ __le32 value;
+} __packed;
+
+struct nxpwifi_ie_types_qos_info {
+ struct nxpwifi_ie_types_header header;
+ u8 qos_info;
+} __packed;
+
+struct host_cmd_ds_mac_reg_access {
+ __le16 action;
+ __le16 offset;
+ __le32 value;
+} __packed;
+
+struct host_cmd_ds_bbp_reg_access {
+ __le16 action;
+ __le16 offset;
+ u8 value;
+ u8 reserved[3];
+} __packed;
+
+struct host_cmd_ds_rf_reg_access {
+ __le16 action;
+ __le16 offset;
+ u8 value;
+ u8 reserved[3];
+} __packed;
+
+struct host_cmd_ds_pmic_reg_access {
+ __le16 action;
+ __le16 offset;
+ u8 value;
+ u8 reserved[3];
+} __packed;
+
+struct host_cmd_ds_802_11_eeprom_access {
+ __le16 action;
+
+ __le16 offset;
+ __le16 byte_count;
+ u8 value;
+} __packed;
+
+struct nxpwifi_assoc_event {
+ u8 sta_addr[ETH_ALEN];
+ __le16 type;
+ __le16 len;
+ __le16 frame_control;
+ __le16 cap_info;
+ __le16 listen_interval;
+ u8 data[];
+} __packed;
+
+struct host_cmd_ds_sys_config {
+ __le16 action;
+ u8 tlv[];
+};
+
+struct host_cmd_11ac_vht_cfg {
+ __le16 action;
+ u8 band_config;
+ u8 misc_config;
+ __le32 cap_info;
+ __le32 mcs_tx_set;
+ __le32 mcs_rx_set;
+} __packed;
+
+struct host_cmd_tlv_akmp {
+ struct nxpwifi_ie_types_header header;
+ __le16 key_mgmt;
+ __le16 key_mgmt_operation;
+} __packed;
+
+struct host_cmd_tlv_pwk_cipher {
+ struct nxpwifi_ie_types_header header;
+ __le16 proto;
+ u8 cipher;
+ u8 reserved;
+} __packed;
+
+struct host_cmd_tlv_gwk_cipher {
+ struct nxpwifi_ie_types_header header;
+ u8 cipher;
+ u8 reserved;
+} __packed;
+
+struct host_cmd_tlv_passphrase {
+ struct nxpwifi_ie_types_header header;
+ u8 passphrase[];
+} __packed;
+
+struct host_cmd_tlv_wep_key {
+ struct nxpwifi_ie_types_header header;
+ u8 key_index;
+ u8 is_default;
+ u8 key[];
+};
+
+struct host_cmd_tlv_auth_type {
+ struct nxpwifi_ie_types_header header;
+ u8 auth_type;
+ u8 pwe_derivation;
+ u8 transition_disable;
+} __packed;
+
+struct host_cmd_tlv_encrypt_protocol {
+ struct nxpwifi_ie_types_header header;
+ __le16 proto;
+} __packed;
+
+struct host_cmd_tlv_ssid {
+ struct nxpwifi_ie_types_header header;
+ u8 ssid[];
+} __packed;
+
+struct host_cmd_tlv_rates {
+ struct nxpwifi_ie_types_header header;
+ u8 rates[];
+} __packed;
+
+struct nxpwifi_ie_types_bssid_list {
+ struct nxpwifi_ie_types_header header;
+ u8 bssid[ETH_ALEN];
+} __packed;
+
+struct host_cmd_tlv_bcast_ssid {
+ struct nxpwifi_ie_types_header header;
+ u8 bcast_ctl;
+} __packed;
+
+struct host_cmd_tlv_beacon_period {
+ struct nxpwifi_ie_types_header header;
+ __le16 period;
+} __packed;
+
+struct host_cmd_tlv_dtim_period {
+ struct nxpwifi_ie_types_header header;
+ u8 period;
+} __packed;
+
+struct host_cmd_tlv_frag_threshold {
+ struct nxpwifi_ie_types_header header;
+ __le16 frag_thr;
+} __packed;
+
+struct host_cmd_tlv_rts_threshold {
+ struct nxpwifi_ie_types_header header;
+ __le16 rts_thr;
+} __packed;
+
+struct host_cmd_tlv_retry_limit {
+ struct nxpwifi_ie_types_header header;
+ u8 limit;
+} __packed;
+
+struct host_cmd_tlv_mac_addr {
+ struct nxpwifi_ie_types_header header;
+ u8 mac_addr[ETH_ALEN];
+} __packed;
+
+struct host_cmd_tlv_channel_band {
+ struct nxpwifi_ie_types_header header;
+ u8 band_config;
+ u8 channel;
+} __packed;
+
+struct host_cmd_tlv_ageout_timer {
+ struct nxpwifi_ie_types_header header;
+ __le32 sta_ao_timer;
+} __packed;
+
+struct host_cmd_tlv_power_constraint {
+ struct nxpwifi_ie_types_header header;
+ u8 constraint;
+} __packed;
+
+struct nxpwifi_ie_types_btcoex_scan_time {
+ struct nxpwifi_ie_types_header header;
+ u8 coex_scan;
+ u8 reserved;
+ __le16 min_scan_time;
+ __le16 max_scan_time;
+} __packed;
+
+struct nxpwifi_ie_types_btcoex_aggr_win_size {
+ struct nxpwifi_ie_types_header header;
+ u8 coex_win_size;
+ u8 tx_win_size;
+ u8 rx_win_size;
+ u8 reserved;
+} __packed;
+
+struct nxpwifi_ie_types_robust_coex {
+ struct nxpwifi_ie_types_header header;
+ __le32 mode;
+} __packed;
+
+#define NXPWIFI_VERSION_STR_LENGTH 128
+
+struct host_cmd_ds_version_ext {
+ u8 version_str_sel;
+ char version_str[NXPWIFI_VERSION_STR_LENGTH];
+} __packed;
+
+struct host_cmd_ds_mgmt_frame_reg {
+ __le16 action;
+ __le32 mask;
+} __packed;
+
+struct host_cmd_ds_remain_on_chan {
+ __le16 action;
+ u8 status;
+ u8 reserved;
+ u8 band_cfg;
+ u8 channel;
+ __le32 duration;
+} __packed;
+
+struct host_cmd_ds_802_11_ibss_status {
+ __le16 action;
+ __le16 enable;
+ u8 bssid[ETH_ALEN];
+ __le16 beacon_interval;
+ __le16 atim_window;
+ __le16 use_g_rate_protect;
+} __packed;
+
+struct nxpwifi_fw_mef_entry {
+ u8 mode;
+ u8 action;
+ __le16 exprsize;
+ u8 expr[];
+} __packed;
+
+struct host_cmd_ds_mef_cfg {
+ __le32 criteria;
+ __le16 num_entries;
+ u8 mef_entry_data[];
+} __packed;
+
+#define CONNECTION_TYPE_INFRA 0
+#define CONNECTION_TYPE_AP 2
+
+struct host_cmd_ds_set_bss_mode {
+ u8 con_type;
+} __packed;
+
+struct host_cmd_ds_pcie_details {
+ /* TX buffer descriptor ring address */
+ __le32 txbd_addr_lo;
+ __le32 txbd_addr_hi;
+ /* TX buffer descriptor ring count */
+ __le32 txbd_count;
+
+ /* RX buffer descriptor ring address */
+ __le32 rxbd_addr_lo;
+ __le32 rxbd_addr_hi;
+ /* RX buffer descriptor ring count */
+ __le32 rxbd_count;
+
+ /* Event buffer descriptor ring address */
+ __le32 evtbd_addr_lo;
+ __le32 evtbd_addr_hi;
+ /* Event buffer descriptor ring count */
+ __le32 evtbd_count;
+
+ /* Sleep cookie buffer physical address */
+ __le32 sleep_cookie_addr_lo;
+ __le32 sleep_cookie_addr_hi;
+} __packed;
+
+struct nxpwifi_ie_types_rssi_threshold {
+ struct nxpwifi_ie_types_header header;
+ u8 abs_value;
+ u8 evt_freq;
+} __packed;
+
+#define NXPWIFI_DFS_REC_HDR_LEN 8
+#define NXPWIFI_DFS_REC_HDR_NUM 10
+#define NXPWIFI_BIN_COUNTER_LEN 7
+
+struct nxpwifi_radar_det_event {
+ __le32 detect_count;
+ u8 reg_domain; /*1=fcc, 2=etsi, 3=mic*/
+ u8 det_type; /*0=none, 1=pw(chirp), 2=pri(radar)*/
+ __le16 pw_chirp_type;
+ u8 pw_chirp_idx;
+ u8 pw_value;
+ u8 pri_radar_type;
+ u8 pri_bincnt;
+ u8 bin_counter[NXPWIFI_BIN_COUNTER_LEN];
+ u8 num_dfs_records;
+ u8 dfs_record_hdr[NXPWIFI_DFS_REC_HDR_NUM][NXPWIFI_DFS_REC_HDR_LEN];
+ __le32 passed;
+} __packed;
+
+struct nxpwifi_ie_types_multi_chan_info {
+ struct nxpwifi_ie_types_header header;
+ __le16 status;
+ u8 tlv_buffer[];
+} __packed;
+
+struct nxpwifi_ie_types_mc_group_info {
+ struct nxpwifi_ie_types_header header;
+ u8 chan_group_id;
+ u8 chan_buf_weight;
+ u8 band_config;
+ u8 chan_num;
+ __le32 chan_time;
+ __le32 reserved;
+ union {
+ u8 sdio_func_num;
+ u8 usb_ep_num;
+ } hid_num;
+ u8 intf_num;
+ u8 bss_type_numlist[];
+} __packed;
+
+#define MEAS_RPT_MAP_RADAR_MASK 0x08
+#define MEAS_RPT_MAP_RADAR_SHIFT_BIT 3
+
+struct nxpwifi_ie_types_chan_rpt_data {
+ struct nxpwifi_ie_types_header header;
+ u8 meas_rpt_map;
+} __packed;
+
+struct host_cmd_ds_802_11_subsc_evt {
+ __le16 action;
+ __le16 events;
+} __packed;
+
+struct chan_switch_result {
+ u8 cur_chan;
+ u8 status;
+ u8 reason;
+} __packed;
+
+struct nxpwifi_ie {
+ __le16 ie_index;
+ __le16 mgmt_subtype_mask;
+ __le16 ie_length;
+ u8 ie_buffer[IEEE_MAX_IE_SIZE];
+} __packed;
+
+#define MAX_MGMT_IE_INDEX 16
+struct nxpwifi_ie_list {
+ __le16 type;
+ __le16 len;
+ struct nxpwifi_ie ie_list[MAX_MGMT_IE_INDEX];
+} __packed;
+
+struct coalesce_filt_field_param {
+ u8 operation;
+ u8 operand_len;
+ __le16 offset;
+ u8 operand_byte_stream[4];
+};
+
+struct coalesce_receive_filt_rule {
+ struct nxpwifi_ie_types_header header;
+ u8 num_of_fields;
+ u8 pkt_type;
+ __le16 max_coalescing_delay;
+ struct coalesce_filt_field_param params[];
+} __packed;
+
+struct host_cmd_ds_coalesce_cfg {
+ __le16 action;
+ __le16 num_of_rules;
+ u8 rule_data[];
+} __packed;
+
+struct host_cmd_ds_multi_chan_policy {
+ __le16 action;
+ __le16 policy;
+} __packed;
+
+struct host_cmd_ds_robust_coex {
+ __le16 action;
+ __le16 reserved;
+} __packed;
+
+struct host_cmd_ds_wakeup_reason {
+ __le16 wakeup_reason;
+} __packed;
+
+struct host_cmd_ds_gtk_rekey_params {
+ __le16 action;
+ u8 kck[NL80211_KCK_LEN];
+ u8 kek[NL80211_KEK_LEN];
+ __le32 replay_ctr_low;
+ __le32 replay_ctr_high;
+} __packed;
+
+struct host_cmd_ds_chan_region_cfg {
+ __le16 action;
+} __packed;
+
+struct host_cmd_ds_pkt_aggr_ctrl {
+ __le16 action;
+ __le16 enable;
+ __le16 tx_aggr_max_size;
+ __le16 tx_aggr_max_num;
+ __le16 tx_aggr_align;
+} __packed;
+
+struct host_cmd_ds_sta_configure {
+ __le16 action;
+ u8 tlv_buffer[];
+} __packed;
+
+struct nxpwifi_ie_types_sta_flag {
+ struct nxpwifi_ie_types_header header;
+ __le32 sta_flags;
+} __packed;
+
+struct host_cmd_ds_add_station {
+ __le16 action;
+ __le16 aid;
+ u8 peer_mac[ETH_ALEN];
+ __le32 listen_interval;
+ __le16 cap_info;
+ u8 tlv[];
+} __packed;
+
+struct host_cmd_ds_command {
+ __le16 command;
+ __le16 size;
+ __le16 seq_num;
+ __le16 result;
+ union {
+ struct host_cmd_ds_get_hw_spec hw_spec;
+ struct host_cmd_ds_mac_control mac_ctrl;
+ struct host_cmd_ds_802_11_mac_address mac_addr;
+ struct host_cmd_ds_mac_multicast_adr mc_addr;
+ struct host_cmd_ds_802_11_get_log get_log;
+ struct host_cmd_ds_802_11_rssi_info rssi_info;
+ struct host_cmd_ds_802_11_rssi_info_rsp rssi_info_rsp;
+ struct host_cmd_ds_802_11_snmp_mib smib;
+ struct host_cmd_ds_tx_rate_query tx_rate;
+ struct host_cmd_ds_tx_rate_cfg tx_rate_cfg;
+ struct host_cmd_ds_txpwr_cfg txp_cfg;
+ struct host_cmd_ds_rf_tx_pwr txp;
+ struct host_cmd_ds_rf_ant_mimo ant_mimo;
+ struct host_cmd_ds_rf_ant_siso ant_siso;
+ struct host_cmd_ds_802_11_ps_mode_enh psmode_enh;
+ struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg;
+ struct host_cmd_ds_802_11_scan scan;
+ struct host_cmd_ds_802_11_scan_ext ext_scan;
+ struct host_cmd_ds_802_11_scan_rsp scan_resp;
+ struct host_cmd_ds_802_11_bg_scan_config bg_scan_config;
+ struct host_cmd_ds_802_11_bg_scan_query bg_scan_query;
+ struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp;
+ struct host_cmd_ds_802_11_associate associate;
+ struct host_cmd_ds_802_11_associate_rsp associate_rsp;
+ struct host_cmd_ds_802_11_deauthenticate deauth;
+ struct host_cmd_ds_802_11d_domain_info domain_info;
+ struct host_cmd_ds_802_11d_domain_info_rsp domain_info_resp;
+ struct host_cmd_ds_11n_addba_req add_ba_req;
+ struct host_cmd_ds_11n_addba_rsp add_ba_rsp;
+ struct host_cmd_ds_11n_delba del_ba;
+ struct host_cmd_ds_txbuf_cfg tx_buf;
+ struct host_cmd_ds_amsdu_aggr_ctrl amsdu_aggr_ctrl;
+ struct host_cmd_ds_11n_cfg htcfg;
+ struct host_cmd_ds_wmm_get_status get_wmm_status;
+ struct host_cmd_ds_802_11_key_material key_material;
+ struct host_cmd_ds_version_ext verext;
+ struct host_cmd_ds_mgmt_frame_reg reg_mask;
+ struct host_cmd_ds_remain_on_chan roc_cfg;
+ struct host_cmd_ds_802_11_ibss_status ibss_coalescing;
+ struct host_cmd_ds_mef_cfg mef_cfg;
+ struct host_cmd_ds_mem_access mem;
+ struct host_cmd_ds_mac_reg_access mac_reg;
+ struct host_cmd_ds_bbp_reg_access bbp_reg;
+ struct host_cmd_ds_rf_reg_access rf_reg;
+ struct host_cmd_ds_pmic_reg_access pmic_reg;
+ struct host_cmd_ds_set_bss_mode bss_mode;
+ struct host_cmd_ds_pcie_details pcie_host_spec;
+ struct host_cmd_ds_802_11_eeprom_access eeprom;
+ struct host_cmd_ds_802_11_subsc_evt subsc_evt;
+ struct host_cmd_ds_sys_config uap_sys_config;
+ struct host_cmd_ds_sta_deauth sta_deauth;
+ struct host_cmd_ds_sta_list sta_list;
+ struct host_cmd_11ac_vht_cfg vht_cfg;
+ struct host_cmd_ds_coalesce_cfg coalesce_cfg;
+ struct host_cmd_ds_chan_rpt_req chan_rpt_req;
+ struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg;
+ struct host_cmd_ds_multi_chan_policy mc_policy;
+ struct host_cmd_ds_robust_coex coex;
+ struct host_cmd_ds_wakeup_reason hs_wakeup_reason;
+ struct host_cmd_ds_gtk_rekey_params rekey;
+ struct host_cmd_ds_chan_region_cfg reg_cfg;
+ struct host_cmd_ds_pkt_aggr_ctrl pkt_aggr_ctrl;
+ struct host_cmd_ds_sta_configure sta_cfg;
+ struct host_cmd_ds_add_station sta_info;
+ } params;
+} __packed;
+
+struct nxpwifi_opt_sleep_confirm {
+ __le16 command;
+ __le16 size;
+ __le16 seq_num;
+ __le16 result;
+ __le16 action;
+ __le16 resp_ctrl;
+} __packed;
+
+struct hw_spec_max_conn {
+ struct nxpwifi_ie_types_header header;
+ u8 reserved;
+ u8 max_sta_conn;
+} __packed;
+
+#endif /* !_NXPWIFI_FW_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 19/43] wifi: nxpwifi: add ie.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (17 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 18/43] wifi: nxpwifi: add fw.h David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 20/43] wifi: nxpwifi: add init.c David Lin
` (25 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/ie.c | 502 ++++++++++++++++++++++++++
1 file changed, 502 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/ie.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/ie.c b/drivers/net/wireless/nxp/nxpwifi/ie.c
new file mode 100644
index 000000000000..cfe16e68d068
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/ie.c
@@ -0,0 +1,502 @@
+// 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: -1: yes, current IE index is used by someone else.
+ * 0: no, current IE index is NOT used by other interface.
+ */
+static int
+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 -1;
+ }
+ }
+
+ return 0;
+}
+
+/* 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 -1;
+}
+
+/* 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 -1;
+ 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 -1;
+ if (index == NXPWIFI_AUTO_IDX_MASK)
+ return -1;
+
+ 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 -1;
+ /* Check if this index is being used on any
+ * other interface.
+ */
+ if (nxpwifi_ie_index_used_by_other_intf(priv, index))
+ return -1;
+
+ 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 ieee_types_header *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 ieee_types_header *)vendor_ie;
+ if (le16_to_cpu(ie->ie_length) + vs_ie->len + 2 >
+ IEEE_MAX_IE_SIZE)
+ return -EINVAL;
+ memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length),
+ vs_ie, vs_ie->len + 2);
+ le16_unaligned_add_cpu(&ie->ie_length, vs_ie->len + 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 ieee_types_header *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 err = 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 ieee_types_header)) {
+ hdr = (void *)(info->tail + parsed_len);
+ token_len = hdr->len + sizeof(struct ieee_types_header);
+ if (token_len > left_len) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ switch (hdr->element_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) {
+ err = -EINVAL;
+ goto out;
+ }
+ 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 ieee_types_header);
+ if (ie_len + token_len > IEEE_MAX_IE_SIZE) {
+ err = -EINVAL;
+ goto out;
+ }
+ memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, token_len);
+ ie_len += token_len;
+ }
+
+ if (!ie_len)
+ goto out;
+
+ 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);
+
+ if (nxpwifi_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, NULL,
+ NULL, NULL)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ priv->gen_idx = gen_idx;
+
+ out:
+ kfree(gen_ie);
+ return err;
+}
+
+/* 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;
+ if (nxpwifi_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx,
+ NULL, &priv->proberesp_idx,
+ NULL, &priv->assocresp_idx)) {
+ ret = -1;
+ 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;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 20/43] wifi: nxpwifi: add init.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (18 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 19/43] wifi: nxpwifi: add ie.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 21/43] wifi: nxpwifi: add ioctl.h David Lin
` (24 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/init.c | 696 ++++++++++++++++++++++++
1 file changed, 696 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/init.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/init.c b/drivers/net/wireless/nxp/nxpwifi/init.c
new file mode 100644
index 000000000000..792eb346a797
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/init.c
@@ -0,0 +1,696 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: HW/FW Initialization
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cmdevt.h"
+#include "wmm.h"
+#include "11n.h"
+
+/* This function adds a BSS priority table to the table list.
+ *
+ * The function allocates a new BSS priority table node and adds it to
+ * the end of BSS priority table list, kept in driver memory.
+ */
+static int nxpwifi_add_bss_prio_tbl(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_bss_prio_node *bss_prio;
+ struct nxpwifi_bss_prio_tbl *tbl = adapter->bss_prio_tbl;
+
+ bss_prio = kzalloc(sizeof(*bss_prio), GFP_KERNEL);
+ if (!bss_prio)
+ return -ENOMEM;
+
+ bss_prio->priv = priv;
+ INIT_LIST_HEAD(&bss_prio->list);
+
+ spin_lock_bh(&tbl[priv->bss_priority].bss_prio_lock);
+ list_add_tail(&bss_prio->list, &tbl[priv->bss_priority].bss_prio_head);
+ spin_unlock_bh(&tbl[priv->bss_priority].bss_prio_lock);
+
+ return 0;
+}
+
+static void wakeup_timer_fn(struct timer_list *t)
+{
+ struct nxpwifi_adapter *adapter = from_timer(adapter, t, wakeup_timer);
+
+ nxpwifi_dbg(adapter, ERROR, "Firmware wakeup failed\n");
+ adapter->hw_status = NXPWIFI_HW_STATUS_RESET;
+ nxpwifi_cancel_all_pending_cmd(adapter);
+
+ if (adapter->if_ops.card_reset)
+ adapter->if_ops.card_reset(adapter);
+}
+
+static void fw_dump_work(struct work_struct *work)
+{
+ struct nxpwifi_adapter *adapter =
+ container_of(work, struct nxpwifi_adapter, devdump_work.work);
+
+ nxpwifi_upload_device_dump(adapter);
+}
+
+/* This function initializes the private structure and sets default
+ * values to the members.
+ *
+ * Additionally, it also initializes all the locks and sets up all the
+ * lists.
+ */
+int nxpwifi_init_priv(struct nxpwifi_private *priv)
+{
+ u32 i;
+
+ priv->media_connected = false;
+ eth_broadcast_addr(priv->curr_addr);
+ priv->port_open = false;
+ priv->usb_port = NXPWIFI_USB_EP_DATA;
+ priv->pkt_tx_ctrl = 0;
+ priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->data_rate = 0; /* Initially indicate the rate as auto */
+ priv->is_data_rate_auto = true;
+ priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR;
+ priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR;
+
+ priv->auth_flag = 0;
+ priv->auth_alg = WLAN_AUTH_NONE;
+
+ priv->sec_info.wep_enabled = 0;
+ priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;
+ priv->sec_info.encryption_mode = 0;
+ for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++)
+ memset(&priv->wep_key[i], 0, sizeof(struct nxpwifi_wep_key));
+ priv->wep_key_curr_index = 0;
+ priv->curr_pkt_filter = HOST_ACT_MAC_DYNAMIC_BW_ENABLE |
+ HOST_ACT_MAC_RX_ON | HOST_ACT_MAC_TX_ON |
+ HOST_ACT_MAC_ETHERNETII_ENABLE;
+
+ priv->beacon_period = 100; /* beacon interval */
+ priv->attempted_bss_desc = NULL;
+ memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params));
+ priv->listen_interval = NXPWIFI_DEFAULT_LISTEN_INTERVAL;
+
+ memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid));
+ memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid));
+ memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf));
+ priv->assoc_rsp_size = 0;
+ priv->atim_window = 0;
+ priv->tx_power_level = 0;
+ priv->max_tx_power_level = 0;
+ priv->min_tx_power_level = 0;
+ priv->tx_ant = 0;
+ priv->rx_ant = 0;
+ priv->tx_rate = 0;
+ priv->rxpd_htinfo = 0;
+ priv->rxpd_rate = 0;
+ priv->rate_bitmap = 0;
+ priv->data_rssi_last = 0;
+ priv->data_rssi_avg = 0;
+ priv->data_nf_avg = 0;
+ priv->data_nf_last = 0;
+ priv->bcn_rssi_last = 0;
+ priv->bcn_rssi_avg = 0;
+ priv->bcn_nf_avg = 0;
+ priv->bcn_nf_last = 0;
+ memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie));
+ memset(&priv->aes_key, 0, sizeof(priv->aes_key));
+ priv->wpa_ie_len = 0;
+ priv->wpa_is_gtk_set = false;
+
+ memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf));
+ priv->assoc_tlv_buf_len = 0;
+ memset(&priv->wps, 0, sizeof(priv->wps));
+ memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf));
+ priv->gen_ie_buf_len = 0;
+ memset(priv->vs_ie, 0, sizeof(priv->vs_ie));
+
+ priv->wmm_required = true;
+ priv->wmm_enabled = false;
+ priv->wmm_qosinfo = 0;
+ priv->curr_bcn_buf = NULL;
+ priv->curr_bcn_size = 0;
+ priv->wps_ie = NULL;
+ priv->wps_ie_len = 0;
+ priv->ap_11n_enabled = 0;
+ memset(&priv->roc_cfg, 0, sizeof(priv->roc_cfg));
+
+ priv->scan_block = false;
+
+ priv->csa_chan = 0;
+ priv->csa_expire_time = 0;
+ priv->del_list_idx = 0;
+ priv->hs2_enabled = false;
+ memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID);
+
+ nxpwifi_init_11h_params(priv);
+
+ return nxpwifi_add_bss_prio_tbl(priv);
+}
+
+/* This function allocates buffers for members of the adapter
+ * structure.
+ *
+ * The memory allocated includes scan table, command buffers, and
+ * sleep confirm command buffer. In addition, the queues are
+ * also initialized.
+ */
+static int nxpwifi_allocate_adapter(struct nxpwifi_adapter *adapter)
+{
+ int ret;
+
+ /* Allocate command buffer */
+ ret = nxpwifi_alloc_cmd_buffer(adapter);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: failed to alloc cmd buffer\n",
+ __func__);
+ return -1;
+ }
+
+ adapter->sleep_cfm =
+ dev_alloc_skb(sizeof(struct nxpwifi_opt_sleep_confirm)
+ + INTF_HEADER_LEN);
+
+ if (!adapter->sleep_cfm) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: failed to alloc sleep cfm\t"
+ " cmd buffer\n", __func__);
+ return -1;
+ }
+ skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN);
+
+ return 0;
+}
+
+/* This function initializes the adapter structure and sets default
+ * values to the members of adapter.
+ *
+ * This also initializes the WMM related parameters in the driver private
+ * structures.
+ */
+static void nxpwifi_init_adapter(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_opt_sleep_confirm *sleep_cfm_buf = NULL;
+
+ skb_put(adapter->sleep_cfm, sizeof(struct nxpwifi_opt_sleep_confirm));
+
+ adapter->cmd_sent = false;
+ adapter->data_sent = true;
+
+ adapter->intf_hdr_len = INTF_HEADER_LEN;
+
+ adapter->cmd_resp_received = false;
+ adapter->event_received = false;
+ adapter->data_received = false;
+ adapter->assoc_resp_received = false;
+ adapter->priv_link_lost = NULL;
+ adapter->host_mlme_link_lost = false;
+
+ clear_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags);
+
+ adapter->hw_status = NXPWIFI_HW_STATUS_INITIALIZING;
+
+ adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_CAM;
+ adapter->ps_state = PS_STATE_AWAKE;
+ adapter->need_to_wakeup = false;
+
+ adapter->scan_mode = HOST_BSS_MODE_ANY;
+ adapter->specific_scan_time = NXPWIFI_SPECIFIC_SCAN_CHAN_TIME;
+ adapter->active_scan_time = NXPWIFI_ACTIVE_SCAN_CHAN_TIME;
+ adapter->passive_scan_time = NXPWIFI_PASSIVE_SCAN_CHAN_TIME;
+ adapter->scan_chan_gap_time = NXPWIFI_DEF_SCAN_CHAN_GAP_TIME;
+
+ adapter->scan_probes = 1;
+
+ adapter->multiple_dtim = 1;
+
+ /* default value in firmware will be used */
+ adapter->local_listen_interval = 0;
+
+ adapter->is_deep_sleep = false;
+
+ adapter->delay_null_pkt = false;
+ adapter->delay_to_ps = 1000;
+ adapter->enhanced_ps_mode = PS_MODE_AUTO;
+
+ /* Disable NULL Pkg generation by default */
+ adapter->gen_null_pkt = false;
+ /* Disable pps/uapsd mode by default */
+ adapter->pps_uapsd_mode = false;
+ adapter->pm_wakeup_card_req = false;
+
+ adapter->pm_wakeup_fw_try = false;
+
+ adapter->curr_tx_buf_size = NXPWIFI_TX_DATA_BUF_SIZE_2K;
+
+ clear_bit(NXPWIFI_IS_HS_CONFIGURED, &adapter->work_flags);
+ adapter->hs_cfg.conditions = cpu_to_le32(HS_CFG_COND_DEF);
+ adapter->hs_cfg.gpio = HS_CFG_GPIO_DEF;
+ adapter->hs_cfg.gap = HS_CFG_GAP_DEF;
+ adapter->hs_activated = false;
+
+ memset(adapter->event_body, 0, sizeof(adapter->event_body));
+ adapter->hw_dot_11n_dev_cap = 0;
+ adapter->hw_dev_mcs_support = 0;
+ adapter->sec_chan_offset = 0;
+
+ nxpwifi_wmm_init(adapter);
+ atomic_set(&adapter->tx_hw_pending, 0);
+
+ sleep_cfm_buf = (struct nxpwifi_opt_sleep_confirm *)
+ adapter->sleep_cfm->data;
+ memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len);
+ sleep_cfm_buf->command = cpu_to_le16(HOST_CMD_802_11_PS_MODE_ENH);
+ sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len);
+ sleep_cfm_buf->result = 0;
+ sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM);
+ sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED);
+
+ memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period));
+ adapter->tx_lock_flag = false;
+ adapter->null_pkt_interval = 0;
+ adapter->fw_bands = 0;
+ adapter->config_bands = 0;
+ adapter->fw_release_number = 0;
+ adapter->fw_cap_info = 0;
+ memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf));
+ adapter->event_cause = 0;
+ adapter->region_code = 0;
+ adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT;
+ memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter));
+ adapter->arp_filter_size = 0;
+ adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX;
+ adapter->key_api_major_ver = 0;
+ adapter->key_api_minor_ver = 0;
+ eth_broadcast_addr(adapter->perm_addr);
+ adapter->iface_limit.sta_intf = NXPWIFI_MAX_STA_NUM;
+ adapter->iface_limit.uap_intf = NXPWIFI_MAX_UAP_NUM;
+ adapter->active_scan_triggered = false;
+ timer_setup(&adapter->wakeup_timer, wakeup_timer_fn, 0);
+ adapter->devdump_len = 0;
+ INIT_DELAYED_WORK(&adapter->devdump_work, fw_dump_work);
+}
+
+/* This function sets trans_start per tx_queue
+ */
+void nxpwifi_set_trans_start(struct net_device *dev)
+{
+ int i;
+
+ for (i = 0; i < dev->num_tx_queues; i++)
+ txq_trans_cond_update(netdev_get_tx_queue(dev, i));
+
+ netif_trans_update(dev);
+}
+
+/* This function wakes up all queues in net_device
+ */
+void nxpwifi_wake_up_net_dev_queue(struct net_device *netdev,
+ struct nxpwifi_adapter *adapter)
+{
+ spin_lock_bh(&adapter->queue_lock);
+ netif_tx_wake_all_queues(netdev);
+ spin_unlock_bh(&adapter->queue_lock);
+}
+
+/* This function stops all queues in net_device
+ */
+void nxpwifi_stop_net_dev_queue(struct net_device *netdev,
+ struct nxpwifi_adapter *adapter)
+{
+ spin_lock_bh(&adapter->queue_lock);
+ netif_tx_stop_all_queues(netdev);
+ spin_unlock_bh(&adapter->queue_lock);
+}
+
+/* This function invalidates the list heads.
+ */
+static void nxpwifi_invalidate_lists(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_private *priv;
+ s32 i, j;
+
+ list_del(&adapter->cmd_free_q);
+ list_del(&adapter->cmd_pending_q);
+ list_del(&adapter->scan_pending_q);
+
+ for (i = 0; i < adapter->priv_num; i++)
+ list_del(&adapter->bss_prio_tbl[i].bss_prio_head);
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ priv = adapter->priv[i];
+ for (j = 0; j < MAX_NUM_TID; ++j)
+ list_del(&priv->wmm.tid_tbl_ptr[j].ra_list);
+ list_del(&priv->tx_ba_stream_tbl_ptr);
+ list_del(&priv->rx_reorder_tbl_ptr);
+ list_del(&priv->sta_list);
+ }
+ }
+}
+
+/* This function performs cleanup for adapter structure.
+ *
+ * The cleanup is done recursively, by canceling all pending
+ * commands, freeing the member buffers previously allocated
+ * (command buffers, scan table buffer, sleep confirm command
+ * buffer), stopping the timers and calling the cleanup routines
+ * for every interface.
+ */
+static void
+nxpwifi_adapter_cleanup(struct nxpwifi_adapter *adapter)
+{
+ del_timer(&adapter->wakeup_timer);
+ cancel_delayed_work_sync(&adapter->devdump_work);
+ nxpwifi_cancel_all_pending_cmd(adapter);
+ wake_up_interruptible(&adapter->cmd_wait_q.wait);
+ wake_up_interruptible(&adapter->hs_activate_wait_q);
+}
+
+void nxpwifi_free_cmd_buffers(struct nxpwifi_adapter *adapter)
+{
+ nxpwifi_invalidate_lists(adapter);
+
+ /* Free command buffer */
+ nxpwifi_dbg(adapter, INFO, "info: free cmd buffer\n");
+ nxpwifi_free_cmd_buffer(adapter);
+
+ if (adapter->sleep_cfm)
+ dev_kfree_skb_any(adapter->sleep_cfm);
+}
+
+/* This function intializes the lock variables and
+ * the list heads.
+ */
+int nxpwifi_init_lock_list(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_private *priv;
+ s32 i, j;
+
+ spin_lock_init(&adapter->int_lock);
+ spin_lock_init(&adapter->main_proc_lock);
+ spin_lock_init(&adapter->nxpwifi_cmd_lock);
+ spin_lock_init(&adapter->queue_lock);
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ priv = adapter->priv[i];
+ spin_lock_init(&priv->wmm.ra_list_spinlock);
+ spin_lock_init(&priv->curr_bcn_buf_lock);
+ spin_lock_init(&priv->sta_list_spinlock);
+ }
+ }
+
+ /* Initialize cmd_free_q */
+ INIT_LIST_HEAD(&adapter->cmd_free_q);
+ /* Initialize cmd_pending_q */
+ INIT_LIST_HEAD(&adapter->cmd_pending_q);
+ /* Initialize scan_pending_q */
+ INIT_LIST_HEAD(&adapter->scan_pending_q);
+
+ spin_lock_init(&adapter->cmd_free_q_lock);
+ spin_lock_init(&adapter->cmd_pending_q_lock);
+ spin_lock_init(&adapter->scan_pending_q_lock);
+ spin_lock_init(&adapter->rx_proc_lock);
+
+ skb_queue_head_init(&adapter->rx_data_q);
+ skb_queue_head_init(&adapter->tx_data_q);
+
+ for (i = 0; i < adapter->priv_num; ++i) {
+ INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head);
+ spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock);
+ }
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (!adapter->priv[i])
+ continue;
+ priv = adapter->priv[i];
+ for (j = 0; j < MAX_NUM_TID; ++j)
+ INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list);
+ INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
+ INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
+ INIT_LIST_HEAD(&priv->sta_list);
+ skb_queue_head_init(&priv->bypass_txq);
+
+ spin_lock_init(&priv->tx_ba_stream_tbl_lock);
+ spin_lock_init(&priv->rx_reorder_tbl_lock);
+
+ spin_lock_init(&priv->ack_status_lock);
+ idr_init(&priv->ack_status_frames);
+ }
+
+ return 0;
+}
+
+/* This function initializes the firmware.
+ *
+ * The following operations are performed sequentially -
+ * - Allocate adapter structure
+ * - Initialize the adapter structure
+ * - Initialize the private structure
+ * - Add BSS priority tables to the adapter structure
+ * - For each interface, send the init commands to firmware
+ * - Send the first command in command pending queue, if available
+ */
+int nxpwifi_init_fw(struct nxpwifi_adapter *adapter)
+{
+ int ret;
+ struct nxpwifi_private *priv;
+ u8 i, first_sta = true;
+ int is_cmd_pend_q_empty;
+
+ adapter->hw_status = NXPWIFI_HW_STATUS_INITIALIZING;
+
+ /* Allocate memory for member of adapter structure */
+ ret = nxpwifi_allocate_adapter(adapter);
+ if (ret)
+ return -1;
+
+ /* Initialize adapter structure */
+ nxpwifi_init_adapter(adapter);
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ priv = adapter->priv[i];
+
+ /* Initialize private structure */
+ ret = nxpwifi_init_priv(priv);
+ if (ret)
+ return -1;
+ }
+ }
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ ret = nxpwifi_sta_init_cmd(adapter->priv[i],
+ first_sta, true);
+ if (ret == -1)
+ return -1;
+
+ first_sta = false;
+ }
+ }
+
+ spin_lock_bh(&adapter->cmd_pending_q_lock);
+ is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q);
+ spin_unlock_bh(&adapter->cmd_pending_q_lock);
+ if (!is_cmd_pend_q_empty) {
+ /* Send the first command in queue and return */
+ if (nxpwifi_main_process(adapter) != -1)
+ ret = -EINPROGRESS;
+ } else {
+ adapter->hw_status = NXPWIFI_HW_STATUS_READY;
+ }
+
+ return ret;
+}
+
+/* This function deletes the BSS priority tables.
+ *
+ * The function traverses through all the allocated BSS priority nodes
+ * in every BSS priority table and frees them.
+ */
+static void nxpwifi_delete_bss_prio_tbl(struct nxpwifi_private *priv)
+{
+ int i;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_bss_prio_node *bssprio_node, *tmp_node;
+ struct list_head *head;
+ spinlock_t *lock; /* bss priority lock */
+
+ for (i = 0; i < adapter->priv_num; ++i) {
+ head = &adapter->bss_prio_tbl[i].bss_prio_head;
+ lock = &adapter->bss_prio_tbl[i].bss_prio_lock;
+ nxpwifi_dbg(adapter, INFO,
+ "info: delete BSS priority table,\t"
+ "bss_type = %d, bss_num = %d, i = %d,\t"
+ "head = %p\n",
+ priv->bss_type, priv->bss_num, i, head);
+
+ {
+ spin_lock_bh(lock);
+ list_for_each_entry_safe(bssprio_node, tmp_node, head,
+ list) {
+ if (bssprio_node->priv == priv) {
+ nxpwifi_dbg(adapter, INFO,
+ "info: Delete\t"
+ "node %p, next = %p\n",
+ bssprio_node, tmp_node);
+ list_del(&bssprio_node->list);
+ kfree(bssprio_node);
+ }
+ }
+ spin_unlock_bh(lock);
+ }
+ }
+}
+
+/* This function frees the private structure, including cleans
+ * up the TX and RX queues and frees the BSS priority tables.
+ */
+void nxpwifi_free_priv(struct nxpwifi_private *priv)
+{
+ nxpwifi_clean_txrx(priv);
+ nxpwifi_delete_bss_prio_tbl(priv);
+ nxpwifi_free_curr_bcn(priv);
+}
+
+/* This function is used to shutdown the driver.
+ *
+ * The following operations are performed sequentially -
+ * - Check if already shut down
+ * - Make sure the main process has stopped
+ * - Clean up the Tx and Rx queues
+ * - Delete BSS priority tables
+ * - Free the adapter
+ * - Notify completion
+ */
+void
+nxpwifi_shutdown_drv(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_private *priv;
+ s32 i;
+ struct sk_buff *skb;
+
+ /* nxpwifi already shutdown */
+ if (adapter->hw_status == NXPWIFI_HW_STATUS_NOT_READY)
+ return;
+
+ /* cancel current command */
+ if (adapter->curr_cmd) {
+ nxpwifi_dbg(adapter, WARN,
+ "curr_cmd is still in processing\n");
+ del_timer_sync(&adapter->cmd_timer);
+ nxpwifi_recycle_cmd_node(adapter, adapter->curr_cmd);
+ adapter->curr_cmd = NULL;
+ }
+
+ /* shut down nxpwifi */
+ nxpwifi_dbg(adapter, MSG,
+ "info: shutdown nxpwifi...\n");
+
+ /* Clean up Tx/Rx queues and delete BSS priority table */
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ priv = adapter->priv[i];
+
+ nxpwifi_abort_cac(priv);
+ nxpwifi_free_priv(priv);
+ }
+ }
+
+ atomic_set(&adapter->tx_queued, 0);
+ while ((skb = skb_dequeue(&adapter->tx_data_q)))
+ nxpwifi_write_data_complete(adapter, skb, 0, 0);
+
+ spin_lock_bh(&adapter->rx_proc_lock);
+
+ while ((skb = skb_dequeue(&adapter->rx_data_q))) {
+ struct nxpwifi_rxinfo *rx_info = NXPWIFI_SKB_RXCB(skb);
+
+ atomic_dec(&adapter->rx_pending);
+ priv = adapter->priv[rx_info->bss_num];
+ if (priv)
+ priv->stats.rx_dropped++;
+
+ dev_kfree_skb_any(skb);
+ }
+
+ spin_unlock_bh(&adapter->rx_proc_lock);
+
+ nxpwifi_adapter_cleanup(adapter);
+
+ adapter->hw_status = NXPWIFI_HW_STATUS_NOT_READY;
+}
+
+/* This function downloads the firmware to the card.
+ *
+ * The actual download is preceded by two sanity checks -
+ * - Check if firmware is already running
+ * - Check if the interface is the winner to download the firmware
+ *
+ * ...and followed by another -
+ * - Check if the firmware is downloaded successfully
+ *
+ * After download is successfully completed, the host interrupts are enabled.
+ */
+int nxpwifi_dnld_fw(struct nxpwifi_adapter *adapter,
+ struct nxpwifi_fw_image *pmfw)
+{
+ int ret;
+ u32 poll_num = 1;
+
+ /* check if firmware is already running */
+ ret = adapter->if_ops.check_fw_status(adapter, poll_num);
+ if (!ret) {
+ nxpwifi_dbg(adapter, MSG,
+ "WLAN FW already running! Skip FW dnld\n");
+ return 0;
+ }
+
+ /* check if we are the winner for downloading FW */
+ if (adapter->if_ops.check_winner_status) {
+ adapter->winner = 0;
+ ret = adapter->if_ops.check_winner_status(adapter);
+
+ poll_num = MAX_FIRMWARE_POLL_TRIES;
+ if (ret) {
+ nxpwifi_dbg(adapter, MSG,
+ "WLAN read winner status failed!\n");
+ return ret;
+ }
+
+ if (!adapter->winner) {
+ nxpwifi_dbg(adapter, MSG,
+ "WLAN is not the winner! Skip FW dnld\n");
+ goto poll_fw;
+ }
+ }
+
+ if (pmfw) {
+ /* Download firmware with helper */
+ ret = adapter->if_ops.prog_fw(adapter, pmfw);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "prog_fw failed ret=%#x\n", ret);
+ return ret;
+ }
+ }
+
+poll_fw:
+ /* Check if the firmware is downloaded successfully or not */
+ ret = adapter->if_ops.check_fw_status(adapter, poll_num);
+ if (ret)
+ nxpwifi_dbg(adapter, ERROR,
+ "FW failed to be active in time\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nxpwifi_dnld_fw);
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 21/43] wifi: nxpwifi: add ioctl.h
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (19 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 20/43] wifi: nxpwifi: add init.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 22/43] wifi: nxpwifi: add join.c David Lin
` (23 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/ioctl.h | 445 +++++++++++++++++++++++
1 file changed, 445 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/ioctl.h
diff --git a/drivers/net/wireless/nxp/nxpwifi/ioctl.h b/drivers/net/wireless/nxp/nxpwifi/ioctl.h
new file mode 100644
index 000000000000..b55dfb872b76
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/ioctl.h
@@ -0,0 +1,445 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NXP Wireless LAN device driver: ioctl data structures & APIs
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#ifndef _NXPWIFI_IOCTL_H_
+#define _NXPWIFI_IOCTL_H_
+
+#include <net/lib80211.h>
+
+enum {
+ NXPWIFI_SCAN_TYPE_UNCHANGED = 0,
+ NXPWIFI_SCAN_TYPE_ACTIVE,
+ NXPWIFI_SCAN_TYPE_PASSIVE
+};
+
+#define NXPWIFI_PROMISC_MODE 1
+#define NXPWIFI_MULTICAST_MODE 2
+#define NXPWIFI_ALL_MULTI_MODE 4
+#define NXPWIFI_MAX_MULTICAST_LIST_SIZE 32
+
+struct nxpwifi_multicast_list {
+ u32 mode;
+ u32 num_multicast_addr;
+ u8 mac_list[NXPWIFI_MAX_MULTICAST_LIST_SIZE][ETH_ALEN];
+};
+
+struct nxpwifi_chan_freq {
+ u32 channel;
+ u32 freq;
+};
+
+struct nxpwifi_ssid_bssid {
+ struct cfg80211_ssid ssid;
+ u8 bssid[ETH_ALEN];
+};
+
+enum {
+ BAND_B = 1,
+ BAND_G = 2,
+ BAND_A = 4,
+ BAND_GN = 8,
+ BAND_AN = 16,
+ BAND_AAC = 32,
+};
+
+#define NXPWIFI_WPA_PASSHPHRASE_LEN 64
+struct wpa_param {
+ u8 pairwise_cipher_wpa;
+ u8 pairwise_cipher_wpa2;
+ u8 group_cipher;
+ u32 length;
+ u8 passphrase[NXPWIFI_WPA_PASSHPHRASE_LEN];
+};
+
+struct wep_key {
+ u8 key_index;
+ u8 is_default;
+ u16 length;
+ u8 key[WLAN_KEY_LEN_WEP104];
+};
+
+#define KEY_MGMT_ON_HOST 0x03
+#define NXPWIFI_AUTH_MODE_AUTO 0xFF
+#define BAND_CONFIG_BG 0x00
+#define BAND_CONFIG_A 0x01
+#define NXPWIFI_SEC_CHAN_BELOW 0x30
+#define NXPWIFI_SEC_CHAN_ABOVE 0x10
+#define NXPWIFI_SUPPORTED_RATES 14
+#define NXPWIFI_SUPPORTED_RATES_EXT 32
+#define NXPWIFI_PRIO_BK 2
+#define NXPWIFI_PRIO_VI 5
+#define NXPWIFI_SUPPORTED_CHANNELS 2
+#define NXPWIFI_OPERATING_CLASSES 16
+
+struct nxpwifi_uap_bss_param {
+ u8 mac_addr[ETH_ALEN];
+ u8 channel;
+ u8 band_cfg;
+ u16 rts_threshold;
+ u16 frag_threshold;
+ u8 retry_limit;
+ struct nxpwifi_802_11_ssid ssid;
+ u8 bcast_ssid_ctl;
+ u8 radio_ctl;
+ u8 dtim_period;
+ u16 beacon_period;
+ u16 auth_mode;
+ u16 protocol;
+ u16 key_mgmt;
+ u16 key_mgmt_operation;
+ struct wpa_param wpa_cfg;
+ struct wep_key wep_cfg[NUM_WEP_KEYS];
+ struct ieee80211_ht_cap ht_cap;
+ struct ieee80211_vht_cap vht_cap;
+ u8 rates[NXPWIFI_SUPPORTED_RATES];
+ u32 sta_ao_timer;
+ u32 ps_sta_ao_timer;
+ u8 qos_info;
+ u8 power_constraint;
+ struct nxpwifi_types_wmm_info wmm_info;
+};
+
+struct nxpwifi_ds_get_stats {
+ u32 mcast_tx_frame;
+ u32 failed;
+ u32 retry;
+ u32 multi_retry;
+ u32 frame_dup;
+ u32 rts_success;
+ u32 rts_failure;
+ u32 ack_failure;
+ u32 rx_frag;
+ u32 mcast_rx_frame;
+ u32 fcs_error;
+ u32 tx_frame;
+ u32 wep_icv_error[4];
+ u32 bcn_rcv_cnt;
+ u32 bcn_miss_cnt;
+};
+
+#define NXPWIFI_MAX_VER_STR_LEN 128
+
+struct nxpwifi_ver_ext {
+ u32 version_str_sel;
+ char version_str[NXPWIFI_MAX_VER_STR_LEN];
+};
+
+struct nxpwifi_bss_info {
+ u32 bss_mode;
+ struct cfg80211_ssid ssid;
+ u32 bss_chan;
+ u8 country_code[3];
+ u32 media_connected;
+ u32 max_power_level;
+ u32 min_power_level;
+ signed int bcn_nf_last;
+ u32 wep_status;
+ u32 is_hs_configured;
+ u32 is_deep_sleep;
+ u8 bssid[ETH_ALEN];
+};
+
+struct nxpwifi_sta_info {
+ u8 peer_mac[ETH_ALEN];
+ struct station_parameters *params;
+};
+
+#define MAX_NUM_TID 8
+
+#define MAX_RX_WINSIZE 64
+
+struct nxpwifi_ds_rx_reorder_tbl {
+ u16 tid;
+ u8 ta[ETH_ALEN];
+ u32 start_win;
+ u32 win_size;
+ u32 buffer[MAX_RX_WINSIZE];
+};
+
+struct nxpwifi_ds_tx_ba_stream_tbl {
+ u16 tid;
+ u8 ra[ETH_ALEN];
+ u8 amsdu;
+};
+
+#define DBG_CMD_NUM 5
+#define NXPWIFI_DBG_SDIO_MP_NUM 10
+
+struct nxpwifi_debug_info {
+ unsigned int debug_mask;
+ u32 int_counter;
+ u32 packets_out[MAX_NUM_TID];
+ u32 tx_buf_size;
+ u32 curr_tx_buf_size;
+ u32 tx_tbl_num;
+ struct nxpwifi_ds_tx_ba_stream_tbl
+ tx_tbl[NXPWIFI_MAX_TX_BASTREAM_SUPPORTED];
+ u32 rx_tbl_num;
+ struct nxpwifi_ds_rx_reorder_tbl rx_tbl
+ [NXPWIFI_MAX_RX_BASTREAM_SUPPORTED];
+ u16 ps_mode;
+ u32 ps_state;
+ u8 is_deep_sleep;
+ u8 pm_wakeup_card_req;
+ u32 pm_wakeup_fw_try;
+ u8 is_hs_configured;
+ u8 hs_activated;
+ u32 num_cmd_host_to_card_failure;
+ u32 num_cmd_sleep_cfm_host_to_card_failure;
+ u32 num_tx_host_to_card_failure;
+ u32 num_event_deauth;
+ u32 num_event_disassoc;
+ u32 num_event_link_lost;
+ u32 num_cmd_deauth;
+ u32 num_cmd_assoc_success;
+ u32 num_cmd_assoc_failure;
+ u32 num_tx_timeout;
+ u8 is_cmd_timedout;
+ u16 timeout_cmd_id;
+ u16 timeout_cmd_act;
+ u16 last_cmd_id[DBG_CMD_NUM];
+ u16 last_cmd_act[DBG_CMD_NUM];
+ u16 last_cmd_index;
+ u16 last_cmd_resp_id[DBG_CMD_NUM];
+ u16 last_cmd_resp_index;
+ u16 last_event[DBG_CMD_NUM];
+ u16 last_event_index;
+ u8 data_sent;
+ u8 cmd_sent;
+ u8 cmd_resp_received;
+ u8 event_received;
+ u32 last_mp_wr_bitmap[NXPWIFI_DBG_SDIO_MP_NUM];
+ u32 last_mp_wr_ports[NXPWIFI_DBG_SDIO_MP_NUM];
+ u32 last_mp_wr_len[NXPWIFI_DBG_SDIO_MP_NUM];
+ u32 last_mp_curr_wr_port[NXPWIFI_DBG_SDIO_MP_NUM];
+ u8 last_sdio_mp_index;
+};
+
+#define NXPWIFI_KEY_INDEX_UNICAST 0x40000000
+#define PN_LEN 16
+
+struct nxpwifi_ds_encrypt_key {
+ u32 key_disable;
+ u32 key_index;
+ u32 key_len;
+ u8 key_material[WLAN_MAX_KEY_LEN];
+ u8 mac_addr[ETH_ALEN];
+ u8 pn[PN_LEN]; /* packet number */
+ u8 pn_len;
+ u8 is_igtk_key;
+ u8 is_current_wep_key;
+ u8 is_rx_seq_valid;
+ u8 is_igtk_def_key;
+};
+
+struct nxpwifi_power_cfg {
+ u32 is_power_auto;
+ u32 is_power_fixed;
+ u32 power_level;
+};
+
+struct nxpwifi_ds_hs_cfg {
+ u32 is_invoke_hostcmd;
+ /* Bit0: non-unicast data
+ * Bit1: unicast data
+ * Bit2: mac events
+ * Bit3: magic packet
+ */
+ u32 conditions;
+ u32 gpio;
+ u32 gap;
+};
+
+struct nxpwifi_ds_wakeup_reason {
+ u16 hs_wakeup_reason;
+};
+
+#define DEEP_SLEEP_ON 1
+#define DEEP_SLEEP_OFF 0
+#define DEEP_SLEEP_IDLE_TIME 100
+#define PS_MODE_AUTO 1
+
+struct nxpwifi_ds_auto_ds {
+ u16 auto_ds;
+ u16 idle_time;
+};
+
+struct nxpwifi_ds_pm_cfg {
+ union {
+ u32 ps_mode;
+ struct nxpwifi_ds_hs_cfg hs_cfg;
+ struct nxpwifi_ds_auto_ds auto_deep_sleep;
+ u32 sleep_period;
+ } param;
+};
+
+struct nxpwifi_11ac_vht_cfg {
+ u8 band_config;
+ u8 misc_config;
+ u32 cap_info;
+ u32 mcs_tx_set;
+ u32 mcs_rx_set;
+};
+
+struct nxpwifi_ds_11n_tx_cfg {
+ u16 tx_htcap;
+ u16 tx_htinfo;
+ u16 misc_config; /* Needed for 802.11AC cards only */
+};
+
+struct nxpwifi_ds_11n_amsdu_aggr_ctrl {
+ u16 enable;
+ u16 curr_buf_size;
+};
+
+struct nxpwifi_ds_ant_cfg {
+ u32 tx_ant;
+ u32 rx_ant;
+};
+
+#define NXPWIFI_NUM_OF_CMD_BUFFER 50
+#define NXPWIFI_SIZE_OF_CMD_BUFFER 2048
+
+enum {
+ NXPWIFI_IE_TYPE_GEN_IE = 0,
+ NXPWIFI_IE_TYPE_ARP_FILTER,
+};
+
+enum {
+ NXPWIFI_REG_MAC = 1,
+ NXPWIFI_REG_BBP,
+ NXPWIFI_REG_RF,
+ NXPWIFI_REG_PMIC,
+ NXPWIFI_REG_CAU,
+};
+
+struct nxpwifi_ds_reg_rw {
+ u32 type;
+ u32 offset;
+ u32 value;
+};
+
+#define MAX_EEPROM_DATA 256
+
+struct nxpwifi_ds_read_eeprom {
+ u16 offset;
+ u16 byte_count;
+ u8 value[MAX_EEPROM_DATA];
+};
+
+struct nxpwifi_ds_mem_rw {
+ u32 addr;
+ u32 value;
+};
+
+#define IEEE_MAX_IE_SIZE 256
+
+#define NXPWIFI_IE_HDR_SIZE (sizeof(struct nxpwifi_ie) - IEEE_MAX_IE_SIZE)
+
+struct nxpwifi_ds_misc_gen_ie {
+ u32 type;
+ u32 len;
+ u8 ie_data[IEEE_MAX_IE_SIZE];
+};
+
+struct nxpwifi_ds_misc_cmd {
+ u32 len;
+ u8 cmd[NXPWIFI_SIZE_OF_CMD_BUFFER];
+};
+
+#define BITMASK_BCN_RSSI_LOW BIT(0)
+#define BITMASK_BCN_RSSI_HIGH BIT(4)
+
+enum subsc_evt_rssi_state {
+ EVENT_HANDLED,
+ RSSI_LOW_RECVD,
+ RSSI_HIGH_RECVD
+};
+
+struct subsc_evt_cfg {
+ u8 abs_value;
+ u8 evt_freq;
+};
+
+struct nxpwifi_ds_misc_subsc_evt {
+ u16 action;
+ u16 events;
+ struct subsc_evt_cfg bcn_l_rssi_cfg;
+ struct subsc_evt_cfg bcn_h_rssi_cfg;
+};
+
+#define NXPWIFI_MEF_MAX_BYTESEQ 6 /* non-adjustable */
+#define NXPWIFI_MEF_MAX_FILTERS 10
+
+struct nxpwifi_mef_filter {
+ u16 repeat;
+ u16 offset;
+ s8 byte_seq[NXPWIFI_MEF_MAX_BYTESEQ + 1];
+ u8 filt_type;
+ u8 filt_action;
+};
+
+struct nxpwifi_mef_entry {
+ u8 mode;
+ u8 action;
+ struct nxpwifi_mef_filter filter[NXPWIFI_MEF_MAX_FILTERS];
+};
+
+struct nxpwifi_ds_mef_cfg {
+ u32 criteria;
+ u16 num_entries;
+ struct nxpwifi_mef_entry *mef_entry;
+};
+
+#define NXPWIFI_MAX_VSIE_LEN (256)
+#define NXPWIFI_MAX_VSIE_NUM (8)
+#define NXPWIFI_VSIE_MASK_CLEAR 0x00
+#define NXPWIFI_VSIE_MASK_SCAN 0x01
+#define NXPWIFI_VSIE_MASK_ASSOC 0x02
+#define NXPWIFI_VSIE_MASK_BGSCAN 0x08
+
+enum {
+ NXPWIFI_FUNC_INIT = 1,
+ NXPWIFI_FUNC_SHUTDOWN,
+};
+
+enum COALESCE_OPERATION {
+ RECV_FILTER_MATCH_TYPE_EQ = 0x80,
+ RECV_FILTER_MATCH_TYPE_NE,
+};
+
+enum COALESCE_PACKET_TYPE {
+ PACKET_TYPE_UNICAST = 1,
+ PACKET_TYPE_MULTICAST = 2,
+ PACKET_TYPE_BROADCAST = 3
+};
+
+#define NXPWIFI_COALESCE_MAX_RULES 8
+#define NXPWIFI_COALESCE_MAX_BYTESEQ 4 /* non-adjustable */
+#define NXPWIFI_COALESCE_MAX_FILTERS 4
+#define NXPWIFI_MAX_COALESCING_DELAY 100 /* in msecs */
+
+struct filt_field_param {
+ u8 operation;
+ u8 operand_len;
+ u16 offset;
+ u8 operand_byte_stream[NXPWIFI_COALESCE_MAX_BYTESEQ];
+};
+
+struct nxpwifi_coalesce_rule {
+ u16 max_coalescing_delay;
+ u8 num_of_fields;
+ u8 pkt_type;
+ struct filt_field_param params[NXPWIFI_COALESCE_MAX_FILTERS];
+};
+
+struct nxpwifi_ds_coalesce_cfg {
+ u16 num_of_rules;
+ struct nxpwifi_coalesce_rule rule[NXPWIFI_COALESCE_MAX_RULES];
+};
+
+#endif /* !_NXPWIFI_IOCTL_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 22/43] wifi: nxpwifi: add join.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (20 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 21/43] wifi: nxpwifi: add ioctl.h David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 23/43] wifi: nxpwifi: add main.c David Lin
` (22 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@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..a492821dd87a
--- /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 "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cmdevt.h"
+#include "wmm.h"
+#include "11n.h"
+#include "11ac.h"
+
+#define CAPINFO_MASK (~(BIT(15) | BIT(14) | BIT(12) | BIT(11) | BIT(9)))
+
+/* Append a generic IE as a pass through TLV to a TLV buffer.
+ *
+ * This function is called from the network join command preparation routine.
+ *
+ * If the IE 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 ie 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 ie len %d to %p\n",
+ __func__, priv->gen_ie_buf_len, *buffer);
+
+ /* Wrap the generic IE 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 IE 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 IE 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 = -1;
+ 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;
+
+ /* 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 */
+ if (nxpwifi_get_common_rates(priv, out_rates, NXPWIFI_SUPPORTED_RATES,
+ card_rates, card_rates_size)) {
+ *out_rates_size = 0;
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "%s: cannot get common rates\n",
+ __func__);
+ return -1;
+ }
+
+ *out_rates_size =
+ min_t(size_t, strlen(out_rates), NXPWIFI_SUPPORTED_RATES);
+
+ return 0;
+}
+
+/* This function appends a WPS IE. It is called from the network join command
+ * preparation routine.
+ *
+ * If the IE 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 ie buffer setup, append it to the return
+ * parameter buffer pointer.
+ */
+ if (priv->wps_ie_len) {
+ nxpwifi_dbg(priv->adapter, CMD,
+ "cmd: append wps ie %d to %p\n",
+ priv->wps_ie_len, *buffer);
+
+ /* Wrap the generic IE 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 ie 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 -1;
+
+ 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 IE
+ * - 11n TLV
+ * - Vendor specific TLV
+ * - WMM TLV
+ * - Generic IE
+ * - 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 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;
+
+ 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 */
+ if (nxpwifi_setup_rates_from_bssdesc
+ (priv, bss_desc, rates, &rates_size))
+ return -1;
+
+ /* 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(priv->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->ieee_hdr.len &&
+ (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(priv->adapter) &&
+ !(ISSUPP_11NENABLED(priv->adapter->fw_cap_info) &&
+ !bss_desc->disable_11n &&
+ (priv->adapter->config_bands & BAND_GN ||
+ priv->adapter->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(priv->adapter, INFO, "info: Assoc: TLV Chan = %d\n",
+ chan_tlv->chan_scan_param[0].chan_number);
+
+ chan_tlv->chan_scan_param[0].radio_type =
+ nxpwifi_band_to_radio_type((u8)bss_desc->bss_band);
+
+ nxpwifi_dbg(priv->adapter, INFO, "info: Assoc: TLV Band = %d\n",
+ chan_tlv->chan_scan_param[0].radio_type);
+ 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 == -1)
+ return -1;
+ }
+
+ if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) &&
+ !bss_desc->disable_11n &&
+ (priv->adapter->config_bands & BAND_GN ||
+ priv->adapter->config_bands & BAND_AN))
+ nxpwifi_cmd_append_11n_tlv(priv, bss_desc, &pos);
+
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
+ !bss_desc->disable_11n && !bss_desc->disable_11ac &&
+ priv->adapter->config_bands & BAND_AAC)
+ nxpwifi_cmd_append_11ac_tlv(priv, bss_desc, &pos);
+
+ /* Append vendor specific IE 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->adapter->config_bands == BAND_B)
+ tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
+
+ tmp_cap &= CAPINFO_MASK;
+ nxpwifi_dbg(priv->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 0;
+}
+
+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(priv->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)))
+ dev_err(priv->adapter->dev,
+ "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) {
+ priv->adapter->dbg.num_cmd_assoc_failure++;
+ nxpwifi_dbg(priv->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(priv->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(priv->adapter, ERROR,
+ "ASSOC_RESP: AUTH timeout\n");
+ } else {
+ ret = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ nxpwifi_dbg(priv->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;
+
+ priv->adapter->ps_state = PS_STATE_AWAKE;
+ priv->adapter->pps_uapsd_mode = false;
+ priv->adapter->tx_lock_flag = false;
+
+ /* Set the attempted BSSID Index to current */
+ bss_desc = priv->attempted_bss_desc;
+
+ nxpwifi_dbg(priv->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.vend_hdr.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_bitmap &
+ 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 ieee_types_header));
+ priv->assoc_resp_ht_param = assoc_resp_ht_oper->ht_param;
+ priv->ht_param_present = true;
+ } else {
+ priv->ht_param_present = false;
+ }
+
+ nxpwifi_dbg(priv->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(priv->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);
+
+ priv->adapter->dbg.num_cmd_assoc_success++;
+
+ nxpwifi_dbg(priv->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);
+
+ if (!netif_carrier_ok(priv->netdev))
+ 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 -1;
+
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
+ !bss_desc->disable_11n && !bss_desc->disable_11ac &&
+ priv->adapter->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;
+ if (nxpwifi_send_cmd(priv, HOST_CMD_MGMT_FRAME_REG,
+ HOST_ACT_GEN_SET, 0,
+ &priv->mgmt_frame_mask, false)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "could not unregister mgmt frame rx\n");
+ return -1;
+ }
+
+ 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:
+ return 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];
+ if (priv)
+ 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(u8 band)
+{
+ switch (band) {
+ case BAND_A:
+ case BAND_AN:
+ case BAND_A | BAND_AN:
+ case BAND_A | BAND_AN | BAND_AAC:
+ return HOST_SCAN_RADIO_TYPE_A;
+ case BAND_B:
+ case BAND_G:
+ case BAND_B | BAND_G:
+ default:
+ return HOST_SCAN_RADIO_TYPE_BG;
+ }
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 23/43] wifi: nxpwifi: add main.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (21 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 22/43] wifi: nxpwifi: add join.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 24/43] wifi: nxpwifi: add main.h David Lin
` (21 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/main.c | 1726 +++++++++++++++++++++++
1 file changed, 1726 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/main.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/main.c b/drivers/net/wireless/nxp/nxpwifi/main.c
new file mode 100644
index 000000000000..54d224bdb315
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/main.c
@@ -0,0 +1,1726 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: major functions
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include <linux/suspend.h>
+
+#include "main.h"
+#include "cmdevt.h"
+#include "wmm.h"
+#include "cfg80211.h"
+#include "11n.h"
+
+#define VERSION "1.0"
+
+static unsigned int debug_mask = NXPWIFI_DEFAULT_DEBUG_MASK;
+
+char driver_version[] = "nxpwifi " VERSION " (%s) ";
+
+const u16 nxpwifi_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
+
+/* This function registers the device and performs all the necessary
+ * initializations.
+ *
+ * The following initialization operations are performed -
+ * - Allocate adapter structure
+ * - Save interface specific operations table in adapter
+ * - Call interface specific initialization routine
+ * - Allocate private structures
+ * - Set default adapter structure parameters
+ * - Initialize locks
+ *
+ * In case of any errors during inittialization, this function also ensures
+ * proper cleanup before exiting.
+ */
+static int nxpwifi_register(void *card, struct device *dev,
+ struct nxpwifi_if_ops *if_ops, void **padapter)
+{
+ struct nxpwifi_adapter *adapter;
+ int i;
+
+ adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
+ if (!adapter)
+ return -ENOMEM;
+
+ *padapter = adapter;
+ adapter->dev = dev;
+ adapter->card = card;
+
+ /* Save interface specific operations in adapter */
+ memmove(&adapter->if_ops, if_ops, sizeof(struct nxpwifi_if_ops));
+ adapter->debug_mask = debug_mask;
+
+ /* card specific initialization has been deferred until now .. */
+ if (adapter->if_ops.init_if)
+ if (adapter->if_ops.init_if(adapter))
+ goto error;
+
+ adapter->priv_num = 0;
+
+ for (i = 0; i < NXPWIFI_MAX_BSS_NUM; i++) {
+ /* Allocate memory for private structure */
+ adapter->priv[i] =
+ kzalloc(sizeof(struct nxpwifi_private), GFP_KERNEL);
+ if (!adapter->priv[i])
+ goto error;
+
+ adapter->priv[i]->adapter = adapter;
+ adapter->priv_num++;
+ }
+ nxpwifi_init_lock_list(adapter);
+
+ timer_setup(&adapter->cmd_timer, nxpwifi_cmd_timeout_func, 0);
+
+ return 0;
+
+error:
+ nxpwifi_dbg(adapter, ERROR,
+ "info: leave %s with error\n", __func__);
+
+ for (i = 0; i < adapter->priv_num; i++)
+ kfree(adapter->priv[i]);
+
+ kfree(adapter);
+
+ return -1;
+}
+
+/* This function unregisters the device and performs all the necessary
+ * cleanups.
+ *
+ * The following cleanup operations are performed -
+ * - Free the timers
+ * - Free beacon buffers
+ * - Free private structures
+ * - Free adapter structure
+ */
+static int nxpwifi_unregister(struct nxpwifi_adapter *adapter)
+{
+ s32 i;
+
+ if (adapter->if_ops.cleanup_if)
+ adapter->if_ops.cleanup_if(adapter);
+
+ del_timer_sync(&adapter->cmd_timer);
+
+ /* Free private structures */
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ nxpwifi_free_curr_bcn(adapter->priv[i]);
+ kfree(adapter->priv[i]);
+ }
+ }
+
+ if (adapter->nd_info) {
+ for (i = 0 ; i < adapter->nd_info->n_matches ; i++)
+ kfree(adapter->nd_info->matches[i]);
+ kfree(adapter->nd_info);
+ adapter->nd_info = NULL;
+ }
+
+ kfree(adapter->regd);
+
+ kfree(adapter);
+ return 0;
+}
+
+void nxpwifi_queue_main_work(struct nxpwifi_adapter *adapter)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+ if (adapter->nxpwifi_processing) {
+ adapter->more_task_flag = true;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ } else {
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ queue_work(adapter->workqueue, &adapter->main_work);
+ }
+}
+EXPORT_SYMBOL_GPL(nxpwifi_queue_main_work);
+
+static void nxpwifi_queue_rx_work(struct nxpwifi_adapter *adapter)
+{
+ spin_lock_bh(&adapter->rx_proc_lock);
+ if (adapter->rx_processing) {
+ spin_unlock_bh(&adapter->rx_proc_lock);
+ } else {
+ spin_unlock_bh(&adapter->rx_proc_lock);
+ queue_work(adapter->rx_workqueue, &adapter->rx_work);
+ }
+}
+
+static int nxpwifi_process_rx(struct nxpwifi_adapter *adapter)
+{
+ struct sk_buff *skb;
+ struct nxpwifi_rxinfo *rx_info;
+
+ spin_lock_bh(&adapter->rx_proc_lock);
+ if (adapter->rx_processing || adapter->rx_locked) {
+ spin_unlock_bh(&adapter->rx_proc_lock);
+ goto exit_rx_proc;
+ } else {
+ adapter->rx_processing = true;
+ spin_unlock_bh(&adapter->rx_proc_lock);
+ }
+
+ /* Check for Rx data */
+ while ((skb = skb_dequeue(&adapter->rx_data_q))) {
+ atomic_dec(&adapter->rx_pending);
+ if (adapter->delay_main_work &&
+ (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) {
+ adapter->delay_main_work = false;
+ nxpwifi_queue_main_work(adapter);
+ }
+ rx_info = NXPWIFI_SKB_RXCB(skb);
+ if (rx_info->buf_type == NXPWIFI_TYPE_AGGR_DATA) {
+ if (adapter->if_ops.deaggr_pkt)
+ adapter->if_ops.deaggr_pkt(adapter, skb);
+ dev_kfree_skb_any(skb);
+ } else {
+ nxpwifi_handle_rx_packet(adapter, skb);
+ }
+ }
+ spin_lock_bh(&adapter->rx_proc_lock);
+ adapter->rx_processing = false;
+ spin_unlock_bh(&adapter->rx_proc_lock);
+
+exit_rx_proc:
+ return 0;
+}
+
+static void maybe_quirk_fw_disable_ds(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_private *priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA);
+ struct nxpwifi_ver_ext ver_ext;
+
+ if (test_and_set_bit(NXPWIFI_IS_REQUESTING_FW_VEREXT, &adapter->work_flags))
+ return;
+
+ memset(&ver_ext, 0, sizeof(ver_ext));
+ ver_ext.version_str_sel = 1;
+ if (nxpwifi_send_cmd(priv, HOST_CMD_VERSION_EXT,
+ HOST_ACT_GEN_GET, 0, &ver_ext, false)) {
+ nxpwifi_dbg(priv->adapter, MSG,
+ "Checking hardware revision failed.\n");
+ }
+}
+
+/* The main process.
+ *
+ * This function is the main procedure of the driver and handles various driver
+ * operations. It runs in a loop and provides the core functionalities.
+ *
+ * The main responsibilities of this function are -
+ * - Ensure concurrency control
+ * - Handle pending interrupts and call interrupt handlers
+ * - Wake up the card if required
+ * - Handle command responses and call response handlers
+ * - Handle events and call event handlers
+ * - Execute pending commands
+ * - Transmit pending data packets
+ */
+int nxpwifi_main_process(struct nxpwifi_adapter *adapter)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+
+ /* Check if already processing */
+ if (adapter->nxpwifi_processing || adapter->main_locked) {
+ adapter->more_task_flag = true;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ return 0;
+ }
+
+ adapter->nxpwifi_processing = true;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+
+process_start:
+ do {
+ if (adapter->hw_status == NXPWIFI_HW_STATUS_NOT_READY)
+ break;
+
+ /* For non-USB interfaces, If we process interrupts first, it
+ * would increase RX pending even further. Avoid this by
+ * checking if rx_pending has crossed high threshold and
+ * schedule rx work queue and then process interrupts.
+ * For USB interface, there are no interrupts. We already have
+ * HIGH_RX_PENDING check in usb.c
+ */
+ if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING) {
+ adapter->delay_main_work = true;
+ nxpwifi_queue_rx_work(adapter);
+ break;
+ }
+
+ /* Handle pending interrupt if any */
+ if (adapter->int_status) {
+ if (adapter->hs_activated)
+ nxpwifi_process_hs_config(adapter);
+ if (adapter->if_ops.process_int_status)
+ adapter->if_ops.process_int_status(adapter);
+ }
+
+ if (adapter->rx_work_enabled && adapter->data_received)
+ nxpwifi_queue_rx_work(adapter);
+
+ /* Need to wake up the card ? */
+ if (adapter->ps_state == PS_STATE_SLEEP &&
+ (adapter->pm_wakeup_card_req &&
+ !adapter->pm_wakeup_fw_try) &&
+ (is_command_pending(adapter) ||
+ !skb_queue_empty(&adapter->tx_data_q) ||
+ !nxpwifi_bypass_txlist_empty(adapter) ||
+ !nxpwifi_wmm_lists_empty(adapter))) {
+ adapter->pm_wakeup_fw_try = true;
+ mod_timer(&adapter->wakeup_timer, jiffies + (HZ * 3));
+ adapter->if_ops.wakeup(adapter);
+ continue;
+ }
+
+ if (IS_CARD_RX_RCVD(adapter)) {
+ adapter->data_received = false;
+ adapter->pm_wakeup_fw_try = false;
+ del_timer(&adapter->wakeup_timer);
+ if (adapter->ps_state == PS_STATE_SLEEP)
+ adapter->ps_state = PS_STATE_AWAKE;
+ } else {
+ /* We have tried to wakeup the card already */
+ if (adapter->pm_wakeup_fw_try)
+ break;
+ if (adapter->ps_state == PS_STATE_PRE_SLEEP)
+ nxpwifi_check_ps_cond(adapter);
+
+ if (adapter->ps_state != PS_STATE_AWAKE)
+ break;
+ if (adapter->tx_lock_flag)
+ break;
+
+ if ((!adapter->scan_chan_gap_enabled &&
+ adapter->scan_processing) || adapter->data_sent ||
+ (nxpwifi_wmm_lists_empty(adapter) &&
+ nxpwifi_bypass_txlist_empty(adapter) &&
+ skb_queue_empty(&adapter->tx_data_q))) {
+ if (adapter->cmd_sent || adapter->curr_cmd ||
+ (!is_command_pending(adapter)))
+ break;
+ }
+ }
+
+ /* Check for event */
+ if (adapter->event_received) {
+ adapter->event_received = false;
+ nxpwifi_process_event(adapter);
+ }
+
+ /* Check for Cmd Resp */
+ if (adapter->cmd_resp_received) {
+ adapter->cmd_resp_received = false;
+ nxpwifi_process_cmdresp(adapter);
+
+ /* call nxpwifi back when init_fw is done */
+ if (adapter->hw_status == NXPWIFI_HW_STATUS_INIT_DONE) {
+ adapter->hw_status = NXPWIFI_HW_STATUS_READY;
+ nxpwifi_init_fw_complete(adapter);
+ maybe_quirk_fw_disable_ds(adapter);
+ }
+ }
+
+ /* Check if we need to confirm Sleep Request
+ * received previously
+ */
+ if (adapter->ps_state == PS_STATE_PRE_SLEEP)
+ nxpwifi_check_ps_cond(adapter);
+
+ /* * The ps_state may have been changed during processing of
+ * Sleep Request event.
+ */
+ if (adapter->ps_state == PS_STATE_SLEEP ||
+ adapter->ps_state == PS_STATE_PRE_SLEEP ||
+ adapter->ps_state == PS_STATE_SLEEP_CFM) {
+ continue;
+ }
+
+ if (adapter->tx_lock_flag)
+ continue;
+
+ if (!adapter->cmd_sent && !adapter->curr_cmd) {
+ if (nxpwifi_exec_next_cmd(adapter) == -1) {
+ ret = -1;
+ break;
+ }
+ }
+
+ if ((adapter->scan_chan_gap_enabled ||
+ !adapter->scan_processing) &&
+ !adapter->data_sent &&
+ !skb_queue_empty(&adapter->tx_data_q)) {
+ if (adapter->hs_activated_manually) {
+ nxpwifi_cancel_hs(nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY),
+ NXPWIFI_ASYNC_CMD);
+ adapter->hs_activated_manually = false;
+ }
+
+ nxpwifi_process_tx_queue(adapter);
+ if (adapter->hs_activated) {
+ clear_bit(NXPWIFI_IS_HS_CONFIGURED,
+ &adapter->work_flags);
+ nxpwifi_hs_activated_event
+ (nxpwifi_get_priv
+ (adapter, NXPWIFI_BSS_ROLE_ANY),
+ false);
+ }
+ }
+
+ if ((adapter->scan_chan_gap_enabled ||
+ !adapter->scan_processing) &&
+ !adapter->data_sent &&
+ !nxpwifi_bypass_txlist_empty(adapter)) {
+ if (adapter->hs_activated_manually) {
+ nxpwifi_cancel_hs(nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY),
+ NXPWIFI_ASYNC_CMD);
+ adapter->hs_activated_manually = false;
+ }
+
+ nxpwifi_process_bypass_tx(adapter);
+ if (adapter->hs_activated) {
+ clear_bit(NXPWIFI_IS_HS_CONFIGURED,
+ &adapter->work_flags);
+ nxpwifi_hs_activated_event
+ (nxpwifi_get_priv
+ (adapter, NXPWIFI_BSS_ROLE_ANY),
+ false);
+ }
+ }
+
+ if ((adapter->scan_chan_gap_enabled ||
+ !adapter->scan_processing) &&
+ !adapter->data_sent && !nxpwifi_wmm_lists_empty(adapter)) {
+ if (adapter->hs_activated_manually) {
+ nxpwifi_cancel_hs(nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY),
+ NXPWIFI_ASYNC_CMD);
+ adapter->hs_activated_manually = false;
+ }
+
+ nxpwifi_wmm_process_tx(adapter);
+ if (adapter->hs_activated) {
+ clear_bit(NXPWIFI_IS_HS_CONFIGURED,
+ &adapter->work_flags);
+ nxpwifi_hs_activated_event
+ (nxpwifi_get_priv
+ (adapter, NXPWIFI_BSS_ROLE_ANY),
+ false);
+ }
+ }
+
+ if (adapter->delay_null_pkt && !adapter->cmd_sent &&
+ !adapter->curr_cmd && !is_command_pending(adapter) &&
+ (nxpwifi_wmm_lists_empty(adapter) &&
+ nxpwifi_bypass_txlist_empty(adapter) &&
+ skb_queue_empty(&adapter->tx_data_q))) {
+ if (!nxpwifi_send_null_packet
+ (nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA),
+ NXPWIFI_TxPD_POWER_MGMT_NULL_PACKET |
+ NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET)) {
+ adapter->delay_null_pkt = false;
+ adapter->ps_state = PS_STATE_SLEEP;
+ }
+ break;
+ }
+ } while (true);
+
+ spin_lock_irqsave(&adapter->main_proc_lock, flags);
+ if (adapter->more_task_flag) {
+ adapter->more_task_flag = false;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+ goto process_start;
+ }
+ adapter->nxpwifi_processing = false;
+ spin_unlock_irqrestore(&adapter->main_proc_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nxpwifi_main_process);
+
+/* This function frees the adapter structure.
+ *
+ * Additionally, this closes the netlink socket, frees the timers
+ * and private structures.
+ */
+static void nxpwifi_free_adapter(struct nxpwifi_adapter *adapter)
+{
+ if (!adapter) {
+ pr_err("%s: adapter is NULL\n", __func__);
+ return;
+ }
+
+ nxpwifi_unregister(adapter);
+ pr_debug("info: %s: free adapter\n", __func__);
+}
+
+/* This function cancels all works in the queue and destroys
+ * the main workqueue.
+ */
+static void nxpwifi_terminate_workqueue(struct nxpwifi_adapter *adapter)
+{
+ if (adapter->workqueue) {
+ destroy_workqueue(adapter->workqueue);
+ adapter->workqueue = NULL;
+ }
+
+ if (adapter->rx_workqueue) {
+ destroy_workqueue(adapter->rx_workqueue);
+ adapter->rx_workqueue = NULL;
+ }
+
+ if (adapter->host_mlme_workqueue) {
+ destroy_workqueue(adapter->host_mlme_workqueue);
+ adapter->host_mlme_workqueue = NULL;
+ }
+}
+
+/* This function gets firmware and initializes it.
+ *
+ * The main initialization steps followed are -
+ * - Download the correct firmware to card
+ * - Issue the init commands to firmware
+ */
+static int _nxpwifi_fw_dpc(const struct firmware *firmware, void *context)
+{
+ int ret;
+ char fmt[64];
+ struct nxpwifi_adapter *adapter = context;
+ struct nxpwifi_fw_image fw;
+ bool init_failed = false;
+ struct wireless_dev *wdev;
+ struct completion *fw_done = adapter->fw_done;
+
+ if (!firmware) {
+ nxpwifi_dbg(adapter, ERROR,
+ "Failed to get firmware %s\n", adapter->fw_name);
+ goto err_dnld_fw;
+ }
+
+ memset(&fw, 0, sizeof(struct nxpwifi_fw_image));
+ adapter->firmware = firmware;
+ fw.fw_buf = (u8 *)adapter->firmware->data;
+ fw.fw_len = adapter->firmware->size;
+
+ if (adapter->if_ops.dnld_fw)
+ ret = adapter->if_ops.dnld_fw(adapter, &fw);
+ else
+ ret = nxpwifi_dnld_fw(adapter, &fw);
+
+ if (ret == -1)
+ goto err_dnld_fw;
+
+ nxpwifi_dbg(adapter, MSG, "WLAN FW is active\n");
+
+ /* enable host interrupt after fw dnld is successful */
+ if (adapter->if_ops.enable_int) {
+ if (adapter->if_ops.enable_int(adapter))
+ goto err_dnld_fw;
+ }
+
+ adapter->init_wait_q_woken = false;
+ ret = nxpwifi_init_fw(adapter);
+ if (ret == -1) {
+ goto err_init_fw;
+ } else if (!ret) {
+ adapter->hw_status = NXPWIFI_HW_STATUS_READY;
+ goto done;
+ }
+ /* Wait for nxpwifi_init to complete */
+ wait_event_interruptible(adapter->init_wait_q,
+ adapter->init_wait_q_woken);
+ if (adapter->hw_status != NXPWIFI_HW_STATUS_READY)
+ goto err_init_fw;
+
+ if (!adapter->wiphy) {
+ if (nxpwifi_register_cfg80211(adapter)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "cannot register with cfg80211\n");
+ goto err_init_fw;
+ }
+ }
+
+ if (nxpwifi_init_channel_scan_gap(adapter)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "could not init channel stats table\n");
+ goto err_init_chan_scan;
+ }
+
+ rtnl_lock();
+ wiphy_lock(adapter->wiphy);
+ /* Create station interface by default */
+ wdev = nxpwifi_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM,
+ NL80211_IFTYPE_STATION, NULL);
+ if (IS_ERR(wdev)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "cannot create default STA interface\n");
+ wiphy_unlock(adapter->wiphy);
+ rtnl_unlock();
+ goto err_add_intf;
+ }
+
+ wdev = nxpwifi_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM,
+ NL80211_IFTYPE_AP, NULL);
+ if (IS_ERR(wdev)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "cannot create AP interface\n");
+ wiphy_unlock(adapter->wiphy);
+ rtnl_unlock();
+ goto err_add_intf;
+ }
+
+ wiphy_unlock(adapter->wiphy);
+ rtnl_unlock();
+
+ nxpwifi_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
+ nxpwifi_dbg(adapter, MSG, "driver_version = %s\n", fmt);
+ adapter->is_up = true;
+ goto done;
+
+err_add_intf:
+ vfree(adapter->chan_stats);
+err_init_chan_scan:
+ wiphy_unregister(adapter->wiphy);
+ wiphy_free(adapter->wiphy);
+err_init_fw:
+ if (adapter->if_ops.disable_int)
+ adapter->if_ops.disable_int(adapter);
+err_dnld_fw:
+ nxpwifi_dbg(adapter, ERROR,
+ "info: %s: unregister device\n", __func__);
+ if (adapter->if_ops.unregister_dev)
+ adapter->if_ops.unregister_dev(adapter);
+
+ set_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags);
+ nxpwifi_terminate_workqueue(adapter);
+
+ if (adapter->hw_status == NXPWIFI_HW_STATUS_READY) {
+ pr_debug("info: %s: shutdown nxpwifi\n", __func__);
+ nxpwifi_shutdown_drv(adapter);
+ nxpwifi_free_cmd_buffers(adapter);
+ }
+
+ init_failed = true;
+done:
+ if (adapter->cal_data) {
+ release_firmware(adapter->cal_data);
+ adapter->cal_data = NULL;
+ }
+ if (adapter->firmware) {
+ release_firmware(adapter->firmware);
+ adapter->firmware = NULL;
+ }
+ if (init_failed) {
+ if (adapter->irq_wakeup >= 0)
+ device_init_wakeup(adapter->dev, false);
+ nxpwifi_free_adapter(adapter);
+ }
+ /* Tell all current and future waiters we're finished */
+ complete_all(fw_done);
+
+ return init_failed ? -EIO : 0;
+}
+
+static void nxpwifi_fw_dpc(const struct firmware *firmware, void *context)
+{
+ _nxpwifi_fw_dpc(firmware, context);
+}
+
+/* This function gets the firmware and (if called asynchronously) kicks off the
+ * HW init when done.
+ */
+static int nxpwifi_init_hw_fw(struct nxpwifi_adapter *adapter,
+ bool req_fw_nowait)
+{
+ int ret;
+
+ if (req_fw_nowait) {
+ ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name,
+ adapter->dev, GFP_KERNEL, adapter,
+ nxpwifi_fw_dpc);
+ } else {
+ ret = request_firmware(&adapter->firmware,
+ adapter->fw_name,
+ adapter->dev);
+ }
+
+ if (ret < 0)
+ nxpwifi_dbg(adapter, ERROR, "request_firmware%s error %d\n",
+ req_fw_nowait ? "_nowait" : "", ret);
+ return ret;
+}
+
+/* CFG802.11 network device handler for open.
+ *
+ * Starts the data queue.
+ */
+static int
+nxpwifi_open(struct net_device *dev)
+{
+ netif_carrier_off(dev);
+
+ return 0;
+}
+
+/* CFG802.11 network device handler for close.
+ */
+static int
+nxpwifi_close(struct net_device *dev)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+
+ if (priv->scan_request) {
+ struct cfg80211_scan_info info = {
+ .aborted = true,
+ };
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "aborting scan on ndo_stop\n");
+ cfg80211_scan_done(priv->scan_request, &info);
+ priv->scan_request = NULL;
+ priv->scan_aborting = true;
+ }
+
+ if (priv->sched_scanning) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "aborting bgscan on ndo_stop\n");
+ nxpwifi_stop_bg_scan(priv);
+ cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0);
+ }
+
+ return 0;
+}
+
+static bool
+nxpwifi_bypass_tx_queue(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
+
+ if (eth_hdr->h_proto == htons(ETH_P_PAE) ||
+ nxpwifi_is_skb_mgmt_frame(skb)) {
+ nxpwifi_dbg(priv->adapter, DATA,
+ "bypass txqueue; eth type %#x, mgmt %d\n",
+ ntohs(eth_hdr->h_proto),
+ nxpwifi_is_skb_mgmt_frame(skb));
+ if (eth_hdr->h_proto == htons(ETH_P_PAE))
+ nxpwifi_dbg(priv->adapter, MSG,
+ "key: send EAPOL to %pM\n",
+ eth_hdr->h_dest);
+ return true;
+ }
+
+ return false;
+}
+
+/* Add buffer into wmm tx queue and queue work to transmit it.
+ */
+int nxpwifi_queue_tx_pkt(struct nxpwifi_private *priv, struct sk_buff *skb)
+{
+ struct netdev_queue *txq;
+ int index = nxpwifi_1d_to_wmm_queue[skb->priority];
+
+ if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) {
+ txq = netdev_get_tx_queue(priv->netdev, index);
+ if (!netif_tx_queue_stopped(txq)) {
+ netif_tx_stop_queue(txq);
+ nxpwifi_dbg(priv->adapter, DATA,
+ "stop queue: %d\n", index);
+ }
+ }
+
+ if (nxpwifi_bypass_tx_queue(priv, skb)) {
+ atomic_inc(&priv->adapter->tx_pending);
+ atomic_inc(&priv->adapter->bypass_tx_pending);
+ nxpwifi_wmm_add_buf_bypass_txqueue(priv, skb);
+ } else {
+ atomic_inc(&priv->adapter->tx_pending);
+ nxpwifi_wmm_add_buf_txqueue(priv, skb);
+ }
+
+ nxpwifi_queue_main_work(priv->adapter);
+
+ return 0;
+}
+
+struct sk_buff *
+nxpwifi_clone_skb_for_tx_status(struct nxpwifi_private *priv,
+ struct sk_buff *skb, u8 flag, u64 *cookie)
+{
+ struct sk_buff *orig_skb = skb;
+ struct nxpwifi_txinfo *tx_info, *orig_tx_info;
+
+ skb = skb_clone(skb, GFP_ATOMIC);
+ if (skb) {
+ int id;
+
+ spin_lock_bh(&priv->ack_status_lock);
+ id = idr_alloc(&priv->ack_status_frames, orig_skb,
+ 1, 0x10, GFP_ATOMIC);
+ spin_unlock_bh(&priv->ack_status_lock);
+
+ if (id >= 0) {
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+ tx_info->ack_frame_id = id;
+ tx_info->flags |= flag;
+ orig_tx_info = NXPWIFI_SKB_TXCB(orig_skb);
+ orig_tx_info->ack_frame_id = id;
+ orig_tx_info->flags |= flag;
+
+ if (flag == NXPWIFI_BUF_FLAG_ACTION_TX_STATUS && cookie)
+ orig_tx_info->cookie = *cookie;
+
+ } else if (skb_shared(skb)) {
+ kfree_skb(orig_skb);
+ } else {
+ kfree_skb(skb);
+ skb = orig_skb;
+ }
+ } else {
+ /* couldn't clone -- lose tx status ... */
+ skb = orig_skb;
+ }
+
+ return skb;
+}
+
+/* CFG802.11 network device handler for data transmission.
+ */
+static netdev_tx_t
+nxpwifi_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ struct sk_buff *new_skb;
+ struct nxpwifi_txinfo *tx_info;
+ bool multicast;
+
+ nxpwifi_dbg(priv->adapter, DATA,
+ "data: %lu BSS(%d-%d): Data <= kernel\n",
+ jiffies, priv->bss_type, priv->bss_num);
+
+ if (test_bit(NXPWIFI_SURPRISE_REMOVED, &priv->adapter->work_flags)) {
+ kfree_skb(skb);
+ priv->stats.tx_dropped++;
+ return 0;
+ }
+ if (!skb->len || skb->len > ETH_FRAME_LEN) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Tx: bad skb len %d\n", skb->len);
+ kfree_skb(skb);
+ priv->stats.tx_dropped++;
+ return 0;
+ }
+ if (skb_headroom(skb) < NXPWIFI_MIN_DATA_HEADER_LEN) {
+ nxpwifi_dbg(priv->adapter, DATA,
+ "data: Tx: insufficient skb headroom %d\n",
+ skb_headroom(skb));
+ /* Insufficient skb headroom - allocate a new skb */
+ new_skb =
+ skb_realloc_headroom(skb, NXPWIFI_MIN_DATA_HEADER_LEN);
+ if (unlikely(!new_skb)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Tx: cannot alloca new_skb\n");
+ kfree_skb(skb);
+ priv->stats.tx_dropped++;
+ return 0;
+ }
+ kfree_skb(skb);
+ skb = new_skb;
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: new skb headroomd %d\n",
+ skb_headroom(skb));
+ }
+
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+ memset(tx_info, 0, sizeof(*tx_info));
+ tx_info->bss_num = priv->bss_num;
+ tx_info->bss_type = priv->bss_type;
+ tx_info->pkt_len = skb->len;
+
+ multicast = is_multicast_ether_addr(skb->data);
+
+ if (unlikely(!multicast && skb->sk &&
+ skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS &&
+ priv->adapter->fw_api_ver == NXPWIFI_FW_V15))
+ skb = nxpwifi_clone_skb_for_tx_status(priv,
+ skb,
+ NXPWIFI_BUF_FLAG_EAPOL_TX_STATUS, NULL);
+
+ /* Record the current time the packet was queued; used to
+ * determine the amount of time the packet was queued in
+ * the driver before it was sent to the firmware.
+ * The delay is then sent along with the packet to the
+ * firmware for aggregate delay calculation for stats and
+ * MSDU lifetime expiry.
+ */
+ __net_timestamp(skb);
+
+ nxpwifi_queue_tx_pkt(priv, skb);
+
+ return 0;
+}
+
+int nxpwifi_set_mac_address(struct nxpwifi_private *priv,
+ struct net_device *dev, bool external,
+ u8 *new_mac)
+{
+ int ret;
+ u64 mac_addr, old_mac_addr;
+
+ old_mac_addr = ether_addr_to_u64(priv->curr_addr);
+
+ if (external) {
+ mac_addr = ether_addr_to_u64(new_mac);
+ } else {
+ /* Internal mac address change */
+ if (priv->bss_type == NXPWIFI_BSS_TYPE_ANY)
+ return -EOPNOTSUPP;
+
+ mac_addr = old_mac_addr;
+
+ if (priv->adapter->priv[0] != priv) {
+ /* Set mac address based on bss_type/bss_num */
+ mac_addr ^= BIT_ULL(priv->bss_type + 8);
+ mac_addr += priv->bss_num;
+ }
+ }
+
+ u64_to_ether_addr(mac_addr, priv->curr_addr);
+
+ /* Send request to firmware */
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_MAC_ADDRESS,
+ HOST_ACT_GEN_SET, 0, NULL, true);
+
+ if (ret) {
+ u64_to_ether_addr(old_mac_addr, priv->curr_addr);
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "set mac address failed: ret=%d\n", ret);
+ return ret;
+ }
+
+ eth_hw_addr_set(dev, priv->curr_addr);
+ return 0;
+}
+
+/* CFG802.11 network device handler for setting MAC address.
+ */
+static int
+nxpwifi_ndo_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ struct sockaddr *hw_addr = addr;
+
+ return nxpwifi_set_mac_address(priv, dev, true, hw_addr->sa_data);
+}
+
+/* CFG802.11 network device handler for setting multicast list.
+ */
+static void nxpwifi_set_multicast_list(struct net_device *dev)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+ struct nxpwifi_multicast_list mcast_list;
+
+ if (dev->flags & IFF_PROMISC) {
+ mcast_list.mode = NXPWIFI_PROMISC_MODE;
+ } else if (dev->flags & IFF_ALLMULTI ||
+ netdev_mc_count(dev) > NXPWIFI_MAX_MULTICAST_LIST_SIZE) {
+ mcast_list.mode = NXPWIFI_ALL_MULTI_MODE;
+ } else {
+ mcast_list.mode = NXPWIFI_MULTICAST_MODE;
+ mcast_list.num_multicast_addr =
+ nxpwifi_copy_mcast_addr(&mcast_list, dev);
+ }
+ nxpwifi_request_set_multicast_list(priv, &mcast_list);
+}
+
+/* CFG802.11 network device handler for transmission timeout.
+ */
+static void
+nxpwifi_tx_timeout(struct net_device *dev, unsigned int txqueue)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+
+ priv->num_tx_timeout++;
+ priv->tx_timeout_cnt++;
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n",
+ jiffies, priv->tx_timeout_cnt, priv->bss_type,
+ priv->bss_num);
+ nxpwifi_set_trans_start(dev);
+
+ if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD &&
+ priv->adapter->if_ops.card_reset) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "tx_timeout_cnt exceeds threshold.\t"
+ "Triggering card reset!\n");
+ priv->adapter->if_ops.card_reset(priv->adapter);
+ }
+}
+
+void nxpwifi_upload_device_dump(struct nxpwifi_adapter *adapter)
+{
+ /* Dump all the memory data into single file, a userspace script will
+ * be used to split all the memory data to multiple files
+ */
+ nxpwifi_dbg(adapter, MSG,
+ "== nxpwifi dump information to /sys/class/devcoredump start\n");
+ dev_coredumpv(adapter->dev, adapter->devdump_data, adapter->devdump_len,
+ GFP_KERNEL);
+ nxpwifi_dbg(adapter, MSG,
+ "== nxpwifi dump information to /sys/class/devcoredump end\n");
+
+ /* Device dump data will be freed in device coredump release function
+ * after 5 min. Here reset adapter->devdump_data and ->devdump_len
+ * to avoid it been accidentally reused.
+ */
+ adapter->devdump_data = NULL;
+ adapter->devdump_len = 0;
+}
+EXPORT_SYMBOL_GPL(nxpwifi_upload_device_dump);
+
+void nxpwifi_drv_info_dump(struct nxpwifi_adapter *adapter)
+{
+ char *p;
+ char drv_version[64];
+ struct sdio_mmc_card *sdio_card;
+ struct nxpwifi_private *priv;
+ int i, idx;
+ struct netdev_queue *txq;
+ struct nxpwifi_debug_info *debug_info;
+
+ nxpwifi_dbg(adapter, MSG, "===nxpwifi driverinfo dump start===\n");
+
+ p = adapter->devdump_data;
+ strscpy(p, "========Start dump driverinfo========\n", NXPWIFI_FW_DUMP_SIZE);
+ p += strlen("========Start dump driverinfo========\n");
+ p += sprintf(p, "driver_name = ");
+ p += sprintf(p, "\"nxpwifi\"\n");
+
+ nxpwifi_drv_get_driver_version(adapter, drv_version,
+ sizeof(drv_version) - 1);
+ p += sprintf(p, "driver_version = %s\n", drv_version);
+
+ p += sprintf(p, "tx_pending = %d\n",
+ atomic_read(&adapter->tx_pending));
+ p += sprintf(p, "rx_pending = %d\n",
+ atomic_read(&adapter->rx_pending));
+
+ if (adapter->iface_type == NXPWIFI_SDIO) {
+ sdio_card = (struct sdio_mmc_card *)adapter->card;
+ p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n",
+ sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port);
+ p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n",
+ sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port);
+ }
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (!adapter->priv[i] || !adapter->priv[i]->netdev)
+ continue;
+ priv = adapter->priv[i];
+ p += sprintf(p, "\n[interface : \"%s\"]\n",
+ priv->netdev->name);
+ p += sprintf(p, "wmm_tx_pending[0] = %d\n",
+ atomic_read(&priv->wmm_tx_pending[0]));
+ p += sprintf(p, "wmm_tx_pending[1] = %d\n",
+ atomic_read(&priv->wmm_tx_pending[1]));
+ p += sprintf(p, "wmm_tx_pending[2] = %d\n",
+ atomic_read(&priv->wmm_tx_pending[2]));
+ p += sprintf(p, "wmm_tx_pending[3] = %d\n",
+ atomic_read(&priv->wmm_tx_pending[3]));
+ p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ?
+ "Disconnected" : "Connected");
+ p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev)
+ ? "on" : "off"));
+ for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) {
+ txq = netdev_get_tx_queue(priv->netdev, idx);
+ p += sprintf(p, "tx queue %d:%s ", idx,
+ netif_tx_queue_stopped(txq) ?
+ "stopped" : "started");
+ }
+ p += sprintf(p, "\n%s: num_tx_timeout = %d\n",
+ priv->netdev->name, priv->num_tx_timeout);
+ }
+
+ if (adapter->iface_type == NXPWIFI_SDIO) {
+ p += sprintf(p, "\n=== %s register dump===\n", "SDIO");
+ if (adapter->if_ops.reg_dump)
+ p += adapter->if_ops.reg_dump(adapter, p);
+ }
+ p += sprintf(p, "\n=== more debug information\n");
+ debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL);
+ if (debug_info) {
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (!adapter->priv[i] || !adapter->priv[i]->netdev)
+ continue;
+ priv = adapter->priv[i];
+ nxpwifi_get_debug_info(priv, debug_info);
+ p += nxpwifi_debug_info_to_buffer(priv, p, debug_info);
+ break;
+ }
+ kfree(debug_info);
+ }
+
+ p += sprintf(p, "\n========End dump========\n");
+ nxpwifi_dbg(adapter, MSG, "===nxpwifi driverinfo dump end===\n");
+ adapter->devdump_len = p - (char *)adapter->devdump_data;
+}
+EXPORT_SYMBOL_GPL(nxpwifi_drv_info_dump);
+
+void nxpwifi_prepare_fw_dump_info(struct nxpwifi_adapter *adapter)
+{
+ u8 idx;
+ char *fw_dump_ptr;
+ u32 dump_len = 0;
+
+ for (idx = 0; idx < adapter->num_mem_types; idx++) {
+ struct memory_type_mapping *entry =
+ &adapter->mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ dump_len += (strlen("========Start dump ") +
+ strlen(entry->mem_name) +
+ strlen("========\n") +
+ (entry->mem_size + 1) +
+ strlen("\n========End dump========\n"));
+ }
+ }
+
+ if (dump_len + 1 + adapter->devdump_len > NXPWIFI_FW_DUMP_SIZE) {
+ /* Realloc in case buffer overflow */
+ fw_dump_ptr = vzalloc(dump_len + 1 + adapter->devdump_len);
+ nxpwifi_dbg(adapter, MSG, "Realloc device dump data.\n");
+ if (!fw_dump_ptr) {
+ vfree(adapter->devdump_data);
+ nxpwifi_dbg(adapter, ERROR,
+ "vzalloc devdump data failure!\n");
+ return;
+ }
+
+ memmove(fw_dump_ptr, adapter->devdump_data,
+ adapter->devdump_len);
+ vfree(adapter->devdump_data);
+ adapter->devdump_data = fw_dump_ptr;
+ }
+
+ fw_dump_ptr = (char *)adapter->devdump_data + adapter->devdump_len;
+
+ for (idx = 0; idx < adapter->num_mem_types; idx++) {
+ struct memory_type_mapping *entry =
+ &adapter->mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ fw_dump_ptr += sprintf(fw_dump_ptr, "========Start dump ");
+ fw_dump_ptr += sprintf(fw_dump_ptr, "%s", entry->mem_name);
+ fw_dump_ptr += sprintf(fw_dump_ptr, "========\n");
+ memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size);
+ fw_dump_ptr += entry->mem_size;
+ fw_dump_ptr += sprintf(fw_dump_ptr, "\n========End dump========\n");
+ }
+ }
+
+ adapter->devdump_len = fw_dump_ptr - (char *)adapter->devdump_data;
+
+ for (idx = 0; idx < adapter->num_mem_types; idx++) {
+ struct memory_type_mapping *entry =
+ &adapter->mem_type_mapping_tbl[idx];
+
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ entry->mem_size = 0;
+ }
+}
+EXPORT_SYMBOL_GPL(nxpwifi_prepare_fw_dump_info);
+
+/* CFG802.11 network device handler for statistics retrieval.
+ */
+static struct net_device_stats *nxpwifi_get_stats(struct net_device *dev)
+{
+ struct nxpwifi_private *priv = nxpwifi_netdev_get_priv(dev);
+
+ return &priv->stats;
+}
+
+static u16
+nxpwifi_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb,
+ struct net_device *sb_dev)
+{
+ skb->priority = cfg80211_classify8021d(skb, NULL);
+ return nxpwifi_1d_to_wmm_queue[skb->priority];
+}
+
+/* Network device handlers */
+static const struct net_device_ops nxpwifi_netdev_ops = {
+ .ndo_open = nxpwifi_open,
+ .ndo_stop = nxpwifi_close,
+ .ndo_start_xmit = nxpwifi_hard_start_xmit,
+ .ndo_set_mac_address = nxpwifi_ndo_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_tx_timeout = nxpwifi_tx_timeout,
+ .ndo_get_stats = nxpwifi_get_stats,
+ .ndo_set_rx_mode = nxpwifi_set_multicast_list,
+ .ndo_select_queue = nxpwifi_netdev_select_wmm_queue,
+};
+
+/* This function initializes the private structure parameters.
+ *
+ * The following wait queues are initialized -
+ * - IOCTL wait queue
+ * - Command wait queue
+ * - Statistics wait queue
+ *
+ * ...and the following default parameters are set -
+ * - Current key index : Set to 0
+ * - Rate index : Set to auto
+ * - Media connected : Set to disconnected
+ * - Nick name : Set to null
+ * - Number of Tx timeout : Set to 0
+ * - Device address : Set to current address
+ * - Rx histogram statistc : Set to 0
+ *
+ * In addition, the CFG80211 work queue is also created.
+ */
+void nxpwifi_init_priv_params(struct nxpwifi_private *priv,
+ struct net_device *dev)
+{
+ dev->netdev_ops = &nxpwifi_netdev_ops;
+ dev->needs_free_netdev = true;
+ /* Initialize private structure */
+ priv->current_key_index = 0;
+ priv->media_connected = false;
+ memset(priv->mgmt_ie, 0,
+ sizeof(struct nxpwifi_ie) * MAX_MGMT_IE_INDEX);
+ priv->beacon_idx = NXPWIFI_AUTO_IDX_MASK;
+ priv->proberesp_idx = NXPWIFI_AUTO_IDX_MASK;
+ priv->assocresp_idx = NXPWIFI_AUTO_IDX_MASK;
+ priv->gen_idx = NXPWIFI_AUTO_IDX_MASK;
+ priv->num_tx_timeout = 0;
+ if (is_valid_ether_addr(dev->dev_addr))
+ ether_addr_copy(priv->curr_addr, dev->dev_addr);
+ else
+ ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr);
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA ||
+ GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
+ priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL);
+ if (priv->hist_data)
+ nxpwifi_hist_data_reset(priv);
+ }
+}
+
+/* This function check if command is pending.
+ */
+int is_command_pending(struct nxpwifi_adapter *adapter)
+{
+ int is_cmd_pend_q_empty;
+
+ spin_lock_bh(&adapter->cmd_pending_q_lock);
+ is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q);
+ spin_unlock_bh(&adapter->cmd_pending_q_lock);
+
+ return !is_cmd_pend_q_empty;
+}
+
+/* This is the host mlme work queue function.
+ * It handles the host mlme operations.
+ */
+static void nxpwifi_host_mlme_work_queue(struct work_struct *work)
+{
+ struct nxpwifi_adapter *adapter =
+ container_of(work, struct nxpwifi_adapter, host_mlme_work);
+
+ if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags))
+ return;
+
+ /* Check for host mlme disconnection */
+ if (adapter->host_mlme_link_lost) {
+ if (adapter->priv_link_lost) {
+ nxpwifi_reset_connect_state(adapter->priv_link_lost,
+ WLAN_REASON_DEAUTH_LEAVING,
+ true);
+ adapter->priv_link_lost = NULL;
+ }
+ adapter->host_mlme_link_lost = false;
+ }
+
+ /* Check for host mlme Assoc Resp */
+ if (adapter->assoc_resp_received) {
+ nxpwifi_process_assoc_resp(adapter);
+ adapter->assoc_resp_received = false;
+ }
+}
+
+/* This is the RX work queue function.
+ *
+ * It handles the RX operations.
+ */
+static void nxpwifi_rx_work_queue(struct work_struct *work)
+{
+ struct nxpwifi_adapter *adapter =
+ container_of(work, struct nxpwifi_adapter, rx_work);
+
+ if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags))
+ return;
+ nxpwifi_process_rx(adapter);
+}
+
+/* This is the main work queue function.
+ *
+ * It handles the main process, which in turn handles the complete
+ * driver operations.
+ */
+static void nxpwifi_main_work_queue(struct work_struct *work)
+{
+ struct nxpwifi_adapter *adapter =
+ container_of(work, struct nxpwifi_adapter, main_work);
+
+ if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags))
+ return;
+ nxpwifi_main_process(adapter);
+}
+
+/* Common teardown code used for both device removal and reset */
+static void nxpwifi_uninit_sw(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_private *priv;
+ int i;
+
+ /* We can no longer handle interrupts once we start doing the teardown
+ * below.
+ */
+ if (adapter->if_ops.disable_int)
+ adapter->if_ops.disable_int(adapter);
+
+ set_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags);
+ nxpwifi_terminate_workqueue(adapter);
+ adapter->int_status = 0;
+
+ /* Stop data */
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (priv && priv->netdev) {
+ nxpwifi_stop_net_dev_queue(priv->netdev, adapter);
+ if (netif_carrier_ok(priv->netdev))
+ netif_carrier_off(priv->netdev);
+ netif_device_detach(priv->netdev);
+ }
+ }
+
+ nxpwifi_dbg(adapter, CMD, "cmd: calling nxpwifi_shutdown_drv...\n");
+ nxpwifi_shutdown_drv(adapter);
+ nxpwifi_dbg(adapter, CMD, "cmd: nxpwifi_shutdown_drv done\n");
+
+ if (atomic_read(&adapter->rx_pending) ||
+ atomic_read(&adapter->tx_pending) ||
+ atomic_read(&adapter->cmd_pending)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "rx_pending=%d, tx_pending=%d,\t"
+ "cmd_pending=%d\n",
+ atomic_read(&adapter->rx_pending),
+ atomic_read(&adapter->tx_pending),
+ atomic_read(&adapter->cmd_pending));
+ }
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (!priv)
+ continue;
+ rtnl_lock();
+ if (priv->netdev &&
+ priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) {
+ /* Close the netdev now, because if we do it later, the
+ * netdev notifiers will need to acquire the wiphy lock
+ * again --> deadlock.
+ */
+ dev_close(priv->wdev.netdev);
+ wiphy_lock(adapter->wiphy);
+ nxpwifi_del_virtual_intf(adapter->wiphy, &priv->wdev);
+ wiphy_unlock(adapter->wiphy);
+ }
+ rtnl_unlock();
+ }
+
+ wiphy_unregister(adapter->wiphy);
+ wiphy_free(adapter->wiphy);
+ adapter->wiphy = NULL;
+
+ vfree(adapter->chan_stats);
+ nxpwifi_free_cmd_buffers(adapter);
+}
+
+/* This function can be used for shutting down the adapter SW.
+ */
+int nxpwifi_shutdown_sw(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_private *priv;
+
+ if (!adapter)
+ return 0;
+
+ wait_for_completion(adapter->fw_done);
+ /* Caller should ensure we aren't suspending while this happens */
+ reinit_completion(adapter->fw_done);
+
+ priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
+ nxpwifi_deauthenticate(priv, NULL);
+
+ nxpwifi_init_shutdown_fw(priv, NXPWIFI_FUNC_SHUTDOWN);
+
+ nxpwifi_uninit_sw(adapter);
+ adapter->is_up = false;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nxpwifi_shutdown_sw);
+
+/* This function can be used for reinitting the adapter SW. Required
+ * code is extracted from nxpwifi_add_card()
+ */
+int
+nxpwifi_reinit_sw(struct nxpwifi_adapter *adapter)
+{
+ int ret;
+
+ nxpwifi_init_lock_list(adapter);
+ if (adapter->if_ops.up_dev)
+ adapter->if_ops.up_dev(adapter);
+
+ adapter->hw_status = NXPWIFI_HW_STATUS_INITIALIZING;
+ clear_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags);
+ init_waitqueue_head(&adapter->init_wait_q);
+ clear_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags);
+ adapter->hs_activated = false;
+ clear_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags);
+ init_waitqueue_head(&adapter->hs_activate_wait_q);
+ init_waitqueue_head(&adapter->cmd_wait_q.wait);
+ adapter->cmd_wait_q.status = 0;
+ adapter->scan_wait_q_woken = false;
+
+ if (num_possible_cpus() > 1)
+ adapter->rx_work_enabled = true;
+
+ adapter->workqueue =
+ alloc_workqueue("NXPWIFI_WORK_QUEUE",
+ WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
+ if (!adapter->workqueue)
+ goto err_kmalloc;
+
+ INIT_WORK(&adapter->main_work, nxpwifi_main_work_queue);
+
+ if (adapter->rx_work_enabled) {
+ adapter->rx_workqueue = alloc_workqueue("NXPWIFI_RX_WORK_QUEUE",
+ WQ_HIGHPRI |
+ WQ_MEM_RECLAIM |
+ WQ_UNBOUND, 0);
+ if (!adapter->rx_workqueue)
+ goto err_kmalloc;
+ INIT_WORK(&adapter->rx_work, nxpwifi_rx_work_queue);
+ }
+
+ adapter->host_mlme_workqueue =
+ alloc_workqueue("NXPWIFI_HOST_MLME_WORK_QUEUE",
+ WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
+ if (!adapter->host_mlme_workqueue)
+ goto err_kmalloc;
+
+ INIT_WORK(&adapter->host_mlme_work, nxpwifi_host_mlme_work_queue);
+
+ /* Register the device. Fill up the private data structure with
+ * relevant information from the card. Some code extracted from
+ * nxpwifi_register_dev()
+ */
+ nxpwifi_dbg(adapter, INFO, "%s, nxpwifi_init_hw_fw()...\n", __func__);
+
+ if (nxpwifi_init_hw_fw(adapter, false)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: firmware init failed\n", __func__);
+ goto err_init_fw;
+ }
+
+ /* _nxpwifi_fw_dpc() does its own cleanup */
+ ret = _nxpwifi_fw_dpc(adapter->firmware, adapter);
+ if (ret) {
+ pr_err("Failed to bring up adapter: %d\n", ret);
+ return ret;
+ }
+ nxpwifi_dbg(adapter, INFO, "%s, successful\n", __func__);
+
+ return 0;
+
+err_init_fw:
+ nxpwifi_dbg(adapter, ERROR, "info: %s: unregister device\n", __func__);
+ if (adapter->if_ops.unregister_dev)
+ adapter->if_ops.unregister_dev(adapter);
+
+err_kmalloc:
+ set_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags);
+ nxpwifi_terminate_workqueue(adapter);
+ if (adapter->hw_status == NXPWIFI_HW_STATUS_READY) {
+ nxpwifi_dbg(adapter, ERROR,
+ "info: %s: shutdown nxpwifi\n", __func__);
+ nxpwifi_shutdown_drv(adapter);
+ nxpwifi_free_cmd_buffers(adapter);
+ }
+
+ complete_all(adapter->fw_done);
+ nxpwifi_dbg(adapter, INFO, "%s, error\n", __func__);
+
+ return -1;
+}
+EXPORT_SYMBOL_GPL(nxpwifi_reinit_sw);
+
+static irqreturn_t nxpwifi_irq_wakeup_handler(int irq, void *priv)
+{
+ struct nxpwifi_adapter *adapter = priv;
+
+ dev_dbg(adapter->dev, "%s: wake by wifi", __func__);
+ adapter->wake_by_wifi = true;
+ disable_irq_nosync(irq);
+
+ /* Notify PM core we are wakeup source */
+ pm_wakeup_event(adapter->dev, 0);
+ pm_system_wakeup();
+
+ return IRQ_HANDLED;
+}
+
+static void nxpwifi_probe_of(struct nxpwifi_adapter *adapter)
+{
+ int ret;
+ struct device *dev = adapter->dev;
+
+ if (!dev->of_node)
+ goto err_exit;
+
+ adapter->dt_node = dev->of_node;
+ adapter->irq_wakeup = irq_of_parse_and_map(adapter->dt_node, 0);
+ if (!adapter->irq_wakeup) {
+ dev_dbg(dev, "fail to parse irq_wakeup from device tree\n");
+ goto err_exit;
+ }
+
+ ret = devm_request_irq(dev, adapter->irq_wakeup,
+ nxpwifi_irq_wakeup_handler, IRQF_TRIGGER_LOW,
+ "wifi_wake", adapter);
+ if (ret) {
+ dev_err(dev, "Failed to request irq_wakeup %d (%d)\n",
+ adapter->irq_wakeup, ret);
+ goto err_exit;
+ }
+
+ disable_irq(adapter->irq_wakeup);
+ if (device_init_wakeup(dev, true)) {
+ dev_err(dev, "fail to init wakeup for nxpwifi\n");
+ goto err_exit;
+ }
+ return;
+
+err_exit:
+ adapter->irq_wakeup = -1;
+}
+
+/* This function adds the card.
+ *
+ * This function follows the following major steps to set up the device -
+ * - Initialize software. This includes probing the card, registering
+ * the interface operations table, and allocating/initializing the
+ * adapter structure
+ * - Set up the netlink socket
+ * - Create and start the main work queue
+ * - Register the device
+ * - Initialize firmware and hardware
+ * - Add logical interfaces
+ */
+int
+nxpwifi_add_card(void *card, struct completion *fw_done,
+ struct nxpwifi_if_ops *if_ops, u8 iface_type,
+ struct device *dev)
+{
+ struct nxpwifi_adapter *adapter;
+
+ if (nxpwifi_register(card, dev, if_ops, (void **)&adapter)) {
+ pr_err("%s: software init failed\n", __func__);
+ goto err_init_sw;
+ }
+
+ nxpwifi_probe_of(adapter);
+
+ adapter->iface_type = iface_type;
+ adapter->fw_done = fw_done;
+
+ adapter->hw_status = NXPWIFI_HW_STATUS_INITIALIZING;
+ clear_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags);
+ init_waitqueue_head(&adapter->init_wait_q);
+ clear_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags);
+ adapter->hs_activated = false;
+ init_waitqueue_head(&adapter->hs_activate_wait_q);
+ init_waitqueue_head(&adapter->cmd_wait_q.wait);
+ adapter->cmd_wait_q.status = 0;
+ adapter->scan_wait_q_woken = false;
+
+ if (num_possible_cpus() > 1)
+ adapter->rx_work_enabled = true;
+
+ adapter->workqueue =
+ alloc_workqueue("NXPWIFI_WORK_QUEUE",
+ WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
+ if (!adapter->workqueue)
+ goto err_kmalloc;
+
+ INIT_WORK(&adapter->main_work, nxpwifi_main_work_queue);
+
+ if (adapter->rx_work_enabled) {
+ adapter->rx_workqueue = alloc_workqueue("NXPWIFI_RX_WORK_QUEUE",
+ WQ_HIGHPRI |
+ WQ_MEM_RECLAIM |
+ WQ_UNBOUND, 0);
+ if (!adapter->rx_workqueue)
+ goto err_kmalloc;
+
+ INIT_WORK(&adapter->rx_work, nxpwifi_rx_work_queue);
+ }
+
+ adapter->host_mlme_workqueue =
+ alloc_workqueue("NXPWIFI_HOST_MLME_WORK_QUEUE",
+ WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 0);
+ if (!adapter->host_mlme_workqueue)
+ goto err_kmalloc;
+
+ INIT_WORK(&adapter->host_mlme_work, nxpwifi_host_mlme_work_queue);
+
+ /* Register the device. Fill up the private data structure with relevant
+ * information from the card.
+ */
+ if (adapter->if_ops.register_dev(adapter)) {
+ pr_err("%s: failed to register nxpwifi device\n", __func__);
+ goto err_registerdev;
+ }
+
+ if (nxpwifi_init_hw_fw(adapter, true)) {
+ pr_err("%s: firmware init failed\n", __func__);
+ goto err_init_fw;
+ }
+
+ return 0;
+
+err_init_fw:
+ pr_debug("info: %s: unregister device\n", __func__);
+ if (adapter->if_ops.unregister_dev)
+ adapter->if_ops.unregister_dev(adapter);
+err_registerdev:
+ set_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags);
+ nxpwifi_terminate_workqueue(adapter);
+ if (adapter->hw_status == NXPWIFI_HW_STATUS_READY) {
+ pr_debug("info: %s: shutdown nxpwifi\n", __func__);
+ nxpwifi_shutdown_drv(adapter);
+ nxpwifi_free_cmd_buffers(adapter);
+ }
+err_kmalloc:
+ if (adapter->irq_wakeup >= 0)
+ device_init_wakeup(adapter->dev, false);
+ nxpwifi_free_adapter(adapter);
+
+err_init_sw:
+
+ return -1;
+}
+EXPORT_SYMBOL_GPL(nxpwifi_add_card);
+
+/* This function removes the card.
+ *
+ * This function follows the following major steps to remove the device -
+ * - Stop data traffic
+ * - Shutdown firmware
+ * - Remove the logical interfaces
+ * - Terminate the work queue
+ * - Unregister the device
+ * - Free the adapter structure
+ */
+int nxpwifi_remove_card(struct nxpwifi_adapter *adapter)
+{
+ if (!adapter)
+ return 0;
+
+ if (adapter->is_up)
+ nxpwifi_uninit_sw(adapter);
+
+ if (adapter->irq_wakeup >= 0)
+ device_init_wakeup(adapter->dev, false);
+
+ /* Unregister device */
+ nxpwifi_dbg(adapter, INFO,
+ "info: unregister device\n");
+ if (adapter->if_ops.unregister_dev)
+ adapter->if_ops.unregister_dev(adapter);
+ /* Free adapter structure */
+ nxpwifi_dbg(adapter, INFO,
+ "info: free adapter\n");
+ nxpwifi_free_adapter(adapter);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nxpwifi_remove_card);
+
+void _nxpwifi_dbg(const struct nxpwifi_adapter *adapter, int mask,
+ const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ if (!(adapter->debug_mask & mask))
+ return;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (adapter->dev)
+ dev_info(adapter->dev, "%pV", &vaf);
+ else
+ pr_info("%pV", &vaf);
+
+ va_end(args);
+}
+EXPORT_SYMBOL_GPL(_nxpwifi_dbg);
+
+/* This function initializes the module.
+ *
+ * The debug FS is also initialized if configured.
+ */
+static int
+nxpwifi_init_module(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ nxpwifi_debugfs_init();
+#endif
+ return 0;
+}
+
+/* This function cleans up the module.
+ *
+ * The debug FS is removed if available.
+ */
+static void
+nxpwifi_cleanup_module(void)
+{
+#ifdef CONFIG_DEBUG_FS
+ nxpwifi_debugfs_remove();
+#endif
+}
+
+module_init(nxpwifi_init_module);
+module_exit(nxpwifi_cleanup_module);
+
+MODULE_AUTHOR("NXP International Ltd.");
+MODULE_DESCRIPTION("NXP WiFi Driver version " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 24/43] wifi: nxpwifi: add main.h
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (22 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 23/43] wifi: nxpwifi: add main.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 25/43] wifi: nxpwifi: add scan.c David Lin
` (20 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/main.h | 1507 +++++++++++++++++++++++
1 file changed, 1507 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/main.h
diff --git a/drivers/net/wireless/nxp/nxpwifi/main.h b/drivers/net/wireless/nxp/nxpwifi/main.h
new file mode 100644
index 000000000000..e9878ea03d6e
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/main.h
@@ -0,0 +1,1507 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NXP Wireless LAN device driver: major data structures and prototypes
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#ifndef _NXPWIFI_MAIN_H_
+#define _NXPWIFI_MAIN_H_
+
+#include <linux/completion.h>
+#include <linux/kernel.h>
+#include <linux/kstrtox.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/ip.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <net/sock.h>
+#include <net/lib80211.h>
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+#include <linux/ctype.h>
+#include <linux/of.h>
+#include <linux/idr.h>
+#include <linux/inetdevice.h>
+#include <linux/devcoredump.h>
+#include <linux/err.h>
+#include <linux/gfp.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/of_irq.h>
+#include <linux/workqueue.h>
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "sdio.h"
+
+extern char driver_version[];
+
+struct nxpwifi_adapter;
+struct nxpwifi_private;
+
+enum {
+ NXPWIFI_ASYNC_CMD,
+ NXPWIFI_SYNC_CMD
+};
+
+#define NXPWIFI_MAX_AP 64
+
+#define NXPWIFI_MAX_PKTS_TXQ 16
+
+#define NXPWIFI_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ)
+
+#define NXPWIFI_TIMER_10S 10000
+#define NXPWIFI_TIMER_1S 1000
+
+#define MAX_TX_PENDING 400
+#define LOW_TX_PENDING 380
+
+#define HIGH_RX_PENDING 50
+#define LOW_RX_PENDING 20
+
+#define NXPWIFI_UPLD_SIZE (2312)
+
+#define MAX_EVENT_SIZE 2048
+
+#define NXPWIFI_FW_DUMP_SIZE (2 * 1024 * 1024)
+
+#define ARP_FILTER_MAX_BUF_SIZE 68
+
+#define NXPWIFI_KEY_BUFFER_SIZE 16
+#define NXPWIFI_DEFAULT_LISTEN_INTERVAL 10
+#define NXPWIFI_MAX_REGION_CODE 9
+
+#define DEFAULT_BCN_AVG_FACTOR 8
+#define DEFAULT_DATA_AVG_FACTOR 8
+
+#define FIRST_VALID_CHANNEL 0xff
+
+#define DEFAULT_BCN_MISS_TIMEOUT 5
+
+#define MAX_SCAN_BEACON_BUFFER 8000
+
+#define SCAN_BEACON_ENTRY_PAD 6
+
+#define NXPWIFI_PASSIVE_SCAN_CHAN_TIME 110
+#define NXPWIFI_ACTIVE_SCAN_CHAN_TIME 40
+#define NXPWIFI_SPECIFIC_SCAN_CHAN_TIME 40
+#define NXPWIFI_DEF_SCAN_CHAN_GAP_TIME 50
+
+#define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI)))
+
+#define NXPWIFI_MAX_TOTAL_SCAN_TIME (NXPWIFI_TIMER_10S - NXPWIFI_TIMER_1S)
+
+#define WPA_GTK_OUI_OFFSET 2
+#define RSN_GTK_OUI_OFFSET 2
+
+#define NXPWIFI_OUI_NOT_PRESENT 0
+#define NXPWIFI_OUI_PRESENT 1
+
+#define PKT_TYPE_MGMT 0xE5
+
+/* Do not check for data_received for USB, as data_received
+ * is handled in nxpwifi_usb_recv for USB
+ */
+#define IS_CARD_RX_RCVD(adapter) ({ \
+ typeof(adapter) (_adapter) = adapter; \
+ ((_adapter)->cmd_resp_received || \
+ (_adapter)->event_received || \
+ (_adapter)->data_received); \
+ })
+
+#define NXPWIFI_TYPE_CMD 1
+#define NXPWIFI_TYPE_DATA 0
+#define NXPWIFI_TYPE_AGGR_DATA 10
+#define NXPWIFI_TYPE_EVENT 3
+
+#define MAX_BITMAP_RATES_SIZE 18
+
+#define MAX_CHANNEL_BAND_BG 14
+#define MAX_CHANNEL_BAND_A 165
+
+#define MAX_FREQUENCY_BAND_BG 2484
+
+#define NXPWIFI_EVENT_HEADER_LEN 4
+#define NXPWIFI_UAP_EVENT_EXTRA_HEADER 2
+
+#define NXPWIFI_TYPE_LEN 4
+#define NXPWIFI_USB_TYPE_CMD 0xF00DFACE
+#define NXPWIFI_USB_TYPE_DATA 0xBEADC0DE
+#define NXPWIFI_USB_TYPE_EVENT 0xBEEFFACE
+
+/* Threshold for tx_timeout_cnt before we trigger a card reset */
+#define TX_TIMEOUT_THRESHOLD 6
+
+#define NXPWIFI_DRV_INFO_SIZE_MAX 0x40000
+
+/* Address alignment */
+#define NXPWIFI_ALIGN_ADDR(p, a) ({ \
+ typeof(a) (_a) = a; \
+ (((long)(p) + (_a) - 1) & ~((_a) - 1)); \
+ })
+
+#define NXPWIFI_MAC_LOCAL_ADMIN_BIT 41
+
+/**
+ *enum nxpwifi_debug_level - nxp wifi debug level
+ */
+enum NXPWIFI_DEBUG_LEVEL {
+ NXPWIFI_DBG_MSG = 0x00000001,
+ NXPWIFI_DBG_FATAL = 0x00000002,
+ NXPWIFI_DBG_ERROR = 0x00000004,
+ NXPWIFI_DBG_DATA = 0x00000008,
+ NXPWIFI_DBG_CMD = 0x00000010,
+ NXPWIFI_DBG_EVENT = 0x00000020,
+ NXPWIFI_DBG_INTR = 0x00000040,
+ NXPWIFI_DBG_IOCTL = 0x00000080,
+
+ NXPWIFI_DBG_MPA_D = 0x00008000,
+ NXPWIFI_DBG_DAT_D = 0x00010000,
+ NXPWIFI_DBG_CMD_D = 0x00020000,
+ NXPWIFI_DBG_EVT_D = 0x00040000,
+ NXPWIFI_DBG_FW_D = 0x00080000,
+ NXPWIFI_DBG_IF_D = 0x00100000,
+
+ NXPWIFI_DBG_ENTRY = 0x10000000,
+ NXPWIFI_DBG_WARN = 0x20000000,
+ NXPWIFI_DBG_INFO = 0x40000000,
+ NXPWIFI_DBG_DUMP = 0x80000000,
+
+ NXPWIFI_DBG_ANY = 0xffffffff
+};
+
+#define NXPWIFI_DEFAULT_DEBUG_MASK (NXPWIFI_DBG_MSG | \
+ NXPWIFI_DBG_FATAL | \
+ NXPWIFI_DBG_ERROR)
+
+__printf(3, 4)
+void _nxpwifi_dbg(const struct nxpwifi_adapter *adapter, int mask,
+ const char *fmt, ...);
+#define nxpwifi_dbg(adapter, mask, fmt, ...) \
+ _nxpwifi_dbg(adapter, NXPWIFI_DBG_##mask, fmt, ##__VA_ARGS__)
+
+#define DEBUG_DUMP_DATA_MAX_LEN 128
+#define nxpwifi_dbg_dump(adapter, dbg_mask, str, buf, len) \
+do { \
+ if ((adapter)->debug_mask & NXPWIFI_DBG_##dbg_mask) \
+ print_hex_dump(KERN_DEBUG, str, \
+ DUMP_PREFIX_OFFSET, 16, 1, \
+ buf, len, false); \
+} while (0)
+
+/** Min BGSCAN interval 15 second */
+#define NXPWIFI_BGSCAN_INTERVAL 15000
+/** default repeat count */
+#define NXPWIFI_BGSCAN_REPEAT_COUNT 6
+
+struct nxpwifi_dbg {
+ u32 num_cmd_host_to_card_failure;
+ u32 num_cmd_sleep_cfm_host_to_card_failure;
+ u32 num_tx_host_to_card_failure;
+ u32 num_event_deauth;
+ u32 num_event_disassoc;
+ u32 num_event_link_lost;
+ u32 num_cmd_deauth;
+ u32 num_cmd_assoc_success;
+ u32 num_cmd_assoc_failure;
+ u32 num_tx_timeout;
+ u16 timeout_cmd_id;
+ u16 timeout_cmd_act;
+ u16 last_cmd_id[DBG_CMD_NUM];
+ u16 last_cmd_act[DBG_CMD_NUM];
+ u16 last_cmd_index;
+ u16 last_cmd_resp_id[DBG_CMD_NUM];
+ u16 last_cmd_resp_index;
+ u16 last_event[DBG_CMD_NUM];
+ u16 last_event_index;
+ u32 last_mp_wr_bitmap[NXPWIFI_DBG_SDIO_MP_NUM];
+ u32 last_mp_wr_ports[NXPWIFI_DBG_SDIO_MP_NUM];
+ u32 last_mp_wr_len[NXPWIFI_DBG_SDIO_MP_NUM];
+ u32 last_mp_curr_wr_port[NXPWIFI_DBG_SDIO_MP_NUM];
+ u8 last_sdio_mp_index;
+};
+
+enum NXPWIFI_HARDWARE_STATUS {
+ NXPWIFI_HW_STATUS_READY,
+ NXPWIFI_HW_STATUS_INITIALIZING,
+ NXPWIFI_HW_STATUS_INIT_DONE,
+ NXPWIFI_HW_STATUS_RESET,
+ NXPWIFI_HW_STATUS_NOT_READY
+};
+
+enum NXPWIFI_802_11_POWER_MODE {
+ NXPWIFI_802_11_POWER_MODE_CAM,
+ NXPWIFI_802_11_POWER_MODE_PSP
+};
+
+struct nxpwifi_tx_param {
+ u32 next_pkt_len;
+};
+
+enum NXPWIFI_PS_STATE {
+ PS_STATE_AWAKE,
+ PS_STATE_PRE_SLEEP,
+ PS_STATE_SLEEP_CFM,
+ PS_STATE_SLEEP
+};
+
+enum nxpwifi_iface_type {
+ NXPWIFI_SDIO
+};
+
+struct nxpwifi_add_ba_param {
+ u32 tx_win_size;
+ u32 rx_win_size;
+ u32 timeout;
+ u8 tx_amsdu;
+ u8 rx_amsdu;
+};
+
+struct nxpwifi_tx_aggr {
+ u8 ampdu_user;
+ u8 ampdu_ap;
+ u8 amsdu;
+};
+
+enum nxpwifi_ba_status {
+ BA_SETUP_NONE = 0,
+ BA_SETUP_INPROGRESS,
+ BA_SETUP_COMPLETE
+};
+
+struct nxpwifi_ra_list_tbl {
+ struct list_head list;
+ struct sk_buff_head skb_head;
+ u8 ra[ETH_ALEN];
+ u32 is_11n_enabled;
+ u16 max_amsdu;
+ u16 ba_pkt_count;
+ u8 ba_packet_thr;
+ enum nxpwifi_ba_status ba_status;
+ u8 amsdu_in_ampdu;
+ u16 total_pkt_count;
+ bool tx_paused;
+};
+
+struct nxpwifi_tid_tbl {
+ struct list_head ra_list;
+};
+
+#define WMM_HIGHEST_PRIORITY 7
+#define HIGH_PRIO_TID 7
+#define LOW_PRIO_TID 0
+#define NO_PKT_PRIO_TID -1
+#define NXPWIFI_WMM_DRV_DELAY_MAX 510
+
+struct nxpwifi_wmm_desc {
+ struct nxpwifi_tid_tbl tid_tbl_ptr[MAX_NUM_TID];
+ u32 packets_out[MAX_NUM_TID];
+ u32 pkts_paused[MAX_NUM_TID];
+ /* spin lock to protect ra_list */
+ spinlock_t ra_list_spinlock;
+ struct nxpwifi_wmm_ac_status ac_status[IEEE80211_NUM_ACS];
+ enum nxpwifi_wmm_ac_e ac_down_graded_vals[IEEE80211_NUM_ACS];
+ u32 drv_pkt_delay_max;
+ u8 queue_priority[IEEE80211_NUM_ACS];
+ u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */
+ /* Number of transmit packets queued */
+ atomic_t tx_pkts_queued;
+ /* Tracks highest priority with a packet queued */
+ atomic_t highest_queued_prio;
+};
+
+struct nxpwifi_802_11_security {
+ u8 wpa_enabled;
+ u8 wpa2_enabled;
+ u8 wep_enabled;
+ u32 authentication_mode;
+ u8 is_authtype_auto;
+ u32 encryption_mode;
+};
+
+struct ieee_types_header {
+ u8 element_id;
+ u8 len;
+} __packed;
+
+struct ieee_types_vendor_specific {
+ struct ieee_types_vendor_header vend_hdr;
+ u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_vendor_header)];
+} __packed;
+
+struct ieee_types_generic {
+ struct ieee_types_header ieee_hdr;
+ u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)];
+} __packed;
+
+struct ieee_types_bss_co_2040 {
+ struct ieee_types_header ieee_hdr;
+ u8 bss_2040co;
+} __packed;
+
+struct ieee_types_extcap {
+ struct ieee_types_header ieee_hdr;
+ u8 ext_capab[8];
+} __packed;
+
+struct ieee_types_vht_cap {
+ struct ieee_types_header ieee_hdr;
+ struct ieee80211_vht_cap vhtcap;
+} __packed;
+
+struct ieee_types_vht_oper {
+ struct ieee_types_header ieee_hdr;
+ struct ieee80211_vht_operation vhtoper;
+} __packed;
+
+struct ieee_types_aid {
+ struct ieee_types_header ieee_hdr;
+ u16 aid;
+} __packed;
+
+struct nxpwifi_bssdescriptor {
+ u8 mac_address[ETH_ALEN];
+ struct cfg80211_ssid ssid;
+ u32 privacy;
+ s32 rssi;
+ u32 channel;
+ u32 freq;
+ u16 beacon_period;
+ u8 erp_flags;
+ u32 bss_mode;
+ u8 supported_rates[NXPWIFI_SUPPORTED_RATES];
+ u8 data_rates[NXPWIFI_SUPPORTED_RATES];
+ /* Network band.
+ * BAND_B(0x01): 'b' band
+ * BAND_G(0x02): 'g' band
+ * BAND_A(0X04): 'a' band
+ */
+ u16 bss_band;
+ u64 fw_tsf;
+ u64 timestamp;
+ union ieee_types_phy_param_set phy_param_set;
+ union ieee_types_ss_param_set ss_param_set;
+ u16 cap_info_bitmap;
+ struct ieee_types_wmm_parameter wmm_ie;
+ u8 disable_11n;
+ struct ieee80211_ht_cap *bcn_ht_cap;
+ u16 ht_cap_offset;
+ struct ieee80211_ht_operation *bcn_ht_oper;
+ u16 ht_info_offset;
+ u8 *bcn_bss_co_2040;
+ u16 bss_co_2040_offset;
+ u8 *bcn_ext_cap;
+ u16 ext_cap_offset;
+ struct ieee80211_vht_cap *bcn_vht_cap;
+ u16 vht_cap_offset;
+ struct ieee80211_vht_operation *bcn_vht_oper;
+ u16 vht_info_offset;
+ struct ieee_types_oper_mode_ntf *oper_mode;
+ u16 oper_mode_offset;
+ u8 disable_11ac;
+ struct ieee_types_vendor_specific *bcn_wpa_ie;
+ u16 wpa_offset;
+ struct ieee_types_generic *bcn_rsn_ie;
+ u16 rsn_offset;
+ struct ieee_types_generic *bcn_rsnx_ie;
+ u16 rsnx_offset;
+ u8 *beacon_buf;
+ u32 beacon_buf_size;
+ u8 sensed_11h;
+ u8 local_constraint;
+ u8 chan_sw_ie_present;
+};
+
+struct nxpwifi_current_bss_params {
+ struct nxpwifi_bssdescriptor bss_descriptor;
+ u8 wmm_enabled;
+ u8 wmm_uapsd_enabled;
+ u8 band;
+ u32 num_of_rates;
+ u8 data_rates[NXPWIFI_SUPPORTED_RATES];
+};
+
+struct nxpwifi_sleep_period {
+ u16 period;
+ u16 reserved;
+};
+
+struct nxpwifi_wep_key {
+ u32 length;
+ u32 key_index;
+ u32 key_length;
+ u8 key_material[NXPWIFI_KEY_BUFFER_SIZE];
+};
+
+#define MAX_REGION_CHANNEL_NUM 2
+
+struct nxpwifi_chan_freq_power {
+ u16 channel;
+ u32 freq;
+ u16 max_tx_power;
+ u8 unsupported;
+};
+
+enum state_11d_t {
+ DISABLE_11D = 0,
+ ENABLE_11D = 1,
+};
+
+#define NXPWIFI_MAX_TRIPLET_802_11D 83
+
+struct nxpwifi_802_11d_domain_reg {
+ u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
+ u8 no_of_triplet;
+ struct ieee80211_country_ie_triplet
+ triplet[NXPWIFI_MAX_TRIPLET_802_11D];
+};
+
+struct nxpwifi_vendor_spec_cfg_ie {
+ u16 mask;
+ u16 flag;
+ u8 ie[NXPWIFI_MAX_VSIE_LEN];
+};
+
+struct wps {
+ u8 session_enable;
+};
+
+struct nxpwifi_roc_cfg {
+ u64 cookie;
+ struct ieee80211_channel chan;
+};
+
+enum nxpwifi_iface_work_flags {
+ NXPWIFI_IFACE_WORK_DEVICE_DUMP,
+ NXPWIFI_IFACE_WORK_CARD_RESET,
+};
+
+enum nxpwifi_adapter_work_flags {
+ NXPWIFI_SURPRISE_REMOVED,
+ NXPWIFI_IS_CMD_TIMEDOUT,
+ NXPWIFI_IS_SUSPENDED,
+ NXPWIFI_IS_HS_CONFIGURED,
+ NXPWIFI_IS_HS_ENABLING,
+ NXPWIFI_IS_REQUESTING_FW_VEREXT,
+};
+
+struct nxpwifi_band_config {
+ u8 chan_band:2;
+ u8 chan_width:2;
+ u8 chan2_offset:2;
+ u8 scan_mode:2;
+} __packed;
+
+struct nxpwifi_channel_band {
+ struct nxpwifi_band_config band_config;
+ u8 channel;
+};
+
+struct nxpwifi_private {
+ struct nxpwifi_adapter *adapter;
+ u8 bss_type;
+ u8 bss_role;
+ u8 bss_priority;
+ u8 bss_num;
+ u8 bss_started;
+ u8 auth_flag;
+ u16 auth_alg;
+ u8 frame_type;
+ u8 curr_addr[ETH_ALEN];
+ u8 media_connected;
+ u8 port_open;
+ u8 usb_port;
+ u32 num_tx_timeout;
+ /* track consecutive timeout */
+ u8 tx_timeout_cnt;
+ struct net_device *netdev;
+ struct net_device_stats stats;
+ u32 curr_pkt_filter;
+ u32 bss_mode;
+ u32 pkt_tx_ctrl;
+ u16 tx_power_level;
+ u8 max_tx_power_level;
+ u8 min_tx_power_level;
+ u32 tx_ant;
+ u32 rx_ant;
+ u8 tx_rate;
+ u8 tx_htinfo;
+ u8 rxpd_htinfo;
+ u8 rxpd_rate;
+ u16 rate_bitmap;
+ u16 bitmap_rates[MAX_BITMAP_RATES_SIZE];
+ u32 data_rate;
+ u8 is_data_rate_auto;
+ u16 bcn_avg_factor;
+ u16 data_avg_factor;
+ s16 data_rssi_last;
+ s16 data_nf_last;
+ s16 data_rssi_avg;
+ s16 data_nf_avg;
+ s16 bcn_rssi_last;
+ s16 bcn_nf_last;
+ s16 bcn_rssi_avg;
+ s16 bcn_nf_avg;
+ struct nxpwifi_bssdescriptor *attempted_bss_desc;
+ struct cfg80211_ssid prev_ssid;
+ u8 prev_bssid[ETH_ALEN];
+ struct nxpwifi_current_bss_params curr_bss_params;
+ u16 beacon_period;
+ u8 dtim_period;
+ u16 listen_interval;
+ u16 atim_window;
+ struct nxpwifi_802_11_security sec_info;
+ struct nxpwifi_wep_key wep_key[NUM_WEP_KEYS];
+ u16 wep_key_curr_index;
+ u8 wpa_ie[256];
+ u16 wpa_ie_len;
+ u8 wpa_is_gtk_set;
+ struct host_cmd_ds_802_11_key_material aes_key;
+ u8 *wps_ie;
+ u16 wps_ie_len;
+ u8 wmm_required;
+ u8 wmm_enabled;
+ u8 wmm_qosinfo;
+ struct nxpwifi_wmm_desc wmm;
+ atomic_t wmm_tx_pending[IEEE80211_NUM_ACS];
+ struct list_head sta_list;
+ /* spin lock for associated station list */
+ spinlock_t sta_list_spinlock;
+ struct list_head tx_ba_stream_tbl_ptr;
+ /* spin lock for tx_ba_stream_tbl_ptr queue */
+ spinlock_t tx_ba_stream_tbl_lock;
+ struct nxpwifi_tx_aggr aggr_prio_tbl[MAX_NUM_TID];
+ struct nxpwifi_add_ba_param add_ba_param;
+ u16 rx_seq[MAX_NUM_TID];
+ u8 tos_to_tid_inv[MAX_NUM_TID];
+ struct list_head rx_reorder_tbl_ptr;
+ /* spin lock for rx_reorder_tbl_ptr queue */
+ spinlock_t rx_reorder_tbl_lock;
+#define NXPWIFI_ASSOC_RSP_BUF_SIZE 500
+ u8 assoc_rsp_buf[NXPWIFI_ASSOC_RSP_BUF_SIZE];
+ u32 assoc_rsp_size;
+ struct cfg80211_bss *req_bss;
+
+#define NXPWIFI_GENIE_BUF_SIZE 256
+ u8 gen_ie_buf[NXPWIFI_GENIE_BUF_SIZE];
+ u8 gen_ie_buf_len;
+
+ struct nxpwifi_vendor_spec_cfg_ie vs_ie[NXPWIFI_MAX_VSIE_NUM];
+
+#define NXPWIFI_ASSOC_TLV_BUF_SIZE 256
+ u8 assoc_tlv_buf[NXPWIFI_ASSOC_TLV_BUF_SIZE];
+ u8 assoc_tlv_buf_len;
+
+ u8 *curr_bcn_buf;
+ u32 curr_bcn_size;
+ /* spin lock for beacon buffer */
+ spinlock_t curr_bcn_buf_lock;
+ struct wireless_dev wdev;
+ struct nxpwifi_chan_freq_power cfp;
+ u32 versionstrsel;
+ char version_str[NXPWIFI_VERSION_STR_LENGTH];
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dfs_dev_dir;
+#endif
+ u16 current_key_index;
+ /* mutex for scan */
+ struct mutex async_mutex;
+ struct cfg80211_scan_request *scan_request;
+ u8 cfg_bssid[6];
+ struct wps wps;
+ u8 scan_block;
+ s32 cqm_rssi_thold;
+ u32 cqm_rssi_hyst;
+ u8 subsc_evt_rssi_state;
+ struct nxpwifi_ds_misc_subsc_evt async_subsc_evt_storage;
+ struct nxpwifi_ie mgmt_ie[MAX_MGMT_IE_INDEX];
+ u16 beacon_idx;
+ u16 proberesp_idx;
+ u16 assocresp_idx;
+ u16 gen_idx;
+ u8 ap_11n_enabled;
+ u8 ap_11ac_enabled;
+ bool host_mlme_reg;
+ u32 mgmt_frame_mask;
+ struct nxpwifi_roc_cfg roc_cfg;
+ bool scan_aborting;
+ u8 sched_scanning;
+ u8 csa_chan;
+ unsigned long csa_expire_time;
+ u8 del_list_idx;
+ bool hs2_enabled;
+ struct nxpwifi_uap_bss_param bss_cfg;
+ struct cfg80211_chan_def bss_chandef;
+ struct station_parameters *sta_params;
+ struct idr ack_status_frames;
+ /* spin lock for ack status */
+ spinlock_t ack_status_lock;
+ /** rx histogram data */
+ struct nxpwifi_histogram_data *hist_data;
+ struct cfg80211_chan_def dfs_chandef;
+ struct workqueue_struct *dfs_cac_workqueue;
+ struct delayed_work dfs_cac_work;
+ struct workqueue_struct *dfs_chan_sw_workqueue;
+ struct delayed_work dfs_chan_sw_work;
+ bool uap_stop_tx;
+ struct cfg80211_beacon_data beacon_after;
+ struct nxpwifi_11h_intf_state state_11h;
+ struct nxpwifi_ds_mem_rw mem_rw;
+ struct sk_buff_head bypass_txq;
+ struct nxpwifi_user_scan_chan hidden_chan[NXPWIFI_USER_SCAN_CHAN_MAX];
+ u8 assoc_resp_ht_param;
+ bool ht_param_present;
+};
+
+struct nxpwifi_tx_ba_stream_tbl {
+ struct list_head list;
+ int tid;
+ u8 ra[ETH_ALEN];
+ enum nxpwifi_ba_status ba_status;
+ u8 amsdu;
+};
+
+struct nxpwifi_rx_reorder_tbl;
+
+struct reorder_tmr_cnxt {
+ struct timer_list timer;
+ struct nxpwifi_rx_reorder_tbl *ptr;
+ struct nxpwifi_private *priv;
+ u8 timer_is_set;
+};
+
+struct nxpwifi_rx_reorder_tbl {
+ struct list_head list;
+ int tid;
+ u8 ta[ETH_ALEN];
+ int init_win;
+ int start_win;
+ int win_size;
+ void **rx_reorder_ptr;
+ struct reorder_tmr_cnxt timer_context;
+ u8 amsdu;
+ u8 flags;
+};
+
+struct nxpwifi_bss_prio_node {
+ struct list_head list;
+ struct nxpwifi_private *priv;
+};
+
+struct nxpwifi_bss_prio_tbl {
+ struct list_head bss_prio_head;
+ /* spin lock for bss priority */
+ spinlock_t bss_prio_lock;
+ struct nxpwifi_bss_prio_node *bss_prio_cur;
+};
+
+struct cmd_ctrl_node {
+ struct list_head list;
+ struct nxpwifi_private *priv;
+ u32 cmd_no;
+ u32 cmd_flag;
+ struct sk_buff *cmd_skb;
+ struct sk_buff *resp_skb;
+ void *data_buf;
+ u32 wait_q_enabled;
+ struct sk_buff *skb;
+ u8 *condition;
+ u8 cmd_wait_q_woken;
+ int (*cmd_resp)(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf);
+};
+
+struct nxpwifi_bss_priv {
+ u8 band;
+ u64 fw_tsf;
+};
+
+struct nxpwifi_station_stats {
+ u64 last_rx;
+ s8 rssi;
+ u64 rx_bytes;
+ u64 tx_bytes;
+ u32 rx_packets;
+ u32 tx_packets;
+ u32 tx_failed;
+ u8 last_tx_rate;
+ u8 last_tx_htinfo;
+};
+
+/* This is AP specific structure which stores information
+ * about associated/peer STA
+ */
+struct nxpwifi_sta_node {
+ struct list_head list;
+ u8 mac_addr[ETH_ALEN];
+ u8 is_wmm_enabled;
+ u8 is_11n_enabled;
+ u8 is_11ac_enabled;
+ u8 ampdu_sta[MAX_NUM_TID];
+ u16 rx_seq[MAX_NUM_TID];
+ u16 max_amsdu;
+ struct nxpwifi_station_stats stats;
+ u8 tx_pause;
+};
+
+#define NXPWIFI_TYPE_AGGR_DATA_V2 11
+#define NXPWIFI_BUS_AGGR_MODE_LEN_V2 (2)
+#define NXPWIFI_BUS_AGGR_MAX_LEN 16000
+#define NXPWIFI_BUS_AGGR_MAX_NUM 10
+struct bus_aggr_params {
+ u16 enable;
+ u16 mode;
+ u16 tx_aggr_max_size;
+ u16 tx_aggr_max_num;
+ u16 tx_aggr_align;
+};
+
+struct nxpwifi_if_ops {
+ int (*init_if)(struct nxpwifi_adapter *adapter);
+ void (*cleanup_if)(struct nxpwifi_adapter *adapter);
+ int (*check_fw_status)(struct nxpwifi_adapter *adapter, u32 poll_num);
+ int (*check_winner_status)(struct nxpwifi_adapter *adapter);
+ int (*prog_fw)(struct nxpwifi_adapter *adapter,
+ struct nxpwifi_fw_image *fw);
+ int (*register_dev)(struct nxpwifi_adapter *adapter);
+ void (*unregister_dev)(struct nxpwifi_adapter *adapter);
+ int (*enable_int)(struct nxpwifi_adapter *adapter);
+ void (*disable_int)(struct nxpwifi_adapter *adapter);
+ int (*process_int_status)(struct nxpwifi_adapter *adapter);
+ int (*host_to_card)(struct nxpwifi_adapter *adapter, u8 type,
+ struct sk_buff *skb,
+ struct nxpwifi_tx_param *tx_param);
+ int (*wakeup)(struct nxpwifi_adapter *adapter);
+ int (*wakeup_complete)(struct nxpwifi_adapter *adapter);
+
+ /* Interface specific functions */
+ void (*update_mp_end_port)(struct nxpwifi_adapter *adapter, u16 port);
+ void (*cleanup_mpa_buf)(struct nxpwifi_adapter *adapter);
+ int (*cmdrsp_complete)(struct nxpwifi_adapter *adapter,
+ struct sk_buff *skb);
+ int (*event_complete)(struct nxpwifi_adapter *adapter,
+ struct sk_buff *skb);
+ int (*dnld_fw)(struct nxpwifi_adapter *adapter,
+ struct nxpwifi_fw_image *fw);
+ void (*card_reset)(struct nxpwifi_adapter *adapter);
+ int (*reg_dump)(struct nxpwifi_adapter *adapter, char *drv_buf);
+ void (*device_dump)(struct nxpwifi_adapter *adapter);
+ void (*iface_work)(struct work_struct *work);
+ void (*deaggr_pkt)(struct nxpwifi_adapter *adapter,
+ struct sk_buff *skb);
+ bool (*is_port_ready)(struct nxpwifi_private *adapter);
+ void (*up_dev)(struct nxpwifi_adapter *adapter);
+};
+
+struct nxpwifi_adapter {
+ u8 iface_type;
+ unsigned int debug_mask;
+ struct nxpwifi_iface_comb iface_limit;
+ struct nxpwifi_iface_comb curr_iface_comb;
+ struct nxpwifi_private *priv[NXPWIFI_MAX_BSS_NUM];
+ u8 priv_num;
+ const struct firmware *firmware;
+ char fw_name[32];
+ int winner;
+ struct device *dev;
+ struct wiphy *wiphy;
+ u8 perm_addr[ETH_ALEN];
+ unsigned long work_flags;
+ u32 fw_release_number;
+ u8 intf_hdr_len;
+ u16 init_wait_q_woken;
+ wait_queue_head_t init_wait_q;
+ void *card;
+ struct nxpwifi_if_ops if_ops;
+ atomic_t bypass_tx_pending;
+ atomic_t rx_pending;
+ atomic_t tx_pending;
+ atomic_t cmd_pending;
+ atomic_t tx_hw_pending;
+ struct workqueue_struct *workqueue;
+ struct work_struct main_work;
+ struct workqueue_struct *rx_workqueue;
+ struct work_struct rx_work;
+ struct workqueue_struct *host_mlme_workqueue;
+ struct work_struct host_mlme_work;
+ bool rx_work_enabled;
+ bool rx_processing;
+ bool delay_main_work;
+ bool rx_locked;
+ bool main_locked;
+ struct nxpwifi_bss_prio_tbl bss_prio_tbl[NXPWIFI_MAX_BSS_NUM];
+ /* spin lock for main process */
+ spinlock_t main_proc_lock;
+ u32 nxpwifi_processing;
+ u8 more_task_flag;
+ u16 tx_buf_size;
+ u16 curr_tx_buf_size;
+ /* sdio single port rx aggregation capability */
+ bool host_disable_sdio_rx_aggr;
+ bool sdio_rx_aggr_enable;
+ u16 sdio_rx_block_size;
+ u32 ioport;
+ enum NXPWIFI_HARDWARE_STATUS hw_status;
+ u16 number_of_antenna;
+ u32 fw_cap_info;
+ /* spin lock for interrupt handling */
+ spinlock_t int_lock;
+ u8 int_status;
+ u32 event_cause;
+ struct sk_buff *event_skb;
+ u8 upld_buf[NXPWIFI_UPLD_SIZE];
+ u8 data_sent;
+ u8 cmd_sent;
+ u8 cmd_resp_received;
+ u8 event_received;
+ u8 data_received;
+ u8 assoc_resp_received;
+ struct nxpwifi_private *priv_link_lost;
+ u8 host_mlme_link_lost;
+ u16 seq_num;
+ struct cmd_ctrl_node *cmd_pool;
+ struct cmd_ctrl_node *curr_cmd;
+ /* spin lock for command */
+ spinlock_t nxpwifi_cmd_lock;
+ u16 last_init_cmd;
+ struct timer_list cmd_timer;
+ struct list_head cmd_free_q;
+ /* spin lock for cmd_free_q */
+ spinlock_t cmd_free_q_lock;
+ struct list_head cmd_pending_q;
+ /* spin lock for cmd_pending_q */
+ spinlock_t cmd_pending_q_lock;
+ struct list_head scan_pending_q;
+ /* spin lock for scan_pending_q */
+ spinlock_t scan_pending_q_lock;
+ /* spin lock for RX processing routine */
+ spinlock_t rx_proc_lock;
+ struct sk_buff_head tx_data_q;
+ atomic_t tx_queued;
+ u32 scan_processing;
+ u16 region_code;
+ struct nxpwifi_802_11d_domain_reg domain_reg;
+ u16 scan_probes;
+ u32 scan_mode;
+ u16 specific_scan_time;
+ u16 active_scan_time;
+ u16 passive_scan_time;
+ u16 scan_chan_gap_time;
+ u8 fw_bands;
+ u8 config_bands;
+ u8 tx_lock_flag;
+ struct nxpwifi_sleep_period sleep_period;
+ u16 ps_mode;
+ u32 ps_state;
+ u8 need_to_wakeup;
+ u16 multiple_dtim;
+ u16 local_listen_interval;
+ u16 null_pkt_interval;
+ struct sk_buff *sleep_cfm;
+ u16 bcn_miss_time_out;
+ u8 is_deep_sleep;
+ u8 delay_null_pkt;
+ u16 delay_to_ps;
+ u16 enhanced_ps_mode;
+ u8 pm_wakeup_card_req;
+ u16 gen_null_pkt;
+ u16 pps_uapsd_mode;
+ u32 pm_wakeup_fw_try;
+ struct timer_list wakeup_timer;
+ struct nxpwifi_hs_config_param hs_cfg;
+ u8 hs_activated;
+ u8 hs_activated_manually;
+ u16 hs_activate_wait_q_woken;
+ wait_queue_head_t hs_activate_wait_q;
+ u8 event_body[MAX_EVENT_SIZE];
+ u32 hw_dot_11n_dev_cap;
+ u8 hw_dev_mcs_support;
+ u8 user_dev_mcs_support;
+ u8 sec_chan_offset;
+ struct nxpwifi_dbg dbg;
+ u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE];
+ u32 arp_filter_size;
+ struct nxpwifi_wait_queue cmd_wait_q;
+ u8 scan_wait_q_woken;
+ spinlock_t queue_lock; /* lock for tx queues */
+ u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
+ u16 max_mgmt_ie_index;
+ const struct firmware *cal_data;
+ struct device_node *dt_node;
+
+ /* 11AC */
+ u32 is_hw_11ac_capable;
+ u32 hw_dot_11ac_dev_cap;
+ u32 hw_dot_11ac_mcs_support;
+ u32 usr_dot_11ac_dev_cap_bg;
+ u32 usr_dot_11ac_dev_cap_a;
+ u32 usr_dot_11ac_mcs_support;
+
+ atomic_t pending_bridged_pkts;
+
+ /* For synchronizing FW initialization with device lifecycle. */
+ struct completion *fw_done;
+ bool is_up;
+
+ bool ext_scan;
+ u8 fw_api_ver;
+ u8 key_api_major_ver, key_api_minor_ver;
+ u8 max_sta_conn;
+ struct memory_type_mapping *mem_type_mapping_tbl;
+ u8 num_mem_types;
+ bool scan_chan_gap_enabled;
+ struct sk_buff_head rx_data_q;
+ struct nxpwifi_chan_stats *chan_stats;
+ u32 num_in_chan_stats;
+ int survey_idx;
+ u8 coex_scan;
+ u8 coex_min_scan_time;
+ u8 coex_max_scan_time;
+ u8 coex_win_size;
+ u8 coex_tx_win_size;
+ u8 coex_rx_win_size;
+ u8 active_scan_triggered;
+ bool usb_mc_status;
+ bool usb_mc_setup;
+ struct cfg80211_wowlan_nd_info *nd_info;
+ struct ieee80211_regdomain *regd;
+
+ /* Wake-on-WLAN (WoWLAN) */
+ int irq_wakeup;
+ bool wake_by_wifi;
+ /* Aggregation parameters*/
+ struct bus_aggr_params bus_aggr;
+ /* Device dump data/length */
+ void *devdump_data;
+ int devdump_len;
+ struct delayed_work devdump_work;
+
+ bool ignore_btcoex_events;
+};
+
+void nxpwifi_process_tx_queue(struct nxpwifi_adapter *adapter);
+
+int nxpwifi_init_lock_list(struct nxpwifi_adapter *adapter);
+
+void nxpwifi_set_trans_start(struct net_device *dev);
+
+void nxpwifi_stop_net_dev_queue(struct net_device *netdev,
+ struct nxpwifi_adapter *adapter);
+
+void nxpwifi_wake_up_net_dev_queue(struct net_device *netdev,
+ struct nxpwifi_adapter *adapter);
+
+int nxpwifi_init_priv(struct nxpwifi_private *priv);
+void nxpwifi_free_priv(struct nxpwifi_private *priv);
+
+int nxpwifi_init_fw(struct nxpwifi_adapter *adapter);
+
+int nxpwifi_init_fw_complete(struct nxpwifi_adapter *adapter);
+
+void nxpwifi_shutdown_drv(struct nxpwifi_adapter *adapter);
+
+int nxpwifi_dnld_fw(struct nxpwifi_adapter *adapter,
+ struct nxpwifi_fw_image *fw);
+
+int nxpwifi_recv_packet(struct nxpwifi_private *priv, struct sk_buff *skb);
+int nxpwifi_uap_recv_packet(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+
+void nxpwifi_host_mlme_disconnect(struct nxpwifi_private *priv,
+ u16 reason_code, u8 *sa);
+
+int nxpwifi_process_mgmt_packet(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+
+int nxpwifi_complete_cmd(struct nxpwifi_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node);
+
+void nxpwifi_cmd_timeout_func(struct timer_list *t);
+
+int nxpwifi_get_debug_info(struct nxpwifi_private *priv,
+ struct nxpwifi_debug_info *info);
+
+int nxpwifi_alloc_cmd_buffer(struct nxpwifi_adapter *adapter);
+void nxpwifi_free_cmd_buffer(struct nxpwifi_adapter *adapter);
+void nxpwifi_free_cmd_buffers(struct nxpwifi_adapter *adapter);
+void nxpwifi_cancel_all_pending_cmd(struct nxpwifi_adapter *adapter);
+void nxpwifi_cancel_pending_scan_cmd(struct nxpwifi_adapter *adapter);
+void nxpwifi_cancel_scan(struct nxpwifi_adapter *adapter);
+
+void nxpwifi_recycle_cmd_node(struct nxpwifi_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node);
+
+void nxpwifi_insert_cmd_to_pending_q(struct nxpwifi_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node);
+
+int nxpwifi_exec_next_cmd(struct nxpwifi_adapter *adapter);
+int nxpwifi_process_cmdresp(struct nxpwifi_adapter *adapter);
+void nxpwifi_process_assoc_resp(struct nxpwifi_adapter *adapter);
+int nxpwifi_handle_rx_packet(struct nxpwifi_adapter *adapter,
+ struct sk_buff *skb);
+int nxpwifi_process_tx(struct nxpwifi_private *priv, struct sk_buff *skb,
+ struct nxpwifi_tx_param *tx_param);
+int nxpwifi_send_null_packet(struct nxpwifi_private *priv, u8 flags);
+int nxpwifi_write_data_complete(struct nxpwifi_adapter *adapter,
+ struct sk_buff *skb, int aggr, int status);
+void nxpwifi_clean_txrx(struct nxpwifi_private *priv);
+u8 nxpwifi_check_last_packet_indication(struct nxpwifi_private *priv);
+void nxpwifi_check_ps_cond(struct nxpwifi_adapter *adapter);
+void nxpwifi_process_sleep_confirm_resp(struct nxpwifi_adapter *adapter,
+ u8 *pbuf, u32 upld_len);
+void nxpwifi_process_hs_config(struct nxpwifi_adapter *adapter);
+void nxpwifi_hs_activated_event(struct nxpwifi_private *priv,
+ u8 activated);
+int nxpwifi_set_hs_params(struct nxpwifi_private *priv, u16 action,
+ int cmd_type, struct nxpwifi_ds_hs_cfg *hs_cfg);
+int nxpwifi_ret_802_11_hs_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp);
+int nxpwifi_process_rx_packet(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+int nxpwifi_process_sta_rx_packet(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+int nxpwifi_process_uap_rx_packet(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+int nxpwifi_handle_uap_rx_forward(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+void nxpwifi_delete_all_station_list(struct nxpwifi_private *priv);
+void nxpwifi_wmm_del_peer_ra_list(struct nxpwifi_private *priv,
+ const u8 *ra_addr);
+void nxpwifi_process_sta_txpd(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+void nxpwifi_process_uap_txpd(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+int nxpwifi_cmd_802_11_scan(struct host_cmd_ds_command *cmd,
+ struct nxpwifi_scan_cmd_config *scan_cfg);
+void nxpwifi_queue_scan_cmd(struct nxpwifi_private *priv,
+ struct cmd_ctrl_node *cmd_node);
+int nxpwifi_ret_802_11_scan(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp);
+int nxpwifi_associate(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc);
+int nxpwifi_cmd_802_11_associate(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct nxpwifi_bssdescriptor *bss_desc);
+int nxpwifi_ret_802_11_associate(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp);
+u8 nxpwifi_band_to_radio_type(u8 band);
+int nxpwifi_deauthenticate(struct nxpwifi_private *priv, u8 *mac);
+void nxpwifi_deauthenticate_all(struct nxpwifi_adapter *adapter);
+int nxpwifi_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd);
+struct nxpwifi_chan_freq_power *nxpwifi_get_cfp(struct nxpwifi_private *priv,
+ u8 band, u16 channel, u32 freq);
+u32 nxpwifi_index_to_data_rate(struct nxpwifi_private *priv,
+ u8 index, u8 ht_info);
+u32 nxpwifi_index_to_acs_data_rate(struct nxpwifi_private *priv,
+ u8 index, u8 ht_info);
+int nxpwifi_cmd_append_vsie_tlv(struct nxpwifi_private *priv, u16 vsie_mask,
+ u8 **buffer);
+u32 nxpwifi_get_active_data_rates(struct nxpwifi_private *priv,
+ u8 *rates);
+u32 nxpwifi_get_supported_rates(struct nxpwifi_private *priv, u8 *rates);
+u32 nxpwifi_get_rates_from_cfg80211(struct nxpwifi_private *priv,
+ u8 *rates, u8 radio_type);
+u8 nxpwifi_is_rate_auto(struct nxpwifi_private *priv);
+extern u16 region_code_index[NXPWIFI_MAX_REGION_CODE];
+void nxpwifi_save_curr_bcn(struct nxpwifi_private *priv);
+void nxpwifi_free_curr_bcn(struct nxpwifi_private *priv);
+int is_command_pending(struct nxpwifi_adapter *adapter);
+void nxpwifi_init_priv_params(struct nxpwifi_private *priv,
+ struct net_device *dev);
+void nxpwifi_set_ba_params(struct nxpwifi_private *priv);
+void nxpwifi_update_ampdu_txwinsize(struct nxpwifi_adapter *pmadapter);
+void nxpwifi_set_11ac_ba_params(struct nxpwifi_private *priv);
+int nxpwifi_cmd_802_11_scan_ext(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ void *data_buf);
+int nxpwifi_ret_802_11_scan_ext(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp);
+int nxpwifi_handle_event_ext_scan_report(struct nxpwifi_private *priv,
+ void *buf);
+int nxpwifi_cmd_802_11_bg_scan_config(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ void *data_buf);
+int nxpwifi_stop_bg_scan(struct nxpwifi_private *priv);
+
+/* This function checks if the queuing is RA based or not.
+ */
+static inline u8
+nxpwifi_queuing_ra_based(struct nxpwifi_private *priv)
+{
+ /* Currently we assume if we are in Infra, then DA=RA. This might not be
+ * true in the future
+ */
+ if (priv->bss_mode == NL80211_IFTYPE_STATION &&
+ (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA))
+ return false;
+
+ return true;
+}
+
+/* This function copies rates.
+ */
+static inline u32
+nxpwifi_copy_rates(u8 *dest, u32 pos, u8 *src, int len)
+{
+ int i;
+
+ for (i = 0; i < len && src[i]; i++, pos++) {
+ if (pos >= NXPWIFI_SUPPORTED_RATES)
+ break;
+ dest[pos] = src[i];
+ }
+
+ return pos;
+}
+
+/* This function returns the correct private structure pointer based
+ * upon the BSS type and BSS number.
+ */
+static inline struct nxpwifi_private *
+nxpwifi_get_priv_by_id(struct nxpwifi_adapter *adapter,
+ u8 bss_num, u8 bss_type)
+{
+ int i;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ if (adapter->priv[i]->bss_num == bss_num &&
+ adapter->priv[i]->bss_type == bss_type)
+ break;
+ }
+ }
+ return ((i < adapter->priv_num) ? adapter->priv[i] : NULL);
+}
+
+/* This function returns the first available private structure pointer
+ * based upon the BSS role.
+ */
+static inline struct nxpwifi_private *
+nxpwifi_get_priv(struct nxpwifi_adapter *adapter,
+ enum nxpwifi_bss_role bss_role)
+{
+ int i;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ if (adapter->priv[i]) {
+ if (bss_role == NXPWIFI_BSS_ROLE_ANY ||
+ GET_BSS_ROLE(adapter->priv[i]) == bss_role)
+ break;
+ }
+ }
+
+ return ((i < adapter->priv_num) ? adapter->priv[i] : NULL);
+}
+
+/* This function checks available bss_num when adding new interface or
+ * changing interface type.
+ */
+static inline u8
+nxpwifi_get_unused_bss_num(struct nxpwifi_adapter *adapter, u8 bss_type)
+{
+ u8 i, j;
+ int index[NXPWIFI_MAX_BSS_NUM];
+
+ memset(index, 0, sizeof(index));
+ for (i = 0; i < adapter->priv_num; i++)
+ if (adapter->priv[i]) {
+ if (adapter->priv[i]->bss_type == bss_type &&
+ !(adapter->priv[i]->bss_mode ==
+ NL80211_IFTYPE_UNSPECIFIED)) {
+ index[adapter->priv[i]->bss_num] = 1;
+ }
+ }
+ for (j = 0; j < NXPWIFI_MAX_BSS_NUM; j++)
+ if (!index[j])
+ return j;
+ return -1;
+}
+
+/* This function returns the first available unused private structure pointer.
+ */
+static inline struct nxpwifi_private *
+nxpwifi_get_unused_priv_by_bss_type(struct nxpwifi_adapter *adapter,
+ u8 bss_type)
+{
+ u8 i;
+
+ for (i = 0; i < adapter->priv_num; i++)
+ if (adapter->priv[i]->bss_mode ==
+ NL80211_IFTYPE_UNSPECIFIED) {
+ adapter->priv[i]->bss_num =
+ nxpwifi_get_unused_bss_num(adapter, bss_type);
+ break;
+ }
+
+ return ((i < adapter->priv_num) ? adapter->priv[i] : NULL);
+}
+
+/* This function returns the driver private structure of a network device.
+ */
+static inline struct nxpwifi_private *
+nxpwifi_netdev_get_priv(struct net_device *dev)
+{
+ return (struct nxpwifi_private *)(*(unsigned long *)netdev_priv(dev));
+}
+
+/* This function checks if a skb holds a management frame.
+ */
+static inline bool nxpwifi_is_skb_mgmt_frame(struct sk_buff *skb)
+{
+ return (get_unaligned_le32(skb->data) == PKT_TYPE_MGMT);
+}
+
+/* This function retrieves channel closed for operation by Channel
+ * Switch Announcement.
+ */
+static inline u8
+nxpwifi_11h_get_csa_closed_channel(struct nxpwifi_private *priv)
+{
+ if (!priv->csa_chan)
+ return 0;
+
+ /* Clear csa channel, if DFS channel move time has passed */
+ if (time_after(jiffies, priv->csa_expire_time)) {
+ priv->csa_chan = 0;
+ priv->csa_expire_time = 0;
+ }
+
+ return priv->csa_chan;
+}
+
+static inline u8 nxpwifi_is_any_intf_active(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_private *priv_num;
+ int i;
+
+ for (i = 0; i < priv->adapter->priv_num; i++) {
+ priv_num = priv->adapter->priv[i];
+ if (priv_num) {
+ if ((GET_BSS_ROLE(priv_num) == NXPWIFI_BSS_ROLE_UAP &&
+ priv_num->bss_started) ||
+ (GET_BSS_ROLE(priv_num) == NXPWIFI_BSS_ROLE_STA &&
+ priv_num->media_connected))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Disable platform specific wakeup interrupt */
+static inline void nxpwifi_disable_wake(struct nxpwifi_adapter *adapter)
+{
+ if (adapter->irq_wakeup >= 0) {
+ disable_irq_wake(adapter->irq_wakeup);
+ disable_irq(adapter->irq_wakeup);
+ if (adapter->wake_by_wifi)
+ /* Undo our disable, since interrupt handler already
+ * did this.
+ */
+ enable_irq(adapter->irq_wakeup);
+ }
+}
+
+/* Enable platform specific wakeup interrupt */
+static inline void nxpwifi_enable_wake(struct nxpwifi_adapter *adapter)
+{
+ /* Enable platform specific wakeup interrupt */
+ if (adapter->irq_wakeup >= 0) {
+ adapter->wake_by_wifi = false;
+ enable_irq(adapter->irq_wakeup);
+ enable_irq_wake(adapter->irq_wakeup);
+ }
+}
+
+int nxpwifi_init_shutdown_fw(struct nxpwifi_private *priv,
+ u32 func_init_shutdown);
+
+int nxpwifi_add_card(void *card, struct completion *fw_done,
+ struct nxpwifi_if_ops *if_ops, u8 iface_type,
+ struct device *dev);
+int nxpwifi_remove_card(struct nxpwifi_adapter *adapter);
+
+void nxpwifi_get_version(struct nxpwifi_adapter *adapter, char *version,
+ int maxlen);
+int
+nxpwifi_request_set_multicast_list(struct nxpwifi_private *priv,
+ struct nxpwifi_multicast_list *mcast_list);
+int nxpwifi_copy_mcast_addr(struct nxpwifi_multicast_list *mlist,
+ struct net_device *dev);
+int nxpwifi_wait_queue_complete(struct nxpwifi_adapter *adapter,
+ struct cmd_ctrl_node *cmd_queued);
+int nxpwifi_bss_start(struct nxpwifi_private *priv, struct cfg80211_bss *bss,
+ struct cfg80211_ssid *req_ssid);
+int nxpwifi_cancel_hs(struct nxpwifi_private *priv, int cmd_type);
+int nxpwifi_enable_hs(struct nxpwifi_adapter *adapter);
+int nxpwifi_disable_auto_ds(struct nxpwifi_private *priv);
+int nxpwifi_drv_get_data_rate(struct nxpwifi_private *priv, u32 *rate);
+int nxpwifi_request_scan(struct nxpwifi_private *priv,
+ struct cfg80211_ssid *req_ssid);
+int nxpwifi_scan_networks(struct nxpwifi_private *priv,
+ const struct nxpwifi_user_scan_cfg *user_scan_in);
+int nxpwifi_set_radio(struct nxpwifi_private *priv, u8 option);
+
+int nxpwifi_set_encode(struct nxpwifi_private *priv, struct key_params *kp,
+ const u8 *key, int key_len, u8 key_index,
+ const u8 *mac_addr, int disable);
+
+int nxpwifi_set_gen_ie(struct nxpwifi_private *priv, const u8 *ie, int ie_len);
+
+int nxpwifi_get_ver_ext(struct nxpwifi_private *priv, u32 version_str_sel);
+
+int nxpwifi_remain_on_chan_cfg(struct nxpwifi_private *priv, u16 action,
+ struct ieee80211_channel *chan,
+ unsigned int duration);
+
+int nxpwifi_get_stats_info(struct nxpwifi_private *priv,
+ struct nxpwifi_ds_get_stats *log);
+
+int nxpwifi_reg_write(struct nxpwifi_private *priv, u32 reg_type,
+ u32 reg_offset, u32 reg_value);
+
+int nxpwifi_reg_read(struct nxpwifi_private *priv, u32 reg_type,
+ u32 reg_offset, u32 *value);
+
+int nxpwifi_eeprom_read(struct nxpwifi_private *priv, u16 offset, u16 bytes,
+ u8 *value);
+
+int nxpwifi_set_11n_httx_cfg(struct nxpwifi_private *priv, int data);
+
+int nxpwifi_get_11n_httx_cfg(struct nxpwifi_private *priv, int *data);
+
+int nxpwifi_set_tx_rate_cfg(struct nxpwifi_private *priv, int tx_rate_index);
+
+int nxpwifi_get_tx_rate_cfg(struct nxpwifi_private *priv, int *tx_rate_index);
+
+int nxpwifi_drv_set_power(struct nxpwifi_private *priv, u32 *ps_mode);
+
+int nxpwifi_drv_get_driver_version(struct nxpwifi_adapter *adapter,
+ char *version, int max_len);
+
+int nxpwifi_set_tx_power(struct nxpwifi_private *priv,
+ struct nxpwifi_power_cfg *power_cfg);
+
+int nxpwifi_main_process(struct nxpwifi_adapter *adapter);
+
+int nxpwifi_queue_tx_pkt(struct nxpwifi_private *priv, struct sk_buff *skb);
+
+int nxpwifi_get_bss_info(struct nxpwifi_private *priv,
+ struct nxpwifi_bss_info *info);
+int nxpwifi_fill_new_bss_desc(struct nxpwifi_private *priv,
+ struct cfg80211_bss *bss,
+ struct nxpwifi_bssdescriptor *bss_desc);
+int nxpwifi_update_bss_desc_with_ie(struct nxpwifi_adapter *adapter,
+ struct nxpwifi_bssdescriptor *bss_entry);
+int nxpwifi_check_network_compatibility(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc);
+
+u8 nxpwifi_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type);
+u8 nxpwifi_get_chan_type(struct nxpwifi_private *priv);
+
+struct wireless_dev *nxpwifi_add_virtual_intf(struct wiphy *wiphy,
+ const char *name,
+ unsigned char name_assign_type,
+ enum nl80211_iftype type,
+ struct vif_params *params);
+int nxpwifi_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev);
+
+int nxpwifi_add_wowlan_magic_pkt_filter(struct nxpwifi_adapter *adapter);
+
+int nxpwifi_set_mgmt_ies(struct nxpwifi_private *priv,
+ struct cfg80211_beacon_data *data);
+int nxpwifi_del_mgmt_ies(struct nxpwifi_private *priv);
+u8 *nxpwifi_11d_code_2_region(u8 code);
+void nxpwifi_init_11h_params(struct nxpwifi_private *priv);
+int nxpwifi_is_11h_active(struct nxpwifi_private *priv);
+int nxpwifi_11h_activate(struct nxpwifi_private *priv, bool flag);
+void nxpwifi_11h_process_join(struct nxpwifi_private *priv, u8 **buffer,
+ struct nxpwifi_bssdescriptor *bss_desc);
+int nxpwifi_11h_handle_event_chanswann(struct nxpwifi_private *priv);
+void nxpwifi_dnld_txpwr_table(struct nxpwifi_private *priv);
+
+extern const struct ethtool_ops nxpwifi_ethtool_ops;
+
+void nxpwifi_del_all_sta_list(struct nxpwifi_private *priv);
+void nxpwifi_del_sta_entry(struct nxpwifi_private *priv, const u8 *mac);
+void
+nxpwifi_set_sta_ht_cap(struct nxpwifi_private *priv, const u8 *ies,
+ int ies_len, struct nxpwifi_sta_node *node);
+struct nxpwifi_sta_node *
+nxpwifi_add_sta_entry(struct nxpwifi_private *priv, const u8 *mac);
+struct nxpwifi_sta_node *
+nxpwifi_get_sta_entry(struct nxpwifi_private *priv, const u8 *mac);
+bool nxpwifi_is_bss_in_11ac_mode(struct nxpwifi_private *priv);
+u8 nxpwifi_get_center_freq_index(struct nxpwifi_private *priv, u8 band,
+ u32 pri_chan, u8 chan_bw);
+int nxpwifi_init_channel_scan_gap(struct nxpwifi_adapter *adapter);
+
+int nxpwifi_cmd_issue_chan_report_request(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ void *data_buf);
+int nxpwifi_11h_handle_chanrpt_ready(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+
+void nxpwifi_parse_tx_status_event(struct nxpwifi_private *priv,
+ void *event_body);
+
+struct sk_buff *
+nxpwifi_clone_skb_for_tx_status(struct nxpwifi_private *priv,
+ struct sk_buff *skb, u8 flag, u64 *cookie);
+void nxpwifi_dfs_cac_work_queue(struct work_struct *work);
+void nxpwifi_dfs_chan_sw_work_queue(struct work_struct *work);
+void nxpwifi_abort_cac(struct nxpwifi_private *priv);
+int nxpwifi_stop_radar_detection(struct nxpwifi_private *priv,
+ struct cfg80211_chan_def *chandef);
+int nxpwifi_11h_handle_radar_detected(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+
+void nxpwifi_hist_data_set(struct nxpwifi_private *priv, u8 rx_rate, s8 snr,
+ s8 nflr);
+void nxpwifi_hist_data_reset(struct nxpwifi_private *priv);
+void nxpwifi_hist_data_add(struct nxpwifi_private *priv,
+ u8 rx_rate, s8 snr, s8 nflr);
+u8 nxpwifi_adjust_data_rate(struct nxpwifi_private *priv,
+ u8 rx_rate, u8 ht_info);
+
+void nxpwifi_drv_info_dump(struct nxpwifi_adapter *adapter);
+void nxpwifi_prepare_fw_dump_info(struct nxpwifi_adapter *adapter);
+void nxpwifi_upload_device_dump(struct nxpwifi_adapter *adapter);
+void *nxpwifi_alloc_dma_align_buf(int rx_len, gfp_t flags);
+void nxpwifi_fw_dump_event(struct nxpwifi_private *priv);
+void nxpwifi_queue_main_work(struct nxpwifi_adapter *adapter);
+int nxpwifi_get_wakeup_reason(struct nxpwifi_private *priv, u16 action,
+ int cmd_type,
+ struct nxpwifi_ds_wakeup_reason *wakeup_reason);
+int nxpwifi_get_chan_info(struct nxpwifi_private *priv,
+ struct nxpwifi_channel_band *channel_band);
+void nxpwifi_coex_ampdu_rxwinsize(struct nxpwifi_adapter *adapter);
+void nxpwifi_11n_delba(struct nxpwifi_private *priv, int tid);
+int nxpwifi_send_domain_info_cmd_fw(struct wiphy *wiphy);
+int nxpwifi_set_mac_address(struct nxpwifi_private *priv,
+ struct net_device *dev,
+ bool external, u8 *new_mac);
+void nxpwifi_devdump_tmo_func(unsigned long function_context);
+
+#ifdef CONFIG_DEBUG_FS
+void nxpwifi_debugfs_init(void);
+void nxpwifi_debugfs_remove(void);
+
+void nxpwifi_dev_debugfs_init(struct nxpwifi_private *priv);
+void nxpwifi_dev_debugfs_remove(struct nxpwifi_private *priv);
+#endif
+int nxpwifi_reinit_sw(struct nxpwifi_adapter *adapter);
+int nxpwifi_shutdown_sw(struct nxpwifi_adapter *adapter);
+#endif /* !_NXPWIFI_MAIN_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 25/43] wifi: nxpwifi: add scan.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (23 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 24/43] wifi: nxpwifi: add main.h David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 26/43] wifi: nxpwifi: add sdio.c David Lin
` (19 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/scan.c | 2894 +++++++++++++++++++++++
1 file changed, 2894 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/scan.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/scan.c b/drivers/net/wireless/nxp/nxpwifi/scan.c
new file mode 100644
index 000000000000..a41d561cfe57
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/scan.c
@@ -0,0 +1,2894 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: scan ioctl and command handling
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cmdevt.h"
+#include "11n.h"
+#include "cfg80211.h"
+
+/* The maximum number of channels the firmware can scan per command */
+#define NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN 14
+
+#define NXPWIFI_DEF_CHANNELS_PER_SCAN_CMD 4
+
+/* Memory needed to store a max sized Channel List TLV for a firmware scan */
+#define CHAN_TLV_MAX_SIZE (sizeof(struct nxpwifi_ie_types_header) \
+ + (NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN \
+ * sizeof(struct nxpwifi_chan_scan_param_set)))
+
+/* Memory needed to store supported rate */
+#define RATE_TLV_MAX_SIZE (sizeof(struct nxpwifi_ie_types_rates_param_set) \
+ + HOSTCMD_SUPPORTED_RATES)
+
+/* Memory needed to store a max number/size WildCard SSID TLV for a firmware
+ * scan
+ */
+#define WILDCARD_SSID_TLV_MAX_SIZE \
+ (NXPWIFI_MAX_SSID_LIST_LENGTH * \
+ (sizeof(struct nxpwifi_ie_types_wildcard_ssid_params) \
+ + IEEE80211_MAX_SSID_LEN))
+
+/* Maximum memory needed for a nxpwifi_scan_cmd_config with all TLVs at max */
+#define MAX_SCAN_CFG_ALLOC (sizeof(struct nxpwifi_scan_cmd_config) \
+ + sizeof(struct nxpwifi_ie_types_num_probes) \
+ + sizeof(struct nxpwifi_ie_types_htcap) \
+ + CHAN_TLV_MAX_SIZE \
+ + RATE_TLV_MAX_SIZE \
+ + WILDCARD_SSID_TLV_MAX_SIZE)
+
+union nxpwifi_scan_cmd_config_tlv {
+ /* Scan configuration (variable length) */
+ struct nxpwifi_scan_cmd_config config;
+ /* Max allocated block */
+ u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC];
+};
+
+enum cipher_suite {
+ CIPHER_SUITE_TKIP,
+ CIPHER_SUITE_CCMP,
+ CIPHER_SUITE_MAX
+};
+
+static u8 nxpwifi_wpa_oui[CIPHER_SUITE_MAX][4] = {
+ { 0x00, 0x50, 0xf2, 0x02 }, /* TKIP */
+ { 0x00, 0x50, 0xf2, 0x04 }, /* AES */
+};
+
+static u8 nxpwifi_rsn_oui[CIPHER_SUITE_MAX][4] = {
+ { 0x00, 0x0f, 0xac, 0x02 }, /* TKIP */
+ { 0x00, 0x0f, 0xac, 0x04 }, /* AES */
+};
+
+static void
+_dbg_security_flags(int log_level, const char *func, const char *desc,
+ struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc)
+{
+ _nxpwifi_dbg(priv->adapter, log_level,
+ "info: %s: %s:\twpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\tEncMode=%#x privacy=%#x\n",
+ func, desc,
+ bss_desc->bcn_wpa_ie ?
+ bss_desc->bcn_wpa_ie->vend_hdr.element_id : 0,
+ bss_desc->bcn_rsn_ie ?
+ bss_desc->bcn_rsn_ie->ieee_hdr.element_id : 0,
+ priv->sec_info.wep_enabled ? "e" : "d",
+ priv->sec_info.wpa_enabled ? "e" : "d",
+ priv->sec_info.wpa2_enabled ? "e" : "d",
+ priv->sec_info.encryption_mode,
+ bss_desc->privacy);
+}
+
+#define dbg_security_flags(mask, desc, priv, bss_desc) \
+ _dbg_security_flags(NXPWIFI_DBG_##mask, desc, __func__, priv, bss_desc)
+
+static bool
+has_ieee_hdr(struct ieee_types_generic *ie, u8 key)
+{
+ return (ie && ie->ieee_hdr.element_id == key);
+}
+
+static bool
+has_vendor_hdr(struct ieee_types_vendor_specific *ie, u8 key)
+{
+ return (ie && ie->vend_hdr.element_id == key);
+}
+
+/* This function parses a given IE for a given OUI.
+ *
+ * This is used to parse a WPA/RSN IE to find if it has
+ * a given oui in PTK.
+ */
+static u8
+nxpwifi_search_oui_in_ie(struct ie_body *iebody, u8 *oui)
+{
+ u8 count;
+
+ count = iebody->ptk_cnt[0];
+
+ /* There could be multiple OUIs for PTK hence
+ * 1) Take the length.
+ * 2) Check all the OUIs for AES.
+ * 3) If one of them is AES then pass success.
+ */
+ while (count) {
+ if (!memcmp(iebody->ptk_body, oui, sizeof(iebody->ptk_body)))
+ return NXPWIFI_OUI_PRESENT;
+
+ --count;
+ if (count)
+ iebody = (struct ie_body *)((u8 *)iebody +
+ sizeof(iebody->ptk_body));
+ }
+
+ pr_debug("info: %s: OUI is not found in PTK\n", __func__);
+ return NXPWIFI_OUI_NOT_PRESENT;
+}
+
+/* This function checks if a given OUI is present in a RSN IE.
+ *
+ * The function first checks if a RSN IE is present or not in the
+ * BSS descriptor. It tries to locate the OUI only if such an IE is
+ * present.
+ */
+static u8
+nxpwifi_is_rsn_oui_present(struct nxpwifi_bssdescriptor *bss_desc, u32 cipher)
+{
+ u8 *oui;
+ struct ie_body *iebody;
+ u8 ret = NXPWIFI_OUI_NOT_PRESENT;
+
+ if (has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN)) {
+ iebody = (struct ie_body *)
+ (((u8 *)bss_desc->bcn_rsn_ie->data) +
+ RSN_GTK_OUI_OFFSET);
+ oui = &nxpwifi_rsn_oui[cipher][0];
+ ret = nxpwifi_search_oui_in_ie(iebody, oui);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+/* This function checks if a given OUI is present in a WPA IE.
+ *
+ * The function first checks if a WPA IE is present or not in the
+ * BSS descriptor. It tries to locate the OUI only if such an IE is
+ * present.
+ */
+static u8
+nxpwifi_is_wpa_oui_present(struct nxpwifi_bssdescriptor *bss_desc, u32 cipher)
+{
+ u8 *oui;
+ struct ie_body *iebody;
+ u8 ret = NXPWIFI_OUI_NOT_PRESENT;
+
+ if (has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC)) {
+ iebody = (struct ie_body *)((u8 *)bss_desc->bcn_wpa_ie->data +
+ WPA_GTK_OUI_OFFSET);
+ oui = &nxpwifi_wpa_oui[cipher][0];
+ ret = nxpwifi_search_oui_in_ie(iebody, oui);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+/* This function checks if driver is configured with no security mode and
+ * scanned network is compatible with it.
+ */
+static bool
+nxpwifi_is_bss_no_sec(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc)
+{
+ if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
+ !priv->sec_info.wpa2_enabled &&
+ !has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) &&
+ !has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN) &&
+ !priv->sec_info.encryption_mode && !bss_desc->privacy) {
+ return true;
+ }
+ return false;
+}
+
+/* This function checks if static WEP is enabled in driver and scanned network
+ * is compatible with it.
+ */
+static bool
+nxpwifi_is_bss_static_wep(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc)
+{
+ if (priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
+ !priv->sec_info.wpa2_enabled && bss_desc->privacy) {
+ return true;
+ }
+ return false;
+}
+
+/* This function checks if wpa is enabled in driver and scanned network is
+ * compatible with it.
+ */
+static bool
+nxpwifi_is_bss_wpa(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc)
+{
+ if (!priv->sec_info.wep_enabled && priv->sec_info.wpa_enabled &&
+ !priv->sec_info.wpa2_enabled &&
+ has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC)
+ /* Privacy bit may NOT be set in some APs like
+ * LinkSys WRT54G && bss_desc->privacy
+ */
+ ) {
+ dbg_security_flags(INFO, "WPA", priv, bss_desc);
+ return true;
+ }
+ return false;
+}
+
+/* This function checks if wpa2 is enabled in driver and scanned network is
+ * compatible with it.
+ */
+static bool
+nxpwifi_is_bss_wpa2(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc)
+{
+ if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
+ priv->sec_info.wpa2_enabled &&
+ has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN)) {
+ /* Privacy bit may NOT be set in some APs like
+ * LinkSys WRT54G && bss_desc->privacy
+ */
+ dbg_security_flags(INFO, "WAP2", priv, bss_desc);
+ return true;
+ }
+ return false;
+}
+
+/* This function checks if dynamic WEP is enabled in driver and scanned network
+ * is compatible with it.
+ */
+static bool
+nxpwifi_is_bss_dynamic_wep(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc)
+{
+ if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled &&
+ !priv->sec_info.wpa2_enabled &&
+ !has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC) &&
+ !has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN) &&
+ priv->sec_info.encryption_mode && bss_desc->privacy) {
+ dbg_security_flags(INFO, "dynamic", priv, bss_desc);
+ return true;
+ }
+ return false;
+}
+
+/* This function checks if a scanned network is compatible with the driver
+ * settings.
+ *
+ * WEP WPA WPA2 encrypt Network
+ * -enabled -enabled -enabled mode Privacy WPA WPA2 Compatible
+ * 0 0 0 NONE 0 0 0 yes No security
+ * 0 1 0 x 1x 1 x yes WPA (disable
+ * HT if no AES)
+ * 0 0 1 x 1x x 1 yes WPA2 (disable
+ * HT if no AES)
+ * 1 0 0 NONE 1 0 0 yes Static WEP
+ * (disable HT)
+ * 0 0 0 !=NONE 1 0 0 yes Dynamic WEP
+ *
+ * Compatibility is not matched while roaming, except for mode.
+ */
+static s32
+nxpwifi_is_network_compatible(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc, u32 mode)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ bss_desc->disable_11n = false;
+
+ /* Don't check for compatibility if roaming */
+ if (priv->media_connected &&
+ priv->bss_mode == NL80211_IFTYPE_STATION &&
+ bss_desc->bss_mode == NL80211_IFTYPE_STATION)
+ return 0;
+
+ if (priv->wps.session_enable) {
+ nxpwifi_dbg(adapter, IOCTL,
+ "info: return success directly in WPS period\n");
+ return 0;
+ }
+
+ if (bss_desc->chan_sw_ie_present) {
+ nxpwifi_dbg(adapter, INFO,
+ "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n");
+ return -1;
+ }
+
+ if (bss_desc->bss_mode == mode) {
+ if (nxpwifi_is_bss_no_sec(priv, bss_desc)) {
+ /* No security */
+ return 0;
+ } else if (nxpwifi_is_bss_static_wep(priv, bss_desc)) {
+ /* Static WEP enabled */
+ nxpwifi_dbg(adapter, INFO,
+ "info: Disable 11n in WEP mode.\n");
+ bss_desc->disable_11n = true;
+ return 0;
+ } else if (nxpwifi_is_bss_wpa(priv, bss_desc)) {
+ /* WPA enabled */
+ if (((priv->adapter->config_bands & BAND_GN ||
+ priv->adapter->config_bands & BAND_AN) &&
+ bss_desc->bcn_ht_cap) &&
+ !nxpwifi_is_wpa_oui_present(bss_desc,
+ CIPHER_SUITE_CCMP)) {
+ if (nxpwifi_is_wpa_oui_present
+ (bss_desc, CIPHER_SUITE_TKIP)) {
+ nxpwifi_dbg(adapter, INFO,
+ "info: Disable 11n if AES\t"
+ "is not supported by AP\n");
+ bss_desc->disable_11n = true;
+ } else {
+ return -1;
+ }
+ }
+ return 0;
+ } else if (nxpwifi_is_bss_wpa2(priv, bss_desc)) {
+ /* WPA2 enabled */
+ if (((priv->adapter->config_bands & BAND_GN ||
+ priv->adapter->config_bands & BAND_AN) &&
+ bss_desc->bcn_ht_cap) &&
+ !nxpwifi_is_rsn_oui_present(bss_desc,
+ CIPHER_SUITE_CCMP)) {
+ if (nxpwifi_is_rsn_oui_present
+ (bss_desc, CIPHER_SUITE_TKIP)) {
+ nxpwifi_dbg(adapter, INFO,
+ "info: Disable 11n if AES\t"
+ "is not supported by AP\n");
+ bss_desc->disable_11n = true;
+ } else {
+ return -1;
+ }
+ }
+ return 0;
+ } else if (nxpwifi_is_bss_dynamic_wep(priv, bss_desc)) {
+ /* Dynamic WEP enabled */
+ return 0;
+ }
+
+ /* Security doesn't match */
+ dbg_security_flags(ERROR, "failed", priv, bss_desc);
+ return -1;
+ }
+
+ /* Mode doesn't match */
+ return -1;
+}
+
+/* This function creates a channel list for the driver to scan, based
+ * on region/band information.
+ *
+ * This routine is used for any scan that is not provided with a
+ * specific channel list to scan.
+ */
+static int
+nxpwifi_scan_create_channel_list(struct nxpwifi_private *priv,
+ const struct nxpwifi_user_scan_cfg
+ *user_scan_in,
+ struct nxpwifi_chan_scan_param_set
+ *scan_chan_list,
+ u8 filtered_scan)
+{
+ enum nl80211_band band;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *ch;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int chan_idx = 0, i;
+ u16 scan_time = 0;
+
+ if (user_scan_in)
+ scan_time = (u16)user_scan_in->chan_list[0].scan_time;
+
+ for (band = 0; (band < NUM_NL80211_BANDS) ; band++) {
+ if (!priv->wdev.wiphy->bands[band])
+ continue;
+
+ sband = priv->wdev.wiphy->bands[band];
+
+ for (i = 0; (i < sband->n_channels) ; i++) {
+ ch = &sband->channels[i];
+ if (ch->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+ scan_chan_list[chan_idx].radio_type = band;
+
+ if (scan_time)
+ scan_chan_list[chan_idx].max_scan_time =
+ cpu_to_le16(scan_time);
+ else if ((ch->flags & IEEE80211_CHAN_NO_IR) ||
+ (ch->flags & IEEE80211_CHAN_RADAR))
+ scan_chan_list[chan_idx].max_scan_time =
+ cpu_to_le16(adapter->passive_scan_time);
+ else
+ scan_chan_list[chan_idx].max_scan_time =
+ cpu_to_le16(adapter->active_scan_time);
+
+ if (ch->flags & IEEE80211_CHAN_NO_IR)
+ scan_chan_list[chan_idx].chan_scan_mode_bmap |=
+ (NXPWIFI_PASSIVE_SCAN | NXPWIFI_HIDDEN_SSID_REPORT);
+ else
+ scan_chan_list[chan_idx].chan_scan_mode_bmap &=
+ ~NXPWIFI_PASSIVE_SCAN;
+
+ scan_chan_list[chan_idx].chan_number = (u32)ch->hw_value;
+ scan_chan_list[chan_idx].chan_scan_mode_bmap |=
+ NXPWIFI_DISABLE_CHAN_FILT;
+
+ if (filtered_scan &&
+ !((ch->flags & IEEE80211_CHAN_NO_IR) ||
+ (ch->flags & IEEE80211_CHAN_RADAR)))
+ scan_chan_list[chan_idx].max_scan_time =
+ cpu_to_le16(adapter->specific_scan_time);
+
+ chan_idx++;
+ }
+ }
+ return chan_idx;
+}
+
+/* This function creates a channel list tlv for bgscan config, based
+ * on region/band information.
+ */
+static int
+nxpwifi_bgscan_create_channel_list(struct nxpwifi_private *priv,
+ const struct nxpwifi_bg_scan_cfg
+ *bgscan_cfg_in,
+ struct nxpwifi_chan_scan_param_set
+ *scan_chan_list)
+{
+ enum nl80211_band band;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *ch;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int chan_idx = 0, i;
+ u16 scan_time = 0, specific_scan_time = adapter->specific_scan_time;
+
+ if (bgscan_cfg_in)
+ scan_time = (u16)bgscan_cfg_in->chan_list[0].scan_time;
+
+ for (band = 0; (band < NUM_NL80211_BANDS); band++) {
+ if (!priv->wdev.wiphy->bands[band])
+ continue;
+
+ sband = priv->wdev.wiphy->bands[band];
+
+ for (i = 0; (i < sband->n_channels) ; i++) {
+ ch = &sband->channels[i];
+ if (ch->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+ scan_chan_list[chan_idx].radio_type = band;
+
+ if (scan_time)
+ scan_chan_list[chan_idx].max_scan_time =
+ cpu_to_le16(scan_time);
+ else if (ch->flags & IEEE80211_CHAN_NO_IR)
+ scan_chan_list[chan_idx].max_scan_time =
+ cpu_to_le16(adapter->passive_scan_time);
+ else
+ scan_chan_list[chan_idx].max_scan_time =
+ cpu_to_le16(specific_scan_time);
+
+ if (ch->flags & IEEE80211_CHAN_NO_IR)
+ scan_chan_list[chan_idx].chan_scan_mode_bmap |=
+ NXPWIFI_PASSIVE_SCAN;
+ else
+ scan_chan_list[chan_idx].chan_scan_mode_bmap &=
+ ~NXPWIFI_PASSIVE_SCAN;
+
+ scan_chan_list[chan_idx].chan_number = (u32)ch->hw_value;
+ chan_idx++;
+ }
+ }
+ return chan_idx;
+}
+
+/* This function appends rate TLV to scan config command. */
+static int
+nxpwifi_append_rate_tlv(struct nxpwifi_private *priv,
+ struct nxpwifi_scan_cmd_config *scan_cfg_out,
+ u8 radio)
+{
+ struct nxpwifi_ie_types_rates_param_set *rates_tlv;
+ u8 rates[NXPWIFI_SUPPORTED_RATES], *tlv_pos;
+ u32 rates_size;
+
+ memset(rates, 0, sizeof(rates));
+
+ tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len;
+
+ if (priv->scan_request)
+ rates_size = nxpwifi_get_rates_from_cfg80211(priv, rates,
+ radio);
+ else
+ rates_size = nxpwifi_get_supported_rates(priv, rates);
+
+ nxpwifi_dbg(priv->adapter, CMD,
+ "info: SCAN_CMD: Rates size = %d\n",
+ rates_size);
+ rates_tlv = (struct nxpwifi_ie_types_rates_param_set *)tlv_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);
+ scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size;
+
+ return rates_size;
+}
+
+/* This function constructs and sends multiple scan config commands to
+ * the firmware.
+ *
+ * Previous routines in the code flow have created a scan command configuration
+ * with any requested TLVs. This function splits the channel TLV into maximum
+ * channels supported per scan lists and sends the portion of the channel TLV,
+ * along with the other TLVs, to the firmware.
+ */
+static int
+nxpwifi_scan_channel_list(struct nxpwifi_private *priv,
+ u32 max_chan_per_scan, u8 filtered_scan,
+ struct nxpwifi_scan_cmd_config *scan_cfg_out,
+ struct nxpwifi_ie_types_chan_list_param_set *tlv_o,
+ struct nxpwifi_chan_scan_param_set *scan_chan_list)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int ret = 0;
+ struct nxpwifi_chan_scan_param_set *tmp_chan_list;
+ u32 tlv_idx, rates_size, cmd_no;
+ u32 total_scan_time;
+ u32 done_early;
+ u8 radio_type;
+
+ if (!scan_cfg_out || !tlv_o || !scan_chan_list) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "info: Scan: Null detect: %p, %p, %p\n",
+ scan_cfg_out, tlv_o, scan_chan_list);
+ return -1;
+ }
+
+ /* Check csa channel expiry before preparing scan list */
+ nxpwifi_11h_get_csa_closed_channel(priv);
+
+ tlv_o->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
+
+ /* Set the temp channel struct pointer to the start of the desired
+ * list
+ */
+ tmp_chan_list = scan_chan_list;
+
+ /* Loop through the desired channel list, sending a new firmware scan
+ * commands for each max_chan_per_scan channels (or for 1,6,11
+ * individually if configured accordingly)
+ */
+ while (tmp_chan_list->chan_number) {
+ tlv_idx = 0;
+ total_scan_time = 0;
+ radio_type = 0;
+ tlv_o->header.len = 0;
+ done_early = false;
+
+ /* Construct the Channel TLV for the scan command. Continue to
+ * insert channel TLVs until:
+ * - the tlv_idx hits the maximum configured per scan command
+ * - the next channel to insert is 0 (end of desired channel
+ * list)
+ * - done_early is set (controlling individual scanning of
+ * 1,6,11)
+ */
+ while (tlv_idx < max_chan_per_scan &&
+ tmp_chan_list->chan_number && !done_early) {
+ if (tmp_chan_list->chan_number == priv->csa_chan) {
+ tmp_chan_list++;
+ continue;
+ }
+
+ radio_type = tmp_chan_list->radio_type;
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: Scan: Chan(%3d), Radio(%d),\t"
+ "Mode(%d, %d), Dur(%d)\n",
+ tmp_chan_list->chan_number,
+ tmp_chan_list->radio_type,
+ tmp_chan_list->chan_scan_mode_bmap
+ & NXPWIFI_PASSIVE_SCAN,
+ (tmp_chan_list->chan_scan_mode_bmap
+ & NXPWIFI_DISABLE_CHAN_FILT) >> 1,
+ le16_to_cpu(tmp_chan_list->max_scan_time));
+
+ /* Copy the current channel TLV to the command being
+ * prepared
+ */
+ memcpy(&tlv_o->chan_scan_param[tlv_idx],
+ tmp_chan_list,
+ sizeof(*tlv_o->chan_scan_param));
+
+ /* Increment the TLV header length by the size
+ * appended
+ */
+ le16_unaligned_add_cpu(&tlv_o->header.len,
+ sizeof(*tlv_o->chan_scan_param));
+
+ /* The tlv buffer length is set to the number of bytes
+ * of the between the channel tlv pointer and the start
+ * of the tlv buffer. This compensates for any TLVs
+ * that were appended before the channel list.
+ */
+ scan_cfg_out->tlv_buf_len = (u32)((u8 *)tlv_o -
+ scan_cfg_out->tlv_buf);
+
+ /* Add the size of the channel tlv header and the data
+ * length
+ */
+ scan_cfg_out->tlv_buf_len +=
+ (sizeof(tlv_o->header)
+ + le16_to_cpu(tlv_o->header.len));
+
+ /* Increment the index to the channel tlv we are
+ * constructing
+ */
+ tlv_idx++;
+
+ /* Count the total scan time per command */
+ total_scan_time +=
+ le16_to_cpu(tmp_chan_list->max_scan_time);
+
+ done_early = false;
+
+ /* Stop the loop if the *current* channel is in the
+ * 1,6,11 set and we are not filtering on a BSSID
+ * or SSID.
+ */
+ if (!filtered_scan &&
+ (tmp_chan_list->chan_number == 1 ||
+ tmp_chan_list->chan_number == 6 ||
+ tmp_chan_list->chan_number == 11))
+ done_early = true;
+
+ /* Increment the tmp pointer to the next channel to
+ * be scanned
+ */
+ tmp_chan_list++;
+
+ /* Stop the loop if the *next* channel is in the 1,6,11
+ * set. This will cause it to be the only channel
+ * scanned on the next interation
+ */
+ if (!filtered_scan &&
+ (tmp_chan_list->chan_number == 1 ||
+ tmp_chan_list->chan_number == 6 ||
+ tmp_chan_list->chan_number == 11))
+ done_early = true;
+ }
+
+ /* The total scan time should be less than scan command timeout
+ * value
+ */
+ if (total_scan_time > NXPWIFI_MAX_TOTAL_SCAN_TIME) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "total scan time %dms\t"
+ "is over limit (%dms), scan skipped\n",
+ total_scan_time,
+ NXPWIFI_MAX_TOTAL_SCAN_TIME);
+ ret = -1;
+ break;
+ }
+
+ rates_size = nxpwifi_append_rate_tlv(priv, scan_cfg_out,
+ radio_type);
+
+ /* Send the scan command to the firmware with the specified
+ * cfg
+ */
+ if (priv->adapter->ext_scan)
+ cmd_no = HOST_CMD_802_11_SCAN_EXT;
+ else
+ cmd_no = HOST_CMD_802_11_SCAN;
+
+ ret = nxpwifi_send_cmd(priv, cmd_no, HOST_ACT_GEN_SET,
+ 0, scan_cfg_out, false);
+
+ /* rate IE is updated per scan command but same starting
+ * pointer is used each time so that rate IE from earlier
+ * scan_cfg_out->buf is overwritten with new one.
+ */
+ scan_cfg_out->tlv_buf_len -=
+ sizeof(struct nxpwifi_ie_types_header) + rates_size;
+
+ if (ret) {
+ nxpwifi_cancel_pending_scan_cmd(adapter);
+ break;
+ }
+ }
+
+ if (ret)
+ return -1;
+
+ return 0;
+}
+
+/* This function constructs a scan command configuration structure to use
+ * in scan commands.
+ *
+ * Application layer or other functions can invoke network scanning
+ * with a scan configuration supplied in a user scan configuration structure.
+ * This structure is used as the basis of one or many scan command configuration
+ * commands that are sent to the command processing module and eventually to the
+ * firmware.
+ *
+ * This function creates a scan command configuration structure based on the
+ * following user supplied parameters (if present):
+ * - SSID filter
+ * - BSSID filter
+ * - Number of Probes to be sent
+ * - Channel list
+ *
+ * If the SSID or BSSID filter is not present, the filter is disabled/cleared.
+ * If the number of probes is not set, adapter default setting is used.
+ */
+static void
+nxpwifi_config_scan(struct nxpwifi_private *priv,
+ const struct nxpwifi_user_scan_cfg *user_scan_in,
+ struct nxpwifi_scan_cmd_config *scan_cfg_out,
+ struct nxpwifi_ie_types_chan_list_param_set **chan_list_out,
+ struct nxpwifi_chan_scan_param_set *scan_chan_list,
+ u8 *max_chan_per_scan, u8 *filtered_scan,
+ u8 *scan_current_only)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_ie_types_num_probes *num_probes_tlv;
+ struct nxpwifi_ie_types_scan_chan_gap *chan_gap_tlv;
+ struct nxpwifi_ie_types_random_mac *random_mac_tlv;
+ struct nxpwifi_ie_types_wildcard_ssid_params *wildcard_ssid_tlv;
+ struct nxpwifi_ie_types_bssid_list *bssid_tlv;
+ u8 *tlv_pos;
+ u32 num_probes;
+ u32 ssid_len;
+ u32 chan_idx;
+ u32 scan_time;
+ u32 scan_type;
+ u16 scan_dur;
+ u8 channel;
+ u8 radio_type;
+ int i;
+ u8 ssid_filter;
+ struct nxpwifi_ie_types_htcap *ht_cap;
+ struct nxpwifi_ie_types_bss_mode *bss_mode;
+
+ /* The tlv_buf_len is calculated for each scan command. The TLVs added
+ * in this routine will be preserved since the routine that sends the
+ * command will append channelTLVs at *chan_list_out. The difference
+ * between the *chan_list_out and the tlv_buf start will be used to
+ * calculate the size of anything we add in this routine.
+ */
+ scan_cfg_out->tlv_buf_len = 0;
+
+ /* Running tlv pointer. Assigned to chan_list_out at end of function
+ * so later routines know where channels can be added to the command
+ * buf
+ */
+ tlv_pos = scan_cfg_out->tlv_buf;
+
+ /* Initialize the scan as un-filtered; the flag is later set to TRUE
+ * below if a SSID or BSSID filter is sent in the command
+ */
+ *filtered_scan = false;
+
+ /* Initialize the scan as not being only on the current channel. If
+ * the channel list is customized, only contains one channel, and is
+ * the active channel, this is set true and data flow is not halted.
+ */
+ *scan_current_only = false;
+
+ if (user_scan_in) {
+ u8 tmpaddr[ETH_ALEN];
+
+ /* Default the ssid_filter flag to TRUE, set false under
+ * certain wildcard conditions and qualified by the existence
+ * of an SSID list before marking the scan as filtered
+ */
+ ssid_filter = true;
+
+ /* Set the BSS type scan filter, use Adapter setting if
+ * unset
+ */
+ scan_cfg_out->bss_mode =
+ (u8)(user_scan_in->bss_mode ?: adapter->scan_mode);
+
+ /* Set the number of probes to send, use Adapter setting
+ * if unset
+ */
+ num_probes = user_scan_in->num_probes ?: adapter->scan_probes;
+
+ /* Set the BSSID filter to the incoming configuration,
+ * if non-zero. If not set, it will remain disabled
+ * (all zeros).
+ */
+ memcpy(scan_cfg_out->specific_bssid,
+ user_scan_in->specific_bssid,
+ sizeof(scan_cfg_out->specific_bssid));
+
+ memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN);
+
+ if (adapter->ext_scan &&
+ !is_zero_ether_addr(tmpaddr)) {
+ bssid_tlv =
+ (struct nxpwifi_ie_types_bssid_list *)tlv_pos;
+ bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID);
+ bssid_tlv->header.len = cpu_to_le16(ETH_ALEN);
+ memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid,
+ ETH_ALEN);
+ tlv_pos += sizeof(struct nxpwifi_ie_types_bssid_list);
+ }
+
+ for (i = 0; i < user_scan_in->num_ssids; i++) {
+ ssid_len = user_scan_in->ssid_list[i].ssid_len;
+
+ wildcard_ssid_tlv =
+ (struct nxpwifi_ie_types_wildcard_ssid_params *)
+ tlv_pos;
+ wildcard_ssid_tlv->header.type =
+ cpu_to_le16(TLV_TYPE_WILDCARDSSID);
+ wildcard_ssid_tlv->header.len =
+ cpu_to_le16((u16)(ssid_len + sizeof(u8)));
+
+ /* max_ssid_length = 0 tells firmware to perform
+ * specific scan for the SSID filled, whereas
+ * max_ssid_length = IEEE80211_MAX_SSID_LEN is for
+ * wildcard scan.
+ */
+ if (ssid_len)
+ wildcard_ssid_tlv->max_ssid_length = 0;
+ else
+ wildcard_ssid_tlv->max_ssid_length =
+ IEEE80211_MAX_SSID_LEN;
+
+ if (!memcmp(user_scan_in->ssid_list[i].ssid,
+ "DIRECT-", 7))
+ wildcard_ssid_tlv->max_ssid_length = 0xfe;
+
+ memcpy(wildcard_ssid_tlv->ssid,
+ user_scan_in->ssid_list[i].ssid, ssid_len);
+
+ tlv_pos += (sizeof(wildcard_ssid_tlv->header)
+ + le16_to_cpu(wildcard_ssid_tlv->header.len));
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: scan: ssid[%d]: %s, %d\n",
+ i, wildcard_ssid_tlv->ssid,
+ wildcard_ssid_tlv->max_ssid_length);
+
+ /* Empty wildcard ssid with a maxlen will match many or
+ * potentially all SSIDs (maxlen == 32), therefore do
+ * not treat the scan as
+ * filtered.
+ */
+ if (!ssid_len && wildcard_ssid_tlv->max_ssid_length)
+ ssid_filter = false;
+ }
+
+ /* The default number of channels sent in the command is low to
+ * ensure the response buffer from the firmware does not
+ * truncate scan results. That is not an issue with an SSID
+ * or BSSID filter applied to the scan results in the firmware.
+ */
+ memcpy(tmpaddr, scan_cfg_out->specific_bssid, ETH_ALEN);
+ if ((i && ssid_filter) ||
+ !is_zero_ether_addr(tmpaddr))
+ *filtered_scan = true;
+
+ if (user_scan_in->scan_chan_gap) {
+ nxpwifi_dbg(adapter, INFO,
+ "info: scan: channel gap = %d\n",
+ user_scan_in->scan_chan_gap);
+ *max_chan_per_scan =
+ NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN;
+
+ chan_gap_tlv = (void *)tlv_pos;
+ chan_gap_tlv->header.type =
+ cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP);
+ chan_gap_tlv->header.len =
+ cpu_to_le16(sizeof(chan_gap_tlv->chan_gap));
+ chan_gap_tlv->chan_gap =
+ cpu_to_le16((user_scan_in->scan_chan_gap));
+ tlv_pos +=
+ sizeof(struct nxpwifi_ie_types_scan_chan_gap);
+ }
+
+ if (!is_zero_ether_addr(user_scan_in->random_mac)) {
+ random_mac_tlv = (void *)tlv_pos;
+ random_mac_tlv->header.type =
+ cpu_to_le16(TLV_TYPE_RANDOM_MAC);
+ random_mac_tlv->header.len =
+ cpu_to_le16(sizeof(random_mac_tlv->mac));
+ ether_addr_copy(random_mac_tlv->mac,
+ user_scan_in->random_mac);
+ tlv_pos +=
+ sizeof(struct nxpwifi_ie_types_random_mac);
+ }
+ } else {
+ scan_cfg_out->bss_mode = (u8)adapter->scan_mode;
+ num_probes = adapter->scan_probes;
+ }
+
+ /* If a specific BSSID or SSID is used, the number of channels in the
+ * scan command will be increased to the absolute maximum.
+ */
+ if (*filtered_scan) {
+ *max_chan_per_scan = NXPWIFI_MAX_CHANNELS_PER_SPECIFIC_SCAN;
+ } else {
+ if (!priv->media_connected)
+ *max_chan_per_scan = NXPWIFI_DEF_CHANNELS_PER_SCAN_CMD;
+ else
+ *max_chan_per_scan =
+ NXPWIFI_DEF_CHANNELS_PER_SCAN_CMD / 2;
+ }
+
+ if (adapter->ext_scan) {
+ bss_mode = (struct nxpwifi_ie_types_bss_mode *)tlv_pos;
+ bss_mode->header.type = cpu_to_le16(TLV_TYPE_BSS_MODE);
+ bss_mode->header.len = cpu_to_le16(sizeof(bss_mode->bss_mode));
+ bss_mode->bss_mode = scan_cfg_out->bss_mode;
+ tlv_pos += sizeof(bss_mode->header) +
+ le16_to_cpu(bss_mode->header.len);
+ }
+
+ /* If the input config or adapter has the number of Probes set,
+ * add tlv
+ */
+ if (num_probes) {
+ nxpwifi_dbg(adapter, INFO,
+ "info: scan: num_probes = %d\n",
+ num_probes);
+
+ num_probes_tlv = (struct nxpwifi_ie_types_num_probes *)tlv_pos;
+ num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES);
+ num_probes_tlv->header.len =
+ cpu_to_le16(sizeof(num_probes_tlv->num_probes));
+ num_probes_tlv->num_probes = cpu_to_le16((u16)num_probes);
+
+ tlv_pos += sizeof(num_probes_tlv->header) +
+ le16_to_cpu(num_probes_tlv->header.len);
+ }
+
+ if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) &&
+ (priv->adapter->config_bands & BAND_GN ||
+ priv->adapter->config_bands & BAND_AN)) {
+ ht_cap = (struct nxpwifi_ie_types_htcap *)tlv_pos;
+ memset(ht_cap, 0, sizeof(struct nxpwifi_ie_types_htcap));
+ ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
+ ht_cap->header.len =
+ cpu_to_le16(sizeof(struct ieee80211_ht_cap));
+ radio_type =
+ nxpwifi_band_to_radio_type(priv->adapter->config_bands);
+ nxpwifi_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
+ tlv_pos += sizeof(struct nxpwifi_ie_types_htcap);
+ }
+
+ /* Append vendor specific IE TLV */
+ nxpwifi_cmd_append_vsie_tlv(priv, NXPWIFI_VSIE_MASK_SCAN, &tlv_pos);
+
+ /* Set the output for the channel TLV to the address in the tlv buffer
+ * past any TLVs that were added in this function (SSID, num_probes).
+ * Channel TLVs will be added past this for each scan command,
+ * preserving the TLVs that were previously added.
+ */
+ *chan_list_out =
+ (struct nxpwifi_ie_types_chan_list_param_set *)tlv_pos;
+
+ if (user_scan_in && user_scan_in->chan_list[0].chan_number) {
+ nxpwifi_dbg(adapter, INFO,
+ "info: Scan: Using supplied channel list\n");
+
+ for (chan_idx = 0;
+ chan_idx < NXPWIFI_USER_SCAN_CHAN_MAX &&
+ user_scan_in->chan_list[chan_idx].chan_number;
+ chan_idx++) {
+ channel = user_scan_in->chan_list[chan_idx].chan_number;
+ scan_chan_list[chan_idx].chan_number = channel;
+
+ radio_type =
+ user_scan_in->chan_list[chan_idx].radio_type;
+ scan_chan_list[chan_idx].radio_type = radio_type;
+
+ scan_type = user_scan_in->chan_list[chan_idx].scan_type;
+
+ if (scan_type == NXPWIFI_SCAN_TYPE_PASSIVE)
+ scan_chan_list[chan_idx].chan_scan_mode_bmap |=
+ (NXPWIFI_PASSIVE_SCAN |
+ NXPWIFI_HIDDEN_SSID_REPORT);
+ else
+ scan_chan_list[chan_idx].chan_scan_mode_bmap &=
+ ~NXPWIFI_PASSIVE_SCAN;
+
+ scan_chan_list[chan_idx].chan_scan_mode_bmap |=
+ NXPWIFI_DISABLE_CHAN_FILT;
+
+ scan_time = user_scan_in->chan_list[chan_idx].scan_time;
+
+ if (scan_time) {
+ scan_dur = (u16)scan_time;
+ } else {
+ if (scan_type == NXPWIFI_SCAN_TYPE_PASSIVE)
+ scan_dur = adapter->passive_scan_time;
+ else if (*filtered_scan)
+ scan_dur = adapter->specific_scan_time;
+ else
+ scan_dur = adapter->active_scan_time;
+ }
+
+ scan_chan_list[chan_idx].min_scan_time =
+ cpu_to_le16(scan_dur);
+ scan_chan_list[chan_idx].max_scan_time =
+ cpu_to_le16(scan_dur);
+ }
+
+ /* Check if we are only scanning the current channel */
+ if (chan_idx == 1 &&
+ user_scan_in->chan_list[0].chan_number ==
+ priv->curr_bss_params.bss_descriptor.channel) {
+ *scan_current_only = true;
+ nxpwifi_dbg(adapter, INFO,
+ "info: Scan: Scanning current channel only\n");
+ }
+ } else {
+ nxpwifi_dbg(adapter, INFO,
+ "info: Scan: Creating full region channel list\n");
+ nxpwifi_scan_create_channel_list(priv, user_scan_in,
+ scan_chan_list,
+ *filtered_scan);
+ }
+}
+
+/* This function inspects the scan response buffer for pointers to
+ * expected TLVs.
+ *
+ * TLVs can be included at the end of the scan response BSS information.
+ *
+ * Data in the buffer is parsed pointers to TLVs that can potentially
+ * be passed back in the response.
+ */
+static void
+nxpwifi_ret_802_11_scan_get_tlv_ptrs(struct nxpwifi_adapter *adapter,
+ struct nxpwifi_ie_types_data *tlv,
+ u32 tlv_buf_size, u32 req_tlv_type,
+ struct nxpwifi_ie_types_data **tlv_data)
+{
+ struct nxpwifi_ie_types_data *current_tlv;
+ u32 tlv_buf_left;
+ u32 tlv_type;
+ u32 tlv_len;
+
+ current_tlv = tlv;
+ tlv_buf_left = tlv_buf_size;
+ *tlv_data = NULL;
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: SCAN_RESP: tlv_buf_size = %d\n",
+ tlv_buf_size);
+
+ while (tlv_buf_left >= sizeof(struct nxpwifi_ie_types_header)) {
+ tlv_type = le16_to_cpu(current_tlv->header.type);
+ tlv_len = le16_to_cpu(current_tlv->header.len);
+
+ if (sizeof(tlv->header) + tlv_len > tlv_buf_left) {
+ nxpwifi_dbg(adapter, ERROR,
+ "SCAN_RESP: TLV buffer corrupt\n");
+ break;
+ }
+
+ if (req_tlv_type == tlv_type) {
+ switch (tlv_type) {
+ case TLV_TYPE_TSFTIMESTAMP:
+ nxpwifi_dbg(adapter, INFO,
+ "info: SCAN_RESP: TSF\t"
+ "timestamp TLV, len = %d\n",
+ tlv_len);
+ *tlv_data = current_tlv;
+ break;
+ case TLV_TYPE_CHANNELBANDLIST:
+ nxpwifi_dbg(adapter, INFO,
+ "info: SCAN_RESP: channel\t"
+ "band list TLV, len = %d\n",
+ tlv_len);
+ *tlv_data = current_tlv;
+ break;
+ default:
+ nxpwifi_dbg(adapter, ERROR,
+ "SCAN_RESP: unhandled TLV = %d\n",
+ tlv_type);
+ /* Give up, this seems corrupted */
+ return;
+ }
+ }
+
+ if (*tlv_data)
+ break;
+
+ tlv_buf_left -= (sizeof(tlv->header) + tlv_len);
+ current_tlv =
+ (struct nxpwifi_ie_types_data *)(current_tlv->data +
+ tlv_len);
+ } /* while */
+}
+
+/* This function parses provided beacon buffer and updates
+ * respective fields in bss descriptor structure.
+ */
+int nxpwifi_update_bss_desc_with_ie(struct nxpwifi_adapter *adapter,
+ struct nxpwifi_bssdescriptor *bss_entry)
+{
+ u8 element_id;
+ struct ieee_types_fh_param_set *fh_param_set;
+ struct ieee_types_ds_param_set *ds_param_set;
+ struct ieee_types_cf_param_set *cf_param_set;
+ struct ieee_types_ibss_param_set *ibss_param_set;
+ u8 *current_ptr;
+ u8 *rate;
+ u8 element_len;
+ u16 total_ie_len;
+ u8 bytes_to_copy;
+ u8 rate_size;
+ u8 found_data_rate_ie;
+ u32 bytes_left;
+ struct ieee_types_vendor_specific *vendor_ie;
+ const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 };
+ const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 };
+
+ found_data_rate_ie = false;
+ rate_size = 0;
+ current_ptr = bss_entry->beacon_buf;
+ bytes_left = bss_entry->beacon_buf_size;
+
+ /* Process variable IE */
+ while (bytes_left >= 2) {
+ element_id = *current_ptr;
+ element_len = *(current_ptr + 1);
+ total_ie_len = element_len + sizeof(struct ieee_types_header);
+
+ if (bytes_left < total_ie_len) {
+ nxpwifi_dbg(adapter, ERROR,
+ "err: InterpretIE: in processing\t"
+ "IE, bytes left < IE length\n");
+ return -EINVAL;
+ }
+ switch (element_id) {
+ case WLAN_EID_SSID:
+ if (element_len > IEEE80211_MAX_SSID_LEN)
+ return -EINVAL;
+ bss_entry->ssid.ssid_len = element_len;
+ memcpy(bss_entry->ssid.ssid, (current_ptr + 2),
+ element_len);
+ nxpwifi_dbg(adapter, INFO,
+ "info: InterpretIE: ssid: %-32s\n",
+ bss_entry->ssid.ssid);
+ break;
+
+ case WLAN_EID_SUPP_RATES:
+ if (element_len > NXPWIFI_SUPPORTED_RATES)
+ return -EINVAL;
+ memcpy(bss_entry->data_rates, current_ptr + 2,
+ element_len);
+ memcpy(bss_entry->supported_rates, current_ptr + 2,
+ element_len);
+ rate_size = element_len;
+ found_data_rate_ie = true;
+ break;
+
+ case WLAN_EID_FH_PARAMS:
+ if (total_ie_len < sizeof(*fh_param_set))
+ return -EINVAL;
+ fh_param_set =
+ (struct ieee_types_fh_param_set *)current_ptr;
+ memcpy(&bss_entry->phy_param_set.fh_param_set,
+ fh_param_set,
+ sizeof(struct ieee_types_fh_param_set));
+ break;
+
+ case WLAN_EID_DS_PARAMS:
+ if (total_ie_len < sizeof(*ds_param_set))
+ return -EINVAL;
+ ds_param_set =
+ (struct ieee_types_ds_param_set *)current_ptr;
+
+ bss_entry->channel = ds_param_set->current_chan;
+
+ memcpy(&bss_entry->phy_param_set.ds_param_set,
+ ds_param_set,
+ sizeof(struct ieee_types_ds_param_set));
+ break;
+
+ case WLAN_EID_CF_PARAMS:
+ if (total_ie_len < sizeof(*cf_param_set))
+ return -EINVAL;
+ cf_param_set =
+ (struct ieee_types_cf_param_set *)current_ptr;
+ memcpy(&bss_entry->ss_param_set.cf_param_set,
+ cf_param_set,
+ sizeof(struct ieee_types_cf_param_set));
+ break;
+
+ case WLAN_EID_IBSS_PARAMS:
+ if (total_ie_len < sizeof(*ibss_param_set))
+ return -EINVAL;
+ ibss_param_set =
+ (struct ieee_types_ibss_param_set *)
+ current_ptr;
+ memcpy(&bss_entry->ss_param_set.ibss_param_set,
+ ibss_param_set,
+ sizeof(struct ieee_types_ibss_param_set));
+ break;
+
+ case WLAN_EID_ERP_INFO:
+ if (!element_len)
+ return -EINVAL;
+ bss_entry->erp_flags = *(current_ptr + 2);
+ break;
+
+ case WLAN_EID_PWR_CONSTRAINT:
+ if (!element_len)
+ return -EINVAL;
+ bss_entry->local_constraint = *(current_ptr + 2);
+ bss_entry->sensed_11h = true;
+ break;
+
+ case WLAN_EID_CHANNEL_SWITCH:
+ bss_entry->chan_sw_ie_present = true;
+ fallthrough;
+ case WLAN_EID_PWR_CAPABILITY:
+ case WLAN_EID_TPC_REPORT:
+ case WLAN_EID_QUIET:
+ bss_entry->sensed_11h = true;
+ break;
+
+ case WLAN_EID_EXT_SUPP_RATES:
+ /* Only process extended supported rate
+ * if data rate is already found.
+ * Data rate IE should come before
+ * extended supported rate IE
+ */
+ if (found_data_rate_ie) {
+ if ((element_len + rate_size) >
+ NXPWIFI_SUPPORTED_RATES)
+ bytes_to_copy =
+ (NXPWIFI_SUPPORTED_RATES -
+ rate_size);
+ else
+ bytes_to_copy = element_len;
+
+ rate = (u8 *)bss_entry->data_rates;
+ rate += rate_size;
+ memcpy(rate, current_ptr + 2, bytes_to_copy);
+
+ rate = (u8 *)bss_entry->supported_rates;
+ rate += rate_size;
+ memcpy(rate, current_ptr + 2, bytes_to_copy);
+ }
+ break;
+
+ case WLAN_EID_VENDOR_SPECIFIC:
+ vendor_ie = (struct ieee_types_vendor_specific *)
+ current_ptr;
+
+ /* 802.11 requires at least 3-byte OUI. */
+ if (element_len < sizeof(vendor_ie->vend_hdr.oui.oui))
+ return -EINVAL;
+
+ /* Not long enough for a match? Skip it. */
+ if (element_len < sizeof(wpa_oui))
+ break;
+
+ if (!memcmp(&vendor_ie->vend_hdr.oui, wpa_oui,
+ sizeof(wpa_oui))) {
+ bss_entry->bcn_wpa_ie =
+ (struct ieee_types_vendor_specific *)
+ current_ptr;
+ bss_entry->wpa_offset = (u16)
+ (current_ptr - bss_entry->beacon_buf);
+ } else if (!memcmp(&vendor_ie->vend_hdr.oui, wmm_oui,
+ sizeof(wmm_oui))) {
+ if (total_ie_len ==
+ sizeof(struct ieee_types_wmm_parameter) ||
+ total_ie_len ==
+ sizeof(struct ieee_types_wmm_info))
+ /* Only accept and copy the WMM IE if
+ * it matches the size expected for the
+ * WMM Info IE or the WMM Parameter IE.
+ */
+ memcpy((u8 *)&bss_entry->wmm_ie,
+ current_ptr, total_ie_len);
+ }
+ break;
+ case WLAN_EID_RSN:
+ bss_entry->bcn_rsn_ie =
+ (struct ieee_types_generic *)current_ptr;
+ bss_entry->rsn_offset = (u16)(current_ptr -
+ bss_entry->beacon_buf);
+ break;
+ case WLAN_EID_RSNX:
+ bss_entry->bcn_rsnx_ie =
+ (struct ieee_types_generic *)current_ptr;
+ bss_entry->rsnx_offset =
+ (u16)(current_ptr - bss_entry->beacon_buf);
+ break;
+ case WLAN_EID_HT_CAPABILITY:
+ bss_entry->bcn_ht_cap = (struct ieee80211_ht_cap *)
+ (current_ptr +
+ sizeof(struct ieee_types_header));
+ bss_entry->ht_cap_offset = (u16)(current_ptr +
+ sizeof(struct ieee_types_header) -
+ bss_entry->beacon_buf);
+ break;
+ case WLAN_EID_HT_OPERATION:
+ bss_entry->bcn_ht_oper =
+ (struct ieee80211_ht_operation *)(current_ptr +
+ sizeof(struct ieee_types_header));
+ bss_entry->ht_info_offset = (u16)(current_ptr +
+ sizeof(struct ieee_types_header) -
+ bss_entry->beacon_buf);
+ break;
+ case WLAN_EID_VHT_CAPABILITY:
+ bss_entry->disable_11ac = false;
+ bss_entry->bcn_vht_cap =
+ (void *)(current_ptr +
+ sizeof(struct ieee_types_header));
+ bss_entry->vht_cap_offset =
+ (u16)((u8 *)bss_entry->bcn_vht_cap -
+ bss_entry->beacon_buf);
+ break;
+ case WLAN_EID_VHT_OPERATION:
+ bss_entry->bcn_vht_oper =
+ (void *)(current_ptr +
+ sizeof(struct ieee_types_header));
+ bss_entry->vht_info_offset =
+ (u16)((u8 *)bss_entry->bcn_vht_oper -
+ bss_entry->beacon_buf);
+ break;
+ case WLAN_EID_BSS_COEX_2040:
+ bss_entry->bcn_bss_co_2040 = current_ptr;
+ bss_entry->bss_co_2040_offset =
+ (u16)(current_ptr - bss_entry->beacon_buf);
+ break;
+ case WLAN_EID_EXT_CAPABILITY:
+ bss_entry->bcn_ext_cap = current_ptr;
+ bss_entry->ext_cap_offset =
+ (u16)(current_ptr - bss_entry->beacon_buf);
+ break;
+ case WLAN_EID_OPMODE_NOTIF:
+ bss_entry->oper_mode = (void *)current_ptr;
+ bss_entry->oper_mode_offset =
+ (u16)((u8 *)bss_entry->oper_mode -
+ bss_entry->beacon_buf);
+ break;
+ default:
+ break;
+ }
+
+ current_ptr += total_ie_len;
+ bytes_left -= total_ie_len;
+
+ } /* while (bytes_left > 2) */
+ return 0;
+}
+
+/* This function converts radio type scan parameter to a band configuration
+ * to be used in join command.
+ */
+static u8
+nxpwifi_radio_type_to_band(u8 radio_type)
+{
+ switch (radio_type) {
+ case HOST_SCAN_RADIO_TYPE_A:
+ return BAND_A;
+ case HOST_SCAN_RADIO_TYPE_BG:
+ default:
+ return BAND_G;
+ }
+}
+
+/* This is an internal function used to start a scan based on an input
+ * configuration.
+ *
+ * This uses the input user scan configuration information when provided in
+ * order to send the appropriate scan commands to firmware to populate or
+ * update the internal driver scan table.
+ */
+int nxpwifi_scan_networks(struct nxpwifi_private *priv,
+ const struct nxpwifi_user_scan_cfg *user_scan_in)
+{
+ int ret;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct cmd_ctrl_node *cmd_node;
+ union nxpwifi_scan_cmd_config_tlv *scan_cfg_out;
+ struct nxpwifi_ie_types_chan_list_param_set *chan_list_out;
+ struct nxpwifi_chan_scan_param_set *scan_chan_list;
+ u8 filtered_scan;
+ u8 scan_current_chan_only;
+ u8 max_chan_per_scan;
+
+ if (adapter->scan_processing) {
+ nxpwifi_dbg(adapter, WARN,
+ "cmd: Scan already in process...\n");
+ return -EBUSY;
+ }
+
+ if (priv->scan_block) {
+ nxpwifi_dbg(adapter, WARN,
+ "cmd: Scan is blocked during association...\n");
+ return -EBUSY;
+ }
+
+ if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags) ||
+ test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "Ignore scan. Card removed or firmware in bad state\n");
+ return -EFAULT;
+ }
+
+ spin_lock_bh(&adapter->nxpwifi_cmd_lock);
+ adapter->scan_processing = true;
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+
+ scan_cfg_out = kzalloc(sizeof(union nxpwifi_scan_cmd_config_tlv),
+ GFP_KERNEL);
+ if (!scan_cfg_out) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ scan_chan_list = kcalloc(NXPWIFI_USER_SCAN_CHAN_MAX,
+ sizeof(struct nxpwifi_chan_scan_param_set),
+ GFP_KERNEL);
+ if (!scan_chan_list) {
+ kfree(scan_cfg_out);
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ nxpwifi_config_scan(priv, user_scan_in, &scan_cfg_out->config,
+ &chan_list_out, scan_chan_list, &max_chan_per_scan,
+ &filtered_scan, &scan_current_chan_only);
+
+ ret = nxpwifi_scan_channel_list(priv, max_chan_per_scan, filtered_scan,
+ &scan_cfg_out->config, chan_list_out,
+ scan_chan_list);
+
+ /* Get scan command from scan_pending_q and put to cmd_pending_q */
+ if (!ret) {
+ spin_lock_bh(&adapter->scan_pending_q_lock);
+ if (!list_empty(&adapter->scan_pending_q)) {
+ cmd_node = list_first_entry(&adapter->scan_pending_q,
+ struct cmd_ctrl_node, list);
+ list_del(&cmd_node->list);
+ spin_unlock_bh(&adapter->scan_pending_q_lock);
+ nxpwifi_insert_cmd_to_pending_q(adapter, cmd_node);
+ queue_work(adapter->workqueue, &adapter->main_work);
+
+ /* Perform internal scan synchronously */
+ if (!priv->scan_request) {
+ nxpwifi_dbg(adapter, INFO,
+ "wait internal scan\n");
+ nxpwifi_wait_queue_complete(adapter, cmd_node);
+ }
+ } else {
+ spin_unlock_bh(&adapter->scan_pending_q_lock);
+ }
+ }
+
+ kfree(scan_cfg_out);
+ kfree(scan_chan_list);
+done:
+ if (ret) {
+ spin_lock_bh(&adapter->nxpwifi_cmd_lock);
+ adapter->scan_processing = false;
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+ }
+ return ret;
+}
+
+/* This function prepares a scan command to be sent to the firmware.
+ *
+ * This uses the scan command configuration sent to the command processing
+ * module in command preparation stage to configure a scan command structure
+ * to send to firmware.
+ *
+ * The fixed fields specifying the BSS type and BSSID filters as well as a
+ * variable number/length of TLVs are sent in the command to firmware.
+ *
+ * Preparation also includes -
+ * - Setting command ID, and proper size
+ * - Ensuring correct endian-ness
+ */
+int nxpwifi_cmd_802_11_scan(struct host_cmd_ds_command *cmd,
+ struct nxpwifi_scan_cmd_config *scan_cfg)
+{
+ struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan;
+
+ /* Set fixed field variables in scan command */
+ scan_cmd->bss_mode = scan_cfg->bss_mode;
+ memcpy(scan_cmd->bssid, scan_cfg->specific_bssid,
+ sizeof(scan_cmd->bssid));
+ memcpy(scan_cmd->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len);
+
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_SCAN);
+
+ /* Size is equal to the sizeof(fixed portions) + the TLV len + header */
+ cmd->size = cpu_to_le16((u16)(sizeof(scan_cmd->bss_mode)
+ + sizeof(scan_cmd->bssid)
+ + scan_cfg->tlv_buf_len + S_DS_GEN));
+
+ return 0;
+}
+
+/* This function checks compatibility of requested network with current
+ * driver settings.
+ */
+int nxpwifi_check_network_compatibility(struct nxpwifi_private *priv,
+ struct nxpwifi_bssdescriptor *bss_desc)
+{
+ int ret = -1;
+
+ if (!bss_desc)
+ return -1;
+
+ if ((nxpwifi_get_cfp(priv, (u8)bss_desc->bss_band,
+ (u16)bss_desc->channel, 0))) {
+ switch (priv->bss_mode) {
+ case NL80211_IFTYPE_STATION:
+ ret = nxpwifi_is_network_compatible(priv, bss_desc,
+ priv->bss_mode);
+ if (ret)
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Incompatible network settings\n");
+ break;
+ default:
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+/* This function checks if SSID string contains all zeroes or length is zero */
+static bool nxpwifi_is_hidden_ssid(struct cfg80211_ssid *ssid)
+{
+ int idx;
+
+ for (idx = 0; idx < ssid->ssid_len; idx++) {
+ if (ssid->ssid[idx])
+ return false;
+ }
+
+ return true;
+}
+
+/* This function checks if any hidden SSID found in passive scan channels
+ * and save those channels for specific SSID active scan
+ */
+static int nxpwifi_save_hidden_ssid_channels(struct nxpwifi_private *priv,
+ struct cfg80211_bss *bss)
+{
+ struct nxpwifi_bssdescriptor *bss_desc;
+ int ret;
+ int chid;
+
+ /* Allocate and fill new bss descriptor */
+ bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL);
+ if (!bss_desc)
+ return -ENOMEM;
+
+ ret = nxpwifi_fill_new_bss_desc(priv, bss, bss_desc);
+ if (ret)
+ goto done;
+
+ if (nxpwifi_is_hidden_ssid(&bss_desc->ssid)) {
+ nxpwifi_dbg(priv->adapter, INFO, "found hidden SSID\n");
+ for (chid = 0 ; chid < NXPWIFI_USER_SCAN_CHAN_MAX; chid++) {
+ if (priv->hidden_chan[chid].chan_number ==
+ bss->channel->hw_value)
+ break;
+
+ if (!priv->hidden_chan[chid].chan_number) {
+ priv->hidden_chan[chid].chan_number =
+ bss->channel->hw_value;
+ priv->hidden_chan[chid].radio_type =
+ bss->channel->band;
+ priv->hidden_chan[chid].scan_type =
+ NXPWIFI_SCAN_TYPE_ACTIVE;
+ break;
+ }
+ }
+ }
+
+done:
+ /* beacon_ie buffer was allocated in function
+ * nxpwifi_fill_new_bss_desc(). Free it now.
+ */
+ kfree(bss_desc->beacon_buf);
+ kfree(bss_desc);
+ return 0;
+}
+
+static int nxpwifi_update_curr_bss_params(struct nxpwifi_private *priv,
+ struct cfg80211_bss *bss)
+{
+ struct nxpwifi_bssdescriptor *bss_desc;
+ int ret;
+
+ /* Allocate and fill new bss descriptor */
+ bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL);
+ if (!bss_desc)
+ return -ENOMEM;
+
+ ret = nxpwifi_fill_new_bss_desc(priv, bss, bss_desc);
+ if (ret)
+ goto done;
+
+ ret = nxpwifi_check_network_compatibility(priv, bss_desc);
+ if (ret)
+ goto done;
+
+ spin_lock_bh(&priv->curr_bcn_buf_lock);
+ /* Make a copy of current BSSID descriptor */
+ memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc,
+ sizeof(priv->curr_bss_params.bss_descriptor));
+
+ /* The contents of beacon_ie will be copied to its own buffer
+ * in nxpwifi_save_curr_bcn()
+ */
+ nxpwifi_save_curr_bcn(priv);
+ spin_unlock_bh(&priv->curr_bcn_buf_lock);
+
+done:
+ /* beacon_ie buffer was allocated in function
+ * nxpwifi_fill_new_bss_desc(). Free it now.
+ */
+ kfree(bss_desc->beacon_buf);
+ kfree(bss_desc);
+ return 0;
+}
+
+static int
+nxpwifi_parse_single_response_buf(struct nxpwifi_private *priv, u8 **bss_info,
+ u32 *bytes_left, u64 fw_tsf, u8 *radio_type,
+ bool ext_scan, s32 rssi_val)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_chan_freq_power *cfp;
+ struct cfg80211_bss *bss;
+ u8 bssid[ETH_ALEN];
+ s32 rssi;
+ const u8 *ie_buf;
+ size_t ie_len;
+ u16 channel = 0;
+ u16 beacon_size = 0;
+ u32 curr_bcn_bytes;
+ u32 freq;
+ u16 beacon_period;
+ u16 cap_info_bitmap;
+ u8 *current_ptr;
+ u64 timestamp;
+ struct nxpwifi_fixed_bcn_param *bcn_param;
+ struct nxpwifi_bss_priv *bss_priv;
+
+ if (*bytes_left >= sizeof(beacon_size)) {
+ /* Extract & convert beacon size from command buffer */
+ beacon_size = get_unaligned_le16((*bss_info));
+ *bytes_left -= sizeof(beacon_size);
+ *bss_info += sizeof(beacon_size);
+ }
+
+ if (!beacon_size || beacon_size > *bytes_left) {
+ *bss_info += *bytes_left;
+ *bytes_left = 0;
+ return -EFAULT;
+ }
+
+ /* Initialize the current working beacon pointer for this BSS
+ * iteration
+ */
+ current_ptr = *bss_info;
+
+ /* Advance the return beacon pointer past the current beacon */
+ *bss_info += beacon_size;
+ *bytes_left -= beacon_size;
+
+ curr_bcn_bytes = beacon_size;
+
+ /* First 5 fields are bssid, RSSI(for legacy scan only),
+ * time stamp, beacon interval, and capability information
+ */
+ if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) +
+ sizeof(struct nxpwifi_fixed_bcn_param)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "InterpretIE: not enough bytes left\n");
+ return -EFAULT;
+ }
+
+ memcpy(bssid, current_ptr, ETH_ALEN);
+ current_ptr += ETH_ALEN;
+ curr_bcn_bytes -= ETH_ALEN;
+
+ if (!ext_scan) {
+ rssi = (s32)*current_ptr;
+ rssi = (-rssi) * 100; /* Convert dBm to mBm */
+ current_ptr += sizeof(u8);
+ curr_bcn_bytes -= sizeof(u8);
+ nxpwifi_dbg(adapter, INFO,
+ "info: InterpretIE: RSSI=%d\n", rssi);
+ } else {
+ rssi = rssi_val;
+ }
+
+ bcn_param = (struct nxpwifi_fixed_bcn_param *)current_ptr;
+ current_ptr += sizeof(*bcn_param);
+ curr_bcn_bytes -= sizeof(*bcn_param);
+
+ timestamp = le64_to_cpu(bcn_param->timestamp);
+ beacon_period = le16_to_cpu(bcn_param->beacon_period);
+
+ cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap);
+ nxpwifi_dbg(adapter, INFO,
+ "info: InterpretIE: capabilities=0x%X\n",
+ cap_info_bitmap);
+
+ /* Rest of the current buffer are IE's */
+ ie_buf = current_ptr;
+ ie_len = curr_bcn_bytes;
+ nxpwifi_dbg(adapter, INFO,
+ "info: InterpretIE: IELength for this AP = %d\n",
+ curr_bcn_bytes);
+
+ while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) {
+ u8 element_id, element_len;
+
+ element_id = *current_ptr;
+ element_len = *(current_ptr + 1);
+ if (curr_bcn_bytes < element_len +
+ sizeof(struct ieee_types_header)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: bytes left < IE length\n", __func__);
+ return -EFAULT;
+ }
+ if (element_id == WLAN_EID_DS_PARAMS) {
+ channel = *(current_ptr +
+ sizeof(struct ieee_types_header));
+ break;
+ }
+
+ current_ptr += element_len + sizeof(struct ieee_types_header);
+ curr_bcn_bytes -= element_len +
+ sizeof(struct ieee_types_header);
+ }
+
+ if (channel) {
+ struct ieee80211_channel *chan;
+ struct nxpwifi_bssdescriptor *bss_desc;
+ u8 band;
+
+ /* Skip entry if on csa closed channel */
+ if (channel == priv->csa_chan) {
+ nxpwifi_dbg(adapter, WARN,
+ "Dropping entry on csa closed channel\n");
+ return 0;
+ }
+
+ band = BAND_G;
+ if (radio_type)
+ band = nxpwifi_radio_type_to_band(*radio_type &
+ (BIT(0) | BIT(1)));
+
+ cfp = nxpwifi_get_cfp(priv, band, channel, 0);
+
+ freq = cfp ? cfp->freq : 0;
+
+ chan = ieee80211_get_channel(priv->wdev.wiphy, freq);
+
+ if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
+ bss = cfg80211_inform_bss(priv->wdev.wiphy, chan,
+ CFG80211_BSS_FTYPE_UNKNOWN,
+ bssid, timestamp,
+ cap_info_bitmap,
+ beacon_period,
+ ie_buf, ie_len, rssi,
+ GFP_ATOMIC);
+ if (bss) {
+ bss_priv = (struct nxpwifi_bss_priv *)bss->priv;
+ bss_priv->band = band;
+ bss_priv->fw_tsf = fw_tsf;
+ bss_desc =
+ &priv->curr_bss_params.bss_descriptor;
+ if (priv->media_connected &&
+ !memcmp(bssid, bss_desc->mac_address,
+ ETH_ALEN))
+ nxpwifi_update_curr_bss_params(priv,
+ bss);
+
+ if ((chan->flags & IEEE80211_CHAN_RADAR) ||
+ (chan->flags & IEEE80211_CHAN_NO_IR)) {
+ nxpwifi_dbg(adapter, INFO,
+ "radar or passive channel %d\n",
+ channel);
+ nxpwifi_save_hidden_ssid_channels(priv,
+ bss);
+ }
+
+ cfg80211_put_bss(priv->wdev.wiphy, bss);
+ }
+ }
+ } else {
+ nxpwifi_dbg(adapter, WARN, "missing BSS channel IE\n");
+ }
+
+ return 0;
+}
+
+static void nxpwifi_complete_scan(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ adapter->survey_idx = 0;
+ if (adapter->curr_cmd->wait_q_enabled) {
+ adapter->cmd_wait_q.status = 0;
+ if (!priv->scan_request) {
+ nxpwifi_dbg(adapter, INFO,
+ "complete internal scan\n");
+ nxpwifi_complete_cmd(adapter, adapter->curr_cmd);
+ }
+ }
+}
+
+/* This function checks if any hidden SSID found in passive scan channels
+ * and do specific SSID active scan for those channels
+ */
+static int
+nxpwifi_active_scan_req_for_passive_chan(struct nxpwifi_private *priv)
+{
+ int ret;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u8 id = 0;
+ struct nxpwifi_user_scan_cfg *user_scan_cfg;
+
+ if (adapter->active_scan_triggered || !priv->scan_request ||
+ priv->scan_aborting) {
+ adapter->active_scan_triggered = false;
+ return 0;
+ }
+
+ if (!priv->hidden_chan[0].chan_number) {
+ nxpwifi_dbg(adapter, INFO, "No BSS with hidden SSID found on DFS channels\n");
+ return 0;
+ }
+ user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL);
+
+ if (!user_scan_cfg)
+ return -ENOMEM;
+
+ for (id = 0; id < NXPWIFI_USER_SCAN_CHAN_MAX; id++) {
+ if (!priv->hidden_chan[id].chan_number)
+ break;
+ memcpy(&user_scan_cfg->chan_list[id],
+ &priv->hidden_chan[id],
+ sizeof(struct nxpwifi_user_scan_chan));
+ }
+
+ adapter->active_scan_triggered = true;
+ if (priv->scan_request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
+ ether_addr_copy(user_scan_cfg->random_mac,
+ priv->scan_request->mac_addr);
+ user_scan_cfg->num_ssids = priv->scan_request->n_ssids;
+ user_scan_cfg->ssid_list = priv->scan_request->ssids;
+
+ ret = nxpwifi_scan_networks(priv, user_scan_cfg);
+ kfree(user_scan_cfg);
+
+ memset(&priv->hidden_chan, 0, sizeof(priv->hidden_chan));
+
+ if (ret) {
+ dev_err(priv->adapter->dev, "scan failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void nxpwifi_check_next_scan_command(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct cmd_ctrl_node *cmd_node;
+
+ spin_lock_bh(&adapter->scan_pending_q_lock);
+ if (list_empty(&adapter->scan_pending_q)) {
+ spin_unlock_bh(&adapter->scan_pending_q_lock);
+
+ spin_lock_bh(&adapter->nxpwifi_cmd_lock);
+ adapter->scan_processing = false;
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+
+ nxpwifi_active_scan_req_for_passive_chan(priv);
+
+ if (!adapter->ext_scan)
+ nxpwifi_complete_scan(priv);
+
+ if (priv->scan_request) {
+ struct cfg80211_scan_info info = {
+ .aborted = false,
+ };
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: notifying scan done\n");
+ cfg80211_scan_done(priv->scan_request, &info);
+ priv->scan_request = NULL;
+ priv->scan_aborting = false;
+ } else {
+ priv->scan_aborting = false;
+ nxpwifi_dbg(adapter, INFO,
+ "info: scan already aborted\n");
+ }
+ } else if ((priv->scan_aborting && !priv->scan_request) ||
+ priv->scan_block) {
+ spin_unlock_bh(&adapter->scan_pending_q_lock);
+
+ nxpwifi_cancel_pending_scan_cmd(adapter);
+
+ spin_lock_bh(&adapter->nxpwifi_cmd_lock);
+ adapter->scan_processing = false;
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+
+ if (!adapter->active_scan_triggered) {
+ if (priv->scan_request) {
+ struct cfg80211_scan_info info = {
+ .aborted = true,
+ };
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: aborting scan\n");
+ cfg80211_scan_done(priv->scan_request, &info);
+ priv->scan_request = NULL;
+ priv->scan_aborting = false;
+ } else {
+ priv->scan_aborting = false;
+ nxpwifi_dbg(adapter, INFO,
+ "info: scan already aborted\n");
+ }
+ }
+ } else {
+ /* Get scan command from scan_pending_q and put to
+ * cmd_pending_q
+ */
+ cmd_node = list_first_entry(&adapter->scan_pending_q,
+ struct cmd_ctrl_node, list);
+ list_del(&cmd_node->list);
+ spin_unlock_bh(&adapter->scan_pending_q_lock);
+ nxpwifi_insert_cmd_to_pending_q(adapter, cmd_node);
+ }
+}
+
+void nxpwifi_cancel_scan(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_private *priv;
+ int i;
+
+ nxpwifi_cancel_pending_scan_cmd(adapter);
+
+ if (adapter->scan_processing) {
+ spin_lock_bh(&adapter->nxpwifi_cmd_lock);
+ adapter->scan_processing = false;
+ spin_unlock_bh(&adapter->nxpwifi_cmd_lock);
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (!priv)
+ continue;
+ if (priv->scan_request) {
+ struct cfg80211_scan_info info = {
+ .aborted = true,
+ };
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: aborting scan\n");
+ cfg80211_scan_done(priv->scan_request, &info);
+ priv->scan_request = NULL;
+ priv->scan_aborting = false;
+ }
+ }
+ }
+}
+
+/* This function handles the command response of scan.
+ *
+ * The response buffer for the scan command has the following
+ * memory layout:
+ *
+ * .-------------------------------------------------------------.
+ * | Header (4 * sizeof(t_u16)): Standard command response hdr |
+ * .-------------------------------------------------------------.
+ * | BufSize (t_u16) : sizeof the BSS Description data |
+ * .-------------------------------------------------------------.
+ * | NumOfSet (t_u8) : Number of BSS Descs returned |
+ * .-------------------------------------------------------------.
+ * | BSSDescription data (variable, size given in BufSize) |
+ * .-------------------------------------------------------------.
+ * | TLV data (variable, size calculated using Header->Size, |
+ * | BufSize and sizeof the fixed fields above) |
+ * .-------------------------------------------------------------.
+ */
+int nxpwifi_ret_802_11_scan(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ int ret = 0;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_802_11_scan_rsp *scan_rsp;
+ struct nxpwifi_ie_types_data *tlv_data;
+ struct nxpwifi_ie_types_tsf_timestamp *tsf_tlv;
+ u8 *bss_info;
+ u32 scan_resp_size;
+ u32 bytes_left;
+ u32 idx;
+ u32 tlv_buf_size;
+ struct nxpwifi_ie_types_chan_band_list_param_set *chan_band_tlv;
+ struct chan_band_param_set *chan_band;
+ u8 is_bgscan_resp;
+ __le64 fw_tsf = 0;
+ u8 *radio_type;
+ struct cfg80211_wowlan_nd_match *pmatch;
+ struct cfg80211_sched_scan_request *nd_config = NULL;
+
+ is_bgscan_resp = (le16_to_cpu(resp->command)
+ == HOST_CMD_802_11_BG_SCAN_QUERY);
+ if (is_bgscan_resp)
+ scan_rsp = &resp->params.bg_scan_query_resp.scan_resp;
+ else
+ scan_rsp = &resp->params.scan_resp;
+
+ if (scan_rsp->number_of_sets > NXPWIFI_MAX_AP) {
+ nxpwifi_dbg(adapter, ERROR,
+ "SCAN_RESP: too many AP returned (%d)\n",
+ scan_rsp->number_of_sets);
+ ret = -1;
+ goto check_next_scan;
+ }
+
+ /* Check csa channel expiry before parsing scan response */
+ nxpwifi_11h_get_csa_closed_channel(priv);
+
+ bytes_left = le16_to_cpu(scan_rsp->bss_descript_size);
+ nxpwifi_dbg(adapter, INFO,
+ "info: SCAN_RESP: bss_descript_size %d\n",
+ bytes_left);
+
+ scan_resp_size = le16_to_cpu(resp->size);
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: SCAN_RESP: returned %d APs before parsing\n",
+ scan_rsp->number_of_sets);
+
+ bss_info = scan_rsp->bss_desc_and_tlv_buffer;
+
+ /* The size of the TLV buffer is equal to the entire command response
+ * size (scan_resp_size) minus the fixed fields (sizeof()'s), the
+ * BSS Descriptions (bss_descript_size as bytesLef) and the command
+ * response header (S_DS_GEN)
+ */
+ tlv_buf_size = scan_resp_size - (bytes_left
+ + sizeof(scan_rsp->bss_descript_size)
+ + sizeof(scan_rsp->number_of_sets)
+ + S_DS_GEN);
+
+ tlv_data = (struct nxpwifi_ie_types_data *)
+ (scan_rsp->bss_desc_and_tlv_buffer + bytes_left);
+
+ /* Search the TLV buffer space in the scan response for any valid
+ * TLVs
+ */
+ nxpwifi_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size,
+ TLV_TYPE_TSFTIMESTAMP,
+ (struct nxpwifi_ie_types_data **)
+ &tsf_tlv);
+
+ /* Search the TLV buffer space in the scan response for any valid
+ * TLVs
+ */
+ nxpwifi_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size,
+ TLV_TYPE_CHANNELBANDLIST,
+ (struct nxpwifi_ie_types_data **)
+ &chan_band_tlv);
+
+#ifdef CONFIG_PM
+ if (priv->wdev.wiphy->wowlan_config)
+ nd_config = priv->wdev.wiphy->wowlan_config->nd_config;
+#endif
+
+ if (nd_config) {
+ adapter->nd_info =
+ kzalloc(struct_size(adapter->nd_info, matches,
+ scan_rsp->number_of_sets),
+ GFP_ATOMIC);
+
+ if (adapter->nd_info)
+ adapter->nd_info->n_matches = scan_rsp->number_of_sets;
+ }
+
+ for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) {
+ /* If the TSF TLV was appended to the scan results, save this
+ * entry's TSF value in the fw_tsf field. It is the firmware's
+ * TSF value at the time the beacon or probe response was
+ * received.
+ */
+ if (tsf_tlv)
+ memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE],
+ sizeof(fw_tsf));
+
+ if (chan_band_tlv) {
+ chan_band = &chan_band_tlv->chan_band_param[idx];
+ radio_type = &chan_band->radio_type;
+ } else {
+ radio_type = NULL;
+ }
+
+ if (chan_band_tlv && adapter->nd_info) {
+ adapter->nd_info->matches[idx] =
+ kzalloc(sizeof(*pmatch) + sizeof(u32),
+ GFP_ATOMIC);
+
+ pmatch = adapter->nd_info->matches[idx];
+
+ if (pmatch) {
+ pmatch->n_channels = 1;
+ pmatch->channels[0] = chan_band->chan_number;
+ }
+ }
+
+ ret = nxpwifi_parse_single_response_buf(priv, &bss_info,
+ &bytes_left,
+ le64_to_cpu(fw_tsf),
+ radio_type, false, 0);
+ if (ret)
+ goto check_next_scan;
+ }
+
+check_next_scan:
+ nxpwifi_check_next_scan_command(priv);
+ return ret;
+}
+
+/* This function prepares an extended scan command to be sent to the firmware
+ *
+ * This uses the scan command configuration sent to the command processing
+ * module in command preparation stage to configure a extended scan command
+ * structure to send to firmware.
+ */
+int nxpwifi_cmd_802_11_scan_ext(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ void *data_buf)
+{
+ struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan;
+ struct nxpwifi_scan_cmd_config *scan_cfg = data_buf;
+
+ memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len);
+
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_SCAN_EXT);
+
+ /* Size is equal to the sizeof(fixed portions) + the TLV len + header */
+ cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved)
+ + scan_cfg->tlv_buf_len + S_DS_GEN));
+
+ return 0;
+}
+
+/* This function prepares an background scan config command to be sent
+ * to the firmware
+ */
+int nxpwifi_cmd_802_11_bg_scan_config(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ void *data_buf)
+{
+ struct host_cmd_ds_802_11_bg_scan_config *bgscan_config =
+ &cmd->params.bg_scan_config;
+ struct nxpwifi_bg_scan_cfg *bgscan_cfg_in = data_buf;
+ u8 *tlv_pos = bgscan_config->tlv;
+ u8 num_probes;
+ u32 ssid_len, chan_idx, scan_time, scan_type, scan_dur, chan_num;
+ int i;
+ struct nxpwifi_ie_types_num_probes *num_probes_tlv;
+ struct nxpwifi_ie_types_repeat_count *repeat_count_tlv;
+ struct nxpwifi_ie_types_min_rssi_threshold *rssi_threshold_tlv;
+ struct nxpwifi_ie_types_bgscan_start_later *start_later_tlv;
+ struct nxpwifi_ie_types_wildcard_ssid_params *wildcard_ssid_tlv;
+ struct nxpwifi_ie_types_chan_list_param_set *tlv_l;
+ struct nxpwifi_chan_scan_param_set *temp_chan;
+
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_BG_SCAN_CONFIG);
+ cmd->size = cpu_to_le16(sizeof(*bgscan_config) + S_DS_GEN);
+
+ bgscan_config->action = cpu_to_le16(bgscan_cfg_in->action);
+ bgscan_config->enable = bgscan_cfg_in->enable;
+ bgscan_config->bss_type = bgscan_cfg_in->bss_type;
+ bgscan_config->scan_interval =
+ cpu_to_le32(bgscan_cfg_in->scan_interval);
+ bgscan_config->report_condition =
+ cpu_to_le32(bgscan_cfg_in->report_condition);
+
+ /* stop sched scan */
+ if (!bgscan_config->enable)
+ return 0;
+
+ bgscan_config->chan_per_scan = bgscan_cfg_in->chan_per_scan;
+
+ num_probes = (bgscan_cfg_in->num_probes ?
+ bgscan_cfg_in->num_probes : priv->adapter->scan_probes);
+
+ if (num_probes) {
+ num_probes_tlv = (struct nxpwifi_ie_types_num_probes *)tlv_pos;
+ num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES);
+ num_probes_tlv->header.len =
+ cpu_to_le16(sizeof(num_probes_tlv->num_probes));
+ num_probes_tlv->num_probes = cpu_to_le16((u16)num_probes);
+
+ tlv_pos += sizeof(num_probes_tlv->header) +
+ le16_to_cpu(num_probes_tlv->header.len);
+ }
+
+ if (bgscan_cfg_in->repeat_count) {
+ repeat_count_tlv =
+ (struct nxpwifi_ie_types_repeat_count *)tlv_pos;
+ repeat_count_tlv->header.type =
+ cpu_to_le16(TLV_TYPE_REPEAT_COUNT);
+ repeat_count_tlv->header.len =
+ cpu_to_le16(sizeof(repeat_count_tlv->repeat_count));
+ repeat_count_tlv->repeat_count =
+ cpu_to_le16(bgscan_cfg_in->repeat_count);
+
+ tlv_pos += sizeof(repeat_count_tlv->header) +
+ le16_to_cpu(repeat_count_tlv->header.len);
+ }
+
+ if (bgscan_cfg_in->rssi_threshold) {
+ rssi_threshold_tlv =
+ (struct nxpwifi_ie_types_min_rssi_threshold *)tlv_pos;
+ rssi_threshold_tlv->header.type =
+ cpu_to_le16(TLV_TYPE_RSSI_LOW);
+ rssi_threshold_tlv->header.len =
+ cpu_to_le16(sizeof(rssi_threshold_tlv->rssi_threshold));
+ rssi_threshold_tlv->rssi_threshold =
+ cpu_to_le16(bgscan_cfg_in->rssi_threshold);
+
+ tlv_pos += sizeof(rssi_threshold_tlv->header) +
+ le16_to_cpu(rssi_threshold_tlv->header.len);
+ }
+
+ for (i = 0; i < bgscan_cfg_in->num_ssids; i++) {
+ ssid_len = bgscan_cfg_in->ssid_list[i].ssid.ssid_len;
+
+ wildcard_ssid_tlv =
+ (struct nxpwifi_ie_types_wildcard_ssid_params *)tlv_pos;
+ wildcard_ssid_tlv->header.type =
+ cpu_to_le16(TLV_TYPE_WILDCARDSSID);
+ wildcard_ssid_tlv->header.len =
+ cpu_to_le16((u16)(ssid_len + sizeof(u8)));
+
+ /* max_ssid_length = 0 tells firmware to perform
+ * specific scan for the SSID filled, whereas
+ * max_ssid_length = IEEE80211_MAX_SSID_LEN is for
+ * wildcard scan.
+ */
+ if (ssid_len)
+ wildcard_ssid_tlv->max_ssid_length = 0;
+ else
+ wildcard_ssid_tlv->max_ssid_length =
+ IEEE80211_MAX_SSID_LEN;
+
+ memcpy(wildcard_ssid_tlv->ssid,
+ bgscan_cfg_in->ssid_list[i].ssid.ssid, ssid_len);
+
+ tlv_pos += (sizeof(wildcard_ssid_tlv->header)
+ + le16_to_cpu(wildcard_ssid_tlv->header.len));
+ }
+
+ tlv_l = (struct nxpwifi_ie_types_chan_list_param_set *)tlv_pos;
+
+ if (bgscan_cfg_in->chan_list[0].chan_number) {
+ dev_dbg(priv->adapter->dev, "info: bgscan: Using supplied channel list\n");
+
+ tlv_l->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
+
+ for (chan_idx = 0;
+ chan_idx < NXPWIFI_BG_SCAN_CHAN_MAX &&
+ bgscan_cfg_in->chan_list[chan_idx].chan_number;
+ chan_idx++) {
+ temp_chan = &tlv_l->chan_scan_param[chan_idx];
+
+ /* Increment the TLV header length by size appended */
+ le16_unaligned_add_cpu(&tlv_l->header.len,
+ sizeof(*tlv_l->chan_scan_param));
+
+ temp_chan->chan_number =
+ bgscan_cfg_in->chan_list[chan_idx].chan_number;
+ temp_chan->radio_type =
+ bgscan_cfg_in->chan_list[chan_idx].radio_type;
+
+ scan_type =
+ bgscan_cfg_in->chan_list[chan_idx].scan_type;
+
+ if (scan_type == NXPWIFI_SCAN_TYPE_PASSIVE)
+ temp_chan->chan_scan_mode_bmap |=
+ NXPWIFI_PASSIVE_SCAN;
+ else
+ temp_chan->chan_scan_mode_bmap &=
+ ~NXPWIFI_PASSIVE_SCAN;
+
+ scan_time = bgscan_cfg_in->chan_list[chan_idx].scan_time;
+
+ if (scan_time) {
+ scan_dur = (u16)scan_time;
+ } else {
+ scan_dur = (scan_type ==
+ NXPWIFI_SCAN_TYPE_PASSIVE) ?
+ priv->adapter->passive_scan_time :
+ priv->adapter->specific_scan_time;
+ }
+
+ temp_chan->min_scan_time = cpu_to_le16(scan_dur);
+ temp_chan->max_scan_time = cpu_to_le16(scan_dur);
+ }
+ } else {
+ dev_dbg(priv->adapter->dev,
+ "info: bgscan: Creating full region channel list\n");
+ chan_num =
+ nxpwifi_bgscan_create_channel_list
+ (priv, bgscan_cfg_in,
+ tlv_l->chan_scan_param);
+ le16_unaligned_add_cpu(&tlv_l->header.len,
+ chan_num *
+ sizeof(*tlv_l->chan_scan_param));
+ }
+
+ tlv_pos += (sizeof(tlv_l->header)
+ + le16_to_cpu(tlv_l->header.len));
+
+ if (bgscan_cfg_in->start_later) {
+ start_later_tlv =
+ (struct nxpwifi_ie_types_bgscan_start_later *)tlv_pos;
+ start_later_tlv->header.type =
+ cpu_to_le16(TLV_TYPE_BGSCAN_START_LATER);
+ start_later_tlv->header.len =
+ cpu_to_le16(sizeof(start_later_tlv->start_later));
+ start_later_tlv->start_later =
+ cpu_to_le16(bgscan_cfg_in->start_later);
+
+ tlv_pos += sizeof(start_later_tlv->header) +
+ le16_to_cpu(start_later_tlv->header.len);
+ }
+
+ /* Append vendor specific IE TLV */
+ nxpwifi_cmd_append_vsie_tlv(priv, NXPWIFI_VSIE_MASK_BGSCAN, &tlv_pos);
+
+ le16_unaligned_add_cpu(&cmd->size, tlv_pos - bgscan_config->tlv);
+
+ return 0;
+}
+
+int nxpwifi_stop_bg_scan(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_bg_scan_cfg *bgscan_cfg;
+
+ if (!priv->sched_scanning) {
+ dev_dbg(priv->adapter->dev, "bgscan already stopped!\n");
+ return 0;
+ }
+
+ bgscan_cfg = kzalloc(sizeof(*bgscan_cfg), GFP_KERNEL);
+ if (!bgscan_cfg)
+ return -ENOMEM;
+
+ bgscan_cfg->bss_type = NXPWIFI_BSS_MODE_INFRA;
+ bgscan_cfg->action = NXPWIFI_BGSCAN_ACT_SET;
+ bgscan_cfg->enable = false;
+
+ if (nxpwifi_send_cmd(priv, HOST_CMD_802_11_BG_SCAN_CONFIG,
+ HOST_ACT_GEN_SET, 0, bgscan_cfg, true)) {
+ kfree(bgscan_cfg);
+ return -EFAULT;
+ }
+
+ kfree(bgscan_cfg);
+ priv->sched_scanning = false;
+
+ return 0;
+}
+
+static void
+nxpwifi_update_chan_statistics(struct nxpwifi_private *priv,
+ struct nxpwifi_ietypes_chanstats *tlv_stat)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u8 i, num_chan;
+ struct nxpwifi_fw_chan_stats *fw_chan_stats;
+ struct nxpwifi_chan_stats chan_stats;
+
+ fw_chan_stats = (void *)((u8 *)tlv_stat +
+ sizeof(struct nxpwifi_ie_types_header));
+ num_chan = le16_to_cpu(tlv_stat->header.len) /
+ sizeof(struct nxpwifi_chan_stats);
+
+ for (i = 0 ; i < num_chan; i++) {
+ if (adapter->survey_idx >= adapter->num_in_chan_stats) {
+ nxpwifi_dbg(adapter, WARN,
+ "FW reported too many channel results (max %d)\n",
+ adapter->num_in_chan_stats);
+ return;
+ }
+ chan_stats.chan_num = fw_chan_stats->chan_num;
+ chan_stats.bandcfg = fw_chan_stats->bandcfg;
+ chan_stats.flags = fw_chan_stats->flags;
+ chan_stats.noise = fw_chan_stats->noise;
+ chan_stats.total_bss = le16_to_cpu(fw_chan_stats->total_bss);
+ chan_stats.cca_scan_dur =
+ le16_to_cpu(fw_chan_stats->cca_scan_dur);
+ chan_stats.cca_busy_dur =
+ le16_to_cpu(fw_chan_stats->cca_busy_dur);
+ nxpwifi_dbg(adapter, INFO,
+ "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n",
+ chan_stats.chan_num,
+ chan_stats.noise,
+ chan_stats.total_bss,
+ chan_stats.cca_scan_dur,
+ chan_stats.cca_busy_dur);
+ memcpy(&adapter->chan_stats[adapter->survey_idx++], &chan_stats,
+ sizeof(struct nxpwifi_chan_stats));
+ fw_chan_stats++;
+ }
+}
+
+/* This function handles the command response of extended scan */
+int nxpwifi_ret_802_11_scan_ext(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_802_11_scan_ext *ext_scan_resp;
+ struct nxpwifi_ie_types_header *tlv;
+ struct nxpwifi_ietypes_chanstats *tlv_stat;
+ u16 buf_left, type, len;
+
+ struct host_cmd_ds_command *cmd_ptr;
+ struct cmd_ctrl_node *cmd_node;
+ bool complete_scan = false;
+
+ nxpwifi_dbg(adapter, INFO, "info: EXT scan returns successfully\n");
+
+ ext_scan_resp = &resp->params.ext_scan;
+
+ tlv = (void *)ext_scan_resp->tlv_buffer;
+ buf_left = le16_to_cpu(resp->size) - (sizeof(*ext_scan_resp) + S_DS_GEN
+ - 1);
+
+ while (buf_left >= sizeof(struct nxpwifi_ie_types_header)) {
+ type = le16_to_cpu(tlv->type);
+ len = le16_to_cpu(tlv->len);
+
+ if (buf_left < (sizeof(struct nxpwifi_ie_types_header) + len)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "error processing scan response TLVs");
+ break;
+ }
+
+ switch (type) {
+ case TLV_TYPE_CHANNEL_STATS:
+ tlv_stat = (void *)tlv;
+ nxpwifi_update_chan_statistics(priv, tlv_stat);
+ break;
+ default:
+ break;
+ }
+
+ buf_left -= len + sizeof(struct nxpwifi_ie_types_header);
+ tlv = (void *)((u8 *)tlv + len +
+ sizeof(struct nxpwifi_ie_types_header));
+ }
+
+ spin_lock_bh(&adapter->cmd_pending_q_lock);
+ spin_lock_bh(&adapter->scan_pending_q_lock);
+ if (list_empty(&adapter->scan_pending_q)) {
+ complete_scan = true;
+ list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) {
+ cmd_ptr = (void *)cmd_node->cmd_skb->data;
+ if (le16_to_cpu(cmd_ptr->command) ==
+ HOST_CMD_802_11_SCAN_EXT) {
+ nxpwifi_dbg(adapter, INFO,
+ "Scan pending in command pending list");
+ complete_scan = false;
+ break;
+ }
+ }
+ }
+ spin_unlock_bh(&adapter->scan_pending_q_lock);
+ spin_unlock_bh(&adapter->cmd_pending_q_lock);
+
+ if (complete_scan)
+ nxpwifi_complete_scan(priv);
+
+ return 0;
+}
+
+/* This function This function handles the event extended scan report. It
+ * parses extended scan results and informs to cfg80211 stack.
+ */
+int nxpwifi_handle_event_ext_scan_report(struct nxpwifi_private *priv,
+ void *buf)
+{
+ int ret = 0;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u8 *bss_info;
+ u32 bytes_left, bytes_left_for_tlv, idx;
+ u16 type, len;
+ struct nxpwifi_ie_types_data *tlv;
+ struct nxpwifi_ie_types_bss_scan_rsp *scan_rsp_tlv;
+ struct nxpwifi_ie_types_bss_scan_info *scan_info_tlv;
+ u8 *radio_type;
+ u64 fw_tsf = 0;
+ s32 rssi = 0;
+ struct nxpwifi_event_scan_result *event_scan = buf;
+ u8 num_of_set = event_scan->num_of_set;
+ u8 *scan_resp = buf + sizeof(struct nxpwifi_event_scan_result);
+ u16 scan_resp_size = le16_to_cpu(event_scan->buf_size);
+
+ if (num_of_set > NXPWIFI_MAX_AP) {
+ nxpwifi_dbg(adapter, ERROR,
+ "EXT_SCAN: Invalid number of AP returned (%d)!!\n",
+ num_of_set);
+ ret = -1;
+ goto check_next_scan;
+ }
+
+ bytes_left = scan_resp_size;
+ nxpwifi_dbg(adapter, INFO,
+ "EXT_SCAN: size %d, returned %d APs...",
+ scan_resp_size, num_of_set);
+ nxpwifi_dbg_dump(adapter, CMD_D, "EXT_SCAN buffer:", buf,
+ scan_resp_size +
+ sizeof(struct nxpwifi_event_scan_result));
+
+ tlv = (struct nxpwifi_ie_types_data *)scan_resp;
+
+ for (idx = 0; idx < num_of_set && bytes_left; idx++) {
+ type = le16_to_cpu(tlv->header.type);
+ len = le16_to_cpu(tlv->header.len);
+ if (bytes_left < sizeof(struct nxpwifi_ie_types_header) + len) {
+ nxpwifi_dbg(adapter, ERROR,
+ "EXT_SCAN: Error bytes left < TLV length\n");
+ break;
+ }
+ scan_rsp_tlv = NULL;
+ scan_info_tlv = NULL;
+ bytes_left_for_tlv = bytes_left;
+
+ /* BSS response TLV with beacon or probe response buffer
+ * at the initial position of each descriptor
+ */
+ if (type != TLV_TYPE_BSS_SCAN_RSP)
+ break;
+
+ bss_info = (u8 *)tlv;
+ scan_rsp_tlv = (struct nxpwifi_ie_types_bss_scan_rsp *)tlv;
+ tlv = (struct nxpwifi_ie_types_data *)(tlv->data + len);
+ bytes_left_for_tlv -=
+ (len + sizeof(struct nxpwifi_ie_types_header));
+
+ while (bytes_left_for_tlv >=
+ sizeof(struct nxpwifi_ie_types_header) &&
+ le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) {
+ type = le16_to_cpu(tlv->header.type);
+ len = le16_to_cpu(tlv->header.len);
+ if (bytes_left_for_tlv <
+ sizeof(struct nxpwifi_ie_types_header) + len) {
+ nxpwifi_dbg(adapter, ERROR,
+ "EXT_SCAN: Error in processing TLV,\t"
+ "bytes left < TLV length\n");
+ scan_rsp_tlv = NULL;
+ bytes_left_for_tlv = 0;
+ continue;
+ }
+ switch (type) {
+ case TLV_TYPE_BSS_SCAN_INFO:
+ scan_info_tlv =
+ (struct nxpwifi_ie_types_bss_scan_info *)tlv;
+ if (len !=
+ sizeof(struct nxpwifi_ie_types_bss_scan_info) -
+ sizeof(struct nxpwifi_ie_types_header)) {
+ bytes_left_for_tlv = 0;
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+ tlv = (struct nxpwifi_ie_types_data *)(tlv->data + len);
+ bytes_left -=
+ (len + sizeof(struct nxpwifi_ie_types_header));
+ bytes_left_for_tlv -=
+ (len + sizeof(struct nxpwifi_ie_types_header));
+ }
+
+ if (!scan_rsp_tlv)
+ break;
+
+ /* Advance pointer to the beacon buffer length and
+ * update the bytes count so that the function
+ * wlan_interpret_bss_desc_with_ie() can handle the
+ * scan buffer withut any change
+ */
+ bss_info += sizeof(u16);
+ bytes_left -= sizeof(u16);
+
+ if (scan_info_tlv) {
+ rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi));
+ rssi *= 100; /* Convert dBm to mBm */
+ nxpwifi_dbg(adapter, INFO,
+ "info: InterpretIE: RSSI=%d\n", rssi);
+ fw_tsf = le64_to_cpu(scan_info_tlv->tsf);
+ radio_type = &scan_info_tlv->radio_type;
+ } else {
+ radio_type = NULL;
+ }
+ ret = nxpwifi_parse_single_response_buf(priv, &bss_info,
+ &bytes_left, fw_tsf,
+ radio_type, true, rssi);
+ if (ret)
+ goto check_next_scan;
+ }
+
+check_next_scan:
+ if (!event_scan->more_event)
+ nxpwifi_check_next_scan_command(priv);
+
+ return ret;
+}
+
+/* This function prepares command for background scan query.
+ *
+ * Preparation includes -
+ * - Setting command ID and proper size
+ * - Setting background scan flush parameter
+ * - Ensuring correct endian-ness
+ */
+int nxpwifi_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd)
+{
+ struct host_cmd_ds_802_11_bg_scan_query *bg_query =
+ &cmd->params.bg_scan_query;
+
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_BG_SCAN_QUERY);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query)
+ + S_DS_GEN);
+
+ bg_query->flush = 1;
+
+ return 0;
+}
+
+/* This function inserts scan command node to the scan pending queue.
+ */
+void
+nxpwifi_queue_scan_cmd(struct nxpwifi_private *priv,
+ struct cmd_ctrl_node *cmd_node)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ cmd_node->wait_q_enabled = true;
+ cmd_node->condition = &adapter->scan_wait_q_woken;
+ spin_lock_bh(&adapter->scan_pending_q_lock);
+ list_add_tail(&cmd_node->list, &adapter->scan_pending_q);
+ spin_unlock_bh(&adapter->scan_pending_q_lock);
+}
+
+/* This function sends a scan command for all available channels to the
+ * firmware, filtered on a specific SSID.
+ */
+static int nxpwifi_scan_specific_ssid(struct nxpwifi_private *priv,
+ struct cfg80211_ssid *req_ssid)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int ret;
+ struct nxpwifi_user_scan_cfg *scan_cfg;
+
+ if (adapter->scan_processing) {
+ nxpwifi_dbg(adapter, WARN,
+ "cmd: Scan already in process...\n");
+ return -EBUSY;
+ }
+
+ if (priv->scan_block) {
+ nxpwifi_dbg(adapter, WARN,
+ "cmd: Scan is blocked during association...\n");
+ return -EBUSY;
+ }
+
+ scan_cfg = kzalloc(sizeof(*scan_cfg), GFP_KERNEL);
+ if (!scan_cfg)
+ return -ENOMEM;
+
+ scan_cfg->ssid_list = req_ssid;
+ scan_cfg->num_ssids = 1;
+
+ ret = nxpwifi_scan_networks(priv, scan_cfg);
+
+ kfree(scan_cfg);
+ return ret;
+}
+
+/* Sends IOCTL request to start a scan.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ *
+ * Scan command can be issued for both normal scan and specific SSID
+ * scan, depending upon whether an SSID is provided or not.
+ */
+int nxpwifi_request_scan(struct nxpwifi_private *priv,
+ struct cfg80211_ssid *req_ssid)
+{
+ int ret;
+
+ if (mutex_lock_interruptible(&priv->async_mutex)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "%s: acquire semaphore fail\n",
+ __func__);
+ return -1;
+ }
+
+ priv->adapter->scan_wait_q_woken = false;
+
+ if (req_ssid && req_ssid->ssid_len != 0)
+ /* Specific SSID scan */
+ ret = nxpwifi_scan_specific_ssid(priv, req_ssid);
+ else
+ /* Normal scan */
+ ret = nxpwifi_scan_networks(priv, NULL);
+
+ mutex_unlock(&priv->async_mutex);
+
+ return ret;
+}
+
+/* This function appends the vendor specific IE TLV to a buffer.
+ */
+int
+nxpwifi_cmd_append_vsie_tlv(struct nxpwifi_private *priv,
+ u16 vsie_mask, u8 **buffer)
+{
+ int id, ret_len = 0;
+ struct nxpwifi_ie_types_vendor_param_set *vs_param_set;
+
+ if (!buffer)
+ return 0;
+ if (!(*buffer))
+ return 0;
+
+ /* Traverse through the saved vendor specific IE array and append
+ * the selected(scan/assoc) IE as TLV to the command
+ */
+ for (id = 0; id < NXPWIFI_MAX_VSIE_NUM; id++) {
+ if (priv->vs_ie[id].mask & vsie_mask) {
+ vs_param_set =
+ (struct nxpwifi_ie_types_vendor_param_set *)
+ *buffer;
+ vs_param_set->header.type =
+ cpu_to_le16(TLV_TYPE_PASSTHROUGH);
+ vs_param_set->header.len =
+ cpu_to_le16((((u16)priv->vs_ie[id].ie[1])
+ & 0x00FF) + 2);
+ if (le16_to_cpu(vs_param_set->header.len) >
+ NXPWIFI_MAX_VSIE_LEN) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Invalid param length!\n");
+ break;
+ }
+
+ memcpy(vs_param_set->ie, priv->vs_ie[id].ie,
+ le16_to_cpu(vs_param_set->header.len));
+ *buffer += le16_to_cpu(vs_param_set->header.len) +
+ sizeof(struct nxpwifi_ie_types_header);
+ ret_len += le16_to_cpu(vs_param_set->header.len) +
+ sizeof(struct nxpwifi_ie_types_header);
+ }
+ }
+ return ret_len;
+}
+
+/* This function saves a beacon buffer of the current BSS descriptor.
+ *
+ * The current beacon buffer is saved so that it can be restored in the
+ * following cases that makes the beacon buffer not to contain the current
+ * ssid's beacon buffer.
+ * - The current ssid was not found somehow in the last scan.
+ * - The current ssid was the last entry of the scan table and overloaded.
+ */
+void
+nxpwifi_save_curr_bcn(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_bssdescriptor *curr_bss =
+ &priv->curr_bss_params.bss_descriptor;
+
+ if (!curr_bss->beacon_buf_size)
+ return;
+
+ /* allocate beacon buffer at 1st time; or if it's size has changed */
+ if (!priv->curr_bcn_buf ||
+ priv->curr_bcn_size != curr_bss->beacon_buf_size) {
+ priv->curr_bcn_size = curr_bss->beacon_buf_size;
+
+ kfree(priv->curr_bcn_buf);
+ priv->curr_bcn_buf = kmalloc(curr_bss->beacon_buf_size,
+ GFP_ATOMIC);
+ if (!priv->curr_bcn_buf)
+ return;
+ }
+
+ memcpy(priv->curr_bcn_buf, curr_bss->beacon_buf,
+ curr_bss->beacon_buf_size);
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: current beacon saved %d\n",
+ priv->curr_bcn_size);
+
+ curr_bss->beacon_buf = priv->curr_bcn_buf;
+
+ /* adjust the pointers in the current BSS descriptor */
+ if (curr_bss->bcn_wpa_ie)
+ curr_bss->bcn_wpa_ie =
+ (struct ieee_types_vendor_specific *)
+ (curr_bss->beacon_buf +
+ curr_bss->wpa_offset);
+
+ if (curr_bss->bcn_rsn_ie)
+ curr_bss->bcn_rsn_ie = (struct ieee_types_generic *)
+ (curr_bss->beacon_buf +
+ curr_bss->rsn_offset);
+
+ if (curr_bss->bcn_ht_cap)
+ curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *)
+ (curr_bss->beacon_buf +
+ curr_bss->ht_cap_offset);
+
+ if (curr_bss->bcn_ht_oper)
+ curr_bss->bcn_ht_oper = (struct ieee80211_ht_operation *)
+ (curr_bss->beacon_buf +
+ curr_bss->ht_info_offset);
+
+ if (curr_bss->bcn_vht_cap)
+ curr_bss->bcn_vht_cap = (void *)(curr_bss->beacon_buf +
+ curr_bss->vht_cap_offset);
+
+ if (curr_bss->bcn_vht_oper)
+ curr_bss->bcn_vht_oper = (void *)(curr_bss->beacon_buf +
+ curr_bss->vht_info_offset);
+
+ if (curr_bss->bcn_bss_co_2040)
+ curr_bss->bcn_bss_co_2040 =
+ (curr_bss->beacon_buf + curr_bss->bss_co_2040_offset);
+
+ if (curr_bss->bcn_ext_cap)
+ curr_bss->bcn_ext_cap = curr_bss->beacon_buf +
+ curr_bss->ext_cap_offset;
+
+ if (curr_bss->oper_mode)
+ curr_bss->oper_mode = (void *)(curr_bss->beacon_buf +
+ curr_bss->oper_mode_offset);
+}
+
+/* This function frees the current BSS descriptor beacon buffer.
+ */
+void
+nxpwifi_free_curr_bcn(struct nxpwifi_private *priv)
+{
+ kfree(priv->curr_bcn_buf);
+ priv->curr_bcn_buf = NULL;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 26/43] wifi: nxpwifi: add sdio.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (24 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 25/43] wifi: nxpwifi: add scan.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-26 11:40 ` [EXTERNAL] " Nemanov, Michael
2024-06-21 7:51 ` [PATCH 27/43] wifi: nxpwifi: add sdio.h David Lin
` (18 subsequent siblings)
44 siblings, 1 reply; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/sdio.c | 2646 +++++++++++++++++++++++
1 file changed, 2646 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/sdio.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/sdio.c b/drivers/net/wireless/nxp/nxpwifi/sdio.c
new file mode 100644
index 000000000000..962c9b2f7db2
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/sdio.c
@@ -0,0 +1,2646 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: SDIO specific handling
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include <linux/firmware.h>
+#include <linux/completion.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+#include "sdio.h"
+
+#define SDIO_VERSION "1.0"
+
+static void nxpwifi_sdio_work(struct work_struct *work);
+
+static struct nxpwifi_if_ops sdio_ops;
+
+static const struct nxpwifi_sdio_card_reg nxpwifi_reg_iw61x = {
+ .start_rd_port = 0,
+ .start_wr_port = 0,
+ .base_0_reg = 0xF8,
+ .base_1_reg = 0xF9,
+ .poll_reg = 0x5C,
+ .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK |
+ CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK,
+ .host_int_rsr_reg = 0x4,
+ .host_int_status_reg = 0x0C,
+ .host_int_mask_reg = 0x08,
+ .host_strap_reg = 0xF4,
+ .host_strap_mask = 0x01,
+ .host_strap_value = 0x00,
+ .status_reg_0 = 0xE8,
+ .status_reg_1 = 0xE9,
+ .sdio_int_mask = 0xff,
+ .data_port_mask = 0xffffffff,
+ .io_port_0_reg = 0xE4,
+ .io_port_1_reg = 0xE5,
+ .io_port_2_reg = 0xE6,
+ .max_mp_regs = 196,
+ .rd_bitmap_l = 0x10,
+ .rd_bitmap_u = 0x11,
+ .rd_bitmap_1l = 0x12,
+ .rd_bitmap_1u = 0x13,
+ .wr_bitmap_l = 0x14,
+ .wr_bitmap_u = 0x15,
+ .wr_bitmap_1l = 0x16,
+ .wr_bitmap_1u = 0x17,
+ .rd_len_p0_l = 0x18,
+ .rd_len_p0_u = 0x19,
+ .card_misc_cfg_reg = 0xd8,
+ .card_cfg_2_1_reg = 0xd9,
+ .cmd_rd_len_0 = 0xc0,
+ .cmd_rd_len_1 = 0xc1,
+ .cmd_rd_len_2 = 0xc2,
+ .cmd_rd_len_3 = 0xc3,
+ .cmd_cfg_0 = 0xc4,
+ .cmd_cfg_1 = 0xc5,
+ .cmd_cfg_2 = 0xc6,
+ .cmd_cfg_3 = 0xc7,
+ .fw_dump_host_ready = 0xcc,
+ .fw_dump_ctrl = 0xf9,
+ .fw_dump_start = 0xf1,
+ .fw_dump_end = 0xf8,
+ .func1_dump_reg_start = 0x10,
+ .func1_dump_reg_end = 0x17,
+ .func1_scratch_reg = 0xE8,
+ .func1_spec_reg_num = 13,
+ .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60,
+ 0x61, 0x62, 0x64, 0x65, 0x66,
+ 0x68, 0x69, 0x6a},
+};
+
+static const struct nxpwifi_sdio_device nxpwifi_sdio_iw61x = {
+ .firmware = IW61X_SDIO_FW_NAME,
+ .reg = &nxpwifi_reg_iw61x,
+ .max_ports = 32,
+ .mp_agg_pkt_limit = 16,
+ .tx_buf_size = NXPWIFI_TX_DATA_BUF_SIZE_4K,
+ .mp_tx_agg_buf_size = NXPWIFI_MP_AGGR_BSIZE_MAX,
+ .mp_rx_agg_buf_size = NXPWIFI_MP_AGGR_BSIZE_MAX,
+ .can_dump_fw = true,
+ .fw_dump_enh = true,
+ .can_ext_scan = true,
+};
+
+static struct memory_type_mapping generic_mem_type_map[] = {
+ {"DUMP", NULL, 0, 0xDD},
+};
+
+static struct memory_type_mapping mem_type_mapping_tbl[] = {
+ {"ITCM", NULL, 0, 0xF0},
+ {"DTCM", NULL, 0, 0xF1},
+ {"SQRAM", NULL, 0, 0xF2},
+ {"APU", NULL, 0, 0xF3},
+ {"CIU", NULL, 0, 0xF4},
+ {"ICU", NULL, 0, 0xF5},
+ {"MAC", NULL, 0, 0xF6},
+ {"EXT7", NULL, 0, 0xF7},
+ {"EXT8", NULL, 0, 0xF8},
+ {"EXT9", NULL, 0, 0xF9},
+ {"EXT10", NULL, 0, 0xFA},
+ {"EXT11", NULL, 0, 0xFB},
+ {"EXT12", NULL, 0, 0xFC},
+ {"EXT13", NULL, 0, 0xFD},
+ {"EXTLAST", NULL, 0, 0xFE},
+};
+
+static const struct of_device_id nxpwifi_sdio_of_match_table[] __maybe_unused = {
+ { }
+};
+
+/* This function parse device tree node using mmc subnode devicetree API.
+ * The device node is saved in card->plt_of_node.
+ * if the device tree node exist and include interrupts attributes, this
+ * function will also request platform specific wakeup interrupt.
+ */
+static int nxpwifi_sdio_probe_of(struct device *dev)
+{
+ if (!of_match_node(nxpwifi_sdio_of_match_table, dev->of_node)) {
+ dev_err(dev, "required compatible string missing\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* SDIO probe.
+ *
+ * This function probes an nxpwifi device and registers it. It allocates
+ * the card structure, enables SDIO function number and initiates the
+ * device registration and initialization procedure by adding a logical
+ * interface.
+ */
+static int
+nxpwifi_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
+{
+ int ret;
+ struct sdio_mmc_card *card = NULL;
+
+ pr_debug("info: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n",
+ func->vendor, func->device, func->class, func->num);
+
+ card = devm_kzalloc(&func->dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ init_completion(&card->fw_done);
+
+ card->func = func;
+
+ func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
+
+ if (id->driver_data) {
+ struct nxpwifi_sdio_device *data = (void *)id->driver_data;
+
+ card->firmware = data->firmware;
+ card->firmware_sdiouart = data->firmware_sdiouart;
+ card->reg = data->reg;
+ card->max_ports = data->max_ports;
+ card->mp_agg_pkt_limit = data->mp_agg_pkt_limit;
+ card->tx_buf_size = data->tx_buf_size;
+ card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size;
+ card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size;
+ card->can_dump_fw = data->can_dump_fw;
+ card->fw_dump_enh = data->fw_dump_enh;
+ card->can_ext_scan = data->can_ext_scan;
+ INIT_WORK(&card->work, nxpwifi_sdio_work);
+ }
+
+ sdio_claim_host(func);
+ ret = sdio_enable_func(func);
+ sdio_release_host(func);
+
+ if (ret) {
+ dev_err(&func->dev, "failed to enable function\n");
+ return ret;
+ }
+
+ /* device tree node parsing and platform specific configuration*/
+ if (func->dev.of_node) {
+ ret = nxpwifi_sdio_probe_of(&func->dev);
+ if (ret)
+ goto err_disable;
+ }
+
+ ret = nxpwifi_add_card(card, &card->fw_done, &sdio_ops,
+ NXPWIFI_SDIO, &func->dev);
+ if (ret) {
+ dev_err(&func->dev, "add card failed\n");
+ goto err_disable;
+ }
+
+ return 0;
+
+err_disable:
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+
+ return ret;
+}
+
+/* SDIO resume.
+ *
+ * Kernel needs to suspend all functions separately. Therefore all
+ * registered functions must have drivers with suspend and resume
+ * methods. Failing that the kernel simply removes the whole card.
+ *
+ * If already not resumed, this function turns on the traffic and
+ * sends a host sleep cancel request to the firmware.
+ */
+static int nxpwifi_sdio_resume(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct sdio_mmc_card *card;
+ struct nxpwifi_adapter *adapter;
+
+ card = sdio_get_drvdata(func);
+ if (!card || !card->adapter) {
+ dev_err(dev, "resume: invalid card or adapter\n");
+ return 0;
+ }
+
+ adapter = card->adapter;
+
+ if (!test_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags)) {
+ nxpwifi_dbg(adapter, WARN,
+ "device already resumed\n");
+ return 0;
+ }
+
+ clear_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags);
+
+ /* Disable Host Sleep */
+ nxpwifi_cancel_hs(nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA),
+ NXPWIFI_SYNC_CMD);
+
+ nxpwifi_disable_wake(adapter);
+
+ return 0;
+}
+
+/* Write data into SDIO card register. Caller claims SDIO device. */
+static int
+nxpwifi_write_reg_locked(struct sdio_func *func, u32 reg, u8 data)
+{
+ int ret = -1;
+
+ sdio_writeb(func, data, reg, &ret);
+ return ret;
+}
+
+/* This function writes data into SDIO card register.
+ */
+static int
+nxpwifi_write_reg(struct nxpwifi_adapter *adapter, u32 reg, u8 data)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret;
+
+ sdio_claim_host(card->func);
+ ret = nxpwifi_write_reg_locked(card->func, reg, data);
+ sdio_release_host(card->func);
+
+ return ret;
+}
+
+/* This function reads data from SDIO card register.
+ */
+static int
+nxpwifi_read_reg(struct nxpwifi_adapter *adapter, u32 reg, u8 *data)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret = -1;
+ u8 val;
+
+ sdio_claim_host(card->func);
+ val = sdio_readb(card->func, reg, &ret);
+ sdio_release_host(card->func);
+
+ *data = val;
+
+ return ret;
+}
+
+/* This function writes multiple data into SDIO card memory.
+ *
+ * This does not work in suspended mode.
+ */
+static int
+nxpwifi_write_data_sync(struct nxpwifi_adapter *adapter,
+ u8 *buffer, u32 pkt_len, u32 port)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret;
+ u8 blk_mode =
+ (port & NXPWIFI_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE;
+ u32 blk_size = (blk_mode == BLOCK_MODE) ? NXPWIFI_SDIO_BLOCK_SIZE : 1;
+ u32 blk_cnt =
+ (blk_mode ==
+ BLOCK_MODE) ? (pkt_len /
+ NXPWIFI_SDIO_BLOCK_SIZE) : pkt_len;
+ u32 ioport = (port & NXPWIFI_SDIO_IO_PORT_MASK);
+
+ if (test_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: not allowed while suspended\n", __func__);
+ return -1;
+ }
+
+ sdio_claim_host(card->func);
+
+ ret = sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size);
+
+ sdio_release_host(card->func);
+
+ return ret;
+}
+
+/* This function reads multiple data from SDIO card memory.
+ */
+static int nxpwifi_read_data_sync(struct nxpwifi_adapter *adapter, u8 *buffer,
+ u32 len, u32 port, u8 claim)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret;
+ u8 blk_mode = (port & NXPWIFI_SDIO_BYTE_MODE_MASK) ? BYTE_MODE
+ : BLOCK_MODE;
+ u32 blk_size = (blk_mode == BLOCK_MODE) ? NXPWIFI_SDIO_BLOCK_SIZE : 1;
+ u32 blk_cnt = (blk_mode == BLOCK_MODE) ? (len / NXPWIFI_SDIO_BLOCK_SIZE)
+ : len;
+ u32 ioport = (port & NXPWIFI_SDIO_IO_PORT_MASK);
+
+ if (claim)
+ sdio_claim_host(card->func);
+
+ ret = sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size);
+
+ if (claim)
+ sdio_release_host(card->func);
+
+ return ret;
+}
+
+/* This function reads the firmware status.
+ */
+static int
+nxpwifi_sdio_read_fw_status(struct nxpwifi_adapter *adapter, u16 *dat)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct nxpwifi_sdio_card_reg *reg = card->reg;
+ u8 fws0, fws1;
+
+ if (nxpwifi_read_reg(adapter, reg->status_reg_0, &fws0))
+ return -1;
+
+ if (nxpwifi_read_reg(adapter, reg->status_reg_1, &fws1))
+ return -1;
+
+ *dat = (u16)((fws1 << 8) | fws0);
+ return 0;
+}
+
+/* This function checks the firmware status in card.
+ */
+static int nxpwifi_check_fw_status(struct nxpwifi_adapter *adapter,
+ u32 poll_num)
+{
+ int ret = 0;
+ u16 firmware_stat = 0;
+ u32 tries;
+
+ for (tries = 0; tries < poll_num; tries++) {
+ ret = nxpwifi_sdio_read_fw_status(adapter, &firmware_stat);
+ if (ret)
+ continue;
+ if (firmware_stat == FIRMWARE_READY_SDIO) {
+ ret = 0;
+ break;
+ }
+
+ msleep(100);
+ ret = -1;
+ }
+
+ if (firmware_stat == FIRMWARE_READY_SDIO)
+ /* firmware might pretend to be ready, when it's not.
+ * Wait a little bit more as a workaround.
+ */
+ msleep(100);
+
+ return ret;
+}
+
+/* This function checks if WLAN is the winner.
+ */
+static int nxpwifi_check_winner_status(struct nxpwifi_adapter *adapter)
+{
+ int ret = 0;
+ u8 winner = 0;
+ struct sdio_mmc_card *card = adapter->card;
+
+ if (nxpwifi_read_reg(adapter, card->reg->status_reg_0, &winner))
+ return -1;
+
+ if (winner)
+ adapter->winner = 0;
+ else
+ adapter->winner = 1;
+
+ return ret;
+}
+
+/* SDIO remove.
+ *
+ * This function removes the interface and frees up the card structure.
+ */
+static void
+nxpwifi_sdio_remove(struct sdio_func *func)
+{
+ struct sdio_mmc_card *card;
+ struct nxpwifi_adapter *adapter;
+ struct nxpwifi_private *priv;
+ int ret = 0;
+ u16 firmware_stat;
+
+ card = sdio_get_drvdata(func);
+ if (!card)
+ return;
+
+ wait_for_completion(&card->fw_done);
+
+ adapter = card->adapter;
+ if (!adapter || !adapter->priv_num)
+ return;
+
+ nxpwifi_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num);
+
+ ret = nxpwifi_sdio_read_fw_status(adapter, &firmware_stat);
+ if (!ret && firmware_stat == FIRMWARE_READY_SDIO) {
+ nxpwifi_deauthenticate_all(adapter);
+
+ priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
+ nxpwifi_disable_auto_ds(priv);
+ nxpwifi_init_shutdown_fw(priv, NXPWIFI_FUNC_SHUTDOWN);
+ }
+
+ nxpwifi_remove_card(adapter);
+}
+
+/* SDIO suspend.
+ *
+ * Kernel needs to suspend all functions separately. Therefore all
+ * registered functions must have drivers with suspend and resume
+ * methods. Failing that the kernel simply removes the whole card.
+ *
+ * If already not suspended, this function allocates and sends a host
+ * sleep activate request to the firmware and turns off the traffic.
+ */
+static int nxpwifi_sdio_suspend(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct sdio_mmc_card *card;
+ struct nxpwifi_adapter *adapter;
+ mmc_pm_flag_t pm_flag = 0;
+ int ret = 0;
+
+ pm_flag = sdio_get_host_pm_caps(func);
+ pr_debug("cmd: %s: suspend: PM flag = 0x%x\n",
+ sdio_func_id(func), pm_flag);
+ if (!(pm_flag & MMC_PM_KEEP_POWER)) {
+ dev_err(dev,
+ "%s: cannot remain alive while host is suspended\n",
+ sdio_func_id(func));
+ return -EPERM;
+ }
+
+ card = sdio_get_drvdata(func);
+ if (!card) {
+ dev_err(dev, "suspend: invalid card\n");
+ return 0;
+ }
+
+ /* Might still be loading firmware */
+ wait_for_completion(&card->fw_done);
+
+ adapter = card->adapter;
+ if (!adapter) {
+ dev_err(dev, "adapter is not valid\n");
+ return 0;
+ }
+
+ if (!adapter->is_up)
+ return -EBUSY;
+
+ nxpwifi_enable_wake(adapter);
+
+ /* Enable the Host Sleep */
+ if (!nxpwifi_enable_hs(adapter)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "cmd: failed to suspend\n");
+ clear_bit(NXPWIFI_IS_HS_ENABLING, &adapter->work_flags);
+ nxpwifi_disable_wake(adapter);
+ return -EFAULT;
+ }
+
+ nxpwifi_dbg(adapter, INFO,
+ "cmd: suspend with MMC_PM_KEEP_POWER\n");
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+
+ /* Indicate device suspended */
+ set_bit(NXPWIFI_IS_SUSPENDED, &adapter->work_flags);
+ clear_bit(NXPWIFI_IS_HS_ENABLING, &adapter->work_flags);
+
+ return ret;
+}
+
+static void nxpwifi_sdio_coredump(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct sdio_mmc_card *card;
+
+ card = sdio_get_drvdata(func);
+ if (!test_and_set_bit(NXPWIFI_IFACE_WORK_DEVICE_DUMP,
+ &card->work_flags))
+ schedule_work(&card->work);
+}
+
+/* WLAN IDs */
+static const struct sdio_device_id nxpwifi_ids[] = {
+ {SDIO_DEVICE(SDIO_VENDOR_ID_NXP, SDIO_DEVICE_ID_NXP_IW61X),
+ .driver_data = (unsigned long)&nxpwifi_sdio_iw61x},
+ {},
+};
+
+MODULE_DEVICE_TABLE(sdio, nxpwifi_ids);
+
+static const struct dev_pm_ops nxpwifi_sdio_pm_ops = {
+ .suspend = nxpwifi_sdio_suspend,
+ .resume = nxpwifi_sdio_resume,
+};
+
+static struct sdio_driver nxpwifi_sdio = {
+ .name = "nxpwifi_sdio",
+ .id_table = nxpwifi_ids,
+ .probe = nxpwifi_sdio_probe,
+ .remove = nxpwifi_sdio_remove,
+ .drv = {
+ .owner = THIS_MODULE,
+ .coredump = nxpwifi_sdio_coredump,
+ .pm = &nxpwifi_sdio_pm_ops,
+ }
+};
+
+/* This function wakes up the card.
+ *
+ * A host power up command is written to the card configuration
+ * register to wake up the card.
+ */
+static int nxpwifi_pm_wakeup_card(struct nxpwifi_adapter *adapter)
+{
+ nxpwifi_dbg(adapter, EVENT,
+ "event: wakeup device...\n");
+
+ return nxpwifi_write_reg(adapter, CONFIGURATION_REG, HOST_POWER_UP);
+}
+
+/* This function is called after the card has woken up.
+ *
+ * The card configuration register is reset.
+ */
+static int nxpwifi_pm_wakeup_card_complete(struct nxpwifi_adapter *adapter)
+{
+ nxpwifi_dbg(adapter, EVENT,
+ "cmd: wakeup device completed\n");
+
+ return nxpwifi_write_reg(adapter, CONFIGURATION_REG, 0);
+}
+
+static int nxpwifi_sdio_dnld_fw(struct nxpwifi_adapter *adapter,
+ struct nxpwifi_fw_image *fw)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret;
+
+ sdio_claim_host(card->func);
+ ret = nxpwifi_dnld_fw(adapter, fw);
+ sdio_release_host(card->func);
+
+ return ret;
+}
+
+/* This function is used to initialize IO ports for the
+ * chipsets supporting SDIO new mode.
+ */
+static int nxpwifi_init_sdio_new_mode(struct nxpwifi_adapter *adapter)
+{
+ u8 reg;
+ struct sdio_mmc_card *card = adapter->card;
+
+ adapter->ioport = MEM_PORT;
+
+ /* enable sdio new mode */
+ if (nxpwifi_read_reg(adapter, card->reg->card_cfg_2_1_reg, ®))
+ return -1;
+ if (nxpwifi_write_reg(adapter, card->reg->card_cfg_2_1_reg,
+ reg | CMD53_NEW_MODE))
+ return -1;
+
+ /* Configure cmd port and enable reading rx length from the register */
+ if (nxpwifi_read_reg(adapter, card->reg->cmd_cfg_0, ®))
+ return -1;
+ if (nxpwifi_write_reg(adapter, card->reg->cmd_cfg_0,
+ reg | CMD_PORT_RD_LEN_EN))
+ return -1;
+
+ /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is
+ * completed
+ */
+ if (nxpwifi_read_reg(adapter, card->reg->cmd_cfg_1, ®))
+ return -1;
+ if (nxpwifi_write_reg(adapter, card->reg->cmd_cfg_1,
+ reg | CMD_PORT_AUTO_EN))
+ return -1;
+
+ return 0;
+}
+
+/* This function initializes the IO ports.
+ *
+ * The following operations are performed -
+ * - Read the IO ports (0, 1 and 2)
+ * - Set host interrupt Reset-To-Read to clear
+ * - Set auto re-enable interrupt
+ */
+static int nxpwifi_init_sdio_ioport(struct nxpwifi_adapter *adapter)
+{
+ u8 reg;
+ struct sdio_mmc_card *card = adapter->card;
+
+ if (nxpwifi_init_sdio_new_mode(adapter))
+ return -1;
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: SDIO FUNC1 IO port: %#x\n", adapter->ioport);
+
+ /* Set Host interrupt reset to read to clear */
+ if (nxpwifi_read_reg(adapter, card->reg->host_int_rsr_reg, ®))
+ return -1;
+ if (nxpwifi_write_reg(adapter, card->reg->host_int_rsr_reg,
+ reg | card->reg->sdio_int_mask))
+ return -1;
+
+ /* Dnld/Upld ready set to auto reset */
+ if (nxpwifi_read_reg(adapter, card->reg->card_misc_cfg_reg, ®))
+ return -1;
+ if (nxpwifi_write_reg(adapter, card->reg->card_misc_cfg_reg,
+ reg | AUTO_RE_ENABLE_INT))
+ return -1;
+
+ return 0;
+}
+
+/* This function sends data to the card.
+ */
+static int nxpwifi_write_data_to_card(struct nxpwifi_adapter *adapter,
+ u8 *payload, u32 pkt_len, u32 port)
+{
+ u32 i = 0;
+ int ret;
+
+ do {
+ ret = nxpwifi_write_data_sync(adapter, payload, pkt_len, port);
+ if (ret) {
+ i++;
+ nxpwifi_dbg(adapter, ERROR,
+ "host_to_card, write iomem\t"
+ "(%d) failed: %d\n", i, ret);
+ if (nxpwifi_write_reg(adapter, CONFIGURATION_REG, 0x04))
+ nxpwifi_dbg(adapter, ERROR,
+ "write CFG reg failed\n");
+
+ ret = -1;
+ if (i > MAX_WRITE_IOMEM_RETRY)
+ return ret;
+ }
+ } while (ret == -1);
+
+ return ret;
+}
+
+/* This function gets the read port.
+ *
+ * If control port bit is set in MP read bitmap, the control port
+ * is returned, otherwise the current read port is returned and
+ * the value is increased (provided it does not reach the maximum
+ * limit, in which case it is reset to 1)
+ */
+static int nxpwifi_get_rd_port(struct nxpwifi_adapter *adapter, u8 *port)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct nxpwifi_sdio_card_reg *reg = card->reg;
+ u32 rd_bitmap = card->mp_rd_bitmap;
+
+ nxpwifi_dbg(adapter, DATA,
+ "data: mp_rd_bitmap=0x%08x\n", rd_bitmap);
+
+ if (!(rd_bitmap & reg->data_port_mask))
+ return -1;
+
+ if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port)))
+ return -1;
+
+ /* We are now handling the SDIO data ports */
+ card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port));
+ *port = card->curr_rd_port;
+
+ if (++card->curr_rd_port == card->max_ports)
+ card->curr_rd_port = reg->start_rd_port;
+
+ nxpwifi_dbg(adapter, DATA,
+ "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n",
+ *port, rd_bitmap, card->mp_rd_bitmap);
+
+ return 0;
+}
+
+/* This function gets the write port for data.
+ *
+ * The current write port is returned if available and the value is
+ * increased (provided it does not reach the maximum limit, in which
+ * case it is reset to 1)
+ */
+static int nxpwifi_get_wr_port_data(struct nxpwifi_adapter *adapter, u32 *port)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct nxpwifi_sdio_card_reg *reg = card->reg;
+ u32 wr_bitmap = card->mp_wr_bitmap;
+
+ nxpwifi_dbg(adapter, DATA,
+ "data: mp_wr_bitmap=0x%08x\n", wr_bitmap);
+
+ if (!(wr_bitmap & card->mp_data_port_mask)) {
+ adapter->data_sent = true;
+ return -EBUSY;
+ }
+
+ if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) {
+ card->mp_wr_bitmap &= (u32)(~(1 << card->curr_wr_port));
+ *port = card->curr_wr_port;
+ if (++card->curr_wr_port == card->mp_end_port)
+ card->curr_wr_port = reg->start_wr_port;
+ } else {
+ adapter->data_sent = true;
+ return -EBUSY;
+ }
+
+ nxpwifi_dbg(adapter, DATA,
+ "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
+ *port, wr_bitmap, card->mp_wr_bitmap);
+
+ return 0;
+}
+
+/* This function polls the card status.
+ */
+static int
+nxpwifi_sdio_poll_card_status(struct nxpwifi_adapter *adapter, u8 bits)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ u32 tries;
+ u8 cs;
+
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ if (nxpwifi_read_reg(adapter, card->reg->poll_reg, &cs))
+ break;
+ else if ((cs & bits) == bits)
+ return 0;
+
+ usleep_range(10, 20);
+ }
+
+ nxpwifi_dbg(adapter, ERROR,
+ "poll card status failed, tries = %d\n", tries);
+
+ return -1;
+}
+
+/* This function disables the host interrupt.
+ *
+ * The host interrupt mask is read, the disable bit is reset and
+ * written back to the card host interrupt mask register.
+ */
+static void nxpwifi_sdio_disable_host_int(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ struct sdio_func *func = card->func;
+
+ sdio_claim_host(func);
+ nxpwifi_write_reg_locked(func, card->reg->host_int_mask_reg, 0);
+ sdio_release_irq(func);
+ sdio_release_host(func);
+}
+
+/* This function reads the interrupt status from card.
+ */
+static void nxpwifi_interrupt_status(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ u8 sdio_ireg;
+ unsigned long flags;
+
+ if (nxpwifi_read_data_sync(adapter, card->mp_regs,
+ card->reg->max_mp_regs,
+ REG_PORT | NXPWIFI_SDIO_BYTE_MODE_MASK, 0)) {
+ nxpwifi_dbg(adapter, ERROR, "read mp_regs failed\n");
+ return;
+ }
+
+ sdio_ireg = card->mp_regs[card->reg->host_int_status_reg];
+ if (sdio_ireg) {
+ /* DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+ * For SDIO new mode CMD port interrupts
+ * DN_LD_CMD_PORT_HOST_INT_STATUS and/or
+ * UP_LD_CMD_PORT_HOST_INT_STATUS
+ * Clear the interrupt status register
+ */
+ nxpwifi_dbg(adapter, INTR,
+ "int: sdio_ireg = %#x\n", sdio_ireg);
+ spin_lock_irqsave(&adapter->int_lock, flags);
+ adapter->int_status |= sdio_ireg;
+ spin_unlock_irqrestore(&adapter->int_lock, flags);
+ }
+}
+
+/* SDIO interrupt handler.
+ *
+ * This function reads the interrupt status from firmware and handles
+ * the interrupt in current thread (ksdioirqd) right away.
+ */
+static void
+nxpwifi_sdio_interrupt(struct sdio_func *func)
+{
+ struct nxpwifi_adapter *adapter;
+ struct sdio_mmc_card *card;
+
+ card = sdio_get_drvdata(func);
+ if (!card || !card->adapter) {
+ pr_err("int: func=%p card=%p adapter=%p\n",
+ func, card, card ? card->adapter : NULL);
+ return;
+ }
+ adapter = card->adapter;
+
+ if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP)
+ adapter->ps_state = PS_STATE_AWAKE;
+
+ nxpwifi_interrupt_status(adapter);
+ nxpwifi_main_process(adapter);
+}
+
+/* This function enables the host interrupt.
+ *
+ * The host interrupt enable mask is written to the card
+ * host interrupt mask register.
+ */
+static int nxpwifi_sdio_enable_host_int(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ struct sdio_func *func = card->func;
+ int ret;
+
+ sdio_claim_host(func);
+
+ /* Request the SDIO IRQ */
+ ret = sdio_claim_irq(func, nxpwifi_sdio_interrupt);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "claim irq failed: ret=%d\n", ret);
+ goto out;
+ }
+
+ /* Simply write the mask to the register */
+ ret = nxpwifi_write_reg_locked(func, card->reg->host_int_mask_reg,
+ card->reg->host_int_enable);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "enable host interrupt failed\n");
+ sdio_release_irq(func);
+ }
+
+out:
+ sdio_release_host(func);
+ return ret;
+}
+
+/* This function gets a data buffer from the card.
+ */
+static int nxpwifi_sdio_card_to_host(struct nxpwifi_adapter *adapter,
+ u32 *type, u8 *buffer,
+ u32 npayload, u32 ioport)
+{
+ int ret;
+ u32 nb;
+
+ if (!buffer) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: buffer is NULL\n", __func__);
+ return -1;
+ }
+
+ ret = nxpwifi_read_data_sync(adapter, buffer, npayload, ioport, 1);
+
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: read iomem failed: %d\n", __func__,
+ ret);
+ return -1;
+ }
+
+ nb = get_unaligned_le16((buffer));
+ if (nb > npayload) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: invalid packet, nb=%d npayload=%d\n",
+ __func__, nb, npayload);
+ return -1;
+ }
+
+ *type = get_unaligned_le16((buffer + 2));
+
+ return ret;
+}
+
+/* This function downloads the firmware to the card.
+ *
+ * Firmware is downloaded to the card in blocks. Every block download
+ * is tested for CRC errors, and retried a number of times before
+ * returning failure.
+ */
+static int nxpwifi_prog_fw_w_helper(struct nxpwifi_adapter *adapter,
+ struct nxpwifi_fw_image *fw)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct nxpwifi_sdio_card_reg *reg = card->reg;
+ int ret;
+ u8 *firmware = fw->fw_buf;
+ u32 firmware_len = fw->fw_len;
+ u32 offset = 0;
+ u8 base0, base1;
+ u8 *fwbuf;
+ u16 len = 0;
+ u32 txlen, tx_blocks = 0, tries;
+ u32 i = 0;
+
+ if (!firmware_len) {
+ nxpwifi_dbg(adapter, ERROR,
+ "firmware image not found! Terminating download\n");
+ return -1;
+ }
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: downloading FW image (%d bytes)\n",
+ firmware_len);
+
+ /* Assume that the allocated buffer is 8-byte aligned */
+ fwbuf = kzalloc(NXPWIFI_UPLD_SIZE, GFP_KERNEL);
+ if (!fwbuf)
+ return -ENOMEM;
+
+ sdio_claim_host(card->func);
+
+ /* Perform firmware data transfer */
+ do {
+ /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY
+ * bits
+ */
+ ret = nxpwifi_sdio_poll_card_status(adapter, CARD_IO_READY |
+ DN_LD_CARD_RDY);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "FW download with helper:\t"
+ "poll status timeout @ %d\n", offset);
+ goto done;
+ }
+
+ /* More data? */
+ if (offset >= firmware_len)
+ break;
+
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ ret = nxpwifi_read_reg(adapter, reg->base_0_reg,
+ &base0);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "dev BASE0 register read failed:\t"
+ "base0=%#04X(%d). Terminating dnld\n",
+ base0, base0);
+ goto done;
+ }
+ ret = nxpwifi_read_reg(adapter, reg->base_1_reg,
+ &base1);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "dev BASE1 register read failed:\t"
+ "base1=%#04X(%d). Terminating dnld\n",
+ base1, base1);
+ goto done;
+ }
+ len = (u16)(((base1 & 0xff) << 8) | (base0 & 0xff));
+
+ if (len)
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (!len) {
+ break;
+ } else if (len > NXPWIFI_UPLD_SIZE) {
+ nxpwifi_dbg(adapter, ERROR,
+ "FW dnld failed @ %d, invalid length %d\n",
+ offset, len);
+ ret = -1;
+ goto done;
+ }
+
+ txlen = len;
+
+ if (len & BIT(0)) {
+ i++;
+ if (i > MAX_WRITE_IOMEM_RETRY) {
+ nxpwifi_dbg(adapter, ERROR,
+ "FW dnld failed @ %d, over max retry\n",
+ offset);
+ ret = -1;
+ goto done;
+ }
+ nxpwifi_dbg(adapter, ERROR,
+ "CRC indicated by the helper:\t"
+ "len = 0x%04X, txlen = %d\n", len, txlen);
+ len &= ~BIT(0);
+ /* Setting this to 0 to resend from same offset */
+ txlen = 0;
+ } else {
+ i = 0;
+
+ /* Set blocksize to transfer - checking for last
+ * block
+ */
+ if (firmware_len - offset < txlen)
+ txlen = firmware_len - offset;
+
+ tx_blocks = (txlen + NXPWIFI_SDIO_BLOCK_SIZE - 1)
+ / NXPWIFI_SDIO_BLOCK_SIZE;
+
+ /* Copy payload to buffer */
+ memmove(fwbuf, &firmware[offset], txlen);
+ }
+
+ ret = nxpwifi_write_data_sync(adapter, fwbuf, tx_blocks *
+ NXPWIFI_SDIO_BLOCK_SIZE,
+ adapter->ioport);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "FW download, write iomem (%d) failed @ %d\n",
+ i, offset);
+ if (nxpwifi_write_reg(adapter, CONFIGURATION_REG, 0x04))
+ nxpwifi_dbg(adapter, ERROR,
+ "write CFG reg failed\n");
+
+ ret = -1;
+ goto done;
+ }
+
+ offset += txlen;
+ } while (true);
+
+ nxpwifi_dbg(adapter, MSG,
+ "info: FW download over, size %d bytes\n", offset);
+
+ ret = 0;
+done:
+ sdio_release_host(card->func);
+ kfree(fwbuf);
+ return ret;
+}
+
+/* This function decodes sdio aggregation pkt.
+ *
+ * Based on the data block size and pkt_len,
+ * skb data will be decoded to few packets.
+ */
+static void nxpwifi_deaggr_sdio_pkt(struct nxpwifi_adapter *adapter,
+ struct sk_buff *skb)
+{
+ u32 total_pkt_len, pkt_len;
+ struct sk_buff *skb_deaggr;
+ u16 blk_size;
+ u8 blk_num;
+ u8 *data;
+
+ data = skb->data;
+ total_pkt_len = skb->len;
+
+ while (total_pkt_len >= (SDIO_HEADER_OFFSET + adapter->intf_hdr_len)) {
+ if (total_pkt_len < adapter->sdio_rx_block_size)
+ break;
+ blk_num = *(data + BLOCK_NUMBER_OFFSET);
+ blk_size = adapter->sdio_rx_block_size * blk_num;
+ if (blk_size > total_pkt_len) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: error in blk_size,\t"
+ "blk_num=%d, blk_size=%d, total_pkt_len=%d\n",
+ __func__, blk_num, blk_size, total_pkt_len);
+ break;
+ }
+ pkt_len = get_unaligned_le16((data +
+ SDIO_HEADER_OFFSET));
+ if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: error in pkt_len,\t"
+ "pkt_len=%d, blk_size=%d\n",
+ __func__, pkt_len, blk_size);
+ break;
+ }
+
+ skb_deaggr = nxpwifi_alloc_dma_align_buf(pkt_len, GFP_KERNEL);
+ if (!skb_deaggr)
+ break;
+ skb_put(skb_deaggr, pkt_len);
+ memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len);
+ skb_pull(skb_deaggr, adapter->intf_hdr_len);
+
+ nxpwifi_handle_rx_packet(adapter, skb_deaggr);
+ data += blk_size;
+ total_pkt_len -= blk_size;
+ }
+}
+
+/* This function decodes a received packet.
+ *
+ * Based on the type, the packet is treated as either a data, or
+ * a command response, or an event, and the correct handler
+ * function is invoked.
+ */
+static int nxpwifi_decode_rx_packet(struct nxpwifi_adapter *adapter,
+ struct sk_buff *skb, u32 upld_typ)
+{
+ u8 *cmd_buf;
+ u16 pkt_len;
+ struct nxpwifi_rxinfo *rx_info;
+
+ pkt_len = get_unaligned_le16(skb->data);
+
+ if (upld_typ != NXPWIFI_TYPE_AGGR_DATA) {
+ skb_trim(skb, pkt_len);
+ skb_pull(skb, adapter->intf_hdr_len);
+ }
+
+ switch (upld_typ) {
+ case NXPWIFI_TYPE_AGGR_DATA:
+ nxpwifi_dbg(adapter, INFO,
+ "info: --- Rx: Aggr Data packet ---\n");
+ rx_info = NXPWIFI_SKB_RXCB(skb);
+ rx_info->buf_type = NXPWIFI_TYPE_AGGR_DATA;
+ if (adapter->rx_work_enabled) {
+ skb_queue_tail(&adapter->rx_data_q, skb);
+ atomic_inc(&adapter->rx_pending);
+ adapter->data_received = true;
+ } else {
+ nxpwifi_deaggr_sdio_pkt(adapter, skb);
+ dev_kfree_skb_any(skb);
+ }
+ break;
+
+ case NXPWIFI_TYPE_DATA:
+ nxpwifi_dbg(adapter, DATA,
+ "info: --- Rx: Data packet ---\n");
+ if (adapter->rx_work_enabled) {
+ skb_queue_tail(&adapter->rx_data_q, skb);
+ adapter->data_received = true;
+ atomic_inc(&adapter->rx_pending);
+ } else {
+ nxpwifi_handle_rx_packet(adapter, skb);
+ }
+ break;
+
+ case NXPWIFI_TYPE_CMD:
+ nxpwifi_dbg(adapter, CMD,
+ "info: --- Rx: Cmd Response ---\n");
+ /* take care of curr_cmd = NULL case */
+ if (!adapter->curr_cmd) {
+ cmd_buf = adapter->upld_buf;
+
+ if (adapter->ps_state == PS_STATE_SLEEP_CFM)
+ nxpwifi_process_sleep_confirm_resp(adapter,
+ skb->data,
+ skb->len);
+
+ memcpy(cmd_buf, skb->data,
+ min_t(u32, NXPWIFI_SIZE_OF_CMD_BUFFER,
+ skb->len));
+
+ dev_kfree_skb_any(skb);
+ } else {
+ adapter->cmd_resp_received = true;
+ adapter->curr_cmd->resp_skb = skb;
+ }
+ break;
+
+ case NXPWIFI_TYPE_EVENT:
+ nxpwifi_dbg(adapter, EVENT,
+ "info: --- Rx: Event ---\n");
+ adapter->event_cause = get_unaligned_le32(skb->data);
+
+ if (skb->len > 0 && skb->len < MAX_EVENT_SIZE)
+ memcpy(adapter->event_body,
+ skb->data + NXPWIFI_EVENT_HEADER_LEN,
+ skb->len);
+
+ /* event cause has been saved to adapter->event_cause */
+ adapter->event_received = true;
+ adapter->event_skb = skb;
+
+ break;
+
+ default:
+ nxpwifi_dbg(adapter, ERROR,
+ "unknown upload type %#x\n", upld_typ);
+ dev_kfree_skb_any(skb);
+ break;
+ }
+
+ return 0;
+}
+
+/* This function transfers received packets from card to driver, performing
+ * aggregation if required.
+ *
+ * For data received on control port, or if aggregation is disabled, the
+ * received buffers are uploaded as separate packets. However, if aggregation
+ * is enabled and required, the buffers are copied onto an aggregation buffer,
+ * provided there is space left, processed and finally uploaded.
+ */
+static int nxpwifi_sdio_card_to_host_mp_aggr(struct nxpwifi_adapter *adapter,
+ u16 rx_len, u8 port)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ s32 f_do_rx_aggr = 0;
+ s32 f_do_rx_cur = 0;
+ s32 f_aggr_cur = 0;
+ s32 f_post_aggr_cur = 0;
+ struct sk_buff *skb_deaggr;
+ struct sk_buff *skb = NULL;
+ u32 pkt_len, pkt_type, mport, pind;
+ u8 *curr_ptr;
+
+ if (!card->mpa_rx.enabled) {
+ nxpwifi_dbg(adapter, WARN,
+ "info: %s: rx aggregation disabled\n",
+ __func__);
+
+ f_do_rx_cur = 1;
+ goto rx_curr_single;
+ }
+
+ if (card->mp_rd_bitmap & card->reg->data_port_mask) {
+ /* Some more data RX pending */
+ nxpwifi_dbg(adapter, INFO,
+ "info: %s: not last packet\n", __func__);
+
+ if (MP_RX_AGGR_IN_PROGRESS(card)) {
+ if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) {
+ f_aggr_cur = 1;
+ } else {
+ /* No room in Aggr buf, do rx aggr now */
+ f_do_rx_aggr = 1;
+ f_post_aggr_cur = 1;
+ }
+ } else {
+ /* Rx aggr not in progress */
+ f_aggr_cur = 1;
+ }
+
+ } else {
+ /* No more data RX pending */
+ nxpwifi_dbg(adapter, INFO,
+ "info: %s: last packet\n", __func__);
+
+ if (MP_RX_AGGR_IN_PROGRESS(card)) {
+ f_do_rx_aggr = 1;
+ if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len))
+ f_aggr_cur = 1;
+ else
+ /* No room in Aggr buf, do rx aggr now */
+ f_do_rx_cur = 1;
+ } else {
+ f_do_rx_cur = 1;
+ }
+ }
+
+ if (f_aggr_cur) {
+ nxpwifi_dbg(adapter, INFO,
+ "info: current packet aggregation\n");
+ /* Curr pkt can be aggregated */
+ mp_rx_aggr_setup(card, rx_len, port);
+
+ if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) ||
+ mp_rx_aggr_port_limit_reached(card)) {
+ nxpwifi_dbg(adapter, INFO,
+ "info: %s: aggregated packet\t"
+ "limit reached\n", __func__);
+ /* No more pkts allowed in Aggr buf, rx it */
+ f_do_rx_aggr = 1;
+ }
+ }
+
+ if (f_do_rx_aggr) {
+ u32 port_count;
+ int i;
+
+ /* do aggr RX now */
+ nxpwifi_dbg(adapter, DATA,
+ "info: do_rx_aggr: num of packets: %d\n",
+ card->mpa_rx.pkt_cnt);
+
+ for (i = 0, port_count = 0; i < card->max_ports; i++)
+ if (card->mpa_rx.ports & BIT(i))
+ port_count++;
+
+ /* Reading data from "start_port + 0" to "start_port +
+ * port_count -1", so decrease the count by 1
+ */
+ port_count--;
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (port_count << 8)) + card->mpa_rx.start_port;
+
+ if (card->mpa_rx.pkt_cnt == 1)
+ mport = adapter->ioport + card->mpa_rx.start_port;
+
+ if (nxpwifi_read_data_sync(adapter, card->mpa_rx.buf,
+ card->mpa_rx.buf_len, mport, 1))
+ goto error;
+
+ curr_ptr = card->mpa_rx.buf;
+
+ for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) {
+ u32 *len_arr = card->mpa_rx.len_arr;
+
+ /* get curr PKT len & type */
+ pkt_len = get_unaligned_le16(&curr_ptr[0]);
+ pkt_type = get_unaligned_le16(&curr_ptr[2]);
+
+ /* copy pkt to deaggr buf */
+ skb_deaggr = nxpwifi_alloc_dma_align_buf(len_arr[pind],
+ GFP_KERNEL);
+ if (!skb_deaggr) {
+ nxpwifi_dbg(adapter, ERROR, "skb allocation failure\t"
+ "drop pkt len=%d type=%d\n",
+ pkt_len, pkt_type);
+ curr_ptr += len_arr[pind];
+ continue;
+ }
+
+ skb_put(skb_deaggr, len_arr[pind]);
+
+ if ((pkt_type == NXPWIFI_TYPE_DATA ||
+ (pkt_type == NXPWIFI_TYPE_AGGR_DATA &&
+ adapter->sdio_rx_aggr_enable)) &&
+ pkt_len <= len_arr[pind]) {
+ memcpy(skb_deaggr->data, curr_ptr, pkt_len);
+
+ skb_trim(skb_deaggr, pkt_len);
+
+ /* Process de-aggr packet */
+ nxpwifi_decode_rx_packet(adapter, skb_deaggr,
+ pkt_type);
+ } else {
+ nxpwifi_dbg(adapter, ERROR,
+ "drop wrong aggr pkt:\t"
+ "sdio_single_port_rx_aggr=%d\t"
+ "type=%d len=%d max_len=%d\n",
+ adapter->sdio_rx_aggr_enable,
+ pkt_type, pkt_len, len_arr[pind]);
+ dev_kfree_skb_any(skb_deaggr);
+ }
+ curr_ptr += len_arr[pind];
+ }
+ MP_RX_AGGR_BUF_RESET(card);
+ }
+
+rx_curr_single:
+ if (f_do_rx_cur) {
+ nxpwifi_dbg(adapter, INFO, "info: RX: port: %d, rx_len: %d\n",
+ port, rx_len);
+
+ skb = nxpwifi_alloc_dma_align_buf(rx_len, GFP_KERNEL);
+ if (!skb) {
+ nxpwifi_dbg(adapter, ERROR,
+ "single skb allocated fail,\t"
+ "drop pkt port=%d len=%d\n", port, rx_len);
+ if (nxpwifi_sdio_card_to_host(adapter, &pkt_type,
+ card->mpa_rx.buf, rx_len,
+ adapter->ioport + port))
+ goto error;
+ return 0;
+ }
+
+ skb_put(skb, rx_len);
+
+ if (nxpwifi_sdio_card_to_host(adapter, &pkt_type,
+ skb->data, skb->len,
+ adapter->ioport + port))
+ goto error;
+ if (!adapter->sdio_rx_aggr_enable &&
+ pkt_type == NXPWIFI_TYPE_AGGR_DATA) {
+ nxpwifi_dbg(adapter, ERROR, "drop wrong pkt type %d\t"
+ "current SDIO RX Aggr not enabled\n",
+ pkt_type);
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ nxpwifi_decode_rx_packet(adapter, skb, pkt_type);
+ }
+ if (f_post_aggr_cur) {
+ nxpwifi_dbg(adapter, INFO,
+ "info: current packet aggregation\n");
+ /* Curr pkt can be aggregated */
+ mp_rx_aggr_setup(card, rx_len, port);
+ }
+
+ return 0;
+error:
+ if (MP_RX_AGGR_IN_PROGRESS(card))
+ MP_RX_AGGR_BUF_RESET(card);
+
+ if (f_do_rx_cur && skb)
+ /* Single transfer pending. Free curr buff also */
+ dev_kfree_skb_any(skb);
+
+ return -1;
+}
+
+/* This function checks the current interrupt status.
+ *
+ * The following interrupts are checked and handled by this function -
+ * - Data sent
+ * - Command sent
+ * - Packets received
+ *
+ * Since the firmware does not generate download ready interrupt if the
+ * port updated is command port only, command sent interrupt checking
+ * should be done manually, and for every SDIO interrupt.
+ *
+ * In case of Rx packets received, the packets are uploaded from card to
+ * host and processed accordingly.
+ */
+static int nxpwifi_process_int_status(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct nxpwifi_sdio_card_reg *reg = card->reg;
+ int ret = 0;
+ u8 sdio_ireg;
+ struct sk_buff *skb;
+ u8 port;
+ u32 len_reg_l, len_reg_u;
+ u32 rx_blocks;
+ u16 rx_len;
+ unsigned long flags;
+ u32 bitmap;
+ u8 cr;
+
+ spin_lock_irqsave(&adapter->int_lock, flags);
+ sdio_ireg = adapter->int_status;
+ adapter->int_status = 0;
+ spin_unlock_irqrestore(&adapter->int_lock, flags);
+
+ if (!sdio_ireg)
+ return ret;
+
+ /* Following interrupt is only for SDIO new mode */
+ if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent)
+ adapter->cmd_sent = false;
+
+ /* Following interrupt is only for SDIO new mode */
+ if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) {
+ u32 pkt_type;
+
+ /* read the len of control packet */
+ rx_len = card->mp_regs[reg->cmd_rd_len_1] << 8;
+ rx_len |= (u16)card->mp_regs[reg->cmd_rd_len_0];
+ rx_blocks = DIV_ROUND_UP(rx_len, NXPWIFI_SDIO_BLOCK_SIZE);
+ if (rx_len <= adapter->intf_hdr_len ||
+ (rx_blocks * NXPWIFI_SDIO_BLOCK_SIZE) >
+ NXPWIFI_RX_DATA_BUF_SIZE)
+ return -1;
+ rx_len = (u16)(rx_blocks * NXPWIFI_SDIO_BLOCK_SIZE);
+ nxpwifi_dbg(adapter, INFO, "info: rx_len = %d\n", rx_len);
+
+ skb = nxpwifi_alloc_dma_align_buf(rx_len, GFP_KERNEL);
+ if (!skb)
+ return -1;
+
+ skb_put(skb, rx_len);
+
+ if (nxpwifi_sdio_card_to_host(adapter, &pkt_type, skb->data,
+ skb->len, adapter->ioport |
+ CMD_PORT_SLCT)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: failed to card_to_host", __func__);
+ dev_kfree_skb_any(skb);
+ goto term_cmd;
+ }
+
+ if (pkt_type != NXPWIFI_TYPE_CMD &&
+ pkt_type != NXPWIFI_TYPE_EVENT)
+ nxpwifi_dbg(adapter, ERROR,
+ "%s:Received wrong packet on cmd port",
+ __func__);
+
+ nxpwifi_decode_rx_packet(adapter, skb, pkt_type);
+ }
+
+ if (sdio_ireg & DN_LD_HOST_INT_STATUS) {
+ bitmap = (u32)card->mp_regs[reg->wr_bitmap_l];
+ bitmap |= ((u32)card->mp_regs[reg->wr_bitmap_u]) << 8;
+ bitmap |= ((u32)card->mp_regs[reg->wr_bitmap_1l]) << 16;
+ bitmap |= ((u32)card->mp_regs[reg->wr_bitmap_1u]) << 24;
+ card->mp_wr_bitmap = bitmap;
+
+ nxpwifi_dbg(adapter, INTR,
+ "int: DNLD: wr_bitmap=0x%x\n",
+ card->mp_wr_bitmap);
+ if (adapter->data_sent &&
+ (card->mp_wr_bitmap & card->mp_data_port_mask)) {
+ nxpwifi_dbg(adapter, INTR,
+ "info: <--- Tx DONE Interrupt --->\n");
+ adapter->data_sent = false;
+ }
+ }
+
+ nxpwifi_dbg(adapter, INTR, "info: cmd_sent=%d data_sent=%d\n",
+ adapter->cmd_sent, adapter->data_sent);
+ if (sdio_ireg & UP_LD_HOST_INT_STATUS) {
+ bitmap = (u32)card->mp_regs[reg->rd_bitmap_l];
+ bitmap |= ((u32)card->mp_regs[reg->rd_bitmap_u]) << 8;
+ bitmap |= ((u32)card->mp_regs[reg->rd_bitmap_1l]) << 16;
+ bitmap |= ((u32)card->mp_regs[reg->rd_bitmap_1u]) << 24;
+ card->mp_rd_bitmap = bitmap;
+ nxpwifi_dbg(adapter, INTR,
+ "int: UPLD: rd_bitmap=0x%x\n",
+ card->mp_rd_bitmap);
+
+ while (true) {
+ ret = nxpwifi_get_rd_port(adapter, &port);
+ if (ret) {
+ nxpwifi_dbg(adapter, INFO,
+ "info: no more rd_port available\n");
+ break;
+ }
+ len_reg_l = reg->rd_len_p0_l + (port << 1);
+ len_reg_u = reg->rd_len_p0_u + (port << 1);
+ rx_len = ((u16)card->mp_regs[len_reg_u]) << 8;
+ rx_len |= (u16)card->mp_regs[len_reg_l];
+ nxpwifi_dbg(adapter, INFO,
+ "info: RX: port=%d rx_len=%u\n",
+ port, rx_len);
+ rx_blocks =
+ (rx_len + NXPWIFI_SDIO_BLOCK_SIZE -
+ 1) / NXPWIFI_SDIO_BLOCK_SIZE;
+ if (rx_len <= adapter->intf_hdr_len ||
+ (card->mpa_rx.enabled &&
+ ((rx_blocks * NXPWIFI_SDIO_BLOCK_SIZE) >
+ card->mpa_rx.buf_size))) {
+ nxpwifi_dbg(adapter, ERROR,
+ "invalid rx_len=%d\n",
+ rx_len);
+ return -1;
+ }
+
+ rx_len = (u16)(rx_blocks * NXPWIFI_SDIO_BLOCK_SIZE);
+ nxpwifi_dbg(adapter, INFO, "info: rx_len = %d\n",
+ rx_len);
+
+ if (nxpwifi_sdio_card_to_host_mp_aggr(adapter, rx_len,
+ port)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "card_to_host_mpa failed: int status=%#x\n",
+ sdio_ireg);
+ goto term_cmd;
+ }
+ }
+ }
+
+ return 0;
+
+term_cmd:
+ /* terminate cmd */
+ if (nxpwifi_read_reg(adapter, CONFIGURATION_REG, &cr))
+ nxpwifi_dbg(adapter, ERROR, "read CFG reg failed\n");
+ else
+ nxpwifi_dbg(adapter, INFO,
+ "info: CFG reg val = %d\n", cr);
+
+ if (nxpwifi_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04)))
+ nxpwifi_dbg(adapter, ERROR,
+ "write CFG reg failed\n");
+ else
+ nxpwifi_dbg(adapter, INFO, "info: write success\n");
+
+ if (nxpwifi_read_reg(adapter, CONFIGURATION_REG, &cr))
+ nxpwifi_dbg(adapter, ERROR,
+ "read CFG reg failed\n");
+ else
+ nxpwifi_dbg(adapter, INFO,
+ "info: CFG reg val =%x\n", cr);
+
+ return -1;
+}
+
+/* This function aggregates transmission buffers in driver and downloads
+ * the aggregated packet to card.
+ *
+ * The individual packets are aggregated by copying into an aggregation
+ * buffer and then downloaded to the card. Previous unsent packets in the
+ * aggregation buffer are pre-copied first before new packets are added.
+ * Aggregation is done till there is space left in the aggregation buffer,
+ * or till new packets are available.
+ *
+ * The function will only download the packet to the card when aggregation
+ * stops, otherwise it will just aggregate the packet in aggregation buffer
+ * and return.
+ */
+static int nxpwifi_host_to_card_mp_aggr(struct nxpwifi_adapter *adapter,
+ u8 *payload, u32 pkt_len, u32 port,
+ u32 next_pkt_len)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret = 0;
+ s32 f_send_aggr_buf = 0;
+ s32 f_send_cur_buf = 0;
+ s32 f_precopy_cur_buf = 0;
+ s32 f_postcopy_cur_buf = 0;
+ u32 mport;
+ int index;
+
+ if (!card->mpa_tx.enabled || port == CMD_PORT_SLCT) {
+ nxpwifi_dbg(adapter, WARN,
+ "info: %s: tx aggregation disabled\n",
+ __func__);
+
+ f_send_cur_buf = 1;
+ goto tx_curr_single;
+ }
+
+ if (next_pkt_len) {
+ /* More pkt in TX queue */
+ nxpwifi_dbg(adapter, INFO,
+ "info: %s: more packets in queue.\n",
+ __func__);
+
+ if (MP_TX_AGGR_IN_PROGRESS(card)) {
+ if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) {
+ f_precopy_cur_buf = 1;
+
+ if (!(card->mp_wr_bitmap &
+ (1 << card->curr_wr_port)) ||
+ !MP_TX_AGGR_BUF_HAS_ROOM
+ (card, pkt_len + next_pkt_len))
+ f_send_aggr_buf = 1;
+ } else {
+ /* No room in Aggr buf, send it */
+ f_send_aggr_buf = 1;
+
+ if (!(card->mp_wr_bitmap &
+ (1 << card->curr_wr_port)))
+ f_send_cur_buf = 1;
+ else
+ f_postcopy_cur_buf = 1;
+ }
+ } else {
+ if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) &&
+ (card->mp_wr_bitmap & (1 << card->curr_wr_port)))
+ f_precopy_cur_buf = 1;
+ else
+ f_send_cur_buf = 1;
+ }
+ } else {
+ /* Last pkt in TX queue */
+ nxpwifi_dbg(adapter, INFO,
+ "info: %s: Last packet in Tx Queue.\n",
+ __func__);
+
+ if (MP_TX_AGGR_IN_PROGRESS(card)) {
+ /* some packs in Aggr buf already */
+ f_send_aggr_buf = 1;
+
+ if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len))
+ f_precopy_cur_buf = 1;
+ else
+ /* No room in Aggr buf, send it */
+ f_send_cur_buf = 1;
+ } else {
+ f_send_cur_buf = 1;
+ }
+ }
+
+ if (f_precopy_cur_buf) {
+ nxpwifi_dbg(adapter, DATA,
+ "data: %s: precopy current buffer\n",
+ __func__);
+ MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port);
+
+ if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) ||
+ mp_tx_aggr_port_limit_reached(card))
+ /* No more pkts allowed in Aggr buf, send it */
+ f_send_aggr_buf = 1;
+ }
+
+ if (f_send_aggr_buf) {
+ u32 port_count;
+ int i;
+
+ nxpwifi_dbg(adapter, DATA,
+ "data: %s: send aggr buffer: %d %d\n",
+ __func__, card->mpa_tx.start_port,
+ card->mpa_tx.ports);
+
+ for (i = 0, port_count = 0; i < card->max_ports; i++)
+ if (card->mpa_tx.ports & BIT(i))
+ port_count++;
+
+ /* Writing data from "start_port + 0" to "start_port +
+ * port_count -1", so decrease the count by 1
+ */
+ port_count--;
+ mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+ (port_count << 8)) + card->mpa_tx.start_port;
+
+ if (card->mpa_tx.pkt_cnt == 1)
+ mport = adapter->ioport + card->mpa_tx.start_port;
+
+ ret = nxpwifi_write_data_to_card(adapter, card->mpa_tx.buf,
+ card->mpa_tx.buf_len, mport);
+
+ /* Save the last multi port tx aggregation info to debug log */
+ index = adapter->dbg.last_sdio_mp_index;
+ index = (index + 1) % NXPWIFI_DBG_SDIO_MP_NUM;
+ adapter->dbg.last_sdio_mp_index = index;
+ adapter->dbg.last_mp_wr_ports[index] = mport;
+ adapter->dbg.last_mp_wr_bitmap[index] = card->mp_wr_bitmap;
+ adapter->dbg.last_mp_wr_len[index] = card->mpa_tx.buf_len;
+ adapter->dbg.last_mp_curr_wr_port[index] = card->curr_wr_port;
+
+ MP_TX_AGGR_BUF_RESET(card);
+ }
+
+tx_curr_single:
+ if (f_send_cur_buf) {
+ nxpwifi_dbg(adapter, DATA,
+ "data: %s: send current buffer %d\n",
+ __func__, port);
+ ret = nxpwifi_write_data_to_card(adapter, payload, pkt_len,
+ adapter->ioport + port);
+ }
+
+ if (f_postcopy_cur_buf) {
+ nxpwifi_dbg(adapter, DATA,
+ "data: %s: postcopy current buffer\n",
+ __func__);
+ MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port);
+ }
+
+ return ret;
+}
+
+/* This function downloads data from driver to card.
+ *
+ * Both commands and data packets are transferred to the card by this
+ * function.
+ *
+ * This function adds the SDIO specific header to the front of the buffer
+ * before transferring. The header contains the length of the packet and
+ * the type. The firmware handles the packets based upon this set type.
+ */
+static int nxpwifi_sdio_host_to_card(struct nxpwifi_adapter *adapter,
+ u8 type, struct sk_buff *skb,
+ struct nxpwifi_tx_param *tx_param)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret;
+ u32 buf_block_len;
+ u32 blk_size;
+ u32 port;
+ u8 *payload = (u8 *)skb->data;
+ u32 pkt_len = skb->len;
+
+ /* Allocate buffer and copy payload */
+ blk_size = NXPWIFI_SDIO_BLOCK_SIZE;
+ buf_block_len = (pkt_len + blk_size - 1) / blk_size;
+ put_unaligned_le16((u16)pkt_len, payload + 0);
+ put_unaligned_le16((u16)type, payload + 2);
+
+ /* This is SDIO specific header
+ * u16 length,
+ * u16 type (NXPWIFI_TYPE_DATA = 0, NXPWIFI_TYPE_CMD = 1,
+ * NXPWIFI_TYPE_EVENT = 3)
+ */
+ if (type == NXPWIFI_TYPE_DATA) {
+ ret = nxpwifi_get_wr_port_data(adapter, &port);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: no wr_port available\n",
+ __func__);
+ return ret;
+ }
+ } else {
+ adapter->cmd_sent = true;
+ /* Type must be NXPWIFI_TYPE_CMD */
+
+ if (pkt_len <= adapter->intf_hdr_len ||
+ pkt_len > NXPWIFI_UPLD_SIZE)
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: payload=%p, nb=%d\n",
+ __func__, payload, pkt_len);
+
+ port = CMD_PORT_SLCT;
+ }
+
+ /* Transfer data to card */
+ pkt_len = buf_block_len * blk_size;
+
+ if (tx_param)
+ ret = nxpwifi_host_to_card_mp_aggr(adapter, payload, pkt_len,
+ port, tx_param->next_pkt_len
+ );
+ else
+ ret = nxpwifi_host_to_card_mp_aggr(adapter, payload, pkt_len,
+ port, 0);
+
+ if (ret) {
+ if (type == NXPWIFI_TYPE_CMD)
+ adapter->cmd_sent = false;
+ if (type == NXPWIFI_TYPE_DATA) {
+ adapter->data_sent = false;
+ /* restore curr_wr_port in error cases */
+ card->curr_wr_port = port;
+ card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port);
+ }
+ } else {
+ if (type == NXPWIFI_TYPE_DATA) {
+ if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port)))
+ adapter->data_sent = true;
+ else
+ adapter->data_sent = false;
+ }
+ }
+
+ return ret;
+}
+
+/* This function allocates the MPA Tx and Rx buffers.
+ */
+static int nxpwifi_alloc_sdio_mpa_buffers(struct nxpwifi_adapter *adapter,
+ u32 mpa_tx_buf_size,
+ u32 mpa_rx_buf_size)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ u32 rx_buf_size;
+ int ret = 0;
+
+ card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL);
+ if (!card->mpa_tx.buf) {
+ ret = -1;
+ goto error;
+ }
+
+ card->mpa_tx.buf_size = mpa_tx_buf_size;
+
+ rx_buf_size = max_t(u32, mpa_rx_buf_size,
+ (u32)SDIO_MAX_AGGR_BUF_SIZE);
+ card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL);
+ if (!card->mpa_rx.buf) {
+ ret = -1;
+ goto error;
+ }
+
+ card->mpa_rx.buf_size = rx_buf_size;
+
+error:
+ if (ret) {
+ kfree(card->mpa_tx.buf);
+ kfree(card->mpa_rx.buf);
+ card->mpa_tx.buf_size = 0;
+ card->mpa_rx.buf_size = 0;
+ card->mpa_tx.buf = NULL;
+ card->mpa_rx.buf = NULL;
+ }
+
+ return ret;
+}
+
+/* This function unregisters the SDIO device.
+ *
+ * The SDIO IRQ is released, the function is disabled and driver
+ * data is set to null.
+ */
+static void
+nxpwifi_unregister_dev(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+
+ if (adapter->card) {
+ card->adapter = NULL;
+ sdio_claim_host(card->func);
+ sdio_disable_func(card->func);
+ sdio_release_host(card->func);
+ }
+}
+
+/* This function registers the SDIO device.
+ *
+ * SDIO IRQ is claimed, block size is set and driver data is initialized.
+ */
+static int nxpwifi_register_dev(struct nxpwifi_adapter *adapter)
+{
+ int ret;
+ struct sdio_mmc_card *card = adapter->card;
+ struct sdio_func *func = card->func;
+ const char *firmware = card->firmware;
+
+ /* save adapter pointer in card */
+ card->adapter = adapter;
+ adapter->tx_buf_size = card->tx_buf_size;
+
+ sdio_claim_host(func);
+
+ /* Set block size */
+ ret = sdio_set_block_size(card->func, NXPWIFI_SDIO_BLOCK_SIZE);
+ sdio_release_host(func);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "cannot set SDIO block size\n");
+ return ret;
+ }
+
+ /* Select correct firmware (sdsd or sdiouart) firmware based on the strapping
+ * option
+ */
+ if (card->firmware_sdiouart) {
+ u8 val;
+
+ nxpwifi_read_reg(adapter, card->reg->host_strap_reg, &val);
+ if ((val & card->reg->host_strap_mask) == card->reg->host_strap_value)
+ firmware = card->firmware_sdiouart;
+ }
+ strscpy(adapter->fw_name, firmware, sizeof(adapter->fw_name));
+
+ if (card->fw_dump_enh) {
+ adapter->mem_type_mapping_tbl = generic_mem_type_map;
+ adapter->num_mem_types = 1;
+ } else {
+ adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
+ adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
+ }
+
+ return 0;
+}
+
+/* This function initializes the SDIO driver.
+ *
+ * The following initializations steps are followed -
+ * - Read the Host interrupt status register to acknowledge
+ * the first interrupt got from bootloader
+ * - Disable host interrupt mask register
+ * - Get SDIO port
+ * - Initialize SDIO variables in card
+ * - Allocate MP registers
+ * - Allocate MPA Tx and Rx buffers
+ */
+static int nxpwifi_init_sdio(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct nxpwifi_sdio_card_reg *reg = card->reg;
+ int ret;
+ u8 sdio_ireg;
+
+ sdio_set_drvdata(card->func, card);
+
+ /* Read the host_int_status_reg for ACK the first interrupt got
+ * from the bootloader. If we don't do this we get a interrupt
+ * as soon as we register the irq.
+ */
+ nxpwifi_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg);
+
+ /* Get SDIO ioport */
+ if (nxpwifi_init_sdio_ioport(adapter))
+ return -EIO;
+
+ /* Initialize SDIO variables in card */
+ card->mp_rd_bitmap = 0;
+ card->mp_wr_bitmap = 0;
+ card->curr_rd_port = reg->start_rd_port;
+ card->curr_wr_port = reg->start_wr_port;
+
+ card->mp_data_port_mask = reg->data_port_mask;
+
+ card->mpa_tx.buf_len = 0;
+ card->mpa_tx.pkt_cnt = 0;
+ card->mpa_tx.start_port = 0;
+
+ card->mpa_tx.enabled = 1;
+ card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit;
+
+ card->mpa_rx.buf_len = 0;
+ card->mpa_rx.pkt_cnt = 0;
+ card->mpa_rx.start_port = 0;
+
+ card->mpa_rx.enabled = 1;
+ card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit;
+
+ /* Allocate buffers for SDIO MP-A */
+ card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL);
+ if (!card->mp_regs)
+ return -ENOMEM;
+
+ card->mpa_rx.len_arr = kcalloc(card->mp_agg_pkt_limit,
+ sizeof(*card->mpa_rx.len_arr),
+ GFP_KERNEL);
+ if (!card->mpa_rx.len_arr) {
+ kfree(card->mp_regs);
+ return -ENOMEM;
+ }
+
+ ret = nxpwifi_alloc_sdio_mpa_buffers(adapter,
+ card->mp_tx_agg_buf_size,
+ card->mp_rx_agg_buf_size);
+
+ /* Allocate 32k MPA Tx/Rx buffers if 64k memory allocation fails */
+ if (ret && (card->mp_tx_agg_buf_size == NXPWIFI_MP_AGGR_BSIZE_MAX ||
+ card->mp_rx_agg_buf_size == NXPWIFI_MP_AGGR_BSIZE_MAX)) {
+ /* Disable rx single port aggregation */
+ adapter->host_disable_sdio_rx_aggr = true;
+
+ ret = nxpwifi_alloc_sdio_mpa_buffers(adapter,
+ NXPWIFI_MP_AGGR_BSIZE_32K,
+ NXPWIFI_MP_AGGR_BSIZE_32K);
+ if (ret) {
+ /* Disable multi port aggregation */
+ card->mpa_tx.enabled = 0;
+ card->mpa_rx.enabled = 0;
+ }
+ }
+
+ adapter->ext_scan = card->can_ext_scan;
+ return 0;
+}
+
+/* This function resets the MPA Tx and Rx buffers.
+ */
+static void nxpwifi_cleanup_mpa_buf(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+
+ MP_TX_AGGR_BUF_RESET(card);
+ MP_RX_AGGR_BUF_RESET(card);
+}
+
+/* This function cleans up the allocated card buffers.
+ *
+ * The following are freed by this function -
+ * - MP registers
+ * - MPA Tx buffer
+ * - MPA Rx buffer
+ */
+static void nxpwifi_cleanup_sdio(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+
+ cancel_work_sync(&card->work);
+
+ kfree(card->mp_regs);
+ kfree(card->mpa_rx.len_arr);
+ kfree(card->mpa_tx.buf);
+ kfree(card->mpa_rx.buf);
+}
+
+/* This function updates the MP end port in card.
+ */
+static void
+nxpwifi_update_mp_end_port(struct nxpwifi_adapter *adapter, u16 port)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ const struct nxpwifi_sdio_card_reg *reg = card->reg;
+ int i;
+
+ card->mp_end_port = port;
+
+ card->mp_data_port_mask = reg->data_port_mask;
+
+ if (reg->start_wr_port) {
+ for (i = 1; i <= card->max_ports - card->mp_end_port; i++)
+ card->mp_data_port_mask &=
+ ~(1 << (card->max_ports - i));
+ }
+
+ card->curr_wr_port = reg->start_wr_port;
+
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: mp_end_port %d, data port mask 0x%x\n",
+ port, card->mp_data_port_mask);
+}
+
+static void nxpwifi_sdio_card_reset_work(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ struct sdio_func *func = card->func;
+ int ret;
+
+ /* Prepare the adapter for the reset. */
+ nxpwifi_shutdown_sw(adapter);
+ clear_bit(NXPWIFI_IFACE_WORK_DEVICE_DUMP, &card->work_flags);
+ clear_bit(NXPWIFI_IFACE_WORK_CARD_RESET, &card->work_flags);
+
+ /* Run a HW reset of the SDIO interface. */
+ sdio_claim_host(func);
+ ret = mmc_hw_reset(func->card);
+ sdio_release_host(func);
+
+ switch (ret) {
+ case 1:
+ dev_dbg(&func->dev, "SDIO HW reset asynchronous\n");
+ complete_all(adapter->fw_done);
+ break;
+ case 0:
+ ret = nxpwifi_reinit_sw(adapter);
+ if (ret)
+ dev_err(&func->dev, "reinit failed: %d\n", ret);
+ break;
+ default:
+ dev_err(&func->dev, "SDIO HW reset failed: %d\n", ret);
+ break;
+ }
+}
+
+/* This function read/write firmware */
+static enum
+rdwr_status nxpwifi_sdio_rdwr_firmware(struct nxpwifi_adapter *adapter,
+ u8 doneflag)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret, tries;
+ u8 ctrl_data = 0;
+
+ sdio_writeb(card->func, card->reg->fw_dump_host_ready,
+ card->reg->fw_dump_ctrl, &ret);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR, "SDIO Write ERR\n");
+ return RDWR_STATUS_FAILURE;
+ }
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl,
+ &ret);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR, "SDIO read err\n");
+ return RDWR_STATUS_FAILURE;
+ }
+ if (ctrl_data == FW_DUMP_DONE)
+ break;
+ if (doneflag && ctrl_data == doneflag)
+ return RDWR_STATUS_DONE;
+ if (ctrl_data != card->reg->fw_dump_host_ready) {
+ nxpwifi_dbg(adapter, WARN,
+ "The ctrl reg was changed, re-try again\n");
+ sdio_writeb(card->func, card->reg->fw_dump_host_ready,
+ card->reg->fw_dump_ctrl, &ret);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR, "SDIO write err\n");
+ return RDWR_STATUS_FAILURE;
+ }
+ }
+ usleep_range(100, 200);
+ }
+ if (ctrl_data == card->reg->fw_dump_host_ready) {
+ nxpwifi_dbg(adapter, ERROR,
+ "Fail to pull ctrl_data\n");
+ return RDWR_STATUS_FAILURE;
+ }
+
+ return RDWR_STATUS_SUCCESS;
+}
+
+/* This function dump firmware memory to file */
+static void nxpwifi_sdio_fw_dump(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret = 0;
+ unsigned int reg, reg_start, reg_end;
+ u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
+ enum rdwr_status stat;
+ u32 memory_size;
+
+ if (!card->can_dump_fw)
+ return;
+
+ for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+ }
+
+ nxpwifi_pm_wakeup_card(adapter);
+ sdio_claim_host(card->func);
+
+ nxpwifi_dbg(adapter, MSG, "== nxpwifi firmware dump start ==\n");
+
+ stat = nxpwifi_sdio_rdwr_firmware(adapter, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ reg = card->reg->fw_dump_start;
+ /* Read the number of the memories which will dump */
+ dump_num = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR, "SDIO read memory length err\n");
+ goto done;
+ }
+
+ /* Read the length of every memory which will dump */
+ for (idx = 0; idx < dump_num; idx++) {
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+ stat = nxpwifi_sdio_rdwr_firmware(adapter, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ memory_size = 0;
+ reg = card->reg->fw_dump_start;
+ for (i = 0; i < 4; i++) {
+ read_reg = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR, "SDIO read err\n");
+ goto done;
+ }
+ memory_size |= (read_reg << i * 8);
+ reg++;
+ }
+
+ if (memory_size == 0) {
+ nxpwifi_dbg(adapter, DUMP, "Firmware dump Finished!\n");
+ ret = nxpwifi_write_reg(adapter,
+ card->reg->fw_dump_ctrl,
+ FW_DUMP_READ_DONE);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR, "SDIO write err\n");
+ return;
+ }
+ break;
+ }
+
+ nxpwifi_dbg(adapter, DUMP,
+ "%s_SIZE=0x%x\n", entry->mem_name, memory_size);
+ entry->mem_ptr = vmalloc(memory_size + 1);
+ entry->mem_size = memory_size;
+ if (!entry->mem_ptr)
+ goto done;
+ dbg_ptr = entry->mem_ptr;
+ end_ptr = dbg_ptr + memory_size;
+
+ doneflag = entry->done_flag;
+ nxpwifi_dbg(adapter, DUMP,
+ "Start %s output, please wait...\n",
+ entry->mem_name);
+
+ do {
+ stat = nxpwifi_sdio_rdwr_firmware(adapter, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ reg_start = card->reg->fw_dump_start;
+ reg_end = card->reg->fw_dump_end;
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ *dbg_ptr = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "SDIO read err\n");
+ goto done;
+ }
+ if (dbg_ptr < end_ptr)
+ dbg_ptr++;
+ else
+ nxpwifi_dbg(adapter, ERROR,
+ "Allocated buf not enough\n");
+ }
+
+ if (stat != RDWR_STATUS_DONE)
+ continue;
+
+ nxpwifi_dbg(adapter, DUMP, "%s done: size=0x%tx\n",
+ entry->mem_name, dbg_ptr - entry->mem_ptr);
+ break;
+ } while (1);
+ }
+ nxpwifi_dbg(adapter, MSG, "== nxpwifi firmware dump end ==\n");
+
+done:
+ sdio_release_host(card->func);
+}
+
+static void nxpwifi_sdio_generic_fw_dump(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ struct memory_type_mapping *entry = &generic_mem_type_map[0];
+ unsigned int reg, reg_start, reg_end;
+ u8 start_flag = 0, done_flag = 0;
+ u8 *dbg_ptr, *end_ptr;
+ enum rdwr_status stat;
+ int ret = -1, tries;
+
+ if (!card->fw_dump_enh)
+ return;
+
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+
+ nxpwifi_pm_wakeup_card(adapter);
+ sdio_claim_host(card->func);
+
+ nxpwifi_dbg(adapter, MSG, "== nxpwifi firmware dump start ==\n");
+
+ stat = nxpwifi_sdio_rdwr_firmware(adapter, done_flag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ reg_start = card->reg->fw_dump_start;
+ reg_end = card->reg->fw_dump_end;
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ start_flag = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "SDIO read err\n");
+ goto done;
+ }
+ if (start_flag == 0)
+ break;
+ if (tries == MAX_POLL_TRIES) {
+ nxpwifi_dbg(adapter, ERROR,
+ "FW not ready to dump\n");
+ ret = -1;
+ goto done;
+ }
+ }
+ usleep_range(100, 200);
+ }
+
+ entry->mem_ptr = vmalloc(0xf0000 + 1);
+ if (!entry->mem_ptr) {
+ ret = -1;
+ goto done;
+ }
+ dbg_ptr = entry->mem_ptr;
+ entry->mem_size = 0xf0000;
+ end_ptr = dbg_ptr + entry->mem_size;
+
+ done_flag = entry->done_flag;
+ nxpwifi_dbg(adapter, DUMP,
+ "Start %s output, please wait...\n", entry->mem_name);
+
+ while (true) {
+ stat = nxpwifi_sdio_rdwr_firmware(adapter, done_flag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ *dbg_ptr = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR,
+ "SDIO read err\n");
+ goto done;
+ }
+ dbg_ptr++;
+ if (dbg_ptr >= end_ptr) {
+ u8 *tmp_ptr;
+
+ tmp_ptr = vmalloc(entry->mem_size + 0x4000 + 1);
+ if (!tmp_ptr)
+ goto done;
+
+ memcpy(tmp_ptr, entry->mem_ptr,
+ entry->mem_size);
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = tmp_ptr;
+ tmp_ptr = NULL;
+ dbg_ptr = entry->mem_ptr + entry->mem_size;
+ entry->mem_size += 0x4000;
+ end_ptr = entry->mem_ptr + entry->mem_size;
+ }
+ }
+ if (stat == RDWR_STATUS_DONE) {
+ entry->mem_size = dbg_ptr - entry->mem_ptr;
+ nxpwifi_dbg(adapter, DUMP, "dump %s done size=0x%x\n",
+ entry->mem_name, entry->mem_size);
+ ret = 0;
+ break;
+ }
+ }
+ nxpwifi_dbg(adapter, MSG, "== nxpwifi firmware dump end ==\n");
+
+done:
+ if (ret) {
+ nxpwifi_dbg(adapter, ERROR, "firmware dump failed\n");
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+ }
+ sdio_release_host(card->func);
+}
+
+static void nxpwifi_sdio_device_dump_work(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+
+ adapter->devdump_data = vzalloc(NXPWIFI_FW_DUMP_SIZE);
+ if (!adapter->devdump_data)
+ return;
+
+ nxpwifi_drv_info_dump(adapter);
+ if (card->fw_dump_enh)
+ nxpwifi_sdio_generic_fw_dump(adapter);
+ else
+ nxpwifi_sdio_fw_dump(adapter);
+ nxpwifi_prepare_fw_dump_info(adapter);
+ nxpwifi_upload_device_dump(adapter);
+}
+
+static void nxpwifi_sdio_work(struct work_struct *work)
+{
+ struct sdio_mmc_card *card =
+ container_of(work, struct sdio_mmc_card, work);
+
+ if (test_and_clear_bit(NXPWIFI_IFACE_WORK_DEVICE_DUMP,
+ &card->work_flags))
+ nxpwifi_sdio_device_dump_work(card->adapter);
+ if (test_and_clear_bit(NXPWIFI_IFACE_WORK_CARD_RESET,
+ &card->work_flags))
+ nxpwifi_sdio_card_reset_work(card->adapter);
+}
+
+/* This function resets the card */
+static void nxpwifi_sdio_card_reset(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+
+ if (!test_and_set_bit(NXPWIFI_IFACE_WORK_CARD_RESET, &card->work_flags))
+ schedule_work(&card->work);
+}
+
+/* This function dumps FW information */
+static void nxpwifi_sdio_device_dump(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+
+ if (!test_and_set_bit(NXPWIFI_IFACE_WORK_DEVICE_DUMP,
+ &card->work_flags))
+ schedule_work(&card->work);
+}
+
+/* Function to dump SDIO function registers and SDIO scratch registers in case
+ * of FW crash
+ */
+static int
+nxpwifi_sdio_reg_dump(struct nxpwifi_adapter *adapter, char *drv_buf)
+{
+ char *p = drv_buf;
+ struct sdio_mmc_card *cardp = adapter->card;
+ int ret = 0;
+ u8 count, func, data, index = 0, size = 0;
+ u8 reg, reg_start, reg_end;
+ char buf[256], *ptr;
+
+ if (!p)
+ return 0;
+
+ nxpwifi_dbg(adapter, MSG, "SDIO register dump start\n");
+
+ nxpwifi_pm_wakeup_card(adapter);
+
+ sdio_claim_host(cardp->func);
+
+ for (count = 0; count < 5; count++) {
+ memset(buf, 0, sizeof(buf));
+ ptr = buf;
+
+ switch (count) {
+ case 0:
+ /* Read the registers of SDIO function0 */
+ func = count;
+ reg_start = 0;
+ reg_end = 9;
+ break;
+ case 1:
+ /* Read the registers of SDIO function1 */
+ func = count;
+ reg_start = cardp->reg->func1_dump_reg_start;
+ reg_end = cardp->reg->func1_dump_reg_end;
+ break;
+ case 2:
+ index = 0;
+ func = 1;
+ reg_start = cardp->reg->func1_spec_reg_table[index++];
+ size = cardp->reg->func1_spec_reg_num;
+ reg_end = cardp->reg->func1_spec_reg_table[size - 1];
+ break;
+ default:
+ /* Read the scratch registers of SDIO function1 */
+ if (count == 4)
+ mdelay(100);
+ func = 1;
+ reg_start = cardp->reg->func1_scratch_reg;
+ reg_end = reg_start + NXPWIFI_SDIO_SCRATCH_SIZE;
+ }
+
+ if (count != 2)
+ ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ",
+ func, reg_start, reg_end);
+ else
+ ptr += sprintf(ptr, "SDIO Func%d: ", func);
+
+ for (reg = reg_start; reg <= reg_end;) {
+ if (func == 0)
+ data = sdio_f0_readb(cardp->func, reg, &ret);
+ else
+ data = sdio_readb(cardp->func, reg, &ret);
+
+ if (count == 2)
+ ptr += sprintf(ptr, "(%#x) ", reg);
+ if (!ret) {
+ ptr += sprintf(ptr, "%02x ", data);
+ } else {
+ ptr += sprintf(ptr, "ERR");
+ break;
+ }
+
+ if (count == 2 && reg < reg_end)
+ reg = cardp->reg->func1_spec_reg_table[index++];
+ else
+ reg++;
+ }
+
+ nxpwifi_dbg(adapter, MSG, "%s\n", buf);
+ p += sprintf(p, "%s\n", buf);
+ }
+
+ sdio_release_host(cardp->func);
+
+ nxpwifi_dbg(adapter, MSG, "SDIO register dump end\n");
+
+ return p - drv_buf;
+}
+
+/* sdio device/function initialization, code is extracted
+ * from init_if handler and register_dev handler.
+ */
+static void nxpwifi_sdio_up_dev(struct nxpwifi_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ u8 sdio_ireg;
+
+ sdio_claim_host(card->func);
+ sdio_enable_func(card->func);
+ sdio_set_block_size(card->func, NXPWIFI_SDIO_BLOCK_SIZE);
+ sdio_release_host(card->func);
+
+ /* tx_buf_size might be changed to 3584 by firmware during
+ * data transfer, we will reset to default size.
+ */
+ adapter->tx_buf_size = card->tx_buf_size;
+
+ /* Read the host_int_status_reg for ACK the first interrupt got
+ * from the bootloader. If we don't do this we get a interrupt
+ * as soon as we register the irq.
+ */
+ nxpwifi_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg);
+
+ if (nxpwifi_init_sdio_ioport(adapter))
+ dev_err(&card->func->dev, "error enabling SDIO port\n");
+}
+
+static struct nxpwifi_if_ops sdio_ops = {
+ .init_if = nxpwifi_init_sdio,
+ .cleanup_if = nxpwifi_cleanup_sdio,
+ .check_fw_status = nxpwifi_check_fw_status,
+ .check_winner_status = nxpwifi_check_winner_status,
+ .prog_fw = nxpwifi_prog_fw_w_helper,
+ .register_dev = nxpwifi_register_dev,
+ .unregister_dev = nxpwifi_unregister_dev,
+ .enable_int = nxpwifi_sdio_enable_host_int,
+ .disable_int = nxpwifi_sdio_disable_host_int,
+ .process_int_status = nxpwifi_process_int_status,
+ .host_to_card = nxpwifi_sdio_host_to_card,
+ .wakeup = nxpwifi_pm_wakeup_card,
+ .wakeup_complete = nxpwifi_pm_wakeup_card_complete,
+
+ /* SDIO specific */
+ .update_mp_end_port = nxpwifi_update_mp_end_port,
+ .cleanup_mpa_buf = nxpwifi_cleanup_mpa_buf,
+ .cmdrsp_complete = nxpwifi_sdio_cmdrsp_complete,
+ .event_complete = nxpwifi_sdio_event_complete,
+ .dnld_fw = nxpwifi_sdio_dnld_fw,
+ .card_reset = nxpwifi_sdio_card_reset,
+ .reg_dump = nxpwifi_sdio_reg_dump,
+ .device_dump = nxpwifi_sdio_device_dump,
+ .deaggr_pkt = nxpwifi_deaggr_sdio_pkt,
+ .up_dev = nxpwifi_sdio_up_dev,
+};
+
+module_driver(nxpwifi_sdio, sdio_register_driver, sdio_unregister_driver);
+
+MODULE_AUTHOR("NXP International Ltd.");
+MODULE_DESCRIPTION("NXP WiFi SDIO Driver version " SDIO_VERSION);
+MODULE_VERSION(SDIO_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(IW61X_SDIO_FW_NAME);
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* Re: [EXTERNAL] [PATCH 26/43] wifi: nxpwifi: add sdio.c
2024-06-21 7:51 ` [PATCH 26/43] wifi: nxpwifi: add sdio.c David Lin
@ 2024-06-26 11:40 ` Nemanov, Michael
2024-06-27 3:37 ` [EXT] " David Lin
0 siblings, 1 reply; 56+ messages in thread
From: Nemanov, Michael @ 2024-06-26 11:40 UTC (permalink / raw)
To: David Lin, linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh
On 6/21/2024 10:51 AM, David Lin wrote:
...
> +
> +/* This function unregisters the SDIO device.
> + *
> + * The SDIO IRQ is released, the function is disabled and driver
> + * data is set to null.
> + */
> +static void
> +nxpwifi_unregister_dev(struct nxpwifi_adapter *adapter)
> +{
> + struct sdio_mmc_card *card = adapter->card;
> +
> + if (adapter->card) {
> + card->adapter = NULL;
> + sdio_claim_host(card->func);
> + sdio_disable_func(card->func);
> + sdio_release_host(card->func);
> + }
> +}
Missing call to sdio_release_irq() ?
Michael.
^ permalink raw reply [flat|nested] 56+ messages in thread* RE: [EXT] Re: [EXTERNAL] [PATCH 26/43] wifi: nxpwifi: add sdio.c
2024-06-26 11:40 ` [EXTERNAL] " Nemanov, Michael
@ 2024-06-27 3:37 ` David Lin
2024-06-27 6:26 ` Nemanov, Michael
0 siblings, 1 reply; 56+ messages in thread
From: David Lin @ 2024-06-27 3:37 UTC (permalink / raw)
To: Nemanov, Michael, linux-wireless@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org,
kvalo@kernel.org, francesco@dolcini.it, Pete Hsieh
>
> On 6/21/2024 10:51 AM, David Lin wrote:
> ...
> > +
> > +/* This function unregisters the SDIO device.
> > + *
> > + * The SDIO IRQ is released, the function is disabled and driver
> > + * data is set to null.
> > + */
> > +static void
> > +nxpwifi_unregister_dev(struct nxpwifi_adapter *adapter) {
> > + struct sdio_mmc_card *card = adapter->card;
> > +
> > + if (adapter->card) {
> > + card->adapter = NULL;
> > + sdio_claim_host(card->func);
> > + sdio_disable_func(card->func);
> > + sdio_release_host(card->func);
> > + }
> > +}
>
> Missing call to sdio_release_irq() ?
>
> Michael.
sdio_release_irq() is called by nxpwifi_sdio_disable_host_int().
David
^ permalink raw reply [flat|nested] 56+ messages in thread* [PATCH 26/43] wifi: nxpwifi: add sdio.c
2024-06-27 3:37 ` [EXT] " David Lin
@ 2024-06-27 6:26 ` Nemanov, Michael
2024-06-27 6:33 ` [EXT] " David Lin
0 siblings, 1 reply; 56+ messages in thread
From: Nemanov, Michael @ 2024-06-27 6:26 UTC (permalink / raw)
To: David Lin, linux-wireless@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org,
kvalo@kernel.org, francesco@dolcini.it, Pete Hsieh
On 6/27/2024 6:37 AM, David Lin wrote:
>>
>> On 6/21/2024 10:51 AM, David Lin wrote:
>> ...
>>> +
>>> +/* This function unregisters the SDIO device.
>>> + *
>>> + * The SDIO IRQ is released, the function is disabled and driver
>>> + * data is set to null.
>>> + */
>>> +static void
>>> +nxpwifi_unregister_dev(struct nxpwifi_adapter *adapter) {
>>> + struct sdio_mmc_card *card = adapter->card;
>>> +
>>> + if (adapter->card) {
>>> + card->adapter = NULL;
>>> + sdio_claim_host(card->func);
>>> + sdio_disable_func(card->func);
>>> + sdio_release_host(card->func);
>>> + }
>>> +}
>>
>> Missing call to sdio_release_irq() ?
>>
>> Michael.
>
> sdio_release_irq() is called by nxpwifi_sdio_disable_host_int().
>
> David
Right, I see you calling it from nxpwifi_uninit_sw(). Maybe just align
the comment then.
Michael.
^ permalink raw reply [flat|nested] 56+ messages in thread* RE: [EXT] [PATCH 26/43] wifi: nxpwifi: add sdio.c
2024-06-27 6:26 ` Nemanov, Michael
@ 2024-06-27 6:33 ` David Lin
0 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-27 6:33 UTC (permalink / raw)
To: Nemanov, Michael, linux-wireless@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org,
kvalo@kernel.org, francesco@dolcini.it, Pete Hsieh
> -----Original Message-----
> From: Nemanov, Michael <michael.nemanov@ti.com>
> Sent: Thursday, June 27, 2024 2:27 PM
> To: David Lin <yu-hao.lin@nxp.com>; linux-wireless@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; briannorris@chromium.org;
> kvalo@kernel.org; francesco@dolcini.it; Pete Hsieh
> <tsung-hsien.hsieh@nxp.com>
> Subject: [EXT] [PATCH 26/43] wifi: nxpwifi: add sdio.c
>
> Caution: This is an external email. Please take care when clicking links or
> opening attachments. When in doubt, report the message using the 'Report
> this email' button
>
>
> On 6/27/2024 6:37 AM, David Lin wrote:
> >>
> >> On 6/21/2024 10:51 AM, David Lin wrote:
> >> ...
> >>> +
> >>> +/* This function unregisters the SDIO device.
> >>> + *
> >>> + * The SDIO IRQ is released, the function is disabled and driver
> >>> + * data is set to null.
> >>> + */
> >>> +static void
> >>> +nxpwifi_unregister_dev(struct nxpwifi_adapter *adapter) {
> >>> + struct sdio_mmc_card *card = adapter->card;
> >>> +
> >>> + if (adapter->card) {
> >>> + card->adapter = NULL;
> >>> + sdio_claim_host(card->func);
> >>> + sdio_disable_func(card->func);
> >>> + sdio_release_host(card->func);
> >>> + }
> >>> +}
> >>
> >> Missing call to sdio_release_irq() ?
> >>
> >> Michael.
> >
> > sdio_release_irq() is called by nxpwifi_sdio_disable_host_int().
> >
> > David
>
> Right, I see you calling it from nxpwifi_uninit_sw(). Maybe just align the
> comment then.
>
> Michael.
I will modify the comment.
Thanks,
David
^ permalink raw reply [flat|nested] 56+ messages in thread
* [PATCH 27/43] wifi: nxpwifi: add sdio.h
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (25 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 26/43] wifi: nxpwifi: add sdio.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 28/43] wifi: nxpwifi: add sta_cmd.c David Lin
` (17 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/sdio.h | 340 ++++++++++++++++++++++++
1 file changed, 340 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/sdio.h
diff --git a/drivers/net/wireless/nxp/nxpwifi/sdio.h b/drivers/net/wireless/nxp/nxpwifi/sdio.h
new file mode 100644
index 000000000000..de5c884a5b14
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/sdio.h
@@ -0,0 +1,340 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NXP Wireless LAN device driver: SDIO specific definitions
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#ifndef _NXPWIFI_SDIO_H
+#define _NXPWIFI_SDIO_H
+
+#include "main.h"
+
+#define IW61X_SDIO_FW_NAME "nxp/sd_w61x_v1.bin.se"
+
+#define BLOCK_MODE 1
+#define BYTE_MODE 0
+
+#define NXPWIFI_SDIO_IO_PORT_MASK 0xfffff
+
+#define NXPWIFI_SDIO_BYTE_MODE_MASK 0x80000000
+
+#define NXPWIFI_MAX_FUNC2_REG_NUM 13
+#define NXPWIFI_SDIO_SCRATCH_SIZE 10
+
+#define SDIO_MPA_ADDR_BASE 0x1000
+
+#define CMD_PORT_UPLD_INT_MASK (0x1U << 6)
+#define CMD_PORT_DNLD_INT_MASK (0x1U << 7)
+#define HOST_TERM_CMD53 (0x1U << 2)
+#define REG_PORT 0
+#define MEM_PORT 0x10000
+
+#define CMD53_NEW_MODE (0x1U << 0)
+#define CMD_PORT_RD_LEN_EN (0x1U << 2)
+#define CMD_PORT_AUTO_EN (0x1U << 0)
+#define CMD_PORT_SLCT 0x8000
+#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U)
+#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U)
+
+#define NXPWIFI_MP_AGGR_BSIZE_32K (32768)
+/* we leave one block of 256 bytes for DMA alignment*/
+#define NXPWIFI_MP_AGGR_BSIZE_MAX (65280)
+
+/* Misc. Config Register : Auto Re-enable interrupts */
+#define AUTO_RE_ENABLE_INT BIT(4)
+
+/* Host Control Registers : Configuration */
+#define CONFIGURATION_REG 0x00
+/* Host Control Registers : Host power up */
+#define HOST_POWER_UP (0x1U << 1)
+
+/* Host Control Registers : Upload host interrupt mask */
+#define UP_LD_HOST_INT_MASK (0x1U)
+/* Host Control Registers : Download host interrupt mask */
+#define DN_LD_HOST_INT_MASK (0x2U)
+
+/* Host Control Registers : Upload host interrupt status */
+#define UP_LD_HOST_INT_STATUS (0x1U)
+/* Host Control Registers : Download host interrupt status */
+#define DN_LD_HOST_INT_STATUS (0x2U)
+
+/* Host Control Registers : Host interrupt status */
+#define CARD_INT_STATUS_REG 0x28
+
+/* Card Control Registers : Card I/O ready */
+#define CARD_IO_READY (0x1U << 3)
+/* Card Control Registers : Download card ready */
+#define DN_LD_CARD_RDY (0x1U << 0)
+
+/* Max retry number of CMD53 write */
+#define MAX_WRITE_IOMEM_RETRY 2
+
+/* SDIO Tx aggregation in progress ? */
+#define MP_TX_AGGR_IN_PROGRESS(a) ((a)->mpa_tx.pkt_cnt > 0)
+
+/* SDIO Tx aggregation buffer room for next packet ? */
+#define MP_TX_AGGR_BUF_HAS_ROOM(a, len) ({ \
+ typeof(a) (_a) = a; \
+ (((_a)->mpa_tx.buf_len + (len)) <= (_a)->mpa_tx.buf_size); \
+ })
+
+/* Copy current packet (SDIO Tx aggregation buffer) to SDIO buffer */
+#define MP_TX_AGGR_BUF_PUT(a, payload, pkt_len, port) do { \
+ typeof(a) (_a) = (a); \
+ typeof(pkt_len) (_pkt_len) = pkt_len; \
+ typeof(port) (_port) = port; \
+ memmove(&(_a)->mpa_tx.buf[(_a)->mpa_tx.buf_len], \
+ payload, (_pkt_len)); \
+ (_a)->mpa_tx.buf_len += (_pkt_len); \
+ if (!(_a)->mpa_tx.pkt_cnt) \
+ (_a)->mpa_tx.start_port = (_port); \
+ if ((_a)->mpa_tx.start_port <= (_port)) \
+ (_a)->mpa_tx.ports |= (1 << ((_a)->mpa_tx.pkt_cnt)); \
+ else \
+ (_a)->mpa_tx.ports |= (1 << ((_a)->mpa_tx.pkt_cnt + 1 + \
+ ((_a)->max_ports - \
+ (_a)->mp_end_port))); \
+ (_a)->mpa_tx.pkt_cnt++; \
+} while (0)
+
+/* SDIO Tx aggregation limit ? */
+#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) ({ \
+ typeof(a) (_a) = a; \
+ ((_a)->mpa_tx.pkt_cnt == (_a)->mpa_tx.pkt_aggr_limit); \
+ })
+
+/* Reset SDIO Tx aggregation buffer parameters */
+#define MP_TX_AGGR_BUF_RESET(a) do { \
+ typeof(a) (_a) = (a); \
+ (_a)->mpa_tx.pkt_cnt = 0; \
+ (_a)->mpa_tx.buf_len = 0; \
+ (_a)->mpa_tx.ports = 0; \
+ (_a)->mpa_tx.start_port = 0; \
+} while (0)
+
+/* SDIO Rx aggregation limit ? */
+#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) ({ \
+ typeof(a) (_a) = a; \
+ ((_a)->mpa_rx.pkt_cnt == (_a)->mpa_rx.pkt_aggr_limit); \
+ })
+
+/* SDIO Rx aggregation in progress ? */
+#define MP_RX_AGGR_IN_PROGRESS(a) ((a)->mpa_rx.pkt_cnt > 0)
+
+/* SDIO Rx aggregation buffer room for next packet ? */
+#define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) ({ \
+ typeof(a) (_a) = a; \
+ ((((_a)->mpa_rx.buf_len + (rx_len))) <= (_a)->mpa_rx.buf_size); \
+ })
+
+/* Reset SDIO Rx aggregation buffer parameters */
+#define MP_RX_AGGR_BUF_RESET(a) do { \
+ typeof(a) (_a) = (a); \
+ (_a)->mpa_rx.pkt_cnt = 0; \
+ (_a)->mpa_rx.buf_len = 0; \
+ (_a)->mpa_rx.ports = 0; \
+ (_a)->mpa_rx.start_port = 0; \
+} while (0)
+
+/* data structure for SDIO MPA TX */
+struct nxpwifi_sdio_mpa_tx {
+ /* multiport tx aggregation buffer pointer */
+ u8 *buf;
+ u32 buf_len;
+ u32 pkt_cnt;
+ u32 ports;
+ u16 start_port;
+ u8 enabled;
+ u32 buf_size;
+ u32 pkt_aggr_limit;
+};
+
+struct nxpwifi_sdio_mpa_rx {
+ u8 *buf;
+ u32 buf_len;
+ u32 pkt_cnt;
+ u32 ports;
+ u16 start_port;
+ u32 *len_arr;
+ u8 enabled;
+ u32 buf_size;
+ u32 pkt_aggr_limit;
+};
+
+int nxpwifi_bus_register(void);
+void nxpwifi_bus_unregister(void);
+
+struct nxpwifi_sdio_card_reg {
+ u8 start_rd_port;
+ u8 start_wr_port;
+ u8 base_0_reg;
+ u8 base_1_reg;
+ u8 poll_reg;
+ u8 host_int_enable;
+ u8 host_int_rsr_reg;
+ u8 host_int_status_reg;
+ u8 host_int_mask_reg;
+ u8 host_strap_reg;
+ u8 host_strap_mask;
+ u8 host_strap_value;
+ u8 status_reg_0;
+ u8 status_reg_1;
+ u8 sdio_int_mask;
+ u32 data_port_mask;
+ u8 io_port_0_reg;
+ u8 io_port_1_reg;
+ u8 io_port_2_reg;
+ u8 max_mp_regs;
+ u8 rd_bitmap_l;
+ u8 rd_bitmap_u;
+ u8 rd_bitmap_1l;
+ u8 rd_bitmap_1u;
+ u8 wr_bitmap_l;
+ u8 wr_bitmap_u;
+ u8 wr_bitmap_1l;
+ u8 wr_bitmap_1u;
+ u8 rd_len_p0_l;
+ u8 rd_len_p0_u;
+ u8 card_misc_cfg_reg;
+ u8 card_cfg_2_1_reg;
+ u8 cmd_rd_len_0;
+ u8 cmd_rd_len_1;
+ u8 cmd_rd_len_2;
+ u8 cmd_rd_len_3;
+ u8 cmd_cfg_0;
+ u8 cmd_cfg_1;
+ u8 cmd_cfg_2;
+ u8 cmd_cfg_3;
+ u8 fw_dump_host_ready;
+ u8 fw_dump_ctrl;
+ u8 fw_dump_start;
+ u8 fw_dump_end;
+ u8 func1_dump_reg_start;
+ u8 func1_dump_reg_end;
+ u8 func1_scratch_reg;
+ u8 func1_spec_reg_num;
+ u8 func1_spec_reg_table[NXPWIFI_MAX_FUNC2_REG_NUM];
+};
+
+struct sdio_mmc_card {
+ struct sdio_func *func;
+ struct nxpwifi_adapter *adapter;
+
+ struct completion fw_done;
+ const char *firmware;
+ const char *firmware_sdiouart;
+ const struct nxpwifi_sdio_card_reg *reg;
+ u8 max_ports;
+ u8 mp_agg_pkt_limit;
+ u16 tx_buf_size;
+ u32 mp_tx_agg_buf_size;
+ u32 mp_rx_agg_buf_size;
+
+ u32 mp_rd_bitmap;
+ u32 mp_wr_bitmap;
+
+ u16 mp_end_port;
+ u32 mp_data_port_mask;
+
+ u8 curr_rd_port;
+ u8 curr_wr_port;
+
+ u8 *mp_regs;
+ bool can_dump_fw;
+ bool fw_dump_enh;
+ bool can_ext_scan;
+
+ struct nxpwifi_sdio_mpa_tx mpa_tx;
+ struct nxpwifi_sdio_mpa_rx mpa_rx;
+
+ struct work_struct work;
+ unsigned long work_flags;
+};
+
+struct nxpwifi_sdio_device {
+ const char *firmware;
+ const char *firmware_sdiouart;
+ const struct nxpwifi_sdio_card_reg *reg;
+ u8 max_ports;
+ u8 mp_agg_pkt_limit;
+ u16 tx_buf_size;
+ u32 mp_tx_agg_buf_size;
+ u32 mp_rx_agg_buf_size;
+ bool can_dump_fw;
+ bool fw_dump_enh;
+ bool can_ext_scan;
+};
+
+/* .cmdrsp_complete handler
+ */
+static inline int nxpwifi_sdio_cmdrsp_complete(struct nxpwifi_adapter *adapter,
+ struct sk_buff *skb)
+{
+ dev_kfree_skb_any(skb);
+ return 0;
+}
+
+/* .event_complete handler
+ */
+static inline int nxpwifi_sdio_event_complete(struct nxpwifi_adapter *adapter,
+ struct sk_buff *skb)
+{
+ dev_kfree_skb_any(skb);
+ return 0;
+}
+
+static inline bool
+mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card)
+{
+ u8 tmp;
+
+ if (card->curr_rd_port < card->mpa_rx.start_port) {
+ tmp = card->mp_end_port >> 1;
+
+ if (((card->max_ports - card->mpa_rx.start_port) +
+ card->curr_rd_port) >= tmp)
+ return true;
+ }
+
+ if ((card->curr_rd_port - card->mpa_rx.start_port) >=
+ (card->mp_end_port >> 1))
+ return true;
+
+ return false;
+}
+
+static inline bool
+mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card)
+{
+ u16 tmp;
+
+ if (card->curr_wr_port < card->mpa_tx.start_port) {
+ tmp = card->mp_end_port >> 1;
+
+ if (((card->max_ports - card->mpa_tx.start_port) +
+ card->curr_wr_port) >= tmp)
+ return true;
+ }
+
+ if ((card->curr_wr_port - card->mpa_tx.start_port) >=
+ (card->mp_end_port >> 1))
+ return true;
+
+ return false;
+}
+
+/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
+static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card,
+ u16 rx_len, u8 port)
+{
+ card->mpa_rx.buf_len += rx_len;
+
+ if (!card->mpa_rx.pkt_cnt)
+ card->mpa_rx.start_port = port;
+
+ card->mpa_rx.ports |= (1 << port);
+ card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = rx_len;
+ card->mpa_rx.pkt_cnt++;
+}
+#endif /* _NXPWIFI_SDIO_H */
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 28/43] wifi: nxpwifi: add sta_cmd.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (26 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 27/43] wifi: nxpwifi: add sdio.h David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 29/43] wifi: nxpwifi: add sta_event.c David Lin
` (16 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/sta_cmd.c | 3229 ++++++++++++++++++++
1 file changed, 3229 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/sta_cmd.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c b/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c
new file mode 100644
index 000000000000..a570949ceac5
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/sta_cmd.c
@@ -0,0 +1,3229 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: station command handling
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cmdevt.h"
+#include "wmm.h"
+#include "11n.h"
+#include "11ac.h"
+
+static bool disable_auto_ds;
+
+static int
+nxpwifi_cmd_sta_get_hw_spec(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec;
+
+ cmd->command = cpu_to_le16(HOST_CMD_GET_HW_SPEC);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) +
+ S_DS_GEN);
+ memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_get_hw_spec(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_ie_types_header *tlv;
+ struct hw_spec_api_rev *api_rev;
+ struct hw_spec_max_conn *max_conn;
+ u16 resp_size, api_id;
+ int i, left_len, parsed_len = 0;
+
+ adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info);
+
+ if (IS_SUPPORT_MULTI_BANDS(adapter))
+ adapter->fw_bands = (u8)GET_FW_DEFAULT_BANDS(adapter);
+ else
+ adapter->fw_bands = BAND_B;
+
+ adapter->config_bands = adapter->fw_bands;
+
+ if (adapter->fw_bands & BAND_A) {
+ if (adapter->fw_bands & BAND_GN) {
+ adapter->config_bands |= BAND_AN;
+ adapter->fw_bands |= BAND_AN;
+ }
+ }
+
+ adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number);
+ adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff;
+ adapter->number_of_antenna =
+ le16_to_cpu(hw_spec->number_of_antenna) & 0xf;
+
+ if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) {
+ adapter->is_hw_11ac_capable = true;
+
+ /* Copy 11AC cap */
+ adapter->hw_dot_11ac_dev_cap =
+ le32_to_cpu(hw_spec->dot_11ac_dev_cap);
+ adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap
+ & ~NXPWIFI_DEF_11AC_CAP_BF_RESET_MASK;
+ adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap
+ & ~NXPWIFI_DEF_11AC_CAP_BF_RESET_MASK;
+
+ /* Copy 11AC mcs */
+ adapter->hw_dot_11ac_mcs_support =
+ le32_to_cpu(hw_spec->dot_11ac_mcs_support);
+ adapter->usr_dot_11ac_mcs_support =
+ adapter->hw_dot_11ac_mcs_support;
+ } else {
+ adapter->is_hw_11ac_capable = false;
+ }
+
+ resp_size = le16_to_cpu(resp->size) - S_DS_GEN;
+ if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) {
+ /* we have variable HW SPEC information */
+ left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec);
+ while (left_len > sizeof(struct nxpwifi_ie_types_header)) {
+ tlv = (void *)&hw_spec->tlvs + parsed_len;
+ switch (le16_to_cpu(tlv->type)) {
+ case TLV_TYPE_API_REV:
+ api_rev = (struct hw_spec_api_rev *)tlv;
+ api_id = le16_to_cpu(api_rev->api_id);
+ switch (api_id) {
+ case KEY_API_VER_ID:
+ adapter->key_api_major_ver =
+ api_rev->major_ver;
+ adapter->key_api_minor_ver =
+ api_rev->minor_ver;
+ nxpwifi_dbg(adapter, INFO,
+ "key_api v%d.%d\n",
+ adapter->key_api_major_ver,
+ adapter->key_api_minor_ver);
+ break;
+ case FW_API_VER_ID:
+ adapter->fw_api_ver =
+ api_rev->major_ver;
+ nxpwifi_dbg(adapter, INFO,
+ "Firmware api version %d.%d\n",
+ adapter->fw_api_ver,
+ api_rev->minor_ver);
+ break;
+ case UAP_FW_API_VER_ID:
+ nxpwifi_dbg(adapter, INFO,
+ "uAP api version %d.%d\n",
+ api_rev->major_ver,
+ api_rev->minor_ver);
+ break;
+ case CHANRPT_API_VER_ID:
+ nxpwifi_dbg(adapter, INFO,
+ "channel report api version %d.%d\n",
+ api_rev->major_ver,
+ api_rev->minor_ver);
+ break;
+ case FW_HOTFIX_VER_ID:
+ nxpwifi_dbg(adapter, INFO,
+ "Firmware hotfix version %d\n",
+ api_rev->major_ver);
+ break;
+ default:
+ nxpwifi_dbg(adapter, FATAL,
+ "Unknown api_id: %d\n",
+ api_id);
+ break;
+ }
+ break;
+ case TLV_TYPE_MAX_CONN:
+ max_conn = (struct hw_spec_max_conn *)tlv;
+ adapter->max_sta_conn = max_conn->max_sta_conn;
+ nxpwifi_dbg(adapter, INFO,
+ "max sta connections: %u\n",
+ adapter->max_sta_conn);
+ break;
+ default:
+ nxpwifi_dbg(adapter, FATAL,
+ "Unknown GET_HW_SPEC TLV type: %#x\n",
+ le16_to_cpu(tlv->type));
+ break;
+ }
+ parsed_len += le16_to_cpu(tlv->len) +
+ sizeof(struct nxpwifi_ie_types_header);
+ left_len -= le16_to_cpu(tlv->len) +
+ sizeof(struct nxpwifi_ie_types_header);
+ }
+ }
+
+ if (adapter->key_api_major_ver < KEY_API_VER_MAJOR_V2)
+ return -EOPNOTSUPP;
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: GET_HW_SPEC: fw_release_number- %#x\n",
+ adapter->fw_release_number);
+ nxpwifi_dbg(adapter, INFO,
+ "info: GET_HW_SPEC: permanent addr: %pM\n",
+ hw_spec->permanent_addr);
+ nxpwifi_dbg(adapter, INFO,
+ "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n",
+ le16_to_cpu(hw_spec->hw_if_version),
+ le16_to_cpu(hw_spec->version));
+
+ ether_addr_copy(priv->adapter->perm_addr, hw_spec->permanent_addr);
+ adapter->region_code = le16_to_cpu(hw_spec->region_code);
+
+ for (i = 0; i < NXPWIFI_MAX_REGION_CODE; i++)
+ /* Use the region code to search for the index */
+ if (adapter->region_code == region_code_index[i])
+ break;
+
+ /* If it's unidentified region code, use the default (world) */
+ if (i >= NXPWIFI_MAX_REGION_CODE) {
+ adapter->region_code = 0x00;
+ nxpwifi_dbg(adapter, WARN,
+ "cmd: unknown region code, use default (USA)\n");
+ }
+
+ adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap);
+ adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support;
+ adapter->user_dev_mcs_support = adapter->hw_dev_mcs_support;
+
+ if (adapter->if_ops.update_mp_end_port) {
+ u16 mp_end_port;
+
+ mp_end_port = le16_to_cpu(hw_spec->mp_end_port);
+ adapter->if_ops.update_mp_end_port(adapter, mp_end_port);
+ }
+
+ if (adapter->fw_api_ver == NXPWIFI_FW_V15)
+ adapter->scan_chan_gap_enabled = true;
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_802_11_scan(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_802_11_scan(cmd, data_buf);
+}
+
+static int
+nxpwifi_ret_sta_802_11_scan(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int ret;
+
+ ret = nxpwifi_ret_802_11_scan(priv, resp);
+ adapter->curr_cmd->wait_q_enabled = false;
+
+ return ret;
+}
+
+static int
+nxpwifi_cmd_sta_802_11_get_log(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_GET_LOG);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_get_log) +
+ S_DS_GEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_802_11_get_log(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_802_11_get_log *get_log =
+ &resp->params.get_log;
+ struct nxpwifi_ds_get_stats *stats =
+ (struct nxpwifi_ds_get_stats *)data_buf;
+
+ if (stats) {
+ stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame);
+ stats->failed = le32_to_cpu(get_log->failed);
+ stats->retry = le32_to_cpu(get_log->retry);
+ stats->multi_retry = le32_to_cpu(get_log->multi_retry);
+ stats->frame_dup = le32_to_cpu(get_log->frame_dup);
+ stats->rts_success = le32_to_cpu(get_log->rts_success);
+ stats->rts_failure = le32_to_cpu(get_log->rts_failure);
+ stats->ack_failure = le32_to_cpu(get_log->ack_failure);
+ stats->rx_frag = le32_to_cpu(get_log->rx_frag);
+ stats->mcast_rx_frame = le32_to_cpu(get_log->mcast_rx_frame);
+ stats->fcs_error = le32_to_cpu(get_log->fcs_error);
+ stats->tx_frame = le32_to_cpu(get_log->tx_frame);
+ stats->wep_icv_error[0] =
+ le32_to_cpu(get_log->wep_icv_err_cnt[0]);
+ stats->wep_icv_error[1] =
+ le32_to_cpu(get_log->wep_icv_err_cnt[1]);
+ stats->wep_icv_error[2] =
+ le32_to_cpu(get_log->wep_icv_err_cnt[2]);
+ stats->wep_icv_error[3] =
+ le32_to_cpu(get_log->wep_icv_err_cnt[3]);
+ stats->bcn_rcv_cnt = le32_to_cpu(get_log->bcn_rcv_cnt);
+ stats->bcn_miss_cnt = le32_to_cpu(get_log->bcn_miss_cnt);
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_mac_multicast_adr(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_mac_multicast_adr *mcast_addr = &cmd->params.mc_addr;
+ struct nxpwifi_multicast_list *mcast_list =
+ (struct nxpwifi_multicast_list *)data_buf;
+
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_multicast_adr) +
+ S_DS_GEN);
+ cmd->command = cpu_to_le16(HOST_CMD_MAC_MULTICAST_ADR);
+
+ mcast_addr->action = cpu_to_le16(cmd_action);
+ mcast_addr->num_of_adrs =
+ cpu_to_le16((u16)mcast_list->num_multicast_addr);
+ memcpy(mcast_addr->mac_list, mcast_list->mac_list,
+ mcast_list->num_multicast_addr * ETH_ALEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_802_11_associate(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_802_11_associate(priv, cmd, data_buf);
+}
+
+static int
+nxpwifi_ret_sta_802_11_associate(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ return nxpwifi_ret_802_11_associate(priv, resp);
+}
+
+static int
+nxpwifi_cmd_sta_802_11_snmp_mib(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_802_11_snmp_mib *snmp_mib = &cmd->params.smib;
+ u16 *ul_temp = (u16 *)data_buf;
+
+ nxpwifi_dbg(priv->adapter, CMD,
+ "cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_type);
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_SNMP_MIB);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib)
+ + S_DS_GEN);
+
+ snmp_mib->oid = cpu_to_le16((u16)cmd_type);
+ if (cmd_action == HOST_ACT_GEN_GET) {
+ snmp_mib->query_type = cpu_to_le16(HOST_ACT_GEN_GET);
+ snmp_mib->buf_size = cpu_to_le16(MAX_SNMP_BUF_SIZE);
+ le16_unaligned_add_cpu(&cmd->size, MAX_SNMP_BUF_SIZE);
+ } else if (cmd_action == HOST_ACT_GEN_SET) {
+ snmp_mib->query_type = cpu_to_le16(HOST_ACT_GEN_SET);
+ snmp_mib->buf_size = cpu_to_le16(sizeof(u16));
+ put_unaligned_le16(*ul_temp, snmp_mib->value);
+ le16_unaligned_add_cpu(&cmd->size, sizeof(u16));
+ }
+
+ nxpwifi_dbg(priv->adapter, CMD,
+ "cmd: SNMP_CMD: Action=0x%x, OID=0x%x,\t"
+ "OIDSize=0x%x, Value=0x%x\n",
+ cmd_action, cmd_type, le16_to_cpu(snmp_mib->buf_size),
+ get_unaligned_le16(snmp_mib->value));
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_802_11_snmp_mib(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib;
+ u16 oid = le16_to_cpu(smib->oid);
+ u16 query_type = le16_to_cpu(smib->query_type);
+ u32 ul_temp;
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: SNMP_RESP: oid value = %#x,\t"
+ "query_type = %#x, buf size = %#x\n",
+ oid, query_type, le16_to_cpu(smib->buf_size));
+ if (query_type == HOST_ACT_GEN_GET) {
+ ul_temp = get_unaligned_le16(smib->value);
+ if (data_buf)
+ *(u32 *)data_buf = ul_temp;
+ switch (oid) {
+ case FRAG_THRESH_I:
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: SNMP_RESP: FragThsd =%u\n",
+ ul_temp);
+ break;
+ case RTS_THRESH_I:
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: SNMP_RESP: RTSThsd =%u\n",
+ ul_temp);
+ break;
+ case SHORT_RETRY_LIM_I:
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: SNMP_RESP: TxRetryCount=%u\n",
+ ul_temp);
+ break;
+ case DTIM_PERIOD_I:
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: SNMP_RESP: DTIM period=%u\n",
+ ul_temp);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int nxpwifi_cmd_sta_reg_access(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct nxpwifi_ds_reg_rw *reg_rw = data_buf;
+
+ cmd->command = cpu_to_le16(cmd_no);
+
+ switch (cmd_no) {
+ case HOST_CMD_MAC_REG_ACCESS:
+ {
+ struct host_cmd_ds_mac_reg_access *mac_reg;
+
+ cmd->size = cpu_to_le16(sizeof(*mac_reg) + S_DS_GEN);
+ mac_reg = &cmd->params.mac_reg;
+ mac_reg->action = cpu_to_le16(cmd_action);
+ mac_reg->offset = cpu_to_le16((u16)reg_rw->offset);
+ mac_reg->value = cpu_to_le32(reg_rw->value);
+ break;
+ }
+ case HOST_CMD_BBP_REG_ACCESS:
+ {
+ struct host_cmd_ds_bbp_reg_access *bbp_reg;
+
+ cmd->size = cpu_to_le16(sizeof(*bbp_reg) + S_DS_GEN);
+ bbp_reg = &cmd->params.bbp_reg;
+ bbp_reg->action = cpu_to_le16(cmd_action);
+ bbp_reg->offset = cpu_to_le16((u16)reg_rw->offset);
+ bbp_reg->value = (u8)reg_rw->value;
+ break;
+ }
+ case HOST_CMD_RF_REG_ACCESS:
+ {
+ struct host_cmd_ds_rf_reg_access *rf_reg;
+
+ cmd->size = cpu_to_le16(sizeof(*rf_reg) + S_DS_GEN);
+ rf_reg = &cmd->params.rf_reg;
+ rf_reg->action = cpu_to_le16(cmd_action);
+ rf_reg->offset = cpu_to_le16((u16)reg_rw->offset);
+ rf_reg->value = (u8)reg_rw->value;
+ break;
+ }
+ case HOST_CMD_PMIC_REG_ACCESS:
+ {
+ struct host_cmd_ds_pmic_reg_access *pmic_reg;
+
+ cmd->size = cpu_to_le16(sizeof(*pmic_reg) + S_DS_GEN);
+ pmic_reg = &cmd->params.pmic_reg;
+ pmic_reg->action = cpu_to_le16(cmd_action);
+ pmic_reg->offset = cpu_to_le16((u16)reg_rw->offset);
+ pmic_reg->value = (u8)reg_rw->value;
+ break;
+ }
+ case HOST_CMD_CAU_REG_ACCESS:
+ {
+ struct host_cmd_ds_rf_reg_access *cau_reg;
+
+ cmd->size = cpu_to_le16(sizeof(*cau_reg) + S_DS_GEN);
+ cau_reg = &cmd->params.rf_reg;
+ cau_reg->action = cpu_to_le16(cmd_action);
+ cau_reg->offset = cpu_to_le16((u16)reg_rw->offset);
+ cau_reg->value = (u8)reg_rw->value;
+ break;
+ }
+ case HOST_CMD_802_11_EEPROM_ACCESS:
+ {
+ struct nxpwifi_ds_read_eeprom *rd_eeprom = data_buf;
+ struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom =
+ &cmd->params.eeprom;
+
+ cmd->size = cpu_to_le16(sizeof(*cmd_eeprom) + S_DS_GEN);
+ cmd_eeprom->action = cpu_to_le16(cmd_action);
+ cmd_eeprom->offset = cpu_to_le16(rd_eeprom->offset);
+ cmd_eeprom->byte_count = cpu_to_le16(rd_eeprom->byte_count);
+ cmd_eeprom->value = 0;
+ break;
+ }
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_reg_access(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct nxpwifi_ds_reg_rw *reg_rw;
+ struct nxpwifi_ds_read_eeprom *eeprom;
+ union reg {
+ struct host_cmd_ds_mac_reg_access *mac;
+ struct host_cmd_ds_bbp_reg_access *bbp;
+ struct host_cmd_ds_rf_reg_access *rf;
+ struct host_cmd_ds_pmic_reg_access *pmic;
+ struct host_cmd_ds_802_11_eeprom_access *eeprom;
+ } r;
+
+ if (!data_buf)
+ return 0;
+
+ reg_rw = data_buf;
+ eeprom = data_buf;
+ switch (cmdresp_no) {
+ case HOST_CMD_MAC_REG_ACCESS:
+ r.mac = &resp->params.mac_reg;
+ reg_rw->offset = (u32)le16_to_cpu(r.mac->offset);
+ reg_rw->value = le32_to_cpu(r.mac->value);
+ break;
+ case HOST_CMD_BBP_REG_ACCESS:
+ r.bbp = &resp->params.bbp_reg;
+ reg_rw->offset = (u32)le16_to_cpu(r.bbp->offset);
+ reg_rw->value = (u32)r.bbp->value;
+ break;
+
+ case HOST_CMD_RF_REG_ACCESS:
+ r.rf = &resp->params.rf_reg;
+ reg_rw->offset = (u32)le16_to_cpu(r.rf->offset);
+ reg_rw->value = (u32)r.bbp->value;
+ break;
+ case HOST_CMD_PMIC_REG_ACCESS:
+ r.pmic = &resp->params.pmic_reg;
+ reg_rw->offset = (u32)le16_to_cpu(r.pmic->offset);
+ reg_rw->value = (u32)r.pmic->value;
+ break;
+ case HOST_CMD_CAU_REG_ACCESS:
+ r.rf = &resp->params.rf_reg;
+ reg_rw->offset = (u32)le16_to_cpu(r.rf->offset);
+ reg_rw->value = (u32)r.rf->value;
+ break;
+ case HOST_CMD_802_11_EEPROM_ACCESS:
+ r.eeprom = &resp->params.eeprom;
+ pr_debug("info: EEPROM read len=%x\n",
+ le16_to_cpu(r.eeprom->byte_count));
+ if (eeprom->byte_count < le16_to_cpu(r.eeprom->byte_count)) {
+ eeprom->byte_count = 0;
+ pr_debug("info: EEPROM read length is too big\n");
+ return -1;
+ }
+ eeprom->offset = le16_to_cpu(r.eeprom->offset);
+ eeprom->byte_count = le16_to_cpu(r.eeprom->byte_count);
+ if (eeprom->byte_count > 0)
+ memcpy(&eeprom->value, &r.eeprom->value,
+ min((u16)MAX_EEPROM_DATA, eeprom->byte_count));
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_rf_tx_pwr(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_rf_tx_pwr *txp = &cmd->params.txp;
+
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_tx_pwr)
+ + S_DS_GEN);
+ cmd->command = cpu_to_le16(HOST_CMD_RF_TX_PWR);
+ txp->action = cpu_to_le16(cmd_action);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_rf_tx_pwr(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_rf_tx_pwr *txp = &resp->params.txp;
+ u16 action = le16_to_cpu(txp->action);
+
+ priv->tx_power_level = le16_to_cpu(txp->cur_level);
+
+ if (action == HOST_ACT_GEN_GET) {
+ priv->max_tx_power_level = txp->max_power;
+ priv->min_tx_power_level = txp->min_power;
+ }
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "Current TxPower Level=%d, Max Power=%d, Min Power=%d\n",
+ priv->tx_power_level, priv->max_tx_power_level,
+ priv->min_tx_power_level);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_rf_antenna(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_rf_ant_mimo *ant_mimo = &cmd->params.ant_mimo;
+ struct host_cmd_ds_rf_ant_siso *ant_siso = &cmd->params.ant_siso;
+ struct nxpwifi_ds_ant_cfg *ant_cfg =
+ (struct nxpwifi_ds_ant_cfg *)data_buf;
+
+ cmd->command = cpu_to_le16(HOST_CMD_RF_ANTENNA);
+
+ switch (cmd_action) {
+ case HOST_ACT_GEN_SET:
+ if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) {
+ cmd->size = cpu_to_le16(sizeof(struct
+ host_cmd_ds_rf_ant_mimo)
+ + S_DS_GEN);
+ ant_mimo->action_tx = cpu_to_le16(HOST_ACT_SET_TX);
+ ant_mimo->tx_ant_mode =
+ cpu_to_le16((u16)ant_cfg->tx_ant);
+ ant_mimo->action_rx = cpu_to_le16(HOST_ACT_SET_RX);
+ ant_mimo->rx_ant_mode =
+ cpu_to_le16((u16)ant_cfg->rx_ant);
+ } else {
+ cmd->size = cpu_to_le16(sizeof(struct
+ host_cmd_ds_rf_ant_siso) +
+ S_DS_GEN);
+ ant_siso->action = cpu_to_le16(HOST_ACT_SET_BOTH);
+ ant_siso->ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant);
+ }
+ break;
+ case HOST_ACT_GEN_GET:
+ if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) {
+ cmd->size = cpu_to_le16(sizeof(struct
+ host_cmd_ds_rf_ant_mimo) +
+ S_DS_GEN);
+ ant_mimo->action_tx = cpu_to_le16(HOST_ACT_GET_TX);
+ ant_mimo->action_rx = cpu_to_le16(HOST_ACT_GET_RX);
+ } else {
+ cmd->size = cpu_to_le16(sizeof(struct
+ host_cmd_ds_rf_ant_siso) +
+ S_DS_GEN);
+ ant_siso->action = cpu_to_le16(HOST_ACT_GET_BOTH);
+ }
+ break;
+ }
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_rf_antenna(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_rf_ant_mimo *ant_mimo = &resp->params.ant_mimo;
+ struct host_cmd_ds_rf_ant_siso *ant_siso = &resp->params.ant_siso;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) {
+ priv->tx_ant = le16_to_cpu(ant_mimo->tx_ant_mode);
+ priv->rx_ant = le16_to_cpu(ant_mimo->rx_ant_mode);
+ nxpwifi_dbg(adapter, INFO,
+ "RF_ANT_RESP: Tx action = 0x%x, Tx Mode = 0x%04x\t"
+ "Rx action = 0x%x, Rx Mode = 0x%04x\n",
+ le16_to_cpu(ant_mimo->action_tx),
+ le16_to_cpu(ant_mimo->tx_ant_mode),
+ le16_to_cpu(ant_mimo->action_rx),
+ le16_to_cpu(ant_mimo->rx_ant_mode));
+ } else {
+ priv->tx_ant = le16_to_cpu(ant_siso->ant_mode);
+ priv->rx_ant = le16_to_cpu(ant_siso->ant_mode);
+ nxpwifi_dbg(adapter, INFO,
+ "RF_ANT_RESP: action = 0x%x, Mode = 0x%04x\n",
+ le16_to_cpu(ant_siso->action),
+ le16_to_cpu(ant_siso->ant_mode));
+ }
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_802_11_deauthenticate(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_802_11_deauthenticate *deauth = &cmd->params.deauth;
+ u8 *mac = (u8 *)data_buf;
+
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_DEAUTHENTICATE);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_deauthenticate)
+ + S_DS_GEN);
+
+ /* Set AP MAC address */
+ memcpy(deauth->mac_addr, mac, ETH_ALEN);
+
+ nxpwifi_dbg(priv->adapter, CMD, "cmd: Deauth: %pM\n", deauth->mac_addr);
+
+ deauth->reason_code = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_802_11_deauthenticate(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ adapter->dbg.num_cmd_deauth++;
+ if (!memcmp(resp->params.deauth.mac_addr,
+ &priv->curr_bss_params.bss_descriptor.mac_address,
+ sizeof(resp->params.deauth.mac_addr)))
+ nxpwifi_reset_connect_state(priv, WLAN_REASON_DEAUTH_LEAVING,
+ false);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_mac_control(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_mac_control *mac_ctrl = &cmd->params.mac_ctrl;
+ u32 *action = (u32 *)data_buf;
+
+ if (cmd_action != HOST_ACT_GEN_SET) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "mac_control: only support set cmd\n");
+ return -1;
+ }
+
+ cmd->command = cpu_to_le16(HOST_CMD_MAC_CONTROL);
+ cmd->size =
+ cpu_to_le16(sizeof(struct host_cmd_ds_mac_control) + S_DS_GEN);
+ mac_ctrl->action = cpu_to_le32(*action);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_802_11_mac_address(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_MAC_ADDRESS);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_mac_address) +
+ S_DS_GEN);
+ cmd->result = 0;
+
+ cmd->params.mac_addr.action = cpu_to_le16(cmd_action);
+
+ if (cmd_action == HOST_ACT_GEN_SET)
+ memcpy(cmd->params.mac_addr.mac_addr, priv->curr_addr,
+ ETH_ALEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_802_11_mac_address(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_802_11_mac_address *cmd_mac_addr;
+
+ cmd_mac_addr = &resp->params.mac_addr;
+
+ memcpy(priv->curr_addr, cmd_mac_addr->mac_addr, ETH_ALEN);
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: set mac address: %pM\n", priv->curr_addr);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_802_11d_domain_info(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_802_11d_domain_info *domain_info =
+ &cmd->params.domain_info;
+ struct nxpwifi_ietypes_domain_param_set *domain =
+ &domain_info->domain;
+ u8 no_of_triplet = adapter->domain_reg.no_of_triplet;
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: 11D: no_of_triplet=0x%x\n", no_of_triplet);
+
+ cmd->command = cpu_to_le16(HOST_CMD_802_11D_DOMAIN_INFO);
+ domain_info->action = cpu_to_le16(cmd_action);
+ if (cmd_action == HOST_ACT_GEN_GET) {
+ cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN);
+ return 0;
+ }
+
+ /* Set domain info fields */
+ domain->header.type = cpu_to_le16(WLAN_EID_COUNTRY);
+ memcpy(domain->country_code, adapter->domain_reg.country_code,
+ sizeof(domain->country_code));
+
+ domain->header.len =
+ cpu_to_le16((no_of_triplet *
+ sizeof(struct ieee80211_country_ie_triplet))
+ + sizeof(domain->country_code));
+
+ if (no_of_triplet) {
+ memcpy(domain->triplet, adapter->domain_reg.triplet,
+ no_of_triplet * sizeof(struct
+ ieee80211_country_ie_triplet));
+
+ cmd->size = cpu_to_le16(sizeof(domain_info->action) +
+ le16_to_cpu(domain->header.len) +
+ sizeof(struct nxpwifi_ie_types_header)
+ + S_DS_GEN);
+ } else {
+ cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN);
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_802_11d_domain_info(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_802_11d_domain_info_rsp *domain_info =
+ &resp->params.domain_info_resp;
+ struct nxpwifi_ietypes_domain_param_set *domain = &domain_info->domain;
+ u16 action = le16_to_cpu(domain_info->action);
+ u8 no_of_triplet;
+
+ no_of_triplet = (u8)((le16_to_cpu(domain->header.len)
+ - IEEE80211_COUNTRY_STRING_LEN)
+ / sizeof(struct ieee80211_country_ie_triplet));
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: 11D Domain Info Resp: no_of_triplet=%d\n",
+ no_of_triplet);
+
+ if (no_of_triplet > NXPWIFI_MAX_TRIPLET_802_11D) {
+ nxpwifi_dbg(priv->adapter, FATAL,
+ "11D: invalid number of triplets %d returned\n",
+ no_of_triplet);
+ return -1;
+ }
+
+ switch (action) {
+ case HOST_ACT_GEN_SET: /* Proc Set Action */
+ break;
+ case HOST_ACT_GEN_GET:
+ break;
+ default:
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "11D: invalid action:%d\n", domain_info->action);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int nxpwifi_set_aes_key(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct nxpwifi_ds_encrypt_key *enc_key,
+ struct host_cmd_ds_802_11_key_material *km)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u16 size, len = KEY_PARAMS_FIXED_LEN;
+
+ if (enc_key->is_igtk_key) {
+ nxpwifi_dbg(adapter, INFO,
+ "%s: Set CMAC AES Key\n", __func__);
+ if (enc_key->is_rx_seq_valid)
+ memcpy(km->key_param_set.key_params.cmac_aes.ipn,
+ enc_key->pn, enc_key->pn_len);
+ km->key_param_set.key_info &= cpu_to_le16(~KEY_MCAST);
+ km->key_param_set.key_info |= cpu_to_le16(KEY_IGTK);
+ km->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC;
+ km->key_param_set.key_params.cmac_aes.key_len =
+ cpu_to_le16(enc_key->key_len);
+ memcpy(km->key_param_set.key_params.cmac_aes.key,
+ enc_key->key_material, enc_key->key_len);
+ len += sizeof(struct nxpwifi_cmac_aes_param);
+ } else if (enc_key->is_igtk_def_key) {
+ nxpwifi_dbg(adapter, INFO,
+ "%s: Set CMAC default Key index\n", __func__);
+ km->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC_DEF;
+ km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK;
+ } else {
+ nxpwifi_dbg(adapter, INFO,
+ "%s: Set AES Key\n", __func__);
+ if (enc_key->is_rx_seq_valid)
+ memcpy(km->key_param_set.key_params.aes.pn,
+ enc_key->pn, enc_key->pn_len);
+ km->key_param_set.key_type = KEY_TYPE_ID_AES;
+ km->key_param_set.key_params.aes.key_len =
+ cpu_to_le16(enc_key->key_len);
+ memcpy(km->key_param_set.key_params.aes.key,
+ enc_key->key_material, enc_key->key_len);
+ len += sizeof(struct nxpwifi_aes_param);
+ }
+
+ km->key_param_set.len = cpu_to_le16(len);
+ size = len + sizeof(struct nxpwifi_ie_types_header) +
+ sizeof(km->action) + S_DS_GEN;
+ cmd->size = cpu_to_le16(size);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_802_11_key_material(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_ds_encrypt_key *enc_key =
+ (struct nxpwifi_ds_encrypt_key *)data_buf;
+ u8 *mac = enc_key->mac_addr;
+ u16 key_info, len = KEY_PARAMS_FIXED_LEN;
+ struct host_cmd_ds_802_11_key_material *km =
+ &cmd->params.key_material;
+
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_KEY_MATERIAL);
+ km->action = cpu_to_le16(cmd_action);
+
+ if (cmd_action == HOST_ACT_GEN_GET) {
+ nxpwifi_dbg(adapter, INFO, "%s: Get key\n", __func__);
+ km->key_param_set.key_idx =
+ enc_key->key_index & KEY_INDEX_MASK;
+ km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2);
+ km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN);
+ memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN);
+
+ if (enc_key->key_index & NXPWIFI_KEY_INDEX_UNICAST)
+ key_info = KEY_UNICAST;
+ else
+ key_info = KEY_MCAST;
+
+ if (enc_key->is_igtk_key)
+ key_info |= KEY_IGTK;
+
+ km->key_param_set.key_info = cpu_to_le16(key_info);
+
+ cmd->size = cpu_to_le16(sizeof(struct nxpwifi_ie_types_header) +
+ S_DS_GEN + KEY_PARAMS_FIXED_LEN +
+ sizeof(km->action));
+ return 0;
+ }
+
+ memset(&km->key_param_set, 0,
+ sizeof(struct nxpwifi_ie_type_key_param_set));
+
+ if (enc_key->key_disable) {
+ nxpwifi_dbg(adapter, INFO, "%s: Remove key\n", __func__);
+ km->action = cpu_to_le16(HOST_ACT_GEN_REMOVE);
+ km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2);
+ km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN);
+ km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK;
+ key_info = KEY_MCAST | KEY_UNICAST;
+ km->key_param_set.key_info = cpu_to_le16(key_info);
+ memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN);
+ cmd->size = cpu_to_le16(sizeof(struct nxpwifi_ie_types_header) +
+ S_DS_GEN + KEY_PARAMS_FIXED_LEN +
+ sizeof(km->action));
+ return 0;
+ }
+
+ km->action = cpu_to_le16(HOST_ACT_GEN_SET);
+ km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK;
+ km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2);
+ key_info = KEY_ENABLED;
+ memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN);
+
+ if (enc_key->key_len <= WLAN_KEY_LEN_WEP104) {
+ nxpwifi_dbg(adapter, INFO, "%s: Set WEP Key\n", __func__);
+ len += sizeof(struct nxpwifi_wep_param);
+ km->key_param_set.len = cpu_to_le16(len);
+ km->key_param_set.key_type = KEY_TYPE_ID_WEP;
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
+ key_info |= KEY_MCAST | KEY_UNICAST;
+ } else {
+ if (enc_key->is_current_wep_key) {
+ key_info |= KEY_MCAST | KEY_UNICAST;
+ if (km->key_param_set.key_idx ==
+ (priv->wep_key_curr_index & KEY_INDEX_MASK))
+ key_info |= KEY_DEFAULT;
+ } else {
+ if (is_broadcast_ether_addr(mac))
+ key_info |= KEY_MCAST;
+ else
+ key_info |= KEY_UNICAST | KEY_DEFAULT;
+ }
+ }
+ km->key_param_set.key_info = cpu_to_le16(key_info);
+
+ km->key_param_set.key_params.wep.key_len =
+ cpu_to_le16(enc_key->key_len);
+ memcpy(km->key_param_set.key_params.wep.key,
+ enc_key->key_material, enc_key->key_len);
+
+ cmd->size = cpu_to_le16(sizeof(struct nxpwifi_ie_types_header) +
+ len + sizeof(km->action) + S_DS_GEN);
+ return 0;
+ }
+
+ if (is_broadcast_ether_addr(mac))
+ key_info |= KEY_MCAST | KEY_RX_KEY;
+ else
+ key_info |= KEY_UNICAST | KEY_TX_KEY | KEY_RX_KEY;
+
+ /* Enable default key for WPA/WPA2 */
+ if (!priv->wpa_is_gtk_set)
+ key_info |= KEY_DEFAULT;
+
+ km->key_param_set.key_info = cpu_to_le16(key_info);
+
+ if (enc_key->key_len == WLAN_KEY_LEN_CCMP)
+ return nxpwifi_set_aes_key(priv, cmd, enc_key, km);
+
+ if (enc_key->key_len == WLAN_KEY_LEN_TKIP) {
+ nxpwifi_dbg(adapter, INFO,
+ "%s: Set TKIP Key\n", __func__);
+ if (enc_key->is_rx_seq_valid)
+ memcpy(km->key_param_set.key_params.tkip.pn,
+ enc_key->pn, enc_key->pn_len);
+ km->key_param_set.key_type = KEY_TYPE_ID_TKIP;
+ km->key_param_set.key_params.tkip.key_len =
+ cpu_to_le16(enc_key->key_len);
+ memcpy(km->key_param_set.key_params.tkip.key,
+ enc_key->key_material, enc_key->key_len);
+
+ len += sizeof(struct nxpwifi_tkip_param);
+ km->key_param_set.len = cpu_to_le16(len);
+ cmd->size = cpu_to_le16(sizeof(struct nxpwifi_ie_types_header) +
+ len + sizeof(km->action) + S_DS_GEN);
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_802_11_key_material(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_802_11_key_material *key;
+ int len;
+
+ key = &resp->params.key_material;
+
+ len = le16_to_cpu(key->key_param_set.key_params.aes.key_len);
+ if (len > sizeof(key->key_param_set.key_params.aes.key))
+ return -EINVAL;
+
+ if (le16_to_cpu(key->action) == HOST_ACT_GEN_SET) {
+ if ((le16_to_cpu(key->key_param_set.key_info) & KEY_MCAST)) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: key: GTK is set\n");
+ priv->wpa_is_gtk_set = true;
+ priv->scan_block = false;
+ priv->port_open = true;
+ }
+ }
+
+ if (key->key_param_set.key_type != KEY_TYPE_ID_AES)
+ return 0;
+
+ memset(priv->aes_key.key_param_set.key_params.aes.key, 0,
+ sizeof(key->key_param_set.key_params.aes.key));
+ priv->aes_key.key_param_set.key_params.aes.key_len = cpu_to_le16(len);
+ memcpy(priv->aes_key.key_param_set.key_params.aes.key,
+ key->key_param_set.key_params.aes.key, len);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_802_11_bg_scan_config(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_802_11_bg_scan_config(priv, cmd, data_buf);
+}
+
+static int
+nxpwifi_cmd_sta_802_11_bg_scan_query(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_802_11_bg_scan_query(cmd);
+}
+
+static int
+nxpwifi_ret_sta_802_11_bg_scan_query(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int ret;
+
+ ret = nxpwifi_ret_802_11_scan(priv, resp);
+ cfg80211_sched_scan_results(priv->wdev.wiphy, 0);
+ nxpwifi_dbg(adapter, CMD,
+ "info: CMD_RESP: BG_SCAN result is ready!\n");
+
+ return ret;
+}
+
+static int
+nxpwifi_cmd_sta_wmm_get_status(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ cmd->command = cpu_to_le16(HOST_CMD_WMM_GET_STATUS);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_wmm_get_status) +
+ S_DS_GEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_wmm_get_status(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ return nxpwifi_ret_wmm_get_status(priv, resp);
+}
+
+static int
+nxpwifi_cmd_sta_802_11_subsc_evt(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_802_11_subsc_evt *subsc_evt = &cmd->params.subsc_evt;
+ struct nxpwifi_ds_misc_subsc_evt *subsc_evt_cfg =
+ (struct nxpwifi_ds_misc_subsc_evt *)data_buf;
+ struct nxpwifi_ie_types_rssi_threshold *rssi_tlv;
+ u16 event_bitmap;
+ u8 *pos;
+
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_SUBSCRIBE_EVENT);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_subsc_evt) +
+ S_DS_GEN);
+
+ subsc_evt->action = cpu_to_le16(subsc_evt_cfg->action);
+ nxpwifi_dbg(priv->adapter, CMD,
+ "cmd: action: %d\n", subsc_evt_cfg->action);
+
+ /*For query requests, no configuration TLV structures are to be added.*/
+ if (subsc_evt_cfg->action == HOST_ACT_GEN_GET)
+ return 0;
+
+ subsc_evt->events = cpu_to_le16(subsc_evt_cfg->events);
+
+ event_bitmap = subsc_evt_cfg->events;
+ nxpwifi_dbg(priv->adapter, CMD, "cmd: event bitmap : %16x\n",
+ event_bitmap);
+
+ if ((subsc_evt_cfg->action == HOST_ACT_BITWISE_CLR ||
+ subsc_evt_cfg->action == HOST_ACT_BITWISE_SET) &&
+ event_bitmap == 0) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Error: No event specified\t"
+ "for bitwise action type\n");
+ return -EINVAL;
+ }
+
+ /* Append TLV structures for each of the specified events for
+ * subscribing or re-configuring. This is not required for
+ * bitwise unsubscribing request.
+ */
+ if (subsc_evt_cfg->action == HOST_ACT_BITWISE_CLR)
+ return 0;
+
+ pos = ((u8 *)subsc_evt) +
+ sizeof(struct host_cmd_ds_802_11_subsc_evt);
+
+ if (event_bitmap & BITMASK_BCN_RSSI_LOW) {
+ rssi_tlv = (struct nxpwifi_ie_types_rssi_threshold *)pos;
+
+ rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_LOW);
+ rssi_tlv->header.len =
+ cpu_to_le16(sizeof(struct nxpwifi_ie_types_rssi_threshold) -
+ sizeof(struct nxpwifi_ie_types_header));
+ rssi_tlv->abs_value = subsc_evt_cfg->bcn_l_rssi_cfg.abs_value;
+ rssi_tlv->evt_freq = subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq;
+
+ nxpwifi_dbg(priv->adapter, EVENT,
+ "Cfg Beacon Low Rssi event,\t"
+ "RSSI:-%d dBm, Freq:%d\n",
+ subsc_evt_cfg->bcn_l_rssi_cfg.abs_value,
+ subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq);
+
+ pos += sizeof(struct nxpwifi_ie_types_rssi_threshold);
+ le16_unaligned_add_cpu
+ (&cmd->size,
+ sizeof(struct nxpwifi_ie_types_rssi_threshold));
+ }
+
+ if (event_bitmap & BITMASK_BCN_RSSI_HIGH) {
+ rssi_tlv = (struct nxpwifi_ie_types_rssi_threshold *)pos;
+
+ rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH);
+ rssi_tlv->header.len =
+ cpu_to_le16(sizeof(struct nxpwifi_ie_types_rssi_threshold) -
+ sizeof(struct nxpwifi_ie_types_header));
+ rssi_tlv->abs_value = subsc_evt_cfg->bcn_h_rssi_cfg.abs_value;
+ rssi_tlv->evt_freq = subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq;
+
+ nxpwifi_dbg(priv->adapter, EVENT,
+ "Cfg Beacon High Rssi event,\t"
+ "RSSI:-%d dBm, Freq:%d\n",
+ subsc_evt_cfg->bcn_h_rssi_cfg.abs_value,
+ subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq);
+
+ pos += sizeof(struct nxpwifi_ie_types_rssi_threshold);
+ le16_unaligned_add_cpu
+ (&cmd->size,
+ sizeof(struct nxpwifi_ie_types_rssi_threshold));
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_subsc_evt(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_802_11_subsc_evt *cmd_sub_event =
+ &resp->params.subsc_evt;
+
+ /* For every subscribe event command (Get/Set/Clear), FW reports the
+ * current set of subscribed events
+ */
+ nxpwifi_dbg(priv->adapter, EVENT,
+ "Bitmap of currently subscribed events: %16x\n",
+ le16_to_cpu(cmd_sub_event->events));
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_802_11_tx_rate_query(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_TX_RATE_QUERY);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_tx_rate_query) +
+ S_DS_GEN);
+ priv->tx_rate = 0;
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_802_11_tx_rate_query(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ priv->tx_rate = resp->params.tx_rate.tx_rate;
+ priv->tx_htinfo = resp->params.tx_rate.ht_info;
+ if (!priv->is_data_rate_auto)
+ priv->data_rate =
+ nxpwifi_index_to_data_rate(priv, priv->tx_rate,
+ priv->tx_htinfo);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_mem_access(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct nxpwifi_ds_mem_rw *mem_rw =
+ (struct nxpwifi_ds_mem_rw *)data_buf;
+ struct host_cmd_ds_mem_access *mem_access = (void *)&cmd->params.mem;
+
+ cmd->command = cpu_to_le16(HOST_CMD_MEM_ACCESS);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mem_access) +
+ S_DS_GEN);
+
+ mem_access->action = cpu_to_le16(cmd_action);
+ mem_access->addr = cpu_to_le32(mem_rw->addr);
+ mem_access->value = cpu_to_le32(mem_rw->value);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_mem_access(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_mem_access *mem = (void *)&resp->params.mem;
+
+ priv->mem_rw.addr = le32_to_cpu(mem->addr);
+ priv->mem_rw.value = le32_to_cpu(mem->value);
+
+ return 0;
+}
+
+static u32 nxpwifi_parse_cal_cfg(u8 *src, size_t len, u8 *dst)
+{
+ u8 *s = src, *d = dst;
+
+ while (s - src < len) {
+ if (*s && (isspace(*s) || *s == '\t')) {
+ s++;
+ continue;
+ }
+ if (isxdigit(*s)) {
+ if (kstrtou8(s, 16, d))
+ return 0;
+ d++;
+ s += 2;
+ } else {
+ s++;
+ }
+ }
+
+ return d - dst;
+}
+
+static int
+nxpwifi_cmd_sta_cfg_data(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct property *prop = data_buf;
+ u32 len;
+ u8 *data = (u8 *)cmd + S_DS_GEN;
+ int ret;
+
+ if (prop) {
+ len = prop->length;
+ ret = of_property_read_u8_array(adapter->dt_node, prop->name,
+ data, len);
+ if (ret)
+ return ret;
+ nxpwifi_dbg(adapter, INFO,
+ "download cfg_data from device tree: %s\n",
+ prop->name);
+ } else if (adapter->cal_data->data && adapter->cal_data->size > 0) {
+ len = nxpwifi_parse_cal_cfg((u8 *)adapter->cal_data->data,
+ adapter->cal_data->size, data);
+ nxpwifi_dbg(adapter, INFO,
+ "download cfg_data from config file\n");
+ } else {
+ return -1;
+ }
+
+ cmd->command = cpu_to_le16(HOST_CMD_CFG_DATA);
+ cmd->size = cpu_to_le16(S_DS_GEN + len);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_cfg_data(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ if (resp->result != HOST_RESULT_OK) {
+ nxpwifi_dbg(priv->adapter, ERROR, "Cal data cmd resp failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_ver_ext(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ cmd->command = cpu_to_le16(cmd_no);
+ cmd->params.verext.version_str_sel =
+ (u8)(get_unaligned((u32 *)data_buf));
+ memcpy(&cmd->params, data_buf, sizeof(struct host_cmd_ds_version_ext));
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_version_ext) +
+ S_DS_GEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_ver_ext(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext;
+ struct host_cmd_ds_version_ext *version_ext =
+ (struct host_cmd_ds_version_ext *)data_buf;
+
+ if (test_and_clear_bit(NXPWIFI_IS_REQUESTING_FW_VEREXT, &priv->adapter->work_flags)) {
+ if (strncmp(ver_ext->version_str, "ChipRev:20, BB:9b(10.00), RF:40(21)",
+ NXPWIFI_VERSION_STR_LENGTH) == 0) {
+ struct nxpwifi_ds_auto_ds auto_ds = {
+ .auto_ds = DEEP_SLEEP_OFF,
+ };
+
+ nxpwifi_dbg(priv->adapter, MSG,
+ "Bad HW revision detected, disabling deep sleep\n");
+
+ if (nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH,
+ DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, false)) {
+ nxpwifi_dbg(priv->adapter, MSG,
+ "Disabling deep sleep failed.\n");
+ }
+ }
+
+ return 0;
+ }
+
+ if (version_ext) {
+ version_ext->version_str_sel = ver_ext->version_str_sel;
+ memcpy(version_ext->version_str, ver_ext->version_str,
+ NXPWIFI_VERSION_STR_LENGTH);
+ memcpy(priv->version_str, ver_ext->version_str,
+ NXPWIFI_VERSION_STR_LENGTH);
+
+ /* Ensure the version string from the firmware is 0-terminated */
+ priv->version_str[NXPWIFI_VERSION_STR_LENGTH - 1] = '\0';
+ }
+ return 0;
+}
+
+static int
+nxpwifi_cmd_append_rpn_expression(struct nxpwifi_private *priv,
+ struct nxpwifi_mef_entry *mef_entry,
+ u8 **buffer)
+{
+ struct nxpwifi_mef_filter *filter = mef_entry->filter;
+ int i, byte_len;
+ u8 *stack_ptr = *buffer;
+
+ for (i = 0; i < NXPWIFI_MEF_MAX_FILTERS; i++) {
+ filter = &mef_entry->filter[i];
+ if (!filter->filt_type)
+ break;
+ put_unaligned_le32((u32)filter->repeat, stack_ptr);
+ stack_ptr += 4;
+ *stack_ptr = TYPE_DNUM;
+ stack_ptr += 1;
+
+ byte_len = filter->byte_seq[NXPWIFI_MEF_MAX_BYTESEQ];
+ memcpy(stack_ptr, filter->byte_seq, byte_len);
+ stack_ptr += byte_len;
+ *stack_ptr = byte_len;
+ stack_ptr += 1;
+ *stack_ptr = TYPE_BYTESEQ;
+ stack_ptr += 1;
+ put_unaligned_le32((u32)filter->offset, stack_ptr);
+ stack_ptr += 4;
+ *stack_ptr = TYPE_DNUM;
+ stack_ptr += 1;
+
+ *stack_ptr = filter->filt_type;
+ stack_ptr += 1;
+
+ if (filter->filt_action) {
+ *stack_ptr = filter->filt_action;
+ stack_ptr += 1;
+ }
+
+ if (stack_ptr - *buffer > STACK_NBYTES)
+ return -1;
+ }
+
+ *buffer = stack_ptr;
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_mef_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg;
+ struct nxpwifi_ds_mef_cfg *mef =
+ (struct nxpwifi_ds_mef_cfg *)data_buf;
+ struct nxpwifi_fw_mef_entry *mef_entry = NULL;
+ u8 *pos = (u8 *)mef_cfg;
+ u16 i;
+
+ cmd->command = cpu_to_le16(HOST_CMD_MEF_CFG);
+
+ mef_cfg->criteria = cpu_to_le32(mef->criteria);
+ mef_cfg->num_entries = cpu_to_le16(mef->num_entries);
+ pos += sizeof(*mef_cfg);
+
+ for (i = 0; i < mef->num_entries; i++) {
+ mef_entry = (struct nxpwifi_fw_mef_entry *)pos;
+ mef_entry->mode = mef->mef_entry[i].mode;
+ mef_entry->action = mef->mef_entry[i].action;
+ pos += sizeof(*mef_entry);
+
+ if (nxpwifi_cmd_append_rpn_expression(priv,
+ &mef->mef_entry[i], &pos))
+ return -1;
+
+ mef_entry->exprsize =
+ cpu_to_le16(pos - mef_entry->expr);
+ }
+ cmd->size = cpu_to_le16((u16)(pos - (u8 *)mef_cfg) + S_DS_GEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_802_11_rssi_info(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ cmd->command = cpu_to_le16(HOST_CMD_RSSI_INFO);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_rssi_info) +
+ S_DS_GEN);
+ cmd->params.rssi_info.action = cpu_to_le16(cmd_action);
+ cmd->params.rssi_info.ndata = cpu_to_le16(priv->data_avg_factor);
+ cmd->params.rssi_info.nbcn = cpu_to_le16(priv->bcn_avg_factor);
+
+ /* Reset SNR/NF/RSSI values in private structure */
+ 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;
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_802_11_rssi_info(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp =
+ &resp->params.rssi_info_rsp;
+ struct nxpwifi_ds_misc_subsc_evt *subsc_evt =
+ &priv->async_subsc_evt_storage;
+
+ priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last);
+ priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last);
+
+ priv->data_rssi_avg = le16_to_cpu(rssi_info_rsp->data_rssi_avg);
+ priv->data_nf_avg = le16_to_cpu(rssi_info_rsp->data_nf_avg);
+
+ priv->bcn_rssi_last = le16_to_cpu(rssi_info_rsp->bcn_rssi_last);
+ priv->bcn_nf_last = le16_to_cpu(rssi_info_rsp->bcn_nf_last);
+
+ priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg);
+ priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg);
+
+ if (priv->subsc_evt_rssi_state == EVENT_HANDLED)
+ return 0;
+
+ memset(subsc_evt, 0x00, sizeof(struct nxpwifi_ds_misc_subsc_evt));
+
+ /* Resubscribe low and high rssi events with new thresholds */
+ subsc_evt->events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH;
+ subsc_evt->action = HOST_ACT_BITWISE_SET;
+ if (priv->subsc_evt_rssi_state == RSSI_LOW_RECVD) {
+ subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg -
+ priv->cqm_rssi_hyst);
+ subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold);
+ } else if (priv->subsc_evt_rssi_state == RSSI_HIGH_RECVD) {
+ subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold);
+ subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg +
+ priv->cqm_rssi_hyst);
+ }
+ subsc_evt->bcn_l_rssi_cfg.evt_freq = 1;
+ subsc_evt->bcn_h_rssi_cfg.evt_freq = 1;
+
+ priv->subsc_evt_rssi_state = EVENT_HANDLED;
+
+ nxpwifi_send_cmd(priv, HOST_CMD_802_11_SUBSCRIBE_EVENT,
+ 0, 0, subsc_evt, false);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_func_init(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ if (priv->adapter->hw_status == NXPWIFI_HW_STATUS_RESET)
+ priv->adapter->hw_status = NXPWIFI_HW_STATUS_READY;
+ cmd->command = cpu_to_le16(cmd_no);
+ cmd->size = cpu_to_le16(S_DS_GEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_func_shutdown(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ priv->adapter->hw_status = NXPWIFI_HW_STATUS_RESET;
+ cmd->command = cpu_to_le16(cmd_no);
+ cmd->size = cpu_to_le16(S_DS_GEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_11n_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_11n_cfg(priv, cmd, cmd_action, data_buf);
+}
+
+static int
+nxpwifi_cmd_sta_11n_addba_req(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_11n_addba_req(cmd, data_buf);
+}
+
+static int
+nxpwifi_ret_sta_11n_addba_req(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ return nxpwifi_ret_11n_addba_req(priv, resp);
+}
+
+static int
+nxpwifi_cmd_sta_11n_addba_rsp(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_11n_addba_rsp_gen(priv, cmd, data_buf);
+}
+
+static int
+nxpwifi_ret_sta_11n_addba_rsp(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ return nxpwifi_ret_11n_addba_resp(priv, resp);
+}
+
+static int
+nxpwifi_cmd_sta_11n_delba(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_11n_delba(cmd, data_buf);
+}
+
+static int
+nxpwifi_ret_sta_11n_delba(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ return nxpwifi_ret_11n_delba(priv, resp);
+}
+
+static int
+nxpwifi_cmd_sta_tx_power_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct nxpwifi_types_power_group *pg_tlv;
+ struct host_cmd_ds_txpwr_cfg *cmd_txp_cfg = &cmd->params.txp_cfg;
+ struct host_cmd_ds_txpwr_cfg *txp =
+ (struct host_cmd_ds_txpwr_cfg *)data_buf;
+
+ cmd->command = cpu_to_le16(HOST_CMD_TXPWR_CFG);
+ cmd->size =
+ cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_txpwr_cfg));
+ switch (cmd_action) {
+ case HOST_ACT_GEN_SET:
+ if (txp->mode) {
+ pg_tlv = (struct nxpwifi_types_power_group
+ *)((unsigned long)txp +
+ sizeof(struct host_cmd_ds_txpwr_cfg));
+ memmove(cmd_txp_cfg, txp,
+ sizeof(struct host_cmd_ds_txpwr_cfg) +
+ sizeof(struct nxpwifi_types_power_group) +
+ le16_to_cpu(pg_tlv->length));
+
+ pg_tlv = (struct nxpwifi_types_power_group *)((u8 *)
+ cmd_txp_cfg +
+ sizeof(struct host_cmd_ds_txpwr_cfg));
+ cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) +
+ sizeof(struct nxpwifi_types_power_group) +
+ le16_to_cpu(pg_tlv->length));
+ } else {
+ memmove(cmd_txp_cfg, txp, sizeof(*txp));
+ }
+ cmd_txp_cfg->action = cpu_to_le16(cmd_action);
+ break;
+ case HOST_ACT_GEN_GET:
+ cmd_txp_cfg->action = cpu_to_le16(cmd_action);
+ break;
+ }
+
+ return 0;
+}
+
+static int nxpwifi_get_power_level(struct nxpwifi_private *priv, void *data_buf)
+{
+ int length, max_power = -1, min_power = -1;
+ struct nxpwifi_types_power_group *pg_tlv_hdr;
+ struct nxpwifi_power_group *pg;
+
+ if (!data_buf)
+ return -1;
+
+ pg_tlv_hdr = (struct nxpwifi_types_power_group *)((u8 *)data_buf);
+ pg = (struct nxpwifi_power_group *)
+ ((u8 *)pg_tlv_hdr + sizeof(struct nxpwifi_types_power_group));
+ length = le16_to_cpu(pg_tlv_hdr->length);
+
+ /* At least one structure required to update power */
+ if (length < sizeof(struct nxpwifi_power_group))
+ return 0;
+
+ max_power = pg->power_max;
+ min_power = pg->power_min;
+ length -= sizeof(struct nxpwifi_power_group);
+
+ while (length >= sizeof(struct nxpwifi_power_group)) {
+ pg++;
+ if (max_power < pg->power_max)
+ max_power = pg->power_max;
+
+ if (min_power > pg->power_min)
+ min_power = pg->power_min;
+
+ length -= sizeof(struct nxpwifi_power_group);
+ }
+ priv->min_tx_power_level = (u8)min_power;
+ priv->max_tx_power_level = (u8)max_power;
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_tx_power_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg;
+ struct nxpwifi_types_power_group *pg_tlv_hdr;
+ struct nxpwifi_power_group *pg;
+ u16 action = le16_to_cpu(txp_cfg->action);
+ u16 tlv_buf_left;
+
+ pg_tlv_hdr = (struct nxpwifi_types_power_group *)
+ ((u8 *)txp_cfg +
+ sizeof(struct host_cmd_ds_txpwr_cfg));
+
+ pg = (struct nxpwifi_power_group *)
+ ((u8 *)pg_tlv_hdr +
+ sizeof(struct nxpwifi_types_power_group));
+
+ tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*txp_cfg);
+ if (tlv_buf_left <
+ le16_to_cpu(pg_tlv_hdr->length) + sizeof(*pg_tlv_hdr))
+ return 0;
+
+ switch (action) {
+ case HOST_ACT_GEN_GET:
+ if (adapter->hw_status == NXPWIFI_HW_STATUS_INITIALIZING)
+ nxpwifi_get_power_level(priv, pg_tlv_hdr);
+
+ priv->tx_power_level = (u16)pg->power_min;
+ break;
+
+ case HOST_ACT_GEN_SET:
+ if (!le32_to_cpu(txp_cfg->mode))
+ break;
+
+ if (pg->power_max == pg->power_min)
+ priv->tx_power_level = (u16)pg->power_min;
+ break;
+ default:
+ nxpwifi_dbg(adapter, ERROR,
+ "CMD_RESP: unknown cmd action %d\n",
+ action);
+ return 0;
+ }
+ nxpwifi_dbg(adapter, INFO,
+ "info: Current TxPower Level = %d, Max Power=%d, Min Power=%d\n",
+ priv->tx_power_level, priv->max_tx_power_level,
+ priv->min_tx_power_level);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_tx_rate_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg;
+ u16 *pbitmap_rates = (u16 *)data_buf;
+ struct nxpwifi_rate_scope *rate_scope;
+ struct nxpwifi_rate_drop_pattern *rate_drop;
+ u32 i;
+
+ cmd->command = cpu_to_le16(HOST_CMD_TX_RATE_CFG);
+
+ rate_cfg->action = cpu_to_le16(cmd_action);
+ rate_cfg->cfg_index = 0;
+
+ rate_scope = (struct nxpwifi_rate_scope *)((u8 *)rate_cfg +
+ sizeof(struct host_cmd_ds_tx_rate_cfg));
+ rate_scope->type = cpu_to_le16(TLV_TYPE_RATE_SCOPE);
+ rate_scope->length = cpu_to_le16
+ (sizeof(*rate_scope) - sizeof(struct nxpwifi_ie_types_header));
+ if (pbitmap_rates) {
+ rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]);
+ rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]);
+ for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++)
+ rate_scope->ht_mcs_rate_bitmap[i] =
+ cpu_to_le16(pbitmap_rates[2 + i]);
+ if (priv->adapter->fw_api_ver == NXPWIFI_FW_V15) {
+ for (i = 0;
+ i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap);
+ i++)
+ rate_scope->vht_mcs_rate_bitmap[i] =
+ cpu_to_le16(pbitmap_rates[10 + i]);
+ }
+ } else {
+ rate_scope->hr_dsss_rate_bitmap =
+ cpu_to_le16(priv->bitmap_rates[0]);
+ rate_scope->ofdm_rate_bitmap =
+ cpu_to_le16(priv->bitmap_rates[1]);
+ for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++)
+ rate_scope->ht_mcs_rate_bitmap[i] =
+ cpu_to_le16(priv->bitmap_rates[2 + i]);
+ if (priv->adapter->fw_api_ver == NXPWIFI_FW_V15) {
+ for (i = 0;
+ i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap);
+ i++)
+ rate_scope->vht_mcs_rate_bitmap[i] =
+ cpu_to_le16(priv->bitmap_rates[10 + i]);
+ }
+ }
+
+ rate_drop = (struct nxpwifi_rate_drop_pattern *)((u8 *)rate_scope +
+ sizeof(struct nxpwifi_rate_scope));
+ rate_drop->type = cpu_to_le16(TLV_TYPE_RATE_DROP_CONTROL);
+ rate_drop->length = cpu_to_le16(sizeof(rate_drop->rate_drop_mode));
+ rate_drop->rate_drop_mode = 0;
+
+ cmd->size =
+ cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_tx_rate_cfg) +
+ sizeof(struct nxpwifi_rate_scope) +
+ sizeof(struct nxpwifi_rate_drop_pattern));
+
+ return 0;
+}
+
+static void nxpwifi_ret_rate_scope(struct nxpwifi_private *priv, u8 *tlv_buf)
+{
+ struct nxpwifi_rate_scope *rate_scope;
+ int i;
+
+ rate_scope = (struct nxpwifi_rate_scope *)tlv_buf;
+ priv->bitmap_rates[0] =
+ le16_to_cpu(rate_scope->hr_dsss_rate_bitmap);
+ priv->bitmap_rates[1] =
+ le16_to_cpu(rate_scope->ofdm_rate_bitmap);
+ for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++)
+ priv->bitmap_rates[2 + i] =
+ le16_to_cpu(rate_scope->ht_mcs_rate_bitmap[i]);
+
+ if (priv->adapter->fw_api_ver == NXPWIFI_FW_V15) {
+ for (i = 0; i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap);
+ i++)
+ priv->bitmap_rates[10 + i] =
+ le16_to_cpu(rate_scope->vht_mcs_rate_bitmap[i]);
+ }
+}
+
+static int
+nxpwifi_ret_sta_tx_rate_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg;
+ struct nxpwifi_ie_types_header *head;
+ u16 tlv, tlv_buf_len, tlv_buf_left;
+ u8 *tlv_buf;
+
+ tlv_buf = ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg);
+ tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*rate_cfg);
+
+ while (tlv_buf_left >= sizeof(*head)) {
+ head = (struct nxpwifi_ie_types_header *)tlv_buf;
+ tlv = le16_to_cpu(head->type);
+ tlv_buf_len = le16_to_cpu(head->len);
+
+ if (tlv_buf_left < (sizeof(*head) + tlv_buf_len))
+ break;
+
+ switch (tlv) {
+ case TLV_TYPE_RATE_SCOPE:
+ nxpwifi_ret_rate_scope(priv, tlv_buf);
+ break;
+ /* Add RATE_DROP tlv here */
+ }
+
+ tlv_buf += (sizeof(*head) + tlv_buf_len);
+ tlv_buf_left -= (sizeof(*head) + tlv_buf_len);
+ }
+
+ priv->is_data_rate_auto = nxpwifi_is_rate_auto(priv);
+
+ if (priv->is_data_rate_auto)
+ priv->data_rate = 0;
+ else
+ return nxpwifi_send_cmd(priv, HOST_CMD_802_11_TX_RATE_QUERY,
+ HOST_ACT_GEN_GET, 0, NULL, false);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_reconfigure_rx_buff(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_recfg_tx_buf(priv, cmd, cmd_action, data_buf);
+}
+
+static int
+nxpwifi_ret_sta_reconfigure_rx_buff(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (0xffff != (u16)le16_to_cpu(resp->params.tx_buf.buff_size)) {
+ adapter->tx_buf_size =
+ (u16)le16_to_cpu(resp->params.tx_buf.buff_size);
+ adapter->tx_buf_size =
+ (adapter->tx_buf_size / NXPWIFI_SDIO_BLOCK_SIZE) *
+ NXPWIFI_SDIO_BLOCK_SIZE;
+ adapter->curr_tx_buf_size = adapter->tx_buf_size;
+ nxpwifi_dbg(adapter, CMD, "cmd: curr_tx_buf_size=%d\n",
+ adapter->curr_tx_buf_size);
+
+ if (adapter->if_ops.update_mp_end_port) {
+ u16 mp_end_port;
+
+ mp_end_port =
+ le16_to_cpu(resp->params.tx_buf.mp_end_port);
+ adapter->if_ops.update_mp_end_port(adapter,
+ mp_end_port);
+ }
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_chan_report_request(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_issue_chan_report_request(priv, cmd, data_buf);
+}
+
+static int
+nxpwifi_cmf_sta_amsdu_aggr_ctrl(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_amsdu_aggr_ctrl(cmd, cmd_action, data_buf);
+}
+
+static int
+nxpwifi_cmd_sta_robust_coex(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_robust_coex *coex = &cmd->params.coex;
+ bool *is_timeshare = (bool *)data_buf;
+ struct nxpwifi_ie_types_robust_coex *coex_tlv;
+
+ cmd->command = cpu_to_le16(HOST_CMD_ROBUST_COEX);
+ cmd->size = cpu_to_le16(sizeof(*coex) + sizeof(*coex_tlv) + S_DS_GEN);
+
+ coex->action = cpu_to_le16(cmd_action);
+ coex_tlv = (struct nxpwifi_ie_types_robust_coex *)
+ ((u8 *)coex + sizeof(*coex));
+ coex_tlv->header.type = cpu_to_le16(TLV_TYPE_ROBUST_COEX);
+ coex_tlv->header.len = cpu_to_le16(sizeof(coex_tlv->mode));
+
+ if (coex->action == HOST_ACT_GEN_GET)
+ return 0;
+
+ if (*is_timeshare)
+ coex_tlv->mode = cpu_to_le32(NXPWIFI_COEX_MODE_TIMESHARE);
+ else
+ coex_tlv->mode = cpu_to_le32(NXPWIFI_COEX_MODE_SPATIAL);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_robust_coex(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_robust_coex *coex = &resp->params.coex;
+ bool *is_timeshare = (bool *)data_buf;
+ struct nxpwifi_ie_types_robust_coex *coex_tlv;
+ u16 action = le16_to_cpu(coex->action);
+ u32 mode;
+
+ coex_tlv = (struct nxpwifi_ie_types_robust_coex
+ *)((u8 *)coex + sizeof(struct host_cmd_ds_robust_coex));
+ if (action == HOST_ACT_GEN_GET) {
+ mode = le32_to_cpu(coex_tlv->mode);
+ if (mode == NXPWIFI_COEX_MODE_TIMESHARE)
+ *is_timeshare = true;
+ else
+ *is_timeshare = false;
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_enh_power_mode(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh =
+ &cmd->params.psmode_enh;
+ u16 ps_bitmap = (u16)cmd_type;
+ struct nxpwifi_ds_auto_ds *auto_ds =
+ (struct nxpwifi_ds_auto_ds *)data_buf;
+ u8 *tlv;
+ u16 cmd_size = 0;
+
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_PS_MODE_ENH);
+ if (cmd_action == DIS_AUTO_PS) {
+ psmode_enh->action = cpu_to_le16(DIS_AUTO_PS);
+ psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);
+ cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) +
+ sizeof(psmode_enh->params.ps_bitmap));
+ } else if (cmd_action == GET_PS) {
+ psmode_enh->action = cpu_to_le16(GET_PS);
+ psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);
+ cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) +
+ sizeof(psmode_enh->params.ps_bitmap));
+ } else if (cmd_action == EN_AUTO_PS) {
+ psmode_enh->action = cpu_to_le16(EN_AUTO_PS);
+ psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap);
+ cmd_size = S_DS_GEN + sizeof(psmode_enh->action) +
+ sizeof(psmode_enh->params.ps_bitmap);
+ tlv = (u8 *)cmd + cmd_size;
+ if (ps_bitmap & BITMAP_STA_PS) {
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_ie_types_ps_param *ps_tlv =
+ (struct nxpwifi_ie_types_ps_param *)tlv;
+ struct nxpwifi_ps_param *ps_mode = &ps_tlv->param;
+
+ ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM);
+ ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) -
+ sizeof(struct nxpwifi_ie_types_header));
+ cmd_size += sizeof(*ps_tlv);
+ tlv += sizeof(*ps_tlv);
+ nxpwifi_dbg(priv->adapter, CMD,
+ "cmd: PS Command: Enter PS\n");
+ ps_mode->null_pkt_interval =
+ cpu_to_le16(adapter->null_pkt_interval);
+ ps_mode->multiple_dtims =
+ cpu_to_le16(adapter->multiple_dtim);
+ ps_mode->bcn_miss_timeout =
+ cpu_to_le16(adapter->bcn_miss_time_out);
+ ps_mode->local_listen_interval =
+ cpu_to_le16(adapter->local_listen_interval);
+ ps_mode->delay_to_ps =
+ cpu_to_le16(adapter->delay_to_ps);
+ ps_mode->mode = cpu_to_le16(adapter->enhanced_ps_mode);
+ }
+ if (ps_bitmap & BITMAP_AUTO_DS) {
+ struct nxpwifi_ie_types_auto_ds_param *auto_ds_tlv =
+ (struct nxpwifi_ie_types_auto_ds_param *)tlv;
+ u16 idletime = 0;
+
+ auto_ds_tlv->header.type =
+ cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM);
+ auto_ds_tlv->header.len =
+ cpu_to_le16(sizeof(*auto_ds_tlv) -
+ sizeof(struct nxpwifi_ie_types_header));
+ cmd_size += sizeof(*auto_ds_tlv);
+ tlv += sizeof(*auto_ds_tlv);
+ if (auto_ds)
+ idletime = auto_ds->idle_time;
+ nxpwifi_dbg(priv->adapter, CMD,
+ "cmd: PS Command: Enter Auto Deep Sleep\n");
+ auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime);
+ }
+ cmd->size = cpu_to_le16(cmd_size);
+ }
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_enh_power_mode(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_802_11_ps_mode_enh *ps_mode =
+ &resp->params.psmode_enh;
+ struct nxpwifi_ds_pm_cfg *pm_cfg =
+ (struct nxpwifi_ds_pm_cfg *)data_buf;
+ u16 action = le16_to_cpu(ps_mode->action);
+ u16 ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap);
+ u16 auto_ps_bitmap =
+ le16_to_cpu(ps_mode->params.ps_bitmap);
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: %s: PS_MODE cmd reply result=%#x action=%#X\n",
+ __func__, resp->result, action);
+ if (action == EN_AUTO_PS) {
+ if (auto_ps_bitmap & BITMAP_AUTO_DS) {
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: Enabled auto deep sleep\n");
+ priv->adapter->is_deep_sleep = true;
+ }
+ if (auto_ps_bitmap & BITMAP_STA_PS) {
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: Enabled STA power save\n");
+ if (adapter->sleep_period.period)
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: set to uapsd/pps mode\n");
+ }
+ } else if (action == DIS_AUTO_PS) {
+ if (ps_bitmap & BITMAP_AUTO_DS) {
+ priv->adapter->is_deep_sleep = false;
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: Disabled auto deep sleep\n");
+ }
+ if (ps_bitmap & BITMAP_STA_PS) {
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: Disabled STA power save\n");
+ if (adapter->sleep_period.period) {
+ adapter->delay_null_pkt = false;
+ adapter->tx_lock_flag = false;
+ adapter->pps_uapsd_mode = false;
+ }
+ }
+ } else if (action == GET_PS) {
+ if (ps_bitmap & BITMAP_STA_PS)
+ adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_PSP;
+ else
+ adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_CAM;
+
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: ps_bitmap=%#x\n", ps_bitmap);
+
+ if (pm_cfg) {
+ /* This section is for get power save mode */
+ if (ps_bitmap & BITMAP_STA_PS)
+ pm_cfg->param.ps_mode = 1;
+ else
+ pm_cfg->param.ps_mode = 0;
+ }
+ }
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_802_11_hs_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg;
+ struct nxpwifi_hs_config_param *hscfg_param =
+ (struct nxpwifi_hs_config_param *)data_buf;
+ u8 *tlv = (u8 *)hs_cfg + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh);
+ struct nxpwifi_ps_param_in_hs *psparam_tlv = NULL;
+ bool hs_activate = false;
+ u16 size;
+
+ if (!hscfg_param)
+ /* New Activate command */
+ hs_activate = true;
+ cmd->command = cpu_to_le16(HOST_CMD_802_11_HS_CFG_ENH);
+
+ if (!hs_activate &&
+ hscfg_param->conditions != cpu_to_le32(HS_CFG_CANCEL) &&
+ (adapter->arp_filter_size > 0 &&
+ adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE)) {
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: Attach %d bytes ArpFilter to HSCfg cmd\n",
+ adapter->arp_filter_size);
+ memcpy(((u8 *)hs_cfg) +
+ sizeof(struct host_cmd_ds_802_11_hs_cfg_enh),
+ adapter->arp_filter, adapter->arp_filter_size);
+ size = adapter->arp_filter_size +
+ sizeof(struct host_cmd_ds_802_11_hs_cfg_enh)
+ + S_DS_GEN;
+ tlv = (u8 *)hs_cfg
+ + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh)
+ + adapter->arp_filter_size;
+ } else {
+ size = S_DS_GEN + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh);
+ }
+ if (hs_activate) {
+ hs_cfg->action = cpu_to_le16(HS_ACTIVATE);
+ hs_cfg->params.hs_activate.resp_ctrl = cpu_to_le16(RESP_NEEDED);
+
+ adapter->hs_activated_manually = true;
+ nxpwifi_dbg(priv->adapter, CMD,
+ "cmd: Activating host sleep manually\n");
+ } else {
+ hs_cfg->action = cpu_to_le16(HS_CONFIGURE);
+ hs_cfg->params.hs_config.conditions = hscfg_param->conditions;
+ hs_cfg->params.hs_config.gpio = hscfg_param->gpio;
+ hs_cfg->params.hs_config.gap = hscfg_param->gap;
+
+ size += sizeof(struct nxpwifi_ps_param_in_hs);
+ psparam_tlv = (struct nxpwifi_ps_param_in_hs *)tlv;
+ psparam_tlv->header.type =
+ cpu_to_le16(TLV_TYPE_PS_PARAMS_IN_HS);
+ psparam_tlv->header.len =
+ cpu_to_le16(sizeof(struct nxpwifi_ps_param_in_hs)
+ - sizeof(struct nxpwifi_ie_types_header));
+ psparam_tlv->hs_wake_int = cpu_to_le32(HS_DEF_WAKE_INTERVAL);
+ psparam_tlv->hs_inact_timeout =
+ cpu_to_le32(HS_DEF_INACTIVITY_TIMEOUT);
+
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n",
+ hs_cfg->params.hs_config.conditions,
+ hs_cfg->params.hs_config.gpio,
+ hs_cfg->params.hs_config.gap);
+ }
+ cmd->size = cpu_to_le16(size);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_802_11_hs_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ return nxpwifi_ret_802_11_hs_cfg(priv, resp);
+}
+
+static int
+nxpwifi_cmd_sta_set_bss_mode(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ cmd->command = cpu_to_le16(cmd_no);
+ if (priv->bss_mode == NL80211_IFTYPE_STATION)
+ cmd->params.bss_mode.con_type = CONNECTION_TYPE_INFRA;
+ else if (priv->bss_mode == NL80211_IFTYPE_AP)
+ cmd->params.bss_mode.con_type = CONNECTION_TYPE_AP;
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_set_bss_mode) +
+ S_DS_GEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_802_11_scan_ext(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_802_11_scan_ext(priv, cmd, data_buf);
+}
+
+static int
+nxpwifi_ret_sta_802_11_scan_ext(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int ret;
+
+ ret = nxpwifi_ret_802_11_scan_ext(priv, resp);
+ adapter->curr_cmd->wait_q_enabled = false;
+
+ return ret;
+}
+
+static int
+nxpwifi_cmd_sta_coalesce_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_coalesce_cfg *coalesce_cfg =
+ &cmd->params.coalesce_cfg;
+ struct nxpwifi_ds_coalesce_cfg *cfg =
+ (struct nxpwifi_ds_coalesce_cfg *)data_buf;
+ struct coalesce_filt_field_param *param;
+ u16 cnt, idx, length;
+ struct coalesce_receive_filt_rule *rule;
+
+ cmd->command = cpu_to_le16(HOST_CMD_COALESCE_CFG);
+ cmd->size = cpu_to_le16(S_DS_GEN);
+
+ coalesce_cfg->action = cpu_to_le16(cmd_action);
+ coalesce_cfg->num_of_rules = cpu_to_le16(cfg->num_of_rules);
+ rule = (void *)coalesce_cfg->rule_data;
+
+ for (cnt = 0; cnt < cfg->num_of_rules; cnt++) {
+ rule->header.type = cpu_to_le16(TLV_TYPE_COALESCE_RULE);
+ rule->max_coalescing_delay =
+ cpu_to_le16(cfg->rule[cnt].max_coalescing_delay);
+ rule->pkt_type = cfg->rule[cnt].pkt_type;
+ rule->num_of_fields = cfg->rule[cnt].num_of_fields;
+
+ length = 0;
+
+ param = rule->params;
+ for (idx = 0; idx < cfg->rule[cnt].num_of_fields; idx++) {
+ param->operation = cfg->rule[cnt].params[idx].operation;
+ param->operand_len =
+ cfg->rule[cnt].params[idx].operand_len;
+ param->offset =
+ cpu_to_le16(cfg->rule[cnt].params[idx].offset);
+ memcpy(param->operand_byte_stream,
+ cfg->rule[cnt].params[idx].operand_byte_stream,
+ param->operand_len);
+
+ length += sizeof(struct coalesce_filt_field_param);
+
+ param++;
+ }
+
+ /* Total rule length is sizeof max_coalescing_delay(u16),
+ * num_of_fields(u8), pkt_type(u8) and total length of the all
+ * params
+ */
+ rule->header.len = cpu_to_le16(length + sizeof(u16) +
+ sizeof(u8) + sizeof(u8));
+
+ /* Add the rule length to the command size*/
+ le16_unaligned_add_cpu(&cmd->size,
+ le16_to_cpu(rule->header.len) +
+ sizeof(struct nxpwifi_ie_types_header));
+
+ rule = (void *)((u8 *)rule->params + length);
+ }
+
+ /* Add sizeof action, num_of_rules to total command length */
+ le16_unaligned_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16));
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_mgmt_frame_reg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ cmd->command = cpu_to_le16(cmd_no);
+ cmd->params.reg_mask.action = cpu_to_le16(cmd_action);
+ cmd->params.reg_mask.mask =
+ cpu_to_le32(get_unaligned((u32 *)data_buf));
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mgmt_frame_reg) +
+ S_DS_GEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_remain_on_chan(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ cmd->command = cpu_to_le16(cmd_no);
+ memcpy(&cmd->params, data_buf,
+ sizeof(struct host_cmd_ds_remain_on_chan));
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_remain_on_chan) +
+ S_DS_GEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_remain_on_chan(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_remain_on_chan *resp_cfg = &resp->params.roc_cfg;
+ struct host_cmd_ds_remain_on_chan *roc_cfg =
+ (struct host_cmd_ds_remain_on_chan *)data_buf;
+
+ if (roc_cfg)
+ memcpy(roc_cfg, resp_cfg, sizeof(*roc_cfg));
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_gtk_rekey_offload(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_gtk_rekey_params *rekey = &cmd->params.rekey;
+ struct cfg80211_gtk_rekey_data *data =
+ (struct cfg80211_gtk_rekey_data *)data_buf;
+ u64 rekey_ctr;
+
+ cmd->command = cpu_to_le16(HOST_CMD_GTK_REKEY_OFFLOAD_CFG);
+ cmd->size = cpu_to_le16(sizeof(*rekey) + S_DS_GEN);
+
+ rekey->action = cpu_to_le16(cmd_action);
+ if (cmd_action == HOST_ACT_GEN_SET) {
+ memcpy(rekey->kek, data->kek, NL80211_KEK_LEN);
+ memcpy(rekey->kck, data->kck, NL80211_KCK_LEN);
+ rekey_ctr = be64_to_cpup((__be64 *)data->replay_ctr);
+ rekey->replay_ctr_low = cpu_to_le32((u32)rekey_ctr);
+ rekey->replay_ctr_high =
+ cpu_to_le32((u32)((u64)rekey_ctr >> 32));
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_11ac_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_11ac_cfg(priv, cmd, cmd_action, data_buf);
+}
+
+static int
+nxpwifi_cmd_sta_hs_wakeup_reason(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ cmd->command = cpu_to_le16(HOST_CMD_HS_WAKEUP_REASON);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_wakeup_reason) +
+ S_DS_GEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_hs_wakeup_reason(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_wakeup_reason *wakeup_reason =
+ (struct host_cmd_ds_wakeup_reason *)data_buf;
+ wakeup_reason->wakeup_reason =
+ resp->params.hs_wakeup_reason.wakeup_reason;
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_mc_policy(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_multi_chan_policy *mc_pol = &cmd->params.mc_policy;
+ const u16 *drcs_info = data_buf;
+
+ mc_pol->action = cpu_to_le16(cmd_action);
+ mc_pol->policy = cpu_to_le16(*drcs_info);
+ cmd->command = cpu_to_le16(HOST_CMD_MC_POLICY);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_multi_chan_policy) +
+ S_DS_GEN);
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_sdio_rx_aggr_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_sdio_sp_rx_aggr_cfg *cfg =
+ &cmd->params.sdio_rx_aggr_cfg;
+
+ cmd->command = cpu_to_le16(HOST_CMD_SDIO_SP_RX_AGGR_CFG);
+ cmd->size =
+ cpu_to_le16(sizeof(struct host_cmd_sdio_sp_rx_aggr_cfg) +
+ S_DS_GEN);
+ cfg->action = cmd_action;
+ if (cmd_action == HOST_ACT_GEN_SET)
+ cfg->enable = *(u8 *)data_buf;
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_sdio_rx_aggr_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct host_cmd_sdio_sp_rx_aggr_cfg *cfg =
+ &resp->params.sdio_rx_aggr_cfg;
+
+ adapter->sdio_rx_aggr_enable = cfg->enable;
+ adapter->sdio_rx_block_size = le16_to_cpu(cfg->block_size);
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_get_chan_info(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_sta_configure *sta_cfg_cmd = &cmd->params.sta_cfg;
+ struct host_cmd_tlv_channel_band *tlv_band_channel =
+ (struct host_cmd_tlv_channel_band *)sta_cfg_cmd->tlv_buffer;
+
+ cmd->command = cpu_to_le16(HOST_CMD_STA_CONFIGURE);
+ cmd->size = cpu_to_le16(sizeof(*sta_cfg_cmd) +
+ sizeof(*tlv_band_channel) + S_DS_GEN);
+ sta_cfg_cmd->action = cpu_to_le16(cmd_action);
+ memset(tlv_band_channel, 0, sizeof(*tlv_band_channel));
+ tlv_band_channel->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST);
+ tlv_band_channel->header.len = cpu_to_le16(sizeof(*tlv_band_channel) -
+ sizeof(struct nxpwifi_ie_types_header));
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_get_chan_info(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_sta_configure *sta_cfg_cmd = &resp->params.sta_cfg;
+ struct nxpwifi_channel_band *channel_band =
+ (struct nxpwifi_channel_band *)data_buf;
+ struct host_cmd_tlv_channel_band *tlv_band_channel;
+
+ tlv_band_channel =
+ (struct host_cmd_tlv_channel_band *)sta_cfg_cmd->tlv_buffer;
+ memcpy(&channel_band->band_config, &tlv_band_channel->band_config,
+ sizeof(struct nxpwifi_band_config));
+ channel_band->channel = tlv_band_channel->channel;
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_chan_region_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_chan_region_cfg *reg = &cmd->params.reg_cfg;
+
+ cmd->command = cpu_to_le16(HOST_CMD_CHAN_REGION_CFG);
+ cmd->size = cpu_to_le16(sizeof(*reg) + S_DS_GEN);
+
+ if (cmd_action == HOST_ACT_GEN_GET)
+ reg->action = cpu_to_le16(cmd_action);
+
+ return 0;
+}
+
+static struct ieee80211_regdomain *
+nxpwifi_create_custom_regdomain(struct nxpwifi_private *priv,
+ u8 *buf, u16 buf_len)
+{
+ u16 num_chan = buf_len / 2;
+ struct ieee80211_regdomain *regd;
+ struct ieee80211_reg_rule *rule;
+ bool new_rule;
+ int idx, freq, prev_freq = 0;
+ u32 bw, prev_bw = 0;
+ u8 chflags, prev_chflags = 0, valid_rules = 0;
+
+ if (WARN_ON_ONCE(num_chan > NL80211_MAX_SUPP_REG_RULES))
+ return ERR_PTR(-EINVAL);
+
+ regd = kzalloc(struct_size(regd, reg_rules, num_chan), GFP_KERNEL);
+ if (!regd)
+ return ERR_PTR(-ENOMEM);
+
+ for (idx = 0; idx < num_chan; idx++) {
+ u8 chan;
+ enum nl80211_band band;
+
+ chan = *buf++;
+ if (!chan) {
+ kfree(regd);
+ return NULL;
+ }
+ chflags = *buf++;
+ band = (chan <= 14) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
+ freq = ieee80211_channel_to_frequency(chan, band);
+ new_rule = false;
+
+ if (chflags & NXPWIFI_CHANNEL_DISABLED)
+ continue;
+
+ if (band == NL80211_BAND_5GHZ) {
+ if (!(chflags & NXPWIFI_CHANNEL_NOHT80))
+ bw = MHZ_TO_KHZ(80);
+ else if (!(chflags & NXPWIFI_CHANNEL_NOHT40))
+ bw = MHZ_TO_KHZ(40);
+ else
+ bw = MHZ_TO_KHZ(20);
+ } else {
+ if (!(chflags & NXPWIFI_CHANNEL_NOHT40))
+ bw = MHZ_TO_KHZ(40);
+ else
+ bw = MHZ_TO_KHZ(20);
+ }
+
+ if (idx == 0 || prev_chflags != chflags || prev_bw != bw ||
+ freq - prev_freq > 20) {
+ valid_rules++;
+ new_rule = true;
+ }
+
+ rule = ®d->reg_rules[valid_rules - 1];
+
+ rule->freq_range.end_freq_khz = MHZ_TO_KHZ(freq + 10);
+
+ prev_chflags = chflags;
+ prev_freq = freq;
+ prev_bw = bw;
+
+ if (!new_rule)
+ continue;
+
+ rule->freq_range.start_freq_khz = MHZ_TO_KHZ(freq - 10);
+ rule->power_rule.max_eirp = DBM_TO_MBM(19);
+
+ if (chflags & NXPWIFI_CHANNEL_PASSIVE)
+ rule->flags = NL80211_RRF_NO_IR;
+
+ if (chflags & NXPWIFI_CHANNEL_DFS)
+ rule->flags = NL80211_RRF_DFS;
+
+ rule->freq_range.max_bandwidth_khz = bw;
+ }
+
+ regd->n_reg_rules = valid_rules;
+ regd->alpha2[0] = '9';
+ regd->alpha2[1] = '9';
+
+ return regd;
+}
+
+static int
+nxpwifi_ret_sta_chan_region_cfg(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_chan_region_cfg *reg = &resp->params.reg_cfg;
+ u16 action = le16_to_cpu(reg->action);
+ u16 tlv, tlv_buf_len, tlv_buf_left;
+ struct nxpwifi_ie_types_header *head;
+ struct ieee80211_regdomain *regd;
+ u8 *tlv_buf;
+
+ if (action != HOST_ACT_GEN_GET)
+ return 0;
+
+ tlv_buf = (u8 *)reg + sizeof(*reg);
+ tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*reg);
+
+ while (tlv_buf_left >= sizeof(*head)) {
+ head = (struct nxpwifi_ie_types_header *)tlv_buf;
+ tlv = le16_to_cpu(head->type);
+ tlv_buf_len = le16_to_cpu(head->len);
+
+ if (tlv_buf_left < (sizeof(*head) + tlv_buf_len))
+ break;
+
+ switch (tlv) {
+ case TLV_TYPE_CHAN_ATTR_CFG:
+ nxpwifi_dbg_dump(priv->adapter, CMD_D, "CHAN:",
+ (u8 *)head + sizeof(*head),
+ tlv_buf_len);
+ regd = nxpwifi_create_custom_regdomain(priv, (u8 *)head
+ + sizeof(*head),
+ tlv_buf_len);
+ if (!IS_ERR(regd))
+ priv->adapter->regd = regd;
+ break;
+ }
+
+ tlv_buf += (sizeof(*head) + tlv_buf_len);
+ tlv_buf_left -= (sizeof(*head) + tlv_buf_len);
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_sta_pkt_aggr_ctrl(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ cmd->command = cpu_to_le16(cmd_no);
+ cmd->params.pkt_aggr_ctrl.action = cpu_to_le16(cmd_action);
+ cmd->params.pkt_aggr_ctrl.enable = cpu_to_le16(*(u16 *)data_buf);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_pkt_aggr_ctrl) +
+ S_DS_GEN);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_sta_pkt_aggr_ctrl(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_pkt_aggr_ctrl *pkt_aggr_ctrl =
+ &resp->params.pkt_aggr_ctrl;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ adapter->bus_aggr.enable = le16_to_cpu(pkt_aggr_ctrl->enable);
+ if (adapter->bus_aggr.enable)
+ adapter->intf_hdr_len = INTF_HEADER_LEN;
+ adapter->bus_aggr.mode = NXPWIFI_BUS_AGGR_MODE_LEN_V2;
+ adapter->bus_aggr.tx_aggr_max_size =
+ le16_to_cpu(pkt_aggr_ctrl->tx_aggr_max_size);
+ adapter->bus_aggr.tx_aggr_max_num =
+ le16_to_cpu(pkt_aggr_ctrl->tx_aggr_max_num);
+ adapter->bus_aggr.tx_aggr_align =
+ le16_to_cpu(pkt_aggr_ctrl->tx_aggr_align);
+
+ return 0;
+}
+
+static const struct nxpwifi_cmd_entry cmd_table_sta[] = {
+ {.cmd_no = HOST_CMD_GET_HW_SPEC,
+ .prepare_cmd = nxpwifi_cmd_sta_get_hw_spec,
+ .cmd_resp = nxpwifi_ret_sta_get_hw_spec},
+ {.cmd_no = HOST_CMD_802_11_SCAN,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_scan,
+ .cmd_resp = nxpwifi_ret_sta_802_11_scan},
+ {.cmd_no = HOST_CMD_802_11_GET_LOG,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_get_log,
+ .cmd_resp = nxpwifi_ret_sta_802_11_get_log},
+ {.cmd_no = HOST_CMD_MAC_MULTICAST_ADR,
+ .prepare_cmd = nxpwifi_cmd_sta_mac_multicast_adr,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_802_11_ASSOCIATE,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_associate,
+ .cmd_resp = nxpwifi_ret_sta_802_11_associate},
+ {.cmd_no = HOST_CMD_802_11_SNMP_MIB,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_snmp_mib,
+ .cmd_resp = nxpwifi_ret_sta_802_11_snmp_mib},
+ {.cmd_no = HOST_CMD_MAC_REG_ACCESS,
+ .prepare_cmd = nxpwifi_cmd_sta_reg_access,
+ .cmd_resp = nxpwifi_ret_sta_reg_access},
+ {.cmd_no = HOST_CMD_BBP_REG_ACCESS,
+ .prepare_cmd = nxpwifi_cmd_sta_reg_access,
+ .cmd_resp = nxpwifi_ret_sta_reg_access},
+ {.cmd_no = HOST_CMD_RF_REG_ACCESS,
+ .prepare_cmd = nxpwifi_cmd_sta_reg_access,
+ .cmd_resp = nxpwifi_ret_sta_reg_access},
+ {.cmd_no = HOST_CMD_RF_TX_PWR,
+ .prepare_cmd = nxpwifi_cmd_sta_rf_tx_pwr,
+ .cmd_resp = nxpwifi_ret_sta_rf_tx_pwr},
+ {.cmd_no = HOST_CMD_RF_ANTENNA,
+ .prepare_cmd = nxpwifi_cmd_sta_rf_antenna,
+ .cmd_resp = nxpwifi_ret_sta_rf_antenna},
+ {.cmd_no = HOST_CMD_802_11_DEAUTHENTICATE,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_deauthenticate,
+ .cmd_resp = nxpwifi_ret_sta_802_11_deauthenticate},
+ {.cmd_no = HOST_CMD_MAC_CONTROL,
+ .prepare_cmd = nxpwifi_cmd_sta_mac_control,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_802_11_MAC_ADDRESS,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_mac_address,
+ .cmd_resp = nxpwifi_ret_sta_802_11_mac_address},
+ {.cmd_no = HOST_CMD_802_11_EEPROM_ACCESS,
+ .prepare_cmd = nxpwifi_cmd_sta_reg_access,
+ .cmd_resp = nxpwifi_ret_sta_reg_access},
+ {.cmd_no = HOST_CMD_802_11D_DOMAIN_INFO,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11d_domain_info,
+ .cmd_resp = nxpwifi_ret_sta_802_11d_domain_info},
+ {.cmd_no = HOST_CMD_802_11_KEY_MATERIAL,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_key_material,
+ .cmd_resp = nxpwifi_ret_sta_802_11_key_material},
+ {.cmd_no = HOST_CMD_802_11_BG_SCAN_CONFIG,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_bg_scan_config,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_802_11_BG_SCAN_QUERY,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_bg_scan_query,
+ .cmd_resp = nxpwifi_ret_sta_802_11_bg_scan_query},
+ {.cmd_no = HOST_CMD_WMM_GET_STATUS,
+ .prepare_cmd = nxpwifi_cmd_sta_wmm_get_status,
+ .cmd_resp = nxpwifi_ret_sta_wmm_get_status},
+ {.cmd_no = HOST_CMD_802_11_SUBSCRIBE_EVENT,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_subsc_evt,
+ .cmd_resp = nxpwifi_ret_sta_subsc_evt},
+ {.cmd_no = HOST_CMD_802_11_TX_RATE_QUERY,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_tx_rate_query,
+ .cmd_resp = nxpwifi_ret_sta_802_11_tx_rate_query},
+ {.cmd_no = HOST_CMD_MEM_ACCESS,
+ .prepare_cmd = nxpwifi_cmd_sta_mem_access,
+ .cmd_resp = nxpwifi_ret_sta_mem_access},
+ {.cmd_no = HOST_CMD_CFG_DATA,
+ .prepare_cmd = nxpwifi_cmd_sta_cfg_data,
+ .cmd_resp = nxpwifi_ret_sta_cfg_data},
+ {.cmd_no = HOST_CMD_VERSION_EXT,
+ .prepare_cmd = nxpwifi_cmd_sta_ver_ext,
+ .cmd_resp = nxpwifi_ret_sta_ver_ext},
+ {.cmd_no = HOST_CMD_MEF_CFG,
+ .prepare_cmd = nxpwifi_cmd_sta_mef_cfg,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_RSSI_INFO,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_rssi_info,
+ .cmd_resp = nxpwifi_ret_sta_802_11_rssi_info},
+ {.cmd_no = HOST_CMD_FUNC_INIT,
+ .prepare_cmd = nxpwifi_cmd_sta_func_init,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_FUNC_SHUTDOWN,
+ .prepare_cmd = nxpwifi_cmd_sta_func_shutdown,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_PMIC_REG_ACCESS,
+ .prepare_cmd = nxpwifi_cmd_sta_reg_access,
+ .cmd_resp = nxpwifi_ret_sta_reg_access},
+ {.cmd_no = HOST_CMD_11N_CFG,
+ .prepare_cmd = nxpwifi_cmd_sta_11n_cfg,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_11N_ADDBA_REQ,
+ .prepare_cmd = nxpwifi_cmd_sta_11n_addba_req,
+ .cmd_resp = nxpwifi_ret_sta_11n_addba_req},
+ {.cmd_no = HOST_CMD_11N_ADDBA_RSP,
+ .prepare_cmd = nxpwifi_cmd_sta_11n_addba_rsp,
+ .cmd_resp = nxpwifi_ret_sta_11n_addba_rsp},
+ {.cmd_no = HOST_CMD_11N_DELBA,
+ .prepare_cmd = nxpwifi_cmd_sta_11n_delba,
+ .cmd_resp = nxpwifi_ret_sta_11n_delba},
+ {.cmd_no = HOST_CMD_TXPWR_CFG,
+ .prepare_cmd = nxpwifi_cmd_sta_tx_power_cfg,
+ .cmd_resp = nxpwifi_ret_sta_tx_power_cfg},
+ {.cmd_no = HOST_CMD_TX_RATE_CFG,
+ .prepare_cmd = nxpwifi_cmd_sta_tx_rate_cfg,
+ .cmd_resp = nxpwifi_ret_sta_tx_rate_cfg},
+ {.cmd_no = HOST_CMD_RECONFIGURE_TX_BUFF,
+ .prepare_cmd = nxpwifi_cmd_sta_reconfigure_rx_buff,
+ .cmd_resp = nxpwifi_ret_sta_reconfigure_rx_buff},
+ {.cmd_no = HOST_CMD_CHAN_REPORT_REQUEST,
+ .prepare_cmd = nxpwifi_cmd_sta_chan_report_request,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_AMSDU_AGGR_CTRL,
+ .prepare_cmd = nxpwifi_cmf_sta_amsdu_aggr_ctrl,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_ROBUST_COEX,
+ .prepare_cmd = nxpwifi_cmd_sta_robust_coex,
+ .cmd_resp = nxpwifi_ret_sta_robust_coex},
+ {.cmd_no = HOST_CMD_802_11_PS_MODE_ENH,
+ .prepare_cmd = nxpwifi_cmd_sta_enh_power_mode,
+ .cmd_resp = nxpwifi_ret_sta_enh_power_mode},
+ {.cmd_no = HOST_CMD_802_11_HS_CFG_ENH,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_hs_cfg,
+ .cmd_resp = nxpwifi_ret_sta_802_11_hs_cfg},
+ {.cmd_no = HOST_CMD_CAU_REG_ACCESS,
+ .prepare_cmd = nxpwifi_cmd_sta_reg_access,
+ .cmd_resp = nxpwifi_ret_sta_reg_access},
+ {.cmd_no = HOST_CMD_SET_BSS_MODE,
+ .prepare_cmd = nxpwifi_cmd_sta_set_bss_mode,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_802_11_SCAN_EXT,
+ .prepare_cmd = nxpwifi_cmd_sta_802_11_scan_ext,
+ .cmd_resp = nxpwifi_ret_sta_802_11_scan_ext},
+ {.cmd_no = HOST_CMD_COALESCE_CFG,
+ .prepare_cmd = nxpwifi_cmd_sta_coalesce_cfg,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_MGMT_FRAME_REG,
+ .prepare_cmd = nxpwifi_cmd_sta_mgmt_frame_reg,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_REMAIN_ON_CHAN,
+ .prepare_cmd = nxpwifi_cmd_sta_remain_on_chan,
+ .cmd_resp = nxpwifi_ret_sta_remain_on_chan},
+ {.cmd_no = HOST_CMD_GTK_REKEY_OFFLOAD_CFG,
+ .prepare_cmd = nxpwifi_cmd_sta_gtk_rekey_offload,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_11AC_CFG,
+ .prepare_cmd = nxpwifi_cmd_sta_11ac_cfg,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_HS_WAKEUP_REASON,
+ .prepare_cmd = nxpwifi_cmd_sta_hs_wakeup_reason,
+ .cmd_resp = nxpwifi_ret_sta_hs_wakeup_reason},
+ {.cmd_no = HOST_CMD_MC_POLICY,
+ .prepare_cmd = nxpwifi_cmd_sta_mc_policy,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_FW_DUMP_EVENT,
+ .prepare_cmd = nxpwifi_cmd_fill_head_only,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_SDIO_SP_RX_AGGR_CFG,
+ .prepare_cmd = nxpwifi_cmd_sta_sdio_rx_aggr_cfg,
+ .cmd_resp = nxpwifi_ret_sta_sdio_rx_aggr_cfg},
+ {.cmd_no = HOST_CMD_STA_CONFIGURE,
+ .prepare_cmd = nxpwifi_cmd_sta_get_chan_info,
+ .cmd_resp = nxpwifi_ret_sta_get_chan_info},
+ {.cmd_no = HOST_CMD_CHAN_REGION_CFG,
+ .prepare_cmd = nxpwifi_cmd_sta_chan_region_cfg,
+ .cmd_resp = nxpwifi_ret_sta_chan_region_cfg},
+ {.cmd_no = HOST_CMD_PACKET_AGGR_CTRL,
+ .prepare_cmd = nxpwifi_cmd_sta_pkt_aggr_ctrl,
+ .cmd_resp = nxpwifi_ret_sta_pkt_aggr_ctrl},
+};
+
+/* This function prepares the commands before sending them to the firmware.
+ *
+ * This is a generic function which calls specific command preparation
+ * routines based upon the command number.
+ */
+int nxpwifi_sta_prepare_cmd(struct nxpwifi_private *priv,
+ struct cmd_ctrl_node *cmd_node,
+ u16 cmd_action, u32 cmd_oid)
+
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u16 cmd_no = cmd_node->cmd_no;
+ struct host_cmd_ds_command *cmd =
+ (struct host_cmd_ds_command *)cmd_node->skb->data;
+ void *data_buf = cmd_node->data_buf;
+ int i, ret = -1;
+
+ for (i = 0; i < ARRAY_SIZE(cmd_table_sta); i++) {
+ if (cmd_no == cmd_table_sta[i].cmd_no) {
+ if (cmd_table_sta[i].prepare_cmd)
+ ret = cmd_table_sta[i].prepare_cmd(priv, cmd,
+ cmd_no,
+ data_buf,
+ cmd_action,
+ cmd_oid);
+ cmd_node->cmd_resp = cmd_table_sta[i].cmd_resp;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(cmd_table_sta))
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: unknown command: %#x\n",
+ __func__, cmd_no);
+ else
+ nxpwifi_dbg(adapter, EVENT,
+ "%s: command: %#x\n",
+ __func__, cmd_no);
+
+ return ret;
+}
+
+int nxpwifi_dnld_dt_cfgdata(struct nxpwifi_private *priv,
+ struct device_node *node, const char *prefix)
+{
+#ifdef CONFIG_OF
+ struct property *prop;
+ size_t len = strlen(prefix);
+ int ret;
+
+ /* look for all matching property names */
+ for_each_property_of_node(node, prop) {
+ if (len > strlen(prop->name) ||
+ strncmp(prop->name, prefix, len))
+ continue;
+
+ /* property header is 6 bytes, data must fit in cmd buffer */
+ if (prop->value && prop->length > 6 &&
+ prop->length <= NXPWIFI_SIZE_OF_CMD_BUFFER - S_DS_GEN) {
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_CFG_DATA,
+ HOST_ACT_GEN_SET, 0,
+ prop, true);
+ if (ret)
+ return ret;
+ }
+ }
+#endif
+ return 0;
+}
+
+/* This function issues commands to initialize firmware.
+ *
+ * This is called after firmware download to bring the card to
+ * working state.
+ * Function is also called during reinitialization of virtual
+ * interfaces.
+ */
+int nxpwifi_sta_init_cmd(struct nxpwifi_private *priv, u8 first_sta, bool init)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int ret;
+ struct nxpwifi_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl;
+ struct nxpwifi_ds_auto_ds auto_ds;
+ enum state_11d_t state_11d;
+ struct nxpwifi_ds_11n_tx_cfg tx_cfg;
+ u8 sdio_sp_rx_aggr_enable;
+ int data;
+
+ if (first_sta) {
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_FUNC_INIT,
+ HOST_ACT_GEN_SET, 0, NULL, true);
+ if (ret)
+ return -1;
+
+ /* Download calibration data to firmware.
+ * The cal-data can be read from device tree and/or
+ * a configuration file and downloaded to firmware.
+ */
+ if (adapter->dt_node) {
+ if (of_property_read_u32(adapter->dt_node,
+ "nxp,wakeup-pin",
+ &data) == 0) {
+ pr_debug("Wakeup pin = 0x%x\n", data);
+ adapter->hs_cfg.gpio = data;
+ }
+
+ nxpwifi_dnld_dt_cfgdata(priv, adapter->dt_node,
+ "nxp,caldata");
+ }
+
+ if (adapter->cal_data)
+ nxpwifi_send_cmd(priv, HOST_CMD_CFG_DATA,
+ HOST_ACT_GEN_SET, 0, NULL, true);
+
+ /* Read MAC address from HW */
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_GET_HW_SPEC,
+ HOST_ACT_GEN_GET, 0, NULL, true);
+ if (ret)
+ return -1;
+
+ /** Set SDIO Single Port RX Aggr Info */
+ if (priv->adapter->iface_type == NXPWIFI_SDIO &&
+ ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info) &&
+ !priv->adapter->host_disable_sdio_rx_aggr) {
+ sdio_sp_rx_aggr_enable = true;
+ ret = nxpwifi_send_cmd(priv,
+ HOST_CMD_SDIO_SP_RX_AGGR_CFG,
+ HOST_ACT_GEN_SET, 0,
+ &sdio_sp_rx_aggr_enable,
+ true);
+ if (ret) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "error while enabling SP aggregation..disable it");
+ adapter->sdio_rx_aggr_enable = false;
+ }
+ }
+
+ /* Reconfigure tx buf size */
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_RECONFIGURE_TX_BUFF,
+ HOST_ACT_GEN_SET, 0,
+ &priv->adapter->tx_buf_size, true);
+ if (ret)
+ return -1;
+
+ if (priv->bss_type != NXPWIFI_BSS_TYPE_UAP) {
+ /* Enable IEEE PS by default */
+ priv->adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_PSP;
+ ret = nxpwifi_send_cmd(priv,
+ HOST_CMD_802_11_PS_MODE_ENH,
+ EN_AUTO_PS, BITMAP_STA_PS, NULL,
+ true);
+ if (ret)
+ return -1;
+ }
+
+ nxpwifi_send_cmd(priv, HOST_CMD_CHAN_REGION_CFG,
+ HOST_ACT_GEN_GET, 0, NULL, true);
+ }
+
+ /* get tx rate */
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_TX_RATE_CFG,
+ HOST_ACT_GEN_GET, 0, NULL, true);
+ if (ret)
+ return -1;
+ priv->data_rate = 0;
+
+ /* get tx power */
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_RF_TX_PWR,
+ HOST_ACT_GEN_GET, 0, NULL, true);
+ if (ret)
+ return -1;
+
+ memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl));
+ amsdu_aggr_ctrl.enable = true;
+ /* Send request to firmware */
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_AMSDU_AGGR_CTRL,
+ HOST_ACT_GEN_SET, 0,
+ &amsdu_aggr_ctrl, true);
+ if (ret)
+ return -1;
+ /* MAC Control must be the last command in init_fw */
+ /* set MAC Control */
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL,
+ HOST_ACT_GEN_SET, 0,
+ &priv->curr_pkt_filter, true);
+ if (ret)
+ return -1;
+
+ if (!disable_auto_ds && first_sta &&
+ priv->bss_type != NXPWIFI_BSS_TYPE_UAP) {
+ /* Enable auto deep sleep */
+ auto_ds.auto_ds = DEEP_SLEEP_ON;
+ auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME;
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH,
+ EN_AUTO_PS, BITMAP_AUTO_DS,
+ &auto_ds, true);
+ if (ret)
+ return -1;
+ }
+
+ if (priv->bss_type != NXPWIFI_BSS_TYPE_UAP) {
+ /* Send cmd to FW to enable/disable 11D function */
+ state_11d = ENABLE_11D;
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB,
+ HOST_ACT_GEN_SET, DOT11D_I,
+ &state_11d, true);
+ if (ret)
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "11D: failed to enable 11D\n");
+ }
+
+ /* Send cmd to FW to configure 11n specific configuration
+ * (Short GI, Channel BW, Green field support etc.) for transmit
+ */
+ tx_cfg.tx_htcap = NXPWIFI_FW_DEF_HTTXCFG;
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_11N_CFG,
+ HOST_ACT_GEN_SET, 0, &tx_cfg, true);
+
+ if (init) {
+ /* set last_init_cmd before sending the command */
+ priv->adapter->last_init_cmd = HOST_CMD_11N_CFG;
+ ret = -EINPROGRESS;
+ }
+
+ return ret;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 29/43] wifi: nxpwifi: add sta_event.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (27 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 28/43] wifi: nxpwifi: add sta_cmd.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 30/43] wifi: nxpwifi: add sta_ioctl.c David Lin
` (15 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/sta_event.c | 858 +++++++++++++++++++
1 file changed, 858 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/sta_event.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_event.c b/drivers/net/wireless/nxp/nxpwifi/sta_event.c
new file mode 100644
index 000000000000..61daec04665b
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/sta_event.c
@@ -0,0 +1,858 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: station event handling
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cmdevt.h"
+#include "wmm.h"
+#include "11n.h"
+
+static int
+nxpwifi_sta_event_link_lost(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u16 reason_code;
+
+ adapter->dbg.num_event_link_lost++;
+ if (priv->media_connected) {
+ reason_code = get_unaligned_le16(adapter->event_body);
+ adapter->priv_link_lost = priv;
+ adapter->host_mlme_link_lost = true;
+ queue_work(adapter->host_mlme_workqueue,
+ &adapter->host_mlme_work);
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_link_sensed(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (!netif_carrier_ok(priv->netdev))
+ netif_carrier_on(priv->netdev);
+ nxpwifi_wake_up_net_dev_queue(priv->netdev, adapter);
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_deauthenticated(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u16 reason_code;
+
+ if (priv->wps.session_enable) {
+ nxpwifi_dbg(adapter, INFO,
+ "info: receive deauth event in wps session\n");
+ } else {
+ adapter->dbg.num_event_deauth++;
+ if (priv->media_connected) {
+ reason_code =
+ get_unaligned_le16(adapter->event_body);
+ nxpwifi_reset_connect_state(priv, reason_code, true);
+ }
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_disassociated(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u16 reason_code;
+
+ if (priv->wps.session_enable) {
+ nxpwifi_dbg(adapter, INFO,
+ "info: receive disassoc event in wps session\n");
+ } else {
+ adapter->dbg.num_event_disassoc++;
+ if (priv->media_connected) {
+ reason_code =
+ get_unaligned_le16(adapter->event_body);
+ nxpwifi_reset_connect_state(priv, reason_code, true);
+ }
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_ps_awake(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (!adapter->pps_uapsd_mode &&
+ priv->port_open &&
+ priv->media_connected && adapter->sleep_period.period) {
+ adapter->pps_uapsd_mode = true;
+ nxpwifi_dbg(adapter, EVENT,
+ "event: PPS/UAPSD mode activated\n");
+ }
+ adapter->tx_lock_flag = false;
+ if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) {
+ if (nxpwifi_check_last_packet_indication(priv)) {
+ if (adapter->data_sent ||
+ (adapter->if_ops.is_port_ready &&
+ !adapter->if_ops.is_port_ready(priv))) {
+ adapter->ps_state = PS_STATE_AWAKE;
+ adapter->pm_wakeup_card_req = false;
+ adapter->pm_wakeup_fw_try = false;
+ del_timer(&adapter->wakeup_timer);
+ } else {
+ if (!nxpwifi_send_null_packet
+ (priv,
+ NXPWIFI_TxPD_POWER_MGMT_NULL_PACKET |
+ NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET))
+ adapter->ps_state = PS_STATE_SLEEP;
+ }
+
+ return 0;
+ }
+ }
+
+ adapter->ps_state = PS_STATE_AWAKE;
+ adapter->pm_wakeup_card_req = false;
+ adapter->pm_wakeup_fw_try = false;
+ del_timer(&adapter->wakeup_timer);
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_ps_sleep(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ adapter->ps_state = PS_STATE_PRE_SLEEP;
+ nxpwifi_check_ps_cond(adapter);
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_mic_err_multicast(struct nxpwifi_private *priv)
+{
+ cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid,
+ NL80211_KEYTYPE_GROUP,
+ -1, NULL, GFP_KERNEL);
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_mic_err_unicast(struct nxpwifi_private *priv)
+{
+ cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid,
+ NL80211_KEYTYPE_PAIRWISE,
+ -1, NULL, GFP_KERNEL);
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_deep_sleep_awake(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ adapter->if_ops.wakeup_complete(adapter);
+ if (adapter->is_deep_sleep)
+ adapter->is_deep_sleep = false;
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_wmm_status_change(struct nxpwifi_private *priv)
+{
+ return nxpwifi_send_cmd(priv, HOST_CMD_WMM_GET_STATUS,
+ 0, 0, NULL, false);
+}
+
+static int
+nxpwifi_sta_event_bs_scan_report(struct nxpwifi_private *priv)
+{
+ return nxpwifi_send_cmd(priv, HOST_CMD_802_11_BG_SCAN_QUERY,
+ HOST_ACT_GEN_GET, 0, NULL, false);
+}
+
+static int
+nxpwifi_sta_event_rssi_low(struct nxpwifi_private *priv)
+{
+ cfg80211_cqm_rssi_notify(priv->netdev,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ 0, GFP_KERNEL);
+ priv->subsc_evt_rssi_state = RSSI_LOW_RECVD;
+
+ return nxpwifi_send_cmd(priv, HOST_CMD_RSSI_INFO,
+ HOST_ACT_GEN_GET, 0, NULL, false);
+}
+
+static int
+nxpwifi_sta_event_rssi_high(struct nxpwifi_private *priv)
+{
+ cfg80211_cqm_rssi_notify(priv->netdev,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ 0, GFP_KERNEL);
+ priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD;
+
+ return nxpwifi_send_cmd(priv, HOST_CMD_RSSI_INFO,
+ HOST_ACT_GEN_GET, 0, NULL, false);
+}
+
+static int
+nxpwifi_sta_event_port_release(struct nxpwifi_private *priv)
+{
+ priv->port_open = true;
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_addba(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ return nxpwifi_send_cmd(priv, HOST_CMD_11N_ADDBA_RSP,
+ HOST_ACT_GEN_SET, 0,
+ adapter->event_body, false);
+}
+
+static int
+nxpwifi_sta_event_delba(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ nxpwifi_11n_delete_ba_stream(priv, adapter->event_body);
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_bs_stream_timeout(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_11n_batimeout *event =
+ (struct host_cmd_ds_11n_batimeout *)adapter->event_body;
+
+ nxpwifi_11n_ba_stream_timeout(priv, event);
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_amsdu_aggr_ctrl(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u16 ctrl;
+
+ ctrl = get_unaligned_le16(adapter->event_body);
+ adapter->tx_buf_size = min_t(u16, adapter->curr_tx_buf_size, ctrl);
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_hs_act_req(struct nxpwifi_private *priv)
+{
+ return nxpwifi_send_cmd(priv, HOST_CMD_802_11_HS_CFG_ENH,
+ 0, 0, NULL, false);
+}
+
+static int
+nxpwifi_sta_event_channel_switch_ann(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_bssdescriptor *bss_desc;
+
+ bss_desc = &priv->curr_bss_params.bss_descriptor;
+ priv->csa_expire_time = jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME);
+ priv->csa_chan = bss_desc->channel;
+ return nxpwifi_send_cmd(priv, HOST_CMD_802_11_DEAUTHENTICATE,
+ HOST_ACT_GEN_SET, 0,
+ bss_desc->mac_address, false);
+}
+
+static int
+nxpwifi_sta_event_radar_detected(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ return nxpwifi_11h_handle_radar_detected(priv, adapter->event_skb);
+}
+
+static int
+nxpwifi_sta_event_channel_report_rdy(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ return nxpwifi_11h_handle_chanrpt_ready(priv, adapter->event_skb);
+}
+
+static int
+nxpwifi_sta_event_tx_data_pause(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ nxpwifi_process_tx_pause_event(priv, adapter->event_skb);
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_ext_scan_report(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ void *buf = adapter->event_skb->data;
+ int ret = 0;
+
+ /* We intend to skip this event during suspend, but handle
+ * it in interface disabled case
+ */
+ if (adapter->ext_scan && (!priv->scan_aborting ||
+ !netif_running(priv->netdev)))
+ ret = nxpwifi_handle_event_ext_scan_report(priv, buf);
+
+ return ret;
+}
+
+static int
+nxpwifi_sta_event_rxba_sync(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ nxpwifi_11n_rxba_sync_event(priv, adapter->event_body,
+ adapter->event_skb->len -
+ sizeof(adapter->event_cause));
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_remain_on_chan_expired(struct nxpwifi_private *priv)
+{
+ if (priv->auth_flag & HOST_MLME_AUTH_PENDING) {
+ priv->auth_flag = 0;
+ priv->auth_alg = WLAN_AUTH_NONE;
+ } else {
+ cfg80211_remain_on_channel_expired(&priv->wdev,
+ priv->roc_cfg.cookie,
+ &priv->roc_cfg.chan,
+ GFP_ATOMIC);
+ }
+
+ memset(&priv->roc_cfg, 0x00, sizeof(struct nxpwifi_roc_cfg));
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_bg_scan_stopped(struct nxpwifi_private *priv)
+{
+ cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0);
+ if (priv->sched_scanning)
+ priv->sched_scanning = false;
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_multi_chan_info(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ nxpwifi_process_multi_chan_event(priv, adapter->event_skb);
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_tx_status_report(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ nxpwifi_parse_tx_status_event(priv, adapter->event_body);
+
+ return 0;
+}
+
+static int
+nxpwifi_sta_event_bt_coex_wlan_para_change(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (!adapter->ignore_btcoex_events)
+ nxpwifi_bt_coex_wlan_param_update_event(priv,
+ adapter->event_skb);
+
+ return 0;
+}
+
+static const struct nxpwifi_evt_entry evt_table_sta[] = {
+ {.event_cause = EVENT_LINK_LOST,
+ .event_handler = nxpwifi_sta_event_link_lost},
+ {.event_cause = EVENT_LINK_SENSED,
+ .event_handler = nxpwifi_sta_event_link_sensed},
+ {.event_cause = EVENT_DEAUTHENTICATED,
+ .event_handler = nxpwifi_sta_event_deauthenticated},
+ {.event_cause = EVENT_DISASSOCIATED,
+ .event_handler = nxpwifi_sta_event_disassociated},
+ {.event_cause = EVENT_PS_AWAKE,
+ .event_handler = nxpwifi_sta_event_ps_awake},
+ {.event_cause = EVENT_PS_SLEEP,
+ .event_handler = nxpwifi_sta_event_ps_sleep},
+ {.event_cause = EVENT_MIC_ERR_MULTICAST,
+ .event_handler = nxpwifi_sta_event_mic_err_multicast},
+ {.event_cause = EVENT_MIC_ERR_UNICAST,
+ .event_handler = nxpwifi_sta_event_mic_err_unicast},
+ {.event_cause = EVENT_DEEP_SLEEP_AWAKE,
+ .event_handler = nxpwifi_sta_event_deep_sleep_awake},
+ {.event_cause = EVENT_WMM_STATUS_CHANGE,
+ .event_handler = nxpwifi_sta_event_wmm_status_change},
+ {.event_cause = EVENT_BG_SCAN_REPORT,
+ .event_handler = nxpwifi_sta_event_bs_scan_report},
+ {.event_cause = EVENT_RSSI_LOW,
+ .event_handler = nxpwifi_sta_event_rssi_low},
+ {.event_cause = EVENT_RSSI_HIGH,
+ .event_handler = nxpwifi_sta_event_rssi_high},
+ {.event_cause = EVENT_PORT_RELEASE,
+ .event_handler = nxpwifi_sta_event_port_release},
+ {.event_cause = EVENT_ADDBA,
+ .event_handler = nxpwifi_sta_event_addba},
+ {.event_cause = EVENT_DELBA,
+ .event_handler = nxpwifi_sta_event_delba},
+ {.event_cause = EVENT_BA_STREAM_TIEMOUT,
+ .event_handler = nxpwifi_sta_event_bs_stream_timeout},
+ {.event_cause = EVENT_AMSDU_AGGR_CTRL,
+ .event_handler = nxpwifi_sta_event_amsdu_aggr_ctrl},
+ {.event_cause = EVENT_HS_ACT_REQ,
+ .event_handler = nxpwifi_sta_event_hs_act_req},
+ {.event_cause = EVENT_CHANNEL_SWITCH_ANN,
+ .event_handler = nxpwifi_sta_event_channel_switch_ann},
+ {.event_cause = EVENT_RADAR_DETECTED,
+ .event_handler = nxpwifi_sta_event_radar_detected},
+ {.event_cause = EVENT_CHANNEL_REPORT_RDY,
+ .event_handler = nxpwifi_sta_event_channel_report_rdy},
+ {.event_cause = EVENT_TX_DATA_PAUSE,
+ .event_handler = nxpwifi_sta_event_tx_data_pause},
+ {.event_cause = EVENT_EXT_SCAN_REPORT,
+ .event_handler = nxpwifi_sta_event_ext_scan_report},
+ {.event_cause = EVENT_RXBA_SYNC,
+ .event_handler = nxpwifi_sta_event_rxba_sync},
+ {.event_cause = EVENT_REMAIN_ON_CHAN_EXPIRED,
+ .event_handler = nxpwifi_sta_event_remain_on_chan_expired},
+ {.event_cause = EVENT_BG_SCAN_STOPPED,
+ .event_handler = nxpwifi_sta_event_bg_scan_stopped},
+ {.event_cause = EVENT_MULTI_CHAN_INFO,
+ .event_handler = nxpwifi_sta_event_multi_chan_info},
+ {.event_cause = EVENT_TX_STATUS_REPORT,
+ .event_handler = nxpwifi_sta_event_tx_status_report},
+ {.event_cause = EVENT_BT_COEX_WLAN_PARA_CHANGE,
+ .event_handler = nxpwifi_sta_event_bt_coex_wlan_para_change},
+ {.event_cause = EVENT_DUMMY_HOST_WAKEUP_SIGNAL,
+ .event_handler = NULL},
+ {.event_cause = EVENT_MIB_CHANGED,
+ .event_handler = NULL},
+ {.event_cause = EVENT_INIT_DONE,
+ .event_handler = NULL},
+ {.event_cause = EVENT_SNR_LOW,
+ .event_handler = NULL},
+ {.event_cause = EVENT_MAX_FAIL,
+ .event_handler = NULL},
+ {.event_cause = EVENT_SNR_HIGH,
+ .event_handler = NULL},
+ {.event_cause = EVENT_DATA_RSSI_LOW,
+ .event_handler = NULL},
+ {.event_cause = EVENT_DATA_SNR_LOW,
+ .event_handler = NULL},
+ {.event_cause = EVENT_DATA_RSSI_HIGH,
+ .event_handler = NULL},
+ {.event_cause = EVENT_DATA_SNR_HIGH,
+ .event_handler = NULL},
+ {.event_cause = EVENT_LINK_QUALITY,
+ .event_handler = NULL},
+ {.event_cause = EVENT_PRE_BEACON_LOST,
+ .event_handler = NULL},
+ {.event_cause = EVENT_WEP_ICV_ERR,
+ .event_handler = NULL},
+ {.event_cause = EVENT_BW_CHANGE,
+ .event_handler = NULL},
+ {.event_cause = EVENT_HOSTWAKE_STAIE,
+ .event_handler = NULL},
+ {.event_cause = EVENT_UNKNOWN_DEBUG,
+ .event_handler = NULL},
+};
+
+static void nxpwifi_process_uap_tx_pause(struct nxpwifi_private *priv,
+ struct nxpwifi_ie_types_header *tlv)
+{
+ struct nxpwifi_tx_pause_tlv *tp;
+ struct nxpwifi_sta_node *sta_ptr;
+
+ tp = (void *)tlv;
+ nxpwifi_dbg(priv->adapter, EVENT,
+ "uap tx_pause: %pM pause=%d, pkts=%d\n",
+ tp->peermac, tp->tx_pause,
+ tp->pkt_cnt);
+
+ if (ether_addr_equal(tp->peermac, priv->netdev->dev_addr)) {
+ if (tp->tx_pause)
+ priv->port_open = false;
+ else
+ priv->port_open = true;
+ } else if (is_multicast_ether_addr(tp->peermac)) {
+ nxpwifi_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause);
+ } else {
+ spin_lock_bh(&priv->sta_list_spinlock);
+ sta_ptr = nxpwifi_get_sta_entry(priv, tp->peermac);
+ if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) {
+ sta_ptr->tx_pause = tp->tx_pause;
+ spin_unlock_bh(&priv->sta_list_spinlock);
+ nxpwifi_update_ralist_tx_pause(priv, tp->peermac,
+ tp->tx_pause);
+ } else {
+ spin_unlock_bh(&priv->sta_list_spinlock);
+ }
+ }
+}
+
+static void nxpwifi_process_sta_tx_pause(struct nxpwifi_private *priv,
+ struct nxpwifi_ie_types_header *tlv)
+{
+ struct nxpwifi_tx_pause_tlv *tp;
+
+ tp = (void *)tlv;
+ nxpwifi_dbg(priv->adapter, EVENT,
+ "sta tx_pause: %pM pause=%d, pkts=%d\n",
+ tp->peermac, tp->tx_pause,
+ tp->pkt_cnt);
+
+ if (ether_addr_equal(tp->peermac, priv->cfg_bssid)) {
+ if (tp->tx_pause)
+ priv->port_open = false;
+ else
+ priv->port_open = true;
+ }
+}
+
+/* This function resets the connection state.
+ *
+ * The function is invoked after receiving a disconnect event from firmware,
+ * and performs the following actions -
+ * - Set media status to disconnected
+ * - Clean up Tx and Rx packets
+ * - Resets SNR/NF/RSSI value in driver
+ * - Resets security configurations in driver
+ * - Enables auto data rate
+ * - Saves the previous SSID and BSSID so that they can
+ * be used for re-association, if required
+ * - Erases current SSID and BSSID information
+ * - Sends a disconnect event to upper layers/applications.
+ */
+void nxpwifi_reset_connect_state(struct nxpwifi_private *priv, u16 reason_code,
+ bool from_ap)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (!priv->media_connected)
+ return;
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: handles disconnect event\n");
+
+ priv->media_connected = false;
+
+ priv->auth_flag = 0;
+ priv->auth_alg = WLAN_AUTH_NONE;
+
+ priv->scan_block = false;
+ priv->port_open = false;
+
+ /* Free Tx and Rx packets, report disconnect to upper layer */
+ nxpwifi_clean_txrx(priv);
+
+ /* 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;
+ priv->sec_info.wpa_enabled = false;
+ priv->sec_info.wpa2_enabled = false;
+ priv->wpa_ie_len = 0;
+
+ priv->sec_info.encryption_mode = 0;
+
+ /* Enable auto data rate */
+ priv->is_data_rate_auto = true;
+ priv->data_rate = 0;
+
+ priv->assoc_resp_ht_param = 0;
+ priv->ht_param_present = false;
+
+ if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA ||
+ GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) && priv->hist_data)
+ nxpwifi_hist_data_reset(priv);
+
+ /* Memorize the previous SSID and BSSID so
+ * it could be used for re-assoc
+ */
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: previous SSID=%s, SSID len=%u\n",
+ priv->prev_ssid.ssid, priv->prev_ssid.ssid_len);
+
+ nxpwifi_dbg(adapter, INFO,
+ "info: current SSID=%s, SSID len=%u\n",
+ priv->curr_bss_params.bss_descriptor.ssid.ssid,
+ priv->curr_bss_params.bss_descriptor.ssid.ssid_len);
+
+ memcpy(&priv->prev_ssid,
+ &priv->curr_bss_params.bss_descriptor.ssid,
+ sizeof(struct cfg80211_ssid));
+
+ memcpy(priv->prev_bssid,
+ priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN);
+
+ /* Need to erase the current SSID and BSSID info */
+ memset(&priv->curr_bss_params, 0x00, sizeof(priv->curr_bss_params));
+
+ adapter->tx_lock_flag = false;
+ adapter->pps_uapsd_mode = false;
+
+ if (test_bit(NXPWIFI_IS_CMD_TIMEDOUT, &adapter->work_flags) &&
+ adapter->curr_cmd)
+ return;
+
+ priv->media_connected = false;
+ nxpwifi_dbg(adapter, MSG,
+ "info: successfully disconnected from %pM: reason code %d\n",
+ priv->cfg_bssid, reason_code);
+
+ if (priv->bss_mode == NL80211_IFTYPE_STATION) {
+ if (adapter->host_mlme_link_lost)
+ nxpwifi_host_mlme_disconnect(adapter->priv_link_lost,
+ reason_code, NULL);
+ else
+ cfg80211_disconnected(priv->netdev, reason_code, NULL,
+ 0, !from_ap, GFP_KERNEL);
+ }
+ eth_zero_addr(priv->cfg_bssid);
+
+ nxpwifi_stop_net_dev_queue(priv->netdev, adapter);
+ if (netif_carrier_ok(priv->netdev))
+ netif_carrier_off(priv->netdev);
+
+ if (!ISSUPP_FIRMWARE_SUPPLICANT(priv->adapter->fw_cap_info))
+ return;
+
+ nxpwifi_send_cmd(priv, HOST_CMD_GTK_REKEY_OFFLOAD_CFG,
+ HOST_ACT_GEN_REMOVE, 0, NULL, false);
+}
+
+void nxpwifi_process_multi_chan_event(struct nxpwifi_private *priv,
+ struct sk_buff *event_skb)
+{
+ struct nxpwifi_ie_types_multi_chan_info *chan_info;
+ struct nxpwifi_ie_types_mc_group_info *grp_info;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_ie_types_header *tlv;
+ u16 tlv_buf_left, tlv_type, tlv_len;
+ int intf_num, bss_type, bss_num, i;
+ struct nxpwifi_private *intf_priv;
+
+ tlv_buf_left = event_skb->len - sizeof(u32);
+ chan_info = (void *)event_skb->data + sizeof(u32);
+
+ if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO ||
+ tlv_buf_left < sizeof(struct nxpwifi_ie_types_multi_chan_info)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "unknown TLV in chan_info event\n");
+ return;
+ }
+
+ adapter->usb_mc_status = le16_to_cpu(chan_info->status);
+ nxpwifi_dbg(adapter, EVENT, "multi chan operation %s\n",
+ adapter->usb_mc_status ? "started" : "over");
+
+ tlv_buf_left -= sizeof(struct nxpwifi_ie_types_multi_chan_info);
+ tlv = (struct nxpwifi_ie_types_header *)chan_info->tlv_buffer;
+
+ while (tlv_buf_left >= (int)sizeof(struct nxpwifi_ie_types_header)) {
+ tlv_type = le16_to_cpu(tlv->type);
+ tlv_len = le16_to_cpu(tlv->len);
+ if ((sizeof(struct nxpwifi_ie_types_header) + tlv_len) >
+ tlv_buf_left) {
+ nxpwifi_dbg(adapter, ERROR, "wrong tlv: tlvLen=%d,\t"
+ "tlvBufLeft=%d\n", tlv_len, tlv_buf_left);
+ break;
+ }
+ if (tlv_type != TLV_TYPE_MC_GROUP_INFO) {
+ nxpwifi_dbg(adapter, ERROR, "wrong tlv type: 0x%x\n",
+ tlv_type);
+ break;
+ }
+
+ grp_info = (struct nxpwifi_ie_types_mc_group_info *)tlv;
+ intf_num = grp_info->intf_num;
+ for (i = 0; i < intf_num; i++) {
+ bss_type = grp_info->bss_type_numlist[i] >> 4;
+ bss_num = grp_info->bss_type_numlist[i] & BSS_NUM_MASK;
+ intf_priv = nxpwifi_get_priv_by_id(adapter, bss_num,
+ bss_type);
+ if (!intf_priv) {
+ nxpwifi_dbg(adapter, ERROR,
+ "Invalid bss_type bss_num\t"
+ "in multi channel event\n");
+ continue;
+ }
+ }
+
+ tlv_buf_left -= sizeof(struct nxpwifi_ie_types_header) +
+ tlv_len;
+ tlv = (void *)((u8 *)tlv + tlv_len +
+ sizeof(struct nxpwifi_ie_types_header));
+ }
+}
+
+void nxpwifi_process_tx_pause_event(struct nxpwifi_private *priv,
+ struct sk_buff *event_skb)
+{
+ struct nxpwifi_ie_types_header *tlv;
+ u16 tlv_type, tlv_len;
+ int tlv_buf_left;
+
+ if (!priv->media_connected) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "tx_pause event while disconnected; bss_role=%d\n",
+ priv->bss_role);
+ return;
+ }
+
+ tlv_buf_left = event_skb->len - sizeof(u32);
+ tlv = (void *)event_skb->data + sizeof(u32);
+
+ while (tlv_buf_left >= (int)sizeof(struct nxpwifi_ie_types_header)) {
+ tlv_type = le16_to_cpu(tlv->type);
+ tlv_len = le16_to_cpu(tlv->len);
+ if ((sizeof(struct nxpwifi_ie_types_header) + tlv_len) >
+ tlv_buf_left) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n",
+ tlv_len, tlv_buf_left);
+ break;
+ }
+ if (tlv_type == TLV_TYPE_TX_PAUSE) {
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA)
+ nxpwifi_process_sta_tx_pause(priv, tlv);
+ else
+ nxpwifi_process_uap_tx_pause(priv, tlv);
+ }
+
+ tlv_buf_left -= sizeof(struct nxpwifi_ie_types_header) +
+ tlv_len;
+ tlv = (void *)((u8 *)tlv + tlv_len +
+ sizeof(struct nxpwifi_ie_types_header));
+ }
+}
+
+/* This function handles coex events generated by firmware */
+void nxpwifi_bt_coex_wlan_param_update_event(struct nxpwifi_private *priv,
+ struct sk_buff *event_skb)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_ie_types_header *tlv;
+ struct nxpwifi_ie_types_btcoex_aggr_win_size *winsizetlv;
+ struct nxpwifi_ie_types_btcoex_scan_time *scantlv;
+ s32 len = event_skb->len - sizeof(u32);
+ u8 *cur_ptr = event_skb->data + sizeof(u32);
+ u16 tlv_type, tlv_len;
+
+ while (len >= sizeof(struct nxpwifi_ie_types_header)) {
+ tlv = (struct nxpwifi_ie_types_header *)cur_ptr;
+ tlv_len = le16_to_cpu(tlv->len);
+ tlv_type = le16_to_cpu(tlv->type);
+
+ if ((tlv_len + sizeof(struct nxpwifi_ie_types_header)) > len)
+ break;
+ switch (tlv_type) {
+ case TLV_BTCOEX_WL_AGGR_WINSIZE:
+ winsizetlv =
+ (struct nxpwifi_ie_types_btcoex_aggr_win_size *)tlv;
+ adapter->coex_win_size = winsizetlv->coex_win_size;
+ adapter->coex_tx_win_size =
+ winsizetlv->tx_win_size;
+ adapter->coex_rx_win_size =
+ winsizetlv->rx_win_size;
+ nxpwifi_coex_ampdu_rxwinsize(adapter);
+ nxpwifi_update_ampdu_txwinsize(adapter);
+ break;
+
+ case TLV_BTCOEX_WL_SCANTIME:
+ scantlv =
+ (struct nxpwifi_ie_types_btcoex_scan_time *)tlv;
+ adapter->coex_scan = scantlv->coex_scan;
+ adapter->coex_min_scan_time = le16_to_cpu(scantlv->min_scan_time);
+ adapter->coex_max_scan_time = le16_to_cpu(scantlv->max_scan_time);
+ break;
+
+ default:
+ break;
+ }
+
+ len -= tlv_len + sizeof(struct nxpwifi_ie_types_header);
+ cur_ptr += tlv_len +
+ sizeof(struct nxpwifi_ie_types_header);
+ }
+
+ dev_dbg(adapter->dev, "coex_scan=%d min_scan=%d coex_win=%d, tx_win=%d rx_win=%d\n",
+ adapter->coex_scan, adapter->coex_min_scan_time,
+ adapter->coex_win_size, adapter->coex_tx_win_size,
+ adapter->coex_rx_win_size);
+}
+
+/* This function handles events generated by firmware.
+ *
+ * This is a generic function and handles all events.
+ *
+ * Event specific routines are called by this function based
+ * upon the generated event cause.
+ */
+int nxpwifi_process_sta_event(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u32 eventcause = adapter->event_cause;
+ int evt, ret = 0;
+
+ for (evt = 0; evt < ARRAY_SIZE(evt_table_sta); evt++) {
+ if (eventcause == evt_table_sta[evt].event_cause) {
+ if (evt_table_sta[evt].event_handler)
+ ret = evt_table_sta[evt].event_handler(priv);
+ break;
+ }
+ }
+
+ if (evt == ARRAY_SIZE(evt_table_sta))
+ nxpwifi_dbg(adapter, EVENT,
+ "%s: unknown event id: %#x\n",
+ __func__, eventcause);
+ else
+ nxpwifi_dbg(adapter, EVENT,
+ "%s: event id: %#x\n",
+ __func__, eventcause);
+
+ return ret;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 30/43] wifi: nxpwifi: add sta_ioctl.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (28 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 29/43] wifi: nxpwifi: add sta_event.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 31/43] wifi: nxpwifi: add sta_rx.c David Lin
` (14 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/sta_ioctl.c | 1320 ++++++++++++++++++
1 file changed, 1320 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/sta_ioctl.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_ioctl.c b/drivers/net/wireless/nxp/nxpwifi/sta_ioctl.c
new file mode 100644
index 000000000000..00f4a2c6ac83
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/sta_ioctl.c
@@ -0,0 +1,1320 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: functions for station ioctl
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cmdevt.h"
+#include "wmm.h"
+#include "11n.h"
+#include "cfg80211.h"
+
+static int disconnect_on_suspend;
+
+/* Copies the multicast address list from device to driver.
+ *
+ * This function does not validate the destination memory for
+ * size, and the calling function must ensure enough memory is
+ * available.
+ */
+int nxpwifi_copy_mcast_addr(struct nxpwifi_multicast_list *mlist,
+ struct net_device *dev)
+{
+ int i = 0;
+ struct netdev_hw_addr *ha;
+
+ netdev_for_each_mc_addr(ha, dev)
+ memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN);
+
+ return i;
+}
+
+/* Wait queue completion handler.
+ *
+ * This function waits on a cmd wait queue. It also cancels the pending
+ * request after waking up, in case of errors.
+ */
+int nxpwifi_wait_queue_complete(struct nxpwifi_adapter *adapter,
+ struct cmd_ctrl_node *cmd_queued)
+{
+ int status;
+
+ /* Wait for completion */
+ status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait,
+ *cmd_queued->condition,
+ (12 * HZ));
+ if (status <= 0) {
+ if (status == 0)
+ status = -ETIMEDOUT;
+ nxpwifi_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n",
+ status);
+ nxpwifi_cancel_all_pending_cmd(adapter);
+ return status;
+ }
+
+ status = adapter->cmd_wait_q.status;
+ adapter->cmd_wait_q.status = 0;
+
+ return status;
+}
+
+/* This function prepares the correct firmware command and
+ * issues it to set the multicast list.
+ *
+ * This function can be used to enable promiscuous mode, or enable all
+ * multicast packets, or to enable selective multicast.
+ */
+int
+nxpwifi_request_set_multicast_list(struct nxpwifi_private *priv,
+ struct nxpwifi_multicast_list *mcast_list)
+{
+ int ret = 0;
+ u16 old_pkt_filter;
+
+ old_pkt_filter = priv->curr_pkt_filter;
+
+ if (mcast_list->mode == NXPWIFI_PROMISC_MODE) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: Enable Promiscuous mode\n");
+ priv->curr_pkt_filter |= HOST_ACT_MAC_PROMISCUOUS_ENABLE;
+ priv->curr_pkt_filter &=
+ ~HOST_ACT_MAC_ALL_MULTICAST_ENABLE;
+ } else {
+ /* Multicast */
+ priv->curr_pkt_filter &= ~HOST_ACT_MAC_PROMISCUOUS_ENABLE;
+ if (mcast_list->mode == NXPWIFI_ALL_MULTI_MODE) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: Enabling All Multicast!\n");
+ priv->curr_pkt_filter |=
+ HOST_ACT_MAC_ALL_MULTICAST_ENABLE;
+ } else {
+ priv->curr_pkt_filter &=
+ ~HOST_ACT_MAC_ALL_MULTICAST_ENABLE;
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: Set multicast list=%d\n",
+ mcast_list->num_multicast_addr);
+ /* Send multicast addresses to firmware */
+ ret = nxpwifi_send_cmd(priv,
+ HOST_CMD_MAC_MULTICAST_ADR,
+ HOST_ACT_GEN_SET, 0,
+ mcast_list, false);
+ }
+ }
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n",
+ old_pkt_filter, priv->curr_pkt_filter);
+ if (old_pkt_filter != priv->curr_pkt_filter) {
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL,
+ HOST_ACT_GEN_SET,
+ 0, &priv->curr_pkt_filter, false);
+ }
+
+ return ret;
+}
+
+/* This function fills bss descriptor structure using provided
+ * information.
+ * beacon_ie buffer is allocated in this function. It is caller's
+ * responsibility to free the memory.
+ */
+int nxpwifi_fill_new_bss_desc(struct nxpwifi_private *priv,
+ struct cfg80211_bss *bss,
+ struct nxpwifi_bssdescriptor *bss_desc)
+{
+ u8 *beacon_ie;
+ size_t beacon_ie_len;
+ struct nxpwifi_bss_priv *bss_priv = (void *)bss->priv;
+ const struct cfg80211_bss_ies *ies;
+
+ rcu_read_lock();
+ ies = rcu_dereference(bss->ies);
+ beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC);
+ beacon_ie_len = ies->len;
+ bss_desc->timestamp = ies->tsf;
+ rcu_read_unlock();
+
+ if (!beacon_ie) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ " failed to alloc beacon_ie\n");
+ return -ENOMEM;
+ }
+
+ memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN);
+ bss_desc->rssi = bss->signal;
+ /* The caller of this function will free beacon_ie */
+ bss_desc->beacon_buf = beacon_ie;
+ bss_desc->beacon_buf_size = beacon_ie_len;
+ bss_desc->beacon_period = bss->beacon_interval;
+ bss_desc->cap_info_bitmap = bss->capability;
+ bss_desc->bss_band = bss_priv->band;
+ bss_desc->fw_tsf = bss_priv->fw_tsf;
+ if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: InterpretIE: AP WEP enabled\n");
+ bss_desc->privacy = NXPWIFI_802_11_PRIV_FILTER_8021X_WEP;
+ } else {
+ bss_desc->privacy = NXPWIFI_802_11_PRIV_FILTER_ACCEPT_ALL;
+ }
+ bss_desc->bss_mode = NL80211_IFTYPE_STATION;
+
+ /* Disable 11ac by default. Enable it only where there
+ * exist VHT_CAP IE in AP beacon
+ */
+ bss_desc->disable_11ac = true;
+
+ if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT)
+ bss_desc->sensed_11h = true;
+
+ return nxpwifi_update_bss_desc_with_ie(priv->adapter, bss_desc);
+}
+
+void nxpwifi_dnld_txpwr_table(struct nxpwifi_private *priv)
+{
+ if (priv->adapter->dt_node) {
+ char txpwr[] = {"nxp,00_txpwrlimit"};
+
+ memcpy(&txpwr[8], priv->adapter->country_code, 2);
+ nxpwifi_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr);
+ }
+}
+
+static int nxpwifi_process_country_ie(struct nxpwifi_private *priv,
+ struct cfg80211_bss *bss)
+{
+ const u8 *country_ie;
+ u8 country_ie_len;
+ struct nxpwifi_802_11d_domain_reg *domain_info =
+ &priv->adapter->domain_reg;
+
+ rcu_read_lock();
+ country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
+ if (!country_ie) {
+ rcu_read_unlock();
+ return 0;
+ }
+
+ country_ie_len = country_ie[1];
+ if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) {
+ rcu_read_unlock();
+ return 0;
+ }
+
+ if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) {
+ rcu_read_unlock();
+ nxpwifi_dbg(priv->adapter, INFO,
+ "11D: skip setting domain info in FW\n");
+ return 0;
+ }
+
+ if (country_ie_len >
+ (IEEE80211_COUNTRY_STRING_LEN + NXPWIFI_MAX_TRIPLET_802_11D)) {
+ rcu_read_unlock();
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "11D: country_ie_len overflow!, deauth AP\n");
+ return -EINVAL;
+ }
+
+ memcpy(priv->adapter->country_code, &country_ie[2], 2);
+
+ domain_info->country_code[0] = country_ie[2];
+ domain_info->country_code[1] = country_ie[3];
+ domain_info->country_code[2] = ' ';
+
+ country_ie_len -= IEEE80211_COUNTRY_STRING_LEN;
+
+ domain_info->no_of_triplet =
+ country_ie_len / sizeof(struct ieee80211_country_ie_triplet);
+
+ memcpy((u8 *)domain_info->triplet,
+ &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len);
+
+ rcu_read_unlock();
+
+ if (nxpwifi_send_cmd(priv, HOST_CMD_802_11D_DOMAIN_INFO,
+ HOST_ACT_GEN_SET, 0, NULL, false)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "11D: setting domain info in FW fail\n");
+ return -1;
+ }
+
+ nxpwifi_dnld_txpwr_table(priv);
+
+ return 0;
+}
+
+/* In infra mode, an deauthentication is performed
+ * first.
+ */
+int nxpwifi_bss_start(struct nxpwifi_private *priv, struct cfg80211_bss *bss,
+ struct cfg80211_ssid *req_ssid)
+{
+ int ret;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_bssdescriptor *bss_desc = NULL;
+ u8 config_bands;
+
+ priv->scan_block = false;
+
+ if (adapter->region_code == 0x00 &&
+ nxpwifi_process_country_ie(priv, bss))
+ return -EINVAL;
+
+ /* Allocate and fill new bss descriptor */
+ bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL);
+ if (!bss_desc)
+ return -ENOMEM;
+
+ ret = nxpwifi_fill_new_bss_desc(priv, bss, bss_desc);
+ if (ret)
+ goto done;
+
+ if (nxpwifi_band_to_radio_type(bss_desc->bss_band) ==
+ HOST_SCAN_RADIO_TYPE_BG) {
+ config_bands = BAND_B | BAND_G | BAND_GN;
+ } else {
+ config_bands = BAND_A | BAND_AN;
+ if (adapter->fw_bands & BAND_AAC)
+ config_bands |= BAND_AAC;
+ }
+
+ if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands))
+ adapter->config_bands = config_bands;
+
+ ret = nxpwifi_check_network_compatibility(priv, bss_desc);
+ if (ret)
+ goto done;
+
+ if (nxpwifi_11h_get_csa_closed_channel(priv) == (u8)bss_desc->channel) {
+ nxpwifi_dbg(adapter, ERROR,
+ "Attempt to reconnect on csa closed chan(%d)\n",
+ bss_desc->channel);
+ ret = -1;
+ goto done;
+ }
+
+ nxpwifi_stop_net_dev_queue(priv->netdev, adapter);
+ if (netif_carrier_ok(priv->netdev))
+ netif_carrier_off(priv->netdev);
+
+ /* Clear any past association response stored for
+ * application retrieval
+ */
+ priv->assoc_rsp_size = 0;
+ ret = nxpwifi_associate(priv, bss_desc);
+
+ /* If auth type is auto and association fails using open mode,
+ * try to connect using shared mode
+ */
+ if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
+ priv->sec_info.is_authtype_auto &&
+ priv->sec_info.wep_enabled) {
+ priv->sec_info.authentication_mode =
+ NL80211_AUTHTYPE_SHARED_KEY;
+ ret = nxpwifi_associate(priv, bss_desc);
+ }
+
+done:
+ /* beacon_ie buffer was allocated in function
+ * nxpwifi_fill_new_bss_desc(). Free it now.
+ */
+ if (bss_desc)
+ kfree(bss_desc->beacon_buf);
+ kfree(bss_desc);
+
+ if (ret < 0)
+ priv->attempted_bss_desc = NULL;
+
+ return ret;
+}
+
+/* IOCTL request handler to set host sleep configuration.
+ *
+ * This function prepares the correct firmware command and
+ * issues it.
+ */
+int nxpwifi_set_hs_params(struct nxpwifi_private *priv, u16 action,
+ int cmd_type, struct nxpwifi_ds_hs_cfg *hs_cfg)
+
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int status = 0;
+ u32 prev_cond = 0;
+
+ if (!hs_cfg)
+ return -ENOMEM;
+
+ switch (action) {
+ case HOST_ACT_GEN_SET:
+ if (adapter->pps_uapsd_mode) {
+ nxpwifi_dbg(adapter, INFO,
+ "info: Host Sleep IOCTL\t"
+ "is blocked in UAPSD/PPS mode\n");
+ status = -1;
+ break;
+ }
+ if (hs_cfg->is_invoke_hostcmd) {
+ if (hs_cfg->conditions == HS_CFG_CANCEL) {
+ if (!test_bit(NXPWIFI_IS_HS_CONFIGURED,
+ &adapter->work_flags))
+ /* Already cancelled */
+ break;
+ /* Save previous condition */
+ prev_cond = le32_to_cpu(adapter->hs_cfg
+ .conditions);
+ adapter->hs_cfg.conditions =
+ cpu_to_le32(hs_cfg->conditions);
+ } else if (hs_cfg->conditions) {
+ adapter->hs_cfg.conditions =
+ cpu_to_le32(hs_cfg->conditions);
+ adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
+ if (hs_cfg->gap)
+ adapter->hs_cfg.gap = (u8)hs_cfg->gap;
+ } else if (adapter->hs_cfg.conditions ==
+ cpu_to_le32(HS_CFG_CANCEL)) {
+ /* Return failure if no parameters for HS
+ * enable
+ */
+ status = -1;
+ break;
+ }
+
+ status = nxpwifi_send_cmd(priv,
+ HOST_CMD_802_11_HS_CFG_ENH,
+ HOST_ACT_GEN_SET, 0,
+ &adapter->hs_cfg,
+ cmd_type == NXPWIFI_SYNC_CMD);
+
+ if (hs_cfg->conditions == HS_CFG_CANCEL)
+ /* Restore previous condition */
+ adapter->hs_cfg.conditions =
+ cpu_to_le32(prev_cond);
+ } else {
+ adapter->hs_cfg.conditions =
+ cpu_to_le32(hs_cfg->conditions);
+ adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
+ adapter->hs_cfg.gap = (u8)hs_cfg->gap;
+ }
+ break;
+ case HOST_ACT_GEN_GET:
+ hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions);
+ hs_cfg->gpio = adapter->hs_cfg.gpio;
+ hs_cfg->gap = adapter->hs_cfg.gap;
+ break;
+ default:
+ status = -1;
+ break;
+ }
+
+ return status;
+}
+
+/* Sends IOCTL request to cancel the existing Host Sleep configuration.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int nxpwifi_cancel_hs(struct nxpwifi_private *priv, int cmd_type)
+{
+ struct nxpwifi_ds_hs_cfg hscfg;
+
+ hscfg.conditions = HS_CFG_CANCEL;
+ hscfg.is_invoke_hostcmd = true;
+
+ return nxpwifi_set_hs_params(priv, HOST_ACT_GEN_SET,
+ cmd_type, &hscfg);
+}
+EXPORT_SYMBOL_GPL(nxpwifi_cancel_hs);
+
+/* Sends IOCTL request to cancel the existing Host Sleep configuration.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int nxpwifi_enable_hs(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_ds_hs_cfg hscfg;
+ struct nxpwifi_private *priv;
+ int i;
+
+ if (disconnect_on_suspend) {
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (priv)
+ nxpwifi_deauthenticate(priv, NULL);
+ }
+ }
+
+ priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_STA);
+
+ if (priv && priv->sched_scanning) {
+#ifdef CONFIG_PM
+ if (priv->wdev.wiphy->wowlan_config &&
+ !priv->wdev.wiphy->wowlan_config->nd_config) {
+#endif
+ nxpwifi_dbg(adapter, CMD, "aborting bgscan!\n");
+ nxpwifi_stop_bg_scan(priv);
+ cfg80211_sched_scan_stopped(priv->wdev.wiphy, 0);
+#ifdef CONFIG_PM
+ }
+#endif
+ }
+
+ if (adapter->hs_activated) {
+ nxpwifi_dbg(adapter, CMD,
+ "cmd: HS Already activated\n");
+ return true;
+ }
+
+ adapter->hs_activate_wait_q_woken = false;
+
+ memset(&hscfg, 0, sizeof(hscfg));
+ hscfg.is_invoke_hostcmd = true;
+
+ set_bit(NXPWIFI_IS_HS_ENABLING, &adapter->work_flags);
+ nxpwifi_cancel_all_pending_cmd(adapter);
+
+ if (nxpwifi_set_hs_params(nxpwifi_get_priv(adapter,
+ NXPWIFI_BSS_ROLE_STA),
+ HOST_ACT_GEN_SET, NXPWIFI_SYNC_CMD,
+ &hscfg)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "IOCTL request HS enable failed\n");
+ return false;
+ }
+
+ if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q,
+ adapter->hs_activate_wait_q_woken,
+ (10 * HZ)) <= 0) {
+ nxpwifi_dbg(adapter, ERROR,
+ "hs_activate_wait_q terminated\n");
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(nxpwifi_enable_hs);
+
+/* IOCTL request handler to get BSS information.
+ *
+ * This function collates the information from different driver structures
+ * to send to the user.
+ */
+int nxpwifi_get_bss_info(struct nxpwifi_private *priv,
+ struct nxpwifi_bss_info *info)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_bssdescriptor *bss_desc;
+
+ if (!info)
+ return -1;
+
+ bss_desc = &priv->curr_bss_params.bss_descriptor;
+
+ info->bss_mode = priv->bss_mode;
+
+ memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid));
+
+ memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN);
+
+ info->bss_chan = bss_desc->channel;
+
+ memcpy(info->country_code, adapter->country_code,
+ IEEE80211_COUNTRY_STRING_LEN);
+
+ info->media_connected = priv->media_connected;
+
+ info->max_power_level = priv->max_tx_power_level;
+ info->min_power_level = priv->min_tx_power_level;
+
+ info->bcn_nf_last = priv->bcn_nf_last;
+
+ if (priv->sec_info.wep_enabled)
+ info->wep_status = true;
+ else
+ info->wep_status = false;
+
+ info->is_hs_configured = test_bit(NXPWIFI_IS_HS_CONFIGURED,
+ &adapter->work_flags);
+ info->is_deep_sleep = adapter->is_deep_sleep;
+
+ return 0;
+}
+
+/* The function disables auto deep sleep mode.
+ */
+int nxpwifi_disable_auto_ds(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_ds_auto_ds auto_ds = {
+ .auto_ds = DEEP_SLEEP_OFF,
+ };
+
+ return nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH,
+ DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true);
+}
+EXPORT_SYMBOL_GPL(nxpwifi_disable_auto_ds);
+
+/* Sends IOCTL request to get the data rate.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int nxpwifi_drv_get_data_rate(struct nxpwifi_private *priv, u32 *rate)
+{
+ int ret;
+
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_TX_RATE_QUERY,
+ HOST_ACT_GEN_GET, 0, NULL, true);
+
+ if (!ret) {
+ if (priv->is_data_rate_auto)
+ *rate = nxpwifi_index_to_data_rate(priv, priv->tx_rate,
+ priv->tx_htinfo);
+ else
+ *rate = priv->data_rate;
+ }
+
+ return ret;
+}
+
+/* IOCTL request handler to set tx power configuration.
+ *
+ * This function prepares the correct firmware command and
+ * issues it.
+ *
+ * For non-auto power mode, all the following power groups are set -
+ * - Modulation class HR/DSSS
+ * - Modulation class OFDM
+ * - Modulation class HTBW20
+ * - Modulation class HTBW40
+ */
+int nxpwifi_set_tx_power(struct nxpwifi_private *priv,
+ struct nxpwifi_power_cfg *power_cfg)
+{
+ int ret;
+ struct host_cmd_ds_txpwr_cfg *txp_cfg;
+ struct nxpwifi_types_power_group *pg_tlv;
+ struct nxpwifi_power_group *pg;
+ u8 *buf;
+ u16 dbm = 0;
+
+ if (!power_cfg->is_power_auto) {
+ dbm = (u16)power_cfg->power_level;
+ if (dbm < priv->min_tx_power_level ||
+ dbm > priv->max_tx_power_level) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "txpower value %d dBm\t"
+ "is out of range (%d dBm-%d dBm)\n",
+ dbm, priv->min_tx_power_level,
+ priv->max_tx_power_level);
+ return -1;
+ }
+ }
+ buf = kzalloc(NXPWIFI_SIZE_OF_CMD_BUFFER, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ txp_cfg = (struct host_cmd_ds_txpwr_cfg *)buf;
+ txp_cfg->action = cpu_to_le16(HOST_ACT_GEN_SET);
+ if (!power_cfg->is_power_auto) {
+ u16 dbm_min = power_cfg->is_power_fixed ?
+ dbm : priv->min_tx_power_level;
+
+ txp_cfg->mode = cpu_to_le32(1);
+ pg_tlv = (struct nxpwifi_types_power_group *)
+ (buf + sizeof(struct host_cmd_ds_txpwr_cfg));
+ pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP);
+ pg_tlv->length =
+ cpu_to_le16(4 * sizeof(struct nxpwifi_power_group));
+ pg = (struct nxpwifi_power_group *)
+ (buf + sizeof(struct host_cmd_ds_txpwr_cfg)
+ + sizeof(struct nxpwifi_types_power_group));
+ /* Power group for modulation class HR/DSSS */
+ pg->first_rate_code = 0x00;
+ pg->last_rate_code = 0x03;
+ pg->modulation_class = MOD_CLASS_HR_DSSS;
+ pg->power_step = 0;
+ pg->power_min = (s8)dbm_min;
+ pg->power_max = (s8)dbm;
+ pg++;
+ /* Power group for modulation class OFDM */
+ pg->first_rate_code = 0x00;
+ pg->last_rate_code = 0x07;
+ pg->modulation_class = MOD_CLASS_OFDM;
+ pg->power_step = 0;
+ pg->power_min = (s8)dbm_min;
+ pg->power_max = (s8)dbm;
+ pg++;
+ /* Power group for modulation class HTBW20 */
+ pg->first_rate_code = 0x00;
+ pg->last_rate_code = 0x20;
+ pg->modulation_class = MOD_CLASS_HT;
+ pg->power_step = 0;
+ pg->power_min = (s8)dbm_min;
+ pg->power_max = (s8)dbm;
+ pg->ht_bandwidth = HT_BW_20;
+ pg++;
+ /* Power group for modulation class HTBW40 */
+ pg->first_rate_code = 0x00;
+ pg->last_rate_code = 0x20;
+ pg->modulation_class = MOD_CLASS_HT;
+ pg->power_step = 0;
+ pg->power_min = (s8)dbm_min;
+ pg->power_max = (s8)dbm;
+ pg->ht_bandwidth = HT_BW_40;
+ }
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_TXPWR_CFG,
+ HOST_ACT_GEN_SET, 0, buf, true);
+
+ kfree(buf);
+ return ret;
+}
+
+/* IOCTL request handler to get power save mode.
+ *
+ * This function prepares the correct firmware command and
+ * issues it.
+ */
+int nxpwifi_drv_set_power(struct nxpwifi_private *priv, u32 *ps_mode)
+{
+ int ret;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u16 sub_cmd;
+
+ if (*ps_mode)
+ adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_PSP;
+ else
+ adapter->ps_mode = NXPWIFI_802_11_POWER_MODE_CAM;
+ sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS;
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH,
+ sub_cmd, BITMAP_STA_PS, NULL, true);
+ if (!ret && sub_cmd == DIS_AUTO_PS)
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_PS_MODE_ENH,
+ GET_PS, 0, NULL, false);
+
+ return ret;
+}
+
+/* IOCTL request handler to set/reset WPA IE.
+ *
+ * The supplied WPA IE is treated as a opaque buffer. Only the first field
+ * is checked to determine WPA version. If buffer length is zero, the existing
+ * WPA IE is reset.
+ */
+static int nxpwifi_set_wpa_ie(struct nxpwifi_private *priv,
+ u8 *ie_data_ptr, u16 ie_len)
+{
+ if (ie_len) {
+ if (ie_len > sizeof(priv->wpa_ie)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "failed to copy WPA IE, too big\n");
+ return -1;
+ }
+ memcpy(priv->wpa_ie, ie_data_ptr, ie_len);
+ priv->wpa_ie_len = ie_len;
+ nxpwifi_dbg(priv->adapter, CMD,
+ "cmd: Set Wpa_ie_len=%d IE=%#x\n",
+ priv->wpa_ie_len, priv->wpa_ie[0]);
+
+ if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) {
+ priv->sec_info.wpa_enabled = true;
+ } else if (priv->wpa_ie[0] == WLAN_EID_RSN) {
+ priv->sec_info.wpa2_enabled = true;
+ } else {
+ priv->sec_info.wpa_enabled = false;
+ priv->sec_info.wpa2_enabled = false;
+ }
+ } else {
+ memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie));
+ priv->wpa_ie_len = 0;
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: reset wpa_ie_len=%d IE=%#x\n",
+ priv->wpa_ie_len, priv->wpa_ie[0]);
+ priv->sec_info.wpa_enabled = false;
+ priv->sec_info.wpa2_enabled = false;
+ }
+
+ return 0;
+}
+
+/* IOCTL request handler to set/reset WPS IE.
+ *
+ * The supplied WPS IE is treated as a opaque buffer. Only the first field
+ * is checked to internally enable WPS. If buffer length is zero, the existing
+ * WPS IE is reset.
+ */
+static int nxpwifi_set_wps_ie(struct nxpwifi_private *priv,
+ u8 *ie_data_ptr, u16 ie_len)
+{
+ if (ie_len) {
+ if (ie_len > NXPWIFI_MAX_VSIE_LEN) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "info: failed to copy WPS IE, too big\n");
+ return -1;
+ }
+
+ priv->wps_ie = kzalloc(NXPWIFI_MAX_VSIE_LEN, GFP_KERNEL);
+ if (!priv->wps_ie)
+ return -ENOMEM;
+
+ memcpy(priv->wps_ie, ie_data_ptr, ie_len);
+ priv->wps_ie_len = ie_len;
+ nxpwifi_dbg(priv->adapter, CMD,
+ "cmd: Set wps_ie_len=%d IE=%#x\n",
+ priv->wps_ie_len, priv->wps_ie[0]);
+ } else {
+ kfree(priv->wps_ie);
+ priv->wps_ie_len = ie_len;
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: Reset wps_ie_len=%d\n", priv->wps_ie_len);
+ }
+ return 0;
+}
+
+/* IOCTL request handler to set WEP network key.
+ *
+ * This function prepares the correct firmware command and
+ * issues it, after validation checks.
+ */
+static int
+nxpwifi_sec_ioctl_set_wep_key(struct nxpwifi_private *priv,
+ struct nxpwifi_ds_encrypt_key *encrypt_key)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int ret;
+ struct nxpwifi_wep_key *wep_key;
+ int index;
+
+ if (priv->wep_key_curr_index >= NUM_WEP_KEYS)
+ priv->wep_key_curr_index = 0;
+ wep_key = &priv->wep_key[priv->wep_key_curr_index];
+ index = encrypt_key->key_index;
+ if (encrypt_key->key_disable) {
+ priv->sec_info.wep_enabled = 0;
+ } else if (!encrypt_key->key_len) {
+ /* Copy the required key as the current key */
+ wep_key = &priv->wep_key[index];
+ if (!wep_key->key_length) {
+ nxpwifi_dbg(adapter, ERROR,
+ "key not set, so cannot enable it\n");
+ return -1;
+ }
+
+ memcpy(encrypt_key->key_material,
+ wep_key->key_material, wep_key->key_length);
+ encrypt_key->key_len = wep_key->key_length;
+
+ priv->wep_key_curr_index = (u16)index;
+ priv->sec_info.wep_enabled = 1;
+ } else {
+ wep_key = &priv->wep_key[index];
+ memset(wep_key, 0, sizeof(struct nxpwifi_wep_key));
+ /* Copy the key in the driver */
+ memcpy(wep_key->key_material,
+ encrypt_key->key_material,
+ encrypt_key->key_len);
+ wep_key->key_index = index;
+ wep_key->key_length = encrypt_key->key_len;
+ priv->sec_info.wep_enabled = 1;
+ }
+ if (wep_key->key_length) {
+ void *enc_key;
+
+ if (encrypt_key->key_disable) {
+ memset(&priv->wep_key[index], 0,
+ sizeof(struct nxpwifi_wep_key));
+ goto done;
+ }
+
+ enc_key = encrypt_key;
+
+ /* Send request to firmware */
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_KEY_MATERIAL,
+ HOST_ACT_GEN_SET, 0, enc_key, false);
+ if (ret)
+ return ret;
+ }
+
+done:
+ if (priv->sec_info.wep_enabled)
+ priv->curr_pkt_filter |= HOST_ACT_MAC_WEP_ENABLE;
+ else
+ priv->curr_pkt_filter &= ~HOST_ACT_MAC_WEP_ENABLE;
+
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL,
+ HOST_ACT_GEN_SET, 0,
+ &priv->curr_pkt_filter, true);
+
+ return ret;
+}
+
+/* IOCTL request handler to set WPA key.
+ *
+ * This function prepares the correct firmware command and
+ * issues it, after validation checks.
+ *
+ * Current driver only supports key length of up to 32 bytes.
+ *
+ * This function can also be used to disable a currently set key.
+ */
+static int
+nxpwifi_sec_ioctl_set_wpa_key(struct nxpwifi_private *priv,
+ struct nxpwifi_ds_encrypt_key *encrypt_key)
+{
+ int ret;
+ u8 remove_key = false;
+
+ /* Current driver only supports key length of up to 32 bytes */
+ if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "key length too long\n");
+ return -1;
+ }
+
+ if (!encrypt_key->key_index)
+ encrypt_key->key_index = NXPWIFI_KEY_INDEX_UNICAST;
+
+ if (remove_key)
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_KEY_MATERIAL,
+ HOST_ACT_GEN_SET,
+ !KEY_INFO_ENABLED, encrypt_key, true);
+ else
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_KEY_MATERIAL,
+ HOST_ACT_GEN_SET,
+ KEY_INFO_ENABLED, encrypt_key, true);
+
+ return ret;
+}
+
+/* IOCTL request handler to set/get network keys.
+ *
+ * This is a generic key handling function which supports WEP and WPA.
+ */
+static int
+nxpwifi_sec_ioctl_encrypt_key(struct nxpwifi_private *priv,
+ struct nxpwifi_ds_encrypt_key *encrypt_key)
+{
+ int status;
+
+ if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104)
+ status = nxpwifi_sec_ioctl_set_wpa_key(priv, encrypt_key);
+ else
+ status = nxpwifi_sec_ioctl_set_wep_key(priv, encrypt_key);
+
+ return status;
+}
+
+/* This function returns the driver version.
+ */
+int
+nxpwifi_drv_get_driver_version(struct nxpwifi_adapter *adapter, char *version,
+ int max_len)
+{
+ union {
+ __le32 l;
+ u8 c[4];
+ } ver;
+ char fw_ver[32];
+
+ ver.l = cpu_to_le32(adapter->fw_release_number);
+ sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]);
+
+ snprintf(version, max_len, driver_version, fw_ver);
+
+ nxpwifi_dbg(adapter, MSG, "info: NXPWIFI VERSION: %s\n", version);
+
+ return 0;
+}
+
+/* Sends IOCTL request to set encoding parameters.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int nxpwifi_set_encode(struct nxpwifi_private *priv, struct key_params *kp,
+ const u8 *key, int key_len, u8 key_index,
+ const u8 *mac_addr, int disable)
+{
+ struct nxpwifi_ds_encrypt_key encrypt_key;
+
+ memset(&encrypt_key, 0, sizeof(encrypt_key));
+ encrypt_key.key_len = key_len;
+ encrypt_key.key_index = key_index;
+
+ if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+ encrypt_key.is_igtk_key = true;
+
+ if (!disable) {
+ if (key_len)
+ memcpy(encrypt_key.key_material, key, key_len);
+ else
+ encrypt_key.is_current_wep_key = true;
+
+ if (mac_addr)
+ memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
+ if (kp && kp->seq && kp->seq_len) {
+ memcpy(encrypt_key.pn, kp->seq, kp->seq_len);
+ encrypt_key.pn_len = kp->seq_len;
+ encrypt_key.is_rx_seq_valid = true;
+ }
+ } else {
+ encrypt_key.key_disable = true;
+ if (mac_addr)
+ memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN);
+ }
+
+ return nxpwifi_sec_ioctl_encrypt_key(priv, &encrypt_key);
+}
+
+/* Sends IOCTL request to get extended version.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int
+nxpwifi_get_ver_ext(struct nxpwifi_private *priv, u32 version_str_sel)
+{
+ struct nxpwifi_ver_ext ver_ext;
+
+ memset(&ver_ext, 0, sizeof(ver_ext));
+ ver_ext.version_str_sel = version_str_sel;
+ if (nxpwifi_send_cmd(priv, HOST_CMD_VERSION_EXT,
+ HOST_ACT_GEN_GET, 0, &ver_ext, true))
+ return -1;
+
+ return 0;
+}
+
+int
+nxpwifi_remain_on_chan_cfg(struct nxpwifi_private *priv, u16 action,
+ struct ieee80211_channel *chan,
+ unsigned int duration)
+{
+ struct host_cmd_ds_remain_on_chan roc_cfg;
+ u8 sc;
+
+ memset(&roc_cfg, 0, sizeof(roc_cfg));
+ roc_cfg.action = cpu_to_le16(action);
+ if (action == HOST_ACT_GEN_SET) {
+ roc_cfg.band_cfg = chan->band;
+ sc = nxpwifi_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT);
+ roc_cfg.band_cfg |= (sc << 2);
+
+ roc_cfg.channel =
+ ieee80211_frequency_to_channel(chan->center_freq);
+ roc_cfg.duration = cpu_to_le32(duration);
+ }
+ if (nxpwifi_send_cmd(priv, HOST_CMD_REMAIN_ON_CHAN,
+ action, 0, &roc_cfg, true)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "failed to remain on channel\n");
+ return -1;
+ }
+
+ return roc_cfg.status;
+}
+
+/* Sends IOCTL request to get statistics information.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int
+nxpwifi_get_stats_info(struct nxpwifi_private *priv,
+ struct nxpwifi_ds_get_stats *log)
+{
+ return nxpwifi_send_cmd(priv, HOST_CMD_802_11_GET_LOG,
+ HOST_ACT_GEN_GET, 0, log, true);
+}
+
+/* IOCTL request handler to read/write register.
+ *
+ * This function prepares the correct firmware command and
+ * issues it.
+ *
+ * Access to the following registers are supported -
+ * - MAC
+ * - BBP
+ * - RF
+ * - PMIC
+ * - CAU
+ */
+static int nxpwifi_reg_mem_ioctl_reg_rw(struct nxpwifi_private *priv,
+ struct nxpwifi_ds_reg_rw *reg_rw,
+ u16 action)
+{
+ u16 cmd_no;
+
+ switch (reg_rw->type) {
+ case NXPWIFI_REG_MAC:
+ cmd_no = HOST_CMD_MAC_REG_ACCESS;
+ break;
+ case NXPWIFI_REG_BBP:
+ cmd_no = HOST_CMD_BBP_REG_ACCESS;
+ break;
+ case NXPWIFI_REG_RF:
+ cmd_no = HOST_CMD_RF_REG_ACCESS;
+ break;
+ case NXPWIFI_REG_PMIC:
+ cmd_no = HOST_CMD_PMIC_REG_ACCESS;
+ break;
+ case NXPWIFI_REG_CAU:
+ cmd_no = HOST_CMD_CAU_REG_ACCESS;
+ break;
+ default:
+ return -1;
+ }
+
+ return nxpwifi_send_cmd(priv, cmd_no, action, 0, reg_rw, true);
+}
+
+/* Sends IOCTL request to write to a register.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int
+nxpwifi_reg_write(struct nxpwifi_private *priv, u32 reg_type,
+ u32 reg_offset, u32 reg_value)
+{
+ struct nxpwifi_ds_reg_rw reg_rw;
+
+ reg_rw.type = reg_type;
+ reg_rw.offset = reg_offset;
+ reg_rw.value = reg_value;
+
+ return nxpwifi_reg_mem_ioctl_reg_rw(priv, ®_rw, HOST_ACT_GEN_SET);
+}
+
+/* Sends IOCTL request to read from a register.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int
+nxpwifi_reg_read(struct nxpwifi_private *priv, u32 reg_type,
+ u32 reg_offset, u32 *value)
+{
+ int ret;
+ struct nxpwifi_ds_reg_rw reg_rw;
+
+ reg_rw.type = reg_type;
+ reg_rw.offset = reg_offset;
+ ret = nxpwifi_reg_mem_ioctl_reg_rw(priv, ®_rw, HOST_ACT_GEN_GET);
+
+ if (ret)
+ goto done;
+
+ *value = reg_rw.value;
+
+done:
+ return ret;
+}
+
+/* Sends IOCTL request to read from EEPROM.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int
+nxpwifi_eeprom_read(struct nxpwifi_private *priv, u16 offset, u16 bytes,
+ u8 *value)
+{
+ int ret;
+ struct nxpwifi_ds_read_eeprom rd_eeprom;
+
+ rd_eeprom.offset = offset;
+ rd_eeprom.byte_count = bytes;
+
+ /* Send request to firmware */
+ ret = nxpwifi_send_cmd(priv, HOST_CMD_802_11_EEPROM_ACCESS,
+ HOST_ACT_GEN_GET, 0, &rd_eeprom, true);
+
+ if (!ret)
+ memcpy(value, rd_eeprom.value,
+ min((u16)MAX_EEPROM_DATA, rd_eeprom.byte_count));
+ return ret;
+}
+
+/* This function sets a generic IE. In addition to generic IE, it can
+ * also handle WPA and WPA2 IEs.
+ */
+static int
+nxpwifi_set_gen_ie_helper(struct nxpwifi_private *priv, u8 *ie_data_ptr,
+ u16 ie_len)
+{
+ struct ieee_types_vendor_header *pvendor_ie;
+ static const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 };
+ static const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 };
+ u16 unparsed_len = ie_len, cur_ie_len;
+
+ /* If the passed length is zero, reset the buffer */
+ if (!ie_len) {
+ priv->gen_ie_buf_len = 0;
+ priv->wps.session_enable = false;
+ return 0;
+ } else if (!ie_data_ptr ||
+ ie_len <= sizeof(struct ieee_types_header)) {
+ return -1;
+ }
+ pvendor_ie = (struct ieee_types_vendor_header *)ie_data_ptr;
+
+ while (pvendor_ie) {
+ cur_ie_len = pvendor_ie->len + sizeof(struct ieee_types_header);
+
+ if (pvendor_ie->element_id == WLAN_EID_RSN) {
+ /* IE is a WPA/WPA2 IE so call set_wpa function */
+ nxpwifi_set_wpa_ie(priv, (u8 *)pvendor_ie, cur_ie_len);
+ priv->wps.session_enable = false;
+ goto next_ie;
+ }
+
+ if (pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) {
+ /* Test to see if it is a WPA IE, if not, then
+ * it is a gen IE
+ */
+ if (!memcmp(&pvendor_ie->oui, wpa_oui,
+ sizeof(wpa_oui))) {
+ /* IE is a WPA/WPA2 IE so call set_wpa function
+ */
+ nxpwifi_set_wpa_ie(priv, (u8 *)pvendor_ie,
+ cur_ie_len);
+ priv->wps.session_enable = false;
+ goto next_ie;
+ }
+
+ if (!memcmp(&pvendor_ie->oui, wps_oui,
+ sizeof(wps_oui))) {
+ /* Test to see if it is a WPS IE,
+ * if so, enable wps session flag
+ */
+ priv->wps.session_enable = true;
+ nxpwifi_dbg(priv->adapter, MSG,
+ "WPS Session Enabled.\n");
+ nxpwifi_set_wps_ie(priv, (u8 *)pvendor_ie,
+ cur_ie_len);
+ goto next_ie;
+ }
+ }
+
+ /* Verify that the passed length is not larger than the
+ * available space remaining in the buffer
+ */
+ if (cur_ie_len <
+ (sizeof(priv->gen_ie_buf) - priv->gen_ie_buf_len)) {
+ /* Append the passed data to the end
+ * of the genIeBuffer
+ */
+ memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len,
+ (u8 *)pvendor_ie, cur_ie_len);
+ /* Increment the stored buffer length by the
+ * size passed
+ */
+ priv->gen_ie_buf_len += cur_ie_len;
+ }
+
+next_ie:
+ unparsed_len -= cur_ie_len;
+
+ if (unparsed_len <= sizeof(struct ieee_types_header))
+ pvendor_ie = NULL;
+ else
+ pvendor_ie = (struct ieee_types_vendor_header *)
+ (((u8 *)pvendor_ie) + cur_ie_len);
+ }
+
+ return 0;
+}
+
+/* IOCTL request handler to set/get generic IE.
+ *
+ * In addition to various generic IEs, this function can also be
+ * used to set the ARP filter.
+ */
+static int nxpwifi_misc_ioctl_gen_ie(struct nxpwifi_private *priv,
+ struct nxpwifi_ds_misc_gen_ie *gen_ie,
+ u16 action)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ switch (gen_ie->type) {
+ case NXPWIFI_IE_TYPE_GEN_IE:
+ if (action == HOST_ACT_GEN_GET) {
+ gen_ie->len = priv->wpa_ie_len;
+ memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len);
+ } else {
+ nxpwifi_set_gen_ie_helper(priv, gen_ie->ie_data,
+ (u16)gen_ie->len);
+ }
+ break;
+ case NXPWIFI_IE_TYPE_ARP_FILTER:
+ memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter));
+ if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) {
+ adapter->arp_filter_size = 0;
+ nxpwifi_dbg(adapter, ERROR,
+ "invalid ARP filter size\n");
+ return -1;
+ }
+ memcpy(adapter->arp_filter, gen_ie->ie_data, gen_ie->len);
+ adapter->arp_filter_size = gen_ie->len;
+ break;
+ default:
+ nxpwifi_dbg(adapter, ERROR, "invalid IE type\n");
+ return -1;
+ }
+ return 0;
+}
+
+/* Sends IOCTL request to set a generic IE.
+ *
+ * This function allocates the IOCTL request buffer, fills it
+ * with requisite parameters and calls the IOCTL handler.
+ */
+int
+nxpwifi_set_gen_ie(struct nxpwifi_private *priv, const u8 *ie, int ie_len)
+{
+ struct nxpwifi_ds_misc_gen_ie gen_ie;
+
+ if (ie_len > IEEE_MAX_IE_SIZE)
+ return -EFAULT;
+
+ gen_ie.type = NXPWIFI_IE_TYPE_GEN_IE;
+ gen_ie.len = ie_len;
+ memcpy(gen_ie.ie_data, ie, ie_len);
+ if (nxpwifi_misc_ioctl_gen_ie(priv, &gen_ie, HOST_ACT_GEN_SET))
+ return -EFAULT;
+
+ return 0;
+}
+
+/* This function get Host Sleep wake up reason.
+ */
+int nxpwifi_get_wakeup_reason(struct nxpwifi_private *priv, u16 action,
+ int cmd_type,
+ struct nxpwifi_ds_wakeup_reason *wakeup_reason)
+{
+ int status = 0;
+
+ status = nxpwifi_send_cmd(priv, HOST_CMD_HS_WAKEUP_REASON,
+ HOST_ACT_GEN_GET, 0, wakeup_reason,
+ cmd_type == NXPWIFI_SYNC_CMD);
+
+ return status;
+}
+
+int nxpwifi_get_chan_info(struct nxpwifi_private *priv,
+ struct nxpwifi_channel_band *channel_band)
+{
+ int status = 0;
+
+ status = nxpwifi_send_cmd(priv, HOST_CMD_STA_CONFIGURE,
+ HOST_ACT_GEN_GET, 0, channel_band,
+ NXPWIFI_SYNC_CMD);
+
+ return status;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 31/43] wifi: nxpwifi: add sta_rx.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (29 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 30/43] wifi: nxpwifi: add sta_ioctl.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 32/43] wifi: nxpwifi: add sta_tx.c David Lin
` (13 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/sta_rx.c | 244 ++++++++++++++++++++++
1 file changed, 244 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/sta_rx.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_rx.c b/drivers/net/wireless/nxp/nxpwifi/sta_rx.c
new file mode 100644
index 000000000000..9309ac510ac5
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/sta_rx.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: station RX data handling
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include <uapi/linux/ipv6.h>
+#include <net/ndisc.h>
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "11n_aggr.h"
+#include "11n_rxreorder.h"
+
+/* This function checks if a frame is IPv4 ARP or IPv6 Neighbour advertisement
+ * frame. If frame has both source and destination mac address as same, this
+ * function drops such gratuitous frames.
+ */
+static bool
+nxpwifi_discard_gratuitous_arp(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ const struct nxpwifi_arp_eth_header *arp;
+ struct ethhdr *eth;
+ struct ipv6hdr *ipv6;
+ struct icmp6hdr *icmpv6;
+
+ eth = (struct ethhdr *)skb->data;
+ switch (ntohs(eth->h_proto)) {
+ case ETH_P_ARP:
+ arp = (void *)(skb->data + sizeof(struct ethhdr));
+ if (arp->hdr.ar_op == htons(ARPOP_REPLY) ||
+ arp->hdr.ar_op == htons(ARPOP_REQUEST)) {
+ if (!memcmp(arp->ar_sip, arp->ar_tip, 4))
+ return true;
+ }
+ break;
+ case ETH_P_IPV6:
+ ipv6 = (void *)(skb->data + sizeof(struct ethhdr));
+ icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) +
+ sizeof(struct ipv6hdr));
+ if (icmpv6->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) {
+ if (!memcmp(&ipv6->saddr, &ipv6->daddr,
+ sizeof(struct in6_addr)))
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/* This function processes the received packet and forwards it
+ * to kernel/upper layer.
+ *
+ * This function parses through the received packet and determines
+ * if it is a debug packet or normal packet.
+ *
+ * For non-debug packets, the function chops off unnecessary leading
+ * header bytes, reconstructs the packet as an ethernet frame or
+ * 802.2/llc/snap frame as required, and sends it to kernel/upper layer.
+ *
+ * The completion callback is called after processing in complete.
+ */
+int nxpwifi_process_rx_packet(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ int ret;
+ struct rx_packet_hdr *rx_pkt_hdr;
+ struct rxpd *local_rx_pd;
+ int hdr_chop;
+ struct ethhdr *eth;
+ u16 rx_pkt_off, rx_pkt_len;
+ u8 adj_rx_rate = 0;
+
+ local_rx_pd = (struct rxpd *)(skb->data);
+
+ rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset);
+ rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length);
+ rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off;
+
+ if (sizeof(rx_pkt_hdr->eth803_hdr) + sizeof(rfc1042_header) +
+ rx_pkt_off > skb->len) {
+ priv->stats.rx_dropped++;
+ dev_kfree_skb_any(skb);
+ return -1;
+ }
+
+ if (sizeof(*rx_pkt_hdr) + rx_pkt_off <= skb->len &&
+ ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header,
+ sizeof(bridge_tunnel_header))) ||
+ (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header,
+ sizeof(rfc1042_header)) &&
+ rx_pkt_hdr->rfc1042_hdr.snap_type != htons(ETH_P_AARP) &&
+ rx_pkt_hdr->rfc1042_hdr.snap_type != htons(ETH_P_IPX)))) {
+ /* Replace the 803 header and rfc1042 header (llc/snap) with an
+ * EthernetII header, keep the src/dst and snap_type
+ * (ethertype).
+ * The firmware only passes up SNAP frames converting
+ * all RX Data from 802.11 to 802.2/LLC/SNAP frames.
+ * To create the Ethernet II, just move the src, dst address
+ * right before the snap_type.
+ */
+ eth = (struct ethhdr *)
+ ((u8 *)&rx_pkt_hdr->eth803_hdr
+ + sizeof(rx_pkt_hdr->eth803_hdr) +
+ sizeof(rx_pkt_hdr->rfc1042_hdr)
+ - sizeof(rx_pkt_hdr->eth803_hdr.h_dest)
+ - sizeof(rx_pkt_hdr->eth803_hdr.h_source)
+ - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type));
+
+ memcpy(eth->h_source, rx_pkt_hdr->eth803_hdr.h_source,
+ sizeof(eth->h_source));
+ memcpy(eth->h_dest, rx_pkt_hdr->eth803_hdr.h_dest,
+ sizeof(eth->h_dest));
+
+ /* Chop off the rxpd + the excess memory from the 802.2/llc/snap
+ * header that was removed.
+ */
+ hdr_chop = (u8 *)eth - (u8 *)local_rx_pd;
+ } else {
+ /* Chop off the rxpd */
+ hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)local_rx_pd;
+ }
+
+ /* Chop off the leading header bytes so the it points to the start of
+ * either the reconstructed EthII frame or the 802.2/llc/snap frame
+ */
+ skb_pull(skb, hdr_chop);
+
+ if (priv->hs2_enabled &&
+ nxpwifi_discard_gratuitous_arp(priv, skb)) {
+ nxpwifi_dbg(priv->adapter, INFO, "Bypassed Gratuitous ARP\n");
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ /* Only stash RX bitrate for unicast packets. */
+ if (likely(!is_multicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest))) {
+ priv->rxpd_rate = local_rx_pd->rx_rate;
+ priv->rxpd_htinfo = local_rx_pd->ht_info;
+ }
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA ||
+ GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
+ adj_rx_rate = nxpwifi_adjust_data_rate(priv,
+ local_rx_pd->rx_rate,
+ local_rx_pd->ht_info);
+ nxpwifi_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr,
+ local_rx_pd->nf);
+ }
+
+ ret = nxpwifi_recv_packet(priv, skb);
+ if (ret == -1)
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "recv packet failed\n");
+
+ return ret;
+}
+
+/* This function processes the received buffer.
+ *
+ * The function looks into the RxPD and performs sanity tests on the
+ * received buffer to ensure its a valid packet, before processing it
+ * further. If the packet is determined to be aggregated, it is
+ * de-aggregated accordingly. Non-unicast packets are sent directly to
+ * the kernel/upper layers. Unicast packets are handed over to the
+ * Rx reordering routine if 11n is enabled.
+ *
+ * The completion callback is called after processing in complete.
+ */
+int nxpwifi_process_sta_rx_packet(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int ret = 0;
+ struct rxpd *local_rx_pd;
+ struct rx_packet_hdr *rx_pkt_hdr;
+ u8 ta[ETH_ALEN];
+ u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num;
+
+ local_rx_pd = (struct rxpd *)(skb->data);
+ rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type);
+ rx_pkt_offset = le16_to_cpu(local_rx_pd->rx_pkt_offset);
+ rx_pkt_length = le16_to_cpu(local_rx_pd->rx_pkt_length);
+ seq_num = le16_to_cpu(local_rx_pd->seq_num);
+
+ rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_offset;
+
+ if ((rx_pkt_offset + rx_pkt_length) > skb->len ||
+ sizeof(rx_pkt_hdr->eth803_hdr) + rx_pkt_offset > skb->len) {
+ nxpwifi_dbg(adapter, ERROR,
+ "wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n",
+ skb->len, rx_pkt_offset, rx_pkt_length);
+ priv->stats.rx_dropped++;
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+
+ if (rx_pkt_type == PKT_TYPE_MGMT) {
+ ret = nxpwifi_process_mgmt_packet(priv, skb);
+ if (ret)
+ nxpwifi_dbg(adapter, DATA, "Rx of mgmt packet failed");
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+
+ /* If the packet is not an unicast packet then send the packet
+ * directly to os. Don't pass thru rx reordering
+ */
+ if (!IS_11N_ENABLED(priv) ||
+ !ether_addr_equal_unaligned(priv->curr_addr,
+ rx_pkt_hdr->eth803_hdr.h_dest)) {
+ nxpwifi_process_rx_packet(priv, skb);
+ return ret;
+ }
+
+ if (nxpwifi_queuing_ra_based(priv)) {
+ memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN);
+ } else {
+ if (rx_pkt_type != PKT_TYPE_BAR &&
+ local_rx_pd->priority < MAX_NUM_TID)
+ priv->rx_seq[local_rx_pd->priority] = seq_num;
+ memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address,
+ ETH_ALEN);
+ }
+
+ /* Reorder and send to OS */
+ ret = nxpwifi_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority,
+ ta, (u8)rx_pkt_type, skb);
+
+ if (ret || rx_pkt_type == PKT_TYPE_BAR)
+ dev_kfree_skb_any(skb);
+
+ if (ret)
+ priv->stats.rx_dropped++;
+
+ return ret;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 32/43] wifi: nxpwifi: add sta_tx.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (30 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 31/43] wifi: nxpwifi: add sta_rx.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 33/43] wifi: nxpwifi: add txrx.c David Lin
` (12 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/sta_tx.c | 215 ++++++++++++++++++++++
1 file changed, 215 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/sta_tx.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/sta_tx.c b/drivers/net/wireless/nxp/nxpwifi/sta_tx.c
new file mode 100644
index 000000000000..8fb3a146552d
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/sta_tx.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: station TX data handling
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cmdevt.h"
+#include "wmm.h"
+
+/* This function fills the TxPD for tx packets.
+ *
+ * The Tx buffer received by this function should already have the
+ * header space allocated for TxPD.
+ *
+ * This function inserts the TxPD in between interface header and actual
+ * data and adjusts the buffer pointers accordingly.
+ *
+ * The following TxPD fields are set by this function, as required -
+ * - BSS number
+ * - Tx packet length and offset
+ * - Priority
+ * - Packet delay
+ * - Priority specific Tx control
+ * - Flags
+ */
+void nxpwifi_process_sta_txpd(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct txpd *local_tx_pd;
+ struct nxpwifi_txinfo *tx_info = NXPWIFI_SKB_TXCB(skb);
+ unsigned int pad;
+ u16 pkt_type, pkt_length, pkt_offset;
+ int hroom = adapter->intf_hdr_len;
+ u32 tx_control;
+
+ pkt_type = nxpwifi_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0;
+
+ pad = ((uintptr_t)skb->data - (sizeof(*local_tx_pd) + hroom)) &
+ (NXPWIFI_DMA_ALIGN_SZ - 1);
+ skb_push(skb, sizeof(*local_tx_pd) + pad);
+
+ local_tx_pd = (struct txpd *)skb->data;
+ memset(local_tx_pd, 0, sizeof(struct txpd));
+ local_tx_pd->bss_num = priv->bss_num;
+ local_tx_pd->bss_type = priv->bss_type;
+
+ pkt_length = (u16)(skb->len - (sizeof(struct txpd) + pad));
+ if (pkt_type == PKT_TYPE_MGMT)
+ pkt_length -= NXPWIFI_MGMT_FRAME_HEADER_SIZE;
+ local_tx_pd->tx_pkt_length = cpu_to_le16(pkt_length);
+
+ local_tx_pd->priority = (u8)skb->priority;
+ local_tx_pd->pkt_delay_2ms =
+ nxpwifi_wmm_compute_drv_pkt_delay(priv, skb);
+
+ if (tx_info->flags & NXPWIFI_BUF_FLAG_EAPOL_TX_STATUS ||
+ tx_info->flags & NXPWIFI_BUF_FLAG_ACTION_TX_STATUS) {
+ local_tx_pd->tx_token_id = tx_info->ack_frame_id;
+ local_tx_pd->flags |= NXPWIFI_TXPD_FLAGS_REQ_TX_STATUS;
+ }
+
+ if (local_tx_pd->priority <
+ ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl)) {
+ /* Set the priority specific tx_control field, setting of 0 will
+ * cause the default value to be used later in this function
+ */
+ tx_control =
+ priv->wmm.user_pri_pkt_tx_ctrl[local_tx_pd->priority];
+ local_tx_pd->tx_control = cpu_to_le32(tx_control);
+ }
+
+ if (adapter->pps_uapsd_mode) {
+ if (nxpwifi_check_last_packet_indication(priv)) {
+ adapter->tx_lock_flag = true;
+ local_tx_pd->flags =
+ NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET;
+ }
+ }
+
+ /* Offset of actual data */
+ pkt_offset = sizeof(struct txpd) + pad;
+ if (pkt_type == PKT_TYPE_MGMT) {
+ /* Set the packet type and add header for management frame */
+ local_tx_pd->tx_pkt_type = cpu_to_le16(pkt_type);
+ pkt_offset += NXPWIFI_MGMT_FRAME_HEADER_SIZE;
+ }
+
+ local_tx_pd->tx_pkt_offset = cpu_to_le16(pkt_offset);
+
+ /* make space for adapter->intf_hdr_len */
+ skb_push(skb, hroom);
+
+ if (!local_tx_pd->tx_control)
+ /* TxCtrl set by user or default */
+ local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
+}
+
+/* This function tells firmware to send a NULL data packet.
+ *
+ * The function creates a NULL data packet with TxPD and sends to the
+ * firmware for transmission, with highest priority setting.
+ */
+int nxpwifi_send_null_packet(struct nxpwifi_private *priv, u8 flags)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct txpd *local_tx_pd;
+ struct nxpwifi_tx_param tx_param;
+/* sizeof(struct txpd) + Interface specific header */
+#define NULL_PACKET_HDR 64
+ u32 data_len = NULL_PACKET_HDR;
+ struct sk_buff *skb;
+ int ret;
+ struct nxpwifi_txinfo *tx_info = NULL;
+
+ if (test_bit(NXPWIFI_SURPRISE_REMOVED, &adapter->work_flags))
+ return -1;
+
+ if (!priv->media_connected)
+ return -1;
+
+ if (adapter->data_sent)
+ return -1;
+
+ if (adapter->if_ops.is_port_ready &&
+ !adapter->if_ops.is_port_ready(priv))
+ return -1;
+
+ skb = dev_alloc_skb(data_len);
+ if (!skb)
+ return -1;
+
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+ memset(tx_info, 0, sizeof(*tx_info));
+ tx_info->bss_num = priv->bss_num;
+ tx_info->bss_type = priv->bss_type;
+ tx_info->pkt_len = data_len -
+ (sizeof(struct txpd) + adapter->intf_hdr_len);
+ skb_reserve(skb, sizeof(struct txpd) + adapter->intf_hdr_len);
+ skb_push(skb, sizeof(struct txpd));
+
+ local_tx_pd = (struct txpd *)skb->data;
+ local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
+ local_tx_pd->flags = flags;
+ local_tx_pd->priority = WMM_HIGHEST_PRIORITY;
+ local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd));
+ local_tx_pd->bss_num = priv->bss_num;
+ local_tx_pd->bss_type = priv->bss_type;
+
+ skb_push(skb, adapter->intf_hdr_len);
+ tx_param.next_pkt_len = 0;
+ ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_DATA,
+ skb, &tx_param);
+
+ switch (ret) {
+ case -EBUSY:
+ dev_kfree_skb_any(skb);
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: host_to_card failed: ret=%d\n",
+ __func__, ret);
+ adapter->dbg.num_tx_host_to_card_failure++;
+ break;
+ case -1:
+ dev_kfree_skb_any(skb);
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: host_to_card failed: ret=%d\n",
+ __func__, ret);
+ adapter->dbg.num_tx_host_to_card_failure++;
+ break;
+ case 0:
+ dev_kfree_skb_any(skb);
+ nxpwifi_dbg(adapter, DATA,
+ "data: %s: host_to_card succeeded\n",
+ __func__);
+ adapter->tx_lock_flag = true;
+ break;
+ case -EINPROGRESS:
+ adapter->tx_lock_flag = true;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* This function checks if we need to send last packet indication.
+ */
+u8
+nxpwifi_check_last_packet_indication(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u8 ret = false;
+
+ if (!adapter->sleep_period.period)
+ return ret;
+ if (nxpwifi_wmm_lists_empty(adapter))
+ ret = true;
+
+ if (ret && !adapter->cmd_sent && !adapter->curr_cmd &&
+ !is_command_pending(adapter)) {
+ adapter->delay_null_pkt = false;
+ ret = true;
+ } else {
+ ret = false;
+ adapter->delay_null_pkt = true;
+ }
+ return ret;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 33/43] wifi: nxpwifi: add txrx.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (31 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 32/43] wifi: nxpwifi: add sta_tx.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:51 ` [PATCH 34/43] wifi: nxpwifi: add uap_cmd.c David Lin
` (11 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/txrx.c | 362 ++++++++++++++++++++++++
1 file changed, 362 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/txrx.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/txrx.c b/drivers/net/wireless/nxp/nxpwifi/txrx.c
new file mode 100644
index 000000000000..3fe1cbc880e2
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/txrx.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: generic TX/RX data handling
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+
+/* This function processes the received buffer.
+ *
+ * Main responsibility of this function is to parse the RxPD to
+ * identify the correct interface this packet is headed for and
+ * forwarding it to the associated handling function, where the
+ * packet will be further processed and sent to kernel/upper layer
+ * if required.
+ */
+int nxpwifi_handle_rx_packet(struct nxpwifi_adapter *adapter,
+ struct sk_buff *skb)
+{
+ struct nxpwifi_private *priv =
+ nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
+ struct rxpd *local_rx_pd;
+ struct nxpwifi_rxinfo *rx_info = NXPWIFI_SKB_RXCB(skb);
+ int ret;
+
+ local_rx_pd = (struct rxpd *)(skb->data);
+ /* Get the BSS number from rxpd, get corresponding priv */
+ priv = nxpwifi_get_priv_by_id(adapter, local_rx_pd->bss_num &
+ BSS_NUM_MASK, local_rx_pd->bss_type);
+ if (!priv)
+ priv = nxpwifi_get_priv(adapter, NXPWIFI_BSS_ROLE_ANY);
+
+ if (!priv) {
+ nxpwifi_dbg(adapter, ERROR,
+ "data: priv not found. Drop RX packet\n");
+ dev_kfree_skb_any(skb);
+ return -1;
+ }
+
+ nxpwifi_dbg_dump(adapter, DAT_D, "rx pkt:", skb->data,
+ min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN));
+
+ memset(rx_info, 0, sizeof(*rx_info));
+ rx_info->bss_num = priv->bss_num;
+ rx_info->bss_type = priv->bss_type;
+
+ if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP)
+ ret = nxpwifi_process_uap_rx_packet(priv, skb);
+ else
+ ret = nxpwifi_process_sta_rx_packet(priv, skb);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nxpwifi_handle_rx_packet);
+
+/* This function sends a packet to device.
+ *
+ * It processes the packet to add the TxPD, checks condition and
+ * sends the processed packet to firmware for transmission.
+ *
+ * On successful completion, the function calls the completion callback
+ * and logs the time.
+ */
+int nxpwifi_process_tx(struct nxpwifi_private *priv, struct sk_buff *skb,
+ struct nxpwifi_tx_param *tx_param)
+{
+ int hroom, ret;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct txpd *local_tx_pd = NULL;
+ struct nxpwifi_sta_node *dest_node;
+ struct ethhdr *hdr = (void *)skb->data;
+
+ if (unlikely(!skb->len ||
+ skb_headroom(skb) < NXPWIFI_MIN_DATA_HEADER_LEN)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ hroom = adapter->intf_hdr_len;
+
+ if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP) {
+ dest_node = nxpwifi_get_sta_entry(priv, hdr->h_dest);
+ if (dest_node) {
+ dest_node->stats.tx_bytes += skb->len;
+ dest_node->stats.tx_packets++;
+ }
+
+ nxpwifi_process_uap_txpd(priv, skb);
+ } else {
+ nxpwifi_process_sta_txpd(priv, skb);
+ }
+
+ if ((adapter->data_sent || adapter->tx_lock_flag)) {
+ skb_queue_tail(&adapter->tx_data_q, skb);
+ atomic_inc(&adapter->tx_queued);
+ return 0;
+ }
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA)
+ local_tx_pd = (struct txpd *)(skb->data + hroom);
+ ret = adapter->if_ops.host_to_card(adapter,
+ NXPWIFI_TYPE_DATA,
+ skb, tx_param);
+ nxpwifi_dbg_dump(adapter, DAT_D, "tx pkt:", skb->data,
+ min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN));
+
+out:
+ switch (ret) {
+ case -ENOSR:
+ nxpwifi_dbg(adapter, DATA, "data: -ENOSR is returned\n");
+ break;
+ case -EBUSY:
+ if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) &&
+ adapter->pps_uapsd_mode && adapter->tx_lock_flag) {
+ priv->adapter->tx_lock_flag = false;
+ if (local_tx_pd)
+ local_tx_pd->flags = 0;
+ }
+ nxpwifi_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
+ break;
+ case -1:
+ nxpwifi_dbg(adapter, ERROR,
+ "nxpwifi_write_data_async failed: 0x%X\n",
+ ret);
+ adapter->dbg.num_tx_host_to_card_failure++;
+ nxpwifi_write_data_complete(adapter, skb, 0, ret);
+ break;
+ case -EINPROGRESS:
+ break;
+ case -EINVAL:
+ nxpwifi_dbg(adapter, ERROR,
+ "malformed skb (length: %u, headroom: %u)\n",
+ skb->len, skb_headroom(skb));
+ fallthrough;
+ case 0:
+ nxpwifi_write_data_complete(adapter, skb, 0, ret);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int nxpwifi_host_to_card(struct nxpwifi_adapter *adapter,
+ struct sk_buff *skb,
+ struct nxpwifi_tx_param *tx_param)
+{
+ struct txpd *local_tx_pd = NULL;
+ u8 *head_ptr = skb->data;
+ int ret = 0;
+ struct nxpwifi_private *priv;
+ struct nxpwifi_txinfo *tx_info;
+
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+ priv = nxpwifi_get_priv_by_id(adapter, tx_info->bss_num,
+ tx_info->bss_type);
+ if (!priv) {
+ nxpwifi_dbg(adapter, ERROR,
+ "data: priv not found. Drop TX packet\n");
+ adapter->dbg.num_tx_host_to_card_failure++;
+ nxpwifi_write_data_complete(adapter, skb, 0, 0);
+ return ret;
+ }
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA)
+ local_tx_pd = (struct txpd *)(head_ptr + adapter->intf_hdr_len);
+
+ ret = adapter->if_ops.host_to_card(adapter,
+ NXPWIFI_TYPE_DATA,
+ skb, tx_param);
+
+ switch (ret) {
+ case -ENOSR:
+ nxpwifi_dbg(adapter, ERROR, "data: -ENOSR is returned\n");
+ break;
+ case -EBUSY:
+ if ((GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_STA) &&
+ adapter->pps_uapsd_mode &&
+ adapter->tx_lock_flag) {
+ priv->adapter->tx_lock_flag = false;
+ if (local_tx_pd)
+ local_tx_pd->flags = 0;
+ }
+ skb_queue_head(&adapter->tx_data_q, skb);
+ if (tx_info->flags & NXPWIFI_BUF_FLAG_AGGR_PKT)
+ atomic_add(tx_info->aggr_num, &adapter->tx_queued);
+ else
+ atomic_inc(&adapter->tx_queued);
+ nxpwifi_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
+ break;
+ case -1:
+ nxpwifi_dbg(adapter, ERROR,
+ "nxpwifi_write_data_async failed: 0x%X\n", ret);
+ adapter->dbg.num_tx_host_to_card_failure++;
+ nxpwifi_write_data_complete(adapter, skb, 0, ret);
+ break;
+ case -EINPROGRESS:
+ break;
+ case 0:
+ nxpwifi_write_data_complete(adapter, skb, 0, ret);
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int
+nxpwifi_dequeue_tx_queue(struct nxpwifi_adapter *adapter)
+{
+ struct sk_buff *skb, *skb_next;
+ struct nxpwifi_txinfo *tx_info;
+ struct nxpwifi_tx_param tx_param;
+
+ skb = skb_dequeue(&adapter->tx_data_q);
+ if (!skb)
+ return -1;
+
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+ if (tx_info->flags & NXPWIFI_BUF_FLAG_AGGR_PKT)
+ atomic_sub(tx_info->aggr_num, &adapter->tx_queued);
+ else
+ atomic_dec(&adapter->tx_queued);
+
+ if (!skb_queue_empty(&adapter->tx_data_q))
+ skb_next = skb_peek(&adapter->tx_data_q);
+ else
+ skb_next = NULL;
+ tx_param.next_pkt_len = ((skb_next) ? skb_next->len : 0);
+ if (!tx_param.next_pkt_len) {
+ if (!nxpwifi_wmm_lists_empty(adapter))
+ tx_param.next_pkt_len = 1;
+ }
+ return nxpwifi_host_to_card(adapter, skb, &tx_param);
+}
+
+void
+nxpwifi_process_tx_queue(struct nxpwifi_adapter *adapter)
+{
+ do {
+ if (adapter->data_sent || adapter->tx_lock_flag)
+ break;
+ if (nxpwifi_dequeue_tx_queue(adapter))
+ break;
+ } while (!skb_queue_empty(&adapter->tx_data_q));
+}
+
+/* Packet send completion callback handler.
+ *
+ * It either frees the buffer directly or forwards it to another
+ * completion callback which checks conditions, updates statistics,
+ * wakes up stalled traffic queue if required, and then frees the buffer.
+ */
+int nxpwifi_write_data_complete(struct nxpwifi_adapter *adapter,
+ struct sk_buff *skb, int aggr, int status)
+{
+ struct nxpwifi_private *priv;
+ struct nxpwifi_txinfo *tx_info;
+ struct netdev_queue *txq;
+ int index;
+
+ if (!skb)
+ return 0;
+
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+ priv = nxpwifi_get_priv_by_id(adapter, tx_info->bss_num,
+ tx_info->bss_type);
+ if (!priv)
+ goto done;
+
+ nxpwifi_set_trans_start(priv->netdev);
+
+ if (tx_info->flags & NXPWIFI_BUF_FLAG_BRIDGED_PKT)
+ atomic_dec_return(&adapter->pending_bridged_pkts);
+
+ if (tx_info->flags & NXPWIFI_BUF_FLAG_AGGR_PKT)
+ goto done;
+
+ if (!status) {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += tx_info->pkt_len;
+ if (priv->tx_timeout_cnt)
+ priv->tx_timeout_cnt = 0;
+ } else {
+ priv->stats.tx_errors++;
+ }
+
+ if (aggr)
+ /* For skb_aggr, do not wake up tx queue */
+ goto done;
+
+ atomic_dec(&adapter->tx_pending);
+
+ index = nxpwifi_1d_to_wmm_queue[skb->priority];
+ if (atomic_dec_return(&priv->wmm_tx_pending[index]) < LOW_TX_PENDING) {
+ txq = netdev_get_tx_queue(priv->netdev, index);
+ if (netif_tx_queue_stopped(txq)) {
+ netif_tx_wake_queue(txq);
+ nxpwifi_dbg(adapter, DATA, "wake queue: %d\n", index);
+ }
+ }
+done:
+ dev_kfree_skb_any(skb);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nxpwifi_write_data_complete);
+
+void nxpwifi_parse_tx_status_event(struct nxpwifi_private *priv,
+ void *event_body)
+{
+ struct tx_status_event *tx_status = (void *)priv->adapter->event_body;
+ struct sk_buff *ack_skb;
+ struct nxpwifi_txinfo *tx_info;
+
+ if (!tx_status->tx_token_id)
+ return;
+
+ spin_lock_bh(&priv->ack_status_lock);
+ ack_skb = idr_remove(&priv->ack_status_frames, tx_status->tx_token_id);
+ spin_unlock_bh(&priv->ack_status_lock);
+
+ if (ack_skb) {
+ tx_info = NXPWIFI_SKB_TXCB(ack_skb);
+
+ if (tx_info->flags & NXPWIFI_BUF_FLAG_EAPOL_TX_STATUS) {
+ /* consumes ack_skb */
+ skb_complete_wifi_ack(ack_skb, !tx_status->status);
+ } else {
+ /* Remove broadcast address which was added by driver */
+ memmove(ack_skb->data +
+ sizeof(struct ieee80211_hdr_3addr) +
+ NXPWIFI_MGMT_FRAME_HEADER_SIZE + sizeof(u16),
+ ack_skb->data +
+ sizeof(struct ieee80211_hdr_3addr) +
+ NXPWIFI_MGMT_FRAME_HEADER_SIZE + sizeof(u16) +
+ ETH_ALEN, ack_skb->len -
+ (sizeof(struct ieee80211_hdr_3addr) +
+ NXPWIFI_MGMT_FRAME_HEADER_SIZE + sizeof(u16) +
+ ETH_ALEN));
+ ack_skb->len = ack_skb->len - ETH_ALEN;
+ /* Remove driver's proprietary header including 2 bytes
+ * of packet length and pass actual management frame buffer
+ * to cfg80211.
+ */
+ cfg80211_mgmt_tx_status(&priv->wdev, tx_info->cookie,
+ ack_skb->data +
+ NXPWIFI_MGMT_FRAME_HEADER_SIZE +
+ sizeof(u16), ack_skb->len -
+ (NXPWIFI_MGMT_FRAME_HEADER_SIZE
+ + sizeof(u16)),
+ !tx_status->status, GFP_ATOMIC);
+ dev_kfree_skb_any(ack_skb);
+ }
+ }
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 34/43] wifi: nxpwifi: add uap_cmd.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (32 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 33/43] wifi: nxpwifi: add txrx.c David Lin
@ 2024-06-21 7:51 ` David Lin
2024-06-21 7:52 ` [PATCH 35/43] wifi: nxpwifi: add uap_event.c David Lin
` (10 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:51 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/uap_cmd.c | 1170 ++++++++++++++++++++
1 file changed, 1170 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/uap_cmd.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/uap_cmd.c b/drivers/net/wireless/nxp/nxpwifi/uap_cmd.c
new file mode 100644
index 000000000000..ec89e8ba90fe
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/uap_cmd.c
@@ -0,0 +1,1170 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: AP specific command handling
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "main.h"
+#include "cmdevt.h"
+#include "11ac.h"
+#include "11n.h"
+
+/* This function parses BSS related parameters from structure
+ * and prepares TLVs specific to WPA/WPA2 security.
+ * These TLVs are appended to command buffer.
+ */
+static void
+nxpwifi_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
+{
+ struct host_cmd_tlv_pwk_cipher *pwk_cipher;
+ struct host_cmd_tlv_gwk_cipher *gwk_cipher;
+ struct host_cmd_tlv_passphrase *passphrase;
+ struct host_cmd_tlv_akmp *tlv_akmp;
+ struct nxpwifi_uap_bss_param *bss_cfg = cmd_buf;
+ u16 cmd_size = *param_size;
+ u8 *tlv = *tlv_buf;
+
+ tlv_akmp = (struct host_cmd_tlv_akmp *)tlv;
+ tlv_akmp->header.type = cpu_to_le16(TLV_TYPE_UAP_AKMP);
+ tlv_akmp->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) -
+ sizeof(struct nxpwifi_ie_types_header));
+ tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation);
+ tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt);
+ cmd_size += sizeof(struct host_cmd_tlv_akmp);
+ tlv += sizeof(struct host_cmd_tlv_akmp);
+
+ if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) {
+ pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
+ pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
+ pwk_cipher->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
+ sizeof(struct nxpwifi_ie_types_header));
+ pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA);
+ pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa;
+ cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
+ tlv += sizeof(struct host_cmd_tlv_pwk_cipher);
+ }
+
+ if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) {
+ pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
+ pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
+ pwk_cipher->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
+ sizeof(struct nxpwifi_ie_types_header));
+ pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2);
+ pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2;
+ cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
+ tlv += sizeof(struct host_cmd_tlv_pwk_cipher);
+ }
+
+ if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) {
+ gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv;
+ gwk_cipher->header.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER);
+ gwk_cipher->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) -
+ sizeof(struct nxpwifi_ie_types_header));
+ gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher;
+ cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher);
+ tlv += sizeof(struct host_cmd_tlv_gwk_cipher);
+ }
+
+ if (bss_cfg->wpa_cfg.length) {
+ passphrase = (struct host_cmd_tlv_passphrase *)tlv;
+ passphrase->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE);
+ passphrase->header.len = cpu_to_le16(bss_cfg->wpa_cfg.length);
+ memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase,
+ bss_cfg->wpa_cfg.length);
+ cmd_size += sizeof(struct nxpwifi_ie_types_header) +
+ bss_cfg->wpa_cfg.length;
+ tlv += sizeof(struct nxpwifi_ie_types_header) +
+ bss_cfg->wpa_cfg.length;
+ }
+
+ *param_size = cmd_size;
+ *tlv_buf = tlv;
+}
+
+/* This function parses BSS related parameters from structure
+ * and prepares TLVs specific to WEP encryption.
+ * These TLVs are appended to command buffer.
+ */
+static void
+nxpwifi_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
+{
+ struct host_cmd_tlv_wep_key *wep_key;
+ u16 cmd_size = *param_size;
+ int i;
+ u8 *tlv = *tlv_buf;
+ struct nxpwifi_uap_bss_param *bss_cfg = cmd_buf;
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (bss_cfg->wep_cfg[i].length &&
+ (bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 ||
+ bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) {
+ wep_key = (struct host_cmd_tlv_wep_key *)tlv;
+ wep_key->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_WEP_KEY);
+ wep_key->header.len =
+ cpu_to_le16(bss_cfg->wep_cfg[i].length + 2);
+ wep_key->key_index = bss_cfg->wep_cfg[i].key_index;
+ wep_key->is_default = bss_cfg->wep_cfg[i].is_default;
+ memcpy(wep_key->key, bss_cfg->wep_cfg[i].key,
+ bss_cfg->wep_cfg[i].length);
+ cmd_size += sizeof(struct nxpwifi_ie_types_header) + 2 +
+ bss_cfg->wep_cfg[i].length;
+ tlv += sizeof(struct nxpwifi_ie_types_header) + 2 +
+ bss_cfg->wep_cfg[i].length;
+ }
+ }
+
+ *param_size = cmd_size;
+ *tlv_buf = tlv;
+}
+
+/* This function parses BSS related parameters from structure
+ * and prepares TLVs. These TLVs are appended to command buffer.
+ */
+static int
+nxpwifi_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
+{
+ struct host_cmd_tlv_mac_addr *mac_tlv;
+ struct host_cmd_tlv_dtim_period *dtim_period;
+ struct host_cmd_tlv_beacon_period *beacon_period;
+ struct host_cmd_tlv_ssid *ssid;
+ struct host_cmd_tlv_bcast_ssid *bcast_ssid;
+ struct host_cmd_tlv_channel_band *chan_band;
+ struct host_cmd_tlv_frag_threshold *frag_threshold;
+ struct host_cmd_tlv_rts_threshold *rts_threshold;
+ struct host_cmd_tlv_retry_limit *retry_limit;
+ struct host_cmd_tlv_encrypt_protocol *encrypt_protocol;
+ struct host_cmd_tlv_auth_type *auth_type;
+ struct host_cmd_tlv_rates *tlv_rates;
+ struct host_cmd_tlv_ageout_timer *ao_timer, *ps_ao_timer;
+ struct host_cmd_tlv_power_constraint *pwr_ct;
+ struct nxpwifi_ie_types_htcap *htcap;
+ struct nxpwifi_ie_types_wmmcap *wmm_cap;
+ struct nxpwifi_uap_bss_param *bss_cfg = cmd_buf;
+ int i;
+ u16 cmd_size = *param_size;
+
+ mac_tlv = (struct host_cmd_tlv_mac_addr *)tlv;
+ mac_tlv->header.type = cpu_to_le16(TLV_TYPE_UAP_MAC_ADDRESS);
+ mac_tlv->header.len = cpu_to_le16(ETH_ALEN);
+ memcpy(mac_tlv->mac_addr, bss_cfg->mac_addr, ETH_ALEN);
+ cmd_size += sizeof(struct host_cmd_tlv_mac_addr);
+ tlv += sizeof(struct host_cmd_tlv_mac_addr);
+
+ if (bss_cfg->ssid.ssid_len) {
+ ssid = (struct host_cmd_tlv_ssid *)tlv;
+ ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_SSID);
+ ssid->header.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len);
+ memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len);
+ cmd_size += sizeof(struct nxpwifi_ie_types_header) +
+ bss_cfg->ssid.ssid_len;
+ tlv += sizeof(struct nxpwifi_ie_types_header) +
+ bss_cfg->ssid.ssid_len;
+
+ bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv;
+ bcast_ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID);
+ bcast_ssid->header.len =
+ cpu_to_le16(sizeof(bcast_ssid->bcast_ctl));
+ bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl;
+ cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid);
+ tlv += sizeof(struct host_cmd_tlv_bcast_ssid);
+ }
+ if (bss_cfg->rates[0]) {
+ tlv_rates = (struct host_cmd_tlv_rates *)tlv;
+ tlv_rates->header.type = cpu_to_le16(TLV_TYPE_UAP_RATES);
+
+ for (i = 0; i < NXPWIFI_SUPPORTED_RATES && bss_cfg->rates[i];
+ i++)
+ tlv_rates->rates[i] = bss_cfg->rates[i];
+
+ tlv_rates->header.len = cpu_to_le16(i);
+ cmd_size += sizeof(struct host_cmd_tlv_rates) + i;
+ tlv += sizeof(struct host_cmd_tlv_rates) + i;
+ }
+ if (bss_cfg->channel &&
+ (((bss_cfg->band_cfg & BIT(0)) == BAND_CONFIG_BG &&
+ bss_cfg->channel <= MAX_CHANNEL_BAND_BG) ||
+ ((bss_cfg->band_cfg & BIT(0)) == BAND_CONFIG_A &&
+ bss_cfg->channel <= MAX_CHANNEL_BAND_A))) {
+ chan_band = (struct host_cmd_tlv_channel_band *)tlv;
+ chan_band->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST);
+ chan_band->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) -
+ sizeof(struct nxpwifi_ie_types_header));
+ chan_band->band_config = bss_cfg->band_cfg;
+ chan_band->channel = bss_cfg->channel;
+ cmd_size += sizeof(struct host_cmd_tlv_channel_band);
+ tlv += sizeof(struct host_cmd_tlv_channel_band);
+ }
+ if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD &&
+ bss_cfg->beacon_period <= MAX_BEACON_PERIOD) {
+ beacon_period = (struct host_cmd_tlv_beacon_period *)tlv;
+ beacon_period->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD);
+ beacon_period->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) -
+ sizeof(struct nxpwifi_ie_types_header));
+ beacon_period->period = cpu_to_le16(bss_cfg->beacon_period);
+ cmd_size += sizeof(struct host_cmd_tlv_beacon_period);
+ tlv += sizeof(struct host_cmd_tlv_beacon_period);
+ }
+ if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD &&
+ bss_cfg->dtim_period <= MAX_DTIM_PERIOD) {
+ dtim_period = (struct host_cmd_tlv_dtim_period *)tlv;
+ dtim_period->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD);
+ dtim_period->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) -
+ sizeof(struct nxpwifi_ie_types_header));
+ dtim_period->period = bss_cfg->dtim_period;
+ cmd_size += sizeof(struct host_cmd_tlv_dtim_period);
+ tlv += sizeof(struct host_cmd_tlv_dtim_period);
+ }
+ if (bss_cfg->rts_threshold <= NXPWIFI_RTS_MAX_VALUE) {
+ rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv;
+ rts_threshold->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD);
+ rts_threshold->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) -
+ sizeof(struct nxpwifi_ie_types_header));
+ rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold);
+ cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
+ tlv += sizeof(struct host_cmd_tlv_frag_threshold);
+ }
+ if (bss_cfg->frag_threshold >= NXPWIFI_FRAG_MIN_VALUE &&
+ bss_cfg->frag_threshold <= NXPWIFI_FRAG_MAX_VALUE) {
+ frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv;
+ frag_threshold->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD);
+ frag_threshold->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) -
+ sizeof(struct nxpwifi_ie_types_header));
+ frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold);
+ cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
+ tlv += sizeof(struct host_cmd_tlv_frag_threshold);
+ }
+ if (bss_cfg->retry_limit <= NXPWIFI_RETRY_LIMIT) {
+ retry_limit = (struct host_cmd_tlv_retry_limit *)tlv;
+ retry_limit->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT);
+ retry_limit->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) -
+ sizeof(struct nxpwifi_ie_types_header));
+ retry_limit->limit = (u8)bss_cfg->retry_limit;
+ cmd_size += sizeof(struct host_cmd_tlv_retry_limit);
+ tlv += sizeof(struct host_cmd_tlv_retry_limit);
+ }
+ if ((bss_cfg->protocol & PROTOCOL_WPA) ||
+ (bss_cfg->protocol & PROTOCOL_WPA2) ||
+ (bss_cfg->protocol & PROTOCOL_EAP))
+ nxpwifi_uap_bss_wpa(&tlv, cmd_buf, &cmd_size);
+ else
+ nxpwifi_uap_bss_wep(&tlv, cmd_buf, &cmd_size);
+
+ if (bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY ||
+ bss_cfg->auth_mode == NXPWIFI_AUTH_MODE_AUTO) {
+ auth_type = (struct host_cmd_tlv_auth_type *)tlv;
+ auth_type->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
+ auth_type->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) -
+ sizeof(struct nxpwifi_ie_types_header));
+ auth_type->auth_type = (u8)bss_cfg->auth_mode;
+ auth_type->pwe_derivation = 0;
+ auth_type->transition_disable = 0;
+ cmd_size += sizeof(struct host_cmd_tlv_auth_type);
+ tlv += sizeof(struct host_cmd_tlv_auth_type);
+ }
+ if (bss_cfg->protocol) {
+ encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv;
+ encrypt_protocol->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL);
+ encrypt_protocol->header.len =
+ cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol)
+ - sizeof(struct nxpwifi_ie_types_header));
+ encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol);
+ cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol);
+ tlv += sizeof(struct host_cmd_tlv_encrypt_protocol);
+ }
+
+ if (bss_cfg->ht_cap.cap_info) {
+ htcap = (struct nxpwifi_ie_types_htcap *)tlv;
+ htcap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
+ htcap->header.len =
+ cpu_to_le16(sizeof(struct ieee80211_ht_cap));
+ htcap->ht_cap.cap_info = bss_cfg->ht_cap.cap_info;
+ htcap->ht_cap.ampdu_params_info =
+ bss_cfg->ht_cap.ampdu_params_info;
+ memcpy(&htcap->ht_cap.mcs, &bss_cfg->ht_cap.mcs,
+ sizeof(struct ieee80211_mcs_info));
+ htcap->ht_cap.extended_ht_cap_info =
+ bss_cfg->ht_cap.extended_ht_cap_info;
+ htcap->ht_cap.tx_BF_cap_info = bss_cfg->ht_cap.tx_BF_cap_info;
+ htcap->ht_cap.antenna_selection_info =
+ bss_cfg->ht_cap.antenna_selection_info;
+ cmd_size += sizeof(struct nxpwifi_ie_types_htcap);
+ tlv += sizeof(struct nxpwifi_ie_types_htcap);
+ }
+
+ if (bss_cfg->wmm_info.qos_info != 0xFF) {
+ wmm_cap = (struct nxpwifi_ie_types_wmmcap *)tlv;
+ wmm_cap->header.type = cpu_to_le16(WLAN_EID_VENDOR_SPECIFIC);
+ wmm_cap->header.len = cpu_to_le16(sizeof(wmm_cap->wmm_info));
+ memcpy(&wmm_cap->wmm_info, &bss_cfg->wmm_info,
+ sizeof(wmm_cap->wmm_info));
+ cmd_size += sizeof(struct nxpwifi_ie_types_wmmcap);
+ tlv += sizeof(struct nxpwifi_ie_types_wmmcap);
+ }
+
+ if (bss_cfg->sta_ao_timer) {
+ ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
+ ao_timer->header.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER);
+ ao_timer->header.len = cpu_to_le16(sizeof(*ao_timer) -
+ sizeof(struct nxpwifi_ie_types_header));
+ ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer);
+ cmd_size += sizeof(*ao_timer);
+ tlv += sizeof(*ao_timer);
+ }
+
+ if (bss_cfg->power_constraint) {
+ pwr_ct = (void *)tlv;
+ pwr_ct->header.type = cpu_to_le16(TLV_TYPE_PWR_CONSTRAINT);
+ pwr_ct->header.len = cpu_to_le16(sizeof(u8));
+ pwr_ct->constraint = bss_cfg->power_constraint;
+ cmd_size += sizeof(*pwr_ct);
+ tlv += sizeof(*pwr_ct);
+ }
+
+ if (bss_cfg->ps_sta_ao_timer) {
+ ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
+ ps_ao_timer->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER);
+ ps_ao_timer->header.len = cpu_to_le16(sizeof(*ps_ao_timer) -
+ sizeof(struct nxpwifi_ie_types_header));
+ ps_ao_timer->sta_ao_timer =
+ cpu_to_le32(bss_cfg->ps_sta_ao_timer);
+ cmd_size += sizeof(*ps_ao_timer);
+ tlv += sizeof(*ps_ao_timer);
+ }
+
+ *param_size = cmd_size;
+
+ return 0;
+}
+
+/* This function parses custom IEs from IE list and prepares command buffer */
+static int nxpwifi_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size)
+{
+ struct nxpwifi_ie_list *ap_ie = cmd_buf;
+ struct nxpwifi_ie_types_header *tlv_ie = (void *)tlv;
+
+ if (!ap_ie || !ap_ie->len)
+ return -1;
+
+ *ie_size += le16_to_cpu(ap_ie->len) +
+ sizeof(struct nxpwifi_ie_types_header);
+
+ tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
+ tlv_ie->len = ap_ie->len;
+ tlv += sizeof(struct nxpwifi_ie_types_header);
+
+ memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len));
+
+ return 0;
+}
+
+/* Parse AP config structure and prepare TLV based command structure
+ * to be sent to FW for uAP configuration
+ */
+static int
+nxpwifi_cmd_uap_sys_config(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ u8 *tlv;
+ u16 cmd_size, param_size, ie_size;
+ struct host_cmd_ds_sys_config *sys_cfg;
+
+ cmd->command = cpu_to_le16(HOST_CMD_UAP_SYS_CONFIG);
+ cmd_size = (u16)(sizeof(struct host_cmd_ds_sys_config) + S_DS_GEN);
+ sys_cfg = &cmd->params.uap_sys_config;
+ sys_cfg->action = cpu_to_le16(cmd_action);
+ tlv = sys_cfg->tlv;
+
+ switch (cmd_type) {
+ case UAP_BSS_PARAMS_I:
+ param_size = cmd_size;
+ if (nxpwifi_uap_bss_param_prepare(tlv, data_buf, ¶m_size))
+ return -1;
+ cmd->size = cpu_to_le16(param_size);
+ break;
+ case UAP_CUSTOM_IE_I:
+ ie_size = cmd_size;
+ if (nxpwifi_uap_custom_ie_prepare(tlv, data_buf, &ie_size))
+ return -1;
+ cmd->size = cpu_to_le16(ie_size);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_cmd_uap_bss_start(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct nxpwifi_ie_types_host_mlme *tlv;
+ int size;
+
+ cmd->command = cpu_to_le16(HOST_CMD_UAP_BSS_START);
+ size = S_DS_GEN;
+
+ tlv = (struct nxpwifi_ie_types_host_mlme *)((u8 *)cmd + size);
+ tlv->header.type = cpu_to_le16(TLV_TYPE_HOST_MLME);
+ tlv->header.len = cpu_to_le16(sizeof(tlv->host_mlme));
+ tlv->host_mlme = 1;
+ size += sizeof(struct nxpwifi_ie_types_host_mlme);
+
+ cmd->size = cpu_to_le16(size);
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_uap_bss_start(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ adapter->tx_lock_flag = false;
+ adapter->pps_uapsd_mode = false;
+ adapter->delay_null_pkt = false;
+ priv->bss_started = 1;
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_uap_bss_stop(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ priv->bss_started = 0;
+
+ return 0;
+}
+
+static int
+nxpwifi_ret_apcmd_sta_list(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *resp,
+ u16 cmdresp_no,
+ void *data_buf)
+{
+ struct host_cmd_ds_sta_list *sta_list =
+ &resp->params.sta_list;
+ struct nxpwifi_ie_types_sta_info *sta_info = (void *)&sta_list->tlv;
+ int i;
+ struct nxpwifi_sta_node *sta_node;
+
+ for (i = 0; i < (le16_to_cpu(sta_list->sta_count)); i++) {
+ sta_node = nxpwifi_get_sta_entry(priv, sta_info->mac);
+ if (unlikely(!sta_node))
+ continue;
+
+ sta_node->stats.rssi = sta_info->rssi;
+ sta_info++;
+ }
+
+ return 0;
+}
+
+/* This function prepares AP specific deauth command with mac supplied in
+ * function parameter.
+ */
+static int nxpwifi_cmd_uap_sta_deauth(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth;
+ u8 *mac = (u8 *)data_buf;
+
+ cmd->command = cpu_to_le16(HOST_CMD_UAP_STA_DEAUTH);
+ memcpy(sta_deauth->mac, mac, ETH_ALEN);
+ sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING);
+
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) +
+ S_DS_GEN);
+ return 0;
+}
+
+static int
+nxpwifi_cmd_uap_chan_report_request(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ return nxpwifi_cmd_issue_chan_report_request(priv, cmd, data_buf);
+}
+
+/* This function prepares AP specific add station command.
+ */
+static int
+nxpwifi_cmd_uap_add_new_station(struct nxpwifi_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_no, void *data_buf,
+ u16 cmd_action, u32 cmd_type)
+{
+ struct host_cmd_ds_add_station *new_sta = &cmd->params.sta_info;
+ struct nxpwifi_sta_info *add_sta = (struct nxpwifi_sta_info *)data_buf;
+ struct station_parameters *params = add_sta->params;
+ struct nxpwifi_sta_node *sta_ptr;
+ u8 *pos, *cmd_end;
+ u16 tlv_len;
+ struct nxpwifi_ie_types_sta_flag *sta_flag;
+ int i;
+
+ cmd->command = cpu_to_le16(HOST_CMD_ADD_NEW_STATION);
+ new_sta->action = cpu_to_le16(cmd_action);
+ cmd->size = sizeof(struct host_cmd_ds_add_station) + S_DS_GEN;
+
+ if (cmd_action == HOST_ACT_ADD_STA)
+ sta_ptr = nxpwifi_add_sta_entry(priv, add_sta->peer_mac);
+ else
+ sta_ptr = nxpwifi_get_sta_entry(priv, add_sta->peer_mac);
+
+ if (!sta_ptr)
+ return -1;
+
+ memcpy(new_sta->peer_mac, add_sta->peer_mac, ETH_ALEN);
+
+ if (cmd_action == HOST_ACT_REMOVE_STA) {
+ cmd->size = cpu_to_le16(cmd->size);
+ return 0;
+ }
+
+ new_sta->aid = cpu_to_le16(params->aid);
+ new_sta->listen_interval = cpu_to_le32(params->listen_interval);
+ new_sta->cap_info = cpu_to_le16(params->capability);
+
+ pos = new_sta->tlv;
+ cmd_end = (u8 *)cmd;
+ cmd_end += (NXPWIFI_SIZE_OF_CMD_BUFFER - 1);
+
+ if (params->sta_flags_set & NL80211_STA_FLAG_WME)
+ sta_ptr->is_wmm_enabled = 1;
+ sta_flag = (struct nxpwifi_ie_types_sta_flag *)pos;
+ sta_flag->header.type = cpu_to_le16(TLV_TYPE_UAP_STA_FLAGS);
+ sta_flag->header.len = cpu_to_le16(sizeof(__le32));
+ sta_flag->sta_flags = cpu_to_le32(params->sta_flags_set);
+ pos += sizeof(struct nxpwifi_ie_types_sta_flag);
+ cmd->size += sizeof(struct nxpwifi_ie_types_sta_flag);
+
+ if (params->ext_capab_len) {
+ u8 *data = (u8 *)params->ext_capab;
+ u16 len = params->ext_capab_len;
+
+ tlv_len = nxpwifi_append_data_tlv(WLAN_EID_EXT_CAPABILITY,
+ data, len, pos, cmd_end);
+ if (!tlv_len)
+ return -1;
+ pos += tlv_len;
+ cmd->size += tlv_len;
+ }
+
+ if (params->link_sta_params.supported_rates_len) {
+ u8 *data = (u8 *)params->link_sta_params.supported_rates;
+ u16 len = params->link_sta_params.supported_rates_len;
+
+ tlv_len = nxpwifi_append_data_tlv(WLAN_EID_SUPP_RATES,
+ data, len, pos, cmd_end);
+ if (!tlv_len)
+ return -1;
+ pos += tlv_len;
+ cmd->size += tlv_len;
+ }
+
+ if (params->uapsd_queues || params->max_sp) {
+ u8 qos_capability = params->uapsd_queues | (params->max_sp << 5);
+ u8 *data = &qos_capability;
+ u16 len = sizeof(u8);
+
+ tlv_len = nxpwifi_append_data_tlv(WLAN_EID_QOS_CAPA,
+ data, len, pos, cmd_end);
+ if (!tlv_len)
+ return -1;
+ pos += tlv_len;
+ cmd->size += tlv_len;
+ sta_ptr->is_wmm_enabled = 1;
+ }
+
+ if (params->link_sta_params.ht_capa) {
+ u8 *data = (u8 *)params->link_sta_params.ht_capa;
+ u16 len = sizeof(struct ieee80211_ht_cap);
+
+ tlv_len = nxpwifi_append_data_tlv(WLAN_EID_HT_CAPABILITY,
+ data, len, pos, cmd_end);
+ if (!tlv_len)
+ return -1;
+ pos += tlv_len;
+ cmd->size += tlv_len;
+ sta_ptr->is_11n_enabled = 1;
+ sta_ptr->max_amsdu =
+ le16_to_cpu(params->link_sta_params.ht_capa->cap_info) &
+ IEEE80211_HT_CAP_MAX_AMSDU ?
+ NXPWIFI_TX_DATA_BUF_SIZE_8K :
+ NXPWIFI_TX_DATA_BUF_SIZE_4K;
+ }
+
+ if (params->link_sta_params.vht_capa) {
+ u8 *data = (u8 *)params->link_sta_params.vht_capa;
+ u16 len = sizeof(struct ieee80211_vht_cap);
+
+ tlv_len = nxpwifi_append_data_tlv(WLAN_EID_VHT_CAPABILITY,
+ data, len, pos, cmd_end);
+ if (!tlv_len)
+ return -1;
+ pos += tlv_len;
+ cmd->size += tlv_len;
+ sta_ptr->is_11ac_enabled = 1;
+ }
+
+ if (params->link_sta_params.opmode_notif_used) {
+ u8 *data = ¶ms->link_sta_params.opmode_notif;
+ u16 len = sizeof(u8);
+
+ tlv_len = nxpwifi_append_data_tlv(WLAN_EID_OPMODE_NOTIF,
+ data, len, pos, cmd_end);
+ if (!tlv_len)
+ return -1;
+ pos += tlv_len;
+ cmd->size += tlv_len;
+ }
+
+ for (i = 0; i < MAX_NUM_TID; i++) {
+ if (sta_ptr->is_11n_enabled)
+ sta_ptr->ampdu_sta[i] =
+ priv->aggr_prio_tbl[i].ampdu_user;
+ else
+ sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED;
+ }
+
+ memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq));
+
+ cmd->size = cpu_to_le16(cmd->size);
+
+ return 0;
+}
+
+static const struct nxpwifi_cmd_entry cmd_table_uap[] = {
+ {.cmd_no = HOST_CMD_APCMD_SYS_RESET,
+ .prepare_cmd = nxpwifi_cmd_fill_head_only,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_UAP_SYS_CONFIG,
+ .prepare_cmd = nxpwifi_cmd_uap_sys_config,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_UAP_BSS_START,
+ .prepare_cmd = nxpwifi_cmd_uap_bss_start,
+ .cmd_resp = nxpwifi_ret_uap_bss_start},
+ {.cmd_no = HOST_CMD_UAP_BSS_STOP,
+ .prepare_cmd = nxpwifi_cmd_fill_head_only,
+ .cmd_resp = nxpwifi_ret_uap_bss_stop},
+ {.cmd_no = HOST_CMD_APCMD_STA_LIST,
+ .prepare_cmd = nxpwifi_cmd_fill_head_only,
+ .cmd_resp = nxpwifi_ret_apcmd_sta_list},
+ {.cmd_no = HOST_CMD_UAP_STA_DEAUTH,
+ .prepare_cmd = nxpwifi_cmd_uap_sta_deauth,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_CHAN_REPORT_REQUEST,
+ .prepare_cmd = nxpwifi_cmd_uap_chan_report_request,
+ .cmd_resp = NULL},
+ {.cmd_no = HOST_CMD_ADD_NEW_STATION,
+ .prepare_cmd = nxpwifi_cmd_uap_add_new_station,
+ .cmd_resp = NULL},
+};
+
+/* This function prepares the AP specific commands before sending them
+ * to the firmware.
+ * This is a generic function which calls specific command preparation
+ * routines based upon the command number.
+ */
+int nxpwifi_uap_prepare_cmd(struct nxpwifi_private *priv,
+ struct cmd_ctrl_node *cmd_node,
+ u16 cmd_action, u32 type)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u16 cmd_no = cmd_node->cmd_no;
+ struct host_cmd_ds_command *cmd =
+ (struct host_cmd_ds_command *)cmd_node->skb->data;
+ void *data_buf = cmd_node->data_buf;
+ int i, ret = -1;
+
+ for (i = 0; i < ARRAY_SIZE(cmd_table_uap); i++) {
+ if (cmd_no == cmd_table_uap[i].cmd_no) {
+ if (cmd_table_uap[i].prepare_cmd)
+ ret = cmd_table_uap[i].prepare_cmd(priv, cmd,
+ cmd_no,
+ data_buf,
+ cmd_action,
+ type);
+ cmd_node->cmd_resp = cmd_table_uap[i].cmd_resp;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(cmd_table_uap))
+ nxpwifi_dbg(adapter, ERROR,
+ "%s: unknown command: %#x\n",
+ __func__, cmd_no);
+ else
+ nxpwifi_dbg(adapter, EVENT,
+ "%s: command: %#x\n",
+ __func__, cmd_no);
+
+ return ret;
+}
+
+/* This function parses security related parameters from cfg80211_ap_settings
+ * and sets into FW understandable bss_config structure.
+ */
+int nxpwifi_set_secure_params(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_config,
+ struct cfg80211_ap_settings *params)
+{
+ int i;
+ struct nxpwifi_wep_key wep_key;
+
+ if (!params->privacy) {
+ bss_config->protocol = PROTOCOL_NO_SECURITY;
+ bss_config->key_mgmt = KEY_MGMT_NONE;
+ bss_config->wpa_cfg.length = 0;
+ priv->sec_info.wep_enabled = 0;
+ priv->sec_info.wpa_enabled = 0;
+ priv->sec_info.wpa2_enabled = 0;
+
+ return 0;
+ }
+
+ switch (params->auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ bss_config->auth_mode = WLAN_AUTH_OPEN;
+ break;
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ bss_config->auth_mode = WLAN_AUTH_SHARED_KEY;
+ break;
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ bss_config->auth_mode = WLAN_AUTH_LEAP;
+ break;
+ default:
+ bss_config->auth_mode = NXPWIFI_AUTH_MODE_AUTO;
+ break;
+ }
+
+ bss_config->key_mgmt_operation |= KEY_MGMT_ON_HOST;
+
+ for (i = 0; i < params->crypto.n_akm_suites; i++) {
+ switch (params->crypto.akm_suites[i]) {
+ case WLAN_AKM_SUITE_8021X:
+ if (params->crypto.wpa_versions &
+ NL80211_WPA_VERSION_1) {
+ bss_config->protocol = PROTOCOL_WPA;
+ bss_config->key_mgmt = KEY_MGMT_EAP;
+ }
+ if (params->crypto.wpa_versions &
+ NL80211_WPA_VERSION_2) {
+ bss_config->protocol |= PROTOCOL_WPA2;
+ bss_config->key_mgmt = KEY_MGMT_EAP;
+ }
+ break;
+ case WLAN_AKM_SUITE_PSK:
+ if (params->crypto.wpa_versions &
+ NL80211_WPA_VERSION_1) {
+ bss_config->protocol = PROTOCOL_WPA;
+ bss_config->key_mgmt = KEY_MGMT_PSK;
+ }
+ if (params->crypto.wpa_versions &
+ NL80211_WPA_VERSION_2) {
+ bss_config->protocol |= PROTOCOL_WPA2;
+ bss_config->key_mgmt = KEY_MGMT_PSK;
+ }
+ break;
+ case WLAN_AKM_SUITE_SAE:
+ bss_config->protocol = PROTOCOL_WPA2;
+ bss_config->key_mgmt = KEY_MGMT_SAE;
+ break;
+ case WLAN_AKM_SUITE_OWE:
+ bss_config->protocol = PROTOCOL_WPA2;
+ bss_config->key_mgmt = KEY_MGMT_OWE;
+ break;
+ default:
+ break;
+ }
+ }
+ for (i = 0; i < params->crypto.n_ciphers_pairwise; i++) {
+ switch (params->crypto.ciphers_pairwise[i]) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ bss_config->wpa_cfg.pairwise_cipher_wpa |=
+ CIPHER_TKIP;
+ if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ bss_config->wpa_cfg.pairwise_cipher_wpa2 |=
+ CIPHER_TKIP;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+ bss_config->wpa_cfg.pairwise_cipher_wpa |=
+ CIPHER_AES_CCMP;
+ if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+ bss_config->wpa_cfg.pairwise_cipher_wpa2 |=
+ CIPHER_AES_CCMP;
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (params->crypto.cipher_group) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (priv->sec_info.wep_enabled) {
+ bss_config->protocol = PROTOCOL_STATIC_WEP;
+ bss_config->key_mgmt = KEY_MGMT_NONE;
+ bss_config->wpa_cfg.length = 0;
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ wep_key = priv->wep_key[i];
+ bss_config->wep_cfg[i].key_index = i;
+
+ if (priv->wep_key_curr_index == i)
+ bss_config->wep_cfg[i].is_default = 1;
+ else
+ bss_config->wep_cfg[i].is_default = 0;
+
+ bss_config->wep_cfg[i].length =
+ wep_key.key_length;
+ memcpy(&bss_config->wep_cfg[i].key,
+ &wep_key.key_material,
+ wep_key.key_length);
+ }
+ }
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ bss_config->wpa_cfg.group_cipher = CIPHER_TKIP;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ bss_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* This function updates 11n related parameters from IE and sets them into
+ * bss_config structure.
+ */
+void
+nxpwifi_set_ht_params(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params)
+{
+ const u8 *ht_ie;
+
+ if (!ISSUPP_11NENABLED(priv->adapter->fw_cap_info))
+ return;
+
+ ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, params->beacon.tail,
+ params->beacon.tail_len);
+ if (ht_ie) {
+ memcpy(&bss_cfg->ht_cap, ht_ie + 2,
+ sizeof(struct ieee80211_ht_cap));
+ if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap))
+ bss_cfg->ht_cap.tx_BF_cap_info =
+ cpu_to_le32(NXPWIFI_DEF_11N_TX_BF_CAP);
+ priv->ap_11n_enabled = 1;
+ } else {
+ memset(&bss_cfg->ht_cap, 0, sizeof(struct ieee80211_ht_cap));
+ bss_cfg->ht_cap.cap_info = cpu_to_le16(NXPWIFI_DEF_HT_CAP);
+ bss_cfg->ht_cap.ampdu_params_info = NXPWIFI_DEF_AMPDU;
+ }
+}
+
+/* This function updates 11ac related parameters from IE
+ * and sets them into bss_config structure.
+ */
+void nxpwifi_set_vht_params(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params)
+{
+ const u8 *vht_ie;
+
+ vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, params->beacon.tail,
+ params->beacon.tail_len);
+ if (vht_ie) {
+ memcpy(&bss_cfg->vht_cap, vht_ie + 2,
+ sizeof(struct ieee80211_vht_cap));
+ priv->ap_11ac_enabled = 1;
+ } else {
+ priv->ap_11ac_enabled = 0;
+ }
+}
+
+/* This function updates 11ac related parameters from IE
+ * and sets them into bss_config structure.
+ */
+void nxpwifi_set_tpc_params(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params)
+{
+ const u8 *tpc_ie;
+
+ tpc_ie = cfg80211_find_ie(WLAN_EID_TPC_REQUEST, params->beacon.tail,
+ params->beacon.tail_len);
+ if (tpc_ie)
+ bss_cfg->power_constraint = *(tpc_ie + 2);
+ else
+ bss_cfg->power_constraint = 0;
+}
+
+/* Enable VHT only when cfg80211_ap_settings has VHT IE.
+ * Otherwise disable VHT.
+ */
+void nxpwifi_set_vht_width(struct nxpwifi_private *priv,
+ enum nl80211_chan_width width,
+ bool ap_11ac_enable)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_11ac_vht_cfg vht_cfg;
+
+ vht_cfg.band_config = VHT_CFG_5GHZ;
+ vht_cfg.cap_info = adapter->hw_dot_11ac_dev_cap;
+
+ if (!ap_11ac_enable) {
+ vht_cfg.mcs_tx_set = DISABLE_VHT_MCS_SET;
+ vht_cfg.mcs_rx_set = DISABLE_VHT_MCS_SET;
+ } else {
+ vht_cfg.mcs_tx_set = DEFAULT_VHT_MCS_SET;
+ vht_cfg.mcs_rx_set = DEFAULT_VHT_MCS_SET;
+ }
+
+ vht_cfg.misc_config = VHT_CAP_UAP_ONLY;
+
+ if (ap_11ac_enable && width >= NL80211_CHAN_WIDTH_80)
+ vht_cfg.misc_config |= VHT_BW_80_160_80P80;
+
+ nxpwifi_send_cmd(priv, HOST_CMD_11AC_CFG,
+ HOST_ACT_GEN_SET, 0, &vht_cfg, true);
+}
+
+/* This function finds supported rates IE from beacon parameter and sets
+ * these rates into bss_config structure.
+ */
+void
+nxpwifi_set_uap_rates(struct nxpwifi_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params)
+{
+ struct ieee_types_header *rate_ie;
+ int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ const u8 *var_pos = params->beacon.head + var_offset;
+ int len = params->beacon.head_len - var_offset;
+ u8 rate_len = 0;
+
+ rate_ie = (void *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
+ if (rate_ie) {
+ if (rate_ie->len > NXPWIFI_SUPPORTED_RATES)
+ return;
+ memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->len);
+ rate_len = rate_ie->len;
+ }
+
+ rate_ie = (void *)cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES,
+ params->beacon.tail,
+ params->beacon.tail_len);
+ if (rate_ie) {
+ if (rate_ie->len > NXPWIFI_SUPPORTED_RATES - rate_len)
+ return;
+ memcpy(bss_cfg->rates + rate_len, rate_ie + 1, rate_ie->len);
+ }
+}
+
+/* This function initializes some of nxpwifi_uap_bss_param variables.
+ * This helps FW in ignoring invalid values. These values may or may not
+ * be get updated to valid ones at later stage.
+ */
+void nxpwifi_set_sys_config_invalid_data(struct nxpwifi_uap_bss_param *config)
+{
+ config->bcast_ssid_ctl = 0x7F;
+ config->radio_ctl = 0x7F;
+ config->dtim_period = 0x7F;
+ config->beacon_period = 0x7FFF;
+ config->auth_mode = 0x7F;
+ config->rts_threshold = 0x7FFF;
+ config->frag_threshold = 0x7FFF;
+ config->retry_limit = 0x7F;
+ config->qos_info = 0xFF;
+}
+
+/* This function parses WMM related parameters from cfg80211_ap_settings
+ * structure and updates bss_config structure.
+ */
+void
+nxpwifi_set_wmm_params(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params)
+{
+ const u8 *vendor_ie;
+ const u8 *wmm_ie;
+ static const u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02};
+
+ vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+ WLAN_OUI_TYPE_MICROSOFT_WMM,
+ params->beacon.tail,
+ params->beacon.tail_len);
+ if (vendor_ie) {
+ wmm_ie = vendor_ie;
+ if (*(wmm_ie + 1) > sizeof(struct nxpwifi_types_wmm_info))
+ return;
+ memcpy(&bss_cfg->wmm_info, wmm_ie +
+ sizeof(struct ieee_types_header), *(wmm_ie + 1));
+ priv->wmm_enabled = 1;
+ } else {
+ memset(&bss_cfg->wmm_info, 0, sizeof(bss_cfg->wmm_info));
+ memcpy(&bss_cfg->wmm_info.oui, wmm_oui, sizeof(wmm_oui));
+ bss_cfg->wmm_info.subtype = NXPWIFI_WMM_SUBTYPE;
+ bss_cfg->wmm_info.version = NXPWIFI_WMM_VERSION;
+ priv->wmm_enabled = 0;
+ }
+
+ bss_cfg->qos_info = 0x00;
+}
+
+/* This function enable 11D if userspace set the country IE.
+ */
+void nxpwifi_config_uap_11d(struct nxpwifi_private *priv,
+ struct cfg80211_beacon_data *beacon_data)
+{
+ enum state_11d_t state_11d;
+ const u8 *country_ie;
+
+ country_ie = cfg80211_find_ie(WLAN_EID_COUNTRY, beacon_data->tail,
+ beacon_data->tail_len);
+ if (country_ie) {
+ /* Send cmd to FW to enable 11D function */
+ state_11d = ENABLE_11D;
+ if (nxpwifi_send_cmd(priv, HOST_CMD_802_11_SNMP_MIB,
+ HOST_ACT_GEN_SET, DOT11D_I,
+ &state_11d, true)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "11D: failed to enable 11D\n");
+ }
+ }
+}
+
+void nxpwifi_uap_set_channel(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_cfg,
+ struct cfg80211_chan_def chandef)
+{
+ u8 config_bands = 0, old_bands = priv->adapter->config_bands;
+
+ priv->bss_chandef = chandef;
+
+ bss_cfg->channel =
+ ieee80211_frequency_to_channel(chandef.chan->center_freq);
+
+ /* Set appropriate bands */
+ if (chandef.chan->band == NL80211_BAND_2GHZ) {
+ bss_cfg->band_cfg = BAND_CONFIG_BG;
+ config_bands = BAND_B | BAND_G;
+
+ if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
+ config_bands |= BAND_GN;
+ } else {
+ bss_cfg->band_cfg = BAND_CONFIG_A;
+ config_bands = BAND_A;
+
+ if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT)
+ config_bands |= BAND_AN;
+
+ if (chandef.width > NL80211_CHAN_WIDTH_40)
+ config_bands |= BAND_AAC;
+ }
+
+ switch (chandef.width) {
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ if (chandef.center_freq1 < chandef.chan->center_freq)
+ bss_cfg->band_cfg |= NXPWIFI_SEC_CHAN_BELOW;
+ else
+ bss_cfg->band_cfg |= NXPWIFI_SEC_CHAN_ABOVE;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_160:
+ bss_cfg->band_cfg |=
+ nxpwifi_get_sec_chan_offset(bss_cfg->channel) << 4;
+ break;
+ default:
+ nxpwifi_dbg(priv->adapter,
+ WARN, "Unknown channel width: %d\n",
+ chandef.width);
+ break;
+ }
+
+ priv->adapter->config_bands = config_bands;
+
+ if (old_bands != config_bands) {
+ nxpwifi_send_domain_info_cmd_fw(priv->adapter->wiphy);
+ nxpwifi_dnld_txpwr_table(priv);
+ }
+}
+
+int nxpwifi_config_start_uap(struct nxpwifi_private *priv,
+ struct nxpwifi_uap_bss_param *bss_cfg)
+{
+ if (nxpwifi_send_cmd(priv, HOST_CMD_UAP_SYS_CONFIG,
+ HOST_ACT_GEN_SET,
+ UAP_BSS_PARAMS_I, bss_cfg, true)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Failed to set AP configuration\n");
+ return -1;
+ }
+
+ if (nxpwifi_send_cmd(priv, HOST_CMD_UAP_BSS_START,
+ HOST_ACT_GEN_SET, 0, NULL, true)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Failed to start the BSS\n");
+ return -1;
+ }
+
+ if (priv->sec_info.wep_enabled)
+ priv->curr_pkt_filter |= HOST_ACT_MAC_WEP_ENABLE;
+ else
+ priv->curr_pkt_filter &= ~HOST_ACT_MAC_WEP_ENABLE;
+
+ if (nxpwifi_send_cmd(priv, HOST_CMD_MAC_CONTROL,
+ HOST_ACT_GEN_SET, 0,
+ &priv->curr_pkt_filter, true))
+ return -1;
+
+ return 0;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 35/43] wifi: nxpwifi: add uap_event.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (33 preceding siblings ...)
2024-06-21 7:51 ` [PATCH 34/43] wifi: nxpwifi: add uap_cmd.c David Lin
@ 2024-06-21 7:52 ` David Lin
2024-06-21 7:52 ` [PATCH 36/43] wifi: nxpwifi: add uap_txrx.c David Lin
` (9 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:52 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/uap_event.c | 483 +++++++++++++++++++
1 file changed, 483 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/uap_event.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/uap_event.c b/drivers/net/wireless/nxp/nxpwifi/uap_event.c
new file mode 100644
index 000000000000..2c15b01ec018
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/uap_event.c
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: AP event handling
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "main.h"
+#include "cmdevt.h"
+#include "11n.h"
+
+#define NXPWIFI_BSS_START_EVT_FIX_SIZE 12
+
+static int
+nxpwifi_uap_event_ps_awake(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (!adapter->pps_uapsd_mode &&
+ priv->media_connected && adapter->sleep_period.period) {
+ adapter->pps_uapsd_mode = true;
+ nxpwifi_dbg(adapter, EVENT,
+ "event: PPS/UAPSD mode activated\n");
+ }
+ adapter->tx_lock_flag = false;
+ if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) {
+ if (nxpwifi_check_last_packet_indication(priv)) {
+ if (adapter->data_sent ||
+ (adapter->if_ops.is_port_ready &&
+ !adapter->if_ops.is_port_ready(priv))) {
+ adapter->ps_state = PS_STATE_AWAKE;
+ adapter->pm_wakeup_card_req = false;
+ adapter->pm_wakeup_fw_try = false;
+ } else {
+ if (!nxpwifi_send_null_packet
+ (priv,
+ NXPWIFI_TxPD_POWER_MGMT_NULL_PACKET |
+ NXPWIFI_TxPD_POWER_MGMT_LAST_PACKET))
+ adapter->ps_state = PS_STATE_SLEEP;
+ }
+
+ return 0;
+ }
+ }
+
+ adapter->ps_state = PS_STATE_AWAKE;
+ adapter->pm_wakeup_card_req = false;
+ adapter->pm_wakeup_fw_try = false;
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_ps_sleep(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ adapter->ps_state = PS_STATE_PRE_SLEEP;
+ nxpwifi_check_ps_cond(adapter);
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_sta_deauth(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u8 *deauth_mac;
+
+ deauth_mac = adapter->event_body +
+ NXPWIFI_UAP_EVENT_EXTRA_HEADER;
+ cfg80211_del_sta(priv->netdev, deauth_mac, GFP_KERNEL);
+
+ if (priv->ap_11n_enabled) {
+ nxpwifi_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac);
+ nxpwifi_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac);
+ }
+ nxpwifi_wmm_del_peer_ra_list(priv, deauth_mac);
+ nxpwifi_del_sta_entry(priv, deauth_mac);
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_sta_assoc(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct station_info *sinfo;
+ struct nxpwifi_assoc_event *event;
+ struct nxpwifi_sta_node *node;
+ int len, i;
+
+ sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
+ if (!sinfo)
+ return -ENOMEM;
+
+ event = (struct nxpwifi_assoc_event *)
+ (adapter->event_body + NXPWIFI_UAP_EVENT_EXTRA_HEADER);
+ if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) {
+ len = -1;
+
+ if (ieee80211_is_assoc_req(event->frame_control))
+ len = 0;
+ else if (ieee80211_is_reassoc_req(event->frame_control))
+ /* There will be ETH_ALEN bytes of
+ * current_ap_addr before the re-assoc ies.
+ */
+ len = ETH_ALEN;
+
+ if (len != -1) {
+ sinfo->assoc_req_ies = &event->data[len];
+ len = (u8 *)sinfo->assoc_req_ies -
+ (u8 *)&event->frame_control;
+ sinfo->assoc_req_ies_len =
+ le16_to_cpu(event->len) - (u16)len;
+ }
+ }
+ cfg80211_new_sta(priv->netdev, event->sta_addr, sinfo,
+ GFP_KERNEL);
+
+ node = nxpwifi_add_sta_entry(priv, event->sta_addr);
+ if (!node) {
+ nxpwifi_dbg(adapter, ERROR,
+ "could not create station entry!\n");
+ kfree(sinfo);
+ return -1;
+ }
+
+ if (!priv->ap_11n_enabled) {
+ kfree(sinfo);
+ return 0;
+ }
+
+ nxpwifi_set_sta_ht_cap(priv, sinfo->assoc_req_ies,
+ sinfo->assoc_req_ies_len, node);
+
+ for (i = 0; i < MAX_NUM_TID; i++) {
+ if (node->is_11n_enabled)
+ node->ampdu_sta[i] =
+ priv->aggr_prio_tbl[i].ampdu_user;
+ else
+ node->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED;
+ }
+ memset(node->rx_seq, 0xff, sizeof(node->rx_seq));
+ kfree(sinfo);
+
+ return 0;
+}
+
+static int
+nxpwifi_check_uap_capabilities(struct nxpwifi_private *priv,
+ struct sk_buff *event)
+{
+ int evt_len;
+ u8 *curr;
+ u16 tlv_len;
+ struct nxpwifi_ie_types_data *tlv_hdr;
+ struct ieee_types_wmm_parameter *wmm_param_ie = NULL;
+ int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK;
+
+ priv->wmm_enabled = false;
+ skb_pull(event, NXPWIFI_BSS_START_EVT_FIX_SIZE);
+ evt_len = event->len;
+ curr = event->data;
+
+ nxpwifi_dbg_dump(priv->adapter, EVT_D, "uap capabilities:",
+ event->data, event->len);
+
+ skb_push(event, NXPWIFI_BSS_START_EVT_FIX_SIZE);
+
+ while ((evt_len >= sizeof(tlv_hdr->header))) {
+ tlv_hdr = (struct nxpwifi_ie_types_data *)curr;
+ tlv_len = le16_to_cpu(tlv_hdr->header.len);
+
+ if (evt_len < tlv_len + sizeof(tlv_hdr->header))
+ break;
+
+ switch (le16_to_cpu(tlv_hdr->header.type)) {
+ case WLAN_EID_HT_CAPABILITY:
+ priv->ap_11n_enabled = true;
+ break;
+
+ case WLAN_EID_VHT_CAPABILITY:
+ priv->ap_11ac_enabled = true;
+ break;
+
+ case WLAN_EID_VENDOR_SPECIFIC:
+ /* Point the regular IEEE IE 2 bytes into the NXP IE
+ * and setup the IEEE IE type and length byte fields
+ */
+ wmm_param_ie = (void *)(curr + 2);
+ wmm_param_ie->vend_hdr.len = (u8)tlv_len;
+ wmm_param_ie->vend_hdr.element_id =
+ WLAN_EID_VENDOR_SPECIFIC;
+ nxpwifi_dbg(priv->adapter, EVENT,
+ "info: check uap capabilities:\t"
+ "wmm parameter set count: %d\n",
+ wmm_param_ie->qos_info_bitmap & mask);
+
+ nxpwifi_wmm_setup_ac_downgrade(priv);
+ priv->wmm_enabled = true;
+ nxpwifi_wmm_setup_queue_priorities(priv, wmm_param_ie);
+ break;
+
+ default:
+ break;
+ }
+
+ curr += (tlv_len + sizeof(tlv_hdr->header));
+ evt_len -= (tlv_len + sizeof(tlv_hdr->header));
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_bss_start(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ priv->port_open = false;
+ eth_hw_addr_set(priv->netdev, adapter->event_body + 2);
+ if (priv->hist_data)
+ nxpwifi_hist_data_reset(priv);
+ return nxpwifi_check_uap_capabilities(priv, adapter->event_skb);
+}
+
+static int
+nxpwifi_uap_event_addba(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (priv->media_connected)
+ nxpwifi_send_cmd(priv, HOST_CMD_11N_ADDBA_RSP,
+ HOST_ACT_GEN_SET, 0,
+ adapter->event_body, false);
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_delba(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ if (priv->media_connected)
+ nxpwifi_11n_delete_ba_stream(priv, adapter->event_body);
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_ba_stream_timeout(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct host_cmd_ds_11n_batimeout *ba_timeout;
+
+ if (priv->media_connected) {
+ ba_timeout = (void *)adapter->event_body;
+ nxpwifi_11n_ba_stream_timeout(priv, ba_timeout);
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_amsdu_aggr_ctrl(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u16 ctrl;
+
+ ctrl = get_unaligned_le16(adapter->event_body);
+ nxpwifi_dbg(adapter, EVENT,
+ "event: AMSDU_AGGR_CTRL %d\n", ctrl);
+
+ if (priv->media_connected) {
+ adapter->tx_buf_size =
+ min_t(u16, adapter->curr_tx_buf_size, ctrl);
+ nxpwifi_dbg(adapter, EVENT,
+ "event: tx_buf_size %d\n",
+ adapter->tx_buf_size);
+ }
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_bss_idle(struct nxpwifi_private *priv)
+{
+ priv->media_connected = false;
+ priv->port_open = false;
+ nxpwifi_clean_txrx(priv);
+ nxpwifi_del_all_sta_list(priv);
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_bss_active(struct nxpwifi_private *priv)
+{
+ priv->media_connected = true;
+ priv->port_open = true;
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_mic_countermeasures(struct nxpwifi_private *priv)
+{
+ /* For future development */
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_radar_detected(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ return nxpwifi_11h_handle_radar_detected(priv, adapter->event_skb);
+}
+
+static int
+nxpwifi_uap_event_channel_report_rdy(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ return nxpwifi_11h_handle_chanrpt_ready(priv, adapter->event_skb);
+}
+
+static int
+nxpwifi_uap_event_tx_data_pause(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ nxpwifi_process_tx_pause_event(priv, adapter->event_skb);
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_ext_scan_report(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ void *buf = adapter->event_skb->data;
+ int ret = 0;
+
+ if (adapter->ext_scan)
+ ret = nxpwifi_handle_event_ext_scan_report(priv, buf);
+
+ return ret;
+}
+
+static int
+nxpwifi_uap_event_rxba_sync(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ nxpwifi_11n_rxba_sync_event(priv, adapter->event_body,
+ adapter->event_skb->len -
+ sizeof(adapter->event_cause));
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_remain_on_chan_expired(struct nxpwifi_private *priv)
+{
+ cfg80211_remain_on_channel_expired(&priv->wdev,
+ priv->roc_cfg.cookie,
+ &priv->roc_cfg.chan,
+ GFP_ATOMIC);
+ memset(&priv->roc_cfg, 0x00, sizeof(struct nxpwifi_roc_cfg));
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_multi_chan_info(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ nxpwifi_process_multi_chan_event(priv, adapter->event_skb);
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_tx_status_report(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ nxpwifi_parse_tx_status_event(priv, adapter->event_body);
+
+ return 0;
+}
+
+static int
+nxpwifi_uap_event_bt_coex_wlan_para_change(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ nxpwifi_bt_coex_wlan_param_update_event(priv, adapter->event_skb);
+
+ return 0;
+}
+
+static const struct nxpwifi_evt_entry evt_table_uap[] = {
+ {.event_cause = EVENT_PS_AWAKE,
+ .event_handler = nxpwifi_uap_event_ps_awake},
+ {.event_cause = EVENT_PS_SLEEP,
+ .event_handler = nxpwifi_uap_event_ps_sleep},
+ {.event_cause = EVENT_UAP_STA_DEAUTH,
+ .event_handler = nxpwifi_uap_event_sta_deauth},
+ {.event_cause = EVENT_UAP_STA_ASSOC,
+ .event_handler = nxpwifi_uap_event_sta_assoc},
+ {.event_cause = EVENT_UAP_BSS_START,
+ .event_handler = nxpwifi_uap_event_bss_start},
+ {.event_cause = EVENT_ADDBA,
+ .event_handler = nxpwifi_uap_event_addba},
+ {.event_cause = EVENT_DELBA,
+ .event_handler = nxpwifi_uap_event_delba},
+ {.event_cause = EVENT_BA_STREAM_TIEMOUT,
+ .event_handler = nxpwifi_uap_event_ba_stream_timeout},
+ {.event_cause = EVENT_AMSDU_AGGR_CTRL,
+ .event_handler = nxpwifi_uap_event_amsdu_aggr_ctrl},
+ {.event_cause = EVENT_UAP_BSS_IDLE,
+ .event_handler = nxpwifi_uap_event_bss_idle},
+ {.event_cause = EVENT_UAP_BSS_ACTIVE,
+ .event_handler = nxpwifi_uap_event_bss_active},
+ {.event_cause = EVENT_UAP_MIC_COUNTERMEASURES,
+ .event_handler = nxpwifi_uap_event_mic_countermeasures},
+ {.event_cause = EVENT_RADAR_DETECTED,
+ .event_handler = nxpwifi_uap_event_radar_detected},
+ {.event_cause = EVENT_CHANNEL_REPORT_RDY,
+ .event_handler = nxpwifi_uap_event_channel_report_rdy},
+ {.event_cause = EVENT_TX_DATA_PAUSE,
+ .event_handler = nxpwifi_uap_event_tx_data_pause},
+ {.event_cause = EVENT_EXT_SCAN_REPORT,
+ .event_handler = nxpwifi_uap_event_ext_scan_report},
+ {.event_cause = EVENT_RXBA_SYNC,
+ .event_handler = nxpwifi_uap_event_rxba_sync},
+ {.event_cause = EVENT_REMAIN_ON_CHAN_EXPIRED,
+ .event_handler = nxpwifi_uap_event_remain_on_chan_expired},
+ {.event_cause = EVENT_MULTI_CHAN_INFO,
+ .event_handler = nxpwifi_uap_event_multi_chan_info},
+ {.event_cause = EVENT_TX_STATUS_REPORT,
+ .event_handler = nxpwifi_uap_event_tx_status_report},
+ {.event_cause = EVENT_BT_COEX_WLAN_PARA_CHANGE,
+ .event_handler = nxpwifi_uap_event_bt_coex_wlan_para_change},
+};
+
+/* This function handles AP interface specific events generated by firmware.
+ *
+ * Event specific routines are called by this function based
+ * upon the generated event cause.
+ */
+int nxpwifi_process_uap_event(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u32 eventcause = adapter->event_cause;
+ int evt, ret = 0;
+
+ for (evt = 0; evt < ARRAY_SIZE(evt_table_uap); evt++) {
+ if (eventcause == evt_table_uap[evt].event_cause) {
+ if (evt_table_uap[evt].event_handler)
+ ret = evt_table_uap[evt].event_handler(priv);
+ break;
+ }
+ }
+
+ if (evt == ARRAY_SIZE(evt_table_uap))
+ nxpwifi_dbg(adapter, EVENT,
+ "%s: unknown event id: %#x\n",
+ __func__, eventcause);
+ else
+ nxpwifi_dbg(adapter, EVENT,
+ "%s: event id: %#x\n",
+ __func__, eventcause);
+
+ return ret;
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 36/43] wifi: nxpwifi: add uap_txrx.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (34 preceding siblings ...)
2024-06-21 7:52 ` [PATCH 35/43] wifi: nxpwifi: add uap_event.c David Lin
@ 2024-06-21 7:52 ` David Lin
2024-06-21 7:52 ` [PATCH 37/43] wifi: nxpwifi: add util.c David Lin
` (8 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:52 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/uap_txrx.c | 498 ++++++++++++++++++++
1 file changed, 498 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/uap_txrx.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/uap_txrx.c b/drivers/net/wireless/nxp/nxpwifi/uap_txrx.c
new file mode 100644
index 000000000000..055d59cb942f
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/uap_txrx.c
@@ -0,0 +1,498 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: AP TX and RX data handling
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n_aggr.h"
+#include "11n_rxreorder.h"
+
+/* This function checks if particular RA list has packets more than low bridge
+ * packet threshold and then deletes packet from this RA list.
+ * Function deletes packets from such RA list and returns true. If no such list
+ * is found, false is returned.
+ */
+static bool
+nxpwifi_uap_del_tx_pkts_in_ralist(struct nxpwifi_private *priv,
+ struct list_head *ra_list_head,
+ int tid)
+{
+ struct nxpwifi_ra_list_tbl *ra_list;
+ struct sk_buff *skb, *tmp;
+ bool pkt_deleted = false;
+ struct nxpwifi_txinfo *tx_info;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+
+ list_for_each_entry(ra_list, ra_list_head, list) {
+ if (skb_queue_empty(&ra_list->skb_head))
+ continue;
+
+ skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) {
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+ if (tx_info->flags & NXPWIFI_BUF_FLAG_BRIDGED_PKT) {
+ __skb_unlink(skb, &ra_list->skb_head);
+ nxpwifi_write_data_complete(adapter, skb, 0,
+ -1);
+ if (ra_list->tx_paused)
+ priv->wmm.pkts_paused[tid]--;
+ else
+ atomic_dec(&priv->wmm.tx_pkts_queued);
+ pkt_deleted = true;
+ }
+ if ((atomic_read(&adapter->pending_bridged_pkts) <=
+ NXPWIFI_BRIDGED_PKTS_THR_LOW))
+ break;
+ }
+ }
+
+ return pkt_deleted;
+}
+
+/* This function deletes packets from particular RA List. RA list index
+ * from which packets are deleted is preserved so that packets from next RA
+ * list are deleted upon subsequent call thus maintaining fairness.
+ */
+static void nxpwifi_uap_cleanup_tx_queues(struct nxpwifi_private *priv)
+{
+ struct list_head *ra_list;
+ int i;
+
+ spin_lock_bh(&priv->wmm.ra_list_spinlock);
+
+ for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) {
+ if (priv->del_list_idx == MAX_NUM_TID)
+ priv->del_list_idx = 0;
+ ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list;
+ if (nxpwifi_uap_del_tx_pkts_in_ralist(priv, ra_list, i)) {
+ priv->del_list_idx++;
+ break;
+ }
+ }
+
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+}
+
+static void
+nxpwifi_uap_queue_bridged_pkt(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct uap_rxpd *uap_rx_pd;
+ struct rx_packet_hdr *rx_pkt_hdr;
+ struct sk_buff *new_skb;
+ struct nxpwifi_txinfo *tx_info;
+ int hdr_chop;
+ struct ethhdr *p_ethhdr;
+ struct nxpwifi_sta_node *src_node;
+ int index;
+
+ uap_rx_pd = (struct uap_rxpd *)(skb->data);
+ rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
+
+ if ((atomic_read(&adapter->pending_bridged_pkts) >=
+ NXPWIFI_BRIDGED_PKTS_THR_HIGH)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Tx: Bridge packet limit reached. Drop packet!\n");
+ kfree_skb(skb);
+ nxpwifi_uap_cleanup_tx_queues(priv);
+ return;
+ }
+
+ if (sizeof(*rx_pkt_hdr) +
+ le16_to_cpu(uap_rx_pd->rx_pkt_offset) > skb->len) {
+ priv->stats.rx_dropped++;
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header,
+ sizeof(bridge_tunnel_header))) ||
+ (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header,
+ sizeof(rfc1042_header)) &&
+ rx_pkt_hdr->rfc1042_hdr.snap_type != htons(ETH_P_AARP) &&
+ rx_pkt_hdr->rfc1042_hdr.snap_type != htons(ETH_P_IPX))) {
+ /* Replace the 803 header and rfc1042 header (llc/snap) with
+ * an Ethernet II header, keep the src/dst and snap_type
+ * (ethertype).
+ *
+ * The firmware only passes up SNAP frames converting all RX
+ * data from 802.11 to 802.2/LLC/SNAP frames.
+ *
+ * To create the Ethernet II, just move the src, dst address
+ * right before the snap_type.
+ */
+ p_ethhdr = (struct ethhdr *)
+ ((u8 *)(&rx_pkt_hdr->eth803_hdr)
+ + sizeof(rx_pkt_hdr->eth803_hdr)
+ + sizeof(rx_pkt_hdr->rfc1042_hdr)
+ - sizeof(rx_pkt_hdr->eth803_hdr.h_dest)
+ - sizeof(rx_pkt_hdr->eth803_hdr.h_source)
+ - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type));
+ memcpy(p_ethhdr->h_source, rx_pkt_hdr->eth803_hdr.h_source,
+ sizeof(p_ethhdr->h_source));
+ memcpy(p_ethhdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest,
+ sizeof(p_ethhdr->h_dest));
+ /* Chop off the rxpd + the excess memory from
+ * 802.2/llc/snap header that was removed.
+ */
+ hdr_chop = (u8 *)p_ethhdr - (u8 *)uap_rx_pd;
+ } else {
+ /* Chop off the rxpd */
+ hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd;
+ }
+
+ /* Chop off the leading header bytes so that it points
+ * to the start of either the reconstructed EthII frame
+ * or the 802.2/llc/snap frame.
+ */
+ skb_pull(skb, hdr_chop);
+
+ if (skb_headroom(skb) < NXPWIFI_MIN_DATA_HEADER_LEN) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "data: Tx: insufficient skb headroom %d\n",
+ skb_headroom(skb));
+ /* Insufficient skb headroom - allocate a new skb */
+ new_skb =
+ skb_realloc_headroom(skb, NXPWIFI_MIN_DATA_HEADER_LEN);
+ if (unlikely(!new_skb)) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "Tx: cannot allocate new_skb\n");
+ kfree_skb(skb);
+ priv->stats.tx_dropped++;
+ return;
+ }
+
+ kfree_skb(skb);
+ skb = new_skb;
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: new skb headroom %d\n",
+ skb_headroom(skb));
+ }
+
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+ memset(tx_info, 0, sizeof(*tx_info));
+ tx_info->bss_num = priv->bss_num;
+ tx_info->bss_type = priv->bss_type;
+ tx_info->flags |= NXPWIFI_BUF_FLAG_BRIDGED_PKT;
+
+ src_node = nxpwifi_get_sta_entry(priv, rx_pkt_hdr->eth803_hdr.h_source);
+ if (src_node) {
+ src_node->stats.last_rx = jiffies;
+ src_node->stats.rx_bytes += skb->len;
+ src_node->stats.rx_packets++;
+ src_node->stats.last_tx_rate = uap_rx_pd->rx_rate;
+ src_node->stats.last_tx_htinfo = uap_rx_pd->ht_info;
+ }
+
+ if (is_unicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest)) {
+ /* Update bridge packet statistics as the
+ * packet is not going to kernel/upper layer.
+ */
+ priv->stats.rx_bytes += skb->len;
+ priv->stats.rx_packets++;
+
+ /* Sending bridge packet to TX queue, so save the packet
+ * length in TXCB to update statistics in TX complete.
+ */
+ tx_info->pkt_len = skb->len;
+ }
+
+ __net_timestamp(skb);
+
+ index = nxpwifi_1d_to_wmm_queue[skb->priority];
+ atomic_inc(&priv->wmm_tx_pending[index]);
+ nxpwifi_wmm_add_buf_txqueue(priv, skb);
+ atomic_inc(&adapter->tx_pending);
+ atomic_inc(&adapter->pending_bridged_pkts);
+
+ nxpwifi_queue_main_work(priv->adapter);
+}
+
+/* This function contains logic for AP packet forwarding.
+ *
+ * If a packet is multicast/broadcast, it is sent to kernel/upper layer
+ * as well as queued back to AP TX queue so that it can be sent to other
+ * associated stations.
+ * If a packet is unicast and RA is present in associated station list,
+ * it is again requeued into AP TX queue.
+ * If a packet is unicast and RA is not in associated station list,
+ * packet is forwarded to kernel to handle routing logic.
+ */
+int nxpwifi_handle_uap_rx_forward(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct uap_rxpd *uap_rx_pd;
+ struct rx_packet_hdr *rx_pkt_hdr;
+ u8 ra[ETH_ALEN];
+ struct sk_buff *skb_uap;
+
+ uap_rx_pd = (struct uap_rxpd *)(skb->data);
+ rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
+
+ /* don't do packet forwarding in disconnected state */
+ if (!priv->media_connected) {
+ nxpwifi_dbg(adapter, ERROR,
+ "drop packet in disconnected state.\n");
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ memcpy(ra, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN);
+
+ if (is_multicast_ether_addr(ra)) {
+ skb_uap = skb_copy(skb, GFP_ATOMIC);
+ if (likely(skb_uap)) {
+ nxpwifi_uap_queue_bridged_pkt(priv, skb_uap);
+ } else {
+ nxpwifi_dbg(adapter, ERROR,
+ "failed to copy skb for uAP\n");
+ priv->stats.rx_dropped++;
+ dev_kfree_skb_any(skb);
+ return -1;
+ }
+ } else {
+ if (nxpwifi_get_sta_entry(priv, ra)) {
+ /* Requeue Intra-BSS packet */
+ nxpwifi_uap_queue_bridged_pkt(priv, skb);
+ return 0;
+ }
+ }
+
+ /* Forward unicat/Inter-BSS packets to kernel. */
+ return nxpwifi_process_rx_packet(priv, skb);
+}
+
+int nxpwifi_uap_recv_packet(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_sta_node *src_node;
+ struct ethhdr *p_ethhdr;
+ struct sk_buff *skb_uap;
+ struct nxpwifi_txinfo *tx_info;
+
+ if (!skb)
+ return -1;
+
+ 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++;
+ }
+
+ if (is_multicast_ether_addr(p_ethhdr->h_dest) ||
+ nxpwifi_get_sta_entry(priv, p_ethhdr->h_dest)) {
+ if (skb_headroom(skb) < NXPWIFI_MIN_DATA_HEADER_LEN)
+ skb_uap =
+ skb_realloc_headroom(skb, NXPWIFI_MIN_DATA_HEADER_LEN);
+ else
+ skb_uap = skb_copy(skb, GFP_ATOMIC);
+
+ if (likely(skb_uap)) {
+ tx_info = NXPWIFI_SKB_TXCB(skb_uap);
+ memset(tx_info, 0, sizeof(*tx_info));
+ tx_info->bss_num = priv->bss_num;
+ tx_info->bss_type = priv->bss_type;
+ tx_info->flags |= NXPWIFI_BUF_FLAG_BRIDGED_PKT;
+ __net_timestamp(skb_uap);
+ nxpwifi_wmm_add_buf_txqueue(priv, skb_uap);
+ atomic_inc(&adapter->tx_pending);
+ atomic_inc(&adapter->pending_bridged_pkts);
+ if ((atomic_read(&adapter->pending_bridged_pkts) >=
+ NXPWIFI_BRIDGED_PKTS_THR_HIGH)) {
+ nxpwifi_dbg(adapter, ERROR,
+ "Tx: Bridge packet limit reached. Drop packet!\n");
+ nxpwifi_uap_cleanup_tx_queues(priv);
+ }
+
+ } else {
+ nxpwifi_dbg(adapter, ERROR, "failed to allocate skb_uap");
+ }
+
+ nxpwifi_queue_main_work(adapter);
+ /* Don't forward Intra-BSS unicast packet to upper layer*/
+ if (nxpwifi_get_sta_entry(priv, p_ethhdr->h_dest))
+ return 0;
+ }
+
+ skb->dev = priv->netdev;
+ skb->protocol = eth_type_trans(skb, priv->netdev);
+ skb->ip_summed = CHECKSUM_NONE;
+
+ /* Forward multicast/broadcast packet to upper layer*/
+ netif_rx(skb);
+ return 0;
+}
+
+/* This function processes the packet received on AP interface.
+ *
+ * The function looks into the RxPD and performs sanity tests on the
+ * received buffer to ensure its a valid packet before processing it
+ * further. If the packet is determined to be aggregated, it is
+ * de-aggregated accordingly. Then skb is passed to AP packet forwarding logic.
+ *
+ * The completion callback is called after processing is complete.
+ */
+int nxpwifi_process_uap_rx_packet(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int ret;
+ struct uap_rxpd *uap_rx_pd;
+ struct rx_packet_hdr *rx_pkt_hdr;
+ u16 rx_pkt_type;
+ u8 ta[ETH_ALEN], pkt_type;
+ struct nxpwifi_sta_node *node;
+
+ uap_rx_pd = (struct uap_rxpd *)(skb->data);
+ rx_pkt_type = le16_to_cpu(uap_rx_pd->rx_pkt_type);
+ rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
+
+ if (le16_to_cpu(uap_rx_pd->rx_pkt_offset) +
+ sizeof(rx_pkt_hdr->eth803_hdr) > skb->len) {
+ nxpwifi_dbg(adapter, ERROR,
+ "wrong rx packet for struct ethhdr: len=%d, offset=%d\n",
+ skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset));
+ priv->stats.rx_dropped++;
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ ether_addr_copy(ta, rx_pkt_hdr->eth803_hdr.h_source);
+
+ if ((le16_to_cpu(uap_rx_pd->rx_pkt_offset) +
+ le16_to_cpu(uap_rx_pd->rx_pkt_length)) > (u16)skb->len) {
+ nxpwifi_dbg(adapter, ERROR,
+ "wrong rx packet: len=%d, offset=%d, length=%d\n",
+ skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset),
+ le16_to_cpu(uap_rx_pd->rx_pkt_length));
+ priv->stats.rx_dropped++;
+
+ node = nxpwifi_get_sta_entry(priv, ta);
+ if (node)
+ node->stats.tx_failed++;
+
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ if (rx_pkt_type == PKT_TYPE_MGMT) {
+ ret = nxpwifi_process_mgmt_packet(priv, skb);
+ if (ret)
+ nxpwifi_dbg(adapter, DATA, "Rx of mgmt packet failed");
+ dev_kfree_skb_any(skb);
+ return ret;
+ }
+
+ if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) {
+ spin_lock_bh(&priv->sta_list_spinlock);
+ node = nxpwifi_get_sta_entry(priv, ta);
+ if (node)
+ node->rx_seq[uap_rx_pd->priority] =
+ le16_to_cpu(uap_rx_pd->seq_num);
+ spin_unlock_bh(&priv->sta_list_spinlock);
+ }
+
+ if (!priv->ap_11n_enabled ||
+ (!nxpwifi_11n_get_rx_reorder_tbl(priv, uap_rx_pd->priority, ta) &&
+ (le16_to_cpu(uap_rx_pd->rx_pkt_type) != PKT_TYPE_AMSDU))) {
+ ret = nxpwifi_handle_uap_rx_forward(priv, skb);
+ return ret;
+ }
+
+ /* Reorder and send to kernel */
+ pkt_type = (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type);
+ ret = nxpwifi_11n_rx_reorder_pkt(priv, le16_to_cpu(uap_rx_pd->seq_num),
+ uap_rx_pd->priority, ta, pkt_type,
+ skb);
+
+ if (ret || rx_pkt_type == PKT_TYPE_BAR)
+ dev_kfree_skb_any(skb);
+
+ if (ret)
+ priv->stats.rx_dropped++;
+
+ return ret;
+}
+
+/* This function fills the TxPD for AP tx packets.
+ *
+ * The Tx buffer received by this function should already have the
+ * header space allocated for TxPD.
+ *
+ * This function inserts the TxPD in between interface header and actual
+ * data and adjusts the buffer pointers accordingly.
+ *
+ * The following TxPD fields are set by this function, as required -
+ * - BSS number
+ * - Tx packet length and offset
+ * - Priority
+ * - Packet delay
+ * - Priority specific Tx control
+ * - Flags
+ */
+void nxpwifi_process_uap_txpd(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct uap_txpd *txpd;
+ struct nxpwifi_txinfo *tx_info = NXPWIFI_SKB_TXCB(skb);
+ int pad;
+ u16 pkt_type, pkt_offset;
+ int hroom = adapter->intf_hdr_len;
+
+ pkt_type = nxpwifi_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0;
+
+ pad = ((uintptr_t)skb->data - (sizeof(*txpd) + hroom)) &
+ (NXPWIFI_DMA_ALIGN_SZ - 1);
+
+ skb_push(skb, sizeof(*txpd) + pad);
+
+ txpd = (struct uap_txpd *)skb->data;
+ memset(txpd, 0, sizeof(*txpd));
+ txpd->bss_num = priv->bss_num;
+ txpd->bss_type = priv->bss_type;
+ txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - (sizeof(*txpd) +
+ pad)));
+ txpd->priority = (u8)skb->priority;
+
+ txpd->pkt_delay_2ms = nxpwifi_wmm_compute_drv_pkt_delay(priv, skb);
+
+ if (tx_info->flags & NXPWIFI_BUF_FLAG_EAPOL_TX_STATUS ||
+ tx_info->flags & NXPWIFI_BUF_FLAG_ACTION_TX_STATUS) {
+ txpd->tx_token_id = tx_info->ack_frame_id;
+ txpd->flags |= NXPWIFI_TXPD_FLAGS_REQ_TX_STATUS;
+ }
+
+ if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
+ /* Set the priority specific tx_control field, setting of 0 will
+ * cause the default value to be used later in this function.
+ */
+ txpd->tx_control =
+ cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]);
+
+ /* Offset of actual data */
+ pkt_offset = sizeof(*txpd) + pad;
+ if (pkt_type == PKT_TYPE_MGMT) {
+ /* Set the packet type and add header for management frame */
+ txpd->tx_pkt_type = cpu_to_le16(pkt_type);
+ pkt_offset += NXPWIFI_MGMT_FRAME_HEADER_SIZE;
+ }
+
+ txpd->tx_pkt_offset = cpu_to_le16(pkt_offset);
+
+ /* make space for adapter->intf_hdr_len */
+ skb_push(skb, hroom);
+
+ if (!txpd->tx_control)
+ /* TxCtrl set by user or default */
+ txpd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 37/43] wifi: nxpwifi: add util.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (35 preceding siblings ...)
2024-06-21 7:52 ` [PATCH 36/43] wifi: nxpwifi: add uap_txrx.c David Lin
@ 2024-06-21 7:52 ` David Lin
2024-06-21 7:52 ` [PATCH 38/43] wifi: nxpwifi: add util.h David Lin
` (7 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:52 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/util.c | 751 ++++++++++++++++++++++++
1 file changed, 751 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/util.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/util.c b/drivers/net/wireless/nxp/nxpwifi/util.c
new file mode 100644
index 000000000000..bae1216e7599
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/util.c
@@ -0,0 +1,751 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: utility functions
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.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},
+ {"rx_pending", adapter_item_size(rx_pending),
+ adapter_item_addr(rx_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.
+ */
+int nxpwifi_init_fw_complete(struct nxpwifi_adapter *adapter)
+{
+ adapter->init_wait_q_woken = true;
+ wake_up_interruptible(&adapter->init_wait_q);
+ return 0;
+}
+
+/* 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 -1;
+ }
+
+ 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;
+}
+
+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 -1;
+ 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 rxpd *rx_pd;
+ u16 pkt_len;
+ struct ieee80211_hdr *ieee_hdr;
+
+ if (!skb)
+ return -1;
+
+ if (!priv->mgmt_frame_mask ||
+ priv->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) {
+ nxpwifi_dbg(priv->adapter, ERROR,
+ "do not receive mgmt frames on uninitialized intf");
+ return -1;
+ }
+
+ 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(priv->adapter, ERROR, "invalid rx_pkt_length");
+ return -1;
+ }
+
+ 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)) {
+ if (nxpwifi_parse_mgmt_packet(priv, (u8 *)ieee_hdr,
+ pkt_len, rx_pd))
+ return -1;
+ }
+ /* 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))) {
+ 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(priv->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(priv->adapter, MSG,
+ "auth: receive deauth from %pM\n",
+ ieee_hdr->addr3);
+ priv->auth_flag = 0;
+ priv->auth_alg = WLAN_AUTH_NONE;
+ } else {
+ nxpwifi_dbg(priv->adapter, MSG,
+ "assoc: receive disassoc from %pM\n",
+ ieee_hdr->addr3);
+ }
+ }
+
+ cfg80211_rx_mlme_mgmt(priv->netdev, skb->data, pkt_len);
+ }
+
+ if (GET_BSS_ROLE(priv) == NXPWIFI_BSS_ROLE_UAP) {
+ if (ieee80211_is_auth(ieee_hdr->frame_control))
+ nxpwifi_dbg(priv->adapter, MSG,
+ "auth: receive auth from %pM\n",
+ ieee_hdr->addr2);
+ if (ieee80211_is_deauth(ieee_hdr->frame_control))
+ nxpwifi_dbg(priv->adapter, MSG,
+ "auth: receive deauth from %pM\n",
+ ieee_hdr->addr2);
+ if (ieee80211_is_disassoc(ieee_hdr->frame_control))
+ nxpwifi_dbg(priv->adapter, MSG,
+ "assoc: receive disassoc from %pM\n",
+ ieee_hdr->addr2);
+ if (ieee80211_is_assoc_req(ieee_hdr->frame_control))
+ nxpwifi_dbg(priv->adapter, MSG,
+ "assoc: receive assoc req from %pM\n",
+ ieee_hdr->addr2);
+ if (ieee80211_is_reassoc_req(ieee_hdr->frame_control))
+ nxpwifi_dbg(priv->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 -1;
+
+ 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 ieee_types_header *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);
+
+ if (pos + len > cmd_end)
+ return 0;
+
+ tlv = (struct nxpwifi_ie_types_data *)pos;
+ tlv->header.type = cpu_to_le16(id);
+ tlv->header.len = cpu_to_le16(len);
+ memcpy(tlv->data, data, len);
+
+ return (header_len + len);
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 38/43] wifi: nxpwifi: add util.h
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (36 preceding siblings ...)
2024-06-21 7:52 ` [PATCH 37/43] wifi: nxpwifi: add util.c David Lin
@ 2024-06-21 7:52 ` David Lin
2024-06-21 7:52 ` [PATCH 39/43] wifi: nxpwifi: add wmm.c David Lin
` (6 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:52 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/util.h | 90 +++++++++++++++++++++++++
1 file changed, 90 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/util.h
diff --git a/drivers/net/wireless/nxp/nxpwifi/util.h b/drivers/net/wireless/nxp/nxpwifi/util.h
new file mode 100644
index 000000000000..b9cccb340933
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/util.h
@@ -0,0 +1,90 @@
+/* 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_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);
+#endif /* !_NXPWIFI_UTIL_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 39/43] wifi: nxpwifi: add wmm.c
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (37 preceding siblings ...)
2024-06-21 7:52 ` [PATCH 38/43] wifi: nxpwifi: add util.h David Lin
@ 2024-06-21 7:52 ` David Lin
2024-06-21 7:52 ` [PATCH 40/43] wifi: nxpwifi: add wmm.h David Lin
` (5 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:52 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/wmm.c | 1397 ++++++++++++++++++++++++
1 file changed, 1397 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/wmm.c
diff --git a/drivers/net/wireless/nxp/nxpwifi/wmm.c b/drivers/net/wireless/nxp/nxpwifi/wmm.c
new file mode 100644
index 000000000000..1d90e52f447d
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/wmm.c
@@ -0,0 +1,1397 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NXP Wireless LAN device driver: WMM
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+
+/* Maximum value FW can accept for driver delay in packet transmission */
+#define DRV_PKT_DELAY_TO_FW_MAX 512
+
+#define WMM_QUEUED_PACKET_LOWER_LIMIT 180
+
+#define WMM_QUEUED_PACKET_UPPER_LIMIT 200
+
+/* Offset for TOS field in the IP header */
+#define IPTOS_OFFSET 5
+
+static bool disable_tx_amsdu;
+
+/* This table inverses the tos_to_tid operation to get a priority
+ * which is in sequential order, and can be compared.
+ * Use this to compare the priority of two different TIDs.
+ */
+const u8 tos_to_tid_inv[] = {
+ 0x02, /* from tos_to_tid[2] = 0 */
+ 0x00, /* from tos_to_tid[0] = 1 */
+ 0x01, /* from tos_to_tid[1] = 2 */
+ 0x03,
+ 0x04,
+ 0x05,
+ 0x06,
+ 0x07
+};
+
+/* WMM information IE */
+static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07,
+ 0x00, 0x50, 0xf2, 0x02,
+ 0x00, 0x01, 0x00
+};
+
+static const u8 wmm_aci_to_qidx_map[] = { WMM_AC_BE,
+ WMM_AC_BK,
+ WMM_AC_VI,
+ WMM_AC_VO
+};
+
+static u8 tos_to_tid[] = {
+ /* TID DSCP_P2 DSCP_P1 DSCP_P0 WMM_AC */
+ 0x01, /* 0 1 0 AC_BK */
+ 0x02, /* 0 0 0 AC_BK */
+ 0x00, /* 0 0 1 AC_BE */
+ 0x03, /* 0 1 1 AC_BE */
+ 0x04, /* 1 0 0 AC_VI */
+ 0x05, /* 1 0 1 AC_VI */
+ 0x06, /* 1 1 0 AC_VO */
+ 0x07 /* 1 1 1 AC_VO */
+};
+
+static u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} };
+
+/* This function debug prints the priority parameters for a WMM AC.
+ */
+static void
+nxpwifi_wmm_ac_debug_print(const struct ieee_types_wmm_ac_parameters *ac_param)
+{
+ static const char * const ac_str[] = { "BK", "BE", "VI", "VO" };
+
+ pr_debug("info: WMM AC_%s: ACI=%d, ACM=%d, Aifsn=%d, ",
+ ac_str[wmm_aci_to_qidx_map[(ac_param->aci_aifsn_bitmap
+ & NXPWIFI_ACI) >> 5]],
+ (ac_param->aci_aifsn_bitmap & NXPWIFI_ACI) >> 5,
+ (ac_param->aci_aifsn_bitmap & NXPWIFI_ACM) >> 4,
+ ac_param->aci_aifsn_bitmap & NXPWIFI_AIFSN);
+ pr_debug("EcwMin=%d, EcwMax=%d, TxopLimit=%d\n",
+ ac_param->ecw_bitmap & NXPWIFI_ECW_MIN,
+ (ac_param->ecw_bitmap & NXPWIFI_ECW_MAX) >> 4,
+ le16_to_cpu(ac_param->tx_op_limit));
+}
+
+/* This function allocates a route address list.
+ *
+ * The function also initializes the list with the provided RA.
+ */
+static struct nxpwifi_ra_list_tbl *
+nxpwifi_wmm_allocate_ralist_node(struct nxpwifi_adapter *adapter, const u8 *ra)
+{
+ struct nxpwifi_ra_list_tbl *ra_list;
+
+ ra_list = kzalloc(sizeof(*ra_list), GFP_ATOMIC);
+ if (!ra_list)
+ return NULL;
+
+ INIT_LIST_HEAD(&ra_list->list);
+ skb_queue_head_init(&ra_list->skb_head);
+
+ memcpy(ra_list->ra, ra, ETH_ALEN);
+
+ ra_list->total_pkt_count = 0;
+
+ nxpwifi_dbg(adapter, INFO, "info: allocated ra_list %p\n", ra_list);
+
+ return ra_list;
+}
+
+/* This function returns random no between 16 and 32 to be used as threshold
+ * for no of packets after which BA setup is initiated.
+ */
+static u8 nxpwifi_get_random_ba_threshold(void)
+{
+ u64 ns;
+ /* setup ba_packet_threshold here random number between
+ * [BA_SETUP_PACKET_OFFSET,
+ * BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1]
+ */
+ ns = ktime_get_ns();
+ ns += (ns >> 32) + (ns >> 16);
+
+ return ((u8)ns % BA_SETUP_MAX_PACKET_THRESHOLD) + BA_SETUP_PACKET_OFFSET;
+}
+
+/* This function allocates and adds a RA list for all TIDs
+ * with the given RA.
+ */
+void nxpwifi_ralist_add(struct nxpwifi_private *priv, const u8 *ra)
+{
+ int i;
+ struct nxpwifi_ra_list_tbl *ra_list;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_sta_node *node;
+
+ for (i = 0; i < MAX_NUM_TID; ++i) {
+ ra_list = nxpwifi_wmm_allocate_ralist_node(adapter, ra);
+ nxpwifi_dbg(adapter, INFO,
+ "info: created ra_list %p\n", ra_list);
+
+ if (!ra_list)
+ break;
+
+ ra_list->is_11n_enabled = 0;
+ ra_list->ba_status = BA_SETUP_NONE;
+ ra_list->amsdu_in_ampdu = false;
+ if (!nxpwifi_queuing_ra_based(priv)) {
+ ra_list->is_11n_enabled = IS_11N_ENABLED(priv);
+ } else {
+ spin_lock_bh(&priv->sta_list_spinlock);
+ node = nxpwifi_get_sta_entry(priv, ra);
+ if (node)
+ ra_list->tx_paused = node->tx_pause;
+ ra_list->is_11n_enabled =
+ nxpwifi_is_sta_11n_enabled(priv, node);
+ if (ra_list->is_11n_enabled)
+ ra_list->max_amsdu = node->max_amsdu;
+ spin_unlock_bh(&priv->sta_list_spinlock);
+ }
+
+ nxpwifi_dbg(adapter, DATA, "data: ralist %p: is_11n_enabled=%d\n",
+ ra_list, ra_list->is_11n_enabled);
+
+ if (ra_list->is_11n_enabled) {
+ ra_list->ba_pkt_count = 0;
+ ra_list->ba_packet_thr =
+ nxpwifi_get_random_ba_threshold();
+ }
+ list_add_tail(&ra_list->list,
+ &priv->wmm.tid_tbl_ptr[i].ra_list);
+ }
+}
+
+/* This function sets the WMM queue priorities to their default values.
+ */
+static void nxpwifi_wmm_default_queue_priorities(struct nxpwifi_private *priv)
+{
+ /* Default queue priorities: VO->VI->BE->BK */
+ priv->wmm.queue_priority[0] = WMM_AC_VO;
+ priv->wmm.queue_priority[1] = WMM_AC_VI;
+ priv->wmm.queue_priority[2] = WMM_AC_BE;
+ priv->wmm.queue_priority[3] = WMM_AC_BK;
+}
+
+/* This function map ACs to TIDs.
+ */
+static void
+nxpwifi_wmm_queue_priorities_tid(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_wmm_desc *wmm = &priv->wmm;
+ u8 *queue_priority = wmm->queue_priority;
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ tos_to_tid[7 - (i * 2)] = ac_to_tid[queue_priority[i]][1];
+ tos_to_tid[6 - (i * 2)] = ac_to_tid[queue_priority[i]][0];
+ }
+
+ for (i = 0; i < MAX_NUM_TID; ++i)
+ priv->tos_to_tid_inv[tos_to_tid[i]] = (u8)i;
+
+ atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID);
+}
+
+/* This function initializes WMM priority queues.
+ */
+void
+nxpwifi_wmm_setup_queue_priorities(struct nxpwifi_private *priv,
+ struct ieee_types_wmm_parameter *wmm_ie)
+{
+ u16 cw_min, avg_back_off, tmp[4];
+ u32 i, j, num_ac;
+ u8 ac_idx;
+
+ if (!wmm_ie || !priv->wmm_enabled) {
+ /* WMM is not enabled, just set the defaults and return */
+ nxpwifi_wmm_default_queue_priorities(priv);
+ return;
+ }
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: WMM Parameter IE: version=%d,\t"
+ "qos_info Parameter Set Count=%d, Reserved=%#x\n",
+ wmm_ie->version, wmm_ie->qos_info_bitmap &
+ IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK,
+ wmm_ie->reserved);
+
+ for (num_ac = 0; num_ac < ARRAY_SIZE(wmm_ie->ac_params); num_ac++) {
+ u8 ecw = wmm_ie->ac_params[num_ac].ecw_bitmap;
+ u8 aci_aifsn = wmm_ie->ac_params[num_ac].aci_aifsn_bitmap;
+
+ cw_min = (1 << (ecw & NXPWIFI_ECW_MIN)) - 1;
+ avg_back_off = (cw_min >> 1) + (aci_aifsn & NXPWIFI_AIFSN);
+
+ ac_idx = wmm_aci_to_qidx_map[(aci_aifsn & NXPWIFI_ACI) >> 5];
+ priv->wmm.queue_priority[ac_idx] = ac_idx;
+ tmp[ac_idx] = avg_back_off;
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n",
+ (1 << ((ecw & NXPWIFI_ECW_MAX) >> 4)) - 1,
+ cw_min, avg_back_off);
+ nxpwifi_wmm_ac_debug_print(&wmm_ie->ac_params[num_ac]);
+ }
+
+ /* Bubble sort */
+ for (i = 0; i < num_ac; i++) {
+ for (j = 1; j < num_ac - i; j++) {
+ if (tmp[j - 1] > tmp[j]) {
+ swap(tmp[j - 1], tmp[j]);
+ swap(priv->wmm.queue_priority[j - 1],
+ priv->wmm.queue_priority[j]);
+ } else if (tmp[j - 1] == tmp[j]) {
+ if (priv->wmm.queue_priority[j - 1]
+ < priv->wmm.queue_priority[j])
+ swap(priv->wmm.queue_priority[j - 1],
+ priv->wmm.queue_priority[j]);
+ }
+ }
+ }
+
+ nxpwifi_wmm_queue_priorities_tid(priv);
+}
+
+/* This function evaluates whether or not an AC is to be downgraded.
+ *
+ * In case the AC is not enabled, the highest AC is returned that is
+ * enabled and does not require admission control.
+ */
+static enum nxpwifi_wmm_ac_e
+nxpwifi_wmm_eval_downgrade_ac(struct nxpwifi_private *priv,
+ enum nxpwifi_wmm_ac_e eval_ac)
+{
+ int down_ac;
+ enum nxpwifi_wmm_ac_e ret_ac;
+ struct nxpwifi_wmm_ac_status *ac_status;
+
+ ac_status = &priv->wmm.ac_status[eval_ac];
+
+ if (!ac_status->disabled)
+ /* Okay to use this AC, its enabled */
+ return eval_ac;
+
+ /* Setup a default return value of the lowest priority */
+ ret_ac = WMM_AC_BK;
+
+ /* Find the highest AC that is enabled and does not require
+ * admission control. The spec disallows downgrading to an AC,
+ * which is enabled due to a completed admission control.
+ * Unadmitted traffic is not to be sent on an AC with admitted
+ * traffic.
+ */
+ for (down_ac = WMM_AC_BK; down_ac < eval_ac; down_ac++) {
+ ac_status = &priv->wmm.ac_status[down_ac];
+
+ if (!ac_status->disabled && !ac_status->flow_required)
+ /* AC is enabled and does not require admission
+ * control
+ */
+ ret_ac = (enum nxpwifi_wmm_ac_e)down_ac;
+ }
+
+ return ret_ac;
+}
+
+/* This function downgrades WMM priority queue.
+ */
+void
+nxpwifi_wmm_setup_ac_downgrade(struct nxpwifi_private *priv)
+{
+ int ac_val;
+
+ nxpwifi_dbg(priv->adapter, INFO, "info: WMM: AC Priorities:\t"
+ "BK(0), BE(1), VI(2), VO(3)\n");
+
+ if (!priv->wmm_enabled) {
+ /* WMM is not enabled, default priorities */
+ for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++)
+ priv->wmm.ac_down_graded_vals[ac_val] =
+ (enum nxpwifi_wmm_ac_e)ac_val;
+ } else {
+ for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) {
+ priv->wmm.ac_down_graded_vals[ac_val] =
+ nxpwifi_wmm_eval_downgrade_ac
+ (priv, (enum nxpwifi_wmm_ac_e)ac_val);
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: WMM: AC PRIO %d maps to %d\n",
+ ac_val,
+ priv->wmm.ac_down_graded_vals[ac_val]);
+ }
+ }
+}
+
+/* This function converts the IP TOS field to an WMM AC
+ * Queue assignment.
+ */
+static enum nxpwifi_wmm_ac_e
+nxpwifi_wmm_convert_tos_to_ac(struct nxpwifi_adapter *adapter, u32 tos)
+{
+ /* Map of TOS UP values to WMM AC */
+ static const enum nxpwifi_wmm_ac_e tos_to_ac[] = {
+ WMM_AC_BE,
+ WMM_AC_BK,
+ WMM_AC_BK,
+ WMM_AC_BE,
+ WMM_AC_VI,
+ WMM_AC_VI,
+ WMM_AC_VO,
+ WMM_AC_VO
+ };
+
+ if (tos >= ARRAY_SIZE(tos_to_ac))
+ return WMM_AC_BE;
+
+ return tos_to_ac[tos];
+}
+
+/* This function evaluates a given TID and downgrades it to a lower
+ * TID if the WMM Parameter IE received from the AP indicates that the
+ * AP is disabled (due to call admission control (ACM bit). Mapping
+ * of TID to AC is taken care of internally.
+ */
+u8 nxpwifi_wmm_downgrade_tid(struct nxpwifi_private *priv, u32 tid)
+{
+ enum nxpwifi_wmm_ac_e ac, ac_down;
+ u8 new_tid;
+
+ ac = nxpwifi_wmm_convert_tos_to_ac(priv->adapter, tid);
+ ac_down = priv->wmm.ac_down_graded_vals[ac];
+
+ /* Send the index to tid array, picking from the array will be
+ * taken care by dequeuing function
+ */
+ new_tid = ac_to_tid[ac_down][tid % 2];
+
+ return new_tid;
+}
+
+/* This function initializes the WMM state information and the
+ * WMM data path queues.
+ */
+void
+nxpwifi_wmm_init(struct nxpwifi_adapter *adapter)
+{
+ int i, j;
+ struct nxpwifi_private *priv;
+
+ for (j = 0; j < adapter->priv_num; ++j) {
+ priv = adapter->priv[j];
+ if (!priv)
+ continue;
+
+ for (i = 0; i < MAX_NUM_TID; ++i) {
+ if (!disable_tx_amsdu &&
+ adapter->tx_buf_size > NXPWIFI_TX_DATA_BUF_SIZE_2K)
+ priv->aggr_prio_tbl[i].amsdu =
+ priv->tos_to_tid_inv[i];
+ else
+ priv->aggr_prio_tbl[i].amsdu =
+ BA_STREAM_NOT_ALLOWED;
+ priv->aggr_prio_tbl[i].ampdu_ap =
+ priv->tos_to_tid_inv[i];
+ priv->aggr_prio_tbl[i].ampdu_user =
+ priv->tos_to_tid_inv[i];
+ }
+
+ priv->aggr_prio_tbl[6].amsdu =
+ priv->aggr_prio_tbl[6].ampdu_ap =
+ priv->aggr_prio_tbl[6].ampdu_user =
+ BA_STREAM_NOT_ALLOWED;
+
+ priv->aggr_prio_tbl[7].amsdu =
+ priv->aggr_prio_tbl[7].ampdu_ap =
+ priv->aggr_prio_tbl[7].ampdu_user =
+ BA_STREAM_NOT_ALLOWED;
+
+ nxpwifi_set_ba_params(priv);
+ nxpwifi_reset_11n_rx_seq_num(priv);
+
+ priv->wmm.drv_pkt_delay_max = NXPWIFI_WMM_DRV_DELAY_MAX;
+ atomic_set(&priv->wmm.tx_pkts_queued, 0);
+ atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID);
+ }
+}
+
+int nxpwifi_bypass_txlist_empty(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_private *priv;
+ int i;
+
+ for (i = 0; i < adapter->priv_num; i++) {
+ priv = adapter->priv[i];
+ if (!priv)
+ continue;
+ if (adapter->if_ops.is_port_ready &&
+ !adapter->if_ops.is_port_ready(priv))
+ continue;
+ if (!skb_queue_empty(&priv->bypass_txq))
+ return false;
+ }
+
+ return true;
+}
+
+/* This function checks if WMM Tx queue is empty.
+ */
+int
+nxpwifi_wmm_lists_empty(struct nxpwifi_adapter *adapter)
+{
+ int i;
+ struct nxpwifi_private *priv;
+
+ for (i = 0; i < adapter->priv_num; ++i) {
+ priv = adapter->priv[i];
+ if (!priv)
+ continue;
+ if (!priv->port_open)
+ continue;
+ if (adapter->if_ops.is_port_ready &&
+ !adapter->if_ops.is_port_ready(priv))
+ continue;
+ if (atomic_read(&priv->wmm.tx_pkts_queued))
+ return false;
+ }
+
+ return true;
+}
+
+/* This function deletes all packets in an RA list node.
+ *
+ * The packet sent completion callback handler are called with
+ * status failure, after they are dequeued to ensure proper
+ * cleanup. The RA list node itself is freed at the end.
+ */
+static void
+nxpwifi_wmm_del_pkts_in_ralist_node(struct nxpwifi_private *priv,
+ struct nxpwifi_ra_list_tbl *ra_list)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct sk_buff *skb, *tmp;
+
+ skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) {
+ skb_unlink(skb, &ra_list->skb_head);
+ nxpwifi_write_data_complete(adapter, skb, 0, -1);
+ }
+}
+
+/* This function deletes all packets in an RA list.
+ *
+ * Each nodes in the RA list are freed individually first, and then
+ * the RA list itself is freed.
+ */
+static void
+nxpwifi_wmm_del_pkts_in_ralist(struct nxpwifi_private *priv,
+ struct list_head *ra_list_head)
+{
+ struct nxpwifi_ra_list_tbl *ra_list;
+
+ list_for_each_entry(ra_list, ra_list_head, list)
+ nxpwifi_wmm_del_pkts_in_ralist_node(priv, ra_list);
+}
+
+/* This function deletes all packets in all RA lists.
+ */
+static void nxpwifi_wmm_cleanup_queues(struct nxpwifi_private *priv)
+{
+ int i;
+
+ for (i = 0; i < MAX_NUM_TID; i++)
+ nxpwifi_wmm_del_pkts_in_ralist
+ (priv, &priv->wmm.tid_tbl_ptr[i].ra_list);
+
+ atomic_set(&priv->wmm.tx_pkts_queued, 0);
+ atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID);
+}
+
+/* This function deletes all route addresses from all RA lists.
+ */
+static void nxpwifi_wmm_delete_all_ralist(struct nxpwifi_private *priv)
+{
+ struct nxpwifi_ra_list_tbl *ra_list, *tmp_node;
+ int i;
+
+ for (i = 0; i < MAX_NUM_TID; ++i) {
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: ra_list: freeing buf for tid %d\n", i);
+ list_for_each_entry_safe(ra_list, tmp_node,
+ &priv->wmm.tid_tbl_ptr[i].ra_list,
+ list) {
+ list_del(&ra_list->list);
+ kfree(ra_list);
+ }
+
+ INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[i].ra_list);
+ }
+}
+
+static int nxpwifi_free_ack_frame(int id, void *p, void *data)
+{
+ pr_warn("Have pending ack frames!\n");
+ kfree_skb(p);
+ return 0;
+}
+
+/* This function cleans up the Tx and Rx queues.
+ *
+ * Cleanup includes -
+ * - All packets in RA lists
+ * - All entries in Rx reorder table
+ * - All entries in Tx BA stream table
+ * - MPA buffer (if required)
+ * - All RA lists
+ */
+void
+nxpwifi_clean_txrx(struct nxpwifi_private *priv)
+{
+ struct sk_buff *skb, *tmp;
+
+ nxpwifi_11n_cleanup_reorder_tbl(priv);
+ spin_lock_bh(&priv->wmm.ra_list_spinlock);
+
+ nxpwifi_wmm_cleanup_queues(priv);
+ nxpwifi_11n_delete_all_tx_ba_stream_tbl(priv);
+
+ if (priv->adapter->if_ops.cleanup_mpa_buf)
+ priv->adapter->if_ops.cleanup_mpa_buf(priv->adapter);
+
+ nxpwifi_wmm_delete_all_ralist(priv);
+ memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid));
+
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+
+ skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) {
+ skb_unlink(skb, &priv->bypass_txq);
+ nxpwifi_write_data_complete(priv->adapter, skb, 0, -1);
+ }
+ atomic_set(&priv->adapter->bypass_tx_pending, 0);
+
+ idr_for_each(&priv->ack_status_frames, nxpwifi_free_ack_frame, NULL);
+ idr_destroy(&priv->ack_status_frames);
+}
+
+/* This function retrieves a particular RA list node, matching with the
+ * given TID and RA address.
+ */
+struct nxpwifi_ra_list_tbl *
+nxpwifi_wmm_get_ralist_node(struct nxpwifi_private *priv, u8 tid,
+ const u8 *ra_addr)
+{
+ struct nxpwifi_ra_list_tbl *ra_list;
+
+ list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[tid].ra_list,
+ list) {
+ if (!memcmp(ra_list->ra, ra_addr, ETH_ALEN))
+ return ra_list;
+ }
+
+ return NULL;
+}
+
+void nxpwifi_update_ralist_tx_pause(struct nxpwifi_private *priv, u8 *mac,
+ u8 tx_pause)
+{
+ struct nxpwifi_ra_list_tbl *ra_list;
+ u32 pkt_cnt = 0, tx_pkts_queued;
+ int i;
+
+ spin_lock_bh(&priv->wmm.ra_list_spinlock);
+
+ for (i = 0; i < MAX_NUM_TID; ++i) {
+ ra_list = nxpwifi_wmm_get_ralist_node(priv, i, mac);
+ if (ra_list && ra_list->tx_paused != tx_pause) {
+ pkt_cnt += ra_list->total_pkt_count;
+ ra_list->tx_paused = tx_pause;
+ if (tx_pause)
+ priv->wmm.pkts_paused[i] +=
+ ra_list->total_pkt_count;
+ else
+ priv->wmm.pkts_paused[i] -=
+ ra_list->total_pkt_count;
+ }
+ }
+
+ if (pkt_cnt) {
+ tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued);
+ if (tx_pause)
+ tx_pkts_queued -= pkt_cnt;
+ else
+ tx_pkts_queued += pkt_cnt;
+
+ atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued);
+ atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID);
+ }
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+}
+
+/* This function retrieves an RA list node for a given TID and
+ * RA address pair.
+ *
+ * If no such node is found, a new node is added first and then
+ * retrieved.
+ */
+struct nxpwifi_ra_list_tbl *
+nxpwifi_wmm_get_queue_raptr(struct nxpwifi_private *priv, u8 tid,
+ const u8 *ra_addr)
+{
+ struct nxpwifi_ra_list_tbl *ra_list;
+
+ ra_list = nxpwifi_wmm_get_ralist_node(priv, tid, ra_addr);
+ if (ra_list)
+ return ra_list;
+ nxpwifi_ralist_add(priv, ra_addr);
+
+ return nxpwifi_wmm_get_ralist_node(priv, tid, ra_addr);
+}
+
+/* This function deletes RA list nodes for given mac for all TIDs.
+ * Function also decrements TX pending count accordingly.
+ */
+void
+nxpwifi_wmm_del_peer_ra_list(struct nxpwifi_private *priv, const u8 *ra_addr)
+{
+ struct nxpwifi_ra_list_tbl *ra_list;
+ int i;
+
+ spin_lock_bh(&priv->wmm.ra_list_spinlock);
+
+ for (i = 0; i < MAX_NUM_TID; ++i) {
+ ra_list = nxpwifi_wmm_get_ralist_node(priv, i, ra_addr);
+
+ if (!ra_list)
+ continue;
+ nxpwifi_wmm_del_pkts_in_ralist_node(priv, ra_list);
+ if (ra_list->tx_paused)
+ priv->wmm.pkts_paused[i] -= ra_list->total_pkt_count;
+ else
+ atomic_sub(ra_list->total_pkt_count,
+ &priv->wmm.tx_pkts_queued);
+ list_del(&ra_list->list);
+ kfree(ra_list);
+ }
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+}
+
+/* This function checks if a particular RA list node exists in a given TID
+ * table index.
+ */
+int
+nxpwifi_is_ralist_valid(struct nxpwifi_private *priv,
+ struct nxpwifi_ra_list_tbl *ra_list, int ptr_index)
+{
+ struct nxpwifi_ra_list_tbl *rlist;
+
+ list_for_each_entry(rlist, &priv->wmm.tid_tbl_ptr[ptr_index].ra_list,
+ list) {
+ if (rlist == ra_list)
+ return true;
+ }
+
+ return false;
+}
+
+/* This function adds a packet to bypass TX queue.
+ * This is special TX queue for packets which can be sent even when port_open
+ * is false.
+ */
+void
+nxpwifi_wmm_add_buf_bypass_txqueue(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ skb_queue_tail(&priv->bypass_txq, skb);
+}
+
+/* This function adds a packet to WMM queue.
+ *
+ * In disconnected state the packet is immediately dropped and the
+ * packet send completion callback is called with status failure.
+ *
+ * Otherwise, the correct RA list node is located and the packet
+ * is queued at the list tail.
+ */
+void
+nxpwifi_wmm_add_buf_txqueue(struct nxpwifi_private *priv,
+ struct sk_buff *skb)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ u32 tid;
+ struct nxpwifi_ra_list_tbl *ra_list;
+ u8 ra[ETH_ALEN], tid_down;
+ struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
+
+ memcpy(ra, eth_hdr->h_dest, ETH_ALEN);
+
+ if (!priv->media_connected && !nxpwifi_is_skb_mgmt_frame(skb)) {
+ nxpwifi_dbg(adapter, DATA, "data: drop packet in disconnect\n");
+ nxpwifi_write_data_complete(adapter, skb, 0, -1);
+ return;
+ }
+
+ tid = skb->priority;
+
+ spin_lock_bh(&priv->wmm.ra_list_spinlock);
+
+ tid_down = nxpwifi_wmm_downgrade_tid(priv, tid);
+
+ /* In case of infra as we have already created the list during
+ * association we just don't have to call get_queue_raptr, we will
+ * have only 1 raptr for a tid in case of infra
+ */
+ memcpy(ra, skb->data, ETH_ALEN);
+ if (is_multicast_ether_addr(ra) || nxpwifi_is_skb_mgmt_frame(skb))
+ eth_broadcast_addr(ra);
+ ra_list = nxpwifi_wmm_get_queue_raptr(priv, tid_down, ra);
+
+ if (!ra_list) {
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ nxpwifi_write_data_complete(adapter, skb, 0, -1);
+ return;
+ }
+
+ skb_queue_tail(&ra_list->skb_head, skb);
+
+ ra_list->ba_pkt_count++;
+ ra_list->total_pkt_count++;
+
+ if (atomic_read(&priv->wmm.highest_queued_prio) <
+ priv->tos_to_tid_inv[tid_down])
+ atomic_set(&priv->wmm.highest_queued_prio,
+ priv->tos_to_tid_inv[tid_down]);
+
+ if (ra_list->tx_paused)
+ priv->wmm.pkts_paused[tid_down]++;
+ else
+ atomic_inc(&priv->wmm.tx_pkts_queued);
+
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+}
+
+/* This function processes the get WMM status command response from firmware.
+ *
+ * The response may contain multiple TLVs -
+ * - AC Queue status TLVs
+ * - Current WMM Parameter IE TLV
+ * - Admission Control action frame TLVs
+ *
+ * This function parses the TLVs and then calls further specific functions
+ * to process any changes in the queue prioritize or state.
+ */
+int nxpwifi_ret_wmm_get_status(struct nxpwifi_private *priv,
+ const struct host_cmd_ds_command *resp)
+{
+ u8 *curr = (u8 *)&resp->params.get_wmm_status;
+ u16 resp_len = le16_to_cpu(resp->size), tlv_len;
+ int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK;
+ bool valid = true;
+
+ struct nxpwifi_ie_types_data *tlv_hdr;
+ struct nxpwifi_ie_types_wmm_queue_status *wmm_qs;
+ struct ieee_types_wmm_parameter *wmm_param_ie = NULL;
+ struct nxpwifi_wmm_ac_status *ac_status;
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: WMM: WMM_GET_STATUS cmdresp received: %d\n",
+ resp_len);
+
+ while ((resp_len >= sizeof(tlv_hdr->header)) && valid) {
+ tlv_hdr = (struct nxpwifi_ie_types_data *)curr;
+ tlv_len = le16_to_cpu(tlv_hdr->header.len);
+
+ if (resp_len < tlv_len + sizeof(tlv_hdr->header))
+ break;
+
+ switch (le16_to_cpu(tlv_hdr->header.type)) {
+ case TLV_TYPE_WMMQSTATUS:
+ wmm_qs = (struct nxpwifi_ie_types_wmm_queue_status *)
+ tlv_hdr;
+ nxpwifi_dbg(priv->adapter, CMD,
+ "info: CMD_RESP: WMM_GET_STATUS:\t"
+ "QSTATUS TLV: %d, %d, %d\n",
+ wmm_qs->queue_index,
+ wmm_qs->flow_required,
+ wmm_qs->disabled);
+
+ ac_status = &priv->wmm.ac_status[wmm_qs->queue_index];
+ ac_status->disabled = wmm_qs->disabled;
+ ac_status->flow_required = wmm_qs->flow_required;
+ ac_status->flow_created = wmm_qs->flow_created;
+ break;
+
+ case WLAN_EID_VENDOR_SPECIFIC:
+ /* Point the regular IEEE IE 2 bytes into the NXP IE
+ * and setup the IEEE IE type and length byte fields
+ */
+
+ wmm_param_ie =
+ (struct ieee_types_wmm_parameter *)(curr + 2);
+ wmm_param_ie->vend_hdr.len = (u8)tlv_len;
+ wmm_param_ie->vend_hdr.element_id =
+ WLAN_EID_VENDOR_SPECIFIC;
+
+ nxpwifi_dbg(priv->adapter, CMD,
+ "info: CMD_RESP: WMM_GET_STATUS:\t"
+ "WMM Parameter Set Count: %d\n",
+ wmm_param_ie->qos_info_bitmap & mask);
+
+ if (wmm_param_ie->vend_hdr.len + 2 >
+ sizeof(struct ieee_types_wmm_parameter))
+ break;
+
+ memcpy(&priv->curr_bss_params.bss_descriptor.wmm_ie,
+ wmm_param_ie, wmm_param_ie->vend_hdr.len + 2);
+
+ break;
+
+ default:
+ valid = false;
+ break;
+ }
+
+ curr += (tlv_len + sizeof(tlv_hdr->header));
+ resp_len -= (tlv_len + sizeof(tlv_hdr->header));
+ }
+
+ nxpwifi_wmm_setup_queue_priorities(priv, wmm_param_ie);
+ nxpwifi_wmm_setup_ac_downgrade(priv);
+
+ return 0;
+}
+
+/* Callback handler from the command module to allow insertion of a WMM TLV.
+ *
+ * If the BSS we are associating to supports WMM, this function adds the
+ * required WMM Information IE to the association request command buffer in
+ * the form of a NXP extended IEEE IE.
+ */
+u32
+nxpwifi_wmm_process_association_req(struct nxpwifi_private *priv,
+ u8 **assoc_buf,
+ struct ieee_types_wmm_parameter *wmm_ie,
+ struct ieee80211_ht_cap *ht_cap)
+{
+ struct nxpwifi_ie_types_wmm_param_set *wmm_tlv;
+ u32 ret_len = 0;
+
+ /* Null checks */
+ if (!assoc_buf)
+ return 0;
+ if (!(*assoc_buf))
+ return 0;
+
+ if (!wmm_ie)
+ return 0;
+
+ nxpwifi_dbg(priv->adapter, INFO,
+ "info: WMM: process assoc req: bss->wmm_ie=%#x\n",
+ wmm_ie->vend_hdr.element_id);
+
+ if ((priv->wmm_required ||
+ (ht_cap && (priv->adapter->config_bands & BAND_GN ||
+ priv->adapter->config_bands & BAND_AN))) &&
+ wmm_ie->vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) {
+ wmm_tlv = (struct nxpwifi_ie_types_wmm_param_set *)*assoc_buf;
+ wmm_tlv->header.type = cpu_to_le16((u16)wmm_info_ie[0]);
+ wmm_tlv->header.len = cpu_to_le16((u16)wmm_info_ie[1]);
+ memcpy(wmm_tlv->wmm_ie, &wmm_info_ie[2],
+ le16_to_cpu(wmm_tlv->header.len));
+ if (wmm_ie->qos_info_bitmap & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD)
+ memcpy((u8 *)(wmm_tlv->wmm_ie
+ + le16_to_cpu(wmm_tlv->header.len)
+ - sizeof(priv->wmm_qosinfo)),
+ &priv->wmm_qosinfo, sizeof(priv->wmm_qosinfo));
+
+ ret_len = sizeof(wmm_tlv->header)
+ + le16_to_cpu(wmm_tlv->header.len);
+
+ *assoc_buf += ret_len;
+ }
+
+ return ret_len;
+}
+
+/* This function computes the time delay in the driver queues for a
+ * given packet.
+ *
+ * When the packet is received at the OS/Driver interface, the current
+ * time is set in the packet structure. The difference between the present
+ * time and that received time is computed in this function and limited
+ * based on pre-compiled limits in the driver.
+ */
+u8
+nxpwifi_wmm_compute_drv_pkt_delay(struct nxpwifi_private *priv,
+ const struct sk_buff *skb)
+{
+ u32 queue_delay = ktime_to_ms(net_timedelta(skb->tstamp));
+ u8 ret_val;
+
+ /* Queue delay is passed as a uint8 in units of 2ms (ms shifted
+ * by 1). Min value (other than 0) is therefore 2ms, max is 510ms.
+ *
+ * Pass max value if queue_delay is beyond the uint8 range
+ */
+ ret_val = (u8)(min(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1);
+
+ nxpwifi_dbg(priv->adapter, DATA, "data: WMM: Pkt Delay: %d ms,\t"
+ "%d ms sent to FW\n", queue_delay, ret_val);
+
+ return ret_val;
+}
+
+/* This function retrieves the highest priority RA list table pointer.
+ */
+static struct nxpwifi_ra_list_tbl *
+nxpwifi_wmm_get_highest_priolist_ptr(struct nxpwifi_adapter *adapter,
+ struct nxpwifi_private **priv, int *tid)
+{
+ struct nxpwifi_private *priv_tmp;
+ struct nxpwifi_ra_list_tbl *ptr;
+ struct nxpwifi_tid_tbl *tid_ptr;
+ atomic_t *hqp;
+ int i, j;
+ u8 to_tid;
+
+ /* check the BSS with highest priority first */
+ for (j = adapter->priv_num - 1; j >= 0; --j) {
+ /* iterate over BSS with the equal priority */
+ list_for_each_entry(adapter->bss_prio_tbl[j].bss_prio_cur,
+ &adapter->bss_prio_tbl[j].bss_prio_head,
+ list) {
+try_again:
+ priv_tmp = adapter->bss_prio_tbl[j].bss_prio_cur->priv;
+
+ if (!priv_tmp->port_open ||
+ (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0))
+ continue;
+
+ if (adapter->if_ops.is_port_ready &&
+ !adapter->if_ops.is_port_ready(priv_tmp))
+ continue;
+
+ /* iterate over the WMM queues of the BSS */
+ hqp = &priv_tmp->wmm.highest_queued_prio;
+ for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) {
+ spin_lock_bh(&priv_tmp->wmm.ra_list_spinlock);
+
+ to_tid = tos_to_tid[i];
+ tid_ptr = &(priv_tmp)->wmm.tid_tbl_ptr[to_tid];
+
+ /* iterate over receiver addresses */
+ list_for_each_entry(ptr, &tid_ptr->ra_list,
+ list) {
+ if (!ptr->tx_paused &&
+ !skb_queue_empty(&ptr->skb_head))
+ /* holds both locks */
+ goto found;
+ }
+
+ spin_unlock_bh(&priv_tmp->wmm.ra_list_spinlock);
+ }
+
+ if (atomic_read(&priv_tmp->wmm.tx_pkts_queued) != 0) {
+ atomic_set(&priv_tmp->wmm.highest_queued_prio,
+ HIGH_PRIO_TID);
+ /* Iterate current private once more, since
+ * there still exist packets in data queue
+ */
+ goto try_again;
+ } else {
+ atomic_set(&priv_tmp->wmm.highest_queued_prio,
+ NO_PKT_PRIO_TID);
+ }
+ }
+ }
+
+ return NULL;
+
+found:
+ /* holds ra_list_spinlock */
+ if (atomic_read(hqp) > i)
+ atomic_set(hqp, i);
+ spin_unlock_bh(&priv_tmp->wmm.ra_list_spinlock);
+
+ *priv = priv_tmp;
+ *tid = tos_to_tid[i];
+
+ return ptr;
+}
+
+/* This functions rotates ra and bss lists so packets are picked round robin.
+ *
+ * After a packet is successfully transmitted, rotate the ra list, so the ra
+ * next to the one transmitted, will come first in the list. This way we pick
+ * the ra' in a round robin fashion. Same applies to bss nodes of equal
+ * priority.
+ *
+ * Function also increments wmm.packets_out counter.
+ */
+void nxpwifi_rotate_priolists(struct nxpwifi_private *priv,
+ struct nxpwifi_ra_list_tbl *ra,
+ int tid)
+{
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_bss_prio_tbl *tbl = adapter->bss_prio_tbl;
+ struct nxpwifi_tid_tbl *tid_ptr = &priv->wmm.tid_tbl_ptr[tid];
+
+ spin_lock_bh(&tbl[priv->bss_priority].bss_prio_lock);
+ /* dirty trick: we remove 'head' temporarily and reinsert it after
+ * curr bss node. imagine list to stay fixed while head is moved
+ */
+ list_move(&tbl[priv->bss_priority].bss_prio_head,
+ &tbl[priv->bss_priority].bss_prio_cur->list);
+ spin_unlock_bh(&tbl[priv->bss_priority].bss_prio_lock);
+
+ spin_lock_bh(&priv->wmm.ra_list_spinlock);
+ if (nxpwifi_is_ralist_valid(priv, ra, tid)) {
+ priv->wmm.packets_out[tid]++;
+ /* same as above */
+ list_move(&tid_ptr->ra_list, &ra->list);
+ }
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+}
+
+/* This function checks if 11n aggregation is possible.
+ */
+static int
+nxpwifi_is_11n_aggragation_possible(struct nxpwifi_private *priv,
+ struct nxpwifi_ra_list_tbl *ptr,
+ int max_buf_size)
+{
+ int count = 0, total_size = 0;
+ struct sk_buff *skb, *tmp;
+ int max_amsdu_size;
+
+ if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP && priv->ap_11n_enabled &&
+ ptr->is_11n_enabled)
+ max_amsdu_size = min_t(int, ptr->max_amsdu, max_buf_size);
+ else
+ max_amsdu_size = max_buf_size;
+
+ skb_queue_walk_safe(&ptr->skb_head, skb, tmp) {
+ total_size += skb->len;
+ if (total_size >= max_amsdu_size)
+ break;
+ if (++count >= MIN_NUM_AMSDU)
+ return true;
+ }
+
+ return false;
+}
+
+/* This function sends a single packet to firmware for transmission.
+ */
+static void
+nxpwifi_send_single_packet(struct nxpwifi_private *priv,
+ struct nxpwifi_ra_list_tbl *ptr, int ptr_index)
+__releases(&priv->wmm.ra_list_spinlock)
+{
+ struct sk_buff *skb, *skb_next;
+ struct nxpwifi_tx_param tx_param;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ struct nxpwifi_txinfo *tx_info;
+
+ if (skb_queue_empty(&ptr->skb_head)) {
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ nxpwifi_dbg(adapter, DATA, "data: nothing to send\n");
+ return;
+ }
+
+ skb = skb_dequeue(&ptr->skb_head);
+
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+ nxpwifi_dbg(adapter, DATA,
+ "data: dequeuing the packet %p %p\n", ptr, skb);
+
+ ptr->total_pkt_count--;
+
+ if (!skb_queue_empty(&ptr->skb_head))
+ skb_next = skb_peek(&ptr->skb_head);
+ else
+ skb_next = NULL;
+
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+
+ tx_param.next_pkt_len = ((skb_next) ? skb_next->len +
+ sizeof(struct txpd) : 0);
+
+ if (nxpwifi_process_tx(priv, skb, &tx_param) == -EBUSY) {
+ /* Queue the packet back at the head */
+ spin_lock_bh(&priv->wmm.ra_list_spinlock);
+
+ if (!nxpwifi_is_ralist_valid(priv, ptr, ptr_index)) {
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ nxpwifi_write_data_complete(adapter, skb, 0, -1);
+ return;
+ }
+
+ skb_queue_tail(&ptr->skb_head, skb);
+
+ ptr->total_pkt_count++;
+ ptr->ba_pkt_count++;
+ tx_info->flags |= NXPWIFI_BUF_FLAG_REQUEUED_PKT;
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ } else {
+ nxpwifi_rotate_priolists(priv, ptr, ptr_index);
+ atomic_dec(&priv->wmm.tx_pkts_queued);
+ }
+}
+
+/* This function checks if the first packet in the given RA list
+ * is already processed or not.
+ */
+static int
+nxpwifi_is_ptr_processed(struct nxpwifi_private *priv,
+ struct nxpwifi_ra_list_tbl *ptr)
+{
+ struct sk_buff *skb;
+ struct nxpwifi_txinfo *tx_info;
+
+ if (skb_queue_empty(&ptr->skb_head))
+ return false;
+
+ skb = skb_peek(&ptr->skb_head);
+
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+ if (tx_info->flags & NXPWIFI_BUF_FLAG_REQUEUED_PKT)
+ return true;
+
+ return false;
+}
+
+/* This function sends a single processed packet to firmware for
+ * transmission.
+ */
+static void
+nxpwifi_send_processed_packet(struct nxpwifi_private *priv,
+ struct nxpwifi_ra_list_tbl *ptr, int ptr_index)
+ __releases(&priv->wmm.ra_list_spinlock)
+{
+ struct nxpwifi_tx_param tx_param;
+ struct nxpwifi_adapter *adapter = priv->adapter;
+ int ret = -1;
+ struct sk_buff *skb, *skb_next;
+ struct nxpwifi_txinfo *tx_info;
+
+ if (skb_queue_empty(&ptr->skb_head)) {
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ return;
+ }
+
+ skb = skb_dequeue(&ptr->skb_head);
+
+ if (adapter->data_sent || adapter->tx_lock_flag) {
+ ptr->total_pkt_count--;
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ skb_queue_tail(&adapter->tx_data_q, skb);
+ atomic_dec(&priv->wmm.tx_pkts_queued);
+ atomic_inc(&adapter->tx_queued);
+ return;
+ }
+
+ if (!skb_queue_empty(&ptr->skb_head))
+ skb_next = skb_peek(&ptr->skb_head);
+ else
+ skb_next = NULL;
+
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+
+ tx_param.next_pkt_len =
+ ((skb_next) ? skb_next->len +
+ sizeof(struct txpd) : 0);
+
+ ret = adapter->if_ops.host_to_card(adapter, NXPWIFI_TYPE_DATA,
+ skb, &tx_param);
+
+ switch (ret) {
+ case -EBUSY:
+ nxpwifi_dbg(adapter, ERROR, "data: -EBUSY is returned\n");
+ spin_lock_bh(&priv->wmm.ra_list_spinlock);
+
+ if (!nxpwifi_is_ralist_valid(priv, ptr, ptr_index)) {
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ nxpwifi_write_data_complete(adapter, skb, 0, -1);
+ return;
+ }
+
+ skb_queue_tail(&ptr->skb_head, skb);
+
+ tx_info->flags |= NXPWIFI_BUF_FLAG_REQUEUED_PKT;
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ break;
+ case -1:
+ nxpwifi_dbg(adapter, ERROR, "host_to_card failed: %#x\n", ret);
+ adapter->dbg.num_tx_host_to_card_failure++;
+ nxpwifi_write_data_complete(adapter, skb, 0, ret);
+ break;
+ case -EINPROGRESS:
+ break;
+ case 0:
+ nxpwifi_write_data_complete(adapter, skb, 0, ret);
+ break;
+ default:
+ break;
+ }
+ if (ret != -EBUSY) {
+ nxpwifi_rotate_priolists(priv, ptr, ptr_index);
+ atomic_dec(&priv->wmm.tx_pkts_queued);
+ spin_lock_bh(&priv->wmm.ra_list_spinlock);
+ ptr->total_pkt_count--;
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ }
+}
+
+/* This function dequeues a packet from the highest priority list
+ * and transmits it.
+ */
+static int
+nxpwifi_dequeue_tx_packet(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_ra_list_tbl *ptr;
+ struct nxpwifi_private *priv = NULL;
+ int ptr_index = 0;
+ u8 ra[ETH_ALEN];
+ int tid_del = 0, tid = 0;
+
+ ptr = nxpwifi_wmm_get_highest_priolist_ptr(adapter, &priv, &ptr_index);
+ if (!ptr)
+ return -1;
+
+ tid = nxpwifi_get_tid(ptr);
+
+ nxpwifi_dbg(adapter, DATA, "data: tid=%d\n", tid);
+
+ spin_lock_bh(&priv->wmm.ra_list_spinlock);
+ if (!nxpwifi_is_ralist_valid(priv, ptr, ptr_index)) {
+ spin_unlock_bh(&priv->wmm.ra_list_spinlock);
+ return -1;
+ }
+
+ if (nxpwifi_is_ptr_processed(priv, ptr)) {
+ nxpwifi_send_processed_packet(priv, ptr, ptr_index);
+ /* ra_list_spinlock has been freed in
+ * nxpwifi_send_processed_packet()
+ */
+ return 0;
+ }
+
+ if (!ptr->is_11n_enabled ||
+ ptr->ba_status ||
+ priv->wps.session_enable) {
+ if (ptr->is_11n_enabled &&
+ ptr->ba_status &&
+ ptr->amsdu_in_ampdu &&
+ nxpwifi_is_amsdu_allowed(priv, tid) &&
+ nxpwifi_is_11n_aggragation_possible(priv, ptr,
+ adapter->tx_buf_size))
+ nxpwifi_11n_aggregate_pkt(priv, ptr, ptr_index);
+ /* ra_list_spinlock has been freed in
+ * nxpwifi_11n_aggregate_pkt()
+ */
+ else
+ nxpwifi_send_single_packet(priv, ptr, ptr_index);
+ /* ra_list_spinlock has been freed in
+ * nxpwifi_send_single_packet()
+ */
+ } else {
+ if (nxpwifi_is_ampdu_allowed(priv, ptr, tid) &&
+ ptr->ba_pkt_count > ptr->ba_packet_thr) {
+ if (nxpwifi_space_avail_for_new_ba_stream(adapter)) {
+ nxpwifi_create_ba_tbl(priv, ptr->ra, tid,
+ BA_SETUP_INPROGRESS);
+ nxpwifi_send_addba(priv, tid, ptr->ra);
+ } else if (nxpwifi_find_stream_to_delete
+ (priv, tid, &tid_del, ra)) {
+ nxpwifi_create_ba_tbl(priv, ptr->ra, tid,
+ BA_SETUP_INPROGRESS);
+ nxpwifi_send_delba(priv, tid_del, ra, 1);
+ }
+ }
+ if (nxpwifi_is_amsdu_allowed(priv, tid) &&
+ nxpwifi_is_11n_aggragation_possible(priv, ptr,
+ adapter->tx_buf_size))
+ nxpwifi_11n_aggregate_pkt(priv, ptr, ptr_index);
+ /* ra_list_spinlock has been freed in
+ * nxpwifi_11n_aggregate_pkt()
+ */
+ else
+ nxpwifi_send_single_packet(priv, ptr, ptr_index);
+ /* ra_list_spinlock has been freed in
+ * nxpwifi_send_single_packet()
+ */
+ }
+ return 0;
+}
+
+void nxpwifi_process_bypass_tx(struct nxpwifi_adapter *adapter)
+{
+ struct nxpwifi_tx_param tx_param;
+ struct sk_buff *skb;
+ struct nxpwifi_txinfo *tx_info;
+ struct nxpwifi_private *priv;
+ int i;
+
+ if (adapter->data_sent || adapter->tx_lock_flag)
+ return;
+
+ for (i = 0; i < adapter->priv_num; ++i) {
+ priv = adapter->priv[i];
+
+ if (!priv)
+ continue;
+
+ if (adapter->if_ops.is_port_ready &&
+ !adapter->if_ops.is_port_ready(priv))
+ continue;
+
+ if (skb_queue_empty(&priv->bypass_txq))
+ continue;
+
+ skb = skb_dequeue(&priv->bypass_txq);
+ tx_info = NXPWIFI_SKB_TXCB(skb);
+
+ /* no aggregation for bypass packets */
+ tx_param.next_pkt_len = 0;
+
+ if (nxpwifi_process_tx(priv, skb, &tx_param) == -EBUSY) {
+ skb_queue_head(&priv->bypass_txq, skb);
+ tx_info->flags |= NXPWIFI_BUF_FLAG_REQUEUED_PKT;
+ } else {
+ atomic_dec(&adapter->bypass_tx_pending);
+ }
+ }
+}
+
+/* This function transmits the highest priority packet awaiting in the
+ * WMM Queues.
+ */
+void
+nxpwifi_wmm_process_tx(struct nxpwifi_adapter *adapter)
+{
+ do {
+ if (nxpwifi_dequeue_tx_packet(adapter))
+ break;
+ if (adapter->iface_type != NXPWIFI_SDIO) {
+ if (adapter->data_sent ||
+ adapter->tx_lock_flag)
+ break;
+ } else {
+ if (atomic_read(&adapter->tx_queued) >=
+ NXPWIFI_MAX_PKTS_TXQ)
+ break;
+ }
+ } while (!nxpwifi_wmm_lists_empty(adapter));
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 40/43] wifi: nxpwifi: add wmm.h
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (38 preceding siblings ...)
2024-06-21 7:52 ` [PATCH 39/43] wifi: nxpwifi: add wmm.c David Lin
@ 2024-06-21 7:52 ` David Lin
2024-06-21 7:52 ` [PATCH 41/43] wifi: nxpwifi: add nxp sdio vendor id and iw61x device id David Lin
` (4 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:52 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/nxp/nxpwifi/wmm.h | 95 ++++++++++++++++++++++++++
1 file changed, 95 insertions(+)
create mode 100644 drivers/net/wireless/nxp/nxpwifi/wmm.h
diff --git a/drivers/net/wireless/nxp/nxpwifi/wmm.h b/drivers/net/wireless/nxp/nxpwifi/wmm.h
new file mode 100644
index 000000000000..e1f8a1c80a2f
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/wmm.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NXP Wireless LAN device driver: WMM
+ *
+ * Copyright 2011-2024 NXP
+ */
+
+#ifndef _NXPWIFI_WMM_H_
+#define _NXPWIFI_WMM_H_
+
+enum ieee_types_wmm_aciaifsn_bitmasks {
+ NXPWIFI_AIFSN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)),
+ NXPWIFI_ACM = BIT(4),
+ NXPWIFI_ACI = (BIT(5) | BIT(6)),
+};
+
+enum ieee_types_wmm_ecw_bitmasks {
+ NXPWIFI_ECW_MIN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)),
+ NXPWIFI_ECW_MAX = (BIT(4) | BIT(5) | BIT(6) | BIT(7)),
+};
+
+extern const u16 nxpwifi_1d_to_wmm_queue[];
+extern const u8 tos_to_tid_inv[];
+
+/* This function retrieves the TID of the given RA list.
+ */
+static inline int
+nxpwifi_get_tid(struct nxpwifi_ra_list_tbl *ptr)
+{
+ struct sk_buff *skb;
+
+ if (skb_queue_empty(&ptr->skb_head))
+ return 0;
+
+ skb = skb_peek(&ptr->skb_head);
+
+ return skb->priority;
+}
+
+/* This function checks if a RA list is empty or not.
+ */
+static inline u8
+nxpwifi_wmm_is_ra_list_empty(struct list_head *ra_list_hhead)
+{
+ struct nxpwifi_ra_list_tbl *ra_list;
+ int is_list_empty;
+
+ list_for_each_entry(ra_list, ra_list_hhead, list) {
+ is_list_empty = skb_queue_empty(&ra_list->skb_head);
+ if (!is_list_empty)
+ return false;
+ }
+
+ return true;
+}
+
+void nxpwifi_wmm_add_buf_txqueue(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+void nxpwifi_wmm_add_buf_bypass_txqueue(struct nxpwifi_private *priv,
+ struct sk_buff *skb);
+void nxpwifi_ralist_add(struct nxpwifi_private *priv, const u8 *ra);
+void nxpwifi_rotate_priolists(struct nxpwifi_private *priv,
+ struct nxpwifi_ra_list_tbl *ra, int tid);
+
+int nxpwifi_wmm_lists_empty(struct nxpwifi_adapter *adapter);
+int nxpwifi_bypass_txlist_empty(struct nxpwifi_adapter *adapter);
+void nxpwifi_wmm_process_tx(struct nxpwifi_adapter *adapter);
+void nxpwifi_process_bypass_tx(struct nxpwifi_adapter *adapter);
+int nxpwifi_is_ralist_valid(struct nxpwifi_private *priv,
+ struct nxpwifi_ra_list_tbl *ra_list, int tid);
+
+u8 nxpwifi_wmm_compute_drv_pkt_delay(struct nxpwifi_private *priv,
+ const struct sk_buff *skb);
+void nxpwifi_wmm_init(struct nxpwifi_adapter *adapter);
+
+u32 nxpwifi_wmm_process_association_req(struct nxpwifi_private *priv,
+ u8 **assoc_buf,
+ struct ieee_types_wmm_parameter *wmmie,
+ struct ieee80211_ht_cap *htcap);
+
+void nxpwifi_wmm_setup_queue_priorities(struct nxpwifi_private *priv,
+ struct ieee_types_wmm_parameter *wmm_ie);
+void nxpwifi_wmm_setup_ac_downgrade(struct nxpwifi_private *priv);
+int nxpwifi_ret_wmm_get_status(struct nxpwifi_private *priv,
+ const struct host_cmd_ds_command *resp);
+struct nxpwifi_ra_list_tbl *
+nxpwifi_wmm_get_queue_raptr(struct nxpwifi_private *priv, u8 tid,
+ const u8 *ra_addr);
+u8 nxpwifi_wmm_downgrade_tid(struct nxpwifi_private *priv, u32 tid);
+void nxpwifi_update_ralist_tx_pause(struct nxpwifi_private *priv, u8 *mac,
+ u8 tx_pause);
+
+struct nxpwifi_ra_list_tbl *nxpwifi_wmm_get_ralist_node(struct nxpwifi_private
+ *priv, u8 tid, const u8 *ra_addr);
+#endif /* !_NXPWIFI_WMM_H_ */
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 41/43] wifi: nxpwifi: add nxp sdio vendor id and iw61x device id
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (39 preceding siblings ...)
2024-06-21 7:52 ` [PATCH 40/43] wifi: nxpwifi: add wmm.h David Lin
@ 2024-06-21 7:52 ` David Lin
2024-06-21 7:52 ` [PATCH 42/43] wifi: nxpwifi: add Makefile and Kconfig files for nxpwifi compilation David Lin
` (3 subsequent siblings)
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:52 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
include/linux/mmc/sdio_ids.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
index 7cddfdac2f57..8446841d62ef 100644
--- a/include/linux/mmc/sdio_ids.h
+++ b/include/linux/mmc/sdio_ids.h
@@ -115,6 +115,9 @@
#define SDIO_VENDOR_ID_MICROCHIP_WILC 0x0296
#define SDIO_DEVICE_ID_MICROCHIP_WILC1000 0x5347
+#define SDIO_VENDOR_ID_NXP 0x0471
+#define SDIO_DEVICE_ID_NXP_IW61X 0x0205
+
#define SDIO_VENDOR_ID_REALTEK 0x024c
#define SDIO_DEVICE_ID_REALTEK_RTW8723BS 0xb723
#define SDIO_DEVICE_ID_REALTEK_RTW8821BS 0xb821
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* [PATCH 42/43] wifi: nxpwifi: add Makefile and Kconfig files for nxpwifi compilation
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (40 preceding siblings ...)
2024-06-21 7:52 ` [PATCH 41/43] wifi: nxpwifi: add nxp sdio vendor id and iw61x device id David Lin
@ 2024-06-21 7:52 ` David Lin
2024-06-26 0:19 ` kernel test robot
` (2 more replies)
2024-06-21 7:52 ` [PATCH 43/43] wifi: nxpwifi: add nxpwifi related information to MAINTAINERS David Lin
` (2 subsequent siblings)
44 siblings, 3 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:52 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
drivers/net/wireless/Kconfig | 1 +
drivers/net/wireless/Makefile | 1 +
drivers/net/wireless/nxp/Kconfig | 17 ++++++++++
drivers/net/wireless/nxp/Makefile | 3 ++
drivers/net/wireless/nxp/nxpwifi/Kconfig | 22 +++++++++++++
drivers/net/wireless/nxp/nxpwifi/Makefile | 38 +++++++++++++++++++++++
6 files changed, 82 insertions(+)
create mode 100644 drivers/net/wireless/nxp/Kconfig
create mode 100644 drivers/net/wireless/nxp/Makefile
create mode 100644 drivers/net/wireless/nxp/nxpwifi/Kconfig
create mode 100644 drivers/net/wireless/nxp/nxpwifi/Makefile
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index c6599594dc99..4d7b81182925 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -27,6 +27,7 @@ source "drivers/net/wireless/intersil/Kconfig"
source "drivers/net/wireless/marvell/Kconfig"
source "drivers/net/wireless/mediatek/Kconfig"
source "drivers/net/wireless/microchip/Kconfig"
+source "drivers/net/wireless/nxp/Kconfig"
source "drivers/net/wireless/purelifi/Kconfig"
source "drivers/net/wireless/ralink/Kconfig"
source "drivers/net/wireless/realtek/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index e1c4141c6004..0c6b3cc719db 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/
obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/
obj-$(CONFIG_WLAN_VENDOR_MEDIATEK) += mediatek/
obj-$(CONFIG_WLAN_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_WLAN_VENDOR_NXP) += nxp/
obj-$(CONFIG_WLAN_VENDOR_PURELIFI) += purelifi/
obj-$(CONFIG_WLAN_VENDOR_QUANTENNA) += quantenna/
obj-$(CONFIG_WLAN_VENDOR_RALINK) += ralink/
diff --git a/drivers/net/wireless/nxp/Kconfig b/drivers/net/wireless/nxp/Kconfig
new file mode 100644
index 000000000000..68b32d4536e5
--- /dev/null
+++ b/drivers/net/wireless/nxp/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_NXP
+ bool "NXP devices"
+ default y
+ help
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_NXP
+
+source "drivers/net/wireless/nxp/nxpwifi/Kconfig"
+
+endif # WLAN_VENDOR_NXP
diff --git a/drivers/net/wireless/nxp/Makefile b/drivers/net/wireless/nxp/Makefile
new file mode 100644
index 000000000000..27b41a0afdd2
--- /dev/null
+++ b/drivers/net/wireless/nxp/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_NXPWIFI) += nxpwifi/
diff --git a/drivers/net/wireless/nxp/nxpwifi/Kconfig b/drivers/net/wireless/nxp/nxpwifi/Kconfig
new file mode 100644
index 000000000000..3637068574b8
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config NXPWIFI
+ tristate "NXP WiFi Driver"
+ depends on CFG80211
+ help
+ This adds support for wireless adapters based on NXP
+ 802.11n/ac chipsets.
+
+ If you choose to build it as a module, it will be called
+ nxpwifi.
+
+config NXPWIFI_SDIO
+ tristate "NXP WiFi Driver for IW61x"
+ depends on NXPWIFI && MMC
+ select FW_LOADER
+ select WANT_DEV_COREDUMP
+ help
+ This adds support for wireless adapters based on NXP
+ IW61x interface.
+
+ If you choose to build it as a module, it will be called
+ nxpwifi_sdio.
diff --git a/drivers/net/wireless/nxp/nxpwifi/Makefile b/drivers/net/wireless/nxp/nxpwifi/Makefile
new file mode 100644
index 000000000000..a9e5a528324b
--- /dev/null
+++ b/drivers/net/wireless/nxp/nxpwifi/Makefile
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright 2011-2020 NXP
+#
+
+
+nxpwifi-y += main.o
+nxpwifi-y += init.o
+nxpwifi-y += cfp.o
+nxpwifi-y += cmdevt.o
+nxpwifi-y += util.o
+nxpwifi-y += txrx.o
+nxpwifi-y += wmm.o
+nxpwifi-y += 11n.o
+nxpwifi-y += 11ac.o
+nxpwifi-y += 11n_aggr.o
+nxpwifi-y += 11n_rxreorder.o
+nxpwifi-y += scan.o
+nxpwifi-y += join.o
+nxpwifi-y += sta_ioctl.o
+nxpwifi-y += sta_cmd.o
+nxpwifi-y += uap_cmd.o
+nxpwifi-y += ie.o
+nxpwifi-y += sta_event.o
+nxpwifi-y += uap_event.o
+nxpwifi-y += sta_tx.o
+nxpwifi-y += sta_rx.o
+nxpwifi-y += uap_txrx.o
+nxpwifi-y += cfg80211.o
+nxpwifi-y += ethtool.o
+nxpwifi-y += 11h.o
+nxpwifi-$(CONFIG_DEBUG_FS) += debugfs.o
+obj-$(CONFIG_NXPWIFI) += nxpwifi.o
+
+nxpwifi_sdio-y += sdio.o
+obj-$(CONFIG_NXPWIFI_SDIO) += nxpwifi_sdio.o
+
+ccflags-y += -D__CHECK_ENDIAN
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* Re: [PATCH 42/43] wifi: nxpwifi: add Makefile and Kconfig files for nxpwifi compilation
2024-06-21 7:52 ` [PATCH 42/43] wifi: nxpwifi: add Makefile and Kconfig files for nxpwifi compilation David Lin
@ 2024-06-26 0:19 ` kernel test robot
2024-06-26 0:24 ` kernel test robot
2024-06-28 4:46 ` kernel test robot
2 siblings, 0 replies; 56+ messages in thread
From: kernel test robot @ 2024-06-26 0:19 UTC (permalink / raw)
To: David Lin, linux-wireless
Cc: llvm, oe-kbuild-all, linux-kernel, briannorris, kvalo, francesco,
tsung-hsien.hsieh, David Lin
Hi David,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 238d636723a30311e20fde0a361662e829fe488b]
url: https://github.com/intel-lab-lkp/linux/commits/David-Lin/wifi-nxpwifi-add-11ac-c/20240625-161306
base: 238d636723a30311e20fde0a361662e829fe488b
patch link: https://lore.kernel.org/r/20240621075208.513497-43-yu-hao.lin%40nxp.com
patch subject: [PATCH 42/43] wifi: nxpwifi: add Makefile and Kconfig files for nxpwifi compilation
config: x86_64-allyesconfig (https://download.01.org/0day-ci/archive/20240626/202406260755.fqLnsFhr-lkp@intel.com/config)
compiler: clang version 18.1.5 (https://github.com/llvm/llvm-project 617a15a9eac96088ae5e9134248d8236e34b91b1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240626/202406260755.fqLnsFhr-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202406260755.fqLnsFhr-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/net/wireless/nxp/nxpwifi/11h.c:208:7: warning: variable 'chan2_offset' is used uninitialized whenever switch case is taken [-Wsometimes-uninitialized]
208 | case NL80211_CHAN_WIDTH_80P80:
| ^~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:214:16: note: uninitialized use occurs here
214 | *band_cfg = ((chan2_offset << BAND_CFG_CHAN2_SHIFT_BIT) &
| ^~~~~~~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:209:7: warning: variable 'chan2_offset' is used uninitialized whenever switch case is taken [-Wsometimes-uninitialized]
209 | case NL80211_CHAN_WIDTH_160:
| ^~~~~~~~~~~~~~~~~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:214:16: note: uninitialized use occurs here
214 | *band_cfg = ((chan2_offset << BAND_CFG_CHAN2_SHIFT_BIT) &
| ^~~~~~~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:192:7: warning: variable 'chan2_offset' is used uninitialized whenever switch case is taken [-Wsometimes-uninitialized]
192 | case NL80211_CHAN_WIDTH_20_NOHT:
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:214:16: note: uninitialized use occurs here
214 | *band_cfg = ((chan2_offset << BAND_CFG_CHAN2_SHIFT_BIT) &
| ^~~~~~~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:193:7: warning: variable 'chan2_offset' is used uninitialized whenever switch case is taken [-Wsometimes-uninitialized]
193 | case NL80211_CHAN_WIDTH_20:
| ^~~~~~~~~~~~~~~~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:214:16: note: uninitialized use occurs here
214 | *band_cfg = ((chan2_offset << BAND_CFG_CHAN2_SHIFT_BIT) &
| ^~~~~~~~~~~~
>> drivers/net/wireless/nxp/nxpwifi/11h.c:210:2: warning: variable 'chan2_offset' is used uninitialized whenever switch default is taken [-Wsometimes-uninitialized]
210 | default:
| ^~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:214:16: note: uninitialized use occurs here
214 | *band_cfg = ((chan2_offset << BAND_CFG_CHAN2_SHIFT_BIT) &
| ^~~~~~~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:178:40: note: initialize the variable 'chan2_offset' to silence this warning
178 | u8 chan_band, chan_width, chan2_offset;
| ^
| = '\0'
>> drivers/net/wireless/nxp/nxpwifi/11h.c:208:7: warning: variable 'chan_width' is used uninitialized whenever switch case is taken [-Wsometimes-uninitialized]
208 | case NL80211_CHAN_WIDTH_80P80:
| ^~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:216:9: note: uninitialized use occurs here
216 | ((chan_width << BAND_CFG_CHAN_WIDTH_SHIFT_BIT) &
| ^~~~~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:209:7: warning: variable 'chan_width' is used uninitialized whenever switch case is taken [-Wsometimes-uninitialized]
209 | case NL80211_CHAN_WIDTH_160:
| ^~~~~~~~~~~~~~~~~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:216:9: note: uninitialized use occurs here
216 | ((chan_width << BAND_CFG_CHAN_WIDTH_SHIFT_BIT) &
| ^~~~~~~~~~
>> drivers/net/wireless/nxp/nxpwifi/11h.c:210:2: warning: variable 'chan_width' is used uninitialized whenever switch default is taken [-Wsometimes-uninitialized]
210 | default:
| ^~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:216:9: note: uninitialized use occurs here
216 | ((chan_width << BAND_CFG_CHAN_WIDTH_SHIFT_BIT) &
| ^~~~~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:178:26: note: initialize the variable 'chan_width' to silence this warning
178 | u8 chan_band, chan_width, chan2_offset;
| ^
| = '\0'
>> drivers/net/wireless/nxp/nxpwifi/11h.c:187:2: warning: variable 'chan_band' is used uninitialized whenever switch default is taken [-Wsometimes-uninitialized]
187 | default:
| ^~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:218:9: note: uninitialized use occurs here
218 | ((chan_band << BAND_CFG_CHAN_BAND_SHIFT_BIT) &
| ^~~~~~~~~
drivers/net/wireless/nxp/nxpwifi/11h.c:178:14: note: initialize the variable 'chan_band' to silence this warning
178 | u8 chan_band, chan_width, chan2_offset;
| ^
| = '\0'
9 warnings generated.
vim +/chan2_offset +208 drivers/net/wireless/nxp/nxpwifi/11h.c
647388e635d138 David Lin 2024-06-21 174
647388e635d138 David Lin 2024-06-21 175 static void nxpwifi_convert_chan_to_band_cfg(u8 *band_cfg,
647388e635d138 David Lin 2024-06-21 176 struct cfg80211_chan_def *chan_def)
647388e635d138 David Lin 2024-06-21 177 {
647388e635d138 David Lin 2024-06-21 178 u8 chan_band, chan_width, chan2_offset;
647388e635d138 David Lin 2024-06-21 179
647388e635d138 David Lin 2024-06-21 180 switch (chan_def->chan->band) {
647388e635d138 David Lin 2024-06-21 181 case NL80211_BAND_2GHZ:
647388e635d138 David Lin 2024-06-21 182 chan_band = BAND_2GHZ;
647388e635d138 David Lin 2024-06-21 183 break;
647388e635d138 David Lin 2024-06-21 184 case NL80211_BAND_5GHZ:
647388e635d138 David Lin 2024-06-21 185 chan_band = BAND_5GHZ;
647388e635d138 David Lin 2024-06-21 186 break;
647388e635d138 David Lin 2024-06-21 @187 default:
647388e635d138 David Lin 2024-06-21 188 break;
647388e635d138 David Lin 2024-06-21 189 }
647388e635d138 David Lin 2024-06-21 190
647388e635d138 David Lin 2024-06-21 191 switch (chan_def->width) {
647388e635d138 David Lin 2024-06-21 192 case NL80211_CHAN_WIDTH_20_NOHT:
647388e635d138 David Lin 2024-06-21 193 case NL80211_CHAN_WIDTH_20:
647388e635d138 David Lin 2024-06-21 194 chan_width = CHAN_BW_20MHZ;
647388e635d138 David Lin 2024-06-21 195 break;
647388e635d138 David Lin 2024-06-21 196 case NL80211_CHAN_WIDTH_40:
647388e635d138 David Lin 2024-06-21 197 chan_width = CHAN_BW_40MHZ;
647388e635d138 David Lin 2024-06-21 198 if (chan_def->center_freq1 > chan_def->chan->center_freq)
647388e635d138 David Lin 2024-06-21 199 chan2_offset = SEC_CHAN_ABOVE;
647388e635d138 David Lin 2024-06-21 200 else
647388e635d138 David Lin 2024-06-21 201 chan2_offset = SEC_CHAN_BELOW;
647388e635d138 David Lin 2024-06-21 202 break;
647388e635d138 David Lin 2024-06-21 203 case NL80211_CHAN_WIDTH_80:
647388e635d138 David Lin 2024-06-21 204 chan2_offset =
647388e635d138 David Lin 2024-06-21 205 nxpwifi_get_channel_2_offset(chan_def->chan->hw_value);
647388e635d138 David Lin 2024-06-21 206 chan_width = CHAN_BW_80MHZ;
647388e635d138 David Lin 2024-06-21 207 break;
647388e635d138 David Lin 2024-06-21 @208 case NL80211_CHAN_WIDTH_80P80:
647388e635d138 David Lin 2024-06-21 209 case NL80211_CHAN_WIDTH_160:
647388e635d138 David Lin 2024-06-21 @210 default:
647388e635d138 David Lin 2024-06-21 211 break;
647388e635d138 David Lin 2024-06-21 212 }
647388e635d138 David Lin 2024-06-21 213
647388e635d138 David Lin 2024-06-21 @214 *band_cfg = ((chan2_offset << BAND_CFG_CHAN2_SHIFT_BIT) &
647388e635d138 David Lin 2024-06-21 215 BAND_CFG_CHAN2_OFFSET_MASK) |
647388e635d138 David Lin 2024-06-21 @216 ((chan_width << BAND_CFG_CHAN_WIDTH_SHIFT_BIT) &
647388e635d138 David Lin 2024-06-21 217 BAND_CFG_CHAN_WIDTH_MASK) |
647388e635d138 David Lin 2024-06-21 218 ((chan_band << BAND_CFG_CHAN_BAND_SHIFT_BIT) &
647388e635d138 David Lin 2024-06-21 219 BAND_CFG_CHAN_BAND_MASK);
647388e635d138 David Lin 2024-06-21 220 }
647388e635d138 David Lin 2024-06-21 221
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 56+ messages in thread* Re: [PATCH 42/43] wifi: nxpwifi: add Makefile and Kconfig files for nxpwifi compilation
2024-06-21 7:52 ` [PATCH 42/43] wifi: nxpwifi: add Makefile and Kconfig files for nxpwifi compilation David Lin
2024-06-26 0:19 ` kernel test robot
@ 2024-06-26 0:24 ` kernel test robot
2024-06-28 4:46 ` kernel test robot
2 siblings, 0 replies; 56+ messages in thread
From: kernel test robot @ 2024-06-26 0:24 UTC (permalink / raw)
To: David Lin, linux-wireless
Cc: oe-kbuild-all, linux-kernel, briannorris, kvalo, francesco,
tsung-hsien.hsieh, David Lin
Hi David,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 238d636723a30311e20fde0a361662e829fe488b]
url: https://github.com/intel-lab-lkp/linux/commits/David-Lin/wifi-nxpwifi-add-11ac-c/20240625-161306
base: 238d636723a30311e20fde0a361662e829fe488b
patch link: https://lore.kernel.org/r/20240621075208.513497-43-yu-hao.lin%40nxp.com
patch subject: [PATCH 42/43] wifi: nxpwifi: add Makefile and Kconfig files for nxpwifi compilation
config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20240626/202406260848.0pH4xjvI-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240626/202406260848.0pH4xjvI-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202406260848.0pH4xjvI-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c: In function 'nxpwifi_11n_dispatch_amsdu_pkt':
>> drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c:40:47: warning: variable 'rx_hdr' set but not used [-Wunused-but-set-variable]
40 | struct rx_packet_hdr *rx_hdr;
| ^~~~~~
--
drivers/net/wireless/nxp/nxpwifi/sta_event.c: In function 'nxpwifi_sta_event_link_lost':
>> drivers/net/wireless/nxp/nxpwifi/sta_event.c:21:13: warning: variable 'reason_code' set but not used [-Wunused-but-set-variable]
21 | u16 reason_code;
| ^~~~~~~~~~~
--
drivers/net/wireless/nxp/nxpwifi/sta_rx.c: In function 'nxpwifi_process_rx_packet':
>> drivers/net/wireless/nxp/nxpwifi/sta_rx.c:78:25: warning: variable 'rx_pkt_len' set but not used [-Wunused-but-set-variable]
78 | u16 rx_pkt_off, rx_pkt_len;
| ^~~~~~~~~~
vim +/rx_hdr +40 drivers/net/wireless/nxp/nxpwifi/11n_rxreorder.c
148be2798f7a0c David Lin 2024-06-21 17
148be2798f7a0c David Lin 2024-06-21 18 /* This function will dispatch amsdu packet and forward it to kernel/upper
148be2798f7a0c David Lin 2024-06-21 19 * layer.
148be2798f7a0c David Lin 2024-06-21 20 */
148be2798f7a0c David Lin 2024-06-21 21 static int nxpwifi_11n_dispatch_amsdu_pkt(struct nxpwifi_private *priv,
148be2798f7a0c David Lin 2024-06-21 22 struct sk_buff *skb)
148be2798f7a0c David Lin 2024-06-21 23 {
148be2798f7a0c David Lin 2024-06-21 24 struct rxpd *local_rx_pd = (struct rxpd *)(skb->data);
148be2798f7a0c David Lin 2024-06-21 25 int ret;
148be2798f7a0c David Lin 2024-06-21 26
148be2798f7a0c David Lin 2024-06-21 27 if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) {
148be2798f7a0c David Lin 2024-06-21 28 struct sk_buff_head list;
148be2798f7a0c David Lin 2024-06-21 29 struct sk_buff *rx_skb;
148be2798f7a0c David Lin 2024-06-21 30
148be2798f7a0c David Lin 2024-06-21 31 __skb_queue_head_init(&list);
148be2798f7a0c David Lin 2024-06-21 32
148be2798f7a0c David Lin 2024-06-21 33 skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset));
148be2798f7a0c David Lin 2024-06-21 34 skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
148be2798f7a0c David Lin 2024-06-21 35
148be2798f7a0c David Lin 2024-06-21 36 ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
148be2798f7a0c David Lin 2024-06-21 37 priv->wdev.iftype, 0, NULL, NULL, false);
148be2798f7a0c David Lin 2024-06-21 38
148be2798f7a0c David Lin 2024-06-21 39 while (!skb_queue_empty(&list)) {
148be2798f7a0c David Lin 2024-06-21 @40 struct rx_packet_hdr *rx_hdr;
148be2798f7a0c David Lin 2024-06-21 41
148be2798f7a0c David Lin 2024-06-21 42 rx_skb = __skb_dequeue(&list);
148be2798f7a0c David Lin 2024-06-21 43 rx_hdr = (struct rx_packet_hdr *)rx_skb->data;
148be2798f7a0c David Lin 2024-06-21 44
148be2798f7a0c David Lin 2024-06-21 45 if (priv->bss_role == NXPWIFI_BSS_ROLE_UAP)
148be2798f7a0c David Lin 2024-06-21 46 ret = nxpwifi_uap_recv_packet(priv, rx_skb);
148be2798f7a0c David Lin 2024-06-21 47 else
148be2798f7a0c David Lin 2024-06-21 48 ret = nxpwifi_recv_packet(priv, rx_skb);
148be2798f7a0c David Lin 2024-06-21 49 if (ret == -1)
148be2798f7a0c David Lin 2024-06-21 50 nxpwifi_dbg(priv->adapter, ERROR,
148be2798f7a0c David Lin 2024-06-21 51 "Rx of A-MSDU failed");
148be2798f7a0c David Lin 2024-06-21 52 }
148be2798f7a0c David Lin 2024-06-21 53 return 0;
148be2798f7a0c David Lin 2024-06-21 54 }
148be2798f7a0c David Lin 2024-06-21 55
148be2798f7a0c David Lin 2024-06-21 56 return -1;
148be2798f7a0c David Lin 2024-06-21 57 }
148be2798f7a0c David Lin 2024-06-21 58
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 56+ messages in thread* Re: [PATCH 42/43] wifi: nxpwifi: add Makefile and Kconfig files for nxpwifi compilation
2024-06-21 7:52 ` [PATCH 42/43] wifi: nxpwifi: add Makefile and Kconfig files for nxpwifi compilation David Lin
2024-06-26 0:19 ` kernel test robot
2024-06-26 0:24 ` kernel test robot
@ 2024-06-28 4:46 ` kernel test robot
2 siblings, 0 replies; 56+ messages in thread
From: kernel test robot @ 2024-06-28 4:46 UTC (permalink / raw)
To: David Lin, linux-wireless
Cc: oe-kbuild-all, linux-kernel, briannorris, kvalo, francesco,
tsung-hsien.hsieh, David Lin
Hi David,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 238d636723a30311e20fde0a361662e829fe488b]
url: https://github.com/intel-lab-lkp/linux/commits/David-Lin/wifi-nxpwifi-add-11ac-c/20240625-161306
base: 238d636723a30311e20fde0a361662e829fe488b
patch link: https://lore.kernel.org/r/20240621075208.513497-43-yu-hao.lin%40nxp.com
patch subject: [PATCH 42/43] wifi: nxpwifi: add Makefile and Kconfig files for nxpwifi compilation
config: i386-randconfig-062-20240628 (https://download.01.org/0day-ci/archive/20240628/202406281235.idQnZtIJ-lkp@intel.com/config)
compiler: gcc-13 (Ubuntu 13.2.0-4ubuntu3) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240628/202406281235.idQnZtIJ-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202406281235.idQnZtIJ-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
>> drivers/net/wireless/nxp/nxpwifi/uap_cmd.c:541:19: sparse: sparse: incorrect type in assignment (different base types) @@ expected restricted __le16 [usertype] size @@ got unsigned int @@
drivers/net/wireless/nxp/nxpwifi/uap_cmd.c:541:19: sparse: expected restricted __le16 [usertype] size
drivers/net/wireless/nxp/nxpwifi/uap_cmd.c:541:19: sparse: got unsigned int
>> drivers/net/wireless/nxp/nxpwifi/uap_cmd.c:554:29: sparse: sparse: cast from restricted __le16
>> drivers/net/wireless/nxp/nxpwifi/uap_cmd.c:573:19: sparse: sparse: bad assignment (+=) to restricted __le16
drivers/net/wireless/nxp/nxpwifi/uap_cmd.c:584:27: sparse: sparse: bad assignment (+=) to restricted __le16
drivers/net/wireless/nxp/nxpwifi/uap_cmd.c:596:27: sparse: sparse: bad assignment (+=) to restricted __le16
drivers/net/wireless/nxp/nxpwifi/uap_cmd.c:609:27: sparse: sparse: bad assignment (+=) to restricted __le16
drivers/net/wireless/nxp/nxpwifi/uap_cmd.c:622:27: sparse: sparse: bad assignment (+=) to restricted __le16
drivers/net/wireless/nxp/nxpwifi/uap_cmd.c:640:27: sparse: sparse: bad assignment (+=) to restricted __le16
drivers/net/wireless/nxp/nxpwifi/uap_cmd.c:653:27: sparse: sparse: bad assignment (+=) to restricted __le16
drivers/net/wireless/nxp/nxpwifi/uap_cmd.c:666:21: sparse: sparse: cast from restricted __le16
vim +541 drivers/net/wireless/nxp/nxpwifi/uap_cmd.c
309a0039e40257 David Lin 2024-06-21 521
309a0039e40257 David Lin 2024-06-21 522 /* This function prepares AP specific add station command.
309a0039e40257 David Lin 2024-06-21 523 */
309a0039e40257 David Lin 2024-06-21 524 static int
309a0039e40257 David Lin 2024-06-21 525 nxpwifi_cmd_uap_add_new_station(struct nxpwifi_private *priv,
309a0039e40257 David Lin 2024-06-21 526 struct host_cmd_ds_command *cmd,
309a0039e40257 David Lin 2024-06-21 527 u16 cmd_no, void *data_buf,
309a0039e40257 David Lin 2024-06-21 528 u16 cmd_action, u32 cmd_type)
309a0039e40257 David Lin 2024-06-21 529 {
309a0039e40257 David Lin 2024-06-21 530 struct host_cmd_ds_add_station *new_sta = &cmd->params.sta_info;
309a0039e40257 David Lin 2024-06-21 531 struct nxpwifi_sta_info *add_sta = (struct nxpwifi_sta_info *)data_buf;
309a0039e40257 David Lin 2024-06-21 532 struct station_parameters *params = add_sta->params;
309a0039e40257 David Lin 2024-06-21 533 struct nxpwifi_sta_node *sta_ptr;
309a0039e40257 David Lin 2024-06-21 534 u8 *pos, *cmd_end;
309a0039e40257 David Lin 2024-06-21 535 u16 tlv_len;
309a0039e40257 David Lin 2024-06-21 536 struct nxpwifi_ie_types_sta_flag *sta_flag;
309a0039e40257 David Lin 2024-06-21 537 int i;
309a0039e40257 David Lin 2024-06-21 538
309a0039e40257 David Lin 2024-06-21 539 cmd->command = cpu_to_le16(HOST_CMD_ADD_NEW_STATION);
309a0039e40257 David Lin 2024-06-21 540 new_sta->action = cpu_to_le16(cmd_action);
309a0039e40257 David Lin 2024-06-21 @541 cmd->size = sizeof(struct host_cmd_ds_add_station) + S_DS_GEN;
309a0039e40257 David Lin 2024-06-21 542
309a0039e40257 David Lin 2024-06-21 543 if (cmd_action == HOST_ACT_ADD_STA)
309a0039e40257 David Lin 2024-06-21 544 sta_ptr = nxpwifi_add_sta_entry(priv, add_sta->peer_mac);
309a0039e40257 David Lin 2024-06-21 545 else
309a0039e40257 David Lin 2024-06-21 546 sta_ptr = nxpwifi_get_sta_entry(priv, add_sta->peer_mac);
309a0039e40257 David Lin 2024-06-21 547
309a0039e40257 David Lin 2024-06-21 548 if (!sta_ptr)
309a0039e40257 David Lin 2024-06-21 549 return -1;
309a0039e40257 David Lin 2024-06-21 550
309a0039e40257 David Lin 2024-06-21 551 memcpy(new_sta->peer_mac, add_sta->peer_mac, ETH_ALEN);
309a0039e40257 David Lin 2024-06-21 552
309a0039e40257 David Lin 2024-06-21 553 if (cmd_action == HOST_ACT_REMOVE_STA) {
309a0039e40257 David Lin 2024-06-21 @554 cmd->size = cpu_to_le16(cmd->size);
309a0039e40257 David Lin 2024-06-21 555 return 0;
309a0039e40257 David Lin 2024-06-21 556 }
309a0039e40257 David Lin 2024-06-21 557
309a0039e40257 David Lin 2024-06-21 558 new_sta->aid = cpu_to_le16(params->aid);
309a0039e40257 David Lin 2024-06-21 559 new_sta->listen_interval = cpu_to_le32(params->listen_interval);
309a0039e40257 David Lin 2024-06-21 560 new_sta->cap_info = cpu_to_le16(params->capability);
309a0039e40257 David Lin 2024-06-21 561
309a0039e40257 David Lin 2024-06-21 562 pos = new_sta->tlv;
309a0039e40257 David Lin 2024-06-21 563 cmd_end = (u8 *)cmd;
309a0039e40257 David Lin 2024-06-21 564 cmd_end += (NXPWIFI_SIZE_OF_CMD_BUFFER - 1);
309a0039e40257 David Lin 2024-06-21 565
309a0039e40257 David Lin 2024-06-21 566 if (params->sta_flags_set & NL80211_STA_FLAG_WME)
309a0039e40257 David Lin 2024-06-21 567 sta_ptr->is_wmm_enabled = 1;
309a0039e40257 David Lin 2024-06-21 568 sta_flag = (struct nxpwifi_ie_types_sta_flag *)pos;
309a0039e40257 David Lin 2024-06-21 569 sta_flag->header.type = cpu_to_le16(TLV_TYPE_UAP_STA_FLAGS);
309a0039e40257 David Lin 2024-06-21 570 sta_flag->header.len = cpu_to_le16(sizeof(__le32));
309a0039e40257 David Lin 2024-06-21 571 sta_flag->sta_flags = cpu_to_le32(params->sta_flags_set);
309a0039e40257 David Lin 2024-06-21 572 pos += sizeof(struct nxpwifi_ie_types_sta_flag);
309a0039e40257 David Lin 2024-06-21 @573 cmd->size += sizeof(struct nxpwifi_ie_types_sta_flag);
309a0039e40257 David Lin 2024-06-21 574
309a0039e40257 David Lin 2024-06-21 575 if (params->ext_capab_len) {
309a0039e40257 David Lin 2024-06-21 576 u8 *data = (u8 *)params->ext_capab;
309a0039e40257 David Lin 2024-06-21 577 u16 len = params->ext_capab_len;
309a0039e40257 David Lin 2024-06-21 578
309a0039e40257 David Lin 2024-06-21 579 tlv_len = nxpwifi_append_data_tlv(WLAN_EID_EXT_CAPABILITY,
309a0039e40257 David Lin 2024-06-21 580 data, len, pos, cmd_end);
309a0039e40257 David Lin 2024-06-21 581 if (!tlv_len)
309a0039e40257 David Lin 2024-06-21 582 return -1;
309a0039e40257 David Lin 2024-06-21 583 pos += tlv_len;
309a0039e40257 David Lin 2024-06-21 584 cmd->size += tlv_len;
309a0039e40257 David Lin 2024-06-21 585 }
309a0039e40257 David Lin 2024-06-21 586
309a0039e40257 David Lin 2024-06-21 587 if (params->link_sta_params.supported_rates_len) {
309a0039e40257 David Lin 2024-06-21 588 u8 *data = (u8 *)params->link_sta_params.supported_rates;
309a0039e40257 David Lin 2024-06-21 589 u16 len = params->link_sta_params.supported_rates_len;
309a0039e40257 David Lin 2024-06-21 590
309a0039e40257 David Lin 2024-06-21 591 tlv_len = nxpwifi_append_data_tlv(WLAN_EID_SUPP_RATES,
309a0039e40257 David Lin 2024-06-21 592 data, len, pos, cmd_end);
309a0039e40257 David Lin 2024-06-21 593 if (!tlv_len)
309a0039e40257 David Lin 2024-06-21 594 return -1;
309a0039e40257 David Lin 2024-06-21 595 pos += tlv_len;
309a0039e40257 David Lin 2024-06-21 596 cmd->size += tlv_len;
309a0039e40257 David Lin 2024-06-21 597 }
309a0039e40257 David Lin 2024-06-21 598
309a0039e40257 David Lin 2024-06-21 599 if (params->uapsd_queues || params->max_sp) {
309a0039e40257 David Lin 2024-06-21 600 u8 qos_capability = params->uapsd_queues | (params->max_sp << 5);
309a0039e40257 David Lin 2024-06-21 601 u8 *data = &qos_capability;
309a0039e40257 David Lin 2024-06-21 602 u16 len = sizeof(u8);
309a0039e40257 David Lin 2024-06-21 603
309a0039e40257 David Lin 2024-06-21 604 tlv_len = nxpwifi_append_data_tlv(WLAN_EID_QOS_CAPA,
309a0039e40257 David Lin 2024-06-21 605 data, len, pos, cmd_end);
309a0039e40257 David Lin 2024-06-21 606 if (!tlv_len)
309a0039e40257 David Lin 2024-06-21 607 return -1;
309a0039e40257 David Lin 2024-06-21 608 pos += tlv_len;
309a0039e40257 David Lin 2024-06-21 609 cmd->size += tlv_len;
309a0039e40257 David Lin 2024-06-21 610 sta_ptr->is_wmm_enabled = 1;
309a0039e40257 David Lin 2024-06-21 611 }
309a0039e40257 David Lin 2024-06-21 612
309a0039e40257 David Lin 2024-06-21 613 if (params->link_sta_params.ht_capa) {
309a0039e40257 David Lin 2024-06-21 614 u8 *data = (u8 *)params->link_sta_params.ht_capa;
309a0039e40257 David Lin 2024-06-21 615 u16 len = sizeof(struct ieee80211_ht_cap);
309a0039e40257 David Lin 2024-06-21 616
309a0039e40257 David Lin 2024-06-21 617 tlv_len = nxpwifi_append_data_tlv(WLAN_EID_HT_CAPABILITY,
309a0039e40257 David Lin 2024-06-21 618 data, len, pos, cmd_end);
309a0039e40257 David Lin 2024-06-21 619 if (!tlv_len)
309a0039e40257 David Lin 2024-06-21 620 return -1;
309a0039e40257 David Lin 2024-06-21 621 pos += tlv_len;
309a0039e40257 David Lin 2024-06-21 622 cmd->size += tlv_len;
309a0039e40257 David Lin 2024-06-21 623 sta_ptr->is_11n_enabled = 1;
309a0039e40257 David Lin 2024-06-21 624 sta_ptr->max_amsdu =
309a0039e40257 David Lin 2024-06-21 625 le16_to_cpu(params->link_sta_params.ht_capa->cap_info) &
309a0039e40257 David Lin 2024-06-21 626 IEEE80211_HT_CAP_MAX_AMSDU ?
309a0039e40257 David Lin 2024-06-21 627 NXPWIFI_TX_DATA_BUF_SIZE_8K :
309a0039e40257 David Lin 2024-06-21 628 NXPWIFI_TX_DATA_BUF_SIZE_4K;
309a0039e40257 David Lin 2024-06-21 629 }
309a0039e40257 David Lin 2024-06-21 630
309a0039e40257 David Lin 2024-06-21 631 if (params->link_sta_params.vht_capa) {
309a0039e40257 David Lin 2024-06-21 632 u8 *data = (u8 *)params->link_sta_params.vht_capa;
309a0039e40257 David Lin 2024-06-21 633 u16 len = sizeof(struct ieee80211_vht_cap);
309a0039e40257 David Lin 2024-06-21 634
309a0039e40257 David Lin 2024-06-21 635 tlv_len = nxpwifi_append_data_tlv(WLAN_EID_VHT_CAPABILITY,
309a0039e40257 David Lin 2024-06-21 636 data, len, pos, cmd_end);
309a0039e40257 David Lin 2024-06-21 637 if (!tlv_len)
309a0039e40257 David Lin 2024-06-21 638 return -1;
309a0039e40257 David Lin 2024-06-21 639 pos += tlv_len;
309a0039e40257 David Lin 2024-06-21 640 cmd->size += tlv_len;
309a0039e40257 David Lin 2024-06-21 641 sta_ptr->is_11ac_enabled = 1;
309a0039e40257 David Lin 2024-06-21 642 }
309a0039e40257 David Lin 2024-06-21 643
309a0039e40257 David Lin 2024-06-21 644 if (params->link_sta_params.opmode_notif_used) {
309a0039e40257 David Lin 2024-06-21 645 u8 *data = ¶ms->link_sta_params.opmode_notif;
309a0039e40257 David Lin 2024-06-21 646 u16 len = sizeof(u8);
309a0039e40257 David Lin 2024-06-21 647
309a0039e40257 David Lin 2024-06-21 648 tlv_len = nxpwifi_append_data_tlv(WLAN_EID_OPMODE_NOTIF,
309a0039e40257 David Lin 2024-06-21 649 data, len, pos, cmd_end);
309a0039e40257 David Lin 2024-06-21 650 if (!tlv_len)
309a0039e40257 David Lin 2024-06-21 651 return -1;
309a0039e40257 David Lin 2024-06-21 652 pos += tlv_len;
309a0039e40257 David Lin 2024-06-21 653 cmd->size += tlv_len;
309a0039e40257 David Lin 2024-06-21 654 }
309a0039e40257 David Lin 2024-06-21 655
309a0039e40257 David Lin 2024-06-21 656 for (i = 0; i < MAX_NUM_TID; i++) {
309a0039e40257 David Lin 2024-06-21 657 if (sta_ptr->is_11n_enabled)
309a0039e40257 David Lin 2024-06-21 658 sta_ptr->ampdu_sta[i] =
309a0039e40257 David Lin 2024-06-21 659 priv->aggr_prio_tbl[i].ampdu_user;
309a0039e40257 David Lin 2024-06-21 660 else
309a0039e40257 David Lin 2024-06-21 661 sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED;
309a0039e40257 David Lin 2024-06-21 662 }
309a0039e40257 David Lin 2024-06-21 663
309a0039e40257 David Lin 2024-06-21 664 memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq));
309a0039e40257 David Lin 2024-06-21 665
309a0039e40257 David Lin 2024-06-21 666 cmd->size = cpu_to_le16(cmd->size);
309a0039e40257 David Lin 2024-06-21 667
309a0039e40257 David Lin 2024-06-21 668 return 0;
309a0039e40257 David Lin 2024-06-21 669 }
309a0039e40257 David Lin 2024-06-21 670
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 56+ messages in thread
* [PATCH 43/43] wifi: nxpwifi: add nxpwifi related information to MAINTAINERS
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (41 preceding siblings ...)
2024-06-21 7:52 ` [PATCH 42/43] wifi: nxpwifi: add Makefile and Kconfig files for nxpwifi compilation David Lin
@ 2024-06-21 7:52 ` David Lin
2024-06-21 17:53 ` [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x Brian Norris
2024-06-21 18:20 ` Johannes Berg
44 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-06-21 7:52 UTC (permalink / raw)
To: linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh,
David Lin
Signed-off-by: David Lin <yu-hao.lin@nxp.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 92ad1d545690..847051340c91 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16178,6 +16178,13 @@ F: Documentation/devicetree/bindings/clock/imx*
F: drivers/clk/imx/
F: include/dt-bindings/clock/imx*
+NXP NXPWIFI WIRELESS DRIVER
+M: David Lin <yu-hao.lin@nxp.com>
+R: Pete Hsieh <tsung-hsien.hsieh@nxp.com>
+L: linux-wireless@vger.kernel.org
+S: Maintained
+F: drivers/net/wireless/nxp/nxpwifi
+
NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER
M: Jagan Teki <jagan@amarulasolutions.com>
S: Maintained
--
2.34.1
^ permalink raw reply related [flat|nested] 56+ messages in thread* Re: [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (42 preceding siblings ...)
2024-06-21 7:52 ` [PATCH 43/43] wifi: nxpwifi: add nxpwifi related information to MAINTAINERS David Lin
@ 2024-06-21 17:53 ` Brian Norris
2024-06-21 18:20 ` Johannes Berg
44 siblings, 0 replies; 56+ messages in thread
From: Brian Norris @ 2024-06-21 17:53 UTC (permalink / raw)
To: David Lin
Cc: linux-wireless, linux-kernel, kvalo, francesco, tsung-hsien.hsieh
On Fri, Jun 21, 2024 at 12:52 AM David Lin <yu-hao.lin@nxp.com> wrote:
> This driver is a derivative of existing Mwifiex [1] and based on similar
> full-MAC architecture [2].
For the record, mwifiex is a fairly awful driver. For one, its locking
schemes are generally unhelpful or nonexistent, and sometimes
placebo-like (as in, they look like they are protecting certain data,
but they do a very poor job of it). So I'm not sure this is a
promising start. It was just yesterday, in fact, that I was telling a
colleague that if mwifiex was proposed for inclusion in mainline
today, I would reject it.
Also, I'm far from interested in reviewing a new driver here. My only
interest in mwifiex is in making sure existing hardware (especially
those used on Chromebooks) doesn't get significantly worse. That
interest doesn't extend to "nxpwifi".
I just want to be up-front about it, and that you might as well drop
me from the CC list. (Of course, that's not a requirement. I can
ignore email too.)
Brian
^ permalink raw reply [flat|nested] 56+ messages in thread* Re: [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x
2024-06-21 7:51 [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x David Lin
` (43 preceding siblings ...)
2024-06-21 17:53 ` [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x Brian Norris
@ 2024-06-21 18:20 ` Johannes Berg
2024-07-01 1:08 ` [EXT] " David Lin
44 siblings, 1 reply; 56+ messages in thread
From: Johannes Berg @ 2024-06-21 18:20 UTC (permalink / raw)
To: David Lin, linux-wireless
Cc: linux-kernel, briannorris, kvalo, francesco, tsung-hsien.hsieh
On Fri, 2024-06-21 at 15:51 +0800, David Lin wrote:
>
> wifi: nxpwifi: add ioctl.h
even the name here sounds questionable :)
> 48 files changed, 34928 insertions(+)
>
This is ... huge. I don't know who could possibly review it at all.
A quick look suggests that it's got a bunch of things we probably really
don't want to do that way any more, like
using semaphores in a wifi driver:
> +#include <linux/semaphore.h>
having a bunch of (sometimes wrong!) element definitions in a driver:
> +struct ieee_types_aid {
...
> + u16 aid;
embedding a (default?) wireless_dev when clearly the driver supports
more than one netdev/wdev:
> + struct wireless_dev wdev;
Having multiple own workqueues is probably also unreasonable:
> + struct workqueue_struct *dfs_cac_workqueue;
> + struct workqueue_struct *dfs_chan_sw_workqueue;
> + struct workqueue_struct *workqueue;
> + struct workqueue_struct *rx_workqueue;
> + struct workqueue_struct *host_mlme_workqueue;
as is a misnamed mutex, but really you could use wiphy work and likely
not have a mutex at all:
> + /* mutex for scan */
> + struct mutex async_mutex;
(even mac80211 only has one mutex left, and that's for a specific case
where otherwise we have some issues!)
questionable locking schemes, as evidenced simply by "is something
locked" variables existing:
> + bool rx_locked;
> + bool main_locked;
locking code, rather than data?
> + /* spin lock for main process */
> + spinlock_t main_proc_lock;
but also simple things like not wanting to use ERR_PTR()?
> +static int nxpwifi_register(void *card, struct device *dev,
> + struct nxpwifi_if_ops *if_ops, void **padapter)
(padapter is an out parameter)
Why random numbers for cookies instead of just assigning from a static
variable:
> + *cookie = get_random_u32() | 1;
Open-coding -EPERM?
> + if (nxpwifi_deinit_priv_params(priv))
> + return -1;
Using -EFAULT for FW errors seems like a really bad idea:
> + if (nxpwifi_drv_get_data_rate(priv, &rate)) {
> + nxpwifi_dbg(priv->adapter, ERROR,
> + "getting data rate error\n");
> + return -EFAULT;
But I really just scrolled through this briefly, this wasn't a real
review. I don't know who could do a real review, but as is, it looks
like someone _should_.
johannes
^ permalink raw reply [flat|nested] 56+ messages in thread* RE: [EXT] Re: [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x
2024-06-21 18:20 ` Johannes Berg
@ 2024-07-01 1:08 ` David Lin
0 siblings, 0 replies; 56+ messages in thread
From: David Lin @ 2024-07-01 1:08 UTC (permalink / raw)
To: Johannes Berg, linux-wireless@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, briannorris@chromium.org,
kvalo@kernel.org, francesco@dolcini.it, Pete Hsieh
Hi Johannes,
> From: Johannes Berg <johannes@sipsolutions.net>
> Sent: Saturday, June 22, 2024 2:20 AM
> To: David Lin <yu-hao.lin@nxp.com>; linux-wireless@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; briannorris@chromium.org;
> kvalo@kernel.org; francesco@dolcini.it; Pete Hsieh
> <tsung-hsien.hsieh@nxp.com>
> Subject: [EXT] Re: [PATCH 00/43] wifi: nxpwifi: create nxpwifi to support iw61x
>
> Caution: This is an external email. Please take care when clicking links or
> opening attachments. When in doubt, report the message using the 'Report
> this email' button
>
>
> On Fri, 2024-06-21 at 15:51 +0800, David Lin wrote:
> >
> > wifi: nxpwifi: add ioctl.h
>
> even the name here sounds questionable :)
>
> > 48 files changed, 34928 insertions(+)
> >
>
> This is ... huge. I don't know who could possibly review it at all.
>
> A quick look suggests that it's got a bunch of things we probably really don't
> want to do that way any more, like
>
> using semaphores in a wifi driver:
>
> > +#include <linux/semaphore.h>
>
> having a bunch of (sometimes wrong!) element definitions in a driver:
>
> > +struct ieee_types_aid {
> ...
> > + u16 aid;
>
> embedding a (default?) wireless_dev when clearly the driver supports more
> than one netdev/wdev:
>
> > + struct wireless_dev wdev;
>
> Having multiple own workqueues is probably also unreasonable:
>
> > + struct workqueue_struct *dfs_cac_workqueue;
> > + struct workqueue_struct *dfs_chan_sw_workqueue;
> > + struct workqueue_struct *workqueue;
> > + struct workqueue_struct *rx_workqueue;
> > + struct workqueue_struct *host_mlme_workqueue;
>
> as is a misnamed mutex, but really you could use wiphy work and likely not
> have a mutex at all:
>
> > + /* mutex for scan */
> > + struct mutex async_mutex;
>
> (even mac80211 only has one mutex left, and that's for a specific case where
> otherwise we have some issues!)
>
> questionable locking schemes, as evidenced simply by "is something locked"
> variables existing:
>
> > + bool rx_locked;
> > + bool main_locked;
>
> locking code, rather than data?
>
> > + /* spin lock for main process */
> > + spinlock_t main_proc_lock;
>
> but also simple things like not wanting to use ERR_PTR()?
>
> > +static int nxpwifi_register(void *card, struct device *dev,
> > + struct nxpwifi_if_ops *if_ops, void
> > +**padapter)
>
> (padapter is an out parameter)
>
> Why random numbers for cookies instead of just assigning from a static
> variable:
>
> > + *cookie = get_random_u32() | 1;
>
> Open-coding -EPERM?
>
> > + if (nxpwifi_deinit_priv_params(priv))
> > + return -1;
>
> Using -EFAULT for FW errors seems like a really bad idea:
>
> > + if (nxpwifi_drv_get_data_rate(priv, &rate)) {
> > + nxpwifi_dbg(priv->adapter, ERROR,
> > + "getting data rate error\n");
> > + return -EFAULT;
>
>
> But I really just scrolled through this briefly, this wasn't a real review. I don't
> know who could do a real review, but as is, it looks like someone _should_.
>
> Johannes
Enhancement of nxpwifi based on your comments is ongoing.
Thanks,
David
^ permalink raw reply [flat|nested] 56+ messages in thread