Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,382 @@
/*
* Marvell Wireless LAN device driver: 802.11ac
*
* Copyright (C) 2013-2014, 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 "fw.h"
#include "main.h"
#include "11ac.h"
/* Tables of the MCS map to the highest data rate (in Mbps) supported
* for long GI.
*/
static const u16 max_rate_lgi_80MHZ[8][3] = {
{0x124, 0x15F, 0x186}, /* NSS = 1 */
{0x249, 0x2BE, 0x30C}, /* NSS = 2 */
{0x36D, 0x41D, 0x492}, /* NSS = 3 */
{0x492, 0x57C, 0x618}, /* NSS = 4 */
{0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */
{0x6DB, 0x83A, 0x0}, /* NSS = 6 */
{0x7FF, 0x999, 0xAAA}, /* NSS = 7 */
{0x924, 0xAF8, 0xC30} /* NSS = 8 */
};
static const u16 max_rate_lgi_160MHZ[8][3] = {
{0x249, 0x2BE, 0x30C}, /* NSS = 1 */
{0x492, 0x57C, 0x618}, /* NSS = 2 */
{0x6DB, 0x83A, 0x0}, /* NSS = 3 */
{0x924, 0xAF8, 0xC30}, /* NSS = 4 */
{0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */
{0xDB6, 0x1074, 0x1248}, /* NSS = 6 */
{0xFFF, 0x1332, 0x1554}, /* NSS = 7 */
{0x1248, 0x15F0, 0x1860} /* NSS = 8 */
};
/* This function converts the 2-bit MCS map to the highest long GI
* VHT data rate.
*/
static u16
mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv,
u8 bands, u16 mcs_map)
{
u8 i, nss, mcs;
u16 max_rate = 0;
u32 usr_vht_cap_info = 0;
struct mwifiex_adapter *adapter = priv->adapter;
if (bands & BAND_AAC)
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a;
else
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
/* find the max NSS supported */
nss = 1;
for (i = 1; i <= 8; i++) {
mcs = GET_VHTNSSMCS(mcs_map, i);
if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED)
nss = i;
}
mcs = GET_VHTNSSMCS(mcs_map, nss);
/* if mcs is 3, nss must be 1 (NSS = 1). Default mcs to MCS 0~9 */
if (mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED)
mcs = IEEE80211_VHT_MCS_SUPPORT_0_9;
if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) {
/* support 160 MHz */
max_rate = max_rate_lgi_160MHZ[nss - 1][mcs];
if (!max_rate)
/* MCS9 is not supported in NSS6 */
max_rate = max_rate_lgi_160MHZ[nss - 1][mcs - 1];
} else {
max_rate = max_rate_lgi_80MHZ[nss - 1][mcs];
if (!max_rate)
/* MCS9 is not supported in NSS3 */
max_rate = max_rate_lgi_80MHZ[nss - 1][mcs - 1];
}
return max_rate;
}
static void
mwifiex_fill_vht_cap_info(struct mwifiex_private *priv,
struct ieee80211_vht_cap *vht_cap, u8 bands)
{
struct mwifiex_adapter *adapter = priv->adapter;
if (bands & BAND_A)
vht_cap->vht_cap_info =
cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a);
else
vht_cap->vht_cap_info =
cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg);
}
void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv,
struct ieee80211_vht_cap *vht_cap, u8 bands)
{
struct mwifiex_adapter *adapter = priv->adapter;
u16 mcs_map_user, mcs_map_resp, mcs_map_result;
u16 mcs_user, mcs_resp, nss, tmp;
/* Fill VHT cap info */
mwifiex_fill_vht_cap_info(priv, vht_cap, bands);
/* rx MCS Set: find the minimum of the user rx mcs and ap rx mcs */
mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support);
mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map);
mcs_map_result = 0;
for (nss = 1; nss <= 8; nss++) {
mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) ||
(mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED))
SET_VHTNSSMCS(mcs_map_result, nss,
IEEE80211_VHT_MCS_NOT_SUPPORTED);
else
SET_VHTNSSMCS(mcs_map_result, nss,
min(mcs_user, mcs_resp));
}
vht_cap->supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result);
tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
vht_cap->supp_mcs.rx_highest = cpu_to_le16(tmp);
/* tx MCS Set: find the minimum of the user tx mcs and ap tx mcs */
mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support);
mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
mcs_map_result = 0;
for (nss = 1; nss <= 8; nss++) {
mcs_user = GET_VHTNSSMCS(mcs_map_user, nss);
mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss);
if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) ||
(mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED))
SET_VHTNSSMCS(mcs_map_result, nss,
IEEE80211_VHT_MCS_NOT_SUPPORTED);
else
SET_VHTNSSMCS(mcs_map_result, nss,
min(mcs_user, mcs_resp));
}
vht_cap->supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result);
tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result);
vht_cap->supp_mcs.tx_highest = cpu_to_le16(tmp);
return;
}
int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv,
struct mwifiex_bssdescriptor *bss_desc,
u8 **buffer)
{
struct mwifiex_ie_types_vhtcap *vht_cap;
struct mwifiex_ie_types_oper_mode_ntf *oper_ntf;
struct ieee_types_oper_mode_ntf *ieee_oper_ntf;
struct mwifiex_ie_types_vht_oper *vht_op;
struct mwifiex_adapter *adapter = priv->adapter;
u8 supp_chwd_set;
u32 usr_vht_cap_info;
int ret_len = 0;
if (bss_desc->bss_band & BAND_A)
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a;
else
usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg;
/* VHT Capabilities IE */
if (bss_desc->bcn_vht_cap) {
vht_cap = (struct mwifiex_ie_types_vhtcap *)*buffer;
memset(vht_cap, 0, sizeof(*vht_cap));
vht_cap->header.type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY);
vht_cap->header.len =
cpu_to_le16(sizeof(struct ieee80211_vht_cap));
memcpy((u8 *)vht_cap + sizeof(struct mwifiex_ie_types_header),
(u8 *)bss_desc->bcn_vht_cap,
le16_to_cpu(vht_cap->header.len));
mwifiex_fill_vht_cap_tlv(priv, &vht_cap->vht_cap,
bss_desc->bss_band);
*buffer += sizeof(*vht_cap);
ret_len += sizeof(*vht_cap);
}
/* VHT Operation IE */
if (bss_desc->bcn_vht_oper) {
if (priv->bss_mode == NL80211_IFTYPE_STATION) {
vht_op = (struct mwifiex_ie_types_vht_oper *)*buffer;
memset(vht_op, 0, sizeof(*vht_op));
vht_op->header.type =
cpu_to_le16(WLAN_EID_VHT_OPERATION);
vht_op->header.len = cpu_to_le16(sizeof(*vht_op) -
sizeof(struct mwifiex_ie_types_header));
memcpy((u8 *)vht_op +
sizeof(struct mwifiex_ie_types_header),
(u8 *)bss_desc->bcn_vht_oper,
le16_to_cpu(vht_op->header.len));
/* negotiate the channel width and central freq
* and keep the central freq as the peer suggests
*/
supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info);
switch (supp_chwd_set) {
case 0:
vht_op->chan_width =
min_t(u8, IEEE80211_VHT_CHANWIDTH_80MHZ,
bss_desc->bcn_vht_oper->chan_width);
break;
case 1:
vht_op->chan_width =
min_t(u8, IEEE80211_VHT_CHANWIDTH_160MHZ,
bss_desc->bcn_vht_oper->chan_width);
break;
case 2:
vht_op->chan_width =
min_t(u8, IEEE80211_VHT_CHANWIDTH_80P80MHZ,
bss_desc->bcn_vht_oper->chan_width);
break;
default:
vht_op->chan_width =
IEEE80211_VHT_CHANWIDTH_USE_HT;
break;
}
*buffer += sizeof(*vht_op);
ret_len += sizeof(*vht_op);
}
}
/* Operating Mode Notification IE */
if (bss_desc->oper_mode) {
ieee_oper_ntf = bss_desc->oper_mode;
oper_ntf = (void *)*buffer;
memset(oper_ntf, 0, sizeof(*oper_ntf));
oper_ntf->header.type = cpu_to_le16(WLAN_EID_OPMODE_NOTIF);
oper_ntf->header.len = cpu_to_le16(sizeof(u8));
oper_ntf->oper_mode = ieee_oper_ntf->oper_mode;
*buffer += sizeof(*oper_ntf);
ret_len += sizeof(*oper_ntf);
}
return ret_len;
}
int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd, u16 cmd_action,
struct mwifiex_11ac_vht_cfg *cfg)
{
struct host_cmd_11ac_vht_cfg *vhtcfg = &cmd->params.vht_cfg;
cmd->command = cpu_to_le16(HostCmd_CMD_11AC_CFG);
cmd->size = cpu_to_le16(sizeof(struct host_cmd_11ac_vht_cfg) +
S_DS_GEN);
vhtcfg->action = cpu_to_le16(cmd_action);
vhtcfg->band_config = cfg->band_config;
vhtcfg->misc_config = cfg->misc_config;
vhtcfg->cap_info = cpu_to_le32(cfg->cap_info);
vhtcfg->mcs_tx_set = cpu_to_le32(cfg->mcs_tx_set);
vhtcfg->mcs_rx_set = cpu_to_le32(cfg->mcs_rx_set);
return 0;
}
/* This function initializes the BlockACK setup information for given
* mwifiex_private structure for 11ac enabled networks.
*/
void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv)
{
priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT;
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
priv->add_ba_param.tx_win_size =
MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE;
priv->add_ba_param.rx_win_size =
MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE;
} else {
priv->add_ba_param.tx_win_size =
MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE;
priv->add_ba_param.rx_win_size =
MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE;
}
return;
}
bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv)
{
struct mwifiex_bssdescriptor *bss_desc;
struct ieee80211_vht_operation *vht_oper;
bss_desc = &priv->curr_bss_params.bss_descriptor;
vht_oper = bss_desc->bcn_vht_oper;
if (!bss_desc->bcn_vht_cap || !vht_oper)
return false;
if (vht_oper->chan_width == IEEE80211_VHT_CHANWIDTH_USE_HT)
return false;
return true;
}
u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band,
u32 pri_chan, u8 chan_bw)
{
u8 center_freq_idx = 0;
if (band & BAND_AAC) {
switch (pri_chan) {
case 36:
case 40:
case 44:
case 48:
if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
center_freq_idx = 42;
break;
case 52:
case 56:
case 60:
case 64:
if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
center_freq_idx = 58;
else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ)
center_freq_idx = 50;
break;
case 100:
case 104:
case 108:
case 112:
if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
center_freq_idx = 106;
break;
case 116:
case 120:
case 124:
case 128:
if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
center_freq_idx = 122;
else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ)
center_freq_idx = 114;
break;
case 132:
case 136:
case 140:
case 144:
if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
center_freq_idx = 138;
break;
case 149:
case 153:
case 157:
case 161:
if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ)
center_freq_idx = 155;
break;
default:
center_freq_idx = 42;
}
}
return center_freq_idx;
}

View file

@ -0,0 +1,45 @@
/*
* Marvell Wireless LAN device driver: 802.11ac
*
* Copyright (C) 2013-2014, 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_11AC_H_
#define _MWIFIEX_11AC_H_
#define VHT_CFG_2GHZ BIT(0)
#define VHT_CFG_5GHZ BIT(1)
enum vht_cfg_misc_config {
VHT_CAP_TX_OPERATION = 1,
VHT_CAP_ASSOCIATION,
VHT_CAP_UAP_ONLY
};
#define DEFAULT_VHT_MCS_SET 0xfffa
#define DISABLE_VHT_MCS_SET 0xffff
#define VHT_BW_80_160_80P80 BIT(2)
int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv,
struct mwifiex_bssdescriptor *bss_desc,
u8 **buffer);
int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd, u16 cmd_action,
struct mwifiex_11ac_vht_cfg *cfg);
void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv,
struct ieee80211_vht_cap *vht_cap, u8 bands);
#endif /* _MWIFIEX_11AC_H_ */

View file

@ -0,0 +1,101 @@
/*
* Marvell Wireless LAN device driver: 802.11h
*
* Copyright (C) 2013-2014, 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 "main.h"
#include "fw.h"
/* This function appends 11h info to a buffer while joining an
* infrastructure BSS
*/
static void
mwifiex_11h_process_infra_join(struct mwifiex_private *priv, u8 **buffer,
struct mwifiex_bssdescriptor *bss_desc)
{
struct mwifiex_ie_types_header *ie_header;
struct mwifiex_ie_types_pwr_capability *cap;
struct mwifiex_ie_types_local_pwr_constraint *constraint;
struct ieee80211_supported_band *sband;
u8 radio_type;
int i;
if (!buffer || !(*buffer))
return;
radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
sband = priv->wdev->wiphy->bands[radio_type];
cap = (struct mwifiex_ie_types_pwr_capability *)*buffer;
cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY);
cap->header.len = cpu_to_le16(2);
cap->min_pwr = 0;
cap->max_pwr = 0;
*buffer += sizeof(*cap);
constraint = (struct mwifiex_ie_types_local_pwr_constraint *)*buffer;
constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT);
constraint->header.len = cpu_to_le16(2);
constraint->chan = bss_desc->channel;
constraint->constraint = bss_desc->local_constraint;
*buffer += sizeof(*constraint);
ie_header = (struct mwifiex_ie_types_header *)*buffer;
ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH);
ie_header->len = cpu_to_le16(2 * sband->n_channels + 2);
*buffer += sizeof(*ie_header);
*(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS;
*(*buffer)++ = 2 * sband->n_channels;
for (i = 0; i < sband->n_channels; i++) {
*(*buffer)++ = ieee80211_frequency_to_channel(
sband->channels[i].center_freq);
*(*buffer)++ = 1; /* one channel in the subband */
}
}
/* Enable or disable the 11h extensions in the firmware */
static int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag)
{
u32 enable = flag;
return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
HostCmd_ACT_GEN_SET, DOT11H_I, &enable, true);
}
/* This functions processes TLV buffer for a pending BSS Join command.
*
* Activate 11h functionality in the firmware if the spectrum management
* capability bit is found in the network we are joining. Also, necessary
* TLVs are set based on requested network's 11h capability.
*/
void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer,
struct mwifiex_bssdescriptor *bss_desc)
{
if (bss_desc->sensed_11h) {
/* Activate 11h functions in firmware, turns on capability
* bit
*/
mwifiex_11h_activate(priv, true);
bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT;
mwifiex_11h_process_infra_join(priv, buffer, bss_desc);
} else {
/* Deactivate 11h functions in the firmware */
mwifiex_11h_activate(priv, false);
bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT;
}
}

View file

@ -0,0 +1,794 @@
/*
* Marvell Wireless LAN device driver: 802.11n
*
* Copyright (C) 2011-2014, 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.
*
* HT capability information field, AMPDU Parameters field, supported MCS set
* fields are retrieved from cfg80211 stack
*
* RD responder bit to set to clear in the extended capability header.
*/
int mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type,
struct ieee80211_ht_cap *ht_cap)
{
uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info);
struct ieee80211_supported_band *sband =
priv->wdev->wiphy->bands[radio_type];
if (WARN_ON_ONCE(!sband)) {
dev_err(priv->adapter->dev, "Invalid radio type!\n");
return -EINVAL;
}
ht_cap->ampdu_params_info =
(sband->ht_cap.ampdu_factor &
IEEE80211_HT_AMPDU_PARM_FACTOR) |
((sband->ht_cap.ampdu_density <<
IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) &
IEEE80211_HT_AMPDU_PARM_DENSITY);
memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs,
sizeof(sband->ht_cap.mcs));
if (priv->bss_mode == NL80211_IFTYPE_STATION ||
(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
(priv->adapter->sec_chan_offset !=
IEEE80211_HT_PARAM_CHA_SEC_NONE)))
/* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */
SETHT_MCS32(ht_cap->mcs.rx_mask);
/* Clear RD responder bit */
ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER;
ht_cap->cap_info = cpu_to_le16(sband->ht_cap.cap);
ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap);
if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap))
ht_cap->tx_BF_cap_info = cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP);
return 0;
}
/*
* This function returns the pointer to an entry in BA Stream
* table which matches the requested BA status.
*/
static struct mwifiex_tx_ba_stream_tbl *
mwifiex_get_ba_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 = &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_del_ba_tbl(priv, tid, del_ba->peer_mac_addr,
TYPE_DELBA_SENT,
INITIATOR_BIT(del_ba_param_set));
tx_ba_tbl = mwifiex_get_ba_status(priv, BA_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))
return 0;
mwifiex_create_ba_tbl(priv, del_ba->peer_mac_addr, tid,
BA_SETUP_INPROGRESS);
tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS);
if (tx_ba_tbl)
mwifiex_del_ba_tbl(priv, tx_ba_tbl->tid, tx_ba_tbl->ra,
TYPE_DELBA_SENT, true);
}
return 0;
}
/*
* This function handles the command response of add a block
* ack request.
*
* Handling includes changing the header fields to CPU formats, checking
* the response success status and taking actions accordingly (delete the
* BA stream table in case of failure).
*/
int 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 = &resp->params.add_ba_rsp;
struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl;
u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set);
add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn))
& SSN_MASK);
tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK)
>> BLOCKACKPARAM_TID_POS;
if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) {
mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr,
TYPE_DELBA_SENT, true);
if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT)
priv->aggr_prio_tbl[tid].ampdu_ap =
BA_STREAM_NOT_ALLOWED;
return 0;
}
tx_ba_tbl = mwifiex_get_ba_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_SETUP_COMPLETE;
if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) &&
priv->add_ba_param.tx_amsdu &&
(priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
tx_ba_tbl->amsdu = true;
else
tx_ba_tbl->amsdu = false;
} else {
dev_err(priv->adapter->dev, "BA stream not created\n");
}
return 0;
}
/*
* This function prepares command of reconfigure Tx buffer.
*
* Preparation includes -
* - Setting command ID, action and proper size
* - Setting Tx buffer size (for SET only)
* - Ensuring correct endian-ness
*/
int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd, int cmd_action,
u16 *buf_size)
{
struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf;
u16 action = (u16) cmd_action;
cmd->command = cpu_to_le16(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 host_cmd_ds_command *cmd,
int cmd_action,
struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl)
{
struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl =
&cmd->params.amsdu_aggr_ctrl;
u16 action = (u16) cmd_action;
cmd->command = cpu_to_le16(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 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,
struct mwifiex_ds_11n_tx_cfg *txcfg)
{
struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg;
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);
if (priv->adapter->is_hw_11ac_capable)
htcfg->misc_config = cpu_to_le16(txcfg->misc_config);
return 0;
}
/*
* This function appends an 11n TLV to a buffer.
*
* Buffer allocation is responsibility of the calling
* function. No size validation is made here.
*
* The function fills up the following sections, if applicable -
* - HT capability IE
* - HT information IE (with channel list)
* - 20/40 BSS Coexistence IE
* - HT Extended Capabilities IE
*/
int
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;
struct ieee80211_supported_band *sband;
struct ieee_types_header *hdr;
u8 radio_type;
if (!buffer || !*buffer)
return ret_len;
radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
sband = priv->wdev->wiphy->bands[radio_type];
if (bss_desc->bcn_ht_cap) {
ht_cap = (struct 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,
le16_to_cpu(ht_cap->header.len));
mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap);
*buffer += sizeof(struct mwifiex_ie_types_htcap);
ret_len += sizeof(struct mwifiex_ie_types_htcap);
}
if (bss_desc->bcn_ht_oper) {
if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
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_OPERATION);
ht_info->header.len =
cpu_to_le16(
sizeof(struct ieee80211_ht_operation));
memcpy((u8 *) ht_info +
sizeof(struct mwifiex_ie_types_header),
(u8 *)bss_desc->bcn_ht_oper,
le16_to_cpu(ht_info->header.len));
if (!(sband->ht_cap.cap &
IEEE80211_HT_CAP_SUP_WIDTH_20_40))
ht_info->ht_oper.ht_param &=
~(IEEE80211_HT_PARAM_CHAN_WIDTH_ANY |
IEEE80211_HT_PARAM_CHA_SEC_OFFSET);
*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_oper->primary_chan;
chan_list->chan_scan_param[0].radio_type =
mwifiex_band_to_radio_type((u8) bss_desc->bss_band);
if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
bss_desc->bcn_ht_oper->ht_param &
IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)
SET_SECONDARYCHAN(chan_list->chan_scan_param[0].
radio_type,
(bss_desc->bcn_ht_oper->ht_param &
IEEE80211_HT_PARAM_CHA_SEC_OFFSET));
*buffer += 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),
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) {
hdr = (void *)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(hdr->len);
memcpy((u8 *)ext_cap->ext_capab,
bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header),
le16_to_cpu(ext_cap->header.len));
if (hdr->len > 3 &&
ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED)
priv->hs2_enabled = true;
else
priv->hs2_enabled = false;
*buffer += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;
ret_len += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;
}
return ret_len;
}
/*
* This function checks if the given pointer is valid entry of
* Tx BA Stream table.
*/
static int 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);
}
/*
* 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_get_ba_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 (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) &&
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_create_ba_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_get_ba_tbl(priv, tid, ra)) {
new_node = kzalloc(sizeof(struct mwifiex_tx_ba_stream_tbl),
GFP_ATOMIC);
if (!new_node)
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);
}
}
/*
* 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;
u32 tx_win_size = priv->add_ba_param.tx_win_size;
static u8 dialog_tok;
int ret;
u16 block_ack_param_set;
dev_dbg(priv->adapter->dev, "cmd: %s: tid %d\n", __func__, tid);
if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
priv->adapter->is_hw_11ac_capable &&
memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) {
struct mwifiex_sta_node *sta_ptr;
sta_ptr = mwifiex_get_sta_entry(priv, peer_mac);
if (!sta_ptr) {
dev_warn(priv->adapter->dev,
"BA setup with unknown TDLS peer %pM!\n",
peer_mac);
return -1;
}
if (sta_ptr->is_11ac_enabled)
tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE;
}
block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) |
tx_win_size << BLOCKACKPARAM_WINSIZE_POS |
IMMEDIATE_BLOCK_ACK);
/* enable AMSDU inside AMPDU */
if (priv->add_ba_param.tx_amsdu &&
(priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK;
add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set);
add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout);
++dialog_tok;
if (dialog_tok == 0)
dialog_tok = 1;
add_ba_req.dialog_token = dialog_tok;
memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN);
/* We don't wait for the response of this command */
ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ,
0, 0, &add_ba_req, false);
return ret;
}
/*
* This function sends a delete BA request to the given TID/RA pair.
*/
int 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_send_cmd(priv, HostCmd_CMD_11N_DELBA,
HostCmd_ACT_GEN_SET, 0, &delba, false);
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_del_ba_tbl(priv, tid, cmd_del_ba->peer_mac_addr,
TYPE_DELBA_RECEIVE, INITIATOR_BIT(del_ba_param_set));
}
/*
* This function retrieves the Rx reordering table.
*/
int 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->amsdu = tx_ba_tsr_tbl->amsdu;
rx_reo_tbl++;
count++;
if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED)
break;
}
spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
return count;
}
/*
* This function retrieves the entry for specific tx BA stream table by RA and
* deletes it.
*/
void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra)
{
struct mwifiex_tx_ba_stream_tbl *tbl, *tmp;
unsigned long flags;
if (!ra)
return;
spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
list_for_each_entry_safe(tbl, tmp, &priv->tx_ba_stream_tbl_ptr, list) {
if (!memcmp(tbl->ra, ra, ETH_ALEN)) {
spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock,
flags);
mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, tbl);
spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags);
}
}
spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags);
return;
}
/* This function initializes the BlockACK setup information for given
* mwifiex_private structure.
*/
void mwifiex_set_ba_params(struct mwifiex_private *priv)
{
priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT;
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
priv->add_ba_param.tx_win_size =
MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE;
priv->add_ba_param.rx_win_size =
MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE;
} else {
priv->add_ba_param.tx_win_size =
MWIFIEX_STA_AMPDU_DEF_TXWINSIZE;
priv->add_ba_param.rx_win_size =
MWIFIEX_STA_AMPDU_DEF_RXWINSIZE;
}
priv->add_ba_param.tx_amsdu = true;
priv->add_ba_param.rx_amsdu = true;
return;
}
u8 mwifiex_get_sec_chan_offset(int chan)
{
u8 sec_offset;
switch (chan) {
case 36:
case 44:
case 52:
case 60:
case 100:
case 108:
case 116:
case 124:
case 132:
case 140:
case 149:
case 157:
sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
break;
case 40:
case 48:
case 56:
case 64:
case 104:
case 112:
case 120:
case 128:
case 136:
case 144:
case 153:
case 161:
sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
break;
case 165:
default:
sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
break;
}
return sec_offset;
}

View file

@ -0,0 +1,211 @@
/*
* Marvell Wireless LAN device driver: 802.11n
*
* Copyright (C) 2011-2014, 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"
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_cmd_11n_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd, u16 cmd_action,
struct mwifiex_ds_11n_tx_cfg *txcfg);
int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv,
struct mwifiex_bssdescriptor *bss_desc,
u8 **buffer);
int mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type,
struct ieee80211_ht_cap *);
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_get_ba_tbl(struct
mwifiex_private
*priv, int tid,
u8 *ra);
void mwifiex_create_ba_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_cmd_recfg_tx_buf(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
int cmd_action, u16 *buf_size);
int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd,
int cmd_action,
struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl);
void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra);
u8 mwifiex_get_sec_chan_offset(int chan);
static inline u8
mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv,
struct mwifiex_ra_list_tbl *ptr, int tid)
{
struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ptr->ra);
if (unlikely(!node))
return false;
return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false;
}
/* This function checks whether AMSDU is allowed for BA stream. */
static inline u8
mwifiex_is_amsdu_in_ampdu_allowed(struct mwifiex_private *priv,
struct mwifiex_ra_list_tbl *ptr, int tid)
{
struct mwifiex_tx_ba_stream_tbl *tx_tbl;
tx_tbl = mwifiex_get_ba_tbl(priv, tid, ptr->ra);
if (tx_tbl)
return tx_tbl->amsdu;
return false;
}
/* 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)
{
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) {
return mwifiex_is_station_ampdu_allowed(priv, ptr, tid);
} else {
if (ptr->tdls_link)
return mwifiex_is_station_ampdu_allowed(priv, ptr, 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, int tid)
{
return (((priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) &&
(priv->is_data_rate_auto || !(priv->bitmap_rates[2] & 0x03)))
? true : false);
}
/*
* This function checks whether a space is available for new BA stream or not.
*/
static inline u8 mwifiex_space_avail_for_new_ba_stream(
struct mwifiex_adapter *adapter)
{
struct mwifiex_private *priv;
u8 i;
u32 ba_stream_num = 0;
for (i = 0; i < adapter->priv_num; i++) {
priv = adapter->priv[i];
if (priv)
ba_stream_num += mwifiex_wmm_list_len(
&priv->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, 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_get_ba_tbl(priv, tid, ptr->ra);
if (tx_tbl && IS_BASTREAM_SETUP(tx_tbl))
return true;
return false;
}
/*
* This function checks whether associated station is 11n enabled
*/
static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv,
struct mwifiex_sta_node *node)
{
if (!node || (priv->bss_role != MWIFIEX_BSS_ROLE_UAP) ||
!priv->ap_11n_enabled)
return 0;
return node->is_11n_enabled;
}
static inline u8
mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, const u8 *ra)
{
struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ra);
if (node)
return node->is_11n_enabled;
return false;
}
#endif /* !_MWIFIEX_11N_H_ */

