From: Raja Mani <[email protected]>
This series of patch enables the user to specify the suspend mode
via module parameter (suspend_mode) while doing insmod of the driver.
To select Cut Power mode:
insmod ath6kl_sdio.ko suspend_mode = 1
To select Deep Sleep mode:
insmod ath6kl_sdio.ko suspend_mode = 2
To select WOW suspend mode:
insmod ath6kl_sdio.ko suspend_mode = 3
Existing module param 'suspend_cutpower' variable is renamed to
'suspend_mode' for this purspose.
Additionally, If the user doesn't provide any WOW patterns,
the default WOW patterns will be configured while going to
WOW suspend mode.
V3 changes:
* Module parameter suspend_mode data type is changed from
unsigned char to unsigned int.
* Host sleep mode cmd processed event related changes are grouped into
a single patch.
* All WOW pattern configuration changes are grouped into a single patch.
* Renamed few func names,
ath6kl_set_sdio_pm_keep_pwr_wake_irq() => ath6kl_sdio_pm_caps()
ath6kl_ap_add_default_wow_patterns() => ath6kl_wow_ap()
ath6kl_non_ap_add_default_wow_patterns() => ath6kl_wow_sta()
ath6kl_add_usr_wow_patterns() => ath6kl_wow_usr()
V2 changes:
* Existing automatic suspend mode selection logic is retained.
* Suspend mode retry logic is improved without using loop.
* Small patches are folded with appropriate patches as per Kalle's comments.
* New wmi function is added to process set host sleep mode cmd processed event.
Raja Mani (4):
ath6kl: Re-architect suspend mode handling in ath6kl_sdio_suspend
ath6kl: Configure WOW patterns while going to wow_suspend
ath6kl: Wait for host sleep mode cmd processed event during WOW
suspend
ath6kl: Return a proper error code when not in connected state
drivers/net/wireless/ath/ath6kl/cfg80211.c | 244 +++++++++++++++++++++++-----
drivers/net/wireless/ath/ath6kl/core.c | 12 +-
drivers/net/wireless/ath/ath6kl/core.h | 5 +-
drivers/net/wireless/ath/ath6kl/sdio.c | 86 ++++++----
drivers/net/wireless/ath/ath6kl/wmi.c | 20 +++-
drivers/net/wireless/ath/ath6kl/wmi.h | 5 +-
6 files changed, 290 insertions(+), 82 deletions(-)
On 01/30/2012 01:43 PM, [email protected] wrote:
> From: Raja Mani <[email protected]>
>
> This series of patch enables the user to specify the suspend mode
> via module parameter (suspend_mode) while doing insmod of the driver.
>
> To select Cut Power mode:
> insmod ath6kl_sdio.ko suspend_mode = 1
>
> To select Deep Sleep mode:
> insmod ath6kl_sdio.ko suspend_mode = 2
>
> To select WOW suspend mode:
> insmod ath6kl_sdio.ko suspend_mode = 3
>
> Existing module param 'suspend_cutpower' variable is renamed to
> 'suspend_mode' for this purspose.
>
> Additionally, If the user doesn't provide any WOW patterns,
> the default WOW patterns will be configured while going to
> WOW suspend mode.
Thanks, all four applied.
Kalle
From: Raja Mani <[email protected]>
First preference is given to the user configured WOW patterns.
If the user doesn't configure any patterns (for ex, via iw command),
the default patterns will be configured based on the current mode
(vif->nw_type) while going to WOW suspend.
Summary of changes:
* ath6kl_wow_ap() is added to configure the below default
patterns when the system enters into WOW suspend in AP mode.
+ Unicast IP, EAPOL-like and ARP packet pattern
+ ARP packet pattern
+ mDNS/SSDP/LLMNR pattern
+ DHCP broadcast pattern
* ath6kl_wow_sta() is added to configure the below default
patterns when the system enters into WOW suspend in STA mode.
+ Unicast packet pattern
+ mDNS/SSDP/LLMNR pattern
* Move the user provided WOW patterns configuration code
from ath6kl_wow_suspend() to a separate function called
ath6kl_wow_usr().
* Two argument variable's ('filter' and 'mask) data type in
ath6kl_wmi_add_wow_pattern_cmd() are changed from 'u8 *' to
'const u8 *'. This is needed to make all pattern and mask
arrays to be 'static const u8' in the caller function.
* New conditional check is added to make sure user
configured pattern count is within the limit (WOW_MAX_FILTERS_PER_LIST).
Signed-off-by: Raja Mani <[email protected]>
---
drivers/net/wireless/ath/ath6kl/cfg80211.c | 231 +++++++++++++++++++++++-----
drivers/net/wireless/ath/ath6kl/wmi.c | 3 +-
drivers/net/wireless/ath/ath6kl/wmi.h | 3 +-
3 files changed, 193 insertions(+), 44 deletions(-)
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index fed7b80..e676cfc 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -1723,32 +1723,14 @@ static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
return 0;
}
-static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif,
+ struct cfg80211_wowlan *wow, u32 *filter)
{
- struct in_device *in_dev;
- struct in_ifaddr *ifa;
- struct ath6kl_vif *vif;
- int ret, pos, left;
- u32 filter = 0;
+ int ret, pos;
+ u8 mask[WOW_MASK_SIZE];
u16 i;
- u8 mask[WOW_MASK_SIZE], index = 0;
- __be32 ips[MAX_IP_ADDRS];
-
- vif = ath6kl_vif_first(ar);
- if (!vif)
- return -EIO;
-
- if (!ath6kl_cfg80211_ready(vif))
- return -EIO;
-
- if (!test_bit(CONNECTED, &vif->flags))
- return -EINVAL;
- /* Clear existing WOW patterns */
- for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
- ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
- WOW_LIST_ID, i);
- /* Configure new WOW patterns */
+ /* Configure the patterns that we received from the user. */
for (i = 0; i < wow->n_patterns; i++) {
/*
@@ -1771,14 +1753,194 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
* matched from the first byte of received pkt in the firmware.
*/
ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
- vif->fw_vif_idx, WOW_LIST_ID,
- wow->patterns[i].pattern_len,
- 0 /* pattern offset */,
- wow->patterns[i].pattern, mask);
+ vif->fw_vif_idx, WOW_LIST_ID,
+ wow->patterns[i].pattern_len,
+ 0 /* pattern offset */,
+ wow->patterns[i].pattern, mask);
if (ret)
return ret;
}
+ if (wow->disconnect)
+ *filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
+
+ if (wow->magic_pkt)
+ *filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
+
+ if (wow->gtk_rekey_failure)
+ *filter |= WOW_FILTER_OPTION_GTK_ERROR;
+
+ if (wow->eap_identity_req)
+ *filter |= WOW_FILTER_OPTION_EAP_REQ;
+
+ if (wow->four_way_handshake)
+ *filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
+
+ return 0;
+}
+
+static int ath6kl_wow_ap(struct ath6kl *ar, struct ath6kl_vif *vif)
+{
+ static const u8 unicst_pattern[] = { 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08 };
+ static const u8 unicst_mask[] = { 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x7f };
+ u8 unicst_offset = 0;
+ static const u8 arp_pattern[] = { 0x08, 0x06 };
+ static const u8 arp_mask[] = { 0xff, 0xff };
+ u8 arp_offset = 20;
+ static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
+ static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
+ u8 discvr_offset = 38;
+ static const u8 dhcp_pattern[] = { 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x43 /* port 67 */ };
+ static const u8 dhcp_mask[] = { 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff /* port 67 */ };
+ u8 dhcp_offset = 0;
+ int ret;
+
+ /* Setup unicast IP, EAPOL-like and ARP pkt pattern */
+ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+ vif->fw_vif_idx, WOW_LIST_ID,
+ sizeof(unicst_pattern), unicst_offset,
+ unicst_pattern, unicst_mask);
+ if (ret) {
+ ath6kl_err("failed to add WOW unicast IP pattern\n");
+ return ret;
+ }
+
+ /* Setup all ARP pkt pattern */
+ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+ vif->fw_vif_idx, WOW_LIST_ID,
+ sizeof(arp_pattern), arp_offset,
+ arp_pattern, arp_mask);
+ if (ret) {
+ ath6kl_err("failed to add WOW ARP pattern\n");
+ return ret;
+ }
+
+ /*
+ * Setup multicast pattern for mDNS 224.0.0.251,
+ * SSDP 239.255.255.250 and LLMNR 224.0.0.252
+ */
+ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+ vif->fw_vif_idx, WOW_LIST_ID,
+ sizeof(discvr_pattern), discvr_offset,
+ discvr_pattern, discvr_mask);
+ if (ret) {
+ ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n");
+ return ret;
+ }
+
+ /* Setup all DHCP broadcast pkt pattern */
+ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+ vif->fw_vif_idx, WOW_LIST_ID,
+ sizeof(dhcp_pattern), dhcp_offset,
+ dhcp_pattern, dhcp_mask);
+ if (ret) {
+ ath6kl_err("failed to add WOW DHCP broadcast pattern\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif)
+{
+ struct net_device *ndev = vif->ndev;
+ static const u8 discvr_pattern[] = { 0xe0, 0x00, 0x00, 0xf8 };
+ static const u8 discvr_mask[] = { 0xf0, 0x00, 0x00, 0xf8 };
+ u8 discvr_offset = 38;
+ u8 mac_mask[ETH_ALEN];
+ int ret;
+
+ /* Setup unicast pkt pattern */
+ memset(mac_mask, 0xff, ETH_ALEN);
+ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+ vif->fw_vif_idx, WOW_LIST_ID,
+ ETH_ALEN, 0, ndev->dev_addr,
+ mac_mask);
+ if (ret) {
+ ath6kl_err("failed to add WOW unicast pattern\n");
+ return ret;
+ }
+
+ /*
+ * Setup multicast pattern for mDNS 224.0.0.251,
+ * SSDP 239.255.255.250 and LLMNR 224.0.0.252
+ */
+ if ((ndev->flags & IFF_ALLMULTI) ||
+ (ndev->flags & IFF_MULTICAST && netdev_mc_count(ndev) > 0)) {
+ ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+ vif->fw_vif_idx, WOW_LIST_ID,
+ sizeof(discvr_pattern), discvr_offset,
+ discvr_pattern, discvr_mask);
+ if (ret) {
+ ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR "
+ "pattern\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+{
+ struct in_device *in_dev;
+ struct in_ifaddr *ifa;
+ struct ath6kl_vif *vif;
+ int ret, left;
+ u32 filter = 0;
+ u16 i;
+ u8 index = 0;
+ __be32 ips[MAX_IP_ADDRS];
+
+ vif = ath6kl_vif_first(ar);
+ if (!vif)
+ return -EIO;
+
+ if (!ath6kl_cfg80211_ready(vif))
+ return -EIO;
+
+ if (!test_bit(CONNECTED, &vif->flags))
+ return -EINVAL;
+
+ if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
+ return -EINVAL;
+
+ /* Clear existing WOW patterns */
+ for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
+ ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
+ WOW_LIST_ID, i);
+
+ /*
+ * Skip the default WOW pattern configuration
+ * if the driver receives any WOW patterns from
+ * the user.
+ */
+ if (wow)
+ ret = ath6kl_wow_usr(ar, vif, wow, &filter);
+ else if (vif->nw_type == AP_NETWORK)
+ ret = ath6kl_wow_ap(ar, vif);
+ else
+ ret = ath6kl_wow_sta(ar, vif);
+
+ if (ret)
+ return ret;
+
/* Setup own IP addr for ARP agent. */
in_dev = __in_dev_get_rtnl(vif->ndev);
if (!in_dev)
@@ -1806,21 +1968,6 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
}
skip_arp:
- if (wow->disconnect)
- filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
-
- if (wow->magic_pkt)
- filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
-
- if (wow->gtk_rekey_failure)
- filter |= WOW_FILTER_OPTION_GTK_ERROR;
-
- if (wow->eap_identity_req)
- filter |= WOW_FILTER_OPTION_EAP_REQ;
-
- if (wow->four_way_handshake)
- filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
-
ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
ATH6KL_WOW_MODE_ENABLE,
filter,
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index 5d678bf..9c8e4df 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -2620,7 +2620,8 @@ int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx,
int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx,
u8 list_id, u8 filter_size,
- u8 filter_offset, u8 *filter, u8 *mask)
+ u8 filter_offset, const u8 *filter,
+ const u8 *mask)
{
struct sk_buff *skb;
struct wmi_add_wow_pattern_cmd *cmd;
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h
index 48e9d26..85698ef 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.h
+++ b/drivers/net/wireless/ath/ath6kl/wmi.h
@@ -2461,7 +2461,8 @@ int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx,
u32 filter, u16 host_req_delay);
int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx,
u8 list_id, u8 filter_size,
- u8 filter_offset, u8 *filter, u8 *mask);
+ u8 filter_offset, const u8 *filter,
+ const u8 *mask);
int ath6kl_wmi_del_wow_pattern_cmd(struct wmi *wmi, u8 if_idx,
u16 list_id, u16 filter_id);
int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi);
--
1.7.1
From: Raja Mani <[email protected]>
Using this patch, the user can bypass existing auto
suspend mode selection logic and force ath6kl to enter
into the suspend mode what he/she wants.
If the user doesn't choose any suspend mode while doing
insmod of the driver, auto suspend mode selection logic
will kick in and choose suspend mode based on the host
SDIO controller capability.
Generic module parameter is required to specify suspend
mode including Deep Sleep and WOW while doing insmod.
Renaming existing mod param variable suspend_cutpower
would be sufficient to meet this requirement.
New module parameter suspend_mode can take any one of
the below suspend state,
1. cut power
2. deep sleep
3. wow
Signed-off-by: Raja Mani <[email protected]>
---
drivers/net/wireless/ath/ath6kl/core.c | 12 +++--
drivers/net/wireless/ath/ath6kl/core.h | 4 +-
drivers/net/wireless/ath/ath6kl/sdio.c | 86 ++++++++++++++++++++------------
3 files changed, 64 insertions(+), 38 deletions(-)
diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c
index 0d92e71..a0b7684 100644
--- a/drivers/net/wireless/ath/ath6kl/core.c
+++ b/drivers/net/wireless/ath/ath6kl/core.c
@@ -25,12 +25,12 @@
#include "cfg80211.h"
unsigned int debug_mask;
-static bool suspend_cutpower;
+static unsigned int suspend_mode;
static unsigned int uart_debug;
static unsigned int ath6kl_p2p;
module_param(debug_mask, uint, 0644);
-module_param(suspend_cutpower, bool, 0444);
+module_param(suspend_mode, uint, 0644);
module_param(uart_debug, uint, 0644);
module_param(ath6kl_p2p, uint, 0644);
@@ -143,8 +143,12 @@ int ath6kl_core_init(struct ath6kl *ar)
ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER |
ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST;
- if (suspend_cutpower)
- ar->conf_flags |= ATH6KL_CONF_SUSPEND_CUTPOWER;
+ if (suspend_mode &&
+ suspend_mode >= WLAN_POWER_STATE_CUT_PWR &&
+ suspend_mode <= WLAN_POWER_STATE_WOW)
+ ar->suspend_mode = suspend_mode;
+ else
+ ar->suspend_mode = 0;
if (uart_debug)
ar->conf_flags |= ATH6KL_CONF_UART_DEBUG;
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
index ed9fcc1..ab232c5 100644
--- a/drivers/net/wireless/ath/ath6kl/core.h
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -196,8 +196,7 @@ struct ath6kl_fw_ie {
#define ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN BIT(1)
#define ATH6KL_CONF_ENABLE_11N BIT(2)
#define ATH6KL_CONF_ENABLE_TX_BURST BIT(3)
-#define ATH6KL_CONF_SUSPEND_CUTPOWER BIT(4)
-#define ATH6KL_CONF_UART_DEBUG BIT(5)
+#define ATH6KL_CONF_UART_DEBUG BIT(4)
enum wlan_low_pwr_state {
WLAN_POWER_STATE_ON,
@@ -618,6 +617,7 @@ struct ath6kl {
} hw;
u16 conf_flags;
+ u16 suspend_mode;
wait_queue_head_t event_wq;
struct ath6kl_mbox_info mbox_info;
diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c
index d9f5591..07dcf00 100644
--- a/drivers/net/wireless/ath/ath6kl/sdio.c
+++ b/drivers/net/wireless/ath/ath6kl/sdio.c
@@ -779,7 +779,7 @@ out:
return ret;
}
-static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+static int ath6kl_set_sdio_pm_caps(struct ath6kl *ar)
{
struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
struct sdio_func *func = ar_sdio->func;
@@ -790,60 +790,82 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sdio suspend pm_caps 0x%x\n", flags);
- if (!(flags & MMC_PM_KEEP_POWER) ||
- (ar->conf_flags & ATH6KL_CONF_SUSPEND_CUTPOWER)) {
- /* as host doesn't support keep power we need to cut power */
- return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER,
- NULL);
- }
+ if (!(flags & MMC_PM_WAKE_SDIO_IRQ) ||
+ !(flags & MMC_PM_KEEP_POWER))
+ return -EINVAL;
ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
if (ret) {
- printk(KERN_ERR "ath6kl: set sdio pm flags failed: %d\n",
- ret);
+ ath6kl_err("set sdio keep pwr flag failed: %d\n", ret);
return ret;
}
- if (!(flags & MMC_PM_WAKE_SDIO_IRQ))
- goto deepsleep;
-
/* sdio irq wakes up host */
+ ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
+ if (ret)
+ ath6kl_err("set sdio wake irq flag failed: %d\n", ret);
+
+ return ret;
+}
+
+static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+{
+ struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar);
+ struct sdio_func *func = ar_sdio->func;
+ mmc_pm_flag_t flags;
+ int ret;
if (ar->state == ATH6KL_STATE_SCHED_SCAN) {
+ ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sched scan is in progress\n");
+
+ ret = ath6kl_set_sdio_pm_caps(ar);
+ if (ret)
+ goto cut_pwr;
+
ret = ath6kl_cfg80211_suspend(ar,
ATH6KL_CFG_SUSPEND_SCHED_SCAN,
NULL);
- if (ret) {
- ath6kl_warn("Schedule scan suspend failed: %d", ret);
- return ret;
- }
-
- ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
if (ret)
- ath6kl_warn("set sdio wake irq flag failed: %d\n", ret);
+ goto cut_pwr;
- return ret;
+ return 0;
}
- if (wow) {
- /*
- * The host sdio controller is capable of keep power and
- * sdio irq wake up at this point. It's fine to continue
- * wow suspend operation.
- */
+ if (ar->suspend_mode == WLAN_POWER_STATE_WOW ||
+ (!ar->suspend_mode && wow)) {
+
+ ret = ath6kl_set_sdio_pm_caps(ar);
+ if (ret)
+ goto cut_pwr;
+
ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_WOW, wow);
if (ret)
- return ret;
+ goto cut_pwr;
+
+ return 0;
+ }
+
+ if (ar->suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP ||
+ !ar->suspend_mode) {
- ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
+ flags = sdio_get_host_pm_caps(func);
+ if (!(flags & MMC_PM_KEEP_POWER))
+ goto cut_pwr;
+
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
if (ret)
- ath6kl_err("set sdio wake irq flag failed: %d\n", ret);
+ goto cut_pwr;
- return ret;
+ ret = ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP,
+ NULL);
+ if (ret)
+ goto cut_pwr;
+
+ return 0;
}
-deepsleep:
- return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_DEEPSLEEP, NULL);
+cut_pwr:
+ return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER, NULL);
}
static int ath6kl_sdio_resume(struct ath6kl *ar)
--
1.7.1
From: Raja Mani <[email protected]>
For every WMI_SET_HOST_SLEEP_MODE_CMDID command (send from the host),
the firmware sends WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID as
an acknowledgement to the host.
In order to being sync with the firmware, the host has to wait for
WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENT event before going to
the suspend state. This patch ensures ath6kl_wow_suspend() waits
until it gets this event after sending set host sleep mode command.
This patch adds,
* New command WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID in
WMI event table.
* New WMI function ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx()
to process the event.
* New flag HOST_SLEEP_MODE_CMD_PROCESSED in VIF flags to record
the arrival of the event.
Signed-off-by: Raja Mani <[email protected]>
---
drivers/net/wireless/ath/ath6kl/cfg80211.c | 15 +++++++++++++++
drivers/net/wireless/ath/ath6kl/core.h | 1 +
drivers/net/wireless/ath/ath6kl/wmi.c | 17 +++++++++++++++++
drivers/net/wireless/ath/ath6kl/wmi.h | 2 ++
4 files changed, 35 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index e676cfc..1902e2f 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -1975,11 +1975,26 @@ skip_arp:
if (ret)
return ret;
+ clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
+
ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
ATH6KL_HOST_MODE_ASLEEP);
if (ret)
return ret;
+ left = wait_event_interruptible_timeout(ar->event_wq,
+ test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags),
+ WMI_TIMEOUT);
+ if (left == 0) {
+ ath6kl_warn("timeout, didn't get host sleep cmd "
+ "processed event\n");
+ ret = -ETIMEDOUT;
+ } else if (left < 0) {
+ ath6kl_warn("error while waiting for host sleep cmd "
+ "processed event %d\n", left);
+ ret = left;
+ }
+
if (ar->tx_pending[ar->ctrl_ep]) {
left = wait_event_interruptible_timeout(ar->event_wq,
ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
index ab232c5..998a745 100644
--- a/drivers/net/wireless/ath/ath6kl/core.h
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -451,6 +451,7 @@ enum ath6kl_vif_state {
DTIM_PERIOD_AVAIL,
WLAN_ENABLED,
STATS_UPDATE_PEND,
+ HOST_SLEEP_MODE_CMD_PROCESSED,
};
struct ath6kl_vif {
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c
index 9c8e4df..18fa9aa 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.c
+++ b/drivers/net/wireless/ath/ath6kl/wmi.c
@@ -2590,6 +2590,18 @@ int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx,
return ret;
}
+/* This command has zero length payload */
+static int ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(struct wmi *wmi,
+ struct ath6kl_vif *vif)
+{
+ struct ath6kl *ar = wmi->parent_dev;
+
+ set_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags);
+ wake_up(&ar->event_wq);
+
+ return 0;
+}
+
int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx,
enum ath6kl_wow_mode wow_mode,
u32 filter, u16 host_req_delay)
@@ -3556,6 +3568,11 @@ int ath6kl_wmi_control_rx(struct wmi *wmi, struct sk_buff *skb)
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TX_COMPLETE_EVENTID\n");
ret = ath6kl_wmi_tx_complete_event_rx(datap, len);
break;
+ case WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID:
+ ath6kl_dbg(ATH6KL_DBG_WMI,
+ "WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID");
+ ret = ath6kl_wmi_host_sleep_mode_cmd_prcd_evt_rx(wmi, vif);
+ break;
case WMI_REMAIN_ON_CHNL_EVENTID:
ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_REMAIN_ON_CHNL_EVENTID\n");
ret = ath6kl_wmi_remain_on_chnl_event_rx(wmi, datap, len, vif);
diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h
index 85698ef..e791986 100644
--- a/drivers/net/wireless/ath/ath6kl/wmi.h
+++ b/drivers/net/wireless/ath/ath6kl/wmi.h
@@ -1357,6 +1357,8 @@ enum wmi_event_id {
WMI_P2P_START_SDPD_EVENTID,
WMI_P2P_SDPD_RX_EVENTID,
+ WMI_SET_HOST_SLEEP_MODE_CMD_PROCESSED_EVENTID = 0x1047,
+
WMI_THIN_RESERVED_START_EVENTID = 0x8000,
/* Events in this range are reserved for thinmode */
WMI_THIN_RESERVED_END_EVENTID = 0x8fff,
--
1.7.1
From: Raja Mani <[email protected]>
Error code ENOTCONN is more suitable than EINVAL to report
when the driver is not in connected state in ath6kl_wow_suspend().
I found this during code review.
Signed-off-by: Raja Mani <[email protected]>
---
drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 1902e2f..5934e40 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -1916,7 +1916,7 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
return -EIO;
if (!test_bit(CONNECTED, &vif->flags))
- return -EINVAL;
+ return -ENOTCONN;
if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
return -EINVAL;
--
1.7.1