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