wireless: mwifiex: initial commit for Marvell mwifiex driver

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>
This commit is contained in:
Bing Zhao 2011-03-21 18:00:50 -07:00 committed by John W. Linville
parent 903946e6e2
commit 5e6e3a92b9
37 changed files with 25465 additions and 0 deletions

View 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

View File

@ -56,3 +56,5 @@ obj-$(CONFIG_WL12XX) += wl12xx/
obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx/
obj-$(CONFIG_IWM) += iwmc3200wifi/
obj-$(CONFIG_MWIFIEX) += mwifiex/

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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));
}

View File

@ -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_ */

View File

@ -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.

View File

@ -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

View File

@ -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
===============================================================================

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,665 @@
/*
* Marvell Wireless LAN device driver: HW/FW Initialization
*
* 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 adds a BSS priority table to the table list.
*
* The function allocates a new BSS priority table node and adds it to
* the end of BSS priority table list, kept in driver memory.
*/
static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv)
{
struct mwifiex_adapter *adapter = priv->adapter;
struct mwifiex_bss_prio_node *bss_prio;
int status = 0;
unsigned long flags;
bss_prio = kzalloc(sizeof(struct mwifiex_bss_prio_node), GFP_KERNEL);
if (!bss_prio) {
dev_err(adapter->dev, "%s: failed to alloc bss_prio\n",
__func__);
return -1;
}
bss_prio->priv = priv;
INIT_LIST_HEAD(&bss_prio->list);
if (!adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur)
adapter->bss_prio_tbl[priv->bss_priority].bss_prio_cur =
bss_prio;
spin_lock_irqsave(&adapter->bss_prio_tbl[priv->bss_priority]
.bss_prio_lock, flags);
list_add_tail(&bss_prio->list,
&adapter->bss_prio_tbl[priv->bss_priority]
.bss_prio_head);
spin_unlock_irqrestore(&adapter->bss_prio_tbl[priv->bss_priority]
.bss_prio_lock, flags);
return status;
}
/*
* This function initializes the private structure and sets default
* values to the members.
*
* Additionally, it also initializes all the locks and sets up all the
* lists.
*/
static int mwifiex_init_priv(struct mwifiex_private *priv)
{
u32 i;
int ret = 0;
priv->media_connected = false;
memset(priv->curr_addr, 0xff, ETH_ALEN);
priv->pkt_tx_ctrl = 0;
priv->bss_mode = MWIFIEX_BSS_MODE_INFRA;
priv->data_rate = 0; /* Initially indicate the rate as auto */
priv->is_data_rate_auto = true;
priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR;
priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR;
priv->sec_info.wep_status = MWIFIEX_802_11_WEP_DISABLED;
priv->sec_info.authentication_mode = MWIFIEX_AUTH_MODE_OPEN;
priv->sec_info.encryption_mode = MWIFIEX_ENCRYPTION_MODE_NONE;
for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++)
memset(&priv->wep_key[i], 0, sizeof(struct mwifiex_wep_key));
priv->wep_key_curr_index = 0;
priv->curr_pkt_filter = HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON |
HostCmd_ACT_MAC_ETHERNETII_ENABLE;
priv->beacon_period = 100; /* beacon interval */ ;
priv->attempted_bss_desc = NULL;
memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params));
priv->listen_interval = MWIFIEX_DEFAULT_LISTEN_INTERVAL;
memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid));
memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid));
memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf));
priv->assoc_rsp_size = 0;
priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL;
priv->atim_window = 0;
priv->adhoc_state = ADHOC_IDLE;
priv->tx_power_level = 0;
priv->max_tx_power_level = 0;
priv->min_tx_power_level = 0;
priv->tx_rate = 0;
priv->rxpd_htinfo = 0;
priv->rxpd_rate = 0;
priv->rate_bitmap = 0;
priv->data_rssi_last = 0;
priv->data_rssi_avg = 0;
priv->data_nf_avg = 0;
priv->data_nf_last = 0;
priv->bcn_rssi_last = 0;
priv->bcn_rssi_avg = 0;
priv->bcn_nf_avg = 0;
priv->bcn_nf_last = 0;
memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie));
memset(&priv->aes_key, 0, sizeof(priv->aes_key));
priv->wpa_ie_len = 0;
priv->wpa_is_gtk_set = false;
memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf));
priv->assoc_tlv_buf_len = 0;
memset(&priv->wps, 0, sizeof(priv->wps));
memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf));
priv->gen_ie_buf_len = 0;
memset(priv->vs_ie, 0, sizeof(priv->vs_ie));
priv->wmm_required = true;
priv->wmm_enabled = false;
priv->wmm_qosinfo = 0;
priv->curr_bcn_buf = NULL;
priv->curr_bcn_size = 0;
priv->scan_block = false;
ret = mwifiex_add_bss_prio_tbl(priv);
return ret;
}
/*
* This function allocates buffers for members of the adapter
* structure.
*
* The memory allocated includes scan table, command buffers, and
* sleep confirm command buffer. In addition, the queues are
* also initialized.
*/
static int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter)
{
int ret = 0;
u32 buf_size;
struct mwifiex_bssdescriptor *temp_scan_table;
/* Allocate buffer to store the BSSID list */
buf_size = sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP;
temp_scan_table = kzalloc(buf_size, GFP_KERNEL);
if (!temp_scan_table) {
dev_err(adapter->dev, "%s: failed to alloc temp_scan_table\n",
__func__);
return -1;
}
adapter->scan_table = temp_scan_table;
/* Allocate command buffer */
ret = mwifiex_alloc_cmd_buffer(adapter);
if (ret) {
dev_err(adapter->dev, "%s: failed to alloc cmd buffer\n",
__func__);
return -1;
}
adapter->sleep_cfm =
dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm_buffer)
+ INTF_HEADER_LEN);
if (!adapter->sleep_cfm) {
dev_err(adapter->dev, "%s: failed to alloc sleep cfm"
" cmd buffer\n", __func__);
return -1;
}
skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN);
return 0;
}
/*
* This function initializes the adapter structure and sets default
* values to the members of adapter.
*
* This also initializes the WMM related parameters in the driver private
* structures.
*/
static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
{
struct mwifiex_opt_sleep_confirm_buffer *sleep_cfm_buf = NULL;
skb_put(adapter->sleep_cfm, sizeof(sleep_cfm_buf->ps_cfm_sleep));
sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm_buffer *)
(adapter->sleep_cfm->data);
adapter->cmd_sent = false;
adapter->data_sent = true;
adapter->cmd_resp_received = false;
adapter->event_received = false;
adapter->data_received = false;
adapter->surprise_removed = false;
adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM;
adapter->ps_state = PS_STATE_AWAKE;
adapter->need_to_wakeup = false;
adapter->scan_mode = HostCmd_BSS_MODE_ANY;
adapter->specific_scan_time = MWIFIEX_SPECIFIC_SCAN_CHAN_TIME;
adapter->active_scan_time = MWIFIEX_ACTIVE_SCAN_CHAN_TIME;
adapter->passive_scan_time = MWIFIEX_PASSIVE_SCAN_CHAN_TIME;
adapter->num_in_scan_table = 0;
memset(adapter->scan_table, 0,
(sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP));
adapter->scan_probes = 1;
memset(adapter->bcn_buf, 0, sizeof(adapter->bcn_buf));
adapter->bcn_buf_end = adapter->bcn_buf;
adapter->radio_on = RADIO_ON;
adapter->multiple_dtim = 1;
adapter->local_listen_interval = 0; /* default value in firmware
will be used */
adapter->is_deep_sleep = false;
adapter->delay_null_pkt = false;
adapter->delay_to_ps = 1000;
adapter->enhanced_ps_mode = PS_MODE_AUTO;
adapter->gen_null_pkt = false; /* Disable NULL Pkg generation by
default */
adapter->pps_uapsd_mode = false; /* Disable pps/uapsd mode by
default */
adapter->pm_wakeup_card_req = false;
adapter->pm_wakeup_fw_try = false;
adapter->max_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
adapter->is_hs_configured = false;
adapter->hs_cfg.conditions = cpu_to_le32(HOST_SLEEP_CFG_COND_DEF);
adapter->hs_cfg.gpio = HOST_SLEEP_CFG_GPIO_DEF;
adapter->hs_cfg.gap = HOST_SLEEP_CFG_GAP_DEF;
adapter->hs_activated = false;
memset(adapter->event_body, 0, sizeof(adapter->event_body));
adapter->hw_dot_11n_dev_cap = 0;
adapter->hw_dev_mcs_support = 0;
adapter->usr_dot_11n_dev_cap = 0;
adapter->usr_dev_mcs_support = 0;
adapter->chan_offset = 0;
adapter->adhoc_11n_enabled = false;
mwifiex_wmm_init(adapter);
if (adapter->sleep_cfm) {
memset(&sleep_cfm_buf->ps_cfm_sleep, 0,
adapter->sleep_cfm->len);
sleep_cfm_buf->ps_cfm_sleep.command =
cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH);
sleep_cfm_buf->ps_cfm_sleep.size =
cpu_to_le16(adapter->sleep_cfm->len);
sleep_cfm_buf->ps_cfm_sleep.result = 0;
sleep_cfm_buf->ps_cfm_sleep.action = cpu_to_le16(SLEEP_CONFIRM);
sleep_cfm_buf->ps_cfm_sleep.sleep_cfm.resp_ctrl =
cpu_to_le16(RESP_NEEDED);
}
memset(&adapter->sleep_params, 0, sizeof(adapter->sleep_params));
memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period));
adapter->tx_lock_flag = false;
adapter->null_pkt_interval = 0;
adapter->fw_bands = 0;
adapter->config_bands = 0;
adapter->adhoc_start_band = 0;
adapter->scan_channels = NULL;
adapter->fw_release_number = 0;
adapter->fw_cap_info = 0;
memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf));
adapter->event_cause = 0;
adapter->region_code = 0;
adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT;
adapter->adhoc_awake_period = 0;
memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter));
adapter->arp_filter_size = 0;
return;
}
/*
* This function frees the adapter structure.
*
* The freeing operation is done recursively, by canceling all
* pending commands, freeing the member buffers previously
* allocated (command buffers, scan table buffer, sleep confirm
* command buffer), stopping the timers and calling the cleanup
* routines for every interface, before the actual adapter
* structure is freed.
*/
static void
mwifiex_free_adapter(struct mwifiex_adapter *adapter)
{
if (!adapter) {
pr_err("%s: adapter is NULL\n", __func__);
return;
}
mwifiex_cancel_all_pending_cmd(adapter);
/* Free lock variables */
mwifiex_free_lock_list(adapter);
/* Free command buffer */
dev_dbg(adapter->dev, "info: free cmd buffer\n");
mwifiex_free_cmd_buffer(adapter);
del_timer(&adapter->cmd_timer);
dev_dbg(adapter->dev, "info: free scan table\n");
kfree(adapter->scan_table);
adapter->scan_table = NULL;
adapter->if_ops.cleanup_if(adapter);
dev_kfree_skb_any(adapter->sleep_cfm);
return;
}
/*
* This function intializes the lock variables and
* the list heads.
*/
int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
{
struct mwifiex_private *priv = NULL;
s32 i = 0;
u32 j = 0;
spin_lock_init(&adapter->mwifiex_lock);
spin_lock_init(&adapter->int_lock);
spin_lock_init(&adapter->main_proc_lock);
spin_lock_init(&adapter->mwifiex_cmd_lock);
for (i = 0; i < adapter->priv_num; i++) {
if (adapter->priv[i]) {
priv = adapter->priv[i];
spin_lock_init(&priv->rx_pkt_lock);
spin_lock_init(&priv->wmm.ra_list_spinlock);
spin_lock_init(&priv->curr_bcn_buf_lock);
}
}
/* Initialize cmd_free_q */
INIT_LIST_HEAD(&adapter->cmd_free_q);
/* Initialize cmd_pending_q */
INIT_LIST_HEAD(&adapter->cmd_pending_q);
/* Initialize scan_pending_q */
INIT_LIST_HEAD(&adapter->scan_pending_q);
spin_lock_init(&adapter->cmd_free_q_lock);
spin_lock_init(&adapter->cmd_pending_q_lock);
spin_lock_init(&adapter->scan_pending_q_lock);
for (i = 0; i < adapter->priv_num; ++i) {
INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head);
adapter->bss_prio_tbl[i].bss_prio_cur = NULL;
spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock);
}
for (i = 0; i < adapter->priv_num; i++) {
if (!adapter->priv[i])
continue;
priv = adapter->priv[i];
for (j = 0; j < MAX_NUM_TID; ++j) {
INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list);
spin_lock_init(&priv->wmm.tid_tbl_ptr[j].tid_tbl_lock);
}
INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
spin_lock_init(&priv->tx_ba_stream_tbl_lock);
spin_lock_init(&priv->rx_reorder_tbl_lock);
}
return 0;
}
/*
* This function releases the lock variables and frees the locks and
* associated locks.
*/
void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
{
struct mwifiex_private *priv = NULL;
s32 i = 0;
s32 j = 0;
/* Free lists */
list_del(&adapter->cmd_free_q);
list_del(&adapter->cmd_pending_q);
list_del(&adapter->scan_pending_q);
for (i = 0; i < adapter->priv_num; i++)
list_del(&adapter->bss_prio_tbl[i].bss_prio_head);
for (i = 0; i < adapter->priv_num; i++) {
if (adapter->priv[i]) {
priv = adapter->priv[i];
for (j = 0; j < MAX_NUM_TID; ++j)
list_del(&priv->wmm.tid_tbl_ptr[j].ra_list);
list_del(&priv->tx_ba_stream_tbl_ptr);
list_del(&priv->rx_reorder_tbl_ptr);
}
}
return;
}
/*
* This function initializes the firmware.
*
* The following operations are performed sequentially -
* - Allocate adapter structure
* - Initialize the adapter structure
* - Initialize the private structure
* - Add BSS priority tables to the adapter structure
* - For each interface, send the init commands to firmware
* - Send the first command in command pending queue, if available
*/
int mwifiex_init_fw(struct mwifiex_adapter *adapter)
{
int ret = 0;
struct mwifiex_private *priv = NULL;
u8 i = 0;
u8 first_sta = true;
int is_cmd_pend_q_empty;
unsigned long flags;
adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
/* Allocate memory for member of adapter structure */
ret = mwifiex_allocate_adapter(adapter);
if (ret)
return -1;
/* Initialize adapter structure */
mwifiex_init_adapter(adapter);
for (i = 0; i < adapter->priv_num; i++) {
if (adapter->priv[i]) {
priv = adapter->priv[i];
/* Initialize private structure */
ret = mwifiex_init_priv(priv);
if (ret)
return -1;
}
}
for (i = 0; i < adapter->priv_num; i++) {
if (adapter->priv[i]) {
ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta);
if (ret == -1)
return -1;
first_sta = false;
}
}
spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q);
spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
if (!is_cmd_pend_q_empty) {
/* Send the first command in queue and return */
if (mwifiex_main_process(adapter) != -1)
ret = -EINPROGRESS;
} else {
adapter->hw_status = MWIFIEX_HW_STATUS_READY;
}
return ret;
}
/*
* This function deletes the BSS priority tables.
*
* The function traverses through all the allocated BSS priority nodes
* in every BSS priority table and frees them.
*/
static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv)
{
int i;
struct mwifiex_adapter *adapter = priv->adapter;
struct mwifiex_bss_prio_node *bssprio_node = NULL, *tmp_node = NULL,
**cur = NULL;
struct list_head *head;
spinlock_t *lock;
unsigned long flags;
for (i = 0; i < adapter->priv_num; ++i) {
head = &adapter->bss_prio_tbl[i].bss_prio_head;
cur = &adapter->bss_prio_tbl[i].bss_prio_cur;
lock = &adapter->bss_prio_tbl[i].bss_prio_lock;
dev_dbg(adapter->dev, "info: delete BSS priority table,"
" index = %d, i = %d, head = %p, cur = %p\n",
priv->bss_index, i, head, *cur);
if (*cur) {
spin_lock_irqsave(lock, flags);
if (list_empty(head)) {
spin_unlock_irqrestore(lock, flags);
continue;
}
bssprio_node = list_first_entry(head,
struct mwifiex_bss_prio_node, list);
spin_unlock_irqrestore(lock, flags);
list_for_each_entry_safe(bssprio_node, tmp_node, head,
list) {
if (bssprio_node->priv == priv) {
dev_dbg(adapter->dev, "info: Delete "
"node %p, next = %p\n",
bssprio_node, tmp_node);
spin_lock_irqsave(lock, flags);
list_del(&bssprio_node->list);
spin_unlock_irqrestore(lock, flags);
kfree(bssprio_node);
}
}
*cur = (struct mwifiex_bss_prio_node *)head;
}
}
}
/*
* This function is used to shutdown the driver.
*
* The following operations are performed sequentially -
* - Check if already shut down
* - Make sure the main process has stopped
* - Clean up the Tx and Rx queues
* - Delete BSS priority tables
* - Free the adapter
* - Notify completion
*/
int
mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
{
int ret = -EINPROGRESS;
struct mwifiex_private *priv = NULL;
s32 i = 0;
unsigned long flags;
/* mwifiex already shutdown */
if (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)
return 0;
adapter->hw_status = MWIFIEX_HW_STATUS_CLOSING;
/* wait for mwifiex_process to complete */
if (adapter->mwifiex_processing) {
dev_warn(adapter->dev, "main process is still running\n");
return ret;
}
/* shut down mwifiex */
dev_dbg(adapter->dev, "info: shutdown mwifiex...\n");
/* Clean up Tx/Rx queues and delete BSS priority table */
for (i = 0; i < adapter->priv_num; i++) {
if (adapter->priv[i]) {
priv = adapter->priv[i];
mwifiex_clean_txrx(priv);
mwifiex_delete_bss_prio_tbl(priv);
}
}
spin_lock_irqsave(&adapter->mwifiex_lock, flags);
/* Free adapter structure */
mwifiex_free_adapter(adapter);
spin_unlock_irqrestore(&adapter->mwifiex_lock, flags);
/* Notify completion */
ret = mwifiex_shutdown_fw_complete(adapter);
return ret;
}
/*
* This function downloads the firmware to the card.
*
* The actual download is preceded by two sanity checks -
* - Check if firmware is already running
* - Check if the interface is the winner to download the firmware
*
* ...and followed by another -
* - Check if the firmware is downloaded successfully
*
* After download is successfully completed, the host interrupts are enabled.
*/
int mwifiex_dnld_fw(struct mwifiex_adapter *adapter,
struct mwifiex_fw_image *pmfw)
{
int ret = 0;
u32 poll_num = 1;
int winner;
/* Check if firmware is already running */
ret = adapter->if_ops.check_fw_status(adapter, poll_num, &winner);
if (!ret) {
dev_notice(adapter->dev,
"WLAN FW already running! Skip FW download\n");
goto done;
}
poll_num = MAX_FIRMWARE_POLL_TRIES;
/* Check if we are the winner for downloading FW */
if (!winner) {
dev_notice(adapter->dev,
"Other interface already running!"
" Skip FW download\n");
poll_num = MAX_MULTI_INTERFACE_POLL_TRIES;
goto poll_fw;
}
if (pmfw) {
/* Download firmware with helper */
ret = adapter->if_ops.prog_fw(adapter, pmfw);
if (ret) {
dev_err(adapter->dev, "prog_fw failed ret=%#x\n", ret);
return ret;
}
}
poll_fw:
/* Check if the firmware is downloaded successfully or not */
ret = adapter->if_ops.check_fw_status(adapter, poll_num, NULL);
if (ret) {
dev_err(adapter->dev, "FW failed to be active in time\n");
return -1;
}
done:
/* re-enable host interrupt for mwifiex after fw dnld is successful */
adapter->if_ops.enable_int(adapter);
return ret;
}