View file

@ -0,0 +1,310 @@
/*
* Marvell Wireless LAN device driver: 802.11n Aggregation
*
* Copyright (C) 2011-2014, 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 sk_buff *skb_aggr,
struct sk_buff *skb_src, int *pad)
{
int dt_offset;
struct rfc_1042_hdr snap = {
0xaa, /* LLC DSAP */
0xaa, /* LLC SSAP */
0x03, /* LLC CTRL */
{0x00, 0x00, 0x00}, /* SNAP OUI */
0x0000 /* SNAP type */
/*
* This field will be overwritten
* later with ethertype
*/
};
struct tx_packet_hdr *tx_header;
tx_header = (void *)skb_put(skb_aggr, sizeof(*tx_header));
/* Copy DA and SA */
dt_offset = 2 * ETH_ALEN;
memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset);
/* Copy SNAP header */
snap.snap_type = ((struct ethhdr *)skb_src->data)->h_proto;
dt_offset += sizeof(__be16);
memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr));
skb_pull(skb_src, dt_offset);
/* Update Length field */
tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN);
/* Add payload */
memcpy(skb_put(skb_aggr, skb_src->len), skb_src->data, skb_src->len);
/* Add padding for new MSDU to start from 4 byte boundary */
*pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4;
return skb_aggr->len + *pad;
}
/*
* Adds TxPD to AMSDU header.
*
* Each AMSDU packet will contain one TxPD at the beginning,
* followed by multiple AMSDU subframes.
*/
static void
mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv,
struct sk_buff *skb)
{
struct txpd *local_tx_pd;
struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
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 (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET;
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;
}
}
}
/*
* 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 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, ret;
struct mwifiex_tx_param tx_param;
struct txpd *ptx_pd = NULL;
struct timeval tv;
int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN;
skb_src = skb_peek(&pra_list->skb_head);
if (!skb_src) {
spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
ra_list_flags);
return 0;
}
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);
memset(tx_info_aggr, 0, sizeof(*tx_info_aggr));
tx_info_aggr->bss_type = tx_info_src->bss_type;
tx_info_aggr->bss_num = tx_info_src->bss_num;
if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
skb_aggr->priority = skb_src->priority;
do_gettimeofday(&tv);
skb_aggr->tstamp = timeval_to_ktime(tv);
do {
/* Check if AMSDU can accommodate this MSDU */
if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN))
break;
skb_src = skb_dequeue(&pra_list->skb_head);
pra_list->total_pkt_count--;
atomic_dec(&priv->wmm.tx_pkts_queued);
spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
ra_list_flags);
mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad);
mwifiex_write_data_complete(adapter, skb_src, 0, 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_tailroom(skb_aggr) < pad) {
pad = 0;
break;
}
skb_put(skb_aggr, pad);
skb_src = skb_peek(&pra_list->skb_head);
} while (skb_src);
spin_unlock_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);
if (adapter->iface_type == MWIFIEX_USB) {
adapter->data_sent = true;
ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA,
skb_aggr, NULL);
} else {
if (skb_src)
tx_param.next_pkt_len =
skb_src->len + sizeof(struct txpd);
else
tx_param.next_pkt_len = 0;
ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
skb_aggr, &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, -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;
if (ptx_pd)
ptx_pd->flags = 0;
}
skb_queue_tail(&pra_list->skb_head, skb_aggr);
pra_list->total_pkt_count++;
atomic_inc(&priv->wmm.tx_pkts_queued);
tx_info_aggr->flags |= 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:
if (adapter->iface_type != MWIFIEX_PCIE)
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, 1, ret);
return 0;
case -EINPROGRESS:
if (adapter->iface_type != MWIFIEX_PCIE)
adapter->data_sent = false;
break;
case 0:
mwifiex_write_data_complete(adapter, skb_aggr, 1, ret);
break;
default:
break;
}
if (ret != -EBUSY) {
mwifiex_rotate_priolists(priv, pra_list, ptrindex);
}
return 0;
}

View file

@ -0,0 +1,33 @@
/*
* Marvell Wireless LAN device driver: 802.11n Aggregation
*
* Copyright (C) 2011-2014, 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
#define MIN_NUM_AMSDU 2
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 ptr_index, unsigned long flags)
__releases(&priv->wmm.ra_list_spinlock);
#endif /* !_MWIFIEX_11N_AGGR_H_ */

View file

@ -0,0 +1,817 @@
/*
* Marvell Wireless LAN device driver: 802.11n RX Re-ordering
*
* Copyright (C) 2011-2014, 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 will dispatch amsdu packet and forward it to kernel/upper
* layer.
*/
static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv,
struct sk_buff *skb)
{
struct rxpd *local_rx_pd = (struct rxpd *)(skb->data);
int ret;
if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) {
struct sk_buff_head list;
struct sk_buff *rx_skb;
__skb_queue_head_init(&list);
skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset));
skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length));
ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr,
priv->wdev->iftype, 0, false);
while (!skb_queue_empty(&list)) {
rx_skb = __skb_dequeue(&list);
ret = mwifiex_recv_packet(priv, rx_skb);
if (ret == -1)
dev_err(priv->adapter->dev,
"Rx of A-MSDU failed");
}
return 0;
}
return -1;
}
/* This function will process the rx packet and forward it to kernel/upper
* layer.
*/
static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload)
{
int ret = mwifiex_11n_dispatch_amsdu_pkt(priv, payload);
if (!ret)
return 0;
if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
return mwifiex_handle_uap_rx_forward(priv, payload);
return mwifiex_process_rx_packet(priv, payload);
}
/*
* This function dispatches all packets in the Rx reorder table until the
* start window.
*
* There could be holes in the buffer, which are skipped by the function.
* Since the buffer is linear, the function uses rotation to simulate
* circular buffer.
*/
static void
mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv,
struct mwifiex_rx_reorder_tbl *tbl,
int start_win)
{
int pkt_to_send, i;
void *rx_tmp_ptr;
unsigned long flags;
pkt_to_send = (start_win > tbl->start_win) ?
min((start_win - tbl->start_win), tbl->win_size) :
tbl->win_size;
for (i = 0; i < pkt_to_send; ++i) {
spin_lock_irqsave(&priv->rx_pkt_lock, flags);
rx_tmp_ptr = NULL;
if (tbl->rx_reorder_ptr[i]) {
rx_tmp_ptr = tbl->rx_reorder_ptr[i];
tbl->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
*/
for (i = 0; i < tbl->win_size - pkt_to_send; ++i) {
tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i];
tbl->rx_reorder_ptr[pkt_to_send + i] = NULL;
}
tbl->start_win = start_win;
spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
}
/*
* This function dispatches all packets in the Rx reorder table until
* a hole is found.
*
* The start window is adjusted automatically when a hole is located.
* Since the buffer is linear, the function uses rotation to simulate
* circular buffer.
*/
static void
mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv,
struct mwifiex_rx_reorder_tbl *tbl)
{
int i, j, xchg;
void *rx_tmp_ptr;
unsigned long flags;
for (i = 0; i < tbl->win_size; ++i) {
spin_lock_irqsave(&priv->rx_pkt_lock, flags);
if (!tbl->rx_reorder_ptr[i]) {
spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
break;
}
rx_tmp_ptr = tbl->rx_reorder_ptr[i];
tbl->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 = tbl->win_size - i;
for (j = 0; j < xchg; ++j) {
tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j];
tbl->rx_reorder_ptr[i + j] = NULL;
}
}
tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1);
spin_unlock_irqrestore(&priv->rx_pkt_lock, flags);
}
/*
* 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_del_rx_reorder_entry(struct mwifiex_private *priv,
struct mwifiex_rx_reorder_tbl *tbl)
{
unsigned long flags;
int start_win;
if (!tbl)
return;
spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags);
priv->adapter->rx_locked = true;
if (priv->adapter->rx_processing) {
spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags);
flush_workqueue(priv->adapter->rx_workqueue);
} else {
spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags);
}
start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1);
mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
del_timer_sync(&tbl->timer_context.timer);
tbl->timer_context.timer_is_set = false;
spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
list_del(&tbl->list);
spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
kfree(tbl->rx_reorder_ptr);
kfree(tbl);
spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags);
priv->adapter->rx_locked = false;
spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags);
}
/*
* This function returns the pointer to an entry in Rx reordering
* table which matches the given TA/TID pair.
*/
struct mwifiex_rx_reorder_tbl *
mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta)
{
struct mwifiex_rx_reorder_tbl *tbl;
unsigned long flags;
spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) {
if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) {
spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
flags);
return tbl;
}
}
spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
return NULL;
}
/* This function retrieves the pointer to an entry in Rx reordering
* table which matches the given TA and deletes it.
*/
void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta)
{
struct mwifiex_rx_reorder_tbl *tbl, *tmp;
unsigned long flags;
if (!ta)
return;
spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) {
if (!memcmp(tbl->ta, ta, ETH_ALEN)) {
spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
flags);
mwifiex_del_rx_reorder_entry(priv, tbl);
spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
}
}
spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
return;
}
/*
* 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 reorder_tmr_cnxt *ctx)
{
struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr;
struct mwifiex_private *priv = ctx->priv;
unsigned long flags;
int i;
spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) {
if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) {
spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
flags);
return i;
}
}
spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
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 *ctx =
(struct reorder_tmr_cnxt *) context;
int start_win, seq_num;
ctx->timer_is_set = false;
seq_num = mwifiex_11n_find_last_seq_num(ctx);
if (seq_num < 0)
return;
dev_dbg(ctx->priv->adapter->dev, "info: flush data %d\n", seq_num);
start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1);
mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr,
start_win);
}
/*
* This function creates an entry in Rx reordering table for the
* given TA/TID.
*
* The function also initializes the entry with sequence number, window
* size as well as initializes the timer.
*
* If the received TA/TID pair is already present, all the packets are
* dispatched and the window size is moved until the SSN.
*/
static void
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 *tbl, *new_node;
u16 last_seq = 0;
unsigned long flags;
struct mwifiex_sta_node *node;
/*
* If we get a TID, ta pair which is already present dispatch all the
* the packets and move the window size until the ssn
*/
tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
if (tbl) {
mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num);
return;
}
/* if !tbl then create one */
new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL);
if (!new_node)
return;
INIT_LIST_HEAD(&new_node->list);
new_node->tid = tid;
memcpy(new_node->ta, ta, ETH_ALEN);
new_node->start_win = seq_num;
new_node->init_win = seq_num;
new_node->flags = 0;
if (mwifiex_queuing_ra_based(priv)) {
dev_dbg(priv->adapter->dev,
"info: AP/ADHOC:last_seq=%d start_win=%d\n",
last_seq, new_node->start_win);
if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) {
node = mwifiex_get_sta_entry(priv, ta);
if (node)
last_seq = node->rx_seq[tid];
}
} else {
node = mwifiex_get_sta_entry(priv, ta);
if (node)
last_seq = node->rx_seq[tid];
else
last_seq = priv->rx_seq[tid];
}
if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM &&
last_seq >= new_node->start_win) {
new_node->start_win = last_seq + 1;
new_node->flags |= RXREOR_INIT_WINDOW_SHIFT;
}
new_node->win_size = win_size;
new_node->rx_reorder_ptr = 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;
new_node->timer_context.timer_is_set = false;
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);
}
static void
mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl *tbl)
{
u32 min_flush_time;
if (tbl->win_size >= MWIFIEX_BA_WIN_SIZE_32)
min_flush_time = MIN_FLUSH_TIMER_15_MS;
else
min_flush_time = MIN_FLUSH_TIMER_MS;
mod_timer(&tbl->timer_context.timer,
jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size));
tbl->timer_context.timer_is_set = true;
}
/*
* This function prepares command for adding a BA request.
*
* Preparation includes -
* - Setting command ID and proper size
* - Setting add BA request buffer
* - Ensuring correct endian-ness
*/
int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf)
{
struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req;
cmd->command = cpu_to_le16(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,
struct host_cmd_ds_11n_addba_req
*cmd_addba_req)
{
struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp;
struct mwifiex_sta_node *sta_ptr;
u32 rx_win_size = priv->add_ba_param.rx_win_size;
u8 tid;
int win_size;
uint16_t block_ack_param_set;
if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
priv->adapter->is_hw_11ac_capable &&
memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) {
sta_ptr = mwifiex_get_sta_entry(priv,
cmd_addba_req->peer_mac_addr);
if (!sta_ptr) {
dev_warn(priv->adapter->dev,
"BA setup with unknown TDLS peer %pM!\n",
cmd_addba_req->peer_mac_addr);
return -1;
}
if (sta_ptr->is_11ac_enabled)
rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE;
}
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;
/* If we don't support AMSDU inside AMPDU, reset the bit */
if (!priv->add_ba_param.rx_amsdu ||
(priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED))
block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK;
block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS;
add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set);
win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set)
& IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
>> BLOCKACKPARAM_WINSIZE_POS;
cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set);
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 host_cmd_ds_command *cmd, void *data_buf)
{
struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba;
cmd->command = cpu_to_le16(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 *tbl;
int prev_start_win, start_win, end_win, win_size;
u16 pkt_index;
bool init_window_shift = false;
int ret = 0;
tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta);
if (!tbl) {
if (pkt_type != PKT_TYPE_BAR)
mwifiex_11n_dispatch_pkt(priv, payload);
return ret;
}
if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) {
mwifiex_11n_dispatch_pkt(priv, payload);
return ret;
}
start_win = tbl->start_win;
prev_start_win = start_win;
win_size = tbl->win_size;
end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) {
init_window_shift = true;
tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT;
}
if (tbl->flags & RXREOR_FORCE_NO_DROP) {
dev_dbg(priv->adapter->dev,
"RXREOR_FORCE_NO_DROP when HS is activated\n");
tbl->flags &= ~RXREOR_FORCE_NO_DROP;
} else if (init_window_shift && seq_num < start_win &&
seq_num >= tbl->init_win) {
dev_dbg(priv->adapter->dev,
"Sender TID sequence number reset %d->%d for SSN %d\n",
start_win, seq_num, tbl->init_win);
tbl->start_win = start_win = seq_num;
end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1);
} else {
/*
* If seq_num is less then starting win then ignore and drop
* the packet
*/
if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) {
if (seq_num >= ((start_win + TWOPOW11) &
(MAX_TID_VALUE - 1)) &&
seq_num < start_win) {
ret = -1;
goto done;
}
} else if ((seq_num < start_win) ||
(seq_num >= (start_win + TWOPOW11))) {
ret = -1;
goto done;
}
}
/*
* If this packet is a BAR we adjust seq_num as
* WinStart = seq_num
*/
if (pkt_type == PKT_TYPE_BAR)
seq_num = ((seq_num + win_size) - 1) & (MAX_TID_VALUE - 1);
if (((end_win < start_win) &&
(seq_num < start_win) && (seq_num > end_win)) ||
((end_win > start_win) && ((seq_num > end_win) ||
(seq_num < start_win)))) {
end_win = seq_num;
if (((seq_num - win_size) + 1) >= 0)
start_win = (end_win - win_size) + 1;
else
start_win = (MAX_TID_VALUE - (win_size - seq_num)) + 1;
mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win);
}
if (pkt_type != PKT_TYPE_BAR) {
if (seq_num >= start_win)
pkt_index = seq_num - start_win;
else
pkt_index = (seq_num+MAX_TID_VALUE) - start_win;
if (tbl->rx_reorder_ptr[pkt_index]) {
ret = -1;
goto done;
}
tbl->rx_reorder_ptr[pkt_index] = payload;
}
/*
* Dispatch all packets sequentially from start_win until a
* hole is found and adjust the start_win appropriately
*/
mwifiex_11n_scan_and_dispatch(priv, tbl);
done:
if (!tbl->timer_context.timer_is_set ||
prev_start_win != tbl->start_win)
mwifiex_11n_rxreorder_timer_restart(tbl);
return ret;
}
/*
* This function deletes an entry for a given TID/TA pair.
*
* The TID/TA are taken from del BA event body.
*/
void
mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac,
u8 type, int initiator)
{
struct mwifiex_rx_reorder_tbl *tbl;
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) {
tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
peer_mac);
if (!tbl) {
dev_dbg(priv->adapter->dev,
"event: TID, TA not found in table\n");
return;
}
mwifiex_del_rx_reorder_entry(priv, tbl);
} else {
ptx_tbl = mwifiex_get_ba_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 = &resp->params.add_ba_rsp;
int tid, win_size;
struct mwifiex_rx_reorder_tbl *tbl;
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) {
dev_err(priv->adapter->dev, "ADDBA RSP: failed %pM tid=%d)\n",
add_ba_rsp->peer_mac_addr, tid);
tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
add_ba_rsp->peer_mac_addr);
if (tbl)
mwifiex_del_rx_reorder_entry(priv, tbl);
return 0;
}
win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK)
>> BLOCKACKPARAM_WINSIZE_POS;
tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid,
add_ba_rsp->peer_mac_addr);
if (tbl) {
if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) &&
priv->add_ba_param.rx_amsdu &&
(priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED))
tbl->amsdu = true;
else
tbl->amsdu = false;
}
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);
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_send_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba, false);
}
/*
* 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_del_rx_reorder_entry(priv, del_tbl_ptr);
spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags);
}
INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags);
mwifiex_reset_11n_rx_seq_num(priv);
}
/*
* This function updates all rx_reorder_tbl's flags.
*/
void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags)
{
struct mwifiex_private *priv;
struct mwifiex_rx_reorder_tbl *tbl;
unsigned long lock_flags;
int i;
for (i = 0; i < adapter->priv_num; i++) {
priv = adapter->priv[i];
if (!priv)
continue;
spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags);
if (list_empty(&priv->rx_reorder_tbl_ptr)) {
spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock,
lock_flags);
continue;
}
list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list)
tbl->flags = flags;
spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags);
}
return;
}

View file

@ -0,0 +1,85 @@
/*
* Marvell Wireless LAN device driver: 802.11n RX Re-ordering
*
* Copyright (C) 2011-2014, 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 MIN_FLUSH_TIMER_15_MS 15
#define MWIFIEX_BA_WIN_SIZE_32 32
#define PKT_TYPE_BAR 0xE7
#define MAX_TID_VALUE (2 << 11)
#define TWOPOW11 (2 << 10)
#define BLOCKACKPARAM_TID_POS 2
#define BLOCKACKPARAM_AMSDU_SUPP_MASK 0x1
#define BLOCKACKPARAM_WINSIZE_POS 6
#define DELBA_TID_POS 12
#define DELBA_INITIATOR_POS 11
#define TYPE_DELBA_SENT 1
#define TYPE_DELBA_RECEIVE 2
#define IMMEDIATE_BLOCK_ACK 0x2
#define ADDBA_RSP_STATUS_ACCEPT 0
#define MWIFIEX_DEF_11N_RX_SEQ_NUM 0xffff
#define BA_SETUP_MAX_PACKET_THRESHOLD 16
#define BA_SETUP_PACKET_OFFSET 16
enum mwifiex_rxreor_flags {
RXREOR_FORCE_NO_DROP = 1<<0,
RXREOR_INIT_WINDOW_SHIFT = 1<<1,
};
static inline void mwifiex_reset_11n_rx_seq_num(struct mwifiex_private *priv)
{
memset(priv->rx_seq, 0xff, sizeof(priv->rx_seq));
}
int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *,
u16 seqNum,
u16 tid, u8 *ta,
u8 pkttype, void *payload);
void mwifiex_del_ba_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 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,
struct host_cmd_ds_11n_addba_req
*cmd_addba_req);
int mwifiex_cmd_11n_addba_req(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);
struct mwifiex_rx_reorder_tbl *
mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta);
void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta);
void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags);
#endif /* _MWIFIEX_11N_RXREORDER_H_ */

View file

@ -0,0 +1,42 @@
config MWIFIEX
tristate "Marvell WiFi-Ex Driver"
depends on CFG80211
---help---
This adds support for wireless adapters based on Marvell
802.11n/ac chipsets.
If you choose to build it as a module, it will be called
mwifiex.
config MWIFIEX_SDIO
tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8887/SD8897"
depends on MWIFIEX && MMC
select FW_LOADER
---help---
This adds support for wireless adapters based on Marvell
8786/8787/8797/8887/8897 chipsets with SDIO interface.
If you choose to build it as a module, it will be called
mwifiex_sdio.
config MWIFIEX_PCIE
tristate "Marvell WiFi-Ex Driver for PCIE 8766/8897"
depends on MWIFIEX && PCI
select FW_LOADER
---help---
This adds support for wireless adapters based on Marvell
8766/8897 chipsets with PCIe interface.
If you choose to build it as a module, it will be called
mwifiex_pcie.
config MWIFIEX_USB
tristate "Marvell WiFi-Ex Driver for USB8797/8897"
depends on MWIFIEX && USB
select FW_LOADER
---help---
This adds support for wireless adapters based on Marvell
8797/8897 chipset with USB interface.
If you choose to build it as a module, it will be called
mwifiex_usb.

View file

@ -0,0 +1,55 @@
#
# Copyright (C) 2011-2014, 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 += 11ac.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 += uap_cmd.o
mwifiex-y += ie.o
mwifiex-y += sta_cmdresp.o
mwifiex-y += sta_event.o
mwifiex-y += uap_event.o
mwifiex-y += sta_tx.o
mwifiex-y += sta_rx.o
mwifiex-y += uap_txrx.o
mwifiex-y += cfg80211.o
mwifiex-y += ethtool.o
mwifiex-y += 11h.o
mwifiex-y += tdls.o
mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o
obj-$(CONFIG_MWIFIEX) += mwifiex.o
mwifiex_sdio-y += sdio.o
obj-$(CONFIG_MWIFIEX_SDIO) += mwifiex_sdio.o
mwifiex_pcie-y += pcie.o
obj-$(CONFIG_MWIFIEX_PCIE) += mwifiex_pcie.o
mwifiex_usb-y += usb.o
obj-$(CONFIG_MWIFIEX_USB) += mwifiex_usb.o

View file

