|
@@ -20,6 +20,7 @@
|
|
|
#include <net/mac80211.h>
|
|
|
#include <linux/etherdevice.h>
|
|
|
|
|
|
+#include "hif.h"
|
|
|
#include "core.h"
|
|
|
#include "debug.h"
|
|
|
#include "wmi.h"
|
|
@@ -43,6 +44,8 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
|
|
|
.macaddr = macaddr,
|
|
|
};
|
|
|
|
|
|
+ lockdep_assert_held(&arvif->ar->conf_mutex);
|
|
|
+
|
|
|
if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
|
|
|
arg.key_flags = WMI_KEY_PAIRWISE;
|
|
|
else
|
|
@@ -87,6 +90,8 @@ static int ath10k_install_key(struct ath10k_vif *arvif,
|
|
|
struct ath10k *ar = arvif->ar;
|
|
|
int ret;
|
|
|
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
INIT_COMPLETION(ar->install_key_done);
|
|
|
|
|
|
ret = ath10k_send_key(arvif, key, cmd, macaddr);
|
|
@@ -327,6 +332,29 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
|
|
|
+{
|
|
|
+ if (value != 0xFFFFFFFF)
|
|
|
+ value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold,
|
|
|
+ ATH10K_RTS_MAX);
|
|
|
+
|
|
|
+ return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
|
|
|
+ WMI_VDEV_PARAM_RTS_THRESHOLD,
|
|
|
+ value);
|
|
|
+}
|
|
|
+
|
|
|
+static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value)
|
|
|
+{
|
|
|
+ if (value != 0xFFFFFFFF)
|
|
|
+ value = clamp_t(u32, arvif->ar->hw->wiphy->frag_threshold,
|
|
|
+ ATH10K_FRAGMT_THRESHOLD_MIN,
|
|
|
+ ATH10K_FRAGMT_THRESHOLD_MAX);
|
|
|
+
|
|
|
+ return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id,
|
|
|
+ WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
|
|
|
+ value);
|
|
|
+}
|
|
|
+
|
|
|
static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
|
|
|
{
|
|
|
int ret;
|
|
@@ -364,6 +392,20 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
|
|
|
spin_unlock_bh(&ar->data_lock);
|
|
|
}
|
|
|
|
|
|
+static void ath10k_peer_cleanup_all(struct ath10k *ar)
|
|
|
+{
|
|
|
+ struct ath10k_peer *peer, *tmp;
|
|
|
+
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
|
+ list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
|
|
|
+ list_del(&peer->list);
|
|
|
+ kfree(peer);
|
|
|
+ }
|
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
|
+}
|
|
|
+
|
|
|
/************************/
|
|
|
/* Interface management */
|
|
|
/************************/
|
|
@@ -372,6 +414,8 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
ret = wait_for_completion_timeout(&ar->vdev_setup_done,
|
|
|
ATH10K_VDEV_SETUP_TIMEOUT_HZ);
|
|
|
if (ret == 0)
|
|
@@ -605,6 +649,8 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
|
|
|
{
|
|
|
int ret = 0;
|
|
|
|
|
|
+ lockdep_assert_held(&arvif->ar->conf_mutex);
|
|
|
+
|
|
|
if (!info->enable_beacon) {
|
|
|
ath10k_vdev_stop(arvif);
|
|
|
return;
|
|
@@ -631,6 +677,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
|
|
|
{
|
|
|
int ret = 0;
|
|
|
|
|
|
+ lockdep_assert_held(&arvif->ar->conf_mutex);
|
|
|
+
|
|
|
if (!info->ibss_joined) {
|
|
|
ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
|
|
|
if (ret)
|
|
@@ -680,6 +728,8 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
|
|
|
enum wmi_sta_ps_mode psmode;
|
|
|
int ret;
|
|
|
|
|
|
+ lockdep_assert_held(&arvif->ar->conf_mutex);
|
|
|
+
|
|
|
if (vif->type != NL80211_IFTYPE_STATION)
|
|
|
return;
|
|
|
|
|
@@ -722,6 +772,8 @@ static void ath10k_peer_assoc_h_basic(struct ath10k *ar,
|
|
|
struct ieee80211_bss_conf *bss_conf,
|
|
|
struct wmi_peer_assoc_complete_arg *arg)
|
|
|
{
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
memcpy(arg->addr, sta->addr, ETH_ALEN);
|
|
|
arg->vdev_id = arvif->vdev_id;
|
|
|
arg->peer_aid = sta->aid;
|
|
@@ -764,6 +816,8 @@ static void ath10k_peer_assoc_h_crypto(struct ath10k *ar,
|
|
|
const u8 *rsnie = NULL;
|
|
|
const u8 *wpaie = NULL;
|
|
|
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
|
|
|
info->bssid, NULL, 0, 0, 0);
|
|
|
if (bss) {
|
|
@@ -804,6 +858,8 @@ static void ath10k_peer_assoc_h_rates(struct ath10k *ar,
|
|
|
u32 ratemask;
|
|
|
int i;
|
|
|
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
|
|
|
ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band];
|
|
|
rates = sband->bitrates;
|
|
@@ -827,6 +883,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
|
|
|
int smps;
|
|
|
int i, n;
|
|
|
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
if (!ht_cap->ht_supported)
|
|
|
return;
|
|
|
|
|
@@ -905,6 +963,8 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
|
|
|
u32 uapsd = 0;
|
|
|
u32 max_sp = 0;
|
|
|
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
if (sta->wme)
|
|
|
arg->peer_flags |= WMI_PEER_QOS;
|
|
|
|
|
@@ -1056,6 +1116,8 @@ static int ath10k_peer_assoc(struct ath10k *ar,
|
|
|
{
|
|
|
struct wmi_peer_assoc_complete_arg arg;
|
|
|
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
|
|
|
|
|
|
ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
|
|
@@ -1079,6 +1141,8 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
|
|
|
struct ieee80211_sta *ap_sta;
|
|
|
int ret;
|
|
|
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
rcu_read_lock();
|
|
|
|
|
|
ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
|
|
@@ -1119,6 +1183,8 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
|
|
|
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
|
|
|
int ret;
|
|
|
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
/*
|
|
|
* For some reason, calling VDEV-DOWN before VDEV-STOP
|
|
|
* makes the FW to send frames via HTT after disassociation.
|
|
@@ -1152,6 +1218,8 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
|
|
|
{
|
|
|
int ret = 0;
|
|
|
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
|
|
|
if (ret) {
|
|
|
ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
|
|
@@ -1172,6 +1240,8 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif,
|
|
|
{
|
|
|
int ret = 0;
|
|
|
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
ret = ath10k_clear_peer_keys(arvif, sta->addr);
|
|
|
if (ret) {
|
|
|
ath10k_warn("could not clear all peer wep keys (%d)\n", ret);
|
|
@@ -1198,6 +1268,8 @@ static int ath10k_update_channel_list(struct ath10k *ar)
|
|
|
int ret;
|
|
|
int i;
|
|
|
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
bands = hw->wiphy->bands;
|
|
|
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
|
|
if (!bands[band])
|
|
@@ -1276,21 +1348,19 @@ static int ath10k_update_channel_list(struct ath10k *ar)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void ath10k_reg_notifier(struct wiphy *wiphy,
|
|
|
- struct regulatory_request *request)
|
|
|
+static void ath10k_regd_update(struct ath10k *ar)
|
|
|
{
|
|
|
- struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
|
|
|
struct reg_dmn_pair_mapping *regpair;
|
|
|
- struct ath10k *ar = hw->priv;
|
|
|
int ret;
|
|
|
|
|
|
- ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
|
|
|
ret = ath10k_update_channel_list(ar);
|
|
|
if (ret)
|
|
|
ath10k_warn("could not update channel list (%d)\n", ret);
|
|
|
|
|
|
regpair = ar->ath_common.regulatory.regpair;
|
|
|
+
|
|
|
/* Target allows setting up per-band regdomain but ath_common provides
|
|
|
* a combined one only */
|
|
|
ret = ath10k_wmi_pdev_set_regdomain(ar,
|
|
@@ -1303,6 +1373,20 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
|
|
|
ath10k_warn("could not set pdev regdomain (%d)\n", ret);
|
|
|
}
|
|
|
|
|
|
+static void ath10k_reg_notifier(struct wiphy *wiphy,
|
|
|
+ struct regulatory_request *request)
|
|
|
+{
|
|
|
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
|
|
|
+ struct ath10k *ar = hw->priv;
|
|
|
+
|
|
|
+ ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
|
|
|
+
|
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
|
+ if (ar->state == ATH10K_STATE_ON)
|
|
|
+ ath10k_regd_update(ar);
|
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
|
+}
|
|
|
+
|
|
|
/***************/
|
|
|
/* TX handlers */
|
|
|
/***************/
|
|
@@ -1397,15 +1481,15 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb)
|
|
|
int ret;
|
|
|
|
|
|
if (ieee80211_is_mgmt(hdr->frame_control))
|
|
|
- ret = ath10k_htt_mgmt_tx(ar->htt, skb);
|
|
|
+ ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
|
|
|
else if (ieee80211_is_nullfunc(hdr->frame_control))
|
|
|
/* FW does not report tx status properly for NullFunc frames
|
|
|
* unless they are sent through mgmt tx path. mac80211 sends
|
|
|
* those frames when it detects link/beacon loss and depends on
|
|
|
* the tx status to be correct. */
|
|
|
- ret = ath10k_htt_mgmt_tx(ar->htt, skb);
|
|
|
+ ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
|
|
|
else
|
|
|
- ret = ath10k_htt_tx(ar->htt, skb);
|
|
|
+ ret = ath10k_htt_tx(&ar->htt, skb);
|
|
|
|
|
|
if (ret) {
|
|
|
ath10k_warn("tx failed (%d). dropping packet.\n", ret);
|
|
@@ -1552,6 +1636,10 @@ static int ath10k_abort_scan(struct ath10k *ar)
|
|
|
ret = ath10k_wmi_stop_scan(ar, &arg);
|
|
|
if (ret) {
|
|
|
ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
|
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
|
+ ar->scan.in_progress = false;
|
|
|
+ ath10k_offchan_tx_purge(ar);
|
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
|
return -EIO;
|
|
|
}
|
|
|
|
|
@@ -1645,10 +1733,14 @@ static void ath10k_tx(struct ieee80211_hw *hw,
|
|
|
tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
|
|
|
}
|
|
|
|
|
|
- ath10k_tx_h_qos_workaround(hw, control, skb);
|
|
|
- ath10k_tx_h_update_wep_key(skb);
|
|
|
- ath10k_tx_h_add_p2p_noa_ie(ar, skb);
|
|
|
- ath10k_tx_h_seq_no(skb);
|
|
|
+ /* it makes no sense to process injected frames like that */
|
|
|
+ if (info->control.vif &&
|
|
|
+ info->control.vif->type != NL80211_IFTYPE_MONITOR) {
|
|
|
+ ath10k_tx_h_qos_workaround(hw, control, skb);
|
|
|
+ ath10k_tx_h_update_wep_key(skb);
|
|
|
+ ath10k_tx_h_add_p2p_noa_ie(ar, skb);
|
|
|
+ ath10k_tx_h_seq_no(skb);
|
|
|
+ }
|
|
|
|
|
|
memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb)));
|
|
|
ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id;
|
|
@@ -1673,10 +1765,57 @@ static void ath10k_tx(struct ieee80211_hw *hw,
|
|
|
/*
|
|
|
* Initialize various parameters with default vaules.
|
|
|
*/
|
|
|
+void ath10k_halt(struct ath10k *ar)
|
|
|
+{
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
+ del_timer_sync(&ar->scan.timeout);
|
|
|
+ ath10k_offchan_tx_purge(ar);
|
|
|
+ ath10k_peer_cleanup_all(ar);
|
|
|
+ ath10k_core_stop(ar);
|
|
|
+ ath10k_hif_power_down(ar);
|
|
|
+
|
|
|
+ spin_lock_bh(&ar->data_lock);
|
|
|
+ if (ar->scan.in_progress) {
|
|
|
+ del_timer(&ar->scan.timeout);
|
|
|
+ ar->scan.in_progress = false;
|
|
|
+ ieee80211_scan_completed(ar->hw, true);
|
|
|
+ }
|
|
|
+ spin_unlock_bh(&ar->data_lock);
|
|
|
+}
|
|
|
+
|
|
|
static int ath10k_start(struct ieee80211_hw *hw)
|
|
|
{
|
|
|
struct ath10k *ar = hw->priv;
|
|
|
- int ret;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
|
+
|
|
|
+ if (ar->state != ATH10K_STATE_OFF &&
|
|
|
+ ar->state != ATH10K_STATE_RESTARTING) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto exit;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = ath10k_hif_power_up(ar);
|
|
|
+ if (ret) {
|
|
|
+ ath10k_err("could not init hif (%d)\n", ret);
|
|
|
+ ar->state = ATH10K_STATE_OFF;
|
|
|
+ goto exit;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = ath10k_core_start(ar);
|
|
|
+ if (ret) {
|
|
|
+ ath10k_err("could not init core (%d)\n", ret);
|
|
|
+ ath10k_hif_power_down(ar);
|
|
|
+ ar->state = ATH10K_STATE_OFF;
|
|
|
+ goto exit;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ar->state == ATH10K_STATE_OFF)
|
|
|
+ ar->state = ATH10K_STATE_ON;
|
|
|
+ else if (ar->state == ATH10K_STATE_RESTARTING)
|
|
|
+ ar->state = ATH10K_STATE_RESTARTED;
|
|
|
|
|
|
ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1);
|
|
|
if (ret)
|
|
@@ -1688,6 +1827,10 @@ static int ath10k_start(struct ieee80211_hw *hw)
|
|
|
ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
|
|
|
ret);
|
|
|
|
|
|
+ ath10k_regd_update(ar);
|
|
|
+
|
|
|
+exit:
|
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1695,18 +1838,48 @@ static void ath10k_stop(struct ieee80211_hw *hw)
|
|
|
{
|
|
|
struct ath10k *ar = hw->priv;
|
|
|
|
|
|
- /* avoid leaks in case FW never confirms scan for offchannel */
|
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
|
+ if (ar->state == ATH10K_STATE_ON ||
|
|
|
+ ar->state == ATH10K_STATE_RESTARTED ||
|
|
|
+ ar->state == ATH10K_STATE_WEDGED)
|
|
|
+ ath10k_halt(ar);
|
|
|
+
|
|
|
+ ar->state = ATH10K_STATE_OFF;
|
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
|
+
|
|
|
cancel_work_sync(&ar->offchan_tx_work);
|
|
|
- ath10k_offchan_tx_purge(ar);
|
|
|
+ cancel_work_sync(&ar->restart_work);
|
|
|
}
|
|
|
|
|
|
-static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
|
|
|
+static void ath10k_config_ps(struct ath10k *ar)
|
|
|
{
|
|
|
struct ath10k_generic_iter ar_iter;
|
|
|
+
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
+ /* During HW reconfiguration mac80211 reports all interfaces that were
|
|
|
+ * running until reconfiguration was started. Since FW doesn't have any
|
|
|
+ * vdevs at this point we must not iterate over this interface list.
|
|
|
+ * This setting will be updated upon add_interface(). */
|
|
|
+ if (ar->state == ATH10K_STATE_RESTARTED)
|
|
|
+ return;
|
|
|
+
|
|
|
+ memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
|
|
|
+ ar_iter.ar = ar;
|
|
|
+
|
|
|
+ ieee80211_iterate_active_interfaces_atomic(
|
|
|
+ ar->hw, IEEE80211_IFACE_ITER_NORMAL,
|
|
|
+ ath10k_ps_iter, &ar_iter);
|
|
|
+
|
|
|
+ if (ar_iter.ret)
|
|
|
+ ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret);
|
|
|
+}
|
|
|
+
|
|
|
+static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
|
|
|
+{
|
|
|
struct ath10k *ar = hw->priv;
|
|
|
struct ieee80211_conf *conf = &hw->conf;
|
|
|
int ret = 0;
|
|
|
- u32 flags;
|
|
|
|
|
|
mutex_lock(&ar->conf_mutex);
|
|
|
|
|
@@ -1718,18 +1891,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
|
|
|
spin_unlock_bh(&ar->data_lock);
|
|
|
}
|
|
|
|
|
|
- if (changed & IEEE80211_CONF_CHANGE_PS) {
|
|
|
- memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
|
|
|
- ar_iter.ar = ar;
|
|
|
- flags = IEEE80211_IFACE_ITER_RESUME_ALL;
|
|
|
-
|
|
|
- ieee80211_iterate_active_interfaces_atomic(hw,
|
|
|
- flags,
|
|
|
- ath10k_ps_iter,
|
|
|
- &ar_iter);
|
|
|
-
|
|
|
- ret = ar_iter.ret;
|
|
|
- }
|
|
|
+ if (changed & IEEE80211_CONF_CHANGE_PS)
|
|
|
+ ath10k_config_ps(ar);
|
|
|
|
|
|
if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
|
|
|
if (conf->flags & IEEE80211_CONF_MONITOR)
|
|
@@ -1738,6 +1901,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
|
|
|
ret = ath10k_monitor_destroy(ar);
|
|
|
}
|
|
|
|
|
|
+ ath10k_wmi_flush_tx(ar);
|
|
|
mutex_unlock(&ar->conf_mutex);
|
|
|
return ret;
|
|
|
}
|
|
@@ -1859,6 +2023,16 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
|
|
|
ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
|
|
|
}
|
|
|
|
|
|
+ ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold);
|
|
|
+ if (ret)
|
|
|
+ ath10k_warn("failed to set rts threshold for vdev %d (%d)\n",
|
|
|
+ arvif->vdev_id, ret);
|
|
|
+
|
|
|
+ ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold);
|
|
|
+ if (ret)
|
|
|
+ ath10k_warn("failed to set frag threshold for vdev %d (%d)\n",
|
|
|
+ arvif->vdev_id, ret);
|
|
|
+
|
|
|
if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
|
|
|
ar->monitor_present = true;
|
|
|
|
|
@@ -2363,6 +2537,8 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif,
|
|
|
u32 value = 0;
|
|
|
int ret = 0;
|
|
|
|
|
|
+ lockdep_assert_held(&ar->conf_mutex);
|
|
|
+
|
|
|
if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
|
|
|
return 0;
|
|
|
|
|
@@ -2558,11 +2734,16 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
|
|
|
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
|
|
|
u32 rts = ar_iter->ar->hw->wiphy->rts_threshold;
|
|
|
|
|
|
- rts = min_t(u32, rts, ATH10K_RTS_MAX);
|
|
|
+ lockdep_assert_held(&arvif->ar->conf_mutex);
|
|
|
|
|
|
- ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
|
|
|
- WMI_VDEV_PARAM_RTS_THRESHOLD,
|
|
|
- rts);
|
|
|
+ /* During HW reconfiguration mac80211 reports all interfaces that were
|
|
|
+ * running until reconfiguration was started. Since FW doesn't have any
|
|
|
+ * vdevs at this point we must not iterate over this interface list.
|
|
|
+ * This setting will be updated upon add_interface(). */
|
|
|
+ if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
|
|
|
+ return;
|
|
|
+
|
|
|
+ ar_iter->ret = ath10k_mac_set_rts(arvif, rts);
|
|
|
if (ar_iter->ret)
|
|
|
ath10k_warn("Failed to set RTS threshold for VDEV: %d\n",
|
|
|
arvif->vdev_id);
|
|
@@ -2581,8 +2762,9 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
|
|
|
ar_iter.ar = ar;
|
|
|
|
|
|
mutex_lock(&ar->conf_mutex);
|
|
|
- ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
|
|
- ath10k_set_rts_iter, &ar_iter);
|
|
|
+ ieee80211_iterate_active_interfaces_atomic(
|
|
|
+ hw, IEEE80211_IFACE_ITER_NORMAL,
|
|
|
+ ath10k_set_rts_iter, &ar_iter);
|
|
|
mutex_unlock(&ar->conf_mutex);
|
|
|
|
|
|
return ar_iter.ret;
|
|
@@ -2593,17 +2775,17 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
|
|
|
struct ath10k_generic_iter *ar_iter = data;
|
|
|
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
|
|
|
u32 frag = ar_iter->ar->hw->wiphy->frag_threshold;
|
|
|
- int ret;
|
|
|
|
|
|
- frag = clamp_t(u32, frag,
|
|
|
- ATH10K_FRAGMT_THRESHOLD_MIN,
|
|
|
- ATH10K_FRAGMT_THRESHOLD_MAX);
|
|
|
+ lockdep_assert_held(&arvif->ar->conf_mutex);
|
|
|
|
|
|
- ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
|
|
|
- WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
|
|
|
- frag);
|
|
|
+ /* During HW reconfiguration mac80211 reports all interfaces that were
|
|
|
+ * running until reconfiguration was started. Since FW doesn't have any
|
|
|
+ * vdevs at this point we must not iterate over this interface list.
|
|
|
+ * This setting will be updated upon add_interface(). */
|
|
|
+ if (ar_iter->ar->state == ATH10K_STATE_RESTARTED)
|
|
|
+ return;
|
|
|
|
|
|
- ar_iter->ret = ret;
|
|
|
+ ar_iter->ret = ath10k_mac_set_frag(arvif, frag);
|
|
|
if (ar_iter->ret)
|
|
|
ath10k_warn("Failed to set frag threshold for VDEV: %d\n",
|
|
|
arvif->vdev_id);
|
|
@@ -2622,8 +2804,9 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
|
|
|
ar_iter.ar = ar;
|
|
|
|
|
|
mutex_lock(&ar->conf_mutex);
|
|
|
- ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
|
|
|
- ath10k_set_frag_iter, &ar_iter);
|
|
|
+ ieee80211_iterate_active_interfaces_atomic(
|
|
|
+ hw, IEEE80211_IFACE_ITER_NORMAL,
|
|
|
+ ath10k_set_frag_iter, &ar_iter);
|
|
|
mutex_unlock(&ar->conf_mutex);
|
|
|
|
|
|
return ar_iter.ret;
|
|
@@ -2632,6 +2815,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
|
|
|
static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
|
|
|
{
|
|
|
struct ath10k *ar = hw->priv;
|
|
|
+ bool skip;
|
|
|
int ret;
|
|
|
|
|
|
/* mac80211 doesn't care if we really xmit queued frames or not
|
|
@@ -2639,16 +2823,29 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
|
|
|
if (drop)
|
|
|
return;
|
|
|
|
|
|
- ret = wait_event_timeout(ar->htt->empty_tx_wq, ({
|
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
|
+
|
|
|
+ if (ar->state == ATH10K_STATE_WEDGED)
|
|
|
+ goto skip;
|
|
|
+
|
|
|
+ ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
|
|
|
bool empty;
|
|
|
- spin_lock_bh(&ar->htt->tx_lock);
|
|
|
- empty = bitmap_empty(ar->htt->used_msdu_ids,
|
|
|
- ar->htt->max_num_pending_tx);
|
|
|
- spin_unlock_bh(&ar->htt->tx_lock);
|
|
|
- (empty);
|
|
|
+
|
|
|
+ spin_lock_bh(&ar->htt.tx_lock);
|
|
|
+ empty = bitmap_empty(ar->htt.used_msdu_ids,
|
|
|
+ ar->htt.max_num_pending_tx);
|
|
|
+ spin_unlock_bh(&ar->htt.tx_lock);
|
|
|
+
|
|
|
+ skip = (ar->state == ATH10K_STATE_WEDGED);
|
|
|
+
|
|
|
+ (empty || skip);
|
|
|
}), ATH10K_FLUSH_TIMEOUT_HZ);
|
|
|
- if (ret <= 0)
|
|
|
+
|
|
|
+ if (ret <= 0 || skip)
|
|
|
ath10k_warn("tx not flushed\n");
|
|
|
+
|
|
|
+skip:
|
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
|
}
|
|
|
|
|
|
/* TODO: Implement this function properly
|
|
@@ -2660,6 +2857,83 @@ static int ath10k_tx_last_beacon(struct ieee80211_hw *hw)
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_PM
|
|
|
+static int ath10k_suspend(struct ieee80211_hw *hw,
|
|
|
+ struct cfg80211_wowlan *wowlan)
|
|
|
+{
|
|
|
+ struct ath10k *ar = hw->priv;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ar->is_target_paused = false;
|
|
|
+
|
|
|
+ ret = ath10k_wmi_pdev_suspend_target(ar);
|
|
|
+ if (ret) {
|
|
|
+ ath10k_warn("could not suspend target (%d)\n", ret);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = wait_event_interruptible_timeout(ar->event_queue,
|
|
|
+ ar->is_target_paused == true,
|
|
|
+ 1 * HZ);
|
|
|
+ if (ret < 0) {
|
|
|
+ ath10k_warn("suspend interrupted (%d)\n", ret);
|
|
|
+ goto resume;
|
|
|
+ } else if (ret == 0) {
|
|
|
+ ath10k_warn("suspend timed out - target pause event never came\n");
|
|
|
+ goto resume;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = ath10k_hif_suspend(ar);
|
|
|
+ if (ret) {
|
|
|
+ ath10k_warn("could not suspend hif (%d)\n", ret);
|
|
|
+ goto resume;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+resume:
|
|
|
+ ret = ath10k_wmi_pdev_resume_target(ar);
|
|
|
+ if (ret)
|
|
|
+ ath10k_warn("could not resume target (%d)\n", ret);
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static int ath10k_resume(struct ieee80211_hw *hw)
|
|
|
+{
|
|
|
+ struct ath10k *ar = hw->priv;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = ath10k_hif_resume(ar);
|
|
|
+ if (ret) {
|
|
|
+ ath10k_warn("could not resume hif (%d)\n", ret);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = ath10k_wmi_pdev_resume_target(ar);
|
|
|
+ if (ret) {
|
|
|
+ ath10k_warn("could not resume target (%d)\n", ret);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+static void ath10k_restart_complete(struct ieee80211_hw *hw)
|
|
|
+{
|
|
|
+ struct ath10k *ar = hw->priv;
|
|
|
+
|
|
|
+ mutex_lock(&ar->conf_mutex);
|
|
|
+
|
|
|
+ /* If device failed to restart it will be in a different state, e.g.
|
|
|
+ * ATH10K_STATE_WEDGED */
|
|
|
+ if (ar->state == ATH10K_STATE_RESTARTED) {
|
|
|
+ ath10k_info("device successfully recovered\n");
|
|
|
+ ar->state = ATH10K_STATE_ON;
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_unlock(&ar->conf_mutex);
|
|
|
+}
|
|
|
+
|
|
|
static const struct ieee80211_ops ath10k_ops = {
|
|
|
.tx = ath10k_tx,
|
|
|
.start = ath10k_start,
|
|
@@ -2680,6 +2954,11 @@ static const struct ieee80211_ops ath10k_ops = {
|
|
|
.set_frag_threshold = ath10k_set_frag_threshold,
|
|
|
.flush = ath10k_flush,
|
|
|
.tx_last_beacon = ath10k_tx_last_beacon,
|
|
|
+ .restart_complete = ath10k_restart_complete,
|
|
|
+#ifdef CONFIG_PM
|
|
|
+ .suspend = ath10k_suspend,
|
|
|
+ .resume = ath10k_resume,
|
|
|
+#endif
|
|
|
};
|
|
|
|
|
|
#define RATETAB_ENT(_rate, _rateid, _flags) { \
|
|
@@ -2948,8 +3227,10 @@ int ath10k_mac_register(struct ath10k *ar)
|
|
|
channels = kmemdup(ath10k_2ghz_channels,
|
|
|
sizeof(ath10k_2ghz_channels),
|
|
|
GFP_KERNEL);
|
|
|
- if (!channels)
|
|
|
- return -ENOMEM;
|
|
|
+ if (!channels) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err_free;
|
|
|
+ }
|
|
|
|
|
|
band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
|
|
|
band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels);
|
|
@@ -2968,11 +3249,8 @@ int ath10k_mac_register(struct ath10k *ar)
|
|
|
sizeof(ath10k_5ghz_channels),
|
|
|
GFP_KERNEL);
|
|
|
if (!channels) {
|
|
|
- if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
|
|
|
- band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
|
|
|
- kfree(band->channels);
|
|
|
- }
|
|
|
- return -ENOMEM;
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err_free;
|
|
|
}
|
|
|
|
|
|
band = &ar->mac.sbands[IEEE80211_BAND_5GHZ];
|
|
@@ -3036,25 +3314,30 @@ int ath10k_mac_register(struct ath10k *ar)
|
|
|
ath10k_reg_notifier);
|
|
|
if (ret) {
|
|
|
ath10k_err("Regulatory initialization failed\n");
|
|
|
- return ret;
|
|
|
+ goto err_free;
|
|
|
}
|
|
|
|
|
|
ret = ieee80211_register_hw(ar->hw);
|
|
|
if (ret) {
|
|
|
ath10k_err("ieee80211 registration failed: %d\n", ret);
|
|
|
- return ret;
|
|
|
+ goto err_free;
|
|
|
}
|
|
|
|
|
|
if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
|
|
|
ret = regulatory_hint(ar->hw->wiphy,
|
|
|
ar->ath_common.regulatory.alpha2);
|
|
|
if (ret)
|
|
|
- goto exit;
|
|
|
+ goto err_unregister;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
-exit:
|
|
|
+
|
|
|
+err_unregister:
|
|
|
ieee80211_unregister_hw(ar->hw);
|
|
|
+err_free:
|
|
|
+ kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
|
|
|
+ kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
|