View File

@ -0,0 +1,433 @@
/*
* Marvell Wireless LAN device driver: ioctl data structures & 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_IOCTL_H_
#define _MWIFIEX_IOCTL_H_
#include <net/mac80211.h>
enum {
MWIFIEX_SCAN_MODE_UNCHANGED = 0,
MWIFIEX_SCAN_MODE_BSS,
MWIFIEX_SCAN_MODE_IBSS,
MWIFIEX_SCAN_MODE_ANY
};
enum {
MWIFIEX_SCAN_TYPE_UNCHANGED = 0,
MWIFIEX_SCAN_TYPE_ACTIVE,
MWIFIEX_SCAN_TYPE_PASSIVE
};
struct mwifiex_get_scan_table_fixed {
u8 bssid[ETH_ALEN];
u8 channel;
u8 rssi;
long long network_tsf;
};
struct mwifiex_scan_time_params {
u32 specific_scan_time;
u32 active_scan_time;
u32 passive_scan_time;
};
struct mwifiex_user_scan {
u32 scan_cfg_len;
u8 scan_cfg_buf[1];
};
struct mwifiex_scan_req {
u32 scan_mode;
u32 scan_type;
struct mwifiex_802_11_ssid scan_ssid;
struct mwifiex_scan_time_params scan_time;
struct mwifiex_user_scan user_scan;
};
struct mwifiex_scan_resp {
u32 num_in_scan_table;
u8 *scan_table;
};
enum {
MWIFIEX_BSS_MODE_INFRA = 1,
MWIFIEX_BSS_MODE_IBSS,
MWIFIEX_BSS_MODE_AUTO
};
#define MWIFIEX_PROMISC_MODE 1
#define MWIFIEX_MULTICAST_MODE 2
#define MWIFIEX_ALL_MULTI_MODE 4
#define MWIFIEX_MAX_MULTICAST_LIST_SIZE 32
struct mwifiex_multicast_list {
u32 mode;
u32 num_multicast_addr;
u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN];
};
#define MWIFIEX_MAX_CHANNEL_NUM 128
struct mwifiex_chan_freq {
u32 channel;
u32 freq;
};
struct mwifiex_chan_list {
u32 num_of_chan;
struct mwifiex_chan_freq cf[MWIFIEX_MAX_CHANNEL_NUM];
};
struct mwifiex_ssid_bssid {
struct mwifiex_802_11_ssid ssid;
u8 bssid[ETH_ALEN];
};
enum {
BAND_B = 1,
BAND_G = 2,
BAND_A = 4,
BAND_GN = 8,
BAND_AN = 16,
};
#define NO_SEC_CHANNEL 0
#define SEC_CHANNEL_ABOVE 1
#define SEC_CHANNEL_BELOW 3
struct mwifiex_ds_band_cfg {
u32 config_bands;
u32 adhoc_start_band;
u32 adhoc_channel;
u32 sec_chan_offset;
};
enum {
ADHOC_IDLE,
ADHOC_STARTED,
ADHOC_JOINED,
ADHOC_COALESCED
};
struct mwifiex_ds_get_stats {
u32 mcast_tx_frame;
u32 failed;
u32 retry;
u32 multi_retry;
u32 frame_dup;
u32 rts_success;
u32 rts_failure;
u32 ack_failure;
u32 rx_frag;
u32 mcast_rx_frame;
u32 fcs_error;
u32 tx_frame;
u32 wep_icv_error[4];
};
#define BCN_RSSI_LAST_MASK 0x00000001
#define BCN_RSSI_AVG_MASK 0x00000002
#define DATA_RSSI_LAST_MASK 0x00000004
#define DATA_RSSI_AVG_MASK 0x00000008
#define BCN_SNR_LAST_MASK 0x00000010
#define BCN_SNR_AVG_MASK 0x00000020
#define DATA_SNR_LAST_MASK 0x00000040
#define DATA_SNR_AVG_MASK 0x00000080
#define BCN_NF_LAST_MASK 0x00000100
#define BCN_NF_AVG_MASK 0x00000200
#define DATA_NF_LAST_MASK 0x00000400
#define DATA_NF_AVG_MASK 0x00000800
#define ALL_RSSI_INFO_MASK 0x00000fff
struct mwifiex_ds_get_signal {
/*
* Bit0: Last Beacon RSSI, Bit1: Average Beacon RSSI,
* Bit2: Last Data RSSI, Bit3: Average Data RSSI,
* Bit4: Last Beacon SNR, Bit5: Average Beacon SNR,
* Bit6: Last Data SNR, Bit7: Average Data SNR,
* Bit8: Last Beacon NF, Bit9: Average Beacon NF,
* Bit10: Last Data NF, Bit11: Average Data NF
*/
u16 selector;
s16 bcn_rssi_last;
s16 bcn_rssi_avg;
s16 data_rssi_last;
s16 data_rssi_avg;
s16 bcn_snr_last;
s16 bcn_snr_avg;
s16 data_snr_last;
s16 data_snr_avg;
s16 bcn_nf_last;
s16 bcn_nf_avg;
s16 data_nf_last;
s16 data_nf_avg;
};
struct mwifiex_fw_info {
u32 fw_ver;
u8 mac_addr[ETH_ALEN];
};
#define MWIFIEX_MAX_VER_STR_LEN 128
struct mwifiex_ver_ext {
u32 version_str_sel;
char version_str[MWIFIEX_MAX_VER_STR_LEN];
};
struct mwifiex_bss_info {
u32 bss_mode;
struct mwifiex_802_11_ssid ssid;
u32 scan_table_idx;
u32 bss_chan;
u32 region_code;
u32 media_connected;
u32 radio_on;
u32 max_power_level;
u32 min_power_level;
u32 adhoc_state;
signed int bcn_nf_last;
u32 wep_status;
u32 is_hs_configured;
u32 is_deep_sleep;
u8 bssid[ETH_ALEN];
};
#define MAX_NUM_TID 8
#define MAX_RX_WINSIZE 64
struct mwifiex_ds_rx_reorder_tbl {
u16 tid;
u8 ta[ETH_ALEN];
u32 start_win;
u32 win_size;
u32 buffer[MAX_RX_WINSIZE];
};
struct mwifiex_ds_tx_ba_stream_tbl {
u16 tid;
u8 ra[ETH_ALEN];
};
#define DBG_CMD_NUM 5
struct mwifiex_debug_info {
u32 int_counter;
u32 packets_out[MAX_NUM_TID];
u32 max_tx_buf_size;
u32 tx_buf_size;
u32 curr_tx_buf_size;
u32 tx_tbl_num;
struct mwifiex_ds_tx_ba_stream_tbl
tx_tbl[MWIFIEX_MAX_TX_BASTREAM_SUPPORTED];
u32 rx_tbl_num;
struct mwifiex_ds_rx_reorder_tbl rx_tbl
[MWIFIEX_MAX_RX_BASTREAM_SUPPORTED];
u16 ps_mode;
u32 ps_state;
u8 is_deep_sleep;
u8 pm_wakeup_card_req;
u32 pm_wakeup_fw_try;
u8 is_hs_configured;
u8 hs_activated;
u32 num_cmd_host_to_card_failure;
u32 num_cmd_sleep_cfm_host_to_card_failure;
u32 num_tx_host_to_card_failure;
u32 num_event_deauth;
u32 num_event_disassoc;
u32 num_event_link_lost;
u32 num_cmd_deauth;
u32 num_cmd_assoc_success;
u32 num_cmd_assoc_failure;
u32 num_tx_timeout;
u32 num_cmd_timeout;
u16 timeout_cmd_id;
u16 timeout_cmd_act;
u16 last_cmd_id[DBG_CMD_NUM];
u16 last_cmd_act[DBG_CMD_NUM];
u16 last_cmd_index;
u16 last_cmd_resp_id[DBG_CMD_NUM];
u16 last_cmd_resp_index;
u16 last_event[DBG_CMD_NUM];
u16 last_event_index;
u8 data_sent;
u8 cmd_sent;
u8 cmd_resp_received;
u8 event_received;
};
enum {
MWIFIEX_AUTH_MODE_OPEN = 0x00,
MWIFIEX_AUTH_MODE_SHARED = 0x01,
MWIFIEX_AUTH_MODE_NETWORKEAP = 0x80,
MWIFIEX_AUTH_MODE_AUTO = 0xFF,
};
enum {
MWIFIEX_ENCRYPTION_MODE_NONE = 0,
MWIFIEX_ENCRYPTION_MODE_WEP40 = 1,
MWIFIEX_ENCRYPTION_MODE_TKIP = 2,
MWIFIEX_ENCRYPTION_MODE_CCMP = 3,
MWIFIEX_ENCRYPTION_MODE_WEP104 = 4,
};
#define MWIFIEX_KEY_INDEX_UNICAST 0x40000000
#define MWIFIEX_MAX_KEY_LENGTH 32
#define WAPI_RXPN_LEN 16
struct mwifiex_ds_encrypt_key {
u32 key_disable;
u32 key_index;
u32 key_len;
u8 key_material[MWIFIEX_MAX_KEY_LENGTH];
u8 mac_addr[ETH_ALEN];
u32 is_wapi_key;
u8 wapi_rxpn[WAPI_RXPN_LEN];
};
struct mwifiex_rate_cfg {
u32 action;
u32 is_rate_auto;
u32 rate;
};
struct mwifiex_data_rate {
u32 tx_data_rate;
u32 rx_data_rate;
};
struct mwifiex_power_cfg {
u32 is_power_auto;
u32 power_level;
};
struct mwifiex_ds_hs_cfg {
u32 is_invoke_hostcmd;
/* Bit0: non-unicast data
* Bit1: unicast data
* Bit2: mac events
* Bit3: magic packet
*/
u32 conditions;
u32 gpio;
u32 gap;
};
#define DEEP_SLEEP_ON 1
#define DEEP_SLEEP_OFF 0
#define DEEP_SLEEP_IDLE_TIME 100
struct mwifiex_ds_auto_ds {
u16 auto_ds;
u16 idle_time;
};
#define PS_MODE_UNCHANGED 0
#define PS_MODE_AUTO 1
#define PS_MODE_POLL 2
#define PS_MODE_NULL 3
struct mwifiex_ds_pm_cfg {
union {
u32 ps_mode;
struct mwifiex_ds_hs_cfg hs_cfg;
struct mwifiex_ds_auto_ds auto_deep_sleep;
u32 sleep_period;
} param;
};
struct mwifiex_ioctl_wmm_queue_status_ac {
u8 wmm_acm;
u8 flow_required;
u8 flow_created;
u8 disabled;
};
struct mwifiex_ds_wmm_queue_status {
struct mwifiex_ioctl_wmm_queue_status_ac
ac_status[IEEE80211_MAX_QUEUES];
};
struct mwifiex_ds_11n_tx_cfg {
u16 tx_htcap;
u16 tx_htinfo;
};
struct mwifiex_ds_11n_amsdu_aggr_ctrl {
u16 enable;
u16 curr_buf_size;
};
#define MWIFIEX_NUM_OF_CMD_BUFFER 20
#define MWIFIEX_SIZE_OF_CMD_BUFFER 2048
enum {
MWIFIEX_IE_TYPE_GEN_IE = 0,
MWIFIEX_IE_TYPE_ARP_FILTER,
};
enum {
MWIFIEX_REG_MAC = 1,
MWIFIEX_REG_BBP,
MWIFIEX_REG_RF,
MWIFIEX_REG_PMIC,
MWIFIEX_REG_CAU,
};
struct mwifiex_ds_reg_rw {
__le32 type;
__le32 offset;
__le32 value;
};
#define MAX_EEPROM_DATA 256
struct mwifiex_ds_read_eeprom {
__le16 offset;
__le16 byte_count;
u8 value[MAX_EEPROM_DATA];
};
struct mwifiex_ds_misc_gen_ie {
u32 type;
u32 len;
u8 ie_data[IW_CUSTOM_MAX];
};
struct mwifiex_ds_misc_cmd {
u32 len;
u8 cmd[MWIFIEX_SIZE_OF_CMD_BUFFER];
};
#define MWIFIEX_MAX_VSIE_LEN (256)
#define MWIFIEX_MAX_VSIE_NUM (8)
#define MWIFIEX_VSIE_MASK_SCAN 0x01
#define MWIFIEX_VSIE_MASK_ASSOC 0x02
#define MWIFIEX_VSIE_MASK_ADHOC 0x04
enum {
MWIFIEX_FUNC_INIT = 1,
MWIFIEX_FUNC_SHUTDOWN,
};
#endif /* !_MWIFIEX_IOCTL_H_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,305 @@
/*
* Marvell Wireless LAN device driver: SDIO specific definitions
*
* 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_SDIO_H
#define _MWIFIEX_SDIO_H
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h>
#include "main.h"
#define BLOCK_MODE 1
#define BYTE_MODE 0
#define REG_PORT 0
#define RD_BITMAP_L 0x04
#define RD_BITMAP_U 0x05
#define WR_BITMAP_L 0x06
#define WR_BITMAP_U 0x07
#define RD_LEN_P0_L 0x08
#define RD_LEN_P0_U 0x09
#define MWIFIEX_SDIO_IO_PORT_MASK 0xfffff
#define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000
#define CTRL_PORT 0
#define CTRL_PORT_MASK 0x0001
#define DATA_PORT_MASK 0xfffe
#define MAX_MP_REGS 64
#define MAX_PORT 16
#define SDIO_MP_AGGR_DEF_PKT_LIMIT 8
#define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (4096) /* 4K */
/* Multi port RX aggregation buffer size */
#define SDIO_MP_RX_AGGR_DEF_BUF_SIZE (4096) /* 4K */
/* Misc. Config Register : Auto Re-enable interrupts */
#define AUTO_RE_ENABLE_INT BIT(4)
/* Host Control Registers */
/* Host Control Registers : I/O port 0 */
#define IO_PORT_0_REG 0x78
/* Host Control Registers : I/O port 1 */
#define IO_PORT_1_REG 0x79
/* Host Control Registers : I/O port 2 */
#define IO_PORT_2_REG 0x7A
/* Host Control Registers : Configuration */
#define CONFIGURATION_REG 0x00
/* Host Control Registers : Host without Command 53 finish host*/
#define HOST_TO_CARD_EVENT (0x1U << 3)
/* Host Control Registers : Host without Command 53 finish host */
#define HOST_WO_CMD53_FINISH_HOST (0x1U << 2)
/* Host Control Registers : Host power up */
#define HOST_POWER_UP (0x1U << 1)
/* Host Control Registers : Host power down */
#define HOST_POWER_DOWN (0x1U << 0)
/* Host Control Registers : Host interrupt mask */
#define HOST_INT_MASK_REG 0x02
/* Host Control Registers : Upload host interrupt mask */
#define UP_LD_HOST_INT_MASK (0x1U)
/* Host Control Registers : Download host interrupt mask */
#define DN_LD_HOST_INT_MASK (0x2U)
/* Enable Host interrupt mask */
#define HOST_INT_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK)
/* Disable Host interrupt mask */
#define HOST_INT_DISABLE 0xff
/* Host Control Registers : Host interrupt status */
#define HOST_INTSTATUS_REG 0x03
/* Host Control Registers : Upload host interrupt status */
#define UP_LD_HOST_INT_STATUS (0x1U)
/* Host Control Registers : Download host interrupt status */
#define DN_LD_HOST_INT_STATUS (0x2U)
/* Host Control Registers : Host interrupt RSR */
#define HOST_INT_RSR_REG 0x01
/* Host Control Registers : Upload host interrupt RSR */
#define UP_LD_HOST_INT_RSR (0x1U)
#define SDIO_INT_MASK 0x3F
/* Host Control Registers : Host interrupt status */
#define HOST_INT_STATUS_REG 0x28
/* Host Control Registers : Upload CRC error */
#define UP_LD_CRC_ERR (0x1U << 2)
/* Host Control Registers : Upload restart */
#define UP_LD_RESTART (0x1U << 1)
/* Host Control Registers : Download restart */
#define DN_LD_RESTART (0x1U << 0)
/* Card Control Registers : Card status register */
#define CARD_STATUS_REG 0x30
/* Card Control Registers : Card I/O ready */
#define CARD_IO_READY (0x1U << 3)
/* Card Control Registers : CIS card ready */
#define CIS_CARD_RDY (0x1U << 2)
/* Card Control Registers : Upload card ready */
#define UP_LD_CARD_RDY (0x1U << 1)
/* Card Control Registers : Download card ready */
#define DN_LD_CARD_RDY (0x1U << 0)
/* Card Control Registers : Host interrupt mask register */
#define HOST_INTERRUPT_MASK_REG 0x34
/* Card Control Registers : Host power interrupt mask */
#define HOST_POWER_INT_MASK (0x1U << 3)
/* Card Control Registers : Abort card interrupt mask */
#define ABORT_CARD_INT_MASK (0x1U << 2)
/* Card Control Registers : Upload card interrupt mask */
#define UP_LD_CARD_INT_MASK (0x1U << 1)
/* Card Control Registers : Download card interrupt mask */
#define DN_LD_CARD_INT_MASK (0x1U << 0)
/* Card Control Registers : Card interrupt status register */
#define CARD_INTERRUPT_STATUS_REG 0x38
/* Card Control Registers : Power up interrupt */
#define POWER_UP_INT (0x1U << 4)
/* Card Control Registers : Power down interrupt */
#define POWER_DOWN_INT (0x1U << 3)
/* Card Control Registers : Card interrupt RSR register */
#define CARD_INTERRUPT_RSR_REG 0x3c
/* Card Control Registers : Power up RSR */
#define POWER_UP_RSR (0x1U << 4)
/* Card Control Registers : Power down RSR */
#define POWER_DOWN_RSR (0x1U << 3)
/* Card Control Registers : Miscellaneous Configuration Register */
#define CARD_MISC_CFG_REG 0x6C
/* Host F1 read base 0 */
#define HOST_F1_RD_BASE_0 0x0040
/* Host F1 read base 1 */
#define HOST_F1_RD_BASE_1 0x0041
/* Host F1 card ready */
#define HOST_F1_CARD_RDY 0x0020
/* Firmware status 0 register */
#define CARD_FW_STATUS0_REG 0x60
/* Firmware status 1 register */
#define CARD_FW_STATUS1_REG 0x61
/* Rx length register */
#define CARD_RX_LEN_REG 0x62
/* Rx unit register */
#define CARD_RX_UNIT_REG 0x63
/* Event header Len*/
#define MWIFIEX_EVENT_HEADER_LEN 8
/* Max retry number of CMD53 write */
#define MAX_WRITE_IOMEM_RETRY 2
/* SDIO Tx aggregation in progress ? */
#define MP_TX_AGGR_IN_PROGRESS(a) (a->mpa_tx.pkt_cnt > 0)
/* SDIO Tx aggregation buffer room for next packet ? */
#define MP_TX_AGGR_BUF_HAS_ROOM(a, len) ((a->mpa_tx.buf_len+len) \
<= a->mpa_tx.buf_size)
/* Copy current packet (SDIO Tx aggregation buffer) to SDIO buffer */
#define MP_TX_AGGR_BUF_PUT(a, payload, pkt_len, port) do { \
memmove(&a->mpa_tx.buf[a->mpa_tx.buf_len], \
payload, pkt_len); \
a->mpa_tx.buf_len += pkt_len; \
if (!a->mpa_tx.pkt_cnt) \
a->mpa_tx.start_port = port; \
if (a->mpa_tx.start_port <= port) \
a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt)); \
else \
a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+(MAX_PORT - \
a->mp_end_port))); \
a->mpa_tx.pkt_cnt++; \
} while (0);
/* SDIO Tx aggregation limit ? */
#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \
(a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit)
/* SDIO Tx aggregation port limit ? */
#define MP_TX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_wr_port < \
a->mpa_tx.start_port) && (((MAX_PORT - \
a->mpa_tx.start_port) + a->curr_wr_port) >= \
SDIO_MP_AGGR_DEF_PKT_LIMIT))
/* Reset SDIO Tx aggregation buffer parameters */
#define MP_TX_AGGR_BUF_RESET(a) do { \
a->mpa_tx.pkt_cnt = 0; \
a->mpa_tx.buf_len = 0; \
a->mpa_tx.ports = 0; \
a->mpa_tx.start_port = 0; \
} while (0);
/* SDIO Rx aggregation limit ? */
#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \
(a->mpa_rx.pkt_cnt == a->mpa_rx.pkt_aggr_limit)
/* SDIO Tx aggregation port limit ? */
#define MP_RX_AGGR_PORT_LIMIT_REACHED(a) ((a->curr_rd_port < \
a->mpa_rx.start_port) && (((MAX_PORT - \
a->mpa_rx.start_port) + a->curr_rd_port) >= \
SDIO_MP_AGGR_DEF_PKT_LIMIT))
/* SDIO Rx aggregation in progress ? */
#define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0)
/* SDIO Rx aggregation buffer room for next packet ? */
#define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \
((a->mpa_rx.buf_len+rx_len) <= a->mpa_rx.buf_size)
/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
#define MP_RX_AGGR_SETUP(a, skb, port) do { \
a->mpa_rx.buf_len += skb->len; \
if (!a->mpa_rx.pkt_cnt) \
a->mpa_rx.start_port = port; \
if (a->mpa_rx.start_port <= port) \
a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt)); \
else \
a->mpa_rx.ports |= (1<<(a->mpa_rx.pkt_cnt+1)); \
a->mpa_rx.skb_arr[a->mpa_rx.pkt_cnt] = skb; \
a->mpa_rx.len_arr[a->mpa_rx.pkt_cnt] = skb->len; \
a->mpa_rx.pkt_cnt++; \
} while (0);
/* Reset SDIO Rx aggregation buffer parameters */
#define MP_RX_AGGR_BUF_RESET(a) do { \
a->mpa_rx.pkt_cnt = 0; \
a->mpa_rx.buf_len = 0; \
a->mpa_rx.ports = 0; \
a->mpa_rx.start_port = 0; \
} while (0);
/* data structure for SDIO MPA TX */
struct mwifiex_sdio_mpa_tx {
/* multiport tx aggregation buffer pointer */
u8 *buf;
u32 buf_len;
u32 pkt_cnt;
u16 ports;
u16 start_port;
u8 enabled;
u32 buf_size;
u32 pkt_aggr_limit;
};
struct mwifiex_sdio_mpa_rx {
u8 *buf;
u32 buf_len;
u32 pkt_cnt;
u16 ports;
u16 start_port;
struct sk_buff *skb_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT];
u32 len_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT];
u8 enabled;
u32 buf_size;
u32 pkt_aggr_limit;
};
int mwifiex_bus_register(void);
void mwifiex_bus_unregister(void);
struct sdio_mmc_card {
struct sdio_func *func;
struct mwifiex_adapter *adapter;
u16 mp_rd_bitmap;
u16 mp_wr_bitmap;
u16 mp_end_port;
u16 mp_data_port_mask;
u8 curr_rd_port;
u8 curr_wr_port;
u8 *mp_regs;
struct mwifiex_sdio_mpa_tx mpa_tx;
struct mwifiex_sdio_mpa_rx mpa_rx;
};
#endif /* _MWIFIEX_SDIO_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,986 @@
/*
* Marvell Wireless LAN device driver: station command response handling
*
* 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 handles the command response error case.
*
* For scan response error, the function cancels all the pending
* scan commands and generates an event to inform the applications
* of the scan completion.
*
* For Power Save command failure, we do not retry enter PS
* command in case of Ad-hoc mode.
*
* For all other response errors, the current command buffer is freed
* and returned to the free command queue.
*/
static void
mwifiex_process_cmdresp_error(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
struct mwifiex_wait_queue *wq_buf)
{
struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL;
struct mwifiex_adapter *adapter = priv->adapter;
unsigned long flags;
dev_err(adapter->dev, "CMD_RESP: cmd %#x error, result=%#x\n",
resp->command, resp->result);
if (wq_buf)
wq_buf->status = MWIFIEX_ERROR_FW_CMDRESP;
switch (le16_to_cpu(resp->command)) {
case HostCmd_CMD_802_11_PS_MODE_ENH:
{
struct host_cmd_ds_802_11_ps_mode_enh *pm =
&resp->params.psmode_enh;
dev_err(adapter->dev, "PS_MODE_ENH cmd failed: "
"result=0x%x action=0x%X\n",
resp->result, le16_to_cpu(pm->action));
/* We do not re-try enter-ps command in ad-hoc mode. */
if (le16_to_cpu(pm->action) == EN_AUTO_PS &&
(le16_to_cpu(pm->params.auto_ps.ps_bitmap) &
BITMAP_STA_PS)
&& priv->bss_mode == MWIFIEX_BSS_MODE_IBSS)
adapter->ps_mode =
MWIFIEX_802_11_POWER_MODE_CAM;
}
break;
case HostCmd_CMD_802_11_SCAN:
/* 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);
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);
if (priv->report_scan_result)
priv->report_scan_result = false;
if (priv->scan_pending_on_block) {
priv->scan_pending_on_block = false;
up(&priv->async_sem);
}
break;
case HostCmd_CMD_MAC_CONTROL:
break;
default:
break;
}
/* Handling errors here */
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;
}
/*
* This function handles the command response of get RSSI info.
*
* Handling includes changing the header fields into CPU format
* and saving the following parameters in driver -
* - Last data and beacon RSSI value
* - Average data and beacon RSSI value
* - Last data and beacon NF value
* - Average data and beacon NF value
*
* The parameters are send to the application as well, along with
* calculated SNR values.
*/
static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
void *data_buf)
{
struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp =
&resp->params.rssi_info_rsp;
struct mwifiex_ds_get_signal *signal = NULL;
priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last);
priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last);
priv->data_rssi_avg = le16_to_cpu(rssi_info_rsp->data_rssi_avg);
priv->data_nf_avg = le16_to_cpu(rssi_info_rsp->data_nf_avg);
priv->bcn_rssi_last = le16_to_cpu(rssi_info_rsp->bcn_rssi_last);
priv->bcn_nf_last = le16_to_cpu(rssi_info_rsp->bcn_nf_last);
priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg);
priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg);
/* Need to indicate IOCTL complete */
if (data_buf) {
signal = (struct mwifiex_ds_get_signal *) data_buf;
memset(signal, 0, sizeof(struct mwifiex_ds_get_signal));
signal->selector = ALL_RSSI_INFO_MASK;
/* RSSI */
signal->bcn_rssi_last = priv->bcn_rssi_last;
signal->bcn_rssi_avg = priv->bcn_rssi_avg;
signal->data_rssi_last = priv->data_rssi_last;
signal->data_rssi_avg = priv->data_rssi_avg;
/* SNR */
signal->bcn_snr_last =
CAL_SNR(priv->bcn_rssi_last, priv->bcn_nf_last);
signal->bcn_snr_avg =
CAL_SNR(priv->bcn_rssi_avg, priv->bcn_nf_avg);
signal->data_snr_last =
CAL_SNR(priv->data_rssi_last, priv->data_nf_last);
signal->data_snr_avg =
CAL_SNR(priv->data_rssi_avg, priv->data_nf_avg);
/* NF */
signal->bcn_nf_last = priv->bcn_nf_last;
signal->bcn_nf_avg = priv->bcn_nf_avg;
signal->data_nf_last = priv->data_nf_last;
signal->data_nf_avg = priv->data_nf_avg;
}
return 0;
}
/*
* This function handles the command response of set/get SNMP
* MIB parameters.
*
* Handling includes changing the header fields into CPU format
* and saving the parameter in driver.
*
* The following parameters are supported -
* - Fragmentation threshold
* - RTS threshold
* - Short retry limit
*/
static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
void *data_buf)
{
struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib;
u16 oid = le16_to_cpu(smib->oid);
u16 query_type = le16_to_cpu(smib->query_type);
u32 ul_temp;
dev_dbg(priv->adapter->dev, "info: SNMP_RESP: oid value = %#x,"
" query_type = %#x, buf size = %#x\n",
oid, query_type, le16_to_cpu(smib->buf_size));
if (query_type == HostCmd_ACT_GEN_GET) {
ul_temp = le16_to_cpu(*((__le16 *) (smib->value)));
if (data_buf)
*(u32 *)data_buf = ul_temp;
switch (oid) {
case FRAG_THRESH_I:
dev_dbg(priv->adapter->dev,
"info: SNMP_RESP: FragThsd =%u\n", ul_temp);
break;
case RTS_THRESH_I:
dev_dbg(priv->adapter->dev,
"info: SNMP_RESP: RTSThsd =%u\n", ul_temp);
break;
case SHORT_RETRY_LIM_I:
dev_dbg(priv->adapter->dev,
"info: SNMP_RESP: TxRetryCount=%u\n", ul_temp);
break;
default:
break;
}
}
return 0;
}
/*
* This function handles the command response of get log request
*
* Handling includes changing the header fields into CPU format
* and sending the received parameters to application.
*/
static int mwifiex_ret_get_log(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
void *data_buf)
{
struct host_cmd_ds_802_11_get_log *get_log =
(struct host_cmd_ds_802_11_get_log *) &resp->params.get_log;
struct mwifiex_ds_get_stats *stats = NULL;
if (data_buf) {
stats = (struct mwifiex_ds_get_stats *) data_buf;
stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame);
stats->failed = le32_to_cpu(get_log->failed);
stats->retry = le32_to_cpu(get_log->retry);
stats->multi_retry = le32_to_cpu(get_log->multi_retry);
stats->frame_dup = le32_to_cpu(get_log->frame_dup);
stats->rts_success = le32_to_cpu(get_log->rts_success);
stats->rts_failure = le32_to_cpu(get_log->rts_failure);
stats->ack_failure = le32_to_cpu(get_log->ack_failure);
stats->rx_frag = le32_to_cpu(get_log->rx_frag);
stats->mcast_rx_frame = le32_to_cpu(get_log->mcast_rx_frame);
stats->fcs_error = le32_to_cpu(get_log->fcs_error);
stats->tx_frame = le32_to_cpu(get_log->tx_frame);
stats->wep_icv_error[0] =
le32_to_cpu(get_log->wep_icv_err_cnt[0]);
stats->wep_icv_error[1] =
le32_to_cpu(get_log->wep_icv_err_cnt[1]);
stats->wep_icv_error[2] =
le32_to_cpu(get_log->wep_icv_err_cnt[2]);
stats->wep_icv_error[3] =
le32_to_cpu(get_log->wep_icv_err_cnt[3]);
}
return 0;
}
/*
* This function handles the command response of set/get Tx rate
* configurations.
*
* Handling includes changing the header fields into CPU format
* and saving the following parameters in driver -
* - DSSS rate bitmap
* - OFDM rate bitmap
* - HT MCS rate bitmaps
*
* Based on the new rate bitmaps, the function re-evaluates if
* auto data rate has been activated. If not, it sends another
* query to the firmware to get the current Tx data rate and updates
* the driver value.
*/
static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
void *data_buf)
{
struct mwifiex_adapter *adapter = priv->adapter;
struct mwifiex_rate_cfg *ds_rate = NULL;
struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg;
struct mwifiex_rate_scope *rate_scope;
struct mwifiex_ie_types_header *head = NULL;
u16 tlv, tlv_buf_len;
u8 *tlv_buf;
u32 i;
int ret = 0;
tlv_buf = (u8 *) ((u8 *) rate_cfg) +
sizeof(struct host_cmd_ds_tx_rate_cfg);
tlv_buf_len = *(u16 *) (tlv_buf + sizeof(u16));
while (tlv_buf && tlv_buf_len > 0) {
tlv = (*tlv_buf);
tlv = tlv | (*(tlv_buf + 1) << 8);
switch (tlv) {
case TLV_TYPE_RATE_SCOPE:
rate_scope = (struct mwifiex_rate_scope *) tlv_buf;
priv->bitmap_rates[0] =
le16_to_cpu(rate_scope->hr_dsss_rate_bitmap);
priv->bitmap_rates[1] =
le16_to_cpu(rate_scope->ofdm_rate_bitmap);
for (i = 0;
i <
sizeof(rate_scope->ht_mcs_rate_bitmap) /
sizeof(u16); i++)
priv->bitmap_rates[2 + i] =
le16_to_cpu(rate_scope->
ht_mcs_rate_bitmap[i]);
break;
/* Add RATE_DROP tlv here */
}
head = (struct mwifiex_ie_types_header *) tlv_buf;
tlv_buf += le16_to_cpu(head->len) + sizeof(*head);
tlv_buf_len -= le16_to_cpu(head->len);
}
priv->is_data_rate_auto = mwifiex_is_rate_auto(priv);
if (priv->is_data_rate_auto)
priv->data_rate = 0;
else
ret = mwifiex_prepare_cmd(priv,
HostCmd_CMD_802_11_TX_RATE_QUERY,
HostCmd_ACT_GEN_GET, 0, NULL, NULL);
if (data_buf) {
ds_rate = (struct mwifiex_rate_cfg *) data_buf;
if (le16_to_cpu(rate_cfg->action) == HostCmd_ACT_GEN_GET) {
if (priv->is_data_rate_auto) {
ds_rate->is_rate_auto = 1;
} else {
ds_rate->rate =
mwifiex_get_rate_index(adapter,
priv->
bitmap_rates,
sizeof(priv->
bitmap_rates));
if (ds_rate->rate >=
MWIFIEX_RATE_BITMAP_OFDM0
&& ds_rate->rate <=
MWIFIEX_RATE_BITMAP_OFDM7)
ds_rate->rate -=
(MWIFIEX_RATE_BITMAP_OFDM0 -
MWIFIEX_RATE_INDEX_OFDM0);
if (ds_rate->rate >=
MWIFIEX_RATE_BITMAP_MCS0
&& ds_rate->rate <=
MWIFIEX_RATE_BITMAP_MCS127)
ds_rate->rate -=
(MWIFIEX_RATE_BITMAP_MCS0 -
MWIFIEX_RATE_INDEX_MCS0);
}
}
}
return ret;
}
/*
* This function handles the command response of get Tx power level.
*
* Handling includes saving the maximum and minimum Tx power levels
* in driver, as well as sending the values to user.
*/
static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf)
{
int length = -1, max_power = -1, min_power = -1;
struct mwifiex_types_power_group *pg_tlv_hdr = NULL;
struct mwifiex_power_group *pg = NULL;
if (data_buf) {
pg_tlv_hdr =
(struct mwifiex_types_power_group *) ((u8 *) data_buf
+ sizeof(struct host_cmd_ds_txpwr_cfg));
pg = (struct mwifiex_power_group *) ((u8 *) pg_tlv_hdr +
sizeof(struct mwifiex_types_power_group));
length = pg_tlv_hdr->length;
if (length > 0) {
max_power = pg->power_max;
min_power = pg->power_min;
length -= sizeof(struct mwifiex_power_group);
}
while (length) {
pg++;
if (max_power < pg->power_max)
max_power = pg->power_max;
if (min_power > pg->power_min)
min_power = pg->power_min;
length -= sizeof(struct mwifiex_power_group);
}
if (pg_tlv_hdr->length > 0) {
priv->min_tx_power_level = (u8) min_power;
priv->max_tx_power_level = (u8) max_power;
}
} else {
return -1;
}
return 0;
}
/*
* This function handles the command response of set/get Tx power
* configurations.
*
* Handling includes changing the header fields into CPU format
* and saving the current Tx power level in driver.
*/
static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
void *data_buf)
{
struct mwifiex_adapter *adapter = priv->adapter;
struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg;
struct mwifiex_types_power_group *pg_tlv_hdr = NULL;
struct mwifiex_power_group *pg = NULL;
u16 action = le16_to_cpu(txp_cfg->action);
switch (action) {
case HostCmd_ACT_GEN_GET:
{
pg_tlv_hdr =
(struct mwifiex_types_power_group *) ((u8 *)
txp_cfg +
sizeof
(struct
host_cmd_ds_txpwr_cfg));
pg = (struct mwifiex_power_group *) ((u8 *)
pg_tlv_hdr +
sizeof(struct
mwifiex_types_power_group));
if (adapter->hw_status ==
MWIFIEX_HW_STATUS_INITIALIZING)
mwifiex_get_power_level(priv, txp_cfg);
priv->tx_power_level = (u16) pg->power_min;
break;
}
case HostCmd_ACT_GEN_SET:
if (le32_to_cpu(txp_cfg->mode)) {
pg_tlv_hdr =
(struct mwifiex_types_power_group *) ((u8 *)
txp_cfg +
sizeof
(struct
host_cmd_ds_txpwr_cfg));
pg = (struct mwifiex_power_group *) ((u8 *) pg_tlv_hdr
+
sizeof(struct
mwifiex_types_power_group));
if (pg->power_max == pg->power_min)
priv->tx_power_level = (u16) pg->power_min;
}
break;
default:
dev_err(adapter->dev, "CMD_RESP: unknown cmd action %d\n",
action);
return 0;
}
dev_dbg(adapter->dev,
"info: Current TxPower Level = %d, Max Power=%d, Min Power=%d\n",
priv->tx_power_level, priv->max_tx_power_level,
priv->min_tx_power_level);
return 0;
}
/*
* This function handles the command response of set/get MAC address.
*
* Handling includes saving the MAC address in driver.
*/
static int mwifiex_ret_802_11_mac_address(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp)
{
struct host_cmd_ds_802_11_mac_address *cmd_mac_addr =
&resp->params.mac_addr;
memcpy(priv->curr_addr, cmd_mac_addr->mac_addr, ETH_ALEN);
dev_dbg(priv->adapter->dev,
"info: set mac address: %pM\n", priv->curr_addr);
return 0;
}
/*
* This function handles the command response of set/get MAC multicast
* address.
*/
static int mwifiex_ret_mac_multicast_adr(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp)
{
return 0;
}
/*
* This function handles the command response of get Tx rate query.
*
* Handling includes changing the header fields into CPU format
* and saving the Tx rate and HT information parameters in driver.
*
* Both rate configuration and current data rate can be retrieved
* with this request.
*/
static int mwifiex_ret_802_11_tx_rate_query(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp)
{
struct mwifiex_adapter *adapter = priv->adapter;
priv->tx_rate = resp->params.tx_rate.tx_rate;
priv->tx_htinfo = resp->params.tx_rate.ht_info;
if (!priv->is_data_rate_auto)
priv->data_rate =
mwifiex_index_to_data_rate(adapter, priv->tx_rate,
priv->tx_htinfo);
return 0;
}
/*
* This function handles the command response of a deauthenticate
* command.
*
* If the deauthenticated MAC matches the current BSS MAC, the connection
* state is reset.
*/
static int mwifiex_ret_802_11_deauthenticate(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp)
{
struct mwifiex_adapter *adapter = priv->adapter;
adapter->dbg.num_cmd_deauth++;
if (!memcmp(resp->params.deauth.mac_addr,
&priv->curr_bss_params.bss_descriptor.mac_address,
sizeof(resp->params.deauth.mac_addr)))
mwifiex_reset_connect_state(priv);
return 0;
}
/*
* This function handles the command response of ad-hoc stop.
*
* The function resets the connection state in driver.
*/
static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp)
{
mwifiex_reset_connect_state(priv);
return 0;
}
/*
* This function handles the command response of set/get key material.
*
* Handling includes updating the driver parameters to reflect the
* changes.
*/
static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp)
{
struct host_cmd_ds_802_11_key_material *key =
&resp->params.key_material;
if (le16_to_cpu(key->action) == HostCmd_ACT_GEN_SET) {
if ((le16_to_cpu(key->key_param_set.key_info) &
KEY_INFO_TKIP_MCAST)) {
dev_dbg(priv->adapter->dev, "info: key: GTK is set\n");
priv->wpa_is_gtk_set = true;
priv->scan_block = false;
}
}
memset(priv->aes_key.key_param_set.key, 0,
sizeof(key->key_param_set.key));
priv->aes_key.key_param_set.key_len = key->key_param_set.key_len;
memcpy(priv->aes_key.key_param_set.key, key->key_param_set.key,
le16_to_cpu(priv->aes_key.key_param_set.key_len));
return 0;
}
/*
* This function handles the command response of get 11d domain information.
*/
static int mwifiex_ret_802_11d_domain_info(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp)
{
struct host_cmd_ds_802_11d_domain_info_rsp *domain_info =
&resp->params.domain_info_resp;
struct mwifiex_ietypes_domain_param_set *domain = &domain_info->domain;
u16 action = le16_to_cpu(domain_info->action);
u8 no_of_triplet = 0;
no_of_triplet = (u8) ((le16_to_cpu(domain->header.len) -
IEEE80211_COUNTRY_STRING_LEN) /
sizeof(struct ieee80211_country_ie_triplet));
dev_dbg(priv->adapter->dev, "info: 11D Domain Info Resp:"
" no_of_triplet=%d\n", no_of_triplet);
if (no_of_triplet > MWIFIEX_MAX_TRIPLET_802_11D) {
dev_warn(priv->adapter->dev,
"11D: invalid number of triplets %d "
"returned!!\n", no_of_triplet);
return -1;
}
switch (action) {
case HostCmd_ACT_GEN_SET: /* Proc Set Action */
break;
case HostCmd_ACT_GEN_GET:
break;
default:
dev_err(priv->adapter->dev,
"11D: invalid action:%d\n", domain_info->action);
return -1;
}
return 0;
}
/*
* This function handles the command response of get RF channel.
*
* Handling includes changing the header fields into CPU format
* and saving the new channel in driver.
*/
static int mwifiex_ret_802_11_rf_channel(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
void *data_buf)
{
struct host_cmd_ds_802_11_rf_channel *rf_channel =
&resp->params.rf_channel;
u16 new_channel = le16_to_cpu(rf_channel->current_channel);
if (priv->curr_bss_params.bss_descriptor.channel != new_channel) {
dev_dbg(priv->adapter->dev, "cmd: Channel Switch: %d to %d\n",
priv->curr_bss_params.bss_descriptor.channel,
new_channel);
/* Update the channel again */
priv->curr_bss_params.bss_descriptor.channel = new_channel;
}
if (data_buf)
*((u16 *)data_buf) = new_channel;
return 0;
}
/*
* This function handles the command response of get extended version.
*
* Handling includes forming the extended version string and sending it
* to application.
*/
static int mwifiex_ret_ver_ext(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp,
void *data_buf)
{
struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext;
struct host_cmd_ds_version_ext *version_ext = NULL;
if (data_buf) {
version_ext = (struct host_cmd_ds_version_ext *)data_buf;
version_ext->version_str_sel = ver_ext->version_str_sel;
memcpy(version_ext->version_str, ver_ext->version_str,
sizeof(char) * 128);
memcpy(priv->version_str, ver_ext->version_str, 128);
}
return 0;
}
/*
* This function handles the command response of register access.
*
* The register value and offset are returned to the user. For EEPROM
* access, the byte count is also returned.
*/
static int mwifiex_ret_reg_access(u16 type, struct host_cmd_ds_command *resp,
void *data_buf)
{
struct mwifiex_ds_reg_rw *reg_rw = NULL;
struct mwifiex_ds_read_eeprom *eeprom = NULL;
if (data_buf) {
reg_rw = (struct mwifiex_ds_reg_rw *) data_buf;
eeprom = (struct mwifiex_ds_read_eeprom *) data_buf;
switch (type) {
case HostCmd_CMD_MAC_REG_ACCESS:
{
struct host_cmd_ds_mac_reg_access *reg;
reg = (struct host_cmd_ds_mac_reg_access *)
&resp->params.mac_reg;
reg_rw->offset = cpu_to_le32(
(u32) le16_to_cpu(reg->offset));
reg_rw->value = reg->value;
break;
}
case HostCmd_CMD_BBP_REG_ACCESS:
{
struct host_cmd_ds_bbp_reg_access *reg;
reg = (struct host_cmd_ds_bbp_reg_access *)
&resp->params.bbp_reg;
reg_rw->offset = cpu_to_le32(
(u32) le16_to_cpu(reg->offset));
reg_rw->value = cpu_to_le32((u32) reg->value);
break;
}
case HostCmd_CMD_RF_REG_ACCESS:
{
struct host_cmd_ds_rf_reg_access *reg;
reg = (struct host_cmd_ds_rf_reg_access *)
&resp->params.rf_reg;
reg_rw->offset = cpu_to_le32(
(u32) le16_to_cpu(reg->offset));
reg_rw->value = cpu_to_le32((u32) reg->value);
break;
}
case HostCmd_CMD_PMIC_REG_ACCESS:
{
struct host_cmd_ds_pmic_reg_access *reg;
reg = (struct host_cmd_ds_pmic_reg_access *)
&resp->params.pmic_reg;
reg_rw->offset = cpu_to_le32(
(u32) le16_to_cpu(reg->offset));
reg_rw->value = cpu_to_le32((u32) reg->value);
break;
}
case HostCmd_CMD_CAU_REG_ACCESS:
{
struct host_cmd_ds_rf_reg_access *reg;
reg = (struct host_cmd_ds_rf_reg_access *)
&resp->params.rf_reg;
reg_rw->offset = cpu_to_le32(
(u32) le16_to_cpu(reg->offset));
reg_rw->value = cpu_to_le32((u32) reg->value);
break;
}
case HostCmd_CMD_802_11_EEPROM_ACCESS:
{
struct host_cmd_ds_802_11_eeprom_access
*cmd_eeprom =
(struct host_cmd_ds_802_11_eeprom_access
*) &resp->params.eeprom;
pr_debug("info: EEPROM read len=%x\n",
cmd_eeprom->byte_count);
if (le16_to_cpu(eeprom->byte_count) <
le16_to_cpu(
cmd_eeprom->byte_count)) {
eeprom->byte_count = cpu_to_le16(0);
pr_debug("info: EEPROM read "
"length is too big\n");
return -1;
}
eeprom->offset = cmd_eeprom->offset;
eeprom->byte_count = cmd_eeprom->byte_count;
if (le16_to_cpu(eeprom->byte_count) > 0)
memcpy(&eeprom->value,
&cmd_eeprom->value,
le16_to_cpu(eeprom->byte_count));
break;
}
default:
return -1;
}
}
return 0;
}
/*
* This function handles the command response of get IBSS coalescing status.
*
* If the received BSSID is different than the current one, the current BSSID,
* beacon interval, ATIM window and ERP information are updated, along with
* changing the ad-hoc state accordingly.
*/
static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp)
{
struct host_cmd_ds_802_11_ibss_status *ibss_coal_resp =
&(resp->params.ibss_coalescing);
u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
if (le16_to_cpu(ibss_coal_resp->action) == HostCmd_ACT_GEN_SET)
return 0;
dev_dbg(priv->adapter->dev,
"info: new BSSID %pM\n", ibss_coal_resp->bssid);
/* If rsp has NULL BSSID, Just return..... No Action */
if (!memcmp(ibss_coal_resp->bssid, zero_mac, ETH_ALEN)) {
dev_warn(priv->adapter->dev, "new BSSID is NULL\n");
return 0;
}
/* If BSSID is diff, modify current BSS parameters */
if (memcmp(priv->curr_bss_params.bss_descriptor.mac_address,
ibss_coal_resp->bssid, ETH_ALEN)) {
/* BSSID */
memcpy(priv->curr_bss_params.bss_descriptor.mac_address,
ibss_coal_resp->bssid, ETH_ALEN);
/* Beacon Interval */
priv->curr_bss_params.bss_descriptor.beacon_period
= le16_to_cpu(ibss_coal_resp->beacon_interval);
/* ERP Information */
priv->curr_bss_params.bss_descriptor.erp_flags =
(u8) le16_to_cpu(ibss_coal_resp->use_g_rate_protect);
priv->adhoc_state = ADHOC_COALESCED;
}
return 0;
}
/*
* This function handles the command responses.
*
* This is a generic function, which calls command specific
* response handlers based on the command ID.
*/
int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv,
u16 cmdresp_no, void *cmd_buf, void *wq_buf)
{
int ret = 0;
struct mwifiex_adapter *adapter = priv->adapter;
struct host_cmd_ds_command *resp =
(struct host_cmd_ds_command *) cmd_buf;
struct mwifiex_wait_queue *wait_queue =
(struct mwifiex_wait_queue *) wq_buf;
void *data_buf = adapter->curr_cmd->data_buf;
/* If the command is not successful, cleanup and return failure */
if (resp->result != HostCmd_RESULT_OK) {
mwifiex_process_cmdresp_error(priv, resp, wait_queue);
return -1;
}
/* Command successful, handle response */
switch (cmdresp_no) {
case HostCmd_CMD_GET_HW_SPEC:
ret = mwifiex_ret_get_hw_spec(priv, resp);
break;
case HostCmd_CMD_MAC_CONTROL:
break;
case HostCmd_CMD_802_11_MAC_ADDRESS:
ret = mwifiex_ret_802_11_mac_address(priv, resp);
break;
case HostCmd_CMD_MAC_MULTICAST_ADR:
ret = mwifiex_ret_mac_multicast_adr(priv, resp);
break;
case HostCmd_CMD_TX_RATE_CFG:
ret = mwifiex_ret_tx_rate_cfg(priv, resp, data_buf);
break;
case HostCmd_CMD_802_11_SCAN:
ret = mwifiex_ret_802_11_scan(priv, resp, wait_queue);
wait_queue = NULL;
adapter->curr_cmd->wq_buf = NULL;
break;
case HostCmd_CMD_802_11_BG_SCAN_QUERY:
ret = mwifiex_ret_802_11_scan(priv, resp, wait_queue);
dev_dbg(adapter->dev,
"info: CMD_RESP: BG_SCAN result is ready!\n");
break;
case HostCmd_CMD_TXPWR_CFG:
ret = mwifiex_ret_tx_power_cfg(priv, resp, data_buf);
break;
case HostCmd_CMD_802_11_PS_MODE_ENH:
ret = mwifiex_ret_enh_power_mode(priv, resp, data_buf);
break;
case HostCmd_CMD_802_11_HS_CFG_ENH:
ret = mwifiex_ret_802_11_hs_cfg(priv, resp);
break;
case HostCmd_CMD_802_11_ASSOCIATE:
ret = mwifiex_ret_802_11_associate(priv, resp, wait_queue);
break;
case HostCmd_CMD_802_11_DEAUTHENTICATE:
ret = mwifiex_ret_802_11_deauthenticate(priv, resp);
break;
case HostCmd_CMD_802_11_AD_HOC_START:
case HostCmd_CMD_802_11_AD_HOC_JOIN:
ret = mwifiex_ret_802_11_ad_hoc(priv, resp, wait_queue);
break;
case HostCmd_CMD_802_11_AD_HOC_STOP:
ret = mwifiex_ret_802_11_ad_hoc_stop(priv, resp);
break;
case HostCmd_CMD_802_11_GET_LOG:
ret = mwifiex_ret_get_log(priv, resp, data_buf);
break;
case HostCmd_CMD_RSSI_INFO:
ret = mwifiex_ret_802_11_rssi_info(priv, resp, data_buf);
break;
case HostCmd_CMD_802_11_SNMP_MIB:
ret = mwifiex_ret_802_11_snmp_mib(priv, resp, data_buf);
break;
case HostCmd_CMD_802_11_TX_RATE_QUERY:
ret = mwifiex_ret_802_11_tx_rate_query(priv, resp);
break;
case HostCmd_CMD_802_11_RF_CHANNEL:
ret = mwifiex_ret_802_11_rf_channel(priv, resp, data_buf);
break;
case HostCmd_CMD_VERSION_EXT:
ret = mwifiex_ret_ver_ext(priv, resp, data_buf);
break;
case HostCmd_CMD_FUNC_INIT:
case HostCmd_CMD_FUNC_SHUTDOWN:
break;
case HostCmd_CMD_802_11_KEY_MATERIAL:
ret = mwifiex_ret_802_11_key_material(priv, resp);
break;
case HostCmd_CMD_802_11D_DOMAIN_INFO:
ret = mwifiex_ret_802_11d_domain_info(priv, resp);
break;
case HostCmd_CMD_11N_ADDBA_REQ:
ret = mwifiex_ret_11n_addba_req(priv, resp);
break;
case HostCmd_CMD_11N_DELBA:
ret = mwifiex_ret_11n_delba(priv, resp);
break;
case HostCmd_CMD_11N_ADDBA_RSP:
ret = mwifiex_ret_11n_addba_resp(priv, resp);
break;
case HostCmd_CMD_RECONFIGURE_TX_BUFF:
adapter->tx_buf_size = (u16) le16_to_cpu(resp->params.
tx_buf.buff_size);
adapter->tx_buf_size = (adapter->tx_buf_size /
MWIFIEX_SDIO_BLOCK_SIZE) *
MWIFIEX_SDIO_BLOCK_SIZE;
adapter->curr_tx_buf_size = adapter->tx_buf_size;
dev_dbg(adapter->dev,
"cmd: max_tx_buf_size=%d, tx_buf_size=%d\n",
adapter->max_tx_buf_size, adapter->tx_buf_size);
if (adapter->if_ops.update_mp_end_port)
adapter->if_ops.update_mp_end_port(adapter,
le16_to_cpu(resp->
params.
tx_buf.
mp_end_port));
break;
case HostCmd_CMD_AMSDU_AGGR_CTRL:
ret = mwifiex_ret_amsdu_aggr_ctrl(priv, resp, data_buf);
break;
case HostCmd_CMD_WMM_GET_STATUS:
ret = mwifiex_ret_wmm_get_status(priv, resp);
break;
case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS:
ret = mwifiex_ret_ibss_coalescing_status(priv, resp);
break;
case HostCmd_CMD_MAC_REG_ACCESS:
case HostCmd_CMD_BBP_REG_ACCESS:
case HostCmd_CMD_RF_REG_ACCESS:
case HostCmd_CMD_PMIC_REG_ACCESS:
case HostCmd_CMD_CAU_REG_ACCESS:
case HostCmd_CMD_802_11_EEPROM_ACCESS:
ret = mwifiex_ret_reg_access(cmdresp_no, resp, data_buf);
break;
case HostCmd_CMD_SET_BSS_MODE:
break;
case HostCmd_CMD_11N_CFG:
ret = mwifiex_ret_11n_cfg(priv, resp, data_buf);
break;
default:
dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
resp->command);
break;
}
return ret;
}