@ -0,0 +1,240 @@
# Copyright (C) 2011-2014, 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>
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>
is_cmd_timedout = <0/1 command timeout not occurred/occurred>
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>
cmd_pending = <number of cmd 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
hscfg
This command is used to debug/simulate host sleep feature using
different configuration parameters.
Usage:
echo "<condition> [GPIO# [gap]]]" > hscfg
cat hscfg
where the parameters are,
<condition>: bit 0 = 1 -- broadcast data
bit 1 = 1 -- unicast data
bit 2 = 1 -- mac event
bit 3 = 1 -- multicast data
[GPIO#]: pin number of GPIO used to wakeup the host.
GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO
will be used instead).
[gap]: the gap in milliseconds between wakeup signal and
wakeup event or 0xff for special setting (host
acknowledge required) when GPIO is used to wakeup host.
Examples:
echo "-1" > hscfg : Cancel host sleep mode
echo "3" > hscfg : Broadcast and unicast data;
Use GPIO and gap set previously
echo "2 3" > hscfg : Unicast data and GPIO 3;
Use gap set previously
echo "2 1 160" > hscfg : Unicast data, GPIO 1 and gap 160 ms
echo "2 1 0xff" > hscfg : Unicast data, GPIO 1; Wait for host
to ack before sending wakeup event
getlog
This command is used to get the statistics available in the station.
Usage:
cat getlog
fw_dump
This command is used to dump firmware memory into files.
Separate file will be created for each memory segment.
Usage:
cat fw_dump
===============================================================================

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
/*
* Marvell Wireless LAN device driver: CFG80211
*
* Copyright (C) 2011-2014, 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 mwifiex_adapter *);
#endif

View file

@ -0,0 +1,511 @@
/*
* Marvell Wireless LAN device driver: Channel, Frequence and Power
*
* Copyright (C) 2011-2014, 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 };
static 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 };
static u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 };
static u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24,
0x30, 0x48, 0x60, 0x6c, 0 };
static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c,
0x12, 0x16, 0x18, 0x24, 0x30, 0x48,
0x60, 0x6c, 0 };
u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30,
0x32, 0x40, 0x41, 0xff };
static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 };
/* For every mcs_rate line, the first 8 bytes are for stream 1x1,
* and all 16 bytes are for stream 2x2.
*/
static const u16 mcs_rate[4][16] = {
/* LGI 40M */
{ 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e,
0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c },
/* SGI 40M */
{ 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c,
0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 },
/* LGI 20M */
{ 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82,
0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 },
/* SGI 20M */
{ 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90,
0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 }
};
/* AC rates */
static const u16 ac_mcs_rate_nss1[8][10] = {
/* LG 160M */
{ 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
0x492, 0x57C, 0x618 },
/* SG 160M */
{ 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
0x514, 0x618, 0x6C6 },
/* LG 80M */
{ 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F,
0x249, 0x2BE, 0x30C },
/* SG 80M */
{ 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249,
0x28A, 0x30C, 0x363 },
/* LG 40M */
{ 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3,
0x10E, 0x144, 0x168 },
/* SG 40M */
{ 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E,
0x12C, 0x168, 0x190 },
/* LG 20M */
{ 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 },
/* SG 20M */
{ 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 },
};
/* NSS2 note: the value in the table is 2 multiplier of the actual rate */
static const u16 ac_mcs_rate_nss2[8][10] = {
/* LG 160M */
{ 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A,
0x924, 0xAF8, 0xC30 },
/* SG 160M */
{ 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924,
0xA28, 0xC30, 0xD8B },
/* LG 80M */
{ 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D,
0x492, 0x57C, 0x618 },
/* SG 80M */
{ 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492,
0x514, 0x618, 0x6C6 },
/* LG 40M */
{ 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6,
0x21C, 0x288, 0x2D0 },
/* SG 40M */
{ 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C,
0x258, 0x2D0, 0x320 },
/* LG 20M */
{ 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104,
0x138, 0x00 },
/* SG 20M */
{ 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121,
0x15B, 0x00 },
};
struct region_code_mapping {
u8 code;
u8 region[IEEE80211_COUNTRY_STRING_LEN];
};
static struct region_code_mapping region_code_mapping_t[] = {
{ 0x10, "US " }, /* US FCC */
{ 0x20, "CA " }, /* IC Canada */
{ 0x30, "EU " }, /* ETSI */
{ 0x31, "ES " }, /* Spain */
{ 0x32, "FR " }, /* France */
{ 0x40, "JP " }, /* Japan */
{ 0x41, "JP " }, /* Japan */
{ 0x50, "CN " }, /* China */
};
/* This function converts integer code to region string */
u8 *mwifiex_11d_code_2_region(u8 code)
{
u8 i;
u8 size = sizeof(region_code_mapping_t)/
sizeof(struct region_code_mapping);
/* Look for code in mapping table */
for (i = 0; i < size; i++)
if (region_code_mapping_t[i].code == code)
return region_code_mapping_t[i].region;
return NULL;
}
/*
* This function maps an index in supported rates table into
* the corresponding data rate.
*/
u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv,
u8 index, u8 ht_info)
{
u32 rate = 0;
u8 mcs_index = 0;
u8 bw = 0;
u8 gi = 0;
if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_VHT) {
mcs_index = min(index & 0xF, 9);
/* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */
bw = (ht_info & 0xC) >> 2;
/* LGI: gi =0, SGI: gi = 1 */
gi = (ht_info & 0x10) >> 4;
if ((index >> 4) == 1) /* NSS = 2 */
rate = ac_mcs_rate_nss2[2 * (3 - bw) + gi][mcs_index];
else /* NSS = 1 */
rate = ac_mcs_rate_nss1[2 * (3 - bw) + gi][mcs_index];
} else if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_HT) {
/* 20M: bw=0, 40M: bw=1 */
bw = (ht_info & 0xC) >> 2;
/* LGI: gi =0, SGI: gi = 1 */
gi = (ht_info & 0x10) >> 4;
if (index == MWIFIEX_RATE_BITMAP_MCS0) {
if (gi == 1)
rate = 0x0D; /* MCS 32 SGI rate */
else
rate = 0x0C; /* MCS 32 LGI rate */
} else if (index < 16) {
if ((bw == 1) || (bw == 0))
rate = mcs_rate[2 * (1 - bw) + gi][index];
else
rate = mwifiex_data_rates[0];
} else {
rate = mwifiex_data_rates[0];
}
} else {
/* 11n non-HT rates */
if (index >= MWIFIEX_SUPPORTED_RATES_EXT)
index = 0;
rate = mwifiex_data_rates[index];
}
return rate;
}
/* This function maps an index in supported rates table into
* the corresponding data rate.
*/
u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv,
u8 index, u8 ht_info)
{
u32 mcs_num_supp =
(priv->adapter->user_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8;
u32 rate;
if (priv->adapter->is_hw_11ac_capable)
return mwifiex_index_to_acs_data_rate(priv, index, ht_info);
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 < mcs_num_supp) {
if (ht_info & BIT(1)) {
if (ht_info & BIT(2))
/* SGI, 40M */
rate = mcs_rate[1][index];
else
/* LGI, 40M */
rate = mcs_rate[0][index];
} else {
if (ht_info & BIT(2))
/* SGI, 20M */
rate = mcs_rate[3][index];
else
/* LGI, 20M */
rate = mcs_rate[2][index];
}
} else
rate = mwifiex_data_rates[0];
} else {
if (index >= MWIFIEX_SUPPORTED_RATES_EXT)
index = 0;
rate = mwifiex_data_rates[index];
}
return rate;
}
/*
* 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)
{
if (!priv->media_connected)
return mwifiex_get_supported_rates(priv, rates);
else
return mwifiex_copy_rates(rates, 0,
priv->curr_bss_params.data_rates,
priv->curr_bss_params.num_of_rates);
}
/*
* This function locates the Channel-Frequency-Power triplet based upon
* band and channel/frequency parameters.
*/
struct mwifiex_chan_freq_power *
mwifiex_get_cfp(struct mwifiex_private *priv, u8 band, u16 channel, u32 freq)
{
struct mwifiex_chan_freq_power *cfp = NULL;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *ch = NULL;
int i;
if (!channel && !freq)
return cfp;
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\n",
__func__, band);
return cfp;
}
for (i = 0; i < sband->n_channels; i++) {
ch = &sband->channels[i];
if (ch->flags & IEEE80211_CHAN_DISABLED)
continue;
if (freq) {
if (ch->center_freq == freq)
break;
} else {
/* find by valid channel*/
if (ch->hw_value == channel ||
channel == FIRST_VALID_CHANNEL)
break;
}
}
if (i == sband->n_channels) {
dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d"
" & channel=%d freq=%d\n", __func__, band, channel,
freq);
} else {
if (!ch)
return cfp;
priv->cfp.channel = ch->hw_value;
priv->cfp.freq = ch->center_freq;
priv->cfp.max_tx_power = ch->max_power;
cfp = &priv->cfp;
}
return cfp;
}
/*
* This function checks if the data rate is set to auto.
*/
u8
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 gets the supported data rates from bitmask inside
* cfg80211_scan_request.
*/
u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv,
u8 *rates, u8 radio_type)
{
struct wiphy *wiphy = priv->adapter->wiphy;
struct cfg80211_scan_request *request = priv->scan_request;
u32 num_rates, rate_mask;
struct ieee80211_supported_band *sband;
int i;
if (radio_type) {
sband = wiphy->bands[IEEE80211_BAND_5GHZ];
if (WARN_ON_ONCE(!sband))
return 0;
rate_mask = request->rates[IEEE80211_BAND_5GHZ];
} else {
sband = wiphy->bands[IEEE80211_BAND_2GHZ];
if (WARN_ON_ONCE(!sband))
return 0;
rate_mask = request->rates[IEEE80211_BAND_2GHZ];
}
num_rates = 0;
for (i = 0; i < sband->n_bitrates; i++) {
if ((BIT(i) & rate_mask) == 0)
continue; /* skip rate */
rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5);
}
return num_rates;
}
/* This function gets the supported data rates. The function works in
* both Ad-Hoc and infra mode by printing the band and returning the
* data rates.
*/
u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
{
u32 k = 0;
struct mwifiex_adapter *adapter = priv->adapter;
if (priv->bss_mode == NL80211_IFTYPE_STATION ||
priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
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_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC:
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_AN:
case BAND_A | BAND_AN:
case BAND_A | BAND_AN | BAND_AAC:
case BAND_A | BAND_G | BAND_AN | BAND_GN:
case BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC:
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,875 @@
/*
* Marvell Wireless LAN device driver: debugfs
*
* Copyright (C) 2011-2014, 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[] = {
"UNSPECIFIED",
"ADHOC",
"STATION",
"AP",
"AP_VLAN",
"WDS",
"MONITOR",
"MESH_POINT",
"P2P_CLIENT",
"P2P_GO",
"P2P_DEVICE",
};
/* 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},
{"tx_buf_size", item_size(tx_buf_size),
item_addr(tx_buf_size), 1},
{"curr_tx_buf_size", item_size(curr_tx_buf_size),
item_addr(curr_tx_buf_size), 1},
{"ps_mode", item_size(ps_mode),
item_addr(ps_mode), 1},
{"ps_state", item_size(ps_state),
item_addr(ps_state), 1},
{"is_deep_sleep", item_size(is_deep_sleep),
item_addr(is_deep_sleep), 1},
{"wakeup_dev_req", item_size(pm_wakeup_card_req),
item_addr(pm_wakeup_card_req), 1},
{"wakeup_tries", item_size(pm_wakeup_fw_try),
item_addr(pm_wakeup_fw_try), 1},
{"hs_configured", item_size(is_hs_configured),
item_addr(is_hs_configured), 1},
{"hs_activated", item_size(hs_activated),
item_addr(hs_activated), 1},
{"num_tx_timeout", item_size(num_tx_timeout),
item_addr(num_tx_timeout), 1},
{"is_cmd_timedout", item_size(is_cmd_timedout),
item_addr(is_cmd_timedout), 1},
{"timeout_cmd_id", item_size(timeout_cmd_id),
item_addr(timeout_cmd_id), 1},
{"timeout_cmd_act", item_size(timeout_cmd_act),
item_addr(timeout_cmd_act), 1},
{"last_cmd_id", item_size(last_cmd_id),
item_addr(last_cmd_id), DBG_CMD_NUM},
{"last_cmd_act", item_size(last_cmd_act),
item_addr(last_cmd_act), DBG_CMD_NUM},
{"last_cmd_index", item_size(last_cmd_index),
item_addr(last_cmd_index), 1},
{"last_cmd_resp_id", item_size(last_cmd_resp_id),
item_addr(last_cmd_resp_id), DBG_CMD_NUM},
{"last_cmd_resp_index", item_size(last_cmd_resp_index),
item_addr(last_cmd_resp_index), 1},
{"last_event", item_size(last_event),
item_addr(last_event), DBG_CMD_NUM},
{"last_event_index", item_size(last_event_index),
item_addr(last_event_index), 1},
{"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 */
{"cmd_pending", adapter_item_size(cmd_pending),
adapter_item_addr(cmd_pending), 1},
{"tx_pending", adapter_item_size(tx_pending),
adapter_item_addr(tx_pending), 1},
{"rx_pending", adapter_item_size(rx_pending),
adapter_item_addr(rx_pending), 1},
};
static int num_of_items = ARRAY_SIZE(items);
/*
* 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;
struct netdev_queue *txq;
unsigned long page = get_zeroed_page(GFP_KERNEL);
char *p = (char *) page, fmt[64];
struct mwifiex_bss_info info;
ssize_t ret;
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);
if (info.bss_mode >= ARRAY_SIZE(bss_modes))
p += sprintf(p, "bss_mode=\"%d\"\n", info.bss_mode);
else
p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]);
p += sprintf(p, "media_state=\"%s\"\n",
(!priv->media_connected ? "Disconnected" : "Connected"));
p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr);
if (GET_BSS_ROLE(priv) == 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=\"%pM\"\n", info.bssid);
p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan);
p += sprintf(p, "country_code = \"%s\"\n", info.country_code);
netdev_for_each_mc_addr(ha, netdev)
p += sprintf(p, "multicast_address[%d]=\"%pM\"\n",
i++, ha->addr);
}
p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes);
p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes);
p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets);
p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets);
p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped);
p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped);
p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors);
p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors);
p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev))
? "on" : "off"));
p += sprintf(p, "tx queue");
for (i = 0; i < netdev->num_tx_queues; i++) {
txq = netdev_get_tx_queue(netdev, i);
p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ?
"stopped" : "started");
}
p += sprintf(p, "\n");
ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page,
(unsigned long) p - page);
free_and_exit:
free_page(page);
return ret;
}
/*
* Proc firmware dump read handler.
*
* This function is called when the 'fw_dump' file is opened for
* reading.
* This function dumps firmware memory in different files
* (ex. DTCM, ITCM, SQRAM etc.) based on the the segments for
* debugging.
*/
static ssize_t
mwifiex_fw_dump_read(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
struct mwifiex_private *priv = file->private_data;
if (!priv->adapter->if_ops.fw_dump)
return -EIO;
priv->adapter->if_ops.fw_dump(priv->adapter);
return 0;
}
/*
* 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;
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;
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 = %pM\n",
info.tx_tbl[i].tid, info.tx_tbl[i].ra);
}
if (info.rx_tbl_num) {
p += sprintf(p, "Rx reorder table:\n");
for (i = 0; i < info.rx_tbl_num; i++) {
p += sprintf(p, "tid = %d, ta = %pM, "
"start_win = %d, "
"win_size = %d, buffer: ",
info.rx_tbl[i].tid,
info.rx_tbl[i].ta,
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_t(size_t, count, PAGE_SIZE - 1);
int ret;
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_t(size_t, count, 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;
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;
}
/* Proc hscfg file write handler
* This function can be used to configure the host sleep parameters.
*/
static ssize_t
mwifiex_hscfg_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct mwifiex_private *priv = (void *)file->private_data;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
int ret, arg_num;
struct mwifiex_ds_hs_cfg hscfg;
int conditions = HS_CFG_COND_DEF;
u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF;
if (!buf)
return -ENOMEM;
if (copy_from_user(buf, ubuf, buf_size)) {
ret = -EFAULT;
goto done;
}
arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap);
memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg));
if (arg_num > 3) {
dev_err(priv->adapter->dev, "Too many arguments\n");
ret = -EINVAL;
goto done;
}
if (arg_num >= 1 && arg_num < 3)
mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,
MWIFIEX_SYNC_CMD, &hscfg);
if (arg_num) {
if (conditions == HS_CFG_CANCEL) {
mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD);
ret = count;
goto done;
}
hscfg.conditions = conditions;
}
if (arg_num >= 2)
hscfg.gpio = gpio;
if (arg_num == 3)
hscfg.gap = gap;
hscfg.is_invoke_hostcmd = false;
mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
MWIFIEX_SYNC_CMD, &hscfg);
mwifiex_enable_hs(priv->adapter);
priv->adapter->hs_enabling = false;
ret = count;
done:
free_page(addr);
return ret;
}
/* Proc hscfg file read handler
* This function can be used to read host sleep configuration
* parameters from driver.
*/
static ssize_t
mwifiex_hscfg_read(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
struct mwifiex_private *priv = (void *)file->private_data;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
int pos, ret;
struct mwifiex_ds_hs_cfg hscfg;
if (!buf)
return -ENOMEM;
mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,
MWIFIEX_SYNC_CMD, &hscfg);
pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions,
hscfg.gpio, hscfg.gap);
ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
free_page(addr);
return ret;
}
#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 = simple_open, \
};
#define MWIFIEX_DFS_FILE_READ_OPS(name) \
static const struct file_operations mwifiex_dfs_##name##_fops = { \
.read = mwifiex_##name##_read, \
.open = simple_open, \
};
#define MWIFIEX_DFS_FILE_WRITE_OPS(name) \
static const struct file_operations mwifiex_dfs_##name##_fops = { \
.write = mwifiex_##name##_write, \
.open = simple_open, \
};
MWIFIEX_DFS_FILE_READ_OPS(info);
MWIFIEX_DFS_FILE_READ_OPS(debug);
MWIFIEX_DFS_FILE_READ_OPS(getlog);
MWIFIEX_DFS_FILE_READ_OPS(fw_dump);
MWIFIEX_DFS_FILE_OPS(regrdwr);
MWIFIEX_DFS_FILE_OPS(rdeeprom);
MWIFIEX_DFS_FILE_OPS(hscfg);
/*
* 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);
MWIFIEX_DFS_ADD_FILE(fw_dump);
MWIFIEX_DFS_ADD_FILE(hscfg);
}
/*
* 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);
}
/*
* 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,188 @@
/*
* Marvell Wireless LAN device driver: generic data structures and APIs
*
* Copyright (C) 2011-2014, 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>
#include <uapi/linux/if_arp.h>
#include <net/mac80211.h>
#define MWIFIEX_MAX_BSS_NUM (3)
#define MWIFIEX_MIN_DATA_HEADER_LEN 36 /* sizeof(mwifiex_txpd)
* + 4 byte alignment
*/
#define MWIFIEX_MGMT_FRAME_HEADER_SIZE 8 /* sizeof(pkt_type)
* + sizeof(tx_control)
*/
#define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2
#define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16
#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE 64
#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE 64
#define MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE 32
#define MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE 16
#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE 64
#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE 64
#define MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE 64
#define MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE 64
#define MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff
#define MWIFIEX_RATE_BITMAP_MCS0 32
#define MWIFIEX_RX_DATA_BUF_SIZE (4 * 1024)
#define MWIFIEX_RX_CMD_BUF_SIZE (2 * 1024)
#define MAX_BEACON_PERIOD (4000)
#define MIN_BEACON_PERIOD (50)
#define MAX_DTIM_PERIOD (100)
#define MIN_DTIM_PERIOD (1)
#define 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_WMM_VERSION 0x01
#define MWIFIEX_WMM_SUBTYPE 0x01
#define MWIFIEX_RETRY_LIMIT 14
#define MWIFIEX_SDIO_BLOCK_SIZE 256
#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0)
#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1)
#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2)
#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024
#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128
#define MWIFIEX_TDLS_DISABLE_LINK 0x00
#define MWIFIEX_TDLS_ENABLE_LINK 0x01
#define MWIFIEX_TDLS_CREATE_LINK 0x02
#define MWIFIEX_TDLS_CONFIG_LINK 0x03
enum mwifiex_bss_type {
MWIFIEX_BSS_TYPE_STA = 0,
MWIFIEX_BSS_TYPE_UAP = 1,
MWIFIEX_BSS_TYPE_P2P = 2,
MWIFIEX_BSS_TYPE_ANY = 0xff,
};
enum mwifiex_bss_role {
MWIFIEX_BSS_ROLE_STA = 0,
MWIFIEX_BSS_ROLE_UAP = 1,
MWIFIEX_BSS_ROLE_ANY = 0xff,
};
enum mwifiex_tdls_status {
TDLS_NOT_SETUP = 0,
TDLS_SETUP_INPROGRESS,
TDLS_SETUP_COMPLETE,
TDLS_SETUP_FAILURE,
TDLS_LINK_TEARDOWN,
};
enum mwifiex_tdls_error_code {
TDLS_ERR_NO_ERROR = 0,
TDLS_ERR_INTERNAL_ERROR,
TDLS_ERR_MAX_LINKS_EST,
TDLS_ERR_LINK_EXISTS,
TDLS_ERR_LINK_NONEXISTENT,
TDLS_ERR_PEER_STA_UNREACHABLE = 25,
};
#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 {
wait_queue_head_t wait;
int status;
};
struct mwifiex_rxinfo {
u8 bss_num;
u8 bss_type;
struct sk_buff *parent;
u8 use_count;
};
struct mwifiex_txinfo {
u32 status_code;
u8 flags;
u8 bss_num;
u8 bss_type;
u32 pkt_len;
};
enum mwifiex_wmm_ac_e {
WMM_AC_BK,
WMM_AC_BE,
WMM_AC_VI,
WMM_AC_VO
} __packed;
struct ieee_types_wmm_ac_parameters {
u8 aci_aifsn_bitmap;
u8 ecw_bitmap;
__le16 tx_op_limit;
} __packed;
struct mwifiex_types_wmm_info {
u8 oui[4];
u8 subtype;
u8 version;
u8 qos_info;
u8 reserved;
struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS];
} __packed;
struct mwifiex_arp_eth_header {
struct arphdr hdr;
u8 ar_sha[ETH_ALEN];
u8 ar_sip[4];
u8 ar_tha[ETH_ALEN];
u8 ar_tip[4];
} __packed;
#endif /* !_MWIFIEX_DECL_H_ */

View file

