wireless: mwifiex: initial commit for Marvell mwifiex driver
Bing Zhao [Tue, 22 Mar 2011 01:00:50 +0000 (18:00 -0700)]
This driver adds WiFi support for Marvell 802.11n based chipsets
with SDIO interface. Currently only SD8787 is supported. More
chipsets will be supported later.

drivers/net/wireless/mwifiex/

Signed-off-by: Nishant Sarmukadam <nishants@marvell.com>
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Kiran Divekar <dkiran@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: Yogesh Ashok Powar <yogeshp@marvell.com>
Signed-off-by: Marc Yang <yangyang@marvell.com>
Signed-off-by: Ramesh Radhakrishnan <rramesh@marvell.com>
Signed-off-by: Frank Huang <frankh@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

37 files changed:
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/mwifiex/11n.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/11n.h [new file with mode: 0644]
drivers/net/wireless/mwifiex/11n_aggr.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/11n_aggr.h [new file with mode: 0644]
drivers/net/wireless/mwifiex/11n_rxreorder.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/11n_rxreorder.h [new file with mode: 0644]
drivers/net/wireless/mwifiex/Kconfig [new file with mode: 0644]
drivers/net/wireless/mwifiex/Makefile [new file with mode: 0644]
drivers/net/wireless/mwifiex/README [new file with mode: 0644]
drivers/net/wireless/mwifiex/cfg80211.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/cfg80211.h [new file with mode: 0644]
drivers/net/wireless/mwifiex/cfp.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/cmdevt.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/debugfs.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/decl.h [new file with mode: 0644]
drivers/net/wireless/mwifiex/fw.h [new file with mode: 0644]
drivers/net/wireless/mwifiex/init.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/ioctl.h [new file with mode: 0644]
drivers/net/wireless/mwifiex/join.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/main.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/main.h [new file with mode: 0644]
drivers/net/wireless/mwifiex/scan.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/sdio.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/sdio.h [new file with mode: 0644]
drivers/net/wireless/mwifiex/sta_cmd.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/sta_cmdresp.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/sta_event.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/sta_ioctl.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/sta_rx.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/sta_tx.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/txrx.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/util.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/util.h [new file with mode: 0644]
drivers/net/wireless/mwifiex/wmm.c [new file with mode: 0644]
drivers/net/wireless/mwifiex/wmm.h [new file with mode: 0644]

index 7aeb113..f354bd4 100644 (file)
@@ -284,5 +284,6 @@ source "drivers/net/wireless/rtlwifi/Kconfig"
 source "drivers/net/wireless/wl1251/Kconfig"
 source "drivers/net/wireless/wl12xx/Kconfig"
 source "drivers/net/wireless/zd1211rw/Kconfig"
+source "drivers/net/wireless/mwifiex/Kconfig"
 
 endif # WLAN
index ddd3fb6..7bba6a8 100644 (file)
@@ -56,3 +56,5 @@ obj-$(CONFIG_WL12XX)  += wl12xx/
 obj-$(CONFIG_WL12XX_PLATFORM_DATA)     += wl12xx/
 
 obj-$(CONFIG_IWM)      += iwmc3200wifi/