View File

@ -0,0 +1,405 @@
/*
* Marvell Wireless LAN device driver: station event handling
*
* 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 resets the connection state.
*
* The function is invoked after receiving a disconnect event from firmware,
* and performs the following actions -
* - Set media status to disconnected
* - Clean up Tx and Rx packets
* - Resets SNR/NF/RSSI value in driver
* - Resets security configurations in driver
* - Enables auto data rate
* - Saves the previous SSID and BSSID so that they can
* be used for re-association, if required
* - Erases current SSID and BSSID information
* - Sends a disconnect event to upper layers/applications.
*/
void
mwifiex_reset_connect_state(struct mwifiex_private *priv)
{
struct mwifiex_adapter *adapter = priv->adapter;
if (!priv->media_connected)
return;
dev_dbg(adapter->dev, "info: handles disconnect event\n");
priv->media_connected = false;
priv->scan_block = false;
/* Free Tx and Rx packets, report disconnect to upper layer */
mwifiex_clean_txrx(priv);
/* Reset SNR/NF/RSSI values */
priv->data_rssi_last = 0;
priv->data_nf_last = 0;
priv->data_rssi_avg = 0;
priv->data_nf_avg = 0;
priv->bcn_rssi_last = 0;
priv->bcn_nf_last = 0;
priv->bcn_rssi_avg = 0;
priv->bcn_nf_avg = 0;
priv->rxpd_rate = 0;
priv->rxpd_htinfo = 0;
priv->sec_info.wpa_enabled = false;
priv->sec_info.wpa2_enabled = false;
priv->wpa_ie_len = 0;
priv->sec_info.wapi_enabled = false;
priv->wapi_ie_len = 0;
priv->sec_info.wapi_key_on = false;
priv->sec_info.encryption_mode = MWIFIEX_ENCRYPTION_MODE_NONE;
/* Enable auto data rate */
priv->is_data_rate_auto = true;
priv->data_rate = 0;
if (priv->bss_mode == MWIFIEX_BSS_MODE_IBSS) {
priv->adhoc_state = ADHOC_IDLE;
priv->adhoc_is_link_sensed = false;
}
/*
* Memorize the previous SSID and BSSID so
* it could be used for re-assoc
*/
dev_dbg(adapter->dev, "info: previous SSID=%s, SSID len=%u\n",
priv->prev_ssid.ssid, priv->prev_ssid.ssid_len);
dev_dbg(adapter->dev, "info: current SSID=%s, SSID len=%u\n",
priv->curr_bss_params.bss_descriptor.ssid.ssid,
priv->curr_bss_params.bss_descriptor.ssid.ssid_len);
memcpy(&priv->prev_ssid,
&priv->curr_bss_params.bss_descriptor.ssid,
sizeof(struct mwifiex_802_11_ssid));
memcpy(priv->prev_bssid,
priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN);
/* Need to erase the current SSID and BSSID info */
memset(&priv->curr_bss_params, 0x00, sizeof(priv->curr_bss_params));
adapter->tx_lock_flag = false;
adapter->pps_uapsd_mode = false;
if (adapter->num_cmd_timeout && adapter->curr_cmd)
return;
priv->media_connected = false;
if (!priv->disconnect) {
priv->disconnect = 1;
dev_dbg(adapter->dev, "info: successfully disconnected from"
" %pM: reason code %d\n", priv->cfg_bssid,
WLAN_REASON_DEAUTH_LEAVING);
cfg80211_disconnected(priv->netdev,
WLAN_REASON_DEAUTH_LEAVING, NULL, 0,
GFP_KERNEL);
queue_work(priv->workqueue, &priv->cfg_workqueue);
}
if (!netif_queue_stopped(priv->netdev))
netif_stop_queue(priv->netdev);
if (netif_carrier_ok(priv->netdev))
netif_carrier_off(priv->netdev);
/* Reset wireless stats signal info */
priv->w_stats.qual.level = 0;
priv->w_stats.qual.noise = 0;
}
/*
* This function handles events generated by firmware.
*
* This is a generic function and handles all events.
*
* Event specific routines are called by this function based
* upon the generated event cause.
*
* For the following events, the function just forwards them to upper
* layers, optionally recording the change -
* - EVENT_LINK_SENSED
* - EVENT_MIC_ERR_UNICAST
* - EVENT_MIC_ERR_MULTICAST
* - EVENT_PORT_RELEASE
* - EVENT_RSSI_LOW
* - EVENT_SNR_LOW
* - EVENT_MAX_FAIL
* - EVENT_RSSI_HIGH
* - EVENT_SNR_HIGH
* - EVENT_DATA_RSSI_LOW
* - EVENT_DATA_SNR_LOW
* - EVENT_DATA_RSSI_HIGH
* - EVENT_DATA_SNR_HIGH
* - EVENT_LINK_QUALITY
* - EVENT_PRE_BEACON_LOST
* - EVENT_IBSS_COALESCED
* - EVENT_WEP_ICV_ERR
* - EVENT_BW_CHANGE
* - EVENT_HOSTWAKE_STAIE
*
* For the following events, no action is taken -
* - EVENT_MIB_CHANGED
* - EVENT_INIT_DONE
* - EVENT_DUMMY_HOST_WAKEUP_SIGNAL
*
* Rest of the supported events requires driver handling -
* - EVENT_DEAUTHENTICATED
* - EVENT_DISASSOCIATED
* - EVENT_LINK_LOST
* - EVENT_PS_SLEEP
* - EVENT_PS_AWAKE
* - EVENT_DEEP_SLEEP_AWAKE
* - EVENT_HS_ACT_REQ
* - EVENT_ADHOC_BCN_LOST
* - EVENT_BG_SCAN_REPORT
* - EVENT_WMM_STATUS_CHANGE
* - EVENT_ADDBA
* - EVENT_DELBA
* - EVENT_BA_STREAM_TIEMOUT
* - EVENT_AMSDU_AGGR_CTRL
*/
int mwifiex_process_sta_event(struct mwifiex_private *priv)
{
struct mwifiex_adapter *adapter = priv->adapter;
int ret = 0;
u32 eventcause = adapter->event_cause;
switch (eventcause) {
case EVENT_DUMMY_HOST_WAKEUP_SIGNAL:
dev_err(adapter->dev, "invalid EVENT: DUMMY_HOST_WAKEUP_SIGNAL,"
" ignoring it\n");
break;
case EVENT_LINK_SENSED:
dev_dbg(adapter->dev, "event: LINK_SENSED\n");
if (!netif_carrier_ok(priv->netdev))
netif_carrier_on(priv->netdev);
if (netif_queue_stopped(priv->netdev))
netif_wake_queue(priv->netdev);
break;
case EVENT_DEAUTHENTICATED:
dev_dbg(adapter->dev, "event: Deauthenticated\n");
adapter->dbg.num_event_deauth++;
if (priv->media_connected)
mwifiex_reset_connect_state(priv);
break;
case EVENT_DISASSOCIATED:
dev_dbg(adapter->dev, "event: Disassociated\n");
adapter->dbg.num_event_disassoc++;
if (priv->media_connected)
mwifiex_reset_connect_state(priv);
break;
case EVENT_LINK_LOST:
dev_dbg(adapter->dev, "event: Link lost\n");
adapter->dbg.num_event_link_lost++;
if (priv->media_connected)
mwifiex_reset_connect_state(priv);
break;
case EVENT_PS_SLEEP:
dev_dbg(adapter->dev, "info: EVENT: SLEEP\n");
adapter->ps_state = PS_STATE_PRE_SLEEP;
mwifiex_check_ps_cond(adapter);
break;
case EVENT_PS_AWAKE:
dev_dbg(adapter->dev, "info: EVENT: AWAKE\n");
if (!adapter->pps_uapsd_mode &&
priv->media_connected &&
adapter->sleep_period.period) {
adapter->pps_uapsd_mode = true;
dev_dbg(adapter->dev,
"event: PPS/UAPSD mode activated\n");
}
adapter->tx_lock_flag = false;
if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) {
if (mwifiex_check_last_packet_indication(priv)) {
if (!adapter->data_sent) {
if (!mwifiex_send_null_packet(priv,
MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET
|
MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET))
adapter->ps_state =
PS_STATE_SLEEP;
return 0;
}
}
}
adapter->ps_state = PS_STATE_AWAKE;
adapter->pm_wakeup_card_req = false;
adapter->pm_wakeup_fw_try = false;
break;
case EVENT_DEEP_SLEEP_AWAKE:
adapter->if_ops.wakeup_complete(adapter);
dev_dbg(adapter->dev, "event: DS_AWAKE\n");
if (adapter->is_deep_sleep)
adapter->is_deep_sleep = false;
break;
case EVENT_HS_ACT_REQ:
dev_dbg(adapter->dev, "event: HS_ACT_REQ\n");
ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_802_11_HS_CFG_ENH,
0, 0, NULL, NULL);
break;
case EVENT_MIC_ERR_UNICAST:
dev_dbg(adapter->dev, "event: UNICAST MIC ERROR\n");
break;
case EVENT_MIC_ERR_MULTICAST:
dev_dbg(adapter->dev, "event: MULTICAST MIC ERROR\n");
break;
case EVENT_MIB_CHANGED:
case EVENT_INIT_DONE:
break;
case EVENT_ADHOC_BCN_LOST:
dev_dbg(adapter->dev, "event: ADHOC_BCN_LOST\n");
priv->adhoc_is_link_sensed = false;
mwifiex_clean_txrx(priv);
if (!netif_queue_stopped(priv->netdev))
netif_stop_queue(priv->netdev);
if (netif_carrier_ok(priv->netdev))
netif_carrier_off(priv->netdev);
break;
case EVENT_BG_SCAN_REPORT:
dev_dbg(adapter->dev, "event: BGS_REPORT\n");
/* Clear the previous scan result */
memset(adapter->scan_table, 0x00,
sizeof(struct mwifiex_bssdescriptor) * IW_MAX_AP);
adapter->num_in_scan_table = 0;
adapter->bcn_buf_end = adapter->bcn_buf;
ret = mwifiex_prepare_cmd(priv,
HostCmd_CMD_802_11_BG_SCAN_QUERY,
HostCmd_ACT_GEN_GET, 0, NULL, NULL);
break;
case EVENT_PORT_RELEASE:
dev_dbg(adapter->dev, "event: PORT RELEASE\n");
break;
case EVENT_WMM_STATUS_CHANGE:
dev_dbg(adapter->dev, "event: WMM status changed\n");
ret = mwifiex_prepare_cmd(priv, HostCmd_CMD_WMM_GET_STATUS,
0, 0, NULL, NULL);
break;
case EVENT_RSSI_LOW:
dev_dbg(adapter->dev, "event: Beacon RSSI_LOW\n");
break;
case EVENT_SNR_LOW:
dev_dbg(adapter->dev, "event: Beacon SNR_LOW\n");
break;
case EVENT_MAX_FAIL:
dev_dbg(adapter->dev, "event: MAX_FAIL\n");
break;
case EVENT_RSSI_HIGH:
dev_dbg(adapter->dev, "event: Beacon RSSI_HIGH\n");
break;
case EVENT_SNR_HIGH:
dev_dbg(adapter->dev, "event: Beacon SNR_HIGH\n");
break;
case EVENT_DATA_RSSI_LOW:
dev_dbg(adapter->dev, "event: Data RSSI_LOW\n");
break;
case EVENT_DATA_SNR_LOW:
dev_dbg(adapter->dev, "event: Data SNR_LOW\n");
break;
case EVENT_DATA_RSSI_HIGH:
dev_dbg(adapter->dev, "event: Data RSSI_HIGH\n");
break;
case EVENT_DATA_SNR_HIGH:
dev_dbg(adapter->dev, "event: Data SNR_HIGH\n");
break;
case EVENT_LINK_QUALITY:
dev_dbg(adapter->dev, "event: Link Quality\n");
break;
case EVENT_PRE_BEACON_LOST:
dev_dbg(adapter->dev, "event: Pre-Beacon Lost\n");
break;
case EVENT_IBSS_COALESCED:
dev_dbg(adapter->dev, "event: IBSS_COALESCED\n");
ret = mwifiex_prepare_cmd(priv,
HostCmd_CMD_802_11_IBSS_COALESCING_STATUS,
HostCmd_ACT_GEN_GET, 0, NULL, NULL);
break;
case EVENT_ADDBA:
dev_dbg(adapter->dev, "event: ADDBA Request\n");
mwifiex_prepare_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP,
HostCmd_ACT_GEN_SET, 0, NULL,
adapter->event_body);
break;
case EVENT_DELBA:
dev_dbg(adapter->dev, "event: DELBA Request\n");
mwifiex_11n_delete_ba_stream(priv, adapter->event_body);
break;
case EVENT_BA_STREAM_TIEMOUT:
dev_dbg(adapter->dev, "event: BA Stream timeout\n");
mwifiex_11n_ba_stream_timeout(priv,
(struct host_cmd_ds_11n_batimeout
*)
adapter->event_body);
break;
case EVENT_AMSDU_AGGR_CTRL:
dev_dbg(adapter->dev, "event: AMSDU_AGGR_CTRL %d\n",
*(u16 *) adapter->event_body);
adapter->tx_buf_size =
min(adapter->curr_tx_buf_size,
le16_to_cpu(*(__le16 *) adapter->event_body));
dev_dbg(adapter->dev, "event: tx_buf_size %d\n",
adapter->tx_buf_size);
break;
case EVENT_WEP_ICV_ERR:
dev_dbg(adapter->dev, "event: WEP ICV error\n");
break;
case EVENT_BW_CHANGE:
dev_dbg(adapter->dev, "event: BW Change\n");
break;
case EVENT_HOSTWAKE_STAIE:
dev_dbg(adapter->dev, "event: HOSTWAKE_STAIE %d\n", eventcause);
break;
default:
dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
eventcause);
break;
}
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
/*
* Marvell Wireless LAN device driver: station RX data handling
*
* 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 "11n_aggr.h"
#include "11n_rxreorder.h"
/*
* This function processes the received packet and forwards it
* to kernel/upper layer.
*
* This function parses through the received packet and determines
* if it is a debug packet or normal packet.
*
* For non-debug packets, the function chops off unnecessary leading
* header bytes, reconstructs the packet as an ethernet frame or
* 802.2/llc/snap frame as required, and sends it to kernel/upper layer.
*
* The completion callback is called after processing in complete.
*/
int mwifiex_process_rx_packet(struct mwifiex_adapter *adapter,
struct sk_buff *skb)
{
int ret = 0;
struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
struct mwifiex_private *priv = adapter->priv[rx_info->bss_index];
struct rx_packet_hdr *rx_pkt_hdr;
struct rxpd *local_rx_pd;
int hdr_chop;
struct ethhdr *eth_hdr;
u8 rfc1042_eth_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
local_rx_pd = (struct rxpd *) (skb->data);
rx_pkt_hdr = (struct rx_packet_hdr *) ((u8 *) local_rx_pd +
local_rx_pd->rx_pkt_offset);
if (!memcmp(&rx_pkt_hdr->rfc1042_hdr,
rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr))) {
/*
* Replace the 803 header and rfc1042 header (llc/snap) with an
* EthernetII header, keep the src/dst and snap_type
* (ethertype).
* The firmware only passes up SNAP frames converting
* all RX Data from 802.11 to 802.2/LLC/SNAP frames.
* To create the Ethernet II, just move the src, dst address
* right before the snap_type.
*/
eth_hdr = (struct ethhdr *)
((u8 *) &rx_pkt_hdr->eth803_hdr
+ sizeof(rx_pkt_hdr->eth803_hdr) +
sizeof(rx_pkt_hdr->rfc1042_hdr)
- sizeof(rx_pkt_hdr->eth803_hdr.h_dest)
- sizeof(rx_pkt_hdr->eth803_hdr.h_source)
- sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type));
memcpy(eth_hdr->h_source, rx_pkt_hdr->eth803_hdr.h_source,
sizeof(eth_hdr->h_source));
memcpy(eth_hdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest,
sizeof(eth_hdr->h_dest));
/* Chop off the rxpd + the excess memory from the 802.2/llc/snap
header that was removed. */
hdr_chop = (u8 *) eth_hdr - (u8 *) local_rx_pd;
} else {
/* Chop off the rxpd */
hdr_chop = (u8 *) &rx_pkt_hdr->eth803_hdr -
(u8 *) local_rx_pd;
}
/* Chop off the leading header bytes so the it points to the start of
either the reconstructed EthII frame or the 802.2/llc/snap frame */
skb_pull(skb, hdr_chop);
priv->rxpd_rate = local_rx_pd->rx_rate;
priv->rxpd_htinfo = local_rx_pd->ht_info;
ret = mwifiex_recv_packet(adapter, skb);
if (ret == -1)
dev_err(adapter->dev, "recv packet failed\n");
return ret;
}
/*
* This function processes the received buffer.
*
* The function looks into the RxPD and performs sanity tests on the
* received buffer to ensure its a valid packet, before processing it
* further. If the packet is determined to be aggregated, it is
* de-aggregated accordingly. Non-unicast packets are sent directly to
* the kernel/upper layers. Unicast packets are handed over to the
* Rx reordering routine if 11n is enabled.
*
* The completion callback is called after processing in complete.
*/
int mwifiex_process_sta_rx_packet(struct mwifiex_adapter *adapter,
struct sk_buff *skb)
{
int ret = 0;
struct rxpd *local_rx_pd;
struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
struct rx_packet_hdr *rx_pkt_hdr;
u8 ta[ETH_ALEN];
u16 rx_pkt_type = 0;
struct mwifiex_private *priv = adapter->priv[rx_info->bss_index];
local_rx_pd = (struct rxpd *) (skb->data);
rx_pkt_type = local_rx_pd->rx_pkt_type;
rx_pkt_hdr = (struct rx_packet_hdr *) ((u8 *) local_rx_pd +
local_rx_pd->rx_pkt_offset);
if ((local_rx_pd->rx_pkt_offset + local_rx_pd->rx_pkt_length) >
(u16) skb->len) {
dev_err(adapter->dev, "wrong rx packet: len=%d,"
" rx_pkt_offset=%d, rx_pkt_length=%d\n", skb->len,
local_rx_pd->rx_pkt_offset, local_rx_pd->rx_pkt_length);
priv->stats.rx_dropped++;
dev_kfree_skb_any(skb);
return ret;
}
if (local_rx_pd->rx_pkt_type == PKT_TYPE_AMSDU) {
mwifiex_11n_deaggregate_pkt(priv, skb);
return ret;
}
/*
* If the packet is not an unicast packet then send the packet
* directly to os. Don't pass thru rx reordering
*/
if (!IS_11N_ENABLED(priv) ||
memcmp(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN)) {
mwifiex_process_rx_packet(adapter, skb);
return ret;
}
if (mwifiex_queuing_ra_based(priv)) {
memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN);
} else {
if (rx_pkt_type != PKT_TYPE_BAR)
priv->rx_seq[local_rx_pd->priority] =
local_rx_pd->seq_num;
memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address,
ETH_ALEN);
}
/* Reorder and send to OS */
ret = mwifiex_11n_rx_reorder_pkt(priv, local_rx_pd->seq_num,
local_rx_pd->priority, ta,
(u8) local_rx_pd->rx_pkt_type,
(void *) skb);
if (ret || (rx_pkt_type == PKT_TYPE_BAR)) {
if (priv && (ret == -1))
priv->stats.rx_dropped++;
dev_kfree_skb_any(skb);
}
return ret;
}

