wow.c (cdd38c5f1ce4398ec58fec95904b75824daab7b5) | wow.c (ba9177fcef21fa98406e73c472b5ac2eb4ec5f31) |
---|---|
1// SPDX-License-Identifier: BSD-3-Clause-Clear 2/* 3 * Copyright (c) 2020 The Linux Foundation. All rights reserved. 4 */ 5 6#include <linux/delay.h> 7 8#include "mac.h" | 1// SPDX-License-Identifier: BSD-3-Clause-Clear 2/* 3 * Copyright (c) 2020 The Linux Foundation. All rights reserved. 4 */ 5 6#include <linux/delay.h> 7 8#include "mac.h" |
9 10#include <net/mac80211.h> |
|
9#include "core.h" 10#include "hif.h" 11#include "debug.h" 12#include "wmi.h" 13#include "wow.h" 14 | 11#include "core.h" 12#include "hif.h" 13#include "debug.h" 14#include "wmi.h" 15#include "wow.h" 16 |
17static const struct wiphy_wowlan_support ath11k_wowlan_support = { 18 .flags = WIPHY_WOWLAN_DISCONNECT | 19 WIPHY_WOWLAN_MAGIC_PKT, 20 .pattern_min_len = WOW_MIN_PATTERN_SIZE, 21 .pattern_max_len = WOW_MAX_PATTERN_SIZE, 22 .max_pkt_offset = WOW_MAX_PKT_OFFSET, 23}; 24 |
|
15int ath11k_wow_enable(struct ath11k_base *ab) 16{ 17 struct ath11k *ar = ath11k_ab_to_ar(ab, 0); 18 int i, ret; 19 20 clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags); 21 22 for (i = 0; i < ATH11K_WOW_RETRY_NUM; i++) { --- 43 unchanged lines hidden (view full) --- 66 ret = wait_for_completion_timeout(&ab->wow.wakeup_completed, 3 * HZ); 67 if (ret == 0) { 68 ath11k_warn(ab, "timed out while waiting for wow wakeup completion\n"); 69 return -ETIMEDOUT; 70 } 71 72 return 0; 73} | 25int ath11k_wow_enable(struct ath11k_base *ab) 26{ 27 struct ath11k *ar = ath11k_ab_to_ar(ab, 0); 28 int i, ret; 29 30 clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags); 31 32 for (i = 0; i < ATH11K_WOW_RETRY_NUM; i++) { --- 43 unchanged lines hidden (view full) --- 76 ret = wait_for_completion_timeout(&ab->wow.wakeup_completed, 3 * HZ); 77 if (ret == 0) { 78 ath11k_warn(ab, "timed out while waiting for wow wakeup completion\n"); 79 return -ETIMEDOUT; 80 } 81 82 return 0; 83} |
84 85static int ath11k_wow_vif_cleanup(struct ath11k_vif *arvif) 86{ 87 struct ath11k *ar = arvif->ar; 88 int i, ret; 89 90 for (i = 0; i < WOW_EVENT_MAX; i++) { 91 ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0); 92 if (ret) { 93 ath11k_warn(ar->ab, "failed to issue wow wakeup for event %s on vdev %i: %d\n", 94 wow_wakeup_event(i), arvif->vdev_id, ret); 95 return ret; 96 } 97 } 98 99 for (i = 0; i < ar->wow.max_num_patterns; i++) { 100 ret = ath11k_wmi_wow_del_pattern(ar, arvif->vdev_id, i); 101 if (ret) { 102 ath11k_warn(ar->ab, "failed to delete wow pattern %d for vdev %i: %d\n", 103 i, arvif->vdev_id, ret); 104 return ret; 105 } 106 } 107 108 return 0; 109} 110 111static int ath11k_wow_cleanup(struct ath11k *ar) 112{ 113 struct ath11k_vif *arvif; 114 int ret; 115 116 lockdep_assert_held(&ar->conf_mutex); 117 118 list_for_each_entry(arvif, &ar->arvifs, list) { 119 ret = ath11k_wow_vif_cleanup(arvif); 120 if (ret) { 121 ath11k_warn(ar->ab, "failed to clean wow wakeups on vdev %i: %d\n", 122 arvif->vdev_id, ret); 123 return ret; 124 } 125 } 126 127 return 0; 128} 129 130/* Convert a 802.3 format to a 802.11 format. 131 * +------------+-----------+--------+----------------+ 132 * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... | 133 * +------------+-----------+--------+----------------+ 134 * |__ |_______ |____________ |________ 135 * | | | | 136 * +--+------------+----+-----------+---------------+-----------+ 137 * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... | 138 * +--+------------+----+-----------+---------------+-----------+ 139 */ 140static void ath11k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new, 141 const struct cfg80211_pkt_pattern *old) 142{ 143 u8 hdr_8023_pattern[ETH_HLEN] = {}; 144 u8 hdr_8023_bit_mask[ETH_HLEN] = {}; 145 u8 hdr_80211_pattern[WOW_HDR_LEN] = {}; 146 u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {}; 147 148 int total_len = old->pkt_offset + old->pattern_len; 149 int hdr_80211_end_offset; 150 151 struct ieee80211_hdr_3addr *new_hdr_pattern = 152 (struct ieee80211_hdr_3addr *)hdr_80211_pattern; 153 struct ieee80211_hdr_3addr *new_hdr_mask = 154 (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask; 155 struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern; 156 struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask; 157 int hdr_len = sizeof(*new_hdr_pattern); 158 159 struct rfc1042_hdr *new_rfc_pattern = 160 (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len); 161 struct rfc1042_hdr *new_rfc_mask = 162 (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len); 163 int rfc_len = sizeof(*new_rfc_pattern); 164 165 memcpy(hdr_8023_pattern + old->pkt_offset, 166 old->pattern, ETH_HLEN - old->pkt_offset); 167 memcpy(hdr_8023_bit_mask + old->pkt_offset, 168 old->mask, ETH_HLEN - old->pkt_offset); 169 170 /* Copy destination address */ 171 memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN); 172 memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN); 173 174 /* Copy source address */ 175 memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN); 176 memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN); 177 178 /* Copy logic link type */ 179 memcpy(&new_rfc_pattern->snap_type, 180 &old_hdr_pattern->h_proto, 181 sizeof(old_hdr_pattern->h_proto)); 182 memcpy(&new_rfc_mask->snap_type, 183 &old_hdr_mask->h_proto, 184 sizeof(old_hdr_mask->h_proto)); 185 186 /* Compute new pkt_offset */ 187 if (old->pkt_offset < ETH_ALEN) 188 new->pkt_offset = old->pkt_offset + 189 offsetof(struct ieee80211_hdr_3addr, addr1); 190 else if (old->pkt_offset < offsetof(struct ethhdr, h_proto)) 191 new->pkt_offset = old->pkt_offset + 192 offsetof(struct ieee80211_hdr_3addr, addr3) - 193 offsetof(struct ethhdr, h_source); 194 else 195 new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN; 196 197 /* Compute new hdr end offset */ 198 if (total_len > ETH_HLEN) 199 hdr_80211_end_offset = hdr_len + rfc_len; 200 else if (total_len > offsetof(struct ethhdr, h_proto)) 201 hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN; 202 else if (total_len > ETH_ALEN) 203 hdr_80211_end_offset = total_len - ETH_ALEN + 204 offsetof(struct ieee80211_hdr_3addr, addr3); 205 else 206 hdr_80211_end_offset = total_len + 207 offsetof(struct ieee80211_hdr_3addr, addr1); 208 209 new->pattern_len = hdr_80211_end_offset - new->pkt_offset; 210 211 memcpy((u8 *)new->pattern, 212 hdr_80211_pattern + new->pkt_offset, 213 new->pattern_len); 214 memcpy((u8 *)new->mask, 215 hdr_80211_bit_mask + new->pkt_offset, 216 new->pattern_len); 217 218 if (total_len > ETH_HLEN) { 219 /* Copy frame body */ 220 memcpy((u8 *)new->pattern + new->pattern_len, 221 (void *)old->pattern + ETH_HLEN - old->pkt_offset, 222 total_len - ETH_HLEN); 223 memcpy((u8 *)new->mask + new->pattern_len, 224 (void *)old->mask + ETH_HLEN - old->pkt_offset, 225 total_len - ETH_HLEN); 226 227 new->pattern_len += total_len - ETH_HLEN; 228 } 229} 230 231static int ath11k_vif_wow_set_wakeups(struct ath11k_vif *arvif, 232 struct cfg80211_wowlan *wowlan) 233{ 234 int ret, i; 235 unsigned long wow_mask = 0; 236 struct ath11k *ar = arvif->ar; 237 const struct cfg80211_pkt_pattern *patterns = wowlan->patterns; 238 int pattern_id = 0; 239 240 /* Setup requested WOW features */ 241 switch (arvif->vdev_type) { 242 case WMI_VDEV_TYPE_IBSS: 243 __set_bit(WOW_BEACON_EVENT, &wow_mask); 244 fallthrough; 245 case WMI_VDEV_TYPE_AP: 246 __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 247 __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 248 __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask); 249 __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask); 250 __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask); 251 __set_bit(WOW_HTT_EVENT, &wow_mask); 252 __set_bit(WOW_RA_MATCH_EVENT, &wow_mask); 253 break; 254 case WMI_VDEV_TYPE_STA: 255 if (wowlan->disconnect) { 256 __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); 257 __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); 258 __set_bit(WOW_BMISS_EVENT, &wow_mask); 259 __set_bit(WOW_CSA_IE_EVENT, &wow_mask); 260 } 261 262 if (wowlan->magic_pkt) 263 __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); 264 break; 265 default: 266 break; 267 } 268 269 for (i = 0; i < wowlan->n_patterns; i++) { 270 u8 bitmask[WOW_MAX_PATTERN_SIZE] = {}; 271 u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {}; 272 u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {}; 273 struct cfg80211_pkt_pattern new_pattern = {}; 274 struct cfg80211_pkt_pattern old_pattern = patterns[i]; 275 int j; 276 277 new_pattern.pattern = ath_pattern; 278 new_pattern.mask = ath_bitmask; 279 if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE) 280 continue; 281 /* convert bytemask to bitmask */ 282 for (j = 0; j < patterns[i].pattern_len; j++) 283 if (patterns[i].mask[j / 8] & BIT(j % 8)) 284 bitmask[j] = 0xff; 285 old_pattern.mask = bitmask; 286 287 if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode == 288 ATH11K_HW_TXRX_NATIVE_WIFI) { 289 if (patterns[i].pkt_offset < ETH_HLEN) { 290 u8 pattern_ext[WOW_MAX_PATTERN_SIZE] = {}; 291 292 memcpy(pattern_ext, old_pattern.pattern, 293 old_pattern.pattern_len); 294 old_pattern.pattern = pattern_ext; 295 ath11k_wow_convert_8023_to_80211(&new_pattern, 296 &old_pattern); 297 } else { 298 new_pattern = old_pattern; 299 new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN; 300 } 301 } 302 303 if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE)) 304 return -EINVAL; 305 306 ret = ath11k_wmi_wow_add_pattern(ar, arvif->vdev_id, 307 pattern_id, 308 new_pattern.pattern, 309 new_pattern.mask, 310 new_pattern.pattern_len, 311 new_pattern.pkt_offset); 312 if (ret) { 313 ath11k_warn(ar->ab, "failed to add pattern %i to vdev %i: %d\n", 314 pattern_id, 315 arvif->vdev_id, ret); 316 return ret; 317 } 318 319 pattern_id++; 320 __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask); 321 } 322 323 for (i = 0; i < WOW_EVENT_MAX; i++) { 324 if (!test_bit(i, &wow_mask)) 325 continue; 326 ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1); 327 if (ret) { 328 ath11k_warn(ar->ab, "failed to enable wakeup event %s on vdev %i: %d\n", 329 wow_wakeup_event(i), arvif->vdev_id, ret); 330 return ret; 331 } 332 } 333 334 return 0; 335} 336 337static int ath11k_wow_set_wakeups(struct ath11k *ar, 338 struct cfg80211_wowlan *wowlan) 339{ 340 struct ath11k_vif *arvif; 341 int ret; 342 343 lockdep_assert_held(&ar->conf_mutex); 344 345 list_for_each_entry(arvif, &ar->arvifs, list) { 346 ret = ath11k_vif_wow_set_wakeups(arvif, wowlan); 347 if (ret) { 348 ath11k_warn(ar->ab, "failed to set wow wakeups on vdev %i: %d\n", 349 arvif->vdev_id, ret); 350 return ret; 351 } 352 } 353 354 return 0; 355} 356 357int ath11k_wow_op_suspend(struct ieee80211_hw *hw, 358 struct cfg80211_wowlan *wowlan) 359{ 360 struct ath11k *ar = hw->priv; 361 int ret; 362 363 mutex_lock(&ar->conf_mutex); 364 365 ret = ath11k_wow_cleanup(ar); 366 if (ret) { 367 ath11k_warn(ar->ab, "failed to clear wow wakeup events: %d\n", 368 ret); 369 goto exit; 370 } 371 372 ret = ath11k_wow_set_wakeups(ar, wowlan); 373 if (ret) { 374 ath11k_warn(ar->ab, "failed to set wow wakeup events: %d\n", 375 ret); 376 goto cleanup; 377 } 378 379 ret = ath11k_mac_wait_tx_complete(ar); 380 if (ret) { 381 ath11k_warn(ar->ab, "failed to wait tx complete: %d\n", ret); 382 goto cleanup; 383 } 384 385 ret = ath11k_wow_enable(ar->ab); 386 if (ret) { 387 ath11k_warn(ar->ab, "failed to start wow: %d\n", ret); 388 goto cleanup; 389 } 390 391 ath11k_ce_stop_shadow_timers(ar->ab); 392 ath11k_dp_stop_shadow_timers(ar->ab); 393 394 ath11k_hif_irq_disable(ar->ab); 395 ath11k_hif_ce_irq_disable(ar->ab); 396 397 ret = ath11k_hif_suspend(ar->ab); 398 if (ret) { 399 ath11k_warn(ar->ab, "failed to suspend hif: %d\n", ret); 400 goto wakeup; 401 } 402 403 goto exit; 404 405wakeup: 406 ath11k_wow_wakeup(ar->ab); 407 408cleanup: 409 ath11k_wow_cleanup(ar); 410 411exit: 412 mutex_unlock(&ar->conf_mutex); 413 return ret ? 1 : 0; 414} 415 416void ath11k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled) 417{ 418 struct ath11k *ar = hw->priv; 419 420 mutex_lock(&ar->conf_mutex); 421 device_set_wakeup_enable(ar->ab->dev, enabled); 422 mutex_unlock(&ar->conf_mutex); 423} 424 425int ath11k_wow_op_resume(struct ieee80211_hw *hw) 426{ 427 struct ath11k *ar = hw->priv; 428 int ret; 429 430 mutex_lock(&ar->conf_mutex); 431 432 ret = ath11k_hif_resume(ar->ab); 433 if (ret) { 434 ath11k_warn(ar->ab, "failed to resume hif: %d\n", ret); 435 goto exit; 436 } 437 438 ath11k_hif_ce_irq_enable(ar->ab); 439 ath11k_hif_irq_enable(ar->ab); 440 441 ret = ath11k_wow_wakeup(ar->ab); 442 if (ret) 443 ath11k_warn(ar->ab, "failed to wakeup from wow: %d\n", ret); 444 445exit: 446 if (ret) { 447 switch (ar->state) { 448 case ATH11K_STATE_ON: 449 ar->state = ATH11K_STATE_RESTARTING; 450 ret = 1; 451 break; 452 case ATH11K_STATE_OFF: 453 case ATH11K_STATE_RESTARTING: 454 case ATH11K_STATE_RESTARTED: 455 case ATH11K_STATE_WEDGED: 456 ath11k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n", 457 ar->state); 458 ret = -EIO; 459 break; 460 } 461 } 462 463 mutex_unlock(&ar->conf_mutex); 464 return ret; 465} 466 467int ath11k_wow_init(struct ath11k *ar) 468{ 469 if (WARN_ON(!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map))) 470 return -EINVAL; 471 472 ar->wow.wowlan_support = ath11k_wowlan_support; 473 474 if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode == 475 ATH11K_HW_TXRX_NATIVE_WIFI) { 476 ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE; 477 ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE; 478 } 479 480 ar->wow.max_num_patterns = ATH11K_WOW_PATTERNS; 481 ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; 482 ar->hw->wiphy->wowlan = &ar->wow.wowlan_support; 483 484 device_set_wakeup_capable(ar->ab->dev, true); 485 486 return 0; 487} |
|