|
@@ -2092,33 +2092,16 @@ static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
|
|
|
+static int ath6kl_wow_suspend_vif(struct ath6kl_vif *vif,
|
|
|
+ struct cfg80211_wowlan *wow, u32 *filter)
|
|
|
{
|
|
|
+ struct ath6kl *ar = vif->ar;
|
|
|
struct in_device *in_dev;
|
|
|
struct in_ifaddr *ifa;
|
|
|
- struct ath6kl_vif *vif;
|
|
|
int ret;
|
|
|
- u32 filter = 0;
|
|
|
u16 i, bmiss_time;
|
|
|
- u8 index = 0;
|
|
|
__be32 ips[MAX_IP_ADDRS];
|
|
|
-
|
|
|
- /* The FW currently can't support multi-vif WoW properly. */
|
|
|
- if (ar->num_vif > 1)
|
|
|
- return -EIO;
|
|
|
-
|
|
|
- vif = ath6kl_vif_first(ar);
|
|
|
- if (!vif)
|
|
|
- return -EIO;
|
|
|
-
|
|
|
- if (!ath6kl_cfg80211_ready(vif))
|
|
|
- return -EIO;
|
|
|
-
|
|
|
- if (!test_bit(CONNECTED, &vif->flags))
|
|
|
- return -ENOTCONN;
|
|
|
-
|
|
|
- if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
|
|
|
- return -EINVAL;
|
|
|
+ u8 index = 0;
|
|
|
|
|
|
if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags) &&
|
|
|
test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
|
|
@@ -2140,7 +2123,7 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
|
|
|
* the user.
|
|
|
*/
|
|
|
if (wow)
|
|
|
- ret = ath6kl_wow_usr(ar, vif, wow, &filter);
|
|
|
+ ret = ath6kl_wow_usr(ar, vif, wow, filter);
|
|
|
else if (vif->nw_type == AP_NETWORK)
|
|
|
ret = ath6kl_wow_ap(ar, vif);
|
|
|
else
|
|
@@ -2175,12 +2158,10 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- ar->state = ATH6KL_STATE_SUSPENDING;
|
|
|
-
|
|
|
/* Setup own IP addr for ARP agent. */
|
|
|
in_dev = __in_dev_get_rtnl(vif->ndev);
|
|
|
if (!in_dev)
|
|
|
- goto skip_arp;
|
|
|
+ return 0;
|
|
|
|
|
|
ifa = in_dev->ifa_list;
|
|
|
memset(&ips, 0, sizeof(ips));
|
|
@@ -2203,41 +2184,61 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-skip_arp:
|
|
|
- ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
|
|
|
+{
|
|
|
+ struct ath6kl_vif *first_vif, *vif;
|
|
|
+ int ret = 0;
|
|
|
+ u32 filter = 0;
|
|
|
+ bool connected = false;
|
|
|
+
|
|
|
+ /* enter / leave wow suspend on first vif always */
|
|
|
+ first_vif = ath6kl_vif_first(ar);
|
|
|
+ if (WARN_ON(unlikely(!first_vif)) ||
|
|
|
+ !ath6kl_cfg80211_ready(first_vif))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* install filters for each connected vif */
|
|
|
+ spin_lock_bh(&ar->list_lock);
|
|
|
+ list_for_each_entry(vif, &ar->vif_list, list) {
|
|
|
+ if (!test_bit(CONNECTED, &vif->flags) ||
|
|
|
+ !ath6kl_cfg80211_ready(vif))
|
|
|
+ continue;
|
|
|
+ connected = true;
|
|
|
+
|
|
|
+ ret = ath6kl_wow_suspend_vif(vif, wow, &filter);
|
|
|
+ if (ret)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ spin_unlock_bh(&ar->list_lock);
|
|
|
+
|
|
|
+ if (!connected)
|
|
|
+ return -ENOTCONN;
|
|
|
+ else if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ar->state = ATH6KL_STATE_SUSPENDING;
|
|
|
+
|
|
|
+ ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, first_vif->fw_vif_idx,
|
|
|
ATH6KL_WOW_MODE_ENABLE,
|
|
|
filter,
|
|
|
WOW_HOST_REQ_DELAY);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- ret = ath6kl_cfg80211_host_sleep(ar, vif);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
-
|
|
|
- return 0;
|
|
|
+ return ath6kl_cfg80211_host_sleep(ar, first_vif);
|
|
|
}
|
|
|
|
|
|
-static int ath6kl_wow_resume(struct ath6kl *ar)
|
|
|
+static int ath6kl_wow_resume_vif(struct ath6kl_vif *vif)
|
|
|
{
|
|
|
- struct ath6kl_vif *vif;
|
|
|
+ struct ath6kl *ar = vif->ar;
|
|
|
int ret;
|
|
|
|
|
|
- vif = ath6kl_vif_first(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);
|
|
|
- if (ret) {
|
|
|
- ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n",
|
|
|
- ret);
|
|
|
- ar->state = ATH6KL_STATE_WOW;
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
if (vif->nw_type != AP_NETWORK) {
|
|
|
ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
|
|
|
0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
|
|
@@ -2255,13 +2256,11 @@ static int ath6kl_wow_resume(struct ath6kl *ar)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- ar->state = ATH6KL_STATE_ON;
|
|
|
-
|
|
|
if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags) &&
|
|
|
test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER,
|
|
|
ar->fw_capabilities)) {
|
|
|
ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi,
|
|
|
- vif->fw_vif_idx, true);
|
|
|
+ vif->fw_vif_idx, true);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
}
|
|
@@ -2271,6 +2270,48 @@ static int ath6kl_wow_resume(struct ath6kl *ar)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int ath6kl_wow_resume(struct ath6kl *ar)
|
|
|
+{
|
|
|
+ struct ath6kl_vif *vif;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ vif = ath6kl_vif_first(ar);
|
|
|
+ if (WARN_ON(unlikely(!vif)) ||
|
|
|
+ !ath6kl_cfg80211_ready(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);
|
|
|
+ if (ret) {
|
|
|
+ ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n",
|
|
|
+ ret);
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_lock_bh(&ar->list_lock);
|
|
|
+ list_for_each_entry(vif, &ar->vif_list, list) {
|
|
|
+ if (!test_bit(CONNECTED, &vif->flags) ||
|
|
|
+ !ath6kl_cfg80211_ready(vif))
|
|
|
+ continue;
|
|
|
+ ret = ath6kl_wow_resume_vif(vif);
|
|
|
+ if (ret)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ spin_unlock_bh(&ar->list_lock);
|
|
|
+
|
|
|
+ if (ret)
|
|
|
+ goto cleanup;
|
|
|
+
|
|
|
+ ar->state = ATH6KL_STATE_ON;
|
|
|
+ return 0;
|
|
|
+
|
|
|
+cleanup:
|
|
|
+ ar->state = ATH6KL_STATE_WOW;
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar)
|
|
|
{
|
|
|
struct ath6kl_vif *vif;
|