@ -0,0 +1,153 @@
/*
* Marvell Wireless LAN device driver: ethtool
*
* Copyright (C) 2013-2014, 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 "main.h"
static void mwifiex_ethtool_get_wol(struct net_device *dev,
struct ethtool_wolinfo *wol)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
u32 conditions = le32_to_cpu(priv->adapter->hs_cfg.conditions);
wol->supported = WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY;
if (conditions == HS_CFG_COND_DEF)
return;
if (conditions & HS_CFG_COND_UNICAST_DATA)
wol->wolopts |= WAKE_UCAST;
if (conditions & HS_CFG_COND_MULTICAST_DATA)
wol->wolopts |= WAKE_MCAST;
if (conditions & HS_CFG_COND_BROADCAST_DATA)
wol->wolopts |= WAKE_BCAST;
if (conditions & HS_CFG_COND_MAC_EVENT)
wol->wolopts |= WAKE_PHY;
}
static int mwifiex_ethtool_set_wol(struct net_device *dev,
struct ethtool_wolinfo *wol)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
u32 conditions = 0;
if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY))
return -EOPNOTSUPP;
if (wol->wolopts & WAKE_UCAST)
conditions |= HS_CFG_COND_UNICAST_DATA;
if (wol->wolopts & WAKE_MCAST)
conditions |= HS_CFG_COND_MULTICAST_DATA;
if (wol->wolopts & WAKE_BCAST)
conditions |= HS_CFG_COND_BROADCAST_DATA;
if (wol->wolopts & WAKE_PHY)
conditions |= HS_CFG_COND_MAC_EVENT;
if (wol->wolopts == 0)
conditions |= HS_CFG_COND_DEF;
priv->adapter->hs_cfg.conditions = cpu_to_le32(conditions);
return 0;
}
static int
mwifiex_get_dump_flag(struct net_device *dev, struct ethtool_dump *dump)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
struct mwifiex_adapter *adapter = priv->adapter;
struct memory_type_mapping *entry;
if (!adapter->if_ops.fw_dump)
return -ENOTSUPP;
dump->flag = adapter->curr_mem_idx;
dump->version = 1;
if (adapter->curr_mem_idx != MWIFIEX_FW_DUMP_IDX) {
entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx];
dump->len = entry->mem_size;
} else {
dump->len = 0;
}
return 0;
}
static int
mwifiex_get_dump_data(struct net_device *dev, struct ethtool_dump *dump,
void *buffer)
{
u8 *p = buffer;
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
struct mwifiex_adapter *adapter = priv->adapter;
struct memory_type_mapping *entry;
if (!adapter->if_ops.fw_dump)
return -ENOTSUPP;
if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) {
dev_err(adapter->dev, "firmware dump in progress!!\n");
return -EBUSY;
}
entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx];
if (!entry->mem_ptr)
return -EFAULT;
memcpy(p, entry->mem_ptr, entry->mem_size);
entry->mem_size = 0;
vfree(entry->mem_ptr);
entry->mem_ptr = NULL;
return 0;
}
static int mwifiex_set_dump(struct net_device *dev, struct ethtool_dump *val)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
struct mwifiex_adapter *adapter = priv->adapter;
if (!adapter->if_ops.fw_dump)
return -ENOTSUPP;
if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) {
dev_err(adapter->dev, "firmware dump in progress!!\n");
return -EBUSY;
}
if (val->flag == MWIFIEX_FW_DUMP_IDX) {
adapter->curr_mem_idx = val->flag;
adapter->if_ops.fw_dump(adapter);
return 0;
}
if (val->flag < 0 || val->flag >= adapter->num_mem_types)
return -EINVAL;
adapter->curr_mem_idx = val->flag;
return 0;
}
const struct ethtool_ops mwifiex_ethtool_ops = {
.get_wol = mwifiex_ethtool_get_wol,
.set_wol = mwifiex_ethtool_set_wol,
.get_dump_flag = mwifiex_get_dump_flag,
.get_dump_data = mwifiex_get_dump_data,
.set_dump = mwifiex_set_dump,
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,446 @@
/*
* Marvell Wireless LAN device driver: management IE handling- setting and
* deleting IE.
*
* Copyright (C) 2012-2014, 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 "main.h"
/* This function checks if current IE index is used by any on other interface.
* Return: -1: yes, current IE index is used by someone else.
* 0: no, current IE index is NOT used by other interface.
*/
static int
mwifiex_ie_index_used_by_other_intf(struct mwifiex_private *priv, u16 idx)
{
int i;
struct mwifiex_adapter *adapter = priv->adapter;
struct mwifiex_ie *ie;
for (i = 0; i < adapter->priv_num; i++) {
if (adapter->priv[i] != priv) {
ie = &adapter->priv[i]->mgmt_ie[idx];
if (ie->mgmt_subtype_mask && ie->ie_length)
return -1;
}
}
return 0;
}
/* Get unused IE index. This index will be used for setting new IE */
static int
mwifiex_ie_get_autoidx(struct mwifiex_private *priv, u16 subtype_mask,
struct mwifiex_ie *ie, u16 *index)
{
u16 mask, len, i;
for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) {
mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask);
len = le16_to_cpu(ie->ie_length);
if (mask == MWIFIEX_AUTO_IDX_MASK)
continue;
if (mask == subtype_mask) {
if (len > IEEE_MAX_IE_SIZE)
continue;
*index = i;
return 0;
}
if (!priv->mgmt_ie[i].ie_length) {
if (mwifiex_ie_index_used_by_other_intf(priv, i))
continue;
*index = i;
return 0;
}
}
return -1;
}
/* This function prepares IE data buffer for command to be sent to FW */
static int
mwifiex_update_autoindex_ies(struct mwifiex_private *priv,
struct mwifiex_ie_list *ie_list)
{
u16 travel_len, index, mask;
s16 input_len, tlv_len;
struct mwifiex_ie *ie;
u8 *tmp;
input_len = le16_to_cpu(ie_list->len);
travel_len = sizeof(struct mwifiex_ie_types_header);
ie_list->len = 0;
while (input_len >= sizeof(struct mwifiex_ie_types_header)) {
ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len);
tlv_len = le16_to_cpu(ie->ie_length);
travel_len += tlv_len + MWIFIEX_IE_HDR_SIZE;
if (input_len < tlv_len + MWIFIEX_IE_HDR_SIZE)
return -1;
index = le16_to_cpu(ie->ie_index);
mask = le16_to_cpu(ie->mgmt_subtype_mask);
if (index == MWIFIEX_AUTO_IDX_MASK) {
/* automatic addition */
if (mwifiex_ie_get_autoidx(priv, mask, ie, &index))
return -1;
if (index == MWIFIEX_AUTO_IDX_MASK)
return -1;
tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer;
memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length));
priv->mgmt_ie[index].ie_length = ie->ie_length;
priv->mgmt_ie[index].ie_index = cpu_to_le16(index);
priv->mgmt_ie[index].mgmt_subtype_mask =
cpu_to_le16(mask);
ie->ie_index = cpu_to_le16(index);
} else {
if (mask != MWIFIEX_DELETE_MASK)
return -1;
/*
* Check if this index is being used on any
* other interface.
*/
if (mwifiex_ie_index_used_by_other_intf(priv, index))
return -1;
ie->ie_length = 0;
memcpy(&priv->mgmt_ie[index], ie,
sizeof(struct mwifiex_ie));
}
le16_add_cpu(&ie_list->len,
le16_to_cpu(priv->mgmt_ie[index].ie_length) +
MWIFIEX_IE_HDR_SIZE);
input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE;
}
if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP)
return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG,
HostCmd_ACT_GEN_SET,
UAP_CUSTOM_IE_I, ie_list, false);
return 0;
}
/* Copy individual custom IEs for beacon, probe response and assoc response
* and prepare single structure for IE setting.
* This function also updates allocated IE indices from driver.
*/
static int
mwifiex_update_uap_custom_ie(struct mwifiex_private *priv,
struct mwifiex_ie *beacon_ie, u16 *beacon_idx,
struct mwifiex_ie *pr_ie, u16 *probe_idx,
struct mwifiex_ie *ar_ie, u16 *assoc_idx)
{
struct mwifiex_ie_list *ap_custom_ie;
u8 *pos;
u16 len;
int ret;
ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL);
if (!ap_custom_ie)
return -ENOMEM;
ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
pos = (u8 *)ap_custom_ie->ie_list;
if (beacon_ie) {
len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
le16_to_cpu(beacon_ie->ie_length);
memcpy(pos, beacon_ie, len);
pos += len;
le16_add_cpu(&ap_custom_ie->len, len);
}
if (pr_ie) {
len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
le16_to_cpu(pr_ie->ie_length);
memcpy(pos, pr_ie, len);
pos += len;
le16_add_cpu(&ap_custom_ie->len, len);
}
if (ar_ie) {
len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE +
le16_to_cpu(ar_ie->ie_length);
memcpy(pos, ar_ie, len);
pos += len;
le16_add_cpu(&ap_custom_ie->len, len);
}
ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie);
pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index);
if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) {
/* save beacon ie index after auto-indexing */
*beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index);
len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE +
le16_to_cpu(beacon_ie->ie_length);
pos += len;
}
if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) {
/* save probe resp ie index after auto-indexing */
*probe_idx = *((u16 *)pos);
len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE +
le16_to_cpu(pr_ie->ie_length);
pos += len;
}
if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK)
/* save assoc resp ie index after auto-indexing */
*assoc_idx = *((u16 *)pos);
kfree(ap_custom_ie);
return ret;
}
/* This function checks if the vendor specified IE is present in passed buffer
* and copies it to mwifiex_ie structure.
* Function takes pointer to struct mwifiex_ie pointer as argument.
* If the vendor specified IE is present then memory is allocated for
* mwifiex_ie pointer and filled in with IE. Caller should take care of freeing
* this memory.
*/
static int mwifiex_update_vs_ie(const u8 *ies, int ies_len,
struct mwifiex_ie **ie_ptr, u16 mask,
unsigned int oui, u8 oui_type)
{
struct ieee_types_header *vs_ie;
struct mwifiex_ie *ie = *ie_ptr;
const u8 *vendor_ie;
vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len);
if (vendor_ie) {
if (!*ie_ptr) {
*ie_ptr = kzalloc(sizeof(struct mwifiex_ie),
GFP_KERNEL);
if (!*ie_ptr)
return -ENOMEM;
ie = *ie_ptr;
}
vs_ie = (struct ieee_types_header *)vendor_ie;
memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length),
vs_ie, vs_ie->len + 2);
le16_add_cpu(&ie->ie_length, vs_ie->len + 2);
ie->mgmt_subtype_mask = cpu_to_le16(mask);
ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK);
}
*ie_ptr = ie;
return 0;
}
/* This function parses beacon IEs, probe response IEs, association response IEs
* from cfg80211_ap_settings->beacon and sets these IE to FW.
*/
static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv,
struct cfg80211_beacon_data *data)
{
struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL;
u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK;
u16 ar_idx = MWIFIEX_AUTO_IDX_MASK;
int ret = 0;
if (data->beacon_ies && data->beacon_ies_len) {
mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
&beacon_ie, MGMT_MASK_BEACON,
WLAN_OUI_MICROSOFT,
WLAN_OUI_TYPE_MICROSOFT_WPS);
mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len,
&beacon_ie, MGMT_MASK_BEACON,
WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
}
if (data->proberesp_ies && data->proberesp_ies_len) {
mwifiex_update_vs_ie(data->proberesp_ies,
data->proberesp_ies_len, &pr_ie,
MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT,
WLAN_OUI_TYPE_MICROSOFT_WPS);
mwifiex_update_vs_ie(data->proberesp_ies,
data->proberesp_ies_len, &pr_ie,
MGMT_MASK_PROBE_RESP,
WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P);
}
if (data->assocresp_ies && data->assocresp_ies_len) {
mwifiex_update_vs_ie(data->assocresp_ies,
data->assocresp_ies_len, &ar_ie,
MGMT_MASK_ASSOC_RESP |
MGMT_MASK_REASSOC_RESP,
WLAN_OUI_MICROSOFT,
WLAN_OUI_TYPE_MICROSOFT_WPS);
mwifiex_update_vs_ie(data->assocresp_ies,
data->assocresp_ies_len, &ar_ie,
MGMT_MASK_ASSOC_RESP |
MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA,
WLAN_OUI_TYPE_WFA_P2P);
}
if (beacon_ie || pr_ie || ar_ie) {
ret = mwifiex_update_uap_custom_ie(priv, beacon_ie,
&beacon_idx, pr_ie,
&pr_idx, ar_ie, &ar_idx);
if (ret)
goto done;
}
priv->beacon_idx = beacon_idx;
priv->proberesp_idx = pr_idx;
priv->assocresp_idx = ar_idx;
done:
kfree(beacon_ie);
kfree(pr_ie);
kfree(ar_ie);
return ret;
}
/* This function parses different IEs-tail IEs, beacon IEs, probe response IEs,
* association response IEs from cfg80211_ap_settings function and sets these IE
* to FW.
*/
int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
struct cfg80211_beacon_data *info)
{
struct mwifiex_ie *gen_ie;
struct ieee_types_header *rsn_ie, *wpa_ie = NULL;
u16 rsn_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0;
const u8 *vendor_ie;
if (info->tail && info->tail_len) {
gen_ie = kzalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
if (!gen_ie)
return -ENOMEM;
gen_ie->ie_index = cpu_to_le16(rsn_idx);
gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON |
MGMT_MASK_PROBE_RESP |
MGMT_MASK_ASSOC_RESP);
rsn_ie = (void *)cfg80211_find_ie(WLAN_EID_RSN,
info->tail, info->tail_len);
if (rsn_ie) {
memcpy(gen_ie->ie_buffer, rsn_ie, rsn_ie->len + 2);
ie_len = rsn_ie->len + 2;
gen_ie->ie_length = cpu_to_le16(ie_len);
}
vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
WLAN_OUI_TYPE_MICROSOFT_WPA,
info->tail,
info->tail_len);
if (vendor_ie) {
wpa_ie = (struct ieee_types_header *)vendor_ie;
memcpy(gen_ie->ie_buffer + ie_len,
wpa_ie, wpa_ie->len + 2);
ie_len += wpa_ie->len + 2;
gen_ie->ie_length = cpu_to_le16(ie_len);
}
if (rsn_ie || wpa_ie) {
if (mwifiex_update_uap_custom_ie(priv, gen_ie, &rsn_idx,
NULL, NULL,
NULL, NULL)) {
kfree(gen_ie);
return -1;
}
priv->rsn_idx = rsn_idx;
}
kfree(gen_ie);
}
return mwifiex_set_mgmt_beacon_data_ies(priv, info);
}
/* This function removes management IE set */
int mwifiex_del_mgmt_ies(struct mwifiex_private *priv)
{
struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL;
struct mwifiex_ie *ar_ie = NULL, *rsn_ie = NULL;
int ret = 0;
if (priv->rsn_idx != MWIFIEX_AUTO_IDX_MASK) {
rsn_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
if (!rsn_ie)
return -ENOMEM;
rsn_ie->ie_index = cpu_to_le16(priv->rsn_idx);
rsn_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
rsn_ie->ie_length = 0;
if (mwifiex_update_uap_custom_ie(priv, rsn_ie, &priv->rsn_idx,
NULL, &priv->proberesp_idx,
NULL, &priv->assocresp_idx)) {
ret = -1;
goto done;
}
priv->rsn_idx = MWIFIEX_AUTO_IDX_MASK;
}
if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) {
beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
if (!beacon_ie) {
ret = -ENOMEM;
goto done;
}
beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx);
beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
beacon_ie->ie_length = 0;
}
if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) {
pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
if (!pr_ie) {
ret = -ENOMEM;
goto done;
}
pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx);
pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
pr_ie->ie_length = 0;
}
if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) {
ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL);
if (!ar_ie) {
ret = -ENOMEM;
goto done;
}
ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx);
ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK);
ar_ie->ie_length = 0;
}
if (beacon_ie || pr_ie || ar_ie)
ret = mwifiex_update_uap_custom_ie(priv,
beacon_ie, &priv->beacon_idx,
pr_ie, &priv->proberesp_idx,
ar_ie, &priv->assocresp_idx);
done:
kfree(beacon_ie);
kfree(pr_ie);
kfree(ar_ie);
kfree(rsn_ie);
return ret;
}

View file

@ -0,0 +1,749 @@
/*
* Marvell Wireless LAN device driver: HW/FW Initialization
*
* Copyright (C) 2011-2014, 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;
struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl;
unsigned long flags;
bss_prio = kzalloc(sizeof(struct mwifiex_bss_prio_node), GFP_KERNEL);
if (!bss_prio)
return -ENOMEM;
bss_prio->priv = priv;
INIT_LIST_HEAD(&bss_prio->list);
spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags);
list_add_tail(&bss_prio->list, &tbl[priv->bss_priority].bss_prio_head);
spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags);
return 0;
}
/*
* This function initializes the private structure and sets default
* values to the members.
*
* Additionally, it also initializes all the locks and sets up all the
* lists.
*/
int mwifiex_init_priv(struct mwifiex_private *priv)
{
u32 i;
priv->media_connected = false;
memset(priv->curr_addr, 0xff, ETH_ALEN);
priv->pkt_tx_ctrl = 0;
priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
priv->data_rate = 0; /* Initially indicate the rate as auto */
priv->is_data_rate_auto = true;
priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR;
priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR;
priv->sec_info.wep_enabled = 0;
priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM;
priv->sec_info.encryption_mode = 0;
for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++)
memset(&priv->wep_key[i], 0, sizeof(struct 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->wps_ie = NULL;
priv->wps_ie_len = 0;
priv->ap_11n_enabled = 0;
memset(&priv->roc_cfg, 0, sizeof(priv->roc_cfg));
priv->scan_block = false;
priv->csa_chan = 0;
priv->csa_expire_time = 0;
priv->del_list_idx = 0;
priv->hs2_enabled = false;
memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID);
return mwifiex_add_bss_prio_tbl(priv);
}
/*
* This function allocates buffers for members of the adapter
* structure.
*
* The memory allocated includes scan table, command buffers, and
* sleep confirm command buffer. In addition, the queues are
* also initialized.
*/
static int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter)
{
int ret;
/* 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)
+ 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 *sleep_cfm_buf = NULL;
skb_put(adapter->sleep_cfm, sizeof(struct mwifiex_opt_sleep_confirm));
adapter->cmd_sent = false;
if (adapter->iface_type == MWIFIEX_SDIO)
adapter->data_sent = true;
else
adapter->data_sent = false;
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->scan_chan_gap_time = MWIFIEX_DEF_SCAN_CHAN_GAP_TIME;
adapter->scan_probes = 1;
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->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
adapter->is_hs_configured = false;
adapter->hs_cfg.conditions = cpu_to_le32(HS_CFG_COND_DEF);
adapter->hs_cfg.gpio = HS_CFG_GPIO_DEF;
adapter->hs_cfg.gap = HS_CFG_GAP_DEF;
adapter->hs_activated = false;
memset(adapter->event_body, 0, sizeof(adapter->event_body));
adapter->hw_dot_11n_dev_cap = 0;
adapter->hw_dev_mcs_support = 0;
adapter->sec_chan_offset = 0;
adapter->adhoc_11n_enabled = false;
mwifiex_wmm_init(adapter);
if (adapter->sleep_cfm) {
sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *)
adapter->sleep_cfm->data;
memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len);
sleep_cfm_buf->command =
cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH);
sleep_cfm_buf->size =
cpu_to_le16(adapter->sleep_cfm->len);
sleep_cfm_buf->result = 0;
sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM);
sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED);
}
memset(&adapter->sleep_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;
adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX;
adapter->ext_scan = true;
adapter->key_api_major_ver = 0;
adapter->key_api_minor_ver = 0;
}
/*
* This function sets trans_start per tx_queue
*/
void mwifiex_set_trans_start(struct net_device *dev)
{
int i;
for (i = 0; i < dev->num_tx_queues; i++)
netdev_get_tx_queue(dev, i)->trans_start = jiffies;
dev->trans_start = jiffies;
}
/*
* This function wakes up all queues in net_device
*/
void mwifiex_wake_up_net_dev_queue(struct net_device *netdev,
struct mwifiex_adapter *adapter)
{
unsigned long dev_queue_flags;
unsigned int i;
spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags);
for (i = 0; i < netdev->num_tx_queues; i++) {
struct netdev_queue *txq = netdev_get_tx_queue(netdev, i);
if (netif_tx_queue_stopped(txq))
netif_tx_wake_queue(txq);
}
spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags);
}
/*
* This function stops all queues in net_device
*/
void mwifiex_stop_net_dev_queue(struct net_device *netdev,
struct mwifiex_adapter *adapter)
{
unsigned long dev_queue_flags;
unsigned int i;
spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags);
for (i = 0; i < netdev->num_tx_queues; i++) {
struct netdev_queue *txq = netdev_get_tx_queue(netdev, i);
if (!netif_tx_queue_stopped(txq))
netif_tx_stop_queue(txq);
}
spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags);
}
/*
* This function releases the lock variables and frees the locks and
* associated locks.
*/
static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
{
struct mwifiex_private *priv;
s32 i, j;
/* 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);
list_del(&priv->sta_list);
}
}
}
/*
* This function performs cleanup for adapter structure.
*
* The cleanup is done recursively, by canceling all pending
* commands, freeing the member buffers previously allocated
* (command buffers, scan table buffer, sleep confirm command
* buffer), stopping the timers and calling the cleanup routines
* for every interface.
*/
static void
mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
{
int idx;
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);
for (idx = 0; idx < adapter->num_mem_types; idx++) {
struct memory_type_mapping *entry =
&adapter->mem_type_mapping_tbl[idx];
if (entry->mem_ptr) {
vfree(entry->mem_ptr);
entry->mem_ptr = NULL;
}
entry->mem_size = 0;
}
if (adapter->sleep_cfm)
dev_kfree_skb_any(adapter->sleep_cfm);
}
/*
* This function intializes the lock variables and
* the list heads.
*/
int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
{
struct mwifiex_private *priv;
s32 i, j;
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);
spin_lock_init(&adapter->queue_lock);
for (i = 0; i < adapter->priv_num; i++) {
if (adapter->priv[i]) {
priv = adapter->priv[i];
spin_lock_init(&priv->rx_pkt_lock);
spin_lock_init(&priv->wmm.ra_list_spinlock);
spin_lock_init(&priv->curr_bcn_buf_lock);
spin_lock_init(&priv->sta_list_spinlock);
}
}
/* Initialize cmd_free_q */
INIT_LIST_HEAD(&adapter->cmd_free_q);
/* Initialize cmd_pending_q */
INIT_LIST_HEAD(&adapter->cmd_pending_q);
/* Initialize scan_pending_q */
INIT_LIST_HEAD(&adapter->scan_pending_q);
spin_lock_init(&adapter->cmd_free_q_lock);
spin_lock_init(&adapter->cmd_pending_q_lock);
spin_lock_init(&adapter->scan_pending_q_lock);
spin_lock_init(&adapter->rx_proc_lock);
skb_queue_head_init(&adapter->usb_rx_data_q);
skb_queue_head_init(&adapter->rx_data_q);
for (i = 0; i < adapter->priv_num; ++i) {
INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head);
spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock);
}
for (i = 0; i < adapter->priv_num; i++) {
if (!adapter->priv[i])
continue;
priv = adapter->priv[i];
for (j = 0; j < MAX_NUM_TID; ++j)
INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list);
INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
INIT_LIST_HEAD(&priv->sta_list);
skb_queue_head_init(&priv->tdls_txq);
spin_lock_init(&priv->tx_ba_stream_tbl_lock);
spin_lock_init(&priv->rx_reorder_tbl_lock);
}
return 0;
}
/*
* This function initializes the firmware.
*
* The following operations are performed sequentially -
* - Allocate adapter structure
* - Initialize the adapter structure
* - Initialize the private structure
* - Add BSS priority tables to the adapter structure
* - For each interface, send the init commands to firmware
* - Send the first command in command pending queue, if available
*/
int mwifiex_init_fw(struct mwifiex_adapter *adapter)
{
int ret;
struct mwifiex_private *priv;
u8 i, 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;
}
}
if (adapter->if_ops.init_fw_port) {
if (adapter->if_ops.init_fw_port(adapter))
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, *tmp_node;
struct list_head *head;
spinlock_t *lock; /* bss priority lock */
unsigned long flags;
for (i = 0; i < adapter->priv_num; ++i) {
head = &adapter->bss_prio_tbl[i].bss_prio_head;
lock = &adapter->bss_prio_tbl[i].bss_prio_lock;
dev_dbg(adapter->dev, "info: delete BSS priority table,"
" bss_type = %d, bss_num = %d, i = %d,"
" head = %p\n",
priv->bss_type, priv->bss_num, i, head);
{
spin_lock_irqsave(lock, flags);
if (list_empty(head)) {
spin_unlock_irqrestore(lock, flags);
continue;
}
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);
list_del(&bssprio_node->list);
kfree(bssprio_node);
}
}
spin_unlock_irqrestore(lock, flags);
}
}
}
/*
* This function frees the private structure, including cleans
* up the TX and RX queues and frees the BSS priority tables.
*/
void mwifiex_free_priv(struct mwifiex_private *priv)
{
mwifiex_clean_txrx(priv);
mwifiex_delete_bss_prio_tbl(priv);
mwifiex_free_curr_bcn(priv);
}
/*
* This function is used to shutdown the driver.
*
* The following operations are performed sequentially -
* - Check if already shut down
* - Make sure the main process has stopped
* - Clean up the Tx and Rx queues
* - Delete BSS priority tables
* - Free the adapter
* - Notify completion
*/
int
mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
{
int ret = -EINPROGRESS;
struct mwifiex_private *priv;
s32 i;
unsigned long flags;
struct sk_buff *skb;
/* 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;
}
/* cancel current command */
if (adapter->curr_cmd) {
dev_warn(adapter->dev, "curr_cmd is still in processing\n");
del_timer_sync(&adapter->cmd_timer);
mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
adapter->curr_cmd = NULL;
}
/* 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->rx_proc_lock, flags);
while ((skb = skb_dequeue(&adapter->rx_data_q))) {
struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
atomic_dec(&adapter->rx_pending);
priv = adapter->priv[rx_info->bss_num];
if (priv)
priv->stats.rx_dropped++;
dev_kfree_skb_any(skb);
}
spin_unlock_irqrestore(&adapter->rx_proc_lock, flags);
spin_lock(&adapter->mwifiex_lock);
if (adapter->if_ops.data_complete) {
while ((skb = skb_dequeue(&adapter->usb_rx_data_q))) {
struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb);
priv = adapter->priv[rx_info->bss_num];
if (priv)
priv->stats.rx_dropped++;
dev_kfree_skb_any(skb);
adapter->if_ops.data_complete(adapter);
}
}
mwifiex_adapter_cleanup(adapter);
spin_unlock(&adapter->mwifiex_lock);
/* 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;
u32 poll_num = 1;
if (adapter->if_ops.check_fw_status) {
adapter->winner = 0;
/* check if firmware is already running */
ret = adapter->if_ops.check_fw_status(adapter, poll_num);
if (!ret) {
dev_notice(adapter->dev,
"WLAN FW already running! Skip FW dnld\n");
return 0;
}
poll_num = MAX_FIRMWARE_POLL_TRIES;
/* check if we are the winner for downloading FW */
if (!adapter->winner) {
dev_notice(adapter->dev,
"FW already running! Skip FW dnld\n");
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);
if (ret)
dev_err(adapter->dev, "FW failed to be active in time\n");
return ret;
}

View file

@ -0,0 +1,454 @@
/*
* Marvell Wireless LAN device driver: ioctl data structures & APIs
*
* Copyright (C) 2011-2014, 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/lib80211.h>
enum {
MWIFIEX_SCAN_TYPE_UNCHANGED = 0,
MWIFIEX_SCAN_TYPE_ACTIVE,
MWIFIEX_SCAN_TYPE_PASSIVE
};
struct mwifiex_user_scan {
u32 scan_cfg_len;
u8 scan_cfg_buf[1];
};
#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];
};
struct mwifiex_chan_freq {
u32 channel;
u32 freq;
};
struct mwifiex_ssid_bssid {
struct cfg80211_ssid ssid;
u8 bssid[ETH_ALEN];
};
enum {
BAND_B = 1,
BAND_G = 2,
BAND_A = 4,
BAND_GN = 8,
BAND_AN = 16,
BAND_AAC = 32,
};
#define MWIFIEX_WPA_PASSHPHRASE_LEN 64
struct wpa_param {
u8 pairwise_cipher_wpa;
u8 pairwise_cipher_wpa2;
u8 group_cipher;
u32 length;
u8 passphrase[MWIFIEX_WPA_PASSHPHRASE_LEN];
};
struct wep_key {
u8 key_index;
u8 is_default;
u16 length;
u8 key[WLAN_KEY_LEN_WEP104];
};
#define KEY_MGMT_ON_HOST 0x03
#define MWIFIEX_AUTH_MODE_AUTO 0xFF
#define BAND_CONFIG_BG 0x00
#define BAND_CONFIG_A 0x01
#define MWIFIEX_SUPPORTED_RATES 14
#define MWIFIEX_SUPPORTED_RATES_EXT 32
#define MWIFIEX_TDLS_SUPPORTED_RATES 8
#define MWIFIEX_TDLS_DEF_QOS_CAPAB 0xf
#define MWIFIEX_PRIO_BK 2
#define MWIFIEX_PRIO_VI 5
struct mwifiex_uap_bss_param {
u8 channel;
u8 band_cfg;
u16 rts_threshold;
u16 frag_threshold;
u8 retry_limit;
struct mwifiex_802_11_ssid ssid;
u8 bcast_ssid_ctl;
u8 radio_ctl;
u8 dtim_period;
u16 beacon_period;
u16 auth_mode;
u16 protocol;
u16 key_mgmt;
u16 key_mgmt_operation;
struct wpa_param wpa_cfg;
struct wep_key wep_cfg[NUM_WEP_KEYS];
struct ieee80211_ht_cap ht_cap;
struct ieee80211_vht_cap vht_cap;
u8 rates[MWIFIEX_SUPPORTED_RATES];
u32 sta_ao_timer;
u32 ps_sta_ao_timer;
u8 qos_info;
struct mwifiex_types_wmm_info wmm_info;
};
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 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 cfg80211_ssid ssid;
u32 bss_chan;
u8 country_code[3];
u32 media_connected;
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];
u8 amsdu;
};
#define DBG_CMD_NUM 5
struct mwifiex_debug_info {
u32 int_counter;
u32 packets_out[MAX_NUM_TID];
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;
u8 is_cmd_timedout;
u16 timeout_cmd_id;
u16 timeout_cmd_act;
u16 last_cmd_id[DBG_CMD_NUM];
u16 last_cmd_act[DBG_CMD_NUM];
u16 last_cmd_index;
u16 last_cmd_resp_id[DBG_CMD_NUM];
u16 last_cmd_resp_index;
u16 last_event[DBG_CMD_NUM];
u16 last_event_index;
u8 data_sent;
u8 cmd_sent;
u8 cmd_resp_received;
u8 event_received;
};
#define MWIFIEX_KEY_INDEX_UNICAST 0x40000000
#define PN_LEN 16
struct mwifiex_ds_encrypt_key {
u32 key_disable;
u32 key_index;
u32 key_len;
u8 key_material[WLAN_MAX_KEY_LEN];
u8 mac_addr[ETH_ALEN];
u32 is_wapi_key;
u8 pn[PN_LEN]; /* packet number */
u8 pn_len;
u8 is_igtk_key;
u8 is_current_wep_key;
u8 is_rx_seq_valid;
};
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
#define PS_MODE_AUTO 1
struct mwifiex_ds_auto_ds {
u16 auto_ds;
u16 idle_time;
};
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_11ac_vht_cfg {
u8 band_config;
u8 misc_config;
u32 cap_info;
u32 mcs_tx_set;
u32 mcs_rx_set;
};
struct mwifiex_ds_11n_tx_cfg {
u16 tx_htcap;
u16 tx_htinfo;
u16 misc_config; /* Needed for 802.11AC cards only */
};
struct mwifiex_ds_11n_amsdu_aggr_ctrl {
u16 enable;
u16 curr_buf_size;
};
struct mwifiex_ds_ant_cfg {
u32 tx_ant;
u32 rx_ant;
};
#define MWIFIEX_NUM_OF_CMD_BUFFER 50
#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];
};
#define IEEE_MAX_IE_SIZE 256
#define MWIFIEX_IE_HDR_SIZE (sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE)
struct mwifiex_ds_misc_gen_ie {
u32 type;
u32 len;
u8 ie_data[IEEE_MAX_IE_SIZE];
};
struct mwifiex_ds_misc_cmd {
u32 len;
u8 cmd[MWIFIEX_SIZE_OF_CMD_BUFFER];
};
#define BITMASK_BCN_RSSI_LOW BIT(0)
#define BITMASK_BCN_RSSI_HIGH BIT(4)
enum subsc_evt_rssi_state {
EVENT_HANDLED,
RSSI_LOW_RECVD,
RSSI_HIGH_RECVD
};
struct subsc_evt_cfg {
u8 abs_value;
u8 evt_freq;
};
struct mwifiex_ds_misc_subsc_evt {
u16 action;
u16 events;
struct subsc_evt_cfg bcn_l_rssi_cfg;
struct subsc_evt_cfg bcn_h_rssi_cfg;
};
#define MWIFIEX_MEF_MAX_BYTESEQ 6 /* non-adjustable */
#define MWIFIEX_MEF_MAX_FILTERS 10
struct mwifiex_mef_filter {
u16 repeat;
u16 offset;
s8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1];
u8 filt_type;
u8 filt_action;
};
struct mwifiex_mef_entry {
u8 mode;
u8 action;
struct mwifiex_mef_filter filter[MWIFIEX_MEF_MAX_FILTERS];
};
struct mwifiex_ds_mef_cfg {
u32 criteria;
u16 num_entries;
struct mwifiex_mef_entry *mef_entry;
};
#define MWIFIEX_MAX_VSIE_LEN (256)
#define MWIFIEX_MAX_VSIE_NUM (8)
#define MWIFIEX_VSIE_MASK_CLEAR 0x00
#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,
};
enum COALESCE_OPERATION {
RECV_FILTER_MATCH_TYPE_EQ = 0x80,
RECV_FILTER_MATCH_TYPE_NE,
};
enum COALESCE_PACKET_TYPE {
PACKET_TYPE_UNICAST = 1,
PACKET_TYPE_MULTICAST = 2,
PACKET_TYPE_BROADCAST = 3
};
#define MWIFIEX_COALESCE_MAX_RULES 8
#define MWIFIEX_COALESCE_MAX_BYTESEQ 4 /* non-adjustable */
#define MWIFIEX_COALESCE_MAX_FILTERS 4
#define MWIFIEX_MAX_COALESCING_DELAY 100 /* in msecs */
struct filt_field_param {
u8 operation;
u8 operand_len;
u16 offset;
u8 operand_byte_stream[MWIFIEX_COALESCE_MAX_BYTESEQ];
};
struct mwifiex_coalesce_rule {
u16 max_coalescing_delay;
u8 num_of_fields;
u8 pkt_type;
struct filt_field_param params[MWIFIEX_COALESCE_MAX_FILTERS];
};
struct mwifiex_ds_coalesce_cfg {
u16 num_of_rules;
struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES];
};
struct mwifiex_ds_tdls_oper {
u16 tdls_action;
u8 peer_mac[ETH_ALEN];
u16 capability;
u8 qos_info;
u8 *ext_capab;
u8 ext_capab_len;
u8 *supp_rates;
u8 supp_rates_len;
u8 *ht_capab;
};
#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

