summaryrefslogtreecommitdiff
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/Kconfig12
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c130
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c39
-rw-r--r--drivers/net/wireless/mwifiex/debugfs.c14
-rw-r--r--drivers/net/wireless/mwifiex/decl.h3
-rw-r--r--drivers/net/wireless/mwifiex/fw.h95
-rw-r--r--drivers/net/wireless/mwifiex/ie.c3
-rw-r--r--drivers/net/wireless/mwifiex/init.c10
-rw-r--r--drivers/net/wireless/mwifiex/join.c2
-rw-r--r--drivers/net/wireless/mwifiex/main.c63
-rw-r--r--drivers/net/wireless/mwifiex/main.h40
-rw-r--r--drivers/net/wireless/mwifiex/pcie.c12
-rw-r--r--drivers/net/wireless/mwifiex/pcie.h45
-rw-r--r--drivers/net/wireless/mwifiex/scan.c157
-rw-r--r--drivers/net/wireless/mwifiex/sdio.c206
-rw-r--r--drivers/net/wireless/mwifiex/sdio.h77
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmd.c90
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmdresp.c7
-rw-r--r--drivers/net/wireless/mwifiex/sta_event.c207
-rw-r--r--drivers/net/wireless/mwifiex/sta_ioctl.c4
-rw-r--r--drivers/net/wireless/mwifiex/tdls.c80
-rw-r--r--drivers/net/wireless/mwifiex/txrx.c22
-rw-r--r--drivers/net/wireless/mwifiex/uap_cmd.c7
-rw-r--r--drivers/net/wireless/mwifiex/uap_event.c15
-rw-r--r--drivers/net/wireless/mwifiex/usb.c23
-rw-r--r--drivers/net/wireless/mwifiex/usb.h3
-rw-r--r--drivers/net/wireless/mwifiex/util.c75
-rw-r--r--drivers/net/wireless/mwifiex/wmm.c156
-rw-r--r--drivers/net/wireless/mwifiex/wmm.h8
29 files changed, 1481 insertions, 124 deletions
diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig
index 48edf3876..317d99189 100644
--- a/drivers/net/wireless/mwifiex/Kconfig
+++ b/drivers/net/wireless/mwifiex/Kconfig
@@ -9,36 +9,36 @@ config MWIFIEX
mwifiex.
config MWIFIEX_SDIO
- tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8887/SD8897"
+ tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8887/SD8897/SD8997"
depends on MWIFIEX && MMC
select FW_LOADER
select WANT_DEV_COREDUMP
---help---
This adds support for wireless adapters based on Marvell
- 8786/8787/8797/8887/8897 chipsets with SDIO interface.
+ 8786/8787/8797/8887/8897/8997 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"
+ tristate "Marvell WiFi-Ex Driver for PCIE 8766/8897/8997"
depends on MWIFIEX && PCI
select FW_LOADER
select WANT_DEV_COREDUMP
---help---
This adds support for wireless adapters based on Marvell
- 8766/8897 chipsets with PCIe interface.
+ 8766/8897/8997 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 USB8766/8797/8897"
+ tristate "Marvell WiFi-Ex Driver for USB8766/8797/8897/8997"
depends on MWIFIEX && USB
select FW_LOADER
---help---
This adds support for wireless adapters based on Marvell
- 8797/8897 chipset with USB interface.
+ 8797/8897/8997 chipset with USB interface.
If you choose to build it as a module, it will be called
mwifiex_usb.
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index b15e4c7ac..ff63cb563 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -19,6 +19,7 @@
#include "cfg80211.h"
#include "main.h"
+#include "11n.h"
static char *reg_alpha2;
module_param(reg_alpha2, charp, 0);
@@ -34,12 +35,38 @@ static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = {
},
};
-static const struct ieee80211_iface_combination mwifiex_iface_comb_ap_sta = {
+static const struct ieee80211_iface_combination
+mwifiex_iface_comb_ap_sta = {
.limits = mwifiex_ap_sta_limits,
.num_different_channels = 1,
.n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits),
.max_interfaces = MWIFIEX_MAX_BSS_NUM,
.beacon_int_infra_match = true,
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40),
+};
+
+static const struct ieee80211_iface_combination
+mwifiex_iface_comb_ap_sta_vht = {
+ .limits = mwifiex_ap_sta_limits,
+ .num_different_channels = 1,
+ .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits),
+ .max_interfaces = MWIFIEX_MAX_BSS_NUM,
+ .beacon_int_infra_match = true,
+ .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40) |
+ BIT(NL80211_CHAN_WIDTH_80),
+};
+
+static const struct
+ieee80211_iface_combination mwifiex_iface_comb_ap_sta_drcs = {
+ .limits = mwifiex_ap_sta_limits,
+ .num_different_channels = 2,
+ .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits),
+ .max_interfaces = MWIFIEX_MAX_BSS_NUM,
+ .beacon_int_infra_match = true,
};
/*
@@ -441,7 +468,7 @@ mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev,
* - Country codes
* - Sub bands (first channel, number of channels, maximum Tx power)
*/
-static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy)
+int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy)
{
u8 no_of_triplet = 0;
struct ieee80211_country_ie_triplet *t;
@@ -804,10 +831,13 @@ mwifiex_init_new_priv_params(struct mwifiex_private *priv,
priv->bss_type = MWIFIEX_BSS_TYPE_STA;
break;
case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
priv->bss_role = MWIFIEX_BSS_ROLE_STA;
priv->bss_type = MWIFIEX_BSS_TYPE_P2P;
break;
+ case NL80211_IFTYPE_P2P_GO:
+ priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
+ priv->bss_type = MWIFIEX_BSS_TYPE_P2P;
+ break;
case NL80211_IFTYPE_AP:
priv->bss_type = MWIFIEX_BSS_TYPE_UAP;
priv->bss_role = MWIFIEX_BSS_ROLE_UAP;
@@ -1115,8 +1145,10 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy,
case NL80211_IFTYPE_P2P_GO:
switch (type) {
case NL80211_IFTYPE_STATION:
- if (mwifiex_cfg80211_init_p2p_client(priv))
+ if (mwifiex_cfg80211_deinit_p2p(priv))
return -EFAULT;
+ priv->adapter->curr_iface_comb.p2p_intf--;
+ priv->adapter->curr_iface_comb.sta_intf++;
dev->ieee80211_ptr->iftype = type;
break;
case NL80211_IFTYPE_ADHOC:
@@ -2788,6 +2820,7 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
struct mwifiex_adapter *adapter = priv->adapter;
+ struct sk_buff *skb, *tmp;
#ifdef CONFIG_DEBUG_FS
mwifiex_dev_debugfs_remove(priv);
@@ -2795,6 +2828,9 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
mwifiex_stop_net_dev_queue(priv->netdev, adapter);
+ skb_queue_walk_safe(&priv->bypass_txq, skb, tmp)
+ mwifiex_write_data_complete(priv->adapter, skb, 0, -1);
+
if (netif_carrier_ok(priv->netdev))
netif_carrier_off(priv->netdev);
@@ -2954,7 +2990,6 @@ static int mwifiex_set_wowlan_mef_entry(struct mwifiex_private *priv,
MWIFIEX_MEF_MAX_BYTESEQ)) {
mwifiex_dbg(priv->adapter, ERROR,
"Pattern not supported\n");
- kfree(mef_entry);
return -EOPNOTSUPP;
}
@@ -3036,9 +3071,12 @@ static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
mwifiex_set_auto_arp_mef_entry(priv, &mef_entry[0]);
- if (wowlan->n_patterns || wowlan->magic_pkt)
+ if (wowlan->n_patterns || wowlan->magic_pkt) {
ret = mwifiex_set_wowlan_mef_entry(priv, &mef_cfg,
&mef_entry[1], wowlan);
+ if (ret)
+ goto err;
+ }
if (!mef_cfg.criteria)
mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST |
@@ -3048,6 +3086,8 @@ static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG,
HostCmd_ACT_GEN_SET, 0,
&mef_cfg, true);
+
+err:
kfree(mef_entry);
return ret;
}
@@ -3360,6 +3400,72 @@ mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
}
static int
+mwifiex_cfg80211_tdls_chan_switch(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *addr, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ struct mwifiex_sta_node *sta_ptr;
+ unsigned long flags;
+ u16 chan;
+ u8 second_chan_offset, band;
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ sta_ptr = mwifiex_get_sta_entry(priv, addr);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+ if (!sta_ptr) {
+ wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n",
+ __func__, addr);
+ return -ENOENT;
+ }
+
+ if (!(sta_ptr->tdls_cap.extcap.ext_capab[3] &
+ WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)) {
+ wiphy_err(wiphy, "%pM do not support tdls cs\n", addr);
+ return -ENOENT;
+ }
+
+ if (sta_ptr->tdls_status == TDLS_CHAN_SWITCHING ||
+ sta_ptr->tdls_status == TDLS_IN_OFF_CHAN) {
+ wiphy_err(wiphy, "channel switch is running, abort request\n");
+ return -EALREADY;
+ }
+
+ chan = chandef->chan->hw_value;
+ second_chan_offset = mwifiex_get_sec_chan_offset(chan);
+ band = chandef->chan->band;
+ mwifiex_start_tdls_cs(priv, addr, chan, second_chan_offset, band);
+
+ return 0;
+}
+
+static void
+mwifiex_cfg80211_tdls_cancel_chan_switch(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr)
+{
+ struct mwifiex_sta_node *sta_ptr;
+ unsigned long flags;
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ sta_ptr = mwifiex_get_sta_entry(priv, addr);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+ if (!sta_ptr) {
+ wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n",
+ __func__, addr);
+ } else if (!(sta_ptr->tdls_status == TDLS_CHAN_SWITCHING ||
+ sta_ptr->tdls_status == TDLS_IN_BASE_CHAN ||
+ sta_ptr->tdls_status == TDLS_IN_OFF_CHAN)) {
+ wiphy_err(wiphy, "tdls chan switch not initialize by %pM\n",
+ addr);
+ } else
+ mwifiex_stop_tdls_cs(priv, addr);
+}
+
+static int
mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
const u8 *mac, struct station_parameters *params)
{
@@ -3575,6 +3681,8 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
.set_coalesce = mwifiex_cfg80211_set_coalesce,
.tdls_mgmt = mwifiex_cfg80211_tdls_mgmt,
.tdls_oper = mwifiex_cfg80211_tdls_oper,
+ .tdls_channel_switch = mwifiex_cfg80211_tdls_chan_switch,
+ .tdls_cancel_channel_switch = mwifiex_cfg80211_tdls_cancel_chan_switch,
.add_station = mwifiex_cfg80211_add_station,
.change_station = mwifiex_cfg80211_change_station,
.get_channel = mwifiex_cfg80211_get_channel,
@@ -3672,7 +3780,12 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
else
wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
- wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta;
+ if (adapter->drcs_enabled && ISSUPP_DRCS_ENABLED(adapter->fw_cap_info))
+ wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_drcs;
+ else if (adapter->is_hw_11ac_capable)
+ wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_vht;
+ else
+ wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta;
wiphy->n_iface_combinations = 1;
/* Initialize cipher suits */
@@ -3709,6 +3822,9 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
NL80211_FEATURE_INACTIVITY_TIMER |
NL80211_FEATURE_NEED_OBSS_SCAN;
+ if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info))
+ wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+
if (adapter->fw_api_ver == MWIFIEX_FW_V15)
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
index 207da4050..45ae38e32 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/mwifiex/cmdevt.c
@@ -167,8 +167,6 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
mwifiex_dbg(adapter, ERROR,
"DNLD_CMD: FW in reset state, ignore cmd %#x\n",
cmd_code);
- if (cmd_node->wait_q_enabled)
- mwifiex_complete_cmd(adapter, cmd_node);
mwifiex_recycle_cmd_node(adapter, cmd_node);
queue_work(adapter->workqueue, &adapter->main_work);
return -1;
@@ -809,17 +807,6 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter)
adapter->is_cmd_timedout = 0;
resp = (struct host_cmd_ds_command *) adapter->curr_cmd->resp_skb->data;
- if (adapter->curr_cmd->cmd_flag & CMD_F_CANCELED) {
- mwifiex_dbg(adapter, ERROR,
- "CMD_RESP: %#x been canceled\n",
- le16_to_cpu(resp->command));
- 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);
- return -1;
- }
-
if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) {
/* Copy original response back to response buffer */
struct mwifiex_ds_misc_cmd *hostcmd;
@@ -989,12 +976,13 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
if (cmd_node->wait_q_enabled) {
adapter->cmd_wait_q.status = -ETIMEDOUT;
- wake_up_interruptible(&adapter->cmd_wait_q.wait);
mwifiex_cancel_pending_ioctl(adapter);
}
}
- if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING)
+ if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {
mwifiex_init_fw_complete(adapter);
+ return;
+ }
if (adapter->if_ops.device_dump)
adapter->if_ops.device_dump(adapter);
@@ -1024,6 +1012,7 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)
adapter->curr_cmd->wait_q_enabled = false;
adapter->cmd_wait_q.status = -1;
mwifiex_complete_cmd(adapter, adapter->curr_cmd);
+ /* no recycle probably wait for response */
}
/* Cancel all pending command */
spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
@@ -1032,11 +1021,8 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter)
list_del(&cmd_node->list);
spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags);
- if (cmd_node->wait_q_enabled) {
+ if (cmd_node->wait_q_enabled)
adapter->cmd_wait_q.status = -1;
- mwifiex_complete_cmd(adapter, cmd_node);
- cmd_node->wait_q_enabled = false;
- }
mwifiex_recycle_cmd_node(adapter, cmd_node);
spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags);
}
@@ -1094,12 +1080,18 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)
(adapter->curr_cmd->wait_q_enabled)) {
spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags);
cmd_node = adapter->curr_cmd;
- cmd_node->wait_q_enabled = false;
- cmd_node->cmd_flag |= CMD_F_CANCELED;
- mwifiex_recycle_cmd_node(adapter, cmd_node);
- mwifiex_complete_cmd(adapter, adapter->curr_cmd);
+ /* setting curr_cmd to NULL is quite dangerous, because
+ * mwifiex_process_cmdresp checks curr_cmd to be != NULL
+ * at the beginning then relies on it and dereferences
+ * it at will
+ * this probably works since mwifiex_cmd_timeout_func
+ * is the only caller of this function and responses
+ * at that point
+ */
adapter->curr_cmd = NULL;
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags);
+
+ mwifiex_recycle_cmd_node(adapter, cmd_node);
}
/* Cancel all pending scan command */
@@ -1129,7 +1121,6 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)
}
}
}
- adapter->cmd_wait_q.status = -1;
}
/*
diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c
index 5a0636d43..5583856fc 100644
--- a/drivers/net/wireless/mwifiex/debugfs.c
+++ b/drivers/net/wireless/mwifiex/debugfs.c
@@ -731,7 +731,7 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf,
(struct mwifiex_private *) file->private_data;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *) addr;
- int pos = 0, ret = 0, i;
+ int pos, ret, i;
u8 value[MAX_EEPROM_DATA];
if (!buf)
@@ -739,7 +739,7 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf,
if (saved_offset == -1) {
/* No command has been given */
- pos += snprintf(buf, PAGE_SIZE, "0");
+ pos = snprintf(buf, PAGE_SIZE, "0");
goto done;
}
@@ -748,17 +748,17 @@ mwifiex_rdeeprom_read(struct file *file, char __user *ubuf,
(u16) saved_bytes, value);
if (ret) {
ret = -EINVAL;
- goto done;
+ goto out_free;
}
- pos += snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes);
+ 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);
+ pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]);
done:
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+out_free:
free_page(addr);
return ret;
}
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h
index 51e344789..098e1f14d 100644
--- a/drivers/net/wireless/mwifiex/decl.h
+++ b/drivers/net/wireless/mwifiex/decl.h
@@ -141,6 +141,9 @@ enum mwifiex_tdls_status {
TDLS_SETUP_COMPLETE,
TDLS_SETUP_FAILURE,
TDLS_LINK_TEARDOWN,
+ TDLS_CHAN_SWITCHING,
+ TDLS_IN_BASE_CHAN,
+ TDLS_IN_OFF_CHAN,
};
enum mwifiex_tdls_error_code {
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index cd0905171..3ec2ac82e 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -169,14 +169,17 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123)
#define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145)
#define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146)
+#define TLV_TYPE_TX_PAUSE (PROPRIETARY_TLV_BASE_ID + 148)
#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154)
#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156)
+#define TLV_TYPE_MULTI_CHAN_INFO (PROPRIETARY_TLV_BASE_ID + 183)
#define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194)
#define TLV_TYPE_SCAN_CHANNEL_GAP (PROPRIETARY_TLV_BASE_ID + 197)
#define TLV_TYPE_API_REV (PROPRIETARY_TLV_BASE_ID + 199)
#define TLV_TYPE_CHANNEL_STATS (PROPRIETARY_TLV_BASE_ID + 198)
#define TLV_BTCOEX_WL_AGGR_WINSIZE (PROPRIETARY_TLV_BASE_ID + 202)
#define TLV_BTCOEX_WL_SCANTIME (PROPRIETARY_TLV_BASE_ID + 203)
+#define TLV_TYPE_BSS_MODE (PROPRIETARY_TLV_BASE_ID + 206)
#define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048
@@ -200,6 +203,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11))
#define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14))
+#define ISSUPP_DRCS_ENABLED(FwCapInfo) (FwCapInfo & BIT(15))
#define ISSUPP_SDIO_SPA_ENABLED(FwCapInfo) (FwCapInfo & BIT(16))
#define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \
@@ -359,6 +363,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define HostCmd_CMD_MGMT_FRAME_REG 0x010c
#define HostCmd_CMD_REMAIN_ON_CHAN 0x010d
#define HostCmd_CMD_11AC_CFG 0x0112
+#define HostCmd_CMD_TDLS_CONFIG 0x0100
+#define HostCmd_CMD_MC_POLICY 0x0121
#define HostCmd_CMD_TDLS_OPER 0x0122
#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG 0x0223
@@ -432,7 +438,6 @@ enum P2P_MODES {
#define CMD_F_HOSTCMD (1 << 0)
-#define CMD_F_CANCELED (1 << 1)
#define HostCmd_CMD_ID_MASK 0x0fff
@@ -509,8 +514,10 @@ enum P2P_MODES {
#define EVENT_TDLS_GENERIC_EVENT 0x00000052
#define EVENT_RADAR_DETECTED 0x00000053
#define EVENT_CHANNEL_REPORT_RDY 0x00000054
+#define EVENT_TX_DATA_PAUSE 0x00000055
#define EVENT_EXT_SCAN_REPORT 0x00000058
#define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f
+#define EVENT_MULTI_CHAN_INFO 0x0000006a
#define EVENT_TX_STATUS_REPORT 0x00000074
#define EVENT_BT_COEX_WLAN_PARA_CHANGE 0X00000076
@@ -545,7 +552,27 @@ enum P2P_MODES {
#define ACT_TDLS_DELETE 0x00
#define ACT_TDLS_CREATE 0x01
#define ACT_TDLS_CONFIG 0x02
-#define TDLS_EVENT_LINK_TEAR_DOWN 3
+
+#define TDLS_EVENT_LINK_TEAR_DOWN 3
+#define TDLS_EVENT_CHAN_SWITCH_RESULT 7
+#define TDLS_EVENT_START_CHAN_SWITCH 8
+#define TDLS_EVENT_CHAN_SWITCH_STOPPED 9
+
+#define TDLS_BASE_CHANNEL 0
+#define TDLS_OFF_CHANNEL 1
+
+#define ACT_TDLS_CS_ENABLE_CONFIG 0x00
+#define ACT_TDLS_CS_INIT 0x06
+#define ACT_TDLS_CS_STOP 0x07
+#define ACT_TDLS_CS_PARAMS 0x08
+
+#define MWIFIEX_DEF_CS_UNIT_TIME 2
+#define MWIFIEX_DEF_CS_THR_OTHERLINK 10
+#define MWIFIEX_DEF_THR_DIRECTLINK 0
+#define MWIFIEX_DEF_CS_TIME 10
+#define MWIFIEX_DEF_CS_TIMEOUT 16
+#define MWIFIEX_DEF_CS_REG_CLASS 12
+#define MWIFIEX_DEF_CS_PERIODICITY 1
#define MWIFIEX_FW_V15 15
@@ -658,6 +685,7 @@ struct mwifiex_fw_chan_stats {
enum mwifiex_chan_scan_mode_bitmasks {
MWIFIEX_PASSIVE_SCAN = BIT(0),
MWIFIEX_DISABLE_CHAN_FILT = BIT(1),
+ MWIFIEX_HIDDEN_SSID_REPORT = BIT(4),
};
struct mwifiex_chan_scan_param_set {
@@ -1131,6 +1159,13 @@ struct host_cmd_ds_tx_rate_query {
u8 ht_info;
} __packed;
+struct mwifiex_tx_pause_tlv {
+ struct mwifiex_ie_types_header header;
+ u8 peermac[ETH_ALEN];
+ u8 tx_pause;
+ u8 pkt_cnt;
+} __packed;
+
enum Host_Sleep_Action {
HS_CONFIGURE = 0x0001,
HS_ACTIVATE = 0x0002,
@@ -1249,6 +1284,36 @@ struct host_cmd_ds_tdls_oper {
u8 peer_mac[ETH_ALEN];
} __packed;
+struct mwifiex_tdls_config {
+ __le16 enable;
+};
+
+struct mwifiex_tdls_config_cs_params {
+ u8 unit_time;
+ u8 thr_otherlink;
+ u8 thr_directlink;
+};
+
+struct mwifiex_tdls_init_cs_params {
+ u8 peer_mac[ETH_ALEN];
+ u8 primary_chan;
+ u8 second_chan_offset;
+ u8 band;
+ __le16 switch_time;
+ __le16 switch_timeout;
+ u8 reg_class;
+ u8 periodicity;
+} __packed;
+
+struct mwifiex_tdls_stop_cs_params {
+ u8 peer_mac[ETH_ALEN];
+};
+
+struct host_cmd_ds_tdls_config {
+ __le16 tdls_action;
+ u8 tdls_data[1];
+} __packed;
+
struct mwifiex_chan_desc {
__le16 start_freq;
u8 chan_width;
@@ -1370,6 +1435,11 @@ struct host_cmd_ds_802_11_scan_ext {
u8 tlv_buffer[1];
} __packed;
+struct mwifiex_ie_types_bss_mode {
+ struct mwifiex_ie_types_header header;
+ u8 bss_mode;
+} __packed;
+
struct mwifiex_ie_types_bss_scan_rsp {
struct mwifiex_ie_types_header header;
u8 bssid[ETH_ALEN];
@@ -1908,6 +1978,12 @@ struct mwifiex_radar_det_event {
__le32 passed;
} __packed;
+struct mwifiex_ie_types_multi_chan_info {
+ struct mwifiex_ie_types_header header;
+ __le16 status;
+ u8 tlv_buffer[0];
+} __packed;
+
struct meas_rpt_map {
u8 rssi:3;
u8 unmeasured:1;
@@ -1927,10 +2003,18 @@ struct host_cmd_ds_802_11_subsc_evt {
__le16 events;
} __packed;
+struct chan_switch_result {
+ u8 cur_chan;
+ u8 status;
+ u8 reason;
+} __packed;
+
struct mwifiex_tdls_generic_event {
__le16 type;
u8 peer_mac[ETH_ALEN];
union {
+ struct chan_switch_result switch_result;
+ u8 cs_stop_reason;
__le16 reason_code;
__le16 reserved;
} u;
@@ -1971,6 +2055,11 @@ struct host_cmd_ds_coalesce_cfg {
struct coalesce_receive_filt_rule rule[0];
} __packed;
+struct host_cmd_ds_multi_chan_policy {
+ __le16 action;
+ __le16 policy;
+} __packed;
+
struct host_cmd_ds_command {
__le16 command;
__le16 size;
@@ -2035,9 +2124,11 @@ struct host_cmd_ds_command {
struct host_cmd_ds_sta_list sta_list;
struct host_cmd_11ac_vht_cfg vht_cfg;
struct host_cmd_ds_coalesce_cfg coalesce_cfg;
+ struct host_cmd_ds_tdls_config tdls_config;
struct host_cmd_ds_tdls_oper tdls_oper;
struct host_cmd_ds_chan_rpt_req chan_rpt_req;
struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg;
+ struct host_cmd_ds_multi_chan_policy mc_policy;
} params;
} __packed;
diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c
index 0ba894509..abf52d25b 100644
--- a/drivers/net/wireless/mwifiex/ie.c
+++ b/drivers/net/wireless/mwifiex/ie.c
@@ -409,6 +409,8 @@ int mwifiex_set_mgmt_ies(struct mwifiex_private *priv,
int ret;
ret = mwifiex_uap_parse_tail_ies(priv, info);
+
+ if (ret)
return ret;
return mwifiex_set_mgmt_beacon_data_ies(priv, info);
@@ -477,6 +479,7 @@ int mwifiex_del_mgmt_ies(struct mwifiex_private *priv)
ar_ie, &priv->assocresp_idx);
done:
+ kfree(gen_ie);
kfree(beacon_ie);
kfree(pr_ie);
kfree(ar_ie);
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index df7fdc09d..5d3ae63ba 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -77,7 +77,7 @@ int mwifiex_init_priv(struct mwifiex_private *priv)
priv->media_connected = false;
eth_broadcast_addr(priv->curr_addr);
-
+ priv->port_open = false;
priv->pkt_tx_ctrl = 0;
priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
priv->data_rate = 0; /* Initially indicate the rate as auto */
@@ -301,7 +301,7 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter)
adapter->iface_limit.sta_intf = MWIFIEX_MAX_STA_NUM;
adapter->iface_limit.uap_intf = MWIFIEX_MAX_UAP_NUM;
adapter->iface_limit.p2p_intf = MWIFIEX_MAX_P2P_NUM;
-
+ adapter->active_scan_triggered = false;
setup_timer(&adapter->wakeup_timer, wakeup_timer_fn,
(unsigned long)adapter);
}
@@ -499,6 +499,7 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter)
INIT_LIST_HEAD(&priv->sta_list);
INIT_LIST_HEAD(&priv->auto_tdls_list);
skb_queue_head_init(&priv->tdls_txq);
+ skb_queue_head_init(&priv->bypass_txq);
spin_lock_init(&priv->tx_ba_stream_tbl_lock);
spin_lock_init(&priv->rx_reorder_tbl_lock);
@@ -550,11 +551,6 @@ int mwifiex_init_fw(struct mwifiex_adapter *adapter)
}
}
- 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,
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
index 56b024a6a..3cda1f956 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/mwifiex/join.c
@@ -783,6 +783,8 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled)
priv->scan_block = true;
+ else
+ priv->port_open = true;
done:
/* Need to indicate IOCTL complete */
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
index d501b4780..a94fd0f4b 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -276,6 +276,7 @@ process_start:
!adapter->pm_wakeup_fw_try) &&
(is_command_pending(adapter) ||
!skb_queue_empty(&adapter->tx_data_q) ||
+ !mwifiex_bypass_txlist_empty(adapter) ||
!mwifiex_wmm_lists_empty(adapter))) {
adapter->pm_wakeup_fw_try = true;
mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3));
@@ -299,9 +300,16 @@ process_start:
if ((!adapter->scan_chan_gap_enabled &&
adapter->scan_processing) || adapter->data_sent ||
+ mwifiex_is_tdls_chan_switching
+ (mwifiex_get_priv(adapter,
+ MWIFIEX_BSS_ROLE_STA)) ||
(mwifiex_wmm_lists_empty(adapter) &&
+ mwifiex_bypass_txlist_empty(adapter) &&
skb_queue_empty(&adapter->tx_data_q))) {
if (adapter->cmd_sent || adapter->curr_cmd ||
+ !mwifiex_is_send_cmd_allowed
+ (mwifiex_get_priv(adapter,
+ MWIFIEX_BSS_ROLE_STA)) ||
(!is_command_pending(adapter)))
break;
}
@@ -342,7 +350,9 @@ process_start:
continue;
}
- if (!adapter->cmd_sent && !adapter->curr_cmd) {
+ if (!adapter->cmd_sent && !adapter->curr_cmd &&
+ mwifiex_is_send_cmd_allowed
+ (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
if (mwifiex_exec_next_cmd(adapter) == -1) {
ret = -1;
break;
@@ -365,7 +375,25 @@ process_start:
if ((adapter->scan_chan_gap_enabled ||
!adapter->scan_processing) &&
- !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter)) {
+ !adapter->data_sent &&
+ !mwifiex_bypass_txlist_empty(adapter) &&
+ !mwifiex_is_tdls_chan_switching
+ (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
+ mwifiex_process_bypass_tx(adapter);
+ if (adapter->hs_activated) {
+ adapter->is_hs_configured = false;
+ mwifiex_hs_activated_event
+ (mwifiex_get_priv
+ (adapter, MWIFIEX_BSS_ROLE_ANY),
+ false);
+ }
+ }
+
+ if ((adapter->scan_chan_gap_enabled ||
+ !adapter->scan_processing) &&
+ !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter) &&
+ !mwifiex_is_tdls_chan_switching
+ (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) {
mwifiex_wmm_process_tx(adapter);
if (adapter->hs_activated) {
adapter->is_hs_configured = false;
@@ -379,6 +407,7 @@ process_start:
if (adapter->delay_null_pkt && !adapter->cmd_sent &&
!adapter->curr_cmd && !is_command_pending(adapter) &&
(mwifiex_wmm_lists_empty(adapter) &&
+ mwifiex_bypass_txlist_empty(adapter) &&
skb_queue_empty(&adapter->tx_data_q))) {
if (!mwifiex_send_null_packet
(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
@@ -649,6 +678,26 @@ mwifiex_close(struct net_device *dev)
return 0;
}
+static bool
+mwifiex_bypass_tx_queue(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
+
+ if (ntohs(eth_hdr->h_proto) == ETH_P_PAE ||
+ mwifiex_is_skb_mgmt_frame(skb) ||
+ (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA &&
+ ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ (ntohs(eth_hdr->h_proto) == ETH_P_TDLS))) {
+ mwifiex_dbg(priv->adapter, DATA,
+ "bypass txqueue; eth type %#x, mgmt %d\n",
+ ntohs(eth_hdr->h_proto),
+ mwifiex_is_skb_mgmt_frame(skb));
+ return true;
+ }
+
+ return false;
+}
/*
* Add buffer into wmm tx queue and queue work to transmit it.
*/
@@ -666,8 +715,14 @@ int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb)
}
}
- atomic_inc(&priv->adapter->tx_pending);
- mwifiex_wmm_add_buf_txqueue(priv, skb);
+ if (mwifiex_bypass_tx_queue(priv, skb)) {
+ atomic_inc(&priv->adapter->tx_pending);
+ atomic_inc(&priv->adapter->bypass_tx_pending);
+ mwifiex_wmm_add_buf_bypass_txqueue(priv, skb);
+ } else {
+ atomic_inc(&priv->adapter->tx_pending);
+ mwifiex_wmm_add_buf_txqueue(priv, skb);
+ }
mwifiex_queue_main_work(priv->adapter);
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index ae98b5b83..6b9512140 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -281,6 +281,7 @@ struct mwifiex_ra_list_tbl {
u8 amsdu_in_ampdu;
u16 total_pkt_count;
bool tdls_link;
+ bool tx_paused;
};
struct mwifiex_tid_tbl {
@@ -294,6 +295,7 @@ struct mwifiex_tid_tbl {
struct mwifiex_wmm_desc {
struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID];
u32 packets_out[MAX_NUM_TID];
+ u32 pkts_paused[MAX_NUM_TID];
/* spin lock to protect ra_list */
spinlock_t ra_list_spinlock;
struct mwifiex_wmm_ac_status ac_status[IEEE80211_NUM_ACS];
@@ -517,6 +519,7 @@ struct mwifiex_private {
u8 frame_type;
u8 curr_addr[ETH_ALEN];
u8 media_connected;
+ u8 port_open;
u32 num_tx_timeout;
/* track consecutive timeout */
u8 tx_timeout_cnt;
@@ -662,6 +665,8 @@ struct mwifiex_private {
struct cfg80211_beacon_data beacon_after;
struct mwifiex_11h_intf_state state_11h;
struct mwifiex_ds_mem_rw mem_rw;
+ struct sk_buff_head bypass_txq;
+ struct mwifiex_user_scan_chan hidden_chan[MWIFIEX_USER_SCAN_CHAN_MAX];
};
@@ -768,6 +773,7 @@ struct mwifiex_sta_node {
u8 tdls_status;
struct mwifiex_tdls_capab tdls_cap;
struct mwifiex_station_stats stats;
+ u8 tx_pause;
};
struct mwifiex_auto_tdls_peer {
@@ -831,6 +837,7 @@ struct mwifiex_adapter {
wait_queue_head_t init_wait_q;
void *card;
struct mwifiex_if_ops if_ops;
+ atomic_t bypass_tx_pending;
atomic_t rx_pending;
atomic_t tx_pending;
atomic_t cmd_pending;
@@ -979,6 +986,8 @@ struct mwifiex_adapter {
u8 coex_win_size;
u8 coex_tx_win_size;
u8 coex_rx_win_size;
+ bool drcs_enabled;
+ u8 active_scan_triggered;
};
void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter);
@@ -1330,6 +1339,21 @@ static inline u8 mwifiex_is_any_intf_active(struct mwifiex_private *priv)
return 0;
}
+static inline u8 mwifiex_is_tdls_link_setup(u8 status)
+{
+ switch (status) {
+ case TDLS_SETUP_COMPLETE:
+ case TDLS_CHAN_SWITCHING:
+ case TDLS_IN_BASE_CHAN:
+ case TDLS_IN_OFF_CHAN:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
int mwifiex_init_shutdown_fw(struct mwifiex_private *priv,
u32 func_init_shutdown);
int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8);
@@ -1458,6 +1482,9 @@ struct mwifiex_sta_node *
mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac);
struct mwifiex_sta_node *
mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac);
+u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv);
+u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv);
+u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv);
int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
u8 action_code, u8 dialog_token,
u16 status_code, const u8 *extra_ies,
@@ -1488,6 +1515,13 @@ void mwifiex_check_auto_tdls(unsigned long context);
void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac);
void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv);
void mwifiex_clean_auto_tdls(struct mwifiex_private *priv);
+int mwifiex_config_tdls_enable(struct mwifiex_private *priv);
+int mwifiex_config_tdls_disable(struct mwifiex_private *priv);
+int mwifiex_config_tdls_cs_params(struct mwifiex_private *priv);
+int mwifiex_stop_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac);
+int mwifiex_start_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac,
+ u8 primary_chan, u8 second_chan_offset, u8 band);
+
int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
void *data_buf);
@@ -1522,6 +1556,12 @@ void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags);
void mwifiex_queue_main_work(struct mwifiex_adapter *adapter);
void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter);
void mwifiex_11n_delba(struct mwifiex_private *priv, int tid);
+int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy);
+void mwifiex_process_tx_pause_event(struct mwifiex_private *priv,
+ struct sk_buff *event);
+void mwifiex_process_multi_chan_event(struct mwifiex_private *priv,
+ struct sk_buff *event_skb);
+
#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 01210fb0f..d186d2864 100644
--- a/drivers/net/wireless/mwifiex/pcie.c
+++ b/drivers/net/wireless/mwifiex/pcie.c
@@ -266,12 +266,17 @@ static const struct pci_device_id mwifiex_ids[] = {
{
PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- .driver_data = (unsigned long) &mwifiex_pcie8766,
+ .driver_data = (unsigned long)&mwifiex_pcie8766,
},
{
PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8897,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- .driver_data = (unsigned long) &mwifiex_pcie8897,
+ .driver_data = (unsigned long)&mwifiex_pcie8897,
+ },
+ {
+ PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8997,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ .driver_data = (unsigned long)&mwifiex_pcie8997,
},
{},
};
@@ -1082,6 +1087,7 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter)
card->txbd_rdptr++;
break;
case PCIE_DEVICE_ID_MARVELL_88W8897:
+ case PCIE_DEVICE_ID_MARVELL_88W8997:
card->txbd_rdptr += reg->ring_tx_start_ptr;
break;
}
@@ -1179,6 +1185,7 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb,
card->txbd_wrptr++;
break;
case PCIE_DEVICE_ID_MARVELL_88W8897:
+ case PCIE_DEVICE_ID_MARVELL_88W8997:
card->txbd_wrptr += reg->ring_tx_start_ptr;
break;
}
@@ -1807,6 +1814,7 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter,
if (!card->evt_buf_list[rdptr]) {
skb_push(skb, INTF_HEADER_LEN);
+ skb_put(skb, MAX_EVENT_SIZE - skb->len);
if (mwifiex_map_pci_memory(adapter, skb,
MAX_EVENT_SIZE,
PCI_DMA_FROMDEVICE))
diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h
index d97ff27f6..56633c9f0 100644
--- a/drivers/net/wireless/mwifiex/pcie.h
+++ b/drivers/net/wireless/mwifiex/pcie.h
@@ -30,10 +30,12 @@
#define PCIE8766_DEFAULT_FW_NAME "/*(DEBLOBBED)*/"
#define PCIE8897_DEFAULT_FW_NAME "/*(DEBLOBBED)*/"
+#define PCIE8997_DEFAULT_FW_NAME "/*(DEBLOBBED)*/"
#define PCIE_VENDOR_ID_MARVELL (0x11ab)
#define PCIE_DEVICE_ID_MARVELL_88W8766P (0x2b30)
#define PCIE_DEVICE_ID_MARVELL_88W8897 (0x2b38)
+#define PCIE_DEVICE_ID_MARVELL_88W8997 (0x2b42)
/* Constants for Buffer Descriptor (BD) rings */
#define MWIFIEX_MAX_TXRX_BD 0x20
@@ -197,7 +199,38 @@ static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = {
.sleep_cookie = 0,
.fw_dump_ctrl = 0xcf4,
.fw_dump_start = 0xcf8,
- .fw_dump_end = 0xcff
+ .fw_dump_end = 0xcff,
+};
+
+static const struct mwifiex_pcie_card_reg mwifiex_reg_8997 = {
+ .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 = 0xC1A4,
+ .tx_wrptr = 0xC1A8,
+ .rx_rdptr = 0xC1A8,
+ .rx_wrptr = 0xC1A4,
+ .evt_rdptr = PCIE_SCRATCH_10_REG,
+ .evt_wrptr = PCIE_SCRATCH_11_REG,
+ .drv_rdy = PCIE_SCRATCH_12_REG,
+ .tx_start_ptr = 16,
+ .tx_mask = 0x0FFF0000,
+ .tx_wrap_mask = 0x01FF0000,
+ .rx_mask = 0x00000FFF,
+ .rx_wrap_mask = 0x000001FF,
+ .tx_rollover_ind = BIT(28),
+ .rx_rollover_ind = BIT(12),
+ .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,
};
struct mwifiex_pcie_device {
@@ -227,6 +260,15 @@ static const struct mwifiex_pcie_device mwifiex_pcie8897 = {
.can_ext_scan = true,
};
+static const struct mwifiex_pcie_device mwifiex_pcie8997 = {
+ .firmware = PCIE8997_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_8997,
+ .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
+ .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
+ .can_dump_fw = false,
+ .can_ext_scan = true,
+};
+
struct mwifiex_evt_buf_desc {
u64 paddr;
u16 len;
@@ -325,6 +367,7 @@ mwifiex_pcie_txbd_not_full(struct pcie_service_card *card)
return 1;
break;
case PCIE_DEVICE_ID_MARVELL_88W8897:
+ case PCIE_DEVICE_ID_MARVELL_88W8997:
if (((card->txbd_wrptr & reg->tx_mask) !=
(card->txbd_rdptr & reg->tx_mask)) ||
((card->txbd_wrptr & reg->tx_rollover_ind) ==
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index baf9715dd..278ec477f 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/mwifiex/scan.c
@@ -527,7 +527,8 @@ mwifiex_scan_create_channel_list(struct mwifiex_private *priv,
if (ch->flags & IEEE80211_CHAN_NO_IR)
scan_chan_list[chan_idx].chan_scan_mode_bitmap
- |= MWIFIEX_PASSIVE_SCAN;
+ |= (MWIFIEX_PASSIVE_SCAN |
+ MWIFIEX_HIDDEN_SSID_REPORT);
else
scan_chan_list[chan_idx].chan_scan_mode_bitmap
&= ~MWIFIEX_PASSIVE_SCAN;
@@ -823,6 +824,7 @@ mwifiex_config_scan(struct mwifiex_private *priv,
int i;
u8 ssid_filter;
struct mwifiex_ie_types_htcap *ht_cap;
+ struct mwifiex_ie_types_bss_mode *bss_mode;
/* The tlv_buf_len is calculated for each scan command. The TLVs added
in this routine will be preserved since the routine that sends the
@@ -908,6 +910,10 @@ mwifiex_config_scan(struct mwifiex_private *priv,
wildcard_ssid_tlv->max_ssid_length =
IEEE80211_MAX_SSID_LEN;
+ if (!memcmp(user_scan_in->ssid_list[i].ssid,
+ "DIRECT-", 7))
+ wildcard_ssid_tlv->max_ssid_length = 0xfe;
+
memcpy(wildcard_ssid_tlv->ssid,
user_scan_in->ssid_list[i].ssid, ssid_len);
@@ -968,6 +974,15 @@ mwifiex_config_scan(struct mwifiex_private *priv,
else
*max_chan_per_scan = MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD;
+ if (adapter->ext_scan) {
+ bss_mode = (struct mwifiex_ie_types_bss_mode *)tlv_pos;
+ bss_mode->header.type = cpu_to_le16(TLV_TYPE_BSS_MODE);
+ bss_mode->header.len = cpu_to_le16(sizeof(bss_mode->bss_mode));
+ bss_mode->bss_mode = scan_cfg_out->bss_mode;
+ tlv_pos += sizeof(bss_mode->header) +
+ le16_to_cpu(bss_mode->header.len);
+ }
+
/* If the input config or adapter has the number of Probes set,
add tlv */
if (num_probes) {
@@ -1035,7 +1050,8 @@ mwifiex_config_scan(struct mwifiex_private *priv,
if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE)
(scan_chan_list +
chan_idx)->chan_scan_mode_bitmap
- |= MWIFIEX_PASSIVE_SCAN;
+ |= (MWIFIEX_PASSIVE_SCAN |
+ MWIFIEX_HIDDEN_SSID_REPORT);
else
(scan_chan_list +
chan_idx)->chan_scan_mode_bitmap
@@ -1586,6 +1602,62 @@ int mwifiex_check_network_compatibility(struct mwifiex_private *priv,
return ret;
}
+/* This function checks if SSID string contains all zeroes or length is zero */
+static bool mwifiex_is_hidden_ssid(struct cfg80211_ssid *ssid)
+{
+ int idx;
+
+ for (idx = 0; idx < ssid->ssid_len; idx++) {
+ if (ssid->ssid[idx])
+ return false;
+ }
+
+ return true;
+}
+
+/* This function checks if any hidden SSID found in passive scan channels
+ * and save those channels for specific SSID active scan
+ */
+static int mwifiex_save_hidden_ssid_channels(struct mwifiex_private *priv,
+ struct cfg80211_bss *bss)
+{
+ struct mwifiex_bssdescriptor *bss_desc;
+ int ret;
+ int chid;
+
+ /* Allocate and fill new bss descriptor */
+ bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL);
+ if (!bss_desc)
+ return -ENOMEM;
+
+ ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc);
+ if (ret)
+ goto done;
+
+ if (mwifiex_is_hidden_ssid(&bss_desc->ssid)) {
+ mwifiex_dbg(priv->adapter, INFO, "found hidden SSID\n");
+ for (chid = 0 ; chid < MWIFIEX_USER_SCAN_CHAN_MAX; chid++) {
+ if (priv->hidden_chan[chid].chan_number ==
+ bss->channel->hw_value)
+ break;
+
+ if (!priv->hidden_chan[chid].chan_number) {
+ priv->hidden_chan[chid].chan_number =
+ bss->channel->hw_value;
+ priv->hidden_chan[chid].radio_type =
+ bss->channel->band;
+ priv->hidden_chan[chid].scan_type =
+ MWIFIEX_SCAN_TYPE_ACTIVE;
+ break;
+ }
+ }
+ }
+
+done:
+ kfree(bss_desc);
+ return 0;
+}
+
static int mwifiex_update_curr_bss_params(struct mwifiex_private *priv,
struct cfg80211_bss *bss)
{
@@ -1775,6 +1847,14 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info,
.mac_address, ETH_ALEN))
mwifiex_update_curr_bss_params(priv, bss);
cfg80211_put_bss(priv->wdev.wiphy, bss);
+
+ if ((chan->flags & IEEE80211_CHAN_RADAR) ||
+ (chan->flags & IEEE80211_CHAN_NO_IR)) {
+ mwifiex_dbg(adapter, INFO,
+ "radar or passive channel %d\n",
+ channel);
+ mwifiex_save_hidden_ssid_channels(priv, bss);
+ }
}
} else {
mwifiex_dbg(adapter, WARN, "missing BSS channel IE\n");
@@ -1798,6 +1878,57 @@ static void mwifiex_complete_scan(struct mwifiex_private *priv)
}
}
+/* This function checks if any hidden SSID found in passive scan channels
+ * and do specific SSID active scan for those channels
+ */
+static int
+mwifiex_active_scan_req_for_passive_chan(struct mwifiex_private *priv)
+{
+ int ret;
+ struct mwifiex_adapter *adapter = priv->adapter;
+ u8 id = 0;
+ struct mwifiex_user_scan_cfg *user_scan_cfg;
+
+ if (adapter->active_scan_triggered || !priv->scan_request) {
+ adapter->active_scan_triggered = false;
+ return 0;
+ }
+
+ if (!priv->hidden_chan[0].chan_number) {
+ mwifiex_dbg(adapter, INFO, "No BSS with hidden SSID found on DFS channels\n");
+ return 0;
+ }
+ user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL);
+
+ if (!user_scan_cfg)
+ return -ENOMEM;
+
+ memset(user_scan_cfg, 0, sizeof(*user_scan_cfg));
+
+ for (id = 0; id < MWIFIEX_USER_SCAN_CHAN_MAX; id++) {
+ if (!priv->hidden_chan[id].chan_number)
+ break;
+ memcpy(&user_scan_cfg->chan_list[id],
+ &priv->hidden_chan[id],
+ sizeof(struct mwifiex_user_scan_chan));
+ }
+
+ adapter->active_scan_triggered = true;
+ user_scan_cfg->num_ssids = priv->scan_request->n_ssids;
+ user_scan_cfg->ssid_list = priv->scan_request->ssids;
+
+ ret = mwifiex_scan_networks(priv, user_scan_cfg);
+ kfree(user_scan_cfg);
+
+ memset(&priv->hidden_chan, 0, sizeof(priv->hidden_chan));
+
+ if (ret) {
+ dev_err(priv->adapter->dev, "scan failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
static void mwifiex_check_next_scan_command(struct mwifiex_private *priv)
{
struct mwifiex_adapter *adapter = priv->adapter;
@@ -1811,6 +1942,8 @@ static void mwifiex_check_next_scan_command(struct mwifiex_private *priv)
adapter->scan_processing = false;
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
+ mwifiex_active_scan_req_for_passive_chan(priv);
+
if (!adapter->ext_scan)
mwifiex_complete_scan(priv);
@@ -1837,15 +1970,17 @@ static void mwifiex_check_next_scan_command(struct mwifiex_private *priv)
adapter->scan_processing = false;
spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags);
- if (priv->scan_request) {
- mwifiex_dbg(adapter, INFO,
- "info: aborting scan\n");
- cfg80211_scan_done(priv->scan_request, 1);
- priv->scan_request = NULL;
- } else {
- priv->scan_aborting = false;
- mwifiex_dbg(adapter, INFO,
- "info: scan already aborted\n");
+ if (!adapter->active_scan_triggered) {
+ if (priv->scan_request) {
+ mwifiex_dbg(adapter, INFO,
+ "info: aborting scan\n");
+ cfg80211_scan_done(priv->scan_request, 1);
+ priv->scan_request = NULL;
+ } else {
+ priv->scan_aborting = false;
+ mwifiex_dbg(adapter, INFO,
+ "info: scan already aborted\n");
+ }
}
} else {
/* Get scan command from scan_pending_q and put to
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c
index 03098cc5f..6ab76e2b5 100644
--- a/drivers/net/wireless/mwifiex/sdio.c
+++ b/drivers/net/wireless/mwifiex/sdio.c
@@ -51,6 +51,10 @@ static unsigned long iface_work_flags;
static struct semaphore add_remove_card_sem;
+static struct memory_type_mapping generic_mem_type_map[] = {
+ {"DUMP", NULL, 0, 0xDD},
+};
+
static struct memory_type_mapping mem_type_mapping_tbl[] = {
{"ITCM", NULL, 0, 0xF0},
{"DTCM", NULL, 0, 0xF1},
@@ -91,6 +95,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
return -ENOMEM;
card->func = func;
+ card->device_id = id;
func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
@@ -107,6 +112,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size;
card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size;
card->can_dump_fw = data->can_dump_fw;
+ card->fw_dump_enh = data->fw_dump_enh;
card->can_auto_tdls = data->can_auto_tdls;
card->can_ext_scan = data->can_ext_scan;
}
@@ -287,6 +293,8 @@ static int mwifiex_sdio_suspend(struct device *dev)
#define SDIO_DEVICE_ID_MARVELL_8887 (0x9135)
/* Device ID for SD8801 */
#define SDIO_DEVICE_ID_MARVELL_8801 (0x9139)
+/* Device ID for SD8997 */
+#define SDIO_DEVICE_ID_MARVELL_8997 (0x9141)
/* WLAN IDs */
@@ -303,6 +311,8 @@ static const struct sdio_device_id mwifiex_ids[] = {
.driver_data = (unsigned long)&mwifiex_sdio_sd8887},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801),
.driver_data = (unsigned long)&mwifiex_sdio_sd8801},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997),
+ .driver_data = (unsigned long)&mwifiex_sdio_sd8997},
{},
};
@@ -910,6 +920,8 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
if (!fwbuf)
return -ENOMEM;
+ sdio_claim_host(card->func);
+
/* Perform firmware data transfer */
do {
/* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY
@@ -1014,6 +1026,8 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
offset += txlen;
} while (true);
+ sdio_release_host(card->func);
+
mwifiex_dbg(adapter, MSG,
"info: FW download over, size %d bytes\n", offset);
@@ -1964,8 +1978,13 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
adapter->dev = &func->dev;
strcpy(adapter->fw_name, card->firmware);
- adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
- adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
+ if (card->fw_dump_enh) {
+ adapter->mem_type_mapping_tbl = generic_mem_type_map;
+ adapter->num_mem_types = 1;
+ } else {
+ adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
+ adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
+ }
return 0;
}
@@ -2107,26 +2126,46 @@ mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port)
port, card->mp_data_port_mask);
}
+static void mwifiex_recreate_adapter(struct sdio_mmc_card *card)
+{
+ struct sdio_func *func = card->func;
+ const struct sdio_device_id *device_id = card->device_id;
+
+ /* TODO mmc_hw_reset does not require destroying and re-probing the
+ * whole adapter. Hence there was no need to for this rube-goldberg
+ * design to reload the fw from an external workqueue. If we don't
+ * destroy the adapter we could reload the fw from
+ * mwifiex_main_work_queue directly.
+ * The real difficulty with fw reset is to restore all the user
+ * settings applied through ioctl. By destroying and recreating the
+ * adapter, we take the easy way out, since we rely on user space to
+ * restore them. We assume that user space will treat the new
+ * incarnation of the adapter(interfaces) as if they had been just
+ * discovered and initializes them from scratch.
+ */
+
+ mwifiex_sdio_remove(func);
+
+ /* power cycle the adapter */
+ sdio_claim_host(func);
+ mmc_hw_reset(func->card->host);
+ sdio_release_host(func);
+
+ mwifiex_sdio_probe(func, device_id);
+}
+
static struct mwifiex_adapter *save_adapter;
static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter)
{
struct sdio_mmc_card *card = adapter->card;
- struct mmc_host *target = card->func->card->host;
-
- /* The actual reset operation must be run outside of driver thread.
- * This is because mmc_remove_host() will cause the device to be
- * instantly destroyed, and the driver then needs to end its thread,
- * leading to a deadlock.
- *
- * We run it in a totally independent workqueue.
- */
- mwifiex_dbg(adapter, WARN, "Resetting card...\n");
- mmc_remove_host(target);
- /* 200ms delay is based on experiment with sdhci controller */
- mdelay(200);
- target->rescan_entered = 0; /* rescan non-removable cards */
- mmc_add_host(target);
+ /* TODO card pointer is unprotected. If the adapter is removed
+ * physically, sdio core might trigger mwifiex_sdio_remove, before this
+ * workqueue is run, which will destroy the adapter struct. When this
+ * workqueue eventually exceutes it will dereference an invalid adapter
+ * pointer
+ */
+ mwifiex_recreate_adapter(card);
}
/* This function read/write firmware */
@@ -2138,8 +2177,8 @@ rdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter,
int ret, tries;
u8 ctrl_data = 0;
- sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl,
- &ret);
+ sdio_writeb(card->func, card->reg->fw_dump_host_ready,
+ card->reg->fw_dump_ctrl, &ret);
if (ret) {
mwifiex_dbg(adapter, ERROR, "SDIO Write ERR\n");
return RDWR_STATUS_FAILURE;
@@ -2155,10 +2194,10 @@ rdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter,
break;
if (doneflag && ctrl_data == doneflag)
return RDWR_STATUS_DONE;
- if (ctrl_data != FW_DUMP_HOST_READY) {
+ if (ctrl_data != card->reg->fw_dump_host_ready) {
mwifiex_dbg(adapter, WARN,
- "The ctrl reg was changed, re-try again!\n");
- sdio_writeb(card->func, FW_DUMP_HOST_READY,
+ "The ctrl reg was changed, re-try again\n");
+ sdio_writeb(card->func, card->reg->fw_dump_host_ready,
card->reg->fw_dump_ctrl, &ret);
if (ret) {
mwifiex_dbg(adapter, ERROR, "SDIO write err\n");
@@ -2167,7 +2206,7 @@ rdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter,
}
usleep_range(100, 200);
}
- if (ctrl_data == FW_DUMP_HOST_READY) {
+ if (ctrl_data == card->reg->fw_dump_host_ready) {
mwifiex_dbg(adapter, ERROR,
"Fail to pull ctrl_data\n");
return RDWR_STATUS_FAILURE;
@@ -2300,10 +2339,129 @@ done:
sdio_release_host(card->func);
}
+static void mwifiex_sdio_generic_fw_dump(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ struct memory_type_mapping *entry = &generic_mem_type_map[0];
+ unsigned int reg, reg_start, reg_end;
+ u8 start_flag = 0, done_flag = 0;
+ u8 *dbg_ptr, *end_ptr;
+ enum rdwr_status stat;
+ int ret = -1, tries;
+
+ if (!card->fw_dump_enh)
+ return;
+
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+
+ mwifiex_pm_wakeup_card(adapter);
+ sdio_claim_host(card->func);
+
+ mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n");
+
+ stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ reg_start = card->reg->fw_dump_start;
+ reg_end = card->reg->fw_dump_end;
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ start_flag = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "SDIO read err\n");
+ goto done;
+ }
+ if (start_flag == 0)
+ break;
+ if (tries == MAX_POLL_TRIES) {
+ mwifiex_dbg(adapter, ERROR,
+ "FW not ready to dump\n");
+ ret = -1;
+ goto done;
+ }
+ }
+ usleep_range(100, 200);
+ }
+
+ entry->mem_ptr = vmalloc(0xf0000 + 1);
+ if (!entry->mem_ptr) {
+ ret = -1;
+ goto done;
+ }
+ dbg_ptr = entry->mem_ptr;
+ entry->mem_size = 0xf0000;
+ end_ptr = dbg_ptr + entry->mem_size;
+
+ done_flag = entry->done_flag;
+ mwifiex_dbg(adapter, DUMP,
+ "Start %s output, please wait...\n", entry->mem_name);
+
+ while (true) {
+ stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ *dbg_ptr = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR,
+ "SDIO read err\n");
+ goto done;
+ }
+ dbg_ptr++;
+ if (dbg_ptr >= end_ptr) {
+ u8 *tmp_ptr;
+
+ tmp_ptr = vmalloc(entry->mem_size + 0x4000 + 1);
+ if (!tmp_ptr)
+ goto done;
+
+ memcpy(tmp_ptr, entry->mem_ptr,
+ entry->mem_size);
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = tmp_ptr;
+ tmp_ptr = NULL;
+ dbg_ptr = entry->mem_ptr + entry->mem_size;
+ entry->mem_size += 0x4000;
+ end_ptr = entry->mem_ptr + entry->mem_size;
+ }
+ }
+ if (stat == RDWR_STATUS_DONE) {
+ entry->mem_size = dbg_ptr - entry->mem_ptr;
+ mwifiex_dbg(adapter, DUMP, "dump %s done size=0x%x\n",
+ entry->mem_name, entry->mem_size);
+ ret = 0;
+ break;
+ }
+ }
+ mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n");
+
+done:
+ if (ret) {
+ mwifiex_dbg(adapter, ERROR, "firmware dump failed\n");
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+ }
+ sdio_release_host(card->func);
+}
+
static void mwifiex_sdio_device_dump_work(struct mwifiex_adapter *adapter)
{
+ struct sdio_mmc_card *card = adapter->card;
+
mwifiex_drv_info_dump(adapter);
- mwifiex_sdio_fw_dump(adapter);
+ if (card->fw_dump_enh)
+ mwifiex_sdio_generic_fw_dump(adapter);
+ else
+ mwifiex_sdio_fw_dump(adapter);
mwifiex_upload_device_dump(adapter);
}
diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h
index 06c956c0e..da70fde39 100644
--- a/drivers/net/wireless/mwifiex/sdio.h
+++ b/drivers/net/wireless/mwifiex/sdio.h
@@ -35,6 +35,7 @@
#define SD8897_DEFAULT_FW_NAME "/*(DEBLOBBED)*/"
#define SD8887_DEFAULT_FW_NAME "/*(DEBLOBBED)*/"
#define SD8801_DEFAULT_FW_NAME "/*(DEBLOBBED)*/"
+#define SD8997_DEFAULT_FW_NAME "/*(DEBLOBBED)*/"
#define BLOCK_MODE 1
#define BYTE_MODE 0
@@ -222,6 +223,7 @@ struct mwifiex_sdio_card_reg {
u8 cmd_cfg_1;
u8 cmd_cfg_2;
u8 cmd_cfg_3;
+ u8 fw_dump_host_ready;
u8 fw_dump_ctrl;
u8 fw_dump_start;
u8 fw_dump_end;
@@ -257,11 +259,15 @@ struct sdio_mmc_card {
bool supports_sdio_new_mode;
bool has_control_mask;
bool can_dump_fw;
+ bool fw_dump_enh;
bool can_auto_tdls;
bool can_ext_scan;
struct mwifiex_sdio_mpa_tx mpa_tx;
struct mwifiex_sdio_mpa_rx mpa_rx;
+
+ /* needed for card reset */
+ const struct sdio_device_id *device_id;
};
struct mwifiex_sdio_device {
@@ -275,6 +281,7 @@ struct mwifiex_sdio_device {
bool supports_sdio_new_mode;
bool has_control_mask;
bool can_dump_fw;
+ bool fw_dump_enh;
bool can_auto_tdls;
bool can_ext_scan;
};
@@ -350,6 +357,7 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = {
.cmd_cfg_1 = 0xb9,
.cmd_cfg_2 = 0xba,
.cmd_cfg_3 = 0xbb,
+ .fw_dump_host_ready = 0xee,
.fw_dump_ctrl = 0xe2,
.fw_dump_start = 0xe3,
.fw_dump_end = 0xea,
@@ -361,6 +369,59 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = {
0x59, 0x5c, 0x5d},
};
+static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8997 = {
+ .start_rd_port = 0,
+ .start_wr_port = 0,
+ .base_0_reg = 0xF8,
+ .base_1_reg = 0xF9,
+ .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 = 0xE8,
+ .status_reg_1 = 0xE9,
+ .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,
+ .fw_dump_host_ready = 0xcc,
+ .fw_dump_ctrl = 0xf0,
+ .fw_dump_start = 0xf1,
+ .fw_dump_end = 0xf8,
+ .func1_dump_reg_start = 0x10,
+ .func1_dump_reg_end = 0x17,
+ .func1_scratch_reg = 0xe8,
+ .func1_spec_reg_num = 13,
+ .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D,
+ 0x60, 0x61, 0x62, 0x64,
+ 0x65, 0x66, 0x68, 0x69,
+ 0x6a},
+};
+
static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8887 = {
.start_rd_port = 0,
.start_wr_port = 0,
@@ -469,6 +530,22 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
.can_ext_scan = true,
};
+static const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = {
+ .firmware = SD8997_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_sd8997,
+ .max_ports = 32,
+ .mp_agg_pkt_limit = 16,
+ .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
+ .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX,
+ .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX,
+ .supports_sdio_new_mode = true,
+ .has_control_mask = false,
+ .can_dump_fw = true,
+ .fw_dump_enh = true,
+ .can_auto_tdls = false,
+ .can_ext_scan = true,
+};
+
static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = {
.firmware = SD8887_DEFAULT_FW_NAME,
.reg = &mwifiex_reg_sd8887,
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c
index 037adcd1f..a49a80dd7 100644
--- a/drivers/net/wireless/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/mwifiex/sta_cmd.c
@@ -26,6 +26,10 @@
#include "11n.h"
#include "11ac.h"
+static bool drcs;
+module_param(drcs, bool, 0644);
+MODULE_PARM_DESC(drcs, "multi-channel operation:1, single-channel operation:0");
+
static bool disable_auto_ds;
module_param(disable_auto_ds, bool, 0);
MODULE_PARM_DESC(disable_auto_ds,
@@ -1512,6 +1516,22 @@ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv,
}
static int
+mwifiex_cmd_set_mc_policy(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_action, void *data_buf)
+{
+ struct host_cmd_ds_multi_chan_policy *mc_pol = &cmd->params.mc_policy;
+ const u16 *drcs_info = data_buf;
+
+ mc_pol->action = cpu_to_le16(cmd_action);
+ mc_pol->policy = cpu_to_le16(*drcs_info);
+ cmd->command = cpu_to_le16(HostCmd_CMD_MC_POLICY);
+ cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_multi_chan_policy) +
+ S_DS_GEN);
+ return 0;
+}
+
+static int
mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
u16 cmd_action, void *data_buf)
@@ -1576,6 +1596,50 @@ mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv,
}
static int
+mwifiex_cmd_tdls_config(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_action, void *data_buf)
+{
+ struct host_cmd_ds_tdls_config *tdls_config = &cmd->params.tdls_config;
+ struct mwifiex_tdls_init_cs_params *config;
+ struct mwifiex_tdls_config *init_config;
+ u16 len;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_CONFIG);
+ cmd->size = cpu_to_le16(S_DS_GEN);
+ tdls_config->tdls_action = cpu_to_le16(cmd_action);
+ le16_add_cpu(&cmd->size, sizeof(tdls_config->tdls_action));
+
+ switch (cmd_action) {
+ case ACT_TDLS_CS_ENABLE_CONFIG:
+ init_config = data_buf;
+ len = sizeof(*init_config);
+ memcpy(tdls_config->tdls_data, init_config, len);
+ break;
+ case ACT_TDLS_CS_INIT:
+ config = data_buf;
+ len = sizeof(*config);
+ memcpy(tdls_config->tdls_data, config, len);
+ break;
+ case ACT_TDLS_CS_STOP:
+ len = sizeof(struct mwifiex_tdls_stop_cs_params);
+ memcpy(tdls_config->tdls_data, data_buf, len);
+ break;
+ case ACT_TDLS_CS_PARAMS:
+ len = sizeof(struct mwifiex_tdls_config_cs_params);
+ memcpy(tdls_config->tdls_data, data_buf, len);
+ break;
+ default:
+ mwifiex_dbg(priv->adapter, ERROR,
+ "Unknown TDLS configuration\n");
+ return -ENOTSUPP;
+ }
+
+ le16_add_cpu(&cmd->size, len);
+ return 0;
+}
+
+static int
mwifiex_cmd_tdls_oper(struct mwifiex_private *priv,
struct host_cmd_ds_command *cmd,
void *data_buf)
@@ -1933,10 +1997,12 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
if (priv->bss_mode == NL80211_IFTYPE_ADHOC)
cmd_ptr->params.bss_mode.con_type =
CONNECTION_TYPE_ADHOC;
- else if (priv->bss_mode == NL80211_IFTYPE_STATION)
+ else if (priv->bss_mode == NL80211_IFTYPE_STATION ||
+ priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT)
cmd_ptr->params.bss_mode.con_type =
CONNECTION_TYPE_INFRA;
- else if (priv->bss_mode == NL80211_IFTYPE_AP)
+ else if (priv->bss_mode == NL80211_IFTYPE_AP ||
+ priv->bss_mode == NL80211_IFTYPE_P2P_GO)
cmd_ptr->params.bss_mode.con_type = CONNECTION_TYPE_AP;
cmd_ptr->size = cpu_to_le16(sizeof(struct
host_cmd_ds_set_bss_mode) + S_DS_GEN);
@@ -1958,6 +2024,10 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
case HostCmd_CMD_TDLS_OPER:
ret = mwifiex_cmd_tdls_oper(priv, cmd_ptr, data_buf);
break;
+ case HostCmd_CMD_TDLS_CONFIG:
+ ret = mwifiex_cmd_tdls_config(priv, cmd_ptr, cmd_action,
+ data_buf);
+ break;
case HostCmd_CMD_CHAN_REPORT_REQUEST:
ret = mwifiex_cmd_issue_chan_report_request(priv, cmd_ptr,
data_buf);
@@ -1966,6 +2036,10 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
ret = mwifiex_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action,
data_buf);
break;
+ case HostCmd_CMD_MC_POLICY:
+ ret = mwifiex_cmd_set_mc_policy(priv, cmd_ptr, cmd_action,
+ data_buf);
+ break;
default:
mwifiex_dbg(priv->adapter, ERROR,
"PREP_CMD: unknown cmd- %#x\n", cmd_no);
@@ -2082,6 +2156,18 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init)
if (ret)
return -1;
}
+
+ if (drcs) {
+ adapter->drcs_enabled = true;
+ if (ISSUPP_DRCS_ENABLED(adapter->fw_cap_info))
+ ret = mwifiex_send_cmd(priv,
+ HostCmd_CMD_MC_POLICY,
+ HostCmd_ACT_GEN_SET, 0,
+ &adapter->drcs_enabled,
+ true);
+ if (ret)
+ return -1;
+ }
}
/* get tx rate */
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c
index b645884b3..87b69d8ad 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c
@@ -599,6 +599,7 @@ static int mwifiex_ret_802_11_key_material_v1(struct mwifiex_private *priv,
"info: key: GTK is set\n");
priv->wpa_is_gtk_set = true;
priv->scan_block = false;
+ priv->port_open = true;
}
}
@@ -629,6 +630,7 @@ static int mwifiex_ret_802_11_key_material_v2(struct mwifiex_private *priv,
mwifiex_dbg(priv->adapter, INFO, "info: key: GTK is set\n");
priv->wpa_is_gtk_set = true;
priv->scan_block = false;
+ priv->port_open = true;
}
}
@@ -893,7 +895,7 @@ static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv,
case ACT_TDLS_DELETE:
if (reason) {
if (!node || reason == TDLS_ERR_LINK_NONEXISTENT)
- mwifiex_dbg(priv->adapter, ERROR,
+ mwifiex_dbg(priv->adapter, MSG,
"TDLS link delete for %pM failed: reason %d\n",
cmd_tdls_oper->peer_mac, reason);
else
@@ -1191,12 +1193,15 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
break;
case HostCmd_CMD_TDLS_OPER:
ret = mwifiex_ret_tdls_oper(priv, resp);
+ case HostCmd_CMD_MC_POLICY:
break;
case HostCmd_CMD_CHAN_REPORT_REQUEST:
break;
case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG:
ret = mwifiex_ret_sdio_rx_aggr_cfg(priv, resp);
break;
+ case HostCmd_CMD_TDLS_CONFIG:
+ break;
default:
mwifiex_dbg(adapter, ERROR,
"CMD_RESP: unknown cmd response %#x\n",
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c
index 848de2621..3d18c585e 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/mwifiex/sta_event.c
@@ -54,6 +54,7 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code)
priv->media_connected = false;
priv->scan_block = false;
+ priv->port_open = false;
if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) &&
ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) {
@@ -153,6 +154,7 @@ static int mwifiex_parse_tdls_event(struct mwifiex_private *priv,
struct mwifiex_sta_node *sta_ptr;
struct mwifiex_tdls_generic_event *tdls_evt =
(void *)event_skb->data + sizeof(adapter->event_cause);
+ u8 *mac = tdls_evt->peer_mac;
/* reserved 2 bytes are not mandatory in tdls event */
if (event_skb->len < (sizeof(struct mwifiex_tdls_generic_event) -
@@ -175,6 +177,59 @@ static int mwifiex_parse_tdls_event(struct mwifiex_private *priv,
le16_to_cpu(tdls_evt->u.reason_code),
GFP_KERNEL);
break;
+ case TDLS_EVENT_CHAN_SWITCH_RESULT:
+ mwifiex_dbg(adapter, EVENT, "tdls channel switch result :\n");
+ mwifiex_dbg(adapter, EVENT,
+ "status=0x%x, reason=0x%x cur_chan=%d\n",
+ tdls_evt->u.switch_result.status,
+ tdls_evt->u.switch_result.reason,
+ tdls_evt->u.switch_result.cur_chan);
+
+ /* tdls channel switch failed */
+ if (tdls_evt->u.switch_result.status != 0) {
+ switch (tdls_evt->u.switch_result.cur_chan) {
+ case TDLS_BASE_CHANNEL:
+ sta_ptr->tdls_status = TDLS_IN_BASE_CHAN;
+ break;
+ case TDLS_OFF_CHANNEL:
+ sta_ptr->tdls_status = TDLS_IN_OFF_CHAN;
+ break;
+ default:
+ break;
+ }
+ return ret;
+ }
+
+ /* tdls channel switch success */
+ switch (tdls_evt->u.switch_result.cur_chan) {
+ case TDLS_BASE_CHANNEL:
+ if (sta_ptr->tdls_status == TDLS_IN_BASE_CHAN)
+ break;
+ mwifiex_update_ralist_tx_pause_in_tdls_cs(priv, mac,
+ false);
+ sta_ptr->tdls_status = TDLS_IN_BASE_CHAN;
+ break;
+ case TDLS_OFF_CHANNEL:
+ if (sta_ptr->tdls_status == TDLS_IN_OFF_CHAN)
+ break;
+ mwifiex_update_ralist_tx_pause_in_tdls_cs(priv, mac,
+ true);
+ sta_ptr->tdls_status = TDLS_IN_OFF_CHAN;
+ break;
+ default:
+ break;
+ }
+
+ break;
+ case TDLS_EVENT_START_CHAN_SWITCH:
+ mwifiex_dbg(adapter, EVENT, "tdls start channel switch...\n");
+ sta_ptr->tdls_status = TDLS_CHAN_SWITCHING;
+ break;
+ case TDLS_EVENT_CHAN_SWITCH_STOPPED:
+ mwifiex_dbg(adapter, EVENT,
+ "tdls chan switch stopped, reason=%d\n",
+ tdls_evt->u.cs_stop_reason);
+ break;
default:
break;
}
@@ -182,6 +237,145 @@ static int mwifiex_parse_tdls_event(struct mwifiex_private *priv,
return ret;
}
+static void mwifiex_process_uap_tx_pause(struct mwifiex_private *priv,
+ struct mwifiex_ie_types_header *tlv)
+{
+ struct mwifiex_tx_pause_tlv *tp;
+ struct mwifiex_sta_node *sta_ptr;
+ unsigned long flags;
+
+ tp = (void *)tlv;
+ mwifiex_dbg(priv->adapter, EVENT,
+ "uap tx_pause: %pM pause=%d, pkts=%d\n",
+ tp->peermac, tp->tx_pause,
+ tp->pkt_cnt);
+
+ if (ether_addr_equal(tp->peermac, priv->netdev->dev_addr)) {
+ if (tp->tx_pause)
+ priv->port_open = false;
+ else
+ priv->port_open = true;
+ } else if (is_multicast_ether_addr(tp->peermac)) {
+ mwifiex_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause);
+ } else {
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+ if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) {
+ sta_ptr->tx_pause = tp->tx_pause;
+ mwifiex_update_ralist_tx_pause(priv, tp->peermac,
+ tp->tx_pause);
+ }
+ }
+}
+
+static void mwifiex_process_sta_tx_pause(struct mwifiex_private *priv,
+ struct mwifiex_ie_types_header *tlv)
+{
+ struct mwifiex_tx_pause_tlv *tp;
+ struct mwifiex_sta_node *sta_ptr;
+ int status;
+ unsigned long flags;
+
+ tp = (void *)tlv;
+ mwifiex_dbg(priv->adapter, EVENT,
+ "sta tx_pause: %pM pause=%d, pkts=%d\n",
+ tp->peermac, tp->tx_pause,
+ tp->pkt_cnt);
+
+ if (ether_addr_equal(tp->peermac, priv->cfg_bssid)) {
+ if (tp->tx_pause)
+ priv->port_open = false;
+ else
+ priv->port_open = true;
+ } else {
+ if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info))
+ return;
+
+ status = mwifiex_get_tdls_link_status(priv, tp->peermac);
+ if (mwifiex_is_tdls_link_setup(status)) {
+ spin_lock_irqsave(&priv->sta_list_spinlock, flags);
+ sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac);
+ spin_unlock_irqrestore(&priv->sta_list_spinlock, flags);
+
+ if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) {
+ sta_ptr->tx_pause = tp->tx_pause;
+ mwifiex_update_ralist_tx_pause(priv,
+ tp->peermac,
+ tp->tx_pause);
+ }
+ }
+ }
+}
+
+void mwifiex_process_multi_chan_event(struct mwifiex_private *priv,
+ struct sk_buff *event_skb)
+{
+ struct mwifiex_ie_types_multi_chan_info *chan_info;
+ u16 status;
+
+ chan_info = (void *)event_skb->data + sizeof(u32);
+
+ if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "unknown TLV in chan_info event\n");
+ return;
+ }
+
+ status = le16_to_cpu(chan_info->status);
+
+ if (status) {
+ mwifiex_dbg(priv->adapter, EVENT,
+ "multi-channel operation started\n");
+ } else {
+ mwifiex_dbg(priv->adapter, EVENT,
+ "multi-channel operation over\n");
+ }
+}
+
+void mwifiex_process_tx_pause_event(struct mwifiex_private *priv,
+ struct sk_buff *event_skb)
+{
+ struct mwifiex_ie_types_header *tlv;
+ u16 tlv_type, tlv_len;
+ int tlv_buf_left;
+
+ if (!priv->media_connected) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "tx_pause event while disconnected; bss_role=%d\n",
+ priv->bss_role);
+ return;
+ }
+
+ tlv_buf_left = event_skb->len - sizeof(u32);
+ tlv = (void *)event_skb->data + sizeof(u32);
+
+ while (tlv_buf_left >= (int)sizeof(struct mwifiex_ie_types_header)) {
+ tlv_type = le16_to_cpu(tlv->type);
+ tlv_len = le16_to_cpu(tlv->len);
+ if ((sizeof(struct mwifiex_ie_types_header) + tlv_len) >
+ tlv_buf_left) {
+ mwifiex_dbg(priv->adapter, ERROR,
+ "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n",
+ tlv_len, tlv_buf_left);
+ break;
+ }
+ if (tlv_type == TLV_TYPE_TX_PAUSE) {
+ if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)
+ mwifiex_process_sta_tx_pause(priv, tlv);
+ else
+ mwifiex_process_uap_tx_pause(priv, tlv);
+ }
+
+ tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) +
+ tlv_len;
+ tlv = (void *)((u8 *)tlv + tlv_len +
+ sizeof(struct mwifiex_ie_types_header));
+ }
+
+}
+
/*
* This function handles coex events generated by firmware
*/
@@ -359,7 +553,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
case EVENT_PS_AWAKE:
mwifiex_dbg(adapter, EVENT, "info: EVENT: AWAKE\n");
- if (!adapter->pps_uapsd_mode &&
+ if (!adapter->pps_uapsd_mode && priv->port_open &&
priv->media_connected && adapter->sleep_period.period) {
adapter->pps_uapsd_mode = true;
mwifiex_dbg(adapter, EVENT,
@@ -438,6 +632,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
case EVENT_PORT_RELEASE:
mwifiex_dbg(adapter, EVENT, "event: PORT RELEASE\n");
+ priv->port_open = true;
break;
case EVENT_EXT_SCAN_REPORT:
@@ -573,6 +768,16 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv)
ret = mwifiex_parse_tdls_event(priv, adapter->event_skb);
break;
+ case EVENT_TX_DATA_PAUSE:
+ mwifiex_dbg(adapter, EVENT, "event: TX DATA PAUSE\n");
+ mwifiex_process_tx_pause_event(priv, adapter->event_skb);
+ break;
+
+ case EVENT_MULTI_CHAN_INFO:
+ mwifiex_dbg(adapter, EVENT, "event: multi-chan info\n");
+ mwifiex_process_multi_chan_event(priv, adapter->event_skb);
+ break;
+
case EVENT_TX_STATUS_REPORT:
mwifiex_dbg(adapter, EVENT, "event: TX_STATUS Report\n");
mwifiex_parse_tx_status_event(priv, adapter->event_body);
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c
index d8b7d9c20..a6c8a4f7b 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/mwifiex/sta_ioctl.c
@@ -66,8 +66,8 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter,
if (status <= 0) {
if (status == 0)
status = -ETIMEDOUT;
- mwifiex_dbg(adapter, ERROR,
- "cmd_wait_q terminated: %d\n", status);
+ mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n",
+ status);
mwifiex_cancel_all_pending_cmd(adapter);
return status;
}
diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c
index 2faa1bc42..b3e163de9 100644
--- a/drivers/net/wireless/mwifiex/tdls.c
+++ b/drivers/net/wireless/mwifiex/tdls.c
@@ -49,7 +49,7 @@ static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv,
tid = skb->priority;
tid_down = mwifiex_wmm_downgrade_tid(priv, tid);
- if (status == TDLS_SETUP_COMPLETE) {
+ if (mwifiex_is_tdls_link_setup(status)) {
ra_list = mwifiex_wmm_get_queue_raptr(priv, tid, mac);
ra_list->tdls_link = true;
tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
@@ -164,7 +164,7 @@ static void mwifiex_tdls_add_aid(struct mwifiex_private *priv,
pos = (void *)skb_put(skb, 4);
*pos++ = WLAN_EID_AID;
*pos++ = 2;
- *pos++ = le16_to_cpu(assoc_rsp->a_id);
+ memcpy(pos, &assoc_rsp->a_id, sizeof(assoc_rsp->a_id));
return;
}
@@ -355,6 +355,7 @@ static void mwifiex_tdls_add_ext_capab(struct mwifiex_private *priv,
extcap->ieee_hdr.len = 8;
memset(extcap->ext_capab, 0, 8);
extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED;
+ extcap->ext_capab[3] |= WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH;
if (priv->adapter->is_hw_11ac_capable)
extcap->ext_capab[7] |= WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED;
@@ -1071,6 +1072,11 @@ mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer)
for (i = 0; i < MAX_NUM_TID; i++)
sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED;
}
+ if (sta_ptr->tdls_cap.extcap.ext_capab[3] &
+ WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) {
+ mwifiex_config_tdls_enable(priv);
+ mwifiex_config_tdls_cs_params(priv);
+ }
memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq));
mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE);
@@ -1141,7 +1147,7 @@ int mwifiex_get_tdls_list(struct mwifiex_private *priv,
spin_lock_irqsave(&priv->sta_list_spinlock, flags);
list_for_each_entry(sta_ptr, &priv->sta_list, list) {
- if (sta_ptr->tdls_status == TDLS_SETUP_COMPLETE) {
+ if (mwifiex_is_tdls_link_setup(sta_ptr->tdls_status)) {
ether_addr_copy(peer->peer_addr, sta_ptr->mac_addr);
peer++;
count++;
@@ -1295,7 +1301,7 @@ void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv,
if ((link_status == TDLS_NOT_SETUP) &&
(peer->tdls_status == TDLS_SETUP_INPROGRESS))
peer->failure_count++;
- else if (link_status == TDLS_SETUP_COMPLETE)
+ else if (mwifiex_is_tdls_link_setup(link_status))
peer->failure_count = 0;
peer->tdls_status = link_status;
@@ -1367,7 +1373,7 @@ void mwifiex_check_auto_tdls(unsigned long context)
if (((tdls_peer->rssi >= MWIFIEX_TDLS_RSSI_LOW) ||
!tdls_peer->rssi) &&
- tdls_peer->tdls_status == TDLS_SETUP_COMPLETE) {
+ mwifiex_is_tdls_link_setup(tdls_peer->tdls_status)) {
tdls_peer->tdls_status = TDLS_LINK_TEARDOWN;
mwifiex_dbg(priv->adapter, MSG,
"teardown TDLS link,peer=%pM rssi=%d\n",
@@ -1416,3 +1422,67 @@ void mwifiex_clean_auto_tdls(struct mwifiex_private *priv)
mwifiex_flush_auto_tdls_list(priv);
}
}
+
+static int mwifiex_config_tdls(struct mwifiex_private *priv, u8 enable)
+{
+ struct mwifiex_tdls_config config;
+
+ config.enable = cpu_to_le16(enable);
+ return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG,
+ ACT_TDLS_CS_ENABLE_CONFIG, 0, &config, true);
+}
+
+int mwifiex_config_tdls_enable(struct mwifiex_private *priv)
+{
+ return mwifiex_config_tdls(priv, true);
+}
+
+int mwifiex_config_tdls_disable(struct mwifiex_private *priv)
+{
+ return mwifiex_config_tdls(priv, false);
+}
+
+int mwifiex_config_tdls_cs_params(struct mwifiex_private *priv)
+{
+ struct mwifiex_tdls_config_cs_params config_tdls_cs_params;
+
+ config_tdls_cs_params.unit_time = MWIFIEX_DEF_CS_UNIT_TIME;
+ config_tdls_cs_params.thr_otherlink = MWIFIEX_DEF_CS_THR_OTHERLINK;
+ config_tdls_cs_params.thr_directlink = MWIFIEX_DEF_THR_DIRECTLINK;
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG,
+ ACT_TDLS_CS_PARAMS, 0,
+ &config_tdls_cs_params, true);
+}
+
+int mwifiex_stop_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac)
+{
+ struct mwifiex_tdls_stop_cs_params stop_tdls_cs_params;
+
+ ether_addr_copy(stop_tdls_cs_params.peer_mac, peer_mac);
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG,
+ ACT_TDLS_CS_STOP, 0,
+ &stop_tdls_cs_params, true);
+}
+
+int mwifiex_start_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac,
+ u8 primary_chan, u8 second_chan_offset, u8 band)
+{
+ struct mwifiex_tdls_init_cs_params start_tdls_cs_params;
+
+ ether_addr_copy(start_tdls_cs_params.peer_mac, peer_mac);
+ start_tdls_cs_params.primary_chan = primary_chan;
+ start_tdls_cs_params.second_chan_offset = second_chan_offset;
+ start_tdls_cs_params.band = band;
+
+ start_tdls_cs_params.switch_time = cpu_to_le16(MWIFIEX_DEF_CS_TIME);
+ start_tdls_cs_params.switch_timeout =
+ cpu_to_le16(MWIFIEX_DEF_CS_TIMEOUT);
+ start_tdls_cs_params.reg_class = MWIFIEX_DEF_CS_REG_CLASS;
+ start_tdls_cs_params.periodicity = MWIFIEX_DEF_CS_PERIODICITY;
+
+ return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG,
+ ACT_TDLS_CS_INIT, 0,
+ &start_tdls_cs_params, true);
+}
diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c
index 5ed9b7940..8b1e5b5d4 100644
--- a/drivers/net/wireless/mwifiex/txrx.c
+++ b/drivers/net/wireless/mwifiex/txrx.c
@@ -370,8 +370,28 @@ void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
/* consumes ack_skb */
skb_complete_wifi_ack(ack_skb, !tx_status->status);
} else {
+ /* Remove broadcast address which was added by driver */
+ memmove(ack_skb->data +
+ sizeof(struct ieee80211_hdr_3addr) +
+ MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16),
+ ack_skb->data +
+ sizeof(struct ieee80211_hdr_3addr) +
+ MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16) +
+ ETH_ALEN, ack_skb->len -
+ (sizeof(struct ieee80211_hdr_3addr) +
+ MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16) +
+ ETH_ALEN));
+ ack_skb->len = ack_skb->len - ETH_ALEN;
+ /* Remove driver's proprietary header including 2 bytes
+ * of packet length and pass actual management frame buffer
+ * to cfg80211.
+ */
cfg80211_mgmt_tx_status(&priv->wdev, tx_info->cookie,
- ack_skb->data, ack_skb->len,
+ ack_skb->data +
+ MWIFIEX_MGMT_FRAME_HEADER_SIZE +
+ sizeof(u16), ack_skb->len -
+ (MWIFIEX_MGMT_FRAME_HEADER_SIZE
+ + sizeof(u16)),
!tx_status->status, GFP_ATOMIC);
dev_kfree_skb_any(ack_skb);
}
diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c
index b74930054..4d5a6e3b6 100644
--- a/drivers/net/wireless/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/mwifiex/uap_cmd.c
@@ -808,7 +808,7 @@ void mwifiex_uap_set_channel(struct mwifiex_private *priv,
struct mwifiex_uap_bss_param *bss_cfg,
struct cfg80211_chan_def chandef)
{
- u8 config_bands = 0;
+ u8 config_bands = 0, old_bands = priv->adapter->config_bands;
priv->bss_chandef = chandef;
@@ -834,6 +834,11 @@ void mwifiex_uap_set_channel(struct mwifiex_private *priv,
}
priv->adapter->config_bands = config_bands;
+
+ if (old_bands != config_bands) {
+ mwifiex_send_domain_info_cmd_fw(priv->adapter->wiphy);
+ mwifiex_dnld_txpwr_table(priv);
+ }
}
int mwifiex_config_start_uap(struct mwifiex_private *priv,
diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c
index 7bc1f850e..46c972a65 100644
--- a/drivers/net/wireless/mwifiex/uap_event.c
+++ b/drivers/net/wireless/mwifiex/uap_event.c
@@ -41,6 +41,8 @@ static int mwifiex_check_uap_capabilties(struct mwifiex_private *priv,
mwifiex_dbg_dump(priv->adapter, EVT_D, "uap capabilties:",
event->data, event->len);
+ skb_push(event, MWIFIEX_BSS_START_EVT_FIX_SIZE);
+
while ((evt_len >= sizeof(tlv_hdr->header))) {
tlv_hdr = (struct mwifiex_ie_types_data *)curr;
tlv_len = le16_to_cpu(tlv_hdr->header.len);
@@ -176,6 +178,7 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
break;
case EVENT_UAP_BSS_IDLE:
priv->media_connected = false;
+ priv->port_open = false;
if (netif_carrier_ok(priv->netdev))
netif_carrier_off(priv->netdev);
mwifiex_stop_net_dev_queue(priv->netdev, adapter);
@@ -185,6 +188,7 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
break;
case EVENT_UAP_BSS_ACTIVE:
priv->media_connected = true;
+ priv->port_open = true;
if (!netif_carrier_ok(priv->netdev))
netif_carrier_on(priv->netdev);
mwifiex_wake_up_net_dev_queue(priv->netdev, adapter);
@@ -192,6 +196,7 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
case EVENT_UAP_BSS_START:
mwifiex_dbg(adapter, EVENT,
"AP EVENT: event id: %#x\n", eventcause);
+ priv->port_open = false;
memcpy(priv->netdev->dev_addr, adapter->event_body + 2,
ETH_ALEN);
if (priv->hist_data)
@@ -297,6 +302,16 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
mwifiex_bt_coex_wlan_param_update_event(priv,
adapter->event_skb);
break;
+ case EVENT_TX_DATA_PAUSE:
+ mwifiex_dbg(adapter, EVENT, "event: TX DATA PAUSE\n");
+ mwifiex_process_tx_pause_event(priv, adapter->event_skb);
+ break;
+
+ case EVENT_MULTI_CHAN_INFO:
+ mwifiex_dbg(adapter, EVENT, "event: multi-chan info\n");
+ mwifiex_process_multi_chan_event(priv, adapter->event_skb);
+ break;
+
default:
mwifiex_dbg(adapter, EVENT,
"event: unknown event id: %#x\n", eventcause);
diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c
index 539c7aba4..a43934053 100644
--- a/drivers/net/wireless/mwifiex/usb.c
+++ b/drivers/net/wireless/mwifiex/usb.c
@@ -47,6 +47,11 @@ static struct usb_device_id mwifiex_usb_table[] = {
{USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8897_PID_2,
USB_CLASS_VENDOR_SPEC,
USB_SUBCLASS_VENDOR_SPEC, 0xff)},
+ /* 8997 */
+ {USB_DEVICE(USB8XXX_VID, USB8997_PID_1)},
+ {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8997_PID_2,
+ USB_CLASS_VENDOR_SPEC,
+ USB_SUBCLASS_VENDOR_SPEC, 0xff)},
{ } /* Terminating entry */
};
@@ -244,9 +249,11 @@ setup_for_next:
if (card->rx_cmd_ep == context->ep) {
mwifiex_usb_submit_rx_urb(context, size);
} else {
- context->skb = NULL;
- if (atomic_read(&adapter->rx_pending) <= HIGH_RX_PENDING)
+ if (atomic_read(&adapter->rx_pending) <= HIGH_RX_PENDING){
mwifiex_usb_submit_rx_urb(context, size);
+ }else{
+ context->skb = NULL;
+ }
}
return;
@@ -380,12 +387,14 @@ static int mwifiex_usb_probe(struct usb_interface *intf,
case USB8797_PID_1:
case USB8801_PID_1:
case USB8897_PID_1:
+ case USB8997_PID_1:
card->usb_boot_state = USB8XXX_FW_DNLD;
break;
case USB8766_PID_2:
case USB8797_PID_2:
case USB8801_PID_2:
case USB8897_PID_2:
+ case USB8997_PID_2:
card->usb_boot_state = USB8XXX_FW_READY;
break;
default:
@@ -812,6 +821,12 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
adapter->dev = &card->udev->dev;
switch (le16_to_cpu(card->udev->descriptor.idProduct)) {
+ case USB8997_PID_1:
+ case USB8997_PID_2:
+ adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K;
+ strcpy(adapter->fw_name, USB8997_DEFAULT_FW_NAME);
+ adapter->ext_scan = true;
+ break;
case USB8897_PID_1:
case USB8897_PID_2:
adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K;
@@ -868,8 +883,10 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
/* Allocate memory for transmit */
fwdata = kzalloc(FW_DNLD_TX_BUF_SIZE, GFP_KERNEL);
- if (!fwdata)
+ if (!fwdata) {
+ ret = -ENOMEM;
goto fw_exit;
+ }
/* Allocate memory for receive */
recv_buff = kzalloc(FW_DNLD_RX_BUF_SIZE, GFP_KERNEL);
diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h
index 8499587bd..a6dbf4e08 100644
--- a/drivers/net/wireless/mwifiex/usb.h
+++ b/drivers/net/wireless/mwifiex/usb.h
@@ -32,6 +32,8 @@
#define USB8897_PID_2 0x2046
#define USB8801_PID_1 0x2049
#define USB8801_PID_2 0x204a
+#define USB8997_PID_1 0x204d
+#define USB8997_PID_2 0x204e
#define USB8XXX_FW_DNLD 1
@@ -46,6 +48,7 @@
#define USB8797_DEFAULT_FW_NAME "/*(DEBLOBBED)*/"
#define USB8801_DEFAULT_FW_NAME "/*(DEBLOBBED)*/"
#define USB8897_DEFAULT_FW_NAME "/*(DEBLOBBED)*/"
+#define USB8997_DEFAULT_FW_NAME "/*(DEBLOBBED)*/"
#define FW_DNLD_TX_BUF_SIZE 620
#define FW_DNLD_RX_BUF_SIZE 2048
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c
index 790e61953..0cec8a644 100644
--- a/drivers/net/wireless/mwifiex/util.c
+++ b/drivers/net/wireless/mwifiex/util.c
@@ -126,6 +126,10 @@ static int num_of_items = ARRAY_SIZE(items);
int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter)
{
+ if (adapter->hw_status == MWIFIEX_HW_STATUS_READY)
+ if (adapter->if_ops.init_fw_port)
+ adapter->if_ops.init_fw_port(adapter);
+
adapter->init_wait_q_woken = true;
wake_up_interruptible(&adapter->init_wait_q);
return 0;
@@ -496,16 +500,12 @@ 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)
{
- mwifiex_dbg(adapter, CMD,
- "cmd completed: status=%d\n",
+ WARN_ON(!cmd_node->wait_q_enabled);
+ mwifiex_dbg(adapter, CMD, "cmd completed: status=%d\n",
adapter->cmd_wait_q.status);
- *(cmd_node->condition) = true;
-
- if (adapter->cmd_wait_q.status == -ETIMEDOUT)
- mwifiex_dbg(adapter, ERROR, "cmd timeout\n");
- else
- wake_up_interruptible(&adapter->cmd_wait_q.wait);
+ *cmd_node->condition = true;
+ wake_up_interruptible(&adapter->cmd_wait_q.wait);
return 0;
}
@@ -531,6 +531,65 @@ mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac)
return NULL;
}
+static struct mwifiex_sta_node *
+mwifiex_get_tdls_sta_entry(struct mwifiex_private *priv, u8 status)
+{
+ struct mwifiex_sta_node *node;
+
+ list_for_each_entry(node, &priv->sta_list, list) {
+ if (node->tdls_status == status)
+ return node;
+ }
+
+ return NULL;
+}
+
+/* If tdls channel switching is on-going, tx data traffic should be
+ * blocked until the switching stage completed.
+ */
+u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv)
+{
+ struct mwifiex_sta_node *sta_ptr;
+
+ if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info))
+ return false;
+
+ sta_ptr = mwifiex_get_tdls_sta_entry(priv, TDLS_CHAN_SWITCHING);
+ if (sta_ptr)
+ return true;
+
+ return false;
+}
+
+u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv)
+{
+ struct mwifiex_sta_node *sta_ptr;
+
+ if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info))
+ return false;
+
+ sta_ptr = mwifiex_get_tdls_sta_entry(priv, TDLS_IN_OFF_CHAN);
+ if (sta_ptr)
+ return true;
+
+ return false;
+}
+
+/* If tdls channel switching is on-going or tdls operate on off-channel,
+ * cmd path should be blocked until tdls switched to base-channel.
+ */
+u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv)
+{
+ if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info))
+ return true;
+
+ if (mwifiex_is_tdls_chan_switching(priv) ||
+ mwifiex_is_tdls_off_chan(priv))
+ return false;
+
+ return true;
+}
+
/* 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.
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c
index a8ea21c33..173d3663c 100644
--- a/drivers/net/wireless/mwifiex/wmm.c
+++ b/drivers/net/wireless/mwifiex/wmm.c
@@ -160,9 +160,10 @@ void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra)
ra_list->tdls_link = false;
ra_list->ba_status = BA_SETUP_NONE;
ra_list->amsdu_in_ampdu = false;
+ ra_list->tx_paused = false;
if (!mwifiex_queuing_ra_based(priv)) {
- if (mwifiex_get_tdls_link_status(priv, ra) ==
- TDLS_SETUP_COMPLETE) {
+ if (mwifiex_is_tdls_link_setup
+ (mwifiex_get_tdls_link_status(priv, ra))) {
ra_list->tdls_link = true;
ra_list->is_11n_enabled =
mwifiex_tdls_peer_11n_enabled(priv, ra);
@@ -448,6 +449,11 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter)
}
}
+int mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter)
+{
+ return atomic_read(&adapter->bypass_tx_pending) ? false : true;
+}
+
/*
* This function checks if WMM Tx queue is empty.
*/
@@ -459,6 +465,8 @@ mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter)
for (i = 0; i < adapter->priv_num; ++i) {
priv = adapter->priv[i];
+ if (priv && !priv->port_open)
+ continue;
if (priv && atomic_read(&priv->wmm.tx_pkts_queued))
return false;
}
@@ -580,6 +588,10 @@ mwifiex_clean_txrx(struct mwifiex_private *priv)
skb_queue_walk_safe(&priv->tdls_txq, skb, tmp)
mwifiex_write_data_complete(priv->adapter, skb, 0, -1);
+ skb_queue_walk_safe(&priv->bypass_txq, skb, tmp)
+ mwifiex_write_data_complete(priv->adapter, skb, 0, -1);
+ atomic_set(&priv->adapter->bypass_tx_pending, 0);
+
idr_for_each(&priv->ack_status_frames, mwifiex_free_ack_frame, NULL);
idr_destroy(&priv->ack_status_frames);
}
@@ -603,6 +615,88 @@ mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid,
return NULL;
}
+void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac,
+ u8 tx_pause)
+{
+ struct mwifiex_ra_list_tbl *ra_list;
+ u32 pkt_cnt = 0, tx_pkts_queued;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+ for (i = 0; i < MAX_NUM_TID; ++i) {
+ ra_list = mwifiex_wmm_get_ralist_node(priv, i, mac);
+ if (ra_list && ra_list->tx_paused != tx_pause) {
+ pkt_cnt += ra_list->total_pkt_count;
+ ra_list->tx_paused = tx_pause;
+ if (tx_pause)
+ priv->wmm.pkts_paused[i] +=
+ ra_list->total_pkt_count;
+ else
+ priv->wmm.pkts_paused[i] -=
+ ra_list->total_pkt_count;
+ }
+ }
+
+ if (pkt_cnt) {
+ tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued);
+ if (tx_pause)
+ tx_pkts_queued -= pkt_cnt;
+ else
+ tx_pkts_queued += pkt_cnt;
+
+ atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued);
+ atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID);
+ }
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+}
+
+/* This function update non-tdls peer ralist tx_pause while
+ * tdls channel swithing
+ */
+void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv,
+ u8 *mac, u8 tx_pause)
+{
+ struct mwifiex_ra_list_tbl *ra_list;
+ u32 pkt_cnt = 0, tx_pkts_queued;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+ for (i = 0; i < MAX_NUM_TID; ++i) {
+ list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[i].ra_list,
+ list) {
+ if (!memcmp(ra_list->ra, mac, ETH_ALEN))
+ continue;
+
+ if (ra_list && ra_list->tx_paused != tx_pause) {
+ pkt_cnt += ra_list->total_pkt_count;
+ ra_list->tx_paused = tx_pause;
+ if (tx_pause)
+ priv->wmm.pkts_paused[i] +=
+ ra_list->total_pkt_count;
+ else
+ priv->wmm.pkts_paused[i] -=
+ ra_list->total_pkt_count;
+ }
+ }
+ }
+
+ if (pkt_cnt) {
+ tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued);
+ if (tx_pause)
+ tx_pkts_queued -= pkt_cnt;
+ else
+ tx_pkts_queued += pkt_cnt;
+
+ atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued);
+ atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID);
+ }
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+}
+
/*
* This function retrieves an RA list node for a given TID and
* RA address pair.
@@ -670,6 +764,18 @@ mwifiex_is_ralist_valid(struct mwifiex_private *priv,
}
/*
+ * This function adds a packet to bypass TX queue.
+ * This is special TX queue for packets which can be sent even when port_open
+ * is false.
+ */
+void
+mwifiex_wmm_add_buf_bypass_txqueue(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ skb_queue_tail(&priv->bypass_txq, skb);
+}
+
+/*
* This function adds a packet to WMM queue.
*
* In disconnected state the packet is immediately dropped and the
@@ -723,6 +829,9 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
!mwifiex_is_skb_mgmt_frame(skb)) {
switch (tdls_status) {
case TDLS_SETUP_COMPLETE:
+ case TDLS_CHAN_SWITCHING:
+ case TDLS_IN_BASE_CHAN:
+ case TDLS_IN_OFF_CHAN:
ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down,
ra);
tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT;
@@ -765,7 +874,10 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
atomic_set(&priv->wmm.highest_queued_prio,
priv->tos_to_tid_inv[tid_down]);
- atomic_inc(&priv->wmm.tx_pkts_queued);
+ if (ra_list->tx_paused)
+ priv->wmm.pkts_paused[tid_down]++;
+ else
+ atomic_inc(&priv->wmm.tx_pkts_queued);
spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
}
@@ -970,7 +1082,8 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
priv_tmp = adapter->bss_prio_tbl[j].bss_prio_cur->priv;
- if (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0)
+ if (!priv_tmp->port_open ||
+ (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0))
continue;
/* iterate over the WMM queues of the BSS */
@@ -987,7 +1100,8 @@ mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter,
list_for_each_entry(ptr, &tid_ptr->ra_list,
list) {
- if (!skb_queue_empty(&ptr->skb_head))
+ if (!ptr->tx_paused &&
+ !skb_queue_empty(&ptr->skb_head))
/* holds both locks */
goto found;
}
@@ -1339,6 +1453,38 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter)
return 0;
}
+void mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter)
+{
+ struct mwifiex_tx_param tx_param;
+ struct sk_buff *skb;
+ struct mwifiex_txinfo *tx_info;
+ struct mwifiex_private *priv;
+ int i;
+
+ if (adapter->data_sent || adapter->tx_lock_flag)
+ return;
+
+ for (i = 0; i < adapter->priv_num; ++i) {
+ priv = adapter->priv[i];
+
+ if (skb_queue_empty(&priv->bypass_txq))
+ continue;
+
+ skb = skb_dequeue(&priv->bypass_txq);
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+
+ /* no aggregation for bypass packets */
+ tx_param.next_pkt_len = 0;
+
+ if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) {
+ skb_queue_head(&priv->bypass_txq, skb);
+ tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT;
+ } else {
+ atomic_dec(&adapter->bypass_tx_pending);
+ }
+ }
+}
+
/*
* This function transmits the highest priority packet awaiting in the
* WMM Queues.
diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h
index 48ece0b35..38f09762b 100644
--- a/drivers/net/wireless/mwifiex/wmm.h
+++ b/drivers/net/wireless/mwifiex/wmm.h
@@ -99,12 +99,16 @@ mwifiex_wmm_is_ra_list_empty(struct list_head *ra_list_hhead)
void mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv,
struct sk_buff *skb);
+void mwifiex_wmm_add_buf_bypass_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);
+int mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter);
void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter);
+void mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter);
int mwifiex_is_ralist_valid(struct mwifiex_private *priv,
struct mwifiex_ra_list_tbl *ra_list, int tid);
@@ -126,6 +130,10 @@ 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);
+void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac,
+ u8 tx_pause);
+void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv,
+ u8 *mac, u8 tx_pause);
struct mwifiex_ra_list_tbl *mwifiex_wmm_get_ralist_node(struct mwifiex_private
*priv, u8 tid, const u8 *ra_addr);