|
@@ -611,7 +611,7 @@ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id)
|
|
|
|
|
|
ret = ath10k_vdev_setup_sync(ar);
|
|
ret = ath10k_vdev_setup_sync(ar);
|
|
if (ret) {
|
|
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);
|
|
vdev_id, ret);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -658,7 +658,7 @@ static int ath10k_monitor_vdev_stop(struct ath10k *ar)
|
|
|
|
|
|
ret = ath10k_vdev_setup_sync(ar);
|
|
ret = ath10k_vdev_setup_sync(ar);
|
|
if (ret)
|
|
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);
|
|
ar->monitor_vdev_id, ret);
|
|
|
|
|
|
ath10k_dbg(ar, ATH10K_DBG_MAC, "mac monitor vdev %i stopped\n",
|
|
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);
|
|
ret = ath10k_vdev_setup_sync(ar);
|
|
if (ret) {
|
|
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;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -966,7 +967,7 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif)
|
|
|
|
|
|
ret = ath10k_vdev_setup_sync(ar);
|
|
ret = ath10k_vdev_setup_sync(ar);
|
|
if (ret) {
|
|
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);
|
|
arvif->vdev_id, ret);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -1253,6 +1254,20 @@ static int ath10k_mac_vif_recalc_ps_poll_count(struct ath10k_vif *arvif)
|
|
return 0;
|
|
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)
|
|
static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif)
|
|
{
|
|
{
|
|
struct ath10k *ar = arvif->ar;
|
|
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;
|
|
enum wmi_sta_ps_mode psmode;
|
|
int ret;
|
|
int ret;
|
|
int ps_timeout;
|
|
int ps_timeout;
|
|
|
|
+ bool enable_ps;
|
|
|
|
|
|
lockdep_assert_held(&arvif->ar->conf_mutex);
|
|
lockdep_assert_held(&arvif->ar->conf_mutex);
|
|
|
|
|
|
if (arvif->vif->type != NL80211_IFTYPE_STATION)
|
|
if (arvif->vif->type != NL80211_IFTYPE_STATION)
|
|
return 0;
|
|
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;
|
|
psmode = WMI_STA_PS_MODE_ENABLED;
|
|
param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
|
|
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]);
|
|
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 */
|
|
/* can be called only in mac80211 callbacks due to `key_count` usage */
|
|
static void ath10k_bss_assoc(struct ieee80211_hw *hw,
|
|
static void ath10k_bss_assoc(struct ieee80211_hw *hw,
|
|
struct ieee80211_vif *vif,
|
|
struct ieee80211_vif *vif,
|
|
@@ -1789,6 +1877,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
|
|
struct ath10k *ar = hw->priv;
|
|
struct ath10k *ar = hw->priv;
|
|
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
|
|
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
|
|
struct ieee80211_sta_ht_cap ht_cap;
|
|
struct ieee80211_sta_ht_cap ht_cap;
|
|
|
|
+ struct ieee80211_sta_vht_cap vht_cap;
|
|
struct wmi_peer_assoc_complete_arg peer_arg;
|
|
struct wmi_peer_assoc_complete_arg peer_arg;
|
|
struct ieee80211_sta *ap_sta;
|
|
struct ieee80211_sta *ap_sta;
|
|
int ret;
|
|
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
|
|
/* ap_sta must be accessed only within rcu section which must be left
|
|
* before calling ath10k_setup_peer_smps() which might sleep. */
|
|
* before calling ath10k_setup_peer_smps() which might sleep. */
|
|
ht_cap = ap_sta->ht_cap;
|
|
ht_cap = ap_sta->ht_cap;
|
|
|
|
+ vht_cap = ap_sta->vht_cap;
|
|
|
|
|
|
ret = ath10k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg);
|
|
ret = ath10k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg);
|
|
if (ret) {
|
|
if (ret) {
|
|
@@ -1836,6 +1926,13 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
|
|
return;
|
|
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,
|
|
ath10k_dbg(ar, ATH10K_DBG_MAC,
|
|
"mac vdev %d up (associated) bssid %pM aid %d\n",
|
|
"mac vdev %d up (associated) bssid %pM aid %d\n",
|
|
arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
|
|
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;
|
|
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,
|
|
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 *ar = hw->priv;
|
|
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
|
|
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
|
|
|
|
+ struct ieee80211_sta_vht_cap vht_cap = {};
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
lockdep_assert_held(&ar->conf_mutex);
|
|
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;
|
|
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;
|
|
arvif->is_up = false;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2554,6 +2671,17 @@ static int ath10k_start_scan(struct ath10k *ar,
|
|
return -ETIMEDOUT;
|
|
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 */
|
|
/* Add a 200ms margin to account for event/command processing */
|
|
ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout,
|
|
ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout,
|
|
msecs_to_jiffies(arg->max_scan_time+200));
|
|
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);
|
|
list_del(&arvif->list);
|
|
|
|
|
|
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
|
|
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)
|
|
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);
|
|
arvif->vdev_id, ret);
|
|
|
|
|
|
kfree(arvif->u.ap.noa_data);
|
|
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",
|
|
ath10k_warn(ar, "failed to delete WMI vdev %i: %d\n",
|
|
arvif->vdev_id, ret);
|
|
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);
|
|
ath10k_peer_cleanup(ar, arvif->vdev_id);
|
|
|
|
|
|
mutex_unlock(&ar->conf_mutex);
|
|
mutex_unlock(&ar->conf_mutex);
|
|
@@ -3534,7 +3678,9 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
|
|
}
|
|
}
|
|
|
|
|
|
if (changed & BSS_CHANGED_PS) {
|
|
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)
|
|
if (ret)
|
|
ath10k_warn(ar, "failed to setup ps on vdev %i: %d\n",
|
|
ath10k_warn(ar, "failed to setup ps on vdev %i: %d\n",
|
|
arvif->vdev_id, ret);
|
|
arvif->vdev_id, ret);
|