diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 528529406e49..d877a9766b40 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1948,6 +1948,10 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) if (ret) return ret; + netif_stop_queue(vif->ndev); + + ar->state = ATH6KL_STATE_SUSPENDING; + /* Setup own IP addr for ARP agent. */ in_dev = __in_dev_get_rtnl(vif->ndev); if (!in_dev) @@ -2026,15 +2030,29 @@ static int ath6kl_wow_resume(struct ath6kl *ar) if (!vif) return -EIO; + ar->state = ATH6KL_STATE_RESUMING; + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_HOST_MODE_AWAKE); - return ret; + if (ret) { + ath6kl_warn("Failed to configure host sleep mode for " + "wow resume: %d\n", ret); + ar->state = ATH6KL_STATE_WOW; + return ret; + } + + ar->state = ATH6KL_STATE_ON; + + netif_wake_queue(vif->ndev); + + return 0; } int ath6kl_cfg80211_suspend(struct ath6kl *ar, enum ath6kl_cfg_suspend_mode mode, struct cfg80211_wowlan *wow) { + enum ath6kl_state prev_state; int ret; switch (mode) { @@ -2045,9 +2063,13 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, /* Flush all non control pkts in TX path */ ath6kl_tx_data_cleanup(ar); + prev_state = ar->state; + ret = ath6kl_wow_suspend(ar, wow); - if (ret) + if (ret) { + ar->state = prev_state; return ret; + } ar->state = ATH6KL_STATE_WOW; break; @@ -2120,7 +2142,6 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar) return ret; } - ar->state = ATH6KL_STATE_ON; break; case ATH6KL_STATE_DEEPSLEEP: @@ -2194,6 +2215,9 @@ static int __ath6kl_cfg80211_resume(struct wiphy *wiphy) */ void ath6kl_check_wow_status(struct ath6kl *ar) { + if (ar->state == ATH6KL_STATE_SUSPENDING) + return; + if (ar->state == ATH6KL_STATE_WOW) ath6kl_cfg80211_resume(ar); } diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index b9d810f32006..31f13d7a0430 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -537,6 +537,8 @@ enum ath6kl_dev_state { enum ath6kl_state { ATH6KL_STATE_OFF, ATH6KL_STATE_ON, + ATH6KL_STATE_SUSPENDING, + ATH6KL_STATE_RESUMING, ATH6KL_STATE_DEEPSLEEP, ATH6KL_STATE_CUTPOWER, ATH6KL_STATE_WOW, diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 9b6282a14225..5b36086a56cf 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -932,8 +932,15 @@ static int ath6kl_sdio_resume(struct ath6kl *ar) case ATH6KL_STATE_WOW: break; + case ATH6KL_STATE_SCHED_SCAN: break; + + case ATH6KL_STATE_SUSPENDING: + break; + + case ATH6KL_STATE_RESUMING: + break; } ath6kl_cfg80211_resume(ar); diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 6754441105ef..b05f3537cfd9 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -285,6 +285,9 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb, int status = 0; struct ath6kl_cookie *cookie = NULL; + if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) + return -EACCES; + spin_lock_bh(&ar->lock); ath6kl_dbg(ATH6KL_DBG_WLAN_TX, @@ -360,6 +363,11 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) return 0; } + if (WARN_ON_ONCE(ar->state != ATH6KL_STATE_ON)) { + dev_kfree_skb(skb); + return 0; + } + if (!test_bit(WMI_READY, &ar->flag)) goto fail_tx;