View File

@ -0,0 +1,202 @@
/*
* Marvell Wireless LAN device driver: station TX data handling
*
* 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"
/*
* This function fills the TxPD for tx packets.
*
* The Tx buffer received by this function should already have the
* header space allocated for TxPD.
*
* This function inserts the TxPD in between interface header and actual
* data and adjusts the buffer pointers accordingly.
*
* The following TxPD fields are set by this function, as required -
* - BSS number
* - Tx packet length and offset
* - Priority
* - Packet delay
* - Priority specific Tx control
* - Flags
*/
void *mwifiex_process_sta_txpd(struct mwifiex_private *priv,
struct sk_buff *skb)
{
struct mwifiex_adapter *adapter = priv->adapter;
struct txpd *local_tx_pd;
struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
if (!skb->len) {
dev_err(adapter->dev, "Tx: bad packet length: %d\n",
skb->len);
tx_info->status_code = MWIFIEX_ERROR_PKT_SIZE_INVALID;
return skb->data;
}
BUG_ON(skb_headroom(skb) < (sizeof(*local_tx_pd) + INTF_HEADER_LEN));
skb_push(skb, sizeof(*local_tx_pd));
local_tx_pd = (struct txpd *) skb->data;
memset(local_tx_pd, 0, sizeof(struct txpd));
local_tx_pd->bss_num = priv->bss_num;
local_tx_pd->bss_type = priv->bss_type;
local_tx_pd->tx_pkt_length = cpu_to_le16((u16) (skb->len -
sizeof(struct txpd)));
local_tx_pd->priority = (u8) skb->priority;
local_tx_pd->pkt_delay_2ms =
mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
if (local_tx_pd->priority <
ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
/*
* Set the priority specific tx_control field, setting of 0 will
* cause the default value to be used later in this function
*/
local_tx_pd->tx_control =
cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[local_tx_pd->
priority]);
if (adapter->pps_uapsd_mode) {
if (mwifiex_check_last_packet_indication(priv)) {
adapter->tx_lock_flag = true;
local_tx_pd->flags =
MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET;
}
}
/* Offset of actual data */
local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd));
/* make space for INTF_HEADER_LEN */
skb_push(skb, INTF_HEADER_LEN);
if (!local_tx_pd->tx_control)
/* TxCtrl set by user or default */
local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
return skb->data;
}
/*
* This function tells firmware to send a NULL data packet.
*
* The function creates a NULL data packet with TxPD and sends to the
* firmware for transmission, with highest priority setting.
*/
int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags)
{
struct mwifiex_adapter *adapter = priv->adapter;
struct txpd *local_tx_pd;
/* sizeof(struct txpd) + Interface specific header */
#define NULL_PACKET_HDR 64
u32 data_len = NULL_PACKET_HDR;
struct sk_buff *skb = NULL;
int ret = 0;
struct mwifiex_txinfo *tx_info = NULL;
if (adapter->surprise_removed)
return -1;
if (!priv->media_connected)
return -1;
if (adapter->data_sent)
return -1;
skb = dev_alloc_skb(data_len);
if (!skb)
return -1;
tx_info = MWIFIEX_SKB_TXCB(skb);
tx_info->bss_index = priv->bss_index;
skb_reserve(skb, sizeof(struct txpd) + INTF_HEADER_LEN);
skb_push(skb, sizeof(struct txpd));
local_tx_pd = (struct txpd *) skb->data;
local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
local_tx_pd->flags = flags;
local_tx_pd->priority = WMM_HIGHEST_PRIORITY;
local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd));
local_tx_pd->bss_num = priv->bss_num;
local_tx_pd->bss_type = priv->bss_type;
skb_push(skb, INTF_HEADER_LEN);
ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
skb->data, skb->len, NULL);
switch (ret) {
case -EBUSY:
adapter->data_sent = true;
/* Fall through FAILURE handling */
case -1:
dev_kfree_skb_any(skb);
dev_err(adapter->dev, "%s: host_to_card failed: ret=%d\n",
__func__, ret);
adapter->dbg.num_tx_host_to_card_failure++;
break;
case 0:
dev_kfree_skb_any(skb);
dev_dbg(adapter->dev, "data: %s: host_to_card succeeded\n",
__func__);
adapter->tx_lock_flag = true;
break;
case -EINPROGRESS:
break;
default:
break;
}
return ret;
}
/*
* This function checks if we need to send last packet indication.
*/
u8
mwifiex_check_last_packet_indication(struct mwifiex_private *priv)
{
struct mwifiex_adapter *adapter = priv->adapter;
u8 ret = false;
u8 prop_ps = true;
if (!adapter->sleep_period.period)
return ret;
if (mwifiex_wmm_lists_empty(adapter)) {
if ((priv->curr_bss_params.wmm_uapsd_enabled &&
priv->wmm_qosinfo) || prop_ps)
ret = true;
}
if (ret && !adapter->cmd_sent && !adapter->curr_cmd
&& !is_command_pending(adapter)) {
adapter->delay_null_pkt = false;
ret = true;
} else {
ret = false;
adapter->delay_null_pkt = true;
}
return ret;
}

