1*5fd3ac3cSJanusz Dziedzic /* 2*5fd3ac3cSJanusz Dziedzic * Copyright (c) 2015 Qualcomm Atheros, Inc. 3*5fd3ac3cSJanusz Dziedzic * 4*5fd3ac3cSJanusz Dziedzic * Permission to use, copy, modify, and/or distribute this software for any 5*5fd3ac3cSJanusz Dziedzic * purpose with or without fee is hereby granted, provided that the above 6*5fd3ac3cSJanusz Dziedzic * copyright notice and this permission notice appear in all copies. 7*5fd3ac3cSJanusz Dziedzic * 8*5fd3ac3cSJanusz Dziedzic * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9*5fd3ac3cSJanusz Dziedzic * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10*5fd3ac3cSJanusz Dziedzic * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11*5fd3ac3cSJanusz Dziedzic * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12*5fd3ac3cSJanusz Dziedzic * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13*5fd3ac3cSJanusz Dziedzic * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14*5fd3ac3cSJanusz Dziedzic * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15*5fd3ac3cSJanusz Dziedzic */ 16*5fd3ac3cSJanusz Dziedzic 17*5fd3ac3cSJanusz Dziedzic #include "mac.h" 18*5fd3ac3cSJanusz Dziedzic 19*5fd3ac3cSJanusz Dziedzic #include <net/mac80211.h> 20*5fd3ac3cSJanusz Dziedzic #include "hif.h" 21*5fd3ac3cSJanusz Dziedzic #include "core.h" 22*5fd3ac3cSJanusz Dziedzic #include "debug.h" 23*5fd3ac3cSJanusz Dziedzic #include "wmi.h" 24*5fd3ac3cSJanusz Dziedzic #include "wmi-ops.h" 25*5fd3ac3cSJanusz Dziedzic 26*5fd3ac3cSJanusz Dziedzic static const struct wiphy_wowlan_support ath10k_wowlan_support = { 27*5fd3ac3cSJanusz Dziedzic .flags = WIPHY_WOWLAN_DISCONNECT | 28*5fd3ac3cSJanusz Dziedzic WIPHY_WOWLAN_MAGIC_PKT, 29*5fd3ac3cSJanusz Dziedzic }; 30*5fd3ac3cSJanusz Dziedzic 31*5fd3ac3cSJanusz Dziedzic static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif) 32*5fd3ac3cSJanusz Dziedzic { 33*5fd3ac3cSJanusz Dziedzic struct ath10k *ar = arvif->ar; 34*5fd3ac3cSJanusz Dziedzic int i, ret; 35*5fd3ac3cSJanusz Dziedzic 36*5fd3ac3cSJanusz Dziedzic for (i = 0; i < WOW_EVENT_MAX; i++) { 37*5fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0); 38*5fd3ac3cSJanusz Dziedzic if (ret) { 39*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n", 40*5fd3ac3cSJanusz Dziedzic wow_wakeup_event(i), arvif->vdev_id, ret); 41*5fd3ac3cSJanusz Dziedzic return ret; 42*5fd3ac3cSJanusz Dziedzic } 43*5fd3ac3cSJanusz Dziedzic } 44*5fd3ac3cSJanusz Dziedzic 45*5fd3ac3cSJanusz Dziedzic return 0; 46*5fd3ac3cSJanusz Dziedzic } 47*5fd3ac3cSJanusz Dziedzic 48*5fd3ac3cSJanusz Dziedzic static int ath10k_wow_cleanup(struct ath10k *ar) 49*5fd3ac3cSJanusz Dziedzic { 50*5fd3ac3cSJanusz Dziedzic struct ath10k_vif *arvif; 51*5fd3ac3cSJanusz Dziedzic int ret; 52*5fd3ac3cSJanusz Dziedzic 53*5fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 54*5fd3ac3cSJanusz Dziedzic 55*5fd3ac3cSJanusz Dziedzic list_for_each_entry(arvif, &ar->arvifs, list) { 56*5fd3ac3cSJanusz Dziedzic ret = ath10k_wow_vif_cleanup(arvif); 57*5fd3ac3cSJanusz Dziedzic if (ret) { 58*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n", 59*5fd3ac3cSJanusz Dziedzic arvif->vdev_id, ret); 60*5fd3ac3cSJanusz Dziedzic return ret; 61*5fd3ac3cSJanusz Dziedzic } 62*5fd3ac3cSJanusz Dziedzic } 63*5fd3ac3cSJanusz Dziedzic 64*5fd3ac3cSJanusz Dziedzic return 0; 65*5fd3ac3cSJanusz Dziedzic } 66*5fd3ac3cSJanusz Dziedzic 67*5fd3ac3cSJanusz Dziedzic static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif, 68*5fd3ac3cSJanusz Dziedzic struct cfg80211_wowlan *wowlan) 69*5fd3ac3cSJanusz Dziedzic { 70*5fd3ac3cSJanusz Dziedzic int ret, i; 71*5fd3ac3cSJanusz Dziedzic unsigned long wow_mask = 0; 72*5fd3ac3cSJanusz Dziedzic struct ath10k *ar = arvif->ar; 73*5fd3ac3cSJanusz Dziedzic 74*5fd3ac3cSJanusz Dziedzic /* Setup requested WOW features */ 75*5fd3ac3cSJanusz Dziedzic switch (arvif->vdev_type) { 76*5fd3ac3cSJanusz Dziedzic case WMI_VDEV_TYPE_IBSS: 77*5fd3ac3cSJanusz Dziedzic __set_bit(WOW_BEACON_EVENT, &wow_mask); 78*5fd3ac3cSJanusz Dziedzic /* fall through */ 79*5fd3ac3cSJanusz Dziedzic case WMI_VDEV_TYPE_AP: 80*5fd3ac3cSJanusz Dziedzic __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 81*5fd3ac3cSJanusz Dziedzic __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 82*5fd3ac3cSJanusz Dziedzic __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask); 83*5fd3ac3cSJanusz Dziedzic __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask); 84*5fd3ac3cSJanusz Dziedzic __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask); 85*5fd3ac3cSJanusz Dziedzic __set_bit(WOW_HTT_EVENT, &wow_mask); 86*5fd3ac3cSJanusz Dziedzic __set_bit(WOW_RA_MATCH_EVENT, &wow_mask); 87*5fd3ac3cSJanusz Dziedzic break; 88*5fd3ac3cSJanusz Dziedzic case WMI_VDEV_TYPE_STA: 89*5fd3ac3cSJanusz Dziedzic if (wowlan->disconnect) { 90*5fd3ac3cSJanusz Dziedzic __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 91*5fd3ac3cSJanusz Dziedzic __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 92*5fd3ac3cSJanusz Dziedzic __set_bit(WOW_BMISS_EVENT, &wow_mask); 93*5fd3ac3cSJanusz Dziedzic __set_bit(WOW_CSA_IE_EVENT, &wow_mask); 94*5fd3ac3cSJanusz Dziedzic } 95*5fd3ac3cSJanusz Dziedzic 96*5fd3ac3cSJanusz Dziedzic if (wowlan->magic_pkt) 97*5fd3ac3cSJanusz Dziedzic __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); 98*5fd3ac3cSJanusz Dziedzic break; 99*5fd3ac3cSJanusz Dziedzic default: 100*5fd3ac3cSJanusz Dziedzic break; 101*5fd3ac3cSJanusz Dziedzic } 102*5fd3ac3cSJanusz Dziedzic 103*5fd3ac3cSJanusz Dziedzic for (i = 0; i < WOW_EVENT_MAX; i++) { 104*5fd3ac3cSJanusz Dziedzic if (!test_bit(i, &wow_mask)) 105*5fd3ac3cSJanusz Dziedzic continue; 106*5fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1); 107*5fd3ac3cSJanusz Dziedzic if (ret) { 108*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n", 109*5fd3ac3cSJanusz Dziedzic wow_wakeup_event(i), arvif->vdev_id, ret); 110*5fd3ac3cSJanusz Dziedzic return ret; 111*5fd3ac3cSJanusz Dziedzic } 112*5fd3ac3cSJanusz Dziedzic } 113*5fd3ac3cSJanusz Dziedzic 114*5fd3ac3cSJanusz Dziedzic return 0; 115*5fd3ac3cSJanusz Dziedzic } 116*5fd3ac3cSJanusz Dziedzic 117*5fd3ac3cSJanusz Dziedzic static int ath10k_wow_set_wakeups(struct ath10k *ar, 118*5fd3ac3cSJanusz Dziedzic struct cfg80211_wowlan *wowlan) 119*5fd3ac3cSJanusz Dziedzic { 120*5fd3ac3cSJanusz Dziedzic struct ath10k_vif *arvif; 121*5fd3ac3cSJanusz Dziedzic int ret; 122*5fd3ac3cSJanusz Dziedzic 123*5fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 124*5fd3ac3cSJanusz Dziedzic 125*5fd3ac3cSJanusz Dziedzic list_for_each_entry(arvif, &ar->arvifs, list) { 126*5fd3ac3cSJanusz Dziedzic ret = ath10k_vif_wow_set_wakeups(arvif, wowlan); 127*5fd3ac3cSJanusz Dziedzic if (ret) { 128*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n", 129*5fd3ac3cSJanusz Dziedzic arvif->vdev_id, ret); 130*5fd3ac3cSJanusz Dziedzic return ret; 131*5fd3ac3cSJanusz Dziedzic } 132*5fd3ac3cSJanusz Dziedzic } 133*5fd3ac3cSJanusz Dziedzic 134*5fd3ac3cSJanusz Dziedzic return 0; 135*5fd3ac3cSJanusz Dziedzic } 136*5fd3ac3cSJanusz Dziedzic 137*5fd3ac3cSJanusz Dziedzic static int ath10k_wow_enable(struct ath10k *ar) 138*5fd3ac3cSJanusz Dziedzic { 139*5fd3ac3cSJanusz Dziedzic int ret; 140*5fd3ac3cSJanusz Dziedzic 141*5fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 142*5fd3ac3cSJanusz Dziedzic 143*5fd3ac3cSJanusz Dziedzic reinit_completion(&ar->target_suspend); 144*5fd3ac3cSJanusz Dziedzic 145*5fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_enable(ar); 146*5fd3ac3cSJanusz Dziedzic if (ret) { 147*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to issue wow enable: %d\n", ret); 148*5fd3ac3cSJanusz Dziedzic return ret; 149*5fd3ac3cSJanusz Dziedzic } 150*5fd3ac3cSJanusz Dziedzic 151*5fd3ac3cSJanusz Dziedzic ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ); 152*5fd3ac3cSJanusz Dziedzic if (ret == 0) { 153*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "timed out while waiting for suspend completion\n"); 154*5fd3ac3cSJanusz Dziedzic return -ETIMEDOUT; 155*5fd3ac3cSJanusz Dziedzic } 156*5fd3ac3cSJanusz Dziedzic 157*5fd3ac3cSJanusz Dziedzic return 0; 158*5fd3ac3cSJanusz Dziedzic } 159*5fd3ac3cSJanusz Dziedzic 160*5fd3ac3cSJanusz Dziedzic static int ath10k_wow_wakeup(struct ath10k *ar) 161*5fd3ac3cSJanusz Dziedzic { 162*5fd3ac3cSJanusz Dziedzic int ret; 163*5fd3ac3cSJanusz Dziedzic 164*5fd3ac3cSJanusz Dziedzic lockdep_assert_held(&ar->conf_mutex); 165*5fd3ac3cSJanusz Dziedzic 166*5fd3ac3cSJanusz Dziedzic reinit_completion(&ar->wow.wakeup_completed); 167*5fd3ac3cSJanusz Dziedzic 168*5fd3ac3cSJanusz Dziedzic ret = ath10k_wmi_wow_host_wakeup_ind(ar); 169*5fd3ac3cSJanusz Dziedzic if (ret) { 170*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to send wow wakeup indication: %d\n", 171*5fd3ac3cSJanusz Dziedzic ret); 172*5fd3ac3cSJanusz Dziedzic return ret; 173*5fd3ac3cSJanusz Dziedzic } 174*5fd3ac3cSJanusz Dziedzic 175*5fd3ac3cSJanusz Dziedzic ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ); 176*5fd3ac3cSJanusz Dziedzic if (ret == 0) { 177*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n"); 178*5fd3ac3cSJanusz Dziedzic return -ETIMEDOUT; 179*5fd3ac3cSJanusz Dziedzic } 180*5fd3ac3cSJanusz Dziedzic 181*5fd3ac3cSJanusz Dziedzic return 0; 182*5fd3ac3cSJanusz Dziedzic } 183*5fd3ac3cSJanusz Dziedzic 184*5fd3ac3cSJanusz Dziedzic int ath10k_wow_op_suspend(struct ieee80211_hw *hw, 185*5fd3ac3cSJanusz Dziedzic struct cfg80211_wowlan *wowlan) 186*5fd3ac3cSJanusz Dziedzic { 187*5fd3ac3cSJanusz Dziedzic struct ath10k *ar = hw->priv; 188*5fd3ac3cSJanusz Dziedzic int ret; 189*5fd3ac3cSJanusz Dziedzic 190*5fd3ac3cSJanusz Dziedzic mutex_lock(&ar->conf_mutex); 191*5fd3ac3cSJanusz Dziedzic 192*5fd3ac3cSJanusz Dziedzic if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 193*5fd3ac3cSJanusz Dziedzic ar->fw_features))) { 194*5fd3ac3cSJanusz Dziedzic ret = 1; 195*5fd3ac3cSJanusz Dziedzic goto exit; 196*5fd3ac3cSJanusz Dziedzic } 197*5fd3ac3cSJanusz Dziedzic 198*5fd3ac3cSJanusz Dziedzic ret = ath10k_wow_cleanup(ar); 199*5fd3ac3cSJanusz Dziedzic if (ret) { 200*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to clear wow wakeup events: %d\n", 201*5fd3ac3cSJanusz Dziedzic ret); 202*5fd3ac3cSJanusz Dziedzic goto exit; 203*5fd3ac3cSJanusz Dziedzic } 204*5fd3ac3cSJanusz Dziedzic 205*5fd3ac3cSJanusz Dziedzic ret = ath10k_wow_set_wakeups(ar, wowlan); 206*5fd3ac3cSJanusz Dziedzic if (ret) { 207*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to set wow wakeup events: %d\n", 208*5fd3ac3cSJanusz Dziedzic ret); 209*5fd3ac3cSJanusz Dziedzic goto cleanup; 210*5fd3ac3cSJanusz Dziedzic } 211*5fd3ac3cSJanusz Dziedzic 212*5fd3ac3cSJanusz Dziedzic ret = ath10k_wow_enable(ar); 213*5fd3ac3cSJanusz Dziedzic if (ret) { 214*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to start wow: %d\n", ret); 215*5fd3ac3cSJanusz Dziedzic goto cleanup; 216*5fd3ac3cSJanusz Dziedzic } 217*5fd3ac3cSJanusz Dziedzic 218*5fd3ac3cSJanusz Dziedzic ret = ath10k_hif_suspend(ar); 219*5fd3ac3cSJanusz Dziedzic if (ret) { 220*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to suspend hif: %d\n", ret); 221*5fd3ac3cSJanusz Dziedzic goto wakeup; 222*5fd3ac3cSJanusz Dziedzic } 223*5fd3ac3cSJanusz Dziedzic 224*5fd3ac3cSJanusz Dziedzic goto exit; 225*5fd3ac3cSJanusz Dziedzic 226*5fd3ac3cSJanusz Dziedzic wakeup: 227*5fd3ac3cSJanusz Dziedzic ath10k_wow_wakeup(ar); 228*5fd3ac3cSJanusz Dziedzic 229*5fd3ac3cSJanusz Dziedzic cleanup: 230*5fd3ac3cSJanusz Dziedzic ath10k_wow_cleanup(ar); 231*5fd3ac3cSJanusz Dziedzic 232*5fd3ac3cSJanusz Dziedzic exit: 233*5fd3ac3cSJanusz Dziedzic mutex_unlock(&ar->conf_mutex); 234*5fd3ac3cSJanusz Dziedzic return ret ? 1 : 0; 235*5fd3ac3cSJanusz Dziedzic } 236*5fd3ac3cSJanusz Dziedzic 237*5fd3ac3cSJanusz Dziedzic int ath10k_wow_op_resume(struct ieee80211_hw *hw) 238*5fd3ac3cSJanusz Dziedzic { 239*5fd3ac3cSJanusz Dziedzic struct ath10k *ar = hw->priv; 240*5fd3ac3cSJanusz Dziedzic int ret; 241*5fd3ac3cSJanusz Dziedzic 242*5fd3ac3cSJanusz Dziedzic mutex_lock(&ar->conf_mutex); 243*5fd3ac3cSJanusz Dziedzic 244*5fd3ac3cSJanusz Dziedzic if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, 245*5fd3ac3cSJanusz Dziedzic ar->fw_features))) { 246*5fd3ac3cSJanusz Dziedzic ret = 1; 247*5fd3ac3cSJanusz Dziedzic goto exit; 248*5fd3ac3cSJanusz Dziedzic } 249*5fd3ac3cSJanusz Dziedzic 250*5fd3ac3cSJanusz Dziedzic ret = ath10k_hif_resume(ar); 251*5fd3ac3cSJanusz Dziedzic if (ret) { 252*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to resume hif: %d\n", ret); 253*5fd3ac3cSJanusz Dziedzic goto exit; 254*5fd3ac3cSJanusz Dziedzic } 255*5fd3ac3cSJanusz Dziedzic 256*5fd3ac3cSJanusz Dziedzic ret = ath10k_wow_wakeup(ar); 257*5fd3ac3cSJanusz Dziedzic if (ret) 258*5fd3ac3cSJanusz Dziedzic ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret); 259*5fd3ac3cSJanusz Dziedzic 260*5fd3ac3cSJanusz Dziedzic exit: 261*5fd3ac3cSJanusz Dziedzic mutex_unlock(&ar->conf_mutex); 262*5fd3ac3cSJanusz Dziedzic return ret ? 1 : 0; 263*5fd3ac3cSJanusz Dziedzic } 264*5fd3ac3cSJanusz Dziedzic 265*5fd3ac3cSJanusz Dziedzic int ath10k_wow_init(struct ath10k *ar) 266*5fd3ac3cSJanusz Dziedzic { 267*5fd3ac3cSJanusz Dziedzic if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, ar->fw_features)) 268*5fd3ac3cSJanusz Dziedzic return 0; 269*5fd3ac3cSJanusz Dziedzic 270*5fd3ac3cSJanusz Dziedzic if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map))) 271*5fd3ac3cSJanusz Dziedzic return -EINVAL; 272*5fd3ac3cSJanusz Dziedzic 273*5fd3ac3cSJanusz Dziedzic ar->hw->wiphy->wowlan = &ath10k_wowlan_support; 274*5fd3ac3cSJanusz Dziedzic 275*5fd3ac3cSJanusz Dziedzic return 0; 276*5fd3ac3cSJanusz Dziedzic } 277