123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- /*
- * Copyright (c) 2015 Qualcomm Atheros, Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #include "mac.h"
- #include <net/mac80211.h>
- #include "hif.h"
- #include "core.h"
- #include "debug.h"
- #include "wmi.h"
- #include "wmi-ops.h"
- static const struct wiphy_wowlan_support ath10k_wowlan_support = {
- .flags = WIPHY_WOWLAN_DISCONNECT |
- WIPHY_WOWLAN_MAGIC_PKT,
- .pattern_min_len = WOW_MIN_PATTERN_SIZE,
- .pattern_max_len = WOW_MAX_PATTERN_SIZE,
- .max_pkt_offset = WOW_MAX_PKT_OFFSET,
- };
- static int ath10k_wow_vif_cleanup(struct ath10k_vif *arvif)
- {
- struct ath10k *ar = arvif->ar;
- int i, ret;
- for (i = 0; i < WOW_EVENT_MAX; i++) {
- ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
- if (ret) {
- ath10k_warn(ar, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
- wow_wakeup_event(i), arvif->vdev_id, ret);
- return ret;
- }
- }
- for (i = 0; i < ar->wow.max_num_patterns; i++) {
- ret = ath10k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
- if (ret) {
- ath10k_warn(ar, "failed to delete wow pattern %d for vdev %i: %d\n",
- i, arvif->vdev_id, ret);
- return ret;
- }
- }
- return 0;
- }
- static int ath10k_wow_cleanup(struct ath10k *ar)
- {
- struct ath10k_vif *arvif;
- int ret;
- lockdep_assert_held(&ar->conf_mutex);
- list_for_each_entry(arvif, &ar->arvifs, list) {
- ret = ath10k_wow_vif_cleanup(arvif);
- if (ret) {
- ath10k_warn(ar, "failed to clean wow wakeups on vdev %i: %d\n",
- arvif->vdev_id, ret);
- return ret;
- }
- }
- return 0;
- }
- static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
- struct cfg80211_wowlan *wowlan)
- {
- int ret, i;
- unsigned long wow_mask = 0;
- struct ath10k *ar = arvif->ar;
- const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
- int pattern_id = 0;
- /* Setup requested WOW features */
- switch (arvif->vdev_type) {
- case WMI_VDEV_TYPE_IBSS:
- __set_bit(WOW_BEACON_EVENT, &wow_mask);
- /* fall through */
- case WMI_VDEV_TYPE_AP:
- __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
- __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
- __set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
- __set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
- __set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
- __set_bit(WOW_HTT_EVENT, &wow_mask);
- __set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
- break;
- case WMI_VDEV_TYPE_STA:
- if (wowlan->disconnect) {
- __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
- __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
- __set_bit(WOW_BMISS_EVENT, &wow_mask);
- __set_bit(WOW_CSA_IE_EVENT, &wow_mask);
- }
- if (wowlan->magic_pkt)
- __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
- break;
- default:
- break;
- }
- for (i = 0; i < wowlan->n_patterns; i++) {
- u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
- int j;
- if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
- continue;
- /* convert bytemask to bitmask */
- for (j = 0; j < patterns[i].pattern_len; j++)
- if (patterns[i].mask[j / 8] & BIT(j % 8))
- bitmask[j] = 0xff;
- ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
- pattern_id,
- patterns[i].pattern,
- bitmask,
- patterns[i].pattern_len,
- patterns[i].pkt_offset);
- if (ret) {
- ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",
- pattern_id,
- arvif->vdev_id, ret);
- return ret;
- }
- pattern_id++;
- __set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
- }
- for (i = 0; i < WOW_EVENT_MAX; i++) {
- if (!test_bit(i, &wow_mask))
- continue;
- ret = ath10k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
- if (ret) {
- ath10k_warn(ar, "failed to enable wakeup event %s on vdev %i: %d\n",
- wow_wakeup_event(i), arvif->vdev_id, ret);
- return ret;
- }
- }
- return 0;
- }
- static int ath10k_wow_set_wakeups(struct ath10k *ar,
- struct cfg80211_wowlan *wowlan)
- {
- struct ath10k_vif *arvif;
- int ret;
- lockdep_assert_held(&ar->conf_mutex);
- list_for_each_entry(arvif, &ar->arvifs, list) {
- ret = ath10k_vif_wow_set_wakeups(arvif, wowlan);
- if (ret) {
- ath10k_warn(ar, "failed to set wow wakeups on vdev %i: %d\n",
- arvif->vdev_id, ret);
- return ret;
- }
- }
- return 0;
- }
- static int ath10k_wow_enable(struct ath10k *ar)
- {
- int ret;
- lockdep_assert_held(&ar->conf_mutex);
- reinit_completion(&ar->target_suspend);
- ret = ath10k_wmi_wow_enable(ar);
- if (ret) {
- ath10k_warn(ar, "failed to issue wow enable: %d\n", ret);
- return ret;
- }
- ret = wait_for_completion_timeout(&ar->target_suspend, 3 * HZ);
- if (ret == 0) {
- ath10k_warn(ar, "timed out while waiting for suspend completion\n");
- return -ETIMEDOUT;
- }
- return 0;
- }
- static int ath10k_wow_wakeup(struct ath10k *ar)
- {
- int ret;
- lockdep_assert_held(&ar->conf_mutex);
- reinit_completion(&ar->wow.wakeup_completed);
- ret = ath10k_wmi_wow_host_wakeup_ind(ar);
- if (ret) {
- ath10k_warn(ar, "failed to send wow wakeup indication: %d\n",
- ret);
- return ret;
- }
- ret = wait_for_completion_timeout(&ar->wow.wakeup_completed, 3 * HZ);
- if (ret == 0) {
- ath10k_warn(ar, "timed out while waiting for wow wakeup completion\n");
- return -ETIMEDOUT;
- }
- return 0;
- }
- int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
- struct cfg80211_wowlan *wowlan)
- {
- struct ath10k *ar = hw->priv;
- int ret;
- mutex_lock(&ar->conf_mutex);
- if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
- ar->fw_features))) {
- ret = 1;
- goto exit;
- }
- ret = ath10k_wow_cleanup(ar);
- if (ret) {
- ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
- ret);
- goto exit;
- }
- ret = ath10k_wow_set_wakeups(ar, wowlan);
- if (ret) {
- ath10k_warn(ar, "failed to set wow wakeup events: %d\n",
- ret);
- goto cleanup;
- }
- ret = ath10k_wow_enable(ar);
- if (ret) {
- ath10k_warn(ar, "failed to start wow: %d\n", ret);
- goto cleanup;
- }
- ret = ath10k_hif_suspend(ar);
- if (ret) {
- ath10k_warn(ar, "failed to suspend hif: %d\n", ret);
- goto wakeup;
- }
- goto exit;
- wakeup:
- ath10k_wow_wakeup(ar);
- cleanup:
- ath10k_wow_cleanup(ar);
- exit:
- mutex_unlock(&ar->conf_mutex);
- return ret ? 1 : 0;
- }
- int ath10k_wow_op_resume(struct ieee80211_hw *hw)
- {
- struct ath10k *ar = hw->priv;
- int ret;
- mutex_lock(&ar->conf_mutex);
- if (WARN_ON(!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT,
- ar->fw_features))) {
- ret = 1;
- goto exit;
- }
- ret = ath10k_hif_resume(ar);
- if (ret) {
- ath10k_warn(ar, "failed to resume hif: %d\n", ret);
- goto exit;
- }
- ret = ath10k_wow_wakeup(ar);
- if (ret)
- ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
- exit:
- if (ret) {
- switch (ar->state) {
- case ATH10K_STATE_ON:
- ar->state = ATH10K_STATE_RESTARTING;
- ret = 1;
- break;
- case ATH10K_STATE_OFF:
- case ATH10K_STATE_RESTARTING:
- case ATH10K_STATE_RESTARTED:
- case ATH10K_STATE_UTF:
- case ATH10K_STATE_WEDGED:
- ath10k_warn(ar, "encountered unexpected device state %d on resume, cannot recover\n",
- ar->state);
- ret = -EIO;
- break;
- }
- }
- mutex_unlock(&ar->conf_mutex);
- return ret;
- }
- int ath10k_wow_init(struct ath10k *ar)
- {
- if (!test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, ar->fw_features))
- return 0;
- if (WARN_ON(!test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)))
- return -EINVAL;
- ar->wow.wowlan_support = ath10k_wowlan_support;
- ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
- ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
- return 0;
- }
|