summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/mwifiex
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/mwifiex')
-rw-r--r--drivers/net/wireless/mwifiex/11ac.c41
-rw-r--r--drivers/net/wireless/mwifiex/11ac.h17
-rw-r--r--drivers/net/wireless/mwifiex/11n.c22
-rw-r--r--drivers/net/wireless/mwifiex/Makefile1
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c233
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c57
-rw-r--r--drivers/net/wireless/mwifiex/decl.h11
-rw-r--r--drivers/net/wireless/mwifiex/ethtool.c70
-rw-r--r--drivers/net/wireless/mwifiex/fw.h83
-rw-r--r--drivers/net/wireless/mwifiex/init.c18
-rw-r--r--drivers/net/wireless/mwifiex/ioctl.h31
-rw-r--r--drivers/net/wireless/mwifiex/join.c30
-rw-r--r--drivers/net/wireless/mwifiex/main.c15
-rw-r--r--drivers/net/wireless/mwifiex/main.h26
-rw-r--r--drivers/net/wireless/mwifiex/pcie.c206
-rw-r--r--drivers/net/wireless/mwifiex/scan.c17
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmd.c83
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmdresp.c6
-rw-r--r--drivers/net/wireless/mwifiex/sta_ioctl.c23
-rw-r--r--drivers/net/wireless/mwifiex/txrx.c2
-rw-r--r--drivers/net/wireless/mwifiex/uap_cmd.c55
-rw-r--r--drivers/net/wireless/mwifiex/util.c6
-rw-r--r--drivers/net/wireless/mwifiex/wmm.c76
23 files changed, 842 insertions, 287 deletions
diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c
index cf43b3c29250..966a78f8e21a 100644
--- a/drivers/net/wireless/mwifiex/11ac.c
+++ b/drivers/net/wireless/mwifiex/11ac.c
@@ -259,3 +259,44 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv,
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;
+}
diff --git a/drivers/net/wireless/mwifiex/11ac.h b/drivers/net/wireless/mwifiex/11ac.h
index 80fd1ba46200..7c2c69b5b3eb 100644
--- a/drivers/net/wireless/mwifiex/11ac.h
+++ b/drivers/net/wireless/mwifiex/11ac.h
@@ -20,7 +20,24 @@
#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);
#endif /* _MWIFIEX_11AC_H_ */
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c
index 45f19716687e..41e9d25a2d8e 100644
--- a/drivers/net/wireless/mwifiex/11n.c
+++ b/drivers/net/wireless/mwifiex/11n.c
@@ -679,3 +679,25 @@ void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra)
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;
+ }
+
+ return;
+}
diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile
index 97b245cbafd8..ecf28464367f 100644
--- a/drivers/net/wireless/mwifiex/Makefile
+++ b/drivers/net/wireless/mwifiex/Makefile
@@ -39,6 +39,7 @@ mwifiex-y += sta_tx.o
mwifiex-y += sta_rx.o
mwifiex-y += uap_txrx.o
mwifiex-y += cfg80211.o
+mwifiex-y += ethtool.o
mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o
obj-$(CONFIG_MWIFIEX) += mwifiex.o
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index a44023a7bd57..47012947a447 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -1374,6 +1374,18 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy,
}
mwifiex_set_ht_params(priv, bss_cfg, params);
+
+ if (priv->adapter->is_hw_11ac_capable) {
+ mwifiex_set_vht_params(priv, bss_cfg, params);
+ mwifiex_set_vht_width(priv, params->chandef.width,
+ priv->ap_11ac_enabled);
+ }
+
+ if (priv->ap_11ac_enabled)
+ mwifiex_set_11ac_ba_params(priv);
+ else
+ mwifiex_set_ba_params(priv);
+
mwifiex_set_wmm_params(priv, bss_cfg, params);
if (params->inactivity_timeout > 0) {
@@ -1892,7 +1904,8 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
}
}
- for (i = 0; i < request->n_channels; i++) {
+ for (i = 0; i < min_t(u32, request->n_channels,
+ MWIFIEX_USER_SCAN_CHAN_MAX); i++) {
chan = request->channels[i];
priv->user_scan_cfg->chan_list[i].chan_number = chan->hw_value;
priv->user_scan_cfg->chan_list[i].radio_type = chan->band;
@@ -1932,66 +1945,10 @@ static void mwifiex_setup_vht_caps(struct ieee80211_sta_vht_cap *vht_info,
struct mwifiex_private *priv)
{
struct mwifiex_adapter *adapter = priv->adapter;
- u32 vht_cap = 0, cap = adapter->hw_dot_11ac_dev_cap;
vht_info->vht_supported = true;
- switch (GET_VHTCAP_MAXMPDULEN(cap)) {
- case 0x00:
- vht_cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
- break;
- case 0x01:
- vht_cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
- break;
- case 0x10:
- vht_cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
- break;
- default:
- dev_err(adapter->dev, "unsupported MAX MPDU len\n");
- break;
- }
-
- if (ISSUPP_11ACVHTHTCVHT(cap))
- vht_cap |= IEEE80211_VHT_CAP_HTC_VHT;
-
- if (ISSUPP_11ACVHTTXOPPS(cap))
- vht_cap |= IEEE80211_VHT_CAP_VHT_TXOP_PS;
-
- if (ISSUPP_11ACMURXBEAMFORMEE(cap))
- vht_cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
-
- if (ISSUPP_11ACMUTXBEAMFORMEE(cap))
- vht_cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
-
- if (ISSUPP_11ACSUBEAMFORMER(cap))
- vht_cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
-
- if (ISSUPP_11ACSUBEAMFORMEE(cap))
- vht_cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
-
- if (ISSUPP_11ACRXSTBC(cap))
- vht_cap |= IEEE80211_VHT_CAP_RXSTBC_1;
-
- if (ISSUPP_11ACTXSTBC(cap))
- vht_cap |= IEEE80211_VHT_CAP_TXSTBC;
-
- if (ISSUPP_11ACSGI160(cap))
- vht_cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
-
- if (ISSUPP_11ACSGI80(cap))
- vht_cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
-
- if (ISSUPP_11ACLDPC(cap))
- vht_cap |= IEEE80211_VHT_CAP_RXLDPC;
-
- if (ISSUPP_11ACBW8080(cap))
- vht_cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
-
- if (ISSUPP_11ACBW160(cap))
- vht_cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
-
- vht_info->cap = vht_cap;
-
+ vht_info->cap = adapter->hw_dot_11ac_dev_cap;
/* Update MCS support for VHT */
vht_info->vht_mcs.rx_mcs_map = cpu_to_le16(
adapter->hw_dot_11ac_mcs_support & 0xFFFF);
@@ -2235,6 +2192,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT;
dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN;
+ dev->ethtool_ops = &mwifiex_ethtool_ops;
mdev_priv = netdev_priv(dev);
*((unsigned long *) mdev_priv) = (unsigned long) priv;
@@ -2293,6 +2251,152 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
}
EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
+#ifdef CONFIG_PM
+static bool
+mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat,
+ s8 *byte_seq)
+{
+ int j, k, valid_byte_cnt = 0;
+ bool dont_care_byte = false;
+
+ for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) {
+ for (k = 0; k < 8; k++) {
+ if (pat->mask[j] & 1 << k) {
+ memcpy(byte_seq + valid_byte_cnt,
+ &pat->pattern[j * 8 + k], 1);
+ valid_byte_cnt++;
+ if (dont_care_byte)
+ return false;
+ } else {
+ if (valid_byte_cnt)
+ dont_care_byte = true;
+ }
+
+ if (valid_byte_cnt > MAX_BYTESEQ)
+ return false;
+ }
+ }
+
+ byte_seq[MAX_BYTESEQ] = valid_byte_cnt;
+
+ return true;
+}
+
+static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
+ struct cfg80211_wowlan *wowlan)
+{
+ struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+ struct mwifiex_ds_mef_cfg mef_cfg;
+ struct mwifiex_mef_entry *mef_entry;
+ int i, filt_num = 0, ret;
+ bool first_pat = true;
+ u8 byte_seq[MAX_BYTESEQ + 1];
+ const u8 ipv4_mc_mac[] = {0x33, 0x33};
+ const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
+ struct mwifiex_private *priv =
+ mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
+
+ if (!wowlan) {
+ dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n");
+ return 0;
+ }
+
+ if (!priv->media_connected) {
+ dev_warn(adapter->dev,
+ "Can not configure WOWLAN in disconnected state\n");
+ return 0;
+ }
+
+ mef_entry = kzalloc(sizeof(*mef_entry), GFP_KERNEL);
+ if (!mef_entry)
+ return -ENOMEM;
+
+ memset(&mef_cfg, 0, sizeof(mef_cfg));
+ mef_cfg.num_entries = 1;
+ mef_cfg.mef_entry = mef_entry;
+ mef_entry->mode = MEF_MODE_HOST_SLEEP;
+ mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST;
+
+ for (i = 0; i < wowlan->n_patterns; i++) {
+ memset(byte_seq, 0, sizeof(byte_seq));
+ if (!mwifiex_is_pattern_supported(&wowlan->patterns[i],
+ byte_seq)) {
+ wiphy_err(wiphy, "Pattern not supported\n");
+ kfree(mef_entry);
+ return -EOPNOTSUPP;
+ }
+
+ if (!wowlan->patterns[i].pkt_offset) {
+ if (!(byte_seq[0] & 0x01) &&
+ (byte_seq[MAX_BYTESEQ] == 1)) {
+ mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST;
+ continue;
+ } else if (is_broadcast_ether_addr(byte_seq)) {
+ mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST;
+ continue;
+ } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
+ (byte_seq[MAX_BYTESEQ] == 2)) ||
+ (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
+ (byte_seq[MAX_BYTESEQ] == 3))) {
+ mef_cfg.criteria |= MWIFIEX_CRITERIA_MULTICAST;
+ continue;
+ }
+ }
+
+ mef_entry->filter[filt_num].repeat = 1;
+ mef_entry->filter[filt_num].offset =
+ wowlan->patterns[i].pkt_offset;
+ memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq,
+ sizeof(byte_seq));
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+
+ if (first_pat)
+ first_pat = false;
+ else
+ mef_entry->filter[filt_num].filt_action = TYPE_AND;
+
+ filt_num++;
+ }
+
+ if (wowlan->magic_pkt) {
+ mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST;
+ mef_entry->filter[filt_num].repeat = 16;
+ memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
+ ETH_ALEN);
+ mef_entry->filter[filt_num].byte_seq[MAX_BYTESEQ] = ETH_ALEN;
+ mef_entry->filter[filt_num].offset = 14;
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+ if (filt_num)
+ mef_entry->filter[filt_num].filt_action = TYPE_OR;
+ }
+
+ if (!mef_cfg.criteria)
+ mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST |
+ MWIFIEX_CRITERIA_UNICAST |
+ MWIFIEX_CRITERIA_MULTICAST;
+
+ ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_MEF_CFG,
+ HostCmd_ACT_GEN_SET, 0,
+ &mef_cfg);
+
+ kfree(mef_entry);
+ return ret;
+}
+
+static int mwifiex_cfg80211_resume(struct wiphy *wiphy)
+{
+ return 0;
+}
+
+static void mwifiex_cfg80211_set_wakeup(struct wiphy *wiphy,
+ bool enabled)
+{
+ struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+
+ device_set_wakeup_enable(adapter->dev, enabled);
+}
+#endif
+
/* station cfg80211 operations */
static struct cfg80211_ops mwifiex_cfg80211_ops = {
.add_virtual_intf = mwifiex_add_virtual_intf,
@@ -2321,6 +2425,11 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
.change_beacon = mwifiex_cfg80211_change_beacon,
.set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config,
.set_antenna = mwifiex_cfg80211_set_antenna,
+#ifdef CONFIG_PM
+ .suspend = mwifiex_cfg80211_suspend,
+ .resume = mwifiex_cfg80211_resume,
+ .set_wakeup = mwifiex_cfg80211_set_wakeup,
+#endif
};
/*
@@ -2379,6 +2488,14 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom);
+#ifdef CONFIG_PM
+ wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT;
+ wiphy->wowlan.n_patterns = MWIFIEX_MAX_FILTERS;
+ wiphy->wowlan.pattern_min_len = 1;
+ wiphy->wowlan.pattern_max_len = MWIFIEX_MAX_PATTERN_LEN;
+ wiphy->wowlan.max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN;
+#endif
+
wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
index 20a6c5555873..da469c336aa1 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/mwifiex/cmdevt.c
@@ -153,7 +153,21 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
" or cmd size is 0, not sending\n");
if (cmd_node->wait_q_enabled)
adapter->cmd_wait_q.status = -1;
- mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+ mwifiex_recycle_cmd_node(adapter, cmd_node);
+ return -1;
+ }
+
+ cmd_code = le16_to_cpu(host_cmd->command);
+ cmd_size = le16_to_cpu(host_cmd->size);
+
+ if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET &&
+ cmd_code != HostCmd_CMD_FUNC_SHUTDOWN &&
+ cmd_code != HostCmd_CMD_FUNC_INIT) {
+ dev_err(adapter->dev,
+ "DNLD_CMD: FW in reset state, ignore cmd %#x\n",
+ cmd_code);
+ mwifiex_complete_cmd(adapter, cmd_node);
+ mwifiex_recycle_cmd_node(adapter, cmd_node);
return -1;
}
@@ -168,9 +182,6 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
adapter->curr_cmd = cmd_node;
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
- cmd_code = le16_to_cpu(host_cmd->command);
- cmd_size = le16_to_cpu(host_cmd->size);
-
/* Adjust skb length */
if (cmd_node->cmd_skb->len > cmd_size)
/*
@@ -217,7 +228,7 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
adapter->cmd_sent = false;
if (cmd_node->wait_q_enabled)
adapter->cmd_wait_q.status = -1;
- mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+ mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
adapter->curr_cmd = NULL;
@@ -484,8 +495,6 @@ int mwifiex_send_cmd_sync(struct mwifiex_private *priv, uint16_t cmd_no,
ret = mwifiex_send_cmd_async(priv, cmd_no, cmd_action, cmd_oid,
data_buf);
- if (!ret)
- ret = mwifiex_wait_queue_complete(adapter);
return ret;
}
@@ -588,9 +597,10 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no,
if (cmd_no == HostCmd_CMD_802_11_SCAN) {
mwifiex_queue_scan_cmd(priv, cmd_node);
} else {
- adapter->cmd_queued = cmd_node;
mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true);
queue_work(adapter->workqueue, &adapter->main_work);
+ if (cmd_node->wait_q_enabled)
+ ret = mwifiex_wait_queue_complete(adapter, cmd_node);
}
return ret;
@@ -622,6 +632,20 @@ mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter,
spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags);
}
+/* This function reuses a command node. */
+void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node)
+{
+ struct host_cmd_ds_command *host_cmd = (void *)cmd_node->cmd_skb->data;
+
+ mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+
+ atomic_dec(&adapter->cmd_pending);
+ dev_dbg(adapter->dev, "cmd: FREE_CMD: cmd=%#x, cmd_pending=%d\n",
+ le16_to_cpu(host_cmd->command),
+ atomic_read(&adapter->cmd_pending));
+}
+
/*
* This function queues a command to the command pending queue.
*
@@ -663,7 +687,9 @@ mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter,
list_add(&cmd_node->list, &adapter->cmd_pending_q);
spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
- dev_dbg(adapter->dev, "cmd: QUEUE_CMD: cmd=%#x is queued\n", command);
+ atomic_inc(&adapter->cmd_pending);
+ dev_dbg(adapter->dev, "cmd: QUEUE_CMD: cmd=%#x, cmd_pending=%d\n",
+ command, atomic_read(&adapter->cmd_pending));
}
/*
@@ -773,7 +799,7 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
if (adapter->curr_cmd->cmd_flag & CMD_F_CANCELED) {
dev_err(adapter->dev, "CMD_RESP: %#x been canceled\n",
le16_to_cpu(resp->command));
- mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+ mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
adapter->curr_cmd = NULL;
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
@@ -823,7 +849,7 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
if (adapter->curr_cmd->wait_q_enabled)
adapter->cmd_wait_q.status = -1;
- mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+ mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
adapter->curr_cmd = NULL;
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
@@ -855,8 +881,7 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
if (adapter->curr_cmd->wait_q_enabled)
adapter->cmd_wait_q.status = ret;
- /* Clean up and put current command back to cmd_free_q */
- mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+ mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
adapter->curr_cmd = NULL;
@@ -983,7 +1008,7 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)
mwifiex_complete_cmd(adapter, cmd_node);
cmd_node->wait_q_enabled = false;
}
- mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+ mwifiex_recycle_cmd_node(adapter, cmd_node);
spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
}
spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
@@ -1030,7 +1055,7 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)
cmd_node = adapter->curr_cmd;
cmd_node->wait_q_enabled = false;
cmd_node->cmd_flag |= CMD_F_CANCELED;
- mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
+ mwifiex_recycle_cmd_node(adapter, cmd_node);
mwifiex_complete_cmd(adapter, adapter->curr_cmd);
adapter->curr_cmd = NULL;
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
@@ -1139,7 +1164,7 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
phs_cfg->params.hs_config.gpio,
phs_cfg->params.hs_config.gap);
}
- if (conditions != HOST_SLEEP_CFG_CANCEL) {
+ if (conditions != HS_CFG_CANCEL) {
adapter->is_hs_configured = true;
if (adapter->iface_type == MWIFIEX_USB ||
adapter->iface_type == MWIFIEX_PCIE)
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h
index e8a569aaa2e8..94cc09d48444 100644
--- a/drivers/net/wireless/mwifiex/decl.h
+++ b/drivers/net/wireless/mwifiex/decl.h
@@ -41,8 +41,15 @@
#define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2
#define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16
-#define MWIFIEX_AMPDU_DEF_TXWINSIZE 32
-#define MWIFIEX_AMPDU_DEF_RXWINSIZE 16
+#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE 16
+#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE 32
+#define MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE 32
+#define MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE 16
+#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE 32
+#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE 48
+#define MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE 48
+#define MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE 32
+
#define MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff
#define MWIFIEX_RATE_BITMAP_MCS0 32
diff --git a/drivers/net/wireless/mwifiex/ethtool.c b/drivers/net/wireless/mwifiex/ethtool.c
new file mode 100644
index 000000000000..bfb39908b2c6
--- /dev/null
+++ b/drivers/net/wireless/mwifiex/ethtool.c
@@ -0,0 +1,70 @@
+/*
+ * Marvell Wireless LAN device driver: ethtool
+ *
+ * Copyright (C) 2013, 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;
+}
+
+const struct ethtool_ops mwifiex_ethtool_ops = {
+ .get_wol = mwifiex_ethtool_get_wol,
+ .set_wol = mwifiex_ethtool_set_wol,
+};
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index 25acb0682c56..1f7578d553ec 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -230,40 +230,12 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & (BIT(13)|BIT(14)))
-#define GET_VHTCAP_MAXMPDULEN(vht_cap_info) (vht_cap_info & 0x3)
#define GET_VHTCAP_CHWDSET(vht_cap_info) ((vht_cap_info >> 2) & 0x3)
#define GET_VHTNSSMCS(mcs_mapset, nss) ((mcs_mapset >> (2 * (nss - 1))) & 0x3)
#define SET_VHTNSSMCS(mcs_mapset, nss, value) (mcs_mapset |= (value & 0x3) << \
(2 * (nss - 1)))
#define NO_NSS_SUPPORT 0x3
-/* HW_SPEC: HTC-VHT supported */
-#define ISSUPP_11ACVHTHTCVHT(Dot11acDevCap) (Dot11acDevCap & BIT(22))
-/* HW_SPEC: VHT TXOP PS support */
-#define ISSUPP_11ACVHTTXOPPS(Dot11acDevCap) (Dot11acDevCap & BIT(21))
-/* HW_SPEC: MU RX beamformee support */
-#define ISSUPP_11ACMURXBEAMFORMEE(Dot11acDevCap) (Dot11acDevCap & BIT(20))
-/* HW_SPEC: MU TX beamformee support */
-#define ISSUPP_11ACMUTXBEAMFORMEE(Dot11acDevCap) (Dot11acDevCap & BIT(19))
-/* HW_SPEC: SU Beamformee support */
-#define ISSUPP_11ACSUBEAMFORMEE(Dot11acDevCap) (Dot11acDevCap & BIT(10))
-/* HW_SPEC: SU Beamformer support */
-#define ISSUPP_11ACSUBEAMFORMER(Dot11acDevCap) (Dot11acDevCap & BIT(9))
-/* HW_SPEC: Rx STBC support */
-#define ISSUPP_11ACRXSTBC(Dot11acDevCap) (Dot11acDevCap & BIT(8))
-/* HW_SPEC: Tx STBC support */
-#define ISSUPP_11ACTXSTBC(Dot11acDevCap) (Dot11acDevCap & BIT(7))
-/* HW_SPEC: Short GI support for 160MHz BW */
-#define ISSUPP_11ACSGI160(Dot11acDevCap) (Dot11acDevCap & BIT(6))
-/* HW_SPEC: Short GI support for 80MHz BW */
-#define ISSUPP_11ACSGI80(Dot11acDevCap) (Dot11acDevCap & BIT(5))
-/* HW_SPEC: LDPC coding support */
-#define ISSUPP_11ACLDPC(Dot11acDevCap) (Dot11acDevCap & BIT(4))
-/* HW_SPEC: Channel BW 20/40/80/160/80+80 MHz support */
-#define ISSUPP_11ACBW8080(Dot11acDevCap) (Dot11acDevCap & BIT(3))
-/* HW_SPEC: Channel BW 20/40/80/160 MHz support */
-#define ISSUPP_11ACBW160(Dot11acDevCap) (Dot11acDevCap & BIT(2))
-
#define GET_DEVTXMCSMAP(dev_mcs_map) (dev_mcs_map >> 16)
#define GET_DEVRXMCSMAP(dev_mcs_map) (dev_mcs_map & 0xFFFF)
@@ -300,6 +272,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f
#define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083
#define HostCmd_CMD_VERSION_EXT 0x0097
+#define HostCmd_CMD_MEF_CFG 0x009a
#define HostCmd_CMD_RSSI_INFO 0x00a4
#define HostCmd_CMD_FUNC_INIT 0x00a9
#define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa
@@ -322,6 +295,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa
#define HostCmd_CMD_MGMT_FRAME_REG 0x010c
#define HostCmd_CMD_REMAIN_ON_CHAN 0x010d
+#define HostCmd_CMD_11AC_CFG 0x0112
#define PROTOCOL_NO_SECURITY 0x01
#define PROTOCOL_STATIC_WEP 0x02
@@ -376,10 +350,14 @@ enum P2P_MODES {
#define HostCmd_SCAN_RADIO_TYPE_BG 0
#define HostCmd_SCAN_RADIO_TYPE_A 1
-#define HOST_SLEEP_CFG_CANCEL 0xffffffff
-#define HOST_SLEEP_CFG_COND_DEF 0x00000000
-#define HOST_SLEEP_CFG_GPIO_DEF 0xff
-#define HOST_SLEEP_CFG_GAP_DEF 0
+#define HS_CFG_CANCEL 0xffffffff
+#define HS_CFG_COND_DEF 0x00000000
+#define HS_CFG_GPIO_DEF 0xff
+#define HS_CFG_GAP_DEF 0
+#define HS_CFG_COND_BROADCAST_DATA 0x00000001
+#define HS_CFG_COND_UNICAST_DATA 0x00000002
+#define HS_CFG_COND_MAC_EVENT 0x00000004
+#define HS_CFG_COND_MULTICAST_DATA 0x00000008
#define MWIFIEX_TIMEOUT_FOR_AP_RESP 0xfffc
#define MWIFIEX_STATUS_CODE_AUTH_TIMEOUT 2
@@ -469,6 +447,23 @@ enum P2P_MODES {
#define EVENT_GET_BSS_TYPE(event_cause) \
(((event_cause) >> 24) & 0x00ff)
+#define MWIFIEX_MAX_PATTERN_LEN 20
+#define MWIFIEX_MAX_OFFSET_LEN 50
+#define STACK_NBYTES 100
+#define TYPE_DNUM 1
+#define TYPE_BYTESEQ 2
+#define MAX_OPERAND 0x40
+#define TYPE_EQ (MAX_OPERAND+1)
+#define TYPE_EQ_DNUM (MAX_OPERAND+2)
+#define TYPE_EQ_BIT (MAX_OPERAND+3)
+#define TYPE_AND (MAX_OPERAND+4)
+#define TYPE_OR (MAX_OPERAND+5)
+#define MEF_MODE_HOST_SLEEP 1
+#define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3
+#define MWIFIEX_CRITERIA_BROADCAST BIT(0)
+#define MWIFIEX_CRITERIA_UNICAST BIT(1)
+#define MWIFIEX_CRITERIA_MULTICAST BIT(3)
+
struct mwifiex_ie_types_header {
__le16 type;
__le16 len;
@@ -1369,6 +1364,15 @@ struct host_cmd_ds_sys_config {
u8 tlv[0];
};
+struct host_cmd_11ac_vht_cfg {
+ __le16 action;
+ u8 band_config;
+ u8 misc_config;
+ __le32 cap_info;
+ __le32 mcs_tx_set;
+ __le32 mcs_rx_set;
+} __packed;
+
struct host_cmd_tlv_akmp {
struct host_cmd_tlv tlv;
__le16 key_mgmt;
@@ -1499,6 +1503,19 @@ struct host_cmd_ds_802_11_ibss_status {
__le16 use_g_rate_protect;
} __packed;
+struct mwifiex_fw_mef_entry {
+ u8 mode;
+ u8 action;
+ __le16 exprsize;
+ u8 expr[0];
+} __packed;
+
+struct host_cmd_ds_mef_cfg {
+ __le32 criteria;
+ __le16 num_entries;
+ struct mwifiex_fw_mef_entry mef_entry[0];
+} __packed;
+
#define CONNECTION_TYPE_INFRA 0
#define CONNECTION_TYPE_ADHOC 1
#define CONNECTION_TYPE_AP 2
@@ -1603,6 +1620,7 @@ struct host_cmd_ds_command {
struct host_cmd_ds_remain_on_chan roc_cfg;
struct host_cmd_ds_p2p_mode_cfg mode_cfg;
struct host_cmd_ds_802_11_ibss_status ibss_coalescing;
+ struct host_cmd_ds_mef_cfg mef_cfg;
struct host_cmd_ds_mac_reg_access mac_reg;
struct host_cmd_ds_bbp_reg_access bbp_reg;
struct host_cmd_ds_rf_reg_access rf_reg;
@@ -1612,6 +1630,7 @@ struct host_cmd_ds_command {
struct host_cmd_ds_802_11_eeprom_access eeprom;
struct host_cmd_ds_802_11_subsc_evt subsc_evt;
struct host_cmd_ds_sys_config uap_sys_config;
+ struct host_cmd_11ac_vht_cfg vht_cfg;
} params;
} __packed;
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index e38aa9b3663d..42d7f0adf9bd 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -318,9 +318,9 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K;
adapter->is_hs_configured = false;
- adapter->hs_cfg.conditions = cpu_to_le32(HOST_SLEEP_CFG_COND_DEF);
- adapter->hs_cfg.gpio = HOST_SLEEP_CFG_GPIO_DEF;
- adapter->hs_cfg.gap = HOST_SLEEP_CFG_GAP_DEF;
+ adapter->hs_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));
@@ -533,10 +533,8 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
if (!adapter->priv[i])
continue;
priv = adapter->priv[i];
- for (j = 0; j < MAX_NUM_TID; ++j) {
+ for (j = 0; j < MAX_NUM_TID; ++j)
INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list);
- spin_lock_init(&priv->wmm.tid_tbl_ptr[j].tid_tbl_lock);
- }
INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr);
INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr);
INIT_LIST_HEAD(&priv->sta_list);
@@ -709,6 +707,14 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
return ret;
}
+ /* cancel current command */
+ if (adapter->curr_cmd) {
+ dev_warn(adapter->dev, "curr_cmd is still in processing\n");
+ del_timer(&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");
diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h
index d85e6eb1f58a..7f27e45680b5 100644
--- a/drivers/net/wireless/mwifiex/ioctl.h
+++ b/drivers/net/wireless/mwifiex/ioctl.h
@@ -272,6 +272,14 @@ struct mwifiex_ds_pm_cfg {
} 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;
@@ -354,6 +362,29 @@ struct mwifiex_ds_misc_subsc_evt {
struct subsc_evt_cfg bcn_h_rssi_cfg;
};
+#define MAX_BYTESEQ 6 /* non-adjustable */
+#define MWIFIEX_MAX_FILTERS 10
+
+struct mwifiex_mef_filter {
+ u16 repeat;
+ u16 offset;
+ s8 byte_seq[MAX_BYTESEQ + 1];
+ u8 filt_type;
+ u8 filt_action;
+};
+
+struct mwifiex_mef_entry {
+ u8 mode;
+ u8 action;
+ struct mwifiex_mef_filter filter[MWIFIEX_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
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
index 246aa62a4817..6bcb66e6e97c 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/mwifiex/join.c
@@ -1117,10 +1117,9 @@ mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv,
adhoc_join->bss_descriptor.bssid,
adhoc_join->bss_descriptor.ssid);
- for (i = 0; bss_desc->supported_rates[i] &&
- i < MWIFIEX_SUPPORTED_RATES;
- i++)
- ;
+ for (i = 0; i < MWIFIEX_SUPPORTED_RATES &&
+ bss_desc->supported_rates[i]; i++)
+ ;
rates_size = i;
/* Copy Data Rates from the Rates recorded in scan response */
@@ -1296,6 +1295,14 @@ int mwifiex_associate(struct mwifiex_private *priv,
(bss_desc->bss_mode != NL80211_IFTYPE_STATION))
return -1;
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
+ !bss_desc->disable_11n && !bss_desc->disable_11ac &&
+ (priv->adapter->config_bands & BAND_GAC ||
+ priv->adapter->config_bands & BAND_AAC))
+ mwifiex_set_11ac_ba_params(priv);
+ else
+ mwifiex_set_ba_params(priv);
+
memcpy(&current_bssid,
&priv->curr_bss_params.bss_descriptor.mac_address,
sizeof(current_bssid));
@@ -1324,6 +1331,13 @@ mwifiex_adhoc_start(struct mwifiex_private *priv,
dev_dbg(priv->adapter->dev, "info: curr_bss_params.band = %d\n",
priv->curr_bss_params.band);
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
+ (priv->adapter->config_bands & BAND_GAC ||
+ priv->adapter->config_bands & BAND_AAC))
+ mwifiex_set_11ac_ba_params(priv);
+ else
+ mwifiex_set_ba_params(priv);
+
return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_AD_HOC_START,
HostCmd_ACT_GEN_SET, 0, adhoc_ssid);
}
@@ -1357,6 +1371,14 @@ int mwifiex_adhoc_join(struct mwifiex_private *priv,
return -1;
}
+ if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) &&
+ !bss_desc->disable_11n && !bss_desc->disable_11ac &&
+ (priv->adapter->config_bands & BAND_GAC ||
+ priv->adapter->config_bands & BAND_AAC))
+ mwifiex_set_11ac_ba_params(priv);
+ else
+ mwifiex_set_ba_params(priv);
+
dev_dbg(priv->adapter->dev, "info: curr_bss_params.channel = %d\n",
priv->curr_bss_params.bss_descriptor.channel);
dev_dbg(priv->adapter->dev, "info: curr_bss_params.band = %c\n",
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
index 9c802ede9c3b..121443a0f2a1 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -588,10 +588,19 @@ mwifiex_tx_timeout(struct net_device *dev)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- dev_err(priv->adapter->dev, "%lu : Tx timeout, bss_type-num = %d-%d\n",
- jiffies, priv->bss_type, priv->bss_num);
- mwifiex_set_trans_start(dev);
priv->num_tx_timeout++;
+ priv->tx_timeout_cnt++;
+ dev_err(priv->adapter->dev,
+ "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n",
+ jiffies, priv->tx_timeout_cnt, priv->bss_type, priv->bss_num);
+ mwifiex_set_trans_start(dev);
+
+ if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD &&
+ priv->adapter->if_ops.card_reset) {
+ dev_err(priv->adapter->dev,
+ "tx_timeout_cnt exceeds threshold. Triggering card reset!\n");
+ priv->adapter->if_ops.card_reset(priv->adapter);
+ }
}
/*
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index 553adfb0aa81..b7484efc9443 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -130,6 +130,9 @@ enum {
#define MWIFIEX_USB_TYPE_DATA 0xBEADC0DE
#define MWIFIEX_USB_TYPE_EVENT 0xBEEFFACE
+/* Threshold for tx_timeout_cnt before we trigger a card reset */
+#define TX_TIMEOUT_THRESHOLD 6
+
struct mwifiex_dbg {
u32 num_cmd_host_to_card_failure;
u32 num_cmd_sleep_cfm_host_to_card_failure;
@@ -210,15 +213,12 @@ struct mwifiex_ra_list_tbl {
struct mwifiex_tid_tbl {
struct list_head ra_list;
- /* spin lock for tid table */
- spinlock_t tid_tbl_lock;
struct mwifiex_ra_list_tbl *ra_list_curr;
};
#define WMM_HIGHEST_PRIORITY 7
#define HIGH_PRIO_TID 7
#define LOW_PRIO_TID 0
-#define NO_PKT_PRIO_TID (-1)
struct mwifiex_wmm_desc {
struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID];
@@ -394,6 +394,8 @@ struct mwifiex_private {
u8 curr_addr[ETH_ALEN];
u8 media_connected;
u32 num_tx_timeout;
+ /* track consecutive timeout */
+ u8 tx_timeout_cnt;
struct net_device *netdev;
struct net_device_stats stats;
u16 curr_pkt_filter;
@@ -723,7 +725,6 @@ struct mwifiex_adapter {
u16 cmd_wait_q_required;
struct mwifiex_wait_queue cmd_wait_q;
u8 scan_wait_q_woken;
- struct cmd_ctrl_node *cmd_queued;
spinlock_t queue_lock; /* lock for tx queues */
struct completion fw_load;
u8 country_code[IEEE80211_COUNTRY_STRING_LEN];
@@ -794,6 +795,8 @@ void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter);
void mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter,
struct cmd_ctrl_node *cmd_node);
+void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_node);
void mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter,
struct cmd_ctrl_node *cmd_node,
@@ -908,12 +911,20 @@ int mwifiex_set_secure_params(struct mwifiex_private *priv,
void mwifiex_set_ht_params(struct mwifiex_private *priv,
struct mwifiex_uap_bss_param *bss_cfg,
struct cfg80211_ap_settings *params);
+void mwifiex_set_vht_params(struct mwifiex_private *priv,
+ struct mwifiex_uap_bss_param *bss_cfg,
+ struct cfg80211_ap_settings *params);
void mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg,
struct cfg80211_ap_settings *params);
+void mwifiex_set_vht_width(struct mwifiex_private *priv,
+ enum nl80211_chan_width width,
+ bool ap_11ac_disable);
void
mwifiex_set_wmm_params(struct mwifiex_private *priv,
struct mwifiex_uap_bss_param *bss_cfg,
struct cfg80211_ap_settings *params);
+void mwifiex_set_ba_params(struct mwifiex_private *priv);
+void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv);
/*
* This function checks if the queuing is RA based or not.
@@ -1018,7 +1029,8 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv,
struct mwifiex_multicast_list *mcast_list);
int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,
struct net_device *dev);
-int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter);
+int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_queued);
int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
struct cfg80211_ssid *req_ssid);
int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type);
@@ -1098,11 +1110,15 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev);
void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config);
+int mwifiex_add_wowlan_magic_pkt_filter(struct mwifiex_adapter *adapter);
+
int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
struct cfg80211_beacon_data *data);
int mwifiex_del_mgmt_ies(struct mwifiex_private *priv);
u8 *mwifiex_11d_code_2_region(u8 code);
+extern const struct ethtool_ops mwifiex_ethtool_ops;
+
#ifdef CONFIG_DEBUG_FS
void mwifiex_debugfs_init(void);
void mwifiex_debugfs_remove(void);
diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c
index 35c79722c361..856959b64bc7 100644
--- a/drivers/net/wireless/mwifiex/pcie.c
+++ b/drivers/net/wireless/mwifiex/pcie.c
@@ -36,8 +36,6 @@ static u8 user_rmmod;
static struct mwifiex_if_ops pcie_ops;
static struct semaphore add_remove_card_sem;
-static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter);
-static int mwifiex_pcie_resume(struct pci_dev *pdev);
static int
mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb,
@@ -78,6 +76,82 @@ static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter)
return false;
}
+#ifdef CONFIG_PM
+/*
+ * Kernel needs to suspend all functions separately. Therefore all
+ * registered functions must have drivers with suspend and resume
+ * methods. Failing that the kernel simply removes the whole card.
+ *
+ * If already not suspended, this function allocates and sends a host
+ * sleep activate request to the firmware and turns off the traffic.
+ */
+static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct mwifiex_adapter *adapter;
+ struct pcie_service_card *card;
+ int hs_actived;
+
+ if (pdev) {
+ card = (struct pcie_service_card *) pci_get_drvdata(pdev);
+ if (!card || !card->adapter) {
+ pr_err("Card or adapter structure is not valid\n");
+ return 0;
+ }
+ } else {
+ pr_err("PCIE device is not specified\n");
+ return 0;
+ }
+
+ adapter = card->adapter;
+
+ hs_actived = mwifiex_enable_hs(adapter);
+
+ /* Indicate device suspended */
+ adapter->is_suspended = true;
+
+ return 0;
+}
+
+/*
+ * Kernel needs to suspend all functions separately. Therefore all
+ * registered functions must have drivers with suspend and resume
+ * methods. Failing that the kernel simply removes the whole card.
+ *
+ * If already not resumed, this function turns on the traffic and
+ * sends a host sleep cancel request to the firmware.
+ */
+static int mwifiex_pcie_resume(struct pci_dev *pdev)
+{
+ struct mwifiex_adapter *adapter;
+ struct pcie_service_card *card;
+
+ if (pdev) {
+ card = (struct pcie_service_card *) pci_get_drvdata(pdev);
+ if (!card || !card->adapter) {
+ pr_err("Card or adapter structure is not valid\n");
+ return 0;
+ }
+ } else {
+ pr_err("PCIE device is not specified\n");
+ return 0;
+ }
+
+ adapter = card->adapter;
+
+ if (!adapter->is_suspended) {
+ dev_warn(adapter->dev, "Device already resumed\n");
+ return 0;
+ }
+
+ adapter->is_suspended = false;
+
+ mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
+ MWIFIEX_ASYNC_CMD);
+
+ return 0;
+}
+#endif
+
/*
* This function probes an mwifiex device and registers it. It allocates
* the card structure, enables PCIE function number and initiates the
@@ -159,80 +233,6 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
kfree(card);
}
-/*
- * Kernel needs to suspend all functions separately. Therefore all
- * registered functions must have drivers with suspend and resume
- * methods. Failing that the kernel simply removes the whole card.
- *
- * If already not suspended, this function allocates and sends a host
- * sleep activate request to the firmware and turns off the traffic.
- */
-static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- struct mwifiex_adapter *adapter;
- struct pcie_service_card *card;
- int hs_actived;
-
- if (pdev) {
- card = (struct pcie_service_card *) pci_get_drvdata(pdev);
- if (!card || !card->adapter) {
- pr_err("Card or adapter structure is not valid\n");
- return 0;
- }
- } else {
- pr_err("PCIE device is not specified\n");
- return 0;
- }
-
- adapter = card->adapter;
-
- hs_actived = mwifiex_enable_hs(adapter);
-
- /* Indicate device suspended */
- adapter->is_suspended = true;
-
- return 0;
-}
-
-/*
- * Kernel needs to suspend all functions separately. Therefore all
- * registered functions must have drivers with suspend and resume
- * methods. Failing that the kernel simply removes the whole card.
- *
- * If already not resumed, this function turns on the traffic and
- * sends a host sleep cancel request to the firmware.
- */
-static int mwifiex_pcie_resume(struct pci_dev *pdev)
-{
- struct mwifiex_adapter *adapter;
- struct pcie_service_card *card;
-
- if (pdev) {
- card = (struct pcie_service_card *) pci_get_drvdata(pdev);
- if (!card || !card->adapter) {
- pr_err("Card or adapter structure is not valid\n");
- return 0;
- }
- } else {
- pr_err("PCIE device is not specified\n");
- return 0;
- }
-
- adapter = card->adapter;
-
- if (!adapter->is_suspended) {
- dev_warn(adapter->dev, "Device already resumed\n");
- return 0;
- }
-
- adapter->is_suspended = false;
-
- mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
- MWIFIEX_ASYNC_CMD);
-
- return 0;
-}
-
static DEFINE_PCI_DEVICE_TABLE(mwifiex_ids) = {
{
PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P,
@@ -287,35 +287,46 @@ static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data)
}
/*
- * This function wakes up the card.
- *
- * A host power up command is written to the card configuration
- * register to wake up the card.
+ * This function adds delay loop to ensure FW is awake before proceeding.
*/
-static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
+static void mwifiex_pcie_dev_wakeup_delay(struct mwifiex_adapter *adapter)
{
int i = 0;
- struct pcie_service_card *card = adapter->card;
- const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
- while (reg->sleep_cookie && mwifiex_pcie_ok_to_access_hw(adapter)) {
+ while (mwifiex_pcie_ok_to_access_hw(adapter)) {
i++;
usleep_range(10, 20);
/* 50ms max wait */
- if (i == 50000)
+ if (i == 5000)
break;
}
+ return;
+}
+
+/* This function wakes up the card by reading fw_status register. */
+static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter)
+{
+ u32 fw_status;
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
dev_dbg(adapter->dev, "event: Wakeup device...\n");
- /* Enable interrupts or any chip access will wakeup device */
- if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, HOST_INTR_MASK)) {
- dev_warn(adapter->dev, "Enable host interrupt failed\n");
+ if (reg->sleep_cookie)
+ mwifiex_pcie_dev_wakeup_delay(adapter);
+
+ /* Reading fw_status register will wakeup device */
+ if (mwifiex_read_reg(adapter, reg->fw_status, &fw_status)) {
+ dev_warn(adapter->dev, "Reading fw_status register failed\n");
return -1;
}
- dev_dbg(adapter->dev, "PCIE wakeup: Setting PS_STATE_AWAKE\n");
- adapter->ps_state = PS_STATE_AWAKE;
+ if (reg->sleep_cookie) {
+ mwifiex_pcie_dev_wakeup_delay(adapter);
+ dev_dbg(adapter->dev, "PCIE wakeup: Setting PS_STATE_AWAKE\n");
+ adapter->ps_state = PS_STATE_AWAKE;
+ }
return 0;
}
@@ -1030,8 +1041,8 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb,
u32 wrindx, num_tx_buffs, rx_val;
int ret;
dma_addr_t buf_pa;
- struct mwifiex_pcie_buf_desc *desc;
- struct mwifiex_pfu_buf_desc *desc2;
+ struct mwifiex_pcie_buf_desc *desc = NULL;
+ struct mwifiex_pfu_buf_desc *desc2 = NULL;
__le16 *tmp;
if (!(skb->data && skb->len)) {
@@ -1508,6 +1519,7 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter)
}
memcpy(adapter->upld_buf, skb->data,
min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len));
+ skb_push(skb, INTF_HEADER_LEN);
if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE,
PCI_DMA_FROMDEVICE))
return -1;
@@ -1983,12 +1995,13 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
}
}
} else if (!adapter->pps_uapsd_mode &&
- adapter->ps_state == PS_STATE_SLEEP) {
+ adapter->ps_state == PS_STATE_SLEEP &&
+ mwifiex_pcie_ok_to_access_hw(adapter)) {
/* Potentially for PCIe we could get other
* interrupts like shared. Don't change power
* state until cookie is set */
- if (mwifiex_pcie_ok_to_access_hw(adapter))
- adapter->ps_state = PS_STATE_AWAKE;
+ adapter->ps_state = PS_STATE_AWAKE;
+ adapter->pm_wakeup_fw_try = false;
}
}
}
@@ -2111,7 +2124,8 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter)
}
dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n",
adapter->cmd_sent, adapter->data_sent);
- mwifiex_pcie_enable_host_int(adapter);
+ if (adapter->ps_state != PS_STATE_SLEEP)
+ mwifiex_pcie_enable_host_int(adapter);
return 0;
}
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index bb60c2754a97..e7f6deaf715e 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/mwifiex/scan.c
@@ -1388,10 +1388,15 @@ int mwifiex_scan_networks(struct mwifiex_private *priv,
list_del(&cmd_node->list);
spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
flags);
- adapter->cmd_queued = cmd_node;
mwifiex_insert_cmd_to_pending_q(adapter, cmd_node,
true);
queue_work(adapter->workqueue, &adapter->main_work);
+
+ /* Perform internal scan synchronously */
+ if (!priv->scan_request) {
+ dev_dbg(adapter->dev, "wait internal scan\n");
+ mwifiex_wait_queue_complete(adapter, cmd_node);
+ }
} else {
spin_unlock_irqrestore(&adapter->scan_pending_q_lock,
flags);
@@ -1790,7 +1795,12 @@ check_next_scan:
/* Need to indicate IOCTL complete */
if (adapter->curr_cmd->wait_q_enabled) {
adapter->cmd_wait_q.status = 0;
- mwifiex_complete_cmd(adapter, adapter->curr_cmd);
+ if (!priv->scan_request) {
+ dev_dbg(adapter->dev,
+ "complete internal scan\n");
+ mwifiex_complete_cmd(adapter,
+ adapter->curr_cmd);
+ }
}
if (priv->report_scan_result)
priv->report_scan_result = false;
@@ -1946,9 +1956,6 @@ int mwifiex_request_scan(struct mwifiex_private *priv,
/* Normal scan */
ret = mwifiex_scan_networks(priv, NULL);
- if (!ret)
- ret = mwifiex_wait_queue_complete(priv->adapter);
-
up(&priv->async_sem);
return ret;
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c
index c55c5bb93134..b193e25977d2 100644
--- a/drivers/net/wireless/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/mwifiex/sta_cmd.c
@@ -24,6 +24,7 @@
#include "main.h"
#include "wmm.h"
#include "11n.h"
+#include "11ac.h"
/*
* This function prepares command to set/get RSSI information.
@@ -334,7 +335,7 @@ mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv,
cmd->command = cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH);
if (!hs_activate &&
- (hscfg_param->conditions != cpu_to_le32(HOST_SLEEP_CFG_CANCEL)) &&
+ (hscfg_param->conditions != cpu_to_le32(HS_CFG_CANCEL)) &&
((adapter->arp_filter_size > 0) &&
(adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE))) {
dev_dbg(adapter->dev,
@@ -1059,6 +1060,80 @@ mwifiex_cmd_802_11_subsc_evt(struct mwifiex_private *priv,
return 0;
}
+static int
+mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv,
+ struct mwifiex_mef_entry *mef_entry,
+ u8 **buffer)
+{
+ struct mwifiex_mef_filter *filter = mef_entry->filter;
+ int i, byte_len;
+ u8 *stack_ptr = *buffer;
+
+ for (i = 0; i < MWIFIEX_MAX_FILTERS; i++) {
+ filter = &mef_entry->filter[i];
+ if (!filter->filt_type)
+ break;
+ *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->repeat);
+ stack_ptr += 4;
+ *stack_ptr = TYPE_DNUM;
+ stack_ptr += 1;
+
+ byte_len = filter->byte_seq[MAX_BYTESEQ];
+ memcpy(stack_ptr, filter->byte_seq, byte_len);
+ stack_ptr += byte_len;
+ *stack_ptr = byte_len;
+ stack_ptr += 1;
+ *stack_ptr = TYPE_BYTESEQ;
+ stack_ptr += 1;
+
+ *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->offset);
+ stack_ptr += 4;
+ *stack_ptr = TYPE_DNUM;
+ stack_ptr += 1;
+
+ *stack_ptr = filter->filt_type;
+ stack_ptr += 1;
+
+ if (filter->filt_action) {
+ *stack_ptr = filter->filt_action;
+ stack_ptr += 1;
+ }
+
+ if (stack_ptr - *buffer > STACK_NBYTES)
+ return -1;
+ }
+
+ *buffer = stack_ptr;
+ return 0;
+}
+
+static int
+mwifiex_cmd_mef_cfg(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ struct mwifiex_ds_mef_cfg *mef)
+{
+ struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg;
+ u8 *pos = (u8 *)mef_cfg;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_MEF_CFG);
+
+ mef_cfg->criteria = cpu_to_le32(mef->criteria);
+ mef_cfg->num_entries = cpu_to_le16(mef->num_entries);
+ pos += sizeof(*mef_cfg);
+ mef_cfg->mef_entry->mode = mef->mef_entry->mode;
+ mef_cfg->mef_entry->action = mef->mef_entry->action;
+ pos += sizeof(*(mef_cfg->mef_entry));
+
+ if (mwifiex_cmd_append_rpn_expression(priv, mef->mef_entry, &pos))
+ return -1;
+
+ mef_cfg->mef_entry->exprsize =
+ cpu_to_le16(pos - mef_cfg->mef_entry->expr);
+ cmd->size = cpu_to_le16((u16) (pos - (u8 *)mef_cfg) + S_DS_GEN);
+
+ return 0;
+}
+
/*
* This function prepares the commands before sending them to the firmware.
*
@@ -1184,6 +1259,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
cpu_to_le16(sizeof(struct host_cmd_ds_remain_on_chan) +
S_DS_GEN);
break;
+ case HostCmd_CMD_11AC_CFG:
+ ret = mwifiex_cmd_11ac_cfg(priv, cmd_ptr, cmd_action, data_buf);
+ break;
case HostCmd_CMD_P2P_MODE_CFG:
cmd_ptr->command = cpu_to_le16(cmd_no);
cmd_ptr->params.mode_cfg.action = cpu_to_le16(cmd_action);
@@ -1273,6 +1351,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
case HostCmd_CMD_802_11_SUBSCRIBE_EVENT:
ret = mwifiex_cmd_802_11_subsc_evt(priv, cmd_ptr, data_buf);
break;
+ case HostCmd_CMD_MEF_CFG:
+ ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf);
+ break;
default:
dev_err(priv->adapter->dev,
"PREP_CMD: unknown cmd- %#x\n", cmd_no);
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c
index 4669f8d9389f..9f990e14966e 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c
@@ -95,7 +95,7 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv,
break;
}
/* Handling errors here */
- mwifiex_insert_cmd_to_free_q(adapter, adapter->curr_cmd);
+ mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd);
spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags);
adapter->curr_cmd = NULL;
@@ -907,6 +907,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
case HostCmd_CMD_REMAIN_ON_CHAN:
ret = mwifiex_ret_remain_on_chan(priv, resp, data_buf);
break;
+ case HostCmd_CMD_11AC_CFG:
+ break;
case HostCmd_CMD_P2P_MODE_CFG:
ret = mwifiex_ret_p2p_mode_cfg(priv, resp, data_buf);
break;
@@ -976,6 +978,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
case HostCmd_CMD_UAP_BSS_STOP:
priv->bss_started = 0;
break;
+ case HostCmd_CMD_MEF_CFG:
+ break;
default:
dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
resp->command);
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c
index 9f33c92c90f5..e6c9b2ae22ed 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/mwifiex/sta_ioctl.c
@@ -54,19 +54,10 @@ int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,
* This function waits on a cmd wait queue. It also cancels the pending
* request after waking up, in case of errors.
*/
-int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter)
+int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter,
+ struct cmd_ctrl_node *cmd_queued)
{
int status;
- struct cmd_ctrl_node *cmd_queued;
-
- if (!adapter->cmd_queued)
- return 0;
-
- cmd_queued = adapter->cmd_queued;
- adapter->cmd_queued = NULL;
-
- dev_dbg(adapter->dev, "cmd pending\n");
- atomic_inc(&adapter->cmd_pending);
/* Wait for completion */
status = wait_event_interruptible(adapter->cmd_wait_q.wait,
@@ -388,7 +379,7 @@ static int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
break;
}
if (hs_cfg->is_invoke_hostcmd) {
- if (hs_cfg->conditions == HOST_SLEEP_CFG_CANCEL) {
+ if (hs_cfg->conditions == HS_CFG_CANCEL) {
if (!adapter->is_hs_configured)
/* Already cancelled */
break;
@@ -403,8 +394,8 @@ static int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
adapter->hs_cfg.gpio = (u8)hs_cfg->gpio;
if (hs_cfg->gap)
adapter->hs_cfg.gap = (u8)hs_cfg->gap;
- } else if (adapter->hs_cfg.conditions
- == cpu_to_le32(HOST_SLEEP_CFG_CANCEL)) {
+ } else if (adapter->hs_cfg.conditions ==
+ cpu_to_le32(HS_CFG_CANCEL)) {
/* Return failure if no parameters for HS
enable */
status = -1;
@@ -420,7 +411,7 @@ static int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
HostCmd_CMD_802_11_HS_CFG_ENH,
HostCmd_ACT_GEN_SET, 0,
&adapter->hs_cfg);
- if (hs_cfg->conditions == HOST_SLEEP_CFG_CANCEL)
+ if (hs_cfg->conditions == HS_CFG_CANCEL)
/* Restore previous condition */
adapter->hs_cfg.conditions =
cpu_to_le32(prev_cond);
@@ -454,7 +445,7 @@ int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type)
{
struct mwifiex_ds_hs_cfg hscfg;
- hscfg.conditions = HOST_SLEEP_CFG_CANCEL;
+ hscfg.conditions = HS_CFG_CANCEL;
hscfg.is_invoke_hostcmd = true;
return mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c
index 296faec14365..8f923d0d2ba6 100644
--- a/drivers/net/wireless/mwifiex/txrx.c
+++ b/drivers/net/wireless/mwifiex/txrx.c
@@ -169,6 +169,8 @@ int mwifiex_write_data_complete(struct mwifiex_adapter *adapter,
if (!status) {
priv->stats.tx_packets++;
priv->stats.tx_bytes += skb->len;
+ if (priv->tx_timeout_cnt)
+ priv->tx_timeout_cnt = 0;
} else {
priv->stats.tx_errors++;
}
diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c
index 6e76a15a8950..b04b1db29100 100644
--- a/drivers/net/wireless/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/mwifiex/uap_cmd.c
@@ -18,6 +18,7 @@
*/
#include "main.h"
+#include "11ac.h"
/* This function parses security related parameters from cfg80211_ap_settings
* and sets into FW understandable bss_config structure.
@@ -177,6 +178,60 @@ mwifiex_set_ht_params(struct mwifiex_private *priv,
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_sync(priv, HostCmd_CMD_11AC_CFG,
+ HostCmd_ACT_GEN_SET, 0, &vht_cfg);
+
+ return;
+}
+
/* This function finds supported rates IE from beacon parameter and sets
* these rates into bss_config structure.
*/
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c
index 21553976b550..e57ac0dd3ab5 100644
--- a/drivers/net/wireless/mwifiex/util.c
+++ b/drivers/net/wireless/mwifiex/util.c
@@ -195,7 +195,7 @@ int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb)
skb->protocol = eth_type_trans(skb, priv->netdev);
skb->ip_summed = CHECKSUM_NONE;
- /* This is required only in case of 11n and USB as we alloc
+ /* 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.
@@ -212,7 +212,8 @@ int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb)
* fragments. Currently we fail the Filesndl-ht.scr script
* for UDP, hence this fix
*/
- if ((priv->adapter->iface_type == MWIFIEX_USB) &&
+ 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);
@@ -238,7 +239,6 @@ int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb)
int mwifiex_complete_cmd(struct mwifiex_adapter *adapter,
struct cmd_ctrl_node *cmd_node)
{
- atomic_dec(&adapter->cmd_pending);
dev_dbg(adapter->dev, "cmd completed: status=%d\n",
adapter->cmd_wait_q.status);
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c
index 32adc878041d..2cc81ba590e3 100644
--- a/drivers/net/wireless/mwifiex/wmm.c
+++ b/drivers/net/wireless/mwifiex/wmm.c
@@ -436,10 +436,7 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter)
= priv->aggr_prio_tbl[7].ampdu_user
= BA_STREAM_NOT_ALLOWED;
- priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT;
- priv->add_ba_param.tx_win_size = MWIFIEX_AMPDU_DEF_TXWINSIZE;
- priv->add_ba_param.rx_win_size = MWIFIEX_AMPDU_DEF_RXWINSIZE;
-
+ mwifiex_set_ba_params(priv);
mwifiex_reset_11n_rx_seq_num(priv);
atomic_set(&priv->wmm.tx_pkts_queued, 0);
@@ -688,13 +685,13 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
ra_list->total_pkts_size += skb->len;
ra_list->pkt_count++;
- atomic_inc(&priv->wmm.tx_pkts_queued);
-
if (atomic_read(&priv->wmm.highest_queued_prio) <
tos_to_tid_inv[tid_down])
atomic_set(&priv->wmm.highest_queued_prio,
tos_to_tid_inv[tid_down]);
+ atomic_inc(&priv->wmm.tx_pkts_queued);
+
spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
}
@@ -890,19 +887,15 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
struct mwifiex_bss_prio_node *bssprio_node, *bssprio_head;
struct mwifiex_tid_tbl *tid_ptr;
atomic_t *hqp;
- int is_list_empty;
- unsigned long flags;
+ unsigned long flags_bss, flags_ra;
int i, j;
for (j = adapter->priv_num - 1; j >= 0; --j) {
spin_lock_irqsave(&adapter->bss_prio_tbl[j].bss_prio_lock,
- flags);
- is_list_empty = list_empty(&adapter->bss_prio_tbl[j]
- .bss_prio_head);
- spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock,
- flags);
- if (is_list_empty)
- continue;
+ flags_bss);
+
+ if (list_empty(&adapter->bss_prio_tbl[j].bss_prio_head))
+ goto skip_prio_tbl;
if (adapter->bss_prio_tbl[j].bss_prio_cur ==
(struct mwifiex_bss_prio_node *)
@@ -919,26 +912,26 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
do {
priv_tmp = bssprio_node->priv;
- hqp = &priv_tmp->wmm.highest_queued_prio;
+ if (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0)
+ goto skip_bss;
+
+ /* iterate over the WMM queues of the BSS */
+ hqp = &priv_tmp->wmm.highest_queued_prio;
for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) {
+ spin_lock_irqsave(&priv_tmp->wmm.
+ ra_list_spinlock, flags_ra);
+
tid_ptr = &(priv_tmp)->wmm.
tid_tbl_ptr[tos_to_tid[i]];
/* For non-STA ra_list_curr may be NULL */
if (!tid_ptr->ra_list_curr)
- continue;
-
- spin_lock_irqsave(&tid_ptr->tid_tbl_lock,
- flags);
- is_list_empty =
- list_empty(&adapter->bss_prio_tbl[j]
- .bss_prio_head);
- spin_unlock_irqrestore(&tid_ptr->tid_tbl_lock,
- flags);
- if (is_list_empty)
- continue;
+ goto skip_wmm_queue;
+
+ if (list_empty(&tid_ptr->ra_list))
+ goto skip_wmm_queue;
/*
* Always choose the next ra we transmitted
@@ -960,10 +953,8 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
}
do {
- is_list_empty =
- skb_queue_empty(&ptr->skb_head);
-
- if (!is_list_empty)
+ if (!skb_queue_empty(&ptr->skb_head))
+ /* holds both locks */
goto found;
/* Get next ra */
@@ -978,14 +969,14 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
struct mwifiex_ra_list_tbl,
list);
} while (ptr != head);
- }
- /* No packet at any TID for this priv. Mark as such
- * to skip checking TIDs for this priv (until pkt is
- * added).
- */
- atomic_set(hqp, NO_PKT_PRIO_TID);
+skip_wmm_queue:
+ spin_unlock_irqrestore(&priv_tmp->wmm.
+ ra_list_spinlock,
+ flags_ra);
+ }
+skip_bss:
/* Get next bss priority node */
bssprio_node = list_first_entry(&bssprio_node->list,
struct mwifiex_bss_prio_node,
@@ -1000,14 +991,21 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
struct mwifiex_bss_prio_node,
list);
} while (bssprio_node != bssprio_head);
+
+skip_prio_tbl:
+ spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock,
+ flags_bss);
}
+
return NULL;
found:
- spin_lock_irqsave(&priv_tmp->wmm.ra_list_spinlock, flags);
+ /* holds bss_prio_lock / ra_list_spinlock */
if (atomic_read(hqp) > i)
atomic_set(hqp, i);
- spin_unlock_irqrestore(&priv_tmp->wmm.ra_list_spinlock, flags);
+ spin_unlock_irqrestore(&priv_tmp->wmm.ra_list_spinlock, flags_ra);
+ spin_unlock_irqrestore(&adapter->bss_prio_tbl[j].bss_prio_lock,
+ flags_bss);
*priv = priv_tmp;
*tid = tos_to_tid[i];