View file

@ -0,0 +1,336 @@
/* @file mwifiex_pcie.h
*
* @brief This file contains definitions for PCI-E interface.
* driver.
*
* Copyright (C) 2011-2014, 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_PCIE_H
#define _MWIFIEX_PCIE_H
#include <linux/pci.h>
#include <linux/pcieport_if.h>
#include <linux/interrupt.h>
#include "main.h"
#define PCIE8766_DEFAULT_FW_NAME "mrvl/pcie8766_uapsta.bin"
#define PCIE8897_DEFAULT_FW_NAME "mrvl/pcie8897_uapsta.bin"
#define PCIE_VENDOR_ID_MARVELL (0x11ab)
#define PCIE_DEVICE_ID_MARVELL_88W8766P (0x2b30)
#define PCIE_DEVICE_ID_MARVELL_88W8897 (0x2b38)
/* Constants for Buffer Descriptor (BD) rings */
#define MWIFIEX_MAX_TXRX_BD 0x20
#define MWIFIEX_TXBD_MASK 0x3F
#define MWIFIEX_RXBD_MASK 0x3F
#define MWIFIEX_MAX_EVT_BD 0x08
#define MWIFIEX_EVTBD_MASK 0x0f
/* PCIE INTERNAL REGISTERS */
#define PCIE_SCRATCH_0_REG 0xC10
#define PCIE_SCRATCH_1_REG 0xC14
#define PCIE_CPU_INT_EVENT 0xC18
#define PCIE_CPU_INT_STATUS 0xC1C
#define PCIE_HOST_INT_STATUS 0xC30
#define PCIE_HOST_INT_MASK 0xC34
#define PCIE_HOST_INT_STATUS_MASK 0xC3C
#define PCIE_SCRATCH_2_REG 0xC40
#define PCIE_SCRATCH_3_REG 0xC44
#define PCIE_SCRATCH_4_REG 0xCD0
#define PCIE_SCRATCH_5_REG 0xCD4
#define PCIE_SCRATCH_6_REG 0xCD8
#define PCIE_SCRATCH_7_REG 0xCDC
#define PCIE_SCRATCH_8_REG 0xCE0
#define PCIE_SCRATCH_9_REG 0xCE4
#define PCIE_SCRATCH_10_REG 0xCE8
#define PCIE_SCRATCH_11_REG 0xCEC
#define PCIE_SCRATCH_12_REG 0xCF0
#define PCIE_RD_DATA_PTR_Q0_Q1 0xC08C
#define PCIE_WR_DATA_PTR_Q0_Q1 0xC05C
#define CPU_INTR_DNLD_RDY BIT(0)
#define CPU_INTR_DOOR_BELL BIT(1)
#define CPU_INTR_SLEEP_CFM_DONE BIT(2)
#define CPU_INTR_RESET BIT(3)
#define CPU_INTR_EVENT_DONE BIT(5)
#define HOST_INTR_DNLD_DONE BIT(0)
#define HOST_INTR_UPLD_RDY BIT(1)
#define HOST_INTR_CMD_DONE BIT(2)
#define HOST_INTR_EVENT_RDY BIT(3)
#define HOST_INTR_MASK (HOST_INTR_DNLD_DONE | \
HOST_INTR_UPLD_RDY | \
HOST_INTR_CMD_DONE | \
HOST_INTR_EVENT_RDY)
#define MWIFIEX_BD_FLAG_ROLLOVER_IND BIT(7)
#define MWIFIEX_BD_FLAG_FIRST_DESC BIT(0)
#define MWIFIEX_BD_FLAG_LAST_DESC BIT(1)
#define MWIFIEX_BD_FLAG_SOP BIT(0)
#define MWIFIEX_BD_FLAG_EOP BIT(1)
#define MWIFIEX_BD_FLAG_XS_SOP BIT(2)
#define MWIFIEX_BD_FLAG_XS_EOP BIT(3)
#define MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND BIT(7)
#define MWIFIEX_BD_FLAG_RX_ROLLOVER_IND BIT(10)
#define MWIFIEX_BD_FLAG_TX_START_PTR BIT(16)
#define MWIFIEX_BD_FLAG_TX_ROLLOVER_IND BIT(26)
/* Max retry number of command write */
#define MAX_WRITE_IOMEM_RETRY 2
/* Define PCIE block size for firmware download */
#define MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD 256
/* FW awake cookie after FW ready */
#define FW_AWAKE_COOKIE (0xAA55AA55)
#define MWIFIEX_DEF_SLEEP_COOKIE 0xBEEFBEEF
#define MWIFIEX_MAX_DELAY_COUNT 5
struct mwifiex_pcie_card_reg {
u16 cmd_addr_lo;
u16 cmd_addr_hi;
u16 fw_status;
u16 cmd_size;
u16 cmdrsp_addr_lo;
u16 cmdrsp_addr_hi;
u16 tx_rdptr;
u16 tx_wrptr;
u16 rx_rdptr;
u16 rx_wrptr;
u16 evt_rdptr;
u16 evt_wrptr;
u16 drv_rdy;
u16 tx_start_ptr;
u32 tx_mask;
u32 tx_wrap_mask;
u32 rx_mask;
u32 rx_wrap_mask;
u32 tx_rollover_ind;
u32 rx_rollover_ind;
u32 evt_rollover_ind;
u8 ring_flag_sop;
u8 ring_flag_eop;
u8 ring_flag_xs_sop;
u8 ring_flag_xs_eop;
u32 ring_tx_start_ptr;
u8 pfu_enabled;
u8 sleep_cookie;
u16 fw_dump_ctrl;
u16 fw_dump_start;
u16 fw_dump_end;
};
static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = {
.cmd_addr_lo = PCIE_SCRATCH_0_REG,
.cmd_addr_hi = PCIE_SCRATCH_1_REG,
.cmd_size = PCIE_SCRATCH_2_REG,
.fw_status = PCIE_SCRATCH_3_REG,
.cmdrsp_addr_lo = PCIE_SCRATCH_4_REG,
.cmdrsp_addr_hi = PCIE_SCRATCH_5_REG,
.tx_rdptr = PCIE_SCRATCH_6_REG,
.tx_wrptr = PCIE_SCRATCH_7_REG,
.rx_rdptr = PCIE_SCRATCH_8_REG,
.rx_wrptr = PCIE_SCRATCH_9_REG,
.evt_rdptr = PCIE_SCRATCH_10_REG,
.evt_wrptr = PCIE_SCRATCH_11_REG,
.drv_rdy = PCIE_SCRATCH_12_REG,
.tx_start_ptr = 0,
.tx_mask = MWIFIEX_TXBD_MASK,
.tx_wrap_mask = 0,
.rx_mask = MWIFIEX_RXBD_MASK,
.rx_wrap_mask = 0,
.tx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND,
.rx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND,
.evt_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND,
.ring_flag_sop = 0,
.ring_flag_eop = 0,
.ring_flag_xs_sop = 0,
.ring_flag_xs_eop = 0,
.ring_tx_start_ptr = 0,
.pfu_enabled = 0,
.sleep_cookie = 1,
};
static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = {
.cmd_addr_lo = PCIE_SCRATCH_0_REG,
.cmd_addr_hi = PCIE_SCRATCH_1_REG,
.cmd_size = PCIE_SCRATCH_2_REG,
.fw_status = PCIE_SCRATCH_3_REG,
.cmdrsp_addr_lo = PCIE_SCRATCH_4_REG,
.cmdrsp_addr_hi = PCIE_SCRATCH_5_REG,
.tx_rdptr = PCIE_RD_DATA_PTR_Q0_Q1,
.tx_wrptr = PCIE_WR_DATA_PTR_Q0_Q1,
.rx_rdptr = PCIE_WR_DATA_PTR_Q0_Q1,
.rx_wrptr = PCIE_RD_DATA_PTR_Q0_Q1,
.evt_rdptr = PCIE_SCRATCH_10_REG,
.evt_wrptr = PCIE_SCRATCH_11_REG,
.drv_rdy = PCIE_SCRATCH_12_REG,
.tx_start_ptr = 16,
.tx_mask = 0x03FF0000,
.tx_wrap_mask = 0x07FF0000,
.rx_mask = 0x000003FF,
.rx_wrap_mask = 0x000007FF,
.tx_rollover_ind = MWIFIEX_BD_FLAG_TX_ROLLOVER_IND,
.rx_rollover_ind = MWIFIEX_BD_FLAG_RX_ROLLOVER_IND,
.evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND,
.ring_flag_sop = MWIFIEX_BD_FLAG_SOP,
.ring_flag_eop = MWIFIEX_BD_FLAG_EOP,
.ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP,
.ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP,
.ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR,
.pfu_enabled = 1,
.sleep_cookie = 0,
.fw_dump_ctrl = 0xcf4,
.fw_dump_start = 0xcf8,
.fw_dump_end = 0xcff
};
struct mwifiex_pcie_device {
const char *firmware;
const struct mwifiex_pcie_card_reg *reg;
u16 blksz_fw_dl;
u16 tx_buf_size;
bool supports_fw_dump;
};
static const struct mwifiex_pcie_device mwifiex_pcie8766 = {
.firmware = PCIE8766_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_8766,
.blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
.supports_fw_dump = false,
};
static const struct mwifiex_pcie_device mwifiex_pcie8897 = {
.firmware = PCIE8897_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_8897,
.blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
.supports_fw_dump = true,
};
struct mwifiex_evt_buf_desc {
u64 paddr;
u16 len;
u16 flags;
} __packed;
struct mwifiex_pcie_buf_desc {
u64 paddr;
u16 len;
u16 flags;
} __packed;
struct mwifiex_pfu_buf_desc {
u16 flags;
u16 offset;
u16 frag_len;
u16 len;
u64 paddr;
u32 reserved;
} __packed;
struct pcie_service_card {
struct pci_dev *dev;
struct mwifiex_adapter *adapter;
struct mwifiex_pcie_device pcie;
u8 txbd_flush;
u32 txbd_wrptr;
u32 txbd_rdptr;
u32 txbd_ring_size;
u8 *txbd_ring_vbase;
dma_addr_t txbd_ring_pbase;
void *txbd_ring[MWIFIEX_MAX_TXRX_BD];
struct sk_buff *tx_buf_list[MWIFIEX_MAX_TXRX_BD];
u32 rxbd_wrptr;
u32 rxbd_rdptr;
u32 rxbd_ring_size;
u8 *rxbd_ring_vbase;
dma_addr_t rxbd_ring_pbase;
void *rxbd_ring[MWIFIEX_MAX_TXRX_BD];
struct sk_buff *rx_buf_list[MWIFIEX_MAX_TXRX_BD];
u32 evtbd_wrptr;
u32 evtbd_rdptr;
u32 evtbd_ring_size;
u8 *evtbd_ring_vbase;
dma_addr_t evtbd_ring_pbase;
void *evtbd_ring[MWIFIEX_MAX_EVT_BD];
struct sk_buff *evt_buf_list[MWIFIEX_MAX_EVT_BD];
struct sk_buff *cmd_buf;
struct sk_buff *cmdrsp_buf;
u8 *sleep_cookie_vbase;
dma_addr_t sleep_cookie_pbase;
void __iomem *pci_mmap;
void __iomem *pci_mmap1;
};
static inline int
mwifiex_pcie_txbd_empty(struct pcie_service_card *card, u32 rdptr)
{
const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
switch (card->dev->device) {
case PCIE_DEVICE_ID_MARVELL_88W8766P:
if (((card->txbd_wrptr & reg->tx_mask) ==
(rdptr & reg->tx_mask)) &&
((card->txbd_wrptr & reg->tx_rollover_ind) !=
(rdptr & reg->tx_rollover_ind)))
return 1;
break;
case PCIE_DEVICE_ID_MARVELL_88W8897:
if (((card->txbd_wrptr & reg->tx_mask) ==
(rdptr & reg->tx_mask)) &&
((card->txbd_wrptr & reg->tx_rollover_ind) ==
(rdptr & reg->tx_rollover_ind)))
return 1;
break;
}
return 0;
}
static inline int
mwifiex_pcie_txbd_not_full(struct pcie_service_card *card)
{
const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
switch (card->dev->device) {
case PCIE_DEVICE_ID_MARVELL_88W8766P:
if (((card->txbd_wrptr & reg->tx_mask) !=
(card->txbd_rdptr & reg->tx_mask)) ||
((card->txbd_wrptr & reg->tx_rollover_ind) !=
(card->txbd_rdptr & reg->tx_rollover_ind)))
return 1;
break;
case PCIE_DEVICE_ID_MARVELL_88W8897:
if (((card->txbd_wrptr & reg->tx_mask) !=
(card->txbd_rdptr & reg->tx_mask)) ||
((card->txbd_wrptr & reg->tx_rollover_ind) ==
(card->txbd_rdptr & reg->tx_rollover_ind)))
return 1;
break;
}
return 0;
}
#endif /* _MWIFIEX_PCIE_H */

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,537 @@
/*
* Marvell Wireless LAN device driver: SDIO specific definitions
*
* Copyright (C) 2011-2014, 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 <linux/mmc/host.h>
#include "main.h"
#define SD8786_DEFAULT_FW_NAME "mrvl/sd8786_uapsta.bin"
#define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin"
#define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin"
#define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin"
#define SD8887_DEFAULT_FW_NAME "mrvl/sd8887_uapsta.bin"
#define BLOCK_MODE 1
#define BYTE_MODE 0
#define REG_PORT 0
#define MWIFIEX_SDIO_IO_PORT_MASK 0xfffff
#define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000
#define SDIO_MPA_ADDR_BASE 0x1000
#define CTRL_PORT 0
#define CTRL_PORT_MASK 0x0001
#define CMD_PORT_UPLD_INT_MASK (0x1U<<6)
#define CMD_PORT_DNLD_INT_MASK (0x1U<<7)
#define HOST_TERM_CMD53 (0x1U << 2)
#define REG_PORT 0
#define MEM_PORT 0x10000
#define CMD53_NEW_MODE (0x1U << 0)
#define CMD_PORT_RD_LEN_EN (0x1U << 2)
#define CMD_PORT_AUTO_EN (0x1U << 0)
#define CMD_PORT_SLCT 0x8000
#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U)
#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U)
#define MWIFIEX_MP_AGGR_BUF_SIZE_16K (16384)
#define MWIFIEX_MP_AGGR_BUF_SIZE_32K (32768)
/* Misc. Config Register : Auto Re-enable interrupts */
#define AUTO_RE_ENABLE_INT BIT(4)
/* Host Control Registers : Configuration */
#define CONFIGURATION_REG 0x00
/* Host Control Registers : Host power up */
#define HOST_POWER_UP (0x1U << 1)
/* Host Control Registers : Upload host interrupt mask */
#define UP_LD_HOST_INT_MASK (0x1U)
/* Host Control Registers : Download host interrupt mask */
#define DN_LD_HOST_INT_MASK (0x2U)
/* Host Control Registers : Upload host interrupt status */
#define UP_LD_HOST_INT_STATUS (0x1U)
/* Host Control Registers : Download host interrupt status */
#define DN_LD_HOST_INT_STATUS (0x2U)
/* Host Control Registers : Host interrupt status */
#define CARD_INT_STATUS_REG 0x28
/* Card Control Registers : Card I/O ready */
#define CARD_IO_READY (0x1U << 3)
/* Card Control Registers : Download card ready */
#define DN_LD_CARD_RDY (0x1U << 0)
/* Max retry number of CMD53 write */
#define MAX_WRITE_IOMEM_RETRY 2
/* SDIO Tx aggregation in progress ? */
#define MP_TX_AGGR_IN_PROGRESS(a) (a->mpa_tx.pkt_cnt > 0)
/* SDIO Tx aggregation buffer room for next packet ? */
#define MP_TX_AGGR_BUF_HAS_ROOM(a, len) ((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+ \
(a->max_ports - \
a->mp_end_port))); \
a->mpa_tx.pkt_cnt++; \
} while (0)
/* SDIO Tx aggregation limit ? */
#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \
(a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit)
/* Reset SDIO Tx aggregation buffer parameters */
#define MP_TX_AGGR_BUF_RESET(a) do { \
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 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)
/* 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;
u32 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;
u32 ports;
u16 start_port;
struct sk_buff **skb_arr;
u32 *len_arr;
u8 enabled;
u32 buf_size;
u32 pkt_aggr_limit;
};
int mwifiex_bus_register(void);
void mwifiex_bus_unregister(void);
struct mwifiex_sdio_card_reg {
u8 start_rd_port;
u8 start_wr_port;
u8 base_0_reg;
u8 base_1_reg;
u8 poll_reg;
u8 host_int_enable;
u8 host_int_rsr_reg;
u8 host_int_status_reg;
u8 host_int_mask_reg;
u8 status_reg_0;
u8 status_reg_1;
u8 sdio_int_mask;
u32 data_port_mask;
u8 io_port_0_reg;
u8 io_port_1_reg;
u8 io_port_2_reg;
u8 max_mp_regs;
u8 rd_bitmap_l;
u8 rd_bitmap_u;
u8 rd_bitmap_1l;
u8 rd_bitmap_1u;
u8 wr_bitmap_l;
u8 wr_bitmap_u;
u8 wr_bitmap_1l;
u8 wr_bitmap_1u;
u8 rd_len_p0_l;
u8 rd_len_p0_u;
u8 card_misc_cfg_reg;
u8 card_cfg_2_1_reg;
u8 cmd_rd_len_0;
u8 cmd_rd_len_1;
u8 cmd_rd_len_2;
u8 cmd_rd_len_3;
u8 cmd_cfg_0;
u8 cmd_cfg_1;
u8 cmd_cfg_2;
u8 cmd_cfg_3;
u8 fw_dump_ctrl;
u8 fw_dump_start;
u8 fw_dump_end;
};
struct sdio_mmc_card {
struct sdio_func *func;
struct mwifiex_adapter *adapter;
const char *firmware;
const struct mwifiex_sdio_card_reg *reg;
u8 max_ports;
u8 mp_agg_pkt_limit;
bool supports_sdio_new_mode;
bool has_control_mask;
bool supports_fw_dump;
u16 tx_buf_size;
u32 mp_tx_agg_buf_size;
u32 mp_rx_agg_buf_size;
u32 mp_rd_bitmap;
u32 mp_wr_bitmap;
u16 mp_end_port;
u32 mp_data_port_mask;
u8 curr_rd_port;
u8 curr_wr_port;
u8 *mp_regs;
struct mwifiex_sdio_mpa_tx mpa_tx;
struct mwifiex_sdio_mpa_rx mpa_rx;
};
struct mwifiex_sdio_device {
const char *firmware;
const struct mwifiex_sdio_card_reg *reg;
u8 max_ports;
u8 mp_agg_pkt_limit;
bool supports_sdio_new_mode;
bool has_control_mask;
bool supports_fw_dump;
u16 tx_buf_size;
u32 mp_tx_agg_buf_size;
u32 mp_rx_agg_buf_size;
};
static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = {
.start_rd_port = 1,
.start_wr_port = 1,
.base_0_reg = 0x0040,
.base_1_reg = 0x0041,
.poll_reg = 0x30,
.host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK,
.host_int_rsr_reg = 0x1,
.host_int_mask_reg = 0x02,
.host_int_status_reg = 0x03,
.status_reg_0 = 0x60,
.status_reg_1 = 0x61,
.sdio_int_mask = 0x3f,
.data_port_mask = 0x0000fffe,
.io_port_0_reg = 0x78,
.io_port_1_reg = 0x79,
.io_port_2_reg = 0x7A,
.max_mp_regs = 64,
.rd_bitmap_l = 0x04,
.rd_bitmap_u = 0x05,
.wr_bitmap_l = 0x06,
.wr_bitmap_u = 0x07,
.rd_len_p0_l = 0x08,
.rd_len_p0_u = 0x09,
.card_misc_cfg_reg = 0x6c,
};
static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = {
.start_rd_port = 0,
.start_wr_port = 0,
.base_0_reg = 0x60,
.base_1_reg = 0x61,
.poll_reg = 0x50,
.host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK |
CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK,
.host_int_rsr_reg = 0x1,
.host_int_status_reg = 0x03,
.host_int_mask_reg = 0x02,
.status_reg_0 = 0xc0,
.status_reg_1 = 0xc1,
.sdio_int_mask = 0xff,
.data_port_mask = 0xffffffff,
.io_port_0_reg = 0xD8,
.io_port_1_reg = 0xD9,
.io_port_2_reg = 0xDA,
.max_mp_regs = 184,
.rd_bitmap_l = 0x04,
.rd_bitmap_u = 0x05,
.rd_bitmap_1l = 0x06,
.rd_bitmap_1u = 0x07,
.wr_bitmap_l = 0x08,
.wr_bitmap_u = 0x09,
.wr_bitmap_1l = 0x0a,
.wr_bitmap_1u = 0x0b,
.rd_len_p0_l = 0x0c,
.rd_len_p0_u = 0x0d,
.card_misc_cfg_reg = 0xcc,
.card_cfg_2_1_reg = 0xcd,
.cmd_rd_len_0 = 0xb4,
.cmd_rd_len_1 = 0xb5,
.cmd_rd_len_2 = 0xb6,
.cmd_rd_len_3 = 0xb7,
.cmd_cfg_0 = 0xb8,
.cmd_cfg_1 = 0xb9,
.cmd_cfg_2 = 0xba,
.cmd_cfg_3 = 0xbb,
.fw_dump_ctrl = 0xe2,
.fw_dump_start = 0xe3,
.fw_dump_end = 0xea,
};
static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8887 = {
.start_rd_port = 0,
.start_wr_port = 0,
.base_0_reg = 0x6C,
.base_1_reg = 0x6D,
.poll_reg = 0x5C,
.host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK |
CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK,
.host_int_rsr_reg = 0x4,
.host_int_status_reg = 0x0C,
.host_int_mask_reg = 0x08,
.status_reg_0 = 0x90,
.status_reg_1 = 0x91,
.sdio_int_mask = 0xff,
.data_port_mask = 0xffffffff,
.io_port_0_reg = 0xE4,
.io_port_1_reg = 0xE5,
.io_port_2_reg = 0xE6,
.max_mp_regs = 196,
.rd_bitmap_l = 0x10,
.rd_bitmap_u = 0x11,
.rd_bitmap_1l = 0x12,
.rd_bitmap_1u = 0x13,
.wr_bitmap_l = 0x14,
.wr_bitmap_u = 0x15,
.wr_bitmap_1l = 0x16,
.wr_bitmap_1u = 0x17,
.rd_len_p0_l = 0x18,
.rd_len_p0_u = 0x19,
.card_misc_cfg_reg = 0xd8,
.card_cfg_2_1_reg = 0xd9,
.cmd_rd_len_0 = 0xc0,
.cmd_rd_len_1 = 0xc1,
.cmd_rd_len_2 = 0xc2,
.cmd_rd_len_3 = 0xc3,
.cmd_cfg_0 = 0xc4,
.cmd_cfg_1 = 0xc5,
.cmd_cfg_2 = 0xc6,
.cmd_cfg_3 = 0xc7,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
.firmware = SD8786_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_sd87xx,
.max_ports = 16,
.mp_agg_pkt_limit = 8,
.supports_sdio_new_mode = false,
.has_control_mask = true,
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.supports_fw_dump = false,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
.firmware = SD8787_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_sd87xx,
.max_ports = 16,
.mp_agg_pkt_limit = 8,
.supports_sdio_new_mode = false,
.has_control_mask = true,
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.supports_fw_dump = false,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
.firmware = SD8797_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_sd87xx,
.max_ports = 16,
.mp_agg_pkt_limit = 8,
.supports_sdio_new_mode = false,
.has_control_mask = true,
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.supports_fw_dump = false,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
.firmware = SD8897_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_sd8897,
.max_ports = 32,
.mp_agg_pkt_limit = 16,
.supports_sdio_new_mode = true,
.has_control_mask = false,
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
.supports_fw_dump = true,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = {
.firmware = SD8887_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_sd8887,
.max_ports = 32,
.mp_agg_pkt_limit = 16,
.supports_sdio_new_mode = true,
.has_control_mask = false,
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
.supports_fw_dump = false,
};
/*
* .cmdrsp_complete handler
*/
static inline int mwifiex_sdio_cmdrsp_complete(struct mwifiex_adapter *adapter,
struct sk_buff *skb)
{
dev_kfree_skb_any(skb);
return 0;
}
/*
* .event_complete handler
*/
static inline int mwifiex_sdio_event_complete(struct mwifiex_adapter *adapter,
struct sk_buff *skb)
{
dev_kfree_skb_any(skb);
return 0;
}
static inline bool
mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card)
{
u8 tmp;
if (card->curr_rd_port < card->mpa_rx.start_port) {
if (card->supports_sdio_new_mode)
tmp = card->mp_end_port >> 1;
else
tmp = card->mp_agg_pkt_limit;
if (((card->max_ports - card->mpa_rx.start_port) +
card->curr_rd_port) >= tmp)
return true;
}
if (!card->supports_sdio_new_mode)
return false;
if ((card->curr_rd_port - card->mpa_rx.start_port) >=
(card->mp_end_port >> 1))
return true;
return false;
}
static inline bool
mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card)
{
u16 tmp;
if (card->curr_wr_port < card->mpa_tx.start_port) {
if (card->supports_sdio_new_mode)
tmp = card->mp_end_port >> 1;
else
tmp = card->mp_agg_pkt_limit;
if (((card->max_ports - card->mpa_tx.start_port) +
card->curr_wr_port) >= tmp)
return true;
}
if (!card->supports_sdio_new_mode)
return false;
if ((card->curr_wr_port - card->mpa_tx.start_port) >=
(card->mp_end_port >> 1))
return true;
return false;
}
/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */
static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card,
struct sk_buff *skb, u8 port)
{
card->mpa_rx.buf_len += skb->len;
if (!card->mpa_rx.pkt_cnt)
card->mpa_rx.start_port = port;
if (card->supports_sdio_new_mode) {
card->mpa_rx.ports |= (1 << port);
} else {
if (card->mpa_rx.start_port <= port)
card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt);
else
card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1);
}
card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = skb;
card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = skb->len;
card->mpa_rx.pkt_cnt++;
}
#endif /* _MWIFIEX_SDIO_H */

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,513 @@
/*
* Marvell Wireless LAN device driver: station event handling
*
* Copyright (C) 2011-2014, 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, u16 reason_code)
{
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;
if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info))
mwifiex_disable_all_tdls_links(priv);
/* 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 = 0;
/* Enable auto data rate */
priv->is_data_rate_auto = true;
priv->data_rate = 0;
if (priv->bss_mode == NL80211_IFTYPE_ADHOC) {
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 cfg80211_ssid));
memcpy(priv->prev_bssid,
priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN);
/* Need to erase the current SSID and BSSID info */
memset(&priv->curr_bss_params, 0x00, sizeof(priv->curr_bss_params));
adapter->tx_lock_flag = false;
adapter->pps_uapsd_mode = false;
if (adapter->is_cmd_timedout && adapter->curr_cmd)
return;
priv->media_connected = false;
dev_dbg(adapter->dev,
"info: successfully disconnected from %pM: reason code %d\n",
priv->cfg_bssid, reason_code);
if (priv->bss_mode == NL80211_IFTYPE_STATION ||
priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
cfg80211_disconnected(priv->netdev, reason_code, NULL, 0,
GFP_KERNEL);
}
memset(priv->cfg_bssid, 0, ETH_ALEN);
mwifiex_stop_net_dev_queue(priv->netdev, adapter);
if (netif_carrier_ok(priv->netdev))
netif_carrier_off(priv->netdev);
}
static int mwifiex_parse_tdls_event(struct mwifiex_private *priv,
struct sk_buff *event_skb)
{
int ret = 0;
struct mwifiex_adapter *adapter = priv->adapter;
struct mwifiex_sta_node *sta_ptr;
struct mwifiex_tdls_generic_event *tdls_evt =
(void *)event_skb->data + sizeof(adapter->event_cause);
/* reserved 2 bytes are not mandatory in tdls event */
if (event_skb->len < (sizeof(struct mwifiex_tdls_generic_event) -
sizeof(u16) - sizeof(adapter->event_cause))) {
dev_err(adapter->dev, "Invalid event length!\n");
return -1;
}
sta_ptr = mwifiex_get_sta_entry(priv, tdls_evt->peer_mac);
if (!sta_ptr) {
dev_err(adapter->dev, "cannot get sta entry!\n");
return -1;
}
switch (le16_to_cpu(tdls_evt->type)) {
case TDLS_EVENT_LINK_TEAR_DOWN:
cfg80211_tdls_oper_request(priv->netdev,
tdls_evt->peer_mac,
NL80211_TDLS_TEARDOWN,
le16_to_cpu(tdls_evt->u.reason_code),
GFP_KERNEL);
ret = mwifiex_tdls_oper(priv, tdls_evt->peer_mac,
MWIFIEX_TDLS_DISABLE_LINK);
queue_work(adapter->workqueue, &adapter->main_work);
break;
default:
break;
}
return ret;
}
/*
* 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;
u16 ctrl, reason_code;
switch (eventcause) {
case EVENT_DUMMY_HOST_WAKEUP_SIGNAL:
dev_err(adapter->dev,
"invalid EVENT: DUMMY_HOST_WAKEUP_SIGNAL, ignore 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);
mwifiex_wake_up_net_dev_queue(priv->netdev, adapter);
break;
case EVENT_DEAUTHENTICATED:
dev_dbg(adapter->dev, "event: Deauthenticated\n");
if (priv->wps.session_enable) {
dev_dbg(adapter->dev,
"info: receive deauth event in wps session\n");
break;
}
adapter->dbg.num_event_deauth++;
if (priv->media_connected) {
reason_code =
le16_to_cpu(*(__le16 *)adapter->event_body);
mwifiex_reset_connect_state(priv, reason_code);
}
break;
case EVENT_DISASSOCIATED:
dev_dbg(adapter->dev, "event: Disassociated\n");
if (priv->wps.session_enable) {
dev_dbg(adapter->dev,
"info: receive disassoc event in wps session\n");
break;
}
adapter->dbg.num_event_disassoc++;
if (priv->media_connected) {
reason_code =
le16_to_cpu(*(__le16 *)adapter->event_body);
mwifiex_reset_connect_state(priv, reason_code);
}
break;
case EVENT_LINK_LOST:
dev_dbg(adapter->dev, "event: Link lost\n");
adapter->dbg.num_event_link_lost++;
if (priv->media_connected) {
reason_code =
le16_to_cpu(*(__le16 *)adapter->event_body);
mwifiex_reset_connect_state(priv, reason_code);
}
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) {
adapter->ps_state = PS_STATE_AWAKE;
adapter->pm_wakeup_card_req = false;
adapter->pm_wakeup_fw_try = false;
break;
}
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_send_cmd(priv, HostCmd_CMD_802_11_HS_CFG_ENH,
0, 0, NULL, false);
break;
case EVENT_MIC_ERR_UNICAST:
dev_dbg(adapter->dev, "event: UNICAST MIC ERROR\n");
cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid,
NL80211_KEYTYPE_PAIRWISE,
-1, NULL, GFP_KERNEL);
break;
case EVENT_MIC_ERR_MULTICAST:
dev_dbg(adapter->dev, "event: MULTICAST MIC ERROR\n");
cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid,
NL80211_KEYTYPE_GROUP,
-1, NULL, GFP_KERNEL);
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);
mwifiex_stop_net_dev_queue(priv->netdev, adapter);
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");
ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_BG_SCAN_QUERY,
HostCmd_ACT_GEN_GET, 0, NULL, false);
break;
case EVENT_PORT_RELEASE:
dev_dbg(adapter->dev, "event: PORT RELEASE\n");
break;
case EVENT_EXT_SCAN_REPORT:
dev_dbg(adapter->dev, "event: EXT_SCAN Report\n");
if (adapter->ext_scan)
ret = mwifiex_handle_event_ext_scan_report(priv,
adapter->event_skb->data);
break;
case EVENT_WMM_STATUS_CHANGE:
dev_dbg(adapter->dev, "event: WMM status changed\n");
ret = mwifiex_send_cmd(priv, HostCmd_CMD_WMM_GET_STATUS,
0, 0, NULL, false);
break;
case EVENT_RSSI_LOW:
cfg80211_cqm_rssi_notify(priv->netdev,
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
GFP_KERNEL);
mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO,
HostCmd_ACT_GEN_GET, 0, NULL, false);
priv->subsc_evt_rssi_state = RSSI_LOW_RECVD;
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:
cfg80211_cqm_rssi_notify(priv->netdev,
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
GFP_KERNEL);
mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO,
HostCmd_ACT_GEN_GET, 0, NULL, false);
priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD;
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_send_cmd(priv,
HostCmd_CMD_802_11_IBSS_COALESCING_STATUS,
HostCmd_ACT_GEN_GET, 0, NULL, false);
break;
case EVENT_ADDBA:
dev_dbg(adapter->dev, "event: ADDBA Request\n");
mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP,
HostCmd_ACT_GEN_SET, 0,
adapter->event_body, false);
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:
ctrl = le16_to_cpu(*(__le16 *)adapter->event_body);
dev_dbg(adapter->dev, "event: AMSDU_AGGR_CTRL %d\n", ctrl);
adapter->tx_buf_size =
min_t(u16, adapter->curr_tx_buf_size, ctrl);
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;
case EVENT_REMAIN_ON_CHAN_EXPIRED:
dev_dbg(adapter->dev, "event: Remain on channel expired\n");
cfg80211_remain_on_channel_expired(priv->wdev,
priv->roc_cfg.cookie,
&priv->roc_cfg.chan,
GFP_ATOMIC);
memset(&priv->roc_cfg, 0x00, sizeof(struct mwifiex_roc_cfg));
break;
case EVENT_CHANNEL_SWITCH_ANN:
dev_dbg(adapter->dev, "event: Channel Switch Announcement\n");
priv->csa_expire_time =
jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME);
priv->csa_chan = priv->curr_bss_params.bss_descriptor.channel;
ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE,
HostCmd_ACT_GEN_SET, 0,
priv->curr_bss_params.bss_descriptor.mac_address,
false);
break;
case EVENT_TDLS_GENERIC_EVENT:
ret = mwifiex_parse_tdls_event(priv, adapter->event_skb);
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,254 @@
/*
* Marvell Wireless LAN device driver: station RX data handling
*
* Copyright (C) 2011-2014, 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 <uapi/linux/ipv6.h>
#include <net/ndisc.h>
#include "decl.h"
#include "ioctl.h"
#include "util.h"
#include "fw.h"
#include "main.h"
#include "11n_aggr.h"
#include "11n_rxreorder.h"
/* This function checks if a frame is IPv4 ARP or IPv6 Neighbour advertisement
* frame. If frame has both source and destination mac address as same, this
* function drops such gratuitous frames.
*/
static bool
mwifiex_discard_gratuitous_arp(struct mwifiex_private *priv,
struct sk_buff *skb)
{
const struct mwifiex_arp_eth_header *arp;
struct ethhdr *eth;
struct ipv6hdr *ipv6;
struct icmp6hdr *icmpv6;
eth = (struct ethhdr *)skb->data;
switch (ntohs(eth->h_proto)) {
case ETH_P_ARP:
arp = (void *)(skb->data + sizeof(struct ethhdr));
if (arp->hdr.ar_op == htons(ARPOP_REPLY) ||
arp->hdr.ar_op == htons(ARPOP_REQUEST)) {
if (!memcmp(arp->ar_sip, arp->ar_tip, 4))
return true;
}
break;
case ETH_P_IPV6:
ipv6 = (void *)(skb->data + sizeof(struct ethhdr));
icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) +
sizeof(struct ipv6hdr));
if (NDISC_NEIGHBOUR_ADVERTISEMENT == icmpv6->icmp6_type) {
if (!memcmp(&ipv6->saddr, &ipv6->daddr,
sizeof(struct in6_addr)))
return true;
}
break;
default:
break;
}
return false;
}
/*
* This function processes the received packet and forwards it
* to kernel/upper layer.
*
* This function parses through the received packet and determines
* if it is a debug packet or normal packet.
*
* For non-debug packets, the function chops off unnecessary leading
* header bytes, reconstructs the packet as an ethernet frame or
* 802.2/llc/snap frame as required, and sends it to kernel/upper layer.
*
* The completion callback is called after processing in complete.
*/
int mwifiex_process_rx_packet(struct mwifiex_private *priv,
struct sk_buff *skb)
{
int ret;
struct rx_packet_hdr *rx_pkt_hdr;
struct rxpd *local_rx_pd;
int hdr_chop;
struct ethhdr *eth;
u16 rx_pkt_off, rx_pkt_len;
u8 *offset;
local_rx_pd = (struct rxpd *) (skb->data);
rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset);
rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length);
rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off;
if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header,
sizeof(bridge_tunnel_header))) ||
(!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header,
sizeof(rfc1042_header)) &&
ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP &&
ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) {
/*
* Replace the 803 header and rfc1042 header (llc/snap) with an
* EthernetII header, keep the src/dst and snap_type
* (ethertype).
* The firmware only passes up SNAP frames converting
* all RX Data from 802.11 to 802.2/LLC/SNAP frames.
* To create the Ethernet II, just move the src, dst address
* right before the snap_type.
*/
eth = (struct ethhdr *)
((u8 *) &rx_pkt_hdr->eth803_hdr
+ sizeof(rx_pkt_hdr->eth803_hdr) +
sizeof(rx_pkt_hdr->rfc1042_hdr)
- sizeof(rx_pkt_hdr->eth803_hdr.h_dest)
- sizeof(rx_pkt_hdr->eth803_hdr.h_source)
- sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type));
memcpy(eth->h_source, rx_pkt_hdr->eth803_hdr.h_source,
sizeof(eth->h_source));
memcpy(eth->h_dest, rx_pkt_hdr->eth803_hdr.h_dest,
sizeof(eth->h_dest));
/* Chop off the rxpd + the excess memory from the 802.2/llc/snap
header that was removed. */
hdr_chop = (u8 *) eth - (u8 *) local_rx_pd;
} else {
/* Chop off the rxpd */
hdr_chop = (u8 *) &rx_pkt_hdr->eth803_hdr -
(u8 *) local_rx_pd;
}
/* Chop off the leading header bytes so the it points to the start of
either the reconstructed EthII frame or the 802.2/llc/snap frame */
skb_pull(skb, hdr_chop);
if (priv->hs2_enabled &&
mwifiex_discard_gratuitous_arp(priv, skb)) {
dev_dbg(priv->adapter->dev, "Bypassed Gratuitous ARP\n");
dev_kfree_skb_any(skb);
return 0;
}
if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
ntohs(rx_pkt_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {
offset = (u8 *)local_rx_pd + rx_pkt_off;
mwifiex_process_tdls_action_frame(priv, offset, rx_pkt_len);
}
priv->rxpd_rate = local_rx_pd->rx_rate;
priv->rxpd_htinfo = local_rx_pd->ht_info;
ret = mwifiex_recv_packet(priv, skb);
if (ret == -1)
dev_err(priv->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_private *priv,
struct sk_buff *skb)
{
struct mwifiex_adapter *adapter = priv->adapter;
int ret = 0;
struct rxpd *local_rx_pd;
struct rx_packet_hdr *rx_pkt_hdr;
u8 ta[ETH_ALEN];
u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num;
struct mwifiex_sta_node *sta_ptr;
local_rx_pd = (struct rxpd *) (skb->data);
rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type);
rx_pkt_offset = le16_to_cpu(local_rx_pd->rx_pkt_offset);
rx_pkt_length = le16_to_cpu(local_rx_pd->rx_pkt_length);
seq_num = le16_to_cpu(local_rx_pd->seq_num);
rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_offset;
if ((rx_pkt_offset + rx_pkt_length) > (u16) skb->len) {
dev_err(adapter->dev,
"wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n",
skb->len, rx_pkt_offset, rx_pkt_length);
priv->stats.rx_dropped++;
dev_kfree_skb_any(skb);
return ret;
}
if (rx_pkt_type == PKT_TYPE_MGMT) {
ret = mwifiex_process_mgmt_packet(priv, skb);
if (ret)
dev_err(adapter->dev, "Rx of mgmt packet failed");
dev_kfree_skb_any(skb);
return ret;
}
/*
* If the packet is not an unicast packet then send the packet
* directly to os. Don't pass thru rx reordering
*/
if ((!IS_11N_ENABLED(priv) &&
!(ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
!(local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET))) ||
!ether_addr_equal_unaligned(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest)) {
mwifiex_process_rx_packet(priv, skb);
return ret;
}
if (mwifiex_queuing_ra_based(priv) ||
(ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET)) {
memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN);
if (local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET &&
local_rx_pd->priority < MAX_NUM_TID) {
sta_ptr = mwifiex_get_sta_entry(priv, ta);
if (sta_ptr)
sta_ptr->rx_seq[local_rx_pd->priority] =
le16_to_cpu(local_rx_pd->seq_num);
}
} else {
if (rx_pkt_type != PKT_TYPE_BAR)
priv->rx_seq[local_rx_pd->priority] = seq_num;
memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address,
ETH_ALEN);
}
/* Reorder and send to OS */
ret = mwifiex_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority,
ta, (u8) rx_pkt_type, skb);
if (ret || (rx_pkt_type == PKT_TYPE_BAR))
dev_kfree_skb_any(skb);
if (ret)
priv->stats.rx_dropped++;
return ret;
}

