diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath10k/mac.c')
-rw-r--r-- | drivers/net/wireless/ath/ath10k/mac.c | 164 |
1 files changed, 155 insertions, 9 deletions
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index d6d2f0f00caa..8ef64a6d931e 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -611,7 +611,7 @@ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id) ret = ath10k_vdev_setup_sync(ar); if (ret) { - ath10k_warn(ar, "failed to synchronize setup for monitor vdev %i: %d\n", + ath10k_warn(ar, "failed to synchronize setup for monitor vdev %i start: %d\n", vdev_id, ret); return ret; } @@ -658,7 +658,7 @@ static int ath10k_monitor_vdev_stop(struct ath10k *ar) ret = ath10k_vdev_setup_sync(ar); if (ret) - ath10k_warn(ar, "failed to synchronise monitor vdev %i: %d\n", + ath10k_warn(ar, "failed to synchronize monitor vdev %i stop: %d\n", ar->monitor_vdev_id, ret); ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %i stopped\n", @@ -927,8 +927,9 @@ static int ath10k_vdev_start_restart(struct ath10k_vif *arvif, bool restart) ret = ath10k_vdev_setup_sync(ar); if (ret) { - ath10k_warn(ar, "failed to synchronise setup for vdev %i: %d\n", - arg.vdev_id, ret); + ath10k_warn(ar, + "failed to synchronize setup for vdev %i restart %d: %d\n", + arg.vdev_id, restart, ret); return ret; } @@ -966,7 +967,7 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif) ret = ath10k_vdev_setup_sync(ar); if (ret) { - ath10k_warn(ar, "failed to syncronise setup for vdev %i: %d\n", + ath10k_warn(ar, "failed to synchronize setup for vdev %i stop: %d\n", arvif->vdev_id, ret); return ret; } @@ -1253,6 +1254,20 @@ static int ath10k_mac_vif_recalc_ps_poll_count(struct ath10k_vif *arvif) return 0; } +static int ath10k_mac_ps_vif_count(struct ath10k *ar) +{ + struct ath10k_vif *arvif; + int num = 0; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) + if (arvif->ps) + num++; + + return num; +} + static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) { struct ath10k *ar = arvif->ar; @@ -1262,13 +1277,24 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) enum wmi_sta_ps_mode psmode; int ret; int ps_timeout; + bool enable_ps; lockdep_assert_held(&arvif->ar->conf_mutex); if (arvif->vif->type != NL80211_IFTYPE_STATION) return 0; - if (vif->bss_conf.ps) { + enable_ps = arvif->ps; + + if (enable_ps && ath10k_mac_ps_vif_count(ar) > 1 && + !test_bit(ATH10K_FW_FEATURE_MULTI_VIF_PS_SUPPORT, + ar->fw_features)) { + ath10k_warn(ar, "refusing to enable ps on vdev %i: not supported by fw\n", + arvif->vdev_id); + enable_ps = false; + } + + if (enable_ps) { psmode = WMI_STA_PS_MODE_ENABLED; param = WMI_STA_PS_PARAM_INACTIVITY_TIME; @@ -1781,6 +1807,68 @@ static int ath10k_setup_peer_smps(struct ath10k *ar, struct ath10k_vif *arvif, ath10k_smps_map[smps]); } +static int ath10k_mac_vif_recalc_txbf(struct ath10k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta_vht_cap vht_cap) +{ + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + int ret; + u32 param; + u32 value; + + if (!(ar->vht_cap_info & + (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | + IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE))) + return 0; + + param = ar->wmi.vdev_param->txbf; + value = 0; + + if (WARN_ON(param == WMI_VDEV_PARAM_UNSUPPORTED)) + return 0; + + /* The following logic is correct. If a remote STA advertises support + * for being a beamformer then we should enable us being a beamformee. + */ + + if (ar->vht_cap_info & + (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) { + if (vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) + value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE; + + if (vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) + value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFEE; + } + + if (ar->vht_cap_info & + (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)) { + if (vht_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) + value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER; + + if (vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) + value |= WMI_VDEV_PARAM_TXBF_MU_TX_BFER; + } + + if (value & WMI_VDEV_PARAM_TXBF_MU_TX_BFEE) + value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFEE; + + if (value & WMI_VDEV_PARAM_TXBF_MU_TX_BFER) + value |= WMI_VDEV_PARAM_TXBF_SU_TX_BFER; + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, value); + if (ret) { + ath10k_warn(ar, "failed to submit vdev param txbf 0x%x: %d\n", + value, ret); + return ret; + } + + return 0; +} + /* can be called only in mac80211 callbacks due to `key_count` usage */ static void ath10k_bss_assoc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -1789,6 +1877,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); struct ieee80211_sta_ht_cap ht_cap; + struct ieee80211_sta_vht_cap vht_cap; struct wmi_peer_assoc_complete_arg peer_arg; struct ieee80211_sta *ap_sta; int ret; @@ -1811,6 +1900,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, /* ap_sta must be accessed only within rcu section which must be left * before calling ath10k_setup_peer_smps() which might sleep. */ ht_cap = ap_sta->ht_cap; + vht_cap = ap_sta->vht_cap; ret = ath10k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg); if (ret) { @@ -1836,6 +1926,13 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, return; } + ret = ath10k_mac_vif_recalc_txbf(ar, vif, vht_cap); + if (ret) { + ath10k_warn(ar, "failed to recalc txbf for vdev %i on bss %pM: %d\n", + arvif->vdev_id, bss_conf->bssid, ret); + return; + } + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d up (associated) bssid %pM aid %d\n", arvif->vdev_id, bss_conf->bssid, bss_conf->aid); @@ -1853,6 +1950,18 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, } arvif->is_up = true; + + /* Workaround: Some firmware revisions (tested with qca6174 + * WLAN.RM.2.0-00073) have buggy powersave state machine and must be + * poked with peer param command. + */ + ret = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, arvif->bssid, + WMI_PEER_DUMMY_VAR, 1); + if (ret) { + ath10k_warn(ar, "failed to poke peer %pM param for ps workaround on vdev %i: %d\n", + arvif->bssid, arvif->vdev_id, ret); + return; + } } static void ath10k_bss_disassoc(struct ieee80211_hw *hw, @@ -1860,6 +1969,7 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ieee80211_sta_vht_cap vht_cap = {}; int ret; lockdep_assert_held(&ar->conf_mutex); @@ -1874,6 +1984,13 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, arvif->def_wep_key_idx = -1; + ret = ath10k_mac_vif_recalc_txbf(ar, vif, vht_cap); + if (ret) { + ath10k_warn(ar, "failed to recalc txbf for vdev %i: %d\n", + arvif->vdev_id, ret); + return; + } + arvif->is_up = false; } @@ -2554,6 +2671,17 @@ static int ath10k_start_scan(struct ath10k *ar, return -ETIMEDOUT; } + /* If we failed to start the scan, return error code at + * this point. This is probably due to some issue in the + * firmware, but no need to wedge the driver due to that... + */ + spin_lock_bh(&ar->data_lock); + if (ar->scan.state == ATH10K_SCAN_IDLE) { + spin_unlock_bh(&ar->data_lock); + return -EINVAL; + } + spin_unlock_bh(&ar->data_lock); + /* Add a 200ms margin to account for event/command processing */ ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout, msecs_to_jiffies(arg->max_scan_time+200)); @@ -3323,9 +3451,10 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, list_del(&arvif->list); if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { - ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr); + ret = ath10k_wmi_peer_delete(arvif->ar, arvif->vdev_id, + vif->addr); if (ret) - ath10k_warn(ar, "failed to remove peer for AP vdev %i: %d\n", + ath10k_warn(ar, "failed to submit AP self-peer removal on vdev %i: %d\n", arvif->vdev_id, ret); kfree(arvif->u.ap.noa_data); @@ -3339,6 +3468,21 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, ath10k_warn(ar, "failed to delete WMI vdev %i: %d\n", arvif->vdev_id, ret); + /* Some firmware revisions don't notify host about self-peer removal + * until after associated vdev is deleted. + */ + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + ret = ath10k_wait_for_peer_deleted(ar, arvif->vdev_id, + vif->addr); + if (ret) + ath10k_warn(ar, "failed to remove AP self-peer on vdev %i: %d\n", + arvif->vdev_id, ret); + + spin_lock_bh(&ar->data_lock); + ar->num_peers--; + spin_unlock_bh(&ar->data_lock); + } + ath10k_peer_cleanup(ar, arvif->vdev_id); mutex_unlock(&ar->conf_mutex); @@ -3534,7 +3678,9 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, } if (changed & BSS_CHANGED_PS) { - ret = ath10k_mac_vif_setup_ps(arvif); + arvif->ps = vif->bss_conf.ps; + + ret = ath10k_config_ps(ar); if (ret) ath10k_warn(ar, "failed to setup ps on vdev %i: %d\n", arvif->vdev_id, ret); |