View File

@ -0,0 +1,202 @@
/*
* Marvell Wireless LAN device driver: generic TX/RX data handling
*
* 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"
/*
* This function processes the received buffer.
*
* Main responsibility of this function is to parse the RxPD to
* identify the correct interface this packet is headed for and
* forwarding it to the associated handling function, where the
* packet will be further processed and sent to kernel/upper layer
* if required.
*/
int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter,
struct sk_buff *skb)
{
int ret = 0;
struct mwifiex_private *priv =
mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
struct rxpd *local_rx_pd;
struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
local_rx_pd = (struct rxpd *) (skb->data);
/* Get the BSS number from rxpd, get corresponding priv */
priv = mwifiex_get_priv_by_id(adapter, local_rx_pd->bss_num &
BSS_NUM_MASK, local_rx_pd->bss_type);
if (!priv)
priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
rx_info->bss_index = priv->bss_index;
ret = mwifiex_process_sta_rx_packet(adapter, skb);
return ret;
}
EXPORT_SYMBOL_GPL(mwifiex_handle_rx_packet);
/*
* This function sends a packet to device.
*
* It processes the packet to add the TxPD, checks condition and
* sends the processed packet to firmware for transmission.
*
* On successful completion, the function calls the completion callback
* and logs the time.
*/
int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb,
struct mwifiex_tx_param *tx_param)
{
int ret = -1;
struct mwifiex_adapter *adapter = priv->adapter;
u8 *head_ptr = NULL;
struct txpd *local_tx_pd = NULL;
head_ptr = (u8 *) mwifiex_process_sta_txpd(priv, skb);
if (head_ptr) {
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)
local_tx_pd =
(struct txpd *) (head_ptr + INTF_HEADER_LEN);
ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
skb->data, skb->len, tx_param);
}
switch (ret) {
case -EBUSY:
if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
(adapter->pps_uapsd_mode) &&
(adapter->tx_lock_flag)) {
priv->adapter->tx_lock_flag = false;
local_tx_pd->flags = 0;
}
dev_dbg(adapter->dev, "data: -EBUSY is returned\n");
break;
case -1:
adapter->data_sent = false;
dev_err(adapter->dev, "mwifiex_write_data_async failed: 0x%X\n",
ret);
adapter->dbg.num_tx_host_to_card_failure++;
mwifiex_write_data_complete(adapter, skb, ret);
break;
case -EINPROGRESS:
adapter->data_sent = false;
break;
case 0:
mwifiex_write_data_complete(adapter, skb, ret);
break;
default:
break;
}
return ret;
}
/*
* Packet send completion callback handler.
*
* It either frees the buffer directly or forwards it to another
* completion callback which checks conditions, updates statistics,
* wakes up stalled traffic queue if required, and then frees the buffer.
*/
int mwifiex_write_data_complete(struct mwifiex_adapter *adapter,
struct sk_buff *skb, int status)
{
struct mwifiex_private *priv = NULL, *tpriv = NULL;
struct mwifiex_txinfo *tx_info = NULL;
int i;
if (!skb)
return 0;
tx_info = MWIFIEX_SKB_TXCB(skb);
priv = mwifiex_bss_index_to_priv(adapter, tx_info->bss_index);
if (!priv)
goto done;
priv->netdev->trans_start = jiffies;
if (!status) {
priv->stats.tx_packets++;
priv->stats.tx_bytes += skb->len;
} else {
priv->stats.tx_errors++;
}
atomic_dec(&adapter->tx_pending);
for (i = 0; i < adapter->priv_num; i++) {
tpriv = adapter->priv[i];
if ((GET_BSS_ROLE(tpriv) == MWIFIEX_BSS_ROLE_STA)
&& (tpriv->media_connected)) {
if (netif_queue_stopped(tpriv->netdev))
netif_wake_queue(tpriv->netdev);
}
}
done:
dev_kfree_skb_any(skb);
return 0;
}
/*
* Packet receive completion callback handler.
*
* This function calls another completion callback handler which
* updates the statistics, and optionally updates the parent buffer
* use count before freeing the received packet.
*/
int mwifiex_recv_packet_complete(struct mwifiex_adapter *adapter,
struct sk_buff *skb, int status)
{
struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
struct mwifiex_rxinfo *rx_info_parent = NULL;
struct mwifiex_private *priv;
struct sk_buff *skb_parent = NULL;
unsigned long flags;
priv = adapter->priv[rx_info->bss_index];
if (priv && (status == -1))
priv->stats.rx_dropped++;
if (rx_info->parent) {
skb_parent = rx_info->parent;
rx_info_parent = MWIFIEX_SKB_RXCB(skb_parent);
spin_lock_irqsave(&priv->rx_pkt_lock, flags);
--rx_info_parent->use_count;
if (!rx_info_parent->use_count) {
spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
dev_kfree_skb_any(skb_parent);
} else {
spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
}
} else {
dev_kfree_skb_any(skb);
}
return 0;
}

