mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
382
drivers/net/wireless/mwifiex/11ac.c
Normal file
382
drivers/net/wireless/mwifiex/11ac.c
Normal 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;
|
||||
}
|
45
drivers/net/wireless/mwifiex/11ac.h
Normal file
45
drivers/net/wireless/mwifiex/11ac.h
Normal 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_ */
|
101
drivers/net/wireless/mwifiex/11h.c
Normal file
101
drivers/net/wireless/mwifiex/11h.c
Normal 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;
|
||||
}
|
||||
}
|
794
drivers/net/wireless/mwifiex/11n.c
Normal file
794
drivers/net/wireless/mwifiex/11n.c
Normal 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;
|
||||
}
|
211
drivers/net/wireless/mwifiex/11n.h
Normal file
211
drivers/net/wireless/mwifiex/11n.h
Normal 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_ */
|
310
drivers/net/wireless/mwifiex/11n_aggr.c
Normal file
310
drivers/net/wireless/mwifiex/11n_aggr.c
Normal 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;
|
||||
}
|
33
drivers/net/wireless/mwifiex/11n_aggr.h
Normal file
33
drivers/net/wireless/mwifiex/11n_aggr.h
Normal 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_ */
|
817
drivers/net/wireless/mwifiex/11n_rxreorder.c
Normal file
817
drivers/net/wireless/mwifiex/11n_rxreorder.c
Normal 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;
|
||||
}
|
85
drivers/net/wireless/mwifiex/11n_rxreorder.h
Normal file
85
drivers/net/wireless/mwifiex/11n_rxreorder.h
Normal 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_ */
|
42
drivers/net/wireless/mwifiex/Kconfig
Normal file
42
drivers/net/wireless/mwifiex/Kconfig
Normal 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.
|
55
drivers/net/wireless/mwifiex/Makefile
Normal file
55
drivers/net/wireless/mwifiex/Makefile
Normal 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
|
240
drivers/net/wireless/mwifiex/README
Normal file
240
drivers/net/wireless/mwifiex/README
Normal 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
|
||||
|
||||
===============================================================================
|
2962
drivers/net/wireless/mwifiex/cfg80211.c
Normal file
2962
drivers/net/wireless/mwifiex/cfg80211.c
Normal file
File diff suppressed because it is too large
Load diff
29
drivers/net/wireless/mwifiex/cfg80211.h
Normal file
29
drivers/net/wireless/mwifiex/cfg80211.h
Normal 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
|
511
drivers/net/wireless/mwifiex/cfp.c
Normal file
511
drivers/net/wireless/mwifiex/cfp.c
Normal 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;
|
||||
}
|
1620
drivers/net/wireless/mwifiex/cmdevt.c
Normal file
1620
drivers/net/wireless/mwifiex/cmdevt.c
Normal file
File diff suppressed because it is too large
Load diff
875
drivers/net/wireless/mwifiex/debugfs.c
Normal file
875
drivers/net/wireless/mwifiex/debugfs.c
Normal 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", ®_type, ®_offset, ®_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, ®_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);
|
||||
}
|
188
drivers/net/wireless/mwifiex/decl.h
Normal file
188
drivers/net/wireless/mwifiex/decl.h
Normal 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_ */
|
153
drivers/net/wireless/mwifiex/ethtool.c
Normal file
153
drivers/net/wireless/mwifiex/ethtool.c
Normal 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,
|
||||
};
|
1887
drivers/net/wireless/mwifiex/fw.h
Normal file
1887
drivers/net/wireless/mwifiex/fw.h
Normal file
File diff suppressed because it is too large
Load diff
446
drivers/net/wireless/mwifiex/ie.c
Normal file
446
drivers/net/wireless/mwifiex/ie.c
Normal 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;
|
||||
}
|
749
drivers/net/wireless/mwifiex/init.c
Normal file
749
drivers/net/wireless/mwifiex/init.c
Normal 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;
|
||||
}
|
454
drivers/net/wireless/mwifiex/ioctl.h
Normal file
454
drivers/net/wireless/mwifiex/ioctl.h
Normal 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_ */
|
1473
drivers/net/wireless/mwifiex/join.c
Normal file
1473
drivers/net/wireless/mwifiex/join.c
Normal file
File diff suppressed because it is too large
Load diff
1050
drivers/net/wireless/mwifiex/main.c
Normal file
1050
drivers/net/wireless/mwifiex/main.c
Normal file
File diff suppressed because it is too large
Load diff
1311
drivers/net/wireless/mwifiex/main.h
Normal file
1311
drivers/net/wireless/mwifiex/main.h
Normal file
File diff suppressed because it is too large
Load diff
2674
drivers/net/wireless/mwifiex/pcie.c
Normal file
2674
drivers/net/wireless/mwifiex/pcie.c
Normal file
File diff suppressed because it is too large
Load diff
336
drivers/net/wireless/mwifiex/pcie.h
Normal file
336
drivers/net/wireless/mwifiex/pcie.h
Normal 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 */
|
2379
drivers/net/wireless/mwifiex/scan.c
Normal file
2379
drivers/net/wireless/mwifiex/scan.c
Normal file
File diff suppressed because it is too large
Load diff
2240
drivers/net/wireless/mwifiex/sdio.c
Normal file
2240
drivers/net/wireless/mwifiex/sdio.c
Normal file
File diff suppressed because it is too large
Load diff
537
drivers/net/wireless/mwifiex/sdio.h
Normal file
537
drivers/net/wireless/mwifiex/sdio.h
Normal 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 */
|
2070
drivers/net/wireless/mwifiex/sta_cmd.c
Normal file
2070
drivers/net/wireless/mwifiex/sta_cmd.c
Normal file
File diff suppressed because it is too large
Load diff
1127
drivers/net/wireless/mwifiex/sta_cmdresp.c
Normal file
1127
drivers/net/wireless/mwifiex/sta_cmdresp.c
Normal file
File diff suppressed because it is too large
Load diff
513
drivers/net/wireless/mwifiex/sta_event.c
Normal file
513
drivers/net/wireless/mwifiex/sta_event.c
Normal 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;
|
||||
}
|
1426
drivers/net/wireless/mwifiex/sta_ioctl.c
Normal file
1426
drivers/net/wireless/mwifiex/sta_ioctl.c
Normal file
File diff suppressed because it is too large
Load diff
254
drivers/net/wireless/mwifiex/sta_rx.c
Normal file
254
drivers/net/wireless/mwifiex/sta_rx.c
Normal 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;
|
||||
}
|
225
drivers/net/wireless/mwifiex/sta_tx.c
Normal file
225
drivers/net/wireless/mwifiex/sta_tx.c
Normal 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;
|
||||
}
|
1099
drivers/net/wireless/mwifiex/tdls.c
Normal file
1099
drivers/net/wireless/mwifiex/tdls.c
Normal file
File diff suppressed because it is too large
Load diff
209
drivers/net/wireless/mwifiex/txrx.c
Normal file
209
drivers/net/wireless/mwifiex/txrx.c
Normal 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);
|
||||
|
771
drivers/net/wireless/mwifiex/uap_cmd.c
Normal file
771
drivers/net/wireless/mwifiex/uap_cmd.c
Normal 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, ¶m_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;
|
||||
}
|
198
drivers/net/wireless/mwifiex/uap_event.c
Normal file
198
drivers/net/wireless/mwifiex/uap_event.c
Normal 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;
|
||||
}
|
402
drivers/net/wireless/mwifiex/uap_txrx.c
Normal file
402
drivers/net/wireless/mwifiex/uap_txrx.c
Normal 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;
|
||||
}
|
1052
drivers/net/wireless/mwifiex/usb.c
Normal file
1052
drivers/net/wireless/mwifiex/usb.c
Normal file
File diff suppressed because it is too large
Load diff
103
drivers/net/wireless/mwifiex/usb.h
Normal file
103
drivers/net/wireless/mwifiex/usb.h
Normal 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 */
|
368
drivers/net/wireless/mwifiex/util.c
Normal file
368
drivers/net/wireless/mwifiex/util.c
Normal 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;
|
||||
}
|
76
drivers/net/wireless/mwifiex/util.h
Normal file
76
drivers/net/wireless/mwifiex/util.h
Normal 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_ */
|
1300
drivers/net/wireless/mwifiex/wmm.c
Normal file
1300
drivers/net/wireless/mwifiex/wmm.c
Normal file
File diff suppressed because it is too large
Load diff
130
drivers/net/wireless/mwifiex/wmm.h
Normal file
130
drivers/net/wireless/mwifiex/wmm.h
Normal 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_ */
|
Loading…
Add table
Add a link
Reference in a new issue