summaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/marvell/libertas/cfg.c3
-rw-r--r--drivers/net/wireless/marvell/mwifiex/fw.h3
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/cfg80211.c17
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/commands.c5
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/core.c27
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/core.h4
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c15
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h1
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/qlink.h11
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_core.c8
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mac80211.c131
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_main.c1
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mgmt.c69
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_sdio.c141
-rw-r--r--drivers/net/wireless/rsi/rsi_common.h3
-rw-r--r--drivers/net/wireless/rsi/rsi_main.h12
-rw-r--r--drivers/net/wireless/rsi/rsi_mgmt.h33
17 files changed, 454 insertions, 30 deletions
diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c
index 71ba2c8d09b5..bb76707881cd 100644
--- a/drivers/net/wireless/marvell/libertas/cfg.c
+++ b/drivers/net/wireless/marvell/libertas/cfg.c
@@ -1698,9 +1698,6 @@ static void lbs_join_post(struct lbs_private *priv,
0, GFP_KERNEL);
cfg80211_put_bss(priv->wdev->wiphy, bss);
- memcpy(priv->wdev->ssid, params->ssid, params->ssid_len);
- priv->wdev->ssid_len = params->ssid_len;
-
cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan,
GFP_KERNEL);
diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h
index 6b765f3f37fd..13cd58e963b3 100644
--- a/drivers/net/wireless/marvell/mwifiex/fw.h
+++ b/drivers/net/wireless/marvell/mwifiex/fw.h
@@ -225,7 +225,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
#define IS_11N_ENABLED(priv) ((priv->adapter->config_bands & BAND_GN || \
priv->adapter->config_bands & BAND_AN) && \
- priv->curr_bss_params.bss_descriptor.bcn_ht_cap)
+ priv->curr_bss_params.bss_descriptor.bcn_ht_cap && \
+ !priv->curr_bss_params.bss_descriptor.disable_11n)
#define INITIATOR_BIT(DelBAParamSet) (((DelBAParamSet) &\
BIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS)
diff --git a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
index 63a540d1216e..e70f5bd5e498 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
@@ -73,7 +73,10 @@ qtnf_mgmt_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_AP] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
- BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+ BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+ BIT(IEEE80211_STYPE_AUTH >> 4),
},
};
@@ -353,6 +356,13 @@ qtnf_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev,
return;
switch (frame_type & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_REASSOC_REQ:
+ case IEEE80211_STYPE_ASSOC_REQ:
+ qlink_frame_type = QLINK_MGMT_FRAME_ASSOC_REQ;
+ break;
+ case IEEE80211_STYPE_AUTH:
+ qlink_frame_type = QLINK_MGMT_FRAME_AUTH;
+ break;
case IEEE80211_STYPE_PROBE_REQ:
qlink_frame_type = QLINK_MGMT_FRAME_PROBE_REQ;
break;
@@ -946,7 +956,10 @@ int qtnf_wiphy_register(struct qtnf_hw_info *hw_info, struct qtnf_wmac *mac)
ether_addr_copy(wiphy->perm_addr, mac->macaddr);
- if (hw_info->hw_capab & QLINK_HW_SUPPORTS_REG_UPDATE) {
+ if (hw_info->hw_capab & QLINK_HW_CAPAB_STA_INACT_TIMEOUT)
+ wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
+
+ if (hw_info->hw_capab & QLINK_HW_CAPAB_REG_UPDATE) {
wiphy->regulatory_flags |= REGULATORY_STRICT_REG |
REGULATORY_CUSTOM_REG;
wiphy->reg_notifier = qtnf_cfg80211_reg_notifier;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/commands.c b/drivers/net/wireless/quantenna/qtnfmac/commands.c
index babdc600c193..b81f81bd1411 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/commands.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/commands.c
@@ -975,10 +975,11 @@ qtnf_cmd_resp_proc_hw_info(struct qtnf_bus *bus,
return -EINVAL;
}
- pr_info("fw_version=%d, MACs map %#x, alpha2=\"%c%c\", chains Tx=%u Rx=%u\n",
+ pr_info("fw_version=%d, MACs map %#x, alpha2=\"%c%c\", chains Tx=%u Rx=%u, capab=0x%x\n",
hwinfo->fw_ver, hwinfo->mac_bitmap,
hwinfo->rd->alpha2[0], hwinfo->rd->alpha2[1],
- hwinfo->total_tx_chain, hwinfo->total_rx_chain);
+ hwinfo->total_tx_chain, hwinfo->total_rx_chain,
+ hwinfo->hw_capab);
return 0;
}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c
index aa7f146278a7..6a6e5ffb0348 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.c
@@ -618,6 +618,33 @@ out:
}
EXPORT_SYMBOL_GPL(qtnf_classify_skb);
+void qtnf_wake_all_queues(struct net_device *ndev)
+{
+ struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev);
+ struct qtnf_wmac *mac;
+ struct qtnf_bus *bus;
+ int macid;
+ int i;
+
+ if (unlikely(!vif || !vif->mac || !vif->mac->bus))
+ return;
+
+ bus = vif->mac->bus;
+
+ for (macid = 0; macid < QTNF_MAX_MAC; macid++) {
+ if (!(bus->hw_info.mac_bitmap & BIT(macid)))
+ continue;
+
+ mac = bus->mac[macid];
+ for (i = 0; i < QTNF_MAX_INTF; i++) {
+ vif = &mac->iflist[i];
+ if (vif->netdev && netif_queue_stopped(vif->netdev))
+ netif_tx_wake_all_queues(vif->netdev);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(qtnf_wake_all_queues);
+
MODULE_AUTHOR("Quantenna Communications");
MODULE_DESCRIPTION("Quantenna 802.11 wireless LAN FullMAC driver.");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.h b/drivers/net/wireless/quantenna/qtnfmac/core.h
index 44a2cbb19310..da2c24e2271d 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.h
@@ -153,9 +153,7 @@ int qtnf_cmd_send_get_phy_params(struct qtnf_wmac *mac);
struct qtnf_wmac *qtnf_core_get_mac(const struct qtnf_bus *bus, u8 macid);
struct net_device *qtnf_classify_skb(struct qtnf_bus *bus, struct sk_buff *skb);
-struct net_device *qtnf_classify_skb_no_mbss(struct qtnf_bus *bus,
- struct sk_buff *skb);
-
+void qtnf_wake_all_queues(struct net_device *ndev);
void qtnf_virtual_intf_cleanup(struct net_device *ndev);
void qtnf_netdev_updown(struct net_device *ndev, bool up);
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c
index 69131965a298..7e487622d87d 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c
@@ -617,9 +617,10 @@ static void qtnf_pcie_data_tx_reclaim(struct qtnf_pcie_bus_priv *priv)
if (skb->dev) {
skb->dev->stats.tx_packets++;
skb->dev->stats.tx_bytes += skb->len;
-
- if (netif_queue_stopped(skb->dev))
- netif_wake_queue(skb->dev);
+ if (unlikely(priv->tx_stopped)) {
+ qtnf_wake_all_queues(skb->dev);
+ priv->tx_stopped = 0;
+ }
}
dev_kfree_skb_any(skb);
@@ -643,11 +644,11 @@ static int qtnf_tx_queue_ready(struct qtnf_pcie_bus_priv *priv)
{
if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index,
priv->tx_bd_num)) {
- pr_err_ratelimited("reclaim full Tx queue\n");
qtnf_pcie_data_tx_reclaim(priv);
if (!CIRC_SPACE(priv->tx_bd_w_index, priv->tx_bd_r_index,
priv->tx_bd_num)) {
+ pr_warn_ratelimited("reclaim full Tx queue\n");
priv->tx_full_count++;
return 0;
}
@@ -669,8 +670,10 @@ static int qtnf_pcie_data_tx(struct qtnf_bus *bus, struct sk_buff *skb)
spin_lock_irqsave(&priv->tx0_lock, flags);
if (!qtnf_tx_queue_ready(priv)) {
- if (skb->dev)
- netif_stop_queue(skb->dev);
+ if (skb->dev) {
+ netif_tx_stop_all_queues(skb->dev);
+ priv->tx_stopped = 1;
+ }
spin_unlock_irqrestore(&priv->tx0_lock, flags);
return NETDEV_TX_BUSY;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h
index 86ac1ccedb52..397875a50fc2 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/pearl/pcie_bus_priv.h
@@ -37,6 +37,7 @@ struct qtnf_pcie_bus_priv {
/* lock for tx0 operations */
spinlock_t tx0_lock;
u8 msi_enabled;
+ u8 tx_stopped;
int mps;
struct workqueue_struct *workqueue;
diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
index 7b313d38c30b..0f582782682f 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h
+++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h
@@ -61,8 +61,17 @@ struct qlink_msg_header {
/* Generic definitions of data and information carried in QLINK messages
*/
+/**
+ * enum qlink_hw_capab - device capabilities.
+ *
+ * @QLINK_HW_CAPAB_REG_UPDATE: device can update it's regulatory region.
+ * @QLINK_HW_CAPAB_STA_INACT_TIMEOUT: device implements a logic to kick-out
+ * associated STAs due to inactivity. Inactivity timeout period is taken
+ * from QLINK_CMD_START_AP parameters.
+ */
enum qlink_hw_capab {
- QLINK_HW_SUPPORTS_REG_UPDATE = BIT(0),
+ QLINK_HW_CAPAB_REG_UPDATE = BIT(0),
+ QLINK_HW_CAPAB_STA_INACT_TIMEOUT = BIT(1),
};
enum qlink_phy_mode {
diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c
index bc18a191aef9..d0d2201830e8 100644
--- a/drivers/net/wireless/rsi/rsi_91x_core.c
+++ b/drivers/net/wireless/rsi/rsi_91x_core.c
@@ -276,6 +276,8 @@ void rsi_core_qos_processor(struct rsi_common *common)
rsi_dbg(DATA_TX_ZONE, "%s: No More Pkt\n", __func__);
break;
}
+ if (common->hibernate_resume)
+ break;
mutex_lock(&common->tx_lock);
@@ -379,6 +381,12 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
rsi_dbg(ERR_ZONE, "%s: FSM state not open\n", __func__);
goto xmit_fail;
}
+ if (common->wow_flags & RSI_WOW_ENABLED) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Blocking Tx_packets when WOWLAN is enabled\n",
+ __func__);
+ goto xmit_fail;
+ }
info = IEEE80211_SKB_CB(skb);
tx_params = (struct skb_info *)info->driver_data;
diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
index 9427c9d7c9c7..32f5cb46fd4f 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
@@ -17,6 +17,7 @@
#include <linux/etherdevice.h>
#include "rsi_debugfs.h"
#include "rsi_mgmt.h"
+#include "rsi_sdio.h"
#include "rsi_common.h"
#include "rsi_ps.h"
@@ -325,6 +326,11 @@ static int rsi_mac80211_start(struct ieee80211_hw *hw)
rsi_dbg(ERR_ZONE, "===> Interface UP <===\n");
mutex_lock(&common->mutex);
+ if (common->hibernate_resume) {
+ common->reinit_hw = true;
+ adapter->host_intf_ops->reinit_device(adapter);
+ wait_for_completion(&adapter->priv->wlan_init_completion);
+ }
common->iface_down = false;
wiphy_rfkill_start_polling(hw->wiphy);
rsi_send_rx_filter_frame(common, 0);
@@ -1746,6 +1752,123 @@ static int rsi_mac80211_cancel_roc(struct ieee80211_hw *hw)
return 0;
}
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support rsi_wowlan_support = {
+ .flags = WIPHY_WOWLAN_ANY |
+ WIPHY_WOWLAN_MAGIC_PKT |
+ WIPHY_WOWLAN_DISCONNECT |
+ WIPHY_WOWLAN_GTK_REKEY_FAILURE |
+ WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
+ WIPHY_WOWLAN_EAP_IDENTITY_REQ |
+ WIPHY_WOWLAN_4WAY_HANDSHAKE,
+};
+
+static u16 rsi_wow_map_triggers(struct rsi_common *common,
+ struct cfg80211_wowlan *wowlan)
+{
+ u16 wow_triggers = 0;
+
+ rsi_dbg(INFO_ZONE, "Mapping wowlan triggers\n");
+
+ if (wowlan->any)
+ wow_triggers |= RSI_WOW_ANY;
+ if (wowlan->magic_pkt)
+ wow_triggers |= RSI_WOW_MAGIC_PKT;
+ if (wowlan->disconnect)
+ wow_triggers |= RSI_WOW_DISCONNECT;
+ if (wowlan->gtk_rekey_failure || wowlan->eap_identity_req ||
+ wowlan->four_way_handshake)
+ wow_triggers |= RSI_WOW_GTK_REKEY;
+
+ return wow_triggers;
+}
+
+int rsi_config_wowlan(struct rsi_hw *adapter, struct cfg80211_wowlan *wowlan)
+{
+ struct rsi_common *common = adapter->priv;
+ u16 triggers = 0;
+ u16 rx_filter_word = 0;
+ struct ieee80211_bss_conf *bss = &adapter->vifs[0]->bss_conf;
+
+ rsi_dbg(INFO_ZONE, "Config WoWLAN to device\n");
+
+ if (WARN_ON(!wowlan)) {
+ rsi_dbg(ERR_ZONE, "WoW triggers not enabled\n");
+ return -EINVAL;
+ }
+
+ triggers = rsi_wow_map_triggers(common, wowlan);
+ if (!triggers) {
+ rsi_dbg(ERR_ZONE, "%s:No valid WoW triggers\n", __func__);
+ return -EINVAL;
+ }
+ if (!bss->assoc) {
+ rsi_dbg(ERR_ZONE,
+ "Cannot configure WoWLAN (Station not connected)\n");
+ common->wow_flags |= RSI_WOW_NO_CONNECTION;
+ return 0;
+ }
+ rsi_dbg(INFO_ZONE, "TRIGGERS %x\n", triggers);
+ rsi_send_wowlan_request(common, triggers, 1);
+
+ /**
+ * Increase the beacon_miss threshold & keep-alive timers in
+ * vap_update frame
+ */
+ rsi_send_vap_dynamic_update(common);
+
+ rx_filter_word = (ALLOW_DATA_ASSOC_PEER | DISALLOW_BEACONS);
+ rsi_send_rx_filter_frame(common, rx_filter_word);
+ common->wow_flags |= RSI_WOW_ENABLED;
+
+ return 0;
+}
+EXPORT_SYMBOL(rsi_config_wowlan);
+
+static int rsi_mac80211_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ rsi_dbg(INFO_ZONE, "%s: mac80211 suspend\n", __func__);
+ mutex_lock(&common->mutex);
+ if (rsi_config_wowlan(adapter, wowlan)) {
+ rsi_dbg(ERR_ZONE, "Failed to configure WoWLAN\n");
+ mutex_unlock(&common->mutex);
+ return 1;
+ }
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+static int rsi_mac80211_resume(struct ieee80211_hw *hw)
+{
+ u16 rx_filter_word = 0;
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+
+ common->wow_flags = 0;
+
+ rsi_dbg(INFO_ZONE, "%s: mac80211 resume\n", __func__);
+
+ if (common->hibernate_resume)
+ return 0;
+
+ mutex_lock(&common->mutex);
+ rsi_send_wowlan_request(common, 0, 0);
+
+ rx_filter_word = (ALLOW_DATA_ASSOC_PEER | ALLOW_CTRL_ASSOC_PEER |
+ ALLOW_MGMT_ASSOC_PEER);
+ rsi_send_rx_filter_frame(common, rx_filter_word);
+ mutex_unlock(&common->mutex);
+
+ return 0;
+}
+
+#endif
+
static const struct ieee80211_ops mac80211_ops = {
.tx = rsi_mac80211_tx,
.start = rsi_mac80211_start,
@@ -1767,6 +1890,10 @@ static const struct ieee80211_ops mac80211_ops = {
.rfkill_poll = rsi_mac80211_rfkill_poll,
.remain_on_channel = rsi_mac80211_roc,
.cancel_remain_on_channel = rsi_mac80211_cancel_roc,
+#ifdef CONFIG_PM
+ .suspend = rsi_mac80211_suspend,
+ .resume = rsi_mac80211_resume,
+#endif
};
/**
@@ -1850,6 +1977,10 @@ int rsi_mac80211_attach(struct rsi_common *common)
wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
wiphy->reg_notifier = rsi_reg_notify;
+#ifdef CONFIG_PM
+ wiphy->wowlan = &rsi_wowlan_support;
+#endif
+
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
/* Wi-Fi direct parameters */
diff --git a/drivers/net/wireless/rsi/rsi_91x_main.c b/drivers/net/wireless/rsi/rsi_91x_main.c
index 2a1fbb7db6c4..0cb8e68bab58 100644
--- a/drivers/net/wireless/rsi/rsi_91x_main.c
+++ b/drivers/net/wireless/rsi/rsi_91x_main.c
@@ -263,6 +263,7 @@ struct rsi_hw *rsi_91x_init(void)
rsi_default_ps_params(adapter);
spin_lock_init(&adapter->ps_lock);
timer_setup(&common->roc_timer, rsi_roc_timeout, 0);
+ init_completion(&common->wlan_init_completion);
common->init_done = true;
return adapter;
diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
index 4b94190c9797..46c9d5470dfb 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
@@ -1094,9 +1094,18 @@ int rsi_send_vap_dynamic_update(struct rsi_common *common)
dynamic_frame->desc_dword0.frame_type = VAP_DYNAMIC_UPDATE;
dynamic_frame->desc_dword2.pkt_info =
cpu_to_le32(common->rts_threshold);
- /* Beacon miss threshold */
- dynamic_frame->frame_body.keep_alive_period =
+
+ if (common->wow_flags & RSI_WOW_ENABLED) {
+ /* Beacon miss threshold */
+ dynamic_frame->desc_dword3.token =
+ cpu_to_le16(RSI_BCN_MISS_THRESHOLD);
+ dynamic_frame->frame_body.keep_alive_period =
+ cpu_to_le16(RSI_WOW_KEEPALIVE);
+ } else {
+ dynamic_frame->frame_body.keep_alive_period =
cpu_to_le16(RSI_DEF_KEEPALIVE);
+ }
+
dynamic_frame->desc_dword3.sta_id = 0; /* vap id */
skb_put(skb, sizeof(struct rsi_dynamic_s));
@@ -1340,13 +1349,12 @@ void rsi_inform_bss_status(struct rsi_common *common,
} else {
if (opmode == RSI_OPMODE_STA)
common->hw_data_qs_blocked = true;
- rsi_hal_send_sta_notify_frame(common,
- opmode,
- STA_DISCONNECTED,
- addr,
- qos_enable,
- aid, sta_id,
- vif);
+
+ if (!(common->wow_flags & RSI_WOW_ENABLED))
+ rsi_hal_send_sta_notify_frame(common, opmode,
+ STA_DISCONNECTED, addr,
+ qos_enable, aid, sta_id,
+ vif);
if (opmode == RSI_OPMODE_STA)
rsi_send_block_unblock_frame(common, true);
}
@@ -1589,6 +1597,42 @@ static int rsi_send_beacon(struct rsi_common *common)
return 0;
}
+#ifdef CONFIG_PM
+int rsi_send_wowlan_request(struct rsi_common *common, u16 flags,
+ u16 sleep_status)
+{
+ struct rsi_wowlan_req *cmd_frame;
+ struct sk_buff *skb;
+ u8 length;
+
+ rsi_dbg(ERR_ZONE, "%s: Sending wowlan request frame\n", __func__);
+
+ length = sizeof(*cmd_frame);
+ skb = dev_alloc_skb(length);
+ if (!skb)
+ return -ENOMEM;
+ memset(skb->data, 0, length);
+ cmd_frame = (struct rsi_wowlan_req *)skb->data;
+
+ rsi_set_len_qno(&cmd_frame->desc.desc_dword0.len_qno,
+ (length - FRAME_DESC_SZ),
+ RSI_WIFI_MGMT_Q);
+ cmd_frame->desc.desc_dword0.frame_type = WOWLAN_CONFIG_PARAMS;
+ cmd_frame->host_sleep_status = sleep_status;
+ if (common->secinfo.security_enable &&
+ common->secinfo.gtk_cipher)
+ flags |= RSI_WOW_GTK_REKEY;
+ if (sleep_status)
+ cmd_frame->wow_flags = flags;
+ rsi_dbg(INFO_ZONE, "Host_Sleep_Status : %d Flags : %d\n",
+ cmd_frame->host_sleep_status, cmd_frame->wow_flags);
+
+ skb_put(skb, length);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+}
+#endif
+
/**
* rsi_handle_ta_confirm_type() - This function handles the confirm frames.
* @common: Pointer to the driver private structure.
@@ -1719,7 +1763,11 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common,
common->bb_rf_prog_count--;
if (!common->bb_rf_prog_count) {
common->fsm_state = FSM_MAC_INIT_DONE;
- return rsi_mac80211_attach(common);
+ if (common->reinit_hw) {
+ complete(&common->wlan_init_completion);
+ } else {
+ return rsi_mac80211_attach(common);
+ }
}
} else {
rsi_dbg(INFO_ZONE,
@@ -1797,6 +1845,7 @@ int rsi_mgmt_pkt_recv(struct rsi_common *common, u8 *msg)
case TA_CONFIRM_TYPE:
return rsi_handle_ta_confirm_type(common, msg);
case CARD_READY_IND:
+ common->hibernate_resume = false;
rsi_dbg(FSM_ZONE, "%s: Card ready indication received\n",
__func__);
return rsi_handle_card_ready(common, msg);
diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c
index b3f8006c0e9b..b0cf41195051 100644
--- a/drivers/net/wireless/rsi/rsi_91x_sdio.c
+++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c
@@ -871,6 +871,32 @@ fail:
return status;
}
+static int rsi_sdio_reinit_device(struct rsi_hw *adapter)
+{
+ struct rsi_91x_sdiodev *sdev = adapter->rsi_dev;
+ struct sdio_func *pfunction = sdev->pfunction;
+ int ii;
+
+ for (ii = 0; ii < NUM_SOFT_QUEUES; ii++)
+ skb_queue_purge(&adapter->priv->tx_queue[ii]);
+
+ /* Initialize device again */
+ sdio_claim_host(pfunction);
+
+ sdio_release_irq(pfunction);
+ rsi_reset_card(pfunction);
+
+ sdio_enable_func(pfunction);
+ rsi_setupcard(adapter);
+ rsi_init_sdio_slave_regs(adapter);
+ sdio_claim_irq(pfunction, rsi_handle_interrupt);
+ rsi_hal_device_init(adapter);
+
+ sdio_release_host(pfunction);
+
+ return 0;
+}
+
static struct rsi_host_intf_ops sdio_host_intf_ops = {
.write_pkt = rsi_sdio_host_intf_write_pkt,
.read_pkt = rsi_sdio_host_intf_read_pkt,
@@ -880,6 +906,7 @@ static struct rsi_host_intf_ops sdio_host_intf_ops = {
.master_reg_read = rsi_sdio_master_reg_read,
.master_reg_write = rsi_sdio_master_reg_write,
.load_data_master_write = rsi_sdio_load_data_master_write,
+ .reinit_device = rsi_sdio_reinit_device,
};
/**
@@ -936,6 +963,8 @@ static int rsi_probe(struct sdio_func *pfunction,
return -EIO;
}
+ adapter->priv->hibernate_resume = false;
+ adapter->priv->reinit_hw = false;
return 0;
fail:
rsi_91x_deinit(adapter);
@@ -1124,6 +1153,8 @@ static int rsi_sdio_enable_interrupts(struct sdio_func *pfunc)
{
u8 data;
int ret;
+ struct rsi_hw *adapter = sdio_get_drvdata(pfunc);
+ struct rsi_common *common = adapter->priv;
sdio_claim_host(pfunc);
ret = rsi_cmd52readbyte(pfunc->card, RSI_INT_ENABLE_REGISTER, &data);
@@ -1143,6 +1174,11 @@ static int rsi_sdio_enable_interrupts(struct sdio_func *pfunc)
goto done;
}
+ if ((common->wow_flags & RSI_WOW_ENABLED) &&
+ (common->wow_flags & RSI_WOW_NO_CONNECTION))
+ rsi_dbg(ERR_ZONE,
+ "##### Device can not wake up through WLAN\n");
+
ret = rsi_cmd52readbyte(pfunc->card, RSI_INT_ENABLE_REGISTER, &data);
if (ret < 0) {
rsi_dbg(ERR_ZONE,
@@ -1191,9 +1227,113 @@ static int rsi_resume(struct device *dev)
return 0;
}
+static int rsi_freeze(struct device *dev)
+{
+ int ret;
+ struct sdio_func *pfunction = dev_to_sdio_func(dev);
+ struct rsi_hw *adapter = sdio_get_drvdata(pfunction);
+ struct rsi_common *common;
+ struct rsi_91x_sdiodev *sdev;
+
+ rsi_dbg(INFO_ZONE, "SDIO Bus freeze ===>\n");
+
+ if (!adapter) {
+ rsi_dbg(ERR_ZONE, "Device is not ready\n");
+ return -ENODEV;
+ }
+ common = adapter->priv;
+ sdev = (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+
+ if ((common->wow_flags & RSI_WOW_ENABLED) &&
+ (common->wow_flags & RSI_WOW_NO_CONNECTION))
+ rsi_dbg(ERR_ZONE,
+ "##### Device can not wake up through WLAN\n");
+
+ ret = rsi_sdio_disable_interrupts(pfunction);
+
+ if (sdev->write_fail)
+ rsi_dbg(INFO_ZONE, "###### Device is not ready #######\n");
+
+ ret = rsi_set_sdio_pm_caps(adapter);
+ if (ret)
+ rsi_dbg(INFO_ZONE, "Setting power management caps failed\n");
+
+ rsi_dbg(INFO_ZONE, "***** RSI module freezed *****\n");
+
+ return 0;
+}
+
+static int rsi_thaw(struct device *dev)
+{
+ struct sdio_func *pfunction = dev_to_sdio_func(dev);
+ struct rsi_hw *adapter = sdio_get_drvdata(pfunction);
+ struct rsi_common *common = adapter->priv;
+
+ rsi_dbg(ERR_ZONE, "SDIO Bus thaw =====>\n");
+
+ common->hibernate_resume = true;
+ common->fsm_state = FSM_CARD_NOT_READY;
+ common->iface_down = true;
+
+ rsi_sdio_enable_interrupts(pfunction);
+
+ rsi_dbg(INFO_ZONE, "***** RSI module thaw done *****\n");
+
+ return 0;
+}
+
+static void rsi_shutdown(struct device *dev)
+{
+ struct sdio_func *pfunction = dev_to_sdio_func(dev);
+ struct rsi_hw *adapter = sdio_get_drvdata(pfunction);
+ struct rsi_91x_sdiodev *sdev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ struct ieee80211_hw *hw = adapter->hw;
+ struct cfg80211_wowlan *wowlan = hw->wiphy->wowlan_config;
+
+ rsi_dbg(ERR_ZONE, "SDIO Bus shutdown =====>\n");
+
+ if (rsi_config_wowlan(adapter, wowlan))
+ rsi_dbg(ERR_ZONE, "Failed to configure WoWLAN\n");
+
+ rsi_sdio_disable_interrupts(sdev->pfunction);
+
+ if (sdev->write_fail)
+ rsi_dbg(INFO_ZONE, "###### Device is not ready #######\n");
+
+ if (rsi_set_sdio_pm_caps(adapter))
+ rsi_dbg(INFO_ZONE, "Setting power management caps failed\n");
+
+ rsi_dbg(INFO_ZONE, "***** RSI module shut down *****\n");
+}
+
+static int rsi_restore(struct device *dev)
+{
+ struct sdio_func *pfunction = dev_to_sdio_func(dev);
+ struct rsi_hw *adapter = sdio_get_drvdata(pfunction);
+ struct rsi_common *common = adapter->priv;
+
+ rsi_dbg(INFO_ZONE, "SDIO Bus restore ======>\n");
+ common->hibernate_resume = true;
+ common->fsm_state = FSM_FW_NOT_LOADED;
+ common->iface_down = true;
+
+ adapter->sc_nvifs = 0;
+ ieee80211_restart_hw(adapter->hw);
+
+ common->wow_flags = 0;
+ common->iface_down = false;
+
+ rsi_dbg(INFO_ZONE, "RSI module restored\n");
+
+ return 0;
+}
static const struct dev_pm_ops rsi_pm_ops = {
.suspend = rsi_suspend,
.resume = rsi_resume,
+ .freeze = rsi_freeze,
+ .thaw = rsi_thaw,
+ .restore = rsi_restore,
};
#endif
@@ -1213,6 +1353,7 @@ static struct sdio_driver rsi_driver = {
#ifdef CONFIG_PM
.drv = {
.pm = &rsi_pm_ops,
+ .shutdown = rsi_shutdown,
}
#endif
};
diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h
index 29acaea3636d..d07dbba61727 100644
--- a/drivers/net/wireless/rsi/rsi_common.h
+++ b/drivers/net/wireless/rsi/rsi_common.h
@@ -83,6 +83,9 @@ u16 rsi_get_connected_channel(struct ieee80211_vif *vif);
struct rsi_hw *rsi_91x_init(void);
void rsi_91x_deinit(struct rsi_hw *adapter);
int rsi_read_pkt(struct rsi_common *common, s32 rcv_pkt_len);
+#ifdef CONFIG_PM
+int rsi_config_wowlan(struct rsi_hw *adapter, struct cfg80211_wowlan *wowlan);
+#endif
struct rsi_sta *rsi_find_sta(struct rsi_common *common, u8 *mac_addr);
struct ieee80211_vif *rsi_get_vif(struct rsi_hw *adapter, u8 *mac);
void rsi_roc_timeout(struct timer_list *t);
diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h
index a118b7aaeca0..8cab630af4a5 100644
--- a/drivers/net/wireless/rsi/rsi_main.h
+++ b/drivers/net/wireless/rsi/rsi_main.h
@@ -66,6 +66,8 @@ extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...);
#define FRAME_DESC_SZ 16
#define MIN_802_11_HDR_LEN 24
#define RSI_DEF_KEEPALIVE 90
+#define RSI_WOW_KEEPALIVE 5
+#define RSI_BCN_MISS_THRESHOLD 24
#define DATA_QUEUE_WATER_MARK 400
#define MIN_DATA_QUEUE_WATER_MARK 300
@@ -108,6 +110,10 @@ extern __printf(2, 3) void rsi_dbg(u32 zone, const char *fmt, ...);
((_q) == VI_Q) ? IEEE80211_AC_VI : \
IEEE80211_AC_VO)
+/* WoWLAN flags */
+#define RSI_WOW_ENABLED BIT(0)
+#define RSI_WOW_NO_CONNECTION BIT(1)
+
#define RSI_DEV_9113 1
struct version_info {
@@ -208,6 +214,7 @@ struct rsi_common {
struct rsi_thread tx_thread;
struct sk_buff_head tx_queue[NUM_EDCA_QUEUES + 2];
+ struct completion wlan_init_completion;
/* Mutex declaration */
struct mutex mutex;
/* Mutex used for tx thread */
@@ -266,7 +273,9 @@ struct rsi_common {
u8 obm_ant_sel_val;
int tx_power;
u8 ant_in_use;
-
+ bool hibernate_resume;
+ bool reinit_hw;
+ u8 wow_flags;
u16 beacon_interval;
u8 dtim_cnt;
@@ -356,5 +365,6 @@ struct rsi_host_intf_ops {
int (*load_data_master_write)(struct rsi_hw *adapter, u32 addr,
u32 instructions_size, u16 block_size,
u8 *fw);
+ int (*reinit_device)(struct rsi_hw *adapter);
};
#endif
diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h
index e21723013f8d..389094a3f91c 100644
--- a/drivers/net/wireless/rsi/rsi_mgmt.h
+++ b/drivers/net/wireless/rsi/rsi_mgmt.h
@@ -45,6 +45,17 @@
#define MAGIC_WORD 0x5A
#define WLAN_EEPROM_RFTYPE_ADDR 424
+/*WOWLAN RESUME WAKEUP TYPES*/
+#define RSI_UNICAST_MAGIC_PKT BIT(0)
+#define RSI_BROADCAST_MAGICPKT BIT(1)
+#define RSI_EAPOL_PKT BIT(2)
+#define RSI_DISCONNECT_PKT BIT(3)
+#define RSI_HW_BMISS_PKT BIT(4)
+#define RSI_INSERT_SEQ_IN_FW BIT(2)
+
+#define WOW_MAX_FILTERS_PER_LIST 16
+#define WOW_PATTERN_SIZE 256
+
/* Receive Frame Types */
#define TA_CONFIRM_TYPE 0x01
#define RX_DOT11_MGMT 0x02
@@ -201,6 +212,13 @@
#define RSI_DATA_DESC_INSERT_TSF BIT(15)
#define RSI_DATA_DESC_INSERT_SEQ_NO BIT(2)
+#ifdef CONFIG_PM
+#define RSI_WOW_ANY BIT(1)
+#define RSI_WOW_GTK_REKEY BIT(3)
+#define RSI_WOW_MAGIC_PKT BIT(4)
+#define RSI_WOW_DISCONNECT BIT(5)
+#endif
+
enum opmode {
RSI_OPMODE_UNSUPPORTED = -1,
RSI_OPMODE_AP = 0,
@@ -262,7 +280,9 @@ enum cmd_frame_type {
ANT_SEL_FRAME = 0x20,
VAP_DYNAMIC_UPDATE = 0x27,
COMMON_DEV_CONFIG = 0x28,
- RADIO_PARAMS_UPDATE = 0x29
+ RADIO_PARAMS_UPDATE = 0x29,
+ WOWLAN_CONFIG_PARAMS = 0x2B,
+ WOWLAN_WAKEUP_REASON = 0xc5
};
struct rsi_mac_frame {
@@ -581,6 +601,13 @@ struct rsi_request_ps {
__le16 ps_num_dtim_intervals;
} __packed;
+struct rsi_wowlan_req {
+ struct rsi_cmd_desc desc;
+ u8 sourceid[ETH_ALEN];
+ u16 wow_flags;
+ u16 host_sleep_status;
+} __packed;
+
static inline u32 rsi_get_queueno(u8 *addr, u16 offset)
{
return (le16_to_cpu(*(__le16 *)&addr[offset]) & 0x7000) >> 12;
@@ -641,6 +668,10 @@ int rsi_band_check(struct rsi_common *common, struct ieee80211_channel *chan);
int rsi_send_rx_filter_frame(struct rsi_common *common, u16 rx_filter_word);
int rsi_send_radio_params_update(struct rsi_common *common);
int rsi_set_antenna(struct rsi_common *common, u8 antenna);
+#ifdef CONFIG_PM
+int rsi_send_wowlan_request(struct rsi_common *common, u16 flags,
+ u16 sleep_status);
+#endif
int rsi_send_ps_request(struct rsi_hw *adapter, bool enable,
struct ieee80211_vif *vif);
#endif