View File

@ -0,0 +1,252 @@
/*
* Marvell Wireless LAN device driver: utility functions
*
* 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"
/*
* Firmware initialization complete callback handler.
*
* This function wakes up the function waiting on the init
* wait queue for the firmware initialization to complete.
*/
int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter)
{
adapter->init_wait_q_woken = true;
wake_up_interruptible(&adapter->init_wait_q);
return 0;
}
/*
* Firmware shutdown complete callback handler.
*
* This function sets the hardware status to not ready and wakes up
* the function waiting on the init wait queue for the firmware
* shutdown to complete.
*/
int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter)
{
adapter->hw_status = MWIFIEX_HW_STATUS_NOT_READY;
adapter->init_wait_q_woken = true;
wake_up_interruptible(&adapter->init_wait_q);
return 0;
}
/*
* IOCTL request handler to send function init/shutdown command
* to firmware.
*
* This function prepares the correct firmware command and
* issues it.
*/
int mwifiex_misc_ioctl_init_shutdown(struct mwifiex_adapter *adapter,
struct mwifiex_wait_queue *wait,
u32 func_init_shutdown)
{
struct mwifiex_private *priv = adapter->priv[wait->bss_index];
int ret;
u16 cmd;
if (func_init_shutdown == MWIFIEX_FUNC_INIT) {
cmd = HostCmd_CMD_FUNC_INIT;
} else if (func_init_shutdown == MWIFIEX_FUNC_SHUTDOWN) {
cmd = HostCmd_CMD_FUNC_SHUTDOWN;
} else {
dev_err(adapter->dev, "unsupported parameter\n");
return -1;
}
/* Send command to firmware */
ret = mwifiex_prepare_cmd(priv, cmd, HostCmd_ACT_GEN_SET,
0, wait, NULL);
if (!ret)
ret = -EINPROGRESS;
return ret;
}
/*
* IOCTL request handler to set/get debug information.
*
* This function collates/sets the information from/to different driver
* structures.
*/
int mwifiex_get_debug_info(struct mwifiex_private *priv,
struct mwifiex_debug_info *info)
{
struct mwifiex_adapter *adapter = priv->adapter;
if (info) {
memcpy(info->packets_out,
priv->wmm.packets_out,
sizeof(priv->wmm.packets_out));
info->max_tx_buf_size = (u32) adapter->max_tx_buf_size;
info->tx_buf_size = (u32) adapter->tx_buf_size;
info->rx_tbl_num = mwifiex_get_rx_reorder_tbl(
priv, info->rx_tbl);
info->tx_tbl_num = mwifiex_get_tx_ba_stream_tbl(
priv, info->tx_tbl);
info->ps_mode = adapter->ps_mode;
info->ps_state = adapter->ps_state;
info->is_deep_sleep = adapter->is_deep_sleep;
info->pm_wakeup_card_req = adapter->pm_wakeup_card_req;
info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try;
info->is_hs_configured = adapter->is_hs_configured;
info->hs_activated = adapter->hs_activated;
info->num_cmd_host_to_card_failure
= adapter->dbg.num_cmd_host_to_card_failure;
info->num_cmd_sleep_cfm_host_to_card_failure
= adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure;
info->num_tx_host_to_card_failure
= adapter->dbg.num_tx_host_to_card_failure;
info->num_event_deauth = adapter->dbg.num_event_deauth;
info->num_event_disassoc = adapter->dbg.num_event_disassoc;
info->num_event_link_lost = adapter->dbg.num_event_link_lost;
info->num_cmd_deauth = adapter->dbg.num_cmd_deauth;
info->num_cmd_assoc_success =
adapter->dbg.num_cmd_assoc_success;
info->num_cmd_assoc_failure =
adapter->dbg.num_cmd_assoc_failure;
info->num_tx_timeout = adapter->dbg.num_tx_timeout;
info->num_cmd_timeout = adapter->dbg.num_cmd_timeout;
info->timeout_cmd_id = adapter->dbg.timeout_cmd_id;
info->timeout_cmd_act = adapter->dbg.timeout_cmd_act;
memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id,
sizeof(adapter->dbg.last_cmd_id));
memcpy(info->last_cmd_act, adapter->dbg.last_cmd_act,
sizeof(adapter->dbg.last_cmd_act));
info->last_cmd_index = adapter->dbg.last_cmd_index;
memcpy(info->last_cmd_resp_id, adapter->dbg.last_cmd_resp_id,
sizeof(adapter->dbg.last_cmd_resp_id));
info->last_cmd_resp_index = adapter->dbg.last_cmd_resp_index;
memcpy(info->last_event, adapter->dbg.last_event,
sizeof(adapter->dbg.last_event));
info->last_event_index = adapter->dbg.last_event_index;
info->data_sent = adapter->data_sent;
info->cmd_sent = adapter->cmd_sent;
info->cmd_resp_received = adapter->cmd_resp_received;
}
return 0;
}
/*
* This function processes the received packet before sending it to the
* kernel.
*
* It extracts the SKB from the received buffer and sends it to kernel.
* In case the received buffer does not contain the data in SKB format,
* the function creates a blank SKB, fills it with the data from the
* received buffer and then sends this new SKB to the kernel.
*/
int mwifiex_recv_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb)
{
struct mwifiex_rxinfo *rx_info = NULL;
struct mwifiex_private *priv = NULL;
if (!skb)
return -1;
rx_info = MWIFIEX_SKB_RXCB(skb);
priv = mwifiex_bss_index_to_priv(adapter, rx_info->bss_index);
if (!priv)
return -1;
skb->dev = priv->netdev;
skb->protocol = eth_type_trans(skb, priv->netdev);
skb->ip_summed = CHECKSUM_NONE;
priv->stats.rx_bytes += skb->len;
priv->stats.rx_packets++;
if (in_interrupt())
netif_rx(skb);
else
netif_rx_ni(skb);
return 0;
}
/*
* Receive packet completion callback handler.
*
* This function updates the statistics and frees the buffer SKB.
*/
int mwifiex_recv_complete(struct mwifiex_adapter *adapter,
struct sk_buff *skb, int status)
{
struct mwifiex_private *priv = NULL;
struct mwifiex_rxinfo *rx_info = NULL;
if (!skb)
return 0;
rx_info = MWIFIEX_SKB_RXCB(skb);
priv = mwifiex_bss_index_to_priv(adapter, rx_info->bss_index);
if (priv && (status == -1))
priv->stats.rx_dropped++;
dev_kfree_skb_any(skb);
return 0;
}
/*
* IOCTL completion callback handler.
*
* This function is called when a pending IOCTL is completed.
*
* If work queue support is enabled, the function wakes up the
* corresponding waiting function. Otherwise, it processes the
* IOCTL response and frees the response buffer.
*/
int mwifiex_ioctl_complete(struct mwifiex_adapter *adapter,
struct mwifiex_wait_queue *wait_queue,
int status)
{
enum mwifiex_error_code status_code =
(enum mwifiex_error_code) wait_queue->status;
atomic_dec(&adapter->ioctl_pending);
dev_dbg(adapter->dev, "cmd: IOCTL completed: status=%d,"
" status_code=%#x\n", status, status_code);
if (wait_queue->enabled) {
*wait_queue->condition = true;
wait_queue->status = status;
if (status && (status_code == MWIFIEX_ERROR_CMD_TIMEOUT))
dev_err(adapter->dev, "cmd timeout\n");
else
wake_up_interruptible(wait_queue->wait);
} else {
if (status)
dev_err(adapter->dev, "cmd failed: status_code=%#x\n",
status_code);
kfree(wait_queue);
}
return 0;
}