View file

@ -0,0 +1,225 @@
/*
* Marvell Wireless LAN device driver: station TX data handling
*
* Copyright (C) 2011-2014, 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);
u8 pad;
u16 pkt_type, pkt_offset;
if (!skb->len) {
dev_err(adapter->dev, "Tx: bad packet length: %d\n", skb->len);
tx_info->status_code = -1;
return skb->data;
}
pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0;
/* If skb->data is not aligned; add padding */
pad = (4 - (((void *)skb->data - NULL) & 0x3)) % 4;
BUG_ON(skb_headroom(skb) < (sizeof(*local_tx_pd) + INTF_HEADER_LEN
+ pad));
skb_push(skb, sizeof(*local_tx_pd) + pad);
local_tx_pd = (struct txpd *) skb->data;
memset(local_tx_pd, 0, sizeof(struct txpd));
local_tx_pd->bss_num = priv->bss_num;
local_tx_pd->bss_type = priv->bss_type;
local_tx_pd->tx_pkt_length = cpu_to_le16((u16)(skb->len -
(sizeof(struct txpd)
+ pad)));
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;
}
}
if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT)
local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET;
/* Offset of actual data */
pkt_offset = sizeof(struct txpd) + pad;
if (pkt_type == PKT_TYPE_MGMT) {
/* Set the packet type and add header for management frame */
local_tx_pd->tx_pkt_type = cpu_to_le16(pkt_type);
pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE;
}
local_tx_pd->tx_pkt_offset = cpu_to_le16(pkt_offset);
/* 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;
struct mwifiex_tx_param tx_param;
/* sizeof(struct txpd) + Interface specific header */
#define NULL_PACKET_HDR 64
u32 data_len = NULL_PACKET_HDR;
struct sk_buff *skb;
int ret;
struct 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);
memset(tx_info, 0, sizeof(*tx_info));
tx_info->bss_num = priv->bss_num;
tx_info->bss_type = priv->bss_type;
tx_info->pkt_len = data_len - (sizeof(struct txpd) + INTF_HEADER_LEN);
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;
if (adapter->iface_type == MWIFIEX_USB) {
ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA,
skb, NULL);
} else {
skb_push(skb, INTF_HEADER_LEN);
tx_param.next_pkt_len = 0;
ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA,
skb, &tx_param);
}
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;
if (!adapter->sleep_period.period)
return ret;
if (mwifiex_wmm_lists_empty(adapter))
ret = true;
if (ret && !adapter->cmd_sent && !adapter->curr_cmd &&
!is_command_pending(adapter)) {
adapter->delay_null_pkt = false;
ret = true;
} else {
ret = false;
adapter->delay_null_pkt = true;
}
return ret;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,209 @@
/*
* Marvell Wireless LAN device driver: generic TX/RX data handling
*
* Copyright (C) 2011-2014, 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)
{
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);
int ret;
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);
if (!priv) {
dev_err(adapter->dev, "data: priv not found. Drop RX packet\n");
dev_kfree_skb_any(skb);
return -1;
}
memset(rx_info, 0, sizeof(*rx_info));
rx_info->bss_num = priv->bss_num;
rx_info->bss_type = priv->bss_type;
if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
ret = mwifiex_process_uap_rx_packet(priv, skb);
else
ret = mwifiex_process_sta_rx_packet(priv, skb);
/* Decrement RX pending counter for each packet */
if (adapter->if_ops.data_complete)
adapter->if_ops.data_complete(adapter);
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;
struct txpd *local_tx_pd = NULL;
if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)
head_ptr = mwifiex_process_uap_txpd(priv, skb);
else
head_ptr = 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);
if (adapter->iface_type == MWIFIEX_USB) {
adapter->data_sent = true;
skb_pull(skb, INTF_HEADER_LEN);
ret = adapter->if_ops.host_to_card(adapter,
MWIFIEX_USB_EP_DATA,
skb, NULL);
} else {
ret = adapter->if_ops.host_to_card(adapter,
MWIFIEX_TYPE_DATA,
skb, tx_param);
}
}
switch (ret) {
case -ENOSR:
dev_dbg(adapter->dev, "data: -ENOSR is returned\n");
break;
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;
if (local_tx_pd)
local_tx_pd->flags = 0;
}
dev_dbg(adapter->dev, "data: -EBUSY is returned\n");
break;
case -1:
if (adapter->iface_type != MWIFIEX_PCIE)
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, 0, ret);
break;
case -EINPROGRESS:
if (adapter->iface_type != MWIFIEX_PCIE)
adapter->data_sent = false;
break;
case 0:
mwifiex_write_data_complete(adapter, skb, 0, 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 aggr, int status)
{
struct mwifiex_private *priv;
struct mwifiex_txinfo *tx_info;
struct netdev_queue *txq;
int index;
if (!skb)
return 0;
tx_info = MWIFIEX_SKB_TXCB(skb);
priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num,
tx_info->bss_type);
if (!priv)
goto done;
if (adapter->iface_type == MWIFIEX_USB)
adapter->data_sent = false;
mwifiex_set_trans_start(priv->netdev);
if (!status) {
priv->stats.tx_packets++;
priv->stats.tx_bytes += tx_info->pkt_len;
if (priv->tx_timeout_cnt)
priv->tx_timeout_cnt = 0;
} else {
priv->stats.tx_errors++;
}
if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT)
atomic_dec_return(&adapter->pending_bridged_pkts);
if (aggr)
/* For skb_aggr, do not wake up tx queue */
goto done;
atomic_dec(&adapter->tx_pending);
index = mwifiex_1d_to_wmm_queue[skb->priority];
if (atomic_dec_return(&priv->wmm_tx_pending[index]) < LOW_TX_PENDING) {
txq = netdev_get_tx_queue(priv->netdev, index);
if (netif_tx_queue_stopped(txq)) {
netif_tx_wake_queue(txq);
dev_dbg(adapter->dev, "wake queue: %d\n", index);
}
}
done:
dev_kfree_skb_any(skb);
return 0;
}
EXPORT_SYMBOL_GPL(mwifiex_write_data_complete);

View file