+
+obj-$(CONFIG_MWIFIEX)  += mwifiex/
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c
new file mode 100644 (file)
index 0000000..0e04a21
--- /dev/null
@@ -0,0 +1,922 @@
+/*
+ * Marvell Wireless LAN device driver: 802.11n
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+
+/*
+ * Fills HT capability information field, AMPDU Parameters field, HT extended
+ * capability field, and supported MCS set fields.
+ *
+ * Only the following HT capability information fields are used, all other
+ * fields are always turned off.
+ *
+ *  Bit 1 : Supported channel width (0: 20MHz, 1: Both 20 and 40 MHz)
+ *  Bit 4 : Greenfield support (0: Not supported, 1: Supported)
+ *  Bit 5 : Short GI for 20 MHz support (0: Not supported, 1: Supported)
+ *  Bit 6 : Short GI for 40 MHz support (0: Not supported, 1: Supported)
+ *  Bit 7 : Tx STBC (0: Not supported, 1: Supported)
+ *  Bit 8-9 : Rx STBC (0: Not supported, X: Support for up to X spatial streams)
+ *  Bit 10 : Delayed BA support (0: Not supported, 1: Supported)
+ *  Bit 11 : Maximum AMSDU length (0: 3839 octets, 1: 7935 octets)
+ *  Bit 14 : 40-Mhz intolerant support (0: Not supported, 1: Supported)
+ *
+ *  In addition, the following AMPDU Parameters are set -
+ *      - Maximum AMPDU length exponent (set to 3)
+ *      - Minimum AMPDU start spacing (set to 0 - No restrictions)
+ *
+ *  MCS is set for 1x1, with MSC32 for infra mode or ad-hoc mode with 40 MHz
+ *  support.
+ *
+ *  RD responder bit to set to clear in the extended capability header.
+ */
+void
+mwifiex_fill_cap_info(struct mwifiex_private *priv,
+                     struct mwifiex_ie_types_htcap *ht_cap)
+{
+       struct mwifiex_adapter *adapter = priv->adapter;
+       u8 *mcs;
+       int rx_mcs_supp;
+       uint16_t ht_cap_info = le16_to_cpu(ht_cap->ht_cap.cap_info);
+       uint16_t ht_ext_cap = le16_to_cpu(ht_cap->ht_cap.extended_ht_cap_info);
+
+       if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap) &&
+           ISSUPP_CHANWIDTH40(adapter->usr_dot_11n_dev_cap))
+               SETHT_SUPPCHANWIDTH(ht_cap_info);
+       else
+               RESETHT_SUPPCHANWIDTH(ht_cap_info);
+
+       if (ISSUPP_GREENFIELD(adapter->hw_dot_11n_dev_cap) &&
+           ISSUPP_GREENFIELD(adapter->usr_dot_11n_dev_cap))
+               SETHT_GREENFIELD(ht_cap_info);
+       else
+               RESETHT_GREENFIELD(ht_cap_info);
+
+       if (ISSUPP_SHORTGI20(adapter->hw_dot_11n_dev_cap) &&
+           ISSUPP_SHORTGI20(adapter->usr_dot_11n_dev_cap))
+               SETHT_SHORTGI20(ht_cap_info);
+       else
+               RESETHT_SHORTGI20(ht_cap_info);
+
+       if (ISSUPP_SHORTGI40(adapter->hw_dot_11n_dev_cap) &&
+           ISSUPP_SHORTGI40(adapter->usr_dot_11n_dev_cap))
+               SETHT_SHORTGI40(ht_cap_info);
+       else
+               RESETHT_SHORTGI40(ht_cap_info);
+
+       /* No user config for RX STBC yet */
+       if (ISSUPP_RXSTBC(adapter->hw_dot_11n_dev_cap)
+           && ISSUPP_RXSTBC(adapter->usr_dot_11n_dev_cap))
+               SETHT_RXSTBC(ht_cap_info, 1);
+       else
+               RESETHT_RXSTBC(ht_cap_info);
+
+       /* No user config for TX STBC yet */
+       if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap))
+               SETHT_TXSTBC(ht_cap_info);
+       else
+               RESETHT_TXSTBC(ht_cap_info);
+
+       /* No user config for Delayed BACK yet */
+       if (GET_DELAYEDBACK(adapter->hw_dot_11n_dev_cap))
+               SETHT_DELAYEDBACK(ht_cap_info);
+       else
+               RESETHT_DELAYEDBACK(ht_cap_info);
+
+       if (ISENABLED_40MHZ_INTOLARENT(adapter->usr_dot_11n_dev_cap))
+               SETHT_40MHZ_INTOLARANT(ht_cap_info);
+       else
+               RESETHT_40MHZ_INTOLARANT(ht_cap_info);
+
+       SETAMPDU_SIZE(ht_cap->ht_cap.ampdu_params_info, AMPDU_FACTOR_64K);
+       SETAMPDU_SPACING(ht_cap->ht_cap.ampdu_params_info, 0);
+
+       /* Need change to support 8k AMSDU receive */
+       RESETHT_MAXAMSDU(ht_cap_info);
+
+       rx_mcs_supp = GET_RXMCSSUPP(adapter->hw_dev_mcs_support);
+
+       mcs = (u8 *)&ht_cap->ht_cap.mcs;
+
+       /* Set MCS for 1x1 */
+       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 == MWIFIEX_BSS_MODE_INFRA ||
+           (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap) &&
+            ISSUPP_CHANWIDTH40(adapter->usr_dot_11n_dev_cap)))
+               /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
+               SETHT_MCS32(ht_cap->ht_cap.mcs.rx_mask);
+
+       /* Clear RD responder bit */
+       RESETHT_EXTCAP_RDG(ht_ext_cap);
+
+       ht_cap->ht_cap.cap_info = cpu_to_le16(ht_cap_info);
+       ht_cap->ht_cap.extended_ht_cap_info = cpu_to_le16(ht_ext_cap);
+}
+
+/*
+ * Shows HT capability information fields.
+ *
+ * The following HT capability information fields are supported.
+ *      - Maximum AMSDU length (3839 bytes or 7935 bytes)
+ *      - Beam forming support
+ *      - Greenfield preamble support
+ *      - AMPDU support
+ *      - MIMO Power Save support
+ *      - Rx STBC support
+ *      - Tx STBC support
+ *      - Short GI for 20 MHz support
+ *      - Short GI for 40 MHz support
+ *      - LDPC coded packets receive support
+ *      - Number of delayed BA streams
+ *      - Number of immediate BA streams
+ *      - 10 MHz channel width support
+ *      - 20 MHz channel width support
+ *      - 40 MHz channel width support
+ *      - Presence of Tx antenna A/B/C/D
+ *      - Presence of Rx antenna A/B/C/D
+ */
+void
+mwifiex_show_dot_11n_dev_cap(struct mwifiex_adapter *adapter, u32 cap)
+{
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: Max MSDU len = %s octets\n",
+              (ISSUPP_MAXAMSDU(cap) ? "7935" : "3839"));
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: Beam forming %s\n",
+              (ISSUPP_BEAMFORMING(cap) ? "supported" : "not supported"));
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: Greenfield preamble %s\n",
+              (ISSUPP_GREENFIELD(cap) ? "supported" : "not supported"));
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: AMPDU %s\n",
+              (ISSUPP_AMPDU(cap) ? "supported" : "not supported"));
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: MIMO Power Save %s\n",
+              (ISSUPP_MIMOPS(cap) ? "supported" : "not supported"));
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: Rx STBC %s\n",
+              (ISSUPP_RXSTBC(cap) ? "supported" : "not supported"));
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: Tx STBC %s\n",
+              (ISSUPP_TXSTBC(cap) ? "supported" : "not supported"));
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: Short GI for 40 Mhz %s\n",
+              (ISSUPP_SHORTGI40(cap) ? "supported" : "not supported"));
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: Short GI for 20 Mhz %s\n",
+              (ISSUPP_SHORTGI20(cap) ? "supported" : "not supported"));
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: LDPC coded packet receive %s\n",
+              (ISSUPP_RXLDPC(cap) ? "supported" : "not supported"));
+       dev_dbg(adapter->dev,
+               "info: GET_HW_SPEC: Number of Delayed Block Ack streams = %d\n",
+              GET_DELAYEDBACK(cap));
+       dev_dbg(adapter->dev,
+               "info: GET_HW_SPEC: Number of Immediate Block Ack streams = %d\n",
+              GET_IMMEDIATEBACK(cap));
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: 40 Mhz channel width %s\n",
+              (ISSUPP_CHANWIDTH40(cap) ? "supported" : "not supported"));
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: 20 Mhz channel width %s\n",
+              (ISSUPP_CHANWIDTH20(cap) ? "supported" : "not supported"));
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: 10 Mhz channel width %s\n",
+              (ISSUPP_CHANWIDTH10(cap) ? "supported" : "not supported"));
+
+       if (ISSUPP_RXANTENNAA(cap))
+               dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Rx antennea A\n");
+
+       if (ISSUPP_RXANTENNAB(cap))
+               dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Rx antennea B\n");
+
+       if (ISSUPP_RXANTENNAC(cap))
+               dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Rx antennea C\n");
+
+       if (ISSUPP_RXANTENNAD(cap))
+               dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Rx antennea D\n");
+
+       if (ISSUPP_TXANTENNAA(cap))
+               dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Tx antennea A\n");
+
+       if (ISSUPP_TXANTENNAB(cap))
+               dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Tx antennea B\n");
+
+       if (ISSUPP_TXANTENNAC(cap))
+               dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Tx antennea C\n");
+
+       if (ISSUPP_TXANTENNAD(cap))
+               dev_dbg(adapter->dev, "info: GET_HW_SPEC: Prescence of Tx antennea D\n");
+
+       return;
+}
+
+/*
+ * Shows HT MCS support field.
+ */
+void
+mwifiex_show_dev_mcs_support(struct mwifiex_adapter *adapter, u8 support)
+{
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: MCSs for %dx%d MIMO\n",
+              GET_RXMCSSUPP(support), GET_TXMCSSUPP(support));
+       return;
+}
+
+/*
+ * This function returns the pointer to an entry in BA Stream
+ * table which matches the requested BA status.
+ */
+static struct mwifiex_tx_ba_stream_tbl *
+mwifiex_11n_get_tx_ba_stream_status(struct mwifiex_private *priv,
+                                 enum mwifiex_ba_status ba_status)
+{
+       struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
+       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_irqrestore(&priv->tx_ba_stream_tbl_lock,
+                                              flags);
+                       return tx_ba_tsr_tbl;
+               }
+       }
+       spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
+       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 mwifiex_ret_11n_delba(struct mwifiex_private *priv,
+                         struct host_cmd_ds_command *resp)
+{
+       int tid;
+       struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl;
+       struct host_cmd_ds_11n_delba *del_ba =
+               (struct host_cmd_ds_11n_delba *) &resp->params.del_ba;
+       uint16_t 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) {
+               mwifiex_11n_delete_ba_stream_tbl(priv, tid,
+                               del_ba->peer_mac_addr, TYPE_DELBA_SENT,
+                               INITIATOR_BIT(del_ba_param_set));
+
+               tx_ba_tbl = mwifiex_11n_get_tx_ba_stream_status(priv,
+                                               BA_STREAM_SETUP_INPROGRESS);
+               if (tx_ba_tbl)
+                       mwifiex_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 ADDBA
+                 */
+               if (INITIATOR_BIT(del_ba_param_set)) {
+                       mwifiex_11n_create_tx_ba_stream_tbl(priv,
+                                       del_ba->peer_mac_addr, tid,
+                                       BA_STREAM_SETUP_INPROGRESS);
+
+                       tx_ba_tbl = mwifiex_11n_get_tx_ba_stream_status(priv,
+                                       BA_STREAM_SETUP_INPROGRESS);
+                       if (tx_ba_tbl)
+                               mwifiex_11n_delete_ba_stream_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 mwifiex_ret_11n_addba_req(struct mwifiex_private *priv,
+                             struct host_cmd_ds_command *resp)
+{
+       int tid;
+       struct host_cmd_ds_11n_addba_rsp *add_ba_rsp =
+               (struct host_cmd_ds_11n_addba_rsp *) &resp->params.add_ba_rsp;
+       struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl;
+
+       add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn))
+                       & SSN_MASK);
+
+       tid = (le16_to_cpu(add_ba_rsp->block_ack_param_set)
+               & IEEE80211_ADDBA_PARAM_TID_MASK)
+               >> BLOCKACKPARAM_TID_POS;
+       if (le16_to_cpu(add_ba_rsp->status_code) == BA_RESULT_SUCCESS) {
+               tx_ba_tbl = mwifiex_11n_get_tx_ba_stream_tbl(priv, tid,
+                                               add_ba_rsp->peer_mac_addr);
+               if (tx_ba_tbl) {
+                       dev_dbg(priv->adapter->dev, "info: BA stream complete\n");
+                       tx_ba_tbl->ba_status = BA_STREAM_SETUP_COMPLETE;
+               } else {
+                       dev_err(priv->adapter->dev, "BA stream not created\n");
+               }
+       } else {
+               mwifiex_11n_delete_ba_stream_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;
+}
+
+/*
+ * This function handles the command response of 11n configuration request.
+ *
+ * Handling includes changing the header fields into CPU format.
+ */
+int mwifiex_ret_11n_cfg(struct mwifiex_private *priv,
+                       struct host_cmd_ds_command *resp,
+                       void *data_buf)
+{
+       struct mwifiex_ds_11n_tx_cfg *tx_cfg = NULL;
+       struct host_cmd_ds_11n_cfg *htcfg = &resp->params.htcfg;
+
+       if (data_buf) {
+               tx_cfg = (struct mwifiex_ds_11n_tx_cfg *) data_buf;
+               tx_cfg->tx_htcap = le16_to_cpu(htcfg->ht_tx_cap);
+               tx_cfg->tx_htinfo = le16_to_cpu(htcfg->ht_tx_info);
+       }
+       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 mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv,
+                            struct host_cmd_ds_command *cmd, int cmd_action,
+                            void *data_buf)
+{
+       struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf;
+       u16 action = (u16) cmd_action;
+       u16 buf_size = *((u16 *) data_buf);
+
+       cmd->command = cpu_to_le16(HostCmd_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 HostCmd_ACT_GEN_SET:
+               dev_dbg(priv->adapter->dev, "cmd: set tx_buf=%d\n", buf_size);
+               tx_buf->buff_size = cpu_to_le16(buf_size);
+               break;
+       case HostCmd_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 mwifiex_cmd_amsdu_aggr_ctrl(struct mwifiex_private *priv,
+                               struct host_cmd_ds_command *cmd,
+                               int cmd_action, void *data_buf)
+{
+       struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl =
+               &cmd->params.amsdu_aggr_ctrl;
+       u16 action = (u16) cmd_action;
+       struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl =
+               (struct mwifiex_ds_11n_amsdu_aggr_ctrl *) data_buf;
+
+       cmd->command = cpu_to_le16(HostCmd_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 HostCmd_ACT_GEN_SET:
+               amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable);
+               amsdu_ctrl->curr_buf_size = 0;
+               break;
+       case HostCmd_ACT_GEN_GET:
+       default:
+               amsdu_ctrl->curr_buf_size = 0;
+               break;
+       }
+       return 0;
+}
+
+/*
+ * This function handles the command response of AMSDU aggregation
+ * control request.
+ *
+ * Handling includes changing the header fields into CPU format.
+ */
+int mwifiex_ret_amsdu_aggr_ctrl(struct mwifiex_private *priv,
+                               struct host_cmd_ds_command *resp,
+                               void *data_buf)
+{
+       struct mwifiex_ds_11n_amsdu_aggr_ctrl *amsdu_aggr_ctrl = NULL;
+       struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl =
+               &resp->params.amsdu_aggr_ctrl;
+
+       if (data_buf) {
+               amsdu_aggr_ctrl =
+                       (struct mwifiex_ds_11n_amsdu_aggr_ctrl *) data_buf;
+               amsdu_aggr_ctrl->enable = le16_to_cpu(amsdu_ctrl->enable);
+               amsdu_aggr_ctrl->curr_buf_size =
+                       le16_to_cpu(amsdu_ctrl->curr_buf_size);
+       }
+       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 mwifiex_cmd_11n_cfg(struct mwifiex_private *priv,
+                       struct host_cmd_ds_command *cmd,
+                       u16 cmd_action, void *data_buf)
+{
+       struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg;
+       struct mwifiex_ds_11n_tx_cfg *txcfg =
+               (struct mwifiex_ds_11n_tx_cfg *) data_buf;
+
+       cmd->command = cpu_to_le16(HostCmd_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);
+       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
+mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
+                          struct mwifiex_bssdescriptor *bss_desc,
+                          u8 **buffer)
+{
+       struct mwifiex_ie_types_htcap *ht_cap;
+       struct mwifiex_ie_types_htinfo *ht_info;
+       struct mwifiex_ie_types_chan_list_param_set *chan_list;
+       struct mwifiex_ie_types_2040bssco *bss_co_2040;
+       struct mwifiex_ie_types_extcap *ext_cap;
+       int ret_len = 0;
+
+       if (!buffer || !*buffer)
+               return ret_len;
+
+       if (bss_desc->bcn_ht_cap) {
+               ht_cap = (struct mwifiex_ie_types_htcap *) *buffer;
+               memset(ht_cap, 0, sizeof(struct mwifiex_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 mwifiex_ie_types_header),
+                      (u8 *) bss_desc->bcn_ht_cap +
+                      sizeof(struct ieee_types_header),
+                      le16_to_cpu(ht_cap->header.len));
+
+               mwifiex_fill_cap_info(priv, ht_cap);
+
+               *buffer += sizeof(struct mwifiex_ie_types_htcap);
+               ret_len += sizeof(struct mwifiex_ie_types_htcap);
+       }
+
+       if (bss_desc->bcn_ht_info) {
+               if (priv->bss_mode == MWIFIEX_BSS_MODE_IBSS) {
+                       ht_info = (struct mwifiex_ie_types_htinfo *) *buffer;
+                       memset(ht_info, 0,
+                              sizeof(struct mwifiex_ie_types_htinfo));
+                       ht_info->header.type =
+                                       cpu_to_le16(WLAN_EID_HT_INFORMATION);
+                       ht_info->header.len =
+                               cpu_to_le16(sizeof(struct ieee80211_ht_info));
+
+                       memcpy((u8 *) ht_info +
+                              sizeof(struct mwifiex_ie_types_header),
+                              (u8 *) bss_desc->bcn_ht_info +
+                              sizeof(struct ieee_types_header),
+                              le16_to_cpu(ht_info->header.len));
+
+                       if (!ISSUPP_CHANWIDTH40
+                           (priv->adapter->hw_dot_11n_dev_cap)
+                           || !ISSUPP_CHANWIDTH40(priv->adapter->
+                                                  usr_dot_11n_dev_cap))
+                               RESET_CHANWIDTH40(ht_info->ht_info.ht_param);
+
+                       *buffer += sizeof(struct mwifiex_ie_types_htinfo);
+                       ret_len += sizeof(struct mwifiex_ie_types_htinfo);
+               }
+
+               chan_list =
+                       (struct mwifiex_ie_types_chan_list_param_set *) *buffer;
+               memset(chan_list, 0,
+                      sizeof(struct mwifiex_ie_types_chan_list_param_set));
+               chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
+               chan_list->header.len = cpu_to_le16(
+                       sizeof(struct mwifiex_ie_types_chan_list_param_set) -
+                       sizeof(struct mwifiex_ie_types_header));
+               chan_list->chan_scan_param[0].chan_number =
+                       bss_desc->bcn_ht_info->control_chan;
+               chan_list->chan_scan_param[0].radio_type =
+                       mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
+
+               if ((ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) &&
+                    ISSUPP_CHANWIDTH40(priv->adapter->usr_dot_11n_dev_cap))
+                   && ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_info->ht_param))
+                       SET_SECONDARYCHAN(chan_list->chan_scan_param[0].
+                                         radio_type,
+                                         GET_SECONDARYCHAN(bss_desc->
+                                         bcn_ht_info->ht_param));
+
+               *buffer += sizeof(struct mwifiex_ie_types_chan_list_param_set);
+               ret_len += sizeof(struct mwifiex_ie_types_chan_list_param_set);
+       }
+
+       if (bss_desc->bcn_bss_co_2040) {
+               bss_co_2040 = (struct mwifiex_ie_types_2040bssco *) *buffer;
+               memset(bss_co_2040, 0,
+                      sizeof(struct mwifiex_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 mwifiex_ie_types_header),
+                      (u8 *) bss_desc->bcn_bss_co_2040 +
+                      sizeof(struct ieee_types_header),
+                      le16_to_cpu(bss_co_2040->header.len));
+
+               *buffer += sizeof(struct mwifiex_ie_types_2040bssco);
+               ret_len += sizeof(struct mwifiex_ie_types_2040bssco);
+       }
+
+       if (bss_desc->bcn_ext_cap) {
+               ext_cap = (struct mwifiex_ie_types_extcap *) *buffer;
+               memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap));
+               ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY);
+               ext_cap->header.len = cpu_to_le16(sizeof(ext_cap->ext_cap));
+
+               memcpy((u8 *) ext_cap +
+                      sizeof(struct mwifiex_ie_types_header),
+                      (u8 *) bss_desc->bcn_ext_cap +
+                      sizeof(struct ieee_types_header),
+                      le16_to_cpu(ext_cap->header.len));
+
+               *buffer += sizeof(struct mwifiex_ie_types_extcap);
+               ret_len += sizeof(struct mwifiex_ie_types_extcap);
+       }
+
+       return ret_len;
+}
+
+/*
+ * This function reconfigures the Tx buffer size in firmware.
+ *
+ * This function prepares a firmware command and issues it, if
+ * the current Tx buffer size is different from the one requested.
+ * Maximum configurable Tx buffer size is limited by the HT capability
+ * field value.
+ */
+void
+mwifiex_cfg_tx_buf(struct mwifiex_private *priv,
+                  struct mwifiex_bssdescriptor *bss_desc)
+{
+       u16 max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_2K;
+       u16 tx_buf = 0;
+       u16 curr_tx_buf_size = 0;
+
+       if (bss_desc->bcn_ht_cap) {
+               if (GETHT_MAXAMSDU(le16_to_cpu(bss_desc->bcn_ht_cap->cap_info)))
+                       max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_8K;
+               else
+                       max_amsdu = MWIFIEX_TX_DATA_BUF_SIZE_4K;
+       }
+
+       tx_buf = min(priv->adapter->max_tx_buf_size, max_amsdu);
+
+       dev_dbg(priv->adapter->dev, "info: max_amsdu=%d, max_tx_buf=%d\n",
+                       max_amsdu, priv->adapter->max_tx_buf_size);
+
+       if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_2K)
+               curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
+       else if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_4K)
+               curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K;
+       else if (priv->adapter->curr_tx_buf_size <= MWIFIEX_TX_DATA_BUF_SIZE_8K)
+               curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_8K;
+       if (curr_tx_buf_size != tx_buf)
+               mwifiex_prepare_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF,
+                       HostCmd_ACT_GEN_SET, 0,
+                       NULL, &tx_buf);
+
+       return;
+}
+
+/*
+ * This function checks if the given pointer is valid entry of
+ * Tx BA Stream table.
+ */
+static int mwifiex_is_tx_ba_stream_ptr_valid(struct mwifiex_private *priv,
+                               struct mwifiex_tx_ba_stream_tbl *tx_tbl_ptr)
+{
+       struct mwifiex_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 mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv,
+                               struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl)
+{
+       if (!tx_ba_tsr_tbl &&
+                       mwifiex_is_tx_ba_stream_ptr_valid(priv, tx_ba_tsr_tbl))
+               return;
+
+       dev_dbg(priv->adapter->dev, "info: tx_ba_tsr_tbl %p\n", tx_ba_tsr_tbl);
+
+       list_del(&tx_ba_tsr_tbl->list);
+
+       kfree(tx_ba_tsr_tbl);
+
+       return;
+}
+
+/*
+ * This function deletes all the entries in Tx BA Stream table.
+ */
+void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv)
+{
+       int i;
+       struct mwifiex_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
+       list_for_each_entry_safe(del_tbl_ptr, tmp_node,
+                                &priv->tx_ba_stream_tbl_ptr, list)
+               mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr);
+       spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
+
+       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 mwifiex_tx_ba_stream_tbl *
+mwifiex_11n_get_tx_ba_stream_tbl(struct mwifiex_private *priv,
+                                int tid, u8 *ra)
+{
+       struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
+       list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) {
+               if ((!memcmp(tx_ba_tsr_tbl->ra, ra, ETH_ALEN))
+                   && (tx_ba_tsr_tbl->tid == tid)) {
+                       spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock,
+                                              flags);
+                       return tx_ba_tsr_tbl;
+               }
+       }
+       spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
+       return NULL;
+}
+
+/*
+ * This function creates an entry in Tx BA stream table for the
+ * given RA/TID pair.
+ */
+void mwifiex_11n_create_tx_ba_stream_tbl(struct mwifiex_private *priv,
+                                        u8 *ra, int tid,
+                                        enum mwifiex_ba_status ba_status)
+{
+       struct mwifiex_tx_ba_stream_tbl *new_node;
+       unsigned long flags;
+
+       if (!mwifiex_11n_get_tx_ba_stream_tbl(priv, tid, ra)) {
+               new_node = kzalloc(sizeof(struct mwifiex_tx_ba_stream_tbl),
+                                  GFP_ATOMIC);
+               if (!new_node) {
+                       dev_err(priv->adapter->dev,
+                               "%s: failed to alloc new_node\n", __func__);
+                       return;
+               }
+
+               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_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
+               list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr);
+               spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
+       }
+
+       return;
+}
+
+/*
+ * This function sends an add BA request to the given TID/RA pair.
+ */
+int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac)
+{
+       struct host_cmd_ds_11n_addba_req add_ba_req;
+       static u8 dialog_tok;
+       int ret;
+
+       dev_dbg(priv->adapter->dev, "cmd: %s: tid %d\n", __func__, tid);
+
+       add_ba_req.block_ack_param_set = cpu_to_le16(
+               (u16) ((tid << BLOCKACKPARAM_TID_POS) |
+                        (priv->add_ba_param.
+                         tx_win_size << BLOCKACKPARAM_WINSIZE_POS) |
+                        IMMEDIATE_BLOCK_ACK));
+       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 = mwifiex_prepare_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ,
+                                 0, 0, NULL, &add_ba_req);
+
+       return ret;
+}
+
+/*
+ * This function sends a delete BA request to the given TID/RA pair.
+ */
+int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac,
+                      int initiator)
+{
+       struct host_cmd_ds_11n_delba delba;
+       int ret;
+       uint16_t del_ba_param_set;
+
+       memset(&delba, 0, sizeof(delba));
+       delba.del_ba_param_set = cpu_to_le16(tid << DELBA_TID_POS);
+
+       del_ba_param_set = le16_to_cpu(delba.del_ba_param_set);
+       if (initiator)
+               del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK;
+       else
+               del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK;
+
+       memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN);
+
+       /* We don't wait for the response of this command */
+       ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_11N_DELBA,
+                                 HostCmd_ACT_GEN_SET, 0, NULL, &delba);
+
+       return ret;
+}
+
+/*
+ * This function handles the command response of a delete BA request.
+ */
+void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba)
+{
+       struct host_cmd_ds_11n_delba *cmd_del_ba =
+               (struct host_cmd_ds_11n_delba *) del_ba;
+       uint16_t del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set);
+       int tid;
+
+       tid = del_ba_param_set >> DELBA_TID_POS;
+
+       mwifiex_11n_delete_ba_stream_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 mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv,
+                              struct mwifiex_ds_rx_reorder_tbl *buf)
+{
+       int i;
+       struct mwifiex_ds_rx_reorder_tbl *rx_reo_tbl = buf;
+       struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr;
+       int count = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+       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 >= MWIFIEX_MAX_RX_BASTREAM_SUPPORTED)
+                       break;
+       }
+       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+
+       return count;
+}
+
+/*
+ * This function retrieves the Tx BA stream table.
+ */
+int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv,
+                                struct mwifiex_ds_tx_ba_stream_tbl *buf)
+{
+       struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl;
+       struct mwifiex_ds_tx_ba_stream_tbl *rx_reo_tbl = buf;
+       int count = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
+       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;
+               dev_dbg(priv->adapter->dev, "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++;
+               count++;
+               if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED)
+                       break;
+       }
+       spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
+
+       return count;
+}
diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h
new file mode 100644 (file)
index 0000000..769a27f
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Marvell Wireless LAN device driver: 802.11n
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#ifndef _MWIFIEX_11N_H_
+#define _MWIFIEX_11N_H_
+
+#include "11n_aggr.h"
+#include "11n_rxreorder.h"
+#include "wmm.h"
+
+void mwifiex_show_dot_11n_dev_cap(struct mwifiex_adapter *adapter, u32 cap);
+void mwifiex_show_dev_mcs_support(struct mwifiex_adapter *adapter, u8 support);
+int mwifiex_ret_11n_delba(struct mwifiex_private *priv,
+                         struct host_cmd_ds_command *resp);
+int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv,
+                             struct host_cmd_ds_command *resp);
+int mwifiex_ret_11n_cfg(struct mwifiex_private *priv,
+                       struct host_cmd_ds_command *resp,
+                       void *data_buf);
+int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv,
+                       struct host_cmd_ds_command *cmd,
+                       u16 cmd_action, void *data_buf);
+
+int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv,
+                       struct host_cmd_ds_command *cmd,
+                       u16 cmd_action, void *data_buf);
+
+int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
+                              struct mwifiex_bssdescriptor *bss_desc,
+                              u8 **buffer);
+void mwifiex_cfg_tx_buf(struct mwifiex_private *priv,
+                       struct mwifiex_bssdescriptor *bss_desc);
+void mwifiex_fill_cap_info(struct mwifiex_private *,
+                          struct mwifiex_ie_types_htcap *);
+int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv,
+                                 u16 action, int *htcap_cfg);
+void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv,
+                                            struct mwifiex_tx_ba_stream_tbl
+                                            *tx_tbl);
+void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv);
+struct mwifiex_tx_ba_stream_tbl *mwifiex_11n_get_tx_ba_stream_tbl(struct
+                                                            mwifiex_private
+                                                            *priv, int tid,
+                                                            u8 *ra);
+void mwifiex_11n_create_tx_ba_stream_tbl(struct mwifiex_private *priv, u8 *ra,
+                                      int tid,
+                                      enum mwifiex_ba_status ba_status);
+int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac);
+int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac,
+                      int initiator);
+void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba);
+int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv,
+                             struct mwifiex_ds_rx_reorder_tbl *buf);
+int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv,
+                              struct mwifiex_ds_tx_ba_stream_tbl *buf);
+int mwifiex_ret_amsdu_aggr_ctrl(struct mwifiex_private *priv,
+                               struct host_cmd_ds_command
+                               *resp,
+                               void *data_buf);
+int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv,
+                            struct host_cmd_ds_command *cmd,
+                            int cmd_action, void *data_buf);
+int mwifiex_cmd_amsdu_aggr_ctrl(struct mwifiex_private *priv,
+                               struct host_cmd_ds_command *cmd,
+                               int cmd_action,
+                               void *data_buf);
+
+/*
+ * This function checks whether AMPDU is allowed or not for a particular TID.
+ */
+static inline u8
+mwifiex_is_ampdu_allowed(struct mwifiex_private *priv,
+                        struct mwifiex_ra_list_tbl *ptr, int tid)
+{
+       return ((priv->aggr_prio_tbl[tid].ampdu_ap != BA_STREAM_NOT_ALLOWED)
+               ? true : false);
+}
+
+/*
+ * This function checks whether AMSDU is allowed or not for a particular TID.
+ */
+static inline u8
+mwifiex_is_amsdu_allowed(struct mwifiex_private *priv,
+                        struct mwifiex_ra_list_tbl *ptr, 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 BA stream is available or not.
+ */
+static inline u8
+mwifiex_is_ba_stream_avail(struct mwifiex_private *priv)
+{
+       struct mwifiex_private *pmpriv = NULL;
+       u8 i = 0;
+       u32 ba_stream_num = 0;
+
+       for (i = 0; i < priv->adapter->priv_num; i++) {
+               pmpriv = priv->adapter->priv[i];
+               if (pmpriv)
+                       ba_stream_num +=
+                               mwifiex_wmm_list_len(priv->adapter,
+                                                    (struct list_head
+                                                     *) &pmpriv->
+                                                    tx_ba_stream_tbl_ptr);
+       }
+
+       return ((ba_stream_num <
+                MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) ? 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
+mwifiex_find_stream_to_delete(struct mwifiex_private *priv,
+                             struct mwifiex_ra_list_tbl *ptr, int ptr_tid,
+                             int *ptid, u8 *ra)
+{
+       int tid;
+       u8 ret = false;
+       struct mwifiex_tx_ba_stream_tbl *tx_tbl;
+       unsigned long flags;
+
+       tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user;
+
+       spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
+       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_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
+
+       return ret;
+}
+
+/*
+ * This function checks whether BA stream is set up or not.
+ */
+static inline int
+mwifiex_is_ba_stream_setup(struct mwifiex_private *priv,
+                         struct mwifiex_ra_list_tbl *ptr, int tid)
+{
+       struct mwifiex_tx_ba_stream_tbl *tx_tbl;
+
+       tx_tbl = mwifiex_11n_get_tx_ba_stream_tbl(priv, tid, ptr->ra);
+       if (tx_tbl && IS_BASTREAM_SETUP(tx_tbl))
+               return true;
+
+       return false;
+}
+#endif /* !_MWIFIEX_11N_H_ */
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c
new file mode 100644 (file)
index 0000000..c2abced
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * Marvell Wireless LAN device driver: 802.11n Aggregation
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#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
+mwifiex_11n_form_amsdu_pkt(struct mwifiex_adapter *adapter,
+                          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 = NULL;
+
+       skb_put(skb_aggr, sizeof(*tx_header));
+
+       tx_header = (struct tx_packet_hdr *) skb_aggr->data;
+
+       /* 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 = *(u16 *) ((u8 *)skb_src->data + dt_offset);
+       dt_offset += sizeof(u16);
+
+       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(skb_aggr, skb_src->len);
+       memcpy(skb_aggr->data + sizeof(*tx_header), skb_src->data,
+                                                       skb_src->len);
+       *pad = (((skb_src->len + LLC_SNAP_LEN) & 3)) ? (4 - (((skb_src->len +
+                                                     LLC_SNAP_LEN)) & 3)) : 0;
+       skb_put(skb_aggr, *pad);
+
+       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
+mwifiex_11n_form_amsdu_txpd(struct mwifiex_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 =
+               mwifiex_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) == MWIFIEX_BSS_ROLE_STA) &&
+               (priv->adapter->pps_uapsd_mode)) {
+               if (true == mwifiex_check_last_packet_indication(priv)) {
+                       priv->adapter->tx_lock_flag = true;
+                       local_tx_pd->flags =
+                               MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET;
+               }
+       }
+}
+
+/*
+ * Counts the number of subframes in an aggregate packet.
+ *
+ * This function parses an aggregate packet buffer, looking for
+ * subframes and counting the number of such subframe found. The
+ * function automatically skips the DA/SA fields at the beginning
+ * of each subframe and padding at the end.
+ */
+static int
+mwifiex_11n_get_num_aggr_pkts(u8 *data, int total_pkt_len)
+{
+       int pkt_count = 0, pkt_len, pad;
+
+       while (total_pkt_len > 0) {
+               /* Length will be in network format, change it to host */
+               pkt_len = ntohs((*(__be16 *)(data + 2 * ETH_ALEN)));
+               pad = (((pkt_len + sizeof(struct ethhdr)) & 3)) ?
+                       (4 - ((pkt_len + sizeof(struct ethhdr)) & 3)) : 0;
+               data += pkt_len + pad + sizeof(struct ethhdr);
+               total_pkt_len -= pkt_len + pad + sizeof(struct ethhdr);
+               ++pkt_count;
+       }
+
+       return pkt_count;
+}
+
+/*
+ * De-aggregate received packets.
+ *
+ * This function parses the received aggregate buffer, extracts each subframe,
+ * strips off the SNAP header from them and sends the data portion for further
+ * processing.
+ *
+ * Each subframe body is copied onto a separate buffer, which are freed by
+ * upper layer after processing. The function also performs sanity tests on
+ * the received buffer.
+ */
+int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv,
+                               struct sk_buff *skb)
+{
+       u16 pkt_len;
+       int total_pkt_len;
+       u8 *data;
+       int pad;
+       struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
+       struct rxpd *local_rx_pd = (struct rxpd *) skb->data;
+       struct sk_buff *skb_daggr;
+       struct mwifiex_rxinfo *rx_info_daggr = NULL;
+       int ret = -1;
+       struct rx_packet_hdr *rx_pkt_hdr;
+       struct mwifiex_adapter *adapter = priv->adapter;
+       u8 rfc1042_eth_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
+
+       data = (u8 *) (local_rx_pd + local_rx_pd->rx_pkt_offset);
+       total_pkt_len = local_rx_pd->rx_pkt_length;
+
+       /* Sanity test */
+       if (total_pkt_len > MWIFIEX_RX_DATA_BUF_SIZE) {
+               dev_err(adapter->dev, "total pkt len greater than buffer"
+                      " size %d\n", total_pkt_len);
+               return -1;
+       }
+
+       rx_info->use_count = mwifiex_11n_get_num_aggr_pkts(data, total_pkt_len);
+
+       while (total_pkt_len > 0) {
+               rx_pkt_hdr = (struct rx_packet_hdr *) data;
+               /* Length will be in network format, change it to host */
+               pkt_len = ntohs((*(__be16 *) (data + 2 * ETH_ALEN)));
+               if (pkt_len > total_pkt_len) {
+                       dev_err(adapter->dev, "pkt_len %d > total_pkt_len %d\n",
+                              total_pkt_len, pkt_len);
+                       break;
+               }
+
+               pad = (((pkt_len + sizeof(struct ethhdr)) & 3)) ?
+                       (4 - ((pkt_len + sizeof(struct ethhdr)) & 3)) : 0;
+
+               total_pkt_len -= pkt_len + pad + sizeof(struct ethhdr);
+
+               if (memcmp(&rx_pkt_hdr->rfc1042_hdr,
+                          rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) {
+                       memmove(data + LLC_SNAP_LEN, data, 2 * ETH_ALEN);
+                       data += LLC_SNAP_LEN;
+                       pkt_len += sizeof(struct ethhdr) - LLC_SNAP_LEN;
+               } else {
+                       *(u16 *) (data + 2 * ETH_ALEN) = (u16) 0;
+                       pkt_len += sizeof(struct ethhdr);
+               }
+
+               skb_daggr = dev_alloc_skb(pkt_len);
+               if (!skb_daggr) {
+                       dev_err(adapter->dev, "%s: failed to alloc skb_daggr\n",
+                              __func__);
+                       return -1;
+               }
+               rx_info_daggr = MWIFIEX_SKB_RXCB(skb_daggr);
+
+               rx_info_daggr->bss_index = rx_info->bss_index;
+               skb_daggr->tstamp = skb->tstamp;
+               rx_info_daggr->parent = skb;
+               skb_daggr->priority = skb->priority;
+               skb_put(skb_daggr, pkt_len);
+               memcpy(skb_daggr->data, data, pkt_len);
+
+               ret = mwifiex_recv_packet(adapter, skb_daggr);
+
+               switch (ret) {
+               case -EINPROGRESS:
+                       break;
+               case -1:
+                       dev_err(adapter->dev, "deaggr: host_to_card failed\n");
+               case 0:
+                       mwifiex_recv_packet_complete(adapter, skb_daggr, ret);
+                       break;
+               default:
+                       break;
+               }
+
+               data += pkt_len + pad;
+       }
+
+       return ret;
+}
+
+/*
+ * 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
+mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
+                         struct mwifiex_ra_list_tbl *pra_list, int headroom,
+                         int ptrindex, unsigned long ra_list_flags)
+                         __releases(&priv->wmm.ra_list_spinlock)
+{
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct sk_buff *skb_aggr, *skb_src;
+       struct mwifiex_txinfo *tx_info_aggr, *tx_info_src;
+       int pad = 0;
+       int ret = 0;
+       struct mwifiex_tx_param tx_param;
+       struct txpd *ptx_pd = NULL;
+
+       if (skb_queue_empty(&pra_list->skb_head)) {
+               spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+                                      ra_list_flags);
+               return 0;
+       }
+       skb_src = skb_peek(&pra_list->skb_head);
+       tx_info_src = MWIFIEX_SKB_TXCB(skb_src);
+       skb_aggr = dev_alloc_skb(adapter->tx_buf_size);
+       if (!skb_aggr) {
+               dev_err(adapter->dev, "%s: alloc skb_aggr\n", __func__);
+               spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+                                      ra_list_flags);
+               return -1;
+       }
+       skb_reserve(skb_aggr, headroom + sizeof(struct txpd));
+       tx_info_aggr =  MWIFIEX_SKB_TXCB(skb_aggr);
+
+       tx_info_aggr->bss_index = tx_info_src->bss_index;
+       skb_aggr->priority = skb_src->priority;
+
+       while (skb_src && ((skb_headroom(skb_aggr) + skb_src->len
+                                       + LLC_SNAP_LEN)
+                               <= adapter->tx_buf_size)) {
+
+               if (!skb_queue_empty(&pra_list->skb_head))
+                       skb_src = skb_dequeue(&pra_list->skb_head);
+               else
+                       skb_src = NULL;
+
+               pra_list->total_pkts_size -= skb_src->len;
+
+               spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+                                      ra_list_flags);
+               mwifiex_11n_form_amsdu_pkt(adapter, skb_aggr, skb_src, &pad);
+
+               mwifiex_write_data_complete(adapter, skb_src, 0);
+
+               spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags);
+
+               if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) {
+                       spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+                                              ra_list_flags);
+                       return -1;
+               }
+
+               if (!skb_queue_empty(&pra_list->skb_head))
+                       skb_src = skb_peek(&pra_list->skb_head);
+               else
+                       skb_src = NULL;
+       }
+
+       spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags);
+
+       /* Last AMSDU packet does not need padding */
+       skb_trim(skb_aggr, skb_aggr->len - pad);
+
+       /* Form AMSDU */
+       mwifiex_11n_form_amsdu_txpd(priv, skb_aggr);
+       if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)
+               ptx_pd = (struct txpd *)skb_aggr->data;
+
+       skb_push(skb_aggr, headroom);
+
+       tx_param.next_pkt_len = ((pra_list->total_pkts_size) ?
+                                (((pra_list->total_pkts_size) >
+                                  adapter->tx_buf_size) ? adapter->
+                                 tx_buf_size : pra_list->total_pkts_size +
+                                 LLC_SNAP_LEN + sizeof(struct txpd)) : 0);
+       ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
+                                            skb_aggr->data,
+                                            skb_aggr->len, &tx_param);
+       switch (ret) {
+       case -EBUSY:
+               spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags);
+               if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) {
+                       spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+                                              ra_list_flags);
+                       mwifiex_write_data_complete(adapter, skb_aggr, -1);
+                       return -1;
+               }
+               if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
+                       (adapter->pps_uapsd_mode) &&
+                       (adapter->tx_lock_flag)) {
+                               priv->adapter->tx_lock_flag = false;
+                               ptx_pd->flags = 0;
+               }
+
+               skb_queue_tail(&pra_list->skb_head, skb_aggr);
+
+               pra_list->total_pkts_size += skb_aggr->len;
+
+               tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT;
+               spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+                                      ra_list_flags);
+               dev_dbg(adapter->dev, "data: -EBUSY is returned\n");
+               break;
+       case -1:
+               adapter->data_sent = false;
+               dev_err(adapter->dev, "%s: host_to_card failed: %#x\n",
+                                               __func__, ret);
+               adapter->dbg.num_tx_host_to_card_failure++;
+               mwifiex_write_data_complete(adapter, skb_aggr, ret);
+               return 0;
+       case -EINPROGRESS:
+               adapter->data_sent = false;
+               break;
+       case 0:
+               mwifiex_write_data_complete(adapter, skb_aggr, ret);
+               break;
+       default:
+               break;
+       }
+       if (ret != -EBUSY) {
+               spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags);
+               if (mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) {
+                       priv->wmm.packets_out[ptrindex]++;
+                       priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = pra_list;
+               }
+               /* Now bss_prio_cur pointer points to next node */
+               adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur =
+                       list_first_entry(
+                               &adapter->bss_prio_tbl[priv->bss_priority]
+                               .bss_prio_cur->list,
+                               struct mwifiex_bss_prio_node, list);
+               spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
+                                      ra_list_flags);
+       }
+
+       return 0;
+}
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.h b/drivers/net/wireless/mwifiex/11n_aggr.h
new file mode 100644 (file)
index 0000000..9c6dca7
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Marvell Wireless LAN device driver: 802.11n Aggregation
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#ifndef _MWIFIEX_11N_AGGR_H_
+#define _MWIFIEX_11N_AGGR_H_
+
+#define PKT_TYPE_AMSDU 0xE6
+
+int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv,
+                               struct sk_buff *skb);
+int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv,
+                             struct mwifiex_ra_list_tbl *ptr, int headroom,
+                             int ptr_index, unsigned long flags)
+                             __releases(&priv->wmm.ra_list_spinlock);
+
+#endif /* !_MWIFIEX_11N_AGGR_H_ */
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c
new file mode 100644 (file)
index 0000000..8e94e62
--- /dev/null
@@ -0,0 +1,637 @@
+/*
+ * Marvell Wireless LAN device driver: 802.11n RX Re-ordering
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+#include "11n_rxreorder.h"
+
+/*
+ * This function processes a received packet and forwards
+ * it to the kernel/upper layer.
+ */
+static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload)
+{
+       int ret = 0;
+       struct mwifiex_adapter *adapter = priv->adapter;
+
+       ret = mwifiex_process_rx_packet(adapter, (struct sk_buff *) payload);
+       return ret;
+}
+
+/*
+ * This function dispatches all packets in the Rx reorder table.
+ *
+ * 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 int
+mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv,
+                                        struct mwifiex_rx_reorder_tbl
+                                        *rx_reor_tbl_ptr, int start_win)
+{
+       int no_pkt_to_send, i, xchg;
+       void *rx_tmp_ptr = NULL;
+       unsigned long flags;
+
+       no_pkt_to_send = (start_win > rx_reor_tbl_ptr->start_win) ?
+               min((start_win - rx_reor_tbl_ptr->start_win),
+                   rx_reor_tbl_ptr->win_size) : rx_reor_tbl_ptr->win_size;
+
+       for (i = 0; i < no_pkt_to_send; ++i) {
+               spin_lock_irqsave(&priv->rx_pkt_lock, flags);
+               rx_tmp_ptr = NULL;
+               if (rx_reor_tbl_ptr->rx_reorder_ptr[i]) {
+                       rx_tmp_ptr = rx_reor_tbl_ptr->rx_reorder_ptr[i];
+                       rx_reor_tbl_ptr->rx_reorder_ptr[i] = NULL;
+               }
+               spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
+               if (rx_tmp_ptr)
+                       mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr);
+       }
+
+       spin_lock_irqsave(&priv->rx_pkt_lock, flags);
+       /*
+        * We don't have a circular buffer, hence use rotation to simulate
+        * circular buffer
+        */
+       xchg = rx_reor_tbl_ptr->win_size - no_pkt_to_send;
+       for (i = 0; i < xchg; ++i) {
+               rx_reor_tbl_ptr->rx_reorder_ptr[i] =
+                       rx_reor_tbl_ptr->rx_reorder_ptr[no_pkt_to_send + i];
+               rx_reor_tbl_ptr->rx_reorder_ptr[no_pkt_to_send + i] = NULL;
+       }
+
+       rx_reor_tbl_ptr->start_win = start_win;
+       spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
+
+       return 0;
+}
+
+/*
+ * 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 int
+mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv,
+                             struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr)
+{
+       int i, j, xchg;
+       void *rx_tmp_ptr = NULL;
+       unsigned long flags;
+
+       for (i = 0; i < rx_reor_tbl_ptr->win_size; ++i) {
+               spin_lock_irqsave(&priv->rx_pkt_lock, flags);
+               if (!rx_reor_tbl_ptr->rx_reorder_ptr[i]) {
+                       spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
+                       break;
+               }
+               rx_tmp_ptr = rx_reor_tbl_ptr->rx_reorder_ptr[i];
+               rx_reor_tbl_ptr->rx_reorder_ptr[i] = NULL;
+               spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
+               mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr);
+       }
+
+       spin_lock_irqsave(&priv->rx_pkt_lock, flags);
+       /*
+        * We don't have a circular buffer, hence use rotation to simulate
+        * circular buffer
+        */
+       if (i > 0) {
+               xchg = rx_reor_tbl_ptr->win_size - i;
+               for (j = 0; j < xchg; ++j) {
+                       rx_reor_tbl_ptr->rx_reorder_ptr[j] =
+                               rx_reor_tbl_ptr->rx_reorder_ptr[i + j];
+                       rx_reor_tbl_ptr->rx_reorder_ptr[i + j] = NULL;
+               }
+       }
+       rx_reor_tbl_ptr->start_win = (rx_reor_tbl_ptr->start_win + i)
+               &(MAX_TID_VALUE - 1);
+       spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
+       return 0;
+}
+
+/*
+ * 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
+mwifiex_11n_delete_rx_reorder_tbl_entry(struct mwifiex_private *priv,
+                                      struct mwifiex_rx_reorder_tbl
+                                      *rx_reor_tbl_ptr)
+{
+       unsigned long flags;
+
+       if (!rx_reor_tbl_ptr)
+               return;
+
+       mwifiex_11n_dispatch_pkt_until_start_win(priv, rx_reor_tbl_ptr,
+                                                (rx_reor_tbl_ptr->start_win +
+                                                 rx_reor_tbl_ptr->win_size)
+                                                &(MAX_TID_VALUE - 1));
+
+       del_timer(&rx_reor_tbl_ptr->timer_context.timer);
+
+       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+       list_del(&rx_reor_tbl_ptr->list);
+       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+
+       kfree(rx_reor_tbl_ptr->rx_reorder_ptr);
+       kfree(rx_reor_tbl_ptr);
+}
+
+/*
+ * This function returns the pointer to an entry in Rx reordering
+ * table which matches the given TA/TID pair.
+ */
+static struct mwifiex_rx_reorder_tbl *
+mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta)
+{
+       struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+       list_for_each_entry(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) {
+               if ((!memcmp(rx_reor_tbl_ptr->ta, ta, ETH_ALEN))
+                   && (rx_reor_tbl_ptr->tid == tid)) {
+                       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
+                                              flags);
+                       return rx_reor_tbl_ptr;
+               }
+       }
+       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+
+       return NULL;
+}
+
+/*
+ * This function finds the last sequence number used in the packets
+ * buffered in Rx reordering table.
+ */
+static int
+mwifiex_11n_find_last_seq_num(struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr)
+{
+       int i;
+
+       for (i = (rx_reorder_tbl_ptr->win_size - 1); i >= 0; --i)
+               if (rx_reorder_tbl_ptr->rx_reorder_ptr[i])
+                       return i;
+
+       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
+mwifiex_flush_data(unsigned long context)
+{
+       struct reorder_tmr_cnxt *reorder_cnxt =
+               (struct reorder_tmr_cnxt *) context;
+       int start_win;
+
+       start_win = mwifiex_11n_find_last_seq_num(reorder_cnxt->ptr);
+       if (start_win >= 0) {
+               dev_dbg(reorder_cnxt->priv->adapter->dev,
+                               "info: flush data %d\n", start_win);
+               mwifiex_11n_dispatch_pkt_until_start_win(reorder_cnxt->priv,
+                               reorder_cnxt->ptr,
+                               ((reorder_cnxt->ptr->start_win +
+                                 start_win + 1) & (MAX_TID_VALUE - 1)));
+       }
+}
+
+/*
+ * 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
+mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta,
+                                int tid, int win_size, int seq_num)
+{
+       int i;
+       struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr, *new_node;
+       u16 last_seq = 0;
+       unsigned long flags;
+
+       /*
+        * If we get a TID, ta pair which is already present dispatch all the
+        * the packets and move the window size until the ssn
+        */
+       rx_reor_tbl_ptr = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
+       if (rx_reor_tbl_ptr) {
+               mwifiex_11n_dispatch_pkt_until_start_win(priv, rx_reor_tbl_ptr,
+                                                        seq_num);
+               return;
+       }
+       /* if !rx_reor_tbl_ptr then create one */
+       new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL);
+       if (!new_node) {
+               dev_err(priv->adapter->dev, "%s: failed to alloc new_node\n",
+                      __func__);
+               return;
+       }
+
+       INIT_LIST_HEAD(&new_node->list);
+       new_node->tid = tid;
+       memcpy(new_node->ta, ta, ETH_ALEN);
+       new_node->start_win = seq_num;
+       if (mwifiex_queuing_ra_based(priv))
+               /* TODO for adhoc */
+               dev_dbg(priv->adapter->dev,
+                       "info: ADHOC:last_seq=%d start_win=%d\n",
+                       last_seq, new_node->start_win);
+       else
+               last_seq = priv->rx_seq[tid];
+
+       if (last_seq >= new_node->start_win)
+               new_node->start_win = last_seq + 1;
+
+       new_node->win_size = win_size;
+
+       new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size,
+                                       GFP_KERNEL);
+       if (!new_node->rx_reorder_ptr) {
+               kfree((u8 *) new_node);
+               dev_err(priv->adapter->dev,
+                       "%s: failed to alloc reorder_ptr\n", __func__);
+               return;
+       }
+
+       new_node->timer_context.ptr = new_node;
+       new_node->timer_context.priv = priv;
+
+       init_timer(&new_node->timer_context.timer);
+       new_node->timer_context.timer.function = mwifiex_flush_data;
+       new_node->timer_context.timer.data =
+                       (unsigned long) &new_node->timer_context;
+
+       for (i = 0; i < win_size; ++i)
+               new_node->rx_reorder_ptr[i] = NULL;
+
+       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+       list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr);
+       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+
+       return;
+}
+
+/*
+ * 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 mwifiex_cmd_11n_addba_req(struct mwifiex_private *priv,
+                             struct host_cmd_ds_command *cmd, void *data_buf)
+{
+       struct host_cmd_ds_11n_addba_req *add_ba_req =
+               (struct host_cmd_ds_11n_addba_req *)
+               &cmd->params.add_ba_req;
+
+       cmd->command = cpu_to_le16(HostCmd_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 mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv,
+                                 struct host_cmd_ds_command *cmd,
+                                 void *data_buf)
+{
+       struct host_cmd_ds_11n_addba_rsp *add_ba_rsp =
+               (struct host_cmd_ds_11n_addba_rsp *)
+               &cmd->params.add_ba_rsp;
+       struct host_cmd_ds_11n_addba_req *cmd_addba_req =
+               (struct host_cmd_ds_11n_addba_req *) data_buf;
+       u8 tid = 0;
+       int win_size = 0;
+       uint16_t block_ack_param_set;
+
+       cmd->command = cpu_to_le16(HostCmd_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;
+       /* We donot support AMSDU inside AMPDU, hence reset the bit */
+       block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK;
+       block_ack_param_set |= (priv->add_ba_param.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);
+
+       mwifiex_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 mwifiex_cmd_11n_delba(struct mwifiex_private *priv,
+                         struct host_cmd_ds_command *cmd, void *data_buf)
+{
+       struct host_cmd_ds_11n_delba *del_ba = (struct host_cmd_ds_11n_delba *)
+               &cmd->params.del_ba;
+
+       cmd->command = cpu_to_le16(HostCmd_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 mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv,
+                               u16 seq_num, u16 tid,
+                               u8 *ta, u8 pkt_type, void *payload)
+{
+       struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr;
+       int start_win, end_win, win_size;
+       int ret = 0;
+       u16 pkt_index = 0;
+
+       rx_reor_tbl_ptr =
+               mwifiex_11n_get_rx_reorder_tbl((struct mwifiex_private *) priv,
+                                               tid, ta);
+       if (!rx_reor_tbl_ptr) {
+               if (pkt_type != PKT_TYPE_BAR)
+                       mwifiex_11n_dispatch_pkt(priv, payload);
+               return 0;
+       }
+       start_win = rx_reor_tbl_ptr->start_win;
+       win_size = rx_reor_tbl_ptr->win_size;
+       end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
+       del_timer(&rx_reor_tbl_ptr->timer_context.timer);
+       mod_timer(&rx_reor_tbl_ptr->timer_context.timer, jiffies
+                       + (MIN_FLUSH_TIMER_MS * win_size * HZ) / 1000);
+
+       /*
+        * If seq_num is less then starting win then ignore and drop the
+        * packet
+        */
+       if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {/* Wrap */
+               if (seq_num >= ((start_win + (TWOPOW11)) & (MAX_TID_VALUE - 1))
+                               && (seq_num < start_win))
+                       return -1;
+       } else if ((seq_num < start_win)
+                       || (seq_num > (start_win + (TWOPOW11)))) {
+               return -1;
+       }
+
+       /*
+        * 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 < (TWOPOW11 - (MAX_TID_VALUE - start_win)))
+            && (seq_num > end_win)) || ((end_win > start_win)
+            && ((seq_num > end_win) || (seq_num < start_win)))) {
+               end_win = seq_num;
+               if (((seq_num - win_size) + 1) >= 0)
+                       start_win = (end_win - win_size) + 1;
+               else
+                       start_win = (MAX_TID_VALUE - (win_size - seq_num)) + 1;
+               ret = mwifiex_11n_dispatch_pkt_until_start_win(priv,
+                                               rx_reor_tbl_ptr, start_win);
+
+               if (ret)
+                       return ret;
+       }
+
+       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 (rx_reor_tbl_ptr->rx_reorder_ptr[pkt_index])
+                       return -1;
+
+               rx_reor_tbl_ptr->rx_reorder_ptr[pkt_index] = payload;
+       }
+
+       /*
+        * Dispatch all packets sequentially from start_win until a
+        * hole is found and adjust the start_win appropriately
+        */
+       ret = mwifiex_11n_scan_and_dispatch(priv, rx_reor_tbl_ptr);
+
+       return ret;
+}
+
+/*
+ * This function deletes an entry for a given TID/TA pair.
+ *
+ * The TID/TA are taken from del BA event body.
+ */
+void
+mwifiex_11n_delete_ba_stream_tbl(struct mwifiex_private *priv, int tid,
+                               u8 *peer_mac, u8 type, int initiator)
+{
+       struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr;
+       struct mwifiex_tx_ba_stream_tbl *ptx_tbl;
+       u8 cleanup_rx_reorder_tbl;
+       unsigned long flags;
+
+       if (type == TYPE_DELBA_RECEIVE)
+               cleanup_rx_reorder_tbl = (initiator) ? true : false;
+       else
+               cleanup_rx_reorder_tbl = (initiator) ? false : true;
+
+       dev_dbg(priv->adapter->dev, "event: DELBA: %pM tid=%d, "
+              "initiator=%d\n", peer_mac, tid, initiator);
+
+       if (cleanup_rx_reorder_tbl) {
+               rx_reor_tbl_ptr = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
+                                                                peer_mac);
+               if (!rx_reor_tbl_ptr) {
+                       dev_dbg(priv->adapter->dev,
+                                       "event: TID, TA not found in table\n");
+                       return;
+               }
+               mwifiex_11n_delete_rx_reorder_tbl_entry(priv, rx_reor_tbl_ptr);
+       } else {
+               ptx_tbl = mwifiex_11n_get_tx_ba_stream_tbl(priv, tid, peer_mac);
+               if (!ptx_tbl) {
+                       dev_dbg(priv->adapter->dev,
+                                       "event: TID, RA not found in table\n");
+                       return;
+               }
+
+               spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
+               mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl);
+               spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
+       }
+}
+
+/*
+ * 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 mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv,
+                              struct host_cmd_ds_command *resp)
+{
+       struct host_cmd_ds_11n_addba_rsp *add_ba_rsp =
+               (struct host_cmd_ds_11n_addba_rsp *)
+               &resp->params.add_ba_rsp;
+       int tid, win_size;
+       struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr = NULL;
+       uint16_t 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) {
+               win_size = (block_ack_param_set &
+                       IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
+                       >> BLOCKACKPARAM_WINSIZE_POS;
+
+               dev_dbg(priv->adapter->dev, "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);
+       } else {
+               dev_err(priv->adapter->dev, "ADDBA RSP: failed %pM tid=%d)\n",
+                                       add_ba_rsp->peer_mac_addr, tid);
+
+               rx_reor_tbl_ptr = mwifiex_11n_get_rx_reorder_tbl(priv,
+                                       tid, add_ba_rsp->peer_mac_addr);
+               if (rx_reor_tbl_ptr)
+                       mwifiex_11n_delete_rx_reorder_tbl_entry(priv,
+                               rx_reor_tbl_ptr);
+       }
+
+       return 0;
+}
+
+/*
+ * This function handles BA stream timeout event by preparing and sending
+ * a command to the firmware.
+ */
+void mwifiex_11n_ba_stream_timeout(struct mwifiex_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);
+       mwifiex_prepare_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, NULL, &delba);
+
+       return;
+}
+
+/*
+ * This function cleans up the Rx reorder table by deleting all the entries
+ * and re-initializing.
+ */
+void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv)
+{
+       struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+       list_for_each_entry_safe(del_tbl_ptr, tmp_node,
+                                &priv->rx_reorder_tbl_ptr, list) {
+               spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+               mwifiex_11n_delete_rx_reorder_tbl_entry(priv, del_tbl_ptr);
+               spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
+       }
+       spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
+
+       INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
+       memset(priv->rx_seq, 0, sizeof(priv->rx_seq));
+}
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h
new file mode 100644 (file)
index 0000000..42f5690
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Marvell Wireless LAN device driver: 802.11n RX Re-ordering
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#ifndef _MWIFIEX_11N_RXREORDER_H_
+#define _MWIFIEX_11N_RXREORDER_H_
+
+#define MIN_FLUSH_TIMER_MS             50
+
+#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
+
+int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *,
+                              u16 seqNum,
+                              u16 tid, u8 *ta,
+                              u8 pkttype, void *payload);
+void mwifiex_11n_delete_ba_stream_tbl(struct mwifiex_private *priv, int Tid,
+                                    u8 *PeerMACAddr, u8 type,
+                                    int initiator);
+void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv,
+                                  struct host_cmd_ds_11n_batimeout *event);
+int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv,
+                              struct host_cmd_ds_command
+                              *resp);
+int mwifiex_cmd_11n_delba(struct mwifiex_private *priv,
+                         struct host_cmd_ds_command *cmd,
+                         void *data_buf);
+int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv,
+                                 struct host_cmd_ds_command
+                                 *cmd, void *data_buf);
+int mwifiex_cmd_11n_addba_req(struct mwifiex_private *priv,
+                             struct host_cmd_ds_command *cmd,
+                             void *data_buf);
+void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv);
+struct mwifiex_rx_reorder_tbl *mwifiex_11n_get_rxreorder_tbl(struct
+                                                          mwifiex_private
+                                                          *priv, int tid,
+                                                          u8 *ta);
+
+#endif /* _MWIFIEX_11N_RXREORDER_H_ */
diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig
new file mode 100644 (file)
index 0000000..8696292
--- /dev/null
@@ -0,0 +1,21 @@
+config MWIFIEX
+       tristate "Marvell WiFi-Ex Driver"
+       depends on CFG80211
+       select LIB80211
+       ---help---
+         This adds support for wireless adapters based on Marvell
+         802.11n chipsets.
+
+         If you choose to build it as a module, it will be called
+         mwifiex.
+
+config MWIFIEX_SDIO
+       tristate "Marvell WiFi-Ex Driver for SD8787"
+       depends on MWIFIEX && MMC
+       select FW_LOADER
+       ---help---
+         This adds support for wireless adapters based on Marvell
+         8787 chipset with SDIO interface.
+
+         If you choose to build it as a module, it will be called
+         mwifiex_sdio.
diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile
new file mode 100644 (file)
index 0000000..42cb733
--- /dev/null
@@ -0,0 +1,41 @@
+#
+# Copyright (C) 2011, Marvell International Ltd.
+#
+# This software file (the "File") is distributed by Marvell International
+# Ltd. under the terms of the GNU General Public License Version 2, June 1991
+# (the "License").  You may use, redistribute and/or modify this File in
+# accordance with the terms and conditions of the License, a copy of which
+# is available by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+# worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+#
+# THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+# ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+# this warranty disclaimer.
+
+
+mwifiex-y += main.o
+mwifiex-y += init.o
+mwifiex-y += cfp.o
+mwifiex-y += cmdevt.o
+mwifiex-y += util.o
+mwifiex-y += txrx.o
+mwifiex-y += wmm.o
+mwifiex-y += 11n.o
+mwifiex-y += 11n_aggr.o
+mwifiex-y += 11n_rxreorder.o
+mwifiex-y += scan.o
+mwifiex-y += join.o
+mwifiex-y += sta_ioctl.o
+mwifiex-y += sta_cmd.o
+mwifiex-y += sta_cmdresp.o
+mwifiex-y += sta_event.o
+mwifiex-y += sta_tx.o
+mwifiex-y += sta_rx.o
+mwifiex-y += cfg80211.o
+mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o
+obj-$(CONFIG_MWIFIEX) += mwifiex.o
+
+mwifiex_sdio-y += sdio.o
+obj-$(CONFIG_MWIFIEX_SDIO) += mwifiex_sdio.o
diff --git a/drivers/net/wireless/mwifiex/README b/drivers/net/wireless/mwifiex/README
new file mode 100644 (file)
index 0000000..338377f
--- /dev/null
@@ -0,0 +1,204 @@
+# Copyright (C) 2011, Marvell International Ltd.
+#
+# This software file (the "File") is distributed by Marvell International
+# Ltd. under the terms of the GNU General Public License Version 2, June 1991
+# (the "License").  You may use, redistribute and/or modify this File in
+# accordance with the terms and conditions of the License, a copy of which
+# is available by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+# worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+#
+# THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+# ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+# this warranty disclaimer.
+
+
+===============================================================================
+                       U S E R  M A N U A L
+
+1) FOR DRIVER INSTALL
+
+       a) Copy sd8787.bin to /lib/firmware/mrvl/ directory,
+          create the directory if it doesn't exist.
+       b) Install WLAN driver,
+               insmod mwifiex.ko
+       c) Uninstall WLAN driver,
+               ifconfig mlanX down
+               rmmod mwifiex
+
+
+2) FOR DRIVER CONFIGURATION AND INFO
+       The configurations can be done either using the 'iw' user space
+       utility or debugfs.
+
+       a) 'iw' utility commands
+
+       Following are some useful iw commands:-
+
+iw dev mlan0 scan
+
+       This command will trigger a scan.
+       The command will then display the scan table entries
+
+iw dev mlan0 connect -w <SSID> [<freq in MHz>] [<bssid>] [key 0:abcde d:1123456789a]
+       The above command can be used to connect to an AP with a particular SSID.
+       Ap's operating frequency can be specified or even the bssid. If the AP is using
+       WEP encryption, wep keys can be specified in the command.
+       Note: Every time before connecting to an AP scan command (iw dev mlan0 scan) should be used by user.
+
+iw dev mlan0 disconnect
+       This command will be used to disconnect from an AP.
+
+
+iw dev mlan0 ibss join <SSID> <freq in MHz> [fixed-freq] [fixed-bssid] [key 0:abcde]
+       The command will be used to join or create an ibss. Optionally, operating frequency,
+       bssid and the security related parameters can be specified while joining/creating
+       and ibss.
+
+iw dev mlan0 ibss leave
+       The command will be used to leave an ibss network.
+
+iw dev mlan0 link
+       The command will be used to get the connection status. The command will return parameters
+       such as SSID, operating frequency, rx/tx packets, signal strength, tx bitrate.
+
+       Apart from the iw utility all standard configurations using the 'iwconfig' utility are also supported.
+
+       b) Debugfs interface
+
+       The debugfs interface can be used for configurations and for getting
+       some useful information from the driver.
+       The section below explains the configurations that can be
+       done.
+
+       Mount debugfs to /debugfs mount point:
+
+               mkdir /debugfs
+               mount -t debugfs debugfs /debugfs
+
+       The information is provided in /debugfs/mwifiex/mlanX/:
+
+iw reg set <country code>
+       The command will be used to change the regulatory domain.
+
+iw reg get
+       The command will be used to get current regulatory domain.
+
+info
+       This command is used to get driver info.
+
+       Usage:
+               cat info
+
+       driver_name = "mwifiex"
+       driver_version = <driver_name, driver_version, (firmware_version)>
+       interface_name = "mlanX"
+       bss_mode = "Ad-hoc" | "Managed" | "Auto" | "Unknown"
+       media_state = "Disconnected" | "Connected"
+       mac_address = <6-byte adapter MAC address>
+       multicase_count = <multicast address count>
+       essid = <current SSID>
+       bssid = <current BSSID>
+       channel = <current channel>
+       region_code = <current region code>
+       multicasr_address[n] = <multicast address>
+       num_tx_bytes = <number of bytes sent to device>
+       num_rx_bytes = <number of bytes received from device and sent to kernel>
+       num_tx_pkts = <number of packets sent to device>
+       num_rx_pkts = <number of packets received from device and sent to kernel>
+       num_tx_pkts_dropped = <number of Tx packets dropped by driver>
+       num_rx_pkts_dropped = <number of Rx packets dropped by driver>
+       num_tx_pkts_err = <number of Tx packets failed to send to device>
+       num_rx_pkts_err = <number of Rx packets failed to receive from device>
+       carrier "on" | "off"
+       tx queue "stopped" | "started"
+
+       The following debug info are provided in /debugfs/mwifiex/mlanX/debug:
+
+       int_counter = <interrupt count, cleared when interrupt handled>
+       wmm_ac_vo = <number of packets sent to device from WMM AcVo queue>
+       wmm_ac_vi = <number of packets sent to device from WMM AcVi queue>
+       wmm_ac_be = <number of packets sent to device from WMM AcBE queue>
+       wmm_ac_bk = <number of packets sent to device from WMM AcBK queue>
+       max_tx_buf_size = <maximum Tx buffer size>
+       tx_buf_size = <current Tx buffer size>
+       curr_tx_buf_size = <current Tx buffer size>
+       ps_mode = <0/1, CAM mode/PS mode>
+       ps_state = <0/1/2/3, full power state/awake state/pre-sleep state/sleep state>
+       is_deep_sleep = <0/1, not deep sleep state/deep sleep state>
+       wakeup_dev_req = <0/1, wakeup device not required/required>
+       wakeup_tries = <wakeup device count, cleared when device awake>
+       hs_configured = <0/1, host sleep not configured/configured>
+       hs_activated = <0/1, extended host sleep not activated/activated>
+       num_tx_timeout = <number of Tx timeout>
+       num_cmd_timeout = <number of timeout commands>
+       timeout_cmd_id = <command id of the last timeout command>
+       timeout_cmd_act = <command action of the last timeout command>
+       last_cmd_id = <command id of the last several commands sent to device>
+       last_cmd_act = <command action of the last several commands sent to device>
+       last_cmd_index = <0 based last command index>
+       last_cmd_resp_id = <command id of the last several command responses received from device>
+       last_cmd_resp_index = <0 based last command response index>
+       last_event = <event id of the last several events received from device>
+       last_event_index = <0 based last event index>
+       num_cmd_h2c_fail = <number of commands failed to send to device>
+       num_cmd_sleep_cfm_fail = <number of sleep confirm failed to send to device>
+       num_tx_h2c_fail = <number of data packets failed to send to device>
+       num_evt_deauth = <number of deauthenticated events received from device>
+       num_evt_disassoc = <number of disassociated events received from device>
+       num_evt_link_lost = <number of link lost events received from device>
+       num_cmd_deauth = <number of deauthenticate commands sent to device>
+       num_cmd_assoc_ok = <number of associate commands with success return>
+       num_cmd_assoc_fail = <number of associate commands with failure return>
+       cmd_sent = <0/1, send command resources available/sending command to device>
+       data_sent = <0/1, send data resources available/sending data to device>
+       mp_rd_bitmap = <SDIO multi-port read bitmap>
+       mp_wr_bitmap = <SDIO multi-port write bitmap>
+       cmd_resp_received = <0/1, no cmd response to process/response received and yet to process>
+       event_received = <0/1, no event to process/event received and yet to process>
+       ioctl_pending = <number of ioctl pending>
+       tx_pending = <number of Tx packet pending>
+       rx_pending = <number of Rx packet pending>
+
+
+3) FOR DRIVER CONFIGURATION
+
+regrdwr
+       This command is used to read/write the adapter register.
+
+       Usage:
+               echo " <type> <offset> [value]" > regrdwr
+               cat regrdwr
+
+       where the parameters are,
+               <type>:     1:MAC/SOC, 2:BBP, 3:RF, 4:PMIC, 5:CAU
+               <offset>:   offset of register
+               [value]:    value to be written
+
+       Examples:
+               echo "1 0xa060" > regrdwr           : Read the MAC register
+               echo "1 0xa060 0x12" > regrdwr      : Write the MAC register
+               echo "1 0xa794 0x80000000" > regrdwr
+                                                   : Write 0x80000000 to MAC register
+rdeeprom
+       This command is used to read the EEPROM contents of the card.
+
+       Usage:
+               echo "<offset> <length>" > rdeeprom
+               cat rdeeprom
+
+       where the parameters are,
+               <offset>:   multiples of 4
+               <length>:   4-20, multiples of 4
+
+       Example:
+               echo "0 20" > rdeeprom      : Read 20 bytes of EEPROM data from offset 0
+
+getlog
+        This command is used to get the statistics available in the station.
+       Usage:
+
+       cat getlog
+
+===============================================================================
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
new file mode 100644 (file)
index 0000000..80f367f
--- /dev/null
@@ -0,0 +1,1517 @@
+/*
+ * Marvell Wireless LAN device driver: CFG80211
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include "cfg80211.h"
+#include "main.h"
+
+/*
+ * This function maps the nl802.11 channel type into driver channel type.
+ *
+ * The mapping is as follows -
+ *      NL80211_CHAN_NO_HT     -> NO_SEC_CHANNEL
+ *      NL80211_CHAN_HT20      -> NO_SEC_CHANNEL
+ *      NL80211_CHAN_HT40PLUS  -> SEC_CHANNEL_ABOVE
+ *      NL80211_CHAN_HT40MINUS -> SEC_CHANNEL_BELOW
+ *      Others                 -> NO_SEC_CHANNEL
+ */
+static int
+mwifiex_cfg80211_channel_type_to_mwifiex_channels(enum nl80211_channel_type
+                                                 channel_type)
+{
+       int channel;
+       switch (channel_type) {
+       case NL80211_CHAN_NO_HT:
+       case NL80211_CHAN_HT20:
+               channel = NO_SEC_CHANNEL;
+               break;
+       case NL80211_CHAN_HT40PLUS:
+               channel = SEC_CHANNEL_ABOVE;
+               break;
+       case NL80211_CHAN_HT40MINUS:
+               channel = SEC_CHANNEL_BELOW;
+               break;
+       default:
+               channel = NO_SEC_CHANNEL;
+       }
+       return channel;
+}
+
+/*
+ * This function maps the driver channel type into nl802.11 channel type.
+ *
+ * The mapping is as follows -
+ *      NO_SEC_CHANNEL      -> NL80211_CHAN_HT20
+ *      SEC_CHANNEL_ABOVE   -> NL80211_CHAN_HT40PLUS
+ *      SEC_CHANNEL_BELOW   -> NL80211_CHAN_HT40MINUS
+ *      Others              -> NL80211_CHAN_HT20
+ */
+static enum nl80211_channel_type
+mwifiex_channels_to_cfg80211_channel_type(int channel_type)
+{
+       int channel;
+       switch (channel_type) {
+       case NO_SEC_CHANNEL:
+               channel = NL80211_CHAN_HT20;
+               break;
+       case SEC_CHANNEL_ABOVE:
+               channel = NL80211_CHAN_HT40PLUS;
+               break;
+       case SEC_CHANNEL_BELOW:
+               channel = NL80211_CHAN_HT40MINUS;
+               break;
+       default:
+               channel = NL80211_CHAN_HT20;
+       }
+       return channel;
+}
+
+/*
+ * This function checks whether WEP is set.
+ */
+static int
+mwifiex_is_alg_wep(u32 cipher)
+{
+       int alg = 0;
+
+       switch (cipher) {
+       case MWIFIEX_ENCRYPTION_MODE_WEP40:
+       case MWIFIEX_ENCRYPTION_MODE_WEP104:
+               alg = 1;
+               break;
+       default:
+               alg = 0;
+               break;
+       }
+       return alg;
+}
+
+/*
+ * This function maps the given cipher type into driver specific type.
+ *
+ * It also sets a flag to indicate whether WPA is enabled or not.
+ *
+ * The mapping table is -
+ *      Input cipher                Driver cipher type              WPA enabled?
+ *      ------------                ------------------              ------------
+ *      IW_AUTH_CIPHER_NONE         MWIFIEX_ENCRYPTION_MODE_NONE    No
+ *      WLAN_CIPHER_SUITE_WEP40     MWIFIEX_ENCRYPTION_MODE_WEP40   No
+ *      WLAN_CIPHER_SUITE_WEP104    MWIFIEX_ENCRYPTION_MODE_WEP104  No
+ *      WLAN_CIPHER_SUITE_TKIP      MWIFIEX_ENCRYPTION_MODE_TKIP    Yes
+ *      WLAN_CIPHER_SUITE_CCMP      MWIFIEX_ENCRYPTION_MODE_CCMP    Yes
+ *      Others                      -1                              No
+ */
+static int
+mwifiex_get_mwifiex_cipher(u32 cipher, int *wpa_enabled)
+{
+       int encrypt_mode;
+
+       if (wpa_enabled)
+               *wpa_enabled = 0;
+       switch (cipher) {
+       case IW_AUTH_CIPHER_NONE:
+               encrypt_mode = MWIFIEX_ENCRYPTION_MODE_NONE;
+               break;
+       case WLAN_CIPHER_SUITE_WEP40:
+               encrypt_mode = MWIFIEX_ENCRYPTION_MODE_WEP40;
+               break;
+       case WLAN_CIPHER_SUITE_WEP104:
+               encrypt_mode = MWIFIEX_ENCRYPTION_MODE_WEP104;
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+               encrypt_mode = MWIFIEX_ENCRYPTION_MODE_TKIP;
+               if (wpa_enabled)
+                       *wpa_enabled = 1;
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               encrypt_mode = MWIFIEX_ENCRYPTION_MODE_CCMP;
+               if (wpa_enabled)
+                       *wpa_enabled = 1;
+               break;
+       default:
+               encrypt_mode = -1;
+       }
+
+       return encrypt_mode;
+}
+
+/*
+ * This function retrieves the private structure from kernel wiphy structure.
+ */
+static void *mwifiex_cfg80211_get_priv(struct wiphy *wiphy)
+{
+       return (void *) (*(unsigned long *) wiphy_priv(wiphy));
+}
+
+/*
+ * CFG802.11 operation handler to delete a network key.
+ */
+static int
+mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev,
+                        u8 key_index, bool pairwise, const u8 *mac_addr)
+{
+       struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
+       int ret = 0;
+
+       ret = mwifiex_set_encode(priv, NULL, 0, key_index, 1);
+       if (ret) {
+               wiphy_err(wiphy, "deleting the crypto keys\n");
+               return -EFAULT;
+       }
+
+       wiphy_dbg(wiphy, "info: crypto keys deleted\n");
+       return 0;
+}
+
+/*
+ * CFG802.11 operation handler to set Tx power.
+ */
+static int
+mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy,
+                             enum nl80211_tx_power_setting type,
+                             int dbm)
+{
+       int ret = 0;
+       struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
+
+       ret = mwifiex_set_tx_power(priv, type, dbm);
+
+       return ret;
+}
+
+/*
+ * CFG802.11 operation handler to set Power Save option.
+ *
+ * The timeout value, if provided, is currently ignored.
+ */
+static int
+mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+                               struct net_device *dev,
+                               bool enabled, int timeout)
+{
+       int ret = 0;
+       struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
+
+       if (timeout)
+               wiphy_dbg(wiphy,
+                       "info: ignoring the timeout value"
+                       " for IEEE power save\n");
+
+       ret = mwifiex_drv_set_power(priv, enabled);
+
+       return ret;
+}
+
+/*
+ * CFG802.11 operation handler to set the default network key.
+ */
+static int
+mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev,
+                                u8 key_index, bool unicast,
+                                bool multicast)
+{
+       struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
+       int ret;
+
+       ret = mwifiex_set_encode(priv, NULL, 0, key_index, 0);
+
+       wiphy_dbg(wiphy, "info: set default Tx key index\n");
+
+       if (ret)
+               return -EFAULT;
+
+       return 0;
+}
+
+/*
+ * CFG802.11 operation handler to add a network key.
+ */
+static int
+mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev,
+                        u8 key_index, bool pairwise, const u8 *mac_addr,
+                        struct key_params *params)
+{
+       struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
+       int ret = 0;
+       int encrypt_mode;
+
+       encrypt_mode = mwifiex_get_mwifiex_cipher(params->cipher, NULL);
+
+       if (encrypt_mode != -1)
+               ret = mwifiex_set_encode(priv, params->key, params->key_len,
+                                               key_index, 0);
+
+       wiphy_dbg(wiphy, "info: crypto keys added\n");
+
+       if (ret)
+               return -EFAULT;
+
+       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)
+ */
+static int mwifiex_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 ieee80211_band band;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_channel *ch;
+       struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct mwifiex_802_11d_domain_reg *domain_info = &adapter->domain_reg;
+       int ret = 0;
+
+       /* Set country code */
+       domain_info->country_code[0] = priv->country_code[0];
+       domain_info->country_code[1] = priv->country_code[1];
+       domain_info->country_code[2] = ' ';
+
+       band = mwifiex_band_to_radio_type(adapter->config_bands);
+       if (!wiphy->bands[band]) {
+               wiphy_err(wiphy, "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;
+       /* Send cmd to FW to set domain info */
+       ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO,
+                                 HostCmd_ACT_GEN_SET, 0, NULL, NULL);
+       if (ret)
+               wiphy_err(wiphy, "11D: setting domain info in FW\n");
+
+       return ret;
+}
+
+/*
+ * 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 int mwifiex_reg_notifier(struct wiphy *wiphy,
+               struct regulatory_request *request)
+{
+       struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
+
+       wiphy_dbg(wiphy, "info: cfg80211 regulatory domain callback for domain"
+                       " %c%c\n", request->alpha2[0], request->alpha2[1]);
+
+       memcpy(priv->country_code, request->alpha2, sizeof(request->alpha2));
+
+       switch (request->initiator) {
+       case NL80211_REGDOM_SET_BY_DRIVER:
+       case NL80211_REGDOM_SET_BY_CORE:
+       case NL80211_REGDOM_SET_BY_USER:
+               break;
+               /* Todo: apply driver specific changes in channel flags based
+                  on the request initiator if necessary. */
+       case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+               break;
+       }
+       mwifiex_send_domain_info_cmd_fw(wiphy);
+
+       return 0;
+}
+
+/*
+ * This function sets the RF channel.
+ *
+ * This function creates multiple IOCTL requests, populates them accordingly
+ * and issues them to set the band/channel and frequency.
+ */
+static int
+mwifiex_set_rf_channel(struct mwifiex_private *priv,
+                      struct ieee80211_channel *chan,
+                      enum nl80211_channel_type channel_type)
+{
+       struct mwifiex_chan_freq_power cfp;
+       int ret = 0;
+       int status = 0;
+       struct mwifiex_ds_band_cfg band_cfg;
+       int mode;
+       u8 wait_option = MWIFIEX_IOCTL_WAIT;
+       u32 config_bands = 0;
+       struct wiphy *wiphy = priv->wdev->wiphy;
+
+       mode = mwifiex_drv_get_mode(priv, wait_option);
+
+       if (chan) {
+               memset(&band_cfg, 0, sizeof(band_cfg));
+               /* Set appropriate bands */
+               if (chan->band == IEEE80211_BAND_2GHZ)
+                       config_bands = BAND_B | BAND_G | BAND_GN;
+               else
+                       config_bands = BAND_AN | BAND_A;
+               if (mode == MWIFIEX_BSS_MODE_INFRA
+                   || mode == MWIFIEX_BSS_MODE_AUTO) {
+                       band_cfg.config_bands = config_bands;
+               } else if (mode == MWIFIEX_BSS_MODE_IBSS) {
+                       band_cfg.config_bands = config_bands;
+                       band_cfg.adhoc_start_band = config_bands;
+               }
+               /* Set channel offset */
+               band_cfg.sec_chan_offset =
+                       mwifiex_cfg80211_channel_type_to_mwifiex_channels
+                       (channel_type);
+               status = mwifiex_radio_ioctl_band_cfg(priv, HostCmd_ACT_GEN_SET,
+                                                     &band_cfg);
+
+               if (status)
+                       return -EFAULT;
+               mwifiex_send_domain_info_cmd_fw(wiphy);
+       }
+
+       wiphy_dbg(wiphy, "info: setting band %d, channel offset %d and "
+               "mode %d\n", config_bands, band_cfg.sec_chan_offset, mode);
+       if (!chan)
+               return ret;
+
+       memset(&cfp, 0, sizeof(cfp));
+       cfp.freq = chan->center_freq;
+       /* Convert frequency to channel */
+       cfp.channel = ieee80211_frequency_to_channel(chan->center_freq);
+
+       status = mwifiex_bss_ioctl_channel(priv, HostCmd_ACT_GEN_SET, &cfp);
+       if (status)
+               return -EFAULT;
+
+       ret = mwifiex_drv_change_adhoc_chan(priv, cfp.channel);
+
+       return ret;
+}
+
+/*
+ * CFG802.11 operation handler to set channel.
+ *
+ * This function can only be used when station is not connected.
+ */
+static int
+mwifiex_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev,
+                            struct ieee80211_channel *chan,
+                            enum nl80211_channel_type channel_type)
+{
+       struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
+
+       if (priv->media_connected) {
+               wiphy_err(wiphy, "This setting is valid only when station "
+                               "is not connected\n");
+               return -EINVAL;
+       }
+
+       return mwifiex_set_rf_channel(priv, chan, channel_type);
+}
+
+/*
+ * This function sets the fragmentation threshold.
+ *
+ * This function creates an IOCTL request, populates it accordingly
+ * and issues an IOCTL.
+ *
+ * The fragmentation threshold value must lies between MWIFIEX_FRAG_MIN_VALUE
+ * and MWIFIEX_FRAG_MAX_VALUE.
+ */
+static int
+mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr)
+{
+       int ret = 0;
+       int status = 0;
+       struct mwifiex_wait_queue *wait = NULL;
+       u8 wait_option = MWIFIEX_IOCTL_WAIT;
+
+       if (frag_thr < MWIFIEX_FRAG_MIN_VALUE
+           || frag_thr > MWIFIEX_FRAG_MAX_VALUE)
+               return -EINVAL;
+
+       wait = mwifiex_alloc_fill_wait_queue(priv, wait_option);
+       if (!wait)
+               return -ENOMEM;
+
+       status = mwifiex_snmp_mib_ioctl(priv, wait, FRAG_THRESH_I,
+                                       HostCmd_ACT_GEN_SET, &frag_thr);
+
+       if (mwifiex_request_ioctl(priv, wait, status, wait_option))
+               ret = -EFAULT;
+
+       kfree(wait);
+       return ret;
+}
+
+/*
+ * This function sets the RTS threshold.
+ *
+ * This function creates an IOCTL request, populates it accordingly
+ * and issues an IOCTL.
+ */
+static int
+mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr)
+{
+       int ret = 0;
+       struct mwifiex_wait_queue *wait = NULL;
+       int status = 0;
+       u8 wait_option = MWIFIEX_IOCTL_WAIT;
+
+       if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE)
+               rts_thr = MWIFIEX_RTS_MAX_VALUE;
+
+       wait = mwifiex_alloc_fill_wait_queue(priv, wait_option);
+       if (!wait)
+               return -ENOMEM;
+
+       status = mwifiex_snmp_mib_ioctl(priv, wait, RTS_THRESH_I,
+                                       HostCmd_ACT_GEN_SET, &rts_thr);
+
+       if (mwifiex_request_ioctl(priv, wait, status, wait_option))
+               ret = -EFAULT;
+
+       kfree(wait);
+       return ret;
+}
+
+/*
+ * 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
+mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+       struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
+
+       int ret = 0;
+
+       if (changed & WIPHY_PARAM_RTS_THRESHOLD)
+               ret = mwifiex_set_rts(priv, wiphy->rts_threshold);
+
+       if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
+               ret = mwifiex_set_frag(priv, wiphy->frag_threshold);
+
+       return ret;
+}
+
+/*
+ * CFG802.11 operation handler to change interface type.
+ *
+ * This function creates an IOCTL request, populates it accordingly
+ * and issues an IOCTL.
+ *
+ * The function also maps the CFG802.11 mode type into driver mode type.
+ *      NL80211_IFTYPE_ADHOC        -> MWIFIEX_BSS_MODE_IBSS
+ *      NL80211_IFTYPE_STATION      -> MWIFIEX_BSS_MODE_INFRA
+ *      NL80211_IFTYPE_UNSPECIFIED  -> MWIFIEX_BSS_MODE_AUTO
+ */
+static int
+mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy,
+                                    struct net_device *dev,
+                                    enum nl80211_iftype type, u32 *flags,
+                                    struct vif_params *params)
+{
+       int ret = 0;
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       int mode = -1;
+       struct mwifiex_wait_queue *wait = NULL;
+       int status = 0;
+
+       wait = mwifiex_alloc_fill_wait_queue(priv, MWIFIEX_IOCTL_WAIT);
+       if (!wait)
+               return -ENOMEM;
+
+       switch (type) {
+       case NL80211_IFTYPE_ADHOC:
+               mode = MWIFIEX_BSS_MODE_IBSS;
+               dev->ieee80211_ptr->iftype = NL80211_IFTYPE_ADHOC;
+               wiphy_dbg(wiphy, "info: setting interface type to adhoc\n");
+               break;
+       case NL80211_IFTYPE_STATION:
+               mode = MWIFIEX_BSS_MODE_INFRA;
+               dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION;
+               wiphy_dbg(wiphy, "info: Setting interface type to managed\n");
+               break;
+       case NL80211_IFTYPE_UNSPECIFIED:
+               mode = MWIFIEX_BSS_MODE_AUTO;
+               dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION;
+               wiphy_dbg(wiphy, "info: setting interface type to auto\n");
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       if (ret)
+               goto done;
+       status = mwifiex_bss_ioctl_mode(priv, wait, HostCmd_ACT_GEN_SET, &mode);
+
+       if (mwifiex_request_ioctl(priv, wait, status, MWIFIEX_IOCTL_WAIT))
+               ret = -EFAULT;
+
+done:
+       kfree(wait);
+       return ret;
+}
+
+/*
+ * 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
+mwifiex_dump_station_info(struct mwifiex_private *priv,
+                         struct station_info *sinfo)
+{
+       struct mwifiex_ds_get_signal signal;
+       struct mwifiex_rate_cfg rate;
+       int ret = 0;
+
+       sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES |
+               STATION_INFO_RX_PACKETS |
+               STATION_INFO_TX_PACKETS
+               | STATION_INFO_SIGNAL | STATION_INFO_TX_BITRATE;
+
+       /* Get signal information from the firmware */
+       memset(&signal, 0, sizeof(struct mwifiex_ds_get_signal));
+       if (mwifiex_get_signal_info(priv, MWIFIEX_IOCTL_WAIT, &signal)) {
+               dev_err(priv->adapter->dev, "getting signal information\n");
+               ret = -EFAULT;
+       }
+
+       if (mwifiex_drv_get_data_rate(priv, &rate)) {
+               dev_err(priv->adapter->dev, "getting data rate\n");
+               ret = -EFAULT;
+       }
+
+       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->w_stats.qual.level;
+       sinfo->txrate.legacy = rate.rate;
+
+       return ret;
+}
+
+/*
+ * 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
+mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
+                            u8 *mac, struct station_info *sinfo)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       int ret = 0;
+
+       mwifiex_dump_station_info(priv, sinfo);
+
+       if (!priv->media_connected)
+               return -ENOENT;
+       if (memcmp(mac, priv->cfg_bssid, ETH_ALEN))
+               return -ENOENT;
+
+
+       ret = mwifiex_dump_station_info(priv, sinfo);
+
+       return ret;
+}
+
+/* Supported rates to be advertised to the cfg80211 */
+
+static struct ieee80211_rate mwifiex_rates[] = {
+       {.bitrate = 10, .hw_value = 2, },
+       {.bitrate = 20, .hw_value = 4, },
+       {.bitrate = 55, .hw_value = 11, },
+       {.bitrate = 110, .hw_value = 22, },
+       {.bitrate = 220, .hw_value = 44, },
+       {.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, },
+       {.bitrate = 720, .hw_value = 144, },
+};
+
+/* Channel definitions to be advertised to cfg80211 */
+
+static struct ieee80211_channel mwifiex_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 mwifiex_band_2ghz = {
+       .channels = mwifiex_channels_2ghz,
+       .n_channels = ARRAY_SIZE(mwifiex_channels_2ghz),
+       .bitrates = mwifiex_rates,
+       .n_bitrates = 14,
+};
+
+static struct ieee80211_channel mwifiex_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 mwifiex_band_5ghz = {
+       .channels = mwifiex_channels_5ghz,
+       .n_channels = ARRAY_SIZE(mwifiex_channels_5ghz),
+       .bitrates = mwifiex_rates - 4,
+       .n_bitrates = ARRAY_SIZE(mwifiex_rates) + 4,
+};
+
+
+/* Supported crypto cipher suits to be advertised to cfg80211 */
+
+static const u32 mwifiex_cipher_suites[] = {
+       WLAN_CIPHER_SUITE_WEP40,
+       WLAN_CIPHER_SUITE_WEP104,
+       WLAN_CIPHER_SUITE_TKIP,
+       WLAN_CIPHER_SUITE_CCMP,
+};
+
+/*
+ * CFG802.11 operation handler for disconnection request.
+ *
+ * This function does not work when there is already a disconnection
+ * procedure going on.
+ */
+static int
+mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
+                           u16 reason_code)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+       if (priv->disconnect)
+               return -EBUSY;
+
+       priv->disconnect = 1;
+       if (mwifiex_disconnect(priv, MWIFIEX_IOCTL_WAIT, NULL))
+               return -EFAULT;
+
+       wiphy_dbg(wiphy, "info: successfully disconnected from %pM:"
+               " reason code %d\n", priv->cfg_bssid, reason_code);
+
+       queue_work(priv->workqueue, &priv->cfg_workqueue);
+
+       return 0;
+}
+
+/*
+ * This function informs the CFG802.11 subsystem of a new IBSS.
+ *
+ * The following information are sent to the CFG802.11 subsystem
+ * to register the new IBSS. If we do not register the new IBSS,
+ * a kernel panic will result.
+ *      - SSID
+ *      - SSID length
+ *      - BSSID
+ *      - Channel
+ */
+static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv)
+{
+       int ret = 0;
+       struct ieee80211_channel *chan;
+       struct mwifiex_bss_info bss_info;
+       int ie_len = 0;
+       u8 ie_buf[IEEE80211_MAX_SSID_LEN + sizeof(struct ieee_types_header)];
+
+       ret = mwifiex_get_bss_info(priv, &bss_info);
+       if (ret)
+               return ret;
+
+       ie_buf[0] = WLAN_EID_SSID;
+       ie_buf[1] = bss_info.ssid.ssid_len;
+
+       memcpy(&ie_buf[sizeof(struct ieee_types_header)],
+                       &bss_info.ssid.ssid,
+                       bss_info.ssid.ssid_len);
+       ie_len = ie_buf[1] + sizeof(struct ieee_types_header);
+
+       chan = __ieee80211_get_channel(priv->wdev->wiphy,
+                       ieee80211_channel_to_frequency(bss_info.bss_chan,
+                                               priv->curr_bss_params.band));
+
+       cfg80211_inform_bss(priv->wdev->wiphy, chan,
+               bss_info.bssid, 0, WLAN_CAPABILITY_IBSS,
+               0, ie_buf, ie_len, 0, GFP_KERNEL);
+       memcpy(priv->cfg_bssid, bss_info.bssid, ETH_ALEN);
+
+       return ret;
+}
+
+/*
+ * This function informs the CFG802.11 subsystem of a new BSS connection.
+ *
+ * The following information are sent to the CFG802.11 subsystem
+ * to register the new BSS connection. If we do not register the new BSS,
+ * a kernel panic will result.
+ *      - MAC address
+ *      - Capabilities
+ *      - Beacon period
+ *      - RSSI value
+ *      - Channel
+ *      - Supported rates IE
+ *      - Extended capabilities IE
+ *      - DS parameter set IE
+ *      - HT Capability IE
+ *      - Vendor Specific IE (221)
+ *      - WPA IE
+ *      - RSN IE
+ */
+static int mwifiex_inform_bss_from_scan_result(struct mwifiex_private *priv,
+                                              struct mwifiex_802_11_ssid *ssid)
+{
+       struct mwifiex_scan_resp scan_resp;
+       struct mwifiex_bssdescriptor *scan_table;
+       int i, j;
+       struct ieee80211_channel *chan;
+       u8 *ie, *tmp, *ie_buf;
+       u32 ie_len;
+       u64 ts = 0;
+       u8 *beacon;
+       int beacon_size;
+       u8 element_id, element_len;
+
+       memset(&scan_resp, 0, sizeof(scan_resp));
+       if (mwifiex_get_scan_table(priv, MWIFIEX_IOCTL_WAIT, &scan_resp))
+               return -EFAULT;
+
+#define MAX_IE_BUF     2048
+       ie_buf = kzalloc(MAX_IE_BUF, GFP_KERNEL);
+       if (!ie_buf) {
+               dev_err(priv->adapter->dev, "%s: failed to alloc ie_buf\n",
+                                               __func__);
+               return -ENOMEM;
+       }
+
+       scan_table = (struct mwifiex_bssdescriptor *) scan_resp.scan_table;
+       for (i = 0; i < scan_resp.num_in_scan_table; i++) {
+               if (ssid) {
+                       /* Inform specific BSS only */
+                       if (memcmp(ssid->ssid, scan_table[i].ssid.ssid,
+                                          ssid->ssid_len))
+                               continue;
+               }
+               memset(ie_buf, 0, MAX_IE_BUF);
+               ie_buf[0] = WLAN_EID_SSID;
+               ie_buf[1] = scan_table[i].ssid.ssid_len;
+               memcpy(&ie_buf[sizeof(struct ieee_types_header)],
+                      scan_table[i].ssid.ssid, ie_buf[1]);
+
+               ie = ie_buf + ie_buf[1] + sizeof(struct ieee_types_header);
+               ie_len = ie_buf[1] + sizeof(struct ieee_types_header);
+
+               ie[0] = WLAN_EID_SUPP_RATES;
+
+               for (j = 0; j < sizeof(scan_table[i].supported_rates); j++) {
+                       if (!scan_table[i].supported_rates[j])
+                               break;
+                       else
+                               ie[j + sizeof(struct ieee_types_header)] =
+                                       scan_table[i].supported_rates[j];
+               }
+
+               ie[1] = j;
+               ie_len += ie[1] + sizeof(struct ieee_types_header);
+
+               beacon = scan_table[i].beacon_buf;
+               beacon_size = scan_table[i].beacon_buf_size;
+
+               /* Skip time stamp, beacon interval and capability */
+
+               if (beacon) {
+                       beacon += sizeof(scan_table[i].beacon_period)
+                               + sizeof(scan_table[i].time_stamp) +
+                               +sizeof(scan_table[i].cap_info_bitmap);
+
+                       beacon_size -= sizeof(scan_table[i].beacon_period)
+                               + sizeof(scan_table[i].time_stamp)
+                               + sizeof(scan_table[i].cap_info_bitmap);
+               }
+
+               while (beacon_size >= sizeof(struct ieee_types_header)) {
+                       ie = ie_buf + ie_len;
+                       element_id = *beacon;
+                       element_len = *(beacon + 1);
+                       if (beacon_size < (int) element_len +
+                           sizeof(struct ieee_types_header)) {
+                               dev_err(priv->adapter->dev, "%s: in processing"
+                                       " IE, bytes left < IE length\n",
+                                       __func__);
+                               break;
+                       }
+                       switch (element_id) {
+                       case WLAN_EID_EXT_CAPABILITY:
+                       case WLAN_EID_DS_PARAMS:
+                       case WLAN_EID_HT_CAPABILITY:
+                       case WLAN_EID_VENDOR_SPECIFIC:
+                       case WLAN_EID_RSN:
+                       case WLAN_EID_BSS_AC_ACCESS_DELAY:
+                               ie[0] = element_id;
+                               ie[1] = element_len;
+                               tmp = (u8 *) beacon;
+                               memcpy(&ie[sizeof(struct ieee_types_header)],
+                                      tmp + sizeof(struct ieee_types_header),
+                                      element_len);
+                               ie_len += ie[1] +
+                                       sizeof(struct ieee_types_header);
+                               break;
+                       default:
+                               break;
+                       }
+                       beacon += element_len +
+                                       sizeof(struct ieee_types_header);
+                       beacon_size -= element_len +
+                                       sizeof(struct ieee_types_header);
+               }
+               chan = ieee80211_get_channel(priv->wdev->wiphy,
+                                               scan_table[i].freq);
+               cfg80211_inform_bss(priv->wdev->wiphy, chan,
+                                       scan_table[i].mac_address,
+                                       ts, scan_table[i].cap_info_bitmap,
+                                       scan_table[i].beacon_period,
+                                       ie_buf, ie_len,
+                                       scan_table[i].rssi, GFP_KERNEL);
+       }
+
+       kfree(ie_buf);
+       return 0;
+}
+
+/*
+ * This function connects with a BSS.
+ *
+ * This function handles both Infra and Ad-Hoc modes. It also performs
+ * validity checking on the provided parameters, disconnects from the
+ * current BSS (if any), sets up the association/scan parameters,
+ * including security settings, and performs specific SSID scan before
+ * trying to connect.
+ *
+ * For Infra mode, the function returns failure if the specified SSID
+ * is not found in scan table. However, for Ad-Hoc mode, it can create
+ * the IBSS if it does not exist. On successful completion in either case,
+ * the function notifies the CFG802.11 subsystem of the new BSS connection,
+ * otherwise the kernel will panic.
+ */
+static int
+mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid,
+                      u8 *bssid, int mode, struct ieee80211_channel *channel,
+                      struct cfg80211_connect_params *sme, bool privacy)
+{
+       struct mwifiex_802_11_ssid req_ssid;
+       struct mwifiex_ssid_bssid ssid_bssid;
+       int ret = 0;
+       int auth_type = 0, pairwise_encrypt_mode = 0, wpa_enabled = 0;
+       int group_encrypt_mode = 0;
+       int alg_is_wep = 0;
+
+       memset(&req_ssid, 0, sizeof(struct mwifiex_802_11_ssid));
+       memset(&ssid_bssid, 0, sizeof(struct mwifiex_ssid_bssid));
+
+       req_ssid.ssid_len = ssid_len;
+       if (ssid_len > IEEE80211_MAX_SSID_LEN) {
+               dev_err(priv->adapter->dev, "invalid SSID - aborting\n");
+               return -EINVAL;
+       }
+
+       memcpy(req_ssid.ssid, ssid, ssid_len);
+       if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) {
+               dev_err(priv->adapter->dev, "invalid SSID - aborting\n");
+               return -EINVAL;
+       }
+
+       /* disconnect before try to associate */
+       mwifiex_disconnect(priv, MWIFIEX_IOCTL_WAIT, NULL);
+
+       if (channel)
+               ret = mwifiex_set_rf_channel(priv, channel,
+                               mwifiex_channels_to_cfg80211_channel_type
+                               (priv->adapter->chan_offset));
+
+       ret = mwifiex_set_encode(priv, NULL, 0, 0, 1);  /* Disable keys */
+
+       if (mode == MWIFIEX_BSS_MODE_IBSS) {
+               /* "privacy" is set only for ad-hoc mode */
+               if (privacy) {
+                       /*
+                        * Keep MWIFIEX_ENCRYPTION_MODE_WEP104 for now so that
+                        * the firmware can find a matching network from the
+                        * scan. The cfg80211 does not give us the encryption
+                        * mode at this stage so just setting it to WEP here.
+                        */
+                       wpa_enabled = 0;
+                       auth_type = MWIFIEX_AUTH_MODE_OPEN;
+                       ret = mwifiex_set_auth(priv,
+                                               MWIFIEX_ENCRYPTION_MODE_WEP104,
+                                               auth_type, wpa_enabled);
+               }
+
+               goto done;
+       }
+
+       /* Now handle infra mode. "sme" is valid for infra mode only */
+       if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC
+                       || sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM)
+               auth_type = MWIFIEX_AUTH_MODE_OPEN;
+       else if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY)
+               auth_type = MWIFIEX_AUTH_MODE_SHARED;
+
+       if (sme->crypto.n_ciphers_pairwise) {
+               pairwise_encrypt_mode = mwifiex_get_mwifiex_cipher(sme->crypto.
+                                       ciphers_pairwise[0], &wpa_enabled);
+               ret = mwifiex_set_auth(priv, pairwise_encrypt_mode, auth_type,
+                                                               wpa_enabled);
+       }
+
+       if (sme->crypto.cipher_group) {
+               group_encrypt_mode = mwifiex_get_mwifiex_cipher(sme->crypto.
+                                                  cipher_group, &wpa_enabled);
+               ret = mwifiex_set_auth(priv, group_encrypt_mode, auth_type,
+                                                               wpa_enabled);
+       }
+       if (sme->ie)
+               ret = mwifiex_set_gen_ie(priv, sme->ie, sme->ie_len);
+
+       if (sme->key) {
+               alg_is_wep = mwifiex_is_alg_wep(pairwise_encrypt_mode)
+                       | mwifiex_is_alg_wep(group_encrypt_mode);
+               if (alg_is_wep) {
+                       dev_dbg(priv->adapter->dev,
+                               "info: setting wep encryption"
+                               " with key len %d\n", sme->key_len);
+                       ret = mwifiex_set_encode(priv, sme->key, sme->key_len,
+                                                       sme->key_idx, 0);
+               }
+       }
+done:
+       /* Do specific SSID scanning */
+       if (mwifiex_request_scan(priv, MWIFIEX_IOCTL_WAIT, &req_ssid)) {
+               dev_err(priv->adapter->dev, "scan error\n");
+               return -EFAULT;
+       }
+
+
+       memcpy(&ssid_bssid.ssid, &req_ssid, sizeof(struct mwifiex_802_11_ssid));
+
+       if (mode != MWIFIEX_BSS_MODE_IBSS) {
+               if (mwifiex_find_best_bss(priv, MWIFIEX_IOCTL_WAIT,
+                                         &ssid_bssid))
+                       return -EFAULT;
+               /* Inform the BSS information to kernel, otherwise
+                * kernel will give a panic after successful assoc */
+               if (mwifiex_inform_bss_from_scan_result(priv, &req_ssid))
+                       return -EFAULT;
+       }
+
+       dev_dbg(priv->adapter->dev, "info: trying to associate to %s and bssid %pM\n",
+              (char *) req_ssid.ssid, ssid_bssid.bssid);
+
+       memcpy(&priv->cfg_bssid, ssid_bssid.bssid, 6);
+
+       /* Connect to BSS by ESSID */
+       memset(&ssid_bssid.bssid, 0, ETH_ALEN);
+
+       if (mwifiex_bss_start(priv, MWIFIEX_IOCTL_WAIT, &ssid_bssid))
+               return -EFAULT;
+
+       if (mode == MWIFIEX_BSS_MODE_IBSS) {
+               /* Inform the BSS information to kernel, otherwise
+                * kernel will give a panic after successful assoc */
+               if (mwifiex_cfg80211_inform_ibss_bss(priv))
+                       return -EFAULT;
+       }
+
+       return ret;
+}
+
+/*
+ * CFG802.11 operation handler for association request.
+ *
+ * This function does not work when the current mode is set to Ad-Hoc, or
+ * when there is already an association procedure going on. The given BSS
+ * information is used to associate.
+ */
+static int
+mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+                        struct cfg80211_connect_params *sme)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       int ret = 0;
+       int mode = 0;
+
+       if (priv->assoc_request)
+               return -EBUSY;
+
+       mode = mwifiex_drv_get_mode(priv, MWIFIEX_IOCTL_WAIT);
+
+       if (mode == MWIFIEX_BSS_MODE_IBSS) {
+               wiphy_err(wiphy, "received infra assoc request "
+                               "when station is in ibss mode\n");
+               goto done;
+       }
+
+       priv->assoc_request = 1;
+
+       wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n",
+              (char *) sme->ssid, sme->bssid);
+
+       ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid,
+                                    mode, sme->channel, sme, 0);
+
+done:
+       priv->assoc_result = ret;
+       queue_work(priv->workqueue, &priv->cfg_workqueue);
+       return ret;
+}
+
+/*
+ * CFG802.11 operation handler to join an IBSS.
+ *
+ * This function does not work in any mode other than Ad-Hoc, or if
+ * a join operation is already in progress.
+ */
+static int
+mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
+                          struct cfg80211_ibss_params *params)
+{
+       struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
+       int ret = 0;
+       int mode = 0;
+
+       if (priv->ibss_join_request)
+               return -EBUSY;
+
+       mode = mwifiex_drv_get_mode(priv, MWIFIEX_IOCTL_WAIT);
+       if (mode != MWIFIEX_BSS_MODE_IBSS) {
+               wiphy_err(wiphy, "request to join ibss received "
+                               "when station is not in ibss mode\n");
+               goto done;
+       }
+
+       priv->ibss_join_request = 1;
+
+       wiphy_dbg(wiphy, "info: trying to join to %s and bssid %pM\n",
+              (char *) params->ssid, params->bssid);
+
+       ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid,
+                                    params->bssid, mode, params->channel, NULL,
+                                    params->privacy);
+done:
+       priv->ibss_join_result = ret;
+       queue_work(priv->workqueue, &priv->cfg_workqueue);
+       return ret;
+}
+
+/*
+ * CFG802.11 operation handler to leave an IBSS.
+ *
+ * This function does not work if a leave operation is
+ * already in progress.
+ */
+static int
+mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
+{
+       struct mwifiex_private *priv = mwifiex_cfg80211_get_priv(wiphy);
+
+       if (priv->disconnect)
+               return -EBUSY;
+
+       priv->disconnect = 1;
+
+       wiphy_dbg(wiphy, "info: disconnecting from essid %pM\n",
+                       priv->cfg_bssid);
+       if (mwifiex_disconnect(priv, MWIFIEX_IOCTL_WAIT, NULL))
+               return -EFAULT;
+
+       queue_work(priv->workqueue, &priv->cfg_workqueue);
+
+       return 0;
+}
+
+/*
+ * CFG802.11 operation handler for scan request.
+ *
+ * This function issues a scan request to the firmware based upon
+ * the user specified scan configuration. On successfull completion,
+ * it also informs the results.
+ */
+static int
+mwifiex_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev,
+                     struct cfg80211_scan_request *request)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+       wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name);
+
+       if (priv->scan_request && priv->scan_request != request)
+               return -EBUSY;
+
+       priv->scan_request = request;
+
+       queue_work(priv->workqueue, &priv->cfg_workqueue);
+       return 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 = 0x3
+ *      - Minimum AMPDU spacing = 0x6
+ *      - HT Capabilities map = IEEE80211_HT_CAP_SUP_WIDTH_20_40 (0x0002)
+ *      - MCS information, Rx mask = 0xff
+ *      - MCD information, Tx parameters = IEEE80211_HT_MCS_TX_DEFINED (0x01)
+ */
+static void
+mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info,
+                     struct mwifiex_private *priv)
+{
+       int rx_mcs_supp;
+       struct ieee80211_mcs_info mcs_set;
+       u8 *mcs = (u8 *)&mcs_set;
+       struct mwifiex_adapter *adapter = priv->adapter;
+
+       ht_info->ht_supported = true;
+       ht_info->ampdu_factor = 0x3;
+       ht_info->ampdu_density = 0x6;
+
+       memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
+       ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+       rx_mcs_supp = GET_RXMCSSUPP(priv->adapter->hw_dev_mcs_support);
+       /* Set MCS for 1x1 */
+       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 == MWIFIEX_BSS_MODE_INFRA ||
+                       (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap) &&
+                        ISSUPP_CHANWIDTH40(adapter->usr_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;
+}
+
+/* station cfg80211 operations */
+static struct cfg80211_ops mwifiex_cfg80211_ops = {
+       .change_virtual_intf = mwifiex_cfg80211_change_virtual_intf,
+       .scan = mwifiex_cfg80211_scan,
+       .connect = mwifiex_cfg80211_connect,
+       .disconnect = mwifiex_cfg80211_disconnect,
+       .get_station = mwifiex_cfg80211_get_station,
+       .set_wiphy_params = mwifiex_cfg80211_set_wiphy_params,
+       .set_channel = mwifiex_cfg80211_set_channel,
+       .join_ibss = mwifiex_cfg80211_join_ibss,
+       .leave_ibss = mwifiex_cfg80211_leave_ibss,
+       .add_key = mwifiex_cfg80211_add_key,
+       .del_key = mwifiex_cfg80211_del_key,
+       .set_default_key = mwifiex_cfg80211_set_default_key,
+       .set_power_mgmt = mwifiex_cfg80211_set_power_mgmt,
+       .set_tx_power = mwifiex_cfg80211_set_tx_power,
+};
+
+/*
+ * 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 mwifiex_register_cfg80211(struct net_device *dev, u8 *mac,
+                             struct mwifiex_private *priv)
+{
+       int ret = 0;
+       void *wdev_priv = NULL;
+       struct wireless_dev *wdev;
+
+       wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+       if (!wdev) {
+               dev_err(priv->adapter->dev, "%s: allocating wireless device\n",
+                                               __func__);
+               return -ENOMEM;
+       }
+       wdev->wiphy =
+               wiphy_new(&mwifiex_cfg80211_ops,
+                         sizeof(struct mwifiex_private *));
+       if (!wdev->wiphy)
+               return -ENOMEM;
+       wdev->iftype = NL80211_IFTYPE_STATION;
+       wdev->wiphy->max_scan_ssids = 10;
+       wdev->wiphy->interface_modes =
+               BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC);
+       wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz;
+       wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz;
+
+       /* Initialize cipher suits */
+       wdev->wiphy->cipher_suites = mwifiex_cipher_suites;
+       wdev->wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites);
+
+       /* Initialize parameters for 2GHz band */
+
+       mwifiex_setup_ht_caps(&wdev->wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap,
+                                                                       priv);
+       mwifiex_setup_ht_caps(&wdev->wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap,
+                                                                       priv);
+
+       memcpy(wdev->wiphy->perm_addr, mac, 6);
+       wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+       /* We are using custom domains */
+       wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
+
+       wdev->wiphy->reg_notifier = mwifiex_reg_notifier;
+
+       /* Set struct mwifiex_private pointer in wiphy_priv */
+       wdev_priv = wiphy_priv(wdev->wiphy);
+
+       *(unsigned long *) wdev_priv = (unsigned long) priv;
+
+       ret = wiphy_register(wdev->wiphy);
+       if (ret < 0) {
+               dev_err(priv->adapter->dev, "%s: registering cfg80211 device\n",
+                                               __func__);
+               wiphy_free(wdev->wiphy);
+               return ret;
+       } else {
+               dev_dbg(priv->adapter->dev,
+                               "info: successfully registered wiphy device\n");
+       }
+
+       dev_net_set(dev, wiphy_net(wdev->wiphy));
+       dev->ieee80211_ptr = wdev;
+       memcpy(dev->dev_addr, wdev->wiphy->perm_addr, 6);
+       memcpy(dev->perm_addr, wdev->wiphy->perm_addr, 6);
+       SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy));
+       priv->wdev = wdev;
+
+       dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
+       dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT;
+       dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN;
+
+       return ret;
+}
+
+/*
+ * This function handles the result of different pending network operations.
+ *
+ * The following operations are handled and CFG802.11 subsystem is
+ * notified accordingly -
+ *      - Scan request completion
+ *      - Association request completion
+ *      - IBSS join request completion
+ *      - Disconnect request completion
+ */
+void
+mwifiex_cfg80211_results(struct work_struct *work)
+{
+       struct mwifiex_private *priv =
+               container_of(work, struct mwifiex_private, cfg_workqueue);
+       struct mwifiex_user_scan_cfg *scan_req;
+       int ret = 0, i;
+       struct ieee80211_channel *chan;
+
+       if (priv->scan_request) {
+               scan_req = kzalloc(sizeof(struct mwifiex_user_scan_cfg),
+                                  GFP_KERNEL);
+               if (!scan_req) {
+                       dev_err(priv->adapter->dev, "failed to alloc "
+                                                   "scan_req\n");
+                       return;
+               }
+               for (i = 0; i < priv->scan_request->n_ssids; i++) {
+                       memcpy(scan_req->ssid_list[i].ssid,
+                                       priv->scan_request->ssids[i].ssid,
+                                       priv->scan_request->ssids[i].ssid_len);
+                       scan_req->ssid_list[i].max_len =
+                                       priv->scan_request->ssids[i].ssid_len;
+               }
+               for (i = 0; i < priv->scan_request->n_channels; i++) {
+                       chan = priv->scan_request->channels[i];
+                       scan_req->chan_list[i].chan_number = chan->hw_value;
+                       scan_req->chan_list[i].radio_type = chan->band;
+                       if (chan->flags & IEEE80211_CHAN_DISABLED)
+                               scan_req->chan_list[i].scan_type =
+                                       MWIFIEX_SCAN_TYPE_PASSIVE;
+                       else
+                               scan_req->chan_list[i].scan_type =
+                                       MWIFIEX_SCAN_TYPE_ACTIVE;
+                       scan_req->chan_list[i].scan_time = 0;
+               }
+               if (mwifiex_set_user_scan_ioctl(priv, scan_req)) {
+                       ret = -EFAULT;
+                       goto done;
+               }
+               if (mwifiex_inform_bss_from_scan_result(priv, NULL))
+                       ret = -EFAULT;
+done:
+               priv->scan_result_status = ret;
+               dev_dbg(priv->adapter->dev, "info: %s: sending scan results\n",
+                                                       __func__);
+               cfg80211_scan_done(priv->scan_request,
+                               (priv->scan_result_status < 0));
+               priv->scan_request = NULL;
+               kfree(scan_req);
+       }
+
+       if (priv->assoc_request) {
+               if (!priv->assoc_result) {
+                       cfg80211_connect_result(priv->netdev, priv->cfg_bssid,
+                                               NULL, 0, NULL, 0,
+                                               WLAN_STATUS_SUCCESS,
+                                               GFP_KERNEL);
+                       dev_dbg(priv->adapter->dev,
+                               "info: associated to bssid %pM successfully\n",
+                              priv->cfg_bssid);
+               } else {
+                       dev_dbg(priv->adapter->dev,
+                               "info: association to bssid %pM failed\n",
+                              priv->cfg_bssid);
+                       memset(priv->cfg_bssid, 0, ETH_ALEN);
+               }
+               priv->assoc_request = 0;
+               priv->assoc_result = 0;
+       }
+
+       if (priv->ibss_join_request) {
+               if (!priv->ibss_join_result) {
+                       cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid,
+                                            GFP_KERNEL);
+                       dev_dbg(priv->adapter->dev,
+                               "info: joined/created adhoc network with bssid"
+                                       " %pM successfully\n", priv->cfg_bssid);
+               } else {
+                       dev_dbg(priv->adapter->dev,
+                               "info: failed creating/joining adhoc network\n");
+               }
+               priv->ibss_join_request = 0;
+               priv->ibss_join_result = 0;
+       }
+
+       if (priv->disconnect) {
+               memset(priv->cfg_bssid, 0, ETH_ALEN);
+               priv->disconnect = 0;
+       }
+
+       return;
+}
diff --git a/drivers/net/wireless/mwifiex/cfg80211.h b/drivers/net/wireless/mwifiex/cfg80211.h
new file mode 100644 (file)
index 0000000..c4db8f3
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Marvell Wireless LAN device driver: CFG80211
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#ifndef __MWIFIEX_CFG80211__
+#define __MWIFIEX_CFG80211__
+
+#include <net/cfg80211.h>
+
+#include "main.h"
+
+int mwifiex_register_cfg80211(struct net_device *, u8 *,
+                               struct mwifiex_private *);
+
+void mwifiex_cfg80211_results(struct work_struct *work);
+#endif
diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c
new file mode 100644 (file)
index 0000000..999ed81
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * Marvell Wireless LAN device driver: Channel, Frequence and Power
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "cfg80211.h"
+
+/* 100mW */
+#define MWIFIEX_TX_PWR_DEFAULT     20
+/* 100mW */
+#define MWIFIEX_TX_PWR_US_DEFAULT      20
+/* 50mW */
+#define MWIFIEX_TX_PWR_JP_DEFAULT      16
+/* 100mW */
+#define MWIFIEX_TX_PWR_FR_100MW        20
+/* 10mW */
+#define MWIFIEX_TX_PWR_FR_10MW         10
+/* 100mW */
+#define MWIFIEX_TX_PWR_EMEA_DEFAULT    20
+
+static u8 adhoc_rates_b[B_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, 0 };
+
+static u8 adhoc_rates_g[G_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24,
+                                              0xb0, 0x48, 0x60, 0x6c, 0 };
+
+static u8 adhoc_rates_bg[BG_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96,
+                                                0x0c, 0x12, 0x18, 0x24,
+                                                0x30, 0x48, 0x60, 0x6c, 0 };
+
+static u8 adhoc_rates_a[A_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24,
+                                              0xb0, 0x48, 0x60, 0x6c, 0 };
+u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
+                                       0xb0, 0x48, 0x60, 0x6c, 0 };
+static u16 mwifiex_data_rates[MWIFIEX_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 };
+
+u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 };
+
+u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
+                                       0x30, 0x48, 0x60, 0x6c, 0 };
+
+u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c,
+                                       0x12, 0x16, 0x18, 0x24, 0x30, 0x48,
+                                       0x60, 0x6c, 0 };
+
+u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30,
+                                               0x32, 0x40, 0x41, 0xff };
+
+u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 };
+
+/*
+ * This function maps an index in supported rates table into
+ * the corresponding data rate.
+ */
+u32 mwifiex_index_to_data_rate(struct mwifiex_adapter *adapter, u8 index,
+                              u8 ht_info)
+{
+       u16 mcs_rate[4][8] = {
+               {0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e}
+       ,                       /* LG 40M */
+       {0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c}
+       ,                       /* SG 40M */
+       {0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82}
+       ,                       /* LG 20M */
+       {0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90}
+       };                      /* SG 20M */
+
+       u32 rate;
+
+       if (ht_info & BIT(0)) {
+               if (index == MWIFIEX_RATE_BITMAP_MCS0) {
+                       if (ht_info & BIT(2))
+                               rate = 0x0D;    /* MCS 32 SGI rate */
+                       else
+                               rate = 0x0C;    /* MCS 32 LGI rate */
+               } else if (index < 8) {
+                       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 = mwifiex_data_rates[0];
+       } else {
+               if (index >= MWIFIEX_SUPPORTED_RATES_EXT)
+                       index = 0;
+               rate = mwifiex_data_rates[index];
+       }
+       return rate;
+}
+
+/*
+ * This function maps a data rate value into corresponding index in supported
+ * rates table.
+ */
+u8 mwifiex_data_rate_to_index(struct mwifiex_adapter *adapter, u32 rate)
+{
+       u16 *ptr;
+
+       if (rate) {
+               ptr = memchr(mwifiex_data_rates, rate,
+                               sizeof(mwifiex_data_rates));
+               if (ptr)
+                       return (u8) (ptr - mwifiex_data_rates);
+       }
+       return 0;
+}
+
+/*
+ * This function returns the current active data rates.
+ *
+ * The result may vary depending upon connection status.
+ */
+u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, u8 *rates)
+{
+       u32 k;
+
+       if (!priv->media_connected)
+               k = mwifiex_get_supported_rates(priv, rates);
+       else
+               k = mwifiex_copy_rates(rates, 0,
+                                      priv->curr_bss_params.data_rates,
+                                      priv->curr_bss_params.num_of_rates);
+
+       return k;
+}
+
+/*
+ * This function locates the Channel-Frequency-Power triplet based upon
+ * band and channel parameters.
+ */
+struct mwifiex_chan_freq_power *
+mwifiex_get_cfp_by_band_and_channel_from_cfg80211(struct mwifiex_private
+                                                 *priv, u8 band, u16 channel)
+{
+       struct mwifiex_chan_freq_power *cfp = NULL;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_channel *ch;
+       int i;
+
+       if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG)
+               sband = priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ];
+       else
+               sband = priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ];
+
+       if (!sband) {
+               dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d"
+                               " & channel %d\n", __func__, band, channel);
+               return cfp;
+       }
+
+       for (i = 0; i < sband->n_channels; i++) {
+               ch = &sband->channels[i];
+               if (((ch->hw_value == channel) ||
+                       (channel == FIRST_VALID_CHANNEL))
+                       && !(ch->flags & IEEE80211_CHAN_DISABLED)) {
+                       priv->cfp.channel = channel;
+                       priv->cfp.freq = ch->center_freq;
+                       priv->cfp.max_tx_power = ch->max_power;
+                       cfp = &priv->cfp;
+                       break;
+               }
+       }
+       if (i == sband->n_channels)
+               dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d"
+                               " & channel %d\n", __func__, band, channel);
+
+       return cfp;
+}
+
+/*
+ * This function locates the Channel-Frequency-Power triplet based upon
+ * band and frequency parameters.
+ */
+struct mwifiex_chan_freq_power *
+mwifiex_get_cfp_by_band_and_freq_from_cfg80211(struct mwifiex_private *priv,
+                                              u8 band, u32 freq)
+{
+       struct mwifiex_chan_freq_power *cfp = NULL;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_channel *ch;
+       int i;
+
+       if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG)
+               sband = priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ];
+       else
+               sband = priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ];
+
+       if (!sband) {
+               dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d"
+                               " & freq %d\n", __func__, band, freq);
+               return cfp;
+       }
+
+       for (i = 0; i < sband->n_channels; i++) {
+               ch = &sband->channels[i];
+               if ((ch->center_freq == freq) &&
+                       !(ch->flags & IEEE80211_CHAN_DISABLED)) {
+                       priv->cfp.channel = ch->hw_value;
+                       priv->cfp.freq = freq;
+                       priv->cfp.max_tx_power = ch->max_power;
+                       cfp = &priv->cfp;
+                       break;
+               }
+       }
+       if (i == sband->n_channels)
+               dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d"
+                               " & freq %d\n", __func__, band, freq);
+
+       return cfp;
+}
+
+/*
+ * This function checks if the data rate is set to auto.
+ */
+u8
+mwifiex_is_rate_auto(struct mwifiex_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 converts rate bitmap into rate index.
+ */
+int
+mwifiex_get_rate_index(struct mwifiex_adapter *adapter, u16 *rate_bitmap,
+                      int size)
+{
+       int i;
+
+       for (i = 0; i < size * 8; i++)
+               if (rate_bitmap[i / 16] & (1 << (i % 16)))
+                       return i;
+
+       return 0;
+}
+
+/*
+ * 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 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
+{
+       u32 k = 0;
+       struct mwifiex_adapter *adapter = priv->adapter;
+       if (priv->bss_mode == MWIFIEX_BSS_MODE_INFRA) {
+               /* Infra. mode */
+               switch (adapter->config_bands) {
+               case BAND_B:
+                       dev_dbg(adapter->dev, "info: infra band=%d "
+                               "supported_rates_b\n", adapter->config_bands);
+                       k = mwifiex_copy_rates(rates, k, supported_rates_b,
+                                              sizeof(supported_rates_b));
+                       break;
+               case BAND_G:
+               case BAND_G | BAND_GN:
+                       dev_dbg(adapter->dev, "info: infra band=%d "
+                               "supported_rates_g\n", adapter->config_bands);
+                       k = mwifiex_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_B | BAND_G | BAND_GN:
+                       dev_dbg(adapter->dev, "info: infra band=%d "
+                               "supported_rates_bg\n", adapter->config_bands);
+                       k = mwifiex_copy_rates(rates, k, supported_rates_bg,
+                                              sizeof(supported_rates_bg));
+                       break;
+               case BAND_A:
+               case BAND_A | BAND_G:
+                       dev_dbg(adapter->dev, "info: infra band=%d "
+                               "supported_rates_a\n", adapter->config_bands);
+                       k = mwifiex_copy_rates(rates, k, supported_rates_a,
+                                              sizeof(supported_rates_a));
+                       break;
+               case BAND_A | BAND_AN:
+               case BAND_A | BAND_G | BAND_AN | BAND_GN:
+                       dev_dbg(adapter->dev, "info: infra band=%d "
+                               "supported_rates_a\n", adapter->config_bands);
+                       k = mwifiex_copy_rates(rates, k, supported_rates_a,
+                                              sizeof(supported_rates_a));
+                       break;
+               case BAND_GN:
+                       dev_dbg(adapter->dev, "info: infra band=%d "
+                               "supported_rates_n\n", adapter->config_bands);
+                       k = mwifiex_copy_rates(rates, k, supported_rates_n,
+                                              sizeof(supported_rates_n));
+                       break;
+               }
+       } else {
+               /* Ad-hoc mode */
+               switch (adapter->adhoc_start_band) {
+               case BAND_B:
+                       dev_dbg(adapter->dev, "info: adhoc B\n");
+                       k = mwifiex_copy_rates(rates, k, adhoc_rates_b,
+                                              sizeof(adhoc_rates_b));
+                       break;
+               case BAND_G:
+               case BAND_G | BAND_GN:
+                       dev_dbg(adapter->dev, "info: adhoc G only\n");
+                       k = mwifiex_copy_rates(rates, k, adhoc_rates_g,
+                                              sizeof(adhoc_rates_g));
+                       break;
+               case BAND_B | BAND_G:
+               case BAND_B | BAND_G | BAND_GN:
+                       dev_dbg(adapter->dev, "info: adhoc BG\n");
+                       k = mwifiex_copy_rates(rates, k, adhoc_rates_bg,
+                                              sizeof(adhoc_rates_bg));
+                       break;
+               case BAND_A:
+               case BAND_A | BAND_AN:
+                       dev_dbg(adapter->dev, "info: adhoc A\n");
+                       k = mwifiex_copy_rates(rates, k, adhoc_rates_a,
+                                              sizeof(adhoc_rates_a));
+                       break;
+               }
+       }
+
+       return k;
+}
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
new file mode 100644 (file)
index 0000000..3a8fe1e
--- /dev/null
@@ -0,0 +1,1463 @@
+/*
+ * Marvell Wireless LAN device driver: commands and events
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include "decl.h"
+#include "ioctl.h"
+#include "util.h"
+#include "fw.h"
+#include "main.h"
+#include "wmm.h"
+#include "11n.h"
+
+/*
+ * 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
+mwifiex_init_cmd_node(struct mwifiex_private *priv,
+                     struct cmd_ctrl_node *cmd_node,
+                     u32 cmd_oid, void *wait_queue, void *data_buf)
+{
+       cmd_node->priv = priv;
+       cmd_node->cmd_oid = cmd_oid;
+       cmd_node->wq_buf = wait_queue;
+       cmd_node->data_buf = data_buf;
+       cmd_node->cmd_skb = cmd_node->skb;
+}
+
+/*
+ * This function returns a command node from the free queue depending upon
+ * availability.
+ */
+static struct cmd_ctrl_node *
+mwifiex_get_cmd_node(struct mwifiex_adapter *adapter)
+{
+       struct cmd_ctrl_node *cmd_node;
+       unsigned long flags;
+
+       spin_lock_irqsave(&adapter->cmd_free_q_lock, flags);
+       if (list_empty(&adapter->cmd_free_q)) {
+               dev_err(adapter->dev, "GET_CMD_NODE: cmd node not available\n");
+               spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags);
+               return NULL;
+       }
+       cmd_node = list_first_entry(&adapter->cmd_free_q,
+                       struct cmd_ctrl_node, list);
+       list_del(&cmd_node->list);
+       spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags);
+
+       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
+mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter,
+                      struct cmd_ctrl_node *cmd_node)
+{
+       cmd_node->cmd_oid = 0;
+       cmd_node->cmd_flag = 0;
+       cmd_node->wq_buf = NULL;
+       cmd_node->data_buf = NULL;
+
+       if (cmd_node->resp_skb) {
+               mwifiex_recv_complete(adapter, cmd_node->resp_skb, 0);
+               cmd_node->resp_skb = NULL;
+       }
+
+       return;
+}
+
+/*
+ * This function returns a command node from the pending queue which
+ * matches the given IOCTL request.
+ */
+static struct cmd_ctrl_node *
+mwifiex_get_pending_ioctl_cmd(struct mwifiex_adapter *adapter,
+                             struct mwifiex_wait_queue *wait_queue)
+{
+       unsigned long flags;
+       struct cmd_ctrl_node *cmd_node;
+
+       spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
+       list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) {
+               if (cmd_node->wq_buf == wait_queue) {
+                       spin_unlock_irqrestore(&adapter->cmd_pending_q_lock,
+                                              flags);
+                       return cmd_node;
+               }
+       }
+       spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
+
+       return NULL;
+}
+
+/*
+ * 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 mwifiex_cmd_host_cmd(struct mwifiex_private *priv,
+                               struct host_cmd_ds_command *cmd, void *data_buf)
+{
+       struct mwifiex_ds_misc_cmd *pcmd_ptr =
+               (struct mwifiex_ds_misc_cmd *) data_buf;
+
+       /* Copy the HOST command to command buffer */
+       memcpy((void *) cmd, pcmd_ptr->cmd, pcmd_ptr->len);
+       dev_dbg(priv->adapter->dev, "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 mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
+                                 struct cmd_ctrl_node *cmd_node)
+{
+
+       struct mwifiex_adapter *adapter = priv->adapter;
+       int ret = 0;
+       struct host_cmd_ds_command *host_cmd;
+       struct mwifiex_wait_queue *wait_queue = NULL;
+       uint16_t cmd_code;
+       uint16_t cmd_size;
+       struct timeval tstamp;
+       unsigned long flags;
+
+       if (!adapter || !cmd_node)
+               return -1;
+
+       host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
+       if (cmd_node->wq_buf)
+               wait_queue = (struct mwifiex_wait_queue *) cmd_node->wq_buf;
+
+       /* Sanity test */
+       if (host_cmd == NULL || host_cmd->size == 0) {
+               dev_err(adapter->dev, "DNLD_CMD: host_cmd is null"
+                       " or cmd size is 0, not sending\n");
+               if (wait_queue)
+                       wait_queue->status = MWIFIEX_ERROR_CMD_DNLD_FAIL;
+               mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+               return -1;
+       }
+
+       /* Set command sequence number */
+       adapter->seq_num++;
+       host_cmd->seq_num = cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO
+                           (adapter->seq_num, cmd_node->priv->bss_num,
+                            cmd_node->priv->bss_type));
+
+       spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+       adapter->curr_cmd = cmd_node;
+       spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+
+       cmd_code = le16_to_cpu(host_cmd->command);
+       cmd_size = le16_to_cpu(host_cmd->size);
+
+       skb_trim(cmd_node->cmd_skb, cmd_size);
+
+       do_gettimeofday(&tstamp);
+       dev_dbg(adapter->dev, "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d,"
+               " seqno %#x\n",
+               tstamp.tv_sec, tstamp.tv_usec, cmd_code,
+              le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)), cmd_size,
+              le16_to_cpu(host_cmd->seq_num));
+
+       skb_push(cmd_node->cmd_skb, INTF_HEADER_LEN);
+
+       ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD,
+                                            cmd_node->cmd_skb->data,
+                                            cmd_node->cmd_skb->len, NULL);
+
+       if (ret == -1) {
+               dev_err(adapter->dev, "DNLD_CMD: host to card failed\n");
+               if (wait_queue)
+                       wait_queue->status = MWIFIEX_ERROR_CMD_DNLD_FAIL;
+               mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+
+               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+               adapter->curr_cmd = NULL;
+               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+
+               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] =
+               le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN));
+
+       /* Clear BSS_NO_BITS from HostCmd */
+       cmd_code &= HostCmd_CMD_ID_MASK;
+
+       /* Setup the timer after transmit command */
+       mod_timer(&adapter->cmd_timer,
+               jiffies + (MWIFIEX_TIMER_10S * HZ) / 1000);
+
+       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 mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
+{
+       int ret = 0;
+       u16 cmd_len = 0;
+       struct mwifiex_private *priv;
+       struct mwifiex_opt_sleep_confirm_buffer *sleep_cfm_buf =
+                               (struct mwifiex_opt_sleep_confirm_buffer *)
+                               adapter->sleep_cfm->data;
+       cmd_len = sizeof(struct mwifiex_opt_sleep_confirm);
+       priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+
+       sleep_cfm_buf->ps_cfm_sleep.seq_num =
+               cpu_to_le16((HostCmd_SET_SEQ_NO_BSS_INFO
+                                       (adapter->seq_num, priv->bss_num,
+                                        priv->bss_type)));
+       adapter->seq_num++;
+
+       ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD,
+                                            adapter->sleep_cfm->data,
+                                            adapter->sleep_cfm->len +
+                                            INTF_HEADER_LEN, NULL);
+
+       if (ret == -1) {
+               dev_err(adapter->dev, "SLEEP_CFM: failed\n");
+               adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++;
+               return -1;
+       }
+       if (GET_BSS_ROLE(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY))
+                       == MWIFIEX_BSS_ROLE_STA) {
+               if (!sleep_cfm_buf->ps_cfm_sleep.sleep_cfm.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 (!sleep_cfm_buf->ps_cfm_sleep.sleep_cfm.resp_ctrl
+                               && (adapter->is_hs_configured
+                                       && !adapter->sleep_period.period)) {
+                       adapter->pm_wakeup_card_req = true;
+                       mwifiex_hs_activated_event(mwifiex_get_priv(adapter,
+                                               MWIFIEX_BSS_ROLE_STA), 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 mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter)
+{
+       struct cmd_ctrl_node *cmd_array;
+       u32 buf_size;
+       u32 i;
+
+       /* Allocate and initialize struct cmd_ctrl_node */
+       buf_size = sizeof(struct cmd_ctrl_node) * MWIFIEX_NUM_OF_CMD_BUFFER;
+       cmd_array = kzalloc(buf_size, GFP_KERNEL);
+       if (!cmd_array) {
+               dev_err(adapter->dev, "%s: failed to alloc cmd_array\n",
+                               __func__);
+               return -1;
+       }
+
+       adapter->cmd_pool = cmd_array;
+       memset(adapter->cmd_pool, 0, buf_size);
+
+       /* Allocate and initialize command buffers */
+       for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) {
+               cmd_array[i].skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER);
+               if (!cmd_array[i].skb) {
+                       dev_err(adapter->dev, "ALLOC_CMD_BUF: out of memory\n");
+                       return -1;
+               }
+       }
+
+       for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++)
+               mwifiex_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.
+ */
+int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter)
+{
+       struct cmd_ctrl_node *cmd_array;
+       u32 i;
+
+       /* Need to check if cmd pool is allocated or not */
+       if (!adapter->cmd_pool) {
+               dev_dbg(adapter->dev, "info: FREE_CMD_BUF: cmd_pool is null\n");
+               return 0;
+       }
+
+       cmd_array = adapter->cmd_pool;
+
+       /* Release shared memory buffers */
+       for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) {
+               if (cmd_array[i].skb) {
+                       dev_dbg(adapter->dev, "cmd: free cmd buffer %d\n", i);
+                       dev_kfree_skb_any(cmd_array[i].skb);
+               }
+               if (!cmd_array[i].resp_skb)
+                       continue;
+               mwifiex_recv_complete(adapter, cmd_array[i].resp_skb, 0);
+       }
+       /* Release struct cmd_ctrl_node */
+       if (adapter->cmd_pool) {
+               dev_dbg(adapter->dev, "cmd: free cmd pool\n");
+               kfree(adapter->cmd_pool);
+               adapter->cmd_pool = NULL;
+       }
+
+       return 0;
+}
+
+/*
+ * 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 mwifiex_process_event(struct mwifiex_adapter *adapter)
+{
+       int ret = 0;
+       struct mwifiex_private *priv =
+               mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+       struct sk_buff *skb = adapter->event_skb;
+       u32 eventcause = adapter->event_cause;
+       struct timeval tstamp;
+       struct mwifiex_rxinfo *rx_info = NULL;
+
+       /* 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 = mwifiex_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause),
+                                     EVENT_GET_BSS_TYPE(eventcause));
+       if (!priv)
+               priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+       /* Clear BSS_NO_BITS from event */
+       eventcause &= EVENT_ID_MASK;
+       adapter->event_cause = eventcause;
+
+       if (skb) {
+               rx_info = MWIFIEX_SKB_RXCB(skb);
+               rx_info->bss_index = priv->bss_index;
+       }
+
+       if (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE) {
+               do_gettimeofday(&tstamp);
+               dev_dbg(adapter->dev, "event: %lu.%lu: cause: %#x\n",
+                      tstamp.tv_sec, tstamp.tv_usec, eventcause);
+       }
+
+       ret = mwifiex_process_sta_event(priv);
+
+       adapter->event_cause = 0;
+       adapter->event_skb = NULL;
+
+       mwifiex_recv_complete(adapter, skb, 0);
+
+       return ret;
+}
+
+/*
+ * This function prepares a command before sending 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 mwifiex_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
+                       u16 cmd_action, u32 cmd_oid,
+                       void *wait_queue, void *data_buf)
+{
+       int ret = 0;
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *cmd_node = NULL;
+       struct host_cmd_ds_command *cmd_ptr = NULL;
+
+       if (!adapter) {
+               pr_err("PREP_CMD: adapter is NULL\n");
+               return -1;
+       }
+
+       if (adapter->is_suspended) {
+               dev_err(adapter->dev, "PREP_CMD: device in suspended state\n");
+               return -1;
+       }
+
+       if (adapter->surprise_removed) {
+               dev_err(adapter->dev, "PREP_CMD: card is removed\n");
+               return -1;
+       }
+
+       if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) {
+               if (cmd_no != HostCmd_CMD_FUNC_INIT) {
+                       dev_err(adapter->dev, "PREP_CMD: FW in reset state\n");
+                       return -1;
+               }
+       }
+
+       /* Get a new command node */
+       cmd_node = mwifiex_get_cmd_node(adapter);
+
+       if (!cmd_node) {
+               dev_err(adapter->dev, "PREP_CMD: no free cmd node\n");
+               return -1;
+       }
+
+       /* Initialize the command node */
+       mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, wait_queue, data_buf);
+
+       if (!cmd_node->cmd_skb) {
+               dev_err(adapter->dev, "PREP_CMD: no free cmd buf\n");
+               return -1;
+       }
+
+       memset(skb_put(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command)),
+              0, sizeof(struct host_cmd_ds_command));
+
+       cmd_ptr = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
+       cmd_ptr->command = cpu_to_le16(cmd_no);
+       cmd_ptr->result = 0;
+
+       /* Prepare command */
+       if (cmd_no) {
+               ret = mwifiex_sta_prepare_cmd(priv, cmd_no, cmd_action,
+                                             cmd_oid, data_buf, cmd_ptr);
+       } else {
+               ret = mwifiex_cmd_host_cmd(priv, cmd_ptr, data_buf);
+               cmd_node->cmd_flag |= CMD_F_HOSTCMD;
+       }
+
+       /* Return error, since the command preparation failed */
+       if (ret) {
+               dev_err(adapter->dev, "PREP_CMD: cmd %#x preparation failed\n",
+                                                       cmd_no);
+               mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+               return -1;
+       }
+
+       /* Send command */
+       if (cmd_no == HostCmd_CMD_802_11_SCAN)
+               mwifiex_queue_scan_cmd(priv, cmd_node);
+       else
+               mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true);
+
+       return ret;
+}
+
+/*
+ * 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.
+ */
+void
+mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter,
+                            struct cmd_ctrl_node *cmd_node)
+{
+       struct mwifiex_wait_queue *wait_queue = NULL;
+       unsigned long flags;
+
+       if (cmd_node == NULL)
+               return;
+       if (cmd_node->wq_buf) {
+               wait_queue = (struct mwifiex_wait_queue *) cmd_node->wq_buf;
+               if (wait_queue->status != MWIFIEX_ERROR_NO_ERROR)
+                       mwifiex_ioctl_complete(adapter, wait_queue, -1);
+               else
+                       mwifiex_ioctl_complete(adapter, wait_queue, 0);
+       }
+       /* Clean the node */
+       mwifiex_clean_cmd_node(adapter, cmd_node);
+
+       /* Insert node into cmd_free_q */
+       spin_lock_irqsave(&adapter->cmd_free_q_lock, flags);
+       list_add_tail(&cmd_node->list, &adapter->cmd_free_q);
+       spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags);
+
+       return;
+}
+
+/*
+ * 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
+mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter,
+                               struct cmd_ctrl_node *cmd_node, u32 add_tail)
+{
+       struct host_cmd_ds_command *host_cmd = NULL;
+       u16 command;
+       unsigned long flags;
+
+       host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
+       if (!host_cmd) {
+               dev_err(adapter->dev, "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 == HostCmd_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;
+               }
+       }
+
+       spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
+       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_irqrestore(&adapter->cmd_pending_q_lock, flags);
+
+       dev_dbg(adapter->dev, "cmd: QUEUE_CMD: cmd=%#x is queued\n", command);
+
+       return;
+}
+
+/*
+ * 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 mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter)
+{
+       struct mwifiex_private *priv = NULL;
+       struct cmd_ctrl_node *cmd_node = NULL;
+       int ret = 0;
+       struct host_cmd_ds_command *host_cmd;
+       unsigned long cmd_flags;
+       unsigned long cmd_pending_q_flags;
+
+       /* Check if already in processing */
+       if (adapter->curr_cmd) {
+               dev_err(adapter->dev, "EXEC_NEXT_CMD: cmd in processing\n");
+               return -1;
+       }
+
+       spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
+       /* Check if any command is pending */
+       spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags);
+       if (list_empty(&adapter->cmd_pending_q)) {
+               spin_unlock_irqrestore(&adapter->cmd_pending_q_lock,
+                                      cmd_pending_q_flags);
+               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+               return 0;
+       }
+       cmd_node = list_first_entry(&adapter->cmd_pending_q,
+                                   struct cmd_ctrl_node, list);
+       spin_unlock_irqrestore(&adapter->cmd_pending_q_lock,
+                              cmd_pending_q_flags);
+
+       host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data);
+       priv = cmd_node->priv;
+
+       if (adapter->ps_state != PS_STATE_AWAKE) {
+               dev_err(adapter->dev, "%s: cannot send cmd in sleep state,"
+                               " this should not happen\n", __func__);
+               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+               return ret;
+       }
+
+       spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags);
+       list_del(&cmd_node->list);
+       spin_unlock_irqrestore(&adapter->cmd_pending_q_lock,
+                              cmd_pending_q_flags);
+
+       spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+       ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node);
+       priv = mwifiex_get_priv(adapter, MWIFIEX_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(HostCmd_CMD_802_11_HS_CFG_ENH))) {
+               if (adapter->hs_activated) {
+                       adapter->is_hs_configured = false;
+                       mwifiex_hs_activated_event(priv, false);
+               }
+       }
+
+       return ret;
+}
+
+/*
+ * This function handles the command response.
+ *
+ * After processing, the function cleans the command node and puts
+ * it back to the command free queue.
+ */
+int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
+{
+       struct host_cmd_ds_command *resp = NULL;
+       struct mwifiex_private *priv =
+               mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+       int ret = 0;
+       uint16_t orig_cmdresp_no;
+       uint16_t cmdresp_no;
+       uint16_t cmdresp_result;
+       struct mwifiex_wait_queue *wait_queue = NULL;
+       struct timeval tstamp;
+       unsigned long flags;
+
+       /* Now we got response from FW, cancel the command timer */
+       del_timer(&adapter->cmd_timer);
+
+       if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) {
+               resp = (struct host_cmd_ds_command *) adapter->upld_buf;
+               dev_err(adapter->dev, "CMD_RESP: NULL curr_cmd, %#x\n",
+                      le16_to_cpu(resp->command));
+               return -1;
+       }
+
+       if (adapter->curr_cmd->wq_buf)
+               wait_queue = (struct mwifiex_wait_queue *)
+                               adapter->curr_cmd->wq_buf;
+
+       adapter->num_cmd_timeout = 0;
+
+       resp = (struct host_cmd_ds_command *) adapter->curr_cmd->resp_skb->data;
+       if (adapter->curr_cmd->cmd_flag & CMD_F_CANCELED) {
+               dev_err(adapter->dev, "CMD_RESP: %#x been canceled\n",
+                               le16_to_cpu(resp->command));
+               mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+               adapter->curr_cmd = NULL;
+               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+               return -1;
+       }
+
+       if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {
+               /* Copy original response back to response buffer */
+               struct mwifiex_ds_misc_cmd *hostcmd = NULL;
+               uint16_t size = le16_to_cpu(resp->size);
+               dev_dbg(adapter->dev, "info: host cmd resp size = %d\n", size);
+               size = min_t(u16, size, MWIFIEX_SIZE_OF_CMD_BUFFER);
+               if (adapter->curr_cmd->data_buf) {
+                       hostcmd = (struct mwifiex_ds_misc_cmd *)
+                                               adapter->curr_cmd->data_buf;
+                       hostcmd->len = size;
+                       memcpy(hostcmd->cmd, (void *) resp, size);
+               }
+       }
+       orig_cmdresp_no = le16_to_cpu(resp->command);
+
+       /* Get BSS number and corresponding priv */
+       priv = mwifiex_get_priv_by_id(adapter,
+                       HostCmd_GET_BSS_NO(le16_to_cpu(resp->seq_num)),
+                       HostCmd_GET_BSS_TYPE(le16_to_cpu(resp->seq_num)));
+       if (!priv)
+               priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+       /* Clear RET_BIT from HostCmd */
+       resp->command = cpu_to_le16(orig_cmdresp_no & HostCmd_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;
+
+       do_gettimeofday(&tstamp);
+       dev_dbg(adapter->dev, "cmd: CMD_RESP: (%lu.%lu): 0x%x, result %d,"
+               " len %d, seqno 0x%x\n",
+              tstamp.tv_sec, tstamp.tv_usec, orig_cmdresp_no, cmdresp_result,
+              le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num));
+
+       if (!(orig_cmdresp_no & HostCmd_RET_BIT)) {
+               dev_err(adapter->dev, "CMD_RESP: invalid cmd resp\n");
+               if (wait_queue)
+                       wait_queue->status = MWIFIEX_ERROR_FW_CMDRESP;
+
+               mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+               adapter->curr_cmd = NULL;
+               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+               return -1;
+       }
+
+       if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {
+               adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD;
+               if ((cmdresp_result == HostCmd_RESULT_OK)
+                   && (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH))
+                       ret = mwifiex_ret_802_11_hs_cfg(priv, resp);
+       } else {
+               /* handle response */
+               ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp,
+                                                 wait_queue);
+       }
+
+       /* Check init command response */
+       if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {
+               if (ret == -1) {
+                       dev_err(adapter->dev, "%s: cmd %#x failed during "
+                               "initialization\n", __func__, cmdresp_no);
+                       mwifiex_init_fw_complete(adapter);
+                       return -1;
+               } else if (adapter->last_init_cmd == cmdresp_no)
+                       adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE;
+       }
+
+       if (adapter->curr_cmd) {
+               if (wait_queue && (!ret))
+                       wait_queue->status = MWIFIEX_ERROR_NO_ERROR;
+               else if (wait_queue && (ret == -1))
+                       wait_queue->status = MWIFIEX_ERROR_CMD_RESP_FAIL;
+
+               /* Clean up and put current command back to cmd_free_q */
+               mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+
+               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+               adapter->curr_cmd = NULL;
+               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+       }
+
+       return ret;
+}
+
+/*
+ * This function handles the timeout of command sending.
+ *
+ * It will re-send the same command again.
+ */
+void
+mwifiex_cmd_timeout_func(unsigned long function_context)
+{
+       struct mwifiex_adapter *adapter =
+               (struct mwifiex_adapter *) function_context;
+       struct cmd_ctrl_node *cmd_node = NULL;
+       struct mwifiex_wait_queue *wait_queue = NULL;
+       struct timeval tstamp;
+
+       adapter->num_cmd_timeout++;
+       adapter->dbg.num_cmd_timeout++;
+       if (!adapter->curr_cmd) {
+               dev_dbg(adapter->dev, "cmd: empty curr_cmd\n");
+               return;
+       }
+       cmd_node = adapter->curr_cmd;
+       if (cmd_node->wq_buf) {
+               wait_queue = (struct mwifiex_wait_queue *) cmd_node->wq_buf;
+               wait_queue->status = MWIFIEX_ERROR_CMD_TIMEOUT;
+       }
+
+       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];
+               do_gettimeofday(&tstamp);
+               dev_err(adapter->dev, "%s: Timeout cmd id (%lu.%lu) = %#x,"
+                       " act = %#x\n", __func__,
+                      tstamp.tv_sec, tstamp.tv_usec,
+                      adapter->dbg.timeout_cmd_id,
+                      adapter->dbg.timeout_cmd_act);
+
+               dev_err(adapter->dev, "num_data_h2c_failure = %d\n",
+                      adapter->dbg.num_tx_host_to_card_failure);
+               dev_err(adapter->dev, "num_cmd_h2c_failure = %d\n",
+                      adapter->dbg.num_cmd_host_to_card_failure);
+
+               dev_err(adapter->dev, "num_cmd_timeout = %d\n",
+                      adapter->dbg.num_cmd_timeout);
+               dev_err(adapter->dev, "num_tx_timeout = %d\n",
+                      adapter->dbg.num_tx_timeout);
+
+               dev_err(adapter->dev, "last_cmd_index = %d\n",
+                      adapter->dbg.last_cmd_index);
+               print_hex_dump_bytes("last_cmd_id: ", DUMP_PREFIX_OFFSET,
+                               adapter->dbg.last_cmd_id, DBG_CMD_NUM);
+               print_hex_dump_bytes("last_cmd_act: ", DUMP_PREFIX_OFFSET,
+                               adapter->dbg.last_cmd_act, DBG_CMD_NUM);
+
+               dev_err(adapter->dev, "last_cmd_resp_index = %d\n",
+                      adapter->dbg.last_cmd_resp_index);
+               print_hex_dump_bytes("last_cmd_resp_id: ", DUMP_PREFIX_OFFSET,
+                               adapter->dbg.last_cmd_resp_id, DBG_CMD_NUM);
+
+               dev_err(adapter->dev, "last_event_index = %d\n",
+                      adapter->dbg.last_event_index);
+               print_hex_dump_bytes("last_event: ", DUMP_PREFIX_OFFSET,
+                               adapter->dbg.last_event, DBG_CMD_NUM);
+
+               dev_err(adapter->dev, "data_sent=%d cmd_sent=%d\n",
+                      adapter->data_sent, adapter->cmd_sent);
+
+               dev_err(adapter->dev, "ps_mode=%d ps_state=%d\n",
+                               adapter->ps_mode, adapter->ps_state);
+       }
+       if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING)
+               mwifiex_init_fw_complete(adapter);
+
+       return;
+}
+
+/*
+ * 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
+mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)
+{
+       struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL;
+       struct mwifiex_wait_queue *wait_queue = NULL;
+       unsigned long flags;
+
+       /* Cancel current cmd */
+       if ((adapter->curr_cmd) && (adapter->curr_cmd->wq_buf)) {
+               wait_queue =
+                       (struct mwifiex_wait_queue *) adapter->curr_cmd->wq_buf;
+               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+               adapter->curr_cmd->wq_buf = NULL;
+               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+               wait_queue->status = MWIFIEX_ERROR_CMD_CANCEL;
+               mwifiex_ioctl_complete(adapter, wait_queue, -1);
+       }
+       /* Cancel all pending command */
+       spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
+       list_for_each_entry_safe(cmd_node, tmp_node,
+                                &adapter->cmd_pending_q, list) {
+               list_del(&cmd_node->list);
+               spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
+
+               if (cmd_node->wq_buf) {
+                       wait_queue =
+                               (struct mwifiex_wait_queue *) cmd_node->wq_buf;
+                       wait_queue->status = MWIFIEX_ERROR_CMD_CANCEL;
+                       mwifiex_ioctl_complete(adapter, wait_queue, -1);
+                       cmd_node->wq_buf = NULL;
+               }
+               mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+               spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
+       }
+       spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
+
+       /* Cancel all pending scan command */
+       spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+       list_for_each_entry_safe(cmd_node, tmp_node,
+                                &adapter->scan_pending_q, list) {
+               list_del(&cmd_node->list);
+               spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
+
+               cmd_node->wq_buf = NULL;
+               mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+               spin_lock_irqsave(&adapter->scan_pending_q_lock, flags);
+       }
+       spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags);
+
+       spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
+       adapter->scan_processing = false;
+       spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+}
+
+/*
+ * 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.
+ */
+void
+mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter,
+                            struct mwifiex_wait_queue *wait_queue)
+{
+       struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL;
+       unsigned long cmd_flags;
+       unsigned long cmd_pending_q_flags;
+       unsigned long scan_pending_q_flags;
+       uint16_t cancel_scan_cmd = false;
+
+       if ((adapter->curr_cmd) &&
+           (adapter->curr_cmd->wq_buf == wait_queue)) {
+               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
+               cmd_node = adapter->curr_cmd;
+               cmd_node->wq_buf = NULL;
+               cmd_node->cmd_flag |= CMD_F_CANCELED;
+               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+       }
+
+       spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
+       while (1) {
+               cmd_node = mwifiex_get_pending_ioctl_cmd(adapter, wait_queue);
+               if (!cmd_node)
+                       break;
+
+               spin_lock_irqsave(&adapter->cmd_pending_q_lock,
+                                 cmd_pending_q_flags);
+               list_del(&cmd_node->list);
+               spin_unlock_irqrestore(&adapter->cmd_pending_q_lock,
+                                      cmd_pending_q_flags);
+
+               cmd_node->wq_buf = NULL;
+               mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+       }
+       spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+       /* Cancel all pending scan command */
+       spin_lock_irqsave(&adapter->scan_pending_q_lock,
+                         scan_pending_q_flags);
+       list_for_each_entry_safe(cmd_node, tmp_node,
+                                &adapter->scan_pending_q, list) {
+               if (cmd_node->wq_buf == wait_queue) {
+                       list_del(&cmd_node->list);
+                       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+                                              scan_pending_q_flags);
+                       cmd_node->wq_buf = NULL;
+                       mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+                       spin_lock_irqsave(&adapter->scan_pending_q_lock,
+                                         scan_pending_q_flags);
+                       cancel_scan_cmd = true;
+               }
+       }
+       spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
+                              scan_pending_q_flags);
+
+       if (cancel_scan_cmd) {
+               spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
+               adapter->scan_processing = false;
+               spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+       }
+       wait_queue->status = MWIFIEX_ERROR_CMD_CANCEL;
+       mwifiex_ioctl_complete(adapter, wait_queue, -1);
+
+       return;
+}
+
+/*
+ * 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
+mwifiex_check_ps_cond(struct mwifiex_adapter *adapter)
+{
+       if (!adapter->cmd_sent &&
+           !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter))
+               mwifiex_dnld_sleep_confirm_cmd(adapter);
+       else
+               dev_dbg(adapter->dev,
+                       "cmd: Delay Sleep Confirm (%s%s%s)\n",
+                      (adapter->cmd_sent) ? "D" : "",
+                      (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
+mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated)
+{
+       if (activated) {
+               if (priv->adapter->is_hs_configured) {
+                       priv->adapter->hs_activated = true;
+                       dev_dbg(priv->adapter->dev, "event: hs_activated\n");
+                       priv->adapter->hs_activate_wait_q_woken = true;
+                       wake_up_interruptible(
+                               &priv->adapter->hs_activate_wait_q);
+               } else {
+                       dev_dbg(priv->adapter->dev, "event: HS not configured\n");
+               }
+       } else {
+               dev_dbg(priv->adapter->dev, "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 mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
+                             struct host_cmd_ds_command *resp)
+{
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg =
+               &resp->params.opt_hs_cfg;
+       uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions);
+
+       if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE)) {
+               mwifiex_hs_activated_event(priv, true);
+               return 0;
+       } else {
+               dev_dbg(adapter->dev, "cmd: CMD_RESP: HS_CFG cmd reply"
+                       " 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 != HOST_SLEEP_CFG_CANCEL) {
+               adapter->is_hs_configured = true;
+       } else {
+               adapter->is_hs_configured = false;
+               if (adapter->hs_activated)
+                       mwifiex_hs_activated_event(priv, false);
+       }
+
+       return 0;
+}
+
+/*
+ * This function wakes up the adapter and generates a Host Sleep
+ * cancel event on receiving the power up interrupt.
+ */
+void
+mwifiex_process_hs_config(struct mwifiex_adapter *adapter)
+{
+       dev_dbg(adapter->dev, "info: %s: auto cancelling host sleep"
+               " since there is interrupt from the firmware\n", __func__);
+
+       adapter->if_ops.wakeup(adapter);
+       adapter->hs_activated = false;
+       adapter->is_hs_configured = false;
+       mwifiex_hs_activated_event(mwifiex_get_priv(adapter,
+                                  MWIFIEX_BSS_ROLE_ANY), false);
+       return;
+}
+
+/*
+ * 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
+mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter,
+                                  u8 *pbuf, u32 upld_len)
+{
+       struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *) pbuf;
+       struct mwifiex_private *priv =
+               mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+       uint16_t result = le16_to_cpu(cmd->result);
+       uint16_t command = le16_to_cpu(cmd->command);
+       uint16_t seq_num = le16_to_cpu(cmd->seq_num);
+
+       if (!upld_len) {
+               dev_err(adapter->dev, "%s: cmd size is 0\n", __func__);
+               return;
+       }
+
+       /* Get BSS number and corresponding priv */
+       priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num),
+                                     HostCmd_GET_BSS_TYPE(seq_num));
+       if (!priv)
+               priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
+
+       /* Update sequence number */
+       seq_num = HostCmd_GET_SEQ_NO(seq_num);
+       /* Clear RET_BIT from HostCmd */
+       command &= HostCmd_CMD_ID_MASK;
+
+       if (command != HostCmd_CMD_802_11_PS_MODE_ENH) {
+               dev_err(adapter->dev, "%s: received unexpected response for"
+                       " cmd %x, result = %x\n", __func__, command, result);
+               return;
+       }
+
+       if (result) {
+               dev_err(adapter->dev, "%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 (adapter->is_hs_configured)
+               mwifiex_hs_activated_event(mwifiex_get_priv(adapter,
+                                          MWIFIEX_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(mwifiex_process_sleep_confirm_resp);
+
+/*
+ * This function prepares an enhanced power mode command.
+ *
+ * This function can be used to disable power save or to configure
+ * power save with auto PS or STA PS or auto deep sleep.
+ *
+ * Preparation includes -
+ *      - Setting command ID, action and proper size
+ *      - Setting Power Save bitmap, PS parameters TLV, PS mode TLV,
+ *        auto deep sleep TLV (as required)
+ *      - Ensuring correct endian-ness
+ */
+int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv,
+                              struct host_cmd_ds_command *cmd,
+                              u16 cmd_action, uint16_t ps_bitmap,
+                              void *data_buf)
+{
+       struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh =
+               &cmd->params.psmode_enh;
+       u8 *tlv = NULL;
+       u16 cmd_size = 0;
+
+       cmd->command = cpu_to_le16(HostCmd_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 + AUTO_PS_FIX_SIZE);
+       } 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 + AUTO_PS_FIX_SIZE);
+       } else if (cmd_action == EN_AUTO_PS) {
+               psmode_enh->action = cpu_to_le16(EN_AUTO_PS);
+               psmode_enh->params.auto_ps.ps_bitmap = cpu_to_le16(ps_bitmap);
+               cmd_size = S_DS_GEN + AUTO_PS_FIX_SIZE;
+               tlv = (u8 *) cmd + cmd_size;
+               if (ps_bitmap & BITMAP_STA_PS) {
+                       struct mwifiex_adapter *adapter = priv->adapter;
+                       struct mwifiex_ie_types_ps_param *ps_tlv =
+                               (struct mwifiex_ie_types_ps_param *) tlv;
+                       struct mwifiex_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 mwifiex_ie_types_header));
+                       cmd_size += sizeof(*ps_tlv);
+                       tlv += sizeof(*ps_tlv);
+                       dev_dbg(adapter->dev, "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->adhoc_wake_period =
+                               cpu_to_le16(adapter->adhoc_awake_period);
+                       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 mwifiex_ie_types_auto_ds_param *auto_ps_tlv =
+                               (struct mwifiex_ie_types_auto_ds_param *) tlv;
+                       struct mwifiex_auto_ds_param *auto_ds =
+                               &auto_ps_tlv->param;
+                       u16 idletime = 0;
+                       auto_ps_tlv->header.type =
+                               cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM);
+                       auto_ps_tlv->header.len =
+                               cpu_to_le16(sizeof(*auto_ps_tlv) -
+                                       sizeof(struct mwifiex_ie_types_header));
+                       cmd_size += sizeof(*auto_ps_tlv);
+                       tlv += sizeof(*auto_ps_tlv);
+                       if (data_buf)
+                               idletime = ((struct mwifiex_ds_auto_ds *)
+                                            data_buf)->idle_time;
+                       dev_dbg(priv->adapter->dev,
+                                       "cmd: PS Command: Enter Auto Deep Sleep\n");
+                       auto_ds->deep_sleep_timeout = cpu_to_le16(idletime);
+               }
+               cmd->size = cpu_to_le16(cmd_size);
+       }
+       return 0;
+}
+
+/*
+ * This function handles the command response of an enhanced power mode
+ * command.
+ *
+ * Handling includes changing the header fields into CPU format
+ * and setting the current enhanced power mode in driver.
+ */
+int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv,
+                              struct host_cmd_ds_command *resp,
+                              void *data_buf)
+{
+       struct mwifiex_adapter *adapter = priv->adapter;
+       struct host_cmd_ds_802_11_ps_mode_enh *ps_mode =
+               &resp->params.psmode_enh;
+       uint16_t action = le16_to_cpu(ps_mode->action);
+       uint16_t ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap);
+       uint16_t auto_ps_bitmap =
+               le16_to_cpu(ps_mode->params.auto_ps.ps_bitmap);
+
+       dev_dbg(adapter->dev, "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) {
+                       dev_dbg(adapter->dev, "cmd: Enabled auto deep sleep\n");
+                       priv->adapter->is_deep_sleep = true;
+               }
+               if (auto_ps_bitmap & BITMAP_STA_PS) {
+                       dev_dbg(adapter->dev, "cmd: Enabled STA power save\n");
+                       if (adapter->sleep_period.period)
+                               dev_dbg(adapter->dev, "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;
+                       dev_dbg(adapter->dev, "cmd: Disabled auto deep sleep\n");
+               }
+               if (ps_bitmap & BITMAP_STA_PS) {
+                       dev_dbg(adapter->dev, "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 | BITMAP_UAP_INACT_PS
+                                                       | BITMAP_UAP_DTIM_PS))
+                       adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP;
+               else
+                       adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
+
+               dev_dbg(adapter->dev, "cmd: ps_bitmap=%#x\n", ps_bitmap);
+
+               if (data_buf) {
+                       /* This section is for get power save mode */
+                       struct mwifiex_ds_pm_cfg *pm_cfg =
+                                       (struct mwifiex_ds_pm_cfg *)data_buf;
+                       if (ps_bitmap & BITMAP_STA_PS)
+                               pm_cfg->param.ps_mode = 1;
+                       else
+                               pm_cfg->param.ps_mode = 0;
+               }
+       }
+       return 0;
+}
+
+/*
+ * This function prepares command to get hardware specifications.
+ *
+ * Preparation includes -
+ *      - Setting command ID, action and proper size
+ *      - Setting permanent address parameter
+ *      - Ensuring correct endian-ness
+ */
+int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv,
+                           struct host_cmd_ds_command *cmd)
+{
+       struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec;
+
+       cmd->command = cpu_to_le16(HostCmd_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;
+}
+
+/*
+ * This function handles the command response of get hardware
+ * specifications.
+ *
+ * Handling includes changing the header fields into CPU format
+ * and saving/updating the following parameters in driver -
+ *      - Firmware capability information
+ *      - Firmware band settings
+ *      - Ad-hoc start band and channel
+ *      - Ad-hoc 11n activation status
+ *      - Firmware release number
+ *      - Number of antennas
+ *      - Hardware address
+ *      - Hardware interface version
+ *      - Firmware version
+ *      - Region code
+ *      - 11n capabilities
+ *      - MCS support fields
+ *      - MP end port
+ */
+int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv,
+                           struct host_cmd_ds_command *resp)
+{
+       struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec;
+       struct mwifiex_adapter *adapter = priv->adapter;
+       int i;
+
+       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;
+               }
+               if (adapter->fw_bands & BAND_AN) {
+                       adapter->adhoc_start_band = BAND_A | BAND_AN;
+                       adapter->adhoc_11n_enabled = true;
+               } else {
+                       adapter->adhoc_start_band = BAND_A;
+               }
+               priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A;
+       } else if (adapter->fw_bands & BAND_GN) {
+               adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN;
+               priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
+               adapter->adhoc_11n_enabled = true;
+       } else if (adapter->fw_bands & BAND_G) {
+               adapter->adhoc_start_band = BAND_G | BAND_B;
+               priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
+       } else if (adapter->fw_bands & BAND_B) {
+               adapter->adhoc_start_band = BAND_B;
+               priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
+       }
+
+       adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number);
+       adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna);
+
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: fw_release_number- %#x\n",
+              adapter->fw_release_number);
+       dev_dbg(adapter->dev, "info: GET_HW_SPEC: permanent addr: %pM\n",
+                                       hw_spec->permanent_addr);
+       dev_dbg(adapter->dev, "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));
+
+       if (priv->curr_addr[0] == 0xff)
+               memmove(priv->curr_addr, hw_spec->permanent_addr, ETH_ALEN);
+
+       adapter->region_code = le16_to_cpu(hw_spec->region_code);
+
+       for (i = 0; i < MWIFIEX_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 (USA) */
+       if (i >= MWIFIEX_MAX_REGION_CODE) {
+               adapter->region_code = 0x10;
+               dev_dbg(adapter->dev, "cmd: unknown region code, use default (USA)\n");
+       }
+
+       adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap);
+       adapter->usr_dot_11n_dev_cap = adapter->hw_dot_11n_dev_cap &
+               DEFAULT_11N_CAP_MASK;
+       adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support;
+       adapter->usr_dev_mcs_support = adapter->hw_dev_mcs_support;
+       mwifiex_show_dot_11n_dev_cap(adapter, adapter->hw_dot_11n_dev_cap);
+       mwifiex_show_dev_mcs_support(adapter, adapter->hw_dev_mcs_support);
+
+       if (adapter->if_ops.update_mp_end_port)
+               adapter->if_ops.update_mp_end_port(adapter,
+                                       le16_to_cpu(hw_spec->mp_end_port));
+
+       return 0;
+}
diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c
new file mode 100644 (file)
index 0000000..63b0969
--- /dev/null
@@ -0,0 +1,773 @@
+/*
+ * Marvell Wireless LAN device driver: debugfs
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include <linux/debugfs.h>
+
+#include "main.h"
+#include "11n.h"
+
+
+static struct dentry *mwifiex_dfs_dir;
+
+static char *bss_modes[] = {
+       "Unknown",
+       "Managed",
+       "Ad-hoc",
+       "Auto"
+};
+
+/* size/addr for mwifiex_debug_info */
+#define item_size(n)           (FIELD_SIZEOF(struct mwifiex_debug_info, n))
+#define item_addr(n)           (offsetof(struct mwifiex_debug_info, n))
+
+/* size/addr for struct mwifiex_adapter */
+#define adapter_item_size(n)   (FIELD_SIZEOF(struct mwifiex_adapter, n))
+#define adapter_item_addr(n)   (offsetof(struct mwifiex_adapter, n))
+
+struct mwifiex_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 struct mwifiex_debug_data items[] = {
+       {"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},
+       {"max_tx_buf_size", item_size(max_tx_buf_size),
+        item_addr(max_tx_buf_size), 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},
+       {"num_cmd_timeout", item_size(num_cmd_timeout),
+        item_addr(num_cmd_timeout), 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},
+       {"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 mwifiex_adapter */
+       {"ioctl_pending", adapter_item_size(ioctl_pending),
+        adapter_item_addr(ioctl_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);
+
+/*
+ * Generic proc file open handler.
+ *
+ * This function is called every time a file is accessed for read or write.
+ */
+static int
+mwifiex_open_generic(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+
+/*
+ * 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
+mwifiex_info_read(struct file *file, char __user *ubuf,
+                 size_t count, loff_t *ppos)
+{
+       struct mwifiex_private *priv =
+               (struct mwifiex_private *) file->private_data;
+       struct net_device *netdev = priv->netdev;
+       struct netdev_hw_addr *ha;
+       unsigned long page = get_zeroed_page(GFP_KERNEL);
+       char *p = (char *) page, fmt[64];
+       struct mwifiex_bss_info info;
+       ssize_t ret = 0;
+       int i = 0;
+
+       if (!p)
+               return -ENOMEM;
+
+       memset(&info, 0, sizeof(info));
+       ret = mwifiex_get_bss_info(priv, &info);
+       if (ret)
+               goto free_and_exit;
+
+       mwifiex_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1);
+
+       if (!priv->version_str[0])
+               mwifiex_get_ver_ext(priv);
+
+       p += sprintf(p, "driver_name = " "\"mwifiex\"\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);
+       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=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n",
+                    netdev->dev_addr[0], netdev->dev_addr[1],
+                    netdev->dev_addr[2], netdev->dev_addr[3],
+                    netdev->dev_addr[4], netdev->dev_addr[5]);
+
+       if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) {
+               p += sprintf(p, "multicast_count=\"%d\"\n",
+                            netdev_mc_count(netdev));
+               p += sprintf(p, "essid=\"%s\"\n", info.ssid.ssid);
+               p += sprintf(p, "bssid=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n",
+                            info.bssid[0], info.bssid[1],
+                            info.bssid[2], info.bssid[3],
+                            info.bssid[4], info.bssid[5]);
+               p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan);
+               p += sprintf(p, "region_code = \"%02x\"\n", info.region_code);
+
+               netdev_for_each_mc_addr(ha, netdev)
+                       p += sprintf(p, "multicast_address[%d]="
+                                    "\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", i++,
+                                    ha->addr[0], ha->addr[1],
+                                    ha->addr[2], ha->addr[3],
+                                    ha->addr[4], ha->addr[5]);
+       }
+
+       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 %s\n", ((netif_queue_stopped(priv->netdev))
+                                         ? "stopped" : "started"));
+
+       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
+ */
+static ssize_t
+mwifiex_getlog_read(struct file *file, char __user *ubuf,
+                   size_t count, loff_t *ppos)
+{
+       struct mwifiex_private *priv =
+               (struct mwifiex_private *) file->private_data;
+       unsigned long page = get_zeroed_page(GFP_KERNEL);
+       char *p = (char *) page;
+       ssize_t ret = 0;
+       struct mwifiex_ds_get_stats stats;
+
+       if (!p)
+               return -ENOMEM;
+
+       memset(&stats, 0, sizeof(stats));
+       ret = mwifiex_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",
+                    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]);
+
+
+       ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
+                                     (unsigned long) p - page);
+
+free_and_exit:
+       free_page(page);
+       return ret;
+}
+
+static struct mwifiex_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
+mwifiex_debug_read(struct file *file, char __user *ubuf,
+                  size_t count, loff_t *ppos)
+{
+       struct mwifiex_private *priv =
+               (struct mwifiex_private *) file->private_data;
+       struct mwifiex_debug_data *d = &items[0];
+       unsigned long page = get_zeroed_page(GFP_KERNEL);
+       char *p = (char *) page;
+       ssize_t ret = 0;
+       size_t size, addr;
+       long val;
+       int i, j;
+
+       if (!p)
+               return -ENOMEM;
+
+       ret = mwifiex_get_debug_info(priv, &info);
+       if (ret)
+               goto free_and_exit;
+
+       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 mwifiex_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 = *((u16 *) addr);
+                               break;
+                       case 4:
+                               val = *((u32 *) addr);
+                               break;
+                       case 8:
+                               val = *((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 = %02x:%02x:%02x:%02x:%02x:%02x\n",
+                                    info.tx_tbl[i].tid, info.tx_tbl[i].ra[0],
+                                    info.tx_tbl[i].ra[1], info.tx_tbl[i].ra[2],
+                                    info.tx_tbl[i].ra[3], info.tx_tbl[i].ra[4],
+                                    info.tx_tbl[i].ra[5]);
+       }
+
+       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 = %02x:%02x:%02x:%02x:%02x:%02x, "
+                                    "start_win = %d, "
+                                    "win_size = %d, buffer: ",
+                                    info.rx_tbl[i].tid,
+                                    info.rx_tbl[i].ta[0], info.rx_tbl[i].ta[1],
+                                    info.rx_tbl[i].ta[2], info.rx_tbl[i].ta[3],
+                                    info.rx_tbl[i].ta[4], info.rx_tbl[i].ta[5],
+                                    info.rx_tbl[i].start_win,
+                                    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");
+               }
+       }
+
+       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
+mwifiex_regrdwr_write(struct file *file,
+                     const char __user *ubuf, size_t count, loff_t *ppos)
+{
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *) addr;
+       size_t buf_size = min(count, (size_t) (PAGE_SIZE - 1));
+       int ret = 0;
+       u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX;
+
+       if (!buf)
+               return -ENOMEM;
+
+
+       if (copy_from_user(buf, ubuf, buf_size)) {
+               ret = -EFAULT;
+               goto done;
+       }
+
+       sscanf(buf, "%u %x %x", &reg_type, &reg_offset, &reg_value);
+
+       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:
+       free_page(addr);
+       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
+mwifiex_regrdwr_read(struct file *file, char __user *ubuf,
+                    size_t count, loff_t *ppos)
+{
+       struct mwifiex_private *priv =
+               (struct mwifiex_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 = mwifiex_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 = mwifiex_reg_read(priv, saved_reg_type,
+                              saved_reg_offset, &reg_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;
+}
+
+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
+mwifiex_rdeeprom_write(struct file *file,
+                      const char __user *ubuf, size_t count, loff_t *ppos)
+{
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *) addr;
+       size_t buf_size = min(count, (size_t) (PAGE_SIZE - 1));
+       int ret = 0;
+       int offset = -1, bytes = -1;
+
+       if (!buf)
+               return -ENOMEM;
+
+
+       if (copy_from_user(buf, ubuf, buf_size)) {
+               ret = -EFAULT;
+               goto done;
+       }
+
+       sscanf(buf, "%d %d", &offset, &bytes);
+
+       if (offset == -1 || bytes == -1) {
+               ret = -EINVAL;
+               goto done;
+       } else {
+               saved_offset = offset;
+               saved_bytes = bytes;
+               ret = count;
+       }
+done:
+       free_page(addr);
+       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
+mwifiex_rdeeprom_read(struct file *file, char __user *ubuf,
+                     size_t count, loff_t *ppos)
+{
+       struct mwifiex_private *priv =
+               (struct mwifiex_private *) file->private_data;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *) addr;
+       int pos = 0, ret = 0, i = 0;
+       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 = mwifiex_eeprom_read(priv, (u16) saved_offset,
+                                 (u16) saved_bytes, value);
+       if (ret) {
+               ret = -EINVAL;
+               goto done;
+       }
+
+       pos += snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes);
+
+       for (i = 0; i < saved_bytes; i++)
+               pos += snprintf(buf + strlen(buf), PAGE_SIZE, "%d ", value[i]);
+
+       ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+done:
+       free_page(addr);
+       return ret;
+}
+
+
+#define MWIFIEX_DFS_ADD_FILE(name) do {                                 \
+       if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir,        \
+                       priv, &mwifiex_dfs_##name##_fops))              \
+               return;                                                 \
+} while (0);
+
+#define MWIFIEX_DFS_FILE_OPS(name)                                      \
+static const struct file_operations mwifiex_dfs_##name##_fops = {       \
+       .read = mwifiex_##name##_read,                                  \
+       .write = mwifiex_##name##_write,                                \
+       .open = mwifiex_open_generic,                                   \
+};
+
+#define MWIFIEX_DFS_FILE_READ_OPS(name)                                 \
+static const struct file_operations mwifiex_dfs_##name##_fops = {       \
+       .read = mwifiex_##name##_read,                                  \
+       .open = mwifiex_open_generic,                                   \
+};
+
+#define MWIFIEX_DFS_FILE_WRITE_OPS(name)                                \
+static const struct file_operations mwifiex_dfs_##name##_fops = {       \
+       .write = mwifiex_##name##_write,                                \
+       .open = mwifiex_open_generic,                                   \
+};
+
+
+MWIFIEX_DFS_FILE_READ_OPS(info);
+MWIFIEX_DFS_FILE_READ_OPS(debug);
+MWIFIEX_DFS_FILE_READ_OPS(getlog);
+MWIFIEX_DFS_FILE_OPS(regrdwr);
+MWIFIEX_DFS_FILE_OPS(rdeeprom);
+
+/*
+ * This function creates the debug FS directory structure and the files.
+ */
+void
+mwifiex_dev_debugfs_init(struct mwifiex_private *priv)
+{
+       if (!mwifiex_dfs_dir || !priv)
+               return;
+
+       priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name,
+                                              mwifiex_dfs_dir);
+
+       if (!priv->dfs_dev_dir)
+               return;
+
+       MWIFIEX_DFS_ADD_FILE(info);
+       MWIFIEX_DFS_ADD_FILE(debug);
+       MWIFIEX_DFS_ADD_FILE(getlog);
+       MWIFIEX_DFS_ADD_FILE(regrdwr);
+       MWIFIEX_DFS_ADD_FILE(rdeeprom);
+
+       return;
+}
+
+/*
+ * This function removes the debug FS directory structure and the files.
+ */
+void
+mwifiex_dev_debugfs_remove(struct mwifiex_private *priv)
+{
+       if (!priv)
+               return;
+
+       debugfs_remove_recursive(priv->dfs_dev_dir);
+       return;
+}
+
+/*
+ * This function creates the top level proc directory.
+ */
+void
+mwifiex_debugfs_init(void)
+{
+       if (!mwifiex_dfs_dir)
+               mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL);
+}
+
+/*
+ * This function removes the top level proc directory.
+ */
+void
+mwifiex_debugfs_remove(void)
+{
+       if (mwifiex_dfs_dir)
+               debugfs_remove(mwifiex_dfs_dir);
+}
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h
new file mode 100644 (file)
index 0000000..4e1f115
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Marvell Wireless LAN device driver: generic data structures and APIs
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#ifndef _MWIFIEX_DECL_H_
+#define _MWIFIEX_DECL_H_
+
+#undef pr_fmt
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/wait.h>
+#include <linux/timer.h>
+#include <linux/ieee80211.h>
+
+
+#define MWIFIEX_MAX_BSS_NUM         (1)
+
+#define MWIFIEX_MIN_DATA_HEADER_LEN 32 /* (sizeof(mwifiex_txpd)) */
+
+#define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED      2
+#define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED      16
+
+#define MWIFIEX_AMPDU_DEF_TXWINSIZE        32
+#define MWIFIEX_AMPDU_DEF_RXWINSIZE        16
+#define MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT  0xffff
+
+#define MWIFIEX_RATE_INDEX_HRDSSS0 0
+#define MWIFIEX_RATE_INDEX_HRDSSS3 3
+#define MWIFIEX_RATE_INDEX_OFDM0   4
+#define MWIFIEX_RATE_INDEX_OFDM7   11
+#define MWIFIEX_RATE_INDEX_MCS0    12
+
+#define MWIFIEX_RATE_BITMAP_OFDM0  16
+#define MWIFIEX_RATE_BITMAP_OFDM7  23
+#define MWIFIEX_RATE_BITMAP_MCS0   32
+#define MWIFIEX_RATE_BITMAP_MCS127 159
+
+#define MWIFIEX_RX_DATA_BUF_SIZE     (4 * 1024)
+#define MWIFIEX_RX_CMD_BUF_SIZE      (2 * 1024)
+
+#define MWIFIEX_RTS_MIN_VALUE              (0)
+#define MWIFIEX_RTS_MAX_VALUE              (2347)
+#define MWIFIEX_FRAG_MIN_VALUE             (256)
+#define MWIFIEX_FRAG_MAX_VALUE             (2346)
+
+#define MWIFIEX_SDIO_BLOCK_SIZE            256
+
+#define MWIFIEX_BUF_FLAG_REQUEUED_PKT      BIT(0)
+
+enum mwifiex_error_code {
+       MWIFIEX_ERROR_NO_ERROR = 0,
+       MWIFIEX_ERROR_FW_NOT_READY = 0x00000001,
+       MWIFIEX_ERROR_FW_BUSY,
+       MWIFIEX_ERROR_FW_CMDRESP,
+       MWIFIEX_ERROR_PKT_SIZE_INVALID = 0x80000001,
+       MWIFIEX_ERROR_PKT_TIMEOUT,
+       MWIFIEX_ERROR_CMD_INVALID,
+       MWIFIEX_ERROR_CMD_TIMEOUT,
+       MWIFIEX_ERROR_CMD_DNLD_FAIL,
+       MWIFIEX_ERROR_CMD_CANCEL,
+       MWIFIEX_ERROR_CMD_RESP_FAIL,
+       MWIFIEX_ERROR_ASSOC_FAIL,
+       MWIFIEX_ERROR_EVENT_UNKNOWN,
+       MWIFIEX_ERROR_INVALID_PARAMETER,
+};
+
+enum mwifiex_bss_type {
+       MWIFIEX_BSS_TYPE_STA = 0,
+       MWIFIEX_BSS_TYPE_UAP = 1,
+       MWIFIEX_BSS_TYPE_ANY = 0xff,
+};
+
+enum mwifiex_bss_role {
+       MWIFIEX_BSS_ROLE_STA = 0,
+       MWIFIEX_BSS_ROLE_UAP = 1,
+       MWIFIEX_BSS_ROLE_ANY = 0xff,
+};
+
+#define BSS_ROLE_BIT_MASK    BIT(0)
+
+#define GET_BSS_ROLE(priv)   ((priv)->bss_role & BSS_ROLE_BIT_MASK)
+
+enum mwifiex_data_frame_type {
+       MWIFIEX_DATA_FRAME_TYPE_ETH_II = 0,
+       MWIFIEX_DATA_FRAME_TYPE_802_11,
+};
+
+struct mwifiex_fw_image {
+       u8 *helper_buf;
+       u32 helper_len;
+       u8 *fw_buf;
+       u32 fw_len;
+};
+
+struct mwifiex_802_11_ssid {
+       u32 ssid_len;
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+};
+
+struct mwifiex_wait_queue {
+       u32 bss_index;
+       wait_queue_head_t *wait;
+       u16 *condition;
+       u32 start_time;
+       int status;
+       u32 enabled;
+};
+
+struct mwifiex_rxinfo {
+       u8 bss_index;
+       struct sk_buff *parent;
+       u8 use_count;
+};
+
+struct mwifiex_txinfo {
+       u32 status_code;
+       u8 flags;
+       u8 bss_index;
+};
+
+struct mwifiex_bss_attr {
+       u32 bss_type;
+       u32 frame_type;
+       u32 active;
+       u32 bss_priority;
+       u32 bss_num;
+};
+
+enum mwifiex_cmd_result_e {
+       MWIFIEX_CMD_RESULT_SUCCESS = 0,
+       MWIFIEX_CMD_RESULT_FAILURE = 1,
+       MWIFIEX_CMD_RESULT_TIMEOUT = 2,
+       MWIFIEX_CMD_RESULT_INVALID_DATA = 3
+} __packed;
+
+enum mwifiex_wmm_ac_e {
+       WMM_AC_BK,
+       WMM_AC_BE,
+       WMM_AC_VI,
+       WMM_AC_VO
+} __packed;
+
+enum mwifiex_wmm_queue_config_action_e {
+       MWIFIEX_WMM_QUEUE_CONFIG_ACTION_GET = 0,
+       MWIFIEX_WMM_QUEUE_CONFIG_ACTION_SET = 1,
+       MWIFIEX_WMM_QUEUE_CONFIG_ACTION_DEFAULT = 2,
+       MWIFIEX_WMM_QUEUE_CONFIG_ACTION_MAX
+} __packed;
+
+enum mwifiex_wmm_queue_stats_action_e {
+       MWIFIEX_WMM_STATS_ACTION_START = 0,
+       MWIFIEX_WMM_STATS_ACTION_STOP = 1,
+       MWIFIEX_WMM_STATS_ACTION_GET_CLR = 2,
+       MWIFIEX_WMM_STATS_ACTION_SET_CFG = 3,   /* Not currently used */
+       MWIFIEX_WMM_STATS_ACTION_GET_CFG = 4,   /* Not currently used */
+       MWIFIEX_WMM_STATS_ACTION_MAX
+} __packed;
+
+struct mwifiex_device {
+       struct mwifiex_bss_attr bss_attr[MWIFIEX_MAX_BSS_NUM];
+};
+#endif /* !_MWIFIEX_DECL_H_ */
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
new file mode 100644 (file)
index 0000000..e5dae45
--- /dev/null
@@ -0,0 +1,1376 @@
+/*
+ * Marvell Wireless LAN device driver: Firmware specific macros & structures
+ *
+ * Copyright (C) 2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#ifndef _MWIFIEX_FW_H_
+#define _MWIFIEX_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];
+       u16 snap_type;
+};
+
+struct rx_packet_hdr {
+       struct ethhdr eth803_hdr;
+       struct rfc_1042_hdr rfc1042_hdr;
+};
+
+struct tx_packet_hdr {
+       struct ethhdr eth803_hdr;
+       struct rfc_1042_hdr rfc1042_hdr;
+};
+
+#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)
+
+#define FW_MULTI_BANDS_SUPPORT  (BIT(8) | BIT(9) | BIT(10) | BIT(11))
+#define IS_SUPPORT_MULTI_BANDS(adapter)        \
+       (adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT)
+#define GET_FW_DEFAULT_BANDS(adapter)  \
+       ((adapter->fw_cap_info >> 8) & ALL_802_11_BANDS)
+
+#define SHORT_SLOT_TIME_DISABLED(CapInfo) (CapInfo &= ~BIT(10))
+#define SHORT_SLOT_TIME_ENABLED(CapInfo)  (CapInfo |= BIT(10))
+
+extern u8 supported_rates_b[B_SUPPORTED_RATES];
+extern u8 supported_rates_g[G_SUPPORTED_RATES];
+extern u8 supported_rates_bg[BG_SUPPORTED_RATES];
+extern u8 supported_rates_a[A_SUPPORTED_RATES];
+extern u8 supported_rates_n[N_SUPPORTED_RATES];
+
+#define HostCmd_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,
+};
+
+enum KEY_INFO_WEP {
+       KEY_INFO_WEP_MCAST = 0x01,
+       KEY_INFO_WEP_UNICAST = 0x02,
+       KEY_INFO_WEP_ENABLED = 0x04
+};
+
+enum KEY_INFO_TKIP {
+       KEY_INFO_TKIP_MCAST = 0x01,
+       KEY_INFO_TKIP_UNICAST = 0x02,
+       KEY_INFO_TKIP_ENABLED = 0x04
+};
+
+enum KEY_INFO_AES {
+       KEY_INFO_AES_MCAST = 0x01,
+       KEY_INFO_AES_UNICAST = 0x02,
+       KEY_INFO_AES_ENABLED = 0x04
+};
+
+#define WAPI_KEY_LEN                   50
+
+enum KEY_INFO_WAPI {
+       KEY_INFO_WAPI_MCAST = 0x01,
+       KEY_INFO_WAPI_UNICAST = 0x02,
+       KEY_INFO_WAPI_ENABLED = 0x04
+};
+
+#define MAX_POLL_TRIES                 100
+
+#define MAX_MULTI_INTERFACE_POLL_TRIES  1000
+
+#define MAX_FIRMWARE_POLL_TRIES                        100
+
+#define FIRMWARE_READY                         0xfedc
+
+#define FIRMWARE_TRANSFER_NBLOCK               2
+
+enum MWIFIEX_802_11_PRIVACY_FILTER {
+       MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL,
+       MWIFIEX_802_11_PRIV_FILTER_8021X_WEP
+};
+
+enum MWIFIEX_802_11_WEP_STATUS {
+       MWIFIEX_802_11_WEP_ENABLED,
+       MWIFIEX_802_11_WEP_DISABLED,
+};
+
+#define CAL_SNR(RSSI, NF)              ((s16)((s16)(RSSI)-(s16)(NF)))
+
+#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_SNR_LOW            (PROPRIETARY_TLV_BASE_ID + 5)
+#define TLV_TYPE_FAILCOUNT          (PROPRIETARY_TLV_BASE_ID + 6)
+#define TLV_TYPE_BCNMISS            (PROPRIETARY_TLV_BASE_ID + 7)
+#define TLV_TYPE_LEDBEHAVIOR        (PROPRIETARY_TLV_BASE_ID + 9)
+#define TLV_TYPE_PASSTHROUGH        (PROPRIETARY_TLV_BASE_ID + 10)
+#define TLV_TYPE_POWER_TBL_2_4GHZ   (PROPRIETARY_TLV_BASE_ID + 12)
+#define TLV_TYPE_POWER_TBL_5GHZ     (PROPRIETARY_TLV_BASE_ID + 13)
+#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_SNR_HIGH           (PROPRIETARY_TLV_BASE_ID + 23)
+
+#define TLV_TYPE_STARTBGSCANLATER   (PROPRIETARY_TLV_BASE_ID + 30)
+#define TLV_TYPE_AUTH_TYPE          (PROPRIETARY_TLV_BASE_ID + 31)
+#define TLV_TYPE_LINK_QUALITY       (PROPRIETARY_TLV_BASE_ID + 36)
+#define TLV_TYPE_RSSI_LOW_DATA      (PROPRIETARY_TLV_BASE_ID + 38)
+#define TLV_TYPE_SNR_LOW_DATA       (PROPRIETARY_TLV_BASE_ID + 39)
+#define TLV_TYPE_RSSI_HIGH_DATA     (PROPRIETARY_TLV_BASE_ID + 40)
+#define TLV_TYPE_SNR_HIGH_DATA      (PROPRIETARY_TLV_BASE_ID + 41)
+
+#define TLV_TYPE_CHANNELBANDLIST    (PROPRIETARY_TLV_BASE_ID + 42)
+#define TLV_TYPE_WAPI_IE            (PROPRIETARY_TLV_BASE_ID + 94)
+#define TLV_TYPE_BSSID              (PROPRIETARY_TLV_BASE_ID + 35)
+
+#define MWIFIEX_TX_DATA_BUF_SIZE_2K        2048
+
+#define TLV_TYPE_HT_CAP                  (PROPRIETARY_TLV_BASE_ID + 74)
+#define TLV_TYPE_HT_INFO                 (PROPRIETARY_TLV_BASE_ID + 75)
+#define TLV_SECONDARY_CHANNEL_OFFSET     (PROPRIETARY_TLV_BASE_ID + 76)
+#define TLV_TYPE_2040BSS_COEXISTENCE     (PROPRIETARY_TLV_BASE_ID + 77)
+#define TLV_TYPE_OVERLAP_BSS_SCAN_PARAM  (PROPRIETARY_TLV_BASE_ID + 78)
+#define TLV_TYPE_EXTCAP                  (PROPRIETARY_TLV_BASE_ID + 79)
+#define TLV_TYPE_HT_OPERATIONAL_MCS_SET  (PROPRIETARY_TLV_BASE_ID + 80)
+
+#define ADDBA_TID_MASK   (BIT(2) | BIT(3) | BIT(4) | BIT(5))
+#define DELBA_TID_MASK   (BIT(12) | BIT(13) | BIT(14) | BIT(15))
+#define SSN_MASK         0xfff0
+
+#define BA_RESULT_SUCCESS        0x0
+#define BA_RESULT_FAILURE        0x1
+#define BA_RESULT_TIMEOUT        0x2
+#define BA_RESULT_DATA_INVALID   0x3
+
+#define IS_BASTREAM_SETUP(ptr)  (ptr->ba_status)
+
+#define BA_STREAM_NOT_ALLOWED   0xff
+
+#define IS_11N_ENABLED(priv) ((priv->adapter->config_bands & BAND_GN || \
+                       priv->adapter->config_bands & BAND_AN) \
+                       && priv->curr_bss_params.bss_descriptor.bcn_ht_cap)
+#define INITIATOR_BIT(DelBAParamSet) (((DelBAParamSet) &\
+                       BIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS)
+
+#define MWIFIEX_TX_DATA_BUF_SIZE_4K        4096
+#define MWIFIEX_TX_DATA_BUF_SIZE_8K        8192
+#define MAX_RX_AMPDU_SIZE_64K   0x03
+#define NON_GREENFIELD_STAS     0x04
+
+#define HWSPEC_GREENFIELD_SUPP  BIT(29)
+#define HWSPEC_RXSTBC_SUPP      BIT(26)
+#define HWSPEC_SHORTGI40_SUPP   BIT(24)
+#define HWSPEC_SHORTGI20_SUPP   BIT(23)
+#define HWSPEC_CHANBW40_SUPP    BIT(17)
+
+#define DEFAULT_11N_CAP_MASK   (HWSPEC_SHORTGI20_SUPP | HWSPEC_RXSTBC_SUPP)
+#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11))
+#define ISSUPP_MAXAMSDU(Dot11nDevCap) (Dot11nDevCap & BIT(31))
+#define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30))
+#define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29))
+#define ISSUPP_AMPDU(Dot11nDevCap) (Dot11nDevCap & BIT(28))
+#define ISSUPP_MIMOPS(Dot11nDevCap) (Dot11nDevCap & BIT(27))
+#define ISSUPP_RXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(26))
+#define ISSUPP_TXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(25))
+#define ISSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap & BIT(24))
+#define ISSUPP_SHORTGI20(Dot11nDevCap) (Dot11nDevCap & BIT(23))
+#define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22))
+#define GET_DELAYEDBACK(Dot11nDevCap) (((Dot11nDevCap >> 20) & 0x03))
+#define GET_IMMEDIATEBACK(Dot11nDevCap) (((Dot11nDevCap >> 18) & 0x03))
+#define ISSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap & BIT(17))
+#define ISSUPP_CHANWIDTH20(Dot11nDevCap) (Dot11nDevCap & BIT(16))
+#define ISSUPP_CHANWIDTH10(Dot11nDevCap) (Dot11nDevCap & BIT(15))
+#define ISENABLED_40MHZ_INTOLARENT(Dot11nDevCap) (Dot11nDevCap & BIT(8))
+#define ISSUPP_RXANTENNAD(Dot11nDevCap) (Dot11nDevCap & BIT(7))
+#define ISSUPP_RXANTENNAC(Dot11nDevCap) (Dot11nDevCap & BIT(6))
+#define ISSUPP_RXANTENNAB(Dot11nDevCap) (Dot11nDevCap & BIT(5))
+#define ISSUPP_RXANTENNAA(Dot11nDevCap) (Dot11nDevCap & BIT(4))
+#define ISSUPP_TXANTENNAD(Dot11nDevCap) (Dot11nDevCap & BIT(3))
+#define ISSUPP_TXANTENNAC(Dot11nDevCap) (Dot11nDevCap & BIT(2))
+#define ISSUPP_TXANTENNAB(Dot11nDevCap) (Dot11nDevCap & BIT(1))
+#define ISSUPP_TXANTENNAA(Dot11nDevCap) (Dot11nDevCap & BIT(0))
+#define SETSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap |= BIT(17))
+#define RESETSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap &= ~BIT(17))
+#define GET_TXMCSSUPP(DevMCSSupported) (DevMCSSupported >> 4)
+#define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f)
+#define GETHT_SUPPCHANWIDTH(HTCapInfo) (HTCapInfo & BIT(1))
+#define GETHT_GREENFIELD(HTCapInfo) (HTCapInfo & BIT(4))
+#define GETHT_SHORTGI20(HTCapInfo) (HTCapInfo & BIT(5))
+#define GETHT_SHORTGI40(HTCapInfo) (HTCapInfo & BIT(6))
+#define GETHT_TXSTBC(HTCapInfo) (HTCapInfo & BIT(7))
+#define GETHT_RXSTBC(HTCapInfo) ((HTCapInfo >> 8) & 0x03)
+#define GETHT_DELAYEDBACK(HTCapInfo) (HTCapInfo & BIT(10))
+#define GETHT_MAXAMSDU(HTCapInfo) (HTCapInfo & BIT(11))
+#define SETHT_SUPPCHANWIDTH(HTCapInfo) (HTCapInfo |= BIT(1))
+#define SETHT_GREENFIELD(HTCapInfo) (HTCapInfo |= BIT(4))
+#define SETHT_SHORTGI20(HTCapInfo) (HTCapInfo |= BIT(5))
+#define SETHT_SHORTGI40(HTCapInfo) (HTCapInfo |= BIT(6))
+#define SETHT_TXSTBC(HTCapInfo) (HTCapInfo |= BIT(7))
+#define SETHT_RXSTBC(HTCapInfo, value) (HTCapInfo |= (value << 8))
+#define SETHT_DELAYEDBACK(HTCapInfo) (HTCapInfo |= BIT(10))
+#define SETHT_MAXAMSDU(HTCapInfo) (HTCapInfo |= BIT(11))
+#define SETHT_DSSSCCK40(HTCapInfo) (HTCapInfo |= BIT(12))
+#define SETHT_40MHZ_INTOLARANT(HTCapInfo) (HTCapInfo |= BIT(14))
+#define RESETHT_SUPPCHANWIDTH(HTCapInfo) (HTCapInfo &= ~BIT(1))
+#define RESETHT_GREENFIELD(HTCapInfo) (HTCapInfo &= ~BIT(4))
+#define RESETHT_SHORTGI20(HTCapInfo) (HTCapInfo &= ~BIT(5))
+#define RESETHT_SHORTGI40(HTCapInfo) (HTCapInfo &= ~BIT(6))
+#define RESETHT_TXSTBC(HTCapInfo) (HTCapInfo &= ~BIT(7))
+#define RESETHT_RXSTBC(HTCapInfo) (HTCapInfo &= ~(0x03 << 8))
+#define RESETHT_DELAYEDBACK(HTCapInfo) (HTCapInfo &= ~BIT(10))
+#define RESETHT_MAXAMSDU(HTCapInfo) (HTCapInfo &= ~BIT(11))
+#define RESETHT_40MHZ_INTOLARANT(HTCapInfo) (HTCapInfo &= ~BIT(14))
+#define RESETHT_EXTCAP_RDG(HTExtCap) (HTExtCap &= ~BIT(11))
+#define SETHT_MCS32(x) (x[4] |= 1)
+#define SETHT_MCS_SET_DEFINED(x) (x[12] |= 1)
+#define SETHT_RX_HIGHEST_DT_SUPP(x, y) ((*(u16 *) (x + 10)) = y)
+#define AMPDU_FACTOR_64K       0x03
+#define SETAMPDU_SIZE(x, y) do { \
+       x = x & ~0x03; \
+       x |= y & 0x03; \
+} while (0) \
+
+#define SETAMPDU_SPACING(x, y) do { \
+       x = x & ~0x1c; \
+       x |= (y & 0x07) << 2; \
+} while (0) \
+
+#define ISSUPP_BANDA(FwCapInfo) (FwCapInfo & BIT(10))
+#define ISALLOWED_CHANWIDTH40(Field2) (Field2 & BIT(2))
+#define SET_CHANWIDTH40(Field2) (Field2 |= BIT(2))
+#define RESET_CHANWIDTH40(Field2) (Field2 &= ~(BIT(0) | BIT(1) | BIT(2)))
+#define GET_SECONDARYCHAN(Field2) (Field2 & (BIT(0) | BIT(1)))
+#define SET_SECONDARYCHAN(RadioType, SECCHAN) (RadioType |= (SECCHAN << 4))
+
+#define LLC_SNAP_LEN    8
+
+#define TLV_TYPE_RATE_DROP_PATTERN  (PROPRIETARY_TLV_BASE_ID + 81)
+#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 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 HostCmd_CMD_GET_HW_SPEC                       0x0003
+#define HostCmd_CMD_802_11_SCAN                       0x0006
+#define HostCmd_CMD_802_11_GET_LOG                    0x000b
+#define HostCmd_CMD_MAC_MULTICAST_ADR                 0x0010
+#define HostCmd_CMD_802_11_EEPROM_ACCESS              0x0059
+#define HostCmd_CMD_802_11_ASSOCIATE                  0x0012
+#define HostCmd_CMD_802_11_SNMP_MIB                   0x0016
+#define HostCmd_CMD_MAC_REG_ACCESS                    0x0019
+#define HostCmd_CMD_BBP_REG_ACCESS                    0x001a
+#define HostCmd_CMD_RF_REG_ACCESS                     0x001b
+#define HostCmd_CMD_PMIC_REG_ACCESS                   0x00ad
+#define HostCmd_CMD_802_11_RF_CHANNEL                 0x001d
+#define HostCmd_CMD_802_11_DEAUTHENTICATE             0x0024
+#define HostCmd_CMD_MAC_CONTROL                       0x0028
+#define HostCmd_CMD_802_11_AD_HOC_START               0x002b
+#define HostCmd_CMD_802_11_AD_HOC_JOIN                0x002c
+#define HostCmd_CMD_802_11_AD_HOC_STOP                0x0040
+#define HostCmd_CMD_802_11_MAC_ADDRESS                0x004D
+#define HostCmd_CMD_802_11D_DOMAIN_INFO               0x005b
+#define HostCmd_CMD_802_11_KEY_MATERIAL               0x005e
+#define HostCmd_CMD_802_11_BG_SCAN_QUERY              0x006c
+#define HostCmd_CMD_WMM_GET_STATUS                    0x0071
+#define HostCmd_CMD_802_11_TX_RATE_QUERY              0x007f
+#define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS     0x0083
+#define HostCmd_CMD_VERSION_EXT                       0x0097
+#define HostCmd_CMD_RSSI_INFO                         0x00a4
+#define HostCmd_CMD_FUNC_INIT                         0x00a9
+#define HostCmd_CMD_FUNC_SHUTDOWN                     0x00aa
+#define HostCmd_CMD_11N_CFG                           0x00cd
+#define HostCmd_CMD_11N_ADDBA_REQ                     0x00ce
+#define HostCmd_CMD_11N_ADDBA_RSP                     0x00cf
+#define HostCmd_CMD_11N_DELBA                         0x00d0
+#define HostCmd_CMD_RECONFIGURE_TX_BUFF               0x00d9
+#define HostCmd_CMD_AMSDU_AGGR_CTRL                   0x00df
+#define HostCmd_CMD_TXPWR_CFG                         0x00d1
+#define HostCmd_CMD_TX_RATE_CFG                       0x00d6
+#define HostCmd_CMD_802_11_PS_MODE_ENH                0x00e4
+#define HostCmd_CMD_802_11_HS_CFG_ENH                 0x00e5
+#define HostCmd_CMD_CAU_REG_ACCESS                    0x00ed
+#define HostCmd_CMD_SET_BSS_MODE                      0x00f7
+
+
+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,
+};
+
+#define HostCmd_RET_BIT                       0x8000
+#define HostCmd_ACT_GEN_GET                   0x0000
+#define HostCmd_ACT_GEN_SET                   0x0001
+#define HostCmd_ACT_GEN_REMOVE                0x0004
+#define HostCmd_ACT_SET_BOTH                  0x0003
+#define HostCmd_ACT_GET_BOTH                  0x000c
+#define HostCmd_RESULT_OK                     0x0000
+#define HostCmd_RESULT_ERROR                  0x0001
+#define HostCmd_RESULT_NOT_SUPPORT            0x0002
+#define HostCmd_RESULT_PENDING                0x0003
+#define HostCmd_RESULT_BUSY                   0x0004
+#define HostCmd_RESULT_PARTIAL_DATA           0x0005
+
+#define HostCmd_ACT_MAC_RX_ON                 0x0001
+#define HostCmd_ACT_MAC_TX_ON                 0x0002
+#define HostCmd_ACT_MAC_WEP_ENABLE            0x0008
+#define HostCmd_ACT_MAC_ETHERNETII_ENABLE     0x0010
+#define HostCmd_ACT_MAC_PROMISCUOUS_ENABLE    0x0080
+#define HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE  0x0100
+#define HostCmd_ACT_MAC_RTS_CTS_ENABLE        0x0200
+#define HostCmd_ACT_MAC_STRICT_PROTECTION_ENABLE  0x0400
+#define HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON     0x2000
+
+#define HostCmd_BSS_MODE_BSS                0x0001
+#define HostCmd_BSS_MODE_IBSS               0x0002
+#define HostCmd_BSS_MODE_ANY                0x0003
+
+#define HostCmd_SCAN_RADIO_TYPE_BG          0
+#define HostCmd_SCAN_RADIO_TYPE_A           1
+
+#define HOST_SLEEP_CFG_CANCEL          0xffffffff
+#define HOST_SLEEP_CFG_COND_DEF                0x0000000f
+#define HOST_SLEEP_CFG_GPIO_DEF                0xff
+#define HOST_SLEEP_CFG_GAP_DEF         0
+
+#define CMD_F_HOSTCMD           (1 << 0)
+#define CMD_F_CANCELED          (1 << 1)
+
+#define HostCmd_CMD_ID_MASK             0x0fff
+
+#define HostCmd_SEQ_NUM_MASK            0x00ff
+
+#define HostCmd_BSS_NUM_MASK            0x0f00
+
+#define HostCmd_BSS_TYPE_MASK           0xf000
+
+#define HostCmd_SET_SEQ_NO_BSS_INFO(seq, num, type) {   \
+       (((seq) & 0x00ff) |                             \
+        (((num) & 0x000f) << 8)) |                     \
+       (((type) & 0x000f) << 12);                  }
+
+#define HostCmd_GET_SEQ_NO(seq)       \
+       ((seq) & HostCmd_SEQ_NUM_MASK)
+
+#define HostCmd_GET_BSS_NO(seq)         \
+       (((seq) & HostCmd_BSS_NUM_MASK) >> 8)
+
+#define HostCmd_GET_BSS_TYPE(seq)       \
+       (((seq) & HostCmd_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_ADHOC_BCN_LOST            0x00000011
+
+#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_IBSS_COALESCED            0x0000001e
+#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_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_WEP_ICV_ERR               0x00000046
+#define EVENT_HS_ACT_REQ                0x00000047
+#define EVENT_BW_CHANGE                 0x00000048
+
+#define EVENT_HOSTWAKE_STAIE           0x0000004d
+
+#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)
+
+struct mwifiex_event_wep_icv_err {
+       u16 reason_code;
+       u8 src_mac_addr[ETH_ALEN];
+       u8 wep_key_index;
+       u8 wep_key_length;
+       u8 key[WLAN_KEY_LEN_WEP104];
+};
+
+struct mwifiex_802_11_fixed_ies {
+       u8 time_stamp[8];
+       __le16 beacon_interval;
+       __le16 capabilities;
+};
+
+struct mwifiex_ie_types_header {
+       __le16 type;
+       __le16 len;
+} __packed;
+
+struct mwifiex_ie_types_data {
+       struct mwifiex_ie_types_header header;
+       u8 data[1];
+} __packed;
+
+#define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01
+#define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08
+
+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;
+} __packed;
+
+struct rxpd {
+       u8 bss_type;
+       u8 bss_num;
+       u16 rx_pkt_length;
+       u16 rx_pkt_offset;
+       u16 rx_pkt_type;
+       u16 seq_num;
+       u8 priority;
+       u8 rx_rate;
+       s8 snr;
+       s8 nf;
+       /* 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;
+       u8 reserved;
+} __packed;
+
+enum mwifiex_chan_scan_mode_bitmasks {
+       MWIFIEX_PASSIVE_SCAN = BIT(0),
+       MWIFIEX_DISABLE_CHAN_FILT = BIT(1),
+};
+
+#define SECOND_CHANNEL_BELOW    0x30
+#define SECOND_CHANNEL_ABOVE    0x10
+struct mwifiex_chan_scan_param_set {
+       u8 radio_type;
+       u8 chan_number;
+       u8 chan_scan_mode_bitmap;
+       __le16 min_scan_time;
+       __le16 max_scan_time;
+} __packed;
+
+struct mwifiex_ie_types_chan_list_param_set {
+       struct mwifiex_ie_types_header header;
+       struct mwifiex_chan_scan_param_set chan_scan_param[1];
+} __packed;
+
+struct chan_band_param_set {
+       u8 radio_type;
+       u8 chan_number;
+};
+
+struct mwifiex_ie_types_chan_band_list_param_set {
+       struct mwifiex_ie_types_header header;
+       struct chan_band_param_set chan_band_param[1];
+} __packed;
+
+struct mwifiex_ie_types_rates_param_set {
+       struct mwifiex_ie_types_header header;
+       u8 rates[1];
+} __packed;
+
+struct mwifiex_ie_types_ssid_param_set {
+       struct mwifiex_ie_types_header header;
+       u8 ssid[1];
+} __packed;
+
+struct mwifiex_ie_types_num_probes {
+       struct mwifiex_ie_types_header header;
+       __le16 num_probes;
+} __packed;
+
+struct mwifiex_ie_types_wildcard_ssid_params {
+       struct mwifiex_ie_types_header header;
+       u8 max_ssid_length;
+       u8 ssid[1];
+} __packed;
+
+#define TSF_DATA_SIZE            8
+struct mwifiex_ie_types_tsf_timestamp {
+       struct mwifiex_ie_types_header header;
+       u8 tsf_data[1];
+} __packed;
+
+struct mwifiex_cf_param_set {
+       u8 cfp_cnt;
+       u8 cfp_period;
+       u16 cfp_max_duration;
+       u16 cfp_duration_remaining;
+} __packed;
+
+struct mwifiex_ibss_param_set {
+       u16 atim_window;
+} __packed;
+
+struct mwifiex_ie_types_ss_param_set {
+       struct mwifiex_ie_types_header header;
+       union {
+               struct mwifiex_cf_param_set cf_param_set[1];
+               struct mwifiex_ibss_param_set ibss_param_set[1];
+       } cf_ibss;
+} __packed;
+
+struct mwifiex_fh_param_set {
+       u16 dwell_time;
+       u8 hop_set;
+       u8 hop_pattern;
+       u8 hop_index;
+} __packed;
+
+struct mwifiex_ds_param_set {
+       u8 current_chan;
+} __packed;
+
+struct mwifiex_ie_types_phy_param_set {
+       struct mwifiex_ie_types_header header;
+       union {
+               struct mwifiex_fh_param_set fh_param_set[1];
+               struct mwifiex_ds_param_set ds_param_set[1];
+       } fh_ds;
+} __packed;
+
+struct mwifiex_ie_types_auth_type {
+       struct mwifiex_ie_types_header header;
+       __le16 auth_type;
+} __packed;
+
+struct mwifiex_ie_types_vendor_param_set {
+       struct mwifiex_ie_types_header header;
+       u8 ie[MWIFIEX_MAX_VSIE_LEN];
+};
+
+struct mwifiex_ie_types_rsn_param_set {
+       struct mwifiex_ie_types_header header;
+       u8 rsn_ie[1];
+} __packed;
+
+#define KEYPARAMSET_FIXED_LEN 6
+
+struct mwifiex_ie_type_key_param_set {
+       __le16 type;
+       __le16 length;
+       __le16 key_type_id;
+       __le16 key_info;
+       __le16 key_len;
+       u8 key[50];
+} __packed;
+
+struct host_cmd_ds_802_11_key_material {
+       __le16 action;
+       struct mwifiex_ie_type_key_param_set key_param_set;
+} __packed;
+
+struct host_cmd_ds_gen {
+       u16 command;
+       u16 size;
+       u16 seq_num;
+       u16 result;
+};
+
+#define S_DS_GEN        sizeof(struct host_cmd_ds_gen)
+
+enum sleep_resp_ctrl {
+       RESP_NOT_NEEDED = 0,
+       RESP_NEEDED,
+};
+
+struct mwifiex_ps_param {
+       __le16 null_pkt_interval;
+       __le16 multiple_dtims;
+       __le16 bcn_miss_timeout;
+       __le16 local_listen_interval;
+       __le16 adhoc_wake_period;
+       __le16 mode;
+       __le16 delay_to_ps;
+};
+
+struct mwifiex_auto_ds_param {
+       __le16 deep_sleep_timeout;
+};
+
+struct sleep_confirm_param {
+       __le16 resp_ctrl;
+};
+
+#define BITMAP_AUTO_DS         0x01
+#define BITMAP_STA_PS          0x10
+#define BITMAP_UAP_INACT_PS    0x100
+#define BITMAP_UAP_DTIM_PS     0x200
+struct auto_ps_param {
+       __le16 ps_bitmap;
+       /* auto deep sleep parameter,
+        * sta power save parameter
+        * uap inactivity parameter
+        * uap DTIM parameter */
+};
+
+#define AUTO_PS_FIX_SIZE    4
+
+#define TLV_TYPE_AUTO_DS_PARAM        (PROPRIETARY_TLV_BASE_ID + 113)
+#define TLV_TYPE_PS_PARAM             (PROPRIETARY_TLV_BASE_ID + 114)
+
+struct mwifiex_ie_types_auto_ds_param {
+       struct mwifiex_ie_types_header header;
+       struct mwifiex_auto_ds_param param;
+} __packed;
+
+struct mwifiex_ie_types_ps_param {
+       struct mwifiex_ie_types_header header;
+       struct mwifiex_ps_param param;
+} __packed;
+
+struct host_cmd_ds_802_11_ps_mode_enh {
+       __le16 action;
+
+       union {
+               struct mwifiex_ps_param opt_ps;
+               struct mwifiex_auto_ds_param auto_ds;
+               struct sleep_confirm_param sleep_cfm;
+               __le16 ps_bitmap;
+               struct auto_ps_param auto_ps;
+       } params;
+} __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 reserved_4;
+} __packed;
+
+struct host_cmd_ds_802_11_rssi_info {
+       __le16 action;
+       __le16 ndata;
+       __le16 nbcn;
+       __le16 reserved[9];
+       long long reserved_1;
+};
+
+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;
+};
+
+struct host_cmd_ds_802_11_mac_address {
+       __le16 action;
+       u8 mac_addr[ETH_ALEN];
+};
+
+struct host_cmd_ds_mac_control {
+       __le16 action;
+       __le16 reserved;
+};
+
+struct host_cmd_ds_mac_multicast_adr {
+       __le16 action;
+       __le16 num_of_adrs;
+       u8 mac_list[MWIFIEX_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[1];
+} __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;
+       u16 cfp_max_duration;
+       u16 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 host_cmd_ds_802_11_ad_hoc_start {
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+       u8 bss_mode;
+       __le16 beacon_period;
+       u8 dtim_period;
+       union ieee_types_ss_param_set ss_param_set;
+       union ieee_types_phy_param_set phy_param_set;
+       u16 reserved1;
+       __le16 cap_info_bitmap;
+       u8 DataRate[HOSTCMD_SUPPORTED_RATES];
+} __packed;
+
+struct host_cmd_ds_802_11_ad_hoc_result {
+       u8 pad[3];
+       u8 bssid[ETH_ALEN];
+} __packed;
+
+struct adhoc_bss_desc {
+       u8 bssid[ETH_ALEN];
+       u8 ssid[IEEE80211_MAX_SSID_LEN];
+       u8 bss_mode;
+       __le16 beacon_period;
+       u8 dtim_period;
+       u8 time_stamp[8];
+       u8 local_time[8];
+       union ieee_types_phy_param_set phy_param_set;
+       union ieee_types_ss_param_set ss_param_set;
+       __le16 cap_info_bitmap;
+       u8 data_rates[HOSTCMD_SUPPORTED_RATES];
+
+       /*
+        *  DO NOT ADD ANY FIELDS TO THIS STRUCTURE.
+        *  It is used in the Adhoc join command and will cause a
+        *  binary layout mismatch with the firmware
+        */
+} __packed;
+
+struct host_cmd_ds_802_11_ad_hoc_join {
+       struct adhoc_bss_desc bss_descriptor;
+       u16 reserved1;
+       u16 reserved2;
+} __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];
+};
+
+struct host_cmd_ds_tx_rate_query {
+       u8 tx_rate;
+       /* 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;
+
+enum Host_Sleep_Action {
+       HS_CONFIGURE = 0x0001,
+       HS_ACTIVATE  = 0x0002,
+};
+
+struct mwifiex_hs_config_param {
+       __le32 conditions;
+       u8 gpio;
+       u8 gap;
+} __packed;
+
+struct hs_activate_param {
+       u16 resp_ctrl;
+} __packed;
+
+struct host_cmd_ds_802_11_hs_cfg_enh {
+       __le16 action;
+
+       union {
+               struct mwifiex_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,
+};
+
+#define MAX_SNMP_BUF_SIZE   128
+
+struct host_cmd_ds_802_11_snmp_mib {
+       __le16 query_type;
+       __le16 oid;
+       __le16 buf_size;
+       u8 value[1];
+} __packed;
+
+#define RADIO_ON                                0x01
+#define RADIO_OFF                               0x00
+
+struct mwifiex_rate_scope {
+       __le16 type;
+       __le16 length;
+       __le16 hr_dsss_rate_bitmap;
+       __le16 ofdm_rate_bitmap;
+       __le16 ht_mcs_rate_bitmap[8];
+} __packed;
+
+struct mwifiex_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 mwifiex_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 mwifiex_types_power_group {
+       u16 type;
+       u16 length;
+} __packed;
+
+struct host_cmd_ds_txpwr_cfg {
+       __le16 action;
+       __le16 cfg_index;
+       __le32 mode;
+} __packed;
+
+#define MWIFIEX_USER_SCAN_CHAN_MAX             50
+
+#define MWIFIEX_MAX_SSID_LIST_LENGTH         10
+
+struct mwifiex_scan_cmd_config {
+       /*
+        *  BSS Type to be sent in the firmware command
+        *
+        *  Field can be used to restrict the types of networks returned in the
+        *    scan.  Valid settings are:
+        *
+        *   - MWIFIEX_SCAN_MODE_BSS  (infrastructure)
+        *   - MWIFIEX_SCAN_MODE_IBSS (adhoc)
+        *   - MWIFIEX_SCAN_MODE_ANY  (unrestricted, adhoc and infrastructure)
+        */
+       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, mwifiex_ie_types_chan_list_param_set
+        *  WLAN_EID_SSID, mwifiex_ie_types_ssid_param_set
+        */
+       u8 tlv_buf[1];  /* SSID TLV(s) and ChanList TLVs are stored
+                                  here */
+} __packed;
+
+struct mwifiex_user_scan_chan {
+       u8 chan_number;
+       u8 radio_type;
+       u8 scan_type;
+       u8 reserved;
+       u32 scan_time;
+} __packed;
+
+struct mwifiex_user_scan_ssid {
+       u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
+       u8 max_len;
+} __packed;
+
+struct mwifiex_user_scan_cfg {
+       /*
+        *  Flag set to keep the previous scan table intact
+        *
+        *  If set, the scan results will accumulate, replacing any previous
+        *   matched entries for a BSS with the new scan data
+        */
+       u8 keep_previous_scan;
+       /*
+        *  BSS mode to be sent in the firmware command
+        *
+        *  Field can be used to restrict the types of networks returned in the
+        *    scan.  Valid settings are:
+        *
+        *   - MWIFIEX_SCAN_MODE_BSS  (infrastructure)
+        *   - MWIFIEX_SCAN_MODE_IBSS (adhoc)
+        *   - MWIFIEX_SCAN_MODE_ANY  (unrestricted, adhoc and infrastructure)
+        */
+       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 to limit the scan results */
+       struct mwifiex_user_scan_ssid ssid_list[MWIFIEX_MAX_SSID_LIST_LENGTH];
+       /* Variable number (fixed maximum) of channels to scan up */
+       struct mwifiex_user_scan_chan chan_list[MWIFIEX_USER_SCAN_CHAN_MAX];
+} __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[1];
+} __packed;
+
+struct host_cmd_ds_802_11_scan_rsp {
+       __le16 bss_descript_size;
+       u8 number_of_sets;
+       u8 bss_desc_and_tlv_buffer[1];
+} __packed;
+
+struct host_cmd_ds_802_11_bg_scan_query {
+       u8 flush;
+} __packed;
+
+struct host_cmd_ds_802_11_bg_scan_query_rsp {
+       u32 report_condition;
+       struct host_cmd_ds_802_11_scan_rsp scan_resp;
+} __packed;
+
+struct mwifiex_ietypes_domain_param_set {
+       struct mwifiex_ie_types_header header;
+       u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
+       struct ieee80211_country_ie_triplet triplet[1];
+} __packed;
+
+struct host_cmd_ds_802_11d_domain_info {
+       __le16 action;
+       struct mwifiex_ietypes_domain_param_set domain;
+} __packed;
+
+struct host_cmd_ds_802_11d_domain_info_rsp {
+       __le16 action;
+       struct mwifiex_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;
+} __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 mwifiex_ie_types_wmm_param_set {
+       struct mwifiex_ie_types_header header;
+       u8 wmm_ie[1];
+};
+
+struct mwifiex_ie_types_wmm_queue_status {
+       struct mwifiex_ie_types_header header;
+       u8 queue_index;
+       u8 disabled;
+       u16 medium_time;
+       u8 flow_required;
+       u8 flow_created;
+       u32 reserved;
+};
+
+struct ieee_types_vendor_header {
+       u8 element_id;
+       u8 len;
+       u8 oui[3];
+       u8 oui_type;
+       u8 oui_subtype;
+       u8 version;
+} __packed;
+
+struct ieee_types_wmm_ac_parameters {
+       u8 aci_aifsn_bitmap;
+       u8 ecw_bitmap;
+       __le16 tx_op_limit;
+} __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 qos_info_bitmap;
+       u8 reserved;
+       struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_MAX_QUEUES];
+} __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 qos_info_bitmap;
+} __packed;
+
+struct host_cmd_ds_wmm_get_status {
+       u8 queue_status_tlv[sizeof(struct mwifiex_ie_types_wmm_queue_status) *
+                             IEEE80211_MAX_QUEUES];
+       u8 wmm_param_tlv[sizeof(struct ieee_types_wmm_parameter) + 2];
+} __packed;
+
+struct mwifiex_wmm_ac_status {
+       u8 disabled;
+       u8 flow_required;
+       u8 flow_created;
+};
+
+struct mwifiex_ie_types_htcap {
+       struct mwifiex_ie_types_header header;
+       struct ieee80211_ht_cap ht_cap;
+} __packed;
+
+struct mwifiex_ie_types_htinfo {
+       struct mwifiex_ie_types_header header;
+       struct ieee80211_ht_info ht_info;
+} __packed;
+
+struct mwifiex_ie_types_2040bssco {
+       struct mwifiex_ie_types_header header;
+       u8 bss_co_2040;
+} __packed;
+
+struct mwifiex_ie_types_extcap {
+       struct mwifiex_ie_types_header header;
+       u8 ext_cap;
+} __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 host_cmd_ds_802_11_rf_channel {
+       __le16 action;
+       __le16 current_channel;
+       __le16 rf_type;
+       __le16 reserved;
+       u8 reserved_1[32];
+} __packed;
+
+struct host_cmd_ds_version_ext {
+       u8 version_str_sel;
+       char version_str[128];
+} __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;
+
+#define CONNECTION_TYPE_INFRA   0
+#define CONNECTION_TYPE_ADHOC   1
+
+struct host_cmd_ds_set_bss_mode {
+       u8 con_type;
+} __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_802_11_rf_channel rf_channel;
+               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_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_rsp scan_resp;
+               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_11_ad_hoc_start adhoc_start;
+               struct host_cmd_ds_802_11_ad_hoc_result adhoc_result;
+               struct host_cmd_ds_802_11_ad_hoc_join adhoc_join;
+               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_802_11_ibss_status ibss_coalescing;
+               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_802_11_eeprom_access eeprom;
+       } params;
+} __packed;
+
+struct mwifiex_opt_sleep_confirm {
+       __le16 command;
+       __le16 size;
+       __le16 seq_num;
+       __le16 result;
+       __le16 action;
+       struct sleep_confirm_param sleep_cfm;
+} __packed;
+
+struct mwifiex_opt_sleep_confirm_buffer {
+       u8 hdr[4];
+       struct mwifiex_opt_sleep_confirm ps_cfm_sleep;
+} __packed;
+#endif /* !_MWIFIEX_FW_H_ */
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
new file mode 100644 (file)
index 0000000..07ebc97
--- /dev/null
@@ -0,0 +1,665 @@
+/*
+ * Marvell Wireless LAN device driver: HW/FW Initialization
+ *
+ * Copyright (C) 2011, Marvell International Ltd.