View File

@ -0,0 +1,32 @@
/*
* Marvell Wireless LAN device driver: utility functions
*
* 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_UTIL_H_
#define _MWIFIEX_UTIL_H_
static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb)
{
return (struct mwifiex_rxinfo *)skb->cb;
}
static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb)
{
return (struct mwifiex_txinfo *)skb->cb;
}
#endif /* !_MWIFIEX_UTIL_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,112 @@
/*
* Marvell Wireless LAN device driver: WMM
*
* 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_WMM_H_
#define _MWIFIEX_WMM_H_
enum ieee_types_wmm_aciaifsn_bitmasks {
MWIFIEX_AIFSN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)),
MWIFIEX_ACM = BIT(4),
MWIFIEX_ACI = (BIT(5) | BIT(6)),
};
enum ieee_types_wmm_ecw_bitmasks {
MWIFIEX_ECW_MIN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)),
MWIFIEX_ECW_MAX = (BIT(4) | BIT(5) | BIT(6) | BIT(7)),
};
/*
* This function retrieves the TID of the given RA list.
*/
static inline int
mwifiex_get_tid(struct mwifiex_adapter *adapter,
struct mwifiex_ra_list_tbl *ptr)
{
struct sk_buff *skb;
if (skb_queue_empty(&ptr->skb_head))
return 0;
skb = skb_peek(&ptr->skb_head);
return skb->priority;
}
/*
* This function gets the length of a list.
*/
static inline int
mwifiex_wmm_list_len(struct mwifiex_adapter *adapter, struct list_head *head)
{
struct list_head *pos;
int count = 0;
list_for_each(pos, head)
++count;
return count;
}
/*
* This function checks if a RA list is empty or not.
*/
static inline u8
mwifiex_wmm_is_ra_list_empty(struct mwifiex_adapter *adapter,
struct list_head *ra_list_hhead)
{
struct mwifiex_ra_list_tbl *ra_list;
int is_list_empty;
list_for_each_entry(ra_list, ra_list_hhead, list) {
is_list_empty = skb_queue_empty(&ra_list->skb_head);
if (!is_list_empty)
return false;
}
return true;
}
void mwifiex_wmm_add_buf_txqueue(struct mwifiex_adapter *adapter,
struct sk_buff *skb);
void mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra);
int mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter);
void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter);
int mwifiex_is_ralist_valid(struct mwifiex_private *priv,
struct mwifiex_ra_list_tbl *ra_list, int tid);
u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv,
const struct sk_buff *skb);
void mwifiex_wmm_init(struct mwifiex_adapter *adapter);
extern u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv,
u8 **assoc_buf,
struct ieee_types_wmm_parameter
*wmmie,
struct ieee80211_ht_cap
*htcap);
void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv,
struct ieee_types_wmm_parameter
*wmm_ie);
void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv);
extern int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
const struct host_cmd_ds_command *resp);
#endif /* !_MWIFIEX_WMM_H_ */