@ -0,0 +1,771 @@
/*
* Marvell Wireless LAN device driver: AP specific command handling
*
* Copyright (C) 2012-2014, 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 "main.h"
#include "11ac.h"
/* This function parses security related parameters from cfg80211_ap_settings
* and sets into FW understandable bss_config structure.
*/
int mwifiex_set_secure_params(struct mwifiex_private *priv,
struct mwifiex_uap_bss_param *bss_config,
struct cfg80211_ap_settings *params) {
int i;
struct mwifiex_wep_key wep_key;
if (!params->privacy) {
bss_config->protocol = PROTOCOL_NO_SECURITY;
bss_config->key_mgmt = KEY_MGMT_NONE;
bss_config->wpa_cfg.length = 0;
priv->sec_info.wep_enabled = 0;
priv->sec_info.wpa_enabled = 0;
priv->sec_info.wpa2_enabled = 0;
return 0;
}
switch (params->auth_type) {
case NL80211_AUTHTYPE_OPEN_SYSTEM:
bss_config->auth_mode = WLAN_AUTH_OPEN;
break;
case NL80211_AUTHTYPE_SHARED_KEY:
bss_config->auth_mode = WLAN_AUTH_SHARED_KEY;
break;
case NL80211_AUTHTYPE_NETWORK_EAP:
bss_config->auth_mode = WLAN_AUTH_LEAP;
break;
default:
bss_config->auth_mode = MWIFIEX_AUTH_MODE_AUTO;
break;
}
bss_config->key_mgmt_operation |= KEY_MGMT_ON_HOST;
for (i = 0; i < params->crypto.n_akm_suites; i++) {
switch (params->crypto.akm_suites[i]) {
case WLAN_AKM_SUITE_8021X:
if (params->crypto.wpa_versions &
NL80211_WPA_VERSION_1) {
bss_config->protocol = PROTOCOL_WPA;
bss_config->key_mgmt = KEY_MGMT_EAP;
}
if (params->crypto.wpa_versions &
NL80211_WPA_VERSION_2) {
bss_config->protocol |= PROTOCOL_WPA2;
bss_config->key_mgmt = KEY_MGMT_EAP;
}
break;
case WLAN_AKM_SUITE_PSK:
if (params->crypto.wpa_versions &
NL80211_WPA_VERSION_1) {
bss_config->protocol = PROTOCOL_WPA;
bss_config->key_mgmt = KEY_MGMT_PSK;
}
if (params->crypto.wpa_versions &
NL80211_WPA_VERSION_2) {
bss_config->protocol |= PROTOCOL_WPA2;
bss_config->key_mgmt = KEY_MGMT_PSK;
}
break;
default:
break;
}
}
for (i = 0; i < params->crypto.n_ciphers_pairwise; i++) {
switch (params->crypto.ciphers_pairwise[i]) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
break;
case WLAN_CIPHER_SUITE_TKIP:
if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
bss_config->wpa_cfg.pairwise_cipher_wpa |=
CIPHER_TKIP;
if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
bss_config->wpa_cfg.pairwise_cipher_wpa2 |=
CIPHER_TKIP;
break;
case WLAN_CIPHER_SUITE_CCMP:
if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
bss_config->wpa_cfg.pairwise_cipher_wpa |=
CIPHER_AES_CCMP;
if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
bss_config->wpa_cfg.pairwise_cipher_wpa2 |=
CIPHER_AES_CCMP;
default:
break;
}
}
switch (params->crypto.cipher_group) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
if (priv->sec_info.wep_enabled) {
bss_config->protocol = PROTOCOL_STATIC_WEP;
bss_config->key_mgmt = KEY_MGMT_NONE;
bss_config->wpa_cfg.length = 0;
for (i = 0; i < NUM_WEP_KEYS; i++) {
wep_key = priv->wep_key[i];
bss_config->wep_cfg[i].key_index = i;
if (priv->wep_key_curr_index == i)
bss_config->wep_cfg[i].is_default = 1;
else
bss_config->wep_cfg[i].is_default = 0;
bss_config->wep_cfg[i].length =
wep_key.key_length;
memcpy(&bss_config->wep_cfg[i].key,
&wep_key.key_material,
wep_key.key_length);
}
}
break;
case WLAN_CIPHER_SUITE_TKIP:
bss_config->wpa_cfg.group_cipher = CIPHER_TKIP;
break;
case WLAN_CIPHER_SUITE_CCMP:
bss_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP;
break;
default:
break;
}
return 0;
}
/* This function updates 11n related parameters from IE and sets them into
* bss_config structure.
*/
void
mwifiex_set_ht_params(struct mwifiex_private *priv,
struct mwifiex_uap_bss_param *bss_cfg,
struct cfg80211_ap_settings *params)
{
const u8 *ht_ie;
u16 cap_info;
if (!ISSUPP_11NENABLED(priv->adapter->fw_cap_info))
return;
ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, params->beacon.tail,
params->beacon.tail_len);
if (ht_ie) {
memcpy(&bss_cfg->ht_cap, ht_ie + 2,
sizeof(struct ieee80211_ht_cap));
cap_info = le16_to_cpu(bss_cfg->ht_cap.cap_info);
memset(&bss_cfg->ht_cap.mcs, 0,
priv->adapter->number_of_antenna);
switch (GET_RXSTBC(cap_info)) {
case MWIFIEX_RX_STBC1:
/* HT_CAP 1X1 mode */
bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
break;
case MWIFIEX_RX_STBC12: /* fall through */
case MWIFIEX_RX_STBC123:
/* HT_CAP 2X2 mode */
bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff;
break;
default:
dev_warn(priv->adapter->dev,
"Unsupported RX-STBC, default to 2x2\n");
bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff;
bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff;
break;
}
priv->ap_11n_enabled = 1;
} else {
memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap));
bss_cfg->ht_cap.cap_info = cpu_to_le16(MWIFIEX_DEF_HT_CAP);
bss_cfg->ht_cap.ampdu_params_info = MWIFIEX_DEF_AMPDU;
}
return;
}
/* This function updates 11ac related parameters from IE
* and sets them into bss_config structure.
*/
void mwifiex_set_vht_params(struct mwifiex_private *priv,
struct mwifiex_uap_bss_param *bss_cfg,
struct cfg80211_ap_settings *params)
{
const u8 *vht_ie;
vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, params->beacon.tail,
params->beacon.tail_len);
if (vht_ie) {
memcpy(&bss_cfg->vht_cap, vht_ie + 2,
sizeof(struct ieee80211_vht_cap));
priv->ap_11ac_enabled = 1;
} else {
priv->ap_11ac_enabled = 0;
}
return;
}
/* Enable VHT only when cfg80211_ap_settings has VHT IE.
* Otherwise disable VHT.
*/
void mwifiex_set_vht_width(struct mwifiex_private *priv,
enum nl80211_chan_width width,
bool ap_11ac_enable)
{
struct mwifiex_adapter *adapter = priv->adapter;
struct mwifiex_11ac_vht_cfg vht_cfg;
vht_cfg.band_config = VHT_CFG_5GHZ;
vht_cfg.cap_info = adapter->hw_dot_11ac_dev_cap;
if (!ap_11ac_enable) {
vht_cfg.mcs_tx_set = DISABLE_VHT_MCS_SET;
vht_cfg.mcs_rx_set = DISABLE_VHT_MCS_SET;
} else {
vht_cfg.mcs_tx_set = DEFAULT_VHT_MCS_SET;
vht_cfg.mcs_rx_set = DEFAULT_VHT_MCS_SET;
}
vht_cfg.misc_config = VHT_CAP_UAP_ONLY;
if (ap_11ac_enable && width >= NL80211_CHAN_WIDTH_80)
vht_cfg.misc_config |= VHT_BW_80_160_80P80;
mwifiex_send_cmd(priv, HostCmd_CMD_11AC_CFG,
HostCmd_ACT_GEN_SET, 0, &vht_cfg, true);
return;
}
/* This function finds supported rates IE from beacon parameter and sets
* these rates into bss_config structure.
*/
void
mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg,
struct cfg80211_ap_settings *params)
{
struct ieee_types_header *rate_ie;
int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
const u8 *var_pos = params->beacon.head + var_offset;
int len = params->beacon.head_len - var_offset;
u8 rate_len = 0;
rate_ie = (void *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
if (rate_ie) {
memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->len);
rate_len = rate_ie->len;
}
rate_ie = (void *)cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES,
params->beacon.tail,
params->beacon.tail_len);
if (rate_ie)
memcpy(bss_cfg->rates + rate_len, rate_ie + 1, rate_ie->len);
return;
}
/* This function initializes some of mwifiex_uap_bss_param variables.
* This helps FW in ignoring invalid values. These values may or may not
* be get updated to valid ones at later stage.
*/
void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config)
{
config->bcast_ssid_ctl = 0x7F;
config->radio_ctl = 0x7F;
config->dtim_period = 0x7F;
config->beacon_period = 0x7FFF;
config->auth_mode = 0x7F;
config->rts_threshold = 0x7FFF;
config->frag_threshold = 0x7FFF;
config->retry_limit = 0x7F;
config->qos_info = 0xFF;
}
/* This function parses BSS related parameters from structure
* and prepares TLVs specific to WPA/WPA2 security.
* These TLVs are appended to command buffer.
*/
static void
mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
{
struct host_cmd_tlv_pwk_cipher *pwk_cipher;
struct host_cmd_tlv_gwk_cipher *gwk_cipher;
struct host_cmd_tlv_passphrase *passphrase;
struct host_cmd_tlv_akmp *tlv_akmp;
struct mwifiex_uap_bss_param *bss_cfg = cmd_buf;
u16 cmd_size = *param_size;
u8 *tlv = *tlv_buf;
tlv_akmp = (struct host_cmd_tlv_akmp *)tlv;
tlv_akmp->header.type = cpu_to_le16(TLV_TYPE_UAP_AKMP);
tlv_akmp->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) -
sizeof(struct mwifiex_ie_types_header));
tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation);
tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt);
cmd_size += sizeof(struct host_cmd_tlv_akmp);
tlv += sizeof(struct host_cmd_tlv_akmp);
if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) {
pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
pwk_cipher->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
sizeof(struct mwifiex_ie_types_header));
pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA);
pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa;
cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
tlv += sizeof(struct host_cmd_tlv_pwk_cipher);
}
if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) {
pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
pwk_cipher->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
sizeof(struct mwifiex_ie_types_header));
pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2);
pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2;
cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
tlv += sizeof(struct host_cmd_tlv_pwk_cipher);
}
if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) {
gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv;
gwk_cipher->header.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER);
gwk_cipher->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) -
sizeof(struct mwifiex_ie_types_header));
gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher;
cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher);
tlv += sizeof(struct host_cmd_tlv_gwk_cipher);
}
if (bss_cfg->wpa_cfg.length) {
passphrase = (struct host_cmd_tlv_passphrase *)tlv;
passphrase->header.type =
cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE);
passphrase->header.len = cpu_to_le16(bss_cfg->wpa_cfg.length);
memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase,
bss_cfg->wpa_cfg.length);
cmd_size += sizeof(struct mwifiex_ie_types_header) +
bss_cfg->wpa_cfg.length;
tlv += sizeof(struct mwifiex_ie_types_header) +
bss_cfg->wpa_cfg.length;
}
*param_size = cmd_size;
*tlv_buf = tlv;
return;
}
/* This function parses WMM related parameters from cfg80211_ap_settings
* structure and updates bss_config structure.
*/
void
mwifiex_set_wmm_params(struct mwifiex_private *priv,
struct mwifiex_uap_bss_param *bss_cfg,
struct cfg80211_ap_settings *params)
{
const u8 *vendor_ie;
struct ieee_types_header *wmm_ie;
u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02};
vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
WLAN_OUI_TYPE_MICROSOFT_WMM,
params->beacon.tail,
params->beacon.tail_len);
if (vendor_ie) {
wmm_ie = (struct ieee_types_header *)vendor_ie;
memcpy(&bss_cfg->wmm_info, wmm_ie + 1,
sizeof(bss_cfg->wmm_info));
priv->wmm_enabled = 1;
} else {
memset(&bss_cfg->wmm_info, 0, sizeof(bss_cfg->wmm_info));
memcpy(&bss_cfg->wmm_info.oui, wmm_oui, sizeof(wmm_oui));
bss_cfg->wmm_info.subtype = MWIFIEX_WMM_SUBTYPE;
bss_cfg->wmm_info.version = MWIFIEX_WMM_VERSION;
priv->wmm_enabled = 0;
}
bss_cfg->qos_info = 0x00;
return;
}
/* This function parses BSS related parameters from structure
* and prepares TLVs specific to WEP encryption.
* These TLVs are appended to command buffer.
*/
static void
mwifiex_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size)
{
struct host_cmd_tlv_wep_key *wep_key;
u16 cmd_size = *param_size;
int i;
u8 *tlv = *tlv_buf;
struct mwifiex_uap_bss_param *bss_cfg = cmd_buf;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (bss_cfg->wep_cfg[i].length &&
(bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 ||
bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) {
wep_key = (struct host_cmd_tlv_wep_key *)tlv;
wep_key->header.type =
cpu_to_le16(TLV_TYPE_UAP_WEP_KEY);
wep_key->header.len =
cpu_to_le16(bss_cfg->wep_cfg[i].length + 2);
wep_key->key_index = bss_cfg->wep_cfg[i].key_index;
wep_key->is_default = bss_cfg->wep_cfg[i].is_default;
memcpy(wep_key->key, bss_cfg->wep_cfg[i].key,
bss_cfg->wep_cfg[i].length);
cmd_size += sizeof(struct mwifiex_ie_types_header) + 2 +
bss_cfg->wep_cfg[i].length;
tlv += sizeof(struct mwifiex_ie_types_header) + 2 +
bss_cfg->wep_cfg[i].length;
}
}
*param_size = cmd_size;
*tlv_buf = tlv;
return;
}
/* This function parses BSS related parameters from structure
* and prepares TLVs. These TLVs are appended to command buffer.
*/
static int
mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size)
{
struct host_cmd_tlv_dtim_period *dtim_period;
struct host_cmd_tlv_beacon_period *beacon_period;
struct host_cmd_tlv_ssid *ssid;
struct host_cmd_tlv_bcast_ssid *bcast_ssid;
struct host_cmd_tlv_channel_band *chan_band;
struct host_cmd_tlv_frag_threshold *frag_threshold;
struct host_cmd_tlv_rts_threshold *rts_threshold;
struct host_cmd_tlv_retry_limit *retry_limit;
struct host_cmd_tlv_encrypt_protocol *encrypt_protocol;
struct host_cmd_tlv_auth_type *auth_type;
struct host_cmd_tlv_rates *tlv_rates;
struct host_cmd_tlv_ageout_timer *ao_timer, *ps_ao_timer;
struct mwifiex_ie_types_htcap *htcap;
struct mwifiex_ie_types_wmmcap *wmm_cap;
struct mwifiex_uap_bss_param *bss_cfg = cmd_buf;
int i;
u16 cmd_size = *param_size;
if (bss_cfg->ssid.ssid_len) {
ssid = (struct host_cmd_tlv_ssid *)tlv;
ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_SSID);
ssid->header.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len);
memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len);
cmd_size += sizeof(struct mwifiex_ie_types_header) +
bss_cfg->ssid.ssid_len;
tlv += sizeof(struct mwifiex_ie_types_header) +
bss_cfg->ssid.ssid_len;
bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv;
bcast_ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID);
bcast_ssid->header.len =
cpu_to_le16(sizeof(bcast_ssid->bcast_ctl));
bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl;
cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid);
tlv += sizeof(struct host_cmd_tlv_bcast_ssid);
}
if (bss_cfg->rates[0]) {
tlv_rates = (struct host_cmd_tlv_rates *)tlv;
tlv_rates->header.type = cpu_to_le16(TLV_TYPE_UAP_RATES);
for (i = 0; i < MWIFIEX_SUPPORTED_RATES && bss_cfg->rates[i];
i++)
tlv_rates->rates[i] = bss_cfg->rates[i];
tlv_rates->header.len = cpu_to_le16(i);
cmd_size += sizeof(struct host_cmd_tlv_rates) + i;
tlv += sizeof(struct host_cmd_tlv_rates) + i;
}
if (bss_cfg->channel &&
((bss_cfg->band_cfg == BAND_CONFIG_BG &&
bss_cfg->channel <= MAX_CHANNEL_BAND_BG) ||
(bss_cfg->band_cfg == BAND_CONFIG_A &&
bss_cfg->channel <= MAX_CHANNEL_BAND_A))) {
chan_band = (struct host_cmd_tlv_channel_band *)tlv;
chan_band->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST);
chan_band->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) -
sizeof(struct mwifiex_ie_types_header));
chan_band->band_config = bss_cfg->band_cfg;
chan_band->channel = bss_cfg->channel;
cmd_size += sizeof(struct host_cmd_tlv_channel_band);
tlv += sizeof(struct host_cmd_tlv_channel_band);
}
if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD &&
bss_cfg->beacon_period <= MAX_BEACON_PERIOD) {
beacon_period = (struct host_cmd_tlv_beacon_period *)tlv;
beacon_period->header.type =
cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD);
beacon_period->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) -
sizeof(struct mwifiex_ie_types_header));
beacon_period->period = cpu_to_le16(bss_cfg->beacon_period);
cmd_size += sizeof(struct host_cmd_tlv_beacon_period);
tlv += sizeof(struct host_cmd_tlv_beacon_period);
}
if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD &&
bss_cfg->dtim_period <= MAX_DTIM_PERIOD) {
dtim_period = (struct host_cmd_tlv_dtim_period *)tlv;
dtim_period->header.type =
cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD);
dtim_period->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) -
sizeof(struct mwifiex_ie_types_header));
dtim_period->period = bss_cfg->dtim_period;
cmd_size += sizeof(struct host_cmd_tlv_dtim_period);
tlv += sizeof(struct host_cmd_tlv_dtim_period);
}
if (bss_cfg->rts_threshold <= MWIFIEX_RTS_MAX_VALUE) {
rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv;
rts_threshold->header.type =
cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD);
rts_threshold->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) -
sizeof(struct mwifiex_ie_types_header));
rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold);
cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
tlv += sizeof(struct host_cmd_tlv_frag_threshold);
}
if ((bss_cfg->frag_threshold >= MWIFIEX_FRAG_MIN_VALUE) &&
(bss_cfg->frag_threshold <= MWIFIEX_FRAG_MAX_VALUE)) {
frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv;
frag_threshold->header.type =
cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD);
frag_threshold->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) -
sizeof(struct mwifiex_ie_types_header));
frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold);
cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
tlv += sizeof(struct host_cmd_tlv_frag_threshold);
}
if (bss_cfg->retry_limit <= MWIFIEX_RETRY_LIMIT) {
retry_limit = (struct host_cmd_tlv_retry_limit *)tlv;
retry_limit->header.type =
cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT);
retry_limit->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) -
sizeof(struct mwifiex_ie_types_header));
retry_limit->limit = (u8)bss_cfg->retry_limit;
cmd_size += sizeof(struct host_cmd_tlv_retry_limit);
tlv += sizeof(struct host_cmd_tlv_retry_limit);
}
if ((bss_cfg->protocol & PROTOCOL_WPA) ||
(bss_cfg->protocol & PROTOCOL_WPA2) ||
(bss_cfg->protocol & PROTOCOL_EAP))
mwifiex_uap_bss_wpa(&tlv, cmd_buf, &cmd_size);
else
mwifiex_uap_bss_wep(&tlv, cmd_buf, &cmd_size);
if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) ||
(bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) {
auth_type = (struct host_cmd_tlv_auth_type *)tlv;
auth_type->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
auth_type->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) -
sizeof(struct mwifiex_ie_types_header));
auth_type->auth_type = (u8)bss_cfg->auth_mode;
cmd_size += sizeof(struct host_cmd_tlv_auth_type);
tlv += sizeof(struct host_cmd_tlv_auth_type);
}
if (bss_cfg->protocol) {
encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv;
encrypt_protocol->header.type =
cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL);
encrypt_protocol->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol)
- sizeof(struct mwifiex_ie_types_header));
encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol);
cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol);
tlv += sizeof(struct host_cmd_tlv_encrypt_protocol);
}
if (bss_cfg->ht_cap.cap_info) {
htcap = (struct mwifiex_ie_types_htcap *)tlv;
htcap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY);
htcap->header.len =
cpu_to_le16(sizeof(struct ieee80211_ht_cap));
htcap->ht_cap.cap_info = bss_cfg->ht_cap.cap_info;
htcap->ht_cap.ampdu_params_info =
bss_cfg->ht_cap.ampdu_params_info;
memcpy(&htcap->ht_cap.mcs, &bss_cfg->ht_cap.mcs,
sizeof(struct ieee80211_mcs_info));
htcap->ht_cap.extended_ht_cap_info =
bss_cfg->ht_cap.extended_ht_cap_info;
htcap->ht_cap.tx_BF_cap_info = bss_cfg->ht_cap.tx_BF_cap_info;
htcap->ht_cap.antenna_selection_info =
bss_cfg->ht_cap.antenna_selection_info;
cmd_size += sizeof(struct mwifiex_ie_types_htcap);
tlv += sizeof(struct mwifiex_ie_types_htcap);
}
if (bss_cfg->wmm_info.qos_info != 0xFF) {
wmm_cap = (struct mwifiex_ie_types_wmmcap *)tlv;
wmm_cap->header.type = cpu_to_le16(WLAN_EID_VENDOR_SPECIFIC);
wmm_cap->header.len = cpu_to_le16(sizeof(wmm_cap->wmm_info));
memcpy(&wmm_cap->wmm_info, &bss_cfg->wmm_info,
sizeof(wmm_cap->wmm_info));
cmd_size += sizeof(struct mwifiex_ie_types_wmmcap);
tlv += sizeof(struct mwifiex_ie_types_wmmcap);
}
if (bss_cfg->sta_ao_timer) {
ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
ao_timer->header.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER);
ao_timer->header.len = cpu_to_le16(sizeof(*ao_timer) -
sizeof(struct mwifiex_ie_types_header));
ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer);
cmd_size += sizeof(*ao_timer);
tlv += sizeof(*ao_timer);
}
if (bss_cfg->ps_sta_ao_timer) {
ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
ps_ao_timer->header.type =
cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER);
ps_ao_timer->header.len = cpu_to_le16(sizeof(*ps_ao_timer) -
sizeof(struct mwifiex_ie_types_header));
ps_ao_timer->sta_ao_timer =
cpu_to_le32(bss_cfg->ps_sta_ao_timer);
cmd_size += sizeof(*ps_ao_timer);
tlv += sizeof(*ps_ao_timer);
}
*param_size = cmd_size;
return 0;
}
/* This function parses custom IEs from IE list and prepares command buffer */
static int mwifiex_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size)
{
struct mwifiex_ie_list *ap_ie = cmd_buf;
struct mwifiex_ie_types_header *tlv_ie = (void *)tlv;
if (!ap_ie || !ap_ie->len || !ap_ie->ie_list)
return -1;
*ie_size += le16_to_cpu(ap_ie->len) +
sizeof(struct mwifiex_ie_types_header);
tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
tlv_ie->len = ap_ie->len;
tlv += sizeof(struct mwifiex_ie_types_header);
memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len));
return 0;
}
/* Parse AP config structure and prepare TLV based command structure
* to be sent to FW for uAP configuration
*/
static int
mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action,
u32 type, void *cmd_buf)
{
u8 *tlv;
u16 cmd_size, param_size, ie_size;
struct host_cmd_ds_sys_config *sys_cfg;
cmd->command = cpu_to_le16(HostCmd_CMD_UAP_SYS_CONFIG);
cmd_size = (u16)(sizeof(struct host_cmd_ds_sys_config) + S_DS_GEN);
sys_cfg = (struct host_cmd_ds_sys_config *)&cmd->params.uap_sys_config;
sys_cfg->action = cpu_to_le16(cmd_action);
tlv = sys_cfg->tlv;
switch (type) {
case UAP_BSS_PARAMS_I:
param_size = cmd_size;
if (mwifiex_uap_bss_param_prepare(tlv, cmd_buf, &param_size))
return -1;
cmd->size = cpu_to_le16(param_size);
break;
case UAP_CUSTOM_IE_I:
ie_size = cmd_size;
if (mwifiex_uap_custom_ie_prepare(tlv, cmd_buf, &ie_size))
return -1;
cmd->size = cpu_to_le16(ie_size);
break;
default:
return -1;
}
return 0;
}
/* This function prepares AP specific deauth command with mac supplied in
* function parameter.
*/
static int mwifiex_cmd_uap_sta_deauth(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd, u8 *mac)
{
struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth;
cmd->command = cpu_to_le16(HostCmd_CMD_UAP_STA_DEAUTH);
memcpy(sta_deauth->mac, mac, ETH_ALEN);
sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING);
cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) +
S_DS_GEN);
return 0;
}
/* This function prepares the AP specific commands before sending them
* to the firmware.
* This is a generic function which calls specific command preparation
* routines based upon the command number.
*/
int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no,
u16 cmd_action, u32 type,
void *data_buf, void *cmd_buf)
{
struct host_cmd_ds_command *cmd = cmd_buf;
switch (cmd_no) {
case HostCmd_CMD_UAP_SYS_CONFIG:
if (mwifiex_cmd_uap_sys_config(cmd, cmd_action, type, data_buf))
return -1;
break;
case HostCmd_CMD_UAP_BSS_START:
case HostCmd_CMD_UAP_BSS_STOP:
cmd->command = cpu_to_le16(cmd_no);
cmd->size = cpu_to_le16(S_DS_GEN);
break;
case HostCmd_CMD_UAP_STA_DEAUTH:
if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf))
return -1;
break;
default:
dev_err(priv->adapter->dev,
"PREP_CMD: unknown cmd %#x\n", cmd_no);
return -1;
}
return 0;
}

View file

@ -0,0 +1,198 @@
/*
* Marvell Wireless LAN device driver: AP event handling
*
* Copyright (C) 2012-2014, 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 "main.h"
#include "11n.h"
/*
* This function handles AP interface specific events generated by firmware.
*
* Event specific routines are called by this function based
* upon the generated event cause.
*
*
* Events supported for AP -
* - EVENT_UAP_STA_ASSOC
* - EVENT_UAP_STA_DEAUTH
* - EVENT_UAP_BSS_ACTIVE
* - EVENT_UAP_BSS_START
* - EVENT_UAP_BSS_IDLE
* - EVENT_UAP_MIC_COUNTERMEASURES:
*/
int mwifiex_process_uap_event(struct mwifiex_private *priv)
{
struct mwifiex_adapter *adapter = priv->adapter;
int len, i;
u32 eventcause = adapter->event_cause;
struct station_info sinfo;
struct mwifiex_assoc_event *event;
struct mwifiex_sta_node *node;
u8 *deauth_mac;
struct host_cmd_ds_11n_batimeout *ba_timeout;
u16 ctrl;
switch (eventcause) {
case EVENT_UAP_STA_ASSOC:
memset(&sinfo, 0, sizeof(sinfo));
event = (struct mwifiex_assoc_event *)
(adapter->event_body + MWIFIEX_UAP_EVENT_EXTRA_HEADER);
if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) {
len = -1;
if (ieee80211_is_assoc_req(event->frame_control))
len = 0;
else if (ieee80211_is_reassoc_req(event->frame_control))
/* There will be ETH_ALEN bytes of
* current_ap_addr before the re-assoc ies.
*/
len = ETH_ALEN;
if (len != -1) {
sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
sinfo.assoc_req_ies = &event->data[len];
len = (u8 *)sinfo.assoc_req_ies -
(u8 *)&event->frame_control;
sinfo.assoc_req_ies_len =
le16_to_cpu(event->len) - (u16)len;
}
}
cfg80211_new_sta(priv->netdev, event->sta_addr, &sinfo,
GFP_KERNEL);
node = mwifiex_add_sta_entry(priv, event->sta_addr);
if (!node) {
dev_warn(adapter->dev,
"could not create station entry!\n");
return -1;
}
if (!priv->ap_11n_enabled)
break;
mwifiex_set_sta_ht_cap(priv, sinfo.assoc_req_ies,
sinfo.assoc_req_ies_len, node);
for (i = 0; i < MAX_NUM_TID; i++) {
if (node->is_11n_enabled)
node->ampdu_sta[i] =
priv->aggr_prio_tbl[i].ampdu_user;
else
node->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED;
}
memset(node->rx_seq, 0xff, sizeof(node->rx_seq));
break;
case EVENT_UAP_STA_DEAUTH:
deauth_mac = adapter->event_body +
MWIFIEX_UAP_EVENT_EXTRA_HEADER;
cfg80211_del_sta(priv->netdev, deauth_mac, GFP_KERNEL);
if (priv->ap_11n_enabled) {
mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac);
mwifiex_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac);
}
mwifiex_del_sta_entry(priv, deauth_mac);
break;
case EVENT_UAP_BSS_IDLE:
priv->media_connected = false;
if (netif_carrier_ok(priv->netdev))
netif_carrier_off(priv->netdev);
mwifiex_stop_net_dev_queue(priv->netdev, adapter);
mwifiex_clean_txrx(priv);
mwifiex_del_all_sta_list(priv);
break;
case EVENT_UAP_BSS_ACTIVE:
priv->media_connected = true;
if (!netif_carrier_ok(priv->netdev))
netif_carrier_on(priv->netdev);
mwifiex_wake_up_net_dev_queue(priv->netdev, adapter);
break;
case EVENT_UAP_BSS_START:
dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause);
memcpy(priv->netdev->dev_addr, adapter->event_body + 2,
ETH_ALEN);
break;
case EVENT_UAP_MIC_COUNTERMEASURES:
/* For future development */
dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause);
break;
case EVENT_AMSDU_AGGR_CTRL:
ctrl = le16_to_cpu(*(__le16 *)adapter->event_body);
dev_dbg(adapter->dev, "event: AMSDU_AGGR_CTRL %d\n", ctrl);
if (priv->media_connected) {
adapter->tx_buf_size =
min_t(u16, adapter->curr_tx_buf_size, ctrl);
dev_dbg(adapter->dev, "event: tx_buf_size %d\n",
adapter->tx_buf_size);
}
break;
case EVENT_ADDBA:
dev_dbg(adapter->dev, "event: ADDBA Request\n");
if (priv->media_connected)
mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP,
HostCmd_ACT_GEN_SET, 0,
adapter->event_body, false);
break;
case EVENT_DELBA:
dev_dbg(adapter->dev, "event: DELBA Request\n");
if (priv->media_connected)
mwifiex_11n_delete_ba_stream(priv, adapter->event_body);
break;
case EVENT_BA_STREAM_TIEMOUT:
dev_dbg(adapter->dev, "event: BA Stream timeout\n");
if (priv->media_connected) {
ba_timeout = (void *)adapter->event_body;
mwifiex_11n_ba_stream_timeout(priv, ba_timeout);
}
break;
case EVENT_EXT_SCAN_REPORT:
dev_dbg(adapter->dev, "event: EXT_SCAN Report\n");
if (adapter->ext_scan)
return mwifiex_handle_event_ext_scan_report(priv,
adapter->event_skb->data);
break;
default:
dev_dbg(adapter->dev, "event: unknown event id: %#x\n",
eventcause);
break;
}
return 0;
}
/* This function deletes station entry from associated station list.
* Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream
* tables created for this station are deleted.
*/
void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
struct mwifiex_sta_node *node)
{
if (priv->ap_11n_enabled && node->is_11n_enabled) {
mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr);
mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr);
}
mwifiex_del_sta_entry(priv, node->mac_addr);
return;
}

View file

@ -0,0 +1,402 @@
/*
* Marvell Wireless LAN device driver: AP TX and RX data handling
*
* Copyright (C) 2012-2014, 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 "main.h"
#include "wmm.h"
#include "11n_aggr.h"
#include "11n_rxreorder.h"
/* This function checks if particular RA list has packets more than low bridge
* packet threshold and then deletes packet from this RA list.
* Function deletes packets from such RA list and returns true. If no such list
* is found, false is returned.
*/
static bool
mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv,
struct list_head *ra_list_head)
{
struct mwifiex_ra_list_tbl *ra_list;
struct sk_buff *skb, *tmp;
bool pkt_deleted = false;
struct mwifiex_txinfo *tx_info;
struct mwifiex_adapter *adapter = priv->adapter;
list_for_each_entry(ra_list, ra_list_head, list) {
if (skb_queue_empty(&ra_list->skb_head))
continue;
skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) {
tx_info = MWIFIEX_SKB_TXCB(skb);
if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) {
__skb_unlink(skb, &ra_list->skb_head);
mwifiex_write_data_complete(adapter, skb, 0,
-1);
atomic_dec(&priv->wmm.tx_pkts_queued);
pkt_deleted = true;
}
if ((atomic_read(&adapter->pending_bridged_pkts) <=
MWIFIEX_BRIDGED_PKTS_THR_LOW))
break;
}
}
return pkt_deleted;
}
/* This function deletes packets from particular RA List. RA list index
* from which packets are deleted is preserved so that packets from next RA
* list are deleted upon subsequent call thus maintaining fairness.
*/
static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv)
{
unsigned long flags;
struct list_head *ra_list;
int i;
spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) {
if (priv->del_list_idx == MAX_NUM_TID)
priv->del_list_idx = 0;
ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list;
if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list)) {
priv->del_list_idx++;
break;
}
}
spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
}
static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
struct sk_buff *skb)
{
struct mwifiex_adapter *adapter = priv->adapter;
struct uap_rxpd *uap_rx_pd;
struct rx_packet_hdr *rx_pkt_hdr;
struct sk_buff *new_skb;
struct mwifiex_txinfo *tx_info;
int hdr_chop;
struct ethhdr *p_ethhdr;
uap_rx_pd = (struct uap_rxpd *)(skb->data);
rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
if ((atomic_read(&adapter->pending_bridged_pkts) >=
MWIFIEX_BRIDGED_PKTS_THR_HIGH)) {
dev_err(priv->adapter->dev,
"Tx: Bridge packet limit reached. Drop packet!\n");
kfree_skb(skb);
mwifiex_uap_cleanup_tx_queues(priv);
return;
}
if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header,
sizeof(bridge_tunnel_header))) ||
(!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header,
sizeof(rfc1042_header)) &&
ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP &&
ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) {
/* Replace the 803 header and rfc1042 header (llc/snap) with
* an Ethernet II header, keep the src/dst and snap_type
* (ethertype).
*
* The firmware only passes up SNAP frames converting all RX
* data from 802.11 to 802.2/LLC/SNAP frames.
*
* To create the Ethernet II, just move the src, dst address
* right before the snap_type.
*/
p_ethhdr = (struct ethhdr *)
((u8 *)(&rx_pkt_hdr->eth803_hdr)
+ sizeof(rx_pkt_hdr->eth803_hdr)
+ sizeof(rx_pkt_hdr->rfc1042_hdr)
- sizeof(rx_pkt_hdr->eth803_hdr.h_dest)
- sizeof(rx_pkt_hdr->eth803_hdr.h_source)
- sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type));
memcpy(p_ethhdr->h_source, rx_pkt_hdr->eth803_hdr.h_source,
sizeof(p_ethhdr->h_source));
memcpy(p_ethhdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest,
sizeof(p_ethhdr->h_dest));
/* Chop off the rxpd + the excess memory from
* 802.2/llc/snap header that was removed.
*/
hdr_chop = (u8 *)p_ethhdr - (u8 *)uap_rx_pd;
} else {
/* Chop off the rxpd */
hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd;
}
/* Chop off the leading header bytes so that it points
* to the start of either the reconstructed EthII frame
* or the 802.2/llc/snap frame.
*/
skb_pull(skb, hdr_chop);
if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) {
dev_dbg(priv->adapter->dev,
"data: Tx: insufficient skb headroom %d\n",
skb_headroom(skb));
/* Insufficient skb headroom - allocate a new skb */
new_skb =
skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN);
if (unlikely(!new_skb)) {
dev_err(priv->adapter->dev,
"Tx: cannot allocate new_skb\n");
kfree_skb(skb);
priv->stats.tx_dropped++;
return;
}
kfree_skb(skb);
skb = new_skb;
dev_dbg(priv->adapter->dev, "info: new skb headroom %d\n",
skb_headroom(skb));
}
tx_info = MWIFIEX_SKB_TXCB(skb);
memset(tx_info, 0, sizeof(*tx_info));
tx_info->bss_num = priv->bss_num;
tx_info->bss_type = priv->bss_type;
tx_info->flags |= MWIFIEX_BUF_FLAG_BRIDGED_PKT;
if (is_unicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest)) {
/* Update bridge packet statistics as the
* packet is not going to kernel/upper layer.
*/
priv->stats.rx_bytes += skb->len;
priv->stats.rx_packets++;
/* Sending bridge packet to TX queue, so save the packet
* length in TXCB to update statistics in TX complete.
*/
tx_info->pkt_len = skb->len;
}
__net_timestamp(skb);
mwifiex_wmm_add_buf_txqueue(priv, skb);
atomic_inc(&adapter->tx_pending);
atomic_inc(&adapter->pending_bridged_pkts);
return;
}
/*
* This function contains logic for AP packet forwarding.
*
* If a packet is multicast/broadcast, it is sent to kernel/upper layer
* as well as queued back to AP TX queue so that it can be sent to other
* associated stations.
* If a packet is unicast and RA is present in associated station list,
* it is again requeued into AP TX queue.
* If a packet is unicast and RA is not in associated station list,
* packet is forwarded to kernel to handle routing logic.
*/
int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv,
struct sk_buff *skb)
{
struct mwifiex_adapter *adapter = priv->adapter;
struct uap_rxpd *uap_rx_pd;
struct rx_packet_hdr *rx_pkt_hdr;
u8 ra[ETH_ALEN];
struct sk_buff *skb_uap;
uap_rx_pd = (struct uap_rxpd *)(skb->data);
rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
/* don't do packet forwarding in disconnected state */
if (!priv->media_connected) {
dev_err(adapter->dev, "drop packet in disconnected state.\n");
dev_kfree_skb_any(skb);
return 0;
}
memcpy(ra, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN);
if (is_multicast_ether_addr(ra)) {
skb_uap = skb_copy(skb, GFP_ATOMIC);
mwifiex_uap_queue_bridged_pkt(priv, skb_uap);
} else {
if (mwifiex_get_sta_entry(priv, ra)) {
/* Requeue Intra-BSS packet */
mwifiex_uap_queue_bridged_pkt(priv, skb);
return 0;
}
}
/* Forward unicat/Inter-BSS packets to kernel. */
return mwifiex_process_rx_packet(priv, skb);
}
/*
* This function processes the packet received on AP interface.
*
* The function looks into the RxPD and performs sanity tests on the
* received buffer to ensure its a valid packet before processing it
* further. If the packet is determined to be aggregated, it is
* de-aggregated accordingly. Then skb is passed to AP packet forwarding logic.
*
* The completion callback is called after processing is complete.
*/
int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv,
struct sk_buff *skb)
{
struct mwifiex_adapter *adapter = priv->adapter;
int ret;
struct uap_rxpd *uap_rx_pd;
struct rx_packet_hdr *rx_pkt_hdr;
u16 rx_pkt_type;
u8 ta[ETH_ALEN], pkt_type;
struct mwifiex_sta_node *node;
uap_rx_pd = (struct uap_rxpd *)(skb->data);
rx_pkt_type = le16_to_cpu(uap_rx_pd->rx_pkt_type);
rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
if ((le16_to_cpu(uap_rx_pd->rx_pkt_offset) +
le16_to_cpu(uap_rx_pd->rx_pkt_length)) > (u16) skb->len) {
dev_err(adapter->dev,
"wrong rx packet: len=%d, offset=%d, length=%d\n",
skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset),
le16_to_cpu(uap_rx_pd->rx_pkt_length));
priv->stats.rx_dropped++;
dev_kfree_skb_any(skb);
return 0;
}
if (rx_pkt_type == PKT_TYPE_MGMT) {
ret = mwifiex_process_mgmt_packet(priv, skb);
if (ret)
dev_err(adapter->dev, "Rx of mgmt packet failed");
dev_kfree_skb_any(skb);
return ret;
}
memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN);
if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) {
node = mwifiex_get_sta_entry(priv, ta);
if (node)
node->rx_seq[uap_rx_pd->priority] =
le16_to_cpu(uap_rx_pd->seq_num);
}
if (!priv->ap_11n_enabled ||
(!mwifiex_11n_get_rx_reorder_tbl(priv, uap_rx_pd->priority, ta) &&
(le16_to_cpu(uap_rx_pd->rx_pkt_type) != PKT_TYPE_AMSDU))) {
ret = mwifiex_handle_uap_rx_forward(priv, skb);
return ret;
}
/* Reorder and send to kernel */
pkt_type = (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type);
ret = mwifiex_11n_rx_reorder_pkt(priv, le16_to_cpu(uap_rx_pd->seq_num),
uap_rx_pd->priority, ta, pkt_type,
skb);
if (ret || (rx_pkt_type == PKT_TYPE_BAR))
dev_kfree_skb_any(skb);
if (ret)
priv->stats.rx_dropped++;
return ret;
}
/*
* This function fills the TxPD for AP tx packets.
*
* The Tx buffer received by this function should already have the
* header space allocated for TxPD.
*
* This function inserts the TxPD in between interface header and actual
* data and adjusts the buffer pointers accordingly.
*
* The following TxPD fields are set by this function, as required -
* - BSS number
* - Tx packet length and offset
* - Priority
* - Packet delay
* - Priority specific Tx control
* - Flags
*/
void *mwifiex_process_uap_txpd(struct mwifiex_private *priv,
struct sk_buff *skb)
{
struct mwifiex_adapter *adapter = priv->adapter;
struct uap_txpd *txpd;
struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb);
int pad, len;
u16 pkt_type;
if (!skb->len) {
dev_err(adapter->dev, "Tx: bad packet length: %d\n", skb->len);
tx_info->status_code = -1;
return skb->data;
}
pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0;
/* If skb->data is not aligned, add padding */
pad = (4 - (((void *)skb->data - NULL) & 0x3)) % 4;
len = sizeof(*txpd) + pad;
BUG_ON(skb_headroom(skb) < len + INTF_HEADER_LEN);
skb_push(skb, len);
txpd = (struct uap_txpd *)skb->data;
memset(txpd, 0, sizeof(*txpd));
txpd->bss_num = priv->bss_num;
txpd->bss_type = priv->bss_type;
txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - len));
txpd->priority = (u8)skb->priority;
txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb);
if (txpd->priority < ARRAY_SIZE(priv->wmm.user_pri_pkt_tx_ctrl))
/*
* Set the priority specific tx_control field, setting of 0 will
* cause the default value to be used later in this function.
*/
txpd->tx_control =
cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]);
/* Offset of actual data */
if (pkt_type == PKT_TYPE_MGMT) {
/* Set the packet type and add header for management frame */
txpd->tx_pkt_type = cpu_to_le16(pkt_type);
len += MWIFIEX_MGMT_FRAME_HEADER_SIZE;
}
txpd->tx_pkt_offset = cpu_to_le16(len);
/* make space for INTF_HEADER_LEN */
skb_push(skb, INTF_HEADER_LEN);
if (!txpd->tx_control)
/* TxCtrl set by user or default */
txpd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl);
return skb->data;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,103 @@
/*
* This file contains definitions for mwifiex USB interface driver.
*
* Copyright (C) 2012-2014, 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_USB_H
#define _MWIFIEX_USB_H
#include <linux/usb.h>
#define USB8XXX_VID 0x1286
#define USB8797_PID_1 0x2043
#define USB8797_PID_2 0x2044
#define USB8897_PID_1 0x2045
#define USB8897_PID_2 0x2046
#define USB8XXX_FW_DNLD 1
#define USB8XXX_FW_READY 2
#define USB8XXX_FW_MAX_RETRY 3
#define MWIFIEX_TX_DATA_URB 6
#define MWIFIEX_RX_DATA_URB 6
#define MWIFIEX_USB_TIMEOUT 100
#define USB8797_DEFAULT_FW_NAME "mrvl/usb8797_uapsta.bin"
#define USB8897_DEFAULT_FW_NAME "mrvl/usb8897_uapsta.bin"
#define FW_DNLD_TX_BUF_SIZE 620
#define FW_DNLD_RX_BUF_SIZE 2048
#define FW_HAS_LAST_BLOCK 0x00000004
#define FW_DATA_XMIT_SIZE \
(sizeof(struct fw_header) + dlen + sizeof(u32))
struct urb_context {
struct mwifiex_adapter *adapter;
struct sk_buff *skb;
struct urb *urb;
u8 ep;
};
struct usb_card_rec {
struct mwifiex_adapter *adapter;
struct usb_device *udev;
struct usb_interface *intf;
u8 rx_cmd_ep;
struct urb_context rx_cmd;
atomic_t rx_cmd_urb_pending;
struct urb_context rx_data_list[MWIFIEX_RX_DATA_URB];
u8 usb_boot_state;
u8 rx_data_ep;
atomic_t rx_data_urb_pending;
u8 tx_data_ep;
u8 tx_cmd_ep;
atomic_t tx_data_urb_pending;
atomic_t tx_cmd_urb_pending;
int bulk_out_maxpktsize;
struct urb_context tx_cmd;
int tx_data_ix;
struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB];
};
struct fw_header {
__le32 dnld_cmd;
__le32 base_addr;
__le32 data_len;
__le32 crc;
};
struct fw_sync_header {
__le32 cmd;
__le32 seq_num;
};
struct fw_data {
struct fw_header fw_hdr;
__le32 seq_num;
u8 data[1];
};
/* This function is called after the card has woken up. */
static inline int
mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
{
return 0;
}
#endif /*_MWIFIEX_USB_H */

View file

@ -0,0 +1,368 @@
/*
* Marvell Wireless LAN device driver: utility functions
*
* Copyright (C) 2011-2014, 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;
}
/*
* This function sends init/shutdown command
* to firmware.
*/
int mwifiex_init_shutdown_fw(struct mwifiex_private *priv,
u32 func_init_shutdown)
{
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(priv->adapter->dev, "unsupported parameter\n");
return -1;
}
return mwifiex_send_cmd(priv, cmd, HostCmd_ACT_GEN_SET, 0, NULL, true);
}
EXPORT_SYMBOL_GPL(mwifiex_init_shutdown_fw);
/*
* 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->curr_tx_buf_size = (u32) adapter->curr_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->is_cmd_timedout = adapter->is_cmd_timedout;
info->num_cmd_host_to_card_failure
= adapter->dbg.num_cmd_host_to_card_failure;
info->num_cmd_sleep_cfm_host_to_card_failure
= adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure;
info->num_tx_host_to_card_failure
= adapter->dbg.num_tx_host_to_card_failure;
info->num_event_deauth = adapter->dbg.num_event_deauth;
info->num_event_disassoc = adapter->dbg.num_event_disassoc;
info->num_event_link_lost = adapter->dbg.num_event_link_lost;
info->num_cmd_deauth = adapter->dbg.num_cmd_deauth;
info->num_cmd_assoc_success =
adapter->dbg.num_cmd_assoc_success;
info->num_cmd_assoc_failure =
adapter->dbg.num_cmd_assoc_failure;
info->num_tx_timeout = adapter->dbg.num_tx_timeout;
info->timeout_cmd_id = adapter->dbg.timeout_cmd_id;
info->timeout_cmd_act = adapter->dbg.timeout_cmd_act;
memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id,
sizeof(adapter->dbg.last_cmd_id));
memcpy(info->last_cmd_act, adapter->dbg.last_cmd_act,
sizeof(adapter->dbg.last_cmd_act));
info->last_cmd_index = adapter->dbg.last_cmd_index;
memcpy(info->last_cmd_resp_id, adapter->dbg.last_cmd_resp_id,
sizeof(adapter->dbg.last_cmd_resp_id));
info->last_cmd_resp_index = adapter->dbg.last_cmd_resp_index;
memcpy(info->last_event, adapter->dbg.last_event,
sizeof(adapter->dbg.last_event));
info->last_event_index = adapter->dbg.last_event_index;
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 management packet and send it
* to the kernel.
*/
int
mwifiex_process_mgmt_packet(struct mwifiex_private *priv,
struct sk_buff *skb)
{
struct rxpd *rx_pd;
u16 pkt_len;
if (!skb)
return -1;
rx_pd = (struct rxpd *)skb->data;
skb_pull(skb, le16_to_cpu(rx_pd->rx_pkt_offset));
skb_pull(skb, sizeof(pkt_len));
pkt_len = le16_to_cpu(rx_pd->rx_pkt_length);
/* Remove address4 */
memmove(skb->data + sizeof(struct ieee80211_hdr_3addr),
skb->data + sizeof(struct ieee80211_hdr),
pkt_len - sizeof(struct ieee80211_hdr));
pkt_len -= ETH_ALEN + sizeof(pkt_len);
rx_pd->rx_pkt_length = cpu_to_le16(pkt_len);
cfg80211_rx_mgmt(priv->wdev, priv->roc_cfg.chan.center_freq,
CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len,
0);
return 0;
}
/*
* This function processes the received packet before sending it to the
* kernel.
*
* It extracts the SKB from the received buffer and sends it to kernel.
* In case the received buffer does not contain the data in SKB format,
* the function creates a blank SKB, fills it with the data from the
* received buffer and then sends this new SKB to the kernel.
*/
int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb)
{
if (!skb)
return -1;
priv->stats.rx_bytes += skb->len;
priv->stats.rx_packets++;
skb->dev = priv->netdev;
skb->protocol = eth_type_trans(skb, priv->netdev);
skb->ip_summed = CHECKSUM_NONE;
/* This is required only in case of 11n and USB/PCIE as we alloc
* a buffer of 4K only if its 11N (to be able to receive 4K
* AMSDU packets). In case of SD we allocate buffers based
* on the size of packet and hence this is not needed.
*
* Modifying the truesize here as our allocation for each
* skb is 4K but we only receive 2K packets and this cause
* the kernel to start dropping packets in case where
* application has allocated buffer based on 2K size i.e.
* if there a 64K packet received (in IP fragments and
* application allocates 64K to receive this packet but
* this packet would almost double up because we allocate
* each 1.5K fragment in 4K and pass it up. As soon as the
* 64K limit hits kernel will start to drop rest of the
* fragments. Currently we fail the Filesndl-ht.scr script
* for UDP, hence this fix
*/
if ((priv->adapter->iface_type == MWIFIEX_USB ||
priv->adapter->iface_type == MWIFIEX_PCIE) &&
(skb->truesize > MWIFIEX_RX_DATA_BUF_SIZE))
skb->truesize += (skb->len - MWIFIEX_RX_DATA_BUF_SIZE);
if (in_interrupt())
netif_rx(skb);
else
netif_rx_ni(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_complete_cmd(struct mwifiex_adapter *adapter,
struct cmd_ctrl_node *cmd_node)
{
dev_dbg(adapter->dev, "cmd completed: status=%d\n",
adapter->cmd_wait_q.status);
*(cmd_node->condition) = true;
if (adapter->cmd_wait_q.status == -ETIMEDOUT)
dev_err(adapter->dev, "cmd timeout\n");
else
wake_up_interruptible(&adapter->cmd_wait_q.wait);
return 0;
}
/* This function will return the pointer to station entry in station list
* table which matches specified mac address.
* This function should be called after acquiring RA list spinlock.
* NULL is returned if station entry is not found in associated STA list.
*/
struct mwifiex_sta_node *
mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac)
{
struct mwifiex_sta_node *node;
if (!mac)
return NULL;
list_for_each_entry(node, &priv->sta_list, list) {
if (!memcmp(node->mac_addr, mac, ETH_ALEN))
return node;
}
return NULL;
}
/* This function will add a sta_node entry to associated station list
* table with the given mac address.
* If entry exist already, existing entry is returned.
* If received mac address is NULL, NULL is returned.
*/
struct mwifiex_sta_node *
mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac)
{
struct mwifiex_sta_node *node;
unsigned long flags;
if (!mac)
return NULL;
spin_lock_irqsave(&priv->sta_list_spinlock, flags);
node = mwifiex_get_sta_entry(priv, mac);
if (node)
goto done;
node = kzalloc(sizeof(*node), GFP_ATOMIC);
if (!node)
goto done;
memcpy(node->mac_addr, mac, ETH_ALEN);
list_add_tail(&node->list, &priv->sta_list);
done:
spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
return node;
}
/* This function will search for HT IE in association request IEs
* and set station HT parameters accordingly.
*/
void
mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies,
int ies_len, struct mwifiex_sta_node *node)
{
const struct ieee80211_ht_cap *ht_cap;
if (!ies)
return;
ht_cap = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len);
if (ht_cap) {
node->is_11n_enabled = 1;
node->max_amsdu = le16_to_cpu(ht_cap->cap_info) &
IEEE80211_HT_CAP_MAX_AMSDU ?
MWIFIEX_TX_DATA_BUF_SIZE_8K :
MWIFIEX_TX_DATA_BUF_SIZE_4K;
} else {
node->is_11n_enabled = 0;
}
return;
}
/* This function will delete a station entry from station list */
void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac)
{
struct mwifiex_sta_node *node;
unsigned long flags;
spin_lock_irqsave(&priv->sta_list_spinlock, flags);
node = mwifiex_get_sta_entry(priv, mac);
if (node) {
list_del(&node->list);
kfree(node);
}
spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
return;
}
/* This function will delete all stations from associated station list. */
void mwifiex_del_all_sta_list(struct mwifiex_private *priv)
{
struct mwifiex_sta_node *node, *tmp;
unsigned long flags;
spin_lock_irqsave(&priv->sta_list_spinlock, flags);
list_for_each_entry_safe(node, tmp, &priv->sta_list, list) {
list_del(&node->list);
kfree(node);
}
INIT_LIST_HEAD(&priv->sta_list);
spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
return;
}

View file

@ -0,0 +1,76 @@
/*
* Marvell Wireless LAN device driver: utility functions
*
* Copyright (C) 2011-2014, 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_
struct mwifiex_dma_mapping {
dma_addr_t addr;
size_t len;
};
struct mwifiex_cb {
struct mwifiex_dma_mapping dma_mapping;
union {
struct mwifiex_rxinfo rx_info;
struct mwifiex_txinfo tx_info;
};
};
static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb)
{
struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb;
BUILD_BUG_ON(sizeof(struct mwifiex_cb) > sizeof(skb->cb));
return &cb->rx_info;
}
static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb)
{
struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb;
return &cb->tx_info;
}
static inline void mwifiex_store_mapping(struct sk_buff *skb,
struct mwifiex_dma_mapping *mapping)
{
struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb;
memcpy(&cb->dma_mapping, mapping, sizeof(*mapping));
}
static inline void mwifiex_get_mapping(struct sk_buff *skb,
struct mwifiex_dma_mapping *mapping)
{
struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb;
memcpy(mapping, &cb->dma_mapping, sizeof(*mapping));
}
static inline dma_addr_t MWIFIEX_SKB_DMA_ADDR(struct sk_buff *skb)
{
struct mwifiex_dma_mapping mapping;
mwifiex_get_mapping(skb, &mapping);
return mapping.addr;
}
#endif /* !_MWIFIEX_UTIL_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,130 @@
/*
* Marvell Wireless LAN device driver: WMM
*
* Copyright (C) 2011-2014, 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)),
};
static const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
/*
* This table inverses the tos_to_tid operation to get a priority
* which is in sequential order, and can be compared.
* Use this to compare the priority of two different TIDs.
*/
static const u8 tos_to_tid_inv[] = {
0x02, /* from tos_to_tid[2] = 0 */
0x00, /* from tos_to_tid[0] = 1 */
0x01, /* from tos_to_tid[1] = 2 */
0x03,
0x04,
0x05,
0x06,
0x07};
/*
* This function retrieves the TID of the given RA list.
*/
static inline int
mwifiex_get_tid(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 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 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_private *priv,
struct sk_buff *skb);
void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra);
void mwifiex_rotate_priolists(struct mwifiex_private *priv,
struct mwifiex_ra_list_tbl *ra, int tid);
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);
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);
int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
const struct host_cmd_ds_command *resp);
struct mwifiex_ra_list_tbl *
mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid,
const u8 *ra_addr);
u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid);
#endif /* !_MWIFIEX_WMM_H_ */